123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430 |
- /* Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License, version 2.0,
- as published by the Free Software Foundation.
- This program is also distributed with certain software (including
- but not limited to OpenSSL) that is licensed under separate terms,
- as designated in a particular file or component or in included license
- documentation. The authors of MySQL hereby grant you an additional
- permission to link the program and your derivative works with the
- separately licensed software that they have included with MySQL.
- Without limiting anything contained in the foregoing, this file,
- which is part of C Driver for MySQL (Connector/C), is also subject to the
- Universal FOSS Exception, version 1.0, a copy of which can be found at
- http://oss.oracle.com/licenses/universal-foss-exception.
- 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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
- /**
- @file mysys/mf_iocache2.cc
- More functions to be used with IO_CACHE files
- */
- #include <stdarg.h>
- #include <stdio.h>
- #include <string.h>
- #include <sys/types.h>
- #include "m_ctype.h"
- #include "m_string.h"
- #include "my_compiler.h"
- #include "my_dbug.h"
- #include "my_inttypes.h"
- #include "my_io.h"
- #include "my_sys.h"
- #include "mysql/psi/mysql_file.h"
- #include "mysql/psi/mysql_mutex.h"
- #include "template_utils.h"
- /*
- Copy contents of an IO_CACHE to a file.
- SYNOPSIS
- my_b_copy_to_file()
- cache IO_CACHE to copy from
- file File to copy to
- DESCRIPTION
- Copy the contents of the cache to the file. The cache will be
- re-inited to a read cache and will read from the beginning of the
- cache.
- If a failure to write fully occurs, the cache is only copied
- partially.
- TODO
- Make this function solid by handling partial reads from the cache
- in a correct manner: it should be atomic.
- RETURN VALUE
- 0 All OK
- 1 An error occurred
- */
- int my_b_copy_to_file(IO_CACHE *cache, FILE *file) {
- size_t bytes_in_cache;
- DBUG_ENTER("my_b_copy_to_file");
- /* Reinit the cache to read from the beginning of the cache */
- if (reinit_io_cache(cache, READ_CACHE, 0L, false, false)) DBUG_RETURN(1);
- bytes_in_cache = my_b_bytes_in_cache(cache);
- do {
- if (my_fwrite(file, cache->read_pos, bytes_in_cache,
- MYF(MY_WME | MY_NABP)) == (size_t)-1)
- DBUG_RETURN(1);
- cache->read_pos = cache->read_end;
- } while ((bytes_in_cache = my_b_fill(cache)));
- if (cache->error == -1) DBUG_RETURN(1);
- DBUG_RETURN(0);
- }
- /*
- Make next read happen at the given position
- For write cache, make next write happen at the given position
- */
- void my_b_seek(IO_CACHE *info, my_off_t pos) {
- my_off_t offset;
- DBUG_ENTER("my_b_seek");
- DBUG_PRINT("enter", ("pos: %lu", (ulong)pos));
- /*
- TODO:
- Verify that it is OK to do seek in the non-append
- area in SEQ_READ_APPEND cache
- a) see if this always works
- b) see if there is a better way to make it work
- */
- if (info->type == SEQ_READ_APPEND) (void)flush_io_cache(info);
- offset = (pos - info->pos_in_file);
- if (info->type == READ_CACHE || info->type == SEQ_READ_APPEND) {
- /* TODO: explain why this works if pos < info->pos_in_file */
- if ((ulonglong)offset < (ulonglong)(info->read_end - info->buffer)) {
- /* The read is in the current buffer; Reuse it */
- info->read_pos = info->buffer + offset;
- DBUG_VOID_RETURN;
- } else {
- /* Force a new read on next my_b_read */
- info->read_pos = info->read_end = info->buffer;
- }
- } else if (info->type == WRITE_CACHE) {
- /* If write is in current buffer, reuse it */
- if ((ulonglong)offset <=
- (ulonglong)(info->write_end - info->write_buffer)) {
- info->write_pos = info->write_buffer + offset;
- DBUG_VOID_RETURN;
- }
- (void)flush_io_cache(info);
- /* Correct buffer end so that we write in increments of IO_SIZE */
- info->write_end =
- (info->write_buffer + info->buffer_length - (pos & (IO_SIZE - 1)));
- }
- info->pos_in_file = pos;
- info->seek_not_done = 1;
- DBUG_VOID_RETURN;
- }
- /*
- Fill buffer of the cache.
- NOTES
- This assumes that you have already used all characters in the CACHE,
- independent of the read_pos value!
- RETURN
- 0 On error or EOF (info->error = -1 on error)
- # Number of characters
- */
- size_t my_b_fill(IO_CACHE *info) {
- my_off_t pos_in_file =
- (info->pos_in_file + (size_t)(info->read_end - info->buffer));
- size_t diff_length, length, max_length;
- if (info->seek_not_done) { /* File touched, do seek */
- if (mysql_encryption_file_seek(info, pos_in_file, MY_SEEK_SET, MYF(0)) ==
- MY_FILEPOS_ERROR) {
- info->error = 0;
- return 0;
- }
- info->seek_not_done = 0;
- }
- diff_length = (size_t)(pos_in_file & (IO_SIZE - 1));
- max_length = (info->read_length - diff_length);
- if (max_length >= (info->end_of_file - pos_in_file))
- max_length = (size_t)(info->end_of_file - pos_in_file);
- if (!max_length) {
- info->error = 0;
- return 0; /* EOF */
- }
- DBUG_EXECUTE_IF("simulate_my_b_fill_error",
- { DBUG_SET("+d,simulate_file_read_error"); });
- if ((length = mysql_encryption_file_read(info, info->buffer, max_length,
- info->myflags)) == (size_t)-1) {
- info->error = -1;
- return 0;
- }
- info->read_pos = info->buffer;
- info->read_end = info->buffer + length;
- info->pos_in_file = pos_in_file;
- return length;
- }
- /*
- Read a string ended by '\n' into a buffer of 'max_length' size.
- Returns number of characters read, 0 on error.
- last byte is set to '\0'
- If buffer is full then to[max_length-1] will be set to \0.
- */
- size_t my_b_gets(IO_CACHE *info, char *to, size_t max_length) {
- char *start = to;
- size_t length;
- max_length--; /* Save place for end \0 */
- /* Calculate number of characters in buffer */
- if (!(length = my_b_bytes_in_cache(info)) && !(length = my_b_fill(info)))
- return 0;
- for (;;) {
- uchar *pos, *end;
- if (length > max_length) length = max_length;
- for (pos = info->read_pos, end = pos + length; pos < end;) {
- if ((*to++ = *pos++) == '\n') {
- info->read_pos = pos;
- *to = '\0';
- return (size_t)(to - start);
- }
- }
- if (!(max_length -= length)) {
- /* Found enough charcters; Return found string */
- info->read_pos = pos;
- *to = '\0';
- return (size_t)(to - start);
- }
- if (!(length = my_b_fill(info))) return 0;
- }
- }
- my_off_t my_b_filelength(IO_CACHE *info) {
- if (info->type == WRITE_CACHE) return my_b_tell(info);
- info->seek_not_done = 1;
- return mysql_file_seek(info->file, 0L, MY_SEEK_END, MYF(0));
- }
- /**
- Simple printf version. Used for logging in MySQL.
- Supports '%c', '%s', '%b', '%d', '%u', '%ld', '%lu' and '%llu'.
- Returns number of written characters, or (size_t) -1 on error.
- @param info The IO_CACHE to write to
- @param fmt format string
- @param ... variable list of arguments
- @return number of bytes written, -1 if an error occurred
- */
- size_t my_b_printf(IO_CACHE *info, const char *fmt, ...) {
- size_t result;
- va_list args;
- va_start(args, fmt);
- result = my_b_vprintf(info, fmt, args);
- va_end(args);
- return result;
- }
- /**
- Implementation of my_b_printf.
- @param info The IO_CACHE to write to
- @param fmt format string
- @param args variable list of arguments
- @return number of bytes written, -1 if an error occurred
- */
- size_t my_b_vprintf(IO_CACHE *info, const char *fmt, va_list args) {
- size_t out_length = 0;
- uint minimum_width; /* as yet unimplemented */
- uint minimum_width_sign;
- uint precision; /* as yet unimplemented for anything but %b */
- bool is_zero_padded;
- /*
- Store the location of the beginning of a format directive, for the
- case where we learn we shouldn't have been parsing a format string
- at all, and we don't want to lose the flag/precision/width/size
- information.
- */
- const char *backtrack;
- for (; *fmt != '\0'; fmt++) {
- /* Copy everything until '%' or end of string */
- const char *start = fmt;
- size_t length;
- for (; (*fmt != '\0') && (*fmt != '%'); fmt++)
- ;
- length = (size_t)(fmt - start);
- out_length += length;
- if (my_b_write(info, (const uchar *)start, length)) goto err;
- if (*fmt == '\0') /* End of format */
- return out_length;
- /*
- By this point, *fmt must be a percent; Keep track of this location and
- skip over the percent character.
- */
- DBUG_ASSERT(*fmt == '%');
- backtrack = fmt;
- fmt++;
- is_zero_padded = false;
- minimum_width_sign = 1;
- minimum_width = 0;
- precision = 0;
- /* Skip if max size is used (to be compatible with printf) */
- process_flags:
- switch (*fmt) {
- case '-':
- minimum_width_sign = -1;
- fmt++;
- goto process_flags;
- case '0':
- is_zero_padded = true;
- fmt++;
- goto process_flags;
- case '#':
- /** @todo Implement "#" conversion flag. */ fmt++;
- goto process_flags;
- case ' ':
- /** @todo Implement " " conversion flag. */ fmt++;
- goto process_flags;
- case '+':
- /** @todo Implement "+" conversion flag. */ fmt++;
- goto process_flags;
- }
- if (*fmt == '*') {
- minimum_width = (int)va_arg(args, int);
- fmt++;
- } else {
- while (my_isdigit(&my_charset_latin1, *fmt)) {
- minimum_width = (minimum_width * 10) + (*fmt - '0');
- fmt++;
- }
- }
- minimum_width *= minimum_width_sign;
- if (*fmt == '.') {
- fmt++;
- if (*fmt == '*') {
- precision = (int)va_arg(args, int);
- fmt++;
- } else {
- while (my_isdigit(&my_charset_latin1, *fmt)) {
- precision = (precision * 10) + (*fmt - '0');
- fmt++;
- }
- }
- }
- if (*fmt == 's') /* String parameter */
- {
- char *par = va_arg(args, char *);
- size_t length2 = strlen(par);
- /* TODO: implement precision */
- out_length += length2;
- if (my_b_write(info, (uchar *)par, length2)) goto err;
- } else if (*fmt == 'c') /* char type parameter */
- {
- char par[2];
- par[0] = va_arg(args, int);
- out_length++;
- if (my_b_write(info, (uchar *)par, 1)) goto err;
- } else if (*fmt ==
- 'b') /* Sized buffer parameter, only precision makes sense */
- {
- char *par = va_arg(args, char *);
- out_length += precision;
- if (my_b_write(info, (uchar *)par, precision)) goto err;
- } else if (*fmt == 'd' || *fmt == 'u') /* Integer parameter */
- {
- int iarg;
- size_t length2;
- char buff[32];
- iarg = va_arg(args, int);
- if (*fmt == 'd')
- length2 = (size_t)(int10_to_str((long)iarg, buff, -10) - buff);
- else
- length2 = (uint)(int10_to_str((long)(uint)iarg, buff, 10) - buff);
- /* minimum width padding */
- if (minimum_width > length2) {
- char *buffz;
- buffz = static_cast<char *>(my_alloca(minimum_width - length2));
- if (is_zero_padded)
- memset(buffz, '0', minimum_width - length2);
- else
- memset(buffz, ' ', minimum_width - length2);
- if (my_b_write(info, pointer_cast<uchar *>(buffz),
- minimum_width - length2)) {
- goto err;
- }
- }
- out_length += length2;
- if (my_b_write(info, (uchar *)buff, length2)) goto err;
- } else if ((*fmt == 'l' && fmt[1] == 'd') || fmt[1] == 'u')
- /* long parameter */
- {
- long iarg;
- size_t length2;
- char buff[32];
- iarg = va_arg(args, long);
- if (*++fmt == 'd')
- length2 = (size_t)(int10_to_str(iarg, buff, -10) - buff);
- else
- length2 = (size_t)(int10_to_str(iarg, buff, 10) - buff);
- out_length += length2;
- if (my_b_write(info, (uchar *)buff, length2)) goto err;
- } else if (fmt[0] == 'l' && fmt[1] == 'l' && fmt[2] == 'u') {
- ulonglong iarg;
- size_t length2;
- char buff[32];
- iarg = va_arg(args, ulonglong);
- length2 = (size_t)(longlong10_to_str(iarg, buff, 10) - buff);
- out_length += length2;
- fmt += 2;
- if (my_b_write(info, (uchar *)buff, length2)) goto err;
- } else {
- /* %% or unknown code */
- if (my_b_write(info, pointer_cast<const uchar *>(backtrack),
- fmt - backtrack))
- goto err;
- out_length += fmt - backtrack;
- }
- }
- return out_length;
- err:
- return (size_t)-1;
- }
|