launcher.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  1. /*
  2. * Copyright (c) 2002-2003 ActiveState Corp.
  3. * Author: Trent Mick (TrentM@ActiveState.com)
  4. *
  5. * Permission is hereby granted, free of charge, to any person obtaining a
  6. * copy of this software and associated documentation files (the
  7. * "Software"), to deal in the Software without restriction, including
  8. * without limitation the rights to use, copy, modify, merge, publish,
  9. * distribute, sublicense, and/or sell copies of the Software, and to
  10. * permit persons to whom the Software is furnished to do so, subject to
  11. * the following conditions:
  12. *
  13. * The above copyright notice and this permission notice shall be included
  14. * in all copies or substantial portions of the Software.
  15. *
  16. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  17. * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  18. * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  19. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
  20. * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
  21. * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
  22. * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  23. */
  24. /* Console launch executable.
  25. *
  26. * This program exists solely to launch:
  27. * python <installdir>/<exename>.py <argv>
  28. * on Windows. "<exename>.py" must be in the same directory.
  29. *
  30. * Rationale:
  31. * - On some Windows flavours .py *can* be put on the PATHEXT to be
  32. * able to find "<exename>.py" if it is on the PATH. This is fine
  33. * until you need shell redirection to work. It does NOT for
  34. * extensions to PATHEXT. Redirection *does* work for "python
  35. * <script>.py" so we will try to do that.
  36. */
  37. #ifdef WIN32
  38. #include <windows.h>
  39. #include <process.h>
  40. #include <direct.h>
  41. #include <shlwapi.h>
  42. #else /* linux */
  43. #include <unistd.h>
  44. #endif /* WIN32 */
  45. #include <sys/stat.h>
  46. #include <errno.h>
  47. #include <stdio.h>
  48. #include <stdlib.h>
  49. #include <string.h>
  50. #include <stdarg.h>
  51. //---- constants
  52. #define BUF_LENGTH 2048
  53. #define MAX_PYTHON_ARGS 50
  54. #define MAX_FILES 50
  55. #define MAXPATHLEN 1024
  56. #ifdef WIN32
  57. #define SEP '\\'
  58. #define ALTSEP '/'
  59. // path list element separator
  60. #define DELIM ';'
  61. #else /* linux */
  62. #define SEP '/'
  63. // path list element separator
  64. #define DELIM ':'
  65. #endif
  66. #ifdef WIN32
  67. #define spawnvp _spawnvp
  68. #if defined(_MSC_VER) && _MSC_VER < 1900
  69. #define snprintf _snprintf
  70. #define vsnprintf _vsnprintf
  71. #endif
  72. //NOTE: this is for the stat *call* and the stat *struct*
  73. #define stat _stat
  74. #endif
  75. //---- globals
  76. char* programName = NULL;
  77. char* programPath = NULL;
  78. #ifndef WIN32 /* i.e. linux */
  79. extern char **environ; // the user environment
  80. #endif /* linux */
  81. //---- error logging functions
  82. void _LogError(const char* format ...)
  83. {
  84. va_list ap;
  85. va_start(ap, format);
  86. #if defined(WIN32) && defined(_WINDOWS)
  87. // put up a MessageBox
  88. char caption[BUF_LENGTH+1];
  89. snprintf(caption, BUF_LENGTH, "Error in %s", programName);
  90. char msg[BUF_LENGTH+1];
  91. vsnprintf(msg, BUF_LENGTH, format, ap);
  92. va_end(ap);
  93. MessageBox(NULL, msg, caption, MB_OK | MB_ICONEXCLAMATION);
  94. #else
  95. fprintf(stderr, "%s: error: ", programName);
  96. vfprintf(stderr, format, ap);
  97. va_end(ap);
  98. #endif /* WIN32 && _WINDOWS */
  99. }
  100. void _LogWarning(const char* format ...)
  101. {
  102. va_list ap;
  103. va_start(ap, format);
  104. #if defined(WIN32) && defined(_WINDOWS)
  105. // put up a MessageBox
  106. char caption[BUF_LENGTH+1];
  107. snprintf(caption, BUF_LENGTH, "Warning in %s", programName);
  108. char msg[BUF_LENGTH+1];
  109. vsnprintf(msg, BUF_LENGTH, format, ap);
  110. va_end(ap);
  111. MessageBox(NULL, msg, caption, MB_OK | MB_ICONWARNING);
  112. #else
  113. fprintf(stderr, "%s: warning: ", programName);
  114. vfprintf(stderr, format, ap);
  115. va_end(ap);
  116. #endif /* WIN32 && _WINDOWS */
  117. }
  118. //---- utilities functions
  119. /* _IsDir: Is the given dirname an existing directory */
  120. static int _IsDir(char *dirname)
  121. {
  122. #ifdef WIN32
  123. DWORD dwAttrib;
  124. dwAttrib = GetFileAttributes(dirname);
  125. if (dwAttrib == -1) {
  126. return 0;
  127. }
  128. if (dwAttrib & FILE_ATTRIBUTE_DIRECTORY) {
  129. return 1;
  130. }
  131. return 0;
  132. #else /* i.e. linux */
  133. struct stat buf;
  134. if (stat(dirname, &buf) != 0)
  135. return 0;
  136. if (!S_ISDIR(buf.st_mode))
  137. return 0;
  138. return 1;
  139. #endif
  140. }
  141. /* _IsLink: Is the given filename a symbolic link */
  142. static int _IsLink(char *filename)
  143. {
  144. #ifdef WIN32
  145. return 0;
  146. #else /* i.e. linux */
  147. struct stat buf;
  148. if (lstat(filename, &buf) != 0)
  149. return 0;
  150. if (!S_ISLNK(buf.st_mode))
  151. return 0;
  152. return 1;
  153. #endif
  154. }
  155. /* Is executable file
  156. * On Linux: check 'x' permission. On Windows: just check existence.
  157. */
  158. static int _IsExecutableFile(char *filename)
  159. {
  160. #ifdef WIN32
  161. return (int)PathFileExists(filename);
  162. #else /* i.e. linux */
  163. struct stat buf;
  164. if (stat(filename, &buf) != 0)
  165. return 0;
  166. if (!S_ISREG(buf.st_mode))
  167. return 0;
  168. if ((buf.st_mode & 0111) == 0)
  169. return 0;
  170. return 1;
  171. #endif /* WIN32 */
  172. }
  173. /* _GetProgramPath: Determine the absolute path to the given program name.
  174. *
  175. * Takes into account the current working directory, etc.
  176. * The implementations require the global 'programName' to be set.
  177. */
  178. #ifdef WIN32
  179. static char* _GetProgramPath(void)
  180. {
  181. //XXX this is ugly but I didn't want to use malloc, no reason
  182. static char progPath[MAXPATHLEN+1];
  183. // get absolute path to module
  184. if (!GetModuleFileName(NULL, progPath, MAXPATHLEN)) {
  185. _LogError("could not get absolute program name from "\
  186. "GetModuleFileName\n");
  187. exit(1);
  188. }
  189. // just need dirname
  190. for (char* p = progPath+strlen(progPath);
  191. *p != SEP && *p != ALTSEP;
  192. --p)
  193. {
  194. *p = '\0';
  195. }
  196. *p = '\0'; // remove the trailing SEP as well
  197. return progPath;
  198. }
  199. #else
  200. /* _JoinPath requires that any buffer argument passed to it has at
  201. least MAXPATHLEN + 1 bytes allocated. If this requirement is met,
  202. it guarantees that it will never overflow the buffer. If stuff
  203. is too long, buffer will contain a truncated copy of stuff.
  204. */
  205. static void
  206. _JoinPath(char *buffer, char *stuff)
  207. {
  208. size_t n, k;
  209. if (stuff[0] == SEP)
  210. n = 0;
  211. else {
  212. n = strlen(buffer);
  213. if (n > 0 && buffer[n-1] != SEP && n < MAXPATHLEN)
  214. buffer[n++] = SEP;
  215. }
  216. k = strlen(stuff);
  217. if (n + k > MAXPATHLEN)
  218. k = MAXPATHLEN - n;
  219. strncpy(buffer+n, stuff, k);
  220. buffer[n+k] = '\0';
  221. }
  222. static char*
  223. _GetProgramPath(void)
  224. {
  225. /* XXX this routine does *no* error checking */
  226. char* path = getenv("PATH");
  227. static char progPath[MAXPATHLEN+1];
  228. /* If there is no slash in the argv0 path, then we have to
  229. * assume the program is on the user's $PATH, since there's no
  230. * other way to find a directory to start the search from. If
  231. * $PATH isn't exported, you lose.
  232. */
  233. if (strchr(programName, SEP)) {
  234. strncpy(progPath, programName, MAXPATHLEN);
  235. }
  236. else if (path) {
  237. int bufspace = MAXPATHLEN;
  238. while (1) {
  239. char *delim = strchr(path, DELIM);
  240. if (delim) {
  241. size_t len = delim - path;
  242. if (len > bufspace) {
  243. len = bufspace;
  244. }
  245. strncpy(progPath, path, len);
  246. *(progPath + len) = '\0';
  247. bufspace -= len;
  248. }
  249. else {
  250. strncpy(progPath, path, bufspace);
  251. }
  252. _JoinPath(progPath, programName);
  253. if (_IsExecutableFile(progPath)) {
  254. break;
  255. }
  256. if (!delim) {
  257. progPath[0] = '\0';
  258. break;
  259. }
  260. path = delim + 1;
  261. }
  262. }
  263. else {
  264. progPath[0] = '\0';
  265. }
  266. // now we have to resolve a string of possible symlinks
  267. // - we'll just handle the simple case of a single level of
  268. // indirection
  269. //
  270. // XXX note this does not handle multiple levels of symlinks
  271. // here is pseudo-code for that (please implement it :):
  272. // while 1:
  273. // if islink(progPath):
  274. // linkText = readlink(progPath)
  275. // if isabsolute(linkText):
  276. // progPath = os.path.join(dirname(progPath), linkText)
  277. // else:
  278. // progPath = linkText
  279. // else:
  280. // break
  281. if (_IsLink(progPath)) {
  282. char newProgPath[MAXPATHLEN+1];
  283. readlink(progPath, newProgPath, MAXPATHLEN);
  284. strncpy(progPath, newProgPath, MAXPATHLEN);
  285. }
  286. // prefix with the current working directory if the path is
  287. // relative to conform with the Windows version of this
  288. if (strlen(progPath) != 0 && progPath[0] != SEP) {
  289. char cwd[MAXPATHLEN+1];
  290. char tmp[MAXPATHLEN+1];
  291. //XXX should check for failure retvals
  292. getcwd(cwd, MAXPATHLEN);
  293. snprintf(tmp, MAXPATHLEN, "%s%c%s", cwd, SEP, progPath);
  294. strncpy(progPath, tmp, MAXPATHLEN);
  295. }
  296. // 'progPath' now contains the full path to the program *and* the program
  297. // name. The latter is not desire.
  298. char* pLetter = progPath + strlen(progPath);
  299. for (;pLetter != progPath && *pLetter != SEP; --pLetter) {
  300. /* do nothing */
  301. }
  302. *pLetter = '\0';
  303. return progPath;
  304. }
  305. #endif /* WIN32 */
  306. //---- mainline
  307. int main(int argc, char** argv)
  308. {
  309. programName = argv[0];
  310. programPath = _GetProgramPath();
  311. // Determine the extension-less program basename.
  312. // XXX Will not always handle app names with '.' in them (other than
  313. // the '.' for the extension.
  314. char programNameNoExt[MAXPATHLEN+1];
  315. char *pStart, *pEnd;
  316. pStart = pEnd = programName + strlen(programName) - 1;
  317. while (pStart != programName && *(pStart-1) != SEP) {
  318. pStart--;
  319. }
  320. while (1) {
  321. if (pEnd == pStart) {
  322. pEnd = programName + strlen(programName) - 1;
  323. break;
  324. }
  325. pEnd--;
  326. if (*(pEnd+1) == '.') {
  327. break;
  328. }
  329. }
  330. strncpy(programNameNoExt, pStart, pEnd-pStart+1);
  331. *(programNameNoExt+(pEnd-pStart+1)) = '\0';
  332. // determine the full path to "<exename>.py"
  333. char pyFile[MAXPATHLEN+1];
  334. snprintf(pyFile, MAXPATHLEN, "%s%c%s.py", programPath, SEP,
  335. programNameNoExt);
  336. // Build the argument array for launching.
  337. char* pythonArgs[MAX_PYTHON_ARGS+1];
  338. int nPythonArgs = 0;
  339. pythonArgs[nPythonArgs++] = "python";
  340. pythonArgs[nPythonArgs++] = "-tt";
  341. pythonArgs[nPythonArgs++] = pyFile;
  342. for (int i = 1; i < argc; ++i) {
  343. pythonArgs[nPythonArgs++] = argv[i];
  344. }
  345. pythonArgs[nPythonArgs++] = NULL;
  346. return _spawnvp(_P_WAIT, pythonArgs[0], pythonArgs);
  347. }
  348. //---- mainline for win32 subsystem:windows app
  349. #ifdef WIN32
  350. int WINAPI WinMain(
  351. HINSTANCE hInstance, /* handle to current instance */
  352. HINSTANCE hPrevInstance, /* handle to previous instance */
  353. LPSTR lpCmdLine, /* pointer to command line */
  354. int nCmdShow /* show state of window */
  355. )
  356. {
  357. return main(__argc, __argv);
  358. }
  359. #endif