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>
22
23#include <stdio.h>
24#include <string.h>
25
26//using namespace application;
27using namespace basis;
28using namespace filesystem;
29using namespace loggers;
30
31#undef LOG
32#define LOG(s) program_wide_logger::get().log(s, 0)
34
35const int MAX_LINE_LENGTH = 2048;
36
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
47void 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.
249bool 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
#define LOG(s)
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:524
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".
void close()
shuts down the open file, if any.
@ FROM_START
offset is from the beginning of the file.
Definition byte_filer.h:94
bool seek(int where, origins origin=FROM_START)
places the cursor in the file at "where", based on the "origin".
bool eof()
returns true if the cursor is at (or after) the end of the file.
bool good()
returns true if the file seems to be in the appropriate desired state.
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:54
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.
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!
bool is_eol(char to_check)
const char NECHUNG_SEPARATION_CHARACTER