Scid  4.6.5
sc_base.cpp
Go to the documentation of this file.
1 /*
2 # Copyright (C) 2015 Fulvio Benini
3 
4 * This file is part of Scid (Shane's Chess Information Database).
5 *
6 * Scid is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation.
9 *
10 * Scid is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with Scid. If not, see <http://www.gnu.org/licenses/>.
17 */
18 
19 #include "codec_pgn.h"
20 #include "common.h"
21 #include "dbasepool.h"
22 #include "misc.h"
23 #include "scidbase.h"
24 #include "searchtournaments.h"
25 #include "ui.h"
26 #include <cstring>
27 #include <string>
28 
29 namespace {
30 /*
31 * This "sc_base" functions are used by the UI to access the databases.
32 * To encapsulate database internal complexity this functions should only
33 * parse arguments and call other functions/objects.
34 *
35 * Command names are case sensitive
36 * Optional parameter are indicated using [value_opt]
37 * Alternative values are indicated using <value_a|value_b|value_c>
38 * BaseId is the handle used to select the database to work on
39 * "dbfilter" is the name of the default filter of database
40 * Games are numbered starting from "1"
41 * Example:
42 * sc_base open "filename" -> on success returns a baseId handle to the opened database
43 * sc_base gameslist baseId "1" "10" "dbfilter" "N+"
44 * -> returns the list of the first 10 games of the database
45 */
46 
47 
48 /**
49  * doOpenBase() - open/create a database
50  * @filename: the filename of the database to open/create
51  * @fMode: open the database read-only|read-write|create|in_memory
52  * @codec: the type of the database
53  *
54  * If @fMode == FMODE_Both, and the file cannot be opened for writing, this
55  * function will try to open the database read-only.
56  */
57 static UI_res_t doOpenBase(UI_handle_t ti, const char* filename,
58  fileModeT fMode, ICodecDatabase::Codec codec) {
59  if (DBasePool::find(filename) != 0) return UI_Result(ti, ERROR_FileInUse);
60 
62  if (dbase == 0) return UI_Result(ti, ERROR_Full);
63 
64  Progress progress = UI_CreateProgress(ti);
65  errorT err = dbase->Open(codec, fMode, filename, progress);
66 
67  if (err != OK && err != ERROR_NameDataLoss && fMode == FMODE_Both) {
68  err = dbase->Open(codec, FMODE_ReadOnly, filename, progress);
69  }
70  progress.report(1,1);
71 
72  if (err != OK && err != ERROR_NameDataLoss) return UI_Result(ti, err);
73 
74  int res = DBasePool::switchCurrent(dbase);
75  return UI_Result(ti, err, res);
76 }
77 
78 
79 /**
80  * sc_base_close() - close a database
81  */
82 UI_res_t sc_base_close(scidBaseT* dbase, UI_handle_t ti, int, const char**) {
83  if (dbase->getFileName() == "<clipbase>") {
84  return UI_Result(ti, ERROR_BadArg, "Cannot close clipbase.");
85  }
86  return UI_Result(ti, dbase->Close());
87 }
88 
89 
90 /**
91  * sc_base_compact() - compact a database
92  * @stats: if used do not compact the database but calculate stats
93  *
94  * Compacting a database:
95  * - remove all the games marked as "deleted"
96  * - remove unused names
97  * - makes search operations faster
98  * - replace bad names with "???"
99  */
100 UI_res_t sc_base_compact(scidBaseT* dbase, UI_handle_t ti, int argc,
101  const char** argv) {
102  const char* usage = "Usage: sc_base compact baseId [stats]";
103 
104  if (argc == 3) {
105  errorT res = dbase->compact(UI_CreateProgress(ti));
106  return UI_Result(ti, res);
107  } else if (argc == 4 && std::strcmp("stats", argv[3]) == 0) {
108  uint n_deleted, n_unused, n_sparse, n_badNameId;
109  errorT res = dbase->getCompactStat(&n_deleted, &n_unused, &n_sparse,
110  &n_badNameId);
111  UI_List val(4);
112  val.push_back(n_deleted);
113  val.push_back(n_unused);
114  val.push_back(n_sparse);
115  val.push_back(n_badNameId);
116  return UI_Result(ti, res, val);
117  }
118 
119  return UI_Result(ti, ERROR_BadArg, usage);
120 }
121 
122 
123 /**
124  * sc_base_copygames() - copy games from a database to another
125  */
126 UI_res_t sc_base_copygames(scidBaseT* dbase, UI_handle_t ti, int argc, const char** argv)
127 {
128  const char* usage = "Usage: sc_base copygames baseId <gameNum|filterName> targetBaseId";
129  if (argc != 5) return UI_Result(ti, ERROR_BadArg, usage);
130 
131  scidBaseT* targetBase = DBasePool::getBase(strGetUnsigned(argv[4]));
132  if (targetBase == 0)
133  return UI_Result(ti, ERROR_BadArg, "sc_base copygames error: wrong targetBaseId");
134  if (targetBase->isReadOnly())
135  return UI_Result(ti, ERROR_FileReadOnly);
136 
137  errorT err = OK;
138  const HFilter filter = dbase->getFilter(argv[3]);
139  if (filter != 0) {
140  err = targetBase->importGames(dbase, filter, UI_CreateProgress(ti));
141  } else {
142  uint gNum = strGetUnsigned (argv[3]);
143  if (gNum == 0)
144  return UI_Result(ti, ERROR_BadArg, "sc_base copygames error: wrong <gameNum|filterName>");
145  err = targetBase->importGame(dbase, gNum -1);
146  }
147  return UI_Result(ti, err);
148 }
149 
150 
151 /**
152  * sc_base_create() - crate a database
153  * @codec : the type of the database
154  * @filename: the filename of the wanted database.
155  * Database in native Scid format do not use extension ("example").
156  * Other databases require file extension ("example.pgn").
157  *
158  * Return:
159  * - on success the handle assigned to the database
160  */
161 UI_res_t sc_base_create(UI_handle_t ti, int argc, const char** argv) {
162  const char* usage = "Usage: sc_base create <MEMORY|SCID4|PGN> filename";
163  if (argc != 4 && argc != 3) return UI_Result(ti, ERROR_BadArg, usage);
164 
165  // Old interface defaults to SCID4
167  if (argc == 4) {
168  if (std::strcmp("MEMORY", argv[2]) == 0)
169  codec = ICodecDatabase::MEMORY;
170  else if (std::strcmp("SCID4", argv[2]) == 0)
171  codec = ICodecDatabase::SCID4;
172  else if (std::strcmp("PGN", argv[2]) == 0)
173  codec = ICodecDatabase::PGN;
174  else
175  return UI_Result(ti, ERROR_BadArg, usage);
176  }
177 
178  return doOpenBase(
179  ti, argc == 4 ? argv[3] : argv[2],
180  codec == ICodecDatabase::MEMORY ? FMODE_Memory : FMODE_Create, codec);
181 }
182 
183 
184 /**
185  * sc_base_extra() - get/set a database extra tag (i.e. "description" or "type")
186  */
187 UI_res_t sc_base_extra(scidBaseT* dbase, UI_handle_t ti, int argc, const char** argv)
188 {
189  const char* usage = "Usage: sc_base extra baseId tagname [new_value]";
190 
191  if (argc == 4) {
192  std::string res;
193  errorT err = dbase->getExtraInfo(argv[3], &res);
194  return UI_Result(ti, err, res);
195  } else if (argc == 5) {
196  return UI_Result(ti, dbase->setExtraInfo(argv[3], argv[4]));
197  }
198 
199  return UI_Result(ti, ERROR_BadArg, usage);
200 }
201 
202 
203 /**
204  * sc_base_filename() - get the filename of a database
205  *
206  * Return:
207  * the full path filename for non native databases (like pgn)
208  * the full path filename without the .si4 extension for native Scid databases
209  * <clipbase> for the clipbase
210  */
211 UI_res_t sc_base_filename(scidBaseT* dbase, UI_handle_t ti, int argc,
212  const char**) {
213  const char* usage = "Usage: sc_base filename baseId";
214  if (argc != 3) return UI_Result(ti, ERROR_BadArg, usage);
215 
216  return UI_Result(ti, OK, dbase->getFileName());
217 }
218 
219 
220 /**
221  * sc_base_gameflag() - get/set a game flag (i.e. "D" for deleted)
222  *
223  * Return: a boolean value if the requested operation is "get"
224  */
225 UI_res_t sc_base_gameflag(scidBaseT* dbase, UI_handle_t ti, int argc, const char** argv)
226 {
227  const char* usage = "Usage: sc_base gameflag baseId <gameNum|filterName|all> <get|set|unset|invert> flagType";
228  if (argc != 6) return UI_Result(ti, ERROR_BadArg, usage);
229 
230  int cmd = 0;
231  if (std::strcmp("get", argv[4]) == 0)
232  cmd = 1;
233  else if (std::strcmp("set", argv[4]) == 0)
234  cmd = 2;
235  else if (std::strcmp("unset", argv[4]) == 0)
236  cmd = 3;
237  else if (std::strcmp("invert", argv[4]) == 0)
238  cmd = 4;
239  uint flagType = IndexEntry::CharToFlagMask(argv[5][0]);
240  if (flagType != 0 && cmd != 0) {
241  bool value = (cmd == 2);
242 
243  const HFilter filter = dbase->getFilter(argv[3]);
244  if (filter != 0 || (std::strcmp("all", argv[3]) == 0)) {
245  switch (cmd) {
246  case 2:
247  case 3: return UI_Result(ti, dbase->setFlag(value, flagType, filter));
248  case 4: return UI_Result(ti, dbase->invertFlag(flagType, filter));
249  }
250  } else {
251  gamenumT gNum = strGetUnsigned(argv[3]);
252  if (gNum > 0 && gNum <= dbase->numGames()) {
253  gNum--;
254  switch (cmd) {
255  case 1: return UI_Result(ti, OK, dbase->getFlag(flagType, gNum));
256  case 2:
257  case 3: return UI_Result(ti, dbase->setFlag(value, flagType, gNum));
258  case 4: return UI_Result(ti, dbase->invertFlag(flagType, gNum));
259  }
260  }
261  }
262  }
263 
264  return UI_Result(ti, ERROR_BadArg, usage);
265 }
266 
267 
268 /**
269  * sc_base_gamelocation() - find the position of a game
270  * @sortCrit: the order for the list of games to search
271  * @gnumber: the number of the game to search for
272  * @text: if (@gnumber == 0) search the first game that contains @text in
273  * white name or black name or event name or site name
274  * @start_pos: used only with @text, start searching from a zero-based position.
275  * @forward_dir: <true|false>, used only with @start_pos.
276  * if @e true search in the range [start_pos, numGames)
277  * if @e false search in the range [0, start_pos)
278  *
279  * Return: the position (0 == first) of the first match or "none" if not found
280  */
281 UI_res_t sc_base_gamelocation(scidBaseT* dbase, UI_handle_t ti, int argc, const char** argv)
282 {
283  const char* usage = "Usage: sc_base gamelocation baseId filterName sortCrit <gnumber | 0 text start_pos forward_dir>";
284  if (argc < 6) return UI_Result(ti, ERROR_BadArg, usage);
285 
286  const HFilter filter = dbase->getFilter(argv[3]);
287  if (filter == 0) return UI_Result(ti, ERROR_BadArg, usage);
288 
289  const char* sort = argv[4];
290  gamenumT gnumber = strGetUnsigned(argv[5]);
291  size_t location = INVALID_GAMEID;
292  if (gnumber == 0) {
293  if (argc != 9)
294  return UI_Result(ti, ERROR_BadArg, usage);
295  const char* text = argv[6];
296  size_t start = strGetUnsigned(argv[7]);
297  const NameBase* nb = dbase->getNameBase();
298 #if CPP11_SUPPORT
299  auto contains = [dbase, nb, text](gamenumT g) {
300  const IndexEntry* ie = dbase->getIndexEntry(g);
301  return strAlphaContains(ie->GetWhiteName(nb), text) ||
302  strAlphaContains(ie->GetBlackName(nb), text) ||
303  strAlphaContains(ie->GetEventName(nb), text) ||
304  strAlphaContains(ie->GetSiteName(nb), text);
305  };
306  if (strGetBoolean(argv[8])) {
307  std::vector<gamenumT> buf(filter->size() - start);
308  buf.resize(
309  dbase->listGames(sort, start, buf.size(), filter, buf.data()));
310  auto it = std::find_if(buf.begin(), buf.end(), contains);
311  if (it != buf.end())
312  location = start + std::distance(buf.begin(), it);
313  } else {
314  std::vector<gamenumT> buf(start);
315  buf.resize(dbase->listGames(sort, 0, start, filter, buf.data()));
316  auto it = std::find_if(buf.rbegin(), buf.rend(), contains);
317  if (it != buf.rend())
318  location = std::distance(it, buf.rend()) - 1;
319  }
320 #endif
321  } else {
322  location = dbase->sortedPosition(sort, filter, gnumber - 1);
323  }
324  if (location == INVALID_GAMEID)
325  return UI_Result(ti, OK, "none"); // Not found
326  return UI_Result(ti, OK, location);
327 }
328 
329 
330 /**
331  * sc_base_gameslist() - returns the sorted list of games of a database
332  * @start: 0 for the first game
333  */
334 UI_res_t sc_base_gameslist(scidBaseT* dbase, UI_handle_t ti, int argc, const char** argv)
335 {
336  const char* usage = "Usage: sc_base gameslist baseId start count filterName sortCrit";
337  if (argc != 7) return UI_Result(ti, ERROR_BadArg, usage);
338 
339  size_t start = strGetUnsigned(argv[3]);
340  size_t count = strGetUnsigned(argv[4]);
341  const HFilter filter = dbase->getFilter(argv[5]);
342  if (filter == NULL)
343  return UI_Result(ti, ERROR_BadArg, usage);
344  gamenumT* idxList = new gamenumT[count];
345  count = dbase->listGames(argv[6], start, count, filter, idxList);
346 
347  UI_List res (count * 3);
348  UI_List ginfo(24);
349  const NameBase* nb = dbase->getNameBase();
350  for (uint i = 0; i < count; ++i) {
351  uint idx = idxList[i];
352 
353  ASSERT(filter->get(idx) != 0);
354  uint ply = filter->get(idx) -1;
355 
356  const IndexEntry* ie = dbase->getIndexEntry(idx);
357 
358  ginfo.clear();
359  ginfo.push_back(idx +1);
360  ginfo.push_back(RESULT_STR[ie->GetResult()]);
361  ginfo.push_back((ie->GetNumHalfMoves() + 1) / 2);
362  ginfo.push_back(ie->GetWhiteName(nb));
363  std::string eloStr;
364  eloT welo = ie->GetWhiteElo();
365  if (welo != 0) {
366  eloStr = to_string(welo);
367  } else {
368  welo = ie->GetWhiteElo(nb);
369  eloStr = to_string(welo);
370  if (welo != 0) {
371  eloStr.insert(eloStr.begin(), '(');
372  eloStr.insert(eloStr.end(), ')');
373  }
374  }
375  ginfo.push_back(eloStr);
376  ginfo.push_back(ie->GetBlackName(nb));
377  eloT belo = ie->GetBlackElo();
378  if (belo != 0) {
379  eloStr = to_string(belo);
380  } else {
381  belo = ie->GetBlackElo(nb);
382  eloStr = to_string(belo);
383  if (belo != 0) {
384  eloStr.insert(eloStr.begin(), '(');
385  eloStr.insert(eloStr.end(), ')');
386  }
387  }
388  ginfo.push_back(eloStr);
389  char buf_date[16];
390  date_DecodeToString (ie->GetDate(), buf_date);
391  ginfo.push_back(buf_date);
392  ginfo.push_back(ie->GetEventName(nb));
393  ginfo.push_back(ie->GetRoundName(nb));
394  ginfo.push_back(ie->GetSiteName(nb));
395  ginfo.push_back(ie->GetNagCount());
396  ginfo.push_back(ie->GetCommentCount());
397  ginfo.push_back(ie->GetVariationCount());
398  char deleted[2] = {0};
399  deleted[0] = (ie->GetDeleteFlag()) ? 'D' : ' ';
400  ginfo.push_back(deleted);
401  char flags[16];
402  ie->GetFlagStr (flags, "WBMENPTKQ!?U123456");
403  ginfo.push_back(flags);
404  ecoStringT ecoStr;
405  eco_ToExtendedString (ie->GetEcoCode(), ecoStr);
406  ginfo.push_back(ecoStr);
407  std::string endMaterial = matsig_makeString(ie->GetFinalMatSig());
408  ginfo.push_back(endMaterial);
409  char startpos[2] = {0};
410  startpos[0] = (ie->GetStartFlag()) ? 'S' : ' ';
411  ginfo.push_back(startpos);
412  char buf_eventdate[16];
413  date_DecodeToString (ie->GetEventDate(), buf_eventdate);
414  ginfo.push_back(buf_eventdate);
415  ginfo.push_back(ie->GetYear());
416  ginfo.push_back((welo + belo)/2);
417  ginfo.push_back(ie->GetRating(nb));
418  FastGame game = dbase->getGame(ie);
419  ginfo.push_back(game.getMoveSAN(ply, 10));
420 
421  res.push_back(to_string(idx+1) + "_" + to_string(ply));
422  res.push_back(ginfo);
423  res.push_back(deleted);
424  }
425 
426  delete [] idxList;
427  return UI_Result(ti, OK, res);
428 }
429 
430 
431 /**
432 * sc_base_getGame() - return all the positions of a game
433 * @gameNum: the number of the requested game
434 *
435 * Return a list containing all the positions of a game, including variations.
436 * The positions are sorted according to pgn standard:
437 * "The alternate move sequence given by an RAV is one that may be legally played
438 * by first unplaying the move that appears immediately prior to the RAV".
439 * For each position the following informations are provided:
440 * RAVdepth: current variation depth.
441 * RAVnum: current variation num.
442 * FEN: "Forsyth-Edwards Notation" describing the current position.
443 * NAGS: "Numeric Annotation Glyph" is a non-negative integer from 0 to 255
444 * used to indicate a simple annotation in a language independent manner.
445 * comment: text annotation of the current position.
446 * lastMoveSAN: the last move that was played to reach the current position.
447 * The move is indicated using English "Standard Algebraic Notation".
448 */
449 UI_res_t sc_base_getGame(scidBaseT* dbase, UI_handle_t ti, int argc, const char** argv) {
450  const char* usage = "Usage: sc_base getGame baseId gameNum";
451  if (argc != 4) return UI_Result(ti, ERROR_BadArg, usage);
452 
453  const IndexEntry* ie = 0;
454  gamenumT gNum = strGetUnsigned(argv[3]);
455  if (gNum > 0) ie = dbase->getIndexEntry_bounds(gNum - 1);
456  if (ie == 0) return UI_Result(ti, ERROR_BadArg, usage);
457 
458  std::vector<scidBaseT::GamePos> gamepos;
459  errorT err = dbase->getGame(ie, gamepos);
460  if (err != OK) return UI_Result(ti, err);
461 
462  size_t n = gamepos.size();
463  UI_List res(n);
464  UI_List posInfo(6);
465  for (size_t i = 0; i < n; i++) {
466  posInfo.clear();
467  posInfo.push_back(gamepos[i].RAVdepth);
468  posInfo.push_back(gamepos[i].RAVnum);
469  posInfo.push_back(gamepos[i].FEN);
470  std::string nags;
471  for (size_t iNag = 0, nNag = gamepos[i].NAGs.size(); iNag < nNag; iNag++) {
472  char temp[20];
473  game_printNag(gamepos[i].NAGs[iNag], temp, true, PGN_FORMAT_Plain);
474  if (!nags.empty()) nags += ' ';
475  nags += temp;
476  }
477  posInfo.push_back(nags);
478  posInfo.push_back(gamepos[i].comment);
479  posInfo.push_back(gamepos[i].lastMoveSAN);
480  res.push_back(posInfo);
481  }
482 
483  return UI_Result(ti, OK, res);
484 }
485 
486 
487 /**
488  * sc_base_import() - import games from non-native database
489  *
490  * Return:
491  * On success, returns a list of two elements: the number of
492  * games imported and a string containing import errors
493  * or warnings.
494  */
495 UI_res_t sc_base_import(scidBaseT* dbase, UI_handle_t ti, int argc, const char** argv)
496 {
497  const char* usage = "Usage: sc_base import baseId filename";
498  if (argc != 4) return UI_Result(ti, ERROR_BadArg, usage);
499 
500  if (dbase->isReadOnly()) return UI_Result(ti, ERROR_FileReadOnly);
501 
502  const char* filename = argv[3];
503  uint gamesSeen = 0;
504  errorT err = ERROR_BadArg;
505  std::string errorMsg;
506  Progress progress = UI_CreateProgress(ti);
507 
508  // if (pgn) {
509  CodecPgn codec;
510  err = codec.open(filename, FMODE_ReadOnly);
511  if (err == OK) {
512  err = dbase->importGames(codec, progress, gamesSeen, errorMsg);
513  }
514  // }
515 
516  if (err != OK) return UI_Result(ti, err);
517 
518  UI_List res(2);
519  res.push_back(gamesSeen);
520  res.push_back(errorMsg);
521  return UI_Result(ti, OK, res);
522 }
523 
524 
525 /**
526  * sc_base_list() - return a baseId list of opened databases
527  */
528 UI_res_t sc_base_list(UI_handle_t ti, int argc, const char**) {
529  const char* usage = "Usage: sc_base list";
530  if (argc != 2) return UI_Result(ti, ERROR_BadArg, usage);
531 
532  std::vector<int> l = DBasePool::getHandles();
533  UI_List res(l.size());
534  for (size_t i=0, n=l.size(); i < n; i++) res.push_back(l[i]);
535  return UI_Result(ti, OK, res);
536 }
537 
538 
539 /**
540  * sc_base_numGames() - return the number of games in the database
541  */
542 UI_res_t sc_base_numGames(scidBaseT* dbase, UI_handle_t ti, int argc,
543  const char**) {
544  const char* usage = "Usage: sc_base numGames baseId";
545  if (argc != 3) return UI_Result(ti, ERROR_BadArg, usage);
546 
547  return UI_Result(ti, OK, dbase->numGames());
548 }
549 
550 
551 /**
552  * sc_base_open() - open a database
553  * @codec : the type of the database
554  * @filename: the filename of the wanted database.
555  * Database in native Scid format do not use extension ("example").
556  * Other databases require file extension ("example.pgn").
557  *
558  * Return:
559  * - on success or with ERROR_NameDataLoss: the handle assigned to the database
560  */
561 UI_res_t sc_base_open(UI_handle_t ti, int argc, const char** argv) {
562  const char* usage = "Usage: sc_base open <SCID4|PGN> filename";
563  if (argc != 4 && argc != 3) return UI_Result(ti, ERROR_BadArg, usage);
564 
565  // Old interface defaults to SCID4
567  if (argc == 4) {
568  if (std::strcmp("SCID4", argv[2]) == 0)
569  codec = ICodecDatabase::SCID4;
570  else if (std::strcmp("PGN", argv[2]) == 0)
571  codec = ICodecDatabase::PGN;
572  else
573  return UI_Result(ti, ERROR_BadArg, usage);
574  }
575 
576  return doOpenBase(ti, argc == 4 ? argv[3] : argv[2], FMODE_Both, codec);
577 }
578 
579 
580 /**
581  * sc_base_slot() - get the handle of an opened database
582  * @filename: the filename of the wanted database.
583  * Database in native Scid format do not use extension ("example").
584  * Other databases require file extension ("example.pgn").
585  *
586  * Return:
587  * - the handle of the database corresponding to @filename.
588  * - 0 if not found.
589  */
590 UI_res_t sc_base_slot(UI_handle_t ti, int argc, const char** argv)
591 {
592  const char* usage = "Usage: sc_base slot filename";
593  if (argc != 3) return UI_Result(ti, ERROR_BadArg, usage);
594 
595  int res = DBasePool::find(argv[2]);
596  return UI_Result(ti, OK, res);
597 }
598 
599 
600 /**
601  * sc_base_sortcache() - create/release a sortcache
602  *
603  * A sortchace is used to speed up the other sc_base functions with the same "sortCrit"
604  */
605 UI_res_t sc_base_sortcache(scidBaseT* dbase, UI_handle_t ti, int argc, const char** argv)
606 {
607  const char* usage = "Usage: sc_base sortcache baseId <create|release> sortCrit";
608  if (argc != 5) return UI_Result(ti, ERROR_BadArg, usage);
609 
610  if (std::strcmp("create", argv[3]) == 0) {
611  if (dbase->createSortCache(argv[4]) == NULL)
612  return UI_Result(ti, ERROR);
613  } else {
614  dbase->releaseSortCache(argv[4]);
615  }
616  return UI_Result(ti, OK);
617 }
618 
619 
620 /**
621  * sc_base_stats() - return statistics about a database.
622  *
623  * The returned values are specific for every subcomand:
624  * dates -> {minimum year} {maximum year} {mean year}
625  * eco ? -> {number of games with for the ? eco} {white wins} {draws}
626  * {black wins} {n_games with empty result} {score in %}
627  * ? must be a valid ECO code or an abbreviation, for example:
628  * A returns the sum of all the A00,A00a...A99z4
629  * A1 returns the sum of all the A10,A10a,A10a1...A19z4
630  * A10 returns the sum of all the A10,A10a,A10a1...A10z4
631  * An empty string returns the sum of all valid ECO codes
632  * flag ? -> {number of games with the ? flag set}
633  * ? must be a valid flag char (see IndexEntry::CharToFlag())
634  * flags -> {n_games with D flag set} {n_games with W flag set} {n_games with B flag set}
635  * ratings -> {minimum elo} {maximum elo {mean elo}
636  * results -> {number of games won by white} {number of draws} {number of
637  * games won by black} {number of games with no result}
638  */
639 UI_res_t sc_base_stats(const scidBaseT* dbase, UI_handle_t ti, int argc, const char ** argv)
640 {
641  const char* usage = "Usage: sc_base stats baseId <dates|eco ?|flag ?|flags|ratings|results>";
642  if (argc < 4) return UI_Result(ti, ERROR_BadArg, usage);
643 
644  const char* subcmd = argv[3];
645  const scidBaseT::Stats& stats = dbase->getStats();
646  UI_List res(6);
647 
648  enum { OPT_DATE, OPT_ECO, OPT_FLAG, OPT_FLAGS, OPT_RATINGS, OPT_RESULTS };
649  const char * options[] = { "dates", "eco", "flag", "flags", "ratings", "results", NULL };
650  switch (strExactMatch(subcmd, options)) {
651  case OPT_DATE:
652  res.push_back(date_GetYear(stats.minDate));
653  res.push_back(date_GetYear(stats.maxDate));
654  res.push_back(stats.nYears == 0 ? 0 : size_t(stats.sumYears / stats.nYears));
655  break;
656  case OPT_ECO: {
657  const scidBaseT::Stats::Eco* eco = (argc != 5) ? 0 : stats.getEcoStats(argv[4]);
658  if (eco == 0) return UI_Result(ti, ERROR_BadArg, usage);
659  res.push_back(eco->count);
660  res.push_back(eco->results[RESULT_White]);
661  res.push_back(eco->results[RESULT_Draw]);
662  res.push_back(eco->results[RESULT_Black]);
663  res.push_back(eco->results[RESULT_None]);
664  ASSERT(eco->count >= eco->results[RESULT_None]);
665  uint count = eco->count - eco->results[RESULT_None];
666  uint score = eco->results[RESULT_White] * 2;
667  score += eco->results[RESULT_Draw];
668  score *= 500;
669  res.push_back(count == 0 ? 0.0 : score / count / 10.0);
670  break; }
671  case OPT_FLAG: {
672  uint flag = (argc != 5) ? 0 : IndexEntry::CharToFlag(*(argv[4]));
673  if (flag == 0) return UI_Result(ti, ERROR_BadArg, usage);
674  res.push_back(stats.flagCount[flag]);
675  break; }
676  case OPT_FLAGS:
677  res.push_back(stats.flagCount[IndexEntry::CharToFlag('D')]);
678  res.push_back(stats.flagCount[IndexEntry::CharToFlag('W')]);
679  res.push_back(stats.flagCount[IndexEntry::CharToFlag('B')]);
680  break;
681  case OPT_RATINGS:
682  res.push_back(stats.minRating);
683  res.push_back(stats.maxRating);
684  res.push_back(stats.nRatings == 0 ? 0 : size_t(stats.sumRatings / stats.nRatings));
685  break;
686  case OPT_RESULTS:
687  res.push_back(stats.nResults[RESULT_White]);
688  res.push_back(stats.nResults[RESULT_Draw]);
689  res.push_back(stats.nResults[RESULT_Black]);
690  res.push_back(stats.nResults[RESULT_None]);
691  break;
692  default:
693  return UI_Result(ti, ERROR_BadArg, usage);
694  }
695 
696  return UI_Result(ti, OK, res);
697 }
698 
699 
700 /**
701  * sc_base_switch() - change the current database and the current game
702  *
703  * DEPRECATED
704  * Unfortunately Scid used to have only one database, one game, one filter, etc...
705  * This function changes the current database and consequently the current game
706  * (sc_game functions works on the current game)
707  *
708  * Return: the current database ID after the switch
709  */
710 UI_res_t sc_base_switch(scidBaseT* dbase, UI_handle_t ti)
711 {
712  int res = DBasePool::switchCurrent(dbase);
713  return UI_Result(ti, OK, res);
714 }
715 
716 
717 /**
718  * sc_base_tournaments() - return a list of tournaments
719  *
720  * Games with the same [Event], [Site] and [EventDate] tags are considered
721  * a tournament (also games with empty [EventDate] tag are included).
722  * The list returned can be restricted based on the average elo of the
723  * participants, the number of games, the number of players and the name
724  * of a participant.
725  * Ranges can be specified with one or two numbers separated by spaces (min max).
726  * The returned list can be sorted according to the following criteria:
727  * - "Date" : date of the first games in the tournament
728  * - "Players" : number of participants to the tournament
729  * - "Games" : number of games
730  * - "Elo" : average elo of the participants
731  * - "Site" : site name
732  * - "Event" : event name
733  *
734  * Return:
735  * On success, return a list of tournaments.
736  * For each tournament the following info are provided:
737  * date of the first games, site name, event name, number of players,
738  * number of games, average elo of the participants, lowest game number,
739  * winner name, winner elo, winner score, 2nd place name, 2nd elo, 2nd score.
740  */
741 UI_res_t sc_base_tournaments(const scidBaseT* dbase, UI_handle_t ti, int argc, const char ** argv)
742 {
743  const char* usage = "Usage: sc_base tournaments baseId filterName n_maxResults [-avgelo range] [-n_games range] [-n_players range] [-sort criteria] ";
744  if (argc < 5) return UI_Result(ti, ERROR_BadArg, usage);
745 
746  const HFilter filter = dbase->getFilter(argv[3]);
747  if (filter == 0) return UI_Result(ti, ERROR_BadArg, usage);
748 
749  SearchTournaments search(dbase, filter);
750 
751  const char* sortCriteria = 0;
752  long nResults = strGetUnsigned(argv[4]);
753 
754  static const char* options[] = {
755  "-avgelo", "-n_games", "-n_players", "-player", "-sort", NULL
756  };
757  enum { AVGELO, N_GAMES, N_PLAYERS, PLAYER, SORT };
758 
759  for (int i = 5; (i + 1) < argc; i += 2) {
760  int index = strUniqueMatch(argv[i], options);
761  const char* value = argv[i + 1];
762  if (*value == 0) continue;
763  switch (index) {
764  case AVGELO:
765  search.filterByAvgElo(StrRange(value));
766  break;
767  case N_GAMES:
768  search.filterByNGames(StrRange(value));
769  break;
770  case N_PLAYERS:
771  search.filterByNPlayers(StrRange(value));
772  break;
773  case PLAYER:
774  search.filterByPlayer(value);
775  break;
776  case SORT:
777  sortCriteria = value;
778  break;
779  default:
780  return UI_Result(ti, ERROR_BadArg, usage);
781  }
782  }
783 
784  if (sortCriteria != 0) {
785  if (!search.sort(sortCriteria, nResults))
786  return UI_Result(ti, ERROR_BadArg, usage);
787  }
788 
789  SearchTournaments::Iter it = search.begin();
790  SearchTournaments::Iter it_end = search.end();
791  if (std::distance(it, it_end) > nResults) {
792  it_end = it + nResults;
793  }
794 
795  char buf_date[16];
796  const NameBase* nb = dbase->getNameBase();
797  UI_List res(std::distance(it, it_end));
798  UI_List tourney(14);
799  for (; it != it_end; it++) {
800  tourney.clear();
801  date_DecodeToString(it->getStartDate(), buf_date);
802  strTrimDate(buf_date);
803  tourney.push_back(buf_date);
804  tourney.push_back(nb->GetName(NAME_SITE, it->getSiteId()));
805  tourney.push_back(nb->GetName(NAME_EVENT, it->getEventId()));
806  tourney.push_back(it->nPlayers());
807  tourney.push_back(it->nGames());
808  tourney.push_back(it->getAvgElo());
809  tourney.push_back(it->getStartGameNum() + 1);
810  const char* name1st = "";
811  eloT elo1st = 0;
812  double score1st = 0.0;
813  const char* name2nd = "";
814  eloT elo2nd = 0;
815  double score2nd = 0.0;
816  if (it->nPlayers() > 0) {
817  const Tourney::Player& p = it->getPlayer(0);
818  name1st = nb->GetName(NAME_PLAYER, p.nameId);
819  elo1st = p.elo;
820  score1st = p.score / 2.0;
821  }
822  if (it->nPlayers() > 1) {
823  const Tourney::Player& p = it->getPlayer(1);
824  name2nd = nb->GetName(NAME_PLAYER, p.nameId);
825  elo2nd = p.elo;
826  score2nd = p.score / 2.0;
827  }
828  tourney.push_back(name1st);
829  tourney.push_back(elo1st);
830  tourney.push_back(score1st);
831  tourney.push_back(name2nd);
832  tourney.push_back(elo2nd);
833  tourney.push_back(score2nd);
834 
835  res.push_back(tourney);
836  }
837 
838  return UI_Result(ti, OK, res);
839 }
840 
841 } // End of anonymous namespace
842 
843 //TODO: move this function here from tkscid.cpp
844 UI_res_t sc_base_inUse (UI_extra_t, UI_handle_t, int argc, const char ** argv);
845 UI_res_t sc_base_export (UI_extra_t, UI_handle_t, int argc, const char ** argv);
846 UI_res_t sc_base_piecetrack (UI_extra_t, UI_handle_t, int argc, const char ** argv);
847 UI_res_t sc_base_tag (UI_extra_t, UI_handle_t, int argc, const char ** argv);
848 uint sc_base_duplicates (scidBaseT* dbase, UI_handle_t, int argc, const char ** argv);
849 
850 
851 UI_res_t sc_base (UI_extra_t cd, UI_handle_t ti, int argc, const char ** argv)
852 {
853  static const char * options [] = {
854  "close", "compact", "copygames",
855  "create", "current", "duplicates",
856  "export", "extra", "filename", "gameflag",
857  "gamelocation", "gameslist", "getGame", "import",
858  "inUse", "isReadOnly", "list", "numGames", "open",
859  "piecetrack", "slot", "sortcache", "stats",
860  "switch", "tag", "tournaments", "type",
861  NULL
862  };
863  enum {
864  BASE_CLOSE, BASE_COMPACT, BASE_COPYGAMES,
865  BASE_CREATE, BASE_CURRENT, BASE_DUPLICATES,
866  BASE_EXPORT, BASE_EXTRA, BASE_FILENAME, BASE_GAMEFLAG,
867  BASE_GAMELOCATION, BASE_GAMESLIST, BASE_GETGAME, BASE_IMPORT,
868  BASE_INUSE, BASE_ISREADONLY, BASE_LIST, BASE_NUMGAMES, BASE_OPEN,
869  BASE_PTRACK, BASE_SLOT, BASE_SORTCACHE, BASE_STATS,
870  BASE_SWITCH, BASE_TAG, BASE_TOURNAMENTS, BASE_TYPE
871  };
872 
873  if (argc <= 1) return UI_Result(ti, ERROR_BadArg, "Usage: sc_base <cmd>");
874 
875  int index = strUniqueMatch (argv[1], options);
876  switch (index) {
877  case BASE_CREATE:
878  return sc_base_create(ti, argc, argv);
879 
880  case BASE_CURRENT:
881  return sc_base_switch(0, ti);
882 
883  case BASE_EXPORT:
884  return sc_base_export (cd, ti, argc, argv);
885 
886  case BASE_INUSE:
887  return sc_base_inUse (cd, ti, argc, argv);
888 
889  case BASE_LIST:
890  return sc_base_list(ti, argc, argv);
891 
892  case BASE_OPEN:
893  return sc_base_open(ti, argc, argv);
894 
895  case BASE_PTRACK:
896  return sc_base_piecetrack (cd, ti, argc, argv);
897 
898  case BASE_SLOT:
899  return sc_base_slot (ti, argc, argv);
900 
901  case BASE_TAG:
902  return sc_base_tag (cd, ti, argc, argv);
903  }
904 
905  //New multi-base functions
906  if (argc < 3) return UI_Result(ti, ERROR_BadArg, "Usage: sc_base <cmd> baseId [args]");
907  scidBaseT* dbase = DBasePool::getBase(strGetUnsigned(argv[2]));
908  if (dbase == 0) return UI_Result(ti, ERROR_FileNotOpen);
909 
910  switch (index) {
911  case BASE_CLOSE:
912  return sc_base_close(dbase, ti, argc, argv);
913 
914  case BASE_COMPACT:
915  return sc_base_compact(dbase, ti, argc, argv);
916 
917  case BASE_COPYGAMES:
918  return sc_base_copygames(dbase, ti, argc, argv);
919 
920  case BASE_DUPLICATES:
921  if (dbase->isReadOnly()) return UI_Result(ti, ERROR_FileReadOnly);
922  return UI_Result(ti, OK, sc_base_duplicates (dbase, ti, argc, argv));
923 
924  case BASE_EXTRA:
925  return sc_base_extra(dbase, ti, argc, argv);
926 
927  case BASE_FILENAME:
928  return sc_base_filename(dbase, ti, argc, argv);
929 
930  case BASE_GAMEFLAG:
931  return sc_base_gameflag(dbase, ti, argc, argv);
932 
933  case BASE_GAMELOCATION:
934  return sc_base_gamelocation(dbase, ti, argc, argv);
935 
936  case BASE_GAMESLIST:
937  return sc_base_gameslist(dbase, ti, argc, argv);
938 
939  case BASE_GETGAME:
940  return sc_base_getGame(dbase, ti, argc, argv);
941 
942  case BASE_IMPORT:
943  return sc_base_import (dbase, ti, argc, argv);
944 
945  case BASE_NUMGAMES:
946  return sc_base_numGames (dbase, ti, argc, argv);
947 
948  case BASE_ISREADONLY:
949  return UI_Result(ti, OK, dbase->isReadOnly());
950 
951  case BASE_SORTCACHE:
952  return sc_base_sortcache(dbase, ti, argc, argv);
953 
954  case BASE_STATS:
955  return sc_base_stats(dbase, ti, argc, argv);
956 
957  case BASE_SWITCH:
958  return sc_base_switch (dbase, ti);
959 
960  case BASE_TOURNAMENTS:
961  return sc_base_tournaments (dbase, ti, argc, argv);
962 
963  }
964 
965  std::string res = "sc_base\nInvalid minor command: ";
966  res.append(argv[1]);
967  return UI_Result(ti, ERROR_BadArg, res);
968 }
bool getFlag(uint flag, uint gNum) const
Definition: scidbase.h:154
uint date_GetYear(dateT date)
Definition: date.h:55
const errorT ERROR_Full
Definition: error.h:49
bool report(size_t done, size_t total) const
Definition: misc.h:136
int strExactMatch(const char *keyStr, const char **strTable)
Definition: misc.h:339
std::string matsig_makeString(matSigT m)
Definition: matsig.cpp:27
const errorT OK
Definition: error.h:23
int switchCurrent(scidBaseT *dbase=0)
switchCurrent() - DEPRECATED.
Definition: dbasepool.cpp:84
std::vector< Tourney >::const_iterator Iter
const errorT ERROR_FileInUse
Definition: error.h:37
SortCache * createSortCache(const char *criteria)
Increment the reference count of a SortCache object matching criteria.
Definition: scidbase.cpp:855
class SearchTournamens - Search tournaments in a database
#define ASSERT(f)
Definition: common.h:67
const errorT ERROR_BadArg
Definition: error.h:28
gamenumT numGames() const
Definition: scidbase.h:97
UI_res_t sc_base_piecetrack(UI_extra_t, UI_handle_t, int argc, const char **argv)
dateT GetEventDate() const
Definition: indexentry.h:235
const resultT RESULT_Black
Definition: common.h:187
const IndexEntry * getIndexEntry(gamenumT g) const
Definition: scidbase.h:101
Definition: misc.h:124
std::string to_string(int val)
Definition: misc.h:153
uint GetFlagStr(char *dest, const char *flags) const
Definition: indexentry.h:751
errorT Close()
Definition: scidbase.cpp:124
int strUniqueMatch(const char *keyStr, const char **strTable)
Definition: misc.h:336
class StrRange - parse a string interpreting its content as 1 or 2 integers separated by whitespace...
Definition: misc.h:37
uint64_t sumYears
Definition: scidbase.h:43
eloT GetBlackElo() const
Definition: indexentry.h:252
const gamenumT INVALID_GAMEID
Definition: scidbase.h:35
static uint CharToFlag(char ch)
Definition: indexentry.h:670
scidBaseT * getBase(int baseHandle)
getBase() - get a database from the pool.
Definition: dbasepool.cpp:59
char ecoStringT[6]
Definition: common.h:162
int UI_res_t
Definition: ui_tcltk.h:30
resultT GetResult() const
Definition: indexentry.h:245
uint results[NUM_RESULT_TYPES]
Definition: scidbase.h:54
void date_DecodeToString(dateT date, char *str)
Definition: date.h:90
const resultT RESULT_Draw
Definition: common.h:188
errorT importGame(const scidBaseT *srcBase, uint gNum)
Definition: scidbase.cpp:350
ecoT GetEcoCode() const
Definition: indexentry.h:264
uint GetVariationCount() const
Definition: indexentry.h:325
matSigT GetFinalMatSig() const
Definition: indexentry.h:329
int find(const char *filename)
find() - search for a database.
Definition: dbasepool.cpp:51
static uint32_t CharToFlagMask(char flag)
Definition: indexentry.h:700
const char RESULT_STR[4][4]
Definition: common.h:192
errorT setExtraInfo(const std::string &tagname, const char *new_value)
Definition: scidbase.cpp:199
const std::string & getFileName() const
Definition: scidbase.h:95
const Stats & getStats() const
Statistics.
Definition: scidbase.cpp:497
uint64_t sumRatings
Definition: scidbase.h:46
UI_res_t sc_base(UI_extra_t cd, UI_handle_t ti, int argc, const char **argv)
Definition: sc_base.cpp:851
errorT invertFlag(uint flag, uint gNum)
Definition: scidbase.h:366
const char * GetBlackName(const NameBase *nb) const
Definition: indexentry.h:186
bool GetDeleteFlag() const
Definition: indexentry.h:318
bool isReadOnly() const
Definition: scidbase.h:96
sort?type?
Definition: analysis.tcl:321
size_t sortedPosition(const char *criteria, const HFilter &filter, gamenumT gameId)
Get the sorted position of a game.
Definition: scidbase.cpp:872
const resultT RESULT_White
Definition: common.h:186
uint32_t uint
Definition: common.h:99
uint GetCommentCount() const
Definition: indexentry.h:326
bool GetStartFlag() const
Definition: indexentry.h:312
uint sc_base_duplicates(scidBaseT *dbase, UI_handle_t, int argc, const char **argv)
Definition: tkscid.cpp:825
const char * GetName(nameT nt, idNumberT id) const
Definition: namebase.h:97
const char * GetSiteName(const NameBase *nb) const
Definition: indexentry.h:192
std::string getMoveSAN(int ply_to_skip, int count)
Definition: fastgame.h:293
errorT getCompactStat(uint *n_deleted, uint *n_unused, uint *n_sparse, uint *n_badNameId)
Definition: scidbase.cpp:652
bool strAlphaContains(const char *longStr, const char *keyStr)
Definition: misc.h:482
statsfmt
Definition: optable.tcl:717
errorT getExtraInfo(const std::string &tagname, std::string *res) const
Definition: scidbase.cpp:180
ushort GetNumHalfMoves() const
Definition: indexentry.h:265
ushort eloT
Definition: common.h:160
void releaseSortCache(const char *criteria)
Decrement the reference count of the SortCache object matching criteria.
Definition: scidbase.cpp:841
UI_res_t sc_base_tag(UI_extra_t, UI_handle_t, int argc, const char **argv)
void eco_ToExtendedString(ecoT ecoCode, char *ecoStr)
Definition: misc.h:186
uint32_t strGetUnsigned(const char *str)
Definition: misc.h:276
unsigned short errorT
Definition: error.h:20
size_t size() const
Definition: hfilter.h:161
const Eco * getEcoStats(const char *ecoStr) const
Definition: scidbase.cpp:581
uint nResults[NUM_RESULT_TYPES]
Definition: scidbase.h:44
Definition: errors.tcl:17
scidBaseT * getFreeSlot()
getFreeSlot() - search for a free database slot.
Definition: dbasepool.cpp:69
uint flagCount[IndexEntry::IDX_NUM_FLAGS]
Definition: scidbase.h:39
const IndexEntry * getIndexEntry_bounds(gamenumT g) const
Definition: scidbase.h:104
errorT importGames(const scidBaseT *srcBase, const HFilter &filter, const Progress &progress)
Definition: scidbase.cpp:361
const errorT ERROR_FileReadOnly
Definition: error.h:41
UI_res_t UI_Result(UI_handle_t ti, errorT res)
UI_Result() - pass the result of an operation from c++ to UI : OK for success or an error code (error...
Definition: ui.h:140
std::vector< int > getHandles()
getHandles() - get the handles of opened databases.
Definition: dbasepool.cpp:76
errorT compact(const Progress &progress)
Definition: scidbase.cpp:688
void push_back(Tcl_Obj *value)
Definition: ui_tcltk.h:154
const errorT ERROR_FileNotOpen
Definition: error.h:36
class UI_List - create a list of values to be sent to UI : currently there is no automatic reallocati...
Definition: ui.h:163
Tcl_Interp * UI_handle_t
Definition: ui_tcltk.h:32
byte get(gamenumT gnum) const
Definition: hfilter.h:168
const char * GetEventName(const NameBase *nb) const
Definition: indexentry.h:189
cmd
Definition: fics.tcl:443
Progress UI_CreateProgress(UI_handle_t ti)
UI_CreateProgress() - create a Progress object.
Definition: ui.h:122
errorT open(const char *filename, fileModeT fmode)
Opens/creates a PGN database.
Definition: codec_pgn.h:63
errorT Open(ICodecDatabase::Codec dbtype, fileModeT mode, const char *filename=0, const Progress &progress=Progress())
Definition: scidbase.cpp:89
bool strGetBoolean(const char *str)
Definition: misc.cpp:579
uint gamenumT
Definition: common.h:159
void game_printNag(byte nag, char *str, bool asSymbol, gameFormatT format)
Definition: game.cpp:143
UI_res_t sc_base_inUse(UI_extra_t, UI_handle_t, int argc, const char **argv)
fileModeT
Definition: common.h:144
eloT GetWhiteElo() const
Definition: indexentry.h:246
const resultT RESULT_None
Definition: common.h:185
const errorT ERROR_NameDataLoss
Definition: error.h:54
uint64_t nYears
Definition: scidbase.h:42
const char * GetRoundName(const NameBase *nb) const
Definition: indexentry.h:195
byte GetRating(const NameBase *nb) const
Definition: indexentry.h:634
HFilter getFilter(const std::string &filterId) const
Definition: scidbase.h:177
const NameBase * getNameBase() const
Definition: scidbase.h:108
void strTrimDate(char *str)
Definition: misc.cpp:389
dateT GetDate() const
Definition: indexentry.h:231
const char * GetWhiteName(const NameBase *nb) const
Definition: indexentry.h:183
UI_res_t sc_base_export(UI_extra_t, UI_handle_t, int argc, const char **argv)
Definition: indexentry.h:54
uint GetNagCount() const
Definition: indexentry.h:327
FastGame getGame(const IndexEntry *ie) const
Definition: scidbase.h:111
Implements the CodecPgn class, which manages the databases encoded in PGN format. ...
size_t listGames(const char *criteria, size_t start, size_t count, const HFilter &filter, gamenumT *destCont)
Retrieve a list of ordered game indexes sorted by criteria.
Definition: scidbase.cpp:863
ClientData UI_extra_t
Definition: ui_tcltk.h:31
uint GetYear() const
Definition: indexentry.h:232
errorT setFlag(bool value, uint flag, uint gNum)
Definition: scidbase.h:380