1 static char *sccsid = "@(#)dir.c 4.1 10/09/80"; 2 3 #include "sh.h" 4 #include "sh.dir.h" 5 6 /* 7 * C Shell - directory management 8 */ 9 10 struct directory *dfind(); 11 char *dfollow(); 12 struct directory dhead; /* "head" of loop */ 13 int printd; /* force name to be printed */ 14 static char *fakev[] = { "dirs", NOSTR }; 15 16 /* 17 * dinit - initialize current working directory 18 */ 19 dinit(hp) 20 char *hp; 21 { 22 register char *cp; 23 register struct directory *dp; 24 char path[BUFSIZ]; 25 26 if (loginsh && hp) 27 cp = hp; 28 else 29 cp = getwd(path); 30 dp = (struct directory *)calloc(sizeof (struct directory), 1); 31 dp->di_name = savestr(cp); 32 dp->di_count = 0; 33 dhead.di_next = dhead.di_prev = dp; 34 dp->di_next = dp->di_prev = &dhead; 35 printd = 0; 36 dnewcwd(dp); 37 } 38 39 /* 40 * dodirs - list all directories in directory loop 41 */ 42 dodirs(v) 43 char **v; 44 { 45 register struct directory *dp; 46 bool lflag; 47 char *hp = value("home"); 48 49 if (*hp == '\0') 50 hp = NOSTR; 51 if (*++v != NOSTR) 52 if (eq(*v, "-l") && *++v == NOSTR) 53 lflag = 1; 54 else 55 error("Usage: dirs [ -l ]"); 56 else 57 lflag = 0; 58 dp = dcwd; 59 do { 60 if (dp == &dhead) 61 continue; 62 if (!lflag && hp != NOSTR) { 63 dtildepr(hp, dp->di_name); 64 } else 65 printf("%s", dp->di_name); 66 printf(" "); 67 } while ((dp = dp->di_prev) != dcwd); 68 printf("\n"); 69 } 70 71 dtildepr(home, dir) 72 register char *home, *dir; 73 { 74 75 if (!eq(home, "/") && prefix(home, dir)) 76 printf("~%s", dir + strlen(home)); 77 else 78 printf("%s", dir); 79 } 80 81 /* 82 * dochngd - implement chdir command. 83 */ 84 dochngd(v) 85 char **v; 86 { 87 register char *cp; 88 register struct directory *dp; 89 90 printd = 0; 91 if (*++v == NOSTR) { 92 if ((cp = value("home")) == NOSTR || *cp == 0) 93 bferr("No home directory"); 94 if (chdir(cp) < 0) 95 bferr("Can't change to home directory"); 96 cp = savestr(cp); 97 } else if ((dp = dfind(*v)) != 0) { 98 printd = 1; 99 if (chdir(dp->di_name) < 0) 100 Perror(dp->di_name); 101 dcwd->di_prev->di_next = dcwd->di_next; 102 dcwd->di_next->di_prev = dcwd->di_prev; 103 goto flushcwd; 104 } else 105 cp = dfollow(*v); 106 dp = (struct directory *)calloc(sizeof (struct directory), 1); 107 dp->di_name = cp; 108 dp->di_count = 0; 109 dp->di_next = dcwd->di_next; 110 dp->di_prev = dcwd->di_prev; 111 dp->di_prev->di_next = dp; 112 dp->di_next->di_prev = dp; 113 flushcwd: 114 dfree(dcwd); 115 dnewcwd(dp); 116 } 117 118 /* 119 * dfollow - change to arg directory; fall back on cdpath if not valid 120 */ 121 char * 122 dfollow(cp) 123 register char *cp; 124 { 125 register char **cdp; 126 struct varent *c; 127 128 cp = globone(cp); 129 if (chdir(cp) == 0) 130 goto gotcha; 131 if (cp[0] != '/' && !prefix("./", cp) && !prefix("../", cp) 132 && (c = adrof("cdpath"))) { 133 for (cdp = c->vec; *cdp; cdp++) { 134 char buf[BUFSIZ]; 135 136 strcpy(buf, *cdp); 137 strcat(buf, "/"); 138 strcat(buf, cp); 139 if (chdir(buf) >= 0) { 140 printd = 1; 141 xfree(cp); 142 cp = savestr(buf); 143 goto gotcha; 144 } 145 } 146 } 147 if (adrof(cp)) { 148 char *dp = value(cp); 149 150 if (dp[0] == '/' || dp[0] == '.') 151 if (chdir(dp) >= 0) { 152 xfree(cp); 153 cp = savestr(dp); 154 printd = 1; 155 goto gotcha; 156 } 157 } 158 xfree(cp); 159 Perror(cp); 160 161 gotcha: 162 if (*cp != '/') { 163 char *dp = calloc(strlen(cp) + strlen(dcwd->di_name) + 2, 1); 164 strcpy(dp, dcwd->di_name); 165 strcat(dp, "/"); 166 strcat(dp, cp); 167 xfree(cp); 168 cp = dp; 169 } 170 dcanon(cp); 171 return (cp); 172 } 173 174 /* 175 * dopushd - push new directory onto directory stack. 176 * with no arguments exchange top and second. 177 * with numeric argument (+n) bring it to top. 178 */ 179 dopushd(v) 180 char **v; 181 { 182 register struct directory *dp; 183 184 printd = 1; 185 if (*++v == NOSTR) { 186 if ((dp = dcwd->di_prev) == &dhead) 187 dp = dhead.di_prev; 188 if (dp == dcwd) 189 bferr("No other directory"); 190 if (chdir(dp->di_name) < 0) 191 Perror(dp->di_name); 192 dp->di_prev->di_next = dp->di_next; 193 dp->di_next->di_prev = dp->di_prev; 194 dp->di_next = dcwd->di_next; 195 dp->di_prev = dcwd; 196 dcwd->di_next->di_prev = dp; 197 dcwd->di_next = dp; 198 } else if (dp = dfind(*v)) { 199 if (chdir(dp->di_name) < 0) 200 Perror(dp->di_name); 201 } else { 202 register char *cp; 203 204 cp = dfollow(*v); 205 dp = (struct directory *)calloc(sizeof (struct directory), 1); 206 dp->di_name = cp; 207 dp->di_count = 0; 208 dp->di_prev = dcwd; 209 dp->di_next = dcwd->di_next; 210 dcwd->di_next = dp; 211 dp->di_next->di_prev = dp; 212 } 213 dnewcwd(dp); 214 } 215 216 /* 217 * dfind - find a directory if specified by numeric (+n) argument 218 */ 219 struct directory * 220 dfind(cp) 221 register char *cp; 222 { 223 register struct directory *dp; 224 register int i; 225 register char *ep; 226 227 if (*cp++ != '+') 228 return (0); 229 for (ep = cp; digit(*ep); ep++) 230 continue; 231 if (*ep) 232 return (0); 233 i = getn(cp); 234 if (i <= 0) 235 return (0); 236 for (dp = dcwd; i != 0; i--) { 237 if ((dp = dp->di_prev) == &dhead) 238 dp = dp->di_prev; 239 if (dp == dcwd) 240 bferr("Directory stack not that deep"); 241 } 242 return (dp); 243 } 244 245 /* 246 * dopopd - pop a directory out of the directory stack 247 * with a numeric argument just discard it. 248 */ 249 dopopd(v) 250 char **v; 251 { 252 register struct directory *dp, *p; 253 254 printd = 1; 255 if (*++v == NOSTR) 256 dp = dcwd; 257 else if ((dp = dfind(*v)) == 0) 258 bferr("Bad directory"); 259 if (dp->di_prev == &dhead && dp->di_next == &dhead) 260 bferr("Directory stack empty"); 261 if (dp == dcwd) { 262 if ((p = dp->di_prev) == &dhead) 263 p = dhead.di_prev; 264 if (chdir(p->di_name) < 0) 265 Perror(p->di_name); 266 } 267 dp->di_prev->di_next = dp->di_next; 268 dp->di_next->di_prev = dp->di_prev; 269 if (dp == dcwd) 270 dnewcwd(p); 271 else 272 dodirs(fakev); 273 dfree(dp); 274 } 275 276 /* 277 * dfree - free the directory (or keep it if it still has ref count) 278 */ 279 dfree(dp) 280 register struct directory *dp; 281 { 282 283 if (dp->di_count != 0) 284 dp->di_next = dp->di_prev = 0; 285 else 286 xfree(dp->di_name), xfree((char *)dp); 287 } 288 289 /* 290 * dcanon - canonicalize the pathname, removing excess ./ and ../ etc. 291 * we are of course assuming that the file system is standardly 292 * constructed (always have ..'s, directories have links) 293 */ 294 dcanon(cp) 295 char *cp; 296 { 297 register char *p, *sp; 298 register bool slash; 299 300 if (*cp != '/') 301 abort(); 302 for (p = cp; *p; ) { /* for each component */ 303 sp = p; /* save slash address */ 304 while(*++p == '/') /* flush extra slashes */ 305 ; 306 if (p != ++sp) 307 strcpy(sp, p); 308 p = sp; /* save start of component */ 309 slash = 0; 310 while(*++p) /* find next slash or end of path */ 311 if (*p == '/') { 312 slash = 1; 313 *p = 0; 314 break; 315 } 316 if (*sp == '\0') /* if component is null */ 317 if (--sp == cp) /* if path is one char (i.e. /) */ 318 break; 319 else 320 *sp = '\0'; 321 else if (eq(".", sp)) { 322 if (slash) { 323 strcpy(sp, ++p); 324 p = --sp; 325 } else if (--sp != cp) 326 *sp = '\0'; 327 } else if (eq("..", sp)) { 328 if (--sp != cp) 329 while (*--sp != '/') 330 ; 331 if (slash) { 332 strcpy(++sp, ++p); 333 p = --sp; 334 } else if (cp == sp) 335 *++sp = '\0'; 336 else 337 *sp = '\0'; 338 } else if (slash) 339 *p = '/'; 340 } 341 } 342 343 /* 344 * dnewcwd - make a new directory in the loop the current one 345 */ 346 dnewcwd(dp) 347 register struct directory *dp; 348 { 349 350 dcwd = dp; 351 set("cwd", savestr(dcwd->di_name)); 352 if (printd) 353 dodirs(fakev); 354 } 355