summaryrefslogtreecommitdiffstats
path: root/src/LYLocal.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/LYLocal.c')
-rw-r--r--src/LYLocal.c2685
1 files changed, 2685 insertions, 0 deletions
diff --git a/src/LYLocal.c b/src/LYLocal.c
new file mode 100644
index 0000000..754a7d4
--- /dev/null
+++ b/src/LYLocal.c
@@ -0,0 +1,2685 @@
+/*
+ * $LynxId: LYLocal.c,v 1.135 2023/01/02 23:52:10 tom Exp $
+ *
+ * Routines to manipulate the local filesystem.
+ * Written by: Rick Mallett, Carleton University
+ * Report problems to rmallett@ccs.carleton.ca
+ * Modified 18-Dec-95 David Trueman (david@cs.dal.ca):
+ * Added OK_PERMIT compilation option.
+ * Support replacement of compiled-in f)ull menu configuration via
+ * DIRED_MENU definitions in lynx.cfg, so that more than one menu
+ * can be driven by the same executable.
+ * Modified Oct-96 Klaus Weide (kweide@tezcat.com):
+ * Changed to use the library's HTList_* functions and macros for
+ * managing the list of tagged file URLs.
+ * Keep track of proper level of URL escaping, so that unusual filenames
+ * which contain #% etc. are handled properly (some HTUnEscapeSome()'s
+ * left in to be conservative, and to document where superfluous
+ * unescaping took place before).
+ * Dynamic memory instead of fixed length buffers in a few cases.
+ * Other minor changes to make things work as intended.
+ * Modified Jun-97 Klaus Weide (kweide@tezcat.com) & FM:
+ * Modified the code handling DIRED_MENU to do more careful
+ * checking of the selected file. In addition to "TAG", "FILE", and
+ * "DIR", DIRED_MENU definitions in lynx.cfg now also recognize LINK as
+ * a type. DIRED_MENU definitions with a type field of "LINK" are only
+ * used if the current selection is a symbolic link ("FILE" and "DIR"
+ * definitions are not used in that case). The default menu
+ * definitions have been updated to reflect this change, and to avoid
+ * the showing of menu items whose action would always fail - KW
+ * Cast all code into the Lynx programming style. - FM
+ */
+
+#include <HTUtils.h>
+#include <HTAAProt.h>
+#include <HTFile.h>
+#include <HTAlert.h>
+#include <HTParse.h>
+#include <LYCurses.h>
+#include <LYGlobalDefs.h>
+#include <LYUtils.h>
+#include <LYStrings.h>
+#include <LYCharUtils.h>
+#include <LYStructs.h>
+#include <LYHistory.h>
+#include <LYUpload.h>
+#include <LYLocal.h>
+#include <LYClean.h>
+#include <www_wait.h>
+
+#ifdef SUPPORT_CHDIR
+#include <LYMainLoop.h>
+#endif
+
+#include <LYLeaks.h>
+
+#undef USE_COMPRESS
+
+#ifdef __DJGPP__
+#define EXT_TAR_GZ ".tgz"
+#define EXT_TAR_Z ".taz"
+#define EXT_Z ".z"
+#else
+#define EXT_TAR_GZ ".tar.gz"
+#define EXT_TAR_Z ".tar.Z"
+#define EXT_Z ".Z"
+#endif
+
+#ifndef DIRED_MAXBUF
+#define DIRED_MAXBUF 512
+#endif
+
+#ifdef DIRED_SUPPORT
+
+#ifdef OK_INSTALL
+#ifdef FNAMES_8_3
+#define INSTALLDIRS_FILE "instdirs.htm"
+#else
+#define INSTALLDIRS_FILE ".installdirs.html"
+#endif /* FNAMES_8_3 */
+#endif /* OK_INSTALL */
+
+static int get_filename(const char *prompt,
+ bstring **buf);
+
+#ifdef OK_PERMIT
+static int permit_location(char *destpath,
+ char *srcpath,
+ char **newpath);
+#endif /* OK_PERMIT */
+/* *INDENT-OFF* */
+static char *render_item ( const char * s,
+ const char * path,
+ const char * dir,
+ char * buf,
+ size_t bufsize,
+ int url_syntax);
+
+struct dired_menu {
+ int cond;
+#define DE_TAG 1
+#define DE_DIR 2
+#define DE_FILE 3
+#define DE_SYMLINK 4
+ char *sfx;
+ const char *c_sfx;
+ char *link;
+ const char *c_link;
+ char *rest;
+ const char *c_rest;
+ char *href;
+ const char *c_href;
+ struct dired_menu *next;
+};
+
+#define GetDiredSuffix(p) ((p)->sfx ? (p)->sfx : (p)->c_sfx)
+#define GetDiredLink(p) ((p)->link ? (p)->link : (p)->c_link)
+#define GetDiredRest(p) ((p)->rest ? (p)->rest : (p)->c_rest)
+#define GetDiredHref(p) ((p)->href ? (p)->href : (p)->c_href)
+
+#undef DATA
+#define DATA(cond, sfx, link, rest, href) { \
+ cond, \
+ NULL, sfx, \
+ NULL, link, \
+ NULL, rest, \
+ NULL, href, \
+ NULL }
+
+static struct dired_menu *menu_head = NULL;
+static struct dired_menu defmenu[] = {
+
+/*
+ * The following initializations determine the contents of the f)ull menu
+ * selection when in dired mode. If any menu entries are defined in the
+ * configuration file via DIRED_MENU lines, then these default entries are
+ * discarded entirely.
+ */
+#ifdef SUPPORT_CHDIR
+DATA( 0, "", "Change directory",
+ "", "LYNXDIRED://CHDIR"),
+#endif
+DATA( 0, "", "New File",
+"(in current directory)", "LYNXDIRED://NEW_FILE%d"),
+
+DATA( 0, "", "New Directory",
+"(in current directory)", "LYNXDIRED://NEW_FOLDER%d"),
+
+#ifdef OK_INSTALL
+DATA( DE_FILE, "", "Install",
+"selected file to new location", "LYNXDIRED://INSTALL_SRC%p"),
+/* The following (installing a directory) doesn't work for me, at least
+ with the "install" from GNU fileutils 4.0. I leave it in anyway, in
+ case one compiles with INSTALL_PATH / INSTALL_ARGS defined to some
+ other command for which it works (like a script, or maybe "cp -a"). - kw
+*/
+DATA( DE_DIR, "", "Install",
+"selected directory to new location", "LYNXDIRED://INSTALL_SRC%p"),
+#endif /* OK_INSTALL */
+
+DATA( DE_FILE, "", "Modify File Name",
+"(of current selection)", "LYNXDIRED://MODIFY_NAME%p"),
+DATA( DE_DIR, "", "Modify Directory Name",
+"(of current selection)", "LYNXDIRED://MODIFY_NAME%p"),
+#ifdef S_IFLNK
+DATA( DE_SYMLINK, "", "Modify Name",
+"(of selected symbolic link)", "LYNXDIRED://MODIFY_NAME%p"),
+#endif /* S_IFLNK */
+
+#ifdef OK_PERMIT
+DATA( DE_FILE, "", "Modify File Permissions",
+"(of current selection)", "LYNXDIRED://PERMIT_SRC%p"),
+DATA( DE_DIR, "", "Modify Directory Permissions",
+"(of current selection)", "LYNXDIRED://PERMIT_SRC%p"),
+#endif /* OK_PERMIT */
+
+DATA( DE_FILE, "", "Change Location",
+"(of selected file)" , "LYNXDIRED://MODIFY_LOCATION%p"),
+DATA( DE_DIR, "", "Change Location",
+"(of selected directory)", "LYNXDIRED://MODIFY_LOCATION%p"),
+#ifdef S_IFLNK
+DATA( DE_SYMLINK, "", "Change Location",
+"(of selected symbolic link)", "LYNXDIRED://MODIFY_LOCATION%p"),
+#endif /* S_IFLNK */
+
+DATA( DE_FILE, "", "Remove File",
+ "(current selection)", "LYNXDIRED://REMOVE_SINGLE%p"),
+DATA( DE_DIR, "", "Remove Directory",
+ "(current selection)", "LYNXDIRED://REMOVE_SINGLE%p"),
+#ifdef S_IFLNK
+DATA( DE_SYMLINK, "", "Remove Symbolic Link",
+ "(current selection)", "LYNXDIRED://REMOVE_SINGLE%p"),
+#endif /* S_IFLNK */
+
+#if defined(OK_UUDECODE) && !defined(ARCHIVE_ONLY)
+DATA( DE_FILE, "", "UUDecode",
+ "(current selection)", "LYNXDIRED://UUDECODE%p"),
+#endif /* OK_UUDECODE && !ARCHIVE_ONLY */
+
+#if defined(OK_TAR) && !defined(ARCHIVE_ONLY)
+DATA( DE_FILE, EXT_TAR_Z, "Expand",
+ "(current selection)", "LYNXDIRED://UNTAR_Z%p"),
+#endif /* OK_TAR && !ARCHIVE_ONLY */
+
+#if defined(OK_TAR) && defined(OK_GZIP) && !defined(ARCHIVE_ONLY)
+DATA( DE_FILE, ".tar.gz", "Expand",
+ "(current selection)", "LYNXDIRED://UNTAR_GZ%p"),
+
+DATA( DE_FILE, ".tgz", "Expand",
+ "(current selection)", "LYNXDIRED://UNTAR_GZ%p"),
+#endif /* OK_TAR && OK_GZIP && !ARCHIVE_ONLY */
+
+#ifndef ARCHIVE_ONLY
+DATA( DE_FILE, EXT_Z, "Uncompress",
+ "(current selection)", "LYNXDIRED://DECOMPRESS%p"),
+#endif /* ARCHIVE_ONLY */
+
+#if defined(OK_GZIP) && !defined(ARCHIVE_ONLY)
+DATA( DE_FILE, ".gz", "Uncompress",
+ "(current selection)", "LYNXDIRED://UNGZIP%p"),
+#endif /* OK_GZIP && !ARCHIVE_ONLY */
+
+#if defined(OK_ZIP) && !defined(ARCHIVE_ONLY)
+DATA( DE_FILE, ".zip", "Uncompress",
+ "(current selection)", "LYNXDIRED://UNZIP%p"),
+#endif /* OK_ZIP && !ARCHIVE_ONLY */
+
+#if defined(OK_TAR) && !defined(ARCHIVE_ONLY)
+DATA( DE_FILE, ".tar", "UnTar",
+ "(current selection)", "LYNXDIRED://UNTAR%p"),
+#endif /* OK_TAR && !ARCHIVE_ONLY */
+
+#ifdef OK_TAR
+DATA( DE_DIR, "", "Tar",
+ "(current selection)", "LYNXDIRED://TAR%p"),
+#endif /* OK_TAR */
+
+#if defined(OK_TAR) && defined(OK_GZIP)
+DATA( DE_DIR, "", "Tar and compress",
+ "(using GNU gzip)", "LYNXDIRED://TAR_GZ%p"),
+#endif /* OK_TAR && OK_GZIP */
+
+#if defined(OK_TAR) && defined(USE_COMPRESS)
+DATA( DE_DIR, "", "Tar and compress",
+ "(using compress)", "LYNXDIRED://TAR_Z%p"),
+#endif /* OK_TAR && USE_COMPRESS */
+
+#ifdef OK_ZIP
+DATA( DE_DIR, "", "Package and compress",
+ "(using zip)", "LYNXDIRED://ZIP%p"),
+#endif /* OK_ZIP */
+
+DATA( DE_FILE, "", "Compress",
+ "(using Unix compress)", "LYNXDIRED://COMPRESS%p"),
+
+#ifdef OK_GZIP
+DATA( DE_FILE, "", "Compress",
+ "(using gzip)", "LYNXDIRED://GZIP%p"),
+#endif /* OK_GZIP */
+
+#ifdef OK_ZIP
+DATA( DE_FILE, "", "Compress",
+ "(using zip)", "LYNXDIRED://ZIP%p"),
+#endif /* OK_ZIP */
+
+DATA( DE_TAG, "", "Move all tagged items to another location.",
+ "", "LYNXDIRED://MOVE_TAGGED%d"),
+
+#ifdef OK_INSTALL
+DATA( DE_TAG, "", "Install tagged files into another directory.",
+ "", "LYNXDIRED://INSTALL_SRC%00"),
+#endif
+
+DATA( DE_TAG, "", "Remove all tagged files and directories.",
+ "", "LYNXDIRED://REMOVE_TAGGED"),
+
+DATA( DE_TAG, "", "Untag all tagged files and directories.",
+ "", "LYNXDIRED://CLEAR_TAGGED"),
+
+DATA( 0, NULL, NULL,
+ NULL, NULL),
+};
+#undef DATA
+/* *INDENT-ON* */
+
+static BOOLEAN cannot_stat(const char *name)
+{
+ char *tmpbuf = 0;
+
+ HTSprintf0(&tmpbuf, gettext("Unable to get status of '%s'."), name);
+ HTAlert(tmpbuf);
+ FREE(tmpbuf);
+ return FALSE;
+}
+
+#define OK_STAT(name, sb) (stat(name, sb) == 0)
+
+static BOOLEAN ok_stat(const char *name, struct stat *sb)
+{
+ BOOLEAN rc = TRUE;
+
+ CTRACE((tfp, "testing ok_stat(%s)\n", name));
+ if (!OK_STAT(name, sb)) {
+#ifdef DOSPATH
+ size_t len = strlen(name);
+
+ /*
+ * If a path ends with '\' or ':', we can guess that it may be
+ * a directory name. Adding a '.' (after a '\') will produce a
+ * pathname that stat() will accept as a directory name.
+ */
+ if (len != 0 && (name[len - 1] == '\\' || name[len - 1] == ':')) {
+ char *temp = malloc(len + 3);
+
+ if (temp != 0) {
+ strcpy(temp, name);
+ if (temp[len - 1] == '\\') {
+ strcpy(temp + len, ".");
+ } else {
+ strcpy(temp + len, "\\.");
+ }
+ rc = OK_STAT(temp, sb);
+ free(temp);
+ } else {
+ rc = FALSE;
+ }
+ } else
+#endif
+ rc = FALSE;
+ }
+
+ if (rc == FALSE)
+ rc = cannot_stat(name);
+
+ return rc;
+}
+
+#ifdef HAVE_LSTAT
+static BOOLEAN ok_lstat(char *name, struct stat *sb)
+{
+ CTRACE((tfp, "testing ok_lstat(%s)\n", name));
+ if (lstat(name, sb) < 0) {
+ return cannot_stat(name);
+ }
+ return TRUE;
+}
+#else
+#define ok_lstat(name,sb) ok_stat(name,sb)
+#endif
+
+static BOOLEAN ok_file_or_dir(struct stat *sb)
+{
+ if (!S_ISDIR(sb->st_mode)
+ && !S_ISREG(sb->st_mode)) {
+ HTAlert(gettext("The selected item is not a file or a directory! Request ignored."));
+ return FALSE;
+ }
+ return TRUE;
+}
+
+#ifdef OK_INSTALL /* currently only used in local_install */
+static BOOLEAN ok_localname(char *dst, const char *src)
+{
+ struct stat dir_info;
+
+ if (!ok_stat(src, &dir_info)
+ || !ok_file_or_dir(&dir_info)) {
+ return FALSE;
+ }
+ if (strlen(src) >= DIRED_MAXBUF) {
+ CTRACE((tfp, "filename too long in ok_localname!\n"));
+ return FALSE;
+ }
+ strcpy(dst, src);
+ return TRUE;
+}
+#endif /* OK_INSTALL */
+
+#define MAX_ARGC 10
+
+static char **make_argv(const char *command, ...)
+{
+ static char *result[MAX_ARGC];
+ int argc = 0;
+ char *value;
+ va_list ap;
+
+ va_start(ap, command);
+ result[0] = 0;
+ StrAllocCopy(result[argc++], command);
+ do {
+ result[argc] = 0;
+ value = (char *) va_arg(ap, char *);
+
+ if (value != 0)
+ StrAllocCopy(result[argc], value);
+ } while (result[argc++] != 0);
+ va_end(ap);
+
+ return result;
+}
+
+static void free_argv(char **argv)
+{
+ int argc;
+
+ for (argc = 0; argv[argc] != 0; ++argc) {
+ free(argv[argc]);
+ }
+}
+
+/*
+ * Execute DIRED command, return -1 or 0 on failure, 1 success.
+ */
+static int LYExecv(const char *path,
+ char **argv,
+ char *msg)
+{
+ int rc = 0;
+
+#if defined(VMS)
+ CTRACE((tfp, "LYExecv: Called inappropriately! (path=%s)\n", path));
+#else
+ int n;
+ char *tmpbuf = 0;
+
+#if defined(__DJGPP__) || defined(_WINDOWS)
+ (void) msg;
+ stop_curses();
+ HTSprintf0(&tmpbuf, "%s", path);
+ for (n = 1; argv[n] != 0; n++)
+ HTSprintf(&tmpbuf, " %s", argv[n]);
+ HTSprintf(&tmpbuf, "\n");
+ rc = LYSystem(tmpbuf) ? 0 : 1;
+#else
+ int pid;
+
+#ifdef HAVE_TYPE_UNIONWAIT
+ union wait wstatus;
+
+#else
+ int wstatus;
+#endif
+
+ if (TRACE) {
+ CTRACE((tfp, "LYExecv path='%s'\n", path));
+ for (n = 0; argv[n] != 0; n++)
+ CTRACE((tfp, "argv[%d] = '%s'\n", n, argv[n]));
+ }
+
+ rc = 1; /* It will work */
+ stop_curses();
+ pid = fork(); /* fork and execute command */
+
+ switch (pid) {
+ case -1:
+ HTSprintf0(&tmpbuf, gettext("Unable to %s due to system error!"), msg);
+ rc = 0;
+ break; /* don't fall thru! - KW */
+
+ case 0: /* child */
+#ifdef USE_EXECVP
+ execvp(path, argv); /* this uses our $PATH */
+#else
+ execv(path, argv);
+#endif
+ exit(EXIT_FAILURE); /* execv failed, give wait() something to look at */
+ /*NOTREACHED */
+
+ default: /* parent */
+#ifndef HAVE_WAITPID
+ while (wait(&wstatus) != pid) ; /* do nothing */
+#else
+ while (-1 == waitpid(pid, &wstatus, 0)) { /* wait for child */
+#ifdef EINTR
+ if (errno == EINTR)
+ continue;
+#endif /* EINTR */
+#ifdef ERESTARTSYS
+ if (errno == ERESTARTSYS)
+ continue;
+#endif /* ERESTARTSYS */
+ break;
+ }
+#endif /* !HAVE_WAITPID */
+ if ((WIFEXITED(wstatus)
+ && (WEXITSTATUS(wstatus) != 0))
+ || (WIFSIGNALED(wstatus)
+ && (WTERMSIG(wstatus) > 0))) { /* error return */
+ HTSprintf0(&tmpbuf,
+ gettext("Probable failure to %s due to system error!"),
+ msg);
+ rc = 0;
+ }
+ }
+#endif /* __DJGPP__ */
+
+ if (rc == 0) {
+ /*
+ * Screen may have message from the failed execv'd command. Give user
+ * time to look at it before screen refresh.
+ */
+ LYSleepAlert();
+ }
+ start_curses();
+ if (tmpbuf != 0) {
+ if (rc == 0)
+ HTAlert(tmpbuf);
+ FREE(tmpbuf);
+ }
+#endif /* VMS || _WINDOWS */
+ CTRACE((tfp, "LYexecv ->%d\n", rc));
+ return (rc);
+}
+
+static int make_directory(char *path)
+{
+ int code;
+ const char *program;
+
+ if ((program = HTGetProgramPath(ppMKDIR)) != NULL) {
+ char **args;
+ char *msg = 0;
+
+ HTSprintf0(&msg, "make directory %s", path);
+ args = make_argv("mkdir",
+ path,
+ NULL);
+ code = (LYExecv(program, args, msg) <= 0) ? -1 : 1;
+ FREE(msg);
+ free_argv(args);
+ } else {
+#ifdef _WINDOWS
+ code = mkdir(path) ? -1 : 1;
+#else
+ code = mkdir(path, 0777) ? -1 : 1;
+#endif
+ CTRACE((tfp, "builtin mkdir ->%d\n\t%s\n", code, path));
+ }
+ return (code);
+}
+
+static int remove_file(char *path)
+{
+ int code;
+ const char *program;
+
+ if ((program = HTGetProgramPath(ppRM)) != NULL) {
+ char **args;
+ char *tmpbuf = NULL;
+
+ args = make_argv("rm",
+ "-f",
+ path,
+ NULL);
+ HTSprintf0(&tmpbuf, gettext("remove %s"), path);
+ code = LYExecv(program, args, tmpbuf);
+ FREE(tmpbuf);
+ free_argv(args);
+ } else {
+ code = remove(path) ? -1 : 1;
+ CTRACE((tfp, "builtin remove ->%d\n\t%s\n", code, path));
+ }
+ return (code);
+}
+
+static int remove_directory(char *path)
+{
+ int code;
+ const char *program;
+
+ if ((program = HTGetProgramPath(ppRMDIR)) != NULL) {
+ char **args;
+ char *tmpbuf = NULL;
+
+ args = make_argv("rmdir",
+ path,
+ NULL);
+ HTSprintf0(&tmpbuf, gettext("remove %s"), path);
+ code = LYExecv(program, args, tmpbuf);
+ FREE(tmpbuf);
+ free_argv(args);
+ } else {
+ code = rmdir(path) ? -1 : 1;
+ CTRACE((tfp, "builtin rmdir ->%d\n\t%s\n", code, path));
+ }
+ return (code);
+}
+
+static int touch_file(char *path)
+{
+ int code;
+ const char *program;
+
+ if ((program = HTGetProgramPath(ppTOUCH)) != NULL) {
+ char **args;
+ char *msg = NULL;
+
+ HTSprintf0(&msg, gettext("touch %s"), path);
+ args = make_argv("touch",
+ path,
+ NULL);
+ code = (LYExecv(program, args, msg) <= 0) ? -1 : 1;
+ FREE(msg);
+ free_argv(args);
+ } else {
+ FILE *fp;
+
+ if ((fp = fopen(path, BIN_W)) != 0) {
+ fclose(fp);
+ code = 1;
+ } else {
+ code = -1;
+ }
+ CTRACE((tfp, "builtin touch ->%d\n\t%s\n", code, path));
+ }
+ return (code);
+}
+
+static int move_file(char *source, char *target)
+{
+ int code;
+ const char *program;
+
+ if ((program = HTGetProgramPath(ppMV)) != NULL) {
+ char *msg = 0;
+ char **args;
+
+ HTSprintf0(&msg, gettext("move %s to %s"), source, target);
+ args = make_argv("mv",
+ source,
+ target,
+ NULL);
+ code = (LYExecv(program, args, msg) <= 0) ? -1 : 1;
+ FREE(msg);
+ free_argv(args);
+ } else {
+ struct stat sb;
+ char *actual = 0;
+
+ /* the caller sets up a target directory; we need a file path */
+ if (stat(target, &sb) == 0
+ && S_ISDIR(sb.st_mode)) {
+ HTSprintf0(&actual, "%s/%s", target, LYPathLeaf(source));
+ CTRACE((tfp, "move_file source=%s, target=%s\n", source, target));
+ target = actual;
+ }
+ code = rename(source, target);
+ CTRACE((tfp, "builtin move ->%d\n\tsource=%s\n\ttarget=%s\n",
+ code, source, target));
+ if (code != 0) { /* it failed */
+ if ((code = LYCopyFile(source, target)) >= 0) {
+ code = remove(source);
+ CTRACE((tfp, "...remove source after copying ->%d\n", code));
+ }
+ }
+ if (code == 0)
+ code = 1;
+ if (actual != target) {
+ FREE(actual);
+ }
+ }
+ return code;
+}
+
+static BOOLEAN not_already_exists(char *name)
+{
+ struct stat dir_info;
+
+ if (!OK_STAT(name, &dir_info)) {
+ if (errno != ENOENT) {
+ cannot_stat(name);
+ } else {
+ return TRUE;
+ }
+ } else if (S_ISDIR(dir_info.st_mode)) {
+ HTAlert(gettext("There is already a directory with that name! Request ignored."));
+ } else if (S_ISREG(dir_info.st_mode)) {
+ HTAlert(gettext("There is already a file with that name! Request ignored."));
+ } else {
+ HTAlert(gettext("The specified name is already in use! Request ignored."));
+ }
+ return FALSE;
+}
+
+static BOOLEAN dir_has_same_owner(struct stat *dst_info,
+ struct stat *src_info)
+{
+ if (S_ISDIR(dst_info->st_mode)) {
+ if (dst_info->st_uid == src_info->st_uid) {
+ return TRUE;
+ } else {
+ HTAlert(gettext("Destination has different owner! Request denied."));
+ }
+ } else {
+ HTAlert(gettext("Destination is not a valid directory! Request denied."));
+ }
+ return FALSE;
+}
+
+/*
+ * Make sure the source and target are not the same location.
+ */
+static BOOLEAN same_location(struct stat *dst_info,
+ struct stat *src_info)
+{
+ BOOLEAN result = FALSE;
+
+#ifdef UNIX
+ if (src_info->st_dev == dst_info->st_dev &&
+ src_info->st_ino == dst_info->st_ino) {
+ HTAlert(gettext("Source and destination are the same location! Request ignored!"));
+ result = TRUE;
+ }
+#endif
+ return result;
+}
+
+/*
+ * Remove all tagged files and directories.
+ */
+static int remove_tagged(void)
+{
+ int ans;
+ BOOL will_clear = TRUE;
+ char *cp;
+ char *tmpbuf = NULL;
+ char *testpath = NULL;
+ struct stat dir_info;
+ int count;
+ HTList *tag;
+
+ if (HTList_isEmpty(tagged)) /* should never happen */
+ return 0;
+
+ ans = HTConfirm(gettext("Remove all tagged files and directories?"));
+
+ count = 0;
+ tag = tagged;
+ while (ans == YES && (cp = (char *) HTList_nextObject(tag)) != NULL) {
+ if (is_url(cp) == FILE_URL_TYPE) { /* unnecessary check */
+ testpath = HTfullURL_toFile(cp);
+ LYTrimPathSep(testpath);
+ will_clear = TRUE;
+
+ /*
+ * Check the current status of the path to be deleted.
+ */
+ if (!ok_stat(testpath, &dir_info)) {
+ will_clear = FALSE;
+ break;
+ } else {
+ if (remove_file(testpath) <= 0) {
+ if (count == 0)
+ count = -1;
+ will_clear = FALSE;
+ break;
+ }
+ ++count;
+ FREE(testpath);
+ }
+ }
+ }
+ FREE(testpath);
+ FREE(tmpbuf);
+ if (will_clear)
+ clear_tags();
+ return count;
+}
+
+static char *parse_directory(char *path)
+{
+ char *result;
+
+ if (path) {
+ path = strip_trailing_slash(path);
+ path = HTParse(".", path, PARSE_PATH + PARSE_PUNCTUATION);
+ result = HTURLPath_toFile(path, TRUE, FALSE);
+ FREE(path);
+ } else { /* Last resort, should never happen. */
+ result = HTURLPath_toFile(".", TRUE, FALSE);
+ }
+ return result;
+}
+
+/*
+ * Move all tagged files and directories to a new location.
+ *
+ * The 'testpath' parameter is the current location, used for resolving
+ * relative target specifications.
+ */
+static int modify_tagged(char *testpath)
+{
+ char *cp;
+ bstring *given_target = NULL;
+ char *dst_path = NULL;
+ char *src_path = NULL;
+ char *old_path = NULL;
+ struct stat src_info;
+ struct stat dst_info;
+ int count = 0;
+ HTList *tag;
+
+ CTRACE((tfp, "modify_tagged(%s)\n", testpath));
+
+ if (HTList_isEmpty(tagged)) /* should never happen */
+ return 0;
+
+ _statusline(gettext("Enter new location for tagged items: "));
+
+ BStrCopy0(given_target, "");
+ (void) LYgetBString(&given_target, FALSE, 0, NORECALL);
+ if (!isBEmpty(given_target)) {
+ /*
+ * Replace ~/ references to the home directory.
+ */
+ if (LYIsTilde(given_target->str[0]) && LYIsPathSep(given_target->str[1])) {
+ char *cp1 = NULL;
+
+ StrAllocCopy(cp1, Home_Dir());
+ StrAllocCat(cp1, (given_target->str + 1));
+ BStrCopy0(given_target, cp1);
+ FREE(cp1);
+ }
+
+ /*
+ * If path is relative, prefix it with current location.
+ */
+ if (!LYIsPathSep(given_target->str[0])) {
+ dst_path = HTLocalName(testpath);
+ LYAddPathSep(&dst_path);
+ StrAllocCat(dst_path, given_target->str);
+ } else {
+ dst_path = HTLocalName(given_target->str);
+ }
+
+ if (!ok_stat(dst_path, &dst_info)) {
+ FREE(dst_path);
+ BStrFree(given_target);
+ return 0;
+ }
+
+ /*
+ * Determine the ownership of the current location, using the directory
+ * containing the file or subdir from each of the tagged files.
+ */
+ for (tag = tagged; (cp = (char *) HTList_nextObject(tag)) != NULL;) {
+ src_path = parse_directory(cp);
+
+ if (isEmpty(old_path) || strcmp(old_path, src_path)) {
+ if (!ok_stat(src_path, &src_info)
+ || same_location(&dst_info, &src_info)
+ || !dir_has_same_owner(&dst_info, &src_info)) {
+ FREE(src_path);
+ BStrFree(given_target);
+ return 0;
+ }
+ }
+ StrAllocCopy(old_path, src_path);
+ FREE(src_path);
+ }
+
+ /*
+ * Move all tagged items to the target location.
+ */
+ for (tag = tagged; (cp = (char *) HTList_nextObject(tag)) != NULL;) {
+ src_path = HTfullURL_toFile(cp);
+
+ if (move_file(src_path, dst_path) < 0) {
+ if (count == 0)
+ count = -1;
+ break;
+ }
+ FREE(src_path);
+ ++count;
+ }
+ clear_tags();
+ FREE(src_path);
+ FREE(dst_path);
+ }
+ BStrFree(given_target);
+ return count;
+}
+
+/*
+ * Modify the name of the specified item.
+ */
+static int modify_name(char *testpath)
+{
+ const char *cp;
+ bstring *tmpbuf = NULL;
+ char *newpath = NULL;
+ struct stat dir_info;
+ int code = 0;
+
+ /*
+ * Determine the status of the selected item.
+ */
+ testpath = strip_trailing_slash(testpath);
+
+ if (ok_stat(testpath, &dir_info)) {
+
+ /*
+ * Change the name of the file or directory.
+ */
+ if (S_ISDIR(dir_info.st_mode)) {
+ cp = gettext("Enter new name for directory: ");
+ } else if (S_ISREG(dir_info.st_mode)) {
+ cp = gettext("Enter new name for file: ");
+ } else {
+ return ok_file_or_dir(&dir_info);
+ }
+
+ BStrCopy0(tmpbuf, LYPathLeaf(testpath));
+ if (get_filename(cp, &tmpbuf)) {
+
+ /*
+ * Do not allow the user to also change the location at this time.
+ */
+ if (LYLastPathSep(tmpbuf->str) != 0) {
+ HTAlert(gettext("Illegal character (path-separator) found! Request ignored."));
+ } else if (strlen(tmpbuf->str)) {
+ if ((cp = LYLastPathSep(testpath)) != NULL) {
+ HTSprintf0(&newpath, "%.*s%s",
+ (int) (cp - testpath + 1),
+ testpath, tmpbuf->str);
+ } else {
+ StrAllocCopy(newpath, tmpbuf->str);
+ }
+
+ /*
+ * Make sure the destination does not already exist.
+ */
+ if (not_already_exists(newpath)) {
+ code = move_file(testpath, newpath);
+ }
+ FREE(newpath);
+ }
+ }
+ }
+ BStrFree(tmpbuf);
+ return code;
+}
+
+/*
+ * Change the location of a file or directory.
+ */
+static int modify_location(char *testpath)
+{
+ const char *cp;
+ char *sp;
+ bstring *tmpbuf = NULL;
+ char *newpath = NULL;
+ char *savepath = NULL;
+ struct stat old_info;
+ struct stat dir_info;
+ int code = 0;
+
+ /*
+ * Determine the status of the selected item.
+ */
+ testpath = strip_trailing_slash(testpath);
+ if (!ok_stat(testpath, &dir_info)) {
+ return 0;
+ }
+
+ /*
+ * Change the location of the file or directory.
+ */
+ if (S_ISDIR(dir_info.st_mode)) {
+ cp = gettext("Enter new location for directory: ");
+ } else if (S_ISREG(dir_info.st_mode)) {
+ cp = gettext("Enter new location for file: ");
+ } else {
+ return ok_file_or_dir(&dir_info);
+ }
+
+ BStrCopy0(tmpbuf, testpath);
+ *LYPathLeaf(tmpbuf->str) = '\0';
+ if (get_filename(cp, &tmpbuf)) {
+ if (strlen(tmpbuf->str)) {
+ StrAllocCopy(savepath, testpath);
+ StrAllocCopy(newpath, testpath);
+
+ /*
+ * Allow ~/ references to the home directory.
+ */
+ if (LYIsTilde(tmpbuf->str[0])
+ && (tmpbuf->str[1] == '\0' || LYIsPathSep(tmpbuf->str[1]))) {
+ StrAllocCopy(newpath, Home_Dir());
+ StrAllocCat(newpath, (tmpbuf->str + 1));
+ BStrCopy0(tmpbuf, newpath);
+ }
+ if (LYisAbsPath(tmpbuf->str)) {
+ StrAllocCopy(newpath, tmpbuf->str);
+ } else if ((sp = LYLastPathSep(newpath)) != NULL) {
+ *++sp = '\0';
+ StrAllocCat(newpath, tmpbuf->str);
+ } else {
+ HTAlert(gettext("Unexpected failure - unable to find trailing path separator"));
+ FREE(newpath);
+ FREE(savepath);
+ BStrFree(tmpbuf);
+ return 0;
+ }
+
+ /*
+ * Make sure the source and target have the same owner (uid).
+ */
+ old_info = dir_info;
+ if (!ok_stat(newpath, &dir_info)) {
+ code = 0;
+ } else if (same_location(&old_info, &dir_info)) {
+ code = 0;
+ } else if (dir_has_same_owner(&dir_info, &old_info)) {
+ code = move_file(savepath, newpath);
+ }
+ FREE(newpath);
+ FREE(savepath);
+ }
+ }
+ BStrFree(tmpbuf);
+ return code;
+}
+
+/*
+ * Modify name or location of a file or directory on localhost.
+ */
+int local_modify(DocInfo *doc, char **newpath)
+{
+ int ans;
+ char *cp;
+ bstring *testpath = NULL;
+ int count;
+ int code = 0;
+
+ if (!HTList_isEmpty(tagged)) {
+ cp = HTpartURL_toFile(doc->address);
+
+ count = modify_tagged(cp);
+ FREE(cp);
+
+ if (doc->link > (nlinks - count - 1))
+ doc->link = (nlinks - count - 1);
+ doc->link = ((doc->link < 0)
+ ? 0
+ : doc->link);
+
+ return count;
+ } else if (doc->link < 0 || doc->link > nlinks) {
+ /*
+ * Added protection.
+ */
+ return 0;
+ }
+
+ /*
+ * Do not allow simultaneous change of name and location as in Unix. This
+ * reduces functionality but reduces difficulty for the novice.
+ */
+#ifdef OK_PERMIT
+ _statusline(gettext("Modify name, location, or permission (n, l, or p): "));
+#else
+ _statusline(gettext("Modify name or location (n or l): "));
+#endif /* OK_PERMIT */
+ ans = LYgetch_single();
+
+ if (StrChr("NLP", ans) != NULL) {
+ cp = HTfullURL_toFile(links[doc->link].lname);
+ if (strlen(cp) >= DIRED_MAXBUF) {
+ FREE(cp);
+ return 0;
+ }
+ BStrCopy0(testpath, cp);
+ FREE(cp);
+
+ if (ans == 'N') {
+ code = modify_name(testpath->str);
+ } else if (ans == 'L') {
+ if (modify_location(testpath->str)) {
+ if (doc->link == (nlinks - 1))
+ --doc->link;
+ code = 1;
+ }
+#ifdef OK_PERMIT
+ } else if (ans == 'P') {
+ code = permit_location(NULL, testpath->str, newpath);
+#endif /* OK_PERMIT */
+ } else {
+ /*
+ * Code for changing ownership needed here.
+ */
+ HTAlert(gettext("This feature not yet implemented!"));
+ }
+ }
+ BStrFree(testpath);
+ return code;
+}
+
+#define BadChars() ((!no_dotfiles && show_dotfiles) \
+ ? "~/" \
+ : ".~/")
+
+/*
+ * Create a new empty file in the current directory.
+ */
+static int create_file(char *current_location)
+{
+ int code = FALSE;
+ bstring *tmpbuf = NULL;
+ char *testpath = NULL;
+
+ BStrCopy0(tmpbuf, "");
+ if (get_filename(gettext("Enter name of file to create: "), &tmpbuf)) {
+
+ if (strstr(tmpbuf->str, "//") != NULL) {
+ HTAlert(gettext("Illegal redirection \"//\" found! Request ignored."));
+ } else if (strlen(tmpbuf->str) &&
+ StrChr(BadChars(), tmpbuf->str[0]) == NULL) {
+ StrAllocCopy(testpath, current_location);
+ LYAddPathSep(&testpath);
+
+ /*
+ * Append the target filename to the current location.
+ */
+ StrAllocCat(testpath, tmpbuf->str);
+
+ /*
+ * Make sure the target does not already exist
+ */
+ if (not_already_exists(testpath)) {
+ code = touch_file(testpath);
+ }
+ FREE(testpath);
+ }
+ }
+ BStrFree(tmpbuf);
+ return code;
+}
+
+/*
+ * Create a new directory in the current directory.
+ */
+static int create_directory(char *current_location)
+{
+ int code = FALSE;
+ bstring *tmpbuf = NULL;
+ char *testpath = NULL;
+
+ BStrCopy0(tmpbuf, "");
+ if (get_filename(gettext("Enter name for new directory: "), &tmpbuf)) {
+
+ if (strstr(tmpbuf->str, "//") != NULL) {
+ HTAlert(gettext("Illegal redirection \"//\" found! Request ignored."));
+ } else if (strlen(tmpbuf->str) &&
+ StrChr(BadChars(), tmpbuf->str[0]) == NULL) {
+ StrAllocCopy(testpath, current_location);
+ LYAddPathSep(&testpath);
+
+ StrAllocCat(testpath, tmpbuf->str);
+
+ /*
+ * Make sure the target does not already exist.
+ */
+ if (not_already_exists(testpath)) {
+ code = make_directory(testpath);
+ }
+ FREE(testpath);
+ }
+ }
+ BStrFree(tmpbuf);
+ return code;
+}
+
+/*
+ * Create a file or a directory at the current location.
+ */
+int local_create(DocInfo *doc)
+{
+ int ans;
+ char *cp;
+ char testpath[DIRED_MAXBUF];
+
+ cp = HTfullURL_toFile(doc->address);
+ if (strlen(cp) >= DIRED_MAXBUF) {
+ FREE(cp);
+ return 0;
+ }
+ strcpy(testpath, cp);
+ FREE(cp);
+
+ _statusline(gettext("Create file or directory (f or d): "));
+ ans = LYgetch_single();
+
+ if (ans == 'F') {
+ return (create_file(testpath));
+ } else if (ans == 'D') {
+ return (create_directory(testpath));
+ } else {
+ return 0;
+ }
+}
+
+/*
+ * Remove a single file or directory.
+ */
+static int remove_single(char *testpath)
+{
+ int code = 0;
+ char *cp;
+ char *tmpbuf = 0;
+ struct stat dir_info;
+ BOOL is_directory = FALSE;
+
+ if (!ok_lstat(testpath, &dir_info)) {
+ return 0;
+ }
+
+ /*
+ * Locate the filename portion of the path.
+ */
+ if ((cp = LYLastPathSep(testpath)) != NULL) {
+ ++cp;
+ } else {
+ cp = testpath;
+ }
+ if (S_ISDIR(dir_info.st_mode)) {
+ /*
+ * This strlen stuff will probably screw up intl translations. Course,
+ * it's probably broken for screen sizes other 80, too -jes
+ */
+ if (strlen(cp) < 37) {
+ HTSprintf0(&tmpbuf,
+ gettext("Remove directory '%s'?"), cp);
+ } else {
+ HTSprintf0(&tmpbuf,
+ gettext("Remove directory?"));
+ }
+ is_directory = TRUE;
+ } else if (S_ISREG(dir_info.st_mode)) {
+ if (strlen(cp) < 60) {
+ HTSprintf0(&tmpbuf, gettext("Remove file '%s'?"), cp);
+ } else {
+ HTSprintf0(&tmpbuf, gettext("Remove file?"));
+ }
+#ifdef S_IFLNK
+ } else if (S_ISLNK(dir_info.st_mode)) {
+ if (strlen(cp) < 50) {
+ HTSprintf0(&tmpbuf, gettext("Remove symbolic link '%s'?"), cp);
+ } else {
+ HTSprintf0(&tmpbuf, gettext("Remove symbolic link?"));
+ }
+#endif
+ } else {
+ cannot_stat(testpath);
+ FREE(tmpbuf);
+ return 0;
+ }
+
+ if (HTConfirm(tmpbuf) == YES) {
+ code = is_directory
+ ? remove_directory(testpath)
+ : remove_file(testpath);
+ }
+ FREE(tmpbuf);
+ return code;
+}
+
+/*
+ * Remove a file or a directory.
+ */
+int local_remove(DocInfo *doc)
+{
+ char *cp, *tp;
+ char testpath[DIRED_MAXBUF];
+ int count, i;
+
+ if (!HTList_isEmpty(tagged)) {
+ count = remove_tagged();
+ if (doc->link > (nlinks - count - 1))
+ doc->link = (nlinks - count - 1);
+ doc->link = ((doc->link < 0)
+ ? 0
+ : doc->link);
+ return count;
+ } else if (doc->link < 0 || doc->link > nlinks) {
+ return 0;
+ }
+ cp = links[doc->link].lname;
+ if (is_url(cp) == FILE_URL_TYPE) {
+ tp = HTfullURL_toFile(cp);
+ if (strlen(tp) >= DIRED_MAXBUF) {
+ FREE(tp);
+ return 0;
+ }
+ strcpy(testpath, tp);
+ FREE(tp);
+
+ if ((i = (int) strlen(testpath)) && testpath[i - 1] == '/')
+ testpath[(i - 1)] = '\0';
+
+ if (remove_single(testpath)) {
+ if (doc->link == (nlinks - 1))
+ --doc->link;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+#ifdef OK_PERMIT
+
+static bstring *LYValidPermitFile = NULL;
+
+static long permit_bits(char *string_mode)
+{
+ if (!strcmp(string_mode, "IRUSR"))
+ return S_IRUSR;
+ if (!strcmp(string_mode, "IWUSR"))
+ return S_IWUSR;
+ if (!strcmp(string_mode, "IXUSR"))
+ return S_IXUSR;
+ if (!strcmp(string_mode, "IRGRP"))
+ return S_IRGRP;
+ if (!strcmp(string_mode, "IWGRP"))
+ return S_IWGRP;
+ if (!strcmp(string_mode, "IXGRP"))
+ return S_IXGRP;
+ if (!strcmp(string_mode, "IROTH"))
+ return S_IROTH;
+ if (!strcmp(string_mode, "IWOTH"))
+ return S_IWOTH;
+ if (!strcmp(string_mode, "IXOTH"))
+ return S_IXOTH;
+ /* Don't include setuid and friends; use shell access for that. */
+ return 0;
+}
+
+/*
+ * Handle DIRED permissions.
+ */
+static int permit_location(char *destpath,
+ char *srcpath,
+ char **newpath)
+{
+ int code = 0;
+
+#ifndef UNIX
+ HTAlert(gettext("Sorry, don't know how to permit non-UNIX files yet."));
+#else
+ static char tempfile[LY_MAXPATH] = "\0";
+ char *cp;
+ char tmpdst[LY_MAXPATH];
+ struct stat dir_info;
+ const char *program;
+
+ if (srcpath) {
+ /*
+ * Create form.
+ */
+ FILE *fp0;
+ char *user_filename;
+ const char *group_name;
+
+ srcpath = strip_trailing_slash(srcpath);
+
+ /*
+ * A couple of sanity tests.
+ */
+ if (!ok_lstat(srcpath, &dir_info)
+ || !ok_file_or_dir(&dir_info))
+ return code;
+
+ user_filename = LYPathLeaf(srcpath);
+
+ (void) LYRemoveTemp(tempfile);
+ if ((fp0 = LYOpenTemp(tempfile, HTML_SUFFIX, "w")) == NULL) {
+ HTAlert(gettext("Unable to open permit options file"));
+ return (code);
+ }
+
+ /*
+ * Make the tempfile a URL.
+ */
+ LYLocalFileToURL(newpath, tempfile);
+ LYRegisterUIPage(*newpath, UIP_PERMIT_OPTIONS);
+
+ group_name = HTAA_GidToName((int) dir_info.st_gid);
+ BStrCopy0(LYValidPermitFile, srcpath);
+
+ fprintf(fp0, "<Html><Head>\n<Title>%s</Title>\n</Head>\n<Body>\n",
+ PERMIT_OPTIONS_TITLE);
+ fprintf(fp0, "<H1>%s%s</H1>\n", PERMISSIONS_SEGMENT, user_filename);
+ {
+ /*
+ * Prevent filenames which include '#' or '?' from messing it up.
+ */
+ char *srcpath_url = HTEscape(srcpath, URL_PATH);
+
+ fprintf(fp0, "<Form Action=\"%s//PERMIT_LOCATION%s\">\n",
+ STR_LYNXDIRED, srcpath_url);
+ FREE(srcpath_url);
+ }
+
+ fprintf(fp0, "<Ol><Li>%s<Br><Br>\n",
+ gettext("Specify permissions below:"));
+ fprintf(fp0, "%s:<Br>\n", gettext("Owner:"));
+ fprintf(fp0,
+ "<Input Type=\"checkbox\" Name=\"mode\" Value=\"IRUSR\" %s> Read<Br>\n",
+ (dir_info.st_mode & S_IRUSR) ? "checked" : "");
+ fprintf(fp0,
+ "<Input Type=\"checkbox\" Name=\"mode\" Value=\"IWUSR\" %s> Write<Br>\n",
+ (dir_info.st_mode & S_IWUSR) ? "checked" : "");
+ /*
+ * If restricted, only change eXecute permissions on directories.
+ */
+ if (!no_change_exec_perms || S_ISDIR(dir_info.st_mode))
+ fprintf(fp0,
+ "<Input Type=\"checkbox\" Name=\"mode\" Value=\"IXUSR\" %s> %s<Br>\n",
+ (dir_info.st_mode & S_IXUSR) ? "checked" : "",
+ S_ISDIR(dir_info.st_mode) ? "Search" : "Execute");
+
+ fprintf(fp0, "%s %s:<Br>\n", gettext("Group"), group_name);
+ fprintf(fp0,
+ "<Input Type=\"checkbox\" Name=\"mode\" Value=\"IRGRP\" %s> Read<Br>\n",
+ (dir_info.st_mode & S_IRGRP) ? "checked" : "");
+ fprintf(fp0,
+ "<Input Type=\"checkbox\" Name=\"mode\" Value=\"IWGRP\" %s> Write<Br>\n",
+ (dir_info.st_mode & S_IWGRP) ? "checked" : "");
+ /*
+ * If restricted, only change eXecute permissions on directories.
+ */
+ if (!no_change_exec_perms || S_ISDIR(dir_info.st_mode))
+ fprintf(fp0,
+ "<Input Type=\"checkbox\" Name=\"mode\" Value=\"IXGRP\" %s> %s<Br>\n",
+ (dir_info.st_mode & S_IXGRP) ? "checked" : "",
+ S_ISDIR(dir_info.st_mode) ? "Search" : "Execute");
+
+ fprintf(fp0, "%s<Br>\n", gettext("Others:"));
+ fprintf(fp0,
+ "<Input Type=\"checkbox\" Name=\"mode\" Value=\"IROTH\" %s> Read<Br>\n",
+ (dir_info.st_mode & S_IROTH) ? "checked" : "");
+ fprintf(fp0,
+ "<Input Type=\"checkbox\" Name=\"mode\" Value=\"IWOTH\" %s> Write<Br>\n",
+ (dir_info.st_mode & S_IWOTH) ? "checked" : "");
+ /*
+ * If restricted, only change eXecute permissions on directories.
+ */
+ if (!no_change_exec_perms || S_ISDIR(dir_info.st_mode))
+ fprintf(fp0,
+ "<Input Type=\"checkbox\" Name=\"mode\" Value=\"IXOTH\" %s> %s<Br>\n",
+ (dir_info.st_mode & S_IXOTH) ? "checked" : "",
+ S_ISDIR(dir_info.st_mode) ? "Search" : "Execute");
+
+ fprintf(fp0,
+ "<Br>\n<Li><Input Type=\"submit\" Value=\"Submit\"> %s %s %s.\n</Ol>\n</Form>\n",
+ gettext("form to permit"),
+ S_ISDIR(dir_info.st_mode) ? "directory" : "file",
+ user_filename);
+ fprintf(fp0, "</Body></Html>");
+ LYCloseTempFP(fp0);
+
+ LYforce_no_cache = TRUE;
+ code = PERMIT_FORM_RESULT; /* Special flag for LYMainLoop */
+
+ } else { /* The form being activated. */
+ mode_t new_mode = 0;
+
+ /*
+ * Make sure we have a valid set-permission file comparison string
+ * loaded via a previous call with srcpath != NULL. - KW
+ */
+ if (isBEmpty(LYValidPermitFile)) {
+ if (LYCursesON)
+ HTAlert(INVALID_PERMIT_URL);
+ else
+ fprintf(stderr, "%s\n", INVALID_PERMIT_URL);
+ CTRACE((tfp, "permit_location: called for <%s>.\n",
+ (destpath ?
+ destpath : "NULL URL pointer")));
+ return code;
+ }
+ cp = destpath;
+ while (*cp != '\0' && *cp != '?') { /* Find filename */
+ cp++;
+ }
+ if (*cp == '\0') {
+ return (code); /* Nothing to permit. */
+ }
+ *cp++ = '\0'; /* Null terminate file name and
+ start working on the masks. */
+
+ /* Will now operate only on filename part. */
+ if ((destpath = HTURLPath_toFile(destpath, TRUE, FALSE)) == 0)
+ return (code);
+ if (strlen(destpath) >= LY_MAXPATH) {
+ FREE(destpath);
+ return (code);
+ }
+ strcpy(tmpdst, destpath);
+ FREE(destpath);
+ destpath = tmpdst;
+
+ /*
+ * Make sure that the file string is the one from the last displayed
+ * File Permissions menu. - KW
+ */
+ if (strcmp(destpath, LYValidPermitFile->str)) {
+ if (LYCursesON)
+ HTAlert(INVALID_PERMIT_URL);
+ else
+ fprintf(stderr, "%s\n", INVALID_PERMIT_URL);
+ CTRACE((tfp, "permit_location: called for file '%s'.\n",
+ destpath));
+ return code;
+ }
+
+ /*
+ * A couple of sanity tests.
+ */
+ destpath = strip_trailing_slash(destpath);
+ if (!ok_stat(destpath, &dir_info)
+ || !ok_file_or_dir(&dir_info)) {
+ return code;
+ }
+
+ /*
+ * Cycle over permission strings.
+ */
+ while (*cp != '\0') {
+ char *cr = cp;
+
+ while (*cr != '\0' && *cr != '&') { /* GET data split by '&'. */
+ cr++;
+ }
+ if (*cr != '\0') {
+ *cr++ = '\0';
+ }
+ if (StrNCmp(cp, "mode=", 5) == 0) { /* Magic string. */
+ long mask = permit_bits(cp + 5);
+
+ if (mask != 0) {
+ /*
+ * If restricted, only change eXecute permissions on
+ * directories.
+ */
+ if (!no_change_exec_perms
+ || StrChr(cp + 5, 'X') == NULL
+ || S_ISDIR(dir_info.st_mode)) {
+ new_mode |= (mode_t) mask;
+ }
+ } else {
+ HTAlert(gettext("Invalid mode format."));
+ return code;
+ }
+ } else {
+ HTAlert(gettext("Invalid syntax format."));
+ return code;
+ }
+
+ cp = cr;
+ }
+
+ /*
+ * Call chmod().
+ */
+ code = 1;
+ if ((program = HTGetProgramPath(ppCHMOD)) != NULL) {
+ char **args;
+ char amode[10];
+ char *tmpbuf = NULL;
+
+ HTSprintf0(&tmpbuf, "chmod %.4o %s", (unsigned) new_mode, destpath);
+ sprintf(amode, "%.4o", (unsigned) new_mode);
+ args = make_argv("chmod",
+ amode,
+ destpath,
+ NULL);
+ if (LYExecv(program, args, tmpbuf) <= 0) {
+ code = -1;
+ }
+ FREE(tmpbuf);
+ free_argv(args);
+ } else {
+ if (chmod(destpath, new_mode) < 0) {
+ code = -1;
+ }
+ CTRACE((tfp, "builtin chmod %.4o ->%d\n\t%s\n",
+ (unsigned) new_mode, code, destpath));
+ }
+ if (code == 1)
+ LYforce_no_cache = TRUE; /* Force update of dired listing. */
+ }
+#endif /* !UNIX */
+ return code;
+}
+#endif /* OK_PERMIT */
+
+/*
+ * Display or remove a tag from a given link.
+ */
+void tagflag(int flag,
+ int cur)
+{
+ if (nlinks > 0) {
+ LYmove(links[cur].ly, 2);
+ lynx_stop_reverse();
+ if (flag == TRUE) {
+ LYaddch('+');
+ } else {
+ LYaddch(' ');
+ }
+
+#if defined(FANCY_CURSES) || defined(USE_SLANG)
+ if (!LYShowCursor)
+ LYHideCursor(); /* get cursor out of the way */
+ else
+#endif /* FANCY CURSES || USE_SLANG */
+ /*
+ * Never hide the cursor if there's no FANCY CURSES.
+ */
+ LYmove(links[cur].ly, links[cur].lx);
+
+ LYrefresh();
+ }
+}
+
+/*
+ * Handle DIRED tags.
+ */
+void showtags(HTList *t)
+{
+ int i;
+ HTList *s;
+ char *name;
+
+ for (i = 0; i < nlinks; i++) {
+ s = t;
+ while ((name = (char *) HTList_nextObject(s)) != NULL) {
+ if (!strcmp(links[i].lname, name)) {
+ tagflag(TRUE, i);
+ break;
+ }
+ }
+ }
+}
+
+static char *DirectoryOf(char *pathname)
+{
+ char *result = 0;
+ char *leaf;
+
+ StrAllocCopy(result, pathname);
+ leaf = LYPathLeaf(result);
+
+ if (leaf != result) {
+ const char *result1 = 0;
+
+ *leaf = '\0';
+ if (!LYisRootPath(result))
+ LYTrimPathSep(result);
+ result1 = wwwName(result);
+ StrAllocCopy(result, result1);
+ }
+ return result;
+}
+
+#ifdef __DJGPP__
+/*
+ * Convert filenames to acceptable 8+3 names when necessary. Make a copy of
+ * the parameter if we must modify it.
+ */
+static char *LYonedot(char *line)
+{
+ char *dot;
+ static char line1[LY_MAXPATH];
+
+ if (pathconf(line, _PC_NAME_MAX) <= 12) {
+ LYStrNCpy(line1, line, sizeof(line1) - 1);
+ for (;;) {
+ if ((dot = strrchr(line1, '.')) == 0
+ || LYLastPathSep(dot) != 0) {
+ break;
+ } else if (strlen(dot) == 1) {
+ *dot = 0;
+ } else {
+ *dot = '_';
+ }
+ }
+ return (line1);
+ }
+ return (line);
+}
+#else
+#define LYonedot(path) path
+#endif /* __DJGPP__ */
+
+static char *match_op(const char *prefix,
+ char *data)
+{
+ size_t len = strlen(prefix);
+
+ if (!StrNCmp("LYNXDIRED://", data, 12)
+ && !strncasecomp(prefix, data + 12, (int) len)) {
+ len += 12;
+#if defined(USE_DOS_DRIVES)
+ if (data[len] == '/') { /* this is normal */
+ len++;
+ }
+#endif
+ return data + len;
+ }
+ return 0;
+}
+
+/*
+ * Construct the appropriate system command taking care to escape all path
+ * references to avoid spoofing the shell.
+ */
+static char *build_command(char *line,
+ char *dirName,
+ char *arg)
+{
+ char *buffer = NULL;
+ const char *program;
+ const char *tar_path = HTGetProgramPath(ppTAR);
+
+ if ((arg = match_op("DECOMPRESS", line)) != 0) {
+#define FMT_UNCOMPRESS "%s %s"
+ if ((program = HTGetProgramPath(ppUNCOMPRESS)) != NULL) {
+ HTAddParam(&buffer, FMT_UNCOMPRESS, 1, program);
+ HTAddParam(&buffer, FMT_UNCOMPRESS, 2, arg);
+ HTEndParam(&buffer, FMT_UNCOMPRESS, 2);
+ }
+ return buffer;
+ }
+#if defined(OK_UUDECODE) && !defined(ARCHIVE_ONLY)
+ if ((arg = match_op("UUDECODE", line)) != 0) {
+#define FMT_UUDECODE "%s %s"
+ if ((program = HTGetProgramPath(ppUUDECODE)) != NULL) {
+ HTAddParam(&buffer, FMT_UUDECODE, 1, program);
+ HTAddParam(&buffer, FMT_UUDECODE, 2, arg);
+ HTEndParam(&buffer, FMT_UUDECODE, 2);
+ HTAlert(gettext("Warning! UUDecoded file will exist in the directory you started Lynx."));
+ }
+ return buffer;
+ }
+#endif /* OK_UUDECODE && !ARCHIVE_ONLY */
+
+#ifdef OK_TAR
+ if (tar_path != NULL) {
+# ifndef ARCHIVE_ONLY
+# ifdef OK_GZIP
+ if ((arg = match_op("UNTAR_GZ", line)) != 0) {
+#define FMT_UNTAR_GZ "cd %s; %s -qdc %s | %s %s %s"
+ if ((program = HTGetProgramPath(ppGZIP)) != NULL) {
+ dirName = DirectoryOf(arg);
+ HTAddParam(&buffer, FMT_UNTAR_GZ, 1, dirName);
+ HTAddParam(&buffer, FMT_UNTAR_GZ, 2, program);
+ HTAddParam(&buffer, FMT_UNTAR_GZ, 3, arg);
+ HTAddParam(&buffer, FMT_UNTAR_GZ, 4, tar_path);
+ HTAddToCmd(&buffer, FMT_UNTAR_GZ, 5, TAR_DOWN_OPTIONS);
+ HTAddToCmd(&buffer, FMT_UNTAR_GZ, 6, TAR_PIPE_OPTIONS);
+ HTEndParam(&buffer, FMT_UNTAR_GZ, 6);
+ }
+ return buffer;
+ }
+# endif /* OK_GZIP */
+ if ((arg = match_op("UNTAR_Z", line)) != 0) {
+#define FMT_UNTAR_Z "cd %s; %s %s | %s %s %s"
+ if ((program = HTGetProgramPath(ppZCAT)) != NULL) {
+ dirName = DirectoryOf(arg);
+ HTAddParam(&buffer, FMT_UNTAR_Z, 1, dirName);
+ HTAddParam(&buffer, FMT_UNTAR_Z, 2, program);
+ HTAddParam(&buffer, FMT_UNTAR_Z, 3, arg);
+ HTAddParam(&buffer, FMT_UNTAR_Z, 4, tar_path);
+ HTAddToCmd(&buffer, FMT_UNTAR_Z, 5, TAR_DOWN_OPTIONS);
+ HTAddToCmd(&buffer, FMT_UNTAR_Z, 6, TAR_PIPE_OPTIONS);
+ HTEndParam(&buffer, FMT_UNTAR_Z, 6);
+ }
+ return buffer;
+ }
+ if ((arg = match_op("UNTAR", line)) != 0) {
+#define FMT_UNTAR "cd %s; %s %s %s"
+ dirName = DirectoryOf(arg);
+ HTAddParam(&buffer, FMT_UNTAR, 1, dirName);
+ HTAddParam(&buffer, FMT_UNTAR, 2, tar_path);
+ HTAddToCmd(&buffer, FMT_UNTAR, 3, TAR_DOWN_OPTIONS);
+ HTAddParam(&buffer, FMT_UNTAR, 4, arg);
+ HTEndParam(&buffer, FMT_UNTAR, 4);
+ return buffer;
+ }
+# endif /* !ARCHIVE_ONLY */
+
+# ifdef OK_GZIP
+ if ((arg = match_op("TAR_GZ", line)) != 0) {
+#define FMT_TAR_GZ "cd %s; %s %s %s %s | %s -qc >%s%s"
+ if ((program = HTGetProgramPath(ppGZIP)) != NULL) {
+ dirName = DirectoryOf(arg);
+ HTAddParam(&buffer, FMT_TAR_GZ, 1, dirName);
+ HTAddParam(&buffer, FMT_TAR_GZ, 2, tar_path);
+ HTAddToCmd(&buffer, FMT_TAR_GZ, 3, TAR_UP_OPTIONS);
+ HTAddToCmd(&buffer, FMT_TAR_GZ, 4, TAR_PIPE_OPTIONS);
+ HTAddParam(&buffer, FMT_TAR_GZ, 5, LYPathLeaf(arg));
+ HTAddParam(&buffer, FMT_TAR_GZ, 6, program);
+ HTAddParam(&buffer, FMT_TAR_GZ, 7, LYonedot(LYPathLeaf(arg)));
+ HTAddParam(&buffer, FMT_TAR_GZ, 8, EXT_TAR_GZ);
+ HTEndParam(&buffer, FMT_TAR_GZ, 8);
+ }
+ return buffer;
+ }
+# endif /* OK_GZIP */
+
+ if ((arg = match_op("TAR_Z", line)) != 0) {
+#define FMT_TAR_Z "cd %s; %s %s %s %s | %s >%s%s"
+ if ((program = HTGetProgramPath(ppCOMPRESS)) != NULL) {
+ dirName = DirectoryOf(arg);
+ HTAddParam(&buffer, FMT_TAR_Z, 1, dirName);
+ HTAddParam(&buffer, FMT_TAR_Z, 2, tar_path);
+ HTAddToCmd(&buffer, FMT_TAR_Z, 3, TAR_UP_OPTIONS);
+ HTAddToCmd(&buffer, FMT_TAR_Z, 4, TAR_PIPE_OPTIONS);
+ HTAddParam(&buffer, FMT_TAR_Z, 5, LYPathLeaf(arg));
+ HTAddParam(&buffer, FMT_TAR_Z, 6, program);
+ HTAddParam(&buffer, FMT_TAR_Z, 7, LYonedot(LYPathLeaf(arg)));
+ HTAddParam(&buffer, FMT_TAR_Z, 8, EXT_TAR_Z);
+ HTEndParam(&buffer, FMT_TAR_Z, 8);
+ }
+ return buffer;
+ }
+
+ if ((arg = match_op("TAR", line)) != 0) {
+#define FMT_TAR "cd %s; %s %s %s %s.tar %s"
+ dirName = DirectoryOf(arg);
+ HTAddParam(&buffer, FMT_TAR, 1, dirName);
+ HTAddParam(&buffer, FMT_TAR, 2, tar_path);
+ HTAddToCmd(&buffer, FMT_TAR, 3, TAR_UP_OPTIONS);
+ HTAddToCmd(&buffer, FMT_TAR, 4, TAR_FILE_OPTIONS);
+ HTAddParam(&buffer, FMT_TAR, 5, LYonedot(LYPathLeaf(arg)));
+ HTAddParam(&buffer, FMT_TAR, 6, LYPathLeaf(arg));
+ HTEndParam(&buffer, FMT_TAR, 6);
+ return buffer;
+ }
+ }
+#endif /* OK_TAR */
+
+#ifdef OK_GZIP
+ if ((arg = match_op("GZIP", line)) != 0) {
+#define FMT_GZIP "%s -q %s"
+ if ((program = HTGetProgramPath(ppGZIP)) != NULL) {
+ HTAddParam(&buffer, FMT_GZIP, 1, program);
+ HTAddParam(&buffer, FMT_GZIP, 2, arg);
+ HTEndParam(&buffer, FMT_GZIP, 2);
+ }
+ return buffer;
+ }
+#ifndef ARCHIVE_ONLY
+ if ((arg = match_op("UNGZIP", line)) != 0) {
+#define FMT_UNGZIP "%s -d %s"
+ if ((program = HTGetProgramPath(ppGZIP)) != NULL) {
+ HTAddParam(&buffer, FMT_UNGZIP, 1, program);
+ HTAddParam(&buffer, FMT_UNGZIP, 2, arg);
+ HTEndParam(&buffer, FMT_UNGZIP, 2);
+ }
+ return buffer;
+ }
+#endif /* !ARCHIVE_ONLY */
+#endif /* OK_GZIP */
+
+#ifdef OK_ZIP
+ if ((arg = match_op("ZIP", line)) != 0) {
+#define FMT_ZIP "cd %s; %s -rq %s.zip %s"
+ if ((program = HTGetProgramPath(ppZIP)) != NULL) {
+ dirName = DirectoryOf(arg);
+ HTAddParam(&buffer, FMT_ZIP, 1, dirName);
+ HTAddParam(&buffer, FMT_ZIP, 2, program);
+ HTAddParam(&buffer, FMT_ZIP, 3, LYonedot(LYPathLeaf(arg)));
+ HTAddParam(&buffer, FMT_ZIP, 4, LYPathLeaf(arg));
+ HTEndParam(&buffer, FMT_ZIP, 4);
+ }
+ return buffer;
+ }
+#if !defined(ARCHIVE_ONLY)
+ if ((arg = match_op("UNZIP", line)) != 0) {
+#define FMT_UNZIP "cd %s; %s -q %s"
+ if ((program = HTGetProgramPath(ppUNZIP)) != NULL) {
+ dirName = DirectoryOf(arg);
+ HTAddParam(&buffer, FMT_UNZIP, 1, dirName);
+ HTAddParam(&buffer, FMT_UNZIP, 2, program);
+ HTAddParam(&buffer, FMT_UNZIP, 3, arg);
+ HTEndParam(&buffer, FMT_UNZIP, 3);
+ }
+ return buffer;
+ }
+# endif /* !ARCHIVE_ONLY */
+#endif /* OK_ZIP */
+
+ if ((arg = match_op("COMPRESS", line)) != 0) {
+#define FMT_COMPRESS "%s %s"
+ if ((program = HTGetProgramPath(ppCOMPRESS)) != NULL) {
+ HTAddParam(&buffer, FMT_COMPRESS, 1, program);
+ HTAddParam(&buffer, FMT_COMPRESS, 2, arg);
+ HTEndParam(&buffer, FMT_COMPRESS, 2);
+ }
+ return buffer;
+ }
+
+ return NULL;
+}
+
+/*
+ * Perform file management operations for LYNXDIRED URL's. Attempt to be
+ * consistent. These are (pseudo) URLs - i.e., they should be in URL syntax:
+ * some bytes will be URL-escaped with '%'. This is necessary because these
+ * (pseudo) URLs will go through some of the same kinds of interpretations and
+ * mutilations as real ones: HTParse, stripping off #fragments etc. (Some
+ * access schemes currently have special rules about not escaping parsing '#'
+ * "the URL way" built into HTParse, but that doesn't look like a clean way.)
+ */
+int local_dired(DocInfo *doc)
+{
+ char *line_url; /* will point to doc's address, which is a URL */
+ char *line = NULL; /* same as line_url, but HTUnEscaped, will be allocated */
+ char *arg = NULL; /* ...will point into line[] */
+ char *tp = NULL;
+ char *tmpbuf = NULL;
+ char *buffer = NULL;
+ char *dirName = NULL;
+ BOOL do_pop_doc = TRUE;
+
+ line_url = doc->address;
+ CTRACE((tfp, "local_dired: called for <%s>.\n",
+ (line_url
+ ? line_url
+ : gettext("NULL URL pointer"))));
+ HTUnEscapeSome(line_url, "/"); /* don't mess too much with *doc */
+
+ StrAllocCopy(line, line_url);
+ HTUnEscape(line); /* _file_ (not URL) syntax, for those functions
+ that need it. Don't forget to FREE it. */
+ if (match_op("CHDIR", line) != 0) {
+#ifdef SUPPORT_CHDIR
+ handle_LYK_CHDIR();
+ do_pop_doc = FALSE;
+#endif
+ arg = 0; /* do something to avoid cc's complaints */
+ } else if ((arg = match_op("NEW_FILE", line)) != 0) {
+ if (create_file(arg) > 0)
+ LYforce_no_cache = TRUE;
+ } else if ((arg = match_op("NEW_FOLDER", line)) != 0) {
+ if (create_directory(arg) > 0)
+ LYforce_no_cache = TRUE;
+#ifdef OK_INSTALL
+ } else if ((arg = match_op("INSTALL_SRC", line)) != 0) {
+ local_install(NULL, arg, &tp);
+ if (tp) {
+ FREE(doc->address);
+ doc->address = tp;
+ }
+ FREE(line);
+ return 0;
+ } else if ((arg = match_op("INSTALL_DEST", line)) != 0) {
+ local_install(arg, NULL, &tp);
+ LYpop(doc);
+#endif /* OK_INSTALL */
+ } else if ((arg = match_op("MODIFY_NAME", line)) != 0) {
+ if (modify_name(arg) > 0)
+ LYforce_no_cache = TRUE;
+ } else if ((arg = match_op("MODIFY_LOCATION", line)) != 0) {
+ if (modify_location(arg) > 0)
+ LYforce_no_cache = TRUE;
+ } else if ((arg = match_op("MOVE_TAGGED", line_url)) != 0) {
+ if (modify_tagged(arg) > 0)
+ LYforce_no_cache = TRUE;
+#ifdef OK_PERMIT
+ } else if ((arg = match_op("PERMIT_SRC", line)) != 0) {
+ permit_location(NULL, arg, &tp);
+ if (tp) {
+ /*
+ * One of the checks may have failed.
+ */
+ FREE(doc->address);
+ doc->address = tp;
+ }
+ FREE(line);
+ return 0;
+ } else if ((arg = match_op("PERMIT_LOCATION", line_url)) != 0) {
+ permit_location(arg, NULL, &tp);
+#endif /* OK_PERMIT */
+ } else if ((arg = match_op("REMOVE_SINGLE", line)) != 0) {
+ if (remove_single(arg) > 0)
+ LYforce_no_cache = TRUE;
+ } else if (match_op("REMOVE_TAGGED", line) != 0) {
+ if (remove_tagged())
+ LYforce_no_cache = TRUE;
+ } else if (match_op("CLEAR_TAGGED", line) != 0) {
+ clear_tags();
+ } else if ((arg = match_op("UPLOAD", line)) != 0) {
+ /*
+ * They're written by LYUpload_options() HTUnEscaped; don't want to
+ * change that for now... so pass through without more unescaping.
+ * Directory names containing '#' will probably fail.
+ */
+ if (LYUpload(line_url))
+ LYforce_no_cache = TRUE;
+ } else {
+ LYTrimPathSep(line);
+ if (LYLastPathSep(line) == NULL) {
+ FREE(line);
+ return 0;
+ }
+
+ buffer = build_command(line, dirName, arg);
+
+ if (buffer != 0) {
+ if ((int) strlen(buffer) < LYcolLimit - 14) {
+ HTSprintf0(&tmpbuf, gettext("Executing %s "), buffer);
+ } else {
+ HTSprintf0(&tmpbuf,
+ gettext("Executing system command. This might take a while."));
+ }
+ _statusline(tmpbuf);
+ stop_curses();
+ printf("%s\r\n", tmpbuf);
+ LYSystem(buffer);
+#ifdef VMS
+ HadVMSInterrupt = FALSE;
+#endif /* VMS */
+ start_curses();
+ LYforce_no_cache = TRUE;
+ }
+ }
+
+ FREE(dirName);
+ FREE(tmpbuf);
+ FREE(buffer);
+ FREE(line);
+ FREE(tp);
+ if (do_pop_doc)
+ LYpop(doc);
+ return 0;
+}
+
+/*
+ * Provide a menu of file management options.
+ */
+int dired_options(DocInfo *doc, char **newfile)
+{
+ static char tempfile[LY_MAXPATH];
+ const char *my_suffix;
+ char *path = NULL;
+ char *dir;
+ lynx_list_item_type *nxt;
+ struct stat dir_info;
+ FILE *fp0;
+ char *dir_url;
+ char *path_url;
+ BOOLEAN nothing_tagged;
+ int count;
+ struct dired_menu *mp;
+ char buf[2048];
+
+ if ((fp0 = InternalPageFP(tempfile, FALSE)) == 0)
+ return (0);
+
+ /*
+ * Make the tempfile a URL.
+ */
+ LYLocalFileToURL(newfile, tempfile);
+ LYRegisterUIPage(*newfile, UIP_DIRED_MENU);
+
+ if (doc->link > -1 && doc->link < (nlinks + 1)) {
+ path = HTfullURL_toFile(links[doc->link].lname);
+ LYTrimPathSep(path);
+
+ if (!ok_lstat(path, &dir_info)) {
+ LYCloseTempFP(fp0);
+ FREE(path);
+ return 0;
+ }
+
+ } else {
+ StrAllocCopy(path, "");
+ memset(&dir_info, 0, sizeof(dir_info));
+ }
+
+ dir = HTfullURL_toFile(doc->address);
+ LYTrimPathSep(dir);
+
+ nothing_tagged = (BOOL) (HTList_isEmpty(tagged));
+
+ BeginInternalPage(fp0, DIRED_MENU_TITLE, DIRED_MENU_HELP);
+
+ fprintf(fp0, "<em>%s</em> %s<br>\n", gettext("Current directory:"), dir);
+
+ if (nothing_tagged) {
+ fprintf(fp0, "<em>%s</em> ", gettext("Current selection:"));
+ if (strlen(path)) {
+ fprintf(fp0, "%s<p>\n", path);
+ } else {
+ fprintf(fp0, "%s.<p>\n", gettext("Nothing currently selected."));
+ }
+ } else {
+ /*
+ * Write out number of tagged items, and names of first few of them
+ * relative to current (in the DIRED sense) directory.
+ */
+ int n = HTList_count(tagged);
+ char *cp1 = NULL;
+ char *cd = NULL;
+ int i, m;
+
+#define NUM_TAGS_TO_WRITE 10
+ fprintf(fp0, "<em>%s</em> %d %s",
+ gettext("Current selection:"),
+ n, ((n == 1)
+ ? gettext("tagged item:")
+ : gettext("tagged items:")));
+ StrAllocCopy(cd, doc->address);
+ HTUnEscapeSome(cd, "/");
+ LYAddHtmlSep(&cd);
+ m = (n < NUM_TAGS_TO_WRITE) ? n : NUM_TAGS_TO_WRITE;
+ for (i = 1; i <= m; i++) {
+ cp1 = HTRelative((char *) HTList_objectAt(tagged, i - 1),
+ (*cd ? cd : "file://localhost"));
+ HTUnEscape(cp1);
+ LYEntify(&cp1, TRUE); /* _should_ do this everywhere... */
+ fprintf(fp0, "%s<br>\n&nbsp;&nbsp;&nbsp;%s",
+ (i == 1 ? "" : " ,"), cp1);
+ FREE(cp1);
+ }
+ if (n > m) {
+ fprintf(fp0, " , ...");
+ }
+ fprintf(fp0, "<p>\n");
+ FREE(cd);
+ }
+
+ /*
+ * If menu_head is NULL then use defaults and link them together now.
+ */
+ if (menu_head == NULL) {
+ for (mp = defmenu; GetDiredHref(mp) != NULL; mp++)
+ mp->next = (mp + 1);
+ (--mp)->next = NULL;
+ menu_head = defmenu;
+ }
+
+ for (mp = menu_head; mp != NULL; mp = mp->next) {
+ if (mp->cond != DE_TAG && !nothing_tagged)
+ continue;
+ if (mp->cond == DE_TAG && nothing_tagged)
+ continue;
+ if (mp->cond == DE_DIR &&
+ (!*path || !S_ISDIR(dir_info.st_mode)))
+ continue;
+ if (mp->cond == DE_FILE &&
+ (!*path || !S_ISREG(dir_info.st_mode)))
+ continue;
+#ifdef S_IFLNK
+ if (mp->cond == DE_SYMLINK &&
+ (!*path || !S_ISLNK(dir_info.st_mode)))
+ continue;
+#endif
+ my_suffix = GetDiredSuffix(mp);
+ if (non_empty(my_suffix) &&
+ (strlen(path) < strlen(my_suffix) ||
+ strcmp(my_suffix, &path[(strlen(path) - strlen(my_suffix))]) != 0))
+ continue;
+ dir_url = HTEscape(dir, URL_PATH);
+ path_url = HTEscape(path, URL_PATH);
+ fprintf(fp0, "<a href=\"%s",
+ render_item(GetDiredHref(mp),
+ path_url, dir_url, buf, sizeof(buf), YES));
+ fprintf(fp0, "\">%s</a> ",
+ render_item(GetDiredLink(mp),
+ path, dir, buf, sizeof(buf), NO));
+ fprintf(fp0, "%s<br>\n",
+ render_item(GetDiredRest(mp),
+ path, dir, buf, sizeof(buf), NO));
+ FREE(dir_url);
+ FREE(path_url);
+ }
+ FREE(path);
+
+ if (uploaders != NULL) {
+ fprintf(fp0, "<p>Upload to current directory:<p>\n");
+ for (count = 0, nxt = uploaders;
+ nxt != NULL;
+ nxt = nxt->next, count++) {
+ fprintf(fp0,
+ "<a href=\"LYNXDIRED://UPLOAD=%d/TO=%s\"> %s </a><br>\n",
+ count, dir, nxt->name);
+ }
+ }
+ FREE(dir);
+
+ EndInternalPage(fp0);
+ LYCloseTempFP(fp0);
+
+ LYforce_no_cache = TRUE;
+
+ return (0);
+}
+
+/*
+ * Check DIRED filename, return true on success
+ */
+static int get_filename(const char *prompt,
+ bstring **bufp)
+{
+ char *cp;
+
+ _statusline(prompt);
+
+ (void) LYgetBString(bufp, FALSE, 0, NORECALL);
+ if (strstr((*bufp)->str, "../") != NULL) {
+ HTAlert(gettext("Illegal filename; request ignored."));
+ return FALSE;
+ } else if (no_dotfiles || !show_dotfiles) {
+ cp = LYLastPathSep((*bufp)->str); /* find last slash */
+ if (cp)
+ cp += 1;
+ else
+ cp = (*bufp)->str;
+ if (*cp == '.') {
+ HTAlert(gettext("Illegal filename; request ignored."));
+ return FALSE;
+ }
+ }
+ return !isBEmpty((*bufp));
+}
+
+#ifdef OK_INSTALL
+
+#define LYEXECV_MAX_ARGC 15
+/* these are quasi-constant once they have been allocated: */
+static char **install_argp = NULL; /* args for execv install */
+static char *install_path = NULL; /* auxiliary */
+
+#ifdef LY_FIND_LEAKS
+static void clear_install_path(void)
+{
+ FREE(install_argp);
+ FREE(install_path);
+}
+#endif /* LY_FIND_LEAKS */
+
+/*
+ * Fill in args array for execv (or execvp etc.) call, after first allocating
+ * it if necessary. No fancy parsing, cmd_args is just split at spaces. Leave
+ * room for reserve additional args to be added by caller.
+ *
+ * On success *argvp points to new args vector, *pathp is auxiliary. On
+ * success returns index of next argument, else -1. This is generic enough
+ * that it could be used for other calls than install, except the atexit call.
+ * Go through this trouble for install because INSTALL_ARGS may be significant,
+ * and someone may configure it with more than one significant flags. - kw
+ */
+static int fill_argv_for_execv(char ***argvp,
+ char **pathp,
+ char *cmd_path,
+ const char *cmd_args,
+ int reserve)
+{
+ int n = 0;
+
+ char **args;
+ char *cp;
+
+ if (*argvp == NULL) {
+ *argvp = typecallocn(char *, LYEXECV_MAX_ARGC + 1);
+
+ if (!*argvp)
+ return (-1);
+#ifdef LY_FIND_LEAKS
+ atexit(clear_install_path);
+#endif
+ }
+ args = *argvp;
+ args[n++] = cmd_path;
+ if (cmd_args) {
+ StrAllocCopy(*pathp, cmd_args);
+ cp = strtok(*pathp, " ");
+ if (cp) {
+ while (cp && (n < LYEXECV_MAX_ARGC - reserve)) {
+ args[n++] = cp;
+ cp = strtok(NULL, " ");
+ }
+ if (cp && (n >= LYEXECV_MAX_ARGC - reserve)) {
+ CTRACE((tfp, "Too many args for '%s' in '%s'!\n",
+ NONNULL(cmd_path), cmd_args));
+ return (-1);
+ }
+ } else {
+ args[n++] = *pathp;
+ }
+ }
+ args[n] = (char *) 0;
+ return (n);
+}
+
+/*
+ * Install the specified file or directory.
+ */
+BOOLEAN local_install(char *destpath,
+ char *srcpath,
+ char **newpath)
+{
+ char *tmpbuf = NULL;
+ static char savepath[DIRED_MAXBUF]; /* This will be the link that
+
+ is to be installed. */
+ struct stat dir_info;
+ char **args;
+ HTList *tag;
+ char *cp = NULL;
+ char *tmpdest = NULL;
+ int count = 0;
+ int n = 0; /* indices into 'args[]' */
+ static int src = -1;
+ const char *program;
+
+ if ((program = HTGetProgramPath(ppINSTALL)) == NULL) {
+ HTAlert(gettext("Install in the selected directory not permitted."));
+ return 0;
+ }
+
+ /*
+ * Determine the status of the selected item.
+ */
+ if (srcpath) {
+ srcpath = strip_trailing_slash(srcpath);
+ if (is_url(srcpath)) {
+ char *local_src = HTfullURL_toFile(srcpath);
+
+ if (!ok_localname(savepath, local_src)) {
+ FREE(local_src);
+ return 0;
+ }
+ FREE(local_src);
+ } else if (!HTList_isEmpty(tagged) &&
+ srcpath[0] == '\0') {
+ savepath[0] = '\0'; /* will always use tagged list - kw */
+ } else if (!ok_localname(savepath, srcpath)) {
+ return 0;
+ }
+ LYforce_no_cache = TRUE;
+ LYLocalFileToURL(newpath, Home_Dir());
+ LYAddHtmlSep(newpath);
+ StrAllocCat(*newpath, INSTALLDIRS_FILE);
+ LYRegisterUIPage(*newpath, UIP_INSTALL);
+ return 0;
+ }
+
+ /* deal with ~/ or /~/ at the beginning - kw */
+ if (LYIsTilde(destpath[0]) &&
+ (LYIsPathSep(destpath[1]) || destpath[1] == '\0')) {
+ cp = &destpath[1];
+ } else if (LYIsPathSep(destpath[0]) && LYIsTilde(destpath[1]) &&
+ (LYIsPathSep(destpath[2]) || destpath[2] == '\0')) {
+ cp = &destpath[2];
+ }
+ if (cp) {
+ /* If found, allocate new string, make destpath point to it - kw */
+ StrAllocCopy(tmpdest, Home_Dir());
+ if (cp[0] && cp[1]) {
+ LYAddPathSep(&tmpdest);
+ StrAllocCat(tmpdest, cp + 1);
+ }
+ destpath = tmpdest;
+ }
+
+ destpath = strip_trailing_slash(destpath);
+
+ if (!ok_stat(destpath, &dir_info)) {
+ FREE(tmpdest);
+ return 0;
+ } else if (!S_ISDIR(dir_info.st_mode)) {
+ HTAlert(gettext("The selected item is not a directory! Request ignored."));
+ FREE(tmpdest);
+ return 0;
+ } else if (0 /*directory not writable */ ) {
+ HTAlert(gettext("Install in the selected directory not permitted."));
+ FREE(tmpdest);
+ return 0;
+ }
+
+ statusline(gettext("Just a moment, ..."));
+
+ /* fill in the fixed args, if not already done - kw */
+ if (src > 0 && install_argp) {
+ n = src;
+ n++;
+ } else {
+ n = fill_argv_for_execv(&install_argp, &install_path,
+ "install",
+#ifdef INSTALL_ARGS
+ INSTALL_ARGS,
+#else
+ NULL,
+#endif /* INSTALL_ARGS */
+ 2);
+ if (n <= 0) {
+ src = 0;
+ HTAlert(gettext("Error building install args"));
+ FREE(tmpdest);
+ return 0;
+ }
+ src = n++;
+ }
+ args = install_argp;
+
+ args[n++] = destpath;
+ args[n] = (char *) 0;
+ tag = tagged;
+
+ if (HTList_isEmpty(tagged)) {
+ /* simplistic detection of identical src and dest - kw */
+ if (!strcmp(savepath, destpath)) {
+ HTUserMsg2(gettext("Source and target are the same: %s"),
+ savepath);
+ FREE(tmpdest);
+ return (-1); /* don't do it */
+ } else if (!StrNCmp(savepath, destpath, strlen(destpath)) &&
+ LYIsPathSep(savepath[strlen(destpath)]) &&
+ LYLastPathSep(savepath + strlen(destpath) + 1) == 0) {
+ HTUserMsg2(gettext("Already in target directory: %s"),
+ savepath);
+ FREE(tmpdest);
+ return 0; /* don't do it */
+ }
+ args[src] = savepath;
+ HTSprintf0(&tmpbuf, "install %s in %s", savepath, destpath);
+ if (LYExecv(program, args, tmpbuf) <= 0) {
+ FREE(tmpbuf);
+ FREE(tmpdest);
+ return (-1);
+ }
+ count++;
+ } else {
+ char *name;
+
+ HTSprintf0(&tmpbuf, "install in %s", destpath);
+ while ((name = (char *) HTList_nextObject(tag))) {
+ int err;
+
+ args[src] = HTfullURL_toFile(name);
+
+ /* simplistic detection of identical src and dest - kw */
+ if (!strcmp(args[src], destpath)) {
+ HTUserMsg2(gettext("Source and target are the same: %s"),
+ args[src]);
+ FREE(args[src]);
+ continue; /* skip this source file */
+ } else if (!StrNCmp(args[src], destpath, strlen(destpath)) &&
+ LYIsPathSep(args[src][strlen(destpath)]) &&
+ LYLastPathSep(args[src] + strlen(destpath) + 1) == 0) {
+ HTUserMsg2(gettext("Already in target directory: %s"),
+ args[src]);
+ FREE(args[src]);
+ continue; /* skip this source file */
+ }
+ err = (LYExecv(program, args, tmpbuf) <= 0);
+ FREE(args[src]);
+ if (err) {
+ FREE(tmpbuf);
+ FREE(tmpdest);
+ return ((count == 0) ? -1 : count);
+ }
+ count++;
+ }
+ clear_tags();
+ }
+ FREE(tmpbuf);
+ FREE(tmpdest);
+ HTInfoMsg(gettext("Installation complete"));
+ return count;
+}
+#endif /* OK_INSTALL */
+
+/*
+ * Clear DIRED tags.
+ */
+void clear_tags(void)
+{
+ char *cp = NULL;
+
+ while ((cp = (char *) HTList_removeLastObject(tagged)) != NULL) {
+ FREE(cp);
+ }
+ if (HTList_isEmpty(tagged))
+ FREE(tagged);
+}
+
+/*
+ * Handle DIRED menu item.
+ */
+void add_menu_item(char *str)
+{
+ struct dired_menu *tmp, *mp;
+ char *cp;
+ BOOL used = FALSE;
+
+ /*
+ * First custom menu definition causes entire default menu to be discarded.
+ */
+ if (menu_head == defmenu)
+ menu_head = NULL;
+
+ tmp = typecalloc(struct dired_menu);
+
+ if (tmp == NULL)
+ outofmem(__FILE__, "add_menu_item");
+
+ /*
+ * Conditional on tagged != NULL ?
+ */
+ if ((cp = StrChr(str, ':')) != 0) {
+ *cp++ = '\0';
+ if (strcasecomp(str, "tag") == 0) {
+ tmp->cond = DE_TAG;
+ } else if (strcasecomp(str, "dir") == 0) {
+ tmp->cond = DE_DIR;
+ } else if (strcasecomp(str, "file") == 0) {
+ tmp->cond = DE_FILE;
+#ifdef S_IFLNK
+ } else if (strcasecomp(str, "link") == 0) {
+ tmp->cond = DE_SYMLINK;
+#endif /* S_IFLNK */
+ }
+
+ /*
+ * Conditional on matching suffix.
+ */
+ str = cp;
+ if ((cp = StrChr(str, ':')) != 0) {
+ *cp++ = '\0';
+ StrAllocCopy(tmp->sfx, str);
+
+ str = cp;
+ if ((cp = StrChr(str, ':')) != 0) {
+ *cp++ = '\0';
+ StrAllocCopy(tmp->link, str);
+
+ str = cp;
+ if ((cp = StrChr(str, ':')) != 0) {
+ *cp++ = '\0';
+ StrAllocCopy(tmp->rest, str);
+
+ StrAllocCopy(tmp->href, cp);
+
+ if (menu_head) {
+ for (mp = menu_head;
+ mp && mp->next != NULL;
+ mp = mp->next) {
+ ;
+ }
+ if (mp != NULL) {
+ mp->next = tmp;
+ used = TRUE;
+ }
+ } else {
+ menu_head = tmp;
+ used = TRUE;
+ }
+ }
+ }
+ }
+ }
+ if (!used)
+ FREE(tmp);
+}
+
+void reset_dired_menu(void)
+{
+ if (menu_head != defmenu) {
+ struct dired_menu *mp, *mp_next = NULL;
+
+ for (mp = menu_head; mp != NULL; mp = mp_next) {
+ FREE(mp->sfx);
+ FREE(mp->link);
+ FREE(mp->rest);
+ FREE(mp->href);
+ mp_next = mp->next;
+ FREE(mp);
+ }
+ menu_head = NULL;
+ }
+}
+
+/*
+ * Create URL for DIRED HREF value.
+ */
+static char *render_item(const char *s,
+ const char *path,
+ const char *dir,
+ char *buf,
+ size_t bufsize,
+ int url_syntax)
+{
+ const char *cp;
+ char *bp;
+ char overrun = '\0';
+ char *taglist = NULL;
+
+#define BP_INC (bp>buf+bufsize-2 ? &overrun : bp++)
+ /* Buffer overrun could happen for very long
+ tag list, if %l or %t are used */
+ bp = buf;
+ while (*s && !overrun) {
+ if (*s == '%') {
+ s++;
+ switch (*s) {
+ case '%':
+ *BP_INC = '%';
+ break;
+ case 'p':
+ cp = path;
+ if (!LYIsHtmlSep(*cp))
+ *BP_INC = '/';
+ while (*cp)
+ *BP_INC = *cp++;
+ break;
+ case 'd':
+ cp = dir;
+ if (!LYIsHtmlSep(*cp))
+ *BP_INC = '/';
+ while (*cp)
+ *BP_INC = *cp++;
+ break;
+ case 'f':
+ cp = LYLastPathSep(path);
+ if (cp)
+ cp++;
+ else
+ cp = path;
+ while (*cp)
+ *BP_INC = *cp++;
+ break;
+ case 'l':
+ case 't':
+ if (!HTList_isEmpty(tagged)) {
+ HTList *cur = tagged;
+ char *name;
+
+ while (!overrun &&
+ (name = (char *) HTList_nextObject(cur)) != NULL) {
+ if (*s == 'l' && (cp = strrchr(name, '/')))
+ cp++;
+ else
+ cp = name;
+ StrAllocCat(taglist, cp);
+ StrAllocCat(taglist, " "); /* should this be %20? */
+ }
+ }
+ if (taglist) {
+ /* could HTUnescape here... */
+ cp = taglist;
+ while (*cp)
+ *BP_INC = *cp++;
+ FREE(taglist);
+ }
+ break;
+ default:
+ *BP_INC = '%';
+ *BP_INC = *s;
+ break;
+ }
+ } else {
+ /*
+ * Other chars come from the lynx.cfg or the default. Let's assume
+ * there isn't anything weird there that needs escaping.
+ */
+ *BP_INC = *s;
+ }
+ s++;
+ }
+ if (overrun & url_syntax) {
+ HTAlert(gettext("Temporary URL or list would be too long."));
+ bp = buf; /* set to start, will return empty string as URL */
+ }
+ *bp = '\0';
+ return buf;
+}
+
+#endif /* DIRED_SUPPORT */