Discussion:
Fun trick
Add Reply
Arne Vajhøj
2025-01-08 01:58:29 UTC
Reply
Permalink
During some troubleshooting over at VSI forum hb told me that
running a shareable image will execute LIB$INITIALIZE
functions.

That made me write this:

$ type lib.pas
[inherit('sys$library:pascal$lib_routines', 'sys$library:starlet')]
module lib(input, output);

[global]
procedure check;

var
imgnam : varying [1024] of char;

begin
lib$getjpi(item_code := jpi$_imagname, resultant_string :=
imgnam.body, resultant_length := imgnam.length);
if index(imgnam, ']libshr.EXE') > 0 then begin
writeln('This is a shareable image to link against not run');
$exit(ss$_normal);
end;
end;

[global]
procedure say;

begin
writeln('Hi');
end;

end.
$ type prg.pas
program prg(input,output);

[external]
procedure say; external;

begin
say;
end.
$ type trick.mar
.title trick
.extrn lib$initialize
.psect lib$initialize long,nopic,con,gbl,noshr,noexe,nowrt
.address check
.end
$ macro trick
$ pas lib
$ link/share=libshr lib + trick + sys$input/opt
SYMBOL_VECTOR=(say=PROCEDURE)
$
$ define/nolog libshr sys$disk:[]libshr
$ pas prg
$ link prg + sys$input/opt
libshr/share
$
$ run prg
Hi
$ run libshr
This is a shareable image to link against not run

:-)

Arne
Lawrence D'Oliveiro
2025-01-08 02:33:07 UTC
Reply
Permalink
During some troubleshooting over at VSI forum hb told me that running a
shareable image will execute LIB$INITIALIZE functions.
Wonder why shareable images had that .EXE file extension, eh ...
John Reagan
2025-01-08 17:37:44 UTC
Reply
Permalink
Post by Arne Vajhøj
During some troubleshooting over at VSI forum hb told me that
running a shareable image will execute LIB$INITIALIZE
functions.
$ type lib.pas
[inherit('sys$library:pascal$lib_routines', 'sys$library:starlet')]
module lib(input, output);
[global]
procedure check;
var
   imgnam : varying [1024] of char;
begin
   lib$getjpi(item_code := jpi$_imagname, resultant_string :=
imgnam.body, resultant_length := imgnam.length);
   if index(imgnam, ']libshr.EXE') > 0 then begin
      writeln('This is a shareable image to link against not run');
      $exit(ss$_normal);
   end;
end;
[global]
procedure say;
begin
   writeln('Hi');
end;
end.
$ type prg.pas
program prg(input,output);
[external]
procedure say; external;
begin
   say;
end.
$ type trick.mar
        .title  trick
        .extrn  lib$initialize
        .psect  lib$initialize long,nopic,con,gbl,noshr,noexe,nowrt
        .address check
        .end
$ macro trick
$ pas lib
$ link/share=libshr lib + trick + sys$input/opt
SYMBOL_VECTOR=(say=PROCEDURE)
$
$ define/nolog libshr sys$disk:[]libshr
$ pas prg
$ link prg + sys$input/opt
libshr/share
$
$ run prg
Hi
$ run libshr
This is a shareable image to link against not run
:-)
Arne
Pascal has an [INITIALIZE] attribute that you can put on the PROCEDURE
CHECK and the compiler should do the contribution to the LIB$INITIALIZE
PSECT.

And you should include LIB$INITIALIZE (the code module, not the data
PSECT) from STARLET when linking. The x86 linker will do that for you,
but the Alpha and Itanium linkers do not.
Arne Vajhøj
2025-01-08 19:36:13 UTC
Reply
Permalink
Post by John Reagan
Post by Arne Vajhøj
During some troubleshooting over at VSI forum hb told me that
running a shareable image will execute LIB$INITIALIZE
functions.
...
Post by John Reagan
Pascal has an [INITIALIZE] attribute that you can put on the PROCEDURE
CHECK and the compiler should do the contribution to the LIB$INITIALIZE
PSECT.
And you should include LIB$INITIALIZE (the code module, not the data
PSECT) from STARLET when linking.  The x86 linker will do that for you,
but the Alpha and Itanium linkers do not.
Zweite Sehr Verbesserte Ausgabe:

$ type lib.pas
[inherit('sys$library:pascal$lib_routines', 'sys$library:starlet')]
module lib(input, output);

[initialize]
procedure check;

var
imgnam : varying [1024] of char;

begin
lib$getjpi(item_code := jpi$_imagname, resultant_string :=
imgnam.body, resultant_length := imgnam.length);
if index(imgnam, ']libshr.EXE') > 0 then begin
writeln('This is a shareable image to link against not run');
$exit(ss$_normal);
end;
end;

[global]
procedure say;

begin
writeln('Hi');
end;

end.
$ type prg.pas
program prg(input,output);

[external]
procedure say; external;

begin
say;
end.
$ pas lib
$ link/share=libshr lib + sys$library:starlet/lib/incl=lib$initialize +
sys$input/opt
SYMBOL_VECTOR=(say=PROCEDURE)
$
$ define/nolog libshr sys$disk:[]libshr
$ pas prg
$ link prg + sys$input/opt
libshr/share
$
$ run prg
Hi
$ run libshr
This is a shareable image to link against not run

Arne
John Reagan
2025-01-08 20:42:07 UTC
Reply
Permalink
Post by Arne Vajhøj
Post by John Reagan
Post by Arne Vajhøj
During some troubleshooting over at VSI forum hb told me that
running a shareable image will execute LIB$INITIALIZE
functions.
...
Post by John Reagan
Pascal has an [INITIALIZE] attribute that you can put on the PROCEDURE
CHECK and the compiler should do the contribution to the
LIB$INITIALIZE PSECT.
And you should include LIB$INITIALIZE (the code module, not the data
PSECT) from STARLET when linking.  The x86 linker will do that for
you, but the Alpha and Itanium linkers do not.
$ type lib.pas
[inherit('sys$library:pascal$lib_routines', 'sys$library:starlet')]
module lib(input, output);
[initialize]
procedure check;
var
   imgnam : varying [1024] of char;
begin
   lib$getjpi(item_code := jpi$_imagname, resultant_string :=
imgnam.body, resultant_length := imgnam.length);
   if index(imgnam, ']libshr.EXE') > 0 then begin
      writeln('This is a shareable image to link against not run');
      $exit(ss$_normal);
   end;
end;
[global]
procedure say;
begin
   writeln('Hi');
end;
end.
$ type prg.pas
program prg(input,output);
[external]
procedure say; external;
begin
   say;
end.
$ pas lib
$ link/share=libshr lib + sys$library:starlet/lib/incl=lib$initialize +
sys$input/opt
SYMBOL_VECTOR=(say=PROCEDURE)
$
$ define/nolog libshr sys$disk:[]libshr
$ pas prg
$ link prg + sys$input/opt
libshr/share
$
$ run prg
Hi
$ run libshr
This is a shareable image to link against not run
Arne
And the compiler also supports the Extended Pascal TO BEGIN DO and TO
END DO statements. TO BEGIN DO is just the same as [INITIALIZE] but
allows any statement. TO END DO is an exit handler (registered with an
initialization routine).
Arne Vajhøj
2025-01-08 21:32:41 UTC
Reply
Permalink
Post by John Reagan
And the compiler also supports the Extended Pascal TO BEGIN DO and TO
END DO statements.  TO BEGIN DO is just the same as [INITIALIZE] but
allows any statement.  TO END DO is an exit handler (registered with an
initialization routine).
$ type prg.pas
program prg(input,output);

[external]
procedure say; external;

begin
writeln('2');
say;
writeln('4');
end.
$ type lib.pas
module lib(input, output);

[global]
procedure say;

begin
writeln('3');
end;

to begin do writeln('1');
to end do writeln('5');

end.
$ pas lib
$ link/share=libshr lib + sys$library:starlet/lib/incl=lib$initialize +
sys$input/opt
SYMBOL_VECTOR=(say=PROCEDURE)
$
$ define/nolog libshr sys$disk:[]libshr
$ pas prg
$ link prg + sys$input/opt
libshr/share
$
$ run prg
1
2
3
4
5

Arne
Arne Vajhøj
2025-01-08 21:37:21 UTC
Reply
Permalink
Post by Arne Vajhøj
Post by John Reagan
And the compiler also supports the Extended Pascal TO BEGIN DO and TO
END DO statements.  TO BEGIN DO is just the same as [INITIALIZE] but
allows any statement.  TO END DO is an exit handler (registered with
an initialization routine).
$ type prg.pas
program prg(input,output);
[external]
procedure say; external;
begin
    writeln('2');
    say;
    writeln('4');
end.
$ type lib.pas
module lib(input, output);
[global]
procedure say;
begin
    writeln('3');
end;
to begin do writeln('1');
to end do writeln('5');
end.
$ pas lib
$ link/share=libshr lib + sys$library:starlet/lib/incl=lib$initialize +
sys$input/opt
SYMBOL_VECTOR=(say=PROCEDURE)
$
$ define/nolog libshr sys$disk:[]libshr
$ pas prg
$ link prg + sys$input/opt
libshr/share
$
$ run prg
1
2
3
4
5
Or ditching the shareable image that are no longer relevant:

$ type prg2.pas
[inherit('lib2')]
program prg2(input,output);

begin
writeln('2');
say;
writeln('4');
end.
$ type lib2.pas
module lib2(input, output);

procedure say;

begin
writeln('3');
end;

to begin do writeln('1');
to end do writeln('5');

end.
$ pas/env lib2
$ pas prg2
$ link prg2 + lib2
$ run prg2
1
2
3
4
5

Arne
Arne Vajhøj
2025-01-08 21:38:51 UTC
Reply
Permalink
Post by John Reagan
And the compiler also supports the Extended Pascal TO BEGIN DO and TO
END DO statements.  TO BEGIN DO is just the same as [INITIALIZE] but
allows any statement.  TO END DO is an exit handler (registered with an
initialization routine).
The compiler tell me that those two are only valid in modules
not in programs.

They are probably most useful for modules, but why not allow
them for programs?

Arne
John Reagan
2025-01-14 02:08:11 UTC
Reply
Permalink
Post by Arne Vajhøj
Post by John Reagan
And the compiler also supports the Extended Pascal TO BEGIN DO and TO
END DO statements.  TO BEGIN DO is just the same as [INITIALIZE] but
allows any statement.  TO END DO is an exit handler (registered with
an initialization routine).
The compiler tell me that those two are only valid in modules
not in programs.
They are probably most useful for modules, but why not allow
them for programs?
Arne
Good question. Extended Pascal says only MODULEs so we just didn't
think about allowing them in PROGRAMs even though we allow [INITIALIZE].
Lawrence D'Oliveiro
2025-01-14 04:38:48 UTC
Reply
Permalink
Post by John Reagan
Extended Pascal says only MODULEs so we just didn't
think about allowing them in PROGRAMs even though we allow [INITIALIZE].
Not sure what the point would be in having them in PROGRAMs, anyway.
Arne Vajhøj
2025-01-14 16:03:27 UTC
Reply
Permalink
### They are probably most useful for modules, but why not allow
### them for programs?
Post by Lawrence D'Oliveiro
Post by John Reagan
Extended Pascal says only MODULEs so we just didn't
think about allowing them in PROGRAMs even though we allow [INITIALIZE].
Not sure what the point would be in having them in PROGRAMs, anyway.
Definitely most useful for modules. I was just wondering
why not allow it in programs as well.

It seems to have been explicit disallowed. The error message is:

%PASCAL-E-TOPROGRAM, TO BEGIN/END DO not allowed in PROGRAM

If one start to look for something useful then I would say that
TO BEGIN is just code after PROGRAM BEGIN, but TO END is more
than just code before PROGRAM END as it get triggered by other
program exits as well.

Demo:

$ type m.pas
module m(input,output);

to begin do writeln('m to begin');
to end do writeln('m to end');

end.
$ type p.pas
[inherit('m', 'sys$library:starlet')]
program p(input,output);

[initialize]
procedure init;

begin
writeln('init');
end;

procedure done;

begin
writeln('done');
end;

var
ent : integer64;
desblk : array [1..4] of integer;
cond : integer;

begin
writeln('begin');
desblk[1] := 0;
desblk[2] := iaddress(done);
desblk[3] := 0;
desblk[4] := iaddress(cond);
$dclexh(desblk);
$get_entropy(ent, 8);
if (ent mod 2) = 0 then $exit(SS$_NORMAL);
$canexh(desblk);
writeln('end');
end.
$ pas/env m
$ pas p
$ link p + m
$ run p
m to begin
init
begin
end
m to end
$ run p
m to begin
init
begin
done
m to end
$ run p
m to begin
init
begin
end
m to end
$ run p
m to begin
init
begin
done
m to end

Arne
Arne Vajhøj
2025-01-14 16:41:21 UTC
Reply
Permalink
   $get_entropy(ent, 8);
That one is of course VMS x86-64 specific.

More portable and less random:

ent := 42;

:-)

Arne
John Reagan
2025-01-15 14:20:34 UTC
Reply
Permalink
Post by Arne Vajhøj
### They are probably most useful for modules, but why not allow
### them for programs?
Post by Lawrence D'Oliveiro
Post by John Reagan
Extended Pascal says only MODULEs so we just didn't
think about allowing them in PROGRAMs even though we allow [INITIALIZE].
Not sure what the point would be in having them in PROGRAMs, anyway.
Definitely most useful for modules. I was just wondering
why not allow it in programs as well.
%PASCAL-E-TOPROGRAM, TO BEGIN/END DO not allowed in PROGRAM
If one start to look for something useful then I would say that
TO BEGIN is just code after PROGRAM BEGIN, but TO END is more
than just code before PROGRAM END as it get triggered by other
program exits as well.
$ type m.pas
module m(input,output);
to begin do writeln('m to begin');
to end do writeln('m to end');
end.
$ type p.pas
[inherit('m', 'sys$library:starlet')]
program p(input,output);
[initialize]
procedure init;
begin
   writeln('init');
end;
procedure done;
begin
   writeln('done');
end;
var
   ent : integer64;
   desblk : array [1..4] of integer;
   cond : integer;
begin
   writeln('begin');
   desblk[1] := 0;
   desblk[2] := iaddress(done);
   desblk[3] := 0;
   desblk[4] := iaddress(cond);
   $dclexh(desblk);
   $get_entropy(ent, 8);
   if (ent mod 2) = 0 then $exit(SS$_NORMAL);
   $canexh(desblk);
   writeln('end');
end.
$ pas/env m
$ pas p
$ link p + m
$ run p
m to begin
init
begin
end
m to end
$ run p
m to begin
init
begin
done
m to end
$ run p
m to begin
init
begin
end
m to end
$ run p
m to begin
init
begin
done
m to end
Arne
I just looked. The TO BEGIN/END DO was added back in 1987 by a former
developer (not me). I suspect that since the Extended Pascal standard
only allows them in MODULEs, not PROGRAMs, that he just followed the
draft standard at that point without considering that TO END DO is a
little better than "code at the end".
hb0815
2025-01-08 22:11:46 UTC
Reply
Permalink
Post by Arne Vajhøj
During some troubleshooting over at VSI forum hb told me that
running a shareable image will execute LIB$INITIALIZE
functions.
Yeah, but ... You do not need init code for this.
Arne Vajhøj
2025-01-08 22:20:50 UTC
Reply
Permalink
Post by hb0815
Post by Arne Vajhøj
During some troubleshooting over at VSI forum hb told me that
running a shareable image will execute LIB$INITIALIZE
functions.
Yeah, but ... You do not need init code for this.
Oh.

Can one get a transfer address into a shareable image?

Arne
hb0815
2025-01-08 22:31:03 UTC
Reply
Permalink
Post by Arne Vajhøj
Post by hb0815
Post by Arne Vajhøj
During some troubleshooting over at VSI forum hb told me that
running a shareable image will execute LIB$INITIALIZE
functions.
Yeah, but ... You do not need init code for this.
Oh.
Can one get a transfer address into a shareable image?
Arne
$ sh sys/noproc/full
OpenVMS V8.4-2L2 on node EISNER 8-JAN-2025 17:29:41.07 Uptime 3
22:58:56
AlphaServer ES40
$
$ ty s.c
#include <stdio.h>
static int firstFunctionInThisModuleBecomesTheEntryPoint () {
printf ("It's the shareable, stupid!\n");
return 1;
}
int foo () {
printf ("This is %s\n", __func__);
return 1;
}
$ cc s/stand=rel
$ link/share s,tt:/opt ! symbol_v=(foo=proc)
symbol_v=(foo=proc)
Exit
$ r s
It's the shareable, stupid!
$
Arne Vajhøj
2025-01-09 01:00:59 UTC
Reply
Permalink
Post by hb0815
Post by Arne Vajhøj
Post by hb0815
Post by Arne Vajhøj
During some troubleshooting over at VSI forum hb told me that
running a shareable image will execute LIB$INITIALIZE
functions.
Yeah, but ... You do not need init code for this.
Oh.
Can one get a transfer address into a shareable image?
$ ty s.c
#include <stdio.h>
static int firstFunctionInThisModuleBecomesTheEntryPoint () {
  printf ("It's the shareable, stupid!\n");
  return 1;
}
int foo () {
  printf ("This is %s\n", __func__);
  return 1;
}
$ cc s/stand=rel
$ link/share s,tt:/opt ! symbol_v=(foo=proc)
symbol_v=(foo=proc)
 Exit
$ r s
It's the shareable, stupid!
Hmmm.

It works in C, but it does not seem to work in
any other language (tested with Pascal and Fortran).

$ type prg.c
#include <stdio.h>

extern void say();

int main(int argc, char *argv[])
{
say();
return 0;
}

$ type lib.c
#include <stdio.h>

int ooops()
{
printf("This is a shareable image to link against not run\n");
return 1;
}

void say()
{
printf("Hi\n");
}

$ cc lib
$ link/share=libshr lib + sys$input/opt
symbol_vector=(say=procedure)
$
$ define/nolog libshr sys$disk:[]libshr
$ cc prg
$ link prg + sys$input/opt
libshr/share
$
$ run prg
Hi
$ run libshr
This is a shareable image to link against not run
$ type prg.pas
program prg(input,output);

[external]
procedure say; external;

begin
say;
end.
$ type lib.pas
[inherit('sys$library:starlet')]
module lib(input, output);

[global]
procedure ooops;

begin
writeln('This is a shareable image to link against not run');
$exit(ss$_normal);
end;

[global]
procedure say;

begin
writeln('Hi');
end;

end.
$ pas lib
$ link/share=libshr lib + sys$input/opt
symbol_vector=(say=procedure)
$
$ define/nolog libshr sys$disk:[]libshr
$ pas prg
$ link prg + sys$input/opt
libshr/share
$
$ run prg
Hi
$ run libshr
%DCL-E-NOTFR, no transfer address
$ type prg.for
program prg
call say
end
$ type lib.for
subroutine ooops
include '($ssdef)'
write(*,*) 'This is a shareable image to link against not run'
call sys$exit(ss$_normal)
end
c
subroutine say
write(*,*) 'Hi'
end
$ for lib
$ link/share=libshr lib + sys$input/opt
symbol_vector=(say=procedure)
$
$ define/nolog libshr sys$disk:[]libshr
$ for prg
$ link prg + sys$input/opt
libshr/share
$
$ run prg
Hi
$ run libshr
%DCL-E-NOTFR, no transfer address

I assume the difference relates to user code main not being
the real program entry.

Arne
hb0815
2025-01-09 13:01:51 UTC
Reply
Permalink
Post by Arne Vajhøj
Hmmm.
It works in C, but it does not seem to work in
any other language (tested with Pascal and Fortran).
...
I assume the difference relates to user code main not being
the real program entry.
So, make it a main entry.

This is on Alpha. It should work on IA64 and x86. I don't have access to
any of the latter systems. If this does not work on these systems, I
know how to make it work, anyway.

$ gdiff -ub lib.pas-orig lib.pas
--- lib.pas-orig 2025-01-09 07:34:25 -0500
+++ lib.pas 2025-01-09 07:37:14 -0500
@@ -1,19 +1,11 @@
-[inherit('sys$library:starlet')]
-module lib(input, output);
-
-[global]
-procedure ooops;
-
-begin
- writeln('This is a shareable image to link against not run');
- $exit(ss$_normal);
-end;
+program lib(input, output);

[global]
procedure say;
-
begin
writeln('Hi');
end;

+begin
+ writeln('This is a shareable image to link against not run');
end.
$
...
$ def/user libshr sys$disk:[]libshr.exe;
$ r prg
Hi
$ r libshr
This is a shareable image to link against not run
$

and

$ gdiff -ub lib.for-orig lib.for
--- lib.for-orig 2025-01-09 07:43:03 -0500
+++ lib.for 2025-01-09 07:43:25 -0500
@@ -1,7 +1,5 @@
- subroutine ooops
- include '($ssdef)'
+ program ooops
write(*,*) 'This is a shareable image to link against not run'
- call sys$exit(ss$_normal)
end
c
subroutine say
$
...
$ def/user libshr sys$disk:[]libshr.exe;
$ r prg
Hi
$ r libshr
This is a shareable image to link against not run
$

Also, I should have mentioned ... Your shareable with printing a message
from init code can not be used as a "normal" shareable image. The
message will always be printed. That's probably not what you want.

Init code of an image is always run. For a shareable image it is run at
activation time, for a main image it is run at image startup time. For
some reasons I distinguish these two phases although others prefer to
say that "startup" is part of activation.

My shareable image only prints its message if a user (accidently) runs
it as main image.

All this works because VMS defines a weak transfer (or entry) address.
The C compiler in absence of a "main" assigns this weak transfer to the
first function seen in a source module. (I admit, I initially didn't
test this with other compilers; obviously FORTRAN and PASCAL do not
define this). The linker keeps track of the first weak transfer it
encounters and uses it as image transfer, if there is no "strong"
transfer, in C no "main". This makes the shareable image "runnable". If
This often ends up in an unexpected error, if a user (accidently) runs
it as main image: the first (static) function may expect arguments and
may therefore miserably fail. I bet that there are only a few shareable
images that are prepared for being run as a main image.

For C this works at least on Alpha, IA64 and x86. On the latter two this
is more visible, because there is a VMS weak symbol in the symbol table
- of the object module. On Alpha the information is in the EOBJ$C_EEOM
record (and on VAX I assume it is the OBJ$C_EOM record).
Arne Vajhøj
2025-01-13 18:40:46 UTC
Reply
Permalink
Post by hb0815
Post by Arne Vajhøj
Hmmm.
It works in C, but it does not seem to work in
any other language (tested with Pascal and Fortran).
...
I assume the difference relates to user code main not being
the real program entry.
So, make it a main entry.
This is on Alpha. It should work on IA64 and x86. I don't have access to
any of the latter systems. If this does not work on these systems, I
know how to make it work, anyway.
$ gdiff -ub lib.pas-orig lib.pas
$ gdiff -ub lib.for-orig lib.for
For those that do not speak diffish:

$ type prg.c
#include <stdio.h>

extern void say();

int main(int argc, char *argv[])
{
say();
return 0;
}

$ type lib.c
#include <stdio.h>

void say()
{
printf("Hi\n");
}

int main()
{
printf("This is a shareable image to link against not run\n");
return 0;
}

$ cc lib
$ link/share=libshr lib + sys$input/opt
symbol_vector=(say=procedure)
$
$ define/nolog libshr sys$disk:[]libshr
$ cc prg
$ link prg + sys$input/opt
libshr/share
$
$ run prg
Hi
$ run libshr
This is a shareable image to link against not run
$ type prg.pas
program prg(input,output);

[external]
procedure say; external;

begin
say;
end.
$ type lib.pas
program lib(input, output);

[global]
procedure say;

begin
writeln('Hi');
end;

begin
writeln('This is a shareable image to link against not run');
end.
$ pas lib
$ link/share=libshr lib + sys$input/opt
symbol_vector=(say=procedure)
$
$ define/nolog libshr sys$disk:[]libshr
$ pas prg
$ link prg + sys$input/opt
libshr/share
$
$ run prg
Hi
$ run libshr
This is a shareable image to link against not run
$ type prg.for
program prg
call say
end
$ type lib.for
program lib
write(*,*) 'This is a shareable image to link against not run'
end
c
subroutine say
write(*,*) 'Hi'
end
$ for lib
$ link/share=libshr lib + sys$input/opt
symbol_vector=(say=procedure)
$
$ define/nolog libshr sys$disk:[]libshr
$ for prg
$ link prg + sys$input/opt
libshr/share
$
$ run prg
Hi
$ run libshr
This is a shareable image to link against not run

So I think the conclusion must be that one should
not think the traditional grouping:
* executable image
* shareable image
but instead:
* image with transfer address
* image with symbol vector
* image with both transfer address and symbol vector

:-)

I believe that is rare for native code.

But common for non-native code.

$ type prg.py
import lib

if __name__ == '__main__':
lib.say()

$ type lib.py
def say():
print('Hi')

if __name__ == '__main__':
print('This is a library not a program')

$ python prg.py
Hi
$ python lib.py
This is a library not a program
$ type Prg.java
public class Prg {
public static void main(String[] args) {
Lib.say();
}
}
$ type Lib.java
public class Lib {
public static void say() {
System.out.println("Hi");
}
public static void main(String[] args) throws Exception {
System.out.println("This is a library not a program");
}
}
$ javac Prg.java Lib.java
$ java Prg
Hi
$ java Lib
This is a library not a program

Arne
Simon Clubley
2025-01-17 13:11:36 UTC
Reply
Permalink
Post by hb0815
$ gdiff -ub lib.pas-orig lib.pas
$ gdiff -ub lib.for-orig lib.for
Then those people would benefit from learning it IMHO.

It is far, far, superior to VMS DIFF, especially in the unified diff format
used in the above command. If the licence allows it, then it would be an
excellent addition to standard VMS, maybe invoked as a new qualifier on the
VMS DIFF command.

Simon.
--
Simon Clubley, ***@remove_me.eisner.decus.org-Earth.UFP
Walking destinations on a map are further away than they appear.
Craig A. Berry
2025-01-17 14:07:23 UTC
Reply
Permalink
Post by Simon Clubley
Post by hb0815
$ gdiff -ub lib.pas-orig lib.pas
$ gdiff -ub lib.for-orig lib.for
Then those people would benefit from learning it IMHO.
It is far, far, superior to VMS DIFF, especially in the unified diff format
used in the above command. If the licence allows it, then it would be an
excellent addition to standard VMS, maybe invoked as a new qualifier on the
VMS DIFF command.
There are BSD implementations that have the usual Berkeley license:

https://github.com/openbsd/src/tree/master/usr.bin/diff

Don't know about the license but git is available and it should be
possible to get diffs outside a repository with "git diff --noindex".
That said, I tried it just now and couldn't get it to work.
Craig A. Berry
2025-01-17 14:39:37 UTC
Reply
Permalink
Post by Craig A. Berry
it should be
possible to get diffs outside a repository with "git diff --noindex".
That said, I tried it just now and couldn't get it to work.
It does work, but not for VFC files and it cannot handle version numbers
in a filename, so its usefulness on VMS is pretty limited.
hb0815
2025-01-19 12:25:25 UTC
Reply
Permalink
Post by Craig A. Berry
Post by Craig A. Berry
it should be
possible to get diffs outside a repository with "git diff --noindex".
That said, I tried it just now and couldn't get it to work.
It does work, but not for VFC files and it cannot handle version numbers
in a filename, so its usefulness on VMS is pretty limited.
Using git to get a the output in the Unix/GNU diff style seems overkill
to me.

As you probably know, if you can get the GNV diff utility, you can run
it from DCL.

To handle VMS file version, there is a workaround. Assumed gdiff is the
DCL symbol for the GNV diff utility:

$ pipe define/user decc$filename_unix_only 0 && -
gdiff -ub sys$disk:[]x.com;1 sys$disk:[]x.com;2

As indicated, you may need a full file spec and the files have to be in
Stream_LF record format.

PS: For VMS git you should convert all files to Stream_LF and purge all
the versions before you attempt to use git. Especially if you want to
switch to/check out another branch or commit. VMS git is a port of the
open sources, in that sense and despite of its name, it is more a GNV
than a VMS tool. It helps, if you already have the sources in Stream_LF
(probably copied from a non-VMS system) or an editor on VMS that creates
this record format by default. Such editors exist. And, as you probably
noticed, not everything works as expected in the initial version of VMS git.
Craig A. Berry
2025-01-20 23:34:53 UTC
Reply
Permalink
Post by hb0815
Post by Craig A. Berry
Post by Craig A. Berry
it should be
possible to get diffs outside a repository with "git diff --noindex".
That said, I tried it just now and couldn't get it to work.
It does work, but not for VFC files and it cannot handle version numbers
in a filename, so its usefulness on VMS is pretty limited.
Using git to get a the output in the Unix/GNU diff style seems overkill
to me.
As you probably know, if you can get the GNV diff utility, you can run
it from DCL.
git is available for OpenVMS x86. GNV is not yet, as far as I have seen,
so it seemed worth giving a quick try with something people may already
have installed. And to be a bit pedantic, there is no "GNV diff"; there
is GNU diff, which may have been included in various GNV packages. It
is also available various other places, such as

http://www.antinode.info/dec/sw/diffutils.html
Post by hb0815
To handle VMS file version, there is a workaround. Assumed gdiff is the
$ pipe define/user decc$filename_unix_only 0 && -
  gdiff -ub sys$disk:[]x.com;1 sys$disk:[]x.com;2
As indicated, you may need a full file spec and the files have to be in
Stream_LF record format.
The GNU diff I've been using for 24 years does not have those
limitations, and also conveniently does command-line redirection:

https://www.digiater.com/openvms/freeware/v50/gnudiffutils/

Sometime I need to get up-to-date on Steven Schweda's port and see what
I'm missing.
hb0815
2025-01-22 11:58:01 UTC
Reply
Permalink
On 1/21/25 00:34, Craig A. Berry wrote:
...
Post by Craig A. Berry
git is available for OpenVMS x86. GNV is not yet, as far as I have seen,
so it seemed worth giving a quick try with something people may already
have installed.  And to be a bit pedantic, there is no "GNV diff"; there
is GNU diff, which may have been included in various GNV packages.  It
is also available various other places, such as
http://www.antinode.info/dec/sw/diffutils.html
...
The GNU diff I've been using for 24 years does not have those
...
$ sh sys/noproc/full
OpenVMS V8.4-2L2 on node EISNER 21-JAN-2025 08:32:02.26 Uptime 3
08:13:50
AlphaServer ES40
$ sh symb gdiff
GDIFF == "MC GNV$GNU:[USR.BIN]DIFF"
$ gdiff -v
diff (GNU diffutils) 3.5
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later
<http://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Written by Paul Eggert, Mike Haertel, David Hayes,
Richard Stallman, and Len Tower.
$
$ gdiff x.txt;1 x.txt;2
diff: x.txt;1: no such file or directory
diff: x.txt;2: no such file or directory
$

I did not expect a GNV utility to handle VMS versions. I incorrectly
assumed that decc$filename_unix_only would be required. It is not. But I
do not know why it requires more than just the filenames.

$ gdiff sys$disk:[]x.txt;1 sys$disk:[]x.txt;2
diff: sys$disk:[]x.txt;1: illegal seek
$
$ copy x.txt;1 y.txt
$ gdiff -ub y.txt x.txt
diff: y.txt: illegal seek
$
$ pipe dir/full x.txt;1 |search sys$pipe format
Record format: Variable length, maximum 0 bytes, longest 39 bytes
$
$ convert/fdl="record; format stream_lf" x.txt;1 xx.txt
$ convert/fdl="record; format stream_lf" x.txt;2 xx.txt
$ gdiff sys$disk:[]xx.txt;1 sys$disk:[]xx.txt;2
0a1
Post by Craig A. Berry
huhu
$

And I do not expect that the GNV "diff (GNU diffutils)" utility does
command-line redirection on VMS.

$ gdiff sys$disk:[]xx.txt;1 sys$disk:[]xx.txt;2 >x.x
diff: extra operand '>x.x'
diff: Try 'diff --help' for more information.
$
Post by Craig A. Berry
https://www.digiater.com/openvms/freeware/v50/gnudiffutils/
"This is a port of GNU diffutils 2.7.2 for OpenVMS."

It contains VMS-specific source code, which is not in the current
diffutils-3.10.tar.xz.

Anyway, on systems where there is no "diff (GNU diffutils)", it seems
worth to build it from the mentioned sources instead of using git - just
my opinion.
Robert A. Brooks
2025-01-22 23:30:05 UTC
Reply
Permalink
Post by hb0815
GDIFF == "MC GNV$GNU:[USR.BIN]DIFF"
Is there any reason to choose this over
GDIFF == "$GNV$GNU:[USR.BIN]DIFF"
No; both do the same thing and both forms of the syntax will never go away,
even if the "MCR" form is technically unsupported.

I'm not saying it's unsupported, but several aspects of VMS are probably unsupported
(like EDT), but will never go away.
--
-- Rob
Simon Clubley
2025-01-24 13:05:11 UTC
Reply
Permalink
Post by Robert A. Brooks
Post by hb0815
GDIFF == "MC GNV$GNU:[USR.BIN]DIFF"
Is there any reason to choose this over
GDIFF == "$GNV$GNU:[USR.BIN]DIFF"
No; both do the same thing and both forms of the syntax will never go away,
even if the "MCR" form is technically unsupported.
I'm not saying it's unsupported, but several aspects of VMS are probably unsupported
(like EDT), but will never go away.
$ set response/mode=good_natured

TECO went away...

Simon.
--
Simon Clubley, ***@remove_me.eisner.decus.org-Earth.UFP
Walking destinations on a map are further away than they appear.
Dave Froble
2025-01-25 00:59:07 UTC
Reply
Permalink
Post by Simon Clubley
Post by Robert A. Brooks
Post by hb0815
GDIFF == "MC GNV$GNU:[USR.BIN]DIFF"
Is there any reason to choose this over
GDIFF == "$GNV$GNU:[USR.BIN]DIFF"
No; both do the same thing and both forms of the syntax will never go away,
even if the "MCR" form is technically unsupported.
I'm not saying it's unsupported, but several aspects of VMS are probably unsupported
(like EDT), but will never go away.
$ set response/mode=good_natured
TECO went away...
Simon.
No, it just didn't tag along ...
--
David Froble Tel: 724-529-0450
Dave Froble Enterprises, Inc. E-Mail: ***@tsoft-inc.com
DFE Ultralights, Inc.
170 Grimplin Road
Vanderbilt, PA 15486
Arne Vajhøj
2025-01-13 18:45:50 UTC
Reply
Permalink
Post by hb0815
Post by Arne Vajhøj
Hmmm.
It works in C, but it does not seem to work in
any other language (tested with Pascal and Fortran).
...
I assume the difference relates to user code main not being
the real program entry.
So, make it a main entry.
This is on Alpha. It should work on IA64 and x86. I don't have access to
any of the latter systems. If this does not work on these systems, I
know how to make it work, anyway.
$ gdiff -ub lib.pas-orig lib.pas
$ gdiff -ub lib.for-orig lib.for
Also, I should have mentioned ... Your shareable with printing a message
from init code can not be used as a "normal" shareable image. The
message will always be printed. That's probably not what you want.
Init code of an image is always run. For a shareable image it is run at
activation time, for a main image it is run at image startup time. For
some reasons I distinguish these two phases although others prefer to
say that "startup" is part of activation.
My shareable image only prints its message if a user (accidently) runs
it as main image.
My Pascal code a few revisions back looked like:

lib$getjpi(item_code := jpi$_imagname, resultant_string :=
imgnam.body, resultant_length := imgnam.length);
if index(imgnam, ']libshr.EXE') > 0 then begin
writeln('This is a shareable image to link against not run');
$exit(ss$_normal);
end;

Maybe not elegant, but it did check.
Post by hb0815
All this works because VMS defines a weak transfer (or entry) address.
The C compiler in absence of a "main" assigns this weak transfer to the
first function seen in a source module. (I admit, I initially didn't
test this with other compilers; obviously FORTRAN and PASCAL do not
define this). The linker keeps track of the first weak transfer it
encounters and uses it as image transfer, if there is no "strong"
transfer, in C no "main". This makes the shareable image "runnable".
There are probably a lot of shareable images written in C out
there where the developer has no idea that they are runnable or
what code will run.

Arne
Lawrence D'Oliveiro
2025-01-13 22:18:48 UTC
Reply
Permalink
There are probably a lot of shareable images written in C out there
where the developer has no idea that they are runnable or what code will
run.
If it has no main(), then nothing will run.
Arne Vajhøj
2025-01-13 22:55:59 UTC
Reply
Permalink
Post by Lawrence D'Oliveiro
There are probably a lot of shareable images written in C out there
where the developer has no idea that they are runnable or what code will
run.
If it has no main(), then nothing will run.
That is what the developer thinks.

But it is not so.

$ type lawrence.c
#include <stdio.h>

int foobar()
{
printf("Hi Lawrence\n");
return 1;
}
$ cc lawrence
$ link/share lawrence
$ run lawrence
Hi Lawrence

No main() and it runs anyway.

Arne
John Reagan
2025-01-14 02:11:29 UTC
Reply
Permalink
Post by Arne Vajhøj
Post by Lawrence D'Oliveiro
There are probably a lot of shareable images written in C out there
where the developer has no idea that they are runnable or what code will
run.
If it has no main(), then nothing will run.
That is what the developer thinks.
But it is not so.
$ type lawrence.c
#include <stdio.h>
int foobar()
{
    printf("Hi Lawrence\n");
    return 1;
}
$ cc lawrence
$ link/share  lawrence
$ run lawrence
Hi Lawrence
No main() and it runs anyway.
Arne
COBOL paragraphs behave like C. First routine/PARAGRAPH gets a WEAK
transfer address. Linker finds the first one. Shuffle modules around
and you'll get different transfer addresses. Pascal just creates a
STRONG transfer address for a PROGRAM statement.
Arne Vajhøj
2025-01-14 16:06:23 UTC
Reply
Permalink
Post by Arne Vajhøj
Post by Lawrence D'Oliveiro
There are probably a lot of shareable images written in C out there
where the developer has no idea that they are runnable or what code will
run.
If it has no main(), then nothing will run.
That is what the developer thinks.
But it is not so.
$ type lawrence.c
#include <stdio.h>
int foobar()
{
     printf("Hi Lawrence\n");
     return 1;
}
$ cc lawrence
$ link/share  lawrence
$ run lawrence
Hi Lawrence
No main() and it runs anyway.
COBOL paragraphs behave like C.  First routine/PARAGRAPH gets a WEAK
transfer address.  Linker finds the first one.
I have many prejudices about COBOL developers.

One of them is that they don't create shareable images.

:-)

Arne
Arne Vajhøj
2025-02-03 01:30:42 UTC
Reply
Permalink
Post by Arne Vajhøj
COBOL paragraphs behave like C.  First routine/PARAGRAPH gets a WEAK
transfer address.  Linker finds the first one.
I have many prejudices about COBOL developers.
One of them is that they don't create shareable images.
:-)
But let us say that someone wanted to create a shareable
image in Cobol.

A typical Cobol program just have all the paragraphs that
get performed and they share state. Simple.

To make them entry points with arguments in a shareable image,
then I assume one need to make them programs with linkage section
and procedure division using.

Normal variables are just passed as arguments. Seems entirely
unproblematic to me.

But how does one share open files between modules?

Fortran and Basic numeric identifiers work across modules.
Pascal can share via inherit. C can use global variables
or just pass as argument.

But how does one do that in Cobol?

Can one mark an fd as global or can one pass a fd as argument?

Arne
Arne Vajhøj
2025-02-03 01:37:50 UTC
Reply
Permalink
Post by Arne Vajhøj
Post by Arne Vajhøj
Post by John Reagan
COBOL paragraphs behave like C. First routine/PARAGRAPH gets a WEAK
transfer address. Linker finds the first one.
I have many prejudices about COBOL developers.
One of them is that they don't create shareable images.
:-)
But let us say that someone wanted to create a shareable
image in Cobol.
A typical Cobol program just have all the paragraphs that
get performed and they share state. Simple.
To make them entry points with arguments in a shareable image,
then I assume one need to make them programs with linkage section
and procedure division using.
Normal variables are just passed as arguments. Seems entirely
unproblematic to me.
But how does one share open files between modules?
Fortran and Basic numeric identifiers work across modules.
Pascal can share via inherit. C can use global variables
or just pass as argument.
But how does one do that in Cobol?
Can one mark an fd as global or can one pass a fd as argument?
Did some reading.

IS GLOBAL in one module and IS EXTERNAL in the other modules?

Arne
Arne Vajhøj
2025-02-03 02:55:52 UTC
Reply
Permalink
Post by Arne Vajhøj
Post by Arne Vajhøj
Post by Arne Vajhøj
COBOL paragraphs behave like C.  First routine/PARAGRAPH gets a WEAK
transfer address.  Linker finds the first one.
I have many prejudices about COBOL developers.
One of them is that they don't create shareable images.
:-)
But let us say that someone wanted to create a shareable
image in Cobol.
A typical Cobol program just have all the paragraphs that
get performed and they share state. Simple.
To make them entry points with arguments in a shareable image,
then I assume one need to make them programs with linkage section
and procedure division using.
Normal variables are just passed as arguments. Seems entirely
unproblematic to me.
But how does one share open files between modules?
Fortran and Basic numeric identifiers work across modules.
Pascal can share via inherit. C can use global variables
or just pass as argument.
But how does one do that in Cobol?
Can one mark an fd as global or can one pass a fd as argument?
Did some reading.
IS GLOBAL in one module and IS EXTERNAL in the other modules?
And what to put in SELECT if FD is EXTERNAL?

Arne
Arne Vajhøj
2025-02-05 02:03:10 UTC
Reply
Permalink
Post by Arne Vajhøj
 >>> COBOL paragraphs behave like C.  First routine/PARAGRAPH gets a WEAK
 >>> transfer address.  Linker finds the first one.
 >>
 >> I have many prejudices about COBOL developers.
 >>
 >> One of them is that they don't create shareable images.
 >>
 >> :-)
 >
 > But let us say that someone wanted to create a shareable
 > image in Cobol.
 >
 > A typical Cobol program just have all the paragraphs that
 > get performed and they share state. Simple.
 >
 > To make them entry points with arguments in a shareable image,
 > then I assume one need to make them programs with linkage section
 > and procedure division using.
 >
 > Normal variables are just passed as arguments. Seems entirely
 > unproblematic to me.
 >
 > But how does one share open files between modules?
 >
 > Fortran and Basic numeric identifiers work across modules.
 > Pascal can share via inherit. C can use global variables
 > or just pass as argument.
 >
 > But how does one do that in Cobol?
 >
 > Can one mark an fd as global or can one pass a fd as argument?
Did some reading.
IS GLOBAL in one module and IS EXTERNAL in the other modules?
And what to put in SELECT if FD is EXTERNAL?
Just EXTERNAL everywhere and no GLOBAL and nothing on SELECT
seems to do the trick.

identification division.
program-id.m2.
*
environment division.
input-output section.
file-control.
select in-file assign to "a.dat" organization is sequential.
select out-file assign to "c.dat" organization is sequential.
*
data division.
file section.
fd in-file external record is varying in size depending on line-len.
01 in-record.
03 in-line pic x(100).
fd out-file external record is varying in size from 0 to 100 depending
on line-len.
01 out-record.
03 out-line pic x(100).
working-storage section.
01 line-len pic 9(8) comp external.
*
procedure division.
main-paragraph.
open input in-file
open output out-file
call "docopy" end-call
close in-file
close out-file
stop run.
end program m2.
*
identification division.
program-id. docopy.
*
environment division.
input-output section.
file-control.
select in-file assign to "a.dat" organization is sequential.
select out-file assign to "c.dat" organization is sequential.
*
data division.
file section.
fd in-file external record is varying in size depending on line-len.
01 in-record.
03 in-line pic x(100).
fd out-file external record is varying in size from 0 to 100 depending
on line-len.
01 out-record.
03 out-line pic x(100).
working-storage section.
01 line-len pic 9(8) comp external.
01 eof-flag pic x.
*
procedure division.
main-paragraph.
move "N" to eof-flag
perform until eof-flag = "Y"
read in-file
at end
move "Y" to eof-flag
not at end
move in-line(1:line-len) to out-line
write out-record
end-read
end-perform.
end program docopy.

Arne

Lawrence D'Oliveiro
2025-01-14 04:39:19 UTC
Reply
Permalink
Post by Arne Vajhøj
Post by Lawrence D'Oliveiro
If it has no main(), then nothing will run.
That is what the developer thinks.
But it is not so.
Try it on a proper POSIXish system.
Loading...