1*c54f35caSApple OSS Distributions /*
2*c54f35caSApple OSS Distributions * Copyright (c) 2013 Apple Inc. All rights reserved.
3*c54f35caSApple OSS Distributions *
4*c54f35caSApple OSS Distributions * @APPLE_LICENSE_HEADER_START@
5*c54f35caSApple OSS Distributions *
6*c54f35caSApple OSS Distributions * This file contains Original Code and/or Modifications of Original Code
7*c54f35caSApple OSS Distributions * as defined in and that are subject to the Apple Public Source License
8*c54f35caSApple OSS Distributions * Version 2.0 (the 'License'). You may not use this file except in
9*c54f35caSApple OSS Distributions * compliance with the License. Please obtain a copy of the License at
10*c54f35caSApple OSS Distributions * http://www.opensource.apple.com/apsl/ and read it before using this
11*c54f35caSApple OSS Distributions * file.
12*c54f35caSApple OSS Distributions *
13*c54f35caSApple OSS Distributions * The Original Code and all software distributed under the License are
14*c54f35caSApple OSS Distributions * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15*c54f35caSApple OSS Distributions * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16*c54f35caSApple OSS Distributions * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17*c54f35caSApple OSS Distributions * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18*c54f35caSApple OSS Distributions * Please see the License for the specific language governing rights and
19*c54f35caSApple OSS Distributions * limitations under the License.
20*c54f35caSApple OSS Distributions *
21*c54f35caSApple OSS Distributions * @APPLE_LICENSE_HEADER_END@
22*c54f35caSApple OSS Distributions */
23*c54f35caSApple OSS Distributions
24*c54f35caSApple OSS Distributions /*
25*c54f35caSApple OSS Distributions * json_compilation_db is a helper tool that takes a compiler invocation, and
26*c54f35caSApple OSS Distributions * appends it in JSON format to the specified database.
27*c54f35caSApple OSS Distributions */
28*c54f35caSApple OSS Distributions
29*c54f35caSApple OSS Distributions #include <stdio.h>
30*c54f35caSApple OSS Distributions #include <stdlib.h>
31*c54f35caSApple OSS Distributions #include <unistd.h>
32*c54f35caSApple OSS Distributions #include <string.h>
33*c54f35caSApple OSS Distributions #include <stdbool.h>
34*c54f35caSApple OSS Distributions #include <errno.h>
35*c54f35caSApple OSS Distributions #include <err.h>
36*c54f35caSApple OSS Distributions #include <sysexits.h>
37*c54f35caSApple OSS Distributions
38*c54f35caSApple OSS Distributions #include <sys/stat.h>
39*c54f35caSApple OSS Distributions #include <sys/fcntl.h>
40*c54f35caSApple OSS Distributions #include <sys/param.h>
41*c54f35caSApple OSS Distributions
42*c54f35caSApple OSS Distributions void usage(void);
43*c54f35caSApple OSS Distributions char *escape_string(const char *);
44*c54f35caSApple OSS Distributions
45*c54f35caSApple OSS Distributions /*
46*c54f35caSApple OSS Distributions * We support appending to two databases.
47*c54f35caSApple OSS Distributions *
48*c54f35caSApple OSS Distributions * 0-byte: ""
49*c54f35caSApple OSS Distributions *
50*c54f35caSApple OSS Distributions * or
51*c54f35caSApple OSS Distributions *
52*c54f35caSApple OSS Distributions * "["
53*c54f35caSApple OSS Distributions * "{"
54*c54f35caSApple OSS Distributions * " ..."
55*c54f35caSApple OSS Distributions * "}"
56*c54f35caSApple OSS Distributions * "]"
57*c54f35caSApple OSS Distributions */
58*c54f35caSApple OSS Distributions
59*c54f35caSApple OSS Distributions int
main(int argc,char * argv[])60*c54f35caSApple OSS Distributions main(int argc, char * argv[])
61*c54f35caSApple OSS Distributions {
62*c54f35caSApple OSS Distributions struct stat sb;
63*c54f35caSApple OSS Distributions int ret;
64*c54f35caSApple OSS Distributions int dstfd;
65*c54f35caSApple OSS Distributions FILE *dst = NULL;
66*c54f35caSApple OSS Distributions const char *json_output = NULL;
67*c54f35caSApple OSS Distributions const char *cwd = NULL;
68*c54f35caSApple OSS Distributions const char *input_file = NULL;
69*c54f35caSApple OSS Distributions char start[2];
70*c54f35caSApple OSS Distributions size_t read_bytes;
71*c54f35caSApple OSS Distributions int i;
72*c54f35caSApple OSS Distributions size_t input_file_len;
73*c54f35caSApple OSS Distributions
74*c54f35caSApple OSS Distributions if (argc < 5) {
75*c54f35caSApple OSS Distributions usage();
76*c54f35caSApple OSS Distributions }
77*c54f35caSApple OSS Distributions
78*c54f35caSApple OSS Distributions json_output = argv[1];
79*c54f35caSApple OSS Distributions cwd = argv[2];
80*c54f35caSApple OSS Distributions input_file = argv[3];
81*c54f35caSApple OSS Distributions
82*c54f35caSApple OSS Distributions argv += 4;
83*c54f35caSApple OSS Distributions argc -= 4;
84*c54f35caSApple OSS Distributions
85*c54f35caSApple OSS Distributions input_file_len = strlen(input_file);
86*c54f35caSApple OSS Distributions if (!(input_file_len > 2 && 0 == strcmp(".c", input_file + input_file_len - 2)) &&
87*c54f35caSApple OSS Distributions !(input_file_len > 3 && 0 == strcmp(".cp", input_file + input_file_len - 3)) &&
88*c54f35caSApple OSS Distributions !(input_file_len > 4 && 0 == strcmp(".cpp", input_file + input_file_len - 4))) {
89*c54f35caSApple OSS Distributions /* Not a C/C++ file, just skip it */
90*c54f35caSApple OSS Distributions return 0;
91*c54f35caSApple OSS Distributions }
92*c54f35caSApple OSS Distributions
93*c54f35caSApple OSS Distributions dstfd = open(json_output, O_RDWR | O_CREAT | O_EXLOCK, DEFFILEMODE);
94*c54f35caSApple OSS Distributions if (dstfd < 0) {
95*c54f35caSApple OSS Distributions err(EX_NOINPUT, "open(%s)", json_output);
96*c54f35caSApple OSS Distributions }
97*c54f35caSApple OSS Distributions
98*c54f35caSApple OSS Distributions ret = fstat(dstfd, &sb);
99*c54f35caSApple OSS Distributions if (ret < 0) {
100*c54f35caSApple OSS Distributions err(EX_NOINPUT, "fstat(%s)", json_output);
101*c54f35caSApple OSS Distributions }
102*c54f35caSApple OSS Distributions
103*c54f35caSApple OSS Distributions if (!S_ISREG(sb.st_mode)) {
104*c54f35caSApple OSS Distributions err(EX_USAGE, "%s is not a regular file", json_output);
105*c54f35caSApple OSS Distributions }
106*c54f35caSApple OSS Distributions
107*c54f35caSApple OSS Distributions dst = fdopen(dstfd, "w+");
108*c54f35caSApple OSS Distributions if (dst == NULL) {
109*c54f35caSApple OSS Distributions err(EX_UNAVAILABLE, "fdopen");
110*c54f35caSApple OSS Distributions }
111*c54f35caSApple OSS Distributions
112*c54f35caSApple OSS Distributions read_bytes = fread(start, sizeof(start[0]), sizeof(start) / sizeof(start[0]), dst);
113*c54f35caSApple OSS Distributions if ((read_bytes != sizeof(start)) || (0 != memcmp(start, "[\n", sizeof(start) / sizeof(start[0])))) {
114*c54f35caSApple OSS Distributions /* no JSON start, we don't really care why */
115*c54f35caSApple OSS Distributions ret = fseeko(dst, 0, SEEK_SET);
116*c54f35caSApple OSS Distributions if (ret < 0) {
117*c54f35caSApple OSS Distributions err(EX_UNAVAILABLE, "fseeko");
118*c54f35caSApple OSS Distributions }
119*c54f35caSApple OSS Distributions
120*c54f35caSApple OSS Distributions ret = fputs("[", dst);
121*c54f35caSApple OSS Distributions if (ret < 0) {
122*c54f35caSApple OSS Distributions err(EX_UNAVAILABLE, "fputs");
123*c54f35caSApple OSS Distributions }
124*c54f35caSApple OSS Distributions } else {
125*c54f35caSApple OSS Distributions /* has at least two bytes at the start. Seek to 3 bytes before the end */
126*c54f35caSApple OSS Distributions ret = fseeko(dst, -3, SEEK_END);
127*c54f35caSApple OSS Distributions if (ret < 0) {
128*c54f35caSApple OSS Distributions err(EX_UNAVAILABLE, "fseeko");
129*c54f35caSApple OSS Distributions }
130*c54f35caSApple OSS Distributions
131*c54f35caSApple OSS Distributions ret = fputs(",", dst);
132*c54f35caSApple OSS Distributions if (ret < 0) {
133*c54f35caSApple OSS Distributions err(EX_UNAVAILABLE, "fputs");
134*c54f35caSApple OSS Distributions }
135*c54f35caSApple OSS Distributions }
136*c54f35caSApple OSS Distributions
137*c54f35caSApple OSS Distributions fprintf(dst, "\n");
138*c54f35caSApple OSS Distributions fprintf(dst, "{\n");
139*c54f35caSApple OSS Distributions fprintf(dst, " \"directory\": \"%s\",\n", cwd);
140*c54f35caSApple OSS Distributions fprintf(dst, " \"file\": \"%s\",\n", input_file);
141*c54f35caSApple OSS Distributions fprintf(dst, " \"command\": \"");
142*c54f35caSApple OSS Distributions for (i = 0; i < argc; i++) {
143*c54f35caSApple OSS Distributions bool needs_escape = strchr(argv[i], '\\') || strchr(argv[i], '"') || strchr(argv[i], ' ');
144*c54f35caSApple OSS Distributions
145*c54f35caSApple OSS Distributions if (needs_escape) {
146*c54f35caSApple OSS Distributions char *escaped_string = escape_string(argv[i]);
147*c54f35caSApple OSS Distributions fprintf(dst, "%s\\\"%s\\\"", i == 0 ? "" : " ", escaped_string);
148*c54f35caSApple OSS Distributions free(escaped_string);
149*c54f35caSApple OSS Distributions } else {
150*c54f35caSApple OSS Distributions fprintf(dst, "%s%s", i == 0 ? "" : " ", argv[i]);
151*c54f35caSApple OSS Distributions }
152*c54f35caSApple OSS Distributions }
153*c54f35caSApple OSS Distributions fprintf(dst, "\"\n");
154*c54f35caSApple OSS Distributions fprintf(dst, "}\n");
155*c54f35caSApple OSS Distributions fprintf(dst, "]\n");
156*c54f35caSApple OSS Distributions
157*c54f35caSApple OSS Distributions ret = fclose(dst);
158*c54f35caSApple OSS Distributions if (ret < 0) {
159*c54f35caSApple OSS Distributions err(EX_UNAVAILABLE, "fclose");
160*c54f35caSApple OSS Distributions }
161*c54f35caSApple OSS Distributions
162*c54f35caSApple OSS Distributions return 0;
163*c54f35caSApple OSS Distributions }
164*c54f35caSApple OSS Distributions
165*c54f35caSApple OSS Distributions void
usage(void)166*c54f35caSApple OSS Distributions usage(void)
167*c54f35caSApple OSS Distributions {
168*c54f35caSApple OSS Distributions fprintf(stderr, "Usage: %s <json_output> <cwd> <input_file> <compiler> [<invocation> ...]\n", getprogname());
169*c54f35caSApple OSS Distributions exit(EX_USAGE);
170*c54f35caSApple OSS Distributions }
171*c54f35caSApple OSS Distributions
172*c54f35caSApple OSS Distributions /*
173*c54f35caSApple OSS Distributions * A valid JSON string can't contain \ or ", so we look for these in our argv[] array (which
174*c54f35caSApple OSS Distributions * our parent shell would have done shell metacharacter evaluation on, and escape just these.
175*c54f35caSApple OSS Distributions * The entire string is put in \" escaped quotes to handle spaces that are valid JSON
176*c54f35caSApple OSS Distributions * but should be used for grouping when running the compiler for real.
177*c54f35caSApple OSS Distributions */
178*c54f35caSApple OSS Distributions char *
escape_string(const char * input)179*c54f35caSApple OSS Distributions escape_string(const char *input)
180*c54f35caSApple OSS Distributions {
181*c54f35caSApple OSS Distributions size_t len = strlen(input);
182*c54f35caSApple OSS Distributions size_t i, j;
183*c54f35caSApple OSS Distributions char *output = malloc(len * 4 + 1);
184*c54f35caSApple OSS Distributions
185*c54f35caSApple OSS Distributions for (i = 0, j = 0; i < len; i++) {
186*c54f35caSApple OSS Distributions char ch = input[i];
187*c54f35caSApple OSS Distributions
188*c54f35caSApple OSS Distributions if (ch == '\\' || ch == '"') {
189*c54f35caSApple OSS Distributions output[j++] = '\\';
190*c54f35caSApple OSS Distributions output[j++] = '\\'; /* output \\ in JSON, which the final shell will see as \ */
191*c54f35caSApple OSS Distributions output[j++] = '\\'; /* escape \ or ", which the final shell will see and pass to the compiler */
192*c54f35caSApple OSS Distributions }
193*c54f35caSApple OSS Distributions output[j++] = ch;
194*c54f35caSApple OSS Distributions }
195*c54f35caSApple OSS Distributions
196*c54f35caSApple OSS Distributions output[j] = '\0';
197*c54f35caSApple OSS Distributions
198*c54f35caSApple OSS Distributions return output;
199*c54f35caSApple OSS Distributions }
200