so://phie

ductape

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

blob: 4f3eb426cb558b6a17a20bf5ba10bc8af2410721 (10146 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

14#define DUC_SYM_SIG_HASH "__DUC_SIG_HASH_"

15#define DUC_SYM_UID "__DUC_UID_"

16#define DUC_SYM_SPLIT "%"

17#define DUC_SYM_SPLIT_ESCAPE DUC_SYM_SPLIT DUC_SYM_SPLIT

18#define DUC_SYM_WRITE "DUC_WRITE"

19#define DUC_SYM_TEMPLDEF "TEMPLDEF"

20#define DUC_SYM_ENDTEMPL "ENDTEMPL"

21#define DUC_SYM_TEMPLDEF_HEADER "__TEMPLDEF_HEADER"

22

23#define DUC_TMP_C_PATH ".duc_tmp.c"

24#define DUC_TMP_SO_PATH ".duc_tmp.so"

25

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

27

28// @enum duc_err_t

29typedef enum {

30 DUC_ERR_OK,

31 DUC_ERR_NO_ARGS,

32 DUC_ERR_NO_FILE,

33 DUC_ERR_NO_WRITE,

34 DUC_ERR_UNCLOSED_CODE,

35 DUC_ERR_NO_COMPILE,

36 __DUC_ERR__COUNT,

37} duc_err_t;

38

39// @enum duc_mode_t

40typedef enum {

41 DUC_MODE_HEADER,

42 DUC_MODE_MODULE,

43 __DUC_MODE__COUNT,

44} duc_mode_t;

45

46// @func duc_error_str

47void duc_error_str(duc_err_t err) {

48 switch (err) {

49 case DUC_ERR_OK:

50 break;

51 case DUC_ERR_NO_ARGS:

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

53 break;

54 case DUC_ERR_NO_FILE:

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

56 break;

57 case DUC_ERR_NO_WRITE:

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

59 break;

60 case DUC_ERR_UNCLOSED_CODE:

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

62 break;

63 case DUC_ERR_NO_COMPILE:

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

65 break;

66 case __DUC_ERR__COUNT:

67 break;

68 }

69}

70

71// @func duc_sig_hash

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

73 size_t hash = 5381;

74 size_t i = 0;

75 while (i < len) {

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

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

78 i++;

79 continue;

80 }

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

82 i++;

83 }

84 return hash;

85}

86

87// @func duc_render

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

89 size_t len = strlen(content);

90 size_t set = 0;

91 size_t is_code = 0;

92 size_t is_templ = 0;

93 size_t hash = 0;

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

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

96 // ignore escaped DUC_SYM_SPLIT

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

98 i += 1;

99 continue;

100 }

101 if (is_code) {

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

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

104 is_templ = 1;

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

106 bool is_arg = false;

107 bool is_uid = false;

108 size_t uid_set = 0;

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

110 char c = content[set + k];

111 if (c == '(') {

112 is_arg = true;

113 }

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

115 is_uid = true;

116 is_arg = false;

117 uid_set = k;

118 }

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

120 is_uid = false;

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

122 break;

123 }

124 }

125 }

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

127 is_templ = 0;

128 }

129 }

130 if (mode == DUC_MODE_MODULE) {

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

132 str_append(out, "\n");

133 }

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

135 is_templ = 0;

136 str_t templdef = STR_NULL;

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

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

139 str_repall(&templdef, DUC_SYM_TEMPLDEF, DUC_SYM_TEMPLDEF_HEADER);

140 str_concat(out, templdef);

141 str_free(&templdef);

142 }

143 }

144 else {

145 const size_t STRING_LIMIT = 4095;

146 size_t str_len = i - set;

147 size_t rem_len = str_len;

148 size_t hex_len = 0;

149 size_t idx = 0;

150 while (rem_len > 0) {

151 if (rem_len > STRING_LIMIT) {

152 hex_len = STRING_LIMIT;

153 }

154 else {

155 hex_len = str_len - idx;

156 }

157 rem_len -= hex_len;

158 str_t hex = STR_NULL;

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

160 str_repall(&hex, DUC_SYM_SPLIT_ESCAPE, DUC_SYM_SPLIT);

161 str_to_hex(&hex);

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

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

164 str_concat(out, hex);

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

166 }

167 str_free(&hex);

168 idx += hex_len;

169 }

170 }

171 is_code = !is_code;

172 set = i + 1;

173 }

174 }

175 str_t meta = STR_NULL;

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

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

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

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

180 str_concat(&meta, *out);

181 str_free(out);

182 *out = STR_NULL;

183 str_concat(out, meta);

184 str_free(&meta);

185 if (is_code) {

186 return DUC_ERR_UNCLOSED_CODE;

187 }

188 else {

189 const size_t STRING_LIMIT = 4095;

190 size_t str_len = len - set;

191 size_t rem_len = str_len;

192 size_t hex_len = 0;

193 size_t idx = 0;

194 while (rem_len > 0) {

195 if (rem_len > STRING_LIMIT) {

196 hex_len = STRING_LIMIT;

197 }

198 else {

199 hex_len = str_len - idx;

200 }

201 rem_len -= hex_len;

202 str_t hex = STR_NULL;

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

204 str_repall(&hex, DUC_SYM_SPLIT_ESCAPE, DUC_SYM_SPLIT);

205 str_to_hex(&hex);

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

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

208 str_concat(out, hex);

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

210 }

211 str_free(&hex);

212 idx += hex_len;

213 }

214 }

215 str_repall(out, DUC_SYM_SPLIT_ESCAPE, DUC_SYM_SPLIT);

216 return DUC_ERR_OK;

217}

218

219// @func duc_file_read

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

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

222 if (fp == NULL) {

223 return DUC_ERR_NO_FILE;

224 }

225 fseek(fp, 0, SEEK_END);

226 size_t len = ftell(fp);

227 fseek(fp, 0, SEEK_SET);

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

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

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

231 fclose(fp);

232 return DUC_ERR_OK;

233}

234

235// @func duc_file_read_binary

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

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

238 if (fp == NULL) {

239 return DUC_ERR_NO_FILE;

240 }

241 fseek(fp, 0, SEEK_END);

242 *len = ftell(fp);

243 fseek(fp, 0, SEEK_SET);

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

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

246 fclose(fp);

247 return DUC_ERR_OK;

248}

249

250// @func duc_file_write

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

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

253 if (fp == NULL) {

254 return DUC_ERR_NO_FILE;

255 }

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

257 return DUC_ERR_NO_WRITE;

258 }

259 fclose(fp);

260 return DUC_ERR_OK;

261}

262

263// @func duc_compile_file

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

265 duc_err_t err;

266 char *content = NULL;

267 err = duc_file_read(&content, filepath);

268 if (err != DUC_ERR_OK) {

269 duc_error_str(err);

270 return err;

271 }

272 str_t template = STR_NULL;

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

274 free(content);

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

276 str_free(&template);

277 duc_error_str(err);

278 return err;

279 }

280 switch (mode) {

281 case DUC_MODE_HEADER: {

282 char *path = getenv(DUC_HEADERPATH);

283 if (path == NULL) {

284 path = "./";

285 }

286 str_t filename = STR_NULL;

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

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

289 str_free(&filename);

290 str_free(&template);

291 return DUC_ERR_OK;

292 }

293 case DUC_MODE_MODULE: {

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

295 str_free(&template);

296 char cmd[500];

297 snprintf(cmd, sizeof(cmd), DUC_CMD_COMPILE, DUC_TMP_SO_PATH, DUC_TMP_C_PATH);

298 int rc = system(cmd);

299 remove(DUC_TMP_C_PATH);

300 if (rc != 0) {

301 err = DUC_ERR_NO_COMPILE;

302 duc_error_str(err);

303 return err;

304 }

305 char *binary = NULL;

306 size_t len = 0;

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

308 remove(DUC_TMP_SO_PATH);

309 if (err != DUC_ERR_OK) {

310 duc_error_str(err);

311 return err;

312 }

313 char *path = getenv(DUC_BUILDPATH);

314 if (path == NULL) {

315 path = "./";

316 }

317 str_t filename = STR_NULL;

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

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

320 str_free(&filename);

321 free(binary);

322 return DUC_ERR_OK;

323 }

324 case __DUC_MODE__COUNT:

325 break;

326 }

327 return DUC_ERR_OK;

328}

329

330// @func main

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

332 if (argc <= 1) {

333 return DUC_ERR_NO_ARGS;

334 }

335 char *headerpath = getenv(DUC_HEADERPATH);

336 char *buildpath = getenv(DUC_BUILDPATH);

337 fprintf(stdout, "[ductape] Compiling templates...\n ." DUC_HEADERPATH " = '%s'\n ." DUC_BUILDPATH " = '%s'\n", headerpath ? headerpath : "./", buildpath ? buildpath : "./");

338 duc_err_t err = DUC_ERR_OK;

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

340 char *filepath = argv[i];

341 duc_err_t header_err = DUC_ERR_OK;

342 char *uid = NULL;

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

344 err = header_err;

345 }

346 if (uid) {

347 free(uid);

348 }

349 uid = NULL;

350 duc_err_t module_err = DUC_ERR_OK;

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

352 err = module_err;

353 }

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

355 if (uid) {

356 free(uid);

357 }

358 }

359 return err;

360}