Amiga.org

Amiga computer related discussion => General chat about Amiga topics => Topic started by: foleyjo on May 07, 2017, 07:19:44 PM

Title: Executing c command in c
Post by: foleyjo on May 07, 2017, 07:19:44 PM
Hi
Can anybody explain how in Amiga C I can use Execute() but change the directory first. I thought it would be simply Execute("cd path",bptr,bptr); but that is ignored
Title: Re: Executing c command in c
Post by: D00kie on May 07, 2017, 08:58:30 PM
I think you want Dos.library/SetCliCurrentDirName. http://wiki.amigaos.net/amiga/autodocs/dos.doc.txt
Provided the program is run through the shell, that should set the CLI environment to use the directory you want and the new process should pick it up. It would with System() rather than Execute(), anyway.
Title: Re: Executing c command in c
Post by: guest11527 on May 07, 2017, 09:06:41 PM
Quote from: foleyjo;825470
Can anybody explain how in Amiga C I can use Execute() but change the directory first.
In general, I would recommend System() or rather SystemTagList() to execute a command as it is considerably more flexible than Execute().

Anyhow, in general, the current directory of the process is inherited to the shell launched through Execute(), so you could simply newlock=Lock(...) the desired target directory, then call oldlock=CurrentDir(newlock), then Execute(), then UnLock(CurrentDir(oldlock));

Alternatively, you can also set the current directory through the NP_CurrentDir tag of the System() call, which is a bit more convenient.
Title: Re: Executing c command in c
Post by: foleyjo on May 07, 2017, 11:31:58 PM
Thanks.

I've looked for information on the SystemTagList() but can't find much that I understand.
I've tried to use it but I'm just getting a crash.
Heres what I've got with an explanation of why I think it's supposed to be like this. Though probably doing it completely wrong.
Code: [Select]

   struct TagItem * tags;            /*Setup a pointer to the tags that will be used */
   tags = AllocateTagItems(1);    /*Create empty tag*/
   tags[0].ti_Tag = NP_CurrentDir;  /*Set Tag as NP_CurrentDir */
   tags[0].ti_Data = (ULONG)"work:/docs/";  /*Set the directory path prefixed with (ULONG) because ti_Data only accepts ULONG %/
   SystemTagList("dir",tags);  /*Runs the command dir in tags[0] (not the command I'm actually running but used in this instance to shorten code */
   FreeTagItems(tags);   /*Clear tags from memory*/


Sorry if this is all actually simple stuff. A lot of the sites I'm finding for tutorials are either dead sites, not in depth enough to explain things properly or not in English and when I translate the text using google translate the code gets messed up and no longer works.
Title: Re: Executing c command in c
Post by: guest11527 on May 08, 2017, 08:52:40 AM
Quote from: foleyjo;825483
Thanks.

I've looked for information on the SystemTagList() but can't find much that I understand.
I've tried to use it but I'm just getting a crash.
Tags can be placed on the stack, or even in the arguments of the "tag-based" function System(), there is no need to allocate them manually. Tag-lists also need to be terminated by TAG_DONE, or you crash. Last but not least, NP_CurrentDir takes a Lock as argument, not a string.

Code: [Select]
LONG result = -1;
BPTR lock = Lock("my directory",SHARED_ACCESS);

if (lock) {
result = System("my command",NP_CurrentDir,lock,TAG_DONE);
UnLock(lock);
}
Approximately, minus typos.
Title: Re: Executing c command in c
Post by: foleyjo on May 08, 2017, 09:42:35 AM
Thanks again Thomas.
Didn't find anything like that when I was trying to see how to use it. You have been a big help.
Title: Re: Executing c command in c
Post by: kolla on May 08, 2017, 12:42:04 PM
When was the last time someone wrote a book on C programming for Amiga? :)
Title: Re: Executing c command in c
Post by: foleyjo on May 08, 2017, 08:45:24 PM
Thomas I tried using the method you posted but it didn't work.
It gave me an error on the lock bit of system()
So I mixed what I already had by using a lock and not a string and it worked.
Thank you for pointing me in the right direction.
Title: Re: Executing c command in c
Post by: guest11527 on May 08, 2017, 09:49:53 PM
Quote from: foleyjo;825521
Thomas I tried using the method you posted but it didn't work.

This type of thing happens if you don't have the system in front of you. That's why I said "minus typos".
Code: [Select]
#include <dos/dos.h>
#include <dos/dostags.h>
#include <proto/dos.h>

int main(int argc,char **argv)
{
  BPTR lock = Lock(&quot;foo&quot;,SHARED_LOCK);

  if (lock) {
    LONG res = SystemTags(&quot;foo&quot;,NP_CurrentDir,lock,TAG_DONE);
    UnLock(lock);
  }

  return 0;
}
Title: Re: Executing c command in c
Post by: foleyjo on May 09, 2017, 01:54:01 PM
Yeah that's how I had it except I didn't use SHARED_LOCK . My documentation only talked about read_access and write_access so I used read_access.

I'm not sure I tried SystemTags(). I tried System() and SystemTagList() .

I notice your code uses (int argc,char **argv) . Is this important as I haven't included them?
Title: Re: Executing c command in c
Post by: guest11527 on May 09, 2017, 06:02:09 PM
Quote from: foleyjo;825557
I notice your code uses (int argc,char **argv) . Is this important as I haven't included them?
ANSI-C knows two signatures for main:"int main(void)" and "int main(int argc,char **argv)", where the latter is equivalent to "int main(int argc,char *argv[])" due to the way how C handles arrays.

If your compiler supports any other signature, then that's an extension. "int main()" for example does not declare parameters, which was acceptable form in K&R C which your C compiler may still support. It is not ANSI-C.
Title: Re: Executing c command in c
Post by: foleyjo on May 10, 2017, 09:22:41 AM
Good to know thanks.

I'm starting to think I might give up or change language as I'm just not finding anything that I is useful to help me understand what to do. Except for the help here which was great.

For example I'm now trying to open a jpg picture in mui.
I found how to play a sound in mui but nothing about pictures.
I found one tutorial that says how to create a datatype object but nothing on how to set that object as a picture or how to display said picture in a window.
I looked at mui_image. I think I have to use the OLD_Image attribute because I didn't see anywhere else to set the image.

I've managed to create a listview with a list that is loaded from a file and when I double click an item in the list it runs an event (Currently shows me the contents of the directory of where the file is stored in the console). I tried to find information on how to do something for a single click .
The only thing I can find that seems to make sense is select_change (https://muidev.de/wiki/Documentation/API/MUI_Listview#MUIA_Listview_SelectChange)
but this only works if I hold the mouse button down and drag it up and down the list.

So I'm thinking it's either keep asking lots of questions here or just give up and as I don't want to annoy people with my questions I'm leaning towards the give up. :(
Title: Re: Executing c command in c
Post by: foleyjo on May 10, 2017, 04:38:19 PM
With regards to the image I'm trying to load
Do I create an area as a child of the window and then DTpic as a child of the area with MUIA_Dtpic_Name holding the filename (pointer to the string) for the image I want to open?

Will the datatypes library then work out what type of picture it is (iff,png,jpg,etc) or does that need to be specified somewhere else?


Will the list selection on a single click be something like
Code: [Select]
DoMethod(List_Name, MUIM_List_Select, MUIV_List_Select_Active, MUIV_List_Select_Ask, TRUE, Action_Name);  (Where list_name is the name of the list and Action_Name is the name of the called function)
Title: Re: Executing c command in c
Post by: nicholas on May 10, 2017, 06:04:56 PM
Quote from: foleyjo;825604
Good to know thanks.

I'm starting to think I might give up or change language as I'm just not finding anything that I is useful to help me understand what to do. Except for the help here which was great.

For example I'm now trying to open a jpg picture in mui.
I found how to play a sound in mui but nothing about pictures.
I found one tutorial that says how to create a datatype object but nothing on how to set that object as a picture or how to display said picture in a window.
I looked at mui_image. I think I have to use the OLD_Image attribute because I didn't see anywhere else to set the image.

I've managed to create a listview with a list that is loaded from a file and when I double click an item in the list it runs an event (Currently shows me the contents of the directory of where the file is stored in the console). I tried to find information on how to do something for a single click .
The only thing I can find that seems to make sense is select_change (https://muidev.de/wiki/Documentation/API/MUI_Listview#MUIA_Listview_SelectChange)
but this only works if I hold the mouse button down and drag it up and down the list.

So I'm thinking it's either keep asking lots of questions here or just give up and as I don't want to annoy people with my questions I'm leaning towards the give up. :(


This is an old but useful guide to developing in C for the Amiga.

http://library.morph.zone/Magic_User_Interface_Programming

A nice MUI tutorial

http://library.morph.zone/Magic_User_Interface_Programming

If you use IRC the #mui channel on Freenode is a good place to ask questions.
Title: Re: Executing c command in c
Post by: foleyjo on May 10, 2017, 09:45:26 PM
Thanks for the advice nicholas though were the 2 links supposed to be to the same site.
I did find that site but I was looking through an example I downloaded from it but the comments were not in English.

I managed to get an image loaded using Dtpic.
It wasn't working earlier but I think I was using an old version of mui as I downloaded the latest from muidev.de and Dtpic worked.

There's also some demos in this version that I didn't have which seem to do things that I need so I can try and learn the code.
Title: Re: Executing c command in c
Post by: nicholas on May 10, 2017, 10:36:14 PM
Quote from: foleyjo;825621
Thanks for the advice nicholas though were the 2 links supposed to be to the same site.
I did find that site but I was looking through an example I downloaded from it but the comments were not in English.

I managed to get an image loaded using Dtpic.
It wasn't working earlier but I think I was using an old version of mui as I downloaded the latest from muidev.de and Dtpic worked.

There's also some demos in this version that I didn't have which seem to do things that I need so I can try and learn the code.


Doh! I posted the wrong first link lol

This is the correct one http://aminet.net/package/dev/c/ACM

These are also handy, they are quite Amiga generic and not so MorphOS specific.

http://library.morph.zone/MorphOS_API_and_Its_Organization
http://library.morph.zone/Exec_Lists
http://library.morph.zone/Taglists

And the bombjack site has PDFs of the RKRM's and almost every Amiga development book ever published.

http://www.bombjack.org/amiga/index.htm

Another handy site for reference

http://amigadev.elowar.com/

Some good articles in this repo too

https://github.com/inrms/guidetoamigacompatibleprogramming/blob/master/README.md
Title: Re: Executing c command in c
Post by: nicholas on May 10, 2017, 11:06:28 PM
There's always the excellent Hollywood if you give up on C though.

It's powerful yet simple.

http://hollywood-mal.com/
Title: Re: Executing c command in c
Post by: foleyjo on May 10, 2017, 11:26:49 PM
Haha I thought it was strange they both linked to the same page.
. I'll have a read through those other sites tomorrow.

I looked at Hollywood but I read somewhere that it doesn't allow you to launch external programs which is why I avoided it as its something I need.


I've managed to get images changing with a single click now using

Code: [Select]

DoMethod(list, MUIM_Notify, MUIA_List_Active, MUIV_EveryTime,
app, 2,
MUIM_Application_ReturnID, DISPLAY);
   
Where DISPLAY contains a set attributes function on my DtPic object.

So I think I'm going to keep trying for a bit.
I should have enough information now to help me understand what I'm doing :D
Title: Re: Executing c command in c
Post by: nicholas on May 11, 2017, 01:02:02 AM
Quote from: foleyjo;825628
Haha I thought it was strange they both linked to the same page.
. I'll have a read through those other sites tomorrow.

I looked at Hollywood but I read somewhere that it doesn't allow you to launch external programs which is why I avoided it as its something I need.


I've managed to get images changing with a single click now using

Code: [Select]

DoMethod(list, MUIM_Notify, MUIA_List_Active, MUIV_EveryTime,
app, 2,
MUIM_Application_ReturnID, DISPLAY);
   
Where DISPLAY contains a set attributes function on my DtPic object.

So I think I'm going to keep trying for a bit.
I should have enough information now to help me understand what I'm doing :D


Excellent! I look forward to seeing your first creation when it's finished. :)

Hollywood does support launching external programs though btw.

http://www.hollywood-mal.com/docs/html/hollywood/Execute.html
Title: Re: Executing c command in c
Post by: foleyjo on May 11, 2017, 06:27:29 AM
Oh no way. If I knew that would have looked into getting hollywood as it looks much easier.

If I get stuck again I'll definitely consider changing to it.
For now I'll just convince myself I made the right choice because I'm learning useful things.
Title: Re: Executing c command in c
Post by: nicholas on May 11, 2017, 07:17:41 AM
Quote from: foleyjo;825639
Oh no way. If I knew that would have looked into getting hollywood as it looks much easier.

If I get stuck again I'll definitely consider changing to it.
For now I'll just convince myself I made the right choice because I'm learning useful things.


You can't go wrong with C, no pain no gain. :)
Title: Re: Executing c command in c
Post by: foleyjo on May 18, 2017, 08:13:21 AM
Me again :D

Thanks to the advice I received here I have made lots of progress.
I'm still getting things wrong but understand enough to make corrections.
I also understand more about MUI and can work out how to make it do what I want.
I have hit a small stumbling block though.

How do you get the string value from a mui_popstring popup object? So say I wanted the user to select a file and then save the filename to a string what command would I use?

Also what are APTRs and BPTRS, Should I be using them or is it fine to stick with Object * ?
The reason I ask is because the tutorials I used at first had Object * but when I was looking at the mui examples they used APTR. I think APTR looks nicer in the code .

I'm hoping to have something to show soon. (famous last words)
Title: Re: Executing c command in c
Post by: olsen on May 18, 2017, 09:42:26 AM
Quote from: foleyjo;825901
Also what are APTRs and BPTRS, Should I be using them or is it fine to stick with Object * ?


Careful there, this is going to hurt.

You will meet BPTRs almost exclusively in the dos.library functions and the bookeeping data structures which it manages. If a function returns a BPTR, store it as a BPTR, never as an Object pointer or anything else. If a function requires a BPTR as parameter, make sure it came from a variable of type BPTR. Some other operating system functions, which pass parameters to dos.library, also use BPTRs, as well as storing them in the data structures they work with.

You should treat BPTRs as different from any other regular 'C' pointer type you might be familiar with. It helps to keep those variables which hold BPTRs separate from the rest, and always have them use the BPTR type when you declare them.

Why the fuss? A BPTR is not a regular 'C' pointer (the name is an abbreviation for "BCPL pointer", with "BCPL" being the programming language in which dos.library was originally written). You might picture BPTRs as something special which only dos.library knows how to deal with, and the remainder of the operating system has no clue about. If you mix BPTRs up with regular 'C' pointers strange things are guaranteed to happen: crashes are just the beginning, misfortune of all kind will follow you.

You already met the oddly-named APTR (I believe the name may be an abbreviation for "address pointer", but further back in Amiga history, it might have actually stood for "Amiga pointer"). This one is a proper, regular 'C' pointer which holds a memory address. It's just window-dressing for 'void *', which means that it is a pointer which can be assigned to any type of other regular 'C' pointer without causing the 'C' compiler to to warn you that the assignment mixes data types which do not match.
Title: Re: Executing c command in c
Post by: foleyjo on May 18, 2017, 10:14:51 AM
So if I understand you right....
I don't need to worry about BPTR unless I'm using a predefined function that takes or returns a BPTR (which will usually be dos functions such as the Lock one I'm using)

APTRs I assume it's more of a preference thing. So I could use char * or APTR when declaring a string pointer and not expect much difference.
Title: Re: Executing c command in c
Post by: olsen on May 18, 2017, 10:51:50 AM
Quote from: foleyjo;825906
So if I understand you right....
I don't need to worry about BPTR unless I'm using a predefined function that takes or returns a BPTR (which will usually be dos functions such as the Lock one I'm using)


Yes, in a nutshell. There always has to be a very good reason for wanting to know more about what a BPTR is and what exactly it points to. You usually do not need to know about these details.

Quote
APTRs I assume it's more of a preference thing. So I could use char * or APTR when declaring a string pointer and not expect much difference.


There is more to the APTR type, and how/when to use it.

While the APTR can be assigned to any other pointer type without the 'C' compiler bothering you with warnings, there is a cost: you willl, many times in your 'C' programming career, end up using this special "keep it quiet" propery of the 'void *' pointer (which is what the APTR is) unwisely.

My advice is to never, ever use the APTR or 'void *' pointer in your code if you can help it, and if you do need to use it, you should feel guilty about doing so ;)

The use of this pointer type all too easily obscures the fact that the data pointed to may not be compatible. The compiler will warn you about that, and this is not something you should take lightly by shutting up the warning through an APTR or 'void *' pointer type. What is generally safer is to get rid of that warning by having the pointers being assigned or compared be of compatible types so that the similarity of the data comes out of the data structures as known to the 'C' compiler.

While you do have the choice to use an APTR or 'void *' pointer type in place of more specific pointer types, such as 'char *', it will pay off if you stick to the pointer type which best describes the data being referenced by it. The compiler will help you along by checking for you if the data types are sufficiently similar. You could do all this by yourself, but at some point (programs grow and evolve until you cannot realistically keep track of each part, and every problematic piece of code) this becomes a tedious exercise. Let the machine do that work for you :)

Side-note: the APTR also exists because of the need to let programmers develop software for the Amiga operating system in assembly language. The data structures you find in 'C' header files for the operating system have counterparts in 68k assembly language header files. Those 68k assembly language header files build the same data structures as found in the corresponding 'C' language header files through the use of macros. In these assembly language macros, "APTR" is a macro which stands for a 32 bit word, which is a pointer of some sort.
Title: Re: Executing c command in c
Post by: foleyjo on May 18, 2017, 11:44:33 AM
Thanks Olsen,
I understand what they are now.
I decided against using APTR for the reasons you said. I'm making too many rookie mistakes at the moment and luckily the compiler is catching them. If it didn't I'd have lots more problems.
Title: Re: Executing c command in c
Post by: guest11527 on May 18, 2017, 11:56:27 AM
Quote from: foleyjo;825906
I don't need to worry about BPTR unless I'm using a predefined function that takes or returns a BPTR (which will usually be dos functions such as the Lock one I'm using)
Not even then. Or to be more precide: In almost all cases, treat a BPTR as an opaque object. It's what you get out of Lock(), it's what you put into CurrentDir(), don't worry what it means. BPTRs are only for very specific system programming purposes an application developer does not need to worry about.

Quote from: foleyjo;825906
APTRs I assume it's more of a preference thing.
Neither. As Olaf put it: Always use the fully qualified pointer type. If you have a pointer to an intuition window, that's a "struct Window *", and not an "APTR". If you have a pointer to a character array, that's a "char *".

APTRs are "generic pointers" that do not carry type information. The advantage of a fully qualified pointer is that the compiler will warn you if you try something stupid (like asigning the address of a screen to a pointer of a window), and that's a service a beginner should take advantage of.


Quote from: foleyjo;825906
So I could use char * or APTR when declaring a string pointer and not expect much difference.
Yes, there is a difference, beyond type checking. Consider this:

Code: [Select]
int primes[] = {2,3,5,7,11};
int *prime_ptr = &primes[0]; /* this is the same as prime_ptr = primes; */
APTR aprime_ptr = &primes[0]; /* OK so far */

printf(&quot;%d\n&quot;,*prime_ptr); /* Prints 2 */
printf(&quot;%d\n&quot;,*aprime_ptr); /* compiler error! */

prime_ptr++;  /* advance pointer, points now to &quot;3&quot; instead of &quot;2&quot; */
aprime_ptr++; /* compiler error! */
A generic pointer cannot be dereferenced (what does it point to? You do not know!), and a generic pointer cannot be incremented (what is the address of the next object? You do not know as you do not know what the size of the object is it points to).

HTHH,

Thomas
Title: Re: Executing c command in c
Post by: olsen on May 18, 2017, 12:50:18 PM
Quote from: foleyjo;825912
Thanks Olsen,
I understand what they are now.
I decided against using APTR for the reasons you said. I'm making too many rookie mistakes at the moment and luckily the compiler is catching them. If it didn't I'd have lots more problems.


Funny that you mention lots of problems... One thing which I did not know when I started out in 'C' programming, and which initially gave me cause to worry, was the sheer number of complaints the 'C' compiler would throw at me.

You could make one small mistake, and the compiler would be animated to report errors for code which was perfectly sound.

The trick to deal with this situation is to work through the error messages from the top, one at a time. Ignore the bulk of error messages at the tail end of the compiler output for now.

Why does this happen? The compiler may stumble over code which derails its normal processing operations (could be as little as one character in the wrong place), and in an attempt to get back on track, it skips as much code it cannot make sense of until it looks like it may be safe to process the remainder. This attempt may derail things again, mind you, and then more error messages tumble out.
Title: Re: Executing c command in c
Post by: foleyjo on May 18, 2017, 01:13:25 PM
haha tell me about it. Spent a couple of hours last night trying to resolve errors.

I was either being told I needed a ; after an end on one of the lines or would just get 100s of errors.
Turned out the problem was a missing comma further up the page
Title: Re: General C help
Post by: foleyjo on May 19, 2017, 08:57:11 AM
Well Olsen and Thomas thanks to your explanations about APTRs and BPTRs something clicked and I was able to work out how to get the string value from the mui_pop_string.

The talk of pointers, objects and strings made me realise that muia_popstring_string was returning a mui string object and not a string so I retrieved the value in 2 parts.

First I did a get() on the pop object to get the popstring_string object and then did another get on the returned object to get the strings content.

So now I think I understand everything I need to to get my first program completed without having to ask anymore questions.
There's still some stuff that I don't understand such as hooks but I think I'll be able to avoid creating any of my own.

So hopefully I won't be bothering anyone for help and advice till I have something to show.

You guys have helped point me in the right direction and understand so much more than all the guides and tutorials I spent weeks on and the reason why I always return to Amiga.org when I have an Amiga related question.

Cheers :D
Title: Re: General C help
Post by: foleyjo on May 26, 2017, 09:02:15 AM
Quote from: foleyjo;825982


So hopefully I won't be bothering anyone for help and advice till I have something to show.



Well it seems that I was wrong.
I have got through a lot and nearing first release. However I have hit a wall.

My code was getting too long and difficult to read so I decided to separate it into extra files.

I have created .h files and put includes to them in the .c files. I have got externs for when I want to share variables across pages and everything compiles fine.
Yet I keep getting crashes.
I have narrowed it down to if commands not working.

In the main file if I have
Code: [Select]

FILE * fp;
file = fopen(&quot;new_file&quot;,&quot;w&quot;);
if(fp){
  printf(&quot;file open&quot;);
  fclose(fp);
}
printf(&quot;file closed&quot;);


The output as you would expect is
-file_openfile closed

If the same code is placed in one of the sub files the output is
-file closed
and then when I close the program I get an error because the file wasn't closed.

Any ideas what I've missed ?
Title: Re: General C help
Post by: guest11527 on May 26, 2017, 09:49:34 AM
Quote from: foleyjo;826247
I have narrowed it down to if commands not working.

In the main file if I have
Code: [Select]
FILE * fp;
file = fopen(&quot;new_file&quot;,&quot;w&quot;);
if(fp){
  printf(&quot;file open&quot;);
  fclose(fp);
}
printf(&quot;file closed&quot;);

If the same code is placed in one of the sub files the output is
-file closed
Without seeing the full source, it is hard to tell. Probably the following: If you want to use the same object from two translation units, make sure that you define it only once, but declare it in every unit as an object with "external linkage".

That is, in one unit, you define outside of a function body an object with external linkage and program-long lifetime:
Code: [Select]
FILE *file; /* this is a definition, it creates the FILE * */
while, in a second unit, you access this very same object by only *declaring* it:
Code: [Select]
extern FILE *file; /* this is a declaration. The object is defined elsewhere */
The above line just tells the compiler "there is an object named 'file' somewhere else in the project, but not in the current translation unit".

This way, you ensure that the compiler generates code that accesses the identical object. Each object needs to be defined *once*, but can be *declared* multiple times.

I do not know whether this is your problem, it is just a guess from your observation.
Title: Re: Executing c command in c
Post by: foleyjo on May 26, 2017, 10:25:03 AM
With the extern command does it matter that I'm not declaring fp globally or passing it between functions?

so when I had the code in the sub function, in main I had a call to the subfunction with nothing else and then I just had the code from the example in the subfunction.

Everything else had been commented out.

It seems to be happening with all if/while statements where the result is expecting TRUE or FALSE and just taking the FALSE result.
The variables themselves are working without the if/whiles.
Title: Re: Executing c command in c
Post by: olsen on May 26, 2017, 12:25:24 PM
Quote from: foleyjo;826251
With the extern command does it matter that I'm not declaring fp globally or passing it between functions?
Hm... does "fp" have to be a global variable?

I am asking because there are several different ways to shoot yourself in the foot here.

It could be that you have a "FILE * fp;" defined inside a function (a local variable, as opposed to a global variable), which shares the same variable name with the globally defined "fp". This is the kind of thing which will inevitably make you miserable because everything seems to look right, yet the value of the variable seems to change. It does not change, the code is merely preferring the value stored in the local variable to the one in the global variable by the same name.

My advice would be to keep the names of local and global variables completely separate so that you know just by looking at what you code does that it's either a global or a local variable which is being used. If you don't yet have a good idea how to name your variables (this is one of the hard practical problems in computer science, I kid you not), you could start by changing the global "fp" variable name to "global_fp", for example and recompile your code. Does it compile? What needs to be changed to make it compile again?

I strongly recommend that you choose longer, more meaningful names for your variables. Introductions to the 'C' programming language tend to stick to the "no idle chatter" practice, keeping the names of functions and variables short. Variables are typically one letter each, maybe two, or even three if the author is generous.

When I started out writing 'C' code, I found that with short variables names, I had less to type, but more to remember. As a programmer, you already have a lot to remember to begin with, starting with how the individual parts of your program work together. Trying to remember all the names and respective purposes of variables does not help, it just makes your work harder. Worse: with similarly-named two-letter variables a mere typo can stress you out even more.

If you must use a global variable, make sure that you can know whether the contents of that variable are sound or not. If the fopen() function succeeds, it will return a special type of pointer ("FILE", as defined in the "stdio.h" header file) which is not NULL. If you store that pointer in a global variable and later close that file, make sure that you reset the global variable to NULL. That way you can more easily find improper use in your own code which references that global variable after the file has already been closed.
Title: Re: Executing c command in c
Post by: foleyjo on May 26, 2017, 12:53:01 PM
Think I've confused everyone with my explanation.

fp is declared locally. So I either have (just a quick example so ignore typos etc)

Example 1 = in 1 file
int main ()
{
  FILE *fp;
  fp =fopen("file","w");
  if(fp){do something}
  return 0;
}

Example 2 = in 2 files (with  .h files declaring the functions)
in file2.c
int function()
{
  FILE *fp;
  fp =fopen("file","w");
  if(fp){do something}
  return 0;
}

in file1.c

int main()
{
  function();
  return 0;
}


In Example 1 the if(fp) returns TRUE.
In Example 2 the if(fp) returns FALSE

Think I've confused everyone with my explanation.

fp is declared locally. So I either have (just a quick example so ignore typos etc)

Example 1 = in 1 file
int main ()
{
  FILE *fp;
  fp =fopen("file","w");
  if(fp){do something}
  return 0;
}

Example 2 = in 2 files (with  .h files declaring the functions)
in file2.c
int function()
{
  FILE *fp;
  fp =fopen("file","w");
  if(fp){do something}
  return 0;
}

in file1.c

int main()
{
  function();
  return 0;
}


In Example 1 the if(fp) returns TRUE.
In Example 2 the if(fp) returns FALSE

I haven't got fp declared globally. I try to avoid global variables as I've learned my lesson in the past from a global getting changed in a function when I didn't mean it to be changed.

The thing is, it all worked fine before I separated the code it into separate files. Anything in my main code file still works. It's only when I come to the other sections of code. If I put it back into 1 file it should work again but it's getting very long and hard to follow.
I suppose I could try adding comments to split the code up into sections

With variables I admit I don't name them the best but I do try to make them meaningful to me and usually use full words in my actual code. I use abbreviations for some things but only when the abbreviation is automatic to me. So when I see fp I'm actually reading it as File Pointer
Title: Re: Executing c command in c
Post by: LiveForIt on May 26, 2017, 01:19:29 PM
create a file called header.h

include header.h into the two files.

declare all the functions in header.h as extern
in the file header.h write

extern int function();

every file that header.h is included into, the compiler will know that there is a "int function()" somewhere, maybe not in this .cpp or .c file.

and try to not name your function "function", you might stumble onto keywords that compiler misinterprets.
Title: Re: Executing c command in c
Post by: olsen on May 26, 2017, 01:19:49 PM
Quote from: foleyjo;826257
Think I've confused everyone with my explanation.

fp is declared locally. So I either have (just a quick example so ignore typos etc)

...

In Example 1 the if(fp) returns TRUE.
In Example 2 the if(fp) returns FALSE


If fopen() fails, there will be an error code set which explains why it did not work. That error code will be found in the global 'errno' variable (which is declared in the "stdio.h" header file), but you could use the perror() function (also declared in the "stdio.h" header file) to print an error message:

Code: [Select]

int
function_name(void)
{
    FILE * output_file;
    int success = FALSE;

    output_file = fopen(&quot;output&quot;,&quot;w&quot;);
    if(output_file != NULL)
    {
       ...

       fclose(output_file);

       success = TRUE;
    }
    else
    {
       perror(&quot;fopen failed&quot;);
    }

    return(success);
}


Opening a file for writing can only work out if that file is not currently being used, such as for reading or for writing. Protecting a file from deletion or writing can also prevent it from being opened for writing.

Also, if the name of the file you want to overwrite actually refers to a directory, you won't be able to open it for writing either.

If you succeed in opening a file for writing, make sure that you close that file when you no longer need it, or the next attempt to open it for writing may fail, even though your own program opened it.
Title: Re: Executing c command in c
Post by: foleyjo on May 26, 2017, 01:46:23 PM
LiveForIt - You might be onto something. (though I must point out my function isn't called function I just used that for the example. The one where I noticed the problem is actually called WriteNewDataToFile but I noticed the same issue across all functions not just with files).

I do have the header files and they are referenced in the c files.
What I haven't done is added the extern function lines in the main h file.
I have done it for variables that are passed between files.

Olsen - The file does seem to open. It creates an empty file in the correct folder with the correct name.
It doesn't close though as the if command doesn't work and my fclose is within the if.
Title: Re: Executing c command in c
Post by: foleyjo on May 26, 2017, 01:55:58 PM
Just thinking about what has been suggested already I'm just wondering,


if I have (again just examples not actual function names, headers all declared and bits missed out)


main.c
Code: [Select]

function1()
{
  FILE *fp
  fp =  fopen(&quot;thisfile&quot;,&quot;r&quot;);
  if(fp){
  do stuff....
  fclose(fp);}
}

main()
{
  function1();
  function2();


other.c
Code: [Select]
function2()
{
  FILE *fp
  fp =  fopen("thisfile","w");
  if(fp){
  do stuff....
  fclose(fp);}
}

because I have used fp in main.c would I need to set an extern File *fp in one of the headers to use it again in other.c ? Even though fp was declared locally in both function1 and function2 ?
My current way of thinking is that I wouldn't need to because the 2 fps are actually different because they are not declared globally.
However I'm starting to wonder if that is the case.
Title: Re: Executing c command in c
Post by: LiveForIt on May 26, 2017, 03:17:02 PM
Quote
I do have the header files and they are referenced in the c files.
What I haven't done is added the extern function lines in the main h file.
I have done it for variables that are passed between files.

Any function or variable that is not defined in the .cpp has to decaled with "extern", if you don't compiler will think the function is in the .c / .cpp file and fail.
Quote
function1()

If you have function like this one that does return a return value, you should declare it with void.

void function1() {
// do stuff
}

Quote
main()

should always be declared as "int main()", main should always return a value, or else you can get some strange messages in Shell.

Quote
because I have used fp in main.c would I need to set an extern File *fp in one of the headers to use it again in other.c ? Even though fp was declared locally in both function1 and function2 ?
My current way of thinking is that I wouldn't need to because the 2 fps are actually different because they are not declared globally.

You are correct

Quote
because I have used fp in main.c would I need to set an extern File *fp in one of the headers to use it again in other.c ?

As long as FILE *fp is declared inside the code block start { and }, its local variable, to that code block. Local variable are declared on the stack, sometime it might idea declare locals inside sub blocks inside function, saves the CPU from initialize variables that is not used.

You might have seen the typical

for (int I=0; I<10; I++) {
}

Counter "I", is only used once and declared only when it's needed.
Title: Re: Executing c command in c
Post by: foleyjo on May 26, 2017, 03:52:13 PM
again in my real code the functions and declarations are declared properly and I've just been lazy in the examples.
So my real main lookslike

int main(int argc, char *argv[])
{
....
return 1;
}

I'll try adding externs for the functions and if it doesn't work I think I'll just go back to 1 file.
Everything was working as expected till I moved to separate files. In trying to locate the issue I ended up commenting out everything till all I had left was similar to the example I gave where if(fp) didn't work.
Title: Re: Executing c command in c
Post by: LiveForIt on May 27, 2017, 12:12:17 AM
You need to include the same files in all .c / .cpp files.
and you need to compile the .c/.cpp correct.

normally compile file2, file3 into a object, like

vbcc file2.c -o file2.o -c
vbcc file3.c -o file3.o -c

"-c" tells the compiler to make a object.

the you need to list all your file when compiling the main.c

vbcc main.c file2.o file3.o -o main.exe

some thing like that, I'm used to GCC, on AmigaOS4.1 :-), so you might need to look things up for vbcc.
Title: Re: Executing c command in c
Post by: foleyjo on May 30, 2017, 09:05:06 AM
Well quite embarrassing but the issue was in another section of code where I hadn't put in a semi colon inside and else statement.

Only thing I'm struggling with is strings. I've been using malloc wrong and think I've made a mistake of not putting in a close character.

Need to put an integer into a structure string too. I've tried using sprintf(list_items->max_number"%d",total); but it just crashes
Title: Re: Executing c command in c
Post by: guest11527 on May 30, 2017, 11:19:15 AM
Quote from: foleyjo;826397
Need to put an integer into a structure string too. I've tried using sprintf(list_items->max_number"%d",total); but it just crashes
Note that sprintf requires a char * as first argument, and specifically, a buffer that is large enough to hold the largest possible output of the formatted print. With %d, the largest output could be 10 digits, so you would require a buffer of 11 characters at least.

One way or another, sprintf is probably not a very wise choice to use, especially for beginners. You do not know how large its output will become (unless you can compute an upper bound by yourself as in the example above), and if the buffer is too small, it will just overrun some random memory. Bad design decisions of legacy C.

If possible, avoid sprintf() and try to substitute with snprintf. While this may create truncated output, it will at least not trash memory in worst case.
Title: Re: Executing c command in c
Post by: foleyjo on May 30, 2017, 01:55:27 PM
Had a typo there. List_items->max_number is my char *. Should have been a , after it.
I think it'll be easier to make it an int inside the structure. I had problems. Not sure why I did it as a char chat in the first place. Think I was getting a crash and changing it fixed things. Should have known it would come back to haunt me.
I'll check out snprintf too
Title: Re: Executing c command in c
Post by: olsen on May 30, 2017, 02:38:04 PM
Quote from: foleyjo;826408
Had a typo there. List_items->max_number is my char *. Should have been a , after it.
I think it'll be easier to make it an int inside the structure. I had problems. Not sure why I did it as a char chat in the first place. Think I was getting a crash and changing it fixed things. Should have known it would come back to haunt me.
I'll check out snprintf too


You might want to look at your data structure definitions, pondering whether each member's name suggests both its purpose and its content.

If a structure member would be named "max_number", and its purpose is to show that number in text form, "max_number_label" might be a better name.

As short as member names should be, every name tells a story and suggests how you might use it. If the first glance gives an impression that doesn't fit well or may even be misleading, then you are bound to make mistakes. This might be less of a problem in short programs, but as you are building larger and larger programs, you should not need to rely upon memorizing structure names, their respective member names and their purposes.
Title: Re: Executing c command in c
Post by: foleyjo on May 30, 2017, 03:06:34 PM
Thanks for the tip.
I changed it to an int and now it works as expected. I created it as a char because I didn't understand pointers.
I've just been through my code and comments and realised one of my functions uses char ** .
When I tried to make a similar function to work with integers and not strings I had used int ** instead of int*.
I found a good tutorial on pointers and addresses  so was able to spot the mistake this time and understood why it caused a problem.

Just been reading about snprintf and I had come across it before but there is lots of conflicting information.
sprintf,snprintf,strcpy and strncpy all seem to have people saying you should use them or you shouldn't use them.
I tend to malloc my destination string to the size of the source string and then add a null terminator
Title: Re: Executing c command in c
Post by: olsen on May 30, 2017, 03:25:01 PM
Quote from: foleyjo;826417

Just been reading about snprintf and I had come across it before but there is lots of conflicting information.
sprintf,snprintf,strcpy and strncpy all seem to have people saying you should use them or you shouldn't use them.


Well, you need to know why each one would be a poor choice.

strcpy() and strncpy() are part of the basic 'C' language runtime library. They go way back when the programmers who would use them could be relied upon to be intimately aware of the limitations of these functions. Nowadays you should try to avoid them because neither function knows when to stop if it has to copy strings to a destination of limited size.

If you use strcpy() to copy a 16 character string to a string buffer which can only hold 4 characters, strcpy() doesn't know that it should stop after the 3rd character.

The strncpy() function is a special version of strcpy() in that it will copy only a fixed number of characters, but it might not add a NUL termination. There are few reasons for using strncpy() in the first place. You will probably find that the conditions under which strncpy() makes sense apply only a handful of times during your 'C' programming career.

sprintf() has the same problem as strcpy(): it will put text into the output string buffer without regard for that buffer's size. Again, if sprintf() wants to put 16 characters into the output string buffer, and that buffer is just 4 characters in size, sprintf() will not stop after the 3rd character.

What you should be using instead of strcpy() is a function call strlcpy(). strlcpy() can be told how many characters (including the NUL) will fit into the output string buffer. It will stop copying if the output buffer turned out to be too short. Better still, strlcpy() will always make sure that the output string buffer is properly terminated by a NUL. There's one exception: if you tell strlcpy() that the output buffer is 0 characters in size it will not copy anything.

In case you were wondering, there is a strlcat() function which should be used rather than strcat(), too.

Instead of using sprintf(), use snprintf(). It's the same deal as with strcpy() vs. strlcpy(): snprintf() can be told how many characters will go into the output string buffer. If the output buffer turns out to be too short to hold everything snprintf() might want to store there, it will stop before overrunning the output string buffer.

Quote

I tend to malloc my destination string to the size of the source string and then add a null terminator


If that's all that needs to be done here, you might want to try strdup() instead. This will allocate memory for the NUL-terminated string, copy the original string over and return the copied string. It's a standard 'C' runtime library function.
Title: Re: Executing c command in c
Post by: foleyjo on May 30, 2017, 03:50:24 PM
tried strdup() previously and just tried strlcpy().

For both I get no prototype function.
Guessing that relates to the compiler I'm using (Storm C 3.0) .

So really I should be using snprintf in the places where I used strcpy.
I actually used strcpy because the tutorial I read said I shouldn't use sprintf. I only started using sprintf when I was looking for a way to write an integer into a string and that's when I started reading the reverse argument that strcpy shouldn't be used. Both arguments talked of the snprintf and strncpy but again both claimed 1 should be used and the other was bad.
Title: Re: Executing c command in c
Post by: guest11527 on May 30, 2017, 05:32:18 PM
Quote from: foleyjo;826421
tried strdup() previously and just tried strlcpy().

For both I get no prototype function.
Guessing that relates to the compiler I'm using (Storm C 3.0) .
Unfortunately, strlcpy() is not an ANSI-C function but a compiler extension some compilers provide, and unfortunately, strncpy() has its quirks as mentioned by Olaf (namely, not NUL-terminating strings). strdup() is ANSI-C.

Anyhow, regardless of this: It might make a good exercise anyhow to attempt an implementation of these two functions if your compiler does not provide them. It's not really hard to do that, and unless your code is really time-critical (hint: it usually is not), your own versions may do just fine.


Quote from: foleyjo;826421
So really I should be using snprintf in the places where I used strcpy.
Note that this is not a 1:1 substitute. snprintf() comes from the printf() family and does "quite a lot of work", whereas strcpy() is a simple string copy without much intelligence.

snprintf() is a possible substitute for sprintf(), but to use it properly, the return code of the function should be checked as to detect cases, where the buffer would overrun. While this no longer corrupts memory (unlike sprintf()), it still means that the output it generated is probably not what you want - after all, it was truncated. A possible strategy would be to allocate a buffer given the return code of snprintf, and then use that....

Anyhow. As you see, writing good, robust C code is not exactly trivial, due to many legacies of the language (C++ is a bit better in this respect, but also more complex...).



Quote from: foleyjo;826421
I actually used strcpy because the tutorial I read said I shouldn't use sprintf. I only started using sprintf when I was looking for a way to write an integer into a string and that's when I started reading the reverse argument that strcpy shouldn't be used.
sprintf() can be used to convert an int to a char[] - or rather write it into a given buffer - while strcpy() certainly can't, so the purpose of the two functions is quite different. The way how C handles "strings" is quite archaic (actually, it only really has character arrays and string literals, nothing like a "string" as you might know from other higher languages).


Quote from: foleyjo;826421
Both arguments talked of the snprintf and strncpy but again both claimed 1 should be used and the other was bad.
It really depends on what you want...

sprintf() can trash memory if you are not careful enough, snprintf() cannot, but it has similar drawbacks: Its output (or rather, it's side-effect - the letters in the buffer, let's be precise) can be truncated, making it useless in such cases.

So for the matter of this little tutorial: On the Amiga, a
Code: [Select]
char buffer[12];
sprintf(buffer,&quot;%d&quot;,number);
is perfectly safe, because ints are here at most 32 bits wide and hence *cannot possibly* be longer than 11 digits (plus NUL-termination gives 12).

So *in this case* we are perfectly safe. However, in other cases, it is not quite as trivial as how to compute as how long the result of sprintf() could possibly be, and in such cases,
Code: [Select]
char buffer[MAXBUFSIZE];
if (snprintf(buffer,something_complex,complex_arguments) >= MAXBUFSIZE) {
fprintf(stderr,"buffer overrun!\n");
exit(25); /* or some better mechanism how to handle this case - as in : enlarge the buffer */
}
is the better alternative. Unfortunately, it's not simple.
Title: Re: Executing c command in c
Post by: foleyjo on May 31, 2017, 11:07:34 PM
Ok I solved my issue for this post.I had a do while instead of a while do which was causing silly things to happen. My 2nd question I still don't know so leaving it below
Which leads me to a curiosity I have.
If calloc is used to assign memory for an array and a string is an array of characters, why is calloc not used for strings (at least not in the examples I can find)
Title: Re: Executing c command in c
Post by: guest11527 on June 01, 2017, 07:47:53 AM
Quote from: foleyjo;826545
If calloc is used to assign memory for an array and a string is an array of characters, why is calloc not used for strings (at least not in the examples I can find)
You can also use calloc to allocate character arrays (aka "strings") of course, no problem. C is a very low-level language. It does not really care where the memory came from, provided it is sufficient memory for the task at hand. You can use malloc, you can use calloc....

If you look at it, calloc is really only a very thin wrapper around malloc, performing one multiplication, and an additional zero'ing of the array afterwards, so most people simply don't bother.
Title: Re: Executing c command in c
Post by: olsen on June 01, 2017, 09:43:40 AM
Quote from: foleyjo;826545
Ok I solved my issue for this post.I had a do while instead of a while do which was causing silly things to happen. My 2nd question I still don't know so leaving it below
Which leads me to a curiosity I have.
If calloc is used to assign memory for an array and a string is an array of characters, why is calloc not used for strings (at least not in the examples I can find)


The 'C' runtime library contains several functions which deal with memory management. Each serves a specific purpose, although it is hard to say today why they all wound up in the library. There is malloc(), calloc(), realloc() and strdup(). Each of these functions may return a pointer to allocated memory which can be released with free(), or passed to realloc().

calloc() is one of the odd functions in this list. It will allocate memory for you, and if successful, will fill the allocated memory with zeroes. How you tell calloc() how memory is needed is rather peculiar: instead of asking for a certain number of bytes (like you would do with the malloc() and realloc() functions), calloc() wants to know how to break down the total allocation size into portions, and how many portions are required.

To answer your question: calloc() can be used to allocate memory for strings, but since calloc() does so much more than is needed if all you intend is to copy a NUL-terminated string into the buffer, malloc() does the same job just fine. malloc() is the simplest possible way to get this done.

Keep in mind that calloc() will fill the allocated memory with zeroes, and if you are going to copy the string into that memory, the zero-fill operation is not necessarily required. Also, calloc() wants you to break down the size into two parameters, this being a portion size and the number of portions required. malloc(), on the other hand, just needs to know the total size.
Title: Re: Executing c command in c
Post by: olsen on June 01, 2017, 09:53:10 AM
Quote from: foleyjo;826421
tried strdup() previously and just tried strlcpy().

For both I get no prototype function.
Guessing that relates to the compiler I'm using (Storm C 3.0) .


There's the rub. An older compiler will not support more modern library functions unless somebody takes the time to add them to it. You may be stuck with the state of the art at the time StormC 3.0 shipped.

Quote
So really I should be using snprintf in the places where I used strcpy.


Well, that depends upon what you need to accomplish. In my experience, the simplest possible tool that gets the job done tends to be the most appropriate.

snprintf() as well as sprintf() are mainly used for translating data (numbers, single characters, NUL-terminated strings) into text form.

If all you need is to copy one NUL-terminated string to a memory buffer, strcpy() may be a simpler solution. The drawback of strcpy() is that it does not respect output buffer boundaries, whereas snprintf(), if you have it, will do. In that case, snprintf() may be the safer alternative to strcpy() although it's purpose is not in copying strings, but in translating data into text form.

As a 'C' programmer you will have to make choices such as this (strcpy() vs. snprintf()) all the time. When the obvious solution would be the one you chose not to use, it's good practice to add a comment explaining briefly why the choice was made.

Quote

I actually used strcpy because the tutorial I read said I shouldn't use sprintf. I only started using sprintf when I was looking for a way to write an integer into a string and that's when I started reading the reverse argument that strcpy shouldn't be used. Both arguments talked of the snprintf and strncpy but again both claimed 1 should be used and the other was bad.

If the tutorial does not explain why one choice is better suited than the other, what can you do but ask us? :)
Title: Re: Executing c command in c
Post by: foleyjo on June 01, 2017, 02:41:47 PM
Quote from: olsen;826554

If the tutorial does not explain why one choice is better suited than the other, what can you do but ask us? :)


I don't know where I'd be if it wasn't for asking you guys.
Actually yes I do. I'd have given up after "Hello world" .
I have a few bugs that are causing crashes relating to memory now. I may be able to solve them once I have stepped through the code.
Will probably be asking for help later though knowing me
Title: Re: Executing c command in c
Post by: foleyjo on June 02, 2017, 12:13:54 PM
I got rid of the memory crashes.
Just a problem with my mui list now.
I have 2 columns ID and Name but only 1 displayed.
When an entry is clicked it should return the ID and then I use the ID to do tasks relating to the entry.
However the mui examples I found only return the item number in the list.
This is fine until I sort my list. Then the returned number and ID no longer match.

Now I think I need to use a compare hook. but I don't understand how the hooks work.
Or is there another way to return the correct ID.

This is the code I'm currently using to get the number of the item in the list
Code: [Select]

int num;
get (list_mylist,MUIA_List_Active,&num);
Title: Re: Executing c command in c
Post by: Thorham on June 02, 2017, 01:16:11 PM
Quote from: Thomas Richter;826432
Unfortunately, strlcpy() is not an ANSI-C function but a compiler extension some compilers provide
Why not implement simple things like that yourself? I usually don't bother with using the C runtime for strings at all. It bloats the executable for a few functions that are all a few lines of C. Furthermore, your own functions can be tailored to your exact needs.

Quote from: Thomas Richter;826549
C is a very low-level language.
I would call it a mid level language. Too high level compared to assembly language, and too low level compared to things like C#. Pointers != very low level ;)
Title: Re: Executing c command in c
Post by: Thorham on June 02, 2017, 01:22:22 PM
Quote from: Thomas Richter;826432
Unfortunately, strlcpy() is not an ANSI-C function but a compiler extension some compilers provide

Why not implement simple things like that yourself? I usually don't bother with using the C runtime for strings at all. It bloats the executable for a few functions that are all a few lines of C. Furthermore, your own functions can be tailored to your exact needs.
Title: Re: Executing c command in c
Post by: olsen on June 02, 2017, 02:18:37 PM
Quote from: Thorham;826576
Why not implement simple things like that yourself? I usually don't bother with using the C runtime for strings at all. It bloats the executable for a few functions that are all a few lines of C. Furthermore, your own functions can be tailored to your exact needs.

Writing your own string processing functions does make sense. The challenges you will be facing are, however, not going to be smaller than the original designers of the 'C' runtime library had to deal with ;)

You still need to define what your own functions should do (not that hard), and you still need to test your code and document it.

If you are only just starting out writing 'C' code, you might consider the testing & documentation portion to be not that important (I certainly thought that I had more important things to do, back in 1988). 'C' being what it is, testing and documenting your code are essential for writing robust, working code, rather than creating code which keeps surprising you by the many ways in which it causes crashes, or sucks up time for debugging. That's true for most programming effort, but 'C' makes it much easier to shoot yourself in the foot, and much easier to do much higher damage as a result.

The standard 'C' runtime library has its limitations and knock-on effects (not all of them good), but you can at least expect it to be well-documented and well-tested.

The trade-off is in either working within the constraints of the 'C' runtime library and modestly filling in the gaps you perceive with code of your own design, or completely relying upon code of your own design.

The greater freedom afforded by using your own design can be an illusion. Returning to the code you wrote a little time later you might just find that it is underdocumented and/or less robust than you thought it was when you wrote it.

Lack of documentation is certainly a challenge if you have to explain to others how it works, and why it works that way. Relying upon the 'C' runtime library instead of going your own way at least has the advantage that the documentation already exists and need not be fully explained by yourself to others.
Title: Re: Executing c command in c
Post by: guest11527 on June 02, 2017, 05:02:30 PM
Quote from: Thorham;826576
Why not implement simple things like that yourself?
For the OP and the problem at hand, this would make at least a good exercise with a learning experience. It's simple enough, and you'll understand that there is no "magic" going on in the stdlib.

Quote from: Thorham;826576
I usually don't bother with using the C runtime for strings at all. It bloats the executable for a few functions that are all a few lines of C. Furthermore, your own functions can be tailored to your exact needs.
In general, I would advice against this. Not only for the reasons Olaf gave (documentation, portability, readability), but also that some compilers offer compiler built-ins for the most used and most useful functions. SAS/C generates inline assembly code for strlen, strcpy, memset and memcpy. You'll hardly beat that by providing your own implementations. Many other string functions come with assembly language implementations (strcat, memchr, strchr, strrchr and others), so it's typically worth it.

C is of course not very good at string processing anyhow (given that it has no native datatype for strings, this is not a miracle) and other languages can do better by optimizing needless string copies or memory handling away. If you know the data flow in your application, you can certainly avoid some "costly" operations like strcat. E.g. if you know the string length anyhow, a strcpy to the right place will do.

Quote from: Thorham;826576
I would call it a mid level language. Too high level compared to assembly language, and too low level compared to things like C#. Pointers != very low level ;)
Given that we have now interpreted languages like javascript or languages like scala that run on the JVM, C is "pretty low" to me. It's probably the next possible "higher" language if you don't bother touching assembler. (Leaving some exotic intermediate languages like C-- apart).
Title: Re: Executing c command in c
Post by: LiveForIt on June 02, 2017, 05:41:02 PM
If wont to copy a string, I normally use

char *strdup(char *)

Because it bundles malloc() and strcpy()

there is also strndup() if you only need a part of a text string, might not be included older C standard lib's.

sprintf is best used for converting or formatting a text.

as for sprintf being unsafe, its only unsafe if don't calculate required buffer size in advanced, and use malloc to create a buffer.
Sure snprintf is safer, but it might be tiny bit slower, because it checks for if at end of buffer.

static buffer like

char mybuffer[33]; might really unsafe, imaging using this for file names, as old FFS files was only 32 chars, it was not a problem, but then newer versions of file systems started to support 128 char names.

It important to know etch function has It's own use, and don't try to make assumptions.

There is also

salloc()

The cool thing about it is that it allocates memory on the stack, so when exit a function, you get memory back with out freeing it, the disadvantage is that stack is predefined on AmigaOS and can't be auto increased, while program is running. so running out stack memory can be real issue.

maybe better to stick to malloc().

There are also AllovVec() and AllocMem() in Exec.library, this most not be mixed with malloc(), and free().
Title: Re: Executing c command in c
Post by: guest11527 on June 02, 2017, 07:12:51 PM
Quote from: LiveForIt;826595
There is also

salloc()
Unfortunately, that is non-C, non-ANSI, non-POSIX. Linux does not have it either. Gcc suppors "alloca()" which does something similar, and ANSI-C99 has VLAs which do pretty much the same; but even then, support might be flakey as we just found out in another thread. VBCC does not support it correctly, SAS/C not at all.

Worse, as it usually takes memory from the stack, it may overrun the stack easily. With the standard 4K stack on classic Amigas, this just happens too quickly.

One way or another,  I would advise against using it.
Title: Re: Executing c command in c
Post by: LiveForIt on June 02, 2017, 08:20:05 PM
ah, its a macro.
Title: Re: Executing c command in c
Post by: foleyjo on June 02, 2017, 09:24:52 PM
Apart from the mui specific stuff (Not sure if my question on getting items from a sorted list was missed or if nobody had an answer) my main problems have been strings. I'm used to languages where I don't have to worry about where they are in memory or remembering to have the end string characters. Now I have my head around those I'm getting the desired outputs and not getting any memory related errors.
It's nice to learn a different way of looking at them. I always knew they were arrays of characters but never thought too much about what the functions were actually doing.
Title: Re: Executing c command in c
Post by: nyteschayde on June 03, 2017, 04:44:49 AM
Somewhat related, I am curious if anybody knows how power packer and similar crunchers work. From what I understand, they compress an executable and attach the compressed binary to program that decompresses and then *executes* the decompressed contents.

There are clearly several things happening here, but ultimately, the code from the cruncher needs to kick off the uncompressed binary. Since the compressed executable contents are attached to the decompressor binary, it would make sense that the binary needs to read itself and extract some code to memory.

My question is how does one execute arbitrary binary from within one's program.
Title: Re: Executing c command in c
Post by: olsen on June 03, 2017, 08:12:38 AM
Quote from: foleyjo;826602
Apart from the mui specific stuff (Not sure if my question on getting items from a sorted list was missed or if nobody had an answer) my main problems have been strings.
How to use MUI is a subject all by itself. Not everybody who's written Amiga software over the years has used it, or is even sufficiently familiar with it to explain how to get things done. There is more than one way to knit an Amiga user interface. MUI, by virtue of being a framework, is one of the most complex toolboxes you could use.

Quote
I'm used to languages where I don't have to worry about where they are in memory or remembering to have the end string characters. Now I have my head around those I'm getting the desired outputs and not getting any memory related errors.
Now you know why these other languages exist ;)

Quote
It's nice to learn a different way of looking at them. I always knew they were arrays of characters but never thought too much about what the functions were actually doing.
The Amiga, and even the minicomputers, workstations, etc. which came before it, did not have a lot of memory or CPU power to spare. The programs running on these machines had to be very much in control of how memory was spent. There used to be a time when 2 Megabytes of RAM was quite a lot, and 512 Kilobytes was what you had to plan for, because most of your users likely had exactly this much available.

Languages such as Python, JavaScript or even Perl have to perform the same low level memory management operations for strings (among other data structures). The programs running in these interpreters don't have to bother, but the price is in that more memory is needed and more CPU power is needed.
Title: Re: Executing c command in c
Post by: foleyjo on June 03, 2017, 08:41:30 AM
I think I might change from mui to reaction. I have more of an understanding of how things are working now.
Title: Re: Executing c command in c
Post by: olsen on June 03, 2017, 08:44:45 AM
Quote from: nyteschayde;826613
My question is how does one execute arbitrary binary from within one's program.


At the most basic level, it boils down to knowing where the first instruction of the code to execute is located in, assign that address to a function pointer (if you are using 'C') and calling the code through the function pointer. There are no mechanisms present in the Amiga operating system which deny execution of arbitrary code. It's one shared address space for everything, and no sections are specifically restricted to either executable or non-executable content (on the 68030 and beyond some portions of the address space will be marked as cacheable or not cacheable, though).

Things changed a bit with the introduction of the 68030, and a bit more with the 68040, though. If the code you wanted to execute had to be pushed into memory, unpacked or auto-generated, you better made sure that the CPU data cache was flushed and the contents committed to memory (pipeline synchronization included). Sometimes things still worked out if you didn't know that you had to flush the cache and synchronize the pipelines, but you could not rely on being lucky all the time.

That's the basics. It gets more complicated if you need to load code into memory.

The easier case involves position-independent code, which references data, subroutines, etc. relative to the program counter (address of the instruction currently executing). With this code, you just reserve memory for it, copy the code into it and call it through the function pointer, etc. Yours truly once did that kind of thing for a certain Amiga hard disk controller which had to support booting from an FFS formatted partition. That was in the days when the FFS was not in ROM and had to be loaded from disk. However, the FFS at the time was one compact position independent chunk of code, so I stuck it into the hard disk driver's ROM and just ran it from there. Needless to say, don't try this at home, especially if you're 20 years old and under quite some time pressure ;)

The more difficult case requires that you load code from disk, or from ROM, without knowing beforehand where in memory exactly it will have to rest, this code not being position-independent. When you start a program from Workbench, the operating system will do exactly that: figure out how much memory is required to load the program, allocate memory for it, then load the program code and data into that memory. Finally, once everything is in place, the code and data are fixed up so that all the address references (data to data, code to code) match the respective memory addresses which code and data were loaded into (a process called "relocation", which is supported by the Amiga executable file format). The operating system provides all the tools to do this complicated business for you in dos.library, through the LoadSeg() function. If successful, LoadSeg() will return a special kind of pointer that refers to the first instruction of the code loaded (actually, it points to a 32 bit word in front of the first instruction). Again, you can use that code pointer with a 'C' function pointer and just invoke it to execute that code.

That's pretty much it, in a nutshell :)
Title: Re: Executing c command in c
Post by: foleyjo on June 08, 2017, 12:50:07 PM
Well I got a lot further with my program and am now ready to.... start again.
1 of the ways I learn is by going back over what I have already done and trying to do it better, using all the things I've learned. As some of the stuff I did at the start is probably a mess and can be done better now I understand things more.
I do have a few questions though.

1 - I've come across the variables STRPTR and UBYTE *. Should I use these or char * . They don't appear to do much different to each other from the examples I've seen. I like the idea of using STRPTR as to me it makes it clearer what it is. UBYTE I don't like and I've only seen it in early coding examples?

2 - Is Allocmem() better than malloc()?

3 - I'm probably going to stick with MUI for the GUI but am interested to know what you guys would recommend using?

4 - Should I allocate and free memory for every string or just ones that will be shared between functions.

These may seem like questions that I should have asked about at the start but thats why I like to go back over things. So currently in my code I have different functions using various things I've picked up and now I want to make it more consistant .
Title: Re: Executing c command in c
Post by: guest11527 on June 08, 2017, 01:57:17 PM
Quote from: foleyjo;826792
1 - I've come across the variables STRPTR and UBYTE *. Should I use these or char * .
That depends on what you want. As long as you only want to keep "strings", it makes no difference. However, depending on the compiler and compiler option, a "char" may be a signed datatype, whereas a "UBYTE" is unsigned. This makes a difference when comparing strings, i.e. when sorting strings and the like. In most cases, it is probably most helpful to use Stricmp() from the locale.library as it keeps track of local variations of the sorting order (for example, in German, the ΓΌ sorts as ue). For "char", all the "funny characters" have negative values, for UBYTE, they have positive values >= 128.

Quote from: foleyjo;826792
2 - Is Allocmem() better than malloc()?
No, it's different. Again, it depends on what you want. AllocMem() is the system function, malloc() is the C standard library function. Ultimately, malloc() boils down to AllocMem() in one way or another, however with quite some differences:

malloc() records how many bytes you have allocated, AllocMem() does not, and FreeMem() must be told how much bytes to release. (AllocVec() does, with FreeVec() as inverse).

Memory allocated with malloc() will be automatically released by the C startup (or rather shutdown) code, memory allocated with AllocMem() will not. Depending on what you want, this may either be desirable (because you have created some sort of resident program or data structure - then you need to use AllocMem()) or not - if you forget to release some memory, the C library will clean up behind you.

My general advice for novices is to stick with malloc() as it avoids memory holes, but as soon as you run into advanced system programming, switch over to AllocMem() and friends.


Quote from: foleyjo;826792
3 - I'm probably going to stick with MUI for the GUI but am interested to know what you guys would recommend using?
Really a matter of taste - and other constraints you may have. MUI is probably rather easy to handle, unlike other gadget toolkits I've seen, though for my personal taste, it was sometimes "a bit too much", and not precisely license-compatible with my software, so I didn't touch it. I personally used gadtools and (later) reaction. But this shouldn't really a recommendation - it is just my personal taste that I prefer to avoid third-party products and licencing issues.


Quote from: foleyjo;826792
4 - Should I allocate and free memory for every string or just ones that will be shared between functions.
Again, one cannot really recommend anything. It is not a matter of "sharing" strings between functions that makes memory allocation necessary. It is a matter of "lifetime". A character array has three drawbacks: 1) its size is fixed, 2) its life time is contrained to the function it is defined within (except if it is defined outside any function scope). 3) it takes up precious stack space if defined in function scope.

Allocating character arrays "from the heap" (aka "malloc()") has also drawbacks: 1) you need to remember to release the memory when you're done with it, which may or may not be so obvious, 2) if you do a lot of allocations, it may fragment the heap, i.e. slow down any further memory allocation (malloc() will typically limit this problem somewhat). 3) You somehow need to handle the situation gracefully if malloc() cannot find free memory.

Despite these technicalities, the question should really be: "do I need the string somewhere outside of the function (and its sub-functions) where I defined it?" If the answer is "yes", there is no other choice than to take it from the heap.

Maybe an example helps:
Code: [Select]
char *createUniqueName(void)
{
 char buffer[12]; /* borken! */

 sprintf(buffer,&quot;%d&quot;,rand());
 return buffer;
}
will *NOT* work. Essentially, the character array "dies" as soon as the CPU leaves the function. It "may appear to work sometimes", but it will in general generate nonsense.

There are two solutions: 1) allocate from the heap:
Code: [Select]
char *createUniqueName(void)
{
 char *buffer = malloc(12);

 if (buffer)
  sprintf(buffer,"%d",rand());
 return buffer;
}
This has the drawback that the caller needs to release the string itself, otherwise we have a memory leak, plus, there is potentially a situation where the system runs out of heap, and malloc() returns (at least on the Amiga) NULL, and you need to handle this case gracefully, also in the caller, which is often not so easy.

Or, 2), you declare this as "somebody else's problem" and require that the caller prepares the memory for the function:

Code: [Select]
char *createUniqueName(char buffer[12])
{
 sprintf(buffer,"%d",rand());
 return buffer;
}
which also works, and sometimes avoids headaches - or rather, creates some headaches (hopefully less) at another place. This has the problem that the caller has to know how large the buffer has to be. While trivial in this case, it is not always obvious, and overrunning a buffer leads to hard to detect crashes. Note that there is *no* test whatsoever that the buffer is really 12 (or a sufficient number) of bytes large. The number in brackets behind the function argument is just "bogus". The following is completely equivalent:

Code: [Select]
char *createUniqueName(char *buffer)
{
 sprintf(buffer,"%d",rand());
 return buffer;
}

There is no general advice I can give. It really depends on the problem you want to solve.
Title: Re: Executing c command in c
Post by: foleyjo on June 09, 2017, 08:30:08 AM
Thanks Thomas. Just a few follow ups based on your answers

1 -So for strings I think I'll stick with char * .I have UBYTE in a couple of areas of my code but that was from early stuff when I was reading some example code but from what you said I'm guessing that's not a good idea because I'm mixing 2 slightly different formats.

2 -With AllocMem() and malloc() my concern is that I read that you need to store certain things (images, sound) in chip memory and to do this you would use AllocMem and specify chipmem as one of the parameters. What I was reading implied that malloc would just use any available memory.

3 - Rather than what you recommend i guess I just meant what do you use which you answered. I like MUI probably because its the one I was looking at when everything clicked into how it worked. The only thing I don't like is that some things use hooks and I just can't get my head around how the hooks work. Not so much what is happening within the function but the calls to the hooks themselves. Where the parameters are coming from and how the return values work.

4 - Your string exampels were great. It's kind of what I was thinking but you made it clearer for me.
Kind of related to strings there is one other thing that I was reading that there seems to be a bit of a debate about on different sites and forums so I'd like to know what you think about...
When declaring pointers i've read people say that you shold set them to null or 0. Then when you have freed them you should set them back to null or 0.
Some people say this is a must while others say it isn't important and just causes extra work.

*edit* More  relating to the issue I have with hooks. For the mui lists DisplayHook in order to show only 1 column I'm using a hook which is modified from code I found.
So  I have

Code: [Select]
struct Hook disphook;
This I'm guessing is my hook

Code: [Select]
init_hook(&disphook, (HOOKFUNC)dispfunct, NULL); //call to init hook

void init_hook(struct Hook *hook, HOOKFUNC entry, APTR data)  // hook function
{
hook->h_Entry = (HOOKFUNC)HookEntry;
hook->h_SubEntry = (HOOKFUNC)entry;
hook->h_Data = (APTR)data;
}
I guess this is setting up the values in the hook structure. Though I'm not sure what HookEntry is or what data would be expected.
I'm also guessing that the 2nd patameter passed is the function that you would call

Code: [Select]
LONG dispfunct(struct Hook *hook, const char **array, struct ListItems *item)
{
if (rom)
array[0] = item->Name;
else
array[0] = "\033bName";
return(0);
}

and this is where I really get confused. I call this hook in the MUI List with MUIA_List_DisplayHook, &disphook .
How does it know what array and items are as I don't appear to pass anything to them??
Title: Re: Executing c command in c
Post by: Thomas on June 09, 2017, 02:38:06 PM
Quote from: foleyjo;826825
2 -With AllocMem() and malloc() my concern is that I read that you need to store certain things (images, sound) in chip memory and to do this you would use AllocMem and specify chipmem as one of the parameters. What I was reading implied that malloc would just use any available memory.


You should avoid to use AllocMem with MEMF_CHIP. Rather use more specific functions like AllocBitMap or AllocRaster to get your portion of graphics mem. On an RTG system or when using FBlit, graphics mem does not necessarily be in Chip RAM and if you use MEMF_CHIP you waste valuable Chip RAM.

Quote
Though I'm not sure what HookEntry is


This is the high-level-language interface. h_Entry is called by the system with arguments in registers. HookEntry simply pushes the registers to the stack and calls h_SubEntry. h_SubEntry then finds the arguments on the stack like a C subroutine does.


Quote
or what data would be expected.


The h_Data field is for your personal needs. You can put anything there. It can for example be used by the hook routine to pass some data to the main program. Or vice versa.

Quote
and this is where I really get confused. I call this hook in the MUI List with MUIA_List_DisplayHook, &disphook .
How does it know what array and items are as I don't appear to pass anything to them??


It's not you who calls the hook function. You just tell MUI where the hook is and MUI calls the function. The MUI documentation tells you which arguments your routine has to expect. In case of a DisplayHook it is an array and an item.
Title: Re: Executing c command in c
Post by: guest11527 on June 09, 2017, 03:30:16 PM
Quote from: foleyjo;826825
2 -With AllocMem() and malloc() my concern is that I read that you need to store certain things (images, sound) in chip memory and to do this you would use AllocMem and specify chipmem as one of the parameters. What I was reading implied that malloc would just use any available memory.
Correct. If you need specific memory for a specific purpose, malloc() is not a good idea. malloc() works for the average ANSI-C program, though not for Amiga-specific stuff.


Quote from: foleyjo;826825
The only thing I don't like is that some things use hooks and I just can't get my head around how the hooks work.
The calls are made from the MUI code into your code so you can customize its function. The parameters are, of course, prepared by the MUI code. What exactly they are I cannot tell you, I do not know MUI precise enough, though the general principle is the same. A "hook" is essentially a function you provide as "plug in" to an interface of a third party. Essentially, a "function pointer" with a bit of added functionality. How that all works is - hopefully - documented in the MUI documentation.



Quote from: foleyjo;826825
When declaring pointers i've read people say that you shold set them to null or 0. Then when you have freed them you should set them back to null or 0.
Some people say this is a must while others say it isn't important and just causes extra work.
In general, setting them to zero after releasing them is a good safeguard against dangling pointers, and the overhead is typically not worth thinking about. In best case, the compiler will find out itself that the assignment is useless.

There are of course obvious cases where the "set to zero" is useless, and in such obvious cases, it does no harm just to leave it away:
Code: [Select]
void ReleaseString(char *name)
{
   free(name);
   name = NULL;  /* obviously pointless */
}
In this case, it is quite pointless to set "name" to NULL simply because it is not used afterwards, and because the caller of "ReleaseString" might still hold a pointer, namely the argument to the function just called, so it doesn't help either.

Nevertheless, the compiler will typically tell you that it removed a "dead assignment" and no overhead is created. Should you ever extend the function later on (unlikely in the above example, though), the assignment is already there,  so it is also a matter of "conservative programming" to leave it were it is.

Some people complain if the code is not super-optimized, but I disagree. First make the program run, then make it run fast. Writing fast code is an art by itself, and you typically get *not* fast code by only eliminating dead assignments.

 

Quote from: foleyjo;826825
Code: [Select]
init_hook(&disphook, (HOOKFUNC)dispfunct, NULL); //call to init hook

void init_hook(struct Hook *hook, HOOKFUNC entry, APTR data)  // hook function
{
    hook->h_Entry = (HOOKFUNC)HookEntry;
    hook->h_SubEntry = (HOOKFUNC)entry;
    hook->h_Data = (APTR)data;
}
I guess this is setting up the values in the hook structure.
Right. That's a little bit of data around the actual function.


Quote from: foleyjo;826825
Though I'm not sure what HookEntry is or what data would be expected.
hook->h_Data is for your own hook to use. Thus, it's not MUI that should read this pointer. It is for you to place something there you'd need.

hook->h_Entry is the entry point of your hook. This is where MUI goes if it "calls your hook". The tricky part is not hook->h_Entry, but hook>h_SubEntry. The reason for this is as follows: Some compilers require a short "set up" function to load the base registers correctly. In such a case, you would set hook->h_Entry to some "compiler trampile function", ideally provided by the compiler vendor. This trampoline function would load registers as needed for the specific compiler, and would then call by itself your function through "hook->h_SubEntry".

Whether you need that or your compiler needs that is something I cannot answer in general. The SAS/C compiler, for example, does not need it, though it is in general necessary to declare the hook function with "__saveds", a compiler magic that tells the compiler that it needs to reload some important registers from within the function to operate correctly, because the function is called from somewhere outside, plus a parameter "__asm" that also tells the compiler where to pick the arguments from.

Hence, a typical SAS/C hook function looks like this:
Code: [Select]
unsigned long __asm __saveds myHookFunction(register __a0 struct Hook *hk,register __a2 APTR object, register __a1 APTR message)
{
...
}
But again, this is very compiler specific, and the above magic only works for SAS/C. For any other compiler, hook->hk_Entry should be some compiler magic (documented, hopefully, in the compiler manual) and hook->hk_SubEntry points to your code.


Quote from: foleyjo;826825
I'm also guessing that the 2nd patameter passed is the function that you would call

Code: [Select]
LONG dispfunct(struct Hook *hook, const char **array, struct ListItems *item)
{
    if (rom)
        array[0] = item->Name;
    else
        array[0] = "\033bName";
    return(0);
}
and this is where I really get confused.
Hooks just receive three pointers (in registers a0,a2 and a1). What they actually *mean* is part of the infrastructure the hook is used for. In other words, the MUI documentation should say what the "object" and the "message" of the hook is. It seems, in this particular case (though I do not know) that "object" is a character array and "message" is a pointer to struct ListItems. Again, please check the MUI docs for this.



Quote from: foleyjo;826825
I call this hook in the MUI List with MUIA_List_DisplayHook, &disphook .
How does it know what array and items are as I don't appear to pass anything to them??
The array and the list items are prepared by MUI, and it calls your code with these arguments. the function should probably better be declared as

Code: [Select]
LONG dispfunct(struct Hook *hook, APTR carray, APTR citem)
{
const char **array = (const char **)carray;
struct ListItems *items = (struct ListItems *)citem;
....
}
because this would make more clear that the code actually receives carray and citem as "generic pointers", and you explicitly need to tell the compiler what they are. The hook declaration itself (as defined in the utility library) does *not* know. It only knows that three parameters are coming. One "struct Hook *", and two other generic pointers of unknown type. You have (or should) convert them manually to what they actually mean.

What they actually mean is specific to the use case of the hook, so in this case, MUI listviews. It should state somewhere in the manual.
Title: Re: Executing c command in c
Post by: foleyjo on June 09, 2017, 09:28:27 PM
Thanks guys, you've given me a lot to work with and to consider.

This is a steep learning curve for me and once I want to say how great you have all been.

Things have slowed down for me now. Going over everything I've done and everything you have all said very carefully.
After all the advice I've been given I feel committed to get something finished and not let you feel you have wasted time helping me.