1 /*
2 * pkg_mngr.i
3 * $Id: pkg_mngr.i,v 1.19 2008-11-18 13:47:27 paumard Exp $
4 * Yorick package manager
5 */
6 /* Copyright (c) 2005, The Regents of the University of California.
7 * All rights reserved.
8 * This file is part of yorick (http://yorick.sourceforge.net).
9 * Read the accompanying LICENSE file for details.
10 */
11
12 /*= SECTION() yorick package manager ======================================*/
13
14 PKG_MNGR_VERSION = 0.7;
15
16 if (!PKG_SETUP) PKG_SETUP = Y_HOME+"packages/pkg_setup.i";
17
18 local pkg_mngr;
19 /* DOCUMENT pkg_mngr.i
20 *
21 * Yorick Package Manager. Main functions
22 *
23 * pkg_setup Set up pkg_mngr parameters (see below for details)
24 * pkg_sync Sync the local package repository with server
25 * pkg_list List all available packages (list install status
26 * ("i" marks installed packages), the package name,
27 * the last available version, the installed version
28 * (when applicable) and a short description.
29 * pkg_install,"pkg_name" Install package "pkg_name"
30 * pkg_upgrade,"pkg_name" Upgrade package "pkg_name"
31 * pkg_remove,"pkg_name" Remove package "pkg_name"
32 * pkg_info,"pkg_name" Print detail info about package "pkg_name"
33 * pkg_save Saves PKG global variables in default file,
34 * which will be re-read each time pkg-mngr.i
35 * is included. Called by pkg_setup.
36 * pkg_reset Delete all tarballs (in case of problem).
37 *
38 * --- INTRODUCTION TO THE YORICK PACKAGE INSTALLER
39 *
40 * --- TYPICAL USE
41 *
42 * FIRST TIME USE:
43 *
44 * > pkg_setup
45 * ... will ask for parameters for your OS/installation
46 * ... just set the OS, most of the other defaults should be OK.
47 *
48 * PKG_FETCH_CMD : system cmd to fetch a file through an
49 * internet connection [ex: curl]
50 * PKG_SERVER : URL of central server with info files
51 * PKG_OS : OSTYPE-ARCHTYPE. There is a limited number
52 * of these available. pkg_setup will fetch
53 * available value from the server and propose
54 * a list to the user.
55 * PKG_GUNTAR_CMD : system cmd to gunzip and untar a tgz file
56 * [ex; "tar -zxf"]
57 * PKG_TMP_DIR : temporary directory. Normaly Y_HOME/packages/tmp
58 * PKG_VERBOSE : verbose level
59 * PKG_ASK_CONFIRM : ask for confirmation in critical operations
60 * PKG_RUN_CHECK : run check after install
61 *
62 * > pkg_sync
63 * ...to sync your local info file repository with the server
64 * ...this should be done from time to time.
65 *
66 * ---
67 *
68 * > pkg_list
69 * ...fetch all packages .info files on the server, and prints out
70 * the available & installed packages, with their version.
71 *
72 * > pkg_install,"pkgname"
73 * ... will install package "pkgname"
74 *
75 * FURTHER USE:
76 * pkg_list, pkg_install, and pkg_remove are the only 3 functions
77 * that should be necessary for further use.
78 *
79 * --- IN CASE OF PROBLEMS:
80 * In case something went wrong and you downloaded a bad tarball:
81 * Solution 1: try reloading with force=1
82 * Solution 2: Go in Y_HOME/packages/tarballs
83 * and remove the offensive tgz file. In doubt, you can just wipe
84 * up the whole directory content. tarballs will be downloaded again
85 * if pkg_mngr does not find existing local ones.
86 *
87 * --- SERVER / CLIENT ORGANIZATION
88 *
89 * The yorick package manager (pkg_mngr.i) is organized with several
90 * central servers (sourceforge, maumae), being repositories of binary
91 * packages. These packages are self-contained plugins for yorick, and
92 * include yorick include files, autoload files and libraries. Necessary
93 * dependencies have been linked in. Other yorick package dependecies
94 * are cared for, and dependent packages are installed automatically.
95 *
96 * Locally, the directory package is the home of the packager. It contains
97 * 3 subdirectories:
98 * packages/info: home of the info files that describe all available
99 * packages.
100 * packages/installed: info files for installed packages.
101 * packages/tarballs: tarballs for installed packages.
102 *
103 * --- PACKAGE STRUCTURE:
104 *
105 * A standard package includes (once tar zxvf'd):
106 *
107 *
108 * root/
109 * root/pkg.info : package info file
110 * root/preflight.i : if present, will be run, from Y_HOME/packages,
111 * before the copying of the other files (but
112 * after the tar -zxvf or equivalent).
113 * root/postflight.i : if present, will be run, from Y_HOME/packages,
114 * after the copying of the other files (but
115 * before the dist tree cleanup).
116 * root/dist/ : distribution root
117 * root/dist/y_site/ : all the files under this will be copied
118 * recursively into Y_SITE.
119 * root/dist/y_home/ : all the files under this will be copied
120 * recursively into Y_HOME.
121 *
122 * I recommend to include and distribute a copy of the package source:
123 * It is (1) educational, (2) easier to rebuild if needed and (3) that's
124 * a good place to store a check.i and other example for the package (and
125 * perhaps a doc). A good place to store the source is Y_SITE/contrib/pkg.
126 *
127 * Here is an example for the HDF5 package:
128 *
129 * poliahu:tarballs% tar zxvf hdf5-0.5.tgz
130 * hdf5/
131 * hdf5/dist/
132 * hdf5/dist/check.i
133 * hdf5/dist/y_home/
134 * hdf5/dist/y_home/i-start/
135 * hdf5/dist/y_home/i-start/yhdf5.i
136 * hdf5/dist/y_home/lib/
137 * hdf5/dist/y_home/lib/hdf5.so
138 * hdf5/dist/y_site/
139 * hdf5/dist/y_site/contrib/
140 * hdf5/dist/y_site/contrib/hdf5/
141 * hdf5/dist/y_site/contrib/hdf5/check.i
142 * hdf5/dist/y_site/contrib/hdf5/hdf5.c
143 * hdf5/dist/y_site/contrib/hdf5/hdf5.i
144 * hdf5/dist/y_site/contrib/hdf5/Makefile
145 * hdf5/dist/y_site/contrib/hdf5/yhdf5.i
146 * hdf5/dist/y_site/i0/
147 * hdf5/dist/y_site/i0/hdf5.i
148 * hdf5/hdf5.info
149 *
150 * --- INSTALLER MECHANICS
151 *
152 * The operation of installing a packages goes through two main parts:
153 *
154 * part 1. Get the tarball from the server.
155 *
156 * part 2. Once the tarball is local, it is installed. All the libraries
157 * and other files are put where they belong. Usually:
158 * libraries (*.so) are put in Y_HOME/lib
159 * include files (*.i) are put in Y_SITE/i0
160 * autoload files are put in Y_HOME/i-start
161 * However, this is not mandatory, and depends on how the maintainer
162 * has arrange the files in the tarball.
163 * The installer also scans for a preflight and postflight include files,
164 * and run them (include them) if present.
165 *
166 * In more details:
167 *
168 * pkgname example = hdf5;
169 *
170 * I. check for depenency tree. Announce dependencies, possibly
171 * ask for confirm.
172 * II. Start by lowest dependency:
173 * 1. check if already on disk locally [to come]
174 * 2. if yes, check that local version is the last one [to come]
175 * 3. if not, offer the choice to re-install local version
176 * or fetch and install new one [to come]
177 * 4. fetch tarball (if necessary)
178 * 5. md5 it [to come]
179 * 6. gunzip + untar it (e.g. tar -zxvf)
180 * 7. cd in pkgname directory
181 * 8. check if preflight exist. If it does, execute it (include it).
182 * 9. recursively copy the distributed files in Y_SITE and Y_HOME
183 * 10. copy the info file in the installed directory, with a list
184 * of the installed files.
185 * 11. check if postflight exist. If it does, execute it (include it).
186 * 12. clean up after ourselves (temp dist tree). Keep tarball.
187 *
188 * III. Update installed pkg. This is done automatically by putting
189 * the info file of the installed package in package/installed
190 *
191 *
192 * --- .INFO FILE
193 *
194 * Here is an example of a .info file (from the hdf5 package):
195 *
196 * Package: hdf5
197 * Kind: plugin
198 * Version: 0.5
199 * Revision: 1
200 * Description: Hierarchical Data Format 5 interface
201 * License: GPL
202 * Maintainer: Francois Rigaut <frigaut@users.sf.net>
203 * OS: macosx
204 * Depends: yorick(>=1.6.02)
205 * Source: http://www.maumae.net/yorick/packages/%o/tarballs/hdf5-%v.tgz
206 * Source-MD5: 6f8038cd09f72f4ff060e2d278256b6f
207 * Source-Directory: contrib/hdf5
208 * DocFiles: README NEWS doc/*.doc doc/*.pdf doc/*.ps doc/*.tex
209 * Homepage: http://www.maumae.net/yorick/doc/plugins.php
210 * DescDetail: <<
211 * HDF5 is the yorick interface plugin to the NCSA Hierarchical Data Format
212 * version 5. It includes function for reading, writing, updating, getting
213 * information on HDF5 files.
214 * <<
215 * DescUsage: <<
216 * See i/hdf5_tests.i for a test suite. Type
217 * "yorick -batch hdf5_tests.i" in a terminal to run it.
218 * <<
219 * DescPort: <<
220 * This package will compile Yorick only on MacOSX 10.3.4 or later, because
221 * of a bug in the system math library libm (part of /usr/lib/LibSystem.dylib)
222 * in earlier versions of MacOSX 10.3.
223 * <<
224 *
225 * Right now, the only important (used) keywords are:
226 * Version, Description, OS, Depends, Source
227 *
228 * If you want to contribute a package, you will have to generate a
229 * .info file:
230 *
231 * Instructions and tips:
232 * - Keep the description short (< 45 characters), you can expand ad lib in
233 * the DescDetail
234 * - Depends can include several members, separated by a comma (e.g.
235 * yorick(>=1.6.02),imutil(>0.4)
236 * - OS has to be "macosx","linux" or "windows" for now. lowercase pls.
237 * - Source if the URL where the tarball can be fetched. In the near future,
238 * I'm planning to make that compatible with a vector (several URL that
239 * will be tried after another in case a link is down) but for now,
240 * keep that a single URL.
241 * - Version can only contain integer (e.g. 2.1.4-r2 does *not* work).
242 *
243 *
244 * --- HISTORY
245 *
246 * * v. 0.7, Sun, 21 May 2006 19:00:55 +0200
247 * Francois Rigaut & Thibaut Paumard
248 * Allow installations in other locations than the Y_SITE and Y_HOME.
249 * Some users may not have access to these and still want to install
250 * packages for their personnal use.
251 *
252 * --- TO DO
253 *
254 * * Check documentation relative to new functionalities in v. 0.7
255 *
256 * * At one point, we should have, to complement this installer, a
257 * utility to check for symbols conflicts (e.g. same function names
258 * in 2 different packages)
259 *
260 */
261
262 require,"string.i";
263 require,"pathfun.i";
264
265 struct pkginfo_str{
266 string name;
267 string kind;
268 string vers;
269 string revision;
270 string desc;
271 string license;
272 string maintainer;
273 string os;
274 string depends;
275 string depends_pkg(20);
276 string depends_vers(20);
277 string depends_rel(20);
278 string source;
279 string md5;
280 string dir;
281 string docs;
282 string homepage;
283 string desc_details;
284 long version(20);
285 };
286
has_write_permissions(dir)287 func has_write_permissions(dir)
288 /* DOCUMENT has_write_permissions(dir)
289 Test if user has write permission in "dir"
290 returns 1 if the user has write permission, 0 if not.
291 Create and delete a file "test_permissions_$USER", with
292 $USER = name of the user as defined in this session environment
293 SEE ALSO:
294 */
295 {
296 f=open(dir+"test_permissions_"+get_env("USER"),"w",1);
297 if (!f) return 0;
298 close,f;
299 remove,dir+"test_permissions_"+get_env("USER");
300 return 1;
301 }
302
303 setup_done=0; // check is setup variables have been set
304 user_setup_done=0; // has the set-up been done at the user level?
305
306 // read packages/pkg_setup.i if it exists
307 if (open(PKG_SETUP,"r",1)) {
308 extern pkg_other_installed;
309 require,PKG_SETUP;
310
311 // start FR: check the pkg_setup just read is the user one.
312 if (has_write_permissions(dirname(PKG_SETUP))) {
313 // all OK: This user has permission on this part of the file system.
314 // If this was not true, then most likely the user has not done
315 // his/her own pkg_setup, but is reading the system one. In that
316 // case, a pkg_sync or pkg_install is likely to end up in error.
317 user_setup_done=1;
318 } else { // else user_setup_done=0 above is kept.
319 write,format="%s\n","Use \"pkg_setup\" to set up pkg_mngr";
320 }
321 // end FR
322
323 // TP: make sure we didn't load an empty file
324 if (PKG_OS) {
325 // backward compatibility
326 if (!PKG_VAR_STATE) PKG_VAR_STATE = Y_HOME+"packages/";
327 if (!PKG_Y_HOME) PKG_Y_HOME = Y_HOME;
328 if (!PKG_Y_SITE) PKG_Y_SITE = Y_SITE;
329 if (!PKG_OTHER_INSTALLED) PKG_OTHER_INSTALLED = "";
330 pkg_other_installed=pathsplit(PKG_OTHER_INSTALLED);
331 setup_done=1;
332 } else {
333 write,format="Warning: Empty file \"%s\"\n",PKG_SETUP;
334 } // end BC
335 // end TP
336 } else { // else no set-up file found.
337 write,format="%s\n","Use \"pkg_setup\" to set up pkg_mngr";
338 }
339
340
341 func pkg_save
342 /* DOCUMENT pkg_save
343 Save the packager parameters in the file
344 Y_HOME/packages/pkg_setup.i
345 SEE ALSO: pkg_setup
346 */
347 {
348 mkdirp,dirname(PKG_SETUP);
349 f = open(PKG_SETUP,"w",1);
350 if (!f) error,"Can't create "+PKG_SETUP+" (permissions?)";
351 write,f,format="PKG_OS = \"%s\";\n",PKG_OS;
352 write,f,format="PKG_FETCH_CMD = \"%s\";\n",PKG_FETCH_CMD;
353 write,f,format="PKG_SERVER = \"%s\";\n",PKG_SERVER;
354 write,f,format="PKG_GUNTAR_CMD = \"%s\";\n",PKG_GUNTAR_CMD;
355 write,f,format="PKG_TMP_DIR = \"%s\";\n",PKG_TMP_DIR;
356 write,f,format="PKG_VERBOSE = %d;\n",PKG_VERBOSE;
357 write,f,format="PKG_ASK_CONFIRM = %d;\n",PKG_ASK_CONFIRM;
358 write,f,format="PKG_RUN_CHECK = %d;\n",PKG_RUN_CHECK;
359 // TP: here I add my new variables (paths)
360 write,f,format="PKG_VAR_STATE = \"%s\";\n",PKG_VAR_STATE;
361 write,f,format="PKG_Y_HOME = \"%s\";\n",PKG_Y_HOME;
362 write,f,format="PKG_Y_SITE = \"%s\";\n",PKG_Y_SITE;
363 write,f,format="PKG_OTHER_INSTALLED = \"%s\";\n",PKG_OTHER_INSTALLED;
364 // end_TP
365 if (PKG_SYNC_DONE) write,f,format="PKG_SYNC_DONE = \"%s\";\n",PKG_SYNC_DONE;
366 close,f;
367
368 if (PKG_VERBOSE) write,format="%s\n","Parameters saved in "+PKG_SETUP;
369 }
370
371
372
373 func pkg_sync(server,verbose=)
374 /* DOCUMENT pkg_sync(server)
375 sync the info tree with the central server
376 server: overrides the PKG_SERVER variable definition
377 SEE ALSO: pkg_mngr, pkg_list, pkg_install
378 */
379 {
380 extern PKG_SYNC_DONE;
381 if (!(setup_done & user_setup_done)) pkg_setup,first=1;
382
383 if (!server) server = PKG_SERVER;
384 if (verbose==[]) verbose=PKG_VERBOSE;
385
386 mkdirp,PKG_VAR_STATE;
387
388 if (noneof(lsdir(PKG_VAR_STATE)=="info"))
389 mkdir,PKG_VAR_STATE+"info";
390 if (noneof(lsdir(PKG_VAR_STATE)=="installed"))
391 mkdir,PKG_VAR_STATE+"installed";
392 if (noneof(lsdir(PKG_VAR_STATE)=="tarballs"))
393 mkdir,PKG_VAR_STATE+"tarballs";
394 if (noneof(lsdir(PKG_VAR_STATE)=="tmp"))
395 mkdir,PKG_VAR_STATE+"tmp";
396
397
398 if (verbose) write,format="%s","Syncing with server";
399 if (verbose>=3) write,"";
400
401
402 pkg_fetch_url,server+PKG_OS+"/info/",PKG_TMP_DIR+"info.html",
403 verbose=verbose;
404
405 if (verbose<3) write,format="%s",".";
406
407 ctn = rdfile(PKG_TMP_DIR+"info.html");
408 files = strpart(ctn,strgrep("[a-zA-Z0-9\_\.\+\-]+\\.info",ctn));
409 files = files(where(files));
410
411
412 for (i=1;i<=numberof(files);i++) {
413 pkg_fetch_url,server+PKG_OS+"/info/"+files(i), \
414 PKG_VAR_STATE+"info/"+files(i),verbose=verbose;
415 if (verbose<3) write,format="%s",".";
416 }
417
418 if (verbose<3) write,format="done (%d info files fetched)\n",numberof(files);
419 PKG_SYNC_DONE = getdate();
420 pkg_save;
421 }
422
get_international_date(date)423 func get_international_date(date)
424 /* DOCUMENT get_intern_date(date)
425 meant to convert date returned by getdate() into something
426 clear and understandable by people from europe and US
427 SEE ALSO:
428 */
429 {
430 if (!date) return;
431 d = strtok(date,"/",3);
432 n = 0;
433 sread,d(2),n;
434 month = ["Jan","Feb","Mar","Apr","May","Jun",
435 "Jul","Aug","Sep","Oct","Nov","Dec"];
436 return d(1)+" "+month(n)+" 20"+d(3); //valid for 95 years
437 }
438
439 func pkg_list(server,sync=,verbose=)
440 /* DOCUMENT pkg_list,server,sync=,verbose=
441 Print out a list of available packages, including
442 version number, whether it is installed (installed
443 version), and a short description.
444 server: overrides the PKG_SERVER variable definition
445 sync= : if set, equivalent to "pkg_sync; pkg_list;"
446 SEE ALSO: pkg_mngr, pkg_sync, pkg_install
447 */
448 {
449 extern PKG_SYNC_DONE;
450 if (!setup_done) pkg_setup,first=1; // can list if user_setup not done
451 if (!PKG_SYNC_DONE) pkg_sync;
452
453 if (sync) {
454 pkg_sync,server,verbose=verbose;
455 } else {
456 write,format="last sync %s (\"pkg_sync\" to sync)\n",get_international_date(PKG_SYNC_DONE);
457 }
458 if (verbose==[]) verbose=PKG_VERBOSE;
459
460 infodir = PKG_VAR_STATE+"info/";
461 instdir = PKG_VAR_STATE+"installed/";
462
463 infoname = get_avail_pkg();
464 if (numberof(infoname)==0) error,"No info file found in "+infodir;
465
466 instname = get_inst_pkg();
467 if (instname==0) instname="";
468
469 instnameo=[];
470 for (in=1;in<=numberof(pkg_other_installed);in++) {
471 junk=get_inst_pkg(pkg_other_installed(in));
472 if (junk!=0 & !is_void(junk)) {
473 grow,instnameo,junk;
474 grow,instdiro,array(pkg_other_installed(in),numberof(junk));
475 }
476 }
477
478 text = [];
479
480 for (i=1;i<=numberof(infoname);i++) {
481 // for all info file, parse file:
482 pkg = parse_info_file(infodir+infoname(i)+".info");
483
484 is_inst = " ";
485 instvers = "-";
486
487 // is this package installed locally?
488 w = where(instname==infoname(i));
489 // is yes, what version?
490 if (numberof(w)) {
491 is_inst = "i";
492 instvers = (parse_info_file(instdir+instname(w(1))+".info")).vers;
493 if (instvers<pkg.vers) is_inst="u"; // upgradable (2007dec26)
494 }
495
496 // is pkg installed in another place?
497 w = where(instnameo==infoname(i));
498 if (numberof(w)) {
499 is_inst += "o";
500 if (instvers=="-") {
501 instvers = (parse_info_file(instdiro(w(1))+instnameo(w(1))+".info")).vers;
502 }
503 }
504
505 if (am_subroutine()) {
506 write,format="%-2s %-12s %-8s %-8s %-45s\n", is_inst,infoname(i),\
507 pkg.vers,instvers,pkg.desc;
508 } else {
509 grow,text,swrite(format="%-2s %-12s %-8s %-8s %-45s\n", is_inst,infoname(i),\
510 pkg.vers,instvers,pkg.desc);
511 }
512 }
513 return text;
514 }
515
516
517
pkg_info(pkgname)518 func pkg_info(pkgname)
519 /* DOCUMENT pkg_info,pkgname
520 Prints out more information about package "pkgname" (string).
521 Does nothing else.
522 SEE ALSO:
523 */
524 {
525 if (!setup_done) pkg_setup,first=1;
526 if (!PKG_SYNC_DONE) pkg_sync;
527
528 infodir = PKG_VAR_STATE+"info/";
529
530 pkg = parse_info_file(infodir+pkgname+".info");
531
532 write,format="Package : %s\n",pkg.name;
533 write,format="Version : %s\n",pkg.vers;
534 write,format="Maintainer : %s\n",pkg.maintainer;
535 write,format="Depends : %s\n",pkg.depends;
536 write,format="Homepage : %s\n",pkg.homepage;
537 write,format="Description: \n%s\n",pkg.desc_details;
538 write,format="\n%s\n","\"pkg_list\" to see installed status";
539 }
540
541 func pkg_setup(first=)
542 /* DOCUMENT pkg_setup
543 Simply print out the global variables relative to this
544 package manager.
545 SEE ALSO: pkg_mngr, pkg_save.
546 */
547 {
548 extern setup_done, user_setup_done;
549 extern pkg_other_installed;
550 extern PKG_Y_HOME, PKG_Y_SITE, PKG_TMP_DIR, PKG_VAR_STATE, PKG_OTHER_INSTALLED;
551 extern PKG_SYNC_DONE;
552 if (first) {
553 write,format="%s\n\n",
554 "pkg_mngr was not setup! You need to go through this set-up once";
555 }
556 write,format="%s\n","Enter set-up parameters for pkg_mngr ([] = default)";
557 write,format="%s\n","The default values should be ok, so if you don't know";
558 write,format="%s\n","what to enter, just press return.";
559
560 if (!PKG_Y_HOME) PKG_Y_HOME = Y_HOME;
561 if (!PKG_Y_SITE) PKG_Y_SITE = Y_SITE;
562
563 // refine a bit the default for PKG_Y_HOME in case user setup has not been
564 // done, i.e. user is not superuser.
565 if (!user_setup_done) {
566 if (!has_write_permissions(PKG_Y_HOME)) {
567 if (!is_void(PKG_VAR_STATE))
568 pkg_other_installed=grow([PKG_VAR_STATE+"installed/"],pkg_other_installed);
569 PKG_OTHER_INSTALLED=pathform(pkg_other_installed);
570 PKG_Y_HOME = Y_USER;
571 PKG_VAR_STATE = PKG_Y_HOME+"packages/";
572 }
573 if (!has_write_permissions(PKG_Y_SITE)) PKG_Y_SITE=PKG_Y_HOME;
574 if (!PKG_OTHER_INSTALLED) PKG_OTHER_INSTALLED = "";
575 pkg_other_installed=pathsplit(PKG_OTHER_INSTALLED);
576 } // end !user_setup_done
577
578 if (!PKG_VAR_STATE) PKG_VAR_STATE = PKG_Y_HOME+"packages/";
579 if (!PKG_OS) PKG_OS=get_env("OSTYPE")+"-"+get_env("MACHTYPE");
580 if (!PKG_FETCH_CMD) PKG_FETCH_CMD="curl -s";
581 if (!PKG_GUNTAR_CMD) PKG_GUNTAR_CMD="tar zxf";
582 if (!PKG_SERVER) PKG_SERVER="http://www.maumae.net/yorick/packages/";
583 if (!PKG_TMP_DIR) PKG_TMP_DIR=PKG_VAR_STATE+"tmp/";
584 if (PKG_VERBOSE==[]) PKG_VERBOSE=1;
585 if (PKG_ASK_CONFIRM==[]) PKG_ASK_CONFIRM=1;
586 if (PKG_RUN_CHECK==[]) PKG_RUN_CHECK=0;
587
588
589 write,format="\n%s\n","System command syntax to fetch URL?";
590 PKG_FETCH_CMD = strtrim(kinput("PKG_FETCH_CMD",PKG_FETCH_CMD));
591 write,format="\n%s\n","Where to retrieve binary packages?";
592 PKG_SERVER = strtrim(kinput("PKG_SERVER",PKG_SERVER));
593
594 // 2007dec26: modify order to fetch possible OSes/architectures
595 oses = pkg_probe_oses(verbose=verbose);
596 write,format="\n%s\n","What is your OS-machine type?";
597 for (i=1;i<=numberof(oses);i++) write,format=" (%d) %s\n",i,oses(i);
598 osn=0;
599 while ((osn<1)|(osn>numberof(oses))) read,prompt="PKG_OS (enter a number): ",osn;
600 PKG_OS = oses(osn);
601
602 write,format="\n%s\n","System command syntax to untar the package tarballs?";
603 PKG_GUNTAR_CMD = strtrim(kinput("PKG_GUNTAR_CMD",PKG_GUNTAR_CMD));
604 // PKG_TMP_DIR = kinput("PKG_TMP_DIR",PKG_TMP_DIR);
605 write,format="\n%s\n","Verbose level for pkg_mngr commands (more=more chatty)?";
606 PKG_VERBOSE = kinput("PKG_VERBOSE (0|1|2|3)",PKG_VERBOSE);
607 write,format="\n%s\n","Ask confirmation before installing packages?";
608 PKG_ASK_CONFIRM = kinput("PKG_ASK_CONFIRM (0|1)",PKG_ASK_CONFIRM);
609 write,format="\n%s\n","Run package check.i when installing?";
610 PKG_RUN_CHECK = kinput("PKG_RUN_CHECK",PKG_RUN_CHECK);
611
612 // TP: my new variables
613 old_pkg_home = PKG_Y_HOME;
614 old_var=PKG_VAR_STATE;
615
616 write,format="\n%s\n",
617 "Path to install architecture-dependent files?";
618 PKG_Y_HOME = strtrim(kinput("PKG_Y_HOME",PKG_Y_HOME));
619 if (strpart(PKG_Y_HOME,0:0)!="/") PKG_Y_HOME+="/";
620
621 changing_root=(PKG_Y_HOME!=old_pkg_home);
622 if (changing_root) {
623 // PKG_Y_HOME has changed. Use the new value to define defaults for the
624 // rest of our variabes.
625 if (PKG_Y_HOME==Y_HOME) PKG_Y_SITE=Y_SITE; else PKG_Y_SITE=PKG_Y_HOME;
626 if (PKG_VAR_STATE==old_pkg_home+"packages/")
627 PKG_VAR_STATE=PKG_Y_HOME+"packages/";
628 if (PKG_SETUP==old_pkg_home+"packages/pkg_setup.i")
629 PKG_SETUP=PKG_Y_HOME+"packages/pkg_setup.i";
630 }
631
632 write,format="\n%s\n%s\n",
633 "Path to install architecture-independent files?",
634 "It is generally advisable to use PKG_Y_SITE=PKG_Y_HOME.";
635 PKG_Y_SITE = strtrim(kinput("PKG_Y_SITE",PKG_Y_SITE));
636 if (strpart(PKG_Y_SITE,0:0)!="/") PKG_Y_SITE+="/";
637
638 write,format="\n%s\n",
639 "Where should pkg_mngr store its data?";
640 PKG_VAR_STATE = strtrim(kinput("PKG_VAR_STATE",PKG_VAR_STATE));
641 if (strpart(PKG_VAR_STATE,0:0)!="/") PKG_VAR_STATE+="/";
642
643 write,format="\n%s\n",
644 "Where are other package \"installed\" directories on this system?";
645 write,format="%s\n",
646 "Space for empty string. If you don't know what this is, leave it alone.";
647 if (!PKG_OTHER_INSTALLED | changing_root | PKG_OTHER_INSTALLED=="") {
648 if (!is_void(PKG_OTHER_INSTALLED) & PKG_OTHER_INSTALLED!="")
649 write,format="%s%s\n",
650 "Old value of PKG_OTHER_INSTALLED: ",PKG_OTHER_INSTALLED;
651 if (!is_void(Y_HOMES)) {
652 w = where(Y_HOMES==PKG_Y_HOME);
653 if (numberof(w)==0) w=0; else w=w(1);
654 if (w<numberof(Y_HOMES))
655 pkg_other_installed = Y_HOMES(w+1:)+"packages/installed/";
656 }
657 if (PKG_VAR_STATE!=Y_HOME+"packages/")
658 grow,pkg_other_installed,Y_HOME+"packages/installed/";
659 if (pkg_other_installed!=[]) pkg_other_installed =
660 pkg_other_installed(where(pkg_other_installed!=""));
661 PKG_OTHER_INSTALLED = pathform(pkg_other_installed);
662 }
663 PKG_OTHER_INSTALLED = strtrim(kinput("PKG_OTHER_INSTALLED",PKG_OTHER_INSTALLED));
664
665 if (strlen(PKG_OTHER_INSTALLED)>0) {
666 pkg_other_installed = pathsplit(PKG_OTHER_INSTALLED);
667 w = where(!strglob("*/",pkg_other_installed)&(pkg_other_installed!=""));
668 if (numberof(w)) {
669 pkg_other_installed(w)+="/";
670 PKG_OTHER_INSTALLED = pathform(pkg_other_installed);
671 }
672 }
673
674 if (PKG_SETUP==Y_HOME+"packages/pkg_setup.i" |
675 !has_write_permissions(dirname(PKG_SETUP)))
676 PKG_SETUP=PKG_VAR_STATE+"pkg_setup.i";
677 write,format="\n%s\n",
678 "Where should this information be stored?";
679 PKG_SETUP = kinput("PKG_SETUP",PKG_SETUP);
680 while (!strglob("*[^/].i",PKG_SETUP)) {
681 write,format="%s\n",
682 "Please enter a filename ending in .i";
683 PKG_SETUP = kinput("PKG_SETUP",PKG_SETUP);
684 }
685 // end TP
686
687 write,format="\nPKG_OS = \"%s\"\n",PKG_OS;
688 write,format="PKG_FETCH_CMD = \"%s\"\n",PKG_FETCH_CMD;
689 write,format="PKG_SERVER = \"%s\"\n",PKG_SERVER;
690 write,format="PKG_GUNTAR_CMD = \"%s\"\n",PKG_GUNTAR_CMD;
691 // write,format="PKG_TMP_DIR = %s\n",PKG_TMP_DIR;
692 write,format="PKG_VERBOSE = %d\n",PKG_VERBOSE;
693 write,format="PKG_ASK_CONFIRM = %d\n",PKG_ASK_CONFIRM;
694 write,format="PKG_RUN_CHECK = %d\n",PKG_RUN_CHECK;
695 write,format="PKG_Y_HOME = \"%s\";\n",PKG_Y_HOME;
696 write,format="PKG_Y_SITE = \"%s\";\n",PKG_Y_SITE;
697 write,format="PKG_VAR_STATE = \"%s\";\n",PKG_VAR_STATE;
698 write,format="PKG_OTHER_INSTALLED = \"%s\";\n",PKG_OTHER_INSTALLED;
699
700 if (PKG_VAR_STATE!=old_var) {
701 }
702
703 PKG_SYNC_DONE=[];
704 PKG_TMP_DIR=PKG_VAR_STATE+"tmp/";
705
706 setup_done = 1;
707 user_setup_done = 1;
708 pkg_save;
709
710 // inform the user about paths
711
712 if (PKG_Y_HOME != Y_HOME | PKG_Y_SITE != Y_SITE) {
713 write,format="%s\n", "\n"+
714 "You need to make sure the various Yorick paths will take the relevant\n"+
715 "sub-directories of PKG_Y_HOME and PKG_Y_SITE into account, and that future\n"+
716 "runs of pkg_mngr will know where to find PKG_SETUP.\n"+
717 "\n"+
718 "This is easily done by putting the following lines in any startup file,\n"+
719 "e.g. any .i file in Y_HOME/i-start/ or ~/.yorick/i-start/:\n";
720 write,format=" require,\"pathfun.i\";\n"+" PKG_SETUP=\"%s\";\n",PKG_SETUP;
721 if (PKG_Y_HOME==PKG_Y_SITE)
722 write,format=" add_y_home, \"%s\";\n\n",PKG_Y_HOME;
723 else
724 write,format=" add_y_home, \"%s\", \"%s\";\n\n",PKG_Y_HOME,PKG_Y_SITE;
725 write_pkg_setup_start,1;
726 } else if (PKG_SETUP!=Y_HOME+"packages/pkg_setup.i") {
727 write,format="%s\n","\n"+
728 "You need to make sure that future runs of pkg_mngr will know where\n"+
729 "to find PKG_SETUP.\n"+
730 "\n"+
731 "This is easily done by putting the following line in any startup file,\n"+
732 "e.g. any .i file in Y_HOME/i-start/ or ~/.yorick/i-start/:\n";
733 write,format="PKG_SETUP=\"%s\";\n",PKG_SETUP;
734 write_pkg_setup_start,2;
735 }
736 if (PKG_Y_HOME==Y_HOME & PKG_Y_SITE!=Y_SITE) {
737 write,format="%s\n","\n"+
738 "WARNING: you set PKG_Y_HOME==Y_HOME but PKG_Y_SITE!=Y_SITE.\n"+
739 "Expect trouble.\n"+
740 "I strongly advise you to rerun pkg_setup and set PKG_Y_SITE\n"+
741 "and PKG_Y_HOME to sane values.\n\n";
742 } else if (PKG_Y_HOME!=Y_HOME & PKG_Y_SITE==Y_SITE) {
743 write,format="%s\n","\n"+
744 "WARNING: you set PKG_Y_HOME!=Y_HOME but PKG_Y_SITE==Y_SITE.\n"+
745 "Expect trouble.\n"+
746 "I strongly advise you to rerun pkg_setup and set PKG_Y_SITE\n"+
747 "and PKG_Y_HOME to sane values.\n\n";
748 }
749 }
750
751 func pkg_upgrade(pkgnames,verbose=)
752 /* DOCUMENT pkg_upgrade,pkgnames,verbose=
753 Upgrade requested packages, or all upgradable packages if no arguments
754 Upgrade to highest available version.
755
756 pkg_upgrade,packages
757 pkgname can be a string scalar, vector, and contains wildcard to
758 install multiple packages in one call.
759
760 examples: pkg_upgrade,"y*" or pkg_upgrade,["soy","yao"] or pkg_upgrade,"*"
761
762 Without arguments, upgrade all upgradable packages.
763
764 bug: I don't correctly parse the version, so 0.5.03 is considered
765 lower than 0.5.2
766 SEE ALSO:
767 */
768 {
769 if (is_void(pkgnames)) {
770 pkgnames="*";
771 askall=1;
772 }
773
774 instpkg = get_avail_pkg();
775 allpkg = [];
776 for (np=1;np<=numberof(pkgnames);np++) {
777 w = where(strglob(pkgnames(np),instpkg));
778 if (numberof(w)==0) {
779 write,format="WARNING: No such package \"%s\"\n",pkgnames(np);
780 continue;
781 }
782 grow,allpkg,instpkg(w);
783 }
784 pkgnames = allpkg;
785 if (numberof(pkgnames)==0) return 0;
786
787
788 li = pkg_list();
789 upgradable = (strpart(li,1:1)=="u");
790 pname = strtrim(strpart(li,3:16));
791 uvers = strtrim(strpart(li,17:24));
792 ivers = strtrim(strpart(li,26:32));
793 ww=[];
794
795 for (i=1;i<=numberof(pkgnames);i++) {
796 w = where(pkgnames(i)==pname)(1);
797 if (ivers(w)=="-") continue; // not installed, ignore.
798 if (numberof(w)==0) {
799 write,format="WARNING: No such package \"%s\"\n",pkgnames(i);
800 continue;
801 }
802 if (upgradable(w)) {
803 write,format="Upgrade: %s from version %s to %s\n",
804 pname(w),ivers(w),uvers(w);
805 grow,ww,w;
806 } else {
807 if (!askall) \
808 write,format="Package %s is already at the latest version %s\n",
809 pname(w),ivers(w);
810 }
811 }
812
813 if (numberof(ww)==0) {
814 write,format="%s\n","No package to upgrade";
815 return 0;
816 }
817
818 if (PKG_ASK_CONFIRM) {
819 ans = strtolower(strtrim(kinput("OK","y")));
820 if (ans!="y") {
821 write,format="%s\n","Aborting";
822 return;
823 }
824 }
825
826 for (i=1;i<=numberof(ww);i++) {
827 pkg_install,pname(ww(i)),noconfirm=1,verbose=verbose,_version=uvers(ww(i));
828 }
829 }
830
831
832 func pkg_install(pkgnames,verbose=,check=,force=,noconfirm=,_recur=,_version=,_vrel=)
833 /* DOCUMENT pkg_install,pkgname,force=,check=,verbose=
834 Install package "pkgname" (string vector)
835 Grabs the tarball from a central server, untar it, copy the files
836 according to the directory structure specified in the untared
837 package, possibly run a preflight and postflight include files (for
838 special needs), and place the package info file and a list of
839 installed files in package/installed/.
840
841 pkgname can be a string scalar, vector, and contains wildcard to
842 install multiple packages in one call.
843 examples: pkg_install,"y*" or pkg_install,["soy","yao"] or pkg_install,"*"
844
845 Keywords:
846 force: force installation
847 check: run checks after install
848 verbose: 0 (silent), 1 (some messages), 2 (chatty), 3 (more chatty)
849
850 SEE ALSO: pkg_mngr, pkg_remove.
851 */
852 {
853 extern _pkg_recur_tree; // to avoid recursion
854 if (!(setup_done & user_setup_done)) pkg_setup,first=1;
855 if (!PKG_SYNC_DONE) pkg_sync;
856
857 if (!_recur) { // first call in possible recursion
858 // the following is to expand the possible wildcards
859 // in elements of the vector pkgnames
860 // e.g. a call can be pkg_install,["y*","soy"]
861 instpkg = get_avail_pkg();
862 allpkg = [];
863 for (np=1;np<=numberof(pkgnames);np++) {
864 w = where(strglob(pkgnames(np),instpkg));
865 if (numberof(w)==0) {
866 write,format="WARNING: No such package \"%s\"\n",pkgnames(np);
867 continue;
868 }
869 grow,allpkg,instpkg(w);
870 }
871
872 pkgnames = allpkg;
873 if (numberof(pkgnames)==0) return 0;
874
875 if ((PKG_ASK_CONFIRM)&(noconfirm!=1)) {
876 if (force) write,format="%s\n","Packages to (re)install :";
877 else write,format="%s\n","Packages to install :";
878 write,pkgnames;
879 // figure out dependencies (ignore version for now)
880 depjunk=[];
881 infodir = PKG_VAR_STATE+"info/";
882 instdir = PKG_VAR_STATE+"installed/";
883 for (i=1;i<=numberof(pkgnames);i++) {
884 ins=parse_info_file(infodir+pkgnames(i)+".info");
885 tmp=ins.depends_pkg;
886 tmp=tmp(where(tmp));
887 grow,depjunk,tmp;
888 }
889 if (depjunk!=[]) {
890 // already installed packages:
891 ins = lsdir(instdir);
892 instname = "yorick";
893 if (ins!=[]) {
894 w = strgrep("([a-zA-Z0-9\_\.\+\-]*)(.info$)",ins,sub=[1]);
895 tmp = strpart(ins,w);
896 grow,instname,tmp(where(tmp));
897 }
898 // sort dependencies in alphabetical order
899 tmp=depjunk(sort(depjunk));
900 depjunk=[];
901 // get rid of yorick and double entries
902 for (i=1;i<=numberof(tmp);i++) {
903 if (tmp(i)=="yorick") continue;
904 if ((anyof(strmatch(instname,tmp(i))))&(!force)) continue; // already installed
905 if (depjunk==[]) grow,depjunk,tmp(i);
906 else if (tmp(i)!=depjunk(0)) grow,depjunk,tmp(i);
907 }
908 if (depjunk!=[]) {
909 if (force) write,format="%s\n","Dependencies to be re-installed :";
910 else write,format="%s\n","Dependencies to be installed :";
911 write,depjunk;
912 }
913 }
914 if (kinput("OK? ","y")!="y") return 0;
915 }
916 }
917
918 for (np=1;np<=numberof(pkgnames);np++) {
919
920 pkgname = pkgnames(np);
921
922 if (!setup_done) pkg_setup,first=1;
923
924 if (!_recur) _pkg_recur_tree=[];
925 if (verbose==[]) verbose=PKG_VERBOSE;
926 if (_version==[]) _version="0.0";
927 if (_vrel==[]) _vrel=">=";
928 if (check==[]) check=PKG_RUN_CHECK;
929 if (pkgname==[]) error,"Must specify a string-type package name";
930
931 infodir = PKG_VAR_STATE+"info/";
932 instdir = PKG_VAR_STATE+"installed/";
933 tarbdir = PKG_VAR_STATE+"tarballs/";
934
935 if (anyof(_pkg_recur_tree) && anyof(strmatch(_pkg_recur_tree,pkgname)))
936 //we just installed it, to avoid recursing, we should exit.
937 continue;
938
939 // is this package already installed with a version > requested version ?
940 ins = lsdir(instdir);
941 if (!force && anyof(ins)) {
942 w = strgrep("([a-zA-Z0-9\_\.\+\-]*)(.info$)",ins,sub=[1]);
943 instname = strpart(ins,w);
944 w = where(instname==pkgname);
945 if (numberof(w)!=0) {
946 // this package has been installed. check version:
947 ins=parse_info_file(instdir+pkgname+".info");
948 if (vers_cmp(ins.vers,_vrel,_version)) {
949 if (verbose && !_recur) {
950 write,format="Package %s already installed (%s, needed %s)\n", \
951 pkgname,ins.vers,_version;
952 }
953 continue;
954 }
955 }
956 }
957 // TP: repeat the same in each PKG_OTHER_INSTALLED directory.
958 // Rationale: don't reinstall dependencies that are already installed at
959 // the system level.
960 installed_elsewhere=0;
961 for (in=1;in<=numberof(pkg_other_installed);in++) {
962 ins = lsdir(pkg_other_installed(in));
963 if (!force && anyof(ins)) {
964 w = strgrep("([a-zA-Z0-9\_\.\+\-]*)(.info$)",ins,sub=[1]);
965 instname = strpart(ins,w);
966 w = where(instname==pkgname);
967 if (numberof(w)!=0) {
968 // this package has been installed. check version:
969 ins=parse_info_file(pkg_other_installed(in)+pkgname+".info");
970 if (vers_cmp(ins.vers,_vrel,_version)) {
971 if (verbose && !_recur) {
972 write,format="Package %s already installed in %s (%s, needed %s)\n", \
973 pkgname,pkg_other_installed(in),ins.vers,_version;
974 }
975 installed_elsewhere=1;
976 }
977 // don't go further: assume this version is the one that will be
978 // used, we don't want to know whether the right one is installed
979 // somewhere where it will be ignored.
980 break;
981 }
982 }
983 }
984 if (installed_elsewhere) continue;
985 // end TP
986
987 // upgrade: here, check if last version of package already installed
988
989 pkg=parse_info_file(infodir+pkgname+".info");
990
991 // works out dependencies:
992 for (i=1;i<=numberof(where(pkg.depends_pkg));i++) {
993
994 // yorick version number:
995 if (pkg.depends_pkg(i)=="yorick") {
996 if (!vers_cmp(Y_VERSION,pkg.depends_rel(i),pkg.depends_vers)) \
997 error,"This package needs yorick version "+
998 pkg.depends_rel(i)+" "+pkg.depends_vers(i);
999 continue;
1000 }
1001
1002 // here work out the possible recursion. use _pkg_uptree
1003 // other dependencies:
1004 pkg_install,pkg.depends_pkg(i),verbose=verbose,force=force,
1005 _recur=1,_version=pkg.depends_vers(i),_vrel=pkg.depends_rel(i);
1006
1007 // add the name to tree to avoid recursion
1008 grow,_pkg_recur_tree,pkg.depends_pkg(i);
1009 }
1010
1011 pkgtgz = strtok(pkg.source,"/",10);
1012 pkgtgz = pkgtgz(where(pkgtgz))(0);
1013
1014 cd,tarbdir;
1015
1016 // fetch the tarball
1017 localtarballs = lsdir(".");
1018 if ((noneof(localtarballs))|| // no tarballs OR
1019 (noneof(strmatch(localtarballs,pkgtgz)))|| //no match in loc. tarballs
1020 (force) ) { // we were asked to force the install anyway
1021 // this package does not exist locally.
1022 if (verbose==1) {
1023 write,format="%-20s: %s",pkgname,"Fetching tarball..";
1024 } else if (verbose>=2) {
1025 write,format="%s\n","--------------------------";
1026 write,format="%s: %s\n",pkgname,"Fetching tarball";
1027 }
1028 pkg_fetch_url,pkg.source,tarbdir+pkgtgz,verbose=verbose;
1029 } else {
1030 if (verbose==1) {
1031 write,format="%-20s: %s",pkgname,"Using local tarball..";
1032 } else if (verbose>=2) {
1033 write,format="%s\n","--------------------------";
1034 write,format="%s: %s\n",pkgname,"Using local tarball";
1035 }
1036 // the tarball exist locally, we'll use it (unless force=1)
1037 }
1038
1039 // gunzip and untar:
1040 if (verbose==1) {
1041 write,format="%s","Inflating..";
1042 } else if (verbose>=2) {
1043 write,format="%s: %s\n",pkgname,"Inflating tarball";
1044 }
1045 pkg_sys,PKG_GUNTAR_CMD+" "+tarbdir+pkgtgz,verbose=verbose;
1046
1047 // run preflight.i
1048 if (open(pkgname+"/preflight.i","r",1)) {
1049 if (verbose) write,format="\n%s\n","Running preflight.i";
1050 include,pkgname+"/preflight.i",1;
1051 }
1052
1053 list1=list2=[];
1054
1055 // copy to Y_SITE:
1056 if (anyof(lsdir(pkgname+"/dist/y_site/"))) {
1057 recursive_rename,pkgname+"/dist/y_site",PKG_Y_SITE,
1058 list1,verbose=verbose,init=1;
1059 }
1060
1061 // copy to Y_HOME:
1062 if (anyof(lsdir(pkgname+"/dist/y_home/"))) {
1063 recursive_rename,pkgname+"/dist/y_home",PKG_Y_HOME,
1064 list2,verbose=verbose,init=1;
1065 }
1066
1067 list = _(list1,list2);
1068
1069 if (verbose>=2) {
1070 write,format="%s\n","Installed files:";
1071 write,format=" + %s\n",list;
1072 }
1073
1074 // copy info file:
1075 rename,pkgname+"/"+pkgname+".info",instdir+"/"+pkgname+".info";
1076 if (verbose>=3) write,format="Executing rename,%s,%s\n",
1077 pkgname+"/"+pkgname+".info",instdir+"/"+pkgname+".info";
1078
1079 f = open(instdir+"/"+pkgname+".flist","w");
1080 write,f,format="%s\n",list;
1081 close,f;
1082
1083 if (open(pkgname+"/postflight.i","r",1)) {
1084 if (verbose) write,format="%s\n","Running postflight.i";
1085 include,pkgname+"/postflight.i",1;
1086 }
1087
1088 //run check if requested and pkgname/check.i file present
1089 if (check && open(pkgname+"/check.i","r",1)) {
1090 if (verbose==1) {
1091 write,format="%s","Checking..";
1092 } else if (verbose>=2) {
1093 write,format="%s\n","Checking package";
1094 }
1095 cd,pkgname;
1096 if (anyof(pkgname==["imutil","curses","yorz","yutils","yao"])) {
1097 include,"check.i";
1098 } else {
1099 include,"check.i",1;
1100 }
1101 }
1102
1103 // clean up after ourselves
1104 recursive_rmdir,tarbdir+pkgname+"/dist",verbose=verbose;
1105
1106 // if we got there, it ought to be OK
1107 if (verbose==1) {
1108 write,format="%s","installed\n";
1109 } else if (verbose>=2) {
1110 write,format="%s installed sucessfully\n",pkgname;
1111 }
1112
1113 }
1114 return 0;
1115 }
1116
1117
1118 func pkg_reset(verbose=)
1119 /* DOCUMENT pkg_reset(verbose=)
1120 Brute force solution: if a bad tarball has been downloaded,
1121 pkg_mngr will try and try using it and fail. One can remove
1122 it by hand in Y_HOME/packages/tarballs/ or use this routine
1123 to remove them all.
1124 SEE ALSO:
1125 */
1126 {
1127 if (verbose==[]) verbose=PKG_VERBOSE;
1128
1129 rep=kinput("Delete all pkg_mngr tarballs and start from scratch","n");
1130 if (rep!="y") return
1131
1132 tarbdir = PKG_VAR_STATE+"tarballs/";
1133 recursive_rmdir,tarbdir,verbose=verbose;
1134 mkdir,tarbdir;
1135 }
1136
1137
1138 func pkg_remove(pkgnames,verbose=)
1139 /* DOCUMENT pkg_remove,pkgname,verbose=
1140 Remove package "pkgname" (string)
1141 Remove all files (libraries, include files, autoload)
1142 that were installed by the installer.
1143
1144 pkgname can be a string scalar, vector, and contains wildcard to
1145 remove multiple packages in one call.
1146 examples: pkg_remove,"y*" or pkg_remove,["soy","yao"] or pkg_remove,"*"
1147
1148 help,pkg_mngr for more details.
1149
1150 SEE ALSO: pkg_mngr, pkg_install
1151 */
1152 {
1153 if (!(setup_done & user_setup_done)) pkg_setup,first=1;
1154 if (verbose==[]) verbose=PKG_VERBOSE;
1155 if (!PKG_SYNC_DONE) pkg_sync;
1156 if (pkgnames==[]) error,"Must specify a string-type package name";
1157
1158 instpkg = get_inst_pkg();
1159 if (noneof(instpkg)) return;
1160
1161 allpkg = [];
1162 for (np=1;np<=numberof(pkgnames);np++) {
1163 w = where(strglob(pkgnames(np),instpkg));
1164 if (numberof(w)==0) {
1165 write,format="No package corresponding to %s\n",pkgnames(np);
1166 continue;
1167 }
1168 grow,allpkg,instpkg(w);
1169 }
1170
1171 pkgnames = allpkg;
1172 if (numberof(pkgnames)==0) return 0;
1173
1174 if (PKG_ASK_CONFIRM) {
1175 write,format="%s\n","Packages to remove :";
1176 write,pkgnames;
1177 if (kinput("OK? ","y")!="y") return 0;
1178 }
1179
1180 instdir = PKG_VAR_STATE+"installed/";
1181
1182 for (np=1;np<=numberof(pkgnames);np++) {
1183 pkgname = pkgnames(np);
1184
1185 if (verbose==1) {
1186 write,format="%-20s: Removing...",pkgname;
1187 } else if (verbose>=2) {
1188 write,format="%s\n","--------------------------";
1189 write,format="Removing package %s\n",pkgname;
1190 }
1191
1192 instpkg = get_inst_pkg();
1193 if (noneof(strmatch(instpkg,pkgname))) {
1194 if (verbose) write,format="\nPackage %s is not installed\n",pkgname;
1195 continue;
1196 }
1197
1198 files = rdfile(instdir+pkgname+".flist");
1199
1200 for (i=1;i<=numberof(files);i++) {
1201 if (verbose>=2) write,format=" - %s\n",files(i);
1202 remove,files(i);
1203 }
1204
1205 file = instdir+pkgname+".info";
1206 if (verbose>=2) write,format=" - %s\n",file;
1207 remove,file;
1208
1209 file = instdir+pkgname+".flist";
1210 if (verbose>=2) write,format=" - %s\n",file;
1211 remove,file;
1212
1213 // if we got there, we ought to be OK
1214 if (verbose==1) {
1215 write,format="%s\n","done";
1216 } else if (verbose>=2) {
1217 write,format="%s removed successfully\n",pkgname;
1218 }
1219 }
1220 return 0;
1221 }
1222
1223
1224 /********************************************\
1225 * UTILITARY FUNCTIONS *
1226 \********************************************/
1227
get_avail_pkg(void)1228 func get_avail_pkg(void)
1229 {
1230 infodir = PKG_VAR_STATE+"info/";
1231
1232 all = lsdir(infodir);
1233 if (anyof(all)) {
1234 w = strgrep("([a-zA-Z0-9\_\.\+\-]*)(.info$)",all,sub=[1]);
1235 infoname = strpart(all,w);
1236 infoname = infoname(where(infoname));
1237 }
1238 return infoname;
1239 }
1240
get_inst_pkg(instdir)1241 func get_inst_pkg(instdir)
1242 {
1243 if (!instdir) instdir = PKG_VAR_STATE+"installed/";
1244
1245 all = lsdir(instdir);
1246 if (anyof(all)) {
1247 w = strgrep("([a-zA-Z0-9\_\.\+\-]*)(.info$)",all,sub=[1]);
1248 instname = strpart(all,w);
1249 instname = instname(where(instname));
1250 }
1251 return instname;
1252 }
1253
1254 func recursive_rmdir(dir,verbose=)
1255 /* DOCUMENT recursive_rmdir,dir,verbose=
1256 Recursively delete all files and directories under dir, dir
1257 included.
1258 Equivalent to the linux/unix command
1259 rm -rf dir
1260 Note that recursive_rmdir,"." will fail removing "." (for
1261 some reason).
1262 SEE ALSO:
1263 */
1264 {
1265 if (strpart(dir,0:0)!="/") dir+="/";
1266 if (verbose==[]) verbose=PKG_VERBOSE;
1267
1268 if (allof(lsdir(dir)==0)) return 0;
1269
1270 orig_dir = dir;
1271
1272 do {
1273
1274 f = lsdir(dir,subdirs);
1275
1276 if ( (noneof(f)) && (noneof(subdirs)) ) {
1277 // no files, no subdirs, ok to remove dir:
1278 if (verbose>=3) write,format="Removing directory %s\n",dir;
1279 rmdir,dir;
1280 if (lsdir(dir)!=0) error,"Can't remove directory "+dir;
1281 // and exit (cul-de-sac):
1282 break;
1283 }
1284
1285 if (anyof(f)) {
1286 // some files in here, remove them
1287 for (i=1;i<=numberof(f);i++) {
1288 if (verbose>=3) write,format="Removing %s\n",dir+f(i);
1289 remove,dir+f(i);
1290 }
1291 }
1292
1293 // no more files. If no subdirs, remove dir and exit:
1294 if (noneof(subdirs)) {
1295 if (verbose>=3) write,format="Removing directory %s\n",dir;
1296 rmdir,dir;
1297 break;
1298 } else {
1299 // else go down one level
1300 dir += subdirs(1)+"/";
1301 }
1302 } while (1);
1303
1304 recursive_rmdir,orig_dir,verbose=verbose;
1305 }
1306
1307
1308
1309 func recursive_rename(dir1,dir2,&list,verbose=,init=)
1310 /* DOCUMENT recursive_rename,dir1,dir2,&list,verbose=,init=
1311 As it says. This routine will move the whole content of
1312 dir1 to dir2, recursively.
1313 Absolute equivalent to the linux/unix command
1314 cp -pr dir1 dir1
1315 Except that the files get moved, not copied.
1316 SEE ALSO:
1317 */
1318 {
1319 local sub1;
1320
1321 if (init) list=[];
1322 if (verbose==[]) verbose=PKG_VERBOSE;
1323
1324 if (verbose>=3) write,format="Recursive rename %s to %s\n",dir1,dir2;
1325
1326 // make sure dir ends by slash
1327 if (strpart(dir1,0:0)!="/") dir1+="/";
1328 if (strpart(dir2,0:0)!="/") dir2+="/";
1329
1330 // get files and subdirs
1331 f1 = lsdir(dir1,sub1);
1332
1333 // make sure destination dir exists:
1334 f2 = lsdir(dir2);
1335
1336 // if not, create:
1337 if (allof(f2)==0) {
1338 mkdirp,dir2;
1339 if (lsdir(dir2)==0) error,"Creating "+dir2+" failed (check permission)";
1340 if (verbose>=2) write,format="%s\n","Created "+dir2;
1341 }
1342
1343 // finally, move the files:
1344 for (i=1;i<=numberof(f1);i++) {
1345 if (verbose>=3)
1346 write,format="Executing rename,%s,%s\n",dir1+f1(i),dir2+f1(i);
1347 rename,dir1+f1(i),dir2+f1(i);
1348 grow,list,dir2+f1(i);
1349 }
1350
1351 for (i=1;i<=numberof(sub1);i++) {
1352 recursive_rename,dir1+sub1(i),dir2+sub1(i),list,verbose=verbose;
1353 }
1354 }
1355
1356
1357
vers_cmp(v1,op,v2)1358 func vers_cmp(v1,op,v2)
1359 /* DOCUMENT vers_cmp(v1,op,v2)
1360 Compare (software) version, using operator op.
1361 Returns 0 (fail) or 1 (pass).
1362 Operators allowed are >=, >, ==, < and <=
1363 Will possibly fails for subversion # > 99 (will exit in error if
1364 case arises)
1365 SEE ALSO:
1366 */
1367 {
1368
1369 ndown=5;
1370
1371 tok1 = strtok(v1,".",ndown);
1372 tok2 = strtok(v2,".",ndown);
1373
1374 n1=n2=0l;
1375
1376 for (i=1;i<=ndown;i++) {
1377 n=0;
1378 sread,tok1(i),n;
1379 if (n>99) error,
1380 swrite(format="n>99 (%d): possible failure mode!",n);
1381 n1+=n*100^(ndown-i); //limits version numbering to *.99.*
1382 }
1383
1384 for (i=1;i<=ndown;i++) {
1385 n=0;
1386 sread,tok2(i),n;
1387 if (n>99) error,
1388 swrite(format="n>99 (%d): possible failure mode!",n);
1389 n2+=n*100^(ndown-i); //limits version numbering to *.99.*
1390 }
1391
1392 if (op==">=") if (n1>=n2) return 1;
1393 if (op==">") if (n1>n2) return 1;
1394 if (op=="<=") if (n1<=n2) return 1;
1395 if (op=="<") if (n1<n2) return 1;
1396 if (op=="==") if (n1==n2) return 1;
1397
1398 return 0;
1399 }
1400
1401
1402
parse_info_file(file)1403 func parse_info_file(file)
1404 /* DOCUMENT parse_info_file(file)
1405 Parse a .info file (file describing a package in the
1406 yorick package manager, similar to fink/debian info files)
1407 Return a structure with elements = parsed keywords values
1408 SEE ALSO:
1409 */
1410 {
1411 if (!open(file,"r",1)) error,"Can not find (package exist?)"+file;
1412
1413 text = rdfile(file);
1414
1415 pkg = pkginfo_str();
1416
1417 pkg.version -=99;
1418
1419 pkg.name = pkg_get_keyword_value(text,"Package");
1420 pkg.kind = pkg_get_keyword_value(text,"Kind");
1421 pkg.vers = pkg_get_keyword_value(text,"Version");
1422 pkg.revision = pkg_get_keyword_value(text,"Revision");
1423 pkg.desc = pkg_get_keyword_value(text,"Description");
1424 pkg.license = pkg_get_keyword_value(text,"License");
1425 pkg.maintainer = pkg_get_keyword_value(text,"Maintainer");
1426 pkg.os = pkg_get_keyword_value(text,"OS");
1427 pkg.depends = pkg_get_keyword_value(text,"Depends");
1428 pkg.source = pkg_get_keyword_value(text,"Source");
1429 pkg.md5 = pkg_get_keyword_value(text,"Source-MD5");
1430 pkg.dir = pkg_get_keyword_value(text,"Source-Directory");
1431 pkg.docs = pkg_get_keyword_value(text,"DocFiles");
1432 pkg.homepage = pkg_get_keyword_value(text,"Homepage");
1433 pkg.desc_details = pkg_get_keyword_value(text,"DescDetail");
1434
1435
1436 // deals with source
1437 pkg.source = streplace(pkg.source,strgrep("%v",pkg.source),pkg.vers);
1438 pkg.source = streplace(pkg.source,strgrep("%v",pkg.source),pkg.vers);
1439 pkg.source = streplace(pkg.source,strgrep("%o",pkg.source),pkg.os);
1440 pkg.source = streplace(pkg.source,strgrep("%o",pkg.source),pkg.os);
1441
1442 // parse version
1443 i=1; tmp = pkg.vers;
1444 do {
1445 tmp = strtok(tmp,".");
1446 sread,tmp(1),format="%d",pkg.version(i);
1447 tmp = tmp(2);
1448 i++;
1449 } while (tmp);
1450
1451 // parse depends
1452 i=1; tmp = pkg.depends;
1453 do {
1454 tmp = strtok(tmp,",");
1455 if (strmatch(tmp(1),"(")) {
1456 tmp2 = strtok(tmp(1),"(");
1457 pkg.depends_pkg(i) = strtrim(tmp2(1));
1458 pkg.depends_vers(i) = strpart(tmp2(2),strgrep("[0-9.]+",tmp2(2)));
1459 pkg.depends_rel(i) = strpart(tmp2(2),strgrep("[><=]+",tmp2(2)));
1460 } else {
1461 pkg.depends_pkg(i) = strtrim(tmp(1));
1462 }
1463 tmp = tmp(2);
1464 i++;
1465 } while (tmp);
1466
1467
1468 return pkg;
1469 }
1470
1471
1472
pkg_get_keyword_value(text,keyw)1473 func pkg_get_keyword_value(text,keyw)
1474 /* DOCUMENT pkg_get_keyword_value(text,keyw)
1475 return parsed value of keyword read from a .info
1476 file, in the form:
1477 keyword: value
1478 or
1479 keyword: >>
1480 multi
1481 line value
1482 >>
1483 SEE ALSO:
1484 */
1485 {
1486 w = where(strgrep("^"+keyw,text)(2,)!=-1);
1487 if (numberof(w)==0) {
1488 write,format="%s\n","WARNING: Keyword "+keyw+" does not exist";
1489 return "";
1490 }
1491
1492 res = strtrim(strtok(text(w(1)),":")(2));
1493
1494 if (res == "<<") { // multi line keyword
1495 res = "";
1496 w = w(1)+1;
1497 do {
1498 res += text(w)+"\n";
1499 w++;
1500 } while (strtrim(text(w))!="<<");
1501 res = strpart(res,1:-1); //suppress last \n
1502 }
1503
1504 return res;
1505 }
1506
1507
1508
1509 func pkg_sys(cmd,verbose=)
1510 /* DOCUMENT pkg_sys(cmd,verbose=)
1511 simple interface to the system command.
1512 Allows echoing of commands output to system
1513 SEE ALSO:
1514 */
1515 {
1516 if (verbose==[]) verbose=PKG_VERBOSE;
1517
1518 if (verbose>=3) write,format="Spawning %s\n",cmd;
1519
1520 system,cmd;
1521
1522 return 0;
1523 }
1524
1525 func pkg_probe_oses(void,verbose=)
1526 {
1527 write,format="%s\n","\nRetrieving OS-ARCH alternatives...please wait";
1528 tmpdir=Y_USER+"packages/tmp/"; // PKG_TMP_DIR not yet defined by user
1529 mkdirp,tmpdir;
1530 pkg_fetch_url,PKG_SERVER,tmpdir+".oses",verbose=verbose;
1531 ctn = rdfile(tmpdir+".oses");
1532 match = strmatch(ctn,"[DIR]");
1533 if (anyof(match)) {
1534 ctn = ctn(where(match));
1535 oses = strpart(ctn,strgrep("([HREF,href]=\\\")([a-zA-Z0-9\_\.\+\-]+)",ctn,sub=2));
1536 oses = oses(where(oses));
1537 oses = oses(where(oses!="tarballs"));
1538 oses = oses(where(oses!="src"));
1539 oses = oses(where(oses!="linux")); // deprecated. now linux-arch
1540 oses = oses(where(oses!="macosx")); // depracated, now darinw-arch
1541 } else error,swrite(format="Did not find any OS directory at %s\n",PKG_SERVER);
1542 return oses;
1543 }
1544
1545 func pkg_fetch_url(url,dest,verbose=)
1546 /* DOCUMENT pkg_fetch_url(url,dest,verbose=)
1547 wrap the url fetch command (e.g. curl) with error checking
1548 SEE ALSO:
1549 */
1550 {
1551 // fetch the page/file:
1552 pkg_sys,PKG_FETCH_CMD+" "+url+" > "+dest,verbose=verbose;
1553
1554 if (!(f=open(dest,"r",1))) {
1555 write,"";
1556 error,"No file fetched (connection down?)";
1557 }
1558
1559 ctn = rdline(f,30);
1560
1561 if (numberof(where(ctn))==0) {
1562 write,"";
1563 error,"Zero length file (connection down?)";
1564 }
1565
1566 if (anyof(strmatch(ctn,"<title>Error 404: Page Not Found"))) {
1567 write,"";
1568 error,"Page not found (error 404)";
1569 }
1570
1571 return 0;
1572 }
1573
1574
kinput(prompt,default)1575 func kinput(prompt,default)
1576 {
1577 if (typeof(default)=="string") {
1578 s = swrite(format=prompt+" [\"%s\"]: ",default);
1579 sres = rdline(,1,prompt=s)(1);
1580 if (sres == "") return default;
1581 res = sres;
1582 } else if ((typeof(default)=="long")||(typeof(default)=="int")) {
1583 s = swrite(format=prompt+" [%d]: ",long(default));
1584 sres = rdline(,1,prompt=s)(1);
1585 if (sres == "") return default;
1586 res = 1l;
1587 sread,sres,res;
1588 } else if ((typeof(default)=="double")||(typeof(default)=="float")) {
1589 s = swrite(format=prompt+" [%f]: ",double(default));
1590 sres = rdline(,1,prompt=s)(1);
1591 if (sres == "") return default;
1592 res = 1.0;
1593 sread,sres,res;
1594 } else error,"type not supported";
1595
1596 return res;
1597 }
1598
1599
1600
write_pkg_setup_start(case)1601 func write_pkg_setup_start(case)
1602 {
1603 rep=kinput("Do you want me to write this in \""+Y_USER+"i-start/00pkg_mngr.i\" [y/n]?","y");
1604 if (rep=="y") {
1605 mkdirp,Y_USER+"i-start";
1606 f = open(Y_USER+"i-start/00pkg_mngr.i","w");
1607 // write,f,format="%s\n",
1608 // "autoload,\"pkg_mngr.i\",pkg_setup,pkg_list,pkg_sync,pkg_remove,pkg_install;";
1609 if (case==1) write,f,format="%s\n","require,\"pathfun.i\";";
1610 write,f,format="PKG_SETUP=\"%s\";\n",PKG_SETUP;
1611 if (case==1) {
1612 if (PKG_Y_HOME==PKG_Y_SITE)
1613 write,f,format="add_y_home,\"%s\";\n",PKG_Y_HOME;
1614 else
1615 write,f,format="add_y_home,\"%s\",\"%s\";\n",PKG_Y_HOME,PKG_Y_SITE;
1616 }
1617 close,f;
1618 write,format="NOTE: --> Generated \"%s\"\n",Y_USER+"i-start/00pkg_mngr.i";
1619 }
1620 }
1621
1622