projectroot (empty) → 0.1.0.0
raw patch · 8 files changed
+557/−0 lines, 8 filesdep +basedep +directorysetup-changed
Dependencies added: base, directory
Files
- LICENSE +20/−0
- Setup.hs +2/−0
- projectroot.cabal +27/−0
- projectroot/deps/commander/commander.c +269/−0
- projectroot/deps/commander/commander.h +88/−0
- projectroot/libprojectroot.c +90/−0
- projectroot/libprojectroot.h +16/−0
- src/System/Directory/ProjectRoot.hs +45/−0
+ LICENSE view
@@ -0,0 +1,20 @@+Copyright (c) 2015 Pedro Tacla Yamada++Permission is hereby granted, free of charge, to any person obtaining+a copy of this software and associated documentation files (the+"Software"), to deal in the Software without restriction, including+without limitation the rights to use, copy, modify, merge, publish,+distribute, sublicense, and/or sell copies of the Software, and to+permit persons to whom the Software is furnished to do so, subject to+the following conditions:++The above copyright notice and this permission notice shall be included+in all copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ projectroot.cabal view
@@ -0,0 +1,27 @@+name: projectroot+version: 0.1.0.0+synopsis: Bindings to the projectroot C logic+description: Simple way of finding the root of a project given an+ entry-point. This module provides bindings to the+ <https://github.com/yamadapc/projectroot projectroot> C library+homepage: https://gitlab.com/yamadapc/haskell-projectroot+license: MIT+license-file: LICENSE+author: Pedro Tacla Yamada+maintainer: tacla.yamada@gmail.com+copyright: Copyright (c) 2015 Pedro Tacla Yamada+category: System+build-type: Simple+cabal-version: >=1.10++library+ exposed-modules: System.Directory.ProjectRoot+ build-depends: base >=4.8 && <4.9+ , directory+ hs-source-dirs: src+ default-language: Haskell2010+ c-sources: ./projectroot/libprojectroot.c+ , ./projectroot/deps/commander/commander.c+ install-includes: ./projectroot/libprojectroot.h+ , ./projectroot/deps/commander/commander.h+ include-dirs: .
+ projectroot/deps/commander/commander.c view
@@ -0,0 +1,269 @@++//+// commander.c+//+// Copyright (c) 2012 TJ Holowaychuk <tj@vision-media.ca>+//++#include <stdio.h>+#include <stdlib.h>+#include <string.h>+#include <assert.h>+#include "commander.h"++/*+ * Output error and exit.+ */++static void+error(char *msg) {+ fprintf(stderr, "%s\n", msg);+ exit(1);+}++/*+ * Output command version.+ */++static void+command_version(command_t *self) {+ printf("%s\n", self->version);+ command_free(self);+ exit(0);+}++/*+ * Output command help.+ */++void+command_help(command_t *self) {+ printf("\n");+ printf(" Usage: %s %s\n", self->name, self->usage);+ printf("\n");+ printf(" Options:\n");+ printf("\n");++ int i;+ for (i = 0; i < self->option_count; ++i) {+ command_option_t *option = &self->options[i];+ printf(" %s, %-25s %s\n"+ , option->small+ , option->large_with_arg+ , option->description);+ }++ printf("\n");+ command_free(self);+ exit(0);+}++/*+ * Initialize with program `name` and `version`.+ */++void+command_init(command_t *self, const char *name, const char *version) {+ self->arg = NULL;+ self->name = name;+ self->version = version;+ self->option_count = self->argc = 0;+ self->usage = "[options]";+ self->nargv = NULL;+ command_option(self, "-V", "--version", "output program version", command_version);+ command_option(self, "-h", "--help", "output help information", command_help);+}++/*+ * Free up commander after use.+ */++void+command_free(command_t *self) {+ int i;++ for (i = 0; i < self->option_count; ++i) {+ command_option_t *option = &self->options[i];+ free(option->argname);+ free(option->large);+ }++ if (self->nargv) {+ for (i = 0; self->nargv[i]; ++i) {+ free(self->nargv[i]);+ }+ free(self->nargv);+ }+}++/*+ * Parse argname from `str`. For example+ * Take "--required <arg>" and populate `flag`+ * with "--required" and `arg` with "<arg>".+ */++static void+parse_argname(const char *str, char *flag, char *arg) {+ int buffer = 0;+ size_t flagpos = 0;+ size_t argpos = 0;+ size_t len = strlen(str);+ size_t i;++ for (i = 0; i < len; ++i) {+ if (buffer || '[' == str[i] || '<' == str[i]) {+ buffer = 1;+ arg[argpos++] = str[i];+ } else {+ if (' ' == str[i]) continue;+ flag[flagpos++] = str[i];+ }+ }++ arg[argpos] = '\0';+ flag[flagpos] = '\0';+}++/*+ * Normalize the argument vector by exploding+ * multiple options (if any). For example+ * "foo -abc --scm git" -> "foo -a -b -c --scm git"+ */++static char **+normalize_args(int *argc, char **argv) {+ int size = 0;+ int alloc = *argc + 1;+ char **nargv = malloc(alloc * sizeof(char *));+ int i;++ for (i = 0; argv[i]; ++i) {+ const char *arg = argv[i];+ size_t len = strlen(arg);++ // short flag+ if (len > 2 && '-' == arg[0] && !strchr(arg + 1, '-')) {+ alloc += len - 2;+ nargv = realloc(nargv, alloc * sizeof(char *));+ for (size_t j = 1; j < len; ++j) {+ nargv[size] = malloc(3);+ sprintf(nargv[size], "-%c", arg[j]);+ size++;+ }+ continue;+ }++ // regular arg+ nargv[size] = malloc(len + 1);+ strcpy(nargv[size], arg);+ size++;+ }++ nargv[size] = NULL;+ *argc = size;+ return nargv;+}++/*+ * Define an option.+ */++void+command_option(command_t *self, const char *small, const char *large, const char *desc, command_callback_t cb) {+ if (self->option_count == COMMANDER_MAX_OPTIONS) {+ command_free(self);+ error("Maximum option definitions exceeded");+ }+ int n = self->option_count++;+ command_option_t *option = &self->options[n];+ option->cb = cb;+ option->small = small;+ option->description = desc;+ option->required_arg = option->optional_arg = 0;+ option->large_with_arg = large;+ option->argname = malloc(strlen(large) + 1);+ assert(option->argname);+ option->large = malloc(strlen(large) + 1);+ assert(option->large);+ parse_argname(large, option->large, option->argname);+ if ('[' == option->argname[0]) option->optional_arg = 1;+ if ('<' == option->argname[0]) option->required_arg = 1;+}++/*+ * Parse `argv` (internal).+ * Input arguments should be normalized first+ * see `normalize_args`.+ */++static void+command_parse_args(command_t *self, int argc, char **argv) {+ int literal = 0;+ int i, j;++ for (i = 1; i < argc; ++i) {+ const char *arg = argv[i];+ for (j = 0; j < self->option_count; ++j) {+ command_option_t *option = &self->options[j];++ // match flag+ if (!strcmp(arg, option->small) || !strcmp(arg, option->large)) {+ self->arg = NULL;++ // required+ if (option->required_arg) {+ arg = argv[++i];+ if (!arg || '-' == arg[0]) {+ fprintf(stderr, "%s %s argument required\n", option->large, option->argname);+ command_free(self);+ exit(1);+ }+ self->arg = arg;+ }++ // optional+ if (option->optional_arg) {+ if (argv[i + 1] && '-' != argv[i + 1][0]) {+ self->arg = argv[++i];+ }+ }++ // invoke callback+ option->cb(self);+ goto match;+ }+ }++ // --+ if ('-' == arg[0] && '-' == arg[1] && 0 == arg[2]) {+ literal = 1;+ goto match;+ }++ // unrecognized+ if ('-' == arg[0] && !literal) {+ fprintf(stderr, "unrecognized flag %s\n", arg);+ command_free(self);+ exit(1);+ }++ int n = self->argc++;+ if (n == COMMANDER_MAX_ARGS) {+ command_free(self);+ error("Maximum number of arguments exceeded");+ }+ self->argv[n] = (char *) arg;+ match:;+ }+}++/*+ * Parse `argv` (public).+ */++void+command_parse(command_t *self, int argc, char **argv) {+ self->nargv = normalize_args(&argc, argv);+ command_parse_args(self, argc, self->nargv);+ self->argv[self->argc] = NULL;+}
+ projectroot/deps/commander/commander.h view
@@ -0,0 +1,88 @@++//+// commander.h+//+// Copyright (c) 2012 TJ Holowaychuk <tj@vision-media.ca>+//++#ifndef COMMANDER_H+#define COMMANDER_H++/*+ * Max options that can be defined.+ */++#ifndef COMMANDER_MAX_OPTIONS+#define COMMANDER_MAX_OPTIONS 32+#endif++/*+ * Max arguments that can be passed.+ */++#ifndef COMMANDER_MAX_ARGS+#define COMMANDER_MAX_ARGS 32+#endif++/*+ * Command struct.+ */++struct command;++/*+ * Option callback.+ */++typedef void (* command_callback_t)(struct command *self);++/*+ * Command option.+ */++typedef struct {+ int optional_arg;+ int required_arg;+ char *argname;+ char *large;+ const char *small;+ const char *large_with_arg;+ const char *description;+ command_callback_t cb;+} command_option_t;++/*+ * Command.+ */++typedef struct command {+ void *data;+ const char *usage;+ const char *arg;+ const char *name;+ const char *version;+ int option_count;+ command_option_t options[COMMANDER_MAX_OPTIONS];+ int argc;+ char *argv[COMMANDER_MAX_ARGS];+ char **nargv;+} command_t;++// prototypes++void+command_init(command_t *self, const char *name, const char *version);++void+command_free(command_t *self);++void+command_help(command_t *self);++void+command_option(command_t *self, const char *small, const char *large, const char *desc, command_callback_t cb);++void+command_parse(command_t *self, int argc, char **argv);++#endif /* COMMANDER_H */
+ projectroot/libprojectroot.c view
@@ -0,0 +1,90 @@+#include <dirent.h>+#include <glob.h>+#include <libgen.h>+#include <stdio.h>+#include <stdlib.h>+#include <string.h>+#include <unistd.h>+#include "libprojectroot.h"++int weight_mode_on = 0;+int verbose_on = 0;++static const char* const candidates[] = {+ ".git",+ ".hg",+ ".fslckout",+ ".bzr",+ "_darcs",+ ".svn",+ "CVS",+ "rebar.config",+ "project.clj",+ "SConstruct",+ "pom.xml",+ "build.sbt",+ "build.gradle",+ "Gemfile",+ "requirements.txt",+ "tox.ini",+ "package.json",+ "node_modules",+ "gulpfile.js",+ "Gruntfile.js",+ "bower.json",+ "composer.json",+ "Cargo.toml",+ "stack.yaml",+ ".cabal-sandbox",+ "*.cabal",+ "mix.exs",+};++int is_project_root(char* cdt) {+ char pattern[1024];+ glob_t res;+ size_t cdt_size = strlen(cdt);++ strcpy(pattern, cdt);+ strcat(pattern, "/");++ for(int i = 0; i < N_CANDIDATES; i++) {+ strcat(pattern, candidates[i]);+ if(verbose_on) fprintf(stderr, "Matching: %s ~= %s\n", cdt, pattern);+ if(glob(pattern, GLOB_NOSORT, NULL, &res) == 0) {+ if(verbose_on) fprintf(stderr, "--> Matched (weight %d)\n", N_CANDIDATES - i + 1);+ return N_CANDIDATES - i + 1;+ }+ pattern[cdt_size + 1] = 0;+ }++ return 0;+}++char* find_project_root_weighted(char* cwd) {+ char* cdt = cwd;+ char* best_cdt = malloc(sizeof(char) * 1024);+ int best_cdt_weight = 0;+ int cdt_weight;++ while(strcmp(cdt, "/") != 0) {+ if((cdt_weight = is_project_root(cdt)) && cdt_weight > best_cdt_weight) {+ best_cdt_weight = cdt_weight;+ strcpy(best_cdt, cdt);+ }+ cdt = dirname(cdt);+ }++ return best_cdt;+}++char* find_project_root(char* cwd) {+ char* cdt = cwd;++ while(strcmp(cdt, "/") != 0) {+ if(is_project_root(cdt)) return cdt;+ cdt = dirname(cdt);+ }++ return NULL;+}
+ projectroot/libprojectroot.h view
@@ -0,0 +1,16 @@+#include <dirent.h>+#include <glob.h>+#include <libgen.h>+#include <stdio.h>+#include <stdlib.h>+#include <string.h>+#include <unistd.h>++#define N_CANDIDATES 26++int weight_mode_on;+int verbose_on;++int is_project_root(char* cdt);+char* find_project_root_weighted(char* cwd);+char* find_project_root(char* cwd);
+ src/System/Directory/ProjectRoot.hs view
@@ -0,0 +1,45 @@+-- |+-- Module: System.Directory.ProjectRoot+-- Description: Find the root of a project+-- Copyright: (c) Pedro Tacla Yamada, 2015+-- License: MIT+-- Maintainer: tacla.yamada@gmail.com+-- Stability: Stable+-- Portability: POSIX+--+-- Simple way of finding the root of a project given an entry-point.+-- This module provides bindings to the <https://github.com/yamadapc/projectroot projectroot> C library+module System.Directory.ProjectRoot+ where++import Control.Monad ((<=<), (>=>))+import Foreign.C+import System.Directory (getCurrentDirectory)++-- * Haskell Wrappers++-- |+-- Find the project root given an entry point+getProjectRoot :: FilePath -> IO FilePath+getProjectRoot = (find_project_root <=< newCString) >=> peekCString++-- |+-- Find the weighted project root given an entry point+getProjectRootWeighted :: FilePath -> IO FilePath+getProjectRootWeighted = (find_project_root_weighted <=< newCString) >=>+ peekCString++-- |+-- Find the project root of the current directory+getProjectRootCurrent :: IO FilePath+getProjectRootCurrent = getCurrentDirectory >>= getProjectRoot++-- |+-- Find the weighted project root of the current directory+getProjectRootWeightedCurrent :: IO FilePath+getProjectRootWeightedCurrent = getCurrentDirectory >>= getProjectRootWeighted++-- * C functions++foreign import ccall "find_project_root" find_project_root :: CString -> IO CString+foreign import ccall "find_project_root_weighted" find_project_root_weighted :: CString -> IO CString