#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdarg.h>
#include <time.h>
#include <string.h>
#include <sys/timeb.h>
#include <ctype.h>
/* #include <sys/time.h> */
/* #include <sys/timeb.h> */
#include "include/bug.h"
#include "include/structs.h"
#include "include/main.h"
#define _UTILS_C
#include "include/utils.h"
void spin(FILE * fp)
{
static char spinner[4] = { '|', '/', '-', '\\' };
static int spin_state = 0;
fprintf(fp, "%c\b", spinner[(++spin_state > 3) ? (spin_state = 0) : spin_state]);
fflush(stderr);
}
/*
* All this does is call strdup(), but it does error checking internally,
* aborting the program if you have no memory... This makes the code more
* readable and how often do you really recover from running out of RAM?
*/
char *my_strdup(char *Str)
{
char *tmp;
if (!Str)
return ""; /* Note: This works AROUND the bug! */
if (!(tmp = (char *)strdup(Str)))
log_fatal("Cannot get memory to strdup(\"%s\")!", Str);
return tmp;
}
/*
* This just opens a file. It makes the code more readable without
* sacrificing the debugging information.
*/
FILE *open_file(char *Filename, char *Mode)
{
FILE *fp;
if (!(fp = fopen(Filename, Mode)))
log_fatal("Cannot open (%s) for (%s) access!", Filename, Mode);
return fp;
}
/*
* This just calls malloc/calloc with a debugging wrapper.
*/
char *get_mem(long Count, long Size)
{
char *Memory;
if (!(Memory = (char *)malloc(Count * Size)))
log_fatal("Cannot allocate %d bytes!", Count * Size);
return Memory;
}
/*
* Sames as get_mem(), but this is for realloc().
*/
char *get_more_mem(char *Memory, long Count, long Size)
{
char *NewMemory;
/* REMEMBER to zero out your variables on YOUR END! Malloc is evil, and
* realloc shares this trait. I normally use calloc, but since I don't want
* to rewrite realloc myself (I *HOPE* the system version is coded in assy!),
* I figured why make the get_mem() function inconsistant?
*/
if (!(NewMemory = (char *)realloc(Memory, Count * Size)))
log_fatal("Cannot reallocate %d bytes!", Count * Size);
return NewMemory;
}
/*
* This routine will read a line from the given file pointer and return a
* string containing that line, with the end return stripped. If the next
* line was blank, or a comment (denoted by a leading '*', it will be skipped.
* the line-number and byte-position counters passed in will be updated.
* Any failure to get a valid line will result in a NULL return value.
* In this case, line and pos will remain at their last valid positions.
*/
char *get_line(FILE * fp, long *Line, long *Pos, int Skip)
{
static char tmp[MAX_LINE_LEN];
if (!fp)
return NULL;
do {
bzero(tmp, MAX_LINE_LEN);
*Pos = ftell(fp);
if (!fgets(tmp, MAX_LINE_LEN - 1, fp))
return NULL;
(*Line)++;
if (*tmp)
if (tmp[strlen(tmp) - 1] == '\n')
tmp[strlen(tmp) - 1] = '\0';
if (Skip) {
if (!*tmp)
continue; /* ignore blank lines */
if (*tmp == '*')
continue; /* lines starting with '*' are comments */
}
break;
} while (!feof(fp));
return tmp;
}
/*
* This routine verifies a chance of sanity in file pointer position.
* It just saves the current pointer, moves where you tell it, and compares
* what is there with the character you told it to expect. It then
* repositions the file pointer and returns the result of the comparison.
*/
int verify_pos(FILE * fp, long Pos, int Check)
{
long OldPos;
char c;
if (!fp)
return 0;
if ((OldPos = ftell(fp)) < 0)
return 0;
if (fseek(fp, Pos, 0) < 0)
return 0;
if ((c = fgetc(fp)) < 0)
return 0;
if (fseek(fp, OldPos, 0) < 0)
return 0;
return (c == Check);
}
/*
* This routine will repeatedly call get_line() on a given file pointer until
* it finds a line which ends in a tilde '~'. It will construct a string which
* contains all the text from the file pointer's original position up to, but
* not including, the tilde. We may need to suppress the normal black-line
* and comment-line skipping rules.
* ARGH! The wiley people are sloppy! We now have to extend the routine to
* allow for whitespace after the tilde... or manually check the rooms...
* adding routine!
* UGH! We also must strip trailing \n's for cases of ~ strings on their own.
*/
char *get_tilde_string(FILE * fp, long *Line, long *Pos)
{
static char block[MAX_PAGE_LEN];
static char tmp[MAX_LINE_LEN];
if (!fp)
return NULL;
bzero(block, MAX_PAGE_LEN);
while (1) {
int i = 0;
bzero(tmp, MAX_LINE_LEN);
do {
bzero(tmp, MAX_LINE_LEN);
*Pos = ftell(fp);
if (!fgets(tmp, MAX_LINE_LEN - 1, fp))
return NULL;
(*Line)++;
if (*tmp)
if (tmp[strlen(tmp) - 1] == '\n')
tmp[strlen(tmp) - 1] = '\0';
break;
} while (!feof(fp));
if (*tmp && (tmp[i = strlen(tmp) - 1] == '~')) {
tmp[strlen(tmp) - 1] = '\0';
strcat(block, tmp);
break;
} else if ((tmp[i] == ' ') || (tmp[i] == '\t')) {
int done = 0;
for (; i >= 0; i--) {
if (tmp[i] == '~') {
tmp[i] = '\0';
strcat(block, tmp);
done = 1;
break;
} else {
if ((tmp[i] == ' ') || (tmp[i] == '\t'))
continue;
else
break;
}
}
if (done)
break;
}
if (*tmp)
strcat(block, tmp);
if (HardReturns)
strcat(block, "\n");
else
strcat(block, " ");
}
if (!HardReturns)
if (block[strlen(block) - 1] == ' ')
block[strlen(block) - 1] = '\0';
return block;
}
/*
* This function collapses arbitrary strings into alphabetic sets.
* Furthermore, it collapses spaces upto the first non-space/non-alphabetic
* character, and then throws the entire remainder away.
* It then does a case conversion to make the first char capital.
* It also accepts a single-tick (') as a synonym for space.
* I decided to allow '_' to pass unconverted...
* It also now accepts '/' and '-' as synonyms for '_'.
* decided that mapping spaces into '_' makes it more readable.
* I also decided to strip trailing spaces from the string...
* Filtered out \'s now as well.
* Decided that collapsing all of these is nicer for LOoooooooNG names!
* replace the first continue with "Block[j++]= '_'" for the old behaviour.
*/
char *remap_name(char *Old)
{
static char Block[MAX_PAGE_LEN];
int i,
j;
if (!Old)
return NULL;
bzero(Block, MAX_PAGE_LEN);
for (j = i = 0; i < strlen(Old); i++) {
if (isalpha(Old[i]))
Block[j++] = Old[i];
else if ((Old[i] == '_') || (Old[i] == ' ') || (Old[i] == '/')
|| (Old[i] == '\\') || (Old[i] == '-'))
continue;
else if ((Old[i] == '\'') || (Old[i] == '"'))
continue;
else
break;
}
if (*Block) {
for (i = strlen(Block) - 1; i >= 0; i--)
if (Block[i] != '_')
break;
else
Block[i] = '\0';
}
if (*Block)
*Block = toupper(*Block);
return Block;
}
/* This routine accepts a string and breaks it apart into words.
* It then returns a structure containing the number of words and an
* array of those words.
*/
keyword *make_keyword_list(char *String)
{
keyword *Keyword;
char *tmp,
*tmp2;
Keyword = (keyword *)get_mem(1, sizeof(keyword));
Keyword->Count = 0;
Keyword->Word = (char **)get_mem(1, sizeof(char *));
if (!String || !*String)
return Keyword;
tmp = my_strdup(String);
if ((Keyword->Word[Keyword->Count] = (char *)strtok(tmp, " "))) {
Keyword->Count++;
Keyword->Word = (char **)
get_more_mem((char *)Keyword->Word, Keyword->Count + 1, sizeof(char *));
Keyword->Word[Keyword->Count] = NULL;
}
while ((tmp2 = (char *)strtok(NULL, " "))) {
Keyword->Word[Keyword->Count] = my_strdup(tmp2);
Keyword->Count++;
Keyword->Word = (char **)
get_more_mem((char *)Keyword->Word, Keyword->Count + 1, sizeof(char *));
Keyword->Word[Keyword->Count] = NULL;
}
return Keyword;
}
char *timestamp(void)
{
static char Result[256];
struct timeb right_now;
struct tm *now_part;
ftime(&right_now);
now_part = localtime((const time_t *)&right_now);
sprintf(Result, "%02d%02d%02d.%02d%02d%02d.%03d",
now_part->tm_year, now_part->tm_mon + 1, now_part->tm_mday,
now_part->tm_hour, now_part->tm_min, now_part->tm_sec, right_now.millitm);
return Result;
}
void sscanf_dice(char *str, int *x, int *y, int *z)
{
char sign[2];
if (!str || !*str || !x || !y || !z)
return;
*x = *y = *z = sign[1] = 0;
*sign = '+';
sscanf(str, " %dd%d%[+-]%d", x, y, sign, z);
if (!*y)
*y = 1;
if (*sign == '-')
*z = -*z;
}
char *ordinal(int x)
{
if (x < 14 && x > 10)
x = 4;
else
x %= 10;
switch (x) {
case 1:
return "st";
case 2:
return "nd";
case 3:
return "rd";
default:
return "th";
}
}