/*========================================================================= Program: Visualization Toolkit Module: vtkWrapHierarchy.c Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. See Copyright.txt or http://www.kitware.com/Copyright.htm for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notice for more information. =========================================================================*/ /*------------------------------------------------------------------------- Copyright (c) 2010 David Gobbi. Contributed to the VisualizationToolkit by the author in June 2010 under the terms of the Visualization Toolkit 2008 copyright. -------------------------------------------------------------------------*/ /** The vtkWrapHierarchy program builds a text file that describes the class hierarchy. For each class, the output file will have a line in the following format: classname [ : superclass ] ; header.h ; kit [; flags] For each enum type, enumname : enum ; header.h ; kit [; flags] For each typedef, name = [2][3]* const int ; header.h ; kit [; flags] */ #include "vtkParse.h" #include "vtkParseData.h" #include "vtkParseExtras.h" #include "vtkParseMain.h" #include "vtkParsePreprocess.h" #include "vtkParseSystem.h" #include #include #include #include #ifdef _WIN32 #include #else #include #endif /** * Helper to append a text line to an array of lines */ static char** append_unique_line(char** lines, char* line, size_t* np) { size_t l, n, m; n = *np; m = vtkParse_NameLength(line); /* check to make sure this line isn't a duplicate */ for (l = 0; l < n; l++) { if (vtkParse_NameLength(lines[l]) == m && strncmp(line, lines[l], m) == 0) { break; } } if (l == n) { /* allocate more memory if n+1 is a power of two */ if (((n + 1) & n) == 0) { lines = (char**)realloc(lines, (n + 1) * 2 * sizeof(char*)); } lines[n] = (char*)malloc(strlen(line) + 1); strcpy(lines[n++], line); lines[n] = NULL; } *np = n; return lines; } /** * Helper to append to a line, given the end marker */ static char* append_to_line(char* line, const char* text, size_t* pos, size_t* maxlen) { size_t n; n = strlen(text); if ((*pos) + n + 1 > (*maxlen)) { *maxlen = ((*pos) + n + 1 + 2 * (*maxlen)); line = (char*)realloc(line, (*maxlen)); } strcpy(&line[*pos], text); *pos = (*pos) + n; return line; } /** * Append scope to line */ static char* append_scope_to_line(char* line, size_t* m, size_t* maxlen, const char* scope) { if (scope && scope[0] != '\0') { line = append_to_line(line, scope, m, maxlen); line = append_to_line(line, "::", m, maxlen); } return line; } /** * Append template info */ static char* append_template_to_line( char* line, size_t* m, size_t* maxlen, TemplateInfo* template_args) { ValueInfo* arg; int j; line = append_to_line(line, "<", m, maxlen); for (j = 0; j < template_args->NumberOfParameters; j++) { arg = template_args->Parameters[j]; if (arg->Name) { line = append_to_line(line, arg->Name, m, maxlen); } if (arg->Value && arg->Value[0] != '\n') { line = append_to_line(line, "=", m, maxlen); line = append_to_line(line, arg->Value, m, maxlen); } if (j + 1 < template_args->NumberOfParameters) { line = append_to_line(line, ",", m, maxlen); } } line = append_to_line(line, ">", m, maxlen); return line; } /** * Append class info */ static char* append_class_to_line(char* line, size_t* m, size_t* maxlen, ClassInfo* class_info) { int j; line = append_to_line(line, class_info->Name, m, maxlen); if (class_info->Template) { line = append_template_to_line(line, m, maxlen, class_info->Template); } line = append_to_line(line, " ", m, maxlen); if (class_info->NumberOfSuperClasses) { line = append_to_line(line, ": ", m, maxlen); } for (j = 0; j < class_info->NumberOfSuperClasses; j++) { line = append_to_line(line, class_info->SuperClasses[j], m, maxlen); line = append_to_line(line, " ", m, maxlen); if (j + 1 < class_info->NumberOfSuperClasses) { line = append_to_line(line, ", ", m, maxlen); } } return line; } /** * Append enum info */ static char* append_enum_to_line(char* line, size_t* m, size_t* maxlen, EnumInfo* enum_info) { line = append_to_line(line, enum_info->Name, m, maxlen); line = append_to_line(line, " : enum ", m, maxlen); return line; } /** * Append the trailer, i.e. the filename and flags */ static char* append_trailer(char* line, size_t* m, size_t* maxlen, const char* header_file, const char* module_name, const char* flags) { line = append_to_line(line, "; ", m, maxlen); line = append_to_line(line, header_file, m, maxlen); line = append_to_line(line, " ; ", m, maxlen); line = append_to_line(line, module_name, m, maxlen); if (flags && flags[0] != '\0') { line = append_to_line(line, " ; ", m, maxlen); line = append_to_line(line, flags, m, maxlen); } return line; } /** * Append typedef info */ static char* append_typedef_to_line(char* line, size_t* m, size_t* maxlen, ValueInfo* typedef_info) { unsigned int type; int ndims; int dim; line = append_to_line(line, typedef_info->Name, m, maxlen); line = append_to_line(line, " = ", m, maxlen); type = typedef_info->Type; if ((type & VTK_PARSE_REF) != 0) { line = append_to_line(line, "&", m, maxlen); } ndims = typedef_info->NumberOfDimensions; for (dim = 0; dim < ndims; dim++) { line = append_to_line(line, "[", m, maxlen); line = append_to_line(line, typedef_info->Dimensions[dim], m, maxlen); line = append_to_line(line, "]", m, maxlen); } type = (type & VTK_PARSE_POINTER_MASK); if (ndims > 0 && (type & VTK_PARSE_POINTER_LOWMASK) == VTK_PARSE_ARRAY) { type = ((type >> 2) & VTK_PARSE_POINTER_MASK); } else if (ndims == 1) { type = ((type >> 2) & VTK_PARSE_POINTER_MASK); } /* pointers are printed after brackets, and are intentionally * printed in reverse order as compared to C++ declarations */ while (type) { unsigned int bits = (type & VTK_PARSE_POINTER_LOWMASK); type = ((type >> 2) & VTK_PARSE_POINTER_MASK); if (bits == VTK_PARSE_POINTER) { line = append_to_line(line, "*", m, maxlen); } else if (bits == VTK_PARSE_CONST_POINTER) { line = append_to_line(line, "const*", m, maxlen); } else { line = append_to_line(line, "[]", m, maxlen); } } if (line[*m - 1] != ' ') { line = append_to_line(line, " ", m, maxlen); } if ((type & VTK_PARSE_CONST) != 0) { line = append_to_line(line, "const ", m, maxlen); } line = append_to_line(line, typedef_info->Class, m, maxlen); line = append_to_line(line, " ", m, maxlen); return line; } /** * Append all types in a class */ static char** append_class_contents(char** lines, size_t* np, ClassInfo* data, const char* scope, const char* header_file, const char* module_name) { int i; const char* tmpflags; char* new_scope; char* line; size_t m, n, maxlen; size_t scope_m, scope_maxlen; /* append the name to the scope */ new_scope = 0; n = 0; m = 0; if (scope) { n = strlen(scope); } if (data->Name) { m = strlen(data->Name); } if (m && (n || data->Template)) { scope_maxlen = n + m + 3; scope_m = 0; new_scope = (char*)malloc(scope_maxlen); new_scope[0] = '\0'; if (n) { new_scope = append_to_line(new_scope, scope, &scope_m, &scope_maxlen); new_scope = append_to_line(new_scope, "::", &scope_m, &scope_maxlen); } new_scope = append_to_line(new_scope, data->Name, &scope_m, &scope_maxlen); if (data->Template) { new_scope = append_template_to_line(new_scope, &scope_m, &scope_maxlen, data->Template); } scope = new_scope; } else if (m) { scope = data->Name; } /* start with a buffer of 15 chars and grow from there */ maxlen = 15; m = 0; line = (char*)malloc(maxlen); /* add a line for each type that is found */ for (i = 0; i < data->NumberOfItems; i++) { m = 0; line[m] = '\0'; tmpflags = 0; if (data->Items[i].Type == VTK_CLASS_INFO || data->Items[i].Type == VTK_STRUCT_INFO) { ClassInfo* class_info = data->Classes[data->Items[i].Index]; line = append_scope_to_line(line, &m, &maxlen, scope); line = append_class_to_line(line, &m, &maxlen, class_info); /* force exclusion of nested classes, for now */ tmpflags = "WRAPEXCLUDE"; } else if (data->Items[i].Type == VTK_ENUM_INFO) { line = append_scope_to_line(line, &m, &maxlen, scope); line = append_enum_to_line(line, &m, &maxlen, data->Enums[data->Items[i].Index]); if (data->Enums[data->Items[i].Index]->IsExcluded || new_scope) { tmpflags = "WRAPEXCLUDE"; } } else if (data->Items[i].Type == VTK_TYPEDEF_INFO) { line = append_scope_to_line(line, &m, &maxlen, scope); line = append_typedef_to_line(line, &m, &maxlen, data->Typedefs[data->Items[i].Index]); } else { /* unhandled file element */ continue; } /* append filename and flags */ line = append_trailer(line, &m, &maxlen, header_file, module_name, tmpflags); /* append the line to the file */ lines = append_unique_line(lines, line, np); /* for classes, add all types defined within the class */ if ((data->Items[i].Type == VTK_CLASS_INFO || data->Items[i].Type == VTK_STRUCT_INFO) && data->Classes[data->Items[i].Index]->Name) { lines = append_class_contents( lines, np, data->Classes[data->Items[i].Index], scope, header_file, module_name); } } free(line); if (new_scope != 0) { free(new_scope); } return lines; } /** * Append all types in a namespace */ static char** append_namespace_contents(char** lines, size_t* np, NamespaceInfo* data, const char* scope, const char* header_file, const char* module_name, const char* flags) { int i; const char* tmpflags; char* line; char* new_scope; size_t n, m, maxlen; /* append the name to the scope */ new_scope = 0; n = 0; m = 0; if (scope) { n = strlen(scope); } if (data->Name) { m = strlen(data->Name); } if (m && n) { new_scope = (char*)malloc(m + n + 3); snprintf(new_scope, m + n + 3, "%s::%s", scope, data->Name); scope = new_scope; } else if (m) { scope = data->Name; } /* start with a buffer of 15 chars and grow from there */ maxlen = 15; m = 0; line = (char*)malloc(maxlen); /* add a line for each type that is found */ for (i = 0; i < data->NumberOfItems; i++) { tmpflags = flags; m = 0; line[m] = '\0'; if (data->Items[i].Type == VTK_CLASS_INFO || data->Items[i].Type == VTK_STRUCT_INFO) { ClassInfo* class_info = data->Classes[data->Items[i].Index]; if (class_info->IsExcluded || scope) { tmpflags = "WRAPEXCLUDE"; } line = append_scope_to_line(line, &m, &maxlen, scope); line = append_class_to_line(line, &m, &maxlen, class_info); } else if (data->Items[i].Type == VTK_ENUM_INFO) { EnumInfo* enum_info = data->Enums[data->Items[i].Index]; if (enum_info->IsExcluded || new_scope) { tmpflags = "WRAPEXCLUDE"; } line = append_scope_to_line(line, &m, &maxlen, scope); line = append_enum_to_line(line, &m, &maxlen, enum_info); } else if (data->Items[i].Type == VTK_TYPEDEF_INFO) { line = append_scope_to_line(line, &m, &maxlen, scope); line = append_typedef_to_line(line, &m, &maxlen, data->Typedefs[data->Items[i].Index]); } else if (data->Items[i].Type != VTK_NAMESPACE_INFO) { /* unhandled file element */ continue; } if (data->Items[i].Type != VTK_NAMESPACE_INFO) { /* append filename and flags */ line = append_trailer(line, &m, &maxlen, header_file, module_name, tmpflags); /* append the line to the file */ lines = append_unique_line(lines, line, np); } /* for classes, add all typed defined within the class */ if ((data->Items[i].Type == VTK_CLASS_INFO || data->Items[i].Type == VTK_STRUCT_INFO) && data->Classes[data->Items[i].Index]->Name) { lines = append_class_contents( lines, np, data->Classes[data->Items[i].Index], scope, header_file, module_name); } /* for namespaces, add all types in the namespace */ if (data->Items[i].Type == VTK_NAMESPACE_INFO && data->Namespaces[data->Items[i].Index]->Name) { lines = append_namespace_contents( lines, np, data->Namespaces[data->Items[i].Index], scope, header_file, module_name, flags); } } free(line); if (new_scope != 0) { free(new_scope); } return lines; } /** * Read a header file with vtkParse.tab.c * * If "lines" is provided, the file contents * will be appended to them. */ static char** vtkWrapHierarchy_ParseHeaderFile( FILE* fp, const char* filename, const char* module_name, const char* flags, char** lines) { FileInfo* data; const char* header_file; size_t k, n; /* start with just a single output line and grow from there */ if (lines == NULL) { lines = (char**)malloc(sizeof(char*)); lines[0] = NULL; } /* the "concrete" flag doesn't matter, just set to zero */ data = vtkParse_ParseFile(filename, fp, stderr); if (!data) { free(lines); return 0; } /* find the last line in "lines" */ n = 0; while (lines[n] != NULL) { n++; } k = strlen(data->FileName) - 1; while (k > 0 && data->FileName[k - 1] != '/' && data->FileName[k - 1] != '\\') { k--; } header_file = &data->FileName[k]; /* append the file contents to the output */ lines = append_namespace_contents(lines, &n, data->Contents, 0, header_file, module_name, flags); vtkParse_Free(data); return lines; } /** * Read a hierarchy file into "lines" without duplicating lines */ static char** vtkWrapHierarchy_ReadHierarchyFile(FILE* fp, char** lines) { char* line; size_t maxlen = 15; size_t i, n; line = (char*)malloc(maxlen); if (lines == NULL) { lines = (char**)malloc(sizeof(char*)); lines[0] = NULL; } while (fgets(line, (int)maxlen, fp)) { n = strlen(line); /* if buffer not long enough, increase it */ while (n == maxlen - 1 && line[n - 1] != '\n' && !feof(fp)) { char* oldline = line; maxlen *= 2; line = (char*)realloc(line, maxlen); if (!line) { free(oldline); return NULL; } if (!fgets(&line[n], (int)(maxlen - n), fp)) { break; } n += strlen(&line[n]); } while (n > 0 && isspace(line[n - 1])) { n--; } line[n] = '\0'; if (line[0] == '\0') { continue; } for (i = 0; lines[i] != NULL; i++) { if (strcmp(line, lines[i]) == 0) { break; } } if (lines[i] == NULL) { /* allocate more memory if n+1 is a power of two */ if (((i + 1) & i) == 0) { lines = (char**)realloc(lines, (i + 1) * 2 * sizeof(char*)); } lines[i] = (char*)malloc(n + 1); strcpy(lines[i], line); lines[i + 1] = NULL; } } free(line); if (!feof(fp)) { free(lines); return 0; } return lines; } /** * Compare a file to "lines", return 0 if they are different */ static int vtkWrapHierarchy_CompareHierarchyFile(FILE* fp, char* lines[]) { unsigned char* matched; char* line; size_t maxlen = 15; size_t i, n; line = (char*)malloc(maxlen); for (i = 0; lines[i] != NULL; i++) { } matched = (unsigned char*)malloc(i); memset(matched, 0, i); while (fgets(line, (int)maxlen, fp)) { n = strlen(line); /* if buffer not long enough, increase it */ while (n == maxlen - 1 && line[n - 1] != '\n' && !feof(fp)) { char* oldline = line; maxlen *= 2; line = (char*)realloc(line, maxlen); if (!line) { free(oldline); free(matched); return 0; } if (!fgets(&line[n], (int)(maxlen - n), fp)) { break; } n += strlen(&line[n]); } while (n > 0 && isspace(line[n - 1])) { n--; } line[n] = '\0'; if (line[0] == '\0') { continue; } for (i = 0; lines[i] != NULL; i++) { if (strcmp(line, lines[i]) == 0) { break; } } if (lines[i] == NULL) { free(line); free(matched); return 0; } matched[i] = 1; } for (i = 0; lines[i] != NULL; i++) { if (matched[i] == 0) { free(line); free(matched); return 0; } } free(line); free(matched); if (!feof(fp)) { return 0; } return 1; } /** * Write "lines" to a hierarchy file */ static int vtkWrapHierarchy_WriteHierarchyFile(FILE* fp, char* lines[]) { size_t i; for (i = 0; lines[i] != NULL; i++) { if (fprintf(fp, "%s\n", lines[i]) < 0) { return 0; } } return 1; } /** * Try to parse a header file, print error and exit if fail */ static char** vtkWrapHierarchy_TryParseHeaderFile( const char* file_name, const char* module_name, const char* flags, char** lines) { FILE* input_file; input_file = vtkParse_FileOpen(file_name, "r"); if (!input_file) { fprintf(stderr, "vtkWrapHierarchy: couldn't open file %s\n", file_name); exit(1); } lines = vtkWrapHierarchy_ParseHeaderFile(input_file, file_name, module_name, flags, lines); if (!lines) { fclose(input_file); exit(1); } fclose(input_file); return lines; } /** * Try to read a file, print error and exit if fail */ static char** vtkWrapHierarchy_TryReadHierarchyFile(const char* file_name, char** lines) { FILE* input_file; input_file = vtkParse_FileOpen(file_name, "r"); if (!input_file) { fprintf(stderr, "vtkWrapHierarchy: couldn't open file %s\n", file_name); exit(1); } lines = vtkWrapHierarchy_ReadHierarchyFile(input_file, lines); if (!lines) { fclose(input_file); fprintf(stderr, "vtkWrapHierarchy: error reading file %s\n", file_name); exit(1); } fclose(input_file); return lines; } /** * Try to write a file, print error and exit if fail */ static int vtkWrapHierarchy_TryWriteHierarchyFile(const char* file_name, char* lines[]) { FILE* output_file; int matched = 0; output_file = vtkParse_FileOpen(file_name, "r"); if (output_file && vtkWrapHierarchy_CompareHierarchyFile(output_file, lines)) { matched = 1; } if (output_file) { fclose(output_file); } if (!matched) { int tries = 1; output_file = vtkParse_FileOpen(file_name, "w"); while (!output_file && tries < 5) { /* There are two CMAKE_CUSTOM_COMMANDS for vtkWrapHierarchy, * make sure they do not collide. */ tries++; #ifdef _WIN32 Sleep(1000); #else sleep(1); #endif output_file = vtkParse_FileOpen(file_name, "r+"); if (output_file && vtkWrapHierarchy_CompareHierarchyFile(output_file, lines)) { /* if the contents match, no need to write it */ fclose(output_file); return 0; } if (output_file) { /* close and open in order to truncate the file */ fclose(output_file); output_file = vtkParse_FileOpen(file_name, "w"); } } if (!output_file) { fprintf(stderr, "vtkWrapHierarchy: tried %i times to write %s\n", tries, file_name); exit(1); } if (!vtkWrapHierarchy_WriteHierarchyFile(output_file, lines)) { fclose(output_file); fprintf(stderr, "vtkWrapHierarchy: error writing file %s\n", file_name); exit(1); } fclose(output_file); } return 0; } static int string_compare(const void* vp1, const void* vp2) { return strcmp(*(const char**)vp1, *(const char**)vp2); } int VTK_PARSE_MAIN(int argc, char* argv[]) { OptionInfo* options; int i; size_t j, n; char** lines = 0; char** files = 0; char* flags; char* module_name; StringCache* string_cache; /* pre-define a macro to identify the language */ vtkParse_DefineMacro("__VTK_WRAP_HIERARCHY__", 0); /* parse command-line options */ string_cache = vtkParse_MainMulti(argc, argv); options = vtkParse_GetCommandLineOptions(); /* make sure than an output file was given on the command line */ if (options->OutputFileName == NULL) { fprintf(stderr, "No output file was specified\n"); exit(1); } /* read the data file */ files = vtkWrapHierarchy_TryReadHierarchyFile(options->InputFileName, files); /* read in all the prior files */ for (i = 1; i < options->NumberOfFiles; i++) { lines = vtkWrapHierarchy_TryReadHierarchyFile(options->Files[i], lines); } /* merge the files listed in the data file */ for (i = 0; files[i] != NULL; i++) { /* look for semicolon that marks the module name */ module_name = files[i]; while (*module_name != ';' && *module_name != '\0') { module_name++; } if (*module_name == ';') { *module_name++ = '\0'; } /* look for semicolon that marks start of flags */ flags = module_name; while (*flags != ';' && *flags != '\0') { flags++; } if (*flags == ';') { *flags++ = '\0'; } lines = vtkWrapHierarchy_TryParseHeaderFile(files[i], module_name, flags, lines); } /* sort the lines to ease lookups in the file */ for (n = 0; lines[n]; n++) { } qsort(lines, n, sizeof(char*), &string_compare); /* write the file, if it has changed */ vtkWrapHierarchy_TryWriteHierarchyFile(options->OutputFileName, lines); for (j = 0; j < n; j++) { free(lines[j]); } for (j = 0; files[j] != NULL; j++) { free(files[j]); } vtkParse_FreeStringCache(string_cache); free(string_cache); free(files); free(lines); return 0; }