glibc-2.7/libio/iofwrite.c を見てみる。
--- iofwrite.c --- _IO_size_t _IO_fwrite(const void *buf, _IO_size_t size, _IO_size_t count, _IO_FILE *fp) { _IO_size_t request = size * count; _IO_size_t written = 0; CHECK_FILE (fp, 0); if (request == 0) return 0; _IO_acquire_lock (fp); if (_IO_vtable_offset (fp) != 0 || _IO_fwide (fp, -1) == -1) written = _IO_sputn(fp, (const char *) buf, request); _IO_release_lock (fp); if (written == request) return count; else return written / size; }
これが fwrite になるからくりは良く判らないが、すぐ下に次のように書かれてある。
INTDEF(_IO_fwrite) #ifdef weak_alias # includeweak_alias (_IO_fwrite, fwrite) libc_hidden_weak (fwrite) # ifndef _IO_MTSAFE_IO weak_alias (_IO_fwrite, fwrite_unlocked) libc_hidden_weak (fwrite_unlocked) # endif #endif
_IO_sputn を見てみる。 glibc-2.7\libio\libioP.h
#ifdef _G_USING_THUNKS # define JUMP2(FUNC, THIS, X1, X2) (_IO_JUMPS_FUNC(THIS)->FUNC) (THIS, X1, X2) #else /* These macros will change when we re-implement vtables to use "thunks"! */ # define JUMP2(FUNC, THIS, X1, X2) _IO_JUMPS_FUNC(THIS)->FUNC.pfn (THIS, X1, X2) #endif /* The 'xsputn' hook writes upto N characters from buffer DATA. Returns the number of character actually written. It matches the streambuf::xsputn virtual function. */ typedef _IO_size_t (*_IO_xsputn_t) (_IO_FILE *FP, const void *DATA, _IO_size_t N); #define _IO_XSPUTN(FP, DATA, N) JUMP2 (__xsputn, FP, DATA, N) #define _IO_WXSPUTN(FP, DATA, N) WJUMP2 (__xsputn, FP, DATA, N) (略) #define _IO_sputn(__fp, __s, __n) _IO_XSPUTN (__fp, __s, __n)
__xsputn 関数を呼んでいるもよう...
関数テーブルらしく、どれが該当するのかはっきりしないが、その1つとして
glibc-2.7\libio\wfileops.c を見てみる。
--- wfileops.c --- _IO_size_t _IO_wfile_xsputn(_IO_FILE *f, const void *data, _IO_size_t n) { register const wchar_t *s = (const wchar_t *) data; _IO_size_t to_do = n; int must_flush = 0; _IO_size_t count; if (n <= 0) return 0; /* This is an optimized implementation. If the amount to be written straddles a block boundary (or the filebuf is unbuffered), use sys_write directly. */ /* First figure out how much space is available in the buffer. */ count = f->_wide_data->_IO_write_end - f->_wide_data->_IO_write_ptr; if ((f->_flags & _IO_LINE_BUF) && (f->_flags & _IO_CURRENTLY_PUTTING)) { count = f->_wide_data->_IO_buf_end - f->_wide_data->_IO_write_ptr; if (count >= n) { register const wchar_t *p; for (p = s + n; p > s; ) { if (*--p == L'\n') { count = p - s + 1; must_flush = 1; break; } } } } /* Then fill the buffer. */ if (count > 0) { if (count > to_do) count = to_do; if (count > 20) { #ifdef _LIBC f->_wide_data->_IO_write_ptr = __wmempcpy(f->_wide_data->_IO_write_ptr, s, count); #else wmemcpy(f->_wide_data->_IO_write_ptr, s, count); f->_wide_data->_IO_write_ptr += count; #endif s += count; } else { register wchar_t *p = f->_wide_data->_IO_write_ptr; register int i = (int) count; while (--i >= 0) *p++ = *s++; f->_wide_data->_IO_write_ptr = p; } to_do -= count; } if (to_do > 0) to_do -= INTUSE(_IO_wdefault_xsputn) (f, s, to_do); if (must_flush && f->_wide_data->_IO_write_ptr != f->_wide_data->_IO_write_base) INTUSE(_IO_wdo_write)(f, f->_wide_data->_IO_write_base, f->_wide_data->_IO_write_ptr - f->_wide_data->_IO_write_base); return n - to_do; } INTDEF(_IO_wfile_xsputn)
肝心なのは最後の部分の _IO_wdo_write 関数と思われる。
--- wfileops.c --- /* Convert TO_DO wide character from DATA to FP. Then mark FP as having empty buffers. */ int _IO_wdo_write(_IO_FILE *fp, const wchar_t *data, _IO_size_t to_do) { struct _IO_codecvt *cc = fp->_codecvt; if (to_do > 0) { if (fp->_IO_write_end == fp->_IO_write_ptr && fp->_IO_write_end != fp->_IO_write_base) { if (_IO_new_do_write(fp, fp->_IO_write_base, fp->_IO_write_ptr - fp->_IO_write_base) == EOF) return WEOF; } do { enum __codecvt_result result; const wchar_t *new_data; /* Now convert from the internal format into the external buffer. */ result = (*cc->__codecvt_do_out)( cc, &fp->_wide_data->_IO_state, data, data + to_do, &new_data, fp->_IO_write_ptr, fp->_IO_buf_end, &fp->_IO_write_ptr); /* Write out what we produced so far. */ if (_IO_new_do_write(fp, fp->_IO_write_base, fp->_IO_write_ptr - fp->_IO_write_base) == EOF) /* Something went wrong. */ return WEOF; to_do -= new_data - data; /* Next see whether we had problems during the conversion. If yes, we cannot go on. */ if (result != __codecvt_ok && (result != __codecvt_partial || new_data - data == 0)) break; data = new_data; } while (to_do > 0); } _IO_wsetg(fp, fp->_wide_data->_IO_buf_base, fp->_wide_data->_IO_buf_base, fp->_wide_data->_IO_buf_base); fp->_wide_data->_IO_write_base = fp->_wide_data->_IO_write_ptr = fp->_wide_data->_IO_buf_base; fp->_wide_data->_IO_write_end = ((fp->_flags & (_IO_LINE_BUF+_IO_UNBUFFERED)) ? fp->_wide_data->_IO_buf_base : fp->_wide_data->_IO_buf_end); return to_do == 0 ? 0 : WEOF; } INTDEF(_IO_wdo_write)
ふぅ、_IO_new_do_write が肝心か...
--- glibc-2.7/libio/fileops.c --- /* Write TO_DO bytes from DATA to FP. Then mark FP as having empty buffers. */ int _IO_new_do_write(_IO_FILE *fp, const char *data, _IO_size_t to_do) { return (to_do == 0 || (_IO_size_t) new_do_write (fp, data, to_do) == to_do) ? 0 : EOF; } INTDEF2(_IO_new_do_write, _IO_do_write) static _IO_size_t new_do_write(_IO_FILE *fp, const char *data, _IO_size_t to_do) { _IO_size_t count; if (fp->_flags & _IO_IS_APPENDING) /* On a system without a proper O_APPEND implementation, you would need to sys_seek(0, SEEK_END) here, but is is not needed nor desirable for Unix- or Posix-like systems. Instead, just indicate that offset (before and after) is unpredictable. */ fp->_offset = _IO_pos_BAD; else if (fp->_IO_read_end != fp->_IO_write_base) { _IO_off64_t new_pos = _IO_SYSSEEK(fp, fp->_IO_write_base - fp->_IO_read_end, 1); if (new_pos == _IO_pos_BAD) return 0; fp->_offset = new_pos; } count = _IO_SYSWRITE(fp, data, to_do); if (fp->_cur_column && count) fp->_cur_column = INTUSE(_IO_adjust_column) (fp->_cur_column - 1, data, count) + 1; _IO_setg (fp, fp->_IO_buf_base, fp->_IO_buf_base, fp->_IO_buf_base); fp->_IO_write_base = fp->_IO_write_ptr = fp->_IO_buf_base; fp->_IO_write_end = (fp->_mode <= 0 && (fp->_flags & (_IO_LINE_BUF+_IO_UNBUFFERED)) ? fp->_IO_buf_base : fp->_IO_buf_end); return count; }