qi.in 43 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493
  1. #! /bin/sh -
  2. # Copyright (C) 2016-2020 Matias Fonzo <selk@dragora.org>
  3. #
  4. # This program is free software: you can redistribute it and/or modify
  5. # it under the terms of the GNU General Public License as published by
  6. # the Free Software Foundation, either version 3 of the License, or
  7. # (at your option) any later version.
  8. #
  9. # This program is distributed in the hope that it will be useful,
  10. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. # GNU General Public License for more details.
  13. #
  14. # You should have received a copy of the GNU General Public License
  15. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. # EXIT STATUS
  17. # 0 = Successful completion
  18. # 1 = Minor common errors (e.g: help usage, support not available)
  19. # 2 = Command execution error
  20. # 3 = Integrity check error for compressed files
  21. # 4 = File empty, not regular, or expected
  22. # 5 = Empty or not defined variable
  23. # 6 = Package already installed
  24. # 10 = Network manager error
  25. #### Functions
  26. usage()
  27. {
  28. printf "%s\n" \
  29. "Qi - A user-friendly package manager." \
  30. "" \
  31. "Usage: $PROGRAM [OPTION...] [FILE]..." \
  32. "" \
  33. "Operation mode:" \
  34. " -b Build packages using recipe names" \
  35. " -c Create .tlz package from directory" \
  36. " -d Delete packages" \
  37. " -i Install packages" \
  38. " -o Resolve build order through .order files" \
  39. " -u Update packages (implies -i, -d with -p)" \
  40. " -w Warn about files that will be linked" \
  41. " -x Extract a package for debugging purposes" \
  42. "" \
  43. "Common options:" \
  44. " -N Do not read the configuration file" \
  45. " -P <DIR> Package directory for installations." \
  46. " Only valid for -i, -d, or -u options" \
  47. " -f This option can force the build of a recipe," \
  48. " or force the update of a pre-existing package." \
  49. " Only valid for -b, -u options" \
  50. " -t <DIR> Target directory for symbolic links." \
  51. " Only valid for -i, -d, or -u options" \
  52. " -k Keep \`\${srcdir}' or \`\${destdir}' in build mode," \
  53. " keep package directory in delete mode." \
  54. " Only valid for -b, -d or -u options" \
  55. " -p Prune conflicts on package installations" \
  56. " -r <DIR> Use the fully qualified named directory as the" \
  57. " root directory for all qi operations. The target" \
  58. " directory and package directory will be relative to" \
  59. " the specified directory, including the log file for" \
  60. " graft" \
  61. " -v Be verbose (a 2nd -v gives more)" \
  62. "" \
  63. "Options for 'build' mode (-b):" \
  64. " -O <DIR> Where the packages produced are written" \
  65. " -W <DIR> Where archives, patches, and recipes are expected" \
  66. " -Z <DIR> Where (compressed) sources will be found" \
  67. " -a Architecture to use [detected]" \
  68. " -j Parallel jobs for the compiler" \
  69. " -1 Increment release number (\`\${release}' + 1)" \
  70. " -n Don't create a .tlz package" \
  71. " -S Selects the option to skip completed recipes" \
  72. "" \
  73. "Informative options:" \
  74. " -L Print default directory locations" \
  75. " -h Display this help and exit" \
  76. " -V Output version information" \
  77. "" \
  78. "Some influential environment variables:" \
  79. " TMPDIR Temporary directory for sources" \
  80. " QICFLAGS C compiler flags" \
  81. " QICXXFLAGS C++ compiler flags" \
  82. " QILDFLAGS Linker flags" \
  83. "" \
  84. "When FILE is -, read standard input." \
  85. ""
  86. }
  87. version()
  88. {
  89. printf "%s\n" \
  90. "$PROGRAM @VERSION@" \
  91. "Copyright (C) 2016-2020 Matias Andres Fonzo." \
  92. "License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>" \
  93. "This is free software: you are free to change and redistribute it." \
  94. "There is NO WARRANTY, to the extent permitted by law."
  95. }
  96. warn()
  97. {
  98. printf "%s\n" "$@" 1>&2
  99. }
  100. is_readable()
  101. {
  102. if test -e "$1"
  103. then
  104. if ! test -r "$1"
  105. then
  106. echo "${PROGRAM}: cannot read ${1}: Permission denied" 1>&2
  107. return 1
  108. fi
  109. else
  110. echo "${PROGRAM}: cannot access ${1}: No such file or directory" 1>&2
  111. return 1
  112. fi
  113. }
  114. # Portable alternative to the file operator -nt (among shells)
  115. is_newer()
  116. {
  117. if test -n "$(find $1 -prune -newer $2 -print)"
  118. then
  119. return 0
  120. fi
  121. return 1
  122. }
  123. # Determine whether $2 matches pattern $1
  124. fnmatch()
  125. {
  126. case $2 in
  127. $1)
  128. return 0
  129. ;;
  130. *)
  131. return 1
  132. ;;
  133. esac
  134. }
  135. chkstatus_or_exit()
  136. {
  137. status=$?
  138. if test $status -ne 0
  139. then
  140. echo "^ Return status = $status" 1>&2
  141. exit ${1-2}; # If not given, defaults to 2
  142. fi
  143. unset status
  144. }
  145. readconfig()
  146. {
  147. if test $RC = RC
  148. then
  149. is_readable "$HOME/.qirc" 2> /dev/null && RCFILE="$HOME/.qirc";
  150. echo "Processing \`${RCFILE}' ..."
  151. test -f "$RCFILE" || {
  152. warn "${RCFILE} is not a regular file."
  153. return 1
  154. }
  155. # Parse config file
  156. while IFS='=' read -r variable value
  157. do
  158. case $variable in
  159. \#* | "") # Ignore commented or blank lines
  160. continue
  161. ;;
  162. esac
  163. # Set variable name avoiding code execution
  164. eval "$variable=\${value}"
  165. done < "$RCFILE"
  166. fi
  167. }
  168. #### Mode functions
  169. build_mode()
  170. {
  171. recipe=$1
  172. # A recipe is any valid regular file. Qi sets priorities for reading a
  173. # recipe, the order in which qi looks for a recipe is:
  174. #
  175. # 1. Current working directory.
  176. #
  177. # 2. If the specified path name does not contain "recipe" as the last
  178. # component. Qi will complete it by adding "recipe" to the path
  179. # name.
  180. #
  181. # 3. If the recipe is not in the current working directory, it will be
  182. # searched under '${worktree}/recipes'. The last component will be
  183. # completed adding "recipe" to the specified path name.
  184. if test ! -f "$recipe"
  185. then
  186. if test -f "${recipe}/recipe"
  187. then
  188. recipe="${recipe}/recipe"
  189. elif test -f "${worktree}/recipes/${recipe}/recipe"
  190. then
  191. recipe="${worktree}/recipes/${recipe}/recipe"
  192. fi
  193. fi
  194. test -f "$recipe" || {
  195. warn "\`${recipe}' is not a regular file."
  196. return 4
  197. }
  198. # Complain if the file name is not "recipe"
  199. if test "${recipe##*/}" != recipe
  200. then
  201. warn "\`${recipe}' is not a valid recipe name."
  202. return 4
  203. fi
  204. # Start preparations to import the recipe
  205. # Get working directory and base name of the recipe
  206. CWD=$(dirname -- "$recipe")
  207. recipe=$(basename -- "$recipe")
  208. # Check readability for load the recipe on success
  209. is_readable "${CWD}/$recipe" || exit 4
  210. # Find target architecture if 'arch' is not set
  211. if test -z "$arch"
  212. then
  213. arch="$( ${CC:-cc} -dumpmachine 2> /dev/null )" || arch=unknown
  214. arch="${arch%%-*}" # Get the rid of target triplet.
  215. fi
  216. # Re-create external directories
  217. mkdir -p -- "${worktree}/archive" \
  218. "${worktree}/patches" \
  219. "${worktree}/recipes" \
  220. "$tardir"
  221. # Variables treatment for the current and next recipe.
  222. #
  223. # Unset special variables that can only be predefined on
  224. # the recipe and not coming from ${sysconfdir}/qirc
  225. unset srcdir destdir pkgname pkgversion program version release \
  226. fetch description homepage license replace full_pkgname \
  227. CFLAGS CXXFLAGS LDFLAGS
  228. # The following variables must be saved and restored
  229. save_arch="${save_arch:=$arch}"
  230. save_jobs="${save_jobs:=$jobs}"
  231. save_outdir="${save_outdir:=$outdir}"
  232. save_opt_nopkg="${save_opt_nopkg:=$opt_nopkg}"
  233. # Reset variable values in case of return
  234. arch=$save_arch
  235. jobs=$save_jobs
  236. outdir=$save_outdir
  237. opt_nopkg=$save_opt_nopkg
  238. # The following variables cannot be redefined on the recipe
  239. readonly worktree netget rsync
  240. # Import the recipe
  241. echo "{@} Building from ${CWD}/$recipe ..."
  242. . "${CWD}/$recipe"
  243. chkstatus_or_exit
  244. # Check for required variables
  245. if test -z "$program"
  246. then
  247. warn "${recipe}: The variable 'program' is not defined."
  248. exit 5
  249. fi
  250. if test -z "$version"
  251. then
  252. warn "${recipe}: The variable 'version' is not defined."
  253. exit 5
  254. fi
  255. if test -z "$release"
  256. then
  257. warn "${recipe}: The variable 'release' is not defined."
  258. exit 5
  259. fi
  260. # Pre-settings before to start building
  261. # Increment the release number if the option was given
  262. if test "$opt_incr_release" = opt_incr_release
  263. then
  264. release=$(( $release + 1 ))
  265. fi
  266. # Allow the dot as definition for 'tardir'
  267. if test "$tardir" = .
  268. then
  269. tardir="$CWD"
  270. fi
  271. # Set default values for the following special variables
  272. pkgname="${pkgname:=$program}"
  273. pkgversion="${pkgversion:=$version}"
  274. srcdir="${srcdir:=${program}-$version}"
  275. destdir="${destdir:=${TMPDIR}/package-$pkgname}"
  276. # Complete package name adding 'pkgversion-arch+release'
  277. full_pkgname="${full_pkgname:=$pkgname-${pkgversion}-${arch}+${release}}"
  278. # If a package is going to be created, the existence of a
  279. # previous build will be detected and reported. Under normal
  280. # conditions the recipe is built as long as it is newer than
  281. # the produced package, if not, we warn to the user about it.
  282. # Rebuilding the package is possible (through the force ;-)
  283. if test "$opt_nopkg" != opt_nopkg && \
  284. { test "$opt_force" != opt_force && \
  285. test -e "${outdir}/${full_pkgname}.tlz" ; }
  286. then
  287. if is_newer "${CWD}/$recipe" "${outdir}/${full_pkgname}.tlz"
  288. then
  289. warn \
  290. "" \
  291. "The recipe is more RECENT than the detected package:" \
  292. "" \
  293. "$( stat -c "%y %n" "${CWD}/$recipe" )" \
  294. "$( stat -c "%y %n" "${outdir}/${full_pkgname}.tlz" )" \
  295. "" \
  296. " This recipe will be processed ..." \
  297. ""
  298. elif test -e "${CWD}/post-install" && \
  299. is_newer "${CWD}/post-install" "${CWD}/$recipe"
  300. then
  301. warn \
  302. "" \
  303. "The post-install script is more RECENT than the recipe:" \
  304. "" \
  305. "$( stat -c "%y %n" "${CWD}/post-install" )" \
  306. "$( stat -c "%y %n" "${CWD}/$recipe" )" \
  307. "" \
  308. " The recipe will be re-processed ..." \
  309. ""
  310. touch "${CWD}/$recipe"
  311. elif test "$opt_skipqsts" = opt_skipqsts
  312. then
  313. warn "Recipe for '${full_pkgname}.tlz': Ignored." ""
  314. return 0
  315. else
  316. warn \
  317. "" \
  318. "This recipe ALREADY produced a package:" \
  319. "$( stat -c "%y %n" "${outdir}/${full_pkgname}.tlz" )" \
  320. "" \
  321. "The recipe is still OLDER than the produced package:" \
  322. "$( stat -c "%y %n" "${CWD}/$recipe" )" \
  323. "" \
  324. " Probably nothing has changed." \
  325. ""
  326. # In non-interactive mode, the user is asked about
  327. # rebuilding the package. In interactive mode,
  328. # the user need to pass the option explicitly
  329. if test ! -t 0
  330. then
  331. printf "%s\n" \
  332. "Do you want to rebuild this package?" \
  333. "1) Yes, built it" \
  334. "2) No, skip it [default]" \
  335. "3) Resume, skipping completed recipes" \
  336. "Enter an option number:" > /dev/tty
  337. IFS= read -r ANSWER < /dev/tty || exit 2;
  338. case $ANSWER in
  339. 1*)
  340. echo "$ANSWER" > /dev/tty
  341. unset ANSWER
  342. ;;
  343. 3*)
  344. unset ANSWER
  345. echo "Building UNPROCESSED/MODIFIED recipes ..." > /dev/tty
  346. echo ""
  347. opt_skipqsts=opt_skipqsts
  348. readonly opt_skipqsts
  349. return 0
  350. ;;
  351. *)
  352. unset ANSWER
  353. echo "Recipe for '${full_pkgname}.tlz': Cancelled." > /dev/tty
  354. echo ""
  355. return 0
  356. ;;
  357. esac
  358. else
  359. warn "Use the -f option to reprocess ${CWD}/$recipe." ""
  360. return 6;
  361. fi
  362. fi
  363. fi
  364. # Fetch remote sources
  365. echo "Fetching remote sources if needed ..."
  366. if test -n "$fetch"
  367. then
  368. for origin in $fetch
  369. do
  370. _source="${origin##*/}"
  371. echo "Looking for \"$_source\" ..."
  372. echo "Verifying checksum file \"${_source}.sha256\" from '${tardir}'"
  373. if test -e "${tardir}/${_source}.sha256"
  374. then
  375. ( cd -- "$tardir" && sha256sum - ) < "${tardir}/${_source}.sha256"
  376. chkstatus_or_exit
  377. continue;
  378. else
  379. warn "${_source}.sha256: Checksum file does not exist, yet."
  380. fi
  381. # Download source or resume if allowed
  382. if test ! -e "${tardir}/$_source"
  383. then
  384. warn " Can't find $_source in ${tardir};" \
  385. "attempting to get it from ${origin%/*} ..."
  386. fi
  387. case $origin in
  388. rsync://*)
  389. (
  390. cd -- "$tardir" && $rsync $origin || exit $?
  391. sha256sum $_source > ${_source}.sha256
  392. ); chkstatus_or_exit 10
  393. ;;
  394. *://*)
  395. (
  396. cd -- "$tardir" && $netget $origin || exit $?
  397. sha256sum $_source > ${_source}.sha256
  398. ); chkstatus_or_exit 10
  399. ;;
  400. *)
  401. warn "${PROGRAM}: Unrecognized protocol for ${origin}."
  402. exit 4
  403. esac
  404. done
  405. unset origin _source
  406. else
  407. warn "The variable 'fetch' is empty."
  408. fi
  409. # Prepare special directories for build the source,
  410. # the destination and the output of the package
  411. echo "Preparing directories ..."
  412. if test -d "${TMPDIR}/$srcdir" && test -z "$keep_srcdir"
  413. then
  414. rm -rf -- "${TMPDIR}/$srcdir" || chkstatus_or_exit
  415. echo "removed directory: '${TMPDIR}/$srcdir'"
  416. fi
  417. if test -d "$destdir"
  418. then
  419. rm -rf -- "$destdir" || chkstatus_or_exit
  420. echo "removed directory: '$destdir'"
  421. fi
  422. mkdir -p -- "$destdir" || chkstatus_or_exit
  423. echo "mkdir: created directory '$destdir'"
  424. if test ! -d "$outdir"
  425. then
  426. mkdir -p -- "$outdir" || chkstatus_or_exit
  427. echo "mkdir: created directory '$outdir'"
  428. fi
  429. echo "Entering to 'TMPDIR': $TMPDIR ..."
  430. cd -- "$TMPDIR" || chkstatus_or_exit
  431. # Set trap before to run the build() function in order
  432. # to catch the return status, exit code 2 if fails
  433. trap 'chkstatus_or_exit 2' EXIT HUP INT QUIT ABRT TERM
  434. # Determine if the debugging indicators of the shell should be
  435. # retained, assuming that it has been previously passed
  436. case $- in *x*)
  437. _xtrace_flag=_xtrace_flag_is_set ;;
  438. esac
  439. echo "Running build() ..."
  440. build
  441. unset build
  442. # Turn off possible shell flags coming from the recipe
  443. set +e
  444. if test "${_xtrace_flag:+$_xtrace_flag}" != _xtrace_flag_is_set
  445. then
  446. set +x
  447. fi
  448. # Reset given signals
  449. trap - EXIT HUP INT QUIT ABRT TERM
  450. # If 'destdir' is empty, the package won't be created
  451. if rmdir -- "$destdir" 2> /dev/null
  452. then
  453. warn "The package \"${full_pkgname}.tlz\" won't be created. 'destdir' is empty."
  454. opt_nopkg=opt_nopkg
  455. fi
  456. # Create (make) the package
  457. if test "$opt_nopkg" != opt_nopkg
  458. then
  459. # Edit the recipe when 'release' is incremented
  460. if test "$opt_incr_release" = opt_incr_release
  461. then
  462. echo ",s/^\(release\)=.*/\1=${release}/"$'\nw' | \
  463. ed "${CWD}/$recipe" || chkstatus_or_exit
  464. fi
  465. mkdir -p -- "${destdir}/var/lib/qi" || chkstatus_or_exit
  466. # Include a recipe copy into the package
  467. cp -p "${CWD}/$recipe" \
  468. "${destdir}/var/lib/qi/${full_pkgname}.recipe" && \
  469. chmod 644 "${destdir}/var/lib/qi/${full_pkgname}.recipe" || chkstatus_or_exit
  470. # Detect post-install script for inclusion
  471. if test -f "${CWD}/post-install"
  472. then
  473. echo "${CWD}/post-install: Detected."
  474. cp -p "${CWD}/post-install" \
  475. "${destdir}/var/lib/qi/${full_pkgname}.sh" && \
  476. chmod 644 "${destdir}/var/lib/qi/${full_pkgname}.sh" || chkstatus_or_exit
  477. fi
  478. # Detect declared package names for later replacement
  479. if test -n "$replace"
  480. then
  481. warn \
  482. "The following package names has been declared for replacement:" \
  483. " $replace"
  484. rm -f "${destdir}/var/lib/qi/${full_pkgname}.replace"
  485. for item in $replace
  486. do
  487. echo "$replace" >> "${destdir}/var/lib/qi/${full_pkgname}.replace"
  488. done
  489. unset item
  490. fi
  491. # Create meta file for package information
  492. echo "Creating meta file ${full_pkgname}.tlz.txt ..."
  493. do_meta > "${outdir}/${full_pkgname}.tlz.txt" || chkstatus_or_exit
  494. # Make a copy of it for the database
  495. cp -p "${outdir}/${full_pkgname}.tlz.txt" \
  496. "${destdir}/var/lib/qi/${full_pkgname}.txt" || chkstatus_or_exit
  497. # Produce the package
  498. cd -- "$destdir" && create_mode "${outdir}/${full_pkgname}.tlz"
  499. fi
  500. # Back to the current working directory
  501. cd -- "$CWD" || chkstatus_or_exit
  502. # Delete 'srcdir' or 'destdir' if -k was not given
  503. if test "$opt_keep" != opt_keep
  504. then
  505. echo "Deleting temporary directories ..."
  506. srcdir="${srcdir%%/*}" # Directory name without parents.
  507. if test -r "${TMPDIR}/$srcdir"
  508. then
  509. if test -z "$keep_srcdir"
  510. then
  511. (
  512. cd -- "$TMPDIR" && rm -rf -- "$srcdir" && \
  513. echo "removed directory: '${TMPDIR}/${srcdir}'"
  514. ); chkstatus_or_exit
  515. else
  516. warn "The variable 'keep_srcdir' has been set;" \
  517. "'${TMPDIR}/${srcdir}' will not be deleted."
  518. fi
  519. fi
  520. if test -r "$destdir"
  521. then
  522. rm -rf -- "$destdir" || chkstatus_or_exit
  523. echo "removed directory: '$destdir'"
  524. fi
  525. fi
  526. # Install or update the package if -i or -u was passed
  527. if test "$opt_nopkg" != opt_nopkg
  528. then
  529. if test "$opt_install" = opt_install
  530. then
  531. install_mode "${outdir}/${full_pkgname}.tlz"
  532. elif test "$opt_update" = opt_update
  533. then
  534. upgrade_mode "${outdir}/${full_pkgname}.tlz"
  535. fi
  536. fi
  537. echo ""
  538. echo "All done for ${CWD}/${recipe}."
  539. echo ""
  540. }
  541. create_mode()
  542. {
  543. directory=$(dirname -- "$1")
  544. # Perform sanity checks
  545. if ! fnmatch '/?*' "$directory"
  546. then
  547. warn "${PROGRAM}: Output directory \`${directory}' is not fully qualified"
  548. exit 4
  549. fi
  550. is_readable "$directory" || exit 4
  551. name=$(basename -- "$1")
  552. if test "$name" = "${name%.tlz}"
  553. then
  554. warn "Package format '$name' not supported." \
  555. "It should be \"name-version-architecture+release.tlz\""
  556. exit 4
  557. fi
  558. echo "{#} Creating package $name ..."
  559. ( umask 022 ; tarlz --solid -9 -cvf - * ) > "${directory}/$name"
  560. chkstatus_or_exit 3
  561. ( cd -- "$directory" && sha256sum $name > ${name}.sha256 )
  562. chkstatus_or_exit 4
  563. echo "Package created on \`${directory}/${name}'."
  564. echo ""
  565. # Remove used variables
  566. unset directory name
  567. }
  568. delete_mode()
  569. {
  570. expunge="${packagedir}/$(basename -- $1 .tlz)"
  571. echo "{<} Deleting package $rootdir${expunge} ..."
  572. # Complain if the package directory does not exist
  573. test -d "$rootdir${expunge}" || {
  574. warn "Package directory '$rootdir${expunge}' does not exist."
  575. return 4
  576. }
  577. # Complain if the package directory cannot be well-read
  578. is_readable "$rootdir${expunge}" || exit 4
  579. # Remove package from Graft control
  580. # Scan for possible conflicts, stop if arise
  581. if test "$opt_prune" != opt_prune
  582. then
  583. echo "Checking for possible conflicts ..."
  584. if graft -d -n $graft_r -t "$targetdir" "$expunge" 2>&1 | \
  585. grep ^CONFLICT
  586. then
  587. warn "" \
  588. " A conflict occurred during uninstallation;" \
  589. "Unless the -p option is given, this package will be PRESERVED."
  590. return 6;
  591. fi
  592. fi
  593. # Ignore some signals up to completing the deinstallation
  594. trap "" HUP INT QUIT ABRT TERM
  595. # Remove objects (files, links or directories) from the target
  596. # directory that are in conflict with the package directory
  597. echo "Pruning any conflict ..."
  598. graft -p -D -u $graft_r -t "$targetdir" "$expunge"
  599. chkstatus_or_exit 2
  600. echo "Disabling links ..."
  601. graft -d -D -u $graft_v $graft_r -t "$targetdir" "$expunge"
  602. chkstatus_or_exit 2
  603. # Delete package directory
  604. if test "$opt_keep" != opt_keep
  605. then
  606. echo "Deleting package directory, if it exists as such ..."
  607. if test -d "${rootdir}$expunge"
  608. then
  609. rm -rf -- "${rootdir}$expunge" || chkstatus_or_exit
  610. echo "removed directory: '${rootdir}$expunge'"
  611. fi
  612. fi
  613. # Reset given signals
  614. trap - HUP INT QUIT ABRT TERM
  615. # Remove used variables
  616. unset expunge
  617. }
  618. install_mode()
  619. {
  620. # Complain if the package cannot be well-read
  621. is_readable "$1" || exit 4
  622. # Complain if the package does not end in .tlz
  623. if ! fnmatch '*.tlz' "$1"
  624. then
  625. warn "\`${1}' does not end in .tlz"
  626. return 4
  627. fi
  628. # Make preparations to install the package
  629. echo "{>} Installing package $1 ..."
  630. # Get the filename
  631. name=$(basename -- "$1" .tlz)
  632. echo "Checking tarball integrity ..."
  633. tarlz -tf "$1" > /dev/null
  634. chkstatus_or_exit 3
  635. # Create package directory using 'name'
  636. if ! test -d "$rootdir${packagedir}/$name"
  637. then
  638. mkdir -p -- "$rootdir${packagedir}/$name" || chkstatus_or_exit
  639. echo "mkdir: created directory '$rootdir${packagedir}/$name'"
  640. fi
  641. # Scan for possible conflicts, stop if arise
  642. if test "$opt_prune" != opt_prune
  643. then
  644. echo "Checking for possible conflicts ..."
  645. if graft -i -n $graft_r -t "$targetdir" "${packagedir}/$name" 2>&1 | \
  646. grep ^CONFLICT
  647. then
  648. warn "" \
  649. " A conflict occurred during installation;" \
  650. "Unless the -p option is given, this package won't be LINKED."
  651. return 6;
  652. fi
  653. fi
  654. # Ignore some signals up to completing the installation
  655. trap "" HUP INT QUIT ABRT TERM
  656. echo "Decompressing $1 ..."
  657. ( cd -- "$rootdir${packagedir}/$name" && tarlz -xf - ) < "$1"
  658. chkstatus_or_exit 3
  659. # Transite package to Graft control
  660. # Remove objects (files, links or directories) from the target
  661. # directory that are in conflict with the package directory
  662. echo "Pruning any conflict ..."
  663. graft -p -D -u $graft_r -t "$targetdir" "${packagedir}/$name"
  664. chkstatus_or_exit 2
  665. echo "Enabling symbolic links ..."
  666. graft -i -P $graft_v $graft_r -t "$targetdir" "${packagedir}/$name"
  667. chkstatus_or_exit 2
  668. # Avoid unnecessary runs coming from the upgrade_mode(),
  669. # this is when the incoming package is **pre-installed**
  670. if test "$_isUpgrade" != _isUpgrade.on
  671. then
  672. # Show package description
  673. if test -r "$rootdir${packagedir}/${name}/var/lib/qi/${name}.txt"
  674. then
  675. grep '^#' "$rootdir${packagedir}/${name}/var/lib/qi/${name}.txt"
  676. elif test -r "${1}.txt"
  677. then
  678. # From external meta file (current directory)
  679. grep '^#' "${1}.txt"
  680. else
  681. warn "Description file not found for '$name'."
  682. fi
  683. # Check and run the post-install script if exist
  684. if test -r "$rootdir${packagedir}/${name}/var/lib/qi/${name}.sh"
  685. then
  686. echo "Running post-install script for \`${name}' ..."
  687. (
  688. # Rely on 'targetdir' if 'rootdir' is empty
  689. cd -- "${rootdir:-$targetdir}"/ && \
  690. . "$rootdir${packagedir}/${name}/var/lib/qi/${name}.sh"
  691. )
  692. fi
  693. # Check if there are declared packages for replacement
  694. if test -r "$rootdir${packagedir}/${name}/var/lib/qi/${name}.replace"
  695. then
  696. while read -r line
  697. do
  698. for replace in "$rootdir${packagedir}/$(pkgbase $line)"-*
  699. do
  700. if ! test -e "$replace"
  701. then
  702. warn "${replace}: Declared package does not exist. (ignored)"
  703. continue;
  704. fi
  705. replace="${replace##*/}"
  706. # The search for the package to be replaced cannot
  707. # be the same to the incoming package, even to the
  708. # temporary location coming from the upgrade_mode()
  709. if test "$replace" = "$name" || test "$replace" = "${PRVLOC##*/}"
  710. then
  711. continue;
  712. fi
  713. warn "WARNING: Replacing package \`${replace}' ..."
  714. # Since the links belongs to the new package, only
  715. # those which are not in conflict can be deleted.
  716. # To complete, we will remove the package directory
  717. graft -d -D -u $graft_r \
  718. -t "$targetdir" "$replace" > /dev/null 2>&1
  719. rm -rf -- "$rootdir${packagedir}/$replace"
  720. done
  721. done < "$rootdir${packagedir}/${name}/var/lib/qi/${name}.replace"
  722. unset line
  723. fi
  724. fi
  725. # Reset given signals
  726. trap - HUP INT QUIT ABRT TERM
  727. # Remove used variables
  728. unset name
  729. }
  730. resolve_mode() {
  731. # Complain if the file cannot be well-read
  732. is_readable "$1" || exit 4
  733. # Complain if the file does not end in .order
  734. if ! fnmatch '*.order' "$1"
  735. then
  736. warn "\`${1}' does not end in .order"
  737. return 4
  738. fi
  739. # Get a clean list of the file while printing its contents in reverse
  740. # order. The last `awk 'in the pipeline eliminates the non-consecutive
  741. # lines, the duplicates. Blank lines, colons and parentheses are
  742. # simply ignored, comment lines beginning with '#' are allowed
  743. awk \
  744. '{ gsub( /:|^#(.*)$|\([^)]*)|^$/,"" ); for( i=NF; i > 0; i-- ) print $i }' \
  745. "$1" | awk '!s[$0]++'
  746. }
  747. upgrade_mode()
  748. {
  749. # Complain if the package does not end in .tlz
  750. if ! fnmatch '*.tlz' "$1"
  751. then
  752. warn "\`${1}' does not end in .tlz"
  753. return 4
  754. fi
  755. # Get the filename
  756. incoming=$(basename -- "$1" .tlz)
  757. echo "{^} Upgrading to $incoming ..."
  758. # Check packages in the blacklist for installation
  759. echo "Checking blacklist ..."
  760. for item in $blacklist
  761. do
  762. case $item in
  763. ${incoming}*)
  764. if test ! -e "$rootdir${packagedir}/$incoming"
  765. then
  766. warn \
  767. "" \
  768. " The package declared in the blacklist will be" \
  769. "installed instead of being updated ..." \
  770. ""
  771. opt_prune=opt_prune install_mode "$1"
  772. return 0
  773. fi
  774. if is_newer "$1" "$rootdir${packagedir}/$incoming"
  775. then
  776. warn \
  777. "" \
  778. "Incoming package is more RECENT than the installed package:" \
  779. "" \
  780. "$( stat -c "%y %n" "$1" )" \
  781. "$( stat -c "%y %n" "$rootdir${packagedir}/$incoming" )" \
  782. "" \
  783. " The package declared in the blacklist will be" \
  784. " installed as part of the upgrade process ..." \
  785. ""
  786. opt_prune=opt_prune install_mode "$1"
  787. touch "$rootdir${packagedir}/$incoming"
  788. return 0
  789. else
  790. warn \
  791. "" \
  792. "Blacklisted package is already up-to-date:" \
  793. "" \
  794. "$( stat -c "%y %n" "$rootdir${packagedir}/$incoming" )" \
  795. "$( stat -c "%y %n" "$1" )" \
  796. ""
  797. return 6
  798. fi
  799. ;;
  800. esac
  801. done
  802. unset item
  803. # Check package pre-existence
  804. if test "$opt_force" != opt_force && \
  805. test -e "$rootdir${packagedir}/$incoming"
  806. then
  807. warn \
  808. "" \
  809. " The package to be updated already exist;" \
  810. "Unless the -f option is given, this package won't be UPGRADED."
  811. return 6;
  812. fi
  813. # Prepare the package to install it in a temporary location
  814. # Set random directory using packagedir as prefix and 'incoming' as suffix
  815. PRVLOC=$(mktemp -dp "$rootdir${packagedir}" ${incoming}.XXXXXXXXXXXX) || exit 2
  816. # Pre-install the package in the custom 'packagedir'
  817. save_packagedir="$rootdir${packagedir}"
  818. packagedir="$PRVLOC"
  819. echo "Pre-installing package in a temporary location ..."
  820. opt_prune=opt_prune # Turn on prune operation.
  821. _isUpgrade=_isUpgrade.on install_mode "$1" > /dev/null
  822. _isUpgrade=_isUpgrade.off
  823. # Restore variable before looking for old packages
  824. packagedir=$save_packagedir
  825. unset save_packagedir
  826. echo "Looking for installations under the same name ..."
  827. for long_name in "$rootdir${packagedir}/$(pkgbase $incoming)"*
  828. do
  829. found="${long_name##*/}"
  830. # The search for the package to be deleted
  831. # cannot be the same to the temporary location
  832. test "$long_name" = "$PRVLOC" && continue;
  833. fnmatch "$(pkgbase $found)*" "$incoming" || continue;
  834. echo "${long_name}: Detected."
  835. # A package directory is preserved if -k is given
  836. delete_mode "$found" > /dev/null
  837. done
  838. unset long_name found
  839. # Re-install the package removing the temporary location
  840. install_mode "$1"
  841. opt_prune=opt_prune.off # Turn off prune operation.
  842. echo "Deleting temporary location ..."
  843. rm -rf -- "$PRVLOC" || chkstatus_or_exit
  844. echo "removed directory: '$PRVLOC'"
  845. echo ""
  846. echo "Successful upgrade to '${incoming}'."
  847. # Remove remaining variables
  848. unset incoming PRVLOC
  849. }
  850. warn_mode()
  851. {
  852. # Complain if the package cannot be well-read
  853. is_readable "$1" || exit 4
  854. # Complain if the package does not end in .tlz
  855. if ! fnmatch '*.tlz' "$1"
  856. then
  857. warn "\`${1}' does not end in .tlz"
  858. return 4
  859. fi
  860. # List content of files excluding directories
  861. while test -f "$1"
  862. do
  863. tarlz -tvvf "$1" | awk '!/^drwx/'
  864. chkstatus_or_exit 3
  865. shift;
  866. done
  867. return 0
  868. }
  869. extract_mode()
  870. {
  871. # Perform sanity checks before package extraction
  872. is_readable "$1" || exit 4
  873. test -f "$1" || {
  874. warn "\`${1}' is not a regular file."
  875. return 4
  876. }
  877. # Preparations to extract the package
  878. name=$(basename -- "$1" .tlz)
  879. # Set random directory using 'name' as prefix
  880. PRVDIR="${TMPDIR}/${name}.${RANDOM-0}$$"
  881. # Trap to remove 'PRVDIR' on disruptions
  882. trap "rm -rf -- $PRVDIR" HUP INT ABRT TERM
  883. # Create 'PRVDIR' removing access for all but user
  884. ( umask 077 ; mkdir -- $PRVDIR )
  885. mkdir -p -m 700 -- $PRVDIR
  886. echo "Extracting package $name ..."
  887. ( umask 000 ; cd -- $PRVDIR && tarlz -xvf - ) < "$1"
  888. if test $? -ne 0
  889. then
  890. # Try to remove (empty) 'PRVDIR' on failure
  891. rmdir -- $PRVDIR
  892. exit 3;
  893. fi
  894. echo "$name has been extracted on $PRVDIR"
  895. # Reset given signals
  896. trap - HUP INT ABRT TERM
  897. # Remove used variables
  898. unset name PRVDIR
  899. }
  900. #### Extra functions used in the modes
  901. pkgbase()
  902. {
  903. string=$(basename -- "$1" .tlz)
  904. # Match cases to print the package name.
  905. #
  906. # We will take into account the four segments removing
  907. # the last two to print the package (long) name
  908. case $string in
  909. *-*-*+*)
  910. echo "${string%-*-*}"
  911. ;;
  912. *)
  913. echo "$string"
  914. ;;
  915. esac
  916. unset string
  917. }
  918. unpack()
  919. {
  920. for file in "$@"
  921. do
  922. case $file in
  923. *.tar)
  924. tar -tf "$file" > /dev/null && \
  925. tar -xpf "$file"
  926. chkstatus_or_exit 3
  927. ;;
  928. *.tar.gz | *.tgz | *.tar.Z )
  929. gzip -cd "$file" | tar -tf - > /dev/null && \
  930. gzip -cd "$file" | tar -xpf -
  931. chkstatus_or_exit 3
  932. ;;
  933. *.tar.bz2 | *.tbz2 | *.tbz )
  934. bzip2 -cd "$file" | tar -tf - > /dev/null && \
  935. bzip2 -cd "$file" | tar -xpf -
  936. chkstatus_or_exit 3
  937. ;;
  938. *.tar.lz | *.tlz )
  939. lzip -cd "$file" | tar -tf - > /dev/null && \
  940. lzip -cd "$file" | tar -xpf -
  941. chkstatus_or_exit 3
  942. ;;
  943. *.tar.xz | *.txz )
  944. xz -cd "$file" | tar -tf - > /dev/null && \
  945. xz -cd "$file" | tar -xpf -
  946. chkstatus_or_exit 3
  947. ;;
  948. *.zip | *.ZIP )
  949. unzip -t "$file" > /dev/null && \
  950. unzip "$file" > /dev/null
  951. chkstatus_or_exit 3
  952. ;;
  953. *.gz)
  954. gzip -t "$file" && \
  955. gzip -cd "$file" > "$(basename -- $file .gz)"
  956. chkstatus_or_exit 3
  957. ;;
  958. *.Z)
  959. gzip -t "$file" && \
  960. gzip -cd "$file" > "$(basename -- $file .Z)"
  961. chkstatus_or_exit 3
  962. ;;
  963. *.bz2)
  964. bzip2 -t "$file" && \
  965. bzip2 -cd "$file" > "$(basename -- $file .bz2)"
  966. chkstatus_or_exit 3
  967. ;;
  968. *.lz)
  969. lzip -t "$file" && \
  970. lzip -cd "$file" > "$(basename -- $file .lz)"
  971. chkstatus_or_exit 3
  972. ;;
  973. *.xz)
  974. xz -t "$file" && \
  975. xz -cd "$file" > "$(basename -- $file .xz)"
  976. chkstatus_or_exit 3
  977. ;;
  978. *)
  979. warn "${PROGRAM}: cannot unpack ${file}: Unsupported extension"
  980. exit 1
  981. esac
  982. done
  983. unset file
  984. }
  985. do_meta()
  986. {
  987. # Extract information from the recipe to create the meta file.
  988. #
  989. # The package description is pre-formatted in 78 columns,
  990. # the '#' character and a space is added as prefix to conform
  991. # 80 columns in total
  992. cat << EOF
  993. $(echo "$description" | fold -w 78 | awk '$0="# " $0')
  994. QICFLAGS="$QICFLAGS"
  995. QICXXFLAGS="$QICXXFLAGS"
  996. QILDFLAGS="$QILDFLAGS"
  997. pkgname=$pkgname
  998. pkgversion=$pkgversion
  999. arch=$arch
  1000. release=$release
  1001. blurb="$(echo "$description" | sed -e '/^$/d;2q')"
  1002. homepage="$homepage"
  1003. license="$license"
  1004. fetch="$fetch"
  1005. replace="$replace"
  1006. EOF
  1007. }
  1008. #### Default values
  1009. PROGRAM="${0##*/}"
  1010. packagedir=@PACKAGEDIR@
  1011. targetdir=@TARGETDIR@
  1012. blacklist="perl5 graft tarlz plzip musl glibc"
  1013. RC=RC
  1014. RCFILE=@SYSCONFDIR@/qirc
  1015. opt_install=opt_install.off
  1016. opt_update=opt_update.off
  1017. opt_force=opt_force.off
  1018. opt_keep=opt_keep.off
  1019. opt_incr_release=opt_incr_release.off
  1020. opt_skipqsts=opt_skipqsts.off
  1021. opt_nopkg=opt_nopkg.off
  1022. opt_prune=opt_prune.off
  1023. rootdir=""
  1024. jobs=1
  1025. mode=""
  1026. verbose=0
  1027. graft_v=-v
  1028. graft_r=""
  1029. _isUpgrade=_isUpgrade.off
  1030. TMPDIR="${TMPDIR:=/usr/src/qi/build}"
  1031. QICFLAGS="${QICFLAGS:=-g0 -Os}"
  1032. QICXXFLAGS="${QICXXFLAGS:=$QICFLAGS}"
  1033. QILDFLAGS="${QILDFLAGS:=-s}"
  1034. worktree=/usr/src/qi
  1035. tardir=${worktree}/sources
  1036. outdir=/var/cache/qi/packages
  1037. netget="wget -c -w1 -t3 --no-check-certificate"
  1038. rsync="rsync -v -a -L -z -i --progress"
  1039. configure_args="--prefix=@PREFIX@ --libexecdir=@LIBEXECDIR@ --bindir=@BINDIR@ --sbindir=@SBINDIR@ --sysconfdir=@SYSCONFDIR@ --localstatedir=@LOCALSTATEDIR@"
  1040. infodir=@INFODIR@
  1041. mandir=@MANDIR@
  1042. docdir=@DOCDIR@
  1043. # Store (default) directory locations
  1044. QI_TARGETDIR=$targetdir
  1045. QI_PACKAGEDIR=$packagedir
  1046. QI_WORKTREE=$worktree
  1047. QI_TARDIR=$tardir
  1048. QI_OUTDIR=$outdir
  1049. #### Parse options
  1050. while getopts :bcdiouwxLNP:t:fkvO:W:Z:a:j:1nSpr:hV name
  1051. do
  1052. case $name in
  1053. b)
  1054. if test -z "$mode"
  1055. then
  1056. readconfig
  1057. mode=build_mode
  1058. fi
  1059. ;;
  1060. c)
  1061. mode=create_mode
  1062. ;;
  1063. d)
  1064. readconfig
  1065. mode=delete_mode
  1066. ;;
  1067. i)
  1068. if test -z "$mode"
  1069. then
  1070. readconfig
  1071. mode=install_mode
  1072. fi
  1073. if test "$mode" = build_mode
  1074. then
  1075. opt_install=opt_install
  1076. fi
  1077. ;;
  1078. o)
  1079. mode=resolve_mode
  1080. ;;
  1081. u)
  1082. if test -z "$mode"
  1083. then
  1084. readconfig
  1085. mode=upgrade_mode
  1086. fi
  1087. if test "$mode" = build_mode
  1088. then
  1089. opt_update=opt_update
  1090. fi
  1091. ;;
  1092. w)
  1093. mode=warn_mode
  1094. ;;
  1095. x)
  1096. mode=extract_mode
  1097. ;;
  1098. L)
  1099. printf "%s\n" \
  1100. "QI_TARGETDIR=$QI_TARGETDIR" \
  1101. "QI_PACKAGEDIR=$QI_PACKAGEDIR" \
  1102. "QI_WORKTREE=$QI_WORKTREE" \
  1103. "QI_TARDIR=$QI_TARDIR" \
  1104. "QI_OUTDIR=$QI_OUTDIR"
  1105. exit 0
  1106. ;;
  1107. N)
  1108. RC=RC.off
  1109. ;;
  1110. P)
  1111. packagedir="$OPTARG"
  1112. ;;
  1113. t)
  1114. targetdir="$OPTARG"
  1115. ;;
  1116. f)
  1117. opt_force=opt_force
  1118. ;;
  1119. k)
  1120. opt_keep=opt_keep
  1121. ;;
  1122. v)
  1123. verbose=$(( verbose + 1 ))
  1124. ;;
  1125. O)
  1126. outdir="$OPTARG"
  1127. ;;
  1128. W)
  1129. worktree="$OPTARG"
  1130. ;;
  1131. Z)
  1132. tardir="$OPTARG"
  1133. ;;
  1134. a)
  1135. arch="$OPTARG"
  1136. ;;
  1137. j)
  1138. jobs="$OPTARG"
  1139. ;;
  1140. 1)
  1141. opt_incr_release=opt_incr_release
  1142. ;;
  1143. n)
  1144. opt_nopkg=opt_nopkg
  1145. ;;
  1146. S)
  1147. opt_skipqsts=opt_skipqsts
  1148. ;;
  1149. p)
  1150. opt_prune=opt_prune
  1151. ;;
  1152. r)
  1153. rootdir="$OPTARG"
  1154. ;;
  1155. h)
  1156. usage
  1157. exit 0
  1158. ;;
  1159. V)
  1160. version
  1161. exit 0
  1162. ;;
  1163. :)
  1164. warn "Option '-${OPTARG}' requires an argument"
  1165. usage
  1166. exit 1
  1167. ;;
  1168. \?)
  1169. warn "Illegal option -- '-${OPTARG}'"
  1170. usage
  1171. exit 1
  1172. ;;
  1173. esac
  1174. done
  1175. shift $(( OPTIND - 1 ))
  1176. if test $# -eq 0
  1177. then
  1178. usage
  1179. exit 1
  1180. fi
  1181. # Program sanity check
  1182. for need in awk basename chmod cp dirname find fold graft grep \
  1183. mkdir mktemp rm rmdir sed sha256sum stat tarlz
  1184. do
  1185. if ! type $need 1> /dev/null 2> /dev/null
  1186. then
  1187. warn "${PROGRAM}: Prerequisite \`${need}' not found in PATH"
  1188. exit 2
  1189. fi
  1190. done
  1191. unset need
  1192. # Determine verbosity level/flag
  1193. if test "$verbose" -gt 1
  1194. then
  1195. graft_v=-V
  1196. fi
  1197. # Read standard input if FILE is -, or when FILE
  1198. # is not connected to a terminal.
  1199. if test "$1" = - || test ! -t 0
  1200. then
  1201. # Unset positional parameters setting $# to zero
  1202. set --
  1203. # Assign remaining arguments to the positional parameters
  1204. while read -r input
  1205. do
  1206. set -- "$@" "$input"
  1207. done
  1208. fi
  1209. # We need at least one operating mode
  1210. if test -z "$mode"
  1211. then
  1212. usage
  1213. exit 4
  1214. fi
  1215. umask 022; # Remove write permission for group and other.
  1216. # Validate 'packagedir' and 'targetdir' as canonical directories
  1217. # The single slash '/' does not qualify here
  1218. if ! fnmatch '/?*' "$packagedir"
  1219. then
  1220. warn "${PROGRAM}: Package directory \`${packagedir}' is not fully qualified"
  1221. exit 4
  1222. fi
  1223. if test ! -d "$packagedir"
  1224. then
  1225. warn "${PROGRAM}: Package directory \`${packagedir}' does not exist"
  1226. exit 4
  1227. fi
  1228. # The single slash '/' is valid here
  1229. if ! fnmatch '/*' "$targetdir"
  1230. then
  1231. warn "${PROGRAM}: Target directory \`${targetdir}' is not fully qualified"
  1232. exit 4
  1233. fi
  1234. if test ! -d "$targetdir"
  1235. then
  1236. warn "${PROGRAM}: Target directory \`${targetdir}' does not exist"
  1237. exit 4
  1238. fi
  1239. # Validate 'rootdir' directory
  1240. if test -n "$rootdir"
  1241. then
  1242. if test -d "$rootdir" && test "$rootdir" != /
  1243. then
  1244. # Remove slash from the end
  1245. rootdir="${rootdir%/}"
  1246. # A workaround for graft-2.13+. The specified directory is
  1247. # relative to the log file, we prepend it inside the rootdir
  1248. eval "$(graft -L)" ; GRAFT_LOGFILE="${GRAFT_LOGFILE:=/var/log/graft}"
  1249. mkdir -p -- "$rootdir$(dirname -- $GRAFT_LOGFILE)" || chkstatus_or_exit
  1250. # Compose 'rootdir' and log file option to be used with graft(1)
  1251. graft_r="-r $rootdir -l $GRAFT_LOGFILE"
  1252. # Unset variables coming from eval
  1253. unset GRAFT_PERL GRAFT_LOGFILE GRAFT_TARGETDIR GRAFT_PACKAGEDIR
  1254. else
  1255. warn "${PROGRAM}: Root directory \`${rootdir}' is not fully qualified"
  1256. exit 4
  1257. fi
  1258. export rootdir
  1259. fi
  1260. # Ensure 'TMPDIR' creation to prefix temporary files
  1261. if test ! -d "$TMPDIR"
  1262. then
  1263. mkdir -p -- "$TMPDIR" || chkstatus_or_exit
  1264. fi
  1265. readonly TMPDIR
  1266. # Process each package or recipe provided on the command-line
  1267. for package in "$@"
  1268. do
  1269. $mode $package
  1270. done