slbldtls/audit.c

197 lines
5.5 KiB
C
Raw Normal View History

// Copyright (c) 2025, Zak Fenton
// This "audit" program is licensed under the Mulan PSL v2. You can use this
// software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN “AS IS” BASIS, WITHOUT warranties of
// any kind, either express or implied, including but not limited to
// non-infringement, merchantability or fit for a particular purpose.
// See the Mulan PSL v2 for more details.
#include <stdlib.h>
#include <stddef.h>
#include <stdio.h>
#define AUDIT_TYPE_OLD 1
#define AUDIT_TYPE_NEW 2
#define AUDIT_TYPE_UNMARKED 3
typedef struct audit_info audit_info_t;
typedef struct audit_filelist audit_filelist_t;
struct audit_filelist {
const char* name;
audit_filelist_t* next;
int type;
int lines;
};
struct audit_info {
int filecount;
int linecount;
audit_filelist_t* oldcode;
audit_filelist_t* newcode;
audit_filelist_t* unmarkedcode;
};
int audit_filelist_countfiles(audit_filelist_t* l) {
int total = 0;
while (l) {
total++;
l = l->next;
}
return total;
}
int audit_filelist_countlines(audit_filelist_t* l) {
int total = 0;
while (l) {
total += l->lines;
l = l->next;
}
return total;
}
int usage(int argc, char** argv, const char* error) {
FILE* out = error ? stderr : stdout;
fprintf(out, "ABOUT:\n");
fprintf(out, "This is a simple code audit tool to note occurrences like OLD CODE & NEW CODE\n");
fprintf(out, "The main purpose of this tool is to help gradually remove old/third-party code from a codebase,\n");
fprintf(out, "but it could be extended later e.g. for checking each file has been manually audited at a recent date.\n");
fprintf(out, "This tool isn't specific to a particular programming language and can be used for config files too.\n");
fprintf(out, "\nUSAGE:\n");
fprintf(out, "\t%s [source files...]\n", argv[0]);
if (error) {
fprintf(out, "ERROR: %s\n", error);
return -1;
} else {
return 0;
}
}
int audit_check(const char* line, const char* check) {
char c;
while ((c = *line++) != 0) {
if (c == check[0]) {
const char* search = check+1;
while (*search && *search == *line) {
line++;
search++;
}
if (!*search) {
return 1; // Found!
}
}
}
return 0; // Not found
}
void audit_line(audit_info_t* info, audit_filelist_t* file, const char* line) {
if (audit_check(line, "OLD CODE")) {
file->type = AUDIT_TYPE_OLD;
} else if (file->type == AUDIT_TYPE_UNMARKED && audit_check(line, "NEW CODE")) {
file->type = AUDIT_TYPE_NEW;
}
file->lines++;
}
char linebuf[1000];
int audit_run(audit_info_t* info, const char* filename) {
FILE* f = fopen(filename, "r");
if (!f) {
return -1;
}
audit_filelist_t* filelist = malloc(sizeof(audit_filelist_t));
if (!filelist) {
fprintf(stderr, "ERROR: Failed to allocate memory\n");
fclose(f);
return -1;
}
filelist->name = strdup(filename);
filelist->type = AUDIT_TYPE_UNMARKED;
filelist->lines = 0;
filelist->next = NULL;
while (fgets(linebuf, 1000, f)) {
audit_line(info, filelist, linebuf);
}
switch (filelist->type) {
case AUDIT_TYPE_NEW:
filelist->next = info->newcode;
info->newcode = filelist;
break;
case AUDIT_TYPE_OLD:
filelist->next = info->oldcode;
info->oldcode = filelist;
break;
default:
filelist->next = info->unmarkedcode;
info->unmarkedcode = filelist;
break;
}
info->linecount += filelist->lines;
fclose(f);
return 0;
}
void audit_subreport(audit_info_t* info, const char* startline, audit_filelist_t* files, FILE* out, int extrastats) {
int nfiles = audit_filelist_countfiles(files);
int nlines = audit_filelist_countlines(files);
int fpercent = (int) (((double) nfiles) / ((double) (info->filecount)) * 100);
int lpercent = (int) (((double) nlines) / ((double) (info->linecount)) * 100);
fprintf(out, "%s %d FILES (%d%%)\t%d LINES (%d%%)\n", startline, nfiles, fpercent, nlines, lpercent);
if (!files) {
return;
}
//fprintf(out, "%s IN ", startline);
audit_filelist_t* smallest = files;
audit_filelist_t* largest = files;
while (files) {
//fprintf(out, " %s", files->name);
if (files->lines < smallest->lines) {
smallest = files;
} else if (files->lines > largest->lines) {
largest = files;
}
files = files->next;
}
//fprintf(out, "\n");
if (extrastats) {
fprintf(out, "%s SMALLEST %s (%d lines) LARGEST %s (%d lines)\n", startline, smallest->name, smallest->lines, largest->name, largest->lines);
}
}
void audit_report(audit_info_t* info, FILE* out) {
fprintf(out, "THIS IS AN AUTOGENERATED REPORT\n");
fprintf(out, "This report was created by the 'audit' program\n");
fprintf(out, "TOTAL %d FILES %d LINES\n", info->filecount, info->linecount);
audit_subreport(info, "NEW CODE: ", info->newcode, out, 0);
audit_subreport(info, "OLD CODE: ", info->oldcode, out, 1);
audit_subreport(info, "UNMARKED CODE:", info->unmarkedcode, out, 1);
}
int main(int argc, char** argv) {
audit_info_t info;
info.filecount = 0;
info.oldcode = NULL;
info.newcode = NULL;
info.unmarkedcode = NULL;
for (int i = 1; i < argc; i++) {
if (audit_run(&info, argv[i]) != 0) {
fprintf(stderr, "ERROR: Failed to process file '%s'\n", argv[i]);
return -1;
}
info.filecount++;
}
if (info.filecount < 1) {
return usage(argc, argv, "Expecting list of source files\n");
} else {
audit_report(&info, stdout);
return 0;
}
}