creating file for shell output redirection - c++

This is a school assignment.
So basically I have written a C code for a shell that handles terminating the command with &, and input / output redirection using > or <. using pipes and fork(), execvp().
The problem is my input/output redirection only handles that for files that already exist.
What i need to know is how would I go about redirecting output to a file that doesn't exist - I know I would have to create the file, but I'm not sure how that works.
For example: ls -a < test.txt
If test.txt is not in the directory, i need to create it and redirect output to that.
So how do I create this file?
here is some basic example code which does not create a new file:
else if( '>' == buff[i] ){
i++;
j=0;
if( ' ' == buff[i] )
i++;
while( ' ' != buff[i] && i < len )
out_file[j++]=buff[i++];
out_file[j]='\0';
if ( ( ofd = open(out_file,1) ) < 0 ){
perror("output redirected file");
exit( 1 );
}
close(1);
dup(ofd);
}
Any help with how I can output and create a new file would be much appreciated! Thanks!

You need to tell open to create the file, if necessary:
if ( ( ofd = open(out_file, O_WRONLY | O_CREAT) ) < 0 ) {
Note that your code will be more readable if you use the named constants for the flags to open. Compare with the functionally equivalent
if ( ( ofd = open(out_file, 513) ) < 0 ) {
or even
if ( ( ofd = open(out_file, 0x0201) ) < 0 ) {

Related

Need to save product twice to have auto tag updated correctly

Thanks to 7uc1f3r, I was able to programatically assign tags to products based on their stock status. Link here
However, I have run into an issue. There is no error. At times, I have to save the product twice for the correct tag to reflect. I have tried the code using else & elseif but the issue persists. If someone could guide me to use switch case, I can try that & see if the tags get assigned correctly on the first save. Alternatively, if there is information on why this would occur & possible remedies, please let me know. Looks like the product is in the process of saving in the backend & the tag gets assigned prior to that but I don't know for sure. Please advise.
add_action( 'woocommerce_admin_process_product_object', 'mycode_woocommerce_stockstatus_tag', 10, 1 );
function mycode_woocommerce_stockstatus_tag( $product ) {
if( !$product->is_in_stock() ) {
$product->set_tag_ids( array (113) );
}
elseif( $product->is_on_backorder(1) ) {
$product->set_tag_ids( array (111) );
}
elseif( $product->is_in_stock() && !$product->is_on_backorder(1) ) {
$product->set_tag_ids( array (112) );
}
}
I am also posting a variant of the code where I split out where stock is being managed or not. The if statements here are independent & appear to do a better job. However, I still request if someone could pitch in & help with switch case as I believe it will execute faster.
add_action( 'woocommerce_admin_process_product_object', 'mycode_woocommerce_stockstatus_tag', 10, 1 );
function mycode_woocommerce_stockstatus_tag( $product ) {
if( $product->managing_stock() && ( $product->get_stock_quantity() > 0 ) ) {
$product->set_tag_ids( array (112) );
}
if( $product->managing_stock() && ( $product->get_stock_quantity() <= 0 ) && ( $product->backorders_allowed() || $product->backorders_require_notification() ) ) {
$product->set_tag_ids( array (111) );
}
if( $product->managing_stock() && ( $product->get_stock_quantity() <= 0 ) && !$product->backorders_allowed() && !$product->backorders_require_notification() ) {
$product->set_tag_ids( array (113) );
}
if( !$product->managing_stock() && $product->is_in_stock() && !$product->is_on_backorder(1) ) {
$product->set_tag_ids( array (112) );
}
if( !$product->managing_stock() && $product->is_on_backorder(1) ) {
$product->set_tag_ids( array (111) );
}
if( !$product->managing_stock() && !$product->is_in_stock() ) {
$product->set_tag_ids( array (113) );
}
}

C++ Renaming Files with Qt causes SIGSEGV

I just copied all my cds to my computer with a program called "Sound Juicer". It works fine, it creates for each artist a folder and it it for each album another folder. And of course in these folders the mp3 files.
The problem is I want the Tracknumber, the Artist and then the Tracktitle as name for my songs. What Sound Juicer does is adding d1t in front of the file which stands for "Disk 1 Title".
I'm a programmer so I used this problem to practice a little bit. This works :
void MainWindow::rename( const QString & text )
{
static int _files = 0;
QDir dir( text );
QFileInfoList a = dir.entryInfoList( QDir::Files | QDir::Dirs );
for( int i = 2; i < a.size(); i++ )
{
static QDir tmp;
if( a.at( i ).isDir() )
rename( a.at( i ).absoluteFilePath() );
if( a.at( i ).fileName().startsWith( "d1t" ) || a.at( i ).fileName().startsWith( "d2t" ) )
{
QString newFile = a.at( i ).fileName().remove(0,3);
tmp = a.at( i ).dir();
if( !tmp.rename( a.at( i ).fileName(), newFile ) )
qDebug() << "Failed";
_files++;
}
}
}
It checks a directory, selects the first file or directory and checks what it is. If it is a directory it calls itself (recursion) and starts again until he finds some files or no more directories exist. If a file is found, it renames it and adds 1 to the file counter.
However, it only renamed all files in the first 2 or 3 directories. After that it caused a SIGSEGV. Does anyone knows whats wrong?
Example of my directories :
1 Directory ("Sum 41") -> 1 Subdirectory ("All Killer No Filler") ->
Files "d1t01. Sum 41 - Introduction to Destruction.mp3" etc. ...
2 Subdirectory ("Blah Blah") -> Files ...
2 Directory ("Shinedown") -> 1 Subdirectory ("Sound of Madness") ->
Files d1t01. Shinedown - Devour.mp3 etc...
3 Directory ("Guns N’ Roses") -> Subdirectory ("Blah Blah") -> files ...
Subdirectory ("Blah ") -> files ...
With static you try to tell the compiler you want only one instance in the whole program, yet you put it in a for loop. That is not really clean. In this situation static is useless. Static can also cause problems due to the static initialization chaos problem. So if you dont really need static initialization, drop it and make it a local or a class variable.
Rewriting your method, to be able to read it better, i saw that you call the method recursively and that could cause an infinite loop.
Another problem is your files variable, what is the purpose of it?
void MainWindow::rename( const QString & text ) {
int files = 0;
QDir dir(text); // does QDir also accept wrong paths?
QFileInfoList list = dir.entryInfoList(QDir::Files|QDir::Dirs); // does it return a list in all cases?
foreach (QFileInfo entry, list) {
if (entry.isDir()) {
//is everything always ok when doing this?
// POTENTIAL infinite loop
rename(entry.absoluteFilePath());
}
else {
QString fileName = entry.fileName();
if(fileName.startsWith("d1t") || fileName.startsWith("d2t")) {
if (!entry.dir().rename(fileName, fileName.remove(0,3))) qDebug() << "Failed";
files++;
}
}
}
}
I have with the help of fonZ and Frank Osterfeld somehow fixed it. I finally created a GUI to select just a single path. However, I have done it. This piece of code looks up all directories without causing an infinite loop or an overflow (so far). The problem was something with QDir's and QFileInfo's functions to get the path. I played a while with it and this came out :
void MainWindow::rename( const QString& path )
{
//If invalid path return
if( path.isEmpty() || ( !QDir( path ).exists() ) )
return;
//entryInfoList( QDir::NoDotAndDotDot ) doesn't work.
QFileInfoList fileList = QDir( path ).entryInfoList();
foreach (QFileInfo entry, fileList ) {
//Eliminating wrong paths
if( entry.isDir() ){
if( entry.filePath().endsWith(".") )
continue;
//Start function again with new directory
rename( entry.filePath() );
}
else{
QString fileName = entry.fileName();
if( fileName.startsWith( "d1t" ) || fileName.startsWith( "d2t" ) ){
//Remove those characters
fileName.remove( 0, 3 );
//If renaming is successful, increment the successful files
//If not, increment the failed files and print an error
if( entry.dir().rename( entry.fileName(), fileName ) )
files++;
else{
addError( "Could not rename " + entry.fileName() + " to " + fileName );
filesFailed++;
}
}
}
}
}

Adding a -q switch to a list of arguments

I am trying to figure a way to add an optional quiet switch to my command line arguments. The program I'm working on is a text to HTML converter, and at minimum requires a text sourcefile to be included in order for the program to run. What I am trying to get, is when a user enters -q anywhere in the argument list, the program will still run but suppress the output to the console. I have tried a few if statements and loops that will re assign argument values to my infile and outfile variables but those are not working either. The code can be found here: https://gist.github.com/anonymous/ab8ecfd09bddba0d4fcc. I am still relatively new to working with C++ so if you provide an explanation as to how to get closer to my goal in a simple way, I'd really appreciate it.
Something jumped out at me right away, you're testing if the argument equals -q by
if( strcmp( argv[1], "-q" ) != 0) //This is an example of what I am trying to do.
{
quiet = true;
infile.open( argv[2] );
}
which is incorrect. strcmp returns the lexical difference between the two strings compared :
http://www.cplusplus.com/reference/cstring/strcmp/
so I believe you want
if( strcmp( argv[1], "-q" ) == 0) //This is an example of what I am trying to do.
{
quiet = true;
infile.open( argv[2] );
}
Like I said, I haven't tested anything, it just jumped out at me.
edit
how I would parse in the sourcefile, destfile, and -q option
std::string sourceFile;
std::string destFile;
if ( argc == 3 )
{
sourceFile = std::string( argv[1] );
destFile = std::string( argv[2] );
}
else if ( argc == 4 )
{
// quiet mode is enabled
std::string arg1( argv[1] );
std::string arg2( argv[2] );
std::string arg3( argv[3] );
if ( arg1 != "-q" )
vec.push_back( std::string( arg1 );
if ( arg2 != "-q" )
vec.push_back( std::string( arg2 );
if ( arg3 != "-q" )
vec.push_back( std::string( arg3 );
if ( vec.size() != 2 )
{
// maybe error?
}
else
{
sourceFile = vec[0];
destFile = vec[1];
}
}
Certainly not as clean as possible, and I haven't tested it so there may be a small error.

Using Windows API File functions c++

New to windows programming, there are several examples all over the internet of what i am about to ask however none of them show the comparison which i think is failing.
I'm using several windows api calls throughout my C++ Program and just need some guidence on how to use them correctly.
For example below i have GetFileAttributes() which returns anything from File Attribute Constants.
DWORD dwAttributes = GetFileAttributes(strPathOfFile.c_str());
if ( dwAttributes != 0xffffffff )
{
if ( dwAttributes == FILE_ATTRIBUTE_NORMAL )
{
pkFileInfoList->Add( strPathOfFile + "\t" +"FILE_ATTRIBUTE_NORMAL");
}
else if ( dwAttributes == FILE_ATTRIBUTE_ARCHIVE )
{
pkFileInfoList->Add( strPathOfFile + "\t" + "FILE_ATTRIBUTE_ARCHIVE");
}
}
[/CODE]
The if/else statement continues with everything from File Attribute Constants.
Am i using this correctly, i have a directory with over 2500 files which i am feeding the path recusivly. It is always returning FILE_ATTRIBUTE_ARCHIVE.
Thanks,
GetFileAttributes returns a set of attributes, not a single attribute, so to test correctly you should do:
DWORD dwAttributes = GetFileAttributes(strPathOfFile.c_str());
if ( dwAttributes != 0xffffffff )
{
if ( dwAttributes & FILE_ATTRIBUTE_NORMAL )
{
pkFileInfoList->Add( strPathOfFile + "\t" +"FILE_ATTRIBUTE_NORMAL");
}
else if ( dwAttributes & FILE_ATTRIBUTE_ARCHIVE )
{
pkFileInfoList->Add( strPathOfFile + "\t" + "FILE_ATTRIBUTE_ARCHIVE");
}
}
I.e. use bitwise & instead of ==

C++ SQLite3 how to know if select return 0 rows

C++ SQLite3 how to know if select return 0 rows
I have a select statement for SQLite3, how do I know that if after executing the sql statement, the result is 0 rows , no match found etc..
How can i modify my code so that if 0 rows found, it will not execute the part where it put the result into a vector.
My code below:
sqlstatement = "SELECT * from abe_account where department="+quotesql(department)+" AND name="+quotesql(name)+";";
std::vector< std::vector < std:: string > > result;
for( int i = 0; i < 4; i++ )
result.push_back(std::vector< std::string >());
sqlite3_prepare( db, sqlstatement.c_str() , -1, &stmt2, NULL );//preparing the statement
sqlite3_step( stmt2 );//executing the statement
while( sqlite3_column_text( stmt2, 0 ) )
{
for( int i = 0; i < 4; i++ )
result[i].push_back( std::string( (char *)sqlite3_column_text( stmt2, i ) ) );
sqlite3_step( stmt2 );
counter ++;
}
sqlite3_step returns SQLITE_DONE when there are no (more) rows to process:
int stat = sqlite3_step(stmt2);
if (stat == SQLITE_DONE) {
// no rows to process
}
Remember to check for errors as well, e.g.:
if (stat != SQLITE_DONE && stat != SQLITE_ROW) {
// there's probably a problem
}
You can find the complete list of result codes in the manual.
Lastly, you should use the "v2" interface when using SQLite3. From the manual:
The sqlite3_prepare_v2() and sqlite3_prepare16_v2() interfaces are recommended for all new programs. The two older interfaces are retained for backwards compatibility, but their use is discouraged.
Do a SQL Count first, if it returns 0 you can just stop execution without needing to query the select statement.