aboutsummaryrefslogtreecommitdiff
path: root/unix/os/zfacss.c
blob: f6624698f5ce48d1e88d785d841db16df6c24fbc (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
/* Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
 */

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <ctype.h>

#define	import_kernel
#define	import_knames
#define import_spp
#include <iraf.h>

#define	SZ_TESTBLOCK	1024		/* for TEXT/BINARY heuristic	*/
#define	MAX_LINELEN	256		/* when looking for newlines	*/
#define	R		04		/* UNIX access() codes		*/
#define W		02
#define ctrlcode(c)	((c) >= '\007' && (c) <= '\017')


/* ZFACSS -- Try to determine if FILE is accessible in the indicated MODE.
 * If file is accessible for reading and TYPE is given as TEXT_FILE,
 * look at the first block of data to see if it is legal text data.
 * ACCESS(file,0,0) merely checks that the file exists.  Any file is a
 * legal binary file.
 */
int
ZFACSS (
  PKCHAR  *fname,
  XINT	  *mode, 
  XINT 	  *type,
  XINT 	  *status
)
{
	static	char modebits[] = { 0, R, R|W, W, R|W };
	register char	*ip, ch;
	register int	n;
	char	buf[SZ_TESTBLOCK];
	int	fd, acmode, accessible, nchars, newline_seen;
	struct	stat fi;

	/* Null filename? */
	if (*(char *)fname == EOS) {
	    *status = NO;
	    return (NO);
	}

	/* Map IRAF access mode into UNIX access mode.
	 */
	if (*mode >= READ_ONLY && *mode <= APPEND)
	    acmode = modebits[*mode];
	else if (*mode == 0 && *type != 0)
	    acmode = R;
	else
	    acmode = 0;

	/* Is file accessible with the given mode.
	 */
	accessible = (access ((char *)fname, acmode) == 0);

	if (accessible && *type == DIRECTORY_FILE) {
	    stat ((char *)fname, &fi);
	    if (fi.st_mode & S_IFDIR)
		*status = YES;
	    else
		*status = NO;
	    return (*status);

	} else if (!accessible && *type == SYMLINK_FILE) {
	    lstat ((char *)fname, &fi);
	    if (fi.st_mode & S_IFLNK)
		*status = YES;
	    else
		*status = NO;

	    return (*status);
	}

	/* If we have to check the file type (text or binary), then we must
	 * actually look at some file data since UNIX does not discriminate
	 * between text and binary files.  NOTE that this heuristic is not
	 * completely reliable and can fail, although in practice it does
	 * very well.
	 */
	if (accessible && (acmode & R) && *type != 0) {
	    stat ((char *)fname, &fi);

	    /* Do NOT read from a special device (may block) */
	    if ((fi.st_mode & S_IFMT) & S_IFREG) {
		/* If we are testing for a text file the portion of the file
		 * tested must consist of only printable ascii characters or
		 * whitespace, with occasional newline line delimiters.
		 * Control characters embedded in the text will cause the
		 * heuristic to fail.  We require newlines to be present in
		 * the text to disinguish the case of a binary file containing
		 * only ascii data, e.g., a cardimage file.
		 */
		fd = open ((char *)fname, 0);
		if (fd >= 0 && (nchars = read (fd, buf, SZ_TESTBLOCK)) > 0) {
		    ip = buf;
		    for (n=nchars, newline_seen=0;  --n >= 0;  ) {
			ch = *ip++;
			if (ch == '\n')
			    newline_seen++;
			else if (!isprint(ch) && !isspace(ch) && !ctrlcode(ch))
			    break;
		    }

		    if (*type == TEXT_FILE) {
			if (n >= 0 || (nchars > MAX_LINELEN && !newline_seen))
			    accessible = NO;
		    } else if (*type == BINARY_FILE && n < 0)
			accessible = NO;
		    close (fd);
		}
	    } else if (fi.st_mode & S_IFCHR && *type != TEXT_FILE)
		accessible = NO;
	}
		
	(*status) = accessible;

	return (*status);
}