1 /*
2 * Copyright 1999-2009 Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 * Jeremy Friesner
7 * Fredrik Modéen
8 */
9
10
11 #include "ParseCommandLine.h"
12
13 #include <stdio.h>
14 #include <string.h>
15 #include <unistd.h>
16
17 #include <Directory.h>
18 #include <Entry.h>
19 #include <FindDirectory.h>
20 #include <List.h>
21 #include <Path.h>
22 #include <Roster.h>
23 #include <String.h>
24 #include <SupportKit.h>
25
26
27 const char* kTrackerSignature = "application/x-vnd.Be-TRAK";
28
29
30 // This char is used to hold words together into single words...
31 #define GUNK_CHAR 0x01
32
33
34 // Turn all spaces that are not-to-be-counted-as-spaces into GUNK_CHAR chars.
35 static void
GunkSpaces(char * string)36 GunkSpaces(char* string)
37 {
38 bool insideQuote = false;
39 bool afterBackslash = false;
40
41 while (*string != '\0') {
42 switch(*string) {
43 case '\"':
44 if (!afterBackslash) {
45 // toggle escapement mode
46 insideQuote = !insideQuote;
47 }
48 break;
49
50 case ' ':
51 case '\t':
52 if ((insideQuote)||(afterBackslash))
53 *string = GUNK_CHAR;
54 break;
55 }
56
57 afterBackslash = (*string == '\\') ? !afterBackslash : false;
58 string++;
59 }
60 }
61
62
63 // Removes all un-escaped quotes and backslashes from the string, in place
64 static void
RemoveQuotes(char * string)65 RemoveQuotes(char* string)
66 {
67 bool afterBackslash = false;
68 char* endString = strchr(string, '\0');
69 char* to = string;
70
71 while (*string != '\0') {
72 bool temp = (*string == '\\') ? !afterBackslash : false;
73 switch(*string) {
74 case '\"':
75 case '\\':
76 if (afterBackslash)
77 *(to++) = *string;
78 break;
79
80 case 'n':
81 *(to++) = afterBackslash ? '\n' : *string;
82 break;
83
84 case 't':
85 *(to++) = afterBackslash ? '\t' : *string;
86 break;
87
88 default:
89 *(to++) = *string;
90 }
91
92 afterBackslash = temp;
93 string++;
94 }
95 *to = '\0';
96
97 if (to < endString) {
98 // needs to be double-terminated!
99 *(to + 1) = '\0';
100 }
101 }
102
103
104 static bool
IsValidChar(char c)105 IsValidChar(char c)
106 {
107 return ((c > ' ')||(c == '\n')||(c == '\t'));
108 }
109
110
111 // Returns true if it is returning valid results into (*setBegin) & (*setEnd).
112 // If returning true, (*setBegin) now points to the first char in a new word,
113 // and (*setEnd) now points to the char after the last char in the word, which
114 // has been set to a NUL byte.
115 static bool
GetNextWord(char ** setBegin,char ** setEnd)116 GetNextWord(char** setBegin, char** setEnd)
117 {
118 char* next = *setEnd;
119 // we'll start one after the end of the last one...
120
121 while (next++) {
122 if (*next == '\0') {
123 // no words left!
124 return false;
125 }
126 else if ((IsValidChar(*next) == false) && (*next != GUNK_CHAR))
127 *next = '\0';
128 else {
129 // found a non-whitespace char!
130 break;
131 }
132 }
133
134 *setBegin = next;
135 // we found the first char!
136
137 while (next++) {
138 if ((IsValidChar(*next) == false) && (*next != GUNK_CHAR)) {
139 *next = '\0';
140 // terminate the word
141 *setEnd = next;
142 return true;
143 }
144 }
145
146 return false;
147 // should never get here, actually
148 }
149
150
151 // Turns the gunk back into spaces
152 static void
UnGunk(char * str)153 UnGunk(char* str)
154 {
155 char* temp = str;
156 while (*temp) {
157 if (*temp == GUNK_CHAR)
158 *temp = ' ';
159
160 temp++;
161 }
162 }
163
164
165 char**
ParseArgvFromString(const char * command,int32 & argc)166 ParseArgvFromString(const char* command, int32& argc)
167 {
168 // make our own copy of the string...
169 int length = strlen(command);
170
171 // need an extra \0 byte to get GetNextWord() to stop
172 char* cmd = new char[length + 2];
173 strcpy(cmd, command);
174 cmd[length + 1] = '\0';
175 // zero out the second nul byte
176
177 GunkSpaces(cmd);
178 RemoveQuotes(cmd);
179
180 BList wordlist;
181 char* beginWord = NULL, *endWord = cmd - 1;
182
183 while (GetNextWord(&beginWord, &endWord))
184 wordlist.AddItem(beginWord);
185
186 argc = wordlist.CountItems();
187 char** argv = new char* [argc + 1];
188 for (int i = 0; i < argc; i++) {
189 char* temp = (char*) wordlist.ItemAt(i);
190 argv[i] = new char[strlen(temp) + 1];
191 strcpy(argv[i], temp);
192
193 // turn space-markers back into real spaces...
194 UnGunk(argv[i]);
195 }
196 argv[argc] = NULL;
197 // terminate the array
198 delete[] cmd;
199 // don't need our local copy any more
200
201 return argv;
202 }
203
204
205 void
FreeArgv(char ** argv)206 FreeArgv(char** argv)
207 {
208 if (argv != NULL) {
209 int i = 0;
210 while (argv[i] != NULL) {
211 delete[] argv[i];
212 i++;
213 }
214 }
215
216 delete[] argv;
217 }
218
219
220 // Make new, independent clone of an argv array and its strings.
221 char**
CloneArgv(char ** argv)222 CloneArgv(char** argv)
223 {
224 int argc = 0;
225 while (argv[argc] != NULL)
226 argc++;
227
228 char** newArgv = new char* [argc + 1];
229 for (int i = 0; i < argc; i++) {
230 newArgv[i] = new char[strlen(argv[i]) + 1];
231 strcpy(newArgv[i], argv[i]);
232 }
233 newArgv[argc] = NULL;
234
235 return newArgv;
236 }
237
238
239 BString
ParseArgvZeroFromString(const char * command)240 ParseArgvZeroFromString(const char* command)
241 {
242 char* array = NULL;
243
244 // make our own copy of the array...
245 int length = strlen(command);
246
247 // need an extra nul byte to get GetNextWord() to stop
248 char* cmd = new char[length + 2];
249 strcpy(cmd, command);
250 cmd[length + 1] = '\0';
251 // zero out the second \0 byte
252
253 GunkSpaces(cmd);
254 RemoveQuotes(cmd);
255
256 char* beginWord = NULL, *endWord = cmd - 1;
257 if (GetNextWord(&beginWord, &endWord)) {
258 array = new char[strlen(beginWord) + 1];
259 strcpy(array, beginWord);
260 UnGunk(array);
261 }
262 delete[] cmd;
263
264 BString string(array != NULL ? array : "");
265 delete[] array;
266
267 return string;
268 }
269
270
271 bool
DoStandardEscapes(BString & string)272 DoStandardEscapes(BString& string)
273 {
274 bool escape = false;
275
276 // Escape any characters that might mess us up
277 // note: check this first, or we'll detect the slashes WE put in!
278 escape |= EscapeChars(string, '\\');
279 escape |= EscapeChars(string, '\"');
280 escape |= EscapeChars(string, ' ');
281 escape |= EscapeChars(string, '\t');
282
283 return escape;
284 }
285
286
287 // Modifies (string) so that each instance of (badChar) in it is preceded by a
288 // backslash. Returns true iff modifications were made.
289 bool
EscapeChars(BString & string,char badChar)290 EscapeChars(BString& string, char badChar)
291 {
292 if (string.FindFirst(badChar) == -1)
293 return false;
294
295 BString temp;
296 int stringLen = string.Length();
297 for (int i = 0; i < stringLen; i++) {
298 char next = string[i];
299 if (next == badChar)
300 temp += '\\';
301 temp += next;
302 }
303
304 string = temp;
305 return true;
306 }
307
308
309 // Launch the given app/project file. Put here so that Shortcuts and
310 // BartLauncher can share this code!
311 status_t
LaunchCommand(char ** argv,int32 argc)312 LaunchCommand(char** argv, int32 argc)
313 {
314 BEntry entry(argv[0], true);
315 if (entry.Exists()) {
316 // See if it's a directory. If it is, ask Tracker to open it, rather
317 // than launch.
318 BDirectory testDir(&entry);
319 if (testDir.InitCheck() == B_OK) {
320 entry_ref ref;
321 status_t status = entry.GetRef(&ref);
322 if (status < B_OK)
323 return status;
324
325 BMessenger target(kTrackerSignature);
326 BMessage message(B_REFS_RECEIVED);
327 message.AddRef("refs", &ref);
328
329 return target.SendMessage(&message);
330 } else {
331 // It's not a directory, must be a file.
332 entry_ref ref;
333 if (entry.GetRef(&ref) == B_OK) {
334 if (argc > 1)
335 be_roster->Launch(&ref, argc - 1, &argv[1]);
336 else
337 be_roster->Launch(&ref);
338 return B_OK;
339 }
340 }
341 }
342
343 return B_ERROR;
344 }
345