aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am7
-rw-r--r--src/Makefile.in473
-rw-r--r--src/cfg.c87
-rw-r--r--src/compat.c59
-rw-r--r--src/duser.c307
-rw-r--r--src/log.c77
-rw-r--r--src/user.c577
-rw-r--r--src/util.c95
8 files changed, 1682 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..45d46ef
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,7 @@
+sbin_PROGRAMS= duser
+
+duser_SOURCES= duser.c \
+ user.c \
+ util.c \
+ log.c \
+ cfg.c
diff --git a/src/Makefile.in b/src/Makefile.in
new file mode 100644
index 0000000..498c72f
--- /dev/null
+++ b/src/Makefile.in
@@ -0,0 +1,473 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
+# Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+target_triplet = @target@
+sbin_PROGRAMS = duser$(EXEEXT)
+subdir = src
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/configure.in
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(sbindir)"
+PROGRAMS = $(sbin_PROGRAMS)
+am_duser_OBJECTS = duser.$(OBJEXT) user.$(OBJEXT) util.$(OBJEXT) \
+ log.$(OBJEXT) cfg.$(OBJEXT)
+duser_OBJECTS = $(am_duser_OBJECTS)
+duser_LDADD = $(LDADD)
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+SOURCES = $(duser_SOURCES)
+DIST_SOURCES = $(duser_SOURCES)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target = @target@
+target_alias = @target_alias@
+target_cpu = @target_cpu@
+target_os = @target_os@
+target_vendor = @target_vendor@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+duser_SOURCES = duser.c \
+ user.c \
+ util.c \
+ log.c \
+ cfg.c
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu src/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-sbinPROGRAMS: $(sbin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ test -z "$(sbindir)" || $(MKDIR_P) "$(DESTDIR)$(sbindir)"
+ @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p; \
+ then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-sbinPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(sbindir)" && rm -f $$files
+
+clean-sbinPROGRAMS:
+ -test -z "$(sbin_PROGRAMS)" || rm -f $(sbin_PROGRAMS)
+duser$(EXEEXT): $(duser_OBJECTS) $(duser_DEPENDENCIES)
+ @rm -f duser$(EXEEXT)
+ $(LINK) $(duser_OBJECTS) $(duser_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cfg.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/duser.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/log.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/user.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/util.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ set x; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(PROGRAMS)
+installdirs:
+ for dir in "$(DESTDIR)$(sbindir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-sbinPROGRAMS mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-sbinPROGRAMS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-sbinPROGRAMS
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+ clean-sbinPROGRAMS ctags distclean distclean-compile \
+ distclean-generic distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-data \
+ install-data-am install-dvi install-dvi-am install-exec \
+ install-exec-am install-html install-html-am install-info \
+ install-info-am install-man install-pdf install-pdf-am \
+ install-ps install-ps-am install-sbinPROGRAMS install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic pdf pdf-am ps ps-am tags uninstall \
+ uninstall-am uninstall-sbinPROGRAMS
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/src/cfg.c b/src/cfg.c
new file mode 100644
index 0000000..b30bd3f
--- /dev/null
+++ b/src/cfg.c
@@ -0,0 +1,87 @@
+/**
+* duser - Manage MajorDomo lists
+* Copyright (C) 2011 Joseph Hunkeler <jhunkeler@gmail.com, jhunk@stsci.edu>
+*
+* This file is part of duser.
+*
+* duser is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* duser is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with duser. If not, see <http://www.gnu.org/licenses/>.
+**/
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include "duser.h"
+#include "cfg.h"
+
+FILE *cfgfp;
+
+int cfg_open(const char* filename)
+{
+ if((cfgfp = fopen(filename, "r")) == NULL)
+ return errno;
+ else
+ return 0;
+}
+
+void cfg_close()
+{
+ fclose(cfgfp);
+}
+
+//Value of key
+int cfg_get_key(char* val, const char* key)
+{
+
+ if(cfgfp == NULL)
+ return -1;
+
+ if((fseek(cfgfp, 0L, SEEK_SET)) != 0)
+ return errno;
+
+ char buffer[CFG_MAX];
+ char *buf = buffer; //= (char*)malloc(sizeof(char) * CFG_MAX+1);
+ if(buffer == NULL)
+ {
+ fprintf(stderr, "%s: %s\n", SELF, strerror(errno));
+ exit(1);
+ }
+
+ int i = 0;
+ while(!feof(cfgfp))
+ {
+ memset(buffer, 0L, CFG_MAX);
+ fgets(buffer, CFG_MAX, cfgfp);
+ if((strcasestr(buffer, key)))
+ {
+ while(buf)
+ {
+ if(isspace(*buf))
+ {
+ strncpy(val, &buf[i+1], CFG_MAX);
+ val[strlen(val)-1] = 0;
+ break;
+ }
+ buf++;
+ }
+ i++;
+ break;
+ }
+ }
+
+ return 0;
+}
diff --git a/src/compat.c b/src/compat.c
new file mode 100644
index 0000000..d729229
--- /dev/null
+++ b/src/compat.c
@@ -0,0 +1,59 @@
+/**
+* duser - Manage MajorDomo lists
+* Copyright (C) 2011 Joseph Hunkeler <jhunkeler@gmail.com, jhunk@stsci.edu>
+*
+* This file is part of duser.
+*
+* duser is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* duser is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with duser. If not, see <http://www.gnu.org/licenses/>.
+**/
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include "compat.h"
+
+#ifndef HAVE_STRCHRNUL
+#undef strchrnul
+char *strchrnul(const char* s, int c)
+{
+ while(*s && *s != c)
+ {
+ s++;
+ }
+ return (char*)s;
+}
+#endif
+
+#ifndef HAVE_STRCASESTR
+#undef strcasestr
+char *strcasestr(const char *s, const char *find)
+{
+ char c, sc;
+ size_t len;
+
+ if ((c = *find++) != 0) {
+ c = tolower((unsigned char)c);
+ len = strlen(find);
+ do {
+ do {
+ if ((sc = *s++) == 0)
+ return (NULL);
+ } while ((char)tolower((unsigned char)sc) != c);
+ } while (strncasecmp(s, find, len) != 0);
+ s--;
+ }
+ return ((char *)s);
+}
+#endif
+
diff --git a/src/duser.c b/src/duser.c
new file mode 100644
index 0000000..8f5603f
--- /dev/null
+++ b/src/duser.c
@@ -0,0 +1,307 @@
+/**
+* duser - Manage MajorDomo lists
+* Copyright (C) 2011 Joseph Hunkeler <jhunkeler@gmail.com, jhunk@stsci.edu>
+*
+* This file is part of duser.
+*
+* duser is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* duser is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with duser. If not, see <http://www.gnu.org/licenses/>.
+**/
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dirent.h>
+#include <limits.h>
+#include <ctype.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <time.h>
+#include <fcntl.h>
+#include <libgen.h>
+
+#include "version.h"
+#include "cfg.h"
+#include "duser.h"
+
+char list_path[PATH_MAX];
+char logfile[PATH_MAX];
+static char progname[FILENAME_MAX];
+
+int CMD_FLAG_NOOPT = 0;
+int CMD_FLAG_DEL = 0;
+int CMD_FLAG_DEL_ALL = 0;
+int CMD_FLAG_DEL_LIST = 0;
+int CMD_FLAG_MOD = 0;
+int CMD_FLAG_ADD = 0;
+int CMD_FLAG_LIST = 0;
+int CMD_FLAG_HELP = 0;
+int CMD_FLAG_LOOK = 0;
+int CMD_FLAG_NEW = 0;
+int CMD_FLAG_NULL = 0;
+
+void version()
+{
+ printf("%s v%s.%s%s (%s %s)\n",
+ progname, VER_MAJOR, VER_MINOR, VER_OTH, __DATE__, __TIME__);
+}
+
+void usage()
+{
+ version();
+ printf("Usage: %s command [address [list]]\n", progname);
+ printf("Commands:\n");
+ printf(" help This usage statement\n");
+ printf(" add Add a user to a list\n");
+ printf(" del Delete a user from a list\n");
+ printf(" delA Delete a user from all lists\n");
+ printf(" delL Completely delete a mailing list\n");
+ printf(" mod Modify a user in a list\n");
+ printf(" new Creates a new list\n");
+ printf(" list Find and list a user in all lists\n");
+ printf(" look Find user in specific list\n");
+ printf("\n");
+ exit(0);
+}
+
+int main(int argc, char* argv[])
+{
+ strncpy(progname, argv[0], strlen(argv[0]));
+ if((cfg_open(CFG_PATH)) == 0)
+ {
+ cfg_get_key(list_path, "path");
+ cfg_get_key(logfile, "logfile");
+ cfg_close();
+ }
+ else
+ {
+ fprintf(stderr, "%s: %s: %s\n", SELF, CFG_PATH, strerror(errno));
+ return -1;
+ }
+
+ char filename[PATH_MAX];
+ record_t *rec;
+ const char* needle = argv[2];
+ const char* single_list = argv[3];
+
+ if(single_list)
+ snprintf(filename, PATH_MAX, "%s%s", list_path, single_list);
+
+ if(argc < 3)
+ {
+ usage();
+ return 0;
+ }
+
+ stats_init(&processed);
+ user_cmd(argc, argv);
+
+ if((strval(needle)) != 0)
+ {
+ fprintf(stderr, "%s: Invalid string\n", SELF);
+ exit(1);
+ }
+
+ if(CMD_FLAG_DEL)
+ {
+ if(needle == NULL)
+ {
+ printf("You must specify an email address\n");
+ return -1;
+ }
+ else if(single_list == NULL)
+ {
+ printf("You must specify a list to remove '%s' from\n", needle);
+ return -1;
+ }
+
+
+ if((rec = find_in_file(filename, needle)) == NULL)
+ {
+ printf("'%s' not found in '%s'\n", needle, basename(filename));
+ return -1;
+ }
+
+ printf("Please review the information below:\n\n");
+ printf("Email: %s\n", rec->name);
+ printf("At line: %d\n", rec->index);
+ printf("In File: %s\n", basename(rec->file));
+ printf("\nDo you wish to wish to delete this record? [y/N] ");
+
+ char choice = getchar();
+ if((user_choice(choice)) == 0)
+ {
+ if((user_del(rec)) > 0)
+ {
+ printf("Record deleted\n");
+ COM(SELF, "Commmand: DELETE\n");
+ COM(SELF, "'%s' deleted from '%s' at line %d\n", rec->name, basename(rec->file), rec->index);
+ }
+ }
+ else
+ {
+ printf("Aborting...\n");
+ }
+ return 0;
+ }
+
+ if(CMD_FLAG_DEL_ALL)
+ {
+ user_del_all(needle);
+ return 0;
+ }
+
+ if(CMD_FLAG_DEL_LIST)
+ {
+ user_del_list(needle);
+ return 0;
+ }
+
+ if(CMD_FLAG_ADD)
+ {
+ if(needle == NULL)
+ {
+ printf("You must specify an email address\n");
+ return -1;
+ }
+ else if(single_list == NULL)
+ {
+ printf("You must specify a list in which to add '%s' to\n", needle);
+ return -1;
+ }
+
+ printf("Please review the information below:\n\n");;
+ printf("Email: %s\n", needle);
+ printf("In File: %s\n", basename(filename));
+ printf("\nDo you wish to wish to add this record? [y/N] ");
+
+ char choice = getchar();
+ if((user_choice(choice)) == 0)
+ {
+ if((user_add(filename, needle)) > 0)
+ {
+ printf("Record added\n");
+ COM(SELF, "Commmand: ADD\n");
+ COM(SELF, "'%s' added to '%s'\n", needle, basename(filename));
+ }
+ }
+ else
+ {
+ printf("Aborting...\n");
+ }
+ return 0;
+ }
+ if(CMD_FLAG_MOD)
+ {
+ if(needle == NULL)
+ {
+ printf("You must specify an email address\n");
+ return -1;
+ }
+ else if(single_list == NULL)
+ {
+ printf("You must specify a list in which to modify '%s' in\n", needle);
+ return -1;
+ }
+
+ fprintf(stderr, "Not implemented, sorry.\n");
+ return 0;
+ }
+ if(CMD_FLAG_LIST)
+ {
+ if(needle == NULL)
+ {
+ printf("You must specify an email address\n");
+ return -1;
+ }
+ user_list(needle);
+ return 0;
+ }
+ if(CMD_FLAG_LOOK)
+ {
+ if(needle == NULL)
+ {
+ printf("You must specify an email address\n");
+ return -1;
+ }
+ else if(single_list == NULL)
+ {
+ printf("You must specify a list in which to find '%s' in\n", needle);
+ return -1;
+ }
+
+ rec = find_in_file(filename, needle);
+ if(rec)
+ {
+ if(rec->match)
+ {
+ printf("Found '%s' at line %d of list '%s'\n", rec->name, rec->index, basename(rec->file));
+ }
+ else
+ {
+ printf("Corrupt record? This is not supposed to happen.\n");
+ return -1;
+ }
+ }
+ else
+ {
+ printf("Not found in '%s'\n", single_list);
+ }
+ return 0;
+ }
+ if(CMD_FLAG_NEW)
+ {
+ memset(filename, 0L, PATH_MAX);
+ snprintf(filename, PATH_MAX, "%s%s", list_path, needle);
+
+ if(needle == NULL)
+ {
+ printf("You must specify a list to create\n");
+ return -1;
+ }
+
+ printf("Please review the information below:\n\n");;
+ printf("File: %s\n", filename);
+ printf("\nDo you wish to wish to create this list? [y/N] ");
+
+ char choice = getchar();
+ if((user_choice(choice)) == 0)
+ {
+ if((user_new_list(filename)) == 0)
+ {
+ printf("List added\n");
+ COM(SELF, "Commmand: NEW\n");
+ COM(SELF, "Created new list '%s'\n", basename(filename));
+ }
+ }
+ else
+ {
+ printf("Aborting...\n");
+ }
+ return 0;
+ }
+ if(CMD_FLAG_HELP)
+ {
+ usage();
+ }
+ if(CMD_FLAG_NULL)
+ {
+ fprintf(stderr, "Command not specified\n");
+ exit(1);
+ }
+
+ return 0;
+}
diff --git a/src/log.c b/src/log.c
new file mode 100644
index 0000000..c0c1051
--- /dev/null
+++ b/src/log.c
@@ -0,0 +1,77 @@
+/**
+* duser - Manage MajorDomo lists
+* Copyright (C) 2011 Joseph Hunkeler <jhunkeler@gmail.com, jhunk@stsci.edu>
+*
+* This file is part of duser.
+*
+* duser is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* duser is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with duser. If not, see <http://www.gnu.org/licenses/>.
+**/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <limits.h>
+#include <errno.h>
+#include <time.h>
+#include "duser.h"
+
+extern char logfile[PATH_MAX];
+//extern char* logfile;
+
+int logcleanup()
+{
+ int status = 0;
+ if((status = access(logfile, W_OK)) == 0)
+ {
+ status = unlink(logfile);
+ }
+
+ return status;
+}
+
+int COM(const char* func, char *format, ...)
+{
+ struct tm *logtm;
+ time_t logtime = time(NULL);
+ char timestr[64];
+ char *str = (char*)malloc(sizeof(char) * 256);
+ char tmpstr[255];
+ int n;
+
+ FILE *logfp = NULL;
+ logfp = fopen(logfile, "a+");
+ if(logfp == NULL)
+ {
+ fprintf(stderr, "Unable to open %s\n", logfile);
+ exit(1);
+ }
+
+ va_list args;
+ va_start (args, format);
+ n = vsprintf (str, format, args);
+ va_end (args);
+
+ logtm = localtime(&logtime);
+
+ snprintf(timestr, sizeof(timestr), "%02d-%02d-%02d %02d:%02d:%02d", logtm->tm_year+1900, logtm->tm_mon+1, logtm->tm_mday, logtm->tm_hour, logtm->tm_min, logtm->tm_sec);
+ snprintf(tmpstr, sizeof(tmpstr), "%s %d _%s_: %s", timestr, getuid(), func, str);
+ fprintf(logfp, "%s", tmpstr);
+ fclose(logfp);
+
+ free(str);
+ return 0;
+}
diff --git a/src/user.c b/src/user.c
new file mode 100644
index 0000000..72a7c13
--- /dev/null
+++ b/src/user.c
@@ -0,0 +1,577 @@
+/**
+* duser - Manage MajorDomo lists
+* Copyright (C) 2011 Joseph Hunkeler <jhunkeler@gmail.com, jhunk@stsci.edu>
+*
+* This file is part of duser.
+*
+* duser is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* duser is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with duser. If not, see <http://www.gnu.org/licenses/>.
+**/
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dirent.h>
+#include <limits.h>
+#include <ctype.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <time.h>
+#include <fcntl.h>
+#include <libgen.h>
+#ifdef _NLINUX_
+# define HAVE_STRCHRNUL
+# define HAVE_STRCASESTR
+#endif
+#include "duser.h"
+
+extern char list_path[PATH_MAX];
+extern char logfile[PATH_MAX];
+static char progname[FILENAME_MAX];
+extern int CMD_FLAG_NOOPT;
+extern int CMD_FLAG_DEL;
+extern int CMD_FLAG_DEL_ALL;
+extern int CMD_FLAG_DEL_LIST;
+extern int CMD_FLAG_MOD;
+extern int CMD_FLAG_ADD;
+extern int CMD_FLAG_LIST;
+extern int CMD_FLAG_HELP;
+extern int CMD_FLAG_LOOK;
+extern int CMD_FLAG_NEW;
+extern int CMD_FLAG_NULL;
+
+
+int user_del_list(const char* filename)
+{
+ char tmp[PATH_MAX];
+ snprintf(tmp, PATH_MAX, "%s%s", list_path, filename);
+
+ if((access(tmp, F_OK|W_OK)) != 0)
+ {
+ fprintf(stderr, "FATAL: %s: %s: %s\n", SELF, tmp, strerror(errno));
+ return -1;
+ }
+
+ printf("\n!!!WARNING!!!\n");
+ printf("You are about to delete the mailing list '%s'\n\n", basename(tmp));
+ printf("Are SURE you want to continue? [y/N]");
+ int choice = getchar();
+ if((user_choice(choice)) != 0)
+ {
+ printf("Aborting...\n");
+ exit(1);
+ }
+ else
+ {
+ COM(SELF, "Command: DELETE LIST\n");
+ if((unlink(tmp)) != 0)
+ {
+ fprintf(stderr, "FATAL: %s: %s: %s\n", SELF, tmp, strerror(errno));
+ return errno;
+ }
+ else
+ {
+ COM(SELF, "List '%s' deleted\n", basename(tmp));
+ printf("Deleted list: '%s'\n", basename(tmp));
+ }
+ }
+
+ return 0;
+}
+
+int user_del_all(const char* needle)
+{
+ processed.files = get_file_count(list_path);
+ char** list;
+ char tmp[PATH_MAX];
+ record_t *rp;
+ list = get_file_list(list_path, processed.files);
+
+ int i = 0;
+ for(i = 0 ; list[i] != NULL ; i++)
+ {
+ rp = NULL;
+ snprintf(tmp, PATH_MAX, "%s%s", list_path, list[i]);
+ if((rp = find_in_file(tmp, needle)) != NULL)
+ {
+ printf("%20s\t%5d%23s\n", basename(rp->file), rp->index, rp->name);
+ processed.matches++;
+ }
+ }
+
+ printf("\n%d matches\t%d files\t%d lines parsed\n",
+ processed.matches, processed.files, processed.lines);
+
+ if(processed.matches < 1)
+ {
+ free_file_list(list);
+ return -1;
+ }
+
+ printf("\n!!!WARNING!!!\n");
+ printf("You are about to delete '%s' from every list\n", needle);
+ printf("that it appears in!\n\n");
+ printf("Are SURE you want to continue? [y/N]");
+
+ int choice = getchar();
+ if((user_choice(choice)) != 0)
+ {
+ printf("Aborting...\n");
+ exit(1);
+ }
+ else
+ {
+ COM(SELF, "Commmand: DELETE\n");
+ for(i = 0 ; list[i] != NULL ; i++)
+ {
+ rp = NULL;
+ snprintf(tmp, PATH_MAX, "%s%s", list_path, list[i]);
+ if((rp = find_in_file(tmp, needle)) != NULL)
+ {
+ if((user_del(rp)) > 0)
+ {
+ COM(SELF, "'%s' deleted from '%s' at line %d\n", rp->name, basename(rp->file), rp->index);
+ printf("'%s' deleted from '%s'\n", rp->name, basename(rp->file));
+ }
+ }
+ }
+ }
+ free_file_list(list);
+ return 0;
+
+}
+
+int user_del(record_t* rec)
+{
+ FILE *tfp;
+ FILE *fp;
+ int i = 0;
+ int fd = 0;
+ int bytes = 0;
+ int bytes_total = 0;
+ char buf[REGEX_MAX];
+ char tmpfile[255];
+ snprintf(tmpfile, sizeof(tmpfile), "/tmp/duser.%s.XXXXXX", basename(rec->file));
+ if((fd = mkstemp(tmpfile)) < 0 || (tfp = fdopen(fd, "r+")) == NULL)
+ {
+ if(fd != -1)
+ {
+ close(fd);
+ unlink(tmpfile);
+ }
+ fprintf(stderr, "FATAL: %s: %s: %s\n", SELF, tmpfile, strerror(errno));
+ exit(1);
+ }
+
+ if((fp = fopen(rec->file, "r")) == NULL)
+ {
+ fprintf(stderr, "FATAL: %s: %s: %s\n", SELF, rec->file, strerror(errno));
+ exit(1);
+ }
+
+ while(!feof(fp))
+ {
+ memset(buf, 0, sizeof(buf));
+ fgets(buf, REGEX_MAX, fp);
+ buf[strlen(buf) - 1] = '\0';
+ if((strncmp(buf, rec->name, strlen(rec->name))) != 0 && (i != rec->index))
+ {
+ buf[strlen(buf)] = '\n';
+ bytes = write(fd, buf, strlen(buf));
+ }
+ i++;
+ }
+ //Rewind the temp file
+ lseek(fd, 0L, SEEK_SET);
+ //Close the original file
+ fclose(fp);
+ //Truncate original and copy data from tmp to original
+ if((fp = fopen(rec->file, "w+")) == NULL)
+ {
+ fprintf(stderr, "FATAL: %s: %s: %s\n", SELF, rec->file, strerror(errno));
+ exit(1);
+ }
+
+ i = 0;
+ while(!feof(tfp))
+ {
+ memset(buf, 0, sizeof(buf));
+ fgets(buf, REGEX_MAX, tfp);
+ buf[strlen(buf) - 1] = '\0';
+ if((strncmp(buf, rec->name, strlen(rec->name))) != 0 && (i != rec->index))
+ {
+ buf[strlen(buf)] = '\n';
+ if(buf[0] == '\n')
+ buf[0] = '\0';
+
+ bytes = fwrite(buf, strlen(buf), 1, fp);
+ bytes_total += bytes;
+ }
+ }
+
+ fclose(fp);
+ close(fd);
+ unlink(tmpfile);
+
+ if(bytes_total)
+ return bytes_total;
+
+ return 0;
+}
+
+
+// VERIFY that the record is proper (useful for deletion of users)
+int find_in_file_ex(record_t* rec)
+{
+ int match = -1;
+ char buf[REGEX_MAX];
+ FILE *fp;
+
+ if((fp = fopen(rec->file, "r")) == NULL)
+ {
+ fprintf(stderr, "FATAL: %s: %s: %s\n", SELF, rec->file, strerror(errno));
+ return -1;
+ }
+
+ while(!feof(fp))
+ {
+ fgets(buf, REGEX_MAX, fp);
+ buf[strlen(buf) - 1] = '\0';
+ if((strncmp(buf, rec->name, strlen(rec->name))) == 0)
+ {
+ match = 1;
+ break;
+ }
+ }
+
+ fclose(fp);
+ return match;
+}
+
+record_t* find_in_file(const char* filename, const char* needle)
+{
+ //regmatch_t pmatch[10];
+ //regex_t preg;
+ record_t record, *rptr;
+ rptr = &record;
+ rptr->index = 0;
+ rptr->match = 0;
+
+ int index = 0;
+
+ FILE *fp;
+ char *fname = strdup(filename);
+
+ if((fp = fopen(fname, "r")) == NULL)
+ {
+ fprintf(stderr, "FATAL: %s: %s: %s\n", SELF, basename(fname), strerror(errno));
+ exit(1);
+ }
+ strncpy(rptr->file, fname, PATH_MAX);
+ while(!feof(fp))
+ {
+ char cmp[REGEX_MAX];
+ memset(cmp, 0L, REGEX_MAX);
+ fgets(cmp, REGEX_MAX, fp);
+ cmp[strlen(cmp)-1] = '\0';
+ if((strfind(cmp, needle)) == 0)
+ {
+ snprintf(rptr->name, REGEX_MAX, "%s", cmp);
+ rptr->match = 1;
+ rptr->index = index;
+ }
+ index++;
+ processed.lines++;
+ }
+
+ free(fname);
+ fclose(fp);
+ if(rptr->match)
+ return rptr;
+ else
+ return NULL;
+
+}
+
+int get_file_count(const char* path)
+{
+ DIR *dp = NULL;
+ struct dirent *ep = NULL;
+ int file_count = 0;
+
+ if((dp = opendir(path)) == NULL)
+ {
+ fprintf(stderr, "FATAL: %s: %s: %s\n", SELF, path, strerror(errno));
+ exit(1);
+ }
+
+ while((ep = readdir(dp)))
+ {
+#ifdef _NLINUX_
+ char path[PATH_MAX];
+ struct stat st;
+ snprintf(path, PATH_MAX, "%s%s", list_path, ep->d_name);
+ if((stat(path, &st)) == 0)
+ {
+ if(S_ISREG(st.st_mode))
+ {
+ if(!strstr(ep->d_name, "."))
+ {
+ file_count++;
+ }
+ }
+ }
+#else
+ if(ep->d_type == DT_REG && !strstr(ep->d_name, "."))
+ {
+ file_count++;
+ }
+#endif
+ }
+ closedir(dp);
+
+ return file_count;
+}
+
+void free_file_list(char** list)
+{
+ int i = 0;
+ for( i = 0 ; list[i] != NULL ; i++ )
+ {
+ free(list[i]);
+ }
+ free(list);
+}
+
+char** get_file_list(const char* path, int count)
+{
+ DIR *dp = NULL;
+ struct dirent *ep = NULL;
+ int i = 0;
+
+ if((dp = opendir(path)) == NULL)
+ {
+ perror("opendir");
+ exit(1);
+ }
+
+ char **list = (char**)calloc(count, REGEX_MAX);
+ if(list == NULL)
+ {
+ perror("calloc");
+ exit(1);
+ }
+ while((ep = readdir(dp)))
+ {
+#ifdef _NLINUX_
+ char path[PATH_MAX];
+ struct stat st;
+ snprintf(path, PATH_MAX, "%s%s", list_path, ep->d_name);
+ if((stat(path, &st)) == 0)
+ {
+ if(S_ISREG(st.st_mode))
+ {
+ if(!strstr(ep->d_name, "."))
+ {
+ list[i] = (char*)malloc(sizeof(char) * strlen(ep->d_name)+1);
+ memset(list[i], 0L, strlen(ep->d_name)+1);
+ strncpy(list[i], ep->d_name, strlen(ep->d_name)+1);
+ i++;
+ }
+ }
+ }
+#else
+ if(ep->d_type == DT_REG && !strstr(ep->d_name, "."))
+ {
+ list[i] = (char*)malloc(sizeof(char) * strlen(ep->d_name)+1);
+ memset(list[i], 0L, strlen(ep->d_name)+1);
+ strncpy(list[i], ep->d_name, strlen(ep->d_name)+1);
+ i++;
+ }
+#endif
+ }
+ closedir(dp);
+ return list;
+}
+
+
+int user_list(const char* needle)
+{
+ processed.files = get_file_count(list_path);
+ char** list = get_file_list(list_path, processed.files);
+ if(list == NULL)
+ {
+ fprintf(stderr, "abort\n");
+ exit(1);
+ }
+
+ printf("%20s%12s%16s\n", "List", "At line", "Match");
+ printf("\t\t%s\n", "=====================================");
+ int i = 0;
+ for(i = 0 ; list[i] != NULL ; i++)
+ {
+ char tmp[PATH_MAX];
+ snprintf(tmp, PATH_MAX, "%s%s", list_path, list[i]);
+ record_t *rp;
+ if((rp = find_in_file(tmp, needle)) != NULL)
+ {
+ printf(FMTLIST, basename(rp->file), rp->index, rp->name);
+ processed.matches++;
+ }
+ }
+ free_file_list(list);
+
+ printf("\n%d matches\t%d files\t%d lines parsed\n",
+ processed.matches, processed.files, processed.lines);
+ return 0;
+}
+
+int user_add(const char* filename, const char* needle)
+{
+ int bytes = 0;
+ record_t *rp;
+ char *fname = strdup(filename);
+ char *newline = NULL;
+ char buf[REGEX_MAX];
+ FILE *fp;
+ if((access(filename, W_OK|F_OK)) != 0)
+ {
+ fprintf(stderr, "FATAL: %s: %s: %s\n", SELF, filename, strerror(errno));
+ return -1;
+ }
+
+ if((fp = fopen(filename, "r+")) == NULL)
+ {
+ fprintf(stderr, "FATAL: %s: %s: %s\n", SELF, filename, strerror(errno));
+ return -1;
+ }
+
+ if((rp = find_in_file(filename, needle)) != NULL)
+ {
+ fprintf(stderr, "%s: '%s' already exists in '%s'\n", SELF, needle, basename(fname));
+ return -1;
+ }
+ rewind(fp);
+ //Go to end of file and find out what's where
+ fseek(fp, 0L, SEEK_END);
+ fgets(buf, REGEX_MAX, fp);
+
+ //Check for a newline at the end of the file.
+ //If so, add one. If not, do not add a preceding newline.
+ if((newline = strchrnul(buf, '\n')) != NULL)
+ {
+ snprintf(buf, REGEX_MAX, "%s\n", needle);
+ }
+ else
+ {
+ //probably very rarely used.
+ snprintf(buf, REGEX_MAX, "\n%s\n", needle);
+ }
+
+ bytes = fwrite(buf, strlen(buf), 1, fp);
+ //fputs(buf, fp);
+ fflush(fp);
+ free(fname);
+ fclose(fp);
+ return bytes;
+}
+
+int user_cmd(const int argc, char* argv[])
+{
+ const char* cmd = argv[1];
+
+ if(argc < 2)
+ return CMD_FLAG_NULL;
+
+ if(cmd)
+ {
+ if((strncmp(cmd, "del", strlen(cmd))) == 0)
+ {
+ CMD_FLAG_DEL = 1;
+ }
+ if((strncmp(cmd, "delA", strlen(cmd))) == 0)
+ {
+ CMD_FLAG_DEL_ALL = 1;
+ }
+ if((strncmp(cmd, "delL", strlen(cmd))) == 0)
+ {
+ CMD_FLAG_DEL_LIST = 1;
+ }
+ if((strncmp(cmd, "add", strlen(cmd))) == 0)
+ {
+ CMD_FLAG_ADD = 1;
+ }
+ if((strncmp(cmd, "mod", strlen(cmd))) == 0)
+ {
+ CMD_FLAG_MOD = 1;
+ }
+ if((strncmp(cmd, "new", strlen(cmd))) == 0)
+ {
+ CMD_FLAG_NEW = 1;
+ }
+ if((strncmp(cmd, "list", strlen(cmd))) == 0)
+ {
+ CMD_FLAG_LIST = 1;
+ }
+ if((strncmp(cmd, "look", strlen(cmd))) == 0)
+ {
+ CMD_FLAG_LOOK = 1;
+ }
+ if((strncmp(cmd, "help", strlen(cmd))) == 0)
+ {
+ CMD_FLAG_HELP = 1;
+ }
+ }
+
+ return 0;
+}
+
+
+int user_new_list(const char* fname)
+{
+ char *filename = strdup(fname);
+ char *timestr;
+ char message[BUFSIZ];
+ FILE *fp = NULL;
+ time_t ttm;
+ struct tm *tmptr;
+
+ if((access(filename, F_OK)) == 0)
+ {
+ fprintf(stderr, "%s: %s: File already exists\n", SELF, basename(filename));
+ return -1;
+ }
+
+ if((fp = fopen(filename, "w+")) == NULL)
+ {
+ fprintf(stderr, "FATAL: %s: %s: %s\n", SELF, basename(filename), strerror(errno));
+ return -1;
+ }
+
+ time(&ttm);
+ tmptr = localtime(&ttm);
+ timestr = asctime(tmptr);
+ timestr[strlen(timestr)-1] = '\0';
+ snprintf(message, BUFSIZ, "#\n# Created by %s on %s. UID %d\n#\n", basename(progname), timestr, getuid());
+ fputs(message, fp);
+ fflush(fp);
+ fclose(fp);
+
+ free(filename);
+ return 0;
+}
+
+
diff --git a/src/util.c b/src/util.c
new file mode 100644
index 0000000..4d5f58c
--- /dev/null
+++ b/src/util.c
@@ -0,0 +1,95 @@
+/**
+* duser - Manage MajorDomo lists
+* Copyright (C) 2011 Joseph Hunkeler <jhunkeler@gmail.com, jhunk@stsci.edu>
+*
+* This file is part of duser.
+*
+* duser is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* duser is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with duser. If not, see <http://www.gnu.org/licenses/>.
+**/
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include "duser.h"
+
+void stats_init(stats_t *s)
+{
+ s->lines = 0;
+ s->files = 0;
+ s->matches = 0;
+ s->added = 0;
+ s->deleted = 0;
+ s->modified = 0;
+}
+
+int user_choice(char c)
+{
+ if(c == 'y' || c == 'Y')
+ return 0;
+
+ return 1;
+}
+
+int check_cmd_string(char** args, const char* str2, int count)
+{
+ int i = 0;
+ while(i < count)
+ {
+ if((strncmp(args[i], str2, strlen(args[i]))) == 0)
+ {
+ return 0;
+ }
+ i++;
+ }
+ return -1;
+}
+
+//I'm using this until regex can get its head out of its...
+//toilet bowl.
+int strfind(const char* str1, const char* str2)
+{
+ if((strcasestr(str1, str2)) != 0)
+ {
+ if((strcasecmp(str1, str2)) == 0)
+ return 0;
+ }
+ return -1;
+}
+
+int strval(const char* str)
+{
+ const char* bad = "!#$%^&*()+={}[]|\\<>,";
+ unsigned int i = 0;
+ unsigned int ibad = 0;
+ for(i = 0 ; i < strlen(str) ; i++)
+ {
+ unsigned char c = str[i];
+ if((i == 0) && !(isalpha(c)))
+ {
+ return -1;
+ }
+ for(ibad = 0 ; ibad <= strlen(bad) ; ibad++)
+ {
+ if(str[i] == bad[ibad])
+ {
+ return -1;
+ }
+ }
+ }
+
+ return 0;
+}
+