Beginner here,
somewhat confused about an exercise:
Tutorial Last one on the page (it is german). I should read HTML-Lines and print attributes and their values. The declaration of the function which should be used is given.
Two things irritate me:
1. The Line is stored in a const char string, but i would like the User to type in his desired HTML-line. It seems not to be possible to change a const variable at runtime. How can it be achieved without changing the given declaration?
2. The Tutorial wants me to give back the position of strtok-search as an integer, but I read online that this value is stored within strtok, is there a way to cast that, or get it somehow?
To solve the exercise I wrote this code, but the program crashes at runtime with "Segmentation fault (core dumped)"-Message and I don't know why, could someone please explain that to me? (I probably need malloc, but for which variable?)
//cHowTo Uebung Teil 2 Nr. 4
//HTMLine.c
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
//char getHTMLline ();
int getHtmlAttributes(const char *string, int start, char *attrNamem,char *attrValue); //given by Tutorial
int main(int argc, char *argv) //because i want user-input later on, if possible
{
const char strg[]= {"<img src=\"kurt.jpg\" width=\"250\" alt=\"Kurt Kanns\" />"}; //given example line by tutorial
char attriN[255]={0}, attriV[255]={0};
int pos=99;
//printf("Please type the tag for analysis.\n");
//fgets(strg, 255, stdin);
printf("attribute\tvalue\n\n");
do
{
pos = getHtmlAttributes(strg, pos, attriN, attriV); //pos should be strtok-search-position
printf("%s\t\t%s\n", attriN, attriV);
}
while(pos!=1);
return EXIT_SUCCESS;
}
int getHtmlAttributes(const char *string, int start, char *attrNamem, char *attrValue)
{
int i, len;
char *ptr;
len = strlen(string);
char stringT[len]; //variable used to be split by strtok
for(i=0; i<len; i++)
stringT[i]=string[i];//copy string to stringT
if(start==99)
ptr = strtok(stringT, "<="); //get first attribute as whole
else
ptr = strtok(NULL, "= "); // get following attributes
for(i=0; i<len; i++)
attrNamem[i] = ptr[i];
ptr = strtok(NULL, "\""); //get values
for(i=0; i<len; i++)
attrValue[i] = ptr[i];
if(ptr == NULL) //if search complete
{
return 1;
}
else // if search continues
{
return 0;
}
}
//char getHTMLline ()
//{
// char user_input;
// scanf("%s", &user_input);
// return user_input;
//}
What strtok() does is that if you call it with a string different to NULL it stores a pointer to that string internally and returns the first token. A subsequent call with NULL then uses that internally stored pointer to determine the next token.
Now what happens in your code is:
When you call getHtmlAttributes() for the first time you create a copy of the given string in stringT and pass that copy to strtok(). The next time you call strtok( NULL, ... ). And there we have two bugs:
your loop to copy string() to stringT() is not correct. You don't copy
the teminating '\0'. Just use strcpy() in such cases
the important one: When you call getHtmlAttributes() for the second time, you call strtok( NULL, ... ) but the lifetime of the stringT that it has been called with originally has ended with the first call of getHtmlAttributes() returning, because stringT is a local variable that is created anew on the stack every time the function is called. You could solve that problem by either
declaring static char stringT[N] where N must be a constant (like 255), you can't use len (what should have been len+1 anyway) in that case
creating a dynamically allocated copy of string by char *stringT = strdup( string );. Please do that only if you call strtok( stringT, ... ) aferwards and be aware that without additional code you have a memory leak because you are not able to free that memory again.
what I would pefer: use string directly instead of stringT. In that case you should not declare string as const and create a copy of strg in main() that you pass to the function
Edit
I think the request "give back the position of strtok-search as an integer" means, you should return the offset of the found token in the complete string. That's quite easy to achieve, if you use the suggested solution static char stringT[N] from above:
As strtok() works on the string passed by the first call, after receiving a ptr different from NULL, you can calculate and return int offset = ptr - stringT;
Edit 2
And now something completely different
I've just read the Tutorial you have linked to (greetings from Hessen :-) ) and I think the idea is to use a new strtok() loop every time the function is called. That could like this:
int getHtmlAttributes(const char *string, int start, char *attrName, char *attrValue)
{
char *buf;
char *attrptr;
char *ptr;
// copy the substring starting at start to buf
// you should add error checking (buf may become NULL)
buf = strdup( string+start );
// first step: tokenize on whitespace; we stop at the first token that contains a "="
for( attrptr = strtok( buf, " \t" ); attrptr && (ptr = strchr( attrptr, '=' )) != NULL; attrptr = strtok( NULL, " \t" ) ) ;
if( attrptr ) {
// copy everything before "=" to attrName
sprintf( attrName, "%.*s", ptr-attrptr, attrptr );
// copy everything after "=" to attrValue
strcpy( attrValue, ptr+1 );
// the next start is the current start + the offset of the attr found
// + the length of the complete attr
start += (attrptr - buf) + strlen( attrptr );
free( buf );
return start;
} else {
// no more attribute
free( buf );
return -1;
}
}
Related
I'm using an array of strings in C to hold arguments given to a custom shell. I initialize the array of buffers using:
char *args[MAX_CHAR];
Once I parse the arguments, I send them to the following function to determine the type of IO redirection if there are any (this is just the first of 3 functions to check for redirection and it only checks for STDIN redirection).
int parseInputFile(char **args, char *inputFilePath) {
char *inputSymbol = "<";
int isFound = 0;
for (int i = 0; i < MAX_ARG; i++) {
if (strlen(args[i]) == 0) {
isFound = 0;
break;
}
if ((strcmp(args[i], inputSymbol)) == 0) {
strcpy(inputFilePath, args[i+1]);
isFound = 1;
break;
}
}
return isFound;
}
Once I compile and run the shell, it crashes with a SIGSEGV. Using GDB I determined that the shell is crashing on the following line:
if (strlen(args[i]) == 0) {
This is because the address of arg[i] (the first empty string after the parsed commands) is inaccessible. Here is the error from GDB and all relevant variables:
(gdb) next
359 if (strlen(args[i]) == 0) {
(gdb) p args[0]
$1 = 0x7fffffffe570 "echo"
(gdb) p args[1]
$2 = 0x7fffffffe575 "test"
(gdb) p args[2]
$3 = 0x0
(gdb) p i
$4 = 2
(gdb) next
Program received signal SIGSEGV, Segmentation fault.
parseInputFile (args=0x7fffffffd570, inputFilePath=0x7fffffffd240 "") at shell.c:359
359 if (strlen(args[i]) == 0) {
I believe that the p args[2] returning $3 = 0x0 means that because the index has yet to be written to, it is mapped to address 0x0 which is out of the bounds of execution. Although I can't figure out why this is because it was declared as a buffer. Any suggestions on how to solve this problem?
EDIT: Per Kaylum's comment, here is a minimal reproducible example
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include <sys/stat.h>
#include<readline/readline.h>
#include<readline/history.h>
#include <fcntl.h>
// Defined values
#define MAX_CHAR 256
#define MAX_ARG 64
#define clear() printf("\033[H\033[J") // Clear window
#define DEFAULT_PROMPT_SUFFIX "> "
char PROMPT[MAX_CHAR], SPATH[1024];
int parseInputFile(char **args, char *inputFilePath) {
char *inputSymbol = "<";
int isFound = 0;
for (int i = 0; i < MAX_ARG; i++) {
if (strlen(args[i]) == 0) {
isFound = 0;
break;
}
if ((strcmp(args[i], inputSymbol)) == 0) {
strcpy(inputFilePath, args[i+1]);
isFound = 1;
break;
}
}
return isFound;
}
int ioRedirectHandler(char **args) {
char inputFilePath[MAX_CHAR] = "";
// Check if any redirects exist
if (parseInputFile(args, inputFilePath)) {
return 1;
} else {
return 0;
}
}
void parseArgs(char *cmd, char **cmdArgs) {
int na;
// Separate each argument of a command to a separate string
for (na = 0; na < MAX_ARG; na++) {
cmdArgs[na] = strsep(&cmd, " ");
if (cmdArgs[na] == NULL) {
break;
}
if (strlen(cmdArgs[na]) == 0) {
na--;
}
}
}
int processInput(char* input, char **args, char **pipedArgs) {
// Parse the single command and args
parseArgs(input, args);
return 0;
}
int getInput(char *input) {
char *buf, loc_prompt[MAX_CHAR] = "\n";
strcat(loc_prompt, PROMPT);
buf = readline(loc_prompt);
if (strlen(buf) != 0) {
add_history(buf);
strcpy(input, buf);
return 0;
} else {
return 1;
}
}
void init() {
char *uname;
clear();
uname = getenv("USER");
printf("\n\n \t\tWelcome to Student Shell, %s! \n\n", uname);
// Initialize the prompt
snprintf(PROMPT, MAX_CHAR, "%s%s", uname, DEFAULT_PROMPT_SUFFIX);
}
int main() {
char input[MAX_CHAR];
char *args[MAX_CHAR], *pipedArgs[MAX_CHAR];
int isPiped = 0, isIORedir = 0;
init();
while(1) {
// Get the user input
if (getInput(input)) {
continue;
}
isPiped = processInput(input, args, pipedArgs);
isIORedir = ioRedirectHandler(args);
}
return 0;
}
Note: If I forgot to include any important information, please let me know and I can get it updated.
When you write
char *args[MAX_CHAR];
you allocate room for MAX_CHAR pointers to char. You do not initialise the array. If it is a global variable, you will have initialised all the pointers to NULL, but you do it in a function, so the elements in the array can point anywhere. You should not dereference them before you have set the pointers to point at something you are allowed to access.
You also do this, though, in parseArgs(), where you do this:
cmdArgs[na] = strsep(&cmd, " ");
There are two potential issues here, but let's deal with the one you hit first. When strsep() is through the tokens you are splitting, it returns NULL. You test for that to get out of parseArgs() so you already know this. However, where your program crashes you seem to have forgotten this again. You call strlen() on a NULL pointer, and that is a no-no.
There is a difference between NULL and the empty string. An empty string is a pointer to a buffer that has the zero char first; the string "" is a pointer to a location that holds the character '\0'. The NULL pointer is a special value for pointers, often address zero, that means that the pointer doesn't point anywhere. Obviously, the NULL pointer cannot point to an empty string. You need to check if an argument is NULL, not if it is the empty string.
If you want to check both for NULL and the empty string, you could do something like
if (!args[i] || strlen(args[i]) == 0) {
If args[i] is NULL then !args[i] is true, so you will enter the if body if you have NULL or if you have a pointer to an empty string.
(You could also check the empty string with !(*args[i]); *args[i] is the first character that args[i] points at. So *args[i] is zero if you have the empty string; zero is interpreted as false, so !(*args[i]) is true if and only if args[i] is the empty string. Not that this is more readable, but it shows again the difference between empty strings and NULL).
I mentioned another issue with the parsed arguments. Whether it is a problem or not depends on the application. But when you parse a string with strsep(), you get pointers into the parsed string. You have to be careful not to free that string (it is input in your main() function) or to modify it after you have parsed the string. If you change the string, you have changed what all the parsed strings look at. You do not do this in your program, so it isn't a problem here, but it is worth keeping in mind. If you want your parsed arguments to survive longer than they do now, after the next command is passed, you need to copy them. The next command that is passed will change them as it is now.
In main
char input[MAX_CHAR];
char *args[MAX_CHAR], *pipedArgs[MAX_CHAR];
are all uninitialized. They contain indeterminate values. This could be a potential source of bugs, but is not the reason here, as
getInput modifies the contents of input to be a valid string before any reads occur.
pipedArgs is unused, so raises no issues (yet).
args is modified by parseArgs to (possibly!) contain a NULL sentinel value, without any indeterminate pointers being read first.
Firstly, in parseArgs it is possible to completely fill args without setting the NULL sentinel value that other parts of the program should rely on.
Looking deeper, in parseInputFile the following
if (strlen(args[i]) == 0)
contradicts the limits imposed by parseArgs that disallows empty strings in the array. More importantly, args[i] may be the sentinel NULL value, and strlen expects a non-NULL pointer to a valid string.
This termination condition should simply check if args[i] is NULL.
With
strcpy(inputFilePath, args[i+1]);
args[i+1] might also be the NULL sentinel value, and strcpy also expects non-NULL pointers to valid strings. You can see this in action when inputSymbol is a match for the final token in the array.
args[i+1] may also evaluate as args[MAX_ARGS], which would be out of bounds.
Additionally, inputFilePath has a string length limit of MAX_CHAR - 1, and args[i+1] is (possibly!) a dynamically allocated string whose length might exceed this.
Some edge cases found in getInput:
Both arguments to
strcat(loc_prompt, PROMPT);
are of the size MAX_CHAR. Since loc_prompt has a length of 1. If PROMPT has the length MAX_CHAR - 1, the resulting string will have the length MAX_CHAR. This would leave no room for the NUL terminating byte.
readline can return NULL in some situations, so
buf = readline(loc_prompt);
if (strlen(buf) != 0) {
can again pass the NULL pointer to strlen.
A similar issue as before, on success readline returns a string of dynamic length, and
strcpy(input, buf);
can cause a buffer overflow by attempting to copy a string greater in length than MAX_CHAR - 1.
buf is a pointer to data allocated by malloc. It's unclear what add_history does, but this pointer must eventually be passed to free.
Some considerations.
Firstly, it is a good habit to initialize your data, even if it might not matter.
Secondly, using constants (#define MAX_CHAR 256) might help to reduce magic numbers, but they can lead you to design your program too rigidly if used in the same way.
Consider building your functions to accept a limit as an argument, and return a length. This allows you to more strictly track the sizes of your data, and prevents you from always designing around the maximum potential case.
A slightly contrived example of designing like this. We can see that find does not have to concern itself with possibly checking MAX_ARGS elements, as it is told precisely how long the list of valid elements is.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_ARGS 100
char *get_input(char *dest, size_t sz, const char *display) {
char *res;
if (display)
printf("%s", display);
if ((res = fgets(dest, sz, stdin)))
dest[strcspn(dest, "\n")] = '\0';
return res;
}
size_t find(char **list, size_t length, const char *str) {
for (size_t i = 0; i < length; i++)
if (strcmp(list[i], str) == 0)
return i;
return length;
}
size_t split(char **list, size_t limit, char *source, const char *delim) {
size_t length = 0;
char *token;
while (length < limit && (token = strsep(&source, delim)))
if (*token)
list[length++] = token;
return length;
}
int main(void) {
char input[512] = { 0 };
char *args[MAX_ARGS] = { 0 };
puts("Welcome to the shell.");
while (1) {
if (get_input(input, sizeof input, "$ ")) {
size_t argl = split(args, MAX_ARGS, input, " ");
size_t redirection = find(args, argl, "<");
puts("Command parts:");
for (size_t i = 0; i < redirection; i++)
printf("%zu: %s\n", i, args[i]);
puts("Input files:");
if (redirection == argl)
puts("[[NONE]]");
else for (size_t i = redirection + 1; i < argl; i++)
printf("%zu: %s\n", i, args[i]);
}
}
}
Hi I'm trying to insert String into String in Position N using C . So I developed a function that woks well but the problem is that work one time .
This is my code :
char *substring(char *string, int position, int length)
{
char *pointer;
int c;
pointer = (char*) malloc(length+1);
if( pointer == NULL )
exit(EXIT_FAILURE);
for( c = 0 ; c < length ; c++ )
*(pointer+c) = *((string+position-1)+c);
*(pointer+c) = '\0';
return pointer;
}
void insert_substring(char *a, char *b, int position)
{
char *f, *e;
int length;
length = strlen(a);
f = substring(a, 1, position - 1 );
e = substring(a, position, length-position+1);
strcpy(a, "");
strcat(a, f);
free(f);
strcat(a, b);
strcat(a, e);
free(e);
}
int main(void) {
char username[UNLEN+1];
DWORD username_len = UNLEN+1;
GetUserName(username, &username_len);
char msg1 [] ="Good morning mr ";
char msg2 [] ="Good evening mr ";
insert_substring(msg1,username,17);
printf("%s\n",msg1);
insert_substring(msg2,username,17);
printf("%s\n",msg2);
return 0;
}
The program display one message :
Good morning mr XXXXX
What I remark , that the program doesn't execute any instruction after the second call of insert_substring .And it doesn't display any error . Maybe it is a problem of local variable Used in functions
Looking at your code, you seem to be trying to call Win32 API functions, get current user's username and print a greeting message based on time of day (but you didn't implement this part yet).
When concatenating two null-terminated strings, you need to make sure that the target buffer has enough space to hold the two strings that you are concatenating.
In your case, you already know your maximum space required to hold username, through the use of UNLEN macro.
So you can use a buffer with size: sizeof(msg1) + UNLEN alocated through malloc. something like this:
char *target_msg1 = (char *)malloc(sizeof(msg1) + UNLEN);
You can even use the lpnSizeparameter of GetUserName and allocate as little space as possible, but since you are doing Win32 programming I don't think a couple bytes of memory is much of a concern.
Your code would change like this:
strcpy(target_msg1, msg1);
insert_substring(target_msg1,username,17);
printf("%s\n",target_msg1);
Here is my code
//Split up the config by lines
int x;
int numberOfConfigLines = 0;
for (x = 0; x < strlen(buffer); x++)
{
if (buffer[x] == '\n') {
numberOfConfigLines++;
}
}
char *configLines[numberOfConfigLines];
tokenize(configLines, buffer, "\n", numberOfConfigLines);
The idea of this function is to count the amount of newlines in a buffer, then split the buffer into a strtok array using this:
#include <string.h>
#include <stdlib.h>
void tokenize(char **arrToStoreTokens, char *delimitedString, char *delimiter, int expectedTokenArraySize) {
//Create a clone of the original string to prevent making permanent changes to it
char *tempString = (char *)malloc(strlen(delimitedString) + 1);
strcpy(tempString, delimitedString);
if (expectedTokenArraySize >= 1) {
arrToStoreTokens[0] = strtok(tempString, delimiter);
int x;
for (x = 1; x < expectedTokenArraySize; x++ ) {
arrToStoreTokens[x] = strtok(NULL, delimiter);
}
}
//Dispose of temporary clone
free(tempString);
}
If I access arrToStoreTokens[0] directly, I get the correct result, however when I try to access configLines[0] once thetokenize function has ended, I get different results (can be unknown characters or simply empty)
Additionally, I believe this has only started occurring once I began running the program as root (for a different requirement) - I may be wrong though. - EDIT: Confirmed not to be the problem.
Any ideas?
strtok doesn't reallocate anything. It only makes cut and pointers of what you gave to it.
Your array stores pointers that strtok gives you, but don't copy contents.
So if you free your tempString variable, you free data that was pointed by return values of strtok. You have to keep it and free it only at the end.
Or you can make a strdup of each return of strtok to store it in your array to make a real copy of each token, but in this case, you shall have to free each token at the end.
The second solution would looks like this :
void tokenize(char **arrToStoreTokens, char *delimitedString, char *delimiter, int expectedTokenArraySize) {
//Create a clone of the original string to prevent making permanent changes to it
char *tempString = (char *)malloc(strlen(delimitedString) + 1);
strcpy(tempString, delimitedString);
if (expectedTokenArraySize >= 1) {
arrToStoreTokens[0] = strdup(strtok(tempString, delimiter)); // Here is the new part : strdup
int x;
for (x = 1; x < expectedTokenArraySize; x++ ) {
arrToStoreTokens[x] = strdup(strtok(NULL, delimiter)); // Here is the new part : strdup
}
}
//Dispose of temporary clone
free(tempString);
}
And after use of this array, you shall have to delete it, with a function like this :
void deleteTokens(char **arrToStoreTokens, int arraySize)
{
int x;
for (x = 0; x < arraySize; ++x)
{
free(arrToStoreTokens[x]);
}
}
I'm pretty new to C and was wondering if I could get some help! I've been working on this bug for +15 hours.
So, this program is a tokenizer.
Basically, the program is supposed to take a string, or "token stream," and break it up into "tokens." A "token" is a string of either a word, hexadecimal int, octal int, decimal int, floating point int, or symbol.
The code I'm posting is only the code where things go wrong, the other portion of my program is what creates the token.
The gist of how the below code works is this: It takes a "token stream", and then finds the next token from that stream. Once that is completed, it will create a substring of the "token stream" minus the new token, and return that as the new "token stream."
Essentially, when the string "0x4356/*abdc 0777 */[]87656879jlhg kl(/j jlkh 'no thank you' /" is passed through, the program will do everything properly except when "jlhg kl(/j jlkh 'no thank you' /" passes. Once that passes through my program, a "jlhg" token is created BUT then it is added to the end of the token stream again. So, the new token stream to be broken down becomes " kl(/j jlkh 'no thank you' / jlhg" where jlhg is added on at the end, where it wasn't there before. It does this same weird thing once more, right afterwards, but with "kl" instead.
It only does this under extremely weird conditions, so I'm not sure the cause. I put print statements throughout my program and things flow normally except seemingly out of no where, the program will just add those at the end. This I why I feel like it might be a memory problem, but I have absolutely no clue where to go from here.
Any help would be GREATLY appreciated!!!!
EDIT: If you pass the string "array[xyz ] += pi 3.14159e-10 A12B" output should be:
word "array"
left brace "["
word "xyz"
right brace "]"
plusequals "+="
word "pi"
float "3.14159e-10"
word "A12B"
My TokenizerT is this:
struct TokenizerT_
{
char *tokenType;
char *token;
};
typedef struct TokenizerT_ TokenizerT;
Relevant code:
/*
* TKNewStream takes two TokenizerT objects.
* It will locate the index of the end of the last token,
* and create a substring with the new string to be tokenized.
* #tokenStream: old token stream
* #newToken: new token created from old token stream
*
*/
char *TKGetNextStream(char *tokenStream, char *newToken)
{
int i,
index = 0,
count = 0;
char last = newToken[strlen(newToken)-1];
for(i = 0; i < strlen(newToken); i++)
{
if(newToken[i] == last)
{
count++;
}
}
for(i = 0; i < strlen(tokenStream); i++)
{
if(tokenStream[i] == last && count == 1)
{
index = i + 1;
break;
}
else if(tokenStream[i] == last)
{
count--;
}
}
char *ret = malloc(sizeof(char)*(strlen(tokenStream) - index));
for(i = 0; i < strlen(tokenStream) - index; i++)
{
ret[i] = tokenStream[i+index];
}
return ret;
}
/*
* This is my main
*/
int main(int argc, char **argv)
{
char *string = "0x4356/*abdc 0777 */[]87656879jlhg kl(/j jlkh 'no thank you' /";
TokenizerT *newToken = malloc(sizeof(struct TokenizerT_)),
*tokenStream = malloc(sizeof(struct TokenizerT_));
tokenStream->token = string;
while(newToken != NULL)
{
newToken = TKCreate(TKGetNextToken(tokenStream));
if(newToken != NULL)
{
tokenStream->token = TKGetNextStream(tokenStream->token,
newToken->token);
printf("%s \"%s\"\n",
newToken->tokenType,
newToken->token);
}
}
TKDestroy(newToken);
return 0;
}
The string created in ret isn't properly null terminated. So all the functions dealing with strings will assume it goes on until the next random zero byte that happens to be found after the allocated memory.
To fix this allocate one more byte of space for ret and set that to zero, or use an existing function like strdup() to copy the string:
ret = strdup(tokenStream + index);
I came across the below code while googling which works great. (Credit to Chaitanya Bhatt # Performancecompetence.com)
The below function searches for the last occurrence of the passed delimiter and saves the remaining part of the input string to the returned output string.
void strLastOccr(char inputStr[100], char* outputStr, char *delim)
{
char *temp, *temp2;
int i = 0;
temp = "";
while (temp!=NULL)
{
if(i==0)
{
temp2 = temp;
temp = (char *)strtok(inputStr,delim);
i++;
}
if(i>0)
{
temp2 = temp;
temp = (char *)strtok(NULL,delim);
}
lr_save_string(temp2,outputStr);
}
}
Basically trying to add two new options to pass in.
Occurrence No: Instead of defaulting to the last occurrence, allowing to specific which occurrence to stop at and save the remaining of the string.
Part of the string to save: (Left, Right) At the moment the string is saving the right side once the delimiter is found. Additional option is intended to allow the user to specify for the left or right side of the delimiter is found.
void strOccr(char inputStr[100], char* outputStr, char *delim, int *occrNo, char *stringSide)
So the question is what are the modifications I need to the above function?
Also is it actually possible to do?
UPDATE
After I kept at it I was able to workout a solution.
As I can't answer my own question for another 6 hours, points will be awarded to who can provide an improved function. Specifically I don't like the code under the comment "// Removes the delim at the end of the string."
void lr_custom_string_delim_save (char inputStr[500], char* outputStr, char *delim, int occrNo, int stringSide)
{
char *temp, *temp2;
char temp3[500] = {0};
int i = 0;
int i2;
int iOccrNo = 1;
temp = "";
while (temp!=NULL) {
if(i==0) {
temp2 = temp;
temp = (char *)strtok(inputStr,delim);
i++;
}
if(i>0) {
temp2 = temp;
temp = (char *)strtok(NULL,delim);
if (stringSide==0) {
if (iOccrNo > occrNo) {
strcat(temp3, temp2);
// Ensure an extra delim is not added at the end of the string.
if (temp!=NULL) {
// Adds the delim back into the string that is removed by strtok.
strcat(temp3, delim);
}
}
}
if (stringSide==1) {
if (iOccrNo <= occrNo) {
strcat(temp3, temp2);
strcat(temp3, delim);
}
}
// Increase the occurrence counter.
iOccrNo++;
}
}
// Removes the delim at the end of the string.
if (stringSide==1) {
for( i2 = strlen (temp3) - 1; i2 >= 0
&& strchr ( delim, temp3[i2] ) != NULL; i2-- )
// replace the string terminator:
temp3[i2] = '\0';
}
// Saves the new string to new param.
lr_save_string(temp3,outputStr);
}
You really only need to make a few modifications. As you begin walking the string with strtok() you can store two variables, char *current, *previous.
As you hit each new token, move 'current' to 'previous' and store the new 'current.' At the end of the string parse look at the value of 'previous' to get the second from last element.
Other options, keep a counter and build a pseudo array using the LoadRunner variable handling mechanism, lr_save_string(token_value,"LR_variable_name_"). You'll need to build your variable name string first of course. When you fall out of the parse action your count variable will likely hold the total number of token elements parsed out of the string and then you can use the (counter-1) index value to build your string.
char foo[100]="";
...
sprint(foo, "{LR_variable_name_%d}",counter-1);
lr_message("My second to last element is %s",lr_eval_string(foo));
There are likely other options as well, but these are the two that jump to mind. Also, I recommend a book to you that I recommend to all that want to brush up on their C (including my brother and my uncle), "C for Dummies." There are lots of great options here on the string processing front that you can leverage in LoadRunner.