feisty meow concerns codebase  2.140
nechung_oracle.cpp
Go to the documentation of this file.
1 /*****************************************************************************\
2 * *
3 * Name : nechung_oracle *
4 * Author : Chris Koeritz *
5 * *
6 *******************************************************************************
7 * Copyright (c) 1991-$now By Author. This program is free software; you can *
8 * redistribute it and/or modify it under the terms of the GNU General Public *
9 * License as published by the Free Software Foundation; either version 2 of *
10 * the License or (at your option) any later version. This is online at: *
11 * http://www.fsf.org/copyleft/gpl.html *
12 * Please send any updates to: fred@gruntose.com *
13 *****************************************************************************/
14 
15 #include "nechung_oracle.h"
16 
17 #include <basis/astring.h>
18 #include <filesystem/byte_filer.h>
19 #include <filesystem/file_time.h>
22 
23 #include <stdio.h>
24 #include <string.h>
25 
26 //using namespace application;
27 using namespace basis;
28 using namespace filesystem;
29 using namespace loggers;
30 
31 #undef LOG
32 #define LOG(s) program_wide_logger::get().log(s, 0)
34 
35 const int MAX_LINE_LENGTH = 2048;
36 
37 nechung_oracle::nechung_oracle(const astring &nechung_filename,
38  const astring &index_filename)
39 : c_randomizer(),
40  c_filename_held(nechung_filename),
41  c_index_held(index_filename),
42  c_number_of_fortunes(0)
43 { parse_file(); }
44 
46 
47 void nechung_oracle::parse_file()
48 {
49  FUNCDEF("parse_file");
50  // below is code for comparing dates on the fortune file and the index file.
51  byte_filer fortune_file(c_filename_held.s(), "rb");
52 #ifdef DEBUG_NECHUNG
53  LOG(astring("filename=") + c_filename_held + " idx file=" + c_index_held);
54 #endif
55  if (!fortune_file.good())
56  non_continuable_error(class_name(), func, "Cannot open fortune file.");
57 
58  byte_array buffer(MAX_LINE_LENGTH + 1);
59  // used throughout parsing for line storage.
60 
61  byte_filer index_file(c_index_held.observe(), "r");
62  if (index_file.good()) {
63 #ifdef DEBUG_NECHUNG
64  LOG("index file exists");
65 #endif
66  file_time index_time((FILE *)index_file.file_handle());
67  file_time fortune_time((FILE *)fortune_file.file_handle());
68  if (index_time >= fortune_time) {
69  // need to read in the list of indices
70  index_file.getline(buffer, MAX_LINE_LENGTH);
71  sscanf((char *)buffer.access(), "%d", &c_number_of_fortunes);
72 #ifdef DEBUG_NECHUNG
73  LOG(astring(astring::SPRINTF, "%d entries in index",
74  c_number_of_fortunes));
75 #endif
76  return;
77  }
78  }
79  index_file.close();
80 
81  // below is code for creating the list.
82  enum fortune_states {
83  chowing_separators, // looking for the breaks between fortunes.
84  adding_fortunes, // saw the separator so get ready for a new fortune.
85  chowing_fortunes, // currently in a fortune accumulating lines.
86  done_parsing // finished parsing the fortune file.
87  };
88 
89  c_number_of_fortunes = 0;
90  fortune_states state = chowing_separators;
91 
92  int posn;
93  int_array fortune_posns; // our list of fortunes.
94  while (state != done_parsing) {
95 #ifdef DEBUG_NECHUNG
96  LOG(astring(astring::SPRINTF, "#%d", c_number_of_fortunes));
97 #endif
98  if (fortune_file.eof()) {
99  // exit from the loop now...
100  state = done_parsing;
101  continue;
102  }
103  switch (state) {
104  case chowing_separators: {
105 #ifdef DEBUG_NECHUNG
106  LOG("chowseps, ");
107 #endif
108  posn = int(fortune_file.tell());
109  if (posn < 0)
110  non_continuable_error(class_name(), func, "Cannot get file position.");
111  fortune_file.getline(buffer, MAX_LINE_LENGTH);
112 #ifdef DEBUG_NECHUNG
113  LOG(astring("got a line: ") + buffer);
114 #endif
115  if (buffer[0] != NECHUNG_SEPARATION_CHARACTER) state = adding_fortunes;
116  else {
117  // special casing is for when we see a separator on the line
118  // by itself versus when it is the beginning of a line. if the
119  // beginning of a line, we currently take that to mean the rest
120  // of the line is the fortune.
121  if (strlen((char *)buffer.access()) == 2) posn += 2;
122  else posn++;
123  state = adding_fortunes;
124  }
125  break;
126  }
127  case adding_fortunes: {
128 #ifdef DEBUG_NECHUNG
129  LOG("add forts, ");
130 #endif
131  fortune_posns += posn;
132  c_number_of_fortunes++;
133  state = chowing_fortunes;
134  break;
135  }
136  case chowing_fortunes: {
137 #ifdef DEBUG_NECHUNG
138  LOG("chow forts, ");
139 #endif
140  posn = int(fortune_file.tell());
141  if (posn < 0)
142  non_continuable_error(class_name(), func, "Cannot get file size.");
143  fortune_file.getline(buffer, MAX_LINE_LENGTH);
144 #ifdef DEBUG_NECHUNG
145  LOG(astring(astring::SPRINTF, "got a line: %s", buffer.access()));
146  LOG(astring(astring::SPRINTF, "len is %d", strlen((char *)buffer.access())));
147 #endif
148  if ( (buffer[0] == NECHUNG_SEPARATION_CHARACTER)
149  && (strlen((char *)buffer.access()) == 2) )
150  state = chowing_separators;
151  else if (buffer[0] == NECHUNG_SEPARATION_CHARACTER) {
152  posn++;
153  state = adding_fortunes;
154  }
155  break;
156  }
157  case done_parsing: {
158  non_continuable_error(class_name(), func, "Illegal state reached.");
159  }
160  }
161  }
162  fortune_file.close();
163 
164  // make a new index file.
165  index_file.open(c_index_held.observe(), "w");
166  if (!index_file.good())
167  non_continuable_error(class_name(), func, astring("Cannot open index file: ") + c_index_held);
168  astring to_write(astring::SPRINTF, "%d\n", c_number_of_fortunes);
169  index_file.write((abyte *)to_write.s(), to_write.length());
170  for (int j = 0; j < c_number_of_fortunes; j++) {
171  to_write.sprintf("%d\n", fortune_posns[j]);
172  index_file.write((abyte *)to_write.s(), to_write.length());
173  }
174  index_file.close();
175 }
176 
178 {
179  FUNCDEF("pick_random");
180 #ifdef DEBUG_NECHUNG
181  LOG(astring("got to ") + func);
182 #endif
183 
184  byte_filer fortune_file(c_filename_held.s(), "rb");
185 
187 
188  if (!fortune_file.good())
189  non_continuable_error(class_name(), func, "Cannot open data file.");
190  int to_display = c_randomizer.inclusive(0, c_number_of_fortunes - 1);
191 
193 
198  byte_filer index_file(c_index_held.observe(), "r");
199  int chosen_posn = 0; // which position to read the chosen line at.
200  if (index_file.good()) {
201  astring accumulated_text;
202  byte_array buffer(MAX_LINE_LENGTH + 1);
203  for (int i = 0; i <= to_display; i++) {
204 #ifdef DEBUG_NECHUNG
205  accumulated_text += astring(astring::SPRINTF, "#%d: ", i);
206 #endif
207  index_file.getline(buffer, MAX_LINE_LENGTH);
208  sscanf((char *)buffer.access(), "%d", &chosen_posn);
209 #ifdef DEBUG_NECHUNG
210  accumulated_text += astring(astring::SPRINTF, "%d, ", chosen_posn);
211  if ((i + 1) % 5 == 0) accumulated_text += "\n";
212 #endif
213  }
214 #ifdef DEBUG_NECHUNG
215  LOG(accumulated_text);
216 #endif
217 
218  } else {
219  non_continuable_error(class_name(), func, \
220  astring("Could not open the index file \"") + c_index_held + "\"");
221  }
222  index_file.close();
223 #ifdef DEBUG_NECHUNG
224  LOG(astring(astring::SPRINTF, "about to seek @ num %d and "
225  "index %d", to_display, chosen_posn));
226 #endif
227  if (!fortune_file.seek(chosen_posn, byte_filer::FROM_START))
228  non_continuable_error(class_name(), func, "Cannot seek to indexed position.");
229 #ifdef DEBUG_NECHUNG
230  LOG("after seek");
231 #endif
232 
233  astring to_return;
234  byte_array temp(MAX_LINE_LENGTH + 1);
235  while (!fortune_file.eof()) {
236  int chars_read = fortune_file.getline(temp, MAX_LINE_LENGTH);
237  if (!chars_read) {
238  if (!fortune_file.eof()) {
239  non_continuable_error(class_name(), func, "Error while reading fortune.");
240  } else break;
241  }
242  if (temp[0] == NECHUNG_SEPARATION_CHARACTER) break;
243  else to_return += astring((char *)temp.access());
244  }
245  return to_return;
246 }
247 
248 //hmmm: stolen from parser bits. reconnect when available.
249 bool is_eol(char to_check)
250 { return (to_check == '\n') || (to_check == '\r'); }
251 
253 {
254  astring to_show = pick_random();
255  while (is_eol(to_show[to_show.end()]))
256  to_show.zap(to_show.end(), to_show.end());
257  LOG(to_show);
258 }
259 
contents * access()
A non-constant access of the underlying C-array. BE REALLY CAREFUL.
Definition: array.h:175
Provides a dynamically resizable ASCII character string.
Definition: astring.h:35
const char * s() const
synonym for observe. the 's' stands for "string", if that helps.
Definition: astring.h:113
virtual void zap(int start, int end)
Deletes the characters between "start" and "end" inclusively.
Definition: astring.cpp:521
int end() const
returns the index of the last (non-null) character in the string.
Definition: astring.h:86
virtual const char * observe() const
observes the underlying pointer to the zero-terminated string.
Definition: astring.cpp:140
A very common template for a dynamic array of bytes.
Definition: byte_array.h:36
A simple object that wraps a templated array of ints.
Definition: array.h:275
Provides file managment services using the standard I/O support.
Definition: byte_filer.h:32
int getline(basis::abyte *buffer, int desired_size)
reads a line of text (terminated by a return) into the "buffer".
Definition: byte_filer.cpp:201
void close()
shuts down the open file, if any.
Definition: byte_filer.cpp:96
bool seek(int where, origins origin=FROM_START)
places the cursor in the file at "where", based on the "origin".
Definition: byte_filer.cpp:187
bool eof()
returns true if the cursor is at (or after) the end of the file.
Definition: byte_filer.cpp:121
bool good()
returns true if the file seems to be in the appropriate desired state.
Definition: byte_filer.cpp:103
int inclusive(int low, int high) const
< Returns a pseudo-random number r, such that "low" <= r <= "high".
Definition: chaos.h:88
basis::astring pick_random()
nechung_oracle(const basis::astring &data_filename, const basis::astring &index_filename)
virtual ~nechung_oracle()
#define non_continuable_error(c, f, i)
an extra piece of information used, if available, in bounds_halt below.
#define FUNCDEF(func_in)
FUNCDEF sets the name of a function (and plugs it into the callstack).
Definition: enhance_cpp.h:57
The guards collection helps in testing preconditions and reporting errors.
Definition: array.h:30
unsigned char abyte
A fairly important unit which is seldom defined...
Definition: definitions.h:51
A platform independent way to obtain the timestamp of a file.
Definition: byte_filer.cpp:37
A logger that sends to the console screen using the standard output device.
const int MAX_LINE_LENGTH
hmmm: fix filter value to be ALWAYS_PRINT!
#define LOG(s)
bool is_eol(char to_check)
const char NECHUNG_SEPARATION_CHARACTER