1 /*
2  * Copyright (C) 2006 Josh A. Beam
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *   1. Redistributions of source code must retain the above copyright
9  *      notice, this list of conditions and the following disclaimer.
10  *   2. Redistributions in binary form must reproduce the above copyright
11  *      notice, this list of conditions and the following disclaimer in the
12  *      documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
18  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
20  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
21  * WHETHER IN CONTACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
22  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
23  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 
26 /*
27  * This is a simple getopt implementation for use by prtunnel
28  * on win32 systems. Note that it only supports the features
29  * that prtunnel actually uses, so it doesn't have an opterr
30  * variable, for example.
31  */
32 
33 #ifdef _WIN32
34 #include <stdio.h>
35 #include <string.h>
36 #include "getopt.h"
37 
38 #ifndef NULL
39 #define NULL 0
40 #endif
41 
42 int optind = 1;
43 char *optarg = NULL;
44 
45 /*
46  * if opt is an option found in optstring, return opt. otherwise,
47  * return -1. if opt is valid and it has an argument, set the int
48  * pointed to by has_argument to 1. otherwise, set it 0.
49  */
50 static char
get_matching_option(char opt,const char * optstring,int * has_argument)51 get_matching_option(char opt, const char *optstring, int *has_argument)
52 {
53 	int i;
54 
55 	for(i = 0; i < strlen(optstring); i++) {
56 		if(opt == optstring[i]) {
57 			/* if there's a colon after this character, the option has
58 			 * an argument */
59 			if((i + 1) < strlen(optstring) && optstring[i+1] == ':')
60 				*has_argument = 1;
61 			else
62 				*has_argument = 0;
63 
64 			return opt;
65 		}
66 	}
67 
68 	return -1;
69 }
70 
71 /*
72  * permutes argv so that the argument at the specified
73  * index appears at the end of the array.
74  */
75 static void
permute_arguments(int argc,char * argv[],int index)76 permute_arguments(int argc, char *argv[], int index)
77 {
78 	int i;
79 	char *tmp = argv[index];
80 
81 	for(i = index; i < (argc - 1); i++)
82 		argv[i] = argv[i+1];
83 
84 	argv[argc-1] = tmp;
85 }
86 
87 int
getopt(int argc,char * argv[],const char * optstring)88 getopt(int argc, char *argv[], const char *optstring)
89 {
90 	static int current_arg = 1;
91 	static int current_char = 1;
92 	static int loop_argc = -1;
93 	int i;
94 
95 	/* loop_argc is used instead of argc directly so that the
96 	 * permuted arguments are not scanned more than once. this
97 	 * is done by decrementing loop_argc every time an
98 	 * argument is permuted. note that the permute_arguments
99 	 * function still needs to be given the actual argc.*/
100 	if(loop_argc < 0)
101 		loop_argc = argc;
102 
103 	if(current_arg >= argc) /* past end of arguments */
104 		return -1;
105 
106 	/* loop through each argument */
107 	for(i = current_arg; i < loop_argc; i++, current_arg++) {
108 		char opt;
109 		int has_argument;
110 
111 		if(*argv[i] != '-') { /* not an option string */
112 			permute_arguments(argc, argv, i);
113 			i--; /* arguments have been re-arranged, so the one at
114 				  * this index needs to be looked at again */
115 			current_arg--;
116 			loop_argc--;
117 			continue;
118 		} else if(current_char == 1) {
119 			optind++;
120 		}
121 
122 		if(current_char >= strlen(argv[i])) { /* past end of argument */
123 			current_char = 1;
124 			continue;
125 		}
126 
127 		/* if the current character matches an option in the option string,
128 		 * return the character. otherwise, return '?' */
129 		opt = get_matching_option(argv[i][current_char], optstring, &has_argument);
130 		if(opt != -1) {
131 			/* if the option has an argument, set optarg pointer. if
132 			 * the argument is missing, return ':' */
133 			if(has_argument) {
134 				/* if there are any characters after the current one
135 				 * in this argument, use those as the option argument.
136 				 * otherwise, use the next argument */
137 				if(current_char < (strlen(argv[i]) - 1)) {
138 					optarg = argv[i] + current_char + 1;
139 					current_arg++;    /* set past the option argument */
140 					current_char = 0; /* reset */
141 				} else {
142 					current_arg += 2; /* set past the option argument */
143 					current_char = 0; /* reset */
144 					optind++;
145 					if((i + 1) < loop_argc) {
146 						optarg = argv[i + 1];
147 					} else {
148 						optarg = NULL;
149 						current_char++;
150 						return ':';
151 					}
152 				}
153 			}
154 
155 			current_char++;
156 			return (int)opt;
157 		} else {
158 			current_char++;
159 			return '?';
160 		}
161 	}
162 
163 #ifdef DEBUG_GETOPT
164 	printf("argv:");
165 	for(i = 0; i < argc; i++)
166 		printf(" %s", argv[i]);
167 	printf("\n");
168 #endif /* DEBUG_GETOPT */
169 
170 	/* reached end of arguments */
171 	return -1;
172 }
173 #endif /* _WIN32 */
174