git-off

Log

Files

Refs

README

git-off.c (79791B)

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