test.c 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486
  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_types 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 main(int argc, char *argv[])
  150. {
  151. int res;
  152. if (strcmp(argv[0], "[") == 0) {
  153. if (strcmp(argv[--argc], "]"))
  154. syntax(NULL, "missing ]");
  155. argv[argc] = NULL;
  156. }
  157. /* Implement special cases from POSIX.2, section 4.62.4 */
  158. switch (argc) {
  159. case 1:
  160. return 1;
  161. case 2:
  162. return (*argv[1] == '\0');
  163. case 3:
  164. if (argv[1][0] == '!' && argv[1][1] == '\0') {
  165. return !(*argv[2] == '\0');
  166. }
  167. break;
  168. case 4:
  169. if (argv[1][0] != '!' || argv[1][1] != '\0') {
  170. if (t_lex(argv[2]),
  171. t_wp_op && t_wp_op->op_type == BINOP) {
  172. t_wp = &argv[1];
  173. return (binop() == 0);
  174. }
  175. }
  176. break;
  177. case 5:
  178. if (argv[1][0] == '!' && argv[1][1] == '\0') {
  179. if (t_lex(argv[3]),
  180. t_wp_op && t_wp_op->op_type == BINOP) {
  181. t_wp = &argv[2];
  182. return !(binop() == 0);
  183. }
  184. }
  185. break;
  186. }
  187. t_wp = &argv[1];
  188. res = !oexpr(t_lex(*t_wp));
  189. if (*t_wp != NULL && *++t_wp != NULL)
  190. syntax(*t_wp, "unknown operand");
  191. return res;
  192. }
  193. static int oexpr(enum token n)
  194. {
  195. int res;
  196. res = aexpr(n);
  197. if (t_lex(*++t_wp) == BOR)
  198. return oexpr(t_lex(*++t_wp)) || res;
  199. t_wp--;
  200. return res;
  201. }
  202. static int aexpr(enum token n)
  203. {
  204. int res;
  205. res = nexpr(n);
  206. if (t_lex(*++t_wp) == BAND)
  207. return aexpr(t_lex(*++t_wp)) && res;
  208. t_wp--;
  209. return res;
  210. }
  211. static int nexpr(enum token n)
  212. {
  213. if (n == UNOT)
  214. return !nexpr(t_lex(*++t_wp));
  215. return primary(n);
  216. }
  217. static int primary(enum token n)
  218. {
  219. int res;
  220. if (n == EOI)
  221. syntax(NULL, "argument expected");
  222. if (n == LPAREN) {
  223. res = oexpr(t_lex(*++t_wp));
  224. if (t_lex(*++t_wp) != RPAREN)
  225. syntax(NULL, "closing paren expected");
  226. return res;
  227. }
  228. /*
  229. * We need this, if not binary operations with more than 4
  230. * arguments will always fall into unary.
  231. */
  232. if (t_lex_type(t_wp[1]) == BINOP) {
  233. t_lex(t_wp[1]);
  234. if (t_wp_op && t_wp_op->op_type == BINOP)
  235. return binop();
  236. }
  237. if (t_wp_op && t_wp_op->op_type == UNOP) {
  238. /* unary expression */
  239. if (*++t_wp == NULL)
  240. syntax(t_wp_op->op_text, "argument expected");
  241. switch (n) {
  242. case STREZ:
  243. return strlen(*t_wp) == 0;
  244. case STRNZ:
  245. return strlen(*t_wp) != 0;
  246. case FILTT:
  247. return isatty(getn(*t_wp));
  248. default:
  249. return filstat(*t_wp, n);
  250. }
  251. }
  252. return strlen(*t_wp) > 0;
  253. }
  254. static int binop(void)
  255. {
  256. const char *opnd1, *opnd2;
  257. struct t_op const *op;
  258. opnd1 = *t_wp;
  259. (void)t_lex(*++t_wp);
  260. op = t_wp_op;
  261. if (!op)
  262. return 1;
  263. if ((opnd2 = *++t_wp) == NULL)
  264. syntax(op->op_text, "argument expected");
  265. switch (op->op_num) {
  266. case STREQ:
  267. return strcmp(opnd1, opnd2) == 0;
  268. case STRNE:
  269. return strcmp(opnd1, opnd2) != 0;
  270. case STRLT:
  271. return strcmp(opnd1, opnd2) < 0;
  272. case STRGT:
  273. return strcmp(opnd1, opnd2) > 0;
  274. case INTEQ:
  275. return getn(opnd1) == getn(opnd2);
  276. case INTNE:
  277. return getn(opnd1) != getn(opnd2);
  278. case INTGE:
  279. return getn(opnd1) >= getn(opnd2);
  280. case INTGT:
  281. return getn(opnd1) > getn(opnd2);
  282. case INTLE:
  283. return getn(opnd1) <= getn(opnd2);
  284. case INTLT:
  285. return getn(opnd1) < getn(opnd2);
  286. case FILNT:
  287. return newerf(opnd1, opnd2);
  288. case FILOT:
  289. return olderf(opnd1, opnd2);
  290. case FILEQ:
  291. return equalf(opnd1, opnd2);
  292. }
  293. /* NOTREACHED */
  294. return 1; /* to make compiler happy */
  295. }
  296. static enum token_types t_lex_type(char *s)
  297. {
  298. struct t_op const *op = ops;
  299. if (s == NULL)
  300. return -1;
  301. while (op->op_text) {
  302. if (strcmp(s, op->op_text) == 0)
  303. return op->op_type;
  304. op++;
  305. }
  306. return -1;
  307. }
  308. static int filstat(char *nm, enum token mode)
  309. {
  310. struct stat s;
  311. mode_t i;
  312. if (mode == FILSYM) {
  313. #ifdef S_IFLNK
  314. if (lstat(nm, &s) == 0) {
  315. i = S_IFLNK;
  316. goto filetype;
  317. }
  318. #endif
  319. return 0;
  320. }
  321. if (stat(nm, &s) != 0)
  322. return 0;
  323. switch (mode) {
  324. case FILRD:
  325. return access(nm, R_OK) == 0;
  326. case FILWR:
  327. return access(nm, W_OK) == 0;
  328. case FILEX:
  329. return access(nm, X_OK) == 0;
  330. case FILEXIST:
  331. return access(nm, F_OK) == 0;
  332. case FILREG:
  333. i = S_IFREG;
  334. goto filetype;
  335. case FILDIR:
  336. i = S_IFDIR;
  337. goto filetype;
  338. case FILCDEV:
  339. i = S_IFCHR;
  340. goto filetype;
  341. case FILBDEV:
  342. i = S_IFBLK;
  343. goto filetype;
  344. case FILFIFO:
  345. #ifdef S_IFIFO
  346. i = S_IFIFO;
  347. goto filetype;
  348. #else
  349. return 0;
  350. #endif
  351. case FILSOCK:
  352. #ifdef S_IFSOCK
  353. i = S_IFSOCK;
  354. goto filetype;
  355. #else
  356. return 0;
  357. #endif
  358. case FILSUID:
  359. i = S_ISUID;
  360. goto filebit;
  361. case FILSGID:
  362. i = S_ISGID;
  363. goto filebit;
  364. case FILSTCK:
  365. i = S_ISVTX;
  366. goto filebit;
  367. case FILGZ:
  368. return s.st_size > 0L;
  369. case FILUID:
  370. return s.st_uid == geteuid();
  371. case FILGID:
  372. return s.st_gid == getegid();
  373. default:
  374. return 1;
  375. }
  376. filetype:
  377. return ((s.st_mode & S_IFMT) == i);
  378. filebit:
  379. return ((s.st_mode & i) != 0);
  380. }
  381. static enum token t_lex(char *s)
  382. {
  383. struct t_op const *op = ops;
  384. if (s == 0) {
  385. t_wp_op = NULL;
  386. return EOI;
  387. }
  388. while (op->op_text) {
  389. if (strcmp(s, op->op_text) == 0) {
  390. t_wp_op = op;
  391. return op->op_num;
  392. }
  393. op++;
  394. }
  395. t_wp_op = NULL;
  396. return OPERAND;
  397. }
  398. /* atoi with error detection */
  399. static int getn(const char *s)
  400. {
  401. char *p;
  402. long r;
  403. errno = 0;
  404. r = strtol(s, &p, 10);
  405. if (errno != 0)
  406. syntax(NULL, "out of range");
  407. while (isspace(*p))
  408. p++;
  409. if (*p)
  410. syntax(NULL, "bad number");
  411. return (int)r;
  412. }
  413. static int newerf(const char *f1, const char *f2)
  414. {
  415. struct stat b1, b2;
  416. return (stat(f1, &b1) == 0 &&
  417. stat(f2, &b2) == 0 && b1.st_mtime > b2.st_mtime);
  418. }
  419. static int olderf(const char *f1, const char *f2)
  420. {
  421. struct stat b1, b2;
  422. return (stat(f1, &b1) == 0 &&
  423. stat(f2, &b2) == 0 && b1.st_mtime < b2.st_mtime);
  424. }
  425. static int equalf(const char *f1, const char *f2)
  426. {
  427. struct stat b1, b2;
  428. return (stat(f1, &b1) == 0 &&
  429. stat(f2, &b2) == 0 &&
  430. b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino);
  431. }