diff --git a/library/BROWSER b/library/BROWSER
index 5ca7aee9..a23b4691 100644
--- a/library/BROWSER
+++ b/library/BROWSER
@@ -1,16 +1,12 @@
 (DEFINE-FILE-INFO PACKAGE "INTERLISP" READTABLE "INTERLISP" BASE 10)
 
-(FILECREATED "26-Apr-2023 12:41:36" {DSK}<home>larry>il>medley>library>BROWSER.;6 29801  
+(FILECREATED "21-May-2024 18:46:31" {LIB}BROWSER.;2 29502  
 
-      :EDIT-BY "lmm"
+      :EDIT-BY "mth"
 
-      :CHANGES-TO (FNS BROWSER.LEFTFN NUMSPATHS STBROWSER MSPATHS.DISPATCH BROWSER 
-                       BROWSER.WHENFNSCHANGED BRPATHS1 GET.BROWSE.PP.WINDOW 
-                       GET.BROWSE.DESCRIBE.WINDOW BROWSEPP PPREPAINTFN PPRESHAPEFN DESCRIBEREPAINTFN
-                       BROWSERDESCRIBE BROWSER.MIDDLEFN DEDITPROCESSRUNNINGP REDRAWBROWSEGRAPH)
-                  (VARS BROWSERCOMS BROWSER.BORDERS)
+      :CHANGES-TO (FNS BROWSER.LEFTFN)
 
-      :PREVIOUS-DATE "15-Apr-2023 18:55:36" {DSK}<home>larry>il>medley>library>BROWSER.;1)
+      :PREVIOUS-DATE "26-Apr-2023 12:41:36" {LIB}BROWSER.;1)
 
 
 (PRETTYCOMPRINT BROWSERCOMS)
@@ -202,28 +198,29 @@
                 (RETURN ENTRY])
 
 (BROWSER.LEFTFN
-  [LAMBDA (NODE NWINDOW)                                     (* ; "Edited 26-Apr-2023 12:41 by lmm")
+  [LAMBDA (NODE NWINDOW)                                     (* ; "Edited 21-May-2024 18:40 by mth")
+                                                             (* ; "Edited 26-Apr-2023 12:41 by lmm")
                                                              (* ; "Edited 31-Mar-87 11:16 by jop")
                                                              (* ; 
                                                  "function that is applied upon selection of a node.")
     (PROG (FN SELECTION)
-          (IF (NULL NODE)
-              THEN (RETURN)
+          (if (NULL NODE)
+              then (RETURN)
                    (MOVEW NWINDOW)                           (* ; 
                                                        " really want to just drag the content around")
                    (RETURN))
-          (IF (NULL (SETQ FN (FETCH NODELABEL OF NODE)))
-              THEN (RETURN))
-          [SETQ SELECTION (MENU (CREATE MENU
+          (if (NULL (SETQ FN (fetch NODELABEL of NODE)))
+              then (RETURN))
+          [SETQ SELECTION (MENU (create MENU
                                        ITEMS _ '(CallsFrom CallsTo Edit Show InspectCode]
 
      (* ;; "Mot implemented: Ignore Avoid")
 
           (DESTRUCTURING-BIND (FROM TO INVERTED AVOIDING SEPARATE NOTRACE MARKING DEPTH)
-                 (FOR BW IN BROWSERWINDOWS WHEN (EQ (FETCH (BROWSEWIN WINDOW) OF BW)
-                                                    NWINDOW) DO (RETURN (FETCH (BROWSEWIN ARGS)
-                                                                           OF BW))
-                    FINALLY (PROMPTPRINT "No browser window found for" FN)
+                 (for BW in BROWSERWINDOWS when (EQ (fetch (BROWSEWIN WINDOW) of BW)
+                                                    NWINDOW) do (RETURN (fetch (BROWSEWIN ARGS)
+                                                                           of BW))
+                    finally (PROMPTPRINT "No browser window found for" FN)
                           (RETURN))
 
                  (* ;; "Now we have the arguments to MSPATHS .. insert this node?")
@@ -245,11 +242,11 @@
                      NIL
                      (Ignore                                 (* ; "local ignore"))
                      (Avoid                                  (* ; " global ignore"))
-                     (Edit (ED FN (IF (HASDEF FN 'FNS)
-                                      THEN 'FNS
-                                    ELSEIF (HASDEF FN 'FUNCTIONS)
-                                      THEN 'FUNCTIONS
-                                    ELSE (PROMPTPRINT FN "no definition")
+                     (Edit (ED FN (if (HASDEF FN 'FNS)
+                                      then '(FNS :DONTWAIT)
+                                    elseif (HASDEF FN 'FUNCTIONS)
+                                      then '(FUNCTIONS :DONTWAIT)
+                                    else (PROMPTPRINT FN "no definition")
                                          NIL)))
                      (Show (CL:UNLESS (EQ FN (WINDOWPROP (GET.BROWSE.PP.WINDOW)
                                                     'FNBROWSED))
@@ -544,10 +541,10 @@
 (BROWSER T)
 )
 (DECLARE%: DONTCOPY
-  (FILEMAP (NIL (2085 28437 (MSPATHS.DISPATCH 2095 . 2569) (NUMSPATHS 2571 . 6158) (BROWSER 6160 . 6731)
- (BROWSER.WHENFNSCHANGED 6733 . 8518) (BRPATHS1 8520 . 11171) (BROWSER.LEFTFN 11173 . 14599) (
-GET.BROWSE.PP.WINDOW 14601 . 15426) (GET.BROWSE.DESCRIBE.WINDOW 15428 . 16176) (BROWSEPP 16178 . 17052
-) (PPREPAINTFN 17054 . 20180) (PPRESHAPEFN 20182 . 20358) (DESCRIBEREPAINTFN 20360 . 21064) (
-BROWSERDESCRIBE 21066 . 21808) (BROWSER.MIDDLEFN 21810 . 23125) (DEDITPROCESSRUNNINGP 23127 . 23382) (
-REDRAWBROWSEGRAPH 23384 . 24148) (STBROWSER 24150 . 28435)))))
+  (FILEMAP (NIL (1653 28138 (MSPATHS.DISPATCH 1663 . 2137) (NUMSPATHS 2139 . 5726) (BROWSER 5728 . 6299)
+ (BROWSER.WHENFNSCHANGED 6301 . 8086) (BRPATHS1 8088 . 10739) (BROWSER.LEFTFN 10741 . 14300) (
+GET.BROWSE.PP.WINDOW 14302 . 15127) (GET.BROWSE.DESCRIBE.WINDOW 15129 . 15877) (BROWSEPP 15879 . 16753
+) (PPREPAINTFN 16755 . 19881) (PPRESHAPEFN 19883 . 20059) (DESCRIBEREPAINTFN 20061 . 20765) (
+BROWSERDESCRIBE 20767 . 21509) (BROWSER.MIDDLEFN 21511 . 22826) (DEDITPROCESSRUNNINGP 22828 . 23083) (
+REDRAWBROWSEGRAPH 23085 . 23849) (STBROWSER 23851 . 28136)))))
 STOP
diff --git a/library/BROWSER.LCOM b/library/BROWSER.LCOM
index d242ec04..dbea7de0 100644
Binary files a/library/BROWSER.LCOM and b/library/BROWSER.LCOM differ
diff --git a/lispusers/GITFNS b/lispusers/GITFNS
index 4b9790b8..8309c5df 100644
--- a/lispusers/GITFNS
+++ b/lispusers/GITFNS
@@ -1,13 +1,12 @@
 (DEFINE-FILE-INFO PACKAGE "INTERLISP" READTABLE "INTERLISP" BASE 10)
 
-(FILECREATED " 2-May-2024 23:35:36" {WMEDLEY}<lispusers>GITFNS.;511 129269 
+(FILECREATED "20-May-2024 22:13:04" {WMEDLEY}<lispusers>GITFNS.;530 131382 
 
       :EDIT-BY rmk
 
-      :CHANGES-TO (FNS GIT-PUSH GIT-PULL GIT-GET-FILE GIT-FILE-DATE GIT-BRANCH-DIFF GIT-COMMIT-DIFFS
-                       GIT-CHECKOUT GIT-MAKE-BRANCH GIT-BRANCHES GIT-BRANCH-EXISTS? GIT-ADD-WORKTREE)
+      :CHANGES-TO (FNS GIT-PULL-REQUESTS)
 
-      :PREVIOUS-DATE " 2-May-2024 22:57:39" {WMEDLEY}<lispusers>GITFNS.;510)
+      :PREVIOUS-DATE "13-May-2024 19:31:18" {WMEDLEY}<lispusers>GITFNS.;529)
 
 
 (PRETTYCOMPRINT GITFNSCOMS)
@@ -404,7 +403,7 @@
 
 (TYPERECORD GIT-PROJECT (PROJECTNAME GITHOST WHOST EXCLUSIONS DEFAULTSUBDIRS CLONEPATH MAINBRANCH))
 
-(RECORD PULLREQUEST (PRNUMBER PRDESCRIPTION PRNAME PRSTATUS PRPROJECT))
+(RECORD PULLREQUEST (PRNUMBER PRDESCRIPTION PRNAME PRSTATUS PRPROJECT PRURL PRLOGIN))
 )
 )
 
@@ -536,7 +535,8 @@
 (DEFINEQ
 
 (PRC-COMMAND
-  [LAMBDA (REMOTEBRANCH DRAFTS PROJECT)                      (* ; "Edited  2-May-2024 11:44 by rmk")
+  [LAMBDA (REMOTEBRANCH DRAFTS PROJECT)                      (* ; "Edited 13-May-2024 18:49 by rmk")
+                                                             (* ; "Edited  2-May-2024 11:44 by rmk")
                                                              (* ; "Edited  1-Apr-2024 20:24 by rmk")
                                                              (* ; "Edited 28-Jul-2023 09:03 by rmk")
 
@@ -566,22 +566,30 @@
 
          (SETQ PRS (GIT-PULL-REQUESTS (NEQ 'NODRAFTS DRAFTS)
                           PROJECT))
-         (CL:WHEN (AND REMOTEBRANCH (NEQ REMOTEBRANCH 'PinMenu))
 
-             (* ;; "Filter by the REMOTEBRANCH string")
+         (* ;; "Filter by REMOTEBRANCH properties")
 
-             (SETQ PRS (for PR in PRS when (OR (STRPOS REMOTEBRANCH (fetch PRDESCRIPTION of PR)
-                                                      NIL NIL NIL NIL FILEDIRCASEARRAY)
-                                               (STRPOS REMOTEBRANCH (fetch PRNAME of PR)
-                                                      NIL NIL NIL NIL FILEDIRCASEARRAY)) collect
-                                                                                         PR)))
+         (SETQ PRS (for PR FOUND in PRS
+                      when (if (STRING-EQUAL "Interlisp" (fetch PRLOGIN of PR))
+                               then (OR (NULL REMOTEBRANCH)
+                                        (STRPOS REMOTEBRANCH (fetch PRDESCRIPTION of PR)
+                                               NIL NIL NIL NIL FILEDIRCASEARRAY)
+                                        (STRPOS REMOTEBRANCH (fetch PRNAME of PR)
+                                               NIL NIL NIL NIL FILEDIRCASEARRAY))
+                             else (CL:UNLESS FOUND
+                                      (SETQ FOUND T)
+                                      (PRINTOUT T "Ignored because not owned by Interlisp: " T))
+                                  (PRINTOUT T 3 (fetch PRDESCRIPTION of PR)
+                                         "  ("
+                                         (fetch PRLOGIN of PR)
+                                         ")" T)
+                                  NIL) collect PR))
          (IF PRS
              THEN (if (CDR PRS)
                       then (SETQ MENUWINDOW (ADDMENU (GIT-BRANCH-MENU (GIT-PRC-BRANCHES DRAFTS 
                                                                              PROJECT PRS)
                                                             (CONCAT (LENGTH PRS)
-                                                                   " pull requests")
-                                                            NIL PROJECT)
+                                                                   " pull requests"))
                                                    NIL NIL T)) 
 
                            (* ;; "Position the new menu just under the current TTY window, to keep it out of the way of the comparison windows.  If we have menus open for other projects, those probably should be pushed down to make room for the new menu, and moved up when a higher menu is closed.  An edge case that is not worth the effort. ")
@@ -1408,40 +1416,52 @@
                WHENSELECTEDFN _ (FUNCTION GIT-BRANCH-WHENSELECTEDFN)))])
 
 (GIT-BRANCH-WHENSELECTEDFN
-  [LAMBDA (ITEM)                                             (* ; "Edited  1-May-2024 18:17 by rmk")
+  [LAMBDA (ITEM)                                             (* ; "Edited 11-May-2024 11:05 by rmk")
+                                                             (* ; "Edited  1-May-2024 18:17 by rmk")
                                                              (* ; "CAR is git key, 4th is project")
 
     (* ;; "This executes the comparison in the current TTY window, either by stuffing the command there or by evaluating there.  There probably should be a check to make sure that the TTY is in fact an executive--if not, maybe this should be a no-op.  Better than getting the comparison form in the middle of anther SEDIT or TEDIT.")
 
     (* ;; "This could also execute in the mouse process, where the menu is clicked.  But in that case a terminal window pops up with the header lines of the compare, and that seems a nuisance.")
 
-    (if T
-        then 
-             (* ;; "The COPYINSERT causes the compare to run in the TTY process, by stuffing the characters in the input line. Somehow it executes even if the parens are not there, but that looks funny. But it also works if I stuff the parens on both sides.")
+    (LET [(PR (CAR (LAST ITEM]
+         (if [AND NIL (PROGN (GETMOUSESTATE)
+                             (EQ 'MIDDLE (DECODEBUTTONS]
+             then (LET [(POS (ADD1 (STRPOS "#" (CAR ITEM]
+                       (ShellBrowse (fetch PRURL of PR)))
+           elseif (PROGN T)
+             then 
+                  (* ;; "PROGN because DWIM is screwed up")
 
-             (BKSYSBUF '%()
-             [COPYINSERT `(GIT-PR-COMPARE ,(CADR ITEM)
-                                 ',(CADR (CDDDR ITEM]
-             (BKSYSBUF '%))
-      else 
-           (* ;; "This puts the print out after the next event number in the TTY window, unfortunately. We go to the default font so we don't get TTYIN's input bold for this.")
+                  (* ;; "The COPYINSERT causes the compare to run in the TTY process, by stuffing the characters in the input line. Somehow it executes even if the parens are not there, but that looks funny. But it also works if I stuff the parens on both sides.")
 
-           (PROCESS.EVAL (TTY.PROCESS)
-                  `(RESETLST
-                       [RESETSAVE (DSPFONT DEFAULTFONT T)
-                              '(PROGN (DSPFONT OLDVALUE T])])
+                  (BKSYSBUF '%()
+                  [COPYINSERT `(GIT-PR-COMPARE ,(CADR ITEM)
+                                      ',(fetch PRPROJECT of PR]
+                  (BKSYSBUF '%))
+           else 
+                (* ;; "This puts the print out after the next event number in the TTY window, unfortunately. We go to the default font so we don't get TTYIN's input bold for this.")
+
+                (PROCESS.EVAL (TTY.PROCESS)
+                       `(RESETLST
+                            [RESETSAVE (DSPFONT DEFAULTFONT T)
+                                   '(PROGN (DSPFONT OLDVALUE T])])
 
 (GIT-PULL-REQUESTS
-  [LAMBDA (INCLUDEDRAFTS PROJECT)                            (* ; "Edited  1-May-2024 09:23 by rmk")
+  [LAMBDA (INCLUDEDRAFTS PROJECT)                            (* ; "Edited 20-May-2024 22:12 by rmk")
+                                                             (* ; "Edited 13-May-2024 18:59 by rmk")
+                                                             (* ; "Edited 11-May-2024 10:51 by rmk")
+                                                             (* ; "Edited  1-May-2024 09:23 by rmk")
                                                              (* ; "Edited  8-Aug-2022 13:12 by rmk")
                                                              (* ; "Edited  4-Aug-2022 19:01 by rmk")
                                                              (* ; "Edited 17-Jul-2022 11:12 by rmk")
-                                                             (* ; "Edited  9-May-2022 16:54 by rmk")
-                                                             (* ; "Edited 25-Feb-2022 09:26 by rmk")
+                                                             (* ; "Edited  9-May-2022 16:54 by rmk")
+
+    (* ;; "Returns a list of PULLREQUEST records, one for each pull request")
+                                                             (* ; "Edited 25-Feb-2022 09:26 by rmk")
     (CL:UNLESS (EQ 0 (PROCESS-COMMAND "command -v gh"))
            (ERROR "gh must be installed in order to enumerate pull requests:"))
-    (LET [(JPARSE (JSON-PARSE (CAR (GIT-COMMAND 
-                                  "gh pr list --json number,headRefName,title,isDraft,reviewDecision"
+    (LET [(JPARSE (JSON-PARSE (CAR (GIT-COMMAND "gh pr list --json number,headRefName,title,isDraft,reviewDecision,url,headRepository,headRepositoryOwner"
                                           T NIL PROJECT]
          (FOR JSOBJ DRAFT PR IN (SELECTQ (CAR JPARSE)
                                     (ARRAY (CDR JPARSE))
@@ -1449,7 +1469,7 @@
                                     (ERROR "UNRECOGNIZED PRC LIST FROM GIT" JPARSE))
             EACHTIME [SETQ DRAFT (EQ 'true (JSON-GET JSOBJ 'isDraft] WHEN (OR INCLUDEDRAFTS
                                                                               (NOT DRAFT))
-            COLLECT (SETQ PR (CREATE PULLREQUEST
+            COLLECT [SETQ PR (CREATE PULLREQUEST
                                     PRNUMBER _ (JSON-GET JSOBJ 'number)
                                     PRNAME _ (JSON-GET JSOBJ 'headRefName)
                                     PRDESCRIPTION _ (JSON-GET JSOBJ 'title)
@@ -1459,8 +1479,19 @@
                                                                  (JSON-GET JSOBJ 'reviewDecision))
                                                        " "
                                                        'A))
-                                    PRPROJECT _ PROJECT))
+                                    PRPROJECT _ PROJECT
+                                    PRURL _ (JSON-GET JSOBJ 'url)
+                                    PRLOGIN _ (JSON-GET JSOBJ '(headRepositoryOwner login]
                   (CL:WHEN (STRPOS ":" (fetch (PULLREQUEST PRNAME) of PR))
+
+                      (* ;; "From Nick:  Git commands to bring install and deal with the remotes:")
+
+                      (* ;; "git remote add [PRLOGIN] https://github.com/[PRLOGIN]/[PROJECT]")
+
+                      (* ;; "          (project in lower-case)")
+
+                      (* ;; "git remote update [PRLOGIN]")
+
                       (PRINTOUT T "Ignoring PR for forked repo %%%" #" (JSON-GET JSOBJ 'number)
                              " "
                              (fetch (PULLREQUEST PRNAME) of PR)
@@ -1484,7 +1515,9 @@
     (FIND B IN (GIT-BRANCHES WHERE PROJECT EXCLUDEMERGED) SUCHTHAT (STRPOS BRANCH B])
 
 (GIT-PRC-BRANCHES
-  [LAMBDA (DRAFT PROJECT PRS)                                (* ; "Edited  1-May-2024 21:06 by rmk")
+  [LAMBDA (DRAFT PROJECT PRS)                                (* ; "Edited 13-May-2024 19:30 by rmk")
+                                                             (* ; "Edited 11-May-2024 10:52 by rmk")
+                                                             (* ; "Edited  1-May-2024 21:06 by rmk")
                                                              (* ; "Edited  1-Apr-2024 17:09 by rmk")
                                                              (* ; "Edited  8-Aug-2022 18:15 by rmk")
                                                              (* ; "Edited  4-Aug-2022 18:55 by rmk")
@@ -1496,7 +1529,8 @@
     (CL:UNLESS PRS
         (SETQ PRS (GIT-PULL-REQUESTS T PROJECT)))
     (CL:WHEN PRS
-        (LET ((RELATIONS (GIT-BRANCH-RELATIONS (FOR PR IN PRS COLLECT (GITORIGIN (CADDR PR)))
+        (LET ((RELATIONS (GIT-BRANCH-RELATIONS (FOR PR IN PRS
+                                                  COLLECT (GITORIGIN (fetch PRNAME of PR)))
                                 NIL T PROJECT)))
              (SORT (FOR PR REL LABEL PRNAME STATUS (SUPERSETS _ (CAR RELATIONS))
                         (EQUALS _ (CADR RELATIONS)) IN PRS
@@ -1517,7 +1551,7 @@
                                                 (CONCAT " " STATUS " #" (FETCH PRNUMBER OF PR)
                                                        " "
                                                        (FETCH PRDESCRIPTION OF PR))
-                                                NIL PROJECT))
+                                                NIL PR))
                    T)))])
 )
 
@@ -2368,33 +2402,33 @@
 
 (PUTPROPS GITFNS FILETYPE :TCOMPL)
 (DECLARE%: DONTCOPY
-  (FILEMAP (NIL (4348 20927 (GIT-CLONEP 4358 . 5686) (GIT-INIT 5688 . 6318) (GIT-MAKE-PROJECT 6320 . 
-13985) (GIT-GET-PROJECT 13987 . 15912) (GIT-PUT-PROJECT-FIELD 15914 . 17555) (GIT-PROJECT-PATH 17557
- . 18601) (FIND-ANCESTOR-DIRECTORY 18603 . 18952) (GIT-FIND-CLONE 18954 . 20035) (GIT-MAINBRANCH 20037
- . 20432) (GIT-MAINBRANCH? 20434 . 20925)) (26376 30458 (PRC-COMMAND 26386 . 30456)) (30514 33302 (
-ALLSUBDIRS 30524 . 31810) (MEDLEYSUBDIRS 31812 . 32505) (GITSUBDIRS 32507 . 33300)) (33303 38093 (
-TOGIT 33313 . 34719) (FROMGIT 34721 . 35702) (GIT-DELETE-FILE 35704 . 36550) (MYMEDLEY-DELETE-FILES 
-36552 . 38091)) (38094 41097 (MYMEDLEYSUBDIR 38104 . 38560) (GITSUBDIR 38562 . 39005) (STRIPDIR 39007
- . 39378) (STRIPHOST 39380 . 39620) (STRIPNAME 39622 . 40375) (STRIPWHERE 40377 . 41095)) (41098 43000
- (GFILE4MFILE 41108 . 41471) (MFILE4GFILE 41473 . 42042) (GIT-REPO-FILENAME 42044 . 42998)) (43049 
-53300 (GIT-COMMIT 43059 . 43885) (GIT-PUSH 43887 . 44647) (GIT-PULL 44649 . 45401) (GIT-APPROVAL 45403
- . 45752) (GIT-GET-FILE 45754 . 47776) (GIT-FILE-EXISTS? 47778 . 48052) (GIT-REMOTE-UPDATE 48054 . 
-48778) (GIT-REMOTE-ADD 48780 . 49087) (GIT-FILE-DATE 49089 . 50136) (GIT-FILE-HISTORY 50138 . 52072) (
-GIT-PRINT-FILE-HISTORY 52074 . 53124) (GIT-FETCH 53126 . 53298)) (53330 64103 (GIT-BRANCH-DIFF 53340
- . 59740) (GIT-COMMIT-DIFFS 59742 . 60415) (GIT-BRANCH-RELATIONS 60417 . 64101)) (64148 80865 (
-GIT-BRANCH-NUM 64158 . 64731) (GIT-CHECKOUT 64733 . 65908) (GIT-WHICH-BRANCH 65910 . 66208) (
-GIT-MAKE-BRANCH 66210 . 68539) (GIT-BRANCHES 68541 . 71031) (GIT-BRANCH-EXISTS? 71033 . 71904) (
-GIT-PICK-BRANCH 71906 . 72396) (GIT-BRANCH-MENU 72398 . 73279) (GIT-BRANCH-WHENSELECTEDFN 73281 . 
-74916) (GIT-PULL-REQUESTS 74918 . 77527) (GIT-SHORT-BRANCH-NAME 77529 . 77820) (GIT-LONG-NAME 77822 . 
-78139) (GIT-PRC-BRANCHES 78141 . 80863)) (80895 84230 (GIT-MY-CURRENT-BRANCH 80905 . 81275) (
-GIT-MY-BRANCHP 81277 . 81782) (GIT-MY-NEXT-BRANCH 81784 . 82278) (GIT-MY-BRANCHES 82280 . 84228)) (
-84276 88351 (GIT-ADD-WORKTREE 84286 . 85893) (GIT-REMOVE-WORKTREE 85895 . 86825) (GIT-LIST-WORKTREES 
-86827 . 87631) (WORKTREEDIR 87633 . 88349)) (88399 121103 (GIT-GET-DIFFERENT-FILES 88409 . 94833) (
-GIT-BRANCHES-COMPARE-DIRECTORIES 94835 . 101688) (GIT-WORKING-COMPARE-DIRECTORIES 101690 . 107086) (
-GIT-COMPARE-WORKTREE 107088 . 111066) (GITCDOBJBUTTONFN 111068 . 115558) (GIT-CD-LABELFN 115560 . 
-116642) (GIT-CD-MENUFN 116644 . 119084) (GIT-WORKING-COMPARE-FILES 119086 . 119706) (
-GIT-BRANCHES-COMPARE-FILES 119708 . 120872) (GIT-PR-COMPARE 120874 . 121101)) (121173 129202 (CDGITDIR
- 121183 . 121870) (GIT-COMMAND 121872 . 123430) (GITORIGIN 123432 . 124129) (GIT-INITIALS 124131 . 
-124435) (GIT-COMMAND-TO-FILE 124437 . 127926) (GIT-RESULT-TO-LINES 127928 . 128535) (STRIPLOCAL 128537
- . 129200)))))
+  (FILEMAP (NIL (4187 20766 (GIT-CLONEP 4197 . 5525) (GIT-INIT 5527 . 6157) (GIT-MAKE-PROJECT 6159 . 
+13824) (GIT-GET-PROJECT 13826 . 15751) (GIT-PUT-PROJECT-FIELD 15753 . 17394) (GIT-PROJECT-PATH 17396
+ . 18440) (FIND-ANCESTOR-DIRECTORY 18442 . 18791) (GIT-FIND-CLONE 18793 . 19874) (GIT-MAINBRANCH 19876
+ . 20271) (GIT-MAINBRANCH? 20273 . 20764)) (26229 30851 (PRC-COMMAND 26239 . 30849)) (30907 33695 (
+ALLSUBDIRS 30917 . 32203) (MEDLEYSUBDIRS 32205 . 32898) (GITSUBDIRS 32900 . 33693)) (33696 38486 (
+TOGIT 33706 . 35112) (FROMGIT 35114 . 36095) (GIT-DELETE-FILE 36097 . 36943) (MYMEDLEY-DELETE-FILES 
+36945 . 38484)) (38487 41490 (MYMEDLEYSUBDIR 38497 . 38953) (GITSUBDIR 38955 . 39398) (STRIPDIR 39400
+ . 39771) (STRIPHOST 39773 . 40013) (STRIPNAME 40015 . 40768) (STRIPWHERE 40770 . 41488)) (41491 43393
+ (GFILE4MFILE 41501 . 41864) (MFILE4GFILE 41866 . 42435) (GIT-REPO-FILENAME 42437 . 43391)) (43442 
+53693 (GIT-COMMIT 43452 . 44278) (GIT-PUSH 44280 . 45040) (GIT-PULL 45042 . 45794) (GIT-APPROVAL 45796
+ . 46145) (GIT-GET-FILE 46147 . 48169) (GIT-FILE-EXISTS? 48171 . 48445) (GIT-REMOTE-UPDATE 48447 . 
+49171) (GIT-REMOTE-ADD 49173 . 49480) (GIT-FILE-DATE 49482 . 50529) (GIT-FILE-HISTORY 50531 . 52465) (
+GIT-PRINT-FILE-HISTORY 52467 . 53517) (GIT-FETCH 53519 . 53691)) (53723 64496 (GIT-BRANCH-DIFF 53733
+ . 60133) (GIT-COMMIT-DIFFS 60135 . 60808) (GIT-BRANCH-RELATIONS 60810 . 64494)) (64541 82978 (
+GIT-BRANCH-NUM 64551 . 65124) (GIT-CHECKOUT 65126 . 66301) (GIT-WHICH-BRANCH 66303 . 66601) (
+GIT-MAKE-BRANCH 66603 . 68932) (GIT-BRANCHES 68934 . 71424) (GIT-BRANCH-EXISTS? 71426 . 72297) (
+GIT-PICK-BRANCH 72299 . 72789) (GIT-BRANCH-MENU 72791 . 73672) (GIT-BRANCH-WHENSELECTEDFN 73674 . 
+75839) (GIT-PULL-REQUESTS 75841 . 79359) (GIT-SHORT-BRANCH-NAME 79361 . 79652) (GIT-LONG-NAME 79654 . 
+79971) (GIT-PRC-BRANCHES 79973 . 82976)) (83008 86343 (GIT-MY-CURRENT-BRANCH 83018 . 83388) (
+GIT-MY-BRANCHP 83390 . 83895) (GIT-MY-NEXT-BRANCH 83897 . 84391) (GIT-MY-BRANCHES 84393 . 86341)) (
+86389 90464 (GIT-ADD-WORKTREE 86399 . 88006) (GIT-REMOVE-WORKTREE 88008 . 88938) (GIT-LIST-WORKTREES 
+88940 . 89744) (WORKTREEDIR 89746 . 90462)) (90512 123216 (GIT-GET-DIFFERENT-FILES 90522 . 96946) (
+GIT-BRANCHES-COMPARE-DIRECTORIES 96948 . 103801) (GIT-WORKING-COMPARE-DIRECTORIES 103803 . 109199) (
+GIT-COMPARE-WORKTREE 109201 . 113179) (GITCDOBJBUTTONFN 113181 . 117671) (GIT-CD-LABELFN 117673 . 
+118755) (GIT-CD-MENUFN 118757 . 121197) (GIT-WORKING-COMPARE-FILES 121199 . 121819) (
+GIT-BRANCHES-COMPARE-FILES 121821 . 122985) (GIT-PR-COMPARE 122987 . 123214)) (123286 131315 (CDGITDIR
+ 123296 . 123983) (GIT-COMMAND 123985 . 125543) (GITORIGIN 125545 . 126242) (GIT-INITIALS 126244 . 
+126548) (GIT-COMMAND-TO-FILE 126550 . 130039) (GIT-RESULT-TO-LINES 130041 . 130648) (STRIPLOCAL 130650
+ . 131313)))))
 STOP
diff --git a/lispusers/GITFNS.LCOM b/lispusers/GITFNS.LCOM
index 1b58e857..f267d0f6 100644
Binary files a/lispusers/GITFNS.LCOM and b/lispusers/GITFNS.LCOM differ
diff --git a/lispusers/JSON b/lispusers/JSON
index 24fec78a..32498ae4 100644
--- a/lispusers/JSON
+++ b/lispusers/JSON
@@ -1,15 +1,12 @@
 (DEFINE-FILE-INFO PACKAGE "INTERLISP" READTABLE "INTERLISP" BASE 10)
 
-(FILECREATED "30-Apr-2024 14:42:30" {WMEDLEY}<lispusers>JSON.;31 9030   
+(FILECREATED "13-May-2024 22:37:13" {WMEDLEY}<lispusers>JSON.;36 9198   
 
       :EDIT-BY rmk
 
-      :CHANGES-TO (FNS JSON-STRING JSON-GET JSON-VALUE JSON-ARRAY JSON-OBJECT JSON-AVPAIR JSON-NUMBER
-                       JSON-ATOM JSSKIP JSON-SKIP JSON-PARSE)
-                  (VARS JSONCOMS)
-                  (MACROS JSBIN JSPEEK JSBINC JSPEEKC)
+      :CHANGES-TO (FNS JSON-GET)
 
-      :PREVIOUS-DATE "30-Apr-2024 00:54:21" {WMEDLEY}<lispusers>JSON.;9)
+      :PREVIOUS-DATE "13-May-2024 19:23:02" {WMEDLEY}<lispusers>JSON.;33)
 
 
 (PRETTYCOMPRINT JSONCOMS)
@@ -180,11 +177,14 @@
          NIL])
 
 (JSON-GET
-  [LAMBDA (OBJECT ATTRIBUTE)                                 (* ; "Edited 30-Apr-2024 14:26 by rmk")
+  [LAMBDA (OBJECT ATTRIBUTES)                                (* ; "Edited 13-May-2024 22:35 by rmk")
+                                                             (* ; "Edited 30-Apr-2024 14:26 by rmk")
 
-    (* ;; "Returns the value of ATTRIBUTE in OBJECT")
+    (* ;; "Returns the value at the end of a chain of ATTRIBUTES in OBJECT")
 
-    (CADR (ASSOC ATTRIBUTE OBJECT])
+    (for A (OBJ _ OBJECT) inside ATTRIBUTES do (if (EQ 'OBJECT (CAR (LISTP OBJ)))
+                                                   then [SETQ OBJ (CADR (ASSOC A (CDR OBJ]
+                                                 else (RETURN NIL)) finally (RETURN OBJ])
 )
 (DECLARE%: EVAL@COMPILE DONTCOPY 
 (DECLARE%: EVAL@COMPILE 
@@ -201,7 +201,7 @@
 )
 )
 (DECLARE%: DONTCOPY
-  (FILEMAP (NIL (778 8671 (JSON-PARSE 788 . 1134) (JSON-VALUE 1136 . 1505) (JSON-SKIP 1507 . 1781) (
-JSON-STRING 1783 . 2581) (JSON-ARRAY 2583 . 3721) (JSON-OBJECT 3723 . 5180) (JSON-AVPAIR 5182 . 5624) 
-(JSON-NUMBER 5626 . 7140) (JSON-ATOM 7142 . 8449) (JSON-GET 8451 . 8669)))))
+  (FILEMAP (NIL (559 8839 (JSON-PARSE 569 . 915) (JSON-VALUE 917 . 1286) (JSON-SKIP 1288 . 1562) (
+JSON-STRING 1564 . 2362) (JSON-ARRAY 2364 . 3502) (JSON-OBJECT 3504 . 4961) (JSON-AVPAIR 4963 . 5405) 
+(JSON-NUMBER 5407 . 6921) (JSON-ATOM 6923 . 8230) (JSON-GET 8232 . 8837)))))
 STOP
diff --git a/lispusers/JSON.LCOM b/lispusers/JSON.LCOM
index 34793e69..1b0cc52b 100644
Binary files a/lispusers/JSON.LCOM and b/lispusers/JSON.LCOM differ
diff --git a/scripts/copy-all.sh b/scripts/copy-all.sh
index 547f81d8..aebec2b4 100755
--- a/scripts/copy-all.sh
+++ b/scripts/copy-all.sh
@@ -1,35 +1,151 @@
 #!/bin/sh
 
-if [ ! -x run-medley ] ; then
-    echo run from MEDLEYDIR
-    exit 1
+main() {
+	# shellcheck source=./loadup-setup.sh
+	. "${LOADUP_SCRIPTDIR}/loadup-setup.sh"
+
+	echo ">>>>> START ${script_name}"
+
+	/bin/sh "${LOADUP_SCRIPTDIR}/cpv" "${LOADUP_WORKDIR}"/full.sysout "${LOADUP_OUTDIR}"       \
+	    | sed -e "s#${MEDLEYDIR}/##g"
+	/bin/sh "${LOADUP_SCRIPTDIR}/cpv" "${LOADUP_WORKDIR}"/lisp.sysout "${LOADUP_OUTDIR}"       \
+	    | sed -e "s#${MEDLEYDIR}/##g"
+
+	if [ "${1}" = "-apps" ]; then
+	    /bin/sh "${LOADUP_SCRIPTDIR}/cpv" "${LOADUP_WORKDIR}"/apps.sysout "${LOADUP_OUTDIR}"   \
+	        | sed -e "s#${MEDLEYDIR}/##g"
+	fi
+
+	/bin/sh "${LOADUP_SCRIPTDIR}/cpv" "${LOADUP_WORKDIR}"/whereis.hash "${LOADUP_OUTDIR}"      \
+	    | sed -e "s#${MEDLEYDIR}/##g"
+	/bin/sh "${LOADUP_SCRIPTDIR}/cpv" "${LOADUP_WORKDIR}"/exports.all "${LOADUP_OUTDIR}"       \
+	    | sed -e "s#${MEDLEYDIR}/##g"
+
+	/bin/sh "${LOADUP_SCRIPTDIR}/cpv" "${LOADUP_WORKDIR}"/init.dribble "${LOADUP_OUTDIR}"      \
+	    | sed -e "s#${MEDLEYDIR}/##g"
+	/bin/sh "${LOADUP_SCRIPTDIR}/cpv" "${LOADUP_WORKDIR}"/lisp.dribble "${LOADUP_OUTDIR}"      \
+	    | sed -e "s#${MEDLEYDIR}/##g"
+	/bin/sh "${LOADUP_SCRIPTDIR}/cpv" "${LOADUP_WORKDIR}"/full.dribble "${LOADUP_OUTDIR}"      \
+	    | sed -e "s#${MEDLEYDIR}/##g"
+	/bin/sh "${LOADUP_SCRIPTDIR}/cpv" "${LOADUP_WORKDIR}"/whereis.dribble "${LOADUP_OUTDIR}"   \
+	    | sed -e "s#${MEDLEYDIR}/##g"
+
+	if [ "${1}" = "-apps" ]; then
+	    /bin/sh "${LOADUP_SCRIPTDIR}/cpv" "${LOADUP_WORKDIR}"/apps.dribble "${LOADUP_OUTDIR}"  \
+	        | sed -e "s#${MEDLEYDIR}/##g"
+	fi
+
+
+	/bin/sh "${LOADUP_SCRIPTDIR}/cpv" "${LOADUP_WORKDIR}"/RDSYS library                        \
+	    | sed -e "s#${MEDLEYDIR}/##g"
+	/bin/sh "${LOADUP_SCRIPTDIR}/cpv" "${LOADUP_WORKDIR}"/RDSYS.LCOM library                   \
+	    | sed -e "s#${MEDLEYDIR}/##g"
+
+
+	echo "<<<<< END ${script_name}"
+	echo ""
+	exit 0
+}
+
+
+
+# shellcheck disable=SC2164,SC2034
+if [ -z "${LOADUP_SCRIPTDIR}" ]
+then
+	#
+	#
+	# Some functions to determine what directory this script is being executed from
+	#
+	#
+	get_abs_filename() {
+	  # $1 : relative filename
+	  echo "$(cd "$(dirname "$1")" && pwd)/$(basename "$1")"
+	}
+
+	# This function taken from
+	# https://stackoverflow.com/questions/29832037/how-to-get-script-directory-in-posix-sh
+	rreadlink() (
+
+	  # Execute this function in a *subshell* to localize variables and the effect of `cd`.
+
+	  target=$1
+	  fname=
+	  targetDir=
+	  CDPATH=
+
+	  # Try to make the execution environment as predictable as possible:
+	  # All commands below are invoked via `command`, so we must make sure that `command`
+	  # itself is not redefined as an alias or shell function.
+	  # (Note that command is too inconsistent across shells, so we don't use it.)
+	  # `command` is a *builtin* in bash, dash, ksh, zsh, and some platforms do not even have
+	  # an external utility version of it (e.g, Ubuntu).
+	  # `command` bypasses aliases and shell functions and also finds builtins 
+	  # in bash, dash, and ksh. In zsh, option POSIX_BUILTINS must be turned on for that
+	  # to happen.
+	  { \unalias command; \unset -f command; } >/dev/null 2>&1
+	  [ -n "$ZSH_VERSION" ] && options[POSIX_BUILTINS]=on # make zsh find *builtins* with `command` too.
+
+	  while :; do # Resolve potential symlinks until the ultimate target is found.
+	      [ -L "$target" ] || [ -e "$target" ] || { command printf '%s\n' "ERROR: '$target' does not exist." >&2; return 1; }
+	      command cd "$(command dirname -- "$target")" # Change to target dir; necessary for correct resolution of target path.
+	      fname=$(command basename -- "$target") # Extract filename.
+	      [ "$fname" = '/' ] && fname='' # !! curiously, `basename /` returns '/'
+	      if [ -L "$fname" ]; then
+	        # Extract [next] target path, which may be defined
+	        # *relative* to the symlink's own directory.
+	        # Note: We parse `ls -l` output to find the symlink target
+	        #       which is the only POSIX-compliant, albeit somewhat fragile, way.
+	        target=$(command ls -l "$fname")
+	        target=${target#* -> }
+	        continue # Resolve [next] symlink target.
+	      fi
+	      break # Ultimate target reached.
+	  done
+	  targetDir=$(command pwd -P) # Get canonical dir. path
+	  # Output the ultimate target's canonical path.
+	  # Note that we manually resolve paths ending in /. and /.. to make sure we have a normalized path.
+	  if [ "$fname" = '.' ]; then
+	    command printf '%s\n' "${targetDir%/}"
+	  elif  [ "$fname" = '..' ]; then
+	    # Caveat: something like /var/.. will resolve to /private (assuming /var@ -> /private/var), i.e. the '..' is applied
+	    # AFTER canonicalization.
+	    command printf '%s\n' "$(command dirname -- "${targetDir}")"
+	  else
+	    command printf '%s\n' "${targetDir%/}/$fname"
+	  fi
+	)
+
+	get_script_dir() {
+
+	    # call this with $0 (from main script) as its (only) parameter
+	    # if you need to preserve cwd, run this is a subshell since
+	    # it can change cwd
+
+	    # set -x
+
+	    local_SCRIPT_PATH="$( get_abs_filename "$1" )";
+
+	    while [ -h "$local_SCRIPT_PATH" ];
+	    do
+	        cd "$( dirname -- "$local_SCRIPT_PATH"; )";
+	        local_SCRIPT_PATH="$( rreadlink "$local_SCRIPT_PATH" )";
+	    done
+
+	    cd "$( dirname -- "$local_SCRIPT_PATH"; )" > '/dev/null';
+	    local_SCRIPT_PATH="$( pwd; )";
+
+	    # set +x
+
+	    echo "${local_SCRIPT_PATH}"
+	}
+
+	# end of script directory functions
+	###############################################################################
+
+        # figure out the script dir
+        LOADUP_SCRIPTDIR="$(get_script_dir "$0")"
+	export LOADUP_SCRIPTDIR
+
 fi
 
-. scripts/loadup-setup.sh
-
-echo ">>>>> START ${script_name}"
-
-./scripts/cpv "${LOADUP_WORKDIR}"/full.sysout "${LOADUP_OUTDIR}"  | sed -e "s#${MEDLEYDIR}/##g"
-./scripts/cpv "${LOADUP_WORKDIR}"/lisp.sysout "${LOADUP_OUTDIR}"  | sed -e "s#${MEDLEYDIR}/##g"
-if [ "${1}" = "-apps" ]; then
-    ./scripts/cpv "${LOADUP_WORKDIR}"/apps.sysout "${LOADUP_OUTDIR}" | sed -e "s#${MEDLEYDIR}/##g"
-fi
-
-./scripts/cpv "${LOADUP_WORKDIR}"/whereis.hash "${LOADUP_OUTDIR}" | sed -e "s#${MEDLEYDIR}/##g"
-./scripts/cpv "${LOADUP_WORKDIR}"/exports.all "${LOADUP_OUTDIR}" | sed -e "s#${MEDLEYDIR}/##g"
-
-./scripts/cpv "${LOADUP_WORKDIR}"/init.dribble "${LOADUP_OUTDIR}" | sed -e "s#${MEDLEYDIR}/##g"
-./scripts/cpv "${LOADUP_WORKDIR}"/lisp.dribble "${LOADUP_OUTDIR}" | sed -e "s#${MEDLEYDIR}/##g"
-./scripts/cpv "${LOADUP_WORKDIR}"/full.dribble "${LOADUP_OUTDIR}" | sed -e "s#${MEDLEYDIR}/##g"
-./scripts/cpv "${LOADUP_WORKDIR}"/whereis.dribble "${LOADUP_OUTDIR}" | sed -e "s#${MEDLEYDIR}/##g"
-if [ "${1}" = "-apps" ]; then
-    ./scripts/cpv "${LOADUP_WORKDIR}"/apps.dribble "${LOADUP_OUTDIR}" | sed -e "s#${MEDLEYDIR}/##g"
-fi
-
-
-./scripts/cpv "${LOADUP_WORKDIR}"/RDSYS library | sed -e "s#${MEDLEYDIR}/##g"
-./scripts/cpv "${LOADUP_WORKDIR}"/RDSYS.LCOM library | sed -e "s#${MEDLEYDIR}/##g"
-
-echo "<<<<< END ${script_name}"
-echo ""
-exit 0
+main "$@"
diff --git a/scripts/copy-db.sh b/scripts/copy-db.sh
index 77a76b92..f3916906 100755
--- a/scripts/copy-db.sh
+++ b/scripts/copy-db.sh
@@ -1,17 +1,118 @@
 #!/bin/sh
 
-if [ ! -x run-medley ] ; then
-    echo run from MEDLEYDIR
-    exit 1
+main() {
+
+	# shellcheck source=./loadup-setup.sh
+	. "${LOADUP_SCRIPTDIR}/loadup-setup.sh"
+
+	echo ">>>>> START ${script_name}"
+
+	/bin/sh "${LOADUP_SCRIPTDIR}/cpv" "${LOADUP_WORKDIR}"/fuller.database "${LOADUP_OUTDIR}"
+	/bin/sh "${LOADUP_SCRIPTDIR}/cpv" "${LOADUP_WORKDIR}"/fuller.dribble "${LOADUP_OUTDIR}"
+
+	echo "<<<<< END ${script_name}"
+	echo ""
+	exit 0
+}
+
+
+# shellcheck disable=SC2164,SC2034
+if [ -z "${LOADUP_SCRIPTDIR}" ]
+then
+	#
+	#
+	# Some functions to determine what directory this script is being executed from
+	#
+	#
+	get_abs_filename() {
+	  # $1 : relative filename
+	  echo "$(cd "$(dirname "$1")" && pwd)/$(basename "$1")"
+	}
+
+	# This function taken from
+	# https://stackoverflow.com/questions/29832037/how-to-get-script-directory-in-posix-sh
+	rreadlink() (
+
+	  # Execute this function in a *subshell* to localize variables and the effect of `cd`.
+
+	  target=$1
+	  fname=
+	  targetDir=
+	  CDPATH=
+
+	  # Try to make the execution environment as predictable as possible:
+	  # All commands below are invoked via `command`, so we must make sure that `command`
+	  # itself is not redefined as an alias or shell function.
+	  # (Note that command is too inconsistent across shells, so we don't use it.)
+	  # `command` is a *builtin* in bash, dash, ksh, zsh, and some platforms do not even have
+	  # an external utility version of it (e.g, Ubuntu).
+	  # `command` bypasses aliases and shell functions and also finds builtins 
+	  # in bash, dash, and ksh. In zsh, option POSIX_BUILTINS must be turned on for that
+	  # to happen.
+	  { \unalias command; \unset -f command; } >/dev/null 2>&1
+	  [ -n "$ZSH_VERSION" ] && options[POSIX_BUILTINS]=on # make zsh find *builtins* with `command` too.
+
+	  while :; do # Resolve potential symlinks until the ultimate target is found.
+	      [ -L "$target" ] || [ -e "$target" ] || { command printf '%s\n' "ERROR: '$target' does not exist." >&2; return 1; }
+	      command cd "$(command dirname -- "$target")" # Change to target dir; necessary for correct resolution of target path.
+	      fname=$(command basename -- "$target") # Extract filename.
+	      [ "$fname" = '/' ] && fname='' # !! curiously, `basename /` returns '/'
+	      if [ -L "$fname" ]; then
+	        # Extract [next] target path, which may be defined
+	        # *relative* to the symlink's own directory.
+	        # Note: We parse `ls -l` output to find the symlink target
+	        #       which is the only POSIX-compliant, albeit somewhat fragile, way.
+	        target=$(command ls -l "$fname")
+	        target=${target#* -> }
+	        continue # Resolve [next] symlink target.
+	      fi
+	      break # Ultimate target reached.
+	  done
+	  targetDir=$(command pwd -P) # Get canonical dir. path
+	  # Output the ultimate target's canonical path.
+	  # Note that we manually resolve paths ending in /. and /.. to make sure we have a normalized path.
+	  if [ "$fname" = '.' ]; then
+	    command printf '%s\n' "${targetDir%/}"
+	  elif  [ "$fname" = '..' ]; then
+	    # Caveat: something like /var/.. will resolve to /private (assuming /var@ -> /private/var), i.e. the '..' is applied
+	    # AFTER canonicalization.
+	    command printf '%s\n' "$(command dirname -- "${targetDir}")"
+	  else
+	    command printf '%s\n' "${targetDir%/}/$fname"
+	  fi
+	)
+
+	get_script_dir() {
+
+	    # call this with $0 (from main script) as its (only) parameter
+	    # if you need to preserve cwd, run this is a subshell since
+	    # it can change cwd
+
+	    # set -x
+
+	    local_SCRIPT_PATH="$( get_abs_filename "$1" )";
+
+	    while [ -h "$local_SCRIPT_PATH" ];
+	    do
+	        cd "$( dirname -- "$local_SCRIPT_PATH"; )";
+	        local_SCRIPT_PATH="$( rreadlink "$local_SCRIPT_PATH" )";
+	    done
+
+	    cd "$( dirname -- "$local_SCRIPT_PATH"; )" > '/dev/null';
+	    local_SCRIPT_PATH="$( pwd; )";
+
+	    # set +x
+
+	    echo "${local_SCRIPT_PATH}"
+	}
+
+	# end of script directory functions
+	###############################################################################
+
+        # figure out the script dir
+        LOADUP_SCRIPTDIR="$(get_script_dir "$0")"
+	export LOADUP_SCRIPTDIR
+
 fi
 
-. scripts/loadup-setup.sh
-
-echo ">>>>> START ${script_name}"
-
-./scripts/cpv "${LOADUP_WORKDIR}"/fuller.database "${LOADUP_OUTDIR}"
-./scripts/cpv "${LOADUP_WORKDIR}"/fuller.dribble "${LOADUP_OUTDIR}"
-
-echo "<<<<< END ${script_name}"
-echo ""
-exit 0
+main "$@"
diff --git a/scripts/copy-full.sh b/scripts/copy-full.sh
index 14f084e1..59ace2c8 100755
--- a/scripts/copy-full.sh
+++ b/scripts/copy-full.sh
@@ -1,24 +1,130 @@
 #!/bin/sh
 
-if [ ! -x run-medley ] ; then
-    echo run from MEDLEYDIR
-    exit 1
+main() {
+	# shellcheck source=./loadup-setup.sh
+	. "${LOADUP_SCRIPTDIR}/loadup-setup.sh"
+
+	echo ">>>>> START ${script_name}"
+
+	/bin/sh "${LOADUP_SCRIPTDIR}/cpv" "${LOADUP_WORKDIR}"/full.sysout "${LOADUP_OUTDIR}"  \
+	    | sed -e "s#${MEDLEYDIR}/##g"
+	/bin/sh "${LOADUP_SCRIPTDIR}/cpv" "${LOADUP_WORKDIR}"/lisp.sysout "${LOADUP_OUTDIR}"  \
+	    | sed -e "s#${MEDLEYDIR}/##g"
+
+	/bin/sh "${LOADUP_SCRIPTDIR}/cpv" "${LOADUP_WORKDIR}"/init.dribble "${LOADUP_OUTDIR}" \
+	    | sed -e "s#${MEDLEYDIR}/##g"
+	/bin/sh "${LOADUP_SCRIPTDIR}/cpv" "${LOADUP_WORKDIR}"/lisp.dribble "${LOADUP_OUTDIR}" \
+	    | sed -e "s#${MEDLEYDIR}/##g"
+	/bin/sh "${LOADUP_SCRIPTDIR}/cpv" "${LOADUP_WORKDIR}"/full.dribble "${LOADUP_OUTDIR}" \
+	    | sed -e "s#${MEDLEYDIR}/##g"
+
+	/bin/sh "${LOADUP_SCRIPTDIR}/cpv" "${LOADUP_WORKDIR}"/RDSYS library                   \
+	    | sed -e "s#${MEDLEYDIR}/##g"
+	/bin/sh "${LOADUP_SCRIPTDIR}/cpv" "${LOADUP_WORKDIR}"/RDSYS.LCOM library              \
+	    | sed -e "s#${MEDLEYDIR}/##g"
+
+	echo "<<<<< END ${script_name}"
+	echo ""
+	exit 0
+}
+
+# shellcheck disable=SC2164,SC2034
+if [ -z "${LOADUP_SCRIPTDIR}" ]
+then
+	#
+	#
+	# Some functions to determine what directory this script is being executed from
+	#
+	#
+	get_abs_filename() {
+	  # $1 : relative filename
+	  echo "$(cd "$(dirname "$1")" && pwd)/$(basename "$1")"
+	}
+
+	# This function taken from
+	# https://stackoverflow.com/questions/29832037/how-to-get-script-directory-in-posix-sh
+	rreadlink() (
+
+	  # Execute this function in a *subshell* to localize variables and the effect of `cd`.
+
+	  target=$1
+	  fname=
+	  targetDir=
+	  CDPATH=
+
+	  # Try to make the execution environment as predictable as possible:
+	  # All commands below are invoked via `command`, so we must make sure that `command`
+	  # itself is not redefined as an alias or shell function.
+	  # (Note that command is too inconsistent across shells, so we don't use it.)
+	  # `command` is a *builtin* in bash, dash, ksh, zsh, and some platforms do not even have
+	  # an external utility version of it (e.g, Ubuntu).
+	  # `command` bypasses aliases and shell functions and also finds builtins 
+	  # in bash, dash, and ksh. In zsh, option POSIX_BUILTINS must be turned on for that
+	  # to happen.
+	  { \unalias command; \unset -f command; } >/dev/null 2>&1
+	  [ -n "$ZSH_VERSION" ] && options[POSIX_BUILTINS]=on # make zsh find *builtins* with `command` too.
+
+	  while :; do # Resolve potential symlinks until the ultimate target is found.
+	      [ -L "$target" ] || [ -e "$target" ] || { command printf '%s\n' "ERROR: '$target' does not exist." >&2; return 1; }
+	      command cd "$(command dirname -- "$target")" # Change to target dir; necessary for correct resolution of target path.
+	      fname=$(command basename -- "$target") # Extract filename.
+	      [ "$fname" = '/' ] && fname='' # !! curiously, `basename /` returns '/'
+	      if [ -L "$fname" ]; then
+	        # Extract [next] target path, which may be defined
+	        # *relative* to the symlink's own directory.
+	        # Note: We parse `ls -l` output to find the symlink target
+	        #       which is the only POSIX-compliant, albeit somewhat fragile, way.
+	        target=$(command ls -l "$fname")
+	        target=${target#* -> }
+	        continue # Resolve [next] symlink target.
+	      fi
+	      break # Ultimate target reached.
+	  done
+	  targetDir=$(command pwd -P) # Get canonical dir. path
+	  # Output the ultimate target's canonical path.
+	  # Note that we manually resolve paths ending in /. and /.. to make sure we have a normalized path.
+	  if [ "$fname" = '.' ]; then
+	    command printf '%s\n' "${targetDir%/}"
+	  elif  [ "$fname" = '..' ]; then
+	    # Caveat: something like /var/.. will resolve to /private (assuming /var@ -> /private/var), i.e. the '..' is applied
+	    # AFTER canonicalization.
+	    command printf '%s\n' "$(command dirname -- "${targetDir}")"
+	  else
+	    command printf '%s\n' "${targetDir%/}/$fname"
+	  fi
+	)
+
+	get_script_dir() {
+
+	    # call this with $0 (from main script) as its (only) parameter
+	    # if you need to preserve cwd, run this is a subshell since
+	    # it can change cwd
+
+	    # set -x
+
+	    local_SCRIPT_PATH="$( get_abs_filename "$1" )";
+
+	    while [ -h "$local_SCRIPT_PATH" ];
+	    do
+	        cd "$( dirname -- "$local_SCRIPT_PATH"; )";
+	        local_SCRIPT_PATH="$( rreadlink "$local_SCRIPT_PATH" )";
+	    done
+
+	    cd "$( dirname -- "$local_SCRIPT_PATH"; )" > '/dev/null';
+	    local_SCRIPT_PATH="$( pwd; )";
+
+	    # set +x
+
+	    echo "${local_SCRIPT_PATH}"
+	}
+
+	# end of script directory functions
+	###############################################################################
+
+        # figure out the script dir
+        LOADUP_SCRIPTDIR="$(get_script_dir "$0")"
+	export LOADUP_SCRIPTDIR
+
 fi
 
-. scripts/loadup-setup.sh
-
-echo ">>>>> START ${script_name}"
-
-./scripts/cpv "${LOADUP_WORKDIR}"/full.sysout "${LOADUP_OUTDIR}"  | sed -e "s#${MEDLEYDIR}/##g"
-./scripts/cpv "${LOADUP_WORKDIR}"/lisp.sysout "${LOADUP_OUTDIR}"  | sed -e "s#${MEDLEYDIR}/##g"
-
-./scripts/cpv "${LOADUP_WORKDIR}"/init.dribble "${LOADUP_OUTDIR}" | sed -e "s#${MEDLEYDIR}/##g"
-./scripts/cpv "${LOADUP_WORKDIR}"/lisp.dribble "${LOADUP_OUTDIR}" | sed -e "s#${MEDLEYDIR}/##g"
-./scripts/cpv "${LOADUP_WORKDIR}"/full.dribble "${LOADUP_OUTDIR}" | sed -e "s#${MEDLEYDIR}/##g"
-
-./scripts/cpv "${LOADUP_WORKDIR}"/RDSYS library | sed -e "s#${MEDLEYDIR}/##g"
-./scripts/cpv "${LOADUP_WORKDIR}"/RDSYS.LCOM library | sed -e "s#${MEDLEYDIR}/##g"
-
-echo "<<<<< END ${script_name}"
-echo ""
-exit 0
+main "$@"
diff --git a/scripts/loadup-all.sh b/scripts/loadup-all.sh
index cc54a941..77d1fa99 100755
--- a/scripts/loadup-all.sh
+++ b/scripts/loadup-all.sh
@@ -1,50 +1,146 @@
 #!/bin/sh
 # shellcheck disable=SC2181
 
-if [ ! -h ./medley ] || [ ! -d ./lispusers ]
+main() {
+
+	# shellcheck source=./loadup-setup.sh
+	. "${LOADUP_SCRIPTDIR}/loadup-setup.sh"
+
+	# look thru args looking to see if -apps, --apps, or -a was specified in args
+	apps=""
+	j=1
+	jmax=$#
+	while [ "$j" -le "$jmax" ]
+	do
+	  if [ "$(eval "printf %s \${${j}}")" = "-a" ] ||    \
+	     [ "$(eval "printf %s \${${j}}")" = "-apps" ] || \
+	     [ "$(eval "printf %s \${${j}}")" = "--apps" ]
+	  then
+	    apps="-apps"
+	    break
+	  fi
+	done
+
+	#  Do loadup components
+	/bin/sh "${LOADUP_SCRIPTDIR}/loadup-init.sh"                   \
+	&& /bin/sh "${LOADUP_SCRIPTDIR}/loadup-mid-from-init.sh"       \
+	&& /bin/sh "${LOADUP_SCRIPTDIR}/loadup-lisp-from-mid.sh"       \
+	&& /bin/sh "${LOADUP_SCRIPTDIR}/loadup-full-from-lisp.sh"      \
+	&& {                                                           \
+	     if [ -n "${apps}" ];                                      \
+	     then                                                      \
+	       /bin/sh "${LOADUP_SCRIPTDIR}/loadup-apps-from-full.sh"; \
+	     fi;                                                       \
+	    }                                                          \
+	&& /bin/sh "${LOADUP_SCRIPTDIR}/loadup-aux.sh"                 \
+	&& /bin/sh "${LOADUP_SCRIPTDIR}/copy-all.sh" "${apps}"
+
+	if [ $? -eq 0 ]
+	then
+	  echo "+++++ loadup-all.sh: SUCCESS +++++"
+	else
+	  echo "----- loadup-all.sh: FAILURE -----"
+	fi
+
+}
+
+# shellcheck disable=SC2164,SC2034
+if [ -z "${LOADUP_SCRIPTDIR}" ]
 then
-    echo "*** ERROR ***"
-    echo "You must run $(basename "$0") while the cwd is a Medley top-level directory."
-    echo "The cwd ($(pwd)) is not a Medley top-level directory."
-    echo "Exiting."
-    exit 1
+	#
+	#
+	# Some functions to determine what directory this script is being executed from
+	#
+	#
+	get_abs_filename() {
+	  # $1 : relative filename
+	  echo "$(cd "$(dirname "$1")" && pwd)/$(basename "$1")"
+	}
+
+	# This function taken from
+	# https://stackoverflow.com/questions/29832037/how-to-get-script-directory-in-posix-sh
+	rreadlink() (
+
+	  # Execute this function in a *subshell* to localize variables and the effect of `cd`.
+
+	  target=$1
+	  fname=
+	  targetDir=
+	  CDPATH=
+
+	  # Try to make the execution environment as predictable as possible:
+	  # All commands below are invoked via `command`, so we must make sure that `command`
+	  # itself is not redefined as an alias or shell function.
+	  # (Note that command is too inconsistent across shells, so we don't use it.)
+	  # `command` is a *builtin* in bash, dash, ksh, zsh, and some platforms do not even have
+	  # an external utility version of it (e.g, Ubuntu).
+	  # `command` bypasses aliases and shell functions and also finds builtins 
+	  # in bash, dash, and ksh. In zsh, option POSIX_BUILTINS must be turned on for that
+	  # to happen.
+	  { \unalias command; \unset -f command; } >/dev/null 2>&1
+	  [ -n "$ZSH_VERSION" ] && options[POSIX_BUILTINS]=on # make zsh find *builtins* with `command` too.
+
+	  while :; do # Resolve potential symlinks until the ultimate target is found.
+	      [ -L "$target" ] || [ -e "$target" ] || { command printf '%s\n' "ERROR: '$target' does not exist." >&2; return 1; }
+	      command cd "$(command dirname -- "$target")" # Change to target dir; necessary for correct resolution of target path.
+	      fname=$(command basename -- "$target") # Extract filename.
+	      [ "$fname" = '/' ] && fname='' # !! curiously, `basename /` returns '/'
+	      if [ -L "$fname" ]; then
+	        # Extract [next] target path, which may be defined
+	        # *relative* to the symlink's own directory.
+	        # Note: We parse `ls -l` output to find the symlink target
+	        #       which is the only POSIX-compliant, albeit somewhat fragile, way.
+	        target=$(command ls -l "$fname")
+	        target=${target#* -> }
+	        continue # Resolve [next] symlink target.
+	      fi
+	      break # Ultimate target reached.
+	  done
+	  targetDir=$(command pwd -P) # Get canonical dir. path
+	  # Output the ultimate target's canonical path.
+	  # Note that we manually resolve paths ending in /. and /.. to make sure we have a normalized path.
+	  if [ "$fname" = '.' ]; then
+	    command printf '%s\n' "${targetDir%/}"
+	  elif  [ "$fname" = '..' ]; then
+	    # Caveat: something like /var/.. will resolve to /private (assuming /var@ -> /private/var), i.e. the '..' is applied
+	    # AFTER canonicalization.
+	    command printf '%s\n' "$(command dirname -- "${targetDir}")"
+	  else
+	    command printf '%s\n' "${targetDir%/}/$fname"
+	  fi
+	)
+
+	get_script_dir() {
+
+	    # call this with $0 (from main script) as its (only) parameter
+	    # if you need to preserve cwd, run this is a subshell since
+	    # it can change cwd
+
+	    # set -x
+
+	    local_SCRIPT_PATH="$( get_abs_filename "$1" )";
+
+	    while [ -h "$local_SCRIPT_PATH" ];
+	    do
+	        cd "$( dirname -- "$local_SCRIPT_PATH"; )";
+	        local_SCRIPT_PATH="$( rreadlink "$local_SCRIPT_PATH" )";
+	    done
+
+	    cd "$( dirname -- "$local_SCRIPT_PATH"; )" > '/dev/null';
+	    local_SCRIPT_PATH="$( pwd; )";
+
+	    # set +x
+
+	    echo "${local_SCRIPT_PATH}"
+	}
+
+	# end of script directory functions
+	###############################################################################
+
+        # figure out the script dir
+        LOADUP_SCRIPTDIR="$(get_script_dir "$0")"
+	export LOADUP_SCRIPTDIR
+
 fi
 
-# shellcheck source=./loadup-setup.sh
-. scripts/loadup-setup.sh
-
-# look thru args looking to see if -apps, --apps, or -a was specified in args
-apps=true
-j=1
-jmax=$#
-while [ "$j" -le "$jmax" ]
-do
-  if [ "$(eval "printf %s \${${j}}")" = "-a" ] || \
-     [ "$(eval "printf %s \${${j}}")" = "-apps" ] || \
-     [ "$(eval "printf %s \${${j}}")" = "--apps" ]
-  then
-    apps="./scripts/loadup-apps-from-full.sh"
-    break
-  fi
-done
-
-#  Do loadup components
-./scripts/loadup-init.sh && \
-    ./scripts/loadup-mid-from-init.sh && \
-    ./scripts/loadup-lisp-from-mid.sh && \
-    ./scripts/loadup-full-from-lisp.sh && \
-    ${apps} && \
-    ./scripts/loadup-aux.sh && \
-    ./scripts/copy-all.sh "$1"
-
-if [ $? -eq 0 ]
-then
-  echo "+++++ loadup-all.sh: SUCCESS +++++"
-else
-  echo "----- loadup-all.sh: FAILURE -----"
-fi
-
-
-
-
-
+main "$@"
diff --git a/scripts/loadup-apps-from-full.sh b/scripts/loadup-apps-from-full.sh
index 38a2a59c..ea29da90 100755
--- a/scripts/loadup-apps-from-full.sh
+++ b/scripts/loadup-apps-from-full.sh
@@ -1,76 +1,171 @@
 #!/bin/sh
 
-if [ ! -h ./medley ] || [ ! -d ./lispusers ]
+main() {
+	# shellcheck source=./loadup-setup.sh
+	. "${LOADUP_SCRIPTDIR}/loadup-setup.sh"
+
+	loadup_start
+
+	export ROOMSDIR="${MEDLEYDIR}/rooms"
+	export CLOSDIR="${MEDLEYDIR}/clos"
+
+	export NOTECARDSDIR="${MEDLEYDIR}/notecards"
+	if [ ! -e "${NOTECARDSDIR}" ]
+	then
+	    NOTECARDSDIR=$(cd "${MEDLEYDIR}/../" && pwd)/notecards
+	    if [ ! -e "${NOTECARDSDIR}" ]
+	    then
+	        NOTECARDSDIR=$(cd "${MEDLEYDIR}/../../" && pwd)/notecards
+	        if [ ! -e "${NOTECARDSDIR}" ]
+	        then
+	            NOTECARDSDIR=""
+	        fi
+	    fi
+	fi
+
+	if [ -z "${NOTECARDSDIR}" ]
+	then
+	    echo "Error: Cannot find the Notecards directory"
+	    echo "It should be located at ${MEDLEYDIR}/../notecards or"
+	    echo "${MEDLEYDIR}/../../notecards.  But its not."
+	    echo "Exiting"
+	    exit 1
+	fi
+
+	cat >"${cmfile}" <<-"EOF"
+	"
+
+	(PROGN
+	   (IL:MEDLEY-INIT-VARS 'IL:GREET)
+	   (IL:DRIBBLE (IL:CONCAT (QUOTE {DSK})(IL:UNIX-GETENV(QUOTE LOADUP_WORKDIR))(IL:L-CASE (QUOTE /apps.dribble))))
+	   (IL:LOAD (IL:CONCAT (QUOTE {DSK}) (IL:UNIX-GETENV(QUOTE ROOMSDIR))(QUOTE /ROOMS)) 'IL:SYSLOAD)
+	   (IL:LOAD (IL:CONCAT (QUOTE {DSK}) (IL:UNIX-GETENV(QUOTE NOTECARDSDIR))(QUOTE |/system/NOTECARDS.LCOM|)) 'IL:SYSLOAD)
+	   (IL:LOAD (IL:CONCAT (QUOTE {DSK}) (IL:UNIX-GETENV(QUOTE CLOSDIR))(QUOTE /DEFSYS.DFASL)) 'IL:SYSLOAD)
+	   (IL:LOAD (IL:CONCAT (QUOTE {DSK}) (IL:UNIX-GETENV(QUOTE MEDLEYDIR))(QUOTE |lispusers/BUTTONS.LCOM|)) 'IL:SYSLOAD)
+	   (IL:LOAD
+	       (IL:CONCAT (QUOTE {DSK}) (IL:UNIX-GETENV (QUOTE LOADUP_SOURCEDIR)) (QUOTE /LOADUP-APPS.LCOM))
+	       'IL:SYSLOAD
+	   )
+	   (IL:HARDRESET)
+	)
+	SHH
+	(PROGN
+	   (IL:ENDLOADUP)
+	   (CLOS::LOAD-CLOS)
+	   (IL:|Apps.LOADUP|)
+	   (IL:DRIBBLE)
+	   (IL:MAKESYS
+	       (IL:CONCAT (QUOTE {DSK})(IL:UNIX-GETENV(QUOTE LOADUP_WORKDIR))(IL:L-CASE (QUOTE /apps.sysout)))
+	       :APPS)
+	)
+	(IL:LOGOUT T)
+
+	"
+	EOF
+
+	run_medley "${LOADUP_WORKDIR}/full.sysout"
+
+	loadup_finish "apps.sysout" "apps.*"
+}
+
+
+# shellcheck disable=SC2164,SC2034
+if [ -z "${LOADUP_SCRIPTDIR}" ]
 then
-    echo "*** ERROR ***"
-    echo "You must run $(basename "$0") while the cwd is a Medley top-level directory."
-    echo "The cwd ($(pwd)) is not a Medley top-level directory."
-    echo "Exiting."
-    exit 1
+	#
+	#
+	# Some functions to determine what directory this script is being executed from
+	#
+	#
+	get_abs_filename() {
+	  # $1 : relative filename
+	  echo "$(cd "$(dirname "$1")" && pwd)/$(basename "$1")"
+	}
+
+	# This function taken from
+	# https://stackoverflow.com/questions/29832037/how-to-get-script-directory-in-posix-sh
+	rreadlink() (
+
+	  # Execute this function in a *subshell* to localize variables and the effect of `cd`.
+
+	  target=$1
+	  fname=
+	  targetDir=
+	  CDPATH=
+
+	  # Try to make the execution environment as predictable as possible:
+	  # All commands below are invoked via `command`, so we must make sure that `command`
+	  # itself is not redefined as an alias or shell function.
+	  # (Note that command is too inconsistent across shells, so we don't use it.)
+	  # `command` is a *builtin* in bash, dash, ksh, zsh, and some platforms do not even have
+	  # an external utility version of it (e.g, Ubuntu).
+	  # `command` bypasses aliases and shell functions and also finds builtins 
+	  # in bash, dash, and ksh. In zsh, option POSIX_BUILTINS must be turned on for that
+	  # to happen.
+	  { \unalias command; \unset -f command; } >/dev/null 2>&1
+	  [ -n "$ZSH_VERSION" ] && options[POSIX_BUILTINS]=on # make zsh find *builtins* with `command` too.
+
+	  while :; do # Resolve potential symlinks until the ultimate target is found.
+	      [ -L "$target" ] || [ -e "$target" ] || { command printf '%s\n' "ERROR: '$target' does not exist." >&2; return 1; }
+	      command cd "$(command dirname -- "$target")" # Change to target dir; necessary for correct resolution of target path.
+	      fname=$(command basename -- "$target") # Extract filename.
+	      [ "$fname" = '/' ] && fname='' # !! curiously, `basename /` returns '/'
+	      if [ -L "$fname" ]; then
+	        # Extract [next] target path, which may be defined
+	        # *relative* to the symlink's own directory.
+	        # Note: We parse `ls -l` output to find the symlink target
+	        #       which is the only POSIX-compliant, albeit somewhat fragile, way.
+	        target=$(command ls -l "$fname")
+	        target=${target#* -> }
+	        continue # Resolve [next] symlink target.
+	      fi
+	      break # Ultimate target reached.
+	  done
+	  targetDir=$(command pwd -P) # Get canonical dir. path
+	  # Output the ultimate target's canonical path.
+	  # Note that we manually resolve paths ending in /. and /.. to make sure we have a normalized path.
+	  if [ "$fname" = '.' ]; then
+	    command printf '%s\n' "${targetDir%/}"
+	  elif  [ "$fname" = '..' ]; then
+	    # Caveat: something like /var/.. will resolve to /private (assuming /var@ -> /private/var), i.e. the '..' is applied
+	    # AFTER canonicalization.
+	    command printf '%s\n' "$(command dirname -- "${targetDir}")"
+	  else
+	    command printf '%s\n' "${targetDir%/}/$fname"
+	  fi
+	)
+
+	get_script_dir() {
+
+	    # call this with $0 (from main script) as its (only) parameter
+	    # if you need to preserve cwd, run this is a subshell since
+	    # it can change cwd
+
+	    # set -x
+
+	    local_SCRIPT_PATH="$( get_abs_filename "$1" )";
+
+	    while [ -h "$local_SCRIPT_PATH" ];
+	    do
+	        cd "$( dirname -- "$local_SCRIPT_PATH"; )";
+	        local_SCRIPT_PATH="$( rreadlink "$local_SCRIPT_PATH" )";
+	    done
+
+	    cd "$( dirname -- "$local_SCRIPT_PATH"; )" > '/dev/null';
+	    local_SCRIPT_PATH="$( pwd; )";
+
+	    # set +x
+
+	    echo "${local_SCRIPT_PATH}"
+	}
+
+	# end of script directory functions
+	###############################################################################
+
+        # figure out the script dir
+        LOADUP_SCRIPTDIR="$(get_script_dir "$0")"
+	export LOADUP_SCRIPTDIR
+
 fi
 
-# shellcheck source=./loadup-setup.sh
-. scripts/loadup-setup.sh
-
-loadup_start
-
-export ROOMSDIR="${MEDLEYDIR}/rooms"
-export CLOSDIR="${MEDLEYDIR}/clos"
-
-export NOTECARDSDIR="${MEDLEYDIR}/notecards"
-if [ ! -e "${NOTECARDSDIR}" ]
-then
-    NOTECARDSDIR=$(cd "${MEDLEYDIR}/../" && pwd)/notecards
-    if [ ! -e "${NOTECARDSDIR}" ]
-    then
-        NOTECARDSDIR=$(cd "${MEDLEYDIR}/../../" && pwd)/notecards
-        if [ ! -e "${NOTECARDSDIR}" ]
-        then
-            NOTECARDSDIR=""
-        fi
-    fi
-fi
-
-if [ -z "${NOTECARDSDIR}" ]
-then
-    echo "Error: Cannot find the Notecards directory"
-    echo "It should be located at ${MEDLEYDIR}/../notecards or"
-    echo "${MEDLEYDIR}/../../notecards.  But its not."
-    echo "Exiting"
-    exit 1
-fi
-
-cat >"${cmfile}" <<"EOF"
-"
-
-(PROGN
-   (IL:MEDLEY-INIT-VARS 'IL:GREET)
-   (IL:DRIBBLE (IL:CONCAT (QUOTE {DSK})(IL:UNIX-GETENV(QUOTE LOADUP_WORKDIR))(IL:L-CASE (QUOTE /apps.dribble))))
-   (IL:LOAD (IL:CONCAT (QUOTE {DSK}) (IL:UNIX-GETENV(QUOTE ROOMSDIR))(QUOTE /ROOMS)) 'IL:SYSLOAD)
-   (IL:LOAD (IL:CONCAT (QUOTE {DSK}) (IL:UNIX-GETENV(QUOTE NOTECARDSDIR))(QUOTE |/system/NOTECARDS.LCOM|)) 'IL:SYSLOAD)
-   (IL:LOAD (IL:CONCAT (QUOTE {DSK}) (IL:UNIX-GETENV(QUOTE CLOSDIR))(QUOTE /DEFSYS.DFASL)) 'IL:SYSLOAD)
-   (IL:LOAD (IL:CONCAT (QUOTE {DSK}) (IL:UNIX-GETENV(QUOTE MEDLEYDIR))(QUOTE |lispusers/BUTTONS.LCOM|)) 'IL:SYSLOAD)
-   (IL:LOAD
-       (IL:CONCAT (QUOTE {DSK}) (IL:UNIX-GETENV (QUOTE LOADUP_SOURCEDIR)) (QUOTE /LOADUP-APPS.LCOM))
-       'IL:SYSLOAD
-   )
-   (IL:HARDRESET)
-)
-SHH
-(PROGN
-   (IL:ENDLOADUP)
-   (CLOS::LOAD-CLOS)
-   (IL:|Apps.LOADUP|)
-   (IL:DRIBBLE)
-   (IL:MAKESYS
-       (IL:CONCAT (QUOTE {DSK})(IL:UNIX-GETENV(QUOTE LOADUP_WORKDIR))(IL:L-CASE (QUOTE /apps.sysout)))
-       :APPS)
-)
-(IL:LOGOUT T)
-
-"
-EOF
-
-run_medley "${LOADUP_WORKDIR}/full.sysout"
-
-loadup_finish "apps.sysout" "apps.*"
+main "$@"
diff --git a/scripts/loadup-aux.sh b/scripts/loadup-aux.sh
index 625394e6..1a613abb 100755
--- a/scripts/loadup-aux.sh
+++ b/scripts/loadup-aux.sh
@@ -1,36 +1,135 @@
 #!/bin/sh
 
-if [ ! -h ./medley ] || [ ! -d ./lispusers ]
+main() {
+
+	# shellcheck source=./loadup-setup.sh
+	. "${LOADUP_SCRIPTDIR}/loadup-setup.sh"
+
+
+	loadup_start
+
+	cat >"${cmfile}" <<-"EOF"
+	"
+	(PROG
+	  ((WORKDIR (IL:CONCAT (QUOTE {DSK}) (IL:UNIX-GETENV (QUOTE LOADUP_WORKDIR)) (QUOTE /))))
+	  (IL:MEDLEY-INIT-VARS)
+	  (IL:LOAD(QUOTE MEDLEY-UTILS))
+          (DRIBBLE (QUOTE {DSK}<TMP>FOOBAR))
+	  (IL:MAKE-EXPORTS-ALL (IL:CONCAT WORKDIR (IL:L-CASE (QUOTE exports.all))))
+	  (DRIBBLE)
+	  (IL:MAKE-WHEREIS-HASH
+	    (IL:CONCAT WORKDIR (IL:L-CASE (QUOTE whereis.dribble)))
+	    (IL:CONCAT WORKDIR (IL:L-CASE (QUOTE whereis.hash-tmp)))
+	    (IL:CONCAT WORKDIR (IL:L-CASE (QUOTE whereis.hash)))
+	  )
+	  (IL:LOGOUT T)
+	)
+	"
+	EOF
+
+	run_medley "${LOADUP_WORKDIR}/full.sysout"
+
+	loadup_finish "whereis.hash" "whereis.hash" "exports.all"
+}
+
+
+# shellcheck disable=SC2164,SC2034
+if [ -z "${LOADUP_SCRIPTDIR}" ]
 then
-    echo "*** ERROR ***"
-    echo "You must run $(basename "$0") while the cwd is a Medley top-level directory."
-    echo "The cwd ($(pwd)) is not a Medley top-level directory."
-    echo "Exiting."
-    exit 1
+	#
+	#
+	# Some functions to determine what directory this script is being executed from
+	#
+	#
+	get_abs_filename() {
+	  # $1 : relative filename
+	  echo "$(cd "$(dirname "$1")" && pwd)/$(basename "$1")"
+	}
+
+	# This function taken from
+	# https://stackoverflow.com/questions/29832037/how-to-get-script-directory-in-posix-sh
+	rreadlink() (
+
+	  # Execute this function in a *subshell* to localize variables and the effect of `cd`.
+
+	  target=$1
+	  fname=
+	  targetDir=
+	  CDPATH=
+
+	  # Try to make the execution environment as predictable as possible:
+	  # All commands below are invoked via `command`, so we must make sure that `command`
+	  # itself is not redefined as an alias or shell function.
+	  # (Note that command is too inconsistent across shells, so we don't use it.)
+	  # `command` is a *builtin* in bash, dash, ksh, zsh, and some platforms do not even have
+	  # an external utility version of it (e.g, Ubuntu).
+	  # `command` bypasses aliases and shell functions and also finds builtins 
+	  # in bash, dash, and ksh. In zsh, option POSIX_BUILTINS must be turned on for that
+	  # to happen.
+	  { \unalias command; \unset -f command; } >/dev/null 2>&1
+	  [ -n "$ZSH_VERSION" ] && options[POSIX_BUILTINS]=on # make zsh find *builtins* with `command` too.
+
+	  while :; do # Resolve potential symlinks until the ultimate target is found.
+	      [ -L "$target" ] || [ -e "$target" ] || { command printf '%s\n' "ERROR: '$target' does not exist." >&2; return 1; }
+	      command cd "$(command dirname -- "$target")" # Change to target dir; necessary for correct resolution of target path.
+	      fname=$(command basename -- "$target") # Extract filename.
+	      [ "$fname" = '/' ] && fname='' # !! curiously, `basename /` returns '/'
+	      if [ -L "$fname" ]; then
+	        # Extract [next] target path, which may be defined
+	        # *relative* to the symlink's own directory.
+	        # Note: We parse `ls -l` output to find the symlink target
+	        #       which is the only POSIX-compliant, albeit somewhat fragile, way.
+	        target=$(command ls -l "$fname")
+	        target=${target#* -> }
+	        continue # Resolve [next] symlink target.
+	      fi
+	      break # Ultimate target reached.
+	  done
+	  targetDir=$(command pwd -P) # Get canonical dir. path
+	  # Output the ultimate target's canonical path.
+	  # Note that we manually resolve paths ending in /. and /.. to make sure we have a normalized path.
+	  if [ "$fname" = '.' ]; then
+	    command printf '%s\n' "${targetDir%/}"
+	  elif  [ "$fname" = '..' ]; then
+	    # Caveat: something like /var/.. will resolve to /private (assuming /var@ -> /private/var), i.e. the '..' is applied
+	    # AFTER canonicalization.
+	    command printf '%s\n' "$(command dirname -- "${targetDir}")"
+	  else
+	    command printf '%s\n' "${targetDir%/}/$fname"
+	  fi
+	)
+
+	get_script_dir() {
+
+	    # call this with $0 (from main script) as its (only) parameter
+	    # if you need to preserve cwd, run this is a subshell since
+	    # it can change cwd
+
+	    # set -x
+
+	    local_SCRIPT_PATH="$( get_abs_filename "$1" )";
+
+	    while [ -h "$local_SCRIPT_PATH" ];
+	    do
+	        cd "$( dirname -- "$local_SCRIPT_PATH"; )";
+	        local_SCRIPT_PATH="$( rreadlink "$local_SCRIPT_PATH" )";
+	    done
+
+	    cd "$( dirname -- "$local_SCRIPT_PATH"; )" > '/dev/null';
+	    local_SCRIPT_PATH="$( pwd; )";
+
+	    # set +x
+
+	    echo "${local_SCRIPT_PATH}"
+	}
+
+	# end of script directory functions
+	###############################################################################
+
+        # figure out the script dir
+        LOADUP_SCRIPTDIR="$(get_script_dir "$0")"
+	export LOADUP_SCRIPTDIR
+
 fi
 
-# shellcheck source=./loadup-setup.sh
-. scripts/loadup-setup.sh
-
-loadup_start
-
-cat >"${cmfile}" <<"EOF"
-"
-(PROG
-  ((WORKDIR (IL:CONCAT (QUOTE {DSK}) (IL:UNIX-GETENV (QUOTE LOADUP_WORKDIR)) (QUOTE /))))
-  (IL:MEDLEY-INIT-VARS)
-  (IL:LOAD(QUOTE MEDLEY-UTILS))
-  (IL:MAKE-EXPORTS-ALL (IL:CONCAT WORKDIR (IL:L-CASE (QUOTE exports.all))))
-  (IL:MAKE-WHEREIS-HASH
-    (IL:CONCAT WORKDIR (IL:L-CASE (QUOTE whereis.dribble)))
-    (IL:CONCAT WORKDIR (IL:L-CASE (QUOTE whereis.hash-tmp)))
-    (IL:CONCAT WORKDIR (IL:L-CASE (QUOTE whereis.hash)))
-  )
-  (IL:LOGOUT T)
-)
-"
-EOF
-
-run_medley "${LOADUP_WORKDIR}/full.sysout"
-
-loadup_finish "whereis.hash" "whereis.hash" "exports.all"
+main "$@"
diff --git a/scripts/loadup-db-from-full.sh b/scripts/loadup-db-from-full.sh
index 16fd5206..5498c108 100755
--- a/scripts/loadup-db-from-full.sh
+++ b/scripts/loadup-db-from-full.sh
@@ -1,46 +1,141 @@
 #!/bin/sh
 
-if [ ! -h ./medley ] || [ ! -d ./lispusers ]
+main() {
+	# shellcheck source=./loadup-setup.sh
+	. "${LOADUP_SCRIPTDIR}/loadup-setup.sh"
+
+	loadup_start
+
+	SYSOUT="${MEDLEYDIR}/loadups/full.sysout"
+	if [ ! -f "${SYSOUT}" ];
+	then
+	  echo "Error: cannot find ${SYSOUT}.  Exiting."
+	  exit 1
+	fi
+
+	cat >"${cmfile}" <<-"EOF"
+	"
+
+	(PROG
+	  ((WORKDIR (IL:CONCAT (QUOTE {DSK}) (IL:UNIX-GETENV (QUOTE LOADUP_WORKDIR)) (QUOTE /))))
+	  (IL:MEDLEY-INIT-VARS)
+	  (IL:FILESLOAD MEDLEY-UTILS)
+	  (SETQ IL:DIRECTORIES (CONS (IL:UNIX-GETENV (QUOTE LOADUP_SOURCEDIR)) IL:DIRECTORIES))
+	  (IL:MAKE-FULLER-DB
+	    (IL:CONCAT WORKDIR (IL:L-CASE (QUOTE fuller.dribble)))
+	    (IL:CONCAT WORKDIR (IL:L-CASE (QUOTE fuller.database)))
+	    (IL:CONCAT WORKDIR (IL:L-CASE (QUOTE fuller.sysout)))
+	  )
+	  (IL:LOGOUT T)
+	)
+
+	"
+	EOF
+
+	run_medley "${SYSOUT}"
+
+	loadup_finish "fuller.database" "fuller*"
+}
+
+
+
+# shellcheck disable=SC2164,SC2034
+if [ -z "${LOADUP_SCRIPTDIR}" ]
 then
-    echo "*** ERROR ***"
-    echo "You must run $(basename "$0") while the cwd is a Medley top-level directory."
-    echo "The cwd ($(pwd)) is not a Medley top-level directory."
-    echo "Exiting."
-    exit 1
+	#
+	#
+	# Some functions to determine what directory this script is being executed from
+	#
+	#
+	get_abs_filename() {
+	  # $1 : relative filename
+	  echo "$(cd "$(dirname "$1")" && pwd)/$(basename "$1")"
+	}
+
+	# This function taken from
+	# https://stackoverflow.com/questions/29832037/how-to-get-script-directory-in-posix-sh
+	rreadlink() (
+
+	  # Execute this function in a *subshell* to localize variables and the effect of `cd`.
+
+	  target=$1
+	  fname=
+	  targetDir=
+	  CDPATH=
+
+	  # Try to make the execution environment as predictable as possible:
+	  # All commands below are invoked via `command`, so we must make sure that `command`
+	  # itself is not redefined as an alias or shell function.
+	  # (Note that command is too inconsistent across shells, so we don't use it.)
+	  # `command` is a *builtin* in bash, dash, ksh, zsh, and some platforms do not even have
+	  # an external utility version of it (e.g, Ubuntu).
+	  # `command` bypasses aliases and shell functions and also finds builtins 
+	  # in bash, dash, and ksh. In zsh, option POSIX_BUILTINS must be turned on for that
+	  # to happen.
+	  { \unalias command; \unset -f command; } >/dev/null 2>&1
+	  [ -n "$ZSH_VERSION" ] && options[POSIX_BUILTINS]=on # make zsh find *builtins* with `command` too.
+
+	  while :; do # Resolve potential symlinks until the ultimate target is found.
+	      [ -L "$target" ] || [ -e "$target" ] || { command printf '%s\n' "ERROR: '$target' does not exist." >&2; return 1; }
+	      command cd "$(command dirname -- "$target")" # Change to target dir; necessary for correct resolution of target path.
+	      fname=$(command basename -- "$target") # Extract filename.
+	      [ "$fname" = '/' ] && fname='' # !! curiously, `basename /` returns '/'
+	      if [ -L "$fname" ]; then
+	        # Extract [next] target path, which may be defined
+	        # *relative* to the symlink's own directory.
+	        # Note: We parse `ls -l` output to find the symlink target
+	        #       which is the only POSIX-compliant, albeit somewhat fragile, way.
+	        target=$(command ls -l "$fname")
+	        target=${target#* -> }
+	        continue # Resolve [next] symlink target.
+	      fi
+	      break # Ultimate target reached.
+	  done
+	  targetDir=$(command pwd -P) # Get canonical dir. path
+	  # Output the ultimate target's canonical path.
+	  # Note that we manually resolve paths ending in /. and /.. to make sure we have a normalized path.
+	  if [ "$fname" = '.' ]; then
+	    command printf '%s\n' "${targetDir%/}"
+	  elif  [ "$fname" = '..' ]; then
+	    # Caveat: something like /var/.. will resolve to /private (assuming /var@ -> /private/var), i.e. the '..' is applied
+	    # AFTER canonicalization.
+	    command printf '%s\n' "$(command dirname -- "${targetDir}")"
+	  else
+	    command printf '%s\n' "${targetDir%/}/$fname"
+	  fi
+	)
+
+	get_script_dir() {
+
+	    # call this with $0 (from main script) as its (only) parameter
+	    # if you need to preserve cwd, run this is a subshell since
+	    # it can change cwd
+
+	    # set -x
+
+	    local_SCRIPT_PATH="$( get_abs_filename "$1" )";
+
+	    while [ -h "$local_SCRIPT_PATH" ];
+	    do
+	        cd "$( dirname -- "$local_SCRIPT_PATH"; )";
+	        local_SCRIPT_PATH="$( rreadlink "$local_SCRIPT_PATH" )";
+	    done
+
+	    cd "$( dirname -- "$local_SCRIPT_PATH"; )" > '/dev/null';
+	    local_SCRIPT_PATH="$( pwd; )";
+
+	    # set +x
+
+	    echo "${local_SCRIPT_PATH}"
+	}
+
+	# end of script directory functions
+	###############################################################################
+
+        # figure out the script dir
+        LOADUP_SCRIPTDIR="$(get_script_dir "$0")"
+	export LOADUP_SCRIPTDIR
+
 fi
 
-# shellcheck source=./loadup-setup.sh
-. scripts/loadup-setup.sh
-
-loadup_start
-
-SYSOUT="${MEDLEYDIR}/loadups/full.sysout"
-if [ ! -f "${SYSOUT}" ];
-then
-  echo "Error: cannot find ${SYSOUT}.  Exiting."
-  exit 1
-fi
-
-cat >"${cmfile}" <<"EOF"
-"
-
-(PROG
-  ((WORKDIR (IL:CONCAT (QUOTE {DSK}) (IL:UNIX-GETENV (QUOTE LOADUP_WORKDIR)) (QUOTE /))))
-  (IL:MEDLEY-INIT-VARS)
-  (IL:FILESLOAD MEDLEY-UTILS)
-  (SETQ IL:DIRECTORIES (CONS (IL:UNIX-GETENV (QUOTE LOADUP_SOURCEDIR)) IL:DIRECTORIES))
-  (IL:MAKE-FULLER-DB
-    (IL:CONCAT WORKDIR (IL:L-CASE (QUOTE fuller.dribble)))
-    (IL:CONCAT WORKDIR (IL:L-CASE (QUOTE fuller.database)))
-    (IL:CONCAT WORKDIR (IL:L-CASE (QUOTE fuller.sysout)))
-  )
-  (IL:LOGOUT T)
-)
-
-"
-EOF
-
-run_medley "${SYSOUT}"
-
-loadup_finish "fuller.database" "fuller*"
-
+main "$@"
diff --git a/scripts/loadup-db.sh b/scripts/loadup-db.sh
index 1ed597c4..59f93ad2 100755
--- a/scripts/loadup-db.sh
+++ b/scripts/loadup-db.sh
@@ -1,26 +1,121 @@
 #!/bin/sh
-# shellcheck disable=SC2181
 
-if [ ! -h ./medley ] || [ ! -d ./lispusers ]
+main () {
+	# shellcheck source=./loadup-setup.sh
+	. "${LOADUP_SCRIPTDIR}/loadup-setup.sh"
+
+	/bin/sh "${LOADUP_SCRIPTDIR}/loadup-db-from-full.sh"  \
+	&& /bin/sh "${LOADUP_SCRIPTDIR}/copy-db.sh"
+
+        # shellcheck disable=SC2181
+	if [ $? -eq 0 ];
+	then
+	  echo "+++++ loadup-db.sh: SUCCESS +++++"
+	else
+	  echo "----- loadup-db.sh: FAILURE -----"
+	fi
+}
+
+# shellcheck disable=SC2164,SC2034
+if [ -z "${LOADUP_SCRIPTDIR}" ]
 then
-    echo "*** ERROR ***"
-    echo "You must run $(basename "$0") while the cwd is a Medley top-level directory."
-    echo "The cwd ($(pwd)) is not a Medley top-level directory."
-    echo "Exiting."
-    exit 1
+	#
+	#
+	# Some functions to determine what directory this script is being executed from
+	#
+	#
+	get_abs_filename() {
+	  # $1 : relative filename
+	  echo "$(cd "$(dirname "$1")" && pwd)/$(basename "$1")"
+	}
+
+	# This function taken from
+	# https://stackoverflow.com/questions/29832037/how-to-get-script-directory-in-posix-sh
+	rreadlink() (
+
+	  # Execute this function in a *subshell* to localize variables and the effect of `cd`.
+
+	  target=$1
+	  fname=
+	  targetDir=
+	  CDPATH=
+
+	  # Try to make the execution environment as predictable as possible:
+	  # All commands below are invoked via `command`, so we must make sure that `command`
+	  # itself is not redefined as an alias or shell function.
+	  # (Note that command is too inconsistent across shells, so we don't use it.)
+	  # `command` is a *builtin* in bash, dash, ksh, zsh, and some platforms do not even have
+	  # an external utility version of it (e.g, Ubuntu).
+	  # `command` bypasses aliases and shell functions and also finds builtins 
+	  # in bash, dash, and ksh. In zsh, option POSIX_BUILTINS must be turned on for that
+	  # to happen.
+	  { \unalias command; \unset -f command; } >/dev/null 2>&1
+	  [ -n "$ZSH_VERSION" ] && options[POSIX_BUILTINS]=on # make zsh find *builtins* with `command` too.
+
+	  while :; do # Resolve potential symlinks until the ultimate target is found.
+	      [ -L "$target" ] || [ -e "$target" ] || { command printf '%s\n' "ERROR: '$target' does not exist." >&2; return 1; }
+	      command cd "$(command dirname -- "$target")" # Change to target dir; necessary for correct resolution of target path.
+	      fname=$(command basename -- "$target") # Extract filename.
+	      [ "$fname" = '/' ] && fname='' # !! curiously, `basename /` returns '/'
+	      if [ -L "$fname" ]; then
+	        # Extract [next] target path, which may be defined
+	        # *relative* to the symlink's own directory.
+	        # Note: We parse `ls -l` output to find the symlink target
+	        #       which is the only POSIX-compliant, albeit somewhat fragile, way.
+	        target=$(command ls -l "$fname")
+	        target=${target#* -> }
+	        continue # Resolve [next] symlink target.
+	      fi
+	      break # Ultimate target reached.
+	  done
+	  targetDir=$(command pwd -P) # Get canonical dir. path
+	  # Output the ultimate target's canonical path.
+	  # Note that we manually resolve paths ending in /. and /.. to make sure we have a normalized path.
+	  if [ "$fname" = '.' ]; then
+	    command printf '%s\n' "${targetDir%/}"
+	  elif  [ "$fname" = '..' ]; then
+	    # Caveat: something like /var/.. will resolve to /private (assuming /var@ -> /private/var), i.e. the '..' is applied
+	    # AFTER canonicalization.
+	    command printf '%s\n' "$(command dirname -- "${targetDir}")"
+	  else
+	    command printf '%s\n' "${targetDir%/}/$fname"
+	  fi
+	)
+
+	get_script_dir() {
+
+	    # call this with $0 (from main script) as its (only) parameter
+	    # if you need to preserve cwd, run this is a subshell since
+	    # it can change cwd
+
+	    # set -x
+
+	    local_SCRIPT_PATH="$( get_abs_filename "$1" )";
+
+	    while [ -h "$local_SCRIPT_PATH" ];
+	    do
+	        cd "$( dirname -- "$local_SCRIPT_PATH"; )";
+	        local_SCRIPT_PATH="$( rreadlink "$local_SCRIPT_PATH" )";
+	    done
+
+	    cd "$( dirname -- "$local_SCRIPT_PATH"; )" > '/dev/null';
+	    local_SCRIPT_PATH="$( pwd; )";
+
+	    # set +x
+
+	    echo "${local_SCRIPT_PATH}"
+	}
+
+	# end of script directory functions
+	###############################################################################
+
+        # figure out the script dir
+        LOADUP_SCRIPTDIR="$(get_script_dir "$0")"
+	export LOADUP_SCRIPTDIR
+
 fi
 
-# shellcheck source=./loadup-setup.sh
-. scripts/loadup-setup.sh
-
-./scripts/loadup-db-from-full.sh && ./scripts/copy-db.sh
-
-if [ $? -eq 0 ];
-then
-  echo "+++++ loadup-db.sh: SUCCESS +++++"
-else
-  echo "----- loadup-db.sh: FAILURE -----"
-fi
+main "$@"
 
 
 
diff --git a/scripts/loadup-full-from-lisp.sh b/scripts/loadup-full-from-lisp.sh
index c190b96e..e3c036ca 100755
--- a/scripts/loadup-full-from-lisp.sh
+++ b/scripts/loadup-full-from-lisp.sh
@@ -1,38 +1,132 @@
 #!/bin/sh
 
-if [ ! -h ./medley ] || [ ! -d ./lispusers ]
+main() {
+	# shellcheck source=./loadup-setup.sh
+	. "${LOADUP_SCRIPTDIR}/loadup-setup.sh"
+
+	loadup_start
+
+	cat >"${cmfile}" <<-"EOF"
+	"
+
+	(PROGN
+	  (IL:LOAD (IL:CONCAT (QUOTE {DSK}) (IL:UNIX-GETENV (QUOTE LOADUP_SOURCEDIR))(QUOTE /LOADUP-FULL.LCOM)))
+	  (IL:LOADUP-FULL (IL:CONCAT (QUOTE {DSK}) (IL:UNIX-GETENV(QUOTE LOADUP_WORKDIR))(IL:L-CASE (QUOTE /full.dribble))))
+	  (IL:HARDRESET)
+	)
+	SHH
+	(PROGN
+	  (IL:ENDLOADUP)
+	  (IL:MAKESYS (IL:CONCAT (QUOTE {DSK})(IL:UNIX-GETENV(QUOTE LOADUP_WORKDIR))(IL:L-CASE (QUOTE /full.sysout))) :FULL))
+	  (IL:LOGOUT T)
+	)
+
+	"
+	EOF
+
+	run_medley "${LOADUP_WORKDIR}/lisp.sysout"
+
+	loadup_finish "full.sysout" "full.*"
+}
+
+
+# shellcheck disable=SC2164,SC2034
+if [ -z "${LOADUP_SCRIPTDIR}" ]
 then
-    echo "*** ERROR ***"
-    echo "You must run $(basename "$0") while the cwd is a Medley top-level directory."
-    echo "The cwd ($(pwd)) is not a Medley top-level directory."
-    echo "Exiting."
-    exit 1
+	#
+	#
+	# Some functions to determine what directory this script is being executed from
+	#
+	#
+	get_abs_filename() {
+	  # $1 : relative filename
+	  echo "$(cd "$(dirname "$1")" && pwd)/$(basename "$1")"
+	}
+
+	# This function taken from
+	# https://stackoverflow.com/questions/29832037/how-to-get-script-directory-in-posix-sh
+	rreadlink() (
+
+	  # Execute this function in a *subshell* to localize variables and the effect of `cd`.
+
+	  target=$1
+	  fname=
+	  targetDir=
+	  CDPATH=
+
+	  # Try to make the execution environment as predictable as possible:
+	  # All commands below are invoked via `command`, so we must make sure that `command`
+	  # itself is not redefined as an alias or shell function.
+	  # (Note that command is too inconsistent across shells, so we don't use it.)
+	  # `command` is a *builtin* in bash, dash, ksh, zsh, and some platforms do not even have
+	  # an external utility version of it (e.g, Ubuntu).
+	  # `command` bypasses aliases and shell functions and also finds builtins 
+	  # in bash, dash, and ksh. In zsh, option POSIX_BUILTINS must be turned on for that
+	  # to happen.
+	  { \unalias command; \unset -f command; } >/dev/null 2>&1
+	  [ -n "$ZSH_VERSION" ] && options[POSIX_BUILTINS]=on # make zsh find *builtins* with `command` too.
+
+	  while :; do # Resolve potential symlinks until the ultimate target is found.
+	      [ -L "$target" ] || [ -e "$target" ] || { command printf '%s\n' "ERROR: '$target' does not exist." >&2; return 1; }
+	      command cd "$(command dirname -- "$target")" # Change to target dir; necessary for correct resolution of target path.
+	      fname=$(command basename -- "$target") # Extract filename.
+	      [ "$fname" = '/' ] && fname='' # !! curiously, `basename /` returns '/'
+	      if [ -L "$fname" ]; then
+	        # Extract [next] target path, which may be defined
+	        # *relative* to the symlink's own directory.
+	        # Note: We parse `ls -l` output to find the symlink target
+	        #       which is the only POSIX-compliant, albeit somewhat fragile, way.
+	        target=$(command ls -l "$fname")
+	        target=${target#* -> }
+	        continue # Resolve [next] symlink target.
+	      fi
+	      break # Ultimate target reached.
+	  done
+	  targetDir=$(command pwd -P) # Get canonical dir. path
+	  # Output the ultimate target's canonical path.
+	  # Note that we manually resolve paths ending in /. and /.. to make sure we have a normalized path.
+	  if [ "$fname" = '.' ]; then
+	    command printf '%s\n' "${targetDir%/}"
+	  elif  [ "$fname" = '..' ]; then
+	    # Caveat: something like /var/.. will resolve to /private (assuming /var@ -> /private/var), i.e. the '..' is applied
+	    # AFTER canonicalization.
+	    command printf '%s\n' "$(command dirname -- "${targetDir}")"
+	  else
+	    command printf '%s\n' "${targetDir%/}/$fname"
+	  fi
+	)
+
+	get_script_dir() {
+
+	    # call this with $0 (from main script) as its (only) parameter
+	    # if you need to preserve cwd, run this is a subshell since
+	    # it can change cwd
+
+	    # set -x
+
+	    local_SCRIPT_PATH="$( get_abs_filename "$1" )";
+
+	    while [ -h "$local_SCRIPT_PATH" ];
+	    do
+	        cd "$( dirname -- "$local_SCRIPT_PATH"; )";
+	        local_SCRIPT_PATH="$( rreadlink "$local_SCRIPT_PATH" )";
+	    done
+
+	    cd "$( dirname -- "$local_SCRIPT_PATH"; )" > '/dev/null';
+	    local_SCRIPT_PATH="$( pwd; )";
+
+	    # set +x
+
+	    echo "${local_SCRIPT_PATH}"
+	}
+
+	# end of script directory functions
+	###############################################################################
+
+        # figure out the script dir
+        LOADUP_SCRIPTDIR="$(get_script_dir "$0")"
+	export LOADUP_SCRIPTDIR
+
 fi
 
-# shellcheck source=./loadup-setup.sh
-. scripts/loadup-setup.sh
-
-loadup_start
-
-cat >"${cmfile}" <<"EOF"
-"
-
-(PROGN
-  (IL:LOAD (IL:CONCAT (QUOTE {DSK}) (IL:UNIX-GETENV (QUOTE LOADUP_SOURCEDIR))(QUOTE /LOADUP-FULL.LCOM)))
-  (IL:LOADUP-FULL (IL:CONCAT (QUOTE {DSK}) (IL:UNIX-GETENV(QUOTE LOADUP_WORKDIR))(IL:L-CASE (QUOTE /full.dribble))))
-  (IL:HARDRESET)
-)
-SHH
-(PROGN
-  (IL:ENDLOADUP)
-  (IL:MAKESYS (IL:CONCAT (QUOTE {DSK})(IL:UNIX-GETENV(QUOTE LOADUP_WORKDIR))(IL:L-CASE (QUOTE /full.sysout))) :FULL))
-  (IL:LOGOUT T)
-)
-
-"
-EOF
-
-run_medley "${LOADUP_WORKDIR}/lisp.sysout"
-
-loadup_finish "full.sysout" "full.*"
-
+main "$@"
diff --git a/scripts/loadup-full.sh b/scripts/loadup-full.sh
index 385d9804..3df74171 100755
--- a/scripts/loadup-full.sh
+++ b/scripts/loadup-full.sh
@@ -1,29 +1,121 @@
 #!/bin/sh
-# shellcheck disable=SC2181
 
-if [ ! -h ./medley ] || [ ! -d ./lispusers ]
+main() {
+	# shellcheck source=./loadup-setup.sh
+	. "${LOADUP_SCRIPTDIR}/loadup-setup.sh"
+
+	/bin/sh "${LOADUP_SCRIPTDIR}/loadup-init.sh"              \
+	&& /bin/sh "${LOADUP_SCRIPTDIR}/loadup-mid-from-init.sh"  \
+	&& /bin/sh "${LOADUP_SCRIPTDIR}/loadup-lisp-from-mid.sh"  \
+	&& /bin/sh "${LOADUP_SCRIPTDIR}/loadup-full-from-lisp.sh" \
+	&& /bin/sh "${LOADUP_SCRIPTDIR}/copy-full.sh"             ;
+
+	# shellcheck disable=SC2181
+	if [ $? -eq 0 ];
+	then
+	  echo "+++++ loadup-full.sh: SUCCESS +++++"
+	else
+	  echo "----- loadup-full.sh: FAILURE -----"
+	fi
+}
+
+# shellcheck disable=SC2164,SC2034
+if [ -z "${LOADUP_SCRIPTDIR}" ]
 then
-    echo "*** ERROR ***"
-    echo "You must run $(basename "$0") while the cwd is a Medley top-level directory."
-    echo "The cwd ($(pwd)) is not a Medley top-level directory."
-    echo "Exiting."
-    exit 1
+	#
+	#
+	# Some functions to determine what directory this script is being executed from
+	#
+	#
+	get_abs_filename() {
+	  # $1 : relative filename
+	  echo "$(cd "$(dirname "$1")" && pwd)/$(basename "$1")"
+	}
+
+	# This function taken from
+	# https://stackoverflow.com/questions/29832037/how-to-get-script-directory-in-posix-sh
+	rreadlink() (
+
+	  # Execute this function in a *subshell* to localize variables and the effect of `cd`.
+
+	  target=$1
+	  fname=
+	  targetDir=
+	  CDPATH=
+
+	  # Try to make the execution environment as predictable as possible:
+	  # All commands below are invoked via `command`, so we must make sure that `command`
+	  # itself is not redefined as an alias or shell function.
+	  # (Note that command is too inconsistent across shells, so we don't use it.)
+	  # `command` is a *builtin* in bash, dash, ksh, zsh, and some platforms do not even have
+	  # an external utility version of it (e.g, Ubuntu).
+	  # `command` bypasses aliases and shell functions and also finds builtins 
+	  # in bash, dash, and ksh. In zsh, option POSIX_BUILTINS must be turned on for that
+	  # to happen.
+	  { \unalias command; \unset -f command; } >/dev/null 2>&1
+	  [ -n "$ZSH_VERSION" ] && options[POSIX_BUILTINS]=on # make zsh find *builtins* with `command` too.
+
+	  while :; do # Resolve potential symlinks until the ultimate target is found.
+	      [ -L "$target" ] || [ -e "$target" ] || { command printf '%s\n' "ERROR: '$target' does not exist." >&2; return 1; }
+	      command cd "$(command dirname -- "$target")" # Change to target dir; necessary for correct resolution of target path.
+	      fname=$(command basename -- "$target") # Extract filename.
+	      [ "$fname" = '/' ] && fname='' # !! curiously, `basename /` returns '/'
+	      if [ -L "$fname" ]; then
+	        # Extract [next] target path, which may be defined
+	        # *relative* to the symlink's own directory.
+	        # Note: We parse `ls -l` output to find the symlink target
+	        #       which is the only POSIX-compliant, albeit somewhat fragile, way.
+	        target=$(command ls -l "$fname")
+	        target=${target#* -> }
+	        continue # Resolve [next] symlink target.
+	      fi
+	      break # Ultimate target reached.
+	  done
+	  targetDir=$(command pwd -P) # Get canonical dir. path
+	  # Output the ultimate target's canonical path.
+	  # Note that we manually resolve paths ending in /. and /.. to make sure we have a normalized path.
+	  if [ "$fname" = '.' ]; then
+	    command printf '%s\n' "${targetDir%/}"
+	  elif  [ "$fname" = '..' ]; then
+	    # Caveat: something like /var/.. will resolve to /private (assuming /var@ -> /private/var), i.e. the '..' is applied
+	    # AFTER canonicalization.
+	    command printf '%s\n' "$(command dirname -- "${targetDir}")"
+	  else
+	    command printf '%s\n' "${targetDir%/}/$fname"
+	  fi
+	)
+
+	get_script_dir() {
+
+	    # call this with $0 (from main script) as its (only) parameter
+	    # if you need to preserve cwd, run this is a subshell since
+	    # it can change cwd
+
+	    # set -x
+
+	    local_SCRIPT_PATH="$( get_abs_filename "$1" )";
+
+	    while [ -h "$local_SCRIPT_PATH" ];
+	    do
+	        cd "$( dirname -- "$local_SCRIPT_PATH"; )";
+	        local_SCRIPT_PATH="$( rreadlink "$local_SCRIPT_PATH" )";
+	    done
+
+	    cd "$( dirname -- "$local_SCRIPT_PATH"; )" > '/dev/null';
+	    local_SCRIPT_PATH="$( pwd; )";
+
+	    # set +x
+
+	    echo "${local_SCRIPT_PATH}"
+	}
+
+	# end of script directory functions
+	###############################################################################
+
+        # figure out the script dir
+        LOADUP_SCRIPTDIR="$(get_script_dir "$0")"
+	export LOADUP_SCRIPTDIR
+
 fi
 
-# shellcheck source=./loadup-setup.sh
-. ./scripts/loadup-setup.sh
-
-./scripts/loadup-init.sh && \
-./scripts/loadup-mid-from-init.sh && \
-./scripts/loadup-lisp-from-mid.sh && \
-./scripts/loadup-full-from-lisp.sh && \
-./scripts/copy-full.sh
-
-if [ $? -eq 0 ];
-then
-  echo "+++++ loadup-full.sh: SUCCESS +++++"
-else
-  echo "----- loadup-full.sh: FAILURE -----"
-fi
-
-
+main "$@"
diff --git a/scripts/loadup-init.sh b/scripts/loadup-init.sh
index e61403fb..c8257fbe 100755
--- a/scripts/loadup-init.sh
+++ b/scripts/loadup-init.sh
@@ -1,50 +1,145 @@
 #!/bin/sh
 
-if [ ! -h ./medley ] || [ ! -d ./lispusers ]
+main() {
+	# shellcheck source=./loadup-setup.sh
+	. "${LOADUP_SCRIPTDIR}/loadup-setup.sh"
+
+	loadup_start
+
+	cat >"${cmfile}" <<-"EOF"
+	(* "make init files; this file is loaded as a 'greet' file by scripts/loadup-init.sh")
+
+	(SETQ MEDLEYDIR NIL)
+	(LOAD (CONCAT (UNIX-GETENV "MEDLEYDIR") "/sources/MEDLEYDIR.LCOM"))
+	(MEDLEY-INIT-VARS)
+	(CNDIR (UNIX-GETENV "LOADUP_WORKDIR"))
+	(DRIBBLE "init.dribble")
+
+	(UNADVISE)
+	(ADVISE 'PAGEFULLFN '(RETURN))
+	(ADVISE '(ERROR IN \DO-DEFINE-FILE-INFO) '(RETURN))
+	(MOVD? 'NILL 'SETTEMPLATE)
+	(DEFINEQ (RRE (LAMBDA (X Y) Y)))
+	(MOVD? 'RRE 'READ-READER-ENVIRONMENT)
+
+	(LOAD (CONCAT "{DSK}" (UNIX-GETENV "LOADUP_SOURCEDIR") "/" "MAKEINIT.LCOM"))
+	(PROG
+	  ((WORKDIR (CONCAT "{DSK}" (UNIX-GETENV "LOADUP_WORKDIR") "/"))
+	   (LOADUP-SOURCE-DIR (CONCAT "{DSK}" (UNIX-GETENV "LOADUP_SOURCEDIR") "/"))
+	  )
+	  (SETQ DIRECTORIES (CONS LOADUP-SOURCE-DIR DIRECTORIES))
+	  (RESETLST (RESETSAVE OK.TO.MODIFY.FNS T)
+	    (MAKEINITGREET (CONCAT WORKDIR "init.sysout") (CONCAT WORKDIR "init.dlinit"))
+	  )
+	)
+	(DRIBBLE)
+	(LOGOUT T)
+	STOP
+	EOF
+
+	run_medley "${LOADUP_SOURCEDIR}/starter.sysout"
+
+	loadup_finish "init.dlinit" "init.*" "RDSYS*" "I-NEW*"
+}
+
+
+# shellcheck disable=SC2164,SC2034
+if [ -z "${LOADUP_SCRIPTDIR}" ]
 then
-    echo "*** ERROR ***"
-    echo "You must run $(basename "$0") while the cwd is a Medley top-level directory."
-    echo "The cwd ($(pwd)) is not a Medley top-level directory."
-    echo "Exiting."
-    exit 1
+	#
+	#
+	# Some functions to determine what directory this script is being executed from
+	#
+	#
+	get_abs_filename() {
+	  # $1 : relative filename
+	  echo "$(cd "$(dirname "$1")" && pwd)/$(basename "$1")"
+	}
+
+	# This function taken from
+	# https://stackoverflow.com/questions/29832037/how-to-get-script-directory-in-posix-sh
+	rreadlink() (
+
+	  # Execute this function in a *subshell* to localize variables and the effect of `cd`.
+
+	  target=$1
+	  fname=
+	  targetDir=
+	  CDPATH=
+
+	  # Try to make the execution environment as predictable as possible:
+	  # All commands below are invoked via `command`, so we must make sure that `command`
+	  # itself is not redefined as an alias or shell function.
+	  # (Note that command is too inconsistent across shells, so we don't use it.)
+	  # `command` is a *builtin* in bash, dash, ksh, zsh, and some platforms do not even have
+	  # an external utility version of it (e.g, Ubuntu).
+	  # `command` bypasses aliases and shell functions and also finds builtins 
+	  # in bash, dash, and ksh. In zsh, option POSIX_BUILTINS must be turned on for that
+	  # to happen.
+	  { \unalias command; \unset -f command; } >/dev/null 2>&1
+	  [ -n "$ZSH_VERSION" ] && options[POSIX_BUILTINS]=on # make zsh find *builtins* with `command` too.
+
+	  while :; do # Resolve potential symlinks until the ultimate target is found.
+	      [ -L "$target" ] || [ -e "$target" ] || { command printf '%s\n' "ERROR: '$target' does not exist." >&2; return 1; }
+	      command cd "$(command dirname -- "$target")" # Change to target dir; necessary for correct resolution of target path.
+	      fname=$(command basename -- "$target") # Extract filename.
+	      [ "$fname" = '/' ] && fname='' # !! curiously, `basename /` returns '/'
+	      if [ -L "$fname" ]; then
+	        # Extract [next] target path, which may be defined
+	        # *relative* to the symlink's own directory.
+	        # Note: We parse `ls -l` output to find the symlink target
+	        #       which is the only POSIX-compliant, albeit somewhat fragile, way.
+	        target=$(command ls -l "$fname")
+	        target=${target#* -> }
+	        continue # Resolve [next] symlink target.
+	      fi
+	      break # Ultimate target reached.
+	  done
+	  targetDir=$(command pwd -P) # Get canonical dir. path
+	  # Output the ultimate target's canonical path.
+	  # Note that we manually resolve paths ending in /. and /.. to make sure we have a normalized path.
+	  if [ "$fname" = '.' ]; then
+	    command printf '%s\n' "${targetDir%/}"
+	  elif  [ "$fname" = '..' ]; then
+	    # Caveat: something like /var/.. will resolve to /private (assuming /var@ -> /private/var), i.e. the '..' is applied
+	    # AFTER canonicalization.
+	    command printf '%s\n' "$(command dirname -- "${targetDir}")"
+	  else
+	    command printf '%s\n' "${targetDir%/}/$fname"
+	  fi
+	)
+
+	get_script_dir() {
+
+	    # call this with $0 (from main script) as its (only) parameter
+	    # if you need to preserve cwd, run this is a subshell since
+	    # it can change cwd
+
+	    # set -x
+
+	    local_SCRIPT_PATH="$( get_abs_filename "$1" )";
+
+	    while [ -h "$local_SCRIPT_PATH" ];
+	    do
+	        cd "$( dirname -- "$local_SCRIPT_PATH"; )";
+	        local_SCRIPT_PATH="$( rreadlink "$local_SCRIPT_PATH" )";
+	    done
+
+	    cd "$( dirname -- "$local_SCRIPT_PATH"; )" > '/dev/null';
+	    local_SCRIPT_PATH="$( pwd; )";
+
+	    # set +x
+
+	    echo "${local_SCRIPT_PATH}"
+	}
+
+	# end of script directory functions
+	###############################################################################
+
+        # figure out the script dir
+        LOADUP_SCRIPTDIR="$(get_script_dir "$0")"
+	export LOADUP_SCRIPTDIR
+
 fi
 
-# shellcheck source=./loadup-setup.sh
-. scripts/loadup-setup.sh
-
-loadup_start
-
-cat >"${cmfile}" <<"EOF"
-(* "make init files; this file is loaded as a 'greet' file by scripts/loadup-init.sh")
-
-(SETQ MEDLEYDIR NIL)
-(LOAD (CONCAT (UNIX-GETENV "MEDLEYDIR") "/sources/MEDLEYDIR.LCOM"))
-(MEDLEY-INIT-VARS)
-(CNDIR (UNIX-GETENV "LOADUP_WORKDIR"))
-(DRIBBLE "init.dribble")
-
-(UNADVISE)
-(ADVISE 'PAGEFULLFN '(RETURN))
-(ADVISE '(ERROR IN \DO-DEFINE-FILE-INFO) '(RETURN))
-(MOVD? 'NILL 'SETTEMPLATE)
-(DEFINEQ (RRE (LAMBDA (X Y) Y)))
-(MOVD? 'RRE 'READ-READER-ENVIRONMENT)
-
-(LOAD (CONCAT "{DSK}" (UNIX-GETENV "LOADUP_SOURCEDIR") "/" "MAKEINIT.LCOM"))
-(PROG
-  ((WORKDIR (CONCAT "{DSK}" (UNIX-GETENV "LOADUP_WORKDIR") "/"))
-   (LOADUP-SOURCE-DIR (CONCAT "{DSK}" (UNIX-GETENV "LOADUP_SOURCEDIR") "/"))
-  )
-  (SETQ DIRECTORIES (CONS LOADUP-SOURCE-DIR DIRECTORIES))
-  (RESETLST (RESETSAVE OK.TO.MODIFY.FNS T)
-    (MAKEINITGREET (CONCAT WORKDIR "init.sysout") (CONCAT WORKDIR "init.dlinit"))
-  )
-)
-(DRIBBLE)
-(LOGOUT T)
-STOP
-EOF
-
-run_medley "${LOADUP_SOURCEDIR}/starter.sysout"
-
-loadup_finish "init.dlinit" "init.*" "RDSYS*" "I-NEW*"
+main "$@"
diff --git a/scripts/loadup-lisp-from-mid.sh b/scripts/loadup-lisp-from-mid.sh
index 21d33456..f640b796 100755
--- a/scripts/loadup-lisp-from-mid.sh
+++ b/scripts/loadup-lisp-from-mid.sh
@@ -1,39 +1,134 @@
 #!/bin/sh
 
-if [ ! -h ./medley ] || [ ! -d ./lispusers ]
+main() {
+	# shellcheck source=./loadup-setup.sh
+	. "${LOADUP_SCRIPTDIR}/loadup-setup.sh"
+
+	loadup_start
+
+	cat >"${cmfile}" <<-"EOF"
+	"
+
+	(PROGN
+	  (LOAD (CONCAT (QUOTE {DSK}) (UNIX-GETENV (QUOTE MEDLEYDIR)) (QUOTE /sources/MEDLEYDIR.LCOM)))
+	  (MEDLEY-INIT-VARS)
+	  (LOAD (CONCAT (QUOTE {DSK}) (UNIX-GETENV (QUOTE LOADUP_SOURCEDIR)) (QUOTE /LOADUP-LISP.LCOM)))
+	  (LOADUP-LISP (CONCAT (QUOTE {DSK}) (UNIX-GETENV (QUOTE LOADUP_WORKDIR)) (QUOTE /lisp.dribble)))
+	  (HARDRESET)
+	)
+	SHH
+	(PROGN
+	  (IL:ENDLOADUP)
+	  (IL:MAKESYS (IL:CONCAT (QUOTE {DSK}) (IL:UNIX-GETENV(QUOTE LOADUP_WORKDIR)) (IL:L-CASE (QUOTE /lisp.sysout))) :LISP)
+	  (IL:LOGOUT T)
+	)
+
+	"
+	EOF
+
+	run_medley "${LOADUP_WORKDIR}/init-mid.sysout"
+
+	loadup_finish "lisp.sysout" "lisp.*"
+}
+
+
+# shellcheck disable=SC2164,SC2034
+if [ -z "${LOADUP_SCRIPTDIR}" ]
 then
-    echo "*** ERROR ***"
-    echo "You must run $(basename "$0") while the cwd is a Medley top-level directory."
-    echo "The cwd ($(pwd)) is not a Medley top-level directory."
-    echo "Exiting."
-    exit 1
+	#
+	#
+	# Some functions to determine what directory this script is being executed from
+	#
+	#
+	get_abs_filename() {
+	  # $1 : relative filename
+	  echo "$(cd "$(dirname "$1")" && pwd)/$(basename "$1")"
+	}
+
+	# This function taken from
+	# https://stackoverflow.com/questions/29832037/how-to-get-script-directory-in-posix-sh
+	rreadlink() (
+
+	  # Execute this function in a *subshell* to localize variables and the effect of `cd`.
+
+	  target=$1
+	  fname=
+	  targetDir=
+	  CDPATH=
+
+	  # Try to make the execution environment as predictable as possible:
+	  # All commands below are invoked via `command`, so we must make sure that `command`
+	  # itself is not redefined as an alias or shell function.
+	  # (Note that command is too inconsistent across shells, so we don't use it.)
+	  # `command` is a *builtin* in bash, dash, ksh, zsh, and some platforms do not even have
+	  # an external utility version of it (e.g, Ubuntu).
+	  # `command` bypasses aliases and shell functions and also finds builtins 
+	  # in bash, dash, and ksh. In zsh, option POSIX_BUILTINS must be turned on for that
+	  # to happen.
+	  { \unalias command; \unset -f command; } >/dev/null 2>&1
+	  [ -n "$ZSH_VERSION" ] && options[POSIX_BUILTINS]=on # make zsh find *builtins* with `command` too.
+
+	  while :; do # Resolve potential symlinks until the ultimate target is found.
+	      [ -L "$target" ] || [ -e "$target" ] || { command printf '%s\n' "ERROR: '$target' does not exist." >&2; return 1; }
+	      command cd "$(command dirname -- "$target")" # Change to target dir; necessary for correct resolution of target path.
+	      fname=$(command basename -- "$target") # Extract filename.
+	      [ "$fname" = '/' ] && fname='' # !! curiously, `basename /` returns '/'
+	      if [ -L "$fname" ]; then
+	        # Extract [next] target path, which may be defined
+	        # *relative* to the symlink's own directory.
+	        # Note: We parse `ls -l` output to find the symlink target
+	        #       which is the only POSIX-compliant, albeit somewhat fragile, way.
+	        target=$(command ls -l "$fname")
+	        target=${target#* -> }
+	        continue # Resolve [next] symlink target.
+	      fi
+	      break # Ultimate target reached.
+	  done
+	  targetDir=$(command pwd -P) # Get canonical dir. path
+	  # Output the ultimate target's canonical path.
+	  # Note that we manually resolve paths ending in /. and /.. to make sure we have a normalized path.
+	  if [ "$fname" = '.' ]; then
+	    command printf '%s\n' "${targetDir%/}"
+	  elif  [ "$fname" = '..' ]; then
+	    # Caveat: something like /var/.. will resolve to /private (assuming /var@ -> /private/var), i.e. the '..' is applied
+	    # AFTER canonicalization.
+	    command printf '%s\n' "$(command dirname -- "${targetDir}")"
+	  else
+	    command printf '%s\n' "${targetDir%/}/$fname"
+	  fi
+	)
+
+	get_script_dir() {
+
+	    # call this with $0 (from main script) as its (only) parameter
+	    # if you need to preserve cwd, run this is a subshell since
+	    # it can change cwd
+
+	    # set -x
+
+	    local_SCRIPT_PATH="$( get_abs_filename "$1" )";
+
+	    while [ -h "$local_SCRIPT_PATH" ];
+	    do
+	        cd "$( dirname -- "$local_SCRIPT_PATH"; )";
+	        local_SCRIPT_PATH="$( rreadlink "$local_SCRIPT_PATH" )";
+	    done
+
+	    cd "$( dirname -- "$local_SCRIPT_PATH"; )" > '/dev/null';
+	    local_SCRIPT_PATH="$( pwd; )";
+
+	    # set +x
+
+	    echo "${local_SCRIPT_PATH}"
+	}
+
+	# end of script directory functions
+	###############################################################################
+
+        # figure out the script dir
+        LOADUP_SCRIPTDIR="$(get_script_dir "$0")"
+	export LOADUP_SCRIPTDIR
+
 fi
 
-# shellcheck source=./loadup-setup.sh
-. scripts/loadup-setup.sh
-
-loadup_start
-
-cat >"${cmfile}" <<"EOF"
-"
-
-(PROGN
-  (LOAD (CONCAT (QUOTE {DSK}) (UNIX-GETENV (QUOTE MEDLEYDIR)) (QUOTE /sources/MEDLEYDIR.LCOM)))
-  (MEDLEY-INIT-VARS)
-  (LOAD (CONCAT (QUOTE {DSK}) (UNIX-GETENV (QUOTE LOADUP_SOURCEDIR)) (QUOTE /LOADUP-LISP.LCOM)))
-  (LOADUP-LISP (CONCAT (QUOTE {DSK}) (UNIX-GETENV (QUOTE LOADUP_WORKDIR)) (QUOTE /lisp.dribble)))
-  (HARDRESET)
-)
-SHH
-(PROGN
-  (IL:ENDLOADUP)
-  (IL:MAKESYS (IL:CONCAT (QUOTE {DSK}) (IL:UNIX-GETENV(QUOTE LOADUP_WORKDIR)) (IL:L-CASE (QUOTE /lisp.sysout))) :LISP)
-  (IL:LOGOUT T)
-)
-
-"
-EOF
-
-run_medley "${LOADUP_WORKDIR}/init-mid.sysout"
-
-loadup_finish "lisp.sysout" "lisp.*"
+main "$@"
diff --git a/scripts/loadup-mid-from-init.sh b/scripts/loadup-mid-from-init.sh
index bcb2075c..ed7bc94d 100755
--- a/scripts/loadup-mid-from-init.sh
+++ b/scripts/loadup-mid-from-init.sh
@@ -1,30 +1,131 @@
 #!/bin/sh
 
-if [ ! -h ./medley ] || [ ! -d ./lispusers ]
+main() {
+	# shellcheck source=./loadup-setup.sh
+	. "${LOADUP_SCRIPTDIR}/loadup-setup.sh"
+
+	loadup_start
+
+	cat >"${cmfile}" <<-"EOF"
+	"
+	(MOVD? (QUOTE NILL) (QUOTE PROMPTPRINT))
+	(MOVD? (QUOTE NILL) (QUOTE CURSORP))
+	(MOVD? (QUOTE NILL) (QUOTE CHANGEBACKGROUNDBORDER))
+	(LOGOUT)
+	"
+	EOF
+
+	run_medley "${LOADUP_WORKDIR}/init.dlinit" -NF -prog ldeinit --vmem "${LOADUP_WORKDIR}/init-mid.sysout"
+        if [ "${exit_code}" -eq 54 ]
+        then
+          echo "NOTE: The loadup script $0 requires the ldeinit executable"
+          echo "in addition to the lde executable be available in MAIKODIR."
+          echo "The ldeinit executable could not be found."
+	fi
+
+	echo " "
+
+	loadup_finish "init-mid.sysout" "init-mid.sysout"
+}
+
+
+# shellcheck disable=SC2164,SC2034
+if [ -z "${LOADUP_SCRIPTDIR}" ]
 then
-    echo "*** ERROR ***"
-    echo "You must run $(basename "$0") while the cwd is a Medley top-level directory."
-    echo "The cwd ($(pwd)) is not a Medley top-level directory."
-    echo "Exiting."
-    exit 1
+	#
+	#
+	# Some functions to determine what directory this script is being executed from
+	#
+	#
+	get_abs_filename() {
+	  # $1 : relative filename
+	  echo "$(cd "$(dirname "$1")" && pwd)/$(basename "$1")"
+	}
+
+	# This function taken from
+	# https://stackoverflow.com/questions/29832037/how-to-get-script-directory-in-posix-sh
+	rreadlink() (
+
+	  # Execute this function in a *subshell* to localize variables and the effect of `cd`.
+
+	  target=$1
+	  fname=
+	  targetDir=
+	  CDPATH=
+
+	  # Try to make the execution environment as predictable as possible:
+	  # All commands below are invoked via `command`, so we must make sure that `command`
+	  # itself is not redefined as an alias or shell function.
+	  # (Note that command is too inconsistent across shells, so we don't use it.)
+	  # `command` is a *builtin* in bash, dash, ksh, zsh, and some platforms do not even have
+	  # an external utility version of it (e.g, Ubuntu).
+	  # `command` bypasses aliases and shell functions and also finds builtins 
+	  # in bash, dash, and ksh. In zsh, option POSIX_BUILTINS must be turned on for that
+	  # to happen.
+	  { \unalias command; \unset -f command; } >/dev/null 2>&1
+	  [ -n "$ZSH_VERSION" ] && options[POSIX_BUILTINS]=on # make zsh find *builtins* with `command` too.
+
+	  while :; do # Resolve potential symlinks until the ultimate target is found.
+	      [ -L "$target" ] || [ -e "$target" ] || { command printf '%s\n' "ERROR: '$target' does not exist." >&2; return 1; }
+	      command cd "$(command dirname -- "$target")" # Change to target dir; necessary for correct resolution of target path.
+	      fname=$(command basename -- "$target") # Extract filename.
+	      [ "$fname" = '/' ] && fname='' # !! curiously, `basename /` returns '/'
+	      if [ -L "$fname" ]; then
+	        # Extract [next] target path, which may be defined
+	        # *relative* to the symlink's own directory.
+	        # Note: We parse `ls -l` output to find the symlink target
+	        #       which is the only POSIX-compliant, albeit somewhat fragile, way.
+	        target=$(command ls -l "$fname")
+	        target=${target#* -> }
+	        continue # Resolve [next] symlink target.
+	      fi
+	      break # Ultimate target reached.
+	  done
+	  targetDir=$(command pwd -P) # Get canonical dir. path
+	  # Output the ultimate target's canonical path.
+	  # Note that we manually resolve paths ending in /. and /.. to make sure we have a normalized path.
+	  if [ "$fname" = '.' ]; then
+	    command printf '%s\n' "${targetDir%/}"
+	  elif  [ "$fname" = '..' ]; then
+	    # Caveat: something like /var/.. will resolve to /private (assuming /var@ -> /private/var), i.e. the '..' is applied
+	    # AFTER canonicalization.
+	    command printf '%s\n' "$(command dirname -- "${targetDir}")"
+	  else
+	    command printf '%s\n' "${targetDir%/}/$fname"
+	  fi
+	)
+
+	get_script_dir() {
+
+	    # call this with $0 (from main script) as its (only) parameter
+	    # if you need to preserve cwd, run this is a subshell since
+	    # it can change cwd
+
+	    # set -x
+
+	    local_SCRIPT_PATH="$( get_abs_filename "$1" )";
+
+	    while [ -h "$local_SCRIPT_PATH" ];
+	    do
+	        cd "$( dirname -- "$local_SCRIPT_PATH"; )";
+	        local_SCRIPT_PATH="$( rreadlink "$local_SCRIPT_PATH" )";
+	    done
+
+	    cd "$( dirname -- "$local_SCRIPT_PATH"; )" > '/dev/null';
+	    local_SCRIPT_PATH="$( pwd; )";
+
+	    # set +x
+
+	    echo "${local_SCRIPT_PATH}"
+	}
+
+	# end of script directory functions
+	###############################################################################
+
+        # figure out the script dir
+        LOADUP_SCRIPTDIR="$(get_script_dir "$0")"
+	export LOADUP_SCRIPTDIR
+
 fi
 
-# shellcheck source=./loadup-setup.sh
-. scripts/loadup-setup.sh
-
-loadup_start
-
-cat >"${cmfile}" <<"EOF"
-"
-(MOVD? (QUOTE NILL) (QUOTE PROMPTPRINT))
-(MOVD? (QUOTE NILL) (QUOTE CURSORP))
-(MOVD? (QUOTE NILL) (QUOTE CHANGEBACKGROUNDBORDER))
-(LOGOUT)
-"
-EOF
-
-run_medley "${LOADUP_WORKDIR}/init.dlinit" -NF -prog ldeinit --vmem "${LOADUP_WORKDIR}/init-mid.sysout"
-
-echo " "
-
-loadup_finish "init-mid.sysout" "init-mid.sysout"
+main "$@"
diff --git a/scripts/loadup-setup.sh b/scripts/loadup-setup.sh
index f055f198..e764844c 100644
--- a/scripts/loadup-setup.sh
+++ b/scripts/loadup-setup.sh
@@ -1,7 +1,7 @@
-#!sh
+#!to_be_sourced_only
 # shellcheck shell=sh
 
-MEDLEYDIR="$(pwd)"
+MEDLEYDIR=$(cd "${LOADUP_SCRIPTDIR}/.."; pwd)
 export MEDLEYDIR
 
 if [ -z "${LOADUP_WORKDIR}" ]
@@ -141,18 +141,19 @@ loadup_finish () {
 run_medley () {
   if [ ! "${LOADUP_OLDSCHOOL}" = true ]
   then
-    ./medley --config -                                 \
-             --id loadup_+                                     \
-             --geometry "${geometry}"                   \
-             --noscroll                                 \
-             --logindir "${LOADUP_LOGINDIR}"            \
-             --greet "${cmfile}"                        \
-             --sysout "$1"                              \
-             "$2" "$3" "$4" "$5" "$6" "$7"              ;
+    /bin/sh "${MEDLEYDIR}/scripts/medley/medley.command"  \
+             --config -                                          \
+             --id loadup_+                                       \
+             --geometry "${geometry}"                            \
+             --noscroll                                          \
+             --logindir "${LOADUP_LOGINDIR}"                     \
+             --greet "${cmfile}"                                 \
+             --sysout "$1"                                       \
+             "$2" "$3" "$4" "$5" "$6" "$7"                       ;
     exit_code=$?
   else
     # shellcheck disable=SC2086
-    ./run-medley ${scr} $2 $3 $4 $5 $6 $7 -loadup "${cmfile}" "$1"
+    "${MEDLEYDIR}/run-medley" ${scr} $2 $3 $4 $5 $6 $7 -loadup "${cmfile}" "$1"
     exit_code=$?
   fi
 
