2 # Poor man's jigdo - download and assemble Jigsaw Download files
3 # Copyright 2001-2005 Richard Atterer
4 # Portability improvements by J.A. Bezemer, Jan 2002
5 # License: GPL version 2
7 # These 4 variables can be overridden in ~/.jigdo-lite if necessary:
8 jigdoOpts="--cache jigdo-file-cache.db"
9 wgetOpts="--passive-ftp --dot-style=mega --continue --timeout=30"
10 mirrors="mirrors.jigdo"
14 maxMissing=30 # Don't try fallback servers if x% or more of files missing
15 rcFile="$HOME/.jigdo-lite"
17 if test "x$OSTYPE" = "xmsys"; then
20 mirrors="jigdo-bin/mirrors.jigdo"
21 rcFile="jigdo-lite-settings.txt"
22 egrep() { grep -E "$@"; }
23 expr() { echo "$(($@))"; }
24 filesPerFetch=5 # What's the command line length limit?
30 #______________________________________________________________________
32 # read with readline, only if running bash >=2.03 (-e gives error on POSIX)
34 if test "x$BASH_VERSION" != "x"; then
35 if test "x${BASH_VERSION#[2-9].}" != "x$BASH_VERSION"; then
36 if test "x${BASH_VERSION#2.0[012]}" = "x$BASH_VERSION"; then
41 # Non-bash: Check whether "read -r" supported
42 if (echo | read -r REPLY 2>/dev/null); then
46 #______________________________________________________________________
49 # Returns 0 (true) if the supplied string is a HTTP/HTTPS/FTP/FILE
53 [hH][tT][tT][pP]:*) return 0;;
54 [hH][tT][tT][pP][sS]:*) return 0;;
55 [fF][tT][pP]:*) return 0;;
56 [fF][iI][lL][eE]:*) return 0;;
60 #______________________________________________________________________
62 strEqual() { test "x$1" = "x$2"; }
63 strNotEqual() { test "x$1" != "x$2"; }
64 strEmpty() { test "x$1" = "x"; }
65 strNotEmpty() { test "x$1" != "x"; }
66 #______________________________________________________________________
69 # Download a file, storing it in the current dir
71 if test "$#" -eq 0; then return 0; fi
72 wget --user-agent="$userAgent" $wgetOpts "$@" || return 1
74 #______________________________________________________________________
76 # Given URLs, fetch them into $imageTmp, then merge them into image
78 if test "$#" -eq 0; then return 0; fi
79 fetch --force-directories --directory-prefix="$imageTmp" -- "$@"
80 # Merge into the image
81 $jigdoFile $jigdoOpts --no-cache make-image --image="$image" \
82 --jigdo="$jigdoF" --template="$template" "$imageTmp"
84 if test "$jigdoErr" -ge 3; then
85 echo "jigdo-file failed with code $jigdoErr - aborting."
88 # Delete imageTmp, to avoid taking up more space than necessary
92 #______________________________________________________________________
94 # Prompt user to input value, assign result to $REPLY. If user just
95 # presses Return, assign supplied default value instead.
96 # input <prompt string> <default value>
100 if strNotEmpty "$2"; then prompt=" [$2]"; fi
101 printf "%s%s: " "$1" "$prompt"
102 if $nonInteractive; then echo; else $readLine REPLY; fi
103 if strEmpty "$REPLY"; then REPLY="$2"; fi
105 #______________________________________________________________________
107 # Read from $jigdoF and create a menu of images contained in the file.
108 # If invoked just as "imageMenu", print out the menu. If invoked as
109 # "imageMenu 5", set $image and $templateURI to the filename/URI of
110 # the 5th menu entry.
111 # The scan for [Image] sections stops when at least one such section
112 # has been read and a non-[Image] section follows. This is not
113 # correct, but speeds things up a lot because in many cases the large
114 # [Parts] section does not have to be scanned.
124 # Read image section contents
125 unset image templateURI templateMD5 shortInfo info
126 while $readLine l <&3; do
129 Filename=*) image="`echo $l | sed -e 's/^Filename= *//; s%[['\\''\"$\\\`|&/]%%g'`";;
130 Template=*) templateURI="`echo $l | sed -e 's/^Template= *//; s%[['\\''\"$\\\`|&]%%g'`";;
131 Template-MD5Sum=*) templateMD5="`echo $l | sed -e 's/^Template-MD5Sum= *//; s%[['\\''\"$\\\`|&/]%%g'`";;
132 Template-MD5Sum=*) templateMD5="`echo $l | sed -e 's/^Template-MD5Sum= *//; s%[^a-zA-Z0-9_-]%%g'`";;
133 ShortInfo=*) shortInfo="`echo $l | sed -e 's/^ShortInfo= *//; s%[[$\\\`|]%%g'`";;
134 Info=*) info="`echo $l | sed -e 's/^Info= *//; s%[['\\''\"$\\\`|]%%g'`";;
137 # Image section read, check for validity
138 if strNotEmpty "$image" && strNotEmpty "$templateURI"; then
139 curImageCount="`expr $curImageCount + 1`"
140 if strNotEmpty "$imageSel"; then
141 # Return info for image selected via $imageSel
142 if test "$imageSel" -eq "$curImageCount"; then exec 3<&-; return 0; fi
145 if strEmpty "$shortInfo"; then
146 printf "%3d: %s\n" "$curImageCount" "$image"
148 printf "%3d: %s (%s)\n" "$curImageCount" "$shortInfo" "$image"
152 case "$l" in "[Image]"*) continue;; esac
153 # Abort early, avoid reading [Parts]
154 imageCount="$curImageCount"; exec 3<&-; return 0;;
156 # Skip other parts of the file
157 while $readLine l <&3; do
158 case "$l" in "["*"]"*) break;; esac
160 case "$l" in "["*"]"*) continue;; esac
161 imageCount="$curImageCount"; exec 3<&-; return 0;;
164 imageCount="$curImageCount"
168 #______________________________________________________________________
170 # Output a horizontal rule
173 echo "-----------------------------------------------------------------"
175 #______________________________________________________________________
177 # Download template, unless already present in current dir
179 if $fetchedTemplate; then return 0; fi
182 template=`basename "$templateURI"`
184 if strEmpty "$templateMD5"; then
185 echo "[WARNING - \`Template-MD5Sum' missing from image section]"
188 if test -r "$template" && strNotEmpty "$templateMD5"; then
189 set -- `$jigdoFile md5sum --report=quiet "$template"`
190 if test "$1" = "$templateMD5"; then
191 echo "Not downloading .template file - \`$template' already present"
195 # elif test -r "$template"; then
196 # echo "Not downloading .template file - \`$template' already present"
197 # fetchedTemplate=true
200 if isURI "$templateURI"; then
201 # Absolute template URL
202 echo 'Downloading .template file'
204 fetch --continue -- "$templateURI"
205 elif isURI "$url"; then
206 # Template URI is relative to absolute jigdo URL
207 echo 'Downloading .template file'
209 fetch --continue -- `echo "$url" | sed 's%[^/]*$%%'`"$templateURI"
211 # Template URI is relative to local jigdo filename
213 # This is a bit broken - we ought to replace / with \ in templateURI too
214 template=`echo "$url" | sed 's%[^\]*$%%'`"$templateURI"
216 template=`echo "$url" | sed 's%[^/]*$%%'`"$templateURI"
220 # Does template exist now?
221 if test ! -r "$template"; then
222 echo "File \`$template' does not exist!"
225 if strEmpty "$templateMD5"; then return 0; fi
226 set -- `$jigdoFile md5sum --report=quiet "$template"`
227 if strEqual "$1" "$templateMD5"; then return 0; fi
228 echo "Error - template checksum mismatch!"
229 echo "The .template file does not belong to the .jigdo file - the"
230 echo "chances are high that the image generation process will break."
231 echo "I will abort now. If you know better than me and want this error"
232 echo "to be ignored, enter the string \"42\" to proceed."
234 echo "Note that you might get this error if you resumed the download of a"
235 echo ".template file, and that .template file has changed on the server in"
236 echo "the meantime. In this case, you may be able to fix the problem by"
237 echo "deleting the .template file and restarting jigdo-lite."
239 case $REPLY in *42*) return 0;; esac
242 #______________________________________________________________________
246 printf "jigdo='%s'${nl}debianMirror='%s'${nl}nonusMirror='%s'${nl}" \
247 "$jigdo" "$debianMirror" "$nonusMirror" >"$rcFile"
248 printf "tmpDir='%s'${nl}jigdoOpts='%s'${nl}" \
249 "$tmpDir" "$jigdoOpts" >>"$rcFile"
250 printf "wgetOpts='%s'${nl}scanMenu='%s'${nl}" \
251 "$wgetOpts" "$scanMenu" >>"$rcFile"
253 #______________________________________________________________________
258 if $batch; then true; else
259 echo "The fact that you got this far is a strong indication that \`$image'"
260 echo "was generated correctly. I will perform an additional, final check,"
261 echo "which you can interrupt safely with Ctrl-C if you do not want to wait."
264 $jigdoFile verify --image="$image" --jigdo="$jigdoF" --template="$template" \
268 #______________________________________________________________________
270 # $0 is an URL or filename. Download/process it.
274 # Arg can be either URL or filename. Maybe download file
275 if isURI "$url"; then
276 jigdoF=`basename "$url"`
278 if test -r "$jigdoF"; then
279 echo "Not downloading .jigdo file - \`$jigdoF' already present"
281 echo "Downloading .jigdo file"
287 # Does jigdo exist now?
288 if test ! -r "$jigdoF"; then
289 echo "File \`$jigdoF' does not exist!"
292 # Try to gunzip it. In case of error, assume that it wasn't gzipped
293 if gzip -cd "$jigdoF" >"$jigdoF.unpacked" 2>/dev/null; then
294 jigdoF="$jigdoF.unpacked"
296 rm -f "$jigdoF.unpacked"
298 #________________________________________
301 # Batch - download all images
303 echo "Images offered by \`$url':"
304 imageMenu # print out menu, set $imageCount
306 while test "$imageNr" -le "$imageCount"; do
307 imageMenu "$imageNr" # set $image and $templateURI
309 if test "$imageCount" -eq 1; then
310 echo "Batch mode: Will download \`$image'"
312 echo "Batch mode: Will download \`$image' (image $imageNr out of $imageCount)"
315 imageNr="`expr $imageNr + 1`"
324 echo "Images offered by \`$url':"
325 imageMenu # print out menu, set imageCount
326 if test "$imageCount" -eq 1; then
327 imageMenu "1" # set $image and $templateURI
329 break # Only 1 image - don't loop asking for images to download
331 input "Number of image to download" ""
332 if strEmpty "$REPLY"; then continue; fi
333 if test "$REPLY" -ge 1 -a "$REPLY" -le "$imageCount"; then
334 imageMenu "$REPLY" # set $image and $templateURI
335 imageDownload || return 1
341 case "$jigdoF" in *.unpacked) rm -f "$jigdoF";; esac
343 #______________________________________________________________________
346 # If --scan on command line, never ask, just scan that path
347 if strNotEmpty "$opt_filesToScan"; then
348 echo "Path to scan: $opt_filesToScan"
349 if $batch; then return 0; fi
350 # Retrieve template if necessary, then supply files
351 fetchTemplate || return 1
352 $jigdoFile make-image --image="$image" --jigdo="$jigdoF" \
353 --template="$template" $jigdoOpts "$opt_filesToScan"
355 if test "$jigdoErr" -eq 0 -a -r "$image"; then
357 $error 0 # All files were present on local filesystem
358 elif test "$jigdoErr" -ge 3; then
359 echo "jigdo-file failed with code $jigdoErr - aborting."
365 # Ask user for any parts on local filesystems
368 echo "If you already have a previous version of the CD you are"
369 echo "downloading, jigdo can re-use files on the old CD that are also"
370 echo "present in the new image, and you do not need to download them"
372 echo "again. Enter the path to the old CD ROM's contents (e.g. \`d:\\')."
374 echo "again. Mount the old CD ROM and enter the path it is mounted under"
375 echo "(e.g. \`/mnt/cdrom')."
377 echo "Alternatively, just press enter if you want to start downloading"
378 echo "the remaining files."
379 shift "$#" # Solaris /bin/sh doesn't understand "set --"
381 if strNotEmpty "$1"; then
383 echo "You can also enter a single digit from the list below to"
384 echo "select the respective entry for scanning:"
386 if strNotEmpty "$2"; then echo " 2: $2"; fi
387 if strNotEmpty "$3"; then echo " 3: $3"; fi
388 if strNotEmpty "$4"; then echo " 4: $4"; fi
389 if strNotEmpty "$5"; then echo " 5: $5"; fi
391 input "Files to scan"; filesToScan="$REPLY"
392 if strEmpty "$filesToScan"; then return 0; fi
393 # Do not add supplied string to menu if...
394 case "$filesToScan" in
395 *" "*|*"'"*|*'`'*|*'"'*|*'$'*) ;; # ...it has bad chars
396 1) filesToScan="$1";;
397 2) filesToScan="$2";;
398 3) filesToScan="$3";;
399 4) filesToScan="$4";;
400 5) filesToScan="$5";;
401 *) case " $1 $2 $3 $4 $5 " in
402 *" $filesToScan "*) ;; # ...it is already in the menu
403 *) set -- "$filesToScan" $scanMenu
404 scanMenu="$1 $2 $3 $4 $5"
408 if strEmpty "$filesToScan"; then continue; fi
410 # In batch mode, postpone template download, scan later
411 if $batch; then return 0; fi
413 # Retrieve template if necessary, then supply files
414 fetchTemplate || return 1
415 $jigdoFile make-image --image="$image" --jigdo="$jigdoF" \
416 --template="$template" $jigdoOpts "$filesToScan"
418 if test "$jigdoErr" -eq 0 -a -r "$image"; then
420 $error 0 # All files were present on local filesystem
421 elif test "$jigdoErr" -ge 3; then
422 echo "jigdo-file failed with code $jigdoErr - aborting."
427 #______________________________________________________________________
430 if $askQuestions; then true; else return; fi
432 # Crude check for whether any entry in the [Parts] section uses a
433 # "Debian" or "Non-US" label. If yes, start Debian mirror selection
436 # If in batch mode, always ask the server questions; even though
437 # the current .jigdo file might not use Debian servers, a later
438 # one might, but we can't ask then.
443 if egrep '^[^=]+= *["'\'']?Debian:' <"$jigdoF" >/dev/null; then
447 if egrep '^[^=]+= *["'\'']?Non-US:' <"$jigdoF" >/dev/null; then
452 # Extra options to pass to jigdo-file for server selection
455 while $usesDebian; do
457 echo "The jigdo file refers to files stored on Debian mirrors. Please"
458 echo "choose a Debian mirror as follows: Either enter a complete URL"
459 echo "pointing to a mirror (in the form"
460 echo "\`ftp://ftp.debian.org/debian/'), or enter any regular expression"
461 echo "for searching through the list of mirrors: Try a two-letter"
462 echo "country code such as \`de', or a country name like \`United"
463 echo "States', or a server name like \`sunsite'."
464 input "Debian mirror" "$debianMirror"
465 # Special-case two-letter country codes
466 case "$REPLY" in [a-z][a-z]) REPLY="[. ]$REPLY[/. ]";; esac
467 if isURI "$REPLY"; then
468 # Turn any "file:/opt/mirror" into "file:/opt/mirror/"
469 debianMirror=`echo $REPLY | sed -e 's%^ *\([^ ]*[^/ ]\)/*\( .*\)*$%\1/%'`
471 uriOpts="--uri Debian='$debianMirror'"
474 egrep -i "$REPLY" "$mirrors" | sed -n -e 's/^Debian=//p'
476 echo "An up-to-date copy of the above list is available at"
477 echo "ftp://ftp.debian.org/debian/README.mirrors.txt"
482 echo "The jigdo file also refers to the Non-US section of the Debian"
483 echo "archive. Please repeat the mirror selection for Non-US. Do not"
484 echo "simply copy the URL you entered above; this does not work because"
485 echo "the path on the servers differs!"
486 input "Debian non-US mirror" "$nonusMirror"
487 case "$REPLY" in [a-z][a-z]) REPLY="[. ]$REPLY[/. ]";; esac
488 if isURI "$REPLY"; then
489 # Turn any "file:/opt/mirror" into "file:/opt/mirror/"
490 nonusMirror=`echo $REPLY | sed -e 's%^ *\([^ ]*[^/ ]\)/*\( .*\)*$%\1/%'`
492 uriOpts="$uriOpts --uri Non-US='$nonusMirror'";
495 egrep -i "$REPLY" "$mirrors" | sed -n -e 's/^Non-US=//p'
497 echo "An up-to-date copy of the above list is available at"
498 echo "ftp://ftp.debian.org/debian/README.non-US"
501 #______________________________________________________________________
503 # $image: image filename
504 # $templateURI: Absolute/relative URI from .jigdo file
505 # $info: Info=... entry from .jigdo file
508 fetchedTemplate=false
510 if strNotEmpty "$info"; then
511 printf "\nFurther information about \`%s':\n" "$image"
516 # Create name of temporary dir, by stripping extension from $image
517 imageTmp="`echo $image | sed 's%\.\(tmp|iso|raw\)%%'`"
518 if test -f "$imageTmp" -o "x$imageTmp" = "x$image"; then
519 imageTmp="$imageTmp.tmpdir"
522 # Deal with leftover tmpdir from previous, interrupted download
523 if $askQuestions && test -d "$imageTmp"; then
525 echo "The temporary directory \`$imageTmp' already exists. Its contents"
526 echo "ARE GOING TO BE DELETED (possibly after having been copied to the"
527 echo "image, if they are of interest for it). If you do not want this"
528 echo "to happen, press Ctrl-C now. Otherwise, press Return to proceed."
532 if $askQuestions; then
533 # Ask questions and scan dirs, set up $filesToScan
534 scanFiles || return 1
535 if $batch; then selectServers; fi
538 if $batch && strNotEmpty "$filesToScan$opt_filesToScan"; then
539 # Retrieve template if necessary, then supply files. One of the
540 # two variables $filesToScan and $opt_filesToScan is always empty
541 fetchTemplate || return 1
542 $jigdoFile make-image --image="$image" --jigdo="$jigdoF" \
543 --template="$template" $jigdoOpts "$filesToScan$opt_filesToScan"
545 if test "$jigdoErr" -eq 0 -a -r "$image"; then
547 return 0 # All files were present on local filesystem
548 elif test "$jigdoErr" -ge 3; then
549 echo "jigdo-file failed with code $jigdoErr - aborting."
554 # Read any files from non-empty tmpDir. Don't delete tmpDir yet, as
555 # wget may resume some half-finished files
556 if test -d "$imageTmp"; then
557 fetchTemplate || return 1
558 # Merge into the image
559 $jigdoFile $jigdoOpts --no-cache make-image --image="$image" \
560 --jigdo="$jigdoF" --template="$template" "$imageTmp"
563 # Download files and merge them into the image. We instruct wget to
564 # download 10 files at a time and then merge them. This way, compared
565 # to downloading everything, the peak disc space usage is not twice
566 # the size of the final image.
569 if $batch; then true; else selectServers; fi
570 fetchTemplate || return 1
573 # If a "file:" URI was given instead of a server URL, try to merge
574 # any files into the image.
575 echo "Merging parts from \`file:' URIs, if any..."
576 $jigdoFile print-missing-all --image="$image" --jigdo="$jigdoF" \
577 --template="$template" $jigdoOpts $uriOpts \
578 | egrep -v '^([a-zA-Z0-9.+_-]+:|$)' \
579 | $jigdoFile make-image --image="$image" --jigdo="$jigdoF" \
580 --template="$template" $jigdoOpts --files-from=-
582 if test "$jigdoErr" -ge 3; then
583 echo "jigdo-file failed with code $jigdoErr - aborting."
587 # First try to download all files using the first URL in the
588 # print-missing-all list. If any files remain missing, add another
589 # pass, this time try to download the missing files using the 2nd
592 for pass in x xx xxx xxxx xxxxx xxxxxx xxxxxxx xxxxxxxx; do
593 $jigdoFile print-missing-all --image="$image" --jigdo="$jigdoF" \
594 --template="$template" $jigdoOpts $uriOpts \
595 | egrep -i '^(http:|ftp:|$)' >"$list"
596 missingCount=`egrep '^$' <"$list" | wc -l | sed -e 's/ *//g'`
597 # Accumulate URLs in $@, pass them to fetchAndMerge in batches
598 shift "$#" # Solaris /bin/sh doesn't understand "set --"
601 while $readLine url <&3; do
603 if strEmpty "$url"; then count=""; continue; fi
604 if test "$count" != "$pass"; then continue; fi
605 if $noMorePasses; then
607 echo "$missingCount files not found in previous pass, trying"
608 echo "alternative download locations:"
613 if test "$#" -ge "$filesPerFetch"; then
614 if fetchAndMerge "$@"; then true; else exec 3<&-; return 1; fi
615 shift "$#" # Solaris /bin/sh doesn't understand "set --"
619 if test "$#" -ge 1; then fetchAndMerge "$@" || return 1; fi
620 if $noMorePasses; then break; fi
621 if test -r "$image"; then break; fi
626 if test -r "$image"; then break; fi
629 echo "Aaargh - $missingCount files could not be downloaded. This should not"
630 echo "happen! Depending on the problem, it may help to retry downloading"
631 echo "the missing files."
632 if $batch; then return 1; fi
633 if $usesDebian || $usesNonus; then
634 echo "Also, you could try changing to another Debian or Non-US server,"
635 echo "in case the one you used is out of sync."
638 echo "However, if all the files downloaded without errors and you"
639 echo "still get this message, it means that the files changed on the"
640 echo "server, so the image cannot be generated."
641 if $usesDebian || $usesNonus; then
642 echo "As a last resort, you could try to complete the CD image download"
643 echo "by fetching the remaining data with rsync."
646 echo "Press Return to retry downloading the missing files."
647 echo "Press Ctrl-C to abort. (If you re-run jigdo-lite later, it will"
648 echo "resume from here, the downloaded data is not lost if you press"
655 #======================================================================
658 echo 'Jigsaw Download "lite"'
659 echo "Copyright (C) 2001-2005 | jigdo@"
660 echo "Richard Atterer | atterer.org"
662 jigdoFile="jigdo-file"
663 jigdo-file --version >/dev/null 2>/dev/null
664 if test "$?" -ne 0; then
665 # Using ./jigdo-file is possibly a security risk, so only use if
666 # nothing else is there
667 if test -x "./jigdo-file"; then jigdoFile="./jigdo-file"; fi
669 jigdoFileSameDir="`dirname $0`/jigdo-file"
670 if test -x "$jigdoFileSameDir"; then jigdoFile="$jigdoFileSameDir"; fi
672 mirrorsSameDir="`dirname $0`/$mirrors"
673 if test -r "$mirrorsSameDir"; then mirrors="$mirrorsSameDir"; fi
679 for prog in wget egrep sed gzip wc expr; do
680 which "$prog" >/dev/null \
681 || echo "Could not find program \`$prog' - please install it!"
685 userAgent="jigdo-lite/`$jigdoFile --version 2>/dev/null | ($readLine jf v ver; echo $ver)` (`wget --version 2>/dev/null | ($readLine ver; echo $ver)`; $OSTYPE)"
687 # Load preferences file, if present
688 if test -f "$rcFile"; then
689 echo "Loading settings from \`$rcFile'"
693 # When upgrading from versions <0.6.9, update value of tmpDir
694 if strEqual "$tmpDir" "tmp" || strEqual "$tmpDir" "temp"; then
699 # If running for the first time, try to read mirror info from sources.list
700 if strEmpty "$debianMirror$nonusMirror" && test -f "/etc/apt/sources.list"; then
701 echo "Getting mirror information from /etc/apt/sources.list"
702 while $readLine deb url dist rest; do
705 test "$nonusMirror" = "" && \
706 nonusMirror=`echo "$url"|sed 's%^copy://%file://%; s%//*$%/%'`;;
708 test "$debianMirror" = "" && \
709 debianMirror=`echo "$url"|sed 's%^copy://%file://%; s%//*$%/%'`;;
711 done <"/etc/apt/sources.list"
713 #________________________________________
715 # Process command line switches
722 echo "Usage: $0 [OPTIONS] [FILES or URLS...]"
723 echo " -h --help Print this message"
724 echo " -v --version Output version information"
725 echo " --scan PATH Don't ask for \"Files to scan\", use this path"
726 echo " --noask \"Press Return automatically\" for all "
727 echo " questions, accepting the offered defaults"
743 #________________________________________
745 # No cmd line argument => prompt user
746 if test "$#" -eq 0; then
748 echo "To resume a half-finished download, enter name of .jigdo file."
749 echo "To start a new download, enter URL of .jigdo file."
750 echo "You can also enter several URLs/filenames, separated with spaces,"
751 echo "or enumerate in {}, e.g. \`http://server/cd-{1_NONUS,2,3}.jigdo'"
752 input "jigdo" "$jigdo"
757 # Expand "a{b,c{x,y}}" into "ab acx acy". How's that for a sed script?
759 # a{b,c{x,y},d{,2}} FAILS
763 set -- `echo "$*" | sed '
766 s%{\(\([^{},]*,\)*\)\([^{},]*\){\([^,{}]*\),\{0,1\}\([^{}]*\)}\([^{}]*\)}%{\1\3\4,\3{\5}\6}%
770 s%\([^{ ]*\){\([^,{}]*\),\{0,1\}\([^{}]*\)}\([^ ]*\)%\1\2\4 \1{\3}\4%
772 if test "$#" -gt 1; then
774 echo "You have asked me to process several files/URLs:"
775 for file in "$@"; do echo " $file"; done
778 echo "Entering batch mode"
783 #________________________________________