Is it possible to get the entire string on line reported through LINE macro.
Sample code:
#include <stdio.h>
#define LOG(lvl) pLog(lvl, __LINE__, __FILE__)
pLog(const char *str, int line, const char *file)
{
printf("Line [%u]: File [%s]", line, file);
}
int main ()
{
LOG("Hello"
"world");
return 0;
}
The output is: Line [13]: File [macro.c]
Now in a large code base i want to search this file and print the string "Hello world" present at line reported (in this case it is 13)
One way i was thinking is to search for this file first generate the output file with gcc -E do grep for pLog and save their string then grep for LOG in actual code file and save line number match the line number with the line number present in result and then do matching of index and print the string.
As string can be distributed across multiple lines (as in code Hello is in one line and world is in another line) so also need to take care of that.
Is there anyother best and fast way of doing it or gcc provide some option to convert back line and file to actual code
This is very easy to do with Clang. The following command dumps Abstract Syntax Tree (AST) for the file test.c to the file out:
clang -cc1 -ast-dump test.c > out
Looking at the AST in the generated file you can easily find the information you need:
(StringLiteral 0x1376cd8 <line:12:9, line:13:13> 'char [11]' lvalue "Helloworld")))
Clang gives start of the first token of the string (line:12:9), start of the last token of the string (line:13:13) and the full string ("Helloworld").
You can either parse the AST dump or use Clang API to get the same information. If this is not a one time task, I'd go for API since the AST dump format is more likely to change in the future.
All this of course make sense only if you have a reason not to print the string in pLog itself.
Related
I'm currently doing an exercise where I have to create a program that takes all the code that is written inside it, and outputs it to the screen when the program is executed.
The exercise suggests that we may find it appropriate to change the file names of the program in the future - and assuming that the renaming is done in a coordinated manner, i.e. the source file and the execution file are given the same new name (except for the extension), the program should work correctly, without the need for any changes to the source code, and without the need to recompile.
The C program itself is called 'prnt.c'-
I wrote the following code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define ENDFILEC ".c" /* extension */
int main(int argc, char *argv[])
{
FILE *filePointer;
int character;
char *fileNameToOpen;
fileNameToOpen = (char *)malloc(strlen(argv[0]) + 3); /* allocating memory for string + 3 for the extension - '.c' and \0 */
strcpy(fileNameToOpen, argv[0]);
strcat(fileNameToOpen , ENDFILEC); /* ending '.c' in the end */
filePointer = fopen(fileNameToOpen, "r");
while(!feof(filePointer))
{
character = fgetc(filePointer);
printf("%c" , character);
}
fclose(filePointer);
return 0;
}
I made a 'makefile' to compile the program and I made it so that the executable would be called 'prnt1'.
basically, like the following:
prnt1 : prnt.c
gcc -ansi -Wall -pedantic prnt.c -o prnt1
The compilation worked, but whenever I run the program itself, it gives me a runtime error, saying: "Segmentation fault (core dumped)". When I look at the code itself, I don't seem to reach a memory that doesn't belong to me, so what could be an explanation for that problem and what can be done about it? Thank you in advance for your help.
Since you said that the executable is named "prnt1" and the source file (which you want to read the code from) is named "prnt", argv[0] has the name of the executable (i.e. "prnt1") and, when ".c" is appended to argv[0], it becomes "prnt1.c" – which is definitely not the file you are trying to read from; athen, since this file doesn't exist, you're getting a segmentation fault.
So, as Tom Karzes said, always check the return value of fopen().
The text file is a list of words similar to this. each world is on a new line
When i run my code, the only thing being printed is "hi".... how do i get it to continue to read the next lines
hi
hello
welcome
#include <stdio.h>
#include <stdlib.h>
int main()
{
char data[100];
// OPENS THE FILE
FILE *fp = fopen("/classes/cs3304/cs330432/Programs/StringerTest/people.txt", "r");
if (fp == NULL)
{
printf("Unable to open the file\n");
} else {
fscanf(fp, "%s", data);
printf("%s", data);
fclose(fp);
}
}
How to get fscanf to scan the next Line in C
Read the documentation of fscanf. You cannot scan lines with it. And fscanf can fail, and you should handle its failure.
A possible approach is to read the next line using fgets (or getline, or readline) and later to parse it using sscanf.
Be also aware that in 2021, UTF-8 is used everywhere. So document on paper what your program should do if your people.txt file contains a line like Être ou ne pas être or a line with СТАРЫНКЕВИЧ (in Cyrillic letters).
If allowed, use a recent GCC compiler as gcc -Wall -Wextra -g (asking for all warnings and debug info), improve your code to get no warnings, and then use the GDB debugger to understand the behavior of your program. You could install Debian on your laptop to get gcc and gdb
The documentation of your program could use EBNF notation to specify what are the valid inputs.
Consider generating parts of your C code with tools like GNU bison.
When fopen fails you could use perror to report the error.
You may want to read the wikipage about recursive descent parsing.
You may want to study -for inspiration- the source code of GNU coreutils.
The concept of my code is like:
#include <stdio.h>
int main(int argc, char *argv[])
{
int num;
FILE *fp;
getint("num",&num); /* This line is pseudo-code. The first argument is key for argument, the second is the variable storing the input value */
fp = inputfile("input"); /* This line is pseudo-code. The argument is key for argument, fp stores the return file pointer */
...
...
exit(0);
}
Usually, after compiling the code and generating the executable main, in the command line we write this to run the code:
./main num=1 input="data.bin"
However, if there's too many arguments, type in the command line each time we run the code is not convenient. So I'm thinking about writing the arguments and run in Linux shell. At first I wrote this:
#! /bin/sh
num = 1
input="data.bin"
./main $(num) $(input)
But error returns:
bash: adj: command not found
bash: input: command not found
bash: adj: command not found
bash: input: command not found
Can anybody help to see and fix it.
There are three main problems with your code:
You can't use spaces around the = when assigning values
You have to use ${var} and not $(var) when expanding values.
The way your code is written, you are passing the string 1 instead of your required string num=1 as the parameter.
Use an array instead:
#!/bin/bash
parameters=(
num=1
input="data.bin"
)
./main "${parameters[#]}"
num=1 is here just an array element string with an equals sign in it, and is not related to shell variable assignments.
I have a Read and Reverse Coding assignment where I need to pass a Textfile and a character (-L, or -W), depending on whether the operator wants the textfile returned in reverse by lines or by words. (I should also note that the assignment requires that nothing is asked of the user during the code. It must be decided which variation is wanted in the command line.)
I don't need help with the code to reverse the lines or words, but do need help with understanding how to take in character and the textfile, then use them in the code. I've tried using the parameters (int argc, char *argv[]) on the main, but anytime I try to pass in just the -L the terminal either says Command not found or clang: error: argument to '-L' is missing (expected 1 value)
Also, when my teacher passes a textfile to a program he often uses a >. Can someone explain how to use this?
Ex. program.c > hello.txt
Then he would end up using that .txt in the program.
Consider this:
program -L < data.txt
or
program -W < data.txt
or
cat data.txt | program -L
The "-L" or "-W" will be in argv[1].
Good luck!
The idea of Passing a Command Line Arguement is the following
Argc: argument counter amount of "strings"(arguments) passed for execution.
Its always 1 or greater as the calling of the function is an argument.
Argv: argument vectors(pointers), is a pointer to each of the arguments received by the command line
Example of program call:
./myprogram -w
argc=2
argv will have two pointers to strings(char):
argv[0]= "./myprograms"
argv[1]= "-w"
Now with your problem
When excecuting a program via command line you have a lot of options amongst these:
1) One of these is to give the program input of a file(the file will be passed character by character to the standard input ending with an EOF or -1 -not an ascii character-)
These can be done by the follow way
./program.c < hello.txt
2)Redirect the output of the program to a file
./program.c > hello.txt
What you are looking to do is input a file while passing an argument this can be done the following way
./program.c < hello.txt -L
IMPORTANT: "< hello.txt" will NOT count as an argument so for this case the case the argc and argv will be the follow
argc=2
argv[0]="./program.c"
argv[1]="-L"
Hopes this helps comment if you need anymore help or something isn't clear. Good luck with your course!!!
I am generating three address code for a c like program containing declaration,arithmetic,boolean, if and while statements.
Currently i am beginning with arithmetic expression. I am reading the c like program from a text file.
Lex code:
parser.lex
Yacc code:
parser.yacc
Input C like program(contents of test.txt)
a=1+2/3;
I have a make file like:
bison -d -v parser.y
flex -o parser.lex.c parser.lex
gcc -o cparser parser.lex.c parser.tab.c -lfl -lm
./cparser
When i compile my input file, i get the following output:
t1=2/3/3
t2=1+2/3;+t1
a=1+2/3;=t2
Parsing Successful. The three address code is:
syntax error
Successful parsing.
Why are the $1 $2 $3...etc not containing the desired reduction?
Why is the stderr printing syntax error?
In your lexer code, you have things like:
{number} {yylval=yytext; return NUMBER;}
this will set $$ for that token to point at the lexer internal buffer which will be clobbered by the next call to yylex, so when you go to print it in the parser, you'll print some garbage. You need something like:
{number} {yylval=strdup(yytext); return NUMBER;}
In addition, you have patterns like:
'int' return INT;
The ' character is not special in any way to flex, so this pattern matches the 5-character sequence 'int'.
if (yyparse())
should be
if (yyparse() == 0)
In your lex rule "\n" {/*simply skip new line*/} you could keep track of the line number so when there is a syntax error you can print out the line number.