morph

morph command searches functions in files and replaces them with the given functions, when the parameters change order both function declaration and function calls are changed
git clone https://noulin.net/git/morph.git
Log | Files | Refs | LICENSE

commit cd16d6353f0631324c1e53b304121d3f060c44d8
parent 7bdef0f2c86594d0f3bbc9af16065af4f45ed1f6
Author: Remy Noulin <loader2x@gmail.com>
Date:   Sun, 20 May 2018 00:35:38 +0200

refactoring utility

morph.c     | 320 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
package.yml |  18 ++++
2 files changed, 338 insertions(+)

Diffstat:
Amorph.c | 320+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apackage.yml | 18++++++++++++++++++
2 files changed, 338 insertions(+), 0 deletions(-)

diff --git a/morph.c b/morph.c @@ -0,0 +1,320 @@ +#!/usr/bin/env sheepy + +// search and replace +// input: +// file.txt +// replace_configuration.txt +// the files to process are listed in file.txt in current directory. +// Use # to comment lines in files.txt. +// replace_configuration.txt holds the strings to search and replace in the files listed in file.txt +// +// replace_configuration.txt format: +// - line 1: separetor in line 2 and 3 +// - line 2: strings to search separeted with the characters in line 1 +// - line 3: replace strings separeted with the characters in line 1 + +#include "libsheepyObject.h" +#include "pcre.h" + +#define internal static + +#include <stdlib.h> +#include <stdbool.h> +#include <string.h> +#include <stdio.h> + +void replace(); +void replaceWithPcre(char **l, pcre *reCompiled, pcre_extra *pcreExtra, char *func_search, char *replaceWith, smallArrayt *mappingNParams); +int MAIN(int ARGC, char** ARGV); + +int argc; char **argv; + +// get function name +smallStringt *funcName(char *e) { + smallStringt *s = allocG(e); + smallArrayt *spl = extractG(s, "*", "("); + terminateG(s); + s = getNDupG(spl, rtSmallStringt, 0); + terminateG(spl); + return s; +} + +// extract params +smallArrayt *funcParams(char *e) { + smallStringt *s = allocG(e); + smallArrayt *spl = extractG(s, "(", ")"); + terminateG(s); + s = getG(spl, rtSmallStringt, 0); + smallArrayt *params = splitG(s, ","); + terminateG(spl); + enumerateSmallArray(params, P, i) { + castS(p, P); + trimG(p); + setPG(params, i, p); + finishG(p); + } + return params; +} + +void replace() { + char **replace_configuration = NULL; + char **search_what = NULL; + char **replace_with = NULL; + char **config = NULL; + + // Steps + // load config + // create string regex + // compile regexes + // optimize the regex + // create regex for each search string + // for each file in files.txt, search and replace + + printf("\nLoading config...\n"); + printf("\n"); + + // load config + replace_configuration = readText("morph_configuration.txt"); + + exitFailure(replace_configuration); + + search_what = split(replace_configuration[1], replace_configuration[0]); + replace_with = split(replace_configuration[2], replace_configuration[0]); + + if (!search_what || !replace_with) { + printf("wrong replace_configuration.txt"); + printf("\n"); + XFAILURE; + } + + createAllocateSmallArray(a); + createAllocateSmallArray(func_search_what); + createAllocateSmallArray(func_replace_with); + + // create string regex + char **strRegex = NULL; + enumerate(search_what, e, i) { + printf("Replace: "); + printf("\n"); + // char *strLCpy(char *dst, const char *src, size_t dstSize) + // get function name + pushNFreeG(func_search_what, trimG(funcName(*e))); + smallArrayt *params = funcParams(*e); + printf("%s\n", *e); + printf("With:"); + printf("\n"); + // get replace function name + pushNFreeG(func_replace_with, trimG(funcName(replace_with[i]))); + // get replace params + smallArrayt *replace_params = funcParams(replace_with[i]); + // map old->new params + createAllocateSmallArray(param_mapping); + enumerateSmallArray(replace_params, P, j) { + enumerateSmallArray(params, RP, k) { + if (eqG(P, RP)) { + pushG(param_mapping, k); + } + finishG(RP); + } + finishG(P); + } + terminateG(replace_params); + // save info in array a + createAllocateSmallArray(mappingNParams); + pushNFreeG(mappingNParams, params); + pushNFreeG(mappingNParams, param_mapping); + pushNFreeG(a, mappingNParams); + printf("%s\n", replace_with[i]); + char *reg = catS("\\b", getG(func_search_what, rtChar, i), "\\b"); + iListPushS(&strRegex, reg); + } + + //logVarG(a); + //listPrintS(strRegex); + + // compile regexes + // optimize the regex + size_t nSearch = lenG(func_search_what); + pcre **reCompiled = malloc((nSearch+1) * sizeof(pcre *)); + reCompiled[nSearch] = NULL; + pcre_extra **pcreExtra = malloc((nSearch+1) * sizeof(pcre_extra *)); + pcreExtra[nSearch] = NULL; + + const char *pcreErrorStr; + int pcreErrorOffset; + + // create regex for each search string + enumerate(strRegex, e, j) { + reCompiled[j] = pcre_compile(*e, 0, &pcreErrorStr, &pcreErrorOffset, NULL); + if (!reCompiled[j]) { + printf("ERROR: Could not study >%s<: %s", *e, pcreErrorStr); + printf("\n"); + XFAILURE; + } + + pcreExtra[j] = pcre_study(reCompiled[j], 0, &pcreErrorStr); + if (!pcreExtra) { + printf("ERROR: Could not study >%s<: %s", *e, pcreErrorStr); + printf("\n"); + XFAILURE; + } + } + + + printf("\n\nProcess files...\n"); + printf("\n"); + + config = readText("files.txt"); + exitFailure(config); + + // for each file in files.txt, search and replace + forEach(config, f) { + if (!isBlankS(*f)) { + if (*f[0] != '#') { + iTrimS(f); + char **src = readText(*f); + if (!src) { + printf("Cant read file %s", *f); + printf("\n"); + continue; + } + + // scan lines + forEach(src, l) { + range(i, nSearch) { + //iReplaceS_max(l, *a, func_replace_with[i]); + replaceWithPcre(l, reCompiled[i], pcreExtra[i], getG(func_search_what, rtChar, i), getG(func_replace_with, rtChar, i), getG(a, rtSmallArrayt, i)); + } + } + + bool status = writeText(*f, src); + //bool status = false; + if (status) { + printf("CHANGED: %s", *f); + printf("\n"); + } + else { + printf("DIDNT CHANGE: %s", *f); + printf("\n"); + } + } + } + } + + listFreeManyS(replace_configuration, search_what, replace_with, config, strRegex); + terminateManyG(func_search_what, func_replace_with, a); + + forEachType(pcre, reCompiled, e) { + pcre_free(*e); + } + free(reCompiled); + + if (pcreExtra[0]) { + forEachType(pcre_extra, pcreExtra, e) { + #ifdef PCRE_CONFIG_JIT + pcre_free_study(*e); + #else + pcre_free(*e); + #endif + } + } + free(pcreExtra); +} + +void replaceWithPcre(char **l, pcre *reCompiled, pcre_extra *pcreExtra, char *func_search, char *replaceWith, smallArrayt *mappingNParams) { + int r; + int subStrVec[30]; + size_t len; + size_t offset = 0;; + int allMatch[100]; + size_t nMatch = 0;; + + len = strlen(*l); + r = pcre_exec(reCompiled, pcreExtra, *l, len, 0, 0, subStrVec, 30); + + while ((offset < len) && (r >= 0)) { + + if (r == 0) { + printf("But too many substrings were found to fit in subStrVec!\n"); + r = 30/3; + } + + for (int j=0; j < r; j++) { + allMatch[nMatch] = subStrVec[j*2]; + allMatch[nMatch+1] = subStrVec[j*2+1]; + nMatch += 2; + } + + offset = subStrVec[1]; + r = pcre_exec(reCompiled, pcreExtra, *l, len, offset, 0, subStrVec, 30); + } + + if (!nMatch) { + return; + } + + /* logVarG(*l); */ + for (int i = nMatch ; i > 0 ; i-=2) { + /* logVarG(*l); */ + /* logVarG(func_search); */ + /* logVarG(mappingNParams); */ + // split and change order + //puts(*l+allMatch[i-1]); + smallStringt *s = allocG(*l+allMatch[i-1]); + smallArrayt *extract = extractG(s, "(", ")"); + terminateG(s); + /* logVarG(extract); */ + if (extract) { + s = getG(extract, rtSmallStringt, 0); + /* logVarG(s); */ + smallArrayt *spl = splitG(s, ","); + smallArrayt *mapping = getG(mappingNParams, rtSmallArrayt, 1); + createAllocateSmallArray(newPara); + range(p, lenG(mapping)) { + pushG(newPara, getG(spl, rtSmallStringt, getG(mapping, rtI64, p))); + } + smallStringt *s2 = joinG(newPara, ","); + prependG(s, "("); + appendG(s, ")"); + prependG(s2, "("); + appendG(s2, ")"); + //logVarG(s); + //logVarG(s2); + replaceG(l, ssGet(s), ssGet(s2), 0); + smashG(extract); + finishManyG(mapping, spl); + terminateManyG(s, s2); + } + + // replace word + char *sBefore = emptySF();; + + if (allMatch[i-2]) { + // 0 means end of string + sBefore = sliceS(*l, 0, allMatch[i-2]); + } + + char *sAfter = sliceS(*l, allMatch[i-1], 0); + free(*l); + *l = catS(sBefore, replaceWith, sAfter); + freeManyS(sBefore, sAfter); +} + finishG(mappingNParams); + /* logVarG(*l); */ + } + +// ------------------------------------------------------------------------------------- + +#ifndef unitTest +// Remove main when running the unit tests +#define MAIN main +#endif +int MAIN(int ARGC, char** ARGV) { + int dum UNUSED; + + argc = ARGC; argv = ARGV;;// + + replace(); + + XSUCCESS; +} diff --git a/package.yml b/package.yml @@ -0,0 +1,18 @@ +--- + name: morph + version: 0.0.1 + description: morph command searches functions in files and replaces them with the given functions, when the parameters change order both function declaration and function calls are changed + bin: ./morph.c + lflags: -lpcre + repository: + type: git + url: git+https://github.com/RemyNoulin/morph.git + keywords: + - utility + - refactor + - command + author: Remy Noulin + license: MIT + bugs: + url: https://github.com/RemyNoulin/morph/issues + homepage: https://github.com/RemyNoulin/morph