From 5e97eeceb9dec727fe1bce20660069b5f69a5d8a Mon Sep 17 00:00:00 2001 From: hiddenalpha.ch Date: Sun, 30 Aug 2026 00:01:23 +0200 Subject: de-clutter whole project --- src/bulk_ln.c | 514 +++++++++++++++++++++++++++++++++++++++++++++ src/bulk_ln/bulk_ln.c | 429 ------------------------------------- src/bulk_ln/bulk_ln.h | 10 - src/bulk_ln/bulk_ln_main.c | 10 - src/common/commonbase.h | 23 -- 5 files changed, 514 insertions(+), 472 deletions(-) create mode 100644 src/bulk_ln.c delete mode 100644 src/bulk_ln/bulk_ln.c delete mode 100644 src/bulk_ln/bulk_ln.h delete mode 100644 src/bulk_ln/bulk_ln_main.c delete mode 100644 src/common/commonbase.h (limited to 'src') diff --git a/src/bulk_ln.c b/src/bulk_ln.c new file mode 100644 index 0000000..f3cda22 --- /dev/null +++ b/src/bulk_ln.c @@ -0,0 +1,514 @@ +#if 0 +--[===========================================================================[ + + How to build: + + lua src/bulk_ln.c + + How to use: + + ./bulk-ln --help + + ]===========================================================================] +function shEsc(s)return "'".. s:gsub("'", "'\"'\"'") .."'" end +do + local thisFile = debug.getinfo(1,"S").source:sub(2) + local sh = io.popen("sh", "w") + assert(sh, "Failed to launch a shell") + sh:write([=[set -e \ + \ + && CC="${CC:-gcc}" \ + && LD="${LD:-gcc}" \ + && OBJDUMP="${OBJDUMP:-objdump}" \ + && RIMRAF="${RIMRAF:-rm -rf}" \ + && BINEXT="${BINEXT:-}" \ + && CFLAGS="${CFLAGS:- -std=c99 -Wall -Werror -pedantic -nostdlib -nodefaultlibs}" \ + && LDFLAGS="${LDFLAGS:-}" \ + && TMPDIR="${TMPDIR:-/tmp}" \ + && OUTFILE="bulk-ln${BINEXT?}" \ + \ + && if test -z "$PROJECT_VERSION" ;then true \ + && tag="$(git tag|grep -E '^v'|sed -E 's,^v,,'|sort -V|tail -n1)" \ + && cnt="$(git rev-list --count "v${tag:?}"..HEAD)" \ + && hsh="$(git rev-parse --short HEAD)" \ + && if test -n "$tag" -a -n "$cnt" -a -n "$hsh" ;then true \ + && PROJECT_VERSION="${tag:?}-${cnt:?}-g${hsh:?}" \ + ;else true \ + && PROJECT_VERSION="$(date -u +0.0.0-%Y%m%d.%H%M%S)" \ + ;fi \ + ;fi \ + \ + && ${CC:?} -c -o "${TMPDIR:?}"/YC71NcBIJMwpwUfL ]=].. shEsc(thisFile) ..[=[ ${CFLAGS?} -DPROJECT_VERSION="${PROJECT_VERSION:?}" -DEXPOSE_MAIN=1 \ + && ${LD:?} -o ./"${OUTFILE:?}" "${TMPDIR:?}"/YC71NcBIJMwpwUfL ${LDFLAGS?} \ + && ${RIMRAF:?} "${TMPDIR:?}"/YC71NcBIJMwpwUfL \ + && bullshit=$(${OBJDUMP?} -p "${OUTFILE:?}"|grep DLL\ Name|egrep -v ' (KERNEL32.dll|SHELL32.dll|WS2_32.dll|ADVAPI32.dll|msvcrt.dll|GDI32.dll|USER32.dll)$'||true) \ + && if test -n "$bullshit"; then printf '\n ERROR: Bullshit has sneaked in:\n\n%s\n\n' "$bullshit"; rm "${OUTFILE:?}"; false; fi \ +]=]) + -- Shitty systems maybe need adaptions like: + -- && LDFLAGS="-Wl,-lws2_32,-l:libwinpthread.a" \ + local ok, how, code = sh:close() + if not ok then os.exit(code) end +end +--[===========================================================================[ +#endif + +#define _POSIX_C_SOURCE 200809L + +#include +#include +#include +#include +#include +#include +#include + +#define STR_QUOT_IAHGEWIH(s) #s +#define STR_QUOT(s) STR_QUOT_IAHGEWIH(s) + +#ifndef PRIVATE +# define PRIVATE(T) static T +#endif + +#ifndef DATA_FILE_FIELD_SEP_CHR +# define DATA_FILE_FIELD_SEP_CHR '\t' +#endif + + +typedef struct BulkLn { + /* Root context + */ + + char *dataFilePath; + /* File (path) where we read the paths-to-link from. The special + * value - (dash) means to use the stdin stream. + */ + + FILE *dataFd; + + int createdLinksCount; + /* Count of links we created + */ + + int createdDirsCount; + /* Count of direcrories we created + */ + + int dryRun; + /* If a dry-run got requested + */ + + int isPrintEachCreateLink; + /* If true, we will print to stdout for every link we create (like we do + * in dry run) + 0*/ + + int isPrintEachMkdir; + /* print every call we make to mkdir to stdout. Usually only useful for + * debugging + */ + + int isPrintStatus; + /* if true, we periodically print some kind of progress to stderr + */ + + int isPrintSummary; + /* if true, we print a summary what we did at the end of the run to stderr + */ + + int isRelinkExistingFiles; + /* if true, we will override existing files. This will be enabled for + * example if --force got specified. + */ +} BulkLn; + + + +PRIVATE(void) +printHelp(){ + printf(" \n %s @ " STR_QUOT(PROJECT_VERSION) "\n" + " \n" + " Takes paths (pairwise) from stdin (see --stdin for details) and\n" + " creates a hardlink for each pair from the 1st path to the 2nd.\n" + " \n" + " Writing a custom implementation of 'ln' got necessary as we found no\n" + " way to instruct 'ln' to create a few thousand links in an acceptable\n" + " amount of time. Remind that wheels that do not fit should be\n" + " re-invented (yes, no matter how much they already exist!). So now we\n" + " have it. Our re-invented wheel that finally fits our use-case ;) \n" + " \n" + " See also\n" + " https://pubs.opengroup.org/onlinepubs/009695399/utilities/ln.html\n" + " for info about the original 'ln'.\n" + " \n" + " Options:\n" + " \n" + " --stdin\n" + " Read the path pairs to link from stdin. The format is like:\n" + " \n" + " \n" + " \n" + " Example:\n" + " \n" + " origin/foo.txt\tnew/gugg.txt\n" + " origin/bar.txt\tnew/da.txt\n" + " \n" + " HINT: Preferred is LF. But CRLF should work too.\n" + " \n" + " --quiet\n" + " Don't print status or similar stuff. Errors will still be\n" + " printed to stderr.\n" + " \n" + " --verbose\n" + " Print stupid amount of logs. Usually only helpful for debugging.\n" + " Should NOT be combined with --quiet as this would be nonsense\n" + " anyway.\n" + " \n" + " --dry-run\n" + " Will print the actions to stdout instead executing them.\n" + " HINT: Directory count in summary will be inaccurate in this\n" + " mode.\n" + " \n" + " --force\n" + " Same meaning as in original 'ln' command.\n" + " \n", strrchr(__FILE__,'/')+1 ); +} + + +/** returns non-zero on errors. Error messages will already be printed + * internally. */ +PRIVATE(int) +parseArgs( int argc, char**argv, BulkLn*bulkLn ){ + /* init (aka set defaults) */ + bulkLn->dataFilePath = NULL; + bulkLn->dryRun = 0; + bulkLn->isPrintEachCreateLink = 0; + bulkLn->isPrintEachMkdir = 0; + bulkLn->isPrintStatus = !0; + bulkLn->isPrintSummary = !0; + bulkLn->isRelinkExistingFiles = 0; + + /* Parse args */ + for( int i = 1 ; i < argc ; ++i ){ + char *arg = argv[i]; + if( !strcmp(arg, "--help") ){ + printHelp(); + return -1; + } + else if( !strcmp(arg, "--dry-run") ){ + bulkLn->dryRun = !0; + } + else if( !strcmp(arg, "--force") ){ + bulkLn->isRelinkExistingFiles = !0; + } + else if( !strcmp(arg, "--quiet") ){ + bulkLn->isPrintStatus = 0; + bulkLn->isPrintSummary = 0; + } + else if( !strcmp(arg, "--stdin") ){ + bulkLn->dataFilePath = "-"; + } + else if( !strcmp(arg, "--verbose") ){ + bulkLn->isPrintStatus = !0; + bulkLn->isPrintSummary = !0; + bulkLn->isPrintEachCreateLink = !0; + bulkLn->isPrintEachMkdir = !0; + } + else{ + fprintf(stderr, "EINVAL: '%s'\n", arg); + return -1; + } + } + + /* MUST specify input method. Yes there is only one input method. But + * requiring args is the simplest way to prevent damage in case someone (eg + * accidentally) invokes the utility wihout args. Further this also makes + * the utility easier to extend wihout breaking everything. */ + if( bulkLn->dataFilePath == NULL ){ + fprintf(stderr, "EINVAL: '--stdin' missing.\n"); + return -1; + } + + return 0; +} + + +/** + * Like: mkdirs -p path + * + * WARN: Passed 'path' might be temporarily changed during execution. + * Nevertheless will be in original state after return. + */ +PRIVATE(int) +mkdirs( char*path, BulkLn*bulkLn ){ + int err; + char *tmpEnd = path; + /* Backup original length so we later still can recognize the original end + * as we are going to place zeroes in the path during the run. */ + char *pathEnd = path + strlen(path); + + for(;;){ + /* Loop for each slash in the path beginning from the topmost dir toward the + * innermost dir. + * This way our path gets longer by one segment each time we call 'mkdir' below. + * This is to have the same effect as we would call 'mkdir --parents' from cli. + * In other words we make sure every parent dir exists before creating the next + * one */ + + tmpEnd = strchr(tmpEnd + 1, '/'); + if( tmpEnd == NULL ){ + /* The last (innermost) segment to create a dir for */ + tmpEnd = pathEnd; + } + + /* Temporarily zero-terminate the path so we can create the parent dir + * up to there */ + tmpEnd[0] = '\0'; + + /* Print if requested */ + if( bulkLn->dryRun || bulkLn->isPrintEachMkdir ){ + printf("mkdir(\"%s\")\n", path); + } + + /* Create dir up to that found path */ + if( ! bulkLn->dryRun ){ + /* Perform the real action */ + /* mode gets masked by umask and after that we have the defaults + * (aka what we want) */ + err = mkdir(path, 0777); + if( err ){ + if( errno == EEXIST ){ + /* Fine :) So just continue with the next one. */ + }else{ + fprintf(stderr, "mkdir(\"%s\"): %s\n", path, strerror(errno)); + err = -1; goto resolveWithErr; + } + }else{ + /* Only increment the directory counter if we really created + * the dir. Eg if it did NOT already exist */ + bulkLn->createdDirsCount += 1; + } + }else{ + /* We cannot really count how many dirs we created. Because maybe + * we are (hypothetically) creating this directory the 100th time + * now. But we have no way to tell as we do not really create em. + * So just increment to have some value at all */ + bulkLn->createdDirsCount += 1; + } + + if( tmpEnd == pathEnd ){ + /* Nothing to restore as we point to end-of-string. + * This also means we're done. So end the loop */ + break; + }else{ + /* Restore where we did cut-off the path */ + tmpEnd[0] = '/'; + /* Then loop to add and process one more segment */ + } + } + + err = 0; +resolveWithErr: + return err; +} + + +/** Like: ln srcPath dstPath */ +PRIVATE(int) +createHardlink( char*srcPath, char*dstPath, BulkLn*bulkLn ){ + int err; + + if( bulkLn->dryRun || bulkLn->isPrintEachCreateLink ){ + printf("link(\"%s\", \"%s\")\n", srcPath, dstPath); + } + + if( ! bulkLn->dryRun ){ + /* Perform the real action */ + if( bulkLn->isRelinkExistingFiles ){ + /* Delete beforehand so we can be sure 'link' does not fail due + * already existing file */ + err = unlink(dstPath); + if( err ){ + if( errno == ENOENT ){ + /* There is no such entry we could delete. So we're already + * fine :) */ + }else{ + /* Some other (unepxected) error */ + fprintf(stderr, "unlink(%s): %s\n", dstPath, strerror(errno)); + err = -1; goto resolveWithErr; + } + } + } + err = link(srcPath, dstPath); + if( err ){ + fprintf(stderr, "link(\"%s\", \"%s\"): %s\n", srcPath, dstPath, strerror(errno)); + err = -1; goto resolveWithErr; + } + } + + bulkLn->createdLinksCount += 1; + + err = 0; +resolveWithErr: + return err; +} + + +PRIVATE(int) +onPathPair( char*srcPath, char*dstPath, BulkLn*bulkLn ){ + assert(srcPath != NULL); + assert(dstPath != NULL); + assert(bulkLn != NULL); + int err; + + if( bulkLn->isPrintStatus && bulkLn->createdLinksCount % 10000 == 0 ){ + fprintf(stderr, "Created %7d links so far.\n", bulkLn->createdLinksCount); + } + + /* Search end of parent dir path */ + char *tmpEnd = strrchr(dstPath, '/'); + if( tmpEnd != NULL ){ + /* Temporarily cut-off the last segment (filename) to create the + * parent-dirs */ + tmpEnd[0] = '\0'; + /* Create missing parent dirs */ + err = mkdirs(dstPath, bulkLn); + /* Restore path */ + tmpEnd[0] = '/'; + if( err ){ err = -1; goto resolveWithErr; } + } + + err = createHardlink(srcPath, dstPath, bulkLn); + if( err ){ err = -1; goto resolveWithErr; } + + err = 0; +resolveWithErr: + return err; +} + + +PRIVATE(int) +parseDataFileAsPairPerLine( BulkLn*bulkLn ){ + int err; + size_t buf_cap = 0; + size_t buf_len = 0; + char *buf = NULL; + size_t lineNum = 0; + + for(;;){ + lineNum += 1; + + /* Read input line-by-line. Not the most elegant way to parse stuff, + * but should suffice for our use-case */ + err = getline(&buf, &buf_cap, bulkLn->dataFd); + if( err < 0 ){ + /* Error handling */ + if( feof(bulkLn->dataFd) ){ + break; /* End-Of-File. Just break off the loop */ + }else if( ferror(bulkLn->dataFd) ){ + fprintf(stderr, "getline(%s): %s\n", bulkLn->dataFilePath, strerror(errno)); + err = -1; goto resolveWithErr; + }else{ + abort(); /* I don't know how this could happen */ + } + } + buf_len = err; + + /* Extract the two paths from our line */ + char *srcPath = buf; + char *tab = memchr(buf, DATA_FILE_FIELD_SEP_CHR, buf_len); + if( tab == NULL ){ + fprintf(stderr, "Too few field separators (tab) in '%s' @ %lu\n", + bulkLn->dataFilePath, lineNum); + err = -1; goto resolveWithErr; + } + char *unwantedTab = memchr(tab + 1, DATA_FILE_FIELD_SEP_CHR, buf_len - (tab + 1 - buf)); + if( unwantedTab != NULL ){ + fprintf(stderr, "Too many field separators (tab) in '%s' line %lu\n", + bulkLn->dataFilePath, lineNum); + err = -1; goto resolveWithErr; + } + char *dstPath = tab + 1; /* <- path starts one char after the separator */ + char *dstPath_end = buf + buf_len; + for(;; --dstPath_end ){ + if( dstPath_end < buf ){ + fprintf(stderr, "IMHO cannot happen %s:%d\n", __FILE__, __LINE__); + err = -1; goto resolveWithErr; + } + if( dstPath_end[0]=='\n' || dstPath_end[0]=='\0' || dstPath_end[0]=='\r' ){ + continue; /* last char not found yet */ + } + /* 'dstPath_end' now points to the last char of our line. So add + * one to point to the 'end' */ + dstPath_end += 1; + break; + } + + /* Zero-Terminate the two strings */ + tab[0] = '\0'; + dstPath_end[0] = '\0'; + + /* Publish this pair for processing */ + err = onPathPair(srcPath, dstPath, bulkLn); + if( err ){ err = -1; goto resolveWithErr; } + } + + if( bulkLn->isPrintStatus ){ + fprintf(stderr, "Parsed %lu records from '%s'\n", lineNum - 1, bulkLn->dataFilePath); + } + + err = 0; +resolveWithErr: + free(buf); + return err; +} + + +int +bulk_ln_main( int argc, char**argv ){ + int err; + BulkLn bulkLn = {0}; + #define bulkLn (&bulkLn) + + err = parseArgs(argc, argv, bulkLn); + if( err ){ err = -1; goto resolveWithErr; } + + /* Open data source */ + if( !strcmp(bulkLn->dataFilePath, "-") ){ + bulkLn->dataFd = stdin; + }else{ + bulkLn->dataFd = fopen(bulkLn->dataFilePath, "rb"); + if( bulkLn->dataFd == NULL ){ + fprintf(stderr, "fopen(%s): %s\n", bulkLn->dataFilePath, strerror(errno)); + err = -1; goto resolveWithErr; + } + } + + err = parseDataFileAsPairPerLine(bulkLn); + if( err ){ err = -1; goto resolveWithErr; } + + if( bulkLn->isPrintSummary ){ + fprintf(stderr, "Created %d directories and linked %d files.\n", + bulkLn->createdDirsCount, bulkLn->createdLinksCount); + } + + err = 0; +resolveWithErr: + if( bulkLn->dataFd != NULL && bulkLn->dataFd != stdin ){ + fclose(bulkLn->dataFd); + } + return err; + #undef bulkLn +} + + +#if EXPOSE_MAIN +int +main( int argc, char**argv ){ + argc = bulk_ln_main(argc, argv); + if( argc < 0 ) argc = -argc; + return (argc >= 0 && argc <= 0x7F) ? argc : 1; +} +#endif + + +/*]===========================================================================]--*/ diff --git a/src/bulk_ln/bulk_ln.c b/src/bulk_ln/bulk_ln.c deleted file mode 100644 index ffd360a..0000000 --- a/src/bulk_ln/bulk_ln.c +++ /dev/null @@ -1,429 +0,0 @@ - -/* This */ -#include "bulk_ln.h" - -/* System */ -#include -#include -#include -#include -#include -#include -#include - - -#define DATA_FILE_FIELD_SEP_CHR '\t' - -typedef int bool; -typedef struct BulkLn BulkLn; - - -/* Root context */ -struct BulkLn { - - /** File (path) where we read the paths-to-link from. The special - * value - (dash) means to use the stdin stream. */ - char *dataFilePath; - - FILE *dataFd; - - /** Count of links we created */ - int createdLinksCount; - - /** Count of direcrories we created */ - int createdDirsCount; - - /** If a dry-run got requested */ - bool dryRun; - - /** If true, we will print to stdout for every link we create (like we do - * in dry run) */ - bool isPrintEachCreateLink; - - /** print every call we make to mkdir to stdout. Usually only useful for - * debugging */ - bool isPrintEachMkdir; - - /** if true, we periodically print some kind of progress to stderr */ - bool isPrintStatus; - - /** if true, we print a summary what we did at the end of the run to stderr */ - bool isPrintSummary; - - /** if true, we will override existing files. This will be enabled for - * example if --force got specified. */ - bool isRelinkExistingFiles; -}; - - - -static void printHelp(){ - printf(" \n %s @ " STR_QUOT(PROJECT_VERSION) "\n" - " \n" - " Takes paths (pairwise) from stdin (see --stdin for details) and\n" - " creates a hardlink for each pair from the 1st path to the 2nd.\n" - " \n" - " Writing a custom implementation of 'ln' got necessary as we found no\n" - " way to instruct 'ln' to create a few thousand links in an acceptable\n" - " amount of time. Remind that wheels that do not fit should be\n" - " re-invented (yes, no matter how much they already exist!). So now we\n" - " have it. Our re-invented wheel that finally fits our use-case ;) \n" - " \n" - " See also\n" - " https://pubs.opengroup.org/onlinepubs/009695399/utilities/ln.html\n" - " for info about the original 'ln'.\n" - " \n" - " Options:\n" - " \n" - " --stdin\n" - " Read the path pairs to link from stdin. The format is like:\n" - " \n" - " \n" - " \n" - " Example:\n" - " \n" - " origin/foo.txt\tnew/gugg.txt\n" - " origin/bar.txt\tnew/da.txt\n" - " \n" - " HINT: Preferred is LF. But CRLF should work too.\n" - " \n" - " --quiet\n" - " Don't print status or similar stuff. Errors will still be\n" - " printed to stderr.\n" - " \n" - " --verbose\n" - " Print stupid amount of logs. Usually only helpful for debugging.\n" - " Should NOT be combined with --quiet as this would be nonsense\n" - " anyway.\n" - " \n" - " --dry-run\n" - " Will print the actions to stdout instead executing them.\n" - " HINT: Directory count in summary will be inaccurate in this\n" - " mode.\n" - " \n" - " --force\n" - " Same meaning as in original 'ln' command.\n" - " \n", strrchr(__FILE__,'/')+1 ); -} - - -/** returns non-zero on errors. Error messages will already be printed - * internally. */ -static int parseArgs( int argc, char**argv, BulkLn*bulkLn ){ - /* init (aka set defaults) */ - bulkLn->dataFilePath = NULL; - bulkLn->dryRun = 0; - bulkLn->isPrintEachCreateLink = 0; - bulkLn->isPrintEachMkdir = 0; - bulkLn->isPrintStatus = !0; - bulkLn->isPrintSummary = !0; - bulkLn->isRelinkExistingFiles = 0; - - // Parse args - for( int i = 1 ; i < argc ; ++i ){ - char *arg = argv[i]; - if( !strcmp(arg, "--help") ){ - printHelp(); - return -1; - } - else if( !strcmp(arg, "--dry-run") ){ - bulkLn->dryRun = !0; - } - else if( !strcmp(arg, "--force") ){ - bulkLn->isRelinkExistingFiles = !0; - } - else if( !strcmp(arg, "--quiet") ){ - bulkLn->isPrintStatus = 0; - bulkLn->isPrintSummary = 0; - } - else if( !strcmp(arg, "--stdin") ){ - bulkLn->dataFilePath = "-"; - } - else if( !strcmp(arg, "--verbose") ){ - bulkLn->isPrintStatus = !0; - bulkLn->isPrintSummary = !0; - bulkLn->isPrintEachCreateLink = !0; - bulkLn->isPrintEachMkdir = !0; - } - else{ - fprintf(stderr, "EINVAL: '%s'\n", arg); - return -1; - } - } - - /* MUST specify input method. Yes there is only one input method. But - * requiring args is the simplest way to prevent damage in case someone (eg - * accidentally) invokes the utility wihout args. Further this also makes - * the utility easier to extend wihout breaking everything. */ - if( bulkLn->dataFilePath == NULL ){ - fprintf(stderr, "EINVAL: '--stdin' missing.\n"); - return -1; - } - - return 0; -} - - -/** - * Like: mkdirs -p path - * - * WARN: Passed 'path' might be temporarily changed during execution. - * Nevertheless will be in original state after return. - */ -static int mkdirs( char*path, BulkLn*bulkLn ){ - int err; - char *tmpEnd = path; - /* Backup original length so we later still can recognize the original end - * as we are going to place zeroes in the path during the run. */ - char *pathEnd = path + strlen(path); - - for(;;){ - /* Loop for each slash in the path beginning from the topmost dir toward the - * innermost dir. - * This way our path gets longer by one segment each time we call 'mkdir' below. - * This is to have the same effect as we would call 'mkdir --parents' from cli. - * In other words we make sure every parent dir exists before creating the next - * one */ - - tmpEnd = strchr(tmpEnd + 1, '/'); - if( tmpEnd == NULL ){ - /* The last (innermost) segment to create a dir for */ - tmpEnd = pathEnd; - } - - /* Temporarily zero-terminate the path so we can create the parent dir - * up to there */ - tmpEnd[0] = '\0'; - - /* Print if requested */ - if( bulkLn->dryRun || bulkLn->isPrintEachMkdir ){ - printf("mkdir(\"%s\")\n", path); - } - - /* Create dir up to that found path */ - if( ! bulkLn->dryRun ){ - /* Perform the real action */ - /* mode gets masked by umask and after that we have the defaults - * (aka what we want) */ - err = mkdir(path, 0777); - if( err ){ - if( errno == EEXIST ){ - /* Fine :) So just continue with the next one. */ - }else{ - fprintf(stderr, "mkdir(\"%s\"): %s\n", path, strerror(errno)); - err = -1; goto finally; - } - }else{ - /* Only increment the directory counter if we really created - * the dir. Eg if it did NOT already exist */ - bulkLn->createdDirsCount += 1; - } - }else{ - /* We cannot really count how many dirs we created. Because maybe - * we are (hypothetically) creating this directory the 100th time - * now. But we have no way to tell as we do not really create em. - * So just increment to have some value at all */ - bulkLn->createdDirsCount += 1; - } - - if( tmpEnd == pathEnd ){ - /* Nothing to restore as we point to end-of-string. - * This also means we're done. So end the loop */ - break; - }else{ - /* Restore where we did cut-off the path */ - tmpEnd[0] = '/'; - /* Then loop to add and process one more segment */ - } - } - - err = 0; -finally: - return err; -} - - -/** Like: ln srcPath dstPath */ -static int createHardlink( char*srcPath, char*dstPath, BulkLn*bulkLn ){ - int err; - - if( bulkLn->dryRun || bulkLn->isPrintEachCreateLink ){ - printf("link(\"%s\", \"%s\")\n", srcPath, dstPath); - } - - if( ! bulkLn->dryRun ){ - /* Perform the real action */ - if( bulkLn->isRelinkExistingFiles ){ - /* Delete beforehand so we can be sure 'link' does not fail due - * already existing file */ - err = unlink(dstPath); - if( err ){ - if( errno == ENOENT ){ - /* There is no such entry we could delete. So we're already - * fine :) */ - }else{ - /* Some other (unepxected) error */ - fprintf(stderr, "unlink(%s): %s\n", dstPath, strerror(errno)); - err = -1; goto finally; - } - } - } - err = link(srcPath, dstPath); - if( err ){ - fprintf(stderr, "link(\"%s\", \"%s\"): %s\n", srcPath, dstPath, strerror(errno)); - err = -1; goto finally; - } - } - - bulkLn->createdLinksCount += 1; - - err = 0; -finally: - return err; -} - - -static int onPathPair( char*srcPath, char*dstPath, BulkLn*bulkLn ){ - assert(srcPath != NULL); - assert(dstPath != NULL); - assert(bulkLn != NULL); - int err; - - if( bulkLn->isPrintStatus && bulkLn->createdLinksCount % 10000 == 0 ){ - fprintf(stderr, "Created %7d links so far.\n", bulkLn->createdLinksCount); - } - - /* Search end of parent dir path */ - char *tmpEnd = strrchr(dstPath, '/'); - if( tmpEnd != NULL ){ - /* Temporarily cut-off the last segment (filename) to create the - * parent-dirs */ - tmpEnd[0] = '\0'; - /* Create missing parent dirs */ - err = mkdirs(dstPath, bulkLn); - /* Restore path */ - tmpEnd[0] = '/'; - if( err ){ err = -1; goto finally; } - } - - err = createHardlink(srcPath, dstPath, bulkLn); - if( err ){ err = -1; goto finally; } - - err = 0; -finally: - return err; -} - - -static int parseDataFileAsPairPerLine( BulkLn*bulkLn ){ - int err; - size_t buf_cap = 0; - size_t buf_len = 0; - char *buf = NULL; - size_t lineNum = 0; - - for(;;){ - lineNum += 1; - - /* Read input line-by-line. Not the most elegant way to parse stuff, - * but should suffice for our use-case */ - err = getline(&buf, &buf_cap, bulkLn->dataFd); - if(unlikely( err < 0 )){ - /* Error handling */ - if( feof(bulkLn->dataFd) ){ - break; /* End-Of-File. Just break off the loop */ - }else if( ferror(bulkLn->dataFd) ){ - fprintf(stderr, "getline(%s): %s\n", bulkLn->dataFilePath, strerror(errno)); - err = -1; goto finally; - }else{ - abort(); /* I don't know how this could happen */ - } - } - buf_len = err; - - /* Extract the two paths from our line */ - char *srcPath = buf; - char *tab = memchr(buf, DATA_FILE_FIELD_SEP_CHR, buf_len); - if( tab == NULL ){ - fprintf(stderr, "Too few field separators (tab) in '%s' @ %lu\n", - bulkLn->dataFilePath, lineNum); - err = -1; goto finally; - } - char *unwantedTab = memchr(tab + 1, DATA_FILE_FIELD_SEP_CHR, buf_len - (tab + 1 - buf)); - if( unwantedTab != NULL ){ - fprintf(stderr, "Too many field separators (tab) in '%s' line %lu\n", - bulkLn->dataFilePath, lineNum); - err = -1; goto finally; - } - char *dstPath = tab + 1; /* <- path starts one char after the separator */ - char *dstPath_end = buf + buf_len; - for(;; --dstPath_end ){ - if(unlikely( dstPath_end < buf )){ - fprintf(stderr, "IMHO cannot happen %s:%d\n", __FILE__, __LINE__); - err = -1; goto finally; - } - if( dstPath_end[0]=='\n' || dstPath_end[0]=='\0' || dstPath_end[0]=='\r' ){ - continue; /* last char not found yet */ - } - /* 'dstPath_end' now points to the last char of our line. So add - * one to point to the 'end' */ - dstPath_end += 1; - break; - } - - /* Zero-Terminate the two strings */ - tab[0] = '\0'; - dstPath_end[0] = '\0'; - - /* Publish this pair for processing */ - err = onPathPair(srcPath, dstPath, bulkLn); - if( err ){ err = -1; goto finally; } - } - - if( bulkLn->isPrintStatus ){ - fprintf(stderr, "Parsed %lu records from '%s'\n", lineNum - 1, bulkLn->dataFilePath); - } - - err = 0; -finally: - free(buf); - return err; -} - - -int bulk_ln_main( int argc, char**argv ){ - int err; - BulkLn bulkLn = {0}; - #define bulkLn (&bulkLn) - - err = parseArgs(argc, argv, bulkLn); - if( err ){ err = -1; goto finally; } - - /* Open data source */ - if( !strcmp(bulkLn->dataFilePath, "-") ){ - bulkLn->dataFd = stdin; - }else{ - bulkLn->dataFd = fopen(bulkLn->dataFilePath, "rb"); - if( bulkLn->dataFd == NULL ){ - fprintf(stderr, "fopen(%s): %s\n", bulkLn->dataFilePath, strerror(errno)); - err = -1; goto finally; - } - } - - err = parseDataFileAsPairPerLine(bulkLn); - if( err ){ err = -1; goto finally; } - - if( bulkLn->isPrintSummary ){ - fprintf(stderr, "Created %d directories and linked %d files.\n", - bulkLn->createdDirsCount, bulkLn->createdLinksCount); - } - - err = 0; -finally: - if( bulkLn->dataFd != NULL && bulkLn->dataFd != stdin ){ - fclose(bulkLn->dataFd); - } - return -err; - #undef bulkLn -} - diff --git a/src/bulk_ln/bulk_ln.h b/src/bulk_ln/bulk_ln.h deleted file mode 100644 index 6054386..0000000 --- a/src/bulk_ln/bulk_ln.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef INCGUARD_87768d11a9f7d831f2a8b9d0ab7efa9c -#define INCGUARD_87768d11a9f7d831f2a8b9d0ab7efa9c - -#include "commonbase.h" - - -int bulk_ln_main( int argc, char**argv ); - - -#endif /* INCGUARD_87768d11a9f7d831f2a8b9d0ab7efa9c */ diff --git a/src/bulk_ln/bulk_ln_main.c b/src/bulk_ln/bulk_ln_main.c deleted file mode 100644 index a3ac228..0000000 --- a/src/bulk_ln/bulk_ln_main.c +++ /dev/null @@ -1,10 +0,0 @@ - -#include "bulk_ln.h" - -int main( int argc, char**argv ){ - int err = bulk_ln_main(argc, argv); - /* Ensure to return 7-bit value only (See POSIX) */ - if( err < 0 ) err = -err; - return err > 127 ? 1 : err; -} - diff --git a/src/common/commonbase.h b/src/common/commonbase.h deleted file mode 100644 index 497d3fa..0000000 --- a/src/common/commonbase.h +++ /dev/null @@ -1,23 +0,0 @@ - -/* - * common config for project. Here goes stuff like feature-test-macros etc. - * - * Every header file MUST include this file AS THE 1ST include. - */ - - -#define _POSIX_C_SOURCE 200809L - - -#define STR_QUOT_IAHGEWIH(s) #s -#define STR_QUOT(s) STR_QUOT_IAHGEWIH(s) - -#define STR_CAT(a, b) a ## b - -#ifndef likely -# define likely(a) (a) -#endif -#ifndef unlikely -# define unlikely(a) (a) -#endif - -- cgit v1.3.1