mirror of
https://git.gfz-potsdam.de/naaice/iphreeqc.git
synced 2025-12-16 08:38:23 +01:00
Think multiple instances of both SELECTED_OUTPUT and USER_PUNCH are working.
git-svn-id: svn://136.177.114.72/svn_GW/phreeqc3/branches/multi_punch@7865 1feff8c3-07ed-0310-ac33-dd36852eb9cd
This commit is contained in:
parent
276322a936
commit
3893414278
@ -115,9 +115,12 @@ fpunchf_user(int user_index, const char *format, double d)
|
||||
const char *name;
|
||||
|
||||
// check headings
|
||||
//if (user_index < user_punch_count_headings)
|
||||
int user_punch_count_headings = (int) current_user_punch->Get_headings().size();
|
||||
if (user_index < user_punch_count_headings)
|
||||
{
|
||||
name = user_punch_headings[user_index];
|
||||
//name = user_punch_headings[user_index];
|
||||
name = current_user_punch->Get_headings()[user_index].c_str();
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -146,11 +149,13 @@ void Phreeqc::
|
||||
fpunchf_user(int user_index, const char *format, char * d)
|
||||
{
|
||||
const char *name;
|
||||
|
||||
|
||||
int user_punch_count_headings = (int) current_user_punch->Get_headings().size();
|
||||
// check headings
|
||||
if (user_index < user_punch_count_headings)
|
||||
{
|
||||
name = user_punch_headings[user_index];
|
||||
//name = user_punch_headings[user_index];
|
||||
name = current_user_punch->Get_headings()[user_index].c_str();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@ -224,6 +224,7 @@ void Phreeqc::init(void)
|
||||
last_model.surface_charge = NULL;
|
||||
|
||||
current_selected_output = NULL;
|
||||
current_user_punch = NULL;
|
||||
#ifdef SKIP
|
||||
//struct punch punch;
|
||||
/*
|
||||
@ -640,9 +641,11 @@ void Phreeqc::init(void)
|
||||
* USER PRINT COMMANDS
|
||||
* ---------------------------------------------------------------------- */
|
||||
user_print = NULL;
|
||||
#ifdef SKIP
|
||||
user_punch = NULL;
|
||||
user_punch_headings = NULL;
|
||||
user_punch_count_headings = 0;
|
||||
#endif
|
||||
n_user_punch_index = 0;
|
||||
fpunchf_user_s_warning = 0;
|
||||
fpunchf_user_buffer[0] = 0;
|
||||
@ -1789,6 +1792,9 @@ Phreeqc::InternalCopy(const Phreeqc *pSrc)
|
||||
user_print->varbase = NULL;
|
||||
user_print->loopbase = NULL;
|
||||
}
|
||||
|
||||
// For now, User Punch is not copied
|
||||
#ifdef SKIP
|
||||
/*
|
||||
user_punch = NULL;
|
||||
*/
|
||||
@ -1819,6 +1825,7 @@ Phreeqc::InternalCopy(const Phreeqc *pSrc)
|
||||
user_punch_headings[i] = string_hsave(pSrc->user_punch_headings[i]);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
n_user_punch_index = pSrc->n_user_punch_index;
|
||||
fpunchf_user_s_warning = pSrc->fpunchf_user_s_warning;
|
||||
//fpunchf_user_buffer[0] = 0;
|
||||
|
||||
11
Phreeqc.h
11
Phreeqc.h
@ -29,6 +29,7 @@
|
||||
#include "dumper.h"
|
||||
#include "PHRQ_io.h"
|
||||
#include "SelectedOutput.h"
|
||||
#include "UserPunch.h"
|
||||
#ifdef MULTICHART
|
||||
#include "ChartHandler.h"
|
||||
#endif
|
||||
@ -1515,9 +1516,9 @@ protected:
|
||||
* USER PRINT COMMANDS
|
||||
* ---------------------------------------------------------------------- */
|
||||
struct rate *user_print;
|
||||
struct rate *user_punch;
|
||||
const char **user_punch_headings;
|
||||
int user_punch_count_headings;
|
||||
//struct rate *user_punch;
|
||||
//const char **user_punch_headings;
|
||||
//int user_punch_count_headings;
|
||||
int n_user_punch_index;
|
||||
|
||||
int fpunchf_user_s_warning;
|
||||
@ -1613,6 +1614,9 @@ protected:
|
||||
//char *selected_output_file_name;
|
||||
std::map<int, SelectedOutput> SelectedOutput_map;
|
||||
SelectedOutput * current_selected_output;
|
||||
|
||||
std::map <int, UserPunch> UserPunch_map;
|
||||
UserPunch * current_user_punch;
|
||||
|
||||
char *dump_file_name;
|
||||
int remove_unstable_phases;
|
||||
@ -1744,6 +1748,7 @@ protected:
|
||||
FILE *netpath_file;
|
||||
int count_inverse_models, count_pat_solutions;
|
||||
int min_position[32], max_position[32], now[32];
|
||||
std::vector <std::string> inverse_heading_names;
|
||||
|
||||
/* kinetics.cpp ------------------------------- */
|
||||
public:
|
||||
|
||||
19
UserPunch.cpp
Normal file
19
UserPunch.cpp
Normal file
@ -0,0 +1,19 @@
|
||||
#include "UserPunch.h"
|
||||
#include "Phreeqc.h"
|
||||
UserPunch::UserPunch(int n, PHRQ_io *io)
|
||||
: cxxNumKeyword(io)
|
||||
{
|
||||
this->PhreeqcPtr = NULL;
|
||||
this->rate = NULL;
|
||||
}
|
||||
|
||||
|
||||
UserPunch::~UserPunch(void)
|
||||
{
|
||||
if (this->rate != NULL)
|
||||
{
|
||||
this->PhreeqcPtr->rate_free(this->rate);
|
||||
}
|
||||
this->PhreeqcPtr->free_check_null(this->rate);
|
||||
this->rate = NULL;
|
||||
}
|
||||
25
UserPunch.h
Normal file
25
UserPunch.h
Normal file
@ -0,0 +1,25 @@
|
||||
#if !defined(USERPUNCH_H_INCLUDED)
|
||||
#define USERPUNCH_H_INCLUDED
|
||||
#include <vector>
|
||||
#include <string> // std::string
|
||||
#include "NumKeyword.h"
|
||||
class Phreeqc;
|
||||
class UserPunch:public cxxNumKeyword
|
||||
{
|
||||
public:
|
||||
UserPunch(int n=1, PHRQ_io *io=NULL);
|
||||
~UserPunch(void);
|
||||
|
||||
std::vector <std::string> &Get_headings() {return this->headings;}
|
||||
void Set_headings(std::vector <std::string> & h) {this->headings = h;}
|
||||
Phreeqc * Get_PhreeqcPtr() {return this->PhreeqcPtr;}
|
||||
void Set_PhreeqcPtr(Phreeqc * p) {this->PhreeqcPtr = p;}
|
||||
struct rate * Get_rate() {return this->rate;}
|
||||
void Set_rate(struct rate * r) {this->rate = r;}
|
||||
|
||||
protected:
|
||||
std::vector <std::string> headings;
|
||||
struct rate * rate;
|
||||
Phreeqc * PhreeqcPtr;
|
||||
};
|
||||
#endif // !defined(USERPUNCH_H_INCLUDED)
|
||||
51
inverse.cpp
51
inverse.cpp
@ -27,10 +27,11 @@ inverse_models(void)
|
||||
// Revert to previous headings after inverse modeling
|
||||
std::vector<std::string> old_headings;
|
||||
int i;
|
||||
for (i = 0; i < user_punch_count_headings; i++)
|
||||
{
|
||||
old_headings.push_back(user_punch_headings[i]);
|
||||
}
|
||||
|
||||
//for (i = 0; i < user_punch_count_headings; i++)
|
||||
//{
|
||||
// old_headings.push_back(user_punch_headings[i]);
|
||||
//}
|
||||
|
||||
array1 = NULL;
|
||||
inv_zero = NULL;
|
||||
@ -134,15 +135,15 @@ inverse_models(void)
|
||||
}
|
||||
}
|
||||
|
||||
user_punch_count_headings = (int) old_headings.size();
|
||||
user_punch_headings = (const char **) PHRQ_realloc(user_punch_headings,
|
||||
(size_t) (user_punch_count_headings + 1) * sizeof(char *));
|
||||
if (user_punch_headings == NULL)
|
||||
malloc_error();
|
||||
for (i = 0; i < user_punch_count_headings; i++)
|
||||
{
|
||||
user_punch_headings[i] = string_hsave(old_headings[i].c_str());
|
||||
}
|
||||
//user_punch_count_headings = (int) old_headings.size();
|
||||
//user_punch_headings = (const char **) PHRQ_realloc(user_punch_headings,
|
||||
// (size_t) (user_punch_count_headings + 1) * sizeof(char *));
|
||||
//if (user_punch_headings == NULL)
|
||||
// malloc_error();
|
||||
//for (i = 0; i < user_punch_count_headings; i++)
|
||||
//{
|
||||
// user_punch_headings[i] = string_hsave(old_headings[i].c_str());
|
||||
//}
|
||||
return (OK);
|
||||
}
|
||||
|
||||
@ -2082,6 +2083,7 @@ punch_model_heading(struct inverse *inv_ptr)
|
||||
char token[MAX_LENGTH];
|
||||
//if (/*punch.in == FALSE ||*/ pr.punch == FALSE || punch.inverse == FALSE)
|
||||
// return (OK);
|
||||
std::vector<std::string> heading_names;
|
||||
std::map < int, SelectedOutput >::iterator so_it = SelectedOutput_map.begin();
|
||||
for ( ; so_it != SelectedOutput_map.end(); so_it++)
|
||||
{
|
||||
@ -2095,9 +2097,8 @@ punch_model_heading(struct inverse *inv_ptr)
|
||||
continue;
|
||||
phrq_io->Set_punch_ostream(current_selected_output->punch_ostream);
|
||||
|
||||
std::vector<std::string> heading_names;
|
||||
int l = (!current_selected_output->Get_high_precision()) ? 15 : 20;
|
||||
|
||||
heading_names.clear();
|
||||
/*
|
||||
* Print sum of residuals and maximum fractional error
|
||||
*/
|
||||
@ -2140,21 +2141,22 @@ punch_model_heading(struct inverse *inv_ptr)
|
||||
size_t j;
|
||||
|
||||
// punch headings
|
||||
user_punch_count_headings = (int) heading_names.size();
|
||||
user_punch_headings = (const char **) PHRQ_realloc(user_punch_headings,
|
||||
(size_t) (user_punch_count_headings + 1) * sizeof(char *));
|
||||
if (user_punch_headings == NULL)
|
||||
malloc_error();
|
||||
//user_punch_count_headings = (int) heading_names.size();
|
||||
//user_punch_headings = (const char **) PHRQ_realloc(user_punch_headings,
|
||||
// (size_t) (user_punch_count_headings + 1) * sizeof(char *));
|
||||
//if (user_punch_headings == NULL)
|
||||
// malloc_error();
|
||||
|
||||
for (j = 0; j < heading_names.size(); j++)
|
||||
{
|
||||
fpunchf_heading(heading_names[j].c_str());
|
||||
user_punch_headings[j] = string_hsave(heading_names[j].c_str());
|
||||
//user_punch_headings[j] = string_hsave(heading_names[j].c_str());
|
||||
}
|
||||
fpunchf_heading("\n");
|
||||
}
|
||||
current_selected_output = NULL;
|
||||
phrq_io->Set_punch_ostream(NULL);
|
||||
inverse_heading_names = heading_names;
|
||||
/*
|
||||
* Flush buffer after each model
|
||||
*/
|
||||
@ -2174,6 +2176,11 @@ punch_model(struct inverse *inv_ptr)
|
||||
LDBLE d1, d2, d3;
|
||||
//if (punch.in == FALSE || pr.punch == FALSE || punch.inverse == FALSE)
|
||||
// return (OK);
|
||||
|
||||
UserPunch temp_user_punch;
|
||||
current_user_punch = & temp_user_punch;
|
||||
temp_user_punch.Set_headings(inverse_heading_names);
|
||||
|
||||
std::map < int, SelectedOutput >::iterator so_it = SelectedOutput_map.begin();
|
||||
for ( ; so_it != SelectedOutput_map.end(); so_it++)
|
||||
{
|
||||
@ -2185,7 +2192,7 @@ punch_model(struct inverse *inv_ptr)
|
||||
!current_selected_output->Get_active())
|
||||
continue;
|
||||
phrq_io->Set_punch_ostream(current_selected_output->punch_ostream);
|
||||
|
||||
|
||||
n_user_punch_index = 0;
|
||||
/*
|
||||
* write residual info
|
||||
|
||||
@ -191,6 +191,7 @@ initialize(void)
|
||||
user_print->varbase = NULL;
|
||||
user_print->loopbase = NULL;
|
||||
|
||||
#ifdef SKIP
|
||||
// user_punch
|
||||
user_punch = (struct rate *) PHRQ_malloc((size_t) sizeof(struct rate));
|
||||
if (user_punch == NULL)
|
||||
@ -203,6 +204,7 @@ initialize(void)
|
||||
if (user_punch_headings == NULL)
|
||||
malloc_error();
|
||||
user_punch_count_headings = 0;
|
||||
#endif
|
||||
#if defined PHREEQ98
|
||||
/*
|
||||
* user_graph
|
||||
|
||||
11
print.cpp
11
print.cpp
@ -175,6 +175,11 @@ punch_all(void)
|
||||
current_selected_output->punch_ostream == NULL)
|
||||
continue;
|
||||
phrq_io->Set_punch_ostream(current_selected_output->punch_ostream);
|
||||
|
||||
// UserPunch
|
||||
std::map < int, UserPunch >::iterator up_it = UserPunch_map.find(current_selected_output->Get_n_user());
|
||||
current_user_punch = up_it == UserPunch_map.end() ? NULL : &(up_it->second);
|
||||
|
||||
punch_identifiers();
|
||||
punch_totals();
|
||||
punch_molalities();
|
||||
@ -199,6 +204,7 @@ punch_all(void)
|
||||
punch_flush();
|
||||
}
|
||||
current_selected_output = NULL;
|
||||
current_user_punch = NULL;
|
||||
phrq_io->Set_punch_ostream(NULL);
|
||||
return (OK);
|
||||
}
|
||||
@ -3191,6 +3197,11 @@ punch_user_punch(void)
|
||||
n_user_punch_index = 0;
|
||||
//if (punch.user_punch == FALSE)
|
||||
// return (OK);
|
||||
if (current_user_punch == NULL)
|
||||
return OK;
|
||||
|
||||
struct rate * user_punch = current_user_punch->Get_rate();
|
||||
|
||||
if (user_punch->commands == NULL)
|
||||
return (OK);
|
||||
if (user_punch->new_def == TRUE)
|
||||
|
||||
151
read.cpp
151
read.cpp
@ -17,6 +17,7 @@
|
||||
#include "cxxKinetics.h"
|
||||
#include "Solution.h"
|
||||
#include "SelectedOutput.h"
|
||||
#include "UserPunch.h"
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
int Phreeqc::
|
||||
@ -9211,7 +9212,155 @@ read_user_print(void)
|
||||
/* output_msg(sformatf( "%s", rates[0].commands));
|
||||
*/ return (return_value);
|
||||
}
|
||||
/* ---------------------------------------------------------------------- */
|
||||
int Phreeqc::
|
||||
read_user_punch(void)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
/*
|
||||
* Reads basic code with which to calculate rates
|
||||
*
|
||||
* Arguments:
|
||||
* none
|
||||
*
|
||||
* Returns:
|
||||
* KEYWORD if keyword encountered, input_error may be incremented if
|
||||
* a keyword is encountered in an unexpected position
|
||||
* EOF if eof encountered while reading mass balance concentrations
|
||||
* ERROR if error occurred reading data
|
||||
*
|
||||
*/
|
||||
int length, line_length;
|
||||
int return_value, opt, opt_save;
|
||||
std::string stdtoken;
|
||||
char *next_char;
|
||||
const char *opt_list[] = {
|
||||
"start", /* 0 */
|
||||
"end", /* 1 */
|
||||
"heading", /* 2 */
|
||||
"headings" /* 3 */
|
||||
};
|
||||
int count_opt_list = 4;
|
||||
|
||||
opt_save = OPTION_DEFAULT;
|
||||
/*
|
||||
* Read lines
|
||||
*/
|
||||
|
||||
int n_user, n_user_end;
|
||||
char *description;
|
||||
char *ptr;
|
||||
ptr = line;
|
||||
read_number_description(ptr, &n_user, &n_user_end, &description);
|
||||
|
||||
UserPunch temp_user_punch;
|
||||
temp_user_punch.Set_PhreeqcPtr(this);
|
||||
temp_user_punch.Set_n_user(n_user);
|
||||
temp_user_punch.Set_n_user_end(n_user_end);
|
||||
temp_user_punch.Set_description(description);
|
||||
free_check_null(description);
|
||||
|
||||
//std::map < int, UserPunch >::iterator up = UserPunch_map.find(n_user);
|
||||
//if (up != UserPunch_map.end())
|
||||
//{
|
||||
// UserPunch_map.erase(up);
|
||||
//}
|
||||
|
||||
// Malloc rate structure
|
||||
struct rate *r = (struct rate *) PHRQ_malloc(sizeof(struct rate));
|
||||
if (r == NULL) malloc_error();
|
||||
r->new_def = TRUE;
|
||||
r->linebase = NULL;
|
||||
r->varbase = NULL;
|
||||
r->loopbase = NULL;
|
||||
r->name = string_hsave("user defined Basic punch routine");
|
||||
|
||||
return_value = UNKNOWN;
|
||||
for (;;)
|
||||
{
|
||||
opt = get_option(opt_list, count_opt_list, &next_char);
|
||||
if (opt == OPTION_DEFAULT)
|
||||
{
|
||||
opt = opt_save;
|
||||
}
|
||||
opt_save = OPTION_DEFAULT;
|
||||
switch (opt)
|
||||
{
|
||||
case OPTION_EOF: /* end of file */
|
||||
return_value = EOF;
|
||||
break;
|
||||
case OPTION_KEYWORD: /* keyword */
|
||||
return_value = KEYWORD;
|
||||
break;
|
||||
case OPTION_ERROR:
|
||||
input_error++;
|
||||
error_msg("Unknown input in USER_PUNCH keyword.", CONTINUE);
|
||||
error_msg(line_save, CONTINUE);
|
||||
break;
|
||||
case 0: /* start */
|
||||
opt_save = OPTION_DEFAULT;
|
||||
break;
|
||||
case 1: /* end */
|
||||
opt_save = OPTION_DEFAULT;
|
||||
break;
|
||||
case 2: /* headings */
|
||||
case 3: /* heading */
|
||||
while (copy_token(stdtoken, &next_char) != EMPTY)
|
||||
{
|
||||
temp_user_punch.Get_headings().push_back(stdtoken);
|
||||
}
|
||||
break;
|
||||
case OPTION_DEFAULT: /* read first command */
|
||||
{
|
||||
r->commands = (char *) PHRQ_malloc(sizeof(char));
|
||||
if (r->commands == NULL) malloc_error();
|
||||
r->commands[0] = '\0';
|
||||
}
|
||||
//rate_free(user_punch);
|
||||
//user_punch->new_def = TRUE;
|
||||
//user_punch->commands = (char *) PHRQ_malloc(sizeof(char));
|
||||
//if (user_punch->commands == NULL)
|
||||
// malloc_error();
|
||||
//user_punch->commands[0] = '\0';
|
||||
//user_punch->linebase = NULL;
|
||||
//user_punch->varbase = NULL;
|
||||
//user_punch->loopbase = NULL;
|
||||
//user_punch->name =
|
||||
// string_hsave("user defined Basic punch routine");
|
||||
case OPT_1: /* read command */
|
||||
length = (int) strlen(r->commands);
|
||||
line_length = (int) strlen(line);
|
||||
r->commands = (char *) PHRQ_realloc(r->commands,
|
||||
(size_t) (length + line_length + 2) * sizeof(char));
|
||||
if (r->commands == NULL) malloc_error();
|
||||
|
||||
r->commands[length] = ';';
|
||||
r->commands[length + 1] = '\0';
|
||||
strcat((r->commands), line);
|
||||
//length = (int) strlen(user_punch->commands);
|
||||
//line_length = (int) strlen(line);
|
||||
//user_punch->commands =
|
||||
// (char *) PHRQ_realloc(user_punch->commands,
|
||||
// (size_t) (length + line_length +
|
||||
// 2) * sizeof(char));
|
||||
//if (user_punch->commands == NULL)
|
||||
// malloc_error();
|
||||
//user_punch->commands[length] = ';';
|
||||
//user_punch->commands[length + 1] = '\0';
|
||||
//strcat((user_punch->commands), line);
|
||||
opt_save = OPT_1;
|
||||
break;
|
||||
}
|
||||
if (return_value == EOF || return_value == KEYWORD)
|
||||
break;
|
||||
}
|
||||
|
||||
UserPunch_map[n_user] = temp_user_punch;
|
||||
UserPunch_map[n_user].Set_rate(r);
|
||||
|
||||
return (return_value);
|
||||
}
|
||||
#ifdef SKIP
|
||||
/* ---------------------------------------------------------------------- */
|
||||
int Phreeqc::
|
||||
read_user_punch(void)
|
||||
@ -9323,7 +9472,7 @@ read_user_punch(void)
|
||||
}
|
||||
return (return_value);
|
||||
}
|
||||
|
||||
#endif
|
||||
#if defined PHREEQ98
|
||||
/* ---------------------------------------------------------------------- */
|
||||
int Phreeqc::
|
||||
|
||||
@ -206,14 +206,17 @@ clean_up(void)
|
||||
(struct name_master *) free_check_null(punch.calculate_values);
|
||||
#endif
|
||||
SelectedOutput_map.clear();
|
||||
UserPunch_map.clear();
|
||||
|
||||
/* user_print and user_punch */
|
||||
rate_free(user_print);
|
||||
#ifdef SKIP
|
||||
rate_free(user_punch);
|
||||
user_print = (struct rate *) free_check_null(user_print);
|
||||
|
||||
user_punch = (struct rate *) free_check_null(user_punch);
|
||||
user_punch_headings = (const char **) free_check_null(user_punch_headings);
|
||||
#endif
|
||||
|
||||
/*
|
||||
Free llnl aqueous model parameters
|
||||
|
||||
17
tidy.cpp
17
tidy.cpp
@ -1784,6 +1784,11 @@ tidy_punch(void)
|
||||
current_selected_output->punch_ostream == NULL)
|
||||
continue;
|
||||
phrq_io->Set_punch_ostream(current_selected_output->punch_ostream);
|
||||
|
||||
// UserPunch
|
||||
std::map < int, UserPunch >::iterator up_it = UserPunch_map.find(current_selected_output->Get_n_user());
|
||||
current_user_punch = up_it == UserPunch_map.end() ? NULL : &(up_it->second);
|
||||
|
||||
int l;
|
||||
SelectedOutput *so_ptr = &so_it->second;
|
||||
if (so_ptr->Get_high_precision() == false)
|
||||
@ -2187,7 +2192,14 @@ tidy_punch(void)
|
||||
}
|
||||
|
||||
/* user_punch */
|
||||
// TODO, implement user_punch n TODO
|
||||
if (current_user_punch != NULL)
|
||||
{
|
||||
for (size_t i = 0; i < current_user_punch->Get_headings().size(); i++)
|
||||
{
|
||||
fpunchf_heading(sformatf("%*s\t", l, current_user_punch->Get_headings()[i].c_str()));
|
||||
}
|
||||
}
|
||||
fpunchf_heading("\n");
|
||||
//if (punch.user_punch == TRUE)
|
||||
//{
|
||||
// for (i = 0; i < user_punch_count_headings; i++)
|
||||
@ -2195,7 +2207,7 @@ tidy_punch(void)
|
||||
// fpunchf_heading(sformatf("%*s\t", l, user_punch_headings[i]));
|
||||
// }
|
||||
//}
|
||||
fpunchf_heading("\n");
|
||||
//fpunchf_heading("\n");
|
||||
|
||||
so_ptr->Set_new_def(false);
|
||||
pr.punch = punch_save;
|
||||
@ -2204,6 +2216,7 @@ tidy_punch(void)
|
||||
punch_flush();
|
||||
}
|
||||
current_selected_output = NULL;
|
||||
current_user_punch = NULL;
|
||||
phrq_io->Set_punch_ostream(NULL);
|
||||
return (OK);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user