├── LICENSE ├── README.md ├── client.c ├── main.c ├── misc.c ├── proto.c ├── server.c ├── shared.h ├── strstr.c ├── strstr.h ├── tpl.c ├── tpl.h ├── update.bash └── vim ├── plugin └── ccode.vim └── update.bash /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2010 nsf 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | CCode - An autocompletion daemon for the C programming language. 2 | 3 | 1. Linux only, don't ask me to port it somewhere. If you need that - do it. But kinda works on Mac too. 4 | 2. Relies on the C99 compliance (flexible array members, snprintf behaviour, etc). 5 | 3. Mostly done, but has few quirks. 6 | 4. Can be used to complete C++/ObjC, but I'm not targeting these languages. Don't report C++/ObjC specific bugs. 7 | 5. Currently only per directory CFLAGS configuration (just dump your CFLAGS to .ccode file). CCode supports shell expansion, e.g. `echo "\$(pkg-config --cflags sdl)" > .ccode` will execute pkg-config with each autocompletion request. 8 | 6. Should work on both 32 and 64 bit machines. 9 | 10 | ![CCode in vim](http://nosmileface.ru/images/ccode.png) 11 | 12 | ![CCode in vim 2](http://nosmileface.ru/images/ccode2.png) 13 | 14 | Something that should look like a usage guide 15 | ---------------------------------------------- 16 | 17 | 1. Build everything (see update.bash). 18 | 2. Place it somewhere on your $PATH. 19 | 3. Copy vim plugin to your .vim/plugin dir. 20 | 4. Daemon starts automatically, everything should work out of the box. 21 | 5. Use for autocompletion. 22 | 23 | FAQ 24 | --- 25 | Q: My linux distribution contains broken LLVM/clang build and clang doesn't see its include directory (/usr/lib/clang/2.8/include). What should I do? 26 | 27 | A: In your project dir: `echo " -I/usr/lib/clang/2.8/include" >> .ccode`. 28 | -------------------------------------------------------------------------------- /client.c: -------------------------------------------------------------------------------- 1 | #include "shared.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | // for reference 12 | static int create_client_socket(); 13 | static int try_connect(int sock, const char *file); 14 | static char *prepend_cwd(const char *file); 15 | static int connect_or_die(); 16 | static void run_server_and_wait(const char *path); 17 | 18 | //------------------------------------------------------------------------- 19 | 20 | static int create_client_socket() 21 | { 22 | int sock = socket(AF_UNIX, SOCK_STREAM, 0); 23 | if (sock == -1) 24 | return -1; 25 | 26 | return sock; 27 | } 28 | 29 | static int try_connect(int sock, const char *file) 30 | { 31 | struct sockaddr_un addr; 32 | fstr_t addrpath; 33 | 34 | addr.sun_family = AF_UNIX; 35 | 36 | FSTR_INIT_FOR_BUF(&addrpath, addr.sun_path); 37 | fstr_add_cstr(&addrpath, file); 38 | 39 | return connect(sock, (struct sockaddr*)&addr, sizeof addr); 40 | } 41 | 42 | static void run_server_and_wait(const char *path) 43 | { 44 | if (fork() == 0) { 45 | pid_t sid; 46 | 47 | // Change file mode mask 48 | umask(0); 49 | // new SID for the child, detach from the parent 50 | sid = setsid(); 51 | if (sid < 0) 52 | exit(1); 53 | // chdir (unlock the dir) 54 | if (chdir("/") < 0) 55 | exit(1); 56 | 57 | // redirect standard files to /dev/null 58 | freopen( "/dev/null", "r", stdin); 59 | freopen( "/dev/null", "w", stdout); 60 | freopen( "/dev/null", "w", stderr); 61 | 62 | server_main(); 63 | 64 | exit(0); 65 | } else { 66 | // wait for 10ms up to 100 times (1 second) for socket 67 | for (int i = 0; i < 100; ++i) { 68 | usleep(10000); 69 | if (file_exists(path)) 70 | return; 71 | } 72 | fprintf(stderr, "Failed to start a server, can't see socket: %s\n", 73 | path); 74 | exit(1); 75 | } 76 | } 77 | 78 | static int connect_or_die() 79 | { 80 | str_t *path; 81 | int sock; 82 | 83 | path = get_socket_path(); 84 | 85 | if (!file_exists(path->data)) 86 | run_server_and_wait(path->data); 87 | 88 | sock = create_client_socket(); 89 | if (sock == -1) { 90 | fprintf(stderr, "Error! Failed to create a client socket: %s\n", path->data); 91 | exit(1); 92 | } 93 | 94 | if (-1 == try_connect(sock, (char*)path->data)) { 95 | fprintf(stderr, "Error! Failed to connect to a server at: %s\n", path->data); 96 | exit(1); 97 | } 98 | str_free(path); 99 | return sock; 100 | } 101 | 102 | 103 | static char *prepend_cwd(const char *file) 104 | { 105 | str_t *tmp; 106 | char cwd[1024]; 107 | char *ret; 108 | char *pcwd; 109 | 110 | pcwd = getcwd(cwd, 1024); 111 | if (!pcwd) { 112 | fprintf(stderr, "Path is too long, more than 1024? wtf, man?\n"); 113 | exit(1); 114 | } 115 | 116 | tmp = str_from_cstr(pcwd); 117 | str_add_cstr(&tmp, "/"); 118 | str_add_cstr(&tmp, file); 119 | ret = strdup(tmp->data); 120 | str_free(tmp); 121 | return ret; 122 | } 123 | 124 | //------------------------------------------------------------------------- 125 | 126 | void client_main(int argc, char **argv) 127 | { 128 | int sock; 129 | 130 | if (argc < 2) { 131 | printf("ccode client, commands:\n" 132 | " close\n" 133 | " ac (+ currently editted buffer as stdin)\n"); 134 | return; 135 | } 136 | 137 | if (strcmp(argv[1], "close") == 0) { 138 | sock = connect_or_die(); 139 | tpl_node *tn = msg_node_pack(MSG_CLOSE); 140 | tpl_dump(tn, TPL_FD, sock); 141 | tpl_free(tn); 142 | close(sock); 143 | } else if (strcmp(argv[1], "ac") == 0) { 144 | sock = connect_or_die(); 145 | char *end; 146 | size_t sz; 147 | struct msg_ac msg; 148 | 149 | if (argc != 5 && argc != 6) { 150 | fprintf(stderr, "Not enough arguments\n"); 151 | exit(1); 152 | } 153 | 154 | if (starts_with(argv[2], "/")) 155 | msg.filename = strdup(argv[2]); 156 | else 157 | msg.filename = prepend_cwd(argv[2]); 158 | 159 | msg.line = strtol(argv[3], &end, 10); 160 | if (*end != '\0') { 161 | fprintf(stderr, "Failed to parse an int from string: %s\n", argv[3]); 162 | exit(1); 163 | } 164 | msg.col = strtol(argv[4], &end, 10); 165 | if (*end != '\0') { 166 | fprintf(stderr, "Failed to parse an int from string: %s\n", argv[4]); 167 | exit(1); 168 | } 169 | 170 | // if there is a fifth argument, load currently editted buffer 171 | // from a file, otherwise use stdin 172 | if (argc == 6) { 173 | const char *fn = argv[5]; 174 | if (read_file(&msg.buffer.addr, &sz, fn) == -1) { 175 | fprintf(stderr, "Error! Failed to read from file: %s\n", fn); 176 | exit(1); 177 | } 178 | msg.buffer.sz = (uint32_t)sz; 179 | } else { 180 | if (read_stdin(&msg.buffer.addr, &sz) == -1) { 181 | fprintf(stderr, "Error! Failed to read from stdin\n"); 182 | exit(1); 183 | } 184 | msg.buffer.sz = (uint32_t)sz; 185 | } 186 | 187 | // send msg type 188 | tpl_node *tn = msg_node_pack(MSG_AC); 189 | tpl_dump(tn, TPL_FD, sock); 190 | tpl_free(tn); 191 | 192 | // send ac msg itself 193 | tn = msg_ac_node(&msg); 194 | tpl_pack(tn, 0); 195 | tpl_dump(tn, TPL_FD, sock); 196 | tpl_free(tn); 197 | 198 | struct msg_ac_response msg_r; 199 | 200 | msg_ac_response_recv(&msg_r, sock); 201 | printf("[%d, [", msg_r.partial); 202 | for (size_t i = 0; i < msg_r.proposals_n; ++i) { 203 | struct ac_proposal *p = &msg_r.proposals[i]; 204 | printf("{'word':'%s','abbr':'%s'}", p->word, p->abbr); 205 | if (i != msg_r.proposals_n - 1) 206 | printf(","); 207 | 208 | } 209 | printf("]]"); 210 | free_msg_ac_response(&msg_r); 211 | close(sock); 212 | } else { 213 | printf("ccode client, commands:\n" 214 | " close\n" 215 | " ac (+ currently editted buffer as stdin)\n"); 216 | } 217 | 218 | } 219 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "shared.h" 3 | 4 | int main(int argc, char **argv) 5 | { 6 | if (argc > 1 && strcmp("-s", argv[1]) == 0) { 7 | server_main(); 8 | } else { 9 | client_main(argc, argv); 10 | } 11 | return 0; 12 | } 13 | -------------------------------------------------------------------------------- /misc.c: -------------------------------------------------------------------------------- 1 | #include "shared.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | int file_exists(const char *filename) 8 | { 9 | struct stat st; 10 | return stat(filename, &st) == 0; 11 | } 12 | 13 | int starts_with(const char *s1, const char *s2) 14 | { 15 | while (*s2) if (*s1++ != *s2++) return 0; return 1; 16 | } 17 | 18 | int read_file(void **out, size_t *size, const char *filename) 19 | { 20 | struct stat st; 21 | FILE *f = fopen(filename, "r"); 22 | if (!f) 23 | return -1; 24 | 25 | if (-1 == fstat(fileno(f), &st)) { 26 | fclose(f); 27 | return -1; 28 | } 29 | 30 | *size = st.st_size; 31 | *out = malloc(*size); 32 | if (*size != fread(*out, 1, *size, f)) { 33 | fclose(f); 34 | free(*out); 35 | return -1; 36 | } 37 | 38 | return 0; 39 | } 40 | 41 | int read_stdin(void **out, size_t *size) 42 | { 43 | size_t read_n = 0; 44 | size_t alloc_n = 1024; 45 | void *buf = 0; 46 | 47 | *out = buf; 48 | *size = read_n; 49 | 50 | while (1) { 51 | if (feof(stdin)) 52 | break; 53 | if (ferror(stdin)) { 54 | free(buf); 55 | return -1; 56 | } 57 | buf = realloc(buf, alloc_n); 58 | alloc_n *= 2; 59 | size_t n = fread(buf+read_n, 1, 1024, stdin); 60 | read_n += n; 61 | } 62 | 63 | *out = buf; 64 | *size = read_n; 65 | return 0; 66 | } 67 | 68 | str_t *get_socket_path() 69 | { 70 | char *user = getenv("USER"); 71 | if (user) 72 | return str_printf("/tmp/ccode-server.%s", user); 73 | else 74 | return str_from_cstr("/tmp/ccode-server"); 75 | } 76 | -------------------------------------------------------------------------------- /proto.c: -------------------------------------------------------------------------------- 1 | #include "shared.h" 2 | #include 3 | 4 | tpl_node *msg_node_pack(int msgtype) 5 | { 6 | tpl_node *tn = tpl_map("i", &msgtype); 7 | tpl_pack(tn, 0); 8 | return tn; 9 | } 10 | 11 | //------------------------------------------------------------------------- 12 | 13 | tpl_node *msg_ac_node(struct msg_ac *msg) 14 | { 15 | tpl_node *tn = tpl_map(MSG_AC_FMT, 16 | &msg->buffer, 17 | &msg->filename, 18 | &msg->line, 19 | &msg->col); 20 | return tn; 21 | } 22 | 23 | void free_msg_ac(struct msg_ac *msg) 24 | { 25 | free(msg->buffer.addr); 26 | free(msg->filename); 27 | } 28 | 29 | //------------------------------------------------------------------------- 30 | 31 | void msg_ac_response_send(struct msg_ac_response *msg, int sock) 32 | { 33 | struct ac_proposal prop; 34 | tpl_node *tn; 35 | 36 | tn = tpl_map(MSG_AC_RESPONSE_FMT, 37 | &msg->partial, 38 | &prop); 39 | tpl_pack(tn, 0); 40 | for (size_t i = 0; i < msg->proposals_n; ++i) { 41 | prop = msg->proposals[i]; 42 | tpl_pack(tn, 1); 43 | } 44 | tpl_dump(tn, TPL_FD, sock); 45 | tpl_free(tn); 46 | } 47 | 48 | void msg_ac_response_recv(struct msg_ac_response *msg, int sock) 49 | { 50 | struct ac_proposal prop; 51 | tpl_node *tn; 52 | 53 | tn = tpl_map(MSG_AC_RESPONSE_FMT, 54 | &msg->partial, 55 | &prop); 56 | tpl_load(tn, TPL_FD, sock); 57 | tpl_unpack(tn, 0); 58 | msg->proposals_n = tpl_Alen(tn, 1); 59 | msg->proposals = malloc(sizeof(struct ac_proposal) * 60 | msg->proposals_n); 61 | for (size_t i = 0; i < msg->proposals_n; ++i) { 62 | tpl_unpack(tn, 1); 63 | msg->proposals[i] = prop; 64 | } 65 | tpl_free(tn); 66 | } 67 | 68 | void free_msg_ac_response(struct msg_ac_response *msg) 69 | { 70 | for (size_t i = 0; i < msg->proposals_n; ++i) { 71 | struct ac_proposal *p = &msg->proposals[i]; 72 | free(p->abbr); 73 | free(p->word); 74 | } 75 | if (msg->proposals) 76 | free(msg->proposals); 77 | } 78 | -------------------------------------------------------------------------------- /server.c: -------------------------------------------------------------------------------- 1 | #include "shared.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | struct make_ac_ctx { 14 | str_t *word; 15 | str_t *abbr; 16 | str_t *type; 17 | str_t *text; 18 | }; 19 | 20 | static void init_make_ac_ctx(struct make_ac_ctx *ctx); 21 | static void free_make_ac_ctx(struct make_ac_ctx *ctx); 22 | 23 | // for reference 24 | static int create_server_socket(const str_t *file); 25 | static void server_loop(int sock); 26 | static void process_ac(int sock); 27 | static void print_completion_result(CXCompletionResult *r); 28 | static int make_ac_proposal(struct make_ac_ctx *ctx, 29 | struct ac_proposal *p, 30 | CXCompletionResult *r, 31 | str_t *fmt); 32 | static str_t *extract_partial(struct msg_ac *msg); 33 | static int isident(int c); 34 | static void try_load_dotccode(wordexp_t *wexp); 35 | static void handle_sigint(int); 36 | static int wordexps_the_same(wordexp_t *a, wordexp_t *b); 37 | static int needs_reparsing(wordexp_t *w, const char *filename); 38 | static void sort_cc_results(CXCompletionResult *results, size_t results_n); 39 | static int code_completion_results_cmp(CXCompletionResult *r1, 40 | CXCompletionResult *r2); 41 | static CXString get_result_typed_text(CXCompletionResult *r); 42 | static size_t count_type_chars(CXCompletionResult *r); 43 | static size_t filter_out_cc_results(CXCompletionResult *results, 44 | size_t results_n, str_t *partial, 45 | str_t **fmt); 46 | static str_t *all_results_fmt(CXCompletionResult *results, 47 | size_t results_n); 48 | 49 | //------------------------------------------------------------------------- 50 | 51 | static CXIndex clang_index; 52 | static CXTranslationUnit clang_tu; 53 | static char *last_filename; 54 | static wordexp_t last_wordexp; 55 | static str_t *sock_path; 56 | 57 | #define SERVER_SOCKET_BACKLOG 10 58 | #define MAX_AC_RESULTS 999999 59 | #define MAX_TYPE_CHARS 20 60 | #define WIDTH_SIGNIFICANCE_THRESHOLD 100 61 | #define AUTO_SHUTDOWN_TIME 15 62 | 63 | static void init_make_ac_ctx(struct make_ac_ctx *ctx) 64 | { 65 | ctx->word = str_new(0); 66 | ctx->abbr = str_new(0); 67 | ctx->type = str_new(0); 68 | ctx->text = str_new(0); 69 | } 70 | 71 | static void free_make_ac_ctx(struct make_ac_ctx *ctx) 72 | { 73 | str_free(ctx->word); 74 | str_free(ctx->abbr); 75 | str_free(ctx->type); 76 | str_free(ctx->text); 77 | } 78 | 79 | static int needs_reparsing(wordexp_t *w, const char *filename) 80 | { 81 | if (!last_filename) 82 | return 1; 83 | 84 | if (strcmp(filename, last_filename) != 0) 85 | return 1; 86 | 87 | if (!wordexps_the_same(w, &last_wordexp)) 88 | return 1; 89 | 90 | return 0; 91 | } 92 | 93 | static int create_server_socket(const str_t *file) 94 | { 95 | int sock = socket(AF_UNIX, SOCK_STREAM, 0); 96 | if (sock == -1) 97 | return -1; 98 | 99 | struct sockaddr_un addr; 100 | fstr_t addrpath; 101 | 102 | addr.sun_family = AF_UNIX; 103 | 104 | FSTR_INIT_FOR_BUF(&addrpath, addr.sun_path); 105 | fstr_add_str(&addrpath, file); 106 | 107 | if (-1 == bind(sock, (struct sockaddr*)&addr, sizeof addr)) 108 | return -1; 109 | 110 | if (-1 == listen(sock, SERVER_SOCKET_BACKLOG)) 111 | return -1; 112 | 113 | return sock; 114 | } 115 | 116 | static void server_loop(int sock) 117 | { 118 | struct timeval oneminute = { 60, 0 }; 119 | fd_set sockset; 120 | int minutes_idle = 0; 121 | 122 | // accepting and dispatching messages 123 | for (;;) { 124 | int msg_type; 125 | tpl_node *tn; 126 | int incoming; 127 | int maxfd, result; 128 | 129 | FD_ZERO(&sockset); 130 | FD_SET(sock, &sockset); 131 | maxfd = sock; 132 | result = select(maxfd+1, &sockset, 0, 0, &oneminute); 133 | if (!result) { 134 | minutes_idle++; 135 | if (minutes_idle >= AUTO_SHUTDOWN_TIME) 136 | return; 137 | continue; 138 | } 139 | 140 | minutes_idle = 0; 141 | incoming = accept(sock, 0, 0); 142 | if (incoming == -1) { 143 | fprintf(stderr, "Error! Failed to accept an incoming connection.\n"); 144 | exit(1); 145 | } 146 | 147 | tn = tpl_map("i", &msg_type); 148 | tpl_load(tn, TPL_FD, incoming); 149 | tpl_unpack(tn, 0); 150 | tpl_free(tn); 151 | 152 | switch (msg_type) { 153 | case MSG_CLOSE: 154 | close(incoming); 155 | return; 156 | case MSG_AC: 157 | process_ac(incoming); 158 | break; 159 | default: 160 | ; 161 | } 162 | 163 | close(incoming); 164 | } 165 | } 166 | 167 | static str_t *extract_partial(struct msg_ac *msg) 168 | { 169 | char *cursor; 170 | char *c = msg->buffer.addr; 171 | char *end = msg->buffer.addr + msg->buffer.sz; 172 | 173 | for (int line = 1; line < msg->line; line++) { 174 | while (1) { 175 | if (c == end) 176 | return 0; 177 | if (*c == '\n') { 178 | c++; 179 | break; 180 | } 181 | c++; 182 | } 183 | } 184 | 185 | cursor = c + (msg->col - 1); 186 | c += msg->col - 2; 187 | while (isident(*c)) 188 | c--; 189 | c++; 190 | 191 | if (c == cursor) 192 | return 0; 193 | 194 | return str_from_cstr_len(c, cursor - c); 195 | } 196 | 197 | static int isident(int c) 198 | { 199 | if (isalnum(c) || c == '_') 200 | return 1; 201 | return 0; 202 | } 203 | 204 | static void try_load_dotccode(wordexp_t *wexp) 205 | { 206 | void *buf; 207 | size_t size; 208 | 209 | wexp->we_wordc = 0; 210 | wexp->we_wordv = 0; 211 | 212 | if (read_file(&buf, &size, ".ccode") == -1) { 213 | return; 214 | } 215 | 216 | // TODO: fstr trim? cstr trim? 217 | str_t *contents = str_from_cstr_len(buf, (unsigned int)size); 218 | str_trim(contents); 219 | 220 | wordexp(contents->data, wexp, 0); 221 | str_free(contents); 222 | free(buf); 223 | } 224 | 225 | static void change_dir(const char *filename) 226 | { 227 | str_t *dir, *fn; 228 | fn = str_from_cstr(filename); 229 | dir = str_split_path(fn, 0); 230 | chdir(dir->data); 231 | str_free(dir); 232 | str_free(fn); 233 | } 234 | 235 | static void process_ac(int sock) 236 | { 237 | tpl_node *tn; 238 | struct msg_ac msg; 239 | wordexp_t flags; 240 | 241 | tn = msg_ac_node(&msg); 242 | tpl_load(tn, TPL_FD, sock); 243 | tpl_unpack(tn, 0); 244 | tpl_free(tn); 245 | 246 | struct CXUnsavedFile unsaved = { 247 | msg.filename, 248 | msg.buffer.addr, 249 | msg.buffer.sz 250 | }; 251 | 252 | change_dir(msg.filename); 253 | try_load_dotccode(&flags); 254 | 255 | str_t *partial = extract_partial(&msg); 256 | 257 | if (partial) 258 | msg.col -= partial->len; 259 | 260 | if (needs_reparsing(&flags, msg.filename)) { 261 | if (clang_tu) 262 | clang_disposeTranslationUnit(clang_tu); 263 | 264 | clang_tu = clang_parseTranslationUnit(clang_index, msg.filename, 265 | (char const * const *)flags.we_wordv, 266 | flags.we_wordc, 267 | &unsaved, 1, 268 | clang_defaultEditingTranslationUnitOptions()); 269 | if (last_filename) 270 | free(last_filename); 271 | if (last_wordexp.we_wordv) 272 | wordfree(&last_wordexp); 273 | last_filename = strdup(msg.filename); 274 | last_wordexp = flags; 275 | } 276 | 277 | // diag 278 | /* 279 | for (int i = 0, n = clang_getNumDiagnostics(clang_tu); i != n; ++i) { 280 | CXDiagnostic diag = clang_getDiagnostic(clang_tu, i); 281 | CXString string = clang_formatDiagnostic(diag, clang_defaultDiagnosticDisplayOptions()); 282 | fprintf(stderr, "%s\n", clang_getCString(string)); 283 | clang_disposeString(string); 284 | clang_disposeDiagnostic(diag); 285 | } 286 | */ 287 | 288 | CXCodeCompleteResults *results; 289 | results = clang_codeCompleteAt(clang_tu, msg.filename, msg.line, msg.col, 290 | &unsaved, 1, 291 | CXCodeComplete_IncludeMacros); 292 | free_msg_ac(&msg); 293 | 294 | // diag 295 | /* 296 | for (int i = 0, n = clang_codeCompleteGetNumDiagnostics(results); i != n; ++i) { 297 | CXDiagnostic diag = clang_codeCompleteGetDiagnostic(results, i); 298 | CXString string = clang_formatDiagnostic(diag, clang_defaultDiagnosticDisplayOptions()); 299 | fprintf(stderr, "%s\n", clang_getCString(string)); 300 | clang_disposeString(string); 301 | clang_disposeDiagnostic(diag); 302 | } 303 | */ 304 | 305 | struct msg_ac_response msg_r = { (partial) ? partial->len : 0, 0, 0 }; 306 | 307 | if (results) { 308 | struct make_ac_ctx ctx; 309 | str_t *fmt; 310 | 311 | init_make_ac_ctx(&ctx); 312 | msg_r.proposals_n = filter_out_cc_results(results->Results, 313 | results->NumResults, 314 | partial, &fmt); 315 | sort_cc_results(results->Results, msg_r.proposals_n); 316 | if (msg_r.proposals_n > MAX_AC_RESULTS) 317 | msg_r.proposals_n = MAX_AC_RESULTS; 318 | msg_r.proposals = malloc(sizeof(struct ac_proposal) * 319 | msg_r.proposals_n); 320 | 321 | int cur = 0; 322 | for (int i = 0; i < msg_r.proposals_n; ++i) { 323 | int added; 324 | added = make_ac_proposal(&ctx, 325 | &msg_r.proposals[cur], 326 | &results->Results[i], 327 | fmt); 328 | if (added) 329 | cur++; 330 | } 331 | msg_r.proposals_n = cur; 332 | free_make_ac_ctx(&ctx); 333 | str_free(fmt); 334 | } 335 | 336 | if (partial) 337 | 338 | str_free(partial); 339 | clang_disposeCodeCompleteResults(results); 340 | 341 | msg_ac_response_send(&msg_r, sock); 342 | free_msg_ac_response(&msg_r); 343 | } 344 | 345 | static int code_completion_results_cmp(CXCompletionResult *r1, 346 | CXCompletionResult *r2) 347 | { 348 | int prio1 = clang_getCompletionPriority(r1->CompletionString); 349 | int prio2 = clang_getCompletionPriority(r2->CompletionString); 350 | if (prio1 != prio2) 351 | return prio1 - prio2; 352 | 353 | CXString r1t = get_result_typed_text(r1); 354 | CXString r2t = get_result_typed_text(r2); 355 | int cmp = strcmp(clang_getCString(r1t), 356 | clang_getCString(r2t)); 357 | clang_disposeString(r1t); 358 | clang_disposeString(r2t); 359 | return cmp; 360 | } 361 | 362 | static CXString get_result_typed_text(CXCompletionResult *r) 363 | { 364 | unsigned int chunks_n = clang_getNumCompletionChunks(r->CompletionString); 365 | for (unsigned int i = 0; i < chunks_n; ++i) { 366 | enum CXCompletionChunkKind kind; 367 | kind = clang_getCompletionChunkKind(r->CompletionString, i); 368 | if (kind == CXCompletionChunk_TypedText) 369 | return clang_getCompletionChunkText(r->CompletionString, i); 370 | } 371 | CXString empty = {0,0}; 372 | return empty; 373 | } 374 | 375 | static size_t count_type_chars(CXCompletionResult *r) 376 | { 377 | unsigned int chars = 0; 378 | unsigned int chunks_n = clang_getNumCompletionChunks(r->CompletionString); 379 | for (unsigned int i = 0; i < chunks_n; ++i) { 380 | enum CXCompletionChunkKind kind; 381 | kind = clang_getCompletionChunkKind(r->CompletionString, i); 382 | if (kind == CXCompletionChunk_ResultType) { 383 | CXString s = clang_getCompletionChunkText(r->CompletionString, i); 384 | chars += strlen(clang_getCString(s)); 385 | clang_disposeString(s); 386 | } 387 | } 388 | return chars; 389 | } 390 | 391 | static void sort_cc_results(CXCompletionResult *results, size_t results_n) 392 | { 393 | qsort(results, results_n, sizeof(CXCompletionResult), 394 | (int (*)(const void*,const void*))code_completion_results_cmp); 395 | } 396 | 397 | static str_t *all_results_fmt(CXCompletionResult *results, 398 | size_t results_n) 399 | { 400 | if (results_n > WIDTH_SIGNIFICANCE_THRESHOLD) 401 | return str_printf("%%%ds %%s", MAX_TYPE_CHARS); 402 | 403 | size_t maxl = 0; 404 | for (size_t i = 0; i < results_n; ++i) { 405 | if (maxl != MAX_TYPE_CHARS) { 406 | size_t l = count_type_chars(&results[i]); 407 | if (l > maxl) { 408 | if (l > MAX_TYPE_CHARS) 409 | maxl = MAX_TYPE_CHARS; 410 | else 411 | maxl = l; 412 | } 413 | } 414 | } 415 | return str_printf("%%%ds %%s", maxl); 416 | } 417 | 418 | static size_t filter_out_cc_results(CXCompletionResult *results, 419 | size_t results_n, 420 | str_t *partial, 421 | str_t **fmt) 422 | { 423 | if (!partial) { 424 | *fmt = all_results_fmt(results, results_n); 425 | return results_n; 426 | } 427 | 428 | size_t maxl = 0; 429 | size_t cur = 0; 430 | for (size_t i = 0; i < results_n; ++i) { 431 | CXString s = get_result_typed_text(&results[i]); 432 | if (!s.data) 433 | continue; 434 | if (!starts_with(clang_getCString(s), partial->data)) { 435 | clang_disposeString(s); 436 | continue; 437 | } 438 | clang_disposeString(s); 439 | 440 | CXCompletionResult tmp = results[cur]; 441 | results[cur] = results[i]; 442 | results[i] = tmp; 443 | 444 | if (maxl != MAX_TYPE_CHARS) { 445 | size_t l = count_type_chars(&results[cur]); 446 | if (l > maxl) { 447 | if (l > MAX_TYPE_CHARS) 448 | maxl = MAX_TYPE_CHARS; 449 | else 450 | maxl = l; 451 | } 452 | } 453 | 454 | cur++; 455 | } 456 | *fmt = str_printf("%%%ds %%s", maxl); 457 | return cur; 458 | } 459 | 460 | static int make_ac_proposal(struct make_ac_ctx *ctx, struct ac_proposal *p, 461 | CXCompletionResult *r, str_t *fmt) 462 | { 463 | unsigned int chunks_n; 464 | 465 | chunks_n = clang_getNumCompletionChunks(r->CompletionString); 466 | str_clear(ctx->word); 467 | str_clear(ctx->abbr); 468 | str_clear(ctx->type); 469 | str_clear(ctx->text); 470 | 471 | for (unsigned int i = 0; i < chunks_n; ++i) { 472 | enum CXCompletionChunkKind kind; 473 | CXString s; 474 | 475 | kind = clang_getCompletionChunkKind(r->CompletionString, i); 476 | s = clang_getCompletionChunkText(r->CompletionString, i); 477 | switch (kind) { 478 | case CXCompletionChunk_ResultType: 479 | str_add_printf(&ctx->type, "%s", clang_getCString(s)); 480 | break; 481 | case CXCompletionChunk_TypedText: 482 | str_add_cstr(&ctx->word, clang_getCString(s)); 483 | default: 484 | str_add_cstr(&ctx->text, clang_getCString(s)); 485 | break; 486 | } 487 | clang_disposeString(s); 488 | } 489 | 490 | if (ctx->type->len > MAX_TYPE_CHARS) { 491 | ctx->type->len = MAX_TYPE_CHARS-1; 492 | str_add_cstr(&ctx->type, "…"); 493 | } 494 | str_add_printf(&ctx->abbr, fmt->data, 495 | ctx->type->data, ctx->text->data); 496 | 497 | p->abbr = strdup(ctx->abbr->data); 498 | p->word = strdup(ctx->word->data); 499 | return 1; 500 | } 501 | 502 | static void handle_sigint(int unused) 503 | { 504 | unlink(sock_path->data); 505 | exit(0); 506 | } 507 | 508 | static int wordexps_the_same(wordexp_t *a, wordexp_t *b) 509 | { 510 | if (a->we_wordc != b->we_wordc) 511 | return 0; 512 | 513 | for (size_t i = 0; i < a->we_wordc; i++) { 514 | if (strcmp(a->we_wordv[i], b->we_wordv[i]) != 0) 515 | return 0; 516 | } 517 | return 1; 518 | } 519 | 520 | void server_main() 521 | { 522 | struct sigaction sa; 523 | int sock; 524 | 525 | sock_path = get_socket_path(); 526 | sock = create_server_socket(sock_path); 527 | if (sock == -1) { 528 | fprintf(stderr, "Error! Failed to create a server socket: %s\n", 529 | sock_path->data); 530 | exit(1); 531 | } 532 | 533 | sa.sa_handler = handle_sigint; 534 | sa.sa_flags = 0; 535 | sigaction(SIGINT, &sa, 0); 536 | 537 | clang_index = clang_createIndex(0, 0); 538 | server_loop(sock); 539 | if (clang_tu) 540 | clang_disposeTranslationUnit(clang_tu); 541 | clang_disposeIndex(clang_index); 542 | 543 | close(sock); 544 | unlink(sock_path->data); 545 | str_free(sock_path); 546 | } 547 | -------------------------------------------------------------------------------- /shared.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "strstr.h" 4 | #include "tpl.h" 5 | 6 | //------------------------------------------------------------------------- 7 | // Protocol 8 | //------------------------------------------------------------------------- 9 | 10 | tpl_node *msg_node_pack(int msgtype); 11 | 12 | // CLOSE 13 | 14 | #define MSG_CLOSE 0 15 | 16 | // AC (autocompletion) 17 | 18 | #define MSG_AC 1 19 | #define MSG_AC_FMT "Bsii" 20 | 21 | struct msg_ac { 22 | tpl_bin buffer; 23 | char *filename; 24 | int line; 25 | int col; 26 | }; 27 | 28 | tpl_node *msg_ac_node(struct msg_ac *msg); 29 | void free_msg_ac(struct msg_ac *msg); 30 | 31 | // AC_RESPONSE 32 | 33 | #define MSG_AC_RESPONSE 2 34 | #define MSG_AC_RESPONSE_FMT "iA(S(ss))" 35 | 36 | struct ac_proposal { 37 | char *word; 38 | char *abbr; 39 | }; 40 | 41 | struct msg_ac_response { 42 | int partial; 43 | struct ac_proposal *proposals; 44 | size_t proposals_n; 45 | }; 46 | 47 | void msg_ac_response_send(struct msg_ac_response *msg, int sock); 48 | void msg_ac_response_recv(struct msg_ac_response *msg, int sock); 49 | void free_msg_ac_response(struct msg_ac_response *msg); 50 | 51 | //------------------------------------------------------------------------- 52 | // Misc 53 | //------------------------------------------------------------------------- 54 | 55 | int file_exists(const char *filename); 56 | int starts_with(const char *s1, const char *s2); 57 | 58 | // read file to a newly allocated buf, 0 on success, -1 on error 59 | int read_file(void **out, size_t *size, const char *filename); 60 | int read_stdin(void **out, size_t *size); 61 | 62 | str_t *get_socket_path(); 63 | 64 | void client_main(int argc, char **argv); 65 | void server_main(); 66 | -------------------------------------------------------------------------------- /strstr.c: -------------------------------------------------------------------------------- 1 | #include "strstr.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | //------------------------------------------------------------------------------- 11 | // Default allocator 12 | //------------------------------------------------------------------------------- 13 | 14 | static void *xmalloc(size_t size) 15 | { 16 | void *m = malloc(size); 17 | if (!m) { 18 | fprintf(stderr, "Fatal error! Memory allocation failed.\n"); 19 | exit(1); 20 | } 21 | return m; 22 | } 23 | 24 | static __thread str_allocator_t allocator = { 25 | xmalloc, 26 | free 27 | }; 28 | 29 | void str_set_allocator(const str_allocator_t *a) 30 | { 31 | allocator = *a; 32 | } 33 | 34 | void str_get_allocator(str_allocator_t *a) 35 | { 36 | *a = allocator; 37 | } 38 | 39 | //------------------------------------------------------------------------------- 40 | // STR 41 | //------------------------------------------------------------------------------- 42 | 43 | static int is_cstr_in_str(str_t *str, const char *cstr) 44 | { 45 | if ((str->data <= cstr) && (str->data + str->cap >= cstr)) 46 | return 1; 47 | return 0; 48 | } 49 | 50 | //------------------------------------------------------------------------------ 51 | 52 | str_t *str_new(unsigned int cap) 53 | { 54 | if (!cap) 55 | cap = STR_DEFAULT_CAPACITY; 56 | str_t *str = (*allocator.malloc)(sizeof(str_t) + cap + 1); 57 | str->len = 0; 58 | str->cap = cap; 59 | str->data[0] = '\0'; 60 | return str; 61 | } 62 | 63 | void str_free(str_t *str) 64 | { 65 | (*allocator.free)(str); 66 | } 67 | 68 | void str_clear(str_t *str) 69 | { 70 | str->len = 0; 71 | str->data[0] = '\0'; 72 | } 73 | 74 | str_t *str_from_cstr(const char *cstr) 75 | { 76 | assert(cstr != 0); 77 | return str_from_cstr_len(cstr, strlen(cstr)); 78 | } 79 | 80 | str_t *str_from_cstr_len(const char *cstr, unsigned int len) 81 | { 82 | unsigned int cap = len > 0 ? len : STR_DEFAULT_CAPACITY; 83 | str_t *str = (*allocator.malloc)(sizeof(str_t) + cap + 1); 84 | str->len = len; 85 | str->cap = cap; 86 | if (len > 0) 87 | memcpy(str->data, cstr, len); 88 | str->data[len] = '\0'; 89 | return str; 90 | } 91 | 92 | str_t *str_dup(const str_t *rhs) 93 | { 94 | assert(rhs != 0); 95 | return str_from_cstr_len(rhs->data, rhs->len); 96 | } 97 | 98 | str_t *str_from_file(const char *filename) 99 | { 100 | assert(filename != 0); 101 | 102 | struct stat st; 103 | FILE *f; 104 | str_t *str; 105 | 106 | if (-1 == stat(filename, &st)) 107 | return 0; 108 | 109 | f = fopen(filename, "r"); 110 | if (!f) 111 | return 0; 112 | 113 | 114 | str = (*allocator.malloc)(sizeof(str_t) + st.st_size + 1); 115 | str->cap = str->len = st.st_size; 116 | if (st.st_size != fread(str->data, 1, st.st_size, f)) { 117 | fclose(f); 118 | (*allocator.free)(str); 119 | return 0; 120 | } 121 | fclose(f); 122 | str->data[st.st_size] = '\0'; 123 | return str; 124 | } 125 | 126 | void str_ensure_cap(str_t **out_str, unsigned int n) 127 | { 128 | assert(out_str != 0); 129 | assert(*out_str != 0); 130 | 131 | str_t *str = *out_str; 132 | if (str->cap - str->len < n) { 133 | unsigned int newcap = str->cap * 2; 134 | if (newcap - str->len < n) 135 | newcap = str->len + n; 136 | 137 | str_t *newstr = (*allocator.malloc)(sizeof(str_t) + newcap + 1); 138 | newstr->cap = newcap; 139 | newstr->len = str->len; 140 | if (str->len > 0) 141 | memcpy(newstr->data, str->data, str->len + 1); 142 | else 143 | newstr->data[0] = '\0'; 144 | (*allocator.free)(str); 145 | *out_str = newstr; 146 | } 147 | } 148 | 149 | str_t *str_printf(const char *fmt, ...) 150 | { 151 | assert(fmt != 0); 152 | 153 | va_list va; 154 | 155 | va_start(va, fmt); 156 | unsigned int len = vsnprintf(0, 0, fmt, va); 157 | va_end(va); 158 | 159 | str_t *str = (*allocator.malloc)(sizeof(str_t) + len + 1); 160 | str->len = str->cap = len; 161 | va_start(va, fmt); 162 | vsnprintf(str->data, len + 1, fmt, va); 163 | va_end(va); 164 | return str; 165 | } 166 | 167 | void str_add_str(str_t **str, const str_t *str2) 168 | { 169 | assert(str != 0); 170 | assert(str2 != 0); 171 | assert(*str != 0); 172 | assert(*str != str2); 173 | 174 | str_add_cstr_len(str, str2->data, str2->len); 175 | } 176 | 177 | void str_add_cstr(str_t **str, const char *cstr) 178 | { 179 | assert(str != 0); 180 | assert(cstr != 0); 181 | assert(*str != 0); 182 | assert(!is_cstr_in_str(*str, cstr)); 183 | 184 | str_add_cstr_len(str, cstr, strlen(cstr)); 185 | } 186 | 187 | void str_add_cstr_len(str_t **str, const char *data, unsigned int len) 188 | { 189 | if (!len) 190 | return; 191 | 192 | str_ensure_cap(str, len); 193 | 194 | str_t *s = *str; 195 | memcpy(&s->data[s->len], data, len + 1); 196 | s->len += len; 197 | } 198 | 199 | void str_add_printf(str_t **str, const char *fmt, ...) 200 | { 201 | assert(str != 0); 202 | assert(fmt != 0); 203 | assert(*str != 0); 204 | assert(!is_cstr_in_str(*str, fmt)); 205 | 206 | va_list va; 207 | 208 | va_start(va, fmt); 209 | unsigned int len = vsnprintf(0, 0, fmt, va); 210 | va_end(va); 211 | 212 | str_ensure_cap(str, len); 213 | 214 | str_t *s = *str; 215 | va_start(va, fmt); 216 | vsnprintf(&s->data[s->len], len + 1, fmt, va); 217 | va_end(va); 218 | s->len += len; 219 | } 220 | 221 | void str_add_file(str_t **str, const char *filename) 222 | { 223 | assert(str != 0); 224 | assert(filename != 0); 225 | assert(*str != 0); 226 | 227 | struct stat st; 228 | FILE *f; 229 | 230 | if (-1 == stat(filename, &st)) 231 | return; 232 | 233 | f = fopen(filename, "r"); 234 | if (!f) 235 | return; 236 | 237 | str_ensure_cap(str, st.st_size); 238 | str_t *s = *str; 239 | if (st.st_size == fread(s->data + s->len, 1, st.st_size, f)) 240 | s->len += st.st_size; 241 | fclose(f); 242 | s->data[s->len] = '\0'; 243 | } 244 | 245 | void str_trim(str_t *str) 246 | { 247 | str_rtrim(str); 248 | str_ltrim(str); 249 | } 250 | 251 | void str_ltrim(str_t *str) 252 | { 253 | char *c = str->data; 254 | while (str->len > 0 && isspace(*c)) { 255 | str->len--; 256 | c++; 257 | } 258 | memmove(str->data, c, str->len); 259 | str->data[str->len] = '\0'; 260 | } 261 | 262 | void str_rtrim(str_t *str) 263 | { 264 | while (str->len > 0 && isspace(str->data[str->len - 1])) 265 | str->len--; 266 | str->data[str->len] = '\0'; 267 | } 268 | 269 | str_t *str_split_path(const str_t *str, str_t **half2) 270 | { 271 | const char *c = str->data + (str->len - 1); 272 | while (c != str->data && *c != '/') 273 | c--; 274 | 275 | if (c == str->data) { 276 | if (half2) 277 | *half2 = str_dup(str); 278 | return 0; 279 | } 280 | 281 | if (half2) 282 | *half2 = str_from_cstr_len(c+1, str->data + str->len - (c+1)); 283 | 284 | return str_from_cstr_len(str->data, c - str->data); 285 | } 286 | 287 | //------------------------------------------------------------------------------- 288 | // FSTR 289 | //------------------------------------------------------------------------------- 290 | 291 | void fstr_add_cstr_len(fstr_t *fstr, const char *data, unsigned int len) 292 | { 293 | if (!len) 294 | return; 295 | 296 | unsigned int avail = fstr->cap - fstr->len; 297 | if (len > avail) 298 | len = avail; 299 | 300 | memcpy(&fstr->data[fstr->len], data, len); 301 | fstr->data[fstr->len + len] = '\0'; 302 | fstr->len += len; 303 | } 304 | 305 | //------------------------------------------------------------------------------ 306 | 307 | void fstr_init(fstr_t *fstr, char *data, unsigned int len, unsigned int cap) 308 | { 309 | assert(fstr != 0); 310 | assert(data != 0); 311 | assert(cap >= len); 312 | assert(cap > 0); 313 | 314 | fstr->cap = cap; 315 | fstr->len = len; 316 | fstr->data = data; 317 | fstr->data[0] = '\0'; 318 | } 319 | 320 | void fstr_add_str(fstr_t *fstr, const str_t *str) 321 | { 322 | assert(fstr != 0); 323 | assert(str != 0); 324 | 325 | fstr_add_cstr_len(fstr, str->data, str->len); 326 | } 327 | 328 | void fstr_add_cstr(fstr_t *fstr, const char *cstr) 329 | { 330 | assert(fstr != 0); 331 | assert(cstr != 0); 332 | 333 | fstr_add_cstr_len(fstr, cstr, strlen(cstr)); 334 | } 335 | 336 | void fstr_add_printf(fstr_t *fstr, const char *fmt, ...) 337 | { 338 | assert(fstr != 0); 339 | assert(fmt != 0); 340 | 341 | va_list va; 342 | unsigned int avail = fstr->cap - fstr->len; 343 | 344 | va_start(va, fmt); 345 | int plen = vsnprintf(&fstr->data[fstr->len], avail+1, fmt, va); 346 | va_end(va); 347 | 348 | assert(plen >= 0); 349 | 350 | unsigned int len = (unsigned int)plen; 351 | if (len > avail) 352 | len = avail; 353 | fstr->len += len; 354 | } 355 | -------------------------------------------------------------------------------- /strstr.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | * Str is a contiguous chunk of memory: 5 | * +-----+-----+-------------+ 6 | * | CAP | LEN | STRING DATA | 7 | * +-----+-----+-------------+ 8 | * 9 | * It can be used as a safe string implementation. The string itself is 10 | * null-terminated, so.. it's good for interop with standard C strings. 11 | * 12 | * I use 'unsigned int' as a type for length and capacity simply because 4 13 | * billion is enough for practically anything. And 'unsigned int' is 32 bits on 14 | * both x86 and x86_64, so I think it's a reasonable choice. 15 | * 16 | * The amount of memory required for a str with capacity == 5 is: 17 | * (sizeof(str_t) + 5 + 1) 18 | * 19 | * Additional byte is used for zero termination and it's not a part of the 20 | * capacity. 21 | * 22 | * String data is always a correct C string. 23 | */ 24 | 25 | #include /* for size_t */ 26 | 27 | typedef struct str_allocator { 28 | void *(*malloc)(size_t); 29 | void (*free)(void*); 30 | } str_allocator_t; 31 | 32 | /* These functions do not store the pointer to an allocator structure, they 33 | * just copy the contents of this structure. 34 | * 35 | * str_set_allocator - to specify a new pair of allocator functions for a 36 | * current thread 37 | * 38 | * str_get_allocator - to retrieve a pair of allocator functions for a current 39 | * thread. 40 | */ 41 | void str_set_allocator(const str_allocator_t *new_alloc); 42 | void str_get_allocator(str_allocator_t *out); 43 | 44 | /* should be > 0, and remember, that real memory size is +1 (trailing \0 byte) */ 45 | #ifndef STR_DEFAULT_CAPACITY 46 | #define STR_DEFAULT_CAPACITY 7 47 | #endif 48 | 49 | typedef struct str { 50 | unsigned int cap; 51 | unsigned int len; 52 | char data[]; 53 | } str_t; 54 | 55 | /* different ways to create a str */ 56 | str_t *str_new(unsigned int cap); 57 | str_t *str_from_cstr(const char *cstr); 58 | str_t *str_from_cstr_len(const char *cstr, unsigned int len); 59 | str_t *str_printf(const char *fmt, ...); 60 | str_t *str_dup(const str_t *str); 61 | str_t *str_from_file(const char *filename); 62 | 63 | void str_free(str_t *str); 64 | void str_clear(str_t *str); 65 | 66 | /* make sure there is enough capacity for 'n' additional bytes */ 67 | void str_ensure_cap(str_t **str, unsigned int n); 68 | 69 | /* appending to a str */ 70 | void str_add_str(str_t **str, const str_t *str2); 71 | void str_add_cstr(str_t **str, const char *cstr); 72 | void str_add_cstr_len(str_t **str, const char *cstr, unsigned int len); 73 | void str_add_printf(str_t **str, const char *fmt, ...); 74 | void str_add_file(str_t **str, const char *filename); 75 | 76 | /* trim, removes 'isspace' characters from sides: both, left, right */ 77 | void str_trim(str_t *str); 78 | void str_ltrim(str_t *str); 79 | void str_rtrim(str_t *str); 80 | 81 | /* Splits 'path' immediately following the final path separator, separating it 82 | * into a directory and file name component. Returns a directory component if 83 | * any (if none, returns zero). If 'half2' isn't zero, writes file name 84 | * component to it (allocating a str, you're responsible to free it). 85 | */ 86 | str_t *str_split_path(const str_t *path, str_t **half2); 87 | 88 | /* 89 | * FStr is a fixed string. 90 | * It doesn't manage its own memory, that's why it's called fixed. 91 | * 92 | * Length and capacity meanings are the same as in Str. Therefore usually the 93 | * capacity of the FStr is a buffer length minus one (zero termination). 94 | */ 95 | 96 | typedef struct fstr { 97 | unsigned int cap; 98 | unsigned int len; 99 | char *data; 100 | } fstr_t; 101 | 102 | #define FSTR_INIT_FOR_BUF(fstr, buf)\ 103 | fstr_init(fstr, buf, 0, sizeof(buf)/sizeof(buf[0])-1) 104 | void fstr_init(fstr_t *fstr, char *data, unsigned int len, unsigned int cap); 105 | 106 | /* appending to a fstr */ 107 | void fstr_add_str(fstr_t *fstr, const str_t *str); 108 | void fstr_add_cstr(fstr_t *fstr, const char *cstr); 109 | void fstr_add_printf(fstr_t *fstr, const char *fmt, ...); 110 | -------------------------------------------------------------------------------- /tpl.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2005-2010, Troy D. Hanson http://tpl.sourceforge.net 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 12 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 13 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 14 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 15 | OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 16 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 17 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 18 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 19 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 20 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 21 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | */ 23 | 24 | #define TPL_VERSION 1.5 25 | 26 | static const char id[]="$Id: tpl.c 192 2009-04-24 10:35:30Z thanson $"; 27 | 28 | 29 | #include /* malloc */ 30 | #include /* va_list */ 31 | #include /* memcpy, memset, strchr */ 32 | #include /* printf (tpl_hook.oops default function) */ 33 | 34 | #ifndef _WIN32 35 | #include /* for ftruncate */ 36 | #else 37 | #include 38 | #define ftruncate(x,y) _chsize(x,y) 39 | #endif 40 | #include /* for 'open' */ 41 | #include /* for 'open' */ 42 | #include /* for 'open' */ 43 | #include 44 | #ifndef _WIN32 45 | #include /* uint32_t, uint64_t, etc */ 46 | #else 47 | typedef unsigned short ushort; 48 | typedef __int16 int16_t; 49 | typedef __int32 int32_t; 50 | typedef __int64 int64_t; 51 | typedef unsigned __int16 uint16_t; 52 | typedef unsigned __int32 uint32_t; 53 | typedef unsigned __int64 uint64_t; 54 | #endif 55 | 56 | 57 | #if ( defined __CYGWIN__ || defined __MINGW32__ || defined _WIN32 ) 58 | #include "win/mman.h" /* mmap */ 59 | #else 60 | #include /* mmap */ 61 | #endif 62 | 63 | #include "tpl.h" 64 | 65 | #define TPL_GATHER_BUFLEN 8192 66 | #define TPL_MAGIC "tpl" 67 | 68 | /* macro to add a structure to a doubly-linked list */ 69 | #define DL_ADD(head,add) \ 70 | do { \ 71 | if (head) { \ 72 | (add)->prev = (head)->prev; \ 73 | (head)->prev->next = (add); \ 74 | (head)->prev = (add); \ 75 | (add)->next = NULL; \ 76 | } else { \ 77 | (head)=(add); \ 78 | (head)->prev = (head); \ 79 | (head)->next = NULL; \ 80 | } \ 81 | } while (0); 82 | 83 | #define fatal_oom() tpl_hook.fatal("out of memory\n") 84 | 85 | /* bit flags (internal). preceded by the external flags in tpl.h */ 86 | #define TPL_WRONLY (1 << 9) /* app has initiated tpl packing */ 87 | #define TPL_RDONLY (1 << 10) /* tpl was loaded (for unpacking) */ 88 | #define TPL_XENDIAN (1 << 11) /* swap endianness when unpacking */ 89 | #define TPL_OLD_STRING_FMT (1 << 12) /* tpl has strings in 1.2 format */ 90 | 91 | /* values for the flags byte that appears after the magic prefix */ 92 | #define TPL_SUPPORTED_BITFLAGS 3 93 | #define TPL_FL_BIGENDIAN (1 << 0) 94 | #define TPL_FL_NULLSTRINGS (1 << 1) 95 | 96 | /* char values for node type */ 97 | #define TPL_TYPE_ROOT 0 98 | #define TPL_TYPE_INT32 1 99 | #define TPL_TYPE_UINT32 2 100 | #define TPL_TYPE_BYTE 3 101 | #define TPL_TYPE_STR 4 102 | #define TPL_TYPE_ARY 5 103 | #define TPL_TYPE_BIN 6 104 | #define TPL_TYPE_DOUBLE 7 105 | #define TPL_TYPE_INT64 8 106 | #define TPL_TYPE_UINT64 9 107 | #define TPL_TYPE_INT16 10 108 | #define TPL_TYPE_UINT16 11 109 | #define TPL_TYPE_POUND 12 110 | 111 | /* error codes */ 112 | #define ERR_NOT_MINSIZE (-1) 113 | #define ERR_MAGIC_MISMATCH (-2) 114 | #define ERR_INCONSISTENT_SZ (-3) 115 | #define ERR_FMT_INVALID (-4) 116 | #define ERR_FMT_MISSING_NUL (-5) 117 | #define ERR_FMT_MISMATCH (-6) 118 | #define ERR_FLEN_MISMATCH (-7) 119 | #define ERR_INCONSISTENT_SZ2 (-8) 120 | #define ERR_INCONSISTENT_SZ3 (-9) 121 | #define ERR_INCONSISTENT_SZ4 (-10) 122 | #define ERR_UNSUPPORTED_FLAGS (-11) 123 | 124 | /* access to A(...) nodes by index */ 125 | typedef struct tpl_pidx { 126 | struct tpl_node *node; 127 | struct tpl_pidx *next,*prev; 128 | } tpl_pidx; 129 | 130 | /* A(...) node datum */ 131 | typedef struct tpl_atyp { 132 | uint32_t num; /* num elements */ 133 | size_t sz; /* size of each backbone's datum */ 134 | struct tpl_backbone *bb,*bbtail; 135 | void *cur; 136 | } tpl_atyp; 137 | 138 | /* backbone to extend A(...) lists dynamically */ 139 | typedef struct tpl_backbone { 140 | struct tpl_backbone *next; 141 | /* when this structure is malloc'd, extra space is alloc'd at the 142 | * end to store the backbone "datum", and data points to it. */ 143 | #if __STDC_VERSION__ < 199901 144 | char *data; 145 | #else 146 | char data[]; 147 | #endif 148 | } tpl_backbone; 149 | 150 | /* mmap record */ 151 | typedef struct tpl_mmap_rec { 152 | int fd; 153 | void *text; 154 | size_t text_sz; 155 | } tpl_mmap_rec; 156 | 157 | /* root node datum */ 158 | typedef struct tpl_root_data { 159 | int flags; 160 | tpl_pidx *pidx; 161 | tpl_mmap_rec mmap; 162 | char *fmt; 163 | int *fxlens, num_fxlens; 164 | } tpl_root_data; 165 | 166 | /* node type to size mapping */ 167 | struct tpl_type_t { 168 | char c; 169 | int sz; 170 | }; 171 | 172 | 173 | /* Internal prototypes */ 174 | static tpl_node *tpl_node_new(tpl_node *parent); 175 | static tpl_node *tpl_find_i(tpl_node *n, int i); 176 | static void *tpl_cpv(void *datav, void *data, size_t sz); 177 | static void *tpl_extend_backbone(tpl_node *n); 178 | static char *tpl_fmt(tpl_node *r); 179 | static void *tpl_dump_atyp(tpl_node *n, tpl_atyp* at, void *dv); 180 | static size_t tpl_ser_osz(tpl_node *n); 181 | static void tpl_free_atyp(tpl_node *n,tpl_atyp *atyp); 182 | static int tpl_dump_to_mem(tpl_node *r, void *addr, size_t sz); 183 | static int tpl_mmap_file(char *filename, tpl_mmap_rec *map_rec); 184 | static int tpl_mmap_output_file(char *filename, size_t sz, void **text_out); 185 | static int tpl_cpu_bigendian(void); 186 | static int tpl_needs_endian_swap(void *); 187 | static void tpl_byteswap(void *word, int len); 188 | static void tpl_fatal(char *fmt, ...); 189 | static int tpl_serlen(tpl_node *r, tpl_node *n, void *dv, size_t *serlen); 190 | static int tpl_unpackA0(tpl_node *r); 191 | static int tpl_oops(const char *fmt, ...); 192 | static int tpl_gather_mem( char *buf, size_t len, tpl_gather_t **gs, tpl_gather_cb *cb, void *data); 193 | static int tpl_gather_nonblocking( int fd, tpl_gather_t **gs, tpl_gather_cb *cb, void *data); 194 | static int tpl_gather_blocking(int fd, void **img, size_t *sz); 195 | static tpl_node *tpl_map_va(char *fmt, va_list ap); 196 | 197 | /* This is used internally to help calculate padding when a 'double' 198 | * follows a smaller datatype in a structure. Normally under gcc 199 | * on x86, d will be aligned at +4, however use of -malign-double 200 | * causes d to be aligned at +8 (this is actually faster on x86). 201 | * Also SPARC and x86_64 seem to align always on +8. 202 | */ 203 | struct tpl_double_alignment_detector { 204 | char a; 205 | double d; /* some platforms align this on +4, others on +8 */ 206 | }; 207 | 208 | /* this is another case where alignment varies. mac os x/gcc was observed 209 | * to align the int64_t at +4 under -m32 and at +8 under -m64 */ 210 | struct tpl_int64_alignment_detector { 211 | int i; 212 | int64_t j; /* some platforms align this on +4, others on +8 */ 213 | }; 214 | 215 | typedef struct { 216 | size_t inter_elt_len; /* padded inter-element len; i.e. &a[1].field - &a[0].field */ 217 | tpl_node *iter_start_node; /* node to jump back to, as we start each new iteration */ 218 | size_t iternum; /* current iteration number (total req'd. iter's in n->num) */ 219 | } tpl_pound_data; 220 | 221 | /* Hooks for customizing tpl mem alloc, error handling, etc. Set defaults. */ 222 | tpl_hook_t tpl_hook = { 223 | /* .oops = */ tpl_oops, 224 | /* .malloc = */ malloc, 225 | /* .realloc = */ realloc, 226 | /* .free = */ free, 227 | /* .fatal = */ tpl_fatal, 228 | /* .gather_max = */ 0 /* max tpl size (bytes) for tpl_gather */ 229 | }; 230 | 231 | static const char tpl_fmt_chars[] = "AS($)BiucsfIUjv#"; /* valid format chars */ 232 | static const char tpl_S_fmt_chars[] = "iucsfIUjv#$()"; /* valid within S(...) */ 233 | static const char tpl_datapeek_ok_chars[] = "iucsfIUjv"; /* valid in datapeek */ 234 | static const struct tpl_type_t tpl_types[] = { 235 | /* [TPL_TYPE_ROOT] = */ {'r', 0}, 236 | /* [TPL_TYPE_INT32] = */ {'i', sizeof(int32_t)}, 237 | /* [TPL_TYPE_UINT32] = */ {'u', sizeof(uint32_t)}, 238 | /* [TPL_TYPE_BYTE] = */ {'c', sizeof(char)}, 239 | /* [TPL_TYPE_STR] = */ {'s', sizeof(char*)}, 240 | /* [TPL_TYPE_ARY] = */ {'A', 0}, 241 | /* [TPL_TYPE_BIN] = */ {'B', 0}, 242 | /* [TPL_TYPE_DOUBLE] = */ {'f', 8}, /* not sizeof(double) as that varies */ 243 | /* [TPL_TYPE_INT64] = */ {'I', sizeof(int64_t)}, 244 | /* [TPL_TYPE_UINT64] = */ {'U', sizeof(uint64_t)}, 245 | /* [TPL_TYPE_INT16] = */ {'j', sizeof(int16_t)}, 246 | /* [TPL_TYPE_UINT16] = */ {'v', sizeof(uint16_t)}, 247 | /* [TPL_TYPE_POUND] = */ {'#', 0}, 248 | }; 249 | 250 | /* default error-reporting function. Just writes to stderr. */ 251 | static int tpl_oops(const char *fmt, ...) { 252 | va_list ap; 253 | va_start(ap,fmt); 254 | vfprintf(stderr,fmt,ap); 255 | va_end(ap); 256 | return 0; 257 | } 258 | 259 | 260 | static tpl_node *tpl_node_new(tpl_node *parent) { 261 | tpl_node *n; 262 | if ((n=tpl_hook.malloc(sizeof(tpl_node))) == NULL) { 263 | fatal_oom(); 264 | } 265 | n->addr=NULL; 266 | n->data=NULL; 267 | n->num=1; 268 | n->ser_osz=0; 269 | n->children=NULL; 270 | n->next=NULL; 271 | n->parent=parent; 272 | return n; 273 | } 274 | 275 | /* Used in S(..) formats to pack several fields from a structure based on 276 | * only the structure address. We need to calculate field addresses 277 | * manually taking into account the size of the fields and intervening padding. 278 | * The wrinkle is that double is not normally aligned on x86-32 but the 279 | * -malign-double compiler option causes it to be. Double are aligned 280 | * on Sparc, and apparently on 64 bit x86. We use a helper structure 281 | * to detect whether double is aligned in this compilation environment. 282 | */ 283 | char *calc_field_addr(tpl_node *parent, int type,char *struct_addr, int ordinal) { 284 | tpl_node *prev; 285 | int offset; 286 | int align_sz; 287 | 288 | if (ordinal == 1) return struct_addr; /* first field starts on structure address */ 289 | 290 | /* generate enough padding so field addr is divisible by it's align_sz. 4, 8, etc */ 291 | prev = parent->children->prev; 292 | switch(type) { 293 | case TPL_TYPE_DOUBLE: 294 | align_sz = sizeof(struct tpl_double_alignment_detector) > 12 ? 8 : 4; 295 | break; 296 | case TPL_TYPE_INT64: 297 | case TPL_TYPE_UINT64: 298 | align_sz = sizeof(struct tpl_int64_alignment_detector) > 12 ? 8 : 4; 299 | break; 300 | default: 301 | align_sz = tpl_types[type].sz; 302 | break; 303 | } 304 | offset = ((uintptr_t)prev->addr - (uintptr_t)struct_addr) 305 | + (tpl_types[prev->type].sz * prev->num); 306 | offset = (offset + align_sz - 1) / align_sz * align_sz; 307 | return struct_addr + offset; 308 | } 309 | 310 | TPL_API tpl_node *tpl_map(char *fmt,...) { 311 | va_list ap; 312 | tpl_node *tn; 313 | 314 | va_start(ap,fmt); 315 | tn = tpl_map_va(fmt, ap); 316 | va_end(ap); 317 | return tn; 318 | } 319 | 320 | static tpl_node *tpl_map_va(char *fmt, va_list ap) { 321 | int lparen_level=0,expect_lparen=0,t=0,in_structure=0,ordinal=0; 322 | int in_nested_structure=0; 323 | char *c, *peek, *struct_addr=NULL, *struct_next; 324 | tpl_node *root,*parent,*n=NULL,*preceding,*iter_start_node=NULL, 325 | *struct_widest_node=NULL, *np; tpl_pidx *pidx; 326 | tpl_pound_data *pd; 327 | int *fxlens, num_fxlens, pound_num, pound_prod, applies_to_struct; 328 | int contig_fxlens[10]; /* temp space for contiguous fxlens */ 329 | int num_contig_fxlens, i, j; 330 | ptrdiff_t inter_elt_len=0; /* padded element length of contiguous structs in array */ 331 | 332 | 333 | root = tpl_node_new(NULL); 334 | root->type = TPL_TYPE_ROOT; 335 | root->data = (tpl_root_data*)tpl_hook.malloc(sizeof(tpl_root_data)); 336 | if (!root->data) fatal_oom(); 337 | memset((tpl_root_data*)root->data,0,sizeof(tpl_root_data)); 338 | 339 | /* set up root nodes special ser_osz to reflect overhead of preamble */ 340 | root->ser_osz = sizeof(uint32_t); /* tpl leading length */ 341 | root->ser_osz += strlen(fmt) + 1; /* fmt + NUL-terminator */ 342 | root->ser_osz += 4; /* 'tpl' magic prefix + flags byte */ 343 | 344 | parent=root; 345 | 346 | c=fmt; 347 | while (*c != '\0') { 348 | switch (*c) { 349 | case 'c': 350 | case 'i': 351 | case 'u': 352 | case 'j': 353 | case 'v': 354 | case 'I': 355 | case 'U': 356 | case 'f': 357 | if (*c=='c') t=TPL_TYPE_BYTE; 358 | else if (*c=='i') t=TPL_TYPE_INT32; 359 | else if (*c=='u') t=TPL_TYPE_UINT32; 360 | else if (*c=='j') t=TPL_TYPE_INT16; 361 | else if (*c=='v') t=TPL_TYPE_UINT16; 362 | else if (*c=='I') t=TPL_TYPE_INT64; 363 | else if (*c=='U') t=TPL_TYPE_UINT64; 364 | else if (*c=='f') t=TPL_TYPE_DOUBLE; 365 | 366 | if (expect_lparen) goto fail; 367 | n = tpl_node_new(parent); 368 | n->type = t; 369 | if (in_structure) { 370 | if (ordinal == 1) { 371 | /* for S(...)# iteration. Apply any changes to case 's' too!!! */ 372 | iter_start_node = n; 373 | struct_widest_node = n; 374 | } 375 | if (tpl_types[n->type].sz > tpl_types[struct_widest_node->type].sz) { 376 | struct_widest_node = n; 377 | } 378 | n->addr = calc_field_addr(parent,n->type,struct_addr,ordinal++); 379 | } else n->addr = (void*)va_arg(ap,void*); 380 | n->data = tpl_hook.malloc(tpl_types[t].sz); 381 | if (!n->data) fatal_oom(); 382 | if (n->parent->type == TPL_TYPE_ARY) 383 | ((tpl_atyp*)(n->parent->data))->sz += tpl_types[t].sz; 384 | DL_ADD(parent->children,n); 385 | break; 386 | case 's': 387 | if (expect_lparen) goto fail; 388 | n = tpl_node_new(parent); 389 | n->type = TPL_TYPE_STR; 390 | if (in_structure) { 391 | if (ordinal == 1) { 392 | iter_start_node = n; /* for S(...)# iteration */ 393 | struct_widest_node = n; 394 | } 395 | if (tpl_types[n->type].sz > tpl_types[struct_widest_node->type].sz) { 396 | struct_widest_node = n; 397 | } 398 | n->addr = calc_field_addr(parent,n->type,struct_addr,ordinal++); 399 | } else n->addr = (void*)va_arg(ap,void*); 400 | n->data = tpl_hook.malloc(sizeof(char*)); 401 | if (!n->data) fatal_oom(); 402 | *(char**)(n->data) = NULL; 403 | if (n->parent->type == TPL_TYPE_ARY) 404 | ((tpl_atyp*)(n->parent->data))->sz += sizeof(void*); 405 | DL_ADD(parent->children,n); 406 | break; 407 | case '#': 408 | /* apply a 'num' to preceding atom */ 409 | if (!parent->children) goto fail; 410 | preceding = parent->children->prev; /* first child's prev is 'last child'*/ 411 | t = preceding->type; 412 | applies_to_struct = (*(c-1) == ')') ? 1 : 0; 413 | if (!applies_to_struct) { 414 | if (!(t == TPL_TYPE_BYTE || t == TPL_TYPE_INT32 || 415 | t == TPL_TYPE_UINT32 || t == TPL_TYPE_DOUBLE || 416 | t == TPL_TYPE_UINT64 || t == TPL_TYPE_INT64 || 417 | t == TPL_TYPE_UINT16 || t == TPL_TYPE_INT16 || 418 | t == TPL_TYPE_STR )) goto fail; 419 | } 420 | /* count up how many contiguous # and form their product */ 421 | pound_prod=1; 422 | num_contig_fxlens=0; 423 | for(peek=c; *peek == '#'; peek++) { 424 | pound_num = va_arg(ap, int); 425 | if (pound_num < 1) { 426 | tpl_hook.fatal("non-positive iteration count %d\n", pound_num); 427 | } 428 | if (num_contig_fxlens >= (sizeof(contig_fxlens)/sizeof(contig_fxlens[0]))) { 429 | tpl_hook.fatal("contiguous # exceeds hardcoded limit\n"); 430 | } 431 | contig_fxlens[num_contig_fxlens++] = pound_num; 432 | pound_prod *= pound_num; 433 | } 434 | /* increment c to skip contiguous # so its points to last one */ 435 | c = peek-1; 436 | /* differentiate atom-# from struct-# by noting preceding rparen */ 437 | if (applies_to_struct) { /* insert # node to induce looping */ 438 | n = tpl_node_new(parent); 439 | n->type = TPL_TYPE_POUND; 440 | n->num = pound_prod; 441 | n->data = tpl_hook.malloc(sizeof(tpl_pound_data)); 442 | if (!n->data) fatal_oom(); 443 | pd = (tpl_pound_data*)n->data; 444 | pd->inter_elt_len = inter_elt_len; 445 | pd->iter_start_node = iter_start_node; 446 | pd->iternum = 0; 447 | DL_ADD(parent->children,n); 448 | /* multiply the 'num' and data space on each atom in the structure */ 449 | for(np = iter_start_node; np != n; np = np->next) { 450 | if (n->parent->type == TPL_TYPE_ARY) { 451 | ((tpl_atyp*)(n->parent->data))->sz += 452 | tpl_types[np->type].sz * (np->num * (n->num - 1)); 453 | } 454 | np->data = tpl_hook.realloc(np->data, tpl_types[np->type].sz * 455 | np->num * n->num); 456 | if (!np->data) fatal_oom(); 457 | memset(np->data, 0, tpl_types[np->type].sz * np->num * n->num); 458 | } 459 | } else { /* simple atom-# form does not require a loop */ 460 | preceding->num = pound_prod; 461 | preceding->data = tpl_hook.realloc(preceding->data, 462 | tpl_types[t].sz * preceding->num); 463 | if (!preceding->data) fatal_oom(); 464 | memset(preceding->data,0,tpl_types[t].sz * preceding->num); 465 | if (n->parent->type == TPL_TYPE_ARY) { 466 | ((tpl_atyp*)(n->parent->data))->sz += tpl_types[t].sz * 467 | (preceding->num-1); 468 | } 469 | } 470 | root->ser_osz += (sizeof(uint32_t) * num_contig_fxlens); 471 | 472 | j = ((tpl_root_data*)root->data)->num_fxlens; /* before incrementing */ 473 | (((tpl_root_data*)root->data)->num_fxlens) += num_contig_fxlens; 474 | num_fxlens = ((tpl_root_data*)root->data)->num_fxlens; /* new value */ 475 | fxlens = ((tpl_root_data*)root->data)->fxlens; 476 | fxlens = tpl_hook.realloc(fxlens, sizeof(int) * num_fxlens); 477 | if (!fxlens) fatal_oom(); 478 | ((tpl_root_data*)root->data)->fxlens = fxlens; 479 | for(i=0; i < num_contig_fxlens; i++) fxlens[j++] = contig_fxlens[i]; 480 | 481 | break; 482 | case 'B': 483 | if (expect_lparen) goto fail; 484 | if (in_structure) goto fail; 485 | n = tpl_node_new(parent); 486 | n->type = TPL_TYPE_BIN; 487 | n->addr = (tpl_bin*)va_arg(ap,void*); 488 | n->data = tpl_hook.malloc(sizeof(tpl_bin*)); 489 | if (!n->data) fatal_oom(); 490 | *((tpl_bin**)n->data) = NULL; 491 | if (n->parent->type == TPL_TYPE_ARY) 492 | ((tpl_atyp*)(n->parent->data))->sz += sizeof(tpl_bin); 493 | DL_ADD(parent->children,n); 494 | break; 495 | case 'A': 496 | if (in_structure) goto fail; 497 | n = tpl_node_new(parent); 498 | n->type = TPL_TYPE_ARY; 499 | DL_ADD(parent->children,n); 500 | parent = n; 501 | expect_lparen=1; 502 | pidx = (tpl_pidx*)tpl_hook.malloc(sizeof(tpl_pidx)); 503 | if (!pidx) fatal_oom(); 504 | pidx->node = n; 505 | pidx->next = NULL; 506 | DL_ADD(((tpl_root_data*)(root->data))->pidx,pidx); 507 | /* set up the A's tpl_atyp */ 508 | n->data = (tpl_atyp*)tpl_hook.malloc(sizeof(tpl_atyp)); 509 | if (!n->data) fatal_oom(); 510 | ((tpl_atyp*)(n->data))->num = 0; 511 | ((tpl_atyp*)(n->data))->sz = 0; 512 | ((tpl_atyp*)(n->data))->bb = NULL; 513 | ((tpl_atyp*)(n->data))->bbtail = NULL; 514 | ((tpl_atyp*)(n->data))->cur = NULL; 515 | if (n->parent->type == TPL_TYPE_ARY) 516 | ((tpl_atyp*)(n->parent->data))->sz += sizeof(void*); 517 | break; 518 | case 'S': 519 | if (in_structure) goto fail; 520 | expect_lparen=1; 521 | ordinal=1; /* index upcoming atoms in S(..) */ 522 | in_structure=1+lparen_level; /* so we can tell where S fmt ends */ 523 | struct_addr = (char*)va_arg(ap,void*); 524 | break; 525 | case '$': /* nested structure */ 526 | if (!in_structure) goto fail; 527 | expect_lparen=1; 528 | in_nested_structure++; 529 | break; 530 | case ')': 531 | lparen_level--; 532 | if (lparen_level < 0) goto fail; 533 | if (*(c-1) == '(') goto fail; 534 | if (in_nested_structure) in_nested_structure--; 535 | else if (in_structure && (in_structure-1 == lparen_level)) { 536 | /* calculate delta between contiguous structures in array */ 537 | struct_next = calc_field_addr(parent, struct_widest_node->type, 538 | struct_addr, ordinal++); 539 | inter_elt_len = struct_next - struct_addr; 540 | in_structure=0; 541 | } 542 | else parent = parent->parent; /* rparen ends A() type, not S() type */ 543 | break; 544 | case '(': 545 | if (!expect_lparen) goto fail; 546 | expect_lparen=0; 547 | lparen_level++; 548 | break; 549 | default: 550 | tpl_hook.oops("unsupported option %c\n", *c); 551 | goto fail; 552 | } 553 | c++; 554 | } 555 | if (lparen_level != 0) goto fail; 556 | 557 | /* copy the format string, save for convenience */ 558 | ((tpl_root_data*)(root->data))->fmt = tpl_hook.malloc(strlen(fmt)+1); 559 | if (((tpl_root_data*)(root->data))->fmt == NULL) 560 | fatal_oom(); 561 | memcpy(((tpl_root_data*)(root->data))->fmt,fmt,strlen(fmt)+1); 562 | 563 | return root; 564 | 565 | fail: 566 | tpl_hook.oops("failed to parse %s\n", fmt); 567 | tpl_free(root); 568 | return NULL; 569 | } 570 | 571 | static int tpl_unmap_file( tpl_mmap_rec *mr) { 572 | 573 | if ( munmap( mr->text, mr->text_sz ) == -1 ) { 574 | tpl_hook.oops("Failed to munmap: %s\n", strerror(errno)); 575 | } 576 | close(mr->fd); 577 | mr->text = NULL; 578 | mr->text_sz = 0; 579 | return 0; 580 | } 581 | 582 | static void tpl_free_keep_map(tpl_node *r) { 583 | int mmap_bits = (TPL_RDONLY|TPL_FILE); 584 | int ufree_bits = (TPL_MEM|TPL_UFREE); 585 | tpl_node *nxtc,*c; 586 | int find_next_node=0,looking,i; 587 | size_t sz; 588 | 589 | /* For mmap'd files, or for 'ufree' memory images , do appropriate release */ 590 | if ((((tpl_root_data*)(r->data))->flags & mmap_bits) == mmap_bits) { 591 | tpl_unmap_file( &((tpl_root_data*)(r->data))->mmap); 592 | } else if ((((tpl_root_data*)(r->data))->flags & ufree_bits) == ufree_bits) { 593 | tpl_hook.free( ((tpl_root_data*)(r->data))->mmap.text ); 594 | } 595 | 596 | c = r->children; 597 | if (c) { 598 | while(c->type != TPL_TYPE_ROOT) { /* loop until we come back to root node */ 599 | switch (c->type) { 600 | case TPL_TYPE_BIN: 601 | /* free any binary buffer hanging from tpl_bin */ 602 | if ( *((tpl_bin**)(c->data)) ) { 603 | if ( (*((tpl_bin**)(c->data)))->addr ) { 604 | tpl_hook.free( (*((tpl_bin**)(c->data)))->addr ); 605 | } 606 | *((tpl_bin**)c->data) = NULL; /* reset tpl_bin */ 607 | } 608 | find_next_node=1; 609 | break; 610 | case TPL_TYPE_STR: 611 | /* free any packed (copied) string */ 612 | for(i=0; i < c->num; i++) { 613 | char *str = ((char**)c->data)[i]; 614 | if (str) { 615 | tpl_hook.free(str); 616 | ((char**)c->data)[i] = NULL; 617 | } 618 | } 619 | find_next_node=1; 620 | break; 621 | case TPL_TYPE_INT32: 622 | case TPL_TYPE_UINT32: 623 | case TPL_TYPE_INT64: 624 | case TPL_TYPE_UINT64: 625 | case TPL_TYPE_BYTE: 626 | case TPL_TYPE_DOUBLE: 627 | case TPL_TYPE_INT16: 628 | case TPL_TYPE_UINT16: 629 | case TPL_TYPE_POUND: 630 | find_next_node=1; 631 | break; 632 | case TPL_TYPE_ARY: 633 | c->ser_osz = 0; /* zero out the serialization output size */ 634 | 635 | sz = ((tpl_atyp*)(c->data))->sz; /* save sz to use below */ 636 | tpl_free_atyp(c,c->data); 637 | 638 | /* make new atyp */ 639 | c->data = (tpl_atyp*)tpl_hook.malloc(sizeof(tpl_atyp)); 640 | if (!c->data) fatal_oom(); 641 | ((tpl_atyp*)(c->data))->num = 0; 642 | ((tpl_atyp*)(c->data))->sz = sz; /* restore bb datum sz */ 643 | ((tpl_atyp*)(c->data))->bb = NULL; 644 | ((tpl_atyp*)(c->data))->bbtail = NULL; 645 | ((tpl_atyp*)(c->data))->cur = NULL; 646 | 647 | c = c->children; 648 | break; 649 | default: 650 | tpl_hook.fatal("unsupported format character\n"); 651 | break; 652 | } 653 | 654 | if (find_next_node) { 655 | find_next_node=0; 656 | looking=1; 657 | while(looking) { 658 | if (c->next) { 659 | nxtc=c->next; 660 | c=nxtc; 661 | looking=0; 662 | } else { 663 | if (c->type == TPL_TYPE_ROOT) break; /* root node */ 664 | else { 665 | nxtc=c->parent; 666 | c=nxtc; 667 | } 668 | } 669 | } 670 | } 671 | } 672 | } 673 | 674 | ((tpl_root_data*)(r->data))->flags = 0; /* reset flags */ 675 | } 676 | 677 | TPL_API void tpl_free(tpl_node *r) { 678 | int mmap_bits = (TPL_RDONLY|TPL_FILE); 679 | int ufree_bits = (TPL_MEM|TPL_UFREE); 680 | tpl_node *nxtc,*c; 681 | int find_next_node=0,looking,i; 682 | tpl_pidx *pidx,*pidx_nxt; 683 | 684 | /* For mmap'd files, or for 'ufree' memory images , do appropriate release */ 685 | if ((((tpl_root_data*)(r->data))->flags & mmap_bits) == mmap_bits) { 686 | tpl_unmap_file( &((tpl_root_data*)(r->data))->mmap); 687 | } else if ((((tpl_root_data*)(r->data))->flags & ufree_bits) == ufree_bits) { 688 | tpl_hook.free( ((tpl_root_data*)(r->data))->mmap.text ); 689 | } 690 | 691 | c = r->children; 692 | if (c) { 693 | while(c->type != TPL_TYPE_ROOT) { /* loop until we come back to root node */ 694 | switch (c->type) { 695 | case TPL_TYPE_BIN: 696 | /* free any binary buffer hanging from tpl_bin */ 697 | if ( *((tpl_bin**)(c->data)) ) { 698 | if ( (*((tpl_bin**)(c->data)))->sz != 0 ) { 699 | tpl_hook.free( (*((tpl_bin**)(c->data)))->addr ); 700 | } 701 | tpl_hook.free(*((tpl_bin**)c->data)); /* free tpl_bin */ 702 | } 703 | tpl_hook.free(c->data); /* free tpl_bin* */ 704 | find_next_node=1; 705 | break; 706 | case TPL_TYPE_STR: 707 | /* free any packed (copied) string */ 708 | for(i=0; i < c->num; i++) { 709 | char *str = ((char**)c->data)[i]; 710 | if (str) { 711 | tpl_hook.free(str); 712 | ((char**)c->data)[i] = NULL; 713 | } 714 | } 715 | tpl_hook.free(c->data); 716 | find_next_node=1; 717 | break; 718 | case TPL_TYPE_INT32: 719 | case TPL_TYPE_UINT32: 720 | case TPL_TYPE_INT64: 721 | case TPL_TYPE_UINT64: 722 | case TPL_TYPE_BYTE: 723 | case TPL_TYPE_DOUBLE: 724 | case TPL_TYPE_INT16: 725 | case TPL_TYPE_UINT16: 726 | case TPL_TYPE_POUND: 727 | tpl_hook.free(c->data); 728 | find_next_node=1; 729 | break; 730 | case TPL_TYPE_ARY: 731 | tpl_free_atyp(c,c->data); 732 | if (c->children) c = c->children; /* normal case */ 733 | else find_next_node=1; /* edge case, handle bad format A() */ 734 | break; 735 | default: 736 | tpl_hook.fatal("unsupported format character\n"); 737 | break; 738 | } 739 | 740 | if (find_next_node) { 741 | find_next_node=0; 742 | looking=1; 743 | while(looking) { 744 | if (c->next) { 745 | nxtc=c->next; 746 | tpl_hook.free(c); 747 | c=nxtc; 748 | looking=0; 749 | } else { 750 | if (c->type == TPL_TYPE_ROOT) break; /* root node */ 751 | else { 752 | nxtc=c->parent; 753 | tpl_hook.free(c); 754 | c=nxtc; 755 | } 756 | } 757 | } 758 | } 759 | } 760 | } 761 | 762 | /* free root */ 763 | for(pidx=((tpl_root_data*)(r->data))->pidx; pidx; pidx=pidx_nxt) { 764 | pidx_nxt = pidx->next; 765 | tpl_hook.free(pidx); 766 | } 767 | tpl_hook.free(((tpl_root_data*)(r->data))->fmt); 768 | if (((tpl_root_data*)(r->data))->num_fxlens > 0) { 769 | tpl_hook.free(((tpl_root_data*)(r->data))->fxlens); 770 | } 771 | tpl_hook.free(r->data); /* tpl_root_data */ 772 | tpl_hook.free(r); 773 | } 774 | 775 | 776 | /* Find the i'th packable ('A' node) */ 777 | static tpl_node *tpl_find_i(tpl_node *n, int i) { 778 | int j=0; 779 | tpl_pidx *pidx; 780 | if (n->type != TPL_TYPE_ROOT) return NULL; 781 | if (i == 0) return n; /* packable 0 is root */ 782 | for(pidx=((tpl_root_data*)(n->data))->pidx; pidx; pidx=pidx->next) { 783 | if (++j == i) return pidx->node; 784 | } 785 | return NULL; 786 | } 787 | 788 | static void *tpl_cpv(void *datav, void *data, size_t sz) { 789 | if (sz>0) memcpy(datav,data,sz); 790 | return (void*)((uintptr_t)datav + sz); 791 | } 792 | 793 | static void *tpl_extend_backbone(tpl_node *n) { 794 | tpl_backbone *bb; 795 | bb = (tpl_backbone*)tpl_hook.malloc(sizeof(tpl_backbone) + 796 | ((tpl_atyp*)(n->data))->sz ); /* datum hangs on coattails of bb */ 797 | if (!bb) fatal_oom(); 798 | #if __STDC_VERSION__ < 199901 799 | bb->data = (char*)((uintptr_t)bb + sizeof(tpl_backbone)); 800 | #endif 801 | memset(bb->data,0,((tpl_atyp*)(n->data))->sz); 802 | bb->next = NULL; 803 | /* Add the new backbone to the tail, also setting head if necessary */ 804 | if (((tpl_atyp*)(n->data))->bb == NULL) { 805 | ((tpl_atyp*)(n->data))->bb = bb; 806 | ((tpl_atyp*)(n->data))->bbtail = bb; 807 | } else { 808 | ((tpl_atyp*)(n->data))->bbtail->next = bb; 809 | ((tpl_atyp*)(n->data))->bbtail = bb; 810 | } 811 | 812 | ((tpl_atyp*)(n->data))->num++; 813 | return bb->data; 814 | } 815 | 816 | /* Get the format string corresponding to a given tpl (root node) */ 817 | static char *tpl_fmt(tpl_node *r) { 818 | return ((tpl_root_data*)(r->data))->fmt; 819 | } 820 | 821 | /* Get the fmt # lengths as a contiguous buffer of ints (length num_fxlens) */ 822 | static int *tpl_fxlens(tpl_node *r, int *num_fxlens) { 823 | *num_fxlens = ((tpl_root_data*)(r->data))->num_fxlens; 824 | return ((tpl_root_data*)(r->data))->fxlens; 825 | } 826 | 827 | /* called when serializing an 'A' type node into a buffer which has 828 | * already been set up with the proper space. The backbone is walked 829 | * which was obtained from the tpl_atyp header passed in. 830 | */ 831 | static void *tpl_dump_atyp(tpl_node *n, tpl_atyp* at, void *dv) { 832 | tpl_backbone *bb; 833 | tpl_node *c; 834 | void *datav; 835 | uint32_t slen; 836 | tpl_bin *binp; 837 | char *strp; 838 | tpl_atyp *atypp; 839 | tpl_pound_data *pd; 840 | int i; 841 | size_t itermax; 842 | 843 | /* handle 'A' nodes */ 844 | dv = tpl_cpv(dv,&at->num,sizeof(uint32_t)); /* array len */ 845 | for(bb=at->bb; bb; bb=bb->next) { 846 | datav = bb->data; 847 | c=n->children; 848 | while(c) { 849 | switch (c->type) { 850 | case TPL_TYPE_BYTE: 851 | case TPL_TYPE_DOUBLE: 852 | case TPL_TYPE_INT32: 853 | case TPL_TYPE_UINT32: 854 | case TPL_TYPE_INT64: 855 | case TPL_TYPE_UINT64: 856 | case TPL_TYPE_INT16: 857 | case TPL_TYPE_UINT16: 858 | dv = tpl_cpv(dv,datav,tpl_types[c->type].sz * c->num); 859 | datav = (void*)((uintptr_t)datav + tpl_types[c->type].sz * c->num); 860 | break; 861 | case TPL_TYPE_BIN: 862 | /* dump the buffer length followed by the buffer */ 863 | memcpy(&binp,datav,sizeof(tpl_bin*)); /* cp to aligned */ 864 | slen = binp->sz; 865 | dv = tpl_cpv(dv,&slen,sizeof(uint32_t)); 866 | dv = tpl_cpv(dv,binp->addr,slen); 867 | datav = (void*)((uintptr_t)datav + sizeof(tpl_bin*)); 868 | break; 869 | case TPL_TYPE_STR: 870 | /* dump the string length followed by the string */ 871 | for(i=0; i < c->num; i++) { 872 | memcpy(&strp,datav,sizeof(char*)); /* cp to aligned */ 873 | slen = strp ? (strlen(strp)+1) : 0; 874 | dv = tpl_cpv(dv,&slen,sizeof(uint32_t)); 875 | if (slen > 1) dv = tpl_cpv(dv,strp,slen-1); 876 | datav = (void*)((uintptr_t)datav + sizeof(char*)); 877 | } 878 | break; 879 | case TPL_TYPE_ARY: 880 | memcpy(&atypp,datav,sizeof(tpl_atyp*)); /* cp to aligned */ 881 | dv = tpl_dump_atyp(c,atypp,dv); 882 | datav = (void*)((uintptr_t)datav + sizeof(void*)); 883 | break; 884 | case TPL_TYPE_POUND: 885 | /* iterate over the preceding nodes */ 886 | pd = (tpl_pound_data*)c->data; 887 | itermax = c->num; 888 | if (++(pd->iternum) < itermax) { 889 | c = pd->iter_start_node; 890 | continue; 891 | } else { /* loop complete. */ 892 | pd->iternum = 0; 893 | } 894 | break; 895 | default: 896 | tpl_hook.fatal("unsupported format character\n"); 897 | break; 898 | } 899 | c=c->next; 900 | } 901 | } 902 | return dv; 903 | } 904 | 905 | /* figure the serialization output size needed for tpl whose root is n*/ 906 | static size_t tpl_ser_osz(tpl_node *n) { 907 | tpl_node *c, *np; 908 | size_t sz, itermax; 909 | tpl_bin *binp; 910 | char *strp; 911 | tpl_pound_data *pd; 912 | int i; 913 | 914 | /* handle the root node ONLY (subtree's ser_osz have been bubbled-up) */ 915 | if (n->type != TPL_TYPE_ROOT) { 916 | tpl_hook.fatal("internal error: tpl_ser_osz on non-root node\n"); 917 | } 918 | 919 | sz = n->ser_osz; /* start with fixed overhead, already stored */ 920 | c=n->children; 921 | while (c) { 922 | switch (c->type) { 923 | case TPL_TYPE_BYTE: 924 | case TPL_TYPE_DOUBLE: 925 | case TPL_TYPE_INT32: 926 | case TPL_TYPE_UINT32: 927 | case TPL_TYPE_INT64: 928 | case TPL_TYPE_UINT64: 929 | case TPL_TYPE_INT16: 930 | case TPL_TYPE_UINT16: 931 | sz += tpl_types[c->type].sz * c->num; 932 | break; 933 | case TPL_TYPE_BIN: 934 | sz += sizeof(uint32_t); /* binary buf len */ 935 | memcpy(&binp,c->data,sizeof(tpl_bin*)); /* cp to aligned */ 936 | sz += binp->sz; 937 | break; 938 | case TPL_TYPE_STR: 939 | for(i=0; i < c->num; i++) { 940 | sz += sizeof(uint32_t); /* string len */ 941 | memcpy(&strp,&((char**)c->data)[i],sizeof(char*)); /* cp to aligned */ 942 | sz += strp ? strlen(strp) : 0; 943 | } 944 | break; 945 | case TPL_TYPE_ARY: 946 | sz += sizeof(uint32_t); /* array len */ 947 | sz += c->ser_osz; /* bubbled-up child array ser_osz */ 948 | break; 949 | case TPL_TYPE_POUND: 950 | /* iterate over the preceding nodes */ 951 | itermax = c->num; 952 | pd = (tpl_pound_data*)c->data; 953 | if (++(pd->iternum) < itermax) { 954 | for(np=pd->iter_start_node; np != c; np = np->next) { 955 | np->data = (char*)(np->data) + 956 | (tpl_types[np->type].sz * np->num); 957 | } 958 | c = pd->iter_start_node; 959 | continue; 960 | } else { /* loop complete. */ 961 | pd->iternum = 0; 962 | for(np=pd->iter_start_node; np != c; np = np->next) { 963 | np->data = (char*)(np->data) - ((itermax-1) * 964 | tpl_types[np->type].sz * 965 | np->num); 966 | } 967 | } 968 | break; 969 | default: 970 | tpl_hook.fatal("unsupported format character\n"); 971 | break; 972 | } 973 | c=c->next; 974 | } 975 | return sz; 976 | } 977 | 978 | 979 | TPL_API int tpl_dump(tpl_node *r, int mode, ...) { 980 | va_list ap; 981 | char *filename, *bufv; 982 | void **addr_out,*buf, *pa_addr; 983 | int fd,rc=0; 984 | size_t sz,*sz_out, pa_sz; 985 | 986 | if (((tpl_root_data*)(r->data))->flags & TPL_RDONLY) { /* unusual */ 987 | tpl_hook.oops("error: tpl_dump called for a loaded tpl\n"); 988 | return -1; 989 | } 990 | 991 | sz = tpl_ser_osz(r); /* compute the size needed to serialize */ 992 | 993 | va_start(ap,mode); 994 | if (mode & TPL_FILE) { 995 | filename = va_arg(ap,char*); 996 | fd = tpl_mmap_output_file(filename, sz, &buf); 997 | if (fd == -1) rc = -1; 998 | else { 999 | rc = tpl_dump_to_mem(r,buf,sz); 1000 | if (msync(buf,sz,MS_SYNC) == -1) { 1001 | tpl_hook.oops("msync failed on fd %d: %s\n", fd, strerror(errno)); 1002 | } 1003 | if (munmap(buf, sz) == -1) { 1004 | tpl_hook.oops("munmap failed on fd %d: %s\n", fd, strerror(errno)); 1005 | } 1006 | close(fd); 1007 | } 1008 | } else if (mode & TPL_FD) { 1009 | fd = va_arg(ap, int); 1010 | if ( (buf = tpl_hook.malloc(sz)) == NULL) fatal_oom(); 1011 | tpl_dump_to_mem(r,buf,sz); 1012 | bufv = buf; 1013 | do { 1014 | rc = write(fd,bufv,sz); 1015 | if (rc > 0) { 1016 | sz -= rc; 1017 | bufv += rc; 1018 | } else if (rc == -1) { 1019 | if (errno == EINTR || errno == EAGAIN) continue; 1020 | tpl_hook.oops("error writing to fd %d: %s\n", fd, strerror(errno)); 1021 | free(buf); 1022 | return -1; 1023 | } 1024 | } while (sz > 0); 1025 | free(buf); 1026 | rc = 0; 1027 | } else if (mode & TPL_MEM) { 1028 | if (mode & TPL_PREALLOCD) { /* caller allocated */ 1029 | pa_addr = (void*)va_arg(ap, void*); 1030 | pa_sz = va_arg(ap, size_t); 1031 | if (pa_sz < sz) { 1032 | tpl_hook.oops("tpl_dump: buffer too small, need %d bytes\n", sz); 1033 | return -1; 1034 | } 1035 | rc=tpl_dump_to_mem(r,pa_addr,sz); 1036 | } else { /* we allocate */ 1037 | addr_out = (void**)va_arg(ap, void*); 1038 | sz_out = va_arg(ap, size_t*); 1039 | if ( (buf = tpl_hook.malloc(sz)) == NULL) fatal_oom(); 1040 | *sz_out = sz; 1041 | *addr_out = buf; 1042 | rc=tpl_dump_to_mem(r,buf,sz); 1043 | } 1044 | } else if (mode & TPL_GETSIZE) { 1045 | sz_out = va_arg(ap, size_t*); 1046 | *sz_out = sz; 1047 | } else { 1048 | tpl_hook.oops("unsupported tpl_dump mode %d\n", mode); 1049 | rc=-1; 1050 | } 1051 | va_end(ap); 1052 | return rc; 1053 | } 1054 | 1055 | /* This function expects the caller to have set up a memory buffer of 1056 | * adequate size to hold the serialized tpl. The sz parameter must be 1057 | * the result of tpl_ser_osz(r). 1058 | */ 1059 | static int tpl_dump_to_mem(tpl_node *r,void *addr,size_t sz) { 1060 | uint32_t slen, sz32; 1061 | int *fxlens, num_fxlens, i; 1062 | void *dv; 1063 | char *fmt,flags; 1064 | tpl_node *c, *np; 1065 | tpl_pound_data *pd; 1066 | size_t itermax; 1067 | 1068 | fmt = tpl_fmt(r); 1069 | flags = 0; 1070 | if (tpl_cpu_bigendian()) flags |= TPL_FL_BIGENDIAN; 1071 | if (strchr(fmt,'s')) flags |= TPL_FL_NULLSTRINGS; 1072 | sz32 = sz; 1073 | 1074 | dv = addr; 1075 | dv = tpl_cpv(dv,TPL_MAGIC,3); /* copy tpl magic prefix */ 1076 | dv = tpl_cpv(dv,&flags,1); /* copy flags byte */ 1077 | dv = tpl_cpv(dv,&sz32,sizeof(uint32_t));/* overall length (inclusive) */ 1078 | dv = tpl_cpv(dv,fmt,strlen(fmt)+1); /* copy format with NUL-term */ 1079 | fxlens = tpl_fxlens(r,&num_fxlens); 1080 | dv = tpl_cpv(dv,fxlens,num_fxlens*sizeof(uint32_t));/* fmt # lengths */ 1081 | 1082 | /* serialize the tpl content, iterating over direct children of root */ 1083 | c = r->children; 1084 | while (c) { 1085 | switch (c->type) { 1086 | case TPL_TYPE_BYTE: 1087 | case TPL_TYPE_DOUBLE: 1088 | case TPL_TYPE_INT32: 1089 | case TPL_TYPE_UINT32: 1090 | case TPL_TYPE_INT64: 1091 | case TPL_TYPE_UINT64: 1092 | case TPL_TYPE_INT16: 1093 | case TPL_TYPE_UINT16: 1094 | dv = tpl_cpv(dv,c->data,tpl_types[c->type].sz * c->num); 1095 | break; 1096 | case TPL_TYPE_BIN: 1097 | slen = (*(tpl_bin**)(c->data))->sz; 1098 | dv = tpl_cpv(dv,&slen,sizeof(uint32_t)); /* buffer len */ 1099 | dv = tpl_cpv(dv,(*(tpl_bin**)(c->data))->addr,slen); /* buf */ 1100 | break; 1101 | case TPL_TYPE_STR: 1102 | for(i=0; i < c->num; i++) { 1103 | char *str = ((char**)c->data)[i]; 1104 | slen = str ? strlen(str)+1 : 0; 1105 | dv = tpl_cpv(dv,&slen,sizeof(uint32_t)); /* string len */ 1106 | if (slen>1) dv = tpl_cpv(dv,str,slen-1); /*string*/ 1107 | } 1108 | break; 1109 | case TPL_TYPE_ARY: 1110 | dv = tpl_dump_atyp(c,(tpl_atyp*)c->data,dv); 1111 | break; 1112 | case TPL_TYPE_POUND: 1113 | pd = (tpl_pound_data*)c->data; 1114 | itermax = c->num; 1115 | if (++(pd->iternum) < itermax) { 1116 | 1117 | /* in start or midst of loop. advance data pointers. */ 1118 | for(np=pd->iter_start_node; np != c; np = np->next) { 1119 | np->data = (char*)(np->data) + 1120 | (tpl_types[np->type].sz * np->num); 1121 | } 1122 | /* do next iteration */ 1123 | c = pd->iter_start_node; 1124 | continue; 1125 | 1126 | } else { /* loop complete. */ 1127 | 1128 | /* reset iteration index and addr/data pointers. */ 1129 | pd->iternum = 0; 1130 | for(np=pd->iter_start_node; np != c; np = np->next) { 1131 | np->data = (char*)(np->data) - ((itermax-1) * 1132 | tpl_types[np->type].sz * 1133 | np->num); 1134 | } 1135 | 1136 | } 1137 | break; 1138 | default: 1139 | tpl_hook.fatal("unsupported format character\n"); 1140 | break; 1141 | } 1142 | c = c->next; 1143 | } 1144 | 1145 | return 0; 1146 | } 1147 | 1148 | static int tpl_cpu_bigendian() { 1149 | unsigned i = 1; 1150 | char *c; 1151 | c = (char*)&i; 1152 | return (c[0] == 1 ? 0 : 1); 1153 | } 1154 | 1155 | 1156 | /* 1157 | * algorithm for sanity-checking a tpl image: 1158 | * scan the tpl whilst not exceeding the buffer size (bufsz) , 1159 | * formulating a calculated (expected) size of the tpl based 1160 | * on walking its data. When calcsize has been calculated it 1161 | * should exactly match the buffer size (bufsz) and the internal 1162 | * recorded size (intlsz) 1163 | */ 1164 | static int tpl_sanity(tpl_node *r, int excess_ok) { 1165 | uint32_t intlsz; 1166 | int found_nul=0,rc, octothorpes=0, num_fxlens, *fxlens, flen; 1167 | void *d, *dv; 1168 | char intlflags, *fmt, c, *mapfmt; 1169 | size_t bufsz, serlen; 1170 | 1171 | d = ((tpl_root_data*)(r->data))->mmap.text; 1172 | bufsz = ((tpl_root_data*)(r->data))->mmap.text_sz; 1173 | 1174 | dv = d; 1175 | if (bufsz < (4 + sizeof(uint32_t) + 1)) return ERR_NOT_MINSIZE; /* min sz: magic+flags+len+nul */ 1176 | if (memcmp(dv,TPL_MAGIC, 3) != 0) return ERR_MAGIC_MISMATCH; /* missing tpl magic prefix */ 1177 | if (tpl_needs_endian_swap(dv)) ((tpl_root_data*)(r->data))->flags |= TPL_XENDIAN; 1178 | dv = (void*)((uintptr_t)dv + 3); 1179 | memcpy(&intlflags,dv,sizeof(char)); /* extract flags */ 1180 | if (intlflags & ~TPL_SUPPORTED_BITFLAGS) return ERR_UNSUPPORTED_FLAGS; 1181 | /* TPL1.3 stores strings with a "length+1" prefix to discern NULL strings from 1182 | empty strings from non-empty strings; TPL1.2 only handled the latter two. 1183 | So we need to be mindful of which string format we're reading from. */ 1184 | if (!(intlflags & TPL_FL_NULLSTRINGS)) { 1185 | ((tpl_root_data*)(r->data))->flags |= TPL_OLD_STRING_FMT; 1186 | } 1187 | dv = (void*)((uintptr_t)dv + 1); 1188 | memcpy(&intlsz,dv,sizeof(uint32_t)); /* extract internal size */ 1189 | if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN) tpl_byteswap(&intlsz, sizeof(uint32_t)); 1190 | if (!excess_ok && (intlsz != bufsz)) return ERR_INCONSISTENT_SZ; /* inconsisent buffer/internal size */ 1191 | dv = (void*)((uintptr_t)dv + sizeof(uint32_t)); 1192 | 1193 | /* dv points to the start of the format string. Look for nul w/in buf sz */ 1194 | fmt = (char*)dv; 1195 | while ((uintptr_t)dv-(uintptr_t)d < bufsz && !found_nul) { 1196 | if ( (c = *(char*)dv) != '\0') { 1197 | if (strchr(tpl_fmt_chars,c) == NULL) 1198 | return ERR_FMT_INVALID; /* invalid char in format string */ 1199 | if ( (c = *(char*)dv) == '#') octothorpes++; 1200 | dv = (void*)((uintptr_t)dv + 1); 1201 | } 1202 | else found_nul = 1; 1203 | } 1204 | if (!found_nul) return ERR_FMT_MISSING_NUL; /* runaway format string */ 1205 | dv = (void*)((uintptr_t)dv + 1); /* advance to octothorpe lengths buffer */ 1206 | 1207 | /* compare the map format to the format of this tpl image */ 1208 | mapfmt = tpl_fmt(r); 1209 | rc = strcmp(mapfmt,fmt); 1210 | if (rc != 0) return ERR_FMT_MISMATCH; 1211 | 1212 | /* compare octothorpe lengths in image to the mapped values */ 1213 | if ((((uintptr_t)dv + (octothorpes * 4)) - (uintptr_t)d) > bufsz) return ERR_INCONSISTENT_SZ4; 1214 | fxlens = tpl_fxlens(r,&num_fxlens); /* mapped fxlens */ 1215 | while(num_fxlens--) { 1216 | memcpy(&flen,dv,sizeof(uint32_t)); /* stored flen */ 1217 | if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN) tpl_byteswap(&flen, sizeof(uint32_t)); 1218 | if (flen != *fxlens) return ERR_FLEN_MISMATCH; 1219 | dv = (void*)((uintptr_t)dv + sizeof(uint32_t)); 1220 | fxlens++; 1221 | } 1222 | 1223 | /* dv now points to beginning of data */ 1224 | rc = tpl_serlen(r,r,dv,&serlen); /* get computed serlen of data part */ 1225 | if (rc == -1) return ERR_INCONSISTENT_SZ2; /* internal inconsistency in tpl image */ 1226 | serlen += ((uintptr_t)dv - (uintptr_t)d); /* add back serlen of preamble part */ 1227 | if (excess_ok && (bufsz < serlen)) return ERR_INCONSISTENT_SZ3; 1228 | if (!excess_ok && (serlen != bufsz)) return ERR_INCONSISTENT_SZ3; /* buffer/internal sz exceeds serlen */ 1229 | return 0; 1230 | } 1231 | 1232 | static void *tpl_find_data_start(void *d) { 1233 | int octothorpes=0; 1234 | d = (void*)((uintptr_t)d + 4); /* skip TPL_MAGIC and flags byte */ 1235 | d = (void*)((uintptr_t)d + 4); /* skip int32 overall len */ 1236 | while(*(char*)d != '\0') { 1237 | if (*(char*)d == '#') octothorpes++; 1238 | d = (void*)((uintptr_t)d + 1); 1239 | } 1240 | d = (void*)((uintptr_t)d + 1); /* skip NUL */ 1241 | d = (void*)((uintptr_t)d + (octothorpes * sizeof(uint32_t))); /* skip # array lens */ 1242 | return d; 1243 | } 1244 | 1245 | static int tpl_needs_endian_swap(void *d) { 1246 | char *c; 1247 | int cpu_is_bigendian; 1248 | c = (char*)d; 1249 | cpu_is_bigendian = tpl_cpu_bigendian(); 1250 | return ((c[3] & TPL_FL_BIGENDIAN) == cpu_is_bigendian) ? 0 : 1; 1251 | } 1252 | 1253 | static size_t tpl_size_for(char c) { 1254 | int i; 1255 | for(i=0; i < sizeof(tpl_types)/sizeof(tpl_types[0]); i++) { 1256 | if (tpl_types[i].c == c) return tpl_types[i].sz; 1257 | } 1258 | return 0; 1259 | } 1260 | 1261 | TPL_API char* tpl_peek(int mode, ...) { 1262 | va_list ap; 1263 | int xendian=0,found_nul=0,old_string_format=0; 1264 | char *filename=NULL, *datapeek_f=NULL, *datapeek_c, *datapeek_s; 1265 | void *addr=NULL, *dv, *datapeek_p=NULL; 1266 | size_t sz=0, fmt_len, first_atom, num_fxlens=0; 1267 | uint32_t datapeek_ssz, datapeek_csz, datapeek_flen; 1268 | tpl_mmap_rec mr = {0,NULL,0}; 1269 | char *fmt,*fmt_cpy=NULL,c; 1270 | uint32_t intlsz, **fxlens=NULL, *num_fxlens_out=NULL, *fxlensv; 1271 | 1272 | va_start(ap,mode); 1273 | if ((mode & TPL_FXLENS) && (mode & TPL_DATAPEEK)) { 1274 | tpl_hook.oops("TPL_FXLENS and TPL_DATAPEEK mutually exclusive\n"); 1275 | goto fail; 1276 | } 1277 | if (mode & TPL_FILE) filename = va_arg(ap,char *); 1278 | else if (mode & TPL_MEM) { 1279 | addr = va_arg(ap,void *); 1280 | sz = va_arg(ap,size_t); 1281 | } else { 1282 | tpl_hook.oops("unsupported tpl_peek mode %d\n", mode); 1283 | goto fail; 1284 | } 1285 | if (mode & TPL_DATAPEEK) { 1286 | datapeek_f = va_arg(ap, char*); 1287 | } 1288 | if (mode & TPL_FXLENS) { 1289 | num_fxlens_out = va_arg(ap,uint32_t *); 1290 | fxlens = va_arg(ap,uint32_t **); 1291 | *num_fxlens_out = 0; 1292 | *fxlens = NULL; 1293 | } 1294 | 1295 | if (mode & TPL_FILE) { 1296 | if (tpl_mmap_file(filename, &mr) != 0) { 1297 | tpl_hook.oops("tpl_peek failed for file %s\n", filename); 1298 | goto fail; 1299 | } 1300 | addr = mr.text; 1301 | sz = mr.text_sz; 1302 | } 1303 | 1304 | dv = addr; 1305 | if (sz < (4 + sizeof(uint32_t) + 1)) goto fail; /* min sz */ 1306 | if (memcmp(dv,TPL_MAGIC, 3) != 0) goto fail; /* missing tpl magic prefix */ 1307 | if (tpl_needs_endian_swap(dv)) xendian=1; 1308 | if ((((char*)dv)[3] & TPL_FL_NULLSTRINGS)==0) old_string_format=1; 1309 | dv = (void*)((uintptr_t)dv + 4); 1310 | memcpy(&intlsz,dv,sizeof(uint32_t)); /* extract internal size */ 1311 | if (xendian) tpl_byteswap(&intlsz, sizeof(uint32_t)); 1312 | if (intlsz != sz) goto fail; /* inconsisent buffer/internal size */ 1313 | dv = (void*)((uintptr_t)dv + sizeof(uint32_t)); 1314 | 1315 | /* dv points to the start of the format string. Look for nul w/in buf sz */ 1316 | fmt = (char*)dv; 1317 | while ((uintptr_t)dv-(uintptr_t)addr < sz && !found_nul) { 1318 | if ( (c = *(char*)dv) == '\0') { 1319 | found_nul = 1; 1320 | } else if (c == '#') { 1321 | num_fxlens++; 1322 | } 1323 | dv = (void*)((uintptr_t)dv + 1); 1324 | } 1325 | if (!found_nul) goto fail; /* runaway format string */ 1326 | fmt_len = (char*)dv - fmt; /* include space for \0 */ 1327 | fmt_cpy = tpl_hook.malloc(fmt_len); 1328 | if (fmt_cpy == NULL) { 1329 | fatal_oom(); 1330 | } 1331 | memcpy(fmt_cpy, fmt, fmt_len); 1332 | 1333 | /* retrieve the octothorpic lengths if requested */ 1334 | if (num_fxlens > 0) { 1335 | if (sz < ((uintptr_t)dv + (num_fxlens * sizeof(uint32_t)) - (uintptr_t)addr)) { 1336 | goto fail; 1337 | } 1338 | } 1339 | if ((mode & TPL_FXLENS) && (num_fxlens > 0)) { 1340 | *fxlens = tpl_hook.malloc(num_fxlens * sizeof(uint32_t)); 1341 | if (*fxlens == NULL) tpl_hook.fatal("out of memory"); 1342 | *num_fxlens_out = num_fxlens; 1343 | fxlensv = *fxlens; 1344 | while(num_fxlens--) { 1345 | memcpy(fxlensv,dv,sizeof(uint32_t)); 1346 | if (xendian) tpl_byteswap(fxlensv, sizeof(uint32_t)); 1347 | dv = (void*)((uintptr_t)dv + sizeof(uint32_t)); 1348 | fxlensv++; 1349 | } 1350 | } 1351 | /* if caller requested, peek into the specified data elements */ 1352 | if (mode & TPL_DATAPEEK) { 1353 | 1354 | first_atom = strspn(fmt, "S()"); /* skip any leading S() */ 1355 | 1356 | datapeek_flen = strlen(datapeek_f); 1357 | if (strspn(datapeek_f, tpl_datapeek_ok_chars) < datapeek_flen) { 1358 | tpl_hook.oops("invalid TPL_DATAPEEK format: %s\n", datapeek_f); 1359 | tpl_hook.free(fmt_cpy); fmt_cpy = NULL; /* fail */ 1360 | goto fail; 1361 | } 1362 | 1363 | if (strncmp( &fmt[first_atom], datapeek_f, datapeek_flen) != 0) { 1364 | tpl_hook.oops("TPL_DATAPEEK format mismatches tpl iamge\n"); 1365 | tpl_hook.free(fmt_cpy); fmt_cpy = NULL; /* fail */ 1366 | goto fail; 1367 | } 1368 | 1369 | /* advance to data start, then copy out requested elements */ 1370 | dv = (void*)((uintptr_t)dv + (num_fxlens * sizeof(uint32_t))); 1371 | for(datapeek_c = datapeek_f; *datapeek_c != '\0'; datapeek_c++) { 1372 | datapeek_p = va_arg(ap, void*); 1373 | if (*datapeek_c == 's') { /* special handling for strings */ 1374 | if ((uintptr_t)dv-(uintptr_t)addr + sizeof(uint32_t) > sz) { 1375 | tpl_hook.oops("tpl_peek: tpl has insufficient length\n"); 1376 | tpl_hook.free(fmt_cpy); fmt_cpy = NULL; /* fail */ 1377 | goto fail; 1378 | } 1379 | memcpy(&datapeek_ssz,dv,sizeof(uint32_t)); /* get slen */ 1380 | if (xendian) tpl_byteswap(&datapeek_ssz, sizeof(uint32_t)); 1381 | if (old_string_format) datapeek_ssz++; 1382 | dv = (void*)((uintptr_t)dv + sizeof(uint32_t)); /* adv. to str */ 1383 | if (datapeek_ssz == 0) datapeek_s = NULL; 1384 | else { 1385 | if ((uintptr_t)dv-(uintptr_t)addr + datapeek_ssz-1 > sz) { 1386 | tpl_hook.oops("tpl_peek: tpl has insufficient length\n"); 1387 | tpl_hook.free(fmt_cpy); fmt_cpy = NULL; /* fail */ 1388 | goto fail; 1389 | } 1390 | datapeek_s = tpl_hook.malloc(datapeek_ssz); 1391 | if (datapeek_s == NULL) fatal_oom(); 1392 | memcpy(datapeek_s, dv, datapeek_ssz-1); 1393 | datapeek_s[datapeek_ssz-1] = '\0'; 1394 | dv = (void*)((uintptr_t)dv + datapeek_ssz-1); 1395 | } 1396 | *(char**)datapeek_p = datapeek_s; 1397 | } else { 1398 | datapeek_csz = tpl_size_for(*datapeek_c); 1399 | if ((uintptr_t)dv-(uintptr_t)addr + datapeek_csz > sz) { 1400 | tpl_hook.oops("tpl_peek: tpl has insufficient length\n"); 1401 | tpl_hook.free(fmt_cpy); fmt_cpy = NULL; /* fail */ 1402 | goto fail; 1403 | } 1404 | memcpy(datapeek_p, dv, datapeek_csz); 1405 | if (xendian) tpl_byteswap(datapeek_p, datapeek_csz); 1406 | dv = (void*)((uintptr_t)dv + datapeek_csz); 1407 | } 1408 | } 1409 | } 1410 | 1411 | fail: 1412 | va_end(ap); 1413 | if ((mode & TPL_FILE) && mr.text != NULL) tpl_unmap_file( &mr ); 1414 | return fmt_cpy; 1415 | } 1416 | 1417 | /* tpl_jot(TPL_FILE, "file.tpl", "si", &s, &i); */ 1418 | /* tpl_jot(TPL_MEM, &buf, &sz, "si", &s, &i); */ 1419 | /* tpl_jot(TPL_FD, fd, "si", &s, &i); */ 1420 | TPL_API int tpl_jot(int mode, ...) { 1421 | va_list ap; 1422 | char *filename, *fmt; 1423 | size_t *sz; 1424 | int fd, rc=0; 1425 | void **buf; 1426 | tpl_node *tn; 1427 | 1428 | va_start(ap,mode); 1429 | if (mode & TPL_FILE) { 1430 | filename = va_arg(ap,char*); 1431 | fmt = va_arg(ap,char*); 1432 | tn = tpl_map_va(fmt, ap); 1433 | if (tn == NULL) { rc=-1; goto fail;} 1434 | tpl_pack(tn, 0); 1435 | rc = tpl_dump(tn, TPL_FILE, filename); 1436 | tpl_free(tn); 1437 | } else if (mode & TPL_MEM) { 1438 | buf = va_arg(ap,void*); 1439 | sz = va_arg(ap,size_t*); 1440 | fmt = va_arg(ap,char*); 1441 | tn = tpl_map_va(fmt,ap); 1442 | if (tn == NULL) { rc=-1; goto fail;} 1443 | tpl_pack(tn,0); 1444 | rc = tpl_dump(tn, TPL_MEM, buf, sz); 1445 | tpl_free(tn); 1446 | } else if (mode & TPL_FD) { 1447 | fd = va_arg(ap,int); 1448 | fmt = va_arg(ap,char*); 1449 | tn = tpl_map_va(fmt,ap); 1450 | if (tn == NULL) { rc=-1; goto fail;} 1451 | tpl_pack(tn,0); 1452 | rc = tpl_dump(tn, TPL_FD, fd); 1453 | tpl_free(tn); 1454 | } else { 1455 | tpl_hook.fatal("invalid tpl_jot mode\n"); 1456 | } 1457 | 1458 | fail: 1459 | va_end(ap); 1460 | return rc; 1461 | } 1462 | 1463 | TPL_API int tpl_load(tpl_node *r, int mode, ...) { 1464 | va_list ap; 1465 | int rc=0,fd=0; 1466 | char *filename=NULL; 1467 | void *addr; 1468 | size_t sz; 1469 | 1470 | va_start(ap,mode); 1471 | if (mode & TPL_FILE) filename = va_arg(ap,char *); 1472 | else if (mode & TPL_MEM) { 1473 | addr = va_arg(ap,void *); 1474 | sz = va_arg(ap,size_t); 1475 | } else if (mode & TPL_FD) { 1476 | fd = va_arg(ap,int); 1477 | } else { 1478 | tpl_hook.oops("unsupported tpl_load mode %d\n", mode); 1479 | return -1; 1480 | } 1481 | va_end(ap); 1482 | 1483 | if (r->type != TPL_TYPE_ROOT) { 1484 | tpl_hook.oops("error: tpl_load to non-root node\n"); 1485 | return -1; 1486 | } 1487 | if (((tpl_root_data*)(r->data))->flags & (TPL_WRONLY|TPL_RDONLY)) { 1488 | /* already packed or loaded, so reset it as if newly mapped */ 1489 | tpl_free_keep_map(r); 1490 | } 1491 | if (mode & TPL_FILE) { 1492 | if (tpl_mmap_file(filename, &((tpl_root_data*)(r->data))->mmap) != 0) { 1493 | tpl_hook.oops("tpl_load failed for file %s\n", filename); 1494 | return -1; 1495 | } 1496 | if ( (rc = tpl_sanity(r, (mode & TPL_EXCESS_OK))) != 0) { 1497 | if (rc == ERR_FMT_MISMATCH) { 1498 | tpl_hook.oops("%s: format signature mismatch\n", filename); 1499 | } else if (rc == ERR_FLEN_MISMATCH) { 1500 | tpl_hook.oops("%s: array lengths mismatch\n", filename); 1501 | } else { 1502 | tpl_hook.oops("%s: not a valid tpl file\n", filename); 1503 | } 1504 | tpl_unmap_file( &((tpl_root_data*)(r->data))->mmap ); 1505 | return -1; 1506 | } 1507 | ((tpl_root_data*)(r->data))->flags = (TPL_FILE | TPL_RDONLY); 1508 | } else if (mode & TPL_MEM) { 1509 | ((tpl_root_data*)(r->data))->mmap.text = addr; 1510 | ((tpl_root_data*)(r->data))->mmap.text_sz = sz; 1511 | if ( (rc = tpl_sanity(r, (mode & TPL_EXCESS_OK))) != 0) { 1512 | if (rc == ERR_FMT_MISMATCH) { 1513 | tpl_hook.oops("format signature mismatch\n"); 1514 | } else { 1515 | tpl_hook.oops("not a valid tpl file\n"); 1516 | } 1517 | return -1; 1518 | } 1519 | ((tpl_root_data*)(r->data))->flags = (TPL_MEM | TPL_RDONLY); 1520 | if (mode & TPL_UFREE) ((tpl_root_data*)(r->data))->flags |= TPL_UFREE; 1521 | } else if (mode & TPL_FD) { 1522 | /* if fd read succeeds, resulting mem img is used for load */ 1523 | if (tpl_gather(TPL_GATHER_BLOCKING,fd,&addr,&sz) > 0) { 1524 | return tpl_load(r, TPL_MEM|TPL_UFREE, addr, sz); 1525 | } else return -1; 1526 | } else { 1527 | tpl_hook.oops("invalid tpl_load mode %d\n", mode); 1528 | return -1; 1529 | } 1530 | /* this applies to TPL_MEM or TPL_FILE */ 1531 | if (tpl_needs_endian_swap(((tpl_root_data*)(r->data))->mmap.text)) 1532 | ((tpl_root_data*)(r->data))->flags |= TPL_XENDIAN; 1533 | tpl_unpackA0(r); /* prepare root A nodes for use */ 1534 | return 0; 1535 | } 1536 | 1537 | TPL_API int tpl_Alen(tpl_node *r, int i) { 1538 | tpl_node *n; 1539 | 1540 | n = tpl_find_i(r,i); 1541 | if (n == NULL) { 1542 | tpl_hook.oops("invalid index %d to tpl_unpack\n", i); 1543 | return -1; 1544 | } 1545 | if (n->type != TPL_TYPE_ARY) return -1; 1546 | return ((tpl_atyp*)(n->data))->num; 1547 | } 1548 | 1549 | static void tpl_free_atyp(tpl_node *n, tpl_atyp *atyp) { 1550 | tpl_backbone *bb,*bbnxt; 1551 | tpl_node *c; 1552 | void *dv; 1553 | tpl_bin *binp; 1554 | tpl_atyp *atypp; 1555 | char *strp; 1556 | size_t itermax; 1557 | tpl_pound_data *pd; 1558 | int i; 1559 | 1560 | bb = atyp->bb; 1561 | while (bb) { 1562 | bbnxt = bb->next; 1563 | dv = bb->data; 1564 | c=n->children; 1565 | while (c) { 1566 | switch (c->type) { 1567 | case TPL_TYPE_BYTE: 1568 | case TPL_TYPE_DOUBLE: 1569 | case TPL_TYPE_INT32: 1570 | case TPL_TYPE_UINT32: 1571 | case TPL_TYPE_INT64: 1572 | case TPL_TYPE_UINT64: 1573 | case TPL_TYPE_INT16: 1574 | case TPL_TYPE_UINT16: 1575 | dv = (void*)((uintptr_t)dv + tpl_types[c->type].sz*c->num); 1576 | break; 1577 | case TPL_TYPE_BIN: 1578 | memcpy(&binp,dv,sizeof(tpl_bin*)); /* cp to aligned */ 1579 | if (binp->addr) tpl_hook.free( binp->addr ); /* free buf */ 1580 | tpl_hook.free(binp); /* free tpl_bin */ 1581 | dv = (void*)((uintptr_t)dv + sizeof(tpl_bin*)); 1582 | break; 1583 | case TPL_TYPE_STR: 1584 | for(i=0; i < c->num; i++) { 1585 | memcpy(&strp,dv,sizeof(char*)); /* cp to aligned */ 1586 | if (strp) tpl_hook.free(strp); /* free string */ 1587 | dv = (void*)((uintptr_t)dv + sizeof(char*)); 1588 | } 1589 | break; 1590 | case TPL_TYPE_POUND: 1591 | /* iterate over the preceding nodes */ 1592 | itermax = c->num; 1593 | pd = (tpl_pound_data*)c->data; 1594 | if (++(pd->iternum) < itermax) { 1595 | c = pd->iter_start_node; 1596 | continue; 1597 | } else { /* loop complete. */ 1598 | pd->iternum = 0; 1599 | } 1600 | break; 1601 | case TPL_TYPE_ARY: 1602 | memcpy(&atypp,dv,sizeof(tpl_atyp*)); /* cp to aligned */ 1603 | tpl_free_atyp(c,atypp); /* free atyp */ 1604 | dv = (void*)((uintptr_t)dv + sizeof(void*)); 1605 | break; 1606 | default: 1607 | tpl_hook.fatal("unsupported format character\n"); 1608 | break; 1609 | } 1610 | c=c->next; 1611 | } 1612 | tpl_hook.free(bb); 1613 | bb = bbnxt; 1614 | } 1615 | tpl_hook.free(atyp); 1616 | } 1617 | 1618 | /* determine (by walking) byte length of serialized r/A node at address dv 1619 | * returns 0 on success, or -1 if the tpl isn't trustworthy (fails consistency) 1620 | */ 1621 | static int tpl_serlen(tpl_node *r, tpl_node *n, void *dv, size_t *serlen) { 1622 | uint32_t slen; 1623 | int num,fidx; 1624 | tpl_node *c; 1625 | size_t len=0, alen, buf_past, itermax; 1626 | tpl_pound_data *pd; 1627 | 1628 | buf_past = ((uintptr_t)((tpl_root_data*)(r->data))->mmap.text + 1629 | ((tpl_root_data*)(r->data))->mmap.text_sz); 1630 | 1631 | if (n->type == TPL_TYPE_ROOT) num = 1; 1632 | else if (n->type == TPL_TYPE_ARY) { 1633 | if ((uintptr_t)dv + sizeof(uint32_t) > buf_past) return -1; 1634 | memcpy(&num,dv,sizeof(uint32_t)); 1635 | if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN) 1636 | tpl_byteswap(&num, sizeof(uint32_t)); 1637 | dv = (void*)((uintptr_t)dv + sizeof(uint32_t)); 1638 | len += sizeof(uint32_t); 1639 | } else tpl_hook.fatal("internal error in tpl_serlen\n"); 1640 | 1641 | while (num-- > 0) { 1642 | c=n->children; 1643 | while (c) { 1644 | switch (c->type) { 1645 | case TPL_TYPE_BYTE: 1646 | case TPL_TYPE_DOUBLE: 1647 | case TPL_TYPE_INT32: 1648 | case TPL_TYPE_UINT32: 1649 | case TPL_TYPE_INT64: 1650 | case TPL_TYPE_UINT64: 1651 | case TPL_TYPE_INT16: 1652 | case TPL_TYPE_UINT16: 1653 | for(fidx=0; fidx < c->num; fidx++) { /* octothorpe support */ 1654 | if ((uintptr_t)dv + tpl_types[c->type].sz > buf_past) return -1; 1655 | dv = (void*)((uintptr_t)dv + tpl_types[c->type].sz); 1656 | len += tpl_types[c->type].sz; 1657 | } 1658 | break; 1659 | case TPL_TYPE_BIN: 1660 | len += sizeof(uint32_t); 1661 | if ((uintptr_t)dv + sizeof(uint32_t) > buf_past) return -1; 1662 | memcpy(&slen,dv,sizeof(uint32_t)); 1663 | if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN) 1664 | tpl_byteswap(&slen, sizeof(uint32_t)); 1665 | len += slen; 1666 | dv = (void*)((uintptr_t)dv + sizeof(uint32_t)); 1667 | if ((uintptr_t)dv + slen > buf_past) return -1; 1668 | dv = (void*)((uintptr_t)dv + slen); 1669 | break; 1670 | case TPL_TYPE_STR: 1671 | for(fidx=0; fidx < c->num; fidx++) { /* octothorpe support */ 1672 | len += sizeof(uint32_t); 1673 | if ((uintptr_t)dv + sizeof(uint32_t) > buf_past) return -1; 1674 | memcpy(&slen,dv,sizeof(uint32_t)); 1675 | if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN) 1676 | tpl_byteswap(&slen, sizeof(uint32_t)); 1677 | if (!(((tpl_root_data*)(r->data))->flags & TPL_OLD_STRING_FMT)) 1678 | slen = (slen>1) ? (slen-1) : 0; 1679 | len += slen; 1680 | dv = (void*)((uintptr_t)dv + sizeof(uint32_t)); 1681 | if ((uintptr_t)dv + slen > buf_past) return -1; 1682 | dv = (void*)((uintptr_t)dv + slen); 1683 | } 1684 | break; 1685 | case TPL_TYPE_ARY: 1686 | if ( tpl_serlen(r,c,dv, &alen) == -1) return -1; 1687 | dv = (void*)((uintptr_t)dv + alen); 1688 | len += alen; 1689 | break; 1690 | case TPL_TYPE_POUND: 1691 | /* iterate over the preceding nodes */ 1692 | itermax = c->num; 1693 | pd = (tpl_pound_data*)c->data; 1694 | if (++(pd->iternum) < itermax) { 1695 | c = pd->iter_start_node; 1696 | continue; 1697 | } else { /* loop complete. */ 1698 | pd->iternum = 0; 1699 | } 1700 | break; 1701 | default: 1702 | tpl_hook.fatal("unsupported format character\n"); 1703 | break; 1704 | } 1705 | c=c->next; 1706 | } 1707 | } 1708 | *serlen = len; 1709 | return 0; 1710 | } 1711 | 1712 | static int tpl_mmap_output_file(char *filename, size_t sz, void **text_out) { 1713 | void *text; 1714 | int fd,perms; 1715 | 1716 | #ifndef _WIN32 1717 | perms = S_IRUSR|S_IWUSR|S_IWGRP|S_IRGRP|S_IROTH; /* ug+w o+r */ 1718 | fd=open(filename,O_CREAT|O_TRUNC|O_RDWR,perms); 1719 | #else 1720 | perms = _S_IWRITE; 1721 | fd=_open(filename,_O_CREAT|_O_TRUNC|_O_RDWR,perms); 1722 | #endif 1723 | 1724 | if ( fd == -1 ) { 1725 | tpl_hook.oops("Couldn't open file %s: %s\n", filename, strerror(errno)); 1726 | return -1; 1727 | } 1728 | 1729 | text = mmap(0, sz, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); 1730 | if (text == MAP_FAILED) { 1731 | tpl_hook.oops("Failed to mmap %s: %s\n", filename, strerror(errno)); 1732 | close(fd); 1733 | return -1; 1734 | } 1735 | if (ftruncate(fd,sz) == -1) { 1736 | tpl_hook.oops("ftruncate failed: %s\n", strerror(errno)); 1737 | munmap( text, sz ); 1738 | close(fd); 1739 | return -1; 1740 | } 1741 | *text_out = text; 1742 | return fd; 1743 | } 1744 | 1745 | static int tpl_mmap_file(char *filename, tpl_mmap_rec *mr) { 1746 | struct stat stat_buf; 1747 | 1748 | if ( (mr->fd = open(filename, O_RDONLY)) == -1 ) { 1749 | tpl_hook.oops("Couldn't open file %s: %s\n", filename, strerror(errno)); 1750 | return -1; 1751 | } 1752 | 1753 | if ( fstat(mr->fd, &stat_buf) == -1) { 1754 | close(mr->fd); 1755 | tpl_hook.oops("Couldn't stat file %s: %s\n", filename, strerror(errno)); 1756 | return -1; 1757 | } 1758 | 1759 | mr->text_sz = (size_t)stat_buf.st_size; 1760 | mr->text = mmap(0, stat_buf.st_size, PROT_READ, MAP_PRIVATE, mr->fd, 0); 1761 | if (mr->text == MAP_FAILED) { 1762 | close(mr->fd); 1763 | tpl_hook.oops("Failed to mmap %s: %s\n", filename, strerror(errno)); 1764 | return -1; 1765 | } 1766 | 1767 | return 0; 1768 | } 1769 | 1770 | TPL_API int tpl_pack(tpl_node *r, int i) { 1771 | tpl_node *n, *child, *np; 1772 | void *datav=NULL; 1773 | size_t sz, itermax; 1774 | uint32_t slen; 1775 | char *str; 1776 | tpl_bin *bin; 1777 | tpl_pound_data *pd; 1778 | int fidx; 1779 | 1780 | n = tpl_find_i(r,i); 1781 | if (n == NULL) { 1782 | tpl_hook.oops("invalid index %d to tpl_pack\n", i); 1783 | return -1; 1784 | } 1785 | 1786 | if (((tpl_root_data*)(r->data))->flags & TPL_RDONLY) { 1787 | /* convert to an writeable tpl, initially empty */ 1788 | tpl_free_keep_map(r); 1789 | } 1790 | 1791 | ((tpl_root_data*)(r->data))->flags |= TPL_WRONLY; 1792 | 1793 | if (n->type == TPL_TYPE_ARY) datav = tpl_extend_backbone(n); 1794 | child = n->children; 1795 | while(child) { 1796 | switch(child->type) { 1797 | case TPL_TYPE_BYTE: 1798 | case TPL_TYPE_DOUBLE: 1799 | case TPL_TYPE_INT32: 1800 | case TPL_TYPE_UINT32: 1801 | case TPL_TYPE_INT64: 1802 | case TPL_TYPE_UINT64: 1803 | case TPL_TYPE_INT16: 1804 | case TPL_TYPE_UINT16: 1805 | /* no need to use fidx iteration here; we can copy multiple values in one memcpy */ 1806 | memcpy(child->data,child->addr,tpl_types[child->type].sz * child->num); 1807 | if (datav) datav = tpl_cpv(datav,child->data,tpl_types[child->type].sz * child->num); 1808 | if (n->type == TPL_TYPE_ARY) n->ser_osz += tpl_types[child->type].sz * child->num; 1809 | break; 1810 | case TPL_TYPE_BIN: 1811 | /* copy the buffer to be packed */ 1812 | slen = ((tpl_bin*)child->addr)->sz; 1813 | if (slen >0) { 1814 | str = tpl_hook.malloc(slen); 1815 | if (!str) fatal_oom(); 1816 | memcpy(str,((tpl_bin*)child->addr)->addr,slen); 1817 | } else str = NULL; 1818 | /* and make a tpl_bin to point to it */ 1819 | bin = tpl_hook.malloc(sizeof(tpl_bin)); 1820 | if (!bin) fatal_oom(); 1821 | bin->addr = str; 1822 | bin->sz = slen; 1823 | /* now pack its pointer, first deep freeing any pre-existing bin */ 1824 | if (*(tpl_bin**)(child->data) != NULL) { 1825 | if ((*(tpl_bin**)(child->data))->sz != 0) { 1826 | tpl_hook.free( (*(tpl_bin**)(child->data))->addr ); 1827 | } 1828 | tpl_hook.free(*(tpl_bin**)(child->data)); 1829 | } 1830 | memcpy(child->data,&bin,sizeof(tpl_bin*)); 1831 | if (datav) { 1832 | datav = tpl_cpv(datav, &bin, sizeof(tpl_bin*)); 1833 | *(tpl_bin**)(child->data) = NULL; 1834 | } 1835 | if (n->type == TPL_TYPE_ARY) { 1836 | n->ser_osz += sizeof(uint32_t); /* binary buf len word */ 1837 | n->ser_osz += bin->sz; /* binary buf */ 1838 | } 1839 | break; 1840 | case TPL_TYPE_STR: 1841 | for(fidx=0; fidx < child->num; fidx++) { 1842 | /* copy the string to be packed. slen includes \0. this 1843 | block also works if the string pointer is NULL. */ 1844 | char *caddr = ((char**)child->addr)[fidx]; 1845 | char **cdata = &((char**)child->data)[fidx]; 1846 | slen = caddr ? (strlen(caddr) + 1) : 0; 1847 | if (slen) { 1848 | str = tpl_hook.malloc(slen); 1849 | if (!str) fatal_oom(); 1850 | memcpy(str,caddr,slen); /* include \0 */ 1851 | } else { 1852 | str = NULL; 1853 | } 1854 | /* now pack its pointer, first freeing any pre-existing string */ 1855 | if (*cdata != NULL) { 1856 | tpl_hook.free(*cdata); 1857 | } 1858 | memcpy(cdata,&str,sizeof(char*)); 1859 | if (datav) { 1860 | datav = tpl_cpv(datav, &str, sizeof(char*)); 1861 | *cdata = NULL; 1862 | } 1863 | if (n->type == TPL_TYPE_ARY) { 1864 | n->ser_osz += sizeof(uint32_t); /* string len word */ 1865 | if (slen>1) n->ser_osz += slen-1;/* string (without nul) */ 1866 | } 1867 | } 1868 | break; 1869 | case TPL_TYPE_ARY: 1870 | /* copy the child's tpl_atype* and reset it to empty */ 1871 | if (datav) { 1872 | sz = ((tpl_atyp*)(child->data))->sz; 1873 | datav = tpl_cpv(datav, &child->data, sizeof(void*)); 1874 | child->data = tpl_hook.malloc(sizeof(tpl_atyp)); 1875 | if (!child->data) fatal_oom(); 1876 | ((tpl_atyp*)(child->data))->num = 0; 1877 | ((tpl_atyp*)(child->data))->sz = sz; 1878 | ((tpl_atyp*)(child->data))->bb = NULL; 1879 | ((tpl_atyp*)(child->data))->bbtail = NULL; 1880 | } 1881 | /* parent is array? then bubble up child array's ser_osz */ 1882 | if (n->type == TPL_TYPE_ARY) { 1883 | n->ser_osz += sizeof(uint32_t); /* array len word */ 1884 | n->ser_osz += child->ser_osz; /* child array ser_osz */ 1885 | child->ser_osz = 0; /* reset child array ser_osz */ 1886 | } 1887 | break; 1888 | 1889 | case TPL_TYPE_POUND: 1890 | /* we need to iterate n times over preceding nodes in S(...). 1891 | * we may be in the midst of an iteration each time or starting. */ 1892 | pd = (tpl_pound_data*)child->data; 1893 | itermax = child->num; 1894 | 1895 | /* itermax is total num of iterations needed */ 1896 | /* pd->iternum is current iteration index */ 1897 | /* pd->inter_elt_len is element-to-element len of contiguous structs */ 1898 | /* pd->iter_start_node is where we jump to at each iteration. */ 1899 | 1900 | if (++(pd->iternum) < itermax) { 1901 | 1902 | /* in start or midst of loop. advance addr/data pointers. */ 1903 | for(np=pd->iter_start_node; np != child; np = np->next) { 1904 | np->data = (char*)(np->data) + 1905 | (tpl_types[np->type].sz * np->num); 1906 | np->addr = (char*)(np->addr) + pd->inter_elt_len; 1907 | } 1908 | /* do next iteration */ 1909 | child = pd->iter_start_node; 1910 | continue; 1911 | 1912 | } else { /* loop complete. */ 1913 | 1914 | /* reset iteration index and addr/data pointers. */ 1915 | pd->iternum = 0; 1916 | for(np=pd->iter_start_node; np != child; np = np->next) { 1917 | np->data = (char*)(np->data) - ((itermax-1) * 1918 | tpl_types[np->type].sz * 1919 | np->num); 1920 | np->addr = (char*)(np->addr) - ((itermax-1) * pd->inter_elt_len); 1921 | } 1922 | 1923 | } 1924 | break; 1925 | default: 1926 | tpl_hook.fatal("unsupported format character\n"); 1927 | break; 1928 | } 1929 | child=child->next; 1930 | } 1931 | return 0; 1932 | } 1933 | 1934 | TPL_API int tpl_unpack(tpl_node *r, int i) { 1935 | tpl_node *n, *c, *np; 1936 | uint32_t slen; 1937 | int rc=1, fidx; 1938 | char *str; 1939 | void *dv=NULL, *caddr; 1940 | size_t A_bytes, itermax; 1941 | tpl_pound_data *pd; 1942 | void *img; 1943 | size_t sz; 1944 | 1945 | 1946 | /* handle unusual case of tpl_pack,tpl_unpack without an 1947 | * intervening tpl_dump. do a dump/load implicitly. */ 1948 | if (((tpl_root_data*)(r->data))->flags & TPL_WRONLY) { 1949 | if (tpl_dump(r,TPL_MEM,&img,&sz) != 0) return -1; 1950 | if (tpl_load(r,TPL_MEM|TPL_UFREE,img,sz) != 0) { 1951 | tpl_hook.free(img); 1952 | return -1; 1953 | }; 1954 | } 1955 | 1956 | n = tpl_find_i(r,i); 1957 | if (n == NULL) { 1958 | tpl_hook.oops("invalid index %d to tpl_unpack\n", i); 1959 | return -1; 1960 | } 1961 | 1962 | /* either root node or an A node */ 1963 | if (n->type == TPL_TYPE_ROOT) { 1964 | dv = tpl_find_data_start( ((tpl_root_data*)(n->data))->mmap.text ); 1965 | } else if (n->type == TPL_TYPE_ARY) { 1966 | if (((tpl_atyp*)(n->data))->num <= 0) return 0; /* array consumed */ 1967 | else rc = ((tpl_atyp*)(n->data))->num--; 1968 | dv = ((tpl_atyp*)(n->data))->cur; 1969 | if (!dv) tpl_hook.fatal("must unpack parent of node before node itself\n"); 1970 | } 1971 | 1972 | c = n->children; 1973 | while (c) { 1974 | switch (c->type) { 1975 | case TPL_TYPE_BYTE: 1976 | case TPL_TYPE_DOUBLE: 1977 | case TPL_TYPE_INT32: 1978 | case TPL_TYPE_UINT32: 1979 | case TPL_TYPE_INT64: 1980 | case TPL_TYPE_UINT64: 1981 | case TPL_TYPE_INT16: 1982 | case TPL_TYPE_UINT16: 1983 | /* unpack elements of cross-endian octothorpic array individually */ 1984 | if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN) { 1985 | for(fidx=0; fidx < c->num; fidx++) { 1986 | caddr = (void*)((uintptr_t)c->addr + (fidx * tpl_types[c->type].sz)); 1987 | memcpy(caddr,dv,tpl_types[c->type].sz); 1988 | tpl_byteswap(caddr, tpl_types[c->type].sz); 1989 | dv = (void*)((uintptr_t)dv + tpl_types[c->type].sz); 1990 | } 1991 | } else { 1992 | /* bulk unpack ok if not cross-endian */ 1993 | memcpy(c->addr, dv, tpl_types[c->type].sz * c->num); 1994 | dv = (void*)((uintptr_t)dv + tpl_types[c->type].sz * c->num); 1995 | } 1996 | break; 1997 | case TPL_TYPE_BIN: 1998 | memcpy(&slen,dv,sizeof(uint32_t)); 1999 | if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN) 2000 | tpl_byteswap(&slen, sizeof(uint32_t)); 2001 | if (slen > 0) { 2002 | str = (char*)tpl_hook.malloc(slen); 2003 | if (!str) fatal_oom(); 2004 | } else str=NULL; 2005 | dv = (void*)((uintptr_t)dv + sizeof(uint32_t)); 2006 | if (slen>0) memcpy(str,dv,slen); 2007 | memcpy(&(((tpl_bin*)c->addr)->addr),&str,sizeof(void*)); 2008 | memcpy(&(((tpl_bin*)c->addr)->sz),&slen,sizeof(uint32_t)); 2009 | dv = (void*)((uintptr_t)dv + slen); 2010 | break; 2011 | case TPL_TYPE_STR: 2012 | for(fidx=0; fidx < c->num; fidx++) { 2013 | memcpy(&slen,dv,sizeof(uint32_t)); 2014 | if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN) 2015 | tpl_byteswap(&slen, sizeof(uint32_t)); 2016 | if (((tpl_root_data*)(r->data))->flags & TPL_OLD_STRING_FMT) 2017 | slen += 1; 2018 | dv = (void*)((uintptr_t)dv + sizeof(uint32_t)); 2019 | if (slen) { /* slen includes \0 */ 2020 | str = (char*)tpl_hook.malloc(slen); 2021 | if (!str) fatal_oom(); 2022 | if (slen>1) memcpy(str,dv,slen-1); 2023 | str[slen-1] = '\0'; /* nul terminate */ 2024 | dv = (void*)((uintptr_t)dv + slen-1); 2025 | } else str=NULL; 2026 | memcpy(&((char**)c->addr)[fidx],&str,sizeof(char*)); 2027 | } 2028 | break; 2029 | case TPL_TYPE_POUND: 2030 | /* iterate over preceding nodes */ 2031 | pd = (tpl_pound_data*)c->data; 2032 | itermax = c->num; 2033 | if (++(pd->iternum) < itermax) { 2034 | /* in start or midst of loop. advance addr/data pointers. */ 2035 | for(np=pd->iter_start_node; np != c; np = np->next) { 2036 | np->addr = (char*)(np->addr) + pd->inter_elt_len; 2037 | } 2038 | /* do next iteration */ 2039 | c = pd->iter_start_node; 2040 | continue; 2041 | 2042 | } else { /* loop complete. */ 2043 | 2044 | /* reset iteration index and addr/data pointers. */ 2045 | pd->iternum = 0; 2046 | for(np=pd->iter_start_node; np != c; np = np->next) { 2047 | np->addr = (char*)(np->addr) - ((itermax-1) * pd->inter_elt_len); 2048 | } 2049 | 2050 | } 2051 | break; 2052 | case TPL_TYPE_ARY: 2053 | if (tpl_serlen(r,c,dv, &A_bytes) == -1) 2054 | tpl_hook.fatal("internal error in unpack\n"); 2055 | memcpy( &((tpl_atyp*)(c->data))->num, dv, sizeof(uint32_t)); 2056 | if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN) 2057 | tpl_byteswap(&((tpl_atyp*)(c->data))->num, sizeof(uint32_t)); 2058 | ((tpl_atyp*)(c->data))->cur = (void*)((uintptr_t)dv+sizeof(uint32_t)); 2059 | dv = (void*)((uintptr_t)dv + A_bytes); 2060 | break; 2061 | default: 2062 | tpl_hook.fatal("unsupported format character\n"); 2063 | break; 2064 | } 2065 | 2066 | c = c->next; 2067 | } 2068 | if (n->type == TPL_TYPE_ARY) ((tpl_atyp*)(n->data))->cur = dv; /* next element */ 2069 | return rc; 2070 | } 2071 | 2072 | /* Specialized function that unpacks only the root's A nodes, after tpl_load */ 2073 | static int tpl_unpackA0(tpl_node *r) { 2074 | tpl_node *n, *c; 2075 | uint32_t slen; 2076 | int rc=1,fidx,i; 2077 | void *dv; 2078 | size_t A_bytes, itermax; 2079 | tpl_pound_data *pd; 2080 | 2081 | n = r; 2082 | dv = tpl_find_data_start( ((tpl_root_data*)(r->data))->mmap.text); 2083 | 2084 | c=n->children; 2085 | while (c) { 2086 | switch (c->type) { 2087 | case TPL_TYPE_BYTE: 2088 | case TPL_TYPE_DOUBLE: 2089 | case TPL_TYPE_INT32: 2090 | case TPL_TYPE_UINT32: 2091 | case TPL_TYPE_INT64: 2092 | case TPL_TYPE_UINT64: 2093 | case TPL_TYPE_INT16: 2094 | case TPL_TYPE_UINT16: 2095 | for(fidx=0;fidx < c->num; fidx++) { 2096 | dv = (void*)((uintptr_t)dv + tpl_types[c->type].sz); 2097 | } 2098 | break; 2099 | case TPL_TYPE_BIN: 2100 | memcpy(&slen,dv,sizeof(uint32_t)); 2101 | if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN) 2102 | tpl_byteswap(&slen, sizeof(uint32_t)); 2103 | dv = (void*)((uintptr_t)dv + sizeof(uint32_t)); 2104 | dv = (void*)((uintptr_t)dv + slen); 2105 | break; 2106 | case TPL_TYPE_STR: 2107 | for(i=0; inum; i++) { 2108 | memcpy(&slen,dv,sizeof(uint32_t)); 2109 | if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN) 2110 | tpl_byteswap(&slen, sizeof(uint32_t)); 2111 | if (((tpl_root_data*)(r->data))->flags & TPL_OLD_STRING_FMT) 2112 | slen += 1; 2113 | dv = (void*)((uintptr_t)dv + sizeof(uint32_t)); 2114 | if (slen>1) dv = (void*)((uintptr_t)dv + slen-1); 2115 | } 2116 | break; 2117 | case TPL_TYPE_POUND: 2118 | /* iterate over the preceding nodes */ 2119 | itermax = c->num; 2120 | pd = (tpl_pound_data*)c->data; 2121 | if (++(pd->iternum) < itermax) { 2122 | c = pd->iter_start_node; 2123 | continue; 2124 | } else { /* loop complete. */ 2125 | pd->iternum = 0; 2126 | } 2127 | break; 2128 | case TPL_TYPE_ARY: 2129 | if ( tpl_serlen(r,c,dv, &A_bytes) == -1) 2130 | tpl_hook.fatal("internal error in unpackA0\n"); 2131 | memcpy( &((tpl_atyp*)(c->data))->num, dv, sizeof(uint32_t)); 2132 | if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN) 2133 | tpl_byteswap(&((tpl_atyp*)(c->data))->num, sizeof(uint32_t)); 2134 | ((tpl_atyp*)(c->data))->cur = (void*)((uintptr_t)dv+sizeof(uint32_t)); 2135 | dv = (void*)((uintptr_t)dv + A_bytes); 2136 | break; 2137 | default: 2138 | tpl_hook.fatal("unsupported format character\n"); 2139 | break; 2140 | } 2141 | c=c->next; 2142 | } 2143 | return rc; 2144 | } 2145 | 2146 | /* In-place byte order swapping of a word of length "len" bytes */ 2147 | static void tpl_byteswap(void *word, int len) { 2148 | int i; 2149 | char c, *w; 2150 | w = (char*)word; 2151 | for(i=0; i0) ? rc : 0; 2222 | } while ((rc==-1 && (errno==EINTR||errno==EAGAIN)) || (rc>0 && i<8)); 2223 | 2224 | if (rc<0) { 2225 | tpl_hook.oops("tpl_gather_fd_blocking failed: %s\n", strerror(errno)); 2226 | return -1; 2227 | } else if (rc == 0) { 2228 | /* tpl_hook.oops("tpl_gather_fd_blocking: eof\n"); */ 2229 | return 0; 2230 | } else if (i != 8) { 2231 | tpl_hook.oops("internal error\n"); 2232 | return -1; 2233 | } 2234 | 2235 | if (preamble[0] == 't' && preamble[1] == 'p' && preamble[2] == 'l') { 2236 | memcpy(&tpllen,&preamble[4],4); 2237 | if (tpl_needs_endian_swap(preamble)) tpl_byteswap(&tpllen,4); 2238 | } else { 2239 | tpl_hook.oops("tpl_gather_fd_blocking: non-tpl input\n"); 2240 | return -1; 2241 | } 2242 | 2243 | /* malloc space for remainder of tpl image (overall length tpllen) 2244 | * and read it in 2245 | */ 2246 | if (tpl_hook.gather_max > 0 && 2247 | tpllen > tpl_hook.gather_max) { 2248 | tpl_hook.oops("tpl exceeds max length %d\n", 2249 | tpl_hook.gather_max); 2250 | return -2; 2251 | } 2252 | *sz = tpllen; 2253 | if ( (*img = tpl_hook.malloc(tpllen)) == NULL) { 2254 | fatal_oom(); 2255 | } 2256 | 2257 | memcpy(*img,preamble,8); /* copy preamble to output buffer */ 2258 | i=8; 2259 | do { 2260 | rc = read(fd,&((*(char**)img)[i]),tpllen-i); 2261 | i += (rc>0) ? rc : 0; 2262 | } while ((rc==-1 && (errno==EINTR||errno==EAGAIN)) || (rc>0 && iimg); 2298 | tpl_hook.free(*gs); 2299 | *gs = NULL; 2300 | } 2301 | return -1; /* error, caller should close fd */ 2302 | } 2303 | } else if (rc == 0) { 2304 | if (*gs) { 2305 | tpl_hook.oops("tpl_gather: partial tpl image precedes EOF\n"); 2306 | tpl_hook.free((*gs)->img); 2307 | tpl_hook.free(*gs); 2308 | *gs = NULL; 2309 | } 2310 | return 0; /* EOF, caller should close fd */ 2311 | } else { 2312 | /* concatenate any partial tpl from last read with new buffer */ 2313 | if (*gs) { 2314 | catlen = (*gs)->len + rc; 2315 | if (tpl_hook.gather_max > 0 && 2316 | catlen > tpl_hook.gather_max) { 2317 | tpl_hook.free( (*gs)->img ); 2318 | tpl_hook.free( (*gs) ); 2319 | *gs = NULL; 2320 | tpl_hook.oops("tpl exceeds max length %d\n", 2321 | tpl_hook.gather_max); 2322 | return -2; /* error, caller should close fd */ 2323 | } 2324 | if ( (img = tpl_hook.realloc((*gs)->img, catlen)) == NULL) { 2325 | fatal_oom(); 2326 | } 2327 | memcpy(img + (*gs)->len, buf, rc); 2328 | tpl_hook.free(*gs); 2329 | *gs = NULL; 2330 | } else { 2331 | img = buf; 2332 | catlen = rc; 2333 | } 2334 | /* isolate any full tpl(s) in img and invoke cb for each */ 2335 | tpl = img; 2336 | keep_looping = (tpl+8 < img+catlen) ? 1 : 0; 2337 | while (keep_looping) { 2338 | if (strncmp("tpl", tpl, 3) != 0) { 2339 | tpl_hook.oops("tpl prefix invalid\n"); 2340 | if (img != buf) tpl_hook.free(img); 2341 | tpl_hook.free(*gs); 2342 | *gs = NULL; 2343 | return -3; /* error, caller should close fd */ 2344 | } 2345 | memcpy(&tpllen,&tpl[4],4); 2346 | if (tpl_needs_endian_swap(tpl)) tpl_byteswap(&tpllen,4); 2347 | if (tpl+tpllen <= img+catlen) { 2348 | cbrc = (cb)(tpl,tpllen,data); /* invoke cb for tpl image */ 2349 | tpl += tpllen; /* point to next tpl image */ 2350 | if (cbrc < 0) keep_looping = 0; 2351 | else keep_looping = (tpl+8 < img+catlen) ? 1 : 0; 2352 | } else keep_looping=0; 2353 | } 2354 | /* check if app callback requested closure of tpl source */ 2355 | if (cbrc < 0) { 2356 | tpl_hook.oops("tpl_fd_gather aborted by app callback\n"); 2357 | if (img != buf) tpl_hook.free(img); 2358 | if (*gs) tpl_hook.free(*gs); 2359 | *gs = NULL; 2360 | return -4; 2361 | } 2362 | /* store any leftover, partial tpl fragment for next read */ 2363 | if (tpl == img && img != buf) { 2364 | /* consumed nothing from img!=buf */ 2365 | if ( (*gs = tpl_hook.malloc(sizeof(tpl_gather_t))) == NULL ) { 2366 | fatal_oom(); 2367 | } 2368 | (*gs)->img = tpl; 2369 | (*gs)->len = catlen; 2370 | } else if (tpl < img+catlen) { 2371 | /* consumed 1+ tpl(s) from img!=buf or 0 from img==buf */ 2372 | if ( (*gs = tpl_hook.malloc(sizeof(tpl_gather_t))) == NULL ) { 2373 | fatal_oom(); 2374 | } 2375 | if ( ((*gs)->img = tpl_hook.malloc(img+catlen - tpl)) == NULL ) { 2376 | fatal_oom(); 2377 | } 2378 | (*gs)->len = img+catlen - tpl; 2379 | memcpy( (*gs)->img, tpl, img+catlen - tpl); 2380 | /* free partially consumed concat buffer if used */ 2381 | if (img != buf) tpl_hook.free(img); 2382 | } else { /* tpl(s) fully consumed */ 2383 | /* free consumed concat buffer if used */ 2384 | if (img != buf) tpl_hook.free(img); 2385 | } 2386 | } 2387 | } 2388 | } 2389 | 2390 | /* gather tpl piecemeal from memory buffer (not fd) e.g., from a lower-level api */ 2391 | static int tpl_gather_mem( char *buf, size_t len, tpl_gather_t **gs, tpl_gather_cb *cb, void *data) { 2392 | char *img, *tpl; 2393 | int keep_looping, cbrc=0; 2394 | size_t catlen; 2395 | uint32_t tpllen; 2396 | 2397 | /* concatenate any partial tpl from last read with new buffer */ 2398 | if (*gs) { 2399 | catlen = (*gs)->len + len; 2400 | if (tpl_hook.gather_max > 0 && 2401 | catlen > tpl_hook.gather_max) { 2402 | tpl_hook.free( (*gs)->img ); 2403 | tpl_hook.free( (*gs) ); 2404 | *gs = NULL; 2405 | tpl_hook.oops("tpl exceeds max length %d\n", 2406 | tpl_hook.gather_max); 2407 | return -2; /* error, caller should stop accepting input from source*/ 2408 | } 2409 | if ( (img = tpl_hook.realloc((*gs)->img, catlen)) == NULL) { 2410 | fatal_oom(); 2411 | } 2412 | memcpy(img + (*gs)->len, buf, len); 2413 | tpl_hook.free(*gs); 2414 | *gs = NULL; 2415 | } else { 2416 | img = buf; 2417 | catlen = len; 2418 | } 2419 | /* isolate any full tpl(s) in img and invoke cb for each */ 2420 | tpl = img; 2421 | keep_looping = (tpl+8 < img+catlen) ? 1 : 0; 2422 | while (keep_looping) { 2423 | if (strncmp("tpl", tpl, 3) != 0) { 2424 | tpl_hook.oops("tpl prefix invalid\n"); 2425 | if (img != buf) tpl_hook.free(img); 2426 | tpl_hook.free(*gs); 2427 | *gs = NULL; 2428 | return -3; /* error, caller should stop accepting input from source*/ 2429 | } 2430 | memcpy(&tpllen,&tpl[4],4); 2431 | if (tpl_needs_endian_swap(tpl)) tpl_byteswap(&tpllen,4); 2432 | if (tpl+tpllen <= img+catlen) { 2433 | cbrc = (cb)(tpl,tpllen,data); /* invoke cb for tpl image */ 2434 | tpl += tpllen; /* point to next tpl image */ 2435 | if (cbrc < 0) keep_looping = 0; 2436 | else keep_looping = (tpl+8 < img+catlen) ? 1 : 0; 2437 | } else keep_looping=0; 2438 | } 2439 | /* check if app callback requested closure of tpl source */ 2440 | if (cbrc < 0) { 2441 | tpl_hook.oops("tpl_mem_gather aborted by app callback\n"); 2442 | if (img != buf) tpl_hook.free(img); 2443 | if (*gs) tpl_hook.free(*gs); 2444 | *gs = NULL; 2445 | return -4; 2446 | } 2447 | /* store any leftover, partial tpl fragment for next read */ 2448 | if (tpl == img && img != buf) { 2449 | /* consumed nothing from img!=buf */ 2450 | if ( (*gs = tpl_hook.malloc(sizeof(tpl_gather_t))) == NULL ) { 2451 | fatal_oom(); 2452 | } 2453 | (*gs)->img = tpl; 2454 | (*gs)->len = catlen; 2455 | } else if (tpl < img+catlen) { 2456 | /* consumed 1+ tpl(s) from img!=buf or 0 from img==buf */ 2457 | if ( (*gs = tpl_hook.malloc(sizeof(tpl_gather_t))) == NULL ) { 2458 | fatal_oom(); 2459 | } 2460 | if ( ((*gs)->img = tpl_hook.malloc(img+catlen - tpl)) == NULL ) { 2461 | fatal_oom(); 2462 | } 2463 | (*gs)->len = img+catlen - tpl; 2464 | memcpy( (*gs)->img, tpl, img+catlen - tpl); 2465 | /* free partially consumed concat buffer if used */ 2466 | if (img != buf) tpl_hook.free(img); 2467 | } else { /* tpl(s) fully consumed */ 2468 | /* free consumed concat buffer if used */ 2469 | if (img != buf) tpl_hook.free(img); 2470 | } 2471 | return 1; 2472 | } 2473 | -------------------------------------------------------------------------------- /tpl.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2005-2010, Troy D. Hanson http://tpl.sourceforge.net 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 12 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 13 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 14 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 15 | OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 16 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 17 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 18 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 19 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 20 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 21 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | */ 23 | 24 | #ifndef TPL_H 25 | #define TPL_H 26 | 27 | #include /* size_t */ 28 | 29 | #ifdef __INTEL_COMPILER 30 | #include 31 | #endif /* Intel Compiler efficient memcpy etc */ 32 | 33 | #ifdef _MSC_VER 34 | typedef unsigned int uint32_t; 35 | #else 36 | #include /* uint32_t */ 37 | #endif 38 | 39 | #if defined __cplusplus 40 | extern "C" { 41 | #endif 42 | 43 | #ifdef _WIN32 44 | #ifdef TPL_EXPORTS 45 | #define TPL_API __declspec(dllexport) 46 | #else /* */ 47 | #ifdef TPL_NOLIB 48 | #define TPL_API 49 | #else 50 | #define TPL_API __declspec(dllimport) 51 | #endif /* TPL_NOLIB */ 52 | #endif /* TPL_EXPORTS*/ 53 | #else 54 | #define TPL_API 55 | #endif 56 | 57 | /* bit flags (external) */ 58 | #define TPL_FILE (1 << 0) 59 | #define TPL_MEM (1 << 1) 60 | #define TPL_PREALLOCD (1 << 2) 61 | #define TPL_EXCESS_OK (1 << 3) 62 | #define TPL_FD (1 << 4) 63 | #define TPL_UFREE (1 << 5) 64 | #define TPL_DATAPEEK (1 << 6) 65 | #define TPL_FXLENS (1 << 7) 66 | #define TPL_GETSIZE (1 << 8) 67 | /* do not add flags here without renumbering the internal flags! */ 68 | 69 | /* flags for tpl_gather mode */ 70 | #define TPL_GATHER_BLOCKING 1 71 | #define TPL_GATHER_NONBLOCKING 2 72 | #define TPL_GATHER_MEM 3 73 | 74 | /* Hooks for error logging, memory allocation functions and fatal */ 75 | typedef int (tpl_print_fcn)(const char *fmt, ...); 76 | typedef void *(tpl_malloc_fcn)(size_t sz); 77 | typedef void *(tpl_realloc_fcn)(void *ptr, size_t sz); 78 | typedef void (tpl_free_fcn)(void *ptr); 79 | typedef void (tpl_fatal_fcn)(char *fmt, ...); 80 | 81 | typedef struct tpl_hook_t { 82 | tpl_print_fcn *oops; 83 | tpl_malloc_fcn *malloc; 84 | tpl_realloc_fcn *realloc; 85 | tpl_free_fcn *free; 86 | tpl_fatal_fcn *fatal; 87 | size_t gather_max; 88 | } tpl_hook_t; 89 | 90 | typedef struct tpl_node { 91 | int type; 92 | void *addr; 93 | void *data; /* r:tpl_root_data*. A:tpl_atyp*. ow:szof type */ 94 | int num; /* length of type if its a C array */ 95 | size_t ser_osz; /* serialization output size for subtree */ 96 | struct tpl_node *children; /* my children; linked-list */ 97 | struct tpl_node *next,*prev; /* my siblings (next child of my parent) */ 98 | struct tpl_node *parent; /* my parent */ 99 | } tpl_node; 100 | 101 | /* used when un/packing 'B' type (binary buffers) */ 102 | typedef struct tpl_bin { 103 | void *addr; 104 | uint32_t sz; 105 | } tpl_bin; 106 | 107 | /* for async/piecemeal reading of tpl images */ 108 | typedef struct tpl_gather_t { 109 | char *img; 110 | int len; 111 | } tpl_gather_t; 112 | 113 | /* Callback used when tpl_gather has read a full tpl image */ 114 | typedef int (tpl_gather_cb)(void *img, size_t sz, void *data); 115 | 116 | /* Prototypes */ 117 | TPL_API tpl_node *tpl_map(char *fmt,...); /* define tpl using format */ 118 | TPL_API void tpl_free(tpl_node *r); /* free a tpl map */ 119 | TPL_API int tpl_pack(tpl_node *r, int i); /* pack the n'th packable */ 120 | TPL_API int tpl_unpack(tpl_node *r, int i); /* unpack the n'th packable */ 121 | TPL_API int tpl_dump(tpl_node *r, int mode, ...); /* serialize to mem/file */ 122 | TPL_API int tpl_load(tpl_node *r, int mode, ...); /* set mem/file to unpack */ 123 | TPL_API int tpl_Alen(tpl_node *r, int i); /* array len of packable i */ 124 | TPL_API char* tpl_peek(int mode, ...); /* sneak peek at format string */ 125 | TPL_API int tpl_gather( int mode, ...); /* non-blocking image gather */ 126 | TPL_API int tpl_jot(int mode, ...); /* quick write a simple tpl */ 127 | 128 | #if defined __cplusplus 129 | } 130 | #endif 131 | 132 | #endif /* TPL_H */ 133 | 134 | -------------------------------------------------------------------------------- /update.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | clang -o ccode -L$(llvm-config --libdir) -lclang client.c server.c misc.c main.c strstr.c tpl.c proto.c 3 | cp ccode ~/bin 4 | 5 | -------------------------------------------------------------------------------- /vim/plugin/ccode.vim: -------------------------------------------------------------------------------- 1 | if exists('g:loaded_ccode') 2 | finish 3 | endif 4 | let g:loaded_ccode = 1 5 | 6 | au FileType c,cpp,objc,objcpp call s:ccodeInit() 7 | 8 | fu! s:ccodeCurrentBuffer() 9 | let buf = getline(1, '$') 10 | let file = tempname() 11 | call writefile(buf, file) 12 | return file 13 | endf 14 | 15 | fu! s:system(str, ...) 16 | return (a:0 == 0 ? system(a:str) : system(a:str, join(a:000))) 17 | endf 18 | 19 | fu! s:ccodeCommand(cmd, args) 20 | for i in range(0, len(a:args) - 1) 21 | let a:args[i] = shellescape(a:args[i]) 22 | endfor 23 | let cmdstr = printf('ccode %s %s', a:cmd, join(a:args)) 24 | let result = s:system(cmdstr) 25 | if v:shell_error != 0 26 | return "[\"0\", []]" 27 | else 28 | return result 29 | endif 30 | endf 31 | 32 | fu! s:ccodeLine() 33 | return printf('%d', line('.')) 34 | endf 35 | 36 | fu! s:ccodeCol() 37 | return printf('%d', col('.')) 38 | endf 39 | 40 | fu! s:ccodeAutocomplete() 41 | let filename = s:ccodeCurrentBuffer() 42 | let result = s:ccodeCommand('ac', [bufname('%'), 43 | \ s:ccodeLine(), s:ccodeCol(), 44 | \ filename]) 45 | call delete(filename) 46 | return result 47 | endf 48 | 49 | fu! CCodeComplete(findstart, base) 50 | "findstart = 1 when we need to get the text length 51 | if a:findstart == 1 52 | execute "silent let g:ccode_completions = " . s:ccodeAutocomplete() 53 | return col('.') - g:ccode_completions[0] - 1 54 | "findstart = 0 when we need to return the list of completions 55 | else 56 | return g:ccode_completions[1] 57 | endif 58 | endf 59 | 60 | fu! s:ccodeInit() 61 | setlocal omnifunc=CCodeComplete 62 | endf 63 | -------------------------------------------------------------------------------- /vim/update.bash: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | mkdir -p ~/.vim/plugin 3 | cp plugin/ccode.vim ~/.vim/plugin 4 | --------------------------------------------------------------------------------