test.c 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500
  1. /* $OpenBSD: test.c,v 1.11 2009/10/27 23:59:22 deraadt Exp $ */
  2. /* $NetBSD: test.c,v 1.15 1995/03/21 07:04:06 cgd Exp $ */
  3. /*
  4. * test(1); version 7-like -- author Erik Baalbergen
  5. * modified by Eric Gisin to be used as built-in.
  6. * modified by Arnold Robbins to add SVR3 compatibility
  7. * (-x -c -b -p -u -g -k) plus Korn's -L -nt -ot -ef and new -S (socket).
  8. * modified by J.T. Conklin for NetBSD.
  9. *
  10. * This program is in the Public Domain.
  11. */
  12. #include <sys/types.h>
  13. #include <sys/stat.h>
  14. #include <unistd.h>
  15. #include <ctype.h>
  16. #include <errno.h>
  17. #include <stdio.h>
  18. #include <stdlib.h>
  19. #include <string.h>
  20. #include <err.h>
  21. #ifndef __dead
  22. #define __dead __attribute__((noreturn))
  23. #endif
  24. #define main testcmd
  25. /* test(1) accepts the following grammar:
  26. oexpr ::= aexpr | aexpr "-o" oexpr ;
  27. aexpr ::= nexpr | nexpr "-a" aexpr ;
  28. nexpr ::= primary | "!" primary
  29. primary ::= unary-operator operand
  30. | operand binary-operator operand
  31. | operand
  32. | "(" oexpr ")"
  33. ;
  34. unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"|
  35. "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S";
  36. binary-operator ::= "="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
  37. "-nt"|"-ot"|"-ef";
  38. operand ::= <any legal UNIX file name>
  39. */
  40. enum token {
  41. EOI,
  42. FILRD,
  43. FILWR,
  44. FILEX,
  45. FILEXIST,
  46. FILREG,
  47. FILDIR,
  48. FILCDEV,
  49. FILBDEV,
  50. FILFIFO,
  51. FILSOCK,
  52. FILSYM,
  53. FILGZ,
  54. FILTT,
  55. FILSUID,
  56. FILSGID,
  57. FILSTCK,
  58. FILNT,
  59. FILOT,
  60. FILEQ,
  61. FILUID,
  62. FILGID,
  63. STREZ,
  64. STRNZ,
  65. STREQ,
  66. STRNE,
  67. STRLT,
  68. STRGT,
  69. INTEQ,
  70. INTNE,
  71. INTGE,
  72. INTGT,
  73. INTLE,
  74. INTLT,
  75. UNOT,
  76. BAND,
  77. BOR,
  78. LPAREN,
  79. RPAREN,
  80. OPERAND
  81. };
  82. enum token_types {
  83. UNOP,
  84. BINOP,
  85. BUNOP,
  86. BBINOP,
  87. PAREN
  88. };
  89. struct t_op {
  90. const char *op_text;
  91. short op_num, op_type;
  92. } const ops [] = {
  93. {"-r", FILRD, UNOP},
  94. {"-w", FILWR, UNOP},
  95. {"-x", FILEX, UNOP},
  96. {"-e", FILEXIST,UNOP},
  97. {"-f", FILREG, UNOP},
  98. {"-d", FILDIR, UNOP},
  99. {"-c", FILCDEV,UNOP},
  100. {"-b", FILBDEV,UNOP},
  101. {"-p", FILFIFO,UNOP},
  102. {"-u", FILSUID,UNOP},
  103. {"-g", FILSGID,UNOP},
  104. {"-k", FILSTCK,UNOP},
  105. {"-s", FILGZ, UNOP},
  106. {"-t", FILTT, UNOP},
  107. {"-z", STREZ, UNOP},
  108. {"-n", STRNZ, UNOP},
  109. {"-h", FILSYM, UNOP}, /* for backwards compat */
  110. {"-O", FILUID, UNOP},
  111. {"-G", FILGID, UNOP},
  112. {"-L", FILSYM, UNOP},
  113. {"-S", FILSOCK,UNOP},
  114. {"=", STREQ, BINOP},
  115. {"!=", STRNE, BINOP},
  116. {"<", STRLT, BINOP},
  117. {">", STRGT, BINOP},
  118. {"-eq", INTEQ, BINOP},
  119. {"-ne", INTNE, BINOP},
  120. {"-ge", INTGE, BINOP},
  121. {"-gt", INTGT, BINOP},
  122. {"-le", INTLE, BINOP},
  123. {"-lt", INTLT, BINOP},
  124. {"-nt", FILNT, BINOP},
  125. {"-ot", FILOT, BINOP},
  126. {"-ef", FILEQ, BINOP},
  127. {"!", UNOT, BUNOP},
  128. {"-a", BAND, BBINOP},
  129. {"-o", BOR, BBINOP},
  130. {"(", LPAREN, PAREN},
  131. {")", RPAREN, PAREN},
  132. {0, 0, 0}
  133. };
  134. char **t_wp;
  135. struct t_op const *t_wp_op;
  136. static enum token t_lex(char *);
  137. static enum token t_lex_type(char *);
  138. static int oexpr(enum token n);
  139. static int aexpr(enum token n);
  140. static int nexpr(enum token n);
  141. static int binop(void);
  142. static int primary(enum token n);
  143. static int filstat(char *nm, enum token mode);
  144. static int getn(const char *s);
  145. static int newerf(const char *, const char *);
  146. static int olderf(const char *, const char *);
  147. static int equalf(const char *, const char *);
  148. #define syntax(op,msg) {return 2;}
  149. int
  150. main(int argc, char *argv[])
  151. {
  152. int res;
  153. if (strcmp(argv[0], "[") == 0) {
  154. if (strcmp(argv[--argc], "]"))
  155. syntax(NULL, "missing ]");
  156. argv[argc] = NULL;
  157. }
  158. /* Implement special cases from POSIX.2, section 4.62.4 */
  159. switch (argc) {
  160. case 1:
  161. return 1;
  162. case 2:
  163. return (*argv[1] == '\0');
  164. case 3:
  165. if (argv[1][0] == '!' && argv[1][1] == '\0') {
  166. return !(*argv[2] == '\0');
  167. }
  168. break;
  169. case 4:
  170. if (argv[1][0] != '!' || argv[1][1] != '\0') {
  171. if (t_lex(argv[2]),
  172. t_wp_op && t_wp_op->op_type == BINOP) {
  173. t_wp = &argv[1];
  174. return (binop() == 0);
  175. }
  176. }
  177. break;
  178. case 5:
  179. if (argv[1][0] == '!' && argv[1][1] == '\0') {
  180. if (t_lex(argv[3]),
  181. t_wp_op && t_wp_op->op_type == BINOP) {
  182. t_wp = &argv[2];
  183. return !(binop() == 0);
  184. }
  185. }
  186. break;
  187. }
  188. t_wp = &argv[1];
  189. res = !oexpr(t_lex(*t_wp));
  190. if (*t_wp != NULL && *++t_wp != NULL)
  191. syntax(*t_wp, "unknown operand");
  192. return res;
  193. }
  194. static int
  195. oexpr(enum token n)
  196. {
  197. int res;
  198. res = aexpr(n);
  199. if (t_lex(*++t_wp) == BOR)
  200. return oexpr(t_lex(*++t_wp)) || res;
  201. t_wp--;
  202. return res;
  203. }
  204. static int
  205. aexpr(enum token n)
  206. {
  207. int res;
  208. res = nexpr(n);
  209. if (t_lex(*++t_wp) == BAND)
  210. return aexpr(t_lex(*++t_wp)) && res;
  211. t_wp--;
  212. return res;
  213. }
  214. static int
  215. nexpr(enum token n)
  216. {
  217. if (n == UNOT)
  218. return !nexpr(t_lex(*++t_wp));
  219. return primary(n);
  220. }
  221. static int
  222. primary(enum token n)
  223. {
  224. int res;
  225. if (n == EOI)
  226. syntax(NULL, "argument expected");
  227. if (n == LPAREN) {
  228. res = oexpr(t_lex(*++t_wp));
  229. if (t_lex(*++t_wp) != RPAREN)
  230. syntax(NULL, "closing paren expected");
  231. return res;
  232. }
  233. /*
  234. * We need this, if not binary operations with more than 4
  235. * arguments will always fall into unary.
  236. */
  237. if(t_lex_type(t_wp[1]) == BINOP) {
  238. t_lex(t_wp[1]);
  239. if (t_wp_op && t_wp_op->op_type == BINOP)
  240. return binop();
  241. }
  242. if (t_wp_op && t_wp_op->op_type == UNOP) {
  243. /* unary expression */
  244. if (*++t_wp == NULL)
  245. syntax(t_wp_op->op_text, "argument expected");
  246. switch (n) {
  247. case STREZ:
  248. return strlen(*t_wp) == 0;
  249. case STRNZ:
  250. return strlen(*t_wp) != 0;
  251. case FILTT:
  252. return isatty(getn(*t_wp));
  253. default:
  254. return filstat(*t_wp, n);
  255. }
  256. }
  257. return strlen(*t_wp) > 0;
  258. }
  259. static int
  260. binop(void)
  261. {
  262. const char *opnd1, *opnd2;
  263. struct t_op const *op;
  264. opnd1 = *t_wp;
  265. (void) t_lex(*++t_wp);
  266. op = t_wp_op;
  267. if ((opnd2 = *++t_wp) == NULL)
  268. syntax(op->op_text, "argument expected");
  269. switch (op->op_num) {
  270. case STREQ:
  271. return strcmp(opnd1, opnd2) == 0;
  272. case STRNE:
  273. return strcmp(opnd1, opnd2) != 0;
  274. case STRLT:
  275. return strcmp(opnd1, opnd2) < 0;
  276. case STRGT:
  277. return strcmp(opnd1, opnd2) > 0;
  278. case INTEQ:
  279. return getn(opnd1) == getn(opnd2);
  280. case INTNE:
  281. return getn(opnd1) != getn(opnd2);
  282. case INTGE:
  283. return getn(opnd1) >= getn(opnd2);
  284. case INTGT:
  285. return getn(opnd1) > getn(opnd2);
  286. case INTLE:
  287. return getn(opnd1) <= getn(opnd2);
  288. case INTLT:
  289. return getn(opnd1) < getn(opnd2);
  290. case FILNT:
  291. return newerf(opnd1, opnd2);
  292. case FILOT:
  293. return olderf(opnd1, opnd2);
  294. case FILEQ:
  295. return equalf(opnd1, opnd2);
  296. }
  297. /* NOTREACHED */
  298. return 1; /* to make compiler happy */
  299. }
  300. static enum token
  301. t_lex_type(char *s)
  302. {
  303. struct t_op const *op = ops;
  304. if (s == NULL)
  305. return -1;
  306. while (op->op_text) {
  307. if (strcmp(s, op->op_text) == 0)
  308. return op->op_type;
  309. op++;
  310. }
  311. return -1;
  312. }
  313. static int
  314. filstat(char *nm, enum token mode)
  315. {
  316. struct stat s;
  317. mode_t i;
  318. if (mode == FILSYM) {
  319. #ifdef S_IFLNK
  320. if (lstat(nm, &s) == 0) {
  321. i = S_IFLNK;
  322. goto filetype;
  323. }
  324. #endif
  325. return 0;
  326. }
  327. if (stat(nm, &s) != 0)
  328. return 0;
  329. switch (mode) {
  330. case FILRD:
  331. return access(nm, R_OK) == 0;
  332. case FILWR:
  333. return access(nm, W_OK) == 0;
  334. case FILEX:
  335. return access(nm, X_OK) == 0;
  336. case FILEXIST:
  337. return access(nm, F_OK) == 0;
  338. case FILREG:
  339. i = S_IFREG;
  340. goto filetype;
  341. case FILDIR:
  342. i = S_IFDIR;
  343. goto filetype;
  344. case FILCDEV:
  345. i = S_IFCHR;
  346. goto filetype;
  347. case FILBDEV:
  348. i = S_IFBLK;
  349. goto filetype;
  350. case FILFIFO:
  351. #ifdef S_IFIFO
  352. i = S_IFIFO;
  353. goto filetype;
  354. #else
  355. return 0;
  356. #endif
  357. case FILSOCK:
  358. #ifdef S_IFSOCK
  359. i = S_IFSOCK;
  360. goto filetype;
  361. #else
  362. return 0;
  363. #endif
  364. case FILSUID:
  365. i = S_ISUID;
  366. goto filebit;
  367. case FILSGID:
  368. i = S_ISGID;
  369. goto filebit;
  370. case FILSTCK:
  371. i = S_ISVTX;
  372. goto filebit;
  373. case FILGZ:
  374. return s.st_size > 0L;
  375. case FILUID:
  376. return s.st_uid == geteuid();
  377. case FILGID:
  378. return s.st_gid == getegid();
  379. default:
  380. return 1;
  381. }
  382. filetype:
  383. return ((s.st_mode & S_IFMT) == i);
  384. filebit:
  385. return ((s.st_mode & i) != 0);
  386. }
  387. static enum token
  388. t_lex(char *s)
  389. {
  390. struct t_op const *op = ops;
  391. if (s == 0) {
  392. t_wp_op = NULL;
  393. return EOI;
  394. }
  395. while (op->op_text) {
  396. if (strcmp(s, op->op_text) == 0) {
  397. t_wp_op = op;
  398. return op->op_num;
  399. }
  400. op++;
  401. }
  402. t_wp_op = NULL;
  403. return OPERAND;
  404. }
  405. /* atoi with error detection */
  406. static int
  407. getn(const char *s)
  408. {
  409. char *p;
  410. long r;
  411. errno = 0;
  412. r = strtol(s, &p, 10);
  413. if (errno != 0)
  414. syntax(NULL, "out of range");
  415. while (isspace(*p))
  416. p++;
  417. if (*p)
  418. syntax(NULL, "bad number");
  419. return (int) r;
  420. }
  421. static int
  422. newerf(const char *f1, const char *f2)
  423. {
  424. struct stat b1, b2;
  425. return (stat(f1, &b1) == 0 &&
  426. stat(f2, &b2) == 0 &&
  427. b1.st_mtime > b2.st_mtime);
  428. }
  429. static int
  430. olderf(const char *f1, const char *f2)
  431. {
  432. struct stat b1, b2;
  433. return (stat(f1, &b1) == 0 &&
  434. stat(f2, &b2) == 0 &&
  435. b1.st_mtime < b2.st_mtime);
  436. }
  437. static int
  438. equalf(const char *f1, const char *f2)
  439. {
  440. struct stat b1, b2;
  441. return (stat(f1, &b1) == 0 &&
  442. stat(f2, &b2) == 0 &&
  443. b1.st_dev == b2.st_dev &&
  444. b1.st_ino == b2.st_ino);
  445. }