Browse Source

Abandon popt for getopt_long and make our own semiautomatic help.

Douglas William Thrift 14 years ago
parent
commit
aef1b026ee
3 changed files with 248 additions and 93 deletions
  1. 1 3
      GNUmakefile
  2. 234 73
      dtpstree.cpp
  3. 13 17
      man1/dtpstree.1

+ 1 - 3
GNUmakefile

@@ -1,8 +1,6 @@
-CPPFLAGS ?= -I/usr/local/include
 CXXFLAGS ?= -g -O2
 CXXFLAGS += -pedantic -Wall -Wno-long-long
-LDFLAGS ?= -L/usr/local/lib
-LDLIBS := -lkvm -lpopt
+LDLIBS := -lkvm
 
 .PHONY: all man clean
 

+ 234 - 73
dtpstree.cpp

@@ -14,10 +14,10 @@
 
 #include <err.h>
 #include <fcntl.h>
+#include <getopt.h>
 #include <kvm.h>
 #include <libgen.h>
 #include <paths.h>
-#include <popt.h>
 #include <pwd.h>
 #include <sys/param.h>
 #include <sys/sysctl.h>
@@ -194,94 +194,255 @@ private:
 	}
 };
 
-int main(int argc, char *argv[])
+static void help(const char *program, option options[], int code = 0)
 {
-	int flags(0);
-	pid_t hpid, pid;
-	char *user(NULL);
+	std::cout << "Usage: " << basename(program) << " [options] [PID|USER]" << std::endl << std::endl << "Options:" << std::endl;
 
+	for (option *option(options); option->name; ++option)
 	{
-		poptOption options[] = {
-			{ "arguments", 'a', POPT_ARG_VAL | POPT_ARGFLAG_OR, &flags, Arguments, "show command line arguments", NULL },
-			{ "ascii", 'A', POPT_ARG_VAL | POPT_ARGFLAG_OR, &flags, Ascii, "use ASCII line drawing characters", NULL },
-			{ "compact", 'c', POPT_ARG_VAL | POPT_ARGFLAG_OR, &flags, Compact, "don't compact identical subtrees", NULL },
-			{ "highlight-all", 'h', POPT_ARG_NONE, NULL, 'h', "highlight current process and its ancestors", NULL },
-			{ "highlight-pid", 'H', POPT_ARG_INT, &hpid, 'H', "highlight this process and its ancestors", "PID" },
-			{ "vt100", 'G', POPT_ARG_VAL | POPT_ARGFLAG_OR, &flags, Vt100, "use VT100 line drawing characters", NULL },
-			{ "show-kernel", 'k', POPT_ARG_VAL | POPT_ARGFLAG_OR, &flags, ShowKernel, "show kernel processes", NULL },
-			{ "long", 'l', POPT_ARG_VAL | POPT_ARGFLAG_OR, &flags, Long, "don't truncate long lines", NULL },
-			{ "numeric-sort", 'n', POPT_ARG_VAL | POPT_ARGFLAG_OR, &flags, NumericSort, "sort output by PID", NULL },
-			{ "show-pids", 'p', POPT_ARG_VAL | POPT_ARGFLAG_OR, &flags, ShowPids, "show PIDs; implies -c", NULL },
-			{ "show-titles", 't', POPT_ARG_VAL | POPT_ARGFLAG_OR, &flags, ShowTitles, "show process titles", NULL },
-			{ "uid-changes", 'u', POPT_ARG_VAL | POPT_ARGFLAG_OR, &flags, UidChanges, "show uid transitions", NULL },
-			{ "unicode", 'U', POPT_ARG_VAL | POPT_ARGFLAG_OR, &flags, Unicode, "use Unicode line drawing characters", NULL },
-			{ "version", 'V', POPT_ARG_VAL | POPT_ARGFLAG_OR, &flags, Version, "display version information", NULL },
-			{ "pid", '\0', POPT_ARG_INT, &pid, 'p', "start at this PID", "PID" },
-			{ "user", '\0', POPT_ARG_STRING, &user, 'u', "show only trees rooted at processes of this user", "USER" },
-			POPT_AUTOHELP
-			POPT_TABLEEND
-		};
-		poptContext context(poptGetContext(NULL, argc, const_cast<const char **>(argv), options, 0));
-		int versionArgc;
-		const char **versionArgv;
-
-		poptParseArgvString("-V", &versionArgc, &versionArgv);
-
-		poptAlias versionAlias = { NULL, 'v', versionArgc, versionArgv };
-
-		poptAddAlias(context, versionAlias, 0);
-		poptSetOtherOptionHelp(context, "[OPTION...] [PID|USER]");
-
-		int option;
-
-		while ((option = poptGetNextOpt(context)) >= 0)
-			switch (option)
-			{
-			case 'h':
-				hpid = getpid();
-			case 'H':
-				flags |= Highlight;
+		std::string name(option->name);
 
-				break;
-			case 'p':
-				flags |= Pid;
-				flags &= ~User;
+		if (name == "highlight")
+			continue;
 
-				break;
-			case 'u':
-				flags |= User;
-				flags &= ~Pid;
-			}
+		std::ostringstream arguments;
 
-		if (option != -1)
-			errx(1, "%s: %s", poptStrerror(option), poptBadOption(context, 0));
+		switch (option->val)
+		{
+		case 'h':
+			arguments << "-h, --highlight"; break;
+		case 'H':
+			arguments << "-H PID, --highlight=PID"; break;
+		case 0:
+			if (name == "pid")
+				arguments << "PID, --pid=PID";
+			else if (name == "user")
+				arguments << "USER, --user=USER";
+			else
+				goto argument;
+
+			break;
+		default:
+			arguments << '-' << static_cast<char>(option->val) << ", ";
+		argument:
+			arguments << "--" << name;
+		}
+
+		const char *description("");
 
-		for (const char **arg(poptGetArgs(context)); arg && *arg; ++arg)
+		switch (option->val)
 		{
-			char *end;
-			long value(std::strtol(*arg, &end, 0));
+		case 'a':
+			description = "show command line arguments"; break;
+		case 'A':
+			description = "use ASCII line drawing characters"; break;
+		case 'c':
+			description = "don't compact identical subtrees"; break;
+		case 'h':
+			description = "highlight the current process and its ancestors"; break;
+		case 'H':
+			description = "highlight the process PID and its ancestors"; break;
+		case 'G':
+			description = "use VT100 line drawing characters"; break;
+		case 'k':
+			description = "show kernel processes"; break;
+		case 'l':
+			description = "don't truncate long lines"; break;
+		case 'n':
+			description = "sort output by PID"; break;
+		case 'p':
+			description = "show PIDs; implies -c"; break;
+		case 't':
+			description = "show process titles"; break;
+		case 'u':
+			description = "show uid transitions"; break;
+		case 'U':
+			description = "use Unicode line drawing characters"; break;
+		case 'V':
+			description = "show version information and exit"; break;
+		case 0:
+			if (name == "help")
+				description = "show this help message and exit";
+			else if (name == "pid")
+				description = "show only the tree roted at the process PID";
+			else if (name == "user")
+				description = "show only trees rooted at processes of USER";
+		}
 
-			if (*arg == end || *end != '\0')
-			{
-				std::free(user);
+		std::printf("  %-25s %s\n", arguments.str().c_str(), description);
+	}
 
-				user = strdup(*arg);
-				flags |= User;
-				flags &= ~Pid;
-			}
-			else if (value > INT_MAX || value < INT_MIN)
-				errx(1, "%s: %s", poptStrerror(POPT_ERROR_OVERFLOW), *arg);
-			else
+	std::exit(code);
+}
+
+template <typename Type, long minimum, long maximum>
+static Type value(char *program, option options[], bool *success = NULL)
+{
+	char *end;
+	long value(std::strtol(optarg, &end, 0));
+
+	if (optarg == end || *end != '\0')
+		if (success)
+			*success = false;
+		else
+		{
+			warnx("invalid numeric value: \"%s\"", optarg);
+			help(program, options, 1);
+		}
+	else if (value < minimum)
+	{
+		warnx("number too small: %s", optarg);
+		help(program, options, 1);
+	}
+	else if (value > maximum)
+	{
+		warnx("number too large: %s", optarg);
+		help(program, options, 1);
+	}
+	else if (success)
+		*success = true;
+
+	return value;
+}
+
+static int options(int argc, char *argv[], pid_t &hpid, pid_t &pid, char *&user)
+{
+	option options[] = {
+		{ "arguments", no_argument, NULL, 'a' },
+		{ "ascii", no_argument, NULL, 'A' },
+		{ "compact", no_argument, NULL, 'c' },
+		{ "highlight", optional_argument, NULL, 0 },
+		{ "highlight-all", no_argument, NULL, 'h' },
+		{ "highlight-pid", required_argument, NULL, 'H' },
+		{ "vt100", no_argument, NULL, 'G' },
+		{ "show-kernel", no_argument, NULL, 'k' },
+		{ "long", no_argument, NULL, 'l' },
+		{ "numeric-sort", no_argument, NULL, 'n' },
+		{ "show-pids", no_argument, NULL, 'p' },
+		{ "show-titles", no_argument, NULL, 't' },
+		{ "uid-changes", no_argument, NULL, 'u' },
+		{ "unicode", no_argument, NULL, 'U' },
+		{ "version", no_argument, NULL, 'V' },
+		{ "help", no_argument, NULL, 0 },
+		{ "pid", required_argument, NULL, 0 },
+		{ "user", required_argument, NULL, 0 },
+		{ NULL, 0, NULL, 0 }
+	};
+	int option, index, flags(0);
+	char *program(argv[0]);
+
+	while ((option = getopt_long(argc, argv, "aAchH:GklnptuUV", options, &index)) != -1)
+		switch (option)
+		{
+		case 'a':
+			flags |= Arguments; break;
+		case 'A':
+			flags |= Ascii;
+			flags &= ~Vt100;
+			flags &= ~Unicode;
+
+			break;
+		case 'c':
+			flags |= Compact; break;
+		case 'h':
+			hpid = getpid();
+			flags |= Highlight;
+
+			break;
+		case 'H':
+			hpid = value<pid_t, 0, INT_MAX>(program, options);
+			flags |= Highlight;
+
+			break;
+		case 'G':
+			flags |= Vt100;
+			flags &= ~Ascii;
+			flags &= ~Unicode;
+
+			break;
+		case 'k':
+			flags |= ShowKernel; break;
+		case 'l':
+			flags |= Long; break;
+		case 'n':
+			flags |= NumericSort; break;
+		case 'p':
+			flags |= ShowPids; break;
+		case 't':
+			flags |= ShowTitles; break;
+		case 'u':
+			flags |= UidChanges; break;
+		case 'U':
+			flags |= Unicode;
+			flags &= ~Ascii;
+			flags &= ~Vt100;
+
+			break;
+		case 'V':
+			flags |= Version; break;
+		case 0:
 			{
-				pid = value;
-				flags |= Pid;
-				flags &= ~User;
+				std::string option(options[index].name);
+
+				if (option == "highlight")
+				{
+					hpid = optarg ? value<pid_t, 0, INT_MAX>(program, options) : getpid();
+					flags |= Highlight;
+				}
+				else if (option == "pid")
+				{
+					pid = value<pid_t, 0, INT_MAX>(program, options);
+					flags |= Pid;
+					flags &= ~User;
+				}
+				else if (option == "user")
+				{
+					std::free(user);
+
+					user = strdup(optarg);
+					flags |= User;
+					flags &= ~Pid;
+				}
+				else
+					help(program, options);
 			}
+
+			break;
+		case '?':
+			help(program, options, 1);
 		}
 
-		poptFreeContext(context);
+	_forall (int, index, optind, argc)
+	{
+		bool success;
+
+		optarg = argv[index];
+		pid = value<pid_t, 0, INT_MAX>(program, options, &success);
+
+		if (success)
+		{
+			std::free(user);
+
+			user = strdup(optarg);
+			flags |= User;
+			flags &= ~Pid;
+		}
+		else
+		{
+			flags |= Pid;
+			flags &= ~User;
+		}
 	}
 
+	return flags;
+}
+
+int main(int argc, char *argv[])
+{
+	pid_t hpid, pid;
+	char *user(NULL);
+	int flags(options(argc, argv, hpid, pid, user));
+
 	if (flags & Version)
 	{
 		std::cout << DTPSTREE_PROGRAM " " DTPSTREE_VERSION << std::endl;

+ 13 - 17
man1/dtpstree.1

@@ -4,8 +4,8 @@
 dtpstree \- display a tree of processes
 .SH SYNOPSIS
 .B dtpstree
-[\fIOPTION\fR...] [\fIPID|USER\fR]
-.SH DESCRIPTION
+[\fIoptions\fR] [\fIPID|USER\fR]
+.SH OPTIONS
 .TP
 \fB\-a\fR, \fB\-\-arguments\fR
 show command line arguments
@@ -16,11 +16,11 @@ use ASCII line drawing characters
 \fB\-c\fR, \fB\-\-compact\fR
 don't compact identical subtrees
 .TP
-\fB\-h\fR, \fB\-\-highlight\-all\fR
-highlight current process and its ancestors
+\fB\-h\fR, \fB\-\-highlight\fR
+highlight the current process and its ancestors
 .TP
-\fB\-H\fR, \fB\-\-highlight\-pid\fR=\fIPID\fR
-highlight this process and its ancestors
+\fB\-H\fR PID, \fB\-\-highlight\fR=\fIPID\fR
+highlight the process PID and its ancestors
 .TP
 \fB\-G\fR, \fB\-\-vt100\fR
 use VT100 line drawing characters
@@ -47,17 +47,13 @@ show uid transitions
 use Unicode line drawing characters
 .TP
 \fB\-V\fR, \fB\-\-version\fR
-display version information
+show version information and exit
 .TP
-\fB\-\-pid\fR=\fIPID\fR
-start at this PID
+\fB\-\-help\fR
+show this help message and exit
 .TP
-\fB\-\-user\fR=\fIUSER\fR
-show only trees rooted at processes of this user
-.SS "Help options:"
+PID, \fB\-\-pid\fR=\fIPID\fR
+show only the tree roted at the process PID
 .TP
-\-?, \fB\-\-help\fR
-Show this help message
-.TP
-\fB\-\-usage\fR
-Display brief usage message
+USER, \fB\-\-user\fR=\fIUSER\fR
+show only trees rooted at processes of USER