Using return should be analogous to exit in both cases...without the pesky side-effect of killing the parent.
What exactly is the issue? Have you traced the process with set -x? [embed set -x into each function in use to capture the flow across each subprocess...]
If you do source it, there are implications which could only be overcome programmatically.
The simple solution is: Don't source the script. If you have to source it, then source it from another script, not from the login prompt (if your concern is that you get logged out when you "exit" the script). If you really have to source it from the shell, start a second shell before you source it.
The main purpose of the script is to set some environment variables, so I MUST source it. However, the same script also manage a set of choices for these variables (i.e., it can create or remove an "environment", list all the "environments", alias an "environment" and then set all the environment variables to the values specified by the particular "environment" chosen). In the "management" case, you do not need to source it, e.g.:
$ test add an_environment
$ test remove another_environment
$ . test set an_environment
---------- Post updated at 12:55 PM ---------- Previous update was at 12:41 PM ----------
Well, if you must source and it must not kill the parent shell, it cannot use exit. Refactor the script so that it does not require it.
If you want to get really ugly, you could wrap the sourcing with something like:
exit() { return $1; }
. script
unset -f exit
Although that will not work if the exit calls occur within a function (the sourced function will return but the sourced script will continue executing).