Commit: 51da5915b03d9b06936ac32f47287beb73d96973
Author: zavok
Date: Mon, 23 Nov 2015 17:08:20 +0300
This is a "barely works" version of spine, I hope it works, at least barely.
Diffstat:
37 files changed, 5736 insertions(+), 0 deletions(-)
diff --git a/LICENSE b/LICENSE
@@ -0,0 +1,28 @@
+MIT/X Consortium License
+
+© 2006-2014 Anselm R Garbe <anselm@garbe.us>
+© 2010-2012 Connor Lane Smith <cls@lubutu.com>
+© 2009 Gottox <gottox@s01.de>
+© 2009 Markus Schnalke <meillo@marmaro.de>
+© 2009 Evan Gates <evan.gates@gmail.com>
+© 2006-2008 Sander van Dijk <a dot h dot vandijk at gmail dot com>
+© 2006-2007 Michał Janeczek <janeczek at gmail dot com>
+© 2014-2015 Hiltjo Posthuma <hiltjo@codemadness.org>
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/Makefile b/Makefile
@@ -0,0 +1,35 @@
+# spine - dmenu-like stupid pin entry
+# See LICENSE file for copyright and license details.
+
+include config.mk
+
+SRC = spine.c drw.c util.c
+OBJ = ${SRC:.c=.o}
+
+all: options spine
+
+options:
+ @echo spine build options:
+ @echo "CFLAGS = ${CFLAGS}"
+ @echo "LDFLAGS = ${LDFLAGS}"
+ @echo "CC = ${CC}"
+
+.c.o:
+ @echo CC $<
+ @${CC} -c ${CFLAGS} $<
+
+config.h:
+ @echo creating $@ from config.def.h
+ @cp config.def.h $@
+
+${OBJ}: config.h config.mk drw.h
+
+spine: spine.o drw.o util.o
+ @echo CC -o $@
+ @${CC} -o $@ spine.o drw.o util.o pinentry/pinentry.o pinentry/util.o pinentry/password-cache.o pinentry/argparse.o pinentry/secmem.o ${LDFLAGS} -lassuan -lgpgme -lgpg-error
+
+clean:
+ @echo cleaning
+ @rm -f spine ${OBJ}
+
+.PHONY: all options clean
diff --git a/README b/README
@@ -0,0 +1,5 @@
+spine - stupid pinentry replacement
+
+I didn't like how long it takes to load pinentry-gtk/qt and pinentry-curses/tty is not always the option, so I duct-taped pinentry and dmenu together.
+
+Right now it's a real mess, so don't expect much, but I w-will work on it some more I promise.
diff --git a/TODO b/TODO
@@ -0,0 +1,3 @@
+* replace pinentry's code with our own talker to gpg-agent
+* look over code and see what I broke while copypasting code from dmenu
+* get licenses in order
diff --git a/arg.h b/arg.h
@@ -0,0 +1,41 @@
+/*
+ * Copy me if you can.
+ * by 20h
+ */
+
+#ifndef __ARG_H__
+#define __ARG_H__
+
+extern char *argv0;
+
+#define USED(x) ((void)(x))
+
+#define ARGBEGIN for (argv0 = *argv, argv++, argc--;\
+ argv[0] && argv[0][1]\
+ && argv[0][0] == '-';\
+ argc--, argv++) {\
+ char _argc;\
+ char **_argv;\
+ if (argv[0][1] == '-' && argv[0][2] == '\0') {\
+ argv++;\
+ argc--;\
+ break;\
+ }\
+ for (argv[0]++, _argv = argv; argv[0][0];\
+ argv[0]++) {\
+ if (_argv != argv)\
+ break;\
+ _argc = argv[0][0];\
+ switch (_argc)
+
+#define ARGEND }\
+ USED(_argc);\
+ }\
+ USED(argv);\
+ USED(argc);
+
+#define EARGF(x) ((argv[1] == NULL)? ((x), abort(), (char *)0) :\
+ (argc--, argv++, argv[0]))
+
+#endif
+
diff --git a/config.h b/config.h
@@ -0,0 +1,12 @@
+/* See LICENSE file for copyright and license details. */
+static Bool topbar = True;
+static const char *fonts[]={
+ "monospace:size=8"
+};
+static char *secchar = "*";
+static char *description = NULL;
+static char *prompt = "PIN:";
+static const char *normbgcolor = "#000000";
+static const char *normfgcolor = "#ffffff";
+static const char *selbgcolor = "#ff0000";
+static const char *selfgcolor = "#ffffff";
diff --git a/config.mk b/config.mk
@@ -0,0 +1,31 @@
+# spine version
+VERSION = 0.1
+
+# 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_BSD_SOURCE -D_POSIX_C_SOURCE=200809L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS}
+CFLAGS = -ansi -pedantic -Wall -Os ${INCS} ${CPPFLAGS}
+LDFLAGS = -s ${LIBS}
+
+# compiler and linker
+CC = cc
diff --git a/drw.c b/drw.c
@@ -0,0 +1,413 @@
+/* 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 != 0)
+ 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 = (Drw *)calloc(1, sizeof(Drw));
+ if(!drw)
+ return NULL;
+ 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);
+ drw->fontcount = 0;
+ 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 != 0)
+ XFreePixmap(drw->dpy, drw->drawable);
+ drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, DefaultDepth(drw->dpy, drw->screen));
+}
+
+void
+drw_free(Drw *drw) {
+ size_t i;
+
+ for (i = 0; i < drw->fontcount; i++) {
+ drw_font_free(drw->fonts[i]);
+ }
+ XFreePixmap(drw->dpy, drw->drawable);
+ XFreeGC(drw->dpy, drw->gc);
+ free(drw);
+}
+
+/* This function is an implementation detail. Library users should use
+ * drw_font_create instead.
+ */
+static Fnt *
+drw_font_xcreate(Drw *drw, const char *fontname, FcPattern *fontpattern) {
+ Fnt *font;
+
+ if (!(fontname || fontpattern))
+ die("No font specified.\n");
+
+ if (!(font = (Fnt *)calloc(1, sizeof(Fnt))))
+ return NULL;
+
+ if (fontname) {
+ /* Using the pattern found at font->xfont->pattern does not yield same
+ * 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 (!(font->xfont = XftFontOpenName(drw->dpy, drw->screen, fontname)) ||
+ !(font->pattern = FcNameParse((FcChar8 *) fontname))) {
+ if (font->xfont) {
+ XftFontClose(drw->dpy, font->xfont);
+ font->xfont = NULL;
+ }
+ fprintf(stderr, "error, cannot load font: '%s'\n", fontname);
+ }
+ } else if (fontpattern) {
+ if (!(font->xfont = XftFontOpenPattern(drw->dpy, fontpattern))) {
+ fprintf(stderr, "error, cannot load font pattern.\n");
+ } else {
+ font->pattern = NULL;
+ }
+ }
+
+ if (!font->xfont) {
+ free(font);
+ return NULL;
+ }
+
+ font->ascent = font->xfont->ascent;
+ font->descent = font->xfont->descent;
+ font->h = font->ascent + font->descent;
+ font->dpy = drw->dpy;
+ return font;
+}
+
+Fnt*
+drw_font_create(Drw *drw, const char *fontname) {
+ return drw_font_xcreate(drw, fontname, NULL);
+}
+
+void
+drw_load_fonts(Drw* drw, const char *fonts[], size_t fontcount) {
+ size_t i;
+ Fnt *font;
+
+ for (i = 0; i < fontcount; i++) {
+ if (drw->fontcount >= DRW_FONT_CACHE_SIZE) {
+ die("Font cache exhausted.\n");
+ } else if ((font = drw_font_xcreate(drw, fonts[i], NULL))) {
+ drw->fonts[drw->fontcount++] = font;
+ }
+ }
+}
+
+void
+drw_font_free(Fnt *font) {
+ if(!font)
+ return;
+ if(font->pattern)
+ FcPatternDestroy(font->pattern);
+ XftFontClose(font->dpy, font->xfont);
+ free(font);
+}
+
+Clr *
+drw_clr_create(Drw *drw, const char *clrname) {
+ Clr *clr;
+ Colormap cmap;
+ Visual *vis;
+
+ if(!drw)
+ return NULL;
+ clr = (Clr *)calloc(1, sizeof(Clr));
+ if(!clr)
+ return NULL;
+ cmap = DefaultColormap(drw->dpy, drw->screen);
+ vis = DefaultVisual(drw->dpy, drw->screen);
+ if(!XftColorAllocName(drw->dpy, vis, cmap, clrname, &clr->rgb))
+ die("error, cannot allocate color '%s'\n", clrname);
+ clr->pix = clr->rgb.pixel;
+ return clr;
+}
+
+void
+drw_clr_free(Clr *clr) {
+ free(clr);
+}
+
+void
+drw_setscheme(Drw *drw, ClrScheme *scheme) {
+ if(!drw)
+ return;
+ drw->scheme = scheme;
+}
+
+void
+drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int empty, int invert) {
+ if(!drw || !drw->scheme)
+ return;
+ XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme->bg->pix : drw->scheme->fg->pix);
+ if(filled)
+ XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w + 1, h + 1);
+ else if(empty)
+ XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
+}
+
+int
+drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, const char *text, int invert) {
+ char buf[1024];
+ int tx, ty, th;
+ Extnts tex;
+ Colormap cmap;
+ Visual *vis;
+ XftDraw *d;
+ Fnt *curfont, *nextfont;
+ size_t i, len;
+ int utf8strlen, utf8charlen, render;
+ long utf8codepoint = 0;
+ const char *utf8str;
+ FcCharSet *fccharset;
+ FcPattern *fcpattern;
+ FcPattern *match;
+ XftResult result;
+ int charexists = 0;
+
+ if (!(render = x || y || w || h)) {
+ w = ~w;
+ }
+
+ if (!drw || !drw->scheme) {
+ return 0;
+ } else if (render) {
+ XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme->fg->pix : drw->scheme->bg->pix);
+ XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
+ }
+
+ if (!text || !drw->fontcount) {
+ return 0;
+ } else if (render) {
+ cmap = DefaultColormap(drw->dpy, drw->screen);
+ vis = DefaultVisual(drw->dpy, drw->screen);
+ d = XftDrawCreate(drw->dpy, drw->drawable, vis, cmap);
+ }
+
+ curfont = drw->fonts[0];
+ while (1) {
+ utf8strlen = 0;
+ utf8str = text;
+ nextfont = NULL;
+ while (*text) {
+ utf8charlen = utf8decode(text, &utf8codepoint, UTF_SIZ);
+ for (i = 0; i < drw->fontcount; i++) {
+ charexists = charexists || XftCharExists(drw->dpy, drw->fonts[i]->xfont, utf8codepoint);
+ if (charexists) {
+ if (drw->fonts[i] == curfont) {
+ utf8strlen += utf8charlen;
+ text += utf8charlen;
+ } else {
+ nextfont = drw->fonts[i];
+ }
+ break;
+ }
+ }
+
+ if (!charexists || (nextfont && nextfont != curfont)) {
+ break;
+ } else {
+ charexists = 0;
+ }
+ }
+
+ if (utf8strlen) {
+ drw_font_getexts(curfont, utf8str, utf8strlen, &tex);
+ /* shorten text if necessary */
+ for(len = MIN(utf8strlen, (sizeof buf) - 1); len && (tex.w > w - drw->fonts[0]->h || w < drw->fonts[0]->h); len--)
+ drw_font_getexts(curfont, utf8str, len, &tex);
+
+ if (len) {
+ memcpy(buf, utf8str, len);
+ buf[len] = '\0';
+ if(len < utf8strlen)
+ for(i = len; i && i > len - 3; buf[--i] = '.');
+
+ if (render) {
+ th = curfont->ascent + curfont->descent;
+ ty = y + (h / 2) - (th / 2) + curfont->ascent;
+ tx = x + (h / 2);
+ XftDrawStringUtf8(d, invert ? &drw->scheme->bg->rgb : &drw->scheme->fg->rgb, curfont->xfont, tx, ty, (XftChar8 *)buf, len);
+ }
+
+ x += tex.w;
+ w -= tex.w;
+ }
+ }
+
+ if (!*text) {
+ break;
+ } else if (nextfont) {
+ charexists = 0;
+ curfont = nextfont;
+ } else {
+ /* Regardless of whether or not a fallback font is found, the
+ * character must be drawn.
+ */
+ charexists = 1;
+
+ if (drw->fontcount >= DRW_FONT_CACHE_SIZE) {
+ continue;
+ }
+
+ fccharset = FcCharSetCreate();
+ FcCharSetAddChar(fccharset, utf8codepoint);
+
+ if (!drw->fonts[0]->pattern) {
+ /* Refer to the comment in drw_font_xcreate for more
+ * information.
+ */
+ die("The first font in the cache must be loaded from a font string.\n");
+ }
+
+ fcpattern = FcPatternDuplicate(drw->fonts[0]->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) {
+ curfont = drw_font_xcreate(drw, NULL, match);
+ if (curfont && XftCharExists(drw->dpy, curfont->xfont, utf8codepoint)) {
+ drw->fonts[drw->fontcount++] = curfont;
+ } else {
+ if (curfont) {
+ drw_font_free(curfont);
+ }
+ curfont = drw->fonts[0];
+ }
+ }
+ }
+ }
+
+ if (render) {
+ XftDrawDestroy(d);
+ }
+
+ return x;
+}
+
+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);
+}
+
+
+void
+drw_font_getexts(Fnt *font, const char *text, unsigned int len, Extnts *tex) {
+ XGlyphInfo ext;
+
+ if(!font || !text)
+ return;
+ XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext);
+ tex->h = font->h;
+ tex->w = ext.xOff;
+}
+
+unsigned int
+drw_font_getexts_width(Fnt *font, const char *text, unsigned int len) {
+ Extnts tex;
+
+ if(!font)
+ return -1;
+ drw_font_getexts(font, text, len, &tex);
+ return tex.w;
+}
+
+Cur *
+drw_cur_create(Drw *drw, int shape) {
+ Cur *cur;
+
+ if(!drw)
+ return NULL;
+ cur = (Cur *)calloc(1, sizeof(Cur));
+ if (!cur)
+ return NULL;
+ cur->cursor = XCreateFontCursor(drw->dpy, shape);
+ return cur;
+}
+
+void
+drw_cur_free(Drw *drw, Cur *cursor) {
+ if(!drw || !cursor)
+ return;
+ XFreeCursor(drw->dpy, cursor->cursor);
+ free(cursor);
+}
diff --git a/drw.h b/drw.h
@@ -0,0 +1,74 @@
+/* See LICENSE file for copyright and license details. */
+#define DRW_FONT_CACHE_SIZE 32
+
+typedef struct {
+ unsigned long pix;
+ XftColor rgb;
+} Clr;
+
+typedef struct {
+ Cursor cursor;
+} Cur;
+
+typedef struct {
+ Display *dpy;
+ int ascent;
+ int descent;
+ unsigned int h;
+ XftFont *xfont;
+ FcPattern *pattern;
+} Fnt;
+
+typedef struct {
+ Clr *fg;
+ Clr *bg;
+ Clr *border;
+} ClrScheme;
+
+typedef struct {
+ unsigned int w, h;
+ Display *dpy;
+ int screen;
+ Window root;
+ Drawable drawable;
+ GC gc;
+ ClrScheme *scheme;
+ size_t fontcount;
+ Fnt *fonts[DRW_FONT_CACHE_SIZE];
+} Drw;
+
+typedef struct {
+ unsigned int w;
+ unsigned int h;
+} Extnts;
+
+/* 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_font_create(Drw *drw, const char *fontname);
+void drw_load_fonts(Drw* drw, const char *fonts[], size_t fontcount);
+void drw_font_free(Fnt *font);
+void drw_font_getexts(Fnt *font, const char *text, unsigned int len, Extnts *extnts);
+unsigned int drw_font_getexts_width(Fnt *font, const char *text, unsigned int len);
+
+/* Colour abstraction */
+Clr *drw_clr_create(Drw *drw, const char *clrname);
+void drw_clr_free(Clr *clr);
+
+/* Cursor abstraction */
+Cur *drw_cur_create(Drw *drw, int shape);
+void drw_cur_free(Drw *drw, Cur *cursor);
+
+/* Drawing context manipulation */
+void drw_setfont(Drw *drw, Fnt *font);
+void drw_setscheme(Drw *drw, ClrScheme *scheme);
+
+/* Drawing functions */
+void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int empty, int invert);
+int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, const char *text, int invert);
+
+/* Map functions */
+void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h);
diff --git a/drw.o b/drw.o
Binary files differ.
diff --git a/pinentry/AUTHORS b/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>
diff --git a/pinentry/COPYING b/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
diff --git a/pinentry/Makefile b/pinentry/Makefile
@@ -0,0 +1,20 @@
+include ../config.mk
+
+SRC = pinentry.c argparse.c password-cache.c
+OBJ = ${SRC:.c=.o}
+
+all: pinentry
+
+.c.o:
+ @echo CC $<
+ @${CC} -c ${CFLAGS} $<
+
+${OBJ}: pinentry.h argparse.h password-cache.h memory.h
+
+pinentry: pinentry.o argparse.o password-cache.o secmem.o
+
+clean:
+ @echo cleaning
+ @rm -f ${OBJ}
+
+.PHONY: all clean pinentry
diff --git a/pinentry/argparse.c b/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 ()
+{
+ 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 ****/
diff --git a/pinentry/argparse.h b/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*/
diff --git a/pinentry/argparse.o b/pinentry/argparse.o
Binary files differ.
diff --git a/pinentry/memory.h b/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 */
diff --git a/pinentry/password-cache.c b/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
+}
diff --git a/pinentry/password-cache.h b/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
diff --git a/pinentry/password-cache.o b/pinentry/password-cache.o
Binary files differ.
diff --git a/pinentry/pinentry.c b/pinentry/pinentry.c
@@ -0,0 +1,1308 @@
+/* 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 <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 = "*REDACTED*"; break;
+ case 14: p = "Copyright (C) 2015 g10 Code GmbH"; break;
+ case 19: p = "Please report bugs to <" "*REDACTED*" ">.\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);
+}
diff --git a/pinentry/pinentry.h b/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 */
diff --git a/pinentry/pinentry.o b/pinentry/pinentry.o
Binary files differ.
diff --git a/pinentry/secmem++.h b/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__ */
diff --git a/pinentry/secmem-util.h b/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"
diff --git a/pinentry/secmem.c b/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 */
+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)
+{
+ 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()
+{
+ 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()
+{
+ 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;
+}
diff --git a/pinentry/secmem.o b/pinentry/secmem.o
Binary files differ.
diff --git a/pinentry/util.c b/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()
+{
+#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
+}
diff --git a/pinentry/util.h b/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
diff --git a/pinentry/util.o b/pinentry/util.o
Binary files differ.
diff --git a/spine b/spine
Binary files differ.
diff --git a/spine.c b/spine.c
@@ -0,0 +1,339 @@
+#include <signal.h>
+#include <time.h>
+#include <ctype.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <string.h>
+#include <unistd.h>
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <X11/Xutil.h>
+#include <X11/Xft/Xft.h>
+
+#include "drw.h"
+#include "util.h"
+
+#include "pinentry/pinentry.h"
+#include "pinentry/memory.h"
+
+/* macros */
+#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 TEXTNW(X,N) (drw_font_getexts_width(drw->fonts[0], (X), (N)))
+#define TEXTW(X) (drw_text(drw, 0, 0, 0, 0, (X), 0) + drw->fonts[0]->h)
+
+const char *str_OK = "OK\n";
+const char *str_ERRUNPARS = "ERR1337 dunno what to do with it\n";
+const char *str_ERRNOTIMP = "ERR4100 not implemented yet\n";
+
+/* enums */
+enum { SchemeNorm, SchemeSel, SchemeLast }; /* color schemes */
+
+static char text[2048] = "";
+static int bh, mw, mh;
+static int inputw, promptw;
+static size_t cursor = 0;
+static Atom clip, utf8;
+static Window win;
+static XIC xic;
+static int mon = -1;
+
+static ClrScheme scheme[SchemeLast];
+static Display *dpy;
+static int screen;
+static Window root;
+static Drw *drw;
+static int sw, sh;
+
+static int timed_out;
+
+#include "config.h"
+
+void
+grabkeyboard(void) {
+ int i;
+
+ /* 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\n");
+}
+
+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 && (text[n] & 0xc0) == 0x80; n += inc);
+ return n;
+}
+
+
+void
+insert(const char *str, ssize_t n) {
+ if(strlen(text) + n > sizeof text - 1)
+ return;
+ /* move existing text out of the way, insert new text, and update cursor */
+ memmove(&text[cursor + n], &text[cursor], sizeof text - cursor - MAX(n, 0));
+ if(n > 0)
+ memcpy(&text[cursor], str, n);
+ cursor += n;
+}
+
+void
+drawwin(void){
+ int curpos;
+ int x=0, y=0, h=bh, w;
+ drw_setscheme(drw, &scheme[SchemeNorm]);
+ drw_rect(drw, 0,0,mw,mh,True,1,1);
+
+ if (description && *description) {
+ drw_setscheme(drw, &scheme[SchemeSel]);
+ drw_text(drw, 0,0,mw,bh,description,0);
+ y+=bh;
+ }
+
+ if (prompt && *prompt) {
+ drw_setscheme(drw, &scheme[SchemeSel]);
+ drw_text(drw, x, y, promptw, bh, prompt, 0);
+ x += promptw;
+ }
+
+ w = inputw;
+ drw_setscheme(drw, &scheme[SchemeNorm]);
+
+ char *sectext = malloc (sizeof (char) * 2048);
+ sectext[0] = '\0';
+ int i;
+ int seccursor=0;
+ for (i=0; text[i]!='\0'; i=nextrune(i, +1)){
+ strcat(sectext, secchar);
+ if (i<cursor){
+ ssize_t n;
+ for(n = seccursor + 1; n + 1 >= 0 && (sectext[n] & 0xc0) == 0x80; n ++);
+ seccursor = n;
+ }
+ }
+
+ drw_text(drw, x, y, mw, bh, sectext, 0);
+ if((curpos = TEXTNW(sectext, seccursor) + bh/2 - 2) < w) {
+ drw_rect(drw, x + curpos + 2, y + 2, 1 , bh-4 , True, 1, 0);
+ }
+
+ drw_map(drw, win, 0, 0, mw, mh);
+}
+
+void
+setup(void){
+ int x,y;
+ XSetWindowAttributes swa;
+ XIM xim;
+ scheme[SchemeNorm].bg = drw_clr_create(drw, normbgcolor);
+ scheme[SchemeNorm].fg = drw_clr_create(drw, normfgcolor);
+ scheme[SchemeSel].bg = drw_clr_create(drw, selbgcolor);
+ scheme[SchemeSel].fg = drw_clr_create(drw, selfgcolor);
+ clip = XInternAtom(dpy, "CLIPBOARD", False);
+ utf8 = XInternAtom(dpy, "UTF8_STRING", False);
+ bh = drw->fonts[0]->h + 2;
+ mh = (description && *description)? bh * 2 : bh;
+ x = 0;
+ y = topbar ? 0 : sh - mh;
+ mw = sw;
+ promptw = (prompt && *prompt) ? TEXTW(prompt) : 0;
+ inputw = mw-promptw;
+ swa.override_redirect = True;
+ swa.background_pixel = scheme[SchemeNorm].bg->pix;
+ swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask;
+ win = XCreateWindow(dpy, root, x, y, mw, mh, 0,
+ DefaultDepth(dpy, screen), CopyFromParent,
+ DefaultVisual(dpy, screen),
+ CWOverrideRedirect | CWBackPixel | CWEventMask, &swa);
+
+ xim = XOpenIM(dpy, NULL, NULL, NULL);
+ xic = XCreateIC(xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
+ XNClientWindow, win, XNFocusWindow, win, NULL);
+
+ XMapRaised(dpy, win);
+ drw_resize(drw, mw, mh);
+
+ drawwin();
+}
+
+void
+cleanup(void) {
+ XUngrabKey(dpy, AnyKey, AnyModifier, root);
+ drw_clr_free(scheme[SchemeNorm].bg);
+ drw_clr_free(scheme[SchemeNorm].fg);
+ drw_clr_free(scheme[SchemeSel].fg);
+ drw_clr_free(scheme[SchemeSel].bg);
+ drw_free(drw);
+ XSync(dpy, False);
+ XCloseDisplay(dpy);
+}
+
+
+int
+keypress(XKeyEvent *ev) {
+ char buf[32];
+ int len;
+ KeySym ksym = NoSymbol;
+ Status status;
+ len = XmbLookupString(xic, ev, buf, sizeof buf, &ksym, &status);
+ if (status == XBufferOverflow)
+ return 0;
+ switch(ksym){
+ default:
+ if (!iscntrl(*buf))
+ insert(buf, len);
+ break;
+ case XK_Delete:
+ if(text[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:
+ cleanup();
+ exit(1);
+ break;
+ case XK_Left:
+ if(cursor > 0) {
+ cursor = nextrune(cursor, -1);
+ }
+ break;
+ case XK_Right:
+ if(text[cursor]!='\0') {
+ cursor = nextrune(cursor, +1);
+ }
+ break;
+ case XK_Return:
+ case XK_KP_Enter:
+ return 1;
+ break;
+ }
+ drawwin();
+ return 0;
+}
+
+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, (sizeof text / 4) + 1, 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;
+ while(!XNextEvent(dpy, &ev)) {
+ if(XFilterEvent(&ev, win))
+ continue; /*what is this I don't even*/
+ 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;
+ }
+ }
+}
+
+void
+promptwin(pinentry_t pinentry) {
+ if(!setlocale(LC_CTYPE, "") || !XSupportsLocale())
+ fputs("warning: no locale support\n", stderr);
+ if(!(dpy = XOpenDisplay(pinentry->display))) /*NULL was here*/
+ die("dmenu: cannot open display\n");
+ screen = DefaultScreen(dpy);
+ root = RootWindow(dpy, screen);
+ sw = DisplayWidth(dpy, screen);
+ sh = DisplayHeight(dpy, screen);
+ drw = drw_create(dpy, screen, root, sw, sh);
+ drw_load_fonts(drw, fonts, LENGTH(fonts));
+ if(!drw->fontcount)
+ die("No fonts could be loaded.\n");
+ drw_setscheme(drw, &scheme[SchemeNorm]);
+ grabkeyboard();
+ setup();
+ run();
+ cleanup();
+}
+
+static void
+catchsig(int sig)
+{
+ if (sig == SIGALRM)
+ timed_out = 1;
+}
+
+int
+password (pinentry_t pinentry) {
+ promptwin(pinentry);
+ char *buf = secmem_malloc(strlen(text));
+ strcpy(buf, text);
+ pinentry_setbuffer_use (pinentry, buf, 0);
+ return 1;
+}
+
+int
+confirm(pinentry_t pinentry) {
+ return 1;
+}
+
+int
+spinecmdhandler (pinentry_t pinentry) {
+ if (pinentry->timeout){
+ struct sigaction sa;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = catchsig;
+ sigaction(SIGALRM, &sa, NULL);
+ alarm(pinentry->timeout);
+ }
+ if (pinentry->pin)
+ return password(pinentry);
+ else
+ return confirm(pinentry);
+
+ return -1;
+}
+
+pinentry_cmd_handler_t pinentry_cmd_handler = spinecmdhandler;
+
+int
+main(int argc, char *argv[]){
+ pinentry_init("spine");
+ pinentry_parse_opts(argc, argv);
+ if (pinentry_loop())
+ return 1;
+ return 0;
+}
+\ No newline at end of file
diff --git a/spine.o b/spine.o
Binary files differ.
diff --git a/spinetest b/spinetest
@@ -0,0 +1,3 @@
+#!/bin/sh
+echo "GETPIN
+BYE" | ./spine
+\ No newline at end of file
diff --git a/util.c b/util.c
@@ -0,0 +1,17 @@
+/* See LICENSE file for copyright and license details. */
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "util.h"
+
+void
+die(const char *errstr, ...) {
+ va_list ap;
+
+ va_start(ap, errstr);
+ vfprintf(stderr, errstr, ap);
+ va_end(ap);
+ exit(EXIT_FAILURE);
+}
+
diff --git a/util.h b/util.h
@@ -0,0 +1,7 @@
+/* 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 *errstr, ...);
diff --git a/util.o b/util.o
Binary files differ.