static Char * dgoto(Char *cp) { Char *dp, *ret; if (!ABSOLUTEP(cp)) { Char *p, *q; size_t cwdlen; cwdlen = Strlen(dcwd->di_name); if (cwdlen == 1) /* root */ cwdlen = 0; dp = xmalloc((cwdlen + Strlen(cp) + 2) * sizeof(Char)); for (p = dp, q = dcwd->di_name; (*p++ = *q++) != '\0';) continue; if (cwdlen) p[-1] = '/'; else p--; /* don't add a / after root */ Strcpy(p, cp); xfree(cp); cp = dp; dp += cwdlen; } else dp = cp; #if defined(WINNT_NATIVE) return agetcwd(); #elif defined(__CYGWIN__) if (ABSOLUTEP(cp) && cp[1] == ':') { /* Only DOS paths are treated that way */ return agetcwd(); } else { cleanup_push(cp, xfree); ret = dcanon(cp, dp); cleanup_ignore(cp); cleanup_until(cp); } #else /* !WINNT_NATIVE */ cleanup_push(cp, xfree); ret = dcanon(cp, dp); cleanup_ignore(cp); cleanup_until(cp); #endif /* WINNT_NATIVE */ return ret; }
/* * Get HOME directory. */ astr get_home_dir(void) { char *s = getenv("HOME"); astr as; if (s != NULL && strlen(s) < PATH_MAX) as = astr_new_cstr(s); else as = agetcwd(); return as; }
int main(int argc, char *argv[]) { char *cwd, c; char mode = 'L'; while((c = getopt(argc, argv, "LP")) != -1) switch(c) { case 'L': case 'P': mode = c; break; default: exit(EXIT_FAILURE); } cwd = agetcwd(); puts((mode == 'L') ? getpwd(cwd) : cwd); return EXIT_SUCCESS; }
/* * Return the current directory. */ static astr get_current_dir(void) { astr buf; int p; if (cur_bp->filename != NULL) /* If the current buffer has a filename, get the current directory name from it. */ buf = astr_new_cstr(cur_bp->filename); else { /* Get the current directory name from the system. */ buf = agetcwd(); if (astr_len(buf) != 0 && *astr_char(buf, -1) != '/') astr_cat_char(buf, '/'); } p = astr_rfind_cstr(buf, "/"); astr_truncate(buf, p + 1); return buf; }
/* * This functions does some corrections and expansions to * the passed path: * * - expands `~/' and `~name/' expressions; * - replaces `//' with `/' (restarting from the root directory); * - removes `..' and `.' entries. * * The return value indicates success or failure. */ bool expand_path (astr path) { int ok = true; const char *sp = astr_cstr (path); astr epath = astr_new (); if (*sp != '/' && *sp != '~') { astr_cat (epath, agetcwd ()); if (astr_len (epath) == 0 || astr_get (epath, astr_len (epath) - 1) != '/') astr_cat_char (epath, '/'); } for (const char *p = sp; *p != '\0';) { if (*p == '/') { if (*++p == '/') { /* Got `//'. Restart from this point. */ while (*p == '/') p++; astr_truncate (epath, 0); } if (astr_len (epath) == 0 || astr_get (epath, astr_len (epath) - 1) != '/') astr_cat_char (epath, '/'); } else if (*p == '~' && (p == sp || p[-1] == '/')) { /* Got `/~' or leading `~'. Restart from this point. */ struct passwd *pw; astr_truncate (epath, 0); ++p; if (*p == '/') { /* Got `~/'. Insert the user's home directory. */ pw = getpwuid (getuid ()); if (pw == NULL) { ok = false; break; } if (!STREQ (pw->pw_dir, "/")) astr_cat_cstr (epath, pw->pw_dir); } else { /* Got `~something'. Insert that user's home directory. */ astr as = astr_new (); while (*p != '\0' && *p != '/') astr_cat_char (as, *p++); pw = getpwnam (astr_cstr (as)); if (pw == NULL) { ok = false; break; } astr_cat_cstr (epath, pw->pw_dir); } } else if (*p == '.' && (p[1] == '/' || p[1] == '\0')) { /* Got `.'. */ ++p; } else if (*p == '.' && p[1] == '.' && (p[2] == '/' || p[2] == '\0')) { /* Got `..'. */ if (astr_len (epath) >= 1 && astr_get (epath, astr_len (epath) - 1) == '/') astr_truncate (epath, astr_len (epath) - 1); while (astr_get (epath, astr_len (epath) - 1) != '/' && astr_len (epath) >= 1) astr_truncate (epath, astr_len (epath) - 1); p += 2; } if (*p != '~') while (*p != '\0' && *p != '/') astr_cat_char (epath, *p++); } astr_cpy (path, epath); return ok; }
int main(int argc, char *argv[]) { if (argc < 1) { dprintf(STDERR_FILENO, "wrun called without argument\n"); terminate_nocore(); } shift(&argc, &argv); if (argc > 1 && !strcmp(argv[0], "--tool_name")) { shift(&argc, &argv); tool_name = shift(&argc, &argv); } fill_std_fd_info_identity(STDIN_FILENO); fill_std_fd_info_identity(STDOUT_FILENO); fill_std_fd_info_identity(STDERR_FILENO); bool force_redirects = false; bool silent_breakaway = false; int port; bool terminate = !get_outbash_infos(&port, &force_redirects); struct string outbash_command = string_create(""); if (argc && !strcmp(argv[0], ":")) { shift(&argc, &argv); string_append(&outbash_command, "cd:~\n"); } else { char* cwd = agetcwd(); if (is_absolute_drive_fs_path(cwd)) { char* cwd_win32 = convert_drive_fs_path_to_win32(cwd); string_append(&outbash_command, "cd:"); string_append(&outbash_command, cwd_win32); string_append(&outbash_command, "\n"); free(cwd_win32); } free(cwd); } while (argc && !strncmp(argv[0], "--", 2)) { if (!strcmp(argv[0], "--")) { shift(&argc, &argv); break; } if (!strcmp(argv[0], "--env")) { shift(&argc, &argv); while (argc && strncmp(argv[0], "--", 2) != 0 && *argv[0] != '\0' && strchr(argv[0] + 1, '=')) { string_append(&outbash_command, "env:"); string_append(&outbash_command, argv[0]); string_append(&outbash_command, "\n"); shift(&argc, &argv); } } else if (!strcmp(argv[0], "--force-redirects")) { force_redirects = true; shift(&argc, &argv); } else if (!strcmp(argv[0], "--silent-breakaway")) { silent_breakaway = true; shift(&argc, &argv); } else if (!strcmp(argv[0], "--help")) { print_help(); exit(1); } else { dprintf(STDERR_FILENO, "%s: unknown command line option: %s\n", tool_name, argv[0]); dprintf(STDERR_FILENO, "type %s --help for more information.\n", tool_name); terminate_nocore(); } } if (terminate) terminate_nocore(); check_argc(argc); decide_will_redirect(STDIN_FILENO, force_redirects); decide_will_redirect(STDOUT_FILENO, force_redirects); decide_will_redirect(STDERR_FILENO, force_redirects); int sock_ctrl = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (sock_ctrl < 0) { dprintf(STDERR_FILENO, "%s: socket() failed: %s\n", tool_name, my_strerror(errno)); terminate_nocore(); } #define STDIN_NEEDS_SOCKET_REDIRECT 1 #define STDOUT_NEEDS_SOCKET_REDIRECT 2 #define STDERR_NEEDS_SOCKET_REDIRECT 4 #define STDERR_SOCKREDIR_TO_STDOUT 8 int redirects = (needs_socket_redirect(STDIN_FILENO) ? STDIN_NEEDS_SOCKET_REDIRECT : 0) | (needs_socket_redirect(STDOUT_FILENO) ? STDOUT_NEEDS_SOCKET_REDIRECT : 0); if (needs_socket_redirect(STDERR_FILENO)) { if ((redirects & STDOUT_NEEDS_SOCKET_REDIRECT) && are_stdfd_to_same_thing(STDOUT_FILENO, STDERR_FILENO)) redirects |= STDERR_SOCKREDIR_TO_STDOUT; else redirects |= STDERR_NEEDS_SOCKET_REDIRECT; } struct listening_socket lsock_in = NO_LISTENING_SOCKET; struct listening_socket lsock_out = NO_LISTENING_SOCKET; struct listening_socket lsock_err = NO_LISTENING_SOCKET; if (redirects & STDIN_NEEDS_SOCKET_REDIRECT) lsock_in = socket_listen_one_loopback(); if (redirects & STDOUT_NEEDS_SOCKET_REDIRECT) lsock_out = socket_listen_one_loopback(); if (redirects & STDERR_NEEDS_SOCKET_REDIRECT) lsock_err = socket_listen_one_loopback(); ask_redirect(&outbash_command, "stdin:", STDIN_FILENO, lsock_in.port); ask_redirect(&outbash_command, "stdout:", STDOUT_FILENO, lsock_out.port); ask_redirect(&outbash_command, "stderr:", STDERR_FILENO, (redirects & STDERR_NEEDS_SOCKET_REDIRECT) ? lsock_err.port : lsock_out.port); if (silent_breakaway) string_append(&outbash_command, "silent_breakaway:1\n"); char* win32_module; if (is_absolute_drive_fs_path(argv[0])) { win32_module = convert_drive_fs_path_to_win32(argv[0]); string_append(&outbash_command, "module:"); string_append(&outbash_command, win32_module); string_append(&outbash_command, "\n"); } else { win32_module = convert_slash_to_backslash(argv[0]); } const bool module_need_quotes = (NULL != strpbrk(win32_module, " \t")); string_append(&outbash_command, "run:"); if (module_need_quotes) string_append(&outbash_command, "\""); string_append(&outbash_command, win32_module); if (module_need_quotes) string_append(&outbash_command, "\""); free(win32_module); for (int i = 1; i < argc; i++) { string_append(&outbash_command, " "); string_append(&outbash_command, argv[i]); } string_append(&outbash_command, "\n\n"); //dprintf(STDOUT_FILENO, "%s", outbash_command.str); //return EXIT_FAILURE; signal(SIGPIPE, SIG_IGN); sigset_t signal_set, orig_mask; //////////////////////////// unblock SIGUSR1 sigemptyset(&signal_set); sigaddset(&signal_set, SIGUSR1); pthread_sigmask(SIG_UNBLOCK, &signal_set, NULL); //////////////////////////// block SIGTSTP sigemptyset(&signal_set); sigaddset(&signal_set, SIGTSTP); pthread_sigmask(SIG_BLOCK, &signal_set, &orig_mask); //////////////////////////// install custom SIGTSTP handler if signal was not ignored struct sigaction sa; sigaction(SIGTSTP, NULL, &sa); const bool ignore_sigtstp = (sa.sa_handler == SIG_IGN); if (!ignore_sigtstp) { sa.sa_handler = tstop_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sigaction(SIGTSTP, &sa, NULL); } //////////////////////////// install custom SIGUSR1 handler to wake-up blocked IO forwarding threads // NOTE: the handler itself do nothing, but any blocked syscall will return with EINTR error sa.sa_handler = noop_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sigaction(SIGUSR1, &sa, NULL); struct sockaddr_in serv_addr; memset(&serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); serv_addr.sin_port = htons(port); if (connect(sock_ctrl, (const struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) { // NOTE: I'm not sure that WSL does what Linux does concerning // http://www.madore.org/~david/computers/connect-intr.html // for now we do not expect to recover after an interruption here. dprintf(STDERR_FILENO, "%s: connect() failed: %s\n", tool_name, my_strerror(errno)); terminate_nocore(); } if (send_all(sock_ctrl, outbash_command.str, outbash_command.length, 0) < 0) { dprintf(STDERR_FILENO, "%s: send_all() failed: %s\n", tool_name, my_strerror(errno)); terminate_nocore(); } string_destroy(&outbash_command); static struct forward_state fs[3]; fs_init_accept_as_needed(&fs[STDIN_FILENO], &lsock_in, redirects & STDIN_NEEDS_SOCKET_REDIRECT, STDIN_FILENO, "stdin"); fs_init_accept_as_needed(&fs[STDOUT_FILENO], &lsock_out, redirects & STDOUT_NEEDS_SOCKET_REDIRECT, STDOUT_FILENO, "stdout"); fs_init_accept_as_needed(&fs[STDERR_FILENO], &lsock_err, redirects & STDERR_NEEDS_SOCKET_REDIRECT, STDERR_FILENO, "stderr"); char *line = ctrl_readln(sock_ctrl, NULL); if (!line || strcmp(line, "connected")) { dprintf(STDERR_FILENO, "%s: did not receive connection validation from outbash.exe\n", tool_name); terminate_nocore(); } close_listener(&lsock_in); close_listener(&lsock_out); close_listener(&lsock_err); enum state_e state = RUNNING; int program_return_code = 255; pthread_t forward_threads[3]; bool active_threads[3] = {0}; for (int i = 0; i < 3; i++) { if ((!fs[i].dead_in) || (!fs[i].dead_out)) { int err = pthread_create(&forward_threads[i], NULL, forward_one_stream, &fs[i]); if (err != 0) { dprintf(STDERR_FILENO, "%s: pthread_create() failed: %s\n", tool_name, my_strerror(err)); terminate_nocore(); } active_threads[i] = true; } } int nfds = sock_ctrl + 1; while (state != TERMINATED) { fd_set rfds; FD_ZERO(&rfds); if (sock_ctrl < 0 || sock_ctrl >= FD_SETSIZE) { dprintf(STDERR_FILENO, "%s: sock_ctrl=%d out of range\n", tool_name, sock_ctrl); abort(); } FD_SET(sock_ctrl, &rfds); int pselect_res = pselect(nfds, &rfds, NULL, NULL, NULL, &orig_mask); // tstop_handler can run here int pselect_errno = errno; if (tstop_req && state == RUNNING) { int r = send_all(sock_ctrl, "suspend\n", strlen("suspend\n"), 0); if (r < 0 && err_is_connection_broken(errno)) { // We will never be able to ask outbash to suspend the // Windows process, the expected reason is that it actually // has already terminated and we don't know yet about that, // so stop the suspend forwarding mechanism and suspend // ourselves immediately. shutdown(sock_ctrl, SHUT_WR); // also we can't send anything anymore // XXX to comment for WSL bug workaround? proba low here... signal(SIGTSTP, SIG_DFL); state = DYING; raise(SIGTSTP); pthread_sigmask(SIG_SETMASK, &orig_mask, NULL); } else if (r < 0) { // other errors dprintf(STDERR_FILENO, "%s: send_all(\"suspend\\n\") failed: %s\n", tool_name, my_strerror(errno)); abort(); } else { // OK // It's up to outbash now, just wait for its "suspend_ok" // answer after it has suspended the Windows process. state = SUSPEND_PENDING; } } if (pselect_res < 0 && pselect_errno == EINTR) { // "On error, -1 is returned, and errno is set appropriately; // the sets and timeout become undefined, so do not rely on // their contents after an error." continue; } if (pselect_res < 0) { dprintf(STDERR_FILENO, "%s: pselect() failed: %s\n", tool_name, my_strerror(pselect_errno)); abort(); } if (FD_ISSET(sock_ctrl, &rfds)) { while (1) { int nonblock_marker; line = ctrl_readln(sock_ctrl, &nonblock_marker); if (!line && nonblock_marker) break; if (line && !strcmp(line, "suspend_ok")) { if (state == SUSPEND_PENDING) { signal(SIGTSTP, SIG_DFL); raise(SIGTSTP); sigset_t previous_mask; pthread_sigmask(SIG_SETMASK, &orig_mask, &previous_mask); // >>> Process will Stop here, until SIGCONT <<< pthread_sigmask(SIG_SETMASK, &previous_mask, NULL); tstop_req = 0; int r = send_all(sock_ctrl, "resume\n", strlen("resume\n"), 0); if (r < 0 && err_is_connection_broken(errno)) { // killed when suspended (if this is possible?) // or maybe just before an attempt? shutdown(sock_ctrl, SHUT_WR); // XXX to comment for WSL bug workaround? proba low here... state = DYING; pthread_sigmask(SIG_SETMASK, &orig_mask, NULL); } else if (r < 0) { dprintf(STDERR_FILENO, "%s: send_all(\"resume\\n\") failed: %s\n", tool_name, my_strerror(errno)); abort(); } else { state = RUNNING; sa.sa_handler = tstop_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sigaction(SIGTSTP, &sa, NULL); } } else { dprintf(STDERR_FILENO, "%s: spurious \"suspend_ok\" received\n", tool_name); } } else { // not "suspend_ok" => for now only other cases are exit conditions program_return_code = get_return_code(line); shutdown(sock_ctrl, SHUT_RDWR); signal(SIGTSTP, ignore_sigtstp ? SIG_IGN : SIG_DFL); if ((tstop_req && state == RUNNING) || state == SUSPEND_PENDING) { // We expect to stop soon, but not without flushing the OS TCP // buffers and our owns, and other WSL processes in a pipe might // already be suspended, so we better honor suspend requests ASAP. raise(SIGTSTP); } pthread_sigmask(SIG_SETMASK, &orig_mask, NULL); tstop_req = 0; state = TERMINATED; break; } } } } // XXX: this is not ideal if the Win32 side managed to maintain the // redirection socket beyond the lifetime of the launched process, // however things seem to already be not reliable for Windows reasons // in this case if (active_threads[0]) { __sync_fetch_and_add(&fs[0].ask_terminate, 1); useconds_t usec_sleep = 1000; while (!__sync_fetch_and_add(&fs[0].finished, 0)) { pthread_kill(forward_threads[0], SIGUSR1); usleep(usec_sleep); usec_sleep *= 2; if (usec_sleep > 60000000) usec_sleep = 60000000; } } for (int i = 0; i < 3; i++) if (active_threads[i]) pthread_join(forward_threads[i], NULL); return program_return_code; }
/* * This functions does some corrections and expansions to * the passed path: * - expands `~/' and `~name/' expressions; * - replaces `//' with `/' (restarting from the root directory); * - removes `..' and `.' entries. * * If something goes wrong, the string is deleted and NULL returned */ astr expand_path(astr path) { int ret = TRUE; struct passwd *pw; const char *sp = astr_cstr(path); astr epath = astr_new(); if (*sp != '/') { astr_cat_delete(epath, agetcwd()); if (astr_len(epath) == 0 || *astr_char(epath, -1) != '/') astr_cat_char(epath, '/'); } while (*sp != '\0') { if (*sp == '/') { if (*++sp == '/') { /* Got `//'. Restart from this point. */ while (*sp == '/') sp++; astr_truncate(epath, 0); } astr_cat_char(epath, '/'); } else if (*sp == '~') { if (*(sp + 1) == '/') { /* Got `~/'. Restart from this point and insert the user's home directory. */ astr_truncate(epath, 0); if ((pw = getpwuid(getuid())) == NULL) { ret = FALSE; break; } if (strcmp(pw->pw_dir, "/") != 0) astr_cat_cstr(epath, pw->pw_dir); ++sp; } else { /* Got `~something'. Restart from this point and insert that user's home directory. */ astr as = astr_new(); astr_truncate(epath, 0); ++sp; while (*sp != '\0' && *sp != '/') astr_cat_char(as, *sp++); pw = getpwnam(astr_cstr(as)); astr_delete(as); if (pw == NULL) { ret = FALSE; break; } astr_cat_cstr(epath, pw->pw_dir); } } else if (*sp == '.') { if (*(sp + 1) == '/' || *(sp + 1) == '\0') { ++sp; if (*sp == '/' && *(sp + 1) != '/') ++sp; } else if (*(sp + 1) == '.' && (*(sp + 2) == '/' || *(sp + 2) == '\0')) { if (astr_len(epath) >= 1 && *astr_char(epath, -1) == '/') astr_truncate(epath, -1); while (*astr_char(epath, -1) != '/' && astr_len(epath) >= 1) astr_truncate(epath, -1); sp += 2; if (*sp == '/' && *(sp + 1) != '/') ++sp; } else goto got_component; } else { const char *p; got_component: p = sp; while (*p != '\0' && *p != '/') p++; if (*p == '\0') { /* Final filename */ astr_cat_cstr(epath, sp); break; } else { /* Non-final directory */ while (*sp != '/') astr_cat_char(epath, *sp++); } } } astr_cpy(path, epath); astr_delete(epath); if (!ret) { astr_delete(path); return NULL; } return path; }
/* * dinit - initialize current working directory */ void dinit(Char *hp) { Char *cp, *tcp; struct directory *dp; /* Don't believe the login shell home, because it may be a symlink */ tcp = agetcwd(); if (tcp == NULL) { xprintf("%s: %s\n", progname, strerror(errno)); if (hp && *hp) { char *xcp = short2str(hp); dstart(xcp); if (chdir(xcp) == -1) cp = NULL; else cp = Strsave(hp); } else cp = NULL; if (cp == NULL) { dstart("/"); if (chdir("/") == -1) /* I am not even try to print an error message! */ xexit(1); cp = SAVE("/"); } } else { #ifdef S_IFLNK struct stat swd, shp; int swd_ok; swd_ok = stat(short2str(tcp), &swd) == 0; /* * See if $HOME is the working directory we got and use that */ if (swd_ok && hp && *hp && stat(short2str(hp), &shp) != -1 && DEV_DEV_COMPARE(swd.st_dev, shp.st_dev) && swd.st_ino == shp.st_ino) cp = Strsave(hp); else { char *cwd; /* * use PWD if we have it (for subshells) */ if (swd_ok && (cwd = getenv("PWD")) != NULL) { if (stat(cwd, &shp) != -1 && DEV_DEV_COMPARE(swd.st_dev, shp.st_dev) && swd.st_ino == shp.st_ino) { tcp = SAVE(cwd); cleanup_push(tcp, xfree); } } cleanup_push(tcp, xfree); cp = dcanon(tcp, STRNULL); cleanup_ignore(tcp); cleanup_until(tcp); } #else /* S_IFLNK */ cleanup_push(tcp, xfree); cp = dcanon(tcp, STRNULL); cleanup_ignore(tcp); cleanup_until(tcp); #endif /* S_IFLNK */ } dp = xcalloc(sizeof(struct directory), 1); dp->di_name = cp; dp->di_count = 0; dhead.di_next = dhead.di_prev = dp; dp->di_next = dp->di_prev = &dhead; printd = 0; dnewcwd(dp, 0); setcopy(STRdirstack, dp->di_name, VAR_READWRITE|VAR_NOGLOB); }
/* * dcanon - canonicalize the pathname, removing excess ./ and ../ etc. * we are of course assuming that the file system is standardly * constructed (always have ..'s, directories have links) */ Char * dcanon(Char *cp, Char *p) { Char *sp; Char *p1, *p2; /* general purpose */ int slash; #ifdef HAVE_SLASHSLASH int slashslash; #endif /* HAVE_SLASHSLASH */ size_t clen; #ifdef S_IFLNK /* if we have symlinks */ Char *mlink, *newcp; char *tlink; size_t cc; #endif /* S_IFLNK */ clen = Strlen(cp); /* * christos: if the path given does not start with a slash prepend cwd. If * cwd does not start with a slash or the result would be too long try to * correct it. */ if (!ABSOLUTEP(cp)) { Char *tmpdir; size_t len; p1 = varval(STRcwd); if (p1 == STRNULL || !ABSOLUTEP(p1)) { Char *new_cwd = agetcwd(); if (new_cwd == NULL) { xprintf("%s: %s\n", progname, strerror(errno)); setcopy(STRcwd, str2short("/"), VAR_READWRITE|VAR_NOGLOB); } else setv(STRcwd, new_cwd, VAR_READWRITE|VAR_NOGLOB); p1 = varval(STRcwd); } len = Strlen(p1); tmpdir = xmalloc((len + clen + 2) * sizeof (*tmpdir)); (void) Strcpy(tmpdir, p1); (void) Strcat(tmpdir, STRslash); (void) Strcat(tmpdir, cp); xfree(cp); cp = p = tmpdir; } #ifdef HAVE_SLASHSLASH slashslash = (cp[0] == '/' && cp[1] == '/'); #endif /* HAVE_SLASHSLASH */ while (*p) { /* for each component */ sp = p; /* save slash address */ while (*++p == '/') /* flush extra slashes */ continue; if (p != ++sp) for (p1 = sp, p2 = p; (*p1++ = *p2++) != '\0';) continue; p = sp; /* save start of component */ slash = 0; if (*p) while (*++p) /* find next slash or end of path */ if (*p == '/') { slash = 1; *p = 0; break; } #ifdef HAVE_SLASHSLASH if (&cp[1] == sp && sp[0] == '.' && sp[1] == '.' && sp[2] == '\0') slashslash = 1; #endif /* HAVE_SLASHSLASH */ if (*sp == '\0') { /* if component is null */ if (--sp == cp) /* if path is one char (i.e. /) */ break; else *sp = '\0'; } else if (sp[0] == '.' && sp[1] == 0) { if (slash) { for (p1 = sp, p2 = p + 1; (*p1++ = *p2++) != '\0';) continue; p = --sp; } else if (--sp != cp) *sp = '\0'; else sp[1] = '\0'; } else if (sp[0] == '.' && sp[1] == '.' && sp[2] == 0) { /* * We have something like "yyy/xxx/..", where "yyy" can be null or * a path starting at /, and "xxx" is a single component. Before * compressing "xxx/..", we want to expand "yyy/xxx", if it is a * symbolic link. */ *--sp = 0; /* form the pathname for readlink */ #ifdef S_IFLNK /* if we have symlinks */ if (sp != cp && /* symlinks != SYM_IGNORE && */ (tlink = areadlink(short2str(cp))) != NULL) { mlink = str2short(tlink); xfree(tlink); if (slash) *p = '/'; /* * Point p to the '/' in "/..", and restore the '/'. */ *(p = sp) = '/'; if (*mlink != '/') { /* * Relative path, expand it between the "yyy/" and the * "/..". First, back sp up to the character past "yyy/". */ while (*--sp != '/') continue; sp++; *sp = 0; /* * New length is "yyy/" + mlink + "/.." and rest */ p1 = newcp = xmalloc(((sp - cp) + Strlen(mlink) + Strlen(p) + 1) * sizeof(Char)); /* * Copy new path into newcp */ for (p2 = cp; (*p1++ = *p2++) != '\0';) continue; for (p1--, p2 = mlink; (*p1++ = *p2++) != '\0';) continue; for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) continue; /* * Restart canonicalization at expanded "/xxx". */ p = sp - cp - 1 + newcp; } else { newcp = Strspl(mlink, p); /* * Restart canonicalization at beginning */ p = newcp; } xfree(cp); cp = newcp; #ifdef HAVE_SLASHSLASH slashslash = (cp[0] == '/' && cp[1] == '/'); #endif /* HAVE_SLASHSLASH */ continue; /* canonicalize the link */ } #endif /* S_IFLNK */ *sp = '/'; if (sp != cp) while (*--sp != '/') continue; if (slash) { for (p1 = sp + 1, p2 = p + 1; (*p1++ = *p2++) != '\0';) continue; p = sp; } else if (cp == sp) *++sp = '\0'; else *sp = '\0'; } else { /* normal dir name (not . or .. or nothing) */ #ifdef S_IFLNK /* if we have symlinks */ if (sp != cp && symlinks == SYM_CHASE && (tlink = areadlink(short2str(cp))) != NULL) { mlink = str2short(tlink); xfree(tlink); /* * restore the '/'. */ if (slash) *p = '/'; /* * point sp to p (rather than backing up). */ sp = p; if (*mlink != '/') { /* * Relative path, expand it between the "yyy/" and the * remainder. First, back sp up to the character past * "yyy/". */ while (*--sp != '/') continue; sp++; *sp = 0; /* * New length is "yyy/" + mlink + "/.." and rest */ p1 = newcp = xmalloc(((sp - cp) + Strlen(mlink) + Strlen(p) + 1) * sizeof(Char)); /* * Copy new path into newcp */ for (p2 = cp; (*p1++ = *p2++) != '\0';) continue; for (p1--, p2 = mlink; (*p1++ = *p2++) != '\0';) continue; for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) continue; /* * Restart canonicalization at expanded "/xxx". */ p = sp - cp - 1 + newcp; } else { newcp = Strspl(mlink, p); /* * Restart canonicalization at beginning */ p = newcp; } xfree(cp); cp = newcp; #ifdef HAVE_SLASHSLASH slashslash = (cp[0] == '/' && cp[1] == '/'); #endif /* HAVE_SLASHSLASH */ continue; /* canonicalize the mlink */ } #endif /* S_IFLNK */ if (slash) *p = '/'; } } /* * fix home... */ #ifdef S_IFLNK p1 = varval(STRhome); cc = Strlen(p1); /* * See if we're not in a subdir of STRhome */ if (p1 && *p1 == '/' && (Strncmp(p1, cp, cc) != 0 || (cp[cc] != '/' && cp[cc] != '\0'))) { static ino_t home_ino = (ino_t) -1; static dev_t home_dev = (dev_t) -1; static Char *home_ptr = NULL; struct stat statbuf; int found; Char *copy; /* * Get dev and ino of STRhome */ if (home_ptr != p1 && stat(short2str(p1), &statbuf) != -1) { home_dev = statbuf.st_dev; home_ino = statbuf.st_ino; home_ptr = p1; } /* * Start comparing dev & ino backwards */ p2 = copy = Strsave(cp); found = 0; while (*p2 && stat(short2str(p2), &statbuf) != -1) { if (DEV_DEV_COMPARE(statbuf.st_dev, home_dev) && statbuf.st_ino == home_ino) { found = 1; break; } if ((sp = Strrchr(p2, '/')) != NULL) *sp = '\0'; } /* * See if we found it */ if (*p2 && found) { /* * Use STRhome to make '~' work */ newcp = Strspl(p1, cp + Strlen(p2)); xfree(cp); cp = newcp; } xfree(copy); } #endif /* S_IFLNK */ #ifdef HAVE_SLASHSLASH if (slashslash) { if (cp[1] != '/') { p = xmalloc((Strlen(cp) + 2) * sizeof(Char)); *p = '/'; (void) Strcpy(&p[1], cp); xfree(cp); cp = p; } } if (cp[1] == '/' && cp[2] == '/') { for (p1 = &cp[1], p2 = &cp[2]; (*p1++ = *p2++) != '\0';) continue; } #endif /* HAVE_SLASHSLASH */ return cp; }
/* * dfollow - change to arg directory; fall back on cdpath if not valid */ static Char * dfollow(Char *cp, int old) { Char *dp; struct varent *c; int serrno; cp = old ? Strsave(cp) : globone(cp, G_ERROR); cleanup_push(cp, xfree); #ifdef apollo if (Strchr(cp, '`')) { char *dptr; if (chdir(dptr = short2str(cp)) < 0) stderror(ERR_SYSTEM, dptr, strerror(errno)); dp = agetcwd(); cleanup_push(dp, xfree); if (dp != NULL) { cleanup_until(cp); return dgoto(dp); } else stderror(ERR_SYSTEM, dptr, strerror(errno)); } #endif /* apollo */ /* * if we are ignoring symlinks, try to fix relatives now. * if we are expading symlinks, it should be done by now. */ dp = dnormalize(cp, symlinks == SYM_IGNORE); if (chdir(short2str(dp)) >= 0) { cleanup_until(cp); return dgoto(dp); } else { xfree(dp); if (chdir(short2str(cp)) >= 0) { cleanup_ignore(cp); cleanup_until(cp); return dgoto(cp); } else if (errno != ENOENT && errno != ENOTDIR) { int err; err = errno; stderror(ERR_SYSTEM, short2str(cp), strerror(err)); } serrno = errno; } if (cp[0] != '/' && !prefix(STRdotsl, cp) && !prefix(STRdotdotsl, cp) && (c = adrof(STRcdpath)) && c->vec != NULL) { struct Strbuf buf = Strbuf_INIT; Char **cdp; for (cdp = c->vec; *cdp; cdp++) { size_t len = Strlen(*cdp); buf.len = 0; if (len > 0) { Strbuf_append(&buf, *cdp); if ((*cdp)[len - 1] != '/') Strbuf_append1(&buf, '/'); } Strbuf_append(&buf, cp); Strbuf_terminate(&buf); /* * We always want to fix the directory here * If we are normalizing symlinks */ dp = dnormalize(buf.s, symlinks == SYM_IGNORE || symlinks == SYM_EXPAND); if (chdir(short2str(dp)) >= 0) { printd = 1; xfree(buf.s); cleanup_until(cp); return dgoto(dp); } else if (chdir(short2str(cp)) >= 0) { printd = 1; xfree(dp); xfree(buf.s); cleanup_ignore(cp); cleanup_until(cp); return dgoto(cp); } } xfree(buf.s); } dp = varval(cp); if ((dp[0] == '/' || dp[0] == '.') && chdir(short2str(dp)) >= 0) { cleanup_until(cp); cp = Strsave(dp); printd = 1; return dgoto(cp); } /* * on login source of ~/.cshdirs, errors are eaten. the dir stack is all * directories we could get to. */ if (!bequiet) stderror(ERR_SYSTEM, short2str(cp), strerror(serrno)); cleanup_until(cp); return (NULL); }
int main(int argc, char **argv) { int i; char *p; FILE *fp = NULL; char *file; char *mode = "w"; int sortenv = 0; char *include = NULL; char **itab = NULL; pid_t pid = 0; int sig = SIGHUP; progname = strrchr(argv[0], '/'); if (progname) progname++; else progname = argv[0]; while ((i = getopt(argc, argv, "af:hi:k:s")) != EOF) switch (i) { case 'a': mode = "a"; break; case 'f': file = optarg; break; case 'h': printf("usage: %s [-ahsx] [-f FILE] [-i INCLUDELIST] [-k [@]PID[:SIG]] [ARGS...]\n", progname); return 0; case 's': sortenv = 1; break; case 'i': include = optarg; break; case 'k': read_pid_and_sig(optarg, &pid, &sig); break; default: return 1; } if (file) { fp = fopen(file, mode); if (!fp) { fprintf(stderr, "%s: ", progname); perror(file); return 1; } } else fp = stderr; fprintf(fp, "# Dump of execution environment\n"); p = agetcwd(); if (p) { fprintf(fp, "cwd is %s\n", p); free(p); } fprintf(fp, "# Arguments\n"); for (i = 0; i < argc; i++) fprintf(fp, "argv[%d]=%s\n", i, argv[i]); if (sortenv) { for (i = 0; environ[i]; i++); qsort(environ, i, sizeof(environ[0]), compenv); } if (include) { i = 1; for (p = include; *p; p++) { if (*p == ':') i++; } itab = calloc(i + 1, sizeof(itab)); itab[0] = include; for (p = include, i = 1; *p; p++) { if (*p == ':') { *p = 0; itab[i++] = p + 1; } } itab[i] = NULL; } fprintf(fp, "# Environment\n"); for (i = 0; environ[i]; i++) { if (!itab || locate(itab, environ[i])) fprintf(fp, "%s\n", environ[i]); } fprintf(fp, "# End\n"); if (pid) kill(pid, sig); return 0; }