shellscan.C 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480
  1. /*
  2. * CDE - Common Desktop Environment
  3. *
  4. * Copyright (c) 1993-2012, The Open Group. All rights reserved.
  5. *
  6. * These libraries and programs are free software; you can
  7. * redistribute them and/or modify them under the terms of the GNU
  8. * Lesser General Public License as published by the Free Software
  9. * Foundation; either version 2 of the License, or (at your option)
  10. * any later version.
  11. *
  12. * These libraries and programs are distributed in the hope that
  13. * they will be useful, but WITHOUT ANY WARRANTY; without even the
  14. * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
  15. * PURPOSE. See the GNU Lesser General Public License for more
  16. * details.
  17. *
  18. * You should have received a copy of the GNU Lesser General Public
  19. * License along with these libraries and programs; if not, write
  20. * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
  21. * Floor, Boston, MA 02110-1301 USA
  22. */
  23. /*
  24. * $TOG: shellscan.C /main/9 1999/10/14 15:05:42 mgreess $
  25. *
  26. * (c) Copyright 1996 Digital Equipment Corporation.
  27. * (c) Copyright 1993,1994,1996 Hewlett-Packard Company.
  28. * (c) Copyright 1993,1994,1996 International Business Machines Corp.
  29. * (c) Copyright 1993,1994,1996 Sun Microsystems, Inc.
  30. * (c) Copyright 1993,1994,1996 Novell, Inc.
  31. * (c) Copyright 1996 FUJITSU LIMITED.
  32. * (c) Copyright 1996 Hitachi.
  33. */
  34. #include <stdio.h>
  35. #include <stdlib.h>
  36. #include <string.h>
  37. #define X_INCLUDE_PWD_H
  38. #define XOS_USE_XT_LOCKING
  39. #include <X11/Xos_r.h>
  40. #include <codelibs/nl_hack.h>
  41. # include <unistd.h>
  42. #include "stringio.h"
  43. #include "buf.h"
  44. #include <codelibs/shellutils.h>
  45. #include <codelibs/boolean.h>
  46. #include <codelibs/stringx.h>
  47. #include "DtSvcLock.h"
  48. #ifdef XTHREADS
  49. extern "C" {
  50. extern void XtProcessLock(void);
  51. extern void XtProcessUnlock(void);
  52. }
  53. #endif
  54. #define ISIDENT(CH) (isalnum(CH) || (CH) == '_')
  55. static _SHXbuf *buf = NULL;
  56. static const char *getvar(const char *var, char *);
  57. // Make this a global someday:
  58. static const char *(*shellvarfn)(const char *, char *) = getvar;
  59. // Parse a sequence of the ksh meta-characters ;&|<> and whitespace
  60. // into a single ksh token. Return a pointer to the token as a
  61. // string. All whitespace characters are mapped to a single space
  62. // character. If ch is not a meta-character, return a NULL pointer.
  63. static char *
  64. parsemeta(int ch, _StringIO &in, char *ifs, unsigned opts, char *meta)
  65. {
  66. if (ch == '\0')
  67. return " "; // whitespace
  68. _DtSvcProcessLock();
  69. if (buf->quote() != NOQUOTE) {
  70. _DtSvcProcessUnlock();
  71. return NULL; // normal character
  72. }
  73. if (!(opts & SHX_NOSPACE) && strchr(ifs, ch) != NULL) {
  74. _DtSvcProcessUnlock();
  75. return " "; // whitespace
  76. }
  77. if (!(opts & SHX_NOMETA))
  78. {
  79. int len = 0;
  80. if (buf->new_token() && isascii(ch) && isdigit(ch))
  81. if (in.next() == '<' || in.next() == '>')
  82. {
  83. meta[len++] = (char)ch;
  84. ch = in.get();
  85. }
  86. switch (meta[len++] = (char)ch, ch)
  87. {
  88. case ';':
  89. case '&':
  90. case '(':
  91. case ')':
  92. meta[len++] = (in.next() == ch) ? in.get() : '\0';
  93. meta[len] = '\0';
  94. _DtSvcProcessUnlock();
  95. return meta;
  96. case '|':
  97. meta[len++] = (in.next() == '|' || in.next() == '&') ?
  98. in.get() : '\0';
  99. meta[len] = '\0';
  100. _DtSvcProcessUnlock();
  101. return meta;
  102. case '>':
  103. case '<':
  104. if (in.next() == ch || in.next() == '&')
  105. meta[len++] = (char)in.get();
  106. meta[len] = '\0';
  107. _DtSvcProcessUnlock();
  108. return meta;
  109. }
  110. }
  111. _DtSvcProcessUnlock();
  112. return NULL; // normal character
  113. }
  114. // Takes the name of a variable, and looks up it's value. Someday,
  115. // this will be replaceable by the user.
  116. static const char *
  117. getvar(const char *name, char *buff)
  118. {
  119. if (name[0] != '\0' && name[1] == '\0')
  120. switch (name[0])
  121. {
  122. case '$':
  123. sprintf(buff, "%d", getpid());
  124. return buff;
  125. case '#':
  126. case '?':
  127. return "0";
  128. }
  129. return getenv(name);
  130. }
  131. // Parse an environment variable name from the _StringIO stream,
  132. // and push its value into the _StringIO stream stack.
  133. static boolean
  134. pushvar(_StringIO &in, char *buff)
  135. {
  136. _StringIO tmp;
  137. privbuf_charbuf name;
  138. tmp = in;
  139. int ch = tmp.get(); // get the first character after the $
  140. if (!isascii(ch))
  141. return FALSE;
  142. if (ch == '{')
  143. while ((ch = tmp.get()) != '\0')
  144. {
  145. // ${foo!bar} form, grab everything inside {} as name
  146. if (ch == '\\') // Only \ does quoting inside ${}
  147. ch = tmp.get();
  148. else if (ch == '}')
  149. break;
  150. name.end() = ch;
  151. }
  152. else if (ispunct(ch))
  153. switch (ch) // Special non-alnum shell variables
  154. {
  155. case '#':
  156. case '?':
  157. case '$':
  158. case '!':
  159. case '-':
  160. case '*':
  161. case '@':
  162. case '_':
  163. name.end() = ch;
  164. break;
  165. default:
  166. return FALSE;
  167. }
  168. else if (isdigit(ch))
  169. name.end() = ch; // single-digit variables
  170. else if (ISIDENT(ch))
  171. {
  172. // normal variable
  173. do
  174. name.end() = ch;
  175. while (isascii(ch = tmp.get()) && ISIDENT(ch));
  176. tmp.unget();
  177. }
  178. else
  179. return FALSE;
  180. name.end() = '\0';
  181. in = tmp;
  182. in.push(shellvarfn(name.getarr(), buff));
  183. return TRUE;
  184. }
  185. static boolean
  186. pushenv(_StringIO &in, char const *name)
  187. {
  188. char *str = getenv(name);
  189. if (str == NULL || *str == '\0')
  190. return FALSE;
  191. else
  192. {
  193. in.push(str);
  194. return TRUE;
  195. }
  196. }
  197. static boolean
  198. pushtilde(_StringIO &in)
  199. {
  200. _StringIO tmp;
  201. int namelen = 0;
  202. privbuf_charbuf name;
  203. tmp = in;
  204. int ch;
  205. while ((ch = tmp.get()) != '\0' && ch != '/')
  206. name[namelen++] = ch;
  207. name[namelen] = '\0';
  208. tmp.unget();
  209. char *str = name.getarr();
  210. switch (*str)
  211. {
  212. case '\0':
  213. if (!pushenv(tmp, "HOME"))
  214. return FALSE;
  215. break;
  216. case '+':
  217. if (!pushenv(tmp, "PWD"))
  218. return FALSE;
  219. break;
  220. case '-':
  221. if (!pushenv(tmp, "OLDPWD"))
  222. return FALSE;
  223. break;
  224. default:
  225. {
  226. _Xgetpwparams pwd_buf;
  227. memset((char*) &pwd_buf, 0, sizeof(_Xgetpwparams));
  228. struct passwd * pwd_ret = _XGetpwnam(str, pwd_buf);
  229. if (pwd_ret == NULL)
  230. return FALSE;
  231. tmp.push(pwd_ret->pw_dir);
  232. }
  233. break;
  234. }
  235. in = tmp;
  236. return TRUE;
  237. }
  238. void
  239. pushgrave(_StringIO &in, const char endchar, boolean quotes, privbuf_charbuf &result)
  240. {
  241. int ch;
  242. char quote = NOQUOTE;
  243. privbuf_charbuf cmd;
  244. do
  245. {
  246. ch = in.get();
  247. if (quotes)
  248. switch (ch)
  249. {
  250. case '"':
  251. quote = DOUBLEQUOTE;
  252. continue;
  253. case '\'':
  254. if (quote == '"')
  255. break; // not recognized inside of ""
  256. do
  257. cmd.end() = ch;
  258. while ((ch = in.get()) != '\'' && ch != '\0');
  259. cmd.end() = '\'';
  260. quote = NOQUOTE;
  261. continue;
  262. case '\\':
  263. cmd.end() = ch;
  264. ch = in.get();
  265. if (ch != '\0')
  266. cmd.end() = ch;
  267. continue;
  268. }
  269. if (ch == endchar)
  270. ch = '\0';
  271. cmd.end() = ch;
  272. } while (ch != '\0');
  273. result.reset();
  274. FILE *fp = popen(cmd.getarr(), "r");
  275. if (fp == NULL)
  276. return;
  277. while ((ch = getc(fp)) != EOF)
  278. result.end() = ch;
  279. pclose(fp);
  280. // Remove trailing newline, if any
  281. long end = result.size() - 1;
  282. if (result[end] == '\n')
  283. result.reset(end);
  284. result.end() = '\0';
  285. in.push(result.getarr());
  286. }
  287. char const *const *
  288. shellscan(char const *str, int *argc, unsigned opts)
  289. {
  290. if (opts & SHX_COMPLETE)
  291. opts |= SHX_NOSPACE | SHX_NOMETA;
  292. char *ifs = getenv("IFS");
  293. if (ifs == NULL)
  294. ifs = " \t\n";
  295. _DtSvcProcessLock();
  296. if (buf == NULL)
  297. buf = new _SHXbuf;
  298. buf->reset((boolean)!(opts & SHX_NOGLOB), (boolean)(opts & SHX_COMPLETE));
  299. _StringIO in(str);
  300. int ch;
  301. char buff[10], meta_buff[4];
  302. privbuf_charbuf result;
  303. do
  304. {
  305. ch = in.get();
  306. // Don't recognize special characters if this is a shell
  307. // variable or command substitution.
  308. if (!in.in_expansion())
  309. {
  310. // Handle quoting rules, setting the flag array and
  311. // quote variable appropriately.
  312. if (!(opts & SHX_NOQUOTES))
  313. switch (ch)
  314. {
  315. case '"':
  316. buf->quote(DOUBLEQUOTE);
  317. continue;
  318. case '\'':
  319. if (buf->quote() == DOUBLEQUOTE)
  320. break; // not recognized inside of ""
  321. buf->quote(SINGLEQUOTE);
  322. while ((ch = in.get()) != '\'' && ch != '\0')
  323. buf->append(ch);
  324. buf->quote(SINGLEQUOTE);
  325. continue;
  326. case '\\':
  327. ch = in.get();
  328. if (ch == '\n') // ignore \<newline>
  329. continue;
  330. if (ch == '\0')
  331. {
  332. #if defined(__aix) /* Our Macro doesn't like '\\' (ignores rest of line) */
  333. buf->append('\\',
  334. SINGLEQUOTE);
  335. #else
  336. buf->append('\\', SINGLEQUOTE);
  337. #endif
  338. break;
  339. }
  340. if (buf->quote() == NOQUOTE)
  341. {
  342. buf->append(ch, SINGLEQUOTE);
  343. continue;
  344. }
  345. else
  346. {
  347. // inside "", \ only quotes these 4 characters:
  348. switch (ch)
  349. {
  350. case '$':
  351. case '\\':
  352. case '`':
  353. case '"':
  354. buf->append(ch, SINGLEQUOTE);
  355. continue;
  356. default:
  357. // treat the \ and the following char normally
  358. buf->append('\\');
  359. break;
  360. }
  361. }
  362. break;
  363. }
  364. if (!(opts & SHX_NOCMD))
  365. switch (ch)
  366. {
  367. case '`':
  368. pushgrave(in, '`', (boolean)!(opts & SHX_NOQUOTES), result);
  369. continue;
  370. case '$':
  371. if (in.next() != '(')
  372. break;
  373. in.get(); // skip the '('
  374. pushgrave(in, ')', (boolean)!(opts & SHX_NOQUOTES), result);
  375. continue;
  376. }
  377. if (ch == '~' && buf->new_token() && buf->quote() == NOQUOTE)
  378. if (!(opts & SHX_NOTILDE))
  379. {
  380. if (pushtilde(in))
  381. continue;
  382. buf->append('~');
  383. continue;
  384. }
  385. if (ch == '$' && !(opts & SHX_NOVARS))
  386. {
  387. if (pushvar(in, buff))
  388. continue;
  389. buf->append('$');
  390. continue;
  391. }
  392. }
  393. // If the next item is an unquoted whitespace character or
  394. // metacharacter token, terminate the current token. The NUL
  395. // character is considered to be whitespace.
  396. {
  397. int curr_opts = opts;
  398. if (in.in_expansion())
  399. curr_opts |= SHX_NOMETA;
  400. char *meta = parsemeta(ch, in, ifs, curr_opts, meta_buff);
  401. if (meta != NULL) // is it a meta-character?
  402. {
  403. // Terminate current token, if any
  404. if (!buf->new_token())
  405. buf->append('\0');
  406. if (*meta == ' ') // whitespace
  407. {
  408. // ignore contiguous whitespace chars
  409. if (buf->new_token())
  410. continue;
  411. }
  412. else // append the metachar token
  413. buf->append(meta);
  414. continue;
  415. }
  416. }
  417. buf->append(ch);
  418. } while (ch != '\0');
  419. if (argc != NULL)
  420. *argc = buf->ntokens();
  421. _DtSvcProcessUnlock();
  422. return ( (char const *const *) buf->vector() );
  423. /* !!! error 1325: `)' missing at end of input */
  424. }