git-off

Log

Files

Refs

README

gitoff.c (86037B)

     1 // TODO
     2 // use the path function
     3 // handle wrong config like getLog
     4 // check parameters from CLI
     5 //
     6 // parse params
     7 // use logger
     8 //
     9 // How to add a new config
    10 //   add variable in runtimeConfig
    11 //   add default settings in offDEFAULTS
    12 //   add a helper in offHelpers
    13 //   add setting of default in offCommands.install
    14 //   add a command for setting value in offCommands
    15 //   print value in offCommands.env
    16 //   add command in CLI parser
    17 //   update help
    18 //
    19 // How to add a new transport
    20 //   add transport functions in offHelpers.setTransport
    21 //   add transport in offHelpers.mkdirStore and offHelpers.rmAllStore
    22 //   add transport in offCommands.clearAll
    23 //   update git off mode help
    24 //   if needed, add store creation in offCommands.install
    25 //
    26 // How to add a new command
    27 //   add command in offCommands
    28 //   add command in CLI parser
    29 //   update help
    30 //
    31 //////
    32 // CODE
    33 //
    34 // modules
    35 // defaults
    36 // helpers
    37 // core
    38 // main
    39 //
    40 //
    41 // modules
    42 //   all dependencies
    43 //
    44 // defaults
    45 //   objInfo fields:  for indexing cat-diff response in push command
    46 //   externalHelpers: shell commands to be used with the exec function
    47 //   runtimeConfig:   config built by offHelpers
    48 //   offDEFAULTS:     default configuration for first time install
    49 //
    50 // helpers
    51 //   expandHome:      expands ~/
    52 //   gitConfig:       handles global git config
    53 //   offLog:          appends log to git config off.log
    54 //   offLogRepo:      log for commands run in git repo
    55 //   exec:            runs an externalHelpers with parameters
    56 //   mkdirParents:    recursive mkdir
    57 //   rmAll:           delete recursively files and directories
    58 //   copy:            copies files
    59 //   offHelpers:      git-off helpers
    60 //
    61 // core
    62 //   transport:       transport functions for git off store
    63 //   offCommands:     command line functions
    64 //
    65 // main
    66 //   parse CLI arguments
    67 //////
    68 
    69 //////
    70 // modules
    71 //////
    72 // includes and function prototypes
    73  #ifdef unitTest
    74 // gitoff.h is used in unit tests
    75 #include "gitoff.h"
    76  #else
    77 typedef void (*gConfig_set_t)(char*, char*);
    78 typedef void (*cmdSetF_t)(gConfig_set_t);
    79  #endif
    80 
    81 #define internal static
    82 
    83 #include <sys/stat.h>
    84 #include <inttypes.h>
    85 #include <stdio.h>
    86 #include <dirent.h>
    87 #include <libgen.h>
    88 #if (!__OpenBSD__)
    89 #include <wordexp.h>
    90 #endif
    91 #include <stdbool.h>
    92 #include <string.h>
    93 #include <stdlib.h>
    94 #include <unistd.h>
    95 #include <stdint.h>
    96 
    97 char *expandHome(char *p);
    98 void offLog(char* s);
    99 void offLogRepo(char* s);
   100 char *exec(int cmdName, char* paramsArray);
   101 bool strEq(char *string1, char *string2);
   102 void gitConfig_set(char* key, char* value);
   103 void gitConfig_setLocal(char* key, char* value);
   104 void gitConfig_setThisRepo(char* key, char* value);
   105 char *gitConfig_getDefault(char* key);
   106 char *gitConfig_get(char *key);
   107 void mkdirParents(char* p);
   108 void rmAll(char* p);
   109 void copy(char* src, char* dst);
   110 char **execOut(char *cmd);
   111 char **walkDir(char* dir);
   112 void freeList(void **list);
   113 char **split(char *string, char* delim);
   114 char *join(char **list, char* delim);
   115 char *offHelpers_gitRepoRoot();
   116 char *offHelpers_objectPath();
   117 char *offHelpers_offStore();
   118 char *offHelpers_offHttp();
   119 char *offHelpers_offCurlOptions();
   120 char *offHelpers_offMode();
   121 char *offHelpers_offIntegrity();
   122 char *offHelpers_offPem();
   123 char *offHelpers_offSshOptions();
   124 char *offHelpers_offScpOptions();
   125 char *offHelpers_offRsyncOptions();
   126 char *offHelpers_offScp();
   127 char *offHelpers_offScpUser();
   128 char *offHelpers_log();
   129 char *offHelpers_getLog();
   130 char *offHelpers_offConfigAlways();
   131 char *offHelpers_s3Region();
   132 char *offHelpers_s3Bucket();
   133 char *offHelpers_transform();
   134 char *offHelpers_transformTo();
   135 char *offHelpers_transformFrom();
   136 char *offHelpers_userAt();
   137 char **offHelpers_getSSHConfig();
   138 void offHelpers_mkdirStore(char *p);
   139 void offHelpers_rmAllStore(char *p);
   140 void offHelpers_copyTo();
   141 bool offHelpers_checkIntegrity(char *p);
   142 void transportCopySend(char *file);
   143 void transportCopyReceive(char *file);
   144 void transportRsyncSend(char *file);
   145 void transportRsyncReceive(char *file);
   146 void transportScpSend(char *file);
   147 void transportScpReceive(char *file);
   148 void transportHttpSend(char *file);
   149 void transportHttpReceive(char *file);
   150 void offHelpers_setTransport(char *mode );
   151 #define offHelpers_setTransport_mode() offHelpers_setTransport( "config")
   152 char **offHelpers_getOffFilePath(char *offFile);
   153 void send(char *src);
   154 void receive(char *src);
   155 void transport_transformFrom(char *file);
   156 void thisrepo(cmdSetF_t cmd);
   157 int findCommand(char *p);
   158 void showAllCommandsHelp();
   159 void offCommands_localSetup();
   160 void offCommands_install(gConfig_set_t setF );
   161 #define offCommands_install_setF() offCommands_install( gitConfig_set)
   162 void offCommands_track();
   163 void offCommands_configAlways();
   164 void offCommands_setGetGitConfig(gConfig_set_t setF);
   165 void offCommands_clean();
   166 void offCommands_prepush();
   167 void offCommands_push(char *line);
   168 void offCommands_smudge();
   169 void offCommands_copyTo();
   170 void offCommands_pushTo();
   171 void offCommands_clearAll();
   172 void offCommands_clearCache();
   173 void offCommands_clearStore();
   174 void offCommands_clearTmp();
   175 void offCommands_defaults();
   176 void offCommands_env();
   177 void offCommands_help();
   178 void installF();
   179 void modeF();
   180 void storeF();
   181 void scpF();
   182 void httpF();
   183 void curlF();
   184 void integrityF();
   185 void pemF();
   186 void sshoptionsF();
   187 void scpoptionsF();
   188 void rsyncoptionsF();
   189 void scpuserF();
   190 void trackF();
   191 void configAlwaysF();
   192 void s3regionF();
   193 void s3bucketF();
   194 void transformF();
   195 void transformToF();
   196 void transformFromF();
   197 void cleanF();
   198 void prepushF();
   199 void smudgeF();
   200 void copyToF();
   201 void pushF();
   202 void clearAllF();
   203 void caF();
   204 void clearCacheF();
   205 void ccF();
   206 void clearStoreF();
   207 void csF();
   208 void clearTmpF();
   209 void ctF();
   210 void defaultsF();
   211 void envF();
   212 void helpF();
   213 void initCOMMAND_FUNC();
   214 int MAIN(int ARGC, char** ARGV);
   215 
   216 int argc; char **argv;
   217 
   218 //////
   219 // defaults
   220 //////
   221 
   222 // objInfo fields for indexing cat-diff response in push command
   223 #define oiPREVIOUSPERMISSIONS   0
   224 #define oiPERMISSIONS           1
   225 #define oiPREVIOUSOID           2
   226 #define oiOID                   3
   227 #define oiNAME                  4
   228 
   229 // shell commands to be used with the exec function
   230 // example: exec 'gitRepoRoot'
   231 enum { gitConfigGlobal, gitConfig, gitRepoRoot, gitList, gitDff, gitCat, sha, listAttr, ssh, scp, rsync, curl, mv, mkdirE, cp, rm };
   232 char* externalHelpers[16] = { "git config --global", "git config", "git rev-parse --show-toplevel", "git rev-list", "git show --raw --format=\"\" --no-abbrev", "git cat-file -p", "git hash-object --no-filters", "git check-attr -a", "ssh", "scp", "rsync", "curl", "mv", "mkdir -p", "cp -u", "rm -rf" };
   233 
   234 // config built by offHelpers
   235 // use offHelpers to access runtimeConfig
   236 enum { currentRepoRoot, objectPath, offStore, offHttp, offMode, offIntegrity, offScp, offScpUser, offPem, offSshOptions, offScpOptions, offRsyncOptions, offCurlOptions, log, offConfigAlways, s3Region, s3Bucket, transform, transformTo, transformFrom, scpHost, scpPath, scpPort };
   237 char* runtimeConfig[23] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
   238 
   239 // default configuration for first time install
   240 // objectPath is git off cache in repo
   241 enum { objectPathD, modeD, integrityD, pemD, scpHostD, scpUserD, sshOptionsD, scpOptionsD, rsyncOptionsD, storeD, httpD, curlOptionsD, logD, configAlwaysD, s3RegionD, s3BucketD, transformD, transformToD, transformFromD, prePushD, offSignatureD, lastD };
   242 char* offDEFAULTS[22] = { "/.git/off/objects", "copy", "disable", "offNoValue", "offNoValue", "", "-C -o StrictHostKeyChecking=no -o ConnectTimeout=3", "-C -o StrictHostKeyChecking=no -o ConnectTimeout=3 -p", "-az -e \"ssh -i _i\"", "~/.git-off/offStore", "offNoValue", "-o", "~/.git-off/log", "offNoValue", "offNoValue", "offNoValue", "disable", "pbzip2 -9 -c _1 > _2", "pbzip2 -d -c _1 > _2", "#!/bin/sh\ncommand -v git-off >/dev/null 2>&1 || { echo >&2 \"\\nThis repository is configured for Git off but \"git-off\" was not found on your path. If you no longer wish to use git off, remove this hook by deleting .git/hooks/pre-push.\\n\"; exit 2; }\ngit off pre-push \"$@\"", "### git-off v1 sha:", "not used" };
   243 
   244 #define offDEFAULTS_shaLength   40
   245 
   246 //////
   247 // helpers
   248 //////
   249 
   250 #if (!__OpenBSD__)
   251 // expands ~/
   252 char *expandHome(char *p) {
   253   wordexp_t exp_result;
   254   size_t len;
   255 
   256   if (p == NULL) {
   257     return(NULL);
   258   }
   259   wordexp(p, &exp_result, 0);
   260   p = realloc(p, strlen(exp_result.we_wordv[0])+1);
   261   strcpy(p, exp_result.we_wordv[0]);
   262   wordfree(&exp_result);
   263   return(p);
   264 }
   265 #else
   266 #define and &&
   267 #define or ||
   268 #define not !
   269 #define elif else if
   270 #define TOKENPASTE2(a, b) a ## b
   271 #define TOKENPASTE(a, b) TOKENPASTE2(a, b)
   272 #define UNIQVAR(name) TOKENPASTE(name, __LINE__)
   273 #define forEachS(list, element) \
   274   ;size_t UNIQVAR(libsheepyInternalIndex) = 0; \
   275   for (char *element = list[0]; list[UNIQVAR(libsheepyInternalIndex)]!= NULL ; UNIQVAR(libsheepyInternalIndex)++, element = list[UNIQVAR(libsheepyInternalIndex)])
   276 char *findS(const char *string, const char *needle) {
   277 
   278   // sanity checks
   279   if (!string || !needle) {
   280     return(NULL);
   281   }
   282   return(strstr(string, needle));
   283 }
   284 char *strLCpy(char *dst, size_t dstSize, const char *src) {
   285 
   286   if (!dst || !src) {
   287     return(NULL);
   288   }
   289 
   290   char *r = strncpy(dst, src, dstSize);;
   291   if (dstSize) {
   292     r[dstSize-1] = 0;
   293   }
   294   return(r);
   295 }
   296 char *strLNCat(char *dst, size_t dstSize, const char *src, size_t srcLen) {
   297 
   298   if (!dst || !src) {
   299     return(NULL);
   300   }
   301 
   302   size_t dL     = strnlen(dst, dstSize);
   303   size_t dstLen = dstSize-1;
   304 
   305   if (dL >= dstLen) {
   306     // buffer is full
   307     return(dst);
   308   }
   309 
   310   size_t sL     = strnlen(src, srcLen);
   311 
   312   if (dL+sL > dstLen) {
   313     // truncate
   314     return(strncat(dst, src, dstLen - dL));
   315   }
   316   else {
   317     return(strncat(dst, src, srcLen));
   318 }
   319   }
   320 #define forEachCharP(list, element) \
   321   for (char **element=list ; *element != NULL ; element++)
   322 size_t listLengthS(char **list) {
   323   size_t r = 0;;
   324 
   325   // sanity checks
   326   if (!list) {
   327     return(0);
   328   }
   329   forEachCharP(list, i) {
   330     r++;
   331   }
   332   return(r);
   333 }
   334 char **listPushS(char ***list, const char *s) {
   335 
   336   // sanity checks
   337   if (!list) {
   338     return(NULL);
   339   }
   340 
   341   if (!*list) {
   342     *list = malloc(2 * sizeof(char *));
   343     if (!s) {
   344       (*list)[0] = NULL;
   345     }
   346     else {
   347       (*list)[0] = strdup(s);
   348     }
   349     (*list)[1] = NULL;
   350   }
   351   else {
   352     // realloc list and copy s to last element
   353     size_t len;
   354     len  = listLengthS(*list);
   355     char **tmp = realloc(*list, (len+2) * sizeof(char *));
   356     if (!tmp) {
   357       return(NULL);
   358     }
   359     else {
   360       *list = tmp;
   361       (*list)[len+1] = NULL;
   362       if (!s) {
   363         // s is NULL, add NULL in list
   364         (*list)[len] = NULL;
   365       }
   366       else {
   367         (*list)[len] = strdup(s);
   368   }
   369     }
   370       }
   371   return(*list);
   372 }
   373 #define listEmptyS(list) \
   374   do {\
   375     list = malloc(1 * sizeof(char *)); \
   376     if (list) list[0] = NULL; \
   377   } while(0);
   378 char **readText(const char *filePath) {
   379   FILE *fp = NULL;
   380   size_t len;
   381   char* line = NULL;
   382   ssize_t read;
   383   char **list = NULL;
   384 
   385   // sanity checks
   386   if (!filePath) {
   387     return(NULL);
   388   }
   389   fp = fopen(filePath, "r");
   390   if (!fp) {
   391     //pFuncError
   392     //shEPrintfS("The path was: \"%s\"\n", filePath);
   393     return(NULL);
   394   }
   395   // read all lines
   396   read = getline(&line, &len, fp);
   397   while (read != -1) {
   398     {
   399         char* pos = NULL;
   400         pos = strchr(line, '\n');
   401         if (pos != NULL)
   402             *pos = '\0';
   403     }
   404     listPushS(&list, line);
   405     read = getline(&line, &len, fp);
   406   }
   407   fclose(fp);
   408   free(line);
   409   if (!list) {
   410     // nothing was read
   411     listEmptyS(list);
   412   }
   413   return(list);
   414 }
   415 bool startsWithS(const char *string1, const char *string2) {
   416 
   417   // sanity checks
   418   if (!string1 || !string2) {
   419     return(false);
   420   }
   421   return(strncmp(string1, string2, strlen(string2)) == 0);;
   422 }
   423 void listFreeS(char **list) {
   424 
   425   // sanity checks
   426   if (list) {
   427     forEachCharP(list, e) {
   428       free(*e);
   429     }
   430     free(list);
   431 }
   432   }
   433 char *iAppendS(char **string1, const char *string2) {
   434   char *tmp = NULL;
   435   size_t l1;
   436   size_t l2;
   437 
   438   if (!string1 || !string2) {
   439     return(NULL);
   440   }
   441 
   442   if (!*string1) {
   443     *string1 = strdup(string2);
   444     return(*string1);
   445   }
   446 
   447   l1 = strlen(*string1);
   448   l2 = strlen(string2);
   449 
   450   if (!l2) {
   451     // empty string
   452     return(*string1);
   453   }
   454 
   455   tmp = realloc(*string1, l1 + l2 + 1);
   456   if (!tmp) {
   457     return(NULL);
   458   }
   459 
   460   *string1 = tmp;
   461   strcat(*string1, string2);
   462   return(*string1);
   463 }
   464 char *expandHome(char *path) {
   465 
   466   // sanity checks
   467   if (!path) {
   468     return(NULL);
   469   }
   470 
   471 
   472   if (path[0] == 0) {
   473     // path is empty
   474     return(path);
   475   }
   476 
   477   // steps
   478   // determine path format
   479   // set user name, either current user or user name in path
   480   // add : at the end of username to match line in the /etc/passwd file
   481   // find home path in the passwd file
   482 
   483   enum {noExpand, currentUser, otherUser};
   484 
   485   // determine path format
   486   int status = noExpand;
   487   if (path[0] == '~' and (path[1] == '/' or path[1] == 0)) {
   488     // path="~/..."
   489     status = currentUser;
   490   }
   491   elif (path[0] == '~' and path[1] != '/') {
   492     // path="~USER..."
   493     status = otherUser;
   494   }
   495 
   496   // there is no tilde to expand, return path unchanged
   497   if (status == noExpand) {
   498     return(path);
   499   }
   500 
   501   // set user name, either current user or user name in path
   502   char   user[33]  = {0};
   503   size_t pathStart = 0;
   504 
   505   if (status == currentUser) {
   506     // use getlogin to find current username
   507     // sizeof(user)-1 to keep a char tp append final ':'
   508     if (getlogin_r(user, sizeof(user)-1)) {
   509       // error
   510       //logE("getlogin for current user");
   511     } {
   512     pathStart = 1;
   513   }
   514       }
   515   else {
   516     // username is in path
   517     // from index 1 (skip ~) to the first /
   518     char *slash = findS(path, "/");
   519     if (!slash) {
   520       // no slash found, path is in the form: ~USER
   521       // copy string after ~ to the end of the string
   522       strLCpy(user,  sizeof(user)-1, path+1);
   523       pathStart = strlen(path);
   524     }
   525     else {
   526       // copy string after ~ to first slash
   527       pathStart = slash - path;
   528       strLNCat(user,  sizeof(user)-1, path+1, pathStart-1);
   529   }
   530     }
   531 
   532   // add : at the end of username to match line in passwd
   533   size_t len  = strlen(user);
   534   user[len]   = ':';
   535   user[len+1] = 0;
   536 
   537 
   538   // find home path in the passwd file
   539   char **pwd = readText("/etc/passwd");
   540 
   541   if (!pwd) {
   542     return(NULL);
   543   }
   544 
   545   // result
   546   char *r = NULL;
   547 
   548   forEachS(pwd, l) {
   549     if (startsWithS(l, user)) {
   550       // found the user
   551       char **l_l = split(l, ":");
   552       // copy home path to result
   553       r = strdup(l_l[5]);
   554       listFreeS(l_l);
   555       break;
   556   }
   557     }
   558 
   559   listFreeS(pwd);
   560 
   561   if (!r) {
   562     // user not found in this system
   563     return(NULL);
   564   }
   565 
   566   // append rest of the path to the result
   567   iAppendS(&r, path + pathStart);
   568 
   569   free(path);
   570 
   571   return(r);
   572 }
   573 #endif
   574 
   575 
   576 // appends log to git config off.log
   577 void offLog(char* s) {
   578   char* r = NULL;
   579   char* dirName = NULL;
   580   DIR* d = NULL;
   581   FILE* f = NULL;
   582 
   583   if (runtimeConfig[log] == NULL) {
   584     // set runtimeConfig.log and get config in git
   585     r = exec(gitConfigGlobal, "off.log");
   586     r = expandHome(r);
   587     runtimeConfig[log] = r;
   588     // error handling
   589     if (r == NULL) {
   590       printf("Missing off.log config. Run \"git config --global off.log ~/.git-off/log\".");
   591       printf("\n");
   592       exit(EXIT_FAILURE);
   593   }
   594     }
   595   dirName = strdup(runtimeConfig[log]);
   596   dirname(dirName);
   597   d = opendir(dirName);
   598   if (d == NULL) {
   599     mkdirParents(dirName);
   600   }
   601   free(dirName);
   602   free(d);
   603 
   604   f = fopen(runtimeConfig[log], "a");
   605   fprintf(f, "%s\n", s);
   606   fclose(f);
   607 }
   608 
   609 // log for commands run in git repo
   610 // adds current repo path to string s in log
   611 void offLogRepo(char* s) {
   612   // depends on offHelpers
   613   char* ss = NULL;
   614 
   615   ss = malloc(strlen(offHelpers_gitRepoRoot()) + strlen(s) + 1 + 12);
   616   sprintf(ss, "REPO path: %s %s", offHelpers_gitRepoRoot(), s);
   617   offLog(ss);
   618   free(ss);
   619 }
   620 
   621 
   622 // runs an externalHelpers with parameters
   623 // cmdName is string
   624 // paramsArray is an array of strings
   625 // returns line
   626 char *exec(int cmdName, char* paramsArray) {
   627   char* cmd = NULL;
   628   FILE* fp = NULL;
   629   size_t len;
   630   char* line = NULL;
   631   ssize_t read;
   632 
   633   cmd = malloc(strlen(externalHelpers[cmdName]) + strlen(paramsArray) + 1 + 1);
   634   sprintf(cmd, "%s %s", externalHelpers[cmdName], paramsArray);
   635   //print cmd
   636 
   637   fp = popen(cmd, "r");
   638   if (fp == NULL) {
   639      exit(EXIT_FAILURE);
   640   }
   641   free(cmd);
   642 
   643   read = getline(&line, &len, fp);
   644   pclose(fp);
   645 
   646   if (read == -1) {
   647     // nothing was read
   648     return(NULL);
   649   }
   650 
   651   {
   652     char* pos = NULL;
   653     pos = strchr(line, '\n');
   654     if (pos != NULL)
   655       *pos = '\0';
   656   }
   657   //print line
   658   return(line);
   659 }
   660 
   661 // compare strings
   662 bool strEq(char *string1, char *string2) {
   663 
   664   if (string1 == NULL) {
   665     return(false);
   666   }
   667   if (string2 == NULL) {
   668     return(false);
   669   }
   670   return(strcmp(string1,string2) == 0);;
   671 }
   672 
   673 
   674 // handles global git config
   675 void gitConfig_set(char* key, char* value) {
   676   char* p = NULL;
   677   char* r = NULL;
   678 
   679   // dont use double quote, problem with rsyncOptions - p = '%s \"%s\"', key, value
   680   p = malloc(strlen(key) + strlen(value) + 1 + 5);;
   681   sprintf(p, "%s '%s'", key, value);
   682   r = exec(gitConfigGlobal, p);
   683   free(p);
   684   free(r);
   685 }
   686 
   687 void gitConfig_setLocal(char* key, char* value) {
   688   char* p = NULL;
   689   char* r = NULL;
   690 
   691   p = malloc(strlen(key) + strlen(value) + 1 + 5);
   692   sprintf(p, "%s \"%s\"", key, value);
   693   r = exec(gitConfig, p);
   694   free(p);
   695   free(r);
   696 }
   697 
   698 void gitConfig_setThisRepo(char* key, char* value) {
   699   char* p = NULL;
   700   char* r = NULL;
   701 
   702   p = malloc(strlen(offHelpers_gitRepoRoot()) + strlen(key) + strlen(value) + 1 + 22);
   703   sprintf(p, "--file %s/.git-off %s \"%s\"", offHelpers_gitRepoRoot() , key, value);
   704   r = exec(gitConfig, p);
   705   free(p);
   706   free(r);
   707 }
   708 
   709 char *gitConfig_getDefault(char* key) {
   710 
   711   return(exec(gitConfig, key));
   712 }
   713 
   714 char *gitConfig_get(char *key) {
   715     // use configAlways setting or
   716     // return value from
   717     // GIT_OFF_CONFIG if found
   718     // repo config if found
   719     // global config
   720     char* r = NULL;
   721     char* p = NULL;
   722     char* gitOffConfigEnv = NULL;
   723 
   724     // configAlways
   725     if (strEq(offHelpers_offConfigAlways(), "GIT_OFF_CONFIG")) {
   726       gitOffConfigEnv = getenv("GIT_OFF_CONFIG");
   727       if ((gitOffConfigEnv != NULL) && (access(gitOffConfigEnv, F_OK) != -1)) {
   728         p = malloc(strlen(gitOffConfigEnv) + strlen(key) + 1 + 8);
   729         sprintf(p, "--file %s %s", gitOffConfigEnv, key);
   730         r = exec(gitConfig, p);
   731         free(p);
   732       }
   733       return(r);
   734     }
   735     if (strEq(offHelpers_offConfigAlways(), "repo")) {
   736       p = malloc(strlen(offHelpers_gitRepoRoot()) + 1 + 9);
   737       sprintf(p, "%s/.git-off", offHelpers_gitRepoRoot());
   738       if (access(p, F_OK) != -1) {
   739         free(p);
   740         p = malloc(strlen(offHelpers_gitRepoRoot()) + strlen(key) + 1 + 17);
   741         sprintf(p, "--file %s/.git-off %s", offHelpers_gitRepoRoot() , key);
   742         r = exec(gitConfig, p);
   743       }
   744       free(p);
   745       return(r);
   746     }
   747     if (strEq(offHelpers_offConfigAlways(), "global")) {
   748       r = gitConfig_getDefault(key);
   749       return(r);
   750     }
   751 
   752     // interpolate
   753     gitOffConfigEnv = getenv("GIT_OFF_CONFIG");
   754     if ((gitOffConfigEnv != NULL) && (access(gitOffConfigEnv, F_OK) != -1)) {
   755       p = malloc(strlen(gitOffConfigEnv) + strlen(key) + 1 + 8);
   756       sprintf(p, "--file %s %s", gitOffConfigEnv, key);
   757       r = exec(gitConfig, p);
   758       free(p);
   759       if (r != NULL) {
   760         return(r);
   761     }
   762       }
   763 
   764     if (offHelpers_gitRepoRoot()) {
   765       // skip repo config when running outside a git repo
   766       p = malloc(strlen(offHelpers_gitRepoRoot()) + 1 + 9);
   767       sprintf(p, "%s/.git-off", offHelpers_gitRepoRoot());
   768       if (access(p, F_OK) != -1) {
   769         free(p);
   770         p = malloc(strlen(offHelpers_gitRepoRoot()) + strlen(key) + 1 + 17);
   771         sprintf(p, "--file %s/.git-off %s", offHelpers_gitRepoRoot() , key);
   772         r = exec(gitConfig, p);
   773         if (r != NULL) {
   774           free(p);
   775           return(r);
   776       }
   777         }
   778       free(p);
   779     }
   780     r = gitConfig_getDefault(key);
   781     return(r);
   782 }
   783 
   784 
   785 // recursive mkdir
   786 void mkdirParents(char* p) {
   787   char* r = NULL;
   788 
   789   r = exec(mkdirE, p);
   790   free(r);
   791 }
   792 
   793 // delete recursively files and directories
   794 void rmAll(char* p) {
   795   char* r = NULL;
   796 
   797   r = exec(rm, p);
   798   free(r);
   799 }
   800 
   801 // copies files
   802 // file modes are not kept
   803 void copy(char* src, char* dst) {
   804   char* r = NULL;
   805   char* p = NULL;
   806 
   807   p = malloc(strlen(src) + strlen(dst) + 1 + 1);
   808   sprintf(p, "%s %s", src, dst);
   809   r = exec(cp, p);
   810   free(p);
   811   free(r);
   812 }
   813 
   814 // exec
   815 // return stdout from cmd
   816 char **execOut(char *cmd) {
   817   FILE* fp = NULL;
   818   size_t len;
   819   char* line = NULL;
   820   ssize_t read;
   821   char **list = NULL;
   822   int count = 0;;
   823 
   824   //print cmd
   825 
   826   fp = popen(cmd, "r");
   827   if (fp == NULL) {
   828      exit(EXIT_FAILURE);
   829   }
   830 
   831   read = getline(&line, &len, fp);
   832   while (read != -1) {
   833     {
   834         char* pos = NULL;
   835         pos = strchr(line, '\n');
   836         if (pos != NULL)
   837             *pos = '\0';
   838     }
   839     if (list == NULL) {
   840       list          = malloc(2 * sizeof(int *));
   841       list[1]       = NULL;
   842     }
   843     else {
   844       list          = realloc(list, (count+2) * sizeof(int *));
   845       if (list == NULL) {
   846         exit(EXIT_FAILURE);
   847       }
   848       list[count+1] = NULL;
   849     }
   850     list[count] = strdup(line);
   851     read = getline(&line, &len, fp);
   852     count++;
   853   }
   854 
   855   pclose(fp);
   856   return(list);
   857 }
   858 
   859 // List all files in a directory
   860 char **walkDir(char* dir) {
   861   char* cmd = NULL;
   862   char **list = NULL;
   863 
   864   cmd  = malloc(strlen(dir) + 1 + 13);
   865   sprintf(cmd, "find %s -type f", dir);
   866   list = execOut(cmd);
   867   free(cmd);
   868   return(list);
   869 }
   870 
   871 void freeList(void **list) {
   872   int i = 0;;
   873 
   874   if (list != NULL) {
   875     while (list[i] != NULL) {
   876       free(list[i]);
   877       i++;
   878     }
   879     free(list);
   880 }
   881   }
   882 
   883 
   884 char **split(char *string, char* delim) {
   885   char *startString = NULL;
   886   char *workingString = NULL;
   887   char *line = NULL;
   888   char *token = NULL;
   889   int count = 0;;
   890   char **r = NULL;
   891 
   892   startString   = strdup(string);
   893   workingString = startString;
   894   while (strlen(workingString) != 0) {
   895     line  = workingString;
   896     token = strtok_r(line, delim, &workingString);
   897     if (r == NULL) {
   898       r          = malloc(2 * sizeof(int *));
   899       r[1]       = NULL;
   900     }
   901     else {
   902       r          = realloc(r, (count+2) * sizeof(int *));
   903       if (r == NULL) {
   904         exit(EXIT_FAILURE);
   905       }
   906       r[count+1] = NULL;
   907     }
   908     r[count] = strdup(token);
   909     count++;
   910     // macOS sets workingString to NULL at the end
   911     if (!workingString) {
   912       break;
   913   }
   914     }
   915 
   916   free(startString);
   917   return(r);
   918 }
   919 
   920 char *join(char **list, char* delim) {
   921   char *r = NULL;
   922 
   923   while (*list != NULL) {
   924     if (r == NULL) {
   925       r = strdup(*list);
   926     }
   927     else {
   928       char *tmp;
   929       tmp = realloc(r, strlen(r) + strlen(delim) + 1);
   930       if (tmp == NULL) {
   931         exit(EXIT_FAILURE);
   932       }
   933       r = tmp;
   934       strcat(r, delim);
   935       tmp = realloc(r, strlen(r) + strlen(*list) + 1);
   936       if (tmp == NULL) {
   937         exit(EXIT_FAILURE);
   938       }
   939       r = tmp;
   940       strcat(r, *list);
   941     }
   942     list++;
   943   }
   944   return(r);
   945 }
   946 
   947 
   948 // transport
   949 
   950 typedef void (*transportSend_t)(char *);
   951 typedef void (*transportReceive_t)(char *);
   952 typedef struct {transportSend_t send; transportReceive_t receive;} transport_t;
   953 transport_t transport;
   954 
   955 // git-off helpers
   956 // gitRepoRoot:    sets and returns runtimeConfig.currentRepoRoot
   957 // objectPath:     sets and returns runtimeConfig.objectPath
   958 // offStore:       sets and returns runtimeConfig.offStore
   959 // offMode:        sets and returns runtimeConfig.offMode
   960 // offIntegrity    sets and returns runtimeConfig.offIntegrity
   961 // offScp:         sets and returns runtimeConfig.offScp
   962 // offScpUser:     sets and returns runtimeConfig.offScpUser
   963 // log:            sets and returns runtimeConfig.log
   964 // getLog:         sets and returns runtimeConfig.log with error checking
   965 // userAt:         returns user@ or ''
   966 // getSSHConfig    extracts host, port and path from off.sshhost
   967 // mkdirStore      mkdir in remote store
   968 // rmAllStore      rm in remote store
   969 // checkIntegrity  check integrity of files coming from the store
   970 // setTransport:   set send and receive functions for transport
   971 // getOffFilePath: creates path for offFile
   972 
   973 char *offHelpers_gitRepoRoot() {
   974 
   975   if (runtimeConfig[currentRepoRoot] == NULL) {
   976     runtimeConfig[currentRepoRoot] = exec(gitRepoRoot, "");
   977   }
   978   return(runtimeConfig[currentRepoRoot]);
   979 }
   980 
   981 char *offHelpers_objectPath() {
   982 
   983     if (runtimeConfig[objectPath] == NULL) {
   984       runtimeConfig[objectPath] = malloc(strlen(offHelpers_gitRepoRoot()) + strlen(offDEFAULTS[objectPathD]) + 1 + 0);
   985       sprintf(runtimeConfig[objectPath], "%s%s", offHelpers_gitRepoRoot(), offDEFAULTS[objectPathD]);
   986     }
   987     return(runtimeConfig[objectPath]);
   988 }
   989 
   990 char *offHelpers_offStore() {
   991 
   992     if (runtimeConfig[offStore] == NULL) {
   993       runtimeConfig[offStore] = gitConfig_get("off.store");
   994     }
   995     return(runtimeConfig[offStore]);
   996 }
   997 
   998 char *offHelpers_offHttp() {
   999 
  1000     if (runtimeConfig[offHttp] == NULL) {
  1001       runtimeConfig[offHttp] = gitConfig_get("off.http");
  1002     }
  1003     return(runtimeConfig[offHttp]);
  1004 }
  1005 
  1006 char *offHelpers_offCurlOptions() {
  1007 
  1008     if (runtimeConfig[offCurlOptions] == NULL) {
  1009       runtimeConfig[offCurlOptions] = gitConfig_get("off.curloptions");
  1010     }
  1011     return(runtimeConfig[offCurlOptions]);
  1012 }
  1013 
  1014 char *offHelpers_offMode() {
  1015 
  1016     if (runtimeConfig[offMode] == NULL) {
  1017       runtimeConfig[offMode] = gitConfig_get("off.mode");
  1018     }
  1019     return(runtimeConfig[offMode]);
  1020 }
  1021 
  1022 char *offHelpers_offIntegrity() {
  1023 
  1024     if (runtimeConfig[offIntegrity] == NULL) {
  1025       runtimeConfig[offIntegrity] = gitConfig_get("off.integrity");
  1026     }
  1027     return(runtimeConfig[offIntegrity]);
  1028 }
  1029 
  1030 char *offHelpers_offPem() {
  1031 
  1032     if (runtimeConfig[offPem] == NULL) {
  1033       runtimeConfig[offPem] = gitConfig_get("off.pem");
  1034     }
  1035     return(runtimeConfig[offPem]);
  1036 }
  1037 
  1038 char *offHelpers_offSshOptions() {
  1039 
  1040     if (runtimeConfig[offSshOptions] == NULL) {
  1041       runtimeConfig[offSshOptions] = gitConfig_get("off.sshoptions");
  1042     }
  1043     return(runtimeConfig[offSshOptions]);
  1044 }
  1045 
  1046 char *offHelpers_offScpOptions() {
  1047 
  1048     if (runtimeConfig[offScpOptions] == NULL) {
  1049       runtimeConfig[offScpOptions] = gitConfig_get("off.scpoptions");
  1050     }
  1051     return(runtimeConfig[offScpOptions]);
  1052 }
  1053 
  1054 char *offHelpers_offRsyncOptions() {
  1055 
  1056     if (runtimeConfig[offRsyncOptions] == NULL) {
  1057       runtimeConfig[offRsyncOptions] = gitConfig_get("off.rsyncoptions");
  1058     }
  1059     return(runtimeConfig[offRsyncOptions]);
  1060 }
  1061 
  1062 char *offHelpers_offScp() {
  1063 
  1064     if (runtimeConfig[offScp] == NULL) {
  1065       runtimeConfig[offScp] = gitConfig_get("off.scphost");
  1066     }
  1067     return(runtimeConfig[offScp]);
  1068 }
  1069 
  1070 char *offHelpers_offScpUser() {
  1071 
  1072     if (runtimeConfig[offScpUser] == NULL) {
  1073       runtimeConfig[offScpUser] = gitConfig_get("off.scpuser");
  1074     }
  1075     return(runtimeConfig[offScpUser]);
  1076 }
  1077 
  1078 char *offHelpers_log() {
  1079 
  1080     if (runtimeConfig[log] == NULL) {
  1081       runtimeConfig[log] = gitConfig_getDefault("off.log");
  1082       runtimeConfig[log] = expandHome(runtimeConfig[log]);
  1083     }
  1084     return(runtimeConfig[log]);
  1085 }
  1086 
  1087 char *offHelpers_getLog() {
  1088 
  1089     offHelpers_log();
  1090     // error handling
  1091     if (runtimeConfig[log] == NULL) {
  1092       printf("Missing off.log config. Run \"git config --global off.log ~/.git-off/log\".");
  1093       printf("\n");
  1094       exit(EXIT_FAILURE);
  1095     }
  1096     return(runtimeConfig[log]);
  1097 }
  1098 
  1099 char *offHelpers_offConfigAlways() {
  1100 
  1101     if (runtimeConfig[offConfigAlways] == NULL) {
  1102       runtimeConfig[offConfigAlways] = gitConfig_getDefault("off.configAlways");
  1103     }
  1104     return(runtimeConfig[offConfigAlways]);
  1105 }
  1106 
  1107 char *offHelpers_s3Region() {
  1108 
  1109     if (runtimeConfig[s3Region] == NULL) {
  1110       runtimeConfig[s3Region] = gitConfig_get("off.s3Region");
  1111     }
  1112     return(runtimeConfig[s3Region]);
  1113 }
  1114 
  1115 char *offHelpers_s3Bucket() {
  1116 
  1117     if (runtimeConfig[s3Bucket] == NULL) {
  1118       runtimeConfig[s3Bucket] = gitConfig_get("off.s3Bucket");
  1119     }
  1120     return(runtimeConfig[s3Bucket]);
  1121 }
  1122 
  1123 char *offHelpers_transform() {
  1124 
  1125     if (runtimeConfig[transform] == NULL) {
  1126       runtimeConfig[transform] = gitConfig_get("off.transform");
  1127     }
  1128     return(runtimeConfig[transform]);
  1129 }
  1130 
  1131 char *offHelpers_transformTo() {
  1132 
  1133     if (runtimeConfig[transformTo] == NULL) {
  1134       runtimeConfig[transformTo] = gitConfig_get("off.transformTo");
  1135     }
  1136     return(runtimeConfig[transformTo]);
  1137 }
  1138 
  1139 char *offHelpers_transformFrom() {
  1140 
  1141     if (runtimeConfig[transformFrom] == NULL) {
  1142       runtimeConfig[transformFrom] = gitConfig_get("off.transformFrom");
  1143     }
  1144     return(runtimeConfig[transformFrom]);
  1145 }
  1146 
  1147 char *offHelpers_userAt() {
  1148 
  1149     if ((offHelpers_offScpUser() != NULL) && (!strEq(offHelpers_offScpUser(), ""))) {
  1150       char *u;
  1151       u = malloc(strlen(offHelpers_offScpUser()) + 1 + 1);
  1152       sprintf(u, "%s@", offHelpers_offScpUser());
  1153       return(u);
  1154     }
  1155     else {
  1156       return "";
  1157 }
  1158     }
  1159 
  1160 char **offHelpers_getSSHConfig() {
  1161   char *user = NULL;
  1162   char **h_l = NULL;
  1163   char *port = NULL;
  1164   char *host = NULL;
  1165   char *storePath = NULL;
  1166   char **portAndPath_l = NULL;
  1167   char **r = NULL;
  1168 
  1169   // extract host, port and path from off.sshhost
  1170 
  1171   user     = offHelpers_userAt();
  1172   h_l      = split(offHelpers_offScp(), ":");
  1173   host     = strdup(h_l[0]);
  1174   if (h_l[1] != NULL) {
  1175     int i;
  1176 
  1177     portAndPath_l = split(h_l[1], "/");
  1178     i             = strtoumax(portAndPath_l[0], NULL, 10);
  1179     if (i != 0) {
  1180       port          = strdup(portAndPath_l[0]);
  1181   }
  1182     }
  1183   if (port == NULL) {
  1184     // use default port
  1185     storePath = strdup(h_l[1]);
  1186   }
  1187   else {
  1188     storePath = strdup("/");
  1189     char *tmp;
  1190     tmp       = join(portAndPath_l + 1, "/");
  1191     char *tmpr;
  1192     tmpr      = realloc(storePath, strlen(storePath) + strlen(tmp) + 1);
  1193     if (tmpr == NULL) {
  1194       exit(EXIT_FAILURE);
  1195     }
  1196     storePath = tmpr;
  1197     strcat(storePath, tmp);
  1198     free(tmp);
  1199   }
  1200   freeList((void **) h_l);
  1201   freeList((void **) portAndPath_l);
  1202 
  1203   // result [user + host, storePath, port]
  1204   r    = malloc(4 * sizeof(int *));
  1205   r[3] = NULL;
  1206   r[0] = strdup(user);
  1207   char *t;
  1208   t    = realloc(r[0], strlen(r[0]) + strlen(host) + 1);
  1209   if (t == NULL) {
  1210     exit(EXIT_FAILURE);
  1211   }
  1212   r[0] = t;
  1213   strcat(r[0], host);
  1214   r[1] = storePath;
  1215   r[2] = port;
  1216   free(host);
  1217   return(r);
  1218 }
  1219 
  1220 
  1221 void offHelpers_mkdirStore(char *p) {
  1222     // mkdir in remote store
  1223     char *params = NULL;
  1224     char *sshCmd = NULL;
  1225     char *pem = NULL;
  1226     char **h_l = NULL;
  1227 
  1228     // TODO handle multiple transports
  1229     h_l = offHelpers_getSSHConfig();
  1230 
  1231     sshCmd = malloc(strlen(h_l[1]) + strlen(p) + 1 + 10);
  1232     sprintf(sshCmd, "mkdir -p %s/%s", h_l[1], p);
  1233 
  1234     // setup ssh/scp private key
  1235     if ((offHelpers_offPem() == NULL) || (strEq(offHelpers_offPem(), "offNoValue")) || (offHelpers_offPem() == NULL)) {
  1236       pem = strdup("");
  1237     }
  1238     else {
  1239       pem = malloc(strlen(offHelpers_offPem()) + 1 + 3);
  1240       sprintf(pem, "-i %s", offHelpers_offPem());
  1241     }
  1242 
  1243     // ignore error from mkdir for already existing store
  1244     if (h_l[2] == NULL) {
  1245       params = malloc(strlen(offHelpers_offSshOptions()) + strlen(pem) + strlen(h_l[0]) + strlen(sshCmd) + 1 + 7);
  1246       sprintf(params, "%s %s %s \"%s\"", offHelpers_offSshOptions(), pem, h_l[0], sshCmd);
  1247     }
  1248     else {
  1249       params = malloc(strlen(offHelpers_offSshOptions()) + strlen(pem) + strlen(h_l[2]) + strlen(h_l[0]) + strlen(sshCmd) + 1 + 11);
  1250       sprintf(params, "%s %s -p %s %s \"%s\"", offHelpers_offSshOptions(), pem, h_l[2], h_l[0], sshCmd);
  1251     }
  1252 
  1253     freeList((void **) h_l);
  1254     free(sshCmd);
  1255     free(pem);
  1256     exec(ssh, params);
  1257     free(params);
  1258 }
  1259 
  1260 
  1261 void offHelpers_rmAllStore(char *p) {
  1262     // rm in remote store
  1263     char *params = NULL;
  1264     char *sshCmd = NULL;
  1265     char *pem = NULL;
  1266     char **h_l = NULL;
  1267 
  1268     // setup ssh/scp private key
  1269     if ((offHelpers_offPem() == NULL) || (strEq(offHelpers_offPem(), "offNoValue")) || (offHelpers_offPem() == NULL)) {
  1270       pem = strdup("");
  1271     }
  1272     else {
  1273       pem = malloc(strlen(offHelpers_offPem()) + 1 + 3);
  1274       sprintf(pem, "-i %s", offHelpers_offPem());
  1275     }
  1276 
  1277     // scphost format is host:path
  1278     h_l = offHelpers_getSSHConfig();
  1279 
  1280     sshCmd = malloc(strlen(h_l[1]) + strlen(p) + 1 + 8);
  1281     sprintf(sshCmd, "rm -rf %s/%s", h_l[1], p);
  1282 
  1283     // ignore error from rm
  1284     if (h_l[2] == NULL) {
  1285       params = malloc(strlen(offHelpers_offSshOptions()) + strlen(pem) + strlen(h_l[0]) + strlen(sshCmd) + 1 + 7);
  1286       sprintf(params, "%s %s %s \"%s\"", offHelpers_offSshOptions(), pem, h_l[0], sshCmd);
  1287     }
  1288     else {
  1289       params = malloc(strlen(offHelpers_offSshOptions()) + strlen(pem) + strlen(h_l[2]) + strlen(h_l[0]) + strlen(sshCmd) + 1 + 11);
  1290       sprintf(params, "%s %s -p %s %s \"%s\"", offHelpers_offSshOptions(), pem, h_l[2], h_l[0], sshCmd);
  1291     }
  1292 
  1293     freeList((void **) h_l);
  1294     free(sshCmd);
  1295     free(pem);
  1296     exec(ssh, params);
  1297     free(params);
  1298 }
  1299 
  1300 void offHelpers_copyTo() {
  1301     // list file in cache
  1302     // copy to store
  1303     char **files = NULL;
  1304     int i;
  1305 
  1306     // list file in cache
  1307 
  1308     files = walkDir(offHelpers_objectPath());
  1309     if (files == NULL) {
  1310       return;
  1311     }
  1312 
  1313     i = 0;
  1314     while (files[i] != NULL) {
  1315       char *tmp;
  1316       tmp = strdup(files[i] + strlen(offHelpers_objectPath()) + 1);
  1317       free(files[i]);
  1318       files[i] = tmp;
  1319       i++;
  1320     }
  1321 
  1322     // copy to store
  1323 
  1324     i = 0;
  1325     while (files[i] != NULL) {
  1326       transport.send(files[i]);
  1327       i++;
  1328     }
  1329     freeList((void **) files);
  1330 }
  1331 
  1332 bool offHelpers_checkIntegrity(char *p) {
  1333     // check integrity of files coming from the store
  1334     bool result = true;;
  1335     char *receivedSha = NULL;
  1336     char *base = NULL;
  1337 
  1338     if (strEq(offHelpers_offIntegrity(), "disable")) {
  1339       return(true);
  1340     }
  1341 
  1342     receivedSha = exec(sha, p);
  1343 
  1344     base = strdup(basename(p));
  1345     if (!strEq(base, receivedSha)) {
  1346       // the file received is different from the one that was sent.
  1347       printf("git-off: The file %s differs from the one that was pushed.", p); {
  1348       printf("\n");
  1349       result = false;
  1350     }
  1351       }
  1352     free(base);
  1353 
  1354     return(result);
  1355 }
  1356 
  1357 void transportCopySend(char *file) {
  1358   // create file directories in store
  1359   char **f_l = NULL;
  1360   char *p = NULL;
  1361   DIR *d = NULL;
  1362 
  1363   f_l = split(file, "/");
  1364   p = malloc(strlen(offHelpers_offStore()) + strlen(f_l[0]) + strlen(f_l[1]) + 1 + 2);
  1365   sprintf(p, "%s/%s/%s", offHelpers_offStore(), f_l[0], f_l[1]);
  1366   d = opendir(p);;
  1367   if (d == NULL) {
  1368     mkdirParents(p);
  1369   }
  1370   free(p);
  1371 
  1372   p    = malloc(strlen(offHelpers_objectPath()) + strlen(file) + 1 + 1);
  1373   sprintf(p, "%s/%s", offHelpers_objectPath(), file);
  1374   char *dest;
  1375   dest = malloc(strlen(offHelpers_offStore()) + strlen(file) + 1 + 1);
  1376   sprintf(dest, "%s/%s", offHelpers_offStore(), file);
  1377   copy(p, dest);
  1378   free(dest);
  1379   free(p);
  1380   freeList((void **)f_l);
  1381 }
  1382 
  1383 void transportCopyReceive(char *file) {
  1384   // transfer and transform
  1385   char *p = NULL;
  1386   int sR;
  1387   char b[1024];
  1388   FILE *f = NULL;
  1389 
  1390   if ((strEq(offHelpers_transform(), "enable")) && (offHelpers_transformFrom() != NULL)) {
  1391     // create file directories in objectPath
  1392     char **f_l;
  1393     DIR *d;
  1394     f_l = split(file, "/");
  1395     p = malloc(strlen(offHelpers_objectPath()) + strlen(f_l[0]) + strlen(f_l[1]) + 1 + 2);
  1396     sprintf(p, "%s/%s/%s", offHelpers_objectPath(), f_l[0], f_l[1]);
  1397     d = opendir(p);;
  1398     if (d == NULL) {
  1399       mkdirParents(p);
  1400     }
  1401     free(p);
  1402 
  1403     p    = malloc(strlen(offHelpers_offStore()) + strlen(file) + 1 + 1);
  1404     sprintf(p, "%s/%s", offHelpers_offStore(), file);
  1405     char *dest;
  1406     dest = malloc(strlen(offHelpers_objectPath()) + strlen(file) + 1 + 1);
  1407     sprintf(dest, "%s/%s", offHelpers_objectPath(), file);
  1408     copy(p, dest);
  1409 
  1410     transport_transformFrom(file);
  1411 
  1412     free(p);
  1413     p = malloc(strlen(offHelpers_objectPath()) + strlen(file) + 1 + 8);
  1414     sprintf(p, "%s/../tmp/%s", offHelpers_objectPath(), file);
  1415     if (offHelpers_checkIntegrity(p)) {
  1416       f = fopen(p, "r");
  1417       sR = fread(b, 1, 1024, f);
  1418       while (sR != 0) {
  1419         fwrite(b, 1, sR, stdout);
  1420         sR = fread(b, 1, 1024, f);
  1421       }
  1422       fclose(f);
  1423     }
  1424 
  1425     free(dest);
  1426     freeList((void **)f_l);
  1427   }
  1428   else {
  1429     p = malloc(strlen(offHelpers_offStore()) + strlen(file) + 1 + 1);
  1430     sprintf(p, "%s/%s", offHelpers_offStore(), file);
  1431     if (offHelpers_checkIntegrity(p)) {
  1432       f = fopen(p, "r");
  1433       sR = fread(b, 1, 1024, f);
  1434       while (sR != 0) {
  1435         fwrite(b, 1, sR, stdout);
  1436         sR = fread(b, 1, 1024, f);
  1437       }
  1438       fclose(f);
  1439   }
  1440     }
  1441 
  1442   free(p);
  1443 }
  1444 
  1445 
  1446 void transportRsyncSend(char *file) {
  1447   // create file directories in store
  1448   char **f_l = NULL;
  1449   char *p = NULL;
  1450   char *pem = NULL;
  1451   char *params = NULL;
  1452   char *options = NULL;
  1453   char *options2 = NULL;
  1454   char *tmp = NULL;
  1455 
  1456   f_l = split(file, "/");
  1457   p   = malloc(strlen(f_l[0]) + strlen(f_l[1]) + 1 + 1);
  1458   sprintf(p, "%s/%s", f_l[0], f_l[1]);
  1459   offHelpers_mkdirStore(p);
  1460   free(p);
  1461 
  1462   options = strdup(offHelpers_offRsyncOptions());
  1463 
  1464   // set pem or not? check rsyncoptions
  1465   // find _i index in options
  1466   tmp  = strstr(options, " _i");
  1467   if (tmp != NULL) {
  1468     if ((offHelpers_offPem() == NULL) || (strEq(offHelpers_offPem(), "offNoValue"))) {
  1469       options2 = strdup(options);
  1470     }
  1471     else {
  1472       tmp[1] = 0;
  1473       options2 = malloc(strlen(options) + strlen(offHelpers_offPem()) + strlen(tmp+3) + 1 + 0);
  1474       sprintf(options2, "%s%s%s", options, offHelpers_offPem(), tmp+3);
  1475   }
  1476     }
  1477   else {
  1478     options2 = strdup(options);
  1479   }
  1480 
  1481   char *dest;
  1482 
  1483   p    = malloc(strlen(offHelpers_objectPath()) + strlen(file) + 1 + 1);
  1484   sprintf(p, "%s/%s", offHelpers_objectPath(), file);
  1485   dest = malloc(strlen(offHelpers_offScp()) + strlen(file) + 1 + 1);
  1486   sprintf(dest, "%s/%s", offHelpers_offScp(), file);
  1487 
  1488   params = malloc(strlen(options2) + strlen(p) + strlen(dest) + 1 + 2);
  1489   sprintf(params, "%s %s %s", options2, p, dest);
  1490 
  1491   exec(rsync, params);
  1492 
  1493   free(p);
  1494   free(dest);
  1495   free(pem);
  1496   free(params);
  1497   free(options);
  1498   free(options2);
  1499 }
  1500 
  1501 void transportRsyncReceive(char *file) {
  1502   char *p = NULL;
  1503   char **f_l = NULL;
  1504   DIR *d = NULL;
  1505   char *pem = NULL;
  1506   char *params = NULL;
  1507   char *options = NULL;
  1508   char *options2 = NULL;
  1509   char *tmp = NULL;
  1510 
  1511   // create file directories in cache
  1512   f_l = split(file, "/");
  1513   p = malloc(strlen(offHelpers_objectPath()) + strlen(f_l[0]) + strlen(f_l[1]) + 1 + 2);
  1514   sprintf(p, "%s/%s/%s", offHelpers_objectPath(), f_l[0], f_l[1]);
  1515   d = opendir(p);;
  1516   if (d == NULL) {
  1517     mkdirParents(p);
  1518   }
  1519   free(p);
  1520   freeList((void **)f_l);
  1521 
  1522 
  1523   options = strdup(offHelpers_offRsyncOptions());
  1524 
  1525   // set pem or not? check rsyncoptions
  1526   // find _i index in options
  1527   tmp  = strstr(options, " _i");
  1528   if (tmp != NULL) {
  1529     if ((offHelpers_offPem() == NULL) || (strEq(offHelpers_offPem(), "offNoValue"))) {
  1530       options2 = strdup(options);
  1531     }
  1532     else {
  1533       tmp[1] = 0;
  1534       options2 = malloc(strlen(options) + strlen(offHelpers_offPem()) + strlen(tmp+3) + 1 + 0);
  1535       sprintf(options2, "%s%s%s", options, offHelpers_offPem(), tmp+3);
  1536   }
  1537     }
  1538   else {
  1539     options2 = strdup(options);
  1540   }
  1541 
  1542   char *dest;
  1543 
  1544   dest = malloc(strlen(offHelpers_objectPath()) + strlen(file) + 1 + 1);
  1545   sprintf(dest, "%s/%s", offHelpers_objectPath(), file);
  1546   p    = malloc(strlen(offHelpers_offScp()) + strlen(file) + 1 + 1);
  1547   sprintf(p, "%s/%s", offHelpers_offScp(), file);
  1548 
  1549   params = malloc(strlen(options2) + strlen(p) + strlen(dest) + 1 + 2);
  1550   sprintf(params, "%s %s %s", options2, p, dest);
  1551 
  1552   exec(rsync, params);
  1553 
  1554   free(p);
  1555   free(dest);
  1556   free(pem);
  1557   free(params);
  1558   free(options);
  1559   free(options2);
  1560 
  1561   // transform
  1562   int sR;
  1563   char b[1024];
  1564   FILE *f;
  1565 
  1566   if ((strEq(offHelpers_transform(), "enable")) && (offHelpers_transformFrom() != NULL)) {
  1567     transport_transformFrom(file);
  1568     p = malloc(strlen(offHelpers_objectPath()) + strlen(file) + 1 + 8);
  1569     sprintf(p, "%s/../tmp/%s", offHelpers_objectPath(), file);
  1570     if (offHelpers_checkIntegrity(p)) {
  1571       f = fopen(p, "r");
  1572       sR = fread(b, 1, 1024, f);
  1573       while (sR != 0) {
  1574         fwrite(b, 1, sR, stdout);
  1575         sR = fread(b, 1, 1024, f);
  1576       }
  1577       fclose(f);
  1578   }
  1579     }
  1580   else {
  1581     p = malloc(strlen(offHelpers_objectPath()) + strlen(file) + 1 + 1);
  1582     sprintf(p, "%s/%s", offHelpers_objectPath(), file);
  1583     if (offHelpers_checkIntegrity(p)) {
  1584       f = fopen(p, "r");
  1585       sR = fread(b, 1, 1024, f);
  1586       while (sR != 0) {
  1587         fwrite(b, 1, sR, stdout);
  1588         sR = fread(b, 1, 1024, f);
  1589       }
  1590       fclose(f);
  1591   }
  1592     }
  1593 
  1594   free(p);
  1595 }
  1596 
  1597 
  1598 void transportScpSend(char *file) {
  1599   // create file directories in store
  1600   char **f_l = NULL;
  1601   char *p = NULL;
  1602   char *pem = NULL;
  1603   char *params = NULL;
  1604   char **h_l = NULL;
  1605 
  1606   f_l = split(file, "/");
  1607   p   = malloc(strlen(f_l[0]) + strlen(f_l[1]) + 1 + 1);
  1608   sprintf(p, "%s/%s", f_l[0], f_l[1]);
  1609   offHelpers_mkdirStore(p);
  1610   free(p);
  1611 
  1612   h_l = offHelpers_getSSHConfig();
  1613 
  1614   // setup ssh/scp private key
  1615   if ((offHelpers_offPem() == NULL) || (strEq(offHelpers_offPem(), "offNoValue"))) {
  1616     pem = strdup("");
  1617   }
  1618   else {
  1619     pem = malloc(strlen(offHelpers_offPem()) + 1 + 3);
  1620     sprintf(pem, "-i %s", offHelpers_offPem());
  1621   }
  1622 
  1623   char *dest;
  1624 
  1625   p    = malloc(strlen(offHelpers_objectPath()) + strlen(file) + 1 + 1);
  1626   sprintf(p, "%s/%s", offHelpers_objectPath(), file);
  1627   dest = malloc(strlen(h_l[0]) + strlen(h_l[1]) + strlen(file) + 1 + 2);
  1628   sprintf(dest, "%s:%s/%s", h_l[0], h_l[1], file);
  1629 
  1630   if (h_l[2] == NULL) {
  1631     params = malloc(strlen(offHelpers_offScpOptions()) + strlen(pem) + strlen(p) + strlen(dest) + 1 + 3);
  1632     sprintf(params, "%s %s %s %s", offHelpers_offScpOptions(), pem, p, dest);
  1633   }
  1634   else {
  1635     params = malloc(strlen(offHelpers_offScpOptions()) + strlen(pem) + strlen(h_l[2]) + strlen(p) + strlen(dest) + 1 + 7);
  1636     sprintf(params, "%s %s -P %s %s %s", offHelpers_offScpOptions(), pem, h_l[2], p, dest);
  1637   }
  1638 
  1639   exec(scp, params);
  1640 
  1641   free(p);
  1642   free(dest);
  1643   freeList((void **) h_l);
  1644   free(pem);
  1645   free(params);
  1646 }
  1647 
  1648 void transportScpReceive(char *file) {
  1649   char *p = NULL;
  1650   char **f_l = NULL;
  1651   DIR *d = NULL;
  1652   char **h_l = NULL;
  1653   char *pem = NULL;
  1654   char *params = NULL;
  1655 
  1656   // create file directories in cache
  1657   f_l = split(file, "/");
  1658   p = malloc(strlen(offHelpers_objectPath()) + strlen(f_l[0]) + strlen(f_l[1]) + 1 + 2);
  1659   sprintf(p, "%s/%s/%s", offHelpers_objectPath(), f_l[0], f_l[1]);
  1660   d = opendir(p);;
  1661   if (d == NULL) {
  1662     mkdirParents(p);
  1663   }
  1664   free(p);
  1665   freeList((void **)f_l);
  1666 
  1667   h_l = offHelpers_getSSHConfig();
  1668 
  1669   // setup ssh/scp private key
  1670   if ((offHelpers_offPem() == NULL) || (strEq(offHelpers_offPem(), "offNoValue"))) {
  1671     pem = strdup("");
  1672   }
  1673   else {
  1674     pem = malloc(strlen(offHelpers_offPem()) + 1 + 3);
  1675     sprintf(pem, "-i %s", offHelpers_offPem());
  1676   }
  1677 
  1678   char *dest;
  1679 
  1680   dest = malloc(strlen(offHelpers_objectPath()) + strlen(file) + 1 + 1);
  1681   sprintf(dest, "%s/%s", offHelpers_objectPath(), file);
  1682   p    = malloc(strlen(h_l[0]) + strlen(h_l[1]) + strlen(file) + 1 + 2);
  1683   sprintf(p, "%s:%s/%s", h_l[0], h_l[1], file);
  1684 
  1685   if (h_l[2] == NULL) {
  1686     params = malloc(strlen(offHelpers_offScpOptions()) + strlen(pem) + strlen(p) + strlen(dest) + 1 + 3);
  1687     sprintf(params, "%s %s %s %s", offHelpers_offScpOptions(), pem, p, dest);
  1688   }
  1689   else {
  1690     params = malloc(strlen(offHelpers_offScpOptions()) + strlen(pem) + strlen(h_l[2]) + strlen(p) + strlen(dest) + 1 + 7);
  1691     sprintf(params, "%s %s -P %s %s %s", offHelpers_offScpOptions(), pem, h_l[2], p, dest);
  1692   }
  1693 
  1694   exec(scp, params);
  1695 
  1696   free(p);
  1697   free(dest);
  1698   freeList((void **) h_l);
  1699   free(pem);
  1700   free(params);
  1701 
  1702   // transform
  1703   int sR;
  1704   char b[1024];
  1705   FILE *f;
  1706 
  1707   if ((strEq(offHelpers_transform(), "enable")) && (offHelpers_transformFrom() != NULL)) {
  1708     transport_transformFrom(file);
  1709     p = malloc(strlen(offHelpers_objectPath()) + strlen(file) + 1 + 8);
  1710     sprintf(p, "%s/../tmp/%s", offHelpers_objectPath(), file);
  1711     if (offHelpers_checkIntegrity(p)) {
  1712       f = fopen(p, "r");
  1713       sR = fread(b, 1, 1024, f);
  1714       while (sR != 0) {
  1715         fwrite(b, 1, sR, stdout);
  1716         sR = fread(b, 1, 1024, f);
  1717       }
  1718       fclose(f);
  1719   }
  1720     }
  1721   else {
  1722     p = malloc(strlen(offHelpers_objectPath()) + strlen(file) + 1 + 1);
  1723     sprintf(p, "%s/%s", offHelpers_objectPath(), file);
  1724     if (offHelpers_checkIntegrity(p)) {
  1725       f = fopen(p, "r");
  1726       sR = fread(b, 1, 1024, f);
  1727       while (sR != 0) {
  1728         fwrite(b, 1, sR, stdout);
  1729         sR = fread(b, 1, 1024, f);
  1730       }
  1731       fclose(f);
  1732   }
  1733     }
  1734 
  1735   free(p);
  1736 }
  1737 
  1738 
  1739 void transportHttpSend(char *file) {
  1740   char *p = NULL;
  1741 
  1742   p = malloc(strlen(file) + 1 + 48);
  1743   sprintf(p, "Http mode is read-only: %s is stored in cache only", file);
  1744   offLogRepo(p);
  1745   free(p);
  1746 }
  1747 
  1748 void transportHttpReceive(char *file) {
  1749   char *p = NULL;
  1750   char **f_l = NULL;
  1751   DIR *d = NULL;
  1752 
  1753   // create file directories in cache
  1754   f_l = split(file, "/");
  1755   p = malloc(strlen(offHelpers_objectPath()) + strlen(f_l[0]) + strlen(f_l[1]) + 1 + 2);
  1756   sprintf(p, "%s/%s/%s", offHelpers_objectPath(), f_l[0], f_l[1]);
  1757   d = opendir(p);;
  1758   if (d == NULL) {
  1759     mkdirParents(p);
  1760   }
  1761   free(p);
  1762   freeList((void **)f_l);
  1763 
  1764   p = malloc(strlen(offHelpers_offCurlOptions()) + strlen(offHelpers_objectPath()) + strlen(file) + strlen(offHelpers_offHttp()) + strlen(file) + 1 + 4);
  1765   sprintf(p, "%s %s/%s %s/%s", offHelpers_offCurlOptions(), offHelpers_objectPath(), file, offHelpers_offHttp(), file);
  1766   exec(curl, p);
  1767   free(p);
  1768 
  1769   // transform
  1770   int sR;
  1771   char b[1024];
  1772   FILE *f;
  1773 
  1774   if ((strEq(offHelpers_transform(), "enable")) && (offHelpers_transformFrom() != NULL)) {
  1775     transport_transformFrom(file);
  1776     p = malloc(strlen(offHelpers_objectPath()) + strlen(file) + 1 + 8);
  1777     sprintf(p, "%s/../tmp/%s", offHelpers_objectPath(), file);
  1778     if (offHelpers_checkIntegrity(p)) {
  1779       f = fopen(p, "r");
  1780       sR = fread(b, 1, 1024, f);
  1781       while (sR != 0) {
  1782         fwrite(b, 1, sR, stdout);
  1783         sR = fread(b, 1, 1024, f);
  1784       }
  1785       fclose(f);
  1786   }
  1787     }
  1788   else {
  1789     p = malloc(strlen(offHelpers_objectPath()) + strlen(file) + 1 + 1);
  1790     sprintf(p, "%s/%s", offHelpers_objectPath(), file);
  1791     if (offHelpers_checkIntegrity(p)) {
  1792       f = fopen(p, "r");
  1793       sR = fread(b, 1, 1024, f);
  1794       while (sR != 0) {
  1795         fwrite(b, 1, sR, stdout);
  1796         sR = fread(b, 1, 1024, f);
  1797       }
  1798       fclose(f);
  1799   }
  1800     }
  1801 
  1802   free(p);
  1803 }
  1804 
  1805 void offHelpers_setTransport(char *mode ) {
  1806   char *m = NULL;
  1807 
  1808   // set send and receive functions for transport
  1809   // copy, scp
  1810 
  1811   // use mode from config or from parameter
  1812   if (strEq(mode, "config")) {
  1813     m = strdup(offHelpers_offMode());
  1814   }
  1815   else {
  1816     m = mode;
  1817   }
  1818 
  1819   // copy mode
  1820   if (strEq(m, "copy")) {
  1821     transport.send    = transportCopySend;
  1822     transport.receive = transportCopyReceive;
  1823   }
  1824 
  1825   // rsync mode
  1826   else if (strEq(m, "rsync")) {
  1827     transport.send    = transportRsyncSend;
  1828     transport.receive = transportRsyncReceive;
  1829   }
  1830 
  1831   // scp mode
  1832   else if (strEq(m, "scp")) {
  1833     transport.send    = transportScpSend;
  1834     transport.receive = transportScpReceive;
  1835   }
  1836 
  1837   // http mode
  1838   else if (strEq(m, "http")) {
  1839     transport.send    = transportHttpSend;
  1840     transport.receive = transportHttpReceive;
  1841   }
  1842 
  1843   if (m != mode) {
  1844     free(m);
  1845 }
  1846   }
  1847 //
  1848 //  // s3 mode
  1849 //  else if mode == 's3'
  1850 //    transport['send'] = (file) ->
  1851 //      AWS = require 'aws-sdk'
  1852 //      s3 = new AWS.S3({region: offHelpers.s3Region(), signatureVersion: 'v4'})
  1853 //
  1854 //      // upload to s3
  1855 //      upParams = {Bucket: offHelpers.s3Bucket(), Key: file, Body: ''}
  1856 //      fileStream = fs.createReadStream(offHelpers.objectPath() + '/' + file)
  1857 //      upParams.Body = fileStream
  1858 //      s3.upload(upParams, (err, data) ->
  1859 //        // TODO log eventual error
  1860 //        return
  1861 //      )
  1862 //
  1863 //      return
  1864 //    transport['receive'] = (file) ->
  1865 //      AWS = require 'aws-sdk'
  1866 //      s3 = new AWS.S3({region: offHelpers.s3Region(), signatureVersion: 'v4'})
  1867 //
  1868 //      // create file directories in cache
  1869 //      f_l = file.split '/'
  1870 //      if fs.existsSync(offHelpers.objectPath() + '/' + f_l[0] + '/' + f_l[1]) == false
  1871 //        mkdirParents offHelpers.objectPath() + '/' + f_l[0] + '/' + f_l[1]
  1872 //
  1873 //      // download from s3
  1874 //      dlParams = {Bucket: offHelpers.s3Bucket(), Key: file}
  1875 //      fileStream = fs.createWriteStream(offHelpers.objectPath() + '/' + file)
  1876 //      fileStream.on('finish', ->
  1877 //        // transfer is finished, the complete file is in the cache
  1878 //        // transform
  1879 //        if offHelpers.transform() == 'enable' and offHelpers.transformFrom() != ''
  1880 //          transport['transformFrom'](file)
  1881 //          if offHelpers.checkIntegrity offHelpers.objectPath() + '/../tmp/' + file
  1882 //            readStream = fs.createReadStream(offHelpers.objectPath() + '/../tmp/' + file)
  1883 //            readStream.pipe(process.stdout)
  1884 //        else
  1885 //          if offHelpers.checkIntegrity offHelpers.objectPath() + '/' + file
  1886 //            readStream = fs.createReadStream(offHelpers.objectPath() + '/' + file)
  1887 //            readStream.pipe(process.stdout)
  1888 //        return
  1889 //      )
  1890 //      s3.getObject(dlParams, (err, data) ->
  1891 //        // TODO log eventual error
  1892 //        return
  1893 //      ).on('httpData', (chunk) ->
  1894 //        fileStream.write chunk
  1895 //        return
  1896 //      ).on('httpDone', ->
  1897 //        fileStream.end()
  1898 //        return
  1899 //      )
  1900 //      return
  1901 //  return
  1902 
  1903 char **offHelpers_getOffFilePath(char *offFile) {
  1904   char **r = NULL;
  1905   char tmp[6];
  1906 
  1907   tmp[0] = offFile[0];
  1908   tmp[1] = offFile[1];
  1909   tmp[2] = '/';
  1910   tmp[3] = offFile[2];
  1911   tmp[4] = offFile[3];
  1912   tmp[5] = 0;
  1913 
  1914   // [offFile.slice(0,2) + '/' + offFile.slice(2,4) + '/' + offFile, offFile.slice(0,2) + '/' + offFile.slice(2,4)]
  1915   r    = malloc(3 * sizeof(int *));
  1916   r[2] = NULL;
  1917   r[0] = malloc(strlen(tmp) + strlen(offFile) + 1 + 1);
  1918   sprintf(r[0], "%s/%s", tmp, offFile);
  1919   r[1] = strdup(tmp);
  1920   return(r);
  1921 }
  1922 
  1923 
  1924 
  1925 //////
  1926 // core
  1927 //////
  1928 
  1929 // transport functions
  1930 
  1931 void send(char *src) {
  1932 
  1933   // to be initialized by setTransport
  1934   printf("SEND %s", src);
  1935   printf("\n");
  1936 }
  1937 
  1938 void receive(char *src) {
  1939 
  1940   // to be initialized by setTransport
  1941   printf("RECEIVE %s", src);
  1942   printf("\n");
  1943 }
  1944 
  1945 void transport_transformFrom(char *file) {
  1946   // transform file in objectPath to objectPath/../tmp/file
  1947   // create directories in tmp
  1948   char *offFile = NULL;
  1949   char **offFilePath = NULL;
  1950   char *tmp = NULL;
  1951   DIR *d = NULL;
  1952   char *cmd = NULL;
  1953   char *cmd2 = NULL;
  1954   char *p1 = NULL;
  1955   char *p2 = NULL;
  1956 
  1957   offFile = strdup(basename(file));
  1958   offFilePath = offHelpers_getOffFilePath(offFile);
  1959 
  1960   tmp  = malloc(strlen(offHelpers_objectPath()) + strlen(offFilePath[1]) + 1 + 8);
  1961   sprintf(tmp, "%s/../tmp/%s", offHelpers_objectPath(), offFilePath[1]);
  1962   d = opendir(tmp);
  1963   if (d == NULL) {
  1964     // create the file directory
  1965     mkdirParents(tmp);
  1966   }
  1967   free(tmp);
  1968 
  1969   cmd  = strdup(offHelpers_transformFrom());
  1970 
  1971   p1   = malloc(strlen(offHelpers_objectPath()) + strlen(file) + 1 + 1);
  1972   sprintf(p1, "%s/%s", offHelpers_objectPath(), file);
  1973   // find _1 index in cmd
  1974   tmp  = strstr(cmd, " _1");
  1975   if (tmp != NULL) {
  1976     tmp[1] = 0;
  1977   }
  1978   cmd2 = malloc(strlen(cmd) + strlen(p1) + strlen(tmp+3) + 1 + 0);
  1979   sprintf(cmd2, "%s%s%s", cmd, p1, tmp+3);
  1980   free(p1);
  1981   free(cmd);
  1982 
  1983   p2   = malloc(strlen(offHelpers_objectPath()) + strlen(file) + 1 + 8);
  1984   sprintf(p2, "%s/../tmp/%s", offHelpers_objectPath(), file);
  1985   // find _2 and replace
  1986   tmp  = strstr(cmd2, " _2");
  1987   if (tmp != NULL) {
  1988     tmp[1] = 0;
  1989   }
  1990   cmd  = malloc(strlen(cmd2) + strlen(p2) + strlen(tmp+3) + 1 + 0);
  1991   sprintf(cmd, "%s%s%s", cmd2, p2, tmp+3);
  1992   free(p2);
  1993   //print cmd
  1994   free(cmd2);
  1995   freeList((void **)offFilePath);
  1996   offFilePath = execOut(cmd);
  1997   freeList((void **)offFilePath);
  1998   free(offFile);
  1999   free(cmd);
  2000 }
  2001 
  2002 
  2003 
  2004 
  2005 
  2006 
  2007 
  2008 
  2009 
  2010 void thisrepo(cmdSetF_t cmd) {
  2011 
  2012   if ((argc > 2) && (strEq(argv[2], "thisrepo"))) {
  2013     cmd(gitConfig_setThisRepo);
  2014     return;
  2015   }
  2016   cmd(gitConfig_set);
  2017 }
  2018 
  2019 
  2020 
  2021 enum { installH, modeH, storeH, scpH, httpH, curlH, integrityH, pemH, sshoptionsH, scpoptionsH, rsyncoptionsH, scpuserH, trackH, configAlwaysH, s3regionH, s3bucketH, transformH, transformToH, transformFromH, cleanH, prepushH, smudgeH, copyToH, pushH, clearAllH, caH, clearCacheH, ccH, clearStoreH, csH, clearTmpH, ctH, defaultsH, envH, helpH, lastH };
  2022 char* COMMAND_HELP[36] = { "git off install [thisrepo]\n  setup git config (default global)\n  thisrepo sets up config in current repo", "git off mode [thisrepo] [copy|rsync|scp|http|s3]\n  set/show git off mode", "git off store [thisrepo] [path]\n  set/show git off store path for copy mode", "git off scp [thisrepo] [host]\n  setup scp config\n  host has format host:path, user@host:path, user@host:port/path\n  Example: localhost:/tmp/offStore", "git off http [thisrepo] [host]\n  setup http config\n  host has format http://host/path\n  Example http://localhost/offStore", "git off curl [thisrepo] [options]\n  setup curl config", "git off integrity [thisrepo] [enable|disable]\n  set/show git off integrity.\n  when enabled, the SHA of the file received from the store is\n  checked again the SHA of the original file", "git off pem [thisrepo] [pathToPrivateKey]\n  set/show git off pem.\n  off.pem is the private key for ssh and scp\n  set \"offNoValue\" to set an empty value (useful when there are multiple configs)", "git off sshoptions [thisrepo] [options]\n  set/show git off sshoptions", "git off scpoptions [thisrepo] [options]\n  set/show git off scpoptions", "git off rsyncoptions [thisrepo] [options]\n  set/show git off rsyncoptions", "git off scpuser [thisrepo] [username]\n  setup scp username config", "git off track\n  setup gitattribute filters\n  example: git off track \"*.bin\"\n  without parameter, list git off attributes\n  calls git off install", "git off configAlways [\"\"|GIT_OFF_CONFIG|repo|global]\n  \"\" disable configAlways\n  GIT_OFF_CONFIG load all configurations from $GIT_OFF_CONFIG\n  repo load all configurations from current git repo\n  global load all configurations from global git config\n  set \"offNoValue\" to set an empty value", "git off s3region [thisrepo] [region]\n  setup amazon s3 region for the bucket", "git off s3bucket [thisrepo] [bucket]\n  setup amazon s3 bucket", "git off transform [thisrepo] [enable|disable]\n  enable transform in clean and smudge filters", "git off transformTo [thisrepo] [\"cmd _1 _2\"]\n  setup transform command for clear filter\n  When the command is empty the regular transport is performed", "git off transformFrom [thisrepo] [\"cmd _1 _2\"]\n  setup transform command for smudge filter\n  When the command is empty the regular transport is performed", "git off clean\n  internal filter\n  dont use directly", "git off pre-push\n  internal filter\n  dont use directly", "git off smudge\n  internal filter\n  dont use directly", "git off copyTo [copy|rsync|scp|s3]\n  copy cache to store for specified mode", "git off push\n  copy cache to store for selected mode", "git off clearAll\n  delete store, cache and log", "git off ca\n  delete store, cache and log", "git off clearCache\n  delete cache in current git", "git off cc\n  delete cache in current git", "git off clearStore\n  delete store", "git off cs\n  delete store", "git off clearTmp\n  delete tmp in git off cache\n  Useful when transform is enabled", "git off ct\n  delete tmp in git off cache\n  Useful when transform is enabled", "git off defaults\n  shows first time config", "git off env\n  shows config", "git off help [cmd]\n  git off help. Run git off help command to get help for a specific command.", "not a command" };
  2023 
  2024 enum { installC, modeC, storeC, scpC, httpC, curlC, integrityC, pemC, sshoptionsC, scpoptionsC, rsyncoptionsC, scpuserC, trackC, configAlwaysC, s3regionC, s3bucketC, transformC, transformToC, transformFromC, cleanC, prepushC, smudgeC, copyToC, pushC, clearAllC, caC, clearCacheC, ccC, clearStoreC, csC, clearTmpC, ctC, defaultsC, envC, helpC, lastC };
  2025 char* COMMAND_MAP[36] = { "install", "mode", "store", "scp", "http", "curl", "integrity", "pem", "sshoptions", "scpoptions", "rsyncoptions", "scpuser", "track", "configAlways", "s3region", "s3bucket", "transform", "transformTo", "transformFrom", "clean", "pre-push", "smudge", "copyTo", "push", "clearAll", "ca", "clearCache", "cc", "clearStore", "cs", "clearTmp", "ct", "defaults", "env", "help", "not a command" };
  2026 
  2027 enum { installG, modeG, storeG, scpG, httpG, curlG, integrityG, pemG, sshoptionsG, scpoptionsG, rsyncoptionsG, scpuserG, trackG, configAlwaysG, s3regionG, s3bucketG, transformG, transformToG, transformFromG, cleanG, prepushG, smudgeG, copyToG, pushG, clearAllG, caG, clearCacheG, ccG, clearStoreG, csG, clearTmpG, ctG, defaultsG, envG, helpG, lastG };
  2028 char* COMMAND_GITCONFIG[36] = { "empty", "off.mode", "off.store", "off.scphost", "off.http", "off.curloptions", "off.integrity", "off.pem", "off.sshoptions", "off.scpoptions", "off.rsyncoptions", "off.scpuser", "empty", "off.configAlways", "off.s3region", "off.s3bucket", "off.transform", "off.transformTo", "off.transformFrom", "empty", "empty", "empty", "empty", "empty", "empty", "empty", "empty", "empty", "empty", "empty", "empty", "empty", "empty", "empty", "empty", "not a command" };
  2029 
  2030 // functions called for commands
  2031 typedef void (*cmdF_t)();
  2032 
  2033 // lastC is the index of last element
  2034 cmdF_t COMMAND_FUNC[lastC + 1 ];
  2035 
  2036 // helpers called for commands that read a git config
  2037 typedef char *(*cmdH_t)();
  2038 cmdH_t COMMAND_HELPERS[lastC + 1 ];
  2039 
  2040 int findCommand(char *p) {
  2041   int i;
  2042 
  2043   for (i=0; i < lastC ;i++) {
  2044     if (strEq(p, COMMAND_MAP[i])) {
  2045       break;
  2046   }
  2047     }
  2048   if (i == lastC) {
  2049     i = -1;
  2050   }
  2051   return(i);
  2052 }
  2053 
  2054 void showAllCommandsHelp() {
  2055   int i;
  2056 
  2057   for (i=0; i < lastH ;i++) {
  2058     printf("%s\n", COMMAND_HELP[i]);
  2059 }
  2060   }
  2061 
  2062 
  2063 
  2064 
  2065 // command line functions
  2066 // localSetup: setup current git
  2067 // install:    setup git config (default global)
  2068 // mode:       set/show git off mode
  2069 // scp:        setup scp config
  2070 // scpuser:    setup scp username config
  2071 // track:      setup gitattribute filters
  2072 // clean:      replace files handled by git off with reference
  2073 // prepush:    read stdin and calls push
  2074 // push:       copy objects to off.store
  2075 // smudge:     copy objects from off.store for files handled by git off
  2076 // clearAll:   delete store, cache in current git and log
  2077 // clearCache: delete cache in current git
  2078 // clearStore: delete store
  2079 // defaults:   show first time config (offDEFAULTS)
  2080 // env:        show config
  2081 void offCommands_localSetup() {
  2082     // setup current git
  2083     // create off directories in current .git
  2084     // install pre-push hooks
  2085     char *hook = NULL;
  2086 
  2087     mkdirParents(offHelpers_objectPath());
  2088 
  2089     hook = malloc(strlen(offHelpers_gitRepoRoot()) + 1 + 20);
  2090     sprintf(hook, "%s/.git/hooks/pre-push", offHelpers_gitRepoRoot());
  2091     if (access(hook, F_OK) == -1) {
  2092       FILE *f;
  2093       f = fopen(hook, "w");
  2094       fprintf(f, "%s\n", offDEFAULTS[prePushD]);
  2095       fclose(f);
  2096       chmod(hook, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
  2097     }
  2098     // error disabled because localSetup is run many times (called from clean and smudge)
  2099     // else
  2100     //   console.error hook.red.bold + ' already exists. Skipping pre-push hook setup.'.red.bold
  2101     //   process.exit(1)
  2102     free(hook);
  2103 }
  2104 
  2105 void offCommands_install(gConfig_set_t setF ) {
  2106 
  2107   // setup git config
  2108   // create off.store
  2109 
  2110   offCommands_localSetup();
  2111 
  2112   // setup config only if not already set
  2113   if ((gitConfig_get("filter.off.clean") == NULL) || (setF != gitConfig_set)) {
  2114     if (setF == gitConfig_setThisRepo) {
  2115       gitConfig_setLocal("filter.off.clean","git off clean %f");
  2116     }
  2117     else {
  2118       setF("filter.off.clean","git off clean %f");
  2119   }
  2120     }
  2121   if ((gitConfig_get("filter.off.smudge") == NULL) || (setF != gitConfig_set)) {
  2122     if (setF == gitConfig_setThisRepo) {
  2123       gitConfig_setLocal("filter.off.smudge","git off smudge %f");
  2124     }
  2125     else {
  2126       setF("filter.off.smudge","git off smudge %f");
  2127   }
  2128     }
  2129   if ((offHelpers_log() == NULL) || (setF != gitConfig_set)) {
  2130       // log always in global config
  2131       gitConfig_set("off.log",offDEFAULTS[logD]);
  2132   }
  2133 
  2134   if ((offHelpers_offMode() == NULL) || (setF != gitConfig_set)) {
  2135     setF("off.mode", offDEFAULTS[modeD]);
  2136   }
  2137   if ((offHelpers_offIntegrity() == NULL) || (setF != gitConfig_set)) {
  2138     setF("off.integrity", offDEFAULTS[integrityD]);
  2139   }
  2140   if ((offHelpers_offPem() == NULL) || (setF != gitConfig_set)) {
  2141     setF("off.pem", offDEFAULTS[pemD]);
  2142   }
  2143   if ((offHelpers_offScp() == NULL) || (setF != gitConfig_set)) {
  2144     setF("off.scphost", offDEFAULTS[scpHostD]);
  2145   }
  2146   if ((offHelpers_offScpUser() == NULL) || (setF != gitConfig_set)) {
  2147     setF("off.scpuser", offDEFAULTS[scpUserD]);
  2148   }
  2149   if ((offHelpers_offSshOptions() == NULL) || (setF != gitConfig_set)) {
  2150     setF("off.sshoptions", offDEFAULTS[sshOptionsD]);
  2151   }
  2152   if ((offHelpers_offScpOptions() == NULL) || (setF != gitConfig_set)) {
  2153     setF("off.scpoptions", offDEFAULTS[scpOptionsD]);
  2154   }
  2155   if ((offHelpers_offRsyncOptions() == NULL) || (setF != gitConfig_set)) {
  2156     setF("off.rsyncoptions", offDEFAULTS[rsyncOptionsD]);
  2157   }
  2158   if ((offHelpers_offStore() == NULL) || (setF != gitConfig_set)) {
  2159     setF("off.store", offDEFAULTS[storeD]);
  2160   }
  2161   if ((offHelpers_offHttp() == NULL) || (setF != gitConfig_set)) {
  2162     setF("off.http", offDEFAULTS[httpD]);
  2163   }
  2164   if ((offHelpers_offCurlOptions() == NULL) || (setF != gitConfig_set)) {
  2165     setF("off.curloptions", offDEFAULTS[curlOptionsD]);
  2166   }
  2167   if ((offHelpers_offConfigAlways() == NULL) || (setF != gitConfig_set)) {
  2168     setF("off.configAlways", offDEFAULTS[configAlwaysD]);
  2169   }
  2170   if ((offHelpers_s3Region() == NULL) || (setF != gitConfig_set)) {
  2171     setF("off.s3Region", offDEFAULTS[s3RegionD]);
  2172   }
  2173   if ((offHelpers_s3Bucket() == NULL) || (setF != gitConfig_set)) {
  2174     setF("off.s3Bucket", offDEFAULTS[s3BucketD]);
  2175   }
  2176   if ((offHelpers_transform() == NULL) || (setF != gitConfig_set)) {
  2177     setF("off.transform", offDEFAULTS[transformD]);
  2178   }
  2179   if ((offHelpers_transformTo() == NULL) || (setF != gitConfig_set)) {
  2180     setF("off.transformTo", offDEFAULTS[transformToD]);
  2181   }
  2182   if ((offHelpers_transformFrom() == NULL) || (setF != gitConfig_set)) {
  2183     setF("off.transformFrom", offDEFAULTS[transformFromD]);
  2184   }
  2185 
  2186   // create off.store
  2187 
  2188   if (strEq(runtimeConfig[offMode], "copy")) {
  2189     mkdirParents(runtimeConfig[offStore]);
  2190   }
  2191 
  2192   if (strEq(runtimeConfig[offMode], "scp") || strEq(runtimeConfig[offMode], "rsync")) {
  2193     offHelpers_mkdirStore("");
  2194 }
  2195   }
  2196 
  2197 void offCommands_track() {
  2198   FILE* f = NULL;
  2199 
  2200   // setup gitattribute filters in current folder
  2201   // list current git off attributes when there is no parameter
  2202   if (argc > 2) {
  2203     char *fileName;
  2204     char *line;
  2205     offCommands_install_setF();
  2206     fileName = malloc(strlen(offHelpers_gitRepoRoot()) + 1 + 15);
  2207     sprintf(fileName, "%s/.gitattributes", offHelpers_gitRepoRoot());
  2208     f = fopen(fileName, "a");
  2209     free(fileName);
  2210     line = malloc(strlen(argv[2]) + 1 + 17);
  2211     sprintf(line, "%s filter=off -text", argv[2]);
  2212     fprintf(f, "%s\n", line);
  2213     free(line);
  2214     fclose(f);
  2215   }
  2216   else {
  2217     // list current git off attributes
  2218     char *cmd;
  2219     char **list;
  2220     int i;
  2221 
  2222     cmd = malloc(strlen(externalHelpers[listAttr]) + strlen(offHelpers_gitRepoRoot()) + 1 + 20);
  2223     sprintf(cmd, "%s `cd %s; git ls-files`", externalHelpers[listAttr], offHelpers_gitRepoRoot());
  2224     list = execOut(cmd);
  2225     free(cmd);
  2226 
  2227     // gff/b.bin: filter: off
  2228     if (list != NULL) {
  2229       i = 0;
  2230       while (list[i] != NULL) {
  2231         // find string
  2232         if (strstr(list[i], ": filter: off") != NULL) {
  2233           printf("%s\n", list[i]);
  2234         }
  2235         i++;
  2236     }
  2237       }
  2238     freeList((void **)list);
  2239 }
  2240   }
  2241 
  2242 void offCommands_configAlways() {
  2243 
  2244   if (!strEq(argv[argc-1], "configAlways")) {
  2245     gitConfig_set(COMMAND_GITCONFIG[configAlwaysC], argv[argc-1]);
  2246   }
  2247   else {
  2248     if (COMMAND_HELPERS[configAlwaysC]()) {
  2249       // print when config is available
  2250       printf("%s %s", COMMAND_GITCONFIG[configAlwaysC], COMMAND_HELPERS[configAlwaysC]());
  2251       printf("\n");
  2252     }
  2253     else {
  2254       printf("Not in config: %s", COMMAND_GITCONFIG[configAlwaysC]);
  2255       printf("\n");
  2256 }
  2257   }
  2258     }
  2259 
  2260 void offCommands_setGetGitConfig(gConfig_set_t setF) {
  2261   int i;
  2262 
  2263   i = findCommand(argv[1]);
  2264   if ((argc > 2) && (!strEq(argv[argc-1], "thisrepo"))) {
  2265     setF(COMMAND_GITCONFIG[i], argv[argc-1]);
  2266   }
  2267   else {
  2268     if (COMMAND_HELPERS[i]()) {
  2269       printf("%s %s", COMMAND_GITCONFIG[i], COMMAND_HELPERS[i]());
  2270       printf("\n");
  2271     }
  2272     else {
  2273       printf("Not in config: %s", COMMAND_GITCONFIG[i]);
  2274       printf("\n");
  2275 }
  2276   }
  2277     }
  2278 
  2279 void offCommands_clean() {
  2280   char *file = NULL;
  2281   uint64_t size;
  2282   char **offFilePath = NULL;
  2283   char *offFile = NULL;
  2284   char *fileDir = NULL;
  2285   char *filePath = NULL;
  2286   DIR *d = NULL;
  2287   char b[1024];
  2288   FILE *f = NULL;
  2289   int sR;
  2290   char *quotedFile = NULL;
  2291 
  2292   // replace files handled by git off with reference
  2293   // stdin is data from the working directory file
  2294   //
  2295   // create local setup in case the repo is freshly cloned
  2296   // create file information (size, sha = git off filename)
  2297   // copy stdin to git off cache in current git
  2298   //   transform input file
  2299   // print git off ref to stdout for git
  2300 
  2301   // create local setup in case the repo is freshly cloned
  2302   offCommands_localSetup();
  2303 
  2304   // create file information (size, sha)
  2305   file              = argv[2];
  2306   //print file
  2307   struct stat st;
  2308   stat(file, &st);
  2309   size              = st.st_size;
  2310   quotedFile  = malloc(strlen(file) + 1 + 4);
  2311   sprintf(quotedFile, "\"%s\"", file);
  2312   offFile           = exec(sha, quotedFile);
  2313   free(quotedFile);
  2314   offFilePath       = offHelpers_getOffFilePath(offFile);
  2315 
  2316   fileDir           = malloc(strlen(offHelpers_objectPath()) + strlen(offFilePath[1]) + 1 + 1);
  2317   sprintf(fileDir, "%s/%s", offHelpers_objectPath(), offFilePath[1]);
  2318   d                 = opendir(fileDir);
  2319   if (d == NULL) {
  2320     // create the file directory
  2321     mkdirParents(fileDir);
  2322   }
  2323 
  2324   // copy stdin to git off cache in current git
  2325   // clean runs during git add and git commit, do work only once
  2326   filePath    = malloc(strlen(offHelpers_objectPath()) + strlen(offFilePath[0]) + 1 + 1);
  2327   sprintf(filePath, "%s/%s", offHelpers_objectPath(), offFilePath[0]);
  2328   if (access(filePath, F_OK) == -1) {
  2329     // stream stdin to sha in git off cache
  2330     f = fopen(filePath, "w");
  2331 
  2332     sR = fread(b, 1, 1024, stdin);
  2333     while (sR != 0) {
  2334       fwrite(b, 1, sR, f);
  2335       sR = fread(b, 1, 1024, stdin);
  2336     }
  2337     fclose(f);
  2338 
  2339     // all objects in cache are read-only
  2340     chmod(filePath, S_IRUSR | S_IRGRP | S_IROTH);
  2341 
  2342     // transform input file
  2343     if ((strEq(offHelpers_transform(), "enable")) && (offHelpers_transformTo() != NULL)) {
  2344       char *tmpDir;
  2345       tmpDir  = malloc(strlen(offHelpers_objectPath()) + strlen(offFilePath[1]) + 1 + 8);
  2346       sprintf(tmpDir, "%s/../tmp/%s", offHelpers_objectPath(), offFilePath[1]);
  2347       d = opendir(tmpDir);
  2348       if (d == NULL) {
  2349         // create the file directory
  2350         mkdirParents(tmpDir);
  2351       }
  2352 
  2353       // original file is moved to tmp
  2354       // transformed file is stored in objectPath under the same name
  2355       char *p;
  2356       p = malloc(strlen(filePath) + strlen(offHelpers_objectPath()) + strlen(offFilePath[0]) + 1 + 9);
  2357       sprintf(p, "%s %s/../tmp/%s", filePath, offHelpers_objectPath(), offFilePath[0]);
  2358       exec(mv, p);
  2359       free(p);
  2360 
  2361       char *cmd;
  2362       char *cmd2;
  2363       char *p1;
  2364       char *tmp;
  2365 
  2366       cmd = strdup(offHelpers_transformTo());
  2367 
  2368       p1   = malloc(strlen(offHelpers_objectPath()) + strlen(offFilePath[0]) + 1 + 8);
  2369       sprintf(p1, "%s/../tmp/%s", offHelpers_objectPath(), offFilePath[0]);
  2370       // find _1 index in cmd
  2371       tmp  = strstr(cmd, " _1");
  2372       if (tmp != NULL) {
  2373         tmp[1] = 0;
  2374       }
  2375       cmd2 = malloc(strlen(cmd) + strlen(p1) + strlen(tmp+3) + 1 + 0);
  2376       sprintf(cmd2, "%s%s%s", cmd, p1, tmp+3);
  2377       free(p1);
  2378       free(cmd);
  2379 
  2380       // find _2 and replace
  2381       tmp  = strstr(cmd2, " _2");
  2382       if (tmp != NULL) {
  2383         tmp[1] = 0;
  2384       }
  2385       cmd  = malloc(strlen(cmd2) + strlen(filePath) + strlen(tmp+3) + 1 + 0);
  2386       sprintf(cmd, "%s%s%s", cmd2, filePath, tmp+3);
  2387       freeList((void **)offFilePath);
  2388       offFilePath = execOut(cmd);
  2389       free(cmd);
  2390       free(tmpDir);
  2391   }
  2392     }
  2393   else {
  2394     // file already exists, discard data
  2395     f = fopen("/dev/null", "w");
  2396 
  2397     sR = fread(b, 1, 1024, stdin);
  2398     while (sR != 0) {
  2399       fwrite(b, 1, sR, f);
  2400       sR = fread(b, 1, 1024, stdin);
  2401     }
  2402     fclose(f);
  2403   }
  2404 
  2405   free(fileDir);
  2406   free(filePath);
  2407 
  2408   // print git off ref to stdout for git
  2409   // ### git-off v1 sha:be3e02b60effe3eab232d5590a6a2e2c2c2f443b size:119
  2410   char sst[20];
  2411   sprintf(sst,"%lu", size);
  2412   char *tmp;
  2413   tmp = malloc(strlen(offDEFAULTS[offSignatureD]) + strlen(offFile) + strlen(sst) + 1 + 6);
  2414   sprintf(tmp, "%s%s size:%s", offDEFAULTS[offSignatureD], offFile, sst);
  2415   printf("%s\n", tmp);
  2416   free(tmp);
  2417   freeList((void **)offFilePath);
  2418   free(offFile);
  2419   sR = 0;
  2420 }
  2421 
  2422 void offCommands_prepush() {
  2423   size_t len;
  2424   char* line = NULL;
  2425   ssize_t read;
  2426   char **list = NULL;
  2427   int count = 0;;
  2428 
  2429   // add offCommands_push function
  2430   // read stdin and calls push
  2431   // stdin (data from git) is a line with remoteName url localRef localSha remoteRef remoteSha
  2432 
  2433   offHelpers_setTransport_mode();
  2434 
  2435   read = getline(&line, &len, stdin);
  2436   if (read == -1) {
  2437     // exit directly when there is nothing to do
  2438     exit(EXIT_SUCCESS);
  2439   }
  2440   while (read != -1) {
  2441     {
  2442         char* pos = NULL;
  2443         pos = strchr(line, '\n');
  2444         if (pos != NULL)
  2445             *pos = '\0';
  2446     }
  2447     if (list == NULL) {
  2448       list          = malloc(2 * sizeof(int *));
  2449       list[1]       = NULL;
  2450     }
  2451     else {
  2452       list          = realloc(list, (count+2) * sizeof(int *));
  2453       if (list == NULL) {
  2454         exit(EXIT_FAILURE);
  2455       }
  2456       list[count+1] = NULL;
  2457     }
  2458     list[count] = strdup(line);
  2459     read = getline(&line, &len, stdin);
  2460     count++;
  2461   }
  2462 
  2463   count = 0;
  2464   while (list[count] != NULL) {
  2465     offCommands_push(list[count]);
  2466     count++;
  2467   }
  2468   free(line);
  2469   freeList((void **)list);
  2470   count = 0;
  2471 }
  2472 
  2473 void offCommands_push(char *line) {
  2474   // copy objects to off.store
  2475   //
  2476   // set remoteName url localRef localSha remoteRef remoteSha
  2477   // list missing commits in remote
  2478   // list changed objects in missing commits
  2479   // check header with git cat
  2480   // copy git off objects to off.store
  2481   char *remoteName = NULL;
  2482   char *url = NULL;
  2483   char *localRef = NULL;
  2484   char *localSha = NULL;
  2485   char *remoteRef = NULL;
  2486   char *remoteSha = NULL;
  2487   char **line_l = NULL;
  2488   char **commitListToPush = NULL;
  2489   char *cmd = NULL;
  2490   int i;
  2491 
  2492   // set remoteName url localRef localSha remoteRef remoteSha
  2493   remoteName = argv[argc-2];
  2494   url        = argv[argc-1];
  2495 
  2496   line_l     = split(line, " ");
  2497   localRef   = line_l[0];
  2498   localSha   = line_l[1];
  2499   remoteRef  = line_l[2];
  2500   remoteSha  = line_l[3];
  2501 
  2502   // list missing commits in remote
  2503   // handle empty cloned repo
  2504   if (strEq(remoteSha, "0000000000000000000000000000000000000000")) {
  2505     // run git rev-list --max-parents=0 localSha
  2506     // when an empty git was cloned.
  2507     char *parent = "--max-parents=0";;
  2508     cmd = malloc(strlen(externalHelpers[gitList]) + strlen(parent) + strlen(localSha) + 1 + 2);
  2509     sprintf(cmd, "%s %s %s", externalHelpers[gitList], parent, localSha);
  2510   }
  2511   else {
  2512     cmd = malloc(strlen(externalHelpers[gitList]) + strlen(localSha) + strlen(remoteSha) + 1 + 3);
  2513     sprintf(cmd, "%s %s ^%s", externalHelpers[gitList], localSha, remoteSha);
  2514   }
  2515   commitListToPush = execOut(cmd);
  2516   free(cmd);
  2517 
  2518   // list changed objects in missing commits
  2519   int commitListIndex = 0;
  2520   while (commitListToPush[commitListIndex] != NULL) {
  2521 
  2522     // list changes for commit sha
  2523     char **objInfoList;
  2524     cmd         = malloc(strlen(externalHelpers[gitDff]) + strlen(commitListToPush[commitListIndex]) + 1 + 1);
  2525     sprintf(cmd, "%s %s", externalHelpers[gitDff], commitListToPush[commitListIndex]);
  2526     objInfoList = execOut(cmd);
  2527     free(cmd);
  2528     // :000000 100644 0000000000000000000000000000000000000000 23636079ef005f53e81066aaf092521d8f2346df A      dsd
  2529     // TODO filter according to .gitattributes, check files in filter only
  2530 
  2531     int oi = 0;
  2532     while (objInfoList[oi] != NULL) {
  2533       char **objInfo;
  2534       objInfo = split(objInfoList[oi], " ");
  2535 
  2536       // only lines starting with : (the first line from git diff-tree is the sha)
  2537       // dont consider deleted files
  2538       if ((objInfoList[oi][0] == ':') && (objInfo[oiNAME][0] != 'D')) {
  2539 
  2540         // check header with git cat
  2541 
  2542         char *offRef;
  2543         offRef = exec(gitCat, objInfo[oiOID]);
  2544 
  2545         if (offRef && (strstr(offRef, offDEFAULTS[offSignatureD]) != NULL)) {
  2546 
  2547           // copy git off objects to off.store
  2548           // found an off reference
  2549           // ### git-off v1 sha:be3e02b60effe3eab232d5590a6a2e2c2c2f443b size:119 b.bin
  2550 
  2551           char **offRefS1;
  2552           char **offRefS2;
  2553           char *offFile;
  2554           char **offFilePath;
  2555           offRefS1    = split(offRef, ":");
  2556           offRefS2    = split(offRefS1[1], " ");
  2557           offFile     = offRefS2[0];
  2558           offFilePath = offHelpers_getOffFilePath(offFile);
  2559 
  2560           transport.send(offFilePath[0]);
  2561           freeList((void **)offFilePath);
  2562           freeList((void **)offRefS1);
  2563           freeList((void **)offRefS2);
  2564         }
  2565         free(offRef);
  2566       }
  2567       freeList((void **)objInfo);
  2568       oi++;
  2569     }
  2570     freeList((void **)objInfoList);
  2571     commitListIndex++;
  2572   }
  2573 
  2574   freeList((void **)commitListToPush);
  2575   freeList((void **)line_l);
  2576 
  2577   // test prevent push by issuing an error
  2578   //exit(EXIT_FAILURE);
  2579 }
  2580 
  2581 void offCommands_smudge() {
  2582 
  2583   // copy objects from off.store for files handled by git off
  2584   // stdin is the data from the git repo
  2585   //
  2586   // load header
  2587   // for git off object, replace git off reference with data from cache or off.store
  2588   // for normal object, copy data from repo to stdout
  2589 
  2590   offHelpers_setTransport_mode();
  2591   offCommands_localSetup();
  2592 
  2593   // load header
  2594   char b[1024];
  2595   int sR;
  2596   sR = fread(b, 1, strlen(offDEFAULTS[offSignatureD]) + offDEFAULTS_shaLength, stdin);
  2597   if (sR == 0) {
  2598     // error no data
  2599     offLogRepo("smudge error: cant get data from stdin.");
  2600     exit(EXIT_FAILURE);
  2601   }
  2602 
  2603   if (strncmp(b, offDEFAULTS[offSignatureD], strlen(offDEFAULTS[offSignatureD])) == 0) {
  2604     // for git off object, replace git off reference with data from cache or off.store
  2605 
  2606     b[strlen(offDEFAULTS[offSignatureD])  + offDEFAULTS_shaLength] = 0;
  2607     char **header_l;
  2608     char *offFile;
  2609     char **offFilePath;
  2610     header_l    = split(b, ":");
  2611     offFile     = header_l[1];
  2612     offFilePath = offHelpers_getOffFilePath(offFile);
  2613 
  2614     // detect if file is already in cache
  2615     char *filePath;
  2616     filePath    = malloc(strlen(offHelpers_objectPath()) + strlen(offFilePath[0]) + 1 + 1);
  2617     sprintf(filePath, "%s/%s", offHelpers_objectPath(), offFilePath[0]);
  2618     if (access(filePath, F_OK) != -1) {
  2619       // copy from cache
  2620       FILE *fCache;
  2621       if ((strEq(offHelpers_transform(), "enable")) && (offHelpers_transformFrom() != NULL)) {
  2622         // transform file
  2623 
  2624         transport_transformFrom(offFilePath[0]);
  2625 
  2626         char *p;
  2627         p     = malloc(strlen(offHelpers_objectPath()) + strlen(offFilePath[0]) + 1 + 8);
  2628         sprintf(p, "%s/../tmp/%s", offHelpers_objectPath(), offFilePath[0]);
  2629 
  2630         fCache = fopen(p, "r");
  2631 
  2632         free(p);
  2633       }
  2634       else {
  2635         fCache = fopen(filePath, "r");
  2636       }
  2637 
  2638       sR = fread(b, 1, 1024, fCache);
  2639       while (sR != 0) {
  2640         fwrite(b, 1, sR, stdout);
  2641         sR = fread(b, 1, 1024, fCache);
  2642       }
  2643       fclose(fCache);
  2644     }
  2645     else {
  2646       // copy from off.store
  2647       transport.receive(offFilePath[0]);
  2648     }
  2649 
  2650     free(filePath);
  2651     freeList((void **)header_l);
  2652     freeList((void **)offFilePath);
  2653   }
  2654   else {
  2655     // for normal object, copy data from repo to stdout
  2656 
  2657     //store header
  2658     fwrite(b, 1, sR, stdout);
  2659 
  2660     sR = fread(b, 1, 1024, stdin);
  2661     while (sR != 0) {
  2662       fwrite(b, 1, sR, stdout);
  2663       sR = fread(b, 1, 1024, stdin);
  2664   }
  2665     }
  2666   sR = 0;
  2667 }
  2668 
  2669 void offCommands_copyTo() {
  2670 
  2671   // copy cache to store for argv[2] mode
  2672   if (argc < 3) {
  2673     printf("Choose a mode where to copy the cache");
  2674     printf("\n");
  2675     return;
  2676   }
  2677 
  2678   offHelpers_setTransport(argv[2]);
  2679   offHelpers_copyTo();
  2680 }
  2681 
  2682 void offCommands_pushTo() {
  2683 
  2684   if (offHelpers_offMode() != NULL) {
  2685     printf("mode: %s", offHelpers_offMode());
  2686     printf("\n");
  2687     offHelpers_setTransport(offHelpers_offMode());
  2688     offHelpers_copyTo();
  2689 }
  2690   }
  2691 
  2692 void offCommands_clearAll() {
  2693 
  2694   // delete store, cache in current git and log
  2695   offCommands_clearStore();
  2696   offCommands_clearCache();
  2697   rmAll(offHelpers_getLog());
  2698 }
  2699 
  2700 void offCommands_clearCache() {
  2701   char *p = NULL;
  2702 
  2703   // delete cache in current git
  2704   p = malloc(strlen(offHelpers_gitRepoRoot()) + 1 + 9);
  2705   sprintf(p, "%s/.git/off", offHelpers_gitRepoRoot());
  2706   rmAll(p);
  2707   free(p);
  2708 }
  2709 
  2710 void offCommands_clearStore() {
  2711 
  2712   // delete store
  2713   if (strEq(offHelpers_offMode(), "copy")) {
  2714     rmAll(offHelpers_offStore());
  2715   }
  2716   if (strEq(offHelpers_offMode(), "scp") || strEq(offHelpers_offMode(), "rsync")) {
  2717     offHelpers_rmAllStore("");
  2718 }
  2719   }
  2720 
  2721 void offCommands_clearTmp() {
  2722   char *p = NULL;
  2723 
  2724   // delete tmp in cache in current git
  2725   p = malloc(strlen(offHelpers_gitRepoRoot()) + 1 + 13);
  2726   sprintf(p, "%s/.git/off/tmp", offHelpers_gitRepoRoot());
  2727   rmAll(p);
  2728   free(p);
  2729 }
  2730 
  2731 void offCommands_defaults() {
  2732   int i;
  2733 
  2734   // TODO add default key strings
  2735   // show first time config (offDEFAULTS)
  2736   for (i=0; i < lastD ; i++) {
  2737     printf("%s\n", offDEFAULTS[i]);
  2738 }
  2739   }
  2740 
  2741 void offCommands_env() {
  2742   char *s = NULL;
  2743 
  2744   s = offHelpers_offMode();
  2745   if (s != NULL) {
  2746     printf("off.mode %s", offHelpers_offMode());
  2747     printf("\n");
  2748   }
  2749   s = offHelpers_offIntegrity();
  2750   if (s != NULL) {
  2751     printf("off.integrity %s", offHelpers_offIntegrity());
  2752     printf("\n");
  2753   }
  2754   s = offHelpers_offPem();
  2755   if (s != NULL) {
  2756     printf("off.pem %s", offHelpers_offPem());
  2757     printf("\n");
  2758   }
  2759   s = offHelpers_offSshOptions();
  2760   if (s != NULL) {
  2761     printf("off.sshoptions %s", offHelpers_offSshOptions());
  2762     printf("\n");
  2763   }
  2764   s = offHelpers_offScpOptions();
  2765   if (s != NULL) {
  2766     printf("off.scpoptions %s", offHelpers_offScpOptions());
  2767     printf("\n");
  2768   }
  2769   s = offHelpers_offRsyncOptions();
  2770   if (s != NULL) {
  2771     printf("off.rsyncoptions %s", offHelpers_offRsyncOptions());
  2772     printf("\n");
  2773   }
  2774   s = offHelpers_offStore();
  2775   if (s != NULL) {
  2776     printf("off.store %s", offHelpers_offStore());
  2777     printf("\n");
  2778   }
  2779   s = offHelpers_offHttp();
  2780   if (s != NULL) {
  2781     printf("off.http %s", offHelpers_offHttp());
  2782     printf("\n");
  2783   }
  2784   s = offHelpers_offCurlOptions();
  2785   if (s != NULL) {
  2786     printf("off.curloptions %s", offHelpers_offCurlOptions());
  2787     printf("\n");
  2788   }
  2789   s = offHelpers_offScp();
  2790   if (s != NULL) {
  2791     printf("off.scphost %s", offHelpers_offScp());
  2792     printf("\n");
  2793   }
  2794   s = offHelpers_offScpUser();
  2795   if (s != NULL) {
  2796     printf("off.scpuser %s", offHelpers_offScpUser());
  2797     printf("\n");
  2798   }
  2799   s = offHelpers_getLog();
  2800   if (s != NULL) {
  2801     printf("off.log %s", offHelpers_getLog());
  2802     printf("\n");
  2803   }
  2804   s = offHelpers_offConfigAlways();
  2805   if (s != NULL) {
  2806     printf("off.configAlways %s", offHelpers_offConfigAlways());
  2807     printf("\n");
  2808   }
  2809   s = offHelpers_s3Region();
  2810   if (s != NULL) {
  2811     printf("off.s3region %s", offHelpers_s3Region());
  2812     printf("\n");
  2813   }
  2814   s = offHelpers_s3Bucket();
  2815   if (s != NULL) {
  2816     printf("off.s3bucket %s", offHelpers_s3Bucket());
  2817     printf("\n");
  2818   }
  2819   s = offHelpers_transform();
  2820   if (s != NULL) {
  2821     printf("off.transform %s", offHelpers_transform());
  2822     printf("\n");
  2823   }
  2824   s = offHelpers_transformTo();
  2825   if (s != NULL) {
  2826     printf("off.transformTo %s", offHelpers_transformTo());
  2827     printf("\n");
  2828   }
  2829   s = offHelpers_transformFrom();
  2830   if (s != NULL) {
  2831     printf("off.transformFrom %s", offHelpers_transformFrom());
  2832     printf("\n");
  2833 }
  2834   }
  2835 
  2836 void offCommands_help() {
  2837 
  2838   printf("git-off help\n");
  2839   printf("\n");
  2840   if (argc < 3) {
  2841     printf("# Quick Start");
  2842     printf("\n");
  2843     printf("Setup:");
  2844     printf("\n");
  2845     printf("git off track '*.bin'");
  2846     printf("\n");
  2847     printf("git add .");
  2848     printf("\n");
  2849     printf("git commit");
  2850     printf("\n");
  2851     printf("git push");
  2852     printf("\n");
  2853     printf("git checkout master");
  2854     printf("\n");
  2855     printf("\n# Other");
  2856     printf("\n");
  2857     printf("git off install");
  2858     printf("\n");
  2859     printf("git off mode scp");
  2860     printf("\n");
  2861     printf("git off scp localhost:/tmp/offStore");
  2862     printf("\n");
  2863     printf("git off scpuser username");
  2864     printf("\n");
  2865     printf("git off cc");
  2866     printf("\n");
  2867     printf("git off ca");
  2868     printf("\n");
  2869     printf("git off env");
  2870     printf("\n");
  2871     printf("git off defaults\n");
  2872     printf("\n");
  2873 
  2874     showAllCommandsHelp();
  2875 
  2876     printf("More information at https://noulin.net/git-off/file/README.md.html");
  2877     printf("\n");
  2878   }
  2879   else {
  2880     int cmd;
  2881     cmd = findCommand(argv[2]);
  2882     if (cmd == -1) {
  2883       // show help for help
  2884       printf("git-off command \"%s\" not found.\n", argv[2]);
  2885       printf("\n");
  2886       printf("%s\n", COMMAND_HELP[helpC]);
  2887     }
  2888     else {
  2889       printf("%s\n", COMMAND_HELP[cmd]);
  2890 }
  2891   }
  2892     }
  2893 
  2894 // --------------------------------------------------------------------------------------------
  2895 void installF() {
  2896 
  2897   thisrepo(offCommands_install);
  2898 }
  2899 
  2900 void modeF() {
  2901 
  2902   thisrepo(offCommands_setGetGitConfig);
  2903 }
  2904 
  2905 void storeF() {
  2906 
  2907   thisrepo(offCommands_setGetGitConfig);
  2908 }
  2909 
  2910 void scpF() {
  2911 
  2912   thisrepo(offCommands_setGetGitConfig);
  2913 }
  2914 
  2915 void httpF() {
  2916 
  2917   thisrepo(offCommands_setGetGitConfig);
  2918 }
  2919 
  2920 void curlF() {
  2921 
  2922   thisrepo(offCommands_setGetGitConfig);
  2923 }
  2924 
  2925 void integrityF() {
  2926 
  2927   thisrepo(offCommands_setGetGitConfig);
  2928 }
  2929 
  2930 void pemF() {
  2931 
  2932   thisrepo(offCommands_setGetGitConfig);
  2933 }
  2934 
  2935 void sshoptionsF() {
  2936 
  2937   thisrepo(offCommands_setGetGitConfig);
  2938 }
  2939 
  2940 void scpoptionsF() {
  2941 
  2942   thisrepo(offCommands_setGetGitConfig);
  2943 }
  2944 
  2945 void rsyncoptionsF() {
  2946 
  2947   thisrepo(offCommands_setGetGitConfig);
  2948 }
  2949 
  2950 void scpuserF() {
  2951 
  2952   thisrepo(offCommands_setGetGitConfig);
  2953 }
  2954 
  2955 void trackF() {
  2956 
  2957   offCommands_track();
  2958 }
  2959 
  2960 void configAlwaysF() {
  2961 
  2962   offCommands_configAlways();
  2963 }
  2964 
  2965 void s3regionF() {
  2966 
  2967   thisrepo(offCommands_setGetGitConfig);
  2968 }
  2969 
  2970 void s3bucketF() {
  2971 
  2972   thisrepo(offCommands_setGetGitConfig);
  2973 }
  2974 
  2975 void transformF() {
  2976 
  2977   thisrepo(offCommands_setGetGitConfig);
  2978 }
  2979 
  2980 void transformToF() {
  2981 
  2982   thisrepo(offCommands_setGetGitConfig);
  2983 }
  2984 
  2985 void transformFromF() {
  2986 
  2987   thisrepo(offCommands_setGetGitConfig);
  2988 }
  2989 
  2990 void cleanF() {
  2991 
  2992   offCommands_clean();
  2993 }
  2994 
  2995 void prepushF() {
  2996 
  2997   offCommands_prepush();
  2998 }
  2999 
  3000 void smudgeF() {
  3001 
  3002   offCommands_smudge();
  3003 }
  3004 
  3005 void copyToF() {
  3006 
  3007   offCommands_copyTo();
  3008 }
  3009 
  3010 void pushF() {
  3011 
  3012   offCommands_pushTo();
  3013 }
  3014 
  3015 void clearAllF() {
  3016 
  3017   offCommands_clearAll();
  3018 }
  3019 
  3020 void caF() {
  3021 
  3022   offCommands_clearAll();
  3023 }
  3024 
  3025 void clearCacheF() {
  3026 
  3027   offCommands_clearCache();
  3028 }
  3029 
  3030 void ccF() {
  3031 
  3032   offCommands_clearCache();
  3033 }
  3034 
  3035 void clearStoreF() {
  3036 
  3037   offCommands_clearStore();
  3038 }
  3039 
  3040 void csF() {
  3041 
  3042   offCommands_clearStore();
  3043 }
  3044 
  3045 void clearTmpF() {
  3046 
  3047   offCommands_clearTmp();
  3048 }
  3049 
  3050 void ctF() {
  3051 
  3052   offCommands_clearTmp();
  3053 }
  3054 
  3055 void defaultsF() {
  3056 
  3057   offCommands_defaults();
  3058 }
  3059 
  3060 void envF() {
  3061 
  3062   offCommands_env();
  3063 }
  3064 
  3065 void helpF() {
  3066 
  3067   offCommands_help();
  3068 }
  3069 
  3070 // Initialize commands
  3071 void initCOMMAND_FUNC() {
  3072 
  3073   COMMAND_FUNC[0]     = installF;
  3074   COMMAND_FUNC[1]     = modeF;
  3075   COMMAND_FUNC[2]     = storeF;
  3076   COMMAND_FUNC[3]     = scpF;
  3077   COMMAND_FUNC[4]     = httpF;
  3078   COMMAND_FUNC[5]     = curlF;
  3079   COMMAND_FUNC[6]     = integrityF;
  3080   COMMAND_FUNC[7]     = pemF;
  3081   COMMAND_FUNC[8]     = sshoptionsF;
  3082   COMMAND_FUNC[9]     = scpoptionsF;
  3083   COMMAND_FUNC[10]    = rsyncoptionsF;
  3084   COMMAND_FUNC[11]    = scpuserF;
  3085   COMMAND_FUNC[12]    = trackF;
  3086   COMMAND_FUNC[13]    = configAlwaysF;
  3087   COMMAND_FUNC[14]    = s3regionF;
  3088   COMMAND_FUNC[15]    = s3bucketF;
  3089   COMMAND_FUNC[16]    = transformF;
  3090   COMMAND_FUNC[17]    = transformToF;
  3091   COMMAND_FUNC[18]    = transformFromF;
  3092   COMMAND_FUNC[19]    = cleanF;
  3093   COMMAND_FUNC[20]    = prepushF;
  3094   COMMAND_FUNC[21]    = smudgeF;
  3095   COMMAND_FUNC[22]    = copyToF;
  3096   COMMAND_FUNC[23]    = pushF;
  3097   COMMAND_FUNC[24]    = clearAllF;
  3098   COMMAND_FUNC[25]    = caF;
  3099   COMMAND_FUNC[26]    = clearCacheF;
  3100   COMMAND_FUNC[27]    = ccF;
  3101   COMMAND_FUNC[28]    = clearStoreF;
  3102   COMMAND_FUNC[29]    = csF;
  3103   COMMAND_FUNC[30]    = clearTmpF;
  3104   COMMAND_FUNC[31]    = ctF;
  3105   COMMAND_FUNC[32]    = defaultsF;
  3106   COMMAND_FUNC[33]    = envF;
  3107   COMMAND_FUNC[34]    = helpF;
  3108   COMMAND_FUNC[lastC] = NULL;
  3109 
  3110   COMMAND_HELPERS[0]     = NULL;
  3111   COMMAND_HELPERS[1]     = offHelpers_offMode;
  3112   COMMAND_HELPERS[2]     = offHelpers_offStore;
  3113   COMMAND_HELPERS[3]     = offHelpers_offScp;
  3114   COMMAND_HELPERS[4]     = offHelpers_offHttp;
  3115   COMMAND_HELPERS[5]     = offHelpers_offCurlOptions;
  3116   COMMAND_HELPERS[6]     = offHelpers_offIntegrity;
  3117   COMMAND_HELPERS[7]     = offHelpers_offPem;
  3118   COMMAND_HELPERS[8]     = offHelpers_offSshOptions;
  3119   COMMAND_HELPERS[9]     = offHelpers_offScpOptions;
  3120   COMMAND_HELPERS[10]    = offHelpers_offRsyncOptions;
  3121   COMMAND_HELPERS[11]    = offHelpers_offScpUser;
  3122   COMMAND_HELPERS[12]    = NULL;
  3123   COMMAND_HELPERS[13]    = offHelpers_offConfigAlways;
  3124   COMMAND_HELPERS[14]    = offHelpers_s3Region;
  3125   COMMAND_HELPERS[15]    = offHelpers_s3Bucket;
  3126   COMMAND_HELPERS[16]    = offHelpers_transform;
  3127   COMMAND_HELPERS[17]    = offHelpers_transformTo;
  3128   COMMAND_HELPERS[18]    = offHelpers_transformFrom;
  3129   COMMAND_HELPERS[19]    = NULL;
  3130   COMMAND_HELPERS[20]    = NULL;
  3131   COMMAND_HELPERS[21]    = NULL;
  3132   COMMAND_HELPERS[22]    = NULL;
  3133   COMMAND_HELPERS[23]    = NULL;
  3134   COMMAND_HELPERS[24]    = NULL;
  3135   COMMAND_HELPERS[25]    = NULL;
  3136   COMMAND_HELPERS[26]    = NULL;
  3137   COMMAND_HELPERS[27]    = NULL;
  3138   COMMAND_HELPERS[28]    = NULL;
  3139   COMMAND_HELPERS[29]    = NULL;
  3140   COMMAND_HELPERS[30]    = NULL;
  3141   COMMAND_HELPERS[31]    = NULL;
  3142   COMMAND_HELPERS[32]    = NULL;
  3143   COMMAND_HELPERS[33]    = NULL;
  3144   COMMAND_HELPERS[34]    = NULL;
  3145   COMMAND_HELPERS[lastC] = NULL;
  3146 }
  3147 
  3148 #ifndef unitTest
  3149 // Remove main when running the unit tests
  3150 #define MAIN   main
  3151 #endif
  3152 int MAIN(int ARGC, char** ARGV) {
  3153   int dummy;
  3154   // TODO remove dummy
  3155 
  3156   argc = ARGC; argv = ARGV;;// init transport functions
  3157   transport.send    = send;
  3158   transport.receive = receive;
  3159   initCOMMAND_FUNC();
  3160 
  3161   // parse CLI arguments
  3162 
  3163   if (argc == 1) {
  3164     offCommands_help();
  3165   }
  3166   else {
  3167     // call argv[1] command
  3168     int i;
  3169     i = findCommand(argv[1]);
  3170     if (i == -1) {
  3171       offCommands_help();
  3172     }
  3173     else {
  3174       COMMAND_FUNC[i]();
  3175 }
  3176   }
  3177     }