#! /usr/bin/env ruby $KCODE = "none" $testnum=0 $ntest=0 $failed = 0 def test_check(what) printf "%s\n", what $what = what $testnum = 0 end def test_ok(cond,n=1) $testnum+=1 $ntest+=1 if cond printf "ok %d\n", $testnum else where = caller(n)[0] printf "not ok %s %d -- %s\n", $what, $testnum, where $failed+=1 end end # make sure conditional operators work test_check "assignment" a=[]; a[0] ||= "bar"; test_ok(a[0] == "bar") h={}; h["foo"] ||= "bar"; test_ok(h["foo"] == "bar") aa = 5 aa ||= 25 test_ok(aa == 5) bb ||= 25 test_ok(bb == 25) cc &&=33 test_ok(cc == nil) cc = 5 cc &&=44 test_ok(cc == 44) a = nil; test_ok(a == nil) a = 1; test_ok(a == 1) a = []; test_ok(a == []) a = [1]; test_ok(a == [1]) a = [nil]; test_ok(a == [nil]) a = [[]]; test_ok(a == [[]]) a = [1,2]; test_ok(a == [1,2]) a = [*[]]; test_ok(a == []) a = [*[1]]; test_ok(a == [1]) a = [*[1,2]]; test_ok(a == [1,2]) a = *nil; test_ok(a == nil) a = *1; test_ok(a == 1) a = *[]; test_ok(a == nil) a = *[1]; test_ok(a == 1) a = *[nil]; test_ok(a == nil) a = *[[]]; test_ok(a == []) a = *[1,2]; test_ok(a == [1,2]) a = *[*[]]; test_ok(a == nil) a = *[*[1]]; test_ok(a == 1) a = *[*[1,2]]; test_ok(a == [1,2]) a, = nil; test_ok(a == nil) a, = 1; test_ok(a == 1) a, = []; test_ok(a == nil) a, = [1]; test_ok(a == 1) a, = [nil]; test_ok(a == nil) a, = [[]]; test_ok(a == []) a, = 1,2; test_ok(a == 1) a, = [1,2]; test_ok(a == 1) a, = [*[]]; test_ok(a == nil) a, = [*[1]]; test_ok(a == 1) a, = *[1,2]; test_ok(a == 1) a, = [*[1,2]]; test_ok(a == 1) a, = *nil; test_ok(a == nil) a, = *1; test_ok(a == 1) a, = *[]; test_ok(a == nil) a, = *[1]; test_ok(a == 1) a, = *[nil]; test_ok(a == nil) a, = *[[]]; test_ok(a == []) a, = *[1,2]; test_ok(a == 1) a, = *[*[]]; test_ok(a == nil) a, = *[*[1]]; test_ok(a == 1) a, = *[*[1,2]]; test_ok(a == 1) *a = nil; test_ok(a == [nil]) *a = 1; test_ok(a == [1]) *a = []; test_ok(a == [[]]) *a = [1]; test_ok(a == [[1]]) *a = [nil]; test_ok(a == [[nil]]) *a = [[]]; test_ok(a == [[[]]]) *a = [1,2]; test_ok(a == [[1,2]]) *a = [*[]]; test_ok(a == [[]]) *a = [*[1]]; test_ok(a == [[1]]) *a = [*[1,2]]; test_ok(a == [[1,2]]) *a = *nil; test_ok(a == [nil]) *a = *1; test_ok(a == [1]) *a = *[]; test_ok(a == []) *a = *[1]; test_ok(a == [1]) *a = *[nil]; test_ok(a == [nil]) *a = *[[]]; test_ok(a == [[]]) *a = *[1,2]; test_ok(a == [1,2]) *a = *[*[]]; test_ok(a == []) *a = *[*[1]]; test_ok(a == [1]) *a = *[*[1,2]]; test_ok(a == [1,2]) a,b,*c = nil; test_ok([a,b,c] == [nil,nil,[]]) a,b,*c = 1; test_ok([a,b,c] == [1,nil,[]]) a,b,*c = []; test_ok([a,b,c] == [nil,nil,[]]) a,b,*c = [1]; test_ok([a,b,c] == [1,nil,[]]) a,b,*c = [nil]; test_ok([a,b,c] == [nil,nil,[]]) a,b,*c = [[]]; test_ok([a,b,c] == [[],nil,[]]) a,b,*c = [1,2]; test_ok([a,b,c] == [1,2,[]]) a,b,*c = [*[]]; test_ok([a,b,c] == [nil,nil,[]]) a,b,*c = [*[1]]; test_ok([a,b,c] == [1,nil,[]]) a,b,*c = [*[1,2]]; test_ok([a,b,c] == [1,2,[]]) a,b,*c = *nil; test_ok([a,b,c] == [nil,nil,[]]) a,b,*c = *1; test_ok([a,b,c] == [1,nil,[]]) a,b,*c = *[]; test_ok([a,b,c] == [nil,nil,[]]) a,b,*c = *[1]; test_ok([a,b,c] == [1,nil,[]]) a,b,*c = *[nil]; test_ok([a,b,c] == [nil,nil,[]]) a,b,*c = *[[]]; test_ok([a,b,c] == [[],nil,[]]) a,b,*c = *[1,2]; test_ok([a,b,c] == [1,2,[]]) a,b,*c = *[*[]]; test_ok([a,b,c] == [nil,nil,[]]) a,b,*c = *[*[1]]; test_ok([a,b,c] == [1,nil,[]]) a,b,*c = *[*[1,2]]; test_ok([a,b,c] == [1,2,[]]) def f; yield nil; end; f {|a| test_ok(a == nil)} def f; yield 1; end; f {|a| test_ok(a == 1)} def f; yield []; end; f {|a| test_ok(a == [])} def f; yield [1]; end; f {|a| test_ok(a == [1])} def f; yield [nil]; end; f {|a| test_ok(a == [nil])} def f; yield [[]]; end; f {|a| test_ok(a == [[]])} def f; yield [*[]]; end; f {|a| test_ok(a == [])} def f; yield [*[1]]; end; f {|a| test_ok(a == [1])} def f; yield [*[1,2]]; end; f {|a| test_ok(a == [1,2])} def f; yield *nil; end; f {|a| test_ok(a == nil)} def f; yield *1; end; f {|a| test_ok(a == 1)} def f; yield *[1]; end; f {|a| test_ok(a == 1)} def f; yield *[nil]; end; f {|a| test_ok(a == nil)} def f; yield *[[]]; end; f {|a| test_ok(a == [])} def f; yield *[*[1]]; end; f {|a| test_ok(a == 1)} def f; yield; end; f {|a,| test_ok(a == nil)} def f; yield nil; end; f {|a,| test_ok(a == nil)} def f; yield 1; end; f {|a,| test_ok(a == 1)} def f; yield []; end; f {|a,| test_ok(a == nil)} def f; yield [1]; end; f {|a,| test_ok(a == 1)} def f; yield [nil]; end; f {|a,| test_ok(a == nil)} def f; yield [[]]; end; f {|a,| test_ok(a == [])} def f; yield [*[]]; end; f {|a,| test_ok(a == nil)} def f; yield [*[1]]; end; f {|a,| test_ok(a == 1)} def f; yield [*[1,2]]; end; f {|a,| test_ok(a == 1)} def f; yield *nil; end; f {|a,| test_ok(a == nil)} def f; yield *1; end; f {|a,| test_ok(a == 1)} def f; yield *[]; end; f {|a,| test_ok(a == nil)} def f; yield *[1]; end; f {|a,| test_ok(a == 1)} def f; yield *[nil]; end; f {|a,| test_ok(a == nil)} def f; yield *[[]]; end; f {|a,| test_ok(a == [])} def f; yield *[*[]]; end; f {|a,| test_ok(a == nil)} def f; yield *[*[1]]; end; f {|a,| test_ok(a == 1)} def f; yield *[*[1,2]]; end; f {|a,| test_ok(a == 1)} def f; yield; end; f {|*a| test_ok(a == [])} def f; yield nil; end; f {|*a| test_ok(a == [nil])} def f; yield 1; end; f {|*a| test_ok(a == [1])} def f; yield []; end; f {|*a| test_ok(a == [[]])} def f; yield [1]; end; f {|*a| test_ok(a == [[1]])} def f; yield [nil]; end; f {|*a| test_ok(a == [[nil]])} def f; yield [[]]; end; f {|*a| test_ok(a == [[[]]])} def f; yield [1,2]; end; f {|*a| test_ok(a == [[1,2]])} def f; yield [*[]]; end; f {|*a| test_ok(a == [[]])} def f; yield [*[1]]; end; f {|*a| test_ok(a == [[1]])} def f; yield [*[1,2]]; end; f {|*a| test_ok(a == [[1,2]])} def f; yield *nil; end; f {|*a| test_ok(a == [nil])} def f; yield *1; end; f {|*a| test_ok(a == [1])} def f; yield *[]; end; f {|*a| test_ok(a == [])} def f; yield *[1]; end; f {|*a| test_ok(a == [1])} def f; yield *[nil]; end; f {|*a| test_ok(a == [nil])} def f; yield *[[]]; end; f {|*a| test_ok(a == [[]])} def f; yield *[*[]]; end; f {|*a| test_ok(a == [])} def f; yield *[*[1]]; end; f {|*a| test_ok(a == [1])} def f; yield *[*[1,2]]; end; f {|*a| test_ok(a == [1,2])} def f; yield; end; f {|a,b,*c| test_ok([a,b,c] == [nil,nil,[]])} def f; yield nil; end; f {|a,b,*c| test_ok([a,b,c] == [nil,nil,[]])} def f; yield 1; end; f {|a,b,*c| test_ok([a,b,c] == [1,nil,[]])} def f; yield []; end; f {|a,b,*c| test_ok([a,b,c] == [nil,nil,[]])} def f; yield [1]; end; f {|a,b,*c| test_ok([a,b,c] == [1,nil,[]])} def f; yield [nil]; end; f {|a,b,*c| test_ok([a,b,c] == [nil,nil,[]])} def f; yield [[]]; end; f {|a,b,*c| test_ok([a,b,c] == [[],nil,[]])} def f; yield [*[]]; end; f {|a,b,*c| test_ok([a,b,c] == [nil,nil,[]])} def f; yield [*[1]]; end; f {|a,b,*c| test_ok([a,b,c] == [1,nil,[]])} def f; yield [*[1,2]]; end; f {|a,b,*c| test_ok([a,b,c] == [1,2,[]])} def f; yield *nil; end; f {|a,b,*c| test_ok([a,b,c] == [nil,nil,[]])} def f; yield *1; end; f {|a,b,*c| test_ok([a,b,c] == [1,nil,[]])} def f; yield *[]; end; f {|a,b,*c| test_ok([a,b,c] == [nil,nil,[]])} def f; yield *[1]; end; f {|a,b,*c| test_ok([a,b,c] == [1,nil,[]])} def f; yield *[nil]; end; f {|a,b,*c| test_ok([a,b,c] == [nil,nil,[]])} def f; yield *[[]]; end; f {|a,b,*c| test_ok([a,b,c] == [[],nil,[]])} def f; yield *[*[]]; end; f {|a,b,*c| test_ok([a,b,c] == [nil,nil,[]])} def f; yield *[*[1]]; end; f {|a,b,*c| test_ok([a,b,c] == [1,nil,[]])} def f; yield *[*[1,2]]; end; f {|a,b,*c| test_ok([a,b,c] == [1,2,[]])} def r; return; end; a = r(); test_ok(a == nil) def r; return nil; end; a = r(); test_ok(a == nil) def r; return 1; end; a = r(); test_ok(a == 1) def r; return []; end; a = r(); test_ok(a == []) def r; return [1]; end; a = r(); test_ok(a == [1]) def r; return [nil]; end; a = r(); test_ok(a == [nil]) def r; return [[]]; end; a = r(); test_ok(a == [[]]) def r; return [*[]]; end; a = r(); test_ok(a == []) def r; return [*[1]]; end; a = r(); test_ok(a == [1]) def r; return [*[1,2]]; end; a = r(); test_ok(a == [1,2]) def r; return *nil; end; a = r(); test_ok(a == nil) def r; return *1; end; a = r(); test_ok(a == 1) def r; return *[]; end; a = r(); test_ok(a == nil) def r; return *[1]; end; a = r(); test_ok(a == 1) def r; return *[nil]; end; a = r(); test_ok(a == nil) def r; return *[[]]; end; a = r(); test_ok(a == []) def r; return *[*[]]; end; a = r(); test_ok(a == nil) def r; return *[*[1]]; end; a = r(); test_ok(a == 1) def r; return *[*[1,2]]; end; a = r(); test_ok(a == [1,2]) def r; return *nil; end; a = *r(); test_ok(a == nil) def r; return *1; end; a = *r(); test_ok(a == 1) def r; return *[]; end; a = *r(); test_ok(a == nil) def r; return *[1]; end; a = *r(); test_ok(a == 1) def r; return *[nil]; end; a = *r(); test_ok(a == nil) def r; return *[[]]; end; a = *r(); test_ok(a == nil) def r; return *[*[]]; end; a = *r(); test_ok(a == nil) def r; return *[*[1]]; end; a = *r(); test_ok(a == 1) def r; return *[*[1,2]]; end; a = *r(); test_ok(a == [1,2]) def r; return; end; *a = r(); test_ok(a == [nil]) def r; return nil; end; *a = r(); test_ok(a == [nil]) def r; return 1; end; *a = r(); test_ok(a == [1]) def r; return []; end; *a = r(); test_ok(a == [[]]) def r; return [1]; end; *a = r(); test_ok(a == [[1]]) def r; return [nil]; end; *a = r(); test_ok(a == [[nil]]) def r; return [[]]; end; *a = r(); test_ok(a == [[[]]]) def r; return [1,2]; end; *a = r(); test_ok(a == [[1,2]]) def r; return [*[]]; end; *a = r(); test_ok(a == [[]]) def r; return [*[1]]; end; *a = r(); test_ok(a == [[1]]) def r; return [*[1,2]]; end; *a = r(); test_ok(a == [[1,2]]) def r; return *nil; end; *a = r(); test_ok(a == [nil]) def r; return *1; end; *a = r(); test_ok(a == [1]) def r; return *[]; end; *a = r(); test_ok(a == [nil]) def r; return *[1]; end; *a = r(); test_ok(a == [1]) def r; return *[nil]; end; *a = r(); test_ok(a == [nil]) def r; return *[[]]; end; *a = r(); test_ok(a == [[]]) def r; return *[1,2]; end; *a = r(); test_ok(a == [[1,2]]) def r; return *[*[]]; end; *a = r(); test_ok(a == [nil]) def r; return *[*[1]]; end; *a = r(); test_ok(a == [1]) def r; return *[*[1,2]]; end; *a = r(); test_ok(a == [[1,2]]) def r; return *nil; end; *a = *r(); test_ok(a == [nil]) def r; return *1; end; *a = *r(); test_ok(a == [1]) def r; return *[]; end; *a = *r(); test_ok(a == [nil]) def r; return *[1]; end; *a = *r(); test_ok(a == [1]) def r; return *[nil]; end; *a = *r(); test_ok(a == [nil]) def r; return *[[]]; end; *a = *r(); test_ok(a == []) def r; return *[1,2]; end; *a = *r(); test_ok(a == [1,2]) def r; return *[*[]]; end; *a = *r(); test_ok(a == [nil]) def r; return *[*[1]]; end; *a = *r(); test_ok(a == [1]) def r; return *[*[1,2]]; end; *a = *r(); test_ok(a == [1,2]) def r; return; end; a,b,*c = r(); test_ok([a,b,c] == [nil,nil,[]]) def r; return nil; end; a,b,*c = r(); test_ok([a,b,c] == [nil,nil,[]]) def r; return 1; end; a,b,*c = r(); test_ok([a,b,c] == [1,nil,[]]) def r; return []; end; a,b,*c = r(); test_ok([a,b,c] == [nil,nil,[]]) def r; return [1]; end; a,b,*c = r(); test_ok([a,b,c] == [1,nil,[]]) def r; return [nil]; end; a,b,*c = r(); test_ok([a,b,c] == [nil,nil,[]]) def r; return [[]]; end; a,b,*c = r(); test_ok([a,b,c] == [[],nil,[]]) def r; return [1,2]; end; a,b,*c = r(); test_ok([a,b,c] == [1,2,[]]) def r; return [*[]]; end; a,b,*c = r(); test_ok([a,b,c] == [nil,nil,[]]) def r; return [*[1]]; end; a,b,*c = r(); test_ok([a,b,c] == [1,nil,[]]) def r; return [*[1,2]]; end; a,b,*c = r(); test_ok([a,b,c] == [1,2,[]]) def r; return *nil; end; a,b,*c = r(); test_ok([a,b,c] == [nil,nil,[]]) def r; return *1; end; a,b,*c = r(); test_ok([a,b,c] == [1,nil,[]]) def r; return *[]; end; a,b,*c = r(); test_ok([a,b,c] == [nil,nil,[]]) def r; return *[1]; end; a,b,*c = r(); test_ok([a,b,c] == [1,nil,[]]) def r; return *[nil]; end; a,b,*c = r(); test_ok([a,b,c] == [nil,nil,[]]) def r; return *[[]]; end; a,b,*c = r(); test_ok([a,b,c] == [nil,nil,[]]) def r; return *[1,2]; end; a,b,*c = r(); test_ok([a,b,c] == [1,2,[]]) def r; return *[*[]]; end; a,b,*c = r(); test_ok([a,b,c] == [nil,nil,[]]) def r; return *[*[1]]; end; a,b,*c = r(); test_ok([a,b,c] == [1,nil,[]]) def r; return *[*[1,2]]; end; a,b,*c = r(); test_ok([a,b,c] == [1,2,[]]) f = lambda {|r,| test_ok([] == r)} f.call([], *[]) f = lambda {|r,*l| test_ok([] == r); test_ok([1] == l)} f.call([], *[1]) f = lambda{|x| x} test_ok(f.call(42) == 42) test_ok(f.call([42]) == [42]) test_ok(f.call([[42]]) == [[42]]) test_ok(f.call([42,55]) == [42,55]) f = lambda{|x,| x} test_ok(f.call(42) == 42) test_ok(f.call([42]) == [42]) test_ok(f.call([[42]]) == [[42]]) test_ok(f.call([42,55]) == [42,55]) f = lambda{|*x| x} test_ok(f.call(42) == [42]) test_ok(f.call([42]) == [[42]]) test_ok(f.call([[42]]) == [[[42]]]) test_ok(f.call([42,55]) == [[42,55]]) test_ok(f.call(42,55) == [42,55]) a,=*[1] test_ok(a == 1) a,=*[[1]] test_ok(a == [1]) a,=*[[[1]]] test_ok(a == [[1]]) x, (y, z) = 1, 2, 3 test_ok([1,2,nil] == [x,y,z]) x, (y, z) = 1, [2,3] test_ok([1,2,3] == [x,y,z]) x, (y, z) = 1, [2] test_ok([1,2,nil] == [x,y,z]) a = loop do break; end; test_ok(a == nil) a = loop do break nil; end; test_ok(a == nil) a = loop do break 1; end; test_ok(a == 1) a = loop do break []; end; test_ok(a == []) a = loop do break [1]; end; test_ok(a == [1]) a = loop do break [nil]; end; test_ok(a == [nil]) a = loop do break [[]]; end; test_ok(a == [[]]) a = loop do break [*[]]; end; test_ok(a == []) a = loop do break [*[1]]; end; test_ok(a == [1]) a = loop do break [*[1,2]]; end; test_ok(a == [1,2]) a = loop do break *nil; end; test_ok(a == nil) a = loop do break *1; end; test_ok(a == 1) a = loop do break *[]; end; test_ok(a == nil) a = loop do break *[1]; end; test_ok(a == 1) a = loop do break *[nil]; end; test_ok(a == nil) a = loop do break *[[]]; end; test_ok(a == []) a = loop do break *[*[]]; end; test_ok(a == nil) a = loop do break *[*[1]]; end; test_ok(a == 1) a = loop do break *[*[1,2]]; end; test_ok(a == [1,2]) *a = loop do break; end; test_ok(a == [nil]) *a = loop do break nil; end; test_ok(a == [nil]) *a = loop do break 1; end; test_ok(a == [1]) *a = loop do break []; end; test_ok(a == [[]]) *a = loop do break [1]; end; test_ok(a == [[1]]) *a = loop do break [nil]; end; test_ok(a == [[nil]]) *a = loop do break [[]]; end; test_ok(a == [[[]]]) *a = loop do break [1,2]; end; test_ok(a == [[1,2]]) *a = loop do break [*[]]; end; test_ok(a == [[]]) *a = loop do break [*[1]]; end; test_ok(a == [[1]]) *a = loop do break [*[1,2]]; end; test_ok(a == [[1,2]]) *a = loop do break *nil; end; test_ok(a == [nil]) *a = loop do break *1; end; test_ok(a == [1]) *a = loop do break *[]; end; test_ok(a == [nil]) *a = loop do break *[1]; end; test_ok(a == [1]) *a = loop do break *[nil]; end; test_ok(a == [nil]) *a = loop do break *[[]]; end; test_ok(a == [[]]) *a = loop do break *[1,2]; end; test_ok(a == [[1,2]]) *a = loop do break *[*[]]; end; test_ok(a == [nil]) *a = loop do break *[*[1]]; end; test_ok(a == [1]) *a = loop do break *[*[1,2]]; end; test_ok(a == [[1,2]]) *a = *loop do break *nil; end; test_ok(a == [nil]) *a = *loop do break *1; end; test_ok(a == [1]) *a = *loop do break *[]; end; test_ok(a == [nil]) *a = *loop do break *[1]; end; test_ok(a == [1]) *a = *loop do break *[nil]; end; test_ok(a == [nil]) *a = *loop do break *[[]]; end; test_ok(a == []) *a = *loop do break *[1,2]; end; test_ok(a == [1,2]) *a = *loop do break *[*[]]; end; test_ok(a == [nil]) *a = *loop do break *[*[1]]; end; test_ok(a == [1]) *a = *loop do break *[*[1,2]]; end; test_ok(a == [1,2]) a,b,*c = loop do break; end; test_ok([a,b,c] == [nil,nil,[]]) a,b,*c = loop do break nil; end; test_ok([a,b,c] == [nil,nil,[]]) a,b,*c = loop do break 1; end; test_ok([a,b,c] == [1,nil,[]]) a,b,*c = loop do break []; end; test_ok([a,b,c] == [nil,nil,[]]) a,b,*c = loop do break [1]; end; test_ok([a,b,c] == [1,nil,[]]) a,b,*c = loop do break [nil]; end; test_ok([a,b,c] == [nil,nil,[]]) a,b,*c = loop do break [[]]; end; test_ok([a,b,c] == [[],nil,[]]) a,b,*c = loop do break [1,2]; end; test_ok([a,b,c] == [1,2,[]]) a,b,*c = loop do break [*[]]; end; test_ok([a,b,c] == [nil,nil,[]]) a,b,*c = loop do break [*[1]]; end; test_ok([a,b,c] == [1,nil,[]]) a,b,*c = loop do break [*[1,2]]; end; test_ok([a,b,c] == [1,2,[]]) a,b,*c = loop do break *nil; end; test_ok([a,b,c] == [nil,nil,[]]) a,b,*c = loop do break *1; end; test_ok([a,b,c] == [1,nil,[]]) a,b,*c = loop do break *[]; end; test_ok([a,b,c] == [nil,nil,[]]) a,b,*c = loop do break *[1]; end; test_ok([a,b,c] == [1,nil,[]]) a,b,*c = loop do break *[nil]; end; test_ok([a,b,c] == [nil,nil,[]]) a,b,*c = loop do break *[[]]; end; test_ok([a,b,c] == [nil,nil,[]]) a,b,*c = loop do break *[1,2]; end; test_ok([a,b,c] == [1,2,[]]) a,b,*c = loop do break *[*[]]; end; test_ok([a,b,c] == [nil,nil,[]]) a,b,*c = loop do break *[*[1]]; end; test_ok([a,b,c] == [1,nil,[]]) a,b,*c = loop do break *[*[1,2]]; end; test_ok([a,b,c] == [1,2,[]]) def r(val); a = yield(); test_ok(a == val, 2); end r(nil){next} r(nil){next nil} r(1){next 1} r([]){next []} r([1]){next [1]} r([nil]){next [nil]} r([[]]){next [[]]} r([]){next [*[]]} r([1]){next [*[1]]} r([1,2]){next [*[1,2]]} r(nil){next *nil} r(1){next *1} r(nil){next *[]} r(1){next *[1]} r(nil){next *[nil]} r([]){next *[[]]} r(nil){next *[*[]]} r(1){next *[*[1]]} r([1,2]){next *[*[1,2]]} def r(val); *a = yield(); test_ok(a == val, 2); end r([nil]){next} r([nil]){next nil} r([1]){next 1} r([[]]){next []} r([[1]]){next [1]} r([[nil]]){next [nil]} r([[[]]]){next [[]]} r([[1,2]]){next [1,2]} r([[]]){next [*[]]} r([[1]]){next [*[1]]} r([[1,2]]){next [*[1,2]]} def r(val); *a = *yield(); test_ok(a == val, 2); end r([nil]){next *nil} r([1]){next *1} r([nil]){next *[]} r([1]){next *[1]} r([nil]){next *[nil]} r([]){next *[[]]} r([1,2]){next *[1,2]} r([nil]){next *[*[]]} r([1]){next *[*[1]]} r([1,2]){next *[*[1,2]]} def r(val); a,b,*c = yield(); test_ok([a,b,c] == val, 2); end r([nil,nil,[]]){next} r([nil,nil,[]]){next nil} r([1,nil,[]]){next 1} r([nil,nil,[]]){next []} r([1,nil,[]]){next [1]} r([nil,nil,[]]){next [nil]} r([[],nil,[]]){next [[]]} r([1,2,[]]){next [1,2]} r([nil,nil,[]]){next [*[]]} r([1,nil,[]]){next [*[1]]} r([1,2,[]]){next [*[1,2]]} def r(val); a,b,*c = *yield(); test_ok([a,b,c] == val, 2); end r([nil,nil,[]]){next *nil} r([1,nil,[]]){next *1} r([nil,nil,[]]){next *[]} r([1,nil,[]]){next *[1]} r([nil,nil,[]]){next *[nil]} r([nil,nil,[]]){next *[[]]} r([1,2,[]]){next *[1,2]} r([nil,nil,[]]){next *[*[]]} r([1,nil,[]]){next *[*[1]]} r([1,2,[]]){next *[*[1,2]]} test_check "condition" $x = '0'; $x == $x && test_ok(true) $x != $x && test_ok(false) $x == $x || test_ok(false) $x != $x || test_ok(true) # first test to see if we can run the tests. test_check "if/unless"; $x = 'test'; test_ok(if $x == $x then true else false end) $bad = false unless $x == $x $bad = true end test_ok(!$bad) test_ok(unless $x != $x then true else false end) test_check "case" case 5 when 1, 2, 3, 4, 6, 7, 8 test_ok(false) when 5 test_ok(true) end case 5 when 5 test_ok(true) when 1..10 test_ok(false) end case 5 when 1..10 test_ok(true) else test_ok(false) end case 5 when 5 test_ok(true) else test_ok(false) end case "foobar" when /^f.*r$/ test_ok(true) else test_ok(false) end test_check "while/until"; tmp = open("while_tmp", "w") tmp.print "tvi925\n"; tmp.print "tvi920\n"; tmp.print "vt100\n"; tmp.print "Amiga\n"; tmp.print "paper\n"; tmp.close # test break tmp = open("while_tmp", "r") test_ok(tmp.kind_of?(File)) while line = tmp.gets() break if /vt100/ =~ line end test_ok(!tmp.eof? && /vt100/ =~ line) tmp.close # test next $bad = false tmp = open("while_tmp", "r") while line = tmp.gets() next if /vt100/ =~ line $bad = 1 if /vt100/ =~ line end test_ok(!(!tmp.eof? || /vt100/ =~ line || $bad)) tmp.close # test redo $bad = false tmp = open("while_tmp", "r") while tmp.gets() line = $_ gsub(/vt100/, 'VT100') if $_ != line $_.gsub!('VT100', 'Vt100') redo end $bad = 1 if /vt100/ =~ $_ $bad = 1 if /VT100/ =~ $_ end test_ok(tmp.eof? && !$bad) tmp.close sum=0 for i in 1..10 sum += i i -= 1 if i > 0 redo end end test_ok(sum == 220) # test interval $bad = false tmp = open("while_tmp", "r") while line = tmp.gets() break if 3 case line when /vt100/, /Amiga/, /paper/ $bad = true end end test_ok(!$bad) tmp.close File.unlink "while_tmp" or `/bin/rm -f "while_tmp"` test_ok(!File.exist?("while_tmp")) i = 0 until i>4 i+=1 end test_ok(i>4) # exception handling test_check "exception"; begin raise "this must be handled" test_ok(false) rescue test_ok(true) end $bad = true begin raise "this must be handled no.2" rescue if $bad $bad = false retry test_ok(false) end end test_ok(true) # exception in rescue clause $string = "this must be handled no.3" begin begin raise "exception in rescue clause" rescue raise $string end test_ok(false) rescue test_ok(true) if $! == $string end # exception in ensure clause begin begin raise "this must be handled no.4" ensure raise "exception in ensure clause" end test_ok(false) rescue test_ok(true) end $bad = true begin begin raise "this must be handled no.5" ensure $bad = false end rescue end test_ok(!$bad) $bad = true begin begin raise "this must be handled no.6" ensure $bad = false end rescue end test_ok(!$bad) $bad = true while true begin break ensure $bad = false end end test_ok(!$bad) test_ok(catch(:foo) { loop do loop do throw :foo, true break end break test_ok(false) # should no reach here end false }) test_check "array" test_ok([1, 2] + [3, 4] == [1, 2, 3, 4]) test_ok([1, 2] * 2 == [1, 2, 1, 2]) test_ok([1, 2] * ":" == "1:2") test_ok([1, 2].hash == [1, 2].hash) test_ok([1,2,3] & [2,3,4] == [2,3]) test_ok([1,2,3] | [2,3,4] == [1,2,3,4]) test_ok([1,2,3] - [2,3] == [1]) $x = [0, 1, 2, 3, 4, 5] test_ok($x[2] == 2) test_ok($x[1..3] == [1, 2, 3]) test_ok($x[1,3] == [1, 2, 3]) $x[0, 2] = 10 test_ok($x[0] == 10 && $x[1] == 2) $x[0, 0] = -1 test_ok($x[0] == -1 && $x[1] == 10) $x[-1, 1] = 20 test_ok($x[-1] == 20 && $x.pop == 20) # array and/or test_ok(([1,2,3]&[2,4,6]) == [2]) test_ok(([1,2,3]|[2,4,6]) == [1,2,3,4,6]) # compact $x = [nil, 1, nil, nil, 5, nil, nil] $x.compact! test_ok($x == [1, 5]) # uniq $x = [1, 1, 4, 2, 5, 4, 5, 1, 2] $x.uniq! test_ok($x == [1, 4, 2, 5]) # empty? test_ok(!$x.empty?) $x = [] test_ok($x.empty?) # sort $x = ["it", "came", "to", "pass", "that", "..."] $x = $x.sort.join(" ") test_ok($x == "... came it pass that to") $x = [2,5,3,1,7] $x.sort!{|a,b| a<=>b} # sort with condition test_ok($x == [1,2,3,5,7]) $x.sort!{|a,b| b-a} # reverse sort test_ok($x == [7,5,3,2,1]) # split test $x = "The Book of Mormon" test_ok($x.split(//).reverse!.join == $x.reverse) test_ok($x.reverse == $x.reverse!) test_ok("1 byte string".split(//).reverse.join(":") == "g:n:i:r:t:s: :e:t:y:b: :1") $x = "a b c d" test_ok($x.split == ['a', 'b', 'c', 'd']) test_ok($x.split(' ') == ['a', 'b', 'c', 'd']) test_ok(defined? "a".chomp) test_ok("abc".scan(/./) == ["a", "b", "c"]) test_ok("1a2b3c".scan(/(\d.)/) == [["1a"], ["2b"], ["3c"]]) # non-greedy match test_ok("a=12;b=22".scan(/(.*?)=(\d*);?/) == [["a", "12"], ["b", "22"]]) $x = [1] test_ok(($x * 5).join(":") == '1:1:1:1:1') test_ok(($x * 1).join(":") == '1') test_ok(($x * 0).join(":") == '') *$x = *(1..7).to_a test_ok($x.size == 7) test_ok($x == [1, 2, 3, 4, 5, 6, 7]) $x = [1,2,3] $x[1,0] = $x test_ok($x == [1,1,2,3,2,3]) $x = [1,2,3] $x[-1,0] = $x test_ok($x == [1,2,1,2,3,3]) $x = [1,2,3] $x.concat($x) test_ok($x == [1,2,3,1,2,3]) test_check "hash" $x = {1=>2, 2=>4, 3=>6} $y = {1, 2, 2, 4, 3, 6} test_ok($x[1] == 2) test_ok(begin for k,v in $y raise if k*2 != v end true rescue false end) test_ok($x.length == 3) test_ok($x.has_key?(1)) test_ok($x.has_value?(4)) test_ok($x.values_at(2,3) == [4,6]) test_ok($x == {1=>2, 2=>4, 3=>6}) $z = $y.keys.join(":") test_ok($z == "1:2:3") $z = $y.values.join(":") test_ok($z == "2:4:6") test_ok($x == $y) $y.shift test_ok($y.length == 2) $z = [1,2] $y[$z] = 256 test_ok($y[$z] == 256) $x = Hash.new(0) $x[1] = 1 test_ok($x[1] == 1) test_ok($x[2] == 0) $x = Hash.new([]) test_ok($x[22] == []) test_ok($x[22].equal?($x[22])) $x = Hash.new{[]} test_ok($x[22] == []) test_ok(!$x[22].equal?($x[22])) $x = Hash.new{|h,k| $z = k; h[k] = k*2} $z = 0 test_ok($x[22] == 44) test_ok($z == 22) $z = 0 test_ok($x[22] == 44) test_ok($z == 0) $x.default = 5 test_ok($x[23] == 5) $x = Hash.new def $x.default(k) $z = k self[k] = k*2 end $z = 0 test_ok($x[22] == 44) test_ok($z == 22) $z = 0 test_ok($x[22] == 44) test_ok($z == 0) test_check "iterator" test_ok(!iterator?) def ttt test_ok(iterator?) end ttt{} # yield at top level test_ok(!defined?(yield)) $x = [1, 2, 3, 4] $y = [] # iterator over array for i in $x $y.push i end test_ok($x == $y) # nested iterator def tt 1.upto(10) {|i| yield i } end tt{|i| break if i == 5} test_ok(i == 5) def tt2(dummy) yield 1 end def tt3(&block) tt2(raise(ArgumentError,""),&block) end $x = false begin tt3{} rescue ArgumentError $x = true rescue Exception end test_ok($x) def tt4 &block tt2(raise(ArgumentError,""),&block) end $x = false begin tt4{} rescue ArgumentError $x = true rescue Exception end test_ok($x) # iterator break/redo/next/retry done = true loop{ break done = false # should not reach here } test_ok(done) done = false $bad = false loop { break if done done = true next $bad = true # should not reach here } test_ok(!$bad) done = false $bad = false loop { break if done done = true redo $bad = true # should not reach here } test_ok(!$bad) $x = [] for i in 1 .. 7 $x.push i end test_ok($x.size == 7) test_ok($x == [1, 2, 3, 4, 5, 6, 7]) $done = false $x = [] for i in 1 .. 7 # see how retry works in iterator loop if i == 4 and not $done $done = true retry end $x.push(i) end test_ok($x.size == 10) test_ok($x == [1, 2, 3, 1, 2, 3, 4, 5, 6, 7]) # append method to built-in class class Array def iter_test1 collect{|e| [e, yield(e)]}.sort{|a,b|a[1]<=>b[1]} end def iter_test2 a = collect{|e| [e, yield(e)]} a.sort{|a,b|a[1]<=>b[1]} end end $x = [[1,2],[3,4],[5,6]] test_ok($x.iter_test1{|x|x} == $x.iter_test2{|x|x}) class IterTest def initialize(e); @body = e; end def each0(&block); @body.each(&block); end def each1(&block); @body.each {|*x| block.call(*x) } end def each2(&block); @body.each {|*x| block.call(x) } end def each3(&block); @body.each {|x| block.call(*x) } end def each4(&block); @body.each {|x| block.call(x) } end def each5; @body.each {|*x| yield(*x) } end def each6; @body.each {|*x| yield(x) } end def each7; @body.each {|x| yield(*x) } end def each8; @body.each {|x| yield(x) } end def f(a) a end end test_ok(IterTest.new(nil).method(:f).to_proc.call([1]) == [1]) m = /\w+/.match("abc") test_ok(IterTest.new(nil).method(:f).to_proc.call([m]) == [m]) IterTest.new([0]).each0 {|x| test_ok(x == 0)} IterTest.new([1]).each1 {|x| test_ok(x == 1)} IterTest.new([2]).each2 {|x| test_ok(x == [2])} IterTest.new([3]).each3 {|x| test_ok(x == 3)} IterTest.new([4]).each4 {|x| test_ok(x == 4)} IterTest.new([5]).each5 {|x| test_ok(x == 5)} IterTest.new([6]).each6 {|x| test_ok(x == [6])} IterTest.new([7]).each7 {|x| test_ok(x == 7)} IterTest.new([8]).each8 {|x| test_ok(x == 8)} IterTest.new([[0]]).each0 {|x| test_ok(x == [0])} IterTest.new([[1]]).each1 {|x| test_ok(x == [1])} IterTest.new([[2]]).each2 {|x| test_ok(x == [[2]])} IterTest.new([[3]]).each3 {|x| test_ok(x == 3)} IterTest.new([[4]]).each4 {|x| test_ok(x == [4])} IterTest.new([[5]]).each5 {|x| test_ok(x == [5])} IterTest.new([[6]]).each6 {|x| test_ok(x == [[6]])} IterTest.new([[7]]).each7 {|x| test_ok(x == 7)} IterTest.new([[8]]).each8 {|x| test_ok(x == [8])} IterTest.new([[0,0]]).each0 {|x| test_ok(x == [0,0])} IterTest.new([[8,8]]).each8 {|x| test_ok(x == [8,8])} def m0(v) v end def m1 m0(block_given?) end test_ok(m1{p 'test'}) test_ok(!m1) def m m0(block_given?,&proc{}) end test_ok(m1{p 'test'}) test_ok(!m1) class C include Enumerable def initialize @a = [1,2,3] end def each(&block) @a.each(&block) end end test_ok(C.new.collect{|n| n} == [1,2,3]) test_ok(Proc == lambda{}.class) test_ok(Proc == Proc.new{}.class) lambda{|a|test_ok(a==1)}.call(1) def block_test(klass, &block) test_ok(klass === block) end block_test(NilClass) block_test(Proc){} def argument_test(state, proc, *args) x = state begin proc.call(*args) rescue ArgumentError x = !x end test_ok(x,2) end argument_test(true, lambda{||}) argument_test(false, lambda{||}, 1) argument_test(true, lambda{|a,|}, 1) argument_test(false, lambda{|a,|}) argument_test(false, lambda{|a,|}, 1,2) def get_block(&block) block end test_ok(Proc == get_block{}.class) argument_test(true, get_block{||}) argument_test(true, get_block{||}, 1) argument_test(true, get_block{|a,|}, 1) argument_test(true, get_block{|a,|}) argument_test(true, get_block{|a,|}, 1,2) argument_test(true, get_block(&lambda{||})) argument_test(false, get_block(&lambda{||}),1) argument_test(true, get_block(&lambda{|a,|}),1) argument_test(false, get_block(&lambda{|a,|}),1,2) block = get_block{11} test_ok(block.class == Proc) test_ok(block.to_proc.class == Proc) test_ok(block.clone.call == 11) test_ok(get_block(&block).class == Proc) lambda = lambda{44} test_ok(lambda.class == Proc) test_ok(lambda.to_proc.class == Proc) test_ok(lambda.clone.call == 44) test_ok(get_block(&lambda).class == Proc) test_ok(Proc.new{|a,| a}.call(1,2,3) == 1) argument_test(true, Proc.new{|a,|}, 1,2) test_ok(Proc.new{|&b| b.call(10)}.call {|x| x} == 10) test_ok(Proc.new{|a,&b| b.call(a)}.call(12) {|x| x} == 12) def test_return1 Proc.new { return 55 }.call + 5 end test_ok(test_return1() == 55) def test_return2 lambda { return 55 }.call + 5 end test_ok(test_return2() == 60) def proc_call(&b) b.call end def proc_yield() yield end def proc_return1 proc_call{return 42}+1 end test_ok(proc_return1() == 42) def proc_return2 proc_yield{return 42}+1 end test_ok(proc_return2() == 42) def ljump_test(state, proc, *args) x = state begin proc.call(*args) rescue LocalJumpError x = !x end test_ok(x,2) end ljump_test(false, get_block{break}) ljump_test(true, lambda{break}) def exit_value_test(&block) block.call rescue LocalJumpError $!.exit_value end test_ok(45, exit_value_test{break 45}) test_ok(55, begin get_block{break 55}.call rescue LocalJumpError $!.exit_value end) def block_call(&block) block.call end def block_get(&block) block end def test_b1 block_call{break 11} end test_ok(test_b1() == 11) def ljump_rescue(r) begin yield rescue LocalJumpError => e r if /from proc-closure/ =~ e.message end end def test_b2 ljump_rescue(22) do block_get{break 21}.call end end test_ok(test_b2() == 22) def test_b3 ljump_rescue(33) do Proc.new{break 31}.call end end test_ok(test_b3() == 33) def test_b4 lambda{break 44}.call end test_ok(test_b4() == 44) def test_b5 ljump_rescue(55) do b = block_get{break 54} block_call(&b) end end test_ok(test_b5() == 55) def test_b6 b = lambda{break 67} block_call(&b) 66 end test_ok(test_b6() == 66) def util_r7 block_get{break 78} end def test_b7 b = util_r7() ljump_rescue(77) do block_call(&b) end end test_ok(test_b7() == 77) def util_b8(&block) block_call(&block) end def test_b8 util_b8{break 88} end test_ok(test_b8() == 88) def util_b9(&block) lambda{block.call}.call end def test_b9 util_b9{break 99} end test_ok(test_b9() == 99) def util_b10 util_b9{break 100} end def test_b10 util_b10() end test_ok(test_b10() == 100) def test_b11 ljump_rescue(111) do loop do Proc.new{break 110}.call break 112 end end end test_ok(test_b11() == 111) def test_b12 loop do break lambda{break 122}.call break 121 end end test_ok(test_b12() == 122) def test_b13 ljump_rescue(133) do while true Proc.new{break 130}.call break 131 end end end test_ok(test_b13() == 133) def test_b14 while true break lambda{break 144}.call break 143 end end test_ok(test_b14() == 144) def test_b15 [0].each {|c| yield 1 } 156 end test_ok(test_b15{|e| break 155 } == 155) def marity_test(m) method = method(m) test_ok(method.arity == method.to_proc.arity, 2) end marity_test(:test_ok) marity_test(:marity_test) marity_test(:p) lambda(&method(:test_ok)).call(true) lambda(&get_block{|a,n| test_ok(a,n)}).call(true, 2) class ITER_TEST1 def a block_given? end end class ITER_TEST2 < ITER_TEST1 def a test_ok(super) super end end test_ok(ITER_TEST2.new.a {}) class ITER_TEST3 def foo x return yield if block_given? x end end class ITER_TEST4 < ITER_TEST3 def foo x test_ok(super == yield) test_ok(super(x, &nil) == x) end end ITER_TEST4.new.foo(44){55} test_check "float" test_ok(2.6.floor == 2) test_ok((-2.6).floor == -3) test_ok(2.6.ceil == 3) test_ok((-2.6).ceil == -2) test_ok(2.6.truncate == 2) test_ok((-2.6).truncate == -2) test_ok(2.6.round == 3) test_ok((-2.4).truncate == -2) test_ok((13.4 % 1 - 0.4).abs < 0.0001) nan = 0.0/0 def nan_test(x,y) test_ok(x != y) test_ok((x < y) == false) test_ok((x > y) == false) test_ok((x <= y) == false) test_ok((x >= y) == false) end nan_test(nan, nan) nan_test(nan, 0) nan_test(nan, 1) nan_test(nan, -1) nan_test(nan, 1000) nan_test(nan, -1000) nan_test(nan, 1_000_000_000_000) nan_test(nan, -1_000_000_000_000) nan_test(nan, 100.0); nan_test(nan, -100.0); nan_test(nan, 0.001); nan_test(nan, -0.001); nan_test(nan, 1.0/0); nan_test(nan, -1.0/0); #s = "3.7517675036461267e+17" #test_ok(s == sprintf("%.16e", s.to_f)) f = 3.7517675036461267e+17 test_ok(f == sprintf("%.16e", f).to_f) test_check "bignum" def fact(n) return 1 if n == 0 f = 1 while n>0 f *= n n -= 1 end return f end $x = fact(40) test_ok($x == $x) test_ok($x == fact(40)) test_ok($x < $x+2) test_ok($x > $x-2) test_ok($x == 815915283247897734345611269596115894272000000000) test_ok($x != 815915283247897734345611269596115894272000000001) test_ok($x+1 == 815915283247897734345611269596115894272000000001) test_ok($x/fact(20) == 335367096786357081410764800000) $x = -$x test_ok($x == -815915283247897734345611269596115894272000000000) test_ok(2-(2**32) == -(2**32-2)) test_ok(2**32 - 5 == (2**32-3)-2) $good = true; for i in 1000..1014 $good = false if ((1 << i) != (2**i)) end test_ok($good) $good = true; n1= 1 << 1000 for i in 1000..1014 $good = false if ((1 << i) != n1) n1 *= 2 end test_ok($good) $good = true; n2=n1 for i in 1..10 n1 = n1 / 2 n2 = n2 >> 1 $good = false if (n1 != n2) end test_ok($good) $good = true; for i in 4000..4096 n1 = 1 << i; if (n1**2-1) / (n1+1) != (n1-1) p i $good = false end end test_ok($good) b = 10**80 a = b * 9 + 7 test_ok(7 == a.modulo(b)) test_ok(-b + 7 == a.modulo(-b)) test_ok(b + -7 == (-a).modulo(b)) test_ok(-7 == (-a).modulo(-b)) test_ok(7 == a.remainder(b)) test_ok(7 == a.remainder(-b)) test_ok(-7 == (-a).remainder(b)) test_ok(-7 == (-a).remainder(-b)) test_ok(10**40+10**20 == 10000000000000000000100000000000000000000) test_ok(10**40/10**20 == 100000000000000000000) a = 677330545177305025495135714080 b = 14269972710765292560 test_ok(a % b == 0) test_ok(-a % b == 0) def shift_test(a) b = a / (2 ** 32) c = a >> 32 test_ok(b == c) b = a * (2 ** 32) c = a << 32 test_ok(b == c) end shift_test(-4518325415524767873) shift_test(-0xfffffffffffffffff) test_check "string & char" test_ok("abcd" == "abcd") test_ok("abcd" =~ /abcd/) test_ok("abcd" === "abcd") # compile time string concatenation test_ok("ab" "cd" == "abcd") test_ok("#{22}aa" "cd#{44}" == "22aacd44") test_ok("#{22}aa" "cd#{44}" "55" "#{66}" == "22aacd445566") test_ok("abc" !~ /^$/) test_ok("abc\n" !~ /^$/) test_ok("abc" !~ /^d*$/) test_ok(("abc" =~ /d*$/) == 3) test_ok("" =~ /^$/) test_ok("\n" =~ /^$/) test_ok("a\n\n" =~ /^$/) test_ok("abcabc" =~ /.*a/ && $& == "abca") test_ok("abcabc" =~ /.*c/ && $& == "abcabc") test_ok("abcabc" =~ /.*?a/ && $& == "a") test_ok("abcabc" =~ /.*?c/ && $& == "abc") test_ok(/(.|\n)*?\n(b|\n)/ =~ "a\nb\n\n" && $& == "a\nb") test_ok(/^(ab+)+b/ =~ "ababb" && $& == "ababb") test_ok(/^(?:ab+)+b/ =~ "ababb" && $& == "ababb") test_ok(/^(ab+)+/ =~ "ababb" && $& == "ababb") test_ok(/^(?:ab+)+/ =~ "ababb" && $& == "ababb") test_ok(/(\s+\d+){2}/ =~ " 1 2" && $& == " 1 2") test_ok(/(?:\s+\d+){2}/ =~ " 1 2" && $& == " 1 2") $x = <') == "") # character constants(assumes ASCII) test_ok("a"[0] == ?a) test_ok(?a == ?a) test_ok(?\C-a == 1) test_ok(?\M-a == 225) test_ok(?\M-\C-a == 129) test_ok("a".upcase![0] == ?A) test_ok("A".downcase![0] == ?a) test_ok("abc".tr!("a-z", "A-Z") == "ABC") test_ok("aabbcccc".tr_s!("a-z", "A-Z") == "ABC") test_ok("abcc".squeeze!("a-z") == "abc") test_ok("abcd".delete!("bc") == "ad") $x = "abcdef" $y = [ ?a, ?b, ?c, ?d, ?e, ?f ] $bad = false $x.each_byte {|i| if i != $y.shift $bad = true break end } test_ok(!$bad) s = "a string" s[0..s.size]="another string" test_ok(s == "another string") s = < Object) == -1) test_ok((Object <=> String) == 1) test_ok((Array <=> String) == nil) test_check "clone" foo = Object.new def foo.test "test" end bar = foo.clone def bar.test2 "test2" end test_ok(bar.test2 == "test2") test_ok(bar.test == "test") test_ok(foo.test == "test") begin foo.test2 test_ok false rescue NoMethodError test_ok true end module M001; end module M002; end module M003; include M002; end module M002; include M001; end module M003; include M002; end test_ok(M003.ancestors == [M003, M002, M001]) test_check "marshal" $x = [1,2,3,[4,5,"foo"],{1=>"bar"},2.5,fact(30)] $y = Marshal.dump($x) test_ok($x == Marshal.load($y)) StrClone=String.clone; test_ok(Marshal.load(Marshal.dump(StrClone.new("abc"))).class == StrClone) [[1,2,3,4], [81, 2, 118, 3146]].each { |w,x,y,z| a = (x.to_f + y.to_f / z.to_f) * Math.exp(w.to_f / (x.to_f + y.to_f / z.to_f)) ma = Marshal.dump(a) b = Marshal.load(ma) test_ok(a == b) } test_check "pack" $format = "c2x5CCxsdils_l_a6"; # Need the expression in here to force ary[5] to be numeric. This avoids # test2 failing because ary2 goes str->numeric->str and ary does not. ary = [1,-100,127,128,32767,987.654321098 / 100.0,12345,123456,-32767,-123456,"abcdef"] $x = ary.pack($format) ary2 = $x.unpack($format) test_ok(ary.length == ary2.length) test_ok(ary.join(':') == ary2.join(':')) test_ok($x =~ /def/) $x = [-1073741825] test_ok($x.pack("q").unpack("q") == $x) test_check "math" test_ok(Math.sqrt(4) == 2) include Math test_ok(sqrt(4) == 2) test_check "struct" struct_test = Struct.new("Test", :foo, :bar) test_ok(struct_test == Struct::Test) test = struct_test.new(1, 2) test_ok(test.foo == 1 && test.bar == 2) test_ok(test[0] == 1 && test[1] == 2) a, b = test.to_a test_ok(a == 1 && b == 2) test[0] = 22 test_ok(test.foo == 22) test.bar = 47 test_ok(test.bar == 47) test_check "variable" test_ok($$.instance_of?(Fixnum)) # read-only variable begin $$ = 5 test_ok false rescue NameError test_ok true end foobar = "foobar" $_ = foobar test_ok($_ == foobar) class Gods @@rule = "Uranus" # private to Gods def ruler0 @@rule end def self.ruler1 # <= per method definition style @@rule end class << self # <= multiple method definition style def ruler2 @@rule end end end module Olympians @@rule ="Zeus" def ruler3 @@rule end end class Titans < Gods @@rule = "Cronus" # do not affect @@rule in Gods include Olympians def ruler4 @@rule end end test_ok(Gods.new.ruler0 == "Uranus") test_ok(Gods.ruler1 == "Uranus") test_ok(Gods.ruler2 == "Uranus") test_ok(Titans.ruler1 == "Uranus") test_ok(Titans.ruler2 == "Uranus") atlas = Titans.new test_ok(atlas.ruler0 == "Uranus") test_ok(atlas.ruler3 == "Zeus") test_ok(atlas.ruler4 == "Cronus") test_check "trace" $x = 1234 $y = 0 trace_var :$x, proc{$y = $x} $x = 40414 test_ok($y == $x) untrace_var :$x $x = 19660208 test_ok($y != $x) trace_var :$x, proc{$x *= 2} $x = 5 test_ok($x == 10) untrace_var :$x test_check "defined?" test_ok(defined?($x)) # global variable test_ok(defined?($x) == 'global-variable')# returns description foo=5 test_ok(defined?(foo)) # local variable test_ok(defined?(Array)) # constant test_ok(defined?(Object.new)) # method test_ok(!defined?(Object.print))# private method test_ok(defined?(1 == 2)) # operator expression class Foo def foo p :foo end protected :foo def bar(f) test_ok(defined?(self.foo)) test_ok(defined?(f.foo)) end end f = Foo.new test_ok(defined?(f.foo) == nil) f.bar(f) def defined_test return !defined?(yield) end test_ok(defined_test) # not iterator test_ok(!defined_test{}) # called as iterator test_check "alias" class Alias0 def foo; "foo" end end class Alias1 0 printf "test: %d failed %d\n", $ntest, $failed else printf "end of test(test: %d)\n", $ntest end 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619
/*
   SSSD

   System Database

   Copyright (C) Simo Sorce <ssorce@redhat.com> 2008

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "util/util.h"
#include "db/sysdb_private.h"
#include "confdb/confdb.h"
#include <time.h>


struct ldb_dn *sysdb_custom_subtree_dn(struct sysdb_ctx *ctx, void *memctx,
                                       const char *domain,
                                       const char *subtree_name)
{
    return ldb_dn_new_fmt(memctx, ctx->ldb, SYSDB_TMPL_CUSTOM_SUBTREE,
                          subtree_name, domain);
}
struct ldb_dn *sysdb_custom_dn(struct sysdb_ctx *ctx, void *memctx,
                                const char *domain, const char *object_name,
                                const char *subtree_name)
{
    return ldb_dn_new_fmt(memctx, ctx->ldb, SYSDB_TMPL_CUSTOM, object_name,
                          subtree_name, domain);
}

struct ldb_dn *sysdb_user_dn(struct sysdb_ctx *ctx, void *memctx,
                             const char *domain, const char *name)
{
    return ldb_dn_new_fmt(memctx, ctx->ldb, SYSDB_TMPL_USER, name, domain);
}

struct ldb_dn *sysdb_group_dn(struct sysdb_ctx *ctx, void *memctx,
                              const char *domain, const char *name)
{
    return ldb_dn_new_fmt(memctx, ctx->ldb, SYSDB_TMPL_GROUP, name, domain);
}

struct ldb_dn *sysdb_domain_dn(struct sysdb_ctx *ctx, void *memctx,
                              const char *domain)
{
    return ldb_dn_new_fmt(memctx, ctx->ldb, SYSDB_DOM_BASE, domain);
}

struct ldb_context *sysdb_ctx_get_ldb(struct sysdb_ctx *ctx)
{
    return ctx->ldb;
}

struct sysdb_attrs *sysdb_new_attrs(TALLOC_CTX *memctx)
{
    return talloc_zero(memctx, struct sysdb_attrs);
}

static int sysdb_attrs_get_el_int(struct sysdb_attrs *attrs, const char *name,
                                  bool alloc, struct ldb_message_element **el)
{
    struct ldb_message_element *e = NULL;
    int i;

    for (i = 0; i < attrs->num; i++) {
        if (strcasecmp(name, attrs->a[i].name) == 0)
            e = &(attrs->a[i]);
    }

    if (!e && alloc) {
        e = talloc_realloc(attrs, attrs->a,
                           struct ldb_message_element, attrs->num+1);
        if (!e) return ENOMEM;
        attrs->a = e;

        e[attrs->num].name = talloc_strdup(e, name);
        if (!e[attrs->num].name) return ENOMEM;

        e[attrs->num].num_values = 0;
        e[attrs->num].values = NULL;
        e[attrs->num].flags = 0;

        e = &(attrs->a[attrs->num]);
        attrs->num++;
    }

    if (!e) {
        return ENOENT;
    }

    *el = e;

    return EOK;
}

int sysdb_attrs_get_el(struct sysdb_attrs *attrs, const char *name,
                       struct ldb_message_element **el)
{
    return sysdb_attrs_get_el_int(attrs, name, true, el);
}

int sysdb_attrs_get_string(struct sysdb_attrs *attrs, const char *name,
                           const char **string)
{
    struct ldb_message_element *el;
    int ret;

    ret = sysdb_attrs_get_el_int(attrs, name, false, &el);
    if (ret) {
        return ret;
    }

    if (el->num_values != 1) {
        return ERANGE;
    }

    *string = (const char *)el->values[0].data;
    return EOK;
}

int sysdb_attrs_get_string_array(struct sysdb_attrs *attrs, const char *name,
                                 TALLOC_CTX *mem_ctx, const char ***string)
{
    struct ldb_message_element *el;
    int ret;
    unsigned int u;
    const char **a;

    ret = sysdb_attrs_get_el_int(attrs, name, false, &el);
    if (ret) {
        return ret;
    }

    a = talloc_array(mem_ctx, const char *, el->num_values + 1);
    if (a == NULL) {
        return ENOMEM;
    }

    memset(a, 0, sizeof(const char *) * (el->num_values + 1));

    for(u = 0; u < el->num_values; u++) {
        a[u] = talloc_strndup(a, (const char *)el->values[u].data,
                              el->values[u].length);
        if (a[u] == NULL) {
            talloc_free(a);
            return ENOMEM;
        }
    }

    *string = a;
    return EOK;
}

int sysdb_attrs_add_val(struct sysdb_attrs *attrs,
                        const char *name, const struct ldb_val *val)
{
    struct ldb_message_element *el = NULL;
    struct ldb_val *vals;
    int ret;

    ret = sysdb_attrs_get_el(attrs, name, &el);

    vals = talloc_realloc(attrs->a, el->values,
                          struct ldb_val, el->num_values+1);
    if (!vals) return ENOMEM;

    vals[el->num_values] = ldb_val_dup(vals, val);
    if (vals[el->num_values].data == NULL &&
        vals[el->num_values].length != 0) {
        return ENOMEM;
    }

    el->values = vals;
    el->num_values++;

    return EOK;
}

int sysdb_attrs_add_string(struct sysdb_attrs *attrs,
                           const char *name, const char *str)
{
    struct ldb_val v;

    v.data = (uint8_t *)discard_const(str);
    v.length = strlen(str);

    return sysdb_attrs_add_val(attrs, name, &v);
}

int sysdb_attrs_add_bool(struct sysdb_attrs *attrs,
                         const char *name, bool value)
{
    if(value) {
        return sysdb_attrs_add_string(attrs, name, "TRUE");
    }

    return sysdb_attrs_add_string(attrs, name, "FALSE");
}

int sysdb_attrs_steal_string(struct sysdb_attrs *attrs,
                             const char *name, char *str)
{
    struct ldb_message_element *el = NULL;
    struct ldb_val *vals;
    int ret;

    ret = sysdb_attrs_get_el(attrs, name, &el);

    vals = talloc_realloc(attrs->a, el->values,
                          struct ldb_val, el->num_values+1);
    if (!vals) return ENOMEM;
    el->values = vals;

    /* now steal and assign the string */
    talloc_steal(el->values, str);

    el->values[el->num_values].data = (uint8_t *)str;
    el->values[el->num_values].length = strlen(str);
    el->num_values++;

    return EOK;
}

int sysdb_attrs_add_long(struct sysdb_attrs *attrs,
                         const char *name, long value)
{
    struct ldb_val v;
    char *str;
    int ret;

    str = talloc_asprintf(attrs, "%ld", value);
    if (!str) return ENOMEM;

    v.data = (uint8_t *)str;
    v.length = strlen(str);

    ret = sysdb_attrs_add_val(attrs, name, &v);
    talloc_free(str);

    return ret;
}

int sysdb_attrs_add_uint32(struct sysdb_attrs *attrs,
                           const char *name, uint32_t value)
{
    unsigned long val = value;
    struct ldb_val v;
    char *str;
    int ret;

    str = talloc_asprintf(attrs, "%lu", val);
    if (!str) return ENOMEM;

    v.data = (uint8_t *)str;
    v.length = strlen(str);

    ret = sysdb_attrs_add_val(attrs, name, &v);
    talloc_free(str);

    return ret;
}

int sysdb_attrs_add_time_t(struct sysdb_attrs *attrs,
                           const char *name, time_t value)
{
    long long val = value;
    struct ldb_val v;
    char *str;
    int ret;

    str = talloc_asprintf(attrs, "%lld", val);
    if (!str) return ENOMEM;

    v.data = (uint8_t *)str;
    v.length = strlen(str);

    ret = sysdb_attrs_add_val(attrs, name, &v);
    talloc_free(str);

    return ret;
}

int sysdb_attrs_users_from_str_list(struct sysdb_attrs *attrs,
                                    const char *attr_name,
                                    const char *domain,
                                    const char **list)
{
    struct ldb_message_element *el = NULL;
    struct ldb_val *vals;
    int i, j, num;
    char *member;
    int ret;

    ret = sysdb_attrs_get_el(attrs, attr_name, &el);
    if (ret) {
        return ret;
    }

    for (num = 0; list[num]; num++) /* count */ ;

    vals = talloc_realloc(attrs->a, el->values,
                          struct ldb_val, el->num_values + num);
    if (!vals) {
        return ENOMEM;
    }
    el->values = vals;

    DEBUG(9, ("Adding %d members to existing %d ones\n",
              num, el->num_values));

    for (i = 0, j = el->num_values; i < num; i++) {

        member = sysdb_user_strdn(el->values, domain, list[i]);
        if (!member) {
            DEBUG(4, ("Failed to get user dn for [%s]\n", list[i]));
            continue;
        }
        el->values[j].data = (uint8_t *)member;
        el->values[j].length = strlen(member);
        j++;

        DEBUG(7, ("    member #%d: [%s]\n", i, member));
    }
    el->num_values = j;

    return EOK;
}

int sysdb_attrs_users_from_ldb_vals(struct sysdb_attrs *attrs,
                                    const char *attr_name,
                                    const char *domain,
                                    struct ldb_val *values,
                                    int num_values)
{
    struct ldb_message_element *el = NULL;
    struct ldb_val *vals;
    int i, j;
    char *member;
    int ret;

    ret = sysdb_attrs_get_el(attrs, attr_name, &el);
    if (ret) {
        return ret;
    }

    vals = talloc_realloc(attrs->a, el->values, struct ldb_val,
                          el->num_values + num_values);
    if (!vals) {
        return ENOMEM;
    }
    el->values = vals;

    DEBUG(9, ("Adding %d members to existing %d ones\n",
              num_values, el->num_values));

    for (i = 0, j = el->num_values; i < num_values; i++) {
        member = sysdb_user_strdn(el->values, domain,
                                  (char *)values[i].data);
        if (!member) {
            DEBUG(4, ("Failed to get user dn for [%s]\n",
                      (char *)values[i].data));
            return ENOMEM;
        }
        el->values[j].data = (uint8_t *)member;
        el->values[j].length = strlen(member);
        j++;

        DEBUG(7, ("    member #%d: [%s]\n", i, member));
    }
    el->num_values = j;

    return EOK;
}

static char *build_dom_dn_str_escape(TALLOC_CTX *memctx, const char *template,
                                     const char *domain, const char *name)
{
    char *ret;
    int l;

    l = strcspn(name, ",=\n+<>#;\\\"");
    if (name[l] != '\0') {
        struct ldb_val v;
        char *tmp;

        v.data = discard_const_p(uint8_t, name);
        v.length = strlen(name);

        tmp = ldb_dn_escape_value(memctx, v);
        if (!tmp) {
            return NULL;
        }

        ret = talloc_asprintf(memctx, template, tmp, domain);
        talloc_zfree(tmp);
        if (!ret) {
            return NULL;
        }

        return ret;
    }

    ret = talloc_asprintf(memctx, template, name, domain);
    if (!ret) {
        return NULL;
    }

    return ret;
}

char *sysdb_user_strdn(TALLOC_CTX *memctx,
                       const char *domain, const char *name)
{
    return build_dom_dn_str_escape(memctx, SYSDB_TMPL_USER, domain, name);
}

char *sysdb_group_strdn(TALLOC_CTX *memctx,
                        const char *domain, const char *name)
{
    return build_dom_dn_str_escape(memctx, SYSDB_TMPL_GROUP, domain, name);
}

/* TODO: make a more complete and precise mapping */
int sysdb_error_to_errno(int ldberr)
{
    switch (ldberr) {
    case LDB_SUCCESS:
        return EOK;
    case LDB_ERR_OPERATIONS_ERROR:
        return EIO;
    case LDB_ERR_NO_SUCH_OBJECT:
        return ENOENT;
    case LDB_ERR_BUSY:
        return EBUSY;
    case LDB_ERR_ENTRY_ALREADY_EXISTS:
        return EEXIST;
    default:
        return EFAULT;
    }
}

/* =Transactions========================================================== */

int sysdb_transaction_start(struct sysdb_ctx *ctx)
{
    int ret;

    ret = ldb_transaction_start(ctx->ldb);
    if (ret != LDB_SUCCESS) {
        DEBUG(1, ("Failed to start ldb transaction! (%d)\n", ret));
    }
    return sysdb_error_to_errno(ret);
}

int sysdb_transaction_commit(struct sysdb_ctx *ctx)
{
    int ret;

    ret = ldb_transaction_commit(ctx->ldb);
    if (ret != LDB_SUCCESS) {
        DEBUG(1, ("Failed to commit ldb transaction! (%d)\n", ret));
    }
    return sysdb_error_to_errno(ret);
}

int sysdb_transaction_cancel(struct sysdb_ctx *ctx)
{
    int ret;

    ret = ldb_transaction_cancel(ctx->ldb);
    if (ret != LDB_SUCCESS) {
        DEBUG(1, ("Failed to cancel ldb transaction! (%d)\n", ret));
    }
    return sysdb_error_to_errno(ret);
}

/* =Initialization======================================================== */

static int sysdb_domain_init_internal(TALLOC_CTX *mem_ctx,
                                      struct sss_domain_info *domain,
                                      const char *db_path,
                                      bool allow_upgrade,
                                      struct sysdb_ctx **_ctx);

static int sysdb_get_db_file(TALLOC_CTX *mem_ctx,
                             const char *provider, const char *name,
                             const char *base_path, char **_ldb_file)
{
    char *ldb_file;

    /* special case for the local domain */
    if (strcasecmp(provider, "local") == 0) {
        ldb_file = talloc_asprintf(mem_ctx, "%s/"LOCAL_SYSDB_FILE,
                                   base_path);
    } else {
        ldb_file = talloc_asprintf(mem_ctx, "%s/"CACHE_SYSDB_FILE,
                                   base_path, name);
    }
    if (!ldb_file) {
        return ENOMEM;
    }

    *_ldb_file = ldb_file;
    return EOK;
}

/* serach all groups that have a memberUid attribute.
 * change it into a member attribute for a user of same domain.
 * remove the memberUid attribute
 * add the new member attribute
 * finally stop indexing memberUid
 * upgrade version to 0.2
 */
static int sysdb_upgrade_01(TALLOC_CTX *mem_ctx,
                            struct ldb_context *ldb,
                            const char **ver)
{
    struct ldb_message_element *el;
    struct ldb_result *res;
    struct ldb_dn *basedn;
    struct ldb_dn *mem_dn;
    struct ldb_message *msg;
    const struct ldb_val *val;
    const char *filter = "(&(memberUid=*)(objectclass=group))";
    const char *attrs[] = { "memberUid", NULL };
    const char *mdn;
    char *domain;
    int ret, i, j;

    basedn = ldb_dn_new(mem_ctx, ldb, "cn=sysdb");
    if (!basedn) {
        ret = EIO;
        goto done;
    }

    ret = ldb_search(ldb, mem_ctx, &res,
                     basedn, LDB_SCOPE_SUBTREE,
                     attrs, filter);
    if (ret != LDB_SUCCESS) {
        ret = EIO;
        goto done;
    }

    ret = ldb_transaction_start(ldb);
    if (ret != LDB_SUCCESS) {
        ret = EIO;
        goto done;
    }

    for (i = 0; i < res->count; i++) {
        el = ldb_msg_find_element(res->msgs[i], "memberUid");
        if (!el) {
            DEBUG(1, ("memberUid is missing from message [%s], skipping\n",
                      ldb_dn_get_linearized(res->msgs[i]->dn)));
            continue;
        }

        /* create modification message */
        msg = ldb_msg_new(mem_ctx);
        if (!msg) {
            ret = ENOMEM;
            goto done;
        }
        msg->dn = res->msgs[i]->dn;

        ret = ldb_msg_add_empty(msg, "memberUid", LDB_FLAG_MOD_DELETE, NULL);
        if (ret != LDB_SUCCESS) {
            ret = ENOMEM;
            goto done;
        }

        ret = ldb_msg_add_empty(msg, SYSDB_MEMBER, LDB_FLAG_MOD_ADD, NULL);
        if (ret != LDB_SUCCESS) {
            ret = ENOMEM;
            goto done;
        }

        /* get domain name component value */
        val = ldb_dn_get_component_val(res->msgs[i]->dn, 2);
        domain = talloc_strndup(mem_ctx, (const char *)val->data, val->length);
        if (!domain) {
            ret = ENOMEM;
            goto done;
        }

        for (j = 0; j < el->num_values; j++) {
            mem_dn = ldb_dn_new_fmt(mem_ctx, ldb, SYSDB_TMPL_USER,
                                    (const char *)el->values[j].data, domain);
            if (!mem_dn) {
                ret = ENOMEM;
                goto done;
            }

            mdn = talloc_strdup(msg, ldb_dn_get_linearized(mem_dn));
            if (!mdn) {
                ret = ENOMEM;
                goto done;
            }
            ret = ldb_msg_add_string(msg, SYSDB_MEMBER, mdn);
            if (ret != LDB_SUCCESS) {
                ret = ENOMEM;
                goto done;
            }

            talloc_zfree(mem_dn);
        }

        /* ok now we are ready to modify the entry */
        ret = ldb_modify(ldb, msg);
        if (ret != LDB_SUCCESS) {
            ret = sysdb_error_to_errno(ret);
            goto done;
        }

        talloc_zfree(msg);
    }

    /* conversion done, upgrade version number */
    msg = ldb_msg_new(mem_ctx);
    if (!msg) {
        ret = ENOMEM;
        goto done;
    }
    msg->dn = ldb_dn_new(mem_ctx, ldb, "cn=sysdb");
    if (!msg->dn) {
        ret = ENOMEM;
        goto done;
    }

    ret = ldb_msg_add_empty(msg, "version", LDB_FLAG_MOD_REPLACE, NULL);
    if (ret != LDB_SUCCESS) {
        ret = ENOMEM;
        goto done;
    }
    ret = ldb_msg_add_string(msg, "version", SYSDB_VERSION_0_2);
    if (ret != LDB_SUCCESS) {
        ret = ENOMEM;
        goto done;
    }

    ret = ldb_modify(ldb, msg);
    if (ret != LDB_SUCCESS) {
        ret = sysdb_error_to_errno(ret);
        goto done;
    }

    ret = EOK;

done:
    if (ret != EOK) {
        ldb_transaction_cancel(ldb);
    } else {
        ret = ldb_transaction_commit(ldb);
        if (ret != LDB_SUCCESS) {
            return EIO;
        }

        *ver = SYSDB_VERSION_0_2;
    }

    return ret;
}

static int sysdb_check_upgrade_02(TALLOC_CTX *mem_ctx,
                                  struct sss_domain_info *domains,
                                  const char *db_path)
{
    TALLOC_CTX *tmp_ctx = NULL;
    struct ldb_context *ldb;
    char *ldb_file;
    struct sysdb_ctx *ctx;
    struct sss_domain_info *dom;
    struct ldb_message_element *el;
    struct ldb_message *msg;
    struct ldb_result *res;
    struct ldb_dn *verdn;
    const char *version = NULL;
    bool do_02_upgrade = false;
    bool ctx_trans = false;
    int ret;

    tmp_ctx = talloc_new(mem_ctx);
    if (!tmp_ctx) {
        return ENOMEM;
    }

    ret = sysdb_get_db_file(mem_ctx,
                            "local", "UPGRADE",
                            db_path, &ldb_file);
    if (ret != EOK) {
        goto exit;
    }

    ldb = ldb_init(tmp_ctx, NULL);
    if (!ldb) {
        ret = EIO;
        goto exit;
    }

    ret = ldb_set_debug(ldb, ldb_debug_messages, NULL);
    if (ret != LDB_SUCCESS) {
        ret = EIO;
        goto exit;
    }

#ifdef SYSDB_TEST
    ldb_set_modules_dir(ctx->ldb, ABS_BUILD_DIR"/.libs");
#endif

    ret = ldb_connect(ldb, ldb_file, 0, NULL);
    if (ret != LDB_SUCCESS) {
        ret = EIO;
        goto exit;
    }

    verdn = ldb_dn_new(tmp_ctx, ldb, "cn=sysdb");
    if (!verdn) {
        ret = EIO;
        goto exit;
    }

    ret = ldb_search(ldb, tmp_ctx, &res,
                     verdn, LDB_SCOPE_BASE,
                     NULL, NULL);
    if (ret != LDB_SUCCESS) {
        ret = EIO;
        goto exit;
    }
    if (res->count > 1) {
        ret = EIO;
        goto exit;
    }

    if (res->count == 1) {
        el = ldb_msg_find_element(res->msgs[0], "version");
        if (el) {
            if (el->num_values != 1) {
                ret = EINVAL;
                goto exit;
            }
            version = talloc_strndup(tmp_ctx,
                                     (char *)(el->values[0].data),
                                     el->values[0].length);
            if (!version) {
                ret = ENOMEM;
                goto exit;
            }

            if (strcmp(version, SYSDB_VERSION) == 0) {
                /* all fine, return */
                ret = EOK;
                goto exit;
            }

            DEBUG(4, ("Upgrading DB from version: %s\n", version));

            if (strcmp(version, SYSDB_VERSION_0_1) == 0) {
                /* convert database */
                ret = sysdb_upgrade_01(tmp_ctx, ldb, &version);
                if (ret != EOK) goto exit;
            }

            if (strcmp(version, SYSDB_VERSION_0_2) == 0) {
                /* need to convert database to split files */
                do_02_upgrade = true;
            }

        }
    }

    if (!do_02_upgrade) {
        /* not a v2 upgrade, return and let the normal code take over any
        * further upgrade */
        ret = EOK;
        goto exit;
    }

    /* == V2->V3 UPGRADE == */

    DEBUG(0, ("UPGRADING DB TO VERSION %s\n", SYSDB_VERSION_0_3));

    /* ldb uses posix locks,
     * posix is stupid and kills all locks when you close *any* file
     * descriptor associated to the same file.
     * Therefore we must close and reopen the ldb file here */

    /* == Backup and reopen ldb == */

    /* close */
    talloc_zfree(ldb);

    /* backup*/
    ret = backup_file(ldb_file, 0);
    if (ret != EOK) {
        goto exit;
    }

    /* reopen */
    ldb = ldb_init(tmp_ctx, NULL);
    if (!ldb) {
        ret = EIO;
        goto exit;
    }

    ret = ldb_set_debug(ldb, ldb_debug_messages, NULL);
    if (ret != LDB_SUCCESS) {
        ret = EIO;
        goto exit;
    }

    ret = ldb_connect(ldb, ldb_file, 0, NULL);
    if (ret != LDB_SUCCESS) {
        ret = EIO;
        goto exit;
    }

    /* open a transaction */
    ret = ldb_transaction_start(ldb);
    if (ret != LDB_SUCCESS) {
        DEBUG(1, ("Failed to start ldb transaction! (%d)\n", ret));
        ret = EIO;
        goto exit;
    }

    /* == Upgrade contents == */

    for (dom = domains; dom; dom = dom->next) {
        struct ldb_dn *domain_dn;
        struct ldb_dn *users_dn;
        struct ldb_dn *groups_dn;
        int i;

        /* skip local */
        if (strcasecmp(dom->provider, "local") == 0) {
            continue;
        }

        /* create new dom db */
        ret = sysdb_domain_init_internal(tmp_ctx, dom,
                                         db_path, false, &ctx);
        if (ret != EOK) {
            goto done;
        }

        ret = ldb_transaction_start(ctx->ldb);
        if (ret != LDB_SUCCESS) {
            DEBUG(1, ("Failed to start ldb transaction! (%d)\n", ret));
            ret = EIO;
            goto done;
        }
        ctx_trans = true;

        /* search all entries for this domain in local,
         * copy them all in the new database,
         * then remove them from local */

        domain_dn = ldb_dn_new_fmt(tmp_ctx, ctx->ldb,
                                   SYSDB_DOM_BASE, ctx->domain->name);
        if (!domain_dn) {
            ret = ENOMEM;
            goto done;
        }

        ret = ldb_search(ldb, tmp_ctx, &res,
                         domain_dn, LDB_SCOPE_SUBTREE,
                         NULL, NULL);
        if (ret != LDB_SUCCESS) {
            ret = EIO;
            goto done;
        }

        users_dn = ldb_dn_new_fmt(tmp_ctx, ctx->ldb,
                                 SYSDB_TMPL_USER_BASE, ctx->domain->name);
        if (!users_dn) {
            ret = ENOMEM;
            goto done;
        }
        groups_dn = ldb_dn_new_fmt(tmp_ctx, ctx->ldb,
                                   SYSDB_TMPL_GROUP_BASE, ctx->domain->name);
        if (!groups_dn) {
            ret = ENOMEM;
            goto done;
        }

        for (i = 0; i < res->count; i++) {

            struct ldb_dn *orig_dn;

            msg = res->msgs[i];

            /* skip pre-created congtainers */
            if ((ldb_dn_compare(msg->dn, domain_dn) == 0) ||
                (ldb_dn_compare(msg->dn, users_dn) == 0) ||
                (ldb_dn_compare(msg->dn, groups_dn) == 0)) {
                continue;
            }

            /* regenerate the DN against the new ldb as it may have different
             * casefolding rules (example: name changing from case insensitive
             * to case sensitive) */
            orig_dn = msg->dn;
            msg->dn = ldb_dn_new(msg, ctx->ldb,
                                 ldb_dn_get_linearized(orig_dn));
            if (!msg->dn) {
                ret = ENOMEM;
                goto done;
            }

            ret = ldb_add(ctx->ldb, msg);
            if (ret != LDB_SUCCESS) {
                DEBUG(0, ("WARNING: Could not add entry %s,"
                          " to new ldb file! (%d [%s])\n",
                          ldb_dn_get_linearized(msg->dn),
                          ret, ldb_errstring(ctx->ldb)));
            }

            ret = ldb_delete(ldb, orig_dn);
            if (ret != LDB_SUCCESS) {
                DEBUG(0, ("WARNING: Could not remove entry %s,"
                          " from old ldb file! (%d [%s])\n",
                          ldb_dn_get_linearized(orig_dn),
                          ret, ldb_errstring(ldb)));
            }
        }

        /* now remove the basic containers from local */
        /* these were optional so debug at level 9 in case
         * of failure just for tracing */
        ret = ldb_delete(ldb, groups_dn);
        if (ret != LDB_SUCCESS) {
            DEBUG(9, ("WARNING: Could not remove entry %s,"
                      " from old ldb file! (%d [%s])\n",
                      ldb_dn_get_linearized(groups_dn),
                      ret, ldb_errstring(ldb)));
        }
        ret = ldb_delete(ldb, users_dn);
        if (ret != LDB_SUCCESS) {
            DEBUG(9, ("WARNING: Could not remove entry %s,"
                      " from old ldb file! (%d [%s])\n",
                      ldb_dn_get_linearized(users_dn),
                      ret, ldb_errstring(ldb)));
        }
        ret = ldb_delete(ldb, domain_dn);
        if (ret != LDB_SUCCESS) {
            DEBUG(9, ("WARNING: Could not remove entry %s,"
                      " from old ldb file! (%d [%s])\n",
                      ldb_dn_get_linearized(domain_dn),
                      ret, ldb_errstring(ldb)));
        }

        ret = ldb_transaction_commit(ctx->ldb);
        if (ret != LDB_SUCCESS) {
            DEBUG(1, ("Failed to commit ldb transaction! (%d)\n", ret));
            ret = EIO;
            goto done;
        }
        ctx_trans = false;

        talloc_zfree(domain_dn);
        talloc_zfree(groups_dn);
        talloc_zfree(users_dn);
        talloc_zfree(res);
    }

    /* conversion done, upgrade version number */
    msg = ldb_msg_new(tmp_ctx);
    if (!msg) {
        ret = ENOMEM;
        goto done;
    }
    msg->dn = ldb_dn_new(tmp_ctx, ldb, "cn=sysdb");
    if (!msg->dn) {
        ret = ENOMEM;
        goto done;
    }

    ret = ldb_msg_add_empty(msg, "version", LDB_FLAG_MOD_REPLACE, NULL);
    if (ret != LDB_SUCCESS) {
        ret = ENOMEM;
        goto done;
    }
    ret = ldb_msg_add_string(msg, "version", SYSDB_VERSION_0_3);
    if (ret != LDB_SUCCESS) {
        ret = ENOMEM;
        goto done;
    }

    ret = ldb_modify(ldb, msg);
    if (ret != LDB_SUCCESS) {
        ret = sysdb_error_to_errno(ret);
        goto done;
    }

    ret = ldb_transaction_commit(ldb);
    if (ret != LDB_SUCCESS) {
        DEBUG(1, ("Failed to commit ldb transaction! (%d)\n", ret));
        ret = EIO;
        goto exit;
    }

    ret = EOK;

done:
    if (ret != EOK) {
        if (ctx_trans) {
            ret = ldb_transaction_cancel(ctx->ldb);
            if (ret != LDB_SUCCESS) {
                DEBUG(1, ("Failed to cancel ldb transaction! (%d)\n", ret));
            }
        }
        ret = ldb_transaction_cancel(ldb);
        if (ret != LDB_SUCCESS) {
            DEBUG(1, ("Failed to cancel ldb transaction! (%d)\n", ret));
        }
    }

exit:
    talloc_free(tmp_ctx);
    return ret;
}

static int sysdb_upgrade_03(struct sysdb_ctx *ctx, const char **ver)
{
    TALLOC_CTX *tmp_ctx;
    int ret;
    struct ldb_message *msg;

    tmp_ctx = talloc_new(ctx);
    if (!tmp_ctx) {
        return ENOMEM;
    }

    DEBUG(0, ("UPGRADING DB TO VERSION %s\n", SYSDB_VERSION_0_4));

    ret = ldb_transaction_start(ctx->ldb);
    if (ret != LDB_SUCCESS) {
        ret = EIO;
        goto done;
    }

    /* Make this database case-sensitive */
    msg = ldb_msg_new(tmp_ctx);
    if (!msg) {
        ret = ENOMEM;
        goto done;
    }
    msg->dn = ldb_dn_new(tmp_ctx, ctx->ldb, "@ATTRIBUTES");
    if (!msg->dn) {
        ret = ENOMEM;
        goto done;
    }

    ret = ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_DELETE, NULL);
    if (ret != LDB_SUCCESS) {
        ret = ENOMEM;
        goto done;
    }

    ret = ldb_modify(ctx->ldb, msg);
    if (ret != LDB_SUCCESS) {
        ret = sysdb_error_to_errno(ret);
        goto done;
    }

    /* conversion done, upgrade version number */
    msg = ldb_msg_new(tmp_ctx);
    if (!msg) {
        ret = ENOMEM;
        goto done;
    }
    msg->dn = ldb_dn_new(tmp_ctx, ctx->ldb, "cn=sysdb");
    if (!msg->dn) {
        ret = ENOMEM;
        goto done;
    }

    ret = ldb_msg_add_empty(msg, "version", LDB_FLAG_MOD_REPLACE, NULL);
    if (ret != LDB_SUCCESS) {
        ret = ENOMEM;
        goto done;
    }
    ret = ldb_msg_add_string(msg, "version", SYSDB_VERSION_0_4);
    if (ret != LDB_SUCCESS) {
        ret = ENOMEM;
        goto done;
    }

    ret = ldb_modify(ctx->ldb, msg);
    if (ret != LDB_SUCCESS) {
        ret = sysdb_error_to_errno(ret);
        goto done;
    }

    ret = EOK;

done:
    talloc_zfree(tmp_ctx);

    if (ret != EOK) {
        ret = ldb_transaction_cancel(ctx->ldb);
    } else {
        ret = ldb_transaction_commit(ctx->ldb);
        *ver = SYSDB_VERSION_0_4;
    }
    if (ret != LDB_SUCCESS) {
        ret = EIO;
    }

    return ret;
}

static int sysdb_upgrade_04(struct sysdb_ctx *ctx, const char **ver)
{
    TALLOC_CTX *tmp_ctx;
    int ret;
    struct ldb_message *msg;

    tmp_ctx = talloc_new(ctx);
    if (!tmp_ctx) {
        return ENOMEM;
    }

    DEBUG(0, ("UPGRADING DB TO VERSION %s\n", SYSDB_VERSION_0_5));

    ret = ldb_transaction_start(ctx->ldb);
    if (ret != LDB_SUCCESS) {
        ret = EIO;
        goto done;
    }

    /* Add new index */
    msg = ldb_msg_new(tmp_ctx);
    if (!msg) {
        ret = ENOMEM;
        goto done;
    }
    msg->dn = ldb_dn_new(tmp_ctx, ctx->ldb, "@INDEXLIST");
    if (!msg->dn) {
        ret = ENOMEM;
        goto done;
    }

    ret = ldb_msg_add_empty(msg, "@IDXATTR", LDB_FLAG_MOD_ADD, NULL);
    if (ret != LDB_SUCCESS) {
        ret = ENOMEM;
        goto done;
    }
    ret = ldb_msg_add_string(msg, "@IDXATTR", "originalDN");
    if (ret != LDB_SUCCESS) {
        ret = ENOMEM;
        goto done;
    }

    ret = ldb_modify(ctx->ldb, msg);
    if (ret != LDB_SUCCESS) {
        ret = sysdb_error_to_errno(ret);
        goto done;
    }

    /* Rebuild memberuid and memberoif attributes */
    msg = ldb_msg_new(tmp_ctx);
    if (!msg) {
        ret = ENOMEM;
        goto done;
    }
    msg->dn = ldb_dn_new(tmp_ctx, ctx->ldb, "@MEMBEROF-REBUILD");
    if (!msg->dn) {
        ret = ENOMEM;
        goto done;
    }

    ret = ldb_add(ctx->ldb, msg);
    if (ret != LDB_SUCCESS) {
        ret = sysdb_error_to_errno(ret);
        goto done;
    }

    /* conversion done, upgrade version number */
    msg = ldb_msg_new(tmp_ctx);
    if (!msg) {
        ret = ENOMEM;
        goto done;
    }
    msg->dn = ldb_dn_new(tmp_ctx, ctx->ldb, "cn=sysdb");
    if (!msg->dn) {
        ret = ENOMEM;
        goto done;
    }

    ret = ldb_msg_add_empty(msg, "version", LDB_FLAG_MOD_REPLACE, NULL);
    if (ret != LDB_SUCCESS) {
        ret = ENOMEM;
        goto done;
    }
    ret = ldb_msg_add_string(msg, "version", SYSDB_VERSION_0_5);
    if (ret != LDB_SUCCESS) {
        ret = ENOMEM;
        goto done;
    }

    ret = ldb_modify(ctx->ldb, msg);
    if (ret != LDB_SUCCESS) {
        ret = sysdb_error_to_errno(ret);
        goto done;
    }

    ret = EOK;

done:
    talloc_zfree(tmp_ctx);

    if (ret != EOK) {
        ret = ldb_transaction_cancel(ctx->ldb);
    } else {
        ret = ldb_transaction_commit(ctx->ldb);
        *ver = SYSDB_VERSION_0_5;
    }
    if (ret != LDB_SUCCESS) {
        ret = EIO;
    }

    return ret;
}

static int sysdb_domain_init_internal(TALLOC_CTX *mem_ctx,
                                      struct sss_domain_info *domain,
                                      const char *db_path,
                                      bool allow_upgrade,
                                      struct sysdb_ctx **_ctx)
{
    TALLOC_CTX *tmp_ctx = NULL;
    struct sysdb_ctx *ctx;
    const char *base_ldif;
    struct ldb_ldif *ldif;
    struct ldb_message *msg;
    struct ldb_message_element *el;
    struct ldb_result *res;
    struct ldb_dn *verdn;
    const char *version = NULL;
    int ret;

    ctx = talloc_zero(mem_ctx, struct sysdb_ctx);
    if (!ctx) {
        return ENOMEM;
    }
    ctx->domain = domain;

    /* The local provider s the only true MPG,
     * for the other domains, the provider actually unrolls MPGs */
    if (strcasecmp(domain->provider, "local") == 0) {
        ctx->mpg = true;
    }

    ret = sysdb_get_db_file(ctx, domain->provider,
                            domain->name, db_path,
                            &ctx->ldb_file);
    if (ret != EOK) {
        return ret;
    }
    DEBUG(5, ("DB File for %s: %s\n", domain->name, ctx->ldb_file));

    ctx->ldb = ldb_init(ctx, NULL);
    if (!ctx->ldb) {
        return EIO;
    }

    ret = ldb_set_debug(ctx->ldb, ldb_debug_messages, NULL);
    if (ret != LDB_SUCCESS) {
        return EIO;
    }

#ifdef SYSDB_TEST
    ldb_set_modules_dir(ctx->ldb, ABS_BUILD_DIR"/.libs");
#endif

    ret = ldb_connect(ctx->ldb, ctx->ldb_file, 0, NULL);
    if (ret != LDB_SUCCESS) {
        return EIO;
    }

    tmp_ctx = talloc_new(ctx);
    if (!tmp_ctx) {
        return ENOMEM;
    }

    verdn = ldb_dn_new(tmp_ctx, ctx->ldb, "cn=sysdb");
    if (!verdn) {
        ret = EIO;
        goto done;
    }

    ret = ldb_search(ctx->ldb, tmp_ctx, &res,
                     verdn, LDB_SCOPE_BASE,
                     NULL, NULL);
    if (ret != LDB_SUCCESS) {
        ret = EIO;
        goto done;
    }
    if (res->count > 1) {
        ret = EIO;
        goto done;
    }

    if (res->count == 1) {
        el = ldb_msg_find_element(res->msgs[0], "version");
        if (el) {
            if (el->num_values != 1) {
                ret = EINVAL;
                goto done;
            }
            version = talloc_strndup(tmp_ctx,
                                     (char *)(el->values[0].data),
                                     el->values[0].length);
            if (!version) {
                ret = ENOMEM;
                goto done;
            }

            if (strcmp(version, SYSDB_VERSION) == 0) {
                /* all fine, return */
                ret = EOK;
                goto done;
            }

            if (!allow_upgrade) {
                DEBUG(0, ("Wrong DB version (got %s expected %s)\n",
                          version, SYSDB_VERSION));
                ret = EINVAL;
                goto done;
            }

            DEBUG(4, ("Upgrading DB [%s] from version: %s\n",
                      domain->name, version));

            if (strcmp(version, SYSDB_VERSION_0_3) == 0) {
                ret = sysdb_upgrade_03(ctx, &version);
                if (ret != EOK) {
                    goto done;
                }
            }

            if (strcmp(version, SYSDB_VERSION_0_4) == 0) {
                ret = sysdb_upgrade_04(ctx, &version);
                goto done;
            }
        }

        DEBUG(0,("Unknown DB version [%s], expected [%s] for domain %s!\n",
                 version?version:"not found", SYSDB_VERSION, domain->name));
        ret = EINVAL;
        goto done;
    }

    /* cn=sysdb does not exists, means db is empty, populate */

    base_ldif = SYSDB_BASE_LDIF;
    while ((ldif = ldb_ldif_read_string(ctx->ldb, &base_ldif))) {
        ret = ldb_add(ctx->ldb, ldif->msg);
        if (ret != LDB_SUCCESS) {
            DEBUG(0, ("Failed to initialize DB (%d, [%s]) for domain %s!\n",
                      ret, ldb_errstring(ctx->ldb), domain->name));
            ret = EIO;
            goto done;
        }
        ldb_ldif_read_free(ctx->ldb, ldif);
    }

    /* == create base domain object == */

    msg = ldb_msg_new(tmp_ctx);
    if (!msg) {
        ret = ENOMEM;
        goto done;
    }
    msg->dn = ldb_dn_new_fmt(msg, ctx->ldb, SYSDB_DOM_BASE, domain->name);
    if (!msg->dn) {
        ret = ENOMEM;
        goto done;
    }
    ret = ldb_msg_add_fmt(msg, "cn", "%s", domain->name);
    if (ret != LDB_SUCCESS) {
        ret = EIO;
        goto done;
    }
    /* do a synchronous add */
    ret = ldb_add(ctx->ldb, msg);
    if (ret != LDB_SUCCESS) {
        DEBUG(0, ("Failed to initialize DB (%d, [%s]) for domain %s!\n",
                  ret, ldb_errstring(ctx->ldb), domain->name));
        ret = EIO;
        goto done;
    }
    talloc_zfree(msg);

    /* == create Users tree == */

    msg = ldb_msg_new(tmp_ctx);
    if (!msg) {
        ret = ENOMEM;
        goto done;
    }
    msg->dn = ldb_dn_new_fmt(msg, ctx->ldb,
                             SYSDB_TMPL_USER_BASE, domain->name);
    if (!msg->dn) {
        ret = ENOMEM;
        goto done;
    }
    ret = ldb_msg_add_fmt(msg, "cn", "Users");
    if (ret != LDB_SUCCESS) {
        ret = EIO;
        goto done;
    }
    /* do a synchronous add */
    ret = ldb_add(ctx->ldb, msg);
    if (ret != LDB_SUCCESS) {
        DEBUG(0, ("Failed to initialize DB (%d, [%s]) for domain %s!\n",
                  ret, ldb_errstring(ctx->ldb), domain->name));
        ret = EIO;
        goto done;
    }
    talloc_zfree(msg);

    /* == create Groups tree == */

    msg = ldb_msg_new(tmp_ctx);
    if (!msg) {
        ret = ENOMEM;
        goto done;
    }
    msg->dn = ldb_dn_new_fmt(msg, ctx->ldb,
                             SYSDB_TMPL_GROUP_BASE, domain->name);
    if (!msg->dn) {
        ret = ENOMEM;
        goto done;
    }
    ret = ldb_msg_add_fmt(msg, "cn", "Groups");
    if (ret != LDB_SUCCESS) {
        ret = EIO;
        goto done;
    }
    /* do a synchronous add */
    ret = ldb_add(ctx->ldb, msg);
    if (ret != LDB_SUCCESS) {
        DEBUG(0, ("Failed to initialize DB (%d, [%s]) for domain %s!\n",
                  ret, ldb_errstring(ctx->ldb), domain->name));
        ret = EIO;
        goto done;
    }
    talloc_zfree(msg);

    ret = EOK;

done:
    if (ret == EOK) {
        *_ctx = ctx;
    }
    talloc_free(tmp_ctx);
    return ret;
}

int sysdb_init(TALLOC_CTX *mem_ctx,
               struct confdb_ctx *cdb,
               const char *alt_db_path,
               bool allow_upgrade,
               struct sysdb_ctx_list **_ctx_list)
{
    struct sysdb_ctx_list *ctx_list;
    struct sss_domain_info *domains, *dom;
    struct sysdb_ctx *ctx;
    int ret;

    ctx_list = talloc_zero(mem_ctx, struct sysdb_ctx_list);
    if (!ctx_list) {
        return ENOMEM;
    }

    if (alt_db_path) {
        ctx_list->db_path = talloc_strdup(ctx_list, alt_db_path);
    } else {
        ctx_list->db_path = talloc_strdup(ctx_list, DB_PATH);
    }
    if (!ctx_list->db_path) {
        talloc_zfree(ctx_list);
        return ENOMEM;
    }

    /* open a db for each backend */
    ret = confdb_get_domains(cdb, &domains);
    if (ret != EOK) {
        talloc_zfree(ctx_list);
        return ret;
    }

    if (allow_upgrade) {
        /* check if we have an old sssd.ldb to upgrade */
        ret = sysdb_check_upgrade_02(ctx_list, domains,
                                     ctx_list->db_path);
        if (ret != EOK) {
            talloc_zfree(ctx_list);
            return ret;
        }
    }

    for (dom = domains; dom; dom = dom->next) {

        ctx_list->dbs = talloc_realloc(ctx_list, ctx_list->dbs,
                                       struct sysdb_ctx *,
                                       ctx_list->num_dbs + 1);
        if (!ctx_list->dbs) {
            talloc_zfree(ctx_list);
            return ENOMEM;
        }

        ret = sysdb_domain_init_internal(ctx_list, dom,
                                         ctx_list->db_path,
                                         allow_upgrade, &ctx);
        if (ret != EOK) {
            talloc_zfree(ctx_list);
            return ret;
        }

        ctx_list->dbs[ctx_list->num_dbs] = ctx;
        ctx_list->num_dbs++;
    }
    if (ctx_list->num_dbs == 0) {
        /* what? .. */
        talloc_zfree(ctx_list);
        return ENOENT;
    }

    *_ctx_list = ctx_list;

    return EOK;
}

int sysdb_domain_init(TALLOC_CTX *mem_ctx,
                      struct sss_domain_info *domain,
                      const char *db_path,
                      struct sysdb_ctx **_ctx)
{
    return sysdb_domain_init_internal(mem_ctx, domain,
                                      db_path, false, _ctx);
}

int sysdb_get_ctx_from_list(struct sysdb_ctx_list *ctx_list,
                            struct sss_domain_info *domain,
                            struct sysdb_ctx **ctx)
{
    int i;

    for (i = 0; i < ctx_list->num_dbs; i++) {
        if (ctx_list->dbs[i]->domain == domain) {
            *ctx = ctx_list->dbs[i];
            return EOK;
        }
        if (strcasecmp(ctx_list->dbs[i]->domain->name, domain->name) == 0) {
            *ctx = ctx_list->dbs[i];
            return EOK;
        }
    }
    /* definitely not found */
    return ENOENT;
}


int compare_ldb_dn_comp_num(const void *m1, const void *m2)
{
    struct ldb_message *msg1 = talloc_get_type(*(void **) discard_const(m1),
                                               struct ldb_message);
    struct ldb_message *msg2 = talloc_get_type(*(void **) discard_const(m2),
                                               struct ldb_message);

    return ldb_dn_get_comp_num(msg2->dn) - ldb_dn_get_comp_num(msg1->dn);
}

int sysdb_attrs_replace_name(struct sysdb_attrs *attrs, const char *oldname,
                             const char *newname)
{
    struct ldb_message_element *e = NULL;
    int i;
    const char *dummy;

    if (attrs == NULL || oldname == NULL || newname == NULL) return EINVAL;

    for (i = 0; i < attrs->num; i++) {
        if (strcasecmp(oldname, attrs->a[i].name) == 0) {
            e = &(attrs->a[i]);
        }
        if (strcasecmp(newname, attrs->a[i].name) == 0) {
            DEBUG(3, ("New attribute name [%s] already exists.\n", newname));
            return EEXIST;
        }
    }

    if (e != NULL) {
        dummy = talloc_strdup(attrs, newname);
        if (dummy == NULL) {
            DEBUG(1, ("talloc_strdup failed.\n"));
            return ENOMEM;
        }

        talloc_free(discard_const(e->name));
        e->name = dummy;
    }

    return EOK;
}