I have coded this program in C that reads in a file and prints it out to a table that is aligned with vertical bars including a header and footprint. The file always has 3 columns but different amounts of rows. The columns will differ in the amount of characters in each. The problem that I am having is that the first file I read in aligns perfectly but when I read in others that are increased sizes in all of the columns it does not align. Is there a way to make it automatically align or do I have to do this manually every time?
Code:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
// constant to store the maximum size of the records that can be read from the file
enum
{
// update MAX_SIZE to if the number of rows is more than 1000
MAX_SIZE = 1024
};
// structure to store a record from the file
typedef struct
{
char define[20];
char octal[26];
char description[1000];
} Record;
// string to store the header of the table
char header1[20], header2[20], header3[20];
// function to read the file and return the maximum length of the descriptions read from the file
int readFile(Record rec[], int *size)
{
// open a file for input
FILE *fp = fopen("input2.txt", "r");
// check if the file opened successfully
if (fp == NULL)
{
printf("File could not be opened!");
exit(1);
}
int i = 0;
int maxLength=0;
// read the file
// first we read the header of each column from the file
fscanf(fp, "%s %s %s\n", header1, header2, header3);
// now we read each record from the file
while (fscanf(fp, "%19s ", rec[i].define) == 1)
{
fscanf(fp, "%10s '%[^']'", rec[i].octal, rec[i].description);
// find the length of the description and update the 'maxLength'
if (strlen(rec[i].description) > maxLength)
{
maxLength = strlen(rec[i].description);
}
// increment the value of i
i++;
}
// close the input file
fclose(fp);
// update the number of records read from the file
*size = i;
return maxLength;
}
void printTable(Record rec[], int size, int WIDTH)
{
char p='|';
// print the header of the table
for (int i = 0; i < 22 + WIDTH; i++)
printf("-");
printf("\n| %-7s %c %5s %c %-*s %c\n", header1, p, header2, p, WIDTH, header3,p);
for (int i = 0; i < 22 + WIDTH; i++)
printf("-");
printf("\n");
// print the table data
for (int i = 0; i < size; i++)
printf("| %-7s | %5s | %-*s |\n", rec[i].define, rec[i].octal, WIDTH, rec[i].description);
// print the footer
for (int i = 0; i < 22 + WIDTH; i++)
printf("-");
printf("\n");
}
// driver function
int main()
{
// create an array of recors of MAX_SIZE
Record recs[MAX_SIZE];
// initialize size of recs to zero
int size = 0;
// call readFile() function to read data from the file and update the size of recs
int WIDTH = readFile(recs, &size);
// call printTable() function to print the table in a well-formatted manner
printTable(recs, size, WIDTH);
return 0;
}
File:
#define octal description
O_APPEND 02000 'The file is opened in append mode.'
O_ASYNC 020000 'Enable signal-driven I/O.'
O_CLOEXEC 02000000 'Enable the close-on-exec flag for the new file descriptor.'
O_CREAT 0100 'If pathname does not exist, create it as a regular file.'
O_DIRECT 040000 'Try to minimize cache effects of the I/O to and from this file.'
O_DIRECTORY 0200000 'If pathname is not a directory, cause the open to fail.'
O_DSYNC 010000 'Write operations will complete according to the requirements of synchronized I/O data integrity completion.'
O_EXCL 0200 'Ensure that this call creates the file.'
O_LARGEFILE 0 'Allow files whose sizes cannot be represented in an off_t to be opened.'
O_NOATIME 01000000 'Do not update the file last access time (st_atime in the inode) when the file is read(2).'
O_NOCTTY 0400 'If pathname refers to a terminal device it will not become the processs controlling terminal.'
O_NOFOLLOW 0400000 'If pathname is a symbolic link, then the open fails, with the error ELOOP.'
O_NONBLOCK 04000 'When possible, the file is opened in nonblocking mode.'
O_PATH 010000000 'Obtain a file descriptor that can be used perform operations that act purely at the file descriptor level.'
O_SYNC 04010000 'Write operations will complete according to the requirements of synchronized I/O file integrity completion.'
O_TMPFILE 020200000 'Create an unnamed temporary regular file.'
O_TRUNC 01000 'If the file already exists and is a regular file it will be truncated to length 0.'
What is currently being printed:
---------------------------------------------------------------------------------------------------------------------------------
| #define | octal | description |
---------------------------------------------------------------------------------------------------------------------------------
| O_APPEND | 02000 | The file is opened in append mode. |
| O_ASYNC | 020000 | Enable signal-driven I/O. |
| O_CLOEXEC | 02000000 | Enable the close-on-exec flag for the new file descriptor. |
| O_CREAT | 0100 | If pathname does not exist, create it as a regular file. |
| O_DIRECT | 040000 | Try to minimize cache effects of the I/O to and from this file. |
| O_DIRECTORY | 0200000 | If pathname is not a directory, cause the open to fail. |
| O_DSYNC | 010000 | Write operations will complete according to the requirements of synchronized I/O data integrity completion. |
| O_EXCL | 0200 | Ensure that this call creates the file. |
| O_LARGEFILE | 0 | Allow files whose sizes cannot be represented in an off_t to be opened. |
| O_NOATIME | 01000000 | Do not update the file last access time (st_atime in the inode) when the file is read(2). |
| O_NOCTTY | 0400 | If pathname refers to a terminal device it will not become the processs controlling terminal. |
| O_NOFOLLOW | 0400000 | If pathname is a symbolic link, then the open fails, with the error ELOOP. |
| O_NONBLOCK | 04000 | When possible, the file is opened in nonblocking mode. |
| O_PATH | 010000000 | Obtain a file descriptor that can be used perform operations that act purely at the file descriptor level. |
| O_SYNC | 04010000 | Write operations will complete according to the requirements of synchronized I/O file integrity completion. |
| O_TMPFILE | 020200000 | Create an unnamed temporary regular file. |
| O_TRUNC | 01000 | If the file already exists and is a regular file it will be truncated to length 0. |
---------------------------------------------------------------------------------------------------------------------------------
What it should look like:
-----------------------------------------------------------------------------------------------------------------------------------------
| #define | octal | description |
-----------------------------------------------------------------------------------------------------------------------------------------
| O_APPEND | 02000 | The file is opened in append mode. |
| O_ASYNC | 020000 | Enable signal-driven I/O. |
| O_CLOEXEC | 02000000 | Enable the close-on-exec flag for the new file descriptor. |
| O_CREAT | 0100 | If pathname does not exist, create it as a regular file. |
| O_DIRECT | 040000 | Try to minimize cache effects of the I/O to and from this file. |
| O_DIRECTORY | 0200000 | If pathname is not a directory, cause the open to fail. |
| O_DSYNC | 010000 | Write operations will complete according to the requirements of synchronized I/O data integrity completion. |
| O_EXCL | 0200 | Ensure that this call creates the file. |
| O_LARGEFILE | 0 | Allow files whose sizes cannot be represented in an off_t to be opened. |
| O_NOATIME | 01000000 | Do not update the file last access time (st_atime in the inode) when the file is read(2). |
| O_NOCTTY | 0400 | If pathname refers to a terminal device it will not become the processs controlling terminal. |
| O_NOFOLLOW | 0400000 | If pathname is a symbolic link, then the open fails, with the error ELOOP. |
| O_NONBLOCK | 04000 | When possible, the file is opened in nonblocking mode. |
| O_PATH | 010000000 | Obtain a file descriptor that can be used perform operations that act purely at the file descriptor level. |
| O_SYNC | 04010000 | Write operations will complete according to the requirements of synchronized I/O file integrity completion. |
| O_TMPFILE | 020200000 | Create an unnamed temporary regular file. |
| O_TRUNC | 01000 | If the file already exists and is a regular file it will be truncated to length 0. |
-----------------------------------------------------------------------------------------------------------------------------------------
Example function.
struct a
{
char *s ; int x; char *y;
} a[] = {
{"Hello", 435342435432, "1"},
{"dfgsfdgdgdfgdf", 5, "1fdsfsdfs"},
{"Hellodfds", 43534, "sdffs"},
{"H", 2435432, "1dfdsfdsfsdd"},
{NULL,}
};
void print(struct a *data)
{
int widths[3] = {0};
struct a *wrk;
size_t len;
for(wrk = data; wrk -> s; wrk++)
{
if((len = strlen(wrk -> s)) > widths[0]) widths[0] = len;
if((len = strlen(wrk -> y)) > widths[2]) widths[2] = len;
if((len = snprintf(NULL, 0, "%d", wrk -> x)) > widths[1]) widths[1] = len;
}
for(wrk = data; wrk -> s; wrk++)
{
printf("| %-*s | %*d | %*s |\n", widths[0], wrk -> s , widths[1], wrk -> x, widths[2], wrk -> y);
}
}
Output:
| Hello | 1550738536 | 1 |
| dfgsfdgdgdfgdf | 5 | 1fdsfsdfs |
| Hellodfds | 43534 | sdffs |
| H | 2435432 | 1dfdsfdsfsdd |
Consider a contrived example of accomplishing the same goal.
#include <stdio.h>
#include <string.h>
struct ContrivedData {
char a[100];
char b[100];
char c[100];
};
int main(void) {
struct ContrivedData cd1 = {"Hello", "world", "!"};
struct ContrivedData cd2 = {"foo", "bar", "baz"};
struct ContrivedData cd[2];
cd[0] = cd1;
cd[1] = cd2;
int cd_len = sizeof(cd) / sizeof(*cd);
int max_a_len = 0;
int max_b_len = 0;
int max_c_len = 0;
for (int i = 0; i < cd_len; i++) {
int a_len = strlen(cd[i].a);
if (a_len > max_a_len) {
max_a_len = a_len;
}
int b_len = strlen(cd[i].b);
if (b_len > max_b_len) {
max_b_len = b_len;
}
int c_len = strlen(cd[i].c);
if (c_len > max_c_len) {
max_c_len = c_len;
}
}
for (int i = 0; i < cd_len; i++) {
printf("%-*s | %-*s | %-*s\n",
max_a_len, cd[i].a,
max_b_len, cd[i].b,
max_c_len, cd[i].c);
}
return 0;
}
Before printing the data I iterate over the array and calculate the maximum width of each field. Depending on the type of data, you may need to use some function other than strlen.
The result I get is:
% ./test2
Hello | world | !
foo | bar | baz
If I change the data a bit, you can see that it still stays aligned.
% ./test2
Hello | world | !
foo | barbarbar | baz
The issue arises with fopen(), stating that permission is denied. I have tried to input all the modes however this was unsuccessful. Same when adding all the writing modes. I am attempting to open a file to with which i can write. Thanks to anyone who helps
if(filename != NULL && redirectArrow == true){
mode_t modes = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
int fd = open(filename, O_WRONLY | O_CREAT , S_IWUSR | S_IWGRP | S_IWOTH); //opening file in write only, and creating file if it does not exist
if(fd == -1){
perror("Failed to create and open the file\n");
exit(3);
}
fileDes= open(dbPathname, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
}
printf("File %s opened successfully with handle %d\n", dbPathname, fileDes );
if(-1 == fileDes)
{
PERS_ERROR( "pers_db_open_default: File %s opened Failed %d \n", dbPathname, fileDes );
return NULL; //return PERS_COM_FAILURE;
}
*db = fileDes;
printf("file info: FD=[%d], &FD=[%d], fileDes=[%d] \n", (*db),(db), fileDes );
if( 0 != fstat(fileDes, &sb))
{;
perror("fileInfo() fstat-perror= \n");
printf("\nfstat error: [%s] and st.st_mode is [%d]\n",strerror(errno), sb.st_mode);
return NULL;
}
output:-
fileInfo() fstat-perror=
: Invalid argument
Segmentation fault (core dumped)
Im making a shell and working on output redirection, specifically appending to files. Problem is, the following code always overwrites the contents of the file and does not simply append.
int val = -1;
//Flag issue?
val = open(destination, O_RDWR | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR);
if (val != -1) {
close(1);
dup(val);
close(val);
}
I don't know why redirection doesn't work in the shell I have written. Here's my code"
int i;
for (i=1; !args[i];i++)
{
if (args[i]== ">")
{
printf("argv[i] %s %d \n", args[i], i);
int out;
// out = open("out", O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IRGRP | S_IWGRP | S_IWUSR);
out=open("out", O_CREAT | O_WRONLY | O_TRUNC, S_IRWXU);
int fdl=dup2(out,1);
close(out);
execvp(args[0],args);
}
}
Also here's the error I receive :
mysh> ls
basic_shell basic_shell.c~ fork fork_2 fork_cp.c
basic_shell.c basic_shell_OK.c fork_1 fork.c
mysh> ls > file
ls: cannot access >: No such file or directory
ls: cannot access file: No such file or directory
Please let me know what's wrong?
If args is an array of char*, then this condition
if (args[i]== ">")
does not do what you think it does. It compares the pointers and not what they point to. To compare string you have to use strcmp.