reactos/sdk/tools/rgenstat/rgenstat.c

923 lines
19 KiB
C

/*
* Generate a file with API status information from a list
* of files in a directory.
* Casper S. Hornstrup <chorns@users.sourceforge.net>
*/
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <string.h>
#ifdef WIN32
#include <io.h>
#include <dos.h>
#include <windows.h>
int __cdecl strcasecmp (const char * __sz1, const char * __sz2)
{return _stricmp (__sz1, __sz2);}
#else
#if !defined(__FreeBSD__) && !defined(__APPLE__)
#include <sys/io.h>
#endif
#include <errno.h>
#include <sys/types.h>
#include <dirent.h>
#include <unistd.h>
#endif
#include <ctype.h>
#ifndef WIN32
#ifndef MAX_PATH
#define MAX_PATH 260
#endif
#define DIR_SEPARATOR_CHAR '/'
#define DIR_SEPARATOR_STRING "/"
#else
#define DIR_SEPARATOR_CHAR '\\'
#define DIR_SEPARATOR_STRING "\\"
#endif
#define TAG_UNKNOWN -1
#define TAG_IMPLEMENTED 0
#define TAG_UNIMPLEMENTED 1
typedef struct _API_INFO
{
struct _API_INFO *next;
int tag_id;
char name[100];
char filename[MAX_PATH];
} API_INFO, *PAPI_INFO;
PAPI_INFO sort_linked_list(PAPI_INFO,
unsigned, int (*)(PAPI_INFO, PAPI_INFO));
static FILE *in;
static FILE *out;
static FILE *file_handle = NULL;
static char *file_buffer = NULL;
static unsigned int file_size = 0;
static unsigned int file_pointer = 0;
static char tagname[200];
static PAPI_INFO api_info_list = NULL;
static char*
convert_path(char* origpath)
{
char* newpath;
int i;
newpath = strdup(origpath);
i = 0;
while (newpath[i] != 0)
{
#ifndef WIN32
if (newpath[i] == '\\')
{
newpath[i] = '/';
}
#else
#ifdef WIN32
if (newpath[i] == '/')
{
newpath[i] = '\\';
}
#endif
#endif
i++;
}
return(newpath);
}
static char*
path_to_url(char* path)
{
int i;
i = 0;
while (path[i] != 0)
{
if (path[i] == '\\')
{
path[i] = '/';
}
i++;
}
return(path);
}
static void
write_line(char *line)
{
char buf[200];
memset(buf, 0, sizeof(buf));
strcpy(buf, line);
/* Terminate the line */
buf[strlen(buf)] = '\r';
buf[strlen(buf)] = '\n';
(void)fwrite(&buf[0], 1, strlen(buf), out);
}
static void
read_file(char *filename)
{
file_handle = fopen(filename, "rb");
if (file_handle == NULL)
{
printf("Can't open %s\n", filename);
exit(1);
}
// Get the size of the file
fseek(file_handle, 0, SEEK_END);
file_size = ftell(file_handle);
// Load it all into memory
file_buffer = malloc(file_size);
if (file_buffer == NULL)
{
fclose(file_handle);
printf("Out of memory\n");
exit(1);
}
fseek(file_handle, 0, SEEK_SET);
if (file_size > 0)
{
if (fread (file_buffer, 1, file_size, file_handle) < 1)
{
fclose(file_handle);
printf("Read error in file %s\n", filename);
exit(1);
}
}
file_pointer = 0;
}
static void
close_file()
{
free(file_buffer);
file_buffer = NULL;
fclose(file_handle);
file_handle = NULL;
file_pointer = 0;
}
static int
is_whitespace(char ch)
{
if (ch == ' ')
{
return 1;
}
if (ch == '\t')
{
return 1;
}
return 0;
}
static int
is_eol_char(char ch)
{
if (ch == '\r')
{
return 1;
}
if (ch == '\n')
{
return 1;
}
return 0;
}
static int
is_end_of_tag(char ch)
{
if ((ch >= 'a') && (ch <= 'z'))
{
return 0;
}
if ((ch >= 'A') && (ch <= 'Z'))
{
return 0;
}
if ((ch >= '0') && (ch <= '9'))
{
return 0;
}
if (ch == '_')
{
return 0;
}
return 1;
}
static int
is_end_of_name(char ch)
{
/* Currently the same as is_end_of_tag() */
return is_end_of_tag(ch);
}
static int
is_valid_file(char *filename)
{
char ext[MAX_PATH];
int i;
i = strlen(filename);
while (i > 0 && filename[i] != '.')
{
i--;
}
if (i > 0)
{
memset(ext, 0, sizeof(ext));
strncpy(&ext[0], &filename[i], strlen(&filename[i]));
if ((strncmp(ext, ".c", 2) == 0) || (strncmp(ext, ".C", 2) == 0))
{
return 1;
}
}
return 0;
}
static int
get_tag_id(char *tag)
{
if (strcasecmp(tag, "implemented") == 0)
{
return TAG_IMPLEMENTED;
}
if (strcasecmp(tag, "unimplemented") == 0)
{
return TAG_UNIMPLEMENTED;
}
return TAG_UNKNOWN;
}
static int
skip_to_next_tag()
{
unsigned int start;
int end_of_tag;
int found_tag = 0;
int tag_id;
int len;
tagname[0] = 0;
while ((file_pointer < file_size) && (!found_tag))
{
if (file_buffer[file_pointer] == '@')
{
file_pointer++;
start = file_pointer;
end_of_tag = 0;
while ((file_pointer < file_size) && (!end_of_tag))
{
end_of_tag = is_end_of_tag(file_buffer[file_pointer]);
file_pointer++;
}
len = file_pointer > start ? file_pointer - start - 1 : 0;
strncpy(tagname, &file_buffer[start], len);
tagname[len] = 0;
tag_id = get_tag_id(tagname);
if (tag_id != TAG_UNKNOWN)
{
return tag_id;
}
}
file_pointer++;
}
return TAG_UNKNOWN;
}
static void
skip_line()
{
while ((file_pointer < file_size) && (!is_eol_char(file_buffer[file_pointer])))
{
file_pointer++;
}
if ((file_pointer < file_size) && (file_buffer[file_pointer] == '\n'))
{
file_pointer++;
}
}
static void
skip_comments()
{
while ((file_pointer < file_size))
{
if (file_buffer[file_pointer] == '*')
{
if ((file_pointer + 1 < file_size))
{
if (file_buffer[file_pointer + 1] == '/')
{
skip_line();
return;
}
}
}
file_pointer++;
}
}
static int
get_previous_identifier(unsigned int end, char *name)
{
unsigned int my_file_pointer = end;
int len;
name[0] = 0;
while ((my_file_pointer > 0) && (is_whitespace(file_buffer[my_file_pointer])
|| is_eol_char(file_buffer[my_file_pointer])))
{
my_file_pointer--;
}
/* Skip any comments between function name and it's parameters */
if ((my_file_pointer > 0) && (file_buffer[my_file_pointer] == '/'))
{
if ((my_file_pointer > 0) && (file_buffer[my_file_pointer - 1] == '*'))
{
my_file_pointer--;
while ((my_file_pointer > 0) && !((file_buffer[my_file_pointer] == '*')
&& (file_buffer[my_file_pointer - 1] == '/')))
{
my_file_pointer--;
}
my_file_pointer -= 2;
}
}
/* Skip any remaining whitespace */
while ((my_file_pointer > 0) && (is_whitespace(file_buffer[my_file_pointer])))
{
my_file_pointer--;
}
end = my_file_pointer;
while ((my_file_pointer > 0))
{
if (is_end_of_name(file_buffer[my_file_pointer]))
{
len = end - my_file_pointer;
strncpy(name, &file_buffer[my_file_pointer + 1], len);
name[len] = 0;
return 1;
}
my_file_pointer--;
}
return 0;
}
static int
skip_to_next_name(char *name)
{
while ((file_pointer < file_size))
{
if (file_buffer[file_pointer] == '(')
{
return get_previous_identifier(file_pointer - 1, name);
}
file_pointer++;
}
return 0;
}
// Build a path and filename so it is of the format [module][directory][filename].
// Also convert all backslashes into forward slashes.
static void
get_filename(char *cvspath, char *filename, char *result)
{
strcpy(result, cvspath);
strcat(result, filename);
path_to_url(result);
}
static void
parse_file(char *fullname, char *cvspath, char *filename)
{
PAPI_INFO api_info;
char prev[200];
char name[200];
int tag_id;
read_file(fullname);
prev[0] = 0;
do
{
tag_id = skip_to_next_tag();
if (tag_id == TAG_UNKNOWN)
{
break;
}
/* Skip rest of the comments between the tag and the function name */
skip_comments();
if (skip_to_next_name(name))
{
if (strlen(name) == 0)
{
printf("Warning: empty function name in file %s. Previous function name was %s.\n",
fullname, prev);
}
api_info = malloc(sizeof(API_INFO));
if (api_info == NULL)
{
printf("Out of memory\n");
exit(1);
}
api_info->tag_id = tag_id;
strcpy(api_info->name, name);
get_filename(cvspath, filename, api_info->filename);
api_info->next = api_info_list;
api_info_list = api_info;
strcpy(prev, name);
}
} while (1);
close_file();
}
#ifdef WIN32
/* Win32 version */
static void
process_directory (char *path, char *cvspath)
{
struct _finddata_t f;
int findhandle;
char searchbuf[MAX_PATH];
char buf[MAX_PATH];
char newcvspath[MAX_PATH];
strcpy(searchbuf, path);
strcat(searchbuf, "*.*");
findhandle =_findfirst(searchbuf, &f);
if (findhandle != -1)
{
do
{
if (f.attrib & _A_SUBDIR)
{
if (f.name[0] != '.')
{
strcpy(buf, path);
strcat(buf, f.name);
strcat(buf, DIR_SEPARATOR_STRING);
strcpy(newcvspath, cvspath);
strcat(newcvspath, f.name);
strcat(newcvspath, "/");
process_directory(buf, newcvspath);
}
continue;
}
strcpy(buf, path);
strcat(buf, f.name);
/* Must be a .c file */
if (!is_valid_file(buf))
{
continue;
}
parse_file(buf, cvspath, f.name);
}
while (_findnext(findhandle, &f) == 0);
_findclose(findhandle);
}
else
{
printf("Cannot open directory '%s'", path);
exit(1);
}
}
#else
/* Linux version */
static void
process_directory (char *path, char *cvspath)
{
DIR *dirp;
struct dirent *entry;
struct stat stbuf;
char buf[MAX_PATH];
char newcvspath[MAX_PATH];
#ifdef HAVE_D_TYPE
dirp = opendir(path);
if (dirp != NULL)
{
while ((entry = readdir(dirp)) != NULL)
{
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
continue; // skip self and parent
if (entry->d_type == DT_REG) // normal file
{
// Check for an absolute path
if (path[0] == DIR_SEPARATOR_CHAR)
{
strcpy(buf, path);
strcat(buf, DIR_SEPARATOR_STRING);
strcat(buf, entry->d_name);
}
else
{
if (!getcwd(buf, sizeof(buf)))
{
printf("Can't get CWD: %s\n", strerror(errno));
return;
}
strcat(buf, DIR_SEPARATOR_STRING);
strcat(buf, path);
strcat(buf, entry->d_name);
}
if (stat(buf, &stbuf) == -1)
{
printf("Can't access '%s' (%s)\n", buf, strerror(errno));
return;
}
if (S_ISDIR(stbuf.st_mode))
{
strcpy(newcvspath, cvspath);
strcat(newcvspath, f.name);
strcat(newcvspath, "/");
process_directory(buf, newcvspath);
continue;
}
/* Must be a .c file */
if (!is_valid_file(buf))
{
continue;
}
parse_file(buf, cvspath, entry->d_name);
}
}
closedir(dirp);
}
else
{
printf("Can't open %s\n", path);
exit(1);
}
#else
dirp = opendir(path);
if (dirp != NULL)
{
while ((entry = readdir(dirp)) != NULL)
{
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
continue; // skip self and parent
// Check for an absolute path
if (path[0] == DIR_SEPARATOR_CHAR)
{
strcpy(buf, path);
strcat(buf, DIR_SEPARATOR_STRING);
strcat(buf, entry->d_name);
}
else
{
if (!getcwd(buf, sizeof(buf)))
{
printf("Can't get CWD: %s\n", strerror(errno));
return;
}
strcat(buf, DIR_SEPARATOR_STRING);
strcat(buf, path);
strcat(buf, entry->d_name);
}
if (stat(buf, &stbuf) == -1)
{
printf("Can't access '%s' (%s)\n", buf, strerror(errno));
return;
}
if (S_ISDIR(stbuf.st_mode))
{
strcpy(newcvspath, cvspath);
strcat(newcvspath, entry->d_name);
strcat(newcvspath, "/");
process_directory(buf, newcvspath);
continue;
}
/* Must be a .c file */
if (!is_valid_file(buf))
{
continue;
}
parse_file(buf, cvspath, entry->d_name);
}
closedir(dirp);
}
else
{
printf("Can't open %s\n", path);
exit(1);
}
#endif
}
#endif
/*
* This function compares two API entries. It returns a negative value if p is
* before q, or a positive value if p is after q.
*/
static int
compare_api_order(PAPI_INFO p, PAPI_INFO q)
{
return strcmp(p->name, q->name);
}
char *
get_filename_without_base(char *component_base,
char *filename)
{
return &filename[strlen(component_base)];
}
static void
generate_xml_for_component(char *component_name,
char *component_base)
{
PAPI_INFO api_info;
char canonical_base[MAX_PATH];
char buf[200];
int complete;
int implemented_total;
int unimplemented_total;
// Sort list
api_info_list = sort_linked_list(api_info_list, 0, compare_api_order);
implemented_total = 0;
unimplemented_total = 0;
api_info = api_info_list;
while (api_info != NULL)
{
if (api_info->tag_id == TAG_IMPLEMENTED)
implemented_total ++;
else if (api_info->tag_id == TAG_UNIMPLEMENTED)
unimplemented_total ++;
api_info = api_info->next;
}
if (implemented_total + unimplemented_total > 0)
complete = ((implemented_total) * 100) / (implemented_total + unimplemented_total);
else
complete = 100;
strcpy(canonical_base, component_base);
path_to_url(canonical_base);
sprintf(buf, "<component name=\"%s\" base=\"%s\" complete=\"%d\" implemented_total=\"%d\" unimplemented_total=\"%d\">",
component_name, canonical_base, complete, implemented_total, unimplemented_total);
write_line(buf);
if (api_info_list != NULL)
{
write_line("<functions>");
api_info = api_info_list;
while (api_info != NULL)
{
sprintf(buf, "<f n=\"%s\" i=\"%s\" f=\"%s\" />",
api_info->name,
api_info->tag_id == TAG_IMPLEMENTED ? "true" : "false",
get_filename_without_base(component_base,
api_info->filename));
write_line(buf);
api_info = api_info->next;
}
write_line("</functions>");
}
write_line("</component>");
}
static void
read_input_file(char *input_file)
{
char component_name[MAX_PATH];
char component_path[MAX_PATH];
char *canonical_path;
unsigned int index;
unsigned int start;
PAPI_INFO api_info;
PAPI_INFO next_api_info;
char *buffer;
unsigned int size;
int len;
in = fopen(input_file, "rb");
if (in == NULL)
{
printf("Cannot open input file");
exit(1);
}
// Get the size of the file
fseek(in, 0, SEEK_END);
size = ftell(in);
// Load it all into memory
buffer = malloc(size);
if (buffer == NULL)
{
fclose(in);
printf("Out of memory\n");
exit(1);
}
fseek(in, 0, SEEK_SET);
if (fread (buffer, 1, size, in) < 1)
{
fclose(in);
printf("Read error in file %s\n", input_file);
exit(1);
}
index = 0;
write_line("<?xml version=\"1.0\" encoding=\"iso-8859-1\" ?>");
write_line("<?xml-stylesheet type=\"text/xsl\" href=\"rapistatus.xsl\"?>");
write_line("");
write_line("<components>");
while (1)
{
/* Free previous list */
for (api_info = api_info_list; api_info != NULL; api_info = next_api_info)
{
next_api_info = api_info->next;
free(api_info);
}
api_info_list = NULL;
/* Skip whitespace and eol characters */
while ((index < size) && (is_whitespace(buffer[index]) || (is_eol_char(buffer[index]))))
index++;
if ((file_pointer < size) && (buffer[index] == '\n'))
index++;
if (buffer[index] == ';')
{
/* Skip comments */
while ((index < size) && (!is_eol_char(buffer[index])))
index++;
if ((index < size) && (buffer[index] == '\n'))
index++;
continue;
}
/* Get component name */
start = index;
while ((index < size) && (!is_whitespace(buffer[index])))
index++;
if (index >= size)
break;
len = index - start;
strncpy(component_name, &buffer[start], len);
component_name[len] = 0;
/* Skip whitespace */
while ((index < size) && (is_whitespace(buffer[index])))
index++;
if (index >= size)
break;
/* Get component path */
start = index;
while ((index < size) && (!is_whitespace(buffer[index]) && !is_eol_char(buffer[index])))
index++;
len = index - start;
strncpy(component_path, &buffer[start], len);
component_path[len] = 0;
/* Append directory separator if needed */
if (component_path[strlen(component_path)] != DIR_SEPARATOR_CHAR)
{
int i = strlen(component_path);
component_path[strlen(component_path)] = DIR_SEPARATOR_CHAR;
component_path[i + 1] = 0;
}
/* Skip to end of line */
while ((index < size) && (!is_eol_char(buffer[index])))
index++;
if ((index < size) && (buffer[index] == '\n'))
index++;
canonical_path = convert_path(component_path);
if (canonical_path != NULL)
{
process_directory(canonical_path, canonical_path);
free(canonical_path);
generate_xml_for_component(component_name,
component_path);
}
}
write_line("</components>");
}
static char HELP[] =
"RGENSTAT input-filename output-filename\n"
"\n"
" input-filename File containing list of components to process\n"
" output-filename File to create\n";
int main(int argc, char **argv)
{
char *input_file;
char *output_file;
if (argc != 3)
{
puts(HELP);
return 1;
}
input_file = convert_path(argv[1]);
if (input_file[0] == 0)
{
free(input_file);
printf("Missing input-filename\n");
return 1;
}
output_file = convert_path(argv[2]);
if (output_file[0] == 0)
{
free(input_file);
free(output_file);
printf("Missing output-filename\n");
return 1;
}
out = fopen(output_file, "wb");
if (out == NULL)
{
free(input_file);
free(output_file);
printf("Cannot open output file");
return 1;
}
read_input_file(input_file);
free(input_file);
free(output_file);
fclose(out);
return 0;
}
/* EOF */