This repository has been archived on 2025-02-27. You can view files and clone it, but cannot push or open issues or pull requests.
CnC_Renegade/Code/wwlib/argv.cpp

510 lines
22 KiB
C++

/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* $Header: /VSS_Sync/wwlib/argv.cpp 11 8/29/01 10:25p Vss_sync $ */
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : Library *
* *
* $Archive:: /VSS_Sync/wwlib/argv.cpp $*
* *
* $Author:: Vss_sync $*
* *
* $Modtime:: 8/29/01 10:24p $*
* *
* $Revision:: 11 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* CurrentPos -- Create an instance to parse argv with. *
* ArgvClass::Free -- Release data allocated. *
* ArgvClass::Load_File -- Load args from a file. *
* *ArgvClass::Find_Value -- Find value of argument given prefix. *
* *ArgvClass::Get_Cur_Value -- Get value of current argugment. *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "argv.h"
#include <assert.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ffactory.h"
#include "rawfile.h"
int ArgvClass::Argc = 0;
char *ArgvClass::Argv[MAX_ARGC];
/***********************************************************************************************
* CurrentPos -- Create an instance to parse argv with. *
* *
* INPUT: *
* bool case_sensitive - Do you want to perform a case sensitive search (stricmp)? *
* bool exact_size - Do you want string of same lenght (strncmp) ? *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 06/18/1999 SKB : Created. *
*=============================================================================================*/
ArgvClass::ArgvClass(bool case_sensitive, bool exact_size):
Flags(0),
LastArg(0),
CurrentPos(-1)
{
Case_Sensitive(case_sensitive);
Exact_Size(exact_size);
}
/***********************************************************************************************
* *ArgvClass::Find_Again -- Search for a string given the flags. *
* *
* INPUT: *
* const char *arg - String to search for. If NULL, LastArg will be used. *
* *
* OUTPUT: *
* const char *string found (null if not found) *
* *
* WARNINGS: *
* *
* HISTORY: *
* 06/17/1999 SKB : Created. *
*=============================================================================================*/
const char *ArgvClass::Find_Again(const char *arg)
{
if (arg) {
LastArg = arg;
} else {
arg = LastArg;
}
// Make sure user has given us something to work with here.
assert(LastArg);
CurrentPos++;
if (CurrentPos < Argc) {
if (Is_Case_Sensitive()) {
if (Is_Exact_Size()) {
// Case Sensitive, Exact Size.
for (; CurrentPos < Argc; CurrentPos++) {
if (!strcmp(arg, Argv[CurrentPos])) {
return Argv[CurrentPos];
}
}
} else {
// Case Sensitive, Match first strlen(arg).
int len = strlen(arg);
for (; CurrentPos < Argc; CurrentPos++) {
if (!strncmp(arg, Argv[CurrentPos], len)) {
return Argv[CurrentPos];
}
}
}
} else {
if (Is_Exact_Size()) {
// Note case sensitive, Exact Size.
for (; CurrentPos < Argc; CurrentPos++) {
if (!stricmp(arg, Argv[CurrentPos])) {
return Argv[CurrentPos];
}
}
} else {
// Note case sensitive, Match first strlen(arg).
int len = strlen(arg);
for (; CurrentPos < Argc; CurrentPos++) {
if (!strnicmp(arg, Argv[CurrentPos], len)) {
return Argv[CurrentPos];
}
}
}
}
}
return NULL;
}
/***********************************************************************************************
* ArgvClass::Init -- Setup the command line. *
* *
* INPUT: *
* LPSTR lpCmdLine - A string of white space seperated strings. Quotes force spaces to *
* be ignored. *
* char *fileprefix - A prefix on an arguement telling system to load postfix file name *
* as command line params. *
* *
* OUTPUT: *
* *
* WARNINGS: *
* This may be called multible times with different strings. *
* Once Argc reaches MAX_ARGC, no more will be added. *
* *
* HISTORY: *
* 06/17/1999 SKB : Created. *
* 07/15/2001 SKB : Put file arguements in the correct order they were included. *
*=============================================================================================*/
int ArgvClass::Init(char *lpCmdLine, char *fileprefix)
{
// Get pointer to command line.
char *ptr = lpCmdLine;
if (!ptr || !*ptr) {
return 0;
}
int fp_cmp_len = (fileprefix) ? strlen(fileprefix) : 0;
// Save original Argc for return.
int origargc = Argc;
while (*ptr) {
char *eos;
char save;
// Keep anything within quotes as one string.
if (*ptr == '"') {
ptr++;
eos = ptr;
// Search for next " or a null.
while (*eos && (*eos != '"')) {
eos++;
}
} else if (isspace(*ptr)) {
ptr++;
continue;
} else {
eos = ptr + 1;
// search for next white space or null.
while (*eos && !isspace(*eos)) {
eos++;
}
}
// Null out end of string so string function work.
// Save the end to restore later.
save = *eos;
*eos = 0;
bool was_file = false;
// See if we are to load a file with parameters in it.
if (fp_cmp_len && !strncmp(fileprefix, ptr, fp_cmp_len)) {
ptr += fp_cmp_len;
if (*ptr) {
was_file = Load_File(ptr);
}
}
// If it was not the file or the load failed...then add parameter.
if (!was_file) {
// Copy string over and continue.
Argv[Argc] = strdup(ptr);
Argc++;
}
// If save is null, then we are at the end and we can bail out.
if (!save) break;
// resore whitespace.
*eos = save;
ptr = eos + 1;
}
// Return number of params read in.
return(Argc - origargc);
}
/***********************************************************************************************
* ArgvClass::Load_File -- Load args from a file. *
* *
* INPUT: *
* const char *fname - file to load. *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 06/18/1999 SKB : Created. *
*=============================================================================================*/
bool ArgvClass::Load_File(const char *fname)
{
file_auto_ptr fileap(_TheFileFactory, fname);
FILE *fp = fopen(fileap->File_Name(), "r");
// [SKB: May 08 2001 @ 8:42pm] :
// If file factor fails to return a valid name (i.e. can't open the file),
// then see if user (namely me) specified a full path for the file and see
// if we can open just fname.
if (!fp)
fp = fopen(fname, "r");
if (fp) {
while (Argc < MAX_ARGC) {
const int maxstrlen = 255;
char string[maxstrlen + 1];
// Get next line in file.
if (!fgets(string, maxstrlen - 1, fp)) {
break;
}
// Check for comments.
if ((*string != '#') && (*string != ';')) {
// Make sure null terminated.
string[maxstrlen - 1] = '\0';
char *ptr = string + (strlen(string) - 1);
while (*ptr <= ' ') {
*ptr = 0;
// Is it just a blank line?
if (ptr == string) {
break;
}
ptr--;
}
// If there is anyting in the string. (NAK: old code used to fail for 1 char options)
if (strlen(string)) {
Argv[Argc] = strdup(string);
Argc++;
}
}
}
fclose(fp);
return(true);
}
return(false);
}
/***********************************************************************************************
* ArgvClass::Free -- Release data allocated. *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 06/18/1999 SKB : Created. *
*=============================================================================================*/
void ArgvClass::Free()
{
for (int lp = 0; lp < Argc; lp++) {
free(Argv[lp]);
Argv[lp] = 0;
}
Argc = -1;
}
/***********************************************************************************************
* *ArgvClass::Find_Value -- Find value of argument given prefix. *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 08/23/1999 SKB : Created. *
*=============================================================================================*/
const char *ArgvClass::Find_Value(const char *arg)
{
if (arg && *arg) {
const char *ptr = Find(arg);
if (ptr) {
return(Get_Cur_Value(strlen(arg)));
}
}
return(NULL);
}
/***********************************************************************************************
* *ArgvClass::Get_Cur_Value -- Get value of current argugment. *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 08/23/1999 SKB : Created. *
* 06/25/2001 SKB : add flag user can check to see if value was extracted from next location.*
*=============================================================================================*/
const char *ArgvClass::Get_Cur_Value(unsigned prefixlen, bool * val_in_next)
{
if (val_in_next) *val_in_next = false;
if (CurrentPos < 0) {
return NULL;
}
char *ptr = Argv[CurrentPos];
if (strlen(ptr) < prefixlen) {
return(NULL);
}
ptr += prefixlen;
// Look for non white space (or eol).
while (*ptr && !isgraph(*ptr)) {
ptr++;
}
if (*ptr) {
return ptr;
}
// Goto next line to handle '-P data' case on command line.
ptr = Argv[CurrentPos + 1];
if (!ptr) {
return NULL;
}
while (*ptr) {
if (isgraph(*ptr)) {
if (val_in_next) *val_in_next = true;
return ptr;
}
ptr++;
}
return (NULL);
}
/***********************************************************************************************
* void ArgvClass::Update_Value -- Add/Replace a value *
* *
* INPUT: *
* attrib = cmd line attrib to add/replace *
* value = new value for attrib *
* *
* OUTPUT: *
* *
* WARNINGS: Not Tested! *
* *
* HISTORY: *
* 12/13/1999 NAK : Created. *
*=============================================================================================*/
void ArgvClass::Update_Value(const char *attrib, const char *value)
{
if ((Find_Value(attrib))!=NULL)
{
if (((CurrentPos+1) < Argc) && (Argv[CurrentPos+1][0] != '-')) // update old value
{
free(Argv[CurrentPos+1]);
Argv[CurrentPos+1]=strdup(value);
}
else // add new value
{
// shift vals down to make room
memmove(&(Argv[CurrentPos+2]),&(Argv[CurrentPos+1]),sizeof(char *) * (MAX_ARGC-CurrentPos-2));
Argv[CurrentPos+1]=strdup(value);
Argc++;
}
}
else // just add the new stuff
Add_Value(attrib, value);
}
/***********************************************************************************************
* void ArgvClass::Add_Value -- Add a value *
* *
* INPUT: *
* attrib = thing to add *
* value = new optional value *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 12/13/1999 NAK : Created. *
*=============================================================================================*/
void ArgvClass::Add_Value(const char *attrib, const char *value)
{
if (attrib)
{
Argv[Argc]=strdup(attrib);
Argc++;
if (value)
{
Argv[Argc]=strdup(value);
Argc++;
}
}
}
/***********************************************************************************************
* bool ArgvClass::Remove_Value -- Remove a value *
* *
* INPUT: *
* attrib = thing to remove *
* *
* OUTPUT: *
* *
* WARNINGS: *
* THIS CONTAINS A POTENTIAL BUG - I don't want to fix it because it might be a desired *
* behavior. Given: *
* Argv[0] = "-i test" "*.txt" as values in Argv, *
* calling Remove_Value("-i") will remove *.txt as well. *
* *
* HISTORY: *
* 12/13/1999 NAK : Created. *
* 06/25/2001 SKB : WARNINGS message *
*=============================================================================================*/
bool ArgvClass::Remove_Value(const char *attrib)
{
int removeCount=1;
if ((Find_Value(attrib))!=NULL)
{
free(Argv[CurrentPos]);
if (((CurrentPos+1) < Argc)&&(Argv[CurrentPos+1][0]!='-')) // value for this arg
{
free(Argv[CurrentPos+1]);
removeCount=2;
}
memmove(&(Argv[CurrentPos]),&(Argv[CurrentPos+removeCount]),sizeof(char *) * (MAX_ARGC-CurrentPos-removeCount));
Argc-=removeCount;
return(true);
}
return(false);
}