Commit Diff


commit - 21b2f17707ec9adbf894d788875deb9b12f86563
commit + 07ff3c8466af412d8ba624f1a8deb5adf47cf5f9
blob - 4540054d402832361cb710685d24d3b1d4e63b1e
blob + 1481cb252d09dd97d0115b5285cf048490ac036b
--- Makefile
+++ Makefile
@@ -15,9 +15,29 @@ HDR_STEST	= dmenu/arg.h
 SRC_SLOCK	= slock/slock.c
 HDR_SLOCK	= slock/arg.h slock/config.h slock/util.h
 
+SRC_PD		= pinentry-dmenu/pinentry-dmenu.c		\
+		  pinentry-dmenu/drw.c				\
+		  pinentry-dmenu/util.c				\
+		  pinentry-dmenu/pinentry/pinentry.c		\
+		  pinentry-dmenu/pinentry/util.c		\
+		  pinentry-dmenu/pinentry/password-cache.c	\
+		  pinentry-dmenu/pinentry/argparse.c		\
+		  pinentry-dmenu/pinentry/secmem.c
+HDR_PD		= master.h					\
+		  pinentry-dmenu/config.h			\
+		  pinentry-dmenu/drw.h				\
+		  pinentry-dmenu/util.h				\
+		  pinentry-dmenu/pinentry/argparse.h		\
+		  pinentry-dmenu/pinentry/memory.h		\
+		  pinentry-dmenu/pinentry/util.h		\
+		  pinentry-dmenu/pinentry/password-cache.h	\
+		  pinentry-dmenu/pinentry/secmem-util.h		\
+		  pinentry-dmenu/pinentry/pinentry.h
+
+
 MAN		= dwm/dwm.1 st/st.1 dmenu/dmenu.1 dmenu/stest.1 slock/slock.1
 
-all: bin/dwm bin/st bin/bedstatus bin/dmenu bin/stest bin/xbgcd bin/slock
+all: bin/dwm bin/st bin/bedstatus bin/dmenu bin/stest bin/xbgcd bin/slock bin/pinentry-dmenu
 
 clean:
 	rm -rf bin
@@ -70,4 +90,8 @@ bin/slock: ${SRC_SLOCK} ${HDR_SLOCK}
 	@mkdir -p bin
 	${CC} -o $@ ${SRC_SLOCK} ${CFLAGS} `pkg-config --cflags --libs x11 xext xrandr` -lpthread
 
+bin/pinentry-dmenu: ${SRC_PD} ${HDR_PD}
+	@mkdir -p bin
+	${CC} -o $@ ${SRC_PD} ${CFLAGS} `pkg-config --cflags --libs libassuan libconfig fontconfig freetype2 x11 xft xinerama`
+
 .PHONY: all clean install
blob - ea82302b07e891a2b93dfd9c34f4afabc3110421
blob + e0f5302830df390b346b453d81cd7eb1c95081ef
--- TODO
+++ TODO
@@ -2,10 +2,10 @@
 - surf
 - tabbed
 - nsxiv
-- pinentry-dmenu (https://github.com/ritze/pinentry-dmenu)
 
 # Fix
 - dwm: XKeycodeToKeysum
+- pinentry-dmenu: strcpy() -> strlcpy()
 
 # Refactor
-- split common code into a library
+- split common code into a library (drw.c)
blob - 53dd47cbc0ceeb7f85926a0bfbb5a58bf94452de
blob + 2123b7845881ea3347470bd153b92623e41eeeba
--- master.h
+++ master.h
@@ -1 +1,2 @@
+#define BUGREPORT "Benjamin Stürz <benni@stuerz.xyz>"
 #define TOPBAR_FONT "monospace:size=6"
blob - /dev/null
blob + c7aea1896f776a9dea105ff388d6058a612d4961 (mode 644)
--- /dev/null
+++ pinentry-dmenu/LICENSE
@@ -0,0 +1,280 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                          675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
blob - /dev/null
blob + 6d646723095f530b04c590d64207813d57ad10a4 (mode 644)
--- /dev/null
+++ pinentry-dmenu/config.h
@@ -0,0 +1,21 @@
+/* See LICENSE file for copyright and license details. */
+/* Default settings; can be overriden by command line. */
+
+static int bottom = 0;
+static int embedded = 0;
+static int minpwlen = 32;
+static int mon = -1;
+static int lineheight = 0;
+static int min_lineheight = 8;
+
+static const char *asterisk = "*";
+static const char *fonts[] = {
+	"monospace:size=10"
+};
+static const char *prompt = NULL;
+static const char *colors[SchemeLast][4] = {
+	[SchemePrompt] = { "#bbbbbb", "#222222" },
+	[SchemeNormal] = { "#bbbbbb", "#222222" },
+	[SchemeSelect] = { "#eeeeee", "#005577" },
+	[SchemeDesc]   = { "#bbbbbb", "#222222" }
+};
blob - /dev/null
blob + 4c71211e2dcb777ead8d64cd55031de59e0f9cfb (mode 644)
--- /dev/null
+++ pinentry-dmenu/config.mk
@@ -0,0 +1,33 @@
+# Pinentry settings
+DATE      = $$(date +'%B %Y')
+VERSION   = 0.1
+BUGREPORT = https:\/\/github.com\/ritze\/pinentry-dmenu
+
+# Paths
+PREFIX    = /usr/local
+MANPREFIX = ${PREFIX}/share/man
+
+X11INC = /usr/X11R6/include
+X11LIB = /usr/X11R6/lib
+
+# Xinerama, comment if you don't want it
+XINERAMALIBS  = -lXinerama
+XINERAMAFLAGS = -DXINERAMA
+
+# Freetype
+FREETYPELIBS = -lfontconfig -lXft
+FREETYPEINC = /usr/include/freetype2
+# OpenBSD (uncomment)
+#FREETYPEINC = ${X11INC}/freetype2
+
+# Includes and libs
+INCS = -I${X11INC} -I${FREETYPEINC}
+LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS}
+
+# Flags
+CPPFLAGS = -D_DEFAULT_SOURCE -D_POSIX_C_SOURCE=200809L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} -DPACKAGE_VERSION=\"${VERSION}\" -DPACKAGE_BUGREPORT=\"${BUGREPORT}\"
+CFLAGS   = -std=c99 -pedantic -Wall -Os ${INCS} ${CPPFLAGS}
+LDFLAGS  = -s ${LIBS}
+
+# Compiler and linker
+CC = cc
blob - /dev/null
blob + c1582e746cc57b7a475c1de3fcc53507cdb50b37 (mode 644)
--- /dev/null
+++ pinentry-dmenu/drw.c
@@ -0,0 +1,421 @@
+/* See LICENSE file for copyright and license details. */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <X11/Xlib.h>
+#include <X11/Xft/Xft.h>
+
+#include "drw.h"
+#include "util.h"
+
+#define UTF_INVALID 0xFFFD
+#define UTF_SIZ     4
+
+static const unsigned char utfbyte[UTF_SIZ + 1] = {0x80,    0, 0xC0, 0xE0, 0xF0};
+static const unsigned char utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8};
+static const long utfmin[UTF_SIZ + 1] = {       0,    0,  0x80,  0x800,  0x10000};
+static const long utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF};
+
+static long
+utf8decodebyte(const char c, size_t *i)
+{
+	for (*i = 0; *i < (UTF_SIZ + 1); ++(*i))
+		if (((unsigned char)c & utfmask[*i]) == utfbyte[*i])
+			return (unsigned char)c & ~utfmask[*i];
+	return 0;
+}
+
+static size_t
+utf8validate(long *u, size_t i)
+{
+	if (!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF))
+		*u = UTF_INVALID;
+	for (i = 1; *u > utfmax[i]; ++i)
+		;
+	return i;
+}
+
+static size_t
+utf8decode(const char *c, long *u, size_t clen)
+{
+	size_t i, j, len, type;
+	long udecoded;
+
+	*u = UTF_INVALID;
+	if (!clen)
+		return 0;
+	udecoded = utf8decodebyte(c[0], &len);
+	if (!BETWEEN(len, 1, UTF_SIZ))
+		return 1;
+	for (i = 1, j = 1; i < clen && j < len; ++i, ++j) {
+		udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type);
+		if (type)
+			return j;
+	}
+	if (j < len)
+		return 0;
+	*u = udecoded;
+	utf8validate(u, len);
+
+	return len;
+}
+
+Drw *
+drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h)
+{
+	Drw *drw = ecalloc(1, sizeof(Drw));
+
+	drw->dpy = dpy;
+	drw->screen = screen;
+	drw->root = root;
+	drw->w = w;
+	drw->h = h;
+	drw->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen));
+	drw->gc = XCreateGC(dpy, root, 0, NULL);
+	XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter);
+
+	return drw;
+}
+
+void
+drw_resize(Drw *drw, unsigned int w, unsigned int h)
+{
+	if (!drw)
+		return;
+
+	drw->w = w;
+	drw->h = h;
+	if (drw->drawable)
+		XFreePixmap(drw->dpy, drw->drawable);
+	drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, DefaultDepth(drw->dpy, drw->screen));
+}
+
+void
+drw_free(Drw *drw)
+{
+	XFreePixmap(drw->dpy, drw->drawable);
+	XFreeGC(drw->dpy, drw->gc);
+	free(drw);
+}
+
+/* This function is an implementation detail. Library users should use
+ * drw_fontset_create instead.
+ */
+static Fnt *
+xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern)
+{
+	Fnt *font;
+	XftFont *xfont = NULL;
+	FcPattern *pattern = NULL;
+
+	if (fontname) {
+		/* Using the pattern found at font->xfont->pattern does not yield the
+		 * same substitution results as using the pattern returned by
+		 * FcNameParse; using the latter results in the desired fallback
+		 * behaviour whereas the former just results in missing-character
+		 * rectangles being drawn, at least with some fonts. */
+		if (!(xfont = XftFontOpenName(drw->dpy, drw->screen, fontname))) {
+			fprintf(stderr, "error, cannot load font from name: '%s'\n", fontname);
+			return NULL;
+		}
+		if (!(pattern = FcNameParse((FcChar8 *) fontname))) {
+			fprintf(stderr, "error, cannot parse font name to pattern: '%s'\n", fontname);
+			XftFontClose(drw->dpy, xfont);
+			return NULL;
+		}
+	} else if (fontpattern) {
+		if (!(xfont = XftFontOpenPattern(drw->dpy, fontpattern))) {
+			fprintf(stderr, "error, cannot load font from pattern.\n");
+			return NULL;
+		}
+	} else {
+		die("no font specified.");
+	}
+
+	font = ecalloc(1, sizeof(Fnt));
+	font->xfont = xfont;
+	font->pattern = pattern;
+	font->h = xfont->ascent + xfont->descent;
+	font->dpy = drw->dpy;
+
+	return font;
+}
+
+static void
+xfont_free(Fnt *font)
+{
+	if (!font)
+		return;
+	if (font->pattern)
+		FcPatternDestroy(font->pattern);
+	XftFontClose(font->dpy, font->xfont);
+	free(font);
+}
+
+Fnt*
+drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount)
+{
+	Fnt *cur, *ret = NULL;
+	size_t i;
+
+	if (!drw || !fonts)
+		return NULL;
+
+	for (i = 1; i <= fontcount; i++) {
+		if ((cur = xfont_create(drw, fonts[fontcount - i], NULL))) {
+			cur->next = ret;
+			ret = cur;
+		}
+	}
+	return (drw->fonts = ret);
+}
+
+void
+drw_fontset_free(Fnt *font)
+{
+	if (font) {
+		drw_fontset_free(font->next);
+		xfont_free(font);
+	}
+}
+
+void
+drw_clr_create(Drw *drw, Clr *dest, const char *clrname)
+{
+	if (!drw || !dest || !clrname)
+		return;
+
+	if (!XftColorAllocName(drw->dpy, DefaultVisual(drw->dpy, drw->screen),
+	                       DefaultColormap(drw->dpy, drw->screen),
+	                       clrname, dest))
+		die("error, cannot allocate color '%s'", clrname);
+}
+
+/* Wrapper to create color schemes. The caller has to call free(3) on the
+ * returned color scheme when done using it. */
+Clr *
+drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount)
+{
+	size_t i;
+	Clr *ret;
+
+	/* need at least two colors for a scheme */
+	if (!drw || !clrnames || clrcount < 2 || !(ret = ecalloc(clrcount, sizeof(XftColor))))
+		return NULL;
+
+	for (i = 0; i < clrcount; i++)
+		drw_clr_create(drw, &ret[i], clrnames[i]);
+	return ret;
+}
+
+void
+drw_setfontset(Drw *drw, Fnt *set)
+{
+	if (drw)
+		drw->fonts = set;
+}
+
+void
+drw_setscheme(Drw *drw, Clr *scm)
+{
+	if (drw)
+		drw->scheme = scm;
+}
+
+void
+drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert)
+{
+	if (!drw || !drw->scheme)
+		return;
+	XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme[ColBg].pixel : drw->scheme[ColFg].pixel);
+	if (filled)
+		XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
+	else
+		XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w - 1, h - 1);
+}
+
+int
+drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert)
+{
+	char buf[1024];
+	int ty;
+	unsigned int ew;
+	XftDraw *d = NULL;
+	Fnt *usedfont, *curfont, *nextfont;
+	size_t i, len;
+	int utf8strlen, utf8charlen, render = x || y || w || h;
+	long utf8codepoint = 0;
+	const char *utf8str;
+	FcCharSet *fccharset;
+	FcPattern *fcpattern;
+	FcPattern *match;
+	XftResult result;
+	int charexists = 0;
+
+	if (!drw || (render && !drw->scheme) || !text || !drw->fonts)
+		return 0;
+
+	if (!render) {
+		w = ~w;
+	} else {
+		XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel);
+		XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
+		d = XftDrawCreate(drw->dpy, drw->drawable,
+		                  DefaultVisual(drw->dpy, drw->screen),
+		                  DefaultColormap(drw->dpy, drw->screen));
+		x += lpad;
+		w -= lpad;
+	}
+
+	usedfont = drw->fonts;
+	while (1) {
+		utf8strlen = 0;
+		utf8str = text;
+		nextfont = NULL;
+		while (*text) {
+			utf8charlen = utf8decode(text, &utf8codepoint, UTF_SIZ);
+			for (curfont = drw->fonts; curfont; curfont = curfont->next) {
+				charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint);
+				if (charexists) {
+					if (curfont == usedfont) {
+						utf8strlen += utf8charlen;
+						text += utf8charlen;
+					} else {
+						nextfont = curfont;
+					}
+					break;
+				}
+			}
+
+			if (!charexists || nextfont)
+				break;
+			else
+				charexists = 0;
+		}
+
+		if (utf8strlen) {
+			drw_font_getexts(usedfont, utf8str, utf8strlen, &ew, NULL);
+			/* shorten text if necessary */
+			for (len = MIN(utf8strlen, sizeof(buf) - 1); len && ew > w; len--)
+				drw_font_getexts(usedfont, utf8str, len, &ew, NULL);
+
+			if (len) {
+				memcpy(buf, utf8str, len);
+				buf[len] = '\0';
+				if (len < utf8strlen)
+					for (i = len; i && i > len - 3; buf[--i] = '.')
+						; /* NOP */
+
+				if (render) {
+					ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent;
+					XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg],
+					                  usedfont->xfont, x, ty, (XftChar8 *)buf, len);
+				}
+				x += ew;
+				w -= ew;
+			}
+		}
+
+		if (!*text) {
+			break;
+		} else if (nextfont) {
+			charexists = 0;
+			usedfont = nextfont;
+		} else {
+			/* Regardless of whether or not a fallback font is found, the
+			 * character must be drawn. */
+			charexists = 1;
+
+			fccharset = FcCharSetCreate();
+			FcCharSetAddChar(fccharset, utf8codepoint);
+
+			if (!drw->fonts->pattern) {
+				/* Refer to the comment in xfont_create for more information. */
+				die("the first font in the cache must be loaded from a font string.");
+			}
+
+			fcpattern = FcPatternDuplicate(drw->fonts->pattern);
+			FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset);
+			FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue);
+
+			FcConfigSubstitute(NULL, fcpattern, FcMatchPattern);
+			FcDefaultSubstitute(fcpattern);
+			match = XftFontMatch(drw->dpy, drw->screen, fcpattern, &result);
+
+			FcCharSetDestroy(fccharset);
+			FcPatternDestroy(fcpattern);
+
+			if (match) {
+				usedfont = xfont_create(drw, NULL, match);
+				if (usedfont && XftCharExists(drw->dpy, usedfont->xfont, utf8codepoint)) {
+					for (curfont = drw->fonts; curfont->next; curfont = curfont->next)
+						; /* NOP */
+					curfont->next = usedfont;
+				} else {
+					xfont_free(usedfont);
+					usedfont = drw->fonts;
+				}
+			}
+		}
+	}
+	if (d)
+		XftDrawDestroy(d);
+
+	return x + (render ? w : 0);
+}
+
+void
+drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h)
+{
+	if (!drw)
+		return;
+
+	XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, y);
+	XSync(drw->dpy, False);
+}
+
+unsigned int
+drw_fontset_getwidth(Drw *drw, const char *text)
+{
+	if (!drw || !drw->fonts || !text)
+		return 0;
+	return drw_text(drw, 0, 0, 0, 0, 0, text, 0);
+}
+
+void
+drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h)
+{
+	XGlyphInfo ext;
+
+	if (!font || !text)
+		return;
+
+	XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext);
+	if (w)
+		*w = ext.xOff;
+	if (h)
+		*h = font->h;
+}
+
+Cur *
+drw_cur_create(Drw *drw, int shape)
+{
+	Cur *cur;
+
+	if (!drw || !(cur = ecalloc(1, sizeof(Cur))))
+		return NULL;
+
+	cur->cursor = XCreateFontCursor(drw->dpy, shape);
+
+	return cur;
+}
+
+void
+drw_cur_free(Drw *drw, Cur *cursor)
+{
+	if (!cursor)
+		return;
+
+	XFreeCursor(drw->dpy, cursor->cursor);
+	free(cursor);
+}
blob - /dev/null
blob + 4c67419a98dd6d87e0b15e6f14094f24aa44c143 (mode 644)
--- /dev/null
+++ pinentry-dmenu/drw.h
@@ -0,0 +1,57 @@
+/* See LICENSE file for copyright and license details. */
+
+typedef struct {
+	Cursor cursor;
+} Cur;
+
+typedef struct Fnt {
+	Display *dpy;
+	unsigned int h;
+	XftFont *xfont;
+	FcPattern *pattern;
+	struct Fnt *next;
+} Fnt;
+
+enum { ColFg, ColBg }; /* Clr scheme index */
+typedef XftColor Clr;
+
+typedef struct {
+	unsigned int w, h;
+	Display *dpy;
+	int screen;
+	Window root;
+	Drawable drawable;
+	GC gc;
+	Clr *scheme;
+	Fnt *fonts;
+} Drw;
+
+/* Drawable abstraction */
+Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h);
+void drw_resize(Drw *drw, unsigned int w, unsigned int h);
+void drw_free(Drw *drw);
+
+/* Fnt abstraction */
+Fnt *drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount);
+void drw_fontset_free(Fnt* set);
+unsigned int drw_fontset_getwidth(Drw *drw, const char *text);
+void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h);
+
+/* Colorscheme abstraction */
+void drw_clr_create(Drw *drw, Clr *dest, const char *clrname);
+Clr *drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount);
+
+/* Cursor abstraction */
+Cur *drw_cur_create(Drw *drw, int shape);
+void drw_cur_free(Drw *drw, Cur *cursor);
+
+/* Drawing context manipulation */
+void drw_setfontset(Drw *drw, Fnt *set);
+void drw_setscheme(Drw *drw, Clr *scm);
+
+/* Drawing functions */
+void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert);
+int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert);
+
+/* Map functions */
+void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h);
blob - /dev/null
blob + 695d0bae2fc394836118e15ec86ca097bdbe9ceb (mode 644)
--- /dev/null
+++ pinentry-dmenu/pinentry/AUTHORS
@@ -0,0 +1,11 @@
+Program: Pinentry
+Bug reports: <gpa-dev@gnupg.org>
+Security related bug reports: <security@gnupg.org>
+License: GPLv2+
+
+Robert Bihlmeyer <robbe@orcus.priv.at>
+Werner Koch, g10 Code GmbH <wk@gnupg.org>
+Steffen Hansen, Klarälvdalens Datakonsult AB <steffen@klaralvdalens-datakonsult.se>
+Marcus Brinkmann, g10 Code GmbH <marcus@g10code.com>
+Timo Schulz, g10 Code GmbH
+Neal Walfied, g10 Code GmbH <neal@gnu.org>
blob - /dev/null
blob + c7aea1896f776a9dea105ff388d6058a612d4961 (mode 644)
--- /dev/null
+++ pinentry-dmenu/pinentry/COPYING
@@ -0,0 +1,280 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                          675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
blob - /dev/null
blob + 4de24703f540b239f128d04550337e58d769936b (mode 644)
--- /dev/null
+++ pinentry-dmenu/pinentry/Makefile
@@ -0,0 +1,21 @@
+include ../config.mk
+
+SRC = util.c pinentry.c argparse.c password-cache.c secmem.c
+OBJ = ${SRC:.c=.o}
+CFLAGS += -DHAVE_MLOCK
+
+all: pinentry
+
+.c.o:
+	@echo CC $<
+	@${CC} -c ${CFLAGS} $<
+
+${OBJ}: pinentry.h argparse.h password-cache.h memory.h util.h
+
+pinentry: pinentry.o argparse.o password-cache.o secmem.o util.o
+
+clean:
+	@echo cleaning
+	@rm -f ${OBJ}
+
+.PHONY: all clean pinentry
blob - /dev/null
blob + 953326f1c2ccac35da9f00c0b2f15212ee1f781f (mode 644)
--- /dev/null
+++ pinentry-dmenu/pinentry/argparse.c
@@ -0,0 +1,1607 @@
+/* [argparse.c wk 17.06.97] Argument Parser for option handling
+ * Copyright (C) 1998-2001, 2006-2008, 2012 Free Software Foundation, Inc.
+ * Copyright (C) 1997-2001, 2006-2008, 2013-2015 Werner Koch
+ *
+ * This file is part of JNLIB, which is a subsystem of GnuPG.
+ *
+ * JNLIB is free software; you can redistribute it and/or modify it
+ * under the terms of either
+ *
+ *   - the GNU Lesser General Public License as published by the Free
+ *     Software Foundation; either version 3 of the License, or (at
+ *     your option) any later version.
+ *
+ * or
+ *
+ *   - the GNU General Public License as published by the Free
+ *     Software Foundation; either version 2 of the License, or (at
+ *     your option) any later version.
+ *
+ * or both in parallel, as here.
+ *
+ * JNLIB 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 copies of the GNU General Public License
+ * and the GNU Lesser General Public License along with this program;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* This file may be used as part of GnuPG or standalone.  A GnuPG
+   build is detected by the presence of the macro GNUPG_MAJOR_VERSION.
+   Some feature are only availalbe in the GnuPG build mode.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <errno.h>
+
+#ifdef GNUPG_MAJOR_VERSION
+# include "libjnlib-config.h"
+# include "mischelp.h"
+# include "stringhelp.h"
+# include "logging.h"
+# ifdef JNLIB_NEED_UTF8CONV
+#  include "utf8conv.h"
+# endif
+#endif /*GNUPG_MAJOR_VERSION*/
+
+#include "argparse.h"
+
+/* GnuPG uses GPLv3+ but a standalone version of this defaults to
+   GPLv2+ because that is the license of this file.  Change this if
+   you include it in a program which uses GPLv3.  If you don't want to
+   set a a copyright string for your usage() you may also hardcode it
+   here.  */
+#ifndef GNUPG_MAJOR_VERSION
+
+# define ARGPARSE_GPL_VERSION      2
+# define ARGPARSE_CRIGHT_STR "Copyright (C) YEAR NAME"
+
+#else /* Used by GnuPG  */
+
+# define ARGPARSE_GPL_VERSION      3
+# define ARGPARSE_CRIGHT_STR "Copyright (C) 2015 Free Software Foundation, Inc."
+
+#endif /*GNUPG_MAJOR_VERSION*/
+
+/* Replacements for standalone builds.  */
+#ifndef GNUPG_MAJOR_VERSION
+# ifndef _
+#  define _(a)  (a)
+# endif
+# ifndef DIM
+#  define DIM(v)           (sizeof(v)/sizeof((v)[0]))
+# endif
+# define jnlib_malloc(a)    malloc ((a))
+# define jnlib_realloc(a,b) realloc ((a), (b))
+# define jnlib_strdup(a)    strdup ((a))
+# define jnlib_free(a)      free ((a))
+# define jnlib_log_error    my_log_error
+# define jnlib_log_bug	    my_log_bug
+# define trim_spaces(a)     my_trim_spaces ((a))
+# define map_static_macro_string(a)  (a)
+#endif /*!GNUPG_MAJOR_VERSION*/
+
+
+#define ARGPARSE_STR(v) #v
+#define ARGPARSE_STR2(v) ARGPARSE_STR(v)
+
+
+/* Replacements for standalone builds.  */
+#ifndef GNUPG_MAJOR_VERSION
+static void
+my_log_error (const char *fmt, ...)
+{
+  va_list arg_ptr ;
+
+  va_start (arg_ptr, fmt);
+  fprintf (stderr, "%s: ", strusage (11));
+  vfprintf (stderr, fmt, arg_ptr);
+  va_end (arg_ptr);
+}
+
+static void
+my_log_bug (const char *fmt, ...)
+{
+  va_list arg_ptr ;
+
+  va_start (arg_ptr, fmt);
+  fprintf (stderr, "%s: Ohhhh jeeee: ", strusage (11));
+  vfprintf (stderr, fmt, arg_ptr);
+  va_end (arg_ptr);
+  abort ();
+}
+
+static char *
+my_trim_spaces (char *str)
+{
+  char *string, *p, *mark;
+
+  string = str;
+  /* Find first non space character. */
+  for (p=string; *p && isspace (*(unsigned char*)p) ; p++)
+    ;
+  /* Move characters. */
+  for ((mark = NULL); (*string = *p); string++, p++)
+    if (isspace (*(unsigned char*)p))
+      {
+        if (!mark)
+          mark = string;
+      }
+    else
+      mark = NULL;
+  if (mark)
+    *mark = '\0' ;  /* Remove trailing spaces. */
+
+  return str ;
+}
+
+#endif /*!GNUPG_MAJOR_VERSION*/
+
+
+
+/*********************************
+ * @Summary arg_parse
+ *  #include "argparse.h"
+ *
+ *  typedef struct {
+ *	char *argc;		  pointer to argc (value subject to change)
+ *	char ***argv;		  pointer to argv (value subject to change)
+ *	unsigned flags; 	  Global flags (DO NOT CHANGE)
+ *	int err;		  print error about last option
+ *				  1 = warning, 2 = abort
+ *	int r_opt;		  return option
+ *	int r_type;		  type of return value (0 = no argument found)
+ *	union {
+ *	    int   ret_int;
+ *	    long  ret_long
+ *	    ulong ret_ulong;
+ *	    char *ret_str;
+ *	} r;			  Return values
+ *	struct {
+ *	    int idx;
+ *	    const char *last;
+ *	    void *aliases;
+ *	} internal;		  DO NOT CHANGE
+ *  } ARGPARSE_ARGS;
+ *
+ *  typedef struct {
+ *	int	    short_opt;
+ *	const char *long_opt;
+ *	unsigned flags;
+ *  } ARGPARSE_OPTS;
+ *
+ *  int arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts );
+ *
+ * @Description
+ *  This is my replacement for getopt(). See the example for a typical usage.
+ *  Global flags are:
+ *     Bit 0 : Do not remove options form argv
+ *     Bit 1 : Do not stop at last option but return other args
+ *	       with r_opt set to -1.
+ *     Bit 2 : Assume options and real args are mixed.
+ *     Bit 3 : Do not use -- to stop option processing.
+ *     Bit 4 : Do not skip the first arg.
+ *     Bit 5 : allow usage of long option with only one dash
+ *     Bit 6 : ignore --version
+ *     all other bits must be set to zero, this value is modified by the
+ *     function, so assume this is write only.
+ *  Local flags (for each option):
+ *     Bit 2-0 : 0 = does not take an argument
+ *		 1 = takes int argument
+ *		 2 = takes string argument
+ *		 3 = takes long argument
+ *		 4 = takes ulong argument
+ *     Bit 3 : argument is optional (r_type will the be set to 0)
+ *     Bit 4 : allow 0x etc. prefixed values.
+ *     Bit 6 : Ignore this option
+ *     Bit 7 : This is a command and not an option
+ *  You stop the option processing by setting opts to NULL, the function will
+ *  then return 0.
+ * @Return Value
+ *   Returns the args.r_opt or 0 if ready
+ *   r_opt may be -2/-7 to indicate an unknown option/command.
+ * @See Also
+ *   ArgExpand
+ * @Notes
+ *  You do not need to process the options 'h', '--help' or '--version'
+ *  because this function includes standard help processing; but if you
+ *  specify '-h', '--help' or '--version' you have to do it yourself.
+ *  The option '--' stops argument processing; if bit 1 is set the function
+ *  continues to return normal arguments.
+ *  To process float args or unsigned args you must use a string args and do
+ *  the conversion yourself.
+ * @Example
+ *
+ *     ARGPARSE_OPTS opts[] = {
+ *     { 'v', "verbose",   0 },
+ *     { 'd', "debug",     0 },
+ *     { 'o', "output",    2 },
+ *     { 'c', "cross-ref", 2|8 },
+ *     { 'm', "my-option", 1|8 },
+ *     { 300, "ignored-long-option, ARGPARSE_OP_IGNORE},
+ *     { 500, "have-no-short-option-for-this-long-option", 0 },
+ *     {0} };
+ *     ARGPARSE_ARGS pargs = { &argc, &argv, 0 }
+ *
+ *     while( ArgParse( &pargs, &opts) ) {
+ *	   switch( pargs.r_opt ) {
+ *	     case 'v': opt.verbose++; break;
+ *	     case 'd': opt.debug++; break;
+ *	     case 'o': opt.outfile = pargs.r.ret_str; break;
+ *	     case 'c': opt.crf = pargs.r_type? pargs.r.ret_str:"a.crf"; break;
+ *	     case 'm': opt.myopt = pargs.r_type? pargs.r.ret_int : 1; break;
+ *	     case 500: opt.a_long_one++;  break
+ *	     default : pargs.err = 1; break; -- force warning output --
+ *	   }
+ *     }
+ *     if( argc > 1 )
+ *	   log_fatal( "Too many args");
+ *
+ */
+
+typedef struct alias_def_s *ALIAS_DEF;
+struct alias_def_s {
+    ALIAS_DEF next;
+    char *name;   /* malloced buffer with name, \0, value */
+    const char *value; /* ptr into name */
+};
+
+
+/* Object to store the names for the --ignore-invalid-option option.
+   This is a simple linked list.  */
+typedef struct iio_item_def_s *IIO_ITEM_DEF;
+struct iio_item_def_s
+{
+  IIO_ITEM_DEF next;
+  char name[1];      /* String with the long option name.  */
+};
+
+static const char *(*strusage_handler)( int ) = NULL;
+static int (*custom_outfnc) (int, const char *);
+
+static int  set_opt_arg(ARGPARSE_ARGS *arg, unsigned flags, char *s);
+static void show_help(ARGPARSE_OPTS *opts, unsigned flags);
+static void show_version(void);
+static int writestrings (int is_error, const char *string, ...)
+#if __GNUC__ >= 4
+  __attribute__ ((sentinel(0)))
+#endif
+  ;
+
+
+void
+argparse_register_outfnc (int (*fnc)(int, const char *))
+{
+  custom_outfnc = fnc;
+}
+
+
+/* Write STRING and all following const char * arguments either to
+   stdout or, if IS_ERROR is set, to stderr.  The list of strings must
+   be terminated by a NULL.  */
+static int
+writestrings (int is_error, const char *string, ...)
+{
+  va_list arg_ptr;
+  const char *s;
+  int count = 0;
+
+  if (string)
+    {
+      s = string;
+      va_start (arg_ptr, string);
+      do
+        {
+          if (custom_outfnc)
+            custom_outfnc (is_error? 2:1, s);
+          else
+            fputs (s, is_error? stderr : stdout);
+          count += strlen (s);
+        }
+      while ((s = va_arg (arg_ptr, const char *)));
+      va_end (arg_ptr);
+    }
+  return count;
+}
+
+
+static void
+flushstrings (int is_error)
+{
+  if (custom_outfnc)
+    custom_outfnc (is_error? 2:1, NULL);
+  else
+    fflush (is_error? stderr : stdout);
+}
+
+
+static void
+initialize( ARGPARSE_ARGS *arg, const char *filename, unsigned *lineno )
+{
+  if( !(arg->flags & (1<<15)) )
+    {
+      /* Initialize this instance. */
+      arg->internal.idx = 0;
+      arg->internal.last = NULL;
+      arg->internal.inarg = 0;
+      arg->internal.stopped = 0;
+      arg->internal.aliases = NULL;
+      arg->internal.cur_alias = NULL;
+      arg->internal.iio_list = NULL;
+      arg->err = 0;
+      arg->flags |= 1<<15; /* Mark as initialized.  */
+      if ( *arg->argc < 0 )
+        jnlib_log_bug ("invalid argument for arg_parse\n");
+    }
+
+
+  if (arg->err)
+    {
+      /* Last option was erroneous.  */
+      const char *s;
+
+      if (filename)
+        {
+          if ( arg->r_opt == ARGPARSE_UNEXPECTED_ARG )
+            s = _("argument not expected");
+          else if ( arg->r_opt == ARGPARSE_READ_ERROR )
+            s = _("read error");
+          else if ( arg->r_opt == ARGPARSE_KEYWORD_TOO_LONG )
+            s = _("keyword too long");
+          else if ( arg->r_opt == ARGPARSE_MISSING_ARG )
+            s = _("missing argument");
+          else if ( arg->r_opt == ARGPARSE_INVALID_ARG )
+            s = _("invalid argument");
+          else if ( arg->r_opt == ARGPARSE_INVALID_COMMAND )
+            s = _("invalid command");
+          else if ( arg->r_opt == ARGPARSE_INVALID_ALIAS )
+            s = _("invalid alias definition");
+          else if ( arg->r_opt == ARGPARSE_OUT_OF_CORE )
+            s = _("out of core");
+          else
+            s = _("invalid option");
+          jnlib_log_error ("%s:%u: %s\n", filename, *lineno, s);
+	}
+      else
+        {
+          s = arg->internal.last? arg->internal.last:"[??]";
+
+          if ( arg->r_opt == ARGPARSE_MISSING_ARG )
+            jnlib_log_error (_("missing argument for option \"%.50s\"\n"), s);
+          else if ( arg->r_opt == ARGPARSE_INVALID_ARG )
+            jnlib_log_error (_("invalid argument for option \"%.50s\"\n"), s);
+          else if ( arg->r_opt == ARGPARSE_UNEXPECTED_ARG )
+            jnlib_log_error (_("option \"%.50s\" does not expect an "
+                               "argument\n"), s );
+          else if ( arg->r_opt == ARGPARSE_INVALID_COMMAND )
+            jnlib_log_error (_("invalid command \"%.50s\"\n"), s);
+          else if ( arg->r_opt == ARGPARSE_AMBIGUOUS_OPTION )
+            jnlib_log_error (_("option \"%.50s\" is ambiguous\n"), s);
+          else if ( arg->r_opt == ARGPARSE_AMBIGUOUS_COMMAND )
+            jnlib_log_error (_("command \"%.50s\" is ambiguous\n"),s );
+          else if ( arg->r_opt == ARGPARSE_OUT_OF_CORE )
+            jnlib_log_error ("%s\n", _("out of core\n"));
+          else
+            jnlib_log_error (_("invalid option \"%.50s\"\n"), s);
+	}
+      if (arg->err != ARGPARSE_PRINT_WARNING)
+        exit (2);
+      arg->err = 0;
+    }
+
+  /* Zero out the return value union.  */
+  arg->r.ret_str = NULL;
+  arg->r.ret_long = 0;
+}
+
+
+static void
+store_alias( ARGPARSE_ARGS *arg, char *name, char *value )
+{
+    /* TODO: replace this dummy function with a rea one
+     * and fix the probelms IRIX has with (ALIAS_DEV)arg..
+     * used as lvalue
+     */
+  (void)arg;
+  (void)name;
+  (void)value;
+#if 0
+    ALIAS_DEF a = jnlib_xmalloc( sizeof *a );
+    a->name = name;
+    a->value = value;
+    a->next = (ALIAS_DEF)arg->internal.aliases;
+    (ALIAS_DEF)arg->internal.aliases = a;
+#endif
+}
+
+
+/* Return true if KEYWORD is in the ignore-invalid-option list.  */
+static int
+ignore_invalid_option_p (ARGPARSE_ARGS *arg, const char *keyword)
+{
+  IIO_ITEM_DEF item = arg->internal.iio_list;
+
+  for (; item; item = item->next)
+    if (!strcmp (item->name, keyword))
+      return 1;
+  return 0;
+}
+
+
+/* Add the keywords up to the next LF to the list of to be ignored
+   options.  After returning FP will either be at EOF or the next
+   character read wll be the first of a new line.  The function
+   returns 0 on success or true on malloc failure.  */
+static int
+ignore_invalid_option_add (ARGPARSE_ARGS *arg, FILE *fp)
+{
+  IIO_ITEM_DEF item;
+  int c;
+  char name[100];
+  int namelen = 0;
+  int ready = 0;
+  enum { skipWS, collectNAME, skipNAME, addNAME} state = skipWS;
+
+  while (!ready)
+    {
+      c = getc (fp);
+      if (c == '\n')
+        ready = 1;
+      else if (c == EOF)
+        {
+          c = '\n';
+          ready = 1;
+        }
+    again:
+      switch (state)
+        {
+        case skipWS:
+          if (!isascii (c) || !isspace(c))
+            {
+              namelen = 0;
+              state = collectNAME;
+              goto again;
+            }
+          break;
+
+        case collectNAME:
+          if (isspace (c))
+            {
+              state = addNAME;
+              goto again;
+            }
+          else if (namelen < DIM(name)-1)
+            name[namelen++] = c;
+          else /* Too long.  */
+            state = skipNAME;
+          break;
+
+        case skipNAME:
+          if (isspace (c))
+            {
+              state = skipWS;
+              goto again;
+            }
+          break;
+
+        case addNAME:
+          name[namelen] = 0;
+          if (!ignore_invalid_option_p (arg, name))
+            {
+              item = jnlib_malloc (sizeof *item + namelen);
+              if (!item)
+                return 1;
+              strcpy (item->name, name);
+              item->next = (IIO_ITEM_DEF)arg->internal.iio_list;
+              arg->internal.iio_list = item;
+            }
+          state = skipWS;
+          goto again;
+        }
+    }
+  return 0;
+}
+
+
+/* Clear the entire ignore-invalid-option list.  */
+static void
+ignore_invalid_option_clear (ARGPARSE_ARGS *arg)
+{
+  IIO_ITEM_DEF item, tmpitem;
+
+  for (item = arg->internal.iio_list; item; item = tmpitem)
+    {
+      tmpitem = item->next;
+      jnlib_free (item);
+    }
+  arg->internal.iio_list = NULL;
+}
+
+
+
+/****************
+ * Get options from a file.
+ * Lines starting with '#' are comment lines.
+ * Syntax is simply a keyword and the argument.
+ * Valid keywords are all keywords from the long_opt list without
+ * the leading dashes. The special keywords "help", "warranty" and "version"
+ * are not valid here.
+ * The special keyword "alias" may be used to store alias definitions,
+ * which are later expanded like long options.
+ * The option
+ *   ignore-invalid-option OPTIONNAMEs
+ * is recognized and updates a list of option which should be ignored if they
+ * are not defined.
+ * Caller must free returned strings.
+ * If called with FP set to NULL command line args are parse instead.
+ *
+ * Q: Should we allow the syntax
+ *     keyword = value
+ *    and accept for boolean options a value of 1/0, yes/no or true/false?
+ * Note: Abbreviation of options is here not allowed.
+ */
+int
+optfile_parse (FILE *fp, const char *filename, unsigned *lineno,
+	       ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
+{
+  int state, i, c;
+  int idx=0;
+  char keyword[100];
+  char *buffer = NULL;
+  size_t buflen = 0;
+  int in_alias=0;
+
+  if (!fp) /* Divert to to arg_parse() in this case.  */
+    return arg_parse (arg, opts);
+
+  initialize (arg, filename, lineno);
+
+  /* Find the next keyword.  */
+  state = i = 0;
+  for (;;)
+    {
+      c = getc (fp);
+      if (c == '\n' || c== EOF )
+        {
+          if ( c != EOF )
+            ++*lineno;
+          if (state == -1)
+            break;
+          else if (state == 2)
+            {
+              keyword[i] = 0;
+              for (i=0; opts[i].short_opt; i++ )
+                {
+                  if (opts[i].long_opt && !strcmp (opts[i].long_opt, keyword))
+                    break;
+                }
+              idx = i;
+              arg->r_opt = opts[idx].short_opt;
+              if ((opts[idx].flags & ARGPARSE_OPT_IGNORE))
+                {
+                  state = i = 0;
+                  continue;
+                }
+              else if (!opts[idx].short_opt )
+                {
+                  if (!strcmp (keyword, "ignore-invalid-option"))
+                    {
+                      /* No argument - ignore this meta option.  */
+                      state = i = 0;
+                      continue;
+                    }
+                  else if (ignore_invalid_option_p (arg, keyword))
+                    {
+                      /* This invalid option is in the iio list.  */
+                      state = i = 0;
+                      continue;
+                    }
+                  arg->r_opt = ((opts[idx].flags & ARGPARSE_OPT_COMMAND)
+                                ? ARGPARSE_INVALID_COMMAND
+                                : ARGPARSE_INVALID_OPTION);
+                }
+              else if (!(opts[idx].flags & ARGPARSE_TYPE_MASK))
+                arg->r_type = 0; /* Does not take an arg. */
+              else if ((opts[idx].flags & ARGPARSE_OPT_OPTIONAL) )
+                arg->r_type = 0; /* Arg is optional.  */
+              else
+                arg->r_opt = ARGPARSE_MISSING_ARG;
+
+              break;
+	    }
+          else if (state == 3)
+            {
+              /* No argument found.  */
+              if (in_alias)
+                arg->r_opt = ARGPARSE_MISSING_ARG;
+              else if (!(opts[idx].flags & ARGPARSE_TYPE_MASK))
+                arg->r_type = 0; /* Does not take an arg. */
+              else if ((opts[idx].flags & ARGPARSE_OPT_OPTIONAL))
+                arg->r_type = 0; /* No optional argument. */
+              else
+                arg->r_opt = ARGPARSE_MISSING_ARG;
+
+              break;
+	    }
+          else if (state == 4)
+            {
+              /* Has an argument. */
+              if (in_alias)
+                {
+                  if (!buffer)
+                    arg->r_opt = ARGPARSE_UNEXPECTED_ARG;
+                  else
+                    {
+                      char *p;
+
+                      buffer[i] = 0;
+                      p = strpbrk (buffer, " \t");
+                      if (p)
+                        {
+                          *p++ = 0;
+                          trim_spaces (p);
+			}
+                      if (!p || !*p)
+                        {
+                          jnlib_free (buffer);
+                          arg->r_opt = ARGPARSE_INVALID_ALIAS;
+                        }
+                      else
+                        {
+                          store_alias (arg, buffer, p);
+                        }
+		    }
+		}
+              else if (!(opts[idx].flags & ARGPARSE_TYPE_MASK))
+                arg->r_opt = ARGPARSE_UNEXPECTED_ARG;
+              else
+                {
+                  char *p;
+
+                  if (!buffer)
+                    {
+                      keyword[i] = 0;
+                      buffer = jnlib_strdup (keyword);
+                      if (!buffer)
+                        arg->r_opt = ARGPARSE_OUT_OF_CORE;
+		    }
+                  else
+                    buffer[i] = 0;
+
+                  if (buffer)
+                    {
+                      trim_spaces (buffer);
+                      p = buffer;
+                      if (*p == '"')
+                        {
+                          /* Remove quotes. */
+                          p++;
+                          if (*p && p[strlen(p)-1] == '\"' )
+                            p[strlen(p)-1] = 0;
+                        }
+                      if (!set_opt_arg (arg, opts[idx].flags, p))
+                        jnlib_free(buffer);
+                    }
+                }
+              break;
+            }
+          else if (c == EOF)
+            {
+              ignore_invalid_option_clear (arg);
+              if (ferror (fp))
+                arg->r_opt = ARGPARSE_READ_ERROR;
+              else
+                arg->r_opt = 0; /* EOF. */
+              break;
+            }
+          state = 0;
+          i = 0;
+        }
+      else if (state == -1)
+        ; /* Skip. */
+      else if (state == 0 && isascii (c) && isspace(c))
+        ; /* Skip leading white space.  */
+      else if (state == 0 && c == '#' )
+        state = 1;	/* Start of a comment.  */
+      else if (state == 1)
+        ; /* Skip comments. */
+      else if (state == 2 && isascii (c) && isspace(c))
+        {
+          /* Check keyword.  */
+          keyword[i] = 0;
+          for (i=0; opts[i].short_opt; i++ )
+            if (opts[i].long_opt && !strcmp (opts[i].long_opt, keyword))
+              break;
+          idx = i;
+          arg->r_opt = opts[idx].short_opt;
+          if ((opts[idx].flags & ARGPARSE_OPT_IGNORE))
+            {
+              state = 1; /* Process like a comment.  */
+            }
+          else if (!opts[idx].short_opt)
+            {
+              if (!strcmp (keyword, "alias"))
+                {
+                  in_alias = 1;
+                  state = 3;
+                }
+              else if (!strcmp (keyword, "ignore-invalid-option"))
+                {
+                  if (ignore_invalid_option_add (arg, fp))
+                    {
+                      arg->r_opt = ARGPARSE_OUT_OF_CORE;
+                      break;
+                    }
+                  state = i = 0;
+                  ++*lineno;
+                }
+              else if (ignore_invalid_option_p (arg, keyword))
+                state = 1; /* Process like a comment.  */
+              else
+                {
+                  arg->r_opt = ((opts[idx].flags & ARGPARSE_OPT_COMMAND)
+                                ? ARGPARSE_INVALID_COMMAND
+                                : ARGPARSE_INVALID_OPTION);
+                  state = -1; /* Skip rest of line and leave.  */
+                }
+            }
+          else
+            state = 3;
+        }
+      else if (state == 3)
+        {
+          /* Skip leading spaces of the argument.  */
+          if (!isascii (c) || !isspace(c))
+            {
+              i = 0;
+              keyword[i++] = c;
+              state = 4;
+            }
+        }
+      else if (state == 4)
+        {
+          /* Collect the argument. */
+          if (buffer)
+            {
+              if (i < buflen-1)
+                buffer[i++] = c;
+              else
+                {
+                  char *tmp;
+                  size_t tmplen = buflen + 50;
+
+                  tmp = jnlib_realloc (buffer, tmplen);
+                  if (tmp)
+                    {
+                      buflen = tmplen;
+                      buffer = tmp;
+                      buffer[i++] = c;
+                    }
+                  else
+                    {
+                      jnlib_free (buffer);
+                      arg->r_opt = ARGPARSE_OUT_OF_CORE;
+                      break;
+                    }
+                }
+            }
+          else if (i < DIM(keyword)-1)
+            keyword[i++] = c;
+          else
+            {
+              size_t tmplen = DIM(keyword) + 50;
+              buffer = jnlib_malloc (tmplen);
+              if (buffer)
+                {
+                  buflen = tmplen;
+                  memcpy(buffer, keyword, i);
+                  buffer[i++] = c;
+                }
+              else
+                {
+                  arg->r_opt = ARGPARSE_OUT_OF_CORE;
+                  break;
+                }
+            }
+        }
+      else if (i >= DIM(keyword)-1)
+        {
+          arg->r_opt = ARGPARSE_KEYWORD_TOO_LONG;
+          state = -1; /* Skip rest of line and leave.  */
+        }
+      else
+        {
+          keyword[i++] = c;
+          state = 2;
+        }
+    }
+
+  return arg->r_opt;
+}
+
+
+
+static int
+find_long_option( ARGPARSE_ARGS *arg,
+		  ARGPARSE_OPTS *opts, const char *keyword )
+{
+    int i;
+    size_t n;
+
+    (void)arg;
+
+    /* Would be better if we can do a binary search, but it is not
+       possible to reorder our option table because we would mess
+       up our help strings - What we can do is: Build a nice option
+       lookup table wehn this function is first invoked */
+    if( !*keyword )
+	return -1;
+    for(i=0; opts[i].short_opt; i++ )
+	if( opts[i].long_opt && !strcmp( opts[i].long_opt, keyword) )
+	    return i;
+#if 0
+    {
+	ALIAS_DEF a;
+	/* see whether it is an alias */
+	for( a = args->internal.aliases; a; a = a->next ) {
+	    if( !strcmp( a->name, keyword) ) {
+		/* todo: must parse the alias here */
+		args->internal.cur_alias = a;
+		return -3; /* alias available */
+	    }
+	}
+    }
+#endif
+    /* not found, see whether it is an abbreviation */
+    /* aliases may not be abbreviated */
+    n = strlen( keyword );
+    for(i=0; opts[i].short_opt; i++ ) {
+	if( opts[i].long_opt && !strncmp( opts[i].long_opt, keyword, n ) ) {
+	    int j;
+	    for(j=i+1; opts[j].short_opt; j++ ) {
+		if( opts[j].long_opt
+		    && !strncmp( opts[j].long_opt, keyword, n ) )
+		    return -2;	/* abbreviation is ambiguous */
+	    }
+	    return i;
+	}
+    }
+    return -1;  /* Not found.  */
+}
+
+int
+arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
+{
+  int idx;
+  int argc;
+  char **argv;
+  char *s, *s2;
+  int i;
+
+  initialize( arg, NULL, NULL );
+  argc = *arg->argc;
+  argv = *arg->argv;
+  idx = arg->internal.idx;
+
+  if (!idx && argc && !(arg->flags & ARGPARSE_FLAG_ARG0))
+    {
+      /* Skip the first argument.  */
+      argc--; argv++; idx++;
+    }
+
+ next_one:
+  if (!argc)
+    {
+      /* No more args.  */
+      arg->r_opt = 0;
+      goto leave; /* Ready. */
+    }
+
+  s = *argv;
+  arg->internal.last = s;
+
+  if (arg->internal.stopped && (arg->flags & ARGPARSE_FLAG_ALL))
+    {
+      arg->r_opt = ARGPARSE_IS_ARG;  /* Not an option but an argument.  */
+      arg->r_type = 2;
+      arg->r.ret_str = s;
+      argc--; argv++; idx++; /* set to next one */
+    }
+  else if( arg->internal.stopped )
+    {
+      arg->r_opt = 0;
+      goto leave; /* Ready.  */
+    }
+  else if ( *s == '-' && s[1] == '-' )
+    {
+      /* Long option.  */
+      char *argpos;
+
+      arg->internal.inarg = 0;
+      if (!s[2] && !(arg->flags & ARGPARSE_FLAG_NOSTOP))
+        {
+          /* Stop option processing.  */
+          arg->internal.stopped = 1;
+          arg->flags |= ARGPARSE_FLAG_STOP_SEEN;
+          argc--; argv++; idx++;
+          goto next_one;
+	}
+
+      argpos = strchr( s+2, '=' );
+      if ( argpos )
+        *argpos = 0;
+      i = find_long_option ( arg, opts, s+2 );
+      if ( argpos )
+        *argpos = '=';
+
+      if ( i < 0 && !strcmp ( "help", s+2) )
+        show_help (opts, arg->flags);
+      else if ( i < 0 && !strcmp ( "version", s+2) )
+        {
+          if (!(arg->flags & ARGPARSE_FLAG_NOVERSION))
+            {
+              show_version ();
+              exit(0);
+            }
+	}
+      else if ( i < 0 && !strcmp( "warranty", s+2))
+        {
+          writestrings (0, strusage (16), "\n", NULL);
+          exit (0);
+	}
+      else if ( i < 0 && !strcmp( "dump-options", s+2) )
+        {
+          for (i=0; opts[i].short_opt; i++ )
+            {
+              if (opts[i].long_opt && !(opts[i].flags & ARGPARSE_OPT_IGNORE))
+                writestrings (0, "--", opts[i].long_opt, "\n", NULL);
+	    }
+          writestrings (0, "--dump-options\n--help\n--version\n--warranty\n",
+                        NULL);
+          exit (0);
+	}
+
+      if ( i == -2 )
+        arg->r_opt = ARGPARSE_AMBIGUOUS_OPTION;
+      else if ( i == -1 )
+        {
+          arg->r_opt = ARGPARSE_INVALID_OPTION;
+          arg->r.ret_str = s+2;
+	}
+      else
+        arg->r_opt = opts[i].short_opt;
+      if ( i < 0 )
+        ;
+      else if ( (opts[i].flags & ARGPARSE_TYPE_MASK) )
+        {
+          if ( argpos )
+            {
+              s2 = argpos+1;
+              if ( !*s2 )
+                s2 = NULL;
+	    }
+          else
+            s2 = argv[1];
+          if ( !s2 && (opts[i].flags & ARGPARSE_OPT_OPTIONAL) )
+            {
+              arg->r_type = ARGPARSE_TYPE_NONE; /* Argument is optional.  */
+	    }
+          else if ( !s2 )
+            {
+              arg->r_opt = ARGPARSE_MISSING_ARG;
+	    }
+          else if ( !argpos && *s2 == '-'
+                    && (opts[i].flags & ARGPARSE_OPT_OPTIONAL) )
+            {
+              /* The argument is optional and the next seems to be an
+                 option.  We do not check this possible option but
+                 assume no argument */
+              arg->r_type = ARGPARSE_TYPE_NONE;
+	    }
+          else
+            {
+              set_opt_arg (arg, opts[i].flags, s2);
+              if ( !argpos )
+                {
+                  argc--; argv++; idx++; /* Skip one.  */
+		}
+	    }
+	}
+      else
+        {
+          /* Does not take an argument. */
+          if ( argpos )
+            arg->r_type = ARGPARSE_UNEXPECTED_ARG;
+          else
+            arg->r_type = 0;
+	}
+      argc--; argv++; idx++; /* Set to next one.  */
+    }
+    else if ( (*s == '-' && s[1]) || arg->internal.inarg )
+      {
+        /* Short option.  */
+	int dash_kludge = 0;
+
+	i = 0;
+	if ( !arg->internal.inarg )
+          {
+	    arg->internal.inarg++;
+	    if ( (arg->flags & ARGPARSE_FLAG_ONEDASH) )
+              {
+                for (i=0; opts[i].short_opt; i++ )
+                  if ( opts[i].long_opt && !strcmp (opts[i].long_opt, s+1))
+                    {
+                      dash_kludge = 1;
+                      break;
+		    }
+              }
+          }
+	s += arg->internal.inarg;
+
+	if (!dash_kludge )
+          {
+	    for (i=0; opts[i].short_opt; i++ )
+              if ( opts[i].short_opt == *s )
+                break;
+          }
+
+	if ( !opts[i].short_opt && ( *s == 'h' || *s == '?' ) )
+          show_help (opts, arg->flags);
+
+	arg->r_opt = opts[i].short_opt;
+	if (!opts[i].short_opt )
+          {
+	    arg->r_opt = (opts[i].flags & ARGPARSE_OPT_COMMAND)?
+              ARGPARSE_INVALID_COMMAND:ARGPARSE_INVALID_OPTION;
+	    arg->internal.inarg++; /* Point to the next arg.  */
+	    arg->r.ret_str = s;
+          }
+	else if ( (opts[i].flags & ARGPARSE_TYPE_MASK) )
+          {
+	    if ( s[1] && !dash_kludge )
+              {
+		s2 = s+1;
+		set_opt_arg (arg, opts[i].flags, s2);
+              }
+	    else
+              {
+		s2 = argv[1];
+		if ( !s2 && (opts[i].flags & ARGPARSE_OPT_OPTIONAL) )
+                  {
+		    arg->r_type = ARGPARSE_TYPE_NONE;
+                  }
+		else if ( !s2 )
+                  {
+		    arg->r_opt = ARGPARSE_MISSING_ARG;
+                  }
+		else if ( *s2 == '-' && s2[1]
+                          && (opts[i].flags & ARGPARSE_OPT_OPTIONAL) )
+                  {
+		    /* The argument is optional and the next seems to
+	               be an option.  We do not check this possible
+	               option but assume no argument.  */
+		    arg->r_type = ARGPARSE_TYPE_NONE;
+                  }
+		else
+                  {
+		    set_opt_arg (arg, opts[i].flags, s2);
+		    argc--; argv++; idx++; /* Skip one.  */
+                  }
+              }
+	    s = "x"; /* This is so that !s[1] yields false.  */
+          }
+	else
+          {
+            /* Does not take an argument.  */
+	    arg->r_type = ARGPARSE_TYPE_NONE;
+	    arg->internal.inarg++; /* Point to the next arg.  */
+          }
+	if ( !s[1] || dash_kludge )
+          {
+            /* No more concatenated short options.  */
+	    arg->internal.inarg = 0;
+	    argc--; argv++; idx++;
+          }
+      }
+  else if ( arg->flags & ARGPARSE_FLAG_MIXED )
+    {
+      arg->r_opt = ARGPARSE_IS_ARG;
+      arg->r_type = 2;
+      arg->r.ret_str = s;
+      argc--; argv++; idx++; /* Set to next one.  */
+    }
+  else
+    {
+      arg->internal.stopped = 1; /* Stop option processing.  */
+      goto next_one;
+    }
+
+ leave:
+  *arg->argc = argc;
+  *arg->argv = argv;
+  arg->internal.idx = idx;
+  return arg->r_opt;
+}
+
+
+/* Returns: -1 on error, 0 for an integer type and 1 for a non integer
+   type argument.  */
+static int
+set_opt_arg (ARGPARSE_ARGS *arg, unsigned flags, char *s)
+{
+  int base = (flags & ARGPARSE_OPT_PREFIX)? 0 : 10;
+  long l;
+
+  switch ( (arg->r_type = (flags & ARGPARSE_TYPE_MASK)) )
+    {
+    case ARGPARSE_TYPE_LONG:
+    case ARGPARSE_TYPE_INT:
+      errno = 0;
+      l = strtol (s, NULL, base);
+      if ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE)
+        {
+          arg->r_opt = ARGPARSE_INVALID_ARG;
+          return -1;
+        }
+      if (arg->r_type == ARGPARSE_TYPE_LONG)
+        arg->r.ret_long = l;
+      else if ( (l < 0 && l < INT_MIN) || l > INT_MAX )
+        {
+          arg->r_opt = ARGPARSE_INVALID_ARG;
+          return -1;
+        }
+      else
+        arg->r.ret_int = (int)l;
+      return 0;
+
+    case ARGPARSE_TYPE_ULONG:
+      while (isascii (*s) && isspace(*s))
+        s++;
+      if (*s == '-')
+        {
+          arg->r.ret_ulong = 0;
+          arg->r_opt = ARGPARSE_INVALID_ARG;
+          return -1;
+        }
+      errno = 0;
+      arg->r.ret_ulong = strtoul (s, NULL, base);
+      if (arg->r.ret_ulong == ULONG_MAX && errno == ERANGE)
+        {
+          arg->r_opt = ARGPARSE_INVALID_ARG;
+          return -1;
+        }
+      return 0;
+
+    case ARGPARSE_TYPE_STRING:
+    default:
+      arg->r.ret_str = s;
+      return 1;
+    }
+}
+
+
+static size_t
+long_opt_strlen( ARGPARSE_OPTS *o )
+{
+  size_t n = strlen (o->long_opt);
+
+  if ( o->description && *o->description == '|' )
+    {
+      const char *s;
+#ifdef JNLIB_NEED_UTF8CONV
+      int is_utf8 = is_native_utf8 ();
+#endif
+
+      s=o->description+1;
+      if ( *s != '=' )
+        n++;
+      /* For a (mostly) correct length calculation we exclude
+         continuation bytes (10xxxxxx) if we are on a native utf8
+         terminal. */
+      for (; *s && *s != '|'; s++ )
+#ifdef JNLIB_NEED_UTF8CONV
+        if ( is_utf8 && (*s&0xc0) != 0x80 )
+#endif
+          n++;
+    }
+  return n;
+}
+
+
+/****************
+ * Print formatted help. The description string has some special
+ * meanings:
+ *  - A description string which is "@" suppresses help output for
+ *    this option
+ *  - a description,ine which starts with a '@' and is followed by
+ *    any other characters is printed as is; this may be used for examples
+ *    ans such.
+ *  - A description which starts with a '|' outputs the string between this
+ *    bar and the next one as arguments of the long option.
+ */
+static void
+show_help (ARGPARSE_OPTS *opts, unsigned int flags)
+{
+  const char *s;
+  char tmp[2];
+
+  show_version ();
+  writestrings (0, "\n", NULL);
+  s = strusage (42);
+  if (s && *s == '1')
+    {
+      s = strusage (40);
+      writestrings (1, s, NULL);
+      if (*s && s[strlen(s)] != '\n')
+        writestrings (1, "\n", NULL);
+    }
+  s = strusage(41);
+  writestrings (0, s, "\n", NULL);
+  if ( opts[0].description )
+    {
+      /* Auto format the option description.  */
+      int i,j, indent;
+
+      /* Get max. length of long options.  */
+      for (i=indent=0; opts[i].short_opt; i++ )
+        {
+          if ( opts[i].long_opt )
+            if ( !opts[i].description || *opts[i].description != '@' )
+              if ( (j=long_opt_strlen(opts+i)) > indent && j < 35 )
+                indent = j;
+	}
+
+      /* Example: " -v, --verbose   Viele Sachen ausgeben" */
+      indent += 10;
+      if ( *opts[0].description != '@' )
+        writestrings (0, "Options:", "\n", NULL);
+      for (i=0; opts[i].short_opt; i++ )
+        {
+          s = map_static_macro_string (_( opts[i].description ));
+          if ( s && *s== '@' && !s[1] ) /* Hide this line.  */
+            continue;
+          if ( s && *s == '@' )  /* Unindented comment only line.  */
+            {
+              for (s++; *s; s++ )
+                {
+                  if ( *s == '\n' )
+                    {
+                      if( s[1] )
+                        writestrings (0, "\n", NULL);
+		    }
+                  else
+                    {
+                      tmp[0] = *s;
+                      tmp[1] = 0;
+                      writestrings (0, tmp, NULL);
+                    }
+                }
+              writestrings (0, "\n", NULL);
+              continue;
+	    }
+
+          j = 3;
+          if ( opts[i].short_opt < 256 )
+            {
+              tmp[0] = opts[i].short_opt;
+              tmp[1] = 0;
+              writestrings (0, " -", tmp, NULL );
+              if ( !opts[i].long_opt )
+                {
+                  if (s && *s == '|' )
+                    {
+                      writestrings (0, " ", NULL); j++;
+                      for (s++ ; *s && *s != '|'; s++, j++ )
+                        {
+                          tmp[0] = *s;
+                          tmp[1] = 0;
+                          writestrings (0, tmp, NULL);
+                        }
+                      if ( *s )
+                        s++;
+		    }
+		}
+	    }
+          else
+            writestrings (0, "   ", NULL);
+          if ( opts[i].long_opt )
+            {
+              tmp[0] = opts[i].short_opt < 256?',':' ';
+              tmp[1] = 0;
+              j += writestrings (0, tmp, " --", opts[i].long_opt, NULL);
+              if (s && *s == '|' )
+                {
+                  if ( *++s != '=' )
+                    {
+                      writestrings (0, " ", NULL);
+                      j++;
+		    }
+                  for ( ; *s && *s != '|'; s++, j++ )
+                    {
+                      tmp[0] = *s;
+                      tmp[1] = 0;
+                      writestrings (0, tmp, NULL);
+                    }
+                  if ( *s )
+                    s++;
+		}
+              writestrings (0, "   ", NULL);
+              j += 3;
+	    }
+          for (;j < indent; j++ )
+            writestrings (0, " ", NULL);
+          if ( s )
+            {
+              if ( *s && j > indent )
+                {
+                  writestrings (0, "\n", NULL);
+                  for (j=0;j < indent; j++ )
+                    writestrings (0, " ", NULL);
+		}
+              for (; *s; s++ )
+                {
+                  if ( *s == '\n' )
+                    {
+                      if ( s[1] )
+                        {
+                          writestrings (0, "\n", NULL);
+                          for (j=0; j < indent; j++ )
+                            writestrings (0, " ", NULL);
+			}
+		    }
+                  else
+                    {
+                      tmp[0] = *s;
+                      tmp[1] = 0;
+                      writestrings (0, tmp, NULL);
+                    }
+		}
+	    }
+          writestrings (0, "\n", NULL);
+	}
+	if ( (flags & ARGPARSE_FLAG_ONEDASH) )
+          writestrings (0, "\n(A single dash may be used "
+                        "instead of the double ones)\n", NULL);
+    }
+  if ( (s=strusage(19)) )
+    {
+      writestrings (0, "\n", NULL);
+      writestrings (0, s, NULL);
+    }
+  flushstrings (0);
+  exit(0);
+}
+
+static void
+show_version (void)
+{
+  const char *s;
+  int i;
+
+  /* Version line.  */
+  writestrings (0, strusage (11), NULL);
+  if ((s=strusage (12)))
+    writestrings (0, " (", s, ")", NULL);
+  writestrings (0, " ", strusage (13), "\n", NULL);
+  /* Additional version lines. */
+  for (i=20; i < 30; i++)
+    if ((s=strusage (i)))
+      writestrings (0, s, "\n", NULL);
+  /* Copyright string.  */
+  if ((s=strusage (14)))
+    writestrings (0, s, "\n", NULL);
+  /* Licence string.  */
+  if( (s=strusage (10)) )
+    writestrings (0, s, "\n", NULL);
+  /* Copying conditions. */
+  if ( (s=strusage(15)) )
+    writestrings (0, s, NULL);
+  /* Thanks. */
+  if ((s=strusage(18)))
+    writestrings (0, s, NULL);
+  /* Additional program info. */
+  for (i=30; i < 40; i++ )
+    if ( (s=strusage (i)) )
+      writestrings (0, s, NULL);
+  flushstrings (0);
+}
+
+
+void
+usage (int level)
+{
+  const char *p;
+
+  if (!level)
+    {
+      writestrings (1, strusage(11), " ", strusage(13), "; ",
+                    strusage (14), "\n", NULL);
+      flushstrings (1);
+    }
+  else if (level == 1)
+    {
+      p = strusage (40);
+      writestrings (1, p, NULL);
+      if (*p && p[strlen(p)] != '\n')
+        writestrings (1, "\n", NULL);
+      exit (2);
+    }
+  else if (level == 2)
+    {
+      p = strusage (42);
+      if (p && *p == '1')
+        {
+          p = strusage (40);
+          writestrings (1, p, NULL);
+          if (*p && p[strlen(p)] != '\n')
+            writestrings (1, "\n", NULL);
+        }
+      writestrings (0, strusage(41), "\n", NULL);
+      exit (0);
+    }
+}
+
+/* Level
+ *     0: Print copyright string to stderr
+ *     1: Print a short usage hint to stderr and terminate
+ *     2: Print a long usage hint to stdout and terminate
+ *    10: Return license info string
+ *    11: Return the name of the program
+ *    12: Return optional name of package which includes this program.
+ *    13: version  string
+ *    14: copyright string
+ *    15: Short copying conditions (with LFs)
+ *    16: Long copying conditions (with LFs)
+ *    17: Optional printable OS name
+ *    18: Optional thanks list (with LFs)
+ *    19: Bug report info
+ *20..29: Additional lib version strings.
+ *30..39: Additional program info (with LFs)
+ *    40: short usage note (with LF)
+ *    41: long usage note (with LF)
+ *    42: Flag string:
+ *          First char is '1':
+ *             The short usage notes needs to be printed
+ *             before the long usage note.
+ */
+const char *
+strusage( int level )
+{
+  const char *p = strusage_handler? strusage_handler(level) : NULL;
+
+  if ( p )
+    return map_static_macro_string (p);
+
+  switch ( level )
+    {
+
+    case 10:
+#if ARGPARSE_GPL_VERSION == 3
+      p = ("License GPLv3+: GNU GPL version 3 or later "
+           "<http://gnu.org/licenses/gpl.html>");
+#else
+      p = ("License GPLv2+: GNU GPL version 2 or later "
+           "<http://gnu.org/licenses/>");
+#endif
+      break;
+    case 11: p = "foo"; break;
+    case 13: p = "0.0"; break;
+    case 14: p = ARGPARSE_CRIGHT_STR; break;
+    case 15: p =
+"This is free software: you are free to change and redistribute it.\n"
+"There is NO WARRANTY, to the extent permitted by law.\n";
+      break;
+    case 16: p =
+"This is free software; you can redistribute it and/or modify\n"
+"it under the terms of the GNU General Public License as published by\n"
+"the Free Software Foundation; either version "
+ARGPARSE_STR2(ARGPARSE_GPL_VERSION)
+" of the License, or\n"
+"(at your option) any later version.\n\n"
+"It is distributed in the hope that it will be useful,\n"
+"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
+"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
+"GNU General Public License for more details.\n\n"
+"You should have received a copy of the GNU General Public License\n"
+"along with this software.  If not, see <http://www.gnu.org/licenses/>.\n";
+      break;
+    case 40: /* short and long usage */
+    case 41: p = ""; break;
+    }
+
+  return p;
+}
+
+
+/* Set the usage handler.  This function is basically a constructor.  */
+void
+set_strusage ( const char *(*f)( int ) )
+{
+  strusage_handler = f;
+}
+
+
+#ifdef TEST
+static struct {
+    int verbose;
+    int debug;
+    char *outfile;
+    char *crf;
+    int myopt;
+    int echo;
+    int a_long_one;
+} opt;
+
+int
+main(int argc, char **argv)
+{
+  ARGPARSE_OPTS opts[] = {
+    ARGPARSE_x('v', "verbose", NONE, 0, "Laut sein"),
+    ARGPARSE_s_n('e', "echo"   , ("Zeile ausgeben, damit wir sehen, "
+                                  "was wir eingegeben haben")),
+    ARGPARSE_s_n('d', "debug", "Debug\nfalls mal etwas\nschief geht"),
+    ARGPARSE_s_s('o', "output", 0 ),
+    ARGPARSE_o_s('c', "cross-ref", "cross-reference erzeugen\n" ),
+    /* Note that on a non-utf8 terminal the ß might garble the output. */
+    ARGPARSE_s_n('s', "street","|Straße|set the name of the street to Straße"),
+    ARGPARSE_o_i('m', "my-option", 0),
+    ARGPARSE_s_n(500, "a-long-option", 0 ),
+    ARGPARSE_end()
+  };
+  ARGPARSE_ARGS pargs = { &argc, &argv, (ARGPARSE_FLAG_ALL
+                                         | ARGPARSE_FLAG_MIXED
+                                         | ARGPARSE_FLAG_ONEDASH) };
+  int i;
+
+  while (arg_parse  (&pargs, opts))
+    {
+      switch (pargs.r_opt)
+        {
+        case ARGPARSE_IS_ARG :
+          printf ("arg='%s'\n", pargs.r.ret_str);
+          break;
+        case 'v': opt.verbose++; break;
+        case 'e': opt.echo++; break;
+        case 'd': opt.debug++; break;
+        case 'o': opt.outfile = pargs.r.ret_str; break;
+        case 'c': opt.crf = pargs.r_type? pargs.r.ret_str:"a.crf"; break;
+        case 'm': opt.myopt = pargs.r_type? pargs.r.ret_int : 1; break;
+        case 500: opt.a_long_one++;  break;
+        default : pargs.err = ARGPARSE_PRINT_WARNING; break;
+	}
+    }
+  for (i=0; i < argc; i++ )
+    printf ("%3d -> (%s)\n", i, argv[i] );
+  puts ("Options:");
+  if (opt.verbose)
+    printf ("  verbose=%d\n", opt.verbose );
+  if (opt.debug)
+    printf ("  debug=%d\n", opt.debug );
+  if (opt.outfile)
+    printf ("  outfile='%s'\n", opt.outfile );
+  if (opt.crf)
+    printf ("  crffile='%s'\n", opt.crf );
+  if (opt.myopt)
+    printf ("  myopt=%d\n", opt.myopt );
+  if (opt.a_long_one)
+    printf ("  a-long-one=%d\n", opt.a_long_one );
+  if (opt.echo)
+    printf ("  echo=%d\n", opt.echo );
+
+  return 0;
+}
+#endif /*TEST*/
+
+/**** bottom of file ****/
blob - /dev/null
blob + b4dc253730009daf43fa23e2d04231241d888abc (mode 644)
--- /dev/null
+++ pinentry-dmenu/pinentry/argparse.h
@@ -0,0 +1,203 @@
+/* argparse.h - Argument parser for option handling.
+ *	Copyright (C) 1998,1999,2000,2001,2006 Free Software Foundation, Inc.
+ *
+ * This file is part of JNLIB, which is a subsystem of GnuPG.
+ *
+ * JNLIB is free software; you can redistribute it and/or modify it
+ * under the terms of either
+ *
+ *   - the GNU Lesser General Public License as published by the Free
+ *     Software Foundation; either version 3 of the License, or (at
+ *     your option) any later version.
+ *
+ * or
+ *
+ *   - the GNU General Public License as published by the Free
+ *     Software Foundation; either version 2 of the License, or (at
+ *     your option) any later version.
+ *
+ * or both in parallel, as here.
+ *
+ * JNLIB 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 copies of the GNU General Public License
+ * and the GNU Lesser General Public License along with this program;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LIBJNLIB_ARGPARSE_H
+#define LIBJNLIB_ARGPARSE_H
+
+#include <stdio.h>
+
+typedef struct
+{
+  int  *argc;	      /* Pointer to ARGC (value subject to change). */
+  char ***argv;	      /* Pointer to ARGV (value subject to change). */
+  unsigned int flags; /* Global flags.  May be set prior to calling the
+                         parser.  The parser may change the value.  */
+  int err;            /* Print error description for last option.
+                         Either 0,  ARGPARSE_PRINT_WARNING or
+                         ARGPARSE_PRINT_ERROR.  */
+
+  int r_opt; 	      /* Returns option code. */
+  int r_type;	      /* Returns type of option value.  */
+  union {
+    int   ret_int;
+    long  ret_long;
+    unsigned long ret_ulong;
+    char *ret_str;
+  } r;		      /* Return values */
+
+  struct {
+    int idx;
+    int inarg;
+    int stopped;
+    const char *last;
+    void *aliases;
+    const void *cur_alias;
+    void *iio_list;
+  } internal;	    /* Private - do not change. */
+} ARGPARSE_ARGS;
+
+typedef struct
+{
+  int          short_opt;
+  const char  *long_opt;
+  unsigned int flags;
+  const char  *description; /* Optional option description. */
+} ARGPARSE_OPTS;
+
+
+/* Global flags (ARGPARSE_ARGS).  */
+#define ARGPARSE_FLAG_KEEP       1   /* Do not remove options form argv.     */
+#define ARGPARSE_FLAG_ALL        2   /* Do not stop at last option but return
+                                        remaining args with R_OPT set to -1. */
+#define ARGPARSE_FLAG_MIXED      4   /* Assume options and args are mixed.   */
+#define ARGPARSE_FLAG_NOSTOP     8   /* Do not stop processing at "--".      */
+#define ARGPARSE_FLAG_ARG0      16   /* Do not skip the first arg.           */
+#define ARGPARSE_FLAG_ONEDASH   32   /* Allow long options with one dash.    */
+#define ARGPARSE_FLAG_NOVERSION 64   /* No output for "--version".           */
+
+#define ARGPARSE_FLAG_STOP_SEEN 256  /* Set to true if a "--" has been seen. */
+
+/* Flags for each option (ARGPARSE_OPTS).  The type code may be
+   ORed with the OPT flags.  */
+#define ARGPARSE_TYPE_NONE        0  /* Does not take an argument.        */
+#define ARGPARSE_TYPE_INT         1  /* Takes an int argument.            */
+#define ARGPARSE_TYPE_STRING      2  /* Takes a string argument.          */
+#define ARGPARSE_TYPE_LONG        3  /* Takes a long argument.            */
+#define ARGPARSE_TYPE_ULONG       4  /* Takes an unsigned long argument.  */
+#define ARGPARSE_OPT_OPTIONAL (1<<3) /* Argument is optional.             */
+#define ARGPARSE_OPT_PREFIX   (1<<4) /* Allow 0x etc. prefixed values.    */
+#define ARGPARSE_OPT_IGNORE   (1<<6) /* Ignore command or option.         */
+#define ARGPARSE_OPT_COMMAND  (1<<7) /* The argument is a command.        */
+
+#define ARGPARSE_TYPE_MASK  7  /* Mask for the type values (internal).  */
+
+/* A set of macros to make option definitions easier to read.  */
+#define ARGPARSE_x(s,l,t,f,d) \
+     { (s), (l), ARGPARSE_TYPE_ ## t | (f), (d) }
+
+#define ARGPARSE_s(s,l,t,d) \
+     { (s), (l), ARGPARSE_TYPE_ ## t, (d) }
+#define ARGPARSE_s_n(s,l,d) \
+     { (s), (l), ARGPARSE_TYPE_NONE, (d) }
+#define ARGPARSE_s_i(s,l,d) \
+     { (s), (l), ARGPARSE_TYPE_INT, (d) }
+#define ARGPARSE_s_s(s,l,d) \
+     { (s), (l), ARGPARSE_TYPE_STRING, (d) }
+#define ARGPARSE_s_l(s,l,d) \
+     { (s), (l), ARGPARSE_TYPE_LONG, (d) }
+#define ARGPARSE_s_u(s,l,d) \
+     { (s), (l), ARGPARSE_TYPE_ULONG, (d) }
+
+#define ARGPARSE_o(s,l,t,d) \
+     { (s), (l), (ARGPARSE_TYPE_ ## t  | ARGPARSE_OPT_OPTIONAL), (d) }
+#define ARGPARSE_o_n(s,l,d) \
+     { (s), (l), (ARGPARSE_TYPE_NONE   | ARGPARSE_OPT_OPTIONAL), (d) }
+#define ARGPARSE_o_i(s,l,d) \
+     { (s), (l), (ARGPARSE_TYPE_INT    | ARGPARSE_OPT_OPTIONAL), (d) }
+#define ARGPARSE_o_s(s,l,d) \
+     { (s), (l), (ARGPARSE_TYPE_STRING | ARGPARSE_OPT_OPTIONAL), (d) }
+#define ARGPARSE_o_l(s,l,d) \
+     { (s), (l), (ARGPARSE_TYPE_LONG   | ARGPARSE_OPT_OPTIONAL), (d) }
+#define ARGPARSE_o_u(s,l,d) \
+     { (s), (l), (ARGPARSE_TYPE_ULONG  | ARGPARSE_OPT_OPTIONAL), (d) }
+
+#define ARGPARSE_p(s,l,t,d) \
+     { (s), (l), (ARGPARSE_TYPE_ ## t  | ARGPARSE_OPT_PREFIX), (d) }
+#define ARGPARSE_p_n(s,l,d) \
+     { (s), (l), (ARGPARSE_TYPE_NONE   | ARGPARSE_OPT_PREFIX), (d) }
+#define ARGPARSE_p_i(s,l,d) \
+     { (s), (l), (ARGPARSE_TYPE_INT    | ARGPARSE_OPT_PREFIX), (d) }
+#define ARGPARSE_p_s(s,l,d) \
+     { (s), (l), (ARGPARSE_TYPE_STRING | ARGPARSE_OPT_PREFIX), (d) }
+#define ARGPARSE_p_l(s,l,d) \
+     { (s), (l), (ARGPARSE_TYPE_LONG   | ARGPARSE_OPT_PREFIX), (d) }
+#define ARGPARSE_p_u(s,l,d) \
+     { (s), (l), (ARGPARSE_TYPE_ULONG  | ARGPARSE_OPT_PREFIX), (d) }
+
+#define ARGPARSE_op(s,l,t,d) \
+     { (s), (l), (ARGPARSE_TYPE_ ## t \
+                  | ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) }
+#define ARGPARSE_op_n(s,l,d) \
+     { (s), (l), (ARGPARSE_TYPE_NONE \
+                  | ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) }
+#define ARGPARSE_op_i(s,l,d) \
+     { (s), (l), (ARGPARSE_TYPE_INT \
+                  | ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) }
+#define ARGPARSE_op_s(s,l,d) \
+     { (s), (l), (ARGPARSE_TYPE_STRING \
+                  | ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) }
+#define ARGPARSE_op_l(s,l,d) \
+     { (s), (l), (ARGPARSE_TYPE_LONG \
+                  | ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) }
+#define ARGPARSE_op_u(s,l,d) \
+     { (s), (l), (ARGPARSE_TYPE_ULONG \
+                  | ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) }
+
+#define ARGPARSE_c(s,l,d) \
+     { (s), (l), (ARGPARSE_TYPE_NONE | ARGPARSE_OPT_COMMAND), (d) }
+
+#define ARGPARSE_ignore(s,l) \
+     { (s), (l), (ARGPARSE_OPT_IGNORE), "@" }
+
+#define ARGPARSE_group(s,d) \
+     { (s), NULL, 0, (d) }
+
+#define ARGPARSE_end()  { 0, NULL, 0, NULL }
+
+
+/* Other constants.  */
+#define ARGPARSE_PRINT_WARNING  1
+#define ARGPARSE_PRINT_ERROR    2
+
+
+/* Error values.  */
+#define ARGPARSE_IS_ARG            (-1)
+#define ARGPARSE_INVALID_OPTION    (-2)
+#define ARGPARSE_MISSING_ARG       (-3)
+#define ARGPARSE_KEYWORD_TOO_LONG  (-4)
+#define ARGPARSE_READ_ERROR        (-5)
+#define ARGPARSE_UNEXPECTED_ARG    (-6)
+#define ARGPARSE_INVALID_COMMAND   (-7)
+#define ARGPARSE_AMBIGUOUS_OPTION  (-8)
+#define ARGPARSE_AMBIGUOUS_COMMAND (-9)
+#define ARGPARSE_INVALID_ALIAS     (-10)
+#define ARGPARSE_OUT_OF_CORE       (-11)
+#define ARGPARSE_INVALID_ARG       (-12)
+
+
+int arg_parse (ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts);
+int optfile_parse (FILE *fp, const char *filename, unsigned *lineno,
+		   ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts);
+void usage (int level);
+const char *strusage (int level);
+void set_strusage (const char *(*f)( int ));
+void argparse_register_outfnc (int (*fnc)(int, const char *));
+
+#endif /*LIBJNLIB_ARGPARSE_H*/
blob - /dev/null
blob + 354c6c96cd8585f1295998368c32087ae336bbf4 (mode 644)
--- /dev/null
+++ pinentry-dmenu/pinentry/memory.h
@@ -0,0 +1,55 @@
+/* Quintuple Agent secure memory allocation
+ * Copyright (C) 1998,1999 Free Software Foundation, Inc.
+ * Copyright (C) 1999,2000 Robert Bihlmeyer <robbe@orcus.priv.at>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _MEMORY_H
+#define _MEMORY_H
+
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#if 0 
+}
+#endif
+#endif
+
+
+/* values for flags, hardcoded in secmem.c */
+#define SECMEM_WARN		0
+#define SECMEM_DONT_WARN	1
+#define SECMEM_SUSPEND_WARN	2
+
+void secmem_init( size_t npool );
+void secmem_term( void );
+void *secmem_malloc( size_t size );
+void *secmem_realloc( void *a, size_t newsize );
+void secmem_free( void *a );
+int  m_is_secure( const void *p );
+void secmem_dump_stats(void);
+void secmem_set_flags( unsigned flags );
+unsigned secmem_get_flags(void);
+size_t secmem_get_max_size (void);
+
+#if 0 
+{
+#endif
+#ifdef __cplusplus
+}
+#endif
+#endif /* _MEMORY_H */
blob - /dev/null
blob + 70b33f4a3774c601efd28473fa4e00d9beee342a (mode 644)
--- /dev/null
+++ pinentry-dmenu/pinentry/password-cache.c
@@ -0,0 +1,163 @@
+/* password-cache.c - Password cache support.
+   Copyright (C) 2015 g10 Code GmbH
+
+   This file is part of PINENTRY.
+
+   PINENTRY 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 2 of the License, or
+   (at your option) any later version.
+
+   PINENTRY is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#ifdef HAVE_LIBSECRET
+# include <libsecret/secret.h>
+#endif
+
+#include "password-cache.h"
+#include "memory.h"
+
+#ifdef HAVE_LIBSECRET
+static const SecretSchema *
+gpg_schema (void)
+{
+    static const SecretSchema the_schema = {
+        "org.gnupg.Passphrase", SECRET_SCHEMA_NONE,
+        {
+	  { "stored-by", SECRET_SCHEMA_ATTRIBUTE_STRING },
+	  { "keygrip", SECRET_SCHEMA_ATTRIBUTE_STRING },
+	  { "NULL", 0 },
+	}
+    };
+    return &the_schema;
+}
+
+static char *
+keygrip_to_label (const char *keygrip)
+{
+  char const prefix[] = "GnuPG: ";
+  char *label;
+
+  label = malloc (sizeof (prefix) + strlen (keygrip));
+  if (label)
+    {
+      memcpy (label, prefix, sizeof (prefix) - 1);
+      strcpy (&label[sizeof (prefix) - 1], keygrip);
+    }
+  return label;
+}
+#endif
+
+void
+password_cache_save (const char *keygrip, const char *password)
+{
+#ifdef HAVE_LIBSECRET
+  char *label;
+  GError *error = NULL;
+
+  if (! *keygrip)
+    return;
+
+  label = keygrip_to_label (keygrip);
+  if (! label)
+    return;
+
+  if (! secret_password_store_sync (gpg_schema (),
+				    SECRET_COLLECTION_DEFAULT,
+				    label, password, NULL, &error,
+				    "stored-by", "GnuPG Pinentry",
+				    "keygrip", keygrip, NULL))
+    {
+      printf("Failed to cache password for key %s with secret service: %s\n",
+	     keygrip, error->message);
+
+      g_error_free (error);
+    }
+
+  free (label);
+#else
+  return;
+#endif
+}
+
+char *
+password_cache_lookup (const char *keygrip)
+{
+#ifdef HAVE_LIBSECRET
+  GError *error = NULL;
+  char *password;
+  char *password2;
+
+  if (! *keygrip)
+    return NULL;
+
+  password = secret_password_lookup_nonpageable_sync
+    (gpg_schema (), NULL, &error,
+     "keygrip", keygrip, NULL);
+
+  if (error != NULL)
+    {
+      printf("Failed to lookup password for key %s with secret service: %s\n",
+	     keygrip, error->message);
+      g_error_free (error);
+      return NULL;
+    }
+  if (! password)
+    /* The password for this key is not cached.  Just return NULL.  */
+    return NULL;
+
+  /* The password needs to be returned in secmem allocated memory.  */
+  password2 = secmem_malloc (strlen (password) + 1);
+  if (password2)
+    strcpy(password2, password);
+  else
+    printf("secmem_malloc failed: can't copy password!\n");
+
+  secret_password_free (password);
+
+  return password2;
+#else
+  return NULL;
+#endif
+}
+
+/* Try and remove the cached password for key grip.  Returns -1 on
+   error, 0 if the key is not found and 1 if the password was
+   removed.  */
+int
+password_cache_clear (const char *keygrip)
+{
+#ifdef HAVE_LIBSECRET
+  GError *error = NULL;
+  int removed = secret_password_clear_sync (gpg_schema (), NULL, &error,
+					    "keygrip", keygrip, NULL);
+  if (error != NULL)
+    {
+      printf("Failed to clear password for key %s with secret service: %s\n",
+	     keygrip, error->message);
+      g_debug("%s", error->message);
+      g_error_free (error);
+      return -1;
+    }
+  if (removed)
+    return 1;
+  return 0;
+#else
+  return -1;
+#endif
+}
blob - /dev/null
blob + 0bc8788f15d28879c4bf63532949397274926b9d (mode 644)
--- /dev/null
+++ pinentry-dmenu/pinentry/password-cache.h
@@ -0,0 +1,29 @@
+/* password-cache.h - Password cache support interfaces.
+   Copyright (C) 2015 g10 Code GmbH
+
+   This file is part of PINENTRY.
+
+   PINENTRY 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 2 of the License, or
+   (at your option) any later version.
+
+   PINENTRY is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PASSWORD_CACHE_H
+#define PASSWORD_CACHE_H
+
+void password_cache_save (const char *key_grip, const char *password);
+
+char *password_cache_lookup (const char *key_grip);
+
+int password_cache_clear (const char *keygrip);
+
+#endif
blob - /dev/null
blob + 53636f102ed83dd64c906500322800ae5186f8cb (mode 644)
--- /dev/null
+++ pinentry-dmenu/pinentry/pinentry.c
@@ -0,0 +1,1309 @@
+/* pinentry.c - The PIN entry support library
+   Copyright (C) 2002, 2003, 2007, 2008, 2010, 2015 g10 Code GmbH
+
+   This file is part of PINENTRY.
+
+   PINENTRY 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 2 of the License, or
+   (at your option) any later version.
+
+   PINENTRY is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "../../master.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <assert.h>
+
+#include <assuan.h>
+
+#include "memory.h"
+#include "secmem-util.h"
+#include "argparse.h"
+#include "pinentry.h"
+#include "password-cache.h"
+
+/* Keep the name of our program here. */
+static char this_pgmname[50];
+
+struct pinentry pinentry;
+
+static void
+pinentry_reset (int use_defaults)
+{
+  /* GPG Agent sets these options once when it starts the pinentry.
+     Don't reset them.  */
+  int grab = pinentry.grab;
+  char *ttyname = pinentry.ttyname;
+  char *ttytype = pinentry.ttytype;
+  char *lc_ctype = pinentry.lc_ctype;
+  char *lc_messages = pinentry.lc_messages;
+  int allow_external_password_cache = pinentry.allow_external_password_cache;
+  char *default_ok = pinentry.default_ok;
+  char *default_cancel = pinentry.default_cancel;
+  char *default_prompt = pinentry.default_prompt;
+  char *default_pwmngr = pinentry.default_pwmngr;
+  char *touch_file = pinentry.touch_file;
+
+  /* These options are set from the command line.  Don't reset
+     them.  */
+  int debug = pinentry.debug;
+  char *display = pinentry.display;
+  int parent_wid = pinentry.parent_wid;
+
+  pinentry_color_t color_fg = pinentry.color_fg;
+  int color_fg_bright = pinentry.color_fg_bright;
+  pinentry_color_t color_bg = pinentry.color_bg;
+  pinentry_color_t color_so = pinentry.color_so;
+  int color_so_bright = pinentry.color_so_bright;
+
+  int timout = pinentry.timeout;
+
+  /* Free any allocated memory.  */
+  if (use_defaults)
+    {
+      free (pinentry.ttyname);
+      free (pinentry.ttytype);
+      free (pinentry.lc_ctype);
+      free (pinentry.lc_messages);
+      free (pinentry.default_ok);
+      free (pinentry.default_cancel);
+      free (pinentry.default_prompt);
+      free (pinentry.default_pwmngr);
+      free (pinentry.touch_file);
+      free (pinentry.display);
+    }
+
+  free (pinentry.title);
+  free (pinentry.description);
+  free (pinentry.error);
+  free (pinentry.prompt);
+  free (pinentry.ok);
+  free (pinentry.notok);
+  free (pinentry.cancel);
+  secmem_free (pinentry.pin);
+  free (pinentry.repeat_passphrase);
+  free (pinentry.repeat_error_string);
+  free (pinentry.quality_bar);
+  free (pinentry.quality_bar_tt);
+  free (pinentry.keyinfo);
+
+  /* Reset the pinentry structure.  */
+  memset (&pinentry, 0, sizeof (pinentry));
+
+  if (use_defaults)
+    {
+      /* Pinentry timeout in seconds.  */
+      pinentry.timeout = 60;
+
+      /* Global grab.  */
+      pinentry.grab = 1;
+
+      pinentry.color_fg = PINENTRY_COLOR_DEFAULT;
+      pinentry.color_fg_bright = 0;
+      pinentry.color_bg = PINENTRY_COLOR_DEFAULT;
+      pinentry.color_so = PINENTRY_COLOR_DEFAULT;
+      pinentry.color_so_bright = 0;
+    }
+  else
+    /* Restore the options.  */
+    {
+      pinentry.grab = grab;
+      pinentry.ttyname = ttyname;
+      pinentry.ttytype = ttytype;
+      pinentry.lc_ctype = lc_ctype;
+      pinentry.lc_messages = lc_messages;
+      pinentry.allow_external_password_cache = allow_external_password_cache;
+      pinentry.default_ok = default_ok;
+      pinentry.default_cancel = default_cancel;
+      pinentry.default_prompt = default_prompt;
+      pinentry.default_pwmngr = default_pwmngr;
+      pinentry.touch_file = touch_file;
+
+      pinentry.debug = debug;
+      pinentry.display = display;
+      pinentry.parent_wid = parent_wid;
+
+      pinentry.color_fg = color_fg;
+      pinentry.color_fg_bright = color_fg_bright;
+      pinentry.color_bg = color_bg;
+      pinentry.color_so = color_so;
+      pinentry.color_so_bright = color_so_bright;
+
+      pinentry.timeout = timout;
+    }
+}
+
+static gpg_error_t
+pinentry_assuan_reset_handler (assuan_context_t ctx, char *line)
+{
+  (void)ctx;
+  (void)line;
+
+  pinentry_reset (0);
+
+  return 0;
+}
+
+
+//static int lc_ctype_unknown_warning = 0;
+
+/* Copy TEXT or TEXTLEN to BUFFER and escape as required.  Return a
+   pointer to the end of the new buffer.  Note that BUFFER must be
+   large enough to keep the entire text; allocataing it 3 times of
+   TEXTLEN is sufficient.  */
+static char *
+copy_and_escape (char *buffer, const void *text, size_t textlen)
+{
+  int i;
+  const unsigned char *s = (unsigned char *)text;
+  char *p = buffer;
+
+  for (i=0; i < textlen; i++)
+    {
+      if (s[i] < ' ' || s[i] == '+')
+        {
+          snprintf (p, 4, "%%%02X", s[i]);
+          p += 3;
+        }
+      else if (s[i] == ' ')
+        *p++ = '+';
+      else
+        *p++ = s[i];
+    }
+  return p;
+}
+
+
+
+/* Run a quality inquiry for PASSPHRASE of LENGTH.  (We need LENGTH
+   because not all backends might be able to return a proper
+   C-string.).  Returns: A value between -100 and 100 to give an
+   estimate of the passphrase's quality.  Negative values are use if
+   the caller won't even accept that passphrase.  Note that we expect
+   just one data line which should not be escaped in any represent a
+   numeric signed decimal value.  Extra data is currently ignored but
+   should not be send at all.  */
+int
+pinentry_inq_quality (pinentry_t pin, const char *passphrase, size_t length)
+{
+  assuan_context_t ctx = pin->ctx_assuan;
+  const char prefix[] = "INQUIRE QUALITY ";
+  char *command;
+  char *line;
+  size_t linelen;
+  int gotvalue = 0;
+  int value = 0;
+  int rc;
+
+  if (!ctx)
+    return 0; /* Can't run the callback.  */
+
+  if (length > 300)
+    length = 300;  /* Limit so that it definitely fits into an Assuan
+                      line.  */
+
+  command = secmem_malloc (strlen (prefix) + 3*length + 1);
+  if (!command)
+    return 0;
+  strcpy (command, prefix);
+  copy_and_escape (command + strlen(command), passphrase, length);
+  rc = assuan_write_line (ctx, command);
+  secmem_free (command);
+  if (rc)
+    {
+      fprintf (stderr, "ASSUAN WRITE LINE failed: rc=%d\n", rc);
+      return 0;
+    }
+
+  for (;;)
+    {
+      do
+        {
+          rc = assuan_read_line (ctx, &line, &linelen);
+          if (rc)
+            {
+              fprintf (stderr, "ASSUAN READ LINE failed: rc=%d\n", rc);
+              return 0;
+            }
+        }
+      while (*line == '#' || !linelen);
+      if (line[0] == 'E' && line[1] == 'N' && line[2] == 'D'
+          && (!line[3] || line[3] == ' '))
+        break; /* END command received*/
+      if (line[0] == 'C' && line[1] == 'A' && line[2] == 'N'
+          && (!line[3] || line[3] == ' '))
+        break; /* CAN command received*/
+      if (line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
+          && (!line[3] || line[3] == ' '))
+        break; /* ERR command received*/
+      if (line[0] != 'D' || line[1] != ' ' || linelen < 3 || gotvalue)
+        continue;
+      gotvalue = 1;
+      value = atoi (line+2);
+    }
+  if (value < -100)
+    value = -100;
+  else if (value > 100)
+    value = 100;
+
+  return value;
+}
+
+
+
+/* Try to make room for at least LEN bytes in the pinentry.  Returns
+   new buffer on success and 0 on failure or when the old buffer is
+   sufficient.  */
+char *
+pinentry_setbufferlen (pinentry_t pin, int len)
+{
+  char *newp;
+
+  if (pin->pin_len)
+    assert (pin->pin);
+  else
+    assert (!pin->pin);
+
+  if (len < 2048)
+    len = 2048;
+
+  if (len <= pin->pin_len)
+    return pin->pin;
+
+  newp = secmem_realloc (pin->pin, len);
+  if (newp)
+    {
+      pin->pin = newp;
+      pin->pin_len = len;
+    }
+  else
+    {
+      secmem_free (pin->pin);
+      pin->pin = 0;
+      pin->pin_len = 0;
+    }
+  return newp;
+}
+
+static void
+pinentry_setbuffer_clear (pinentry_t pin)
+{
+  if (! pin->pin)
+    {
+      assert (pin->pin_len == 0);
+      return;
+    }
+
+  assert (pin->pin_len > 0);
+
+  secmem_free (pin->pin);
+  pin->pin = NULL;
+  pin->pin_len = 0;
+}
+
+static void
+pinentry_setbuffer_init (pinentry_t pin)
+{
+  pinentry_setbuffer_clear (pin);
+  pinentry_setbufferlen (pin, 0);
+}
+
+/* passphrase better be alloced with secmem_alloc.  */
+void
+pinentry_setbuffer_use (pinentry_t pin, char *passphrase, int len)
+{
+  if (! passphrase)
+    {
+      assert (len == 0);
+      pinentry_setbuffer_clear (pin);
+
+      return;
+    }
+
+  if (passphrase && len == 0)
+    len = strlen (passphrase) + 1;
+
+  if (pin->pin)
+    secmem_free (pin->pin);
+
+  pin->pin = passphrase;
+  pin->pin_len = len;
+}
+
+static struct assuan_malloc_hooks assuan_malloc_hooks = {
+  secmem_malloc, secmem_realloc, secmem_free
+};
+
+/* Initialize the secure memory subsystem, drop privileges and return.
+   Must be called early. */
+void
+pinentry_init (const char *pgmname)
+{
+  /* Store away our name. */
+  if (strlen (pgmname) > sizeof this_pgmname - 2)
+    abort ();
+  strcpy (this_pgmname, pgmname);
+
+  gpgrt_check_version (NULL);
+
+  /* Initialize secure memory.  1 is too small, so the default size
+     will be used.  */
+  secmem_init (1);
+  secmem_set_flags (SECMEM_WARN);
+  drop_privs ();
+
+  if (atexit (secmem_term))
+    {
+      /* FIXME: Could not register at-exit function, bail out.  */
+    }
+
+  assuan_set_malloc_hooks (&assuan_malloc_hooks);
+}
+
+/* Simple test to check whether DISPLAY is set or the option --display
+   was given.  Used to decide whether the GUI or curses should be
+   initialized.  */
+int
+pinentry_have_display (int argc, char **argv)
+{
+  for (; argc; argc--, argv++)
+    if (!strcmp (*argv, "--display") || !strncmp (*argv, "--display=", 10))
+      return 1;
+  return 0;
+}
+
+
+
+/* Print usage information and and provide strings for help. */
+static const char *
+my_strusage( int level )
+{
+  const char *p;
+
+  switch (level)
+    {
+    case 11: p = this_pgmname; break;
+    case 12: p = "pinentry"; break;
+    case 13: p = VERSION; break;
+    case 14: p = "Copyright (C) 2015 g10 Code GmbH"; break;
+    case 19: p = "Please report bugs to <" BUGREPORT ">.\n"; break;
+    case 1:
+    case 40:
+      {
+        static char *str;
+
+        if (!str)
+          {
+            size_t n = 50 + strlen (this_pgmname);
+            str = malloc (n);
+            if (str)
+              snprintf (str, n, "Usage: %s [options] (-h for help)",
+                        this_pgmname);
+          }
+        p = str;
+      }
+      break;
+    case 41:
+      p = "Ask securely for a secret and print it to stdout.";
+      break;
+
+    case 42:
+      p = "1"; /* Flag print 40 as part of 41. */
+      break;
+
+    default: p = NULL; break;
+    }
+  return p;
+}
+
+
+char *
+parse_color (char *arg, pinentry_color_t *color_p, int *bright_p)
+{
+  static struct
+  {
+    const char *name;
+    pinentry_color_t color;
+  } colors[] = { { "none", PINENTRY_COLOR_NONE },
+		 { "default", PINENTRY_COLOR_DEFAULT },
+		 { "black", PINENTRY_COLOR_BLACK },
+		 { "red", PINENTRY_COLOR_RED },
+		 { "green", PINENTRY_COLOR_GREEN },
+		 { "yellow", PINENTRY_COLOR_YELLOW },
+		 { "blue", PINENTRY_COLOR_BLUE },
+		 { "magenta", PINENTRY_COLOR_MAGENTA },
+		 { "cyan", PINENTRY_COLOR_CYAN },
+		 { "white", PINENTRY_COLOR_WHITE } };
+
+  int i;
+  char *new_arg;
+  pinentry_color_t color = PINENTRY_COLOR_DEFAULT;
+
+  if (!arg)
+    return NULL;
+
+  new_arg = strchr (arg, ',');
+  if (new_arg)
+    new_arg++;
+
+  if (bright_p)
+    {
+      const char *bname[] = { "bright-", "bright", "bold-", "bold" };
+
+      *bright_p = 0;
+      for (i = 0; i < sizeof (bname) / sizeof (bname[0]); i++)
+	if (!strncasecmp (arg, bname[i], strlen (bname[i])))
+	  {
+	    *bright_p = 1;
+	    arg += strlen (bname[i]);
+	  }
+    }
+
+  for (i = 0; i < sizeof (colors) / sizeof (colors[0]); i++)
+    if (!strncasecmp (arg, colors[i].name, strlen (colors[i].name)))
+      color = colors[i].color;
+
+  *color_p = color;
+  return new_arg;
+}
+
+/* Parse the command line options.  May exit the program if only help
+   or version output is requested.  */
+void
+pinentry_parse_opts (int argc, char *argv[])
+{
+  static ARGPARSE_OPTS opts[] = {
+    ARGPARSE_s_n('d', "debug",    "Turn on debugging output"),
+    ARGPARSE_s_s('D', "display",  "|DISPLAY|Set the X display"),
+    ARGPARSE_s_s('T', "ttyname",  "|FILE|Set the tty terminal node name"),
+    ARGPARSE_s_s('N', "ttytype",  "|NAME|Set the tty terminal type"),
+    ARGPARSE_s_s('C', "lc-ctype", "|STRING|Set the tty LC_CTYPE value"),
+    ARGPARSE_s_s('M', "lc-messages", "|STRING|Set the tty LC_MESSAGES value"),
+    ARGPARSE_s_i('o', "timeout",
+                 "|SECS|Timeout waiting for input after this many seconds"),
+    ARGPARSE_s_n('g', "no-global-grab",
+                 "Grab keyboard only while window is focused"),
+    ARGPARSE_s_u('W', "parent-wid", "Parent window ID (for positioning)"),
+    ARGPARSE_s_s('c', "colors", "|STRING|Set custom colors for ncurses"),
+    ARGPARSE_end()
+  };
+  ARGPARSE_ARGS pargs = { &argc, &argv, 0 };
+
+  set_strusage (my_strusage);
+
+  pinentry_reset (1);
+
+  while (arg_parse  (&pargs, opts))
+    {
+      switch (pargs.r_opt)
+        {
+        case 'd':
+          pinentry.debug = 1;
+          break;
+        case 'g':
+          pinentry.grab = 0;
+          break;
+
+	case 'D':
+          /* Note, this is currently not used because the GUI engine
+             has already been initialized when parsing these options. */
+	  pinentry.display = strdup (pargs.r.ret_str);
+	  if (!pinentry.display)
+	    {
+	      exit (EXIT_FAILURE);
+	    }
+	  break;
+	case 'T':
+	  pinentry.ttyname = strdup (pargs.r.ret_str);
+	  if (!pinentry.ttyname)
+	    {
+	      exit (EXIT_FAILURE);
+	    }
+	  break;
+	case 'N':
+	  pinentry.ttytype = strdup (pargs.r.ret_str);
+	  if (!pinentry.ttytype)
+	    {
+	      exit (EXIT_FAILURE);
+	    }
+	  break;
+	case 'C':
+	  pinentry.lc_ctype = strdup (pargs.r.ret_str);
+	  if (!pinentry.lc_ctype)
+	    {
+	      exit (EXIT_FAILURE);
+	    }
+	  break;
+	case 'M':
+	  pinentry.lc_messages = strdup (pargs.r.ret_str);
+	  if (!pinentry.lc_messages)
+	    {
+	      exit (EXIT_FAILURE);
+	    }
+	  break;
+	case 'W':
+	  pinentry.parent_wid = pargs.r.ret_ulong;
+	  break;
+
+	case 'c':
+          {
+            char *tmpstr = pargs.r.ret_str;
+
+            tmpstr = parse_color (tmpstr, &pinentry.color_fg,
+                                  &pinentry.color_fg_bright);
+            tmpstr = parse_color (tmpstr, &pinentry.color_bg, NULL);
+            tmpstr = parse_color (tmpstr, &pinentry.color_so,
+                                  &pinentry.color_so_bright);
+          }
+	  break;
+
+	case 'o':
+	  pinentry.timeout = pargs.r.ret_int;
+	  break;
+
+        default:
+          pargs.err = ARGPARSE_PRINT_WARNING;
+	  break;
+        }
+    }
+}
+
+
+static gpg_error_t
+option_handler (assuan_context_t ctx, const char *key, const char *value)
+{
+  (void)ctx;
+
+  if (!strcmp (key, "no-grab") && !*value)
+    pinentry.grab = 0;
+  else if (!strcmp (key, "grab") && !*value)
+    pinentry.grab = 1;
+  else if (!strcmp (key, "debug-wait"))
+    {
+    }
+  else if (!strcmp (key, "display"))
+    {
+      if (pinentry.display)
+	free (pinentry.display);
+      pinentry.display = strdup (value);
+      if (!pinentry.display)
+	return gpg_error_from_syserror ();
+    }
+  else if (!strcmp (key, "ttyname"))
+    {
+      if (pinentry.ttyname)
+	free (pinentry.ttyname);
+      pinentry.ttyname = strdup (value);
+      if (!pinentry.ttyname)
+	return gpg_error_from_syserror ();
+    }
+  else if (!strcmp (key, "ttytype"))
+    {
+      if (pinentry.ttytype)
+	free (pinentry.ttytype);
+      pinentry.ttytype = strdup (value);
+      if (!pinentry.ttytype)
+	return gpg_error_from_syserror ();
+    }
+  else if (!strcmp (key, "lc-ctype"))
+    {
+      if (pinentry.lc_ctype)
+	free (pinentry.lc_ctype);
+      pinentry.lc_ctype = strdup (value);
+      if (!pinentry.lc_ctype)
+	return gpg_error_from_syserror ();
+    }
+  else if (!strcmp (key, "lc-messages"))
+    {
+      if (pinentry.lc_messages)
+	free (pinentry.lc_messages);
+      pinentry.lc_messages = strdup (value);
+      if (!pinentry.lc_messages)
+	return gpg_error_from_syserror ();
+    }
+  else if (!strcmp (key, "parent-wid"))
+    {
+      pinentry.parent_wid = atoi (value);
+      /* FIXME: Use strtol and add some error handling.  */
+    }
+  else if (!strcmp (key, "touch-file"))
+    {
+      if (pinentry.touch_file)
+        free (pinentry.touch_file);
+      pinentry.touch_file = strdup (value);
+      if (!pinentry.touch_file)
+	return gpg_error_from_syserror ();
+    }
+  else if (!strcmp (key, "default-ok"))
+    {
+      pinentry.default_ok = strdup (value);
+      if (!pinentry.default_ok)
+	return gpg_error_from_syserror ();
+    }
+  else if (!strcmp (key, "default-cancel"))
+    {
+      pinentry.default_cancel = strdup (value);
+      if (!pinentry.default_cancel)
+	return gpg_error_from_syserror ();
+    }
+  else if (!strcmp (key, "default-prompt"))
+    {
+      pinentry.default_prompt = strdup (value);
+      if (!pinentry.default_prompt)
+	return gpg_error_from_syserror ();
+    }
+  else if (!strcmp (key, "default-pwmngr"))
+    {
+      pinentry.default_pwmngr = strdup (value);
+      if (!pinentry.default_pwmngr)
+	return gpg_error_from_syserror ();
+    }
+  else if (!strcmp (key, "allow-external-password-cache") && !*value)
+    {
+      pinentry.allow_external_password_cache = 1;
+      pinentry.tried_password_cache = 0;
+    }
+  else if (!strcmp (key, "allow-emacs-prompt") && !*value)
+    {
+      return gpg_error (GPG_ERR_NOT_SUPPORTED);
+    }
+  else
+    return gpg_error (GPG_ERR_UNKNOWN_OPTION);
+  return 0;
+}
+
+
+/* Note, that it is sufficient to allocate the target string D as
+   long as the source string S, i.e.: strlen(s)+1; */
+static void
+strcpy_escaped (char *d, const char *s)
+{
+  while (*s)
+    {
+      if (*s == '%' && s[1] && s[2])
+        {
+          s++;
+          *d++ = xtoi_2 ( s);
+          s += 2;
+        }
+      else
+        *d++ = *s++;
+    }
+  *d = 0;
+}
+
+
+static gpg_error_t
+cmd_setdesc (assuan_context_t ctx, char *line)
+{
+  char *newd;
+
+  (void)ctx;
+
+  newd = malloc (strlen (line) + 1);
+  if (!newd)
+    return gpg_error_from_syserror ();
+
+  strcpy_escaped (newd, line);
+  if (pinentry.description)
+    free (pinentry.description);
+  pinentry.description = newd;
+  return 0;
+}
+
+
+static gpg_error_t
+cmd_setprompt (assuan_context_t ctx, char *line)
+{
+  char *newp;
+
+  (void)ctx;
+
+  newp = malloc (strlen (line) + 1);
+  if (!newp)
+    return gpg_error_from_syserror ();
+
+  strcpy_escaped (newp, line);
+  if (pinentry.prompt)
+    free (pinentry.prompt);
+  pinentry.prompt = newp;
+  return 0;
+}
+
+
+/* The data provided at LINE may be used by pinentry implementations
+   to identify a key for caching strategies of its own.  The empty
+   string and --clear mean that the key does not have a stable
+   identifier.  */
+static gpg_error_t
+cmd_setkeyinfo (assuan_context_t ctx, char *line)
+{
+  (void)ctx;
+
+  if (pinentry.keyinfo)
+    free (pinentry.keyinfo);
+
+  if (*line && strcmp(line, "--clear") != 0)
+    pinentry.keyinfo = strdup (line);
+  else
+    pinentry.keyinfo = NULL;
+
+  return 0;
+}
+
+
+static gpg_error_t
+cmd_setrepeat (assuan_context_t ctx, char *line)
+{
+  char *p;
+
+  (void)ctx;
+
+  p = malloc (strlen (line) + 1);
+  if (!p)
+    return gpg_error_from_syserror ();
+
+  strcpy_escaped (p, line);
+  free (pinentry.repeat_passphrase);
+  pinentry.repeat_passphrase = p;
+  return 0;
+}
+
+
+static gpg_error_t
+cmd_setrepeaterror (assuan_context_t ctx, char *line)
+{
+  char *p;
+
+  (void)ctx;
+
+  p = malloc (strlen (line) + 1);
+  if (!p)
+    return gpg_error_from_syserror ();
+
+  strcpy_escaped (p, line);
+  free (pinentry.repeat_error_string);
+  pinentry.repeat_error_string = p;
+  return 0;
+}
+
+
+static gpg_error_t
+cmd_seterror (assuan_context_t ctx, char *line)
+{
+  char *newe;
+
+  (void)ctx;
+
+  newe = malloc (strlen (line) + 1);
+  if (!newe)
+    return gpg_error_from_syserror ();
+
+  strcpy_escaped (newe, line);
+  if (pinentry.error)
+    free (pinentry.error);
+  pinentry.error = newe;
+  return 0;
+}
+
+
+static gpg_error_t
+cmd_setok (assuan_context_t ctx, char *line)
+{
+  char *newo;
+
+  (void)ctx;
+
+  newo = malloc (strlen (line) + 1);
+  if (!newo)
+    return gpg_error_from_syserror ();
+
+  strcpy_escaped (newo, line);
+  if (pinentry.ok)
+    free (pinentry.ok);
+  pinentry.ok = newo;
+  return 0;
+}
+
+
+static gpg_error_t
+cmd_setnotok (assuan_context_t ctx, char *line)
+{
+  char *newo;
+
+  (void)ctx;
+
+  newo = malloc (strlen (line) + 1);
+  if (!newo)
+    return gpg_error_from_syserror ();
+
+  strcpy_escaped (newo, line);
+  if (pinentry.notok)
+    free (pinentry.notok);
+  pinentry.notok = newo;
+  return 0;
+}
+
+
+static gpg_error_t
+cmd_setcancel (assuan_context_t ctx, char *line)
+{
+  char *newc;
+
+  (void)ctx;
+
+  newc = malloc (strlen (line) + 1);
+  if (!newc)
+    return gpg_error_from_syserror ();
+
+  strcpy_escaped (newc, line);
+  if (pinentry.cancel)
+    free (pinentry.cancel);
+  pinentry.cancel = newc;
+  return 0;
+}
+
+
+static gpg_error_t
+cmd_settimeout (assuan_context_t ctx, char *line)
+{
+  (void)ctx;
+
+  if (line && *line)
+    pinentry.timeout = atoi (line);
+
+  return 0;
+}
+
+static gpg_error_t
+cmd_settitle (assuan_context_t ctx, char *line)
+{
+  char *newt;
+
+  (void)ctx;
+
+  newt = malloc (strlen (line) + 1);
+  if (!newt)
+    return gpg_error_from_syserror ();
+
+  strcpy_escaped (newt, line);
+  if (pinentry.title)
+    free (pinentry.title);
+  pinentry.title = newt;
+  return 0;
+}
+
+static gpg_error_t
+cmd_setqualitybar (assuan_context_t ctx, char *line)
+{
+  char *newval;
+
+  (void)ctx;
+
+  if (!*line)
+    line = "Quality:";
+
+  newval = malloc (strlen (line) + 1);
+  if (!newval)
+    return gpg_error_from_syserror ();
+
+  strcpy_escaped (newval, line);
+  if (pinentry.quality_bar)
+    free (pinentry.quality_bar);
+  pinentry.quality_bar = newval;
+  return 0;
+}
+
+/* Set the tooltip to be used for a quality bar.  */
+static gpg_error_t
+cmd_setqualitybar_tt (assuan_context_t ctx, char *line)
+{
+  char *newval;
+
+  (void)ctx;
+
+  if (*line)
+    {
+      newval = malloc (strlen (line) + 1);
+      if (!newval)
+        return gpg_error_from_syserror ();
+
+      strcpy_escaped (newval, line);
+    }
+  else
+    newval = NULL;
+  if (pinentry.quality_bar_tt)
+    free (pinentry.quality_bar_tt);
+  pinentry.quality_bar_tt = newval;
+  return 0;
+}
+
+
+static gpg_error_t
+cmd_getpin (assuan_context_t ctx, char *line)
+{
+  int result;
+  int set_prompt = 0;
+  int just_read_password_from_cache = 0;
+
+  (void)line;
+
+  pinentry_setbuffer_init (&pinentry);
+  if (!pinentry.pin)
+    return gpg_error (GPG_ERR_ENOMEM);
+
+  /* Try reading from the password cache.  */
+  if (/* If repeat passphrase is set, then we don't want to read from
+	 the cache.  */
+      ! pinentry.repeat_passphrase
+      /* Are we allowed to read from the cache?  */
+      && pinentry.allow_external_password_cache
+      && pinentry.keyinfo
+      /* Only read from the cache if we haven't already tried it.  */
+      && ! pinentry.tried_password_cache
+      /* If the last read resulted in an error, then don't read from
+	 the cache.  */
+      && ! pinentry.error)
+    {
+      char *password;
+
+      pinentry.tried_password_cache = 1;
+
+      password = password_cache_lookup (pinentry.keyinfo);
+      if (password)
+	/* There is a cached password.  Try it.  */
+	{
+	  int len = strlen(password) + 1;
+	  if (len > pinentry.pin_len)
+	    len = pinentry.pin_len;
+
+	  memcpy (pinentry.pin, password, len);
+	  pinentry.pin[len] = '\0';
+
+	  secmem_free (password);
+
+	  pinentry.pin_from_cache = 1;
+
+	  assuan_write_status (ctx, "PASSWORD_FROM_CACHE", "");
+
+	  /* Result is the length of the password not including the
+	     NUL terminator.  */
+	  result = len - 1;
+
+	  just_read_password_from_cache = 1;
+
+	  goto out;
+	}
+    }
+
+  /* The password was not cached (or we are not allowed to / cannot
+     use the cache).  Prompt the user.  */
+  pinentry.pin_from_cache = 0;
+
+  if (!pinentry.prompt)
+    {
+      pinentry.prompt = pinentry.default_prompt?pinentry.default_prompt:"PIN:";
+      set_prompt = 1;
+    }
+  pinentry.locale_err = 0;
+  pinentry.specific_err = 0;
+  pinentry.close_button = 0;
+  pinentry.repeat_okay = 0;
+  pinentry.one_button = 0;
+  pinentry.ctx_assuan = ctx;
+  result = (*pinentry_cmd_handler) (&pinentry);
+  pinentry.ctx_assuan = NULL;
+  if (pinentry.error)
+    {
+      free (pinentry.error);
+      pinentry.error = NULL;
+    }
+  if (pinentry.repeat_passphrase)
+    {
+      free (pinentry.repeat_passphrase);
+      pinentry.repeat_passphrase = NULL;
+    }
+  if (set_prompt)
+    pinentry.prompt = NULL;
+
+  pinentry.quality_bar = 0;  /* Reset it after the command.  */
+
+  if (pinentry.close_button)
+    assuan_write_status (ctx, "BUTTON_INFO", "close");
+
+  if (result < 0)
+    {
+      pinentry_setbuffer_clear (&pinentry);
+      if (pinentry.specific_err)
+        return pinentry.specific_err;
+      return (pinentry.locale_err
+	      ? gpg_error (GPG_ERR_LOCALE_PROBLEM)
+	      : gpg_error (GPG_ERR_CANCELED));
+    }
+
+ out:
+  if (result)
+    {
+      if (pinentry.repeat_okay)
+        assuan_write_status (ctx, "PIN_REPEATED", "");
+      result = assuan_send_data (ctx, pinentry.pin, strlen(pinentry.pin));
+      if (!result)
+	result = assuan_send_data (ctx, NULL, 0);
+
+      if (/* GPG Agent says it's okay.  */
+	  pinentry.allow_external_password_cache && pinentry.keyinfo
+	  /* We didn't just read it from the cache.  */
+	  && ! just_read_password_from_cache
+	  /* And the user said it's okay.  */
+	  && pinentry.may_cache_password)
+	/* Cache the password.  */
+	password_cache_save (pinentry.keyinfo, pinentry.pin);
+    }
+
+  pinentry_setbuffer_clear (&pinentry);
+
+  return result;
+}
+
+
+/* Note that the option --one-button is a hack to allow the use of old
+   pinentries while the caller is ignoring the result.  Given that
+   options have never been used or flagged as an error the new option
+   is an easy way to enable the messsage mode while not requiring to
+   update pinentry or to have the caller test for the message
+   command.  New applications which are free to require an updated
+   pinentry should use MESSAGE instead. */
+static gpg_error_t
+cmd_confirm (assuan_context_t ctx, char *line)
+{
+  int result;
+
+  pinentry.one_button = !!strstr (line, "--one-button");
+  pinentry.quality_bar = 0;
+  pinentry.close_button = 0;
+  pinentry.locale_err = 0;
+  pinentry.specific_err = 0;
+  pinentry.canceled = 0;
+  pinentry_setbuffer_clear (&pinentry);
+  result = (*pinentry_cmd_handler) (&pinentry);
+  if (pinentry.error)
+    {
+      free (pinentry.error);
+      pinentry.error = NULL;
+    }
+
+  if (pinentry.close_button)
+    assuan_write_status (ctx, "BUTTON_INFO", "close");
+
+  if (result)
+    return 0;
+
+  if (pinentry.specific_err)
+    return pinentry.specific_err;
+
+  if (pinentry.locale_err)
+    return gpg_error (GPG_ERR_LOCALE_PROBLEM);
+
+  if (pinentry.one_button)
+    return 0;
+
+  if (pinentry.canceled)
+    return gpg_error (GPG_ERR_CANCELED);
+  return gpg_error (GPG_ERR_NOT_CONFIRMED);
+}
+
+
+static gpg_error_t
+cmd_message (assuan_context_t ctx, char *line)
+{
+  (void)line;
+
+  return cmd_confirm (ctx, "--one-button");
+}
+
+/* GETINFO <what>
+
+   Multipurpose function to return a variety of information.
+   Supported values for WHAT are:
+
+     version     - Return the version of the program.
+     pid         - Return the process id of the server.
+ */
+static gpg_error_t
+cmd_getinfo (assuan_context_t ctx, char *line)
+{
+  int rc;
+
+  if (!strcmp (line, "version"))
+    {
+      const char *s = VERSION;
+      rc = assuan_send_data (ctx, s, strlen (s));
+    }
+  else if (!strcmp (line, "pid"))
+    {
+      char numbuf[50];
+
+      snprintf (numbuf, sizeof numbuf, "%lu", (unsigned long)getpid ());
+      rc = assuan_send_data (ctx, numbuf, strlen (numbuf));
+    }
+  else
+    rc = gpg_error (GPG_ERR_ASS_PARAMETER);
+  return rc;
+}
+
+/* CLEARPASSPHRASE <cacheid>
+
+   Clear the cache passphrase associated with the key identified by
+   cacheid.
+ */
+static gpg_error_t
+cmd_clear_passphrase (assuan_context_t ctx, char *line)
+{
+  (void)ctx;
+
+  if (! line)
+    return gpg_error (GPG_ERR_ASS_INV_VALUE);
+
+  /* Remove leading and trailing white space.  */
+  while (*line == ' ')
+    line ++;
+  while (line[strlen (line) - 1] == ' ')
+    line[strlen (line) - 1] = 0;
+
+  switch (password_cache_clear (line))
+    {
+    case 1: return 0;
+    case 0: return gpg_error (GPG_ERR_ASS_INV_VALUE);
+    default: return gpg_error (GPG_ERR_ASS_GENERAL);
+    }
+}
+
+/* Tell the assuan library about our commands.  */
+static gpg_error_t
+register_commands (assuan_context_t ctx)
+{
+  static struct
+  {
+    const char *name;
+    gpg_error_t (*handler) (assuan_context_t, char *line);
+  } table[] =
+    {
+      { "SETDESC",    cmd_setdesc },
+      { "SETPROMPT",  cmd_setprompt },
+      { "SETKEYINFO", cmd_setkeyinfo },
+      { "SETREPEAT",  cmd_setrepeat },
+      { "SETREPEATERROR", cmd_setrepeaterror },
+      { "SETERROR",   cmd_seterror },
+      { "SETOK",      cmd_setok },
+      { "SETNOTOK",   cmd_setnotok },
+      { "SETCANCEL",  cmd_setcancel },
+      { "GETPIN",     cmd_getpin },
+      { "CONFIRM",    cmd_confirm },
+      { "MESSAGE",    cmd_message },
+      { "SETQUALITYBAR", cmd_setqualitybar },
+      { "SETQUALITYBAR_TT", cmd_setqualitybar_tt },
+      { "GETINFO",    cmd_getinfo },
+      { "SETTITLE",   cmd_settitle },
+      { "SETTIMEOUT", cmd_settimeout },
+      { "CLEARPASSPHRASE", cmd_clear_passphrase },
+      { NULL }
+    };
+  int i, j;
+  gpg_error_t rc;
+
+  for (i = j = 0; table[i].name; i++)
+    {
+      rc = assuan_register_command (ctx, table[i].name, table[i].handler, NULL);
+      if (rc)
+        return rc;
+    }
+  return 0;
+}
+
+
+int
+pinentry_loop2 (int infd, int outfd)
+{
+  gpg_error_t rc;
+  assuan_fd_t filedes[2];
+  assuan_context_t ctx;
+
+  /* Extra check to make sure we have dropped privs. */
+  if (getuid() != geteuid())
+    abort ();
+
+  rc = assuan_new (&ctx);
+  if (rc)
+    {
+      fprintf (stderr, "server context creation failed: %s\n",
+	       gpg_strerror (rc));
+      return -1;
+    }
+
+  /* For now we use a simple pipe based server so that we can work
+     from scripts.  We will later add options to run as a daemon and
+     wait for requests on a Unix domain socket.  */
+  filedes[0] = assuan_fdopen (infd);
+  filedes[1] = assuan_fdopen (outfd);
+  rc = assuan_init_pipe_server (ctx, filedes);
+  if (rc)
+    {
+      fprintf (stderr, "%s: failed to initialize the server: %s\n",
+               this_pgmname, gpg_strerror (rc));
+      return -1;
+    }
+  rc = register_commands (ctx);
+  if (rc)
+    {
+      fprintf (stderr, "%s: failed to the register commands with Assuan: %s\n",
+               this_pgmname, gpg_strerror (rc));
+      return -1;
+    }
+
+  assuan_register_option_handler (ctx, option_handler);
+  assuan_register_reset_notify (ctx, pinentry_assuan_reset_handler);
+
+  for (;;)
+    {
+      rc = assuan_accept (ctx);
+      if (rc == -1)
+          break;
+      else if (rc)
+        {
+          fprintf (stderr, "%s: Assuan accept problem: %s\n",
+                   this_pgmname, gpg_strerror (rc));
+          break;
+        }
+
+      rc = assuan_process (ctx);
+      if (rc)
+        {
+          fprintf (stderr, "%s: Assuan processing failed: %s\n",
+                   this_pgmname, gpg_strerror (rc));
+          continue;
+        }
+    }
+
+  assuan_release (ctx);
+  return 0;
+}
+
+
+/* Start the pinentry event loop.  The program will start to process
+   Assuan commands until it is finished or an error occurs.  If an
+   error occurs, -1 is returned.  Otherwise, 0 is returned.  */
+int
+pinentry_loop (void)
+{
+  return pinentry_loop2 (STDIN_FILENO, STDOUT_FILENO);
+}
blob - /dev/null
blob + e154ac5cec9da26363e72be00caa1175a2724a2e (mode 644)
--- /dev/null
+++ pinentry-dmenu/pinentry/pinentry.h
@@ -0,0 +1,280 @@
+/* pinentry.h - The interface for the PIN entry support library.
+   Copyright (C) 2002, 2003, 2010, 2015 g10 Code GmbH
+
+   This file is part of PINENTRY.
+
+   PINENTRY 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 2 of the License, or
+   (at your option) any later version.
+
+   PINENTRY is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PINENTRY_H
+#define PINENTRY_H
+
+#ifdef __cplusplus
+extern "C" {
+#if 0
+}
+#endif
+#endif
+
+typedef enum {
+  PINENTRY_COLOR_NONE, PINENTRY_COLOR_DEFAULT,
+  PINENTRY_COLOR_BLACK, PINENTRY_COLOR_RED,
+  PINENTRY_COLOR_GREEN, PINENTRY_COLOR_YELLOW,
+  PINENTRY_COLOR_BLUE, PINENTRY_COLOR_MAGENTA,
+  PINENTRY_COLOR_CYAN, PINENTRY_COLOR_WHITE
+} pinentry_color_t;
+
+struct pinentry
+{
+  /* The window title, or NULL.  (Assuan: "SETTITLE TITLE".)  */
+  char *title;
+  /* The description to display, or NULL.  (Assuan: "SETDESC
+     DESC".) */
+  char *description;
+  /* The error message to display, or NULL.  (Assuan: "SETERROR
+     MESSAGE".) */
+  char *error;
+  /* The prompt to display, or NULL.  (Assuan: "SETPROMPT
+     prompt".)  */
+  char *prompt;
+  /* The OK button text to display, or NULL.  (Assuan: "SETOK
+     OK".)  */
+  char *ok;
+  /* The Not-OK button text to display, or NULL.  This is the text for
+     the alternative option shown by the third button.  (Assuan:
+     "SETNOTOK NOTOK".)  */
+  char *notok;
+  /* The Cancel button text to display, or NULL.  (Assuan: "SETCANCEL
+     CANCEL".)  */
+  char *cancel;
+
+  /* The buffer to store the secret into.  */
+  char *pin;
+  /* The length of the buffer.  */
+  int pin_len;
+  /* Whether the pin was read from an external cache (1) or entered by
+     the user (0). */
+  int pin_from_cache;
+
+  /* The name of the X display to use if X is available and supported.
+     (Assuan: "OPTION display DISPLAY".)  */
+  char *display;
+  /* The name of the terminal node to open if X not available or
+     supported.  (Assuan: "OPTION ttyname TTYNAME".)  */
+  char *ttyname;
+  /* The type of the terminal.  (Assuan: "OPTION ttytype TTYTYPE".)  */
+  char *ttytype;
+  /* The LC_CTYPE value for the terminal.  (Assuan: "OPTION lc-ctype
+     LC_CTYPE".)  */
+  char *lc_ctype;
+  /* The LC_MESSAGES value for the terminal.  (Assuan: "OPTION
+     lc-messages LC_MESSAGES".)  */
+  char *lc_messages;
+
+  /* True if debug mode is requested.  */
+  int debug;
+
+  /* The number of seconds before giving up while waiting for user input. */
+  int timeout;
+
+  /* True if caller should grab the keyboard.  (Assuan: "OPTION grab"
+     or "OPTION no-grab".)  */
+  int grab;
+  /* The window ID of the parent window over which the pinentry window
+     should be displayed.  (Assuan: "OPTION parent-wid WID".)  */
+  int parent_wid;
+
+  /* The name of an optional file which will be touched after a curses
+     entry has been displayed.  (Assuan: "OPTION touch-file
+     FILENAME".)  */
+  char *touch_file;
+
+  /* The frontend should set this to -1 if the user canceled the
+     request, and to the length of the PIN stored in pin
+     otherwise.  */
+  int result;
+
+  /* The frontend should set this if the NOTOK button was pressed.  */
+  int canceled;
+
+  /* The frontend should set this to true if an error with the local
+     conversion occured. */
+  int locale_err;
+
+  /* The frontend should set this to a gpg-error so that commands are
+     able to return specific error codes.  This is an ugly hack due to
+     the fact that pinentry_cmd_handler_t returns the length of the
+     passphrase or a negative error code.  */
+  int specific_err;
+
+  /* The frontend should set this to true if the window close button
+     has been used.  This flag is used in addition to a regular return
+     value.  */
+  int close_button;
+
+  /* The caller should set this to true if only one button is
+     required.  This is useful for notification dialogs where only a
+     dismiss button is required. */
+  int one_button;
+
+  /* If true a second prompt for the passphrase is shown and the user
+     is expected to enter the same passphrase again.  Pinentry checks
+     that both match.  (Assuan: "SETREPEAT".)  */
+  char *repeat_passphrase;
+
+  /* The string to show if a repeated passphrase does not match.
+     (Assuan: "SETREPEATERROR ERROR".)  */
+  char *repeat_error_string;
+
+  /* Set to true if the passphrase has been entered a second time and
+     matches the first passphrase.  */
+  int repeat_okay;
+
+  /* If this is not NULL, a passphrase quality indicator is shown.
+     There will also be an inquiry back to the caller to get an
+     indication of the quality for the passphrase entered so far.  The
+     string is used as a label for the quality bar.  (Assuan:
+     "SETQUALITYBAR LABEL".)  */
+  char *quality_bar;
+
+  /* The tooltip to be show for the qualitybar.  Malloced or NULL.
+     (Assuan: "SETQUALITYBAR_TT TOOLTIP".)  */
+  char *quality_bar_tt;
+
+  /* For the curses pinentry, the color of error messages.  */
+  pinentry_color_t color_fg;
+  int color_fg_bright;
+  pinentry_color_t color_bg;
+  pinentry_color_t color_so;
+  int color_so_bright;
+
+  /* Malloced and i18ned default strings or NULL.  These strings may
+     include an underscore character to indicate an accelerator key.
+     A double underscore represents a plain one.  */
+  /* (Assuan: "OPTION default-ok OK").  */
+  char *default_ok;
+  /* (Assuan: "OPTION default-cancel CANCEL").  */
+  char *default_cancel;
+  /* (Assuan: "OPTION default-prompt PROMPT").  */
+  char *default_prompt;
+  /* (Assuan: "OPTION default-pwmngr
+     SAVE_PASSWORD_WITH_PASSWORD_MANAGER?").  */
+  char *default_pwmngr;
+
+  /* Whether we are allowed to read the password from an external
+     cache.  (Assuan: "OPTION allow-external-password-cache")  */
+  int allow_external_password_cache;
+
+  /* We only try the cache once.  */
+  int tried_password_cache;
+
+  /* A stable identifier for the key.  (Assuan: "SETKEYINFO
+     KEYINFO".)  */
+  char *keyinfo;
+
+  /* Whether we may cache the password (according to the user).  */
+  int may_cache_password;
+
+  /* NOTE: If you add any additional fields to this structure, be sure
+     to update the initializer in pinentry/pinentry.c!!!  */
+
+  /* For the quality indicator we need to do an inquiry.  Thus we need
+     to save the assuan ctx.  */
+  void *ctx_assuan;
+
+};
+typedef struct pinentry *pinentry_t;
+
+
+/* The pinentry command handler type processes the pinentry request
+   PIN.  If PIN->pin is zero, request a confirmation, otherwise a PIN
+   entry.  On confirmation, the function should return TRUE if
+   confirmed, and FALSE otherwise.  On PIN entry, the function should
+   return -1 if an error occured or the user cancelled the operation
+   and 1 otherwise.  */
+typedef int (*pinentry_cmd_handler_t) (pinentry_t pin);
+
+/* Start the pinentry event loop.  The program will start to process
+   Assuan commands until it is finished or an error occurs.  If an
+   error occurs, -1 is returned and errno indicates the type of an
+   error.  Otherwise, 0 is returned.  */
+int pinentry_loop (void);
+
+/* The same as above but allows to specify the i/o descriptors.
+ * infd and outfd will be duplicated in this function so the caller
+ * still has to close them if necessary.
+ */
+int pinentry_loop2 (int infd, int outfd);
+
+
+/* Convert the UTF-8 encoded string TEXT to the encoding given in
+   LC_CTYPE.  Return NULL on error. */
+char *pinentry_utf8_to_local (const char *lc_ctype, const char *text);
+
+/* Convert TEXT which is encoded according to LC_CTYPE to UTF-8.  With
+   SECURE set to true, use secure memory for the returned buffer.
+   Return NULL on error. */
+char *pinentry_local_to_utf8 (char *lc_ctype, char *text, int secure);
+
+
+/* Run a quality inquiry for PASSPHRASE of LENGTH. */
+int pinentry_inq_quality (pinentry_t pin,
+                          const char *passphrase, size_t length);
+
+/* Try to make room for at least LEN bytes for the pin in the pinentry
+   PIN.  Returns new buffer on success and 0 on failure.  */
+char *pinentry_setbufferlen (pinentry_t pin, int len);
+
+/* Use the buffer at BUFFER for PIN->PIN.  BUFFER must be NULL or
+   allocated using secmem_alloc.  LEN is the size of the buffer.  If
+   it is unknown, but BUFFER is a NUL terminated string, you pass 0 to
+   just use strlen(buffer)+1.  */
+void pinentry_setbuffer_use (pinentry_t pin, char *buffer, int len);
+
+/* Initialize the secure memory subsystem, drop privileges and
+   return.  Must be called early.  */
+void pinentry_init (const char *pgmname);
+
+/* Return true if either DISPLAY is set or ARGV contains the string
+   "--display". */
+int pinentry_have_display (int argc, char **argv);
+
+/* Parse the command line options.  May exit the program if only help
+   or version output is requested.  */
+void pinentry_parse_opts (int argc, char *argv[]);
+
+
+/* The caller must define this variable to process assuan commands.  */
+extern pinentry_cmd_handler_t pinentry_cmd_handler;
+
+
+
+
+
+#ifdef HAVE_W32_SYSTEM
+/* Windows declares sleep as obsolete, but provides a definition for
+   _sleep but non for the still existing sleep.  */
+#define sleep(a) _sleep ((a))
+#endif /*HAVE_W32_SYSTEM*/
+
+
+
+#if 0
+{
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+#endif	/* PINENTRY_H */
blob - /dev/null
blob + 88a5d45b954d447cc049094dcb1a54f8a405b8ad (mode 644)
--- /dev/null
+++ pinentry-dmenu/pinentry/secmem++.h
@@ -0,0 +1,91 @@
+/* STL allocator for secmem
+ * Copyright (C) 2008 Marc Mutz <marc@kdab.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __SECMEM_SECMEMPP_H__
+#define __SECMEM_SECMEMPP_H__
+
+#include "secmem/memory.h"
+#include <cstddef>
+
+namespace secmem {
+
+    template <typename T>
+    class alloc {
+    public:
+        // type definitions:
+        typedef size_t    size_type;
+        typedef ptrdiff_t difference_type;
+        typedef T*        pointer;
+        typedef const T*  const_pointer;
+        typedef T&        reference;
+        typedef const T&  const_reference;
+        typedef T         value_type;
+
+        // rebind
+        template <typename U>
+        struct rebind {
+            typedef alloc<U> other;
+        };
+
+        // address
+        pointer address( reference value ) const {
+            return &value;
+        }
+        const_pointer address( const_reference value ) const {
+            return &value;
+        }
+
+        // (trivial) ctors and dtors
+        alloc() {}
+        alloc( const alloc & ) {}
+        template <typename U> alloc( const alloc<U> & ) {}
+        // copy ctor is ok
+        ~alloc() {}
+
+        // de/allocation
+        size_type max_size() const {
+            return secmem_get_max_size();
+        }
+
+        pointer allocate( size_type n, void * =0 ) {
+            return static_cast<pointer>( secmem_malloc( n * sizeof(T) ) );
+        }
+
+        void deallocate( pointer p, size_type ) {
+            secmem_free( p );
+        }
+
+        // de/construct
+        void construct( pointer p, const T & value ) {
+            void * loc = p;
+            new (loc)T(value);
+        }
+        void destruct( pointer p ) {
+            p->~T();
+        }
+    };
+
+    // equality comparison
+    template <typename T1,typename T2>
+    bool operator==( const alloc<T1> &, const alloc<T2> & ) { return true; }
+    template <typename T1, typename T2>
+    bool operator!=( const alloc<T1> &, const alloc<T2> & ) { return false; }
+
+}
+
+#endif /* __SECMEM_SECMEMPP_H__ */
blob - /dev/null
blob + b422182b499b577adbf844227b48750ae62d3930 (mode 644)
--- /dev/null
+++ pinentry-dmenu/pinentry/secmem-util.h
@@ -0,0 +1,3 @@
+/* This file exists because "util.h" is such a generic name that it is
+   likely to clash with other such files.  */
+#include "util.h"
blob - /dev/null
blob + 3d5b2cd357bca4b88fb147472773efeb66844528 (mode 644)
--- /dev/null
+++ pinentry-dmenu/pinentry/secmem.c
@@ -0,0 +1,460 @@
+/* secmem.c  -	memory allocation from a secure heap
+ *	Copyright (C) 1998, 1999, 2003 Free Software Foundation, Inc.
+ *      Copyright (C) 2015 g10 Code GmbH
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <unistd.h>
+#if defined(HAVE_MLOCK) || defined(HAVE_MMAP)
+# include <sys/mman.h>
+# include <sys/types.h>
+# include <fcntl.h>
+# ifdef USE_CAPABILITIES
+#  include <sys/capability.h>
+# endif
+#endif
+#include <string.h>
+
+#include "memory.h"
+
+#ifdef ORIGINAL_GPG_VERSION
+#include "types.h"
+#include "util.h"
+#else /* ORIGINAL_GPG_VERSION */
+
+#include "util.h"
+
+typedef union {
+    int a;
+    short b;
+    char c[1];
+    long d;
+#ifdef HAVE_U64_TYPEDEF
+    u64 e;
+#endif
+    float f;
+    double g;
+} PROPERLY_ALIGNED_TYPE;
+
+#define log_error log_info
+#define log_bug log_fatal
+
+void 
+log_info(char *template, ...)
+{
+  va_list args;
+  
+  va_start(args, template);
+  vfprintf(stderr, template, args);
+  va_end(args);
+}
+
+void 
+log_fatal(char *template, ...)
+{
+  va_list args;
+  
+  va_start(args, template);
+  vfprintf(stderr, template, args);
+  va_end(args);
+  exit(EXIT_FAILURE);
+}
+
+#endif /* ORIGINAL_GPG_VERSION */
+
+#if defined(MAP_ANON) && !defined(MAP_ANONYMOUS)
+#  define MAP_ANONYMOUS MAP_ANON
+#endif
+
+#define DEFAULT_POOLSIZE 16384
+
+typedef struct memblock_struct MEMBLOCK;
+struct memblock_struct {
+    unsigned size;
+    union {
+	MEMBLOCK *next;
+	PROPERLY_ALIGNED_TYPE aligned;
+    } u;
+};
+
+
+
+static void  *pool;
+static volatile int pool_okay; /* may be checked in an atexit function */
+__attribute__((unused)) static int   pool_is_mmapped;
+static size_t poolsize; /* allocated length */
+static size_t poollen;	/* used length */
+static MEMBLOCK *unused_blocks;
+static unsigned max_alloced;
+static unsigned cur_alloced;
+static unsigned max_blocks;
+static unsigned cur_blocks;
+static int disable_secmem;
+static int show_warning;
+static int no_warning;
+static int suspend_warning;
+
+
+static void
+print_warn(void)
+{
+    if( !no_warning )
+	log_info("Warning: using insecure memory!\n");
+}
+
+
+static void
+lock_pool( void *p, size_t n )
+{
+#if defined(USE_CAPABILITIES) && defined(HAVE_MLOCK)
+    int err;
+
+    cap_set_proc( cap_from_text("cap_ipc_lock+ep") );
+    err = mlock( p, n );
+    if( err && errno )
+	err = errno;
+    cap_set_proc( cap_from_text("cap_ipc_lock+p") );
+
+    if( err ) {
+	if( errno != EPERM
+	  #ifdef EAGAIN  /* OpenBSD returns this */
+	    && errno != EAGAIN
+	  #endif
+	  )
+	    log_error("can't lock memory: %s\n", strerror(err));
+	show_warning = 1;
+    }
+
+#elif defined(HAVE_MLOCK)
+    uid_t uid;
+    int err;
+
+    uid = getuid();
+
+#ifdef HAVE_BROKEN_MLOCK
+    if( uid ) {
+	errno = EPERM;
+	err = errno;
+    }
+    else {
+	err = mlock( p, n );
+	if( err && errno )
+	    err = errno;
+    }
+#else
+    err = mlock( p, n );
+    if( err && errno )
+	err = errno;
+#endif
+
+    if( uid && !geteuid() ) {
+	if( setuid( uid ) || getuid() != geteuid()  )
+	    log_fatal("failed to reset uid: %s\n", strerror(errno));
+    }
+
+    if( err ) {
+	if( errno != EPERM
+#ifdef EAGAIN  /* OpenBSD returns this */
+	    && errno != EAGAIN
+#endif
+	  )
+	    log_error("can't lock memory: %s\n", strerror(err));
+	show_warning = 1;
+    }
+
+#else
+    log_info("Please note that you don't have secure memory on this system\n");
+#endif
+}
+
+
+static void
+init_pool( size_t n)
+{
+    __attribute__((unused)) size_t pgsize;
+
+    poolsize = n;
+
+    if( disable_secmem )
+	log_bug("secure memory is disabled");
+
+#ifdef HAVE_GETPAGESIZE
+    pgsize = getpagesize();
+#else
+    pgsize = 4096;
+#endif
+
+#if HAVE_MMAP
+    poolsize = (poolsize + pgsize -1 ) & ~(pgsize-1);
+# ifdef MAP_ANONYMOUS
+       pool = mmap( 0, poolsize, PROT_READ|PROT_WRITE,
+				 MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
+# else /* map /dev/zero instead */
+    {	int fd;
+
+	fd = open("/dev/zero", O_RDWR);
+	if( fd == -1 ) {
+	    log_error("can't open /dev/zero: %s\n", strerror(errno) );
+	    pool = (void*)-1;
+	}
+	else {
+	    pool = mmap( 0, poolsize, PROT_READ|PROT_WRITE,
+				      MAP_PRIVATE, fd, 0);
+	    close (fd);
+	}
+    }
+# endif
+    if( pool == (void*)-1 )
+	log_info("can't mmap pool of %u bytes: %s - using malloc\n",
+			    (unsigned)poolsize, strerror(errno));
+    else {
+	pool_is_mmapped = 1;
+	pool_okay = 1;
+    }
+
+#endif
+    if( !pool_okay ) {
+	pool = malloc( poolsize );
+	if( !pool )
+	    log_fatal("can't allocate memory pool of %u bytes\n",
+						       (unsigned)poolsize);
+	else
+	    pool_okay = 1;
+    }
+    lock_pool( pool, poolsize );
+    poollen = 0;
+}
+
+
+/* concatenate unused blocks */
+static void
+compress_pool(void)
+{
+    /* fixme: we really should do this */
+}
+
+void
+secmem_set_flags( unsigned flags )
+{
+    int was_susp = suspend_warning;
+
+    no_warning = flags & 1;
+    suspend_warning = flags & 2;
+
+    /* and now issue the warning if it is not longer suspended */
+    if( was_susp && !suspend_warning && show_warning ) {
+	show_warning = 0;
+	print_warn();
+    }
+}
+
+unsigned
+secmem_get_flags(void)
+{
+    unsigned flags;
+
+    flags  = no_warning      ? 1:0;
+    flags |= suspend_warning ? 2:0;
+    return flags;
+}
+
+void
+secmem_init( size_t n )
+{
+    if( !n ) {
+#ifdef USE_CAPABILITIES
+	/* drop all capabilities */
+	cap_set_proc( cap_from_text("all-eip") );
+
+#elif !defined(HAVE_DOSISH_SYSTEM)
+	uid_t uid;
+
+	disable_secmem=1;
+	uid = getuid();
+	if( uid != geteuid() ) {
+	    if( setuid( uid ) || getuid() != geteuid() )
+		log_fatal("failed to drop setuid\n" );
+	}
+#endif
+    }
+    else {
+	if( n < DEFAULT_POOLSIZE )
+	    n = DEFAULT_POOLSIZE;
+	if( !pool_okay )
+	    init_pool(n);
+	else
+	    log_error("Oops, secure memory pool already initialized\n");
+    }
+}
+
+
+void *
+secmem_malloc( size_t size )
+{
+    MEMBLOCK *mb, *mb2;
+    int compressed=0;
+
+    if( !pool_okay ) {
+	log_info(
+	"operation is not possible without initialized secure memory\n");
+	log_info("(you may have used the wrong program for this task)\n");
+	exit(2);
+    }
+    if( show_warning && !suspend_warning ) {
+	show_warning = 0;
+	print_warn();
+    }
+
+    /* blocks are always a multiple of 32 */
+    size += sizeof(MEMBLOCK);
+    size = ((size + 31) / 32) * 32;
+
+  retry:
+    /* try to get it from the used blocks */
+    for(mb = unused_blocks,mb2=NULL; mb; mb2=mb, mb = mb->u.next )
+	if( mb->size >= size ) {
+	    if( mb2 )
+		mb2->u.next = mb->u.next;
+	    else
+		unused_blocks = mb->u.next;
+	    goto leave;
+	}
+    /* allocate a new block */
+    if( (poollen + size <= poolsize) ) {
+	mb = (void*)((char*)pool + poollen);
+	poollen += size;
+	mb->size = size;
+    }
+    else if( !compressed ) {
+	compressed=1;
+	compress_pool();
+	goto retry;
+    }
+    else
+	return NULL;
+
+  leave:
+    cur_alloced += mb->size;
+    cur_blocks++;
+    if( cur_alloced > max_alloced )
+	max_alloced = cur_alloced;
+    if( cur_blocks > max_blocks )
+	max_blocks = cur_blocks;
+
+    memset (&mb->u.aligned.c, 0,
+	    size - (size_t) &((struct memblock_struct *) 0)->u.aligned.c);
+
+    return &mb->u.aligned.c;
+}
+
+
+void *
+secmem_realloc( void *p, size_t newsize )
+{
+    MEMBLOCK *mb;
+    size_t size;
+    void *a;
+
+    if (! p)
+      return secmem_malloc(newsize);
+
+    mb = (MEMBLOCK*)((char*)p - ((size_t) &((MEMBLOCK*)0)->u.aligned.c));
+    size = mb->size;
+    if( newsize < size )
+	return p; /* it is easier not to shrink the memory */
+    a = secmem_malloc( newsize );
+    memcpy(a, p, size);
+    memset((char*)a+size, 0, newsize-size);
+    secmem_free(p);
+    return a;
+}
+
+
+void
+secmem_free( void *a )
+{
+    MEMBLOCK *mb;
+    size_t size;
+
+    if( !a )
+	return;
+
+    mb = (MEMBLOCK*)((char*)a - ((size_t) &((MEMBLOCK*)0)->u.aligned.c));
+    size = mb->size;
+    /* This does not make much sense: probably this memory is held in the
+     * cache. We do it anyway: */
+    wipememory2(mb, 0xff, size );
+    wipememory2(mb, 0xaa, size );
+    wipememory2(mb, 0x55, size );
+    wipememory2(mb, 0x00, size );
+    mb->size = size;
+    mb->u.next = unused_blocks;
+    unused_blocks = mb;
+    cur_blocks--;
+    cur_alloced -= size;
+}
+
+int
+m_is_secure( const void *p )
+{
+    return p >= pool && p < (void*)((char*)pool+poolsize);
+}
+
+void
+secmem_term(void)
+{
+    if( !pool_okay )
+	return;
+
+    wipememory2( pool, 0xff, poolsize);
+    wipememory2( pool, 0xaa, poolsize);
+    wipememory2( pool, 0x55, poolsize);
+    wipememory2( pool, 0x00, poolsize);
+#if HAVE_MMAP
+    if( pool_is_mmapped )
+	munmap( pool, poolsize );
+#endif
+    pool = NULL;
+    pool_okay = 0;
+    poolsize=0;
+    poollen=0;
+    unused_blocks=NULL;
+}
+
+
+void
+secmem_dump_stats(void)
+{
+    if( disable_secmem )
+	return;
+    fprintf(stderr,
+		"secmem usage: %u/%u bytes in %u/%u blocks of pool %lu/%lu\n",
+		cur_alloced, max_alloced, cur_blocks, max_blocks,
+		(ulong)poollen, (ulong)poolsize );
+}
+
+
+size_t 
+secmem_get_max_size (void)
+{
+  return poolsize;
+}
blob - /dev/null
blob + feddaf73b09360c488fa7cceec91a62481da3ee3 (mode 644)
--- /dev/null
+++ pinentry-dmenu/pinentry/util.c
@@ -0,0 +1,150 @@
+/* Quintuple Agent
+ * Copyright (C) 1999 Robert Bihlmeyer <robbe@orcus.priv.at>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE 1
+
+#include <unistd.h>
+#ifndef HAVE_W32CE_SYSTEM
+# include <errno.h>
+#endif
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "util.h"
+
+#ifndef HAVE_DOSISH_SYSTEM
+static int uid_set = 0;
+static uid_t real_uid, file_uid;
+#endif /*!HAVE_DOSISH_SYSTEM*/
+
+/* Write DATA of size BYTES to FD, until all is written or an error
+   occurs.  */
+ssize_t 
+xwrite(int fd, const void *data, size_t bytes)
+{
+  char *ptr;
+  size_t todo;
+  ssize_t written = 0;
+
+  for (ptr = (char *)data, todo = bytes; todo; ptr += written, todo -= written)
+    {
+      do
+        written = write (fd, ptr, todo);
+      while (
+#ifdef HAVE_W32CE_SYSTEM
+             0
+#else
+             written == -1 && errno == EINTR
+#endif
+             );
+      if (written < 0)
+        break;
+    }
+  return written;
+}
+
+#if 0
+extern int debug;
+
+int 
+debugmsg(const char *fmt, ...)
+{
+  va_list va;
+  int ret;
+
+  if (debug) {
+    va_start(va, fmt);
+    fprintf(stderr, "\e[4m");
+    ret = vfprintf(stderr, fmt, va);
+    fprintf(stderr, "\e[24m");
+    va_end(va);
+    return ret;
+  } else
+    return 0;
+}
+#endif
+
+/* initialize uid variables */
+#ifndef HAVE_DOSISH_SYSTEM
+static void 
+init_uids(void)
+{
+  real_uid = getuid();
+  file_uid = geteuid();
+  uid_set = 1;
+}
+#endif
+
+
+#if 0 /* Not used. */
+/* lower privileges to the real user's */
+void 
+lower_privs()
+{
+  if (!uid_set)
+    init_uids();
+  if (real_uid != file_uid) {
+#ifdef HAVE_SETEUID
+    if (seteuid(real_uid) < 0) {
+      perror("lowering privileges failed");
+      exit(EXIT_FAILURE);
+    }
+#else
+    fprintf(stderr, _("Warning: running q-agent setuid on this system is dangerous\n"));
+#endif /* HAVE_SETEUID */
+  }
+}
+#endif /* if 0 */
+
+#if 0 /* Not used. */
+/* raise privileges to the effective user's */
+void 
+raise_privs()
+{
+  assert(real_uid >= 0);	/* lower_privs() must be called before this */
+#ifdef HAVE_SETEUID
+  if (real_uid != file_uid && seteuid(file_uid) < 0) {
+   perror("Warning: raising privileges failed");
+  }
+#endif /* HAVE_SETEUID */
+}
+#endif /* if 0 */
+
+/* drop all additional privileges */
+void 
+drop_privs(void)
+{
+#ifndef HAVE_DOSISH_SYSTEM
+  if (!uid_set)
+    init_uids();
+  if (real_uid != file_uid) {
+    if (setuid(real_uid) < 0) {
+      perror("dropping privileges failed");
+      exit(EXIT_FAILURE);
+    }
+    file_uid = real_uid;
+  }
+#endif
+}
blob - /dev/null
blob + 7986c996c6afd3141c99e14c7d6ef123e7563e3f (mode 644)
--- /dev/null
+++ pinentry-dmenu/pinentry/util.h
@@ -0,0 +1,66 @@
+/* Quintuple Agent utilities
+ * Copyright (C) 1999 Robert Bihlmeyer <robbe@orcus.priv.at>
+ * Copyright (C) 2003 g10 Code GmbH
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _UTIL_H
+#define _UTIL_H
+
+#include <sys/types.h>
+
+#ifndef HAVE_BYTE_TYPEDEF
+# undef byte	   
+# ifdef __riscos__
+    /* Norcroft treats char == unsigned char but char* != unsigned char*  */
+    typedef char byte;
+# else 
+    typedef unsigned char byte;
+# endif 
+# define HAVE_BYTE_TYPEDEF
+#endif
+
+#ifndef HAVE_ULONG_TYPEDEF
+# undef ulong	   
+  typedef unsigned long ulong;
+# define HAVE_ULONG_TYPEDEF
+#endif
+
+
+ssize_t xwrite(int, const void *, size_t); /* write until finished */
+int debugmsg(const char *, ...); /* output a debug message if debugging==on */
+void drop_privs(void);		/* finally drop privileges */
+
+
+/* To avoid that a compiler optimizes certain memset calls away, these
+   macros may be used instead. */
+#define wipememory2(_ptr,_set,_len) do { \
+              volatile char *_vptr=(volatile char *)(_ptr); \
+              size_t _vlen=(_len); \
+              while(_vlen) { *_vptr=(_set); _vptr++; _vlen--; } \
+                  } while(0)
+#define wipememory(_ptr,_len) wipememory2(_ptr,0,_len)
+#define wipe(_ptr,_len)       wipememory2(_ptr,0,_len)
+
+
+
+
+#define xtoi_1(p)   (*(p) <= '9'? (*(p)- '0'): \
+                     *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10))
+#define xtoi_2(p)   ((xtoi_1(p) * 16) + xtoi_1((p)+1))
+
+
+#endif
blob - /dev/null
blob + 67aafd18d32a53f6a3c111e331e58424ebffcb44 (mode 644)
--- /dev/null
+++ pinentry-dmenu/pinentry-dmenu.1
@@ -0,0 +1,228 @@
+.TH PINENTRY-DMENU 1 "DATE" pinentry-dmenu\-VERSION "pinentry-dmenu Manual"
+
+
+.SH NAME
+pinentry-dmenu - a pinentry program with the charm of dmenu
+.SH DESCRIPTION
+.B pinentry-dmenu 
+is a dmenu- and pinentry-based passphrase dialog called from the
+.BR gpg-agent (1)
+daemon. It is not intended to be invoked directly.
+
+
+.SH SYNOPSIS
+Set the 
+.B pinentry-program
+in
+.IR ~/.gnupg/gpg-agent.conf
+to
+.B pinentry-dmenu
+to use the program as the regular dialog for
+.BR gpg-agent .
+.PP
+The configuration is placed in
+.IR ~/.gnupg/pinentry-dmenu.conf .
+You can change the path to the config file with the environment variable
+.IR GNUPGHOME .
+
+
+.SH OPTIONS
+.TP
+.BI "asterisk =" " *"
+Defines the symbol which is showed for each typed character.
+.TP
+.BI "bottom =" " false"
+pinentry-dmenu appears at the bottom of the screen.
+.TP
+.BI "min_password_length =" " 32"
+The minimal space of the password field. This value has affect to the description field after the password field.
+.TP
+.BI "height =" " 8"
+Height of pinentry-dmenu in pixels (no less than 8)
+.TP
+.BI "monitor =" " -1"
+pinentry-dmenu is displayed on the monitor number supplied. Monitor numbers are starting from 0.
+.TP
+.BI "prompt =" " """"
+Defines the prompt to be displayed to the left of the input field.
+.TP
+.BI "font =" " monospace:size=10"
+Defines the font or font set used.
+.TP
+.BI "prompt_bg =" " #bbbbbb"
+Defines the prompt background color.
+.IR #RGB ,
+.I #RRGGBB
+and X color names are supported.
+.TP
+.BI "prompt_fg =" " #222222"
+Defines the prompt foreground color.
+.TP
+.BI "normal_bg =" " #bbbbbb"
+Defines the normal background color.
+.TP
+.BI "normal_fg =" " #222222"
+Defines the normal foreground color.
+.TP
+.BI "select_bg =" " #eeeeee"
+Defines the selected background color.
+.TP
+.BI "select_fg =" " #005577"
+Defines the selected foreground color.
+.TP
+.BI "desc_bg =" " #bbbbbb"
+Defines the description background color.
+.TP
+.BI "desc_fg =" " #222222"
+Defines the description foreground color.
+.TP
+.BI "embedded =" " false"
+Embed into window.
+
+
+.SH USAGE
+pinentry-dmenu is completely controlled by the keyboard.
+.TP
+.B Return
+Confirm input
+.TP
+.B Ctrl-Return
+Confirm input
+.TP
+.B Shift\-Return
+Confirm input
+.TP
+.B Escape
+Cancel input
+.TP
+.B C\-c
+Escape
+
+.SS Confirm Mode
+.TP
+.B Down
+Right
+.TP
+.B End
+Right
+.TP
+.B Home
+Left
+.TP
+.B Next
+Right
+.TP
+.B Prior
+Left
+.TP
+.B Up
+Left
+.TP
+.B g
+Cancel input
+.TP
+.B G
+Cancel input
+.TP
+.B h
+Left
+.TP
+.B j
+Left
+.TP
+.B k
+Right
+.TP
+.B l
+Right
+.TP
+.B n
+Confirm with no
+.TP
+.B N
+Confirm with no
+.TP
+.B y
+Confirm with yes
+.TP
+.B Y
+Confirm with yes
+
+.SS Pin Mode
+.TP
+.B End
+Move cursor to the line end
+.TP
+.B Home
+Move cursor to the line begin
+.TP
+.B C\-a
+Home
+.TP
+.B C\-b
+Left
+.TP
+.B C\-d
+Delete
+.TP
+.B C\-e
+End
+.TP
+.B C\-f
+Right
+.TP
+.B C\-g
+Escape
+.TP
+.B C\-h
+Backspace
+.TP
+.B C\-k
+Delete line right
+.TP
+.B C\-u
+Delete line left
+.TP
+.B C\-v
+Paste from primary X selection
+
+
+.SH EXAMPLES
+.sp
+.if n \{
+.RS 4
+.\}
+.nf
+asterisk= "# ";
+prompt = "$";
+font = "Noto Sans UI:size=13";
+prompt_fg = "#eeeeee";
+prompt_bg = "#d9904a";
+normal_fg = "#ffffff";
+normal_bg = "#000000";
+select_fg = "#eeeeee";
+select_bg = "#d9904a";
+desc_fg = "#eeeeee";
+desc_bg = "#d9904a";
+
+
+.SH AUTHORS
+.B pinentry-dmenu
+is a fork of
+.B dmenu
+<https://tools.suckless.org/dmenu>
+and uses the api of
+.B pinentry
+, a GnuPG tool.
+.B pinentry-dmenu
+itself was written by Moritz Lüdecke <ritze@skweez.net>.
+
+
+.SH REPORTING BUGS
+Report pinentry-dmenu bugs to <BUGREPORT>
+
+
+.SH SEE ALSO
+.BR dmenu (1),
+.BR dwm (1),
+.BR gpg-agent (1)
blob - /dev/null
blob + 3086b4360be90e33ef88724728402486a52d9e1a (mode 644)
--- /dev/null
+++ pinentry-dmenu/pinentry-dmenu.c
@@ -0,0 +1,803 @@
+/* See LICENSE file for copyright and license details. */
+#include <ctype.h>
+#include <libconfig.h>
+#include <locale.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <pwd.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <X11/Xutil.h>
+#ifdef XINERAMA
+#include <X11/extensions/Xinerama.h>
+#endif
+#include <X11/Xft/Xft.h>
+
+#include "drw.h"
+#include "util.h"
+
+#include "pinentry/pinentry.h"
+#include "pinentry/memory.h"
+
+#define CONFIG_DIR "/.gnupg"
+#define CONFIG_FILE "/pinentry-dmenu.conf"
+#define INTERSECT(x, y, w, h, r) \
+		(MAX(0, MIN((x)+(w),(r).x_org+(r).width) - MAX((x),(r).x_org)) \
+		 && MAX(0, MIN((y)+(h),(r).y_org+(r).height) - MAX((y),(r).y_org)))
+#define LENGTH(X) (sizeof(X) / sizeof(X[0]))
+#define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad)
+#define MINDESCLEN 8
+
+
+enum { SchemePrompt, SchemeNormal, SchemeSelect, SchemeDesc, SchemeLast };
+enum { WinPin, WinConfirm };
+enum { Ok, NotOk, Cancel };
+enum { Nothing, Yes, No };
+
+static int bh, mw, mh;
+static int sel;
+static int promptw, pdescw;
+/* Sum of left and right padding */
+static int lrpad;
+static size_t cursor;
+static int screen;
+
+static char* pin;
+static int pin_len;
+static char* pin_repeat;
+static int pin_repeat_len;
+static int repeat;
+
+static Atom clip, utf8;
+static Display *dpy;
+static Window root, parentwin, win;
+static XIC xic;
+
+static Drw *drw;
+static Clr *scheme[SchemeLast];
+
+static int timed_out;
+static int winmode;
+pinentry_t pinentry_info;
+
+#include "config.h"
+
+static int
+drawitem(const char* text, Bool sel, int x, int y, int w) {
+	unsigned int i = (sel) ? SchemeSelect : SchemeNormal;
+
+	drw_setscheme(drw, scheme[i]);
+
+	return drw_text(drw, x, y, w, bh, lrpad / 2, text, 0);
+}
+
+static void
+grabfocus(void) {
+	Window focuswin;
+	int i, revertwin;
+
+	for (i = 0; i < 100; ++i) {
+		XGetInputFocus(dpy, &focuswin, &revertwin);
+		if (focuswin == win) {
+			return;
+		}
+		XSetInputFocus(dpy, win, RevertToParent, CurrentTime);
+		usleep(1000);
+	}
+
+	die("cannot grab focus");
+}
+
+static void
+grabkeyboard(void) {
+	int i;
+
+	if (embedded) {
+		return;
+	}
+
+	/* Try to grab keyboard,
+	 * we may have to wait for another process to ungrab */
+	for (i = 0; i < 1000; i++) {
+		if (XGrabKeyboard(dpy, DefaultRootWindow(dpy), True, GrabModeAsync,
+		                  GrabModeAsync, CurrentTime) == GrabSuccess) {
+			return;
+		}
+		usleep(1000);
+	}
+
+	die("cannot grab keyboard");
+}
+
+static size_t
+nextrune(int cursor, int inc) {
+	ssize_t n;
+
+	/* Return location of next utf8 rune in the given direction (+1 or -1) */
+	for (n = cursor + inc;
+	     n + inc >= 0 && (pin[n] & 0xc0) == 0x80;
+	     n += inc);
+
+	return n;
+}
+
+static void
+setup_pin(char* pin_ptr, int len, int reset) {
+	pin = pin_ptr;
+	pin_len = len;
+
+	if (reset) {
+		promptw = (prompt) ? TEXTW(prompt) - lrpad / 4 : 0;
+		cursor = 0;
+
+		if (pin) {
+			pin[0] = '\0';
+		}
+	}
+}
+
+static void
+insert(const char *str, ssize_t n) {
+	size_t len = strlen(pin);
+
+	// FIXME: Pinentry crashes when increasing the pin buffer the second time.
+	//        Other pinentry programs has a limited passwort field length.
+	if (len + n > pin_len - 1) {
+		if (repeat) {
+			pin_repeat_len = 2 * pin_repeat_len;
+			pin_repeat = secmem_realloc(pin_repeat, pin_repeat_len);
+			setup_pin(pin_repeat, pin_repeat_len, 0);
+			if (!pin_repeat) {
+				pin_len = 0;
+			}
+		} else {
+			if (!pinentry_setbufferlen(pinentry_info, 2 * pinentry_info->pin_len)) {
+				pin_len = 0;
+			} else {
+				setup_pin(pinentry_info->pin, pinentry_info->pin_len, 0);
+			}
+		}
+		if (pin_len == 0) {
+			printf("Error: Couldn't allocate secure memory\n");
+			return;
+		}
+	}
+
+	/* Move existing text out of the way, insert new text, and update cursor */
+	memmove(&pin[cursor + n], &pin[cursor], pin_len - cursor - MAX(n, 0));
+
+	if (n > 0) {
+		memcpy(&pin[cursor], str, n);
+	}
+
+	cursor += n;
+	pin[len + n] = '\0';
+}
+
+static void
+drawwin(void) {
+	unsigned int curpos;
+	int x = 0, fh = drw->fonts->h, pb, pbw = 0, i;
+	size_t asterlen = strlen(asterisk);
+	size_t pdesclen;
+	int leftinput;
+	char* censort;
+
+	char* pprompt = (repeat) ? pinentry_info->repeat_passphrase : pinentry_info->prompt;
+	int ppromptw = (pprompt) ? TEXTW(pprompt) : 0;
+
+	unsigned int censortl = minpwlen * TEXTW(asterisk) / strlen(asterisk);
+	unsigned int confirml = TEXTW(" YesNo ") + 3 * lrpad;
+
+	drw_setscheme(drw, scheme[SchemeNormal]);
+	drw_rect(drw, 0, 0, mw, mh, 1, 1);
+
+	if (prompt) {
+		drw_setscheme(drw, scheme[SchemePrompt]);
+		x = drw_text(drw, x, 0, promptw, bh, lrpad / 2, prompt, 0);
+	}
+
+	if (pprompt) {
+		drw_setscheme(drw, scheme[SchemePrompt]);
+		drw_text(drw, x, 0, ppromptw, bh, lrpad / 2, pprompt, 0);
+		x += ppromptw;
+	}
+
+	if (pinentry_info->description) {
+		pb = mw - x;
+		pdesclen = strlen(pinentry_info->description);
+
+		if (pb > 0) {
+			pb -= (winmode == WinPin) ? censortl : confirml;
+			pbw = MINDESCLEN * pdescw / pdesclen;
+			pbw = MIN(pbw, pdescw);
+
+			if (pb >= pbw) {
+				pbw = MAX(pbw, pdescw);
+				pbw = MIN(pbw, pb);
+				pb = mw - pbw;
+
+				for (i = 0; i < pdesclen; i++) {
+					if (pinentry_info->description[i] == '\n') {
+						pinentry_info->description[i] = ' ';
+					}
+				}
+
+				drw_setscheme(drw, scheme[SchemeDesc]);
+				drw_text(drw, pb, 0, pbw, bh, lrpad / 2, pinentry_info->description,
+				         0);
+			} else {
+				pbw = 0;
+			}
+		}
+	}
+
+	/* Draw input field */
+	drw_setscheme(drw, scheme[SchemeNormal]);
+
+	if (winmode == WinPin) {
+		censort = ecalloc(1, asterlen * pin_len);
+
+		for (i = 0; i < asterlen * strlen(pin); i += asterlen) {
+			memcpy(&censort[i], asterisk, asterlen);
+		}
+
+		censort[i+1] = '\n';
+		leftinput = mw - x - pbw;
+		drw_text(drw, x, 0, leftinput, bh, lrpad / 2, censort, 0);
+		drw_font_getexts(drw->fonts, censort, cursor * asterlen, &curpos, NULL);
+
+		if ((curpos += lrpad / 2 - 1) < leftinput) {
+			drw_setscheme(drw, scheme[SchemeNormal]);
+			drw_rect(drw, x + curpos, 2 + (bh - fh) / 2, 2, fh - 4, 1, 0);
+		}
+
+		free(censort);
+	} else {
+		x += TEXTW(" ");
+		x = drawitem("No", (sel == No), x, 0, TEXTW("No"));
+		x = drawitem("Yes", (sel == Yes), x, 0, TEXTW("Yes"));
+	}
+
+	drw_map(drw, win, 0, 0, mw, mh);
+}
+
+static void
+setup(void) {
+	int x, y, i = 0;
+	unsigned int du;
+	XSetWindowAttributes swa;
+	XIM xim;
+	Window w, dw, *dws;
+	XWindowAttributes wa;
+#ifdef XINERAMA
+	XineramaScreenInfo *info;
+	Window pw;
+	int a, j, di, n, area = 0;
+#endif
+
+	/* Init appearance */
+	scheme[SchemePrompt] = drw_scm_create(drw, colors[SchemePrompt], 2);
+	scheme[SchemeNormal] = drw_scm_create(drw, colors[SchemeNormal], 2);
+	scheme[SchemeSelect] = drw_scm_create(drw, colors[SchemeSelect], 2);
+	scheme[SchemeDesc]   = drw_scm_create(drw, colors[SchemeDesc],   2);
+
+	clip = XInternAtom(dpy, "CLIPBOARD",   False);
+	utf8 = XInternAtom(dpy, "UTF8_STRING", False);
+
+	/* Calculate menu geometry */
+	bh = drw->fonts->h + 2;
+	bh = MAX(bh, lineheight);
+	mh = bh;
+#ifdef XINERAMA
+	info = XineramaQueryScreens(dpy, &n);
+
+	if (parentwin == root && info) {
+		XGetInputFocus(dpy, &w, &di);
+		if (mon >= 0 && mon < n) {
+			i = mon;
+		} else if (w != root && w != PointerRoot && w != None) {
+			/* Find top-level window containing current input focus */
+			do {
+				if (XQueryTree(dpy, (pw = w), &dw, &w, &dws, &du) && dws) {
+					XFree(dws);
+				}
+			} while (w != root && w != pw);
+			/* Find xinerama screen with which the window intersects most */
+			if (XGetWindowAttributes(dpy, pw, &wa)) {
+				for (j = 0; j < n; j++) {
+					a = INTERSECT(wa.x, wa.y, wa.width, wa.height, info[j]);
+					if (a > area) {
+						area = a;
+						i = j;
+					}
+				}
+			}
+		}
+		/* No focused window is on screen, so use pointer location instead */
+		if (mon < 0 && !area
+		    && XQueryPointer(dpy, root, &dw, &dw, &x, &y, &di, &di, &du)) {
+			for (i = 0; i < n; i++) {
+				if (INTERSECT(x, y, 1, 1, info[i])) {
+					break;
+				}
+			}
+		}
+
+		x = info[i].x_org;
+		y = info[i].y_org + (bottom ? info[i].height - mh : 0);
+		mw = info[i].width;
+		XFree(info);
+	} else
+#endif
+	{
+		if (!XGetWindowAttributes(dpy, parentwin, &wa)) {
+			die("could not get embedding window attributes: 0x%lx", parentwin);
+		}
+		x = 0;
+		y = bottom ? wa.height - mh : 0;
+		mw = wa.width;
+	}
+
+	pdescw = (pinentry_info->description) ? TEXTW(pinentry_info->description) : 0;
+
+	/* Create menu window */
+	swa.override_redirect = True;
+	swa.background_pixel = scheme[SchemePrompt][ColBg].pixel;
+	swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask;
+	win = XCreateWindow(dpy, parentwin, x, y, mw, mh, 0,
+	                    CopyFromParent, CopyFromParent, CopyFromParent,
+	                    CWOverrideRedirect | CWBackPixel | CWEventMask, &swa);
+
+	/* Open input methods */
+	xim = XOpenIM(dpy, NULL, NULL, NULL);
+	xic = XCreateIC(xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
+	                XNClientWindow, win, XNFocusWindow, win, NULL);
+	XMapRaised(dpy, win);
+
+	if (embedded) {
+		XSelectInput(dpy, parentwin, FocusChangeMask);
+
+		if (XQueryTree(dpy, parentwin, &dw, &w, &dws, &du) && dws) {
+			for (i = 0; i < du && dws[i] != win; ++i) {
+				XSelectInput(dpy, dws[i], FocusChangeMask);
+			}
+
+			XFree(dws);
+		}
+		grabfocus();
+	}
+
+	drw_resize(drw, mw, mh);
+}
+
+static void
+cleanup(void) {
+	XUngrabKey(dpy, AnyKey, AnyModifier, root);
+	free(scheme[SchemeDesc]);
+	free(scheme[SchemeSelect]);
+	free(scheme[SchemeNormal]);
+	free(scheme[SchemePrompt]);
+	drw_free(drw);
+	XSync(dpy, False);
+	XCloseDisplay(dpy);
+}
+
+static int
+keypress_confirm(XKeyEvent *ev, KeySym ksym) {
+	if (ev->state & ControlMask) {
+		switch(ksym) {
+		case XK_c:
+			pinentry_info->canceled = 1;
+			sel = No;
+			return 1;
+		default:
+			return 1;
+		}
+	}
+
+	switch(ksym) {
+	case XK_KP_Enter:
+	case XK_Return:
+		if (sel != Nothing) {
+			return 1;
+		}
+		break;
+	case XK_y:
+	case XK_Y:
+		sel = Yes;
+		return 1;
+	case XK_n:
+	case XK_N:
+		sel = No;
+		return 1;
+	case XK_g:
+	case XK_G:
+	case XK_Escape:
+		pinentry_info->canceled = 1;
+		sel = No;
+		return 1;
+	case XK_h:
+	case XK_j:
+	case XK_Home:
+	case XK_Left:
+	case XK_Prior:
+	case XK_Up:
+		sel = No;
+		break;
+	case XK_k:
+	case XK_l:
+	case XK_Down:
+	case XK_End:
+	case XK_Next:
+	case XK_Right:
+		sel = Yes;
+		break;
+	}
+
+	return 0;
+}
+
+static int
+keypress_pin(XKeyEvent *ev, KeySym ksym, char* buf, int len) {
+	int old;
+
+	if (ev->state & ControlMask) {
+		switch(ksym) {
+		case XK_a: ksym = XK_Home;      break;
+		case XK_b: ksym = XK_Left;      break;
+		case XK_c: ksym = XK_Escape;    break;
+		case XK_d: ksym = XK_Delete;    break;
+		case XK_e: ksym = XK_End;       break;
+		case XK_f: ksym = XK_Right;     break;
+		case XK_g: ksym = XK_Escape;    break;
+		case XK_h: ksym = XK_BackSpace; break;
+		case XK_k:
+			old = cursor;
+			cursor = strlen(pin);
+			insert(NULL, old - cursor);
+			break;
+		case XK_u:
+			insert(NULL, -cursor);
+			break;
+		case XK_v:
+			XConvertSelection(dpy, (ev->state & ShiftMask) ? clip : XA_PRIMARY,
+			                  utf8, utf8, win, CurrentTime);
+			return 0;
+		case XK_Return:
+		case XK_KP_Enter:
+			break;
+		case XK_bracketleft:
+			pinentry_info->canceled = 1;
+			return 1;
+		default:
+			return 1;
+		}
+	}
+
+	switch(ksym) {
+	case XK_Delete:
+		if (pin[cursor] == '\0') {
+			return 0;
+		}
+		cursor = nextrune(cursor, +1);
+		/* Fallthrough */
+	case XK_BackSpace:
+		if (cursor == 0) {
+			return 0;
+		}
+		insert(NULL, nextrune(cursor, -1) - cursor);
+		break;
+	case XK_Escape:
+		pinentry_info->canceled = 1;
+		return 1;
+	case XK_Left:
+		if (cursor > 0) {
+			cursor = nextrune(cursor, -1);
+		}
+		break;
+	case XK_Right:
+		if (pin[cursor] != '\0') {
+			cursor = nextrune(cursor, +1);
+		}
+		break;
+	case XK_Home:
+		cursor = 0;
+		break;
+	case XK_End:
+		cursor = strlen(pin);
+		break;
+	case XK_Return:
+	case XK_KP_Enter:
+		return 1;
+		break;
+	default:
+		if (!iscntrl(*buf)) {
+			insert(buf, len);
+		}
+	}
+
+	return 0;
+}
+
+static int
+keypress(XKeyEvent *ev) {
+	char buf[32];
+	int len;
+	int ret = 1;
+
+	KeySym ksym = NoSymbol;
+	Status status;
+	len = XmbLookupString(xic, ev, buf, sizeof(buf), &ksym, &status);
+
+	if (status != XBufferOverflow) {
+		if (winmode == WinConfirm) {
+			ret = keypress_confirm(ev, ksym);
+		} else {
+			ret = keypress_pin(ev, ksym, buf, len);
+		}
+
+		if (ret == 0) {
+			drawwin();
+		}
+	}
+
+	return ret;
+}
+
+static void
+paste(void) {
+	char *p, *q;
+	int di;
+	unsigned long dl;
+	Atom da;
+
+	/* We have been given the current selection, now insert it into input */
+	XGetWindowProperty(dpy, win, utf8, 0, pin_len / 4, False, utf8, &da, &di,
+	                   &dl, &dl, (unsigned char **)&p);
+	insert(p, (q = strchr(p, '\n')) ? q - p : (ssize_t) strlen(p));
+	XFree(p);
+	drawwin();
+}
+
+void
+run(void) {
+	XEvent ev;
+
+	drawwin();
+
+	while (!XNextEvent(dpy, &ev)) {
+		if (XFilterEvent(&ev, win)) {
+			continue;
+		}
+		switch(ev.type) {
+		case Expose:
+			if (ev.xexpose.count == 0) {
+				drw_map(drw, win, 0, 0, mw, mh);
+			}
+			break;
+		case KeyPress:
+			if (keypress(&ev.xkey)) {
+				return;
+			}
+			break;
+		case SelectionNotify:
+			if (ev.xselection.property == utf8) {
+				paste();
+			}
+			break;
+		case VisibilityNotify:
+			if (ev.xvisibility.state != VisibilityUnobscured) {
+				XRaiseWindow(dpy, win);
+			}
+			break;
+		}
+	}
+}
+
+static void
+catchsig(int sig) {
+	if (sig == SIGALRM) {
+		timed_out = 1;
+	}
+}
+
+static void
+password(void) {
+	winmode = WinPin;
+	repeat = 0;
+	setup_pin(pinentry_info->pin, pinentry_info->pin_len, 1);
+	run();
+
+	if (!pinentry_info->canceled && pinentry_info->repeat_passphrase) {
+		repeat = 1;
+		pin_repeat_len = pinentry_info->pin_len;
+		pin_repeat = secmem_malloc(pinentry_info->pin_len);
+		setup_pin(pin_repeat, pin_repeat_len, 1);
+		run();
+
+		pinentry_info->repeat_okay = (strcmp(pinentry_info->pin, pin_repeat) == 0)? 1 : 0;
+		secmem_free(pin_repeat);
+
+		if (!pinentry_info->repeat_okay) {
+			pinentry_info->result = -1;
+			return;
+		}
+	}
+
+	if (pinentry_info->canceled) {
+		pinentry_info->result = -1;
+		return;
+	}
+
+	pinentry_info->result = strlen(pinentry_info->pin);
+}
+
+static void
+confirm(void) {
+	winmode = WinConfirm;
+	sel = Nothing;
+	run();
+	pinentry_info->result = sel != No;
+}
+
+static int
+cmdhandler(pinentry_t received_pinentry) {
+	struct sigaction sa;
+	XWindowAttributes wa;
+
+	pinentry_info = received_pinentry;
+
+	if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) {
+		fputs("warning: no locale support\n", stderr);
+	}
+	if (!(dpy = XOpenDisplay(pinentry_info->display))) {
+		die("cannot open display");
+	}
+	screen = DefaultScreen(dpy);
+	root = RootWindow(dpy, screen);
+	embedded = (pinentry_info->parent_wid) ? embedded : 0;
+	parentwin = (embedded) ? pinentry_info->parent_wid : root;
+	if (!XGetWindowAttributes(dpy, parentwin, &wa)) {
+		die("could not get embedding window attributes: 0x%lx", parentwin);
+	}
+	drw = drw_create(dpy, screen, root, wa.width, wa.height);
+	if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) {
+		die("no fonts could be loaded.");
+	}
+	lrpad = drw->fonts->h;
+	drw_setscheme(drw, scheme[SchemePrompt]);
+
+	if (pinentry_info->timeout) {
+		memset(&sa, 0, sizeof(sa));
+		sa.sa_handler = catchsig;
+		sigaction(SIGALRM, &sa, NULL);
+		alarm(pinentry_info->timeout);
+	}
+
+	grabkeyboard();
+	setup();
+
+	if (pinentry_info->pin) {
+		do {
+			password();
+		} while (!pinentry_info->canceled && pinentry_info->repeat_passphrase
+		                             && !pinentry_info->repeat_okay);
+	} else {
+		confirm();
+	}
+
+	cleanup();
+
+	return pinentry_info->result;
+}
+
+pinentry_cmd_handler_t pinentry_cmd_handler = cmdhandler;
+
+int
+main(int argc, char *argv[]) {
+	Bool bval;
+	int i, val;
+	const char *str;
+	struct passwd *pw;
+	char path[PATH_MAX];
+	char *sudo_uid = getenv("SUDO_UID");
+	char *home = getenv("HOME");
+	char *gnupghome = getenv("GNUPGHOME");
+	config_t cfg;
+
+	if (gnupghome) {
+		i = strlen(gnupghome);
+		strcpy(path, gnupghome);
+	} else {
+		/* Get the home dir even if the user used sudo or logged in as root */
+		if (sudo_uid) {
+			i = atoi(sudo_uid);
+			pw = getpwuid(i);
+			home = pw->pw_dir;
+		}
+
+		i = strlen(home);
+		strcpy(path, home);
+		strcpy(&path[i], CONFIG_DIR);
+		i += strlen(CONFIG_DIR);
+	}
+
+	strcpy(&path[i], CONFIG_FILE);
+	endpwent();
+
+	config_init(&cfg);
+
+	/* Read the file. If there is an error, report it and exit. */
+	if (config_read_file(&cfg, path)) {
+		if (config_lookup_string(&cfg, "asterisk", &str)) {
+			asterisk = str;
+		}
+		if (config_lookup_bool(&cfg, "bottom", &bval)) {
+			bottom = bval;
+		}
+		if (config_lookup_int(&cfg, "min_password_length", &val)) {
+			minpwlen = val;
+		}
+		if (config_lookup_int(&cfg, "height", &val)) {
+			lineheight = MAX(val, min_lineheight);
+		}
+		if (config_lookup_int(&cfg, "monitor", &val)) {
+			mon = val;
+		}
+		if (config_lookup_string(&cfg, "prompt", &str)) {
+			prompt = str;
+		}
+		if (config_lookup_string(&cfg, "font", &str)) {
+			fonts[0] = str;
+		}
+		if (config_lookup_string(&cfg, "prompt_bg", &str)) {
+			colors[SchemePrompt][ColBg] = str;
+		}
+		if (config_lookup_string(&cfg, "prompt_fg", &str)) {
+			colors[SchemePrompt][ColFg] = str;
+		}
+		if (config_lookup_string(&cfg, "normal_bg", &str)) {
+			colors[SchemeNormal][ColBg] = str;
+		}
+		if (config_lookup_string(&cfg, "normal_fg", &str)) {
+			colors[SchemeNormal][ColFg] = str;
+		}
+		if (config_lookup_string(&cfg, "select_bg", &str)) {
+			colors[SchemeSelect][ColBg] = str;
+		}
+		if (config_lookup_string(&cfg, "select_fg", &str)) {
+			colors[SchemeSelect][ColFg] = str;
+		}
+		if (config_lookup_string(&cfg, "desc_bg", &str)) {
+			colors[SchemeDesc][ColBg] = str;
+		}
+		if (config_lookup_string(&cfg, "desc_fg", &str)) {
+			colors[SchemeDesc][ColFg] = str;
+		}
+		if (config_lookup_bool(&cfg, "embedded", &bval)) {
+			embedded = bval;
+		}
+	} else if ((str = config_error_file(&cfg))) {
+		fprintf(stderr, "%s:%d: %s\n", config_error_file(&cfg),
+		        config_error_line(&cfg), config_error_text(&cfg));
+		return(EXIT_FAILURE);
+	}
+
+	pinentry_init("pinentry-dmenu");
+	pinentry_parse_opts(argc, argv);
+
+	if (pinentry_loop()) {
+		return 1;
+	}
+
+	config_destroy(&cfg);
+
+	return 0;
+}
blob - /dev/null
blob + fe044fc7b7978d1f3c23768e315aae415d90b11a (mode 644)
--- /dev/null
+++ pinentry-dmenu/util.c
@@ -0,0 +1,35 @@
+/* See LICENSE file for copyright and license details. */
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "util.h"
+
+void *
+ecalloc(size_t nmemb, size_t size)
+{
+	void *p;
+
+	if (!(p = calloc(nmemb, size)))
+		die("calloc:");
+	return p;
+}
+
+void
+die(const char *fmt, ...) {
+	va_list ap;
+
+	va_start(ap, fmt);
+	vfprintf(stderr, fmt, ap);
+	va_end(ap);
+
+	if (fmt[0] && fmt[strlen(fmt)-1] == ':') {
+		fputc(' ', stderr);
+		perror(NULL);
+	} else {
+		fputc('\n', stderr);
+	}
+
+	exit(1);
+}
blob - /dev/null
blob + f633b5173ad2b5058d48574d5a18fe3f135c4193 (mode 644)
--- /dev/null
+++ pinentry-dmenu/util.h
@@ -0,0 +1,8 @@
+/* See LICENSE file for copyright and license details. */
+
+#define MAX(A, B)               ((A) > (B) ? (A) : (B))
+#define MIN(A, B)               ((A) < (B) ? (A) : (B))
+#define BETWEEN(X, A, B)        ((A) <= (X) && (X) <= (B))
+
+void die(const char *fmt, ...);
+void *ecalloc(size_t nmemb, size_t size);