GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: bin/csh/dir.c Lines: 0 376 0.0 %
Date: 2016-12-06 Branches: 0 340 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: dir.c,v 1.21 2015/12/26 13:48:38 mestre Exp $	*/
2
/*	$NetBSD: dir.c,v 1.9 1995/03/21 09:02:42 cgd Exp $	*/
3
4
/*-
5
 * Copyright (c) 1980, 1991, 1993
6
 *	The Regents of the University of California.  All rights reserved.
7
 *
8
 * Redistribution and use in source and binary forms, with or without
9
 * modification, are permitted provided that the following conditions
10
 * are met:
11
 * 1. Redistributions of source code must retain the above copyright
12
 *    notice, this list of conditions and the following disclaimer.
13
 * 2. Redistributions in binary form must reproduce the above copyright
14
 *    notice, this list of conditions and the following disclaimer in the
15
 *    documentation and/or other materials provided with the distribution.
16
 * 3. Neither the name of the University nor the names of its contributors
17
 *    may be used to endorse or promote products derived from this software
18
 *    without specific prior written permission.
19
 *
20
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30
 * SUCH DAMAGE.
31
 */
32
33
#include <sys/stat.h>
34
#include <errno.h>
35
#include <stdlib.h>
36
#include <string.h>
37
#include <unistd.h>
38
#include <limits.h>
39
#include <stdarg.h>
40
41
#include "csh.h"
42
#include "dir.h"
43
#include "extern.h"
44
45
/* Directory management. */
46
47
static struct directory
48
		*dfind(Char *);
49
static Char	*dfollow(Char *);
50
static void	 printdirs(void);
51
static Char	*dgoto(Char *);
52
static void	 dnewcwd(struct directory *);
53
static void	 dset(Char *);
54
55
struct directory dhead;		/* "head" of loop */
56
int     printd;			/* force name to be printed */
57
58
static int dirflag = 0;
59
60
/*
61
 * dinit - initialize current working directory
62
 */
63
void
64
dinit(Char *hp)
65
{
66
    char *tcp;
67
    Char *cp;
68
    struct directory *dp;
69
    char    path[PATH_MAX];
70
    static const char emsg[] = "csh: Trying to start from \"%s\"\n";
71
72
    /* Don't believe the login shell home, because it may be a symlink */
73
    tcp = getcwd(path, PATH_MAX);
74
    if (tcp == NULL || *tcp == '\0') {
75
	(void) fprintf(csherr, "csh: %s\n", strerror(errno));
76
	if (hp && *hp) {
77
	    tcp = short2str(hp);
78
	    if (chdir(tcp) == -1)
79
		cp = NULL;
80
	    else
81
		cp = hp;
82
	    (void) fprintf(csherr, emsg, vis_str(hp));
83
	}
84
	else
85
	    cp = NULL;
86
	if (cp == NULL) {
87
	    (void) fprintf(csherr, emsg, "/");
88
	    if (chdir("/") == -1)
89
		/* I am not even try to print an error message! */
90
		xexit(1);
91
	    cp = SAVE("/");
92
	}
93
    }
94
    else {
95
	struct stat swd, shp;
96
97
	/*
98
	 * See if $HOME is the working directory we got and use that
99
	 */
100
	if (hp && *hp &&
101
	    stat(tcp, &swd) != -1 && stat(short2str(hp), &shp) != -1 &&
102
	    swd.st_dev == shp.st_dev && swd.st_ino == shp.st_ino)
103
	    cp = hp;
104
	else {
105
	    char   *cwd;
106
107
	    /*
108
	     * use PWD if we have it (for subshells)
109
	     */
110
	    if ((cwd = getenv("PWD")) != NULL) {
111
		if (stat(cwd, &shp) != -1 && swd.st_dev == shp.st_dev &&
112
		    swd.st_ino == shp.st_ino)
113
		    tcp = cwd;
114
	    }
115
	    cp = dcanon(SAVE(tcp), STRNULL);
116
	}
117
    }
118
119
    dp = xcalloc(1, sizeof(struct directory));
120
    dp->di_name = Strsave(cp);
121
    dp->di_count = 0;
122
    dhead.di_next = dhead.di_prev = dp;
123
    dp->di_next = dp->di_prev = &dhead;
124
    printd = 0;
125
    dnewcwd(dp);
126
}
127
128
static void
129
dset(Char *dp)
130
{
131
    /*
132
     * Don't call set() directly cause if the directory contains ` or
133
     * other junk characters glob will fail.
134
     */
135
    Char **vec = xreallocarray(NULL, 2, sizeof(Char **));
136
137
    vec[0] = Strsave(dp);
138
    vec[1] = 0;
139
    setq(STRcwd, vec, &shvhed);
140
    Setenv(STRPWD, dp);
141
}
142
143
#define DIR_LONG 1
144
#define DIR_VERT 2
145
#define DIR_LINE 4
146
147
static void
148
skipargs(Char ***v, char *str)
149
{
150
    Char  **n = *v, *s;
151
152
    dirflag = 0;
153
    for (n++; *n != NULL && (*n)[0] == '-'; n++)
154
	for (s = &((*n)[1]); *s; s++)
155
	    switch (*s) {
156
	    case 'l':
157
		dirflag |= DIR_LONG;
158
		break;
159
	    case 'v':
160
		dirflag |= DIR_VERT;
161
		break;
162
	    case 'n':
163
		dirflag |= DIR_LINE;
164
		break;
165
	    default:
166
		stderror(ERR_DIRUS, vis_str(**v), str);
167
		break;
168
	    }
169
    *v = n;
170
}
171
172
/*
173
 * dodirs - list all directories in directory loop
174
 */
175
void
176
/*ARGSUSED*/
177
dodirs(Char **v, struct command *t)
178
{
179
    skipargs(&v, "");
180
181
    if (*v != NULL)
182
	stderror(ERR_DIRUS, "dirs", "");
183
    printdirs();
184
}
185
186
static void
187
printdirs(void)
188
{
189
    struct directory *dp;
190
    Char   *s, *hp = value(STRhome);
191
    int     idx, len, cur;
192
193
    if (*hp == '\0')
194
	hp = NULL;
195
    dp = dcwd;
196
    idx = 0;
197
    cur = 0;
198
    do {
199
	if (dp == &dhead)
200
	    continue;
201
	if (dirflag & DIR_VERT) {
202
	    (void) fprintf(cshout, "%d\t", idx++);
203
	    cur = 0;
204
	}
205
	if (!(dirflag & DIR_LONG) && hp != NULL && !eq(hp, STRslash) &&
206
	    (len = Strlen(hp), Strncmp(hp, dp->di_name, len) == 0) &&
207
	    (dp->di_name[len] == '\0' || dp->di_name[len] == '/'))
208
	    len = Strlen(s = (dp->di_name + len)) + 2;
209
	else
210
	    len = Strlen(s = dp->di_name) + 1;
211
212
	cur += len;
213
	if ((dirflag & DIR_LINE) && cur >= 80 - 1 && len < 80) {
214
	    (void) fprintf(cshout, "\n");
215
	    cur = len;
216
	}
217
	(void) fprintf(cshout, s != dp->di_name ? "~%s%c" : "%s%c",
218
		vis_str(s), (dirflag & DIR_VERT) ? '\n' : ' ');
219
    } while ((dp = dp->di_prev) != dcwd);
220
    if (!(dirflag & DIR_VERT))
221
	(void) fprintf(cshout, "\n");
222
}
223
224
void
225
dtildepr(Char *home, Char *dir)
226
{
227
228
    if (!eq(home, STRslash) && prefix(home, dir))
229
	(void) fprintf(cshout, "~%s", vis_str(dir + Strlen(home)));
230
    else
231
	(void) fprintf(cshout, "%s", vis_str(dir));
232
}
233
234
void
235
dtilde(void)
236
{
237
    struct directory *d = dcwd;
238
239
    do {
240
	if (d == &dhead)
241
	    continue;
242
	d->di_name = dcanon(d->di_name, STRNULL);
243
    } while ((d = d->di_prev) != dcwd);
244
245
    dset(dcwd->di_name);
246
}
247
248
249
/* dnormalize():
250
 *	If the name starts with . or .. then we might need to normalize
251
 *	it depending on the symbolic link flags
252
 */
253
Char   *
254
dnormalize(Char *cp)
255
{
256
257
#define UC (unsigned char)
258
#define ISDOT(c) (UC(c)[0] == '.' && ((UC(c)[1] == '\0') || (UC(c)[1] == '/')))
259
#define ISDOTDOT(c) (UC(c)[0] == '.' && ISDOT(&((c)[1])))
260
261
    if ((unsigned char) cp[0] == '/')
262
	return (Strsave(cp));
263
264
    if (adrof(STRignore_symlinks)) {
265
	int     dotdot = 0;
266
	Char   *dp, *cwd;
267
	size_t	len;
268
269
	len = (size_t) (Strlen(dcwd->di_name) + 3);
270
	cwd = xreallocarray(NULL, len, sizeof(Char));
271
	(void) Strlcpy(cwd, dcwd->di_name, len);
272
273
	/*
274
	 * Ignore . and count ..'s
275
	 */
276
	while (*cp) {
277
	    if (ISDOT(cp)) {
278
		if (*++cp)
279
		    cp++;
280
	    }
281
	    else if (ISDOTDOT(cp)) {
282
		dotdot++;
283
		cp += 2;
284
		if (*cp)
285
		    cp++;
286
	    }
287
	    else
288
		break;
289
	}
290
	while (dotdot > 0)
291
	    if ((dp = Strrchr(cwd, '/'))) {
292
		*dp = '\0';
293
		dotdot--;
294
	    }
295
	    else
296
		break;
297
298
	if (*cp) {
299
	    cwd[dotdot = Strlen(cwd)] = '/';
300
	    cwd[dotdot + 1] = '\0';
301
	    dp = Strspl(cwd, cp);
302
	    free(cwd);
303
	    return dp;
304
	}
305
	else {
306
	    if (!*cwd) {
307
		cwd[0] = '/';
308
		cwd[1] = '\0';
309
	    }
310
	    return cwd;
311
	}
312
    }
313
    return Strsave(cp);
314
}
315
316
/*
317
 * dochngd - implement chdir command.
318
 */
319
void
320
/*ARGSUSED*/
321
dochngd(Char **v, struct command *t)
322
{
323
    Char *cp;
324
    struct directory *dp;
325
326
    skipargs(&v, " [<dir>]");
327
    printd = 0;
328
    if (*v == NULL) {
329
	if ((cp = value(STRhome)) == NULL || *cp == 0)
330
	    stderror(ERR_NAME | ERR_NOHOMEDIR);
331
	if (chdir(short2str(cp)) < 0)
332
	    stderror(ERR_NAME | ERR_CANTCHANGE);
333
	cp = Strsave(cp);
334
    }
335
    else if (v[1] != NULL) {
336
	stderror(ERR_NAME | ERR_TOOMANY);
337
	/* NOTREACHED */
338
	return;
339
    }
340
    else if ((dp = dfind(*v)) != 0) {
341
	char   *tmp;
342
343
	printd = 1;
344
	if (chdir(tmp = short2str(dp->di_name)) < 0)
345
	    stderror(ERR_SYSTEM, tmp, strerror(errno));
346
	dcwd->di_prev->di_next = dcwd->di_next;
347
	dcwd->di_next->di_prev = dcwd->di_prev;
348
	dfree(dcwd);
349
	dnewcwd(dp);
350
	return;
351
    }
352
    else
353
	cp = dfollow(*v);
354
    dp = xcalloc(1, sizeof(struct directory));
355
    dp->di_name = cp;
356
    dp->di_count = 0;
357
    dp->di_next = dcwd->di_next;
358
    dp->di_prev = dcwd->di_prev;
359
    dp->di_prev->di_next = dp;
360
    dp->di_next->di_prev = dp;
361
    dfree(dcwd);
362
    dnewcwd(dp);
363
}
364
365
static Char *
366
dgoto(Char *cp)
367
{
368
    Char   *dp;
369
370
    if (*cp != '/') {
371
	Char *p, *q;
372
	int     cwdlen;
373
374
	for (p = dcwd->di_name; *p++;)
375
	    continue;
376
	if ((cwdlen = p - dcwd->di_name - 1) == 1)	/* root */
377
	    cwdlen = 0;
378
	for (p = cp; *p++;)
379
	    continue;
380
	dp = xreallocarray(NULL, (cwdlen + (p - cp) + 1), sizeof(Char));
381
	for (p = dp, q = dcwd->di_name; (*p++ = *q++) != '\0';)
382
	    continue;
383
	if (cwdlen)
384
	    p[-1] = '/';
385
	else
386
	    p--;		/* don't add a / after root */
387
	for (q = cp; (*p++ = *q++) != '\0';)
388
	    continue;
389
	free(cp);
390
	cp = dp;
391
	dp += cwdlen;
392
    }
393
    else
394
	dp = cp;
395
396
    cp = dcanon(cp, dp);
397
    return cp;
398
}
399
400
/*
401
 * dfollow - change to arg directory; fall back on cdpath if not valid
402
 */
403
static Char *
404
dfollow(Char *cp)
405
{
406
    Char *dp;
407
    struct varent *c;
408
    char    ebuf[PATH_MAX];
409
    int serrno;
410
411
    cp = globone(cp, G_ERROR);
412
    /*
413
     * if we are ignoring symlinks, try to fix relatives now.
414
     */
415
    dp = dnormalize(cp);
416
    if (chdir(short2str(dp)) >= 0) {
417
	free(cp);
418
	return dgoto(dp);
419
    }
420
    else {
421
	free(dp);
422
	if (chdir(short2str(cp)) >= 0)
423
	    return dgoto(cp);
424
	serrno = errno;
425
    }
426
427
    if (cp[0] != '/' && !prefix(STRdotsl, cp) && !prefix(STRdotdotsl, cp)
428
	&& (c = adrof(STRcdpath))) {
429
	Char  **cdp;
430
	Char *p;
431
	Char    buf[PATH_MAX];
432
433
	for (cdp = c->vec; *cdp; cdp++) {
434
	    for (dp = buf, p = *cdp; (*dp++ = *p++) != '\0';)
435
		continue;
436
	    dp[-1] = '/';
437
	    for (p = cp; (*dp++ = *p++) != '\0';)
438
		continue;
439
	    if (chdir(short2str(buf)) >= 0) {
440
		printd = 1;
441
		free(cp);
442
		cp = Strsave(buf);
443
		return dgoto(cp);
444
	    }
445
	}
446
    }
447
    dp = value(cp);
448
    if ((dp[0] == '/' || dp[0] == '.') && chdir(short2str(dp)) >= 0) {
449
	free(cp);
450
	cp = Strsave(dp);
451
	printd = 1;
452
	return dgoto(cp);
453
    }
454
    (void) strlcpy(ebuf, short2str(cp), sizeof ebuf);
455
    free(cp);
456
    stderror(ERR_SYSTEM, ebuf, strerror(serrno));
457
    return (NULL);
458
}
459
460
461
/*
462
 * dopushd - push new directory onto directory stack.
463
 *	with no arguments exchange top and second.
464
 *	with numeric argument (+n) bring it to top.
465
 */
466
void
467
/*ARGSUSED*/
468
dopushd(Char **v, struct command *t)
469
{
470
    struct directory *dp;
471
472
    skipargs(&v, " [<dir>|+<n>]");
473
    printd = 1;
474
    if (*v == NULL) {
475
	char   *tmp;
476
477
	if ((dp = dcwd->di_prev) == &dhead)
478
	    dp = dhead.di_prev;
479
	if (dp == dcwd)
480
	    stderror(ERR_NAME | ERR_NODIR);
481
	if (chdir(tmp = short2str(dp->di_name)) < 0)
482
	    stderror(ERR_SYSTEM, tmp, strerror(errno));
483
	dp->di_prev->di_next = dp->di_next;
484
	dp->di_next->di_prev = dp->di_prev;
485
	dp->di_next = dcwd->di_next;
486
	dp->di_prev = dcwd;
487
	dcwd->di_next->di_prev = dp;
488
	dcwd->di_next = dp;
489
    }
490
    else if (v[1] != NULL) {
491
	stderror(ERR_NAME | ERR_TOOMANY);
492
	/* NOTREACHED */
493
	return;
494
    }
495
    else if ((dp = dfind(*v)) != NULL) {
496
	char   *tmp;
497
498
	if (chdir(tmp = short2str(dp->di_name)) < 0)
499
	    stderror(ERR_SYSTEM, tmp, strerror(errno));
500
    }
501
    else {
502
	Char *ccp;
503
504
	ccp = dfollow(*v);
505
	dp = xcalloc(1, sizeof(struct directory));
506
	dp->di_name = ccp;
507
	dp->di_count = 0;
508
	dp->di_prev = dcwd;
509
	dp->di_next = dcwd->di_next;
510
	dcwd->di_next = dp;
511
	dp->di_next->di_prev = dp;
512
    }
513
    dnewcwd(dp);
514
}
515
516
/*
517
 * dfind - find a directory if specified by numeric (+n) argument
518
 */
519
static struct directory *
520
dfind(Char *cp)
521
{
522
    struct directory *dp;
523
    int i;
524
    Char *ep;
525
526
    if (*cp++ != '+')
527
	return (0);
528
    for (ep = cp; Isdigit(*ep); ep++)
529
	continue;
530
    if (*ep)
531
	return (0);
532
    i = getn(cp);
533
    if (i <= 0)
534
	return (0);
535
    for (dp = dcwd; i != 0; i--) {
536
	if ((dp = dp->di_prev) == &dhead)
537
	    dp = dp->di_prev;
538
	if (dp == dcwd)
539
	    stderror(ERR_NAME | ERR_DEEP);
540
    }
541
    return (dp);
542
}
543
544
/*
545
 * dopopd - pop a directory out of the directory stack
546
 *	with a numeric argument just discard it.
547
 */
548
void
549
/*ARGSUSED*/
550
dopopd(Char **v, struct command *t)
551
{
552
    struct directory *dp, *p = NULL;
553
554
    skipargs(&v, " [+<n>]");
555
    printd = 1;
556
    if (*v == NULL)
557
	dp = dcwd;
558
    else if (v[1] != NULL) {
559
	stderror(ERR_NAME | ERR_TOOMANY);
560
	/* NOTREACHED */
561
	return;
562
    }
563
    else if ((dp = dfind(*v)) == 0)
564
	stderror(ERR_NAME | ERR_BADDIR);
565
    if (dp->di_prev == &dhead && dp->di_next == &dhead)
566
	stderror(ERR_NAME | ERR_EMPTY);
567
    if (dp == dcwd) {
568
	char   *tmp;
569
570
	if ((p = dp->di_prev) == &dhead)
571
	    p = dhead.di_prev;
572
	if (chdir(tmp = short2str(p->di_name)) < 0)
573
	    stderror(ERR_SYSTEM, tmp, strerror(errno));
574
    }
575
    dp->di_prev->di_next = dp->di_next;
576
    dp->di_next->di_prev = dp->di_prev;
577
    if (dp == dcwd)
578
	dnewcwd(p);
579
    else {
580
	printdirs();
581
    }
582
    dfree(dp);
583
}
584
585
/*
586
 * dfree - free the directory (or keep it if it still has ref count)
587
 */
588
void
589
dfree(struct directory *dp)
590
{
591
592
    if (dp->di_count != 0) {
593
	dp->di_next = dp->di_prev = 0;
594
    }
595
    else {
596
	free((char *) dp->di_name);
597
	free(dp);
598
    }
599
}
600
601
/*
602
 * dcanon - canonicalize the pathname, removing excess ./ and ../ etc.
603
 *	we are of course assuming that the file system is standardly
604
 *	constructed (always have ..'s, directories have links)
605
 */
606
Char   *
607
dcanon(Char *cp, Char *p)
608
{
609
    Char *sp;
610
    Char *p1, *p2;	/* general purpose */
611
    bool    slash;
612
613
    Char    link[PATH_MAX];
614
    char    tlink[PATH_MAX];
615
    int     cc;
616
    Char   *newcp;
617
618
    /*
619
     * christos: if the path given does not start with a slash prepend cwd. If
620
     * cwd does not start with a path or the result would be too long abort().
621
     */
622
    if (*cp != '/') {
623
	Char    tmpdir[PATH_MAX];
624
625
	p1 = value(STRcwd);
626
	if (p1 == NULL || *p1 != '/')
627
	    abort();
628
	if (Strlen(p1) + Strlen(cp) + 1 >= PATH_MAX)
629
	    abort();
630
	(void) Strlcpy(tmpdir, p1, sizeof tmpdir/sizeof(Char));
631
	(void) Strlcat(tmpdir, STRslash, sizeof tmpdir/sizeof(Char));
632
	(void) Strlcat(tmpdir, cp, sizeof tmpdir/sizeof(Char));
633
	free(cp);
634
	cp = p = Strsave(tmpdir);
635
    }
636
637
    while (*p) {		/* for each component */
638
	sp = p;			/* save slash address */
639
	while (*++p == '/')	/* flush extra slashes */
640
	    continue;
641
	if (p != ++sp)
642
	    for (p1 = sp, p2 = p; (*p1++ = *p2++) != '\0';)
643
		continue;
644
	p = sp;			/* save start of component */
645
	slash = 0;
646
	while (*p)		/* find next slash or end of path */
647
	    if (*++p == '/') {
648
		slash = 1;
649
		*p = 0;
650
		break;
651
	    }
652
653
	if (*sp == '\0')	/* if component is null */
654
	    if (--sp == cp)	/* if path is one char (i.e. /) */
655
		break;
656
	    else
657
		*sp = '\0';
658
	else if (sp[0] == '.' && sp[1] == 0) {
659
	    if (slash) {
660
		for (p1 = sp, p2 = p + 1; (*p1++ = *p2++) != '\0';)
661
		    continue;
662
		p = --sp;
663
	    }
664
	    else if (--sp != cp)
665
		*sp = '\0';
666
	}
667
	else if (sp[0] == '.' && sp[1] == '.' && sp[2] == 0) {
668
	    /*
669
	     * We have something like "yyy/xxx/..", where "yyy" can be null or
670
	     * a path starting at /, and "xxx" is a single component. Before
671
	     * compressing "xxx/..", we want to expand "yyy/xxx", if it is a
672
	     * symbolic link.
673
	     */
674
	    *--sp = 0;		/* form the pathname for readlink */
675
	    if (sp != cp && !adrof(STRignore_symlinks) &&
676
		(cc = readlink(short2str(cp), tlink,
677
			       sizeof tlink-1)) >= 0) {
678
		tlink[cc] = '\0';
679
		(void) Strlcpy(link, str2short(tlink), sizeof link/sizeof(Char));
680
681
		if (slash)
682
		    *p = '/';
683
		/*
684
		 * Point p to the '/' in "/..", and restore the '/'.
685
		 */
686
		*(p = sp) = '/';
687
		/*
688
		 * find length of p
689
		 */
690
		for (p1 = p; *p1++;)
691
		    continue;
692
		if (*link != '/') {
693
		    /*
694
		     * Relative path, expand it between the "yyy/" and the
695
		     * "/..". First, back sp up to the character past "yyy/".
696
		     */
697
		    while (*--sp != '/')
698
			continue;
699
		    sp++;
700
		    *sp = 0;
701
		    /*
702
		     * New length is "yyy/" + link + "/.." and rest
703
		     */
704
		    p1 = newcp = xreallocarray(NULL,
705
			(sp - cp) + cc + (p1 - p), sizeof(Char));
706
		    /*
707
		     * Copy new path into newcp
708
		     */
709
		    for (p2 = cp; (*p1++ = *p2++) != '\0';)
710
			continue;
711
		    for (p1--, p2 = link; (*p1++ = *p2++) != '\0';)
712
			continue;
713
		    for (p1--, p2 = p; (*p1++ = *p2++) != '\0';)
714
			continue;
715
		    /*
716
		     * Restart canonicalization at expanded "/xxx".
717
		     */
718
		    p = sp - cp - 1 + newcp;
719
		}
720
		else {
721
		    /*
722
		     * New length is link + "/.." and rest
723
		     */
724
		    p1 = newcp = xreallocarray(NULL, cc + (p1 - p),
725
		        sizeof(Char));
726
		    /*
727
		     * Copy new path into newcp
728
		     */
729
		    for (p2 = link; (*p1++ = *p2++) != '\0';)
730
			continue;
731
		    for (p1--, p2 = p; (*p1++ = *p2++) != '\0';)
732
			continue;
733
		    /*
734
		     * Restart canonicalization at beginning
735
		     */
736
		    p = newcp;
737
		}
738
		free(cp);
739
		cp = newcp;
740
		continue;	/* canonicalize the link */
741
	    }
742
	    *sp = '/';
743
	    if (sp != cp)
744
		while (*--sp != '/')
745
		    continue;
746
	    if (slash) {
747
		for (p1 = sp + 1, p2 = p + 1; (*p1++ = *p2++) != '\0';)
748
		    continue;
749
		p = sp;
750
	    }
751
	    else if (cp == sp)
752
		*++sp = '\0';
753
	    else
754
		*sp = '\0';
755
	}
756
	else {			/* normal dir name (not . or .. or nothing) */
757
758
	    if (sp != cp && adrof(STRchase_symlinks) &&
759
		!adrof(STRignore_symlinks) &&
760
		(cc = readlink(short2str(cp), tlink,
761
			       sizeof tlink-1)) >= 0) {
762
		tlink[cc] = '\0';
763
		(void) Strlcpy(link, str2short(tlink), sizeof link/sizeof(Char));
764
765
		/*
766
		 * restore the '/'.
767
		 */
768
		if (slash)
769
		    *p = '/';
770
771
		/*
772
		 * point sp to p (rather than backing up).
773
		 */
774
		sp = p;
775
776
		/*
777
		 * find length of p
778
		 */
779
		for (p1 = p; *p1++;)
780
		    continue;
781
		if (*link != '/') {
782
		    /*
783
		     * Relative path, expand it between the "yyy/" and the
784
		     * remainder. First, back sp up to the character past
785
		     * "yyy/".
786
		     */
787
		    while (*--sp != '/')
788
			continue;
789
		    sp++;
790
		    *sp = 0;
791
		    /*
792
		     * New length is "yyy/" + link + "/.." and rest
793
		     */
794
		    p1 = newcp = xreallocarray(NULL,
795
			  (sp - cp) + cc + (p1 - p), sizeof(Char));
796
		    /*
797
		     * Copy new path into newcp
798
		     */
799
		    for (p2 = cp; (*p1++ = *p2++) != '\0';)
800
			continue;
801
		    for (p1--, p2 = link; (*p1++ = *p2++) != '\0';)
802
			continue;
803
		    for (p1--, p2 = p; (*p1++ = *p2++) != '\0';)
804
			continue;
805
		    /*
806
		     * Restart canonicalization at expanded "/xxx".
807
		     */
808
		    p = sp - cp - 1 + newcp;
809
		}
810
		else {
811
		    /*
812
		     * New length is link + the rest
813
		     */
814
		    p1 = newcp = xreallocarray(NULL, cc + (p1 - p), sizeof(Char));
815
		    /*
816
		     * Copy new path into newcp
817
		     */
818
		    for (p2 = link; (*p1++ = *p2++) != '\0';)
819
			continue;
820
		    for (p1--, p2 = p; (*p1++ = *p2++) != '\0';)
821
			continue;
822
		    /*
823
		     * Restart canonicalization at beginning
824
		     */
825
		    p = newcp;
826
		}
827
		free(cp);
828
		cp = newcp;
829
		continue;	/* canonicalize the link */
830
	    }
831
	    if (slash)
832
		*p = '/';
833
	}
834
    }
835
836
    /*
837
     * fix home...
838
     */
839
    p1 = value(STRhome);
840
    cc = Strlen(p1);
841
    /*
842
     * See if we're not in a subdir of STRhome
843
     */
844
    if (p1 && *p1 == '/' &&
845
	(Strncmp(p1, cp, cc) != 0 || (cp[cc] != '/' && cp[cc] != '\0'))) {
846
	static ino_t home_ino = -1;
847
	static dev_t home_dev = -1;
848
	static Char *home_ptr = NULL;
849
	struct stat statbuf;
850
851
	/*
852
	 * Get dev and ino of STRhome
853
	 */
854
	if (home_ptr != p1 &&
855
	    stat(short2str(p1), &statbuf) != -1) {
856
	    home_dev = statbuf.st_dev;
857
	    home_ino = statbuf.st_ino;
858
	    home_ptr = p1;
859
	}
860
	/*
861
	 * Start comparing dev & ino backwards
862
	 */
863
	Strlcpy(link, cp, sizeof link/sizeof(Char));
864
	p2 = link;
865
	for (sp = NULL; *p2 && stat(short2str(p2), &statbuf) != -1;) {
866
	    if (statbuf.st_dev == home_dev &&
867
		statbuf.st_ino == home_ino) {
868
		sp = (Char *) - 1;
869
		break;
870
	    }
871
	    if ((sp = Strrchr(p2, '/')) != NULL)
872
		*sp = '\0';
873
	}
874
	/*
875
	 * See if we found it
876
	 */
877
	if (*p2 && sp == (Char *) -1) {
878
	    /*
879
	     * Use STRhome to make '~' work
880
	     */
881
	    newcp = Strspl(p1, cp + Strlen(p2));
882
	    free(cp);
883
	    cp = newcp;
884
	}
885
    }
886
    return cp;
887
}
888
889
890
/*
891
 * dnewcwd - make a new directory in the loop the current one
892
 */
893
static void
894
dnewcwd(struct directory *dp)
895
{
896
    dcwd = dp;
897
    dset(dcwd->di_name);
898
    if (printd && !(adrof(STRpushdsilent)))
899
	printdirs();
900
}