tinycdb-0.81/LICENSE0000644000175000017500000000200014542364467012260 0ustar mjtmjtPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. tinycdb-0.81/Makefile0000644000175000017500000001132214542364427012716 0ustar mjtmjt#! /usr/bin/make -rf # Makefile: make file for tinycdb package # # This file is a part of tinycdb package. # Copyright (C) 2001-2023 Michael Tokarev # Tinycdb is licensed under MIT license. VERSION = 0.81 prefix=/usr/local exec_prefix=$(prefix) bindir=$(exec_prefix)/bin libdir=$(exec_prefix)/lib pkgconfdir=$(libdir)/pkgconfig syslibdir=$(libdir) sysconfdir=/etc includedir=$(prefix)/include mandir=$(prefix)/man NSSCDB_DIR = $(sysconfdir) DESTDIR= CC = cc CFLAGS = -O2 CDEFS = -D_FILE_OFFSET_BITS=64 LD = $(CC) LDFLAGS = AR = ar ARFLAGS = rv RANLIB = ranlib TAR = tar SED = sed BASE = cdb NSS_CDB = libnss_$(BASE).so.2 LIBBASE = lib$(BASE) LIB = $(LIBBASE).a PICLIB = $(LIBBASE)_pic.a SHAREDLIB = $(LIBBASE).so.1 SOLIB = $(LIBBASE).so CDB_USELIB = $(LIB) NSS_USELIB = $(PICLIB) LIBMAP = $(LIBBASE).map INSTALLPROG = cdb # The following assumes GNU CC/LD - # used for building shared libraries only CFLAGS_PIC = -fPIC LDFLAGS_SHARED = -shared LDFLAGS_SONAME = -Wl,--soname= LDFLAGS_VSCRIPT = -Wl,--version-script= CP = cp LIB_SRCS = cdb_init.c cdb_find.c cdb_findnext.c cdb_seq.c cdb_seek.c \ cdb_pack.c cdb_unpack.c \ cdb_make_add.c cdb_make_put.c cdb_make.c cdb_hash.c NSS_SRCS = nss_cdb.c nss_cdb-passwd.c nss_cdb-group.c nss_cdb-spwd.c NSSMAP = nss_cdb.map DISTFILES = LICENSE Makefile cdb.h cdb_int.h $(LIB_SRCS) cdb.c \ $(NSS_SRCS) nss_cdb.h nss_cdb-Makefile \ cdb.3 cdb.1 cdb.5 \ tinycdb.spec tests.sh tests.ok \ $(LIBMAP) $(NSSMAP) \ NEWS all: static static: staticlib cdb staticlib: $(LIB) nss: $(NSS_CDB) piclib: $(PICLIB) sharedlib: $(SHAREDLIB) shared: sharedlib cdb-shared LIB_OBJS = $(LIB_SRCS:.c=.o) LIB_OBJS_PIC = $(LIB_SRCS:.c=.lo) NSS_OBJS = $(NSS_SRCS:.c=.lo) $(LIB): $(LIB_OBJS) -rm -f $@ $(AR) $(ARFLAGS) $@ $(LIB_OBJS) -$(RANLIB) $@ $(PICLIB): $(LIB_OBJS_PIC) -rm -f $@ $(AR) $(ARFLAGS) $@ $(LIB_OBJS_PIC) -$(RANLIB) $@ $(SHAREDLIB): $(LIB_OBJS_PIC) $(LIBMAP) -rm -f $(SOLIB) ln -s $@ $(SOLIB) $(LD) $(LDFLAGS) $(LDFLAGS_SHARED) -o $@ \ $(LDFLAGS_SONAME)$(SHAREDLIB) $(LDFLAGS_VSCRIPT)$(LIBMAP) \ $(LIB_OBJS_PIC) cdb: cdb.o $(CDB_USELIB) $(LD) $(LDFLAGS) -o $@ cdb.o $(CDB_USELIB) cdb-shared: cdb.o $(SHAREDLIB) $(LD) $(LDFLAGS) -o $@ cdb.o $(SHAREDLIB) $(NSS_CDB): $(NSS_OBJS) $(NSS_USELIB) $(NSSMAP) $(LD) $(LDFLAGS) $(LDFLAGS_SHARED) -o $@ \ $(LDFLAGS_SONAME)$@ $(LDFLAGS_VSCRIPT)$(NSSMAP) \ $(NSS_OBJS) $(NSS_USELIB) .SUFFIXES: .SUFFIXES: .c .o .lo .c.o: $(CC) $(CFLAGS) $(CDEFS) -c $< .c.lo: $(CC) $(CFLAGS) $(CDEFS) $(CFLAGS_PIC) -c -o $@ -DNSSCDB_DIR=\"$(NSSCDB_DIR)\" $< cdb.o: cdb.h $(LIB_OBJS) $(LIB_OBJS_PIC): cdb_int.h cdb.h $(NSS_OBJS): nss_cdb.h cdb.h $(LIBBASE).pc: Makefile { echo 'libdir=$(libdir)'; \ echo 'includedir=$(includedir)'; \ echo 'Name: ${LIBBASE}'; \ echo 'Description: tinycdb - Constant Data Base library'; \ echo 'Version: $(VERSION)'; \ echo 'Libs: -L$${libdir} -l$(BASE)'; \ echo 'Cflags: -I$${includedir}'; \ } >$@ clean: -rm -f *.o *.lo core *~ tests.out tests-shared.ok realclean distclean: clean -rm -f $(LIBBASE)[._][aps]* $(NSS_CDB)* cdb cdb-shared $(LIBBASE).pc test tests check: cdb sh ./tests.sh ./cdb > tests.out 2>&1 diff tests.ok tests.out @echo All tests passed test-shared tests-shared check-shared: cdb-shared $(SED) 's/^cdb: /cdb-shared: /' tests-shared.ok LD_LIBRARY_PATH=. sh ./tests.sh ./cdb-shared > tests.out 2>&1 diff tests-shared.ok tests.out rm -f tests-shared.ok @echo All tests passed do_install = \ while [ "$$1" ] ; do \ if [ .$$4 = .- ]; then f=$$1; else f=$$4; fi; \ d=$(DESTDIR)$$3 ; echo installing $$1 to $$d/$$f; \ [ -d $$d ] || mkdir -p $$d || exit 1 ; \ $(CP) $$1 $$d/$$f || exit 1; \ chmod 0$$2 $$d/$$f || exit 1; \ shift 4; \ done install-all: all $(INSTALLPROG) $(LIBBASE).pc set -- \ cdb.h 644 $(includedir) - \ cdb.3 644 $(mandir)/man3 - \ cdb.1 644 $(mandir)/man1 - \ cdb.5 644 $(mandir)/man5 - \ $(INSTALLPROG) 755 $(bindir) cdb \ $(LIB) 644 $(libdir) - \ $(LIBBASE).pc 644 $(pkgconfdir) - \ ; $(do_install) install-nss: nss @set -- \ $(NSS_CDB) 644 $(syslibdir) - \ nss_cdb-Makefile 644 $(sysconfdir) cdb-Makefile \ ; $(do_install) install-sharedlib: sharedlib @set -- \ $(SHAREDLIB) 644 $(libdir) - \ ; $(do_install) ln -sf $(SHAREDLIB) $(DESTDIR)$(libdir)/$(SOLIB) install-piclib: piclib @set -- $(PICLIB) 644 $(libdir) - ; \ $(do_install) install: install-all DNAME = tinycdb-$(VERSION) dist: ../$(DNAME).tar.gz ../$(DNAME).tar.gz: $(DISTFILES) rm -f $@ $(TAR) cfz $@ --transform='s|^|$(DNAME)/|' $(DISTFILES) .PHONY: all clean realclean dist spec .PHONY: test tests check test-shared tests-shared check-shared .PHONY: static staticlib shared sharedlib nss piclib .PHONY: install install-all install-sharedlib install-piclib install-nss tinycdb-0.81/cdb.h0000644000175000017500000001200614542364427012157 0ustar mjtmjt/* cdb.h: public cdb include file * * This file is a part of tinycdb package. * Copyright (C) 2001-2023 Michael Tokarev * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #ifndef TINYCDB_VERSION #define TINYCDB_VERSION 0.80 #ifdef __cplusplus extern "C" { #endif typedef unsigned int cdbi_t; /* compatibility */ /* common routines */ unsigned cdb_hash(const void *buf, unsigned len); unsigned cdb_unpack(const unsigned char buf[4]); void cdb_pack(unsigned num, unsigned char buf[4]); struct cdb { int cdb_fd; /* file descriptor */ /* private members */ unsigned cdb_fsize; /* datafile size */ unsigned cdb_dend; /* end of data ptr */ const unsigned char *cdb_mem; /* mmap'ed file memory */ unsigned cdb_vpos, cdb_vlen; /* found data */ unsigned cdb_kpos, cdb_klen; /* found key */ }; #define CDB_STATIC_INIT {0,0,0,0,0,0,0,0} #define cdb_datapos(c) ((c)->cdb_vpos) #define cdb_datalen(c) ((c)->cdb_vlen) #define cdb_keypos(c) ((c)->cdb_kpos) #define cdb_keylen(c) ((c)->cdb_klen) #define cdb_fileno(c) ((c)->cdb_fd) int cdb_init(struct cdb *cdbp, int fd); void cdb_free(struct cdb *cdbp); int cdb_read(const struct cdb *cdbp, void *buf, unsigned len, unsigned pos); #define cdb_readdata(cdbp, buf) \ cdb_read((cdbp), (buf), cdb_datalen(cdbp), cdb_datapos(cdbp)) #define cdb_readkey(cdbp, buf) \ cdb_read((cdbp), (buf), cdb_keylen(cdbp), cdb_keypos(cdbp)) const void *cdb_get(const struct cdb *cdbp, unsigned len, unsigned pos); #define cdb_getdata(cdbp) \ cdb_get((cdbp), cdb_datalen(cdbp), cdb_datapos(cdbp)) #define cdb_getkey(cdbp) \ cdb_get((cdbp), cdb_keylen(cdbp), cdb_keypos(cdbp)) int cdb_find(struct cdb *cdbp, const void *key, unsigned klen); struct cdb_find { struct cdb *cdb_cdbp; unsigned cdb_hval; const unsigned char *cdb_htp, *cdb_htab, *cdb_htend; unsigned cdb_httodo; const void *cdb_key; unsigned cdb_klen; }; int cdb_findinit(struct cdb_find *cdbfp, struct cdb *cdbp, const void *key, unsigned klen); int cdb_findnext(struct cdb_find *cdbfp); #define cdb_seqinit(cptr, cdbp) ((*(cptr))=2048) int cdb_seqnext(unsigned *cptr, struct cdb *cdbp); /* old simple interface */ /* open file using standard routine, then: */ int cdb_seek(int fd, const void *key, unsigned klen, unsigned *dlenp); int cdb_bread(int fd, void *buf, int len); /* cdb_make */ struct cdb_make { int cdb_fd; /* file descriptor */ /* private */ unsigned cdb_dpos; /* data position so far */ unsigned cdb_rcnt; /* record count so far */ unsigned char cdb_buf[4096]; /* write buffer */ unsigned char *cdb_bpos; /* current buf position */ struct cdb_rl *cdb_rec[256]; /* list of arrays of record infos */ }; enum cdb_put_mode { CDB_PUT_ADD = 0, /* add unconditionnaly, like cdb_make_add() */ #define CDB_PUT_ADD CDB_PUT_ADD CDB_FIND = CDB_PUT_ADD, CDB_PUT_REPLACE, /* replace: do not place to index OLD record */ #define CDB_PUT_REPLACE CDB_PUT_REPLACE CDB_FIND_REMOVE = CDB_PUT_REPLACE, CDB_PUT_INSERT, /* add only if not already exists */ #define CDB_PUT_INSERT CDB_PUT_INSERT CDB_PUT_WARN, /* add unconditionally but ret. 1 if exists */ #define CDB_PUT_WARN CDB_PUT_WARN CDB_PUT_REPLACE0, /* if a record exists, fill old one with zeros */ #define CDB_PUT_REPLACE0 CDB_PUT_REPLACE0 CDB_FIND_FILL0 = CDB_PUT_REPLACE0 }; int cdb_make_start(struct cdb_make *cdbmp, int fd); int cdb_make_add(struct cdb_make *cdbmp, const void *key, unsigned klen, const void *val, unsigned vlen); int cdb_make_exists(struct cdb_make *cdbmp, const void *key, unsigned klen); int cdb_make_find(struct cdb_make *cdbmp, const void *key, unsigned klen, enum cdb_put_mode mode); int cdb_make_put(struct cdb_make *cdbmp, const void *key, unsigned klen, const void *val, unsigned vlen, enum cdb_put_mode mode); int cdb_make_finish(struct cdb_make *cdbmp); #ifdef __cplusplus } /* extern "C" */ #endif #endif /* include guard */ tinycdb-0.81/cdb_int.h0000644000175000017500000000373114542364427013036 0ustar mjtmjt/* cdb_int.h: internal cdb library declarations * * This file is a part of tinycdb package. * Copyright (C) 2001-2023 Michael Tokarev * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include "cdb.h" #include #include #ifndef EPROTO # define EPROTO EINVAL #endif #ifndef internal_function # ifdef __GNUC__ # define internal_function __attribute__((visibility("hidden"))) # else # define internal_function # endif #endif struct cdb_rec { unsigned hval; unsigned rpos; }; struct cdb_rl { struct cdb_rl *next; unsigned cnt; struct cdb_rec rec[254]; }; int _cdb_make_write(struct cdb_make *cdbmp, const unsigned char *ptr, unsigned len); int _cdb_make_fullwrite(int fd, const unsigned char *buf, unsigned len); int _cdb_make_flush(struct cdb_make *cdbmp); int _cdb_make_add(struct cdb_make *cdbmp, unsigned hval, const void *key, unsigned klen, const void *val, unsigned vlen); tinycdb-0.81/cdb_init.c0000644000175000017500000000715514542364427013206 0ustar mjtmjt/* cdb_init.c: cdb_init, cdb_free and cdb_read routines * * This file is a part of tinycdb package. * Copyright (C) 2001-2023 Michael Tokarev * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include #ifdef _WIN32 # include #else # include # ifndef MAP_FAILED # define MAP_FAILED ((void*)-1) # endif #endif #include #include "cdb_int.h" int cdb_init(struct cdb *cdbp, int fd) { struct stat st; unsigned char *mem; unsigned fsize, dend; #ifdef _WIN32 HANDLE hFile, hMapping; #endif /* get file size */ if (fstat(fd, &st) < 0) return -1; /* trivial sanity check: at least toc should be here */ if (st.st_size < 2048) return errno = EPROTO, -1; fsize = st.st_size < 0xffffffffu ? st.st_size : 0xffffffffu; /* memory-map file */ #ifdef _WIN32 hFile = (HANDLE) _get_osfhandle(fd); if (hFile == (HANDLE) -1) return -1; hMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL); if (!hMapping) return -1; mem = (unsigned char *)MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0); CloseHandle(hMapping); if (!mem) return -1; #else mem = (unsigned char*)mmap(NULL, fsize, PROT_READ, MAP_SHARED, fd, 0); if (mem == MAP_FAILED) return -1; #endif /* _WIN32 */ cdbp->cdb_fd = fd; cdbp->cdb_fsize = fsize; cdbp->cdb_mem = mem; #if 0 /* XXX don't know well about madvise syscall -- is it legal to set different options for parts of one mmap() region? There is also posix_madvise() exist, with POSIX_MADV_RANDOM etc... */ #ifdef MADV_RANDOM /* set madvise() parameters. Ignore errors for now if system doesn't support it */ madvise(mem, 2048, MADV_WILLNEED); madvise(mem + 2048, cdbp->cdb_fsize - 2048, MADV_RANDOM); #endif #endif cdbp->cdb_vpos = cdbp->cdb_vlen = 0; cdbp->cdb_kpos = cdbp->cdb_klen = 0; dend = cdb_unpack(mem); if (dend < 2048) dend = 2048; else if (dend >= fsize) dend = fsize; cdbp->cdb_dend = dend; return 0; } void cdb_free(struct cdb *cdbp) { if (cdbp->cdb_mem) { #ifdef _WIN32 UnmapViewOfFile((void*) cdbp->cdb_mem); #else munmap((void*)cdbp->cdb_mem, cdbp->cdb_fsize); #endif /* _WIN32 */ cdbp->cdb_mem = NULL; } cdbp->cdb_fsize = 0; } const void * cdb_get(const struct cdb *cdbp, unsigned len, unsigned pos) { if (pos > cdbp->cdb_fsize || cdbp->cdb_fsize - pos < len) { errno = EPROTO; return NULL; } return cdbp->cdb_mem + pos; } int cdb_read(const struct cdb *cdbp, void *buf, unsigned len, unsigned pos) { const void *data = cdb_get(cdbp, len, pos); if (!data) return -1; memcpy(buf, data, len); return 0; } tinycdb-0.81/cdb_find.c0000644000175000017500000000641114542364427013155 0ustar mjtmjt/* cdb_find.c: cdb_find routine * * This file is a part of tinycdb package. * Copyright (C) 2001-2023 Michael Tokarev * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include "cdb_int.h" int cdb_find(struct cdb *cdbp, const void *key, unsigned klen) { const unsigned char *htp; /* hash table pointer */ const unsigned char *htab; /* hash table */ const unsigned char *htend; /* end of hash table */ unsigned httodo; /* ht bytes left to look */ unsigned pos, n; unsigned hval; if (klen >= cdbp->cdb_dend) /* if key size is too large */ return 0; hval = cdb_hash(key, klen); /* find (pos,n) hash table to use */ /* first 2048 bytes (toc) are always available */ /* (hval % 256) * 8 */ htp = cdbp->cdb_mem + ((hval << 3) & 2047); /* index in toc (256x8) */ n = cdb_unpack(htp + 4); /* table size */ if (!n) /* empty table */ return 0; /* not found */ httodo = n << 3; /* bytes of htab to lookup */ pos = cdb_unpack(htp); /* htab position */ if (n > (cdbp->cdb_fsize >> 3) /* overflow of httodo ? */ || pos < cdbp->cdb_dend /* is htab inside data section ? */ || pos > cdbp->cdb_fsize /* htab start within file ? */ || httodo > cdbp->cdb_fsize - pos) /* entrie htab within file ? */ return errno = EPROTO, -1; htab = cdbp->cdb_mem + pos; /* htab pointer */ htend = htab + httodo; /* after end of htab */ /* htab starting position: rest of hval modulo htsize, 8bytes per elt */ htp = htab + (((hval >> 8) % n) << 3); for(;;) { pos = cdb_unpack(htp + 4); /* record position */ if (!pos) return 0; if (cdb_unpack(htp) == hval) { if (pos > cdbp->cdb_dend - 8) /* key+val lengths */ return errno = EPROTO, -1; if (cdb_unpack(cdbp->cdb_mem + pos) == klen) { if (cdbp->cdb_dend - klen < pos + 8) return errno = EPROTO, -1; if (memcmp(key, cdbp->cdb_mem + pos + 8, klen) == 0) { n = cdb_unpack(cdbp->cdb_mem + pos + 4); pos += 8; if (cdbp->cdb_dend < n || cdbp->cdb_dend - n < pos + klen) return errno = EPROTO, -1; cdbp->cdb_kpos = pos; cdbp->cdb_klen = klen; cdbp->cdb_vpos = pos + klen; cdbp->cdb_vlen = n; return 1; } } } httodo -= 8; if (!httodo) return 0; if ((htp += 8) >= htend) htp = htab; } } tinycdb-0.81/cdb_findnext.c0000644000175000017500000000604414542364427014056 0ustar mjtmjt/* cdb_findnext.c: sequential cdb_find routines * * This file is a part of tinycdb package. * Copyright (C) 2001-2023 Michael Tokarev * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ /* see cdb_find.c for comments */ #include "cdb_int.h" int cdb_findinit(struct cdb_find *cdbfp, struct cdb *cdbp, const void *key, unsigned klen) { unsigned n, pos; cdbfp->cdb_cdbp = cdbp; cdbfp->cdb_key = key; cdbfp->cdb_klen = klen; cdbfp->cdb_hval = cdb_hash(key, klen); cdbfp->cdb_htp = cdbp->cdb_mem + ((cdbfp->cdb_hval << 3) & 2047); n = cdb_unpack(cdbfp->cdb_htp + 4); cdbfp->cdb_httodo = n << 3; if (!n) return 0; pos = cdb_unpack(cdbfp->cdb_htp); if (n > (cdbp->cdb_fsize >> 3) || pos < cdbp->cdb_dend || pos > cdbp->cdb_fsize || cdbfp->cdb_httodo > cdbp->cdb_fsize - pos) return errno = EPROTO, -1; cdbfp->cdb_htab = cdbp->cdb_mem + pos; cdbfp->cdb_htend = cdbfp->cdb_htab + cdbfp->cdb_httodo; cdbfp->cdb_htp = cdbfp->cdb_htab + (((cdbfp->cdb_hval >> 8) % n) << 3); return 1; } int cdb_findnext(struct cdb_find *cdbfp) { struct cdb *cdbp = cdbfp->cdb_cdbp; unsigned pos, n; unsigned klen = cdbfp->cdb_klen; while(cdbfp->cdb_httodo) { pos = cdb_unpack(cdbfp->cdb_htp + 4); if (!pos) return 0; n = cdb_unpack(cdbfp->cdb_htp) == cdbfp->cdb_hval; if ((cdbfp->cdb_htp += 8) >= cdbfp->cdb_htend) cdbfp->cdb_htp = cdbfp->cdb_htab; cdbfp->cdb_httodo -= 8; if (n) { if (pos > cdbp->cdb_fsize - 8) return errno = EPROTO, -1; if (cdb_unpack(cdbp->cdb_mem + pos) == klen) { if (cdbp->cdb_fsize - klen < pos + 8) return errno = EPROTO, -1; if (memcmp(cdbfp->cdb_key, cdbp->cdb_mem + pos + 8, klen) == 0) { n = cdb_unpack(cdbp->cdb_mem + pos + 4); pos += 8; if (cdbp->cdb_fsize < n || cdbp->cdb_fsize - n < pos + klen) return errno = EPROTO, -1; cdbp->cdb_kpos = pos; cdbp->cdb_klen = klen; cdbp->cdb_vpos = pos + klen; cdbp->cdb_vlen = n; return 1; } } } } return 0; } tinycdb-0.81/cdb_seq.c0000644000175000017500000000337414542364427013032 0ustar mjtmjt/* cdb_seq.c: sequential record retrieval routines * * This file is a part of tinycdb package. * Copyright (C) 2001-2023 Michael Tokarev * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include "cdb_int.h" int cdb_seqnext(unsigned *cptr, struct cdb *cdbp) { unsigned klen, vlen; unsigned pos = *cptr; unsigned dend = cdbp->cdb_dend; const unsigned char *mem = cdbp->cdb_mem; if (pos > dend - 8) return 0; klen = cdb_unpack(mem + pos); vlen = cdb_unpack(mem + pos + 4); pos += 8; if (dend - klen < pos || dend - vlen < pos + klen) return errno = EPROTO, -1; cdbp->cdb_kpos = pos; cdbp->cdb_klen = klen; cdbp->cdb_vpos = pos + klen; cdbp->cdb_vlen = vlen; *cptr = pos + klen + vlen; return 1; } tinycdb-0.81/cdb_seek.c0000644000175000017500000000725514542364427013173 0ustar mjtmjt/* cdb_seek.c: old interface for reading cdb file * * This file is a part of tinycdb package. * Copyright (C) 2001-2023 Michael Tokarev * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include #include "cdb_int.h" #ifndef SEEK_SET # define SEEK_SET 0 #endif /* read a chunk from file, ignoring interrupts (EINTR) */ int cdb_bread(int fd, void *buf, int len) { int l; while(len > 0) { do l = read(fd, buf, len); while(l < 0 && errno == EINTR); if (l <= 0) { if (!l) errno = EIO; return -1; } buf = (char*)buf + l; len -= l; } return 0; } /* find a given key in cdb file, seek a file pointer to it's value and place data length to *dlenp. */ int cdb_seek(int fd, const void *key, unsigned klen, unsigned *dlenp) { unsigned htstart; /* hash table start position */ unsigned htsize; /* number of elements in a hash table */ unsigned httodo; /* hash table elements left to look */ unsigned hti; /* hash table index */ unsigned pos; /* position in a file */ unsigned hval; /* key's hash value */ unsigned char rbuf[64]; /* read buffer */ int needseek = 1; /* if we should seek to a hash slot */ hval = cdb_hash(key, klen); pos = (hval & 0xff) << 3; /* position in TOC */ /* read the hash table parameters */ if (lseek(fd, pos, SEEK_SET) < 0 || cdb_bread(fd, rbuf, 8) < 0) return -1; if ((htsize = cdb_unpack(rbuf + 4)) == 0) return 0; hti = (hval >> 8) % htsize; /* start position in hash table */ httodo = htsize; htstart = cdb_unpack(rbuf); for(;;) { if (needseek && lseek(fd, htstart + (hti << 3), SEEK_SET) < 0) return -1; if (cdb_bread(fd, rbuf, 8) < 0) return -1; if ((pos = cdb_unpack(rbuf + 4)) == 0) /* not found */ return 0; if (cdb_unpack(rbuf) != hval) /* hash value not matched */ needseek = 0; else { /* hash value matched */ if (lseek(fd, pos, SEEK_SET) < 0 || cdb_bread(fd, rbuf, 8) < 0) return -1; if (cdb_unpack(rbuf) == klen) { /* key length matches */ /* read the key from file and compare with wanted */ unsigned l = klen, c; const char *k = (const char*)key; if (dlenp) *dlenp = cdb_unpack(rbuf + 4); /* save value length */ for(;;) { if (!l) /* the whole key read and matches, return */ return 1; c = l > sizeof(rbuf) ? sizeof(rbuf) : l; if (cdb_bread(fd, rbuf, c) < 0) return -1; if (memcmp(rbuf, k, c) != 0) /* no, it differs, stop here */ break; k += c; l -= c; } } needseek = 1; /* we're looked to other place, should seek back */ } if (!--httodo) return 0; if (++hti == htsize) { hti = 0; needseek = 1; } } } tinycdb-0.81/cdb_pack.c0000644000175000017500000000262714542364427013160 0ustar mjtmjt/* cdb_pack.c: pack a 32bit integer (to network byte order) * * This file is a part of tinycdb package. * Copyright (C) 2001-2023 Michael Tokarev * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include "cdb.h" void cdb_pack(unsigned num, unsigned char buf[4]) { buf[0] = num & 255; num >>= 8; buf[1] = num & 255; num >>= 8; buf[2] = num & 255; buf[3] = num >> 8; } tinycdb-0.81/cdb_unpack.c0000644000175000017500000000262714542364427013523 0ustar mjtmjt/* cdb_unpack.c: unpack a 32bit integer from network byte order * * This file is a part of tinycdb package. * Copyright (C) 2001-2023 Michael Tokarev * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include "cdb.h" unsigned cdb_unpack(const unsigned char buf[4]) { unsigned n = buf[3]; n <<= 8; n |= buf[2]; n <<= 8; n |= buf[1]; n <<= 8; n |= buf[0]; return n; } tinycdb-0.81/cdb_make_add.c0000644000175000017500000000466214542364427013770 0ustar mjtmjt/* cdb_make_add.c: basic cdb_make_add routine * * This file is a part of tinycdb package. * Copyright (C) 2001-2023 Michael Tokarev * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include /* for malloc */ #include "cdb_int.h" int internal_function _cdb_make_add(struct cdb_make *cdbmp, unsigned hval, const void *key, unsigned klen, const void *val, unsigned vlen) { unsigned char rlen[8]; struct cdb_rl *rl; unsigned i; if (klen > 0xffffffff - (cdbmp->cdb_dpos + 8) || vlen > 0xffffffff - (cdbmp->cdb_dpos + klen + 8)) return errno = ENOMEM, -1; i = hval & 255; rl = cdbmp->cdb_rec[i]; if (!rl || rl->cnt >= sizeof(rl->rec)/sizeof(rl->rec[0])) { rl = (struct cdb_rl*)malloc(sizeof(struct cdb_rl)); if (!rl) return errno = ENOMEM, -1; rl->cnt = 0; rl->next = cdbmp->cdb_rec[i]; cdbmp->cdb_rec[i] = rl; } i = rl->cnt++; rl->rec[i].hval = hval; rl->rec[i].rpos = cdbmp->cdb_dpos; ++cdbmp->cdb_rcnt; cdb_pack(klen, rlen); cdb_pack(vlen, rlen + 4); if (_cdb_make_write(cdbmp, rlen, 8) < 0 || _cdb_make_write(cdbmp, key, klen) < 0 || _cdb_make_write(cdbmp, val, vlen) < 0) return -1; return 0; } int cdb_make_add(struct cdb_make *cdbmp, const void *key, unsigned klen, const void *val, unsigned vlen) { return _cdb_make_add(cdbmp, cdb_hash(key, klen), key, klen, val, vlen); } tinycdb-0.81/cdb_make_put.c0000644000175000017500000001403314542364427014041 0ustar mjtmjt/* cdb_make_put.c: "advanced" cdb_make_put routine * * This file is a part of tinycdb package. * Copyright (C) 2001-2023 Michael Tokarev * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include #include #include #include "cdb_int.h" static void fixup_rpos(struct cdb_make *cdbmp, unsigned rpos, unsigned rlen) { unsigned i; struct cdb_rl *rl; register struct cdb_rec *rp, *rs; for (i = 0; i < 256; ++i) { for (rl = cdbmp->cdb_rec[i]; rl; rl = rl->next) for (rs = rl->rec, rp = rs + rl->cnt; --rp >= rs;) if (rp->rpos <= rpos) goto nexthash; else rp->rpos -= rlen; nexthash:; } } static int remove_record(struct cdb_make *cdbmp, unsigned rpos, unsigned rlen) { unsigned pos, len; int r, fd; len = cdbmp->cdb_dpos - rpos - rlen; cdbmp->cdb_dpos -= rlen; if (!len) return 0; /* it was the last record, nothing to do */ pos = rpos; fd = cdbmp->cdb_fd; do { r = len > sizeof(cdbmp->cdb_buf) ? sizeof(cdbmp->cdb_buf) : len; if (lseek(fd, pos + rlen, SEEK_SET) < 0 || (r = read(fd, cdbmp->cdb_buf, r)) <= 0) return -1; if (lseek(fd, pos, SEEK_SET) < 0 || _cdb_make_fullwrite(fd, cdbmp->cdb_buf, r) < 0) return -1; pos += r; len -= r; } while(len); assert(cdbmp->cdb_dpos == pos); fixup_rpos(cdbmp, rpos, rlen); return 0; } static int zerofill_record(struct cdb_make *cdbmp, unsigned rpos, unsigned rlen) { if (rpos + rlen == cdbmp->cdb_dpos) { cdbmp->cdb_dpos = rpos; return 0; } if (lseek(cdbmp->cdb_fd, rpos, SEEK_SET) < 0) return -1; memset(cdbmp->cdb_buf, 0, sizeof(cdbmp->cdb_buf)); cdb_pack(rlen - 8, cdbmp->cdb_buf + 4); for(;;) { rpos = rlen > sizeof(cdbmp->cdb_buf) ? sizeof(cdbmp->cdb_buf) : rlen; if (_cdb_make_fullwrite(cdbmp->cdb_fd, cdbmp->cdb_buf, rpos) < 0) return -1; rlen -= rpos; if (!rlen) return 0; memset(cdbmp->cdb_buf + 4, 0, 4); } } /* return: 0 = not found, 1 = error, or record length */ static unsigned match(struct cdb_make *cdbmp, unsigned pos, const char *key, unsigned klen) { int len; unsigned rlen; if (lseek(cdbmp->cdb_fd, pos, SEEK_SET) < 0) return 1; if (read(cdbmp->cdb_fd, cdbmp->cdb_buf, 8) != 8) return 1; if (cdb_unpack(cdbmp->cdb_buf) != klen) return 0; /* record length; check its validity */ rlen = cdb_unpack(cdbmp->cdb_buf + 4); if (rlen > cdbmp->cdb_dpos - pos - klen - 8) return errno = EPROTO, 1; /* someone changed our file? */ rlen += klen + 8; while(klen) { len = klen > sizeof(cdbmp->cdb_buf) ? sizeof(cdbmp->cdb_buf) : klen; len = read(cdbmp->cdb_fd, cdbmp->cdb_buf, len); if (len <= 0) return 1; if (memcmp(cdbmp->cdb_buf, key, len) != 0) return 0; key += len; klen -= len; } return rlen; } static int findrec(struct cdb_make *cdbmp, const void *key, unsigned klen, unsigned hval, enum cdb_put_mode mode) { struct cdb_rl *rl; struct cdb_rec *rp, *rs; unsigned r; int seeked = 0; int ret = 0; for(rl = cdbmp->cdb_rec[hval&255]; rl; rl = rl->next) for(rs = rl->rec, rp = rs + rl->cnt; --rp >= rs;) { if (rp->hval != hval) continue; /*XXX this explicit flush may be unnecessary having * smarter match() that looks into cdb_buf too, but * most of a time here spent in finding hash values * (above), not keys */ if (!seeked && _cdb_make_flush(cdbmp) < 0) return -1; seeked = 1; r = match(cdbmp, rp->rpos, key, klen); if (!r) continue; if (r == 1) return -1; ret = 1; switch(mode) { case CDB_FIND_REMOVE: if (remove_record(cdbmp, rp->rpos, r) < 0) return -1; break; case CDB_FIND_FILL0: if (zerofill_record(cdbmp, rp->rpos, r) < 0) return -1; break; default: goto finish; } memmove(rp, rp + 1, (rs + rl->cnt - 1 - rp) * sizeof(*rp)); --rl->cnt; --cdbmp->cdb_rcnt; } finish: if (seeked && lseek(cdbmp->cdb_fd, cdbmp->cdb_dpos, SEEK_SET) < 0) return -1; return ret; } int cdb_make_find(struct cdb_make *cdbmp, const void *key, unsigned klen, enum cdb_put_mode mode) { return findrec(cdbmp, key, klen, cdb_hash(key, klen), mode); } int cdb_make_exists(struct cdb_make *cdbmp, const void *key, unsigned klen) { return cdb_make_find(cdbmp, key, klen, CDB_FIND); } int cdb_make_put(struct cdb_make *cdbmp, const void *key, unsigned klen, const void *val, unsigned vlen, enum cdb_put_mode mode) { unsigned hval = cdb_hash(key, klen); int r; switch(mode) { case CDB_PUT_REPLACE: case CDB_PUT_INSERT: case CDB_PUT_WARN: case CDB_PUT_REPLACE0: r = findrec(cdbmp, key, klen, hval, mode); if (r < 0) return -1; if (r && mode == CDB_PUT_INSERT) return errno = EEXIST, 1; break; case CDB_PUT_ADD: r = 0; break; default: return errno = EINVAL, -1; } if (_cdb_make_add(cdbmp, hval, key, klen, val, vlen) < 0) return -1; return r; } tinycdb-0.81/cdb_make.c0000644000175000017500000001172614542364427013157 0ustar mjtmjt/* cdb_make.c: basic cdb creation routines * * This file is a part of tinycdb package. * Copyright (C) 2001-2023 Michael Tokarev * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include #include #include #include "cdb_int.h" int cdb_make_start(struct cdb_make *cdbmp, int fd) { memset(cdbmp, 0, sizeof(*cdbmp)); cdbmp->cdb_fd = fd; cdbmp->cdb_dpos = 2048; cdbmp->cdb_bpos = cdbmp->cdb_buf + 2048; return lseek(fd, 0, SEEK_SET); } int internal_function _cdb_make_fullwrite(int fd, const unsigned char *buf, unsigned len) { while(len) { int l = write(fd, buf, len); if (l > 0) { len -= l; buf += l; } else if (l < 0 && errno != EINTR) return -1; } return 0; } int internal_function _cdb_make_flush(struct cdb_make *cdbmp) { unsigned len = cdbmp->cdb_bpos - cdbmp->cdb_buf; if (len) { if (_cdb_make_fullwrite(cdbmp->cdb_fd, cdbmp->cdb_buf, len) < 0) return -1; cdbmp->cdb_bpos = cdbmp->cdb_buf; } return 0; } int internal_function _cdb_make_write(struct cdb_make *cdbmp, const unsigned char *ptr, unsigned len) { unsigned l = sizeof(cdbmp->cdb_buf) - (cdbmp->cdb_bpos - cdbmp->cdb_buf); cdbmp->cdb_dpos += len; if (len > l) { memcpy(cdbmp->cdb_bpos, ptr, l); cdbmp->cdb_bpos += l; if (_cdb_make_flush(cdbmp) < 0) return -1; ptr += l; len -= l; l = len / sizeof(cdbmp->cdb_buf); if (l) { l *= sizeof(cdbmp->cdb_buf); if (_cdb_make_fullwrite(cdbmp->cdb_fd, ptr, l) < 0) return -1; ptr += l; len -= l; } } if (len) { memcpy(cdbmp->cdb_bpos, ptr, len); cdbmp->cdb_bpos += len; } return 0; } static int cdb_make_finish_internal(struct cdb_make *cdbmp) { unsigned hcnt[256]; /* hash table counts */ unsigned hpos[256]; /* hash table positions */ struct cdb_rec *htab; unsigned char *p; struct cdb_rl *rl; unsigned hsize; unsigned t, i; if (((0xffffffff - cdbmp->cdb_dpos) >> 3) < cdbmp->cdb_rcnt) return errno = ENOMEM, -1; /* count htab sizes and reorder reclists */ hsize = 0; for (t = 0; t < 256; ++t) { struct cdb_rl *rlt = NULL; i = 0; rl = cdbmp->cdb_rec[t]; while(rl) { struct cdb_rl *rln = rl->next; rl->next = rlt; rlt = rl; i += rl->cnt; rl = rln; } cdbmp->cdb_rec[t] = rlt; if (hsize < (hcnt[t] = i << 1)) hsize = hcnt[t]; } /* allocate memory to hold max htable */ htab = (struct cdb_rec*)malloc((hsize + 2) * sizeof(struct cdb_rec)); if (!htab) return errno = ENOENT, -1; p = (unsigned char *)htab; htab += 2; /* build hash tables */ for (t = 0; t < 256; ++t) { unsigned len, hi; hpos[t] = cdbmp->cdb_dpos; if ((len = hcnt[t]) == 0) continue; for (i = 0; i < len; ++i) htab[i].hval = htab[i].rpos = 0; for (rl = cdbmp->cdb_rec[t]; rl; rl = rl->next) for (i = 0; i < rl->cnt; ++i) { hi = (rl->rec[i].hval >> 8) % len; while(htab[hi].rpos) if (++hi == len) hi = 0; htab[hi] = rl->rec[i]; } for (i = 0; i < len; ++i) { cdb_pack(htab[i].hval, p + (i << 3)); cdb_pack(htab[i].rpos, p + (i << 3) + 4); } if (_cdb_make_write(cdbmp, p, len << 3) < 0) { free(p); return -1; } } free(p); if (_cdb_make_flush(cdbmp) < 0) return -1; p = cdbmp->cdb_buf; for (t = 0; t < 256; ++t) { cdb_pack(hpos[t], p + (t << 3)); cdb_pack(hcnt[t], p + (t << 3) + 4); } if (lseek(cdbmp->cdb_fd, 0, SEEK_SET) != 0 || _cdb_make_fullwrite(cdbmp->cdb_fd, p, 2048) != 0) return -1; return fsync(cdbmp->cdb_fd); } static void cdb_make_free(struct cdb_make *cdbmp) { unsigned t; for(t = 0; t < 256; ++t) { struct cdb_rl *rl = cdbmp->cdb_rec[t]; while(rl) { struct cdb_rl *tm = rl; rl = rl->next; free(tm); } } } int cdb_make_finish(struct cdb_make *cdbmp) { int r = cdb_make_finish_internal(cdbmp); cdb_make_free(cdbmp); return r; } tinycdb-0.81/cdb_hash.c0000644000175000017500000000277314542364427013167 0ustar mjtmjt/* cdb_hash.c: cdb hashing routine * * This file is a part of tinycdb package. * Copyright (C) 2001-2023 Michael Tokarev * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include "cdb.h" unsigned cdb_hash(const void *buf, unsigned len) { register const unsigned char *p = (const unsigned char *)buf; register const unsigned char *end = p + len; register unsigned hash = 5381; /* start value */ while (p < end) hash = (hash + (hash << 5)) ^ *p++; return hash; } tinycdb-0.81/cdb.c0000644000175000017500000003700114542364427012154 0ustar mjtmjt/* cdb.c: cdb command line tool * * This file is a part of tinycdb package. * Copyright (C) 2001-2023 Michael Tokarev * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #define _GNU_SOURCE /* #define this even on Windows */ #ifdef _WIN32 /* by the way, how about win64? */ # include # include /* This pragma suppresses snippy VC warnings for POSIX functions like read() */ # pragma warning(disable: 4996) #else # include # include #endif #include #include #include #include #include #include #include #include #include "cdb.h" #ifndef EPROTO # define EPROTO EINVAL #endif #ifdef __GLIBC__ # define HAVE_PROGRAM_INVOCATION_SHORT_NAME #endif #ifdef HAVE_PROGRAM_INVOCATION_SHORT_NAME # define progname program_invocation_short_name #else static char *progname; #endif #ifndef O_NOFOLLOW # define O_NOFOLLOW 0 #endif #ifdef _WIN32 # define FBINMODE "b" #else # define FBINMODE #endif #define F_DUPMASK 0x000f #define F_WARNDUP 0x0100 #define F_ERRDUP 0x0200 #define F_MAP 0x1000 /* map format (or else CDB native format) */ /* Silly defines just to suppress silly compiler warnings. * The thing is, trivial routines like strlen(), fgets() etc expects * char* argument, and GCC>=4 complains about using unsigned char* here. * Silly silly silly. */ #ifdef __GNUC__ static inline size_t ustrlen(const unsigned char *s) { return strlen((const char*)s); } static inline unsigned char *ufgets(unsigned char *s, int size, FILE *f) { return (unsigned char*)fgets((char*)s, size, f); } #else # define ustrlen strlen # define ufgets fgets #endif static unsigned char *buf; static unsigned blen; static char *cleanup_tmpname; static void #ifdef __GNUC__ __attribute__((noreturn,format(printf,2,3))) #endif error(int errnum, const char *fmt, ...) { if (fmt) { va_list ap; fprintf(stderr, "%s: ", progname); va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); } if (errnum) fprintf(stderr, ": %s\n", strerror(errnum)); else { if (fmt) putc('\n', stderr); fprintf(stderr, "%s: try `%s -h' for help\n", progname, progname); } fflush(stderr); if (cleanup_tmpname) unlink(cleanup_tmpname); exit(errnum ? 111 : 2); } static void allocbuf(unsigned len) { if (blen < len) { buf = (unsigned char*)(buf ? realloc(buf, len) : malloc(len)); if (!buf) error(ENOMEM, "unable to allocate %u bytes", len); blen = len; } } static int qmode(char *dbname, const char *key, int num, int flags) { struct cdb c; struct cdb_find cf; int r; int n, found; r = open(dbname, O_RDONLY); if (r < 0 || cdb_init(&c, r) != 0) error(errno, "unable to open database `%s'", dbname); r = cdb_findinit(&cf, &c, key, strlen(key)); if (!r) return 100; else if (r < 0) error(errno, "%s", key); n = 0; found = 0; while((r = cdb_findnext(&cf)) > 0) { ++n; if (num && num != n) continue; ++found; allocbuf(cdb_datalen(&c)); if (cdb_read(&c, buf, cdb_datalen(&c), cdb_datapos(&c)) != 0) error(errno, "unable to read value"); fwrite(buf, 1, cdb_datalen(&c), stdout); if (flags & F_MAP) putchar('\n'); if (num) break; } if (r < 0) error(0, "%s", key); return found ? 0 : 100; } static void fget(FILE *f, unsigned char *b, unsigned len, unsigned *posp, unsigned limit) { if (posp && limit - *posp < len) error(EPROTO, "invalid database format"); if (fread(b, 1, len, f) != len) { if (ferror(f)) error(errno, "unable to read"); fprintf(stderr, "%s: unable to read: short file\n", progname); exit(2); } if (posp) *posp += len; } static int fcpy(FILE *fi, FILE *fo, unsigned len, unsigned *posp, unsigned limit) { while(len > blen) { fget(fi, buf, blen, posp, limit); if (fo && fwrite(buf, 1, blen, fo) != blen) return -1; len -= blen; } if (len) { fget(fi, buf, len, posp, limit); if (fo && fwrite(buf, 1, len, fo) != len) return -1; } return 0; } static int dmode(char *dbname, char mode, int flags) { unsigned eod, klen, vlen; unsigned pos = 0; FILE *f; if (strcmp(dbname, "-") == 0) f = stdin; else if ((f = fopen(dbname, "r" FBINMODE)) == NULL) error(errno, "open %s", dbname); allocbuf(2048); fget(f, buf, 2048, &pos, 2048); eod = cdb_unpack(buf); while(pos < eod) { fget(f, buf, 8, &pos, eod); klen = cdb_unpack(buf); vlen = cdb_unpack(buf + 4); if (!(flags & F_MAP)) if (printf(mode == 'd' ? "+%u,%u:" : "+%u:", klen, vlen) < 0) return -1; if (fcpy(f, stdout, klen, &pos, eod) != 0) return -1; if (mode == 'd') if (fputs(flags & F_MAP ? " " : "->", stdout) < 0) return -1; if (fcpy(f, mode == 'd' ? stdout : NULL, vlen, &pos, eod) != 0) return -1; if (putc('\n', stdout) < 0) return -1; } if (pos != eod) error(EPROTO, "invalid cdb file format"); if (!(flags & F_MAP)) if (putc('\n', stdout) < 0) return -1; return 0; } static int smode(char *dbname) { FILE *f; unsigned pos, eod; unsigned cnt = 0; unsigned kmin = 0, kmax = 0, ktot = 0; unsigned vmin = 0, vmax = 0, vtot = 0; unsigned hmin = 0, hmax = 0, htot = 0, hcnt = 0; #define NDIST 11 unsigned dist[NDIST]; unsigned char toc[2048]; unsigned k; if (strcmp(dbname, "-") == 0) f = stdin; else if ((f = fopen(dbname, "r" FBINMODE)) == NULL) error(errno, "open %s", dbname); pos = 0; fget(f, toc, 2048, &pos, 2048); allocbuf(2048); eod = cdb_unpack(toc); while(pos < eod) { unsigned klen, vlen; fget(f, buf, 8, &pos, eod); klen = cdb_unpack(buf); vlen = cdb_unpack(buf + 4); fcpy(f, NULL, klen, &pos, eod); fcpy(f, NULL, vlen, &pos, eod); ++cnt; ktot += klen; if (!kmin || kmin > klen) kmin = klen; if (kmax < klen) kmax = klen; vtot += vlen; if (!vmin || vmin > vlen) vmin = vlen; if (vmax < vlen) vmax = vlen; vlen += klen; } if (pos != eod) error(EPROTO, "invalid cdb file format"); for (k = 0; k < NDIST; ++k) dist[k] = 0; for (k = 0; k < 256; ++k) { unsigned i = cdb_unpack(toc + (k << 3)); unsigned hlen = cdb_unpack(toc + (k << 3) + 4); if (i != pos) error(EPROTO, "invalid cdb hash table"); if (!hlen) continue; for (i = 0; i < hlen; ++i) { unsigned h; fget(f, buf, 8, &pos, 0xffffffff); if (!cdb_unpack(buf + 4)) continue; h = (cdb_unpack(buf) >> 8) % hlen; if (h == i) h = 0; else { if (h < i) h = i - h; else h = hlen - h + i; if (h >= NDIST) h = NDIST - 1; } ++dist[h]; } if (!hmin || hmin > hlen) hmin = hlen; if (hmax < hlen) hmax = hlen; htot += hlen; ++hcnt; } printf("number of records: %u\n", cnt); printf("key min/avg/max length: %u/%u/%u\n", kmin, cnt ? (ktot + cnt / 2) / cnt : 0, kmax); printf("val min/avg/max length: %u/%u/%u\n", vmin, cnt ? (vtot + cnt / 2) / cnt : 0, vmax); printf("hash tables/entries/collisions: %u/%u/%u\n", hcnt, htot, cnt - dist[0]); printf("hash table min/avg/max length: %u/%u/%u\n", hmin, hcnt ? (htot + hcnt / 2) / hcnt : 0, hmax); printf("hash table distances:\n"); for(k = 0; k < NDIST; ++k) printf(" %c%u: %6u %2u%%\n", k == NDIST - 1 ? '>' : 'd', k == NDIST - 1 ? k - 1 : k, dist[k], cnt ? dist[k] * 100 / cnt : 0); return 0; } static void badinput(const char *fn) { fprintf(stderr, "%s: %s: bad format\n", progname, fn); exit(2); } static int getnum(FILE *f, unsigned *np, const char *fn) { unsigned n; int c = getc(f); if (c < '0' || c > '9') badinput(fn); n = c - '0'; while((c = getc(f)) >= '0' && c <= '9') { c -= '0'; if (0xffffffff / 10 - c < n) badinput(fn); n = n * 10 + c; } *np = n; return c; } static void addrec(struct cdb_make *cdbmp, const unsigned char *key, unsigned klen, const unsigned char *val, unsigned vlen, int flags) { int r = cdb_make_put(cdbmp, key, klen, val, vlen, flags & F_DUPMASK); if (r < 0) error(errno, "cdb_make_put"); else if (r && (flags & F_WARNDUP)) { fprintf(stderr, "%s: key `", progname); fwrite(key, 1, klen, stderr); fputs("' duplicated\n", stderr); if (flags & F_ERRDUP) exit(1); } } static void dofile_cdb(struct cdb_make *cdbmp, FILE *f, const char *fn, int flags) { unsigned klen, vlen; int c; while((c = getc(f)) == '+') { if ((c = getnum(f, &klen, fn)) != ',' || (c = getnum(f, &vlen, fn)) != ':' || 0xffffffff - klen < vlen) badinput(fn); allocbuf(klen + vlen); fget(f, buf, klen, NULL, 0); if (getc(f) != '-' || getc(f) != '>') badinput(fn); fget(f, buf + klen, vlen, NULL, 0); if (getc(f) != '\n') badinput(fn); addrec(cdbmp, buf, klen, buf + klen, vlen, flags); } if (c != '\n') badinput(fn); } static void dofile_ln(struct cdb_make *cdbmp, FILE *f, int flags) { unsigned char *k, *v; while(ufgets(buf, blen, f) != NULL) { unsigned l = 0; for (;;) { l += ustrlen(buf + l); v = buf + l; if (v > buf && v[-1] == '\n') { v[-1] = '\0'; break; } if (l < blen) allocbuf(l + 512); if (!ufgets(buf + l, blen - l, f)) break; } k = buf; while(*k == ' ' || *k == '\t') ++k; if (!*k || *k == '#') continue; v = k; while(*v && *v != ' ' && *v != '\t') ++v; if (*v) *v++ = '\0'; while(*v == ' ' || *v == '\t') ++v; addrec(cdbmp, k, ustrlen(k), v, ustrlen(v), flags); } } static void dofile(struct cdb_make *cdbmp, FILE *f, const char *fn, int flags) { if (flags & F_MAP) dofile_ln(cdbmp, f, flags); else dofile_cdb(cdbmp, f, fn, flags); if (ferror(f)) error(errno, "read error"); } static int cmode(char *dbname, char *tmpname, int argc, char **argv, int flags, int perms) { struct cdb_make cdb; int fd; if (!tmpname) { tmpname = (char*)malloc(strlen(dbname) + 5); if (!tmpname) error(ENOMEM, "unable to allocate memory"); /* OpenBSD compiler complains about strcat() and strcpy() usage, * and suggests to replace them with (non-standard) strlcat() and * strlcpy(). This is silly, since it's obvious that usage of * original str*() routines here is correct. * This is compiler/environment bug, not tinycdb bug, so please * fix it in proper place, and don't send patches to me. Thank you. */ strcat(strcpy(tmpname, dbname), ".tmp"); } else if (strcmp(tmpname, "-") == 0 || strcmp(tmpname, dbname) == 0) tmpname = dbname; if (perms >= 0) umask(0); unlink(tmpname); cleanup_tmpname = tmpname; fd = open(tmpname, O_RDWR|O_CREAT|O_EXCL|O_NOFOLLOW, perms >= 0 ? perms : 0666); if (fd < 0 || cdb_make_start(&cdb, fd) < 0) error(errno, "unable to create %s", tmpname); allocbuf(4096); if (argc) { int i; for (i = 0; i < argc; ++i) { if (strcmp(argv[i], "-") == 0) dofile(&cdb, stdin, "(stdin)", flags); else { FILE *f = fopen(argv[i], "r"); if (!f) error(errno, "%s", argv[i]); dofile(&cdb, f, argv[i], flags); fclose(f); } } } else dofile(&cdb, stdin, "(stdin)", flags); if (cdb_make_finish(&cdb) != 0) error(errno, "cdb_make_finish"); if (close(fd) != 0) error(errno, "close %s", tmpname); if (tmpname != dbname) if (rename(tmpname, dbname) != 0) error(errno, "rename %s->%s", tmpname, dbname); return 0; } int main(int argc, char **argv) { int c; char mode = 0; char *tmpname = NULL; int flags = 0; int num = 0; int r; int perms = -1; extern char *optarg; extern int optind; #ifdef HAVE_PROGRAM_INVOCATION_SHORT_NAME argv[0] = progname; #else if (argv[0] && (progname = strrchr(argv[0], '/')) != NULL) argv[0] = ++progname; else progname = argv[0]; #endif if (argc <= 1) error(0, "no arguments given"); while((c = getopt(argc, argv, "qdlcsht:n:mwruep:0")) != EOF) switch(c) { case 'q': case 'd': case 'l': case 'c': case 's': if (mode && mode != c) error(0, "different modes of operation requested"); mode = c; break; case 't': tmpname = optarg; break; case 'w': flags |= F_WARNDUP; break; case 'e': flags |= F_WARNDUP | F_ERRDUP; break; case 'r': flags = (flags & ~F_DUPMASK) | CDB_PUT_REPLACE; break; case 'u': flags = (flags & ~F_DUPMASK) | CDB_PUT_INSERT; break; case '0': flags = (flags & ~F_DUPMASK) | CDB_PUT_REPLACE0; break; case 'm': flags |= F_MAP; break; case 'p': { char *ep = NULL; perms = strtol(optarg, &ep, 0); if (perms < 0 || perms > 0777 || (ep && *ep)) error(0, "invalid permissions `%s'", optarg); break; } case 'n': { char *ep = NULL; if ((num = strtol(optarg, &ep, 0)) <= 0 || (ep && *ep)) error(0, "invalid record number `%s'", optarg); break; } case 'h': #define strify(x) _strify(x) #define _strify(x) #x printf("\ %s: Constant DataBase (CDB) tool version " strify(TINYCDB_VERSION) ". Usage is:\n\ query: %s -q [-m] [-n recno|-a] cdbfile key\n\ dump: %s -d [-m] [cdbfile|-]\n\ list: %s -l [-m] [cdbfile|-]\n\ create: %s -c [-m] [-wrue0] [-t tempfile|-] [-p perms] cdbfile [infile...]\n\ stats: %s -s [cdbfile|-]\n\ help: %s -h\n\ ", progname, progname, progname, progname, progname, progname, progname); return 0; default: error(0, NULL); } argv += optind; argc -= optind; #ifdef SIGXFSZ /* at least on some linux architectures, writing past file size limit makes * the process to receive SIGXFSZ in addition to returning -1 EFBIG * from write(). Ignore SIGXFSZ to be able to handle write errors */ signal(SIGXFSZ, SIG_IGN); #endif switch(mode) { case 'q': if (argc < 2) error(0, "no database or key to query specified"); if (argc > 2) error(0, "extra arguments in command line"); r = qmode(argv[0], argv[1], num, flags); break; case 'c': if (!argc) error(0, "no database name specified"); if ((flags & F_WARNDUP) && !(flags & F_DUPMASK)) flags |= CDB_PUT_WARN; r = cmode(argv[0], tmpname, argc - 1, argv + 1, flags, perms); break; case 'd': case 'l': if (argc > 1) error(0, "extra arguments for dump/list"); r = dmode(argc ? argv[0] : "-", mode, flags); break; case 's': if (argc > 1) error(0, "extra argument(s) for stats"); r = smode(argc ? argv[0] : "-"); break; default: error(0, "no -q, -c, -d, -l or -s option specified"); } if (r < 0 || fflush(stdout) < 0) error(errno, "unable to write: %d", c); return r; } tinycdb-0.81/nss_cdb.c0000644000175000017500000001552014542364427013041 0ustar mjtmjt/* nss_cdb.c: nss_cdb common routines. * * This file is a part of tinycdb package. * Copyright (C) 2001-2023 Michael Tokarev * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include "nss_cdb.h" #include "cdb_int.h" /* for internal_function */ #include #include #include #include #include #if __GLIBC__ /* XXX this is in fact not a right condition */ /* XXX on glibc, this stuff works due to linker/libpthreads stubs/tricks. * On other libcs, it may require linking whole -lpthread, which is * not a good thing to do for nss module... */ #include #define lock_define(class,name) \ class pthread_mutex_t name = PTHREAD_MUTEX_INITIALIZER; #define lock_lock(name) pthread_mutex_lock(&(name)) #define lock_unlock(name) pthread_mutex_unlock(&(name)) #else /* !__GNU_LIBRARY__ */ # define lock_define_initialized(class,name) # define lock_lock(name) # define lock_unlock(name) #endif /* __GNU_LIBRARY__ */ lock_define(static, lock) /* General principle: we skip invalid/unparseable entries completely, * as if there was no such entry at all (returning NOTFOUND). * In case of data read error (e.g. invalid .cdb structure), we * return UNAVAIL. */ #define isopen(dbp) ((dbp)->lastpos) static int __nss_cdb_dosetent(struct nss_cdb *dbp) { int fd; fd = open(dbp->dbname, O_RDONLY); if (fd < 0) return 0; if (cdb_init(&dbp->cdb, fd) != 0) { close(fd); return 0; } close(fd); dbp->lastpos = 2048; /* cdb_seqinit() */ return 1; } static void __nss_cdb_doendent(struct nss_cdb *dbp) { cdb_free(&dbp->cdb); dbp->lastpos = 0; dbp->keepopen = 0; } enum nss_status internal_function __nss_cdb_setent(struct nss_cdb *dbp, int stayopen) { enum nss_status r; lock_lock(lock); if (isopen(dbp) || __nss_cdb_dosetent(dbp)) r = NSS_STATUS_SUCCESS, dbp->keepopen |= stayopen; else r = NSS_STATUS_UNAVAIL; lock_unlock(lock); return r; } enum nss_status internal_function __nss_cdb_endent(struct nss_cdb *dbp) { lock_lock(lock); if (isopen(dbp)) __nss_cdb_doendent(dbp); lock_unlock(lock); return NSS_STATUS_SUCCESS; } static enum nss_status __nss_cdb_dobyname(struct nss_cdb *dbp, const char *key, unsigned len, void *result, char *buf, size_t bufl, int *errnop) { int r; if ((r = cdb_find(&dbp->cdb, key, len)) < 0) return *errnop = errno, NSS_STATUS_UNAVAIL; len = cdb_datalen(&dbp->cdb); if (!r || len < 2) return *errnop = ENOENT, NSS_STATUS_NOTFOUND; if (len >= bufl) return *errnop = ERANGE, NSS_STATUS_TRYAGAIN; if (cdb_read(&dbp->cdb, buf, len, cdb_datapos(&dbp->cdb)) != 0) return *errnop = errno, NSS_STATUS_UNAVAIL; buf[len] = '\0'; if ((r = dbp->parsefn(result, buf, bufl)) < 0) return *errnop = ENOENT, NSS_STATUS_NOTFOUND; if (!r) return *errnop = ERANGE, NSS_STATUS_TRYAGAIN; return NSS_STATUS_SUCCESS; } enum nss_status internal_function __nss_cdb_byname(struct nss_cdb *dbp, const char *name, void *result, char *buf, size_t bufl, int *errnop) { enum nss_status r; if (*name == ':') return *errnop = ENOENT, NSS_STATUS_NOTFOUND; lock_lock(lock); if (!isopen(dbp) && !__nss_cdb_dosetent(dbp)) *errnop = errno, r = NSS_STATUS_UNAVAIL; else { r = __nss_cdb_dobyname(dbp, name, strlen(name), result, buf, bufl, errnop); if (!dbp->keepopen) __nss_cdb_doendent(dbp); } lock_unlock(lock); return r; } static enum nss_status __nss_cdb_dobyid(struct nss_cdb *dbp, unsigned long id, void *result, char *buf, size_t bufl, int *errnop) { int r; unsigned len; const char *data; if ((r = cdb_find(&dbp->cdb, buf, sprintf(buf, ":%lu", id))) < 0) return *errnop = errno, NSS_STATUS_UNAVAIL; len = cdb_datalen(&dbp->cdb); if (!r || len < 2) return *errnop = ENOENT, NSS_STATUS_NOTFOUND; if (!(data = (const char*)cdb_get(&dbp->cdb, len, cdb_datapos(&dbp->cdb)))) return *errnop = errno, NSS_STATUS_UNAVAIL; return __nss_cdb_dobyname(dbp, data, len, result, buf, bufl, errnop); } enum nss_status internal_function __nss_cdb_byid(struct nss_cdb *dbp, unsigned long id, void *result, char *buf, size_t bufl, int *errnop) { enum nss_status r; if (bufl < 30) return *errnop = ERANGE, NSS_STATUS_TRYAGAIN; lock_lock(lock); if (!isopen(dbp) && !__nss_cdb_dosetent(dbp)) *errnop = errno, r = NSS_STATUS_UNAVAIL; else { r = __nss_cdb_dobyid(dbp, id, result, buf, bufl, errnop); if (!dbp->keepopen) __nss_cdb_doendent(dbp); } lock_unlock(lock); return r; } static enum nss_status __nss_cdb_dogetent(struct nss_cdb *dbp, void *result, char *buf, size_t bufl, int *errnop) { int r; unsigned lastpos; if (!isopen(dbp) && !__nss_cdb_dosetent(dbp)) return *errnop = errno, NSS_STATUS_UNAVAIL; while((lastpos = dbp->lastpos, r = cdb_seqnext(&dbp->lastpos, &dbp->cdb)) > 0) { if (cdb_keylen(&dbp->cdb) < 2) continue; if (((const char *)cdb_getkey(&dbp->cdb))[0] == ':') /* can't fail */ continue; if (cdb_datalen(&dbp->cdb) >= bufl) return dbp->lastpos = lastpos, *errnop = ERANGE, NSS_STATUS_TRYAGAIN; cdb_readdata(&dbp->cdb, buf); buf[cdb_datalen(&dbp->cdb)] = '\0'; if ((r = dbp->parsefn(result, buf, bufl)) == 0) return dbp->lastpos = lastpos, *errnop = ERANGE, NSS_STATUS_TRYAGAIN; if (r > 0) return NSS_STATUS_SUCCESS; } if (r < 0) return *errnop = errno, NSS_STATUS_UNAVAIL; else return *errnop = ENOENT, NSS_STATUS_NOTFOUND; } enum nss_status internal_function __nss_cdb_getent(struct nss_cdb *dbp, void *result, char *buf, size_t bufl, int *errnop) { enum nss_status r; if (bufl < 30) return *errnop = ERANGE, NSS_STATUS_TRYAGAIN; lock_lock(lock); dbp->keepopen |= 1; r = __nss_cdb_dogetent(dbp, result, buf, bufl, errnop); lock_unlock(lock); return r; } tinycdb-0.81/nss_cdb-passwd.c0000644000175000017500000000472514542364427014345 0ustar mjtmjt/* nss_cdb-passwd.c: nss_cdb passwd database routines. * * This file is a part of tinycdb package. * Copyright (C) 2001-2023 Michael Tokarev * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include "nss_cdb.h" #include #include nss_common(passwd, struct passwd, pwent); nss_getbyname(getpwnam, struct passwd); nss_getbyid(getpwuid, struct passwd, uid_t); static int nss_passwd_parse(struct passwd *result, char *buf, size_t bufl) { STRING_FIELD(buf, result->pw_name); if (!result->pw_name[0]) return -1; STRING_FIELD(buf, result->pw_passwd); INT_FIELD(buf, result->pw_uid, (uid_t)); INT_FIELD(buf, result->pw_gid, (gid_t)); STRING_FIELD(buf, result->pw_gecos); STRING_FIELD(buf, result->pw_dir); result->pw_shell = buf; bufl = bufl; return 1; } #ifdef TEST #include static void printit(const struct passwd *p) { printf("name=`%s' pass=`%s' uid=%d gid=%d gecos=`%s' dir=`%s' shell=`%s'\n", p->pw_name, p->pw_passwd, p->pw_uid, p->pw_gid, p->pw_gecos, p->pw_dir, p->pw_shell); } int main(int argc, char **argv) { struct passwd pw, *p; char buf[1024]; int err, r; while(*++argv) { r = _nss_cdb_getpwuid_r(atoi(*argv), &pw, buf, sizeof(buf), &err); if (r == NSS_STATUS_SUCCESS) printit(&pw); else printf("cdb(%s): %d %s\n", *argv, r, strerror(err)); p = getpwuid(atoi(*argv)); if (p) printit(p); else printf("pwuid(%s): %m\n", *argv); } return 0; } #endif tinycdb-0.81/nss_cdb-group.c0000644000175000017500000000576414542364427014204 0ustar mjtmjt/* nss_cdb-group.c: nss_cdb group database routines. * * This file is a part of tinycdb package. * Copyright (C) 2001-2023 Michael Tokarev * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include "nss_cdb.h" #include #include #include nss_common(group, struct group, grent); nss_getbyname(getgrnam, struct group); nss_getbyid(getgrgid, struct group, gid_t); static char *getmember(char **bp) { char *b, *m; b = *bp; while(*b == ',') ++b; if (!*b) return NULL; m = b++; for(;;) { if (*b == ',') { *b++ = '\0'; *bp = b; return m; } else if (*b == '\0') { *bp = b; return m; } else ++b; } } static int nss_group_parse(struct group *result, char *buf, size_t bufl) { char *bufend; char **mem; int n; bufend = buf + strlen(buf) + 1; n = (unsigned)bufend % sizeof(char*); if (n) bufend += sizeof(char*) - n; result->gr_mem = mem = (char**)bufend; bufend = buf + bufl - sizeof(char*); STRING_FIELD(buf, result->gr_name); if (!result->gr_name[0]) return -1; STRING_FIELD(buf, result->gr_passwd); INT_FIELD(buf, result->gr_gid, (gid_t)); for(;;) { if ((char*)mem > bufend) return 0; if (!(*mem++ = getmember(&buf))) break; } return 1; } #ifdef TEST #include void printit(struct group *g) { char **mem; printf("name=%s pass=%s gid=%d mem:", g->gr_name, g->gr_passwd, g->gr_gid); mem = g->gr_mem; if (!mem) printf(" none"); else while(*mem) printf(" %s", *mem++); putchar('\n'); } int main(int argc, char **argv) { struct group gr, *g; char buf[36]; int err, r; while(*++argv) { r = _nss_cdb_getgrgid_r(atoi(*argv), &gr, buf, sizeof(buf), &err); if (r == NSS_STATUS_SUCCESS) printit(&gr); else printf("cdb(%s): %d %s\n", *argv, r, strerror(err)); g = getgrgid(atoi(*argv)); if (g) printit(g); else printf("grgid(%s): %m\n", *argv); } return 0; } #endif tinycdb-0.81/nss_cdb-spwd.c0000644000175000017500000000402314542364427014010 0ustar mjtmjt/* nss_cdb-spwd.c: nss_cdb shadow passwd database routines. * * This file is a part of tinycdb package. * Copyright (C) 2001-2023 Michael Tokarev * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include "nss_cdb.h" #include nss_common(shadow, struct spwd, spent); nss_getbyname(getspnam, struct spwd); static int nss_shadow_parse(struct spwd *result, char *buf, size_t bufl) { STRING_FIELD(buf, result->sp_namp); if (!result->sp_namp[0]) return -1; STRING_FIELD(buf, result->sp_pwdp); INT_FIELD_MAYBE_NULL(buf, result->sp_lstchg, (long), -1); INT_FIELD_MAYBE_NULL(buf, result->sp_min, (long), -1); INT_FIELD_MAYBE_NULL(buf, result->sp_max, (long), -1); INT_FIELD_MAYBE_NULL(buf, result->sp_warn, (long), -1); INT_FIELD_MAYBE_NULL(buf, result->sp_inact, (long), -1); INT_FIELD_MAYBE_NULL(buf, result->sp_expire, (long), -1); if (*buf) { result->sp_flag = strtoul(buf, &buf, 10); if (*buf) return -1; } else result->sp_flag = ~0ul; bufl = bufl; return 1; } tinycdb-0.81/nss_cdb.h0000644000175000017500000000744514542364427013055 0ustar mjtmjt/* nss_cdb.h: nss_cdb common include file. * * This file is a part of tinycdb package. * Copyright (C) 2001-2023 Michael Tokarev * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include #include #include #include "cdb.h" #ifndef NSSCDB_DIR # define NSSCDB_DIR "/etc" #endif #ifndef NSSCDB_DB # define NSSCDB_DB(name) NSSCDB_DIR "/" name ".cdb" #endif typedef int (nss_parse_fn)(void *result, char *buf, size_t bufl); struct nss_cdb { nss_parse_fn *parsefn; const char *dbname; int keepopen; unsigned lastpos; struct cdb cdb; }; enum nss_status __nss_cdb_setent(struct nss_cdb *dbp, int stayopen); enum nss_status __nss_cdb_endent(struct nss_cdb *dbp); enum nss_status __nss_cdb_getent(struct nss_cdb *dbp, void *result, char *buf, size_t bufl, int *errnop); enum nss_status __nss_cdb_byname(struct nss_cdb *dbp, const char *name, void *result, char *buf, size_t bufl, int *errnop); enum nss_status __nss_cdb_byid(struct nss_cdb *dbp, unsigned long id, void *result, char *buf, size_t bufl, int *errnop); #define nss_common(dbname,structname,entname) \ static int \ nss_##dbname##_parse(structname *result, char *buf, size_t bufl); \ static struct nss_cdb db = { \ (nss_parse_fn*)&nss_##dbname##_parse, \ NSSCDB_DB(#dbname),0,0,CDB_STATIC_INIT}; \ enum nss_status _nss_cdb_set##entname(int stayopen) { \ return __nss_cdb_setent(&db, stayopen); \ } \ enum nss_status _nss_cdb_end##entname(void) { \ return __nss_cdb_endent(&db); \ } \ enum nss_status \ _nss_cdb_get##entname##_r(structname *result, \ char *buf, size_t bufl, int *errnop) { \ return __nss_cdb_getent(&db, result, buf, bufl, errnop); \ } #define nss_getbyname(getbyname, structname) \ enum nss_status \ _nss_cdb_##getbyname##_r(const char *name, structname *result, \ char *buf, size_t bufl, int *errnop) { \ return __nss_cdb_byname(&db, name, result, buf, bufl, errnop); \ } #define nss_getbyid(getbyid, structname, idtype) \ enum nss_status \ _nss_cdb_##getbyid##_r(idtype id, structname *result, \ char *buf, size_t bufl, int *errnop) { \ return __nss_cdb_byid(&db, id, result, buf, bufl, errnop); \ } #define STRING_FIELD(line, variable) \ variable = line; \ while(*line != ':') \ if (!*line++) return -1; \ *line++ = '\0' #define INT_FIELD(line, variable, convert) \ { \ char *endp; \ variable = convert(strtoul(line, &endp, 10)); \ if (endp == line) return -1; \ if (*endp++ != ':') return -1; \ line = endp; \ } #define INT_FIELD_MAYBE_NULL(line, variable, convert, default) \ { \ char *endp; \ if (!*line) return -1; \ variable = convert(strtoul(line, &endp, 10)); \ if (*endp != ':') return -1; \ if (endp == line) variable = convert(default); \ line = endp; \ } tinycdb-0.81/nss_cdb-Makefile0000755000175000017500000000246214542364427014337 0ustar mjtmjt# nss_cdb-Makefile: Makefile to create cdb-indexed files # for nss_cdb module from /etc/group, /etc/passwd, /etc/shadow. # # This file is a part of tinycdb package. # Copyright (C) 2001-2023 Michael Tokarev # Tinycdb is licensed under MIT license. AWK = awk SRC = . DST = . all: $(DST)/passwd.cdb $(DST)/group.cdb $(DST)/shadow.cdb $(DST)/passwd.cdb: $(SRC)/passwd umask 022; $(AWK) -F: '\ /^#/ { next } \ NF == 7 { print $$1" "$$0; print ":"$$3" "$$1 } \ ' $(SRC)/passwd > $@.in cdb -c -m $@ $@.in rm -f $@.in $(DST)/group.cdb: $(SRC)/group umask 022; $(AWK) -F: '\ /^#/ { next } \ NF == 4 { print $$1" "$$0; print ":"$$3" "$$1 } \ ' $(SRC)/group > $@.in cdb -c -m $@ $@.in rm -f $@.in # for shadow, we first create all files with mode 0600, # and only when everything's done, right before final # rename (which is done explicitly), we change permissions # and ownership to the right values. Assuming parent dirs # have proper permissions (so no symlink attacks etc are # possible) $(DST)/shadow.cdb: $(SRC)/shadow set -e; \ umask 077; \ rm -f $@.in; \ $(AWK) -F: '\ /^#/ { next } \ NF == 9 { print $$1" "$$0 } \ ' $(SRC)/shadow > $@.in cdb -c -m -t $@.tmp -p 0600 $@.tmp2 $@.in rm -f $@.in chown --reference=$(SRC)/shadow $@.tmp2 chmod --reference=$(SRC)/shadow $@.tmp2 mv -f $@.tmp2 $@ tinycdb-0.81/cdb.30000644000175000017500000004761314542364427012106 0ustar mjtmjt.\" cdb.3: cdb library manpage .\" .\" This file is a part of tinycdb package. .\" Copyright (C) 2001-2023 Michael Tokarev .\" Tinycdb is licensed under MIT license. .\" .TH cdb 3 "Jun 2006" .SH NAME cdb \- Constant DataBase library .SH SYNOPSYS .nf .ft B #include cc ... \-lcdb .ft R .fi .SH DESCRIPTION .B cdb is a library to create and access Constant DataBase files. File stores (key,value) pairs and used to quickly find a value based on a given key. Cdb files are create-once files, that is, once created, file cannot be updated but recreated from scratch -- this is why database is called \fIconstant\fR. Cdb file is optimized for quick access. Format of such file described in \fIcdb\fR(5) manpage. This manual page corresponds to version \fB0.81\fR of \fBtinycdb\fR package. Library defines two non-interlaced interfaces: for querying existing cdb file data (read-only mode) and for creating such a file (almost write-only). Strictly speaking, those modes allows very limited set of opposite operation as well (i.e. in query mode, it is possible to update key's value). All routines in this library are thread-safe as no global data used, except of \fIerrno\fR variable for error indication. .B cdb datafiles may be moved between systems safely, since format does not depend on architecture. .SH "QUERY MODE" There are two query modes available. First uses a structure that represents a cdb database, just like \fBFILE\fR structure in stdio library, and another works with plain filedescriptor. First mode is more sophisticated and flexible, and usually somewhat faster. It uses \fBmmap\fR(2) internally. This mode may look more "natural" or object-oriented compared to second one. The following routines works with any mode: .nf unsigned \fBcdb_unpack\fR(\fIbuf\fR) const unsigned char \fIbuf\fR[4]; .fi .RS helper routine to convert 32-bit integer from internal representation to machine format. May be used to handle application integers in a portable way. There is no error return. .RE .SS "Query Mode 1" All query operations in first more deals with common data structure, \fBstruct cdb\fR, associated with an open file descriptor. This structure is opaque to application. The following routines exists for accessing \fBcdb\fR database: .nf int \fBcdb_init\fR(\fIcdbp\fR, \fIfd\fR) struct cdb *\fIcdbp\fR; int \fIfd\fR; .fi .RS initializes structure given by \fIcdbp\fR pointer and associates it with opened file descriptor \fIfd\fR. Memory allocation for structure itself if needed and file open operation should be done by application. File \fIfd\fR should be opened at least read-only, and should be seekable. Routine returns 0 on success or negative value on error. .RE .nf void \fBcdb_free\fR(\fIcdbp\fR) struct cdb *\fIcdbp\fR; .fi .RS frees internal resources held by structure. Note that this routine does \fInot\fR closes a file. .RE .nf int \fBcdb_fileno\fR(\fIcdbp\fR) const struct cdb *\fIcdbp\fR; .fi .RS returns filedescriptor associated with cdb (as was passed to \fBcdb_init\fR()). .RE .nf int \fBcdb_read\fR(\fIcdbp\fR, \fIbuf\fR, \fIlen\fR, \fIpos\fR) int \fBcdb_readdata\fR(\fIcdbp\fR, \fIbuf\fR, \fIlen\fR, \fIpos\fR) int \fBcdb_readkey\fR(\fIcdbp\fR, \fIbuf\fR, \fIlen\fR, \fIpos\fR) const struct cdb *\fIcdbp\fR; void *\fIbuf\fR; unsigned \fIlen\fR; unsigned \fIpos\fR; .fi .RS reads a data from cdb file, starting at position \fIpos\fR of length \fIlen\fR, placing result to \fIbuf\fR. This routine may be used to get actual value found by \fBcdb_find\fR() or other routines that returns position and length of a data. Returns 0 on success or negative value on error. Routines \fBcdb_readdata\fR() and \fBcdb_readkey\fR() are shorthands to read current (after e.g. \fBcdb_find\fR()) data and key respectively, using \fBcdb_read\fR(). .RE .nf const void *\fBcdb_get\fR(\fIcdbp\fR, \fIlen\fR, \fIpos\fR) const void *\fBcdb_getdata\fR(\fIcdbp\fR) const void *\fBcdb_getkey\fR(\fIcdbp\fR) const struct cdb *\fIcdbp\fR; unsigned \fIlen\fR; unsigned \fIpos\fR; .fi .RS Internally, cdb library uses memory-mmaped region to access the on-disk database. \fBcdb_get\fR() allows one to access internal memory in a way similar to \fBcdb_read\fR() but without extra copying and buffer allocation. Returns pointer to actual data on success or NULL on error (position points to outside of the database). Routines \fBcdb_getdata\fR() and \fBcdb_getkey\fR() are shorthands to access current (after e.g. \fBcdb_find\fR()) data and key respectively, using \fBcdb_get\fR(). .RE .nf int \fBcdb_find\fR(\fIcdbp\fR, \fIkey\fR, \fIklen\fR) unsigned \fBcdb_datapos\fR(\fIcdbp\fR) unsigned \fBcdb_datalen\fR(\fIcdbp\fR) unsigned \fBcdb_keypos\fR(\fIcdbp\fR) unsigned \fBcdb_keylen\fR(\fIcdbp\fR) struct cdb *\fIcdbp\fR; const void *\fIkey\fR; unsigned \fIklen\fR; .fi .RS attempts to find a key given by (\fIkey\fR,\fIklen\fR) parameters. If key exists in database, routine returns 1 and places position and length of value associated with this key to internal fields inside \fIcdbp\fR structure, to be accessible by \fBcdb_datapos\fR(\fIcdbp\fR) and \fBcdb_datalen\fR(\fIcdbp\fR) routines. If key is not in database, \fBcdb_find\fR() returns 0. On error, negative value is returned. Data pointers (available via \fBcdb_datapos\fR() and \fBcdb_datalen\fR()) gets updated only in case of successful search. Note that using \fBcdb_find\fR() it is possible to lookup only \fIfirst\fR record with a given key. .RE .nf int \fBcdb_findinit(\fIcdbfp\fR, \fIcdbp\fR, \fIkey\fR, \fIklen\fR) int \fBcdb_findnext\fR(\fIcdbfp\fR) struct cdb_find *\fIcdbfp\fR; const struct cdb *\fIcdbp\fR; const void *\fIkey\fR; unsigned \fIklen\fR; .fi .RS sequential-find routines that used separate structure. It is possible to have more than one record with the same key in a database, and these routines allows one to enumerate all them. \fBcdb_findinit\fR() initializes search structure pointed to by \fIcdbfp\fR. It will return negative value on error or non-negative value on success. \fBcdb_findnext\fR() attempts to find next (first when called right after \fBcdb_findinit\fR()) matching key, setting value position and length in \fIcdbfp\fR structure. It will return positive value if given key was found, 0 if there is no more such key(s), or negative value on error. To access value position and length after successful call to \fBcdb_findnext\fR() (when it returned positive result), use \fBcdb_datapos\fR(\fIcdbp\fR) and \fBcdb_datalen\fR(\fIcdbp\fR) routines. It is error to continue using \fBcdb_findnext\fR() after it returned 0 or error condition (\fBcdb_findinit\fR() should be called again). Current data pointers (available via \fBcdb_datapos\fR() and \fBcdb_datalen\fR()) gets updated only on successful search. .RE .nf void \fBcdb_seqinit\fR(\fIcptr\fR, \fIcdbp\fR) int \fBcdb_seqnext\fR(\fIcptr\fR, \fIcdbp\fR) unsigned *\fIcptr\fR; struct cdb *\fIcdbp\fR; .fi .RS sequential enumeration of all records stored in cdb file. \fBcdb_seqinit\fR() initializes access current data pointer \fIcptr\fR to point before first record in a cdb file. \fBcdb_seqnext\fR() updates data pointers in \fIcdbp\fR to point to the next record and updates \fIcptr\fR, returning positive value on success, 0 on end of data condition and negative value on error. Current record will be available after successful operation using \fBcdb_datapos\fR(\fIcdbp\fR) and \fBcdb_datalen\fR(\fIcdbp\fR) (for the data) and \fBcdb_keypos\fR(\fIcdbp\fR) and \fBcdb_keylen\fR(\fIcdbp\fR) (for the key of the record). Data pointers gets updated only in case of successful operation. .RE .SS "Query Mode 2" In this mode, one need to open a \fBcdb\fR file using one of standard system calls (such as \fBopen\fR(2)) to obtain a filedescriptor, and then pass that filedescriptor to cdb routines. Available methods to query a cdb database using only a filedescriptor include: .nf int \fBcdb_seek\fR(\fIfd\fR, \fIkey\fR, \fIklen\fR, \fIdlenp\fR) int \fIfd\fR; const void *\fIkey\fR; unsigned \fIklen\fR; unsigned *\fIdlenp\fR; .fi .RS searches a cdb database (as pointed to by \fIfd\fR filedescriptor) for a key given by (\fIkey\fR, \fIklen\fR), and positions file pointer to start of data associated with that key if found, so that next read operation from this filedescriptor will read that value, and places length of value, in bytes, to variable pointed to by \fIdlenp\fR. Returns positive value if operation was successful, 0 if key was not found, or negative value on error. To read the data from a cdb file, \fBcdb_bread\fR() routine below can be used. .RE .nf int \fBcdb_bread\fR(\fIfd\fR, \fIbuf\fR, \fIlen\fR) int \fIfd\fR; void *\fIbuf\fR; int \fIlen\fR; .fi .RS reads data from a file (as pointed to by \fIfd\fR filedescriptor) and places \fIlen\fR bytes from this file to a buffer pointed to by \fIbuf\fR. Returns 0 if exactly \fIlen\fR bytes was read, or a negative value in case of error or end-of-file. This routine ignores interrupt errors (EINTR). Sets errno variable to \fBEIO\fR in case of end-of-file condition (when there is less than \fIlen\fR bytes available to read). .RE .SS Notes Note that \fIvalue\fR of any given key may be updated in place by another value of the same size, by writing to file at position found by \fBcdb_find\fR() or \fBcdb_seek\fR(). However one should be very careful when doing so, since write operation may not succeed in case of e.g. power failure, thus leaving corrupted data. When database is (re)created, one can guarantee that no incorrect data will be written to database, but not with inplace update. Note also that it is not possible to update any key or to change length of value. .SS .SH "CREATING MODE" .B cdb database file should usually be created in two steps: first, temporary file created and written to disk, and second, that temporary file is renamed to permanent place. Unix rename(2) call is atomic operation, it removes destination file if any AND renaes another file in one step. This way it is guaranteed that readers will not see incomplete database. To prevent multiple simultaneous updates, locking may also be used. All routines used to create \fBcdb\fR database works with \fBstruct cdb_make\fR object that is opaque to application. Application may assume that \fBstruct cdb_make\fR has at least the same member(s) as published in \fBstruct cdb\fR above. .nf int \fBcdb_make_start\fR(\fIcdbmp\fR, \fIfd\fR) struct cdb_make *\fIcdbmp\fR; int \fIfd\fR; .fi .RS initializes structure to create a database. File \fIfd\fR should be opened read-write and should be seekable. Returns 0 on success or negative value on error. .RE .nf int \fBcdb_make_add\fR(\fIcdbmp\fR, \fIkey\fR, \fIklen\fR, \fIval\fR, \fIvlen\fR) struct cdb_make *\fIcdbmp\fR; const void *\fIkey\fR, *\fIval\fR; unsigned \fIklen\fR, \fIvlen\fR; .fi .RS adds record with key (\fIkey\fR,\fIklen\fR) and value (\fIval\fR,\fIvlen\fR) to a database. Returns 0 on success or negative value on error. Note that this routine does not checks if given key already exists, but \fBcdb_find\fR() will not see second record with the same key. It is not possible to continue building a database if \fBcdb_make_add\fR() returned error indicator. .RE .nf int \fBcdb_make_finish\fR(\fIcdbmp\fR) struct cdb_make *\fIcdbmp\fR; .fi .RS finalizes database file, constructing all needed indexes, and frees memory structures. It does \fInot\fR closes filedescriptor. Returns 0 on success or negative value on error. .RE .nf int \fBcdb_make_exists\fR(\fIcdbmp\fR, \fIkey\fR, \fIklen\fR) struct cdb_make *\fIcdbmp\fR; const void *\fIkey\fR; unsigned \fIklen\fR; .fi .RS This routine attempts to find given by (\fIkey\fR,\fIklen\fR) key in a not-yet-complete database. It may significantly slow down the whole process, and currently it flushes internal buffer to disk on every call with key those hash value already exists in db. Returns 0 if such key doesn't exists, 1 if it is, or negative value on error. Note that database file should be opened read-write (not write-only) to use this routine. If \fBcdb_make_exists\fR() returned error, it may be not possible to continue constructing database. .RE .nf int \fBcdb_make_find\fR(\fIcdbmp\fR, \fIkey\fR, \fIklen\fR, \fImode\fR) struct cdb_make *\fIcdbmp\fR; const void *\fIkey\fR; unsigned \fIklen\fR; int \fImode\fR; .fi .RS This routine attempts to find given by (\fIkey\fR,\fIklen\fR) key in the database being created. If the given key is already exists, it an action specified by \fImode\fR will be performed: .IP \fBCDB_FIND\fR checks whenever the given record is already in the database. .IP \fBCDB_FIND_REMOVE\fR removes all matching records by re-writing the database file accordingly. .IP \fBCDB_FIND_FILL0\fR fills all matching records with zeros and removes them from index so that the records in question will not be findable with \fBcdb_find\fR(). This is faster than CDB_FIND_REMOVE, but leaves zero "gaps" in the database. Lastly inserted records, if matched, are always removed. .PP If no matching keys was found, routine returns 0. In case at least one record has been found/removed, positive value will be returned. On error, negative value will be returned and \fBerrno\fR will be set appropriately. When \fBcdb_make_find\fR() returned negative value in case of error, it is not possible to continue constructing the database. .PP \fBcdb_make_exists\fR() is the same as calling \fBcdb_make_find\fR() with \fImode\fR set to CDB_FIND. .RE .nf int \fBcdb_make_put\fR(\fIcdbmp\fR, \fIkey\fR, \fIklen\fR, \fIval\fR, \fIvlen\fR, \fImode\fR) struct cdb_make *\fIcdbmp\fR; const void *\fIkey\fR, *\fIval\fR; unsigned \fIklen\fR, \fIvlen\fR; int \fImode\fR; .fi .RS This is a somewhat combined \fBcdb_make_exists\fR() and \fBcdb_make_add\fR() routines. \fImode\fR argument controls how repeated (already existing) keys will be treated: .IP \fBCDB_PUT_ADD\fR no duplicate checking will be performed. This mode is the same as \fBcdb_make_add\fR() routine does. .IP \fBCDB_PUT_REPLACE\fR If the key already exists, it will be removed from the database before adding new key,value pair. This requires moving data in the file, and can be quite slow if the file is large. All matching old records will be removed this way. This is the same as calling \fBcdb_make_find\fR() with CDB_FIND_REMOVE \fImode\fR argument followed by calling \fBcdb_make_add\fR(). .IP \fBCDB_PUT_REPLACE0\fR If the key already exists and it isn't the last record in the file, old record will be zeroed out before adding new key,value pair. This is a lot faster than CDB_PUT_REPLACE, but some extra data will still be present in the file. The data -- old record -- will not be accessible by normal searches, but will appear in sequential database traversal. This is the same as calling \fBcdb_make_find\fR() with CDB_FIND_FILL0 \fImode\fR argument followed by \fBcdb_make_add\fR(). .IP \fBCDB_PUT_INSERT\fR add key,value pair only if such key does not exists in a database. Note that since query (see query mode above) will find first added record, this mode is somewhat useless (but allows one to reduce database size in case of repeated keys). This is the same as calling \fBcdb_make_exists\fR(), followed by \fBcdb_make_add\fR() if the key was not found. .IP \fBCDB_PUT_WARN\fR add key,value pair unconditionally, but also check if this key already exists. This is equivalent of \fBcdb_make_exists\fR() to check existence of the given key, unconditionally followed by \fBcdb_make_add\fR(). .PP If any error occurred during operations, the routine will return negative integer and will set global variable \fBerrno\fR to indicate reason of failure. In case of successful operation and no duplicates found, routine will return 0. If any duplicates has been found or removed (which, in case of CDB_PUT_INSERT mode, indicates that the new record was not added), routine will return positive value. If an error occurred and \fBcdb_make_put\fR() returned negative error, it is not possible to continue database construction process. .PP As with \fBcdb_make_exists\fR() and \fBcdb_make_find\fR(), usage of this routine with any but CDB_PUT_ADD mode can significantly slow down database creation process, especially when \fImode\fR is equal to CDB_PUT_REPLACE0. .RE .nf void \fBcdb_pack\fR(\fInum\fR, \fIbuf\fR) unsigned \fInum\fR; unsigned char \fIbuf\fR[4]; .fi .RS helper routine that used internally to convert machine integer \fIn\fR to internal form to be stored in datafile. 32-bit integer is stored in 4 bytes in network byte order. May be used to handle application data. There is no error return. .RE .nf unsigned \fBcdb_hash\fR(\fIbuf\fR, \fIlen\fR) const void *\fIbuf\fR; unsigned \fIlen\fR; .fi .RS helper routine that calculates cdb hash value of given bytes. CDB hash function is .br hash[n] = (hash[n\-1] + (hash[n\-1] << 5)) ^ buf[n] .br starting with .br hash[\-1] = 5381 .br .RE .SH ERRORS .B cdb library may set \fBerrno\fR to following on error: .IP EPROTO database file is corrupted in some way .IP EINVAL the same as EPROTO above if system lacks EPROTO constant .IP EINVAL \fIflag\fR argument for \fBcdb_make_put\fR() is invalid .IP EEXIST \fIflag\fR argument for \fBcdb_make_put\fR() is CDB_PUT_INSERT, and key already exists .IP ENOMEM not enough memory to complete operation (\fBcdb_make_finish\fR and \fBcdb_make_add\fR) .IP EIO set by \fBcdb_bread\fR and \fBcdb_seek\fR if a cdb file is shorter than expected or corrupted in some other way. .SH EXAMPLES .PP Note: in all examples below, error checking is not shown for brewity. .SS "Query Mode" .nf int fd; struct cdb cdb; char *key, *data; unsigned keylen, datalen; /* opening the database */ fd = open(filename, O_RDONLY); cdb_init(&cdb, fd); /* initialize key and keylen here */ /* single\-record search. */ if (cdb_find(&cdb, key, keylen) > 0) { datalen = cdb_datalen(&cdb); data = malloc(datalen + 1); cdb_read(&cdb, data, datalen, cdb_datapos(&cdb)); data[datalen] = '\\0'; printf("key=%s data=%s\\n", key, data); free(data); } else printf("key=%s not found\\n", key); /* multiple record search */ struct cdb_find cdbf; int n; cdb_findinit(&cdbf, &cdb, key, keylen); n = 0; while(cdb_findnext(&cdbf) > 0) { datalen = cdb_datalen(&cdb); data = malloc(datalen + 1); cdb_read(&cdb, data, datalen, cdb_datapos(&cdb)); data[datalen] = '\\0'; printf("key=%s data=%s\\n", key, data); free(data); ++n; } printf("key=%s %d records found\\n", n); /* sequential database access */ unsigned pos; int n; cdb_seqinit(&pos, &cdb); n = 0; while(cdb_seqnext(&pos, &cdb) > 0) { keylen = cdb_keylen(&cdb); key = malloc(keylen + 1); cdb_read(&cdb, key, keylen, cdb_keypos(&cdb)); key[keylen] = '\\0'; datalen = cdb_datalen(&cdb); data = malloc(datalen + 1); cdb_read(&cdb, data, datalen, cdb_datapos(&cdb)); data[datalen] = '\\0'; ++n; printf("record %n: key=%s data=%s\\n", n, key, data); free(data); free(key); } printf("total records found: %d\\n", n); /* close the database */ cdb_free(&cdb); close(fd); /* simplistic query mode */ fd = open(filename, O_RDONLY); if (cdb_seek(fd, key, keylen, &datalen) > 0) { data = malloc(datalen + 1); cdb_bread(fd, data, datalen); data[datalen] = '\\0'; printf("key=%s data=%s\\n", key, data); } else printf("key=%s not found\\n", key); close(fd); .fi .SS "Create Mode" .nf int fd; struct cdb_make cdbm; char *key, *data; unsigned keylen, datalen; /* initialize the database */ fd = open(filename, O_RDWR|O_CREAT|O_TRUNC, 0644); cdb_make_start(&cdbm, fd); while(have_more_data()) { /* initialize key and data */ if (cdb_make_exists(&cdbm, key, keylen) == 0) cdb_make_add(&cdbm, key, keylen, data, datalen); /* or use cdb_make_put() with appropriate flags */ } /* finalize and close the database */ cdb_make_finish(&cdbm); close(fd); .fi .SH "SEE ALSO" cdb(5), cdb(1), dbm(3), db(3), open(2). .SH AUTHOR The \fBtinycdb\fR package written by Michael Tokarev , based on ideas and shares file format with original cdb library by Dan Bernstein. .SH LICENSE Tinycdb is licensed under MIT license. tinycdb-0.81/cdb.10000644000175000017500000001527714542364427012105 0ustar mjtmjt.\" cdb.1: cdb command tool manpage .\" .\" This file is a part of tinycdb package. .\" Copyright (C) 2001-2023 Michael Tokarev .\" Tinycdb is licensed under MIT license. .\" .TH cdb 1 "Jan 2009" .SH NAME cdb \- Constant DataBase manipulation tool .SH SYNOPSYS \fBcdb\fR \-q [\-m] [\-n \fInum\fR] \fIdbname\fR \fIkey\fR .br \fBcdb\fR \-d [\-m] [\fIdbname\fR|\-] .br \fBcdb\fR \-l [\-m] [\fIdbname\fR|\-] .br \fBcdb\fR \-s [\fIdbname\fR|\-] .br \fBcdb\fR \-c [\-m] [\-t \fItmpname\fR|\-] [\-p \fIperms\fR] [\-weru0] \fIdbname\fR [\fIinfile\fR...] .SH DESCRIPTION \fBcdb\fR used to query, dump, list, analyze or create CDB (Constant DataBase) files. Format of cdb described in \fIcdb\fR(5) manpage. This manual page corresponds to version \fB0.81\fR of \fBtinycdb\fR package. .SS Query \fBcdb \-q\fR finds given \fIkey\fR in a given \fIdbname\fR cdb file, and writes associated value to standard output if found (and exits with zero), or exits with non\-zero if not found. \fIdbname\fR must be seekable file, and stdin can not be used as input. By default, \fBcdb\fR will print \fIall\fR records found. Options recognized in query mode: .IP \fB\-n\fInum\fR causes \fBcdb\fR to find and write a record with a given number \fInum\fR starting with 1 \(em when there are many records with a given key. .IP \fB\-m\fR newline will be added after every value printed. By default, multiple values will be written without any delimiter. .SS "Dump/List" \fBcdb \-d\fR dumps contents, and \fBcdb \-l\fR lists keys of \fIcdbfile\fR (or standard input if not specified) to standard output, in format controlled by presence of \fB\-m\fR option. See subsection "Formats" below. Output from \fBcdb \-d\fR can be used as an input for \fBcdb \-c\fR. .SS Create Cdb database created in two stages: temporary database is created, and after it is complete, it gets atomically renamed to permanent place. This avoids requirements for locking between readers and writers (or creaters). \fBcdb \-c\fR will attempt to create cdb in file \fItmpname\fR (or \fIdbname\fR with ".tmp" appended if no \-t option given) and then rename it to \fIdbname\fR. It will read supplied \fIinfile\fRs (or standard input if none specified). Options recognized in create mode: .IP "\fB\-t \fItmpname\fR" use given \fItmpname\fR as temporary file. Defaults to \fIdbname\fR.tmp (i.e. with output file with .tmp added). Note \fItmpname\fR must be in the same filesystem as output file, as .B cdb uses .IR rename (2) to finalize the database creation procedure. If \fItmpname\fR is a single dash (\-), no temp file will be created, database will be built in\-place. This mode is useful when the final renaming is done by the caller. .IP "\fB\-p \fIperms\fR" permissions for the newly created file (usually an octal number, like 0644). By default the permissions are 0666 (with current process umask applied). If this option is specified, current umask value has no effect. .IP \fB\-w\fR warn about duplicate keys. .IP \fB\-e\fR abort on duplicate keys (implies \-w). .IP \fB\-r\fR replace existing key with new one in case of duplicate. This may require database file rewrite to remove old records, and can be slow. .IP \fB\-0\fR zero-fill existing records when duplicate records are added. This is faster than \fB\-r\fR, but leaves extra zeros in the database file in case of duplicates. .IP \fB\-u\fR do not add duplicate records. .IP \fB\-m\fR interpret input as a sequence of lines, one record per line, with value separated from a key by space or tab characters, instead of native cdb format (see "Input/Output Format" below). .PP Note that using any option that requires duplicate checking will slow creation process \fIsignificantly\fR, especially for large databases. .SS Statistics \fBcdb \-s\fR will analyze \fIdbfile\fR and print summary to standard output. Statistics include: total number of rows in a file, minimum, average and maximum key and value lengths, hash tables (max 256) and entries used, number of hash collisions (that is, more than one key point to the same hash table entry), minimum, average and maximum hash table size (of non-empty tables), and number of keys that sits at 10 different distances from it's calculated hash table index \(em keys in distance 0 requires only one hash table lookup, 1 \(em two and so on; more keys at greater distance means slower database search. .SS "Input/Output Format" By default, \fBcdb\fR expects (for create operation) or writes (for dump/list) native cdb format data. Cdb native format is a sequence of records in a form: .br +\fIklen\fR,\fIvlen\fR:\fIkey\fR\->\fIval\fR\\n .br where "+", ",", ":", "\-", ">" and "\\n" (newline) are literal characters, \fIklen\fR and \fIvlen\fR are length of key and value as decimal numbers, and \fIkey\fR and \fIval\fR are key and value themselves. Series of records terminated by an empty line. This is the only format where key and value may contain any character including newline, zero (\\0) and so on. When \fB\-l\fR option requested (list keys mode), \fBcdb\fR will produce slightly modified output in a form: .br +\fIklen\fR:\fIkey\fR\\n .br (note \fIvlen\fR and \fIval\fR are omitted, together with surrounding delimiters). If \fB\-m\fR option is given, \fBcdb\fR will expect or produce one line for every record (newline is a record delimiter), and every line should contain optional whitespace, key, whitespace and value up to end of line. Lines started with hash character (#) and empty lines are ignored. This is the same format as \fBmkmap\fR(1) utility expects. .SH "OPTIONS SUMMARY" Here is a short summary of all options accepted by \fBcdb\fR utility: .IP \fB\-0\fR zero-fill duplicate records in create (\fB\-c\fR) mode. .IP \fB\-c\fR create mode. .IP \fB\-d\fR dump mode. .IP \fB\-e\fR abort (error) on duplicate key in create (\fB\-c\fR) mode. .IP \fB\-h\fR print short help and exit. .IP \fB\-l\fR list mode. .IP \fB\-m\fR input or output is in "map" format, not in native cdb format. In query mode, add a newline after every value written. .IP \fB\-n\fInum\fR find and print \fInum\fRth record in query (\fB\-q\fR) mode. .IP \fB\-q\fR query mode. .IP \fB\-r\fR replace duplicate keys in create (\fB\-c\fR) mode. .IP \fB\-s\fR statistics mode. .IP "\fB\-t\fR \fItempfile\fR" specify temporary file when creating (\fB\-c\fR) cdb file (use single dash (\-) as \fItempfile\fR to stop using temp file). .IP \fB\-u\fR do not insert duplicate keys (unique) in create (\fB\-c\fR) mode. .IP \fB\-w\fR warn about duplicate keys in create (\fB\-c\fR) mode. .SH AUTHOR The \fBtinycdb\fR package written by Michael Tokarev , based on ideas and shares file format with original cdb library by Dan Bernstein. .SH "SEE ALSO" cdb(5), cdb(3). .SH LICENCE Tinycdb is licensed under MIT license. tinycdb-0.81/cdb.50000644000175000017500000000574514542364427012110 0ustar mjtmjt.\" cdb.5: cdb file format manpage .\" .\" This file is a part of tinycdb package. .\" Copyright (C) 2001-2023 Michael Tokarev .\" Tinycdb is licensed under MIT license. .\" .TH cdb 5 "Apr, 2005" .SH NAME cdb \- Constant DataBase file format .SH DESCRIPTION A \fBcdb\fR database is a single file used to map `keys' to `values', having records of (key,value) pairs. File consists of 3 parts: toc (table of contents), data and index (hash tables). Toc has fixed length of 2048 bytes, containing 256 pointers to hash tables inside index sections. Every pointer consists of position of a hash table in bytes from the beginning of a file, and a size of a hash table in entries, both are 4-bytes (32 bits) unsigned integers in little-endian form. Hash table length may have zero length, meaning that corresponding hash table is empty. Right after toc section, data section follows without any alingment. It consists of series of records, each is a key length, value (data) length, key and value. Again, key and value length are 4-byte unsigned integers. Each next record follows previous without any special alignment. After data section, index (hash tables) section follows. It should be looked to in conjunction with toc section, where each of max 256 hash tables are defined. Index section consists of series of hash tables, with starting position and length defined in toc section. Every hash table is a sequence of records each holds two numbers: key's hash value and record position inside data section (bytes from the beginning of a file to first byte of key length starting data record). If record position is zero, then this is an empty hash table slot, pointed to nowhere. CDB hash function is .nf hv = ((hv << 5) + hv) ^ \fIc\fR .fi for every single \fIc\fR byte of a key, starting with hv = \fI5381\fR. Toc section indexed by (hv % 256), i.e. hash value modulo 256 (number of entries in toc section). In order to find a record, one should: first, compute the hash value (hv) of a key. Second, look to hash table number hv modulo 256. If it is empty, then there is no such key exists. If it is not empty, then third, loop by slots inside that hash table, starting from slot with number hv divided by 256 modulo length of that table, or ((hv / 256) % htlen), searching for this hv in hash table. Stop search on empty slot (if record position is zero) or when all slots was probed (note cyclic search, jumping from end to beginning of a table). When hash value in question is found in hash table, look to key of corresponding record, comparing it with key in question. If them of the same length and equals to each other, then record is found, overwise, repeat with next hash table slot. Note that there may be several records with the same key. .SH SEE ALSO cdb(1), cdb(3). .SH AUTHOR The \fBtinycdb\fR package written by Michael Tokarev , based on ideas and shares file format with original cdb library by Dan Bernstein. .SH LICENSE Tinycdb is licensed under MIT license. tinycdb-0.81/tinycdb.spec0000644000175000017500000000453714542366242013575 0ustar mjtmjt# tinycdb.spec: tinycdb RPM spec file. # # This file is a part of tinycdb package by Michael Tokarev, mjt+cdb@corpit.ru. # Tinycdb is licensed under CC BY-SA 4.0. Summary: A package for maintenance of constant databases Name: tinycdb Version: 0.81 Release: 1 Source: http://www.corpit.ru/mjt/tinycdb/tinycdb_%version.tar.gz License: MIT Group: System Environment/Libraries Prefix: %{_prefix} BuildRoot: %{_tmppath}/%{name}-root Summary: TinyCDB - a Constant DataBase %description tinycdb is a small, fast and reliable utility set and subroutine library for creating and reading constant databases. The database structure is tuned for fast reading: + Successful lookups take normally just two disk accesses. + Unsuccessful lookups take only one disk access. + Small disk space and memory size requirements; a database uses 2048 bytes for the header and 24 bytes plus size of (key,value) per record. + Maximum database size is 4GB; individual record size is not otherwise limited. + Portable file format. + Fast creation of new databases. + No locking, updates are atomical. This package contains both the utility and the development files, together with nss_cdb module. %package devel Summary: Development files for the tinycdb library. Group: System Environment/Libraries Requires: %name = %version-%release Summary: Development files for tinycdb %description devel tinycdb is a small, fast and reliable utility set and subroutine library for creating and reading constant databases. This package contains tinycdb development libraries and header files. %prep %setup -q %build make CFLAGS="$RPM_OPT_FLAGS" \ staticlib sharedlib cdb-shared nss \ sysconfdir=/etc %install rm -rf $RPM_BUILD_ROOT mkdir -p $RPM_BUILD_ROOT %makeinstall DESTDIR=$RPM_BUILD_ROOT \ libdir=%_libdir bindir=%_bindir mandir=%_mandir \ syslibdir=/%_lib sysconfdir=/etc \ includedir=%_includedir \ install-all install-nss install-piclib install-sharedlib \ INSTALLPROG=cdb-shared CP="cp -p" %files %defattr(-,root,root) %_bindir/* %_mandir/man1/* %_mandir/man5/* %_libdir/libcdb.so.* /%_lib/libnss_cdb* /etc/cdb-Makefile %doc ChangeLog NEWS debian/changelog %files devel %defattr(-,root,root) %_libdir/libcdb.a %_libdir/libcdb_pic.a %_libdir/libcdb.so %_libdir/pkgconfig/libcdb.pc %_mandir/man3/* %_includedir/* %clean rm -rf $RPM_BUILD_ROOT %post -p /sbin/ldconfig %postun -p /sbin/ldconfig %changelog tinycdb-0.81/tests.sh0000755000175000017500000000466114542364427012767 0ustar mjtmjt#! /bin/sh # tests.sh: This script will run tests for cdb. # Execute with ./tests.sh ./cdb # (first arg if present gives path to cdb tool to use, default is `cdb'). # # This file is a part of tinycdb package. # Copyright (C) 2001-2023 Michael Tokarev # Tinycdb is licensed under MIT license. case "$1" in "") cdb=cdb ;; *) cdb="$1" ;; esac do_csum() { echo checksum may fail if no md5sum program md5sum $1 | sed -e 's|[ ].*||' -e 'y|[ABCDEF]|[abcdef]|' } rm -f 1.cdb 1a.cdb echo Create simple db echo "+3,4:one->here +1,1:a->b +1,3:b->abc +3,4:one->also " | $cdb -c 1.cdb echo $? do_csum 1.cdb echo Dump simple db $cdb -d 1.cdb echo $? echo Stats for simple db $cdb -s 1.cdb echo $? echo "Query simple db (two records match)" $cdb -q 1.cdb one echo " $?" echo Query for non-existed key $cdb -q 1.cdb none echo $? echo Doing 600 repeated records ( for i in 0 1 2 3 4 5 ; do for j in 0 1 2 3 4 5 6 7 8 9 ; do for k in 0 1 2 3 4 5 6 7 8 9 ; do echo "+1,3:a->$i$j$k" done done done echo "+1,5:b->other" echo ) | $cdb -c 1.cdb echo $? do_csum 1.cdb echo cdb stats should show 601 record $cdb -s 1.cdb echo $? echo Querying key $cdb -q 1.cdb b echo " "$? echo Dumping and re-creating db $cdb -d 1.cdb | $cdb -c 1a.cdb echo $? cmp 1.cdb 1a.cdb $cdb -d -m 1.cdb | $cdb -c -m 1a.cdb echo $? cmp 1.cdb 1a.cdb echo Handling large key size echo "+123456789012,1:" | $cdb -c 1.cdb echo $? echo Handling large value size echo "+1,123456789012:" | $cdb -c 1.cdb echo $? echo "Handling invalid input format (short file)" echo "+10,10:" | $cdb -c 1.cdb echo $? echo Creating db with eol in key and value echo "+2,2:a ->b " | $cdb -c 1.cdb echo $? do_csum 1.cdb echo Querying key-value with eol $cdb -q 1.cdb "a " echo $? echo Handling file size limits ( ulimit -f 4 trap '' 25 ( for i in 0 1 2 3 4 5 6 7 8 9 ; do for j in 0 1 2 3 4 5 6 7 8 9 ; do for k in 0 1 2 3 4 5 6 7 8 9 ; do echo "+4,4:k$i$j$k->v$i$j$k" done done done echo ) | $cdb -c 1.cdb echo $? ) if false ; then # does not work for now, bugs in libc echo Handling oom condition ( for i0 in 0 1 2 3 4 5 6 7 8 9 ; do for i1 in 0 1 2 3 4 5 6 7 8 9 ; do for i2 in 0 1 2 3 4 5 6 7 8 9 ; do for i3 in 0 1 2 3 4 5 6 7 8 9 ; do for i4 in 0 1 2 3 4 5 6 7 8 9 ; do echo "+5,0:$i0$i1$i2$i3$i4->" done done done done done echo ) | (ulimit -v 1900; $cdb -c 1.cdb) echo $? fi rm -rf 1.cdb 1a.cdb 1.cdb.tmp exit 0 tinycdb-0.81/tests.ok0000644000175000017500000000302511753175776012764 0ustar mjtmjtCreate simple db 0 checksum may fail if no md5sum program 97549c2e76e2d446430a392d77ed1bcb Dump simple db +3,4:one->here +1,1:a->b +1,3:b->abc +3,4:one->also 0 Stats for simple db number of records: 4 key min/avg/max length: 1/2/3 val min/avg/max length: 1/3/4 hash tables/entries/collisions: 3/8/1 hash table min/avg/max length: 2/3/4 hash table distances: d0: 3 75% d1: 1 25% d2: 0 0% d3: 0 0% d4: 0 0% d5: 0 0% d6: 0 0% d7: 0 0% d8: 0 0% d9: 0 0% >9: 0 0% 0 Query simple db (two records match) herealso 0 Query for non-existed key 100 Doing 600 repeated records 0 checksum may fail if no md5sum program 412a0b7578efca528bf8398c8811caf4 cdb stats should show 601 record number of records: 601 key min/avg/max length: 1/1/1 val min/avg/max length: 3/3/5 hash tables/entries/collisions: 2/1202/599 hash table min/avg/max length: 2/601/1200 hash table distances: d0: 2 0% d1: 1 0% d2: 1 0% d3: 1 0% d4: 1 0% d5: 1 0% d6: 1 0% d7: 1 0% d8: 1 0% d9: 1 0% >9: 590 98% 0 Querying key other 0 Dumping and re-creating db 0 0 Handling large key size cdb: (stdin): bad format 2 Handling large value size cdb: (stdin): bad format 2 Handling invalid input format (short file) cdb: unable to read: short file 2 Creating db with eol in key and value 0 checksum may fail if no md5sum program 1d444fe759c26d36f500d01c41cfda40 Querying key-value with eol b 0 Handling file size limits cdb: cdb_make_put: File too large 111 tinycdb-0.81/libcdb.map0000644000175000017500000000060711753177047013201 0ustar mjtmjt# libcdb.map: libcdb symbol map file for GNU LD { global: cdb_hash; cdb_unpack; cdb_pack; cdb_init; cdb_free; cdb_read; cdb_get; cdb_find; cdb_findinit; cdb_findnext; cdb_seqnext; cdb_seek; cdb_bread; cdb_make_start; cdb_make_add; cdb_make_exists; cdb_make_put; cdb_make_find; cdb_make_finish; local: *; }; tinycdb-0.81/nss_cdb.map0000644000175000017500000000063011753177202013362 0ustar mjtmjt# nss_cdb.map: libnss_cdb symbol map file for GNU LD { global: _nss_cdb_endpwent; _nss_cdb_getpwnam_r; _nss_cdb_endspent; _nss_cdb_getspent_r; _nss_cdb_getpwent_r; _nss_cdb_getgrgid_r; _nss_cdb_endgrent; _nss_cdb_getgrent_r; _nss_cdb_setspent; _nss_cdb_setpwent; _nss_cdb_getpwuid_r; _nss_cdb_getspnam_r; _nss_cdb_setgrent; _nss_cdb_getgrnam_r; local: *; }; tinycdb-0.81/NEWS0000644000175000017500000001056014542363654011761 0ustar mjtmjtUser-visible news. Latest at the top. tinycdb-0.81 2023-12-25 - ship simple libcdb.pc (install it to pkgconfdir=$(libdir)/pkgconfig) - to avoid further questions, change license from "Public domain" to MIT license, add copyright and license text to all source files. - change default compiler optimization level from -O to -O2, use gnu tar --transform option to create dist archive, other small changes to Makefile tinycdb-0.80 2023-09-17 - bugfix: cdb utility: check return value from close() too, when creating the cdb file - portability: cdb utility: ignore SIGXFSZ signal if defined, to be able to clean up when exceeding file limit - robustness: let cdb_make_start to lseek() to the zero position of file. This ensures the file is seekable right at the start instead of at the very end, too - robustness: cdb utility: remove temporary file in case create operation fails - portability use SEE_SET macro instead of 0 for lseek() - split out cdb_pack() into its own .c file tinycdb-0.79 2023-09-17 - bugfix: call fsync() at the end of cdb_make_finish() to ensure data does not stay in some kernel buffer - bugfix: clean tests.out in `make distclean' too - `make dist' makes tarball in the parent dir - stop shipping debian/* - it is not a native debian package tinycdb-0.78 2012-05-11 - bugfix release: o fixed >2Gb file size prob on 32bit platform o fixed handling of files >=4Gb o fixed a few compiler warnings - introduce $(LD) and $(LDFLAGS), and also $(CDEFS) in Makefile tinycdb-0.77 - bugfix release: manpage typos, portability fixes and the like - bugfix: improper logic in EINTR handling in _cdb_make_full_write routine which may lead to corruped .cdb file. tinycdb-0.76 - new cdb utility option: -p permissions, to specify permission bits for the newly created database file. - cdb utility, when creating the database, does unlink() of the (temp) db file and when opens it with O_CREAT|O_EXCL, instead of previous O_CREAT|O_TRUNC w/o unlink() behaviour, and uses O_NOFOLLOW flag if available. This avoids any possible symlink race conditions, but is somewhat incompatible with previous versions, where it was possible to create temp file beforehand with proper permissions. Together with new -p option (above), this isn't that big change. - cdb utility now allows to omit temp file usage (with final rename()) when creating the database, by specifying -t- (`-' as temp file name) (or -t the_same_name_as_the_db_file). Useful if the utility is called from a script which does its own rename afterwards. - alot of minor code changes to make modern compilers happy (mostly signed char vs unsigned char "fixes"). Including additions of `unsigned' into common structure definitions in cdb.h - the API stays compatible still. - several (spelling and other) fixes for manpages. - tinycdb is, once again, maintained as a native Debian package. Debian package has been split into 4 parts: libcdb1, libcdb-dev, tinycdb (the utility) and libnss_cdb. RPM .spec file now builds two packages as well: tinycdb (includes shared library, the utility, and nss_cdb module) and tinycdb-devel (development files). tinycdb-0.75 (2005-04-11) - make cdb_make_put(CDB_PUT_REPLACE) to actually *remove* all (was only first previously) matching records, by rewriting the file. - new mode CDB_PUT_REPLACE0, which zeroes out old duplicate records - fixed fputs() == NULL condition in cdb.c, finally (should be < 0, not == NULL) tinycdb-0.74 (2003-11-04) - reworked cdb utility, see manpage for details. cdb -q now prints all matching records by default (incompat change), use cdb -q -n XX to return only XXth record if any. -m works with -q. - there are several new routines (and macros) in library. - cdb_seqinit() and cdb_seqstart() to fetch all records - cdb_get() to get a pointer to data in internal buffer - cdb_{get,read}{data,key}() - cdbi_t gone (but still defined). Use unsigned instead. - cdb_findnext() changed - fixed a bug in cdb_seek() (EIO instead of getting valid record) - added nss_cdb (for passwd, group and shadow databases only) - Makefile targets to build PIC code (libcdb_pic.a, required for nss_cdb) and shared library (probably not a good idea) - Modifications to allow tinycdb to compile under win32, by Victor Porton (porton at ex-code.com).