1

I'm running this on Lubuntu in a virtual machine using Oracle's VirtualBox. I don't think that would affect anything, but I just thought it was worth mentioning.

It appears the error is appearing from a strange symbol appended to the end of the string returned by the following function:

inline std::string f_settings_get(std::string val){
    int index=0;
    while(strcmp(val.c_str(),f_settings[0][index].c_str())!=0){
        index++;
    };
    // For debugging purposes - printf("\n%s\t%s",f_settings[0][index].c_str(),f_settings[1][index].c_str());
    return f_settings[1][index];
}

I can't see an issue with this function and thus believe it might be caused by the data stored in the variable.

I have been trying to open a file in my program using fopen and have come across the error stated in the title. The code works perfectly in Windows (and I have accounted for the / and \ for each operating system); however, when running on Linux, although it compiles perfectly, I get the error that the file does not exist. I checked online and found several issues others have had such as:

  1. The filename is not REALLY the right filename

  2. Permission to access file was not there

  3. Accessing the file was done by just calling "file.txt" instead of the whole directory

I checked the first out by using the Python script supplied, and recreating the file with a new name. That didn't work (and the Python script showed that the filename was good).

I set all permissions to being 777 using chmod -R 777 clesis and that did not work (though I didn't expect it to be a permissions issue as it said the file did not exist).

I have the full path already called and double checked the path. It is correct.

Finally, I ran the following in the code to double check that: (a) The file existed and (b) I was trying to open the correct file.

    tmp_s="";
    tmp_s = homeDir+binP+f_settings_get("xxxxx"); // Note, you can easily get whatever variable you want using either f_settings_get or num_settings_get
    tmp_file = fopen(tmp_s.c_str(), "r+");
    printf("\n Running the following command: /home/xxxxx/programming/xxxxx/bin\n");
    std::system("ls -l /home/xxxxx/programming/xxxxx/bin");
    getWait();
    printf("\n Tried to open: %s", tmp_s.c_str());
    if (tmp_file == NULL){
        printf("\n");
        perror("Error loading dimensions.sim");
        printf("Press enter to exit...");
        getWait();
        exit(1);
    }

And here's how the output looks (sorry for censoring things. I assure you the user name and folder are spelled the same):

The output shows the following:

Filenames Loaded...

Loading Settings from sim file...
++++++++++++++++++++++++++++++++++++++++++++++++++ Settings Loaded...

Loading System Dimensions from sys file...
--------------------------------------------------
 Running the following command: /home/xxxxx/programming/xxxxx/bin
total 636
-rwxrwxrwx 1 xxxxx xxxxx    958 Jul 25 20:59 dimensions.sim

Tried to open: /home/xxxxx/programming/xxxxx/bin/dimensions.sim

Error loading dimensions.sim: No such file or directory
Press enter to exit...

If the file path is specified directly as "bin/dimensions.sim" or "/home/xxxxx/programming/xxxxx/bin/dimensions.sim" then it works properly. This leads me to a confused conclusion. That somewhere in the conversion of tmp_s from a std::string to a c_str there is an issue that is leading to a wrong directory path. However, this doesn't occur with any of the other files I'm using, and I have never had this issue on a Windows machine. Thus I am confused as to what may be causing it.

One last observation - the conversion was printed out anyway using the printf("tmps = %s",tmp_s.c_str());. And that can be clearly seen to not be 'weird'.

Community
  • 1
  • 1
hherbol
  • 91
  • 12
  • The output of the `printf` only appears not to be weird. Maybe there's a non-printable character inserted somewhere in the path name? – lurker Jul 26 '13 at 01:22
  • I thought that but then I went through every directory location and tried the python script supplied in the first link. I checked starting from the following directories: / /home /home/xxxxx /home/xxxxx/programming /home/xxxxx/programming/xxxxx /home/xxxxx/programming/xxxxx/bin This didn't clear that up for me though as I'm only copying python code and don't know if it is actually checking for non-printable characters or not – hherbol Jul 26 '13 at 01:24
  • You should only call `perror` right after the system call which generates the error. You must not make any other call which might set the error code (and some might, even if they return without error.) Even printf("\n"); should be avoided. Just FYI – rici Jul 26 '13 at 01:34
  • You should also print out the contents of tmp_s.c_str before you call open to see what it is and perhaps call system with the ls -l on that actual variable containing the string. Calling system on another string and saying it doesn't have a problem isn't really very meaningful. – xaxxon Jul 26 '13 at 01:35
  • Run the program in gdb (gnu debugger). This will allow you to inspect the contents of the file name to check it is valid. Note, you may have to add a line like strcpy(teststring,tmp_s.c_str()); just before the call to fopen. – Alec Danyshchuk Jul 26 '13 at 01:35
  • What is r+? read plus..what? It might be something, I just have never seen it before. – Jiminion Jul 26 '13 at 01:43
  • OK, read update. You should ls the whole path (with the filename) to be sure you aren't missing some weird hidden character. Your test does not establish the the file is named as you think it is. – Jiminion Jul 26 '13 at 01:47
  • Do you have `write` permission on the file for the user that is running the program? Since `"r+"` opens the file for read&write, if you don't have that permission the `fopen` will fail. If necessary, do a `chmod 666 filename` to set permission for read/write for "everyone", and see if that fixes the problem - then shut it down again. Do you actually need to write to the file? Just noticed that it looks from the listing that you have `rwxrwxrwx` permissions... which means this is not the issue. – Floris Jul 26 '13 at 01:49
  • Are you running Visual C++ on the windows side? It tends to be more lenient about some stuff than g++. – Jiminion Jul 26 '13 at 01:51
  • Alrighty. I called perror right before and after fopen and got "success" before and "no such file or directory" after. I then called tmp_s.c_str() before the fopen and it looks the same. I did, however, get something interesting when first creating a string `newThing = "ls -l " + tmp_s` and running `std::system(newThing.c_str());`. I got: `newThing = ls -l /home/henry/programming/clesis/bin/dimensions.sim : No such file or directoryry/programming/clesis/bin/dimensions.sim`. It looks like it prints ontop of itself? But the string looks normal still? I don't understand... – hherbol Jul 26 '13 at 01:51
  • Floris, yes I have full 777 permission. Jim, yes I'm running Visual C++ on the windows side. Jim, I believe I have been running `ls` on the entire path. And @AlecDanyshchuk, I'm not entirely sure how to run gdb – hherbol Jul 26 '13 at 01:54
  • What happens if you capture the string from the `ls` command (not `ls -l`), and feed that into the `fopen` command? It seems like the most direct way to make sure it's the same file name... – Floris Jul 26 '13 at 01:54
  • Have you ever been able to open any files for read/write? Can you open a file for writing only - that should create a new file; you can then check that a) it is in the right place, and b) that it works at all. Sometimes these virtual machines are a bit weird. – Floris Jul 26 '13 at 01:56
  • @Floris, what do you mean by that? I really don't want to have to run ls, convert that into a string and use that to open files from now onwards... – hherbol Jul 26 '13 at 01:56
  • @Floris, I just ran a code and I'm able to open "w+", write to it and close it fine. edit - And I just ran one on a blank file I created using "r+" to add "OKAY" to it and that works too. – hherbol Jul 26 '13 at 01:58
  • @hherbol: I can't tell from your comment (the one that starts "Alrighty") what exact output you're getting, but I'm going to venture a guess that there is a stray carriage return (`\r`) in your filename string. That happens a lot when using unix tools with windows tools. – rici Jul 26 '13 at 02:00
  • @rici - That makes a lot of sense. And I coudln't format that comment properly. The following appears on a single line: `No such file or directoryry/programming/clesis/bin/dimensions.sim`. Do you know how I might go about finding the \r and removing that? I cannot determine if it's in an actual folder/file name or appears in the code (I never directly scan it in that I know of). – hherbol Jul 26 '13 at 02:04
  • Like you never heard of `perror()`. –  Jul 26 '13 at 02:04
  • 2
    @hherbol: The most likely place is at the end of the string returned by f_settings_get. The way this sort of thing usually happens is that you have a unix-y program which sets a value to "the rest of the characters on the line", but the line turns out to come from a Windows environment where it is terminated with CR-LF. The CR will then not be detected as part of a line-terminator, so it ends up at the end of the "rest of the characters on the line". Printing the string out in hexadecimal will show you if that's happening. (`for (const char* p=str;*p;++p)printf("%0X ", *p);printf("\n");`) – rici Jul 26 '13 at 02:12
  • @rici: I think you're right. There's a weird symbol printing out in the end. I also did `ls` for everything (homeDir then homeDir+binP then... etc) and found that the error is coming from f_settings_get as you said. I can't understand how though as that function is pretty basic. I'll add that in an edit above so it's formatted properly. – hherbol Jul 26 '13 at 02:17
  • 1
    I think that @rici is hitting at the heart of the problem - with virtual machines, the translation between line endings can be dodgy. – Floris Jul 26 '13 at 02:18
  • 1
    @hherbol: it's undoubtedly coming from the `f_settings` array. I'll bet that array is created by taking each line from some file and putting the part before the `=` as the first string of the pair; and the "rest of the line" as the second string of each pair. See my previous comment about "rest of the line". – rici Jul 26 '13 at 02:30
  • 1
    And this is why IRC is so much better a tool for collaborative debugging than SO. Sigh. I'm getting old. – rici Jul 26 '13 at 02:32
  • Hmmmm... I printed out all data stored in f_settings[1][i] array and got that, once converted to hex, the all end with their last hex 'xx' then 'D' then 'A'. The D A seems to be screwing things up. What confuses me is that it appears not only on the virtual machine version but also the full version on my laptop. – hherbol Jul 26 '13 at 02:37
  • hahahah, well I haven't found any good IRC that were populated so I've stuck to SO. And thank you so very much @Rici. If you write up the answer I'll select it as the correct one :). I just need to drop the last 2 parts of each variable to clean off the extra junk and I think the code will work (in both win and linux). – hherbol Jul 26 '13 at 02:39
  • @hherbol: ok, how's that for an answer? – rici Jul 26 '13 at 02:54

1 Answers1

4

Your problem is most likely coming from stray carriage returns (hex 0D, usually written as \r in C), which is common when using unix-y libraries to read windows-y files. Windows files have their lines terminated with CR-LF, hex 0D0A, while unix just uses a single LF, hex 0A. Windows C stdio will map a CRLF onto a single LF (\n) for files not opened in binary mode. In Unix, it doesn't matter whether files are opened in binary or ASCII modes, since no remapping happens.

The consequence is that the same program compiled on Windows programs can read Windows files, and compiled on Unix can read Unix files; in both cases, the line-terminator will look like a single \n. Windows programs can usually read Unix files, too. But the Unix program reading a Windows file will see a stray CR, \r, hex 0D at the end of every line.

This particularly plagues configuration utilities. Suppose you have a config file of the form:

some_setting=27
some_file=dimensions.sim

etc.

Now, the configuration reader reads each line, splits it at the =, and uses the first part as a key and the second part as a value. It might even convert some of the values to numbers.

Now, if the file was created on Windows (regardless of whether it is currently sitting on Windows or sitting on your laptop), the Unix configuration reader is going to see the value of some_setting as 27\r and the value of some_file as dimensions.sim\r. The first one won't cause any problems unless the config system is paranoid, because atoi and strtod convert up to the first non-digit character, and don't complain as long as there is at least one digit. But the string will not work as a filename, because the filename is unlikely to have a \r at the end.

What makes this problem particularly insidious is that when you try to print the offending string out, the \r actually acts as a carriage return; that is, it returns the cursor to the beginning of the line. If the next output character is \n, then the \r is totally invisible. If the next output is some message, it will get overprinted over the line, which is very confusing.

phuclv
  • 37,963
  • 15
  • 156
  • 475
rici
  • 234,347
  • 28
  • 237
  • 341
  • +1 for the great answer and patience in working through this with hherbol. What is IRC? – Jiminion Jul 26 '13 at 03:02
  • @Jim: my point exactly :). "Internet Relay Chat", the original real-time "social network" chat, long before networks were social. See http://en.wikipedia.org/wiki/Internet_Relay_Chat – rici Jul 26 '13 at 03:04
  • Perfect answer :) And I finally finished editing the code to function. Thank you so much @rici – hherbol Jul 26 '13 at 04:37