/* common parts of buffered methods. these are general purpose ones, ie these do the standardized parts of management of buffered streamoids. they call the device-specific methods to do the actual data transfers. */ #include "io-defs.h" #include #include /* put streamoid's current output buffer back where it belongs in the file. only used with bidirectional streamoids. */ local void __rewrite_output_buffer(self) struct streamoid * self; { int result; /* make sure handle's positioned to where this buffer lives */ if (self->flags & STR_BUF_READ_P) method_call_2(self, METHOD_SEEK_I, self->buffer_pos, L_SET); /* and put this buffer back in the file */ result = method_call_0(self, METHOD_PUTBUF); if (result < 0) errno = self->last_error = result; self->flags &= ~STR_BUF_DIRTY_P; } /* advance the position-in-file of the current (presumably exhausted) buffer by the current buffer index. */ local void __advance_input_buffer(self) struct streamoid * self; { long buffer_index = self->buffer_index; if ((self->flags & STR_OUTPUT_P) && /* bidirectional stream? */ (self->flags & STR_BUF_DIRTY_P)) __rewrite_output_buffer(self); self->flags &= ~STR_BUF_READ_P; /* no longer have a read buf */ self->buffer_pos += buffer_index; self->buffer_index = self->buffer_nbytes = 0; } /* general purpose get-more-input function, for buffered streamoids */ static void __setup_next_input_buffer(self) struct streamoid * self; { int nbytes_read; if (self->flags & STR_AT_EOF_P) return; /* if at eof, just give up */ if (self->buffer_nbytes >= 0) __advance_input_buffer(self); nbytes_read = method_call_0(self, METHOD_GETBUF); self->flags |= STR_BUF_READ_P; /* say this buf was read from file */ if (nbytes_read == 0) /* eof? */ self->flags |= STR_AT_EOF_P; if (nbytes_read < 0) /* error? */ errno = self->last_error = nbytes_read; self->buffer_nbytes = nbytes_read; self->buffer_index = 0; } /* get one byte/char. helper used by bin/text guys */ inline static int __tyi_internal(self) struct streamoid * self; { if (self->buffer_index >= self->buffer_nbytes) { __setup_next_input_buffer(self); if (self->flags & STR_AT_EOF_P) return(EOF_VALUE); /* too bad this is so gross... */ } self->file_position++; return(self->buffer[self->buffer_index++]); } /* get a byte, binary mode. */ DEFMETHOD m_tyi_binary(self) struct streamoid * self; { if (self->flags & STR_UNTYIED_P) { self->flags &= ~STR_UNTYIED_P; self->file_position++; return(self->untyied_char); } return(__tyi_internal(self)); } /* get a character, text mode */ DEFMETHOD m_tyi_text(self) struct streamoid * self; { int ch; if (self->flags & STR_UNTYIED_P) { self->flags &= ~STR_UNTYIED_P; self->file_position++; return(self->untyied_char); } ch = __tyi_internal(self); if (ch == '\r') { int next_ch = __tyi_internal(self); if (next_ch == '\n') ch = next_ch; else m_untyi_byte(self, next_ch); } return(ch); } /* get a bufferful. common to both binary and text, as it currently just tyi's until eof or buf is full. later, redo the binary case. returns nbytes read into buf. */ DEFMETHOD m_string_in(self, buf, nbytes_buf) struct streamoid * self; char * buf; int nbytes_buf; { int nbytes_read = 0; int ch; while ((nbytes_read < nbytes_buf) && ((ch = method_call_0(self, METHOD_TYI)) != EOF_VALUE)) buf[nbytes_read++] = ch; return(nbytes_read); } /* get a line. common to both binary and text, as it currently just tyi's until eol. returns nbytes read into buf */ DEFMETHOD m_line_in(self, buf, nbytes_buf) struct streamoid * self; char * buf; int nbytes_buf; { int nbytes_read = 0; int ch; while ((nbytes_read < nbytes_buf) && ((ch = method_call_0(self, METHOD_TYI)) != EOF_VALUE)) { buf[nbytes_read++] = ch; if (ch == '\n') break; } buf[nbytes_read] = '\0'; return(nbytes_read); } /* output side stuff */ /* reset pointers, indices, and such, to set up for another buffer */ static void __advance_output_buffer(self) struct streamoid * self; { self->buffer_pos += self->buffer_nbytes; self->buffer_index = self->buffer_nbytes = 0; } static void __setup_new_output_buffer(self) struct streamoid * self; { int result; if (self->buffer_nbytes > 0) { if ((self->flags & STR_INPUT_P) && /* bidirectional stream? */ (self->flags & STR_BUF_DIRTY_P)) __rewrite_output_buffer(self); else { /* not bidirectional, do it the easy way */ result = method_call_0(self, METHOD_PUTBUF); if (result < 0) /* error? */ errno = self->last_error = result; } } __advance_output_buffer(self); } void __force_output(self) struct streamoid * self; { if (self->flags & STR_OUTPUT_P) __setup_new_output_buffer(self); } /* guts of tyo */ inline static __tyo_internal(self, ch) struct streamoid * self; { if (self->buffer_index >= self->buffer_size) __setup_new_output_buffer(self); self->buffer[self->buffer_index++] = ch; if (self->buffer_nbytes < self->buffer_index) self->buffer_nbytes = self->buffer_index; self->file_position++; } DEFMETHOD m_tyo_binary(self, byte) struct streamoid * self; int byte; { self->flags |= STR_BUF_DIRTY_P; __tyo_internal(self, byte); } DEFMETHOD m_tyo_text(self, ch) struct streamoid * self; int ch; { self->flags |= STR_BUF_DIRTY_P; if (ch == '\n') __tyo_internal(self, '\r'); __tyo_internal(self, ch); } /* write a bufferfull */ DEFMETHOD m_string_out(self, buf, nbytes_buf) struct streamoid * self; char * buf; int nbytes_buf; { int i = 0; while (i < nbytes_buf) method_call_1(self, METHOD_TYO, buf[i++]); } /* write a line. */ DEFMETHOD m_line_out(self, buf) struct streamoid * self; char * buf; { int ch; /* int eol_p; */ while (ch = *buf++) { method_call_1(self, METHOD_TYO, ch); /* eol_p = (ch == '\n'); */ } /* if (!eol_p) method_call_1(self, METHOD_TYO, '\n'); */ } /* General purpose seek method for buffered streams. This should work for bidirectional streams, as well as unidirectional ones. */ DEFMETHOD m_seek(self, pos, how) struct streamoid * self; long pos; int how; { long result, real_pos, desired_pos; self->flags &= ~STR_AT_EOF_P; /* always clear this */ /* first see if we have the buffer that this pos corresponds to */ switch (how) { case L_SET: { desired_pos = pos; break; }; case L_INCR: { desired_pos = self->file_position + pos; how = L_SET; break; } case L_XTND: { desired_pos = -1; break; }; /* give up */ default: { errno = self->last_error = EBADARG ; return(-1); }; }; if ((desired_pos >= 0) && (desired_pos >= self->buffer_pos) && (desired_pos < (self->buffer_pos + self->buffer_nbytes))) { /* we win. just set the buffer pointer */ self->buffer_index = desired_pos - self->buffer_pos; return(self->file_position = desired_pos); } /* ok, we don't have a buffer, or it didn't contain the position we want. flush any current buffers, and position the underlying handle. */ if ((self->flags & STR_BUF_DIRTY_P)) __rewrite_output_buffer(self); /* this part isn't strictly necessary, but aids efficiency in cases where the caller is scanning backwards thru a file. if desired_pos is less than current pos, seek back to desired_pos - buffer_size/2, then read a buffer and adjust indices. If anything loses, we give up and skip the optimization, fall thru to the standard way. */ if ((desired_pos >= 0) && (desired_pos < self->buffer_pos)) { int real_seek_pos = desired_pos - (self->buffer_size / 2); if (real_seek_pos < 0) real_seek_pos = 0; result = real_pos = method_call_2(self, METHOD_SEEK_I, real_seek_pos, 0); if (result >= 0) { self->buffer_pos = real_pos; /* say where this buffer lives */ result = method_call_0(self, METHOD_GETBUF); if (result >= 0) { self->buffer_nbytes = result; self->buffer_index = desired_pos - real_seek_pos; return(self->file_position = desired_pos); } } } else /* seeking forward */ result = real_pos = method_call_2(self, METHOD_SEEK_I, pos, how); if (result < 0) /* error? */ { errno = self->last_error = result; /* make sure we know what the current pos is */ real_pos = method_call_2(self, METHOD_SEEK_I, 0, L_INCR); } else real_pos = result; self->buffer_pos = self->file_position = real_pos; self->buffer_nbytes = self->buffer_index = 0; return(real_pos); }