I know about fscanf(), fgets() and those other functions to read the next line of a text file. However, if you are given a text file by 'cat msg1.txt | ./anonymizer' would you use the same functions?
For my program the code for the main is:
int main (void)
{
char input[1000]= {'\0'}; //the sentence the user will enter
printf("Enter a sentence:");
scanf("%[^\n]", input);
char newSentence[1000]={'\0'};
sentence=(char *) &newSentence;
line=getText(input,0);
divide(input);
printf("%s\n",sentence);
return 0;
}
In the command line I enter:
gcc -o anonymizer anonymizer.c
cat msg1.txt | ./anonymizer
My msg1 text file contains:
Hi, my email addresses are h.potter#hogwarts.edu and 1a#2b3c#lkj#
Although it's not an email addresses, I'd hate if# you saw my
secret#word. Gary.zenkel#nbcuni.comHoever, input variable only
contains the first line: 'Hi, my email addresses are
h.potter#hogwarts.edu and 1a#2b3c#lkj#'
How can I get the input variable to contain the other two lines?
Almost. While it may not actually be defined in such a way, scanf(...) is essentially equivalent to fscanf(stdin, ...). Similar for gets/fgets. You should be able to use either to read from your standard input stream.
To my limited knowledge (I could be wrong), with the standard libc, there are no efficient ways to read a line when you do not know the max line length. You may get memory overflow with scanf() and gets() because they do not check the length of your buffer. If you use fgets(), you may waste time on frequent strlen() and realloc(). If you use fgetc(), it will be slow as fgetc() has a huge overhead.
For efficient line reading, we have to keep some intermediate information. It is not that easy. I am attaching an implementation. It is quite complicated, but it is very efficient and generic. If you do not care about the details, you may just focus on the main() function about how to use the routines.
To try this program:
gcc -Wall prog.c; ./a.out < input.txt > output.txt
Program:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#ifndef kroundup32
#define kroundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x))
#endif
#define kstype_t FILE* // type of file handler
#define ksread_f(fp, buf, len) fread((buf), 1, (len), (fp)) // function to read a data chunk
typedef struct {
int l, m; // l: length of string; m: allocated size
char *s; // string
} kstring_t;
typedef struct {
kstype_t f; // file handler
int begin, end, is_eof, bufsize;
unsigned char *buf; // buffer
} kstream_t;
kstream_t *ks_open(kstype_t fp, int bufsize)
{
kstream_t *ks;
ks = (kstream_t*)calloc(1, sizeof(kstream_t));
ks->bufsize = bufsize;
ks->buf = (unsigned char*)malloc(bufsize);
ks->f = fp;
return ks;
}
void ks_close(kstream_t *ks)
{
free(ks->buf); free(ks);
}
int ks_readline(kstream_t *ks, int delimiter, kstring_t *str)
{
str->l = 0;
if (ks->begin >= ks->end && ks->is_eof) return -1;
for (;;) {
int i;
if (ks->begin >= ks->end) {
if (!ks->is_eof) {
ks->begin = 0;
ks->end = ksread_f(ks->f, ks->buf, ks->bufsize);
if (ks->end < ks->bufsize) ks->is_eof = 1;
if (ks->end == 0) break;
} else break;
}
for (i = ks->begin; i < ks->end; ++i)
if (ks->buf[i] == delimiter) break;
if (str->m - str->l < i - ks->begin + 1) {
str->m = str->l + (i - ks->begin) + 1;
kroundup32(str->m);
str->s = (char*)realloc(str->s, str->m);
}
memcpy(str->s + str->l, ks->buf + ks->begin, i - ks->begin);
str->l = str->l + (i - ks->begin);
ks->begin = i + 1;
if (i < ks->end) break;
}
if (str->s == 0) {
str->m = 1;
str->s = (char*)calloc(1, 1);
}
str->s[str->l] = '\0';
return str->l;
}
int main()
{
kstream_t *ks;
kstring_t str;
str.l = str.m = 0; str.s = 0; // initialize the string struct
ks = ks_open(stdin, 4096); // initialize the file handler
while (ks_readline(ks, '\n', &str) >= 0) // read each line
puts(str.s); // print it out
ks_close(ks); free(str.s); // free
return 0;
}
Related
I am working on a project in C to implement CBC mode on top of a skeleton code for DES with OpenSSL. We are not allowed to use a function that does the CBC mode automatically, in the sense that we must implement it ourselves. I am getting output but I have result files and my output is not matching up completely with the intended results. I also am stuck on figuring out how to pad the file to ensure all the blocks are of equal size, which is probably one of the reasons why I'm not receiving the correct output. Any help would be appreciated. Here's my modification of the skeleton code so far:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/des.h>
#include <sys/time.h>
#include <unistd.h>
#define ENC 1
#define DEC 0
DES_key_schedule key;
int append(char*s, size_t size, char c) {
if(strlen(s) + 1 >= size) {
return 1;
}
int len = strlen(s);
s[len] = c;
s[len+1] = '\0';
return 0;
}
int getSize (char * s) {
char * t;
for (t = s; *t != '\0'; t++)
;
return t - s;
}
void strToHex(const_DES_cblock input, unsigned char *output) {
int arSize = 8;
unsigned int byte;
for(int i=0; i<arSize; i++) {
if(sscanf(input, "%2x", &byte) != 1) {
break;
}
output[i] = byte;
input += 2;
}
}
void doBitwiseXor(DES_LONG *xorValue, DES_LONG* data, const_DES_cblock roundOutput) {
DES_LONG temp[2];
memcpy(temp, roundOutput, 8*sizeof(unsigned char));
for(int i=0; i<2; i++) {
xorValue[i] = temp[i] ^ data[i];
}
}
void doCBCenc(DES_LONG *data, const_DES_cblock roundOutput, FILE *outFile) {
DES_LONG in[2];
doBitwiseXor(in, data, roundOutput);
DES_encrypt1(in,&key,ENC);
printf("ENCRYPTED\n");
printvalueOfDES_LONG(in);
printf("%s","\n");
fwrite(in, 8, 1, outFile);
memcpy(roundOutput, in, 2*sizeof(DES_LONG));
}
int main(int argc, char** argv)
{
const_DES_cblock cbc_key = {0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef};
const_DES_cblock IV = {0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef};
// Initialize the timing function
struct timeval start, end;
gettimeofday(&start, NULL);
int l;
if ((l = DES_set_key_checked(&cbc_key,&key)) != 0)
printf("\nkey error\n");
FILE *inpFile;
FILE *outFile;
inpFile = fopen("test.txt", "r");
outFile = fopen("test_results.txt", "wb");
if(inpFile && outFile) {
unsigned char ch;
// A char array that will hold all 8 ch values.
// each ch value is appended to this.
unsigned char eight_bits[8];
// counter for the loop that ensures that only 8 chars are done at a time.
int count = 0;
while(!feof(inpFile)) {
// read in a character
ch = fgetc(inpFile);
// print the character
printf("%c",ch);
// append the character to eight_bits
append(eight_bits,1,ch);
// increment the count so that we only go to 8.
count++;
const_DES_cblock roundOutput;
// When count gets to 8
if(count == 8) {
// for formatting
printf("%s","\n");
// Encrypt the eight characters and store them back in the char array.
//DES_encrypt1(eight_bits,&key,ENC);
doCBCenc(eight_bits, roundOutput, outFile);
// prints out the encrypted string
int k;
for(k = 0; k < getSize(eight_bits); k++){
printf("%c", eight_bits[k]);
}
// Sets count back to 0 so that we can do another 8 characters.
count = 0;
// so we just do the first 8. When everything works REMOVE THE BREAK.
//break;
}
}
} else {
printf("Error in opening file\n");
}
fclose(inpFile);
fclose(outFile);
// End the timing
gettimeofday(&end, NULL);
// Initialize seconds and micros to hold values for the time output
long seconds = (end.tv_sec - start.tv_sec);
long micros = ((seconds * 1000000) + end.tv_usec) - (start.tv_usec);
// Output the time
printf("The elapsed time is %d seconds and %d microseconds\n", seconds, micros);
}
Your crypto is at least half correct, but you have a lot of actual or potential other errors.
As you identified, raw CBC mode can only encrypt data which is a multiple of the block size, for DES 64 bits or 8 bytes (on most modern computers and all where you could use OpenSSL). In some applications this is okay; for example if the data is (always) an MD5 or SHA-256 or SHA-512 hash, or a GUID, or an IPv6 (binary) address, then it is a block multiple. But most applications want to handle at least any length in bytes, so they need to use some scheme to pad on encrypt and unpad on decrypt the last block (all blocks before the last already have the correct size). Many different schemes have been developed for this, so you need to know which to use. I assume this is a school assignment (since no real customer would set such a stupid and wasteful combination of requirements) and this should either have been specified or clearly left as a choice. One padding scheme very common today (although not for single-DES, because that is broken, unsafe, obsolete, and not common) is the one defined by PKCS5 and generalized by PKCS7 and variously called PKCS5, PKCS7, or PKCS5/7 padding, so I used that as an example.
Other than that:
you try to test feof(inpFile) before doing fgetc(inpFile). This doesn't work in C. It results in your code treating the low 8 bits of EOF (255 aka 0xFF on practically all implementations) as a valid data character added to the characters that were actually in the file. The common idiom is to store the return of getchar/getc/fgetc in a signed int and compare to EOF, but that would have required more changes so I used an alternate.
you don't initialize eight_bits which is a local-scope automatic duration variable, so its contents are undefined and depending on the implementation are often garbage, which means trying to 'append' to it by using strlen() to look for the end won't work right and might even crash. Although on some implementations at least some times it might happen to contain zero bytes, and 'work'. In addition it is possible in C for a byte read from a file (and stored here) to be \0 which will also make this work wrong, although if this file contains text, as its name suggests, it probably doesn't contain any \0 bytes.
once you fill eight_bits you write 'off-the-end' into element [8] which doesn't exist. Technically this is Undefined Behavior and anything at all can happen, traditionally expressed on Usenet as nasal demons. Plus after main finishes the first block it doesn't change anything in eight_bits so all further calls to append find it full and discard the new character.
while you could fix the above points separately, a much simple solution is available: you are already using count to count the number of bytes in the current block, so just use it as the subscript.
roundOutput is also an uninitialized local/auto variable within the loop, which is then used as the previous block for the CBC step, possibly with garbage or wrong value(s). And you don't use the IV at all, as is needed. You should allocate this before the loop (so it retains its value through all iterations) and initialize it to the IV, and then for each block in the loop your doCBCenc can properly XOR it to the new block and then leave the encrypted new block to be used next time.
your code labelled 'prints out the encrypted string' prints plaintext not ciphertext -- which is binary and shouldn't be printed directly anyway -- and is not needed because your file-read loop already echoes each character read. But if you do want to print a (validly null-terminated) string it's easier to just use fputs(s) or [f]printf([f,]"%s",s) or even fwrite(s,1,strlen(s),f).
your doCBCenc has a reference to printvalueofDES_LONG which isn't defined anywhere, and which along with two surrounding printf is clearly not needed.
you should use a cast to convert the first argument to doCBCenc -- this isn't strictly required but is good style and a good compiler (like mine) complains if you don't
finally, when an error occurs you usually print a message but then continue running, which will never work right and may produce symptoms that disguise the problem and make it hard to fix.
The below code fixes the above except that last (which would have been more work for less benefit) plus I removed routines that are now superfluous, and the timing code which is just silly: Unix already has builtin tools to measure and display process time more easily and reliably than writing code. Code I 'removed' is under #if 0 for reference, and code I added under #else or #if 1 except for the cast. The logic for PKCS5/7 padding is under #if MAYBE so it can be either selected or not. Some consider it better style to use sizeof(DES_block) or define a macro instead of the magic 8's, but I didn't bother -- especially since it would have required changes that aren't really necessary.
// SO70209636
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/des.h>
#include <sys/time.h>
#include <unistd.h>
#define ENC 1
#define DEC 0
DES_key_schedule key;
#if 0
int append(char*s, size_t size, char c) {
if(strlen(s) + 1 >= size) {
return 1;
}
int len = strlen(s);
s[len] = c;
s[len+1] = '\0';
return 0;
}
int getSize (char * s) {
char * t;
for (t = s; *t != '\0'; t++)
;
return t - s;
}
void strToHex(const_DES_cblock input, unsigned char *output) {
int arSize = 8;
unsigned int byte;
for(int i=0; i<arSize; i++) {
if(sscanf(input, "%2x", &byte) != 1) {
break;
}
output[i] = byte;
input += 2;
}
}
#endif
void doBitwiseXor(DES_LONG *xorValue, DES_LONG* data, const_DES_cblock roundOutput) {
DES_LONG temp[2];
memcpy(temp, roundOutput, 8*sizeof(unsigned char));
for(int i=0; i<2; i++) {
xorValue[i] = temp[i] ^ data[i];
}
}
void doCBCenc(DES_LONG *data, const_DES_cblock roundOutput, FILE *outFile) {
DES_LONG in[2];
doBitwiseXor(in, data, roundOutput);
DES_encrypt1(in,&key,ENC);
#if 0
printf("ENCRYPTED\n");
printvalueOfDES_LONG(in);
printf("%s","\n");
#endif
fwrite(in, 8, 1, outFile);
memcpy(roundOutput, in, 2*sizeof(DES_LONG));
}
int main(int argc, char** argv)
{
const_DES_cblock cbc_key = {0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef};
const_DES_cblock IV = {0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef};
#if 0
// Initialize the timing function
struct timeval start, end;
gettimeofday(&start, NULL);
#endif
int l;
if ((l = DES_set_key_checked(&cbc_key,&key)) != 0)
printf("\nkey error\n");
#if 1
DES_cblock roundOutput; // must be outside the loop
memcpy (roundOutput, IV, 8); // and initialized
#endif
FILE *inpFile;
FILE *outFile;
inpFile = fopen("test.txt", "r");
outFile = fopen("test.encrypt", "wb");
if(inpFile && outFile) {
unsigned char ch;
// A char array that will hold all 8 ch values.
// each ch value is appended to this.
unsigned char eight_bits[8];
// counter for the loop that ensures that only 8 chars are done at a time.
int count = 0;
#if 0
while(!feof(inpFile)) {
// read in a character
ch = fgetc(inpFile);
#else
while( ch = fgetc(inpFile), !feof(inpFile) ){
#endif
// print the character
printf("%c",ch);
#if 0
// append the character to eight_bits
append(eight_bits,1,ch);
// increment the count so that we only go to 8.
count++;
#else
eight_bits[count++] = ch;
#endif
#if 0
const_DES_cblock roundOutput;
#endif
// When count gets to 8
if(count == 8) {
// for formatting
printf("%s","\n");
// Encrypt the eight characters and store them back in the char array.
//DES_encrypt1(eight_bits,&key,ENC);
doCBCenc((DES_LONG*)eight_bits, roundOutput, outFile);
#if 0
// prints out the encrypted string
int k;
for(k = 0; k < getSize(eight_bits); k++){
printf("%c", eight_bits[k]);
}
#endif
// Sets count back to 0 so that we can do another 8 characters.
count = 0;
// so we just do the first 8. When everything works REMOVE THE BREAK.
//break;
}
}
#if MAYBE
memset (eight_bits+count, 8-count, 8-count); // PKCS5/7 padding
doCBCenc((DES_LONG*)eight_bits, roundOutput, outFile);
#endif
} else {
printf("Error in opening file\n");
}
fclose(inpFile);
fclose(outFile);
#if 0
// End the timing
gettimeofday(&end, NULL);
// Initialize seconds and micros to hold values for the time output
long seconds = (end.tv_sec - start.tv_sec);
long micros = ((seconds * 1000000) + end.tv_usec) - (start.tv_usec);
// Output the time
printf("The elapsed time is %d seconds and %d microseconds\n", seconds, micros);
#endif
}
PS: personally I wouldn't put the fwrite in doCBCenc; I would only do the encryption and let the caller do whatever I/O is appropriate which might in some cases not be fwrite. But what you have is not wrong for the requirements you apparently have.
I get this warning with gcc -std=gnu17 -Wall -Werror -Wshadow -O3 test.c:
In function ‘insertString’,
inlined from ‘replaceString’ at test.c:94:5,
inlined from ‘main’ at test.c:110:22:
test.c:69:5: error: ‘strncat’ output may be truncated copying between 0 and 77 bytes from a string of length 80 [-Werror=stringop-truncation]
strncat(source, buffer, STRING_SIZE - 1 - position - strlen(stringToInsert));
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
cc1: all warnings being treated as errors
Removing the do while loop (without any changes to the strncat() statement in question) from main() makes the warning go away.
What does the warning mean and why does it go away?
What changes should I incorporate in the code so that the above gcc command doesn't trigger the warning? The solution cannot simply disable the warning (with fe. #pragma statements). The solution has to use strncat() function.
Not important: This is for learning purposes, please be descriptive. The program solves an exercise 9 from chapter 9 of the book "Programming in C (4th Edition)" by Stephen G. Kochan.
The code:
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#define STRING_SIZE 81
int findString(const char strToSearch[], const char strSought[])
{
int strToSearchLength = strlen(strToSearch);
int strSoughtLength = strlen(strSought);
for (int i = 0; i <= strToSearchLength - 1; ++i)
{
if (strToSearch[i] == strSought[0])
{
int j = 0;
while (strToSearch[i+j] == strSought[j])
{
if (strSought[j+1] == '\0')
{
return i;
}
++j;
}
}
else if (i > strToSearchLength - strSoughtLength - 1)
{
return -1;
}
}
return -1;
}
bool removeString(char source[], const int start, const int nCharsToRemove)
{
int i, sourceLength = strlen(source);
if (start + nCharsToRemove > sourceLength || start < 0 || nCharsToRemove < 0)
{
printf("Error in function removeString(): invalid parameters.\n");
return false;
}
else
{
for (i = start; i < sourceLength; ++i)
{
source[i] = source[i + nCharsToRemove];
}
source[i] = '\0';
return true;
}
}
void insertString(char source[], const char stringToInsert[], const int position)
{
char buffer[STRING_SIZE];
int i = 0;
while (source[position + i] != '\0' && position + i < STRING_SIZE - 1)
{
buffer[i] = source[position + i];
++i;
}
buffer[i] = '\0';
source[position] = '\0';
strncat(source, stringToInsert, STRING_SIZE - 1 - position);
// THE STATEMENT MENTIONED IN THE WARNING:
strncat(source, buffer, STRING_SIZE - 1 - position - strlen(stringToInsert));
}
/* A function to replace the first occurence of the string s1
* inside the source string, if it exists, with the string s2
*/
bool replaceString(char source[], const char s1[], const char s2[])
{
int findString(const char strToSearch[], const char strSought[]);
bool removeString(char source[], const int start, const int nCharsToRemove);
void insertString(char source[], const char stringToInsert[], const int position);
int s1_position;
bool success;
// locate s1 inside source
s1_position = findString(source, s1);
if (s1_position == -1)
return false;
// remove s1 from source
success = removeString(source, s1_position, strlen(s1));
if (! success)
return false;
// insert s2 into source at the proper location
insertString(source, s2, s1_position);
return true;
}
int main(void)
{
char text[STRING_SIZE] = "1 is first*";
// uncommenting the following comment and discarding what follows it makes the warning go away
/*
replaceString(text, "is", "one");
printf("%s\n", text);
*/
bool stillFound;
do
stillFound = replaceString(text, "is", "one");
while (stillFound);
printf("%s\n", text);
return 0;
}
What does the warning mean
That is possible, potentially, that buffer will point to a string with 80 characters, but the length STRING_SIZE - 1 - position - strlen(stringToInsert) will be lower then 80. The warning was created to detect cases where not the whole source buffer would be copied to destination (ie. in a strcat(destination, source) call). It may potentially happen. In such case also the destination buffer will not be zero terminated.
why does it go away?
Different code makes compiler make different decisions. In this case compiler doesn't inline the calls, which most probably affects some static analysis done by the compiler. Adding static or attribute((always_inline)) to functions restores the warning. It's hard to answer "why" exactly - either the answer is too broad or too detailed. I believe inspect gcc sources or RTL output to know more.
What changes should I incorporate in the code so that the above gcc command doesn't trigger the warning?
Do not use strncat. Use memcpy. I think something along:
char *dest = &source[position]; // where we copy to
size_t destfree = STRING_SIZE - 1 - position; // how much space we have there
// helper macro
#define MIN(a, b) ((a)<(b)?(a):(b))
size_t to_copy = MIN(strlen(stringToInsert), destfree);
memcpy(dest, stringToInsert, to_copy);
dest += to_copy;
destfree -= to_copy;
to_copy = MIN(strlen(buffer), destfree);
memcpy(dest, buffer, to_copy);
dest += to_copy;
destfree -= to_copy;
dest[0] = '\0';
I recently got an assignment to sort members in a struct by last name and if they are the same to sort by first name. What i have so far only reads their name and age from the file but I am not properly grapsing how I would be able to sort it. So far I gathered the data from the file but im at a loss from there. I followed a code I saw but i didnt get a proper grasping of the process so i reverted back to step one.
struct Members{
int id;
char fname[50];
char lname[50];
int age;
}bio;
int main(){
int i=0;
FILE *fptr;
file = fopen("Members Bio.txt", "r");
while ( fscanf(file, "%d%s%s%d", &bio[i].id,bio[i].fname,bio[i].lname,&bio[i].age) != EOF)
{
printf("%d %s %s %d %d\n", bio[i].id,bio[i].fname, bio[i].lname, bio[i].age);
i++;
}
fclose(fptr);
}
Can anyone help me out on this one?
Code goes something like this for your case.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Members{
int id;
char fname[50];
char lname[50];
int age;
};
typedef int (*compare_func)(void*, void*);
int struct_cmp(void* s1, void* s2)
{
int l_result = strcmp(((struct Members*) s1)->lname, \
((struct Members*) s2)->lname);
if (l_result < 0)
return 1;
else if (l_result > 0)
return 0;
else
return (strcmp(((struct Members*) s1)->fname, \
((struct Members*) s2)->fname) < 0 ? 1 : 0);
}
void sort(void* arr,long ele_size,long start,long end,compare_func compare)
{
// Generic Recursive Quick Sort Algorithm
if (start < end)
{
/* Partitioning index */
void* x = arr+end*ele_size;
long i = (start - 1);
void* tmp=malloc(ele_size);
for (long j = start; j <= end - 1; j++)
{
if ((*compare)(arr+j*ele_size,x))
{
i++;
// Swap is done by copying memory areas
memcpy(tmp,arr+i*ele_size,ele_size);
memcpy(arr+i*ele_size,arr+j*ele_size,ele_size);
memcpy(arr+j*ele_size,tmp,ele_size);
}
}
memcpy(tmp,arr+(i+1)*ele_size,ele_size);
memcpy(arr+(i+1)*ele_size,arr+end*ele_size,ele_size);
memcpy(arr+end*ele_size,tmp,ele_size);
i= (i + 1);
sort(arr,ele_size,start, i - 1,compare);
sort(arr,ele_size,i + 1, end,compare);
}
}
int main()
{
FILE* fp;
int bio_max = 3;
struct Members bio[bio_max]; // Define bio to be large enough.
/* Open FILE and setup bio matrix */
/* For testing */
bio[0].id = 0;
strcpy(bio[0].fname, "");
strcpy(bio[0].lname, "Apple");
bio[0].age = 0;
bio[1].id = 1;
strcpy(bio[1].fname, "");
strcpy(bio[1].lname, "Cat");
bio[1].age = 1;
bio[2].id = 2;
strcpy(bio[2].fname, "");
strcpy(bio[2].lname, "Bat");
bio[2].age = 2;
/* Sort the structure */
sort(bio, sizeof(struct Members), 0, bio_max - 1, struct_cmp);
/* Print the sorted structure */
for (int i = 0; i < bio_max; i++) {
printf("%d %s %s %d\n", bio[i].id, bio[i].fname, \
bio[i].lname, bio[i].age);
}
}
Output
0 Apple 0
2 Bat 2
1 Cat 1
If the strings are not sorting in the way you want, you can redefine the struct_cmp function. Code is self explanatory, the base logic in the code is pass an array and swap elements using memcpy functions. You cant use simple assignment operator if you want to be generic, so that is why the element size is explicitly passed.
Edit
The code was not handling the condition, if lname are same. I missed it thanks for #4386427 for pointing this out.
I think you should define bio to be an array. And google sort algorithms please. Also recommend you google how to use libc function qsort.
I'm working on a homework assignment and I need to basically create a character buffer. One of the functions I need to create is called "b_reset". It's purpose is to reinitialize the given buffer so that it will point to the first position in the char buffer. This is needed because later on, when a new char is added to the buffer, it needs to be added to the first position in the buffer.
This is the code I have thus far:
The struct:
typedef struct BufferDescriptor {
char * ca_head ;
int capacity ;
char inc_factor;
int addc_offset ;
int mark_offset ;
char r_flag;
char mode;
} Buffer ;
The code:
int b_reset ( Buffer *pB )
{
Buffer *temp = NULL;
int i = 0;
int j = 1;
if (pB == NULL)
{
return R_FAIL_1;
}
else
{
temp = (Buffer*)malloc(sizeof(Buffer*));
if (temp == NULL)
{
return R_FAIL_1;
}
temp->ca_head = (char*)malloc(pB->capacity);
if (!temp->ca_head)
{
temp = NULL;
return R_FAIL_1;
}
for(i = 0;i < ca_getsize(pB);++i)
{
temp->ca_head[j] = pB->ca_head[i];
j++;
}
pB->ca_head = temp->ca_head;
//free(temp->ca_head);
//free(temp);
return 0;
}
}
My goal in this code was to create a temporary buffer that would basically shift over everything 1 time based on the actual given buffer. This would make the first position empty so another char could be added.
The problem I'm running into is that the original buffer doesn't seem to be returning the right values after I reset it.
When I do this for example:
temp->ca_head[0] = 'a';
temp->ca_head[1] = 'b';
temp->ca_head[2] = 'c';
temp->ca_head[3] = 'd';
temp->ca_head[4] = 'e';
b_reset(temp); //this will return the size as 0, when it's actually 5
//temp->ca_head[0] = 'i'; //if this is executed, it returns the size as 6
//and prints out the right values, but if it's not,
//it will not print out anything
printf("%d", ca_getsize(temp));
for(i = 0;i < ca_getsize(temp);++i)
{
printf("%c", temp->ca_head[i]);
}
I know something is going wrong here, but I'm not too sure what. Any suggestions would be greatly appreciated.
This code is based on your followup comment:
well I'm not trying to resize the buffer, I just want to create an
empty space in the first position, so basically shifting everything to
the right 1 time. The assumption is that there is a enough space in
the buffer to handle this process.
I don't think you need to do any malloc() ing beyond the initial one. You can just shift everything up in a loop:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#define R_FAIL_1 1
#define BUFFER_SIZE 10
typedef struct BufferDescriptor {
char * ca_head ;
int capacity ;
char inc_factor;
int addc_offset ;
int mark_offset ;
char r_flag;
char mode;
} Buffer ;
void allocate_buffer(Buffer *pB, int size)
{
pB->ca_head = malloc(size);
assert(pB->ca_head);
pB->capacity = size;
}
int ca_getsize( Buffer *pB)
{
return pB->capacity;
}
int b_reset ( Buffer *pB )
{
int i = 0;
if (pB == NULL)
{
return R_FAIL_1;
}
else
{
if ( ca_getsize(pB) <= 0 || pB->ca_head == NULL )
return R_FAIL_1;
}
// shift data up by 1 byte
for( i = ca_getsize(pB) - 1 ; i > 0;i-- )
{
pB->ca_head[i] = pB->ca_head[i-1];
}
pB->ca_head[0] = '\0';
return 0;
}
void print_buffer(Buffer *pB)
{
printf("capacity: %d \n", ca_getsize(pB));
for (int i = 0;i < ca_getsize(pB);++i)
{
printf("buffer(%d): [%d] ",i, pB->ca_head[i]);
}
printf("\n");
}
int main(void)
{
Buffer a_buffer;
allocate_buffer(&a_buffer,BUFFER_SIZE);
strcpy(a_buffer.ca_head,"abcdefgh");
print_buffer(&a_buffer);
int ret = b_reset(&a_buffer);
assert(ret == 0);
print_buffer(&a_buffer);
}
temp = (Buffer*)malloc(sizeof(Buffer*));
You need to allocate enough space to hold a Buffer, but you only allocate enough space to hold a pointer to a buffer. This should be:
temp = (Buffer*)malloc(sizeof(Buffer));
You are managing your memory incorrectly. You are allocating memory for a new Buffer struct when actually you only need to handle the memory of the ca_head member (if my interpretation of your homework problem is correct).
Each time you invoke b_reset, you will allocate memory for this struct that will not be released. If you don't handle your memory correctly, you will experience unexpected results as the one you are reporting in your question.
I suggest you to make a research on the function realloc and use it properly in your b_reset function.
Good luck with your homework.
I am creating a function that will split a full unix filename(like /home/earlz/test.bin) into its individual parts. I have got a function, and it works for the first two parts perfect, but after that it produces erroneous output...
strlcpy_char will copy a string using term as the terminator, as well as 0.
If it is terminated with term, then term will be the last character of the string, then null.
returns trg string length...
int strlcpy_char(char *trg,const char *src,int max,char term){
int i;
if(max==0){return 0;}
for(i=0;i<max-1;i++){
if(*src==0){
*trg=0;
return i;
}
if(*src==term){
*trg=term;
trg++;
*trg=0; //null terminate
return i+1;
}
*trg=*src;
src++;
trg++;
}
*trg=0;
return max;
}
.
int get_path_part(char *file,int n,char *buf){
int i;
int current_i=0;
//file is assumed to start with '/'so it skips the first character.
for(i=0;i<=n;i++){
current_i++;
current_i=strlcpy_char(buf,&file[current_i],MAX_PATH_PART_SIZE,'/');
if(current_i<=1){ //zero length string..
kputs("!"); //just a debug message. This never happens with the example
return -1; //not enough parts to the path
}
}
if(buf[current_i-1]=='/'){
return 1; //is not the last part
}else{
return 0; //is the last part(the file part)
}
}
I use this code to test it:
kputs("test path: ");
kgets(cmd);
kputs("\n");
char *tmp=malloc(256);
int i=0;
get_path_part(cmd,i,tmp);
kputs(tmp);
kputs("\n");
i=1;
get_path_part(cmd,i,tmp);
kputs(tmp);
kputs("\n");
i=2;
get_path_part(cmd,i,tmp);
kputs(tmp);
kputs("\n");
When I try something like "/home/test.bin" it works right outputting
/home
/test.bin
But when I try "/home/earlz/test.bin" I get
/home
/earlz
/arlz
Anyone see the problem in my code, as I've been looking but I just can't see any problem.
Also, before you say "but there is a library for that" I am doing this in an operating system kernel, so I barely have a standard library. I only have parts of string.h and really that's about it for standard.
You overwrite current_i instead of adding it up as you walk through the path.
So
current_i++;
current_i=strlcpy_char(buf,&file[current_i],MAX_PATH_PART_SIZE,'/');
should really be
current_i += strlcpy_char(buf,&file[current_i+1],MAX_PATH_PART_SIZE,'/');
I think you need to track your current_i for i>1 since the max value returned from the strlcpy has no idea of where you are in the overall file string. does it make sense?
current_i=strlcpy_char(buf,&file[current_i],MAX_PATH_PART_SIZE,'/');
Don't you need to do something like
tocurrent_i += strlcpy_char...
instead of
tocurrent_i = strlcpy_char...
Does your code have to be re-entrant?
If not use strtok, it is in strings.h
STRTOK(P)
NAME
strtok, strtok_r - split string into tokens
SYNOPSIS
#include <string.h>
char *strtok(char *restrict s1, const char *restrict s2);
char *strtok_r(char *restrict s, const char *restrict sep,
char **restrict lasts);
Sorry for not commenting on your code though :)
If you are using Glib, g_strsplit is very nice and easy to use.
This is how I'd do it
char ** split_into_parts(char *path) {
char ** parts = malloc(sizeof(char *) * 100);
int i = 0;
int j = 0;
if (*path == '/') {
path++;
}
parts[0] = 0;
while (*path) {
if (*path == '/') {
parts[i][j] = 0;
i++;
parts[i] = 0;
j = 0;
} else {
if (parts[i] == 0) {
parts[i] = malloc(sizeof(char) * 100);
}
parts[i][j] = *path;
j++;
}
path++;
}
parts[i+1] = 0;
return parts;
}
Try something like the code I have below.
If you need implementations of standard C functions (like strchr()) try koders.com or just google for strchr.c.
#include <stdio.h>
#include <string.h>
const char *NextToken(const char *pStart, char chSep, char *pToken, size_t nTokMax)
{
const char *pEnd;
size_t nLength;
/* set output to empty */
*pToken=0;
/* make sure input is OK */
if (!pStart || *pStart!=chSep)
return NULL;
/* find end of token */
pEnd = strchr(pStart+1, chSep);
if (pEnd)
nLength = pEnd - pStart;
else
nLength = strlen(pStart);
if (nLength >= nTokMax) /* too big */
return NULL;
strncpy(pToken, pStart, nLength);
pToken[nLength] = 0;
return pEnd;
}
int main()
{
#define BUFFSIZE 256
char cmd[BUFFSIZE];
char tmp[BUFFSIZE];
const char *pStart=cmd;
int i=0;
puts("test path: ");
fgets(cmd, BUFFSIZE, stdin);
puts("");
do {
pStart = NextToken(pStart, '/', tmp, BUFFSIZE);
if (tmp[0])
puts(tmp);
} while (pStart);
return 0;
}