[SOLVED] Tried to write something "serious" and have failed once again
ProgrammingThis forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
Distribution: Currently: OpenMandriva. Previously: openSUSE, PCLinuxOS, CentOS, among others over the years.
Posts: 3,881
Original Poster
Rep:
Quote:
Originally Posted by GazL
You also have to watch out for partial matches. In my version, rather than use strstr() I used strncasecmp() to find the match and then I checked that the next character in the config line was a space in order to avoid a partial match.
Thanks again GazL, yes that's the problem with my current code - it still uploads a line with a format_id that only partly matches the second arg specified on the command-line. When it should display an error and quit instead.
Quote:
I assume this is what you're intending to do?
[output snipped]
If you want to see my code ask me and I'll post it, but as this is a programming exercise you'll learn a lot through trial and error.
The only problem (that I'm aware of) with my code now is dealing with a quoted string in the conf file as I'm not sure it can be done with strtok() as you don't have visibility of the internal pointer it uses. Might be easier with something like strsep() but that's not a C standard function (if you care about that).
Yes, that's basically what I'm trying to do/what I want my program to do. Thanks again for your help.
I would like to say yes, post your code, but I do agree that more can likely be learnt if I try a few things beforehand. So I'll hold off on asking you to post it for now, but once I've gotten at least a slightly better idea on strncasecmp(), don't be surprised if I do ask you to.
Quote:
Originally Posted by rtmistler
The input file is just something which you read serially.
I'm not sure what you mean? Like I said before; the input file is only being read to check for it's existence. The fopen() call is not there for any other reason - that's why I've got fclose() to close it as soon as it's existence (or otherwise) is checked. It is NOT used for get any args/configuration for the program.
Quote:
If you'll have an input file syntax, then you can check, line by line that each line meets valid forms. Example: Starts with a specific range of keywords, or IS a word and does not include any punctuation or numbers/symbols. Stuff like that. If you see a bad first word in any line, you move to the next as soon as possible.
My intention is that, if the second argument (argv[1]) doesn't match any of the first strings in the config file (ffclifront.conf), then my program doesn't continue, then displays an error message, and then quits. As "mp3" should be a valid string (but not limited too).
Quote:
I see no point in checking the file more than once. I don't get why you wish to see one bad term and hope to exit fast. It's a computer, trust me, it's fast.
I've remove the second fopen() call to ffclifront.conf, like I said before, I overlooked that - not intentional. I'm not sure what exactly you mean by the second comment in the quote above. I'm only trying to do what I've said above, I'm not trying to say it isn't fast, nor trying to make anything "faster".
Quote:
Suggestion for your file, any line starting with #, treat it as a comment and skip parsing the line. Same point I'm making with this post, check the first word, if it's not a term you'd expect, skip parsing the line.
Yes, I was thinking about adding something so it ignores comments before, but again, I was focusing on getting it to parse the config file properly first, before trying anything else.
Anyhow I've tried the strncasecmp() function by itself in a little test program I wrote, to try and figure out how to use the strncasecmp() function. I've also tried to read it's man page, but as per usual, it isn't clear about how to actually use it. And I'm having serious problems understanding the following quote from it;
Quote:
It returns an integer less than, equal to, or greater than zero if s1 is found, respectively, to be less than, to match, or be greater than s2.
What in the hell does that mean in plain simple English?? I'm sorry but that makes zero sense to me. So in other words; does it return less than zero if the two strings match, and more than zero if they don't, or what???
I'm sorry, but this is a perfect example of trying to understand what the man page is saying, where it may as well be saying blah, blah, blah. Not understanding.
So I'd be grateful if someone could explain that/answer my question above. Thanks.
What in the hell does that mean in plain simple English?? I'm sorry but that makes zero sense to me. So in other words; does it return less than zero if the two strings match, and more than zero if they don't, or what???
It means exactly what it says. If the strings match, the return value is zero, otherwise non-zero. Ignore greater than or less than.
The ultimate intention of the input file is to use it and parse strings read from it, even if you're not doing that yet.
What I'm talking about are proper ways to do this. Whatever shortcut you feel you need to have a fast exit with error, seems to be ignoring that you'll have a whole file too parse. But I think you're trying to do something entirely different which makes little sense to me, and you don't understand what I'm describing. So I'll drop it. Please continue and if you have further questions about your code, please be clear.
I've been working under the assumption that input file is the file to be passed to the -i argument of ffmpeg as stated in post #1. Admittedly, this makes the program no more useful than a bash alias or even ffmpeg's own presets, which would both do the same job with a lot less effort, but this is just a programming exercise for jsb' so that doesn't matter.
The only problem (that I'm aware of) with my code now is dealing with a quoted string in the conf file as I'm not sure it can be done with strtok() as you don't have visibility of the internal pointer it uses. Might be easier with something like strsep() but that's not a C standard function (if you care about that).
...
I had a fun couple of evenings wrestling with this one. After trying a couple of different approaches I eventually came to the conclusion that handling embedded quotes correctly wasn't possible using strtok(). Rather than rewrite my program, I wrote my own implementation of strtok() that I called strtok_q(). It works exactly the same as strtok() except it ignores delimiters within the bounds of double-quotes.
At present, my strtok_q() will also strip a matched pair of outer quotes on a token, which is handy if you're going to pass that token as an element of an argv[], or maybe for parsing a line from a csv file, but I'm not entirely convinced whether it's actually a good idea or a misfeature. Hmm, perhaps it might not be a bad idea to have a strtok_q() and a strtok_qs(), one that doesn't strip and one that does. Cover all the bases.
Distribution: Currently: OpenMandriva. Previously: openSUSE, PCLinuxOS, CentOS, among others over the years.
Posts: 3,881
Original Poster
Rep:
Quote:
Originally Posted by GazL
I've been working under the assumption that input file is the file to be passed to the -i argument of ffmpeg as stated in post #1. Admittedly, this makes the program no more useful than a bash alias or even ffmpeg's own presets, which would both do the same job with a lot less effort, but this is just a programming exercise for jsb' so that doesn't matter.
Yes, that's exactly what the input file is for.
The reason I thought it would be a good idea to try and write this program was because I was hoping one day to write a graphical version of it that can batch convert a number of video or audio files using ffmpeg. So it's not much good trying to write something like that without understanding how to at least parse a config file for it.
Anyway, I had at look at some things for the strcasecmp() function, and it looks like it would solve the problem with my current code still finding only partial matches instead of only finding a "full/complete match". So that appears to be like it may well be the solution to that problem. But while I've written a little program below that uses it, and if I have two identical hardcoded strings in my s1 and s2 arrays, it displays the correct message (strings are equal), and if they are not equal, it also displays the correct message (strings are not equal); I cannot figure out how to incorporate it into my code for my program and replace strstr() with it.
I did also have a look at strsep(), but while from what I've read it's similar to strtok(); I really don't understand the examples I've seen of it.
Just so you guys know, and in case I don't respond for a little while; I've just had surgery yesterday, and they give me some pretty powerful pain medication, so it's effects can dope me right out. So it's hard to do much at all after taking it (I tried the strcasecmp() function and wrote my little program below the other day before I went into hospital - I'm out of it now, as it wasn't major surgery. It was to cut open an Abscesses and drain it).
Anyway, this is all I could really do as far as strcasecmp() is concerned;
Code:
#include <stdio.h>
#include <string.h>
int main (void) {
char s1[] = { 't', 'e', 's', 't', '\0' };
char s2[] = { 't', 'e', 's', '\0' };
int result = strcasecmp(s1, s2);
printf("s1 = %s\n", s1);
printf("s2 = %s\n", s2);
if ( result == 0 ) {
puts("Strings are equal");
}
else {
puts("Strings are not equal");
}
return 0;
}
Which should give;
Code:
[james@jamespc devel]$ ./strcasecmp_example
s1 = test
s2 = tes
Strings are not equal
Converting from using strstr() to strncasecmp() really isn't all that difficult. Once your mind is less drug and pain addled I'm sure a little thought will bring you to a working solution, but if you get completely stuck, we're here.
Best wishes from me too. But when you are a bit recovered, look at the stat() function. You use it to retrieve information about a file. But you can use it as a quick-and-dirty way to find out if the file exists which is less logically confusing than trying to open it. Something like this:
Code:
stat *infobuffer_pointer;
int return_code;
return_code = stat ("/path/to/file", infobuffer_pointer);
if (return_code == -1) //stat returned an error
{
if (errno == ENOENT) //error was "No such entity"
{
printf ("No such file as /path/to/file");
}
else
{
printf ("File exists but can't be opened");
}
exit(1); //no point in continuing!
}
Here we are not actually looking into the buffer that stat returns and that infobuffer_pointer points to, but in another program you might use the contents of that buffer to find out things like the ownership, size and access rights of the file.
This is typically why I avoid using the f'functions and instead use open(2), because if you do not specify O_CREAT and the named file does not exist, you will get the ENOENT errno along with a -1 return value.
I did also have a look at strsep(), but while from what I've read it's similar to strtok(); I really don't understand the examples I've seen of it.
Sometimes it's hard to find good examples.
One thing you may find useful at some point is look at the source code from some of these mysterious functions by downloading the source code for glibc.
For example, here is what they have for strtok.c
Code:
/* Copyright (C) 1991-2019 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
#include <string.h>
/* Parse S into tokens separated by characters in DELIM.
If S is NULL, the last string strtok() was called with is
used. For example:
char s[] = "-abc-=-def";
x = strtok(s, "-"); // x = "abc"
x = strtok(NULL, "-="); // x = "def"
x = strtok(NULL, "="); // x = NULL
// s = "abc\0=-def\0"
*/
char *
strtok (char *s, const char *delim)
{
static char *olds;
return __strtok_r (s, delim, &olds);
}
So looks like most of the work is dont by strtok_r.c
Code:
/* Reentrant string tokenizer. Generic version.
Copyright (C) 1991-2019 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <string.h>
#ifndef _LIBC
/* Get specification. */
# include "strtok_r.h"
# define __strtok_r strtok_r
#endif
/* Parse S into tokens separated by characters in DELIM.
If S is NULL, the saved pointer in SAVE_PTR is used as
the next starting point. For example:
char s[] = "-abc-=-def";
char *sp;
x = strtok_r(s, "-", &sp); // x = "abc", sp = "=-def"
x = strtok_r(NULL, "-=", &sp); // x = "def", sp = NULL
x = strtok_r(NULL, "=", &sp); // x = NULL
// s = "abc\0-def\0"
*/
char *
__strtok_r (char *s, const char *delim, char **save_ptr)
{
char *end;
if (s == NULL)
s = *save_ptr;
if (*s == '\0')
{
*save_ptr = s;
return NULL;
}
/* Scan leading delimiters. */
s += strspn (s, delim);
if (*s == '\0')
{
*save_ptr = s;
return NULL;
}
/* Find the end of the token. */
end = s + strcspn (s, delim);
if (*end == '\0')
{
*save_ptr = end;
return s;
}
/* Terminate the token and make *SAVE_PTR point past it. */
*end = '\0';
*save_ptr = end + 1;
return s;
}
#ifdef weak_alias
libc_hidden_def (__strtok_r)
weak_alias (__strtok_r, strtok_r)
#endif
At some point in your education, looking at function definitions from the C library may help you to understand things better.
Quote:
Just so you guys know, and in case I don't respond for a little while; I've just had surgery yesterday, and they give me some pretty powerful pain medication, so it's effects can dope me right out. So it's hard to do much at all after taking it (I tried the strcasecmp() function and wrote my little program below the other day before I went into hospital - I'm out of it now, as it wasn't major surgery. It was to cut open an Abscesses and drain it).
I bet you're glad it's taken care of! Good luck with the pain meds. xD
Distribution: Currently: OpenMandriva. Previously: openSUSE, PCLinuxOS, CentOS, among others over the years.
Posts: 3,881
Original Poster
Rep:
Thanks guys!
Thankfully it's started to heal now. It's just where it is on my body that's almost the worst possible place you could have an Abscesses. Let's just say it's not on the top half of my body, but it isn't on my legs either, and it was hard trying to type anything sideways - or even just watch TV/a movie And in the last couple of days I've done the old distro hop too - because apparently I must enjoy going thru hell trying to get stuff to work. So a big shout out to NVIDIA for the trouble But on the upside, gcc 9.1.0 20190503 in OpenMandriva seems to give better error messages that make at least a little more sense, so at least that's something. But anyway, I should probably get back on topic now (my fault) ...
Anyway, I thought it was an idea to play with my little build_array program to try and figure out how to incorporate strcasecmp() into my ffcliFront program. And it seems to have paid off, as build_array works as intended. So basically if you were to uncomment the following line;
Code:
// int result = strcasecmp(resultstr, string);
and comment out;
Code:
int result = strcasecmp(chunk, string);
you'll get;
Code:
james@jamespc: devel> ./build_array
Original string: ffmpeg: -i: something: blah: blah: blah
chunk = ffmpeg
chunk not the same
resultstr = ffmpe
and so you should
But if you were to compile making no changes to the full source below;
Code:
#include <stdio.h>
#include <string.h>
int main(void) {
char string[100] = "ffmpeg: -i: something: blah: blah: blah";
char resultstr[100] ="ffmpe" ;
char *chunk;
printf("Original string: %s\n", string);
chunk = strtok(string, ":");
printf("chunk = %s\n", chunk);
int result = strcasecmp(chunk, string);
// int result = strcasecmp(resultstr, string);
if ( result == 0 ) {
// strcpy(resultstr, string);
strcat(resultstr, chunk);
while( chunk != NULL ) {
chunk = strtok(NULL, ":");
printf("Token: %s\n", chunk);
if (chunk) {
printf("chunk = %s\n", chunk);
strcat(resultstr, chunk);
}
}
}
else {
puts("chunk not the same");
}
printf("resultstr = %s\n", resultstr);
// concatenate string in "s2" in "s1"
// strcat(s1, s2);
return 0;
}
So now I guess I'll try to incorporate the above code into ffcliFront. I'll have a look at the other suggested functions once I've done that. Thanks again guys!
Distribution: Currently: OpenMandriva. Previously: openSUSE, PCLinuxOS, CentOS, among others over the years.
Posts: 3,881
Original Poster
Rep:
Well, good news (I think), it seems I may have just figured out how to incorporate strcasecmp() into my ffcliFront program. I tested it and it does seem to work and solve the "partial match" problem.
But I've been reading the man page about the access() function, and while it does seem to make some sense to me; I'm not clear about the value of F_OK or the part about the "mask" following that. What is the value of F_OK ? Because I'm not sure what I'm supposed to put for the int mode part in the SYNOPSIS section of the man page. And what's the "mask" part in the DESCRIPTION section of the man page mean? Also, am I right in thinking that you guys are saying I should use access() to check if the input file exists (argv[2]) ?
Anyways, once I've got the access() function sorted out; I was thinking about passing the ffargs array to the execFfmpeg() function of my program below, then somehow copying ffargs[] into a pointer array since execv() expects a pointer array, then tacking the value of argv[3] (for the output file name) on the end of the pointer array in the execFfmpeg() function. But I'm not quite sure exactly how to do that part of it though, or even if that idea is the best way to go?
Anyway, here's the code so far;
Code:
// includes
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdbool.h>
char readConfigFile(char *argv[]);
//char readConfigFile(char *filename, char *formatID);
//char execFfmpeg(char ffargs[]);
// main function
int main(int argc, char *argv[]) {
if ( argc < 4 ) {
fprintf (stderr, "Missing option or filename\nUSAGE: ffcliFront <format_id> <input_file> <output_file>\n");
return 1;
}
// check if input file exists
FILE *inputfile;
if ( (inputfile = fopen(argv[2], "r")) == NULL ) {
fprintf(stderr, "Input file not found, exiting.\n");
return 1;
}
fclose(inputfile);
readConfigFile(argv);
//readConfigFile(argv[1], argv[2]);
return 0;
}
//char readConfigFile(char *filename, char *formatID) {
char readConfigFile(char *argv[]) {
char configLine[128];
char ffargs[128];
char *tokenPtr;
bool foundStr = false;
// read config file
FILE *configfile;
if ( (configfile = fopen("ffcliFront.conf", "r")) == NULL ) {
fprintf(stderr, "Configuration file not found, exiting.\n");
return 1;
}
printf("argv[1] = %s\n", argv[1]);
while( fgets(configLine, 128, configfile ) != NULL) {
printf("Line from config file: %s\n", configLine);
tokenPtr = strtok(configLine, ",");
int result = strcasecmp(tokenPtr, argv[1]);
if ( result == 0 ) {
foundStr = true;
printf("Found string: %s\ntokenPtr = %s\n", configLine, tokenPtr);
strcpy(ffargs, "-i ");
strcat(ffargs, argv[2]);
while( tokenPtr != NULL ) {
tokenPtr = strtok(NULL, ",");
printf("Token: %s\n", tokenPtr);
if (tokenPtr) {
printf("tokenPtr = %s\n", tokenPtr);
strcat(ffargs, tokenPtr);
}
}
//strcat(ffargs, argv[3]);
break;
}
}
if (foundStr == false) {
fprintf(stderr, "\"%s\" format_id not found in configuration file, exiting.\nCheck ffcliFront.conf for a valid format_id or specify a format_id that exists.\n", argv[1]);
return 1;
}
fclose(configfile);
printf("ffargs = %s\n", ffargs);
// pass char array with ffmpeg args & call execFfmpeg() fuction with args from config file
// execFfmpeg(ffargs);
return 0;
}
//char execFfmpeg(char ffargs[]) {
//char *ffcmdline[128];
//printf("%s\n", ffargs);
// run fmpeg with args from config file & filename supplied to this program.
// execv ("ffmpeg", ffcmdline);
//return 0;
//}
And if I pass an argv[1] that doesn't exist in the config file;
Code:
james@jamespc: ffcliFront> ./ffcliFront tes testinput out
argv[1] = tes
Line from config file: test, ffmpeg, ehe, te, teyeyyes, y, eye, yeh, eyh, eeee, eyeye, e, ey
Line from config file: test1, ffmpeg, ehe1, te, teyeyyes, y, eye, yeh, eyh, eeee, eyeye, e, ey
Line from config file: test2, ffmpeg, ehe2, te, teyeyyes, y, eye, yeh, eyh, eeee, eyeye, e, ey
Line from config file:
Line from config file:
Line from config file:
"tes" format_id not found in configuration file, exiting.
Check ffcliFront.conf for a valid format_id or specify a format_id that exists.
And if I do specify say "test2" for argv[1];
Code:
james@jamespc: ffcliFront> ./ffcliFront test2 testinput out
argv[1] = test2
Line from config file: test, ffmpeg, ehe, te, teyeyyes, y, eye, yeh, eyh, eeee, eyeye, e, ey
Line from config file: test1, ffmpeg, ehe1, te, teyeyyes, y, eye, yeh, eyh, eeee, eyeye, e, ey
Line from config file: test2, ffmpeg, ehe2, te, teyeyyes, y, eye, yeh, eyh, eeee, eyeye, e, ey
Found string: test2
tokenPtr = test2
Token: ffmpeg
tokenPtr = ffmpeg
Token: ehe2
tokenPtr = ehe2
Token: te
tokenPtr = te
Token: teyeyyes
tokenPtr = teyeyyes
Token: y
tokenPtr = y
Token: eye
tokenPtr = eye
Token: yeh
tokenPtr = yeh
Token: eyh
tokenPtr = eyh
Token: eeee
tokenPtr = eeee
Token: eyeye
tokenPtr = eyeye
Token: e
tokenPtr = e
Token: ey
tokenPtr = ey
Token: (null)
ffargs = -i testinput ffmpeg ehe2 te teyeyyes y eye yeh eyh eeee eyeye e ey
Which is the correct line. So hopefully I've fixed that problem now.
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.