]> git.saurik.com Git - apple/shell_cmds.git/blame - path_helper/path_helper.c
shell_cmds-149.tar.gz
[apple/shell_cmds.git] / path_helper / path_helper.c
CommitLineData
ddb4a88b
A
1/*
2 * Copyright (c) 2008 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24#include <fts.h>
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
28#include <unistd.h>
29#include <limits.h>
30
31static void usage() {
32 fprintf(stderr, "usage: path_helper [-c | -s]");
33 exit(1);
34}
35
36// Append path segment if it does not exist. Reallocate
37// the path buffer as necessary.
38
39int append_path_segment(char** path, const char* segment) {
40 if (*path == NULL || segment == NULL) return -1;
41
42 size_t pathlen = strlen(*path);
43 size_t seglen = strlen(segment);
44
45 if (seglen == 0) return 0;
46
47 // Does the segment already exist in the path?
48 // (^|:)segment(:|$)
49 char* match = strstr(*path, segment);
50 while (match) {
51 if ((match == *path || match[-1] == ':') &&
52 (match[seglen] == ':' || match[seglen] == 0)) {
53 return 0;
54 }
55 match = strstr(match+1, segment);
56 }
57
58 // size = pathlen + ':' + segment + '\0'
59 size_t size = pathlen + seglen + 2;
60 *path = reallocf(*path, size);
61 if (*path == NULL) return -1;
62
63 if (pathlen > 0) strlcat(*path, ":", size);
64 strlcat(*path, segment, size);
65 return 0;
66}
67
68// Convert fgetln output into a sanitized segment
69// escape quotes, dollars, etc.
70char* read_segment(const char* line, size_t len) {
71 int escapes = 0;
72 size_t i, j;
73
74 for (i = 0; i < len; ++i) {
75 char c = line[i];
76 if (c == '\"' || c == '\'' || c == '$') {
77 ++escapes;
78 }
79 }
80
81 size_t size = len + escapes + 1;
82
83 char* segment = malloc(size);
84 if (segment == NULL) return NULL;
85
86 for (i = 0, j = 0; i < len; ++i, ++j) {
87 char c = line[i];
88 if (c == '\"' || c == '\'' || c == '$') {
89 segment[j++] = '\\';
90 segment[j] = c;
91 } else if (c == '\n') {
92 segment[j] = 0;
93 break;
94 } else {
95 segment[j] = line[i];
96 }
97 }
98
99 return segment;
100}
101
102// Construct a path variable, starting with the contents
103// of the given environment variable, adding the contents
104// of the default file and files in the path directory.
105
106char* construct_path(char* env_var, char* defaults_path, char* dir_path) {
107 FTS* fts;
108 FTSENT* ent;
109
110 char* result = calloc(sizeof(char), 1);
111
112 char* dirpathv[] = { defaults_path, dir_path, NULL };
113 fts = fts_open(dirpathv, FTS_PHYSICAL | FTS_XDEV, NULL);
114 if (!fts) {
115 perror(dir_path);
116 return NULL;
117 }
118
119 while ((ent = fts_read(fts)) != NULL) {
120 // only interested in regular files, one level deep
121 if (ent->fts_info != FTS_F) {
122 if (ent->fts_level >= 1) fts_set(fts, ent, FTS_SKIP);
123 continue;
124 }
125
126 FILE* f = fopen(ent->fts_accpath, "r");
127 if (f == NULL) {
128 perror(ent->fts_accpath);
129 continue;
130 }
131
132 for (;;) {
133 size_t len;
134 char* line = fgetln(f, &len);
135 if (line == NULL) break;
136 char* segment = read_segment(line, len);
137
138 append_path_segment(&result, segment);
139 }
140
141 fclose(f);
142 }
143 fts_close(fts);
144
145 // merge in any existing custom PATH elemenets
146 char* str = getenv(env_var);
147 if (str) str = strdup(str);
148 while (str) {
149 char* sep = strchr(str, ':');
150 if (sep) *sep = 0;
151
152 append_path_segment(&result, str);
153 if (sep) {
154 str = sep + 1;
155 } else {
156 str = NULL;
157 }
158 }
159
160 return result;
161}
162
163enum {
164 STYLE_CSH,
165 STYLE_SH
166};
167
168int main(int argc, char* argv[]) {
169 int style = STYLE_SH;
170
171 if (argc > 2) usage();
172
173 // default to csh style, if $SHELL ends with "csh".
174 char* shell = getenv("SHELL");
175 if (shell) {
176 char* str = strstr(shell, "csh");
177 if (str) style = STYLE_CSH;
178 }
179
180 if (argc == 2 && strcmp(argv[1], "-c") == 0) style = STYLE_CSH;
181 if (argc == 2 && strcmp(argv[1], "-s") == 0) style = STYLE_SH;
182
183 char* path = construct_path("PATH", "/etc/paths", "/etc/paths.d");
184 char* manpath = NULL;
185
186 // only adjust manpath if already set
187 int do_manpath = (getenv("MANPATH") != NULL);
188 if (do_manpath) {
189 manpath = construct_path("MANPATH", "/etc/manpaths", "/etc/manpaths.d");
190 }
191
192 if (style == STYLE_CSH) {
193 printf("setenv PATH \"%s\";\n", path);
194 if (do_manpath) printf("setenv MANPATH \"%s\";\n", manpath);
195 } else {
196 printf("PATH=\"%s\"; export PATH;\n", path);
197 if (do_manpath) printf("MANPATH=\"%s\"; export MANPATH;\n", manpath);
198 }
199
200 return 0;
201}