diff options
-rw-r--r-- | dircmp.c | 181 |
1 files changed, 181 insertions, 0 deletions
diff --git a/dircmp.c b/dircmp.c new file mode 100644 index 0000000..3bca7a3 --- /dev/null +++ b/dircmp.c @@ -0,0 +1,181 @@ +/* + * UNG's Not GNU + * + * Copyright (c) 2022, Jakob Kaivo <jkk@ung.org> + * + * 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. + */ + +#define _XOPEN_SOURCE 700 +#include <fcntl.h> +#include <errno.h> +#include <limits.h> +#include <locale.h> +#include <ftw.h> +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <unistd.h> + +#ifndef OPEN_MAX +#ifdef _XOPEN_MAX +#define OPEN_MAX _XOPEN_MAX +#else +#define OPEN_MAX _POSIX_OPEN_MAX +#endif +#endif + +static int directory_fd[2]; +static char *directory_path[2]; +static int return_value = 0; +static int current_fd = 0; +static size_t directory_base = 0; +static int suppress = 0; +static int diff_output = 0; + +static int compare(char *d1, char *d2, const char *path) +{ + char p1[PATH_MAX]; + char p2[PATH_MAX]; + + snprintf(p1, sizeof(p1), "%s/%s", d1, path); + snprintf(p2, sizeof(p2), "%s/%s", d2, path); + + if (diff_output) { + /* run diff d1/path d2/path */ + return 0; + } + + FILE *f1 = fopen(p1, "r"); + if (!f1) { + fprintf(stderr, "dircmp: %s: %s\n", p1, strerror(errno)); + return 1; + } + + FILE *f2 = fopen(p2, "r"); + if (!f2) { + fprintf(stderr, "dircmp: %s: %s\n", p2, strerror(errno)); + fclose(f1); + return 1; + } + + for (;;) { + int c1 = fgetc(f1); + int c2 = fgetc(f2); + if (c1 != c2) { + printf("files differ: %s %s\n", p1, p2); + break; + } + if (c1 == EOF) { + if (!suppress) { + printf("files match: %s %s\n", p1, p2); + } + break; + } + } + + fclose(f1); + fclose(f2); + + return 0; +} + +static int dircmp(const char *path, const struct stat *st, int type, struct FTW *ftw) +{ + (void)ftw; (void)type; (void)st; + + //int fd1 = directory_fd[current_fd]; + int fd2 = directory_fd[current_fd == 0 ? 1 : 0]; + char *d1 = directory_path[current_fd]; + char *d2 = directory_path[current_fd == 0 ? 1 : 0]; + + path += directory_base; + + struct stat st2; + if (fstatat(fd2, path, &st2, 0) != 0) { + if (errno == ENOENT) { + printf("only in %s: %s\n", d1, path); + return 0; + } + fprintf(stderr, "dircmp: %s/%s: %s\n", d2, path, strerror(errno)); + return_value = 1; + return 0; + } + + /* common files have already been compared */ + if (current_fd == 1) { + return 0; + } + + return_value |= compare(d1, d2, path); + return 0; +} + +int main(int argc, char *argv[]) +{ + setlocale(LC_ALL, ""); + + int c; + while ((c = getopt(argc, argv, "ds")) != EOF) { + switch (c) { + case 'd': /** compare files as with `diff` **/ + diff_output = 1; + break; + + case 's': /** suppress identical files output **/ + suppress = 1; + break; + + default: + return 1; + } + } + + if (optind != argc - 2) { + fprintf(stderr, "cmp: missing operand\n"); + return 1; + } + + directory_path[0] = argv[optind]; + directory_path[1] = argv[optind]; + + directory_fd[0] = open(directory_path[0], O_RDONLY | O_DIRECTORY); + if (directory_fd[0] == -1) { + fprintf(stderr, "dircmp: %s: %s\n", directory_path[0], strerror(errno)); + return 1; + } + + directory_fd[1] = open(directory_path[0], O_RDONLY | O_DIRECTORY); + if (directory_fd[1] == -1) { + fprintf(stderr, "dircmp: %s: %s\n", directory_path[1], strerror(errno)); + return 1; + } + + + directory_base = strlen(directory_path[0]) + 1; + nftw(directory_path[0], dircmp, OPEN_MAX, 0); + + current_fd = 1; + directory_base = strlen(directory_path[1]) + 1; + nftw(directory_path[1], dircmp, OPEN_MAX, 0); + + return return_value; +} |