From 5fd83771641d15c418f747bd343ba6738d3875f7 Mon Sep 17 00:00:00 2001 From: Cameron Katri Date: Sun, 9 May 2021 14:20:58 -0400 Subject: Import macOS userland adv_cmds-176 basic_cmds-55 bootstrap_cmds-116.100.1 developer_cmds-66 diskdev_cmds-667.40.1 doc_cmds-53.60.1 file_cmds-321.40.3 mail_cmds-35 misc_cmds-34 network_cmds-606.40.1 patch_cmds-17 remote_cmds-63 shell_cmds-216.60.1 system_cmds-880.60.2 text_cmds-106 --- shell_cmds/path_helper/path_helper.c | 201 +++++++++++++++++++++++++++++++++++ 1 file changed, 201 insertions(+) create mode 100644 shell_cmds/path_helper/path_helper.c (limited to 'shell_cmds/path_helper/path_helper.c') diff --git a/shell_cmds/path_helper/path_helper.c b/shell_cmds/path_helper/path_helper.c new file mode 100644 index 0000000..2313c99 --- /dev/null +++ b/shell_cmds/path_helper/path_helper.c @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include +#include +#include + +static void usage() { + fprintf(stderr, "usage: path_helper [-c | -s]"); + exit(1); +} + +// Append path segment if it does not exist. Reallocate +// the path buffer as necessary. + +int append_path_segment(char** path, const char* segment) { + if (*path == NULL || segment == NULL) return -1; + + size_t pathlen = strlen(*path); + size_t seglen = strlen(segment); + + if (seglen == 0) return 0; + + // Does the segment already exist in the path? + // (^|:)segment(:|$) + char* match = strstr(*path, segment); + while (match) { + if ((match == *path || match[-1] == ':') && + (match[seglen] == ':' || match[seglen] == 0)) { + return 0; + } + match = strstr(match+1, segment); + } + + // size = pathlen + ':' + segment + '\0' + size_t size = pathlen + seglen + 2; + *path = reallocf(*path, size); + if (*path == NULL) return -1; + + if (pathlen > 0) strlcat(*path, ":", size); + strlcat(*path, segment, size); + return 0; +} + +// Convert fgetln output into a sanitized segment +// escape quotes, dollars, etc. +char* read_segment(const char* line, size_t len) { + int escapes = 0; + size_t i, j; + + for (i = 0; i < len; ++i) { + char c = line[i]; + if (c == '\"' || c == '\'' || c == '$') { + ++escapes; + } + } + + size_t size = len + escapes + 1; + + char* segment = calloc(1, size); + if (segment == NULL) return NULL; + + for (i = 0, j = 0; i < len; ++i, ++j) { + char c = line[i]; + if (c == '\"' || c == '\'' || c == '$') { + segment[j++] = '\\'; + segment[j] = c; + } else if (c == '\n') { + segment[j] = 0; + break; + } else { + segment[j] = line[i]; + } + } + + return segment; +} + +// Construct a path variable, starting with the contents +// of the given environment variable, adding the contents +// of the default file and files in the path directory. + +char* construct_path(char* env_var, char* defaults_path, char* dir_path) { + FTS* fts; + FTSENT* ent; + + char* result = calloc(sizeof(char), 1); + + char* dirpathv[] = { defaults_path, dir_path, NULL }; + fts = fts_open(dirpathv, FTS_PHYSICAL | FTS_XDEV, NULL); + if (!fts) { + perror(dir_path); + return NULL; + } + + while ((ent = fts_read(fts)) != NULL) { + // only interested in regular files, one level deep + if (ent->fts_info != FTS_F) { + if (ent->fts_level >= 1) fts_set(fts, ent, FTS_SKIP); + continue; + } + + FILE* f = fopen(ent->fts_accpath, "r"); + if (f == NULL) { + perror(ent->fts_accpath); + continue; + } + + for (;;) { + size_t len; + char* line = fgetln(f, &len); + if (line == NULL) break; + char* segment = read_segment(line, len); + + append_path_segment(&result, segment); + } + + fclose(f); + } + fts_close(fts); + + // merge in any existing custom PATH elemenets + char* str = getenv(env_var); + if (str) str = strdup(str); + while (str) { + char* sep = strchr(str, ':'); + if (sep) *sep = 0; + + append_path_segment(&result, str); + if (sep) { + str = sep + 1; + } else { + str = NULL; + } + } + + return result; +} + +enum { + STYLE_CSH, + STYLE_SH +}; + +int main(int argc, char* argv[]) { + int style = STYLE_SH; + + if (argc > 2) usage(); + + // default to csh style, if $SHELL ends with "csh". + char* shell = getenv("SHELL"); + if (shell) { + char* str = strstr(shell, "csh"); + if (str) style = STYLE_CSH; + } + + if (argc == 2 && strcmp(argv[1], "-c") == 0) style = STYLE_CSH; + if (argc == 2 && strcmp(argv[1], "-s") == 0) style = STYLE_SH; + + char* path = construct_path("PATH", "/etc/paths", "/etc/paths.d"); + char* manpath = NULL; + + // only adjust manpath if already set + int do_manpath = (getenv("MANPATH") != NULL); + if (do_manpath) { + manpath = construct_path("MANPATH", "/etc/manpaths", "/etc/manpaths.d"); + } + + if (style == STYLE_CSH) { + printf("setenv PATH \"%s\";\n", path); + if (do_manpath) printf("setenv MANPATH \"%s\";\n", manpath); + } else { + printf("PATH=\"%s\"; export PATH;\n", path); + if (do_manpath) printf("MANPATH=\"%s\"; export MANPATH;\n", manpath); + } + + return 0; +} -- cgit v1.2.3-56-ge451