How can I correct this printing output issue? - c

For a homework assignment, I wrote some code in C to print out the permissions and file name of the files that are stored in an achive file. My code compiles, but for some reason, the name of the second file is returned in a new line.
Here is how my output looks like in terminal:
rw-r--r-- 501/20 1-s.txt
rw-r--r-- 501/20
2-s.txt
(empty blank line here)
This is how I want the output to look:
rw-r--r-- 501/20 1-s.txt
rw-r--r-- 501/20 2-s.txt
(no empty blank line here)
Can someone take a look at my code and give me some tips on how to fix my code? I suspect it's related to the way I read and select the file name (using my_ar.ar_name), but I can't figure out how to fix it. Thank you for your time.
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/utsname.h>
#include <ctype.h>
#include <string.h>
#include <ar.h>
int main (int argc, char **argv)
{
FILE *fp;
size_t readNum;
long long tot_file_size, cur_file_size;
struct stat fileStat;
struct ar_hdr my_ar;
//open the archive file (e.g., hw.a)
fp = fopen(argv[1], "r");
if (fp == NULL)
{
perror("Error opening the file\n");
exit(-1);
}
//size of the archive file
fseek(fp, 0, SEEK_END);
tot_file_size =ftell(fp);
rewind(fp);
//read data into struct
fseek(fp, strlen(ARMAG), SEEK_SET);
file_size_cnt = 0;
while (ftell(fp) < tot_file_size - 1)
{
readNum = fread(&my_ar, sizeof(my_ar), 1, fp);
if (stat(argv[1], &fileStat) == -1) {
perror("stat");
exit(EXIT_FAILURE);
}
if ((fileStat.st_mode & S_IFMT) == S_IFREG)
{
printf( (fileStat.st_mode & S_IRUSR) ? "r" : "-");
printf( (fileStat.st_mode & S_IWUSR) ? "w" : "-");
printf( (fileStat.st_mode & S_IXUSR) ? "x" : "-");
printf( (fileStat.st_mode & S_IRGRP) ? "r" : "-");
printf( (fileStat.st_mode & S_IWGRP) ? "w" : "-");
printf( (fileStat.st_mode & S_IXGRP) ? "x" : "-");
printf( (fileStat.st_mode & S_IROTH) ? "r" : "-");
printf( (fileStat.st_mode & S_IWOTH) ? "w" : "-");
printf( (fileStat.st_mode & S_IXOTH) ? "x" : "-");
printf("%.*s", 15, my_ar.ar_name);
printf("\n");
}
cur_file_size = atoll(my_ar.ar_size);
if (fseek(fp, cur_file_size, SEEK_CUR) != 0)
{
perror("You have an error.\n");
exit(-1);
}
}
//printf("\n");
fclose(fp);
return 0;
}

You don't want a new line to be printed? So don't print it!
Remove this:
printf("\n");
It is placed at the end of your code.
EDIT
I gauss you don't want to remove it completely, but you want to print it anytime except the last time.
if (ftell(fp) != tot_file_size - 1) {
printf("\n");
}
EDIT V1.1
I ran your code under my linux mint 13 after my improvment. This is my output:
rw-r--r--a.txt/
rw-r--r--b.txt/
This is the format you want, isn't it?

There are a number of issues.
OP has if (stat(argv[1], &fileStat) == -1) ... which is curious to repeatedly query the file status of the "file name of the files" rather than the file name in my_ar.ar_name. Thus the same file stats will appear.
The first use of printf("%.*s", 15, my_ar.ar_name) gave "501/20 1-s.txt". This looks like an unusual file name. The ar_name field is "If the file member name fits, the ar_name field contains the name directly, and is terminated by a slash (/) and padded with blanks on the right. If the member's name does not fit, ar_name contains a slash (/) followed by a decimal representation of the name's offset in the archive string table described below." So the presence of / implies you have more to do to extract the file name. It also may be that the fields are not NUL byte terminated. OP needs to read up on struct ar_hdr. Try: http://www.lehman.cuny.edu/cgi-bin/man-cgi?ar.h+3
The extracted filename, done errantly in this code, may or may not contain surprising bytes. I am certain this is why you saw an unexpected end-of-line in the 2nd line of output. Testing the filename for printable ASCII char is prudent. isprint()

Related

st_mode not returning expected values for S_IXXX comparison

I have a function that (ideally) would merely simulate a portion of the ls -l command. It is meant to return permissions for the file, and the name of the file. I don't really care about any other information. It also is not the cleanest, as I am stuck using the VIM editor, and it is significantly less user friendly than modern IDEs.
Code:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <fcntl.h>
void ls_l(const char *path){
DIR *dir;
struct dirent *file;
struct stat fileStat;
dir = opendir(path);
if(!dir) {
printf("opendir failed\n");
return;
}
while (((file = readdir(dir)) != NULL)){
char path2[255];
sprintf(path2, "%s/%s", path, file->d_name);
printf("%s\n", path2);
if(lstat(path2, &fileStat)) {
printf("lstat failed\n");
return;
}
//User permissions
printf( (fileStat.st_mode & S_IRUSR) ? " r" : " -");
printf( (fileStat.st_mode & S_IWUSR) ? "w" : "-");
printf( (fileStat.st_mode & S_IXUSR) ? "x" : "-");
//Group permissions
printf( (fileStat.st_mode & S_IRGRP) ? "r" : "-");
printf( (fileStat.st_mode & S_IWGRP) ? "w" : "-");
printf( (fileStat.st_mode & S_IXGRP) ? "x" : "-");
//Other permissions
printf( (fileStat.st_mode & S_IROTH) ? "r" : "-");
printf( (fileStat.st_mode & S_IWOTH) ? "w" : "-");
printf( (fileStat.st_mode & S_IXOTH) ? "x | " : "- | ");
}
}
void doStep(int stepNum){
chdir("dir0");
char cwd[PATH_MAX];
if(getcwd(cwd, sizeof(cwd)) != NULL){
printf("CWD: %s\n", cwd);
}
else{
printf("getcwd() error");
}
switch (stepNum){
case 1:
printf("Display files and permissions for ./dir0\n");
ls_l(cwd);
break;
case 2:
printf("Change permission for file6.txt to -rwxrwx---\n");
chmod("./file6.txt", S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP |
S_IWGRP | S_IXGRP);
break;
case 3:
printf("Remove file1.txt and file2.txt from directory dir1\n");
break;
case 4:
printf("Remove dir1 directory\n");
break;
case 5:
printf("Display files and permission for ./dir0\n");
break;
}
}
int main(){
int i;
for (i = 1; i < 2; i++){
doStep(i);
}
return 0;
}
I am only through the user and group permissions and I already have noticed a serious problem with the code. For whatever reason, the group permissions fail to change with each file, and thus do not return the correct values. I am only aware of this because because changing the permissions of a file and checking the return with ls -l gives me different values that that of my code.
I am aware that this isn't a complete program, as I am not finished with it, but it can compile and run on my school UNIX server, so it should suffice for any testing.
I would also like to not that case 2 never functioned before I noticed this error, so if you'd like to help with that as well I would appreciate it.
Edit: I use this to compile:
'''
gcc task13prog.c -o task13prog
'''
File structure is /dir0, which contains directories /dir1 and /dir2, a regular file called file6.txt, and a symbolic link called link5. I am more or less entirely concerned with this file structure, and only this file structure.
Output as requested by Allan Wind
Slightly edited to remove username data
{cslinux1:~/cs3377/project2} pwd
/home/013/t/tm/***/cs3377/project2
{cslinux1:~/cs3377/project2} ./task13prog
CWD: /home/013/t/tm/***/cs3377/project2/dir0
Display files and permissions for ./dir0
/home/013/t/tm/***/cs3377/project2/dir0/.
rwx--x--x | /home/013/t/tm/***/cs3377/project2/dir0/..
rwx--x--x | /home/013/t/tm/***/cs3377/project2/dir0/link5
rwxrwxrwx | /home/013/t/tm/***/cs3377/project2/dir0/dir2
rwx--x--x | /home/013/t/tm/***/cs3377/project2/dir0/file6.txt
rwxrwxrwx | /home/013/t/tm/***/cs3377/project2/dir0/dir1
rwx--x--x | {cslinux1:~/cs3377/project2} ls -l dir0
total 112
drwx--x--x 2 *** sn 54 Apr 3 18:38 dir1
drwx--x--x 2 *** sn 54 Apr 3 18:38 dir2
-rwxrwxrwx 1 *** sn 0 Apr 3 18:38 file6.txt
lrwxrwxrwx 1 *** sn 14 Apr 3 18:38 link5 -> dir2/file4.txt
{cslinux1:~/cs3377/project2}
Final edit:
The solution marked as correct was in fact, correct. Upon testing on a fresh linux system the solution functioned as expected. It appears that there was an error in the original environment that prevented me from making changes to permissions and reading permissions accurately.
I modified your program to pass in the path, checked return values of the functions you call and implemented the suggestion offered up by #MarkPlotnick which is the root cause of your trouble:
#include <limits.h>
void ls_l(const char *path){
DIR *dir;
struct dirent *file;
struct stat fileStat;
dir = opendir(path);
if(!dir) {
printf("opendir failed\n");
return;
}
while (((file = readdir(dir)) != NULL)){
char path2[PATH_MAX];
// if path contains a trailing /, you will end up with //
// in path2 which is ugly but works fine. You could special case that,
// and also special case ./ not being copied into path2
snprintf(path2, PATH_MAX, "%s/%s", path, file->d_name);
printf("%s\n", path2);
if(lstat(path2, &fileStat)) {
printf("lstat failed\n");
return;
}
...
As for step 2, it works for me, so you just need to ensure that file6.txt is in the dir0/ directory.

problem scanning and scanning data from file

I'm having some trouble in extracting data from a file, I don't understand what's wrong, it works fine for single digit integers, but when the input is double digit, it doesn't scan the integers. Everything else is working.
Suppose the input is:
abc de,1,2,y
then the output is:
"abc de" 1 2 y
but when the input is
cde abc,21.31,y
the scan fails on %d:
fin1 = fscanf(fp1, "%20[^,]%*c%d %*c %d%*c %c%*c", name1, &code1, &season, &relevant);
Help would be nice. Also some input as you asked:
Input:
The Universe,2,3,Y|Zoo,7,3,N|The Hobbit,10,2,Y|True Lies,12,25,N|Animals,22,2,Y| Euphoria,35,5,Y
Code:
FILE *fp1, *fserie;
char file1name[256], name1[21], relevant = 0, active = 0;
int code1, seasons, fin = 0,
while (1) {
puts("Enter First File Name:");
scanf("%s", file1name);
if (!(fp1 = fopen(file1name, "r")))
{
printf("error in opening file %s !!!\n", file1name);
continue;
} else break;
}
if (!(fserie = fopen("series.txt", "w")))
{
fclose(fp1);
exit(1);
}
do
{
fin1 = fscanf(fp1,"%20[^,]%*c %d%*c %d%*c %c%*c", name1, &code1, &season, &active1);
if (relevant == 'y')
fprintf(fserie,"%s,%d,%d\n", name1, code1, season);
} while (fin1 != EOF && fin2 != EOF);
The problem you are having is your format string and your failure to validate the read against all 4 successful conversions per iteration. For your format string you can use:
" %20[^,],%d,%d,%c|"
(the leading space is optional, but required if you have space following the '|' before the next name. Also note, if you have the potential for space surrounding the final active character, you can handle that by including a space in your format string to consume any-and-all whitespace, e.g. " %20[^,],%d,%d, %c |")
An example reading a line with multiple sets of movie information could be:
#include <stdio.h>
#define MAXN 21 /* if you need a constant, #define one (or more) */
int main (int argc, char **argv) {
char name[MAXN], active;
int code, seasons;
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
while (fscanf (fp, " %20[^,],%d,%d,%c|",
name, &code, &seasons, &active) == 4) {
printf ("\nname : %s\ncode : %d\nseason : %d\nactive : %c\n",
name, code, seasons, active);
}
if (fp != stdin) /* close file if not stdin */
fclose (fp);
}
Example Input File
$ cat dat/movies.txt
The Universe,2,3,Y|Zoo,7,3,N|The Hobbit,10,2,Y|True Lies,12,25,N|Animals,22,2,Y| Euphoria,35,5,Y
Example Use/Output
$ ./bin/readmovies dat/movies.txt
name : The Universe
code : 2
season : 3
active : Y
name : Zoo
code : 7
season : 3
active : N
name : The Hobbit
code : 10
season : 2
active : Y
name : True Lies
code : 12
season : 25
active : N
name : Animals
code : 22
season : 2
active : Y
name : Euphoria
code : 35
season : 5
active : Y
Look things over and let me know if you have further questions. There is more than one way to do this, but this is the closest to your original.

If a certain word exists in the .txt file, copy that word to another txt file

TODO: If a certain word exists in the .txt file, copy that word to another txt file
PROBLEM: It won't write the word after it is found in "from.txt" to "to.txt".
ERROR:
This line: while ((fscanf(ifp, "%s", line)) != EOF)
CODE:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h>
#define MAX_LINE 256
void main()
{
FILE *ifp;
FILE *ofp;
char line[MAX_LINE];
char word[MAX_LINE];
if ((ifp = open("from.txt", "r")) == NULL)
{
printf("Can't open input file.");
exit(1);
}
if ((ofp = open("to.txt", "w")) == NULL)
{
printf("Can't open output file.");
exit(1);
}
printf("Enter your word: ");
gets(word);
while ((fscanf(ifp, "%s", line)) != EOF)
{
if (strcmp(line, word) == 0)
{
fputs(line, ofp);
break;
}
}
fclose(ifp);
fclose(ofp);
getch();
}
You are using a wrong API for opening a file. API which you use -- open -- is for low-level, descriptor based access. You'll get an int value out of it, and ifp and ofp won't be correct.
You must use a stream based API, called fopen. It returns a pointer to the FILE structure, which in turn you can pass to fscanf() etc.
Very important: compile this program with all compiler warnings and observe the output. I'm pretty sure you're getting a log of warning messages from the compiler already.
PROBLEM: It won't write the word after it is found in "from.txt" to "to.txt".
As noted in comments and other answers, and for other reasons, open() may not be the best choice for writing strictly ANSI portable code.
But this is not the reason for the stated problem.
The function strcmp(...) is not doing what is needed.
In this line:
if (strcmp(line, word) == 0)
A single word is being compared with the entire line. And the single word is never identified. Even if the line in the file appears to have only a single word, white space, such as a space, tab or new line character ( " ". \n, \t) would cause the two arguments of strcmp to be unequal.
strcmp(string1, string2) possible return values are:
Positive integer when string1 is greater than string2
Zero when string1 is equal to string2
Negative integer when string1 is less than string2
The function strstr would be a better fit. Change the strcmp line to use strstr :
if (strstr(line, word)){...
strstr(...) looks for the existence of a sub-string within a string. And, with the other changes that have been discussed, made your code do as you described it should.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h>
#define MAX_LINE 256
void main()
{
FILE *ifp;
FILE *ofp;
char line[MAX_LINE];
char word[MAX_LINE];
//**************************************************************** it's fopen not open ***********************************************************
if ((ifp = fopen("from.txt", "r")) == NULL)
{
printf("Can't open input file.");
exit(1);
}
if ((ofp = fopen("to.txt", "w")) == NULL)
{
printf("Can't open output file.");
exit(1);
}
printf("Enter your word: ");
gets(word);
while ((fscanf(ifp, "%s", line)) != EOF)
{
if (strcmp(line, word) == 0)
{
fputs(line, ofp);
break;
}
}
fclose(ifp);
fclose(ofp);
getch();
}
its working fine ...

how to handle `%` if it is present in a string while reading it using `fscanf()`

Let's say there is a file a.txt in which each string is a key values pair as <key: value>. But one constraint is that my key could contains character like % too. For example: as given below
string : INDIA
integer : 2015
ratio %: 20
integer2 : 2016
Now by using the fscanf, I want to validate each value of string present in file a.txt.
My sample code is as follows:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
char str[8];
int arr[2];
FILE * fp;
int j=0;
char *out_format[4] = {
"string :",
"integer :",
"ratio %:",
"integer2 :"
};
fp = fopen ("a.txt", "r");
if (fp == NULL) {
perror( "fopen failed for input file\n" );
return -1;
}
for (j=0; j < 4; j++) {
char c[64]={'\0'};
strcat(c, out_format[j]);
if (j == 0) {
strcat(c, " %s ");
fscanf(fp, c, str);
printf("%s %s\n", c, str);
}
else {
strcat(c, " %d ");
fscanf(fp, c, &arr[j-1]);
printf("%s %d\n",c, arr[j-1]);
}
}
}
OUTPUT I received after compilation is:
string : %s INDIA
integer : %ld 2015
ratio %: %ld 0
integer2 : %ld xxxxx // some garbage
This is happening due to % present in line ratio %: 20 of file a.txt.
Please, can someone suggest here ? How to handle this, so that I can get the correct values as present in file?
You can use %% to eascape and match a %. From the scanf man page :
%
Matches a literal '%'. That is, '%%' in the format
string
matches a single input `%' character. No conversion is done,
and assignment does not occur.
Manual: http://www.manpages.info/linux/scanf.3.html
Just use %% to replace %,%% is stand for literal % in C.

New at C and working with directories: Trouble opening files within subdirectories

I'm working with directories for the first time and am running into some difficulties. I wrote the following function to explore a directory, display the filesizes and permissions, then recurse on any subdirectories.
void exploreDIR (DIR* dir, char cwd[], int tab)
{
struct dirent* ent;
while ((ent = readdir(dir)) != NULL)
{
if(strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0)
continue;
int i;
i = 0;
while(i < tab)
{
printf(" ");
i = i + 1;
}
printf("%s ", ent->d_name);
FILE *file = fopen(ent->d_name, "r");
int filesize;
if(file==NULL)
{
printf("[ Could not open! ]\n");
continue;
}
struct stat st;
stat(ent->d_name, &st);
filesize = st.st_size;
if (st.st_mode & S_IFDIR)
{
printf ("(subdirectory) [");
}
else
{
printf("(%d bytes) [", filesize);
}
printf( (S_ISDIR(st.st_mode)) ? "d" : "-");
printf( (st.st_mode & S_IRUSR) ? "r" : "-");
printf( (st.st_mode & S_IWUSR) ? "w" : "-");
printf( (st.st_mode & S_IXUSR) ? "x" : "-");
printf( (st.st_mode & S_IRGRP) ? "r" : "-");
printf( (st.st_mode & S_IWGRP) ? "w" : "-");
printf( (st.st_mode & S_IXGRP) ? "x" : "-");
printf( (st.st_mode & S_IROTH) ? "r" : "-");
printf( (st.st_mode & S_IWOTH) ? "w" : "-");
printf( (st.st_mode & S_IXOTH) ? "x" : "-");
printf("]\n");
fclose(file);
if (st.st_mode & S_IFDIR)
{
char tempwd[1024];
strcpy(tempwd, cwd);
strcat(tempwd, "/");
strcat(tempwd, ent->d_name);
DIR* tempdir;
if ((tempdir = opendir (tempwd)) != NULL)
{
printf("%s", tempwd);
exploreDIR(tempdir, tempwd, tab + 1);
}
}
}
closedir(dir);
}
However the fopen function always returns null when the function recurses on subdirectories. I cannot figure this out for the life of me. For reference, this is the main:
int main(int argc, char** argv)
{
printf("\n");
DIR* dir;
char cwd[1024];
if (getcwd(cwd, sizeof(cwd)) != NULL)
{
if ((dir = opendir (cwd)) != NULL)
{
exploreDIR(dir, cwd, 0);
}
}
printf("\n");
return (EXIT_SUCCESS);
}
I am also somewhat concerned with my methodology. Is strcat() really the best way to explore subdirectories?
Thanks
When you open a sub-directory ./snails, you read file names from the directory with readdir() such as chewy-snails, but to stat() the files, you need to prefix the name with the sub-directory name: ./snails/chewy-snails.
The alternative, not to be undertaken lightly, is to consider using chdir() or (better) fchdir() to change directory to the relevant sub-directory. You can then stat() the names from readdir() without messing with the strings. However, getting back to where you start is a problem — especially if you follow a symlink to get there. This is where fchdir() scores.
If you're on a system with the necessary support, the *at() functions may help (fstatat() etc).
But the simplest solution is to not change directories and to prefix file names with the sub-directory name before trying to use stat() or
lstat() — that's 3 links to the same content.
You don't seem to be putting your cwd variable to use. You should be putting that at the start of each filename.
Also I like using snprintf instead of strcat for building names. It will limit itself to the buffer size that you pass it, so there is no risk of overflow.

Resources