/** BEGIN COPYRIGHT BLOCK * This Program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; version 2 of the License. * * This Program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this Program; if not, write to the Free Software Foundation, Inc., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA. * * In addition, as a special exception, Red Hat, Inc. gives You the additional * right to link the code of this Program with code not covered under the GNU * General Public License ("Non-GPL Code") and to distribute linked combinations * including the two, subject to the limitations in this paragraph. Non-GPL Code * permitted under this exception must only link to the code of this Program * through those well defined interfaces identified in the file named EXCEPTION * found in the source code files (the "Approved Interfaces"). The files of * Non-GPL Code may instantiate templates or use macros or inline functions from * the Approved Interfaces without causing the resulting work to be covered by * the GNU General Public License. Only Red Hat, Inc. may make changes or * additions to the list of Approved Interfaces. You must obey the GNU General * Public License in all respects for all of the Program code and other code used * in conjunction with the Program except the Non-GPL Code covered by this * exception. If you modify this file, you may extend this exception to your * version of the file, but you are not obligated to do so. If you do not wish to * provide this exception without modification, you must delete this exception * statement from your version and license this file solely under the GPL without * exception. * * * Copyright (C) 2001 Sun Microsystems, Inc. Used by permission. * Copyright (C) 2005 Red Hat, Inc. * All rights reserved. * END COPYRIGHT BLOCK **/ #ifdef HAVE_CONFIG_H # include #endif /* fileio.c - layer to adjust EOL to use DOS format via PR_Read/Write on NT */ #include #include #include #include #include #ifndef _WIN32 #include #include #include #endif #include "slap.h" #include "pw.h" #include #if defined( XP_WIN32 ) #include /* PR_CallOnce */ #include /* memmove, memcpy */ #define g_EOF (-1) static PRInt32 PR_CALLBACK readText(PRFileDesc *f, void *buf, PRInt32 amount) { auto PRInt32 size = *(signed char*)&(f->secret); auto char* readAhead = ((char*)&(f->secret)) + 1; if ( size == g_EOF ) { f->secret = NULL; return 0; } if ( size > amount ) { return 0; } if ( size > 0 ) { memcpy( buf, readAhead, size ); } f->secret = NULL; while (1) { auto PRInt32 len = amount - size; auto char* head; auto PRInt32 rval; if (len > 0) { head = (char*)buf + size; } else if (size > 0 && '\r' == ((char*)buf)[size-1]) { head = readAhead; len = 1; } else { break; } rval = PR_Read( f->lower, head, len ); if ( rval < 0 ) { /* error */ return rval; } if ( rval == 0 ) { /* EOF */ if ( size ) { *(signed char*)&(f->secret) = g_EOF; } return size; } if (head == readAhead) { if ( '\n' == *readAhead ) { ((char*)buf)[size-1] = '\n'; } else { *(signed char*)&(f->secret) = rval; } break; } else { auto char* tail = head + rval; auto char* dest = NULL; auto char* p; for ( p = head; p < tail; p++ ) { if ( *p == '\n' && p > (char*)buf && *(p - 1) == '\r' ) { if ( dest == NULL ) { /* first CRLF */ dest = p - 1; } else { auto size_t len = (p - 1) - head; memmove( dest, head, len ); dest += len; } head = p; /* '\n' */ --rval; /* ignore '\r' */ } } if ( dest != NULL ) { auto size_t len = tail - head; memmove( dest, head, len ); } size += rval; } } return size; } static PRInt32 PR_CALLBACK seekText(PRFileDesc *f, PRInt32 offset, PRSeekWhence how) { f->secret = NULL; return PR_Seek(f->lower, offset, how); } static PRInt64 PR_CALLBACK seek64Text(PRFileDesc *f, PRInt64 offset, PRSeekWhence how) { f->secret = NULL; return PR_Seek64(f->lower, offset, how); } static PRInt32 PR_CALLBACK writeText(PRFileDesc *f, const void *buf, PRInt32 amount) { /* note: buf might not be null-terminated */ auto PRInt32 size = 0; auto char* head = (char*)buf; auto char* tail = head + amount; auto char* p; for ( p = head; p <= tail; ++p ) { if ( p == tail || *p == '\n' ) { auto PRInt32 len = p - head; auto PRInt32 rval; if ( len > 0 ) { rval = PR_Write( f->lower, head, len ); if ( rval < 0 ) { return rval; } size += rval; if ( rval < len ) { break; } } if ( p == tail ) { break; } rval = PR_Write( f->lower, "\r", 1 ); if ( rval < 0 ) { return rval; } if ( rval < 1 ) { break; } head = p; } } return size; } static PRInt32 PR_CALLBACK writevText(PRFileDesc *fd, const PRIOVec *iov, PRInt32 size, PRIntervalTime timeout) { auto PRInt32 i; auto size_t total = 0; for (i = 0; i < size; ++i) { register PRInt32 rval = PR_Write(fd, iov[i].iov_base, iov[i].iov_len); if (rval < 0) return rval; total += rval; if (rval < iov[i].iov_len) break; } return total; } /* ONREPL - this is bad because it allows only one thread to use this functionality. Noriko said she would fix this before 5.0 ships. */ static const char* const g_LayerName = "MdsTextIO"; static PRDescIdentity g_LayerID; static PRIOMethods g_IoMethods; static PRStatus PR_CALLBACK closeLayer(PRFileDesc* stack) { auto PRFileDesc* layer = PR_PopIOLayer(stack, g_LayerID); if (!layer) return PR_FAILURE; if (layer->dtor) { layer->secret = NULL; layer->dtor(layer); } return PR_Close(stack); } static PRStatus PR_CALLBACK initialize(void) { g_LayerID = PR_GetUniqueIdentity(g_LayerName); if (PR_INVALID_IO_LAYER == g_LayerID) { return PR_FAILURE; } else { auto const PRIOMethods* defaults = PR_GetDefaultIOMethods(); if (!defaults) { return PR_FAILURE; } else { memcpy (&g_IoMethods, defaults, sizeof(g_IoMethods)); } } /* Customize methods: */ g_IoMethods.read = readText; g_IoMethods.seek = seekText; g_IoMethods.seek64 = seek64Text; g_IoMethods.write = writeText; g_IoMethods.writev = writevText; /* ??? Is this necessary? */ g_IoMethods.close = closeLayer; /* ??? Is this necessary? */ return PR_SUCCESS; } static PRCallOnceType g_callOnce = {0,0}; /* Push a layer that converts from "\n" to the local filesystem's * end-of-line sequence on output, and vice-versa on input. * The layer pops itself (if necessary) when the file is closed. * * This layer does not affect the behavior of PR_Seek or PR_Seek64; * their parameters still measure bytes in the lower-level file, * and consequently will not add up with the results of PR_Read * or PR_Write. For example, if you add up PR_Read return values, * and seek backward in the file that many bytes, the cursor will * *not* be restored to its original position (unless the data you * read didn't require conversion; that is, they didn't contain * any newlines, or you're running on Unix). * * Likewise, the results of PR_Read or PR_Write won't add up to * the 'size' field in the result of PRFileInfo or PRFileInfo64. */ static PRStatus pushTextIOLayer(PRFileDesc* stack) { auto PRStatus rv = PR_CallOnce(&g_callOnce, initialize); if (PR_SUCCESS == rv) { auto PRFileDesc* layer = PR_CreateIOLayerStub(g_LayerID, &g_IoMethods); layer->secret = NULL; rv = PR_PushIOLayer(stack, PR_TOP_IO_LAYER, layer); } return rv; } static PRFileDesc *popTextIOLayer(PRFileDesc* stack) { PRFileDesc *layer; layer = PR_PopIOLayer(stack, g_LayerID); if (layer && layer->dtor) { layer->secret = NULL; layer->dtor(layer); } return layer; } #endif /* XP_WIN32 */ PRInt32 slapi_read_buffer( PRFileDesc *fd, void *buf, PRInt32 amount ) { PRInt32 rval = 0; #if defined( XP_WIN32 ) PRStatus rv; rv = pushTextIOLayer( fd ); if ( PR_SUCCESS != rv ) { return -1; } #endif rval = PR_Read( fd, buf, amount ); #if defined( XP_WIN32 ) popTextIOLayer( fd ); #endif return rval; } /* * slapi_write_buffer -- same as PR_Write * except '\r' is added before '\n'. * Return value: written bytes not including '\r' characters. */ PRInt32 slapi_write_buffer( PRFileDesc *fd, void *buf, PRInt32 amount ) { PRInt32 rval = 0; #if defined( XP_WIN32 ) PRStatus rv; rv = pushTextIOLayer( fd ); if ( PR_SUCCESS != rv ) { return -1; } #endif rval = PR_Write( fd, buf, amount ); #if defined( XP_WIN32 ) popTextIOLayer( fd ); #endif return rval; } /* * This function renames a file to a new name. Unlike PR_Rename or NT rename, this * function can be used if the destfilename exists, and it will overwrite the dest * file name */ int slapi_destructive_rename(const char *srcfilename, const char *destfilename) { int rv = 0; #if defined( XP_WIN32 ) if (!MoveFileEx(srcfilename, destfilename, MOVEFILE_REPLACE_EXISTING)) { rv = GetLastError(); } #else if ( rename(srcfilename, destfilename) < 0 ) { rv = errno; } #endif return rv; } /* * This function copies the source into the dest */ int slapi_copy(const char *srcfilename, const char *destfilename) { int rv = 0; #if defined( XP_WIN32 ) if (!CopyFile(srcfilename, destfilename, FALSE)) { rv = GetLastError(); } #else unlink(destfilename); if ( link(srcfilename, destfilename) < 0 ) { rv = errno; } #endif return rv; }