home *** CD-ROM | disk | FTP | other *** search
/ Dream 52 / Amiga_Dream_52.iso / Linux / Divers / lynx2.8.1dev.10.tar.gz / lynx2.8.1dev.10.tar / lynx2-8 / src / LYLocal.c < prev    next >
C/C++ Source or Header  |  1998-04-23  |  59KB  |  2,293 lines

  1. /*
  2. **  Routines to manipulate the local filesystem.
  3. **  Written by: Rick Mallett, Carleton University
  4. **  Report problems to rmallett@ccs.carleton.ca
  5. **  Modified 18-Dec-95 David Trueman (david@cs.dal.ca):
  6. **    Added OK_PERMIT compilation option.
  7. **    Support replacement of compiled-in f)ull menu configuration via
  8. **      DIRED_MENU definitions in lynx.cfg, so that more than one menu
  9. **      can be driven by the same executable.
  10. **  Modified Oct-96 Klaus Weide (kweide@tezcat.com):
  11. **    Changed to use the library's HTList_* functions and macros for
  12. **      managing the list of tagged file URLs.
  13. **    Keep track of proper level of URL escaping, so that unusual filenames
  14. **      which contain #% etc. are handled properly (some HTUnEscapeSome()'s
  15. **      left in to be conservative, and to document where superfluous
  16. **      unescaping took place before).
  17. **    Dynamic memory instead of fixed length buffers in a few cases.
  18. **    Other minor changes to make things work as intended.
  19. **  Modified Jun-97 Klaus Weide (kweide@tezcat.com) & FM:
  20. **    Modified the code handling DIRED_MENU to do more careful
  21. **      checking of the selected file.  In addition to "TAG", "FILE", and
  22. **      "DIR", DIRED_MENU definitions in lynx.cfg now also recognize LINK as
  23. **      a type.  DIRED_MENU definitions with a type field of "LINK" are only
  24. **      used if the current selection is a symbolic link ("FILE" and "DIR"
  25. **      definitions are not used in that case).  The default menu
  26. **      definitions have been updated to reflect this change, and to avoid
  27. **      the showing of menu items whose action would always fail - KW
  28. **    Cast all code into the Lynx programming style. - FM
  29. */
  30.  
  31. #include <HTUtils.h>
  32. #include <tcp.h>
  33. #include <HTAlert.h>
  34. #include <HTParse.h>
  35. #include <LYCurses.h>
  36. #include <LYGlobalDefs.h>
  37. #include <LYUtils.h>
  38. #include <LYStrings.h>
  39. #include <LYCharUtils.h>
  40. #include <LYStructs.h>
  41. #include <LYGetFile.h>
  42. #include <LYHistory.h>
  43. #include <LYUpload.h>
  44. #include <LYLocal.h>
  45. #include <LYSystem.h>
  46.  
  47. #ifndef VMS
  48. #ifndef _WINDOWS
  49. #ifdef HAVE_SYS_WAIT_H
  50. #include <sys/wait.h>
  51. #endif
  52. #include <errno.h>
  53. #include <grp.h>
  54. #endif /*_WINDOWS */
  55. #endif /* VMS */
  56.  
  57. #ifndef WEXITSTATUS
  58. # if HAVE_TYPE_UNIONWAIT
  59. #  define    WEXITSTATUS(status)    (status.w_retcode)
  60. # else
  61. #  define    WEXITSTATUS(status)    (((status) & 0xff00) >> 8)
  62. # endif
  63. #endif
  64.  
  65. #ifndef WTERMSIG
  66. # if HAVE_TYPE_UNIONWAIT
  67. #  define    WTERMSIG(status)    (status.w_termsig)
  68. # else
  69. #  define    WTERMSIG(status)    ((status) & 0x7f)
  70. # endif
  71. #endif
  72.  
  73. #include <LYLeaks.h>
  74.  
  75. #define FREE(x) if (x) {free(x); x = NULL;}
  76.  
  77.  
  78. PRIVATE int LYExecv PARAMS((
  79.     char *        path,
  80.     char **     argv,
  81.     char *        msg));
  82.  
  83. #ifdef DIRED_SUPPORT
  84. PUBLIC char LYPermitFileURL[256] = "\0";
  85. PUBLIC char LYDiredFileURL[256] = "\0";
  86.  
  87. PRIVATE char *filename PARAMS((
  88.     char *        prompt,
  89.     char *        buf,
  90.     size_t        bufsize));
  91.  
  92. #ifdef OK_PERMIT
  93. PRIVATE BOOLEAN permit_location PARAMS((
  94.     char *        destpath,
  95.     char *        srcpath,
  96.     char **     newpath));
  97. #endif /* OK_PERMIT */
  98.  
  99. PRIVATE char *render_item PARAMS((
  100.     char *        s,
  101.     char *        path,
  102.     char *        dir,
  103.     char *        buf,
  104.     int        bufsize,
  105.     BOOLEAN     url_syntax));
  106.  
  107. PRIVATE struct dired_menu *menu_head = NULL;
  108. struct dired_menu {
  109.     int cond;
  110. #define DE_TAG     1
  111. #define DE_DIR     2
  112. #define DE_FILE    3
  113. #define DE_SYMLINK 4
  114.     char *sfx;
  115.     char *link;
  116.     char *rest;
  117.     char *href;
  118.     struct dired_menu *next;
  119. } defmenu[] = {
  120. /*
  121.  *  The following initializations determine the contents of the f)ull menu
  122.  *  selection when in dired mode.  If any menu entries are defined in the
  123.  *  configuration file via DIRED_MENU lines, then these default entries
  124.  *  are discarded entirely.
  125.  */
  126. { 0,              "", "New File",
  127. "(in current directory)", "LYNXDIRED://NEW_FILE%d",        NULL },
  128.  
  129. { 0,              "", "New Directory",
  130. "(in current directory)", "LYNXDIRED://NEW_FOLDER%d",        NULL },
  131.  
  132. { DE_FILE,          "", "Install",
  133. "(of current selection)", "LYNXDIRED://INSTALL_SRC%p",        NULL },
  134. { DE_DIR,          "", "Install",
  135. "(of current selection)", "LYNXDIRED://INSTALL_SRC%p",        NULL },
  136.  
  137. { DE_FILE,          "", "Modify File Name",
  138. "(of current selection)", "LYNXDIRED://MODIFY_NAME%p",        NULL },
  139. { DE_DIR,          "", "Modify Directory Name",
  140. "(of current selection)", "LYNXDIRED://MODIFY_NAME%p",        NULL },
  141. { DE_SYMLINK,          "", "Modify Name",
  142. "(of selected symbolic link)", "LYNXDIRED://MODIFY_NAME%p",        NULL },
  143.  
  144. #ifdef OK_PERMIT
  145. { DE_FILE,          "", "Modify File Permissions",
  146. "(of current selection)", "LYNXDIRED://PERMIT_SRC%p",        NULL },
  147. { DE_DIR,          "", "Modify Directory Permissions",
  148. "(of current selection)", "LYNXDIRED://PERMIT_SRC%p",        NULL },
  149. #endif /* OK_PERMIT */
  150.  
  151. { DE_FILE,          "", "Change Location",
  152. "(of selected file)"    , "LYNXDIRED://MODIFY_LOCATION%p",    NULL },
  153. { DE_DIR,          "", "Change Location",
  154. "(of selected directory)", "LYNXDIRED://MODIFY_LOCATION%p",    NULL },
  155. { DE_SYMLINK,          "", "Change Location",
  156. "(of selected symbolic link)", "LYNXDIRED://MODIFY_LOCATION%p", NULL },
  157.  
  158. { DE_FILE,          "", "Remove File",
  159.    "(current selection)", "LYNXDIRED://REMOVE_SINGLE%p",    NULL },
  160. { DE_DIR,          "", "Remove Directory",
  161.    "(current selection)", "LYNXDIRED://REMOVE_SINGLE%p",    NULL },
  162. { DE_SYMLINK,          "", "Remove Symbolic Link",
  163.    "(current selection)", "LYNXDIRED://REMOVE_SINGLE%p",    NULL },
  164.  
  165. #if defined(OK_UUDECODE) && !defined(ARCHIVE_ONLY)
  166. { DE_FILE,          "", "UUDecode",
  167.    "(current selection)", "LYNXDIRED://UUDECODE%p",        NULL },
  168. #endif /* OK_UUDECODE && !ARCHIVE_ONLY */
  169.  
  170. #if defined(OK_TAR) && !defined(ARCHIVE_ONLY)
  171. { DE_FILE,    ".tar.Z", "Expand",
  172.    "(current selection)", "LYNXDIRED://UNTAR_Z%p",        NULL },
  173. #endif /* OK_TAR && !ARCHIVE_ONLY */
  174.  
  175. #if defined(OK_TAR) && defined(OK_GZIP) && !defined(ARCHIVE_ONLY)
  176. { DE_FILE,     ".tar.gz", "Expand",
  177.    "(current selection)", "LYNXDIRED://UNTAR_GZ%p",        NULL },
  178.  
  179. { DE_FILE,      ".tgz", "Expand",
  180.    "(current selection)", "LYNXDIRED://UNTAR_GZ%p",        NULL },
  181. #endif /* OK_TAR && OK_GZIP && !ARCHIVE_ONLY */
  182.  
  183. #ifndef ARCHIVE_ONLY
  184. { DE_FILE,        ".Z", "Uncompress",
  185.    "(current selection)", "LYNXDIRED://DECOMPRESS%p",        NULL },
  186. #endif /* ARCHIVE_ONLY */
  187.  
  188. #if defined(OK_GZIP) && !defined(ARCHIVE_ONLY)
  189. { DE_FILE,       ".gz", "Uncompress",
  190.    "(current selection)", "LYNXDIRED://UNGZIP%p",        NULL },
  191. #endif /* OK_GZIP && !ARCHIVE_ONLY */
  192.  
  193. #if defined(OK_ZIP) && !defined(ARCHIVE_ONLY)
  194. { DE_FILE,      ".zip", "Uncompress",
  195.    "(current selection)", "LYNXDIRED://UNZIP%p",        NULL },
  196. #endif /* OK_ZIP && !ARCHIVE_ONLY */
  197.  
  198. #if defined(OK_TAR) && !defined(ARCHIVE_ONLY)
  199. { DE_FILE,      ".tar", "UnTar",
  200.    "(current selection)", "LYNXDIRED://UNTAR%p",        NULL },
  201. #endif /* OK_TAR && !ARCHIVE_ONLY */
  202.  
  203. #ifdef OK_TAR
  204. { DE_DIR,          "", "Tar",
  205.    "(current selection)", "LYNXDIRED://TAR%p",            NULL },
  206. #endif /* OK_TAR */
  207.  
  208. #if defined(OK_TAR) && defined(OK_GZIP)
  209. { DE_DIR,          "", "Tar and compress",
  210.       "(using GNU gzip)", "LYNXDIRED://TAR_GZ%p",        NULL },
  211. #endif /* OK_TAR && OK_GZIP */
  212.  
  213. #ifdef OK_ZIP
  214. { DE_DIR,          "", "Package and compress",
  215.        "(using zip)", "LYNXDIRED://ZIP%p",            NULL },
  216. #endif /* OK_ZIP */
  217.  
  218. { DE_FILE,          "", "Compress",
  219.  "(using Unix compress)", "LYNXDIRED://COMPRESS%p",        NULL },
  220.  
  221. #ifdef OK_GZIP
  222. { DE_FILE,          "", "Compress",
  223.       "(using gzip)", "LYNXDIRED://GZIP%p",         NULL },
  224. #endif /* OK_GZIP */
  225.  
  226. #ifdef OK_ZIP
  227. { DE_FILE,          "", "Compress",
  228.        "(using zip)", "LYNXDIRED://ZIP%p",            NULL },
  229. #endif /* OK_ZIP */
  230.  
  231. { DE_TAG,          "", "Move all tagged items to another location.",
  232.               "", "LYNXDIRED://MOVE_TAGGED%d",        NULL },
  233.  
  234. { DE_TAG,          "", "Remove all tagged files and directories.",
  235.               "", "LYNXDIRED://REMOVE_TAGGED",        NULL },
  236.  
  237. { DE_TAG,          "", "Untag all tagged files and directories.",
  238.               "", "LYNXDIRED://CLEAR_TAGGED",        NULL },
  239.  
  240. { 0,            NULL, NULL,
  241.             NULL, NULL,                 NULL }
  242. };
  243.  
  244. /*
  245.  *  Remove all tagged files and directories.
  246.  */
  247. PRIVATE BOOLEAN remove_tagged NOARGS
  248. {
  249.     int c, ans;
  250.     char *cp, *tp;
  251.     char tmpbuf[1024];
  252.     char *testpath = NULL;
  253.     struct stat dir_info;
  254.     int count, i;
  255.     HTList *tag;
  256.     char *args[5];
  257.  
  258.     if (HTList_isEmpty(tagged))  /* should never happen */
  259.     return 0;
  260.  
  261.     _statusline("Remove all tagged files and directories (y or n): ");
  262.     c = LYgetch();
  263.     ans = TOUPPER(c);
  264.  
  265.     count = 0;
  266.     tag = tagged;
  267.     while (ans == 'Y' && (cp = (char *)HTList_nextObject(tag)) != NULL) {
  268.     if (is_url(cp) == FILE_URL_TYPE) { /* unecessary check */
  269.         tp = cp;
  270.         if (!strncmp(tp, "file://localhost", 16)) {
  271.         tp += 16;
  272.         } else if (!strncmp(tp, "file:", 5)) {
  273.         tp += 5;
  274.         }
  275.         StrAllocCopy(testpath, tp);
  276.         HTUnEscape(testpath);
  277.         if ((i = strlen(testpath)) && testpath[i-1] == '/')
  278.         testpath[(i - 1)] = '\0';
  279.  
  280.         /*
  281.          *    Check the current status of the path to be deleted.
  282.          */
  283.         if (stat(testpath,&dir_info) == -1) {
  284.         sprintf(tmpbuf,
  285.             "System error - failed to get status of '%s'.",
  286.             testpath);
  287.         _statusline(tmpbuf);
  288.         sleep(AlertSecs);
  289.         return count;
  290.         } else {
  291.         args[0] = "rm";
  292.         args[1] = "-rf";
  293.         args[2] = testpath;
  294.         args[3] = (char *) 0;
  295.         sprintf(tmpbuf, "remove %s", testpath);
  296.         if (LYExecv(RM_PATH, args, tmpbuf) <= 0) {
  297.             FREE(testpath);
  298.             return ((count == 0) ? -1 : count);
  299.         }
  300.         ++count;
  301.         }
  302.     }
  303.     }
  304.     FREE(testpath);
  305.     clear_tags();
  306.     return count;
  307. }
  308.  
  309. /*
  310.  *  Move all tagged files and directories to a new location.
  311.  *  Input is current directory.
  312.  *  The tests in this function can, at best, prevent some user mistakes -
  313.  *   anybody who relies on them for security is seriously misguided.
  314.  *  If a user has enough permissions to move a file somewhere, the same
  315.  *   uid with Lynx & dired can do the same thing.
  316.  */
  317. PRIVATE BOOLEAN modify_tagged ARGS1(
  318.     char *,     testpath)
  319. {
  320.     char *cp;
  321.     dev_t dev;
  322.     ino_t inode;
  323.     uid_t owner;
  324.     char tmpbuf[1024];
  325.     char *savepath = NULL;
  326.     char *srcpath = NULL;
  327.     struct stat dir_info;
  328.     char *args[5];
  329.     int count = 0;
  330.     HTList *tag;
  331.  
  332.     if (HTList_isEmpty(tagged))  /* should never happen */
  333.     return 0;
  334.  
  335.     _statusline("Enter new location for tagged items: ");
  336.  
  337.     tmpbuf[0] = '\0';
  338.     LYgetstr(tmpbuf, VISIBLE, sizeof(tmpbuf), NORECALL);
  339.     if (strlen(tmpbuf)) {
  340.     /*
  341.      *    Determine the ownership of the current location.
  342.      */
  343.     /*
  344.      *  This test used to always fail from the dired menu...
  345.      *  changed to something that hopefully makes more sense - KW
  346.      */
  347.     if (testpath && *testpath && 0!=strcmp(testpath,"/")) {
  348.         /*
  349.          *    testpath passed in and is not empty and not a single "/"
  350.          *    (which would probably be bogus) - use it.
  351.          */
  352.         cp = testpath;
  353.     } else {
  354.         /*
  355.          *    Prepare to get directory path from one of the tagged files.
  356.          */
  357.         cp = HTList_lastObject(tagged);
  358.         testpath = NULL;    /* Won't be needed any more in this function,
  359.                    set to NULL as a flag. */
  360.         if (!cp)    /* Last resort, should never happen. */
  361.         cp = "/";
  362.     }
  363.     if (!strncmp(cp, "file://localhost", 16)) {
  364.         cp += 16;
  365.     } else if (!strncmp(cp, "file:", 5)) {
  366.         cp += 5;
  367.     }
  368.     if (testpath == NULL) {
  369.         /*
  370.          *    Get the directory containing the file or subdir.
  371.          */
  372.         cp = strip_trailing_slash(cp);
  373.         savepath = HTParse(".", cp, PARSE_PATH+PARSE_PUNCTUATION);
  374.     } else {
  375.         StrAllocCopy(savepath, cp);
  376.     }
  377.     HTUnEscape(savepath);
  378.     if (stat(savepath, &dir_info) == -1) {
  379.         sprintf(tmpbuf, "Unable to get status of '%s'.", savepath);
  380.         _statusline(tmpbuf);
  381.         sleep(AlertSecs);
  382.         FREE(savepath);
  383.         return 0;
  384.     }
  385.  
  386.     /*
  387.      *  Save the owner of the current location for later use.
  388.      *  Also save the device and inode for location checking/
  389.      */
  390.     dev = dir_info.st_dev;
  391.     inode = dir_info.st_ino;
  392.     owner = dir_info.st_uid;
  393.  
  394.     /*
  395.      *  Replace ~/ references to the home directory.
  396.      */
  397.     if (!strncmp(tmpbuf, "~/", 2)) {
  398.         char *cp1 = NULL;
  399.         StrAllocCopy(cp1, Home_Dir());
  400.         StrAllocCat(cp1, (tmpbuf + 1));
  401.         if (strlen(cp1) > (sizeof(tmpbuf) - 1)) {
  402.         sprintf(tmpbuf, "%s", "Path too long");
  403.         _statusline(tmpbuf);
  404.         sleep(AlertSecs);
  405.         FREE(savepath);
  406.         FREE(cp1);
  407.         return 0;
  408.         }
  409.         strcpy(tmpbuf, cp1);
  410.         FREE(cp1);
  411.     }
  412.  
  413.     /*
  414.      *  If path is relative, prefix it with current location.
  415.      */
  416.     if (tmpbuf[0] != '/') {
  417.         if (savepath[(strlen(savepath) - 1)] != '/')
  418.         StrAllocCat(savepath,"/");
  419.         StrAllocCat(savepath,tmpbuf);
  420.     } else {
  421.         StrAllocCopy(savepath,tmpbuf);
  422.     }
  423.  
  424.     /*
  425.      *  stat() the target location to determine type and ownership.
  426.      */
  427.     if (stat(savepath, &dir_info) == -1) {
  428.         sprintf(tmpbuf,"Unable to get status of '%s'.",savepath);
  429.         _statusline(tmpbuf);
  430.         sleep(AlertSecs);
  431.         FREE(savepath);
  432.         return 0;
  433.     }
  434.  
  435.     /*
  436.      *  Make sure the source and target locations are not the same place.
  437.      */
  438.     if (dev == dir_info.st_dev && inode == dir_info.st_ino) {
  439.         _statusline(
  440.        "Source and destination are the same location - request ignored!");
  441.         sleep(AlertSecs);
  442.         FREE(savepath);
  443.         return 0;
  444.     }
  445.  
  446.     /*
  447.      *  Make sure the target location is a directory which is owned
  448.      * by the same uid as the owner of the current location.
  449.      */
  450.     if ((dir_info.st_mode & S_IFMT) == S_IFDIR) {
  451.         if (dir_info.st_uid == owner) {
  452.         count = 0;
  453.         tag = tagged;
  454.  
  455.         /*
  456.          *  Move all tagged items to the target location.
  457.          */
  458.         while ((cp = (char *)HTList_nextObject(tag)) != NULL) {
  459.             if (!strncmp(cp, "file://localhost", 16)) {
  460.             cp += 16;
  461.             } else if (!strncmp(cp, "file:", 5)) {
  462.             cp += 5;
  463.             }
  464.             StrAllocCopy(srcpath, cp);
  465.             HTUnEscape(srcpath);
  466.  
  467.             sprintf(tmpbuf, "move %s to %s", srcpath, savepath);
  468.             args[0] = "mv";
  469.             args[1] = srcpath;
  470.             args[2] = savepath;
  471.             args[3] = (char *) 0;
  472.             if (LYExecv(MV_PATH, args, tmpbuf) <= 0) {
  473.             if (count == 0)
  474.                 count = -1;
  475.             break;
  476.             }
  477.             ++count;
  478.         }
  479.         FREE(srcpath);
  480.         FREE(savepath);
  481.         clear_tags();
  482.         return count;
  483.         } else {
  484.         _statusline(
  485.             "Destination has different owner! Request denied.");
  486.         sleep(AlertSecs);
  487.         FREE(srcpath);
  488.         FREE(savepath);
  489.         return 0;
  490.         }
  491.     } else {
  492.         _statusline(
  493.            "Destination is not a valid directory! Request denied.");
  494.         sleep(AlertSecs);
  495.         FREE(savepath);
  496.         return 0;
  497.     }
  498.     }
  499.     return 0;
  500. }
  501.  
  502. /*
  503.  *  Modify the name of the specified item.
  504.  */
  505. PRIVATE BOOLEAN modify_name ARGS1(
  506.     char *,     testpath)
  507. {
  508.     char *cp;
  509.     char tmpbuf[512];
  510.     char newpath[512];
  511.     char savepath[512];
  512.     struct stat dir_info;
  513.     char *args[5];
  514.  
  515.     /*
  516.      *    Determine the status of the selected item.
  517.      */
  518.     testpath = strip_trailing_slash(testpath);
  519.  
  520.     if (stat(testpath, &dir_info) == -1) {
  521.     sprintf(tmpbuf, "Unable to get status of '%s'.", testpath);
  522.     _statusline(tmpbuf);
  523.     sleep(AlertSecs);
  524.     } else {
  525.     /*
  526.      *  Change the name of the file or directory.
  527.      */
  528.     if ((dir_info.st_mode & S_IFMT) == S_IFDIR) {
  529.          cp = "Enter new name for directory: ";
  530.     } else if ((dir_info.st_mode & S_IFMT) == S_IFREG) {
  531.          cp = "Enter new name for file: ";
  532.     } else {
  533.          _statusline(
  534.      "The selected item is not a file or a directory! Request ignored.");
  535.          sleep(AlertSecs);
  536.          return 0;
  537.     }
  538.     if (filename(cp, tmpbuf, sizeof(tmpbuf)) == NULL)
  539.         return 0;
  540.  
  541.     /*
  542.      *  Do not allow the user to also change the location at this time.
  543.      */
  544.     if (strchr(tmpbuf, '/') != NULL) {
  545.         _statusline("Illegal character \"/\" found! Request ignored.");
  546.         sleep(AlertSecs);
  547.     } else if (strlen(tmpbuf) &&
  548.            (cp = strrchr(testpath, '/')) != NULL) {
  549.         strcpy(savepath,testpath);
  550.         *(++cp) = '\0';
  551.         strcpy(newpath,testpath);
  552.         strcat(newpath,tmpbuf);
  553.  
  554.         /*
  555.          *    Make sure the destination does not already exist.
  556.          */
  557.         if (stat(newpath, &dir_info) == -1) {
  558.         if (errno != ENOENT) {
  559.             sprintf(tmpbuf,
  560.                 "Unable to determine status of '%s'.", newpath);
  561.             _statusline(tmpbuf);
  562.             sleep(AlertSecs);
  563.         } else {
  564.             sprintf(tmpbuf, "move %s to %s", savepath, newpath);
  565.             args[0] = "mv";
  566.             args[1] = savepath;
  567.             args[2] = newpath;
  568.             args[3] = (char *) 0;
  569.             if (LYExecv(MV_PATH, args, tmpbuf) <= 0)
  570.             return (-1);
  571.             return 1;
  572.         }
  573.         } else if ((dir_info.st_mode & S_IFMT) == S_IFDIR) {
  574.         _statusline(
  575.         "There is already a directory with that name! Request ignored.");
  576.         sleep(AlertSecs);
  577.         } else if ((dir_info.st_mode & S_IFMT) == S_IFREG) {
  578.         _statusline(
  579.          "There is already a file with that name! Request ignored.");
  580.         sleep(AlertSecs);
  581.         } else {
  582.         _statusline(
  583.            "The specified name is already in use! Request ignored.");
  584.         sleep(AlertSecs);
  585.         }
  586.     }
  587.     }
  588.     return 0;
  589. }
  590.  
  591. /*
  592.  *  Change the location of a file or directory.
  593.  */
  594. PRIVATE BOOLEAN modify_location ARGS1(
  595.     char *,     testpath)
  596. {
  597.     int mode;
  598.     char *cp;
  599.     dev_t dev;
  600.     ino_t inode;
  601.     uid_t owner;
  602.     char tmpbuf[1024];
  603.     char newpath[512];
  604.     char savepath[512];
  605.     struct stat dir_info;
  606.     char *args[5];
  607.  
  608.     /*
  609.      *    Determine the status of the selected item.
  610.      */
  611.     testpath = strip_trailing_slash(testpath);
  612.  
  613.     if (stat(testpath, &dir_info) == -1) {
  614.     sprintf(tmpbuf, "Unable to get status of '%s'.", testpath);
  615.     _statusline(tmpbuf);
  616.     sleep(AlertSecs);
  617.     return 0;
  618.     }
  619.  
  620.     /*
  621.      *    Change the location of the file or directory.
  622.      */
  623.     if ((dir_info.st_mode & S_IFMT) == S_IFDIR) {
  624.     cp = "Enter new location for directory: ";
  625.     } else if ((dir_info.st_mode & S_IFMT) == S_IFREG) {
  626.     cp = "Enter new location for file: ";
  627.     } else {
  628.     _statusline(
  629.     "The specified item is not a file or a directory - request ignored.");
  630.     sleep(AlertSecs);
  631.     return 0;
  632.     }
  633.     if (filename(cp, tmpbuf, sizeof(tmpbuf)) == NULL)
  634.     return 0;
  635.     if (strlen(tmpbuf)) {
  636.     strcpy(savepath, testpath);
  637.     strcpy(newpath, testpath);
  638.  
  639.     /*
  640.      *  Allow ~/ references to the home directory.
  641.      */
  642.     if (!strncmp(tmpbuf,"~/",2)) {
  643.         strcpy(newpath, Home_Dir());
  644.         strcat(newpath, (tmpbuf + 1));
  645.         strcpy(tmpbuf, newpath);
  646.     }
  647.     if (tmpbuf[0] != '/') {
  648.         if ((cp = strrchr(newpath,'/')) != NULL) {
  649.         *++cp = '\0';
  650.         strcat(newpath,tmpbuf);
  651.         } else {
  652.         _statusline("Unexpected failure - unable to find trailing \"/\"");
  653.         sleep(AlertSecs);
  654.         return 0;
  655.         }
  656.     } else {
  657.         strcpy(newpath,tmpbuf);
  658.     }
  659.  
  660.     /*
  661.      *  Make sure the source and target have the same owner (uid).
  662.      */
  663.     dev = dir_info.st_dev;
  664.     mode = dir_info.st_mode;
  665.     inode = dir_info.st_ino;
  666.     owner = dir_info.st_uid;
  667.     if (stat(newpath, &dir_info) == -1) {
  668.         sprintf(tmpbuf,"Unable to get status of '%s'.",newpath);
  669.         _statusline(tmpbuf);
  670.         sleep(AlertSecs);
  671.         return 0;
  672.     }
  673.     if ((dir_info.st_mode & S_IFMT) != S_IFDIR) {
  674.         _statusline(
  675.         "Destination is not a valid directory! Request denied.");
  676.         sleep(AlertSecs);
  677.         return 0;
  678.     }
  679.  
  680.     /*
  681.      *  Make sure the source and target are not the same location.
  682.      */
  683.     if (dev == dir_info.st_dev && inode == dir_info.st_ino) {
  684.         _statusline(
  685.        "Source and destination are the same location! Request ignored!");
  686.         sleep(AlertSecs);
  687.         return 0;
  688.     }
  689.     if (dir_info.st_uid == owner) {
  690.         sprintf(tmpbuf,"move %s to %s",savepath,newpath);
  691.         args[0] = "mv";
  692.         args[1] = savepath;
  693.         args[2] = newpath;
  694.         args[3] = (char *) 0;
  695.         if (LYExecv(MV_PATH, args, tmpbuf) <= 0)
  696.         return (-1);
  697.         return 1;
  698.     } else {
  699.      _statusline("Destination has different owner! Request denied.");
  700.         sleep(AlertSecs);
  701.         return 0;
  702.     }
  703.     }
  704.     return 0;
  705. }
  706.  
  707. /*
  708.  *  Modify name or location of a file or directory on localhost.
  709.  */
  710. PUBLIC BOOLEAN local_modify ARGS2(
  711.     document *,    doc,
  712.     char **,    newpath)
  713. {
  714.     int c, ans;
  715.     char *cp;
  716.     char testpath[512]; /* a bit ridiculous */
  717.     int count;
  718.  
  719.     if (!HTList_isEmpty(tagged)) {
  720.     cp = doc->address;
  721.     if (!strncmp(cp, "file://localhost", 16)) {
  722.         cp += 16;
  723.     } else if (!strncmp(cp, "file:", 5)) {
  724.         cp += 5;
  725.     }
  726.     strcpy(testpath, cp);
  727.     HTUnEscapeSome(testpath, "/");
  728.     count = modify_tagged(testpath);
  729.  
  730.     if (doc->link > (nlinks-count - 1))
  731.         doc->link = (nlinks-count - 1);
  732.     doc->link = (doc->link < 0) ?
  733.                   0 : doc->link;
  734.  
  735.     return count;
  736.     } else if (doc->link < 0 || doc->link > nlinks) {
  737.     /*
  738.      *  Added protection.
  739.      */
  740.     return 0;
  741.     }
  742.  
  743.     /*
  744.      *    Do not allow simultaneous change of name and location as in Unix.
  745.      *    This reduces functionality but reduces difficulty for the novice.
  746.      */
  747. #ifdef OK_PERMIT
  748.     _statusline("Modify name, location, or permission (n, l, or p): ");
  749. #else
  750.     _statusline("Modify name, or location (n or l): ");
  751. #endif /* OK_PERMIT */
  752.     c = LYgetch();
  753.     ans = TOUPPER(c);
  754.  
  755.     if (strchr("NLP", ans) != NULL) {
  756.     cp = links[doc->link].lname;
  757.     if (!strncmp(cp, "file://localhost", 16)) {
  758.         cp += 16;
  759.     } else if(!strncmp(cp, "file:", 5)) {
  760.         cp += 5;
  761.     }
  762.     strcpy(testpath, cp);
  763.     HTUnEscape(testpath);
  764.  
  765.     if (ans == 'N') {
  766.         return(modify_name(testpath));
  767.     } else if (ans == 'L') {
  768.         if (modify_location(testpath)) {
  769.         if (doc->link == (nlinks-1))
  770.             --doc->link;
  771.         return 1;
  772.         }
  773. #ifdef OK_PERMIT
  774.     } else if (ans == 'P') {
  775.         return(permit_location(NULL, testpath, newpath));
  776. #endif /* OK_PERMIT */
  777.     } else {
  778.         /*
  779.          *    Code for changing ownership needed here.
  780.          */
  781.          _statusline("This feature not yet implemented!");
  782.         sleep(AlertSecs);
  783.     }
  784.     }
  785.     return 0;
  786. }
  787.  
  788. /*
  789.  *  Create a new empty file in the current directory.
  790.  */
  791. PRIVATE BOOLEAN create_file ARGS1(
  792.     char *,     current_location)
  793. {
  794.     char tmpbuf[512];
  795.     char testpath[512];
  796.     struct stat dir_info;
  797.     char *args[5];
  798.     char *bad_chars = ".~/";
  799.  
  800.     if (filename("Enter name of file to create: ",
  801.          tmpbuf, sizeof(tmpbuf)) == NULL) {
  802.     return 0;
  803.     }
  804.  
  805.     if (!no_dotfiles && show_dotfiles) {
  806.     bad_chars = "~/";
  807.     }
  808.  
  809.     if (strstr(tmpbuf, "//") != NULL) {
  810.     _statusline("Illegal redirection \"//\" found! Request ignored.");
  811.     sleep(AlertSecs);
  812.     } else if (strlen(tmpbuf) && strchr(bad_chars, tmpbuf[0]) == NULL) {
  813.     strcpy(testpath,current_location);
  814.     if (testpath[(strlen(testpath) - 1)] != '/') {
  815.         strcat(testpath,"/");
  816.     }
  817.  
  818.     /*
  819.      *  Append the target filename to the current location.
  820.      */
  821.     strcat(testpath, tmpbuf);
  822.  
  823.     /*
  824.      *  Make sure the target does not already exist
  825.      */
  826.     if (stat(testpath, &dir_info) == -1) {
  827.         if (errno != ENOENT) {
  828.         sprintf(tmpbuf,
  829.             "Unable to determine status of '%s'.", testpath);
  830.         _statusline(tmpbuf);
  831.         sleep(AlertSecs);
  832.         return 0;
  833.         }
  834.         sprintf(tmpbuf,"create %s",testpath);
  835.         args[0] = "touch";
  836.         args[1] = testpath;
  837.         args[2] = (char *) 0;
  838.         if (LYExecv(TOUCH_PATH, args, tmpbuf) <= 0)
  839.         return (-1);
  840.         return 1;
  841.     } else if ((dir_info.st_mode & S_IFMT) == S_IFDIR) {
  842.         _statusline(
  843.        "There is already a directory with that name! Request ignored.");
  844.         sleep(AlertSecs);
  845.     } else if ((dir_info.st_mode & S_IFMT) == S_IFREG) {
  846.         _statusline(
  847.         "There is already a file with that name! Request ignored.");
  848.         sleep(AlertSecs);
  849.     } else {
  850.         _statusline(
  851.           "The specified name is already in use! Request ignored.");
  852.         sleep(AlertSecs);
  853.     }
  854.     }
  855.     return 0;
  856. }
  857.  
  858. /*
  859.  *  Create a new directory in the current directory.
  860.  */
  861. PRIVATE BOOLEAN create_directory ARGS1(
  862.     char *,     current_location)
  863. {
  864.     char tmpbuf[512];
  865.     char testpath[512];
  866.     struct stat dir_info;
  867.     char *args[5];
  868.     char *bad_chars = ".~/";
  869.  
  870.     if (filename("Enter name for new directory: ",
  871.          tmpbuf, sizeof(tmpbuf)) == NULL) {
  872.     return 0;
  873.     }
  874.  
  875.     if (!no_dotfiles && show_dotfiles) {
  876.     bad_chars = "~/";
  877.     }
  878.  
  879.     if (strstr(tmpbuf, "//") != NULL) {
  880.     _statusline("Illegal redirection \"//\" found! Request ignored.");
  881.     sleep(AlertSecs);
  882.     } else if (strlen(tmpbuf) && strchr(bad_chars, tmpbuf[0]) == NULL) {
  883.     strcpy(testpath,current_location);
  884.     if (testpath[(strlen(testpath) - 1)] != '/') {
  885.         strcat(testpath,"/");
  886.     }
  887.     strcat(testpath, tmpbuf);
  888.  
  889.     /*
  890.      *  Make sure the target does not already exist.
  891.      */
  892.     if (stat(testpath, &dir_info) == -1) {
  893.         if (errno != ENOENT) {
  894.         sprintf(tmpbuf,
  895.             "Unable to determine status of '%s'.", testpath);
  896.         _statusline(tmpbuf);
  897.         sleep(AlertSecs);
  898.         return 0;
  899.         }
  900.         sprintf(tmpbuf,"make directory %s",testpath);
  901.         args[0] = "mkdir";
  902.         args[1] = testpath;
  903.         args[2] = (char *) 0;
  904.         if (LYExecv(MKDIR_PATH, args, tmpbuf) <= 0)
  905.         return (-1);
  906.         return 1;
  907.     } else if ((dir_info.st_mode & S_IFMT) == S_IFDIR) {
  908.         _statusline(
  909.        "There is already a directory with that name! Request ignored.");
  910.         sleep(AlertSecs);
  911.     } else if ((dir_info.st_mode & S_IFMT) == S_IFREG) {
  912.         _statusline(
  913.         "There is already a file with that name! Request ignored.");
  914.         sleep(AlertSecs);
  915.     } else {
  916.         _statusline(
  917.           "The specified name is already in use! Request ignored.");
  918.         sleep(AlertSecs);
  919.     }
  920.     }
  921.     return 0;
  922. }
  923.  
  924. /*
  925.  *  Create a file or a directory at the current location.
  926.  */
  927. PUBLIC BOOLEAN local_create ARGS1(
  928.     document *,    doc)
  929. {
  930.     int c, ans;
  931.     char *cp;
  932.     char testpath[512];
  933.  
  934.     _statusline("Create file or directory (f or d): ");
  935.     c = LYgetch();
  936.     ans = TOUPPER(c);
  937.  
  938.     cp = doc->address;
  939.     if (!strncmp(cp, "file://localhost", 16)) {
  940.     cp += 16;
  941.     } else if (!strncmp(cp, "file:", 5)) {
  942.     cp += 5;
  943.     }
  944.     strcpy(testpath,cp);
  945.     HTUnEscape(testpath);
  946.  
  947.     if (ans == 'F') {
  948.     return(create_file(testpath));
  949.     } else if (ans == 'D') {
  950.     return(create_directory(testpath));
  951.     } else {
  952.     return 0;
  953.     }
  954. }
  955.  
  956. /*
  957.  *  Remove a single file or directory.
  958.  */
  959. PRIVATE BOOLEAN remove_single ARGS1(
  960.     char *,     testpath)
  961. {
  962.     int c;
  963.     char *cp;
  964.     char tmpbuf[1024];
  965.     struct stat dir_info;
  966.     char *args[5];
  967.  
  968.     /*
  969.      *    lstat() first in case its a symbolic link.
  970.      */
  971.     if (lstat(testpath, &dir_info) == -1 &&
  972.     stat(testpath, &dir_info) == -1) {
  973.     sprintf(tmpbuf,
  974.         "System error - failed to get status of '%s'.", testpath);
  975.     _statusline(tmpbuf);
  976.     sleep(AlertSecs);
  977.     return 0;
  978.     }
  979.  
  980.     /*
  981.      *    Locate the filename portion of the path.
  982.      */
  983.     if ((cp = strrchr(testpath, '/')) != NULL) {
  984.     ++cp;
  985.     } else {
  986.     cp = testpath;
  987.     }
  988.     if ((dir_info.st_mode & S_IFMT) == S_IFDIR) {
  989.     if (strlen(cp) < 37) {
  990.         sprintf(tmpbuf,
  991.             "Remove '%s' and all of its contents (y or n): ", cp);
  992.     } else {
  993.         sprintf(tmpbuf,
  994.             "Remove directory and all of its contents (y or n): ");
  995.     }
  996.     } else if ((dir_info.st_mode & S_IFMT) == S_IFREG) {
  997.     if (strlen(cp) < 60) {
  998.         sprintf(tmpbuf, "Remove file '%s' (y or n): ", cp);
  999.     } else {
  1000.         sprintf(tmpbuf, "Remove file (y or n): ");
  1001.     }
  1002. #ifdef S_IFLNK
  1003.     } else if ((dir_info.st_mode & S_IFMT) == S_IFLNK) {
  1004.     if (strlen(cp) < 50) {
  1005.         sprintf(tmpbuf, "Remove symbolic link '%s' (y or n): ", cp);
  1006.     } else {
  1007.         sprintf(tmpbuf, "Remove symbolic link (y or n): ");
  1008.     }
  1009. #endif
  1010.     } else {
  1011.     sprintf(tmpbuf, "Unable to determine status of '%s'.", testpath);
  1012.     _statusline(tmpbuf);
  1013.     sleep(AlertSecs);
  1014.     return 0;
  1015.     }
  1016.     _statusline(tmpbuf);
  1017.  
  1018.     c = LYgetch();
  1019.     if (TOUPPER(c) == 'Y') {
  1020.     sprintf(tmpbuf,"remove %s",testpath);
  1021.     args[0] = "rm";
  1022.     args[1] = "-rf";
  1023.     args[2] = testpath;
  1024.     args[3] = (char *) 0;
  1025.     if (LYExecv(RM_PATH, args, tmpbuf) <= 0)
  1026.         return (-1);
  1027.     return 1;
  1028.     }
  1029.     return 0;
  1030. }
  1031.  
  1032. /*
  1033.  *  Remove a file or a directory.
  1034.  */
  1035. PUBLIC BOOLEAN local_remove ARGS1(
  1036.     document *,    doc)
  1037. {
  1038.     char *cp, *tp;
  1039.     char testpath[512];
  1040.     int count, i;
  1041.  
  1042.     if (!HTList_isEmpty(tagged)) {
  1043.     count = remove_tagged();
  1044.     if (doc->link > (nlinks-count - 1))
  1045.         doc->link = (nlinks-count - 1);
  1046.     doc->link = (doc->link < 0) ?
  1047.                   0 : doc->link;
  1048.     return count;
  1049.     } else if (doc->link < 0 || doc->link > nlinks) {
  1050.     return 0;
  1051.     }
  1052.     cp = links[doc->link].lname;
  1053.     if (is_url(cp) == FILE_URL_TYPE) {
  1054.     tp = cp;
  1055. #ifndef __EMX__
  1056.     if (!strncmp(tp, "file://localhost", 16)) {
  1057.         tp += 16;
  1058.     } else if (!strncmp(tp, "file:", 5)) {
  1059.         tp += 5;
  1060.     }
  1061. #else
  1062.     if (!strncmp(tp, "file://localhost/", 17)) {
  1063.         tp += 17;
  1064.     } else if (!strncmp(tp, "file:/", 6)) {
  1065.         tp +=6;
  1066.     }
  1067. #endif /* !EMX */
  1068.  
  1069.     strcpy(testpath, tp);
  1070.     HTUnEscape(testpath);
  1071.     if ((i = strlen(testpath)) && testpath[i - 1] == '/')
  1072.         testpath[(i - 1)] = '\0';
  1073.  
  1074.     if (remove_single(testpath)) {
  1075.         if (doc->link == (nlinks - 1))
  1076.         --doc->link;
  1077.         return 1;
  1078.     }
  1079.     }
  1080.     return 0;
  1081. }
  1082.  
  1083. #ifdef OK_PERMIT
  1084. /*
  1085.  *  Table of permission strings and chmod values.
  1086.  *  Makes the code a bit cleaner.
  1087.  */
  1088. static struct {
  1089.     char *string_mode;    /* Key for  value below */
  1090.     long permit_bits;    /* Value for chmod/whatever */
  1091. } permissions[] = {
  1092.     {"IRUSR", S_IRUSR},
  1093.     {"IWUSR", S_IWUSR},
  1094.     {"IXUSR", S_IXUSR},
  1095.     {"IRGRP", S_IRGRP},
  1096.     {"IWGRP", S_IWGRP},
  1097.     {"IXGRP", S_IXGRP},
  1098.     {"IROTH", S_IROTH},
  1099.     {"IWOTH", S_IWOTH},
  1100.     {"IXOTH", S_IXOTH},
  1101.     {NULL, 0}            /* Don't include setuid and friends;
  1102.                    use shell access for that. */
  1103. };
  1104.  
  1105. #ifndef S_ISDIR
  1106. #define S_ISDIR(mode)   ((mode&0xF000) == 0x4000)
  1107. #endif /* !S_ISDIR */
  1108.  
  1109. PRIVATE char LYValidPermitFile[256] = "\0";
  1110.  
  1111. /*
  1112.  *  Handle DIRED permissions.
  1113.  */
  1114. PRIVATE BOOLEAN permit_location ARGS3(
  1115.     char *,     destpath,
  1116.     char *,     srcpath,
  1117.     char **,    newpath)
  1118. {
  1119. #ifndef UNIX
  1120.     _statusline("Sorry, don't know how to permit non-UNIX files yet.");
  1121.     sleep(AlertSecs);
  1122.     return(0);
  1123. #else
  1124.     static char tempfile[256] = "\0";
  1125.     static BOOLEAN first = TRUE;
  1126.     char *cp;
  1127.     char tmpbuf[LINESIZE];
  1128.     struct stat dir_info;
  1129.  
  1130.     if (srcpath) {
  1131.     /*
  1132.      *  Create form.
  1133.      */
  1134.     FILE *fp0;
  1135.     char * user_filename;
  1136.     struct group * grp;
  1137.     char * group_name;
  1138.  
  1139.     /*
  1140.      *  A couple of sanity tests.
  1141.      */
  1142.     srcpath = strip_trailing_slash(srcpath);
  1143.     if (strncmp(srcpath, "file://localhost", 16) == 0)
  1144.         srcpath += 16;
  1145.     if (lstat(srcpath, &dir_info) == -1) {
  1146.         sprintf(tmpbuf, "Unable to get status of '%s'.", srcpath);
  1147.         _statusline(tmpbuf);
  1148.         sleep(AlertSecs);
  1149.         return 0;
  1150.     } else if ((dir_info.st_mode & S_IFMT) != S_IFDIR &&
  1151.         (dir_info.st_mode & S_IFMT) != S_IFREG) {
  1152.         _statusline(
  1153.     "The specified item is not a file nor a directory - request ignored.");
  1154.         sleep(AlertSecs);
  1155.         return(0);
  1156.     }
  1157.  
  1158.     user_filename = srcpath;
  1159.     cp = strrchr(srcpath, '/');
  1160.     if (cp != NULL) {
  1161.         user_filename = (cp + 1);
  1162.     }
  1163.  
  1164.     if (first) {
  1165.         /*
  1166.          *    Get an unused tempfile name. - FM
  1167.          */
  1168.         tempname(tempfile, NEW_FILE);
  1169.     }
  1170.  
  1171.     /*
  1172.      *  Open the tempfile for writing and set its
  1173.      *  protection in case this wasn't done via an
  1174.      *  external umask. - FM
  1175.      */
  1176.     if ((fp0 = LYNewTxtFile(tempfile)) == NULL) {
  1177.         _statusline("Unable to open permit options file");
  1178.         sleep(AlertSecs);
  1179.         return(0);
  1180.     }
  1181.  
  1182.     if (first) {
  1183.         /*
  1184.          *    Make the tempfile a URL.
  1185.          */
  1186.         strcpy(LYPermitFileURL, "file://localhost");
  1187.         strcat(LYPermitFileURL, tempfile);
  1188.         first = FALSE;
  1189.     }
  1190.     StrAllocCopy(*newpath, LYPermitFileURL);
  1191.  
  1192.     grp = getgrgid(dir_info.st_gid);
  1193.     if (grp == NULL) {
  1194.         group_name = "";
  1195.     } else {
  1196.         group_name = grp->gr_name;
  1197.     }
  1198.  
  1199.     LYstrncpy(LYValidPermitFile,
  1200.           srcpath,
  1201.           (sizeof(LYValidPermitFile) - 1));
  1202.  
  1203.     fprintf(fp0, "<Html><Head>\n<Title>%s</Title>\n</Head>\n<Body>\n",
  1204.         PERMIT_OPTIONS_TITLE);
  1205.     fprintf(fp0,"<H1>Permissions for %s</H1>\n", user_filename);
  1206.     {   /*
  1207.          *    Prevent filenames which include '#' or '?' from messing it up.
  1208.          */
  1209.         char * srcpath_url = HTEscape(srcpath, URL_PATH);
  1210.         fprintf(fp0, "<Form Action=\"LYNXDIRED://PERMIT_LOCATION%s\">\n",
  1211.             srcpath_url);
  1212.         FREE(srcpath_url);
  1213.     }
  1214.  
  1215.     fprintf(fp0, "<Ol><Li>Specify permissions below:<Br><Br>\n");
  1216.     fprintf(fp0, "Owner:<Br>\n");
  1217.     fprintf(fp0,
  1218.      "<Input Type=\"checkbox\" Name=\"mode\" Value=\"IRUSR\" %s> Read<Br>\n",
  1219.         (dir_info.st_mode & S_IRUSR) ? "checked" : "");
  1220.     fprintf(fp0,
  1221.     "<Input Type=\"checkbox\" Name=\"mode\" Value=\"IWUSR\" %s> Write<Br>\n",
  1222.         (dir_info.st_mode & S_IWUSR) ? "checked" : "");
  1223.     /*
  1224.      *  If restricted, only change eXecute permissions on directories.
  1225.      */
  1226.     if (!no_change_exec_perms || S_ISDIR(dir_info.st_mode))
  1227.         fprintf(fp0,
  1228.        "<Input Type=\"checkbox\" Name=\"mode\" Value=\"IXUSR\" %s> %s<Br>\n",
  1229.         (dir_info.st_mode & S_IXUSR) ? "checked" : "",
  1230.         S_ISDIR(dir_info.st_mode) ? "Search" : "Execute");
  1231.  
  1232.     fprintf(fp0, "Group %s:<Br>\n", group_name);
  1233.     fprintf(fp0,
  1234.      "<Input Type=\"checkbox\" Name=\"mode\" Value=\"IRGRP\" %s> Read<Br>\n",
  1235.         (dir_info.st_mode & S_IRGRP) ? "checked" : "");
  1236.     fprintf(fp0,
  1237.     "<Input Type=\"checkbox\" Name=\"mode\" Value=\"IWGRP\" %s> Write<Br>\n",
  1238.         (dir_info.st_mode & S_IWGRP) ? "checked" : "");
  1239.     /*
  1240.      *  If restricted, only change eXecute permissions on directories.
  1241.      */
  1242.     if (!no_change_exec_perms || S_ISDIR(dir_info.st_mode))
  1243.         fprintf(fp0,
  1244.        "<Input Type=\"checkbox\" Name=\"mode\" Value=\"IXGRP\" %s> %s<Br>\n",
  1245.         (dir_info.st_mode & S_IXGRP) ? "checked" : "",
  1246.         S_ISDIR(dir_info.st_mode) ? "Search" : "Execute");
  1247.  
  1248.     fprintf(fp0, "Others:<Br>\n");
  1249.     fprintf(fp0,
  1250.      "<Input Type=\"checkbox\" Name=\"mode\" Value=\"IROTH\" %s> Read<Br>\n",
  1251.         (dir_info.st_mode & S_IROTH) ? "checked" : "");
  1252.     fprintf(fp0,
  1253.     "<Input Type=\"checkbox\" Name=\"mode\" Value=\"IWOTH\" %s> Write<Br>\n",
  1254.         (dir_info.st_mode & S_IWOTH) ? "checked" : "");
  1255.     /*
  1256.      *  If restricted, only change eXecute permissions on directories.
  1257.      */
  1258.     if (!no_change_exec_perms || S_ISDIR(dir_info.st_mode))
  1259.         fprintf(fp0,
  1260.        "<Input Type=\"checkbox\" Name=\"mode\" Value=\"IXOTH\" %s> %s<Br>\n",
  1261.         (dir_info.st_mode & S_IXOTH) ? "checked" : "",
  1262.         S_ISDIR(dir_info.st_mode) ? "Search" : "Execute");
  1263.  
  1264.     fprintf(fp0,
  1265. "<Br>\n<Li><Input Type=\"submit\" Value=\"Submit\"> \
  1266. form to permit %s %s.\n</Ol>\n</Form>\n",
  1267.         (dir_info.st_mode & S_IFMT) == S_IFDIR ? "directory" : "file",
  1268.         user_filename);
  1269.     fprintf(fp0, "</Body></Html>");
  1270.     fclose(fp0);
  1271.  
  1272.     LYforce_no_cache = TRUE;
  1273.     return(PERMIT_FORM_RESULT);     /* Special flag for LYMainLoop */
  1274.  
  1275.     } else {                 /* The form being activated. */
  1276.     mode_t new_mode = 0;
  1277.     char *args[5];
  1278.     char amode[10];
  1279.  
  1280.     /*
  1281.      *  Make sure we have a valid set-permission
  1282.      *  file comparison string loaded via a previous
  1283.      *  call with srcpath != NULL. - KW
  1284.      */
  1285.     if (LYValidPermitFile[0] == '\0') {
  1286.         if (LYCursesON)
  1287.         HTAlert(INVALID_PERMIT_URL);
  1288.         else
  1289.         fprintf(stderr, "%s\n", INVALID_PERMIT_URL);
  1290.         CTRACE(tfp, "permit_location: called for <%s>.\n",
  1291.             (destpath ?
  1292.              destpath : "NULL URL pointer"));
  1293.         return 0;
  1294.     }
  1295.     cp = destpath;
  1296.     while (*cp != '\0' && *cp != '?') { /* Find filename */
  1297.         cp++;
  1298.     }
  1299.     if (*cp == '\0') {
  1300.         return(0);    /* Nothing to permit. */
  1301.     }
  1302.     *cp++ = '\0';    /* Null terminate file name and
  1303.                start working on the masks. */
  1304.  
  1305.     HTUnEscape(destpath);    /* Will now operate only on filename part. */
  1306.  
  1307.     /*
  1308.      *  Make sure that the file string is the one from
  1309.      *  the last displayed File Permissions menu. - KW
  1310.      */
  1311.     if (strcmp(destpath, LYValidPermitFile)) {
  1312.         if (LYCursesON)
  1313.         HTAlert(INVALID_PERMIT_URL);
  1314.         else
  1315.         fprintf(stderr, "%s\n", INVALID_PERMIT_URL);
  1316.         CTRACE(tfp, "permit_location: called for file '%s'.\n",
  1317.             destpath);
  1318.         return 0;
  1319.     }
  1320.  
  1321.     /*
  1322.      *  A couple of sanity tests.
  1323.      */
  1324.     destpath = strip_trailing_slash(destpath);
  1325.     if (stat(destpath, &dir_info) == -1) {
  1326.         sprintf(tmpbuf, "Unable to get status of '%s'.", destpath);
  1327.         _statusline(tmpbuf);
  1328.         sleep(AlertSecs);
  1329.         return 0;
  1330.     } else if ((dir_info.st_mode & S_IFMT) != S_IFDIR &&
  1331.         (dir_info.st_mode & S_IFMT) != S_IFREG) {
  1332.         _statusline(
  1333.     "The specified item is not a file nor a directory - request ignored.");
  1334.         sleep(AlertSecs);
  1335.         return 0;
  1336.     }
  1337.  
  1338.     /*
  1339.      *  Cycle over permission strings.
  1340.      */
  1341.     while(*cp != '\0') {
  1342.         char *cr = cp;
  1343.  
  1344.         while(*cr != '\0' && *cr != '&') { /* GET data split by '&'. */
  1345.         cr++;
  1346.         }
  1347.         if (*cr != '\0') {
  1348.         *cr++ = '\0';
  1349.         }
  1350.         if (strncmp(cp, "mode=", 5) == 0) { /* Magic string. */
  1351.         int i;
  1352.  
  1353.         for(i = 0; permissions[i].string_mode != NULL; i++) {
  1354.             if (strcmp(permissions[i].string_mode, cp+5) == 0) {
  1355.             /*
  1356.              *  If restricted, only change eXecute
  1357.              *  permissions on directories.
  1358.              */
  1359.             if (!no_change_exec_perms ||
  1360.                 strchr(cp+5,'X') == NULL ||
  1361.                 S_ISDIR(dir_info.st_mode))
  1362.                 new_mode |= permissions[i].permit_bits;
  1363.             break;
  1364.             }
  1365.         }
  1366.         if (permissions[i].string_mode == NULL) {
  1367.             _statusline("Invalid mode format.");
  1368.             sleep(AlertSecs);
  1369.             return 0;
  1370.         }
  1371.         } else {
  1372.         _statusline("Invalid syntax format.");
  1373.         sleep(AlertSecs);
  1374.         return 0;
  1375.         }
  1376.  
  1377.         cp = cr;
  1378.     }
  1379.  
  1380. #ifdef UNIX
  1381.     /*
  1382.      *  Call chmod().
  1383.      */
  1384.     sprintf(tmpbuf, "chmod %.4o %s", (unsigned int)new_mode, destpath);
  1385.     sprintf(amode, "%.4o", (unsigned int)new_mode);
  1386.     args[0] = "chmod";
  1387.     args[1] = amode;
  1388.     args[2] = destpath;
  1389.     args[3] = (char *) 0;
  1390.     if (LYExecv(CHMOD_PATH, args, tmpbuf) <= 0) {
  1391.         return (-1);
  1392.     }
  1393. #endif /* UNIX */
  1394.     LYforce_no_cache = TRUE;    /* Force update of dired listing. */
  1395.     return 1;
  1396.     }
  1397. #endif /* !UNIX */
  1398. }
  1399. #endif /* OK_PERMIT */
  1400.  
  1401. /*
  1402.  *  Display or remove a tag from a given link.
  1403.  */
  1404. PUBLIC void tagflag ARGS2(
  1405.     int,        flag,
  1406.     int,        cur)
  1407. {
  1408.     if (nlinks > 0) {
  1409.     move(links[cur].ly, 2);
  1410.     stop_reverse();
  1411.     if (flag == ON) {
  1412.         addch('+');
  1413.     } else {
  1414.         addch(' ');
  1415.     }
  1416.  
  1417. #if defined(FANCY_CURSES) || defined(USE_SLANG)
  1418.     if (!LYShowCursor)
  1419.         move((LYlines - 1), (LYcols - 1)); /* get cursor out of the way */
  1420.     else
  1421. #endif /* FANCY CURSES || USE_SLANG */
  1422.         /*
  1423.          *    Never hide the cursor if there's no FANCY CURSES.
  1424.          */
  1425.         move(links[cur].ly, links[cur].lx);
  1426.  
  1427.     refresh();
  1428.     }
  1429. }
  1430.  
  1431. /*
  1432.  *  Handle DIRED tags.
  1433.  */
  1434. PUBLIC void showtags ARGS1(
  1435.     HTList *,    t)
  1436. {
  1437.     int i;
  1438.     HTList *s;
  1439.     char *name;
  1440.  
  1441.     for (i = 0; i < nlinks; i++) {
  1442.     s = t;
  1443.     while ((name = HTList_nextObject(s)) != NULL) {
  1444.         if (!strcmp(links[i].lname, name)) {
  1445.         tagflag(ON, i);
  1446.         break;
  1447.         }
  1448.     }
  1449.     }
  1450. }
  1451.  
  1452. /*
  1453.  *  Perform file management operations for LYNXDIRED URL's.
  1454.  *  Attempt to be consistent.  These are (pseudo) URLs - i.e. they should
  1455.  *  be in URL syntax: some bytes will be URL-escaped with '%'.    This is
  1456.  *  necessary because these (pseudo) URLs will go through some of the same
  1457.  *  kinds of interpretations and mutilations as real ones: HTParse, stripping
  1458.  *  off #fragments etc.  (Some access schemes currently have special rules
  1459.  *  about not escaping parsing '#' "the URL way" built into HTParse, but that
  1460.  *  doesn't look like a clean way.)
  1461.  */
  1462. PUBLIC int local_dired ARGS1(
  1463.     document *,    doc)
  1464. {
  1465.     char *line_url;    /* will point to doc's address, which is a URL */
  1466.     char *line = NULL; /* same as line_url, but HTUnEscaped, will be alloced */
  1467.     char *cp, *tp, *bp;
  1468.     char tmpbuf[256];
  1469.     char buffer[512];
  1470.  
  1471.     line_url = doc->address;
  1472.     CTRACE(tfp, "local_dired: called for <%s>.\n",
  1473.         (line_url ?
  1474.          line_url : "NULL URL pointer"));
  1475.     HTUnEscapeSome(line_url, "/");    /* don't mess too much with *doc */
  1476.  
  1477.     StrAllocCopy(line, line_url);
  1478.     HTUnEscape(line);    /* _file_ (not URL) syntax, for those functions
  1479.                that need it.  Don't forget to FREE it. */
  1480.  
  1481.     tp = NULL;
  1482.     if (!strncmp(line, "LYNXDIRED://NEW_FILE", 20)) {
  1483.     if (create_file(&line[20]) > 0)
  1484.         LYforce_no_cache = TRUE;
  1485.     } else if (!strncmp(line, "LYNXDIRED://NEW_FOLDER", 22)) {
  1486.     if (create_directory(&line[22]) > 0)
  1487.         LYforce_no_cache = TRUE;
  1488.     } else if (!strncmp(line, "LYNXDIRED://INSTALL_SRC", 23)) {
  1489.     local_install(NULL, &line[23], &tp);
  1490.     StrAllocCopy(doc->address, tp);
  1491.     FREE(line);
  1492.     return 0;
  1493.     } else if (!strncmp(line, "LYNXDIRED://INSTALL_DEST", 24)) {
  1494.     local_install(&line[24], NULL, &tp);
  1495.     LYpop(doc);
  1496.     } else if (!strncmp(line, "LYNXDIRED://MODIFY_NAME", 23)) {
  1497.     if (modify_name(&line[23]) > 0)
  1498.     LYforce_no_cache = TRUE;
  1499.     } else if (!strncmp(line, "LYNXDIRED://MODIFY_LOCATION", 27)) {
  1500.     if (modify_location(&line[27]) > 0)
  1501.         LYforce_no_cache = TRUE;
  1502.     } else if (!strncmp(line, "LYNXDIRED://MOVE_TAGGED", 23)) {
  1503.     if (modify_tagged(&line_url[23]) > 0)
  1504.         LYforce_no_cache = TRUE;
  1505. #ifdef OK_PERMIT
  1506.     } else if (!strncmp(line, "LYNXDIRED://PERMIT_SRC", 22)) {
  1507.     permit_location(NULL, &line[22], &tp);
  1508.     if (tp)
  1509.         /*
  1510.          *    One of the checks may have failed.
  1511.          */
  1512.         StrAllocCopy(doc->address, tp);
  1513.     FREE(line);
  1514.     return 0;
  1515.     } else if (!strncmp(line, "LYNXDIRED://PERMIT_LOCATION", 27)) {
  1516.     permit_location(&line_url[27], NULL, &tp);
  1517. #endif /* OK_PERMIT */
  1518.     } else if (!strncmp(line, "LYNXDIRED://REMOVE_SINGLE", 25)) {
  1519.     if (remove_single(&line[25]) > 0)
  1520.         LYforce_no_cache = TRUE;
  1521.     } else if (!strncmp(line, "LYNXDIRED://REMOVE_TAGGED", 25)) {
  1522.     if (remove_tagged())
  1523.         LYforce_no_cache = TRUE;
  1524.     } else if (!strncmp(line, "LYNXDIRED://CLEAR_TAGGED", 24)) {
  1525.     clear_tags();
  1526.     } else if (!strncmp(line, "LYNXDIRED://UPLOAD", 18)) {
  1527.     /*
  1528.      *  They're written by LYUpload_options() HTUnEscaped;
  1529.      *  don't want to change that for now... so pass through
  1530.      *  without more unescaping.  Directory names containing
  1531.      *  '#' will probably fail.
  1532.      */
  1533.     if (LYUpload(line_url))
  1534.         LYforce_no_cache = TRUE;
  1535.     } else {
  1536.     if (line[(strlen(line) - 1)] == '/')
  1537.         line[strlen(line)-1] = '\0';
  1538.     if ((cp = strrchr(line, '/')) == NULL) {
  1539.         FREE(line);
  1540.         return 0;
  1541.     }
  1542.  
  1543.     /*
  1544.      *  Construct the appropriate system command taking care to
  1545.      *  escape all path references to avoid spoofing the shell.
  1546.      */
  1547.     *buffer = '\0';
  1548.     if (!strncmp(line, "LYNXDIRED://DECOMPRESS", 22)) {
  1549.         tp = quote_pathname(line + 22);
  1550.         sprintf(buffer,"%s %s", UNCOMPRESS_PATH, tp);
  1551.         FREE(tp);
  1552.  
  1553. #if defined(OK_UUDECODE) && !defined(ARCHIVE_ONLY)
  1554.     } else if (!strncmp(line, "LYNXDIRED://UUDECODE", 20)) {
  1555.         tp = quote_pathname(line + 20);
  1556.         sprintf(buffer,"%s %s", UUDECODE_PATH, tp);
  1557.         _statusline(
  1558.       "Warning! UUDecoded file will exist in the directory you started Lynx.");
  1559.         sleep(AlertSecs);
  1560.         FREE(tp);
  1561. #endif /* OK_UUDECODE && !ARCHIVE_ONLY */
  1562.  
  1563. #ifdef OK_TAR
  1564. # ifndef ARCHIVE_ONLY
  1565. #  ifdef OK_GZIP
  1566.     } else if (!strncmp(line, "LYNXDIRED://UNTAR_GZ", 20)) {
  1567.         tp = quote_pathname(line+20);
  1568.         *cp++ = '\0';
  1569.         cp = quote_pathname(line + 20);
  1570.         sprintf(buffer, "%s -qdc %s | (cd %s; %s -xf -)",
  1571.                 GZIP_PATH, tp, cp, TAR_PATH);
  1572.         FREE(cp);
  1573.         FREE(tp);
  1574. #  endif /* OK_GZIP */
  1575.  
  1576.     } else if (!strncmp(line, "LYNXDIRED://UNTAR_Z", 19)) {
  1577.         tp = quote_pathname(line + 19);
  1578.         *cp++ = '\0';
  1579.         cp = quote_pathname(line + 19);
  1580.         sprintf(buffer, "%s %s | (cd %s; %s -xf -)",
  1581.                 ZCAT_PATH, tp, cp, TAR_PATH);
  1582.         FREE(cp);
  1583.         FREE(tp);
  1584.  
  1585.     } else if (!strncmp(line, "LYNXDIRED://UNTAR", 17)) {
  1586.         tp = quote_pathname(line + 17);
  1587.         *cp++ = '\0';
  1588.         cp = quote_pathname(line + 17);
  1589.         sprintf(buffer, "cd %s; %s -xf %s", cp, TAR_PATH, tp);
  1590.         FREE(cp);
  1591.         FREE(tp);
  1592. # endif /* !ARCHIVE_ONLY */
  1593.  
  1594. # ifdef OK_GZIP
  1595.     } else if (!strncmp(line, "LYNXDIRED://TAR_GZ", 18)) {
  1596.         *cp++ = '\0';
  1597.         cp = quote_pathname(cp);
  1598.         tp = quote_pathname(line + 18);
  1599.         sprintf(buffer, "(cd %s; %s -cf - %s) | %s -qc >%s/%s.tar.gz",
  1600.                 tp, TAR_PATH, cp, GZIP_PATH, tp, cp);
  1601.         FREE(cp);
  1602.         FREE(tp);
  1603. # endif /* OK_GZIP */
  1604.  
  1605.     } else if (!strncmp(line, "LYNXDIRED://TAR_Z", 17)) {
  1606.         *cp++ = '\0';
  1607.         cp = quote_pathname(cp);
  1608.         tp = quote_pathname(line + 17);
  1609.         sprintf(buffer, "(cd %s; %s -cf - %s) | %s >%s/%s.tar.Z",
  1610.                 tp, TAR_PATH, cp, COMPRESS_PATH, tp, cp);
  1611.         FREE(cp);
  1612.         FREE(tp);
  1613.  
  1614.     } else if (!strncmp(line, "LYNXDIRED://TAR", 15)) {
  1615.         *cp++ = '\0';
  1616.         cp = quote_pathname(cp);
  1617.         tp = quote_pathname(line + 15);
  1618.         sprintf(buffer, "(cd %s; %s -cf %s.tar %s)",
  1619.                 tp, TAR_PATH, cp, cp);
  1620.         FREE(cp);
  1621.         FREE(tp);
  1622. #endif /* OK_TAR */
  1623.  
  1624. #ifdef OK_GZIP
  1625.     } else if (!strncmp(line, "LYNXDIRED://GZIP", 16)) {
  1626.         tp = quote_pathname(line + 16);
  1627.         sprintf(buffer, "%s -q %s", GZIP_PATH, tp);
  1628.         FREE(tp);
  1629. #ifndef ARCHIVE_ONLY
  1630.     } else if (!strncmp(line, "LYNXDIRED://UNGZIP", 18)) {
  1631.         tp = quote_pathname(line + 18);
  1632.         sprintf(buffer, "%s -d %s", GZIP_PATH, tp);
  1633.         FREE(tp);
  1634. #endif /* !ARCHIVE_ONLY */
  1635. #endif /* OK_GZIP */
  1636.  
  1637. #ifdef OK_ZIP
  1638.     } else if (!strncmp(line, "LYNXDIRED://ZIP", 15)) {
  1639.         tp = quote_pathname(line + 15);
  1640.         *cp++ = '\0';
  1641.         bp = quote_pathname(cp);
  1642.         cp = quote_pathname(line + 15);
  1643.         sprintf(buffer, "cd %s; %s -rq %s.zip %s", cp, ZIP_PATH, tp, bp);
  1644.         FREE(cp);
  1645.         FREE(bp);
  1646.         FREE(tp);
  1647. #ifndef ARCHIVE_ONLY
  1648.     } else if (!strncmp(line, "LYNXDIRED://UNZIP", 17)) {
  1649.         tp = quote_pathname(line + 17);
  1650.         *cp = '\0';
  1651.         cp = quote_pathname(line + 17);
  1652.         sprintf(buffer, "cd %s; %s -q %s", cp, UNZIP_PATH, tp);
  1653.         FREE(cp);
  1654.         FREE(tp);
  1655. # endif /* !ARCHIVE_ONLY */
  1656. #endif /* OK_ZIP */
  1657.  
  1658.     } else if (!strncmp(line, "LYNXDIRED://COMPRESS", 20)) {
  1659.         tp = quote_pathname(line + 20);
  1660.         sprintf(buffer, "%s %s", COMPRESS_PATH, tp);
  1661.         FREE(tp);
  1662.     }
  1663.  
  1664.     if (strlen(buffer)) {
  1665.         if (strlen(buffer) < 60) {
  1666.         sprintf(tmpbuf, "Executing %s ", buffer);
  1667.         } else {
  1668.         sprintf(tmpbuf,
  1669.             "Executing system command. This might take a while.");
  1670.         }
  1671.         _statusline(tmpbuf);
  1672.         stop_curses();
  1673.         printf("%s\n", tmpbuf);
  1674.         fflush(stdout);
  1675.         system(buffer);
  1676. #ifdef VMS
  1677.         extern BOOLEAN HadVMSInterrupt
  1678.         HadVMSInterrupt = FALSE;
  1679. #endif /* VMS */
  1680.         start_curses();
  1681.         LYforce_no_cache = TRUE;
  1682.     }
  1683.     }
  1684.  
  1685.     FREE(line);
  1686.     LYpop(doc);
  1687.     return 0;
  1688. }
  1689.  
  1690. /*
  1691.  *  Provide a menu of file management options.
  1692.  */
  1693. PUBLIC int dired_options ARGS2(
  1694.     document *,    doc,
  1695.     char **,    newfile)
  1696. {
  1697.     static char tempfile[256];
  1698.     static BOOLEAN first = TRUE;
  1699.     char path[512], dir[512]; /* much too large */
  1700.     char tmpbuf[LINESIZE];
  1701.     lynx_html_item_type *nxt;
  1702.     struct stat dir_info;
  1703.     FILE *fp0;
  1704.     char *cp = NULL;
  1705.     char *dir_url = NULL;    /* Will hold URL-escaped path of
  1706.                    directory from where DIRED_MENU was
  1707.                    invoked (NOT its full URL). */
  1708.     char *path_url = NULL;    /* Will hold URL-escaped path of file
  1709.                    (or directory) which was selected
  1710.                    when DIRED_MENU was invoked (NOT
  1711.                    its full URL). */
  1712.     BOOLEAN nothing_tagged;
  1713.     int count;
  1714.     struct dired_menu *mp;
  1715.     char buf[2048];
  1716.  
  1717.  
  1718.     if (first) {
  1719.     /*
  1720.      *  Get an unused tempfile name. - FM
  1721.      */
  1722.     tempname(tempfile, NEW_FILE);
  1723.     }
  1724.  
  1725.     /*
  1726.      *    Open the tempfile for writing and set its
  1727.      *    protection in case this wasn't done via an
  1728.      *    external umask. - FM
  1729.      */
  1730.     if ((fp0 = LYNewTxtFile(tempfile)) == NULL) {
  1731.     _statusline("Unable to open file management menu file.");
  1732.     sleep(AlertSecs);
  1733.     return(0);
  1734.     }
  1735.  
  1736.     if (first) {
  1737.     /*
  1738.      *  Make the tempfile a URL.
  1739.      */
  1740.     strcpy(LYDiredFileURL, "file://localhost");
  1741.     strcat(LYDiredFileURL, tempfile);
  1742.     first = FALSE;
  1743.     }
  1744.     StrAllocCopy(*newfile, LYDiredFileURL);
  1745.  
  1746.     cp = doc->address;
  1747.     if (!strncmp(cp, "file://localhost", 16)) {
  1748.     cp += 16;
  1749.     } else if (!strncmp(cp, "file:", 5)) {
  1750.     cp += 5;
  1751.     }
  1752.     strcpy(dir, cp);
  1753.     StrAllocCopy(dir_url, cp);
  1754.     if (dir_url[(strlen(dir_url) - 1)] == '/')
  1755.     dir_url[(strlen(dir_url) - 1)] = '\0';
  1756.     HTUnEscape(dir);
  1757.     if (dir[(strlen(dir) - 1)] == '/')
  1758.     dir[(strlen(dir) - 1)] = '\0';
  1759.  
  1760.     if (doc->link > -1 && doc->link < (nlinks+1)) {
  1761.     cp = links[doc->link].lname;
  1762.     if (!strncmp(cp, "file://localhost", 16)) {
  1763.         cp += 16;
  1764.     } else if (!strncmp(cp, "file:", 5)) {
  1765.         cp += 5;
  1766.     }
  1767.     strcpy(path, cp);
  1768.     StrAllocCopy(path_url, cp);
  1769.     if (*path_url && path_url[1] && path_url[(strlen(path_url) - 1)] == '/')
  1770.         path_url[(strlen(path_url) - 1)] = '\0';
  1771.     HTUnEscape(path);
  1772.     if (*path && path[1] && path[(strlen(path) - 1)] == '/')
  1773.         path[(strlen(path) - 1)] = '\0';
  1774.  
  1775.     if (lstat(path, &dir_info) == -1 && stat(path, &dir_info) == -1) {
  1776.         sprintf(tmpbuf, "Unable to get status of '%s'.", path);
  1777.         _statusline(tmpbuf);
  1778.         sleep(AlertSecs);
  1779.         FREE(dir_url);
  1780.         FREE(path_url);
  1781.         return 0;
  1782.     }
  1783.  
  1784.     } else {
  1785.     path[0] = '\0';
  1786.     StrAllocCopy(path_url, path);
  1787.     }
  1788.  
  1789.     nothing_tagged = (HTList_isEmpty(tagged));
  1790.  
  1791.     fprintf(fp0,
  1792.         "<head>\n<title>%s</title></head>\n<body>\n", DIRED_MENU_TITLE);
  1793.  
  1794.     fprintf(fp0,
  1795.         "\n<h1>File Management Options (%s Version %s)</h1>",
  1796.         LYNX_NAME, LYNX_VERSION);
  1797.  
  1798.     fprintf(fp0, "Current directory is %s<br>\n", dir);
  1799.  
  1800.     if (nothing_tagged) {
  1801.     if (strlen(path)) {
  1802.         fprintf(fp0, "Current selection is %s<p>\n", path);
  1803.     } else {
  1804.         fprintf(fp0, "Nothing currently selected.<p>\n");
  1805.     }
  1806.     } else {
  1807.     /*
  1808.      *  Write out number of tagged items, and names of first
  1809.      *  few of them relative to current (in the DIRED sense)
  1810.      *  directory.
  1811.      */
  1812.     int n = HTList_count(tagged);
  1813.     char *cp1 = NULL;
  1814.     char *cd = NULL;
  1815.     int i, m;
  1816. #define NUM_TAGS_TO_WRITE 10
  1817.     fprintf(fp0, "Current selection is %d tagged item%s",
  1818.              n, ((n == 1) ? ":" : "s:"));
  1819.     StrAllocCopy(cd, doc->address);
  1820.     HTUnEscapeSome(cd, "/");
  1821.     if (*cd && cd[(strlen(cd) - 1)] != '/')
  1822.         StrAllocCat(cd, "/");
  1823.     m = (n < NUM_TAGS_TO_WRITE) ? n : NUM_TAGS_TO_WRITE;
  1824.     for (i = 1; i <= m; i++) {
  1825.         cp1 = HTRelative(HTList_objectAt(tagged, i-1),
  1826.                  (*cd ? cd : "file://localhost"));
  1827.         HTUnEscape(cp1);
  1828.         LYEntify(&cp1, TRUE); /* _should_ do this everywhere... */
  1829.         fprintf(fp0, "%s <br>\n    %s",
  1830.              (i == 1 ? "" : " ,"), cp1);
  1831.         FREE(cp1);
  1832.     }
  1833.     if (n > m) {
  1834.         fprintf(fp0," , ...");
  1835.     }
  1836.     fprintf(fp0, "<p>\n");
  1837.     FREE(cd);
  1838.     }
  1839.  
  1840.     /*
  1841.      *    If menu_head is NULL then use defaults and link them together now.
  1842.      */
  1843.     if (menu_head == NULL) {
  1844.     for (mp = defmenu; mp->href != NULL; mp++)
  1845.         mp->next = (mp + 1);
  1846.     (--mp)->next = NULL;
  1847.     menu_head = defmenu;
  1848.     }
  1849.  
  1850.     for (mp = menu_head; mp != NULL; mp = mp->next) {
  1851.     if (mp->cond != DE_TAG && !nothing_tagged)
  1852.         continue;
  1853.     if (mp->cond == DE_TAG && nothing_tagged)
  1854.         continue;
  1855.     if (mp->cond == DE_DIR &&
  1856.         (!*path || (dir_info.st_mode & S_IFMT) != S_IFDIR))
  1857.         continue;
  1858.     if (mp->cond == DE_FILE &&
  1859.         (!*path || (dir_info.st_mode & S_IFMT) != S_IFREG))
  1860.         continue;
  1861. #ifdef S_IFLNK
  1862.     if (mp->cond == DE_SYMLINK &&
  1863.         (!*path || (dir_info.st_mode & S_IFMT) != S_IFLNK))
  1864.         continue;
  1865. #endif
  1866.     if (*mp->sfx &&
  1867.         (strlen(path) < strlen(mp->sfx) ||
  1868.          strcmp(mp->sfx, &path[(strlen(path) - strlen(mp->sfx))]) != 0))
  1869.         continue;
  1870.     fprintf(fp0, "<a href=\"%s",
  1871.         render_item(mp->href, path_url, dir_url, buf,2048, YES));
  1872.     fprintf(fp0, "\">%s</a> ",
  1873.         render_item(mp->link, path, dir, buf,2048, NO));
  1874.     fprintf(fp0, "%s<br>\n",
  1875.         render_item(mp->rest, path, dir, buf,2048, NO));
  1876.     }
  1877.  
  1878.     if (uploaders != NULL) {
  1879.     fprintf(fp0, "<p>Upload to current directory:<p>\n");
  1880.     for (count = 0, nxt = uploaders;
  1881.          nxt != NULL;
  1882.          nxt = nxt->next, count++) {
  1883.         fprintf(fp0,
  1884.         "<a href=\"LYNXDIRED://UPLOAD=%d/TO=%s\"> %s </a><br>\n",
  1885.             count, dir, nxt->name);
  1886.     }
  1887.     }
  1888.  
  1889.     fprintf(fp0, "</body>\n");
  1890.     fclose(fp0);
  1891.  
  1892.     FREE(dir_url);
  1893.     FREE(path_url);
  1894.  
  1895.     LYforce_no_cache = TRUE;
  1896.  
  1897.     return(0);
  1898. }
  1899.  
  1900. /*
  1901.  *  Check DIRED filename.
  1902.  */
  1903. PRIVATE char *filename ARGS3(
  1904.     char *,     prompt,
  1905.     char *,     buf,
  1906.     size_t,     bufsize)
  1907. {
  1908.     char *cp;
  1909.  
  1910.     _statusline(prompt);
  1911.  
  1912.     *buf = '\0';
  1913.     LYgetstr(buf, VISIBLE, bufsize, NORECALL);
  1914.     if (strstr(buf, "../") != NULL) {
  1915.     _statusline("Illegal filename; request ignored.");
  1916.     sleep(AlertSecs);
  1917.     return NULL;
  1918.     }
  1919.  
  1920.     if (no_dotfiles || !show_dotfiles) {
  1921.     cp = strrchr(buf, '/'); /* find last slash */
  1922.     if (cp)
  1923.         cp += 1;
  1924.     else
  1925.         cp = buf;
  1926.     if (*cp == '.') {
  1927.         _statusline("Illegal filename; request ignored.");
  1928.         sleep(AlertSecs);
  1929.         return NULL;
  1930.     }
  1931.     }
  1932.     return buf;
  1933. }
  1934.  
  1935. /*
  1936.  *  Install the specified file or directory.
  1937.  */
  1938. PUBLIC BOOLEAN local_install ARGS3(
  1939.     char *,     destpath,
  1940.     char *,     srcpath,
  1941.     char **,    newpath)
  1942. {
  1943.     char tmpbuf[512];
  1944.     static char savepath[512]; /* This will be the link that
  1945.                   is to be installed. */
  1946.     struct stat dir_info;
  1947.     char *args[6];
  1948.     HTList *tag;
  1949.     int count = 0;
  1950.     int n = 0, src;    /* indices into 'args[]' */
  1951.  
  1952.     /*
  1953.      *    Determine the status of the selected item.
  1954.      */
  1955.     if (srcpath) {
  1956.     srcpath = strip_trailing_slash(srcpath);
  1957.     if (strncmp(srcpath, "file://localhost", 16) == 0)
  1958.         srcpath += 16;
  1959.     if (stat(srcpath, &dir_info) == -1) {
  1960.         sprintf(tmpbuf, "Unable to get status of '%s'.", srcpath);
  1961.         _statusline(tmpbuf);
  1962.         sleep(AlertSecs);
  1963.         return 0;
  1964.     } else if ((dir_info.st_mode & S_IFMT) != S_IFDIR &&
  1965.            (dir_info.st_mode & S_IFMT) != S_IFREG) {
  1966.         _statusline(
  1967.       "The selected item is not a file or a directory! Request ignored.");
  1968.         sleep(AlertSecs);
  1969.         return 0;
  1970.     }
  1971.     strcpy(savepath, srcpath);
  1972.     LYforce_no_cache = TRUE;
  1973.     strcpy(tmpbuf, "file://localhost");
  1974.     strcat(tmpbuf, Home_Dir());
  1975.     strcat(tmpbuf, "/.installdirs.html");
  1976.     StrAllocCopy(*newpath, tmpbuf);
  1977.     return 0;
  1978.     }
  1979.  
  1980.     destpath = strip_trailing_slash(destpath);
  1981.  
  1982.     if (stat(destpath,&dir_info) == -1) {
  1983.     sprintf(tmpbuf,"Unable to get status of '%s'.",destpath);
  1984.     _statusline(tmpbuf);
  1985.     sleep(AlertSecs);
  1986.     return 0;
  1987.     } else if ((dir_info.st_mode & S_IFMT) != S_IFDIR) {
  1988.     _statusline(
  1989.         "The selected item is not a directory! Request ignored.");
  1990.     sleep(AlertSecs);
  1991.     return 0;
  1992.     } else if (0 /*directory not writeable*/) {
  1993.     _statusline("Install in the selected directory not permitted.");
  1994.     sleep(AlertSecs);
  1995.     return 0;
  1996.     }
  1997.  
  1998.     statusline("Just a moment, ...");
  1999.     args[n++] = "install";
  2000. #ifdef INSTALL_ARGS
  2001.     args[n++] = INSTALL_ARGS;
  2002. #endif /* INSTALL_ARGS */
  2003.     src = n++;
  2004.     args[n++] = destpath;
  2005.     args[n] = (char *)0;
  2006.     sprintf(tmpbuf, "install %s", destpath);
  2007.     tag = tagged;
  2008.  
  2009.     if (HTList_isEmpty(tagged)) {
  2010.     args[src] = savepath;
  2011.     if (LYExecv(INSTALL_PATH, args, tmpbuf) <= 0)
  2012.         return (-1);
  2013.     count++;
  2014.     } else {
  2015.     char *name;
  2016.     while ((name = (char *)HTList_nextObject(tag))) {
  2017.         args[src] = name;
  2018.         if (strncmp("file://localhost", args[src], 16) == 0)
  2019.          args[src] = (name + 16);
  2020.  
  2021.         if (LYExecv(INSTALL_PATH, args, tmpbuf) <= 0)
  2022.         return ((count == 0) ? -1 : count);
  2023.         count++;
  2024.     }
  2025.     clear_tags();
  2026.     }
  2027.     statusline("Installation complete");
  2028.     sleep(InfoSecs);
  2029.     return count;
  2030. }
  2031.  
  2032. /*
  2033.  *  Clear DIRED tags.
  2034.  */
  2035. PUBLIC void clear_tags NOARGS
  2036. {
  2037.     char *cp = NULL;
  2038.  
  2039.     while ((cp = HTList_removeLastObject(tagged)) != NULL) {
  2040.     FREE(cp);
  2041.     }
  2042.     if (HTList_isEmpty(tagged))
  2043.     FREE(tagged);
  2044. }
  2045.  
  2046. /*
  2047.  *  Handle DIRED menu item.
  2048.  */
  2049. PUBLIC void add_menu_item ARGS1(
  2050.     char *,     str)
  2051. {
  2052.     struct dired_menu *new, *mp;
  2053.     char *cp;
  2054.  
  2055.     /*
  2056.      *    First custom menu definition causes entire default menu to be
  2057.      *    discarded.
  2058.      */
  2059.     if (menu_head == defmenu)
  2060.     menu_head = NULL;
  2061.  
  2062.     new = (struct dired_menu *)calloc(1, sizeof(*new));
  2063.  
  2064.     /*
  2065.      *    Conditional on tagged != NULL ?
  2066.      */
  2067.     cp = strchr(str, ':');
  2068.     *cp++ = '\0';
  2069.     if (strcasecomp(str, "tag") == 0) {
  2070.     new->cond = DE_TAG;
  2071.     } else if (strcasecomp(str, "dir") == 0) {
  2072.     new->cond = DE_DIR;
  2073.     } else if (strcasecomp(str, "file") == 0) {
  2074.     new->cond = DE_FILE;
  2075.     } else if (strcasecomp(str, "link") == 0) {
  2076.     new->cond = DE_SYMLINK;
  2077.     }
  2078.  
  2079.     /*
  2080.      *    Conditional on matching suffix.
  2081.      */
  2082.     str = cp;
  2083.     cp = strchr(str, ':');
  2084.     *cp++ = '\0';
  2085.     StrAllocCopy(new->sfx, str);
  2086.  
  2087.     str = cp;
  2088.     cp = strchr(str, ':');
  2089.     *cp++ = '\0';
  2090.     StrAllocCopy(new->link, str);
  2091.  
  2092.     str = cp;
  2093.     cp = strchr(str, ':');
  2094.     *cp++ = '\0';
  2095.     StrAllocCopy(new->rest, str);
  2096.  
  2097.     StrAllocCopy(new->href, cp);
  2098.  
  2099.     if (menu_head) {
  2100.     for (mp = menu_head; mp && mp->next != NULL; mp = mp->next)
  2101.         ;
  2102.     mp->next = new;
  2103.     } else
  2104.     menu_head = new;
  2105. }
  2106.  
  2107. /*
  2108.  *  Create URL for DIRED HREF value.
  2109.  */
  2110. PRIVATE char * render_item ARGS6(
  2111.     char *,     s,
  2112.     char *,     path,
  2113.     char *,     dir,
  2114.     char *,     buf,
  2115.     int,        bufsize,
  2116.     BOOLEAN,    url_syntax)
  2117. {
  2118.     char *cp;
  2119.     char *bp;
  2120.     char overrun = '\0';
  2121.     char *taglist = NULL;
  2122. #define BP_INC (bp>buf+bufsize-2 ?  &overrun : bp++)
  2123.                 /* Buffer overrun could happen for very long
  2124.                    tag list, if %l or %t are used */
  2125.     bp = buf;
  2126.     while (*s && !overrun) {
  2127.     if (*s == '%') {
  2128.         s++;
  2129.         switch (*s) {
  2130.         case '%':
  2131.             *BP_INC = '%';
  2132. #ifdef NOTDEFINED
  2133.             /*
  2134.              *    These chars come from lynx.cfg or the default, let's
  2135.              *    just assume there won't be any improper %'s there that
  2136.              *    would need escaping.
  2137.              */
  2138.             if(url_syntax) {
  2139.             *BP_INC = '2';
  2140.             *BP_INC = '5';
  2141.             }
  2142. #endif /* NOTDEFINED */
  2143.             break;
  2144.         case 'p':
  2145.             cp = path;
  2146.             while (*cp)
  2147.             *BP_INC = *cp++;
  2148.             break;
  2149.         case 'd':
  2150.             cp = dir;
  2151.             while (*cp)
  2152.             *BP_INC = *cp++;
  2153.             break;
  2154.         case 'f':
  2155.             cp = strrchr(path, '/');
  2156.             if (cp)
  2157.             cp++;
  2158.             else
  2159.             cp = path;
  2160.             while (*cp)
  2161.             *BP_INC = *cp++;
  2162.             break;
  2163.         case 'l':
  2164.         case 't':
  2165.             if (!HTList_isEmpty(tagged)) {
  2166.             HTList *cur = tagged;
  2167.             char *name;
  2168.  
  2169.             while (!overrun &&
  2170.                    (name = (char *)HTList_nextObject(cur))!=NULL) {
  2171.                 if (*s == 'l' && (cp = strrchr(name, '/')))
  2172.                 cp++;
  2173.                 else
  2174.                 cp = name;
  2175.                 StrAllocCat(taglist, cp);
  2176.                 StrAllocCat(taglist, " "); /* should this be %20?*/
  2177.             }
  2178.             }
  2179.             if (taglist) {
  2180.             /* could HTUnescape here... */
  2181.             cp = taglist;
  2182.             while (*cp)
  2183.                 *BP_INC = *cp++;
  2184.             FREE(taglist);
  2185.             }
  2186.             break;
  2187.         default:
  2188.             *BP_INC = '%';
  2189. #ifdef NOTDEFINED
  2190.             if (url_syntax) {
  2191.             *BP_INC = '2';
  2192.             *BP_INC = '5';
  2193.             }
  2194. #endif /* NOTDEFINED */
  2195.             *BP_INC =*s;
  2196.             break;
  2197.         }
  2198.     } else {
  2199.         /*
  2200.          *    Other chars come from the lynx.cfg or
  2201.          *    the default. Let's assume there isn't
  2202.          *    anything weird there that needs escaping.
  2203.          */
  2204.         *BP_INC =*s;
  2205.     }
  2206.     s++;
  2207.     }
  2208.     if (overrun & url_syntax) {
  2209.     sprintf(buf,"Temporary URL or list would be too long.");
  2210.     _statusline(buf);
  2211.     sleep(AlertSecs);
  2212.     bp = buf;    /* set to start, will return empty string as URL */
  2213.     }
  2214.     *bp = '\0';
  2215.     return buf;
  2216. }
  2217. #endif /* DIRED_SUPPORT */
  2218.  
  2219. /*
  2220.  *  Execute DIRED command.
  2221.  */
  2222. PRIVATE int LYExecv ARGS3(
  2223.     char *,     path,
  2224.     char **,    argv,
  2225.     char *,     msg)
  2226. {
  2227. #if defined(VMS) || defined(_WINDOWS)
  2228.     CTRACE(tfp, "LYExecv:  Called inappropriately!\n");
  2229.     return(0);
  2230. #else
  2231.     int rc;
  2232.     char tmpbuf[512];
  2233.     pid_t pid;
  2234. #if HAVE_TYPE_UNIONWAIT
  2235.     union wait wstatus;
  2236. #else
  2237.     int wstatus;
  2238. #endif
  2239.  
  2240.     rc = 1;        /* It will work */
  2241.     tmpbuf[0] = '\0';    /* empty buffer for alert messages */
  2242.     stop_curses();
  2243.     pid = fork();    /* fork and execute rm */
  2244.     switch (pid) {
  2245.     case -1:
  2246.         sprintf(tmpbuf, "Unable to %s due to system error!", msg);
  2247.         rc = 0;
  2248.         break;    /* don't fall thru! - KW */
  2249.     case 0:  /* child */
  2250.         execv(path, argv);
  2251.         exit(-1);    /* execv failed, give wait() something to look at */
  2252.     default:  /* parent */
  2253. #if !HAVE_WAITPID
  2254.         while (wait(&wstatus) != pid)
  2255.         ; /* do nothing */
  2256. #else
  2257.         while (-1 == waitpid(pid, &wstatus, 0)) { /* wait for child */
  2258. #ifdef EINTR
  2259.         if (errno == EINTR)
  2260.             continue;
  2261. #endif /* EINTR */
  2262. #ifdef ERESTARTSYS
  2263.         if (errno == ERESTARTSYS)
  2264.             continue;
  2265. #endif /* ERESTARTSYS */
  2266.         break;
  2267.         }
  2268. #endif /* !HAVE_WAITPID */
  2269.         if (WEXITSTATUS(wstatus) != 0 ||
  2270.         WTERMSIG(wstatus) > 0)    { /* error return */
  2271.         sprintf(tmpbuf, "Probable failure to %s due to system error!",
  2272.                 msg);
  2273.         rc = 0;
  2274.         }
  2275.     }
  2276.  
  2277.     if (rc == 0) {
  2278.     /*
  2279.      *  Screen may have message from the failed execv'd command.
  2280.      *  Give user time to look at it before screen refresh.
  2281.      */
  2282.     sleep(AlertSecs);
  2283.     }
  2284.     start_curses();
  2285.     if (tmpbuf[0]) {
  2286.     _statusline(tmpbuf);
  2287.     sleep(AlertSecs);
  2288.     }
  2289.  
  2290.     return(rc);
  2291. #endif /* VMS */
  2292. }
  2293.