How to load mruby code from external file in C program? - c

I'm getting started with mruby. I'm also pretty new to programming in C, so I may not be familiar with many of the basics. I was able to compile the example mruby program which loads the ruby code from a string. Now I would like to load it from an external file. Here's the example code that I'm using:
#include <stdlib.h>
#include <stdio.h>
#include <mruby.h>
static mrb_value my_c_method(mrb_state *mrb, mrb_value value)
{
puts("Called my C method");
return value;
}
int main(void)
{
mrb_state *mrb = mrb_open();
struct RClass *mymodule = mrb_define_module(mrb, "MyModule");
mrb_define_class_method(mrb, mymodule, "my_c_method", my_c_method, MRB_ARGS_NONE());
mrb_load_string(mrb, "MyModule.my_c_method"); // move this to an external file
return 0;
}
As you can see in the example above, I would like to move the ruby code to an external file. I'm a little confused as to whether or not I can simply "include" the ruby file, or if I need to precompile it into an .mrb file. Even then I'm not sure how to include that .mrb file when compiling.
What do I need to change to be able to load the ruby code from an external file?

Assuming your code is stored in file foo, all you need is to open the file and read it. To open the file, you need fopen(), defined in stdio.h. You can then read it line by line using fgets(). I'm not familiar with mruby, so I'm not exactly sure if mrb_load_string expects that every mruby code is in a single line. I will assume so. Here's how you can do it:
#define MAX_CODE_SIZE 128
FILE *code;
char code_buf[128]
code = fopen("foo", "r");
if (code == NULL) {
/* File couldn't be opened, handle error case... */
}
fgets(code_buf, MAX_CODE_SIZE, code);
/* Do some work ... */
fclose(code); /* Don't forget to close the file */
This piece of code reads the first line of file foo, up to a limit of 127 characters (including newline), and stores it in code_buf. You can then call mrb_load_string:
mrb_load_string(mrb, code);
I'm not sure if this is what you wanted, I never touched mruby, but from what I saw, mrb_load_string expects a char * with the code, and you want that to come from a file. That's how you do it.
If you want to read a file with code in multiple lines, you have no choice but to allocate a large enough buffer and read it using fread():
#include <stdio.h>
#define MAX_LINE_LENGTH 128
#define MAX_LINES 256
#define MAX_FILE_SIZE MAX_LINE_LENGTH*MAX_LINES
char code[MAX_FILE_SIZE];
int read_code(char *filepath) {
FILE *fp = fopen(filepath, "r");
if (fp == NULL)
return 0;
fread(code, 1, MAX_FILE_SIZE, fp);
fclose(fp);
return 1;
}
This function reads the whole file (assuming it doesn't exceed our buffer limits). code is global because you can easily reach the stack capacity if you allocate large local variables (another alternative is to use dynamic allocation). When you call read_code(), you should make sure to check its return value, to check for possible errors upon opening the file. Also, you can play with fread()'s return value to know if the buffer size wasn't enough to read everything.
Just make sure you don't forget to close the file when you're done.
EDIT: For the fgets() version, please note that in case the line holds less than 128 characters, newline will be retained in code_buf. You may want to set code_buf[strlen(code_buf)-1] to '\0' if that's the case.
UPDATE:
From our discussion on the comments below, I am updating my answer with a rudimentary parser to enable you to read the ruby file at compile time. Basically, the parser will read your ruby file and generate an output file with valid C code that inserts the file's content in a char array. Special characters are accordingly escaped. Here it is:
#include <stdio.h>
void inline insert(int, FILE *);
int main(int argc, char *argv[]) {
FILE *out, *in;
int c;
if (argc != 3) {
printf("Usage: %s <input_file> <output_file>\n", argv[0]);
return 1;
}
in = fopen(argv[1], "r");
out = fopen(argv[2], "w");
if (out == NULL) {
printf("Unable to create or write to %s\n", argv[1]);
return 1;
}
if (in == NULL) {
printf("Unable to read %s\n", argv[1]);
return 1;
}
fputs("#ifndef MRUBY_CODE_FILE_GUARD\n", out);
fputs("#define MRUBY_CODE_FILE_GUARD\n", out);
fputs("char mruby_code[] = \"", out);
while ((c = getc(in)) != EOF)
insert(c, out);
fputs("\";\n", out);
fputs("#endif\n", out);
fclose(in);
fclose(out);
return 0;
}
void inline insert(int c, FILE *fp) {
switch (c) {
case '\a':
fputs("\\a", fp);
break;
case '\b':
fputs("\\b", fp);
break;
case '\f':
fputs("\\f", fp);
break;
case '\n':
fputs("\\n", fp);
break;
case '\r':
fputs("\\r", fp);
break;
case '\t':
fputs("\\t", fp);
break;
case '\v':
fputs("\\v", fp);
break;
case '\\':
fputs("\\\\", fp);
break;
case '\'':
fputs("\\'", fp);
break;
case '"':
fputs("\\\"", fp);
break;
default:
fputc(c, fp);
}
}
Now, go back to your original program and add the following include directive in the beginning:
#include mruby_code.h
You have to do the following steps to compile a runnable program, assuming that this parser was compiled into a file named fileparser.c:
Run ./fileparser /path/to/mruby_code_file /path/to/program/mruby_code.h.
Compile your original program (it will include mruby_code.h)
The mruby code is provided in a variable called mruby_code. This is a char array, so you can pass it to mrb_load_string. And voila, you got the mruby file read once at compile time.

In the mruby/compile.h there is a method called mrb_load_file which allows you to load a ruby file and execute it with mruby:
#include <stdlib.h>
#include <stdio.h>
#include <mruby.h>
#include <mruby/compile.h>
static mrb_value my_c_method(mrb_state *mrb, mrb_value value)
{
puts("Called my C method");
return value;
}
int main(void)
{
mrb_state *mrb = mrb_open();
struct RClass *mymodule = mrb_define_module(mrb, "MyModule");
mrb_define_class_method(mrb, mymodule, "my_c_method", my_c_method, MRB_ARGS_NONE());
FILE *f = fopen("example.rb", "r");
mrb_load_file(mrb, f);
return 0;
}

Apart from using mrb_load_file:
FILE *f = fopen("the_file.rb", "r");
mrb_load_file(mrb, f);
fclose(f) ;
The manual option is to read the filename:
int load_ruby_file_to_state(char *filename, mrb_state *state) {
FILE *file = fopen(filename, "r") ;
if (!file) return -1 ;
fseek(file, 0, SEEK_END);
unsigned long length = ftell(file) ;
fseek(file, 0, SEEK_SET);
char *code = malloc(length + 1) ;
if (code) {
fread(code, 1, length, file) ;
code[length + 1] = '\0' ;
} else {
return -1 ;
}
fclose(file) ;
mrb_load_string(state, code) ;
free(code) ;
return 0 ;
}
And load file: load_ruby_file_to_state("the_file.rb", mrb) ;
Do note that mrb_load_file() will give a segfault if the file doesn't exist, so it's better to check for the file existence or if it's readable.

Related

read a text file, make some trivial transformation character by character (swapping the case of all letters), write result to text file

I have to read a text file, make some trivial transformation character by character (swapping the case of all letters), write results to the text files. I wrote this code, but it's not working. Please guide me in this regard. Thanks for in Advance
#include <stdio.h>
#include <stdlib.h>
int main() {
char c[1000];
char x[100];
char var;
int i;
FILE *fptr;
if ((fptr = fopen("text.txt", "r")) == NULL) {
printf("Error! opening file");
// Program exits if file pointer returns NULL...
exit(1);
}
// reads text until a newline is encountered...
fscanf(fptr, "%[^\n]", c);
printf("Data from the file:\n%s", c);
// Convert the file to upper case....
for( i=0;i<= strlen(c);i++){
if(c[i]>=65&&c[i]<=90)
c[i]=c[i]+32;
}
fptr = fopen("program.txt","w");
fprintf(fptr,"%[^\n]",c);
fclose(fptr);
return 0;
}
Edit: added #include <stdlib.h>, removed static describing main()
My proposition, based on example of copying a file given at my uni.
I used toupper() from ctype.h, if you don't want to use it you can just add 32 under condition similarly to your solution
Note: there could be char c instead of int c. (In the original version it actually was char; I changed it because if you look at the headers in the docs of all functions dealing with c, they all take/return int, not char; in your version it would matter more as you keep an array, in my program it changes pretty much nothing – int is just my preferred practice).
Note2: I actually never delved into the difference between "w"/"r" (write/read) and "wb"/"rb" (write/read binary). The code seems to work either way.
(I think there is no big difference when the files are text files anyway, for further assurance that both versions work, note that the code uses feof() to handle EOF)
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
int main(void) {
FILE *from, *to;
int c;//could be char
/* opening the source file */
if ((from = fopen("text.txt", "rb")) == NULL) {
printf("no such source file\n");
exit(1);
}
/* opening the target file */
if ((to = fopen("program.txt", "wb")) == NULL) {
printf("error while opening target file\n");
exit(1);
}
while (!feof(from)) {
c = fgetc(from);
if (ferror(from)) {
printf("error while reading from the source file\n");
exit(1);
}
if (!feof(from)) {//we avoid writing EOF
fputc(toupper(c), to);
if (ferror(to)) {
printf("error while writing to the target file\n");
exit(1);
}
}
}
if (fclose(from) == EOF) {
printf("error while closing...\n");
exit(1);
}
if (fclose(to) == EOF) {
printf("error while closing...\n");
exit(1);
}
return 0;
}
For a version taking arguments from command line (works on windows too) replace the beginning of main with
int main(int argc, char *argv[]) {
FILE *from, *to;
char c;
/* checking the number of arguments in the command line */
if (argc != 3) {
printf("usage: name_of_executable_of_this_main <f1> <f2>\n");//name_of_exe could be copy_to_upper, for example; change adequately
exit(1);
}
/* opening the source file */
if ((from = fopen(argv[1], "rb")) == NULL) {
printf("no such source file\n");
exit(1);
}
/* opening the target file */
if ((to = fopen(argv[2], "wb")) == NULL) {
printf("error while opening the target file\n");
exit(1);
}
I don't know how to code in that language(i think it's C++), but basically want you should be doing is a for loop to iterate through every character in the string. In Python it would look like:
x = open("text.txt", "r")
y = open("new text.txt","w")
z = ""
for char in x:
z += char.upper()
y.write(z)
I hope I was able to give an idea of how to solve your problem. I'm a newbie as well, but in Python.

Opening Files in C

Full Edit:
I am getting frustrated, I don't know what am I doing wrong in here
I still have so many stuff to do in the code but I can't even open a file to continue my work.
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
void main()
{
char letter;
FILE *fp;
fp=fopen("‪‪‪C:\\Users\\LENOVO\\Desktop\\data.txt","r");
if(fp==NULL)
{
printf("error");
getch();
exit(1);
}
while(fscanf(fp,"%d",&letter)!=EOF)
putchar(letter);
getch();
fclose(fp);
}‪
Picture of the path: http://imgur.com/a/YwFYy
Still prints error
Ok, firstly let's take a look at your file path. There are two ways to acces a file from your local storage:
relative addresses if the file has the same root folder as your application
absolute addresses if the file is in a determined place on your machine's storage
I see that you are trying to use an absolute address to read from your file. Your path is correct but you have to take care about string formatting in C because the \ character could be interpreted as something else.
I would suggest to use this instead ( double back-slash )
input=fopen("‪C:\\Users\\LENOVO\\Desktop\\data.txt","r");
This will prevent string formatting interpretations.
Secondly, EOF is just a predefined macro constant and i think it is equal to -1 so your while(! (-1) ) code is not a good ideea for reading until the end of the file.
In order to read from a file until you reach the its end i would consider this property of fscanf() :
fscanf() returns EOF when it reaches the end of the file.
while(fscanf(input,"%ch",&letter) != EOF) {
putchar(letter);
}
This way of reading from a file should do the job.
To read everything from a text file and store its contents into a buffer:
First, you should count how many characters there are in the text file:
size_t get_file_len(FILE *fp)
{
size_t num = 0;
while (fgetc(fp) != EOF)
num++;
return (fseek(fp, 0, SEEK_SET) == 0 ? num : 0);
}
Then allocate memory for a buffer large enough and read all the characters:
char *load_text(const char *path)
{
char *buf = NULL;
FILE *fp = NULL;
size_t num = 0;
size_t i = 0;
int c = 0;
/* open the file in text mode */
fp = fopen(path, "r");
if (!fp)
return NULL;
/* if the file was empty or if an error occurred, return error */
if ((num = get_file_len(fp)) == 0) {
fclose(fp);
return NULL;
}
buf = malloc(num + 1);
if (!buf) {
fclose(fp);
return NULL;
}
while ((c = fgetc(fp)) != EOF)
buf[i++] = (char)c;
/* ensure that the string is null-terminated */
buf[i] = '\0';
fclose(fp);
return buf;
}
Also, in C, all escape sequences begin with a '\' (backslash), so if you wanted to write a backslash in a string or a char you should write it as a '\\' (double backslash):
input=fopen("‪C:\\Users\\LENOVO\\Desktop\\data.txt","r");
pretty simple here :
while(!feof(input)){
fscanf(input,"%c",&letter);
putchar(letter);
}
and remember to close file using fclose(input);

fwrite() appends instead of write C

I have to write a program witch reads from a file received by line and then it overwrites it with the read words uppercased.
This is my code
void toUpperCase(char* string) {
int i=0;
while(string[i])
{
string[i]=toupper(string[i]);
i++;
} }
int main(int argc, char** argv) {
if(argc==1)
{
puts("Error: INSERT PATH");
exit(0);
}
char* file=argv[1];
FILE* fd=fopen(file,"r+");
if(fd<0)
{
perror("Error opening file: ");
exit(0);
}
char buffer[30][30];
int i=0;
while(!feof(fd))
{
fscanf(fd,"%s",buffer[i]);
i++;
}
int j=0;
for(j=0; j<i; j++)
{
toUpperCase(buffer[j]);
fwrite(buffer[j],strlen(buffer[j]),1,fd);
}
fclose(fd);
return 0; }
but this program appends the words contained in buffer[][] instead of overwriting the file.
If the file contain was something like pippo pluto foo then, after the execution is pippo pluto fooPIPPOPLUTOFOO instead of PIPPO PLUTO FOO.
Where am i wrong? Thank you
You have to reset the file position indicator using fseek, as fscanf will advance it. Something like
fseek(fd, length_of_read_string, SEEK_CUR);
This allows you to read the file in chunks, but it will be tricky to get right. Or of course reset it to the file start because you read everything in 1 go:
fseek(fd, 0L, SEEK_SET);
I strongly recommend writing the modified data into a new file, and then after the program has run, delete the initial file and rename the new one. That will also take care of another issue with your program, you are reading the entire file into memory before handling it.
If you want to do in-place translation that doesn't change lengths, you can open the source file in two streams and then do read-chunk, write-chunk in lockstep. That has the advantage of being super-easy to convert to a non-in-place version that will work with nonseekable files too (stdin/stdout, pipes, and sockets).
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <ctype.h> //toupper
inline void upcaseStr(char* str){
for(;*str;str++) { *str=toupper(*str); }
}
int upcaseStream(FILE* in, FILE* out){
char buf[BUFSIZ]; //BUFSIZ is an implementation-defined constant for an optimal buffer size
while(fgets(buf, BUFSIZ, in)){
upcaseStr(buf);
if(fputs(buf, out) == EOF){ return 1; }
}
if(!feof){ return 1; }
return 0;
}
int main(int argc, char **argv)
{
//default in and out
FILE* in = stdin;
FILE* out = stdout;
if(argc == 2) {
in = fopen(argv[1], "r"); //for reading
out = fopen(argv[1], "r+"); //for writing (and reading) starting at the beginning
if(!(in && out)){
fprintf(stderr, "Error opening file %s for reading and writing: %s\n", argv[1], strerror(errno));
}
}
return upcaseStream(in, out);
}
If you do use the in-place version, then in the unlikely event that the if(fputs(buf, out) == EOF){ return 1; } line should return, you're screwed unless you have a backup copy of the file. :)
Note:
You shouldn't name your FILE pointers fd because C people will tend to think you mean "file descriptor". FILE is a struct around a file descriptor. A file descriptor is just an int that you can use for FILE access with the raw system calls. FILE streams are an abstraction layer on top of file descriptors--they aren't file descriptors.
As you read from the file, its internal position indicator gets moved. Once you start writing, you start writing from that position on, which happens to be at the end of the file. So you effectively append the data to the file.
Rewind the handle to reset the position indicator before writing into the file:
rewind(fp);
On a side note, you are reading the file incorrectly:
while(!feof(fd))
{
fscanf(fd,"%s",buffer[i]);
i++;
}
When you reach the end of the file, fscanf will return an error and not read anything, yet you still increment variable i, as if the read was successful. And then you check feof() for end-of-file, but i was already incremented.
Check feof() and return of fscanf() immediately after calling fscanf():
while(1)
{
int read = fscanf(fd,"%s",buffer[i]);
if( read != 1 )
//handle invalid read
if( feof(fd) )
break;
i++;
}
Think about what happens if the string is longer than 29 characters and/or the file contains more than 30 strings. char buffer[30][30];
Welcome to StackOverflow!
Reopening the stream with fopen with the "w" parameter:
fd=fopen(file, "w");
It opens the file and if there are any contents in the file, it clears them.

How to create an exact copy of file in c

I want to create an exact copy of a file(.bmp) in c
#include<stdio.h>
int main()
{
FILE *str,*cptr;
if((str=fopen("org.bmp","rb"))==NULL)
{
fprintf(stderr,"Cannot read file\n");
//return 1;
}
if((cptr=fopen("copy.bmp","wb"))==NULL)
{
fprintf(stderr,"Cannot open output file\n");
//return 1;
}
fseek(str, 0, SEEK_END);
long size=ftell(str);
printf("Size of FILE : %.2f MB \n",(float)size/1024/1024);
char b[2];
for(int i=0;i<size;i++)
{
fread(b,1,1,str);
fwrite(b,1,1,cptr);
}
fseek(cptr, 0, SEEK_END);
long csize=ftell(str);
printf("Size of created FILE : %.2f MB \n",(float)csize/1024/1024);
fclose(str);
fclose(cptr);
return 0;
}
Although it creates a file of the same size but windows throws an error while trying to view the newly created copy of bitmap.
Why is this happening?
You have moved the file pointer for the input file to the end of the file before you start reading it. You need to restore it to the beginning.
Change:
fseek(str, 0, SEEK_END);
long size=ftell(str);
to:
fseek(str, 0, SEEK_BEGIN);
long size=ftell(str);
fseek(str, 0, SEEK_SET);
Note that your code is devoid of error checking - if you had at least checked the result of fread then your mistake would have been immediately apparent. Take-home message: don't cut corners when it comes to error-checking - it will pay dividends later.
You need to seek back to the start of the original file because you are continually reading at the EOF and therefore not making a copy of the file contents, just whatever happens to be in your b[] array.
You are not checking the return codes of fread() and fwrite(). If you had been doing that you might have solved this problem from the return codes.
If you check the size of the original file and the copy in bytes, it should tell you the issue.
This code reads a byte and writes a byte.
#include<stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define KB 1024
int main()
{
unsigned int ifd,ofd,rcnt;
char buf[KB];
ifd=open("orig.jpg",O_RDONLY);
if(ifd==0)
{
fprintf(stderr,"Cannot read file\n");
//return 1;
}
ofd=open("copy.jpg",O_WRONLY|O_CREAT);
if(ofd==0)
{
fprintf(stderr,"Cannot open output file\n");
//return 1;
}
while(rcnt=read(ifd,buf,KB))
write(ofd,buf,rcnt);
}
~
This is a nice function to copy files! Copy char by char is better than read the whole file because that result (if the file is to long) is a bufferoverflow!
double copy(char *input, char *output) {
FILE *f_in = fopen(input, "r");
FILE *f_out = fopen(output, "a");
if (!f_in || !f_out) {
fclose(f_in);
fclose(f_out);
return -1;
}
int c;
while ((c = fgetc(f_in)) != EOF)
fputc(c, f_out);
fclose(f_in);
fseek(f_out, 0, SEEK_END);
long size = ftell(f_out);
fclose(f_out);
return (double)(size / 1024 / 1024); // MB
}
This function returns the MB of the output file. If it wasn't successfully it return 0.
Use this function like this:
double output;
if ((output = copy("What ever you want to copy", "Where ever it should be printed")) != -1)
printf("Size of file: %lf MB.\n", output);
Hope this will help :)
I copped your first code and also used first solution just you need to add this code to your program :fseek(str, 0, SEEK_SET);and done your copy bitmap will be produce.

How can i select the last line of a text file using C

I am trying to find out a way to select the last line of a text file using C (not c++ or c#, just C) and I am having a difficult time finding a way to do this, if anyone could assist me with this problem I would be very grateful, thanks! (btw for a good example of what i am trying to do, this would be similar what to tail -n 1 would be doing in bash)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void main(int argc, char *argv[])
{
FILE *fd; // File pointer
char filename[] = "./Makefile"; // file to read
char buff[1024];
if ((fd = fopen(filename, "r")) != NULL) // open file
{
fseek(fd, 0, SEEK_SET); // make sure start from 0
while(!feof(fd))
{
memset(buff, 0x00, 1024); // clean buffer
fscanf(fd, "%[^\n]\n", buff); // read file *prefer using fscanf
}
printf("Last Line :: %s\n", buff);
}
}
I'm using Linux.
CMIIW
No direct way, but my preferred method is:
Go to the end of the file
Read last X bytes
If they contain '\n' - you got your line - read from that offset to the end of the file
Read X bytes before them
back to 3 until match found
If reached the beginning of the file - the whole file is the last line
E.g.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifndef max
#define max(a, b) ((a)>(b))? (a) : (b)
#endif
long GetFileSize(FILE *fp){
long fsize = 0;
fseek(fp,0,SEEK_END);
fsize = ftell(fp);
fseek(fp,0,SEEK_SET);//reset stream position!!
return fsize;
}
char *lastline(char *filepath){
FILE *fp;
char buff[4096+1];
int size,i;
long fsize;
if(NULL==(fp=fopen(filepath, "r"))){
perror("file cannot open at lastline");
return NULL;
}
fsize= -1L*GetFileSize(fp);
if(size=fseek(fp, max(fsize, -4096L), SEEK_END)){
perror("cannot seek");
exit(1);
}
size=fread(buff, sizeof(char), 4096, fp);
fclose(fp);
buff[size] = '\0';
i=size-1;
if(buff[i]=='\n'){
buff[i] = '\0';
}
while(i >=0 && buff[i] != '\n')
--i;
++i;
return strdup(&buff[i]);
}
int main(void){
char *last;
last = lastline("data.txt");
printf("\"%s\"\n", last);
free(last);
return 0;
}
If you are using *nix operating system, you can use the command 'last'. See 'last' man page for details.
If you want integrate the functionality inside another program, you can use 'system' call to execute 'last' and get it's result.
A simple and inefficient way to do it is to read each line into a buffer.
When the last read gives you EOF, you have the last line in the buffer.
Binyamin Sharet's suggestion is more efficient, but just a bit harder to implement.

Resources