diff options
Diffstat (limited to 'src/LYLocal.c')
-rw-r--r-- | src/LYLocal.c | 2685 |
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 %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 */ |