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