"SfR Fresh" - the SfR Freeware/Shareware Archive 
Member "less-424/filename.c" of archive less-424.tar.gz:
As a special service "SfR Fresh" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting with prefixed line numbers.
Alternatively you can here view or download the uninterpreted source code file.
That can be also achieved for any archive member file by clicking within an archive contents listing on the first character of the file(path) respectively on the according byte size field.
1 /*
2 * Copyright (C) 1984-2008 Mark Nudelman
3 *
4 * You may distribute under the terms of either the GNU General Public
5 * License or the Less License, as specified in the README file.
6 *
7 * For more information about less, or for information on how to
8 * contact the author, see the README file.
9 */
10
11
12 /*
13 * Routines to mess around with filenames (and files).
14 * Much of this is very OS dependent.
15 */
16
17 #include "less.h"
18 #include "lglob.h"
19 #if MSDOS_COMPILER
20 #include <dos.h>
21 #if MSDOS_COMPILER==WIN32C && !defined(_MSC_VER)
22 #include <dir.h>
23 #endif
24 #if MSDOS_COMPILER==DJGPPC
25 #include <glob.h>
26 #include <dir.h>
27 #define _MAX_PATH PATH_MAX
28 #endif
29 #endif
30 #ifdef _OSK
31 #include <rbf.h>
32 #ifndef _OSK_MWC32
33 #include <modes.h>
34 #endif
35 #endif
36 #if OS2
37 #include <signal.h>
38 #endif
39
40 #if HAVE_STAT
41 #include <sys/stat.h>
42 #ifndef S_ISDIR
43 #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
44 #endif
45 #ifndef S_ISREG
46 #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
47 #endif
48 #endif
49
50
51 extern int force_open;
52 extern int secure;
53 extern int use_lessopen;
54 extern int ctldisp;
55 extern int utf_mode;
56 extern IFILE curr_ifile;
57 extern IFILE old_ifile;
58 #if SPACES_IN_FILENAMES
59 extern char openquote;
60 extern char closequote;
61 #endif
62
63 /*
64 * Remove quotes around a filename.
65 */
66 public char *
67 shell_unquote(str)
68 char *str;
69 {
70 char *name;
71 char *p;
72
73 name = p = (char *) ecalloc(strlen(str)+1, sizeof(char));
74 if (*str == openquote)
75 {
76 str++;
77 while (*str != '\0')
78 {
79 if (*str == closequote)
80 {
81 if (str[1] != closequote)
82 break;
83 str++;
84 }
85 *p++ = *str++;
86 }
87 } else
88 {
89 char *esc = get_meta_escape();
90 int esclen = strlen(esc);
91 while (*str != '\0')
92 {
93 if (esclen > 0 && strncmp(str, esc, esclen) == 0)
94 str += esclen;
95 *p++ = *str++;
96 }
97 }
98 *p = '\0';
99 return (name);
100 }
101
102 /*
103 * Get the shell's escape character.
104 */
105 public char *
106 get_meta_escape()
107 {
108 char *s;
109
110 s = lgetenv("LESSMETAESCAPE");
111 if (s == NULL)
112 s = DEF_METAESCAPE;
113 return (s);
114 }
115
116 /*
117 * Get the characters which the shell considers to be "metacharacters".
118 */
119 static char *
120 metachars()
121 {
122 static char *mchars = NULL;
123
124 if (mchars == NULL)
125 {
126 mchars = lgetenv("LESSMETACHARS");
127 if (mchars == NULL)
128 mchars = DEF_METACHARS;
129 }
130 return (mchars);
131 }
132
133 /*
134 * Is this a shell metacharacter?
135 */
136 static int
137 metachar(c)
138 char c;
139 {
140 return (strchr(metachars(), c) != NULL);
141 }
142
143 /*
144 * Insert a backslash before each metacharacter in a string.
145 */
146 public char *
147 shell_quote(s)
148 char *s;
149 {
150 char *p;
151 char *newstr;
152 int len;
153 char *esc = get_meta_escape();
154 int esclen = strlen(esc);
155 int use_quotes = 0;
156 int have_quotes = 0;
157
158 /*
159 * Determine how big a string we need to allocate.
160 */
161 len = 1; /* Trailing null byte */
162 for (p = s; *p != '\0'; p++)
163 {
164 len++;
165 if (*p == openquote || *p == closequote)
166 have_quotes = 1;
167 if (metachar(*p))
168 {
169 if (esclen == 0)
170 {
171 /*
172 * We've got a metachar, but this shell
173 * doesn't support escape chars. Use quotes.
174 */
175 use_quotes = 1;
176 } else
177 {
178 /*
179 * Allow space for the escape char.
180 */
181 len += esclen;
182 }
183 }
184 }
185 if (use_quotes)
186 {
187 if (have_quotes)
188 /*
189 * We can't quote a string that contains quotes.
190 */
191 return (NULL);
192 len = strlen(s) + 3;
193 }
194 /*
195 * Allocate and construct the new string.
196 */
197 newstr = p = (char *) ecalloc(len, sizeof(char));
198 if (use_quotes)
199 {
200 SNPRINTF3(newstr, len, "%c%s%c", openquote, s, closequote);
201 } else
202 {
203 while (*s != '\0')
204 {
205 if (metachar(*s))
206 {
207 /*
208 * Add the escape char.
209 */
210 strcpy(p, esc);
211 p += esclen;
212 }
213 *p++ = *s++;
214 }
215 *p = '\0';
216 }
217 return (newstr);
218 }
219
220 /*
221 * Return a pathname that points to a specified file in a specified directory.
222 * Return NULL if the file does not exist in the directory.
223 */
224 static char *
225 dirfile(dirname, filename)
226 char *dirname;
227 char *filename;
228 {
229 char *pathname;
230 char *qpathname;
231 int len;
232 int f;
233
234 if (dirname == NULL || *dirname == '\0')
235 return (NULL);
236 /*
237 * Construct the full pathname.
238 */
239 len= strlen(dirname) + strlen(filename) + 2;
240 pathname = (char *) calloc(len, sizeof(char));
241 if (pathname == NULL)
242 return (NULL);
243 SNPRINTF3(pathname, len, "%s%s%s", dirname, PATHNAME_SEP, filename);
244 /*
245 * Make sure the file exists.
246 */
247 qpathname = shell_unquote(pathname);
248 f = open(qpathname, OPEN_READ);
249 if (f < 0)
250 {
251 free(pathname);
252 pathname = NULL;
253 } else
254 {
255 close(f);
256 }
257 free(qpathname);
258 return (pathname);
259 }
260
261 /*
262 * Return the full pathname of the given file in the "home directory".
263 */
264 public char *
265 homefile(filename)
266 char *filename;
267 {
268 register char *pathname;
269
270 /*
271 * Try $HOME/filename.
272 */
273 pathname = dirfile(lgetenv("HOME"), filename);
274 if (pathname != NULL)
275 return (pathname);
276 #if OS2
277 /*
278 * Try $INIT/filename.
279 */
280 pathname = dirfile(lgetenv("INIT"), filename);
281 if (pathname != NULL)
282 return (pathname);
283 #endif
284 #if MSDOS_COMPILER || OS2
285 /*
286 * Look for the file anywhere on search path.
287 */
288 pathname = (char *) calloc(_MAX_PATH, sizeof(char));
289 #if MSDOS_COMPILER==DJGPPC
290 {
291 char *res = searchpath(filename);
292 if (res == 0)
293 *pathname = '\0';
294 else
295 strcpy(pathname, res);
296 }
297 #else
298 _searchenv(filename, "PATH", pathname);
299 #endif
300 if (*pathname != '\0')
301 return (pathname);
302 free(pathname);
303 #endif
304 return (NULL);
305 }
306
307 /*
308 * Expand a string, substituting any "%" with the current filename,
309 * and any "#" with the previous filename.
310 * But a string of N "%"s is just replaced with N-1 "%"s.
311 * Likewise for a string of N "#"s.
312 * {{ This is a lot of work just to support % and #. }}
313 */
314 public char *
315 fexpand(s)
316 char *s;
317 {
318 register char *fr, *to;
319 register int n;
320 register char *e;
321 IFILE ifile;
322
323 #define fchar_ifile(c) \
324 ((c) == '%' ? curr_ifile : \
325 (c) == '#' ? old_ifile : NULL_IFILE)
326
327 /*
328 * Make one pass to see how big a buffer we
329 * need to allocate for the expanded string.
330 */
331 n = 0;
332 for (fr = s; *fr != '\0'; fr++)
333 {
334 switch (*fr)
335 {
336 case '%':
337 case '#':
338 if (fr > s && fr[-1] == *fr)
339 {
340 /*
341 * Second (or later) char in a string
342 * of identical chars. Treat as normal.
343 */
344 n++;
345 } else if (fr[1] != *fr)
346 {
347 /*
348 * Single char (not repeated). Treat specially.
349 */
350 ifile = fchar_ifile(*fr);
351 if (ifile == NULL_IFILE)
352 n++;
353 else
354 n += strlen(get_filename(ifile));
355 }
356 /*
357 * Else it is the first char in a string of
358 * identical chars. Just discard it.
359 */
360 break;
361 default:
362 n++;
363 break;
364 }
365 }
366
367 e = (char *) ecalloc(n+1, sizeof(char));
368
369 /*
370 * Now copy the string, expanding any "%" or "#".
371 */
372 to = e;
373 for (fr = s; *fr != '\0'; fr++)
374 {
375 switch (*fr)
376 {
377 case '%':
378 case '#':
379 if (fr > s && fr[-1] == *fr)
380 {
381 *to++ = *fr;
382 } else if (fr[1] != *fr)
383 {
384 ifile = fchar_ifile(*fr);
385 if (ifile == NULL_IFILE)
386 *to++ = *fr;
387 else
388 {
389 strcpy(to, get_filename(ifile));
390 to += strlen(to);
391 }
392 }
393 break;
394 default:
395 *to++ = *fr;
396 break;
397 }
398 }
399 *to = '\0';
400 return (e);
401 }
402
403 #if TAB_COMPLETE_FILENAME
404
405 /*
406 * Return a blank-separated list of filenames which "complete"
407 * the given string.
408 */
409 public char *
410 fcomplete(s)
411 char *s;
412 {
413 char *fpat;
414 char *qs;
415
416 if (secure)
417 return (NULL);
418 /*
419 * Complete the filename "s" by globbing "s*".
420 */
421 #if MSDOS_COMPILER && (MSDOS_COMPILER == MSOFTC || MSDOS_COMPILER == BORLANDC)
422 /*
423 * But in DOS, we have to glob "s*.*".
424 * But if the final component of the filename already has
425 * a dot in it, just do "s*".
426 * (Thus, "FILE" is globbed as "FILE*.*",
427 * but "FILE.A" is globbed as "FILE.A*").
428 */
429 {
430 char *slash;
431 int len;
432 for (slash = s+strlen(s)-1; slash > s; slash--)
433 if (*slash == *PATHNAME_SEP || *slash == '/')
434 break;
435 len = strlen(s) + 4;
436 fpat = (char *) ecalloc(len, sizeof(char));
437 if (strchr(slash, '.') == NULL)
438 SNPRINTF1(fpat, len, "%s*.*", s);
439 else
440 SNPRINTF1(fpat, len, "%s*", s);
441 }
442 #else
443 {
444 int len = strlen(s) + 2;
445 fpat = (char *) ecalloc(len, sizeof(char));
446 SNPRINTF1(fpat, len, "%s*", s);
447 }
448 #endif
449 qs = lglob(fpat);
450 s = shell_unquote(qs);
451 if (strcmp(s,fpat) == 0)
452 {
453 /*
454 * The filename didn't expand.
455 */
456 free(qs);
457 qs = NULL;
458 }
459 free(s);
460 free(fpat);
461 return (qs);
462 }
463 #endif
464
465 /*
466 * Try to determine if a file is "binary".
467 * This is just a guess, and we need not try too hard to make it accurate.
468 */
469 public int
470 bin_file(f)
471 int f;
472 {
473 int i;
474 int n;
475 int bin_count = 0;
476 char data[256];
477 char* p;
478 char* pend;
479
480 if (!seekable(f))
481 return (0);
482 if (lseek(f, (off_t)0, SEEK_SET) == BAD_LSEEK)
483 return (0);
484 n = read(f, data, sizeof(data));
485 pend = &data[n];
486 for (p = data; p < pend; )
487 {
488 LWCHAR c = step_char(&p, +1, pend);
489 if (ctldisp == OPT_ONPLUS && IS_CSI_START(c))
490 {
491 do {
492 c = step_char(&p, +1, pend);
493 } while (p < pend && is_ansi_middle(c));
494 } else if (binary_char(c))
495 bin_count++;
496 }
497 /*
498 * Call it a binary file if there are more than 5 binary characters
499 * in the first 256 bytes of the file.
500 */
501 return (bin_count > 5);
502 }
503
504 /*
505 * Try to determine the size of a file by seeking to the end.
506 */
507 static POSITION
508 seek_filesize(f)
509 int f;
510 {
511 off_t spos;
512
513 spos = lseek(f, (off_t)0, SEEK_END);
514 if (spos == BAD_LSEEK)
515 return (NULL_POSITION);
516 return ((POSITION) spos);
517 }
518
519 /*
520 * Read a string from a file.
521 * Return a pointer to the string in memory.
522 */
523 static char *
524 readfd(fd)
525 FILE *fd;
526 {
527 int len;
528 int ch;
529 char *buf;
530 char *p;
531
532 /*
533 * Make a guess about how many chars in the string
534 * and allocate a buffer to hold it.
535 */
536 len = 100;
537 buf = (char *) ecalloc(len, sizeof(char));
538 for (p = buf; ; p++)
539 {
540 if ((ch = getc(fd)) == '\n' || ch == EOF)
541 break;
542 if (p - buf >= len-1)
543 {
544 /*
545 * The string is too big to fit in the buffer we have.
546 * Allocate a new buffer, twice as big.
547 */
548 len *= 2;
549 *p = '\0';
550 p = (char *) ecalloc(len, sizeof(char));
551 strcpy(p, buf);
552 free(buf);
553 buf = p;
554 p = buf + strlen(buf);
555 }
556 *p = ch;
557 }
558 *p = '\0';
559 return (buf);
560 }
561
562
563
564 #if HAVE_POPEN
565
566 FILE *popen();
567
568 /*
569 * Execute a shell command.
570 * Return a pointer to a pipe connected to the shell command's standard output.
571 */
572 static FILE *
573 shellcmd(cmd)
574 char *cmd;
575 {
576 FILE *fd;
577
578 #if HAVE_SHELL
579 char *shell;
580
581 shell = lgetenv("SHELL");
582 if (shell != NULL && *shell != '\0')
583 {
584 char *scmd;
585 char *esccmd;
586
587 /*
588 * Read the output of <$SHELL -c cmd>.
589 * Escape any metacharacters in the command.
590 */
591 esccmd = shell_quote(cmd);
592 if (esccmd == NULL)
593 {
594 fd = popen(cmd, "r");
595 } else
596 {
597 int len = strlen(shell) + strlen(esccmd) + 5;
598 scmd = (char *) ecalloc(len, sizeof(char));
599 SNPRINTF3(scmd, len, "%s %s %s", shell, shell_coption(), esccmd);
600 free(esccmd);
601 fd = popen(scmd, "r");
602 free(scmd);
603 }
604 } else
605 #endif
606 {
607 fd = popen(cmd, "r");
608 }
609 /*
610 * Redirection in `popen' might have messed with the
611 * standard devices. Restore binary input mode.
612 */
613 SET_BINARY(0);
614 return (fd);
615 }
616
617 #endif /* HAVE_POPEN */
618
619
620 /*
621 * Expand a filename, doing any system-specific metacharacter substitutions.
622 */
623 public char *
624 lglob(filename)
625 char *filename;
626 {
627 char *gfilename;
628 char *ofilename;
629
630 ofilename = fexpand(filename);
631 if (secure)
632 return (ofilename);
633 filename = shell_unquote(ofilename);
634
635 #ifdef DECL_GLOB_LIST
636 {
637 /*
638 * The globbing function returns a list of names.
639 */
640 int length;
641 char *p;
642 char *qfilename;
643 DECL_GLOB_LIST(list)
644
645 GLOB_LIST(filename, list);
646 if (GLOB_LIST_FAILED(list))
647 {
648 free(filename);
649 return (ofilename);
650 }
651 length = 1; /* Room for trailing null byte */
652 for (SCAN_GLOB_LIST(list, p))
653 {
654 INIT_GLOB_LIST(list, p);
655 qfilename = shell_quote(p);
656 if (qfilename != NULL)
657 {
658 length += strlen(qfilename) + 1;
659 free(qfilename);
660 }
661 }
662 gfilename = (char *) ecalloc(length, sizeof(char));
663 for (SCAN_GLOB_LIST(list, p))
664 {
665 INIT_GLOB_LIST(list, p);
666 qfilename = shell_quote(p);
667 if (qfilename != NULL)
668 {
669 sprintf(gfilename + strlen(gfilename), "%s ", qfilename);
670 free(qfilename);
671 }
672 }
673 /*
674 * Overwrite the final trailing space with a null terminator.
675 */
676 *--p = '\0';
677 GLOB_LIST_DONE(list);
678 }
679 #else
680 #ifdef DECL_GLOB_NAME
681 {
682 /*
683 * The globbing function returns a single name, and
684 * is called multiple times to walk thru all names.
685 */
686 register char *p;
687 register int len;
688 register int n;
689 char *pathname;
690 char *qpathname;
691 DECL_GLOB_NAME(fnd,drive,dir,fname,ext,handle)
692
693 GLOB_FIRST_NAME(filename, &fnd, handle);
694 if (GLOB_FIRST_FAILED(handle))
695 {
696 free(filename);
697 return (ofilename);
698 }
699
700 _splitpath(filename, drive, dir, fname, ext);
701 len = 100;
702 gfilename = (char *) ecalloc(len, sizeof(char));
703 p = gfilename;
704 do {
705 n = strlen(drive) + strlen(dir) + strlen(fnd.GLOB_NAME) + 1;
706 pathname = (char *) ecalloc(n, sizeof(char));
707 SNPRINTF3(pathname, n, "%s%s%s", drive, dir, fnd.GLOB_NAME);
708 qpathname = shell_quote(pathname);
709 free(pathname);
710 if (qpathname != NULL)
711 {
712 n = strlen(qpathname);
713 while (p - gfilename + n + 2 >= len)
714 {
715 /*
716 * No room in current buffer.
717 * Allocate a bigger one.
718 */
719 len *= 2;
720 *p = '\0';
721 p = (char *) ecalloc(len, sizeof(char));
722 strcpy(p, gfilename);
723 free(gfilename);
724 gfilename = p;
725 p = gfilename + strlen(gfilename);
726 }
727 strcpy(p, qpathname);
728 free(qpathname);
729 p += n;
730 *p++ = ' ';
731 }
732 } while (GLOB_NEXT_NAME(handle, &fnd) == 0);
733
734 /*
735 * Overwrite the final trailing space with a null terminator.
736 */
737 *--p = '\0';
738 GLOB_NAME_DONE(handle);
739 }
740 #else
741 #if HAVE_POPEN
742 {
743 /*
744 * We get the shell to glob the filename for us by passing
745 * an "echo" command to the shell and reading its output.
746 */
747 FILE *fd;
748 char *s;
749 char *lessecho;
750 char *cmd;
751 char *esc;
752 int len;
753
754 esc = get_meta_escape();
755 if (strlen(esc) == 0)
756 esc = "-";
757 esc = shell_quote(esc);
758 if (esc == NULL)
759 {
760 free(filename);
761 return (ofilename);
762 }
763 lessecho = lgetenv("LESSECHO");
764 if (lessecho == NULL || *lessecho == '\0')
765 lessecho = "lessecho";
766 /*
767 * Invoke lessecho, and read its output (a globbed list of filenames).
768 */
769 len = strlen(lessecho) + strlen(ofilename) + (7*strlen(metachars())) + 24;
770 cmd = (char *) ecalloc(len, sizeof(char));
771 SNPRINTF4(cmd, len, "%s -p0x%x -d0x%x -e%s ", lessecho, openquote, closequote, esc);
772 free(esc);
773 for (s = metachars(); *s != '\0'; s++)
774 sprintf(cmd + strlen(cmd), "-n0x%x ", *s);
775 sprintf(cmd + strlen(cmd), "-- %s", ofilename);
776 fd = shellcmd(cmd);
777 free(cmd);
778 if (fd == NULL)
779 {
780 /*
781 * Cannot create the pipe.
782 * Just return the original (fexpanded) filename.
783 */
784 free(filename);
785 return (ofilename);
786 }
787 gfilename = readfd(fd);
788 pclose(fd);
789 if (*gfilename == '\0')
790 {
791 free(gfilename);
792 free(filename);
793 return (ofilename);
794 }
795 }
796 #else
797 /*
798 * No globbing functions at all. Just use the fexpanded filename.
799 */
800 gfilename = save(filename);
801 #endif
802 #endif
803 #endif
804 free(filename);
805 free(ofilename);
806 return (gfilename);
807 }
808
809 /*
810 * See if we should open a "replacement file"
811 * instead of the file we're about to open.
812 */
813 public char *
814 open_altfile(filename, pf, pfd)
815 char *filename;
816 int *pf;
817 void **pfd;
818 {
819 #if !HAVE_POPEN
820 return (NULL);
821 #else
822 char *lessopen;
823 char *cmd;
824 int len;
825 FILE *fd;
826 #if HAVE_FILENO
827 int returnfd = 0;
828 #endif
829
830 if (!use_lessopen || secure)
831 return (NULL);
832 ch_ungetchar(-1);
833 if ((lessopen = lgetenv("LESSOPEN")) == NULL)
834 return (NULL);
835 if (strcmp(filename, "-") == 0)
836 return (NULL);
837 if (*lessopen == '|')
838 {
839 /*
840 * If LESSOPEN starts with a |, it indicates
841 * a "pipe preprocessor".
842 */
843 #if HAVE_FILENO
844 lessopen++;
845 returnfd = 1;
846 #else
847 error("LESSOPEN pipe is not supported", NULL_PARG);
848 return (NULL);
849 #endif
850 }
851
852 len = strlen(lessopen) + strlen(filename) + 2;
853 cmd = (char *) ecalloc(len, sizeof(char));
854 SNPRINTF1(cmd, len, lessopen, filename);
855 fd = shellcmd(cmd);
856 free(cmd);
857 if (fd == NULL)
858 {
859 /*
860 * Cannot create the pipe.
861 */
862 return (NULL);
863 }
864 #if HAVE_FILENO
865 if (returnfd)
866 {
867 int f;
868 char c;
869
870 /*
871 * Read one char to see if the pipe will produce any data.
872 * If it does, push the char back on the pipe.
873 */
874 f = fileno(fd);
875 SET_BINARY(f);
876 if (read(f, &c, 1) != 1)
877 {
878 /*
879 * Pipe is empty. This means there is no alt file.
880 */
881 pclose(fd);
882 return (NULL);
883 }
884 ch_ungetchar(c);
885 *pfd = (void *) fd;
886 *pf = f;
887 return (save("-"));
888 }
889 #endif
890 cmd = readfd(fd);
891 pclose(fd);
892 if (*cmd == '\0')
893 /*
894 * Pipe is empty. This means there is no alt file.
895 */
896 return (NULL);
897 return (cmd);
898 #endif /* HAVE_POPEN */
899 }
900
901 /*
902 * Close a replacement file.
903 */
904 public void
905 close_altfile(altfilename, filename, pipefd)
906 char *altfilename;
907 char *filename;
908 void *pipefd;
909 {
910 #if HAVE_POPEN
911 char *lessclose;
912 FILE *fd;
913 char *cmd;
914 int len;
915
916 if (secure)
917 return;
918 if (pipefd != NULL)
919 {
920 #if OS2
921 /*
922 * The pclose function of OS/2 emx sometimes fails.
923 * Send SIGINT to the piped process before closing it.
924 */
925 kill(((FILE*)pipefd)->_pid, SIGINT);
926 #endif
927 pclose((FILE*) pipefd);
928 }
929 if ((lessclose = lgetenv("LESSCLOSE")) == NULL)
930 return;
931 len = strlen(lessclose) + strlen(filename) + strlen(altfilename) + 2;
932 cmd = (char *) ecalloc(len, sizeof(char));
933 SNPRINTF2(cmd, len, lessclose, filename, altfilename);
934 fd = shellcmd(cmd);
935 free(cmd);
936 if (fd != NULL)
937 pclose(fd);
938 #endif
939 }
940
941 /*
942 * Is the specified file a directory?
943 */
944 public int
945 is_dir(filename)
946 char *filename;
947 {
948 int isdir = 0;
949
950 filename = shell_unquote(filename);
951 #if HAVE_STAT
952 {
953 int r;
954 struct stat statbuf;
955
956 r = stat(filename, &statbuf);
957 isdir = (r >= 0 && S_ISDIR(statbuf.st_mode));
958 }
959 #else
960 #ifdef _OSK
961 {
962 register int f;
963
964 f = open(filename, S_IREAD | S_IFDIR);
965 if (f >= 0)
966 close(f);
967 isdir = (f >= 0);
968 }
969 #endif
970 #endif
971 free(filename);
972 return (isdir);
973 }
974
975 /*
976 * Returns NULL if the file can be opened and
977 * is an ordinary file, otherwise an error message
978 * (if it cannot be opened or is a directory, etc.)
979 */
980 public char *
981 bad_file(filename)
982 char *filename;
983 {
984 register char *m = NULL;
985
986 filename = shell_unquote(filename);
987 if (!force_open && is_dir(filename))
988 {
989 static char is_a_dir[] = " is a directory";
990
991 m = (char *) ecalloc(strlen(filename) + sizeof(is_a_dir),
992 sizeof(char));
993 strcpy(m, filename);
994 strcat(m, is_a_dir);
995 } else
996 {
997 #if HAVE_STAT
998 int r;
999 struct stat statbuf;
1000
1001 r = stat(filename, &statbuf);
1002 if (r < 0)
1003 {
1004 m = errno_message(filename);
1005 } else if (force_open)
1006 {
1007 m = NULL;
1008 } else if (!S_ISREG(statbuf.st_mode))
1009 {
1010 static char not_reg[] = " is not a regular file (use -f to see it)";
1011 m = (char *) ecalloc(strlen(filename) + sizeof(not_reg),
1012 sizeof(char));
1013 strcpy(m, filename);
1014 strcat(m, not_reg);
1015 }
1016 #endif
1017 }
1018 free(filename);
1019 return (m);
1020 }
1021
1022 /*
1023 * Return the size of a file, as cheaply as possible.
1024 * In Unix, we can stat the file.
1025 */
1026 public POSITION
1027 filesize(f)
1028 int f;
1029 {
1030 #if HAVE_STAT
1031 struct stat statbuf;
1032
1033 if (fstat(f, &statbuf) >= 0)
1034 return ((POSITION) statbuf.st_size);
1035 #else
1036 #ifdef _OSK
1037 long size;
1038
1039 if ((size = (long) _gs_size(f)) >= 0)
1040 return ((POSITION) size);
1041 <