Fix up d_type handling - we need to include <dirent.h> before
[git.git] / show-files.c
index 0ecb2bd..c63c7aa 100644 (file)
@@ -6,7 +6,7 @@
  * Copyright (C) Linus Torvalds, 2005
  */
 #include <dirent.h>
-#include <sys/param.h>
+#include <fnmatch.h>
 
 #include "cache.h"
 
@@ -15,8 +15,73 @@ static int show_cached = 0;
 static int show_others = 0;
 static int show_ignored = 0;
 static int show_stage = 0;
+static int show_unmerged = 0;
 static int line_terminator = '\n';
 
+static int nr_excludes;
+static const char **excludes;
+static int excludes_alloc;
+
+static void add_exclude(const char *string)
+{
+       if (nr_excludes == excludes_alloc) {
+               excludes_alloc = alloc_nr(excludes_alloc);
+               excludes = realloc(excludes, excludes_alloc*sizeof(char *));
+       }
+       excludes[nr_excludes++] = string;
+}
+
+static void add_excludes_from_file(const char *fname)
+{
+       int fd, i;
+       long size;
+       char *buf, *entry;
+
+       fd = open(fname, O_RDONLY);
+       if (fd < 0)
+               goto err;
+       size = lseek(fd, 0, SEEK_END);
+       if (size < 0)
+               goto err;
+       lseek(fd, 0, SEEK_SET);
+       if (size == 0) {
+               close(fd);
+               return;
+       }
+       buf = xmalloc(size);
+       if (read(fd, buf, size) != size)
+               goto err;
+       close(fd);
+
+       entry = buf;
+       for (i = 0; i < size; i++) {
+               if (buf[i] == '\n') {
+                       if (entry != buf + i) {
+                               buf[i] = 0;
+                               add_exclude(entry);
+                       }
+                       entry = buf + i + 1;
+               }
+       }
+       return;
+
+err:   perror(fname);
+       exit(1);
+}
+
+static int excluded(const char *pathname)
+{
+       int i;
+       if (nr_excludes) {
+               const char *basename = strrchr(pathname, '/');
+               basename = (basename) ? basename+1 : pathname;
+               for (i = 0; i < nr_excludes; i++)
+                       if (fnmatch(excludes[i], basename, 0) == 0)
+                               return 1;
+       }
+       return 0;
+}
+
 static const char **dir;
 static int nr_dir;
 static int dir_alloc;
@@ -30,9 +95,9 @@ static void add_name(const char *pathname, int len)
 
        if (nr_dir == dir_alloc) {
                dir_alloc = alloc_nr(dir_alloc);
-               dir = realloc(dir, dir_alloc*sizeof(char *));
+               dir = xrealloc(dir, dir_alloc*sizeof(char *));
        }
-       name = malloc(len + 1);
+       name = xmalloc(len + 1);
        memcpy(name, pathname, len + 1);
        dir[nr_dir++] = name;
 }
@@ -59,10 +124,12 @@ static void read_directory(const char *path, const char *base, int baselen)
 
                        if (de->d_name[0] == '.')
                                continue;
+                       if (excluded(de->d_name) != show_ignored)
+                               continue;
                        len = strlen(de->d_name);
                        memcpy(fullname + baselen, de->d_name, len+1);
 
-                       switch (de->d_type) {
+                       switch (DTYPE(de)) {
                        struct stat st;
                        default:
                                continue;
@@ -101,17 +168,19 @@ static void show_files(void)
        int i;
 
        /* For cached/deleted files we don't need to even do the readdir */
-       if (show_others | show_ignored) {
+       if (show_others) {
                read_directory(".", "", 0);
                qsort(dir, nr_dir, sizeof(char *), cmp_name);
-       }
-       if (show_others) {
                for (i = 0; i < nr_dir; i++)
                        printf("%s%c", dir[i], line_terminator);
        }
        if (show_cached | show_stage) {
                for (i = 0; i < active_nr; i++) {
                        struct cache_entry *ce = active_cache[i];
+                       if (excluded(ce->name) != show_ignored)
+                               continue;
+                       if (show_unmerged && !ce_stage(ce))
+                               continue;
                        if (!show_stage)
                                printf("%s%c", ce->name, line_terminator);
                        else
@@ -128,16 +197,19 @@ static void show_files(void)
                for (i = 0; i < active_nr; i++) {
                        struct cache_entry *ce = active_cache[i];
                        struct stat st;
+                       if (excluded(ce->name) != show_ignored)
+                               continue;
                        if (!stat(ce->name, &st))
                                continue;
                        printf("%s%c", ce->name, line_terminator);
                }
        }
-       if (show_ignored) {
-               /* We don't have any "ignore" list yet */
-       }
 }
 
+static const char *show_files_usage =
+       "show-files [-z] (--[cached|deleted|others|stage|unmerged])* "
+       "[ --ignored [--exclude=<pattern>] [--exclude-from=<file>) ]";
+
 int main(int argc, char **argv)
 {
        int i;
@@ -147,35 +219,39 @@ int main(int argc, char **argv)
 
                if (!strcmp(arg, "-z")) {
                        line_terminator = 0;
-                       continue;
-               }
-
-               if (!strcmp(arg, "--cached")) {
+               } else if (!strcmp(arg, "-c") || !strcmp(arg, "--cached")) {
                        show_cached = 1;
-                       continue;
-               }
-               if (!strcmp(arg, "--deleted")) {
+               } else if (!strcmp(arg, "-d") || !strcmp(arg, "--deleted")) {
                        show_deleted = 1;
-                       continue;
-               }
-               if (!strcmp(arg, "--others")) {
+               } else if (!strcmp(arg, "-o") || !strcmp(arg, "--others")) {
                        show_others = 1;
-                       continue;
-               }
-               if (!strcmp(arg, "--ignored")) {
+               } else if (!strcmp(arg, "-i") || !strcmp(arg, "--ignored")) {
                        show_ignored = 1;
-                       continue;
-               }
-               if (!strcmp(arg, "--stage")) {
+               } else if (!strcmp(arg, "-s") || !strcmp(arg, "--stage")) {
                        show_stage = 1;
-                       continue;
-               }
+               } else if (!strcmp(arg, "-u") || !strcmp(arg, "--unmerged")) {
+                       // There's no point in showing unmerged unless you also show the stage information
+                       show_stage = 1;
+                       show_unmerged = 1;
+               } else if (!strcmp(arg, "-x") && i+1 < argc) {
+                       add_exclude(argv[++i]);
+               } else if (!strncmp(arg, "--exclude=", 10)) {
+                       add_exclude(arg+10);
+               } else if (!strcmp(arg, "-X") && i+1 < argc) {
+                       add_excludes_from_file(argv[++i]);
+               } else if (!strncmp(arg, "--exclude-from=", 15)) {
+                       add_excludes_from_file(arg+15);
+               } else
+                       usage(show_files_usage);
+       }
 
-               usage("show-files [-z] (--[cached|deleted|others|ignored|stage])*");
+       if (show_ignored && !nr_excludes) {
+               fprintf(stderr, "%s: --ignored needs some exclude pattern\n", argv[0]);
+               exit(1);
        }
 
        /* With no flags, we default to showing the cached files */
-       if (!(show_stage | show_deleted | show_others | show_ignored))
+       if (!(show_stage | show_deleted | show_others | show_unmerged))
                show_cached = 1;
 
        read_cache();