feisty meow concerns codebase  2.140
process_control.cpp
Go to the documentation of this file.
1 
2 //NOTE:
3 //
4 // this thing is showing bad behavior on win32 when unicode is enabled.
5 // therefore unicode is currently disabled for win32, which is a shame.
6 // but something needs to be fixed in our unicode conversion stuff; the unicode versions
7 // of the file names were not getting correctly back-converted into the ascii counterpart,
8 // and *that* is broken.
9 // ** this may be a widespread issue in the win32 code related to unicode right now!
10 // ** needs further investigation when a reason to better support ms-windows emerges.
11 //
12 // => but don't panic... right now things work as long as you do a utf-8 / ascii build,
13 // which is how everything's configured.
14 
15 
16 /*
17 * Name : process_control
18 * Author : Chris Koeritz
19 **
20 * Copyright (c) 2000-$now By Author. This program is free software; you can *
21 * redistribute it and/or modify it under the terms of the GNU General Public *
22 * License as published by the Free Software Foundation; either version 2 of *
23 * the License or (at your option) any later version. This is online at: *
24 * http://www.fsf.org/copyleft/gpl.html *
25 * Please send any updates to: fred@gruntose.com *
26 */
27 
28 #include "process_entry.h"
29 #include "process_control.h"
30 
32 #include <basis/astring.h>
33 #include <basis/utf_conversion.h>
35 #include <filesystem/filename.h>
38 #include <mathematics/chaos.h>
39 #include <structures/set.h>
41 
42 #include <stdio.h>
43 #include <stdlib.h>
44 #ifndef _MSC_VER
45  #include <unistd.h>
46 #endif
47 
48 using namespace basis;
49 using namespace configuration;
50 using namespace filesystem;
51 using namespace loggers;
52 using namespace mathematics;
53 using namespace structures;
54 
55 namespace processes {
56 
57 #ifdef _MSC_VER
58  #include <tlhelp32.h>
59  const astring NTVDM_NAME = "ntvdm.exe";
60  // the umbrella process that hangs onto 16 bit tasks for NT.
61 // #ifdef _MSCVER
62 // #include <vdmdbg.h>
63 // #endif
64 #else
65  #include <signal.h>
66  #include <stdio.h>
67 #endif
68 
69 //#define DEBUG_PROCESS_CONTROL
70  // uncomment for noisier debugging.
71 
72 #undef LOG
73 #define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s)
74 
76 
77 class process_implementation_hider
78 {
79 public:
80 #ifdef _MSC_VER
81  // psapi members:
82  application_instance psapi_dll;
83  application_instance vdm_dll;
84  BOOL (WINAPI *enumerate_processes)(basis::un_int *, basis::un_int cb, basis::un_int *);
85  BOOL (WINAPI *enumerate_modules)(HANDLE, HMODULE *, basis::un_int, basis::un_int *);
86  basis::un_int (WINAPI *get_module_name)(HANDLE, HMODULE, LPTSTR, basis::un_int);
87 //#ifdef _MSCVER
88 // INT (WINAPI *tasker_16bit)(basis::un_int, TASKENUMPROCEX fp, LPARAM);
89 //#endif
90 
91  // toolhelp members:
92  application_instance kernel32_dll;
93  HANDLE (WINAPI *create_snapshot)(basis::un_int,basis::un_int);
94  BOOL (WINAPI *first_process)(HANDLE,LPPROCESSENTRY32);
95  BOOL (WINAPI *next_process)(HANDLE,LPPROCESSENTRY32);
96 
97  // get an atomic view of the process list, which rapidly becomes out of date.
99 
100  process_implementation_hider()
101  : psapi_dll(NULL_POINTER), vdm_dll(NULL_POINTER), enumerate_processes(NULL_POINTER),
102  enumerate_modules(NULL_POINTER), get_module_name(NULL_POINTER),
103 //#ifdef _MSCVER
104 // tasker_16bit(NULL_POINTER),
105 //#endif
106  kernel32_dll(NULL_POINTER), create_snapshot(NULL_POINTER), first_process(NULL_POINTER),
107  next_process(NULL_POINTER) {}
108 
109  ~process_implementation_hider() {
110  if (psapi_dll) FreeLibrary(psapi_dll);
111  if (vdm_dll) FreeLibrary(vdm_dll);
112  if (kernel32_dll) FreeLibrary(kernel32_dll);
113  psapi_dll = NULL_POINTER;
114  vdm_dll = NULL_POINTER;
115  kernel32_dll = NULL_POINTER;
116  }
117 #endif
118 };
119 
121 
122 class process_info_clump
123 {
124 public:
125  basis::un_int _process_id;
126  process_entry_array &_to_fill; // where to add entries.
127 
128  process_info_clump(basis::un_int id, process_entry_array &to_fill)
129  : _process_id(id), _to_fill(to_fill) {}
130 };
131 
133 
134 // top-level functions...
135 
136 process_control::process_control()
137 : _ptrs(new process_implementation_hider),
138 #ifdef _MSC_VER
139  _use_psapi(true),
140 #else
141  _rando(new chaos),
142 #endif
143  _healthy(false)
144 {
145  // Check to see if were running under Windows95 or Windows NT.
146  version osver = application_configuration::get_OS_version();
147 
148 #ifdef _MSC_VER
149  if (osver.v_revision() == VER_PLATFORM_WIN32_WINDOWS) {
150  // we're on Windows 95, so use the toolhelp API for the processes.
151  _use_psapi = false;
152  } else if (osver.v_major() >= 5) {
153  // w2k and onward can do the toolhelp instead of psapi.
154  _use_psapi = false;
155  }
156  if (_use_psapi)
157  _healthy = initialize_psapi_support();
158  else
159  _healthy = initialize_toolhelp_support();
160 #else
161  _healthy = true;
162 #endif
163 }
164 
166 {
167  WHACK(_ptrs);
168 #ifndef _MSC_VER
169  WHACK(_rando);
170 #endif
171 }
172 
174 {
175  process_entry temp;
176  for (int gap = v.length() / 2; gap > 0; gap /= 2)
177  for (int i = gap; i < v.length(); i++)
178  for (int j = i - gap; j >= 0
179  && (filename(v[j].path()).basename().raw()
180  > filename(v[j + gap].path()).basename().raw());
181  j = j - gap)
182  { temp = v[j]; v[j] = v[j + gap]; v[j + gap] = temp; }
183 }
184 
186 {
187  process_entry temp;
188  for (int gap = v.length() / 2; gap > 0; gap /= 2)
189  for (int i = gap; i < v.length(); i++)
190  for (int j = i - gap; j >= 0 && (v[j]._process_id
191  > v[j + gap]._process_id); j = j - gap)
192  { temp = v[j]; v[j] = v[j + gap]; v[j + gap] = temp; }
193 }
194 
196 {
197  if (!_healthy) return false;
198 #ifdef _MSC_VER
199  if (!_use_psapi) {
200  // we're on Windows 95 or something, so use the toolhelp API for the
201  // processes.
202  return get_processes_with_toolhelp(to_fill);
203  } else {
204  // we're on Windows NT and so on; use the process API (PSAPI) to get info.
205  return get_processes_with_psapi(to_fill);
206  }
207 #else
208  return get_processes_with_ps(to_fill);
209 #endif
210 }
211 
212 #ifdef _MSC_VER
213 bool process_control::initialize_psapi_support()
214 {
215  // create an instance of the PSAPI dll for querying 32-bit processes and
216  // an instance of the VDM dll support just in case there are also some
217  // 16-bit processes.
218  _ptrs->psapi_dll = LoadLibraryA("psapi.dll");
219  if (!_ptrs->psapi_dll) return false;
220  _ptrs->vdm_dll = LoadLibraryA("vdmdbg.dll");
221  if (!_ptrs->vdm_dll) return false;
222 
223  // locate the functions that we want to call.
224  _ptrs->enumerate_processes = (BOOL(WINAPI *)(basis::un_int *,basis::un_int,basis::un_int*))
225  GetProcAddress(_ptrs->psapi_dll, "EnumProcesses");
226  _ptrs->enumerate_modules
227  = (BOOL(WINAPI *)(HANDLE, HMODULE *, basis::un_int, basis::un_int *))
228  GetProcAddress(_ptrs->psapi_dll, "EnumProcessModules");
229  _ptrs->get_module_name
230  = (basis::un_int (WINAPI *)(HANDLE, HMODULE, LPTSTR, basis::un_int))
231  GetProcAddress(_ptrs->psapi_dll, "GetModuleFileNameExA");
232 //#ifdef _MSCVER
233 // _ptrs->tasker_16bit = (INT(WINAPI *)(basis::un_int, TASKENUMPROCEX, LPARAM))
234 // GetProcAddress(_ptrs->vdm_dll, "VDMEnumTaskWOWEx");
235 //#endif
236  if (!_ptrs->enumerate_processes || !_ptrs->enumerate_modules
237  || !_ptrs->get_module_name
238 //#ifdef _MSCVER
239 // || !_ptrs->tasker_16bit
240 //#endif
241  ) return false;
242 
243  return true;
244 }
245 
246 bool process_control::initialize_toolhelp_support()
247 {
248  // get hooked up with the kernel dll so we can use toolhelp functions.
249  _ptrs->kernel32_dll = LoadLibraryA("Kernel32.DLL");
250  if (!_ptrs->kernel32_dll) return false;
251 
252  // create pointers to the functions we want to invoke.
253  _ptrs->create_snapshot = (HANDLE(WINAPI *)(basis::un_int,basis::un_int))
254  GetProcAddress(_ptrs->kernel32_dll, "CreateToolhelp32Snapshot");
255  _ptrs->first_process = (BOOL(WINAPI *)(HANDLE,LPPROCESSENTRY32))
256  GetProcAddress(_ptrs->kernel32_dll, "Process32First");
257  _ptrs->next_process = (BOOL(WINAPI *)(HANDLE,LPPROCESSENTRY32))
258  GetProcAddress(_ptrs->kernel32_dll, "Process32Next");
259  if (!_ptrs->next_process || !_ptrs->first_process
260  || !_ptrs->create_snapshot) return false;
261  return true;
262 }
263 
264 #endif
265 
267 {
268 #ifdef DEBUG_PROCESS_CONTROL
269  FUNCDEF("zap_process");
270 #endif
271  if (!_healthy) return false;
272 #ifndef _MSC_VER
273  int ret = kill(to_zap, 9);
274  // send the serious take-down signal to the process.
275  return !ret;
276 #else
277  HANDLE h = OpenProcess(PROCESS_TERMINATE, false, to_zap);
278  if (!h) {
279 #ifdef DEBUG_PROCESS_CONTROL
280  int err = critical_events::system_error();
281  LOG(a_sprintf("error zapping process %d=", to_zap)
282  + critical_events::system_error_text(err));
283 #endif
284  return false;
285  }
286  int exit_code = 0;
287  BOOL ret = TerminateProcess(h, exit_code);
288  CloseHandle(h);
289  return !!ret;
290 #endif
291 }
292 
294 {
295  FUNCDEF("query_process");
296  process_entry to_return;
297 
298  process_entry_array to_fill;
299  bool got_em = query_processes(to_fill);
300  if (!got_em) return to_return;
301 
302  for (int i = 0; i < to_fill.length(); i++) {
303  if (to_fill[i]._process_id == to_query)
304  return to_fill[i];
305  }
306 
307 //hmmm: implement more specifically.
308 #ifndef _MSC_VER
309 //put in the single process grabber deal.
310 #else
311 //grab the entry from the list.
312 #endif
313 
314  return to_return;
315 }
316 
318  const astring &app_name_in, int_set &pids)
319 {
320 #ifdef DEBUG_PROCESS_CONTROL
321  FUNCDEF("find_process_in_list");
322 #endif
323  pids.clear();
324  astring app_name = app_name_in.lower();
325 
326  version os_ver = application_configuration::get_OS_version();
327 
328  bool compare_prefix = (os_ver.v_major() == 5) && (os_ver.v_minor() == 0);
329  // we only compare the first 15 letters due to a recently noticed bizarre
330  // bug where w2k only shows (and reports) the first 15 letters of file
331  // names through toolhelp.
332 
333  bool found = false; // was it seen in process list?
334  for (int i = 0; i < processes.length(); i++) {
335  filename path = processes[i].path();
336  astring base = path.basename().raw().lower();
337  // a kludge for w2k is needed--otherwise we will miss seeing names that
338  // really are running due to the toolhelp api being busted and only
339  // reporting the first 15 characters of the name.
340  if ( (compare_prefix && (base.compare(app_name, 0, 0, 15, false)))
341  || (base == app_name) ) {
342  found = true;
343  pids.add(processes[i]._process_id);
344  }
345  }
346 #ifdef DEBUG_PROCESS_CONTROL
347  if (!found)
348  LOG(astring("failed to find the program called ") + app_name);
349 #endif
350  return found;
351 }
352 
354 
355 #ifdef _MSC_VER
356 // this section is the PSAPI version of the query.
357 
358 // called back on each 16 bit task.
359 BOOL WINAPI process_16bit(basis::un_int dwThreadId, WORD module_handle16, WORD hTask16,
360  PSZ pszModName, PSZ pszFileName, LPARAM lpUserDefined)
361 {
362  process_info_clump *to_stuff = (process_info_clump *)lpUserDefined;
363  process_entry to_add;
364  to_add._process_id = to_stuff->_process_id;
365  to_add._module16 = hTask16;
366  to_add.path(pszFileName);
367 //threads, etc?
368  to_stuff->_to_fill += to_add;
369  return true;
370 }
371 
372 bool process_control::get_processes_with_psapi(process_entry_array &to_fill)
373 {
374  // prepare the result object.
375  to_fill.reset();
376 
377  // loop over the process enumeration function until we are sure that we
378  // have allocated a large enough space for all existing processes.
379  bool got_all = false;
380  basis::un_int *pid_list = NULL_POINTER;
381  basis::un_int max_size = 428 * sizeof(basis::un_int);
382  basis::un_int actual_size = 0;
383  while (!got_all) {
384  pid_list = (basis::un_int *)HeapAlloc(GetProcessHeap(), 0, max_size);
385  if (!pid_list) return false;
386  if (!_ptrs->enumerate_processes(pid_list, max_size, &actual_size)) {
387  HeapFree(GetProcessHeap(), 0, pid_list);
388  return false;
389  }
390  if (actual_size == max_size) {
391  // there were too many to store, so whack the partial list.
392  HeapFree(GetProcessHeap(), 0, pid_list);
393  max_size *= 2; // try with twice as much space.
394  } else got_all = true;
395  }
396 
397  // calculate the number of process ids that got stored.
398  basis::un_int ids = actual_size / sizeof(basis::un_int);
399 
400  // examine each process id that we found.
401  for (basis::un_int i = 0; i < ids; i++) {
402  // get process information if security permits.
403 //turn chunk below into "scan process" or something.
404  HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
405  false, pid_list[i]);
406  flexichar process_name[MAX_ABS_PATH + 1] = { '\0' };
407  if (hProcess) {
408  // go over the modules for the process. the first will be the main
409  // application module and the others will be threads. ???
410  basis::un_int max_size = 1 * sizeof(HMODULE);
411 //hmmm: could do a rescan loop here if too many.
412  basis::un_int actual_size = 0;
413  HMODULE *module_handles = new HMODULE[max_size + 1];
414  if (!module_handles) {
415  CloseHandle(hProcess);
416  HeapFree(GetProcessHeap(), 0, pid_list);
417  return false;
418  }
419  if (_ptrs->enumerate_modules(hProcess, module_handles, max_size,
420  &actual_size)) {
421  // we want the name of the first module.
422  if (!_ptrs->get_module_name(hProcess, *module_handles, process_name,
423  sizeof(process_name)))
424  process_name[0] = 0;
425  }
426  WHACK(module_handles);
427  CloseHandle(hProcess);
428  }
429 
430  // we add whatever information we were able to find about this process.
431  process_entry new_entry;
432  new_entry._process_id = pid_list[i];
433  astring converted_name = from_unicode_temp(process_name);
434  new_entry.path(converted_name);
435 
436 //how to get? performance data helper?
438  to_fill += new_entry;
439 
440  // if we're looking at ntvdm, then there might be 16 bit processes
441  // attached to it.
442  if (new_entry.path().length() >= NTVDM_NAME.length()) {
443  astring temp = new_entry.path().substring
444  (new_entry.path().end() - NTVDM_NAME.length() + 1,
445  new_entry.path().end());
446  temp.to_lower();
447 //#ifdef _MSCVER
449 // if (temp == NTVDM_NAME) {
450 // // set up a callback stampede on the 16 bit processes.
451 // process_info_clump info(pid_list[i], to_fill);
452 // _ptrs->tasker_16bit(pid_list[i], (TASKENUMPROCEX)process_16bit,
453 // (LPARAM)&info);
454 // }
455 //#endif
456  }
457  }
458 
459  if (pid_list) HeapFree(GetProcessHeap(), 0, pid_list);
460  return true;
461 }
462 
464 
465 // this is the toolhelp version of the query.
466 
467 bool process_control::get_processes_with_toolhelp(process_entry_array &to_fill)
468 {
469  // prepare the result object.
470  to_fill.reset();
471 
472  // get an atomic view of the process list, which rapidly becomes out of date.
473  HANDLE hSnapShot;
474  hSnapShot = _ptrs->create_snapshot(TH32CS_SNAPPROCESS, 0);
475  if (hSnapShot == INVALID_HANDLE_VALUE) return false;
476 
477  // start iterating through the snapshot by getting the first process.
478  PROCESSENTRY32 entry;
479  entry.dwSize = sizeof(PROCESSENTRY32);
480  BOOL keep_going = _ptrs->first_process(hSnapShot, &entry);
481 
482  // while we see valid processes, iterate through them.
483  while (keep_going) {
484  // add an entry for the current process.
485  process_entry new_entry;
486  new_entry._process_id = entry.th32ProcessID;
487  new_entry._references = entry.cntUsage;
488  new_entry._threads = entry.cntThreads;
489  new_entry._parent_process_id = entry.th32ParentProcessID;
490  astring exe_file = from_unicode_temp(entry.szExeFile);
491  new_entry.path(exe_file);
492  to_fill += new_entry;
493  entry.dwSize = sizeof(PROCESSENTRY32); // reset struct size.
494  keep_going = _ptrs->next_process(hSnapShot, &entry);
495  }
496 
497  CloseHandle(hSnapShot);
498  return true;
499 }
500 #endif // __WIN32__
501 
502 //#ifndef _MSC_VER
503 
504 #define CLOSE_TEMPORARY_FILE { \
505 /* continuable_error("process_control", "get_processes_with_ps", error); */ \
506  if (output) { \
507  fclose(output); \
508  unlink(tmpfile.s()); \
509  } \
510 }
511 
512 bool process_control::get_processes_with_ps(process_entry_array &to_fill)
513 {
514  FUNCDEF("get_processes_with_ps");
515  to_fill.reset();
516  // we ask the operating system to give us a list of processes.
517  a_sprintf tmpfile("/tmp/proc_list_%d_%d.txt", application_configuration::process_id(),
518  _rando->inclusive(1, 400000));
519  a_sprintf cmd("ps wax --format \"%%p %%a\" >%s", tmpfile.s());
520 //hmmm: add more info as we expand the process entry.
521  FILE *output = NULL_POINTER; // initialize now to establish variable for our macro.
522  int sysret = system(cmd.s());
523  if (negative(sysret)) {
524 LOG("got negative return from system()!");
526  return false;
527  }
528  output = fopen(tmpfile.s(), "r");
529  if (!output) {
530 LOG("failed to open process list file!");
532  return false;
533  }
534  const int max_buff = 10000;
535  char buff[max_buff];
536  size_t size_read = 1;
537  astring accumulator;
538  while (size_read > 0) {
539  // read bytes from the file.
540  size_read = fread(buff, 1, max_buff, output);
541  // if there was anything, store it in the string.
542  if (size_read > 0)
543  accumulator += astring(astring::UNTERMINATED, buff, size_read);
544  }
546  // parse the string up now.
547  bool first_line = true;
548  while (accumulator.length()) {
549  // eat any spaces in front.
550  if (first_line) {
551  // we toss the first line since it's a header with columns.
552  int cr_indy = accumulator.find('\n');
553  accumulator.zap(0, cr_indy);
554  if (accumulator[accumulator.end()] == '\r')
555  accumulator.zap(accumulator.end(), accumulator.end());
556  first_line = false;
557  continue;
558  }
559  while (accumulator.length() && (accumulator[0] == ' '))
560  accumulator.zap(0, 0);
561  // look for the first part of the line; the process id.
562  int num_indy = accumulator.find(' ');
563  if (negative(num_indy)) break;
564  basis::un_int p_id = accumulator.substring(0, num_indy).convert(0);
565  accumulator.zap(0, num_indy);
566  int cr_indy = accumulator.find('\n');
567  if (negative(cr_indy))
568  cr_indy = accumulator.end() + 1;
569  astring proc_name = accumulator.substring(0, cr_indy - 1);
570  if (proc_name[proc_name.end()] == '\r')
571  proc_name.zap(proc_name.end(), proc_name.end());
572  accumulator.zap(0, cr_indy);
573  int space_indy = proc_name.find(' ');
574 //hmmm: this is incorrect regarding names that do have spaces in them.
575  if (negative(space_indy))
576  space_indy = proc_name.end() + 1;
577  process_entry to_add;
578  to_add._process_id = p_id;
579  astring path = proc_name.substring(0, space_indy - 1);
580  // patch the pathname if we see any bracketed items.
581  int brackets_in = 0;
582  for (int i = 0; i < path.length(); i++) {
583  if (path[i] == '[') brackets_in++;
584  else if (path[i] == ']') brackets_in--;
585  if (brackets_in) {
586  // if we see a slash inside brackets, then we patch it so it doesn't
587  // confuse the filename object's directory handling.
588  if ( (path[i] == '/') || (path[i] == '\\') )
589  path[i] = '#';
590  }
591  }
592  to_add.path(path);
593  to_fill += to_add;
594  }
595  return true;
596 }
597 //#endif // __UNIX__
598 
599 } //namespace.
600 
a_sprintf is a specialization of astring that provides printf style support.
Definition: astring.h:440
int length() const
Returns the current reported length of the allocated C array.
Definition: array.h:115
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
astring lower() const
like to_lower(), but returns a new string rather than modifying this.
Definition: astring.cpp:542
virtual void zap(int start, int end)
Deletes the characters between "start" and "end" inclusively.
Definition: astring.cpp:521
bool substring(astring &target, int start, int end) const
a version that stores the substring in an existing "target" string.
Definition: astring.cpp:865
int end() const
returns the index of the last (non-null) character in the string.
Definition: astring.h:86
int length() const
Returns the current length of the string.
Definition: astring.cpp:132
bool compare(const astring &to_compare, int start_first, int start_second, int count, bool case_sensitive) const
Compares "this" string with "to_compare".
Definition: astring.cpp:810
int find(char to_find, int position=0, bool reverse=false) const
Locates "to_find" in "this".
Definition: astring.cpp:574
void to_lower()
to_lower modifies "this" by replacing capitals with lower-case.
Definition: astring.cpp:528
Provides operations commonly needed on file names.
Definition: filename.h:64
const basis::astring & raw() const
returns the astring that we're holding onto for the path.
Definition: filename.cpp:97
filename basename() const
returns the base of the filename; no directory.
Definition: filename.cpp:385
a platform-independent way to acquire random numbers in a specific range.
Definition: chaos.h:51
int inclusive(int low, int high) const
< Returns a pseudo-random number r, such that "low" <= r <= "high".
Definition: chaos.h:88
static bool find_process_in_list(const process_entry_array &processes, const basis::astring &app_name, structures::int_set &pids)
uses a pre-existing list of "processes" to search for the "app_name".
void sort_by_pid(process_entry_array &to_sort)
process_entry query_process(basis::un_int to_query)
returns the information for just one process.
bool zap_process(basis::un_int to_zap)
preemptively zaps the process "to_zap".
void sort_by_name(process_entry_array &to_sort)
bool query_processes(process_entry_array &to_fill)
finds the processes that are running and drops them into "to_fill".
a handy class that implements an array of process entries.
Definition: process_entry.h:60
Encapsulates information about OS processes.
Definition: process_entry.h:28
basis::un_int _process_id
the OS identifier of this process.
Definition: process_entry.h:30
basis::un_short _module16
non-zero if this process is a 16-bit application.
Definition: process_entry.h:34
const basis::astring & path() const
A simple object that wraps a templated set of ints.
Definition: set.h:156
bool add(const contents &to_add)
Adds a new element "to_add" to the set.
Definition: set.h:232
void clear()
Empties out this set.
Definition: set.h:55
Holds a file's version identifier.
int v_minor() const
minor version number.
int v_major() const
major version number.
int v_revision() const
revision level.
#define NULL_POINTER
The value representing a pointer to nothing.
Definition: definitions.h:32
#define FUNCDEF(func_in)
FUNCDEF sets the name of a function (and plugs it into the callstack).
Definition: enhance_cpp.h:57
#define MAX_ABS_PATH
Definition: filename.h:37
The guards collection helps in testing preconditions and reporting errors.
Definition: array.h:30
void WHACK(contents *&ptr)
deletion with clearing of the pointer.
Definition: functions.h:121
unsigned int un_int
Abbreviated name for unsigned integers.
Definition: definitions.h:62
bool negative(const type &a)
negative returns true if "a" is less than zero.
Definition: functions.h:43
char flexichar
Definition: definitions.h:58
string path
Definition: eml_to_txt.py:139
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.
An extension to floating point primitives providing approximate equality.
Definition: averager.h:21
A dynamic container class that holds any kind of object via pointers.
Definition: amorph.h:55
#define CLOSE_TEMPORARY_FILE
#define LOG(s)
chaos _rando
Support for unicode builds.
Aids in achievement of platform independence.
void * application_instance
void * HANDLE