mkdeps.c 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969
  1. /****************************************************************************
  2. * tools/mkdeps.c
  3. *
  4. * Licensed to the Apache Software Foundation (ASF) under one or more
  5. * contributor license agreements. See the NOTICE file distributed with
  6. * this work for additional information regarding copyright ownership. The
  7. * ASF licenses this file to you under the Apache License, Version 2.0 (the
  8. * "License"); you may not use this file except in compliance with the
  9. * License. You may obtain a copy of the License at
  10. *
  11. * http://www.apache.org/licenses/LICENSE-2.0
  12. *
  13. * Unless required by applicable law or agreed to in writing, software
  14. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  15. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  16. * License for the specific language governing permissions and limitations
  17. * under the License.
  18. *
  19. ****************************************************************************/
  20. /****************************************************************************
  21. * Included Files
  22. ****************************************************************************/
  23. #define _FILE_OFFSET_BITS 64
  24. #include <sys/stat.h>
  25. #include <stdbool.h>
  26. #include <stdio.h>
  27. #include <stdlib.h>
  28. #include <string.h>
  29. #include <limits.h>
  30. #include <ctype.h>
  31. #include <libgen.h>
  32. #include <errno.h>
  33. #include <dirent.h>
  34. #ifdef HOST_CYGWIN
  35. # include <sys/cygwin.h>
  36. #endif
  37. /****************************************************************************
  38. * Pre-processor Definitions
  39. ****************************************************************************/
  40. #define MAX_BUFFER (8192)
  41. #define MAX_EXPAND (2048)
  42. #define MAX_SHQUOTE (2048)
  43. /* MAX_PATH might be defined in stdlib.h */
  44. #if !defined(MAX_PATH)
  45. # define MAX_PATH (512)
  46. #endif
  47. /* NAME_MAX is typically defined in limits.h */
  48. #if !defined(NAME_MAX)
  49. /* FILENAME_MAX might be defined in stdio.h */
  50. # if defined(FILENAME_MAX)
  51. # define NAME_MAX FILENAME_MAX
  52. # else
  53. /* MAXNAMELEN might be defined in dirent.h */
  54. # if defined(MAXNAMLEN)
  55. # define NAME_MAX MAXNAMLEN
  56. # else
  57. /* Lets not let a silly think like this stop us... just make something up */
  58. # define NAME_MAX 256
  59. # endif
  60. # endif
  61. #endif
  62. /****************************************************************************
  63. * Private Types
  64. ****************************************************************************/
  65. enum slashmode_e
  66. {
  67. MODE_FSLASH = 0,
  68. MODE_BSLASH = 1,
  69. MODE_DBLBACK = 2
  70. };
  71. /****************************************************************************
  72. * Private Data
  73. ****************************************************************************/
  74. static char *g_cc = NULL;
  75. static char *g_cflags = NULL;
  76. static char *g_files = NULL;
  77. static char *g_altpath = NULL;
  78. static char *g_objpath = NULL;
  79. static char *g_suffix = ".o";
  80. static int g_debug = 0;
  81. static bool g_winnative = false;
  82. #ifdef HOST_CYGWIN
  83. static bool g_winpath = false;
  84. #endif
  85. static char g_command[MAX_BUFFER];
  86. static char g_path[MAX_PATH];
  87. #ifdef HOST_CYGWIN
  88. static char g_expand[MAX_EXPAND];
  89. static char g_dequoted[MAX_PATH];
  90. static char g_posixpath[MAX_PATH];
  91. #endif
  92. static char g_shquote[MAX_SHQUOTE];
  93. /****************************************************************************
  94. * Private Functions
  95. ****************************************************************************/
  96. /* MinGW does not seem to provide strtok_r */
  97. #ifndef HAVE_STRTOK_R
  98. static char *my_strtok_r(char *str, const char *delim, char **saveptr)
  99. {
  100. char *pbegin;
  101. char *pend = NULL;
  102. /* Decide if we are starting a new string or continuing from
  103. * the point we left off.
  104. */
  105. if (str)
  106. {
  107. pbegin = str;
  108. }
  109. else if (saveptr && *saveptr)
  110. {
  111. pbegin = *saveptr;
  112. }
  113. else
  114. {
  115. return NULL;
  116. }
  117. /* Find the beginning of the next token */
  118. for (;
  119. *pbegin && strchr(delim, *pbegin) != NULL;
  120. pbegin++);
  121. /* If we are at the end of the string with nothing
  122. * but delimiters found, then return NULL.
  123. */
  124. if (!*pbegin)
  125. {
  126. return NULL;
  127. }
  128. /* Find the end of the token */
  129. for (pend = pbegin + 1;
  130. *pend && strchr(delim, *pend) == NULL;
  131. pend++);
  132. /* pend either points to the end of the string or to
  133. * the first delimiter after the string.
  134. */
  135. if (*pend)
  136. {
  137. /* Turn the delimiter into a null terminator */
  138. *pend++ = '\0';
  139. }
  140. /* Save the pointer where we left off and return the
  141. * beginning of the token.
  142. */
  143. if (saveptr)
  144. {
  145. *saveptr = pend;
  146. }
  147. return pbegin;
  148. }
  149. #undef strtok_r
  150. #define strtok_r my_strtok_r
  151. #endif
  152. static void append(char **base, const char *str)
  153. {
  154. char *oldbase;
  155. char *newbase;
  156. int alloclen;
  157. oldbase = *base;
  158. if (!oldbase)
  159. {
  160. newbase = strdup(str);
  161. if (!newbase)
  162. {
  163. fprintf(stderr, "ERROR: Failed to strdup %s\n", str);
  164. exit(EXIT_FAILURE);
  165. }
  166. }
  167. else
  168. {
  169. alloclen = strlen(oldbase) + strlen(str) +
  170. sizeof((char) ' ') + sizeof((char) '\0');
  171. newbase = (char *)malloc(alloclen);
  172. if (!newbase)
  173. {
  174. fprintf(stderr, "ERROR: Failed to allocate %d bytes\n", alloclen);
  175. exit(EXIT_FAILURE);
  176. }
  177. snprintf(newbase, alloclen, "%s %s", oldbase, str);
  178. free(oldbase);
  179. }
  180. *base = newbase;
  181. }
  182. static void show_usage(const char *progname, const char *msg, int exitcode)
  183. {
  184. if (msg)
  185. {
  186. fprintf(stderr, "\n");
  187. fprintf(stderr, "%s:\n", msg);
  188. }
  189. fprintf(stderr, "\n");
  190. fprintf(stderr, "%s [OPTIONS] CC -- CFLAGS -- file [file [file...]]\n",
  191. progname);
  192. fprintf(stderr, "\n");
  193. fprintf(stderr, "Where:\n");
  194. fprintf(stderr, " CC\n");
  195. fprintf(stderr, " A variable number of arguments that define how to\n");
  196. fprintf(stderr, " execute the compiler\n");
  197. fprintf(stderr, " CFLAGS\n");
  198. fprintf(stderr, " The compiler compilation flags\n");
  199. fprintf(stderr, " file\n");
  200. fprintf(stderr, " One or more C files whose dependencies will be\n");
  201. fprintf(stderr, " checked. Each file is expected\n");
  202. fprintf(stderr, " to reside in the current directory unless\n");
  203. fprintf(stderr, " --dep-path is provided on the command line\n");
  204. fprintf(stderr, "\n");
  205. fprintf(stderr, "And [OPTIONS] include:\n");
  206. fprintf(stderr, " --dep-debug\n");
  207. fprintf(stderr, " Enable script debug\n");
  208. fprintf(stderr, " --dep-path <path>\n");
  209. fprintf(stderr, " Do not look in the current directory for the\n");
  210. fprintf(stderr, " file. Instead, look in <path> to see\n");
  211. fprintf(stderr, " if the file resides there. --dep-path may be\n");
  212. fprintf(stderr, " used multiple times to specify\n");
  213. fprintf(stderr, " multiple alternative location\n");
  214. fprintf(stderr, " --obj-path <path>\n");
  215. fprintf(stderr, " The final objects will not reside in this path\n");
  216. fprintf(stderr, " but, rather, at the path provided by\n");
  217. fprintf(stderr, " <path>. if provided multiple time, only the last\n");
  218. fprintf(stderr, " --obj-path will be used.\n");
  219. fprintf(stderr, " --obj-suffix <suffix>\n");
  220. fprintf(stderr, " If an object path is provided, then the extension\n");
  221. fprintf(stderr, " will be assumed to be .o. This\n");
  222. fprintf(stderr, " default suffix can be overridden with this\n");
  223. fprintf(stderr, " command line option.\n");
  224. fprintf(stderr, " --winnative\n");
  225. fprintf(stderr, " By default, a POSIX-style environment is assumed\n");
  226. fprintf(stderr, " (e.g., Linux, Cygwin, etc.) This option is\n");
  227. fprintf(stderr, " inform the tool that is working in a pure Windows\n");
  228. fprintf(stderr, " native environment.\n");
  229. #ifdef HOST_CYGWIN
  230. fprintf(stderr, " --winpaths\n");
  231. fprintf(stderr, " This option is useful when using a Windows native\n");
  232. fprintf(stderr, " toolchain in a POSIX environment (such such as\n");
  233. fprintf(stderr, " Cygwin). In this case, will CC\n");
  234. fprintf(stderr, " generates dependency lists using Windows paths\n");
  235. fprintf(stderr, " (e.g., C:\\blablah\\blabla).\n");
  236. #endif
  237. fprintf(stderr, " --help\n");
  238. fprintf(stderr, " Shows this message and exits\n");
  239. exit(exitcode);
  240. }
  241. /****************************************************************************
  242. * Name: do_shquote
  243. *
  244. * Description:
  245. * Escape the given string for use with the shell.
  246. *
  247. * The idea was taken from:
  248. * https://netbsd.gw.com/cgi-bin/man-cgi?shquote++NetBSD-current
  249. * However, this implementation doesn't try to elide extraneous quotes.
  250. ****************************************************************************/
  251. static const char *do_shquote(const char *argument)
  252. {
  253. const char *src;
  254. char *dest;
  255. int len;
  256. src = argument;
  257. dest = g_shquote;
  258. len = 0;
  259. if (len < sizeof(g_shquote))
  260. {
  261. *dest++ = '\'';
  262. len++;
  263. }
  264. while (*src && len < sizeof(g_shquote))
  265. {
  266. if (*src == '\'')
  267. {
  268. /* Expand single quote to '\'' */
  269. if (len + 4 > sizeof(g_shquote))
  270. {
  271. break;
  272. }
  273. src++;
  274. memcpy(dest, "\'\\\'\'", 4);
  275. dest += 4;
  276. len += 4;
  277. }
  278. else
  279. {
  280. *dest++ = *src++;
  281. len++;
  282. }
  283. }
  284. if (*src || len + 2 > sizeof(g_shquote))
  285. {
  286. fprintf(stderr,
  287. "ERROR: Truncated during shquote string is too long"
  288. "[%zu/%zu]\n", strlen(argument), sizeof(g_shquote));
  289. exit(EXIT_FAILURE);
  290. }
  291. *dest++ = '\'';
  292. *dest = '\0';
  293. return g_shquote;
  294. }
  295. static void parse_args(int argc, char **argv)
  296. {
  297. char *args = NULL;
  298. int argidx;
  299. int group = 0;
  300. /* Always look in the current directory */
  301. g_altpath = strdup(".");
  302. /* Accumulate CFLAGS up to "--" */
  303. for (argidx = 1; argidx < argc; argidx++)
  304. {
  305. if (strcmp(argv[argidx], "--") == 0)
  306. {
  307. g_cc = g_cflags;
  308. g_cflags = args;
  309. args = NULL;
  310. group++;
  311. }
  312. else if (strcmp(argv[argidx], "--dep-debug") == 0)
  313. {
  314. g_debug++;
  315. }
  316. else if (strcmp(argv[argidx], "--dep-path") == 0)
  317. {
  318. argidx++;
  319. if (argidx >= argc)
  320. {
  321. show_usage(argv[0], "ERROR: Missing argument to --dep-path",
  322. EXIT_FAILURE);
  323. }
  324. if (args)
  325. {
  326. append(&args, argv[argidx]);
  327. }
  328. else
  329. {
  330. append(&g_altpath, argv[argidx]);
  331. }
  332. }
  333. else if (strcmp(argv[argidx], "--obj-path") == 0)
  334. {
  335. argidx++;
  336. if (argidx >= argc)
  337. {
  338. show_usage(argv[0], "ERROR: Missing argument to --obj-path",
  339. EXIT_FAILURE);
  340. }
  341. g_objpath = argv[argidx];
  342. }
  343. else if (strcmp(argv[argidx], "--obj-suffix") == 0)
  344. {
  345. argidx++;
  346. if (argidx >= argc)
  347. {
  348. show_usage(argv[0], "ERROR: Missing argument to --obj-suffix",
  349. EXIT_FAILURE);
  350. }
  351. g_suffix = argv[argidx];
  352. }
  353. else if (strcmp(argv[argidx], "--winnative") == 0)
  354. {
  355. g_winnative = true;
  356. }
  357. #ifdef HOST_CYGWIN
  358. else if (strcmp(argv[argidx], "--winpath") == 0)
  359. {
  360. g_winpath = true;
  361. }
  362. #endif
  363. else if (strcmp(argv[argidx], "--help") == 0)
  364. {
  365. show_usage(argv[0], NULL, EXIT_SUCCESS);
  366. }
  367. else
  368. {
  369. const char *arg = argv[argidx];
  370. /* This condition means "perform shquote for
  371. * g_cflags, but not g_cc or g_files".
  372. *
  373. * It isn't safe to escape g_cc because, for some reasons,
  374. * Makefile passes it as a single argument like:
  375. *
  376. * $(MKDEP) $(DEPPATH) "$(CC)" -- $(CFLAGS) -- $(SRCS)
  377. *
  378. * It isn't safe to escape g_files because
  379. * do_dependency() uses them as bare filenames as well.
  380. * (In addition to passing them to system().)
  381. */
  382. if (group == 1)
  383. {
  384. arg = do_shquote(arg);
  385. }
  386. append(&args, arg);
  387. }
  388. }
  389. /* The final thing accumulated is the list of files */
  390. g_files = args;
  391. if (g_debug)
  392. {
  393. fprintf(stderr, "SELECTIONS\n");
  394. fprintf(stderr, " CC : [%s]\n",
  395. g_cc ? g_cc : "(None)");
  396. fprintf(stderr, " CFLAGS : [%s]\n",
  397. g_cflags ? g_cflags : "(None)");
  398. fprintf(stderr, " FILES : [%s]\n",
  399. g_files ? g_files : "(None)");
  400. fprintf(stderr, " PATHS : [%s]\n",
  401. g_altpath ? g_altpath : "(None)");
  402. if (g_objpath)
  403. {
  404. fprintf(stderr, " OBJDIR : [%s]\n", g_objpath);
  405. fprintf(stderr, " SUFFIX : [%s]\n", g_suffix);
  406. }
  407. else
  408. {
  409. fprintf(stderr, " OBJDIR : (None)\n");
  410. }
  411. #ifdef HOST_CYGWIN
  412. fprintf(stderr, " Windows Paths : [%s]\n",
  413. g_winpath ? "TRUE" : "FALSE");
  414. #endif
  415. fprintf(stderr, " Windows Native : [%s]\n",
  416. g_winnative ? "TRUE" : "FALSE");
  417. }
  418. /* Check for required parameters */
  419. if (!g_cc)
  420. {
  421. show_usage(argv[0], "ERROR: No compiler specified", EXIT_FAILURE);
  422. }
  423. if (!g_files)
  424. {
  425. /* Don't report an error --
  426. * this happens normally in some configurations
  427. */
  428. printf("# No files specified for dependency generation\n");
  429. exit(EXIT_SUCCESS);
  430. }
  431. #ifdef HOST_CYGWIN
  432. if (g_winnative && g_winpath)
  433. {
  434. show_usage(argv[0],
  435. "ERROR: Both --winnative and --winpath makes no sense",
  436. EXIT_FAILURE);
  437. }
  438. #endif
  439. }
  440. static const char *do_expand(const char *argument)
  441. {
  442. #ifdef HOST_CYGWIN
  443. if (g_winpath)
  444. {
  445. const char *src;
  446. char *dest;
  447. int len;
  448. src = argument;
  449. dest = g_expand;
  450. len = 0;
  451. while (*src && len < MAX_EXPAND)
  452. {
  453. if (*src == '\\')
  454. {
  455. /* Copy backslash */
  456. *dest++ = *src++;
  457. if (++len >= MAX_EXPAND)
  458. {
  459. break;
  460. }
  461. /* Already expanded? */
  462. if (*src == '\\')
  463. {
  464. /* Yes... just copy all consecutive backslashes */
  465. do
  466. {
  467. *dest++ = *src++;
  468. if (++len >= MAX_EXPAND)
  469. {
  470. break;
  471. }
  472. }
  473. while (*src == '\\');
  474. }
  475. else
  476. {
  477. /* No.. expeand */
  478. *dest++ = '\\';
  479. if (++len >= MAX_EXPAND)
  480. {
  481. break;
  482. }
  483. }
  484. }
  485. else
  486. {
  487. *dest++ = *src++;
  488. len++;
  489. }
  490. }
  491. if (*src)
  492. {
  493. fprintf(stderr,
  494. "ERROR: Truncated during expansion string is too long"
  495. "[%zu/%u]\n", strlen(argument), MAX_EXPAND);
  496. exit(EXIT_FAILURE);
  497. }
  498. *dest = '\0';
  499. return g_expand;
  500. }
  501. else
  502. #endif
  503. {
  504. return argument;
  505. }
  506. }
  507. #ifdef HOST_CYGWIN
  508. static bool dequote_path(const char *winpath)
  509. {
  510. char *dest = g_dequoted;
  511. const char *src = winpath;
  512. int len = 0;
  513. bool quoted = false;
  514. while (*src && len < MAX_PATH)
  515. {
  516. if (*src != '\\' || (src[1] != ' ' && src[1] != '(' && src[1] != ')'))
  517. {
  518. *dest++ = *src;
  519. len++;
  520. }
  521. else
  522. {
  523. quoted = true;
  524. }
  525. src++;
  526. }
  527. if (*src || len >= MAX_PATH)
  528. {
  529. fprintf(stderr, "# ERROR: Path truncated\n");
  530. exit(EXIT_FAILURE);
  531. }
  532. *dest = '\0';
  533. return quoted;
  534. }
  535. #endif
  536. static const char *convert_path(const char *path)
  537. {
  538. #ifdef HOST_CYGWIN
  539. if (g_winpath)
  540. {
  541. const char *retptr;
  542. ssize_t size;
  543. ssize_t ret;
  544. bool quoted;
  545. quoted = dequote_path(path);
  546. if (quoted)
  547. {
  548. retptr = g_posixpath;
  549. }
  550. else
  551. {
  552. retptr = &g_posixpath[1];
  553. }
  554. size = cygwin_conv_path(CCP_POSIX_TO_WIN_A | CCP_RELATIVE, g_dequoted,
  555. NULL, 0);
  556. if (size > (MAX_PATH - 3))
  557. {
  558. fprintf(stderr, "# ERROR: POSIX path too long: %zd\n", size);
  559. exit(EXIT_FAILURE);
  560. }
  561. ret = cygwin_conv_path(CCP_POSIX_TO_WIN_A | CCP_RELATIVE, g_dequoted,
  562. &g_posixpath[1], MAX_PATH - 3);
  563. if (ret < 0)
  564. {
  565. fprintf(stderr, "# ERROR: cygwin_conv_path '%s' failed: %s\n",
  566. g_dequoted, strerror(errno));
  567. exit(EXIT_FAILURE);
  568. }
  569. if (quoted)
  570. {
  571. size++;
  572. g_posixpath[0] = '"';
  573. g_posixpath[size] = '"';
  574. }
  575. g_posixpath[size + 1] = '\0';
  576. return retptr;
  577. }
  578. else
  579. #endif
  580. {
  581. return path;
  582. }
  583. }
  584. static void do_dependency(const char *file)
  585. {
  586. static const char moption[] = " -M ";
  587. struct stat buf;
  588. char *alloc;
  589. char *altpath;
  590. char *path;
  591. char *lasts;
  592. char separator;
  593. int cmdlen;
  594. int pathlen;
  595. int filelen;
  596. int totallen;
  597. int ret;
  598. /* Initialize the separator */
  599. #ifdef HOST_CYGWIN
  600. separator = (g_winnative || g_winpath) ? '\\' : '/';
  601. #else
  602. separator = g_winnative ? '\\' : '/';
  603. #endif
  604. /* Copy the compiler into the command buffer */
  605. cmdlen = strlen(g_cc);
  606. if (cmdlen >= MAX_BUFFER)
  607. {
  608. fprintf(stderr, "ERROR: Compiler string is too long [%d/%d]: %s\n",
  609. cmdlen, MAX_BUFFER, g_cc);
  610. exit(EXIT_FAILURE);
  611. }
  612. strcpy(g_command, g_cc);
  613. /* Copy " -MT " */
  614. if (g_objpath)
  615. {
  616. char tmp[NAME_MAX + 6];
  617. char *dupname;
  618. char *objname;
  619. char *dotptr;
  620. const char *expanded;
  621. dupname = strdup(file);
  622. if (!dupname)
  623. {
  624. fprintf(stderr, "ERROR: Failed to dup: %s\n", file);
  625. exit(EXIT_FAILURE);
  626. }
  627. objname = basename(dupname);
  628. dotptr = strrchr(objname, '.');
  629. if (dotptr)
  630. {
  631. *dotptr = '\0';
  632. }
  633. snprintf(tmp, NAME_MAX + 6, " -MT %s%c%s%s ",
  634. g_objpath, separator, objname, g_suffix);
  635. expanded = do_expand(tmp);
  636. cmdlen += strlen(expanded);
  637. if (cmdlen >= MAX_BUFFER)
  638. {
  639. fprintf(stderr, "ERROR: Option string is too long [%d/%d]: %s\n",
  640. cmdlen, MAX_BUFFER, moption);
  641. exit(EXIT_FAILURE);
  642. }
  643. strcat(g_command, expanded);
  644. free(dupname);
  645. }
  646. /* Copy " -M " */
  647. cmdlen += strlen(moption);
  648. if (cmdlen >= MAX_BUFFER)
  649. {
  650. fprintf(stderr, "ERROR: Option string is too long [%d/%d]: %s\n",
  651. cmdlen, MAX_BUFFER, moption);
  652. exit(EXIT_FAILURE);
  653. }
  654. strcat(g_command, moption);
  655. /* Copy the CFLAGS into the command buffer */
  656. if (g_cflags)
  657. {
  658. const char *expanded;
  659. expanded = do_expand(g_cflags);
  660. cmdlen += strlen(expanded);
  661. if (cmdlen >= MAX_BUFFER)
  662. {
  663. fprintf(stderr, "ERROR: CFLAG string is too long [%d/%d]: %s\n",
  664. cmdlen, MAX_BUFFER, g_cflags);
  665. exit(EXIT_FAILURE);
  666. }
  667. strcat(g_command, expanded);
  668. }
  669. /* Add a space */
  670. g_command[cmdlen] = ' ';
  671. cmdlen++;
  672. g_command[cmdlen] = '\0';
  673. /* Make a copy of g_altpath. We need to do this because at least the
  674. * version of strtok_r above does modify it.
  675. */
  676. alloc = strdup(g_altpath);
  677. if (!alloc)
  678. {
  679. fprintf(stderr, "ERROR: Failed to strdup paths\n");
  680. exit(EXIT_FAILURE);
  681. }
  682. altpath = alloc;
  683. /* Try each path. This loop will continue until each path has been tried
  684. * (failure) or until stat() finds the file
  685. */
  686. while ((path = strtok_r(altpath, " ", &lasts)) != NULL)
  687. {
  688. const char *expanded;
  689. const char *converted;
  690. /* Create a full path to the file */
  691. pathlen = strlen(path);
  692. if (pathlen >= MAX_PATH)
  693. {
  694. fprintf(stderr, "ERROR: Path is too long [%d/%d]: %s\n",
  695. pathlen, MAX_PATH, path);
  696. exit(EXIT_FAILURE);
  697. }
  698. strcpy(g_path, path);
  699. if (g_path[pathlen] != '\0')
  700. {
  701. fprintf(stderr, "ERROR: Missing NUL terminator\n");
  702. exit(EXIT_FAILURE);
  703. }
  704. if (g_path[pathlen - 1] != separator)
  705. {
  706. g_path[pathlen] = separator;
  707. g_path[pathlen + 1] = '\0';
  708. pathlen++;
  709. }
  710. filelen = strlen(file);
  711. pathlen += filelen;
  712. if (pathlen >= MAX_PATH)
  713. {
  714. fprintf(stderr, "ERROR: Path+file is too long [%d/%d]\n",
  715. pathlen, MAX_PATH);
  716. exit(EXIT_FAILURE);
  717. }
  718. strcat(g_path, file);
  719. /* Check that a file actually exists at this path */
  720. if (g_debug)
  721. {
  722. fprintf(stderr, "Trying path=%s file=%s fullpath=%s\n",
  723. path, file, g_path);
  724. }
  725. converted = convert_path(g_path);
  726. ret = stat(converted, &buf);
  727. if (ret < 0)
  728. {
  729. altpath = NULL;
  730. continue;
  731. }
  732. if (!S_ISREG(buf.st_mode))
  733. {
  734. fprintf(stderr,
  735. "ERROR: File %s exists but is not a regular file\n",
  736. g_path);
  737. exit(EXIT_FAILURE);
  738. }
  739. /* Append the expanded path to the command */
  740. expanded = do_expand(g_path);
  741. pathlen = strlen(expanded);
  742. totallen = cmdlen + pathlen;
  743. if (totallen >= MAX_BUFFER)
  744. {
  745. fprintf(stderr, "ERROR: Path string is too long [%d/%d]: %s\n",
  746. totallen, MAX_BUFFER, g_path);
  747. exit(EXIT_FAILURE);
  748. }
  749. strcat(g_command, expanded);
  750. /* Okay.. we have everything. Create the dependency. One a failure
  751. * to start the compiler, system() will return -1; Otherwise, the
  752. * returned value from the compiler is in WEXITSTATUS(ret).
  753. */
  754. if (g_debug)
  755. {
  756. fprintf(stderr, "Executing: %s\n", g_command);
  757. }
  758. ret = system(g_command);
  759. #ifdef WEXITSTATUS
  760. if (ret < 0 || WEXITSTATUS(ret) != 0)
  761. {
  762. if (ret < 0)
  763. {
  764. fprintf(stderr, "ERROR: system failed: %s\n", strerror(errno));
  765. }
  766. else
  767. {
  768. fprintf(stderr,
  769. "ERROR: %s failed: %d\n", g_cc, WEXITSTATUS(ret));
  770. }
  771. fprintf(stderr, " command: %s\n", g_command);
  772. exit(EXIT_FAILURE);
  773. }
  774. #else
  775. if (ret < 0)
  776. {
  777. fprintf(stderr, "ERROR: system failed: %s\n", strerror(errno));
  778. fprintf(stderr, " command: %s\n", g_command);
  779. exit(EXIT_FAILURE);
  780. }
  781. #endif
  782. /* We don't really know that the command succeeded...
  783. * Let's assume that it did
  784. */
  785. free(alloc);
  786. return;
  787. }
  788. printf("# ERROR: File \"%s\" not found at any location\n", file);
  789. exit(EXIT_FAILURE);
  790. }
  791. /****************************************************************************
  792. * Public Functions
  793. ****************************************************************************/
  794. int main(int argc, char **argv, char **envp)
  795. {
  796. char *lasts;
  797. char *files;
  798. char *file;
  799. /* Parse command line parameters */
  800. parse_args(argc, argv);
  801. /* Then generate dependencies for each path on the command line. NOTE
  802. * strtok_r will clobber the files list. But that is okay because we are
  803. * only going to traverse it once.
  804. */
  805. files = g_files;
  806. while ((file = strtok_r(files, " ", &lasts)) != NULL)
  807. {
  808. /* Check if we need to do path conversions for a Windows-natvive tool
  809. * being using in a POSIX/Cygwin environment.
  810. */
  811. do_dependency(file);
  812. files = NULL;
  813. }
  814. return EXIT_SUCCESS;
  815. }