1 /*
2 * $LynxId: LYLocal.c,v 1.134 2021/07/30 08:23:29 tom Exp $
3 *
4 * Routines to manipulate the local filesystem.
5 * Written by: Rick Mallett, Carleton University
6 * Report problems to rmallett@ccs.carleton.ca
7 * Modified 18-Dec-95 David Trueman (david@cs.dal.ca):
8 * Added OK_PERMIT compilation option.
9 * Support replacement of compiled-in f)ull menu configuration via
10 * DIRED_MENU definitions in lynx.cfg, so that more than one menu
11 * can be driven by the same executable.
12 * Modified Oct-96 Klaus Weide (kweide@tezcat.com):
13 * Changed to use the library's HTList_* functions and macros for
14 * managing the list of tagged file URLs.
15 * Keep track of proper level of URL escaping, so that unusual filenames
16 * which contain #% etc. are handled properly (some HTUnEscapeSome()'s
17 * left in to be conservative, and to document where superfluous
18 * unescaping took place before).
19 * Dynamic memory instead of fixed length buffers in a few cases.
20 * Other minor changes to make things work as intended.
21 * Modified Jun-97 Klaus Weide (kweide@tezcat.com) & FM:
22 * Modified the code handling DIRED_MENU to do more careful
23 * checking of the selected file. In addition to "TAG", "FILE", and
24 * "DIR", DIRED_MENU definitions in lynx.cfg now also recognize LINK as
25 * a type. DIRED_MENU definitions with a type field of "LINK" are only
26 * used if the current selection is a symbolic link ("FILE" and "DIR"
27 * definitions are not used in that case). The default menu
28 * definitions have been updated to reflect this change, and to avoid
29 * the showing of menu items whose action would always fail - KW
30 * Cast all code into the Lynx programming style. - FM
31 */
32
33 #include <HTUtils.h>
34 #include <HTAAProt.h>
35 #include <HTFile.h>
36 #include <HTAlert.h>
37 #include <HTParse.h>
38 #include <LYCurses.h>
39 #include <LYGlobalDefs.h>
40 #include <LYUtils.h>
41 #include <LYStrings.h>
42 #include <LYCharUtils.h>
43 #include <LYStructs.h>
44 #include <LYHistory.h>
45 #include <LYUpload.h>
46 #include <LYLocal.h>
47 #include <LYClean.h>
48 #include <www_wait.h>
49
50 #ifdef SUPPORT_CHDIR
51 #include <LYMainLoop.h>
52 #endif
53
54 #include <LYLeaks.h>
55
56 #undef USE_COMPRESS
57
58 #ifdef __DJGPP__
59 #define EXT_TAR_GZ ".tgz"
60 #define EXT_TAR_Z ".taz"
61 #define EXT_Z ".z"
62 #else
63 #define EXT_TAR_GZ ".tar.gz"
64 #define EXT_TAR_Z ".tar.Z"
65 #define EXT_Z ".Z"
66 #endif
67
68 #ifndef DIRED_MAXBUF
69 #define DIRED_MAXBUF 512
70 #endif
71
72 #ifdef DIRED_SUPPORT
73
74 #ifdef OK_INSTALL
75 #ifdef FNAMES_8_3
76 #define INSTALLDIRS_FILE "instdirs.htm"
77 #else
78 #define INSTALLDIRS_FILE ".installdirs.html"
79 #endif /* FNAMES_8_3 */
80 #endif /* OK_INSTALL */
81
82 static int get_filename(const char *prompt,
83 bstring **buf);
84
85 #ifdef OK_PERMIT
86 static int permit_location(char *destpath,
87 char *srcpath,
88 char **newpath);
89 #endif /* OK_PERMIT */
90 /* *INDENT-OFF* */
91 static char *render_item ( const char * s,
92 const char * path,
93 const char * dir,
94 char * buf,
95 size_t bufsize,
96 int url_syntax);
97
98 struct dired_menu {
99 int cond;
100 #define DE_TAG 1
101 #define DE_DIR 2
102 #define DE_FILE 3
103 #define DE_SYMLINK 4
104 char *sfx;
105 const char *c_sfx;
106 char *link;
107 const char *c_link;
108 char *rest;
109 const char *c_rest;
110 char *href;
111 const char *c_href;
112 struct dired_menu *next;
113 };
114
115 #define GetDiredSuffix(p) ((p)->sfx ? (p)->sfx : (p)->c_sfx)
116 #define GetDiredLink(p) ((p)->link ? (p)->link : (p)->c_link)
117 #define GetDiredRest(p) ((p)->rest ? (p)->rest : (p)->c_rest)
118 #define GetDiredHref(p) ((p)->href ? (p)->href : (p)->c_href)
119
120 #undef DATA
121 #define DATA(cond, sfx, link, rest, href) { \
122 cond, \
123 NULL, sfx, \
124 NULL, link, \
125 NULL, rest, \
126 NULL, href, \
127 NULL }
128
129 static struct dired_menu *menu_head = NULL;
130 static struct dired_menu defmenu[] = {
131
132 /*
133 * The following initializations determine the contents of the f)ull menu
134 * selection when in dired mode. If any menu entries are defined in the
135 * configuration file via DIRED_MENU lines, then these default entries are
136 * discarded entirely.
137 */
138 #ifdef SUPPORT_CHDIR
139 DATA( 0, "", "Change directory",
140 "", "LYNXDIRED://CHDIR"),
141 #endif
142 DATA( 0, "", "New File",
143 "(in current directory)", "LYNXDIRED://NEW_FILE%d"),
144
145 DATA( 0, "", "New Directory",
146 "(in current directory)", "LYNXDIRED://NEW_FOLDER%d"),
147
148 #ifdef OK_INSTALL
149 DATA( DE_FILE, "", "Install",
150 "selected file to new location", "LYNXDIRED://INSTALL_SRC%p"),
151 /* The following (installing a directory) doesn't work for me, at least
152 with the "install" from GNU fileutils 4.0. I leave it in anyway, in
153 case one compiles with INSTALL_PATH / INSTALL_ARGS defined to some
154 other command for which it works (like a script, or maybe "cp -a"). - kw
155 */
156 DATA( DE_DIR, "", "Install",
157 "selected directory to new location", "LYNXDIRED://INSTALL_SRC%p"),
158 #endif /* OK_INSTALL */
159
160 DATA( DE_FILE, "", "Modify File Name",
161 "(of current selection)", "LYNXDIRED://MODIFY_NAME%p"),
162 DATA( DE_DIR, "", "Modify Directory Name",
163 "(of current selection)", "LYNXDIRED://MODIFY_NAME%p"),
164 #ifdef S_IFLNK
165 DATA( DE_SYMLINK, "", "Modify Name",
166 "(of selected symbolic link)", "LYNXDIRED://MODIFY_NAME%p"),
167 #endif /* S_IFLNK */
168
169 #ifdef OK_PERMIT
170 DATA( DE_FILE, "", "Modify File Permissions",
171 "(of current selection)", "LYNXDIRED://PERMIT_SRC%p"),
172 DATA( DE_DIR, "", "Modify Directory Permissions",
173 "(of current selection)", "LYNXDIRED://PERMIT_SRC%p"),
174 #endif /* OK_PERMIT */
175
176 DATA( DE_FILE, "", "Change Location",
177 "(of selected file)" , "LYNXDIRED://MODIFY_LOCATION%p"),
178 DATA( DE_DIR, "", "Change Location",
179 "(of selected directory)", "LYNXDIRED://MODIFY_LOCATION%p"),
180 #ifdef S_IFLNK
181 DATA( DE_SYMLINK, "", "Change Location",
182 "(of selected symbolic link)", "LYNXDIRED://MODIFY_LOCATION%p"),
183 #endif /* S_IFLNK */
184
185 DATA( DE_FILE, "", "Remove File",
186 "(current selection)", "LYNXDIRED://REMOVE_SINGLE%p"),
187 DATA( DE_DIR, "", "Remove Directory",
188 "(current selection)", "LYNXDIRED://REMOVE_SINGLE%p"),
189 #ifdef S_IFLNK
190 DATA( DE_SYMLINK, "", "Remove Symbolic Link",
191 "(current selection)", "LYNXDIRED://REMOVE_SINGLE%p"),
192 #endif /* S_IFLNK */
193
194 #if defined(OK_UUDECODE) && !defined(ARCHIVE_ONLY)
195 DATA( DE_FILE, "", "UUDecode",
196 "(current selection)", "LYNXDIRED://UUDECODE%p"),
197 #endif /* OK_UUDECODE && !ARCHIVE_ONLY */
198
199 #if defined(OK_TAR) && !defined(ARCHIVE_ONLY)
200 DATA( DE_FILE, EXT_TAR_Z, "Expand",
201 "(current selection)", "LYNXDIRED://UNTAR_Z%p"),
202 #endif /* OK_TAR && !ARCHIVE_ONLY */
203
204 #if defined(OK_TAR) && defined(OK_GZIP) && !defined(ARCHIVE_ONLY)
205 DATA( DE_FILE, ".tar.gz", "Expand",
206 "(current selection)", "LYNXDIRED://UNTAR_GZ%p"),
207
208 DATA( DE_FILE, ".tgz", "Expand",
209 "(current selection)", "LYNXDIRED://UNTAR_GZ%p"),
210 #endif /* OK_TAR && OK_GZIP && !ARCHIVE_ONLY */
211
212 #ifndef ARCHIVE_ONLY
213 DATA( DE_FILE, EXT_Z, "Uncompress",
214 "(current selection)", "LYNXDIRED://DECOMPRESS%p"),
215 #endif /* ARCHIVE_ONLY */
216
217 #if defined(OK_GZIP) && !defined(ARCHIVE_ONLY)
218 DATA( DE_FILE, ".gz", "Uncompress",
219 "(current selection)", "LYNXDIRED://UNGZIP%p"),
220 #endif /* OK_GZIP && !ARCHIVE_ONLY */
221
222 #if defined(OK_ZIP) && !defined(ARCHIVE_ONLY)
223 DATA( DE_FILE, ".zip", "Uncompress",
224 "(current selection)", "LYNXDIRED://UNZIP%p"),
225 #endif /* OK_ZIP && !ARCHIVE_ONLY */
226
227 #if defined(OK_TAR) && !defined(ARCHIVE_ONLY)
228 DATA( DE_FILE, ".tar", "UnTar",
229 "(current selection)", "LYNXDIRED://UNTAR%p"),
230 #endif /* OK_TAR && !ARCHIVE_ONLY */
231
232 #ifdef OK_TAR
233 DATA( DE_DIR, "", "Tar",
234 "(current selection)", "LYNXDIRED://TAR%p"),
235 #endif /* OK_TAR */
236
237 #if defined(OK_TAR) && defined(OK_GZIP)
238 DATA( DE_DIR, "", "Tar and compress",
239 "(using GNU gzip)", "LYNXDIRED://TAR_GZ%p"),
240 #endif /* OK_TAR && OK_GZIP */
241
242 #if defined(OK_TAR) && defined(USE_COMPRESS)
243 DATA( DE_DIR, "", "Tar and compress",
244 "(using compress)", "LYNXDIRED://TAR_Z%p"),
245 #endif /* OK_TAR && USE_COMPRESS */
246
247 #ifdef OK_ZIP
248 DATA( DE_DIR, "", "Package and compress",
249 "(using zip)", "LYNXDIRED://ZIP%p"),
250 #endif /* OK_ZIP */
251
252 DATA( DE_FILE, "", "Compress",
253 "(using Unix compress)", "LYNXDIRED://COMPRESS%p"),
254
255 #ifdef OK_GZIP
256 DATA( DE_FILE, "", "Compress",
257 "(using gzip)", "LYNXDIRED://GZIP%p"),
258 #endif /* OK_GZIP */
259
260 #ifdef OK_ZIP
261 DATA( DE_FILE, "", "Compress",
262 "(using zip)", "LYNXDIRED://ZIP%p"),
263 #endif /* OK_ZIP */
264
265 DATA( DE_TAG, "", "Move all tagged items to another location.",
266 "", "LYNXDIRED://MOVE_TAGGED%d"),
267
268 #ifdef OK_INSTALL
269 DATA( DE_TAG, "", "Install tagged files into another directory.",
270 "", "LYNXDIRED://INSTALL_SRC%00"),
271 #endif
272
273 DATA( DE_TAG, "", "Remove all tagged files and directories.",
274 "", "LYNXDIRED://REMOVE_TAGGED"),
275
276 DATA( DE_TAG, "", "Untag all tagged files and directories.",
277 "", "LYNXDIRED://CLEAR_TAGGED"),
278
279 DATA( 0, NULL, NULL,
280 NULL, NULL),
281 };
282 #undef DATA
283 /* *INDENT-ON* */
284
cannot_stat(const char * name)285 static BOOLEAN cannot_stat(const char *name)
286 {
287 char *tmpbuf = 0;
288
289 HTSprintf0(&tmpbuf, gettext("Unable to get status of '%s'."), name);
290 HTAlert(tmpbuf);
291 FREE(tmpbuf);
292 return FALSE;
293 }
294
295 #define OK_STAT(name, sb) (stat(name, sb) == 0)
296
ok_stat(const char * name,struct stat * sb)297 static BOOLEAN ok_stat(const char *name, struct stat *sb)
298 {
299 BOOLEAN rc = TRUE;
300
301 CTRACE((tfp, "testing ok_stat(%s)\n", name));
302 if (!OK_STAT(name, sb)) {
303 #ifdef DOSPATH
304 size_t len = strlen(name);
305
306 /*
307 * If a path ends with '\' or ':', we can guess that it may be
308 * a directory name. Adding a '.' (after a '\') will produce a
309 * pathname that stat() will accept as a directory name.
310 */
311 if (len != 0 && (name[len - 1] == '\\' || name[len - 1] == ':')) {
312 char *temp = malloc(len + 3);
313
314 if (temp != 0) {
315 strcpy(temp, name);
316 if (temp[len - 1] == '\\') {
317 strcpy(temp + len, ".");
318 } else {
319 strcpy(temp + len, "\\.");
320 }
321 rc = OK_STAT(temp, sb);
322 free(temp);
323 } else {
324 rc = FALSE;
325 }
326 } else
327 #endif
328 rc = FALSE;
329 }
330
331 if (rc == FALSE)
332 rc = cannot_stat(name);
333
334 return rc;
335 }
336
337 #ifdef HAVE_LSTAT
ok_lstat(char * name,struct stat * sb)338 static BOOLEAN ok_lstat(char *name, struct stat *sb)
339 {
340 CTRACE((tfp, "testing ok_lstat(%s)\n", name));
341 if (lstat(name, sb) < 0) {
342 return cannot_stat(name);
343 }
344 return TRUE;
345 }
346 #else
347 #define ok_lstat(name,sb) ok_stat(name,sb)
348 #endif
349
ok_file_or_dir(struct stat * sb)350 static BOOLEAN ok_file_or_dir(struct stat *sb)
351 {
352 if (!S_ISDIR(sb->st_mode)
353 && !S_ISREG(sb->st_mode)) {
354 HTAlert(gettext("The selected item is not a file or a directory! Request ignored."));
355 return FALSE;
356 }
357 return TRUE;
358 }
359
360 #ifdef OK_INSTALL /* currently only used in local_install */
ok_localname(char * dst,const char * src)361 static BOOLEAN ok_localname(char *dst, const char *src)
362 {
363 struct stat dir_info;
364
365 if (!ok_stat(src, &dir_info)
366 || !ok_file_or_dir(&dir_info)) {
367 return FALSE;
368 }
369 if (strlen(src) >= DIRED_MAXBUF) {
370 CTRACE((tfp, "filename too long in ok_localname!\n"));
371 return FALSE;
372 }
373 strcpy(dst, src);
374 return TRUE;
375 }
376 #endif /* OK_INSTALL */
377
378 #define MAX_ARGC 10
379
make_argv(const char * command,...)380 static char **make_argv(const char *command, ...)
381 {
382 static char *result[MAX_ARGC];
383 int argc = 0;
384 char *value;
385 va_list ap;
386
387 va_start(ap, command);
388 result[0] = 0;
389 StrAllocCopy(result[argc++], command);
390 do {
391 result[argc] = 0;
392 value = (char *) va_arg(ap, char *);
393
394 if (value != 0)
395 StrAllocCopy(result[argc], value);
396 } while (result[argc++] != 0);
397 va_end(ap);
398
399 return result;
400 }
401
free_argv(char ** argv)402 static void free_argv(char **argv)
403 {
404 int argc;
405
406 for (argc = 0; argv[argc] != 0; ++argc) {
407 free(argv[argc]);
408 }
409 }
410
411 /*
412 * Execute DIRED command, return -1 or 0 on failure, 1 success.
413 */
LYExecv(const char * path,char ** argv,char * msg)414 static int LYExecv(const char *path,
415 char **argv,
416 char *msg)
417 {
418 int rc = 0;
419
420 #if defined(VMS)
421 CTRACE((tfp, "LYExecv: Called inappropriately! (path=%s)\n", path));
422 #else
423 int n;
424 char *tmpbuf = 0;
425
426 #if defined(__DJGPP__) || defined(_WINDOWS)
427 (void) msg;
428 stop_curses();
429 HTSprintf0(&tmpbuf, "%s", path);
430 for (n = 1; argv[n] != 0; n++)
431 HTSprintf(&tmpbuf, " %s", argv[n]);
432 HTSprintf(&tmpbuf, "\n");
433 rc = LYSystem(tmpbuf) ? 0 : 1;
434 #else
435 int pid;
436
437 #ifdef HAVE_TYPE_UNIONWAIT
438 union wait wstatus;
439
440 #else
441 int wstatus;
442 #endif
443
444 if (TRACE) {
445 CTRACE((tfp, "LYExecv path='%s'\n", path));
446 for (n = 0; argv[n] != 0; n++)
447 CTRACE((tfp, "argv[%d] = '%s'\n", n, argv[n]));
448 }
449
450 rc = 1; /* It will work */
451 stop_curses();
452 pid = fork(); /* fork and execute command */
453
454 switch (pid) {
455 case -1:
456 HTSprintf0(&tmpbuf, gettext("Unable to %s due to system error!"), msg);
457 rc = 0;
458 break; /* don't fall thru! - KW */
459
460 case 0: /* child */
461 #ifdef USE_EXECVP
462 execvp(path, argv); /* this uses our $PATH */
463 #else
464 execv(path, argv);
465 #endif
466 exit(EXIT_FAILURE); /* execv failed, give wait() something to look at */
467 /*NOTREACHED */
468
469 default: /* parent */
470 #ifndef HAVE_WAITPID
471 while (wait(&wstatus) != pid) ; /* do nothing */
472 #else
473 while (-1 == waitpid(pid, &wstatus, 0)) { /* wait for child */
474 #ifdef EINTR
475 if (errno == EINTR)
476 continue;
477 #endif /* EINTR */
478 #ifdef ERESTARTSYS
479 if (errno == ERESTARTSYS)
480 continue;
481 #endif /* ERESTARTSYS */
482 break;
483 }
484 #endif /* !HAVE_WAITPID */
485 if ((WIFEXITED(wstatus)
486 && (WEXITSTATUS(wstatus) != 0))
487 || (WIFSIGNALED(wstatus)
488 && (WTERMSIG(wstatus) > 0))) { /* error return */
489 HTSprintf0(&tmpbuf,
490 gettext("Probable failure to %s due to system error!"),
491 msg);
492 rc = 0;
493 }
494 }
495 #endif /* __DJGPP__ */
496
497 if (rc == 0) {
498 /*
499 * Screen may have message from the failed execv'd command. Give user
500 * time to look at it before screen refresh.
501 */
502 LYSleepAlert();
503 }
504 start_curses();
505 if (tmpbuf != 0) {
506 if (rc == 0)
507 HTAlert(tmpbuf);
508 FREE(tmpbuf);
509 }
510 #endif /* VMS || _WINDOWS */
511 CTRACE((tfp, "LYexecv ->%d\n", rc));
512 return (rc);
513 }
514
make_directory(char * path)515 static int make_directory(char *path)
516 {
517 int code;
518 const char *program;
519
520 if ((program = HTGetProgramPath(ppMKDIR)) != NULL) {
521 char **args;
522 char *msg = 0;
523
524 HTSprintf0(&msg, "make directory %s", path);
525 args = make_argv("mkdir",
526 path,
527 NULL);
528 code = (LYExecv(program, args, msg) <= 0) ? -1 : 1;
529 FREE(msg);
530 free_argv(args);
531 } else {
532 #ifdef _WINDOWS
533 code = mkdir(path) ? -1 : 1;
534 #else
535 code = mkdir(path, 0777) ? -1 : 1;
536 #endif
537 CTRACE((tfp, "builtin mkdir ->%d\n\t%s\n", code, path));
538 }
539 return (code);
540 }
541
remove_file(char * path)542 static int remove_file(char *path)
543 {
544 int code;
545 const char *program;
546
547 if ((program = HTGetProgramPath(ppRM)) != NULL) {
548 char **args;
549 char *tmpbuf = NULL;
550
551 args = make_argv("rm",
552 "-f",
553 path,
554 NULL);
555 HTSprintf0(&tmpbuf, gettext("remove %s"), path);
556 code = LYExecv(program, args, tmpbuf);
557 FREE(tmpbuf);
558 free_argv(args);
559 } else {
560 code = remove(path) ? -1 : 1;
561 CTRACE((tfp, "builtin remove ->%d\n\t%s\n", code, path));
562 }
563 return (code);
564 }
565
remove_directory(char * path)566 static int remove_directory(char *path)
567 {
568 int code;
569 const char *program;
570
571 if ((program = HTGetProgramPath(ppRMDIR)) != NULL) {
572 char **args;
573 char *tmpbuf = NULL;
574
575 args = make_argv("rmdir",
576 path,
577 NULL);
578 HTSprintf0(&tmpbuf, gettext("remove %s"), path);
579 code = LYExecv(program, args, tmpbuf);
580 FREE(tmpbuf);
581 free_argv(args);
582 } else {
583 code = rmdir(path) ? -1 : 1;
584 CTRACE((tfp, "builtin rmdir ->%d\n\t%s\n", code, path));
585 }
586 return (code);
587 }
588
touch_file(char * path)589 static int touch_file(char *path)
590 {
591 int code;
592 const char *program;
593
594 if ((program = HTGetProgramPath(ppTOUCH)) != NULL) {
595 char **args;
596 char *msg = NULL;
597
598 HTSprintf0(&msg, gettext("touch %s"), path);
599 args = make_argv("touch",
600 path,
601 NULL);
602 code = (LYExecv(program, args, msg) <= 0) ? -1 : 1;
603 FREE(msg);
604 free_argv(args);
605 } else {
606 FILE *fp;
607
608 if ((fp = fopen(path, BIN_W)) != 0) {
609 fclose(fp);
610 code = 1;
611 } else {
612 code = -1;
613 }
614 CTRACE((tfp, "builtin touch ->%d\n\t%s\n", code, path));
615 }
616 return (code);
617 }
618
move_file(char * source,char * target)619 static int move_file(char *source, char *target)
620 {
621 int code;
622 const char *program;
623
624 if ((program = HTGetProgramPath(ppMV)) != NULL) {
625 char *msg = 0;
626 char **args;
627
628 HTSprintf0(&msg, gettext("move %s to %s"), source, target);
629 args = make_argv("mv",
630 source,
631 target,
632 NULL);
633 code = (LYExecv(program, args, msg) <= 0) ? -1 : 1;
634 FREE(msg);
635 free_argv(args);
636 } else {
637 struct stat sb;
638 char *actual = 0;
639
640 /* the caller sets up a target directory; we need a file path */
641 if (stat(target, &sb) == 0
642 && S_ISDIR(sb.st_mode)) {
643 HTSprintf0(&actual, "%s/%s", target, LYPathLeaf(source));
644 CTRACE((tfp, "move_file source=%s, target=%s\n", source, target));
645 target = actual;
646 }
647 code = rename(source, target);
648 CTRACE((tfp, "builtin move ->%d\n\tsource=%s\n\ttarget=%s\n",
649 code, source, target));
650 if (code != 0) { /* it failed */
651 if ((code = LYCopyFile(source, target)) >= 0) {
652 code = remove(source);
653 CTRACE((tfp, "...remove source after copying ->%d\n", code));
654 }
655 }
656 if (code == 0)
657 code = 1;
658 if (actual != target) {
659 FREE(actual);
660 }
661 }
662 return code;
663 }
664
not_already_exists(char * name)665 static BOOLEAN not_already_exists(char *name)
666 {
667 struct stat dir_info;
668
669 if (!OK_STAT(name, &dir_info)) {
670 if (errno != ENOENT) {
671 cannot_stat(name);
672 } else {
673 return TRUE;
674 }
675 } else if (S_ISDIR(dir_info.st_mode)) {
676 HTAlert(gettext("There is already a directory with that name! Request ignored."));
677 } else if (S_ISREG(dir_info.st_mode)) {
678 HTAlert(gettext("There is already a file with that name! Request ignored."));
679 } else {
680 HTAlert(gettext("The specified name is already in use! Request ignored."));
681 }
682 return FALSE;
683 }
684
dir_has_same_owner(struct stat * dst_info,struct stat * src_info)685 static BOOLEAN dir_has_same_owner(struct stat *dst_info,
686 struct stat *src_info)
687 {
688 if (S_ISDIR(dst_info->st_mode)) {
689 if (dst_info->st_uid == src_info->st_uid) {
690 return TRUE;
691 } else {
692 HTAlert(gettext("Destination has different owner! Request denied."));
693 }
694 } else {
695 HTAlert(gettext("Destination is not a valid directory! Request denied."));
696 }
697 return FALSE;
698 }
699
700 /*
701 * Make sure the source and target are not the same location.
702 */
same_location(struct stat * dst_info,struct stat * src_info)703 static BOOLEAN same_location(struct stat *dst_info,
704 struct stat *src_info)
705 {
706 BOOLEAN result = FALSE;
707
708 #ifdef UNIX
709 if (src_info->st_dev == dst_info->st_dev &&
710 src_info->st_ino == dst_info->st_ino) {
711 HTAlert(gettext("Source and destination are the same location! Request ignored!"));
712 result = TRUE;
713 }
714 #endif
715 return result;
716 }
717
718 /*
719 * Remove all tagged files and directories.
720 */
remove_tagged(void)721 static int remove_tagged(void)
722 {
723 int ans;
724 BOOL will_clear = TRUE;
725 char *cp;
726 char *tmpbuf = NULL;
727 char *testpath = NULL;
728 struct stat dir_info;
729 int count;
730 HTList *tag;
731
732 if (HTList_isEmpty(tagged)) /* should never happen */
733 return 0;
734
735 ans = HTConfirm(gettext("Remove all tagged files and directories?"));
736
737 count = 0;
738 tag = tagged;
739 while (ans == YES && (cp = (char *) HTList_nextObject(tag)) != NULL) {
740 if (is_url(cp) == FILE_URL_TYPE) { /* unnecessary check */
741 testpath = HTfullURL_toFile(cp);
742 LYTrimPathSep(testpath);
743 will_clear = TRUE;
744
745 /*
746 * Check the current status of the path to be deleted.
747 */
748 if (!ok_stat(testpath, &dir_info)) {
749 will_clear = FALSE;
750 break;
751 } else {
752 if (remove_file(testpath) <= 0) {
753 if (count == 0)
754 count = -1;
755 will_clear = FALSE;
756 break;
757 }
758 ++count;
759 FREE(testpath);
760 }
761 }
762 }
763 FREE(testpath);
764 FREE(tmpbuf);
765 if (will_clear)
766 clear_tags();
767 return count;
768 }
769
parse_directory(char * path)770 static char *parse_directory(char *path)
771 {
772 char *result;
773
774 if (path) {
775 path = strip_trailing_slash(path);
776 path = HTParse(".", path, PARSE_PATH + PARSE_PUNCTUATION);
777 result = HTURLPath_toFile(path, TRUE, FALSE);
778 FREE(path);
779 } else { /* Last resort, should never happen. */
780 result = HTURLPath_toFile(".", TRUE, FALSE);
781 }
782 return result;
783 }
784
785 /*
786 * Move all tagged files and directories to a new location.
787 *
788 * The 'testpath' parameter is the current location, used for resolving
789 * relative target specifications.
790 */
modify_tagged(char * testpath)791 static int modify_tagged(char *testpath)
792 {
793 char *cp;
794 bstring *given_target = NULL;
795 char *dst_path = NULL;
796 char *src_path = NULL;
797 char *old_path = NULL;
798 struct stat src_info;
799 struct stat dst_info;
800 int count = 0;
801 HTList *tag;
802
803 CTRACE((tfp, "modify_tagged(%s)\n", testpath));
804
805 if (HTList_isEmpty(tagged)) /* should never happen */
806 return 0;
807
808 _statusline(gettext("Enter new location for tagged items: "));
809
810 BStrCopy0(given_target, "");
811 (void) LYgetBString(&given_target, FALSE, 0, NORECALL);
812 if (!isBEmpty(given_target)) {
813 /*
814 * Replace ~/ references to the home directory.
815 */
816 if (LYIsTilde(given_target->str[0]) && LYIsPathSep(given_target->str[1])) {
817 char *cp1 = NULL;
818
819 StrAllocCopy(cp1, Home_Dir());
820 StrAllocCat(cp1, (given_target->str + 1));
821 BStrCopy0(given_target, cp1);
822 FREE(cp1);
823 }
824
825 /*
826 * If path is relative, prefix it with current location.
827 */
828 if (!LYIsPathSep(given_target->str[0])) {
829 dst_path = HTLocalName(testpath);
830 LYAddPathSep(&dst_path);
831 StrAllocCat(dst_path, given_target->str);
832 } else {
833 dst_path = HTLocalName(given_target->str);
834 }
835
836 if (!ok_stat(dst_path, &dst_info)) {
837 FREE(dst_path);
838 BStrFree(given_target);
839 return 0;
840 }
841
842 /*
843 * Determine the ownership of the current location, using the directory
844 * containing the file or subdir from each of the tagged files.
845 */
846 for (tag = tagged; (cp = (char *) HTList_nextObject(tag)) != NULL;) {
847 src_path = parse_directory(cp);
848
849 if (isEmpty(old_path) || strcmp(old_path, src_path)) {
850 if (!ok_stat(src_path, &src_info)
851 || same_location(&dst_info, &src_info)
852 || !dir_has_same_owner(&dst_info, &src_info)) {
853 FREE(src_path);
854 BStrFree(given_target);
855 return 0;
856 }
857 }
858 StrAllocCopy(old_path, src_path);
859 FREE(src_path);
860 }
861
862 /*
863 * Move all tagged items to the target location.
864 */
865 for (tag = tagged; (cp = (char *) HTList_nextObject(tag)) != NULL;) {
866 src_path = HTfullURL_toFile(cp);
867
868 if (move_file(src_path, dst_path) < 0) {
869 if (count == 0)
870 count = -1;
871 break;
872 }
873 FREE(src_path);
874 ++count;
875 }
876 clear_tags();
877 FREE(src_path);
878 FREE(dst_path);
879 }
880 BStrFree(given_target);
881 return count;
882 }
883
884 /*
885 * Modify the name of the specified item.
886 */
modify_name(char * testpath)887 static int modify_name(char *testpath)
888 {
889 const char *cp;
890 bstring *tmpbuf = NULL;
891 char *newpath = NULL;
892 struct stat dir_info;
893 int code = 0;
894
895 /*
896 * Determine the status of the selected item.
897 */
898 testpath = strip_trailing_slash(testpath);
899
900 if (ok_stat(testpath, &dir_info)) {
901
902 /*
903 * Change the name of the file or directory.
904 */
905 if (S_ISDIR(dir_info.st_mode)) {
906 cp = gettext("Enter new name for directory: ");
907 } else if (S_ISREG(dir_info.st_mode)) {
908 cp = gettext("Enter new name for file: ");
909 } else {
910 return ok_file_or_dir(&dir_info);
911 }
912
913 BStrCopy0(tmpbuf, LYPathLeaf(testpath));
914 if (get_filename(cp, &tmpbuf)) {
915
916 /*
917 * Do not allow the user to also change the location at this time.
918 */
919 if (LYLastPathSep(tmpbuf->str) != 0) {
920 HTAlert(gettext("Illegal character (path-separator) found! Request ignored."));
921 } else if (strlen(tmpbuf->str)) {
922 if ((cp = LYLastPathSep(testpath)) != NULL) {
923 HTSprintf0(&newpath, "%.*s%s",
924 (int) (cp - testpath + 1),
925 testpath, tmpbuf->str);
926 } else {
927 StrAllocCopy(newpath, tmpbuf->str);
928 }
929
930 /*
931 * Make sure the destination does not already exist.
932 */
933 if (not_already_exists(newpath)) {
934 code = move_file(testpath, newpath);
935 }
936 FREE(newpath);
937 }
938 }
939 }
940 BStrFree(tmpbuf);
941 return code;
942 }
943
944 /*
945 * Change the location of a file or directory.
946 */
modify_location(char * testpath)947 static int modify_location(char *testpath)
948 {
949 const char *cp;
950 char *sp;
951 bstring *tmpbuf = NULL;
952 char *newpath = NULL;
953 char *savepath = NULL;
954 struct stat old_info;
955 struct stat dir_info;
956 int code = 0;
957
958 /*
959 * Determine the status of the selected item.
960 */
961 testpath = strip_trailing_slash(testpath);
962 if (!ok_stat(testpath, &dir_info)) {
963 return 0;
964 }
965
966 /*
967 * Change the location of the file or directory.
968 */
969 if (S_ISDIR(dir_info.st_mode)) {
970 cp = gettext("Enter new location for directory: ");
971 } else if (S_ISREG(dir_info.st_mode)) {
972 cp = gettext("Enter new location for file: ");
973 } else {
974 return ok_file_or_dir(&dir_info);
975 }
976
977 BStrCopy0(tmpbuf, testpath);
978 *LYPathLeaf(tmpbuf->str) = '\0';
979 if (get_filename(cp, &tmpbuf)) {
980 if (strlen(tmpbuf->str)) {
981 StrAllocCopy(savepath, testpath);
982 StrAllocCopy(newpath, testpath);
983
984 /*
985 * Allow ~/ references to the home directory.
986 */
987 if (LYIsTilde(tmpbuf->str[0])
988 && (tmpbuf->str[1] == '\0' || LYIsPathSep(tmpbuf->str[1]))) {
989 StrAllocCopy(newpath, Home_Dir());
990 StrAllocCat(newpath, (tmpbuf->str + 1));
991 BStrCopy0(tmpbuf, newpath);
992 }
993 if (LYisAbsPath(tmpbuf->str)) {
994 StrAllocCopy(newpath, tmpbuf->str);
995 } else if ((sp = LYLastPathSep(newpath)) != NULL) {
996 *++sp = '\0';
997 StrAllocCat(newpath, tmpbuf->str);
998 } else {
999 HTAlert(gettext("Unexpected failure - unable to find trailing path separator"));
1000 FREE(newpath);
1001 FREE(savepath);
1002 BStrFree(tmpbuf);
1003 return 0;
1004 }
1005
1006 /*
1007 * Make sure the source and target have the same owner (uid).
1008 */
1009 old_info = dir_info;
1010 if (!ok_stat(newpath, &dir_info)) {
1011 code = 0;
1012 } else if (same_location(&old_info, &dir_info)) {
1013 code = 0;
1014 } else if (dir_has_same_owner(&dir_info, &old_info)) {
1015 code = move_file(savepath, newpath);
1016 }
1017 FREE(newpath);
1018 FREE(savepath);
1019 }
1020 }
1021 BStrFree(tmpbuf);
1022 return code;
1023 }
1024
1025 /*
1026 * Modify name or location of a file or directory on localhost.
1027 */
local_modify(DocInfo * doc,char ** newpath)1028 int local_modify(DocInfo *doc, char **newpath)
1029 {
1030 int ans;
1031 char *cp;
1032 bstring *testpath = NULL;
1033 int count;
1034 int code = 0;
1035
1036 if (!HTList_isEmpty(tagged)) {
1037 cp = HTpartURL_toFile(doc->address);
1038
1039 count = modify_tagged(cp);
1040 FREE(cp);
1041
1042 if (doc->link > (nlinks - count - 1))
1043 doc->link = (nlinks - count - 1);
1044 doc->link = ((doc->link < 0)
1045 ? 0
1046 : doc->link);
1047
1048 return count;
1049 } else if (doc->link < 0 || doc->link > nlinks) {
1050 /*
1051 * Added protection.
1052 */
1053 return 0;
1054 }
1055
1056 /*
1057 * Do not allow simultaneous change of name and location as in Unix. This
1058 * reduces functionality but reduces difficulty for the novice.
1059 */
1060 #ifdef OK_PERMIT
1061 _statusline(gettext("Modify name, location, or permission (n, l, or p): "));
1062 #else
1063 _statusline(gettext("Modify name or location (n or l): "));
1064 #endif /* OK_PERMIT */
1065 ans = LYgetch_single();
1066
1067 if (StrChr("NLP", ans) != NULL) {
1068 cp = HTfullURL_toFile(links[doc->link].lname);
1069 if (strlen(cp) >= DIRED_MAXBUF) {
1070 FREE(cp);
1071 return 0;
1072 }
1073 BStrCopy0(testpath, cp);
1074 FREE(cp);
1075
1076 if (ans == 'N') {
1077 code = modify_name(testpath->str);
1078 } else if (ans == 'L') {
1079 if (modify_location(testpath->str)) {
1080 if (doc->link == (nlinks - 1))
1081 --doc->link;
1082 code = 1;
1083 }
1084 #ifdef OK_PERMIT
1085 } else if (ans == 'P') {
1086 code = permit_location(NULL, testpath->str, newpath);
1087 #endif /* OK_PERMIT */
1088 } else {
1089 /*
1090 * Code for changing ownership needed here.
1091 */
1092 HTAlert(gettext("This feature not yet implemented!"));
1093 }
1094 }
1095 BStrFree(testpath);
1096 return code;
1097 }
1098
1099 #define BadChars() ((!no_dotfiles && show_dotfiles) \
1100 ? "~/" \
1101 : ".~/")
1102
1103 /*
1104 * Create a new empty file in the current directory.
1105 */
create_file(char * current_location)1106 static int create_file(char *current_location)
1107 {
1108 int code = FALSE;
1109 bstring *tmpbuf = NULL;
1110 char *testpath = NULL;
1111
1112 BStrCopy0(tmpbuf, "");
1113 if (get_filename(gettext("Enter name of file to create: "), &tmpbuf)) {
1114
1115 if (strstr(tmpbuf->str, "//") != NULL) {
1116 HTAlert(gettext("Illegal redirection \"//\" found! Request ignored."));
1117 } else if (strlen(tmpbuf->str) &&
1118 StrChr(BadChars(), tmpbuf->str[0]) == NULL) {
1119 StrAllocCopy(testpath, current_location);
1120 LYAddPathSep(&testpath);
1121
1122 /*
1123 * Append the target filename to the current location.
1124 */
1125 StrAllocCat(testpath, tmpbuf->str);
1126
1127 /*
1128 * Make sure the target does not already exist
1129 */
1130 if (not_already_exists(testpath)) {
1131 code = touch_file(testpath);
1132 }
1133 FREE(testpath);
1134 }
1135 }
1136 BStrFree(tmpbuf);
1137 return code;
1138 }
1139
1140 /*
1141 * Create a new directory in the current directory.
1142 */
create_directory(char * current_location)1143 static int create_directory(char *current_location)
1144 {
1145 int code = FALSE;
1146 bstring *tmpbuf = NULL;
1147 char *testpath = NULL;
1148
1149 BStrCopy0(tmpbuf, "");
1150 if (get_filename(gettext("Enter name for new directory: "), &tmpbuf)) {
1151
1152 if (strstr(tmpbuf->str, "//") != NULL) {
1153 HTAlert(gettext("Illegal redirection \"//\" found! Request ignored."));
1154 } else if (strlen(tmpbuf->str) &&
1155 StrChr(BadChars(), tmpbuf->str[0]) == NULL) {
1156 StrAllocCopy(testpath, current_location);
1157 LYAddPathSep(&testpath);
1158
1159 StrAllocCat(testpath, tmpbuf->str);
1160
1161 /*
1162 * Make sure the target does not already exist.
1163 */
1164 if (not_already_exists(testpath)) {
1165 code = make_directory(testpath);
1166 }
1167 FREE(testpath);
1168 }
1169 }
1170 BStrFree(tmpbuf);
1171 return code;
1172 }
1173
1174 /*
1175 * Create a file or a directory at the current location.
1176 */
local_create(DocInfo * doc)1177 int local_create(DocInfo *doc)
1178 {
1179 int ans;
1180 char *cp;
1181 char testpath[DIRED_MAXBUF];
1182
1183 cp = HTfullURL_toFile(doc->address);
1184 if (strlen(cp) >= DIRED_MAXBUF) {
1185 FREE(cp);
1186 return 0;
1187 }
1188 strcpy(testpath, cp);
1189 FREE(cp);
1190
1191 _statusline(gettext("Create file or directory (f or d): "));
1192 ans = LYgetch_single();
1193
1194 if (ans == 'F') {
1195 return (create_file(testpath));
1196 } else if (ans == 'D') {
1197 return (create_directory(testpath));
1198 } else {
1199 return 0;
1200 }
1201 }
1202
1203 /*
1204 * Remove a single file or directory.
1205 */
remove_single(char * testpath)1206 static int remove_single(char *testpath)
1207 {
1208 int code = 0;
1209 char *cp;
1210 char *tmpbuf = 0;
1211 struct stat dir_info;
1212 BOOL is_directory = FALSE;
1213
1214 if (!ok_lstat(testpath, &dir_info)) {
1215 return 0;
1216 }
1217
1218 /*
1219 * Locate the filename portion of the path.
1220 */
1221 if ((cp = LYLastPathSep(testpath)) != NULL) {
1222 ++cp;
1223 } else {
1224 cp = testpath;
1225 }
1226 if (S_ISDIR(dir_info.st_mode)) {
1227 /*
1228 * This strlen stuff will probably screw up intl translations. Course,
1229 * it's probably broken for screen sizes other 80, too -jes
1230 */
1231 if (strlen(cp) < 37) {
1232 HTSprintf0(&tmpbuf,
1233 gettext("Remove directory '%s'?"), cp);
1234 } else {
1235 HTSprintf0(&tmpbuf,
1236 gettext("Remove directory?"));
1237 }
1238 is_directory = TRUE;
1239 } else if (S_ISREG(dir_info.st_mode)) {
1240 if (strlen(cp) < 60) {
1241 HTSprintf0(&tmpbuf, gettext("Remove file '%s'?"), cp);
1242 } else {
1243 HTSprintf0(&tmpbuf, gettext("Remove file?"));
1244 }
1245 #ifdef S_IFLNK
1246 } else if (S_ISLNK(dir_info.st_mode)) {
1247 if (strlen(cp) < 50) {
1248 HTSprintf0(&tmpbuf, gettext("Remove symbolic link '%s'?"), cp);
1249 } else {
1250 HTSprintf0(&tmpbuf, gettext("Remove symbolic link?"));
1251 }
1252 #endif
1253 } else {
1254 cannot_stat(testpath);
1255 FREE(tmpbuf);
1256 return 0;
1257 }
1258
1259 if (HTConfirm(tmpbuf) == YES) {
1260 code = is_directory
1261 ? remove_directory(testpath)
1262 : remove_file(testpath);
1263 }
1264 FREE(tmpbuf);
1265 return code;
1266 }
1267
1268 /*
1269 * Remove a file or a directory.
1270 */
local_remove(DocInfo * doc)1271 int local_remove(DocInfo *doc)
1272 {
1273 char *cp, *tp;
1274 char testpath[DIRED_MAXBUF];
1275 int count, i;
1276
1277 if (!HTList_isEmpty(tagged)) {
1278 count = remove_tagged();
1279 if (doc->link > (nlinks - count - 1))
1280 doc->link = (nlinks - count - 1);
1281 doc->link = ((doc->link < 0)
1282 ? 0
1283 : doc->link);
1284 return count;
1285 } else if (doc->link < 0 || doc->link > nlinks) {
1286 return 0;
1287 }
1288 cp = links[doc->link].lname;
1289 if (is_url(cp) == FILE_URL_TYPE) {
1290 tp = HTfullURL_toFile(cp);
1291 if (strlen(tp) >= DIRED_MAXBUF) {
1292 FREE(tp);
1293 return 0;
1294 }
1295 strcpy(testpath, tp);
1296 FREE(tp);
1297
1298 if ((i = (int) strlen(testpath)) && testpath[i - 1] == '/')
1299 testpath[(i - 1)] = '\0';
1300
1301 if (remove_single(testpath)) {
1302 if (doc->link == (nlinks - 1))
1303 --doc->link;
1304 return 1;
1305 }
1306 }
1307 return 0;
1308 }
1309
1310 #ifdef OK_PERMIT
1311
1312 static bstring *LYValidPermitFile = NULL;
1313
permit_bits(char * string_mode)1314 static long permit_bits(char *string_mode)
1315 {
1316 if (!strcmp(string_mode, "IRUSR"))
1317 return S_IRUSR;
1318 if (!strcmp(string_mode, "IWUSR"))
1319 return S_IWUSR;
1320 if (!strcmp(string_mode, "IXUSR"))
1321 return S_IXUSR;
1322 if (!strcmp(string_mode, "IRGRP"))
1323 return S_IRGRP;
1324 if (!strcmp(string_mode, "IWGRP"))
1325 return S_IWGRP;
1326 if (!strcmp(string_mode, "IXGRP"))
1327 return S_IXGRP;
1328 if (!strcmp(string_mode, "IROTH"))
1329 return S_IROTH;
1330 if (!strcmp(string_mode, "IWOTH"))
1331 return S_IWOTH;
1332 if (!strcmp(string_mode, "IXOTH"))
1333 return S_IXOTH;
1334 /* Don't include setuid and friends; use shell access for that. */
1335 return 0;
1336 }
1337
1338 /*
1339 * Handle DIRED permissions.
1340 */
permit_location(char * destpath,char * srcpath,char ** newpath)1341 static int permit_location(char *destpath,
1342 char *srcpath,
1343 char **newpath)
1344 {
1345 int code = 0;
1346
1347 #ifndef UNIX
1348 HTAlert(gettext("Sorry, don't know how to permit non-UNIX files yet."));
1349 #else
1350 static char tempfile[LY_MAXPATH] = "\0";
1351 char *cp;
1352 char tmpdst[LY_MAXPATH];
1353 struct stat dir_info;
1354 const char *program;
1355
1356 if (srcpath) {
1357 /*
1358 * Create form.
1359 */
1360 FILE *fp0;
1361 char *user_filename;
1362 const char *group_name;
1363
1364 srcpath = strip_trailing_slash(srcpath);
1365
1366 /*
1367 * A couple of sanity tests.
1368 */
1369 if (!ok_lstat(srcpath, &dir_info)
1370 || !ok_file_or_dir(&dir_info))
1371 return code;
1372
1373 user_filename = LYPathLeaf(srcpath);
1374
1375 (void) LYRemoveTemp(tempfile);
1376 if ((fp0 = LYOpenTemp(tempfile, HTML_SUFFIX, "w")) == NULL) {
1377 HTAlert(gettext("Unable to open permit options file"));
1378 return (code);
1379 }
1380
1381 /*
1382 * Make the tempfile a URL.
1383 */
1384 LYLocalFileToURL(newpath, tempfile);
1385 LYRegisterUIPage(*newpath, UIP_PERMIT_OPTIONS);
1386
1387 group_name = HTAA_GidToName((int) dir_info.st_gid);
1388 BStrCopy0(LYValidPermitFile, srcpath);
1389
1390 fprintf(fp0, "<Html><Head>\n<Title>%s</Title>\n</Head>\n<Body>\n",
1391 PERMIT_OPTIONS_TITLE);
1392 fprintf(fp0, "<H1>%s%s</H1>\n", PERMISSIONS_SEGMENT, user_filename);
1393 {
1394 /*
1395 * Prevent filenames which include '#' or '?' from messing it up.
1396 */
1397 char *srcpath_url = HTEscape(srcpath, URL_PATH);
1398
1399 fprintf(fp0, "<Form Action=\"%s//PERMIT_LOCATION%s\">\n",
1400 STR_LYNXDIRED, srcpath_url);
1401 FREE(srcpath_url);
1402 }
1403
1404 fprintf(fp0, "<Ol><Li>%s<Br><Br>\n",
1405 gettext("Specify permissions below:"));
1406 fprintf(fp0, "%s:<Br>\n", gettext("Owner:"));
1407 fprintf(fp0,
1408 "<Input Type=\"checkbox\" Name=\"mode\" Value=\"IRUSR\" %s> Read<Br>\n",
1409 (dir_info.st_mode & S_IRUSR) ? "checked" : "");
1410 fprintf(fp0,
1411 "<Input Type=\"checkbox\" Name=\"mode\" Value=\"IWUSR\" %s> Write<Br>\n",
1412 (dir_info.st_mode & S_IWUSR) ? "checked" : "");
1413 /*
1414 * If restricted, only change eXecute permissions on directories.
1415 */
1416 if (!no_change_exec_perms || S_ISDIR(dir_info.st_mode))
1417 fprintf(fp0,
1418 "<Input Type=\"checkbox\" Name=\"mode\" Value=\"IXUSR\" %s> %s<Br>\n",
1419 (dir_info.st_mode & S_IXUSR) ? "checked" : "",
1420 S_ISDIR(dir_info.st_mode) ? "Search" : "Execute");
1421
1422 fprintf(fp0, "%s %s:<Br>\n", gettext("Group"), group_name);
1423 fprintf(fp0,
1424 "<Input Type=\"checkbox\" Name=\"mode\" Value=\"IRGRP\" %s> Read<Br>\n",
1425 (dir_info.st_mode & S_IRGRP) ? "checked" : "");
1426 fprintf(fp0,
1427 "<Input Type=\"checkbox\" Name=\"mode\" Value=\"IWGRP\" %s> Write<Br>\n",
1428 (dir_info.st_mode & S_IWGRP) ? "checked" : "");
1429 /*
1430 * If restricted, only change eXecute permissions on directories.
1431 */
1432 if (!no_change_exec_perms || S_ISDIR(dir_info.st_mode))
1433 fprintf(fp0,
1434 "<Input Type=\"checkbox\" Name=\"mode\" Value=\"IXGRP\" %s> %s<Br>\n",
1435 (dir_info.st_mode & S_IXGRP) ? "checked" : "",
1436 S_ISDIR(dir_info.st_mode) ? "Search" : "Execute");
1437
1438 fprintf(fp0, "%s<Br>\n", gettext("Others:"));
1439 fprintf(fp0,
1440 "<Input Type=\"checkbox\" Name=\"mode\" Value=\"IROTH\" %s> Read<Br>\n",
1441 (dir_info.st_mode & S_IROTH) ? "checked" : "");
1442 fprintf(fp0,
1443 "<Input Type=\"checkbox\" Name=\"mode\" Value=\"IWOTH\" %s> Write<Br>\n",
1444 (dir_info.st_mode & S_IWOTH) ? "checked" : "");
1445 /*
1446 * If restricted, only change eXecute permissions on directories.
1447 */
1448 if (!no_change_exec_perms || S_ISDIR(dir_info.st_mode))
1449 fprintf(fp0,
1450 "<Input Type=\"checkbox\" Name=\"mode\" Value=\"IXOTH\" %s> %s<Br>\n",
1451 (dir_info.st_mode & S_IXOTH) ? "checked" : "",
1452 S_ISDIR(dir_info.st_mode) ? "Search" : "Execute");
1453
1454 fprintf(fp0,
1455 "<Br>\n<Li><Input Type=\"submit\" Value=\"Submit\"> %s %s %s.\n</Ol>\n</Form>\n",
1456 gettext("form to permit"),
1457 S_ISDIR(dir_info.st_mode) ? "directory" : "file",
1458 user_filename);
1459 fprintf(fp0, "</Body></Html>");
1460 LYCloseTempFP(fp0);
1461
1462 LYforce_no_cache = TRUE;
1463 code = PERMIT_FORM_RESULT; /* Special flag for LYMainLoop */
1464
1465 } else { /* The form being activated. */
1466 mode_t new_mode = 0;
1467
1468 /*
1469 * Make sure we have a valid set-permission file comparison string
1470 * loaded via a previous call with srcpath != NULL. - KW
1471 */
1472 if (isBEmpty(LYValidPermitFile)) {
1473 if (LYCursesON)
1474 HTAlert(INVALID_PERMIT_URL);
1475 else
1476 fprintf(stderr, "%s\n", INVALID_PERMIT_URL);
1477 CTRACE((tfp, "permit_location: called for <%s>.\n",
1478 (destpath ?
1479 destpath : "NULL URL pointer")));
1480 return code;
1481 }
1482 cp = destpath;
1483 while (*cp != '\0' && *cp != '?') { /* Find filename */
1484 cp++;
1485 }
1486 if (*cp == '\0') {
1487 return (code); /* Nothing to permit. */
1488 }
1489 *cp++ = '\0'; /* Null terminate file name and
1490 start working on the masks. */
1491
1492 /* Will now operate only on filename part. */
1493 if ((destpath = HTURLPath_toFile(destpath, TRUE, FALSE)) == 0)
1494 return (code);
1495 if (strlen(destpath) >= LY_MAXPATH) {
1496 FREE(destpath);
1497 return (code);
1498 }
1499 strcpy(tmpdst, destpath);
1500 FREE(destpath);
1501 destpath = tmpdst;
1502
1503 /*
1504 * Make sure that the file string is the one from the last displayed
1505 * File Permissions menu. - KW
1506 */
1507 if (strcmp(destpath, LYValidPermitFile->str)) {
1508 if (LYCursesON)
1509 HTAlert(INVALID_PERMIT_URL);
1510 else
1511 fprintf(stderr, "%s\n", INVALID_PERMIT_URL);
1512 CTRACE((tfp, "permit_location: called for file '%s'.\n",
1513 destpath));
1514 return code;
1515 }
1516
1517 /*
1518 * A couple of sanity tests.
1519 */
1520 destpath = strip_trailing_slash(destpath);
1521 if (!ok_stat(destpath, &dir_info)
1522 || !ok_file_or_dir(&dir_info)) {
1523 return code;
1524 }
1525
1526 /*
1527 * Cycle over permission strings.
1528 */
1529 while (*cp != '\0') {
1530 char *cr = cp;
1531
1532 while (*cr != '\0' && *cr != '&') { /* GET data split by '&'. */
1533 cr++;
1534 }
1535 if (*cr != '\0') {
1536 *cr++ = '\0';
1537 }
1538 if (StrNCmp(cp, "mode=", 5) == 0) { /* Magic string. */
1539 long mask = permit_bits(cp + 5);
1540
1541 if (mask != 0) {
1542 /*
1543 * If restricted, only change eXecute permissions on
1544 * directories.
1545 */
1546 if (!no_change_exec_perms
1547 || StrChr(cp + 5, 'X') == NULL
1548 || S_ISDIR(dir_info.st_mode)) {
1549 new_mode |= (mode_t) mask;
1550 }
1551 } else {
1552 HTAlert(gettext("Invalid mode format."));
1553 return code;
1554 }
1555 } else {
1556 HTAlert(gettext("Invalid syntax format."));
1557 return code;
1558 }
1559
1560 cp = cr;
1561 }
1562
1563 /*
1564 * Call chmod().
1565 */
1566 code = 1;
1567 if ((program = HTGetProgramPath(ppCHMOD)) != NULL) {
1568 char **args;
1569 char amode[10];
1570 char *tmpbuf = NULL;
1571
1572 HTSprintf0(&tmpbuf, "chmod %.4o %s", (unsigned) new_mode, destpath);
1573 sprintf(amode, "%.4o", (unsigned) new_mode);
1574 args = make_argv("chmod",
1575 amode,
1576 destpath,
1577 NULL);
1578 if (LYExecv(program, args, tmpbuf) <= 0) {
1579 code = -1;
1580 }
1581 FREE(tmpbuf);
1582 free_argv(args);
1583 } else {
1584 if (chmod(destpath, new_mode) < 0) {
1585 code = -1;
1586 }
1587 CTRACE((tfp, "builtin chmod %.4o ->%d\n\t%s\n",
1588 (unsigned) new_mode, code, destpath));
1589 }
1590 if (code == 1)
1591 LYforce_no_cache = TRUE; /* Force update of dired listing. */
1592 }
1593 #endif /* !UNIX */
1594 return code;
1595 }
1596 #endif /* OK_PERMIT */
1597
1598 /*
1599 * Display or remove a tag from a given link.
1600 */
tagflag(int flag,int cur)1601 void tagflag(int flag,
1602 int cur)
1603 {
1604 if (nlinks > 0) {
1605 LYmove(links[cur].ly, 2);
1606 lynx_stop_reverse();
1607 if (flag == TRUE) {
1608 LYaddch('+');
1609 } else {
1610 LYaddch(' ');
1611 }
1612
1613 #if defined(FANCY_CURSES) || defined(USE_SLANG)
1614 if (!LYShowCursor)
1615 LYHideCursor(); /* get cursor out of the way */
1616 else
1617 #endif /* FANCY CURSES || USE_SLANG */
1618 /*
1619 * Never hide the cursor if there's no FANCY CURSES.
1620 */
1621 LYmove(links[cur].ly, links[cur].lx);
1622
1623 LYrefresh();
1624 }
1625 }
1626
1627 /*
1628 * Handle DIRED tags.
1629 */
showtags(HTList * t)1630 void showtags(HTList *t)
1631 {
1632 int i;
1633 HTList *s;
1634 char *name;
1635
1636 for (i = 0; i < nlinks; i++) {
1637 s = t;
1638 while ((name = (char *) HTList_nextObject(s)) != NULL) {
1639 if (!strcmp(links[i].lname, name)) {
1640 tagflag(TRUE, i);
1641 break;
1642 }
1643 }
1644 }
1645 }
1646
DirectoryOf(char * pathname)1647 static char *DirectoryOf(char *pathname)
1648 {
1649 char *result = 0;
1650 char *leaf;
1651
1652 StrAllocCopy(result, pathname);
1653 leaf = LYPathLeaf(result);
1654
1655 if (leaf != result) {
1656 const char *result1 = 0;
1657
1658 *leaf = '\0';
1659 if (!LYisRootPath(result))
1660 LYTrimPathSep(result);
1661 result1 = wwwName(result);
1662 StrAllocCopy(result, result1);
1663 }
1664 return result;
1665 }
1666
1667 #ifdef __DJGPP__
1668 /*
1669 * Convert filenames to acceptable 8+3 names when necessary. Make a copy of
1670 * the parameter if we must modify it.
1671 */
LYonedot(char * line)1672 static char *LYonedot(char *line)
1673 {
1674 char *dot;
1675 static char line1[LY_MAXPATH];
1676
1677 if (pathconf(line, _PC_NAME_MAX) <= 12) {
1678 LYStrNCpy(line1, line, sizeof(line1) - 1);
1679 for (;;) {
1680 if ((dot = strrchr(line1, '.')) == 0
1681 || LYLastPathSep(dot) != 0) {
1682 break;
1683 } else if (strlen(dot) == 1) {
1684 *dot = 0;
1685 } else {
1686 *dot = '_';
1687 }
1688 }
1689 return (line1);
1690 }
1691 return (line);
1692 }
1693 #else
1694 #define LYonedot(path) path
1695 #endif /* __DJGPP__ */
1696
match_op(const char * prefix,char * data)1697 static char *match_op(const char *prefix,
1698 char *data)
1699 {
1700 size_t len = strlen(prefix);
1701
1702 if (!StrNCmp("LYNXDIRED://", data, 12)
1703 && !strncasecomp(prefix, data + 12, (int) len)) {
1704 len += 12;
1705 #if defined(USE_DOS_DRIVES)
1706 if (data[len] == '/') { /* this is normal */
1707 len++;
1708 }
1709 #endif
1710 return data + len;
1711 }
1712 return 0;
1713 }
1714
1715 /*
1716 * Construct the appropriate system command taking care to escape all path
1717 * references to avoid spoofing the shell.
1718 */
build_command(char * line,char * dirName,char * arg)1719 static char *build_command(char *line,
1720 char *dirName,
1721 char *arg)
1722 {
1723 char *buffer = NULL;
1724 const char *program;
1725 const char *tar_path = HTGetProgramPath(ppTAR);
1726
1727 if ((arg = match_op("DECOMPRESS", line)) != 0) {
1728 #define FMT_UNCOMPRESS "%s %s"
1729 if ((program = HTGetProgramPath(ppUNCOMPRESS)) != NULL) {
1730 HTAddParam(&buffer, FMT_UNCOMPRESS, 1, program);
1731 HTAddParam(&buffer, FMT_UNCOMPRESS, 2, arg);
1732 HTEndParam(&buffer, FMT_UNCOMPRESS, 2);
1733 }
1734 return buffer;
1735 }
1736 #if defined(OK_UUDECODE) && !defined(ARCHIVE_ONLY)
1737 if ((arg = match_op("UUDECODE", line)) != 0) {
1738 #define FMT_UUDECODE "%s %s"
1739 if ((program = HTGetProgramPath(ppUUDECODE)) != NULL) {
1740 HTAddParam(&buffer, FMT_UUDECODE, 1, program);
1741 HTAddParam(&buffer, FMT_UUDECODE, 2, arg);
1742 HTEndParam(&buffer, FMT_UUDECODE, 2);
1743 HTAlert(gettext("Warning! UUDecoded file will exist in the directory you started Lynx."));
1744 }
1745 return buffer;
1746 }
1747 #endif /* OK_UUDECODE && !ARCHIVE_ONLY */
1748
1749 #ifdef OK_TAR
1750 if (tar_path != NULL) {
1751 # ifndef ARCHIVE_ONLY
1752 # ifdef OK_GZIP
1753 if ((arg = match_op("UNTAR_GZ", line)) != 0) {
1754 #define FMT_UNTAR_GZ "cd %s; %s -qdc %s | %s %s %s"
1755 if ((program = HTGetProgramPath(ppGZIP)) != NULL) {
1756 dirName = DirectoryOf(arg);
1757 HTAddParam(&buffer, FMT_UNTAR_GZ, 1, dirName);
1758 HTAddParam(&buffer, FMT_UNTAR_GZ, 2, program);
1759 HTAddParam(&buffer, FMT_UNTAR_GZ, 3, arg);
1760 HTAddParam(&buffer, FMT_UNTAR_GZ, 4, tar_path);
1761 HTAddToCmd(&buffer, FMT_UNTAR_GZ, 5, TAR_DOWN_OPTIONS);
1762 HTAddToCmd(&buffer, FMT_UNTAR_GZ, 6, TAR_PIPE_OPTIONS);
1763 HTEndParam(&buffer, FMT_UNTAR_GZ, 6);
1764 }
1765 return buffer;
1766 }
1767 # endif /* OK_GZIP */
1768 if ((arg = match_op("UNTAR_Z", line)) != 0) {
1769 #define FMT_UNTAR_Z "cd %s; %s %s | %s %s %s"
1770 if ((program = HTGetProgramPath(ppZCAT)) != NULL) {
1771 dirName = DirectoryOf(arg);
1772 HTAddParam(&buffer, FMT_UNTAR_Z, 1, dirName);
1773 HTAddParam(&buffer, FMT_UNTAR_Z, 2, program);
1774 HTAddParam(&buffer, FMT_UNTAR_Z, 3, arg);
1775 HTAddParam(&buffer, FMT_UNTAR_Z, 4, tar_path);
1776 HTAddToCmd(&buffer, FMT_UNTAR_Z, 5, TAR_DOWN_OPTIONS);
1777 HTAddToCmd(&buffer, FMT_UNTAR_Z, 6, TAR_PIPE_OPTIONS);
1778 HTEndParam(&buffer, FMT_UNTAR_Z, 6);
1779 }
1780 return buffer;
1781 }
1782 if ((arg = match_op("UNTAR", line)) != 0) {
1783 #define FMT_UNTAR "cd %s; %s %s %s"
1784 dirName = DirectoryOf(arg);
1785 HTAddParam(&buffer, FMT_UNTAR, 1, dirName);
1786 HTAddParam(&buffer, FMT_UNTAR, 2, tar_path);
1787 HTAddToCmd(&buffer, FMT_UNTAR, 3, TAR_DOWN_OPTIONS);
1788 HTAddParam(&buffer, FMT_UNTAR, 4, arg);
1789 HTEndParam(&buffer, FMT_UNTAR, 4);
1790 return buffer;
1791 }
1792 # endif /* !ARCHIVE_ONLY */
1793
1794 # ifdef OK_GZIP
1795 if ((arg = match_op("TAR_GZ", line)) != 0) {
1796 #define FMT_TAR_GZ "cd %s; %s %s %s %s | %s -qc >%s%s"
1797 if ((program = HTGetProgramPath(ppGZIP)) != NULL) {
1798 dirName = DirectoryOf(arg);
1799 HTAddParam(&buffer, FMT_TAR_GZ, 1, dirName);
1800 HTAddParam(&buffer, FMT_TAR_GZ, 2, tar_path);
1801 HTAddToCmd(&buffer, FMT_TAR_GZ, 3, TAR_UP_OPTIONS);
1802 HTAddToCmd(&buffer, FMT_TAR_GZ, 4, TAR_PIPE_OPTIONS);
1803 HTAddParam(&buffer, FMT_TAR_GZ, 5, LYPathLeaf(arg));
1804 HTAddParam(&buffer, FMT_TAR_GZ, 6, program);
1805 HTAddParam(&buffer, FMT_TAR_GZ, 7, LYonedot(LYPathLeaf(arg)));
1806 HTAddParam(&buffer, FMT_TAR_GZ, 8, EXT_TAR_GZ);
1807 HTEndParam(&buffer, FMT_TAR_GZ, 8);
1808 }
1809 return buffer;
1810 }
1811 # endif /* OK_GZIP */
1812
1813 if ((arg = match_op("TAR_Z", line)) != 0) {
1814 #define FMT_TAR_Z "cd %s; %s %s %s %s | %s >%s%s"
1815 if ((program = HTGetProgramPath(ppCOMPRESS)) != NULL) {
1816 dirName = DirectoryOf(arg);
1817 HTAddParam(&buffer, FMT_TAR_Z, 1, dirName);
1818 HTAddParam(&buffer, FMT_TAR_Z, 2, tar_path);
1819 HTAddToCmd(&buffer, FMT_TAR_Z, 3, TAR_UP_OPTIONS);
1820 HTAddToCmd(&buffer, FMT_TAR_Z, 4, TAR_PIPE_OPTIONS);
1821 HTAddParam(&buffer, FMT_TAR_Z, 5, LYPathLeaf(arg));
1822 HTAddParam(&buffer, FMT_TAR_Z, 6, program);
1823 HTAddParam(&buffer, FMT_TAR_Z, 7, LYonedot(LYPathLeaf(arg)));
1824 HTAddParam(&buffer, FMT_TAR_Z, 8, EXT_TAR_Z);
1825 HTEndParam(&buffer, FMT_TAR_Z, 8);
1826 }
1827 return buffer;
1828 }
1829
1830 if ((arg = match_op("TAR", line)) != 0) {
1831 #define FMT_TAR "cd %s; %s %s %s %s.tar %s"
1832 dirName = DirectoryOf(arg);
1833 HTAddParam(&buffer, FMT_TAR, 1, dirName);
1834 HTAddParam(&buffer, FMT_TAR, 2, tar_path);
1835 HTAddToCmd(&buffer, FMT_TAR, 3, TAR_UP_OPTIONS);
1836 HTAddToCmd(&buffer, FMT_TAR, 4, TAR_FILE_OPTIONS);
1837 HTAddParam(&buffer, FMT_TAR, 5, LYonedot(LYPathLeaf(arg)));
1838 HTAddParam(&buffer, FMT_TAR, 6, LYPathLeaf(arg));
1839 HTEndParam(&buffer, FMT_TAR, 6);
1840 return buffer;
1841 }
1842 }
1843 #endif /* OK_TAR */
1844
1845 #ifdef OK_GZIP
1846 if ((arg = match_op("GZIP", line)) != 0) {
1847 #define FMT_GZIP "%s -q %s"
1848 if ((program = HTGetProgramPath(ppGZIP)) != NULL) {
1849 HTAddParam(&buffer, FMT_GZIP, 1, program);
1850 HTAddParam(&buffer, FMT_GZIP, 2, arg);
1851 HTEndParam(&buffer, FMT_GZIP, 2);
1852 }
1853 return buffer;
1854 }
1855 #ifndef ARCHIVE_ONLY
1856 if ((arg = match_op("UNGZIP", line)) != 0) {
1857 #define FMT_UNGZIP "%s -d %s"
1858 if ((program = HTGetProgramPath(ppGZIP)) != NULL) {
1859 HTAddParam(&buffer, FMT_UNGZIP, 1, program);
1860 HTAddParam(&buffer, FMT_UNGZIP, 2, arg);
1861 HTEndParam(&buffer, FMT_UNGZIP, 2);
1862 }
1863 return buffer;
1864 }
1865 #endif /* !ARCHIVE_ONLY */
1866 #endif /* OK_GZIP */
1867
1868 #ifdef OK_ZIP
1869 if ((arg = match_op("ZIP", line)) != 0) {
1870 #define FMT_ZIP "cd %s; %s -rq %s.zip %s"
1871 if ((program = HTGetProgramPath(ppZIP)) != NULL) {
1872 dirName = DirectoryOf(arg);
1873 HTAddParam(&buffer, FMT_ZIP, 1, dirName);
1874 HTAddParam(&buffer, FMT_ZIP, 2, program);
1875 HTAddParam(&buffer, FMT_ZIP, 3, LYonedot(LYPathLeaf(arg)));
1876 HTAddParam(&buffer, FMT_ZIP, 4, LYPathLeaf(arg));
1877 HTEndParam(&buffer, FMT_ZIP, 4);
1878 }
1879 return buffer;
1880 }
1881 #if !defined(ARCHIVE_ONLY)
1882 if ((arg = match_op("UNZIP", line)) != 0) {
1883 #define FMT_UNZIP "cd %s; %s -q %s"
1884 if ((program = HTGetProgramPath(ppUNZIP)) != NULL) {
1885 dirName = DirectoryOf(arg);
1886 HTAddParam(&buffer, FMT_UNZIP, 1, dirName);
1887 HTAddParam(&buffer, FMT_UNZIP, 2, program);
1888 HTAddParam(&buffer, FMT_UNZIP, 3, arg);
1889 HTEndParam(&buffer, FMT_UNZIP, 3);
1890 }
1891 return buffer;
1892 }
1893 # endif /* !ARCHIVE_ONLY */
1894 #endif /* OK_ZIP */
1895
1896 if ((arg = match_op("COMPRESS", line)) != 0) {
1897 #define FMT_COMPRESS "%s %s"
1898 if ((program = HTGetProgramPath(ppCOMPRESS)) != NULL) {
1899 HTAddParam(&buffer, FMT_COMPRESS, 1, program);
1900 HTAddParam(&buffer, FMT_COMPRESS, 2, arg);
1901 HTEndParam(&buffer, FMT_COMPRESS, 2);
1902 }
1903 return buffer;
1904 }
1905
1906 return NULL;
1907 }
1908
1909 /*
1910 * Perform file management operations for LYNXDIRED URL's. Attempt to be
1911 * consistent. These are (pseudo) URLs - i.e., they should be in URL syntax:
1912 * some bytes will be URL-escaped with '%'. This is necessary because these
1913 * (pseudo) URLs will go through some of the same kinds of interpretations and
1914 * mutilations as real ones: HTParse, stripping off #fragments etc. (Some
1915 * access schemes currently have special rules about not escaping parsing '#'
1916 * "the URL way" built into HTParse, but that doesn't look like a clean way.)
1917 */
local_dired(DocInfo * doc)1918 int local_dired(DocInfo *doc)
1919 {
1920 char *line_url; /* will point to doc's address, which is a URL */
1921 char *line = NULL; /* same as line_url, but HTUnEscaped, will be alloced */
1922 char *arg = NULL; /* ...will point into line[] */
1923 char *tp = NULL;
1924 char *tmpbuf = NULL;
1925 char *buffer = NULL;
1926 char *dirName = NULL;
1927 BOOL do_pop_doc = TRUE;
1928
1929 line_url = doc->address;
1930 CTRACE((tfp, "local_dired: called for <%s>.\n",
1931 (line_url
1932 ? line_url
1933 : gettext("NULL URL pointer"))));
1934 HTUnEscapeSome(line_url, "/"); /* don't mess too much with *doc */
1935
1936 StrAllocCopy(line, line_url);
1937 HTUnEscape(line); /* _file_ (not URL) syntax, for those functions
1938 that need it. Don't forget to FREE it. */
1939 if (match_op("CHDIR", line) != 0) {
1940 #ifdef SUPPORT_CHDIR
1941 handle_LYK_CHDIR();
1942 do_pop_doc = FALSE;
1943 #endif
1944 arg = 0; /* do something to avoid cc's complaints */
1945 } else if ((arg = match_op("NEW_FILE", line)) != 0) {
1946 if (create_file(arg) > 0)
1947 LYforce_no_cache = TRUE;
1948 } else if ((arg = match_op("NEW_FOLDER", line)) != 0) {
1949 if (create_directory(arg) > 0)
1950 LYforce_no_cache = TRUE;
1951 #ifdef OK_INSTALL
1952 } else if ((arg = match_op("INSTALL_SRC", line)) != 0) {
1953 local_install(NULL, arg, &tp);
1954 if (tp) {
1955 FREE(doc->address);
1956 doc->address = tp;
1957 }
1958 FREE(line);
1959 return 0;
1960 } else if ((arg = match_op("INSTALL_DEST", line)) != 0) {
1961 local_install(arg, NULL, &tp);
1962 LYpop(doc);
1963 #endif /* OK_INSTALL */
1964 } else if ((arg = match_op("MODIFY_NAME", line)) != 0) {
1965 if (modify_name(arg) > 0)
1966 LYforce_no_cache = TRUE;
1967 } else if ((arg = match_op("MODIFY_LOCATION", line)) != 0) {
1968 if (modify_location(arg) > 0)
1969 LYforce_no_cache = TRUE;
1970 } else if ((arg = match_op("MOVE_TAGGED", line_url)) != 0) {
1971 if (modify_tagged(arg) > 0)
1972 LYforce_no_cache = TRUE;
1973 #ifdef OK_PERMIT
1974 } else if ((arg = match_op("PERMIT_SRC", line)) != 0) {
1975 permit_location(NULL, arg, &tp);
1976 if (tp) {
1977 /*
1978 * One of the checks may have failed.
1979 */
1980 FREE(doc->address);
1981 doc->address = tp;
1982 }
1983 FREE(line);
1984 return 0;
1985 } else if ((arg = match_op("PERMIT_LOCATION", line_url)) != 0) {
1986 permit_location(arg, NULL, &tp);
1987 #endif /* OK_PERMIT */
1988 } else if ((arg = match_op("REMOVE_SINGLE", line)) != 0) {
1989 if (remove_single(arg) > 0)
1990 LYforce_no_cache = TRUE;
1991 } else if (match_op("REMOVE_TAGGED", line) != 0) {
1992 if (remove_tagged())
1993 LYforce_no_cache = TRUE;
1994 } else if (match_op("CLEAR_TAGGED", line) != 0) {
1995 clear_tags();
1996 } else if ((arg = match_op("UPLOAD", line)) != 0) {
1997 /*
1998 * They're written by LYUpload_options() HTUnEscaped; don't want to
1999 * change that for now... so pass through without more unescaping.
2000 * Directory names containing '#' will probably fail.
2001 */
2002 if (LYUpload(line_url))
2003 LYforce_no_cache = TRUE;
2004 } else {
2005 LYTrimPathSep(line);
2006 if (LYLastPathSep(line) == NULL) {
2007 FREE(line);
2008 return 0;
2009 }
2010
2011 buffer = build_command(line, dirName, arg);
2012
2013 if (buffer != 0) {
2014 if ((int) strlen(buffer) < LYcolLimit - 14) {
2015 HTSprintf0(&tmpbuf, gettext("Executing %s "), buffer);
2016 } else {
2017 HTSprintf0(&tmpbuf,
2018 gettext("Executing system command. This might take a while."));
2019 }
2020 _statusline(tmpbuf);
2021 stop_curses();
2022 printf("%s\r\n", tmpbuf);
2023 LYSystem(buffer);
2024 #ifdef VMS
2025 HadVMSInterrupt = FALSE;
2026 #endif /* VMS */
2027 start_curses();
2028 LYforce_no_cache = TRUE;
2029 }
2030 }
2031
2032 FREE(dirName);
2033 FREE(tmpbuf);
2034 FREE(buffer);
2035 FREE(line);
2036 FREE(tp);
2037 if (do_pop_doc)
2038 LYpop(doc);
2039 return 0;
2040 }
2041
2042 /*
2043 * Provide a menu of file management options.
2044 */
dired_options(DocInfo * doc,char ** newfile)2045 int dired_options(DocInfo *doc, char **newfile)
2046 {
2047 static char tempfile[LY_MAXPATH];
2048 const char *my_suffix;
2049 char *path = NULL;
2050 char *dir;
2051 lynx_list_item_type *nxt;
2052 struct stat dir_info;
2053 FILE *fp0;
2054 char *dir_url;
2055 char *path_url;
2056 BOOLEAN nothing_tagged;
2057 int count;
2058 struct dired_menu *mp;
2059 char buf[2048];
2060
2061 if ((fp0 = InternalPageFP(tempfile, FALSE)) == 0)
2062 return (0);
2063
2064 /*
2065 * Make the tempfile a URL.
2066 */
2067 LYLocalFileToURL(newfile, tempfile);
2068 LYRegisterUIPage(*newfile, UIP_DIRED_MENU);
2069
2070 if (doc->link > -1 && doc->link < (nlinks + 1)) {
2071 path = HTfullURL_toFile(links[doc->link].lname);
2072 LYTrimPathSep(path);
2073
2074 if (!ok_lstat(path, &dir_info)) {
2075 LYCloseTempFP(fp0);
2076 FREE(path);
2077 return 0;
2078 }
2079
2080 } else {
2081 StrAllocCopy(path, "");
2082 memset(&dir_info, 0, sizeof(dir_info));
2083 }
2084
2085 dir = HTfullURL_toFile(doc->address);
2086 LYTrimPathSep(dir);
2087
2088 nothing_tagged = (BOOL) (HTList_isEmpty(tagged));
2089
2090 BeginInternalPage(fp0, DIRED_MENU_TITLE, DIRED_MENU_HELP);
2091
2092 fprintf(fp0, "<em>%s</em> %s<br>\n", gettext("Current directory:"), dir);
2093
2094 if (nothing_tagged) {
2095 fprintf(fp0, "<em>%s</em> ", gettext("Current selection:"));
2096 if (strlen(path)) {
2097 fprintf(fp0, "%s<p>\n", path);
2098 } else {
2099 fprintf(fp0, "%s.<p>\n", gettext("Nothing currently selected."));
2100 }
2101 } else {
2102 /*
2103 * Write out number of tagged items, and names of first few of them
2104 * relative to current (in the DIRED sense) directory.
2105 */
2106 int n = HTList_count(tagged);
2107 char *cp1 = NULL;
2108 char *cd = NULL;
2109 int i, m;
2110
2111 #define NUM_TAGS_TO_WRITE 10
2112 fprintf(fp0, "<em>%s</em> %d %s",
2113 gettext("Current selection:"),
2114 n, ((n == 1)
2115 ? gettext("tagged item:")
2116 : gettext("tagged items:")));
2117 StrAllocCopy(cd, doc->address);
2118 HTUnEscapeSome(cd, "/");
2119 LYAddHtmlSep(&cd);
2120 m = (n < NUM_TAGS_TO_WRITE) ? n : NUM_TAGS_TO_WRITE;
2121 for (i = 1; i <= m; i++) {
2122 cp1 = HTRelative((char *) HTList_objectAt(tagged, i - 1),
2123 (*cd ? cd : "file://localhost"));
2124 HTUnEscape(cp1);
2125 LYEntify(&cp1, TRUE); /* _should_ do this everywhere... */
2126 fprintf(fp0, "%s<br>\n %s",
2127 (i == 1 ? "" : " ,"), cp1);
2128 FREE(cp1);
2129 }
2130 if (n > m) {
2131 fprintf(fp0, " , ...");
2132 }
2133 fprintf(fp0, "<p>\n");
2134 FREE(cd);
2135 }
2136
2137 /*
2138 * If menu_head is NULL then use defaults and link them together now.
2139 */
2140 if (menu_head == NULL) {
2141 for (mp = defmenu; GetDiredHref(mp) != NULL; mp++)
2142 mp->next = (mp + 1);
2143 (--mp)->next = NULL;
2144 menu_head = defmenu;
2145 }
2146
2147 for (mp = menu_head; mp != NULL; mp = mp->next) {
2148 if (mp->cond != DE_TAG && !nothing_tagged)
2149 continue;
2150 if (mp->cond == DE_TAG && nothing_tagged)
2151 continue;
2152 if (mp->cond == DE_DIR &&
2153 (!*path || !S_ISDIR(dir_info.st_mode)))
2154 continue;
2155 if (mp->cond == DE_FILE &&
2156 (!*path || !S_ISREG(dir_info.st_mode)))
2157 continue;
2158 #ifdef S_IFLNK
2159 if (mp->cond == DE_SYMLINK &&
2160 (!*path || !S_ISLNK(dir_info.st_mode)))
2161 continue;
2162 #endif
2163 my_suffix = GetDiredSuffix(mp);
2164 if (non_empty(my_suffix) &&
2165 (strlen(path) < strlen(my_suffix) ||
2166 strcmp(my_suffix, &path[(strlen(path) - strlen(my_suffix))]) != 0))
2167 continue;
2168 dir_url = HTEscape(dir, URL_PATH);
2169 path_url = HTEscape(path, URL_PATH);
2170 fprintf(fp0, "<a href=\"%s",
2171 render_item(GetDiredHref(mp),
2172 path_url, dir_url, buf, sizeof(buf), YES));
2173 fprintf(fp0, "\">%s</a> ",
2174 render_item(GetDiredLink(mp),
2175 path, dir, buf, sizeof(buf), NO));
2176 fprintf(fp0, "%s<br>\n",
2177 render_item(GetDiredRest(mp),
2178 path, dir, buf, sizeof(buf), NO));
2179 FREE(dir_url);
2180 FREE(path_url);
2181 }
2182 FREE(path);
2183
2184 if (uploaders != NULL) {
2185 fprintf(fp0, "<p>Upload to current directory:<p>\n");
2186 for (count = 0, nxt = uploaders;
2187 nxt != NULL;
2188 nxt = nxt->next, count++) {
2189 fprintf(fp0,
2190 "<a href=\"LYNXDIRED://UPLOAD=%d/TO=%s\"> %s </a><br>\n",
2191 count, dir, nxt->name);
2192 }
2193 }
2194 FREE(dir);
2195
2196 EndInternalPage(fp0);
2197 LYCloseTempFP(fp0);
2198
2199 LYforce_no_cache = TRUE;
2200
2201 return (0);
2202 }
2203
2204 /*
2205 * Check DIRED filename, return true on success
2206 */
get_filename(const char * prompt,bstring ** bufp)2207 static int get_filename(const char *prompt,
2208 bstring **bufp)
2209 {
2210 char *cp;
2211
2212 _statusline(prompt);
2213
2214 (void) LYgetBString(bufp, FALSE, 0, NORECALL);
2215 if (strstr((*bufp)->str, "../") != NULL) {
2216 HTAlert(gettext("Illegal filename; request ignored."));
2217 return FALSE;
2218 } else if (no_dotfiles || !show_dotfiles) {
2219 cp = LYLastPathSep((*bufp)->str); /* find last slash */
2220 if (cp)
2221 cp += 1;
2222 else
2223 cp = (*bufp)->str;
2224 if (*cp == '.') {
2225 HTAlert(gettext("Illegal filename; request ignored."));
2226 return FALSE;
2227 }
2228 }
2229 return !isBEmpty((*bufp));
2230 }
2231
2232 #ifdef OK_INSTALL
2233
2234 #define LYEXECV_MAX_ARGC 15
2235 /* these are quasi-constant once they have been allocated: */
2236 static char **install_argp = NULL; /* args for execv install */
2237 static char *install_path = NULL; /* auxiliary */
2238
2239 #ifdef LY_FIND_LEAKS
clear_install_path(void)2240 static void clear_install_path(void)
2241 {
2242 FREE(install_argp);
2243 FREE(install_path);
2244 }
2245 #endif /* LY_FIND_LEAKS */
2246
2247 /*
2248 * Fill in args array for execv (or execvp etc.) call, after first allocating
2249 * it if necessary. No fancy parsing, cmd_args is just split at spaces. Leave
2250 * room for reserve additional args to be added by caller.
2251 *
2252 * On success *argvp points to new args vector, *pathp is auxiliary. On
2253 * success returns index of next argument, else -1. This is generic enough
2254 * that it could be used for other calls than install, except the atexit call.
2255 * Go through this trouble for install because INSTALL_ARGS may be significant,
2256 * and someone may configure it with more than one significant flags. - kw
2257 */
fill_argv_for_execv(char *** argvp,char ** pathp,char * cmd_path,const char * cmd_args,int reserve)2258 static int fill_argv_for_execv(char ***argvp,
2259 char **pathp,
2260 char *cmd_path,
2261 const char *cmd_args,
2262 int reserve)
2263 {
2264 int n = 0;
2265
2266 char **args;
2267 char *cp;
2268
2269 if (*argvp == NULL) {
2270 *argvp = typecallocn(char *, LYEXECV_MAX_ARGC + 1);
2271
2272 if (!*argvp)
2273 return (-1);
2274 #ifdef LY_FIND_LEAKS
2275 atexit(clear_install_path);
2276 #endif
2277 }
2278 args = *argvp;
2279 args[n++] = cmd_path;
2280 if (cmd_args) {
2281 StrAllocCopy(*pathp, cmd_args);
2282 cp = strtok(*pathp, " ");
2283 if (cp) {
2284 while (cp && (n < LYEXECV_MAX_ARGC - reserve)) {
2285 args[n++] = cp;
2286 cp = strtok(NULL, " ");
2287 }
2288 if (cp && (n >= LYEXECV_MAX_ARGC - reserve)) {
2289 CTRACE((tfp, "Too many args for '%s' in '%s'!\n",
2290 NONNULL(cmd_path), cmd_args));
2291 return (-1);
2292 }
2293 } else {
2294 args[n++] = *pathp;
2295 }
2296 }
2297 args[n] = (char *) 0;
2298 return (n);
2299 }
2300
2301 /*
2302 * Install the specified file or directory.
2303 */
local_install(char * destpath,char * srcpath,char ** newpath)2304 BOOLEAN local_install(char *destpath,
2305 char *srcpath,
2306 char **newpath)
2307 {
2308 char *tmpbuf = NULL;
2309 static char savepath[DIRED_MAXBUF]; /* This will be the link that
2310
2311 is to be installed. */
2312 struct stat dir_info;
2313 char **args;
2314 HTList *tag;
2315 char *cp = NULL;
2316 char *tmpdest = NULL;
2317 int count = 0;
2318 int n = 0; /* indices into 'args[]' */
2319 static int src = -1;
2320 const char *program;
2321
2322 if ((program = HTGetProgramPath(ppINSTALL)) == NULL) {
2323 HTAlert(gettext("Install in the selected directory not permitted."));
2324 return 0;
2325 }
2326
2327 /*
2328 * Determine the status of the selected item.
2329 */
2330 if (srcpath) {
2331 srcpath = strip_trailing_slash(srcpath);
2332 if (is_url(srcpath)) {
2333 char *local_src = HTfullURL_toFile(srcpath);
2334
2335 if (!ok_localname(savepath, local_src)) {
2336 FREE(local_src);
2337 return 0;
2338 }
2339 FREE(local_src);
2340 } else if (!HTList_isEmpty(tagged) &&
2341 srcpath[0] == '\0') {
2342 savepath[0] = '\0'; /* will always use tagged list - kw */
2343 } else if (!ok_localname(savepath, srcpath)) {
2344 return 0;
2345 }
2346 LYforce_no_cache = TRUE;
2347 LYLocalFileToURL(newpath, Home_Dir());
2348 LYAddHtmlSep(newpath);
2349 StrAllocCat(*newpath, INSTALLDIRS_FILE);
2350 LYRegisterUIPage(*newpath, UIP_INSTALL);
2351 return 0;
2352 }
2353
2354 /* deal with ~/ or /~/ at the beginning - kw */
2355 if (LYIsTilde(destpath[0]) &&
2356 (LYIsPathSep(destpath[1]) || destpath[1] == '\0')) {
2357 cp = &destpath[1];
2358 } else if (LYIsPathSep(destpath[0]) && LYIsTilde(destpath[1]) &&
2359 (LYIsPathSep(destpath[2]) || destpath[2] == '\0')) {
2360 cp = &destpath[2];
2361 }
2362 if (cp) {
2363 /* If found, allocate new string, make destpath point to it - kw */
2364 StrAllocCopy(tmpdest, Home_Dir());
2365 if (cp[0] && cp[1]) {
2366 LYAddPathSep(&tmpdest);
2367 StrAllocCat(tmpdest, cp + 1);
2368 }
2369 destpath = tmpdest;
2370 }
2371
2372 destpath = strip_trailing_slash(destpath);
2373
2374 if (!ok_stat(destpath, &dir_info)) {
2375 FREE(tmpdest);
2376 return 0;
2377 } else if (!S_ISDIR(dir_info.st_mode)) {
2378 HTAlert(gettext("The selected item is not a directory! Request ignored."));
2379 FREE(tmpdest);
2380 return 0;
2381 } else if (0 /*directory not writable */ ) {
2382 HTAlert(gettext("Install in the selected directory not permitted."));
2383 FREE(tmpdest);
2384 return 0;
2385 }
2386
2387 statusline(gettext("Just a moment, ..."));
2388
2389 /* fill in the fixed args, if not already done - kw */
2390 if (src > 0 && install_argp) {
2391 n = src;
2392 n++;
2393 } else {
2394 n = fill_argv_for_execv(&install_argp, &install_path,
2395 "install",
2396 #ifdef INSTALL_ARGS
2397 INSTALL_ARGS,
2398 #else
2399 NULL,
2400 #endif /* INSTALL_ARGS */
2401 2);
2402 if (n <= 0) {
2403 src = 0;
2404 HTAlert(gettext("Error building install args"));
2405 FREE(tmpdest);
2406 return 0;
2407 }
2408 src = n++;
2409 }
2410 args = install_argp;
2411
2412 args[n++] = destpath;
2413 args[n] = (char *) 0;
2414 tag = tagged;
2415
2416 if (HTList_isEmpty(tagged)) {
2417 /* simplistic detection of identical src and dest - kw */
2418 if (!strcmp(savepath, destpath)) {
2419 HTUserMsg2(gettext("Source and target are the same: %s"),
2420 savepath);
2421 FREE(tmpdest);
2422 return (-1); /* don't do it */
2423 } else if (!StrNCmp(savepath, destpath, strlen(destpath)) &&
2424 LYIsPathSep(savepath[strlen(destpath)]) &&
2425 LYLastPathSep(savepath + strlen(destpath) + 1) == 0) {
2426 HTUserMsg2(gettext("Already in target directory: %s"),
2427 savepath);
2428 FREE(tmpdest);
2429 return 0; /* don't do it */
2430 }
2431 args[src] = savepath;
2432 HTSprintf0(&tmpbuf, "install %s in %s", savepath, destpath);
2433 if (LYExecv(program, args, tmpbuf) <= 0) {
2434 FREE(tmpbuf);
2435 FREE(tmpdest);
2436 return (-1);
2437 }
2438 count++;
2439 } else {
2440 char *name;
2441
2442 HTSprintf0(&tmpbuf, "install in %s", destpath);
2443 while ((name = (char *) HTList_nextObject(tag))) {
2444 int err;
2445
2446 args[src] = HTfullURL_toFile(name);
2447
2448 /* simplistic detection of identical src and dest - kw */
2449 if (!strcmp(args[src], destpath)) {
2450 HTUserMsg2(gettext("Source and target are the same: %s"),
2451 args[src]);
2452 FREE(args[src]);
2453 continue; /* skip this source file */
2454 } else if (!StrNCmp(args[src], destpath, strlen(destpath)) &&
2455 LYIsPathSep(args[src][strlen(destpath)]) &&
2456 LYLastPathSep(args[src] + strlen(destpath) + 1) == 0) {
2457 HTUserMsg2(gettext("Already in target directory: %s"),
2458 args[src]);
2459 FREE(args[src]);
2460 continue; /* skip this source file */
2461 }
2462 err = (LYExecv(program, args, tmpbuf) <= 0);
2463 FREE(args[src]);
2464 if (err) {
2465 FREE(tmpbuf);
2466 FREE(tmpdest);
2467 return ((count == 0) ? -1 : count);
2468 }
2469 count++;
2470 }
2471 clear_tags();
2472 }
2473 FREE(tmpbuf);
2474 FREE(tmpdest);
2475 HTInfoMsg(gettext("Installation complete"));
2476 return count;
2477 }
2478 #endif /* OK_INSTALL */
2479
2480 /*
2481 * Clear DIRED tags.
2482 */
clear_tags(void)2483 void clear_tags(void)
2484 {
2485 char *cp = NULL;
2486
2487 while ((cp = (char *) HTList_removeLastObject(tagged)) != NULL) {
2488 FREE(cp);
2489 }
2490 if (HTList_isEmpty(tagged))
2491 FREE(tagged);
2492 }
2493
2494 /*
2495 * Handle DIRED menu item.
2496 */
add_menu_item(char * str)2497 void add_menu_item(char *str)
2498 {
2499 struct dired_menu *tmp, *mp;
2500 char *cp;
2501 BOOL used = FALSE;
2502
2503 /*
2504 * First custom menu definition causes entire default menu to be discarded.
2505 */
2506 if (menu_head == defmenu)
2507 menu_head = NULL;
2508
2509 tmp = typecalloc(struct dired_menu);
2510
2511 if (tmp == NULL)
2512 outofmem(__FILE__, "add_menu_item");
2513
2514 /*
2515 * Conditional on tagged != NULL ?
2516 */
2517 if ((cp = StrChr(str, ':')) != 0) {
2518 *cp++ = '\0';
2519 if (strcasecomp(str, "tag") == 0) {
2520 tmp->cond = DE_TAG;
2521 } else if (strcasecomp(str, "dir") == 0) {
2522 tmp->cond = DE_DIR;
2523 } else if (strcasecomp(str, "file") == 0) {
2524 tmp->cond = DE_FILE;
2525 #ifdef S_IFLNK
2526 } else if (strcasecomp(str, "link") == 0) {
2527 tmp->cond = DE_SYMLINK;
2528 #endif /* S_IFLNK */
2529 }
2530
2531 /*
2532 * Conditional on matching suffix.
2533 */
2534 str = cp;
2535 if ((cp = StrChr(str, ':')) != 0) {
2536 *cp++ = '\0';
2537 StrAllocCopy(tmp->sfx, str);
2538
2539 str = cp;
2540 if ((cp = StrChr(str, ':')) != 0) {
2541 *cp++ = '\0';
2542 StrAllocCopy(tmp->link, str);
2543
2544 str = cp;
2545 if ((cp = StrChr(str, ':')) != 0) {
2546 *cp++ = '\0';
2547 StrAllocCopy(tmp->rest, str);
2548
2549 StrAllocCopy(tmp->href, cp);
2550
2551 if (menu_head) {
2552 for (mp = menu_head;
2553 mp && mp->next != NULL;
2554 mp = mp->next) {
2555 ;
2556 }
2557 if (mp != NULL) {
2558 mp->next = tmp;
2559 used = TRUE;
2560 }
2561 } else {
2562 menu_head = tmp;
2563 used = TRUE;
2564 }
2565 }
2566 }
2567 }
2568 }
2569 if (!used)
2570 FREE(tmp);
2571 }
2572
reset_dired_menu(void)2573 void reset_dired_menu(void)
2574 {
2575 if (menu_head != defmenu) {
2576 struct dired_menu *mp, *mp_next = NULL;
2577
2578 for (mp = menu_head; mp != NULL; mp = mp_next) {
2579 FREE(mp->sfx);
2580 FREE(mp->link);
2581 FREE(mp->rest);
2582 FREE(mp->href);
2583 mp_next = mp->next;
2584 FREE(mp);
2585 }
2586 menu_head = NULL;
2587 }
2588 }
2589
2590 /*
2591 * Create URL for DIRED HREF value.
2592 */
render_item(const char * s,const char * path,const char * dir,char * buf,size_t bufsize,int url_syntax)2593 static char *render_item(const char *s,
2594 const char *path,
2595 const char *dir,
2596 char *buf,
2597 size_t bufsize,
2598 int url_syntax)
2599 {
2600 const char *cp;
2601 char *bp;
2602 char overrun = '\0';
2603 char *taglist = NULL;
2604
2605 #define BP_INC (bp>buf+bufsize-2 ? &overrun : bp++)
2606 /* Buffer overrun could happen for very long
2607 tag list, if %l or %t are used */
2608 bp = buf;
2609 while (*s && !overrun) {
2610 if (*s == '%') {
2611 s++;
2612 switch (*s) {
2613 case '%':
2614 *BP_INC = '%';
2615 break;
2616 case 'p':
2617 cp = path;
2618 if (!LYIsHtmlSep(*cp))
2619 *BP_INC = '/';
2620 while (*cp)
2621 *BP_INC = *cp++;
2622 break;
2623 case 'd':
2624 cp = dir;
2625 if (!LYIsHtmlSep(*cp))
2626 *BP_INC = '/';
2627 while (*cp)
2628 *BP_INC = *cp++;
2629 break;
2630 case 'f':
2631 cp = LYLastPathSep(path);
2632 if (cp)
2633 cp++;
2634 else
2635 cp = path;
2636 while (*cp)
2637 *BP_INC = *cp++;
2638 break;
2639 case 'l':
2640 case 't':
2641 if (!HTList_isEmpty(tagged)) {
2642 HTList *cur = tagged;
2643 char *name;
2644
2645 while (!overrun &&
2646 (name = (char *) HTList_nextObject(cur)) != NULL) {
2647 if (*s == 'l' && (cp = strrchr(name, '/')))
2648 cp++;
2649 else
2650 cp = name;
2651 StrAllocCat(taglist, cp);
2652 StrAllocCat(taglist, " "); /* should this be %20? */
2653 }
2654 }
2655 if (taglist) {
2656 /* could HTUnescape here... */
2657 cp = taglist;
2658 while (*cp)
2659 *BP_INC = *cp++;
2660 FREE(taglist);
2661 }
2662 break;
2663 default:
2664 *BP_INC = '%';
2665 *BP_INC = *s;
2666 break;
2667 }
2668 } else {
2669 /*
2670 * Other chars come from the lynx.cfg or the default. Let's assume
2671 * there isn't anything weird there that needs escaping.
2672 */
2673 *BP_INC = *s;
2674 }
2675 s++;
2676 }
2677 if (overrun & url_syntax) {
2678 HTAlert(gettext("Temporary URL or list would be too long."));
2679 bp = buf; /* set to start, will return empty string as URL */
2680 }
2681 *bp = '\0';
2682 return buf;
2683 }
2684
2685 #endif /* DIRED_SUPPORT */
2686