so://phie

ductape

Static template generator for C
git clone git://soophie.de/ductape

blob: 1c462c6f08f1bc7fba3022f864fe9e1896a576b7 (10476 bytes)[doc][blame][raw]

1// @file main.c

2

3#include <stdio.h>

4#include <stdlib.h>

5#include <ctype.h>

6

7#define SO_STR_IMPL

8// @include <so/str.h>

9#include <so/str.h>

10

11#define DUC_BUILDPATH "DUC_BUILDPATH"

12#define DUC_HEADERPATH "DUC_HEADERPATH"

13#define DUC_LIBPATH "DUC_LIBPATH"

14

15#define DUC_SYM_SIG_HASH "__DUC_SIG_HASH_"

16#define DUC_SYM_UID "__DUC_UID_"

17#define DUC_SYM_SPLIT "%"

18#define DUC_SYM_SPLIT_ESCAPE DUC_SYM_SPLIT DUC_SYM_SPLIT

19#define DUC_SYM_WRITE "DUC_WRITE"

20#define DUC_SYM_TEMPLDEF "DUC_TEMPLDEF"

21#define DUC_SYM_ENDTEMPL "DUC_ENDTEMPL"

22#define DUC_SYM_TEMPLDEF_HEADER "__DUC_TEMPLDEF_HEADER"

23

24#define DUC_TMP_C_PATH ".duc_tmp.c"

25#define DUC_TMP_SO_PATH ".duc_tmp.so"

26

27#define DUC_CMD_COMPILE "cc -shared -fPIC -o %s %s %s"

28

29// @enum duc_err_t

30typedef enum {

31 DUC_ERR_OK,

32 DUC_ERR_NO_ARGS,

33 DUC_ERR_NO_FILE,

34 DUC_ERR_NO_WRITE,

35 DUC_ERR_UNCLOSED_CODE,

36 DUC_ERR_NO_COMPILE,

37 __DUC_ERR__COUNT,

38} duc_err_t;

39

40// @enum duc_mode_t

41typedef enum {

42 DUC_MODE_HEADER,

43 DUC_MODE_MODULE,

44 __DUC_MODE__COUNT,

45} duc_mode_t;

46

47// @func duc_error_str

48void duc_error_str(duc_err_t err) {

49 switch (err) {

50 case DUC_ERR_OK:

51 break;

52 case DUC_ERR_NO_ARGS:

53 fprintf(stderr, "Usage: ductape <files...>\n");

54 break;

55 case DUC_ERR_NO_FILE:

56 fprintf(stderr, "Error: No file found!\n");

57 break;

58 case DUC_ERR_NO_WRITE:

59 fprintf(stderr, "Error: Can't write to file!\n");

60 break;

61 case DUC_ERR_UNCLOSED_CODE:

62 fprintf(stderr, "Error: Unclosed code block!\n");

63 break;

64 case DUC_ERR_NO_COMPILE:

65 fprintf(stderr, "Error: Failed to compile!\n");

66 break;

67 case __DUC_ERR__COUNT:

68 break;

69 }

70}

71

72// @func duc_sig_hash

73size_t duc_sig_hash(char *str, size_t len) {

74 size_t hash = 5381;

75 size_t i = 0;

76 while (i < len) {

77 unsigned char c = (unsigned) str[i];

78 if (c == ' ' || c == '\t' || c == '\r' || c == '\n') {

79 i++;

80 continue;

81 }

82 hash = ((hash << 5) + hash) + c;

83 i++;

84 }

85 return hash;

86}

87

88// @func duc_render

89duc_err_t duc_render(duc_mode_t mode, str_t *out, char *content, char **uid) {

90 size_t len = strlen(content);

91 size_t set = 0;

92 size_t is_code = 0;

93 size_t is_templ = 0;

94 size_t hash = 0;

95 for (size_t i = 0; i < len; i++) {

96 if (content[i] == *DUC_SYM_SPLIT) {

97 // ignore escaped DUC_SYM_SPLIT

98 if (i + 1 < len && content[i + 1] == *DUC_SYM_SPLIT) {

99 i += 1;

100 continue;

101 }

102 if (is_code) {

103 for (size_t j = 0; j < i - set; j++) {

104 if (strncmp(content + set + j, DUC_SYM_TEMPLDEF, strlen(DUC_SYM_TEMPLDEF)) == 0) {

105 is_templ = 1;

106 hash = duc_sig_hash(content + set, i - set);

107 bool is_arg = false;

108 bool is_uid = false;

109 size_t uid_set = 0;

110 for (size_t k = j; k < i - set; k++) {

111 char c = content[set + k];

112 if (c == '(') {

113 is_arg = true;

114 }

115 if (is_arg && isalpha(c)) {

116 is_uid = true;

117 is_arg = false;

118 uid_set = k;

119 }

120 if (is_uid && (!isalpha(c) && !isdigit(c) && c != '_')) {

121 is_uid = false;

122 *uid = strndup(content + set + uid_set, k - uid_set);

123 break;

124 }

125 }

126 }

127 if (strncmp(content + set + j, DUC_SYM_ENDTEMPL, strlen(DUC_SYM_ENDTEMPL)) == 0) {

128 is_templ = 0;

129 }

130 }

131 if (mode == DUC_MODE_MODULE) {

132 str_append_len(out, content + set, i - set);

133 str_append(out, "\n");

134 }

135 if (mode == DUC_MODE_HEADER && is_templ == 1) {

136 is_templ = 0;

137 str_t templdef = STR_NULL;

138 str_append_len(&templdef, content + set, i - set);

139 str_append(&templdef, "\n");

140 str_repall(&templdef, DUC_SYM_TEMPLDEF, DUC_SYM_TEMPLDEF_HEADER);

141 str_concat(out, templdef);

142 str_free(&templdef);

143 }

144 }

145 else {

146 const size_t STRING_LIMIT = 4095;

147 size_t str_len = i - set;

148 size_t rem_len = str_len;

149 size_t hex_len = 0;

150 size_t idx = 0;

151 while (rem_len > 0) {

152 if (rem_len > STRING_LIMIT) {

153 hex_len = STRING_LIMIT;

154 }

155 else {

156 hex_len = str_len - idx;

157 }

158 rem_len -= hex_len;

159 str_t hex = STR_NULL;

160 str_append_len(&hex, content + set + idx, hex_len);

161 str_repall(&hex, DUC_SYM_SPLIT_ESCAPE, DUC_SYM_SPLIT);

162 str_to_hex(&hex);

163 if (mode == DUC_MODE_MODULE && is_templ == 1) {

164 str_append(out, DUC_SYM_WRITE "(\"");

165 str_concat(out, hex);

166 str_append(out, "\")\n");

167 }

168 str_free(&hex);

169 idx += hex_len;

170 }

171 }

172 is_code = !is_code;

173 set = i + 1;

174 }

175 }

176 str_t meta = STR_NULL;

177 str_append(&meta, "#include <dlfcn.h>\n");

178 str_append(&meta, "#include <ductape.h>\n");

179 str_appendf(&meta, "const char " DUC_SYM_UID "%s[] = \"%s\";\n", *uid, *uid);

180 str_appendf(&meta, "const size_t " DUC_SYM_SIG_HASH "%s = %ld;\n", *uid, hash);

181 str_concat(&meta, *out);

182 str_free(out);

183 *out = STR_NULL;

184 str_concat(out, meta);

185 str_free(&meta);

186 if (is_code) {

187 return DUC_ERR_UNCLOSED_CODE;

188 }

189 else {

190 const size_t STRING_LIMIT = 4095;

191 size_t str_len = len - set;

192 size_t rem_len = str_len;

193 size_t hex_len = 0;

194 size_t idx = 0;

195 while (rem_len > 0) {

196 if (rem_len > STRING_LIMIT) {

197 hex_len = STRING_LIMIT;

198 }

199 else {

200 hex_len = str_len - idx;

201 }

202 rem_len -= hex_len;

203 str_t hex = STR_NULL;

204 str_append_len(&hex, content + set + idx, hex_len);

205 str_repall(&hex, DUC_SYM_SPLIT_ESCAPE, DUC_SYM_SPLIT);

206 str_to_hex(&hex);

207 if (mode == DUC_MODE_MODULE && is_templ == 1) {

208 str_append(out, DUC_SYM_WRITE "(\"");

209 str_concat(out, hex);

210 str_append(out, "\")\n");

211 }

212 str_free(&hex);

213 idx += hex_len;

214 }

215 }

216 str_repall(out, DUC_SYM_SPLIT_ESCAPE, DUC_SYM_SPLIT);

217 return DUC_ERR_OK;

218}

219

220// @func duc_file_read

221duc_err_t duc_file_read(char **content, const char *filename) {

222 FILE *fp = fopen(filename, "r");

223 if (fp == NULL) {

224 return DUC_ERR_NO_FILE;

225 }

226 fseek(fp, 0, SEEK_END);

227 size_t len = ftell(fp);

228 fseek(fp, 0, SEEK_SET);

229 *content = calloc(len + 1, sizeof(char));

230 fread(*content, sizeof(char), len, fp);

231 (*content)[len] = '\0';

232 fclose(fp);

233 return DUC_ERR_OK;

234}

235

236// @func duc_file_read_binary

237duc_err_t duc_file_read_binary(char **binary, size_t *len, const char *filename) {

238 FILE *fp = fopen(filename, "rb");

239 if (fp == NULL) {

240 return DUC_ERR_NO_FILE;

241 }

242 fseek(fp, 0, SEEK_END);

243 *len = ftell(fp);

244 fseek(fp, 0, SEEK_SET);

245 *binary = calloc(*len, sizeof(char));

246 fread(*binary, sizeof(char), *len, fp);

247 fclose(fp);

248 return DUC_ERR_OK;

249}

250

251// @func duc_file_write

252duc_err_t duc_file_write(const char *content, size_t len, const char *filename) {

253 FILE *fp = fopen(filename, "w");

254 if (fp == NULL) {

255 return DUC_ERR_NO_FILE;

256 }

257 if (fwrite(content, sizeof(char), len, fp) != len) {

258 return DUC_ERR_NO_WRITE;

259 }

260 fclose(fp);

261 return DUC_ERR_OK;

262}

263

264// @func duc_compile_file

265duc_err_t duc_compile_file(duc_mode_t mode, char *filepath, char **uid) {

266 duc_err_t err;

267 char *content = NULL;

268 err = duc_file_read(&content, filepath);

269 if (err != DUC_ERR_OK) {

270 duc_error_str(err);

271 return err;

272 }

273 str_t template = STR_NULL;

274 err = duc_render(mode, &template, content, uid);

275 free(content);

276 if (err != DUC_ERR_OK || *uid == NULL) {

277 str_free(&template);

278 duc_error_str(err);

279 return err;

280 }

281 switch (mode) {

282 case DUC_MODE_HEADER: {

283 char *path = getenv(DUC_HEADERPATH);

284 if (path == NULL) {

285 path = "./";

286 }

287 str_t filename = STR_NULL;

288 str_appendf(&filename, "%s%s.h", path, *uid);

289 duc_file_write((char *) str_get(&template), str_len(&template), (char *) str_get(&filename));

290 str_free(&filename);

291 str_free(&template);

292 return DUC_ERR_OK;

293 }

294 case DUC_MODE_MODULE: {

295 err = duc_file_write(str_get(&template), str_len(&template), DUC_TMP_C_PATH);

296 str_free(&template);

297 char *lib_path = getenv(DUC_LIBPATH);

298 if (lib_path == NULL) {

299 lib_path = "";

300 }

301 char cmd[500];

302 snprintf(cmd, sizeof(cmd), DUC_CMD_COMPILE, DUC_TMP_SO_PATH, lib_path, DUC_TMP_C_PATH);

303 int rc = system(cmd);

304 remove(DUC_TMP_C_PATH);

305 if (rc != 0) {

306 err = DUC_ERR_NO_COMPILE;

307 duc_error_str(err);

308 return err;

309 }

310 char *binary = NULL;

311 size_t len = 0;

312 err = duc_file_read_binary(&binary, &len, DUC_TMP_SO_PATH);

313 remove(DUC_TMP_SO_PATH);

314 if (err != DUC_ERR_OK) {

315 duc_error_str(err);

316 return err;

317 }

318 char *path = getenv(DUC_BUILDPATH);

319 if (path == NULL) {

320 path = "./";

321 }

322 str_t filename = STR_NULL;

323 str_appendf(&filename, "%s%s.so", path, *uid);

324 duc_file_write(binary, len, (char *) str_get(&filename));

325 str_free(&filename);

326 free(binary);

327 return DUC_ERR_OK;

328 }

329 case __DUC_MODE__COUNT:

330 break;

331 }

332 return DUC_ERR_OK;

333}

334

335// @func main

336int main(int argc, char **argv) {

337 if (argc <= 1) {

338 return DUC_ERR_NO_ARGS;

339 }

340 char *headerpath = getenv(DUC_HEADERPATH);

341 char *buildpath = getenv(DUC_BUILDPATH);

342 char *libpath = getenv(DUC_LIBPATH);

343 fprintf(stdout, "[ductape] Compiling templates...\n");

344 fprintf(stdout, " ." DUC_HEADERPATH " = '%s'\n", headerpath ? headerpath : "./");

345 fprintf(stdout, " ." DUC_BUILDPATH " = '%s'\n", buildpath ? buildpath : "./");

346 if (libpath) {

347 fprintf(stdout, " ." DUC_LIBPATH " = '%s'\n", libpath);

348 }

349 duc_err_t err = DUC_ERR_OK;

350 for (size_t i = 1; i < (size_t) argc; i++) {

351 char *filepath = argv[i];

352 duc_err_t header_err = DUC_ERR_OK;

353 char *uid = NULL;

354 if ((header_err = duc_compile_file(DUC_MODE_HEADER, filepath, &uid)) != DUC_ERR_OK) {

355 err = header_err;

356 }

357 if (uid) {

358 free(uid);

359 }

360 uid = NULL;

361 duc_err_t module_err = DUC_ERR_OK;

362 if ((module_err = duc_compile_file(DUC_MODE_MODULE, filepath, &uid)) != DUC_ERR_OK) {

363 err = module_err;

364 }

365 fprintf(stdout, "-- compile '%s' | %s [%s]\n", uid, filepath, header_err == DUC_ERR_OK && module_err == DUC_ERR_OK ? "OK" : "FAILED");

366 if (uid) {

367 free(uid);

368 }

369 }

370 return err;

371}