// 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 #include #include #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; } }