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 27const 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. 35static void 36GunkSpaces(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 64static void 65RemoveQuotes(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 104static bool 105IsValidChar(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. 115static bool 116GetNextWord(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 152static void 153UnGunk(char* str) 154{ 155 char* temp = str; 156 while (*temp) { 157 if (*temp == GUNK_CHAR) 158 *temp = ' '; 159 160 temp++; 161 } 162} 163 164 165char** 166ParseArgvFromString(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 205void 206FreeArgv(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. 221char** 222CloneArgv(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 239BString 240ParseArgvZeroFromString(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 271bool 272DoStandardEscapes(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. 289bool 290EscapeChars(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! 311status_t 312LaunchCommand(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