1require 'mkmf'
2
3dir_config("dbm")
4
5if dblib = with_config("dbm-type", nil)
6  dblib = dblib.split(/[ ,]+/)
7else
8  dblib = %w(libc db db2 db1 db5 db4 db3 gdbm_compat gdbm qdbm)
9end
10
11headers = {
12  "libc" => ["ndbm.h"], # 4.3BSD original ndbm, Berkeley DB 1 in 4.4BSD libc.
13  "db" => ["db.h"],
14  "db1" => ["db1/ndbm.h", "db1.h", "ndbm.h"],
15  "db2" => ["db2/db.h", "db2.h", "db.h"],
16  "db3" => ["db3/db.h", "db3.h", "db.h"],
17  "db4" => ["db4/db.h", "db4.h", "db.h"],
18  "db5" => ["db5/db.h", "db5.h", "db.h"],
19  "gdbm_compat" => ["gdbm-ndbm.h", "gdbm/ndbm.h", "ndbm.h"], # GDBM since 1.8.1
20  "gdbm" => ["gdbm-ndbm.h", "gdbm/ndbm.h", "ndbm.h"], # GDBM until 1.8.0
21  "qdbm" => ["qdbm/relic.h", "relic.h"],
22}
23
24class << headers
25  attr_accessor :found
26  attr_accessor :defs
27end
28headers.found = []
29headers.defs = nil
30
31def headers.db_check(db, hdr)
32  old_libs = $libs.dup
33  old_defs = $defs.dup
34  result = db_check2(db, hdr)
35  if !result
36    $libs = old_libs
37    $defs = old_defs
38  end
39  result
40end
41
42def have_declared_libvar(var, headers = nil, opt = "", &b)
43  checking_for checking_message([*var].compact.join(' '), headers, opt) do
44    try_declared_libvar(var, headers, opt, &b)
45  end
46end
47
48def try_declared_libvar(var, headers = nil, opt = "", &b)
49  if try_link(<<"SRC", opt, &b)
50#{cpp_include(headers)}
51/*top*/
52int main(int argc, char *argv[]) {
53  void *conftest_var = &#{var};
54  return 0;
55}
56SRC
57    $defs.push(format("-DHAVE_DECLARED_LIBVAR_%s", var.tr_cpp))
58    true
59  else
60    false
61  end
62end
63
64def have_undeclared_libvar(var, headers = nil, opt = "", &b)
65  checking_for checking_message([*var].compact.join(' '), headers, opt) do
66    try_undeclared_libvar(var, headers, opt, &b)
67  end
68end
69
70def try_undeclared_libvar(var, headers = nil, opt = "", &b)
71  var, type = *var
72  if try_link(<<"SRC", opt, &b)
73#{cpp_include(headers)}
74/*top*/
75int main(int argc, char *argv[]) {
76  typedef #{type || 'int'} conftest_type;
77  extern conftest_type #{var};
78  conftest_type *conftest_var = &#{var};
79  return 0;
80}
81SRC
82    $defs.push(format("-DHAVE_UNDECLARED_LIBVAR_%s", var.tr_cpp))
83    true
84  else
85    false
86  end
87end
88
89def have_empty_macro_dbm_clearerr(headers = nil, opt = "", &b)
90  checking_for checking_message('empty macro of dbm_clearerr(foobarbaz)',
91                                headers, opt) do
92    try_toplevel('dbm_clearerr(foobarbaz)', headers, opt, &b)
93  end
94end
95
96def try_toplevel(src, headers = nil, opt = "", &b)
97  if try_compile(<<"SRC", opt, &b)
98#{cpp_include(headers)}
99/*top*/
100#{src}
101SRC
102    true
103  else
104    false
105  end
106end
107
108
109def headers.db_check2(db, hdr)
110  $defs.push(%{-DRUBYDBM_DBM_HEADER='"#{hdr}"'})
111  $defs.push(%{-DRUBYDBM_DBM_TYPE='"#{db}"'})
112
113  hsearch = nil
114
115  case db
116  when /^db[2-5]?$/
117    hsearch = "-DDB_DBM_HSEARCH"
118  when "gdbm_compat"
119    have_library("gdbm") or return false
120  end
121
122  if !have_type("DBM", hdr, hsearch)
123    return false
124  end
125
126  # 'libc' means ndbm is provided by libc.
127  # 4.3BSD original ndbm is contained in libc.
128  # 4.4BSD (and its derivatives such as NetBSD) contains Berkeley DB 1 in libc.
129  if !(db == 'libc' ? have_func('dbm_open("", 0, 0)', hdr, hsearch) :
130                      have_library(db, 'dbm_open("", 0, 0)', hdr, hsearch))
131    return false
132  end
133
134  # Skip a mismatch of Berkeley DB's ndbm.h and old GDBM library.
135  #
136  # dbm_clearerr() should be available for any ndbm implementation.
137  # It is available since the original (4.3BSD) ndbm and standardized by POSIX.
138  #
139  # However "can't resolve symbol 'dbm_clearerr'" problem may be caused by
140  # header/library mismatch: Berkeley DB ndbm.h and GDBM library until 1.8.3.
141  # GDBM (until 1.8.3) provides dbm_clearerr() as a empty macro in the header
142  # and the library don't provide dbm_clearerr().
143  # Berkeley DB provides dbm_clearerr() as a usual function.
144  # So Berkeley DB header with GDBM library causes the problem.
145  #
146  if !have_func('dbm_clearerr((DBM *)0)', hdr, hsearch)
147    return false
148  end
149
150  # Berkeley DB's ndbm.h (since 1.85 at least) defines DBM_SUFFIX.
151  # Note that _DB_H_ is not defined on Mac OS X because
152  # it uses Berkeley DB 1 but ndbm.h doesn't include db.h.
153  have_db_header = have_macro('DBM_SUFFIX', hdr, hsearch)
154
155  # Old GDBM's ndbm.h, until 1.8.3, defines dbm_clearerr as a macro which
156  # expands to no tokens.
157  have_gdbm_header1 = have_empty_macro_dbm_clearerr(hdr, hsearch)
158
159  # Recent GDBM's ndbm.h, since 1.9, includes gdbm.h and it defines _GDBM_H_.
160  # ndbm compatibility layer of GDBM is provided by libgdbm (until 1.8.0)
161  # and libgdbm_compat (since 1.8.1).
162  have_gdbm_header2 = have_macro('_GDBM_H_', hdr, hsearch)
163
164  # 4.3BSD's ndbm.h defines _DBM_IOERR.
165  # The original ndbm is provided by libc in 4.3BSD.
166  have_ndbm_header = have_macro('_DBM_IOERR', hdr, hsearch)
167
168  # GDBM provides ndbm functions in libgdbm_compat since GDBM 1.8.1.
169  # GDBM's ndbm.h defines _GDBM_H_ since GDBM 1.9.
170  # If _GDBM_H_ is defined, 'gdbm_compat' is required and reject 'gdbm'.
171  if have_gdbm_header2 && db == 'gdbm'
172    return false
173  end
174
175  if have_db_header
176    $defs.push('-DRUBYDBM_DB_HEADER')
177  end
178
179  have_gdbm_header = have_gdbm_header1 | have_gdbm_header2
180  if have_gdbm_header
181    $defs.push('-DRUBYDBM_GDBM_HEADER')
182  end
183
184  # ndbm.h is provided by the original (4.3BSD) ndbm,
185  # Berkeley DB 1 in libc of 4.4BSD and
186  # ndbm compatibility layer of GDBM.
187  # So, try to check header/library mismatch.
188  #
189  # Several (possibly historical) distributions provides libndbm.
190  # It may be Berkeley DB, GDBM or 4.3BSD ndbm.
191  # So mismatch check is not performed for that.
192  # Note that libndbm is searched only when --with-dbm-type=ndbm is
193  # given for configure.
194  #
195  if hdr == 'ndbm.h' && db != 'libc' && db != 'ndbm'
196    if /\Adb\d?\z/ !~ db && have_db_header
197      return false
198    end
199
200    if /\Agdbm/ !~ db && have_gdbm_header
201      return false
202    end
203
204    if have_ndbm_header
205      return false
206    end
207  end
208
209  # Berkeley DB
210  have_func('db_version((int *)0, (int *)0, (int *)0)', hdr, hsearch)
211
212  # GDBM
213  have_gdbm_version = have_declared_libvar("gdbm_version", hdr, hsearch)
214  # gdbm_version is available since very old version (GDBM 1.5 at least).
215  # However it is not declared by ndbm.h until GDBM 1.8.3.
216  # We can't include both ndbm.h and gdbm.h because they both define datum type.
217  # ndbm.h includes gdbm.h and gdbm_version is declared since GDBM 1.9.
218  have_gdbm_version |= have_undeclared_libvar(["gdbm_version", "char *"], hdr, hsearch)
219
220  # QDBM
221  have_var("dpversion", hdr, hsearch)
222
223  # detect mismatch between GDBM header and other library.
224  # If GDBM header is included, GDBM library should be linked.
225  if have_gdbm_header && !have_gdbm_version
226    return false
227  end
228
229  # DBC type is required to disable error messages by Berkeley DB 2 or later.
230  if have_db_header
231    have_type("DBC", hdr, hsearch)
232  end
233
234  if hsearch
235    $defs << hsearch
236    @defs = hsearch
237  end
238  $defs << '-DDBM_HDR="<'+hdr+'>"'
239  @found << hdr
240
241  puts "header: #{hdr}"
242  puts "library: #{db}"
243
244  true
245end
246
247if dblib.any? {|db| headers.fetch(db, ["ndbm.h"]).any? {|hdr| headers.db_check(db, hdr) } }
248  have_header("cdefs.h")
249  have_header("sys/cdefs.h")
250  have_func("dbm_pagfno((DBM *)0)", headers.found, headers.defs)
251  have_func("dbm_dirfno((DBM *)0)", headers.found, headers.defs)
252  convertible_int("datum.dsize", headers.found, headers.defs)
253  create_makefile("dbm")
254end
255