Editing/Overwritting and printing contents of an array - c

Background
I have been trying to write a line editor like UNIX's ed and I've stumbled into a problem when printing an edited line.
Code
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define NOR 0 /* Normal mode for issuing commands, deleting or changing
lines */
#define INS 1 /* Insert mode for writing a line to a file */
#define OUT 2 /* This mode is not really mode but a state to tell the
program to graciously terminate */
int
main(int argc,char**argv)
{
/* argc = 1 || argc = 2 */
u_int32_t LINS, COLS;
u_int32_t curr_line;
u_int32_t curr_col_max[128];
u_int32_t curr_line_max;
int flag_no_file, flag_err, mode, fd_file, i;
char*normal;char**buffer;
ssize_t bytes_read;
/* Atributions */
LINS = 128; COLS = 120;
curr_line = 0; curr_line_max = 0;
normal = malloc(64*sizeof(char));
buffer = (char**) malloc(LINS*sizeof(char *));
for (i = 0; i <= LINS; i++)
buffer[i] = (char *) malloc(COLS*sizeof(char));
flag_err = 0;
flag_no_file = 1;
/* Error Checking and checking for existing file */
if (argc == 2)
{
fd_file = open(argv[1], O_CREAT|O_WRONLY, 777);
if (fd_file < 0)
{
write(2,"?\n", 2);
flag_no_file = 1;
}
else flag_no_file = 0;
}
if (argc > 2) flag_err = 1; /* set so it is only editor name
and file name */
/* Code Logic */
if (flag_err == 0)
{
mode = NOR;
while (mode != OUT)
{
if (mode == NOR)
{
while (mode == NOR)
{
read(0, normal, COLS);
switch(*normal)
{
case 'q': mode = OUT; break;/* exit command */
case 'i': mode = INS; break;/* switch to insert mode */
case 'a': mode = INS;
curr_line_max++;
curr_line++;
if (*(*buffer + curr_line-1) == '\0')
*(*buffer + curr_line-1) = '\n';
break;
case '+': if (curr_line < curr_line_max)
curr_line++; /* if > curr_max */
else write(1,"?\n",2);
break;
case '-': if (curr_line > 0)
curr_line--; /* if < 0 */
else write(1,"?\n",2);
break;
case 'p': if (buffer[curr_line][0] == '\0') write(1,"?\n",2);
else write(1,buffer[curr_line],(int) COLS);
break;
case 'w': if (flag_no_file) write(1,"?\n",2);
else for (i = 0;
i < curr_line_max;
write(fd_file, buffer[i], curr_col_max[i]),
i++
); /* the correct functioning of this
code is dependant
on the correct opening and reading
of file to buffer */
break;
case 'e': fd_file = open(normal, O_CREAT|O_WRONLY, 777);
/* normal must first remove the first two elements*/
if (fd_file < 0) write(1,"?\n",2);
else flag_no_file = 0;
/* Requires passing the contents of the file
to the buffer */
break;
default: write(1, "?\n",2);
}
*normal = '\0'; /* reset the normal buffer */
}
}
if (mode == INS)
{
**(buffer + curr_line) = '\0';
bytes_read = read(0,buffer[curr_line],(int) COLS);
*(*(buffer + curr_line)+bytes_read) = '\0';
/*printf("%ld\n", bytes_read);
fflush(stdin);*/
curr_col_max[curr_line] = bytes_read;
mode = NOR;
}
}
}
free(normal);
for (i=0; i<LINS;free(buffer[i++]));
free(buffer);
return flag_err;
}
Algorithm and simulation of the unexpected output
INTENDED: editing a line should replace the entire line with the new content;
SOLUTION: as usual with C arrays, the next value after '\n' will be replaced with '\0';
PROBLEM: when using the 'p' command to write the line to the screen the contents of what
was edited still remain, thus printing a '\0'(?) character.
The following is the output
$ ./a.out
i
some string
i
some
p
some
tring
q
$
Addendum
I am aware the first line in the insert mode condition can be replaced with
if (**(buffer + curr_line) != '\0')
for (i=0; i<curr_col_max[curr_line];i++)
*(*(buffer + curr_line) + i) = '\0';
Which I believe would fix this problem, however, it doesn't seem optimal at all, as i would much prefer if write only worked until the first '\0' in the buffer.
TLDR
DESIRED - Without replacing any content in the buffer past the first '\0', write only up until there.

I think I figured it out on my own, the fact that write prints out '\0' should've been a red flag to the real issue, indeed I am writing up until COLS, it should be what is stored in the `curr_col_max' array!
write(1, buffer[curr_line], curr_col_max[curr_line]);
would be the solution to this issue, i believe.

Related

How do I print specfic parts of a string in a file

I'm writing a program that will read from /etc/passwd and output the username and shell.
For example, here is the first line:
root:x:0:0:root:/root:/bin/bash
I need to only output the user and the shell. In this instance it would print:
root:/bin/bash
The values are seperated by : so I just need to print the string before the first : and the string after the 6th :. How can I do that?
Here is the code I have so far:
#include <unistd.h>
#include <fcntl.h>
//printf and scanf prototype
int printf(const char *text, ...);
int scanf(const char *format, ...);
int main(void)
{
int fd;
int buff_size = 1;
char buff[512];
int size;
fd = open("/etc/passwd",O_RDONLY);
if (fd < 0)
{
printf("Error opening file \n");
return -1;
}
while ((size = read(fd,buff,1))>0)
{
buff[1] = '\0';
printf("%s",buff);
}
}
So far the code reads from /etc/passwd and prints out the whole file line by line. I need to configure the printf statement to just print the user and shell for each instance.
I just need to print the string before the first : and the string after the 6th :.
Filter the string, put the needed data into an output buffer, and print the output buffer.
Since you already used char buff[512]; as the original data buffer, then just create another buffer for output. i.e. char outbuff[512];
// char buff[512]; assume already defined as that in PO
// char outbuff[512]; assume already defined as that in PO
void customized_print(char* buff, char* outbuff) {
int i = 0, j = 0, cnt = 0;
while (i < 512) { // copy until first ':'
if((outbuff[j++]=buff[i++]) == ':') break;
}
// skip 5 more ':'
while (i < 512 && cnt < 5) {
if(buff[i++] == ':') cnt++;
}
// copy the remains
while (i < 512) {
if( (outbuff[j++]=buff[i++]) == '\0' ) break;
}
printf("%s\n", outbuff);
outbuff[0] = 0; // clean the outbuff
}
Another solution with a demo of full code.
If you do not need to use buff. You can use the following solution.
#include <stdio.h>
void customized_print(char* buff, char* outbuff) {
int cnt = 0;
char* p = outbuff;
while (*buff) { // copy until first ':'
if((*p++=*buff++) == ':') break;
}
// skip 5 more ':'
while (*buff && cnt < 5) {
if(*buff++ == ':') cnt++;
}
// copy the remains
while (*buff) {
if( (*p++=*buff++) == '\0' ) break;
}
printf("%s\n", outbuff);
outbuff[0]=0; // clean the outbuff
}
int main(){
char outbuff[512];
customized_print("root:x:0:0:root:/root:/bin/bash", outbuff);
customized_print("foo:x:0:0:root:/root:/bin/bash", outbuff);
return 0;
}
Compile and run:
gcc -Wall demo.c -o demo.out
./demo.out
Output:
root:/bin/bash
foo:/bin/bashh

Reading, a set line range from a file in C

I' am writing a C program which allows the user to dynamically specify the File name from which the data is to be read. Next the user enters a lower bound and an upper bound. The data in the lines from between the bounds is to be printed.
For this the main function makes a call: readValues(cTargetName, iLower, iHiger);
The function readValues is supposed to work as follows:
Check if file exist, if yes. Open it with fopen
Read with feof and fgets line by line the whole file, and store each line in char string
With a for loop, print the correct range of lines from the string
I'm not sure why but the while loop doesn't seem to exit although I use the feof statement, which should terminate after the end of the File is reached.
The code looks as follows:
#include <stdio.h>
#include <stdlib.h>
void readValues(char cFileName[75], int n, int m)
{
//Variable declaration;
char strArray[50][50];
char *parser;
int i = 0;
FILE *Data;
if(Data = fopen(cFileName, "rt") == NULL){
printf("File could not be opened");
return 1; //Can you return 1 in a void function?
}
//Read the file line by line
while(feof(Data)==0){
fgets(strArray[i], 200, Data);
i++;
}
//Reading the specified lines
for(n; n<=m; n++){
printf("%s", strArray[n]);
}
}
int main()
{
char cTargetName[75] = {"C:/Users/User1/Desktop/C_Projects_1/TestData.txt"};
int iLower = 2;
int iHiger = 4;
readValues(cTargetName, iLower, iHiger);
return 0;
}
All help is appreciated. Thanks in advance!
Here is my solution to your question:
#include <stdio.h>
#include <stdlib.h>
#define MIN_LINE_LENGTH 64
typedef enum {
false, true
} bool;
int main() {
char filename[PATH_MAX] = {0};
printf("Enter filename:\n");
fgets(filename, PATH_MAX, stdin); // get filename from stdin
char *ptr = filename;
while (*ptr) { // remove trailing newline at the end of filename (fgets() includes newline)
if (*ptr == '\n') {
*ptr = 0;
}
++ptr;
}
printf("Enter starting line and end line, separated by a space:\n");
size_t startLine = 0;
size_t endLine = 0;
bool hasFirstNum = false;
bool hasSecondNum = false;
bool hasMiddleSpace = false;
bool hasLastSpace = false;
size_t numCount = 0;
int ch;
while ((ch = fgetc(stdin)) != EOF && ch != '\n') { // continually receive chars from stdin
if (ch != 32 && !(ch >= 48 && ch <= 57)) { // if not a space or number, raise error
fprintf(stderr, "Only numerical values (and spaces) can be entered.\n");
return 1;
}
if (ch == 32) {
if (hasFirstNum) {
hasMiddleSpace = true;
}
if (hasSecondNum) {
hasLastSpace = true;
}
continue;
}
else if (!hasFirstNum) {
++numCount;
hasFirstNum = true;
}
else if (!hasSecondNum && hasMiddleSpace) {
++numCount;
hasSecondNum = true;
}
else if (hasLastSpace) {
++numCount;
}
if (numCount == 1) {
startLine *= 10;
startLine += ch - 48; // '0' character in ASCII is 48
}
else if (numCount == 2){
endLine *= 10;
endLine += ch - 48;
}
else {
break;
}
}
FILE *fp = fopen(filename, "r");
if (fp == NULL) {
fprintf(stderr, "Error opening file.\n");
return 1;
}
char **lines = malloc(sizeof(char *));
char *line = malloc(MIN_LINE_LENGTH);
*lines = line;
int c;
size_t char_count = 0;
size_t line_count = 1;
while ((c = fgetc(fp)) != EOF) { // continually get chars from file stream
if (c == '\n') { // expand lines pointer if a newline is encountered
*(line + char_count) = 0;
++line_count;
lines = realloc(lines, line_count*sizeof(char *));
line = (*(lines + line_count - 1) = malloc(MIN_LINE_LENGTH));
char_count = 0;
continue;
}
if ((char_count + 1) % MIN_LINE_LENGTH == 0 && char_count != 0) { // expand line pointer if needed
line = realloc(line, char_count + MIN_LINE_LENGTH);
}
*(line + char_count) = c;
++char_count;
}
*(line + char_count) = 0; // to ensure the last line always ends with the null byte
if (startLine >= line_count) { // raise error if starting line specified is greater than num. of lines in doc.
fprintf(stderr, "Specified starting line is less than total lines in document.\n");
return 1;
}
if (endLine > line_count) { // adjust ending line if it is greater than number of lines in doc.
endLine = line_count;
}
if (startLine == 0) { // we will be using the starting index of 1 as the first line
startLine = 1;
}
char **linesPtr = lines + startLine - 1;
while (startLine++ <= endLine) { // print lines
printf("%s\n", *linesPtr++);
}
for (size_t i = 0; i < line_count; ++i) { // free all memory
free(*(lines + i));
}
free(lines);
return 0;
}
It is a little more convoluted, but because it uses dynamic memory allocation, it can handle lines of any length within a text file.
If there is anything unclear, please let me know and I would be happy to explain.
Hope this helps!!
several issues here,
first, you limited the length of lines to 200, not exactly what you might expect to get.
the fgets function returns lines up to specified length unless hit by newline character - this should be taken into account.
additionally, fgets returns NULL if you hit EOF - no real need to use feof.
second, you could save yourself a lot of pain and simply count the number of times you get a string, and for the times you are within the range just print it immediately. will save you a nice amount of overhead
like this:
#include <stdio.h>
#include <stdlib.h>
#define MAXLINE 200//or anything else you want
void readValues(char cFileName[75], int n, int m)
{
//Variable declaration;
char line[MAXLINE];
int i = 0;
FILE *Data;
if((Data = fopen(cFileName, "rt")) == NULL){
printf("File could not be opened");
return 1; //Can you return 1 in a void function?
}
//Read the file line by line and print within range of lines
while((line=fgets(line, MAXLINE,Data))!=NULL){//terminates upon EOF
if (++i>=n&&i<=m)
printf(""%s\n",line);
}
}

zsh: bus error when passing results filename to compile C program

In the following C program, there are different input parameters.
One of those parameters, -f is to especify the filename of the results
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#define SOURCES 4
#define NUM_T 1000
char *prgname;
char usage[]="%s [-uUdipT] -f filename\n";
int main(int argc, char *argv[])
{
FILE *filefd;
char *fname;
int lt; /* lower threshold */
int ut; /* upper threshold */
int T; /* Actualization time (cells) */
double RDF, RIF; /* Rate Decrease-Increate Factor */
double PCR; /* Peak Cell Rate */
int qlen; /* queue length */
int bitEFCI[SOURCES]; /* EFCI bit */
double ACR [SOURCES]; /* Allowed Cell Rate */
double mean[SOURCES]; /* Mean Cell Rate */
int i, t; /* control variables */
double ACRacc;
prgname = strrchr(argv[0], '/');
if (prgname == (char *)NULL) prgname = argv[0];
else prgname++;
/********************************************/
/* DEFAULT VALUES & INICIALIZATION */
/********************************************/
fname = (char *)malloc(200*sizeof(char));
fname = "abr.res";
ut = 550;
lt = 450;
RDF = 0.4;
RIF = 0.02;
PCR = 150E6;
T = 70;
qlen = 0;
for (i=0; i < SOURCES; i++)
{bitEFCI[i] = 0; ACR[i] = 0.0; mean[i] = 0.0;}
/********************************************/
/* USER COMMAND LINE VALUES */
/********************************************/
while (1)
{
int c;
c = getopt(argc, argv, "u:U:d:i:p:T:f:h");
if (c == EOF) break;
switch (c)
{
case 'h': printf(usage, prgname);
fputs("-h\tHelp\n", stdout);
fputs("-u\tLower threshold (cells)\n", stdout);
fputs("-U\tUpper threshold (cells)\n", stdout);
fputs("-d\tDecrement factor\n", stdout);
fputs("-i\tIncrement factor\n", stdout);
fputs("-p\tPeak cell rate (Mbps)\n", stdout);
fputs("-T\tActualization period (cells)\n", stdout);
fputs("-f\tResults file name\n", stdout);
exit(0);
case 'u': lt = atoi(optarg);
break;
case 'U': ut = atoi(optarg);
break;
case 'd': RDF = atof(optarg);
break;
case 'i': RIF = atof(optarg);
break;
case 'p': PCR = atof(optarg);
break;
case 'T': T = atoi(optarg);
break;
case 'f': strcpy(fname, optarg);
break;
default : fprintf(stderr, usage, prgname); exit(1);
}
}
if ((filefd = fopen(fname, "w")) == NULL)
{perror("fopen"); exit(1);}
/********************************************/
/* MAIN LOOP */
/********************************************/
for (t = 1; t<= NUM_T; t++)
{
ACRacc = 0.0;
fprintf(filefd, "%d\t", t);
for (i=0; i < SOURCES; i++)
{
if (bitEFCI[i] == 0)
ACR[i] = (ACR[i]+PCR*RIF>PCR)?(PCR):(ACR[i]+PCR*RIF);
else ACR[i] *= RDF;
mean[i] += (ACR[i]-mean[i])/(double)t;
ACRacc += ACR[i];
fprintf(filefd, "%4.1f\t%4.1f\t", ACR[i]/1E6, mean[i]/1E6);
}
qlen += (int)(ACRacc*T*424/PCR);
qlen = (qlen<=T*424)?(0):(qlen - T*424);
fprintf(filefd, "%d\n", qlen/424);
for (i=0; i < SOURCES; i++)
{
if (qlen >= ut*424) bitEFCI[i] = 1;
else if (qlen < lt*424) bitEFCI[i] = 0;
}
fflush(filefd);
}
fclose(filefd);
exit(0);
}
I have compiled with
gcc -c abr1.c -Wall
gcc abr1.o -o abr1
but when I run the program
./abr1 -f "result01.res"
zsh: bus error ./abr1 -f "result01.res"
How I can pass the filename to this C program?
This is a problem:
fname = (char *)malloc(200*sizeof(char));
fname = "abr.res";
You malloc space that fname points at, then in the next line tell fname to point at the string literal "abr.res". That creates a memory leak, since you now have nothing pointing to your malloced memory.
Furthermore, with the f option, you use strcpy to write to fname. Modifying a string literal is Undefined Behavior, and could certainly explain the behavior you're seeing.
Instead, I recommend
char fname[200] = "abr.res";
This creates fname as a writable memory area of 200 bytes long, initializes it with "abr.res" as a default value, and doesn't deal with manual memory management.
Per #Laci 's comment, you're vulnerable to a buffer overflow blindly copying user input from the command line to your buffer. Steps should be taken to eliminate that risk. One way is to use strncpy:
case 'f':
strncpy(fname, optarg, sizeof fname);
// one of the dangers of strncpy is it is NOT guaranteed to NUL
// terminate the string, so do that manually just in case
fname[sizeof(fname)-1] = '\0';
break;

Attempting to 'insert' or 'add' into a text file - one small problem

Code M.R.E provided.
The symptoms are ' simple ' :
Trying on hello\nhello\nhelloEOF & inserting '_' # ln=2 & col=1 (or rather any line>1 && col==1) the program curiously does it right, but eats up the '\n' , causing line 2 to fuse with line 1 - hello_hello\nhelloEOF , rather than hello\n_hello\nhelloEOF.
Trying on the a char in the last line of file hello\nhello\nhelloEOF & inserting '_' # ln=3 & col=5 (or rather at any char col>1 in a line terminated by EOF) results in it skipping the last actual char of the line : hello\nhello\nhell_EOF instead of hello\nhello\nhell_oEOF.
Trying with the 1st char in the last line (col==1 in line terminated by EOF)results in the error message (\n Invalid Index.) being thrown.
As far as my intuition is concerned, I reckon a flaw when dealing with line termination ('\n',EOF) & how it is counted, as well as loop counters in general, but I'm not getting it.
The idea/algorithm :
Get source file from user
Get index from user (line, col)
Copy from source to a tmp file till (user's index -1 col).
Now, allow user to write strings to tmp file's buffer.
Once user terminates input, copy from user's index to tmp till EOF.
Rename/ Remove+Rename tmp as user's source file's name.
The code below does all the real talking :
#include <stdio.h>
#include <string.h>
/* Likely error region in chknmove(), chkncpy() and/or main() ;
All other functions used are also provided */
long long lncnt(FILE *lcount){
/* counts the number of lines in a file with fgets() */
rewind(lcount); /* ensures lines counted from beginning */
long long lines=0; char line[501];
while((fgets(line,501,lcount))!=NULL){
lines++;
}
rewind(lcount); /* leaves fptr # 0,0 rather than EOF */
return lines;
}
int chknmove(FILE *tomove, long long line, long long col){
/* Function to check & move FILE* to certain line/col */
rewind(tomove); /* make sure fptr is at beginning of file */
int f=0; /* set check-variable to 0 for successful */
if(line<1 || col<1)
f = -1; /* Ln,col cannot be -ve or even 0 -> 1st col is # 1ln,1col in any non-empty file */
else{
long long i,q; i=1;q=0; /* i = lines seen, q = cols seen */
/* i init to 1 for ease of comprehension v/s testing 'line-1' */
while(i<line || q<col){ /* loop must iterate until i==line && q==col */
int ch = fgetc(tomove); /* int to store EOF */
if(ch==EOF){
f=-1; break; /* if file ends before reaching index, failed. */
}
else if(ch=='\n') {
if(i==line){
f=-1; break;/* if line ends before reaching col in ln, failed. */
}
i++; q=0; /* else , increase i by 1 and set q (col) to 0 */
}
else
q++; /* any other character is 'normal' , just increment q (col) by 1 */
}
}
if(f==0){
fseek(tomove,-1,SEEK_CUR);
/* since index must be checked, loop reaches it -> fseek(-1) causes *next* r/w to fptr to be on the index . */
return 0;
}
else{
/* f != 0 : moving has failed. */
printf("\n Invalid Index.\n");
return -1;
}
}
int chkncpy(FILE *source, FILE *dest,long long beginln, long long begincol, long long endln, long long endcol){
rewind(source); int f=0;
if(beginln<1 || begincol<1 || endln<beginln || endcol<1 || (beginln==endln && endcol<begincol))
f=-1; /* line/col must be >= 1 in non-empty file, copying done top to bottom (begin-index <= end-index ) */
else{
if(chknmove(source,beginln,begincol)==0){
/* loop begins if begin-index is valid */
long long i,q; i=beginln;q=begincol; /* i = lines, q = cols */
while(i<endln || q<endcol){
/* while end-index is not reached : !(i==endln && q==endcol) */
int ch = fgetc(source);
if(ch==EOF) {f=-1; break;} /* File ends b/w begin-index & end-index - failed. */
else if(ch=='\n') {
if(i==endln) {f=-1; break;} /* endln ends before reaching endcol - failed */
i++; q=0;
fputc(ch,dest); /* valid '\n' put in dest file */
}
else{
q++; fputc(ch,dest); /* valid char put in dest file */
}
}
}
else f=-1; /* if begin-index is invalid, failed. */
}
if(f==-1){
/* if f != 0 : chkncpy() failed */
printf("\n Invalid Index.\n");
return -1;
}
else /* if f remains == 0 */ return 0;
}
long long lastlncol(FILE *fyl){
rewind(fyl); /* makes sure fptr # beginning */
chknmove(fyl,lncnt(fyl),1); /* move to last line */
long long cnt=0; /* cnt stores no. of chars in last line */
while(1){
int g = fgetc(fyl);
if (g==EOF)
break; /* EOF not counted */
else if(g=='\n'){
cnt++;break; /* \n is counted as a char, but also as EOL */
}
else
cnt++; /* all other chars counted */
}
rewind(fyl); /* leaves file at its beginning */
return cnt;
}
long long lcc(FILE *fyl,long long line){
rewind(fyl); int f = chknmove(fyl,line,1); /* moves fptr to line */
long long cnt=0;
if(f==0){
while(1){
int g = fgetc(fyl);
if (g==EOF)
break;
else if(g=='\n'){
cnt++;break;
}
else
cnt++;
}
rewind(fyl);
return cnt;
}
else
return -1;
}
void writer(FILE *write, int ctrl){
printf("\n Terminate Input with \"/end/\".\n\n Type below :\n\n");
char in[501]; /* str that stores input line-by-line */
char *p; int o;
while(1){
fgets(in,501,stdin); /* takes line from user */
if((p=strstr(in,"/end/"))!=0){
/* if line has "/end/", all chars till /end/ are written to file and input loop breaks */
o = p-in;
fprintf(write,"%.*s",o,in);
break;
}
else{
/* writes line to file */
fputs(in,write);
}
}
if(ctrl==0){
/* in some cases, file must not be closed yet , hence ctrl is taken */
int s = fclose(write);
if(s==EOF) printf("\n Error ");
else printf("\n Success.\n");
}
}
void eat() /* clears stdin */
{
int eat;while ((eat = getchar()) != '\n' && eat != EOF);
}
int main(){
/* main to add/insert to file # given index */
char fadd[501];/* filename str */
printf("\n Filename : "); scanf("%500[^\n]",fadd);
FILE * add = fopen(fadd,"r");
if(add==NULL)
perror("\n Error "); /* if file does not pre-exist */
else{
long long line, col; char sep; printf("\n Index : "); scanf("%lld%c%lld",&line,&sep,&col); /* takes index in ln-char-col form */
eat(); /* clears stdin */
FILE * tmp=fopen("Temp.Ctt","w"); /* opens a tmp file to write */
if(tmp==NULL)
perror("\n Error "); /* failed - tmp file could not be created to write */
else{
int f; /* success var */
if(line==1 && col>1){
/* if user wants to insert # a col>1 in line 1 */
f = chkncpy(add,tmp,1,1,line,col); /* all below calls intend to write till 1 char before the char # given index */
}
else if(line>1 && col>1){
/* if user wants to insert # a col>1 in any line>1*/
f = chkncpy(add,tmp,1,1,line,col-1);
}
else if(line>1 && col==1){ //ERRORS - ignores the '\n' of line-1
/* if user wants to insert # a 1st col in line>1 */
f = chkncpy(add,tmp,1,1,line-1,lcc(add,line-1));
}
else if(line==1 && col==1){
/* if user wants to insert # 1,1 (no moving/copying needed) */
f=0;
}
else{
printf("\n Invalid Index.\n");f=-1;
}
if(f==0){
writer(tmp,1);
/* calls function to allow user to write to fptr , with ctrl != 0, so writer() *won't* fclose(tmp) */
chkncpy(add,tmp,line,col,lncnt(add),lastlncol(add));
/* copies all characters from index till EOF */
int ok = fclose(tmp); fclose(add);
if(ok==EOF){
/* if closing tmp was unsuccessful, the file on disk may be corrupted/incomplete, so must be removed */
remove("Temp.Ctt");perror("\n Error ");
}
else{
if(rename("Temp.Ctt",fadd)==0)
printf("\n Success.\n");
else{
/* on Windows & some other non-POSIX systems, file cannot be renamed to pre-existing filename , hence delete original */
remove(fadd);
if(rename("Temp.Ctt",fadd)==0)
printf("\n Success.\n");
else{
/* if rename still unsuccessful, throw an error, remove tmp file and give up */
remove("Temp.Ctt");
perror("\n Error ");
}
}
}
}
}
}
}
In case of any missing details or inadvertent errors, please comment and I will correct them.
Your code is too complicated:
The different cases of copying the beginning of the file should be merged into a single function call that copies data up to and excluding the byte at column col on line line.
Then you copy from user input.
Finally you copy the rest of the input file.
The code assumes lines and columns are numbered from 1. It may be obvious but you should specify this as it may not be obvious to everyone.
The style is very difficult to read: you should use horizontal space around binary operators, after keywords, , and ; and before {. Cramming multiple statements on a single line is not a good idea either.
Here is a simplified version. As often, most of the code is for error handling:
#include <stdio.h>
#include <string.h>
void flush_line(FILE *fp) { /* read the rest of the current line */
int c;
while ((c = getc(fp)) != '\n' && c != EOF)
continue;
}
int copy_file(FILE *from, FILE *to) {
int c;
while ((c = getc(from)) != EOF) {
if (putc(c, to) == EOF)
return EOF;
}
return 0;
}
int copy_lines(FILE *from, int line1, int col1, FILE *to) {
int c, line = 1, col = 1;
for (;;) {
if (line >= line1 || (line == line1 && col >= col1))
break;
if ((c = getc(from)) == EOF)
break;
if (putc(c, to) == EOF)
return EOF;
col++;
if (c == '\n') {
line++;
col = 1;
}
}
return 0;
}
int writer(FILE *write) {
char in[501]; /* str that stores input line-by-line */
char *p;
printf("\n Terminate Input with \"/end/\".\n\n Type below :\n\n");
while (fgets(in, 501, stdin)) {
if ((p = strstr(in, "/end/")) != NULL) {
/* if line has "/end/", all chars till /end/ are written to file and input loop breaks */
int o = p - in;
if (fprintf(write, "%.*s", o, in) < 0)
return EOF;
break;
} else {
/* writes line to file */
if (fputs(in, write) < 0)
return EOF;
}
}
return 0;
}
int main() {
/* main to add/insert to file # given index */
const char *temp_filename = "Temp.Ctt";
char fadd[501];/* filename str */
printf("\n Filename : ");
if (scanf("%500[^\n]", fadd) != 1)
return 1;
flush_line(stdin);
FILE *add = fopen(fadd, "r");
if (add == NULL) {
perror("\n Cannot open input file");
return 1;
}
int line, col;
/* read the index: 1 based line and column numbers */
printf("\n Index : ");
if (scanf("%d%*c%d", &line, &col) != 2) {
fprintf(stderr, "invalid input\n");
return 1;
}
flush_line(stdin);
FILE *tmp = fopen(temp_filename, "w");
if (tmp == NULL) {
perror("\n Cannot create temporary file");
fclose(add);
return 1;
}
if (copy_lines(add, line, col, tmp)) {
perror("\n Error copying beginning of file");
fclose(add);
fclose(tmp);
remove(temp_filename);
return 1;
}
if (writer(tmp)) {
perror("\n Error writing user input");
fclose(add);
fclose(tmp);
remove(temp_filename);
return 1;
}
if (copy_file(add, tmp)) {
perror("\n Error copying remaining file contents");
fclose(add);
fclose(tmp);
remove(temp_filename);
return 1;
}
fclose(add);
if (fclose(tmp) < 0) {
/* if closing tmp was unsuccessful, the file on disk may be corrupted/incomplete, so must be removed */
perror("\n Error closing temporary file");
remove(temp_filename);
return 1;
}
if (rename(temp_filename, fadd) == 0) {
printf("\n Success.\n");
return 0;
}
/* on Windows & some other non-POSIX systems, file cannot be renamed to pre-existing filename , hence delete original */
if (remove(fadd)) {
perror("\n Cannot remove input file");
remove(temp_filename);
return 1;
}
if (rename(temp_filename, fadd) == 0) {
printf("\n Success.\n");
return 1;
}
/* if rename still unsuccessful, try and copy contents */
tmp = fopen(temp_filename, "r");
if (tmp == NULL) {
perror("\n Cannot re-open temporary file, output is in Temp.Ctt");
return 1;
}
add = fopen(fadd, "w");
if (add == NULL) {
perror("\n Cannot re-open input file, output is in Temp.Ctt");
fclose(tmp);
return 1;
}
if (copy_file(tmp, add)) {
perror("\n Error copying temporary file to input file");
fclose(add);
fclose(tmp);
return 1;
}
fclose(tmp);
if (fclose(add) < 0) {
perror("\n Error closing input file");
return 1;
}
/* throw an error, remove tmp file and give up */
remove(temp_filename);
printf("\n Success.\n");
return 0;
}
This is my self-answer , issues with chknmove() and chkncpy() were resulting in ' glitching ', those have been fixed below.
The below is ready-to-compile , and generously commented and spaced, and hence is actually larger than the main idea, which is encapsulated in chknmove() and chkncpy() - so these have been placed at the top of the code.
Hope this question/answer is useful to others in the future.
#include <stdio.h> // For file & console I/O
#include <string.h> // For strstr() in writer()
int chknmove(FILE *tomove, long long line, long long col){
/* Moves FILE* to given ((line,col) -1 char) , such that next char read from FILE* will be # (line,col)
Checks validity of index as it moves : if col not in line || EOF encountered, returns -1. */
rewind(tomove); // rewind file 'just in case'
int f = 0 ; // control variable which stores state (succeeded/failed) of chknmove()
if (line < 1 || col < 1)
{
f=-1;
printf("\n Illegal Index.\n");
// Follows 1-based line/col index : -ve values are illegal
return -1;
}
else {
long long i,q; i = q = 0; // i = lines encountered ; q = chars encountered in line i ; both are 0-based
while(i < line){
int chk = fgetc(tomove); //
if(chk == EOF){
printf("\nInvalid %s - beyond EOF.\n", (i == line -1 ) ? "Column" : "Line");
f = -1; break;
}
else if(chk == '\n'){
if(i==line-1 && q == col-1)
/* 1. This allows for user to directly point to the '\n' char , allowing him to append to line
2.(line/col - 1) : since i & q are 0-based */
break;
else{
if(i == line-1 ){
// except if target index was the '\n' , reading beyond newline # target line is invalid, since '\n' terminates line
printf("\nInvalid column for line %lld.\n",line);
f=-1; break;
}
i++; q=0; // if not # target line , reset and continue
}
}
else if(i == line-1 && q == col-1 ) // if dest index reached, break .
break;
else // if non-EOF , non-\n char encountered, increment q and continue.
q++;
}
if(f==0){
fseek(tomove,-1,SEEK_CUR); // So that the after returning/exiting chknmove() , the char read from FILE* is # line,col
return 0;
}
else
return -1;
}
}
int chkncpy(FILE* source, FILE *dest, long long beginln, long long begincol, long long endln, long long endcol) {
/* Copies everything from FILE *source to FILE *dest within begining index and terminating index , if they're valid
Returns -1 if they're invalid.*/
if (beginln < 1 || begincol < 1 || endln < beginln || endcol < ((endln == beginln) ? begincol : 1))
// -ve indexes and reading/writing backwards is illegal
return -1;
long long i, q; // i -> lines && q -> chars
int f=0;
if(chknmove(source,beginln,begincol)==0){
// checked if begining index is valid and if so, moved to it.
i=beginln; q=begincol; // i & q have same base as line & col , so 1-based
while(1){
int ch = fgetc(source);
if(ch==EOF){
printf("\nInvalid Terminating Index.\n");
f=-1; break;
}
else if(ch=='\n'){
if(i==endln && q==endcol){
fputc(ch,dest);
break;
}
else{
if(i==endln){
printf("Invalid column for line %lld.\n",endln);
f=-1; break;
}
i++; q=1; // q set to 1 -> q is 1-based !
fputc(ch,dest);
}
}
else if(i==endln && q==endcol){
fputc(ch,dest); break;
}
else{
q++; fputc(ch,dest);
}
}
}
else
f=-1;
if(f==0) return 0;
else return -1;
}
long long lcc(FILE *fyl,long long line){
// L.C.C. == line char count , i.e, count of chars in a line (including the \n).
int f = chknmove(fyl,line,1); /* attempt moving to line, store returned val */
long long cnt=0; // cnt -> number of chars found
if(f==0){ // if line exists , then :
while(1){
int g = fgetc(fyl);
if (g==EOF) // EOF checked in case line is last line
break;
else if(g=='\n'){ // '\n' is EOL , hence it is counted and then loop is terminated .
cnt++; break;
}
else
cnt++;
}
rewind(fyl);
return cnt;
}
else
return -1; // if line doesn't exist, return -1
}
int clone(FILE *wfrm,FILE *wto){
// clones wfrom ("Write From ") onto wto ("Write To") until EOF is encountered.
while(1){
int a =fgetc(wfrm);
if(a==EOF)
break;
else
fputc(a,wto);
}
return 0;
}
void writer(FILE *write){
// Allows basic console line-level I/O for writing to FILE *
printf("\n Terminate Input with \"/end/\".\n\n\tType below :\n\n");
char in[501]; /* str that stores input line-by-line */
char *p; int o;
while(1){
fgets(in,501,stdin); /* takes line from user */
if((p=strstr(in,"/end/"))!=0){
/* if line has "/end/", all chars till /end/ are written to file and input loop breaks */
o = p-in;
fprintf(write,"%.*s",o,in);
break;
}
else{
/* writes line to file */
fputs(in,write);
}
}
}
void eat() /* clears stdin */
{
int eat;while ((eat = getchar()) != '\n' && eat != EOF);
}
int main(){
/* main to add/insert to file # given index */
char fadd[501]=""; // String to store fname
printf("\n Filename : "); scanf("%500[^\n]",fadd); eat(); // Take fname, clear stdin .
FILE * add = fopen(fadd,"r"); // open file
if(add==NULL)
perror("\n Error ");
else{
// If File is loaded for reading successfully
long long line, col; char sep; // line, col and seperating char make up the index
printf("\n Index : "); scanf("%lld%c%lld",&line,&sep,&col); eat(); // take index, clear stdin
FILE * tmp=fopen("Temp.Ctt","w"); // open a temporary file
if(tmp==NULL)
perror("\n Error ");
else{
int f;
if(line>=1 && col>1){ // copy till the line , col-1
f = chkncpy(add,tmp,1,1,line,col-1);
}
else if(line>1 && col==1){ // copy till line-1 , last char
f = chkncpy(add,tmp,1,1,line-1,lcc(add,line-1));
}
else if(line==1 && col==1){ // no moving/copying necessary at all
f=0;
}
else{
printf("\n Invalid Index.\n");f=-1;
}
if(f==0){ // if Index was not invalid
writer(tmp); // let user write to temp file
clone(add,tmp); //clones the rest of add to tmp - both are *not* fclosed
int ok = fclose(tmp); fclose(add);
if(ok==EOF){
/* if closing tmp was unsuccessful, the file on disk may be corrupted/incomplete, so must be removed */
remove("Temp.Ctt");perror("\n Error ");
}
else{
if(rename("Temp.Ctt",fadd)==0)
printf("\n Success.\n");
else{
/* on Windows & some other non-POSIX systems, file cannot be renamed to pre-existing filename , hence delete original */
remove(fadd);
if(rename("Temp.Ctt",fadd)==0)
printf("\n Success.\n");
else{
/* if rename still unsuccessful, throw an error, point user to temp file and give up */
perror("\n Error ");
printf("\n %s lost. File-buffer exists as %s in cwd.\n",fadd,"Temp.Ctt");
}
}
}
}
else
remove("Temp.Ctt");
}
}
return 0;
}
Any constructive critique which may improve the answer is welcome. In case of any inadvertent errors or missing details, please comment and I will reply ASAP.

SegFault in Cache Sim C program

EDIT: so it's easier for you guys to find where in my code I think the segfault is happening, look under the commented section called /* Cache Structure */
EDIT2: made a huge mistake with gdb the correct output when it segfaults from gdb is:
(gdb) run
Starting program: /.autofs/ilab/ilab_users/petejodo/Assignment4/a.out
1
/.autofs/ilab/ilab_users/petejodo/Assignment4/a.out
Program received signal SIGSEGV, Segmentation fault.
0x00449453 in strlen () from /lib/libc.so.6
(gdb)
EDIT3: after resorting to n00b tactics like printing lines, I've found it prints before:
file = fopen(purefile, "r");
if (file == 0){
printf ("Could not find file!\n");
return 0;
}
but doesnt print "test" I have underneath it.
ORIGINAL
So after checking all the threads on segfault and using gdb I can't figure out why my program is segfaulting at this line. I'm writing a cache simulator (only have write-through in my code currently) and I didn't include my methods because they are fine, it's in the main method.
int main(int argc, char **argv) {
FILE* file;
/* Counter variables */
int i;
int j;
/* Helper Variables */
int setAdd;
int totalSet;
int trash;
int size;
int extra;
char rw;
/* Necessary Character Arrays */
char hex[100];
char bin[100];
char origTag[100];
char bbits[100];
char sbits[100];
char tbits[100];
/* Cache Info Variables */
int setNumber = 4096; /* cacheSize/blockSize : (16,384/4) */
int setBits = 12; /* log(setNumber)/log(2) : (log(4096)/log(2)) */
int tagSize = 18; /* 32-(blockBits + setBits) **blockBits = log(blockSize)/log(2)** : (32 - (2 + 12) */
/* Results */
int cacheHit = 0;
int cacheMiss = 0;
int write = 0;
int read = 0;
/* Cache Structure */
tempLine cache[4096];
char* style;
char* purefile;
if (strcmp(argv[1], "-h")==0)
{
puts("Usage: sim <write policy> <trace file>");
return 0;
}
style = argv[1];
purefile = argv[2];
file = fopen(purefile, "r");/* HYPOTHESIZED SEGFAULT HERE */
if (file == 0){
printf ("Could not find file!\n");
return 0;
}
printf("test1");
/* Setting Structure Default Values */
for(i = 0; i < setNumber; i++)
{
cache[i].tag = (char *)malloc(sizeof(char)*(tagSize + 1));
for(j = 0; j < tagSize; j++)
{
cache[i].tag[j] = '0';
}
cache[i].valid = 0;
}
/* Main Loop */
while(fgetc(file) != '#')
{
setAdd = 0;
totalSet = 0;
fseek(file, -1, SEEK_CUR);
fscanf(file, "%d: %c %s\n", &trash, &rw, origTag);
/* Cutting off '0x' off from address '0x00000000' and adding 0's if necessary */
size = strlen(origTag);
extra = (10 - size);
for(i = 0; i < extra; i++)
hex[i] = '0';
for(i = extra, j = 0; i < (size-(2-extra)); i++, j++)
hex[i] = origTag[j + 2];
hex[8] = '\0';
hex2bin(hex, bin);
split(bin, bbits, sbits, tbits);
/* Changing cArray into int */
for(i = 0, j = (setBits - 1); i < setBits; i++, j--)
{
if (sbits[i] == '1')
setAdd = 1;
if (sbits[i] == '0')
setAdd = 0;
setAdd = setAdd * pow(2, j);
totalSet += setAdd;
}
/* Calculating Hits and Misses */
if (cache[totalSet].valid == 0)
{
cache[totalSet].valid = 1;
strcpy(cache[totalSet].tag, tbits);
}
if ((cache[totalSet].valid == 1) && (strcmp(cache[totalSet].tag, tbits) == 0))
{
/* HIT */
if (rw == 'W')
{
cacheHit++;
write++;
}
if (rw == 'R')
cacheHit++;
}
else
{
/* MISS */
if (rw == 'R')
{
cacheMiss++;
read++;
}
if (rw == 'W')
{
cacheMiss++;
read++;
write++;
}
cache[totalSet].valid = 1;
strcpy(cache[totalSet].tag, tbits);
}
/* End Calculations */
}
printResult(cacheHit, cacheMiss, read, write);
return 0;
}
And what I got out of gdb was this:
**INCORRECT**
(gdb) run
Starting program: /.autofs/ilab/ilab_users/petejodo/Assignment4/a.out
Program received signal SIGSEGV, Segmentation fault.
0x080489b6 in main (argc=1, argv=0xbfffe954) at sim.c:128
128 if (strcmp(argv[1], "-h")==0)
(gdb) bt
#0 0x080489b6 in main (argc=1, argv=0xbfffe954) at sim.c:128
I'm kind of lost, any help would be great. Thanks!
Oh, and also checking the value of argv[1] it is NULL or 0x0 because I think it is because of that. argv[1] is supposed to contain either the help flag or write through or write back, I just haven't coded in whether to check for write through or write back just yet.
You need to test if argc > 1 if you want to access argv[1]. A program always receives the name it was called by (e.g. ./a.out in your case) as argv[0], so argc >= 1 is always true. To access the first real argument - i.e. the second element of argv - you need to test if there are at least two arguments. However, testing if there are > n elements when accessing argv[n] is more readable IMO since both numbers are the same.
Here's an example on how to change your code:
if (argc < 3 || (argv > 1 && strcmp(argv[1], "-h")==0))
{
puts("Usage: sim <write policy> <trace file>");
return 0;
}
In this case you will show an error if there are not enough arguments provided to have valid argv[1] and argv[2] elements or if the first argument is -h (in case someone puts stuff after -h - otherwise it'd be handled by the too-few-arguments check).
You should have a look at getopt() by the way - it makes argument/switch parsing much easier and cleaner.

Resources