1/*$Header: /p/tcsh/cvsroot/tcsh/win32/stdio.c,v 1.9 2006/03/11 01:47:40 amold Exp $*/
2/*-
3 * Copyright (c) 1980, 1991 The Regents of the University of California.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of the University nor the names of its contributors
15 *    may be used to endorse or promote products derived from this software
16 *    without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30/*
31 * stdio.c Implement a whole load of i/o functions.
32 *         This makes it much easier to keep track of inherited handles and
33 *         also makes us reasonably vendor crt-independent.
34 * -amol
35 *
36 */
37
38#define WIN32_LEAN_AND_MEAN
39#include <windows.h>
40#include <stdio.h>
41#include <errno.h>
42#include <sys/types.h>
43#include <sys/stat.h>
44#include <fcntl.h>
45#define STDIO_C
46#include <ntport.h>
47#include <forkdata.h>
48
49
50#define __MAX_OPEN_FILES 64
51
52#define FIOCLEX 1
53#define FCONSOLE 2
54
55typedef struct _myfile {
56	HANDLE  handle;
57	unsigned long flags;
58} MY_FILE;
59
60typedef unsigned long u_long;
61
62#define INVHL (INVALID_HANDLE_VALUE)
63
64MY_FILE __gOpenFiles[__MAX_OPEN_FILES]={0};
65MY_FILE __gOpenFilesCopy[__MAX_OPEN_FILES]={0};
66
67MY_FILE *my_stdin=0, *my_stdout=0, *my_stderr=0;
68
69extern int didfds;
70int __dup_stdin = 0;
71
72
73void init_stdio(void) {
74
75	int i;
76	__gOpenFiles[0].handle = GetStdHandle(STD_INPUT_HANDLE);
77	__gOpenFiles[1].handle = GetStdHandle(STD_OUTPUT_HANDLE);
78	__gOpenFiles[2].handle = GetStdHandle(STD_ERROR_HANDLE);
79
80	__gOpenFiles[0].flags = (GetFileType(ULongToPtr(STD_INPUT_HANDLE))==
81			FILE_TYPE_CHAR)?  FCONSOLE:0;
82	__gOpenFiles[1].flags = (GetFileType(ULongToPtr(STD_OUTPUT_HANDLE))==
83			FILE_TYPE_CHAR)?  FCONSOLE:0;
84	__gOpenFiles[2].flags = (GetFileType(ULongToPtr(STD_ERROR_HANDLE))==
85			FILE_TYPE_CHAR)?  FCONSOLE:0;
86
87	for(i=3;i<__MAX_OPEN_FILES;i++) {
88		__gOpenFiles[i].handle = INVHL;
89		__gOpenFilesCopy[i].handle = INVHL;
90		__gOpenFiles[i].flags = 0;
91	}
92
93	my_stdin = &__gOpenFiles[0];
94	my_stdout = &__gOpenFiles[1];
95	my_stderr = &__gOpenFiles[2];
96}
97
98	void nt_close_on_exec(int fd, int on) {
99		if(on)
100			__gOpenFiles[fd].flags |= FIOCLEX;
101		else
102			__gOpenFiles[fd].flags &= ~FIOCLEX;
103	}
104void restore_fds(void ) {
105	int i;
106	int min=3;
107
108	if (__forked && (didfds|| __dup_stdin))
109		min =0;
110	//
111	// ok for tcsh. see fork.c for why
112	//
113	__gOpenFiles[0].handle = INVHL;
114	__gOpenFiles[1].handle = INVHL;
115	__gOpenFiles[2].handle = INVHL;
116	my_stdin = &__gOpenFiles[0];
117	my_stdout = &__gOpenFiles[1];
118	my_stderr = &__gOpenFiles[2];
119	for(i=min;i<__MAX_OPEN_FILES;i++) {
120		if (__gOpenFilesCopy[i].handle == INVHL)
121			continue;
122		__gOpenFiles[i].handle = __gOpenFilesCopy[i].handle ;
123		__gOpenFiles[i].flags = __gOpenFilesCopy[i].flags ;
124	}
125}
126void close_copied_fds(void ) {
127	int i;
128	int min=3;
129	if (didfds|| __dup_stdin)
130		min =0;
131	for(i=min;i<__MAX_OPEN_FILES;i++) {
132		if (__gOpenFilesCopy[i].handle == INVHL)
133			continue;
134		CloseHandle((HANDLE)__gOpenFilesCopy[i].handle);
135		__gOpenFilesCopy[i].handle = INVHL;
136	}
137	__dup_stdin=0;
138}
139void copy_fds(void ) {
140	int i;
141	int min=3;
142	if (didfds || __dup_stdin)
143		min =0;
144	for(i=min;i<__MAX_OPEN_FILES;i++) {
145		if (__gOpenFiles[i].handle == INVHL) {
146			__gOpenFilesCopy[i].handle = INVHL;
147			continue;
148		}
149
150		if(!DuplicateHandle(GetCurrentProcess(),
151					(HANDLE)__gOpenFiles[i].handle ,
152					GetCurrentProcess(),
153					(HANDLE*)&__gOpenFilesCopy[i].handle,
154					0, TRUE, DUPLICATE_SAME_ACCESS) )
155			__gOpenFilesCopy[i].handle = INVHL;
156		__gOpenFilesCopy[i].flags = __gOpenFiles[i].flags;
157	}
158}
159intptr_t __nt_get_osfhandle(int fd) {
160	return (intptr_t)(__gOpenFiles[fd].handle);
161}
162int __nt_open_osfhandle(intptr_t h1, int mode) {
163	int i;
164
165	UNREFERENCED_PARAMETER(mode);
166
167	for(i=0;i<__MAX_OPEN_FILES;i++) {
168		if (__gOpenFiles[i].handle == INVHL) {
169			__gOpenFiles[i].handle = (HANDLE)h1;
170			__gOpenFiles[i].flags = 0;
171			return i;
172		}
173	}
174	errno = EMFILE;
175	return -1;
176}
177int nt_close(int fd) {
178
179	if( (fd == -1) ||(__gOpenFiles[fd].handle == INVHL))
180		return 0;
181	CloseHandle((HANDLE)(__gOpenFiles[fd].handle));
182	__gOpenFiles[fd].handle = INVHL;
183	__gOpenFiles[fd].flags = 0;
184
185	//	dprintf("closing 0x%08x\n",(__gOpenFiles[fd].handle));
186	return 0;
187}
188int nt_access(char *filename, int mode) {
189
190	DWORD attribs=(DWORD)-1, bintype;
191	int tries=0;
192	char buf[512];/*FIXBUF*/
193
194	if (!filename) {
195		errno = ENOENT;
196		return -1;
197	}
198	(void)StringCbPrintf(buf,sizeof(buf),"%s",filename);
199retry:
200	attribs = GetFileAttributes(buf);
201	tries++;
202
203	if (attribs == (DWORD) -1) {
204		if( (GetLastError() == ERROR_FILE_NOT_FOUND) && (mode & X_OK) ) {
205			switch(tries){
206				case 1:
207					(void)StringCbPrintf(buf,sizeof(buf),"%s.exe",filename);
208					break;
209				case 2:
210					(void)StringCbPrintf(buf,sizeof(buf),"%s.cmd",filename);
211					break;
212				case 3:
213					(void)StringCbPrintf(buf,sizeof(buf),"%s.bat",filename);
214					break;
215				case 4:
216					(void)StringCbPrintf(buf,sizeof(buf),"%s.com",filename);
217					break;
218				default:
219					goto giveup;
220					break;
221			}
222			goto retry;
223		}
224	}
225giveup:
226	if (attribs == (DWORD)-1 ) {
227		errno = EACCES;
228		return -1;
229	}
230	if ( (mode & W_OK) &&  (attribs & FILE_ATTRIBUTE_READONLY) ) {
231		errno = EACCES;
232		return -1;
233	}
234	if (mode & X_OK) {
235		if ((mode & XD_OK) && (attribs & FILE_ATTRIBUTE_DIRECTORY) ){
236			errno = EACCES;
237			return -1;
238		}
239		if ((!(attribs & FILE_ATTRIBUTE_DIRECTORY)) &&
240				!GetBinaryType(buf,&bintype) &&(tries >4) ) {
241			errno = EACCES;
242			return -1;
243		}
244	}
245	return 0;
246}
247int nt_seek(HANDLE h1, long offset, int how) {
248	DWORD dwmove;
249
250	switch(how) {
251		case SEEK_CUR:
252			dwmove = FILE_CURRENT;
253			break;
254		case SEEK_END:
255			dwmove = FILE_END;
256			break;
257		case SEEK_SET:
258			dwmove = FILE_BEGIN;
259			break;
260		default:
261			errno = EINVAL;
262			return -1;
263	}
264
265	if (SetFilePointer(h1,offset,NULL,dwmove) == -1){
266		errno = EBADF;
267		return -1;
268	}
269	return 0;
270}
271int nt_lseek(int fd,long offset, int how) {
272	HANDLE h1 ;
273	h1 =__gOpenFiles[fd].handle;
274	return nt_seek(h1,offset,how);
275}
276int nt_isatty(int fd) {
277	return (__gOpenFiles[fd].flags & FCONSOLE);
278}
279int nt_dup(int fdin) {
280
281	HANDLE hdup;
282	HANDLE horig =  __gOpenFiles[fdin].handle;
283	int ret;
284
285
286	if (!DuplicateHandle(GetCurrentProcess(),
287				horig,
288				GetCurrentProcess(),
289				&hdup,
290				0,
291				FALSE,
292				DUPLICATE_SAME_ACCESS)) {
293		errno = GetLastError();
294		errno = EBADF;
295		return -1;
296	}
297	ret = __nt_open_osfhandle((intptr_t)hdup,_O_BINARY | _O_NOINHERIT);
298
299	__gOpenFiles[ret].flags = __gOpenFiles[fdin].flags;
300
301	return  ret;
302}
303int nt_dup2(int fdorig,int fdcopy) {
304
305	HANDLE hdup;
306	HANDLE horig =  __gOpenFiles[fdorig].handle;
307
308
309	if (__gOpenFiles[fdcopy].handle != INVHL) {
310		CloseHandle((HANDLE)__gOpenFiles[fdcopy].handle );
311		__gOpenFiles[fdcopy].handle = INVHL;
312		__gOpenFiles[fdcopy].flags = 0;
313	}
314	if (!DuplicateHandle(GetCurrentProcess(),
315				horig,
316				GetCurrentProcess(),
317				&hdup,
318				0,
319				fdcopy<3?TRUE:FALSE, DUPLICATE_SAME_ACCESS)) {
320		errno = GetLastError();
321		errno = EBADF;
322		return -1;
323	}
324	__gOpenFiles[fdcopy].handle = hdup;
325	__gOpenFiles[fdcopy].flags = __gOpenFiles[fdorig].flags;
326	switch(fdcopy) {
327		case 0:
328			SetStdHandle(STD_INPUT_HANDLE,hdup);
329			break;
330		case 1:
331			SetStdHandle(STD_OUTPUT_HANDLE,hdup);
332			break;
333		case 2:
334			SetStdHandle(STD_ERROR_HANDLE,hdup);
335			break;
336		default:
337			break;
338	}
339
340	return  0;
341}
342int nt_pipe2(HANDLE hpipe[2]) {
343
344	SECURITY_ATTRIBUTES secd;
345
346	secd.nLength=sizeof(secd);
347	secd.lpSecurityDescriptor=NULL;
348	secd.bInheritHandle=FALSE;
349
350	return (!CreatePipe(&hpipe[0],&hpipe[1],&secd,0));
351}
352int nt_pipe(int hpipe[2]) {
353	HANDLE hpipe2[2];
354
355	nt_pipe2(hpipe2);
356	hpipe[0] = __nt_open_osfhandle((intptr_t)hpipe2[0],O_NOINHERIT);
357	hpipe[1] = __nt_open_osfhandle((intptr_t)hpipe2[1],O_NOINHERIT);
358	return 0;
359}
360/* check if name is //server. if checkifShare is set,
361 * also check if //server/share
362 */
363int is_server(const char *name,int checkifShare) {
364	const char *p1, *p2;
365
366	if (!*name || !*(name+1))
367		return 0;
368
369	p1 = name;
370	if (((p1[0] != '/') && (p1[0] != '\\') ) ||
371			((p1[1] != '/') && (p1[1] != '\\') ))
372		return 0;
373
374	p2 = p1 + 2;
375	while (*p2 && *p2 != '/' && *p2 != '\\')
376#ifdef DSPMBYTE
377		if (Ismbyte1(*p2) && *(p2 + 1))
378			p2 += 2;
379		else
380#endif /* DSPMBYTE */
381			p2++;
382
383	/* just check for server */
384	if (!checkifShare) {
385		/* null terminated unc server name */
386		/* terminating '/' (//server/) is also ok */
387		if (!*p2 || !*(p2+1))
388			return 1;
389
390	}
391	else {
392		if (!*p2 || !*(p2+1))
393			return 0;
394		p2++;
395		while(*p2 && *p2 != '/' && *p2 != '\\')
396			p2++;
397		if (!*p2 || !*(p2+1))
398			return 1;
399	}
400	return 0;
401
402}
403__inline int is_unc(char *filename) {
404	if (*filename && (*filename == '/' || *filename == '\\')
405			&& *(filename+1)
406			&& (*(filename+1) == '/' || *(filename+1) == '\\')) {
407		return 1;
408	}
409	return 0;
410}
411int nt_stat(const char *filename, struct stat *stbuf) {
412
413	// stat hangs on server name
414	// Use any  directory, since the info in stat means %$!* on
415	// windows anyway.
416	// -amol 5/28/97
417	/* is server or share */
418	if (is_server(filename,0)  || is_server(filename,1) ||
419			(*(filename+1) && *(filename+1) == ':' && !*(filename+2)) ) {
420		return _stat("C:/",(struct _stat *)stbuf);
421	}
422	else
423		return _stat(filename,(struct _stat *)stbuf);
424}
425//
426// replacement for creat that makes handle non-inheritable.
427// -amol
428//
429int nt_creat(const char *filename, int mode) {
430	// ignore the bloody mode
431
432	int fd = 0,is_cons =0;
433	HANDLE retval;
434	SECURITY_ATTRIBUTES security;
435
436	UNREFERENCED_PARAMETER(mode);
437
438
439	security.nLength = sizeof(security);
440	security.lpSecurityDescriptor = NULL;
441	security.bInheritHandle = FALSE;
442
443	if (!_stricmp(filename,"/dev/tty") ){
444		filename = "CONOUT$";
445		is_cons = 1;
446	}
447	else if (!_stricmp(filename,"/dev/null") ){
448		filename = "NUL";
449	}
450	else if (!_stricmp(filename,"/dev/clipboard")) {
451		retval = create_clip_writer_thread();
452		if (retval == INVHL)
453			return -1;
454		goto get_fd;
455	}
456	retval = CreateFile(filename,
457			GENERIC_READ | GENERIC_WRITE,
458			FILE_SHARE_READ | FILE_SHARE_WRITE,
459			is_cons?NULL:&security,
460			CREATE_ALWAYS,
461			0,
462			NULL);
463
464	if (retval == INVALID_HANDLE_VALUE) {
465		errno = EACCES;
466		return -1;
467	}
468get_fd:
469	fd = __nt_open_osfhandle((intptr_t)retval,_O_BINARY);
470	if (fd <0) {
471		//should never happen
472		abort();
473	}
474	else {
475		if (is_cons) {
476			__gOpenFiles[fd].flags = FCONSOLE;
477		}
478	}
479	return fd;
480
481}
482int nt_open(const char *filename, int perms,...) {
483
484	// ignore the bloody mode
485
486	int fd,mode, is_cons=0;
487	HANDLE retval;
488	SECURITY_ATTRIBUTES security;
489	DWORD dwAccess, dwFlags, dwCreateDist;
490	va_list ap;
491
492	va_start(ap,perms);
493	mode = va_arg(ap,int);
494	va_end(ap);
495
496	if (!lstrcmp(filename,"/dev/tty") ){
497		if (perms == O_RDONLY) //o_rdonly is 0
498			filename = "CONIN$";
499		else if (perms & O_WRONLY)
500			filename = "CONOUT$";
501		is_cons = 1;
502	}
503	else if (!lstrcmp(filename,"/dev/null") ){
504		filename = "NUL";
505	}
506	else if (!_stricmp(filename,"/dev/clipboard")) {
507		retval = create_clip_reader_thread();
508		goto get_fd;
509	}
510	security.nLength = sizeof(security);
511	security.lpSecurityDescriptor = NULL;
512	security.bInheritHandle = FALSE;
513
514	switch (perms & (_O_RDONLY | _O_WRONLY | _O_RDWR) ) {
515		case _O_RDONLY:
516			dwAccess = GENERIC_READ;
517			break;
518		case _O_WRONLY:
519			dwAccess = GENERIC_WRITE;
520			break;
521		case _O_RDWR:
522			dwAccess = GENERIC_READ | GENERIC_WRITE ;
523			break;
524		default:
525			errno = EINVAL;
526			return -1;
527	}
528	switch (perms & (_O_CREAT | _O_TRUNC) ){
529		case 0:
530			dwCreateDist = OPEN_EXISTING;
531			break;
532		case _O_CREAT:
533			dwCreateDist = CREATE_ALWAYS;
534			break;
535		case _O_CREAT | _O_TRUNC:
536			dwCreateDist = CREATE_ALWAYS;
537			break;
538		case _O_TRUNC:
539			dwCreateDist = TRUNCATE_EXISTING;
540			break;
541		default:
542			errno = EINVAL;
543			return -1;
544	}
545	dwFlags = 0;
546	if (perms & O_TEMPORARY)
547		dwFlags = FILE_FLAG_DELETE_ON_CLOSE;
548	retval = CreateFile(filename,
549			dwAccess,//GENERIC_READ | GENERIC_WRITE,
550			FILE_SHARE_READ | FILE_SHARE_WRITE,
551			&security,
552			dwCreateDist,//CREATE_ALWAYS,
553			dwFlags,
554			NULL);
555
556	if (retval == INVALID_HANDLE_VALUE) {
557		int err = GetLastError();
558		if (err == ERROR_FILE_NOT_FOUND)
559			errno = ENOENT;
560		else
561			errno = EACCES;
562		return -1;
563	}
564	if (perms & O_APPEND) {
565		SetFilePointer(retval,0,NULL,FILE_END);
566	}
567get_fd:
568	fd = __nt_open_osfhandle((intptr_t)retval,_O_BINARY);
569	if (fd <0) {
570		//should never happen
571		abort();
572	}
573	else {
574		if (is_cons) {
575			__gOpenFiles[fd].flags = FCONSOLE;
576		}
577	}
578	return fd;
579
580}
581/*
582 * This should be the LAST FUNCTION IN THIS FILE
583 *
584 */
585#undef fstat
586#undef _open_osfhandle
587#undef close
588int nt_fstat(int fd, struct stat *stbuf) {
589	int realfd;
590	HANDLE h1;
591
592	errno = EBADF;
593
594	if(!DuplicateHandle(GetCurrentProcess(),
595				(HANDLE)__gOpenFiles[fd].handle,
596				GetCurrentProcess(),
597				&h1,
598				0,
599				FALSE,
600				DUPLICATE_SAME_ACCESS) )
601		return -1;
602	realfd = _open_osfhandle((intptr_t)h1,0);
603	if (realfd <0 )
604		return -1;
605
606	if( fstat(realfd,stbuf) <0 ) {
607		_close(realfd);
608		return -1;
609	}
610	_close(realfd);
611	errno =0;
612	return 0;
613
614}
615
616