The reason TRIM(ADJUSTL(string)) works is because ADJUSTL and TRIM are functions. This sort of nesting functions as arguments inside another works because TRIM acts on the function result from ADJUSTL.
But UPCASE@ is a subroutine which expects a variable argument, so it can change it. If you write the following, the argument is not a variable.
CALL UPCASE@(ADJUSTL(STRING))
The solution, as you have discovered is to write your own function. But you didn't need to write your own case conversion, you could have just produced a 'function wrapper' for the UPCASE@ subroutine, e.g.
FUNCTION UPCASE(STRING)
CHARACTER(LEN=*), INTENT(IN) :: STRING
CHARACTER(LEN=LEN(STRING)) :: UPCASE
UPCASE = STRING ! Copy the string
CALL UPCASE@(UPCASE) ! Change it to uppercase
END FUNCTION
Note that this version automatically returns a string of the same length as STRING, whereas, your function always returns a string of 120 characters
Once you have this, you can write, for example,
S = ' hello' !< contains leading spaces
print *, UPCASE(ADJUSTL(S)) !< prints 'HELLO '
print *, ADJUSTL(UPCASE(S)) !< prints 'HELLO '
print *, TRIM(ADJUSTL(UPCASE(S))) !< prints 'HELLO'
Hope this helps, it might be useful to wrap other @ subroutines as well.
Regards
David.