/*############################################################# 用法: 1. 修改pclint.cfg中的配置项 2. 执行脚本,会在当前目录生成处理的文件: ret_原文件名.txt 如果pclint.cfg中配置项为: MASK_INFOS="813 530",则生成 的文件名为: ret_no530_no13_原文件名.txt 3. 程序中使用第三方库: pcre进行正则处理 该在linux/windows上安装pcre步骤如下:(以linux为例) a. 解压 b. 在解压后目录 执行: ./configure; make; makeinstall 4. 编译此程序命令: cc deal_lint.c -lpcre ##############################################################*/ #define PCRE_STATIC #include <stdio.h> #include <stdlib.h> #include <string.h> #include <pcre.h> #include <time.h> /*####################################### 全局变量 ########################################*/ #define TRUE 1 #define FALSE 0 // 配置项 char LINT_FILES[20][100]; char MASK_INFOS[20][5]; char LINT_FILE_DIR[100]; char DIV_PATH[100]; // 是否删除"During Specfic Walk:"信息 int DEL_SPEC_FLAG = FALSE; // 字符缓冲区 #define BUF_LEN 1024 char BUF[BUF_LEN]; // 字符串缓冲区 #define LEN 50 char STR_BUF[LEN][1024]; // 日志文件指针 FILE *LOGFILE; // 空格, 用于分割字符串 const char *SPACES = " \t\f\r\v\n"; /*####################################### 打印日志 ########################################*/ void Log(const char *pStr) { if (LOGFILE && pStr) { fputs(pStr, LOGFILE); } } /*####################################### 删除前缀空格,修改源串 ########################################*/ char * leftTrim(char *pStr) { char buf[1024]; char *pTmpStr; pTmpStr = pStr; while (*pTmpStr == ' ' || *pTmpStr == '\t' || *pTmpStr == '\r' || *pTmpStr == '\n') { pTmpStr++; } strcpy(buf, pTmpStr); strcpy(pStr, buf); return pStr; } /*####################################### 删除后缀空格,修改源串 ########################################*/ char * rightTrim(char *pStr) { char buf[1024]; char *pTmpStr; if (NULL == pStr || *pStr == 0) { return pStr; } pTmpStr = pStr + strlen(pStr) - 1; while (*pTmpStr == ' ' || *pTmpStr == '\r' || *pTmpStr == '\n' || *pTmpStr == '\t') { pTmpStr--; } *(pTmpStr + 1) = 0; return pStr; } /*####################################### 删除首尾空格,修改源串 ########################################*/ char * trim(char *pStr) { return leftTrim(rightTrim(pStr)); } /*####################################### 分割字符串 ret: 是否成功 ########################################*/ int split(const char *pStr, const char *pSplitChar) { char *pToken; int i = 0; char buf[1024]; strcpy(buf, pStr); pToken = strtok(buf, pSplitChar); if (!pToken) { return FALSE; } memset(STR_BUF, 0, sizeof(STR_BUF)); while (pToken) { strcpy(STR_BUF[i++], pToken); pToken = strtok(NULL, pSplitChar); } return TRUE; } /*####################################### 对给定的字符串进行正则匹配,匹配的结果保存在全局数组中 arg1: 字符串 arg2: 正则表达式 ret:是否匹配 ########################################*/ int match(const char *pStr, const char *pReg) { pcre *re; const char *error; int erroffset; int ovector[30]; int rc; int i; int j = 0; re = pcre_compile( pReg, /* pattern */ 0, /* default option */ &error, /* for error msg */ &erroffset, /* for error offset */ NULL); /* use default character tables */ rc = pcre_exec( re, /* the compiled pattern */ NULL, /* no extra data */ pStr, /* subject string */ strlen(pStr), /* subject string length */ 0, /* start at offset 0 in the subject */ 0, /* default option */ ovector, /* output of substr */ 30); /* number of elemenets int the ouput ovector */ pcre_free(re); // 将匹配的结果保存 if (rc >= 0) { for (i = 1; i < rc; i++) { char *subStart = (char*)pStr + ovector[2*i]; int subLen = ovector[2*i + 1] - ovector[2*i]; memset(STR_BUF[j], 0, LEN); strncpy(STR_BUF[j++], subStart, subLen); } return TRUE; } else { return FALSE; } } /*####################################### 对指定字符中进行替换操作 ########################################*/ char * replace(char *pSrcStr, const char *pOldSubStr, const char *pNewSubStr, int isGlobal) { int len; char buf[1024]; char *pBegin; char *pEnd; len = strlen(pOldSubStr); do { pBegin = strstr(pSrcStr, pOldSubStr); pEnd = pBegin + len; // 源字串未找到时结束 if (!pBegin) { break; } memset(buf, 0, 1024); strncpy(buf, pSrcStr, pBegin-pSrcStr); strcat(buf, pNewSubStr); strcat(buf, pEnd); // 修改源串 strcpy(pSrcStr, buf); }while(isGlobal); return pSrcStr; } /*####################################### 打开文件,进行二次封装 ########################################*/ FILE *openFile(const char *pFileName, const char *mode) { char buf[200]; FILE *pFile; sprintf(buf, "%s%s", LINT_FILE_DIR, pFileName); pFile = fopen(buf, mode); return pFile; } /*####################################### 检查LINT_FILES配置 ########################################*/ int checkLintFiles(const char *pLintFiles) { int i = 0; int j = 0; int ret = TRUE; FILE *pTmpFile; Log("##################### Check LINT_FILES ################\n"); if (!split(pLintFiles, SPACES)) { Log(" Error: LINT_FILES can't be NULL!\n\n"); return FALSE; } // 判断配置的文件是否存在 for (i = 0; i<LEN; i++) { if (*STR_BUF[i] == 0) { break; } // 如果文件打不开,就认为文件不存在 pTmpFile = openFile(STR_BUF[i], "r"); if (!pTmpFile) { Log(" Error: "); Log(STR_BUF[i]); Log(" does not exist.\n"); ret = FALSE; } else { fclose(pTmpFile); } // 填充全局变量 strcpy(LINT_FILES[j++], STR_BUF[i]); } return ret; } /*####################################### 检查MASK_INFOS配置 ########################################*/ int checkMaskInfos(const char *pMaskInfos) { int ret = TRUE; Log("##################### Check MASK_INFOS ################\n"); if (split(pMaskInfos, SPACES)) { int i = 0; for (i = 0; i<LEN; i++) { if (*STR_BUF[i] == 0) { break; } if (!match(STR_BUF[i], "^\\d+$")) { Log(" Error: "); Log(STR_BUF[i]); Log(" is not a number.\n"); ret = FALSE; } } } return ret; } /*####################################### 检查LINT_FILE_DIR配置 ########################################*/ int checkLintFileDir(const char *pLintDir) { Log("####################### Check LINT_FILE_DIR ###############\n"); // 未配置 if (strcmp(pLintDir, "") == 0) { Log(" Error: LINT_FILE_DIR cant be null!\n\n"); return FALSE; } // 填充全局变量 strcpy(LINT_FILE_DIR, pLintDir); return TRUE; } /*####################################### 检查DIV_PATH配置 ########################################*/ int checkDivPath(const char *pDivPath) { Log("######################## Check DIV_PATH #################\n"); if (strcmp(pDivPath, "") == 0) { Log(" Error: DIV_PATH can not be NULL\n\n"); return FALSE; } // 填充全局变量 strcpy(DIV_PATH, pDivPath); return TRUE; } /*####################################### 配置项检查 ########################################*/ int checkCfg(void) { int ret = TRUE; char *pLeftStr; char *pRightStr; FILE *pCfgFile = fopen("pclint.cfg", "r"); if (!pCfgFile) { Log(" Error: cant not open pclint.cfg!\n\n"); return FALSE; } while (fgets(BUF, BUF_LEN, pCfgFile)) { // 忽略以#开始的注释行 if (match(BUF, "^\\s*#")) { continue; } if (match(BUF, "(.+)=(.*)")) { pLeftStr = trim(STR_BUF[0]); pRightStr = trim(STR_BUF[1]); if (strcmp(pLeftStr, "LINT_FILES") == 0) { if (checkLintFiles(pRightStr)) { Log(" LINT_FILES is OK.\n\n"); } else { ret = FALSE; } } else if (strcmp(pLeftStr, "MASK_INFOS") == 0) { if (checkMaskInfos(pRightStr)) { Log(" MASK_INFOS is OK.\n\n"); } else { ret = FALSE; } } else if (strcmp(pLeftStr, "DEL_SPECK_FLAG") == 0) { if (*pRightStr == 'Y' || *pRightStr == 'y') { DEL_SPEC_FLAG = TRUE; } } else if (strcmp(pLeftStr, "LINT_FILE_DIR") == 0) { checkLintFileDir(pRightStr); } else if (strcmp(pLeftStr, "DIV_PATH") == 0) { checkDivPath(pRightStr); } } } return ret; } /*####################################### 获取代码文件在linux上的真实路径 ########################################*/ char * getLinuxFilePath(char *pCodeFileName, char *pContainer) { char buf[200]; strcpy(pContainer, pCodeFileName); replace(pContainer, "\\", "/", TRUE); replace(pContainer, "X:/", "", FALSE); replace(pContainer, "x:/", "", FALSE); strcpy(buf, DIV_PATH); strcat(buf, pContainer); strcpy(pContainer, buf); return pContainer; } /*####################################### 获取文件中指定行内容: 前50个字符 arg1: 文件名 arg2: 行号 arg3: 结果容器 ########################################*/ int getFileContent(char *pFileName, int lineNum, char *pContainer) { int fileCount = 0; char buf[200]; FILE *pFile; getLinuxFilePath(pFileName, buf); pFile = fopen(buf, "r"); if (!pFile) { strcpy(pContainer, "获取文件失败"); return FALSE; } while (fgets(buf, 200, pFile)) { if (++fileCount == lineNum) { break; } } fclose(pFile); // 删除空格 trim(buf); // 截取前50个字符 *(buf+50) = 0; strcpy(pContainer, buf); return TRUE; } /*####################################### 预处理: 删除无用的行 arg3: 是否删除源文件 ########################################*/ int preDeal(const char *pSrcFileName, char *pRetFileName, int isDelSrcFile) { char macroStr[256] = {0}; int macroFlag = 0; FILE *pInFile = openFile(pSrcFileName, "r"); FILE *pOutFile = openFile(pRetFileName, "w"); if (!pInFile || !pOutFile) { Log("preDeal: open file failed!\n\n"); return FALSE; } while (fgets(BUF, BUF_LEN, pInFile)) { // 删除无用的行 if (match(BUF, "(^\\s*$)|(--- Module:)|(--- Wrap-up for Module:)|(Info 830:)|(Info 813:)|(^\\s[{}_]_)\\s*$|(During Specific Walk:)")) { continue; } // 删除重复行: 以#...开始,则为宏 if (match(BUF, "#\\.\\.\\.")) { strcpy(macroStr, BUF); macroFlag = 1; } else if (macroFlag) // 前一行为宏,则当前行一定是对宏的解释 { macroFlag = 0; fputs(macroStr, pOutFile); fputs(BUF, pOutFile); } else { fputs(BUF, pOutFile); } } fclose(pInFile); fclose(pOutFile); return TRUE; } /*####################################### 以时间生成日志文件名 ########################################*/ void createLogFileName(char *pContainer) { time_t l = time(NULL); struct tm *pTm; char tmpBuf[20]; pTm = localtime(&l); strftime(tmpBuf, sizeof(tmpBuf), "%Y%m%d_%H-%M-%S", pTm); sprintf(pContainer, "log_%s.txt", tmpBuf); } /*####################################### 对文件进行排序 ########################################*/ int sortFile(const char *pSrcFileName, const char *pRetFileName, int isDelSrcFile) { char pCmd[100] = {0}; FILE *pInFile = openFile(pSrcFileName, "r"); FILE *pOutFile = openFile(pRetFileName, "w"); if (!pInFile || !pOutFile) { Log("sortFile: open file faied!\n\n"); return FALSE; } // 利用系统命令 strcpy(pCmd, "/bin/sort "); strcat(pCmd, pSrcFileName); strcat(pCmd, " -o "); strcat(pCmd, pRetFileName); system(pCmd); fclose(pInFile); fclose(pOutFile); return TRUE; } /*####################################### 对排序后的文件进行去重处理 ########################################*/ int uniqFile(const char *pSrcFileName, const char *pRetFileName, int isDelSrcFile) { char pPreLine[BUF_LEN] = {0}; FILE *pInFile = openFile(pSrcFileName, "r"); FILE *pOutFile = openFile(pRetFileName, "w"); if (!pInFile || !pOutFile) { Log("uniqFile: open file failed!\n\n"); return FALSE; } while (fgets(BUF, BUF_LEN, pInFile)) { if (strcmp(pPreLine, BUF) != 0) { fputs(BUF, pOutFile); } strcpy(pPreLine, BUF); } fclose(pInFile); fclose(pOutFile); return TRUE; } /*####################################### 求x的y次幂 ########################################*/ long myPow(int x, int y) { if (y<=0 || y>7) { return 0; } if (y == 1) { return x; } return myPow(x, y-1) * x; } /*####################################### 从"123"得到123整数 ########################################*/ long getStrNumber(const char *pStr) { int len = strlen(pStr); long num = 0; while (*pStr) { num += (*pStr - '0') * myPow(10, len--); pStr++; } return num; } /*####################################### 生成临时文件,其中每行代表一条lint信息 ########################################*/ int createTmpFile(const char *pSrcFileName, const char *pRetFileName, int isDelSrcFile) { char fileName[200] = {0}; char lintInfo[15] = {0}; char lineNum[10] = {0}; char macroFlag[2] = "N"; char macroValue[200] = {0}; char details[1024] = {0}; char content[100] = {0}; char errNum[5] = {0}; char okNum[5] = {0}; char *pTmpStr; FILE *pInFile = openFile(pSrcFileName, "r"); FILE *pOutFile = openFile(pRetFileName, "w"); if (!pInFile || !pOutFile) { Log("createTmpFile: openFile failed!\n\n"); return FALSE; } while (fgets(BUF, BUF_LEN, pInFile)) { // 处理Warning 529: 和Info 715: Warning 550: Warning 530 // pc_lint产生的出错行不准确, 需要从错误描述中提取 if (match(BUF, "(Warning 529:)|(Info 715:)|(Waring 550:)|(Waring 530: .*[(])line \\d+[)])")) { match(BUF, "\\s+(\\d+)\\s+"); strcpy(errNum, STR_BUF[0]); match(BUF, "[(]line\\s+(\\d+)[)]"); strcpy(okNum, STR_BUF[0]); replace(BUF, errNum, okNum, FALSE); } // 宏 if (match(BUF, "#\\.\\.\\.")) { strcpy(macroFlag, "Y"); pTmpStr = trim(BUF); // 去除开头的#... pTmpStr += 5; pTmpStr = leftTrim(pTmpStr); sprintf(macroValue, " Macro valus is: %s", pTmpStr); } // 正常行信息 else if (match(BUF, "[.][ch]\\s+\\d+\\s+[a-z]+\\s+\\d+:(.*)")) { sprintf(details, "%s%s", macroValue, rightTrim(STR_BUF[0])); // 分割行 split(BUF, SPACES); strcpy(fileName, STR_BUF[0]); strcpy(lineNum, STR_BUF[1]); strcpy(lintInfo, STR_BUF[2]); strcat(lintInfo, " "); strncat(lintInfo, STR_BUF[3], strlen(STR_BUF[3]) - 1); // 行内容 getFileContent(fileName, getStrNumber(lineNum), content); // 如果当前行内容为{,并且lint_info为Warning 578,则说明一定是函数 // 参数问题, 那么lint的行号就是错误的,需要修正下 if (strcmp(content, "{") == 0 && strcmp(lintInfo, "Warning 578") == 0) { getFileContent(fileName, getStrNumber(lineNum) -1 , content); } fprintf(pOutFile, "%s@%s@%s@%s@%s@%s\n", fileName, lintInfo, lineNum, macroFlag, details, content); // 清空宏信息 strcpy(macroFlag, "N"); strcpy(macroValue, ""); } // 形如: During Specific Walk:的信息 else if (match(BUF, "File .+ line \\d+(.*)")) { if (DEL_SPEC_FLAG) { continue; } sprintf(details, "During Specific Walk: %s", rightTrim(STR_BUF[0])); // 清空宏信息 strcpy(macroFlag, "N"); strcpy(macroValue, ""); // 分割行 split(BUF, SPACES); strcpy(fileName, STR_BUF[1]); strncpy(lineNum, STR_BUF[3], strlen(STR_BUF[3]) - 1); strcpy(lintInfo, "无"); getFileContent(fileName, getStrNumber(lineNum), content); fprintf(pOutFile, "%s@%s@%s@%s@%s@%s\n", fileName, lintInfo, lineNum, macroFlag, details, content); } } fclose(pInFile); fclose(pOutFile); return TRUE; } /*####################################### 从文件中删除由正则表达式指定的行 ########################################*/ int delLine(const char *pSrcFileName, const char *pRegStr) { char delFileName[100] = {0}; FILE *pInFile; FILE *pOutFile; strcpy(delFileName, "del_"); strcat(delFileName, pSrcFileName); pInFile = openFile(pSrcFileName, "r"); pOutFile = openFile(delFileName, "w"); if (!pInFile || !pOutFile) { Log("delLine: open file failed\n"); return FALSE; } while (fgets(BUF, BUF_LEN, pInFile)) { if (match(BUF, pRegStr)) { continue; } fputs(BUF, pOutFile); } fclose(pInFile); fclose(pOutFile); // 替换源文件内容为生成的新文件 pInFile = openFile(delFileName, "r"); pOutFile = openFile(pSrcFileName, "w"); if (!pInFile || !pOutFile) { Log("delLine: open file failed\n"); return FALSE; } while (fgets(BUF, BUF_LEN, pInFile)) { fputs(BUF, pOutFile); } fclose(pInFile); fclose(pOutFile); return TRUE; } /*####################################### 生成最终结果文件 ########################################*/ int createRetFile(const char *pSrcFileName, const char *pRetFileName, int isDelSrcFile) { // 定义字段名 char fileName[200] = {0}; char lintInfo[15] = {0}; char lineNum[5] = {0}; char macroFlag[2] = {0}; char details[1024] = {0}; char content[100] = {0}; char preName[200] = {0}; int fileCount = 0 ; FILE *pInFile = openFile(pSrcFileName, "r"); FILE *pOutFile = openFile(pRetFileName, "w"); if (!pInFile || !pOutFile) { Log("createRetFile: open file failed!\n\n"); return FALSE; } while (fgets(BUF, BUF_LEN, pInFile)) { split(rightTrim(BUF), "@"); strcpy(fileName, STR_BUF[0]); strcpy(lintInfo, STR_BUF[1]); strcpy(lineNum, STR_BUF[2]); strcpy(macroFlag, STR_BUF[3]); strcpy(details, STR_BUF[4]); strcpy(content, BUF + strlen(fileName) + strlen(lintInfo) + strlen(lineNum) + strlen(macroFlag) + strlen(details) + 5); if (strcmp(preName, fileName) != 0 ) { if (fileCount++ > 0) { fputs("\r\n\r\n", pOutFile); } fprintf(pOutFile, "%d.文件: %s\n", fileCount, fileName); fputs(" ----------------------------------------------------\n", pOutFile); fputs(" 行号 信息 当前行内容(前50字符) 含有宏 详细描述\n", pOutFile); fputs(" ----------------------------------------------------\n", pOutFile); } strcpy(preName, fileName); fprintf(pOutFile, " %-8s%-15s%-55s %-5s%s\n", lineNum, lintInfo, content, macroFlag, details); } if (fileCount == 0) { fputs("Very good, no lint info.\n", pOutFile); } fclose(pInFile); fclose(pOutFile); } /*####################################### 根据MASK_INFOS生成结果文件 ########################################*/ int createMaskRetFile(const char *pSrcFileName, const char *uniqFileName, int isDelSrcFile) { char retMaskFileName[100] = {0}; char regBuf[100] = {0}; int i = 0; // 未配置MASK_INFOS if (*MASK_INFOS[0] == 0) { return FALSE; } // 生成结果文件名 strcpy(retMaskFileName, "ret"); for (i = 0; i<20;i++) { if (*MASK_INFOS[i] == 0) { break; } strcat(retMaskFileName, "_no"); strcat(retMaskFileName, MASK_INFOS[i]); strcpy(regBuf, "@[a-zA-Z]+ "); strcat(regBuf, MASK_INFOS[i]); strcat(regBuf, "@"); delLine(uniqFileName, regBuf); } strcat(retMaskFileName, "_"); strcat(retMaskFileName, pSrcFileName); createRetFile(uniqFileName, retMaskFileName, FALSE); } /*####################################### 处理lint_file文件 ########################################*/ int dealLintFile(const char * pSrcFileName) { char preFileName[100] = {0}; char tmpFileName[100] = {0}; char sortFileName[100] = {0}; char uniqFileName[100] = {0}; char retFileName[100] = {0}; char regBuf[20] = {0}; int i = 0; int ret = TRUE; sprintf(preFileName, "pre_%s", pSrcFileName); sprintf(tmpFileName, "%s.tmp", pSrcFileName); sprintf(sortFileName, "sort_%s", pSrcFileName); sprintf(uniqFileName, "uniq_%s", pSrcFileName); sprintf(retFileName, "ret_%s", pSrcFileName); // 预处理 if (!preDeal(pSrcFileName, preFileName, FALSE)) { return FALSE; } // 生成临时文件 if (!createTmpFile(preFileName, tmpFileName, FALSE)) { return FALSE; } // 排序 if (!sortFile(tmpFileName, sortFileName, FALSE)) { return FALSE; } // 去重 if (!uniqFile(sortFileName, uniqFileName, FALSE)) { return FALSE; } // 生成结果文件 createRetFile(uniqFileName, retFileName, FALSE); // 根据MASK_INFOS生成另一结果文件 createMaskRetFile(pSrcFileName, uniqFileName, FALSE); return TRUE; } //################################ 程序入口 ########################### int main(void) { int i = 0; // 生成日志文件 createLogFileName(BUF); LOGFILE = openFile(BUF, "w"); if (!LOGFILE) { printf("main: create log file failed\n"); } // 配置项检查 checkCfg(); // 处理所有lint文件 for (i = 0; i < 20; i++) { if (*LINT_FILES[i] == 0) { break; } dealLintFile(LINT_FILES[i]); } Log("\n####################### All Done ######################\n"); return 0; } /// pc_lint.cfg # pc_lint文件所在的目录,如果是当前目录,填写为. # LINT_FILE_DIR = . # 要处理的lint_file,以空格分开,注意必须写在同一行 LINT_FILES = LintAll.txt # 不需要处理的lint消息,如果Info 813. 只需要填数字,以空格分开,可为空 # 如MASK_INFOS="813 712" 代表不需要处理813 712信息 MASK_INFOS = 813 # 是否删除"During Specific Walk:" 信息,默认不删除 DEL_SPEC_FLAG = N