Drizzled Public API Documentation

drizzletest.cc
00001 /* - mode: c; c-basic-offset: 2; indent-tabs-mode: nil; -*-
00002  *  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
00003  * 
00004  *  Copyright (C) 2010 Vijay Samuel
00005  *  Copyright (C) 2008 MySQL
00006  *
00007  *  This program is free software; you can redistribute it and/or modify
00008  *  it under the terms of the GNU General Public License as published by
00009  *  the Free Software Foundation; either version 2 of the License, or
00010  *  (at your option) any later version.
00011  *
00012  *  This program is distributed in the hope that it will be useful,
00013  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  *  GNU General Public License for more details.
00016  *
00017  *  You should have received a copy of the GNU General Public License
00018  *  along with this program; if not, write to the Free Software
00019  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00020  */
00021 
00022 /*
00023   drizzletest
00024 
00025   Tool used for executing a .test file
00026 
00027   See the "MySQL Test framework manual" for more information
00028   http://dev.mysql.com/doc/mysqltest/en/index.html
00029 
00030   Please keep the test framework tools identical in all versions!
00031 
00032   Written by:
00033   Sasha Pachev <sasha@mysql.com>
00034   Matt Wagner  <matt@mysql.com>
00035   Monty
00036   Jani
00037   Holyfoot
00038 */
00039 
00040 #define MTEST_VERSION "3.3"
00041 
00042 #include <config.h>
00043 #include <client/get_password.h>
00044 #include <libdrizzle-2.0/libdrizzle.hpp>
00045 
00046 #include <queue>
00047 #include <map>
00048 #include <string>
00049 #include <sstream>
00050 #include <fstream>
00051 #include <iostream>
00052 #include <vector>
00053 #include <algorithm>
00054 #ifdef HAVE_SYS_WAIT_H
00055 #include <sys/wait.h>
00056 #endif
00057 #include <cassert>
00058 #include <sys/stat.h>
00059 #include <sys/types.h>
00060 #include <fcntl.h>
00061 #include <boost/array.hpp>
00062 #include <boost/foreach.hpp>
00063 #include <boost/program_options.hpp>
00064 #include <boost/smart_ptr.hpp>
00065 
00066 #include PCRE_HEADER
00067 
00068 #include <stdarg.h>
00069 #include <boost/unordered_map.hpp>
00070 
00071 /* Added this for string translation. */
00072 #include <drizzled/gettext.h>
00073 
00074 #include <drizzled/definitions.h>
00075 #include <drizzled/internal/my_sys.h>
00076 #include <drizzled/type/time.h>
00077 #include <drizzled/charset.h>
00078 #include <drizzled/typelib.h>
00079 #include <drizzled/configmake.h>
00080 #include <drizzled/util/find_ptr.h>
00081 
00082 #define PTR_BYTE_DIFF(A,B) (ptrdiff_t) (reinterpret_cast<const unsigned char*>(A) - reinterpret_cast<const unsigned char*>(B))
00083 
00084 namespace po= boost::program_options;
00085 using namespace std;
00086 using namespace drizzled;
00087 
00088 unsigned char *get_var_key(const unsigned char* var, size_t *len, bool);
00089 
00090 int get_one_option(int optid, const struct option *, char *argument);
00091 
00092 #define MAX_VAR_NAME_LENGTH    256
00093 #define MAX_COLUMNS            256
00094 #define MAX_DELIMITER_LENGTH 16
00095 /* Flags controlling send and reap */
00096 #define QUERY_SEND_FLAG  1
00097 #define QUERY_REAP_FLAG  2
00098 
00099 typedef boost::unordered_map<std::string, uint32_t> ErrorCodes;
00100 ErrorCodes global_error_names;
00101 
00102 enum {
00103   OPT_PS_PROTOCOL, OPT_SP_PROTOCOL, OPT_CURSOR_PROTOCOL, OPT_VIEW_PROTOCOL,
00104   OPT_MAX_CONNECT_RETRIES, OPT_MARK_PROGRESS, OPT_LOG_DIR, OPT_TAIL_LINES,
00105   OPT_TESTDIR
00106 };
00107 
00108 static int record= 0, opt_sleep= -1;
00109 static char *opt_pass= NULL;
00110 const char *unix_sock= NULL;
00111 static uint32_t opt_port= 0;
00112 static uint32_t opt_max_connect_retries;
00113 static bool silent= false, verbose= false;
00114 static bool opt_mark_progress= false;
00115 static bool parsing_disabled= false;
00116 static bool display_result_vertically= false,
00117   display_metadata= false, display_result_sorted= false;
00118 static bool disable_query_log= false, disable_result_log= false;
00119 static bool disable_warnings= false;
00120 static bool disable_info= true;
00121 static bool abort_on_error= true;
00122 static bool server_initialized= false;
00123 static bool is_windows= false;
00124 static bool use_drizzle_protocol= false;
00125 static char line_buffer[MAX_DELIMITER_LENGTH], *line_buffer_pos= line_buffer;
00126 static void free_all_replace();
00127 
00128 std::string opt_basedir,
00129   opt_charsets_dir,
00130   opt_db,
00131   opt_host,
00132   opt_include,
00133   opt_testdir,
00134   opt_logdir,
00135   password,
00136   opt_password,
00137   result_file_name,
00138   opt_user,
00139   opt_protocol;
00140 
00141 static uint32_t start_lineno= 0; /* Start line of current command */
00142 
00143 /* Number of lines of the result to include in failure report */
00144 static uint32_t opt_tail_lines= 0;
00145 
00146 static char delimiter[MAX_DELIMITER_LENGTH]= ";";
00147 static uint32_t delimiter_length= 1;
00148 
00149 static char TMPDIR[FN_REFLEN];
00150 
00151 /* Block stack */
00152 enum block_cmd {
00153   cmd_none,
00154   cmd_if,
00155   cmd_while
00156 };
00157 
00158 struct st_block
00159 {
00160   int             line; /* Start line of block */
00161   bool         ok;   /* Should block be executed */
00162   enum block_cmd  cmd;  /* Command owning the block */
00163 };
00164 
00165 static struct st_block block_stack[32];
00166 static struct st_block *cur_block, *block_stack_end;
00167 
00168 /* Open file stack */
00169 struct st_test_file
00170 {
00171   FILE* file;
00172   const char *file_name;
00173   uint32_t lineno; /* Current line in file */
00174 };
00175 
00176 static boost::array<st_test_file, 16> file_stack;
00177 static st_test_file* cur_file;
00178 
00179 static const charset_info_st *charset_info= &my_charset_utf8_general_ci; /* Default charset */
00180 
00181 /*
00182   Timer related variables
00183   See the timer_output() definition for details
00184 */
00185 static char *timer_file = NULL;
00186 static uint64_t timer_start;
00187 static void timer_output();
00188 static uint64_t timer_now();
00189 
00190 static uint64_t progress_start= 0;
00191 
00192 vector<struct st_command*> q_lines;
00193 
00194 struct parser_st
00195 {
00196   int read_lines;
00197   int current_line;
00198 } parser;
00199 
00200 struct master_pos_st
00201 {
00202   char file[FN_REFLEN];
00203   uint32_t pos;
00204 };
00205 
00206 master_pos_st master_pos;
00207 
00208 /* if set, all results are concated and compared against this file */
00209 
00210 class VAR
00211 {
00212 public:
00213   char *name;
00214   int name_len;
00215   char *str_val;
00216   int str_val_len;
00217   int int_val;
00218   int alloced_len;
00219   int int_dirty; /* do not update string if int is updated until first read */
00220   int alloced;
00221   char *env_s;
00222 };
00223 
00224 /*Perl/shell-like variable registers */
00225 boost::array<VAR, 10> var_reg;
00226 
00227 typedef boost::unordered_map<string, VAR *> var_hash_t;
00228 var_hash_t var_hash;
00229 
00230 class st_connection
00231 {
00232 public:
00233   st_connection() : con(drizzle)
00234   {
00235     drizzle_con_add_options(*this, use_drizzle_protocol ? DRIZZLE_CON_EXPERIMENTAL : DRIZZLE_CON_MYSQL);
00236   }
00237 
00238   operator drizzle::connection_c&()
00239   {
00240     return con;
00241   }
00242 
00243   operator drizzle_con_st*()
00244   {
00245     return con;
00246   }
00247 
00248   drizzle::drizzle_c drizzle;
00249   drizzle::connection_c con;
00250 };
00251 
00252 typedef map<string, st_connection*> connections_t;
00253 connections_t g_connections;
00254 st_connection* cur_con= NULL;
00255 
00256 /*
00257   List of commands in drizzletest
00258   Must match the "command_names" array
00259   Add new commands before Q_UNKNOWN!
00260 */
00261 enum enum_commands 
00262 {
00263   Q_CONNECTION=1,     Q_QUERY,
00264   Q_CONNECT,      Q_SLEEP, Q_REAL_SLEEP,
00265   Q_INC,        Q_DEC,
00266   Q_SOURCE,      Q_DISCONNECT,
00267   Q_LET,        Q_ECHO,
00268   Q_WHILE,      Q_END_BLOCK,
00269   Q_SYSTEM,      Q_RESULT,
00270   Q_REQUIRE,      Q_SAVE_MASTER_POS,
00271   Q_SYNC_WITH_MASTER,
00272   Q_SYNC_SLAVE_WITH_MASTER,
00273   Q_ERROR,
00274   Q_SEND,        Q_REAP,
00275   Q_DIRTY_CLOSE,      Q_REPLACE, Q_REPLACE_COLUMN,
00276   Q_PING,        Q_EVAL,
00277   Q_EVAL_RESULT,
00278   Q_ENABLE_QUERY_LOG, Q_DISABLE_QUERY_LOG,
00279   Q_ENABLE_RESULT_LOG, Q_DISABLE_RESULT_LOG,
00280   Q_WAIT_FOR_SLAVE_TO_STOP,
00281   Q_ENABLE_WARNINGS, Q_DISABLE_WARNINGS,
00282   Q_ENABLE_INFO, Q_DISABLE_INFO,
00283   Q_ENABLE_METADATA, Q_DISABLE_METADATA,
00284   Q_EXEC, Q_DELIMITER,
00285   Q_DISABLE_ABORT_ON_ERROR, Q_ENABLE_ABORT_ON_ERROR,
00286   Q_DISPLAY_VERTICAL_RESULTS, Q_DISPLAY_HORIZONTAL_RESULTS,
00287   Q_QUERY_VERTICAL, Q_QUERY_HORIZONTAL, Q_SORTED_RESULT,
00288   Q_START_TIMER, Q_END_TIMER,
00289   Q_CHARACTER_SET,
00290   Q_DISABLE_RECONNECT, Q_ENABLE_RECONNECT,
00291   Q_IF,
00292   Q_DISABLE_PARSING, Q_ENABLE_PARSING,
00293   Q_REPLACE_REGEX, Q_REMOVE_FILE, Q_FILE_EXIST,
00294   Q_WRITE_FILE, Q_COPY_FILE, Q_PERL, Q_DIE, Q_EXIT, Q_SKIP,
00295   Q_CHMOD_FILE, Q_APPEND_FILE, Q_CAT_FILE, Q_DIFF_FILES,
00296   Q_SEND_QUIT, Q_CHANGE_USER, Q_MKDIR, Q_RMDIR,
00297 
00298   Q_UNKNOWN,             /* Unknown command.   */
00299   Q_COMMENT,             /* Comments, ignored. */
00300   Q_COMMENT_WITH_COMMAND
00301 };
00302 
00303 
00304 const char *command_names[]=
00305 {
00306   "connection",
00307   "query",
00308   "connect",
00309   "sleep",
00310   "real_sleep",
00311   "inc",
00312   "dec",
00313   "source",
00314   "disconnect",
00315   "let",
00316   "echo",
00317   "while",
00318   "end",
00319   "system",
00320   "result",
00321   "require",
00322   "save_master_pos",
00323   "sync_with_master",
00324   "sync_slave_with_master",
00325   "error",
00326   "send",
00327   "reap",
00328   "dirty_close",
00329   "replace_result",
00330   "replace_column",
00331   "ping",
00332   "eval",
00333   "eval_result",
00334   /* Enable/disable that the _query_ is logged to result file */
00335   "enable_query_log",
00336   "disable_query_log",
00337   /* Enable/disable that the _result_ from a query is logged to result file */
00338   "enable_result_log",
00339   "disable_result_log",
00340   "wait_for_slave_to_stop",
00341   "enable_warnings",
00342   "disable_warnings",
00343   "enable_info",
00344   "disable_info",
00345   "enable_metadata",
00346   "disable_metadata",
00347   "exec",
00348   "delimiter",
00349   "disable_abort_on_error",
00350   "enable_abort_on_error",
00351   "vertical_results",
00352   "horizontal_results",
00353   "query_vertical",
00354   "query_horizontal",
00355   "sorted_result",
00356   "start_timer",
00357   "end_timer",
00358   "character_set",
00359   "disable_reconnect",
00360   "enable_reconnect",
00361   "if",
00362   "disable_parsing",
00363   "enable_parsing",
00364   "replace_regex",
00365   "remove_file",
00366   "file_exists",
00367   "write_file",
00368   "copy_file",
00369   "perl",
00370   "die",
00371 
00372   /* Don't execute any more commands, compare result */
00373   "exit",
00374   "skip",
00375   "chmod",
00376   "append_file",
00377   "cat_file",
00378   "diff_files",
00379   "send_quit",
00380   "change_user",
00381   "mkdir",
00382   "rmdir",
00383 
00384   0
00385 };
00386 
00387 
00388 /*
00389   The list of error codes to --error are stored in an internal array of
00390   structs. This struct can hold numeric SQL error codes, error names or
00391   SQLSTATE codes as strings. The element next to the last active element
00392   in the list is set to type ERR_EMPTY. When an SQL statement returns an
00393   error, we use this list to check if this is an expected error.
00394 */
00395 enum match_err_type
00396 {
00397   ERR_EMPTY= 0,
00398   ERR_ERRNO,
00399   ERR_SQLSTATE
00400 };
00401 
00402 struct st_match_err
00403 {
00404   enum match_err_type type;
00405   union
00406   {
00407     uint32_t errnum;
00408     char sqlstate[DRIZZLE_MAX_SQLSTATE_SIZE+1];  /* \0 terminated string */
00409   } code;
00410 };
00411 
00412 struct st_expected_errors
00413 {
00414   struct st_match_err err[10];
00415   uint32_t count;
00416 };
00417 
00418 static st_expected_errors saved_expected_errors;
00419 
00420 class st_command
00421 {
00422 public:
00423   char *query, *query_buf,*first_argument,*last_argument,*end;
00424   int first_word_len, query_len;
00425   bool abort_on_error;
00426   st_expected_errors expected_errors;
00427   string require_file;
00428   enum_commands type;
00429 
00430   st_command()
00431     : query(NULL), query_buf(NULL), first_argument(NULL), last_argument(NULL),
00432       end(NULL), first_word_len(0), query_len(0), abort_on_error(false),
00433       require_file(""), type(Q_CONNECTION)
00434   {
00435     memset(&expected_errors, 0, sizeof(st_expected_errors));
00436   }
00437 
00438   ~st_command()
00439   {
00440     free(query_buf);
00441   }
00442 };
00443 
00444 TYPELIB command_typelib= {array_elements(command_names),"",
00445                           command_names, 0};
00446 
00447 string ds_res, ds_progress, ds_warning_messages;
00448 
00449 char builtin_echo[FN_REFLEN];
00450 
00451 void die(const char *fmt, ...)
00452   __attribute__((format(printf, 1, 2)));
00453 void abort_not_supported_test(const char *fmt, ...)
00454   __attribute__((format(printf, 1, 2)));
00455 void verbose_msg(const char *fmt, ...)
00456   __attribute__((format(printf, 1, 2)));
00457 void warning_msg(const char *fmt, ...)
00458   __attribute__((format(printf, 1, 2)));
00459 void log_msg(const char *fmt, ...)
00460   __attribute__((format(printf, 1, 2)));
00461 
00462 VAR* var_from_env(const char *, const char *);
00463 VAR* var_init(VAR* v, const char *name, int name_len, const char *val,
00464               int val_len);
00465 VAR* var_get(const char *var_name, const char** var_name_end,
00466              bool raw, bool ignore_not_existing);
00467 void eval_expr(VAR* v, const char *p, const char** p_end);
00468 bool match_delimiter(int c, const char *delim, uint32_t length);
00469 void dump_result_to_reject_file(char *buf, int size);
00470 void dump_result_to_log_file(const char *buf, int size);
00471 void dump_warning_messages();
00472 void dump_progress();
00473 
00474 void do_eval(string *query_eval, const char *query,
00475              const char *query_end, bool pass_through_escape_chars);
00476 void str_to_file(const char *fname, const char *str, int size);
00477 void str_to_file2(const char *fname, const char *str, int size, bool append);
00478 
00479 /* For replace_column */
00480 static char *replace_column[MAX_COLUMNS];
00481 static uint32_t max_replace_column= 0;
00482 void do_get_replace_column(st_command*);
00483 void free_replace_column();
00484 
00485 /* For replace */
00486 void do_get_replace(st_command* command);
00487 void free_replace();
00488 
00489 /* For replace_regex */
00490 void do_get_replace_regex(st_command* command);
00491 
00492 void replace_append_mem(string& ds, const char *val, int len);
00493 void replace_append(string *ds, const char *val);
00494 void replace_append_uint(string& ds, uint32_t val);
00495 void append_sorted(string& ds, const string& ds_input);
00496 
00497 void handle_error(st_command*,
00498                   unsigned int err_errno, const char *err_error,
00499                   const char *err_sqlstate, string *ds);
00500 void handle_no_error(st_command*);
00501 
00502 
00503 void do_eval(string *query_eval, const char *query,
00504              const char *query_end, bool pass_through_escape_chars)
00505 {
00506   char c, next_c;
00507   int escaped = 0;
00508 
00509   for (const char *p= query; (c= *p) && p < query_end; ++p)
00510   {
00511     switch(c) 
00512     {
00513     case '$':
00514       if (escaped)
00515       {
00516         escaped= 0;
00517         query_eval->append(p, 1);
00518       }
00519       else
00520       {
00521         VAR* v= var_get(p, &p, 0, 0);
00522         if (not v)
00523           die("Bad variable in eval");
00524         query_eval->append(v->str_val, v->str_val_len);
00525       }
00526       break;
00527     case '\\':
00528       next_c= *(p+1);
00529       if (escaped)
00530       {
00531         escaped= 0;
00532         query_eval->append(p, 1);
00533       }
00534       else if (next_c == '\\' || next_c == '$' || next_c == '"')
00535       {
00536         /* Set escaped only if next char is \, " or $ */
00537         escaped= 1;
00538 
00539         if (pass_through_escape_chars)
00540         {
00541           /* The escape char should be added to the output string. */
00542           query_eval->append(p, 1);
00543         }
00544       }
00545       else
00546         query_eval->append(p, 1);
00547       break;
00548     default:
00549       escaped= 0;
00550       query_eval->append(p, 1);
00551     }
00552   }
00553 }
00554 
00555 
00556 /*
00557   Concatenates any number of strings, escapes any OS quote in the result then
00558   surround the whole affair in another set of quotes which is finally appended
00559   to specified string.  This function is especially useful when
00560   building strings to be executed with the system() function.
00561 
00562   @param str string which will have addtional strings appended.
00563   @param append string to be appended.
00564   @param ... Optional. Additional string(s) to be appended.
00565 
00566   @note The final argument in the list must be NULL even if no additional
00567   options are passed.
00568 */
00569 
00570 static void append_os_quoted(string *str, const char *append, ...)
00571 {
00572   const char *quote_str= "\'";
00573   const uint32_t  quote_len= 1;
00574 
00575   va_list dirty_text;
00576 
00577   str->append(quote_str, quote_len); /* Leading quote */
00578   va_start(dirty_text, append);
00579   while (append != NULL)
00580   {
00581     const char  *cur_pos= append;
00582     const char *next_pos= cur_pos;
00583 
00584     /* Search for quote in each string and replace with escaped quote */
00585     while ((next_pos= strrchr(cur_pos, quote_str[0])) != NULL)
00586     {
00587       str->append(cur_pos, next_pos - cur_pos);
00588       str->append("\\", 1);
00589       str->append(quote_str, quote_len);
00590       cur_pos= next_pos + 1;
00591     }
00592     str->append(cur_pos);
00593     append= va_arg(dirty_text, char *);
00594   }
00595   va_end(dirty_text);
00596   str->append(quote_str, quote_len); /* Trailing quote */
00597 }
00598 
00599 
00600 /*
00601   Run query and dump the result to stdout in vertical format
00602 
00603   NOTE! This function should be safe to call when an error
00604   has occured and thus any further errors will be ignored(although logged)
00605 
00606   SYNOPSIS
00607   show_query
00608   drizzle - connection to use
00609   query - query to run
00610 
00611 */
00612 
00613 static int dt_query_log(drizzle::connection_c& con, drizzle::result_c& res, const std::string& query)
00614 {
00615   if (drizzle_return_t ret= con.query(res, query))
00616   {
00617     if (ret == DRIZZLE_RETURN_ERROR_CODE)
00618     {
00619       log_msg("Error running query '%s': %d %s", query.c_str(), res.error_code(), res.error());
00620     }
00621     else
00622     {
00623       log_msg("Error running query '%s': %d %s", query.c_str(), ret, con.error());
00624     }
00625     return 1;
00626   }
00627   return res.column_count() == 0;
00628 }
00629 
00630 static void show_query(drizzle::connection_c& con, const char* query)
00631 {
00632   drizzle::result_c res;
00633   if (dt_query_log(con, res, query))
00634     return;
00635 
00636   unsigned int row_num= 0;
00637   unsigned int num_fields= res.column_count();
00638 
00639   fprintf(stderr, "=== %s ===\n", query);
00640   while (drizzle_row_t row= res.row_next())
00641   {
00642     size_t *lengths= res.row_field_sizes();
00643     row_num++;
00644 
00645     fprintf(stderr, "---- %d. ----\n", row_num);
00646     res.column_seek(0);
00647     for (unsigned int i= 0; i < num_fields; i++)
00648     {
00649       drizzle_column_st* column= res.column_next();
00650       fprintf(stderr, "%s\t%.*s\n", drizzle_column_name(column), (int)lengths[i], row[i] ? row[i] : "NULL");
00651     }
00652   }
00653   for (size_t i= 0; i < strlen(query)+8; i++)
00654     fprintf(stderr, "=");
00655   fprintf(stderr, "\n\n");
00656 }
00657 
00658 
00659 /*
00660   Show any warnings just before the error. Since the last error
00661   is added to the warning stack, only print @@warning_count-1 warnings.
00662 
00663   NOTE! This function should be safe to call when an error
00664   has occured and this any further errors will be ignored(although logged)
00665 
00666   SYNOPSIS
00667   show_warnings_before_error
00668   drizzle - connection to use
00669 
00670 */
00671 
00672 static void show_warnings_before_error(drizzle::connection_c& con)
00673 {
00674   drizzle::result_c res;
00675   if (dt_query_log(con, res, "show warnings"))
00676     return;
00677 
00678   if (res.row_count() >= 2) /* Don't display the last row, it's "last error" */
00679   {
00680     unsigned int row_num= 0;
00681     unsigned int num_fields= res.column_count();
00682 
00683     fprintf(stderr, "\nWarnings from just before the error:\n");
00684     while (drizzle_row_t row= res.row_next())
00685     {
00686       size_t *lengths= res.row_field_sizes();
00687 
00688       if (++row_num >= res.row_count())
00689       {
00690         /* Don't display the last row, it's "last error" */
00691         break;
00692       }
00693 
00694       for (uint32_t i= 0; i < num_fields; i++)
00695       {
00696         fprintf(stderr, "%.*s ", (int)lengths[i], row[i] ? row[i] : "NULL");
00697       }
00698       fprintf(stderr, "\n");
00699     }
00700   }
00701 }
00702 
00703 enum arg_type
00704 {
00705   ARG_STRING,
00706   ARG_REST
00707 };
00708 
00709 struct command_arg 
00710 {
00711   const char *argname;       /* Name of argument   */
00712   enum arg_type type;        /* Type of argument   */
00713   bool required;          /* Argument required  */
00714   string *ds;        /* Storage for argument */
00715   const char *description;   /* Description of the argument */
00716 };
00717 
00718 
00719 static void check_command_args(st_command* command,
00720                                const char *arguments,
00721                                const struct command_arg *args,
00722                                int num_args, const char delimiter_arg)
00723 {
00724   const char *ptr= arguments;
00725   const char *start;
00726 
00727   for (int i= 0; i < num_args; i++)
00728   {
00729     const struct command_arg *arg= &args[i];
00730     arg->ds->clear();
00731 
00732     bool known_arg_type= true;
00733     switch (arg->type) {
00734       /* A string */
00735     case ARG_STRING:
00736       /* Skip leading spaces */
00737       while (*ptr && *ptr == ' ')
00738         ptr++;
00739       start= ptr;
00740       /* Find end of arg, terminated by "delimiter_arg" */
00741       while (*ptr && *ptr != delimiter_arg)
00742         ptr++;
00743       if (ptr > start)
00744       {
00745         do_eval(arg->ds, start, ptr, false);
00746       }
00747       else
00748       {
00749         /* Empty string */
00750         arg->ds->erase();
00751       }
00752       command->last_argument= (char*)ptr;
00753 
00754       /* Step past the delimiter */
00755       if (*ptr && *ptr == delimiter_arg)
00756         ptr++;
00757       break;
00758 
00759       /* Rest of line */
00760     case ARG_REST:
00761       start= ptr;
00762       do_eval(arg->ds, start, command->end, false);
00763       command->last_argument= command->end;
00764       break;
00765 
00766     default:
00767       known_arg_type= false;
00768       break;
00769     }
00770     assert(known_arg_type);
00771 
00772     /* Check required arg */
00773     if (arg->ds->length() == 0 && arg->required)
00774       die("Missing required argument '%s' to command '%.*s'", arg->argname,
00775           command->first_word_len, command->query);
00776 
00777   }
00778   /* Check for too many arguments passed */
00779   ptr= command->last_argument;
00780   while (ptr <= command->end)
00781   {
00782     if (*ptr && *ptr != ' ')
00783       die("Extra argument '%s' passed to '%.*s'",
00784           ptr, command->first_word_len, command->query);
00785     ptr++;
00786   }
00787   return;
00788 }
00789 
00790 
00791 static void handle_command_error(st_command* command, uint32_t error)
00792 {
00793   if (error != 0)
00794   {
00795     if (command->abort_on_error)
00796       die("command \"%.*s\" failed with error %d", command->first_word_len, command->query, error);
00797     for (uint32_t i= 0; i < command->expected_errors.count; i++)
00798     {
00799       if (command->expected_errors.err[i].type == ERR_ERRNO &&
00800           command->expected_errors.err[i].code.errnum == error)
00801       {
00802         return;
00803       }
00804     }
00805     die("command \"%.*s\" failed with wrong error: %d",
00806         command->first_word_len, command->query, error);
00807   }
00808   else if (command->expected_errors.err[0].type == ERR_ERRNO &&
00809            command->expected_errors.err[0].code.errnum != 0)
00810   {
00811     /* Error code we wanted was != 0, i.e. not an expected success */
00812     die("command \"%.*s\" succeeded - should have failed with errno %d...",
00813         command->first_word_len, command->query,
00814         command->expected_errors.err[0].code.errnum);
00815   }
00816 }
00817 
00818 static void cleanup_and_exit(int exit_code)
00819 {
00820   if (!silent) 
00821   {
00822     switch (exit_code) 
00823     {
00824     case 1:
00825       printf("not ok\n");
00826       break;
00827     case 0:
00828       printf("ok\n");
00829       break;
00830     case 62:
00831       printf("skipped\n");
00832       break;
00833     default:
00834       printf("unknown exit code: %d\n", exit_code);
00835       assert(false);
00836     }
00837   }
00838   exit(exit_code);
00839 }
00840 
00841 void die(const char *fmt, ...)
00842 {
00843   /*
00844     Protect against dying twice
00845     first time 'die' is called, try to write log files
00846     second time, just exit
00847   */
00848   static bool dying= false;
00849   if (dying)
00850     cleanup_and_exit(1);
00851   dying= true;
00852 
00853   /* Print the error message */
00854   fprintf(stderr, "drizzletest: ");
00855   if (cur_file && cur_file != file_stack.data())
00856     fprintf(stderr, "In included file \"%s\": ", cur_file->file_name);
00857   if (start_lineno > 0)
00858     fprintf(stderr, "At line %u: ", start_lineno);
00859   if (fmt)
00860   {
00861     va_list args;
00862     va_start(args, fmt);
00863     vfprintf(stderr, fmt, args);
00864     va_end(args);
00865   }
00866   else
00867     fprintf(stderr, "unknown error");
00868   fprintf(stderr, "\n");
00869   fflush(stderr);
00870 
00871   /* Show results from queries just before failure */
00872   if (ds_res.length() && opt_tail_lines)
00873   {
00874     int tail_lines= opt_tail_lines;
00875     const char* show_from= ds_res.c_str() + ds_res.length() - 1;
00876     while (show_from > ds_res.c_str() && tail_lines > 0 )
00877     {
00878       show_from--;
00879       if (*show_from == '\n')
00880         tail_lines--;
00881     }
00882     fprintf(stderr, "\nThe result from queries just before the failure was:\n");
00883     if (show_from > ds_res.c_str())
00884       fprintf(stderr, "< snip >");
00885     fprintf(stderr, "%s", show_from);
00886     fflush(stderr);
00887   }
00888 
00889   /* Dump the result that has been accumulated so far to .log file */
00890   if (! result_file_name.empty() && ds_res.length())
00891     dump_result_to_log_file(ds_res.c_str(), ds_res.length());
00892 
00893   /* Dump warning messages */
00894   if (! result_file_name.empty() && ds_warning_messages.length())
00895     dump_warning_messages();
00896 
00897   /*
00898     Help debugging by displaying any warnings that might have
00899     been produced prior to the error
00900   */
00901   if (cur_con)
00902   {
00903     show_warnings_before_error(*cur_con);
00904   }
00905 
00906   cleanup_and_exit(1);
00907 }
00908 
00909 
00910 void abort_not_supported_test(const char *fmt, ...)
00911 {
00912   va_list args;
00913   st_test_file* err_file= cur_file;
00914 
00915 
00916   /* Print include filestack */
00917   fprintf(stderr, "The test '%s' is not supported by this installation\n",
00918           file_stack[0].file_name);
00919   fprintf(stderr, "Detected in file %s at line %d\n",
00920           err_file->file_name, err_file->lineno);
00921   while (err_file != file_stack.data())
00922   {
00923     err_file--;
00924     fprintf(stderr, "included from %s at line %d\n",
00925             err_file->file_name, err_file->lineno);
00926   }
00927 
00928   /* Print error message */
00929   va_start(args, fmt);
00930   if (fmt)
00931   {
00932     fprintf(stderr, "reason: ");
00933     vfprintf(stderr, fmt, args);
00934     fprintf(stderr, "\n");
00935     fflush(stderr);
00936   }
00937   va_end(args);
00938 
00939   cleanup_and_exit(62);
00940 }
00941 
00942 
00943 void verbose_msg(const char *fmt, ...)
00944 {
00945   if (!verbose)
00946     return;
00947   va_list args;
00948   va_start(args, fmt);
00949   fprintf(stderr, "drizzletest: ");
00950   if (cur_file && cur_file != file_stack.data())
00951     fprintf(stderr, "In included file \"%s\": ", cur_file->file_name);
00952   if (start_lineno != 0)
00953     fprintf(stderr, "At line %u: ", start_lineno);
00954   vfprintf(stderr, fmt, args);
00955   fprintf(stderr, "\n");
00956   va_end(args);
00957 }
00958 
00959 
00960 void warning_msg(const char *fmt, ...)
00961 {
00962   va_list args;
00963   char buff[512];
00964   size_t len;
00965 
00966   va_start(args, fmt);
00967   ds_warning_messages += "drizzletest: ";
00968   if (start_lineno != 0)
00969   {
00970     ds_warning_messages += "Warning detected ";
00971     if (cur_file && cur_file != file_stack.data())
00972     {
00973       len= snprintf(buff, sizeof(buff), "in included file %s ", cur_file->file_name);
00974       ds_warning_messages.append(buff, len);
00975     }
00976     len= snprintf(buff, sizeof(buff), "at line %d: ", start_lineno);
00977     ds_warning_messages.append(buff, len);
00978   }
00979 
00980   len= vsnprintf(buff, sizeof(buff), fmt, args);
00981   ds_warning_messages.append(buff, len);
00982 
00983   ds_warning_messages += "\n";
00984   va_end(args);
00985 
00986   return;
00987 }
00988 
00989 
00990 void log_msg(const char *fmt, ...)
00991 {
00992   va_list args;
00993   char buff[1024];
00994 
00995   va_start(args, fmt);
00996   size_t len= vsnprintf(buff, sizeof(buff)-1, fmt, args);
00997   va_end(args);
00998 
00999   ds_res.append(buff, len);
01000   ds_res += "\n";
01001 }
01002 
01003 
01004 /*
01005   Read a file and append it to ds
01006 
01007   SYNOPSIS
01008   cat_file
01009   ds - pointer to dynamic string where to add the files content
01010   filename - name of the file to read
01011 
01012 */
01013 
01014 static void cat_file(string& ds, const char* filename)
01015 {
01016   int fd= internal::my_open(filename, O_RDONLY, MYF(0));
01017   if (fd < 0)
01018     die("Failed to open file '%s'", filename);
01019   char buff[512];
01020   while (uint32_t len= internal::my_read(fd, (unsigned char*)&buff, sizeof(buff), MYF(0)))
01021   {
01022     char *p= buff, *start= buff;
01023     while (p < buff+len)
01024     {
01025       /* Convert cr/lf to lf */
01026       if (*p == '\r' && *(p+1) && *(p+1)== '\n')
01027       {
01028         /* Add fake newline instead of cr and output the line */
01029         *p= '\n';
01030         p++; /* Step past the "fake" newline */
01031         ds.append(start, p - start);
01032         p++; /* Step past the "fake" newline */
01033         start= p;
01034       }
01035       else
01036         p++;
01037     }
01038     /* Output any chars that might be left */
01039     ds.append(start, p - start);
01040   }
01041   internal::my_close(fd, MYF(0));
01042 }
01043 
01044 
01045 /*
01046   Run the specified command with popen
01047 
01048   SYNOPSIS
01049   run_command
01050   cmd - command to execute(should be properly quoted
01051   result - pointer to string where to store the result
01052 
01053 */
01054 
01055 static int run_command(const char* cmd, string& result)
01056 {
01057   FILE* res_file= popen(cmd, "r");
01058   if (not res_file)
01059     die("popen(\"%s\", \"r\") failed", cmd);
01060 
01061   char buf[512]= {0};
01062   while (fgets(buf, sizeof(buf), res_file))
01063   {
01064     /* Save the output of this command in the supplied string */
01065     result.append(buf);
01066   }
01067   int error= pclose(res_file);
01068   return WEXITSTATUS(error);
01069 }
01070 
01071 
01072 /*
01073   Run the specified tool with variable number of arguments
01074 
01075   SYNOPSIS
01076   run_tool
01077   tool_path - the name of the tool to run
01078   result - pointer to dynamic string where to store the result
01079   ... - variable number of arguments that will be properly
01080   quoted and appended after the tool's name
01081 
01082 */
01083 
01084 static int run_tool(const char *tool_path, string& result, ...)
01085 {
01086   string ds_cmdline;
01087   append_os_quoted(&ds_cmdline, tool_path, NULL);
01088   ds_cmdline += " ";
01089 
01090   va_list args;
01091   va_start(args, result);
01092   while (const char* arg= va_arg(args, char *))
01093   {
01094     /* Options should be os quoted */
01095     if (strncmp(arg, "--", 2) == 0)
01096       append_os_quoted(&ds_cmdline, arg, NULL);
01097     else
01098       ds_cmdline += arg;
01099     ds_cmdline += " ";
01100   }
01101 
01102   va_end(args);
01103 
01104   return run_command(ds_cmdline.c_str(), result);
01105 }
01106 
01107 
01108 /*
01109   Show the diff of two files using the systems builtin diff
01110   command. If no such diff command exist, just dump the content
01111   of the two files and inform about how to get "diff"
01112 
01113   SYNOPSIS
01114   show_diff
01115   ds - pointer to dynamic string where to add the diff(may be NULL)
01116   filename1 - name of first file
01117   filename2 - name of second file
01118 
01119 */
01120 
01121 static void show_diff(string* ds, const char* filename1, const char* filename2)
01122 {
01123   string ds_tmp;
01124 
01125   /* First try with unified diff */
01126   if (run_tool("diff",
01127                ds_tmp, /* Get output from diff in ds_tmp */
01128                "-u",
01129                filename1,
01130                filename2,
01131                "2>&1",
01132                NULL) > 1) /* Most "diff" tools return >1 if error */
01133   {
01134 
01135     /* Fallback to context diff with "diff -c" */
01136     if (run_tool("diff",
01137                  ds_tmp, /* Get output from diff in ds_tmp */
01138                  "-c",
01139                  filename1,
01140                  filename2,
01141                  "2>&1",
01142                  NULL) > 1) /* Most "diff" tools return >1 if error */
01143     {
01144       /*
01145         Fallback to dump both files to result file and inform
01146         about installing "diff"
01147       */
01148       ds_tmp=
01149                     "\n"
01150                     "The two files differ but it was not possible to execute 'diff' in\n"
01151                     "order to show only the difference, tried both 'diff -u' or 'diff -c'.\n"
01152                     "Instead the whole content of the two files was shown for you to diff manually. ;)\n"
01153                     "\n"
01154                     "To get a better report you should install 'diff' on your system, which you\n"
01155                     "for example can get from http://www.gnu.org/software/diffutils/diffutils.html\n"
01156                     "\n";
01157 
01158       ds_tmp += " --- ";
01159       ds_tmp += filename1;
01160       ds_tmp += " >>>\n";
01161       cat_file(ds_tmp, filename1);
01162       ds_tmp += "<<<\n --- ";
01163       ds_tmp += filename1;
01164       ds_tmp += " >>>\n";
01165       cat_file(ds_tmp, filename2);
01166       ds_tmp += "<<<<\n";
01167     }
01168   }
01169 
01170   if (ds)
01171   {
01172     /* Add the diff to output */
01173     *ds += ds_tmp;
01174   }
01175   else
01176   {
01177     /* Print diff directly to stdout */
01178     fprintf(stderr, "%s\n", ds_tmp.c_str());
01179   }
01180 
01181 }
01182 
01183 enum compare_files_result_enum 
01184 {
01185   RESULT_OK= 0,
01186   RESULT_CONTENT_MISMATCH= 1,
01187   RESULT_LENGTH_MISMATCH= 2
01188 };
01189 
01190 /*
01191   Compare two files, given a fd to the first file and
01192   name of the second file
01193 
01194   SYNOPSIS
01195   compare_files2
01196   fd - Open file descriptor of the first file
01197   filename2 - Name of second file
01198 
01199   RETURN VALUES
01200   According to the values in "compare_files_result_enum"
01201 
01202 */
01203 
01204 static int compare_files2(int fd, const char* filename2)
01205 {
01206   int error= RESULT_OK;
01207   uint32_t len, len2;
01208   char buff[512], buff2[512];
01209   const char *fname= filename2;
01210   string tmpfile;
01211 
01212   int fd2= internal::my_open(fname, O_RDONLY, MYF(0));
01213   if (fd2 < 0)
01214   {
01215     internal::my_close(fd, MYF(0));
01216     if (! opt_testdir.empty())
01217     {
01218       tmpfile= opt_testdir;
01219       if (tmpfile[tmpfile.length()] != '/')
01220         tmpfile += "/";
01221       tmpfile += filename2;
01222       fname= tmpfile.c_str();
01223     }
01224     if ((fd2= internal::my_open(fname, O_RDONLY, MYF(0))) < 0)
01225     {
01226       internal::my_close(fd, MYF(0));
01227     
01228       die("Failed to open second file: '%s'", fname);
01229     }
01230   }
01231   while ((len= internal::my_read(fd, (unsigned char*)&buff,
01232                       sizeof(buff), MYF(0))) > 0)
01233   {
01234     if ((len2= internal::my_read(fd2, (unsigned char*)&buff2,
01235                        sizeof(buff2), MYF(0))) < len)
01236     {
01237       /* File 2 was smaller */
01238       error= RESULT_LENGTH_MISMATCH;
01239       break;
01240     }
01241     if (len2 > len)
01242     {
01243       /* File 1 was smaller */
01244       error= RESULT_LENGTH_MISMATCH;
01245       break;
01246     }
01247     if ((memcmp(buff, buff2, len)))
01248     {
01249       /* Content of this part differed */
01250       error= RESULT_CONTENT_MISMATCH;
01251       break;
01252     }
01253   }
01254   if (!error && internal::my_read(fd2, (unsigned char*)&buff2,
01255                         sizeof(buff2), MYF(0)) > 0)
01256   {
01257     /* File 1 was smaller */
01258     error= RESULT_LENGTH_MISMATCH;
01259   }
01260 
01261   internal::my_close(fd2, MYF(0));
01262 
01263   return error;
01264 }
01265 
01266 
01267 /*
01268   Compare two files, given their filenames
01269 
01270   SYNOPSIS
01271   compare_files
01272   filename1 - Name of first file
01273   filename2 - Name of second file
01274 
01275   RETURN VALUES
01276   See 'compare_files2'
01277 
01278 */
01279 
01280 static int compare_files(const char* filename1, const char* filename2)
01281 {
01282   int fd= internal::my_open(filename1, O_RDONLY, MYF(0));
01283   if (fd < 0)
01284     die("Failed to open first file: '%s'", filename1);
01285   int error= compare_files2(fd, filename2);
01286   internal::my_close(fd, MYF(0));
01287   return error;
01288 }
01289 
01290 
01291 /*
01292   Compare content of the string in ds to content of file fname
01293 
01294   SYNOPSIS
01295   string_cmp
01296   ds - Dynamic string containing the string o be compared
01297   fname - Name of file to compare with
01298 
01299   RETURN VALUES
01300   See 'compare_files2'
01301 */
01302 
01303 static int string_cmp(const string& ds, const char *fname)
01304 {
01305   char temp_file_path[FN_REFLEN];
01306 
01307   int fd= internal::create_temp_file(temp_file_path, TMPDIR, "tmp", MYF(MY_WME));
01308   if (fd < 0)
01309     die("Failed to create temporary file for ds");
01310 
01311   /* Write ds to temporary file and set file pos to beginning*/
01312   if (internal::my_write(fd, (unsigned char *) ds.data(), ds.length(), MYF(MY_FNABP | MY_WME)) ||
01313       lseek(fd, 0, SEEK_SET) == MY_FILEPOS_ERROR)
01314   {
01315     internal::my_close(fd, MYF(0));
01316     /* Remove the temporary file */
01317     internal::my_delete(temp_file_path, MYF(0));
01318     die("Failed to write file '%s'", temp_file_path);
01319   }
01320 
01321   int error= compare_files2(fd, fname);
01322 
01323   internal::my_close(fd, MYF(0));
01324   /* Remove the temporary file */
01325   internal::my_delete(temp_file_path, MYF(0));
01326 
01327   return error;
01328 }
01329 
01330 
01331 /*
01332   Check the content of ds against result file
01333 
01334   SYNOPSIS
01335   check_result
01336   ds - content to be checked
01337 
01338   RETURN VALUES
01339   error - the function will not return
01340 
01341 */
01342 
01343 static void check_result(string& ds)
01344 {
01345   const char* mess= "Result content mismatch\n";
01346 
01347   if (access(result_file_name.c_str(), F_OK) != 0)
01348     die("The specified result file does not exist: '%s'", result_file_name.c_str());
01349 
01350   switch (string_cmp(ds, result_file_name.c_str())) 
01351   {
01352   case RESULT_OK:
01353     break; /* ok */
01354   case RESULT_LENGTH_MISMATCH:
01355     mess= "Result length mismatch\n";
01356     /* Fallthrough */
01357   case RESULT_CONTENT_MISMATCH:
01358   {
01359     /*
01360       Result mismatched, dump results to .reject file
01361       and then show the diff
01362     */
01363     char reject_file[FN_REFLEN];
01364     size_t reject_length;
01365     internal::dirname_part(reject_file, result_file_name.c_str(), &reject_length);
01366 
01367     if (access(reject_file, W_OK) == 0)
01368     {
01369       /* Result file directory is writable, save reject file there */
01370       internal::fn_format(reject_file, result_file_name.c_str(), NULL, ".reject", MY_REPLACE_EXT);
01371     }
01372     else
01373     {
01374       /* Put reject file in opt_logdir */
01375       internal::fn_format(reject_file, result_file_name.c_str(), opt_logdir.c_str(), ".reject", MY_REPLACE_DIR | MY_REPLACE_EXT);
01376     }
01377     str_to_file(reject_file, ds.data(), ds.length());
01378 
01379     ds.erase(); /* Don't create a .log file */
01380 
01381     show_diff(NULL, result_file_name.c_str(), reject_file);
01382     die("%s",mess);
01383     break;
01384   }
01385   default: /* impossible */
01386     die("Unknown error code from dyn_string_cmp()");
01387   }
01388 }
01389 
01390 
01391 /*
01392   Check the content of ds against a require file
01393   If match fails, abort the test with special error code
01394   indicating that test is not supported
01395 
01396   SYNOPSIS
01397   check_require
01398   ds - content to be checked
01399   fname - name of file to check against
01400 
01401   RETURN VALUES
01402   error - the function will not return
01403 
01404 */
01405 
01406 static void check_require(const string& ds, const string& fname)
01407 {
01408   if (string_cmp(ds, fname.c_str()))
01409   {
01410     char reason[FN_REFLEN];
01411     internal::fn_format(reason, fname.c_str(), "", "", MY_REPLACE_EXT | MY_REPLACE_DIR);
01412     abort_not_supported_test("Test requires: '%s'", reason);
01413   }
01414 }
01415 
01416 
01417 /*
01418   Remove surrounding chars from string
01419 
01420   Return 1 if first character is found but not last
01421 */
01422 static int strip_surrounding(char* str, char c1, char c2)
01423 {
01424   char* ptr= str;
01425 
01426   /* Check if the first non space character is c1 */
01427   while (*ptr && charset_info->isspace(*ptr))
01428     ptr++;
01429   if (*ptr == c1)
01430   {
01431     /* Replace it with a space */
01432     *ptr= ' ';
01433 
01434     /* Last non space charecter should be c2 */
01435     ptr= strchr(str, '\0')-1;
01436     while (*ptr && charset_info->isspace(*ptr))
01437       ptr--;
01438     if (*ptr == c2)
01439     {
01440       /* Replace it with \0 */
01441       *ptr= 0;
01442     }
01443     else
01444     {
01445       /* Mismatch detected */
01446       return 1;
01447     }
01448   }
01449   return 0;
01450 }
01451 
01452 
01453 static void strip_parentheses(st_command* command)
01454 {
01455   if (strip_surrounding(command->first_argument, '(', ')'))
01456     die("%.*s - argument list started with '%c' must be ended with '%c'",
01457         command->first_word_len, command->query, '(', ')');
01458 }
01459 
01460 
01461 
01462 VAR *var_init(VAR *v, const char *name, int name_len, const char *val,
01463               int val_len)
01464 {
01465   if (!name_len && name)
01466     name_len = strlen(name);
01467   if (!val_len && val)
01468     val_len = strlen(val) ;
01469   VAR *tmp_var = v ? v : (VAR*)malloc(sizeof(*tmp_var) + name_len+1);
01470 
01471   tmp_var->name = name ? (char*)&tmp_var[1] : 0;
01472   tmp_var->alloced = (v == 0);
01473 
01474   int val_alloc_len = val_len + 16; /* room to grow */
01475   tmp_var->str_val = (char*)malloc(val_alloc_len+1);
01476 
01477   memcpy(tmp_var->name, name, name_len);
01478   if (val)
01479   {
01480     memcpy(tmp_var->str_val, val, val_len);
01481     tmp_var->str_val[val_len]= 0;
01482   }
01483   tmp_var->name_len = name_len;
01484   tmp_var->str_val_len = val_len;
01485   tmp_var->alloced_len = val_alloc_len;
01486   tmp_var->int_val = val ? atoi(val) : 0;
01487   tmp_var->int_dirty = false;
01488   tmp_var->env_s = 0;
01489   return tmp_var;
01490 }
01491 
01492 VAR* var_from_env(const char *name, const char *def_val)
01493 {
01494   const char *tmp= getenv(name);
01495   if (!tmp)
01496     tmp = def_val;
01497   return var_hash[name] = var_init(0, name, strlen(name), tmp, strlen(tmp));
01498 }
01499 
01500 VAR* var_get(const char *var_name, const char **var_name_end, bool raw,
01501              bool ignore_not_existing)
01502 {
01503   int digit;
01504   VAR *v;
01505   if (*var_name != '$')
01506     goto err;
01507   digit = *++var_name - '0';
01508   if (digit < 0 || digit >= 10)
01509   {
01510     const char *save_var_name = var_name, *end;
01511     uint32_t length;
01512     end = (var_name_end) ? *var_name_end : 0;
01513     while (charset_info->isvar(*var_name) && var_name != end)
01514       var_name++;
01515     if (var_name == save_var_name)
01516     {
01517       if (ignore_not_existing)
01518         return(0);
01519       die("Empty variable");
01520     }
01521     length= (uint32_t) (var_name - save_var_name);
01522     if (length >= MAX_VAR_NAME_LENGTH)
01523       die("Too long variable name: %s", save_var_name);
01524 
01525     string save_var_name_str(save_var_name, length);
01526     if (var_hash_t::mapped_type* ptr= find_ptr(var_hash, save_var_name_str))
01527       v= *ptr;
01528     else
01529     {
01530       char buff[MAX_VAR_NAME_LENGTH+1];
01531       strncpy(buff, save_var_name, length);
01532       buff[length]= '\0';
01533       v= var_from_env(buff, "");
01534     }
01535     var_name--;  /* Point at last character */
01536   }
01537   else
01538     v = &var_reg[digit];
01539 
01540   if (!raw && v->int_dirty)
01541   {
01542     sprintf(v->str_val, "%d", v->int_val);
01543     v->int_dirty = 0;
01544     v->str_val_len = strlen(v->str_val);
01545   }
01546   if (var_name_end)
01547     *var_name_end = var_name  ;
01548   return(v);
01549 err:
01550   if (var_name_end)
01551     *var_name_end = 0;
01552   die("Unsupported variable name: %s", var_name);
01553   return(0);
01554 }
01555 
01556 
01557 static VAR *var_obtain(const char *name, int len)
01558 {
01559   string var_name(name, len);
01560   if (var_hash_t::mapped_type* ptr= find_ptr(var_hash, var_name))
01561     return *ptr;
01562   return var_hash[var_name] = var_init(0, name, len, "", 0);
01563 }
01564 
01565 
01566 /*
01567   - if variable starts with a $ it is regarded as a local test varable
01568   - if not it is treated as a environment variable, and the corresponding
01569   environment variable will be updated
01570 */
01571 
01572 static void var_set(const char *var_name, const char *var_name_end,
01573                     const char *var_val, const char *var_val_end)
01574 {
01575   int digit, env_var= 0;
01576   VAR *v;
01577 
01578   if (*var_name != '$')
01579     env_var= 1;
01580   else
01581     var_name++;
01582 
01583   digit= *var_name - '0';
01584   if (!(digit < 10 && digit >= 0))
01585   {
01586     v= var_obtain(var_name, (uint32_t) (var_name_end - var_name));
01587   }
01588   else
01589     v= &var_reg[digit];
01590 
01591   eval_expr(v, var_val, (const char**) &var_val_end);
01592 
01593   if (env_var)
01594   {
01595     char buf[1024], *old_env_s= v->env_s;
01596     if (v->int_dirty)
01597     {
01598       sprintf(v->str_val, "%d", v->int_val);
01599       v->int_dirty= 0;
01600       v->str_val_len= strlen(v->str_val);
01601     }
01602     snprintf(buf, sizeof(buf), "%.*s=%.*s",
01603              v->name_len, v->name,
01604              v->str_val_len, v->str_val);
01605     v->env_s= strdup(buf);
01606     putenv(v->env_s);
01607     free(old_env_s);
01608   }
01609   return;
01610 }
01611 
01612 
01613 static void var_set_string(const char* name, const char* value)
01614 {
01615   var_set(name, name + strlen(name), value, value + strlen(value));
01616 }
01617 
01618 
01619 static void var_set_int(const char* name, int value)
01620 {
01621   char buf[21];
01622   snprintf(buf, sizeof(buf), "%d", value);
01623   var_set_string(name, buf);
01624 }
01625 
01626 
01627 /*
01628   Store an integer (typically the returncode of the last SQL)
01629   statement in the drizzletest builtin variable $drizzleclient_errno
01630 */
01631 
01632 static void var_set_errno(int sql_errno)
01633 {
01634   var_set_int("$drizzleclient_errno", sql_errno);
01635 }
01636 
01637 
01638 /*
01639   Update $drizzleclient_get_server_version variable with version
01640   of the currently connected server
01641 */
01642 
01643 static void var_set_drizzleclient_get_server_version(drizzle_con_st *con)
01644 {
01645   var_set_int("$drizzle_con_server_version", drizzle_con_server_version_number(con));
01646 }
01647 
01648 
01649 /*
01650   Set variable from the result of a query
01651 
01652   SYNOPSIS
01653   var_query_set()
01654   var          variable to set from query
01655   query       start of query string to execute
01656   query_end   end of the query string to execute
01657 
01658 
01659   DESCRIPTION
01660   let @<var_name> = `<query>`
01661 
01662   Execute the query and assign the first row of result to var as
01663   a tab separated strings
01664 
01665   Also assign each column of the result set to
01666   variable "$<var_name>_<column_name>"
01667   Thus the tab separated output can be read from $<var_name> and
01668   and each individual column can be read as $<var_name>_<col_name>
01669 
01670 */
01671 
01672 static void dt_query(drizzle::connection_c& con, drizzle::result_c& res, const std::string& query)
01673 {
01674   if (drizzle_return_t ret= con.query(res, query))
01675   {
01676     if (ret == DRIZZLE_RETURN_ERROR_CODE)
01677     {
01678       die("Error running query '%s': %d %s", query.c_str(), res.error_code(), res.error());
01679     }
01680     else
01681     {
01682       die("Error running query '%s': %d %s", query.c_str(), ret, con.error());
01683     }
01684   }
01685 
01686   if (res.column_count() == 0)
01687   {
01688     die("Query '%s' didn't return a result set", query.c_str());
01689   }
01690 }
01691 
01692 static void var_query_set(VAR *var, const char *query, const char** query_end)
01693 {
01694   const char *end = ((query_end && *query_end) ? *query_end : query + strlen(query));
01695   drizzle::connection_c& con= *cur_con;
01696 
01697   while (end > query && *end != '`')
01698     --end;
01699   if (query == end)
01700     die("Syntax error in query, missing '`'");
01701   ++query;
01702 
01703   string ds_query;
01704   /* Eval the query, thus replacing all environment variables */
01705   do_eval(&ds_query, query, end, false);
01706 
01707   drizzle::result_c res;
01708   dt_query(con, res, ds_query);
01709 
01710   drizzle_row_t row= res.row_next();
01711   if (row && row[0])
01712   {
01713     /*
01714       Concatenate all fields in the first row with tab in between
01715       and assign that string to the $variable
01716     */
01717     string result;
01718     size_t* lengths= res.row_field_sizes();
01719     for (uint32_t i= 0; i < res.column_count(); i++)
01720     {
01721       if (row[i])
01722       {
01723         /* Add column to tab separated string */
01724         result.append(row[i], lengths[i]);
01725       }
01726       result += "\t";
01727     }
01728     end= result.c_str() + result.length() - 1;
01729     eval_expr(var, result.c_str(), (const char**) &end);
01730   }
01731   else
01732     eval_expr(var, "", 0);
01733 }
01734 
01735 
01736 /*
01737   Set variable from the result of a field in a query
01738 
01739   This function is useful when checking for a certain value
01740   in the output from a query that can't be restricted to only
01741   return some values. A very good example of that is most SHOW
01742   commands.
01743 
01744   SYNOPSIS
01745   var_set_query_get_value()
01746 
01747   DESCRIPTION
01748   let $variable= query_get_value(<query to run>,<column name>,<row no>);
01749 
01750   <query to run> -    The query that should be sent to the server
01751   <column name> -     Name of the column that holds the field be compared
01752   against the expected value
01753   <row no> -          Number of the row that holds the field to be
01754   compared against the expected value
01755 
01756 */
01757 
01758 static void var_set_query_get_value(st_command* command, VAR *var)
01759 {
01760   int col_no= -1;
01761   drizzle::connection_c& con= *cur_con;
01762 
01763   string ds_query;
01764   string ds_col;
01765   string ds_row;
01766   const struct command_arg query_get_value_args[] = {
01767     {"query", ARG_STRING, true, &ds_query, "Query to run"},
01768     {"column name", ARG_STRING, true, &ds_col, "Name of column"},
01769     {"row number", ARG_STRING, true, &ds_row, "Number for row"}
01770   };
01771 
01772 
01773 
01774   strip_parentheses(command);
01775   check_command_args(command, command->first_argument, query_get_value_args,
01776                      sizeof(query_get_value_args)/sizeof(struct command_arg),
01777                      ',');
01778 
01779   /* Convert row number to int */
01780   long row_no= atoi(ds_row.c_str());
01781   
01782   istringstream buff(ds_row);
01783   if ((buff >> row_no).fail())
01784     die("Invalid row number: '%s'", ds_row.c_str());
01785 
01786   /* Remove any surrounding "'s from the query - if there is any */
01787   // (Don't get me started on this)
01788   char* unstripped_query= strdup(ds_query.c_str());
01789   if (strip_surrounding(unstripped_query, '"', '"'))
01790     die("Mismatched \"'s around query '%s'", ds_query.c_str());
01791   ds_query= unstripped_query;
01792 
01793   drizzle::result_c res;
01794   dt_query(con, res, ds_query);
01795 
01796   {
01797     /* Find column number from the given column name */
01798     uint32_t num_fields= res.column_count();
01799     for (uint32_t i= 0; i < num_fields; i++)
01800     {
01801       drizzle_column_st* column= res.column_next();
01802       if (strcmp(drizzle_column_name(column), ds_col.c_str()) == 0 &&
01803           strlen(drizzle_column_name(column)) == ds_col.length())
01804       {
01805         col_no= i;
01806         break;
01807       }
01808     }
01809     if (col_no == -1)
01810     {
01811       die("Could not find column '%s' in the result of '%s'", ds_col.c_str(), ds_query.c_str());
01812     }
01813   }
01814 
01815   {
01816     /* Get the value */
01817     long rows= 0;
01818     const char* value= "No such row";
01819 
01820     while (drizzle_row_t row= res.row_next())
01821     {
01822       if (++rows == row_no)
01823       {
01824         /* Found the row to get */
01825         value= row[col_no] ? row[col_no] : "NULL";
01826         break;
01827       }
01828     }
01829     eval_expr(var, value, 0);
01830   }
01831 }
01832 
01833 
01834 static void var_copy(VAR *dest, VAR *src)
01835 {
01836   dest->int_val= src->int_val;
01837   dest->int_dirty= src->int_dirty;
01838 
01839   /* Alloc/realloc data for str_val in dest */
01840   if (dest->alloced_len < src->alloced_len)
01841   {
01842     char *tmpptr= (char *)realloc(dest->str_val, src->alloced_len);
01843     dest->str_val= tmpptr;
01844   }
01845   else
01846     dest->alloced_len= src->alloced_len;
01847 
01848   /* Copy str_val data to dest */
01849   dest->str_val_len= src->str_val_len;
01850   if (src->str_val_len)
01851     memcpy(dest->str_val, src->str_val, src->str_val_len);
01852 }
01853 
01854 
01855 void eval_expr(VAR *v, const char *p, const char **p_end)
01856 {
01857   if (*p == '$')
01858   {
01859     VAR *vp= var_get(p, p_end, 0, 0);
01860     if (vp)
01861       var_copy(v, vp);
01862     return;
01863   }
01864 
01865   if (*p == '`')
01866   {
01867     var_query_set(v, p, p_end);
01868     return;
01869   }
01870 
01871   {
01872     /* Check if this is a "let $var= query_get_value()" */
01873     const char* get_value_str= "query_get_value";
01874     const size_t len= strlen(get_value_str);
01875     if (strncmp(p, get_value_str, len)==0)
01876     {
01877       st_command command;
01878       command.query= (char*)p;
01879       command.first_word_len= len;
01880       command.first_argument= command.query + len;
01881       command.end= (char*)*p_end;
01882       var_set_query_get_value(&command, v);
01883       return;
01884     }
01885   }
01886 
01887   {
01888     int new_val_len = (p_end && *p_end) ?
01889       (int) (*p_end - p) : (int) strlen(p);
01890     if (new_val_len + 1 >= v->alloced_len)
01891     {
01892       static int MIN_VAR_ALLOC= 32;
01893       v->alloced_len = (new_val_len < MIN_VAR_ALLOC - 1) ?
01894         MIN_VAR_ALLOC : new_val_len + 1;
01895       char *tmpptr= (char *)realloc(v->str_val, v->alloced_len+1);
01896       v->str_val= tmpptr;
01897     }
01898     v->str_val_len = new_val_len;
01899     memcpy(v->str_val, p, new_val_len);
01900     v->str_val[new_val_len] = 0;
01901     v->int_val=atoi(p);
01902     v->int_dirty=0;
01903   }
01904   return;
01905 }
01906 
01907 
01908 static void open_file(const char *name)
01909 {
01910   char buff[FN_REFLEN];
01911 
01912   if (!internal::test_if_hard_path(name))
01913   {
01914     snprintf(buff, sizeof(buff), "%s%s",opt_basedir.c_str(),name);
01915     name=buff;
01916   }
01917   internal::fn_format(buff, name, "", "", MY_UNPACK_FILENAME);
01918 
01919   cur_file++;
01920   if (cur_file == &*file_stack.end())
01921     die("Source directives are nesting too deep");
01922   if (!(cur_file->file= fopen(buff, "r")))
01923   {
01924     cur_file--;
01925     die("Could not open '%s' for reading", buff);
01926   }
01927   cur_file->file_name= strdup(buff);
01928   cur_file->lineno=1;
01929 }
01930 
01931 
01932 /*
01933   Source and execute the given file
01934 
01935   SYNOPSIS
01936   do_source()
01937   query  called command
01938 
01939   DESCRIPTION
01940   source <file_name>
01941 
01942   Open the file <file_name> and execute it
01943 
01944 */
01945 
01946 static void do_source(st_command* command)
01947 {
01948   string ds_filename;
01949   const struct command_arg source_args[] = {
01950     { "filename", ARG_STRING, true, &ds_filename, "File to source" }
01951   };
01952 
01953 
01954   check_command_args(command, command->first_argument, source_args,
01955                      sizeof(source_args)/sizeof(struct command_arg),
01956                      ' ');
01957 
01958   /*
01959     If this file has already been sourced, don't source it again.
01960     It's already available in the q_lines cache.
01961   */
01962   if (parser.current_line < (parser.read_lines - 1))
01963     ; /* Do nothing */
01964   else
01965   {
01966     if (! opt_testdir.empty())
01967     {
01968       string testdir(opt_testdir);
01969       if (testdir[testdir.length()] != '/')
01970         testdir += "/";
01971       testdir += ds_filename;
01972       ds_filename.swap(testdir);
01973     }
01974     open_file(ds_filename.c_str());
01975   }
01976 }
01977 
01978 
01979 static void init_builtin_echo()
01980 {
01981   builtin_echo[0]= 0;
01982 }
01983 
01984 
01985 /*
01986   Replace a substring
01987 
01988   SYNOPSIS
01989   replace
01990   ds_str      The string to search and perform the replace in
01991   search_str  The string to search for
01992   search_len  Length of the string to search for
01993   replace_str The string to replace with
01994   replace_len Length of the string to replace with
01995 
01996   RETURN
01997   0 String replaced
01998   1 Could not find search_str in str
01999 */
02000 
02001 static int replace(string *ds_str,
02002                    const char *search_str, uint32_t search_len,
02003                    const char *replace_str, uint32_t replace_len)
02004 {
02005   string ds_tmp;
02006   const char *start= strstr(ds_str->c_str(), search_str);
02007   if (!start)
02008     return 1;
02009   ds_tmp.append(ds_str->c_str(), start - ds_str->c_str());
02010   ds_tmp.append(replace_str, replace_len);
02011   ds_tmp.append(start + search_len);
02012   *ds_str= ds_tmp;
02013   return 0;
02014 }
02015 
02016 
02017 /*
02018   Execute given command.
02019 
02020   SYNOPSIS
02021   do_exec()
02022   query  called command
02023 
02024   DESCRIPTION
02025   exec <command>
02026 
02027   Execute the text between exec and end of line in a subprocess.
02028   The error code returned from the subprocess is checked against the
02029   expected error array, previously set with the --error command.
02030   It can thus be used to execute a command that shall fail.
02031 
02032   NOTE
02033   Although drizzletest is executed from cygwin shell, the command will be
02034   executed in "cmd.exe". Thus commands like "rm" etc can NOT be used, use
02035   drizzletest commmand(s) like "remove_file" for that
02036 */
02037 
02038 static void do_exec(st_command* command)
02039 {
02040   int error;
02041   char buf[512];
02042   FILE *res_file;
02043   char *cmd= command->first_argument;
02044   string ds_cmd;
02045 
02046   /* Skip leading space */
02047   while (*cmd && charset_info->isspace(*cmd))
02048     cmd++;
02049   if (!*cmd)
02050     die("Missing argument in exec");
02051   command->last_argument= command->end;
02052 
02053   /* Eval the command, thus replacing all environment variables */
02054   do_eval(&ds_cmd, cmd, command->end, !is_windows);
02055 
02056   /* Check if echo should be replaced with "builtin" echo */
02057   if (builtin_echo[0] && strncmp(cmd, "echo", 4) == 0)
02058   {
02059     /* Replace echo with our "builtin" echo */
02060     replace(&ds_cmd, "echo", 4, builtin_echo, strlen(builtin_echo));
02061   }
02062 
02063   if (!(res_file= popen(ds_cmd.c_str(), "r")) && command->abort_on_error)
02064   {
02065     die("popen(\"%s\", \"r\") failed", command->first_argument);
02066   }
02067 
02068   while (fgets(buf, sizeof(buf), res_file))
02069   {
02070     if (disable_result_log)
02071     {
02072       buf[strlen(buf)-1]=0;
02073     }
02074     else
02075     {
02076       replace_append(&ds_res, buf);
02077     }
02078   }
02079   error= pclose(res_file);
02080   if (error > 0)
02081   {
02082     uint32_t status= WEXITSTATUS(error), i;
02083     bool ok= 0;
02084 
02085     if (command->abort_on_error)
02086     {
02087       log_msg("exec of '%s' failed, error: %d, status: %d, errno: %d", ds_cmd.c_str(), error, status, errno);
02088       die("command \"%s\" failed", command->first_argument);
02089     }
02090 
02091     for (i= 0; i < command->expected_errors.count; i++)
02092     {
02093       if ((command->expected_errors.err[i].type == ERR_ERRNO) &&
02094           (command->expected_errors.err[i].code.errnum == status))
02095       {
02096         ok= 1;
02097       }
02098     }
02099     if (!ok)
02100     {
02101       die("command \"%s\" failed with wrong error: %d",
02102           command->first_argument, status);
02103     }
02104   }
02105   else if (command->expected_errors.err[0].type == ERR_ERRNO &&
02106            command->expected_errors.err[0].code.errnum != 0)
02107   {
02108     /* Error code we wanted was != 0, i.e. not an expected success */
02109     log_msg("exec of '%s failed, error: %d, errno: %d", ds_cmd.c_str(), error, errno);
02110     die("command \"%s\" succeeded - should have failed with errno %d...",
02111         command->first_argument, command->expected_errors.err[0].code.errnum);
02112   }
02113 
02114   return;
02115 }
02116 
02117 enum enum_operator
02118 {
02119   DO_DEC,
02120   DO_INC
02121 };
02122 
02123 
02124 /*
02125   Decrease or increase the value of a variable
02126 
02127   SYNOPSIS
02128   do_modify_var()
02129   query  called command
02130   operator    operation to perform on the var
02131 
02132   DESCRIPTION
02133   dec $var_name
02134   inc $var_name
02135 
02136 */
02137 
02138 static int do_modify_var(st_command* command,
02139                          enum enum_operator op)
02140 {
02141   const char *p= command->first_argument;
02142   VAR* v;
02143   if (!*p)
02144     die("Missing argument to %.*s", command->first_word_len, command->query);
02145   if (*p != '$')
02146     die("The argument to %.*s must be a variable (start with $)",
02147         command->first_word_len, command->query);
02148   v= var_get(p, &p, 1, 0);
02149   switch (op) {
02150   case DO_DEC:
02151     v->int_val--;
02152     break;
02153   case DO_INC:
02154     v->int_val++;
02155     break;
02156   default:
02157     die("Invalid operator to do_modify_var");
02158     break;
02159   }
02160   v->int_dirty= 1;
02161   command->last_argument= (char*)++p;
02162   return 0;
02163 }
02164 
02165 
02166 /*
02167   SYNOPSIS
02168   do_system
02169   command  called command
02170 
02171   DESCRIPTION
02172   system <command>
02173 
02174   Eval the query to expand any $variables in the command.
02175   Execute the command with the "system" command.
02176 
02177 */
02178 
02179 static void do_system(st_command* command)
02180 {
02181   string ds_cmd;
02182 
02183 
02184   if (strlen(command->first_argument) == 0)
02185     die("Missing arguments to system, nothing to do!");
02186 
02187   /* Eval the system command, thus replacing all environment variables */
02188   do_eval(&ds_cmd, command->first_argument, command->end, !is_windows);
02189 
02190   if (system(ds_cmd.c_str()))
02191   {
02192     if (command->abort_on_error)
02193       die("system command '%s' failed", command->first_argument);
02194 
02195     /* If ! abort_on_error, log message and continue */
02196     ds_res += "system command '";
02197     replace_append(&ds_res, command->first_argument);
02198     ds_res += "' failed\n";
02199   }
02200 
02201   command->last_argument= command->end;
02202   return;
02203 }
02204 
02205 
02206 /*
02207   SYNOPSIS
02208   do_remove_file
02209   command  called command
02210 
02211   DESCRIPTION
02212   remove_file <file_name>
02213   Remove the file <file_name>
02214 */
02215 
02216 static void do_remove_file(st_command* command)
02217 {
02218   string ds_filename;
02219   const struct command_arg rm_args[] = {
02220     { "filename", ARG_STRING, true, &ds_filename, "File to delete" }
02221   };
02222 
02223 
02224   check_command_args(command, command->first_argument,
02225                      rm_args, sizeof(rm_args)/sizeof(struct command_arg),
02226                      ' ');
02227 
02228   int error= internal::my_delete(ds_filename.c_str(), MYF(0)) != 0;
02229   handle_command_error(command, error);
02230 }
02231 
02232 
02233 /*
02234   SYNOPSIS
02235   do_copy_file
02236   command  command handle
02237 
02238   DESCRIPTION
02239   copy_file <from_file> <to_file>
02240   Copy <from_file> to <to_file>
02241 
02242   NOTE! Will fail if <to_file> exists
02243 */
02244 
02245 static void do_copy_file(st_command* command)
02246 {
02247   string ds_from_file;
02248   string ds_to_file;
02249   const struct command_arg copy_file_args[] = {
02250     { "from_file", ARG_STRING, true, &ds_from_file, "Filename to copy from" },
02251     { "to_file", ARG_STRING, true, &ds_to_file, "Filename to copy to" }
02252   };
02253 
02254 
02255   check_command_args(command, command->first_argument,
02256                      copy_file_args,
02257                      sizeof(copy_file_args)/sizeof(struct command_arg),
02258                      ' ');
02259 
02260   int error= (internal::my_copy(ds_from_file.c_str(), ds_to_file.c_str(),
02261                   MYF(MY_DONT_OVERWRITE_FILE)) != 0);
02262   handle_command_error(command, error);
02263 }
02264 
02265 
02266 /*
02267   SYNOPSIS
02268   do_chmod_file
02269   command  command handle
02270 
02271   DESCRIPTION
02272   chmod <octal> <file_name>
02273   Change file permission of <file_name>
02274 
02275 */
02276 
02277 static void do_chmod_file(st_command* command)
02278 {
02279   long mode= 0;
02280   string ds_mode;
02281   string ds_file;
02282   const struct command_arg chmod_file_args[] = {
02283     { "mode", ARG_STRING, true, &ds_mode, "Mode of file(octal) ex. 0660"},
02284     { "filename", ARG_STRING, true, &ds_file, "Filename of file to modify" }
02285   };
02286 
02287 
02288   check_command_args(command, command->first_argument,
02289                      chmod_file_args,
02290                      sizeof(chmod_file_args)/sizeof(struct command_arg),
02291                      ' ');
02292 
02293   /* Parse what mode to set */
02294   istringstream buff(ds_mode);
02295   if (ds_mode.length() != 4 ||
02296       (buff >> oct >> mode).fail())
02297     die("You must write a 4 digit octal number for mode");
02298 
02299   handle_command_error(command, chmod(ds_file.c_str(), mode));
02300 }
02301 
02302 
02303 /*
02304   SYNOPSIS
02305   do_file_exists
02306   command  called command
02307 
02308   DESCRIPTION
02309   fiile_exist <file_name>
02310   Check if file <file_name> exists
02311 */
02312 
02313 static void do_file_exist(st_command* command)
02314 {
02315   string ds_filename;
02316   const struct command_arg file_exist_args[] = {
02317     { "filename", ARG_STRING, true, &ds_filename, "File to check if it exist" }
02318   };
02319 
02320 
02321   check_command_args(command, command->first_argument,
02322                      file_exist_args,
02323                      sizeof(file_exist_args)/sizeof(struct command_arg),
02324                      ' ');
02325 
02326   int error= access(ds_filename.c_str(), F_OK) != 0;
02327   handle_command_error(command, error);
02328 }
02329 
02330 
02331 /*
02332   SYNOPSIS
02333   do_mkdir
02334   command  called command
02335 
02336   DESCRIPTION
02337   mkdir <dir_name>
02338   Create the directory <dir_name>
02339 */
02340 
02341 static void do_mkdir(st_command* command)
02342 {
02343   string ds_dirname;
02344   const struct command_arg mkdir_args[] = {
02345     {"dirname", ARG_STRING, true, &ds_dirname, "Directory to create"}
02346   };
02347 
02348 
02349   check_command_args(command, command->first_argument,
02350                      mkdir_args, sizeof(mkdir_args)/sizeof(struct command_arg),
02351                      ' ');
02352 
02353   int error= mkdir(ds_dirname.c_str(), (0777 & internal::my_umask_dir)) != 0;
02354   handle_command_error(command, error);
02355 }
02356 
02357 /*
02358   SYNOPSIS
02359   do_rmdir
02360   command  called command
02361 
02362   DESCRIPTION
02363   rmdir <dir_name>
02364   Remove the empty directory <dir_name>
02365 */
02366 
02367 static void do_rmdir(st_command* command)
02368 {
02369   string ds_dirname;
02370   const struct command_arg rmdir_args[] = {
02371     {"dirname", ARG_STRING, true, &ds_dirname, "Directory to remove"}
02372   };
02373 
02374 
02375   check_command_args(command, command->first_argument,
02376                      rmdir_args, sizeof(rmdir_args)/sizeof(struct command_arg),
02377                      ' ');
02378 
02379   int error= rmdir(ds_dirname.c_str()) != 0;
02380   handle_command_error(command, error);
02381 }
02382 
02383 
02384 /*
02385   Read characters from line buffer or file. This is needed to allow
02386   my_ungetc() to buffer MAX_DELIMITER_LENGTH characters for a file
02387 
02388   NOTE:
02389   This works as long as one doesn't change files (with 'source file_name')
02390   when there is things pushed into the buffer.  This should however not
02391   happen for any tests in the test suite.
02392 */
02393 
02394 static int my_getc(FILE *file)
02395 {
02396   if (line_buffer_pos == line_buffer)
02397     return fgetc(file);
02398   return *--line_buffer_pos;
02399 }
02400 
02401 
02402 static void my_ungetc(int c)
02403 {
02404   *line_buffer_pos++= (char) c;
02405 }
02406 
02407 
02408 static void read_until_delimiter(string *ds,
02409                                  string *ds_delimiter)
02410 {
02411   if (ds_delimiter->length() > MAX_DELIMITER_LENGTH)
02412     die("Max delimiter length(%d) exceeded", MAX_DELIMITER_LENGTH);
02413 
02414   /* Read from file until delimiter is found */
02415   while (1)
02416   {
02417     char c= my_getc(cur_file->file);
02418 
02419     if (c == '\n')
02420     {
02421       cur_file->lineno++;
02422 
02423       /* Skip newline from the same line as the command */
02424       if (start_lineno == (cur_file->lineno - 1))
02425         continue;
02426     }
02427     else if (start_lineno == cur_file->lineno)
02428     {
02429       /*
02430         No characters except \n are allowed on
02431         the same line as the command
02432       */
02433       die("Trailing characters found after command");
02434     }
02435 
02436     if (feof(cur_file->file))
02437       die("End of file encountered before '%s' delimiter was found",
02438           ds_delimiter->c_str());
02439 
02440     if (match_delimiter(c, ds_delimiter->c_str(), ds_delimiter->length()))
02441       break;
02442 
02443     ds->push_back(c);
02444   }
02445 }
02446 
02447 
02448 static void do_write_file_command(st_command* command, bool append)
02449 {
02450   string ds_content;
02451   string ds_filename;
02452   string ds_delimiter;
02453   const struct command_arg write_file_args[] = {
02454     { "filename", ARG_STRING, true, &ds_filename, "File to write to" },
02455     { "delimiter", ARG_STRING, false, &ds_delimiter, "Delimiter to read until" }
02456   };
02457 
02458 
02459   check_command_args(command,
02460                      command->first_argument,
02461                      write_file_args,
02462                      sizeof(write_file_args)/sizeof(struct command_arg),
02463                      ' ');
02464 
02465   /* If no delimiter was provided, use EOF */
02466   if (ds_delimiter.length() == 0)
02467     ds_delimiter += "EOF";
02468 
02469   if (!append && access(ds_filename.c_str(), F_OK) == 0)
02470   {
02471     /* The file should not be overwritten */
02472     die("File already exist: '%s'", ds_filename.c_str());
02473   }
02474 
02475   read_until_delimiter(&ds_content, &ds_delimiter);
02476   str_to_file2(ds_filename.c_str(), ds_content.c_str(), ds_content.length(), append);
02477 }
02478 
02479 
02480 /*
02481   SYNOPSIS
02482   do_write_file
02483   command  called command
02484 
02485   DESCRIPTION
02486   write_file <file_name> [<delimiter>];
02487   <what to write line 1>
02488   <...>
02489   < what to write line n>
02490   EOF
02491 
02492   --write_file <file_name>;
02493   <what to write line 1>
02494   <...>
02495   < what to write line n>
02496   EOF
02497 
02498   Write everything between the "write_file" command and 'delimiter'
02499   to "file_name"
02500 
02501   NOTE! Will fail if <file_name> exists
02502 
02503   Default <delimiter> is EOF
02504 
02505 */
02506 
02507 static void do_write_file(st_command* command)
02508 {
02509   do_write_file_command(command, false);
02510 }
02511 
02512 
02513 /*
02514   SYNOPSIS
02515   do_append_file
02516   command  called command
02517 
02518   DESCRIPTION
02519   append_file <file_name> [<delimiter>];
02520   <what to write line 1>
02521   <...>
02522   < what to write line n>
02523   EOF
02524 
02525   --append_file <file_name>;
02526   <what to write line 1>
02527   <...>
02528   < what to write line n>
02529   EOF
02530 
02531   Append everything between the "append_file" command
02532   and 'delimiter' to "file_name"
02533 
02534   Default <delimiter> is EOF
02535 
02536 */
02537 
02538 static void do_append_file(st_command* command)
02539 {
02540   do_write_file_command(command, true);
02541 }
02542 
02543 
02544 /*
02545   SYNOPSIS
02546   do_cat_file
02547   command  called command
02548 
02549   DESCRIPTION
02550   cat_file <file_name>;
02551 
02552   Print the given file to result log
02553 
02554 */
02555 
02556 static void do_cat_file(st_command* command)
02557 {
02558   static string ds_filename;
02559   const struct command_arg cat_file_args[] = {
02560     { "filename", ARG_STRING, true, &ds_filename, "File to read from" }
02561   };
02562 
02563 
02564   check_command_args(command,
02565                      command->first_argument,
02566                      cat_file_args,
02567                      sizeof(cat_file_args)/sizeof(struct command_arg),
02568                      ' ');
02569 
02570   cat_file(ds_res, ds_filename.c_str());
02571 }
02572 
02573 
02574 /*
02575   SYNOPSIS
02576   do_diff_files
02577   command  called command
02578 
02579   DESCRIPTION
02580   diff_files <file1> <file2>;
02581 
02582   Fails if the two files differ.
02583 
02584 */
02585 
02586 static void do_diff_files(st_command* command)
02587 {
02588   string ds_filename;
02589   string ds_filename2;
02590   const struct command_arg diff_file_args[] = {
02591     { "file1", ARG_STRING, true, &ds_filename, "First file to diff" },
02592     { "file2", ARG_STRING, true, &ds_filename2, "Second file to diff" }
02593   };
02594 
02595 
02596   check_command_args(command,
02597                      command->first_argument,
02598                      diff_file_args,
02599                      sizeof(diff_file_args)/sizeof(struct command_arg),
02600                      ' ');
02601 
02602   int error= compare_files(ds_filename.c_str(), ds_filename2.c_str());
02603   if (error)
02604   {
02605     /* Compare of the two files failed, append them to output
02606        so the failure can be analyzed
02607     */
02608     show_diff(&ds_res, ds_filename.c_str(), ds_filename2.c_str());
02609   }
02610 
02611   handle_command_error(command, error);
02612 }
02613 
02614 /*
02615   SYNOPSIS
02616   do_send_quit
02617   command  called command
02618 
02619   DESCRIPTION
02620   Sends a simple quit command to the server for the named connection.
02621 
02622 */
02623 
02624 static void do_send_quit(st_command* command)
02625 {
02626   char* p= command->first_argument;
02627 
02628   if (not *p)
02629     die("Missing connection name in send_quit");
02630   char* name= p;
02631   while (*p && !charset_info->isspace(*p))
02632     p++;
02633 
02634   if (*p)
02635     *p++= 0;
02636   command->last_argument= p;
02637 
02638   st_connection* con= find_ptr2(g_connections, name);
02639   if (not con)
02640     die("connection '%s' not found in connection pool", name);
02641 
02642   drizzle::result_c result;
02643   drizzle_return_t ret;
02644   drizzle_quit(*con, result, &ret);
02645 }
02646 
02647 
02648 /*
02649   SYNOPSIS
02650   do_change_user
02651   command       called command
02652 
02653   DESCRIPTION
02654   change_user [<user>], [<passwd>], [<db>]
02655   <user> - user to change to
02656   <passwd> - user password
02657   <db> - default database
02658 
02659   Changes the user and causes the database specified by db to become
02660   the default (current) database for the the current connection.
02661 
02662 */
02663 
02664 static void do_change_user(st_command *)
02665 {
02666   assert(0);
02667 }
02668 
02669 /*
02670   SYNOPSIS
02671   do_perl
02672   command  command handle
02673 
02674   DESCRIPTION
02675   perl [<delimiter>];
02676   <perlscript line 1>
02677   <...>
02678   <perlscript line n>
02679   EOF
02680 
02681   Execute everything after "perl" until <delimiter> as perl.
02682   Useful for doing more advanced things
02683   but still being able to execute it on all platforms.
02684 
02685   Default <delimiter> is EOF
02686 */
02687 
02688 static void do_perl(st_command* command)
02689 {
02690   char buf[FN_REFLEN];
02691   char temp_file_path[FN_REFLEN];
02692   string ds_script;
02693   string ds_delimiter;
02694   const command_arg perl_args[] = {
02695     { "delimiter", ARG_STRING, false, &ds_delimiter, "Delimiter to read until" }
02696   };
02697 
02698 
02699   check_command_args(command,
02700                      command->first_argument,
02701                      perl_args,
02702                      sizeof(perl_args)/sizeof(struct command_arg),
02703                      ' ');
02704 
02705   /* If no delimiter was provided, use EOF */
02706   if (ds_delimiter.length() == 0)
02707     ds_delimiter += "EOF";
02708 
02709   read_until_delimiter(&ds_script, &ds_delimiter);
02710 
02711   /* Create temporary file name */
02712   int fd= internal::create_temp_file(temp_file_path, getenv("MYSQLTEST_VARDIR"), "tmp", MYF(MY_WME));
02713   if (fd < 0)
02714     die("Failed to create temporary file for perl command");
02715   internal::my_close(fd, MYF(0));
02716 
02717   str_to_file(temp_file_path, ds_script.c_str(), ds_script.length());
02718 
02719   /* Format the "perl <filename>" command */
02720   snprintf(buf, sizeof(buf), "perl %s", temp_file_path);
02721 
02722   FILE* res_file= popen(buf, "r");
02723   if (not res_file && command->abort_on_error)
02724     die("popen(\"%s\", \"r\") failed", buf);
02725 
02726   while (fgets(buf, sizeof(buf), res_file))
02727   {
02728     if (disable_result_log)
02729       buf[strlen(buf)-1]=0;
02730     else
02731       replace_append(&ds_res, buf);
02732   }
02733   int error= pclose(res_file);
02734 
02735   /* Remove the temporary file */
02736   internal::my_delete(temp_file_path, MYF(0));
02737 
02738   handle_command_error(command, WEXITSTATUS(error));
02739 }
02740 
02741 
02742 /*
02743   Print the content between echo and <delimiter> to result file.
02744   Evaluate all variables in the string before printing, allow
02745   for variable names to be escaped using        \
02746 
02747   SYNOPSIS
02748   do_echo()
02749   command  called command
02750 
02751   DESCRIPTION
02752   echo text
02753   Print the text after echo until end of command to result file
02754 
02755   echo $<var_name>
02756   Print the content of the variable <var_name> to result file
02757 
02758   echo Some text $<var_name>
02759   Print "Some text" plus the content of the variable <var_name> to
02760   result file
02761 
02762   echo Some text \$<var_name>
02763   Print "Some text" plus $<var_name> to result file
02764 */
02765 
02766 static void do_echo(st_command* command)
02767 {
02768   string ds_echo;
02769   do_eval(&ds_echo, command->first_argument, command->end, false);
02770   ds_res += ds_echo;
02771   ds_res += "\n";
02772   command->last_argument= command->end;
02773 }
02774 
02775 static void do_wait_for_slave_to_stop()
02776 {
02777   static int SLAVE_POLL_INTERVAL= 300000;
02778   drizzle::connection_c& con= *cur_con;
02779   for (;;)
02780   {
02781     drizzle::result_c res;
02782     dt_query(con, res, "show status like 'Slave_running'");
02783     drizzle_row_t row= res.row_next();
02784     if (!row || !row[1])
02785     {
02786       die("Strange result from query while probing slave for stop");
02787     }
02788     if (!strcmp(row[1], "OFF"))
02789       break;
02790     usleep(SLAVE_POLL_INTERVAL);
02791   }
02792 }
02793 
02794 static void do_sync_with_master2(long offset)
02795 {
02796   drizzle::connection_c& con= *cur_con;
02797 
02798   if (!master_pos.file[0])
02799     die("Calling 'sync_with_master' without calling 'save_master_pos'");
02800 
02801   char query_buf[FN_REFLEN+128];
02802   snprintf(query_buf, sizeof(query_buf), "select master_pos_wait('%s', %ld)", master_pos.file, master_pos.pos + offset);
02803   for (int tries= 0; tries < 30; tries++)
02804   {
02805     drizzle::result_c res;
02806     dt_query(con, res, query_buf);
02807 
02808     drizzle_row_t row= res.row_next();
02809     if (not row)
02810       die("empty result in %s", query_buf);
02811     if (row[0])
02812       return;
02813     /*
02814     It may be that the slave SQL thread has not started yet, though START
02815     SLAVE has been issued ?
02816     */
02817     sleep(1); /* So at most we will wait 30 seconds and make 31 tries */
02818   }
02819   show_query(con, "SHOW MASTER STATUS");
02820   show_query(con, "SHOW SLAVE STATUS");
02821   die("could not sync with master ('%s' returned NULL)", query_buf);
02822 }
02823 
02824 static void do_sync_with_master(st_command* command)
02825 {
02826   long offset= 0;
02827   char *p= command->first_argument;
02828   const char *offset_start= p;
02829   if (*offset_start)
02830   {
02831     for (; charset_info->isdigit(*p); p++)
02832       offset = offset * 10 + *p - '0';
02833 
02834     if(*p && !charset_info->isspace(*p))
02835       die("Invalid integer argument \"%s\"", offset_start);
02836     command->last_argument= p;
02837   }
02838   do_sync_with_master2(offset);
02839   return;
02840 }
02841 
02842 
02843 /*
02844   when ndb binlog is on, this call will wait until last updated epoch
02845   (locally in the drizzled) has been received into the binlog
02846 */
02847 static void do_save_master_pos()
02848 {
02849   drizzle::result_c res;
02850   dt_query(*cur_con, res, "show master status");
02851   drizzle_row_t row= res.row_next();
02852   if (!row)
02853     die("empty result in show master status");
02854   strncpy(master_pos.file, row[0], sizeof(master_pos.file)-1);
02855   master_pos.pos = strtoul(row[1], (char**) 0, 10);
02856 }
02857 
02858 
02859 /*
02860   Assign the variable <var_name> with <var_val>
02861 
02862   SYNOPSIS
02863   do_let()
02864   query  called command
02865 
02866   DESCRIPTION
02867   let $<var_name>=<var_val><delimiter>
02868 
02869   <var_name>  - is the string string found between the $ and =
02870   <var_val>   - is the content between the = and <delimiter>, it may span
02871   multiple line and contain any characters except <delimiter>
02872   <delimiter> - is a string containing of one or more chars, default is ;
02873 
02874   RETURN VALUES
02875   Program will die if error detected
02876 */
02877 
02878 static void do_let(st_command* command)
02879 {
02880   char *p= command->first_argument;
02881   char *var_name, *var_name_end;
02882   string let_rhs_expr;
02883 
02884 
02885   /* Find <var_name> */
02886   if (!*p)
02887     die("Missing arguments to let");
02888   var_name= p;
02889   while (*p && (*p != '=') && !charset_info->isspace(*p))
02890     p++;
02891   var_name_end= p;
02892   if (var_name == var_name_end ||
02893       (var_name+1 == var_name_end && *var_name == '$'))
02894     die("Missing variable name in let");
02895   while (charset_info->isspace(*p))
02896     p++;
02897   if (*p++ != '=')
02898     die("Missing assignment operator in let");
02899 
02900   /* Find start of <var_val> */
02901   while (*p && charset_info->isspace(*p))
02902     p++;
02903 
02904   do_eval(&let_rhs_expr, p, command->end, false);
02905 
02906   command->last_argument= command->end;
02907   /* Assign var_val to var_name */
02908   var_set(var_name, var_name_end, let_rhs_expr.c_str(),
02909           (let_rhs_expr.c_str() + let_rhs_expr.length()));
02910   return;
02911 }
02912 
02913 
02914 /*
02915   Sleep the number of specified seconds
02916 
02917   SYNOPSIS
02918   do_sleep()
02919   q         called command
02920   real_sleep   use the value from opt_sleep as number of seconds to sleep
02921   if real_sleep is false
02922 
02923   DESCRIPTION
02924   sleep <seconds>
02925   real_sleep <seconds>
02926 
02927   The difference between the sleep and real_sleep commands is that sleep
02928   uses the delay from the --sleep command-line option if there is one.
02929   (If the --sleep option is not given, the sleep command uses the delay
02930   specified by its argument.) The real_sleep command always uses the
02931   delay specified by its argument.  The logic is that sometimes delays are
02932   cpu-dependent, and --sleep can be used to set this delay.  real_sleep is
02933   used for cpu-independent delays.
02934 */
02935 
02936 static void do_sleep(st_command* command, bool real_sleep)
02937 {
02938   char *p= command->first_argument;
02939   char *sleep_start, *sleep_end= command->end;
02940   double sleep_val= 0;
02941 
02942   while (charset_info->isspace(*p))
02943     p++;
02944   if (!*p)
02945     die("Missing argument to %.*s", command->first_word_len, command->query);
02946   sleep_start= p;
02947   /* Check that arg starts with a digit, not handled by internal::my_strtod */
02948   if (!charset_info->isdigit(*sleep_start))
02949     die("Invalid argument to %.*s \"%s\"", command->first_word_len,
02950         command->query,command->first_argument);
02951   string buff_str(sleep_start, sleep_end-sleep_start);
02952   istringstream buff(buff_str);
02953   buff >> sleep_val;
02954   if (buff.fail())
02955     die("Invalid argument to %.*s \"%s\"", command->first_word_len, command->query, command->first_argument);
02956 
02957   /* Fixed sleep time selected by --sleep option */
02958   if (opt_sleep >= 0 && !real_sleep)
02959     sleep_val= opt_sleep;
02960 
02961   if (sleep_val)
02962     usleep(sleep_val * 1000000);
02963   command->last_argument= sleep_end;
02964 }
02965 
02966 
02967 static void do_get_file_name(st_command* command, string &dest)
02968 {
02969   char *p= command->first_argument;
02970   if (!*p)
02971     die("Missing file name argument");
02972   char *name= p;
02973   while (*p && !charset_info->isspace(*p))
02974     p++;
02975   if (*p)
02976     *p++= 0;
02977   command->last_argument= p;
02978   if (! opt_testdir.empty())
02979   {
02980     dest= opt_testdir;
02981     if (dest[dest.length()] != '/')
02982       dest += "/";
02983   }
02984   dest.append(name);
02985 }
02986 
02987 
02988 static void do_set_charset(st_command* command)
02989 {
02990   char *charset_name= command->first_argument;
02991   char *p;
02992 
02993   if (!charset_name || !*charset_name)
02994     die("Missing charset name in 'character_set'");
02995   /* Remove end space */
02996   p= charset_name;
02997   while (*p && !charset_info->isspace(*p))
02998     p++;
02999   if(*p)
03000     *p++= 0;
03001   command->last_argument= p;
03002   charset_info= get_charset_by_csname(charset_name, MY_CS_PRIMARY);
03003   if (!charset_info)
03004     abort_not_supported_test("Test requires charset '%s'", charset_name);
03005 }
03006 
03007 static void fill_global_error_names()
03008 {
03009   drizzle::connection_c& con= *cur_con;
03010 
03011   global_error_names.clear();
03012 
03013   drizzle::result_c res;
03014   dt_query(con, res, "select error_name, error_code from data_dictionary.errors");
03015   while (drizzle_row_t row= res.row_next())
03016   {
03017     if (not row[0])
03018       break;
03019     /*
03020       Concatenate all fields in the first row with tab in between
03021       and assign that string to the $variable
03022     */
03023     size_t *lengths= res.row_field_sizes();
03024     try
03025     {
03026       global_error_names[string(row[0], lengths[0])] = boost::lexical_cast<uint32_t>(string(row[1], lengths[1]));
03027     }
03028     catch (boost::bad_lexical_cast &ex)
03029     {
03030       die("Invalid error_code from Drizzle: %s", ex.what());
03031     }
03032   }
03033 }
03034 
03035 static uint32_t get_errcode_from_name(const char *error_name, const char *error_end)
03036 {
03037   string error_name_s(error_name, error_end);
03038 
03039   if (ErrorCodes::mapped_type* ptr= find_ptr(global_error_names, error_name_s))
03040     return *ptr;
03041 
03042   die("Unknown SQL error name '%s'", error_name_s.c_str());
03043   return 0;
03044 }
03045 
03046 static void do_get_errcodes(st_command* command)
03047 {
03048   struct st_match_err *to= saved_expected_errors.err;
03049   char *p= command->first_argument;
03050   uint32_t count= 0;
03051 
03052 
03053 
03054   if (!*p)
03055     die("Missing argument(s) to 'error'");
03056 
03057   do
03058   {
03059     char *end;
03060 
03061     /* Skip leading spaces */
03062     while (*p && *p == ' ')
03063       p++;
03064 
03065     /* Find end */
03066     end= p;
03067     while (*end && *end != ',' && *end != ' ')
03068       end++;
03069 
03070     if (*p == 'S')
03071     {
03072       char *to_ptr= to->code.sqlstate;
03073 
03074       /*
03075         SQLSTATE string
03076         - Must be DRIZZLE_MAX_SQLSTATE_SIZE long
03077         - May contain only digits[0-9] and _uppercase_ letters
03078       */
03079       p++; /* Step past the S */
03080       if ((end - p) != DRIZZLE_MAX_SQLSTATE_SIZE)
03081         die("The sqlstate must be exactly %d chars long", DRIZZLE_MAX_SQLSTATE_SIZE);
03082 
03083       /* Check sqlstate string validity */
03084       while (*p && p < end)
03085       {
03086         if (charset_info->isdigit(*p) || charset_info->isupper(*p))
03087           *to_ptr++= *p++;
03088         else
03089           die("The sqlstate may only consist of digits[0-9] and _uppercase_ letters");
03090       }
03091 
03092       *to_ptr= 0;
03093       to->type= ERR_SQLSTATE;
03094     }
03095     else if (*p == 's')
03096     {
03097       die("The sqlstate definition must start with an uppercase S");
03098     }
03099     else if (*p == 'E')
03100     {
03101       /* Error name string */
03102 
03103       to->code.errnum= get_errcode_from_name(p, end);
03104       to->type= ERR_ERRNO;
03105     }
03106     else if (*p == 'e')
03107     {
03108       die("The error name definition must start with an uppercase E");
03109     }
03110     else if (*p == 'H')
03111     {
03112       /* Error name string */
03113 
03114       to->code.errnum= get_errcode_from_name(p, end);
03115       to->type= ERR_ERRNO;
03116     }
03117     else
03118     {
03119       die ("You must either use the SQLSTATE or built in drizzle error label, numbers are not accepted");
03120     }
03121     to++;
03122     count++;
03123 
03124     if (count >= (sizeof(saved_expected_errors.err) /
03125                   sizeof(struct st_match_err)))
03126       die("Too many errorcodes specified");
03127 
03128     /* Set pointer to the end of the last error code */
03129     p= end;
03130 
03131     /* Find next ',' */
03132     while (*p && *p != ',')
03133       p++;
03134 
03135     if (*p)
03136       p++; /* Step past ',' */
03137 
03138   } while (*p);
03139 
03140   command->last_argument= p;
03141   to->type= ERR_EMPTY;                        /* End of data */
03142 
03143   saved_expected_errors.count= count;
03144   return;
03145 }
03146 
03147 
03148 /*
03149   Get a string;  Return ptr to end of string
03150   Strings may be surrounded by " or '
03151 
03152   If string is a '$variable', return the value of the variable.
03153 */
03154 
03155 static char *get_string(char **to_ptr, char **from_ptr,
03156                         st_command* command)
03157 {
03158   char c, sep;
03159   char *to= *to_ptr, *from= *from_ptr, *start=to;
03160 
03161 
03162   /* Find separator */
03163   if (*from == '"' || *from == '\'')
03164     sep= *from++;
03165   else
03166     sep=' ';        /* Separated with space */
03167 
03168   for ( ; (c=*from) ; from++)
03169   {
03170     if (c == '\\' && from[1])
03171     {          /* Escaped character */
03172       /* We can't translate \0 -> ASCII 0 as replace can't handle ASCII 0 */
03173       switch (*++from) {
03174       case 'n':
03175         *to++= '\n';
03176         break;
03177       case 't':
03178         *to++= '\t';
03179         break;
03180       case 'r':
03181         *to++ = '\r';
03182         break;
03183       case 'b':
03184         *to++ = '\b';
03185         break;
03186       case 'Z':        /* ^Z must be escaped on Win32 */
03187         *to++='\032';
03188         break;
03189       default:
03190         *to++ = *from;
03191         break;
03192       }
03193     }
03194     else if (c == sep)
03195     {
03196       if (c == ' ' || c != *++from)
03197         break;        /* Found end of string */
03198       *to++=c;        /* Copy duplicated separator */
03199     }
03200     else
03201       *to++=c;
03202   }
03203   if (*from != ' ' && *from)
03204     die("Wrong string argument in %s", command->query);
03205 
03206   while (charset_info->isspace(*from))  /* Point to next string */
03207     from++;
03208 
03209   *to =0;        /* End of string marker */
03210   *to_ptr= to+1;      /* Store pointer to end */
03211   *from_ptr= from;
03212 
03213   /* Check if this was a variable */
03214   if (*start == '$')
03215   {
03216     const char *end= to;
03217     VAR *var=var_get(start, &end, 0, 1);
03218     if (var && to == (char*) end+1)
03219       return(var->str_val);  /* return found variable value */
03220   }
03221   return(start);
03222 }
03223 
03224 
03225 static void set_reconnect(drizzle_con_st *con, int val)
03226 {
03227   (void) con;
03228   (void) val;
03229 /* XXX
03230   bool reconnect= val;
03231 
03232   drizzleclient_options(drizzle, DRIZZLE_OPT_RECONNECT, (char *)&reconnect);
03233 */
03234 }
03235 
03236 
03237 static void select_connection_name(const char *name)
03238 {
03239   if (!(cur_con= find_ptr2(g_connections, name)))
03240     die("connection '%s' not found in connection pool", name);
03241 
03242   /* Update $drizzleclient_get_server_version to that of current connection */
03243   var_set_drizzleclient_get_server_version(*cur_con);
03244 }
03245 
03246 
03247 static void select_connection(st_command* command)
03248 {
03249   char *p= command->first_argument;
03250   if (!*p)
03251     die("Missing connection name in connect");
03252   char* name= p;
03253   while (*p && !charset_info->isspace(*p))
03254     p++;
03255   if (*p)
03256     *p++= 0;
03257   command->last_argument= p;
03258   select_connection_name(name);
03259 }
03260 
03261 
03262 static void do_close_connection(st_command* command)
03263 {
03264   char* p= command->first_argument;
03265   if (!*p)
03266     die("Missing connection name in disconnect");
03267   char* name= p;
03268   while (*p && !charset_info->isspace(*p))
03269     p++;
03270 
03271   if (*p)
03272     *p++= 0;
03273   command->last_argument= p;
03274 
03275   st_connection* con= find_ptr2(g_connections, name);
03276   if (!con)
03277     die("connection '%s' not found in connection pool", name);
03278   g_connections.erase(name);
03279   delete con;
03280 }
03281 
03282 
03283 /*
03284   Connect to a server doing several retries if needed.
03285 
03286   SYNOPSIS
03287   safe_connect()
03288   con               - connection structure to be used
03289   host, user, pass, - connection parameters
03290   db, port, sock
03291 
03292   NOTE
03293 
03294   Sometimes in a test the client starts before
03295   the server - to solve the problem, we try again
03296   after some sleep if connection fails the first
03297   time
03298 
03299   This function will try to connect to the given server
03300   "opt_max_connect_retries" times and sleep "connection_retry_sleep"
03301   seconds between attempts before finally giving up.
03302   This helps in situation when the client starts
03303   before the server (which happens sometimes).
03304   It will only ignore connection errors during these retries.
03305 
03306 */
03307 
03308 static st_connection* safe_connect(const char *name, const string host, const string user, const char *pass, const string db, uint32_t port)
03309 {
03310   uint32_t failed_attempts= 0;
03311   st_connection* con0= new st_connection;
03312   drizzle_con_st* con= *con0;
03313   drizzle_con_set_tcp(con, host.c_str(), port);
03314   drizzle_con_set_auth(con, user.c_str(), pass);
03315   while (drizzle_return_t ret= drizzle_con_connect(con))
03316   {
03317     /*
03318       Connect failed
03319 
03320       Only allow retry if this was an error indicating the server
03321       could not be contacted. Error code differs depending
03322       on protocol/connection type
03323     */
03324 
03325     if ((ret == DRIZZLE_RETURN_GETADDRINFO ||
03326          ret == DRIZZLE_RETURN_COULD_NOT_CONNECT) &&
03327         failed_attempts < opt_max_connect_retries)
03328     {
03329       verbose_msg("Connect attempt %d/%d failed: %d: %s", failed_attempts, opt_max_connect_retries, ret, drizzle_con_error(con));
03330       usleep(100000);
03331     }
03332     else
03333     {
03334       if (failed_attempts > 0)
03335         die("Could not open connection '%s' after %d attempts: %d %s", name, failed_attempts, ret, drizzle_con_error(con));
03336       else
03337         die("Could not open connection '%s': %d %s", name, ret, drizzle_con_error(con));
03338     }
03339     failed_attempts++;
03340   }
03341 
03342   {
03343     std::string sql_string("CREATE SCHEMA IF NOT EXISTS mysql");
03344     drizzle_return_t ret;
03345     drizzle_result_st *result= drizzle_query(con, NULL, sql_string.c_str(), sql_string.size(), &ret);
03346     if (ret != DRIZZLE_RETURN_OK)
03347     {
03348       die("Failed to create schema '%s': %d %s", db.c_str(), ret, drizzle_con_error(con));
03349     }
03350 
03351     if (result)
03352     {
03353       drizzle_result_free(result);
03354     }
03355   }
03356 
03357   {
03358     std::string sql_string("CREATE SCHEMA IF NOT EXISTS ");
03359     sql_string+= db;
03360     drizzle_return_t ret;
03361     drizzle_result_st *result= drizzle_query(con, NULL, sql_string.c_str(), sql_string.size(), &ret);
03362     if (ret != DRIZZLE_RETURN_OK)
03363     {
03364       die("Failed to create schema '%s': %d %s", db.c_str(), ret, drizzle_con_error(con));
03365     }
03366 
03367     if (result)
03368     {
03369       drizzle_result_free(result);
03370     }
03371   }
03372 
03373   {
03374     std::string sql_string("USE ");
03375     sql_string+= db;
03376     drizzle_return_t ret;
03377     drizzle_result_st *result= drizzle_query(con, NULL, sql_string.c_str(), sql_string.size(), &ret);
03378     if (ret != DRIZZLE_RETURN_OK)
03379     {
03380       die("Failed to use schema '%s': %d %s", db.c_str(), ret, drizzle_con_error(con));
03381     }
03382 
03383     if (result)
03384     {
03385       drizzle_result_free(result);
03386     }
03387   }
03388 
03389   return con0;
03390 }
03391 
03392 
03393 /*
03394   Connect to a server and handle connection errors in case they occur.
03395 
03396   SYNOPSIS
03397   connect_n_handle_errors()
03398   q                 - context of connect "query" (command)
03399   con               - connection structure to be used
03400   host, user, pass, - connection parameters
03401   db, port, sock
03402 
03403   DESCRIPTION
03404   This function will try to establish a connection to server and handle
03405   possible errors in the same manner as if "connect" was usual SQL-statement
03406   (If error is expected it will ignore it once it occurs and log the
03407   "statement" to the query log).
03408   Unlike safe_connect() it won't do several attempts.
03409 
03410   RETURN VALUES
03411   1 - Connected
03412   0 - Not connected
03413 
03414 */
03415 
03416 static int connect_n_handle_errors(st_command* command,
03417                                    drizzle_con_st *con, const char* host,
03418                                    const char* user, const char* pass,
03419                                    const char* db, int port, const char* sock)
03420 {
03421   /* Only log if an error is expected */
03422   if (!command->abort_on_error &&
03423       !disable_query_log)
03424   {
03425     /*
03426       Log the connect to result log
03427     */
03428     ds_res += "connect(";
03429     replace_append(&ds_res, host);
03430     ds_res += ",";
03431     replace_append(&ds_res, user);
03432     ds_res += ",";
03433     replace_append(&ds_res, pass);
03434     ds_res += ",";
03435     if (db)
03436       replace_append(&ds_res, db);
03437     ds_res += ",";
03438     replace_append_uint(ds_res, port);
03439     ds_res += ",";
03440     if (sock)
03441       replace_append(&ds_res, sock);
03442     ds_res += ")";
03443     ds_res += delimiter;
03444     ds_res += "\n";
03445   }
03446   drizzle_con_set_tcp(con, host, port);
03447   drizzle_con_set_auth(con, user, pass);
03448   drizzle_con_set_db(con, db);
03449   if (drizzle_return_t ret= drizzle_con_connect(con))
03450   {
03451     if (ret == DRIZZLE_RETURN_HANDSHAKE_FAILED)
03452     {
03453       var_set_errno(drizzle_con_error_code(con));
03454       handle_error(command, drizzle_con_error_code(con), drizzle_con_error(con), drizzle_con_sqlstate(con), &ds_res);
03455     }
03456     else
03457     {
03458       var_set_errno(ret);
03459       handle_error(command, ret, drizzle_con_error(con), "", &ds_res);
03460     }
03461     return 0; /* Not connected */
03462   }
03463   var_set_errno(0);
03464   handle_no_error(command);
03465   return 1; /* Connected */
03466 }
03467 
03468 
03469 /*
03470   Open a new connection to DRIZZLE Server with the parameters
03471   specified. Make the new connection the current connection.
03472 
03473   SYNOPSIS
03474   do_connect()
03475   q         called command
03476 
03477   DESCRIPTION
03478   connect(<name>,<host>,<user>,[<pass>,[<db>,[<port>,<sock>[<opts>]]]]);
03479   connect <name>,<host>,<user>,[<pass>,[<db>,[<port>,<sock>[<opts>]]]];
03480 
03481   <name> - name of the new connection
03482   <host> - hostname of server
03483   <user> - user to connect as
03484   <pass> - password used when connecting
03485   <db>   - initial db when connected
03486   <port> - server port
03487   <sock> - server socket
03488   <opts> - options to use for the connection
03489   * SSL - use SSL if available
03490   * COMPRESS - use compression if available
03491 
03492   */
03493 
03494 static void do_connect(st_command* command)
03495 {
03496   uint32_t con_port= opt_port;
03497 
03498   string ds_connection_name;
03499   string ds_host;
03500   string ds_user;
03501   string ds_password;
03502   string ds_database;
03503   string ds_port;
03504   string ds_sock;
03505   string ds_options;
03506   const struct command_arg connect_args[] = {
03507     { "connection name", ARG_STRING, true, &ds_connection_name, "Name of the connection" },
03508     { "host", ARG_STRING, true, &ds_host, "Host to connect to" },
03509     { "user", ARG_STRING, false, &ds_user, "User to connect as" },
03510     { "passsword", ARG_STRING, false, &ds_password, "Password used when connecting" },
03511     { "database", ARG_STRING, false, &ds_database, "Database to select after connect" },
03512     { "port", ARG_STRING, false, &ds_port, "Port to connect to" },
03513     { "socket", ARG_STRING, false, &ds_sock, "Socket to connect with" },
03514     { "options", ARG_STRING, false, &ds_options, "Options to use while connecting" }
03515   };
03516 
03517   strip_parentheses(command);
03518   check_command_args(command, command->first_argument, connect_args,
03519                      sizeof(connect_args)/sizeof(struct command_arg),
03520                      ',');
03521 
03522   /* Port */
03523   if (ds_port.length())
03524   {
03525     con_port= atoi(ds_port.c_str());
03526     if (con_port == 0)
03527       die("Illegal argument for port: '%s'", ds_port.c_str());
03528   }
03529 
03530   /* Sock */
03531   if (!ds_sock.empty())
03532   {
03533     /*
03534       If the socket is specified just as a name without path
03535       append tmpdir in front
03536     */
03537     if (*ds_sock.c_str() != FN_LIBCHAR)
03538     {
03539       char buff[FN_REFLEN];
03540       internal::fn_format(buff, ds_sock.c_str(), TMPDIR, "", 0);
03541       ds_sock= buff;
03542     }
03543   }
03544 
03545   /* Options */
03546   const char* con_options= ds_options.c_str();
03547   while (*con_options)
03548   {
03549     /* Step past any spaces in beginning of option*/
03550     while (*con_options && charset_info->isspace(*con_options))
03551       con_options++;
03552     /* Find end of this option */
03553     const char* end= con_options;
03554     while (*end && !charset_info->isspace(*end))
03555       end++;
03556     die("Illegal option to connect: %.*s", (int) (end - con_options), con_options);
03557     /* Process next option */
03558     con_options= end;
03559   }
03560 
03561   if (find_ptr2(g_connections, ds_connection_name))
03562     die("Connection %s already exists", ds_connection_name.c_str());
03563 
03564   st_connection* con_slot= new st_connection;
03565 
03566   /* Use default db name */
03567   if (ds_database.empty())
03568   {
03569     ds_database= opt_db;
03570   }
03571 
03572   /* Special database to allow one to connect without a database name */
03573   if (ds_database == "*NO-ONE*")
03574   {
03575     ds_database.clear();
03576   }
03577 
03578   if (connect_n_handle_errors(command, *con_slot, ds_host.c_str(), ds_user.c_str(), 
03579     ds_password.c_str(), ds_database.c_str(), con_port, ds_sock.c_str()))
03580   {
03581     g_connections[ds_connection_name]= con_slot;
03582     cur_con= con_slot;
03583   }
03584 
03585   /* Update $drizzleclient_get_server_version to that of current connection */
03586   var_set_drizzleclient_get_server_version(*cur_con);
03587 }
03588 
03589 
03590 static void do_done(st_command* command)
03591 {
03592   /* Check if empty block stack */
03593   if (cur_block == block_stack)
03594   {
03595     if (*command->query != '}')
03596       die("Stray 'end' command - end of block before beginning");
03597     die("Stray '}' - end of block before beginning");
03598   }
03599 
03600   /* Test if inner block has been executed */
03601   if (cur_block->ok && cur_block->cmd == cmd_while)
03602   {
03603     /* Pop block from stack, re-execute outer block */
03604     cur_block--;
03605     parser.current_line = cur_block->line;
03606   }
03607   else
03608   {
03609     /* Pop block from stack, goto next line */
03610     cur_block--;
03611     parser.current_line++;
03612   }
03613 }
03614 
03615 
03616 /*
03617   Process start of a "if" or "while" statement
03618 
03619   SYNOPSIS
03620   do_block()
03621   cmd        Type of block
03622   q         called command
03623 
03624   DESCRIPTION
03625   if ([!]<expr>)
03626   {
03627   <block statements>
03628   }
03629 
03630   while ([!]<expr>)
03631   {
03632   <block statements>
03633   }
03634 
03635   Evaluates the <expr> and if it evaluates to
03636   greater than zero executes the following code block.
03637   A '!' can be used before the <expr> to indicate it should
03638   be executed if it evaluates to zero.
03639 
03640 */
03641 
03642 static void do_block(enum block_cmd cmd, st_command* command)
03643 {
03644   char *p= command->first_argument;
03645   const char *expr_start, *expr_end;
03646   const char *cmd_name= (cmd == cmd_while ? "while" : "if");
03647   bool not_expr= false;
03648 
03649   /* Check stack overflow */
03650   if (cur_block == block_stack_end)
03651     die("Nesting too deeply");
03652 
03653   /* Set way to find outer block again, increase line counter */
03654   cur_block->line= parser.current_line++;
03655 
03656   /* If this block is ignored */
03657   if (!cur_block->ok)
03658   {
03659     /* Inner block should be ignored too */
03660     cur_block++;
03661     cur_block->cmd= cmd;
03662     cur_block->ok= false;
03663     return;
03664   }
03665 
03666   /* Parse and evaluate test expression */
03667   expr_start= strchr(p, '(');
03668   if (!expr_start++)
03669     die("missing '(' in %s", cmd_name);
03670 
03671   /* Check for !<expr> */
03672   if (*expr_start == '!')
03673   {
03674     not_expr= true;
03675     expr_start++; /* Step past the '!' */
03676   }
03677   /* Find ending ')' */
03678   expr_end= strrchr(expr_start, ')');
03679   if (!expr_end)
03680     die("missing ')' in %s", cmd_name);
03681   p= (char*)expr_end+1;
03682 
03683   while (*p && charset_info->isspace(*p))
03684     p++;
03685   if (*p && *p != '{')
03686     die("Missing '{' after %s. Found \"%s\"", cmd_name, p);
03687 
03688   VAR v;
03689   var_init(&v,0,0,0,0);
03690   eval_expr(&v, expr_start, &expr_end);
03691 
03692   /* Define inner block */
03693   cur_block++;
03694   cur_block->cmd= cmd;
03695   cur_block->ok= (v.int_val ? true : false);
03696 
03697   if (not_expr)
03698     cur_block->ok = !cur_block->ok;
03699 
03700   free(v.str_val);
03701   free(v.env_s);
03702 }
03703 
03704 
03705 static void do_delimiter(st_command* command)
03706 {
03707   char* p= command->first_argument;
03708 
03709   while (*p && charset_info->isspace(*p))
03710     p++;
03711 
03712   if (!(*p))
03713   {
03714     die("Can't set empty delimiter");
03715   }
03716 
03717   strncpy(delimiter, p, sizeof(delimiter) - 1);
03718   delimiter_length= strlen(delimiter);
03719 
03720   command->last_argument= p + delimiter_length;
03721 }
03722 
03723 
03724 bool match_delimiter(int c, const char *delim, uint32_t length)
03725 {
03726   uint32_t i;
03727   char tmp[MAX_DELIMITER_LENGTH];
03728 
03729   if (c != *delim)
03730     return 0;
03731 
03732   for (i= 1; i < length &&
03733          (c= my_getc(cur_file->file)) == *(delim + i);
03734        i++)
03735     tmp[i]= c;
03736 
03737   if (i == length)
03738     return 1;          /* Found delimiter */
03739 
03740   /* didn't find delimiter, push back things that we read */
03741   my_ungetc(c);
03742   while (i > 1)
03743     my_ungetc(tmp[--i]);
03744   return 0;
03745 }
03746 
03747 
03748 static bool end_of_query(int c)
03749 {
03750   return match_delimiter(c, delimiter, delimiter_length);
03751 }
03752 
03753 
03754 /*
03755   Read one "line" from the file
03756 
03757   SYNOPSIS
03758   read_line
03759   buf     buffer for the read line
03760   size    size of the buffer i.e max size to read
03761 
03762   DESCRIPTION
03763   This function actually reads several lines and adds them to the
03764   buffer buf. It continues to read until it finds what it believes
03765   is a complete query.
03766 
03767   Normally that means it will read lines until it reaches the
03768   "delimiter" that marks end of query. Default delimiter is ';'
03769   The function should be smart enough not to detect delimiter's
03770   found inside strings surrounded with '"' and '\'' escaped strings.
03771 
03772   If the first line in a query starts with '#' or '-' this line is treated
03773   as a comment. A comment is always terminated when end of line '\n' is
03774   reached.
03775 
03776 */
03777 
03778 
03779 static int my_strnncoll_simple(const charset_info_st * const  cs, const unsigned char *s, size_t slen,
03780                                const unsigned char *t, size_t tlen,
03781                                bool t_is_prefix)
03782 {
03783   size_t len = ( slen > tlen ) ? tlen : slen;
03784   unsigned char *map= cs->sort_order;
03785   if (t_is_prefix && slen > tlen)
03786     slen=tlen;
03787   while (len--)
03788   {
03789     if (map[*s++] != map[*t++])
03790       return ((int) map[s[-1]] - (int) map[t[-1]]);
03791   }
03792   /*
03793     We can't use (slen - tlen) here as the result may be outside of the
03794     precision of a signed int
03795   */
03796   return slen > tlen ? 1 : slen < tlen ? -1 : 0 ;
03797 }
03798 
03799 static int read_line(char *buf, int size)
03800 {
03801   char c, last_quote= 0;
03802   char *p= buf, *buf_end= buf + size - 1;
03803   int skip_char= 0;
03804   enum {R_NORMAL, R_Q, R_SLASH_IN_Q,
03805         R_COMMENT, R_LINE_START} state= R_LINE_START;
03806 
03807 
03808   start_lineno= cur_file->lineno;
03809   for (; p < buf_end ;)
03810   {
03811     skip_char= 0;
03812     c= my_getc(cur_file->file);
03813     if (feof(cur_file->file))
03814     {
03815   found_eof:
03816       if (cur_file->file != stdin)
03817       {
03818         fclose(cur_file->file);
03819         cur_file->file= 0;
03820       }
03821       free((unsigned char*) cur_file->file_name);
03822       cur_file->file_name= 0;
03823       if (cur_file == file_stack.data())
03824       {
03825         /* We're back at the first file, check if
03826            all { have matching }
03827         */
03828         if (cur_block != block_stack)
03829           die("Missing end of block");
03830 
03831         *p= 0;
03832         return(1);
03833       }
03834       cur_file--;
03835       start_lineno= cur_file->lineno;
03836       continue;
03837     }
03838 
03839     if (c == '\n')
03840     {
03841       /* Line counting is independent of state */
03842       cur_file->lineno++;
03843 
03844       /* Convert cr/lf to lf */
03845       if (p != buf && *(p-1) == '\r')
03846         p--;
03847     }
03848 
03849     switch(state) {
03850     case R_NORMAL:
03851       if (end_of_query(c))
03852       {
03853         *p= 0;
03854         return(0);
03855       }
03856       else if ((c == '{' &&
03857                 (!my_strnncoll_simple(charset_info, (const unsigned char*) "while", 5,
03858                                       (unsigned char*) buf, min((ptrdiff_t)5, p - buf), 0) ||
03859                  !my_strnncoll_simple(charset_info, (const unsigned char*) "if", 2,
03860                                       (unsigned char*) buf, min((ptrdiff_t)2, p - buf), 0))))
03861       {
03862         /* Only if and while commands can be terminated by { */
03863         *p++= c;
03864         *p= 0;
03865         return(0);
03866       }
03867       else if (c == '\'' || c == '"' || c == '`')
03868       {
03869         last_quote= c;
03870         state= R_Q;
03871       }
03872       break;
03873 
03874     case R_COMMENT:
03875       if (c == '\n')
03876       {
03877         /* Comments are terminated by newline */
03878         *p= 0;
03879         return(0);
03880       }
03881       break;
03882 
03883     case R_LINE_START:
03884       if (c == '#' || c == '-')
03885       {
03886         /* A # or - in the first position of the line - this is a comment */
03887         state = R_COMMENT;
03888       }
03889       else if (charset_info->isspace(c))
03890       {
03891         /* Skip all space at begining of line */
03892         if (c == '\n')
03893         {
03894           /* Query hasn't started yet */
03895           start_lineno= cur_file->lineno;
03896         }
03897         skip_char= 1;
03898       }
03899       else if (end_of_query(c))
03900       {
03901         *p= 0;
03902         return(0);
03903       }
03904       else if (c == '}')
03905       {
03906         /* A "}" need to be by itself in the begining of a line to terminate */
03907         *p++= c;
03908         *p= 0;
03909         return(0);
03910       }
03911       else if (c == '\'' || c == '"' || c == '`')
03912       {
03913         last_quote= c;
03914         state= R_Q;
03915       }
03916       else
03917         state= R_NORMAL;
03918       break;
03919 
03920     case R_Q:
03921       if (c == last_quote)
03922         state= R_NORMAL;
03923       else if (c == '\\')
03924         state= R_SLASH_IN_Q;
03925       break;
03926 
03927     case R_SLASH_IN_Q:
03928       state= R_Q;
03929       break;
03930 
03931     }
03932 
03933     if (!skip_char)
03934     {
03935       /* Could be a multibyte character */
03936       /* This code is based on the code in "sql_load.cc" */
03937       int charlen = my_mbcharlen(charset_info, c);
03938       /* We give up if multibyte character is started but not */
03939       /* completed before we pass buf_end */
03940       if ((charlen > 1) && (p + charlen) <= buf_end)
03941       {
03942         int i;
03943         char* mb_start = p;
03944 
03945         *p++ = c;
03946 
03947         for (i= 1; i < charlen; i++)
03948         {
03949           if (feof(cur_file->file))
03950             goto found_eof;
03951           c= my_getc(cur_file->file);
03952           *p++ = c;
03953         }
03954         if (! my_ismbchar(charset_info, mb_start, p))
03955         {
03956           /* It was not a multiline char, push back the characters */
03957           /* We leave first 'c', i.e. pretend it was a normal char */
03958           while (p > mb_start)
03959             my_ungetc(*--p);
03960         }
03961       }
03962       else
03963         *p++= c;
03964     }
03965   }
03966   die("The input buffer is too small for this query.x\n"        \
03967       "check your query or increase MAX_QUERY and recompile");
03968   return(0);
03969 }
03970 
03971 
03972 /*
03973   Convert the read query to result format version 1
03974 
03975   That is: After newline, all spaces need to be skipped
03976   unless the previous char was a quote
03977 
03978   This is due to an old bug that has now been fixed, but the
03979   version 1 output format is preserved by using this function
03980 
03981 */
03982 
03983 static void convert_to_format_v1(char* query)
03984 {
03985   int last_c_was_quote= 0;
03986   char *p= query, *to= query;
03987   char *end= strchr(query, '\0');
03988   char last_c;
03989 
03990   while (p <= end)
03991   {
03992     if (*p == '\n' && !last_c_was_quote)
03993     {
03994       *to++ = *p++; /* Save the newline */
03995 
03996       /* Skip any spaces on next line */
03997       while (*p && charset_info->isspace(*p))
03998         p++;
03999 
04000       last_c_was_quote= 0;
04001     }
04002     else if (*p == '\'' || *p == '"' || *p == '`')
04003     {
04004       last_c= *p;
04005       *to++ = *p++;
04006 
04007       /* Copy anything until the next quote of same type */
04008       while (*p && *p != last_c)
04009         *to++ = *p++;
04010 
04011       *to++ = *p++;
04012 
04013       last_c_was_quote= 1;
04014     }
04015     else
04016     {
04017       *to++ = *p++;
04018       last_c_was_quote= 0;
04019     }
04020   }
04021 }
04022 
04023 
04024 /*
04025   Check a command that is about to be sent (or should have been
04026   sent if parsing was enabled) to DRIZZLE server for
04027   suspicious things and generate warnings.
04028 */
04029 
04030 static void scan_command_for_warnings(st_command* command)
04031 {
04032   const char *ptr= command->query;
04033 
04034   while (*ptr)
04035   {
04036     /*
04037       Look for query's that lines that start with a -- comment
04038       and has a drizzletest command
04039     */
04040     if (ptr[0] == '\n' &&
04041         ptr[1] && ptr[1] == '-' &&
04042         ptr[2] && ptr[2] == '-' &&
04043         ptr[3])
04044     {
04045       uint32_t type;
04046       char save;
04047       char *end, *start= (char*)ptr+3;
04048       /* Skip leading spaces */
04049       while (*start && charset_info->isspace(*start))
04050         start++;
04051       end= start;
04052       /* Find end of command(next space) */
04053       while (*end && !charset_info->isspace(*end))
04054         end++;
04055       save= *end;
04056       *end= 0;
04057       type= command_typelib.find_type(start, TYPELIB::e_default);
04058       if (type)
04059         warning_msg("Embedded drizzletest command '--%s' detected in query '%s' was this intentional? ", start, command->query);
04060       *end= save;
04061     }
04062     ptr++;
04063   }
04064 }
04065 
04066 /*
04067   Check for unexpected "junk" after the end of query
04068   This is normally caused by missing delimiters or when
04069   switching between different delimiters
04070 */
04071 
04072 static void check_eol_junk_line(const char *line)
04073 {
04074   const char *p= line;
04075 
04076   /* Check for extra delimiter */
04077   if (*p && !strncmp(p, delimiter, delimiter_length))
04078     die("Extra delimiter \"%s\" found", delimiter);
04079 
04080   /* Allow trailing # comment */
04081   if (*p && *p != '#')
04082   {
04083     if (*p == '\n')
04084       die("Missing delimiter");
04085     die("End of line junk detected: \"%s\"", p);
04086   }
04087   return;
04088 }
04089 
04090 static void check_eol_junk(const char *eol)
04091 {
04092   const char *p= eol;
04093 
04094   /* Skip past all spacing chars and comments */
04095   while (*p && (charset_info->isspace(*p) || *p == '#' || *p == '\n'))
04096   {
04097     /* Skip past comments started with # and ended with newline */
04098     if (*p && *p == '#')
04099     {
04100       p++;
04101       while (*p && *p != '\n')
04102         p++;
04103     }
04104 
04105     /* Check this line */
04106     if (*p && *p == '\n')
04107       check_eol_junk_line(p);
04108 
04109     if (*p)
04110       p++;
04111   }
04112 
04113   check_eol_junk_line(p);
04114 
04115   return;
04116 }
04117 
04118 /*
04119   Create a command from a set of lines
04120 
04121   SYNOPSIS
04122   read_command()
04123   command_ptr pointer where to return the new query
04124 
04125   DESCRIPTION
04126   Converts lines returned by read_line into a command, this involves
04127   parsing the first word in the read line to find the command type.
04128 
04129   A -- comment may contain a valid query as the first word after the
04130   comment start. Thus it's always checked to see if that is the case.
04131   The advantage with this approach is to be able to execute commands
04132   terminated by new line '\n' regardless how many "delimiter" it contain.
04133 */
04134 
04135 #define MAX_QUERY (768*1024*2) /* 256K -- a test in sp-big is >128K */
04136 static char read_command_buf[MAX_QUERY];
04137 
04138 static int read_command(st_command** command_ptr)
04139 {
04140   char *p= read_command_buf;
04141   st_command* command;
04142 
04143 
04144   if (parser.current_line < parser.read_lines)
04145   {
04146     *command_ptr= q_lines[parser.current_line];
04147     return(0);
04148   }
04149   *command_ptr= command= new st_command;
04150   q_lines.push_back(command);
04151   command->type= Q_UNKNOWN;
04152 
04153   read_command_buf[0]= 0;
04154   if (read_line(read_command_buf, sizeof(read_command_buf)))
04155   {
04156     check_eol_junk(read_command_buf);
04157     return(1);
04158   }
04159 
04160   convert_to_format_v1(read_command_buf);
04161 
04162   if (*p == '#')
04163   {
04164     command->type= Q_COMMENT;
04165   }
04166   else if (p[0] == '-' && p[1] == '-')
04167   {
04168     command->type= Q_COMMENT_WITH_COMMAND;
04169     p+= 2; /* Skip past -- */
04170   }
04171 
04172   /* Skip leading spaces */
04173   while (*p && charset_info->isspace(*p))
04174     p++;
04175 
04176   command->query_buf= command->query= strdup(p);
04177 
04178   /* Calculate first word length(the command), terminated by space or ( */
04179   p= command->query;
04180   while (*p && !charset_info->isspace(*p) && *p != '(')
04181     p++;
04182   command->first_word_len= (uint32_t) (p - command->query);
04183 
04184   /* Skip spaces between command and first argument */
04185   while (*p && charset_info->isspace(*p))
04186     p++;
04187   command->first_argument= p;
04188 
04189   command->end= strchr(command->query, '\0');
04190   command->query_len= (command->end - command->query);
04191   parser.read_lines++;
04192 
04193   return(0);
04194 }
04195 
04196 /*
04197   Write the content of str into file
04198 
04199   SYNOPSIS
04200   str_to_file2
04201   fname - name of file to truncate/create and write to
04202   str - content to write to file
04203   size - size of content witten to file
04204   append - append to file instead of overwriting old file
04205 */
04206 
04207 void str_to_file2(const char *fname, const char *str, int size, bool append)
04208 {
04209   char buff[FN_REFLEN];
04210   if (!internal::test_if_hard_path(fname))
04211   {
04212     snprintf(buff, sizeof(buff), "%s%s",opt_basedir.c_str(),fname);
04213     fname= buff;
04214   }
04215   internal::fn_format(buff, fname, "", "", MY_UNPACK_FILENAME);
04216 
04217   int flags= O_WRONLY | O_CREAT;
04218   if (!append)
04219     flags|= O_TRUNC;
04220   int fd= internal::my_open(buff, flags, MYF(MY_WME | MY_FFNF));
04221   if (fd < 0)
04222     die("Could not open '%s' for writing: errno = %d", buff, errno);
04223   if (append && lseek(fd, 0, SEEK_END) == MY_FILEPOS_ERROR)
04224     die("Could not find end of file '%s': errno = %d", buff, errno);
04225   if (internal::my_write(fd, (unsigned char*)str, size, MYF(MY_WME|MY_FNABP)))
04226     die("write failed");
04227   internal::my_close(fd, MYF(0));
04228 }
04229 
04230 /*
04231   Write the content of str into file
04232 
04233   SYNOPSIS
04234   str_to_file
04235   fname - name of file to truncate/create and write to
04236   str - content to write to file
04237   size - size of content witten to file
04238 */
04239 
04240 void str_to_file(const char *fname, const char *str, int size)
04241 {
04242   str_to_file2(fname, str, size, false);
04243 }
04244 
04245 
04246 void dump_result_to_log_file(const char *buf, int size)
04247 {
04248   char log_file[FN_REFLEN];
04249   str_to_file(internal::fn_format(log_file, result_file_name.c_str(), opt_logdir.c_str(), ".log",
04250                         ! opt_logdir.empty() ? MY_REPLACE_DIR | MY_REPLACE_EXT :
04251                         MY_REPLACE_EXT),
04252               buf, size);
04253   fprintf(stderr, "\nMore results from queries before failure can be found in %s\n", log_file);
04254 }
04255 
04256 void dump_progress()
04257 {
04258   char progress_file[FN_REFLEN];
04259   str_to_file(internal::fn_format(progress_file, result_file_name.c_str(),
04260                         opt_logdir.c_str(), ".progress",
04261                         ! opt_logdir.empty() ? MY_REPLACE_DIR | MY_REPLACE_EXT :
04262                         MY_REPLACE_EXT),
04263               ds_progress.c_str(), ds_progress.length());
04264 }
04265 
04266 void dump_warning_messages()
04267 {
04268   char warn_file[FN_REFLEN];
04269 
04270   str_to_file(internal::fn_format(warn_file, result_file_name.c_str(), opt_logdir.c_str(), ".warnings",
04271                         ! opt_logdir.empty() ? MY_REPLACE_DIR | MY_REPLACE_EXT :
04272                         MY_REPLACE_EXT),
04273               ds_warning_messages.c_str(), ds_warning_messages.length());
04274 }
04275 
04276 
04277 /*
04278   Append the result for one field to the dynamic string ds
04279 */
04280 
04281 static void append_field(string *ds, uint32_t col_idx, drizzle_column_st *column,
04282                          const char* val, uint64_t len, bool is_null)
04283 {
04284   if (col_idx < max_replace_column && replace_column[col_idx])
04285   {
04286     val= replace_column[col_idx];
04287     len= strlen(val);
04288   }
04289   else if (is_null)
04290   {
04291     val= "NULL";
04292     len= 4;
04293   }
04294 
04295   if (!display_result_vertically)
04296   {
04297     if (col_idx)
04298       ds->append("\t");
04299     replace_append_mem(*ds, val, (int)len);
04300   }
04301   else
04302   {
04303     ds->append(drizzle_column_name(column));
04304     ds->append("\t");
04305     replace_append_mem(*ds, val, (int)len);
04306     ds->append("\n");
04307   }
04308 }
04309 
04310 
04311 /*
04312   Append all results to the dynamic string separated with '\t'
04313   Values may be converted with 'replace_column'
04314 */
04315 
04316 static void append_result(string *ds, drizzle::result_c& res)
04317 {
04318   uint32_t num_fields= res.column_count();
04319   while (drizzle_row_t row = res.row_next())
04320   {
04321     size_t* lengths = res.row_field_sizes();
04322     res.column_seek(0);
04323     for (uint32_t i = 0; i < num_fields; i++)
04324     {
04325       drizzle_column_st* column= res.column_next();
04326       if (row[i] && drizzle_column_type(column) == DRIZZLE_COLUMN_TYPE_TINY)
04327       {
04328         if (boost::lexical_cast<uint32_t>(row[i]))
04329         {
04330           if (drizzle_column_flags(column) & DRIZZLE_COLUMN_FLAGS_UNSIGNED)
04331           {
04332             append_field(ds, i, column, "YES", 3, false);
04333           }
04334           else
04335           {
04336             append_field(ds, i, column, "TRUE", 4, false);
04337           }
04338         }
04339         else
04340         {
04341           if (drizzle_column_flags(column) & DRIZZLE_COLUMN_FLAGS_UNSIGNED)
04342           {
04343             append_field(ds, i, column, "NO", 2, false);
04344           }
04345           else
04346           {
04347             append_field(ds, i, column, "FALSE", 5, false);
04348           }
04349         }
04350       }
04351       else
04352       {
04353         append_field(ds, i, column, (const char*)row[i], lengths[i], !row[i]);
04354       }
04355     }
04356     if (!display_result_vertically)
04357       ds->append("\n");
04358   }
04359 }
04360 
04361 
04362 /*
04363   Append metadata for fields to output
04364 */
04365 
04366 static void append_metadata(string& ds, drizzle::result_c& res)
04367 {
04368   ds += "Catalog\tDatabase\tTable\tTable_alias\tColumn\tColumn_alias\tType\tLength\tMax length\tIs_null\tFlags\tDecimals\tCharsetnr\n";
04369   res.column_seek(0);
04370   while (drizzle_column_st* column= res.column_next())
04371   {
04372     ds += drizzle_column_catalog(column);
04373     ds += "\t";
04374     ds += drizzle_column_db(column);
04375     ds += "\t";
04376     ds += drizzle_column_orig_table(column);
04377     ds += "\t";
04378     ds += drizzle_column_table(column);
04379     ds += "\t";
04380     ds += drizzle_column_orig_name(column);
04381     ds += "\t";
04382     ds += drizzle_column_name(column);
04383     ds += "\t";
04384     replace_append_uint(ds, drizzle_column_type_drizzle(column));
04385     ds += "\t";
04386     replace_append_uint(ds, drizzle_column_size(column));
04387     ds += "\t";
04388     replace_append_uint(ds, drizzle_column_type(column) == DRIZZLE_COLUMN_TYPE_TINY ? 1 : drizzle_column_max_size(column));
04389     ds += "\t";
04390     ds += drizzle_column_flags(column) & DRIZZLE_COLUMN_FLAGS_NOT_NULL ? "N" : "Y";
04391     ds += "\t";
04392     replace_append_uint(ds, drizzle_column_flags(column));
04393     ds += "\t";
04394     replace_append_uint(ds, drizzle_column_decimals(column));
04395     ds += "\t";
04396     replace_append_uint(ds, drizzle_column_charset(column));
04397     ds += "\n";
04398   }
04399 }
04400 
04401 
04402 /*
04403   Append affected row count and other info to output
04404 */
04405 
04406 static void append_info(string *ds, uint64_t affected_rows,
04407                         const char *info)
04408 {
04409   ostringstream buf;
04410   buf << "affected rows: " << affected_rows << endl;
04411   ds->append(buf.str());
04412   if (info && strcmp(info, ""))
04413   {
04414     ds->append("info: ");
04415     ds->append(info);
04416     ds->append("\n", 1);
04417   }
04418 }
04419 
04420 
04421 /*
04422   Display the table headings with the names tab separated
04423 */
04424 
04425 static void append_table_headings(string& ds, drizzle::result_c& res)
04426 {
04427   uint32_t col_idx= 0;
04428   res.column_seek(0);
04429   while (drizzle_column_st* column= res.column_next())
04430   {
04431     if (col_idx)
04432       ds += "\t";
04433     replace_append(&ds, drizzle_column_name(column));
04434     col_idx++;
04435   }
04436   ds += "\n";
04437 }
04438 
04439 /*
04440   Fetch warnings from server and append to ds
04441 
04442   RETURN VALUE
04443   Number of warnings appended to ds
04444 */
04445 
04446 static int append_warnings(string& ds, drizzle::connection_c& con, drizzle::result_c& res)
04447 {
04448   uint32_t count= drizzle_result_warning_count(res);
04449   if (!count)
04450     return 0;
04451 
04452   drizzle::result_c warn_res;
04453   dt_query(con, warn_res, "show warnings");
04454   append_result(&ds, warn_res);
04455   return count;
04456 }
04457 
04458 
04459 /*
04460   Run query using DRIZZLE C API
04461 
04462   SYNOPSIS
04463   run_query_normal()
04464   drizzle  DRIZZLE handle
04465   command  current command pointer
04466   flags  flags indicating if we should SEND and/or REAP
04467   query  query string to execute
04468   query_len  length query string to execute
04469   ds    output buffer where to store result form query
04470 */
04471 
04472 static void run_query_normal(st_connection& cn,
04473                              st_command* command,
04474                              int flags, char *query, int query_len,
04475                              string *ds, string& ds_warnings)
04476 {
04477   drizzle_return_t ret;
04478   drizzle_con_st *con= cn;
04479   int err= 0;
04480 
04481   drizzle_con_add_options(con, DRIZZLE_CON_NO_RESULT_READ);
04482 
04483   drizzle::result_c res;
04484   if (flags & QUERY_SEND_FLAG)
04485   {
04486     /*
04487      * Send the query
04488      */
04489 
04490     (void) drizzle_query(con, res, query, query_len, &ret);
04491     if (ret != DRIZZLE_RETURN_OK)
04492     {
04493       if (ret == DRIZZLE_RETURN_ERROR_CODE ||
04494           ret == DRIZZLE_RETURN_HANDSHAKE_FAILED)
04495       {
04496         err= res.error_code();
04497         handle_error(command, err, res.error(), drizzle_result_sqlstate(res), ds);
04498       }
04499       else
04500       {
04501         handle_error(command, ret, drizzle_con_error(con), "", ds);
04502         err= ret;
04503       }
04504       goto end;
04505     }
04506   }
04507   if (!(flags & QUERY_REAP_FLAG))
04508     return;
04509 
04510   {
04511     /*
04512      * Read the result packet
04513      */
04514     if (drizzle_result_read(con, res, &ret) == NULL ||
04515         ret != DRIZZLE_RETURN_OK)
04516     {
04517       if (ret == DRIZZLE_RETURN_ERROR_CODE)
04518       {
04519         handle_error(command, res.error_code(), res.error(), drizzle_result_sqlstate(res), ds);
04520       }
04521       else
04522         handle_error(command, ret, drizzle_con_error(con), "", ds);
04523       err= ret;
04524       goto end;
04525     }
04526 
04527     /*
04528       Store the result of the query if it will return any fields
04529     */
04530     if (res.column_count() && (ret= drizzle_result_buffer(res)) != DRIZZLE_RETURN_OK)
04531     {
04532       if (ret == DRIZZLE_RETURN_ERROR_CODE)
04533       {
04534         handle_error(command, res.error_code(), res.error(), drizzle_result_sqlstate(res), ds);
04535       }
04536       else
04537         handle_error(command, ret, drizzle_con_error(con), "", ds);
04538       err= ret;
04539       goto end;
04540     }
04541 
04542     if (!disable_result_log)
04543     {
04544       uint64_t affected_rows= 0;    /* Ok to be undef if 'disable_info' is set */
04545 
04546       if (res.column_count())
04547       {
04548         if (display_metadata)
04549           append_metadata(*ds, res);
04550 
04551         if (!display_result_vertically)
04552           append_table_headings(*ds, res);
04553 
04554         append_result(ds, res);
04555       }
04556 
04557       /*
04558         Need to call drizzle_result_affected_rows() before the "new"
04559         query to find the warnings
04560       */
04561       if (!disable_info)
04562         affected_rows= drizzle_result_affected_rows(res);
04563 
04564       /*
04565         Add all warnings to the result. We can't do this if we are in
04566         the middle of processing results from multi-statement, because
04567         this will break protocol.
04568       */
04569       if (!disable_warnings)
04570       {
04571         drizzle_con_remove_options(con, DRIZZLE_CON_NO_RESULT_READ);
04572         if (append_warnings(ds_warnings, cn, res) || not ds_warnings.empty())
04573         {
04574           ds->append("Warnings:\n", 10);
04575           *ds += ds_warnings;
04576         }
04577       }
04578 
04579       if (!disable_info)
04580         append_info(ds, affected_rows, drizzle_result_info(res));
04581     }
04582 
04583   }
04584 
04585   /* If we come here the query is both executed and read successfully */
04586   handle_no_error(command);
04587 
04588 end:
04589 
04590   /*
04591     We save the return code (drizzleclient_errno(drizzle)) from the last call sent
04592     to the server into the drizzletest builtin variable $drizzleclient_errno. This
04593     variable then can be used from the test case itself.
04594   */
04595   drizzle_con_remove_options(con, DRIZZLE_CON_NO_RESULT_READ);
04596   var_set_errno(err);
04597 }
04598 
04599 
04600 /*
04601   Handle errors which occurred during execution
04602 
04603   SYNOPSIS
04604   handle_error()
04605   q     - query context
04606   err_errno - error number
04607   err_error - error message
04608   err_sqlstate - sql state
04609   ds    - dynamic string which is used for output buffer
04610 
04611   NOTE
04612   If there is an unexpected error this function will abort drizzletest
04613   immediately.
04614 */
04615 
04616 void handle_error(st_command* command,
04617                   unsigned int err_errno, const char *err_error,
04618                   const char *err_sqlstate, string *ds)
04619 {
04620   if (! command->require_file.empty())
04621   {
04622     /*
04623       The query after a "--require" failed. This is fine as long the server
04624       returned a valid reponse. Don't allow 2013 or 2006 to trigger an
04625       abort_not_supported_test
04626     */
04627     if (err_errno == DRIZZLE_RETURN_LOST_CONNECTION)
04628       die("require query '%s' failed: %d: %s", command->query, err_errno, err_error);
04629 
04630     /* Abort the run of this test, pass the failed query as reason */
04631     abort_not_supported_test("Query '%s' failed, required functionality not supported", command->query);
04632   }
04633 
04634   if (command->abort_on_error)
04635     die("query '%s' failed: %d: %s", command->query, err_errno, err_error);
04636 
04637   uint32_t i= 0;
04638   for (; i < command->expected_errors.count; i++)
04639   {
04640     if (((command->expected_errors.err[i].type == ERR_ERRNO) &&
04641          (command->expected_errors.err[i].code.errnum == err_errno)) ||
04642         ((command->expected_errors.err[i].type == ERR_SQLSTATE) &&
04643          (strncmp(command->expected_errors.err[i].code.sqlstate,
04644                   err_sqlstate, DRIZZLE_MAX_SQLSTATE_SIZE) == 0)))
04645     {
04646       if (!disable_result_log)
04647       {
04648         if (command->expected_errors.count == 1)
04649         {
04650           /* Only log error if there is one possible error */
04651           ds->append("ERROR ", 6);
04652           replace_append(ds, err_sqlstate);
04653           ds->append(": ", 2);
04654           replace_append(ds, err_error);
04655           ds->append("\n",1);
04656         }
04657         /* Don't log error if we may not get an error */
04658         else if (command->expected_errors.err[0].type == ERR_SQLSTATE ||
04659                  (command->expected_errors.err[0].type == ERR_ERRNO &&
04660                   command->expected_errors.err[0].code.errnum != 0))
04661           ds->append("Got one of the listed errors\n");
04662       }
04663       /* OK */
04664       return;
04665     }
04666   }
04667 
04668   if (!disable_result_log)
04669   {
04670     ds->append("ERROR ",6);
04671     replace_append(ds, err_sqlstate);
04672     ds->append(": ", 2);
04673     replace_append(ds, err_error);
04674     ds->append("\n", 1);
04675   }
04676 
04677   if (i)
04678   {
04679     if (command->expected_errors.err[0].type == ERR_ERRNO)
04680       die("query '%s' failed with wrong errno %d: '%s', instead of %d...",
04681           command->query, err_errno, err_error,
04682           command->expected_errors.err[0].code.errnum);
04683     else
04684       die("query '%s' failed with wrong sqlstate %s: '%s', instead of %s...",
04685           command->query, err_sqlstate, err_error,
04686           command->expected_errors.err[0].code.sqlstate);
04687   }
04688 }
04689 
04690 
04691 /*
04692   Handle absence of errors after execution
04693 
04694   SYNOPSIS
04695   handle_no_error()
04696   q - context of query
04697 
04698   RETURN VALUE
04699   error - function will not return
04700 */
04701 
04702 void handle_no_error(st_command* command)
04703 {
04704   if (command->expected_errors.err[0].type == ERR_ERRNO &&
04705       command->expected_errors.err[0].code.errnum != 0)
04706   {
04707     /* Error code we wanted was != 0, i.e. not an expected success */
04708     die("query '%s' succeeded - should have failed with errno %d...", command->query, command->expected_errors.err[0].code.errnum);
04709   }
04710   else if (command->expected_errors.err[0].type == ERR_SQLSTATE &&
04711            strcmp(command->expected_errors.err[0].code.sqlstate,"00000") != 0)
04712   {
04713     /* SQLSTATE we wanted was != "00000", i.e. not an expected success */
04714     die("query '%s' succeeded - should have failed with sqlstate %s...", command->query, command->expected_errors.err[0].code.sqlstate);
04715   }
04716 }
04717 
04718 
04719 /*
04720   Run query
04721 
04722   SYNPOSIS
04723   run_query()
04724   drizzle  DRIZZLE handle
04725   command  currrent command pointer
04726 
04727   flags control the phased/stages of query execution to be performed
04728   if QUERY_SEND_FLAG bit is on, the query will be sent. If QUERY_REAP_FLAG
04729   is on the result will be read - for regular query, both bits must be on
04730 */
04731 
04732 static void run_query(st_connection& cn,
04733                       st_command* command,
04734                       int flags)
04735 {
04736   string eval_query;
04737   char *query;
04738   int query_len;
04739 
04740 
04741   /* Scan for warning before sending to server */
04742   scan_command_for_warnings(command);
04743 
04744   /*
04745     Evaluate query if this is an eval command
04746   */
04747   if (command->type == Q_EVAL)
04748   {
04749     do_eval(&eval_query, command->query, command->end, false);
04750     query = strdup(eval_query.c_str());
04751     query_len = eval_query.length();
04752   }
04753   else
04754   {
04755     query = command->query;
04756     query_len = strlen(query);
04757   }
04758 
04759   /*
04760     When command->require_file is set the output of _this_ query
04761     should be compared with an already existing file
04762     Create a temporary dynamic string to contain the output from
04763     this query.
04764   */
04765   string ds_result;
04766   string* ds= command->require_file.empty() ? &ds_res : &ds_result;
04767   /*
04768     Log the query into the output buffer
04769   */
04770   if (!disable_query_log && (flags & QUERY_SEND_FLAG))
04771   {
04772     replace_append_mem(*ds, query, query_len);
04773     ds->append(delimiter, delimiter_length);
04774     ds->append("\n");
04775   }
04776 
04777   string* save_ds= NULL;
04778   string ds_sorted;
04779   if (display_result_sorted)
04780   {
04781     /*
04782       Collect the query output in a separate string
04783       that can be sorted before it's added to the
04784       global result string
04785     */
04786     save_ds= ds; /* Remember original ds */
04787     ds= &ds_sorted;
04788   }
04789 
04790   /*
04791     Always run with normal C API if it's not a complete
04792     SEND + REAP
04793   */
04794   string ds_warnings;
04795   run_query_normal(cn, command, flags, query, query_len, ds, ds_warnings);
04796 
04797   if (display_result_sorted)
04798   {
04799     /* Sort the result set and append it to result */
04800     append_sorted(*save_ds, ds_sorted);
04801     ds= save_ds;
04802   }
04803 
04804   if (! command->require_file.empty())
04805   {
04806     /* A result file was specified for _this_ query
04807        and the output should be checked against an already
04808        existing file which has been specified using --require or --result
04809     */
04810     check_require(*ds, command->require_file);
04811   }
04812 }
04813 
04814 
04815 /****************************************************************************/
04816 
04817 static void get_command_type(st_command* command)
04818 {
04819   if (*command->query == '}')
04820   {
04821     command->type = Q_END_BLOCK;
04822     return;
04823   }
04824 
04825   char save= command->query[command->first_word_len];
04826   command->query[command->first_word_len]= 0;
04827   uint32_t type= command_typelib.find_type(command->query, TYPELIB::e_default);
04828   command->query[command->first_word_len]= save;
04829   if (type > 0)
04830   {
04831     command->type=(enum enum_commands) type;    /* Found command */
04832 
04833     /*
04834       Look for case where "query" was explicitly specified to
04835       force command being sent to server
04836     */
04837     if (type == Q_QUERY)
04838     {
04839       /* Skip the "query" part */
04840       command->query= command->first_argument;
04841     }
04842   }
04843   else
04844   {
04845     /* No drizzletest command matched */
04846 
04847     if (command->type != Q_COMMENT_WITH_COMMAND)
04848     {
04849       /* A query that will sent to drizzled */
04850       command->type= Q_QUERY;
04851     }
04852     else
04853     {
04854       /* -- comment that didn't contain a drizzletest command */
04855       command->type= Q_COMMENT;
04856       warning_msg("Suspicious command '--%s' detected, was this intentional? " \
04857                   "Use # instead of -- to avoid this warning",
04858                   command->query);
04859 
04860       if (command->first_word_len &&
04861           strcmp(command->query + command->first_word_len - 1, delimiter) == 0)
04862       {
04863         /*
04864           Detect comment with command using extra delimiter
04865           Ex --disable_query_log;
04866           ^ Extra delimiter causing the command
04867           to be skipped
04868         */
04869         save= command->query[command->first_word_len-1];
04870         command->query[command->first_word_len-1]= 0;
04871         if (command_typelib.find_type(command->query, TYPELIB::e_default) > 0)
04872           die("Extra delimiter \";\" found");
04873         command->query[command->first_word_len-1]= save;
04874 
04875       }
04876     }
04877   }
04878 
04879   /* Set expected error on command */
04880   memcpy(&command->expected_errors, &saved_expected_errors, sizeof(saved_expected_errors));
04881   command->abort_on_error= (command->expected_errors.count == 0 && abort_on_error);
04882 }
04883 
04884 
04885 
04886 /*
04887   Record how many milliseconds it took to execute the test file
04888   up until the current line and save it in the dynamic string ds_progress.
04889 
04890   The ds_progress will be dumped to <test_name>.progress when
04891   test run completes
04892 
04893 */
04894 
04895 static void mark_progress(st_command*, int line)
04896 {
04897   uint64_t timer= timer_now();
04898   if (!progress_start)
04899     progress_start= timer;
04900   timer-= progress_start;
04901 
04902   ostringstream buf;
04903   /* Milliseconds since start */
04904   buf << timer << "\t";
04905 
04906   /* Parser line number */
04907   buf << line << "\t";
04908 
04909   /* Filename */
04910   buf << cur_file->file_name << ":";
04911 
04912   /* Line in file */
04913   buf << cur_file->lineno << endl;
04914 
04915   ds_progress += buf.str();
04916 
04917 }
04918 
04919 static void check_retries(uint32_t in_opt_max_connect_retries)
04920 {
04921   if (in_opt_max_connect_retries > 10000 || opt_max_connect_retries<1)
04922   {
04923     cout << N_("Error: Invalid Value for opt_max_connect_retries"); 
04924     exit(-1);
04925   }
04926   opt_max_connect_retries= in_opt_max_connect_retries;
04927 }
04928 
04929 static void check_tail_lines(uint32_t in_opt_tail_lines)
04930 {
04931   if (in_opt_tail_lines > 10000)
04932   {
04933     cout << N_("Error: Invalid Value for opt_tail_lines"); 
04934     exit(-1);
04935   }
04936   opt_tail_lines= in_opt_tail_lines;
04937 }
04938 
04939 static void check_sleep(int32_t in_opt_sleep)
04940 {
04941   if (in_opt_sleep < -1)
04942   {
04943     cout << N_("Error: Invalid Value for opt_sleep"); 
04944     exit(-1);
04945   }
04946   opt_sleep= in_opt_sleep;
04947 }
04948 
04949 int main(int argc, char **argv)
04950 {
04951 try
04952 {
04953   bool q_send_flag= 0, abort_flag= 0;
04954   uint32_t command_executed= 0, last_command_executed= 0;
04955   string save_file;
04956 
04957   TMPDIR[0]= 0;
04958 
04959   internal::my_init();
04960 
04961   po::options_description commandline_options("Options used only in command line");
04962   commandline_options.add_options()
04963   ("help,?", "Display this help and exit.")
04964   ("mark-progress", po::value<bool>(&opt_mark_progress)->default_value(false)->zero_tokens(),
04965   "Write linenumber and elapsed time to <testname>.progress ")
04966   ("sleep,T", po::value<int32_t>(&opt_sleep)->default_value(-1)->notifier(&check_sleep),
04967   "Sleep always this many seconds on sleep commands.")
04968   ("test-file,x", po::value<string>(),
04969   "Read test from/in this file (default stdin).")
04970   ("timer-file,f", po::value<string>(),
04971   "File where the timing in micro seconds is stored.")
04972   ("tmpdir,t", po::value<string>(),
04973   "Temporary directory where sockets are put.")
04974   ("verbose,v", po::value<bool>(&verbose)->default_value(false),
04975   "Write more.")
04976   ("version,V", "Output version information and exit.")
04977   ("no-defaults", po::value<bool>()->default_value(false)->zero_tokens(),
04978   "Configuration file defaults are not used if no-defaults is set")
04979   ;
04980 
04981   po::options_description test_options("Options specific to the drizzleimport");
04982   test_options.add_options()
04983   ("basedir,b", po::value<string>(&opt_basedir)->default_value(""),
04984   "Basedir for tests.")
04985   ("character-sets-dir", po::value<string>(&opt_charsets_dir)->default_value(""),
04986   "Directory where character sets are.")
04987   ("database,D", po::value<string>(&opt_db)->default_value(""),
04988   "Database to use.")
04989   ("include,i", po::value<string>(&opt_include)->default_value(""),
04990   "Include SQL before each test case.")  
04991   ("testdir", po::value<string>(&opt_testdir)->default_value(""),
04992   "Path to use to search for test files")
04993   ("logdir", po::value<string>(&opt_logdir)->default_value(""),
04994   "Directory for log files")
04995   ("max-connect-retries", po::value<uint32_t>(&opt_max_connect_retries)->default_value(500)->notifier(&check_retries),
04996   "Max number of connection attempts when connecting to server")
04997   ("quiet,s", po::value<bool>(&silent)->default_value(false)->zero_tokens(),
04998   "Suppress all normal output.")
04999   ("record,r", "Record output of test_file into result file.")
05000   ("result-file,R", po::value<string>(&result_file_name)->default_value(""),
05001   "Read/Store result from/in this file.")
05002   ("silent,s", po::value<bool>(&silent)->default_value(false)->zero_tokens(),
05003   "Suppress all normal output. Synonym for --quiet.")
05004   ("tail-lines", po::value<uint32_t>(&opt_tail_lines)->default_value(0)->notifier(&check_tail_lines),
05005   "Number of lines of the resul to include in a failure report")
05006   ;
05007 
05008   po::options_description client_options("Options specific to the client");
05009   client_options.add_options()
05010 
05011   ("host,h", po::value<string>(&opt_host)->default_value("localhost"),
05012   "Connect to host.")
05013   ("password,P", po::value<string>(&password)->default_value("PASSWORD_SENTINEL"),
05014   "Password to use when connecting to server.")
05015   ("port,p", po::value<uint32_t>(&opt_port)->default_value(0),
05016   "Port number to use for connection or 0 for default")
05017   ("protocol", po::value<string>(&opt_protocol),
05018   "The protocol of connection (mysql or drizzle).")
05019   ("user,u", po::value<string>(&opt_user)->default_value(""),
05020   "User for login.")
05021   ;
05022 
05023   po::positional_options_description p;
05024   p.add("database", 1);
05025 
05026   po::options_description long_options("Allowed Options");
05027   long_options.add(commandline_options).add(test_options).add(client_options);
05028 
05029   std::string system_config_dir_test(SYSCONFDIR); 
05030   system_config_dir_test += "/drizzle/drizzletest.cnf";
05031 
05032   std::string system_config_dir_client(SYSCONFDIR); 
05033   system_config_dir_client += "/drizzle/client.cnf";
05034 
05035   std::string user_config_dir((getenv("XDG_CONFIG_HOME")? getenv("XDG_CONFIG_HOME"):"~/.config"));
05036 
05037   if (user_config_dir.compare(0, 2, "~/") == 0)
05038   {
05039     if (const char *homedir= getenv("HOME"))
05040       user_config_dir.replace(0, 1, homedir);
05041   }
05042 
05043   po::variables_map vm;
05044 
05045   // Disable allow_guessing
05046   int style = po::command_line_style::default_style & ~po::command_line_style::allow_guessing;
05047 
05048   po::store(po::command_line_parser(argc, argv).options(long_options).
05049             style(style).positional(p).extra_parser(parse_password_arg).run(),
05050             vm);
05051 
05052   if (! vm["no-defaults"].as<bool>())
05053   {
05054     std::string user_config_dir_test(user_config_dir);
05055     user_config_dir_test += "/drizzle/drizzletest.cnf"; 
05056 
05057     std::string user_config_dir_client(user_config_dir);
05058     user_config_dir_client += "/drizzle/client.cnf";
05059 
05060     ifstream user_test_ifs(user_config_dir_test.c_str());
05061     po::store(parse_config_file(user_test_ifs, test_options), vm);
05062 
05063     ifstream user_client_ifs(user_config_dir_client.c_str());
05064     po::store(parse_config_file(user_client_ifs, client_options), vm);
05065 
05066     ifstream system_test_ifs(system_config_dir_test.c_str());
05067     store(parse_config_file(system_test_ifs, test_options), vm);
05068 
05069     ifstream system_client_ifs(system_config_dir_client.c_str());
05070     po::store(parse_config_file(system_client_ifs, client_options), vm);
05071   }
05072 
05073   po::notify(vm);
05074 
05075   /* Init expected errors */
05076   memset(&saved_expected_errors, 0, sizeof(saved_expected_errors));
05077 
05078   /* Init file stack */
05079   memset(file_stack.data(), 0, sizeof(file_stack));
05080   cur_file= file_stack.data();
05081 
05082   /* Init block stack */
05083   memset(block_stack, 0, sizeof(block_stack));
05084   block_stack_end=
05085     block_stack + (sizeof(block_stack)/sizeof(struct st_block)) - 1;
05086   cur_block= block_stack;
05087   cur_block->ok= true; /* Outer block should always be executed */
05088   cur_block->cmd= cmd_none;
05089 
05090   var_set_string("$DRIZZLE_SERVER_VERSION", drizzle_version());
05091 
05092   memset(&master_pos, 0, sizeof(master_pos));
05093 
05094   parser.current_line= parser.read_lines= 0;
05095   memset(&var_reg, 0, sizeof(var_reg));
05096 
05097   init_builtin_echo();
05098 
05099   ds_res.reserve(65536);
05100   ds_progress.reserve(2048);
05101   ds_warning_messages.reserve(2048);
05102 
05103  
05104   if (vm.count("record"))
05105   {
05106     record = 1;
05107   }
05108 
05109   if (vm.count("test-file"))
05110   {
05111     string tmp= vm["test-file"].as<string>();
05112     char buff[FN_REFLEN];
05113     if (!internal::test_if_hard_path(tmp.c_str()))
05114     {
05115       snprintf(buff, sizeof(buff), "%s%s",opt_basedir.c_str(),tmp.c_str());
05116       tmp= buff;
05117     }
05118     internal::fn_format(buff, tmp.c_str(), "", "", MY_UNPACK_FILENAME);
05119     assert(cur_file == file_stack.data() && cur_file->file == 0);
05120     if (!(cur_file->file= fopen(buff, "r")))
05121     {
05122       fprintf(stderr, _("Could not open '%s' for reading: errno = %d"), buff, errno);
05123       return EXIT_ARGUMENT_INVALID;
05124     }
05125     cur_file->file_name= strdup(buff);
05126     cur_file->lineno= 1;
05127   }
05128 
05129   if (vm.count("timer-file"))
05130   {
05131     string tmp= vm["timer-file"].as<string>().c_str();
05132     static char buff[FN_REFLEN];
05133     if (!internal::test_if_hard_path(tmp.c_str()))
05134     {
05135       snprintf(buff, sizeof(buff), "%s%s",opt_basedir.c_str(),tmp.c_str());
05136       tmp= buff;
05137     }
05138     internal::fn_format(buff, tmp.c_str(), "", "", MY_UNPACK_FILENAME);
05139     timer_file= buff;
05140     unlink(timer_file);       /* Ignore error, may not exist */
05141   }
05142 
05143   if (vm.count("protocol"))
05144   {
05145     boost::to_lower(opt_protocol);
05146     if (not opt_protocol.compare("mysql"))
05147       use_drizzle_protocol=false;
05148     else if (not opt_protocol.compare("drizzle"))
05149       use_drizzle_protocol=true;
05150     else
05151     {
05152       cout << _("Error: Unknown protocol") << " '" << opt_protocol << "'" << endl;
05153       exit(-1);
05154     }
05155   }
05156 
05157   if (vm.count("port"))
05158   {
05159     /* If the port number is > 65535 it is not a valid port
05160        This also helps with potential data loss casting unsigned long to a
05161        uint32_t. */
05162     if (opt_port > 65535)
05163     {
05164       fprintf(stderr, _("Value supplied for port is not valid.\n"));
05165       exit(EXIT_ARGUMENT_INVALID);
05166     }
05167   }
05168 
05169   if( vm.count("password") )
05170   {
05171     if (!opt_password.empty())
05172       opt_password.erase();
05173     if (password == PASSWORD_SENTINEL)
05174     {
05175       opt_password= "";
05176     }
05177     else
05178     {
05179       opt_password= password;
05180       tty_password= false;
05181     }
05182   }
05183   else
05184   {
05185       tty_password= true;
05186   }
05187 
05188   if (vm.count("tmpdir"))
05189   {
05190     strncpy(TMPDIR, vm["tmpdir"].as<string>().c_str(), sizeof(TMPDIR));
05191   }
05192 
05193   if (vm.count("version"))
05194   {
05195     printf("%s  Ver %s Distrib %s, for %s-%s (%s)\n",internal::my_progname,MTEST_VERSION,
05196     drizzle_version(),HOST_VENDOR,HOST_OS,HOST_CPU);
05197     exit(0);
05198   }
05199   
05200   if (vm.count("help"))
05201   {
05202     printf("%s  Ver %s Distrib %s, for %s-%s (%s)\n",internal::my_progname,MTEST_VERSION,
05203     drizzle_version(),HOST_VENDOR,HOST_OS,HOST_CPU);
05204     printf("MySQL AB, by Sasha, Matt, Monty & Jani\n");
05205     printf("Drizzle version modified by Brian, Jay, Monty Taylor, PatG and Stewart\n");
05206     printf("This software comes with ABSOLUTELY NO WARRANTY\n\n");
05207     printf("Runs a test against the DRIZZLE server and compares output with a results file.\n\n");
05208     printf("Usage: %s [OPTIONS] [database] < test_file\n", internal::my_progname);
05209     exit(0);
05210   }
05211 
05212   if (tty_password)
05213   {
05214     opt_pass= client_get_tty_password(NULL);          /* purify tested */
05215   }
05216 
05217   server_initialized= true;
05218   if (cur_file == file_stack.data() && cur_file->file == 0)
05219   {
05220     cur_file->file= stdin;
05221     cur_file->file_name= strdup("<stdin>");
05222     cur_file->lineno= 1;
05223   }
05224   cur_con= safe_connect("default", opt_host, opt_user, opt_pass, opt_db, opt_port);
05225   g_connections["default"] = cur_con;
05226 
05227   fill_global_error_names();
05228 
05229   /* Use all time until exit if no explicit 'start_timer' */
05230   timer_start= timer_now();
05231 
05232   /*
05233     Initialize $drizzleclient_errno with -1, so we can
05234     - distinguish it from valid values ( >= 0 ) and
05235     - detect if there was never a command sent to the server
05236   */
05237   var_set_errno(-1);
05238 
05239   /* Update $drizzleclient_get_server_version to that of current connection */
05240   var_set_drizzleclient_get_server_version(*cur_con);
05241 
05242   if (! opt_include.empty())
05243   {
05244     open_file(opt_include.c_str());
05245   }
05246 
05247   st_command* command;
05248   while (!read_command(&command) && !abort_flag)
05249   {
05250     int current_line_inc = 1, processed = 0;
05251     if (command->type == Q_UNKNOWN || command->type == Q_COMMENT_WITH_COMMAND)
05252       get_command_type(command);
05253 
05254     if (parsing_disabled &&
05255         command->type != Q_ENABLE_PARSING &&
05256         command->type != Q_DISABLE_PARSING)
05257     {
05258       command->type= Q_COMMENT;
05259       scan_command_for_warnings(command);
05260     }
05261 
05262     if (cur_block->ok)
05263     {
05264       command->last_argument= command->first_argument;
05265       processed = 1;
05266       switch (command->type) {
05267       case Q_CONNECT:
05268         do_connect(command);
05269         break;
05270       case Q_CONNECTION:
05271         select_connection(command);
05272         break;
05273       case Q_DISCONNECT:
05274       case Q_DIRTY_CLOSE:
05275         do_close_connection(command); break;
05276       case Q_ENABLE_QUERY_LOG:   disable_query_log=0; break;
05277       case Q_DISABLE_QUERY_LOG:  disable_query_log=1; break;
05278       case Q_ENABLE_ABORT_ON_ERROR:  abort_on_error=1; break;
05279       case Q_DISABLE_ABORT_ON_ERROR: abort_on_error=0; break;
05280       case Q_ENABLE_RESULT_LOG:  disable_result_log=0; break;
05281       case Q_DISABLE_RESULT_LOG: disable_result_log=1; break;
05282       case Q_ENABLE_WARNINGS:    disable_warnings=0; break;
05283       case Q_DISABLE_WARNINGS:   disable_warnings=1; break;
05284       case Q_ENABLE_INFO:        disable_info=0; break;
05285       case Q_DISABLE_INFO:       disable_info=1; break;
05286       case Q_ENABLE_METADATA:    display_metadata=1; break;
05287       case Q_DISABLE_METADATA:   display_metadata=0; break;
05288       case Q_SOURCE: do_source(command); break;
05289       case Q_SLEEP: do_sleep(command, 0); break;
05290       case Q_REAL_SLEEP: do_sleep(command, 1); break;
05291       case Q_WAIT_FOR_SLAVE_TO_STOP: do_wait_for_slave_to_stop(); break;
05292       case Q_INC: do_modify_var(command, DO_INC); break;
05293       case Q_DEC: do_modify_var(command, DO_DEC); break;
05294       case Q_ECHO: do_echo(command); command_executed++; break;
05295       case Q_SYSTEM: do_system(command); break;
05296       case Q_REMOVE_FILE: do_remove_file(command); break;
05297       case Q_MKDIR: do_mkdir(command); break;
05298       case Q_RMDIR: do_rmdir(command); break;
05299       case Q_FILE_EXIST: do_file_exist(command); break;
05300       case Q_WRITE_FILE: do_write_file(command); break;
05301       case Q_APPEND_FILE: do_append_file(command); break;
05302       case Q_DIFF_FILES: do_diff_files(command); break;
05303       case Q_SEND_QUIT: do_send_quit(command); break;
05304       case Q_CHANGE_USER: do_change_user(command); break;
05305       case Q_CAT_FILE: do_cat_file(command); break;
05306       case Q_COPY_FILE: do_copy_file(command); break;
05307       case Q_CHMOD_FILE: do_chmod_file(command); break;
05308       case Q_PERL: do_perl(command); break;
05309       case Q_DELIMITER:
05310         do_delimiter(command);
05311         break;
05312       case Q_DISPLAY_VERTICAL_RESULTS:
05313         display_result_vertically= true;
05314         break;
05315       case Q_DISPLAY_HORIZONTAL_RESULTS:
05316         display_result_vertically= false;
05317         break;
05318       case Q_SORTED_RESULT:
05319         /*
05320           Turn on sorting of result set, will be reset after next
05321           command
05322         */
05323         display_result_sorted= true;
05324         break;
05325       case Q_LET: do_let(command); break;
05326       case Q_EVAL_RESULT:
05327         die("'eval_result' command  is deprecated");
05328       case Q_EVAL:
05329       case Q_QUERY_VERTICAL:
05330       case Q_QUERY_HORIZONTAL:
05331         if (command->query == command->query_buf)
05332         {
05333           /* Skip the first part of command, i.e query_xxx */
05334           command->query= command->first_argument;
05335           command->first_word_len= 0;
05336         }
05337         /* fall through */
05338       case Q_QUERY:
05339       case Q_REAP:
05340       {
05341         bool old_display_result_vertically= display_result_vertically;
05342         /* Default is full query, both reap and send  */
05343         int flags= QUERY_REAP_FLAG | QUERY_SEND_FLAG;
05344 
05345         if (q_send_flag)
05346         {
05347           /* Last command was an empty 'send' */
05348           flags= QUERY_SEND_FLAG;
05349           q_send_flag= 0;
05350         }
05351         else if (command->type == Q_REAP)
05352         {
05353           flags= QUERY_REAP_FLAG;
05354         }
05355 
05356         /* Check for special property for this query */
05357         display_result_vertically|= (command->type == Q_QUERY_VERTICAL);
05358 
05359         if (! save_file.empty())
05360         {
05361           command->require_file= save_file;
05362           save_file.clear();
05363         }
05364         run_query(*cur_con, command, flags);
05365         command_executed++;
05366         command->last_argument= command->end;
05367 
05368         /* Restore settings */
05369         display_result_vertically= old_display_result_vertically;
05370 
05371         break;
05372       }
05373       case Q_SEND:
05374         if (!*command->first_argument)
05375         {
05376           /*
05377             This is a send without arguments, it indicates that _next_ query
05378             should be send only
05379           */
05380           q_send_flag= 1;
05381           break;
05382         }
05383 
05384         /* Remove "send" if this is first iteration */
05385         if (command->query == command->query_buf)
05386           command->query= command->first_argument;
05387 
05388         /*
05389           run_query() can execute a query partially, depending on the flags.
05390           QUERY_SEND_FLAG flag without QUERY_REAP_FLAG tells it to just send
05391           the query and read the result some time later when reap instruction
05392           is given on this connection.
05393         */
05394         run_query(*cur_con, command, QUERY_SEND_FLAG);
05395         command_executed++;
05396         command->last_argument= command->end;
05397         break;
05398       case Q_REQUIRE:
05399         do_get_file_name(command, save_file);
05400         break;
05401       case Q_ERROR:
05402         do_get_errcodes(command);
05403         break;
05404       case Q_REPLACE:
05405         do_get_replace(command);
05406         break;
05407       case Q_REPLACE_REGEX:
05408         do_get_replace_regex(command);
05409         break;
05410       case Q_REPLACE_COLUMN:
05411         do_get_replace_column(command);
05412         break;
05413       case Q_SAVE_MASTER_POS: do_save_master_pos(); break;
05414       case Q_SYNC_WITH_MASTER: do_sync_with_master(command); break;
05415       case Q_SYNC_SLAVE_WITH_MASTER:
05416       {
05417         do_save_master_pos();
05418         if (*command->first_argument)
05419           select_connection(command);
05420         else
05421           select_connection_name("slave");
05422         do_sync_with_master2(0);
05423         break;
05424       }
05425       case Q_COMMENT:        /* Ignore row */
05426         command->last_argument= command->end;
05427         break;
05428       case Q_PING:
05429         {
05430           drizzle::result_c result;
05431           drizzle_return_t ret;
05432           (void) drizzle_ping(*cur_con, result, &ret);
05433         }
05434         break;
05435       case Q_EXEC:
05436         do_exec(command);
05437         command_executed++;
05438         break;
05439       case Q_START_TIMER:
05440         /* Overwrite possible earlier start of timer */
05441         timer_start= timer_now();
05442         break;
05443       case Q_END_TIMER:
05444         /* End timer before ending drizzletest */
05445         timer_output();
05446         break;
05447       case Q_CHARACTER_SET:
05448         do_set_charset(command);
05449         break;
05450       case Q_DISABLE_RECONNECT:
05451         set_reconnect(*cur_con, 0);
05452         break;
05453       case Q_ENABLE_RECONNECT:
05454         set_reconnect(*cur_con, 1);
05455         break;
05456       case Q_DISABLE_PARSING:
05457         if (parsing_disabled == 0)
05458           parsing_disabled= 1;
05459         else
05460           die("Parsing is already disabled");
05461         break;
05462       case Q_ENABLE_PARSING:
05463         /*
05464           Ensure we don't get parsing_disabled < 0 as this would accidentally
05465           disable code we don't want to have disabled
05466         */
05467         if (parsing_disabled == 1)
05468           parsing_disabled= 0;
05469         else
05470           die("Parsing is already enabled");
05471         break;
05472       case Q_DIE:
05473         /* Abort test with error code and error message */
05474         die("%s", command->first_argument);
05475         break;
05476       case Q_EXIT:
05477         /* Stop processing any more commands */
05478         abort_flag= 1;
05479         break;
05480       case Q_SKIP:
05481         abort_not_supported_test("%s", command->first_argument);
05482         break;
05483 
05484       case Q_RESULT:
05485         die("result, deprecated command");
05486         break;
05487 
05488       default:
05489         processed= 0;
05490         break;
05491       }
05492     }
05493 
05494     if (!processed)
05495     {
05496       current_line_inc= 0;
05497       switch (command->type) {
05498       case Q_WHILE: do_block(cmd_while, command); break;
05499       case Q_IF: do_block(cmd_if, command); break;
05500       case Q_END_BLOCK: do_done(command); break;
05501       default: current_line_inc = 1; break;
05502       }
05503     }
05504     else
05505       check_eol_junk(command->last_argument);
05506 
05507     if (command->type != Q_ERROR &&
05508         command->type != Q_COMMENT)
05509     {
05510       /*
05511         As soon as any non "error" command or comment has been executed,
05512         the array with expected errors should be cleared
05513       */
05514       memset(&saved_expected_errors, 0, sizeof(saved_expected_errors));
05515     }
05516 
05517     if (command_executed != last_command_executed)
05518     {
05519       /*
05520         As soon as any command has been executed,
05521         the replace structures should be cleared
05522       */
05523       free_all_replace();
05524 
05525       /* Also reset "sorted_result" */
05526       display_result_sorted= false;
05527     }
05528     last_command_executed= command_executed;
05529 
05530     parser.current_line += current_line_inc;
05531     if ( opt_mark_progress )
05532       mark_progress(command, parser.current_line);
05533   }
05534 
05535   start_lineno= 0;
05536 
05537   if (parsing_disabled)
05538     die("Test ended with parsing disabled");
05539 
05540   /*
05541     The whole test has been executed _sucessfully_.
05542     Time to compare result or save it to record file.
05543     The entire output from test is now kept in ds_res.
05544   */
05545   if (ds_res.empty())
05546     die("The test didn't produce any output");
05547   if (result_file_name.empty())
05548   {
05549     /* No result_file_name specified to compare with, print to stdout */
05550     printf("%s", ds_res.c_str());
05551   }
05552   else if (record)
05553   {
05554     /* Recording - dump the output from test to result file */
05555     str_to_file(result_file_name.c_str(), ds_res.c_str(), ds_res.length());
05556   }
05557   else
05558   {
05559     /* Check that the output from test is equal to result file
05560     - detect missing result file
05561     - detect zero size result file
05562     */
05563     check_result(ds_res);
05564   }
05565 
05566   struct stat res_info;
05567   if (not command_executed && not result_file_name.empty() && not stat(result_file_name.c_str(), &res_info))
05568   {
05569     /*
05570       my_stat() successful on result file. Check if we have not run a
05571       single query, but we do have a result file that contains data.
05572       Note that we don't care, if my_stat() fails. For example, for a
05573       non-existing or non-readable file, we assume it's fine to have
05574       no query output from the test file, e.g. regarded as no error.
05575     */
05576     die("No queries executed but result file found!");
05577   }
05578 
05579   if ( opt_mark_progress && ! result_file_name.empty() )
05580     dump_progress();
05581 
05582   /* Dump warning messages */
05583   if (not result_file_name.empty() && ds_warning_messages.length())
05584     dump_warning_messages();
05585 
05586   timer_output();
05587   /* Yes, if we got this far the test has suceeded! Sakila smiles */
05588   cleanup_and_exit(0);
05589 }
05590 
05591   catch(exception &err)
05592   {
05593     cerr<<err.what()<<endl;
05594   }
05595 
05596   return 0;
05597 }
05598 
05599 
05600 /*
05601   A primitive timer that give results in milliseconds if the
05602   --timer-file=<filename> is given. The timer result is written
05603   to that file when the result is available. To not confuse
05604   mysql-test-run with an old obsolete result, we remove the file
05605   before executing any commands. The time we measure is
05606 
05607   - If no explicit 'start_timer' or 'end_timer' is given in the
05608   test case, the timer measure how long we execute in drizzletest.
05609 
05610   - If only 'start_timer' is given we measure how long we execute
05611   from that point until we terminate drizzletest.
05612 
05613   - If only 'end_timer' is given we measure how long we execute
05614   from that we enter drizzletest to the 'end_timer' is command is
05615   executed.
05616 
05617   - If both 'start_timer' and 'end_timer' are given we measure
05618   the time between executing the two commands.
05619 */
05620 
05621 void timer_output()
05622 {
05623   if (timer_file)
05624   {
05625     ostringstream buf;
05626     uint64_t timer= timer_now() - timer_start;
05627     buf << timer;
05628     str_to_file(timer_file,buf.str().c_str(), buf.str().size() );
05629     /* Timer has been written to the file, don't use it anymore */
05630     timer_file= 0;
05631   }
05632 }
05633 
05634 
05635 uint64_t timer_now()
05636 {
05637 #if defined(HAVE_GETHRTIME)
05638   return gethrtime()/1000/1000;
05639 #else
05640   uint64_t newtime;
05641   struct timeval t;
05642   /*
05643     The following loop is here because gettimeofday may fail on some systems
05644   */
05645   while (gettimeofday(&t, NULL) != 0)
05646   {}
05647   newtime= (uint64_t)t.tv_sec * 1000000 + t.tv_usec;
05648   return newtime/1000;
05649 #endif  /* defined(HAVE_GETHRTIME) */
05650 }
05651 
05652 
05653 /*
05654   Get arguments for replace_columns. The syntax is:
05655   replace-column column_number to_string [column_number to_string ...]
05656   Where each argument may be quoted with ' or "
05657   A argument may also be a variable, in which case the value of the
05658   variable is replaced.
05659 */
05660 
05661 void do_get_replace_column(st_command* command)
05662 {
05663   char *from= command->first_argument;
05664   char *buff, *start;
05665 
05666 
05667   free_replace_column();
05668   if (!*from)
05669     die("Missing argument in %s", command->query);
05670 
05671   /* Allocate a buffer for results */
05672   start= buff= (char *)malloc(strlen(from)+1);
05673   while (*from)
05674   {
05675     uint32_t column_number;
05676 
05677     char *to= get_string(&buff, &from, command);
05678     if (!(column_number= atoi(to)) || column_number > MAX_COLUMNS)
05679       die("Wrong column number to replace_column in '%s'", command->query);
05680     if (!*from)
05681       die("Wrong number of arguments to replace_column in '%s'", command->query);
05682     to= get_string(&buff, &from, command);
05683     free(replace_column[column_number-1]);
05684     replace_column[column_number-1]= strdup(to);
05685     set_if_bigger(max_replace_column, column_number);
05686   }
05687   free(start);
05688   command->last_argument= command->end;
05689 }
05690 
05691 
05692 void free_replace_column()
05693 {
05694   for (uint32_t i= 0; i < max_replace_column; i++)
05695   {
05696     free(replace_column[i]);
05697     replace_column[i]= 0;
05698   }
05699   max_replace_column= 0;
05700 }
05701 
05702 
05703 /****************************************************************************/
05704 /*
05705   Replace functions
05706 */
05707 
05708 /* Definitions for replace result */
05709 
05710 class POINTER_ARRAY
05711 {    /* when using array-strings */
05712 public:
05713   ~POINTER_ARRAY();
05714   int insert(char* name);
05715 
05716   POINTER_ARRAY()
05717   {
05718     memset(this, 0, sizeof(*this));
05719   }
05720 
05721   TYPELIB typelib;        /* Pointer to strings */
05722   unsigned char *str;          /* Strings is here */
05723   uint8_t* flag;          /* Flag about each var. */
05724   uint32_t array_allocs;
05725   uint32_t max_count;
05726   uint32_t length;
05727   uint32_t max_length;
05728 };
05729 
05730 struct st_replace;
05731 struct st_replace *init_replace(const char **from, const char **to, uint32_t count,
05732                                 char *word_end_chars);
05733 
05734 void replace_strings_append(struct st_replace *rep, string& ds, const char *from, int len);
05735 
05736 st_replace *glob_replace= NULL;
05737 // boost::scoped_ptr<st_replace> glob_replace;
05738 
05739 /*
05740   Get arguments for replace. The syntax is:
05741   replace from to [from to ...]
05742   Where each argument may be quoted with ' or "
05743   A argument may also be a variable, in which case the value of the
05744   variable is replaced.
05745 */
05746 
05747 POINTER_ARRAY::~POINTER_ARRAY()
05748 {
05749   if (!typelib.count)
05750     return;
05751   typelib.count= 0;
05752   free((char*) typelib.type_names);
05753   typelib.type_names=0;
05754   free(str);
05755 }
05756 
05757 void do_get_replace(st_command* command)
05758 {
05759   char *from= command->first_argument;
05760   if (!*from)
05761     die("Missing argument in %s", command->query);
05762   free_replace();
05763   POINTER_ARRAY to_array, from_array;
05764   char* start= (char*)malloc(strlen(from) + 1);
05765   char* buff= start;
05766   while (*from)
05767   {
05768     char *to= get_string(&buff, &from, command);
05769     if (!*from)
05770       die("Wrong number of arguments to replace_result in '%s'", command->query);
05771     from_array.insert(to);
05772     to= get_string(&buff, &from, command);
05773     to_array.insert(to);
05774   }
05775   char word_end_chars[256];
05776   char* pos= word_end_chars;
05777   for (int i= 1; i < 256; i++)
05778   {
05779     if (charset_info->isspace(i))
05780       *pos++= i;
05781   }
05782   *pos=0;          /* End pointer */
05783   if (!(glob_replace= init_replace(from_array.typelib.type_names,
05784                                    to_array.typelib.type_names,
05785                                    from_array.typelib.count,
05786                                    word_end_chars)))
05787     die("Can't initialize replace from '%s'", command->query);
05788   free(start);
05789   command->last_argument= command->end;
05790   return;
05791 }
05792 
05793 
05794 void free_replace()
05795 {
05796   free(glob_replace);
05797   glob_replace=0;
05798 }
05799 
05800 
05801 typedef struct st_replace {
05802   bool found;
05803   struct st_replace *next[256];
05804 } REPLACE;
05805 
05806 typedef struct st_replace_found {
05807   bool found;
05808   char *replace_string;
05809   uint32_t to_offset;
05810   int from_offset;
05811 } REPLACE_STRING;
05812 
05813 
05814 void replace_strings_append(REPLACE *rep, string& ds, const char *str, int len)
05815 {
05816   REPLACE_STRING *rep_str;
05817   const char* start= str;
05818   const char* from= str;
05819 
05820   REPLACE* rep_pos=rep+1;
05821   for (;;)
05822   {
05823     /* Loop through states */
05824     while (!rep_pos->found)
05825       rep_pos= rep_pos->next[(unsigned char) *from++];
05826 
05827     /* Does this state contain a string to be replaced */
05828     if (!(rep_str = ((REPLACE_STRING*) rep_pos))->replace_string)
05829     {
05830       /* No match found */
05831       ds.append(start, from - start - 1);
05832       return;
05833     }
05834 
05835     /* Append part of original string before replace string */
05836     ds.append(start, (from - rep_str->to_offset) - start);
05837 
05838     /* Append replace string */
05839     ds.append(rep_str->replace_string, strlen(rep_str->replace_string));
05840 
05841     if (!*(from-=rep_str->from_offset) && rep_pos->found != 2)
05842       return;
05843 
05844     assert(from <= str+len);
05845     start= from;
05846     rep_pos=rep;
05847   }
05848 }
05849 
05850 
05851 /*
05852   Regex replace  functions
05853 */
05854 
05855 
05856 /* Stores regex substitutions */
05857 
05858 struct st_regex
05859 {
05860   char* pattern; /* Pattern to be replaced */
05861   char* replace; /* String or expression to replace the pattern with */
05862   int icase; /* true if the match is case insensitive */
05863   int global; /* true if the match should be global -- 
05864                  i.e. repeat the matching until the end of the string */
05865 };
05866 
05867 class st_replace_regex
05868 {
05869 public:
05870   st_replace_regex(char* expr);
05871   int multi_reg_replace(char* val);
05872 
05873   /*
05874     Temporary storage areas for substitutions. To reduce unnessary copying
05875     and memory freeing/allocation, we pre-allocate two buffers, and alternate
05876     their use, one for input/one for output, the roles changing on the next
05877     st_regex substition. At the end of substitutions  buf points to the
05878     one containing the final result.
05879   */
05880   typedef vector<st_regex> regex_arr_t;
05881 
05882   char* buf_;
05883   char* even_buf;
05884   char* odd_buf;
05885   int even_buf_len;
05886   int odd_buf_len;
05887   boost::array<char, 8 << 10> buf0_;
05888   boost::array<char, 8 << 10> buf1_;
05889   regex_arr_t regex_arr;
05890 };
05891 
05892 boost::scoped_ptr<st_replace_regex> glob_replace_regex;
05893 
05894 int reg_replace(char** buf_p, int* buf_len_p, char *pattern, char *replace,
05895                 char *string, int icase, int global);
05896 
05897 
05898 
05899 /*
05900   Finds the next (non-escaped) '/' in the expression.
05901   (If the character '/' is needed, it can be escaped using '\'.)
05902 */
05903 
05904 #define PARSE_REGEX_ARG                         \
05905   while (p < expr_end)                          \
05906   {                                             \
05907     char c= *p;                                 \
05908     if (c == '/')                               \
05909     {                                           \
05910       if (last_c == '\\')                       \
05911       {                                         \
05912         buf_p[-1]= '/';                         \
05913       }                                         \
05914       else                                      \
05915       {                                         \
05916         *buf_p++ = 0;                           \
05917         break;                                  \
05918       }                                         \
05919     }                                           \
05920     else                                        \
05921       *buf_p++ = c;                             \
05922                                                 \
05923     last_c= c;                                  \
05924     p++;                                        \
05925   }                                             \
05926                                                 \
05927 /*
05928   Initializes the regular substitution expression to be used in the
05929   result output of test.
05930 
05931   Returns: st_replace_regex struct with pairs of substitutions
05932 */
05933 
05934 st_replace_regex::st_replace_regex(char* expr)
05935 {
05936   uint32_t expr_len= strlen(expr);
05937   char last_c = 0;
05938   st_regex reg;
05939 
05940   char* buf= new char[expr_len];
05941   char* expr_end= expr + expr_len;
05942   char* p= expr;
05943   char* buf_p= buf;
05944 
05945   /* for each regexp substitution statement */
05946   while (p < expr_end)
05947   {
05948     memset(&reg, 0, sizeof(reg));
05949     /* find the start of the statement */
05950     while (p < expr_end)
05951     {
05952       if (*p == '/')
05953         break;
05954       p++;
05955     }
05956 
05957     if (p == expr_end || ++p == expr_end)
05958     {
05959       if (!regex_arr.empty())
05960         break;
05961       else
05962         goto err;
05963     }
05964     /* we found the start */
05965     reg.pattern= buf_p;
05966 
05967     /* Find first argument -- pattern string to be removed */
05968     PARSE_REGEX_ARG
05969 
05970       if (p == expr_end || ++p == expr_end)
05971         goto err;
05972 
05973     /* buf_p now points to the replacement pattern terminated with \0 */
05974     reg.replace= buf_p;
05975 
05976     /* Find second argument -- replace string to replace pattern */
05977     PARSE_REGEX_ARG
05978 
05979       if (p == expr_end)
05980         goto err;
05981 
05982     /* skip the ending '/' in the statement */
05983     p++;
05984 
05985     /* Check if we should do matching case insensitive */
05986     if (p < expr_end && *p == 'i')
05987     {
05988       p++;
05989       reg.icase= 1;
05990     }
05991 
05992     /* Check if we should do matching globally */
05993     if (p < expr_end && *p == 'g')
05994     {
05995       p++;
05996       reg.global= 1;
05997     }
05998     regex_arr.push_back(reg);
05999   }
06000   odd_buf_len= even_buf_len= buf0_.size();
06001   even_buf= buf0_.data();
06002   odd_buf= buf1_.data();
06003   buf_= even_buf;
06004 
06005   return;
06006 
06007 err:
06008   die("Error parsing replace_regex \"%s\"", expr);
06009 }
06010 
06011 /*
06012   Execute all substitutions on val.
06013 
06014   Returns: true if substituition was made, false otherwise
06015   Side-effect: Sets r->buf to be the buffer with all substitutions done.
06016 
06017   IN:
06018   struct st_replace_regex* r
06019   char* val
06020   Out:
06021   struct st_replace_regex* r
06022   r->buf points at the resulting buffer
06023   r->even_buf and r->odd_buf might have been reallocated
06024   r->even_buf_len and r->odd_buf_len might have been changed
06025 
06026   TODO:  at some point figure out if there is a way to do everything
06027   in one pass
06028 */
06029 
06030 int st_replace_regex::multi_reg_replace(char* val)
06031 {
06032   char* in_buf= val;
06033   char* out_buf= even_buf;
06034   int* buf_len_p= &even_buf_len;
06035   buf_= 0;
06036 
06037   /* For each substitution, do the replace */
06038   BOOST_FOREACH(regex_arr_t::const_reference i, regex_arr)
06039   {
06040     char* save_out_buf= out_buf;
06041     if (!reg_replace(&out_buf, buf_len_p, i.pattern, i.replace,
06042                      in_buf, i.icase, i.global))
06043     {
06044       /* if the buffer has been reallocated, make adjustements */
06045       if (save_out_buf != out_buf)
06046       {
06047         if (save_out_buf == even_buf)
06048           even_buf= out_buf;
06049         else
06050           odd_buf= out_buf;
06051       }
06052       buf_= out_buf;
06053       if (in_buf == val)
06054         in_buf= odd_buf;
06055       std::swap(in_buf, out_buf);
06056       buf_len_p= (out_buf == even_buf) ? &even_buf_len : &odd_buf_len;
06057     }
06058   }
06059   return buf_ == 0;
06060 }
06061 
06062 /*
06063   Parse the regular expression to be used in all result files
06064   from now on.
06065 
06066   The syntax is --replace_regex /from/to/i /from/to/i ...
06067   i means case-insensitive match. If omitted, the match is
06068   case-sensitive
06069 
06070 */
06071 void do_get_replace_regex(st_command* command)
06072 {
06073   char *expr= command->first_argument;
06074   glob_replace_regex.reset(new st_replace_regex(expr));
06075   command->last_argument= command->end;
06076 }
06077 
06078 /*
06079   Performs a regex substitution
06080 
06081   IN:
06082 
06083   buf_p - result buffer pointer. Will change if reallocated
06084   buf_len_p - result buffer length. Will change if the buffer is reallocated
06085   pattern - regexp pattern to match
06086   replace - replacement expression
06087   string - the string to perform substituions in
06088   icase - flag, if set to 1 the match is case insensitive
06089 */
06090 int reg_replace(char** buf_p, int* buf_len_p, char *pattern,
06091                 char *replace, char *in_string, int icase, int global)
06092 {
06093   const char *error= NULL;
06094   int erroffset;
06095   int ovector[3];
06096   pcre *re= pcre_compile(pattern,
06097                          icase ? PCRE_CASELESS | PCRE_MULTILINE : PCRE_MULTILINE,
06098                          &error, &erroffset, NULL);
06099   if (re == NULL)
06100     return 1;
06101 
06102   if (! global)
06103   {
06104 
06105     int rc= pcre_exec(re, NULL, in_string, (int)strlen(in_string),
06106                       0, 0, ovector, 3);
06107     if (rc < 0)
06108     {
06109       pcre_free(re);
06110       return 1;
06111     }
06112 
06113     char *substring_to_replace= in_string + ovector[0];
06114     int substring_length= ovector[1] - ovector[0];
06115     *buf_len_p= strlen(in_string) - substring_length + strlen(replace);
06116     char* new_buf= (char*)malloc(*buf_len_p+1);
06117 
06118     memset(new_buf, 0, *buf_len_p+1);
06119     strncpy(new_buf, in_string, substring_to_replace-in_string);
06120     strncpy(new_buf+(substring_to_replace-in_string), replace, strlen(replace));
06121     strncpy(new_buf+(substring_to_replace-in_string)+strlen(replace),
06122             substring_to_replace + substring_length,
06123             strlen(in_string)
06124               - substring_length
06125               - (substring_to_replace-in_string));
06126     *buf_p= new_buf;
06127 
06128     pcre_free(re);
06129     return 0;
06130   }
06131   else
06132   {
06133     /* Repeatedly replace the string with the matched regex */
06134     string subject(in_string);
06135     size_t replace_length= strlen(replace);
06136     size_t length_of_replacement= strlen(replace);
06137     size_t current_position= 0;
06138     int rc= 0;
06139 
06140     while (true) 
06141     {
06142       rc= pcre_exec(re, NULL, subject.c_str(), subject.length(), 
06143                     current_position, 0, ovector, 3);
06144       if (rc < 0)
06145       {
06146         break;
06147       }
06148 
06149       current_position= static_cast<size_t>(ovector[0]);
06150       replace_length= static_cast<size_t>(ovector[1] - ovector[0]);
06151       subject.replace(current_position, replace_length, replace, length_of_replacement);
06152       current_position= current_position + length_of_replacement;
06153     }
06154 
06155     char* new_buf = (char*) malloc(subject.length() + 1);
06156     memset(new_buf, 0, subject.length() + 1);
06157     strncpy(new_buf, subject.c_str(), subject.length());
06158     *buf_len_p= subject.length() + 1;
06159     *buf_p= new_buf;
06160           
06161     pcre_free(re);
06162     return 0;
06163   }
06164 }
06165 
06166 
06167 #ifndef WORD_BIT
06168 #define WORD_BIT (8*sizeof(uint32_t))
06169 #endif
06170 
06171 #define SET_MALLOC_HUNC 64
06172 #define LAST_CHAR_CODE 259
06173 
06174 class REP_SET
06175 {
06176 public:
06177   void internal_set_bit(uint32_t bit);
06178   void internal_clear_bit(uint32_t bit);
06179   void or_bits(const REP_SET *from);
06180   void copy_bits(const REP_SET *from);
06181   int cmp_bits(const REP_SET *set2) const;
06182   int get_next_bit(uint32_t lastpos) const;
06183 
06184   uint32_t  *bits;        /* Pointer to used sets */
06185   short next[LAST_CHAR_CODE];    /* Pointer to next sets */
06186   uint32_t  found_len;      /* Best match to date */
06187   int  found_offset;
06188   uint32_t  table_offset;
06189   uint32_t  size_of_bits;      /* For convinience */
06190 };
06191 
06192 class REP_SETS
06193 {
06194 public:
06195   int find_set(const REP_SET *find);
06196   void free_last_set();
06197   void free_sets();
06198   void make_sets_invisible();
06199 
06200   uint32_t    count;      /* Number of sets */
06201   uint32_t    extra;      /* Extra sets in buffer */
06202   uint32_t    invisible;    /* Sets not shown */
06203   uint32_t    size_of_bits;
06204   REP_SET  *set,*set_buffer;
06205   uint32_t    *bit_buffer;
06206 };
06207 
06208 struct FOUND_SET 
06209 {
06210   uint32_t table_offset;
06211   int found_offset;
06212 };
06213 
06214 struct FOLLOWS
06215 {
06216   int chr;
06217   uint32_t table_offset;
06218   uint32_t len;
06219 };
06220 
06221 void init_sets(REP_SETS *sets, uint32_t states);
06222 REP_SET *make_new_set(REP_SETS *sets);
06223 int find_found(FOUND_SET *found_set, uint32_t table_offset, int found_offset);
06224 
06225 static uint32_t found_sets= 0;
06226 
06227 static uint32_t replace_len(const char *str)
06228 {
06229   uint32_t len=0;
06230   while (*str)
06231   {
06232     if (str[0] == '\\' && str[1])
06233       str++;
06234     str++;
06235     len++;
06236   }
06237   return len;
06238 }
06239 
06240 /* Return 1 if regexp starts with \b or ends with \b*/
06241 
06242 static bool start_at_word(const char *pos)
06243 {
06244   return (!memcmp(pos, "\\b",2) && pos[2]) || !memcmp(pos, "\\^", 2);
06245 }
06246 
06247 static bool end_of_word(const char *pos)
06248 {
06249   const char *end= strchr(pos, '\0');
06250   return (end > pos+2 && !memcmp(end-2, "\\b", 2)) || (end >= pos+2 && !memcmp(end-2, "\\$",2));
06251 }
06252 
06253 /* Init a replace structure for further calls */
06254 
06255 REPLACE *init_replace(const char **from, const char **to, uint32_t count, char *word_end_chars)
06256 {
06257   const int SPACE_CHAR= 256;
06258   const int START_OF_LINE= 257;
06259   const int END_OF_LINE= 258;
06260 
06261   uint32_t i,j,states,set_nr,len,result_len,max_length,found_end,bits_set,bit_nr;
06262   int used_sets,chr,default_state;
06263   char used_chars[LAST_CHAR_CODE],is_word_end[256];
06264   char *to_pos, **to_array;
06265 
06266   /* Count number of states */
06267   for (i=result_len=max_length=0 , states=2; i < count; i++)
06268   {
06269     len=replace_len(from[i]);
06270     if (!len)
06271     {
06272       errno=EINVAL;
06273       return(0);
06274     }
06275     states+=len+1;
06276     result_len+=(uint32_t) strlen(to[i])+1;
06277     if (len > max_length)
06278       max_length=len;
06279   }
06280   memset(is_word_end, 0, sizeof(is_word_end));
06281   for (i=0; word_end_chars[i]; i++)
06282     is_word_end[(unsigned char) word_end_chars[i]]=1;
06283 
06284   REP_SETS sets;
06285   REP_SET *set,*start_states,*word_states,*new_set;
06286   REPLACE_STRING *rep_str;
06287   init_sets(&sets, states);
06288   found_sets=0;
06289   vector<FOUND_SET> found_set(max_length * count);
06290   make_new_set(&sets);      /* Set starting set */
06291   sets.make_sets_invisible();      /* Hide previus sets */
06292   used_sets=-1;
06293   word_states=make_new_set(&sets);    /* Start of new word */
06294   start_states=make_new_set(&sets);    /* This is first state */
06295   vector<FOLLOWS> follow(states + 2);
06296   FOLLOWS *follow_ptr= &follow[1];
06297   /* Init follow_ptr[] */
06298   for (i=0, states=1; i < count; i++)
06299   {
06300     if (from[i][0] == '\\' && from[i][1] == '^')
06301     {
06302       start_states->internal_set_bit(states + 1);
06303       if (!from[i][2])
06304       {
06305         start_states->table_offset=i;
06306         start_states->found_offset=1;
06307       }
06308     }
06309     else if (from[i][0] == '\\' && from[i][1] == '$')
06310     {
06311       start_states->internal_set_bit(states);
06312       word_states->internal_set_bit(states);
06313       if (!from[i][2] && start_states->table_offset == UINT32_MAX)
06314       {
06315         start_states->table_offset=i;
06316         start_states->found_offset=0;
06317       }
06318     }
06319     else
06320     {
06321       word_states->internal_set_bit(states);
06322       if (from[i][0] == '\\' && (from[i][1] == 'b' && from[i][2]))
06323         start_states->internal_set_bit(states + 1);
06324       else
06325         start_states->internal_set_bit(states);
06326     }
06327     const char *pos;
06328     for (pos= from[i], len=0; *pos; pos++)
06329     {
06330       if (*pos == '\\' && *(pos+1))
06331       {
06332         pos++;
06333         switch (*pos) {
06334         case 'b':
06335           follow_ptr->chr = SPACE_CHAR;
06336           break;
06337         case '^':
06338           follow_ptr->chr = START_OF_LINE;
06339           break;
06340         case '$':
06341           follow_ptr->chr = END_OF_LINE;
06342           break;
06343         case 'r':
06344           follow_ptr->chr = '\r';
06345           break;
06346         case 't':
06347           follow_ptr->chr = '\t';
06348           break;
06349         case 'v':
06350           follow_ptr->chr = '\v';
06351           break;
06352         default:
06353           follow_ptr->chr = (unsigned char) *pos;
06354           break;
06355         }
06356       }
06357       else
06358         follow_ptr->chr= (unsigned char) *pos;
06359       follow_ptr->table_offset=i;
06360       follow_ptr->len= ++len;
06361       follow_ptr++;
06362     }
06363     follow_ptr->chr=0;
06364     follow_ptr->table_offset=i;
06365     follow_ptr->len=len;
06366     follow_ptr++;
06367     states+=(uint32_t) len+1;
06368   }
06369 
06370 
06371   for (set_nr=0; set_nr < sets.count; set_nr++)
06372   {
06373     set=sets.set+set_nr;
06374     default_state= 0;        /* Start from beginning */
06375 
06376     /* If end of found-string not found or start-set with current set */
06377 
06378     for (i= UINT32_MAX; (i= set->get_next_bit(i));)
06379     {
06380       if (!follow[i].chr && !default_state)
06381         default_state= find_found(&found_set.front(), set->table_offset, set->found_offset+1);
06382     }
06383     sets.set[used_sets].copy_bits(set);    /* Save set for changes */
06384     if (!default_state)
06385       sets.set[used_sets].or_bits(sets.set);  /* Can restart from start */
06386 
06387     /* Find all chars that follows current sets */
06388     memset(used_chars, 0, sizeof(used_chars));
06389     for (i= UINT32_MAX; (i= sets.set[used_sets].get_next_bit(i));)
06390     {
06391       used_chars[follow[i].chr]=1;
06392       if ((follow[i].chr == SPACE_CHAR && !follow[i+1].chr &&
06393            follow[i].len > 1) || follow[i].chr == END_OF_LINE)
06394         used_chars[0]=1;
06395     }
06396 
06397     /* Mark word_chars used if \b is in state */
06398     if (used_chars[SPACE_CHAR])
06399       for (const char *pos= word_end_chars; *pos; pos++)
06400         used_chars[(int) (unsigned char) *pos] = 1;
06401 
06402     /* Handle other used characters */
06403     for (chr= 0; chr < 256; chr++)
06404     {
06405       if (! used_chars[chr])
06406         set->next[chr]= chr ? default_state : -1;
06407       else
06408       {
06409         new_set=make_new_set(&sets);
06410         set=sets.set+set_nr;      /* if realloc */
06411         new_set->table_offset=set->table_offset;
06412         new_set->found_len=set->found_len;
06413         new_set->found_offset=set->found_offset+1;
06414         found_end=0;
06415 
06416         for (i= UINT32_MAX; (i= sets.set[used_sets].get_next_bit(i));)
06417         {
06418           if (!follow[i].chr || follow[i].chr == chr ||
06419               (follow[i].chr == SPACE_CHAR &&
06420                (is_word_end[chr] ||
06421                 (!chr && follow[i].len > 1 && ! follow[i+1].chr))) ||
06422               (follow[i].chr == END_OF_LINE && ! chr))
06423           {
06424             if ((! chr || (follow[i].chr && !follow[i+1].chr)) &&
06425                 follow[i].len > found_end)
06426               found_end=follow[i].len;
06427             if (chr && follow[i].chr)
06428               new_set->internal_set_bit(i + 1);    /* To next set */
06429             else
06430               new_set->internal_set_bit(i);
06431           }
06432         }
06433         if (found_end)
06434         {
06435           new_set->found_len=0;      /* Set for testing if first */
06436           bits_set=0;
06437           for (i= UINT32_MAX; (i= new_set->get_next_bit(i));)
06438           {
06439             if ((follow[i].chr == SPACE_CHAR ||
06440                  follow[i].chr == END_OF_LINE) && ! chr)
06441               bit_nr=i+1;
06442             else
06443               bit_nr=i;
06444             if (follow[bit_nr-1].len < found_end ||
06445                 (new_set->found_len &&
06446                  (chr == 0 || !follow[bit_nr].chr)))
06447               new_set->internal_clear_bit(i);
06448             else
06449             {
06450               if (chr == 0 || !follow[bit_nr].chr)
06451               {          /* best match  */
06452                 new_set->table_offset=follow[bit_nr].table_offset;
06453                 if (chr || (follow[i].chr == SPACE_CHAR ||
06454                             follow[i].chr == END_OF_LINE))
06455                   new_set->found_offset=found_end;  /* New match */
06456                 new_set->found_len=found_end;
06457               }
06458               bits_set++;
06459             }
06460           }
06461           if (bits_set == 1)
06462           {
06463             set->next[chr] = find_found(&found_set.front(), new_set->table_offset, new_set->found_offset);
06464             sets.free_last_set();
06465           }
06466           else
06467             set->next[chr] = sets.find_set(new_set);
06468         }
06469         else
06470           set->next[chr] = sets.find_set(new_set);
06471       }
06472     }
06473   }
06474 
06475   /* Alloc replace structure for the replace-state-machine */
06476 
06477   REPLACE *replace= (REPLACE*)malloc(sizeof(REPLACE) * (sets.count)
06478     + sizeof(REPLACE_STRING) * (found_sets + 1) + sizeof(char*) * count + result_len);
06479   {
06480     memset(replace, 0, sizeof(REPLACE)*(sets.count)+
06481                        sizeof(REPLACE_STRING)*(found_sets+1)+
06482                        sizeof(char *)*count+result_len);
06483     rep_str=(REPLACE_STRING*) (replace+sets.count);
06484     to_array= (char **) (rep_str+found_sets+1);
06485     to_pos=(char *) (to_array+count);
06486     for (i=0; i < count; i++)
06487     {
06488       to_array[i]=to_pos;
06489       to_pos=strcpy(to_pos,to[i])+strlen(to[i])+1;
06490     }
06491     rep_str[0].found=1;
06492     rep_str[0].replace_string=0;
06493     for (i=1; i <= found_sets; i++)
06494     {
06495       const char *pos= from[found_set[i-1].table_offset];
06496       rep_str[i].found= !memcmp(pos, "\\^", 3) ? 2 : 1;
06497       rep_str[i].replace_string= to_array[found_set[i-1].table_offset];
06498       rep_str[i].to_offset= found_set[i-1].found_offset-start_at_word(pos);
06499       rep_str[i].from_offset= found_set[i-1].found_offset-replace_len(pos) + end_of_word(pos);
06500     }
06501     for (i=0; i < sets.count; i++)
06502     {
06503       for (j=0; j < 256; j++)
06504         if (sets.set[i].next[j] >= 0)
06505           replace[i].next[j]=replace+sets.set[i].next[j];
06506         else
06507           replace[i].next[j]=(REPLACE*) (rep_str+(-sets.set[i].next[j]-1));
06508     }
06509   }
06510   sets.free_sets();
06511   return replace;
06512 }
06513 
06514 
06515 void init_sets(REP_SETS *sets,uint32_t states)
06516 {
06517   memset(sets, 0, sizeof(*sets));
06518   sets->size_of_bits=((states+7)/8);
06519   sets->set_buffer=(REP_SET*) malloc(sizeof(REP_SET) * SET_MALLOC_HUNC);
06520   sets->bit_buffer=(uint*) malloc(sizeof(uint32_t) * sets->size_of_bits * SET_MALLOC_HUNC);
06521 }
06522 
06523 /* Make help sets invisible for nicer codeing */
06524 
06525 void REP_SETS::make_sets_invisible()
06526 {
06527   invisible= count;
06528   set += count;
06529   count= 0;
06530 }
06531 
06532 REP_SET *make_new_set(REP_SETS *sets)
06533 {
06534   REP_SET *set;
06535   if (sets->extra)
06536   {
06537     sets->extra--;
06538     set=sets->set+ sets->count++;
06539     memset(set->bits, 0, sizeof(uint32_t)*sets->size_of_bits);
06540     memset(&set->next[0], 0, sizeof(set->next[0])*LAST_CHAR_CODE);
06541     set->found_offset=0;
06542     set->found_len=0;
06543     set->table_offset= UINT32_MAX;
06544     set->size_of_bits=sets->size_of_bits;
06545     return set;
06546   }
06547   uint32_t count= sets->count + sets->invisible + SET_MALLOC_HUNC;
06548   set= (REP_SET*) realloc((unsigned char*) sets->set_buffer, sizeof(REP_SET)*count);
06549   sets->set_buffer=set;
06550   sets->set=set+sets->invisible;
06551   uint32_t* bit_buffer= (uint*) realloc((unsigned char*) sets->bit_buffer, (sizeof(uint32_t)*sets->size_of_bits)*count);
06552   sets->bit_buffer=bit_buffer;
06553   for (uint32_t i= 0; i < count; i++)
06554   {
06555     sets->set_buffer[i].bits=bit_buffer;
06556     bit_buffer+=sets->size_of_bits;
06557   }
06558   sets->extra=SET_MALLOC_HUNC;
06559   return make_new_set(sets);
06560 }
06561 
06562 void REP_SETS::free_last_set()
06563 {
06564   count--;
06565   extra++;
06566 }
06567 
06568 void REP_SETS::free_sets()
06569 {
06570   free(set_buffer);
06571   free(bit_buffer);
06572 }
06573 
06574 void REP_SET::internal_set_bit(uint32_t bit)
06575 {
06576   bits[bit / WORD_BIT] |= 1 << (bit % WORD_BIT);
06577 }
06578 
06579 void REP_SET::internal_clear_bit(uint32_t bit)
06580 {
06581   bits[bit / WORD_BIT] &= ~ (1 << (bit % WORD_BIT));
06582 }
06583 
06584 
06585 void REP_SET::or_bits(const REP_SET *from)
06586 {
06587   for (uint32_t i= 0; i < size_of_bits; i++)
06588     bits[i]|=from->bits[i];
06589 }
06590 
06591 void REP_SET::copy_bits(const REP_SET *from)
06592 {
06593   memcpy(bits, from->bits, sizeof(uint32_t) * size_of_bits);
06594 }
06595 
06596 int REP_SET::cmp_bits(const REP_SET *set2) const
06597 {
06598   return memcmp(bits, set2->bits, sizeof(uint32_t) * size_of_bits);
06599 }
06600 
06601 /* Get next set bit from set. */
06602 
06603 int REP_SET::get_next_bit(uint32_t lastpos) const
06604 {
06605   uint32_t *start= bits + ((lastpos+1) / WORD_BIT);
06606   uint32_t *end= bits + size_of_bits;
06607   uint32_t bits0= start[0] & ~((1 << ((lastpos+1) % WORD_BIT)) -1);
06608 
06609   while (!bits0 && ++start < end)
06610     bits0= start[0];
06611   if (!bits0)
06612     return 0;
06613   uint32_t pos= (start - bits) * WORD_BIT;
06614   while (!(bits0 & 1))
06615   {
06616     bits0 >>=1;
06617     pos++;
06618   }
06619   return pos;
06620 }
06621 
06622 /* find if there is a same set in sets. If there is, use it and
06623    free given set, else put in given set in sets and return its
06624    position */
06625 
06626 int REP_SETS::find_set(const REP_SET *find)
06627 {
06628   uint32_t i= 0;
06629   for (; i < count - 1; i++)
06630   {
06631     if (!set[i].cmp_bits(find))
06632     {
06633       free_last_set();
06634       return i;
06635     }
06636   }
06637   return i;        /* return new postion */
06638 }
06639 
06640 /* find if there is a found_set with same table_offset & found_offset
06641    If there is return offset to it, else add new offset and return pos.
06642    Pos returned is -offset-2 in found_set_structure because it is
06643    saved in set->next and set->next[] >= 0 points to next set and
06644    set->next[] == -1 is reserved for end without replaces.
06645 */
06646 
06647 int find_found(FOUND_SET *found_set, uint32_t table_offset, int found_offset)
06648 {
06649   uint32_t i= 0;
06650   for (; i < found_sets; i++)
06651   {
06652     if (found_set[i].table_offset == table_offset &&
06653         found_set[i].found_offset == found_offset)
06654       return - i - 2;
06655   }
06656   found_set[i].table_offset= table_offset;
06657   found_set[i].found_offset= found_offset;
06658   found_sets++;
06659   return - i - 2; // return new postion
06660 }
06661 
06662 /****************************************************************************
06663  * Handle replacement of strings
06664  ****************************************************************************/
06665 
06666 #define PC_MALLOC    256  /* Bytes for pointers */
06667 #define PS_MALLOC    512  /* Bytes for data */
06668 
06669 static int insert_pointer_name(POINTER_ARRAY* pa, char* name)
06670 {
06671   uint32_t i,length,old_count;
06672   unsigned char *new_pos;
06673   const char **new_array;
06674 
06675 
06676   if (! pa->typelib.count)
06677   {
06678     pa->typelib.type_names=(const char **)
06679           malloc(((PC_MALLOC-MALLOC_OVERHEAD)/
06680                      (sizeof(char *)+sizeof(*pa->flag))*
06681                      (sizeof(char *)+sizeof(*pa->flag))));
06682     pa->str= (unsigned char*) malloc(PS_MALLOC-MALLOC_OVERHEAD);
06683     pa->max_count=(PC_MALLOC-MALLOC_OVERHEAD)/(sizeof(unsigned char*)+
06684                                                sizeof(*pa->flag));
06685     pa->flag= (uint8_t*) (pa->typelib.type_names+pa->max_count);
06686     pa->length=0;
06687     pa->max_length=PS_MALLOC-MALLOC_OVERHEAD;
06688     pa->array_allocs=1;
06689   }
06690   length=(uint32_t) strlen(name)+1;
06691   if (pa->length+length >= pa->max_length)
06692   {
06693     new_pos= (unsigned char*)realloc((unsigned char*)pa->str, (size_t)(pa->max_length+PS_MALLOC));
06694     if (new_pos != pa->str)
06695     {
06696       ptrdiff_t diff= PTR_BYTE_DIFF(new_pos,pa->str);
06697       for (i=0; i < pa->typelib.count; i++)
06698         pa->typelib.type_names[i]= ADD_TO_PTR(pa->typelib.type_names[i],diff,
06699                                               char*);
06700       pa->str=new_pos;
06701     }
06702     pa->max_length+=PS_MALLOC;
06703   }
06704   if (pa->typelib.count >= pa->max_count-1)
06705   {
06706     size_t len;
06707     pa->array_allocs++;
06708     len=(PC_MALLOC*pa->array_allocs - MALLOC_OVERHEAD);
06709     new_array= (const char **)realloc((unsigned char*) pa->typelib.type_names,
06710                                  len/
06711                                   (sizeof(unsigned char*)+sizeof(*pa->flag))*
06712                                   (sizeof(unsigned char*)+sizeof(*pa->flag)));
06713     pa->typelib.type_names=new_array;
06714     old_count=pa->max_count;
06715     pa->max_count=len/(sizeof(unsigned char*) + sizeof(*pa->flag));
06716     pa->flag= (uint8_t*) (pa->typelib.type_names+pa->max_count);
06717     memcpy(pa->flag, pa->typelib.type_names+old_count,
06718            old_count*sizeof(*pa->flag));
06719   }
06720   pa->flag[pa->typelib.count]=0;      /* Reset flag */
06721   pa->typelib.type_names[pa->typelib.count++]= (char*) pa->str+pa->length;
06722   pa->typelib.type_names[pa->typelib.count]= NULL;  /* Put end-mark */
06723   strcpy((char*) pa->str+pa->length,name);
06724   pa->length+=length;
06725   return(0);
06726 } /* insert_pointer_name */
06727 
06728 int POINTER_ARRAY::insert(char* name)
06729 {
06730   return insert_pointer_name(this, name);
06731 }
06732 
06733 
06734 /* Functions that uses replace and replace_regex */
06735 
06736 /* Append the string to ds, with optional replace */
06737 void replace_append_mem(string& ds, const char *val, int len)
06738 {
06739   char *v= strdup(val);
06740 
06741   if (glob_replace_regex && !glob_replace_regex->multi_reg_replace(v))
06742   {
06743     v= glob_replace_regex->buf_;
06744     len= strlen(v);
06745   }
06746   if (glob_replace)
06747   {
06748     /* Normal replace */
06749     replace_strings_append(glob_replace, ds, v, len);
06750   }
06751   else
06752     ds.append(v, len);
06753 }
06754 
06755 
06756 /* Append zero-terminated string to ds, with optional replace */
06757 void replace_append(string *ds, const char *val)
06758 {
06759   replace_append_mem(*ds, val, strlen(val));
06760 }
06761 
06762 /* Append uint32_t to ds, with optional replace */
06763 void replace_append_uint(string& ds, uint32_t val)
06764 {
06765   ostringstream buff;
06766   buff << val;
06767   replace_append_mem(ds, buff.str().c_str(), buff.str().size());
06768 
06769 }
06770 
06771 
06772 
06773 /*
06774   Build a list of pointer to each line in ds_input, sort
06775   the list and use the sorted list to append the strings
06776   sorted to the output ds
06777 
06778   SYNOPSIS
06779   dynstr_append_sorted
06780   ds - string where the sorted output will be appended
06781   ds_input - string to be sorted
06782 
06783 */
06784 
06785 
06786 void append_sorted(string& ds, const string& ds_input)
06787 {
06788   priority_queue<string, vector<string>, greater<string> > lines;
06789 
06790   if (ds_input.empty())
06791     return;  /* No input */
06792 
06793   unsigned long eol_pos= ds_input.find_first_of('\n', 0);
06794   if (eol_pos == string::npos)
06795     return; // We should have at least one header here
06796 
06797   ds.append(ds_input.substr(0, eol_pos+1));
06798 
06799   unsigned long start_pos= eol_pos+1;
06800 
06801   /* Insert line(s) in array */
06802   do {
06803 
06804     eol_pos= ds_input.find_first_of('\n', start_pos);
06805     /* Find end of line */
06806     lines.push(ds_input.substr(start_pos, eol_pos-start_pos+1));
06807     start_pos= eol_pos+1;
06808 
06809   } while ( eol_pos != string::npos);
06810 
06811   /* Create new result */
06812   while (!lines.empty()) 
06813   {
06814     ds.append(lines.top());
06815     lines.pop();
06816   }
06817 }
06818 
06819 static void free_all_replace()
06820 {
06821   free_replace();
06822   glob_replace_regex.reset();
06823   free_replace_column();
06824 }