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