Date: Fri, 07 Mar 2003 18:40:25 +0000 From: "Richard Dawe" Sender: rich AT phekda DOT freeserve DOT co DOT uk To: djgpp-workers AT delorie DOT com X-Mailer: Emacs 21.3.50 (via feedmail 8.3.emacs20_6 I) and Blat ver 1.8.6 Subject: New POSIX: pwrite [PATCH] Message-Id: Reply-To: djgpp-workers AT delorie DOT com Hello. Below is a patch to add the new POSIX function pwrite. This is a positional write. This patch requires the _write_fill_seek_gap patch for the test program t-pwrite to work correctly. The patch also includes a test program for lseek: t-lseek. I was unsure whether to duplicate the information about handling out-of-space errors or just refer to the write page from the pwrite page. Any thoughts? OK to commit? Thanks, bye, Rich =] Index: include/unistd.h =================================================================== RCS file: /cvs/djgpp/djgpp/include/unistd.h,v retrieving revision 1.16 diff -p -c -3 -r1.16 unistd.h *** include/unistd.h 4 Feb 2003 20:24:30 -0000 1.16 --- include/unistd.h 7 Mar 2003 18:33:36 -0000 *************** off_t lseek(int _fildes, off_t _offset, *** 134,139 **** --- 134,140 ---- long pathconf(const char *_path, int _name); int pause(void); int pipe(int _fildes[2]); + ssize_t pwrite(int _fildes, const void *_buf, size_t _nbyte, off_t _offset); ssize_t read(int _fildes, void *_buf, size_t _nbyte); int rmdir(const char *_path); int setgid(gid_t _gid); Index: src/libc/posix/unistd/makefile =================================================================== RCS file: /cvs/djgpp/djgpp/src/libc/posix/unistd/makefile,v retrieving revision 1.5 diff -p -c -3 -r1.5 makefile *** src/libc/posix/unistd/makefile 14 Jun 2001 02:56:32 -0000 1.5 --- src/libc/posix/unistd/makefile 7 Mar 2003 18:33:36 -0000 *************** *** 1,3 **** --- 1,4 ---- + # Copyright (C) 2003 DJ Delorie, see COPYING.DJ for details # Copyright (C) 2001 DJ Delorie, see COPYING.DJ for details # Copyright (C) 1996 DJ Delorie, see COPYING.DJ for details # Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details *************** SRC += lseek.c *** 39,44 **** --- 40,46 ---- SRC += pathconf.c SRC += pause.c SRC += pipe.c + SRC += pwrite.c SRC += read.c SRC += rmdir.c SRC += setgid.c Index: src/docs/kb/wc204.txi =================================================================== RCS file: /cvs/djgpp/djgpp/src/docs/kb/wc204.txi,v retrieving revision 1.145 diff -p -c -3 -r1.145 wc204.txi *** src/docs/kb/wc204.txi 2 Mar 2003 23:16:37 -0000 1.145 --- src/docs/kb/wc204.txi 7 Mar 2003 18:33:54 -0000 *************** to @code{EBADF}. *** 904,906 **** --- 904,909 ---- @findex perror The function @code{perror} no longer prints a colon and blank if called with a null pointer or a pointer to a null string. + + @findex pwrite + The function @code{pwrite} was added. Index: tests/libc/posix/unistd/makefile =================================================================== RCS file: /cvs/djgpp/djgpp/tests/libc/posix/unistd/makefile,v retrieving revision 1.4 diff -p -c -3 -r1.4 makefile *** tests/libc/posix/unistd/makefile 1 Mar 2003 13:17:36 -0000 1.4 --- tests/libc/posix/unistd/makefile 7 Mar 2003 18:33:54 -0000 *************** SRC += sleep.c *** 9,13 **** --- 9,15 ---- SRC += tread.c SRC += write.c SRC += t-isatty.c + SRC += t-lseek.c + SRC += t-pwrite.c include $(TOP)/../makefile.inc *** /dev/null Fri Mar 7 18:37:25 2003 --- src/libc/posix/unistd/pwrite.c Fri Mar 7 18:33:54 2003 *************** *** 0 **** --- 1,80 ---- + /* Copyright (C) 2003 DJ Delorie, see COPYING.DJ for details */ + #include + #include + #include + #include + + ssize_t + pwrite (int fd, const void *buf, size_t nbyte, off_t offset) + { + const int flags = __get_fd_flags(fd); + offset_t start_pos, curr_pos; + int old_errno; + ssize_t ret; + + /* Is this a pipe? Disallow on pipes. */ + if (flags & FILE_DESC_PIPE) { + errno = ESPIPE; + return(-1); + } + + /* Is this a redirected standard handle: stdout, stderr? + * I.e.: are the standard handles pipes? Disallow on pipes. */ + switch(fd) { + case STDOUT_FILENO: + case STDERR_FILENO: + if (isatty(fd) == 0) { + errno = ESPIPE; + return(-1); + } + break; + + default: + break; + } + + /* TODO: When we support the Large File Summit, fail here, if the offset + * is negative. */ + #if 0 + if (offset < 0) { + errno = EINVAL; + return(-1); + } + #endif + + /* Get the current position. */ + start_pos = llseek(fd, 0LL, SEEK_CUR); + if (start_pos == -1) + return(-1); /* Pass through errno. */ + + /* Seek to the new position. */ + curr_pos = llseek(fd, offset, SEEK_SET); + if (curr_pos == -1) { + old_errno = errno; + + /* Failed. Try to seek back to the original position. */ + llseek(fd, start_pos, SEEK_SET); + + errno = old_errno; + return(-1); + } + + /* Write the data. */ + ret = write(fd, buf, nbyte); + if (ret < 0) { + old_errno = errno; + + /* Failed. Try to seek back to the original position. */ + llseek(fd, start_pos, SEEK_SET); + + errno = old_errno; + return(-1); + } + + /* Seek back to the start. */ + curr_pos = llseek(fd, start_pos, SEEK_SET); + if (curr_pos == -1) + return(-1); /* Pass through errno. */ + + return(ret); + } *** /dev/null Fri Mar 7 18:37:25 2003 --- src/libc/posix/unistd/pwrite.txh Fri Mar 7 13:03:02 2003 *************** *** 0 **** --- 1,53 ---- + @node pwrite, io + @findex pwrite + @subheading Syntax + + @example + #include + + int pwrite(int file, const void *buffer, size_t count, off_t offset); + @end example + + @subheading Description + + This function writes @var{count} bytes from @var{buffer} to + @var{file} at position @var{offset}. It returns the number of bytes + actually written. It will return zero or a number less than + @var{count} if the disk is full, and may return less than @var{count} + even under valid conditions. + + Note that if @var{file} is a text file, @code{pwrite} may write more + bytes than it reports. + + If @var{count} is zero, the function does nothing and returns zero. + Use @code{_write} if you want to actually ask DOS to write zero bytes. + + The precise behavior of @code{pwrite} when the target filesystem is full + are somewhat troublesome, because DOS doesn't fail the underlying system + call. If your application needs to rely on @code{errno} being set to + @code{ENOSPC} in such cases, you need to invoke @code{pwrite} as shown in + an example for @code{write} (@pxref{write}). In a nutshell, the trick is + to call @code{pwrite} one more time after it returns a value smaller + than the @var{count} parameter; then it will @emph{always} set @code{errno} + if the disk is full. + + @subheading Return Value + + The number of bytes written, zero at EOF, or -1 on error. + + @subheading Portability + + @portability !ansi, !posix-1003.2-1992, posix-1003.1-2001 + + @subheading Example + + @example + const char buf[] = "abc"; + const size_t bufsize = strlen(buf); + + /* Write out buf, then overwrite 'b' with 'd'. NB: We should check + * for errors. */ + lseek(fd, 0L, SEEK_SET); + write(fd, buf, bufsize); + pwrite(fd, "d", 1, 1); + @end example *** /dev/null Fri Mar 7 18:37:25 2003 --- tests/libc/posix/unistd/t-lseek.c Fri Mar 7 14:19:02 2003 *************** *** 0 **** --- 1,81 ---- + #include + #include + #include + #include + #include + + #define TEST_FILE "t-lseek.tst" + + int + main (int argc, char *argv[]) + { + int fd; + off_t o; + int ret; + + fd = open(TEST_FILE, O_RDWR|O_TRUNC|O_CREAT|O_BINARY, S_IRUSR|S_IWUSR); + if (fd < 0) + { + puts("Unable to create test file '" TEST_FILE "'!"); + perror(argv[0]); + puts("FAIL"); + return(EXIT_FAILURE); + } + + ret = write(fd, "a", 1); + if (ret < 0) + { + puts("write() failed!"); + perror(argv[0]); + puts("FAIL"); + return(EXIT_FAILURE); + } + + o = lseek(fd, 1024 * 1024, SEEK_SET); + if (ret < 0) + { + puts("lseek() failed!"); + perror(argv[0]); + puts("FAIL"); + return(EXIT_FAILURE); + } + + ret = write(fd, "b", 1); + if (ret < 0) + { + puts("write() failed!"); + perror(argv[0]); + puts("FAIL"); + return(EXIT_FAILURE); + } + + o = lseek(fd, 2 * 1024 * 1024, SEEK_SET); + if (ret < 0) + { + puts("lseek() failed!"); + perror(argv[0]); + puts("FAIL"); + return(EXIT_FAILURE); + } + + ret = write(fd, "c", 1); + if (ret < 0) + { + puts("write() failed!"); + perror(argv[0]); + puts("FAIL"); + return(EXIT_FAILURE); + } + + ret = close(fd); + if (ret < 0) + { + puts("close() failed!"); + perror(argv[0]); + puts("FAIL"); + return(EXIT_FAILURE); + } + + puts("PASS"); + return(EXIT_SUCCESS); + } *** /dev/null Fri Mar 7 18:37:25 2003 --- tests/libc/posix/unistd/t-pwrite.c Fri Mar 7 18:35:18 2003 *************** *** 0 **** --- 1,148 ---- + #include + #include + #include + #include + #include + #include + #include + #include + #include + + #define TEST_FILE "t-pwrite.tst" + #define EXTEND_LENGTH (1024 * 1024) + + int + main (int argc, char *argv[]) + { + int fd; + int ret; + const char orig_buf[] = "abc"; + const size_t orig_bufsize = strlen(orig_buf); + const char repl_char[] = "d"; /* Replacement character */ + const size_t repl_pos = 1; /* Replacement position */ + char after_buf[] = "XXX"; + char buf[128]; + const size_t bufsize = sizeof(buf); + const int fd_stdout = fileno(stdout); + const int fd_stderr = fileno(stderr); + const char msg_stdout[] = "Just a test (stdout)\n"; + const char msg_stderr[] = "Just a test (stderr)\n"; + + /* Test overwriting a particular position. */ + strcpy(after_buf, orig_buf); + after_buf[repl_pos] = repl_char[0]; + + fd = open(TEST_FILE, O_RDWR|O_TRUNC|O_CREAT|O_BINARY, S_IRUSR|S_IWUSR); + if (fd < 0) + { + puts("Unable to create test file '" TEST_FILE "'!"); + perror(argv[0]); + puts("FAIL"); + return(EXIT_FAILURE); + } + + ret = write(fd, orig_buf, orig_bufsize); + if (ret < 0) + { + puts("write() failed!"); + perror(argv[0]); + puts("FAIL"); + return(EXIT_FAILURE); + } + + ret = pwrite(fd, repl_char, 1, repl_pos); + if (ret < 0) + { + puts("pwrite() to overwrite failed!"); + perror(argv[0]); + puts("FAIL"); + return(EXIT_FAILURE); + } + + /* Check that the file now contains after_buf. */ + ret = lseek(fd, 0, SEEK_SET); + if (ret < 0) + { + puts("lseek() failed!"); + perror(argv[0]); + puts("FAIL"); + return(EXIT_FAILURE); + } + + ret = read(fd, buf, bufsize); + if (ret < 0) + { + puts("read() failed!"); + perror(argv[0]); + puts("FAIL"); + return(EXIT_FAILURE); + } + + buf[sizeof(buf) - 1] = '\0'; + + if (strncmp(after_buf, buf, strlen(after_buf)) != 0) + { + puts("Data comparison failed!"); + puts("FAIL"); + return(EXIT_FAILURE); + } + + /* Try extending the file's size. */ + ret = pwrite(fd, orig_buf, orig_bufsize, EXTEND_LENGTH); + if (ret < 0) + { + puts("pwrite() to extend failed!"); + perror(argv[0]); + puts("FAIL"); + return(EXIT_FAILURE); + } + + /* Check the file size. */ + ret = filelength(fd); + if (ret < 0) + { + puts("filelength() failed!"); + perror(argv[0]); + puts("FAIL"); + return(EXIT_FAILURE); + } + + if (ret != (EXTEND_LENGTH + orig_bufsize)) + { + puts("Failed to extend file to correct length!"); + perror(argv[0]); + puts("FAIL"); + return(EXIT_FAILURE); + } + + ret = close(fd); + if (ret < 0) + { + puts("close() failed!"); + perror(argv[0]); + puts("FAIL"); + return(EXIT_FAILURE); + } + + /* Try writing to stdout, stderr. */ + ret = pwrite(fd_stdout, msg_stdout, strlen(msg_stdout), 1024); + if ((ret < 0) && !((errno == ESPIPE) && !isatty(fd_stdout))) + { + puts("pwrite() to stdout failed!"); + perror(argv[0]); + puts("FAIL"); + return(EXIT_FAILURE); + } + + ret = pwrite(fd_stderr, msg_stderr, strlen(msg_stderr), 1024); + if ((ret < 0) && !((errno == ESPIPE) && !isatty(fd_stderr))) + { + puts("pwrite() to stderr failed!"); + perror(argv[0]); + puts("FAIL"); + return(EXIT_FAILURE); + } + + puts("PASS"); + return(EXIT_SUCCESS); + }