1 /*
2  * Macros for processing command arguments.
3  *
4  * Conforms closely to the command option requirements of intro(1) in System V
5  * and intro(C) in Xenix.
6  *
7  * A command consists of: cmdname [ options ] [ cmdarguments ]
8  *
9  * Options consist of a leading dash '-' and a flag letter.  An argument may
10  * follow optionally preceded by white space.
11  * Options without arguments may be grouped behind a single dash.
12  * A dash on its own is interpreted as the end of the options and is retained
13  * as a command argument.
14  * A double dash '--' is interpreted as the end of the options and is discarded.
15  *
16  * For example:
17  *	zap -xz -f flame -q34 -- -x
18  *
19  * where zap.c contains the following in main():
20  *
21  *	OPTIONS("[-xz] [-q queue-id] [-f dump-file] user")
22  *	    FLAG('x', xecute)
23  *	    FLAG('z', zot)
24  *	    STRING('f', file)
25  *		fp = fopen(file, "w");
26  *	    NUMBER('q', queue)
27  *	    ENDOPTS
28  *
29  * Results in:
30  *	xecute = 1
31  *	zot = 1
32  *	file = "flame"
33  *	fp = fopen("flame", "w")
34  *	queue = 34
35  *	argc = 2
36  *	argv[0] = "zap"
37  *	argv[1] = "-x"
38  *
39  * Should the user enter unknown flags or leave out required arguments,
40  * the message:
41  *
42  *	Usage: zap [-xz] [-q queue-id] [-f dump-file] user
43  *
44  * will be printed.  This message can be printed by calling pusage(), or
45  * usage().  usage() will also cause program termination with exit code 1.
46  *
47  * Author: Stephen McKay, February 1991
48  *
49  * Based on recollection of the original options.h produced at the University
50  * of Queensland by Ross Patterson (and possibly others).
51  *
52  * $FreeBSD: stable/10/usr.sbin/ctm/ctm_rmail/options.h 95258 2002-04-22 13:44:47Z des $
53  */
54 
55 static char *O_usage;
56 static char *O_name;
57 extern long atol();
58 
59 void
pusage()60 pusage()
61     {
62     /*
63      * Avoid gratuitously loading stdio.
64      */
65     write(STDERR_FILENO, "usage: ", 7);
66     write(STDERR_FILENO, O_name, strlen(O_name));
67     write(STDERR_FILENO, " ", 1);
68     write(STDERR_FILENO, O_usage, strlen(O_usage));
69     write(STDERR_FILENO, "\n", 1);
70     }
71 
72 #define usage()		(pusage(), exit(1))
73 
74 #define OPTIONS(usage_msg)		\
75     {					\
76     char O_cont;			\
77     O_usage = (usage_msg);		\
78     O_name = argv[0];			\
79     while (*++argv && **argv == '-')	\
80 	{				\
81 	if ((*argv)[1] == '\0')		\
82 	    break;			\
83 	argc--;				\
84 	if ((*argv)[1] == '-' && (*argv)[2] == '\0') \
85 	    {				\
86 	    argv++;			\
87 	    break;			\
88 	    }				\
89 	O_cont = 1;			\
90 	while (O_cont)			\
91 	    switch (*++*argv)		\
92 		{			\
93 		default:		\
94 		case '-':		\
95 		    usage();		\
96 		case '\0':		\
97 		    O_cont = 0;
98 
99 #define	FLAG(x,flag)			\
100 		    break;		\
101 		case (x):		\
102 		    (flag) = 1;
103 
104 #define	CHAR(x,ch)			\
105 		    break;		\
106 		case (x):		\
107 		    O_cont = 0;		\
108 		    if (*++*argv == '\0' && (--argc, *++argv == 0)) \
109 			usage();	\
110 		    (ch) = **argv;
111 
112 #define	NUMBER(x,n)			\
113 		    break;		\
114 		case (x):		\
115 		    O_cont = 0;		\
116 		    if (*++*argv == '\0' && (--argc, *++argv == 0)) \
117 			usage();	\
118 		    (n) = atol(*argv);
119 
120 #define	STRING(x,str)			\
121 		    break;		\
122 		case (x):		\
123 		    O_cont = 0;		\
124 		    if (*++*argv == '\0' && (--argc, *++argv == 0)) \
125 			usage();	\
126 		    (str) = *argv;
127 
128 #define	SUFFIX(x,str)			\
129 		    break;		\
130 		case (x):		\
131 		    (str) = ++*argv;	\
132 		    O_cont = 0;
133 
134 #define ENDOPTS				\
135 		    break;		\
136 		}			\
137 	}				\
138     *--argv = O_name;			\
139     }
140