/* free.c -- Copyright 1993, 1994, 1996 Liam R. E. Quin. * All Rights Reserved. * This code is NOT in the public domain. * See the file COPYRIGHT for full details. * * $Id: free.c,v 1.3 1996/05/23 00:42:55 lee Exp $ * * test the free-block management routines * */ #include "globals.h" /* defines and declarations for database filenames */ #include "error.h" #include /* stderr, also for fileinfo.h */ #include /* stderr, also for fileinfo.h */ #include #ifdef HAVE_STRING_H # include #else # include #endif #include #ifdef HAVE_FCNTL_H # ifdef HAVE_SYSV_FCNTL_H # include # endif # include #endif #include "fileinfo.h" /* for wordinfo.h */ #include "wordinfo.h" #include "wordrules.h" #include "pblock.h" #include "emalloc.h" #include "range.h" #include "lqutil.h" #include "liblqtext.h" #include "lqtrace.h" static int DoCommands(); /** **/ static char *Revision = "@(#) $Id: free.c,v 1.3 1996/05/23 00:42:55 lee Exp $"; char *progname = "lq-text free block storage test"; #define MAX_EXIT_STATUS 127 static t_LQTEXT_Database *db = 0; int main(argc, argv) int argc; char *argv[]; { int Status; t_lqdbOptions *Options; Options = LQT_InitFromArgv(argc, argv); db = LQT_OpenDatabase(Options, O_RDWR, 0); if (!db || LQT_ObtainReadOnlyAccess(db) < 0) { Error(E_FATAL, "couldn't open lq-text database for writing"); } fprintf(stderr, "**** %s\n**** %s\n", progname, Revision ); progname = argv[0]; Status = DoCommands(); if (Status) { printf("**** %s TEST FAILED, status %d\n", progname, Status); } else { printf("**** %s test passed\n", progname); } exit(Status); return -1; /* exit failed! */ } /* status flags for commands to return: */ #define C_OK 000 #define C_FAIL 001 #define C_EXIT_OK 002 #define C_EXIT_FAIL (C_EXIT_OK|C_FAIL) #define C_EXIT 004 typedef struct { int Count; char **Strings; int Flags; char *TheOriginalString; } t_ArgumentList; #define ARGLIST_NORMAL 00 #define ARGLIST_PLEA_FOR_HELP 01 #define ARGLIST_EMPTY 02 #define IsPleaForHelp(ArgList) \ ((ArgList) && ((ArgList)->Flags & ARGLIST_PLEA_FOR_HELP)) static t_ArgumentList EmptyArgument = { 0, (char **) 0, ARGLIST_EMPTY, "please report a bug if you see this message." }; #define NO_ARGUMENT (&EmptyArgument) typedef struct { char *Name; char *ShortestForm; char *Description; int (*Handler)(/* char *theLine */); } t_Command; static int c_ListCommands(); static int c_Echo(); static int c_Quit(); static int c_Trace(); static int c_SetFreeBlock(); static int c_SetFreeByte(); static int c_SetUsedBlock(); static int c_SetUsedByte(); static int c_ShowBlock(); static int c_ShowBytes(); static int c_ShowHelp(); static t_ArgumentList *StringToArgumentList(); static t_Command *StringToCommand(); static t_Command CommandTable[] = { { "help", "?", "provides help about commands", c_ShowHelp }, { "Free", "F", "set BLOCK status to Free", c_SetFreeBlock }, { "Show", "S", "show status of BLOCKS", c_ShowBlock }, { "Use", "U", "set BLOCK to Used", c_SetUsedBlock }, { "echo", "ec", "print arguments without taking further action", c_Echo }, { "freebyte", "f", "set block at given byte offset to Free", c_SetFreeByte }, { "showbyte", "s", "show status of blocks at given byte offsets", c_ShowBytes }, { "usebyte", "u", "set block at given byte offset to Used", c_SetUsedByte }, { "trace", "t", "set the trace level", c_Trace }, { "quit", "q", "quit the test program", c_Quit }, { 0, } }; #define Prefix(pref,str) ((*(pref)==(*str))&&!strncmp(pref,str,strlen(pref))) int IsValidBlockSize(ByteCount, Units) unsigned long ByteCount; int Units; { if (!Units) { Error(E_BUG, "%s: %d: IsValidBlockSize(%lu, >>>0<<<)", __FILE__, __LINE__, ByteCount ); } return (ByteCount % Units) ? 0 : 1; } static int c_Echo(Command, Arguments) t_Command *Command; t_ArgumentList *Arguments; { int i; if (IsPleaForHelp(Arguments)) { printf("%s simply prints out its arguments.\n", Command->Name, progname ); return C_OK; } for (i = 0; i < Arguments->Count; i++) { printf("%s", Arguments->Strings[i]); if (i + 1 < Arguments->Count) { putchar(' '); } } putchar('\n'); return C_OK; } static int c_Trace(Command, Arguments) t_Command *Command; t_ArgumentList *Arguments; { if (IsPleaForHelp(Arguments)) { printf("Use %s on a line by itself to toggle tracing, or\n", Command->Name ); printf("\tgive %s a number as an argument to set the trace level.\n", Command->Name ); return C_OK; } if (Arguments->Count) { if (LQU_cknatstr(Arguments->Strings[0])) { LQT_SetTraceFlag((t_TraceFlag) atol(Arguments->Strings[0])); } else { (void) LQT_SetTraceFlagsFromString(Arguments->Strings[0]); return C_OK; /* don't fail the tests for this, though */ } } else { t_TraceFlag Now = LQT_GetTraceFlags(); if (Now) { (void) LQT_UnSetTraceFlag(Now); } else { Now = ~0L; LQT_SetTraceFlag(Now); } } printf("%s: Trace set to %s\n", progname, LQT_GetTraceFlagsAsString()); return C_OK; } static int c_Quit(Command, Arguments) t_Command *Command; t_ArgumentList *Arguments; { if (IsPleaForHelp(Arguments)) { printf("Use %s on a line by itself to quit the %s program.\n", Command->Name, progname ); printf("\tThe exit status will be the number of tests failed, or\n"); printf("\tgive %s a number as an argument to exit with that status.\n", Command->Name ); return C_OK; } if (Arguments->Count) { if (LQU_cknatstr(Arguments->Strings[0])) { int i = atoi(Arguments->Strings[0]); if (i != 0) { printf("%s: **** Fail --> %s with status %d\n", progname, Command->Name, i ); } exit(i); } else { printf("The argument to %s must be a whole number, not \"%s\"\n", Command->Name, Arguments->Strings[0] ); return C_OK; /* don't fail the tests for this, though */ } } else { return C_EXIT; } return C_EXIT_FAIL; } static void PrintXXXXXRangeHelp(What) char *What; { printf("\ta <%srange> is a number or a comma-separated list of numbers.\n", What ); printf("\tBLOCK numbers are integers;\n"); printf("\tbyte offets must be multiples of %d.\n", BLOCKSIZE); } static int c_SetFreeBlock(Command, Arguments) t_Command *Command; t_ArgumentList *Arguments; { t_Range *Range; int i; if (IsPleaForHelp(Arguments)) { printf("%s sets the given block numbers to FREE.\n", Command->Name ); PrintXXXXXRangeHelp("block"); return C_OK; } if (Arguments->Count == 0) { Error(E_WARN, "Command %s must be followed by ranges of Block Numbers.", Command->Name ); return C_OK; } for (i = 0; i < Arguments->Count; i++) { t_Range *RP; Range = LQU_StringToRange(Arguments->Strings[i]); for (RP = Range; RP; RP = RP->Next) { unsigned long Block; if (RP == Range && RP->AndAllBefore) { printf("\tfree all up to %ld\n", RP->Last); } else if (!RP->Next && RP->AndAllAfter) { Error(E_WARN, "trailing \"-\" on a range not understood here."); return C_OK; } else { printf("\tfree from %ld to %ld\n", RP->First, RP->Last); } for ( Block = (RP == Range && RP->AndAllBefore) ? 0 : RP->First; Block <= RP->Last; Block++ ) { LQT_SetBlockStatus(db, Block * BLOCKSIZE, SET_BLOCK_AS_FREE); } } for (RP = Range; RP; RP = Range) { Range = RP->Next; efree((char *) RP); } } return C_OK; } static int c_SetFreeByte(Command, Arguments) t_Command *Command; t_ArgumentList *Arguments; { if (IsPleaForHelp(Arguments)) { printf("%s sets the given byte offsets to FREE.\n", Command->Name ); PrintXXXXXRangeHelp("byte"); return C_OK; } return C_OK; } static int c_SetUsedBlock(Command, Arguments) t_Command *Command; t_ArgumentList *Arguments; { t_Range *Range; int i; if (IsPleaForHelp(Arguments)) { printf("%s sets the given BLOCK numbers to USED.\n", Command->Name ); PrintXXXXXRangeHelp("block"); return C_OK; } if (Arguments->Count == 0) { Error(E_WARN, "Command %s must be followed by ranges of Block Numbers.", Command->Name ); return C_OK; } for (i = 0; i < Arguments->Count; i++) { t_Range *RP; Range = LQU_StringToRange(Arguments->Strings[i]); for (RP = Range; RP; RP = RP->Next) { unsigned long Block; if (RP == Range && RP->AndAllBefore) { printf("\tuse all up to %ld\n", RP->Last); for (Block = 0; Block <= RP->Last; Block++) { LQT_SetBlockStatus( db, Block * BLOCKSIZE, SET_BLOCK_AS_USED ); } } else if (!RP->Next && RP->AndAllAfter) { Error(E_WARN, "trailing \"-\" on a range not understood here."); return C_OK; } else { printf("\tuse from %ld to %ld\n", RP->First, RP->Last); for (Block = RP->First; Block <= RP->Last; Block++) { LQT_SetBlockStatus( db, Block * BLOCKSIZE, SET_BLOCK_AS_USED ); } } } for (RP = Range; RP; RP = Range) { Range = RP->Next; efree((char *) RP); } } return C_OK; } static int c_SetUsedByte(Command, Arguments) t_Command *Command; t_ArgumentList *Arguments; { if (IsPleaForHelp(Arguments)) { printf("%s sets the given byte offsets to USED.\n", Command->Name ); PrintXXXXXRangeHelp("block"); return C_OK; } return C_OK; } static int c_ShowBlock(Command, Arguments) t_Command *Command; t_ArgumentList *Arguments; { t_Range *Range; int i; int ScreenColumn = 0; #define LEFTMARGIN 12 if (IsPleaForHelp(Arguments)) { printf("%s shows the status of the given BLOCK numbers.\n", Command->Name ); PrintXXXXXRangeHelp("block"); return C_OK; } if (Arguments->Count == 0) { /* could get maximum block here? TODO */ Error(E_WARN, "Command %s must be followed by ranges of Block Numbers.", Command->Name ); return C_OK; } for (i = 0; i < Arguments->Count; i++) { t_Range *RP; Range = LQU_StringToRange(Arguments->Strings[i]); for (RP = Range; RP; RP = RP->Next) { unsigned long Block; unsigned long First; if (!RP->Next && RP->AndAllAfter) { Error(E_WARN, "trailing \"-\" on a range not understood here."); return C_OK; } if (RP == Range && RP->AndAllBefore) { First = 0; } else { First = RP->First; } /* Force minumum to be multiple of 32: */ for (Block = First & ~ (unsigned long) 31L; Block <= RP->Last; Block++) { /* a space every 16 units */ if (ScreenColumn > 0 && (Block & 15) == 0) { putchar(' '); ++ScreenColumn; if (ScreenColumn >= 80 - LEFTMARGIN - 16) { putchar('\n'); ScreenColumn = 0; } } if (ScreenColumn == 0) { char Buffer[80]; (void) sprintf(Buffer, "%5d %d:", Block, Block * BLOCKSIZE ); ScreenColumn = strlen(Buffer); if (ScreenColumn < LEFTMARGIN) { printf("%*s", LEFTMARGIN, Buffer); ScreenColumn = 0; } else { printf("%s", Buffer); ScreenColumn -= LEFTMARGIN; } } if (Block < First) { putchar(' '); } else if (LQT_BlockIsFree(db, Block * BLOCKSIZE)) { putchar('f'); } else { putchar('u'); } if (++ScreenColumn >= 79 - LEFTMARGIN) { putchar('\n'); ScreenColumn = 0; } } printf("\n"); ScreenColumn = 0; } for (RP = Range; RP; RP = Range) { Range = RP->Next; efree((char *) RP); } } return C_OK; } static int c_ShowBytes(Command, Arguments) t_Command *Command; t_ArgumentList *Arguments; { if (IsPleaForHelp(Arguments)) { printf("%s shows the status of the given byte offsets.\n", Command->Name ); PrintXXXXXRangeHelp("block"); return C_OK; } return C_OK; } static int c_ShowHelp(Command, Arguments) t_Command *Command; t_ArgumentList *Arguments; { if (Arguments->Count < 1) { c_ListCommands(Command, NO_ARGUMENT); printf( "Use %s (or %s) as an argument for a command for more details.\n", Command->Name, Command->ShortestForm ); return C_OK; } if (IsPleaForHelp(Arguments)) { printf( "Use %s on a line by itself to get a list of commands.\n", Command->Name ); printf("\tYou can abbreviate commands, or use their short forms.\n"); printf("\tYou can also use %s or %s as an argument to a command\n", Command->Name, Command->ShortestForm ); printf("\tto get more details about that command.\n"); printf("\tArguments are separated by spaces.\n"); } else { int i; for (i = 0; i < Arguments->Count; i++) { t_Command *cp = StringToCommand(Arguments->Strings[i]); if (cp) { printf( "%10s (%s) -- %s\n", cp->Name, cp->ShortestForm, cp->Description ); } else { char *LineOne = "Command \"%s\" unrecognised;"; char *LineTwo = "\tuse %s on a line by itself for a list of commands.\n"; printf(LineOne, Arguments->Strings[i]); /* the -4 in the following is for %s twice: */ if (strlen(LineOne) + strlen(LineTwo) + strlen(Command->Name) + strlen(Arguments->Strings[i] - 4) > 79) { printf("\n\t"); } else { putchar(' '); } printf(LineTwo, Command->Name); } } } printf("Use %s (or %s) as an argument for a command for more details.\n", Command->Name, Command->ShortestForm ); /* asking for help will never make a test fail: */ return C_OK; } static int c_ListCommands(Command, Arguments) t_Command *Command; t_ArgumentList *Arguments; { t_Command *cp; if (IsPleaForHelp(Arguments)) { printf("Use %s to see a list of commands that \"%s\" recognises.\n", Command->Name, progname ); } else { for (cp = CommandTable; cp->Name; cp++) { printf( "%10s (%s) -- %s\n", cp->Name, cp->ShortestForm, cp->Description ); } } return C_OK; } static t_Command * StringToCommand(Command) char *Command; { t_Command *cp; /* first look for an exact match */ for (cp = CommandTable; cp->Name; cp++) { if (STREQ(cp->Name, Command)) { return cp; } } /* now look to see if we used an abbreviation: */ for (cp = CommandTable; cp->Name; cp++) { if (STREQ(cp->ShortestForm, Command)) { return cp; } } /* now look to se if we have used a prefix of a command: */ for (cp = CommandTable; cp->Name; cp++) { if (Prefix(Command, cp->Name)) { return cp; } } /* no */ return (t_Command *) 0; } static char * NextToken(Stringp) char **Stringp; { register char *Start; int Quote = 0; if (!Stringp || !*Stringp) { return (char *) 0; } /** Skip leading white-space **/ while (**Stringp && isspace(**Stringp)) { ++*Stringp; } /** check that there is something there after the space: **/ if (!**Stringp) { return (char *) 0; } /** check fr a quoted string **/ if (**Stringp == '"' || **Stringp == '\'') { Quote = **(Stringp); ++*Stringp; } /** remember the start of the string... **/ Start = *(Stringp); /** read the body of the string **/ while (**Stringp && ((Quote && **Stringp != Quote)||!isspace(**Stringp))) { if (**Stringp == '\\' && Quote != '\'') { ++*Stringp; if (!**Stringp) { printf("Unexpected end of line after a backslash (\\)!\n"); return (char *) 0; } } ++*Stringp; } /** check for unmatched quotes **/ if (Quote) { if (!*Stringp) { Error(E_WARN, "unmatched quote %c in input: %s", Quote, Start); return (char *) 0; } else { --*Stringp; /* don't include the close quote */ } } /** return the string, if not empty **/ if (*Stringp > Start) { char *Result = emalloc("NextToken", (*Stringp - Start) + 1); (void) strncpy(Result, Start, *Stringp - Start); Result[*Stringp - Start] = '\0'; return Result; } else { /* end of string */ return (char *) 0; } } static t_ArgumentList * StringToArgumentList(String) char *String; { register char *p; t_ArgumentList *Result; int AllocatedStrings = 0; if (!String) { return NO_ARGUMENT; } for (p = String; *p; p++) { if (!isspace(*p)) { break; } } if (!*p) { return NO_ARGUMENT; } Result = (t_ArgumentList *) emalloc( "t_ArgumentList", sizeof(t_ArgumentList) ); Result->Count = 0; Result->Strings = (char **) emalloc("Argument List", sizeof(char *) * 2); AllocatedStrings = 1; Result->Strings[0] = (char *) 0; Result->Strings[1] = (char *) 0; Result->Flags = 0; /* now step through the strings */ for (;;) { p = NextToken(&String); if (!p) { if (Result->Count == 0) { efree((char *) Result); return NO_ARGUMENT; } Result->Strings[Result->Count] = (char *) 0; return Result; } if (Result->Count + 1 >= AllocatedStrings) { AllocatedStrings += (String && *String) ? 4 : 1; Result->Strings = (char **) erealloc( (char *) Result->Strings, AllocatedStrings * sizeof(char *) ); } Result->Strings[Result->Count] = p; Result->Count++; } /*NOTREACHED*/ Error(E_BUG, "Reached end of StringToArgumentList"); } #define BUFLEN 8192 static int DoCommands() { char Buffer[BUFLEN]; char *StringFromUser; int ReturnValue = 0; printf("%s> ", progname); fflush(stdout); while ((StringFromUser = fgets(Buffer, BUFLEN, stdin)) != (char *) NULL) { t_Command *Command; char *p; char *CommandName; p = Buffer; CommandName = NextToken(&p); if (CommandName && *CommandName) { Command = StringToCommand(CommandName); if (!Command) { printf("Unknown command, \"%s\"\n", CommandName); printf("Use ? on a line by itself for a list of commands.\n"); } else { t_ArgumentList *Arguments = StringToArgumentList(p); int Status; if (Arguments->Count == 1) { t_Command *cp = StringToCommand(Arguments->Strings[0]); if (cp && cp->Handler == c_ShowHelp) { Arguments->Flags |= ARGLIST_PLEA_FOR_HELP; } } Status = (*(Command->Handler))(Command, Arguments); switch (Status) { case C_OK: break; case C_FAIL: ReturnValue++; if (ReturnValue > MAX_EXIT_STATUS) { ReturnValue = MAX_EXIT_STATUS; } break; case C_EXIT: return ReturnValue; case C_EXIT_OK: return 0; case C_EXIT_FAIL: return 1; default: Error(E_BUG, "Handler for \"%s\" returned %d, not in {%d,%d,%d,%d,%d}\n", CommandName, Status, C_OK, C_FAIL, C_EXIT_OK, C_EXIT_FAIL, C_EXIT ); } efree(CommandName); if (Arguments && Arguments != &EmptyArgument) { while (Arguments->Count > 0) { --Arguments->Count; efree(Arguments->Strings[Arguments->Count]); } efree((char *)(Arguments->Strings)); efree((char *)Arguments); } } } printf("%s> ", progname); fflush(stdout); } return ReturnValue; }