123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403 |
- /**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- #include <ctype.h>
- #include <errno.h>
- #include <limits.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #define MAX_LINE_LEN 16384
- #define IFNDEF "#ifndef"
- #define IFNDEF_LEN (sizeof(IFNDEF) - 1)
- enum parse_state {
- PARSE_IFNDEF = 0,
- PARSE_STRUCTS_AND_ENUMS,
- PARSE_MESSAGES,
- PARSE_DONE,
- };
- #define PROTOBUF_C_END_DECLS_STR "PROTOBUF_C_END_DECLS"
- static const char *PARSE_STATE_TERMINATORS[] = {
- "PROTOBUF_C_BEGIN_DECLS",
- "/* --- messages --- */",
- PROTOBUF_C_END_DECLS_STR
- };
- static const char *MESSAGE_SUFFIXES[] = {
- "__INIT",
- "__get_packed_size",
- "__pack",
- "__pack_to_buffer",
- "__unpack",
- "__free_unpacked",
- };
- #define NUM_MESSAGE_SUFFIXES \
- (sizeof(MESSAGE_SUFFIXES) / sizeof(MESSAGE_SUFFIXES[0]))
- static void add_word(char ***words, size_t *num_words, const char *word)
- {
- size_t new_num_words;
- char *nword;
- char **nwords;
- new_num_words = *num_words + 1;
- nword = strdup(word);
- if (!nword) {
- fprintf(stderr, "failed to allocate memory for %Zd words\n",
- new_num_words);
- exit(1);
- }
- nwords = realloc(*words, sizeof(char **) * new_num_words);
- if (!nwords) {
- fprintf(stderr, "failed to allocate memory for %Zd words\n",
- new_num_words);
- free(nword);
- exit(1);
- }
- nwords[new_num_words - 1] = nword;
- *num_words = new_num_words;
- *words = nwords;
- }
- static int has_suffix(const char *str, const char *suffix)
- {
- int str_len = strlen(str);
- int suffix_len = strlen(suffix);
- if (str_len < suffix_len)
- return 0;
- return strcmp(str + str_len - suffix_len, suffix) == 0;
- }
- static int has_message_suffix(const char *word)
- {
- size_t i = 0;
- for (i = 0; i < NUM_MESSAGE_SUFFIXES; i++) {
- if (has_suffix(word, MESSAGE_SUFFIXES[i]))
- return 1;
- }
- return 0;
- }
- static void add_words(char ***words, size_t *num_words,
- char *line, enum parse_state state)
- {
- char *word, *ptr = NULL;
- for (word = strtok_r(line, " ", &ptr); word;
- word = strtok_r(NULL, " ", &ptr)) {
- if (word[0] == '_')
- continue;
- if (!strstr(word, "__"))
- continue;
- if ((state == PARSE_MESSAGES) && (!has_message_suffix(word)))
- continue;
- add_word(words, num_words, word);
- }
- }
- static int compare_strings(const void *a, const void *b)
- {
- return strcmp(*(char * const*)a, *(char * const*)b);
- }
- static char *get_last_occurrence(char *haystack, const char *needle)
- {
- char *val = NULL, *nval;
- int needle_len = strlen(needle);
- while (1) {
- nval = strstr(haystack, needle);
- if (!nval)
- return val;
- val = nval + needle_len;
- haystack = nval + needle_len;
- }
- }
- static char *get_second_last_occurrence(char *haystack, const char *needle)
- {
- char *pval = NULL, *val = NULL, *nval;
- int needle_len = strlen(needle);
- while (1) {
- nval = strstr(haystack, needle);
- if (!nval)
- return pval;
- pval = val;
- val = nval + needle_len;
- haystack = nval + needle_len;
- }
- }
- static int has_camel_case(const char *str)
- {
- int i, prev_lower = 0;
- for (i = 0; str[i]; i++) {
- if (isupper(str[i])) {
- if (prev_lower)
- return 1;
- } else if (islower(str[i])) {
- prev_lower = 1;
- }
- }
- return 0;
- }
- static char *get_shortened_occurrence(char *str)
- {
- char *last, *slast;
-
- last = get_last_occurrence(str, "__");
- slast = get_second_last_occurrence(str, "__");
- last = get_last_occurrence(str, "__");
- if (!last)
- return NULL;
- if ((!has_message_suffix(str)) &&
- (strstr(last, "_") || has_camel_case(last))) {
- // Heuristic: if the last bit of the string after the double underscore
- // has another underscore inside, or has mixed case, we assume it's
- // complex enough to use on its own.
- return last;
- }
- // Otherwise, we grab the part of the string after the second-last double
- // underscore.
- slast = get_second_last_occurrence(str, "__");
- return slast ? slast : last;
- }
- static int output_shortening_macros(char **words, size_t num_words,
- const char *out_path, FILE *out)
- {
- size_t i;
- const char *prev_word = "";
- const char *shortened;
- for (i = 0; i < num_words; i++) {
- if (strcmp(prev_word, words[i]) == 0) {
- // skip words we've already done
- continue;
- }
- prev_word = words[i];
- shortened = get_shortened_occurrence(words[i]);
- if (shortened) {
- if (fprintf(out, "#define %s %s\n", shortened, words[i]) < 0) {
- fprintf(stderr, "error writing to %s\n", out_path);
- return EIO;
- }
- }
- }
- return 0;
- }
- /**
- * Remove newlines from a buffer.
- *
- * @param line The buffer.
- */
- static void chomp(char *line)
- {
- while (1) {
- int len = strlen(line);
- if (len == 0) {
- return;
- }
- if (line[len - 1] != '\n') {
- return;
- }
- line[len - 1] = '\0';
- }
- }
- /**
- * Remove most non-alphanumeric characters from a buffer.
- *
- * @param line The buffer.
- */
- static void asciify(char *line)
- {
- int i;
- for (i = 0; line[i]; i++) {
- if ((!isalnum(line[i])) && (line[i] != '_') && (line[i] != '#')) {
- line[i] = ' ';
- }
- }
- }
- static const char *base_name(const char *path)
- {
- const char *base;
-
- base = rindex(path, '/');
- if (!base)
- return NULL;
- return base + 1;
- }
- static int process_file_lines(const char *in_path, const char *out_path,
- FILE *in, FILE *out, char ***words, size_t *num_words)
- {
- int ret;
- char header_guard[MAX_LINE_LEN] = { 0 };
- char line[MAX_LINE_LEN] = { 0 };
- const char *base = base_name(in_path);
- enum parse_state state = PARSE_IFNDEF;
- if (!base) {
- fprintf(stderr, "failed to get basename of %s\n", in_path);
- return EINVAL;
- }
- while (1) {
- if (!fgets(line, MAX_LINE_LEN - 1, in)) {
- if (ferror(in)) {
- ret = errno;
- fprintf(stderr, "error reading %s: %s (%d)\n",
- in_path, strerror(ret), ret);
- return ret;
- }
- fprintf(stderr, "error reading %s: didn't find "
- PROTOBUF_C_END_DECLS_STR, in_path);
- return EINVAL;
- }
- if (strstr(line, PARSE_STATE_TERMINATORS[state])) {
- state = state + 1;
- if (state == PARSE_DONE) {
- break;
- }
- continue;
- }
- chomp(line);
- asciify(line);
- switch (state) {
- case PARSE_IFNDEF:
- if (strncmp(line, IFNDEF, IFNDEF_LEN) == 0) {
- strcpy(header_guard, line + IFNDEF_LEN + 1);
- }
- break;
- default:
- add_words(words, num_words, line, state);
- break;
- }
- }
- if (!header_guard[0]) {
- fprintf(stderr, "failed to find header guard for %s\n", in_path);
- return EINVAL;
- }
- qsort(*words, *num_words, sizeof(char*), compare_strings);
- fprintf(out, "#ifndef %s_S\n", header_guard);
- fprintf(out, "#define %s_S\n\n", header_guard);
- fprintf(out, "#include \"%s\"\n\n", base);
- ret = output_shortening_macros(*words, *num_words, out_path, out);
- if (ret)
- return ret;
- fprintf(out, "\n#endif\n");
- return 0;
- }
- static int process_file(const char *in_path)
- {
- char out_path[PATH_MAX] = { 0 };
- int res, ret = 0;
- FILE *in = NULL, *out = NULL;
- char **words = NULL;
- size_t num_words = 0;
- size_t i;
- res = snprintf(out_path, PATH_MAX, "%s.s", in_path);
- if ((res < 0) || (res >= PATH_MAX)) {
- fprintf(stderr, "snprintf error for %s\n", in_path);
- ret = EINVAL;
- goto done;
- }
- in = fopen(in_path, "r");
- if (!in) {
- ret = errno;
- fprintf(stderr, "failed to open %s for read: error %s (%d)\n",
- in_path, strerror(ret), ret);
- goto done;
- }
- out = fopen(out_path, "w");
- if (!out) {
- ret = errno;
- fprintf(stderr, "failed to open %s for write: error %s (%d)\n",
- out_path, strerror(ret), ret);
- goto done;
- }
- ret = process_file_lines(in_path, out_path, in, out, &words, &num_words);
- for (i = 0; i < num_words; i++) {
- free(words[i]);
- }
- free(words);
- if (ret) {
- goto done;
- }
- if (fclose(out)) {
- ret = errno;
- perror("fclose error");
- }
- out = NULL;
- done:
- if (in) {
- fclose(in);
- }
- if (out) {
- fclose(out);
- }
- return ret;
- }
- static void usage(void)
- {
- fprintf(stderr,
- "shorten: creates header files with shorter definitions for protobuf-c\n"
- "definitions. Output files will be written to the same paths as input\n"
- "files, but with a .s extension tacked on.\n"
- "\n"
- "usage: shorten [paths-to-headers]\n");
- }
- int main(int argc, char **argv)
- {
- int i, ret, nproc = 0, rval = EXIT_SUCCESS;
- if (argc < 2) {
- usage();
- exit(EXIT_SUCCESS);
- }
- for (i = 1; i < argc; i++) {
- ret = process_file(argv[i]);
- if (ret) {
- fprintf(stderr, "error processing %s\n", argv[i]);
- rval = EXIT_FAILURE;
- } else {
- nproc++;
- }
- }
- //fprintf(stderr, "successfully processed %d files\n", nproc);
- return rval;
- }
- // vim: ts=4:sw=4:tw=79:et
|