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