1 /*
2  *                       Command Line Option Parser
3  *
4  *   File    : optlist.c
5  *   Purpose : Provide getopt style command line option parsing
6  *   Author  : Michael Dipperstein
7  *   Date    : August 1, 2007
8  *
9  ****************************************************************************
10  *
11  * OptList: A command line option parsing library
12  * Copyright (C) 2007, 2014 2018 by
13  * Michael Dipperstein (mdipperstein@gmail.com)
14  *
15  * This file is part of the OptList library.
16  *
17  * OptList is free software; you can redistribute it and/or modify it
18  * under the terms of the GNU Lesser General Public License as published by
19  * the Free Software Foundation; either version 3 of the License, or (at
20  * your option) any later version.
21  *
22  * OptList is distributed in the hope that it will be useful, but
23  * WITHOUT ANY WARRANTY; without even the implied warranty of
24  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser
25  * General Public License for more details.
26  *
27  * You should have received a copy of the GNU Lesser General Public License
28  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
29  *
30  */
31 module optlist_d.optlist;
32 
33 
34 private static import core.memory;
35 private static import core.stdc.stdio;
36 private static import core.stdc.string;
37 
38 /* CONSTANTS */
39 
40 /**
41  * this option has no arguement
42  */
43 public enum OL_NOINDEX = -1;
44 
45 /* TYPE DEFINITIONS */
46 
47 /**
48  * The structure for storing one of the command line options.
49  */
50 public struct option_t
51 {
52 	/**
53 	 * the current character option character
54 	 */
55 	char option;
56 
57 	/**
58 	 * pointer to arguments for this option
59 	 */
60 	char* argument;
61 
62 	/**
63 	 * index into argv[] containing the argument
64 	 */
65 	int argIndex;
66 
67 	/**
68 	 * the next option in the linked list
69 	 */
70 	.option_t* next;
71 }
72 
73 /* FUNCTIONS */
74 
75 /**
76  * This function is similar to the POSIX function getopt. All options and their corresponding arguments are returned in a linked list. This function should only be called once per an option list and it does not modify argv or argc.
77  *
78  * Params:
79  *      argc = the number of command line arguments (including the name of the executable)
80  *      argv = pointer to the open binary file to write encoded output
81  *      options = getopt style option list. A null terminated string of single character options. Follow an option with a colon to indicate that it requires an argument.
82  *
83  * Effects: Creates a link list of command line options and their arguments.
84  *
85  * NOTE: The caller is responsible for freeing up the option list when it is no longer needed.
86  *
87  * Returns: option_t type value where the option and arguement fields contain the next option symbol and its argument (if any). The argument field will be set to null if there are no arguments or if you specify an option with missing arguments or if memory allocation fails. The option field will be set to PO_NO_OPT if no more options are found.
88  */
89 extern (C)
90 pure nothrow @trusted @nogc
91 public .option_t* GetOptList(const int argc, scope const char** argv, scope const char* options)
92 
93 	in
94 	{
95 		if (argc > 1) {
96 			assert(argv != null);
97 			assert(options != null);
98 		}
99 	}
100 
101 	do
102 	{
103 		/* start with first argument and nothing found */
104 		int nextArg = 1;
105 		.option_t* head = null;
106 		.option_t* tail = null;
107 
108 		/* loop through all of the command line arguments */
109 		while (nextArg < argc) {
110 			size_t argIndex = 1;
111 
112 			while ((core.stdc..string.strlen(argv[nextArg]) > argIndex) && (argv[nextArg][0] == '-')) {
113 				/* attempt to find a matching option */
114 				size_t optIndex = .MatchOpt(argv[nextArg][argIndex], options);
115 
116 				if (options[optIndex] == argv[nextArg][argIndex]) {
117 					/* we found the matching option */
118 					if (head == null) {
119 						head = .MakeOpt(options[optIndex], null, .OL_NOINDEX);
120 
121 						if (head == null) {
122 							return null;
123 						}
124 
125 						tail = head;
126 					} else {
127 						tail.next = .MakeOpt(options[optIndex], null, .OL_NOINDEX);
128 
129 						if (tail.next == null) {
130 							.FreeOptList(head);
131 
132 							return null;
133 						}
134 
135 						tail = tail.next;
136 					}
137 
138 					if (options[optIndex + 1] == ':') {
139 						/* the option found should have a text arguement */
140 						argIndex++;
141 
142 						if (core.stdc..string.strlen(argv[nextArg]) > argIndex) {
143 							/* no space between argument and option */
144 							tail.argument = cast(char*)(&(argv[nextArg][argIndex]));
145 							tail.argIndex = nextArg;
146 						} else if (nextArg < argc) {
147 							/* there must be space between the argument option */
148 							nextArg++;
149 							tail.argument = cast(char*)(argv[nextArg]);
150 							tail.argIndex = nextArg;
151 						}
152 
153 						/* done with argv[nextArg] */
154 						break;
155 					}
156 				}
157 
158 				argIndex++;
159 			}
160 
161 			nextArg++;
162 		}
163 
164 		return head;
165 	}
166 
167 /**
168  * This function uses pureMalloc to allocate space for an option_t type structure and initailizes the structure with the values passed as a parameter.
169  *
170  * Params:
171  *      option = this option character
172  *      argument = pointer string containg the argument for option. Use null for no argument
173  *      index = argv[index] contains argument use OL_NOINDEX for no argument
174  *
175  * Effects: A new option_t type variable is created on the heap.
176  *
177  * Returns: Pointer to newly created and initialized option_t type structure. null if space for structure can't be allocated.
178  */
179 pure nothrow @trusted @nogc
180 private .option_t* MakeOpt(const char option, char* argument, const int index)
181 
182 	do
183 	{
184 		.option_t* opt = cast(.option_t*)(core.memory.pureMalloc(.option_t.sizeof));
185 
186 		if (opt != null) {
187 			opt.option = option;
188 			opt.argument = argument;
189 			opt.argIndex = index;
190 			opt.next = null;
191 		} else {
192 			return null;
193 		}
194 
195 		return opt;
196 	}
197 
198 /**
199  * This function will free all the elements in an option_t type linked list starting from the node passed as a parameter.
200  *
201  * Params:
202  *      list = head of linked list to be freed
203  *
204  * Effects: All elements of the linked list pointed to by list will be freed and list will be set to null.
205  */
206 extern (C)
207 pure nothrow @trusted @nogc
208 public void FreeOptList(.option_t* list)
209 
210 	do
211 	{
212 		.option_t* head = list;
213 		list = null;
214 
215 		while (head != null) {
216 			.option_t* next = head.next;
217 			core.memory.pureFree(head);
218 			head = next;
219 		}
220 	}
221 
222 /**
223  * This function searches for an arguement in an option list. It will return the index to the option matching the arguement or the index to the null if none is found.
224  *
225  * Params:
226  *      arguement = character arguement to be matched to an option in the option list
227  *      options = getopt style option list. A null terminated string of single character options. Follow an option with a colon to indicate that it requires an argument.
228  *
229  * Returns: Index of argument in option list. Index of end of string if arguement does not appear in the option list.
230  */
231 pure nothrow @trusted @nogc @live
232 private size_t MatchOpt(const char argument, scope const char* options)
233 
234 	in
235 	{
236 		assert(options != null);
237 	}
238 
239 	do
240 	{
241 		size_t optIndex = 0;
242 
243 		/* attempt to find a matching option */
244 		while ((options[optIndex] != '\0') && (options[optIndex] != argument)) {
245 			do {
246 				optIndex++;
247 			} while ((options[optIndex] != '\0') && (options[optIndex] == ':'));
248 		}
249 
250 		return optIndex;
251 	}
252 
253 /**
254  * This is function accepts a pointer to the name of a file along with path information and returns a pointer to the first character that is not part of the path.
255  *
256  * Params:
257  *      fullPath = pointer to an array of characters containing a file name and possible path modifiers.
258  *
259  * Returns: Returns a pointer to the first character after any path information.
260  */
261 extern (C)
262 pure nothrow @trusted @nogc @live
263 public char* FindFileName(scope const char* fullPath)
264 
265 	do
266 	{
267 		/* path deliminators */
268 		static immutable char[3] delim = ['\\', '/', ':'];
269 
270 		/* start of file name */
271 		const (char)* start = fullPath;
272 
273 		/* find the first character after all file path delimiters */
274 		for (size_t i = 0; i < 3; i++) {
275 			const (char)* tmp = core.stdc..string.strrchr(start, delim[i]);
276 
277 			if (tmp != null) {
278 				start = tmp + 1;
279 			}
280 		}
281 
282 		return cast(char*)(start);
283 	}