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