so://phie

ductape

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

blob: 5530c41c8eeba41d535cfa1e3f2fce5758da619b (7337 bytes)[doc][blame][raw]

1#include <stdio.h>

2#include <stdlib.h>

3#include <getopt.h>

4

5#define LIB_STR_IMPL

6#include <libstr.h>

7

8#define CTL_TMP_C_PATH ".ctl_tmp.c"

9#define CTL_TMP_SO_PATH ".ctl_tmp.so"

10

11typedef enum {

12 CTL_ERR_OK,

13 CTL_ERR_NO_ARGS,

14 CTL_ERR_NO_FILE,

15 CTL_ERR_NO_WRITE,

16 CTL_ERR_UNCLOSED_CODE,

17 CTL_ERR_NO_COMPILE,

18} ctl_error_t;

19

20typedef enum {

21 CTL_MODE_HEADER,

22 CTL_MODE_MODULE,

23} ctl_mode_t;

24

25void ctl_error_str(ctl_error_t err) {

26 switch (err) {

27 case CTL_ERR_OK:

28 break;

29 case CTL_ERR_NO_ARGS:

30 fprintf(stderr, "Usage: ctl -h <templ_path> (HEADER), ctl -o <templ_path> (MODULE)\n");

31 break;

32 case CTL_ERR_NO_FILE:

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

34 break;

35 case CTL_ERR_NO_WRITE:

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

37 break;

38 case CTL_ERR_UNCLOSED_CODE:

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

40 break;

41 case CTL_ERR_NO_COMPILE:

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

43 break;

44 }

45}

46

47char *str_replace_all_len(char *buffer, char *str, char *replacement, int len) {

48 char *ret = NULL;

49 int str_len = strlen(str);

50 for (int i = 0; i < len; i++) {

51 char *ptr = &buffer[i];

52 if (strncmp(ptr, str, str_len) == 0) {

53 str_append(&ret, replacement);

54 i += str_len - 1;

55 }

56 else {

57 str_append_len(&ret, &buffer[i], 1);

58 }

59 }

60 return ret;

61}

62

63char *str_hex_len(char *str, int len) {

64 const int HEX = 4;

65 char *ret = calloc(HEX * len + 1, sizeof(char));

66 for (int i = 0; i < len; i++) {

67 unsigned char hex = str[i] & 0xff;

68 snprintf(ret + HEX * i, HEX * len, "\\x%02x", hex);

69 }

70 ret[HEX * len] = '\0';

71 return ret;

72}

73

74ctl_error_t ctl_render(ctl_mode_t mode, char **template, char *content) {

75 int len = strlen(content);

76 int set = 0;

77 int is_code = 0;

78 int templ = 0;

79 str_append(template, "#include <dlfcn.h>\n");

80 str_append(template, "#include <so/ctl.h>\n");

81 for (int i = 0; i < len; i++) {

82 if (content[i] == '%') {

83 // ignore escaped %

84 if (i + 1 < len && content[i + 1] == '%') {

85 i += 1;

86 continue;

87 }

88 if (is_code) {

89 for (int j = 0; j < i - set; j++) {

90 if (strncmp(content + set + j, "TEMPLDEF", 8) == 0) {

91 templ = 1;

92 }

93 if (strncmp(content + set + j, "ENDTEMPL", 8) == 0) {

94 templ = 0;

95 }

96 }

97 if (mode == CTL_MODE_MODULE) {

98 str_append_len(template, content + set, i - set);

99 str_append(template, "\n");

100 }

101 if (mode == CTL_MODE_HEADER && templ == 1) {

102 templ = 0;

103 char *templdef = NULL;

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

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

106 templdef = str_replace_all(templdef, "CTL_TEMPLDEF", "__CTL_TEMPLDEF_HEADER");

107 str_append(template, templdef);

108 free(templdef);

109 }

110 }

111 else {

112 const int STRING_LIMIT = 4095;

113 int str_len = i - set;

114 int rem_len = str_len;

115 int hex_len = 0;

116 int idx = 0;

117 while (rem_len > 0) {

118 if (rem_len > STRING_LIMIT) {

119 hex_len = STRING_LIMIT;

120 }

121 else {

122 hex_len = str_len - idx;

123 }

124 rem_len -= hex_len;

125 char *str = str_replace_all_len(content + set + idx, "%%", "%", hex_len);

126 char *hex = str_hex_len(str, strlen(str));

127 if (mode == CTL_MODE_MODULE && templ == 1) {

128 str_append(template, "ctl->write(\"");

129 str_append(template, hex);

130 str_append(template, "\");\n");

131 }

132 free(str);

133 free(hex);

134 idx += hex_len;

135 }

136 }

137 is_code = !is_code;

138 set = i + 1;

139 }

140 }

141 if (is_code) {

142 return CTL_ERR_UNCLOSED_CODE;

143 }

144 else {

145 const int STRING_LIMIT = 4095;

146 int str_len = len - set;

147 int rem_len = str_len;

148 int hex_len = 0;

149 int 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 char *str = str_replace_all_len(content + set + idx, "%%", "%", hex_len);

159 char *hex = str_hex_len(str, strlen(str));

160 if (mode == CTL_MODE_MODULE && templ == 1) {

161 str_append(template, "ctl->write(\"");

162 str_append(template, hex);

163 str_append(template, "\");\n");

164 }

165 free(str);

166 free(hex);

167 idx += hex_len;

168 }

169 }

170 *template = str_replace_all(*template, "%%", "%");

171 return CTL_ERR_OK;

172}

173

174ctl_error_t ctl_file_read(char **content, const char *filename) {

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

176 if (fp == NULL) {

177 return CTL_ERR_NO_FILE;

178 }

179 fseek(fp, 0, SEEK_END);

180 int len = ftell(fp);

181 fseek(fp, 0, SEEK_SET);

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

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

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

185 fclose(fp);

186 return CTL_ERR_OK;

187}

188

189ctl_error_t ctl_file_read_binary(char **binary, size_t *len, const char *filename) {

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

191 if (fp == NULL) {

192 return CTL_ERR_NO_FILE;

193 }

194 fseek(fp, 0, SEEK_END);

195 *len = ftell(fp);

196 fseek(fp, 0, SEEK_SET);

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

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

199 fclose(fp);

200 return CTL_ERR_OK;

201}

202

203ctl_error_t ctl_file_write(const char *content, const char *filename) {

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

205 if (fp == NULL) {

206 return CTL_ERR_NO_FILE;

207 }

208 if (fwrite(content, sizeof(char), strlen(content), fp) < 0) {

209 return CTL_ERR_NO_WRITE;

210 }

211 fclose(fp);

212 return CTL_ERR_OK;

213}

214

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

216 ctl_error_t err;

217 opterr = 0;

218 ctl_mode_t mode;

219 char *path = NULL;

220 int opt;

221 while ((opt = getopt(argc, argv, "h:o:")) != -1) {

222 switch (opt) {

223 case 'h':

224 mode = CTL_MODE_HEADER;

225 path = optarg;

226 break;

227 case 'o':

228 mode = CTL_MODE_MODULE;

229 path = optarg;

230 break;

231 default:

232 err = CTL_ERR_NO_ARGS;

233 ctl_error_str(err);

234 return err;

235 }

236 }

237 if (path == NULL) {

238 err = CTL_ERR_NO_ARGS;

239 ctl_error_str(err);

240 return err;

241 }

242 char *content = NULL;

243 err = ctl_file_read(&content, path);

244 if (err != CTL_ERR_OK) {

245 ctl_error_str(err);

246 return err;

247 }

248 char *template = NULL;

249 err = ctl_render(mode, &template, content);

250 free(content);

251 if (err != CTL_ERR_OK) {

252 if (template != NULL) {

253 free(template);

254 }

255 ctl_error_str(err);

256 return err;

257 }

258 if (mode == CTL_MODE_HEADER) {

259 fprintf(stdout, "%s", template);

260 free(template);

261 return CTL_ERR_OK;

262 }

263 if (mode == CTL_MODE_MODULE) {

264 err = ctl_file_write(template, CTL_TMP_C_PATH);

265 free(template);

266 char cmd[500];

267 snprintf(cmd, sizeof(cmd), "cc -shared -fPIC -o %s %s", CTL_TMP_SO_PATH, CTL_TMP_C_PATH);

268 int rc = system(cmd);

269 remove(CTL_TMP_C_PATH);

270 if (rc != 0) {

271 err = CTL_ERR_NO_COMPILE;

272 ctl_error_str(err);

273 return err;

274 }

275 char *binary = NULL;

276 size_t len = 0;

277 err = ctl_file_read_binary(&binary, &len, CTL_TMP_SO_PATH);

278 remove(CTL_TMP_SO_PATH);

279 if (err != CTL_ERR_OK) {

280 ctl_error_str(err);

281 return err;

282 }

283 fwrite(binary, sizeof(char), len, stdout);

284 free(binary);

285 return CTL_ERR_OK;

286 }

287}