My program opens a read-write file with the append flag to read from any position specified with lseek. But I'm getting some compilation warnings and I'd like you to help me remove them. What is the problem?
#include "lab.h"
#include <fcntl.h>
#define BUFFERSIZE 1024
int main(int argc, const char *argv[])
{
int fd;
char buffer[BUFFERSIZE];
char dx[] = "C is awesome";
if ((fd = open("test", O_APPEND)) < 0)
{
err_sys("open error");
}
if (write(fd, dx, sizeof(dx)) != sizeof(dx)) {
err_sys("write error");
}
if(lseek(fd, 0, SEEK_END) == -1)
{
err_sys("lseek error1");
}
if (read(fd, buffer, BUFFERSIZE) > 0)
{
puts(buffer);
}
if(write(fd, dx, sizeof(dx)) != sizeof(dx))
{
err_sys("write error1");
}
if (lseek(fd, 0, SEEK_CUR) == -1) /* EOF */
{
err_sys("lseek error2");
}
if (read(fd, buffer, BUFFERSIZE) > 0)
{
puts(buffer);
}
if (write(fd, dx, sizeof(dx)) != sizeof(dx))
{
err_sys("write error2");
}
if(lseek(fd, 0, SEEK_SET) == -1) /* the beginning of file */
{
err_sys("lseek error3");
}
if (read(fd, buffer, BUFFERSIZE) > 0)
{
puts(buffer);
}
if ((write(fd, dx, sizeof(dx))) != sizeof(dx))
{
err_sys("write error3");
}
close(fd);
return 0;
}
header:
#ifndef __LAB_H__
#define __LAB_H__
#include <sys/types.h> /* required for some of our prototypes */
#include <stdio.h> /* for convenience */
#include <stdlib.h> /* for convenience */
#include <string.h> /* for convenience */
#include <unistd.h> /* for convenience */
#define MAXLINE 4096 /* max line length */
#define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
/* default file access permissions for new files */
#define DIR_MODE (FILE_MODE | S_IXUSR | S_IXGRP | S_IXOTH)
/* default permissions for new directories */
/* prototypes for our own functions */
char *path_alloc(int *); /* {Prog pathalloc} */
int open_max(void); /* {Prog openmax} */
void clr_fl(int, int); /* {Prog setfl} */
void set_fl(int, int); /* {Prog setfl} */
void err_dump(const char *, ...); /* {App misc_source} */
void err_msg(const char *, ...);
void err_quit(const char *, ...);
void err_ret(const char *, ...);
void err_sys(const char *, ...);
#endif /* __LAB_H__ */
warnings receive:
gcc -Wall -Wextra -O -g -D_FORTIFY_SOURCE=2 -I../exemple/ append.c ../exemple/liblab.a -o append
append.c: In function ‘main’:
append.c:6:14: warning: unused parameter ‘argc’ [-Wunused-parameter]
6 | int main(int argc, const char *argv[])
| ~~~~^~~~
append.c:6:32: warning: unused parameter ‘argv’ [-Wunused-parameter]
6 | int main(int argc, const char *argv[])
| ~~~~~~~~~~~~^~~~~~
-Wall -Wextra is a very high warning level to use, and some of the sub-options it includes produce warnings that are not helpful in all situations. It is okay to compile without -Wextra or to turn off specific options as by using -Wno-unused-parameter to turn off the unused-parameter warning.
Often, any parameter declared in a function prototype is needed to perform the function’s purpose. So, if the parameter does not appear anywhere in the function definition, it could be a sign that some mistake has been made. This is not always true. For example, a program could have a class of functions that all have the same parameters because they are used in some common way, but some of the functions in the class do not need all the parameters. So it is not always a mistake not to use all parameters.
In addition to turning off the specific warning, you can tell the compiler the parameters are deliberately unused by inserting these lines in your main routine:
(void) argc;
(void) argv;
These are expressions that use the parameters argc and argv, but the void cast tells the compiler to discard the values of the expressions. It tells the compiler you are deliberately ignoring these values, and then the compiler will not warn that the parameters are not used.
Because main is a special function, it may be declared as int main(void) or int main(int argc, char *argv[]) (or equivalent). So another solution is to use the alternate declaration for main, int main(void). When this warning occurs with ordinary functions, an option might be to remove the unused parameter from the declaration of the function, as long as the function does not need to have that parameter for some other reason.
You aren't using the argc and argv variables and when you compile the solution, the compiler displays a warning for each of these.
Change the signature of int main(int argc, const char *argv[]) to plain int main() and it should compile without warnings.
Related
There is a process (not process that I wrote) in Arm Linux that write the stdout for /dev/console that unaccessible to me.
How can I redirect that stdout to file ,so I can watch this process output?
Of course I have root on this Arm Linux and I can compile code .
I would write a minimal dynamic library that interposes open(), handling open("/dev/console", ...) separately. Then, run the other binary with LD_PRELOAD environment variable set to point to that library.
At minimum, something like the following libnoconsole.c should work:
#define _POSIX_C_SOURCE 200809L
#define _GNU_SOURCE
#include <unistd.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dlfcn.h>
#include <errno.h>
#ifndef OLD_PATH
#define OLD_PATH "/dev/console"
#endif
#ifndef NEW_PATH
#define NEW_PATH "/tmp/log"
#endif
static int (*original_open)(const char *, int, ...) = NULL;
static int (*original_openat)(int, const char *, int, ...) = NULL;
static int match(const char *a, const char *b)
{
while (*a == *b)
if (!*a) {
return 1;
} else {
a++;
b++;
}
return 0;
}
int open(const char *pathname, int flags, ...)
{
if (!original_open)
original_open = dlsym(RTLD_NEXT, "open");
if (!original_open) {
errno = ENOSYS;
return -1;
}
if (match(pathname, OLD_PATH))
return original_open(NEW_PATH, O_RDWR | O_CREAT, 0600);
if (flags & (O_CREAT | O_TMPFILE)) {
va_list args;
mode_t mode;
va_start(args, flags);
mode = va_arg(args, mode_t);
va_end(args);
return original_open(pathname, flags, mode);
} else
return original_open(pathname, flags);
}
int openat(int dirfd, const char *pathname, int flags, ...)
{
if (!original_openat)
original_openat = dlsym(RTLD_NEXT, "openat");
if (!original_openat) {
errno = ENOSYS;
return -1;
}
if (match(pathname, OLD_PATH))
return original_openat(AT_FDCWD, NEW_PATH, O_RDWR | O_CREAT, 0600);
if (flags & (O_CREAT | O_TMPFILE)) {
va_list args;
mode_t mode;
va_start(args, flags);
mode = va_arg(args, mode_t);
va_end(args);
return original_openat(dirfd, pathname, flags, mode);
} else
return original_openat(dirfd, pathname, flags);
}
Compile it using
gcc -Wall -O2 -c libnoconsole.c
gcc -Wall -O2 -shared -Wl,-soname,libnoconsole.so libnoconsole.o -ldl -o libnoconsole.so
and run your program via
env LD_PRELOAD=$PWD/libnoconsole.so the-other-program
and it should save its output to /tmp/log instead of /dev/console.
The reason for above using the silly match() instead of !strcmp() is that strcmp() wasn't always an async-signal safe function, whereas open() and openat() are: it's a small wart to avoid potential issues.
I use this code for retarget printf(), but it does not work
#ifdef __GNUC__
/* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf
set to 'Yes') calls __io_putchar() */
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */
PUTCHAR_PROTOTYPE
{
/* Place your implementation of fputc here */
/* e.g. write a character to the LCD */
lcd_Data_Write((u8)ch);
return ch;
}
I use STM32F103RBT6
compiler : GCC with emBitz editor
Try hijacking the _write function like so:
#define STDOUT_FILENO 1
#define STDERR_FILENO 2
int _write(int file, char *ptr, int len)
{
switch (file)
{
case STDOUT_FILENO: /*stdout*/
// Send the string somewhere
break;
case STDERR_FILENO: /* stderr */
// Send the string somewhere
break;
default:
return -1;
}
return len;
}
As an alternative, you could write your own printf() function using, Variable Argument Functions (va_list).
With va_list a custom print function looks like the following:
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
void vprint(const char *fmt, va_list argp)
{
char string[200];
if(0 < vsprintf(string,fmt,argp)) // build string
{
HAL_UART_Transmit(&huart1, (uint8_t*)string, strlen(string), 0xffffff); // send message via UART
}
}
void my_printf(const char *fmt, ...) // custom printf() function
{
va_list argp;
va_start(argp, fmt);
vprint(target, fmt, argp);
va_end(argp);
}
Usage example:
uint16_t year = 2016;
uint8_t month = 10;
uint8_t day = 02;
char* date = "date";
// "Today's date: 2015-12-18"
my_printf("Today's %s: %d-%d-%d\r\n", date, year, month, day);
Note that while this solution gives you convenient function to use, it is slower than sending raw data or using even sprintf(). I have used this solution both on AVR and on STM32 microcontrollers.
You could further modify the vprint like this, where periphery_t is a simple enum type:
void vprint(periphery_t target, const char *fmt, va_list argp)
{
char string[200];
if(0 < vsprintf(string,fmt,argp))
{
switch(target)
{
case PC: PC_send_str(string);
break;
case GSM: GSM_send_str(string);
break;
case LCD: LCD_print_str(string);
break;
default: LCD_print_str(string);
break;
}
}
}
Tank you Bence Kaulics
I use tinyprintf library and it worked quite well : github link
just make sure to add the following in the init code:
// Turn off buffers, so I/O occurs immediately
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
setvbuf(stderr, NULL, _IONBF, 0);
Why do I keep getting a set fault when I try and pass -H in the command line as a flag? -h (help) is working perfectly but -H(header) messes up every single time.
I have a main function as well which calls parse_command_line by passing the argc & argc.
the bool is defined as bool header = false;
the file is char** file = NULL;
and the reason I have the file+=1; in the code is so that it compiles because I am using a makefile that changes all warnings into errors.
#include "parse.h" /* prototypes for exported functions */
#include "../main/unused.h"
#include <stdlib.h>
#include <getopt.h>
#include <stdio.h>
#include <stdint.h>
int
parse_command_line (int argc, char **argv, bool *header, char **file)
{
int oc = 0;
file += 1;
bool help = false;
struct option long_options[] =
{
{"header", no_argument, NULL, 'H'},
{"help", no_argument, NULL, 'h'},
{0, 0, 0, 0}
};
while ((oc = getopt_long(argc, argv, "+Hh", long_options, NULL)) != -1)
{
printf("The value of oc = %d\n", oc);
switch(oc)
{
case 'h':
help = true;
break;
case 'H':
printf("inside case H");
*header = true;
break;
case '?':
fprintf(stderr, "Unknown flag = -%c, type -h or --help for help!\n", optopt);
exit(EXIT_FAILURE);
break;
default:
break;
}
}
printf("Out of loop"); if (optind+1 != argc)
{
fprintf(stderr, "Uh oh, invalid input! Try again with -h or --help for help!\n");
exit(EXIT_FAILURE);
}
if (help)
{
printf("\nHaving some trouble? Let me show you the ropes!\n\n");
printf("Format: ydi <option(s)> mini-elf-file\n\n");
printf("Here's your options:\n");
printf("-h --help Display usage\n");
printf("-H --header Show the Mini-Elf header\n");
exit(1);
}
if (header)
{
printf("Inside HEader");
FILE *file;
uint16_t nums[6];
file = fopen(argv[optind], "r");
#define STRUCT_ITEMS 7
fread(nums, 16, 6, file);
int cur_print;
for (cur_print = 0; cur_print < STRUCT_ITEMS; cur_print++)
{
printf("%d ", nums[cur_print]);
}
}
return 0;
}
My parse.h file is as follows:
#ifndef __PARSE_COMMAND_LINE__
#define __PARSE_COMMAND_LINE__
#include <stdbool.h>
int parse_command_line (int argc, char **argv, bool *header, char **file);
#endif
There are other files such as elf.h and elf.c which I have not implemented and are not called at all at this point, which leads me to believe they are not going to be the problem and don't need to post the small 2 line files. My main function is as follows:
#include <stdio.h> /* standard I/O */
#include <stdlib.h>
#include "unused.h" /* UNUSED macro */
#include "../cmdline/parse.h" /* command line parser */
#include "../y86/elf.h" /* Mini-ELF format */
int
main (int argc UNUSED, char **argv UNUSED)
{
printf ("Congratulations, you have compiled your source code!\n");
bool header = false;
char **file = NULL;
parse_command_line (argc, argv, &header, file);
return 0;
}
And the file unused.h (because the compiler will make unused variables an error instead of warning) is as follows:
#ifndef __UNUSED__
#define __UNUSED__
#define UNUSED __attribute__ ((unused))
#endif
The code doesn't check the return value of fopen, which will be NULL in case of an error. Dereferencing NULL in the fread call causes a segfault.
So I want to make a file named genData.c that when executed for example: ./genData filename.txt will write 1 character to that file 1000 times.
In essence creating a 1kb file.
I would like to be able to modify the for loop, say 100000 times, to generate a 1MB file and so on.
Here is what I have tried and it compiles but when executed causes a segmentation fault.
Any suggestions? Sorry C is a language I've never dabbled in.
#include <stdio.h>
int main (int argc, char *argv) {
char ch = 'A';
FILE *fp;
fp = fopen(argv[1], "wb");
int i;
for (i = 0; i < 1000; i++) {
fwrite(&ch, sizeof(char), 1, fp);
}
fclose(fp);
return 0;
}
If you compile with warnings, you get a hint as to the exact problem:
test.c:3:5: warning: second argument of ‘main’ should be ‘char **’ [-Wmain]
int main (int argc, char *argv) {
^
All your troubles start downstream of this error. Fix this argument, and your code will work.
In the future, get into the habit of compiling with warnings turned on:
$ gcc -Wall foo.c
...
This will help catch typos and other oddities that will cause problems.
Since you tagged it Linux, this is how you can do it with the system-level functions (this should be a correct, most efficient way to do it):
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sysexits.h>
#include <fcntl.h>
#include <string.h>
ssize_t /* Write "n" bytes to a descriptor */
writen(int fd, const void *ptr, size_t n);
int
main (int argc, char** argv) {
char buf[1000];
memset(buf, 'A', sizeof(buf));
int fd;
if((fd = open(argv[1], O_WRONLY|O_CREAT, 0666))<0){
perror(argv[1]);
exit(EX_NOPERM);
}
ssize_t left = writen(fd, buf, sizeof(buf));
if(left)
perror("write error\n");
return !!left;
}
ssize_t /* Write "n" bytes to a descriptor */
writen(int fd, const void *ptr, size_t n) {
size_t nleft;
ssize_t nwritten;
nleft = n;
while (nleft > 0) {
if ((nwritten = write(fd, ptr, nleft)) < 0) {
if (nleft == n)
return(-1); /* error, return -1 */
else
break; /* error, return amount written so far */
} else if (nwritten == 0) {
break;
}
nleft -= nwritten;
ptr += nwritten;
}
return(n - nleft); /* return >= 0 */
}
#include <stdio.h>
#include <stdlib.h>
#define SIZE_OF_FILE 1024
int main(int argc, char *argv[])
{
FILE *fdest;
char ch = '\n';
if(argc != 2)
exit(EXIT_FAILURE);
fdest = fopen(argv[1], "wb");
if (fdest == NULL)
exit(EXIT_FAILURE);
fseek(fdest, SIZE_OF_FILE - 1, SEEK_CUR);
fwrite(&ch, sizeof(char), 1, fdest);
fclose(fdest);
return 0;
}
In essence creating a 1kb file
if the only purpose is creating a file with sizeof x, it is more simple i belive.
Now I do have a hw question for everyone...I've been staring at this for a couple of days kind of tinkering and playing around but even with that I end up with a load of errors...
What I'm trying to do is take the program below and change it so that it takes an optional command line argument infile. If infile is given, then copy infile to standard output, otherwise copy standard input to standard output as before.
The trick about this is that the solution must use the original copy loop (lines 9-11) for both cases. One can only insert code, and not change any of the existing code. Any help would be great. Thanks.
/* $begin cpfile */
include "csapp.h"
int main(int argc, char **argv)
{
int n;
rio_t rio;
char buf[MAXLINE];
Rio_readinitb(&rio, STDIN_FILENO); //line 9
while((n = Rio_readlineb(&rio, buf, MAXLINE)) != 0) //line 10
Rio_writen(STDOUT_FILENO, buf, n); //line 11
/* $end cpfile */
exit(0);
/* $begin cpfile */
}
/* $end cpfile */
C programs get command line arguments through the two arguments to main(), traditionally called argc and argv (for argument count and argument vector, respectively).
Arguments are not "named" anything, they're just strings.
A solution for you could look like this:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, char *argv[])
{
int fileno;
/* ... your definitions should remain here, too */
if(argc > 1)
{
/* Assume first argument is filename, and open it. */
fileno = open(argv[1], O_RDONLY);
if(fileno < 0)
{
printf("Unable to open file, aborting\n");
return 1;
}
}
else
fileno = STDIN_FILENO;
/* ... your code goes here ... */
}
Then you'd of course need to change the call to Rio_readinitb() to use the fileno variable for the file descriptor.
If you literally can't change that line, for whatever reason ... I guess you can use the preprocessor to make the symbol evaluate to the new variable name:
#undef STDIN_FILENO
#define STDIN_FILENO fileno
This is of course not exactly pretty, but should work.
Make sure you put those preprocessor macros after the fileno = STDIN_FILENO; line.
You can insert dup2 before the lines 9 - 11 and it seems that you will not need change code on the lines 9 - 11. This is an example.
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <fcntl.h>
int main(int argc, char *argv[])
{
int file_handle;
int dup2_res;
if (argc == 2) {
file_handle = open(argv[1], O_RDONLY);
dup2_res = dup2 (file_handle, STDIN_FILENO);
}
char buffer[100];
ssize_t read_bytes = 1;
while (read_bytes)
{
read_bytes = read(STDIN_FILENO, &buffer, sizeof(buffer) );
buffer[read_bytes] = 0;
printf("%s", buffer);
}
close(file_handle);
return 0;
}
If STDIN_FILENO cannot be reassigned, it sounds like a task for freopen():
freopen(argv[1], "r", stdin);