PageRenderTime 50ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 1ms

/fish.cpp

https://github.com/psychocandy/fish-shell
C++ | 543 lines | 384 code | 85 blank | 74 comment | 46 complexity | b546de1571becd7885d4646b32bdd2c0 MD5 | raw file
Possible License(s): LGPL-2.0
  1. /*
  2. Copyright (C) 2005-2008 Axel Liljencrantz
  3. This program is free software; you can redistribute it and/or modify
  4. it under the terms of the GNU General Public License version 2 as
  5. published by the Free Software Foundation.
  6. This program is distributed in the hope that it will be useful,
  7. but WITHOUT ANY WARRANTY; without even the implied warranty of
  8. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  9. GNU General Public License for more details.
  10. You should have received a copy of the GNU General Public License
  11. along with this program; if not, write to the Free Software
  12. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  13. */
  14. /** \file fish.c
  15. The main loop of <tt>fish</tt>.
  16. */
  17. #include "config.h"
  18. #include <stdlib.h>
  19. #include <stdio.h>
  20. #include <wchar.h>
  21. #include <string.h>
  22. #include <unistd.h>
  23. #include <errno.h>
  24. #include <unistd.h>
  25. #include <termios.h>
  26. #include <fcntl.h>
  27. #include <sys/param.h>
  28. #ifdef HAVE_GETOPT_H
  29. #include <getopt.h>
  30. #endif
  31. #include <locale.h>
  32. #include <signal.h>
  33. #include "fallback.h"
  34. #include "util.h"
  35. #include "common.h"
  36. #include "reader.h"
  37. #include "builtin.h"
  38. #include "function.h"
  39. #include "complete.h"
  40. #include "wutil.h"
  41. #include "env.h"
  42. #include "sanity.h"
  43. #include "proc.h"
  44. #include "parser.h"
  45. #include "expand.h"
  46. #include "intern.h"
  47. #include "exec.h"
  48. #include "event.h"
  49. #include "output.h"
  50. #include "history.h"
  51. #include "path.h"
  52. /* PATH_MAX may not exist */
  53. #ifndef PATH_MAX
  54. #define PATH_MAX 1024
  55. #endif
  56. /**
  57. The string describing the single-character options accepted by the main fish binary
  58. */
  59. #define GETOPT_STRING "+hilnvc:p:d:"
  60. static bool has_suffix(const std::string &path, const char *suffix, bool ignore_case)
  61. {
  62. size_t pathlen = path.size(), suffixlen = strlen(suffix);
  63. return pathlen >= suffixlen && ! (ignore_case ? strcasecmp : strcmp)(path.c_str() + pathlen - suffixlen, suffix);
  64. }
  65. /* Modifies the given path by calling realpath. Returns true if realpath succeeded, false otherwise */
  66. static bool get_realpath(std::string &path)
  67. {
  68. char buff[PATH_MAX], *ptr;
  69. if ((ptr = realpath(path.c_str(), buff)))
  70. {
  71. path = ptr;
  72. }
  73. return ptr != NULL;
  74. }
  75. /* OS X function for getting the executable path */
  76. extern "C" {
  77. int _NSGetExecutablePath(char* buf, uint32_t* bufsize);
  78. }
  79. /* Return the path to the current executable. This needs to be realpath'd. */
  80. static std::string get_executable_path(const char *argv0)
  81. {
  82. char buff[PATH_MAX];
  83. #if __APPLE__
  84. {
  85. /* Returns 0 on success, -1 if the buffer is too small */
  86. uint32_t buffSize = sizeof buff;
  87. if (0 == _NSGetExecutablePath(buff, &buffSize))
  88. return std::string(buff);
  89. /* Loop until we're big enough */
  90. char *mbuff = (char *)malloc(buffSize);
  91. while (0 > _NSGetExecutablePath(mbuff, &buffSize))
  92. mbuff = (char *)realloc(mbuff, buffSize);
  93. /* Return the string */
  94. std::string result = mbuff;
  95. free(mbuff);
  96. return result;
  97. }
  98. #endif
  99. {
  100. /* On other Unixes, try /proc directory. This might be worth breaking out into macros. */
  101. if (0 < readlink("/proc/self/exe", buff, sizeof buff) || // Linux
  102. 0 < readlink("/proc/curproc/file", buff, sizeof buff) || // BSD
  103. 0 < readlink("/proc/self/path/a.out", buff, sizeof buff)) // Solaris
  104. {
  105. return std::string(buff);
  106. }
  107. }
  108. /* Just return argv0, which probably won't work (i.e. it's not an absolute path or a path relative to the working directory, but instead something the caller found via $PATH). We'll eventually fall back to the compile time paths. */
  109. return std::string(argv0 ? argv0 : "");
  110. }
  111. static struct config_paths_t determine_config_directory_paths(const char *argv0)
  112. {
  113. struct config_paths_t paths;
  114. bool done = false;
  115. std::string exec_path = get_executable_path(argv0);
  116. if (get_realpath(exec_path))
  117. {
  118. #if __APPLE__
  119. /* On OS X, maybe we're an app bundle, and should use the bundle's files. Since we don't link CF, use this lame approach to test it: see if the resolved path ends with /Contents/MacOS/fish, case insensitive since HFS+ usually is.
  120. */
  121. if (! done)
  122. {
  123. const char *suffix = "/Contents/MacOS/fish";
  124. const size_t suffixlen = strlen(suffix);
  125. if (has_suffix(exec_path, suffix, true))
  126. {
  127. /* Looks like we're a bundle. Cut the string at the / prefixing /Contents... and then the rest */
  128. wcstring wide_resolved_path = str2wcstring(exec_path);
  129. wide_resolved_path.resize(exec_path.size() - suffixlen);
  130. wide_resolved_path.append(L"/Contents/Resources/");
  131. /* Append share, etc, doc */
  132. paths.data = wide_resolved_path + L"share/fish";
  133. paths.sysconf = wide_resolved_path + L"etc/fish";
  134. paths.doc = wide_resolved_path + L"doc/fish";
  135. /* But the bin_dir is the resolved_path, minus fish (aka the MacOS directory) */
  136. paths.bin = str2wcstring(exec_path);
  137. paths.bin.resize(paths.bin.size() - strlen("/fish"));
  138. done = true;
  139. }
  140. }
  141. #endif
  142. if (! done)
  143. {
  144. /* The next check is that we are in a reloctable directory tree like this:
  145. bin/fish
  146. etc/fish
  147. share/fish
  148. Check it!
  149. */
  150. const char *suffix = "/bin/fish";
  151. if (has_suffix(exec_path, suffix, false))
  152. {
  153. wcstring base_path = str2wcstring(exec_path);
  154. base_path.resize(base_path.size() - strlen(suffix));
  155. paths.data = base_path + L"/share/fish";
  156. paths.sysconf = base_path + L"/etc/fish";
  157. paths.doc = base_path + L"/share/doc/fish";
  158. paths.bin = base_path + L"/bin";
  159. struct stat buf;
  160. if (0 == wstat(paths.data, &buf) && 0 == wstat(paths.sysconf, &buf))
  161. {
  162. done = true;
  163. }
  164. }
  165. }
  166. }
  167. if (! done)
  168. {
  169. /* Fall back to what got compiled in. */
  170. paths.data = L"" DATADIR "/fish";
  171. paths.sysconf = L"" SYSCONFDIR "/fish";
  172. paths.doc = L"" DATADIR "/doc/fish";
  173. paths.bin = L"" PREFIX "/bin";
  174. done = true;
  175. }
  176. return paths;
  177. }
  178. /**
  179. Parse init files. exec_path is the path of fish executable as determined by argv[0].
  180. */
  181. static int read_init(const struct config_paths_t &paths)
  182. {
  183. parser_t &parser = parser_t::principal_parser();
  184. parser.eval( L"builtin . " + paths.data + L"/config.fish 2>/dev/null", 0, TOP );
  185. parser.eval( L"builtin . " + paths.sysconf + L"/config.fish 2>/dev/null", 0, TOP );
  186. /*
  187. We need to get the configuration directory before we can source the user configuration file
  188. */
  189. wcstring config_dir;
  190. /*
  191. If path_get_config returns false then we have no configuration directory
  192. and no custom config to load.
  193. */
  194. if (path_get_config(config_dir))
  195. {
  196. wcstring config_dir_escaped = escape_string( config_dir, 1 );
  197. wcstring eval_buff = format_string(L"builtin . %ls/config.fish 2>/dev/null", config_dir_escaped.c_str());
  198. parser.eval( eval_buff, 0, TOP );
  199. }
  200. return 1;
  201. }
  202. /**
  203. Parse the argument list, return the index of the first non-switch
  204. arguments.
  205. */
  206. static int fish_parse_opt( int argc, char **argv, const char **cmd_ptr )
  207. {
  208. int my_optind;
  209. int force_interactive=0;
  210. while( 1 )
  211. {
  212. static struct option
  213. long_options[] =
  214. {
  215. {
  216. "command", required_argument, 0, 'c'
  217. }
  218. ,
  219. {
  220. "debug-level", required_argument, 0, 'd'
  221. }
  222. ,
  223. {
  224. "interactive", no_argument, 0, 'i'
  225. }
  226. ,
  227. {
  228. "login", no_argument, 0, 'l'
  229. }
  230. ,
  231. {
  232. "no-execute", no_argument, 0, 'n'
  233. }
  234. ,
  235. {
  236. "profile", required_argument, 0, 'p'
  237. }
  238. ,
  239. {
  240. "help", no_argument, 0, 'h'
  241. }
  242. ,
  243. {
  244. "version", no_argument, 0, 'v'
  245. }
  246. ,
  247. {
  248. 0, 0, 0, 0
  249. }
  250. }
  251. ;
  252. int opt_index = 0;
  253. int opt = getopt_long( argc,
  254. argv,
  255. GETOPT_STRING,
  256. long_options,
  257. &opt_index );
  258. if( opt == -1 )
  259. break;
  260. switch( opt )
  261. {
  262. case 0:
  263. {
  264. break;
  265. }
  266. case 'c':
  267. {
  268. *cmd_ptr = optarg;
  269. is_interactive_session = 0;
  270. break;
  271. }
  272. case 'd':
  273. {
  274. char *end;
  275. int tmp;
  276. errno = 0;
  277. tmp = strtol(optarg, &end, 10);
  278. if( tmp >= 0 && tmp <=10 && !*end && !errno )
  279. {
  280. debug_level=tmp;
  281. }
  282. else
  283. {
  284. debug( 0, _(L"Invalid value '%s' for debug level switch"), optarg );
  285. exit_without_destructors(1);
  286. }
  287. break;
  288. }
  289. case 'h':
  290. {
  291. *cmd_ptr = "__fish_print_help fish";
  292. break;
  293. }
  294. case 'i':
  295. {
  296. force_interactive = 1;
  297. break;
  298. }
  299. case 'l':
  300. {
  301. is_login=1;
  302. break;
  303. }
  304. case 'n':
  305. {
  306. no_exec=1;
  307. break;
  308. }
  309. case 'p':
  310. {
  311. profile = optarg;
  312. break;
  313. }
  314. case 'v':
  315. {
  316. fwprintf( stderr,
  317. _(L"%s, version %s\n"),
  318. PACKAGE_NAME,
  319. PACKAGE_VERSION );
  320. exit_without_destructors( 0 );
  321. }
  322. case '?':
  323. {
  324. exit_without_destructors( 1 );
  325. }
  326. }
  327. }
  328. my_optind = optind;
  329. is_login |= (strcmp( argv[0], "-fish") == 0);
  330. /*
  331. We are an interactive session if we have not been given an
  332. explicit command to execute, _and_ stdin is a tty.
  333. */
  334. is_interactive_session &= (*cmd_ptr == 0);
  335. is_interactive_session &= (my_optind == argc);
  336. is_interactive_session &= isatty(STDIN_FILENO);
  337. /*
  338. We are also an interactive session if we have are forced-
  339. */
  340. is_interactive_session |= force_interactive;
  341. return my_optind;
  342. }
  343. /**
  344. Calls a bunch of init functions, parses the init files and then
  345. parses commands from stdin or files, depending on arguments
  346. */
  347. extern int g_fork_count;
  348. int main( int argc, char **argv )
  349. {
  350. struct stat tmp;
  351. int res=1;
  352. const char *cmd=0;
  353. int my_optind=0;
  354. set_main_thread();
  355. setup_fork_guards();
  356. wsetlocale( LC_ALL, L"" );
  357. is_interactive_session=1;
  358. program_name=L"fish";
  359. stat("----------FISH_HIT_MAIN----------", &tmp);
  360. my_optind = fish_parse_opt( argc, argv, &cmd );
  361. /*
  362. No-exec is prohibited when in interactive mode
  363. */
  364. if( is_interactive_session && no_exec)
  365. {
  366. debug( 1, _(L"Can not use the no-execute mode when running an interactive session") );
  367. no_exec = 0;
  368. }
  369. const struct config_paths_t paths = determine_config_directory_paths(argv[0]);
  370. proc_init();
  371. event_init();
  372. wutil_init();
  373. //parser_init();
  374. builtin_init();
  375. function_init();
  376. env_init(&paths);
  377. reader_init();
  378. history_init();
  379. parser_t &parser = parser_t::principal_parser();
  380. if (g_log_forks)
  381. printf("%d: g_fork_count: %d\n", __LINE__, g_fork_count);
  382. if( read_init(paths) )
  383. {
  384. if( cmd != 0 )
  385. {
  386. wchar_t *cmd_wcs = str2wcs( cmd );
  387. res = parser.eval( cmd_wcs, 0, TOP );
  388. free(cmd_wcs);
  389. reader_exit(0, 0);
  390. }
  391. else
  392. {
  393. if( my_optind == argc )
  394. {
  395. res = reader_read( STDIN_FILENO, 0 );
  396. }
  397. else
  398. {
  399. char **ptr;
  400. char *file = *(argv+(my_optind++));
  401. int i;
  402. int fd;
  403. wchar_t *rel_filename, *abs_filename;
  404. if( ( fd = open(file, O_RDONLY) ) == -1 )
  405. {
  406. wperror( L"open" );
  407. return 1;
  408. }
  409. // OK to not do this atomically since we cannot have gone multithreaded yet
  410. set_cloexec(fd);
  411. if( *(argv+my_optind))
  412. {
  413. wcstring sb;
  414. for( i=1,ptr = argv+my_optind; *ptr; i++, ptr++ )
  415. {
  416. if( i != 1 )
  417. sb.append( ARRAY_SEP_STR );
  418. sb.append( str2wcstring( *ptr ));
  419. }
  420. env_set( L"argv", sb.c_str(), 0 );
  421. }
  422. rel_filename = str2wcs( file );
  423. abs_filename = wrealpath( rel_filename, 0 );
  424. if( !abs_filename )
  425. {
  426. abs_filename = wcsdup(rel_filename);
  427. }
  428. reader_push_current_filename( intern( abs_filename ) );
  429. free( rel_filename );
  430. free( abs_filename );
  431. res = reader_read( fd, 0 );
  432. if( res )
  433. {
  434. debug( 1,
  435. _(L"Error while reading file %ls\n"),
  436. reader_current_filename()?reader_current_filename(): _(L"Standard input") );
  437. }
  438. reader_pop_current_filename();
  439. }
  440. }
  441. }
  442. proc_fire_event( L"PROCESS_EXIT", EVENT_EXIT, getpid(), res );
  443. history_destroy();
  444. proc_destroy();
  445. builtin_destroy();
  446. reader_destroy();
  447. parser.destroy();
  448. wutil_destroy();
  449. event_destroy();
  450. env_destroy();
  451. if (g_log_forks)
  452. printf("%d: g_fork_count: %d\n", __LINE__, g_fork_count);
  453. return res?STATUS_UNKNOWN_COMMAND:proc_get_last_status();
  454. }