1 /*
2 * $Id: files.c,v 1.6 2009-05-22 04:02:26 dhmunro Exp $
3 * UNIX version of play file operations
4 */
5 /* Copyright (c) 2005, The Regents of the University of California.
6 * All rights reserved.
7 * This file is part of yorick (http://yorick.sourceforge.net).
8 * Read the accompanying LICENSE file for details.
9 */
10
11 #ifndef _POSIX_SOURCE
12 /* to get fileno declared */
13 #define _POSIX_SOURCE 1
14 #endif
15 #ifndef _XOPEN_SOURCE
16 /* to get popen declared */
17 #define _XOPEN_SOURCE 500
18 #endif
19
20 #include "config.h"
21 #include "pstdio.h"
22 #include "pstdlib.h"
23 #include "playu.h"
24
25 #include <stdio.h>
26 #include <string.h>
27 #include <unistd.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30
31 struct p_file {
32 p_file_ops *ops;
33 FILE *fp; /* use FILE* for text file operations */
34 int fd; /* use file descriptor for binary file operations */
35 int binary; /* 1 to use read/write/lseek, 2 if pipe */
36 int errflag; /* bit 1 is eof, bit 2 is error for binary==1 */
37 };
38
39 static unsigned long pv_fsize(p_file *file);
40 static unsigned long pv_ftell(p_file *file);
41 static int pv_fseek(p_file *file, unsigned long addr);
42
43 static char *pv_fgets(p_file *file, char *buf, int buflen);
44 static int pv_fputs(p_file *file, const char *buf);
45 static unsigned long pv_fread(p_file *file,
46 void *buf, unsigned long nbytes);
47 static unsigned long pv_fwrite(p_file *file,
48 const void *buf, unsigned long nbytes);
49
50 static int pv_feof(p_file *file);
51 static int pv_ferror(p_file *file);
52 static int pv_fflush(p_file *file);
53 static int pv_fclose(p_file *file);
54
55 static p_file_ops u_file_ops = {
56 &pv_fsize, &pv_ftell, &pv_fseek,
57 &pv_fgets, &pv_fputs, &pv_fread, &pv_fwrite,
58 &pv_feof, &pv_ferror, &pv_fflush, &pv_fclose };
59
60 p_file *
p_fopen(const char * unix_name,const char * mode)61 p_fopen(const char *unix_name, const char *mode)
62 {
63 FILE *fp = fopen(u_pathname(unix_name), mode);
64 p_file *f = fp? p_malloc(sizeof(p_file)) : 0;
65 if (f) {
66 f->ops = &u_file_ops;
67 f->fp = fp;
68 f->fd = fileno(fp);
69 for (; mode[0] ; mode++) if (mode[0]=='b') break;
70 f->binary = (mode[0]=='b');
71 f->errflag = 0;
72 u_fdwatch(f->fd, 1);
73 } else if (fp) {
74 fclose(fp);
75 }
76 return f;
77 }
78
79 p_file *
p_popen(const char * command,const char * mode)80 p_popen(const char *command, const char *mode)
81 {
82 #ifndef NO_PROCS
83 FILE *fp = popen(command, mode[0]=='w'? "w" : "r");
84 p_file *f = fp? p_malloc(sizeof(p_file)) : 0;
85 if (f) {
86 f->ops = &u_file_ops;
87 f->fp = fp;
88 f->fd = fileno(fp);
89 f->binary = 2;
90 f->errflag = 0;
91 u_fdwatch(f->fd, 1);
92 } else if (fp) {
93 pclose(fp);
94 }
95 return f;
96 #else
97 return (p_file *)0;
98 #endif
99 }
100
101 p_file *
p_fd_raw(int fd)102 p_fd_raw(int fd)
103 {
104 p_file *f = p_malloc(sizeof(p_file));
105 if (f) {
106 f->ops = &u_file_ops;
107 f->fp = 0;
108 f->fd = fd;
109 f->binary = 5;
110 u_fdwatch(f->fd, 1);
111 }
112 return f;
113 }
114
115 static int
pv_fclose(p_file * file)116 pv_fclose(p_file *file)
117 {
118 int flag = (file->binary&2)? pclose(file->fp) : fclose(file->fp);
119 u_fdwatch(file->fd, 0);
120 p_free(file);
121 return flag;
122 }
123
124 /* could make closeall/fdwatch ultra-clever,
125 * but dumb-and-simple seems to work just as well
126 */
127 static int u_fd_max = 16; /* minimum possible OPEN_MAX in POSIX limits.h */
128 void
u_closeall(void)129 u_closeall(void)
130 {
131 int i;
132 for (i=3 ; i<u_fd_max ; i++) close(i);
133 }
134 void
u_fdwatch(int fd,int on)135 u_fdwatch(int fd, int on)
136 {
137 if (on && fd>=u_fd_max) u_fd_max = fd+1;
138 }
139
140 static char *
pv_fgets(p_file * file,char * buf,int buflen)141 pv_fgets(p_file *file, char *buf, int buflen)
142 {
143 return fgets(buf, buflen, file->fp);
144 }
145
146 static int
pv_fputs(p_file * file,const char * buf)147 pv_fputs(p_file *file, const char *buf)
148 {
149 return fputs(buf, file->fp);
150 }
151
152 static unsigned long
pv_fread(p_file * file,void * buf,unsigned long nbytes)153 pv_fread(p_file *file, void *buf, unsigned long nbytes)
154 {
155 if (file->binary & 1) {
156 long n = read(file->fd, buf, nbytes);
157 if (n!=nbytes && n!=-1L) {
158 /* cope with filesystems which permit read/write to return before
159 * full request completes, even in blocking mode (NFS, Lustre)
160 * - note that read behavior undefined if signed nbytes is < 0
161 * - assume (POSIX standard) that read/write cannot return zero
162 * when O_NONBLOCK is not set - they will not return until
163 * at least one byte has been transferred
164 * UNLESS we are positioned at end of file on read
165 * - apparently read/write timeout after about 1 second even if
166 * the requested number of bytes has not been transferred
167 */
168 unsigned long nb = n;
169 char *cbuf = buf;
170 for (;;) {
171 n = read(file->fd, cbuf+nb, nbytes-nb);
172 if (n == 0) { file->errflag |= 1; return nb; }
173 if ((nb+=n) == nbytes) return nbytes;
174 if (n < 0) { file->errflag |= 2; return -1L; }
175 }
176 }
177 return n;
178 } else {
179 return fread(buf, 1, nbytes, file->fp);
180 }
181 }
182
183 static unsigned long
pv_fwrite(p_file * file,const void * buf,unsigned long nbytes)184 pv_fwrite(p_file *file, const void *buf, unsigned long nbytes)
185 {
186 if (file->binary & 1) {
187 long n = write(file->fd, buf, nbytes);
188 if (n!=nbytes && n!=-1L) {
189 /* see comment in p_fread above */
190 unsigned long nb = n;
191 const char *cbuf = buf;
192 for (;;) {
193 n = write(file->fd, cbuf+nb, nbytes-nb);
194 if ((nb+=n) == nbytes) return nbytes;
195 if (n <= 0) { file->errflag |= 2; return -1L; }
196 }
197 }
198 return n;
199 } else {
200 return fwrite(buf, 1, nbytes, file->fp);
201 }
202 }
203
204 static unsigned long
pv_ftell(p_file * file)205 pv_ftell(p_file *file)
206 {
207 if (file->binary & 1)
208 return lseek(file->fd, 0L, SEEK_CUR);
209 else if (!(file->binary & 2))
210 return ftell(file->fp);
211 else
212 return -1L;
213 }
214
215 static int
pv_fseek(p_file * file,unsigned long addr)216 pv_fseek(p_file *file, unsigned long addr)
217 {
218 file->errflag &= ~1;
219 if (file->binary & 1)
220 return -(lseek(file->fd, addr, SEEK_SET)==-1L);
221 else if (!(file->binary & 2))
222 return fseek(file->fp, addr, SEEK_SET);
223 else
224 return -1;
225 }
226
227 static int
pv_fflush(p_file * file)228 pv_fflush(p_file *file)
229 {
230 return (file->binary & 1)? 0 : fflush(file->fp);
231 }
232
233 static int
pv_feof(p_file * file)234 pv_feof(p_file *file)
235 {
236 return (file->binary&1)? ((file->errflag&1) != 0) : feof(file->fp);
237 }
238
239 static int
pv_ferror(p_file * file)240 pv_ferror(p_file *file)
241 {
242 int flag = (file->binary&1)? ((file->errflag&2) != 0) : ferror(file->fp);
243 clearerr(file->fp);
244 file->errflag = 0;
245 return flag;
246 }
247
248 static unsigned long
pv_fsize(p_file * file)249 pv_fsize(p_file *file)
250 {
251 struct stat buf;
252 if (fstat(file->fd, &buf)) return 0;
253 return buf.st_size;
254 }
255
256 int
p_remove(const char * unix_name)257 p_remove(const char *unix_name)
258 {
259 return remove(u_pathname(unix_name));
260 }
261
262 int
p_rename(const char * unix_old,const char * unix_new)263 p_rename(const char *unix_old, const char *unix_new)
264 {
265 char old[P_WKSIZ+1];
266 old[0] = '\0';
267 strncat(old, u_pathname(unix_old), P_WKSIZ);
268 return rename(old, u_pathname(unix_new));
269 }
270
271 char *
p_native(const char * unix_name)272 p_native(const char *unix_name)
273 {
274 return p_strcpy(u_pathname(unix_name));
275 }
276