197 lines
5.5 KiB
C
197 lines
5.5 KiB
C
// 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;
|
|
}
|
|
}
|
|
|