summaryrefslogtreecommitdiff
path: root/i/pc104/initrd/conf
diff options
context:
space:
mode:
authorjutteau2007-05-11 18:10:19 +0000
committerjutteau2007-05-11 18:10:19 +0000
commit5e1a84ab74d5e97582427f016f291a8c11e66f99 (patch)
treeddae3f36a21e7fa7c97145f4a49edcfe50063aa3 /i/pc104/initrd/conf
parentb95709754c870cbb793266ce8e605a81d01a4f75 (diff)
Completion du script de mise à jour de la pc-104 :
* Ajout des sources de busybox dans ./conf/busybox/ * Ajout d'un fichier réclamé par les script dans ./conf/busybox.links
Diffstat (limited to 'i/pc104/initrd/conf')
-rw-r--r--i/pc104/initrd/conf/busybox.links18
-rw-r--r--i/pc104/initrd/conf/busybox/AUTHORS173
-rw-r--r--i/pc104/initrd/conf/busybox/Config.in496
-rw-r--r--i/pc104/initrd/conf/busybox/INSTALL125
-rw-r--r--i/pc104/initrd/conf/busybox/LICENSE348
-rw-r--r--i/pc104/initrd/conf/busybox/Makefile1280
-rw-r--r--i/pc104/initrd/conf/busybox/Makefile.custom145
-rw-r--r--i/pc104/initrd/conf/busybox/Makefile.flags61
-rw-r--r--i/pc104/initrd/conf/busybox/Makefile.help42
-rw-r--r--i/pc104/initrd/conf/busybox/README198
-rw-r--r--i/pc104/initrd/conf/busybox/TODO320
-rw-r--r--i/pc104/initrd/conf/busybox/applets/Kbuild22
-rw-r--r--i/pc104/initrd/conf/busybox/applets/applets.c495
-rw-r--r--i/pc104/initrd/conf/busybox/applets/busybox.c147
-rwxr-xr-xi/pc104/initrd/conf/busybox/applets/busybox.mkll24
-rw-r--r--i/pc104/initrd/conf/busybox/applets/individual.c27
-rwxr-xr-xi/pc104/initrd/conf/busybox/applets/install.sh94
-rw-r--r--i/pc104/initrd/conf/busybox/applets/usage.c17
-rwxr-xr-xi/pc104/initrd/conf/busybox/applets/usage_compressed19
-rw-r--r--i/pc104/initrd/conf/busybox/arch/i386/Makefile5
-rw-r--r--i/pc104/initrd/conf/busybox/archival/Config.in300
-rw-r--r--i/pc104/initrd/conf/busybox/archival/Kbuild22
-rw-r--r--i/pc104/initrd/conf/busybox/archival/ar.c94
-rw-r--r--i/pc104/initrd/conf/busybox/archival/bbunzip.c355
-rw-r--r--i/pc104/initrd/conf/busybox/archival/cpio.c87
-rw-r--r--i/pc104/initrd/conf/busybox/archival/dpkg.c1754
-rw-r--r--i/pc104/initrd/conf/busybox/archival/dpkg_deb.c97
-rw-r--r--i/pc104/initrd/conf/busybox/archival/gzip.c2085
-rw-r--r--i/pc104/initrd/conf/busybox/archival/libunarchive/Kbuild59
-rw-r--r--i/pc104/initrd/conf/busybox/archival/libunarchive/archive_xread_all_eof.c20
-rw-r--r--i/pc104/initrd/conf/busybox/archival/libunarchive/check_header_gzip.c59
-rw-r--r--i/pc104/initrd/conf/busybox/archival/libunarchive/data_align.c17
-rw-r--r--i/pc104/initrd/conf/busybox/archival/libunarchive/data_extract_all.c127
-rw-r--r--i/pc104/initrd/conf/busybox/archival/libunarchive/data_extract_to_buffer.c17
-rw-r--r--i/pc104/initrd/conf/busybox/archival/libunarchive/data_extract_to_stdout.c13
-rw-r--r--i/pc104/initrd/conf/busybox/archival/libunarchive/data_skip.c16
-rw-r--r--i/pc104/initrd/conf/busybox/archival/libunarchive/decompress_bunzip2.c731
-rw-r--r--i/pc104/initrd/conf/busybox/archival/libunarchive/decompress_uncompress.c308
-rw-r--r--i/pc104/initrd/conf/busybox/archival/libunarchive/decompress_unlzma.c497
-rw-r--r--i/pc104/initrd/conf/busybox/archival/libunarchive/decompress_unzip.c1104
-rw-r--r--i/pc104/initrd/conf/busybox/archival/libunarchive/filter_accept_all.c17
-rw-r--r--i/pc104/initrd/conf/busybox/archival/libunarchive/filter_accept_list.c19
-rw-r--r--i/pc104/initrd/conf/busybox/archival/libunarchive/filter_accept_list_reassign.c44
-rw-r--r--i/pc104/initrd/conf/busybox/archival/libunarchive/filter_accept_reject_list.c33
-rw-r--r--i/pc104/initrd/conf/busybox/archival/libunarchive/find_list_entry.c54
-rw-r--r--i/pc104/initrd/conf/busybox/archival/libunarchive/get_header_ar.c126
-rw-r--r--i/pc104/initrd/conf/busybox/archival/libunarchive/get_header_cpio.c160
-rw-r--r--i/pc104/initrd/conf/busybox/archival/libunarchive/get_header_tar.c284
-rw-r--r--i/pc104/initrd/conf/busybox/archival/libunarchive/get_header_tar_bz2.c20
-rw-r--r--i/pc104/initrd/conf/busybox/archival/libunarchive/get_header_tar_gz.c31
-rw-r--r--i/pc104/initrd/conf/busybox/archival/libunarchive/get_header_tar_lzma.c22
-rw-r--r--i/pc104/initrd/conf/busybox/archival/libunarchive/header_list.c11
-rw-r--r--i/pc104/initrd/conf/busybox/archival/libunarchive/header_skip.c10
-rw-r--r--i/pc104/initrd/conf/busybox/archival/libunarchive/header_verbose_list.c31
-rw-r--r--i/pc104/initrd/conf/busybox/archival/libunarchive/init_handle.c22
-rw-r--r--i/pc104/initrd/conf/busybox/archival/libunarchive/open_transformer.c44
-rw-r--r--i/pc104/initrd/conf/busybox/archival/libunarchive/seek_by_jump.c24
-rw-r--r--i/pc104/initrd/conf/busybox/archival/libunarchive/seek_by_read.c18
-rw-r--r--i/pc104/initrd/conf/busybox/archival/libunarchive/unpack_ar_archive.c22
-rw-r--r--i/pc104/initrd/conf/busybox/archival/rpm.c367
-rw-r--r--i/pc104/initrd/conf/busybox/archival/rpm2cpio.c90
-rw-r--r--i/pc104/initrd/conf/busybox/archival/tar.c939
-rw-r--r--i/pc104/initrd/conf/busybox/archival/unzip.c376
-rwxr-xr-xi/pc104/initrd/conf/busybox/busyboxbin0 -> 573272 bytes
-rw-r--r--i/pc104/initrd/conf/busybox/busybox.links18
-rw-r--r--i/pc104/initrd/conf/busybox/console-tools/Config.in105
-rw-r--r--i/pc104/initrd/conf/busybox/console-tools/Kbuild19
-rw-r--r--i/pc104/initrd/conf/busybox/console-tools/chvt.c34
-rw-r--r--i/pc104/initrd/conf/busybox/console-tools/clear.c22
-rw-r--r--i/pc104/initrd/conf/busybox/console-tools/deallocvt.c38
-rw-r--r--i/pc104/initrd/conf/busybox/console-tools/dumpkmap.c66
-rw-r--r--i/pc104/initrd/conf/busybox/console-tools/loadfont.c194
-rw-r--r--i/pc104/initrd/conf/busybox/console-tools/loadkmap.c62
-rw-r--r--i/pc104/initrd/conf/busybox/console-tools/openvt.c43
-rw-r--r--i/pc104/initrd/conf/busybox/console-tools/reset.c33
-rw-r--r--i/pc104/initrd/conf/busybox/console-tools/resize.c70
-rw-r--r--i/pc104/initrd/conf/busybox/console-tools/setconsole.c48
-rw-r--r--i/pc104/initrd/conf/busybox/console-tools/setkeycodes.c50
-rw-r--r--i/pc104/initrd/conf/busybox/console-tools/setlogcons.c32
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/Config.in782
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/Kbuild85
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/basename.c51
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/cal.c352
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/cat.c43
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/catv.c70
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/chgrp.c28
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/chmod.c159
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/chown.c167
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/chroot.c38
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/cksum.c54
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/cmp.c145
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/comm.c127
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/cp.c100
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/cut.c287
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/date.c241
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/dd.c249
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/df.c155
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/diff.c1227
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/dirname.c27
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/dos2unix.c116
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/du.c247
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/echo.c159
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/env.c128
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/expr.c506
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/false.c20
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/fold.c155
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/head.c140
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/hostid.c26
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/id.c130
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/install.c202
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/length.c20
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/libcoreutils/Kbuild12
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/libcoreutils/coreutils.h16
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/libcoreutils/cp_mv_stat.c43
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/libcoreutils/getopt_mk_fifo_nod.c51
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/ln.c105
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/logname.c43
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/ls.c957
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/md5_sha1_sum.c185
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/mkdir.c82
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/mkfifo.c39
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/mknod.c51
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/mv.c137
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/nice.c55
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/nohup.c60
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/od.c225
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/od_bloaty.c1465
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/printenv.c42
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/printf.c313
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/pwd.c26
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/realpath.c47
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/rm.c53
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/rmdir.c61
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/seq.c41
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/sleep.c68
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/sort.c401
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/stat.c676
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/stty.c1189
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/sum.c100
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/sync.c24
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/tail.c278
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/tee.c101
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/test.c586
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/touch.c62
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/tr.c254
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/true.c20
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/tty.c44
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/uname.c108
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/uniq.c104
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/usleep.c29
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/uudecode.c181
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/uuencode.c76
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/watch.c80
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/wc.c205
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/who.c76
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/whoami.c26
-rw-r--r--i/pc104/initrd/conf/busybox/coreutils/yes.c42
-rw-r--r--i/pc104/initrd/conf/busybox/debianutils/Config.in88
-rw-r--r--i/pc104/initrd/conf/busybox/debianutils/Kbuild13
-rw-r--r--i/pc104/initrd/conf/busybox/debianutils/mktemp.c49
-rw-r--r--i/pc104/initrd/conf/busybox/debianutils/pipe_progress.c43
-rw-r--r--i/pc104/initrd/conf/busybox/debianutils/readlink.c52
-rw-r--r--i/pc104/initrd/conf/busybox/debianutils/run_parts.c192
-rw-r--r--i/pc104/initrd/conf/busybox/debianutils/start_stop_daemon.c330
-rw-r--r--i/pc104/initrd/conf/busybox/debianutils/which.c48
-rwxr-xr-xi/pc104/initrd/conf/busybox/docs/autodocifier.pl303
-rw-r--r--i/pc104/initrd/conf/busybox/docs/busybox.net/FAQ.html1108
-rw-r--r--i/pc104/initrd/conf/busybox/docs/busybox.net/about.html24
-rw-r--r--i/pc104/initrd/conf/busybox/docs/busybox.net/busybox-growth.ps404
-rw-r--r--i/pc104/initrd/conf/busybox/docs/busybox.net/copyright.txt30
-rw-r--r--i/pc104/initrd/conf/busybox/docs/busybox.net/developer.html69
-rw-r--r--i/pc104/initrd/conf/busybox/docs/busybox.net/download.html29
-rw-r--r--i/pc104/initrd/conf/busybox/docs/busybox.net/footer.html47
-rw-r--r--i/pc104/initrd/conf/busybox/docs/busybox.net/header.html96
-rw-r--r--i/pc104/initrd/conf/busybox/docs/busybox.net/images/back.pngbin0 -> 322 bytes
-rw-r--r--i/pc104/initrd/conf/busybox/docs/busybox.net/images/busybox.jpegbin0 -> 9023 bytes
-rw-r--r--i/pc104/initrd/conf/busybox/docs/busybox.net/images/busybox.pngbin0 -> 34014 bytes
-rw-r--r--i/pc104/initrd/conf/busybox/docs/busybox.net/images/busybox1.pngbin0 -> 10913 bytes
-rw-r--r--i/pc104/initrd/conf/busybox/docs/busybox.net/images/busybox2.jpgbin0 -> 8204 bytes
-rw-r--r--i/pc104/initrd/conf/busybox/docs/busybox.net/images/busybox3.jpgbin0 -> 3292 bytes
-rw-r--r--i/pc104/initrd/conf/busybox/docs/busybox.net/images/dir.pngbin0 -> 309 bytes
-rw-r--r--i/pc104/initrd/conf/busybox/docs/busybox.net/images/donate.pngbin0 -> 807 bytes
-rw-r--r--i/pc104/initrd/conf/busybox/docs/busybox.net/images/fm.mini.pngbin0 -> 7708 bytes
-rw-r--r--i/pc104/initrd/conf/busybox/docs/busybox.net/images/gfx_by_gimp.pngbin0 -> 3955 bytes
-rw-r--r--i/pc104/initrd/conf/busybox/docs/busybox.net/images/ltbutton2.pngbin0 -> 6798 bytes
-rw-r--r--i/pc104/initrd/conf/busybox/docs/busybox.net/images/osuosl.pngbin0 -> 8683 bytes
-rw-r--r--i/pc104/initrd/conf/busybox/docs/busybox.net/images/sdsmall.pngbin0 -> 1593 bytes
-rw-r--r--i/pc104/initrd/conf/busybox/docs/busybox.net/images/text.pngbin0 -> 307 bytes
-rw-r--r--i/pc104/initrd/conf/busybox/docs/busybox.net/images/valid-html401.pngbin0 -> 1950 bytes
-rw-r--r--i/pc104/initrd/conf/busybox/docs/busybox.net/images/vh40.gifbin0 -> 906 bytes
-rw-r--r--i/pc104/initrd/conf/busybox/docs/busybox.net/images/written.in.vi.pngbin0 -> 4394 bytes
-rw-r--r--i/pc104/initrd/conf/busybox/docs/busybox.net/index.html1
-rw-r--r--i/pc104/initrd/conf/busybox/docs/busybox.net/license.html97
-rw-r--r--i/pc104/initrd/conf/busybox/docs/busybox.net/links.html19
-rw-r--r--i/pc104/initrd/conf/busybox/docs/busybox.net/lists.html46
-rw-r--r--i/pc104/initrd/conf/busybox/docs/busybox.net/news.html252
-rw-r--r--i/pc104/initrd/conf/busybox/docs/busybox.net/oldnews.html1140
-rw-r--r--i/pc104/initrd/conf/busybox/docs/busybox.net/products.html170
-rw-r--r--i/pc104/initrd/conf/busybox/docs/busybox.net/screenshot.html62
-rw-r--r--i/pc104/initrd/conf/busybox/docs/busybox.net/shame.html82
-rw-r--r--i/pc104/initrd/conf/busybox/docs/busybox.net/sponsors.html35
-rw-r--r--i/pc104/initrd/conf/busybox/docs/busybox.net/subversion.html52
-rw-r--r--i/pc104/initrd/conf/busybox/docs/busybox.net/tinyutils.html86
-rw-r--r--i/pc104/initrd/conf/busybox/docs/busybox_footer.pod258
-rw-r--r--i/pc104/initrd/conf/busybox/docs/busybox_header.pod82
-rw-r--r--i/pc104/initrd/conf/busybox/docs/cgi/cl.html46
-rw-r--r--i/pc104/initrd/conf/busybox/docs/cgi/env.html149
-rw-r--r--i/pc104/initrd/conf/busybox/docs/cgi/in.html33
-rw-r--r--i/pc104/initrd/conf/busybox/docs/cgi/interface.html29
-rw-r--r--i/pc104/initrd/conf/busybox/docs/cgi/out.html126
-rw-r--r--i/pc104/initrd/conf/busybox/docs/contributing.txt449
-rw-r--r--i/pc104/initrd/conf/busybox/docs/draft-coar-cgi-v11-03-clean.html2674
-rw-r--r--i/pc104/initrd/conf/busybox/docs/ipv4_ipv6.txt223
-rw-r--r--i/pc104/initrd/conf/busybox/docs/keep_data_small.txt206
-rw-r--r--i/pc104/initrd/conf/busybox/docs/mdev.txt68
-rw-r--r--i/pc104/initrd/conf/busybox/docs/new-applet-HOWTO.txt151
-rw-r--r--i/pc104/initrd/conf/busybox/docs/sigint.htm627
-rw-r--r--i/pc104/initrd/conf/busybox/docs/style-guide.txt689
-rw-r--r--i/pc104/initrd/conf/busybox/docs/tar_pax.txt239
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/Config.in67
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/Kbuild12
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/README12
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/chattr.c190
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/e2fs_defs.h561
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/e2fs_lib.c197
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/e2fs_lib.h30
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/fsck.c1243
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/lsattr.c115
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/Config.in67
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/Kbuild16
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/README3
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/blkid/Kbuild23
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/blkid/blkid.h105
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/blkid/blkidP.h187
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/blkid/blkid_getsize.c179
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/blkid/cache.c126
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/blkid/dev.c214
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/blkid/devname.c367
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/blkid/devno.c222
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/blkid/list.c110
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/blkid/list.h73
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/blkid/probe.c721
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/blkid/probe.h375
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/blkid/read.c461
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/blkid/resolve.c139
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/blkid/save.c189
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/blkid/tag.c432
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/chattr.c220
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2fsbb.h43
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2fsck.c13548
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2fsck.h640
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2p/Kbuild15
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2p/e2p.h64
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2p/feature.c187
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2p/fgetsetflags.c70
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2p/fgetsetversion.c70
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2p/hashstr.c70
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2p/iod.c52
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2p/ls.c273
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2p/mntopts.c134
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2p/ostype.c74
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2p/parse_num.c65
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2p/pe.c32
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2p/pf.c74
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2p/ps.c27
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2p/uuid.c78
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/Kbuild23
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/alloc.c174
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/alloc_sb.c58
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/alloc_stats.c53
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/alloc_tables.c118
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/badblocks.c328
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/bb_compat.c64
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/bb_inode.c268
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/bitmaps.c211
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/bitops.c91
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/bitops.h107
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/block.c438
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/bmap.c264
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/bmove.c156
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/brel.h87
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/brel_ma.c196
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/check_desc.c69
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/closefs.c381
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/cmp_bitmaps.c73
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/dblist.c260
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/dblist_dir.c76
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/dir_iterate.c220
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/dirblock.c133
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/dirhash.c234
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/dupfs.c97
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/e2image.h52
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/expanddir.c127
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/ext2_err.h116
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/ext2_ext_attr.h53
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/ext2_fs.h570
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/ext2_io.h114
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/ext2_types.h2
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/ext2fs.h923
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/ext2fsP.h89
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/ext2fs_inline.c367
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/ext_attr.c101
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/fileio.c377
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/finddev.c199
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/flushb.c83
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/freefs.c128
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/gen_bitmap.c49
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/get_pathname.c157
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/getsectsize.c58
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/getsize.c291
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/icount.c467
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/imager.c377
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/ind_block.c71
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/initialize.c388
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/inline.c33
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/inode.c767
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/inode_io.c271
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/io_manager.c70
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/irel.h115
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/irel_ma.c367
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/ismounted.c357
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/jfs_dat.h65
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/kernel-jbd.h236
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/kernel-list.h113
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/link.c135
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/lookup.c70
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/mkdir.c142
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/mkjournal.c428
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/namei.c205
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/newdir.c73
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/openfs.c330
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/read_bb.c98
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/read_bb_file.c98
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/res_gdt.c221
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/rs_bitmap.c107
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/rw_bitmaps.c296
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/sparse.c79
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/swapfs.c236
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/test_io.c380
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/unix_io.c703
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/unlink.c100
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/valid_blk.c57
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/version.c51
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/write_bb_file.c35
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/fsck.c1392
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/fsck.h16
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/lsattr.c129
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/mke2fs.c1336
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/tune2fs.c729
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/util.c267
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/util.h22
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/uuid/Kbuild14
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/uuid/compare.c55
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/uuid/gen_uuid.c304
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/uuid/pack.c69
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/uuid/parse.c80
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/uuid/unpack.c63
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/uuid/unparse.c77
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/uuid/uuid.h104
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/uuid/uuidP.h60
-rw-r--r--i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/uuid/uuid_time.c161
-rw-r--r--i/pc104/initrd/conf/busybox/editors/Config.in138
-rw-r--r--i/pc104/initrd/conf/busybox/editors/Kbuild12
-rw-r--r--i/pc104/initrd/conf/busybox/editors/awk.c2781
-rw-r--r--i/pc104/initrd/conf/busybox/editors/ed.c1232
-rw-r--r--i/pc104/initrd/conf/busybox/editors/patch.c278
-rw-r--r--i/pc104/initrd/conf/busybox/editors/sed.c1347
-rw-r--r--i/pc104/initrd/conf/busybox/editors/vi.c3999
-rw-r--r--i/pc104/initrd/conf/busybox/examples/bootfloppy/bootfloppy.txt180
-rw-r--r--i/pc104/initrd/conf/busybox/examples/bootfloppy/display.txt4
-rw-r--r--i/pc104/initrd/conf/busybox/examples/bootfloppy/etc/fstab2
-rwxr-xr-xi/pc104/initrd/conf/busybox/examples/bootfloppy/etc/init.d/rcS3
-rw-r--r--i/pc104/initrd/conf/busybox/examples/bootfloppy/etc/inittab5
-rw-r--r--i/pc104/initrd/conf/busybox/examples/bootfloppy/etc/profile8
-rwxr-xr-xi/pc104/initrd/conf/busybox/examples/bootfloppy/mkdevs.sh62
-rwxr-xr-xi/pc104/initrd/conf/busybox/examples/bootfloppy/mkrootfs.sh105
-rwxr-xr-xi/pc104/initrd/conf/busybox/examples/bootfloppy/mksyslinux.sh48
-rw-r--r--i/pc104/initrd/conf/busybox/examples/bootfloppy/quickstart.txt15
-rw-r--r--i/pc104/initrd/conf/busybox/examples/bootfloppy/syslinux.cfg7
-rw-r--r--i/pc104/initrd/conf/busybox/examples/busybox.spec44
-rwxr-xr-xi/pc104/initrd/conf/busybox/examples/depmod.pl289
-rw-r--r--i/pc104/initrd/conf/busybox/examples/devfsd.conf133
-rw-r--r--i/pc104/initrd/conf/busybox/examples/dnsd.conf1
-rw-r--r--i/pc104/initrd/conf/busybox/examples/inetd.conf73
-rw-r--r--i/pc104/initrd/conf/busybox/examples/inittab90
-rwxr-xr-xi/pc104/initrd/conf/busybox/examples/mk2knr.pl84
-rwxr-xr-xi/pc104/initrd/conf/busybox/examples/udhcp/sample.bound31
-rwxr-xr-xi/pc104/initrd/conf/busybox/examples/udhcp/sample.deconfig4
-rwxr-xr-xi/pc104/initrd/conf/busybox/examples/udhcp/sample.nak4
-rwxr-xr-xi/pc104/initrd/conf/busybox/examples/udhcp/sample.renew31
-rw-r--r--i/pc104/initrd/conf/busybox/examples/udhcp/sample.script7
-rw-r--r--i/pc104/initrd/conf/busybox/examples/udhcp/simple.script40
-rw-r--r--i/pc104/initrd/conf/busybox/examples/udhcp/udhcpd.conf123
-rw-r--r--i/pc104/initrd/conf/busybox/examples/undeb53
-rw-r--r--i/pc104/initrd/conf/busybox/examples/unrpm48
-rw-r--r--i/pc104/initrd/conf/busybox/examples/zcip.script38
-rw-r--r--i/pc104/initrd/conf/busybox/findutils/Config.in175
-rw-r--r--i/pc104/initrd/conf/busybox/findutils/Kbuild10
-rw-r--r--i/pc104/initrd/conf/busybox/findutils/find.c645
-rw-r--r--i/pc104/initrd/conf/busybox/findutils/grep.c507
-rw-r--r--i/pc104/initrd/conf/busybox/findutils/xargs.c540
-rw-r--r--i/pc104/initrd/conf/busybox/include/applets.h341
-rw-r--r--i/pc104/initrd/conf/busybox/include/busybox.h39
-rw-r--r--i/pc104/initrd/conf/busybox/include/dump.h50
-rw-r--r--i/pc104/initrd/conf/busybox/include/grp_.h133
-rw-r--r--i/pc104/initrd/conf/busybox/include/inet_common.h25
-rw-r--r--i/pc104/initrd/conf/busybox/include/libbb.h902
-rw-r--r--i/pc104/initrd/conf/busybox/include/platform.h311
-rw-r--r--i/pc104/initrd/conf/busybox/include/pwd_.h122
-rw-r--r--i/pc104/initrd/conf/busybox/include/shadow_.h114
-rw-r--r--i/pc104/initrd/conf/busybox/include/unarchive.h118
-rw-r--r--i/pc104/initrd/conf/busybox/include/usage.h3688
-rw-r--r--i/pc104/initrd/conf/busybox/include/xatonum.h157
-rw-r--r--i/pc104/initrd/conf/busybox/include/xregex.h18
-rw-r--r--i/pc104/initrd/conf/busybox/init/Config.in89
-rw-r--r--i/pc104/initrd/conf/busybox/init/Kbuild10
-rw-r--r--i/pc104/initrd/conf/busybox/init/halt.c59
-rw-r--r--i/pc104/initrd/conf/busybox/init/init.c1043
-rw-r--r--i/pc104/initrd/conf/busybox/init/mesg.c46
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/Config.in88
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/Kbuild120
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/README11
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/ask_confirmation.c36
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/bb_askpass.c75
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/bb_do_delay.c24
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/bb_pwd.c129
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/bb_strtonum.c156
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/change_identity.c51
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/chomp.c19
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/compare_string_array.c37
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/concat_path_file.c27
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/concat_subpath_file.c23
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/copy_file.c343
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/copyfd.c106
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/correct_password.c69
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/crc32.c39
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/create_icmp6_socket.c37
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/create_icmp_socket.c35
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/default_error_retval.c19
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/device_open.c30
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/dump.c805
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/error_msg.c23
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/error_msg_and_die.c33
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/execable.c71
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/fclose_nonstdin.c23
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/fflush_stdout_and_exit.c21
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/fgets_str.c53
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/find_mount_point.c53
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/find_pid_by_name.c55
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/find_root_device.c34
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/full_write.c36
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/get_console.c72
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/get_last_path_component.c32
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/get_line_from_file.c69
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/getopt32.c531
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/herror_msg.c22
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/herror_msg_and_die.c23
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/human_readable.c89
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/inet_common.c235
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/info_msg.c23
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/inode_hash.c90
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/isdirectory.c39
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/kernel_version.c37
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/last_char_is.c24
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/lineedit.c1801
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/llist.c108
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/login.c105
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/loop.c149
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/make_directory.c104
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/makedev.c23
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/md5.c450
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/messages.c61
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/mode_string.c128
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/mtab.c52
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/mtab_file.c17
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/obscure.c170
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/parse_mode.c148
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/perror_msg.c23
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/perror_msg_and_die.c24
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/perror_nomsg.c21
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/perror_nomsg_and_die.c21
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/process_escape_sequence.c89
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/procps.c249
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/pw_encrypt.c29
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/read.c137
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/recursive_action.c126
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/remove_file.c111
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/restricted_shell.c46
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/run_shell.c101
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/safe_strncpy.c21
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/safe_write.c26
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/selinux_common.c40
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/setup_environment.c83
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/sha1.c178
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/simplify_path.c50
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/skip_whitespace.c25
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/speed_table.c117
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/trim.c31
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/u_signal_names.c57
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/uuencode.c63
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/vdprintf.c21
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/verror_msg.c46
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/vfork_daemon_rexec.c63
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/vherror_msg.c15
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/vinfo_msg.c26
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/vperror_msg.c15
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/warn_ignoring_args.c17
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/wfopen.c20
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/wfopen_input.c31
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/xatonum.c70
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/xatonum_template.c185
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/xconnect.c330
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/xfuncs.c616
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/xgetcwd.c44
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/xgethostbyname.c19
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/xreadlink.c48
-rw-r--r--i/pc104/initrd/conf/busybox/libbb/xregcomp.c32
-rw-r--r--i/pc104/initrd/conf/busybox/libpwdgrp/Kbuild7
-rw-r--r--i/pc104/initrd/conf/busybox/libpwdgrp/pwd_grp.c997
-rw-r--r--i/pc104/initrd/conf/busybox/libpwdgrp/pwd_grp_internal.c62
-rw-r--r--i/pc104/initrd/conf/busybox/libpwdgrp/uidgid_get.c133
-rw-r--r--i/pc104/initrd/conf/busybox/loginutils/Config.in193
-rw-r--r--i/pc104/initrd/conf/busybox/loginutils/Kbuild17
-rw-r--r--i/pc104/initrd/conf/busybox/loginutils/addgroup.c111
-rw-r--r--i/pc104/initrd/conf/busybox/loginutils/adduser.c196
-rw-r--r--i/pc104/initrd/conf/busybox/loginutils/deluser.c83
-rw-r--r--i/pc104/initrd/conf/busybox/loginutils/getty.c856
-rw-r--r--i/pc104/initrd/conf/busybox/loginutils/login.c403
-rw-r--r--i/pc104/initrd/conf/busybox/loginutils/passwd.c347
-rw-r--r--i/pc104/initrd/conf/busybox/loginutils/su.c103
-rw-r--r--i/pc104/initrd/conf/busybox/loginutils/sulogin.c126
-rw-r--r--i/pc104/initrd/conf/busybox/loginutils/vlock.c119
-rw-r--r--i/pc104/initrd/conf/busybox/miscutils/Config.in387
-rw-r--r--i/pc104/initrd/conf/busybox/miscutils/Kbuild31
-rw-r--r--i/pc104/initrd/conf/busybox/miscutils/adjtimex.c118
-rw-r--r--i/pc104/initrd/conf/busybox/miscutils/bbconfig.c12
-rw-r--r--i/pc104/initrd/conf/busybox/miscutils/chrt.c124
-rw-r--r--i/pc104/initrd/conf/busybox/miscutils/crond.c1017
-rw-r--r--i/pc104/initrd/conf/busybox/miscutils/crontab.c343
-rw-r--r--i/pc104/initrd/conf/busybox/miscutils/dc.c232
-rw-r--r--i/pc104/initrd/conf/busybox/miscutils/devfsd.c2030
-rw-r--r--i/pc104/initrd/conf/busybox/miscutils/eject.c61
-rw-r--r--i/pc104/initrd/conf/busybox/miscutils/hdparm.c2048
-rw-r--r--i/pc104/initrd/conf/busybox/miscutils/last.c92
-rw-r--r--i/pc104/initrd/conf/busybox/miscutils/less.c1296
-rw-r--r--i/pc104/initrd/conf/busybox/miscutils/makedevs.c232
-rw-r--r--i/pc104/initrd/conf/busybox/miscutils/mountpoint.c66
-rw-r--r--i/pc104/initrd/conf/busybox/miscutils/mt.c121
-rw-r--r--i/pc104/initrd/conf/busybox/miscutils/nmeter.c849
-rw-r--r--i/pc104/initrd/conf/busybox/miscutils/raidautorun.c27
-rw-r--r--i/pc104/initrd/conf/busybox/miscutils/readahead.c35
-rw-r--r--i/pc104/initrd/conf/busybox/miscutils/runlevel.c43
-rw-r--r--i/pc104/initrd/conf/busybox/miscutils/rx.c290
-rw-r--r--i/pc104/initrd/conf/busybox/miscutils/setsid.c42
-rw-r--r--i/pc104/initrd/conf/busybox/miscutils/strings.c89
-rw-r--r--i/pc104/initrd/conf/busybox/miscutils/taskset.c99
-rw-r--r--i/pc104/initrd/conf/busybox/miscutils/time.c474
-rw-r--r--i/pc104/initrd/conf/busybox/miscutils/watchdog.c66
-rw-r--r--i/pc104/initrd/conf/busybox/modutils/Config.in158
-rw-r--r--i/pc104/initrd/conf/busybox/modutils/Kbuild11
-rw-r--r--i/pc104/initrd/conf/busybox/modutils/insmod.c4315
-rw-r--r--i/pc104/initrd/conf/busybox/modutils/lsmod.c192
-rw-r--r--i/pc104/initrd/conf/busybox/modutils/modprobe.c896
-rw-r--r--i/pc104/initrd/conf/busybox/modutils/rmmod.c97
-rw-r--r--i/pc104/initrd/conf/busybox/networking/Config.in719
-rw-r--r--i/pc104/initrd/conf/busybox/networking/Kbuild40
-rw-r--r--i/pc104/initrd/conf/busybox/networking/arp.c496
-rw-r--r--i/pc104/initrd/conf/busybox/networking/arping.c417
-rw-r--r--i/pc104/initrd/conf/busybox/networking/dnsd.c426
-rw-r--r--i/pc104/initrd/conf/busybox/networking/ether-wake.c284
-rw-r--r--i/pc104/initrd/conf/busybox/networking/ftpgetput.c359
-rw-r--r--i/pc104/initrd/conf/busybox/networking/hostname.c101
-rw-r--r--i/pc104/initrd/conf/busybox/networking/httpd.c2049
-rw-r--r--i/pc104/initrd/conf/busybox/networking/httpd_index_cgi_example50
-rw-r--r--i/pc104/initrd/conf/busybox/networking/ifconfig.c546
-rw-r--r--i/pc104/initrd/conf/busybox/networking/ifupdown.c1273
-rw-r--r--i/pc104/initrd/conf/busybox/networking/inetd.c1791
-rw-r--r--i/pc104/initrd/conf/busybox/networking/interface.c1195
-rw-r--r--i/pc104/initrd/conf/busybox/networking/ip.c50
-rw-r--r--i/pc104/initrd/conf/busybox/networking/ipaddr.c26
-rw-r--r--i/pc104/initrd/conf/busybox/networking/ipcalc.c195
-rw-r--r--i/pc104/initrd/conf/busybox/networking/iplink.c26
-rw-r--r--i/pc104/initrd/conf/busybox/networking/iproute.c26
-rw-r--r--i/pc104/initrd/conf/busybox/networking/iprule.c26
-rw-r--r--i/pc104/initrd/conf/busybox/networking/iptunnel.c26
-rw-r--r--i/pc104/initrd/conf/busybox/networking/isrv.c352
-rw-r--r--i/pc104/initrd/conf/busybox/networking/isrv.h33
-rw-r--r--i/pc104/initrd/conf/busybox/networking/isrv_identd.c144
-rw-r--r--i/pc104/initrd/conf/busybox/networking/libiproute/Kbuild66
-rw-r--r--i/pc104/initrd/conf/busybox/networking/libiproute/ip_common.h35
-rw-r--r--i/pc104/initrd/conf/busybox/networking/libiproute/ip_parse_common_args.c79
-rw-r--r--i/pc104/initrd/conf/busybox/networking/libiproute/ipaddress.c824
-rw-r--r--i/pc104/initrd/conf/busybox/networking/libiproute/iplink.c350
-rw-r--r--i/pc104/initrd/conf/busybox/networking/libiproute/iproute.c878
-rw-r--r--i/pc104/initrd/conf/busybox/networking/libiproute/iprule.c334
-rw-r--r--i/pc104/initrd/conf/busybox/networking/libiproute/iptunnel.c547
-rw-r--r--i/pc104/initrd/conf/busybox/networking/libiproute/libnetlink.c396
-rw-r--r--i/pc104/initrd/conf/busybox/networking/libiproute/libnetlink.h43
-rw-r--r--i/pc104/initrd/conf/busybox/networking/libiproute/ll_addr.c85
-rw-r--r--i/pc104/initrd/conf/busybox/networking/libiproute/ll_map.c186
-rw-r--r--i/pc104/initrd/conf/busybox/networking/libiproute/ll_map.h13
-rw-r--r--i/pc104/initrd/conf/busybox/networking/libiproute/ll_proto.c121
-rw-r--r--i/pc104/initrd/conf/busybox/networking/libiproute/ll_types.c117
-rw-r--r--i/pc104/initrd/conf/busybox/networking/libiproute/rt_names.c365
-rw-r--r--i/pc104/initrd/conf/busybox/networking/libiproute/rt_names.h28
-rw-r--r--i/pc104/initrd/conf/busybox/networking/libiproute/rtm_map.c110
-rw-r--r--i/pc104/initrd/conf/busybox/networking/libiproute/rtm_map.h11
-rw-r--r--i/pc104/initrd/conf/busybox/networking/libiproute/utils.c330
-rw-r--r--i/pc104/initrd/conf/busybox/networking/libiproute/utils.h91
-rw-r--r--i/pc104/initrd/conf/busybox/networking/nameif.c171
-rw-r--r--i/pc104/initrd/conf/busybox/networking/nc.c197
-rw-r--r--i/pc104/initrd/conf/busybox/networking/netstat.c596
-rw-r--r--i/pc104/initrd/conf/busybox/networking/nslookup.c155
-rw-r--r--i/pc104/initrd/conf/busybox/networking/ping.c752
-rw-r--r--i/pc104/initrd/conf/busybox/networking/route.c697
-rw-r--r--i/pc104/initrd/conf/busybox/networking/telnet.c665
-rw-r--r--i/pc104/initrd/conf/busybox/networking/telnetd.c573
-rw-r--r--i/pc104/initrd/conf/busybox/networking/tftp.c518
-rw-r--r--i/pc104/initrd/conf/busybox/networking/traceroute.c1342
-rw-r--r--i/pc104/initrd/conf/busybox/networking/udhcp/Config.in75
-rw-r--r--i/pc104/initrd/conf/busybox/networking/udhcp/Kbuild19
-rw-r--r--i/pc104/initrd/conf/busybox/networking/udhcp/arpping.c114
-rw-r--r--i/pc104/initrd/conf/busybox/networking/udhcp/clientpacket.c224
-rw-r--r--i/pc104/initrd/conf/busybox/networking/udhcp/clientsocket.c51
-rw-r--r--i/pc104/initrd/conf/busybox/networking/udhcp/common.c61
-rw-r--r--i/pc104/initrd/conf/busybox/networking/udhcp/common.h104
-rw-r--r--i/pc104/initrd/conf/busybox/networking/udhcp/dhcpc.c509
-rw-r--r--i/pc104/initrd/conf/busybox/networking/udhcp/dhcpc.h50
-rw-r--r--i/pc104/initrd/conf/busybox/networking/udhcp/dhcpd.c223
-rw-r--r--i/pc104/initrd/conf/busybox/networking/udhcp/dhcpd.h190
-rw-r--r--i/pc104/initrd/conf/busybox/networking/udhcp/dhcprelay.c338
-rw-r--r--i/pc104/initrd/conf/busybox/networking/udhcp/domain_codec.c205
-rw-r--r--i/pc104/initrd/conf/busybox/networking/udhcp/dumpleases.c75
-rw-r--r--i/pc104/initrd/conf/busybox/networking/udhcp/files.c426
-rw-r--r--i/pc104/initrd/conf/busybox/networking/udhcp/leases.c145
-rw-r--r--i/pc104/initrd/conf/busybox/networking/udhcp/options.c180
-rw-r--r--i/pc104/initrd/conf/busybox/networking/udhcp/options.h44
-rw-r--r--i/pc104/initrd/conf/busybox/networking/udhcp/packet.c211
-rw-r--r--i/pc104/initrd/conf/busybox/networking/udhcp/pidfile.c60
-rw-r--r--i/pc104/initrd/conf/busybox/networking/udhcp/script.c224
-rw-r--r--i/pc104/initrd/conf/busybox/networking/udhcp/serverpacket.c261
-rw-r--r--i/pc104/initrd/conf/busybox/networking/udhcp/signalpipe.c77
-rw-r--r--i/pc104/initrd/conf/busybox/networking/udhcp/socket.c126
-rw-r--r--i/pc104/initrd/conf/busybox/networking/udhcp/static_leases.c99
-rw-r--r--i/pc104/initrd/conf/busybox/networking/vconfig.c165
-rw-r--r--i/pc104/initrd/conf/busybox/networking/wget.c808
-rw-r--r--i/pc104/initrd/conf/busybox/networking/zcip.c548
-rw-r--r--i/pc104/initrd/conf/busybox/procps/Config.in121
-rw-r--r--i/pc104/initrd/conf/busybox/procps/Kbuild16
-rw-r--r--i/pc104/initrd/conf/busybox/procps/free.c69
-rw-r--r--i/pc104/initrd/conf/busybox/procps/fuser.c368
-rw-r--r--i/pc104/initrd/conf/busybox/procps/kill.c156
-rw-r--r--i/pc104/initrd/conf/busybox/procps/pidof.c91
-rw-r--r--i/pc104/initrd/conf/busybox/procps/ps.c388
-rw-r--r--i/pc104/initrd/conf/busybox/procps/ps.posix175
-rw-r--r--i/pc104/initrd/conf/busybox/procps/renice.c127
-rw-r--r--i/pc104/initrd/conf/busybox/procps/sysctl.c327
-rw-r--r--i/pc104/initrd/conf/busybox/procps/top.c570
-rw-r--r--i/pc104/initrd/conf/busybox/procps/uptime.c60
-rw-r--r--i/pc104/initrd/conf/busybox/runit/Config.in66
-rw-r--r--i/pc104/initrd/conf/busybox/runit/Kbuild17
-rw-r--r--i/pc104/initrd/conf/busybox/runit/chpst.c374
-rw-r--r--i/pc104/initrd/conf/busybox/runit/runit_lib.c500
-rw-r--r--i/pc104/initrd/conf/busybox/runit/runit_lib.h160
-rw-r--r--i/pc104/initrd/conf/busybox/runit/runsv.c633
-rw-r--r--i/pc104/initrd/conf/busybox/runit/runsvdir.c334
-rw-r--r--i/pc104/initrd/conf/busybox/runit/sv.c590
-rw-r--r--i/pc104/initrd/conf/busybox/runit/svlogd.c960
-rw-r--r--i/pc104/initrd/conf/busybox/scripts/Kbuild7
-rw-r--r--i/pc104/initrd/conf/busybox/scripts/Kbuild.include154
-rw-r--r--i/pc104/initrd/conf/busybox/scripts/Makefile.IMA134
-rw-r--r--i/pc104/initrd/conf/busybox/scripts/Makefile.build338
-rw-r--r--i/pc104/initrd/conf/busybox/scripts/Makefile.clean102
-rw-r--r--i/pc104/initrd/conf/busybox/scripts/Makefile.host156
-rw-r--r--i/pc104/initrd/conf/busybox/scripts/Makefile.lib165
-rw-r--r--i/pc104/initrd/conf/busybox/scripts/basic/Makefile18
-rw-r--r--i/pc104/initrd/conf/busybox/scripts/basic/docproc.c398
-rw-r--r--i/pc104/initrd/conf/busybox/scripts/basic/fixdep.c404
-rw-r--r--i/pc104/initrd/conf/busybox/scripts/basic/split-include.c226
-rwxr-xr-xi/pc104/initrd/conf/busybox/scripts/bloat-o-meter65
-rwxr-xr-xi/pc104/initrd/conf/busybox/scripts/checkhelp.awk40
-rw-r--r--i/pc104/initrd/conf/busybox/scripts/defconfig691
-rwxr-xr-xi/pc104/initrd/conf/busybox/scripts/gcc-version.sh12
-rwxr-xr-xi/pc104/initrd/conf/busybox/scripts/individual129
-rw-r--r--i/pc104/initrd/conf/busybox/scripts/kconfig/Makefile254
-rw-r--r--i/pc104/initrd/conf/busybox/scripts/kconfig/POTFILES.in5
-rw-r--r--i/pc104/initrd/conf/busybox/scripts/kconfig/conf.c612
-rw-r--r--i/pc104/initrd/conf/busybox/scripts/kconfig/confdata.c571
-rw-r--r--i/pc104/initrd/conf/busybox/scripts/kconfig/expr.c1099
-rw-r--r--i/pc104/initrd/conf/busybox/scripts/kconfig/expr.h194
-rw-r--r--i/pc104/initrd/conf/busybox/scripts/kconfig/gconf.c1645
-rw-r--r--i/pc104/initrd/conf/busybox/scripts/kconfig/gconf.glade648
-rw-r--r--i/pc104/initrd/conf/busybox/scripts/kconfig/images.c326
-rw-r--r--i/pc104/initrd/conf/busybox/scripts/kconfig/kconfig_load.c35
-rw-r--r--i/pc104/initrd/conf/busybox/scripts/kconfig/kxgettext.c227
-rw-r--r--i/pc104/initrd/conf/busybox/scripts/kconfig/lex.zconf.c_shipped2317
-rw-r--r--i/pc104/initrd/conf/busybox/scripts/kconfig/lkc.h147
-rw-r--r--i/pc104/initrd/conf/busybox/scripts/kconfig/lkc_proto.h41
-rw-r--r--i/pc104/initrd/conf/busybox/scripts/kconfig/lxdialog/BIG.FAT.WARNING4
-rw-r--r--i/pc104/initrd/conf/busybox/scripts/kconfig/lxdialog/Makefile21
-rw-r--r--i/pc104/initrd/conf/busybox/scripts/kconfig/lxdialog/check-lxdialog.sh84
-rw-r--r--i/pc104/initrd/conf/busybox/scripts/kconfig/lxdialog/checklist.c333
-rw-r--r--i/pc104/initrd/conf/busybox/scripts/kconfig/lxdialog/colors.h154
-rw-r--r--i/pc104/initrd/conf/busybox/scripts/kconfig/lxdialog/dialog.h177
-rw-r--r--i/pc104/initrd/conf/busybox/scripts/kconfig/lxdialog/inputbox.c224
-rw-r--r--i/pc104/initrd/conf/busybox/scripts/kconfig/lxdialog/lxdialog.c204
-rw-r--r--i/pc104/initrd/conf/busybox/scripts/kconfig/lxdialog/menubox.c426
-rw-r--r--i/pc104/initrd/conf/busybox/scripts/kconfig/lxdialog/msgbox.c71
-rw-r--r--i/pc104/initrd/conf/busybox/scripts/kconfig/lxdialog/textbox.c533
-rw-r--r--i/pc104/initrd/conf/busybox/scripts/kconfig/lxdialog/util.c362
-rw-r--r--i/pc104/initrd/conf/busybox/scripts/kconfig/lxdialog/yesno.c102
-rw-r--r--i/pc104/initrd/conf/busybox/scripts/kconfig/mconf.c1098
-rw-r--r--i/pc104/initrd/conf/busybox/scripts/kconfig/menu.c397
-rw-r--r--i/pc104/initrd/conf/busybox/scripts/kconfig/qconf.cc1426
-rw-r--r--i/pc104/initrd/conf/busybox/scripts/kconfig/qconf.h263
-rw-r--r--i/pc104/initrd/conf/busybox/scripts/kconfig/symbol.c882
-rw-r--r--i/pc104/initrd/conf/busybox/scripts/kconfig/util.c109
-rw-r--r--i/pc104/initrd/conf/busybox/scripts/kconfig/zconf.gperf43
-rw-r--r--i/pc104/initrd/conf/busybox/scripts/kconfig/zconf.hash.c_shipped231
-rw-r--r--i/pc104/initrd/conf/busybox/scripts/kconfig/zconf.l350
-rw-r--r--i/pc104/initrd/conf/busybox/scripts/kconfig/zconf.tab.c_shipped2173
-rw-r--r--i/pc104/initrd/conf/busybox/scripts/kconfig/zconf.y681
-rwxr-xr-xi/pc104/initrd/conf/busybox/scripts/mkconfigs52
-rwxr-xr-xi/pc104/initrd/conf/busybox/scripts/mkmakefile36
-rwxr-xr-xi/pc104/initrd/conf/busybox/scripts/objsizes19
-rwxr-xr-xi/pc104/initrd/conf/busybox/scripts/showasm21
-rwxr-xr-xi/pc104/initrd/conf/busybox/scripts/trylink18
-rw-r--r--i/pc104/initrd/conf/busybox/selinux/Config.in75
-rw-r--r--i/pc104/initrd/conf/busybox/selinux/Kbuild15
-rw-r--r--i/pc104/initrd/conf/busybox/selinux/chcon.c175
-rw-r--r--i/pc104/initrd/conf/busybox/selinux/getenforce.c34
-rw-r--r--i/pc104/initrd/conf/busybox/selinux/getsebool.c66
-rw-r--r--i/pc104/initrd/conf/busybox/selinux/matchpathcon.c86
-rw-r--r--i/pc104/initrd/conf/busybox/selinux/runcon.c137
-rw-r--r--i/pc104/initrd/conf/busybox/selinux/selinuxenabled.c14
-rw-r--r--i/pc104/initrd/conf/busybox/selinux/setenforce.c42
-rw-r--r--i/pc104/initrd/conf/busybox/shell/Config.in243
-rw-r--r--i/pc104/initrd/conf/busybox/shell/Kbuild11
-rw-r--r--i/pc104/initrd/conf/busybox/shell/ash.c13003
-rw-r--r--i/pc104/initrd/conf/busybox/shell/ash_test/ash-alias/alias.right4
-rwxr-xr-xi/pc104/initrd/conf/busybox/shell/ash_test/ash-alias/alias.tests37
-rw-r--r--i/pc104/initrd/conf/busybox/shell/ash_test/ash-arith/README.ash1
-rw-r--r--i/pc104/initrd/conf/busybox/shell/ash_test/ash-arith/arith-for.right74
-rwxr-xr-xi/pc104/initrd/conf/busybox/shell/ash_test/ash-arith/arith-for.testsx94
-rw-r--r--i/pc104/initrd/conf/busybox/shell/ash_test/ash-arith/arith.right138
-rwxr-xr-xi/pc104/initrd/conf/busybox/shell/ash_test/ash-arith/arith.tests302
-rwxr-xr-xi/pc104/initrd/conf/busybox/shell/ash_test/ash-arith/arith1.sub40
-rwxr-xr-xi/pc104/initrd/conf/busybox/shell/ash_test/ash-arith/arith2.sub57
-rw-r--r--i/pc104/initrd/conf/busybox/shell/ash_test/ash-heredoc/heredoc.right21
-rwxr-xr-xi/pc104/initrd/conf/busybox/shell/ash_test/ash-heredoc/heredoc.tests94
-rw-r--r--i/pc104/initrd/conf/busybox/shell/ash_test/ash-invert/invert.right10
-rwxr-xr-xi/pc104/initrd/conf/busybox/shell/ash_test/ash-invert/invert.tests19
-rw-r--r--i/pc104/initrd/conf/busybox/shell/ash_test/printenv.c67
-rw-r--r--i/pc104/initrd/conf/busybox/shell/ash_test/recho.c63
-rwxr-xr-xi/pc104/initrd/conf/busybox/shell/ash_test/run-all62
-rw-r--r--i/pc104/initrd/conf/busybox/shell/ash_test/zecho.c39
-rw-r--r--i/pc104/initrd/conf/busybox/shell/bbsh.c223
-rw-r--r--i/pc104/initrd/conf/busybox/shell/hush.c2928
-rw-r--r--i/pc104/initrd/conf/busybox/shell/lash.c1582
-rw-r--r--i/pc104/initrd/conf/busybox/shell/msh.c5406
-rw-r--r--i/pc104/initrd/conf/busybox/sysklogd/Config.in111
-rw-r--r--i/pc104/initrd/conf/busybox/sysklogd/Kbuild11
-rw-r--r--i/pc104/initrd/conf/busybox/sysklogd/klogd.c123
-rw-r--r--i/pc104/initrd/conf/busybox/sysklogd/logger.c168
-rw-r--r--i/pc104/initrd/conf/busybox/sysklogd/logread.c144
-rw-r--r--i/pc104/initrd/conf/busybox/sysklogd/syslogd.c654
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/README34
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/TODO26
-rwxr-xr-xi/pc104/initrd/conf/busybox/testsuite/all_sourcecode.tests78
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/basename/basename-does-not-remove-identical-extension1
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/basename/basename-works2
-rwxr-xr-xi/pc104/initrd/conf/busybox/testsuite/bunzip2.tests84
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/bunzip2/bunzip2-reads-from-standard-input2
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/bunzip2/bunzip2-removes-compressed-file3
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/bunzip2/bzcat-does-not-remove-compressed-file3
-rwxr-xr-xi/pc104/initrd/conf/busybox/testsuite/busybox.tests46
-rwxr-xr-xi/pc104/initrd/conf/busybox/testsuite/bzcat.tests49
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/cat/cat-prints-a-file3
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/cat/cat-prints-a-file-and-standard-input7
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/cmp/cmp-detects-difference9
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/cp/cp-a-files-to-dir14
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/cp/cp-a-preserves-links5
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/cp/cp-copies-empty-file3
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/cp/cp-copies-large-file3
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/cp/cp-copies-small-file3
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/cp/cp-d-files-to-dir11
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/cp/cp-dir-create-dir4
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/cp/cp-dir-existing-dir5
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/cp/cp-does-not-copy-unreadable-file6
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/cp/cp-files-to-dir11
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/cp/cp-follows-links4
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/cp/cp-preserves-hard-links6
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/cp/cp-preserves-links5
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/cp/cp-preserves-source-file3
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/cut/cut-cuts-a-character1
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/cut/cut-cuts-a-closed-range1
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/cut/cut-cuts-a-field1
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/cut/cut-cuts-an-open-range1
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/cut/cut-cuts-an-unclosed-range1
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/date/date-R-works2
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/date/date-format-works1
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/date/date-u-works2
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/date/date-works2
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/dd/dd-accepts-if2
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/dd/dd-accepts-of2
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/dd/dd-copies-from-standard-input-to-standard-output1
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/dd/dd-prints-count-to-standard-error2
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/dirname/dirname-handles-absolute-path1
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/dirname/dirname-handles-empty-path1
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/dirname/dirname-handles-multiple-slashes1
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/dirname/dirname-handles-relative-path1
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/dirname/dirname-handles-root1
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/dirname/dirname-handles-single-component1
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/dirname/dirname-works2
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/du/du-h-works4
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/du/du-k-works4
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/du/du-l-works4
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/du/du-m-works4
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/du/du-s-works4
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/du/du-works4
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/echo/echo-does-not-print-newline1
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/echo/echo-prints-argument1
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/echo/echo-prints-arguments1
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/echo/echo-prints-newline1
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/expr/expr-works59
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/false/false-is-silent1
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/false/false-returns-failure1
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/find/find-supports-minus-xdev1
-rwxr-xr-xi/pc104/initrd/conf/busybox/testsuite/grep.tests84
-rwxr-xr-xi/pc104/initrd/conf/busybox/testsuite/gunzip.tests3
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/gunzip/gunzip-reads-from-standard-input2
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/gzip/gzip-accepts-multiple-files3
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/gzip/gzip-accepts-single-minus1
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/gzip/gzip-removes-original-file3
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/head/head-n-works4
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/head/head-works4
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/hostid/hostid-works2
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/hostname/hostname-d-works2
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/hostname/hostname-i-works2
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/hostname/hostname-s-works1
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/hostname/hostname-works1
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/id/id-g-works1
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/id/id-u-works1
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/id/id-un-works1
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/id/id-ur-works1
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/ln/ln-creates-hard-links4
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/ln/ln-creates-soft-links4
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/ln/ln-force-creates-hard-links5
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/ln/ln-force-creates-soft-links5
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/ln/ln-preserves-hard-links8
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/ln/ln-preserves-soft-links9
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/ls/ls-1-works4
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/ls/ls-h-works4
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/ls/ls-l-works4
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/ls/ls-s-works4
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/md5sum/md5sum-verifies-non-binary-file3
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/mkdir/mkdir-makes-a-directory2
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/mkdir/mkdir-makes-parent-directories2
-rwxr-xr-xi/pc104/initrd/conf/busybox/testsuite/mount.testroot183
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/msh/msh-supports-underscores-in-variable-names1
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/mv/mv-files-to-dir16
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/mv/mv-follows-links4
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/mv/mv-moves-empty-file4
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/mv/mv-moves-file3
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/mv/mv-moves-hardlinks4
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/mv/mv-moves-large-file4
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/mv/mv-moves-small-file4
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/mv/mv-moves-symlinks6
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/mv/mv-moves-unreadable-files5
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/mv/mv-preserves-hard-links6
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/mv/mv-preserves-links5
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/mv/mv-refuses-mv-dir-to-subdir23
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/mv/mv-removes-source-file4
-rwxr-xr-xi/pc104/initrd/conf/busybox/testsuite/pidof.tests29
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/pwd/pwd-prints-working-directory1
-rwxr-xr-xi/pc104/initrd/conf/busybox/testsuite/readlink.tests32
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/rm/rm-removes-file3
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/rmdir/rmdir-removes-parent-directories3
-rwxr-xr-xi/pc104/initrd/conf/busybox/testsuite/runtest150
-rwxr-xr-xi/pc104/initrd/conf/busybox/testsuite/sed.tests203
-rwxr-xr-xi/pc104/initrd/conf/busybox/testsuite/seq.tests36
-rwxr-xr-xi/pc104/initrd/conf/busybox/testsuite/sort.tests113
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/strings/strings-works-like-GNU9
-rwxr-xr-xi/pc104/initrd/conf/busybox/testsuite/sum.tests24
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/tail/tail-n-works4
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/tail/tail-works4
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/tar/tar-archives-multiple-files6
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/tar/tar-complains-about-missing-file3
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/tar/tar-demands-at-least-one-ctx1
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/tar/tar-demands-at-most-one-ctx1
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/tar/tar-extracts-all-subdirs12
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/tar/tar-extracts-file5
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/tar/tar-extracts-from-standard-input5
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/tar/tar-extracts-multiple-files6
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/tar/tar-extracts-to-standard-output3
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/tar/tar-handles-cz-options5
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/tar/tar-handles-empty-include-and-non-empty-exclude-list6
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/tar/tar-handles-exclude-and-extract-lists8
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/tar/tar-handles-multiple-X-options10
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/tar/tar-handles-nested-exclude9
-rwxr-xr-xi/pc104/initrd/conf/busybox/testsuite/taskset.tests17
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/tee/tee-appends-input5
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/tee/tee-tees-input3
-rwxr-xr-xi/pc104/initrd/conf/busybox/testsuite/testing.sh154
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/touch/touch-creates-file2
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/touch/touch-does-not-create-file2
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/touch/touch-touches-files-after-non-existent-file3
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/tr/tr-d-works4
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/tr/tr-non-gnu1
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/tr/tr-works24
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/true/true-is-silent1
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/true/true-returns-success1
-rwxr-xr-xi/pc104/initrd/conf/busybox/testsuite/umlwrapper.sh20
-rwxr-xr-xi/pc104/initrd/conf/busybox/testsuite/uniq.tests70
-rwxr-xr-xi/pc104/initrd/conf/busybox/testsuite/unzip.tests38
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/uptime/uptime-works2
-rwxr-xr-xi/pc104/initrd/conf/busybox/testsuite/uuencode.tests28
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/wc/wc-counts-all2
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/wc/wc-counts-characters1
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/wc/wc-counts-lines1
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/wc/wc-counts-words1
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/wc/wc-prints-longest-line-length1
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/wget/wget--O-overrides--P3
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/wget/wget-handles-empty-path1
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/wget/wget-retrieves-google-index2
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/wget/wget-supports--P3
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/which/which-uses-default-path4
-rw-r--r--i/pc104/initrd/conf/busybox/testsuite/xargs/xargs-works4
-rw-r--r--i/pc104/initrd/conf/busybox/util-linux/Config.in509
-rw-r--r--i/pc104/initrd/conf/busybox/util-linux/Kbuild32
-rw-r--r--i/pc104/initrd/conf/busybox/util-linux/dmesg.c54
-rw-r--r--i/pc104/initrd/conf/busybox/util-linux/fbset.c408
-rw-r--r--i/pc104/initrd/conf/busybox/util-linux/fdformat.c144
-rw-r--r--i/pc104/initrd/conf/busybox/util-linux/fdisk.c3018
-rw-r--r--i/pc104/initrd/conf/busybox/util-linux/fdisk_aix.c73
-rw-r--r--i/pc104/initrd/conf/busybox/util-linux/fdisk_osf.c1063
-rw-r--r--i/pc104/initrd/conf/busybox/util-linux/fdisk_sgi.c891
-rw-r--r--i/pc104/initrd/conf/busybox/util-linux/fdisk_sun.c730
-rw-r--r--i/pc104/initrd/conf/busybox/util-linux/freeramdisk.c35
-rw-r--r--i/pc104/initrd/conf/busybox/util-linux/fsck_minix.c1390
-rw-r--r--i/pc104/initrd/conf/busybox/util-linux/getopt.c366
-rw-r--r--i/pc104/initrd/conf/busybox/util-linux/hexdump.c102
-rw-r--r--i/pc104/initrd/conf/busybox/util-linux/hwclock.c222
-rw-r--r--i/pc104/initrd/conf/busybox/util-linux/ipcrm.c218
-rw-r--r--i/pc104/initrd/conf/busybox/util-linux/ipcs.c631
-rw-r--r--i/pc104/initrd/conf/busybox/util-linux/losetup.c65
-rw-r--r--i/pc104/initrd/conf/busybox/util-linux/mdev.c270
-rw-r--r--i/pc104/initrd/conf/busybox/util-linux/minix.h76
-rw-r--r--i/pc104/initrd/conf/busybox/util-linux/mkfs_minix.c718
-rw-r--r--i/pc104/initrd/conf/busybox/util-linux/mkswap.c48
-rw-r--r--i/pc104/initrd/conf/busybox/util-linux/more.c173
-rw-r--r--i/pc104/initrd/conf/busybox/util-linux/mount.c1742
-rw-r--r--i/pc104/initrd/conf/busybox/util-linux/pivot_root.c27
-rw-r--r--i/pc104/initrd/conf/busybox/util-linux/rdate.c71
-rw-r--r--i/pc104/initrd/conf/busybox/util-linux/readprofile.c237
-rw-r--r--i/pc104/initrd/conf/busybox/util-linux/setarch.c53
-rw-r--r--i/pc104/initrd/conf/busybox/util-linux/swaponoff.c77
-rw-r--r--i/pc104/initrd/conf/busybox/util-linux/switch_root.c123
-rw-r--r--i/pc104/initrd/conf/busybox/util-linux/umount.c152
959 files changed, 231655 insertions, 0 deletions
diff --git a/i/pc104/initrd/conf/busybox.links b/i/pc104/initrd/conf/busybox.links
new file mode 100644
index 0000000..de85a50
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox.links
@@ -0,0 +1,18 @@
+/usr/bin/[
+/usr/bin/[[
+/bin/ash
+/usr/sbin/chroot
+/bin/echo
+/sbin/halt
+/bin/hostname
+/sbin/insmod
+/bin/mount
+/usr/bin/nc
+/sbin/pivot_root
+/sbin/reboot
+/bin/sh
+/bin/sleep
+/bin/sync
+/usr/bin/test
+/sbin/udhcpc
+/bin/umount
diff --git a/i/pc104/initrd/conf/busybox/AUTHORS b/i/pc104/initrd/conf/busybox/AUTHORS
new file mode 100644
index 0000000..8f6498b
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/AUTHORS
@@ -0,0 +1,173 @@
+List of the authors of code contained in BusyBox.
+
+If you have code in BusyBox, you should be listed here. If you should be
+listed, or the description of what you have done needs more detail, or is
+incorrect, _please_ let me know.
+
+ -Erik
+
+-----------
+
+Peter Willis <psyphreak@phreaker.net>
+ eject
+
+Emanuele Aina <emanuele.aina@tiscali.it>
+ run-parts
+
+Erik Andersen <andersen@codepoet.org>
+ Tons of new stuff, major rewrite of most of the
+ core apps, tons of new apps as noted in header files.
+ Lots of tedious effort writing these boring docs that
+ nobody is going to actually read.
+
+Laurence Anderson <l.d.anderson@warwick.ac.uk>
+ rpm2cpio, unzip, get_header_cpio, read_gz interface, rpm
+
+Jeff Angielski <jeff@theptrgroup.com>
+ ftpput, ftpget
+
+Enrik Berkhan <Enrik.Berkhan@inka.de>
+ setconsole
+
+Jim Bauer <jfbauer@nfr.com>
+ modprobe shell dependency
+
+Edward Betts <edward@debian.org>
+ expr, hostid, logname, whoami
+
+John Beppu <beppu@codepoet.org>
+ du, nslookup, sort
+
+David Brownell <dbrownell@users.sourceforge.net>
+ zcip
+
+Brian Candler <B.Candler@pobox.com>
+ tiny-ls(ls)
+
+Randolph Chung <tausq@debian.org>
+ fbset, ping, hostname
+
+Dave Cinege <dcinege@psychosis.com>
+ more(v2), makedevs, dutmp, modularization, auto links file,
+ various fixes, Linux Router Project maintenance
+
+Jordan Crouse <jordan@cosmicpenguin.net>
+ ipcalc
+
+Magnus Damm <damm@opensource.se>
+ tftp client
+ insmod powerpc support
+
+Larry Doolittle <ldoolitt@recycle.lbl.gov>
+ pristine source directory compilation, lots of patches and fixes.
+
+Glenn Engel <glenne@engel.org>
+ httpd
+
+Gennady Feldman <gfeldman@gena01.com>
+ Sysklogd (single threaded syslogd, IPC Circular buffer support,
+ logread), various fixes.
+
+Robert Griebl <sandman@handhelds.org>
+ modprobe, hwclock, suid/sgid handling, tinylogin integration
+ many bugfixes and enhancements
+
+Karl M. Hegbloom <karlheg@debian.org>
+ cp_mv.c, the test suite, various fixes to utility.c, &c.
+
+Daniel Jacobowitz <dan@debian.org>
+ mktemp.c
+
+Matt Kraai <kraai@alumni.cmu.edu>
+ documentation, bugfixes, test suite
+
+Rob Landley <rob@landley.net>
+ Became busybox maintainer in 2006.
+
+ sed (major rewrite in 2003, and I now maintain the thing)
+ bunzip2 (complete from-scratch rewrite, then mjn3 optimized the result)
+ sort (more or less from scratch rewrite in 2004, I now maintain it)
+ mount (rewrite in 2005, I maintain the new one)
+
+Stephan Linz <linz@li-pro.net>
+ ipcalc, Red Hat equivalence
+
+John Lombardo <john@deltanet.com>
+ tr
+
+Glenn McGrath <bug1@iinet.net.au>
+ Common unarchiving code and unarchiving applets, ifupdown, ftpgetput,
+ nameif, sed, patch, fold, install, uudecode.
+ Various bugfixes, review and apply numerous patches.
+
+Manuel Novoa III <mjn3@codepoet.org>
+ cat, head, mkfifo, mknod, rmdir, sleep, tee, tty, uniq, usleep, wc, yes,
+ mesg, vconfig, nice, renice,
+ make_directory, parse_mode, dirname, mode_string,
+ get_last_path_component, simplify_path, and a number trivial libbb routines
+
+ also bug fixes, partial rewrites, and size optimizations in
+ ash, basename, cal, cmp, cp, df, du, echo, env, ln, logname, md5sum, mkdir,
+ mv, realpath, rm, sort, tail, touch, uname, watch, arith, human_readable,
+ interface, dutmp, ifconfig, route
+
+Vladimir Oleynik <dzo@simtreas.ru>
+ cmdedit; bb_mkdep, xargs(current), httpd(current);
+ ports: ash, crond, fdisk (initial, unmaintained now), inetd, stty, traceroute,
+ top;
+ locale, various fixes
+ and irreconcilable critic of everything not perfect.
+
+Bruce Perens <bruce@pixar.com>
+ Original author of BusyBox in 1995, 1996. Some of his code can
+ still be found hiding here and there...
+
+Rodney Radford <rradford@mindspring.com>
+ ipcs, ipcrm
+
+Tim Riker <Tim@Rikers.org>
+ bug fixes, member of fan club
+
+Kent Robotti <robotti@metconnect.com>
+ reset, tons and tons of bug reports and patches.
+
+Chip Rosenthal <chip@unicom.com>, <crosenth@covad.com>
+ wget - Contributed by permission of Covad Communications
+
+Pavel Roskin <proski@gnu.org>
+ Lots of bugs fixes and patches.
+
+Gyepi Sam <gyepi@praxis-sw.com>
+ Remote logging feature for syslogd
+
+Rob Sullivan <cogito.ergo.cogito@gmail.com>
+ comm
+
+Linus Torvalds
+ mkswap, fsck.minix, mkfs.minix
+
+Mark Whitley <markw@codepoet.org>
+ grep, sed, cut, xargs(previous),
+ style-guide, new-applet-HOWTO, bug fixes, etc.
+
+Charles P. Wright <cpwright@villagenet.com>
+ gzip, mini-netcat(nc)
+
+Enrique Zanardi <ezanardi@ull.es>
+ tarcat (since removed), loadkmap, various fixes, Debian maintenance
+
+Tito Ragusa <farmatito@tiscali.it>
+ devfsd and size optimizations in strings, openvt, chvt, deallocvt, hdparm,
+ fdformat, lsattr, chattr, id and eject.
+
+Paul Fox <pgf@foxharp.boston.ma.us>
+ vi editing mode for ash, various other patches/fixes
+
+Roberto A. Foglietta <me@roberto.foglietta.name>
+ port: dnsd
+
+Bernhard Fischer <rep.nop@aon.at>
+ misc
+
+Mike Frysinger <vapier@gentoo.org>
+ initial e2fsprogs, printenv, setarch, sum, misc
diff --git a/i/pc104/initrd/conf/busybox/Config.in b/i/pc104/initrd/conf/busybox/Config.in
new file mode 100644
index 0000000..d15b267
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/Config.in
@@ -0,0 +1,496 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+mainmenu "BusyBox Configuration"
+
+config HAVE_DOT_CONFIG
+ bool
+ default y
+
+menu "Busybox Settings"
+
+menu "General Configuration"
+
+config NITPICK
+ bool "See lots more (probably unnecessary) configuration options."
+ default n
+ help
+ Some BusyBox applets have more configuration options than anyone
+ will ever care about. To avoid drowining people in complexity, most
+ of the applet features that can be set to a sane default value are
+ hidden, unless you hit the above switch.
+
+ This is better than to telling people to edit the busybox source
+ code, but not by much.
+
+ See http://en.wikipedia.org/wiki/Fibber_McGee_and_Molly#The_Closet
+
+ You have been warned.
+
+config DESKTOP
+ bool "Enable options for full-blown desktop systems"
+ default n
+ help
+ Enable options and features which are not essential.
+ Select this only if you plan to use busybox on full-blown
+ desktop machine with common Linux distro, not on an embedded box.
+
+choice
+ prompt "Buffer allocation policy"
+ default FEATURE_BUFFERS_USE_MALLOC
+ depends on NITPICK
+ help
+ There are 3 ways BusyBox can handle buffer allocations:
+ - Use malloc. This costs code size for the call to xmalloc.
+ - Put them on stack. For some very small machines with limited stack
+ space, this can be deadly. For most folks, this works just fine.
+ - Put them in BSS. This works beautifully for computers with a real
+ MMU (and OS support), but wastes runtime RAM for uCLinux. This
+ behavior was the only one available for BusyBox versions 0.48 and
+ earlier.
+
+config FEATURE_BUFFERS_USE_MALLOC
+ bool "Allocate with Malloc"
+
+config FEATURE_BUFFERS_GO_ON_STACK
+ bool "Allocate on the Stack"
+
+config FEATURE_BUFFERS_GO_IN_BSS
+ bool "Allocate in the .bss section"
+
+endchoice
+
+config SHOW_USAGE
+ bool "Show terse applet usage messages"
+ default y
+ help
+ All BusyBox applets will show help messages when invoked with
+ wrong arguments. You can turn off printing these terse usage
+ messages if you say no here.
+ This will save you up to 7k.
+
+config FEATURE_VERBOSE_USAGE
+ bool "Show verbose applet usage messages"
+ default n
+ select SHOW_USAGE
+ help
+ All BusyBox applets will show more verbose help messages when
+ busybox is invoked with --help. This will add a lot of text to the
+ busybox binary. In the default configuration, this will add about
+ 13k, but it can add much more depending on your configuration.
+
+config FEATURE_COMPRESS_USAGE
+ bool "Store applet usage messages in compressed form"
+ default y
+ depends on SHOW_USAGE
+ help
+ Store usage messages in compressed form, uncompress them on-the-fly
+ when <applet> --help is called.
+
+ If you have a really tiny busybox with few applets enabled (and
+ bunzip2 isn't one of them), the overhead of the decompressor might
+ be noticeable. Also, if you run executables directly from ROM
+ and have very little memory, this might not be a win. Otherwise,
+ you probably want this.
+
+config FEATURE_INSTALLER
+ bool "Support --install [-s] to install applet links at runtime"
+ default n
+ help
+ Enable 'busybox --install [-s]' support. This will allow you to use
+ busybox at runtime to create hard links or symlinks for all the
+ applets that are compiled into busybox. This feature requires the
+ /proc filesystem.
+
+config LOCALE_SUPPORT
+ bool "Enable locale support (system needs locale for this to work)"
+ default n
+ help
+ Enable this if your system has locale support and you would like
+ busybox to support locale settings.
+
+config GETOPT_LONG
+ bool "Enable support for --long-options"
+ default y
+ help
+ Enable this if you want busybox applets to use the gnu --long-option
+ style, in addition to single character -a -b -c style options.
+
+config FEATURE_DEVPTS
+ bool "Use the devpts filesystem for Unix98 PTYs"
+ default y
+ help
+ Enable if you want BusyBox to use Unix98 PTY support. If enabled,
+ busybox will use /dev/ptmx for the master side of the pseudoterminal
+ and /dev/pts/<number> for the slave side. Otherwise, BSD style
+ /dev/ttyp<number> will be used. To use this option, you should have
+ devpts mounted.
+
+config FEATURE_CLEAN_UP
+ bool "Clean up all memory before exiting (usually not needed)"
+ default n
+ depends on NITPICK
+ help
+ As a size optimization, busybox normally exits without explicitly
+ freeing dynamically allocated memory or closing files. This saves
+ space since the OS will clean up for us, but it can confuse debuggers
+ like valgrind, which report tons of memory and resource leaks.
+
+ Don't enable this unless you have a really good reason to clean
+ things up manually.
+
+config FEATURE_SUID
+ bool "Support for SUID/SGID handling"
+ default n
+ help
+ With this option you can install the busybox binary belonging
+ to root with the suid bit set, and it'll and it'll automatically drop
+ priviledges for applets that don't need root access.
+
+ If you're really paranoid and don't want to do this, build two
+ busybox binaries with different applets in them (and the appropriate
+ symlinks pointing to each binary), and only set the suid bit on the
+ one that needs it. The applets currently marked to need the suid bit
+ are login, passwd, su, ping, traceroute, crontab, dnsd, ipcrm, ipcs,
+ and vlock.
+
+config FEATURE_SYSLOG
+ bool "Support for syslog"
+ default n
+ help
+ This option is auto-selected when you select any applet which may
+ send its output to syslog. You do not need to select it manually.
+
+config FEATURE_SUID_CONFIG
+ bool "Runtime SUID/SGID configuration via /etc/busybox.conf"
+ default n if FEATURE_SUID
+ depends on FEATURE_SUID
+ help
+ Allow the SUID / SGID state of an applet to be determined at runtime
+ by checking /etc/busybox.conf. (This is sort of a poor man's sudo.)
+ The format of this file is as follows:
+
+ <applet> = [Ssx-][Ssx-][x-] (<username>|<uid>).(<groupname>|<gid>)
+
+ An example might help:
+
+ [SUID]
+ su = ssx root.0 # applet su can be run by anyone and runs with euid=0/egid=0
+ su = ssx # exactly the same
+
+ mount = sx- root.disk # applet mount can be run by root and members of group disk
+ # and runs with euid=0
+
+ cp = --- # disable applet cp for everyone
+
+ The file has to be owned by user root, group root and has to be
+ writeable only by root:
+ (chown 0.0 /etc/busybox.conf; chmod 600 /etc/busybox.conf)
+ The busybox executable has to be owned by user root, group
+ root and has to be setuid root for this to work:
+ (chown 0.0 /bin/busybox; chmod 4755 /bin/busybox)
+
+ Robert 'sandman' Griebl has more information here:
+ <url: http://www.softforge.de/bb/suid.html >.
+
+config FEATURE_SUID_CONFIG_QUIET
+ bool "Suppress warning message if /etc/busybox.conf is not readable"
+ default y
+ depends on FEATURE_SUID_CONFIG
+ help
+ /etc/busybox.conf should be readable by the user needing the SUID, check
+ this option to avoid users to be notified about missing permissions.
+
+config FEATURE_HAVE_RPC
+ bool "RPC support"
+ default y
+ help
+ Select this if you have rpc support.
+ This automatically turns off all configuration options that rely
+ on RPC.
+
+config SELINUX
+ bool "Support NSA Security Enhanced Linux"
+ default n
+ help
+ Enable support for SELinux in applets ls, ps, and id. Also provide
+ the option of compiling in SELinux applets.
+
+ If you do not have a complete SELinux userland installed, this stuff
+ will not compile. Go visit
+ http://www.nsa.gov/selinux/index.html
+ to download the necessary stuff to allow busybox to compile with
+ this option enabled. Specifially, libselinux 1.28 or better is
+ directly required by busybox. If the installation is located in a
+ non-standard directory, provide it by invoking make as follows:
+ CFLAGS=-I<libselinux-include-path> \
+ LDFLAGS=-L<libselinux-lib-path> \
+ make
+
+ Most people will leave this set to 'N'.
+
+config FEATURE_EXEC_PREFER_APPLETS
+ bool "exec prefers applets"
+ default n
+ help
+ This is an experimental option which directs applets about to
+ call 'exec' to try and find an applicable busybox applet before
+ searching the executable path for a binary or symlink to execute.
+
+config BUSYBOX_EXEC_PATH
+ string "Path to BusyBox executable"
+ default "/proc/self/exe"
+ help
+ When Busybox applets need to run other busybox applets, BusyBox
+ sometimes needs to exec() itself. When the /proc filesystem is
+ mounted, /proc/self/exe always points to the currently running
+ executable. If you haven't got /proc, set this to wherever you
+ want to run BusyBox from.
+
+endmenu
+
+menu 'Build Options'
+
+config STATIC
+ bool "Build BusyBox as a static binary (no shared libs)"
+ default n
+ help
+ If you want to build a static BusyBox binary, which does not
+ use or require any shared libraries, then enable this option.
+ This can cause BusyBox to be considerably larger, so you should
+ leave this option false unless you have a good reason (i.e.
+ your target platform does not support shared libraries, or
+ you are building an initrd which doesn't need anything but
+ BusyBox, etc).
+
+ Most people will leave this set to 'N'.
+
+config BUILD_LIBBUSYBOX
+ bool "Build shared libbusybox"
+ default n
+ help
+ Build a shared library libbusybox.so which contains all
+ libraries used inside busybox.
+
+ This is an experimental feature intended to support the upcoming
+ "make standalone" mode. Enabling it against the one big busybox
+ binary serves no purpose (and increases the size). You should
+ almost certainly say "no" to this right now.
+
+config FEATURE_FULL_LIBBUSYBOX
+ bool "Feature-complete libbusybox"
+ default n if !FEATURE_SHARED_BUSYBOX
+ depends on BUILD_LIBBUSYBOX
+ help
+ Build a libbusybox with the complete feature-set, disregarding
+ the actually selected config.
+
+ Normally, libbusybox will only contain the features which are
+ used by busybox itself. If you plan to write a separate
+ standalone application which uses libbusybox say 'Y'.
+
+ Note: libbusybox is GPL, not LGPL, and exports no stable API that
+ might act as a copyright barrier. We can and will modify the
+ exported function set between releases (even minor version number
+ changes), and happily break out-of-tree features.
+
+ Say 'N' if in doubt.
+
+config FEATURE_SHARED_BUSYBOX
+ bool "Use shared libbusybox for busybox"
+ default y if BUILD_LIBBUSYBOX
+ depends on !STATIC && BUILD_LIBBUSYBOX
+ help
+ Use libbusybox.so also for busybox itself.
+ You need to have a working dynamic linker to use this variant.
+
+config LFS
+ bool "Build with Large File Support (for accessing files > 2 GB)"
+ default n
+ select FDISK_SUPPORT_LARGE_DISKS
+ help
+ If you want to build BusyBox with large file support, then enable
+ this option. This will have no effect if your kernel or your C
+ library lacks large file support for large files. Some of the
+ programs that can benefit from large file support include dd, gzip,
+ cp, mount, tar, and many others. If you want to access files larger
+ than 2 Gigabytes, enable this option. Otherwise, leave it set to 'N'.
+
+config BUILD_AT_ONCE
+ bool "Compile all sources at once"
+ default n
+ help
+ Normally each source-file is compiled with one invocation of
+ the compiler.
+ If you set this option, all sources are compiled at once.
+ This gives the compiler more opportunities to optimize which can
+ result in smaller and/or faster binaries.
+
+ Setting this option will consume alot of memory, e.g. if you
+ enable all applets with all features, gcc uses more than 300MB
+ RAM during compilation of busybox.
+
+ This option is most likely only beneficial for newer compilers
+ such as gcc-4.1 and above.
+
+ Say 'N' unless you know what you are doing.
+
+endmenu
+
+menu 'Debugging Options'
+
+config DEBUG
+ bool "Build BusyBox with extra Debugging symbols"
+ default n
+ help
+ Say Y here if you wish to examine BusyBox internals while applets are
+ running. This increases the size of the binary considerably, and
+ should only be used when doing development. If you are doing
+ development and want to debug BusyBox, answer Y.
+
+ Most people should answer N.
+
+config WERROR
+ bool "Abort compilation on any warning"
+ default n
+ help
+ Selecting this will add -Werror to gcc command line.
+
+ Most people should answer N.
+
+# Seems to be unused
+#config DEBUG_PESSIMIZE
+# bool "Disable compiler optimizations."
+# default n
+# depends on DEBUG
+# help
+# The compiler's optimization of source code can eliminate and reorder
+# code, resulting in an executable that's hard to understand when
+# stepping through it with a debugger. This switches it off, resulting
+# in a much bigger executable that more closely matches the source
+# code.
+
+choice
+ prompt "Additional debugging library"
+ default NO_DEBUG_LIB
+ depends on DEBUG
+ help
+ Using an additional debugging library will make BusyBox become
+ considerable larger and will cause it to run more slowly. You
+ should always leave this option disabled for production use.
+
+ dmalloc support:
+ ----------------
+ This enables compiling with dmalloc ( http://dmalloc.com/ )
+ which is an excellent public domain mem leak and malloc problem
+ detector. To enable dmalloc, before running busybox you will
+ want to properly set your environment, for example:
+ export DMALLOC_OPTIONS=debug=0x34f47d83,inter=100,log=logfile
+ The 'debug=' value is generated using the following command
+ dmalloc -p log-stats -p log-non-free -p log-bad-space -p log-elapsed-time \
+ -p check-fence -p check-heap -p check-lists -p check-blank \
+ -p check-funcs -p realloc-copy -p allow-free-null
+
+ Electric-fence support:
+ -----------------------
+ This enables compiling with Electric-fence support. Electric
+ fence is another very useful malloc debugging library which uses
+ your computer's virtual memory hardware to detect illegal memory
+ accesses. This support will make BusyBox be considerable larger
+ and run slower, so you should leave this option disabled unless
+ you are hunting a hard to find memory problem.
+
+
+config NO_DEBUG_LIB
+ bool "None"
+
+config DMALLOC
+ bool "Dmalloc"
+
+config EFENCE
+ bool "Electric-fence"
+
+endchoice
+
+config INCLUDE_SUSv2
+ bool "Enable obsolete features removed before SUSv3?"
+ default y
+ help
+ This option will enable backwards compatibility with SuSv2,
+ specifically, old-style numeric options ('command -1 <file>')
+ will be supported in head, tail, and fold. (Note: should
+ affect renice too.)
+
+endmenu
+
+menu 'Installation Options'
+
+config INSTALL_NO_USR
+ bool "Don't use /usr"
+ default n
+ help
+ Disable use of /usr. Don't activate this option if you don't know
+ that you really want this behaviour.
+
+choice
+ prompt "Applets links"
+ default INSTALL_APPLET_SYMLINKS
+ help
+ Choose how you install applets links.
+
+config INSTALL_APPLET_SYMLINKS
+ bool "as soft-links"
+ help
+ Install applets as soft-links to the busybox binary. This needs some
+ free inodes on the filesystem, but might help with filesystem
+ generators that can't cope with hard-links.
+
+config INSTALL_APPLET_HARDLINKS
+ bool "as hard-links"
+ help
+ Install applets as hard-links to the busybox binary. This might count
+ on a filesystem with few inodes.
+
+config INSTALL_APPLET_DONT
+ bool "not installed"
+ depends on FEATURE_INSTALLER || FEATURE_SH_STANDALONE_SHELL || FEATURE_EXEC_PREFER_APPLETS
+ help
+ Do not install applet links. Useful when using the -install feature
+ or a standalone shell for rescue pruposes.
+
+endchoice
+
+config PREFIX
+ string "BusyBox installation prefix"
+ default "./_install"
+ help
+ Define your directory to install BusyBox files/subdirs in.
+
+endmenu
+
+source libbb/Config.in
+
+endmenu
+
+comment "Applets"
+
+source archival/Config.in
+source coreutils/Config.in
+source console-tools/Config.in
+source debianutils/Config.in
+source editors/Config.in
+source findutils/Config.in
+source init/Config.in
+source loginutils/Config.in
+source e2fsprogs/Config.in
+source modutils/Config.in
+source util-linux/Config.in
+source miscutils/Config.in
+source networking/Config.in
+source procps/Config.in
+source shell/Config.in
+source sysklogd/Config.in
+source runit/Config.in
+source selinux/Config.in
diff --git a/i/pc104/initrd/conf/busybox/INSTALL b/i/pc104/initrd/conf/busybox/INSTALL
new file mode 100644
index 0000000..a7902ab
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/INSTALL
@@ -0,0 +1,125 @@
+Building:
+=========
+
+The BusyBox build process is similar to the Linux kernel build:
+
+ make menuconfig # This creates a file called ".config"
+ make # This creates the "busybox" executable
+ make install # or make CONFIG_PREFIX=/path/from/root install
+
+The full list of configuration and install options is available by typing:
+
+ make help
+
+Quick Start:
+============
+
+The easy way to try out BusyBox for the first time, without having to install
+it, is to enable all features and then use "standalone shell" mode with a
+blank command $PATH.
+
+To enable all features, use "make defconfig", which produces the largest
+general-purpose configuration. (It's allyesconfig minus debugging options,
+optional packaging choices, and a few special-purpose features requiring
+extra configuration to use.)
+
+ make defconfig
+ make
+ PATH= ./busybox ash
+
+Standalone shell mode causes busybox's built-in command shell to run
+any built-in busybox applets directly, without looking for external
+programs by that name. Supplying an empty command path (as above) means
+the only commands busybox can find are the built-in ones.
+
+Note that the standalone shell requires CONFIG_BUSYBOX_EXEC_PATH
+to be set appropriately, depending on whether or not /proc/self/exe is
+available or not. If you do not have /proc, then point that config option
+to the location of your busybox binary, usually /bin/busybox.
+
+Configuring Busybox:
+====================
+
+Busybox is optimized for size, but enabling the full set of functionality
+still results in a fairly large executable -- more than 1 megabyte when
+statically linked. To save space, busybox can be configured with only the
+set of applets needed for each environment. The minimal configuration, with
+all applets disabled, produces a 4k executable. (It's useless, but very small.)
+
+The manual configurator "make menuconfig" modifies the existing configuration.
+(For systems without ncurses, try "make config" instead.) The two most
+interesting starting configurations are "make allnoconfig" (to start with
+everything disabled and add just what you need), and "make defconfig" (to
+start with everything enabled and remove what you don't need). If menuconfig
+is run without an existing configuration, make defconfig will run first to
+create a known starting point.
+
+Other starting configurations (mostly used for testing purposes) include
+"make allbareconfig" (enables all applets but disables all optional features),
+"make allyesconfig" (enables absolutely everything including debug features),
+and "make randconfig" (produce a random configuration).
+
+Configuring BusyBox produces a file ".config", which can be saved for future
+use. Run "make oldconfig" to bring a .config file from an older version of
+busybox up to date.
+
+Installing Busybox:
+===================
+
+Busybox is a single executable that can behave like many different commands,
+and BusyBox uses the name it was invoked under to determine the desired
+behavior. (Try "mv busybox ls" and then "./ls -l".)
+
+Installing busybox consists of creating symlinks (or hardlinks) to the busybox
+binary for each applet enabled in busybox, and making sure these symlinks are
+in the shell's command $PATH. Running "make install" creates these symlinks,
+or "make install-hardlinks" creates hardlinks instead (useful on systems with
+a limited number of inodes). This install process uses the file
+"busybox.links" (created by make), which contains the list of enabled applets
+and the path at which to install them.
+
+Installing links to busybox is not always necessary. The special applet name
+"busybox" (or with any optional suffix, such as "busybox-static") uses the
+first argument to determine which applet to behave as, for example
+"./busybox cat LICENSE". (Running the busybox applet with no arguments gives
+a list of all enabled applets.) The standalone shell can also call busybox
+applets without links to busybox under other names in the filesystem. You can
+also configure a standaone install capability into the busybox base applet,
+and then install such links at runtime with one of "busybox --install" (for
+hardlinks) or "busybox --install -s" (for symlinks).
+
+If you enabled the busybox shared library feature (libbusybox.so) and want
+to run tests without installing, set your LD_LIBRARY_PATH accordingly when
+running the executable:
+
+ LD_LIBRARY_PATH=`pwd` ./busybox
+
+Building out-of-tree:
+=====================
+
+By default, the BusyBox build puts its temporary files in the source tree.
+Building from a read-only source tree, or building multiple configurations from
+the same source directory, requires the ability to put the temporary files
+somewhere else.
+
+To build out of tree, cd to an empty directory and configure busybox from there:
+
+ make -f /path/to/source/Makefile defconfig
+ make
+ make install
+
+Alternately, use the O=$BUILDPATH option (with an absolute path) during the
+configuration step, as in:
+
+ make O=/some/empty/directory allyesconfig
+ cd /some/empty/directory
+ make
+ make CONFIG_PREFIX=. install
+
+More Information:
+=================
+
+Se also the busybox FAQ, under the questions "How can I get started using
+BusyBox" and "How do I build a BusyBox-based system?" The BusyBox FAQ is
+available from http://www.busybox.net/FAQ.html or as the file
+docs/busybox.net/FAQ.html in this tarball.
diff --git a/i/pc104/initrd/conf/busybox/LICENSE b/i/pc104/initrd/conf/busybox/LICENSE
new file mode 100644
index 0000000..9d9bdc7
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/LICENSE
@@ -0,0 +1,348 @@
+--- A note on GPL versions
+
+BusyBox is distributed under version 2 of the General Public License (included
+in its entirety, below). Version 2 is the only version of this license which
+this version of BusyBox (or modified versions derived from this one) may be
+distributed under.
+
+------------------------------------------------------------------------
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/i/pc104/initrd/conf/busybox/Makefile b/i/pc104/initrd/conf/busybox/Makefile
new file mode 100644
index 0000000..b42e179
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/Makefile
@@ -0,0 +1,1280 @@
+VERSION = 1
+PATCHLEVEL = 5
+SUBLEVEL = 0
+EXTRAVERSION =
+NAME = Unnamed
+
+# *DOCUMENTATION*
+# To see a list of typical targets execute "make help"
+# More info can be located in ./README
+# Comments in this file are targeted only to the developer, do not
+# expect to learn how to build the kernel reading this file.
+
+# Do not print "Entering directory ..."
+MAKEFLAGS += --no-print-directory
+
+# We are using a recursive build, so we need to do a little thinking
+# to get the ordering right.
+#
+# Most importantly: sub-Makefiles should only ever modify files in
+# their own directory. If in some directory we have a dependency on
+# a file in another dir (which doesn't happen often, but it's often
+# unavoidable when linking the built-in.o targets which finally
+# turn into busybox), we will call a sub make in that other dir, and
+# after that we are sure that everything which is in that other dir
+# is now up to date.
+#
+# The only cases where we need to modify files which have global
+# effects are thus separated out and done before the recursive
+# descending is started. They are now explicitly listed as the
+# prepare rule.
+
+# To put more focus on warnings, be less verbose as default
+# Use 'make V=1' to see the full commands
+
+ifdef V
+ ifeq ("$(origin V)", "command line")
+ KBUILD_VERBOSE = $(V)
+ endif
+endif
+ifndef KBUILD_VERBOSE
+ KBUILD_VERBOSE = 0
+endif
+
+# Call sparse as part of compilation of C files
+# Use 'make C=1' to enable sparse checking
+
+ifdef C
+ ifeq ("$(origin C)", "command line")
+ KBUILD_CHECKSRC = $(C)
+ endif
+endif
+ifndef KBUILD_CHECKSRC
+ KBUILD_CHECKSRC = 0
+endif
+
+# Use make M=dir to specify directory of external module to build
+# Old syntax make ... SUBDIRS=$PWD is still supported
+# Setting the environment variable KBUILD_EXTMOD take precedence
+ifdef SUBDIRS
+ KBUILD_EXTMOD ?= $(SUBDIRS)
+endif
+ifdef M
+ ifeq ("$(origin M)", "command line")
+ KBUILD_EXTMOD := $(M)
+ endif
+endif
+
+
+# kbuild supports saving output files in a separate directory.
+# To locate output files in a separate directory two syntaxes are supported.
+# In both cases the working directory must be the root of the kernel src.
+# 1) O=
+# Use "make O=dir/to/store/output/files/"
+#
+# 2) Set KBUILD_OUTPUT
+# Set the environment variable KBUILD_OUTPUT to point to the directory
+# where the output files shall be placed.
+# export KBUILD_OUTPUT=dir/to/store/output/files/
+# make
+#
+# The O= assignment takes precedence over the KBUILD_OUTPUT environment
+# variable.
+
+
+# KBUILD_SRC is set on invocation of make in OBJ directory
+# KBUILD_SRC is not intended to be used by the regular user (for now)
+ifeq ($(KBUILD_SRC),)
+
+# OK, Make called in directory where kernel src resides
+# Do we want to locate output files in a separate directory?
+ifdef O
+ ifeq ("$(origin O)", "command line")
+ KBUILD_OUTPUT := $(O)
+ endif
+endif
+
+# That's our default target when none is given on the command line
+PHONY := _all
+_all:
+
+ifneq ($(KBUILD_OUTPUT),)
+# Invoke a second make in the output directory, passing relevant variables
+# check that the output directory actually exists
+saved-output := $(KBUILD_OUTPUT)
+KBUILD_OUTPUT := $(shell cd $(KBUILD_OUTPUT) && /bin/pwd)
+$(if $(KBUILD_OUTPUT),, \
+ $(error output directory "$(saved-output)" does not exist))
+
+PHONY += $(MAKECMDGOALS)
+
+$(filter-out _all,$(MAKECMDGOALS)) _all:
+ $(if $(KBUILD_VERBOSE:1=),@)$(MAKE) -C $(KBUILD_OUTPUT) \
+ KBUILD_SRC=$(CURDIR) \
+ KBUILD_EXTMOD="$(KBUILD_EXTMOD)" -f $(CURDIR)/Makefile $@
+
+# Leave processing to above invocation of make
+skip-makefile := 1
+endif # ifneq ($(KBUILD_OUTPUT),)
+endif # ifeq ($(KBUILD_SRC),)
+
+# We process the rest of the Makefile if this is the final invocation of make
+ifeq ($(skip-makefile),)
+
+# If building an external module we do not care about the all: rule
+# but instead _all depend on modules
+PHONY += all
+ifeq ($(KBUILD_EXTMOD),)
+_all: all
+else
+_all: modules
+endif
+
+srctree := $(if $(KBUILD_SRC),$(KBUILD_SRC),$(CURDIR))
+TOPDIR := $(srctree)
+# FIXME - TOPDIR is obsolete, use srctree/objtree
+objtree := $(CURDIR)
+src := $(srctree)
+obj := $(objtree)
+
+VPATH := $(srctree)$(if $(KBUILD_EXTMOD),:$(KBUILD_EXTMOD))
+
+export srctree objtree VPATH TOPDIR
+
+
+# SUBARCH tells the usermode build what the underlying arch is. That is set
+# first, and if a usermode build is happening, the "ARCH=um" on the command
+# line overrides the setting of ARCH below. If a native build is happening,
+# then ARCH is assigned, getting whatever value it gets normally, and
+# SUBARCH is subsequently ignored.
+
+SUBARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \
+ -e s/arm.*/arm/ -e s/sa110/arm/ \
+ -e s/s390x/s390/ -e s/parisc64/parisc/ \
+ -e s/ppc.*/powerpc/ -e s/mips.*/mips/ )
+
+# Cross compiling and selecting different set of gcc/bin-utils
+# ---------------------------------------------------------------------------
+#
+# When performing cross compilation for other architectures ARCH shall be set
+# to the target architecture. (See arch/* for the possibilities).
+# ARCH can be set during invocation of make:
+# make ARCH=ia64
+# Another way is to have ARCH set in the environment.
+# The default ARCH is the host where make is executed.
+
+# CROSS_COMPILE specify the prefix used for all executables used
+# during compilation. Only gcc and related bin-utils executables
+# are prefixed with $(CROSS_COMPILE).
+# CROSS_COMPILE can be set on the command line
+# make CROSS_COMPILE=ia64-linux-
+# Alternatively CROSS_COMPILE can be set in the environment.
+# Default value for CROSS_COMPILE is not to prefix executables
+# Note: Some architectures assign CROSS_COMPILE in their arch/*/Makefile
+
+ARCH ?= $(SUBARCH)
+CROSS_COMPILE ?=
+
+# Architecture as present in compile.h
+UTS_MACHINE := $(ARCH)
+
+# SHELL used by kbuild
+CONFIG_SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \
+ else if [ -x /bin/bash ]; then echo /bin/bash; \
+ else echo sh; fi ; fi)
+
+HOSTCC = gcc
+HOSTCXX = g++
+HOSTCFLAGS = -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer
+HOSTCXXFLAGS = -O2
+
+# Decide whether to build built-in, modular, or both.
+# Normally, just do built-in.
+
+KBUILD_MODULES :=
+KBUILD_BUILTIN := 1
+
+# If we have only "make modules", don't compile built-in objects.
+# When we're building modules with modversions, we need to consider
+# the built-in objects during the descend as well, in order to
+# make sure the checksums are uptodate before we record them.
+
+ifeq ($(MAKECMDGOALS),modules)
+ KBUILD_BUILTIN := $(if $(CONFIG_MODVERSIONS),1)
+endif
+
+# If we have "make <whatever> modules", compile modules
+# in addition to whatever we do anyway.
+# Just "make" or "make all" shall build modules as well
+
+ifneq ($(filter all _all modules,$(MAKECMDGOALS)),)
+ KBUILD_MODULES := 1
+endif
+
+ifeq ($(MAKECMDGOALS),)
+ KBUILD_MODULES := 1
+endif
+
+export KBUILD_MODULES KBUILD_BUILTIN
+export KBUILD_CHECKSRC KBUILD_SRC KBUILD_EXTMOD
+
+# Beautify output
+# ---------------------------------------------------------------------------
+#
+# Normally, we echo the whole command before executing it. By making
+# that echo $($(quiet)$(cmd)), we now have the possibility to set
+# $(quiet) to choose other forms of output instead, e.g.
+#
+# quiet_cmd_cc_o_c = Compiling $(RELDIR)/$@
+# cmd_cc_o_c = $(CC) $(c_flags) -c -o $@ $<
+#
+# If $(quiet) is empty, the whole command will be printed.
+# If it is set to "quiet_", only the short version will be printed.
+# If it is set to "silent_", nothing wil be printed at all, since
+# the variable $(silent_cmd_cc_o_c) doesn't exist.
+#
+# A simple variant is to prefix commands with $(Q) - that's useful
+# for commands that shall be hidden in non-verbose mode.
+#
+# $(Q)ln $@ :<
+#
+# If KBUILD_VERBOSE equals 0 then the above command will be hidden.
+# If KBUILD_VERBOSE equals 1 then the above command is displayed.
+
+ifeq ($(KBUILD_VERBOSE),1)
+ quiet =
+ Q =
+else
+ quiet=quiet_
+ Q = @
+endif
+
+# If the user is running make -s (silent mode), suppress echoing of
+# commands
+
+ifneq ($(findstring s,$(MAKEFLAGS)),)
+ quiet=silent_
+endif
+
+export quiet Q KBUILD_VERBOSE
+
+
+# Look for make include files relative to root of kernel src
+MAKEFLAGS += --include-dir=$(srctree)
+
+# We need some generic definitions
+include $(srctree)/scripts/Kbuild.include
+
+# For maximum performance (+ possibly random breakage, uncomment
+# the following)
+
+MAKEFLAGS += -rR
+
+# Make variables (CC, etc...)
+
+AS = $(CROSS_COMPILE)as
+CC = $(CROSS_COMPILE)gcc
+LD = $(CC) -nostdlib
+CPP = $(CC) -E
+AR = $(CROSS_COMPILE)ar
+NM = $(CROSS_COMPILE)nm
+STRIP = $(CROSS_COMPILE)strip
+OBJCOPY = $(CROSS_COMPILE)objcopy
+OBJDUMP = $(CROSS_COMPILE)objdump
+AWK = awk
+GENKSYMS = scripts/genksyms/genksyms
+DEPMOD = /sbin/depmod
+KALLSYMS = scripts/kallsyms
+PERL = perl
+CHECK = sparse
+
+CHECKFLAGS := -D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ -Wbitwise $(CF)
+MODFLAGS = -DMODULE
+CFLAGS_MODULE = $(MODFLAGS)
+AFLAGS_MODULE = $(MODFLAGS)
+LDFLAGS_MODULE = -r
+CFLAGS_KERNEL =
+AFLAGS_KERNEL =
+
+
+# Use LINUXINCLUDE when you must reference the include/ directory.
+# Needed to be compatible with the O= option
+CFLAGS := $(CFLAGS)
+CPPFLAGS := $(CPPFLAGS)
+AFLAGS := $(AFLAGS)
+
+# Read KERNELRELEASE from .kernelrelease (if it exists)
+KERNELRELEASE = $(shell cat .kernelrelease 2> /dev/null)
+KERNELVERSION = $(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
+
+export VERSION PATCHLEVEL SUBLEVEL KERNELRELEASE KERNELVERSION \
+ ARCH CONFIG_SHELL HOSTCC HOSTCFLAGS CROSS_COMPILE AS LD CC \
+ CPP AR NM STRIP OBJCOPY OBJDUMP MAKE AWK GENKSYMS PERL UTS_MACHINE \
+ HOSTCXX HOSTCXXFLAGS LDFLAGS_MODULE CHECK CHECKFLAGS
+
+export CPPFLAGS NOSTDINC_FLAGS LINUXINCLUDE OBJCOPYFLAGS LDFLAGS
+export CFLAGS CFLAGS_KERNEL CFLAGS_MODULE
+export AFLAGS AFLAGS_KERNEL AFLAGS_MODULE
+
+# When compiling out-of-tree modules, put MODVERDIR in the module
+# tree rather than in the kernel tree. The kernel tree might
+# even be read-only.
+export MODVERDIR := $(if $(KBUILD_EXTMOD),$(firstword $(KBUILD_EXTMOD))/).tmp_versions
+
+# Files to ignore in find ... statements
+
+RCS_FIND_IGNORE := \( -name SCCS -o -name BitKeeper -o -name .svn -o -name CVS -o -name .pc -o -name .hg -o -name .git \) -prune -o
+export RCS_TAR_IGNORE := --exclude SCCS --exclude BitKeeper --exclude .svn --exclude CVS --exclude .pc --exclude .hg --exclude .git
+
+# ===========================================================================
+# Rules shared between *config targets and build targets
+
+# Basic helpers built in scripts/
+PHONY += scripts_basic
+scripts_basic:
+ $(Q)$(MAKE) $(build)=scripts/basic
+
+# To avoid any implicit rule to kick in, define an empty command.
+scripts/basic/%: scripts_basic ;
+
+PHONY += outputmakefile
+# outputmakefile generates a Makefile in the output directory, if using a
+# separate output directory. This allows convenient use of make in the
+# output directory.
+outputmakefile:
+ifneq ($(KBUILD_SRC),)
+ $(Q)$(CONFIG_SHELL) $(srctree)/scripts/mkmakefile \
+ $(srctree) $(objtree) $(VERSION) $(PATCHLEVEL)
+endif
+
+# To make sure we do not include .config for any of the *config targets
+# catch them early, and hand them over to scripts/kconfig/Makefile
+# It is allowed to specify more targets when calling make, including
+# mixing *config targets and build targets.
+# For example 'make oldconfig all'.
+# Detect when mixed targets is specified, and make a second invocation
+# of make so .config is not included in this case either (for *config).
+
+no-dot-config-targets := clean mrproper distclean \
+ cscope TAGS tags help %docs check%
+
+config-targets := 0
+mixed-targets := 0
+dot-config := 1
+
+ifneq ($(filter $(no-dot-config-targets), $(MAKECMDGOALS)),)
+ ifeq ($(filter-out $(no-dot-config-targets), $(MAKECMDGOALS)),)
+ dot-config := 0
+ endif
+endif
+
+ifeq ($(KBUILD_EXTMOD),)
+ ifneq ($(filter config %config,$(MAKECMDGOALS)),)
+ config-targets := 1
+ ifneq ($(filter-out config %config,$(MAKECMDGOALS)),)
+ mixed-targets := 1
+ endif
+ endif
+endif
+
+ifeq ($(mixed-targets),1)
+# ===========================================================================
+# We're called with mixed targets (*config and build targets).
+# Handle them one by one.
+
+%:: FORCE
+ $(Q)$(MAKE) -C $(srctree) KBUILD_SRC= $@
+
+else
+ifeq ($(config-targets),1)
+# ===========================================================================
+# *config targets only - make sure prerequisites are updated, and descend
+# in scripts/kconfig to make the *config target
+
+# Read arch specific Makefile to set KBUILD_DEFCONFIG as needed.
+# KBUILD_DEFCONFIG may point out an alternative default configuration
+# used for 'make defconfig'
+-include $(srctree)/arch/$(ARCH)/Makefile
+export KBUILD_DEFCONFIG
+
+config %config: scripts_basic outputmakefile FORCE
+ $(Q)mkdir -p include
+ $(Q)$(MAKE) $(build)=scripts/kconfig $@
+ $(Q)$(MAKE) -C $(srctree) KBUILD_SRC= .kernelrelease
+
+else
+# ===========================================================================
+# Build targets only - this includes busybox, arch specific targets, clean
+# targets and others. In general all targets except *config targets.
+
+ifeq ($(KBUILD_EXTMOD),)
+# Additional helpers built in scripts/
+# Carefully list dependencies so we do not try to build scripts twice
+# in parrallel
+PHONY += scripts
+scripts: scripts_basic include/config/MARKER
+ $(Q)$(MAKE) $(build)=$(@)
+
+scripts_basic: include/autoconf.h
+
+# Objects we will link into busybox / subdirs we need to visit
+core-y := \
+ applets/ \
+
+libs-y := \
+ archival/ \
+ archival/libunarchive/ \
+ console-tools/ \
+ coreutils/ \
+ coreutils/libcoreutils/ \
+ debianutils/ \
+ e2fsprogs/ \
+ editors/ \
+ findutils/ \
+ init/ \
+ libbb/ \
+ libpwdgrp/ \
+ loginutils/ \
+ miscutils/ \
+ modutils/ \
+ networking/ \
+ networking/libiproute/ \
+ networking/udhcp/ \
+ procps/ \
+ runit/ \
+ selinux/ \
+ shell/ \
+ sysklogd/ \
+ util-linux/ \
+
+endif # KBUILD_EXTMOD
+
+ifeq ($(dot-config),1)
+# In this section, we need .config
+
+# Read in dependencies to all Kconfig* files, make sure to run
+# oldconfig if changes are detected.
+-include .kconfig.d
+
+-include .config
+
+# If .config needs to be updated, it will be done via the dependency
+# that autoconf has on .config.
+# To avoid any implicit rule to kick in, define an empty command
+.config .kconfig.d: ;
+
+# Now we can define CFLAGS etc according to .config
+include $(srctree)/Makefile.flags
+
+# If .config is newer than include/autoconf.h, someone tinkered
+# with it and forgot to run make oldconfig.
+# If kconfig.d is missing then we are probarly in a cleaned tree so
+# we execute the config step to be sure to catch updated Kconfig files
+include/autoconf.h: .kconfig.d .config
+ $(Q)$(MAKE) -f $(srctree)/Makefile silentoldconfig
+
+else
+# Dummy target needed, because used as prerequisite
+include/autoconf.h: ;
+endif
+
+# The all: target is the default when no target is given on the
+# command line.
+# This allow a user to issue only 'make' to build a kernel including modules
+# Defaults busybox but it is usually overridden in the arch makefile
+all: busybox
+
+-include $(srctree)/arch/$(ARCH)/Makefile
+
+# arch Makefile may override CC so keep this after arch Makefile is included
+#bbox# NOSTDINC_FLAGS += -nostdinc -isystem $(shell $(CC) -print-file-name=include)
+CHECKFLAGS += $(NOSTDINC_FLAGS)
+
+# warn about C99 declaration after statement
+CFLAGS += $(call cc-option,-Wdeclaration-after-statement,)
+
+# disable pointer signedness warnings in gcc 4.0
+CFLAGS += $(call cc-option,-Wno-pointer-sign,)
+
+# Default kernel image to build when no specific target is given.
+# KBUILD_IMAGE may be overruled on the commandline or
+# set in the environment
+# Also any assignments in arch/$(ARCH)/Makefile take precedence over
+# this default value
+export KBUILD_IMAGE ?= busybox
+
+#
+# INSTALL_PATH specifies where to place the updated kernel and system map
+# images. Default is /boot, but you can set it to other values
+export INSTALL_PATH ?= /boot
+
+#
+# INSTALL_MOD_PATH specifies a prefix to MODLIB for module directory
+# relocations required by build roots. This is not defined in the
+# makefile but the arguement can be passed to make if needed.
+#
+
+MODLIB = $(INSTALL_MOD_PATH)/lib/modules/$(KERNELRELEASE)
+export MODLIB
+
+
+ifeq ($(KBUILD_EXTMOD),)
+busybox-dirs := $(patsubst %/,%,$(filter %/, $(core-y) $(core-m) $(libs-y) $(libs-m)))
+
+busybox-alldirs := $(sort $(busybox-dirs) $(patsubst %/,%,$(filter %/, \
+ $(core-n) $(core-) $(libs-n) $(libs-) \
+ )))
+
+core-y := $(patsubst %/, %/built-in.o, $(core-y))
+libs-y1 := $(patsubst %/, %/lib.a, $(libs-y))
+libs-y2 := $(patsubst %/, %/built-in.o, $(libs-y))
+libs-y := $(libs-y1) $(libs-y2)
+
+# Build busybox
+# ---------------------------------------------------------------------------
+# busybox is build from the objects selected by $(busybox-init) and
+# $(busybox-main). Most are built-in.o files from top-level directories
+# in the kernel tree, others are specified in arch/$(ARCH)Makefile.
+# Ordering when linking is important, and $(busybox-init) must be first.
+#
+# busybox
+# ^
+# |
+# +-< $(busybox-init)
+# | +--< init/version.o + more
+# |
+# +--< $(busybox-main)
+# | +--< driver/built-in.o mm/built-in.o + more
+# |
+# +-< kallsyms.o (see description in CONFIG_KALLSYMS section)
+#
+# busybox version (uname -v) cannot be updated during normal
+# descending-into-subdirs phase since we do not yet know if we need to
+# update busybox.
+# Therefore this step is delayed until just before final link of busybox -
+# except in the kallsyms case where it is done just before adding the
+# symbols to the kernel.
+#
+# System.map is generated to document addresses of all kernel symbols
+
+busybox-all := $(core-y) $(libs-y)
+
+# Rule to link busybox - also used during CONFIG_KALLSYMS
+# May be overridden by arch/$(ARCH)/Makefile
+quiet_cmd_busybox__ ?= LINK $@
+ cmd_busybox__ ?= $(srctree)/scripts/trylink $(CC) $(LDFLAGS) \
+ -o $@ -Wl,-M \
+ -Wl,--warn-common -Wl,--sort-common -Wl,--gc-sections \
+ -Wl,--start-group $(busybox-all) -Wl,--end-group
+
+# Generate System.map
+quiet_cmd_sysmap = SYSMAP
+ cmd_sysmap = $(CONFIG_SHELL) $(srctree)/scripts/mksysmap
+
+# Link of busybox
+# If CONFIG_KALLSYMS is set .version is already updated
+# Generate System.map and verify that the content is consistent
+# Use + in front of the busybox_version rule to silent warning with make -j2
+# First command is ':' to allow us to use + in front of the rule
+define rule_busybox__
+ :
+ $(call cmd,busybox__)
+ $(Q)echo 'cmd_$@ := $(cmd_busybox__)' > $(@D)/.$(@F).cmd
+endef
+
+
+ifdef CONFIG_KALLSYMS
+# Generate section listing all symbols and add it into busybox $(kallsyms.o)
+# It's a three stage process:
+# o .tmp_busybox1 has all symbols and sections, but __kallsyms is
+# empty
+# Running kallsyms on that gives us .tmp_kallsyms1.o with
+# the right size - busybox version (uname -v) is updated during this step
+# o .tmp_busybox2 now has a __kallsyms section of the right size,
+# but due to the added section, some addresses have shifted.
+# From here, we generate a correct .tmp_kallsyms2.o
+# o The correct .tmp_kallsyms2.o is linked into the final busybox.
+# o Verify that the System.map from busybox matches the map from
+# .tmp_busybox2, just in case we did not generate kallsyms correctly.
+# o If CONFIG_KALLSYMS_EXTRA_PASS is set, do an extra pass using
+# .tmp_busybox3 and .tmp_kallsyms3.o. This is only meant as a
+# temporary bypass to allow the kernel to be built while the
+# maintainers work out what went wrong with kallsyms.
+
+ifdef CONFIG_KALLSYMS_EXTRA_PASS
+last_kallsyms := 3
+else
+last_kallsyms := 2
+endif
+
+kallsyms.o := .tmp_kallsyms$(last_kallsyms).o
+
+define verify_kallsyms
+ $(Q)$(if $($(quiet)cmd_sysmap), \
+ echo ' $($(quiet)cmd_sysmap) .tmp_System.map' &&) \
+ $(cmd_sysmap) .tmp_busybox$(last_kallsyms) .tmp_System.map
+ $(Q)cmp -s System.map .tmp_System.map || \
+ (echo Inconsistent kallsyms data; \
+ echo Try setting CONFIG_KALLSYMS_EXTRA_PASS; \
+ rm .tmp_kallsyms* ; /bin/false )
+endef
+
+# Update busybox version before link
+# Use + in front of this rule to silent warning about make -j1
+# First command is ':' to allow us to use + in front of this rule
+cmd_ksym_ld = $(cmd_busybox__)
+define rule_ksym_ld
+ :
+ +$(call cmd,busybox_version)
+ $(call cmd,busybox__)
+ $(Q)echo 'cmd_$@ := $(cmd_busybox__)' > $(@D)/.$(@F).cmd
+endef
+
+# Generate .S file with all kernel symbols
+quiet_cmd_kallsyms = KSYM $@
+ cmd_kallsyms = $(NM) -n $< | $(KALLSYMS) \
+ $(if $(CONFIG_KALLSYMS_ALL),--all-symbols) > $@
+
+.tmp_kallsyms1.o .tmp_kallsyms2.o .tmp_kallsyms3.o: %.o: %.S scripts FORCE
+ $(call if_changed_dep,as_o_S)
+
+.tmp_kallsyms%.S: .tmp_busybox% $(KALLSYMS)
+ $(call cmd,kallsyms)
+
+# .tmp_busybox1 must be complete except kallsyms, so update busybox version
+.tmp_busybox1: $(busybox-lds) $(busybox-all) FORCE
+ $(call if_changed_rule,ksym_ld)
+
+.tmp_busybox2: $(busybox-lds) $(busybox-all) .tmp_kallsyms1.o FORCE
+ $(call if_changed,busybox__)
+
+.tmp_busybox3: $(busybox-lds) $(busybox-all) .tmp_kallsyms2.o FORCE
+ $(call if_changed,busybox__)
+
+# Needs to visit scripts/ before $(KALLSYMS) can be used.
+$(KALLSYMS): scripts ;
+
+# Generate some data for debugging strange kallsyms problems
+debug_kallsyms: .tmp_map$(last_kallsyms)
+
+.tmp_map%: .tmp_busybox% FORCE
+ ($(OBJDUMP) -h $< | $(AWK) '/^ +[0-9]/{print $$4 " 0 " $$2}'; $(NM) $<) | sort > $@
+
+.tmp_map3: .tmp_map2
+
+.tmp_map2: .tmp_map1
+
+endif # ifdef CONFIG_KALLSYMS
+
+# busybox image - including updated kernel symbols
+busybox_unstripped: $(busybox-all) FORCE
+ $(call if_changed_rule,busybox__)
+ $(Q)rm -f .old_version
+
+busybox: busybox_unstripped
+ $(Q)$(STRIP) -s --remove-section=.note --remove-section=.comment \
+ busybox_unstripped -o $@
+
+# The actual objects are generated when descending,
+# make sure no implicit rule kicks in
+$(sort $(busybox-all)): $(busybox-dirs) ;
+
+# Handle descending into subdirectories listed in $(busybox-dirs)
+# Preset locale variables to speed up the build process. Limit locale
+# tweaks to this spot to avoid wrong language settings when running
+# make menuconfig etc.
+# Error messages still appears in the original language
+
+PHONY += $(busybox-dirs)
+$(busybox-dirs): prepare scripts
+ $(Q)$(MAKE) $(build)=$@
+
+# Build the kernel release string
+# The KERNELRELEASE is stored in a file named .kernelrelease
+# to be used when executing for example make install or make modules_install
+#
+# Take the contents of any files called localversion* and the config
+# variable CONFIG_LOCALVERSION and append them to KERNELRELEASE.
+# LOCALVERSION from the command line override all of this
+
+nullstring :=
+space := $(nullstring) # end of line
+
+___localver = $(objtree)/localversion* $(srctree)/localversion*
+__localver = $(sort $(wildcard $(___localver)))
+# skip backup files (containing '~')
+_localver = $(foreach f, $(__localver), $(if $(findstring ~, $(f)),,$(f)))
+
+localver = $(subst $(space),, \
+ $(shell cat /dev/null $(_localver)) \
+ $(patsubst "%",%,$(CONFIG_LOCALVERSION)))
+
+# If CONFIG_LOCALVERSION_AUTO is set scripts/setlocalversion is called
+# and if the SCM is know a tag from the SCM is appended.
+# The appended tag is determinded by the SCM used.
+#
+# Currently, only git is supported.
+# Other SCMs can edit scripts/setlocalversion and add the appropriate
+# checks as needed.
+ifdef CONFIG_LOCALVERSION_AUTO
+ _localver-auto = $(shell $(CONFIG_SHELL) \
+ $(srctree)/scripts/setlocalversion $(srctree))
+ localver-auto = $(LOCALVERSION)$(_localver-auto)
+endif
+
+localver-full = $(localver)$(localver-auto)
+
+# Store (new) KERNELRELASE string in .kernelrelease
+kernelrelease = $(KERNELVERSION)$(localver-full)
+.kernelrelease: FORCE
+ $(Q)rm -f $@
+ $(Q)echo $(kernelrelease) > $@
+
+
+# Things we need to do before we recursively start building the kernel
+# or the modules are listed in "prepare".
+# A multi level approach is used. prepareN is processed before prepareN-1.
+# archprepare is used in arch Makefiles and when processed asm symlink,
+# version.h and scripts_basic is processed / created.
+
+# Listed in dependency order
+PHONY += prepare archprepare prepare0 prepare1 prepare2 prepare3
+
+# prepare-all is deprecated, use prepare as valid replacement
+PHONY += prepare-all
+
+# prepare3 is used to check if we are building in a separate output directory,
+# and if so do:
+# 1) Check that make has not been executed in the kernel src $(srctree)
+# 2) Create the include2 directory, used for the second asm symlink
+prepare3: .kernelrelease
+ifneq ($(KBUILD_SRC),)
+ @echo ' Using $(srctree) as source for kernel'
+ $(Q)if [ -f $(srctree)/.config ]; then \
+ echo " $(srctree) is not clean, please run 'make mrproper'";\
+ echo " in the '$(srctree)' directory.";\
+ /bin/false; \
+ fi;
+ $(Q)if [ ! -d include2 ]; then mkdir -p include2; fi;
+ $(Q)ln -fsn $(srctree)/include/asm-$(ARCH) include2/asm
+endif
+
+# prepare2 creates a makefile if using a separate output directory
+prepare2: prepare3 outputmakefile
+
+prepare1: prepare2 include/config/MARKER
+ifneq ($(KBUILD_MODULES),)
+ $(Q)mkdir -p $(MODVERDIR)
+ $(Q)rm -f $(MODVERDIR)/*
+endif
+
+archprepare: prepare1 scripts_basic
+
+prepare0: archprepare FORCE
+ $(Q)$(MAKE) $(build)=.
+
+# All the preparing..
+prepare prepare-all: prepare0
+
+# Leave this as default for preprocessing busybox.lds.S, which is now
+# done in arch/$(ARCH)/kernel/Makefile
+
+export CPPFLAGS_busybox.lds += -P -C -U$(ARCH)
+
+# FIXME: The asm symlink changes when $(ARCH) changes. That's
+# hard to detect, but I suppose "make mrproper" is a good idea
+# before switching between archs anyway.
+
+#bbox# include/asm:
+#bbox# @echo ' SYMLINK $@ -> include/asm-$(ARCH)'
+#bbox# $(Q)if [ ! -d include ]; then mkdir -p include; fi;
+#bbox# @ln -fsn asm-$(ARCH) $@
+
+# Split autoconf.h into include/linux/config/*
+quiet_cmd_gen_bbconfigopts = GEN include/bbconfigopts.h
+ cmd_gen_bbconfigopts = $(srctree)/scripts/mkconfigs > include/bbconfigopts.h
+quiet_cmd_split_autoconf = SPLIT include/autoconf.h -> include/config/*
+ cmd_split_autoconf = scripts/basic/split-include include/autoconf.h include/config
+#bbox# piggybacked generation of few .h files
+include/config/MARKER: scripts/basic/split-include include/autoconf.h
+ $(call cmd,split_autoconf)
+ $(call cmd,gen_bbconfigopts)
+ @touch $@
+
+# Generate some files
+# ---------------------------------------------------------------------------
+
+# KERNELRELEASE can change from a few different places, meaning version.h
+# needs to be updated, so this check is forced on all builds
+
+uts_len := 64
+
+define filechk_version.h
+ if [ `echo -n "$(KERNELRELEASE)" | wc -c ` -gt $(uts_len) ]; then \
+ echo '"$(KERNELRELEASE)" exceeds $(uts_len) characters' >&2; \
+ exit 1; \
+ fi; \
+ (echo \#define UTS_RELEASE \"$(KERNELRELEASE)\"; \
+ echo \#define LINUX_VERSION_CODE `expr $(VERSION) \\* 65536 + $(PATCHLEVEL) \\* 256 + $(SUBLEVEL)`; \
+ echo '#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))'; \
+ )
+endef
+
+# ---------------------------------------------------------------------------
+
+PHONY += depend dep
+depend dep:
+ @echo '*** Warning: make $@ is unnecessary now.'
+
+# ---------------------------------------------------------------------------
+# Modules
+
+ifdef CONFIG_MODULES
+
+# By default, build modules as well
+
+all: modules
+
+# Build modules
+
+PHONY += modules
+modules: $(busybox-dirs) $(if $(KBUILD_BUILTIN),busybox)
+ @echo ' Building modules, stage 2.';
+ $(Q)$(MAKE) -rR -f $(srctree)/scripts/Makefile.modpost
+
+
+# Target to prepare building external modules
+PHONY += modules_prepare
+modules_prepare: prepare scripts
+
+# Target to install modules
+PHONY += modules_install
+modules_install: _modinst_ _modinst_post
+
+PHONY += _modinst_
+_modinst_:
+ @if [ -z "`$(DEPMOD) -V 2>/dev/null | grep module-init-tools`" ]; then \
+ echo "Warning: you may need to install module-init-tools"; \
+ echo "See http://www.codemonkey.org.uk/docs/post-halloween-2.6.txt";\
+ sleep 1; \
+ fi
+ @rm -rf $(MODLIB)/kernel
+ @rm -f $(MODLIB)/source
+ @mkdir -p $(MODLIB)/kernel
+ @ln -s $(srctree) $(MODLIB)/source
+ @if [ ! $(objtree) -ef $(MODLIB)/build ]; then \
+ rm -f $(MODLIB)/build ; \
+ ln -s $(objtree) $(MODLIB)/build ; \
+ fi
+ $(Q)$(MAKE) -rR -f $(srctree)/scripts/Makefile.modinst
+
+# If System.map exists, run depmod. This deliberately does not have a
+# dependency on System.map since that would run the dependency tree on
+# busybox. This depmod is only for convenience to give the initial
+# boot a modules.dep even before / is mounted read-write. However the
+# boot script depmod is the master version.
+ifeq "$(strip $(INSTALL_MOD_PATH))" ""
+depmod_opts :=
+else
+depmod_opts := -b $(INSTALL_MOD_PATH) -r
+endif
+PHONY += _modinst_post
+_modinst_post: _modinst_
+ if [ -r System.map -a -x $(DEPMOD) ]; then $(DEPMOD) -ae -F System.map $(depmod_opts) $(KERNELRELEASE); fi
+
+else # CONFIG_MODULES
+
+# Modules not configured
+# ---------------------------------------------------------------------------
+
+modules modules_install: FORCE
+ @echo
+ @echo "The present busybox configuration has modules disabled."
+ @echo "Type 'make config' and enable loadable module support."
+ @echo "Then build a kernel with module support enabled."
+ @echo
+ @exit 1
+
+endif # CONFIG_MODULES
+
+###
+# Cleaning is done on three levels.
+# make clean Delete most generated files
+# Leave enough to build external modules
+# make mrproper Delete the current configuration, and all generated files
+# make distclean Remove editor backup files, patch leftover files and the like
+
+# Directories & files removed with 'make clean'
+CLEAN_DIRS += $(MODVERDIR)
+CLEAN_FILES += busybox* System.map .kernelrelease \
+ .tmp_kallsyms* .tmp_version .tmp_busybox* .tmp_System.map
+
+# Directories & files removed with 'make mrproper'
+MRPROPER_DIRS += include/config include2
+MRPROPER_FILES += .config .config.old include/asm .version .old_version \
+ include/autoconf.h \
+ include/bbconfigopts.h \
+ include/usage_compressed.h \
+ .kernelrelease Module.symvers tags TAGS cscope*
+
+# clean - Delete most, but leave enough to build external modules
+#
+clean: rm-dirs := $(CLEAN_DIRS)
+clean: rm-files := $(CLEAN_FILES)
+clean-dirs := $(addprefix _clean_,$(srctree) $(busybox-alldirs))
+
+PHONY += $(clean-dirs) clean archclean
+$(clean-dirs):
+ $(Q)$(MAKE) $(clean)=$(patsubst _clean_%,%,$@)
+
+clean: archclean $(clean-dirs)
+ $(call cmd,rmdirs)
+ $(call cmd,rmfiles)
+ @find . $(RCS_FIND_IGNORE) \
+ \( -name '*.[oas]' -o -name '*.ko' -o -name '.*.cmd' \
+ -o -name '.*.d' -o -name '.*.tmp' -o -name '*.mod.c' \) \
+ -type f -print | xargs rm -f
+
+# mrproper - Delete all generated files, including .config
+#
+mrproper: rm-dirs := $(wildcard $(MRPROPER_DIRS))
+mrproper: rm-files := $(wildcard $(MRPROPER_FILES))
+mrproper-dirs := $(addprefix _mrproper_,scripts)
+
+PHONY += $(mrproper-dirs) mrproper archmrproper
+$(mrproper-dirs):
+ $(Q)$(MAKE) $(clean)=$(patsubst _mrproper_%,%,$@)
+
+mrproper: clean archmrproper $(mrproper-dirs)
+ $(call cmd,rmdirs)
+ $(call cmd,rmfiles)
+
+# distclean
+#
+PHONY += distclean
+
+distclean: mrproper
+ @find $(srctree) $(RCS_FIND_IGNORE) \
+ \( -name '*.orig' -o -name '*.rej' -o -name '*~' \
+ -o -name '*.bak' -o -name '#*#' -o -name '.*.orig' \
+ -o -name '.*.rej' -o -size 0 \
+ -o -name '*%' -o -name '.*.cmd' -o -name 'core' \) \
+ -type f -print | xargs rm -f
+
+
+# Packaging of the kernel to various formats
+# ---------------------------------------------------------------------------
+# rpm target kept for backward compatibility
+package-dir := $(srctree)/scripts/package
+
+%pkg: FORCE
+ $(Q)$(MAKE) $(build)=$(package-dir) $@
+rpm: FORCE
+ $(Q)$(MAKE) $(build)=$(package-dir) $@
+
+
+# Brief documentation of the typical targets used
+# ---------------------------------------------------------------------------
+
+boards := $(wildcard $(srctree)/arch/$(ARCH)/configs/*_defconfig)
+boards := $(notdir $(boards))
+
+-include $(srctree)/Makefile.help
+
+# Documentation targets
+# ---------------------------------------------------------------------------
+%docs: scripts_basic FORCE
+ $(Q)$(MAKE) $(build)=Documentation/DocBook $@
+
+else # KBUILD_EXTMOD
+
+###
+# External module support.
+# When building external modules the kernel used as basis is considered
+# read-only, and no consistency checks are made and the make
+# system is not used on the basis kernel. If updates are required
+# in the basis kernel ordinary make commands (without M=...) must
+# be used.
+#
+# The following are the only valid targets when building external
+# modules.
+# make M=dir clean Delete all automatically generated files
+# make M=dir modules Make all modules in specified dir
+# make M=dir Same as 'make M=dir modules'
+# make M=dir modules_install
+# Install the modules build in the module directory
+# Assumes install directory is already created
+
+# We are always building modules
+KBUILD_MODULES := 1
+PHONY += crmodverdir
+crmodverdir:
+ $(Q)mkdir -p $(MODVERDIR)
+ $(Q)rm -f $(MODVERDIR)/*
+
+PHONY += $(objtree)/Module.symvers
+$(objtree)/Module.symvers:
+ @test -e $(objtree)/Module.symvers || ( \
+ echo; \
+ echo " WARNING: Symbol version dump $(objtree)/Module.symvers"; \
+ echo " is missing; modules will have no dependencies and modversions."; \
+ echo )
+
+module-dirs := $(addprefix _module_,$(KBUILD_EXTMOD))
+PHONY += $(module-dirs) modules
+$(module-dirs): crmodverdir $(objtree)/Module.symvers
+ $(Q)$(MAKE) $(build)=$(patsubst _module_%,%,$@)
+
+modules: $(module-dirs)
+ @echo ' Building modules, stage 2.';
+ $(Q)$(MAKE) -rR -f $(srctree)/scripts/Makefile.modpost
+
+PHONY += modules_install
+modules_install: _emodinst_ _emodinst_post
+
+install-dir := $(if $(INSTALL_MOD_DIR),$(INSTALL_MOD_DIR),extra)
+PHONY += _emodinst_
+_emodinst_:
+ $(Q)mkdir -p $(MODLIB)/$(install-dir)
+ $(Q)$(MAKE) -rR -f $(srctree)/scripts/Makefile.modinst
+
+# Run depmod only is we have System.map and depmod is executable
+quiet_cmd_depmod = DEPMOD $(KERNELRELEASE)
+ cmd_depmod = if [ -r System.map -a -x $(DEPMOD) ]; then \
+ $(DEPMOD) -ae -F System.map \
+ $(if $(strip $(INSTALL_MOD_PATH)), \
+ -b $(INSTALL_MOD_PATH) -r) \
+ $(KERNELRELEASE); \
+ fi
+
+PHONY += _emodinst_post
+_emodinst_post: _emodinst_
+ $(call cmd,depmod)
+
+clean-dirs := $(addprefix _clean_,$(KBUILD_EXTMOD))
+
+PHONY += $(clean-dirs) clean
+$(clean-dirs):
+ $(Q)$(MAKE) $(clean)=$(patsubst _clean_%,%,$@)
+
+clean: rm-dirs := $(MODVERDIR)
+clean: $(clean-dirs)
+ $(call cmd,rmdirs)
+ @find $(KBUILD_EXTMOD) $(RCS_FIND_IGNORE) \
+ \( -name '*.[oas]' -o -name '*.ko' -o -name '.*.cmd' \
+ -o -name '.*.d' -o -name '.*.tmp' -o -name '*.mod.c' \) \
+ -type f -print | xargs rm -f
+
+help:
+ @echo ' Building external modules.'
+ @echo ' Syntax: make -C path/to/kernel/src M=$$PWD target'
+ @echo ''
+ @echo ' modules - default target, build the module(s)'
+ @echo ' modules_install - install the module'
+ @echo ' clean - remove generated files in module directory only'
+ @echo ''
+
+# Dummies...
+PHONY += prepare scripts
+prepare: ;
+scripts: ;
+endif # KBUILD_EXTMOD
+
+# Generate tags for editors
+# ---------------------------------------------------------------------------
+
+#We want __srctree to totally vanish out when KBUILD_OUTPUT is not set
+#(which is the most common case IMHO) to avoid unneeded clutter in the big tags file.
+#Adding $(srctree) adds about 20M on i386 to the size of the output file!
+
+ifeq ($(src),$(obj))
+__srctree =
+else
+__srctree = $(srctree)/
+endif
+
+ifeq ($(ALLSOURCE_ARCHS),)
+ifeq ($(ARCH),um)
+ALLINCLUDE_ARCHS := $(ARCH) $(SUBARCH)
+else
+ALLINCLUDE_ARCHS := $(ARCH)
+endif
+else
+#Allow user to specify only ALLSOURCE_PATHS on the command line, keeping existing behaviour.
+ALLINCLUDE_ARCHS := $(ALLSOURCE_ARCHS)
+endif
+
+ALLSOURCE_ARCHS := $(ARCH)
+
+define all-sources
+ ( find $(__srctree) $(RCS_FIND_IGNORE) \
+ \( -name include -o -name arch \) -prune -o \
+ -name '*.[chS]' -print; \
+ for ARCH in $(ALLSOURCE_ARCHS) ; do \
+ find $(__srctree)arch/$${ARCH} $(RCS_FIND_IGNORE) \
+ -name '*.[chS]' -print; \
+ done ; \
+ find $(__srctree)security/selinux/include $(RCS_FIND_IGNORE) \
+ -name '*.[chS]' -print; \
+ find $(__srctree)include $(RCS_FIND_IGNORE) \
+ \( -name config -o -name 'asm-*' \) -prune \
+ -o -name '*.[chS]' -print; \
+ for ARCH in $(ALLINCLUDE_ARCHS) ; do \
+ find $(__srctree)include/asm-$${ARCH} $(RCS_FIND_IGNORE) \
+ -name '*.[chS]' -print; \
+ done ; \
+ find $(__srctree)include/asm-generic $(RCS_FIND_IGNORE) \
+ -name '*.[chS]' -print )
+endef
+
+quiet_cmd_cscope-file = FILELST cscope.files
+ cmd_cscope-file = (echo \-k; echo \-q; $(all-sources)) > cscope.files
+
+quiet_cmd_cscope = MAKE cscope.out
+ cmd_cscope = cscope -b
+
+cscope: FORCE
+ $(call cmd,cscope-file)
+ $(call cmd,cscope)
+
+quiet_cmd_TAGS = MAKE $@
+define cmd_TAGS
+ rm -f $@; \
+ ETAGSF=`etags --version | grep -i exuberant >/dev/null && \
+ echo "-I __initdata,__exitdata,__acquires,__releases \
+ -I EXPORT_SYMBOL,EXPORT_SYMBOL_GPL \
+ --extra=+f --c-kinds=+px"`; \
+ $(all-sources) | xargs etags $$ETAGSF -a
+endef
+
+TAGS: FORCE
+ $(call cmd,TAGS)
+
+
+quiet_cmd_tags = MAKE $@
+define cmd_tags
+ rm -f $@; \
+ CTAGSF=`ctags --version | grep -i exuberant >/dev/null && \
+ echo "-I __initdata,__exitdata,__acquires,__releases \
+ -I EXPORT_SYMBOL,EXPORT_SYMBOL_GPL \
+ --extra=+f --c-kinds=+px"`; \
+ $(all-sources) | xargs ctags $$CTAGSF -a
+endef
+
+tags: FORCE
+ $(call cmd,tags)
+
+
+# Scripts to check various things for consistency
+# ---------------------------------------------------------------------------
+
+includecheck:
+ find * $(RCS_FIND_IGNORE) \
+ -name '*.[hcS]' -type f -print | sort \
+ | xargs $(PERL) -w scripts/checkincludes.pl
+
+versioncheck:
+ find * $(RCS_FIND_IGNORE) \
+ -name '*.[hcS]' -type f -print | sort \
+ | xargs $(PERL) -w scripts/checkversion.pl
+
+namespacecheck:
+ $(PERL) $(srctree)/scripts/namespace.pl
+
+endif #ifeq ($(config-targets),1)
+endif #ifeq ($(mixed-targets),1)
+
+PHONY += checkstack
+checkstack:
+ $(OBJDUMP) -d busybox $$(find . -name '*.ko') | \
+ $(PERL) $(src)/scripts/checkstack.pl $(ARCH)
+
+kernelrelease:
+ $(if $(wildcard .kernelrelease), $(Q)echo $(KERNELRELEASE), \
+ $(error kernelrelease not valid - run 'make *config' to update it))
+kernelversion:
+ @echo $(KERNELVERSION)
+
+# Single targets
+# ---------------------------------------------------------------------------
+# Single targets are compatible with:
+# - build whith mixed source and output
+# - build with separate output dir 'make O=...'
+# - external modules
+#
+# target-dir => where to store outputfile
+# build-dir => directory in kernel source tree to use
+
+ifeq ($(KBUILD_EXTMOD),)
+ build-dir = $(patsubst %/,%,$(dir $@))
+ target-dir = $(dir $@)
+else
+ zap-slash=$(filter-out .,$(patsubst %/,%,$(dir $@)))
+ build-dir = $(KBUILD_EXTMOD)$(if $(zap-slash),/$(zap-slash))
+ target-dir = $(if $(KBUILD_EXTMOD),$(dir $<),$(dir $@))
+endif
+
+%.s: %.c prepare scripts FORCE
+ $(Q)$(MAKE) $(build)=$(build-dir) $(target-dir)$(notdir $@)
+%.i: %.c prepare scripts FORCE
+ $(Q)$(MAKE) $(build)=$(build-dir) $(target-dir)$(notdir $@)
+%.o: %.c prepare scripts FORCE
+ $(Q)$(MAKE) $(build)=$(build-dir) $(target-dir)$(notdir $@)
+%.lst: %.c prepare scripts FORCE
+ $(Q)$(MAKE) $(build)=$(build-dir) $(target-dir)$(notdir $@)
+%.s: %.S prepare scripts FORCE
+ $(Q)$(MAKE) $(build)=$(build-dir) $(target-dir)$(notdir $@)
+%.o: %.S prepare scripts FORCE
+ $(Q)$(MAKE) $(build)=$(build-dir) $(target-dir)$(notdir $@)
+
+# Modules
+/ %/: prepare scripts FORCE
+ $(Q)$(MAKE) KBUILD_MODULES=$(if $(CONFIG_MODULES),1) \
+ $(build)=$(build-dir)
+%.ko: prepare scripts FORCE
+ $(Q)$(MAKE) KBUILD_MODULES=$(if $(CONFIG_MODULES),1) \
+ $(build)=$(build-dir) $(@:.ko=.o)
+ $(Q)$(MAKE) -rR -f $(srctree)/scripts/Makefile.modpost
+
+# FIXME Should go into a make.lib or something
+# ===========================================================================
+
+quiet_cmd_rmdirs = $(if $(wildcard $(rm-dirs)),CLEAN $(wildcard $(rm-dirs)))
+ cmd_rmdirs = rm -rf $(rm-dirs)
+
+quiet_cmd_rmfiles = $(if $(wildcard $(rm-files)),CLEAN $(wildcard $(rm-files)))
+ cmd_rmfiles = rm -f $(rm-files)
+
+
+a_flags = -Wp,-MD,$(depfile) $(AFLAGS) $(AFLAGS_KERNEL) \
+ $(NOSTDINC_FLAGS) $(CPPFLAGS) \
+ $(modkern_aflags) $(EXTRA_AFLAGS) $(AFLAGS_$(*F).o)
+
+quiet_cmd_as_o_S = AS $@
+cmd_as_o_S = $(CC) $(a_flags) -c -o $@ $<
+
+# read all saved command lines
+
+targets := $(wildcard $(sort $(targets)))
+cmd_files := $(wildcard .*.cmd $(foreach f,$(targets),$(dir $(f)).$(notdir $(f)).cmd))
+
+ifneq ($(cmd_files),)
+ $(cmd_files): ; # Do not try to update included dependency files
+ include $(cmd_files)
+endif
+
+# Shorthand for $(Q)$(MAKE) -f scripts/Makefile.clean obj=dir
+# Usage:
+# $(Q)$(MAKE) $(clean)=dir
+clean := -f $(if $(KBUILD_SRC),$(srctree)/)scripts/Makefile.clean obj
+
+endif # skip-makefile
+
+PHONY += FORCE
+FORCE:
+
+-include $(srctree)/Makefile.custom
+
+# Declare the contents of the .PHONY variable as phony. We keep that
+# information in a variable se we can use it in if_changed and friends.
+.PHONY: $(PHONY)
diff --git a/i/pc104/initrd/conf/busybox/Makefile.custom b/i/pc104/initrd/conf/busybox/Makefile.custom
new file mode 100644
index 0000000..0cc40a6
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/Makefile.custom
@@ -0,0 +1,145 @@
+# ==========================================================================
+# Build system
+# ==========================================================================
+
+%.bflt: %_unstripped
+ $(CROSS_COMPILE)elf2flt $(ELF2FLTFLAGS) $< -o $@
+
+busybox.links: $(srctree)/applets/busybox.mkll $(objtree)/include/autoconf.h $(srctree)/include/applets.h
+ $(Q)-$(SHELL) $^ >$@
+
+.PHONY: install
+ifeq ($(CONFIG_INSTALL_APPLET_SYMLINKS),y)
+INSTALL_OPTS:= --symlinks
+endif
+ifeq ($(CONFIG_INSTALL_APPLET_HARDLINKS),y)
+INSTALL_OPTS:= --hardlinks
+endif
+install: $(srctree)/applets/install.sh busybox busybox.links
+ $(Q)DO_INSTALL_LIBS="$(strip $(LIBBUSYBOX_SONAME) $(DO_INSTALL_LIBS))" \
+ $(SHELL) $< $(CONFIG_PREFIX) $(INSTALL_OPTS)
+ifeq ($(strip $(CONFIG_FEATURE_SUID)),y)
+ @echo
+ @echo
+ @echo --------------------------------------------------
+ @echo You will probably need to make your busybox binary
+ @echo setuid root to ensure all configured applets will
+ @echo work properly.
+ @echo --------------------------------------------------
+ @echo
+endif
+
+uninstall: busybox.links
+ rm -f $(CONFIG_PREFIX)/bin/busybox
+ for i in `cat busybox.links` ; do rm -f $(CONFIG_PREFIX)$$i; done
+ifneq ($(strip $(DO_INSTALL_LIBS)),n)
+ for i in $(LIBBUSYBOX_SONAME) $(DO_INSTALL_LIBS); do \
+ rm -f $(CONFIG_PREFIX)$$i; \
+ done
+endif
+
+check test: busybox busybox.links
+ bindir=$(objtree) srcdir=$(srctree)/testsuite SED="$(SED)" \
+ $(SHELL) $(srctree)/testsuite/runtest $(if $(KBUILD_VERBOSE:0=),-v)
+
+.PHONY: release
+release: distclean
+ cd ..; \
+ rm -r -f busybox-$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION); \
+ cp -a busybox busybox-$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) && { \
+ find busybox-$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)/ -type d \
+ -name .svn \
+ -print \
+ -exec rm -r -f {} \; ; \
+ find busybox-$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)/ -type f \
+ -name .\#* \
+ -print \
+ -exec rm -f {} \; ; \
+ tar -czf busybox-$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION).tar.gz \
+ busybox-$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)/ ; }
+
+.PHONY: checkhelp
+checkhelp:
+ $(Q)$(srctree)/scripts/checkhelp.awk \
+ $(patsubst %,$(srctree)/%,$(wildcard $(patsubst %,%/Config.in,$(busybox-dirs) ./)))
+
+.PHONY: sizes
+sizes: busybox_unstripped
+ $(NM) --size-sort $(<)
+
+.PHONY: bloatcheck
+bloatcheck: busybox_old busybox_unstripped
+ @$(srctree)/scripts/bloat-o-meter busybox_old busybox_unstripped
+
+.PHONY: baseline
+baseline: busybox_unstripped
+ @mv busybox_unstripped busybox_old
+
+.PHONY: objsizes
+objsizes: busybox_unstripped
+ $(srctree)/scripts/objsizes
+
+.PHONY: bigdata
+bigdata: busybox_unstripped
+ nm --size-sort busybox_unstripped | grep -vi ' [tr] ' | tail -20
+
+# Documentation Targets
+.PHONY: doc
+doc: docs/busybox.pod docs/BusyBox.txt docs/BusyBox.1 docs/BusyBox.html
+
+docs/busybox.pod: $(srctree)/docs/busybox_header.pod \
+ $(srctree)/include/usage.h \
+ $(srctree)/docs/busybox_footer.pod \
+ $(srctree)/docs/autodocifier.pl
+ $(disp_doc)
+ $(Q)-mkdir -p docs
+ $(Q)-( cat $(srctree)/docs/busybox_header.pod ; \
+ $(srctree)/docs/autodocifier.pl $(srctree)/include/usage.h ; \
+ cat $(srctree)/docs/busybox_footer.pod ; ) > docs/busybox.pod
+
+docs/BusyBox.txt: docs/busybox.pod
+ $(disp_doc)
+ $(Q)-mkdir -p docs
+ $(Q)-pod2text $< > $@
+
+docs/BusyBox.1: docs/busybox.pod
+ $(disp_doc)
+ $(Q)-mkdir -p docs
+ $(Q)-pod2man --center=BusyBox --release="version $(VERSION)" \
+ $< > $@
+
+docs/BusyBox.html: docs/busybox.net/BusyBox.html
+ $(disp_doc)
+ $(Q)-mkdir -p docs
+ $(Q)-rm -f docs/BusyBox.html
+ $(Q)-cp docs/busybox.net/BusyBox.html docs/BusyBox.html
+
+docs/busybox.net/BusyBox.html: docs/busybox.pod
+ $(Q)-mkdir -p docs/busybox.net
+ $(Q)-pod2html --noindex $< > \
+ docs/busybox.net/BusyBox.html
+ $(Q)-rm -f pod2htm*
+
+# documentation, cross-reference
+# Modern distributions already ship synopsis packages (e.g. debian)
+# If you have an old distribution go to http://synopsis.fresco.org/
+syn_tgt = $(wildcard $(patsubst %,%/*.c,$(busybox-alldirs)))
+syn = $(patsubst %.c, %.syn, $(syn_tgt))
+
+comma:= ,
+brace_open:= (
+brace_close:= )
+
+SYN_CPPFLAGS := $(strip $(CPPFLAGS) $(EXTRA_CPPFLAGS))
+SYN_CPPFLAGS := $(subst $(brace_open),\$(brace_open),$(SYN_CPPFLAGS))
+SYN_CPPFLAGS := $(subst $(brace_close),\$(brace_close),$(SYN_CPPFLAGS))
+#SYN_CPPFLAGS := $(subst ",\",$(SYN_CPPFLAGS))
+#")
+#SYN_CPPFLAGS := [$(patsubst %,'%'$(comma),$(SYN_CPPFLAGS))'']
+
+%.syn: %.c
+ synopsis -p C -l Comments.SSDFilter,Comments.Previous -Wp,preprocess=True,cppflags="'$(SYN_CPPFLAGS)'" -o $@ $<
+
+.PHONY: html
+html: $(syn)
+ synopsis -f HTML -Wf,title="'BusyBox Documentation'" -o $@ $^
diff --git a/i/pc104/initrd/conf/busybox/Makefile.flags b/i/pc104/initrd/conf/busybox/Makefile.flags
new file mode 100644
index 0000000..b7711f6
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/Makefile.flags
@@ -0,0 +1,61 @@
+# ==========================================================================
+# Build system
+# ==========================================================================
+
+BB_VER = $(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
+
+# -std=gnu99 needed for [U]LLONG_MAX on some systems
+CPPFLAGS += $(call cc-option,-std=gnu99,)
+
+CPPFLAGS += \
+ -Iinclude -Ilibbb \
+ $(if $(KBUILD_SRC),-Iinclude2 -I$(srctree)/include) -I$(srctree)/libbb \
+ -include include/autoconf.h \
+ -D_GNU_SOURCE -DNDEBUG \
+ $(if $(CONFIG_LFS),-D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64) \
+ -D"BB_VER=KBUILD_STR($(BB_VER))" -DBB_BT=AUTOCONF_TIMESTAMP
+
+# flag checks are grouped together to speed the checks up a bit..
+CFLAGS += $(call cc-option,-Wall -Wshadow -Wwrite-strings,)
+CFLAGS += $(call cc-option,-Wundef -Wstrict-prototypes,)
+# If you want to add "-Wmissing-prototypes -Wmissing-declarations" above
+# (or anything else for that matter) make sure that it is still possible
+# to build bbox without warnings. Current offender: find.c:alloc_action().
+# Looks more like gcc bug: gcc will warn on it with or without prototype.
+# But still, warning-free compile is a must, or else we will drown
+# in warnings pretty soon.
+
+ifeq ($(CONFIG_WERROR),y)
+CFLAGS += $(call cc-option,-Werror,)
+endif
+# gcc 3.x emits bogus "old style proto" warning on find.c:alloc_action()
+CFLAGS += $(call cc-ifversion, -ge, 0400, -Wold-style-definition)
+
+# gcc emits bogus "no prev proto" warning on find.c:alloc_action()
+ifneq ($(CONFIG_WERROR),y)
+CFLAGS += $(call cc-option,-Wmissing-prototypes -Wmissing-declarations,)
+endif
+
+CFLAGS += $(call cc-option,-Os -fno-builtin-strlen -finline-limit=0 -fomit-frame-pointer -ffunction-sections -fdata-sections,)
+# -fno-guess-branch-probability: prohibit pseudo-random guessing
+# of branch probabilities (hopefully makes bloatcheck more stable):
+CFLAGS += $(call cc-option,-fno-guess-branch-probability,)
+CFLAGS += $(call cc-option,-funsigned-char -static-libgcc,)
+CFLAGS += $(call cc-option,-falign-functions=1 -falign-jumps=1 -falign-labels=1 -falign-loops=1,)
+
+# FIXME: These warnings are at least partially to be concerned about and should
+# be fixed..
+#CFLAGS+=$(call cc-option,-Wconversion,)
+
+ifeq ($(CONFIG_DEBUG),y)
+CFLAGS += $(call cc-option,-g)
+endif
+
+ifeq ($(CONFIG_STATIC),y)
+LDFLAGS += -static
+endif
+
+ifeq ($(CONFIG_SELINUX),y)
+LDFLAGS += -lselinux -lsepol
+endif
+#LDFLAGS += -nostdlib
diff --git a/i/pc104/initrd/conf/busybox/Makefile.help b/i/pc104/initrd/conf/busybox/Makefile.help
new file mode 100644
index 0000000..fc661b1
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/Makefile.help
@@ -0,0 +1,42 @@
+# ==========================================================================
+# Build system
+# ==========================================================================
+
+help:
+ @echo 'Cleaning:'
+ @echo ' clean - delete temporary files created by build'
+ @echo ' distclean - delete all non-source files (including .config)'
+ @echo
+ @echo 'Build:'
+ @echo ' all - Executable and documentation'
+ @echo ' busybox - the swiss-army executable'
+ @echo ' doc - docs/BusyBox.{txt,html,1}'
+ @echo ' html - create html-based cross-reference'
+ @echo
+ @echo 'Configuration:'
+ @echo ' allnoconfig - disable all symbols in .config'
+ @echo ' allyesconfig - enable all symbols in .config (see defconfig)'
+ @echo ' allbareconfig - enable all applets without any sub-features'
+ @echo ' config - text based configurator (of last resort)'
+ @echo ' defconfig - set .config to largest generic configuration'
+ @echo ' menuconfig - interactive curses-based configurator'
+ @echo ' oldconfig - resolve any unresolved symbols in .config'
+ @echo ' hosttools - build sed for the host.'
+ @echo ' You can use these commands if the commands on the host'
+ @echo ' is unusable. Afterwards use it like:'
+ @echo ' make SED="$(objtree)/sed"'
+ @echo
+ @echo 'Installation:'
+ @echo ' install - install busybox into $(CONFIG_PREFIX)'
+ @echo ' uninstall'
+ @echo
+ @echo 'Development:'
+ @echo ' baseline - create busybox_old for bloatcheck.'
+ @echo ' bloatcheck - show size difference between old and new versions'
+ @echo ' check - run the test suite for all applets'
+ @echo ' checkhelp - check for missing help-entries in Config.in'
+ @echo ' randconfig - generate a random configuration'
+ @echo ' release - create a distribution tarball'
+ @echo ' sizes - show size of all enabled busybox symbols'
+ @echo ' objsizes - show size of each .o object built'
+ @echo
diff --git a/i/pc104/initrd/conf/busybox/README b/i/pc104/initrd/conf/busybox/README
new file mode 100644
index 0000000..96ca0bb
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/README
@@ -0,0 +1,198 @@
+Please see the LICENSE file for details on copying and usage.
+Please refer to the INSTALL file for instructions on how to build.
+
+What is busybox:
+
+ BusyBox combines tiny versions of many common UNIX utilities into a single
+ small executable. It provides minimalist replacements for most of the
+ utilities you usually find in bzip2, coreutils, dhcp, diffutils, e2fsprogs,
+ file, findutils, gawk, grep, inetutils, less, modutils, net-tools, procps,
+ sed, shadow, sysklogd, sysvinit, tar, util-linux, and vim. The utilities
+ in BusyBox often have fewer options than their full-featured cousins;
+ however, the options that are included provide the expected functionality
+ and behave very much like their larger counterparts.
+
+ BusyBox has been written with size-optimization and limited resources in
+ mind, both to produce small binaries and to reduce run-time memory usage.
+ Busybox is also extremely modular so you can easily include or exclude
+ commands (or features) at compile time. This makes it easy to customize
+ embedded systems; to create a working system, just add /dev, /etc, and a
+ Linux kernel. Busybox (usually together with uClibc) has also been used as
+ a component of "thin client" desktop systems, live-CD distributions, rescue
+ disks, installers, and so on.
+
+ BusyBox provides a fairly complete POSIX environment for any small system,
+ both embedded environments and more full featured systems concerned about
+ space. Busybox is slowly working towards implementing the full Single Unix
+ Specification V3 (http://www.opengroup.org/onlinepubs/009695399/), but isn't
+ there yet (and for size reasons will probably support at most UTF-8 for
+ internationalization). We are also interested in passing the Linux Test
+ Project (http://ltp.sourceforge.net).
+
+----------------
+
+Using busybox:
+
+ BusyBox is extremely configurable. This allows you to include only the
+ components and options you need, thereby reducing binary size. Run 'make
+ config' or 'make menuconfig' to select the functionality that you wish to
+ enable. (See 'make help' for more commands.)
+
+ The behavior of busybox is determined by the name it's called under: as
+ "cp" it behaves like cp, as "sed" it behaves like sed, and so on. Called
+ as "busybox" it takes the second argument as the name of the applet to
+ run (I.E. "./busybox ls -l /proc").
+
+ The "standalone shell" mode is an easy way to try out busybox; this is a
+ command shell that calls the builtin applets without needing them to be
+ installed in the path. (Note that this requires /proc to be mounted, if
+ testing from a boot floppy or in a chroot environment.)
+
+ The build automatically generates a file "busybox.links", which is used by
+ 'make install' to create symlinks to the BusyBox binary for all compiled in
+ commands. This uses the CONFIG_PREFIX environment variable to specify
+ where to install, and installs hardlinks or symlinks depending
+ on the configuration preferences. (You can also manually run
+ the install script at "applets/install.sh").
+
+----------------
+
+Downloading the current source code:
+
+ Source for the latest released version, as well as daily snapshots, can always
+ be downloaded from
+
+ http://busybox.net/downloads/
+
+ You can browse the up to the minute source code and change history online.
+
+ http://www.busybox.net/cgi-bin/viewcvs.cgi/trunk/busybox/
+
+ Anonymous SVN access is available. For instructions, check out:
+
+ http://busybox.net/subversion.html
+
+ For those that are actively contributing and would like to check files in,
+ see:
+
+ http://busybox.net/developer.html
+
+ The developers also have a bug and patch tracking system
+ (http://bugs.busybox.net) although posting a bug/patch to the mailing list
+ is generally a faster way of getting it fixed, and the complete archive of
+ what happened is the subversion changelog.
+
+----------------
+
+getting help:
+
+ when you find you need help, you can check out the busybox mailing list
+ archives at http://busybox.net/lists/busybox/ or even join
+ the mailing list if you are interested.
+
+----------------
+
+bugs:
+
+ if you find bugs, please submit a detailed bug report to the busybox mailing
+ list at busybox@busybox.net. a well-written bug report should include a
+ transcript of a shell session that demonstrates the bad behavior and enables
+ anyone else to duplicate the bug on their own machine. the following is such
+ an example:
+
+ to: busybox@busybox.net
+ from: diligent@testing.linux.org
+ subject: /bin/date doesn't work
+
+ package: busybox
+ version: 1.00
+
+ when i execute busybox 'date' it produces unexpected results.
+ with gnu date i get the following output:
+
+ $ date
+ fri oct 8 14:19:41 mdt 2004
+
+ but when i use busybox date i get this instead:
+
+ $ date
+ illegal instruction
+
+ i am using debian unstable, kernel version 2.4.25-vrs2 on a netwinder,
+ and the latest uclibc from cvs. thanks for the wonderful program!
+
+ -diligent
+
+ note the careful description and use of examples showing not only what
+ busybox does, but also a counter example showing what an equivalent app
+ does (or pointing to the text of a relevant standard). Bug reports lacking
+ such detail may never be fixed... Thanks for understanding.
+
+----------------
+
+Portability:
+
+ Busybox is developed and tested on Linux 2.4 and 2.6 kernels, compiled
+ with gcc (the unit-at-a-time optimizations in version 3.4 and later are
+ worth upgrading to get, but older versions should work), and linked against
+ uClibc (0.9.27 or greater) or glibc (2.2 or greater). In such an
+ environment, the full set of busybox features should work, and if
+ anything doesn't we want to know about it so we can fix it.
+
+ There are many other environments out there, in which busybox may build
+ and run just fine. We just don't test them. Since busybox consists of a
+ large number of more or less independent applets, portability is a question
+ of which features work where. Some busybox applets (such as cat and rm) are
+ highly portable and likely to work just about anywhere, while others (such as
+ insmod and losetup) require recent Linux kernels with recent C libraries.
+
+ Earlier versions of Linux and glibc may or may not work, for any given
+ configuration. Linux 2.2 or earlier should mostly work (there's still
+ some support code in things like mount.c) but this is no longer regularly
+ tested, and inherently won't support certain features (such as long files
+ and --bind mounts). The same is true for glibc 2.0 and 2.1: expect a higher
+ testing and debugging burden using such old infrastructure. (The busybox
+ developers are not very interested in supporting these older versions, but
+ will probably accept small self-contained patches to fix simple problems.)
+
+ Some environments are not recommended. Early versions of uClibc were buggy
+ and missing many features: upgrade. Linking against libc5 or dietlibc is
+ not supported and not interesting to the busybox developers. (The first is
+ obsolete and has no known size or feature advantages over uClibc, the second
+ has known bugs that its developers have actively refused to fix.) Ancient
+ Linux kernels (2.0.x and earlier) are similarly uninteresting.
+
+ In theory it's possible to use Busybox under other operating systems (such as
+ MacOS X, Solaris, Cygwin, or the BSD Fork Du Jour). This generally involves
+ a different kernel and a different C library at the same time. While it
+ should be possible to port the majority of the code to work in one of
+ these environments, don't be suprised if it doesn't work out of the box. If
+ you're into that sort of thing, start small (selecting just a few applets)
+ and work your way up.
+
+ Shaun Jackman has recently (2005) ported busybox to a combination of newlib
+ and libgloss, and some of his patches have been integrated. This platform
+ may join glibc/uclibc and Linux as a supported combination with the 1.1
+ release, but is not supported in 1.0.
+
+Supported hardware:
+
+ BusyBox in general will build on any architecture supported by gcc. We
+ support both 32 and 64 bit platforms, and both big and little endian
+ systems.
+
+ Under 2.4 Linux kernels, kernel module loading was implemented in a
+ platform-specific manner. Busybox's insmod utility has been reported to
+ work under ARM, CRIS, H8/300, x86, ia64, x86_64, m68k, MIPS, PowerPC, S390,
+ SH3/4/5, Sparc, v850e, and x86_64. Anything else probably won't work.
+
+ The module loading mechanism for the 2.6 kernel is much more generic, and
+ we believe 2.6.x kernel module loading support should work on all
+ architectures supported by the kernel.
+
+----------------
+
+Please feed suggestions, bug reports, insults, and bribes back to the busybox
+maintainer:
+ Denis Vlasenko
+ <vda.linux@googlemail.com>
diff --git a/i/pc104/initrd/conf/busybox/TODO b/i/pc104/initrd/conf/busybox/TODO
new file mode 100644
index 0000000..1896928
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/TODO
@@ -0,0 +1,320 @@
+Busybox TODO
+
+Stuff that needs to be done. This is organized by who plans to get around to
+doing it eventually, but that doesn't mean they "own" the item. If you want to
+do one of these bounce an email off the person it's listed under to see if they
+have any suggestions how they plan to go about it, and to minimize conflicts
+between your work and theirs. But otherwise, all of these are fair game.
+
+Rob Landley <rob@landley.net>:
+ Add BB_NOMMU to platform.h and migrate __uClinux__ tests to that.
+ #if defined __UCLIBC__ && !defined __ARCH_USE_MMU__
+ Add a libbb/platform.c
+ Implement fdprintf() for platforms that haven't got one.
+ Implement bb_realpath() that can handle NULL on non-glibc.
+ Cleanup bb_asprintf()
+
+ Migrate calloc() and bb_calloc() occurrences to bb_xzalloc().
+ Remove obsolete _() wrapper crud for internationalization we don't do.
+ Figure out where we need utf8 support, and add it.
+
+ sh
+ The command shell situation is a big mess. We have three or four different
+ shells that don't really share any code, and the "standalone shell" doesn't
+ work all that well (especially not in a chroot environment), due to apps not
+ being reentrant. I'm writing a new shell (bbsh) to unify the various
+ shells and configurably add the minimal set of bash features people
+ actually use. The hardest part is it has to configure down as small as
+ lash while providing lash's features. The rest is easy in comparison.
+ bzip2
+ Compression-side support.
+ init
+ General cleanup (should use ENABLE_FEATURE_INIT_SYSLOG and ENABLE_FEATURE_INIT_DEBUG).
+ depmod
+ busybox lacks a way to update module deps when running from firmware without the
+ use of the depmod.pl (perl is to bloated for most embedded setups) and or orig
+ modutils. The orig depmod is rather pointless to have to add to a firmware image
+ in when we already have a insmod/rmmod and friends.
+ Unify base64 handling.
+ There's base64 encoding and decoding going on in:
+ networking/wget.c:base64enc()
+ coreutils/uudecode.c:read_base64()
+ coreutils/uuencode.c:tbl_base64[]
+ networking/httpd.c:decodeBase64()
+ And probably elsewhere. That needs to be unified into libbb functions.
+ Do a SUSv3 audit
+ Look at the full Single Unix Specification version 3 (available online at
+ "http://www.opengroup.org/onlinepubs/009695399/nfindex.html") and
+ figure out which of our apps are compliant, and what we're missing that
+ we might actually care about.
+
+ Even better would be some kind of automated compliance test harness that
+ exercises each command line option and the various corner cases.
+ Internationalization
+ How much internationalization should we do?
+
+ The low hanging fruit is UTF-8 character set support. We should do this.
+ (Vodz pointed out the shell's cmdedit as needing work here. What else?)
+
+ We also have lots of hardwired english text messages. Consolidating this
+ into some kind of message table not only makes translation easier, but
+ also allows us to consolidate redundant (or close) strings.
+
+ We probably don't want to be bloated with locale support. (Not unless we
+ can cleanly export it from our underlying C library without having to
+ concern ourselves with it directly. Perhaps a few specific things like a
+ config option for "date" are low hanging fruit here?)
+
+ What level should things happen at? How much do we care about
+ internationalizing the text console when X11 and xterms are so much better
+ at it? (There's some infrastructure here we don't implement: The
+ "unicode_start" and "unicode_stop" shell scripts need "vt-is-UTF8" and a
+ --unicode option to loadkeys. That implies a real loadkeys/dumpkeys
+ implementation to replace loadkmap/dumpkmap. Plus messing with console font
+ loading. Is it worth it, or do we just say "use X"?)
+
+ Individual compilation of applets.
+ It would be nice if busybox had the option to compile to individual applets,
+ for people who want an alternate implementation less bloated than the gnu
+ utils (or simply with less political baggage), but without it being one big
+ executable.
+
+ Turning libbb into a real dll is another possibility, especially if libbb
+ could export some of the other library interfaces we've already more or less
+ got the code for (like zlib).
+ buildroot - Make a "dogfood" option
+ Busybox 1.1 will be capable of replacing most gnu packages for real world
+ use, such as developing software or in a live CD. It needs wider testing.
+
+ Busybox should now be able to replace bzip2, coreutils, e2fsprogs, file,
+ findutils, gawk, grep, inetutils, less, modutils, net-tools, patch, procps,
+ sed, shadow, sysklogd, sysvinit, tar, util-linux, and vim. The resulting
+ system should be self-hosting (I.E. able to rebuild itself from source
+ code). This means it would need (at least) binutils, gcc, and make, or
+ equivalents.
+
+ It would be a good "eating our own dogfood" test if buildroot had the option
+ of using a "make allyesconfig" busybox instead of the all of the above
+ packages. Anything that's wrong with the resulting system, we can fix. (It
+ would be nice to be able to upgrade busybox to be able to replace bash and
+ diffutils as well, but we're not there yet.)
+
+ One example of an existing system that does this already is Firmware Linux:
+ http://www.landley.net/code/firmware
+ initramfs
+ Busybox should have a sample initramfs build script. This depends on
+ bbsh, mdev, and switch_root.
+ mkdep
+ Write a mkdep that doesn't segfault if there's a directory it doesn't
+ have permission to read, isn't based on manually editing the output of
+ lexx and yacc, doesn't make such a mess under include/config, etc.
+ Group globals into unions of structures.
+ Go through and turn all the global and static variables into structures,
+ and have all those structures be in a big union shared between processes,
+ so busybox uses less bss. (This is a big win on nommu machines.) See
+ sed.c and mdev.c for examples.
+ Go through bugs.busybox.net and close out all of that somehow.
+ This one's open to everybody, but I'll wind up doing it...
+
+
+Bernhard Fischer <busybox@busybox.net> suggests to look at these:
+ New debug options:
+ -Wlarger-than-127
+ Cleanup any big users
+ -Wunused-parameter
+ Facilitate applet PROTOTYPES to provide means for having applets that
+ do a) not take any arguments b) need only one of argc or argv c) need
+ both argc and argv. All of these three options should go for the most
+ feature complete denominator.
+ Collate BUFSIZ IOBUF_SIZE MY_BUF_SIZE PIPE_PROGRESS_SIZE BUFSIZE PIPESIZE
+ make bb_common_bufsiz1 configurable, size wise.
+ make pipesize configurable, size wise.
+ Use bb_common_bufsiz1 throughout applets!
+
+As yet unclaimed:
+
+----
+find
+ doesn't understand (), lots of susv3 stuff.
+----
+diff
+ Make sure we handle empty files properly:
+ From the patch man page:
+
+ you can remove a file by sending out a context diff that compares
+ the file to be deleted with an empty file dated the Epoch. The
+ file will be removed unless patch is conforming to POSIX and the
+ -E or --remove-empty-files option is not given.
+---
+patch
+ Should have simple fuzz factor support to apply patches at an offset which
+ shouldn't take up too much space.
+
+ And while we're at it, a new patch filename quoting format is apparently
+ coming soon: http://marc.theaimsgroup.com/?l=git&m=112927316408690&w=2
+---
+ps / top
+ Add support for both RSS and VSIZE rather than just one or the other.
+ Or make it a build option.
+---
+man
+ It would be nice to have a man command. Not one that handles troff or
+ anything, just one that can handle preformatted ascii man pages, possibly
+ compressed. This could probably be a script in the extras directory that
+ calls cat/zcat/bzcat | less
+
+ (How doclifter might work into this is anybody's guess.)
+---
+ar
+ Write support?
+----
+stty / catv
+ stty's visible() function and catv's guts are identical. Merge them into
+ an appropriate libbb function.
+----
+struct suffix_mult
+ Several duplicate users of: grep -r "1024\*1024" * -B2 -A1
+ Merge to a single size_suffixes[] in libbb.
+ Users: head tail od_bloaty hexdump and (partially as it wouldn't hurt) svlogd
+
+Architectural issues:
+
+bb_close() with fsync()
+ We should have a bb_close() in place of normal close, with a CONFIG_ option
+ to not just check the return value of close() for an error, but fsync().
+ Close can't reliably report anything useful because if write() accepted the
+ data then it either went out to the network or it's in cache or a pipe
+ buffer. Either way, there's no guarantee it'll make it to its final
+ destination before close() gets called, so there's no guarantee that any
+ error will be reported.
+
+ You need to call fsync() if you care about errors that occur after write(),
+ but that can have a big performance impact. So make it a config option.
+---
+Unify archivers
+ Lots of archivers have the same general infrastructure. The directory
+ traversal code should be factored out, and the guts of each archiver could
+ be some setup code and a series of callbacks for "add this file",
+ "add this directory", "add this symlink" and so on.
+
+ This could clean up tar and zip, and make it cheaper to add cpio and ar
+ write support, and possibly even cheaply add things like mkisofs or
+ mksquashfs someday, if they become relevant.
+---
+Text buffer support.
+ Several existing applets (sort, vi, less...) read
+ a whole file into memory and act on it. There might be an opportunity
+ for shared code in there that could be moved into libbb...
+---
+Memory Allocation
+ We have a CONFIG_BUFFER mechanism that lets us select whether to do memory
+ allocation on the stack or the heap. Unfortunately, we're not using it much.
+ We need to audit our memory allocations and turn a lot of malloc/free calls
+ into RESERVE_CONFIG_BUFFER/RELEASE_CONFIG_BUFFER.
+ For a start, see e.g. make EXTRA_CFLAGS=-Wlarger-than-64
+
+ And while we're at it, many of the CONFIG_FEATURE_CLEAN_UP #ifdefs will be
+ optimized out by the compiler in the stack allocation case (since there's no
+ free for an alloca()), and this means that various cleanup loops that just
+ call free might also be optimized out by the compiler if written right, so
+ we can yank those #ifdefs too, and generally clean up the code.
+---
+Switch CONFIG_SYMBOLS to ENABLE_SYMBOLS
+
+ In busybox 1.0 and earlier, configuration was done by CONFIG_SYMBOLS
+ that were either defined or undefined to indicate whether the symbol was
+ selected in the .config file. They were used with #ifdefs, ala:
+
+ #ifdef CONFIG_SYMBOL
+ if (other_test) {
+ do_code();
+ }
+ #endif
+
+ In 1.1, we have new ENABLE_SYMBOLS which are always defined (as 0 or 1),
+ meaning you can still use them for preprocessor tests by replacing
+ "#ifdef CONFIG_SYMBOL" with "#if ENABLE_SYMBOL". But more importantly, we
+ can use them as a true or false test in normal C code:
+
+ if (ENABLE_SYMBOL && other_test) {
+ do_code();
+ }
+
+ (Optimizing away if() statements that resolve to a constant value
+ is known as "dead code elimination", an optimization so old and simple that
+ Turbo Pascal for DOS did it twenty years ago. Even modern mini-compilers
+ like the Tiny C Compiler (tcc) and the Small Device C Compiler (SDCC)
+ perform dead code elimination.)
+
+ Right now, busybox.h is #including both "config.h" (defining the
+ CONFIG_SYMBOLS) and "bb_config.h" (defining the ENABLE_SYMBOLS). At some
+ point in the future, it would be nice to wean ourselves off of the
+ CONFIG versions. (Among other things, some defective build environments
+ leak the Linux kernel's CONFIG_SYMBOLS into the system's standard #include
+ files. We've experienced collisions before.)
+---
+FEATURE_CLEAN_UP
+ This is more an unresolved issue than a to-do item. More thought is needed.
+
+ Normally we rely on exit() to free memory, close files, and unmap segments
+ for us. This makes most calls to free(), close(), and unmap() optional in
+ busybox applets that don't intend to run for very long, and optional stuff
+ can be omitted to save size.
+
+ The idea was raised that we could simulate fork/exit with setjmp/longjmp
+ for _really_ brainless embedded systems, or speed up the standalone shell
+ by not forking. Doing so would require a reliable FEATURE_CLEAN_UP.
+ Unfortunately, this isn't as easy as it sounds.
+
+ The problem is, lots of things exit(), sometimes unexpectedly (xmalloc())
+ and sometimes reliably (bb_perror_msg_and_die() or show_usage()). This
+ jumps out of the normal flow control and bypasses any cleanup code we
+ put at the end of our applets.
+
+ It's possible to add hooks to libbb functions like xmalloc() and xopen()
+ to add their entries to a linked list, which could be traversed and
+ freed/closed automatically. (This would need to be able to free just the
+ entries after a checkpoint to be usable for a forkless standalone shell.
+ You don't want to free the shell's own resources.)
+
+ Right now, FEATURE_CLEAN_UP is more or less a debugging aid, to make things
+ like valgrind happy. It's also documentation of _what_ we're trusting
+ exit() to clean up for us. But new infrastructure to auto-free stuff would
+ render the existing FEATURE_CLEAN_UP code redundant.
+
+ For right now, exit() handles it just fine.
+
+
+
+Minor stuff:
+ watchdog.c could autodetect the timer duration via:
+ if(!ioctl (fd, WDIOC_GETTIMEOUT, &tmo)) timer_duration = 1 + (tmo / 2);
+ Unfortunately, that needs linux/watchdog.h and that contains unfiltered
+ kernel types on some distros, which breaks the build.
+---
+ use bb_error_msg where appropriate: See
+ egrep "(printf.*\([[:space:]]*(stderr|2)|[^_]write.*\([[:space:]]*(stderr|2))"
+---
+ use bb_perror_msg where appropriate: See
+ egrep "[^_]perror"
+---
+ Remove superfluous fmt occurances: e.g.
+ fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
+ -> fprintf(stderr, "unalias: %s not found\n", *argptr);
+---
+ possible code duplication ingroup() and is_a_group_member()
+---
+ Move __get_hz() to a better place and (re)use it in route.c, ash.c, msh.c
+---
+
+
+Code cleanup:
+
+Replace deprecated functions.
+
+bzero() -> memset()
+---
+sigblock(), siggetmask(), sigsetmask(), sigmask() -> sigprocmask et al
+---
+vdprintf() -> similar sized functionality
+---
diff --git a/i/pc104/initrd/conf/busybox/applets/Kbuild b/i/pc104/initrd/conf/busybox/applets/Kbuild
new file mode 100644
index 0000000..4323e85
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/applets/Kbuild
@@ -0,0 +1,22 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under the GPL v2, see the file LICENSE in this tarball.
+
+obj-y:=
+obj-y += applets.o
+obj-y += busybox.o
+
+# Generated file needs additional love
+
+applets/applets.o: .config include/usage_compressed.h
+
+hostprogs-y += usage
+always := $(hostprogs-y)
+HOSTCFLAGS_usage.o = -I$(srctree)/include
+
+quiet_cmd_gen_usage_compressed = GEN include/usage_compressed.h
+ cmd_gen_usage_compressed = $(srctree)/applets/usage_compressed include/usage_compressed.h applets
+include/usage_compressed.h: .config $(srctree)/include/usage.h applets/usage
+ $(call cmd,gen_usage_compressed)
diff --git a/i/pc104/initrd/conf/busybox/applets/applets.c b/i/pc104/initrd/conf/busybox/applets/applets.c
new file mode 100644
index 0000000..7245ece
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/applets/applets.c
@@ -0,0 +1,495 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) tons of folks. Tracking down who wrote what
+ * isn't something I'm going to worry about... If you wrote something
+ * here, please feel free to acknowledge your work.
+ *
+ * Based in part on code from sash, Copyright (c) 1999 by David I. Bell
+ * Permission has been granted to redistribute this code under the GPL.
+ *
+ * Licensed under GPLv2 or later, see file License in this tarball for details.
+ */
+
+#include <assert.h>
+#include "busybox.h"
+
+#define PROTOTYPES
+#include "applets.h"
+#undef PROTOTYPES
+
+
+/* Apparently uclibc defines __GLIBC__ (compat trick?). Oh well. */
+#if ENABLE_STATIC && defined(__GLIBC__) && !defined(__UCLIBC__)
+#warning Static linking against glibc produces buggy executables
+#warning (glibc does not cope well with ld --gc-sections).
+#warning See sources.redhat.com/bugzilla/show_bug.cgi?id=3400
+#warning Note that glibc is unsuitable for static linking anyway.
+#warning If you still want to do it, remove -Wl,--gc-sections
+#warning from top-level Makefile and remove this warning.
+#endif
+
+#if ENABLE_SHOW_USAGE && !ENABLE_FEATURE_COMPRESS_USAGE
+static const char usage_messages[] =
+#define MAKE_USAGE
+#include "usage.h"
+#include "applets.h"
+;
+#undef MAKE_USAGE
+#else
+#define usage_messages 0
+#endif /* ENABLE_SHOW_USAGE */
+
+#undef APPLET
+#undef APPLET_NOUSAGE
+#undef PROTOTYPES
+#include "applets.h"
+
+static struct BB_applet *applet_using;
+
+/* The -1 arises because of the {0,NULL,0,-1} entry above. */
+const unsigned short NUM_APPLETS = (sizeof (applets) / sizeof (struct BB_applet) - 1);
+
+
+#ifdef CONFIG_FEATURE_SUID_CONFIG
+
+#include <ctype.h>
+
+#define CONFIG_FILE "/etc/busybox.conf"
+
+/* applets [] is const, so we have to define this "override" structure */
+static struct BB_suid_config
+{
+ struct BB_applet *m_applet;
+
+ uid_t m_uid;
+ gid_t m_gid;
+ mode_t m_mode;
+
+ struct BB_suid_config *m_next;
+} *suid_config;
+
+static int suid_cfg_readable;
+
+/* check if u is member of group g */
+static int ingroup(uid_t u, gid_t g)
+{
+ struct group *grp = getgrgid(g);
+
+ if (grp) {
+ char **mem;
+
+ for (mem = grp->gr_mem; *mem; mem++) {
+ struct passwd *pwd = getpwnam(*mem);
+
+ if (pwd && (pwd->pw_uid == u))
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* This should probably be a libbb routine. In that case,
+ * I'd probably rename it to something like bb_trimmed_slice.
+ */
+static char *get_trimmed_slice(char *s, char *e)
+{
+ /* First, consider the value at e to be nul and back up until we
+ * reach a non-space char. Set the char after that (possibly at
+ * the original e) to nul. */
+ while (e-- > s) {
+ if (!isspace(*e)) {
+ break;
+ }
+ }
+ e[1] = 0;
+
+ /* Next, advance past all leading space and return a ptr to the
+ * first non-space char; possibly the terminating nul. */
+ return skip_whitespace(s);
+}
+
+
+#define parse_error(x) do { errmsg = x; goto pe_label; } while(0)
+
+/* Don't depend on the tools to combine strings. */
+static const char config_file[] = CONFIG_FILE;
+
+/* There are 4 chars + 1 nul for each of user/group/other. */
+static const char mode_chars[] = "Ssx-\0Ssx-\0Ttx-";
+
+/* We don't supply a value for the nul, so an index adjustment is
+ * necessary below. Also, we use unsigned short here to save some
+ * space even though these are really mode_t values. */
+static const unsigned short mode_mask[] = {
+ /* SST sst xxx --- */
+ S_ISUID, S_ISUID|S_IXUSR, S_IXUSR, 0, /* user */
+ S_ISGID, S_ISGID|S_IXGRP, S_IXGRP, 0, /* group */
+ 0, S_IXOTH, S_IXOTH, 0 /* other */
+};
+
+static void parse_config_file(void)
+{
+ struct BB_suid_config *sct_head;
+ struct BB_suid_config *sct;
+ struct BB_applet *applet;
+ FILE *f;
+ const char *errmsg;
+ char *s;
+ char *e;
+ int i, lc, section;
+ char buffer[256];
+ struct stat st;
+
+ assert(!suid_config); /* Should be set to NULL by bss init. */
+
+ if ((stat(config_file, &st) != 0) /* No config file? */
+ || !S_ISREG(st.st_mode) /* Not a regular file? */
+ || (st.st_uid != 0) /* Not owned by root? */
+ || (st.st_mode & (S_IWGRP | S_IWOTH)) /* Writable by non-root? */
+ || !(f = fopen(config_file, "r")) /* Cannot open? */
+ ) {
+ return;
+ }
+
+ suid_cfg_readable = 1;
+ sct_head = NULL;
+ section = lc = 0;
+
+ do {
+ s = buffer;
+
+ if (!fgets(s, sizeof(buffer), f)) { /* Are we done? */
+ if (ferror(f)) { /* Make sure it wasn't a read error. */
+ parse_error("reading");
+ }
+ fclose(f);
+ suid_config = sct_head; /* Success, so set the pointer. */
+ return;
+ }
+
+ lc++; /* Got a (partial) line. */
+
+ /* If a line is too long for our buffer, we consider it an error.
+ * The following test does mistreat one corner case though.
+ * If the final line of the file does not end with a newline and
+ * yet exactly fills the buffer, it will be treated as too long
+ * even though there isn't really a problem. But it isn't really
+ * worth adding code to deal with such an unlikely situation, and
+ * we do err on the side of caution. Besides, the line would be
+ * too long if it did end with a newline. */
+ if (!strchr(s, '\n') && !feof(f)) {
+ parse_error("line too long");
+ }
+
+ /* Trim leading and trailing whitespace, ignoring comments, and
+ * check if the resulting string is empty. */
+ if (!*(s = get_trimmed_slice(s, strchrnul(s, '#')))) {
+ continue;
+ }
+
+ /* Check for a section header. */
+
+ if (*s == '[') {
+ /* Unlike the old code, we ignore leading and trailing
+ * whitespace for the section name. We also require that
+ * there are no stray characters after the closing bracket. */
+ if (!(e = strchr(s, ']')) /* Missing right bracket? */
+ || e[1] /* Trailing characters? */
+ || !*(s = get_trimmed_slice(s+1, e)) /* Missing name? */
+ ) {
+ parse_error("section header");
+ }
+ /* Right now we only have one section so just check it.
+ * If more sections are added in the future, please don't
+ * resort to cascading ifs with multiple strcasecmp calls.
+ * That kind of bloated code is all too common. A loop
+ * and a string table would be a better choice unless the
+ * number of sections is very small. */
+ if (strcasecmp(s, "SUID") == 0) {
+ section = 1;
+ continue;
+ }
+ section = -1; /* Unknown section so set to skip. */
+ continue;
+ }
+
+ /* Process sections. */
+
+ if (section == 1) { /* SUID */
+ /* Since we trimmed leading and trailing space above, we're
+ * now looking for strings of the form
+ * <key>[::space::]*=[::space::]*<value>
+ * where both key and value could contain inner whitespace. */
+
+ /* First get the key (an applet name in our case). */
+ if (!!(e = strchr(s, '='))) {
+ s = get_trimmed_slice(s, e);
+ }
+ if (!e || !*s) { /* Missing '=' or empty key. */
+ parse_error("keyword");
+ }
+
+ /* Ok, we have an applet name. Process the rhs if this
+ * applet is currently built in and ignore it otherwise.
+ * Note: This can hide config file bugs which only pop
+ * up when the busybox configuration is changed. */
+ if ((applet = find_applet_by_name(s))) {
+ /* Note: We currently don't check for duplicates!
+ * The last config line for each applet will be the
+ * one used since we insert at the head of the list.
+ * I suppose this could be considered a feature. */
+ sct = xmalloc(sizeof(struct BB_suid_config));
+ sct->m_applet = applet;
+ sct->m_mode = 0;
+ sct->m_next = sct_head;
+ sct_head = sct;
+
+ /* Get the specified mode. */
+
+ e = skip_whitespace(e+1);
+
+ for (i=0 ; i < 3 ; i++) {
+ const char *q;
+ if (!*(q = strchrnul(mode_chars + 5*i, *e++))) {
+ parse_error("mode");
+ }
+ /* Adjust by -i to account for nul. */
+ sct->m_mode |= mode_mask[(q - mode_chars) - i];
+ }
+
+ /* Now get the the user/group info. */
+
+ s = skip_whitespace(e);
+
+ /* Note: We require whitespace between the mode and the
+ * user/group info. */
+ if ((s == e) || !(e = strchr(s, '.'))) {
+ parse_error("<uid>.<gid>");
+ }
+ *e++ = 0;
+
+ /* We can't use get_ug_id here since it would exit()
+ * if a uid or gid was not found. Oh well... */
+ {
+ char *e2;
+
+ sct->m_uid = strtoul(s, &e2, 10);
+ if (*e2 || (s == e2)) {
+ struct passwd *pwd = getpwnam(s);
+ if (!pwd) {
+ parse_error("user");
+ }
+ sct->m_uid = pwd->pw_uid;
+ }
+
+ sct->m_gid = strtoul(e, &e2, 10);
+ if (*e2 || (e == e2)) {
+ struct group *grp;
+ if (!(grp = getgrnam(e))) {
+ parse_error("group");
+ }
+ sct->m_gid = grp->gr_gid;
+ }
+ }
+ }
+ continue;
+ }
+
+ /* Unknown sections are ignored. */
+
+ /* Encountering configuration lines prior to seeing a
+ * section header is treated as an error. This is how
+ * the old code worked, but it may not be desirable.
+ * We may want to simply ignore such lines in case they
+ * are used in some future version of busybox. */
+ if (!section) {
+ parse_error("keyword outside section");
+ }
+
+ } while (1);
+
+ pe_label:
+ fprintf(stderr, "Parse error in %s, line %d: %s\n",
+ config_file, lc, errmsg);
+
+ fclose(f);
+ /* Release any allocated memory before returning. */
+ while (sct_head) {
+ sct = sct_head->m_next;
+ free(sct_head);
+ sct_head = sct;
+ }
+}
+
+#else
+#define parse_config_file() ((void)0)
+#endif /* CONFIG_FEATURE_SUID_CONFIG */
+
+#ifdef CONFIG_FEATURE_SUID
+static void check_suid(struct BB_applet *applet)
+{
+ uid_t ruid = getuid(); /* real [ug]id */
+ uid_t rgid = getgid();
+
+#ifdef CONFIG_FEATURE_SUID_CONFIG
+ if (suid_cfg_readable) {
+ struct BB_suid_config *sct;
+
+ for (sct = suid_config; sct; sct = sct->m_next) {
+ if (sct->m_applet == applet)
+ break;
+ }
+ if (sct) {
+ mode_t m = sct->m_mode;
+
+ if (sct->m_uid == ruid)
+ /* same uid */
+ m >>= 6;
+ else if ((sct->m_gid == rgid) || ingroup(ruid, sct->m_gid))
+ /* same group / in group */
+ m >>= 3;
+
+ if (!(m & S_IXOTH)) /* is x bit not set ? */
+ bb_error_msg_and_die("you have no permission to run this applet!");
+
+ if (sct->m_gid != 0) {
+ /* _both_ have to be set for sgid */
+ if ((sct->m_mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) {
+ xsetgid(sct->m_gid);
+ } else xsetgid(rgid); /* no sgid -> drop */
+ }
+ if (sct->m_uid != 0) {
+ if (sct->m_mode & S_ISUID) xsetuid(sct->m_uid);
+ else xsetuid(ruid); /* no suid -> drop */
+ }
+ } else {
+ /* default: drop all privileges */
+ xsetgid(rgid);
+ xsetuid(ruid);
+ }
+ return;
+ } else {
+#ifndef CONFIG_FEATURE_SUID_CONFIG_QUIET
+ static int onetime = 0;
+
+ if (!onetime) {
+ onetime = 1;
+ fprintf(stderr, "Using fallback suid method\n");
+ }
+#endif
+ }
+#endif
+
+ if (applet->need_suid == _BB_SUID_ALWAYS) {
+ if (geteuid()) bb_error_msg_and_die("applet requires root privileges!");
+ } else if (applet->need_suid == _BB_SUID_NEVER) {
+ xsetgid(rgid); /* drop all privileges */
+ xsetuid(ruid);
+ }
+}
+#else
+#define check_suid(x)
+#endif /* CONFIG_FEATURE_SUID */
+
+
+
+#ifdef CONFIG_FEATURE_COMPRESS_USAGE
+
+#include "usage_compressed.h"
+#include "unarchive.h"
+
+static const char *unpack_usage_messages(void)
+{
+ int input[2], output[2], pid;
+ char *buf;
+
+ if(pipe(input) < 0 || pipe(output) < 0)
+ exit(1);
+
+ pid = fork();
+ switch (pid) {
+ case -1: /* error */
+ exit(1);
+ case 0: /* child */
+ close(input[1]);
+ close(output[0]);
+ uncompressStream(input[0], output[1]);
+ exit(0);
+ }
+ /* parent */
+
+ close(input[0]);
+ close(output[1]);
+ pid = fork();
+ switch (pid) {
+ case -1: /* error */
+ exit(1);
+ case 0: /* child */
+ full_write(input[1], packed_usage, sizeof(packed_usage));
+ exit(0);
+ }
+ /* parent */
+ close(input[1]);
+
+ buf = xmalloc(SIZEOF_usage_messages);
+ full_read(output[0], buf, SIZEOF_usage_messages);
+ return buf;
+}
+
+#else
+#define unpack_usage_messages() usage_messages
+#endif /* ENABLE_FEATURE_COMPRESS_USAGE */
+
+void bb_show_usage(void)
+{
+ if (ENABLE_SHOW_USAGE) {
+ const char *format_string;
+ const char *usage_string = unpack_usage_messages();
+ int i;
+
+ for (i = applet_using - applets; i > 0;)
+ if (!*usage_string++) --i;
+
+ format_string = "%s\n\nUsage: %s %s\n\n";
+ if (*usage_string == '\b')
+ format_string = "%s\n\nNo help available.\n\n";
+ fprintf(stderr, format_string, bb_msg_full_version,
+ applet_using->name, usage_string);
+ }
+
+ exit(xfunc_error_retval);
+}
+
+static int applet_name_compare(const void *name, const void *vapplet)
+{
+ const struct BB_applet *applet = vapplet;
+
+ return strcmp(name, applet->name);
+}
+
+struct BB_applet *find_applet_by_name(const char *name)
+{
+ return bsearch(name, applets, NUM_APPLETS, sizeof(struct BB_applet),
+ applet_name_compare);
+}
+
+void run_applet_by_name(const char *name, int argc, char **argv)
+{
+ if (ENABLE_FEATURE_SUID_CONFIG)
+ parse_config_file();
+
+ if (!strncmp(name, "busybox", 7))
+ exit(busybox_main(argc, argv));
+ /* Do a binary search to find the applet entry given the name. */
+ applet_using = find_applet_by_name(name);
+ if (applet_using) {
+ applet_name = applet_using->name;
+ if (argc == 2 && !strcmp(argv[1], "--help"))
+ bb_show_usage();
+ if (ENABLE_FEATURE_SUID)
+ check_suid(applet_using);
+ exit(applet_using->main(argc, argv));
+ }
+}
diff --git a/i/pc104/initrd/conf/busybox/applets/busybox.c b/i/pc104/initrd/conf/busybox/applets/busybox.c
new file mode 100644
index 0000000..0387d79
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/applets/busybox.c
@@ -0,0 +1,147 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * BusyBox' main applet dispatcher.
+ *
+ * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ */
+#include "busybox.h"
+
+const char *applet_name ATTRIBUTE_EXTERNALLY_VISIBLE;
+
+#ifdef CONFIG_FEATURE_INSTALLER
+/*
+ * directory table
+ * this should be consistent w/ the enum, busybox.h::Location,
+ * or else...
+ */
+static const char usr_bin [] = "/usr/bin";
+static const char usr_sbin[] = "/usr/sbin";
+
+static const char* const install_dir[] = {
+ &usr_bin [8], /* "", equivalent to "/" for concat_path_file() */
+ &usr_bin [4], /* "/bin" */
+ &usr_sbin[4], /* "/sbin" */
+ usr_bin,
+ usr_sbin
+};
+
+/* abstract link() */
+typedef int (*link_func)(const char *, const char *);
+
+/* create (sym)links for each applet */
+static void install_links(const char *busybox, int use_symbolic_links)
+{
+ link_func lf = link;
+ char *fpc;
+ int i;
+ int rc;
+
+ if (use_symbolic_links)
+ lf = symlink;
+
+ for (i = 0; applets[i].name != NULL; i++) {
+ fpc = concat_path_file(
+ install_dir[applets[i].location],
+ applets[i].name);
+ rc = lf(busybox, fpc);
+ if (rc != 0 && errno != EEXIST) {
+ bb_perror_msg("%s", fpc);
+ }
+ free(fpc);
+ }
+}
+
+#else
+#define install_links(x,y)
+#endif /* CONFIG_FEATURE_INSTALLER */
+
+int main(int argc, char **argv)
+{
+ const char *s;
+
+ applet_name = argv[0];
+ if (*applet_name == '-')
+ applet_name++;
+ while ((s = strchr(applet_name, '/')))
+ applet_name = s + 1;
+
+ /* Set locale for everybody except 'init' */
+ if (ENABLE_LOCALE_SUPPORT && getpid() != 1)
+ setlocale(LC_ALL, "");
+
+ run_applet_by_name(applet_name, argc, argv);
+ bb_error_msg_and_die("applet not found");
+}
+
+int busybox_main(int argc, char **argv);
+int busybox_main(int argc, char **argv)
+{
+ /*
+ * This style of argument parsing doesn't scale well
+ * in the event that busybox starts wanting more --options.
+ * If someone has a cleaner approach, by all means implement it.
+ */
+ if (ENABLE_FEATURE_INSTALLER && argc > 1 && !strcmp(argv[1], "--install")) {
+ int use_symbolic_links = 0;
+ char *busybox;
+
+ /* to use symlinks, or not to use symlinks... */
+ if (argc > 2)
+ if (strcmp(argv[2], "-s") == 0)
+ use_symbolic_links = 1;
+
+ /* link */
+// XXX: FIXME: this is broken. Why not just use argv[0] ?
+ busybox = xmalloc_readlink_or_warn("/proc/self/exe");
+ if (!busybox)
+ return 1;
+ install_links(busybox, use_symbolic_links);
+ if (ENABLE_FEATURE_CLEAN_UP)
+ free(busybox);
+ return 0;
+ }
+
+ /* Deal with --help. (Also print help when called with no arguments) */
+
+ if (argc == 1 || !strcmp(argv[1], "--help") ) {
+ if (argc > 2) {
+ applet_name = argv[2];
+ run_applet_by_name(applet_name, 2, argv);
+ } else {
+ const struct BB_applet *a;
+ int col, output_width;
+
+ output_width = 80 - sizeof("start-stop-daemon, ") - 8;
+ if (ENABLE_FEATURE_AUTOWIDTH) {
+ /* Obtain the terminal width. */
+ get_terminal_width_height(0, &output_width, NULL);
+ /* leading tab and room to wrap */
+ output_width -= sizeof("start-stop-daemon, ") + 8;
+ }
+
+ printf("%s\n"
+ "Copyright (C) 1998-2006  Erik Andersen, Rob Landley, and others.\n"
+ "Licensed under GPLv2.  See source distribution for full notice.\n\n"
+ "Usage: busybox [function] [arguments]...\n"
+ " or: [function] [arguments]...\n\n"
+ "\tBusyBox is a multi-call binary that combines many common Unix\n"
+ "\tutilities into a single executable. Most people will create a\n"
+ "\tlink to busybox for each function they wish to use and BusyBox\n"
+ "\twill act like whatever it was invoked as!\n"
+ "\nCurrently defined functions:\n", bb_msg_full_version);
+ col = 0;
+ for (a = applets; a->name;) {
+ col += printf("%s%s", (col ? ", " : "\t"), a->name);
+ a++;
+ if (col > output_width && a->name) {
+ puts(",");
+ col = 0;
+ }
+ }
+ puts("\n");
+ return 0;
+ }
+ } else run_applet_by_name(argv[1], argc - 1, argv + 1);
+
+ bb_error_msg_and_die("applet not found");
+}
diff --git a/i/pc104/initrd/conf/busybox/applets/busybox.mkll b/i/pc104/initrd/conf/busybox/applets/busybox.mkll
new file mode 100755
index 0000000..6d61f7e
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/applets/busybox.mkll
@@ -0,0 +1,24 @@
+#!/bin/sh
+# Make busybox links list file.
+
+# input $1: full path to Config.h
+# input $2: full path to applets.h
+# output (stdout): list of pathnames that should be linked to busybox
+
+# Maintainer: Larry Doolittle <ldoolitt@recycle.lbl.gov>
+
+export LC_ALL=POSIX
+export LC_CTYPE=POSIX
+
+CONFIG_H=${1:-include/autoconf.h}
+APPLETS_H=${2:-include/applets.h}
+$HOSTCC -E -DMAKE_LINKS -include $CONFIG_H $APPLETS_H |
+ awk '/^[ \t]*LINK/{
+ dir=substr($2,8)
+ gsub("_","/",dir)
+ if(dir=="/ROOT") dir=""
+ file=$3
+ gsub("\"","",file)
+ if (file=="busybox") next
+ print tolower(dir) "/" file
+ }'
diff --git a/i/pc104/initrd/conf/busybox/applets/individual.c b/i/pc104/initrd/conf/busybox/applets/individual.c
new file mode 100644
index 0000000..0721683
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/applets/individual.c
@@ -0,0 +1,27 @@
+/* Minimal wrapper to build an individual busybox applet.
+ *
+ * Copyright 2005 Rob Landley <rob@landley.net
+ *
+ * Licensed under GPL version 2, see file LICENSE in this tarball for details
+ */
+
+const char *applet_name;
+
+#include <stdio.h>
+#include <stdlib.h>
+//Ok to remove? #include "bb_config.h"
+#include "usage.h"
+
+int main(int argc, char *argv[])
+{
+ applet_name=argv[0];
+
+ return APPLET_main(argc,argv);
+}
+
+void bb_show_usage(void)
+{
+ printf(APPLET_full_usage "\n");
+
+ exit(1);
+}
diff --git a/i/pc104/initrd/conf/busybox/applets/install.sh b/i/pc104/initrd/conf/busybox/applets/install.sh
new file mode 100755
index 0000000..4ec96c2
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/applets/install.sh
@@ -0,0 +1,94 @@
+#!/bin/sh
+
+export LC_ALL=POSIX
+export LC_CTYPE=POSIX
+
+prefix=${1}
+if [ -z "$prefix" ]; then
+ echo "usage: applets/install.sh DESTINATION [--symlinks/--hardlinks]"
+ exit 1;
+fi
+h=`sort busybox.links | uniq`
+cleanup="0"
+noclobber="0"
+case "$2" in
+ --hardlinks) linkopts="-f";;
+ --symlinks) linkopts="-fs";;
+ --cleanup) cleanup="1";;
+ --noclobber) noclobber="1";;
+ "") h="";;
+ *) echo "Unknown install option: $2"; exit 1;;
+esac
+
+if [ -n "$DO_INSTALL_LIBS" ] && [ "$DO_INSTALL_LIBS" != "n" ]; then
+ # get the target dir for the libs
+ # assume it starts with lib
+ libdir=$($CC -print-file-name=libc.so | \
+ sed -n 's%^.*\(/lib[^\/]*\)/libc.so%\1%p')
+ if test -z "$libdir"; then
+ libdir=/lib
+ fi
+
+ mkdir -p $prefix/$libdir || exit 1
+ for i in $DO_INSTALL_LIBS; do
+ rm -f $prefix/$libdir/$i || exit 1
+ if [ -f $i ]; then
+ cp -a $i $prefix/$libdir/ || exit 1
+ chmod 0644 $prefix/$libdir/$i || exit 1
+ fi
+ done
+fi
+
+if [ "$cleanup" = "1" ] && [ -e "$prefix/bin/busybox" ]; then
+ inode=`ls -i "$prefix/bin/busybox" | awk '{print $1}'`
+ sub_shell_it=`
+ cd "$prefix"
+ for d in usr/sbin usr/bin sbin bin ; do
+ pd=$PWD
+ if [ -d "$d" ]; then
+ cd $d
+ ls -iL . | grep "^ *$inode" | awk '{print $2}' | env -i xargs rm -f
+ fi
+ cd "$pd"
+ done
+ `
+fi
+
+rm -f $prefix/bin/busybox || exit 1
+mkdir -p $prefix/bin || exit 1
+install -m 755 busybox $prefix/bin/busybox || exit 1
+
+for i in $h ; do
+ appdir=`dirname $i`
+ mkdir -p $prefix/$appdir || exit 1
+ if [ "$2" = "--hardlinks" ]; then
+ bb_path="$prefix/bin/busybox"
+ else
+ case "$appdir" in
+ /)
+ bb_path="bin/busybox"
+ ;;
+ /bin)
+ bb_path="busybox"
+ ;;
+ /sbin)
+ bb_path="../bin/busybox"
+ ;;
+ /usr/bin|/usr/sbin)
+ bb_path="../../bin/busybox"
+ ;;
+ *)
+ echo "Unknown installation directory: $appdir"
+ exit 1
+ ;;
+ esac
+ fi
+ if [ "$noclobber" = "0" ] || [ ! -e "$prefix$i" ]; then
+ echo " $prefix$i -> $bb_path"
+ ln $linkopts $bb_path $prefix$i || exit 1
+ else
+ echo " $prefix$i already exists"
+ fi
+done
+
+exit 0
diff --git a/i/pc104/initrd/conf/busybox/applets/usage.c b/i/pc104/initrd/conf/busybox/applets/usage.c
new file mode 100644
index 0000000..bb7ca82
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/applets/usage.c
@@ -0,0 +1,17 @@
+/* vi: set sw=4 ts=4: */
+#include <unistd.h>
+
+#include "../include/autoconf.h"
+#include "../include/busybox.h"
+
+static const char usage_messages[] = ""
+#define MAKE_USAGE
+#include "usage.h"
+#include "applets.h"
+;
+
+int main(void)
+{
+ write(1, usage_messages, sizeof(usage_messages));
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/applets/usage_compressed b/i/pc104/initrd/conf/busybox/applets/usage_compressed
new file mode 100755
index 0000000..fd581cc
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/applets/usage_compressed
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+target="$1"
+loc="$2"
+
+test "$target" || exit 1
+test "$loc" || loc=.
+test -x "$loc/usage" || exit 1
+test "$SED" || SED=sed
+
+sz=`"$loc/usage" | wc -c` || exit 1
+
+exec >"$target"
+
+echo 'static const char packed_usage[] = '
+"$loc/usage" | bzip2 -1 | od -v -t x1 \
+| $SED -e 's/^[^ ]*//' -e 's/ *\(..\)/\\x\1/g' -e 's/^\(.*\)$/"\1"/'
+echo ';'
+echo '#define SIZEOF_usage_messages' `expr 0 + $sz`
diff --git a/i/pc104/initrd/conf/busybox/arch/i386/Makefile b/i/pc104/initrd/conf/busybox/arch/i386/Makefile
new file mode 100644
index 0000000..385dd52
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/arch/i386/Makefile
@@ -0,0 +1,5 @@
+# ==========================================================================
+# Build system
+# ==========================================================================
+
+CFLAGS += $(call cc-option,-march=i386 -mpreferred-stack-boundary=2,)
diff --git a/i/pc104/initrd/conf/busybox/archival/Config.in b/i/pc104/initrd/conf/busybox/archival/Config.in
new file mode 100644
index 0000000..2741982
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/archival/Config.in
@@ -0,0 +1,300 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+menu "Archival Utilities"
+
+config AR
+ bool "ar"
+ default n
+ help
+ ar is an archival utility program used to create, modify, and
+ extract contents from archives. An archive is a single file holding
+ a collection of other files in a structure that makes it possible to
+ retrieve the original individual files (called archive members).
+ The original files' contents, mode (permissions), timestamp, owner,
+ and group are preserved in the archive, and can be restored on
+ extraction.
+
+ The stored filename is limited to 15 characters. (for more information
+ see long filename support).
+ ar has 60 bytes of overheads for every stored file.
+
+ This implementation of ar can extract archives, it cannot create or
+ modify them.
+ On an x86 system, the ar applet adds about 1K.
+
+ Unless you have a specific application which requires ar, you should
+ probably say N here.
+
+config FEATURE_AR_LONG_FILENAMES
+ bool "Enable support for long filenames (not need for debs)"
+ default n
+ depends on AR
+ help
+ By default the ar format can only store the first 15 characters of the
+ filename, this option removes that limitation.
+ It supports the GNU ar long filename method which moves multiple long
+ filenames into a the data section of a new ar entry.
+
+config BUNZIP2
+ bool "bunzip2"
+ default n
+ help
+ bunzip2 is a compression utility using the Burrows-Wheeler block
+ sorting text compression algorithm, and Huffman coding. Compression
+ is generally considerably better than that achieved by more
+ conventional LZ77/LZ78-based compressors, and approaches the
+ performance of the PPM family of statistical compressors.
+
+ The BusyBox bunzip2 applet is limited to de-compression only.
+ On an x86 system, this applet adds about 11K.
+
+ Unless you have a specific application which requires bunzip2, you
+ should probably say N here.
+
+config CPIO
+ bool "cpio"
+ default n
+ help
+ cpio is an archival utility program used to create, modify, and extract
+ contents from archives.
+ cpio has 110 bytes of overheads for every stored file.
+
+ This implementation of cpio can extract cpio archives created in the
+ "newc" or "crc" format, it cannot create or modify them.
+
+ Unless you have a specific application which requires cpio, you should
+ probably say N here.
+
+config DPKG
+ bool "dpkg"
+ default n
+ help
+ dpkg is a medium-level tool to install, build, remove and manage Debian packages.
+
+ This implementation of dpkg has a number of limitations, you should use the
+ official dpkg if possible.
+
+config DPKG_DEB
+ bool "dpkg_deb"
+ default n
+ help
+ dpkg-deb packs, unpacks and provides information about Debian archives.
+
+ This implementation of dpkg-deb cannot pack archives.
+
+ Unless you have a specific application which requires dpkg-deb, you should
+ probably say N here.
+
+config FEATURE_DPKG_DEB_EXTRACT_ONLY
+ bool "extract only (-x)"
+ default n
+ depends on DPKG_DEB
+ help
+ This reduces dpkg-deb to the equivalent of "ar -p <deb> data.tar.gz | tar -zx".
+ However it saves space as none of the extra dpkg-deb, ar or tar options are
+ needed, they are linked to internally.
+
+config GUNZIP
+ bool "gunzip"
+ default n
+ help
+ gunzip is used to decompress archives created by gzip.
+ You can use the `-t' option to test the integrity of
+ an archive, without decompressing it.
+
+config FEATURE_GUNZIP_UNCOMPRESS
+ bool "Uncompress support"
+ default n
+ depends on GUNZIP
+ help
+ Enable if you want gunzip to have the ability to decompress
+ archives created by the program compress (not much
+ used anymore).
+
+config GZIP
+ bool "gzip"
+ default n
+ help
+ gzip is used to compress files.
+ It's probably the most widely used UNIX compression program.
+
+config RPM2CPIO
+ bool "rpm2cpio"
+ default n
+ help
+ Converts an RPM file into a CPIO archive.
+
+config RPM
+ bool "rpm"
+ default n
+ help
+ Mini RPM applet - queries and extracts RPM packages.
+
+config TAR
+ bool "tar"
+ default n
+ help
+ tar is an archiving program. It's commonly used with gzip to
+ create compressed archives. It's probably the most widely used
+ UNIX archive program.
+
+config FEATURE_TAR_CREATE
+ bool "Enable archive creation"
+ default y
+ depends on TAR
+ help
+ If you enable this option you'll be able to create
+ tar archives using the `-c' option.
+
+config FEATURE_TAR_BZIP2
+ bool "Enable -j option to handle .tar.bz2 files"
+ default n
+ depends on TAR
+ help
+ If you enable this option you'll be able to extract
+ archives compressed with bzip2.
+
+config FEATURE_TAR_LZMA
+ bool "Enable -a option to handle .tar.lzma files"
+ default n
+ depends on TAR
+ help
+ If you enable this option you'll be able to extract
+ archives compressed with lzma.
+
+config FEATURE_TAR_FROM
+ bool "Enable -X (exclude from) and -T (include from) options)"
+ default n
+ depends on TAR
+ help
+ If you enable this option you'll be able to specify
+ a list of files to include or exclude from an archive.
+
+config FEATURE_TAR_GZIP
+ bool "Enable -z option"
+ default y
+ depends on TAR
+ help
+ If you enable this option tar will be able to call gzip,
+ when creating or extracting tar gziped archives.
+
+config FEATURE_TAR_COMPRESS
+ bool "Enable -Z option"
+ default n
+ depends on TAR
+ help
+ If you enable this option tar will be able to call uncompress,
+ when extracting .tar.Z archives.
+
+config FEATURE_TAR_OLDGNU_COMPATIBILITY
+ bool "Enable support for old tar header format"
+ default N
+ depends on TAR
+ help
+ This option is required to unpack archives created in
+ the old GNU format; help to kill this old format by
+ repacking your ancient archives with the new format.
+
+config FEATURE_TAR_GNU_EXTENSIONS
+ bool "Enable support for some GNU tar extensions"
+ default y
+ depends on TAR
+ help
+ With this option busybox supports GNU long filenames and
+ linknames.
+
+config FEATURE_TAR_LONG_OPTIONS
+ bool "Enable long options"
+ default n
+ depends on TAR && GETOPT_LONG
+ help
+ Enable use of long options, increases size by about 400 Bytes
+
+config UNCOMPRESS
+ bool "uncompress"
+ default n
+ help
+ uncompress is used to decompress archives created by compress.
+ Not much used anymore, replaced by gzip/gunzip.
+
+config UNLZMA
+ bool "unlzma"
+ default n
+ help
+ unlzma is a compression utility using the Lempel-Ziv-Markov chain
+ compression algorithm, and range coding. Compression
+ is generally considerably better than that achieved by the bzip2
+ compressors.
+
+ The BusyBox unlzma applet is limited to de-compression only.
+ On an x86 system, this applet adds about 4K.
+
+ Unless you have a specific application which requires unlzma, you
+ should probably say N here.
+
+config FEATURE_LZMA_FAST
+ bool "Optimze unlzma for speed"
+ default n
+ depends on UNLZMA
+ help
+ This option reduces decompression time by about 33% at the cost of
+ a 2K bigger binary.
+
+config UNZIP
+ bool "unzip"
+ default n
+ help
+ unzip will list or extract files from a ZIP archive,
+ commonly found on DOS/WIN systems. The default behavior
+ (with no options) is to extract the archive into the
+ current directory. Use the `-d' option to extract to a
+ directory of your choice.
+
+comment "Common options for cpio and tar"
+ depends on CPIO || TAR
+
+config FEATURE_UNARCHIVE_TAPE
+ bool "Enable tape drive support"
+ default n
+ depends on CPIO || TAR
+ help
+ I don't think this is needed anymore.
+
+comment "Common options for dpkg and dpkg_deb"
+ depends on DPKG || DPKG_DEB
+
+config FEATURE_DEB_TAR_GZ
+ bool "gzip debian packages (normal)"
+ default y if DPKG || DPKG_DEB
+ depends on DPKG || DPKG_DEB
+ help
+ This is the default compression method inside the debian ar file.
+
+ If you want compatibility with standard .deb's you should say yes here.
+
+config FEATURE_DEB_TAR_BZ2
+ bool "bzip2 debian packages"
+ default n
+ depends on DPKG || DPKG_DEB
+ help
+ This allows dpkg and dpkg-deb to extract deb's that are compressed internally
+ with bzip2 instead of gzip.
+
+ You only want this if you are creating your own custom debian packages that
+ use an internal control.tar.bz2 or data.tar.bz2.
+
+config FEATURE_DEB_TAR_LZMA
+ bool "lzma debian packages"
+ default n
+ depends on DPKG || DPKG_DEB
+ help
+ This allows dpkg and dpkg-deb to extract deb's that are compressed
+ internally with lzma instead of gzip.
+
+ You only want this if you are creating your own custom debian
+ packages that use an internal control.tar.lzma or data.tar.lzma.
+
+endmenu
diff --git a/i/pc104/initrd/conf/busybox/archival/Kbuild b/i/pc104/initrd/conf/busybox/archival/Kbuild
new file mode 100644
index 0000000..07b442f
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/archival/Kbuild
@@ -0,0 +1,22 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under the GPL v2, see the file LICENSE in this tarball.
+
+libs-y += libunarchive/
+
+lib-y:=
+lib-$(CONFIG_AR) += ar.o
+lib-$(CONFIG_BUNZIP2) += bbunzip.o
+lib-$(CONFIG_UNLZMA) += bbunzip.o
+lib-$(CONFIG_CPIO) += cpio.o
+lib-$(CONFIG_DPKG) += dpkg.o
+lib-$(CONFIG_DPKG_DEB) += dpkg_deb.o
+lib-$(CONFIG_GUNZIP) += bbunzip.o
+lib-$(CONFIG_GZIP) += gzip.o bbunzip.o
+lib-$(CONFIG_RPM2CPIO) += rpm2cpio.o
+lib-$(CONFIG_RPM) += rpm.o
+lib-$(CONFIG_TAR) += tar.o
+lib-$(CONFIG_UNCOMPRESS) += bbunzip.o
+lib-$(CONFIG_UNZIP) += unzip.o
diff --git a/i/pc104/initrd/conf/busybox/archival/ar.c b/i/pc104/initrd/conf/busybox/archival/ar.c
new file mode 100644
index 0000000..23d9b49
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/archival/ar.c
@@ -0,0 +1,94 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini ar implementation for busybox
+ *
+ * Copyright (C) 2000 by Glenn McGrath
+ * Written by Glenn McGrath <bug1@iinet.net.au> 1 June 2000
+ *
+ * Based in part on BusyBox tar, Debian dpkg-deb and GNU ar.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ *
+ * There is no single standard to adhere to so ar may not portable
+ * between different systems
+ * http://www.unix-systems.org/single_unix_specification_v2/xcu/ar.html
+ */
+
+#include "busybox.h"
+#include "unarchive.h"
+
+static void header_verbose_list_ar(const file_header_t *file_header)
+{
+ const char *mode = bb_mode_string(file_header->mode);
+ char *mtime;
+
+ mtime = ctime(&file_header->mtime);
+ mtime[16] = ' ';
+ memmove(&mtime[17], &mtime[20], 4);
+ mtime[21] = '\0';
+ printf("%s %d/%d%7d %s %s\n", &mode[1], file_header->uid, file_header->gid,
+ (int) file_header->size, &mtime[4], file_header->name);
+}
+
+#define AR_CTX_PRINT 0x01
+#define AR_CTX_LIST 0x02
+#define AR_CTX_EXTRACT 0x04
+#define AR_OPT_PRESERVE_DATE 0x08
+#define AR_OPT_VERBOSE 0x10
+#define AR_OPT_CREATE 0x20
+#define AR_OPT_INSERT 0x40
+
+int ar_main(int argc, char **argv);
+int ar_main(int argc, char **argv)
+{
+ archive_handle_t *archive_handle;
+ unsigned opt;
+ static const char msg_unsupported_err[] =
+ "Archive %s not supported. Install binutils 'ar'.";
+ char magic[8];
+
+ archive_handle = init_handle();
+
+ /* Prepend '-' to the first argument if required */
+ opt_complementary = "--:p:t:x:-1:?:p--tx:t--px:x--pt";
+ opt = getopt32(argc, argv, "ptxovcr");
+
+ if (opt & AR_CTX_PRINT) {
+ archive_handle->action_data = data_extract_to_stdout;
+ }
+ if (opt & AR_CTX_LIST) {
+ archive_handle->action_header = header_list;
+ }
+ if (opt & AR_CTX_EXTRACT) {
+ archive_handle->action_data = data_extract_all;
+ }
+ if (opt & AR_OPT_PRESERVE_DATE) {
+ archive_handle->flags |= ARCHIVE_PRESERVE_DATE;
+ }
+ if (opt & AR_OPT_VERBOSE) {
+ archive_handle->action_header = header_verbose_list_ar;
+ }
+ if (opt & AR_OPT_CREATE) {
+ bb_error_msg_and_die(msg_unsupported_err, "creation");
+ }
+ if (opt & AR_OPT_INSERT) {
+ bb_error_msg_and_die(msg_unsupported_err, "insertion");
+ }
+
+ archive_handle->src_fd = xopen(argv[optind++], O_RDONLY);
+
+ while (optind < argc) {
+ archive_handle->filter = filter_accept_list;
+ llist_add_to(&(archive_handle->accept), argv[optind++]);
+ }
+
+ xread(archive_handle->src_fd, magic, 7);
+ if (strncmp(magic, "!<arch>", 7) != 0) {
+ bb_error_msg_and_die("invalid ar magic");
+ }
+ archive_handle->offset += 7;
+
+ while (get_header_ar(archive_handle) == EXIT_SUCCESS) /* repeat */;
+
+ return EXIT_SUCCESS;
+}
diff --git a/i/pc104/initrd/conf/busybox/archival/bbunzip.c b/i/pc104/initrd/conf/busybox/archival/bbunzip.c
new file mode 100644
index 0000000..e16e6b0
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/archival/bbunzip.c
@@ -0,0 +1,355 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Common code for gunzip-like applets
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "busybox.h"
+#include "unarchive.h"
+
+enum {
+ OPT_STDOUT = 0x1,
+ OPT_FORCE = 0x2,
+/* gunzip only: */
+ OPT_VERBOSE = 0x4,
+ OPT_DECOMPRESS = 0x8,
+ OPT_TEST = 0x10,
+};
+
+static
+int open_to_or_warn(int to_fd, const char *filename, int flags, int mode)
+{
+ int fd = open(filename, flags, mode);
+ if (fd < 0) {
+ bb_perror_msg("%s", filename);
+ return 1;
+ }
+ if (fd != to_fd) {
+ if (dup2(fd, to_fd) < 0)
+ bb_perror_msg_and_die("cannot dup");
+ close(fd);
+ }
+ return 0;
+}
+
+int bbunpack(char **argv,
+ char* (*make_new_name)(char *filename),
+ USE_DESKTOP(long long) int (*unpacker)(void)
+)
+{
+ struct stat stat_buf;
+ USE_DESKTOP(long long) int status;
+ char *filename, *new_name;
+ smallint exitcode = 0;
+
+ do {
+ /* NB: new_name is *maybe* malloc'ed! */
+ new_name = NULL;
+ filename = *argv; /* can be NULL - 'streaming' bunzip2 */
+
+ if (filename && LONE_DASH(filename))
+ filename = NULL;
+
+ /* Open src */
+ if (filename) {
+ if (stat(filename, &stat_buf) != 0) {
+ bb_perror_msg("%s", filename);
+ err:
+ exitcode = 1;
+ goto free_name;
+ }
+ if (open_to_or_warn(STDIN_FILENO, filename, O_RDONLY, 0))
+ goto err;
+ }
+
+ /* Special cases: test, stdout */
+ if (option_mask32 & (OPT_STDOUT|OPT_TEST)) {
+ if (option_mask32 & OPT_TEST)
+ if (open_to_or_warn(STDOUT_FILENO, bb_dev_null, O_WRONLY, 0))
+ goto err;
+ filename = NULL;
+ }
+
+ /* Open dst if we are going to unpack to file */
+ if (filename) {
+ new_name = make_new_name(filename);
+ if (!new_name) {
+ bb_error_msg("%s: unknown suffix - ignored", filename);
+ goto err;
+ }
+ /* O_EXCL: "real" bunzip2 doesn't overwrite files */
+ /* GNU gunzip goes not bail out, but goes to next file */
+ if (open_to_or_warn(STDOUT_FILENO, new_name, O_WRONLY | O_CREAT | O_EXCL,
+ stat_buf.st_mode))
+ goto err;
+ }
+
+ /* Check that the input is sane */
+ if (isatty(STDIN_FILENO) && (option_mask32 & OPT_FORCE) == 0) {
+ bb_error_msg_and_die("compressed data not read from terminal, "
+ "use -f to force it");
+ }
+
+ status = unpacker();
+ if (status < 0)
+ exitcode = 1;
+
+ if (filename) {
+ char *del = new_name;
+ if (status >= 0) {
+ /* TODO: restore user/group/times here? */
+ /* Delete _compressed_ file */
+ del = filename;
+ /* restore extension (unless tgz -> tar case) */
+ if (new_name == filename)
+ filename[strlen(filename)] = '.';
+ }
+ if (unlink(del) < 0)
+ bb_perror_msg_and_die("cannot remove %s", del);
+
+#if 0 /* Currently buggy - wrong name: "a.gz: 261% - replaced with a.gz" */
+ /* Extreme bloat for gunzip compat */
+ if (ENABLE_DESKTOP && (option_mask32 & OPT_VERBOSE) && status >= 0) {
+ fprintf(stderr, "%s: %u%% - replaced with %s\n",
+ filename, (unsigned)(stat_buf.st_size*100 / (status+1)), new_name);
+ }
+#endif
+
+ free_name:
+ if (new_name != filename)
+ free(new_name);
+ }
+ } while (*argv && *++argv);
+
+ return exitcode;
+}
+
+#if ENABLE_BUNZIP2 || ENABLE_UNLZMA || ENABLE_UNCOMPRESS
+
+static
+char* make_new_name_generic(char *filename, const char *expected_ext)
+{
+ char *extension = strrchr(filename, '.');
+ if (!extension || strcmp(extension + 1, expected_ext) != 0) {
+ /* Mimic GNU gunzip - "real" bunzip2 tries to */
+ /* unpack file anyway, to file.out */
+ return NULL;
+ }
+ *extension = '\0';
+ return filename;
+}
+
+#endif
+
+
+/*
+ * Modified for busybox by Glenn McGrath <bug1@iinet.net.au>
+ * Added support output to stdout by Thomas Lundquist <thomasez@zelow.no>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#if ENABLE_BUNZIP2
+
+static
+char* make_new_name_bunzip2(char *filename)
+{
+ return make_new_name_generic(filename, "bz2");
+}
+
+static
+USE_DESKTOP(long long) int unpack_bunzip2(void)
+{
+ return uncompressStream(STDIN_FILENO, STDOUT_FILENO);
+}
+
+int bunzip2_main(int argc, char **argv);
+int bunzip2_main(int argc, char **argv)
+{
+ getopt32(argc, argv, "cf");
+ argv += optind;
+ if (applet_name[2] == 'c')
+ option_mask32 |= OPT_STDOUT;
+
+ return bbunpack(argv, make_new_name_bunzip2, unpack_bunzip2);
+}
+
+#endif
+
+
+/*
+ * Gzip implementation for busybox
+ *
+ * Based on GNU gzip v1.2.4 Copyright (C) 1992-1993 Jean-loup Gailly.
+ *
+ * Originally adjusted for busybox by Sven Rudolph <sr1@inf.tu-dresden.de>
+ * based on gzip sources
+ *
+ * Adjusted further by Erik Andersen <andersen@codepoet.org> to support files as
+ * well as stdin/stdout, and to generally behave itself wrt command line
+ * handling.
+ *
+ * General cleanup to better adhere to the style guide and make use of standard
+ * busybox functions by Glenn McGrath <bug1@iinet.net.au>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ *
+ * gzip (GNU zip) -- compress files with zip algorithm and 'compress' interface
+ * Copyright (C) 1992-1993 Jean-loup Gailly
+ * The unzip code was written and put in the public domain by Mark Adler.
+ * Portions of the lzw code are derived from the public domain 'compress'
+ * written by Spencer Thomas, Joe Orost, James Woods, Jim McKie, Steve Davies,
+ * Ken Turkowski, Dave Mack and Peter Jannesen.
+ *
+ * See the license_msg below and the file COPYING for the software license.
+ * See the file algorithm.doc for the compression algorithms and file formats.
+ */
+
+#if ENABLE_GUNZIP
+
+static
+char* make_new_name_gunzip(char *filename)
+{
+ char *extension = strrchr(filename, '.');
+
+ if (!extension)
+ return NULL;
+
+ extension++;
+ if (strcmp(extension, "tgz" + 1) == 0
+#ifdef CONFIG_FEATURE_GUNZIP_UNCOMPRESS
+ || strcmp(extension, "Z") == 0
+#endif
+ ) {
+ extension[-1] = '\0';
+ } else if(strcmp(extension, "tgz") == 0) {
+ filename = xstrdup(filename);
+ extension = strrchr(filename, '.');
+ extension[2] = 'a';
+ extension[3] = 'r';
+ } else {
+ return NULL;
+ }
+ return filename;
+}
+
+static
+USE_DESKTOP(long long) int unpack_gunzip(void)
+{
+ USE_DESKTOP(long long) int status = -1;
+
+ /* do the decompression, and cleanup */
+ if (xread_char(STDIN_FILENO) == 0x1f) {
+ unsigned char magic2;
+
+ magic2 = xread_char(STDIN_FILENO);
+ if (ENABLE_FEATURE_GUNZIP_UNCOMPRESS && magic2 == 0x9d) {
+ status = uncompress(STDIN_FILENO, STDOUT_FILENO);
+ } else if (magic2 == 0x8b) {
+ check_header_gzip_or_die(STDIN_FILENO);
+ status = inflate_gunzip(STDIN_FILENO, STDOUT_FILENO);
+ } else {
+ goto bad_magic;
+ }
+ if (status < 0) {
+ bb_error_msg("error inflating");
+ }
+ } else {
+ bad_magic:
+ bb_error_msg("invalid magic");
+ /* status is still == -1 */
+ }
+ return status;
+}
+
+int gunzip_main(int argc, char **argv);
+int gunzip_main(int argc, char **argv)
+{
+ getopt32(argc, argv, "cfvdt");
+ argv += optind;
+ /* if called as zcat */
+ if (applet_name[1] == 'c')
+ option_mask32 |= OPT_STDOUT;
+
+ return bbunpack(argv, make_new_name_gunzip, unpack_gunzip);
+}
+
+#endif
+
+
+/*
+ * Small lzma deflate implementation.
+ * Copyright (C) 2006 Aurelien Jacobs <aurel@gnuage.org>
+ *
+ * Based on bunzip.c from busybox
+ *
+ * Licensed under GPL v2, see file LICENSE in this tarball for details.
+ */
+
+#if ENABLE_UNLZMA
+
+static
+char* make_new_name_unlzma(char *filename)
+{
+ return make_new_name_generic(filename, "lzma");
+}
+
+static
+USE_DESKTOP(long long) int unpack_unlzma(void)
+{
+ return unlzma(STDIN_FILENO, STDOUT_FILENO);
+}
+
+int unlzma_main(int argc, char **argv);
+int unlzma_main(int argc, char **argv)
+{
+ getopt32(argc, argv, "c");
+ argv += optind;
+ /* lzmacat? */
+ if (applet_name[4] == 'c')
+ option_mask32 |= OPT_STDOUT;
+
+ return bbunpack(argv, make_new_name_unlzma, unpack_unlzma);
+}
+
+#endif
+
+
+/*
+ * Uncompress applet for busybox (c) 2002 Glenn McGrath
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#if ENABLE_UNCOMPRESS
+
+static
+char* make_new_name_uncompress(char *filename)
+{
+ return make_new_name_generic(filename, "Z");
+}
+
+static
+USE_DESKTOP(long long) int unpack_uncompress(void)
+{
+ USE_DESKTOP(long long) int status = -1;
+
+ if ((xread_char(STDIN_FILENO) != 0x1f) || (xread_char(STDIN_FILENO) != 0x9d)) {
+ bb_error_msg("invalid magic");
+ } else {
+ status = uncompress(STDIN_FILENO, STDOUT_FILENO);
+ }
+ return status;
+}
+
+int uncompress_main(int argc, char **argv);
+int uncompress_main(int argc, char **argv)
+{
+ getopt32(argc, argv, "cf");
+ argv += optind;
+
+ return bbunpack(argv, make_new_name_uncompress, unpack_uncompress);
+}
+
+#endif
diff --git a/i/pc104/initrd/conf/busybox/archival/cpio.c b/i/pc104/initrd/conf/busybox/archival/cpio.c
new file mode 100644
index 0000000..0113867
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/archival/cpio.c
@@ -0,0 +1,87 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini cpio implementation for busybox
+ *
+ * Copyright (C) 2001 by Glenn McGrath
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ *
+ * Limitations:
+ * Doesn't check CRC's
+ * Only supports new ASCII and CRC formats
+ *
+ */
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "unarchive.h"
+#include "busybox.h"
+
+#define CPIO_OPT_EXTRACT 0x01
+#define CPIO_OPT_TEST 0x02
+#define CPIO_OPT_UNCONDITIONAL 0x04
+#define CPIO_OPT_VERBOSE 0x08
+#define CPIO_OPT_FILE 0x10
+#define CPIO_OPT_CREATE_LEADING_DIR 0x20
+#define CPIO_OPT_PRESERVE_MTIME 0x40
+
+int cpio_main(int argc, char **argv);
+int cpio_main(int argc, char **argv)
+{
+ archive_handle_t *archive_handle;
+ char *cpio_filename = NULL;
+ unsigned opt;
+
+ /* Initialise */
+ archive_handle = init_handle();
+ archive_handle->src_fd = STDIN_FILENO;
+ archive_handle->seek = seek_by_read;
+ archive_handle->flags = ARCHIVE_EXTRACT_NEWER | ARCHIVE_PRESERVE_DATE;
+
+ opt = getopt32(argc, argv, "ituvF:dm", &cpio_filename);
+
+ /* One of either extract or test options must be given */
+ if ((opt & (CPIO_OPT_TEST | CPIO_OPT_EXTRACT)) == 0) {
+ bb_show_usage();
+ }
+
+ if (opt & CPIO_OPT_TEST) {
+ /* if both extract and test options are given, ignore extract option */
+ if (opt & CPIO_OPT_EXTRACT) {
+ opt &= ~CPIO_OPT_EXTRACT;
+ }
+ archive_handle->action_header = header_list;
+ }
+ if (opt & CPIO_OPT_EXTRACT) {
+ archive_handle->action_data = data_extract_all;
+ }
+ if (opt & CPIO_OPT_UNCONDITIONAL) {
+ archive_handle->flags |= ARCHIVE_EXTRACT_UNCONDITIONAL;
+ archive_handle->flags &= ~ARCHIVE_EXTRACT_NEWER;
+ }
+ if (opt & CPIO_OPT_VERBOSE) {
+ if (archive_handle->action_header == header_list) {
+ archive_handle->action_header = header_verbose_list;
+ } else {
+ archive_handle->action_header = header_list;
+ }
+ }
+ if (cpio_filename) { /* CPIO_OPT_FILE */
+ archive_handle->src_fd = xopen(cpio_filename, O_RDONLY);
+ archive_handle->seek = seek_by_jump;
+ }
+ if (opt & CPIO_OPT_CREATE_LEADING_DIR) {
+ archive_handle->flags |= ARCHIVE_CREATE_LEADING_DIRS;
+ }
+
+ while (optind < argc) {
+ archive_handle->filter = filter_accept_list;
+ llist_add_to(&(archive_handle->accept), argv[optind]);
+ optind++;
+ }
+
+ while (get_header_cpio(archive_handle) == EXIT_SUCCESS);
+
+ return EXIT_SUCCESS;
+}
diff --git a/i/pc104/initrd/conf/busybox/archival/dpkg.c b/i/pc104/initrd/conf/busybox/archival/dpkg.c
new file mode 100644
index 0000000..0a42deb
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/archival/dpkg.c
@@ -0,0 +1,1754 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * mini dpkg implementation for busybox.
+ * this is not meant as a replacement for dpkg
+ *
+ * written by glenn mcgrath with the help of others
+ * copyright (c) 2001 by glenn mcgrath
+ *
+ * started life as a busybox implementation of udpkg
+ *
+ * licensed under gplv2 or later, see file license in this tarball for details.
+ */
+
+/*
+ * known difference between busybox dpkg and the official dpkg that i don't
+ * consider important, its worth keeping a note of differences anyway, just to
+ * make it easier to maintain.
+ * - the first value for the confflile: field isnt placed on a new line.
+ * - when installing a package the status: field is placed at the end of the
+ * section, rather than just after the package: field.
+ *
+ * bugs that need to be fixed
+ * - (unknown, please let me know when you find any)
+ *
+ */
+
+#include "busybox.h"
+#include "unarchive.h"
+
+/* note: if you vary hash_prime sizes be aware,
+ * 1) tweaking these will have a big effect on how much memory this program uses.
+ * 2) for computational efficiency these hash tables should be at least 20%
+ * larger than the maximum number of elements stored in it.
+ * 3) all _hash_prime's must be a prime number or chaos is assured, if your looking
+ * for a prime, try http://www.utm.edu/research/primes/lists/small/10000.txt
+ * 4) if you go bigger than 15 bits you may get into trouble (untested) as its
+ * sometimes cast to an unsigned, if you go to 16 bit you will overlap
+ * int's and chaos is assured, 16381 is the max prime for 14 bit field
+ */
+
+/* NAME_HASH_PRIME, Stores package names and versions,
+ * I estimate it should be at least 50% bigger than PACKAGE_HASH_PRIME,
+ * as there a lot of duplicate version numbers */
+#define NAME_HASH_PRIME 16381
+
+/* PACKAGE_HASH_PRIME, Maximum number of unique packages,
+ * It must not be smaller than STATUS_HASH_PRIME,
+ * Currently only packages from status_hashtable are stored in here, but in
+ * future this may be used to store packages not only from a status file,
+ * but an available_hashtable, and even multiple packages files.
+ * Package can be stored more than once if they have different versions.
+ * e.g. The same package may have different versions in the status file
+ * and available file */
+#define PACKAGE_HASH_PRIME 10007
+typedef struct edge_s {
+ unsigned operator:3;
+ unsigned type:4;
+ unsigned name:14;
+ unsigned version:14;
+} edge_t;
+
+typedef struct common_node_s {
+ unsigned name:14;
+ unsigned version:14;
+ unsigned num_of_edges:14;
+ edge_t **edge;
+} common_node_t;
+
+/* Currently it doesnt store packages that have state-status of not-installed
+ * So it only really has to be the size of the maximum number of packages
+ * likely to be installed at any one time, so there is a bit of leeway here */
+#define STATUS_HASH_PRIME 8191
+typedef struct status_node_s {
+ unsigned package:14; /* has to fit PACKAGE_HASH_PRIME */
+ unsigned status:14; /* has to fit STATUS_HASH_PRIME */
+} status_node_t;
+
+/* Were statically declared here, but such a big bss is nommu-unfriendly */
+static char **name_hashtable; /* [NAME_HASH_PRIME + 1] */
+static common_node_t **package_hashtable; /* [PACKAGE_HASH_PRIME + 1] */
+static status_node_t **status_hashtable; /* [STATUS_HASH_PRIME + 1] */
+
+/* Even numbers are for 'extras', like ored dependencies or null */
+enum edge_type_e {
+ EDGE_NULL = 0,
+ EDGE_PRE_DEPENDS = 1,
+ EDGE_OR_PRE_DEPENDS = 2,
+ EDGE_DEPENDS = 3,
+ EDGE_OR_DEPENDS = 4,
+ EDGE_REPLACES = 5,
+ EDGE_PROVIDES = 7,
+ EDGE_CONFLICTS = 9,
+ EDGE_SUGGESTS = 11,
+ EDGE_RECOMMENDS = 13,
+ EDGE_ENHANCES = 15
+};
+enum operator_e {
+ VER_NULL = 0,
+ VER_EQUAL = 1,
+ VER_LESS = 2,
+ VER_LESS_EQUAL = 3,
+ VER_MORE = 4,
+ VER_MORE_EQUAL = 5,
+ VER_ANY = 6
+};
+
+typedef struct deb_file_s {
+ char *control_file;
+ char *filename;
+ unsigned package:14;
+} deb_file_t;
+
+
+static void make_hash(const char *key, unsigned *start, unsigned *decrement, const int hash_prime)
+{
+ unsigned long int hash_num = key[0];
+ int len = strlen(key);
+ int i;
+
+ /* Maybe i should have uses a "proper" hashing algorithm here instead
+ * of making one up myself, seems to be working ok though. */
+ for (i = 1; i < len; i++) {
+ /* shifts the ascii based value and adds it to previous value
+ * shift amount is mod 24 because long int is 32 bit and data
+ * to be shifted is 8, don't want to shift data to where it has
+ * no effect*/
+ hash_num += ((key[i] + key[i-1]) << ((key[i] * i) % 24));
+ }
+ *start = (unsigned) hash_num % hash_prime;
+ *decrement = (unsigned) 1 + (hash_num % (hash_prime - 1));
+}
+
+/* this adds the key to the hash table */
+static int search_name_hashtable(const char *key)
+{
+ unsigned probe_address = 0;
+ unsigned probe_decrement = 0;
+
+ make_hash(key, &probe_address, &probe_decrement, NAME_HASH_PRIME);
+ while (name_hashtable[probe_address] != NULL) {
+ if (strcmp(name_hashtable[probe_address], key) == 0) {
+ return probe_address;
+ }
+ probe_address -= probe_decrement;
+ if ((int)probe_address < 0) {
+ probe_address += NAME_HASH_PRIME;
+ }
+ }
+ name_hashtable[probe_address] = xstrdup(key);
+ return probe_address;
+}
+
+/* this DOESNT add the key to the hashtable
+ * TODO make it consistent with search_name_hashtable
+ */
+static unsigned search_status_hashtable(const char *key)
+{
+ unsigned probe_address = 0;
+ unsigned probe_decrement = 0;
+
+ make_hash(key, &probe_address, &probe_decrement, STATUS_HASH_PRIME);
+ while (status_hashtable[probe_address] != NULL) {
+ if (strcmp(key, name_hashtable[package_hashtable[status_hashtable[probe_address]->package]->name]) == 0) {
+ break;
+ }
+ probe_address -= probe_decrement;
+ if ((int)probe_address < 0) {
+ probe_address += STATUS_HASH_PRIME;
+ }
+ }
+ return probe_address;
+}
+
+/* Need to rethink version comparison, maybe the official dpkg has something i can use ? */
+static int version_compare_part(const char *version1, const char *version2)
+{
+ int upstream_len1 = 0;
+ int upstream_len2 = 0;
+ char *name1_char;
+ char *name2_char;
+ int len1 = 0;
+ int len2 = 0;
+ int tmp_int;
+ int ver_num1;
+ int ver_num2;
+
+ if (version1 == NULL) {
+ version1 = xstrdup("");
+ }
+ if (version2 == NULL) {
+ version2 = xstrdup("");
+ }
+ upstream_len1 = strlen(version1);
+ upstream_len2 = strlen(version2);
+
+ while ((len1 < upstream_len1) || (len2 < upstream_len2)) {
+ /* Compare non-digit section */
+ tmp_int = strcspn(&version1[len1], "0123456789");
+ name1_char = xstrndup(&version1[len1], tmp_int);
+ len1 += tmp_int;
+ tmp_int = strcspn(&version2[len2], "0123456789");
+ name2_char = xstrndup(&version2[len2], tmp_int);
+ len2 += tmp_int;
+ tmp_int = strcmp(name1_char, name2_char);
+ free(name1_char);
+ free(name2_char);
+ if (tmp_int != 0) {
+ return tmp_int;
+ }
+
+ /* Compare digits */
+ tmp_int = strspn(&version1[len1], "0123456789");
+ name1_char = xstrndup(&version1[len1], tmp_int);
+ len1 += tmp_int;
+ tmp_int = strspn(&version2[len2], "0123456789");
+ name2_char = xstrndup(&version2[len2], tmp_int);
+ len2 += tmp_int;
+ ver_num1 = atoi(name1_char);
+ ver_num2 = atoi(name2_char);
+ free(name1_char);
+ free(name2_char);
+ if (ver_num1 < ver_num2) {
+ return -1;
+ }
+ if (ver_num1 > ver_num2) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* if ver1 < ver2 return -1,
+ * if ver1 = ver2 return 0,
+ * if ver1 > ver2 return 1,
+ */
+static int version_compare(const unsigned ver1, const unsigned ver2)
+{
+ char *ch_ver1 = name_hashtable[ver1];
+ char *ch_ver2 = name_hashtable[ver2];
+
+ char epoch1, epoch2;
+ char *deb_ver1, *deb_ver2;
+ char *ver1_ptr, *ver2_ptr;
+ char *upstream_ver1;
+ char *upstream_ver2;
+ int result;
+
+ /* Compare epoch */
+ if (ch_ver1[1] == ':') {
+ epoch1 = ch_ver1[0];
+ ver1_ptr = strchr(ch_ver1, ':') + 1;
+ } else {
+ epoch1 = '0';
+ ver1_ptr = ch_ver1;
+ }
+ if (ch_ver2[1] == ':') {
+ epoch2 = ch_ver2[0];
+ ver2_ptr = strchr(ch_ver2, ':') + 1;
+ } else {
+ epoch2 = '0';
+ ver2_ptr = ch_ver2;
+ }
+ if (epoch1 < epoch2) {
+ return -1;
+ }
+ else if (epoch1 > epoch2) {
+ return 1;
+ }
+
+ /* Compare upstream version */
+ upstream_ver1 = xstrdup(ver1_ptr);
+ upstream_ver2 = xstrdup(ver2_ptr);
+
+ /* Chop off debian version, and store for later use */
+ deb_ver1 = strrchr(upstream_ver1, '-');
+ deb_ver2 = strrchr(upstream_ver2, '-');
+ if (deb_ver1) {
+ deb_ver1[0] = '\0';
+ deb_ver1++;
+ }
+ if (deb_ver2) {
+ deb_ver2[0] = '\0';
+ deb_ver2++;
+ }
+ result = version_compare_part(upstream_ver1, upstream_ver2);
+ if (!result)
+ /* Compare debian versions */
+ result = version_compare_part(deb_ver1, deb_ver2);
+
+ free(upstream_ver1);
+ free(upstream_ver2);
+ return result;
+}
+
+static int test_version(const unsigned version1, const unsigned version2, const unsigned operator)
+{
+ const int version_result = version_compare(version1, version2);
+ switch (operator) {
+ case VER_ANY:
+ return TRUE;
+ case VER_EQUAL:
+ return (version_result == 0);
+ case VER_LESS:
+ return (version_result < 0);
+ case VER_LESS_EQUAL:
+ return (version_result <= 0);
+ case VER_MORE:
+ return (version_result > 0);
+ case VER_MORE_EQUAL:
+ return (version_result >= 0);
+ }
+ return FALSE;
+}
+
+
+static int search_package_hashtable(const unsigned name, const unsigned version, const unsigned operator)
+{
+ unsigned probe_address = 0;
+ unsigned probe_decrement = 0;
+
+ make_hash(name_hashtable[name], &probe_address, &probe_decrement, PACKAGE_HASH_PRIME);
+ while (package_hashtable[probe_address] != NULL) {
+ if (package_hashtable[probe_address]->name == name) {
+ if (operator == VER_ANY) {
+ return probe_address;
+ }
+ if (test_version(package_hashtable[probe_address]->version, version, operator)) {
+ return probe_address;
+ }
+ }
+ probe_address -= probe_decrement;
+ if ((int)probe_address < 0) {
+ probe_address += PACKAGE_HASH_PRIME;
+ }
+ }
+ return probe_address;
+}
+
+/*
+ * This function searches through the entire package_hashtable looking
+ * for a package which provides "needle". It returns the index into
+ * the package_hashtable for the providing package.
+ *
+ * needle is the index into name_hashtable of the package we are
+ * looking for.
+ *
+ * start_at is the index in the package_hashtable to start looking
+ * at. If start_at is -1 then start at the beginning. This is to allow
+ * for repeated searches since more than one package might provide
+ * needle.
+ *
+ * FIXME: I don't think this is very efficient, but I thought I'd keep
+ * it simple for now until it proves to be a problem.
+ */
+static int search_for_provides(int needle, int start_at) {
+ int i, j;
+ common_node_t *p;
+ for (i = start_at + 1; i < PACKAGE_HASH_PRIME; i++) {
+ p = package_hashtable[i];
+ if (p == NULL)
+ continue;
+ for (j = 0; j < p->num_of_edges; j++)
+ if (p->edge[j]->type == EDGE_PROVIDES && p->edge[j]->name == needle)
+ return i;
+ }
+ return -1;
+}
+
+/*
+ * Add an edge to a node
+ */
+static void add_edge_to_node(common_node_t *node, edge_t *edge)
+{
+ node->num_of_edges++;
+ node->edge = xrealloc(node->edge, sizeof(edge_t) * (node->num_of_edges + 1));
+ node->edge[node->num_of_edges - 1] = edge;
+}
+
+/*
+ * Create one new node and one new edge for every dependency.
+ *
+ * Dependencies which contain multiple alternatives are represented as
+ * an EDGE_OR_PRE_DEPENDS or EDGE_OR_DEPENDS node, followed by a
+ * number of EDGE_PRE_DEPENDS or EDGE_DEPENDS nodes. The name field of
+ * the OR edge contains the full dependency string while the version
+ * field contains the number of EDGE nodes which follow as part of
+ * this alternative.
+ */
+static void add_split_dependencies(common_node_t *parent_node, const char *whole_line, unsigned edge_type)
+{
+ char *line = xstrdup(whole_line);
+ char *line2;
+ char *line_ptr1 = NULL;
+ char *line_ptr2 = NULL;
+ char *field;
+ char *field2;
+ char *version;
+ edge_t *edge;
+ edge_t *or_edge;
+ int offset_ch;
+
+ field = strtok_r(line, ",", &line_ptr1);
+ do {
+ /* skip leading spaces */
+ field += strspn(field, " ");
+ line2 = xstrdup(field);
+ field2 = strtok_r(line2, "|", &line_ptr2);
+ or_edge = NULL;
+ if ((edge_type == EDGE_DEPENDS || edge_type == EDGE_PRE_DEPENDS)
+ && (strcmp(field, field2) != 0)
+ ) {
+ or_edge = xmalloc(sizeof(edge_t));
+ or_edge->type = edge_type + 1;
+ or_edge->name = search_name_hashtable(field);
+ or_edge->version = 0; // tracks the number of altenatives
+ add_edge_to_node(parent_node, or_edge);
+ }
+
+ do {
+ edge = xmalloc(sizeof(edge_t));
+ edge->type = edge_type;
+
+ /* Skip any extra leading spaces */
+ field2 += strspn(field2, " ");
+
+ /* Get dependency version info */
+ version = strchr(field2, '(');
+ if (version == NULL) {
+ edge->operator = VER_ANY;
+ /* Get the versions hash number, adding it if the number isnt already in there */
+ edge->version = search_name_hashtable("ANY");
+ } else {
+ /* Skip leading ' ' or '(' */
+ version += strspn(field2, " (");
+ /* Calculate length of any operator characters */
+ offset_ch = strspn(version, "<=>");
+ /* Determine operator */
+ if (offset_ch > 0) {
+ if (strncmp(version, "=", offset_ch) == 0) {
+ edge->operator = VER_EQUAL;
+ }
+ else if (strncmp(version, "<<", offset_ch) == 0) {
+ edge->operator = VER_LESS;
+ }
+ else if (strncmp(version, "<=", offset_ch) == 0) {
+ edge->operator = VER_LESS_EQUAL;
+ }
+ else if (strncmp(version, ">>", offset_ch) == 0) {
+ edge->operator = VER_MORE;
+ }
+ else if (strncmp(version, ">=", offset_ch) == 0) {
+ edge->operator = VER_MORE_EQUAL;
+ } else {
+ bb_error_msg_and_die("illegal operator");
+ }
+ }
+ /* skip to start of version numbers */
+ version += offset_ch;
+ version += strspn(version, " ");
+
+ /* Truncate version at trailing ' ' or ')' */
+ version[strcspn(version, " )")] = '\0';
+ /* Get the versions hash number, adding it if the number isnt already in there */
+ edge->version = search_name_hashtable(version);
+ }
+
+ /* Get the dependency name */
+ field2[strcspn(field2, " (")] = '\0';
+ edge->name = search_name_hashtable(field2);
+
+ if (or_edge)
+ or_edge->version++;
+
+ add_edge_to_node(parent_node, edge);
+ } while ((field2 = strtok_r(NULL, "|", &line_ptr2)) != NULL);
+ free(line2);
+ } while ((field = strtok_r(NULL, ",", &line_ptr1)) != NULL);
+ free(line);
+}
+
+static void free_package(common_node_t *node)
+{
+ unsigned i;
+ if (node) {
+ for (i = 0; i < node->num_of_edges; i++) {
+ free(node->edge[i]);
+ }
+ free(node->edge);
+ free(node);
+ }
+}
+
+/*
+ * Gets the next package field from package_buffer, seperated into the field name
+ * and field value, it returns the int offset to the first character of the next field
+ */
+static int read_package_field(const char *package_buffer, char **field_name, char **field_value)
+{
+ int offset_name_start = 0;
+ int offset_name_end = 0;
+ int offset_value_start = 0;
+ int offset_value_end = 0;
+ int offset = 0;
+ int next_offset;
+ int name_length;
+ int value_length;
+ int exit_flag = FALSE;
+
+ if (package_buffer == NULL) {
+ *field_name = NULL;
+ *field_value = NULL;
+ return -1;
+ }
+ while (1) {
+ next_offset = offset + 1;
+ switch (package_buffer[offset]) {
+ case '\0':
+ exit_flag = TRUE;
+ break;
+ case ':':
+ if (offset_name_end == 0) {
+ offset_name_end = offset;
+ offset_value_start = next_offset;
+ }
+ /* TODO: Name might still have trailing spaces if ':' isnt
+ * immediately after name */
+ break;
+ case '\n':
+ /* TODO: The char next_offset may be out of bounds */
+ if (package_buffer[next_offset] != ' ') {
+ exit_flag = TRUE;
+ break;
+ }
+ case '\t':
+ case ' ':
+ /* increment the value start point if its a just filler */
+ if (offset_name_start == offset) {
+ offset_name_start++;
+ }
+ if (offset_value_start == offset) {
+ offset_value_start++;
+ }
+ break;
+ }
+ if (exit_flag) {
+ /* Check that the names are valid */
+ offset_value_end = offset;
+ name_length = offset_name_end - offset_name_start;
+ value_length = offset_value_end - offset_value_start;
+ if (name_length == 0) {
+ break;
+ }
+ if ((name_length > 0) && (value_length > 0)) {
+ break;
+ }
+
+ /* If not valid, start fresh with next field */
+ exit_flag = FALSE;
+ offset_name_start = offset + 1;
+ offset_name_end = 0;
+ offset_value_start = offset + 1;
+ offset_value_end = offset + 1;
+ offset++;
+ }
+ offset++;
+ }
+ *field_name = NULL;
+ if (name_length) {
+ *field_name = xstrndup(&package_buffer[offset_name_start], name_length);
+ }
+ *field_value = NULL;
+ if (value_length > 0) {
+ *field_value = xstrndup(&package_buffer[offset_value_start], value_length);
+ }
+ return next_offset;
+}
+
+static unsigned fill_package_struct(char *control_buffer)
+{
+ static const char *const field_names[] = { "Package", "Version",
+ "Pre-Depends", "Depends","Replaces", "Provides",
+ "Conflicts", "Suggests", "Recommends", "Enhances", 0
+ };
+
+ common_node_t *new_node = xzalloc(sizeof(common_node_t));
+ char *field_name;
+ char *field_value;
+ int field_start = 0;
+ int num = -1;
+ int buffer_length = strlen(control_buffer);
+
+ new_node->version = search_name_hashtable("unknown");
+ while (field_start < buffer_length) {
+ unsigned field_num;
+
+ field_start += read_package_field(&control_buffer[field_start],
+ &field_name, &field_value);
+
+ if (field_name == NULL) {
+ goto fill_package_struct_cleanup; /* Oh no, the dreaded goto statement ! */
+ }
+
+ field_num = index_in_str_array(field_names, field_name);
+ switch (field_num) {
+ case 0: /* Package */
+ new_node->name = search_name_hashtable(field_value);
+ break;
+ case 1: /* Version */
+ new_node->version = search_name_hashtable(field_value);
+ break;
+ case 2: /* Pre-Depends */
+ add_split_dependencies(new_node, field_value, EDGE_PRE_DEPENDS);
+ break;
+ case 3: /* Depends */
+ add_split_dependencies(new_node, field_value, EDGE_DEPENDS);
+ break;
+ case 4: /* Replaces */
+ add_split_dependencies(new_node, field_value, EDGE_REPLACES);
+ break;
+ case 5: /* Provides */
+ add_split_dependencies(new_node, field_value, EDGE_PROVIDES);
+ break;
+ case 6: /* Conflicts */
+ add_split_dependencies(new_node, field_value, EDGE_CONFLICTS);
+ break;
+ case 7: /* Suggests */
+ add_split_dependencies(new_node, field_value, EDGE_SUGGESTS);
+ break;
+ case 8: /* Recommends */
+ add_split_dependencies(new_node, field_value, EDGE_RECOMMENDS);
+ break;
+ case 9: /* Enhances */
+ add_split_dependencies(new_node, field_value, EDGE_ENHANCES);
+ break;
+ }
+ fill_package_struct_cleanup:
+ free(field_name);
+ free(field_value);
+ }
+
+ if (new_node->version == search_name_hashtable("unknown")) {
+ free_package(new_node);
+ return -1;
+ }
+ num = search_package_hashtable(new_node->name, new_node->version, VER_EQUAL);
+ free_package(package_hashtable[num]);
+ package_hashtable[num] = new_node;
+ return num;
+}
+
+/* if num = 1, it returns the want status, 2 returns flag, 3 returns status */
+static unsigned get_status(const unsigned status_node, const int num)
+{
+ char *status_string = name_hashtable[status_hashtable[status_node]->status];
+ char *state_sub_string;
+ unsigned state_sub_num;
+ int len;
+ int i;
+
+ /* set tmp_string to point to the start of the word number */
+ for (i = 1; i < num; i++) {
+ /* skip past a word */
+ status_string += strcspn(status_string, " ");
+ /* skip past the separating spaces */
+ status_string += strspn(status_string, " ");
+ }
+ len = strcspn(status_string, " \n");
+ state_sub_string = xstrndup(status_string, len);
+ state_sub_num = search_name_hashtable(state_sub_string);
+ free(state_sub_string);
+ return state_sub_num;
+}
+
+static void set_status(const unsigned status_node_num, const char *new_value, const int position)
+{
+ const unsigned new_value_len = strlen(new_value);
+ const unsigned new_value_num = search_name_hashtable(new_value);
+ unsigned want = get_status(status_node_num, 1);
+ unsigned flag = get_status(status_node_num, 2);
+ unsigned status = get_status(status_node_num, 3);
+ int want_len = strlen(name_hashtable[want]);
+ int flag_len = strlen(name_hashtable[flag]);
+ int status_len = strlen(name_hashtable[status]);
+ char *new_status;
+
+ switch (position) {
+ case 1:
+ want = new_value_num;
+ want_len = new_value_len;
+ break;
+ case 2:
+ flag = new_value_num;
+ flag_len = new_value_len;
+ break;
+ case 3:
+ status = new_value_num;
+ status_len = new_value_len;
+ break;
+ default:
+ bb_error_msg_and_die("DEBUG ONLY: this shouldnt happen");
+ }
+
+ new_status = xasprintf("%s %s %s", name_hashtable[want], name_hashtable[flag], name_hashtable[status]);
+ status_hashtable[status_node_num]->status = search_name_hashtable(new_status);
+ free(new_status);
+}
+
+static const char *describe_status(int status_num) {
+ int status_want, status_state ;
+ if (status_hashtable[status_num] == NULL || status_hashtable[status_num]->status == 0)
+ return "is not installed or flagged to be installed\n";
+
+ status_want = get_status(status_num, 1);
+ status_state = get_status(status_num, 3);
+
+ if (status_state == search_name_hashtable("installed")) {
+ if (status_want == search_name_hashtable("install"))
+ return "is installed";
+ if (status_want == search_name_hashtable("deinstall"))
+ return "is marked to be removed";
+ if (status_want == search_name_hashtable("purge"))
+ return "is marked to be purged";
+ }
+ if (status_want == search_name_hashtable("unknown"))
+ return "is in an indeterminate state";
+ if (status_want == search_name_hashtable("install"))
+ return "is marked to be installed";
+
+ return "is not installed or flagged to be installed";
+}
+
+
+static void index_status_file(const char *filename)
+{
+ FILE *status_file;
+ char *control_buffer;
+ char *status_line;
+ status_node_t *status_node = NULL;
+ unsigned status_num;
+
+ status_file = xfopen(filename, "r");
+ while ((control_buffer = xmalloc_fgets_str(status_file, "\n\n")) != NULL) {
+ const unsigned package_num = fill_package_struct(control_buffer);
+ if (package_num != -1) {
+ status_node = xmalloc(sizeof(status_node_t));
+ /* fill_package_struct doesnt handle the status field */
+ status_line = strstr(control_buffer, "Status:");
+ if (status_line != NULL) {
+ status_line += 7;
+ status_line += strspn(status_line, " \n\t");
+ status_line = xstrndup(status_line, strcspn(status_line, "\n"));
+ status_node->status = search_name_hashtable(status_line);
+ free(status_line);
+ }
+ status_node->package = package_num;
+ status_num = search_status_hashtable(name_hashtable[package_hashtable[status_node->package]->name]);
+ status_hashtable[status_num] = status_node;
+ }
+ free(control_buffer);
+ }
+ fclose(status_file);
+}
+
+static void write_buffer_no_status(FILE *new_status_file, const char *control_buffer)
+{
+ char *name;
+ char *value;
+ int start = 0;
+ while (1) {
+ start += read_package_field(&control_buffer[start], &name, &value);
+ if (name == NULL) {
+ break;
+ }
+ if (strcmp(name, "Status") != 0) {
+ fprintf(new_status_file, "%s: %s\n", name, value);
+ }
+ }
+}
+
+/* This could do with a cleanup */
+static void write_status_file(deb_file_t **deb_file)
+{
+ FILE *old_status_file = xfopen("/var/lib/dpkg/status", "r");
+ FILE *new_status_file = xfopen("/var/lib/dpkg/status.udeb", "w");
+ char *package_name;
+ char *status_from_file;
+ char *control_buffer = NULL;
+ char *tmp_string;
+ int status_num;
+ int field_start = 0;
+ int write_flag;
+ int i = 0;
+
+ /* Update previously known packages */
+ while ((control_buffer = xmalloc_fgets_str(old_status_file, "\n\n")) != NULL) {
+ if ((tmp_string = strstr(control_buffer, "Package:")) == NULL) {
+ continue;
+ }
+
+ tmp_string += 8;
+ tmp_string += strspn(tmp_string, " \n\t");
+ package_name = xstrndup(tmp_string, strcspn(tmp_string, "\n"));
+ write_flag = FALSE;
+ tmp_string = strstr(control_buffer, "Status:");
+ if (tmp_string != NULL) {
+ /* Seperate the status value from the control buffer */
+ tmp_string += 7;
+ tmp_string += strspn(tmp_string, " \n\t");
+ status_from_file = xstrndup(tmp_string, strcspn(tmp_string, "\n"));
+ } else {
+ status_from_file = NULL;
+ }
+
+ /* Find this package in the status hashtable */
+ status_num = search_status_hashtable(package_name);
+ if (status_hashtable[status_num] != NULL) {
+ const char *status_from_hashtable = name_hashtable[status_hashtable[status_num]->status];
+ if (strcmp(status_from_file, status_from_hashtable) != 0) {
+ /* New status isnt exactly the same as old status */
+ const int state_status = get_status(status_num, 3);
+ if ((strcmp("installed", name_hashtable[state_status]) == 0) ||
+ (strcmp("unpacked", name_hashtable[state_status]) == 0)) {
+ /* We need to add the control file from the package */
+ i = 0;
+ while (deb_file[i] != NULL) {
+ if (strcmp(package_name, name_hashtable[package_hashtable[deb_file[i]->package]->name]) == 0) {
+ /* Write a status file entry with a modified status */
+ /* remove trailing \n's */
+ write_buffer_no_status(new_status_file, deb_file[i]->control_file);
+ set_status(status_num, "ok", 2);
+ fprintf(new_status_file, "Status: %s\n\n",
+ name_hashtable[status_hashtable[status_num]->status]);
+ write_flag = TRUE;
+ break;
+ }
+ i++;
+ }
+ /* This is temperary, debugging only */
+ if (deb_file[i] == NULL) {
+ bb_error_msg_and_die("ALERT: cannot find a control file, "
+ "your status file may be broken, status may be "
+ "incorrect for %s", package_name);
+ }
+ }
+ else if (strcmp("not-installed", name_hashtable[state_status]) == 0) {
+ /* Only write the Package, Status, Priority and Section lines */
+ fprintf(new_status_file, "Package: %s\n", package_name);
+ fprintf(new_status_file, "Status: %s\n", status_from_hashtable);
+
+ while (1) {
+ char *field_name;
+ char *field_value;
+ field_start += read_package_field(&control_buffer[field_start], &field_name, &field_value);
+ if (field_name == NULL) {
+ break;
+ }
+ if ((strcmp(field_name, "Priority") == 0) ||
+ (strcmp(field_name, "Section") == 0)) {
+ fprintf(new_status_file, "%s: %s\n", field_name, field_value);
+ }
+ }
+ write_flag = TRUE;
+ fputs("\n", new_status_file);
+ }
+ else if (strcmp("config-files", name_hashtable[state_status]) == 0) {
+ /* only change the status line */
+ while (1) {
+ char *field_name;
+ char *field_value;
+ field_start += read_package_field(&control_buffer[field_start], &field_name, &field_value);
+ if (field_name == NULL) {
+ break;
+ }
+ /* Setup start point for next field */
+ if (strcmp(field_name, "Status") == 0) {
+ fprintf(new_status_file, "Status: %s\n", status_from_hashtable);
+ } else {
+ fprintf(new_status_file, "%s: %s\n", field_name, field_value);
+ }
+ }
+ write_flag = TRUE;
+ fputs("\n", new_status_file);
+ }
+ }
+ }
+ /* If the package from the status file wasnt handle above, do it now*/
+ if (! write_flag) {
+ fprintf(new_status_file, "%s\n\n", control_buffer);
+ }
+
+ free(status_from_file);
+ free(package_name);
+ free(control_buffer);
+ }
+
+ /* Write any new packages */
+ for (i = 0; deb_file[i] != NULL; i++) {
+ status_num = search_status_hashtable(name_hashtable[package_hashtable[deb_file[i]->package]->name]);
+ if (strcmp("reinstreq", name_hashtable[get_status(status_num, 2)]) == 0) {
+ write_buffer_no_status(new_status_file, deb_file[i]->control_file);
+ set_status(status_num, "ok", 2);
+ fprintf(new_status_file, "Status: %s\n\n", name_hashtable[status_hashtable[status_num]->status]);
+ }
+ }
+ fclose(old_status_file);
+ fclose(new_status_file);
+
+
+ /* Create a separate backfile to dpkg */
+ if (rename("/var/lib/dpkg/status", "/var/lib/dpkg/status.udeb.bak") == -1) {
+ struct stat stat_buf;
+ xstat("/var/lib/dpkg/status", &stat_buf);
+ /* Its ok if renaming the status file fails because status
+ * file doesnt exist, maybe we are starting from scratch */
+ bb_error_msg("no status file found, creating new one");
+ }
+
+ if (rename("/var/lib/dpkg/status.udeb", "/var/lib/dpkg/status") == -1) {
+ bb_error_msg_and_die("DANGER: cannot create status file, "
+ "you need to manually repair your status file");
+ }
+}
+
+/* This function returns TRUE if the given package can satisfy a
+ * dependency of type depend_type.
+ *
+ * A pre-depends is satisfied only if a package is already installed,
+ * which a regular depends can be satisfied by a package which we want
+ * to install.
+ */
+static int package_satisfies_dependency(int package, int depend_type)
+{
+ int status_num = search_status_hashtable(name_hashtable[package_hashtable[package]->name]);
+
+ /* status could be unknown if package is a pure virtual
+ * provides which cannot satisfy any dependency by itself.
+ */
+ if (status_hashtable[status_num] == NULL)
+ return 0;
+
+ switch (depend_type) {
+ case EDGE_PRE_DEPENDS: return get_status(status_num, 3) == search_name_hashtable("installed");
+ case EDGE_DEPENDS: return get_status(status_num, 1) == search_name_hashtable("install");
+ }
+ return 0;
+}
+
+static int check_deps(deb_file_t **deb_file, int deb_start, int dep_max_count)
+{
+ int *conflicts = NULL;
+ int conflicts_num = 0;
+ int i = deb_start;
+ int j;
+
+ /* Check for conflicts
+ * TODO: TEST if conflicts with other packages to be installed
+ *
+ * Add install packages and the packages they provide
+ * to the list of files to check conflicts for
+ */
+
+ /* Create array of package numbers to check against
+ * installed package for conflicts*/
+ while (deb_file[i] != NULL) {
+ const unsigned package_num = deb_file[i]->package;
+ conflicts = xrealloc(conflicts, sizeof(int) * (conflicts_num + 1));
+ conflicts[conflicts_num] = package_num;
+ conflicts_num++;
+ /* add provides to conflicts list */
+ for (j = 0; j < package_hashtable[package_num]->num_of_edges; j++) {
+ if (package_hashtable[package_num]->edge[j]->type == EDGE_PROVIDES) {
+ const int conflicts_package_num = search_package_hashtable(
+ package_hashtable[package_num]->edge[j]->name,
+ package_hashtable[package_num]->edge[j]->version,
+ package_hashtable[package_num]->edge[j]->operator);
+ if (package_hashtable[conflicts_package_num] == NULL) {
+ /* create a new package */
+ common_node_t *new_node = xzalloc(sizeof(common_node_t));
+ new_node->name = package_hashtable[package_num]->edge[j]->name;
+ new_node->version = package_hashtable[package_num]->edge[j]->version;
+ package_hashtable[conflicts_package_num] = new_node;
+ }
+ conflicts = xrealloc(conflicts, sizeof(int) * (conflicts_num + 1));
+ conflicts[conflicts_num] = conflicts_package_num;
+ conflicts_num++;
+ }
+ }
+ i++;
+ }
+
+ /* Check conflicts */
+ i = 0;
+ while (deb_file[i] != NULL) {
+ const common_node_t *package_node = package_hashtable[deb_file[i]->package];
+ int status_num = 0;
+ status_num = search_status_hashtable(name_hashtable[package_node->name]);
+
+ if (get_status(status_num, 3) == search_name_hashtable("installed")) {
+ i++;
+ continue;
+ }
+
+ for (j = 0; j < package_node->num_of_edges; j++) {
+ const edge_t *package_edge = package_node->edge[j];
+
+ if (package_edge->type == EDGE_CONFLICTS) {
+ const unsigned package_num =
+ search_package_hashtable(package_edge->name,
+ package_edge->version,
+ package_edge->operator);
+ int result = 0;
+ if (package_hashtable[package_num] != NULL) {
+ status_num = search_status_hashtable(name_hashtable[package_hashtable[package_num]->name]);
+
+ if (get_status(status_num, 1) == search_name_hashtable("install")) {
+ result = test_version(package_hashtable[deb_file[i]->package]->version,
+ package_edge->version, package_edge->operator);
+ }
+ }
+
+ if (result) {
+ bb_error_msg_and_die("package %s conflicts with %s",
+ name_hashtable[package_node->name],
+ name_hashtable[package_edge->name]);
+ }
+ }
+ }
+ i++;
+ }
+
+
+ /* Check dependendcies */
+ for (i = 0; i < PACKAGE_HASH_PRIME; i++) {
+ int status_num = 0;
+ int number_of_alternatives = 0;
+ const edge_t * root_of_alternatives = NULL;
+ const common_node_t *package_node = package_hashtable[i];
+
+ /* If the package node does not exist then this
+ * package is a virtual one. In which case there are
+ * no dependencies to check.
+ */
+ if (package_node == NULL) continue;
+
+ status_num = search_status_hashtable(name_hashtable[package_node->name]);
+
+ /* If there is no status then this package is a
+ * virtual one provided by something else. In which
+ * case there are no dependencies to check.
+ */
+ if (status_hashtable[status_num] == NULL) continue;
+
+ /* If we don't want this package installed then we may
+ * as well ignore it's dependencies.
+ */
+ if (get_status(status_num, 1) != search_name_hashtable("install")) {
+ continue;
+ }
+
+ /* This code is tested only for EDGE_DEPENDS, since I
+ * have no suitable pre-depends available. There is no
+ * reason that it shouldn't work though :-)
+ */
+ for (j = 0; j < package_node->num_of_edges; j++) {
+ const edge_t *package_edge = package_node->edge[j];
+ unsigned package_num;
+
+ if (package_edge->type == EDGE_OR_PRE_DEPENDS ||
+ package_edge->type == EDGE_OR_DEPENDS) { /* start an EDGE_OR_ list */
+ number_of_alternatives = package_edge->version;
+ root_of_alternatives = package_edge;
+ continue;
+ } else if (number_of_alternatives == 0) { /* not in the middle of an EDGE_OR_ list */
+ number_of_alternatives = 1;
+ root_of_alternatives = NULL;
+ }
+
+ package_num = search_package_hashtable(package_edge->name, package_edge->version, package_edge->operator);
+
+ if (package_edge->type == EDGE_PRE_DEPENDS ||
+ package_edge->type == EDGE_DEPENDS) {
+ int result=1;
+ status_num = 0;
+
+ /* If we are inside an alternative then check
+ * this edge is the right type.
+ *
+ * EDGE_DEPENDS == OR_DEPENDS -1
+ * EDGE_PRE_DEPENDS == OR_PRE_DEPENDS -1
+ */
+ if (root_of_alternatives && package_edge->type != root_of_alternatives->type - 1)
+ bb_error_msg_and_die("fatal error, package dependencies corrupt: %d != %d - 1",
+ package_edge->type, root_of_alternatives->type);
+
+ if (package_hashtable[package_num] != NULL)
+ result = !package_satisfies_dependency(package_num, package_edge->type);
+
+ if (result) { /* check for other package which provide what we are looking for */
+ int provider = -1;
+
+ while ((provider = search_for_provides(package_edge->name, provider)) > -1) {
+ if (package_hashtable[provider] == NULL) {
+ puts("Have a provider but no package information for it");
+ continue;
+ }
+ result = !package_satisfies_dependency(provider, package_edge->type);
+
+ if (result == 0)
+ break;
+ }
+ }
+
+ /* It must be already installed, or to be installed */
+ number_of_alternatives--;
+ if (result && number_of_alternatives == 0) {
+ if (root_of_alternatives)
+ bb_error_msg_and_die(
+ "package %s %sdepends on %s, "
+ "which cannot be satisfied",
+ name_hashtable[package_node->name],
+ package_edge->type == EDGE_PRE_DEPENDS ? "pre-" : "",
+ name_hashtable[root_of_alternatives->name]);
+ else
+ bb_error_msg_and_die(
+ "package %s %sdepends on %s, which %s\n",
+ name_hashtable[package_node->name],
+ package_edge->type == EDGE_PRE_DEPENDS ? "pre-" : "",
+ name_hashtable[package_edge->name],
+ describe_status(status_num));
+ } else if (result == 0 && number_of_alternatives) {
+ /* we've found a package which
+ * satisfies the dependency,
+ * so skip over the rest of
+ * the alternatives.
+ */
+ j += number_of_alternatives;
+ number_of_alternatives = 0;
+ }
+ }
+ }
+ }
+ free(conflicts);
+ return TRUE;
+}
+
+static char **create_list(const char *filename)
+{
+ FILE *list_stream;
+ char **file_list = NULL;
+ char *line = NULL;
+ int count = 0;
+
+ /* don't use [xw]fopen here, handle error ourself */
+ list_stream = fopen(filename, "r");
+ if (list_stream == NULL) {
+ return NULL;
+ }
+
+ while ((line = xmalloc_getline(list_stream)) != NULL) {
+ file_list = xrealloc(file_list, sizeof(char *) * (count + 2));
+ file_list[count] = line;
+ count++;
+ }
+ fclose(list_stream);
+
+ if (count == 0) {
+ return NULL;
+ } else {
+ file_list[count] = NULL;
+ return file_list;
+ }
+}
+
+/* maybe i should try and hook this into remove_file.c somehow */
+static int remove_file_array(char **remove_names, char **exclude_names)
+{
+ struct stat path_stat;
+ int match_flag;
+ int remove_flag = FALSE;
+ int i,j;
+
+ if (remove_names == NULL) {
+ return FALSE;
+ }
+ for (i = 0; remove_names[i] != NULL; i++) {
+ match_flag = FALSE;
+ if (exclude_names != NULL) {
+ for (j = 0; exclude_names[j] != 0; j++) {
+ if (strcmp(remove_names[i], exclude_names[j]) == 0) {
+ match_flag = TRUE;
+ break;
+ }
+ }
+ }
+ if (!match_flag) {
+ if (lstat(remove_names[i], &path_stat) < 0) {
+ continue;
+ }
+ if (S_ISDIR(path_stat.st_mode)) {
+ if (rmdir(remove_names[i]) != -1) {
+ remove_flag = TRUE;
+ }
+ } else {
+ if (unlink(remove_names[i]) != -1) {
+ remove_flag = TRUE;
+ }
+ }
+ }
+ }
+ return remove_flag;
+}
+
+static int run_package_script(const char *package_name, const char *script_type)
+{
+ struct stat path_stat;
+ char *script_path;
+ int result;
+
+ script_path = xasprintf("/var/lib/dpkg/info/%s.%s", package_name, script_type);
+
+ /* If the file doesnt exist is isnt a fatal */
+ result = lstat(script_path, &path_stat) < 0 ? EXIT_SUCCESS : system(script_path);
+ free(script_path);
+ return result;
+}
+
+static const char *all_control_files[] = {"preinst", "postinst", "prerm", "postrm",
+ "list", "md5sums", "shlibs", "conffiles", "config", "templates", NULL };
+
+static char **all_control_list(const char *package_name)
+{
+ unsigned i = 0;
+ char **remove_files;
+
+ /* Create a list of all /var/lib/dpkg/info/<package> files */
+ remove_files = xzalloc(sizeof(all_control_files));
+ while (all_control_files[i]) {
+ remove_files[i] = xasprintf("/var/lib/dpkg/info/%s.%s", package_name, all_control_files[i]);
+ i++;
+ }
+
+ return remove_files;
+}
+
+static void free_array(char **array)
+{
+
+ if (array) {
+ unsigned i = 0;
+ while (array[i]) {
+ free(array[i]);
+ i++;
+ }
+ free(array);
+ }
+}
+
+/* This function lists information on the installed packages. It loops through
+ * the status_hashtable to retrieve the info. This results in smaller code than
+ * scanning the status file. The resulting list, however, is unsorted.
+ */
+static void list_packages(void)
+{
+ int i;
+
+ puts(" Name Version");
+ puts("+++-==============-==============");
+
+ /* go through status hash, dereference package hash and finally strings */
+ for (i=0; i<STATUS_HASH_PRIME+1; i++) {
+
+ if (status_hashtable[i]) {
+ const char *stat_str; /* status string */
+ const char *name_str; /* package name */
+ const char *vers_str; /* version */
+ char s1, s2; /* status abbreviations */
+ int spccnt; /* space count */
+ int j;
+
+ stat_str = name_hashtable[status_hashtable[i]->status];
+ name_str = name_hashtable[package_hashtable[status_hashtable[i]->package]->name];
+ vers_str = name_hashtable[package_hashtable[status_hashtable[i]->package]->version];
+
+ /* get abbreviation for status field 1 */
+ s1 = stat_str[0] == 'i' ? 'i' : 'r';
+
+ /* get abbreviation for status field 2 */
+ for (j=0, spccnt=0; stat_str[j] && spccnt<2; j++) {
+ if (stat_str[j] == ' ') spccnt++;
+ }
+ s2 = stat_str[j];
+
+ /* print out the line formatted like Debian dpkg */
+ printf("%c%c %-14s %s\n", s1, s2, name_str, vers_str);
+ }
+ }
+}
+
+static void remove_package(const unsigned package_num, int noisy)
+{
+ const char *package_name = name_hashtable[package_hashtable[package_num]->name];
+ const char *package_version = name_hashtable[package_hashtable[package_num]->version];
+ const unsigned status_num = search_status_hashtable(package_name);
+ const int package_name_length = strlen(package_name);
+ char **remove_files;
+ char **exclude_files;
+ char list_name[package_name_length + 25];
+ char conffile_name[package_name_length + 30];
+ int return_value;
+
+ if (noisy)
+ printf("Removing %s (%s)...\n", package_name, package_version);
+
+ /* run prerm script */
+ return_value = run_package_script(package_name, "prerm");
+ if (return_value == -1) {
+ bb_error_msg_and_die("script failed, prerm failure");
+ }
+
+ /* Create a list of files to remove, and a separate list of those to keep */
+ sprintf(list_name, "/var/lib/dpkg/info/%s.list", package_name);
+ remove_files = create_list(list_name);
+
+ sprintf(conffile_name, "/var/lib/dpkg/info/%s.conffiles", package_name);
+ exclude_files = create_list(conffile_name);
+
+ /* Some directories can't be removed straight away, so do multiple passes */
+ while (remove_file_array(remove_files, exclude_files)) /*repeat */;
+ free_array(exclude_files);
+ free_array(remove_files);
+
+ /* Create a list of files in /var/lib/dpkg/info/<package>.* to keep */
+ exclude_files = xzalloc(sizeof(char*) * 3);
+ exclude_files[0] = xstrdup(conffile_name);
+ exclude_files[1] = xasprintf("/var/lib/dpkg/info/%s.postrm", package_name);
+
+ /* Create a list of all /var/lib/dpkg/info/<package> files */
+ remove_files = all_control_list(package_name);
+
+ remove_file_array(remove_files, exclude_files);
+ free_array(remove_files);
+ free_array(exclude_files);
+
+ /* rename <package>.conffile to <package>.list */
+ rename(conffile_name, list_name);
+
+ /* Change package status */
+ set_status(status_num, "config-files", 3);
+}
+
+static void purge_package(const unsigned package_num)
+{
+ const char *package_name = name_hashtable[package_hashtable[package_num]->name];
+ const char *package_version = name_hashtable[package_hashtable[package_num]->version];
+ const unsigned status_num = search_status_hashtable(package_name);
+ char **remove_files;
+ char **exclude_files;
+ char list_name[strlen(package_name) + 25];
+
+ printf("Purging %s (%s)...\n", package_name, package_version);
+
+ /* run prerm script */
+ if (run_package_script(package_name, "prerm") != 0) {
+ bb_error_msg_and_die("script failed, prerm failure");
+ }
+
+ /* Create a list of files to remove */
+ sprintf(list_name, "/var/lib/dpkg/info/%s.list", package_name);
+ remove_files = create_list(list_name);
+
+ exclude_files = xzalloc(sizeof(char*));
+
+ /* Some directories cant be removed straight away, so do multiple passes */
+ while (remove_file_array(remove_files, exclude_files)) /* repeat */;
+ free_array(remove_files);
+
+ /* Create a list of all /var/lib/dpkg/info/<package> files */
+ remove_files = all_control_list(package_name);
+ remove_file_array(remove_files, exclude_files);
+ free_array(remove_files);
+ free(exclude_files);
+
+ /* run postrm script */
+ if (run_package_script(package_name, "postrm") == -1) {
+ bb_error_msg_and_die("postrm fialure.. set status to what?");
+ }
+
+ /* Change package status */
+ set_status(status_num, "not-installed", 3);
+}
+
+static archive_handle_t *init_archive_deb_ar(const char *filename)
+{
+ archive_handle_t *ar_handle;
+
+ /* Setup an ar archive handle that refers to the gzip sub archive */
+ ar_handle = init_handle();
+ ar_handle->filter = filter_accept_list_reassign;
+ ar_handle->src_fd = xopen(filename, O_RDONLY);
+
+ return ar_handle;
+}
+
+static void init_archive_deb_control(archive_handle_t *ar_handle)
+{
+ archive_handle_t *tar_handle;
+
+ /* Setup the tar archive handle */
+ tar_handle = init_handle();
+ tar_handle->src_fd = ar_handle->src_fd;
+
+ /* We don't care about data.tar.* or debian-binary, just control.tar.* */
+#ifdef CONFIG_FEATURE_DEB_TAR_GZ
+ llist_add_to(&(ar_handle->accept), (char*)"control.tar.gz");
+#endif
+#ifdef CONFIG_FEATURE_DEB_TAR_BZ2
+ llist_add_to(&(ar_handle->accept), (char*)"control.tar.bz2");
+#endif
+
+ /* Assign the tar handle as a subarchive of the ar handle */
+ ar_handle->sub_archive = tar_handle;
+}
+
+static void init_archive_deb_data(archive_handle_t *ar_handle)
+{
+ archive_handle_t *tar_handle;
+
+ /* Setup the tar archive handle */
+ tar_handle = init_handle();
+ tar_handle->src_fd = ar_handle->src_fd;
+
+ /* We don't care about control.tar.* or debian-binary, just data.tar.* */
+#ifdef CONFIG_FEATURE_DEB_TAR_GZ
+ llist_add_to(&(ar_handle->accept), (char*)"data.tar.gz");
+#endif
+#ifdef CONFIG_FEATURE_DEB_TAR_BZ2
+ llist_add_to(&(ar_handle->accept), (char*)"data.tar.bz2");
+#endif
+
+ /* Assign the tar handle as a subarchive of the ar handle */
+ ar_handle->sub_archive = tar_handle;
+}
+
+static char *deb_extract_control_file_to_buffer(archive_handle_t *ar_handle, llist_t *myaccept)
+{
+ ar_handle->sub_archive->action_data = data_extract_to_buffer;
+ ar_handle->sub_archive->accept = myaccept;
+ ar_handle->sub_archive->filter = filter_accept_list;
+
+ unpack_ar_archive(ar_handle);
+ close(ar_handle->src_fd);
+
+ return ar_handle->sub_archive->buffer;
+}
+
+static void data_extract_all_prefix(archive_handle_t *archive_handle)
+{
+ char *name_ptr = archive_handle->file_header->name;
+
+ name_ptr += strspn(name_ptr, "./");
+ if (name_ptr[0] != '\0') {
+ archive_handle->file_header->name = xasprintf("%s%s", archive_handle->buffer, name_ptr);
+ data_extract_all(archive_handle);
+ }
+}
+
+static void unpack_package(deb_file_t *deb_file)
+{
+ const char *package_name = name_hashtable[package_hashtable[deb_file->package]->name];
+ const unsigned status_num = search_status_hashtable(package_name);
+ const unsigned status_package_num = status_hashtable[status_num]->package;
+ char *info_prefix;
+ char *list_filename;
+ archive_handle_t *archive_handle;
+ FILE *out_stream;
+ llist_t *accept_list = NULL;
+ int i = 0;
+
+ /* If existing version, remove it first */
+ if (strcmp(name_hashtable[get_status(status_num, 3)], "installed") == 0) {
+ /* Package is already installed, remove old version first */
+ printf("Preparing to replace %s %s (using %s)...\n", package_name,
+ name_hashtable[package_hashtable[status_package_num]->version],
+ deb_file->filename);
+ remove_package(status_package_num, 0);
+ } else {
+ printf("Unpacking %s (from %s)...\n", package_name, deb_file->filename);
+ }
+
+ /* Extract control.tar.gz to /var/lib/dpkg/info/<package>.filename */
+ info_prefix = xasprintf("/var/lib/dpkg/info/%s.", package_name);
+ archive_handle = init_archive_deb_ar(deb_file->filename);
+ init_archive_deb_control(archive_handle);
+
+ while (all_control_files[i]) {
+ char *c = xasprintf("./%s", all_control_files[i]);
+ llist_add_to(&accept_list, c);
+ i++;
+ }
+ archive_handle->sub_archive->accept = accept_list;
+ archive_handle->sub_archive->filter = filter_accept_list;
+ archive_handle->sub_archive->action_data = data_extract_all_prefix;
+ archive_handle->sub_archive->buffer = info_prefix;
+ archive_handle->sub_archive->flags |= ARCHIVE_EXTRACT_UNCONDITIONAL;
+ unpack_ar_archive(archive_handle);
+
+ /* Run the preinst prior to extracting */
+ if (run_package_script(package_name, "preinst") != 0) {
+ /* when preinst returns exit code != 0 then quit installation process */
+ bb_error_msg_and_die("subprocess pre-installation script returned error");
+ }
+
+ /* Extract data.tar.gz to the root directory */
+ archive_handle = init_archive_deb_ar(deb_file->filename);
+ init_archive_deb_data(archive_handle);
+ archive_handle->sub_archive->action_data = data_extract_all_prefix;
+ archive_handle->sub_archive->buffer = (char*)"/"; /* huh? */
+ archive_handle->sub_archive->flags |= ARCHIVE_EXTRACT_UNCONDITIONAL;
+ unpack_ar_archive(archive_handle);
+
+ /* Create the list file */
+ list_filename = xasprintf("/var/lib/dpkg/info/%s.list", package_name);
+ out_stream = xfopen(list_filename, "w");
+ while (archive_handle->sub_archive->passed) {
+ /* the leading . has been stripped by data_extract_all_prefix already */
+ fputs(archive_handle->sub_archive->passed->data, out_stream);
+ fputc('\n', out_stream);
+ archive_handle->sub_archive->passed = archive_handle->sub_archive->passed->link;
+ }
+ fclose(out_stream);
+
+ /* change status */
+ set_status(status_num, "install", 1);
+ set_status(status_num, "unpacked", 3);
+
+ free(info_prefix);
+ free(list_filename);
+}
+
+static void configure_package(deb_file_t *deb_file)
+{
+ const char *package_name = name_hashtable[package_hashtable[deb_file->package]->name];
+ const char *package_version = name_hashtable[package_hashtable[deb_file->package]->version];
+ const int status_num = search_status_hashtable(package_name);
+
+ printf("Setting up %s (%s)...\n", package_name, package_version);
+
+ /* Run the postinst script */
+ if (run_package_script(package_name, "postinst") != 0) {
+ /* TODO: handle failure gracefully */
+ bb_error_msg_and_die("postrm failure.. set status to what?");
+ }
+ /* Change status to reflect success */
+ set_status(status_num, "install", 1);
+ set_status(status_num, "installed", 3);
+}
+
+int dpkg_main(int argc, char **argv);
+int dpkg_main(int argc, char **argv)
+{
+ deb_file_t **deb_file = NULL;
+ status_node_t *status_node;
+ char *str_f;
+ int opt;
+ int package_num;
+ int deb_count = 0;
+ int state_status;
+ int status_num;
+ int i;
+ enum {
+ OPT_configure = 0x1,
+ OPT_force_ignore_depends = 0x2,
+ OPT_install = 0x4,
+ OPT_list_installed = 0x8,
+ OPT_purge = 0x10,
+ OPT_remove = 0x20,
+ OPT_unpack = 0x40,
+ };
+
+ opt = getopt32(argc, argv, "CF:ilPru", &str_f);
+ //if (opt & OPT_configure) ... // -C
+ if (opt & OPT_force_ignore_depends) { // -F (--force in official dpkg)
+ if (strcmp(str_f, "depends"))
+ opt &= ~OPT_force_ignore_depends;
+ }
+ //if (opt & OPT_install) ... // -i
+ //if (opt & OPT_list_installed) ... // -l
+ //if (opt & OPT_purge) ... // -P
+ //if (opt & OPT_remove) ... // -r
+ //if (opt & OPT_unpack) ... // -u (--unpack in official dpkg)
+ argc -= optind;
+ argv += optind;
+ /* check for non-option argument if expected */
+ if (!opt || (!argc && !(opt && OPT_list_installed)))
+ bb_show_usage();
+
+ name_hashtable = xzalloc(sizeof(name_hashtable[0]) * (NAME_HASH_PRIME + 1));
+ package_hashtable = xzalloc(sizeof(package_hashtable[0]) * (PACKAGE_HASH_PRIME + 1));
+ status_hashtable = xzalloc(sizeof(status_hashtable[0]) * (STATUS_HASH_PRIME + 1));
+
+/* puts("(Reading database ... xxxxx files and directories installed.)"); */
+ index_status_file("/var/lib/dpkg/status");
+
+ /* if the list action was given print the installed packages and exit */
+ if (opt & OPT_list_installed) {
+ list_packages();
+ return EXIT_SUCCESS;
+ }
+
+ /* Read arguments and store relevant info in structs */
+ while (*argv) {
+ /* deb_count = nb_elem - 1 and we need nb_elem + 1 to allocate terminal node [NULL pointer] */
+ deb_file = xrealloc(deb_file, sizeof(deb_file[0]) * (deb_count + 2));
+ deb_file[deb_count] = xzalloc(sizeof(deb_file[0][0]));
+ if (opt & (OPT_install | OPT_unpack)) {
+ /* -i/-u: require filename */
+ archive_handle_t *archive_handle;
+ llist_t *control_list = NULL;
+
+ /* Extract the control file */
+ llist_add_to(&control_list, (char*)"./control");
+ archive_handle = init_archive_deb_ar(argv[0]);
+ init_archive_deb_control(archive_handle);
+ deb_file[deb_count]->control_file = deb_extract_control_file_to_buffer(archive_handle, control_list);
+ if (deb_file[deb_count]->control_file == NULL) {
+ bb_error_msg_and_die("cannot extract control file");
+ }
+ deb_file[deb_count]->filename = xstrdup(argv[0]);
+ package_num = fill_package_struct(deb_file[deb_count]->control_file);
+
+ if (package_num == -1) {
+ bb_error_msg("invalid control file in %s", argv[0]);
+ argv++;
+ continue;
+ }
+ deb_file[deb_count]->package = (unsigned) package_num;
+
+ /* Add the package to the status hashtable */
+ if (opt & (OPT_unpack | OPT_install)) {
+ /* Try and find a currently installed version of this package */
+ status_num = search_status_hashtable(name_hashtable[package_hashtable[deb_file[deb_count]->package]->name]);
+ /* If no previous entry was found initialise a new entry */
+ if (status_hashtable[status_num] == NULL
+ || status_hashtable[status_num]->status == 0
+ ) {
+ status_node = xmalloc(sizeof(status_node_t));
+ status_node->package = deb_file[deb_count]->package;
+ /* reinstreq isnt changed to "ok" until the package control info
+ * is written to the status file*/
+ status_node->status = search_name_hashtable("install reinstreq not-installed");
+ status_hashtable[status_num] = status_node;
+ } else {
+ set_status(status_num, "install", 1);
+ set_status(status_num, "reinstreq", 2);
+ }
+ }
+ } else if (opt & (OPT_configure | OPT_purge | OPT_remove)) {
+ /* -C/-p/-r: require package name */
+ deb_file[deb_count]->package = search_package_hashtable(
+ search_name_hashtable(argv[0]),
+ search_name_hashtable("ANY"), VER_ANY);
+ if (package_hashtable[deb_file[deb_count]->package] == NULL) {
+ bb_error_msg_and_die("package %s is uninstalled or unknown", argv[0]);
+ }
+ package_num = deb_file[deb_count]->package;
+ status_num = search_status_hashtable(name_hashtable[package_hashtable[package_num]->name]);
+ state_status = get_status(status_num, 3);
+
+ /* check package status is "installed" */
+ if (opt & OPT_remove) {
+ if (strcmp(name_hashtable[state_status], "not-installed") == 0
+ || strcmp(name_hashtable[state_status], "config-files") == 0
+ ) {
+ bb_error_msg_and_die("%s is already removed", name_hashtable[package_hashtable[package_num]->name]);
+ }
+ set_status(status_num, "deinstall", 1);
+ } else if (opt & OPT_purge) {
+ /* if package status is "conf-files" then its ok */
+ if (strcmp(name_hashtable[state_status], "not-installed") == 0) {
+ bb_error_msg_and_die("%s is already purged", name_hashtable[package_hashtable[package_num]->name]);
+ }
+ set_status(status_num, "purge", 1);
+ }
+ }
+ deb_count++;
+ argv++;
+ }
+ if (!deb_count)
+ bb_error_msg_and_die("no package files specified");
+ deb_file[deb_count] = NULL;
+
+ /* Check that the deb file arguments are installable */
+ if (!(opt & OPT_force_ignore_depends)) {
+ if (!check_deps(deb_file, 0, deb_count)) {
+ bb_error_msg_and_die("dependency check failed");
+ }
+ }
+
+ /* TODO: install or remove packages in the correct dependency order */
+ for (i = 0; i < deb_count; i++) {
+ /* Remove or purge packages */
+ if (opt & OPT_remove) {
+ remove_package(deb_file[i]->package, 1);
+ }
+ else if (opt & OPT_purge) {
+ purge_package(deb_file[i]->package);
+ }
+ else if (opt & OPT_unpack) {
+ unpack_package(deb_file[i]);
+ }
+ else if (opt & OPT_install) {
+ unpack_package(deb_file[i]);
+ /* package is configured in second pass below */
+ }
+ else if (opt & OPT_configure) {
+ configure_package(deb_file[i]);
+ }
+ }
+ /* configure installed packages */
+ if (opt & OPT_install) {
+ for (i = 0; i < deb_count; i++)
+ configure_package(deb_file[i]);
+ }
+
+ write_status_file(deb_file);
+
+ if (ENABLE_FEATURE_CLEAN_UP) {
+ for (i = 0; i < deb_count; i++) {
+ free(deb_file[i]->control_file);
+ free(deb_file[i]->filename);
+ free(deb_file[i]);
+ }
+
+ free(deb_file);
+
+ for (i = 0; i < NAME_HASH_PRIME; i++) {
+ free(name_hashtable[i]);
+ }
+
+ for (i = 0; i < PACKAGE_HASH_PRIME; i++) {
+ free_package(package_hashtable[i]);
+ }
+
+ for (i = 0; i < STATUS_HASH_PRIME; i++) {
+ free(status_hashtable[i]);
+ }
+
+ free(status_hashtable);
+ free(package_hashtable);
+ free(name_hashtable);
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/i/pc104/initrd/conf/busybox/archival/dpkg_deb.c b/i/pc104/initrd/conf/busybox/archival/dpkg_deb.c
new file mode 100644
index 0000000..fde3c33
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/archival/dpkg_deb.c
@@ -0,0 +1,97 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * dpkg-deb packs, unpacks and provides information about Debian archives.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+#include "busybox.h"
+#include "unarchive.h"
+
+#define DPKG_DEB_OPT_CONTENTS 1
+#define DPKG_DEB_OPT_CONTROL 2
+#define DPKG_DEB_OPT_FIELD 4
+#define DPKG_DEB_OPT_EXTRACT 8
+#define DPKG_DEB_OPT_EXTRACT_VERBOSE 16
+
+int dpkg_deb_main(int argc, char **argv);
+int dpkg_deb_main(int argc, char **argv)
+{
+ archive_handle_t *ar_archive;
+ archive_handle_t *tar_archive;
+ llist_t *control_tar_llist = NULL;
+ unsigned opt;
+ const char *extract_dir = NULL;
+ short argcount = 1;
+
+ /* Setup the tar archive handle */
+ tar_archive = init_handle();
+
+ /* Setup an ar archive handle that refers to the gzip sub archive */
+ ar_archive = init_handle();
+ ar_archive->sub_archive = tar_archive;
+ ar_archive->filter = filter_accept_list_reassign;
+
+#ifdef CONFIG_FEATURE_DEB_TAR_GZ
+ llist_add_to(&(ar_archive->accept), (char*)"data.tar.gz");
+ llist_add_to(&control_tar_llist, (char*)"control.tar.gz");
+#endif
+
+#ifdef CONFIG_FEATURE_DEB_TAR_BZ2
+ llist_add_to(&(ar_archive->accept), (char*)"data.tar.bz2");
+ llist_add_to(&control_tar_llist, (char*)"control.tar.bz2");
+#endif
+
+ opt_complementary = "?c--efXx:e--cfXx:f--ceXx:X--cefx:x--cefX";
+ opt = getopt32(argc, argv, "cefXx");
+
+ if (opt & DPKG_DEB_OPT_CONTENTS) {
+ tar_archive->action_header = header_verbose_list;
+ }
+ if (opt & DPKG_DEB_OPT_CONTROL) {
+ ar_archive->accept = control_tar_llist;
+ tar_archive->action_data = data_extract_all;
+ if (optind + 1 == argc) {
+ extract_dir = "./DEBIAN";
+ } else {
+ argcount++;
+ }
+ }
+ if (opt & DPKG_DEB_OPT_FIELD) {
+ /* Print the entire control file
+ * it should accept a second argument which specifies a
+ * specific field to print */
+ ar_archive->accept = control_tar_llist;
+ llist_add_to(&(tar_archive->accept), (char*)"./control");
+ tar_archive->filter = filter_accept_list;
+ tar_archive->action_data = data_extract_to_stdout;
+ }
+ if (opt & DPKG_DEB_OPT_EXTRACT) {
+ tar_archive->action_header = header_list;
+ }
+ if (opt & (DPKG_DEB_OPT_EXTRACT_VERBOSE | DPKG_DEB_OPT_EXTRACT)) {
+ tar_archive->action_data = data_extract_all;
+ argcount = 2;
+ }
+
+ if ((optind + argcount) != argc) {
+ bb_show_usage();
+ }
+
+ tar_archive->src_fd = ar_archive->src_fd = xopen(argv[optind++], O_RDONLY);
+
+ /* Workout where to extract the files */
+ /* 2nd argument is a dir name */
+ if (argv[optind]) {
+ extract_dir = argv[optind];
+ }
+ if (extract_dir) {
+ mkdir(extract_dir, 0777); /* bb_make_directory(extract_dir, 0777, 0) */
+ xchdir(extract_dir);
+ }
+ unpack_ar_archive(ar_archive);
+
+ /* Cleanup */
+ close(ar_archive->src_fd);
+
+ return EXIT_SUCCESS;
+}
diff --git a/i/pc104/initrd/conf/busybox/archival/gzip.c b/i/pc104/initrd/conf/busybox/archival/gzip.c
new file mode 100644
index 0000000..39391df
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/archival/gzip.c
@@ -0,0 +1,2085 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Gzip implementation for busybox
+ *
+ * Based on GNU gzip Copyright (C) 1992-1993 Jean-loup Gailly.
+ *
+ * Originally adjusted for busybox by Charles P. Wright <cpw@unix.asb.com>
+ * "this is a stripped down version of gzip I put into busybox, it does
+ * only standard in to standard out with -9 compression. It also requires
+ * the zcat module for some important functions."
+ *
+ * Adjusted further by Erik Andersen <andersen@codepoet.org> to support
+ * files as well as stdin/stdout, and to generally behave itself wrt
+ * command line handling.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/* big objects in bss:
+ * 00000020 b bl_count
+ * 00000074 b base_length
+ * 00000078 b base_dist
+ * 00000078 b static_dtree
+ * 0000009c b bl_tree
+ * 000000f4 b dyn_dtree
+ * 00000100 b length_code
+ * 00000200 b dist_code
+ * 0000023d b depth
+ * 00000400 b flag_buf
+ * 0000047a b heap
+ * 00000480 b static_ltree
+ * 000008f4 b dyn_ltree
+ */
+
+/* TODO: full support for -v for DESKTOP
+ * "/usr/bin/gzip -v a bogus aa" should say:
+a: 85.1% -- replaced with a.gz
+gzip: bogus: No such file or directory
+aa: 85.1% -- replaced with aa.gz
+*/
+
+#include "busybox.h"
+
+
+/* ===========================================================================
+ */
+//#define DEBUG 1
+/* Diagnostic functions */
+#ifdef DEBUG
+# define Assert(cond,msg) {if(!(cond)) bb_error_msg(msg);}
+# define Trace(x) fprintf x
+# define Tracev(x) {if (verbose) fprintf x ;}
+# define Tracevv(x) {if (verbose > 1) fprintf x ;}
+# define Tracec(c,x) {if (verbose && (c)) fprintf x ;}
+# define Tracecv(c,x) {if (verbose > 1 && (c)) fprintf x ;}
+#else
+# define Assert(cond,msg)
+# define Trace(x)
+# define Tracev(x)
+# define Tracevv(x)
+# define Tracec(c,x)
+# define Tracecv(c,x)
+#endif
+
+
+/* ===========================================================================
+ */
+#define SMALL_MEM
+
+#ifndef INBUFSIZ
+# ifdef SMALL_MEM
+# define INBUFSIZ 0x2000 /* input buffer size */
+# else
+# define INBUFSIZ 0x8000 /* input buffer size */
+# endif
+#endif
+
+#ifndef OUTBUFSIZ
+# ifdef SMALL_MEM
+# define OUTBUFSIZ 8192 /* output buffer size */
+# else
+# define OUTBUFSIZ 16384 /* output buffer size */
+# endif
+#endif
+
+#ifndef DIST_BUFSIZE
+# ifdef SMALL_MEM
+# define DIST_BUFSIZE 0x2000 /* buffer for distances, see trees.c */
+# else
+# define DIST_BUFSIZE 0x8000 /* buffer for distances, see trees.c */
+# endif
+#endif
+
+/* gzip flag byte */
+#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */
+#define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */
+#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */
+#define ORIG_NAME 0x08 /* bit 3 set: original file name present */
+#define COMMENT 0x10 /* bit 4 set: file comment present */
+#define RESERVED 0xC0 /* bit 6,7: reserved */
+
+/* internal file attribute */
+#define UNKNOWN 0xffff
+#define BINARY 0
+#define ASCII 1
+
+#ifndef WSIZE
+# define WSIZE 0x8000 /* window size--must be a power of two, and */
+#endif /* at least 32K for zip's deflate method */
+
+#define MIN_MATCH 3
+#define MAX_MATCH 258
+/* The minimum and maximum match lengths */
+
+#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1)
+/* Minimum amount of lookahead, except at the end of the input file.
+ * See deflate.c for comments about the MIN_MATCH+1.
+ */
+
+#define MAX_DIST (WSIZE-MIN_LOOKAHEAD)
+/* In order to simplify the code, particularly on 16 bit machines, match
+ * distances are limited to MAX_DIST instead of WSIZE.
+ */
+
+#ifndef MAX_PATH_LEN
+# define MAX_PATH_LEN 1024 /* max pathname length */
+#endif
+
+#define seekable() 0 /* force sequential output */
+#define translate_eol 0 /* no option -a yet */
+
+#ifndef BITS
+# define BITS 16
+#endif
+#define INIT_BITS 9 /* Initial number of bits per code */
+
+#define BIT_MASK 0x1f /* Mask for 'number of compression bits' */
+/* Mask 0x20 is reserved to mean a fourth header byte, and 0x40 is free.
+ * It's a pity that old uncompress does not check bit 0x20. That makes
+ * extension of the format actually undesirable because old compress
+ * would just crash on the new format instead of giving a meaningful
+ * error message. It does check the number of bits, but it's more
+ * helpful to say "unsupported format, get a new version" than
+ * "can only handle 16 bits".
+ */
+
+#ifdef MAX_EXT_CHARS
+# define MAX_SUFFIX MAX_EXT_CHARS
+#else
+# define MAX_SUFFIX 30
+#endif
+
+
+/* ===========================================================================
+ * Compile with MEDIUM_MEM to reduce the memory requirements or
+ * with SMALL_MEM to use as little memory as possible. Use BIG_MEM if the
+ * entire input file can be held in memory (not possible on 16 bit systems).
+ * Warning: defining these symbols affects HASH_BITS (see below) and thus
+ * affects the compression ratio. The compressed output
+ * is still correct, and might even be smaller in some cases.
+ */
+
+#ifdef SMALL_MEM
+# define HASH_BITS 13 /* Number of bits used to hash strings */
+#endif
+#ifdef MEDIUM_MEM
+# define HASH_BITS 14
+#endif
+#ifndef HASH_BITS
+# define HASH_BITS 15
+ /* For portability to 16 bit machines, do not use values above 15. */
+#endif
+
+#define HASH_SIZE (unsigned)(1<<HASH_BITS)
+#define HASH_MASK (HASH_SIZE-1)
+#define WMASK (WSIZE-1)
+/* HASH_SIZE and WSIZE must be powers of two */
+#ifndef TOO_FAR
+# define TOO_FAR 4096
+#endif
+/* Matches of length 3 are discarded if their distance exceeds TOO_FAR */
+
+
+/* ===========================================================================
+ * These types are not really 'char', 'short' and 'long'
+ */
+typedef uint8_t uch;
+typedef uint16_t ush;
+typedef uint32_t ulg;
+typedef int32_t lng;
+
+typedef ush Pos;
+typedef unsigned IPos;
+/* A Pos is an index in the character window. We use short instead of int to
+ * save space in the various tables. IPos is used only for parameter passing.
+ */
+
+enum {
+ WINDOW_SIZE = 2 * WSIZE,
+/* window size, 2*WSIZE except for MMAP or BIG_MEM, where it is the
+ * input file length plus MIN_LOOKAHEAD.
+ */
+
+ max_chain_length = 4096,
+/* To speed up deflation, hash chains are never searched beyond this length.
+ * A higher limit improves compression ratio but degrades the speed.
+ */
+
+ max_lazy_match = 258,
+/* Attempt to find a better match only when the current match is strictly
+ * smaller than this value. This mechanism is used only for compression
+ * levels >= 4.
+ */
+
+ max_insert_length = max_lazy_match,
+/* Insert new strings in the hash table only if the match length
+ * is not greater than this length. This saves time but degrades compression.
+ * max_insert_length is used only for compression levels <= 3.
+ */
+
+ good_match = 32,
+/* Use a faster search when the previous match is longer than this */
+
+/* Values for max_lazy_match, good_match and max_chain_length, depending on
+ * the desired pack level (0..9). The values given below have been tuned to
+ * exclude worst case performance for pathological files. Better values may be
+ * found for specific files.
+ */
+
+ nice_match = 258, /* Stop searching when current match exceeds this */
+/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4
+ * For deflate_fast() (levels <= 3) good is ignored and lazy has a different
+ * meaning.
+ */
+};
+
+
+struct globals {
+
+ lng block_start;
+
+/* window position at the beginning of the current output block. Gets
+ * negative when the window is moved backwards.
+ */
+ unsigned ins_h; /* hash index of string to be inserted */
+
+#define H_SHIFT ((HASH_BITS+MIN_MATCH-1) / MIN_MATCH)
+/* Number of bits by which ins_h and del_h must be shifted at each
+ * input step. It must be such that after MIN_MATCH steps, the oldest
+ * byte no longer takes part in the hash key, that is:
+ * H_SHIFT * MIN_MATCH >= HASH_BITS
+ */
+
+ unsigned prev_length;
+
+/* Length of the best match at previous step. Matches not greater than this
+ * are discarded. This is used in the lazy match evaluation.
+ */
+
+ unsigned strstart; /* start of string to insert */
+ unsigned match_start; /* start of matching string */
+ unsigned lookahead; /* number of valid bytes ahead in window */
+
+/* ===========================================================================
+ */
+#define DECLARE(type, array, size) \
+ type * array
+#define ALLOC(type, array, size) \
+ array = xzalloc((size_t)(((size)+1L)/2) * 2*sizeof(type));
+#define FREE(array) \
+ do { free(array); array = NULL; } while (0)
+
+ /* global buffers */
+
+ /* buffer for literals or lengths */
+ /* DECLARE(uch, l_buf, LIT_BUFSIZE); */
+ DECLARE(uch, l_buf, INBUFSIZ);
+
+ DECLARE(ush, d_buf, DIST_BUFSIZE);
+ DECLARE(uch, outbuf, OUTBUFSIZ);
+
+/* Sliding window. Input bytes are read into the second half of the window,
+ * and move to the first half later to keep a dictionary of at least WSIZE
+ * bytes. With this organization, matches are limited to a distance of
+ * WSIZE-MAX_MATCH bytes, but this ensures that IO is always
+ * performed with a length multiple of the block size. Also, it limits
+ * the window size to 64K, which is quite useful on MSDOS.
+ * To do: limit the window size to WSIZE+BSZ if SMALL_MEM (the code would
+ * be less efficient).
+ */
+ DECLARE(uch, window, 2L * WSIZE);
+
+/* Link to older string with same hash index. To limit the size of this
+ * array to 64K, this link is maintained only for the last 32K strings.
+ * An index in this array is thus a window index modulo 32K.
+ */
+ /* DECLARE(Pos, prev, WSIZE); */
+ DECLARE(ush, prev, 1L << BITS);
+
+/* Heads of the hash chains or 0. */
+ /* DECLARE(Pos, head, 1<<HASH_BITS); */
+#define head (G1.prev + WSIZE) /* hash head (see deflate.c) */
+
+/* number of input bytes */
+ ulg isize; /* only 32 bits stored in .gz file */
+
+/* bbox always use stdin/stdout */
+#define ifd STDIN_FILENO /* input file descriptor */
+#define ofd STDOUT_FILENO /* output file descriptor */
+
+#ifdef DEBUG
+ unsigned insize; /* valid bytes in l_buf */
+#endif
+ unsigned outcnt; /* bytes in output buffer */
+
+ smallint eofile; /* flag set at end of input file */
+
+/* ===========================================================================
+ * Local data used by the "bit string" routines.
+ */
+
+ unsigned short bi_buf;
+
+/* Output buffer. bits are inserted starting at the bottom (least significant
+ * bits).
+ */
+
+#undef BUF_SIZE
+#define BUF_SIZE (8 * sizeof(G1.bi_buf))
+/* Number of bits used within bi_buf. (bi_buf might be implemented on
+ * more than 16 bits on some systems.)
+ */
+
+ int bi_valid;
+
+/* Current input function. Set to mem_read for in-memory compression */
+
+#ifdef DEBUG
+ ulg bits_sent; /* bit length of the compressed data */
+#endif
+
+ uint32_t *crc_32_tab;
+ uint32_t crc; /* shift register contents */
+};
+
+#define G1 (*(ptr_to_globals - 1))
+
+
+/* ===========================================================================
+ * Write the output buffer outbuf[0..outcnt-1] and update bytes_out.
+ * (used for the compressed data only)
+ */
+static void flush_outbuf(void)
+{
+ if (G1.outcnt == 0)
+ return;
+
+ xwrite(ofd, (char *) G1.outbuf, G1.outcnt);
+ G1.outcnt = 0;
+}
+
+
+/* ===========================================================================
+ */
+/* put_8bit is used for the compressed output */
+#define put_8bit(c) \
+do { \
+ G1.outbuf[G1.outcnt++] = (c); \
+ if (G1.outcnt == OUTBUFSIZ) flush_outbuf(); \
+} while (0)
+
+/* Output a 16 bit value, lsb first */
+static void put_16bit(ush w)
+{
+ if (G1.outcnt < OUTBUFSIZ - 2) {
+ G1.outbuf[G1.outcnt++] = w;
+ G1.outbuf[G1.outcnt++] = w >> 8;
+ } else {
+ put_8bit(w);
+ put_8bit(w >> 8);
+ }
+}
+
+static void put_32bit(ulg n)
+{
+ put_16bit(n);
+ put_16bit(n >> 16);
+}
+
+/* ===========================================================================
+ * Clear input and output buffers
+ */
+static void clear_bufs(void)
+{
+ G1.outcnt = 0;
+#ifdef DEBUG
+ G1.insize = 0;
+#endif
+ G1.isize = 0;
+}
+
+
+/* ===========================================================================
+ * Run a set of bytes through the crc shift register. If s is a NULL
+ * pointer, then initialize the crc shift register contents instead.
+ * Return the current crc in either case.
+ */
+static uint32_t updcrc(uch * s, unsigned n)
+{
+ uint32_t c = G1.crc;
+ while (n) {
+ c = G1.crc_32_tab[(uch)(c ^ *s++)] ^ (c >> 8);
+ n--;
+ }
+ G1.crc = c;
+ return c;
+}
+
+
+/* ===========================================================================
+ * Read a new buffer from the current input file, perform end-of-line
+ * translation, and update the crc and input file size.
+ * IN assertion: size >= 2 (for end-of-line translation)
+ */
+static unsigned file_read(void *buf, unsigned size)
+{
+ unsigned len;
+
+ Assert(G1.insize == 0, "l_buf not empty");
+
+ len = safe_read(ifd, buf, size);
+ if (len == (unsigned)(-1) || len == 0)
+ return len;
+
+ updcrc(buf, len);
+ G1.isize += len;
+ return len;
+}
+
+
+/* ===========================================================================
+ * Send a value on a given number of bits.
+ * IN assertion: length <= 16 and value fits in length bits.
+ */
+static void send_bits(int value, int length)
+{
+#ifdef DEBUG
+ Tracev((stderr, " l %2d v %4x ", length, value));
+ Assert(length > 0 && length <= 15, "invalid length");
+ G1.bits_sent += length;
+#endif
+ /* If not enough room in bi_buf, use (valid) bits from bi_buf and
+ * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid))
+ * unused bits in value.
+ */
+ if (G1.bi_valid > (int) BUF_SIZE - length) {
+ G1.bi_buf |= (value << G1.bi_valid);
+ put_16bit(G1.bi_buf);
+ G1.bi_buf = (ush) value >> (BUF_SIZE - G1.bi_valid);
+ G1.bi_valid += length - BUF_SIZE;
+ } else {
+ G1.bi_buf |= value << G1.bi_valid;
+ G1.bi_valid += length;
+ }
+}
+
+
+/* ===========================================================================
+ * Reverse the first len bits of a code, using straightforward code (a faster
+ * method would use a table)
+ * IN assertion: 1 <= len <= 15
+ */
+static unsigned bi_reverse(unsigned code, int len)
+{
+ unsigned res = 0;
+
+ while (1) {
+ res |= code & 1;
+ if (--len <= 0) return res;
+ code >>= 1;
+ res <<= 1;
+ }
+}
+
+
+/* ===========================================================================
+ * Write out any remaining bits in an incomplete byte.
+ */
+static void bi_windup(void)
+{
+ if (G1.bi_valid > 8) {
+ put_16bit(G1.bi_buf);
+ } else if (G1.bi_valid > 0) {
+ put_8bit(G1.bi_buf);
+ }
+ G1.bi_buf = 0;
+ G1.bi_valid = 0;
+#ifdef DEBUG
+ G1.bits_sent = (G1.bits_sent + 7) & ~7;
+#endif
+}
+
+
+/* ===========================================================================
+ * Copy a stored block to the zip file, storing first the length and its
+ * one's complement if requested.
+ */
+static void copy_block(char *buf, unsigned len, int header)
+{
+ bi_windup(); /* align on byte boundary */
+
+ if (header) {
+ put_16bit(len);
+ put_16bit(~len);
+#ifdef DEBUG
+ G1.bits_sent += 2 * 16;
+#endif
+ }
+#ifdef DEBUG
+ G1.bits_sent += (ulg) len << 3;
+#endif
+ while (len--) {
+ put_8bit(*buf++);
+ }
+}
+
+
+/* ===========================================================================
+ * Fill the window when the lookahead becomes insufficient.
+ * Updates strstart and lookahead, and sets eofile if end of input file.
+ * IN assertion: lookahead < MIN_LOOKAHEAD && strstart + lookahead > 0
+ * OUT assertions: at least one byte has been read, or eofile is set;
+ * file reads are performed for at least two bytes (required for the
+ * translate_eol option).
+ */
+static void fill_window(void)
+{
+ unsigned n, m;
+ unsigned more = WINDOW_SIZE - G1.lookahead - G1.strstart;
+ /* Amount of free space at the end of the window. */
+
+ /* If the window is almost full and there is insufficient lookahead,
+ * move the upper half to the lower one to make room in the upper half.
+ */
+ if (more == (unsigned) -1) {
+ /* Very unlikely, but possible on 16 bit machine if strstart == 0
+ * and lookahead == 1 (input done one byte at time)
+ */
+ more--;
+ } else if (G1.strstart >= WSIZE + MAX_DIST) {
+ /* By the IN assertion, the window is not empty so we can't confuse
+ * more == 0 with more == 64K on a 16 bit machine.
+ */
+ Assert(WINDOW_SIZE == 2 * WSIZE, "no sliding with BIG_MEM");
+
+ memcpy(G1.window, G1.window + WSIZE, WSIZE);
+ G1.match_start -= WSIZE;
+ G1.strstart -= WSIZE; /* we now have strstart >= MAX_DIST: */
+
+ G1.block_start -= WSIZE;
+
+ for (n = 0; n < HASH_SIZE; n++) {
+ m = head[n];
+ head[n] = (Pos) (m >= WSIZE ? m - WSIZE : 0);
+ }
+ for (n = 0; n < WSIZE; n++) {
+ m = G1.prev[n];
+ G1.prev[n] = (Pos) (m >= WSIZE ? m - WSIZE : 0);
+ /* If n is not on any hash chain, prev[n] is garbage but
+ * its value will never be used.
+ */
+ }
+ more += WSIZE;
+ }
+ /* At this point, more >= 2 */
+ if (!G1.eofile) {
+ n = file_read(G1.window + G1.strstart + G1.lookahead, more);
+ if (n == 0 || n == (unsigned) -1) {
+ G1.eofile = 1;
+ } else {
+ G1.lookahead += n;
+ }
+ }
+}
+
+
+/* ===========================================================================
+ * Set match_start to the longest match starting at the given string and
+ * return its length. Matches shorter or equal to prev_length are discarded,
+ * in which case the result is equal to prev_length and match_start is
+ * garbage.
+ * IN assertions: cur_match is the head of the hash chain for the current
+ * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1
+ */
+
+/* For MSDOS, OS/2 and 386 Unix, an optimized version is in match.asm or
+ * match.s. The code is functionally equivalent, so you can use the C version
+ * if desired.
+ */
+static int longest_match(IPos cur_match)
+{
+ unsigned chain_length = max_chain_length; /* max hash chain length */
+ uch *scan = G1.window + G1.strstart; /* current string */
+ uch *match; /* matched string */
+ int len; /* length of current match */
+ int best_len = G1.prev_length; /* best match length so far */
+ IPos limit = G1.strstart > (IPos) MAX_DIST ? G1.strstart - (IPos) MAX_DIST : 0;
+ /* Stop when cur_match becomes <= limit. To simplify the code,
+ * we prevent matches with the string of window index 0.
+ */
+
+/* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16.
+ * It is easy to get rid of this optimization if necessary.
+ */
+#if HASH_BITS < 8 || MAX_MATCH != 258
+# error Code too clever
+#endif
+ uch *strend = G1.window + G1.strstart + MAX_MATCH;
+ uch scan_end1 = scan[best_len - 1];
+ uch scan_end = scan[best_len];
+
+ /* Do not waste too much time if we already have a good match: */
+ if (G1.prev_length >= good_match) {
+ chain_length >>= 2;
+ }
+ Assert(G1.strstart <= WINDOW_SIZE - MIN_LOOKAHEAD, "insufficient lookahead");
+
+ do {
+ Assert(cur_match < G1.strstart, "no future");
+ match = G1.window + cur_match;
+
+ /* Skip to next match if the match length cannot increase
+ * or if the match length is less than 2:
+ */
+ if (match[best_len] != scan_end ||
+ match[best_len - 1] != scan_end1 ||
+ *match != *scan || *++match != scan[1])
+ continue;
+
+ /* The check at best_len-1 can be removed because it will be made
+ * again later. (This heuristic is not always a win.)
+ * It is not necessary to compare scan[2] and match[2] since they
+ * are always equal when the other bytes match, given that
+ * the hash keys are equal and that HASH_BITS >= 8.
+ */
+ scan += 2, match++;
+
+ /* We check for insufficient lookahead only every 8th comparison;
+ * the 256th check will be made at strstart+258.
+ */
+ do {
+ } while (*++scan == *++match && *++scan == *++match &&
+ *++scan == *++match && *++scan == *++match &&
+ *++scan == *++match && *++scan == *++match &&
+ *++scan == *++match && *++scan == *++match && scan < strend);
+
+ len = MAX_MATCH - (int) (strend - scan);
+ scan = strend - MAX_MATCH;
+
+ if (len > best_len) {
+ G1.match_start = cur_match;
+ best_len = len;
+ if (len >= nice_match)
+ break;
+ scan_end1 = scan[best_len - 1];
+ scan_end = scan[best_len];
+ }
+ } while ((cur_match = G1.prev[cur_match & WMASK]) > limit
+ && --chain_length != 0);
+
+ return best_len;
+}
+
+
+#ifdef DEBUG
+/* ===========================================================================
+ * Check that the match at match_start is indeed a match.
+ */
+static void check_match(IPos start, IPos match, int length)
+{
+ /* check that the match is indeed a match */
+ if (memcmp(G1.window + match, G1.window + start, length) != 0) {
+ bb_error_msg(" start %d, match %d, length %d", start, match, length);
+ bb_error_msg("invalid match");
+ }
+ if (verbose > 1) {
+ bb_error_msg("\\[%d,%d]", start - match, length);
+ do {
+ putc(G1.window[start++], stderr);
+ } while (--length != 0);
+ }
+}
+#else
+# define check_match(start, match, length) ((void)0)
+#endif
+
+
+/* trees.c -- output deflated data using Huffman coding
+ * Copyright (C) 1992-1993 Jean-loup Gailly
+ * This is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License, see the file COPYING.
+ */
+
+/* PURPOSE
+ * Encode various sets of source values using variable-length
+ * binary code trees.
+ *
+ * DISCUSSION
+ * The PKZIP "deflation" process uses several Huffman trees. The more
+ * common source values are represented by shorter bit sequences.
+ *
+ * Each code tree is stored in the ZIP file in a compressed form
+ * which is itself a Huffman encoding of the lengths of
+ * all the code strings (in ascending order by source values).
+ * The actual code strings are reconstructed from the lengths in
+ * the UNZIP process, as described in the "application note"
+ * (APPNOTE.TXT) distributed as part of PKWARE's PKZIP program.
+ *
+ * REFERENCES
+ * Lynch, Thomas J.
+ * Data Compression: Techniques and Applications, pp. 53-55.
+ * Lifetime Learning Publications, 1985. ISBN 0-534-03418-7.
+ *
+ * Storer, James A.
+ * Data Compression: Methods and Theory, pp. 49-50.
+ * Computer Science Press, 1988. ISBN 0-7167-8156-5.
+ *
+ * Sedgewick, R.
+ * Algorithms, p290.
+ * Addison-Wesley, 1983. ISBN 0-201-06672-6.
+ *
+ * INTERFACE
+ * void ct_init()
+ * Allocate the match buffer, initialize the various tables [and save
+ * the location of the internal file attribute (ascii/binary) and
+ * method (DEFLATE/STORE) -- deleted in bbox]
+ *
+ * void ct_tally(int dist, int lc);
+ * Save the match info and tally the frequency counts.
+ *
+ * ulg flush_block(char *buf, ulg stored_len, int eof)
+ * Determine the best encoding for the current block: dynamic trees,
+ * static trees or store, and output the encoded block to the zip
+ * file. Returns the total compressed length for the file so far.
+ */
+
+#define MAX_BITS 15
+/* All codes must not exceed MAX_BITS bits */
+
+#define MAX_BL_BITS 7
+/* Bit length codes must not exceed MAX_BL_BITS bits */
+
+#define LENGTH_CODES 29
+/* number of length codes, not counting the special END_BLOCK code */
+
+#define LITERALS 256
+/* number of literal bytes 0..255 */
+
+#define END_BLOCK 256
+/* end of block literal code */
+
+#define L_CODES (LITERALS+1+LENGTH_CODES)
+/* number of Literal or Length codes, including the END_BLOCK code */
+
+#define D_CODES 30
+/* number of distance codes */
+
+#define BL_CODES 19
+/* number of codes used to transfer the bit lengths */
+
+typedef uch extra_bits_t;
+
+/* extra bits for each length code */
+static const extra_bits_t extra_lbits[LENGTH_CODES]= {
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4,
+ 4, 4, 5, 5, 5, 5, 0
+};
+
+/* extra bits for each distance code */
+static const extra_bits_t extra_dbits[D_CODES] = {
+ 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9,
+ 10, 10, 11, 11, 12, 12, 13, 13
+};
+
+/* extra bits for each bit length code */
+static const extra_bits_t extra_blbits[BL_CODES] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 7 };
+
+/* number of codes at each bit length for an optimal tree */
+static const uch bl_order[BL_CODES] = {
+ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 };
+
+#define STORED_BLOCK 0
+#define STATIC_TREES 1
+#define DYN_TREES 2
+/* The three kinds of block type */
+
+#ifndef LIT_BUFSIZE
+# ifdef SMALL_MEM
+# define LIT_BUFSIZE 0x2000
+# else
+# ifdef MEDIUM_MEM
+# define LIT_BUFSIZE 0x4000
+# else
+# define LIT_BUFSIZE 0x8000
+# endif
+# endif
+#endif
+#ifndef DIST_BUFSIZE
+# define DIST_BUFSIZE LIT_BUFSIZE
+#endif
+/* Sizes of match buffers for literals/lengths and distances. There are
+ * 4 reasons for limiting LIT_BUFSIZE to 64K:
+ * - frequencies can be kept in 16 bit counters
+ * - if compression is not successful for the first block, all input data is
+ * still in the window so we can still emit a stored block even when input
+ * comes from standard input. (This can also be done for all blocks if
+ * LIT_BUFSIZE is not greater than 32K.)
+ * - if compression is not successful for a file smaller than 64K, we can
+ * even emit a stored file instead of a stored block (saving 5 bytes).
+ * - creating new Huffman trees less frequently may not provide fast
+ * adaptation to changes in the input data statistics. (Take for
+ * example a binary file with poorly compressible code followed by
+ * a highly compressible string table.) Smaller buffer sizes give
+ * fast adaptation but have of course the overhead of transmitting trees
+ * more frequently.
+ * - I can't count above 4
+ * The current code is general and allows DIST_BUFSIZE < LIT_BUFSIZE (to save
+ * memory at the expense of compression). Some optimizations would be possible
+ * if we rely on DIST_BUFSIZE == LIT_BUFSIZE.
+ */
+#define REP_3_6 16
+/* repeat previous bit length 3-6 times (2 bits of repeat count) */
+#define REPZ_3_10 17
+/* repeat a zero length 3-10 times (3 bits of repeat count) */
+#define REPZ_11_138 18
+/* repeat a zero length 11-138 times (7 bits of repeat count) */
+
+/* ===========================================================================
+*/
+/* Data structure describing a single value and its code string. */
+typedef struct ct_data {
+ union {
+ ush freq; /* frequency count */
+ ush code; /* bit string */
+ } fc;
+ union {
+ ush dad; /* father node in Huffman tree */
+ ush len; /* length of bit string */
+ } dl;
+} ct_data;
+
+#define Freq fc.freq
+#define Code fc.code
+#define Dad dl.dad
+#define Len dl.len
+
+#define HEAP_SIZE (2*L_CODES + 1)
+/* maximum heap size */
+
+typedef struct tree_desc {
+ ct_data *dyn_tree; /* the dynamic tree */
+ ct_data *static_tree; /* corresponding static tree or NULL */
+ const extra_bits_t *extra_bits; /* extra bits for each code or NULL */
+ int extra_base; /* base index for extra_bits */
+ int elems; /* max number of elements in the tree */
+ int max_length; /* max bit length for the codes */
+ int max_code; /* largest code with non zero frequency */
+} tree_desc;
+
+struct globals2 {
+
+ ush heap[HEAP_SIZE]; /* heap used to build the Huffman trees */
+ int heap_len; /* number of elements in the heap */
+ int heap_max; /* element of largest frequency */
+
+/* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used.
+ * The same heap array is used to build all trees.
+ */
+
+ ct_data dyn_ltree[HEAP_SIZE]; /* literal and length tree */
+ ct_data dyn_dtree[2 * D_CODES + 1]; /* distance tree */
+
+ ct_data static_ltree[L_CODES + 2];
+
+/* The static literal tree. Since the bit lengths are imposed, there is no
+ * need for the L_CODES extra codes used during heap construction. However
+ * The codes 286 and 287 are needed to build a canonical tree (see ct_init
+ * below).
+ */
+
+ ct_data static_dtree[D_CODES];
+
+/* The static distance tree. (Actually a trivial tree since all codes use
+ * 5 bits.)
+ */
+
+ ct_data bl_tree[2 * BL_CODES + 1];
+
+/* Huffman tree for the bit lengths */
+
+ tree_desc l_desc;
+ tree_desc d_desc;
+ tree_desc bl_desc;
+
+ ush bl_count[MAX_BITS + 1];
+
+/* The lengths of the bit length codes are sent in order of decreasing
+ * probability, to avoid transmitting the lengths for unused bit length codes.
+ */
+
+ uch depth[2 * L_CODES + 1];
+
+/* Depth of each subtree used as tie breaker for trees of equal frequency */
+
+ uch length_code[MAX_MATCH - MIN_MATCH + 1];
+
+/* length code for each normalized match length (0 == MIN_MATCH) */
+
+ uch dist_code[512];
+
+/* distance codes. The first 256 values correspond to the distances
+ * 3 .. 258, the last 256 values correspond to the top 8 bits of
+ * the 15 bit distances.
+ */
+
+ int base_length[LENGTH_CODES];
+
+/* First normalized length for each code (0 = MIN_MATCH) */
+
+ int base_dist[D_CODES];
+
+/* First normalized distance for each code (0 = distance of 1) */
+
+ uch flag_buf[LIT_BUFSIZE / 8];
+
+/* flag_buf is a bit array distinguishing literals from lengths in
+ * l_buf, thus indicating the presence or absence of a distance.
+ */
+
+ unsigned last_lit; /* running index in l_buf */
+ unsigned last_dist; /* running index in d_buf */
+ unsigned last_flags; /* running index in flag_buf */
+ uch flags; /* current flags not yet saved in flag_buf */
+ uch flag_bit; /* current bit used in flags */
+
+/* bits are filled in flags starting at bit 0 (least significant).
+ * Note: these flags are overkill in the current code since we don't
+ * take advantage of DIST_BUFSIZE == LIT_BUFSIZE.
+ */
+
+ ulg opt_len; /* bit length of current block with optimal trees */
+ ulg static_len; /* bit length of current block with static trees */
+
+ ulg compressed_len; /* total bit length of compressed file */
+};
+
+#define G2ptr ((struct globals2*)(ptr_to_globals))
+#define G2 (*G2ptr)
+
+
+/* ===========================================================================
+ */
+static void gen_codes(ct_data * tree, int max_code);
+static void build_tree(tree_desc * desc);
+static void scan_tree(ct_data * tree, int max_code);
+static void send_tree(ct_data * tree, int max_code);
+static int build_bl_tree(void);
+static void send_all_trees(int lcodes, int dcodes, int blcodes);
+static void compress_block(ct_data * ltree, ct_data * dtree);
+
+
+#ifndef DEBUG
+/* Send a code of the given tree. c and tree must not have side effects */
+# define SEND_CODE(c, tree) send_bits(tree[c].Code, tree[c].Len)
+#else
+# define SEND_CODE(c, tree) \
+{ \
+ if (verbose > 1) bb_error_msg("\ncd %3d ",(c)); \
+ send_bits(tree[c].Code, tree[c].Len); \
+}
+#endif
+
+#define D_CODE(dist) \
+ ((dist) < 256 ? G2.dist_code[dist] : G2.dist_code[256 + ((dist)>>7)])
+/* Mapping from a distance to a distance code. dist is the distance - 1 and
+ * must not have side effects. dist_code[256] and dist_code[257] are never
+ * used.
+ * The arguments must not have side effects.
+ */
+
+
+/* ===========================================================================
+ * Initialize a new block.
+ */
+static void init_block(void)
+{
+ int n; /* iterates over tree elements */
+
+ /* Initialize the trees. */
+ for (n = 0; n < L_CODES; n++)
+ G2.dyn_ltree[n].Freq = 0;
+ for (n = 0; n < D_CODES; n++)
+ G2.dyn_dtree[n].Freq = 0;
+ for (n = 0; n < BL_CODES; n++)
+ G2.bl_tree[n].Freq = 0;
+
+ G2.dyn_ltree[END_BLOCK].Freq = 1;
+ G2.opt_len = G2.static_len = 0;
+ G2.last_lit = G2.last_dist = G2.last_flags = 0;
+ G2.flags = 0;
+ G2.flag_bit = 1;
+}
+
+
+/* ===========================================================================
+ * Restore the heap property by moving down the tree starting at node k,
+ * exchanging a node with the smallest of its two sons if necessary, stopping
+ * when the heap property is re-established (each father smaller than its
+ * two sons).
+ */
+
+/* Compares to subtrees, using the tree depth as tie breaker when
+ * the subtrees have equal frequency. This minimizes the worst case length. */
+#define SMALLER(tree, n, m) \
+ (tree[n].Freq < tree[m].Freq \
+ || (tree[n].Freq == tree[m].Freq && G2.depth[n] <= G2.depth[m]))
+
+static void pqdownheap(ct_data * tree, int k)
+{
+ int v = G2.heap[k];
+ int j = k << 1; /* left son of k */
+
+ while (j <= G2.heap_len) {
+ /* Set j to the smallest of the two sons: */
+ if (j < G2.heap_len && SMALLER(tree, G2.heap[j + 1], G2.heap[j]))
+ j++;
+
+ /* Exit if v is smaller than both sons */
+ if (SMALLER(tree, v, G2.heap[j]))
+ break;
+
+ /* Exchange v with the smallest son */
+ G2.heap[k] = G2.heap[j];
+ k = j;
+
+ /* And continue down the tree, setting j to the left son of k */
+ j <<= 1;
+ }
+ G2.heap[k] = v;
+}
+
+
+/* ===========================================================================
+ * Compute the optimal bit lengths for a tree and update the total bit length
+ * for the current block.
+ * IN assertion: the fields freq and dad are set, heap[heap_max] and
+ * above are the tree nodes sorted by increasing frequency.
+ * OUT assertions: the field len is set to the optimal bit length, the
+ * array bl_count contains the frequencies for each bit length.
+ * The length opt_len is updated; static_len is also updated if stree is
+ * not null.
+ */
+static void gen_bitlen(tree_desc * desc)
+{
+ ct_data *tree = desc->dyn_tree;
+ const extra_bits_t *extra = desc->extra_bits;
+ int base = desc->extra_base;
+ int max_code = desc->max_code;
+ int max_length = desc->max_length;
+ ct_data *stree = desc->static_tree;
+ int h; /* heap index */
+ int n, m; /* iterate over the tree elements */
+ int bits; /* bit length */
+ int xbits; /* extra bits */
+ ush f; /* frequency */
+ int overflow = 0; /* number of elements with bit length too large */
+
+ for (bits = 0; bits <= MAX_BITS; bits++)
+ G2.bl_count[bits] = 0;
+
+ /* In a first pass, compute the optimal bit lengths (which may
+ * overflow in the case of the bit length tree).
+ */
+ tree[G2.heap[G2.heap_max]].Len = 0; /* root of the heap */
+
+ for (h = G2.heap_max + 1; h < HEAP_SIZE; h++) {
+ n = G2.heap[h];
+ bits = tree[tree[n].Dad].Len + 1;
+ if (bits > max_length) {
+ bits = max_length;
+ overflow++;
+ }
+ tree[n].Len = (ush) bits;
+ /* We overwrite tree[n].Dad which is no longer needed */
+
+ if (n > max_code)
+ continue; /* not a leaf node */
+
+ G2.bl_count[bits]++;
+ xbits = 0;
+ if (n >= base)
+ xbits = extra[n - base];
+ f = tree[n].Freq;
+ G2.opt_len += (ulg) f *(bits + xbits);
+
+ if (stree)
+ G2.static_len += (ulg) f * (stree[n].Len + xbits);
+ }
+ if (overflow == 0)
+ return;
+
+ Trace((stderr, "\nbit length overflow\n"));
+ /* This happens for example on obj2 and pic of the Calgary corpus */
+
+ /* Find the first bit length which could increase: */
+ do {
+ bits = max_length - 1;
+ while (G2.bl_count[bits] == 0)
+ bits--;
+ G2.bl_count[bits]--; /* move one leaf down the tree */
+ G2.bl_count[bits + 1] += 2; /* move one overflow item as its brother */
+ G2.bl_count[max_length]--;
+ /* The brother of the overflow item also moves one step up,
+ * but this does not affect bl_count[max_length]
+ */
+ overflow -= 2;
+ } while (overflow > 0);
+
+ /* Now recompute all bit lengths, scanning in increasing frequency.
+ * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all
+ * lengths instead of fixing only the wrong ones. This idea is taken
+ * from 'ar' written by Haruhiko Okumura.)
+ */
+ for (bits = max_length; bits != 0; bits--) {
+ n = G2.bl_count[bits];
+ while (n != 0) {
+ m = G2.heap[--h];
+ if (m > max_code)
+ continue;
+ if (tree[m].Len != (unsigned) bits) {
+ Trace((stderr, "code %d bits %d->%d\n", m, tree[m].Len, bits));
+ G2.opt_len += ((int32_t) bits - tree[m].Len) * tree[m].Freq;
+ tree[m].Len = bits;
+ }
+ n--;
+ }
+ }
+}
+
+
+/* ===========================================================================
+ * Generate the codes for a given tree and bit counts (which need not be
+ * optimal).
+ * IN assertion: the array bl_count contains the bit length statistics for
+ * the given tree and the field len is set for all tree elements.
+ * OUT assertion: the field code is set for all tree elements of non
+ * zero code length.
+ */
+static void gen_codes(ct_data * tree, int max_code)
+{
+ ush next_code[MAX_BITS + 1]; /* next code value for each bit length */
+ ush code = 0; /* running code value */
+ int bits; /* bit index */
+ int n; /* code index */
+
+ /* The distribution counts are first used to generate the code values
+ * without bit reversal.
+ */
+ for (bits = 1; bits <= MAX_BITS; bits++) {
+ next_code[bits] = code = (code + G2.bl_count[bits - 1]) << 1;
+ }
+ /* Check that the bit counts in bl_count are consistent. The last code
+ * must be all ones.
+ */
+ Assert(code + G2.bl_count[MAX_BITS] - 1 == (1 << MAX_BITS) - 1,
+ "inconsistent bit counts");
+ Tracev((stderr, "\ngen_codes: max_code %d ", max_code));
+
+ for (n = 0; n <= max_code; n++) {
+ int len = tree[n].Len;
+
+ if (len == 0)
+ continue;
+ /* Now reverse the bits */
+ tree[n].Code = bi_reverse(next_code[len]++, len);
+
+ Tracec(tree != G2.static_ltree,
+ (stderr, "\nn %3d %c l %2d c %4x (%x) ", n,
+ (isgraph(n) ? n : ' '), len, tree[n].Code,
+ next_code[len] - 1));
+ }
+}
+
+
+/* ===========================================================================
+ * Construct one Huffman tree and assigns the code bit strings and lengths.
+ * Update the total bit length for the current block.
+ * IN assertion: the field freq is set for all tree elements.
+ * OUT assertions: the fields len and code are set to the optimal bit length
+ * and corresponding code. The length opt_len is updated; static_len is
+ * also updated if stree is not null. The field max_code is set.
+ */
+
+/* Remove the smallest element from the heap and recreate the heap with
+ * one less element. Updates heap and heap_len. */
+
+#define SMALLEST 1
+/* Index within the heap array of least frequent node in the Huffman tree */
+
+#define PQREMOVE(tree, top) \
+do { \
+ top = G2.heap[SMALLEST]; \
+ G2.heap[SMALLEST] = G2.heap[G2.heap_len--]; \
+ pqdownheap(tree, SMALLEST); \
+} while (0)
+
+static void build_tree(tree_desc * desc)
+{
+ ct_data *tree = desc->dyn_tree;
+ ct_data *stree = desc->static_tree;
+ int elems = desc->elems;
+ int n, m; /* iterate over heap elements */
+ int max_code = -1; /* largest code with non zero frequency */
+ int node = elems; /* next internal node of the tree */
+
+ /* Construct the initial heap, with least frequent element in
+ * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1].
+ * heap[0] is not used.
+ */
+ G2.heap_len = 0;
+ G2.heap_max = HEAP_SIZE;
+
+ for (n = 0; n < elems; n++) {
+ if (tree[n].Freq != 0) {
+ G2.heap[++G2.heap_len] = max_code = n;
+ G2.depth[n] = 0;
+ } else {
+ tree[n].Len = 0;
+ }
+ }
+
+ /* The pkzip format requires that at least one distance code exists,
+ * and that at least one bit should be sent even if there is only one
+ * possible code. So to avoid special checks later on we force at least
+ * two codes of non zero frequency.
+ */
+ while (G2.heap_len < 2) {
+ int new = G2.heap[++G2.heap_len] = (max_code < 2 ? ++max_code : 0);
+
+ tree[new].Freq = 1;
+ G2.depth[new] = 0;
+ G2.opt_len--;
+ if (stree)
+ G2.static_len -= stree[new].Len;
+ /* new is 0 or 1 so it does not have extra bits */
+ }
+ desc->max_code = max_code;
+
+ /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree,
+ * establish sub-heaps of increasing lengths:
+ */
+ for (n = G2.heap_len / 2; n >= 1; n--)
+ pqdownheap(tree, n);
+
+ /* Construct the Huffman tree by repeatedly combining the least two
+ * frequent nodes.
+ */
+ do {
+ PQREMOVE(tree, n); /* n = node of least frequency */
+ m = G2.heap[SMALLEST]; /* m = node of next least frequency */
+
+ G2.heap[--G2.heap_max] = n; /* keep the nodes sorted by frequency */
+ G2.heap[--G2.heap_max] = m;
+
+ /* Create a new node father of n and m */
+ tree[node].Freq = tree[n].Freq + tree[m].Freq;
+ G2.depth[node] = MAX(G2.depth[n], G2.depth[m]) + 1;
+ tree[n].Dad = tree[m].Dad = (ush) node;
+#ifdef DUMP_BL_TREE
+ if (tree == G2.bl_tree) {
+ bb_error_msg("\nnode %d(%d), sons %d(%d) %d(%d)",
+ node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq);
+ }
+#endif
+ /* and insert the new node in the heap */
+ G2.heap[SMALLEST] = node++;
+ pqdownheap(tree, SMALLEST);
+
+ } while (G2.heap_len >= 2);
+
+ G2.heap[--G2.heap_max] = G2.heap[SMALLEST];
+
+ /* At this point, the fields freq and dad are set. We can now
+ * generate the bit lengths.
+ */
+ gen_bitlen((tree_desc *) desc);
+
+ /* The field len is now set, we can generate the bit codes */
+ gen_codes((ct_data *) tree, max_code);
+}
+
+
+/* ===========================================================================
+ * Scan a literal or distance tree to determine the frequencies of the codes
+ * in the bit length tree. Updates opt_len to take into account the repeat
+ * counts. (The contribution of the bit length codes will be added later
+ * during the construction of bl_tree.)
+ */
+static void scan_tree(ct_data * tree, int max_code)
+{
+ int n; /* iterates over all tree elements */
+ int prevlen = -1; /* last emitted length */
+ int curlen; /* length of current code */
+ int nextlen = tree[0].Len; /* length of next code */
+ int count = 0; /* repeat count of the current code */
+ int max_count = 7; /* max repeat count */
+ int min_count = 4; /* min repeat count */
+
+ if (nextlen == 0) {
+ max_count = 138;
+ min_count = 3;
+ }
+ tree[max_code + 1].Len = 0xffff; /* guard */
+
+ for (n = 0; n <= max_code; n++) {
+ curlen = nextlen;
+ nextlen = tree[n + 1].Len;
+ if (++count < max_count && curlen == nextlen)
+ continue;
+
+ if (count < min_count) {
+ G2.bl_tree[curlen].Freq += count;
+ } else if (curlen != 0) {
+ if (curlen != prevlen)
+ G2.bl_tree[curlen].Freq++;
+ G2.bl_tree[REP_3_6].Freq++;
+ } else if (count <= 10) {
+ G2.bl_tree[REPZ_3_10].Freq++;
+ } else {
+ G2.bl_tree[REPZ_11_138].Freq++;
+ }
+ count = 0;
+ prevlen = curlen;
+
+ max_count = 7;
+ min_count = 4;
+ if (nextlen == 0) {
+ max_count = 138;
+ min_count = 3;
+ } else if (curlen == nextlen) {
+ max_count = 6;
+ min_count = 3;
+ }
+ }
+}
+
+
+/* ===========================================================================
+ * Send a literal or distance tree in compressed form, using the codes in
+ * bl_tree.
+ */
+static void send_tree(ct_data * tree, int max_code)
+{
+ int n; /* iterates over all tree elements */
+ int prevlen = -1; /* last emitted length */
+ int curlen; /* length of current code */
+ int nextlen = tree[0].Len; /* length of next code */
+ int count = 0; /* repeat count of the current code */
+ int max_count = 7; /* max repeat count */
+ int min_count = 4; /* min repeat count */
+
+/* tree[max_code+1].Len = -1; *//* guard already set */
+ if (nextlen == 0)
+ max_count = 138, min_count = 3;
+
+ for (n = 0; n <= max_code; n++) {
+ curlen = nextlen;
+ nextlen = tree[n + 1].Len;
+ if (++count < max_count && curlen == nextlen) {
+ continue;
+ } else if (count < min_count) {
+ do {
+ SEND_CODE(curlen, G2.bl_tree);
+ } while (--count);
+ } else if (curlen != 0) {
+ if (curlen != prevlen) {
+ SEND_CODE(curlen, G2.bl_tree);
+ count--;
+ }
+ Assert(count >= 3 && count <= 6, " 3_6?");
+ SEND_CODE(REP_3_6, G2.bl_tree);
+ send_bits(count - 3, 2);
+ } else if (count <= 10) {
+ SEND_CODE(REPZ_3_10, G2.bl_tree);
+ send_bits(count - 3, 3);
+ } else {
+ SEND_CODE(REPZ_11_138, G2.bl_tree);
+ send_bits(count - 11, 7);
+ }
+ count = 0;
+ prevlen = curlen;
+ if (nextlen == 0) {
+ max_count = 138;
+ min_count = 3;
+ } else if (curlen == nextlen) {
+ max_count = 6;
+ min_count = 3;
+ } else {
+ max_count = 7;
+ min_count = 4;
+ }
+ }
+}
+
+
+/* ===========================================================================
+ * Construct the Huffman tree for the bit lengths and return the index in
+ * bl_order of the last bit length code to send.
+ */
+static int build_bl_tree(void)
+{
+ int max_blindex; /* index of last bit length code of non zero freq */
+
+ /* Determine the bit length frequencies for literal and distance trees */
+ scan_tree(G2.dyn_ltree, G2.l_desc.max_code);
+ scan_tree(G2.dyn_dtree, G2.d_desc.max_code);
+
+ /* Build the bit length tree: */
+ build_tree(&G2.bl_desc);
+ /* opt_len now includes the length of the tree representations, except
+ * the lengths of the bit lengths codes and the 5+5+4 bits for the counts.
+ */
+
+ /* Determine the number of bit length codes to send. The pkzip format
+ * requires that at least 4 bit length codes be sent. (appnote.txt says
+ * 3 but the actual value used is 4.)
+ */
+ for (max_blindex = BL_CODES - 1; max_blindex >= 3; max_blindex--) {
+ if (G2.bl_tree[bl_order[max_blindex]].Len != 0)
+ break;
+ }
+ /* Update opt_len to include the bit length tree and counts */
+ G2.opt_len += 3 * (max_blindex + 1) + 5 + 5 + 4;
+ Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld", G2.opt_len, G2.static_len));
+
+ return max_blindex;
+}
+
+
+/* ===========================================================================
+ * Send the header for a block using dynamic Huffman trees: the counts, the
+ * lengths of the bit length codes, the literal tree and the distance tree.
+ * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4.
+ */
+static void send_all_trees(int lcodes, int dcodes, int blcodes)
+{
+ int rank; /* index in bl_order */
+
+ Assert(lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes");
+ Assert(lcodes <= L_CODES && dcodes <= D_CODES
+ && blcodes <= BL_CODES, "too many codes");
+ Tracev((stderr, "\nbl counts: "));
+ send_bits(lcodes - 257, 5); /* not +255 as stated in appnote.txt */
+ send_bits(dcodes - 1, 5);
+ send_bits(blcodes - 4, 4); /* not -3 as stated in appnote.txt */
+ for (rank = 0; rank < blcodes; rank++) {
+ Tracev((stderr, "\nbl code %2d ", bl_order[rank]));
+ send_bits(G2.bl_tree[bl_order[rank]].Len, 3);
+ }
+ Tracev((stderr, "\nbl tree: sent %ld", G1.bits_sent));
+
+ send_tree((ct_data *) G2.dyn_ltree, lcodes - 1); /* send the literal tree */
+ Tracev((stderr, "\nlit tree: sent %ld", G1.bits_sent));
+
+ send_tree((ct_data *) G2.dyn_dtree, dcodes - 1); /* send the distance tree */
+ Tracev((stderr, "\ndist tree: sent %ld", G1.bits_sent));
+}
+
+
+/* ===========================================================================
+ * Save the match info and tally the frequency counts. Return true if
+ * the current block must be flushed.
+ */
+static int ct_tally(int dist, int lc)
+{
+ G1.l_buf[G2.last_lit++] = lc;
+ if (dist == 0) {
+ /* lc is the unmatched char */
+ G2.dyn_ltree[lc].Freq++;
+ } else {
+ /* Here, lc is the match length - MIN_MATCH */
+ dist--; /* dist = match distance - 1 */
+ Assert((ush) dist < (ush) MAX_DIST
+ && (ush) lc <= (ush) (MAX_MATCH - MIN_MATCH)
+ && (ush) D_CODE(dist) < (ush) D_CODES, "ct_tally: bad match"
+ );
+
+ G2.dyn_ltree[G2.length_code[lc] + LITERALS + 1].Freq++;
+ G2.dyn_dtree[D_CODE(dist)].Freq++;
+
+ G1.d_buf[G2.last_dist++] = dist;
+ G2.flags |= G2.flag_bit;
+ }
+ G2.flag_bit <<= 1;
+
+ /* Output the flags if they fill a byte: */
+ if ((G2.last_lit & 7) == 0) {
+ G2.flag_buf[G2.last_flags++] = G2.flags;
+ G2.flags = 0;
+ G2.flag_bit = 1;
+ }
+ /* Try to guess if it is profitable to stop the current block here */
+ if ((G2.last_lit & 0xfff) == 0) {
+ /* Compute an upper bound for the compressed length */
+ ulg out_length = G2.last_lit * 8L;
+ ulg in_length = (ulg) G1.strstart - G1.block_start;
+ int dcode;
+
+ for (dcode = 0; dcode < D_CODES; dcode++) {
+ out_length += G2.dyn_dtree[dcode].Freq * (5L + extra_dbits[dcode]);
+ }
+ out_length >>= 3;
+ Trace((stderr,
+ "\nlast_lit %u, last_dist %u, in %ld, out ~%ld(%ld%%) ",
+ G2.last_lit, G2.last_dist, in_length, out_length,
+ 100L - out_length * 100L / in_length));
+ if (G2.last_dist < G2.last_lit / 2 && out_length < in_length / 2)
+ return 1;
+ }
+ return (G2.last_lit == LIT_BUFSIZE - 1 || G2.last_dist == DIST_BUFSIZE);
+ /* We avoid equality with LIT_BUFSIZE because of wraparound at 64K
+ * on 16 bit machines and because stored blocks are restricted to
+ * 64K-1 bytes.
+ */
+}
+
+/* ===========================================================================
+ * Send the block data compressed using the given Huffman trees
+ */
+static void compress_block(ct_data * ltree, ct_data * dtree)
+{
+ unsigned dist; /* distance of matched string */
+ int lc; /* match length or unmatched char (if dist == 0) */
+ unsigned lx = 0; /* running index in l_buf */
+ unsigned dx = 0; /* running index in d_buf */
+ unsigned fx = 0; /* running index in flag_buf */
+ uch flag = 0; /* current flags */
+ unsigned code; /* the code to send */
+ int extra; /* number of extra bits to send */
+
+ if (G2.last_lit != 0) do {
+ if ((lx & 7) == 0)
+ flag = G2.flag_buf[fx++];
+ lc = G1.l_buf[lx++];
+ if ((flag & 1) == 0) {
+ SEND_CODE(lc, ltree); /* send a literal byte */
+ Tracecv(isgraph(lc), (stderr, " '%c' ", lc));
+ } else {
+ /* Here, lc is the match length - MIN_MATCH */
+ code = G2.length_code[lc];
+ SEND_CODE(code + LITERALS + 1, ltree); /* send the length code */
+ extra = extra_lbits[code];
+ if (extra != 0) {
+ lc -= G2.base_length[code];
+ send_bits(lc, extra); /* send the extra length bits */
+ }
+ dist = G1.d_buf[dx++];
+ /* Here, dist is the match distance - 1 */
+ code = D_CODE(dist);
+ Assert(code < D_CODES, "bad d_code");
+
+ SEND_CODE(code, dtree); /* send the distance code */
+ extra = extra_dbits[code];
+ if (extra != 0) {
+ dist -= G2.base_dist[code];
+ send_bits(dist, extra); /* send the extra distance bits */
+ }
+ } /* literal or match pair ? */
+ flag >>= 1;
+ } while (lx < G2.last_lit);
+
+ SEND_CODE(END_BLOCK, ltree);
+}
+
+
+/* ===========================================================================
+ * Determine the best encoding for the current block: dynamic trees, static
+ * trees or store, and output the encoded block to the zip file. This function
+ * returns the total compressed length for the file so far.
+ */
+static ulg flush_block(char *buf, ulg stored_len, int eof)
+{
+ ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */
+ int max_blindex; /* index of last bit length code of non zero freq */
+
+ G2.flag_buf[G2.last_flags] = G2.flags; /* Save the flags for the last 8 items */
+
+ /* Construct the literal and distance trees */
+ build_tree(&G2.l_desc);
+ Tracev((stderr, "\nlit data: dyn %ld, stat %ld", G2.opt_len, G2.static_len));
+
+ build_tree(&G2.d_desc);
+ Tracev((stderr, "\ndist data: dyn %ld, stat %ld", G2.opt_len, G2.static_len));
+ /* At this point, opt_len and static_len are the total bit lengths of
+ * the compressed block data, excluding the tree representations.
+ */
+
+ /* Build the bit length tree for the above two trees, and get the index
+ * in bl_order of the last bit length code to send.
+ */
+ max_blindex = build_bl_tree();
+
+ /* Determine the best encoding. Compute first the block length in bytes */
+ opt_lenb = (G2.opt_len + 3 + 7) >> 3;
+ static_lenb = (G2.static_len + 3 + 7) >> 3;
+
+ Trace((stderr,
+ "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u dist %u ",
+ opt_lenb, G2.opt_len, static_lenb, G2.static_len, stored_len,
+ G2.last_lit, G2.last_dist));
+
+ if (static_lenb <= opt_lenb)
+ opt_lenb = static_lenb;
+
+ /* If compression failed and this is the first and last block,
+ * and if the zip file can be seeked (to rewrite the local header),
+ * the whole file is transformed into a stored file:
+ */
+ if (stored_len <= opt_lenb && eof && G2.compressed_len == 0L && seekable()) {
+ /* Since LIT_BUFSIZE <= 2*WSIZE, the input data must be there: */
+ if (buf == NULL)
+ bb_error_msg("block vanished");
+
+ copy_block(buf, (unsigned) stored_len, 0); /* without header */
+ G2.compressed_len = stored_len << 3;
+
+ } else if (stored_len + 4 <= opt_lenb && buf != NULL) {
+ /* 4: two words for the lengths */
+ /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE.
+ * Otherwise we can't have processed more than WSIZE input bytes since
+ * the last block flush, because compression would have been
+ * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to
+ * transform a block into a stored block.
+ */
+ send_bits((STORED_BLOCK << 1) + eof, 3); /* send block type */
+ G2.compressed_len = (G2.compressed_len + 3 + 7) & ~7L;
+ G2.compressed_len += (stored_len + 4) << 3;
+
+ copy_block(buf, (unsigned) stored_len, 1); /* with header */
+
+ } else if (static_lenb == opt_lenb) {
+ send_bits((STATIC_TREES << 1) + eof, 3);
+ compress_block((ct_data *) G2.static_ltree, (ct_data *) G2.static_dtree);
+ G2.compressed_len += 3 + G2.static_len;
+ } else {
+ send_bits((DYN_TREES << 1) + eof, 3);
+ send_all_trees(G2.l_desc.max_code + 1, G2.d_desc.max_code + 1,
+ max_blindex + 1);
+ compress_block((ct_data *) G2.dyn_ltree, (ct_data *) G2.dyn_dtree);
+ G2.compressed_len += 3 + G2.opt_len;
+ }
+ Assert(G2.compressed_len == G1.bits_sent, "bad compressed size");
+ init_block();
+
+ if (eof) {
+ bi_windup();
+ G2.compressed_len += 7; /* align on byte boundary */
+ }
+ Tracev((stderr, "\ncomprlen %lu(%lu) ", G2.compressed_len >> 3,
+ G2.compressed_len - 7 * eof));
+
+ return G2.compressed_len >> 3;
+}
+
+
+/* ===========================================================================
+ * Update a hash value with the given input byte
+ * IN assertion: all calls to to UPDATE_HASH are made with consecutive
+ * input characters, so that a running hash key can be computed from the
+ * previous key instead of complete recalculation each time.
+ */
+#define UPDATE_HASH(h, c) (h = (((h)<<H_SHIFT) ^ (c)) & HASH_MASK)
+
+
+/* ===========================================================================
+ * Same as above, but achieves better compression. We use a lazy
+ * evaluation for matches: a match is finally adopted only if there is
+ * no better match at the next window position.
+ *
+ * Processes a new input file and return its compressed length. Sets
+ * the compressed length, crc, deflate flags and internal file
+ * attributes.
+ */
+
+/* Flush the current block, with given end-of-file flag.
+ * IN assertion: strstart is set to the end of the current match. */
+#define FLUSH_BLOCK(eof) \
+ flush_block( \
+ G1.block_start >= 0L \
+ ? (char*)&G1.window[(unsigned)G1.block_start] \
+ : (char*)NULL, \
+ (ulg)G1.strstart - G1.block_start, \
+ (eof) \
+ )
+
+/* Insert string s in the dictionary and set match_head to the previous head
+ * of the hash chain (the most recent string with same hash key). Return
+ * the previous length of the hash chain.
+ * IN assertion: all calls to to INSERT_STRING are made with consecutive
+ * input characters and the first MIN_MATCH bytes of s are valid
+ * (except for the last MIN_MATCH-1 bytes of the input file). */
+#define INSERT_STRING(s, match_head) \
+do { \
+ UPDATE_HASH(G1.ins_h, G1.window[(s) + MIN_MATCH-1]); \
+ G1.prev[(s) & WMASK] = match_head = head[G1.ins_h]; \
+ head[G1.ins_h] = (s); \
+} while (0)
+
+static ulg deflate(void)
+{
+ IPos hash_head; /* head of hash chain */
+ IPos prev_match; /* previous match */
+ int flush; /* set if current block must be flushed */
+ int match_available = 0; /* set if previous match exists */
+ unsigned match_length = MIN_MATCH - 1; /* length of best match */
+
+ /* Process the input block. */
+ while (G1.lookahead != 0) {
+ /* Insert the string window[strstart .. strstart+2] in the
+ * dictionary, and set hash_head to the head of the hash chain:
+ */
+ INSERT_STRING(G1.strstart, hash_head);
+
+ /* Find the longest match, discarding those <= prev_length.
+ */
+ G1.prev_length = match_length;
+ prev_match = G1.match_start;
+ match_length = MIN_MATCH - 1;
+
+ if (hash_head != 0 && G1.prev_length < max_lazy_match
+ && G1.strstart - hash_head <= MAX_DIST
+ ) {
+ /* To simplify the code, we prevent matches with the string
+ * of window index 0 (in particular we have to avoid a match
+ * of the string with itself at the start of the input file).
+ */
+ match_length = longest_match(hash_head);
+ /* longest_match() sets match_start */
+ if (match_length > G1.lookahead)
+ match_length = G1.lookahead;
+
+ /* Ignore a length 3 match if it is too distant: */
+ if (match_length == MIN_MATCH && G1.strstart - G1.match_start > TOO_FAR) {
+ /* If prev_match is also MIN_MATCH, G1.match_start is garbage
+ * but we will ignore the current match anyway.
+ */
+ match_length--;
+ }
+ }
+ /* If there was a match at the previous step and the current
+ * match is not better, output the previous match:
+ */
+ if (G1.prev_length >= MIN_MATCH && match_length <= G1.prev_length) {
+ check_match(G1.strstart - 1, prev_match, G1.prev_length);
+ flush = ct_tally(G1.strstart - 1 - prev_match, G1.prev_length - MIN_MATCH);
+
+ /* Insert in hash table all strings up to the end of the match.
+ * strstart-1 and strstart are already inserted.
+ */
+ G1.lookahead -= G1.prev_length - 1;
+ G1.prev_length -= 2;
+ do {
+ G1.strstart++;
+ INSERT_STRING(G1.strstart, hash_head);
+ /* strstart never exceeds WSIZE-MAX_MATCH, so there are
+ * always MIN_MATCH bytes ahead. If lookahead < MIN_MATCH
+ * these bytes are garbage, but it does not matter since the
+ * next lookahead bytes will always be emitted as literals.
+ */
+ } while (--G1.prev_length != 0);
+ match_available = 0;
+ match_length = MIN_MATCH - 1;
+ G1.strstart++;
+ if (flush) {
+ FLUSH_BLOCK(0);
+ G1.block_start = G1.strstart;
+ }
+ } else if (match_available) {
+ /* If there was no match at the previous position, output a
+ * single literal. If there was a match but the current match
+ * is longer, truncate the previous match to a single literal.
+ */
+ Tracevv((stderr, "%c", G1.window[G1.strstart - 1]));
+ if (ct_tally(0, G1.window[G1.strstart - 1])) {
+ FLUSH_BLOCK(0);
+ G1.block_start = G1.strstart;
+ }
+ G1.strstart++;
+ G1.lookahead--;
+ } else {
+ /* There is no previous match to compare with, wait for
+ * the next step to decide.
+ */
+ match_available = 1;
+ G1.strstart++;
+ G1.lookahead--;
+ }
+ Assert(G1.strstart <= G1.isize && lookahead <= G1.isize, "a bit too far");
+
+ /* Make sure that we always have enough lookahead, except
+ * at the end of the input file. We need MAX_MATCH bytes
+ * for the next match, plus MIN_MATCH bytes to insert the
+ * string following the next match.
+ */
+ while (G1.lookahead < MIN_LOOKAHEAD && !G1.eofile)
+ fill_window();
+ }
+ if (match_available)
+ ct_tally(0, G1.window[G1.strstart - 1]);
+
+ return FLUSH_BLOCK(1); /* eof */
+}
+
+
+/* ===========================================================================
+ * Initialize the bit string routines.
+ */
+static void bi_init(void)
+{
+ G1.bi_buf = 0;
+ G1.bi_valid = 0;
+#ifdef DEBUG
+ G1.bits_sent = 0L;
+#endif
+}
+
+
+/* ===========================================================================
+ * Initialize the "longest match" routines for a new file
+ */
+static void lm_init(ush * flagsp)
+{
+ unsigned j;
+
+ /* Initialize the hash table. */
+ memset(head, 0, HASH_SIZE * sizeof(*head));
+ /* prev will be initialized on the fly */
+
+ /* speed options for the general purpose bit flag */
+ *flagsp |= 2; /* FAST 4, SLOW 2 */
+ /* ??? reduce max_chain_length for binary files */
+
+ G1.strstart = 0;
+ G1.block_start = 0L;
+
+ G1.lookahead = file_read(G1.window,
+ sizeof(int) <= 2 ? (unsigned) WSIZE : 2 * WSIZE);
+
+ if (G1.lookahead == 0 || G1.lookahead == (unsigned) -1) {
+ G1.eofile = 1;
+ G1.lookahead = 0;
+ return;
+ }
+ G1.eofile = 0;
+ /* Make sure that we always have enough lookahead. This is important
+ * if input comes from a device such as a tty.
+ */
+ while (G1.lookahead < MIN_LOOKAHEAD && !G1.eofile)
+ fill_window();
+
+ G1.ins_h = 0;
+ for (j = 0; j < MIN_MATCH - 1; j++)
+ UPDATE_HASH(G1.ins_h, G1.window[j]);
+ /* If lookahead < MIN_MATCH, ins_h is garbage, but this is
+ * not important since only literal bytes will be emitted.
+ */
+}
+
+
+/* ===========================================================================
+ * Allocate the match buffer, initialize the various tables and save the
+ * location of the internal file attribute (ascii/binary) and method
+ * (DEFLATE/STORE).
+ * One callsite in zip()
+ */
+static void ct_init(void)
+{
+ int n; /* iterates over tree elements */
+ int length; /* length value */
+ int code; /* code value */
+ int dist; /* distance index */
+
+ G2.compressed_len = 0L;
+
+#ifdef NOT_NEEDED
+ if (G2.static_dtree[0].Len != 0)
+ return; /* ct_init already called */
+#endif
+
+ /* Initialize the mapping length (0..255) -> length code (0..28) */
+ length = 0;
+ for (code = 0; code < LENGTH_CODES - 1; code++) {
+ G2.base_length[code] = length;
+ for (n = 0; n < (1 << extra_lbits[code]); n++) {
+ G2.length_code[length++] = code;
+ }
+ }
+ Assert(length == 256, "ct_init: length != 256");
+ /* Note that the length 255 (match length 258) can be represented
+ * in two different ways: code 284 + 5 bits or code 285, so we
+ * overwrite length_code[255] to use the best encoding:
+ */
+ G2.length_code[length - 1] = code;
+
+ /* Initialize the mapping dist (0..32K) -> dist code (0..29) */
+ dist = 0;
+ for (code = 0; code < 16; code++) {
+ G2.base_dist[code] = dist;
+ for (n = 0; n < (1 << extra_dbits[code]); n++) {
+ G2.dist_code[dist++] = code;
+ }
+ }
+ Assert(dist == 256, "ct_init: dist != 256");
+ dist >>= 7; /* from now on, all distances are divided by 128 */
+ for (; code < D_CODES; code++) {
+ G2.base_dist[code] = dist << 7;
+ for (n = 0; n < (1 << (extra_dbits[code] - 7)); n++) {
+ G2.dist_code[256 + dist++] = code;
+ }
+ }
+ Assert(dist == 256, "ct_init: 256+dist != 512");
+
+ /* Construct the codes of the static literal tree */
+ /* already zeroed - it's in bss
+ for (n = 0; n <= MAX_BITS; n++)
+ G2.bl_count[n] = 0; */
+
+ n = 0;
+ while (n <= 143) {
+ G2.static_ltree[n++].Len = 8;
+ G2.bl_count[8]++;
+ }
+ while (n <= 255) {
+ G2.static_ltree[n++].Len = 9;
+ G2.bl_count[9]++;
+ }
+ while (n <= 279) {
+ G2.static_ltree[n++].Len = 7;
+ G2.bl_count[7]++;
+ }
+ while (n <= 287) {
+ G2.static_ltree[n++].Len = 8;
+ G2.bl_count[8]++;
+ }
+ /* Codes 286 and 287 do not exist, but we must include them in the
+ * tree construction to get a canonical Huffman tree (longest code
+ * all ones)
+ */
+ gen_codes((ct_data *) G2.static_ltree, L_CODES + 1);
+
+ /* The static distance tree is trivial: */
+ for (n = 0; n < D_CODES; n++) {
+ G2.static_dtree[n].Len = 5;
+ G2.static_dtree[n].Code = bi_reverse(n, 5);
+ }
+
+ /* Initialize the first block of the first file: */
+ init_block();
+}
+
+
+/* ===========================================================================
+ * Deflate in to out.
+ * IN assertions: the input and output buffers are cleared.
+ */
+
+static void zip(ulg time_stamp)
+{
+ ush deflate_flags = 0; /* pkzip -es, -en or -ex equivalent */
+
+ G1.outcnt = 0;
+
+ /* Write the header to the gzip file. See algorithm.doc for the format */
+ /* magic header for gzip files: 1F 8B */
+ /* compression method: 8 (DEFLATED) */
+ /* general flags: 0 */
+ put_32bit(0x00088b1f);
+ put_32bit(time_stamp);
+
+ /* Write deflated file to zip file */
+ G1.crc = ~0;
+
+ bi_init();
+ ct_init();
+ lm_init(&deflate_flags);
+
+ put_8bit(deflate_flags); /* extra flags */
+ put_8bit(3); /* OS identifier = 3 (Unix) */
+
+ deflate();
+
+ /* Write the crc and uncompressed size */
+ put_32bit(~G1.crc);
+ put_32bit(G1.isize);
+
+ flush_outbuf();
+}
+
+
+/* ======================================================================== */
+static
+char* make_new_name_gzip(char *filename)
+{
+ return xasprintf("%s.gz", filename);
+}
+
+static
+USE_DESKTOP(long long) int pack_gzip(void)
+{
+ struct stat s;
+
+ clear_bufs();
+ s.st_ctime = 0;
+ fstat(STDIN_FILENO, &s);
+ zip(s.st_ctime);
+ return 0;
+}
+
+int gzip_main(int argc, char **argv);
+int gzip_main(int argc, char **argv)
+{
+ unsigned opt;
+
+ /* Must match bbunzip's constants OPT_STDOUT, OPT_FORCE! */
+ opt = getopt32(argc, argv, "cfv" USE_GUNZIP("d") "q123456789" );
+ option_mask32 &= 0x7; /* Clear -d, ignore -q, -0..9 */
+ //if (opt & 0x1) // -c
+ //if (opt & 0x2) // -f
+ //if (opt & 0x4) // -v
+#if ENABLE_GUNZIP /* gunzip_main may not be visible... */
+ if (opt & 0x8) { // -d
+ return gunzip_main(argc, argv);
+ }
+#endif
+ argv += optind;
+
+ PTR_TO_GLOBALS = xzalloc(sizeof(struct globals) + sizeof(struct globals2))
+ + sizeof(struct globals);
+ G2.l_desc.dyn_tree = G2.dyn_ltree;
+ G2.l_desc.static_tree = G2.static_ltree;
+ G2.l_desc.extra_bits = extra_lbits;
+ G2.l_desc.extra_base = LITERALS + 1;
+ G2.l_desc.elems = L_CODES;
+ G2.l_desc.max_length = MAX_BITS;
+ //G2.l_desc.max_code = 0;
+
+ G2.d_desc.dyn_tree = G2.dyn_dtree;
+ G2.d_desc.static_tree = G2.static_dtree;
+ G2.d_desc.extra_bits = extra_dbits;
+ //G2.d_desc.extra_base = 0;
+ G2.d_desc.elems = D_CODES;
+ G2.d_desc.max_length = MAX_BITS;
+ //G2.d_desc.max_code = 0;
+
+ G2.bl_desc.dyn_tree = G2.bl_tree;
+ //G2.bl_desc.static_tree = NULL;
+ G2.bl_desc.extra_bits = extra_blbits,
+ //G2.bl_desc.extra_base = 0;
+ G2.bl_desc.elems = BL_CODES;
+ G2.bl_desc.max_length = MAX_BL_BITS;
+ //G2.bl_desc.max_code = 0;
+
+ /* Allocate all global buffers (for DYN_ALLOC option) */
+ ALLOC(uch, G1.l_buf, INBUFSIZ);
+ ALLOC(uch, G1.outbuf, OUTBUFSIZ);
+ ALLOC(ush, G1.d_buf, DIST_BUFSIZE);
+ ALLOC(uch, G1.window, 2L * WSIZE);
+ ALLOC(ush, G1.prev, 1L << BITS);
+
+ /* Initialise the CRC32 table */
+ G1.crc_32_tab = crc32_filltable(0);
+
+ return bbunpack(argv, make_new_name_gzip, pack_gzip);
+}
diff --git a/i/pc104/initrd/conf/busybox/archival/libunarchive/Kbuild b/i/pc104/initrd/conf/busybox/archival/libunarchive/Kbuild
new file mode 100644
index 0000000..4e14541
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/archival/libunarchive/Kbuild
@@ -0,0 +1,59 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+
+lib-y:= \
+\
+ data_skip.o \
+ data_extract_all.o \
+ data_extract_to_stdout.o \
+ data_extract_to_buffer.o \
+\
+ filter_accept_all.o \
+ filter_accept_list.o \
+ filter_accept_reject_list.o \
+\
+ header_skip.o \
+ header_list.o \
+ header_verbose_list.o \
+\
+ archive_xread_all_eof.o \
+\
+ seek_by_read.o \
+ seek_by_jump.o \
+\
+ data_align.o \
+ find_list_entry.o \
+ open_transformer.o \
+ init_handle.o
+
+GUNZIP_FILES:= check_header_gzip.o decompress_unzip.o
+DPKG_FILES:= \
+ get_header_ar.o \
+ unpack_ar_archive.o \
+ get_header_tar.o \
+ filter_accept_list_reassign.o
+
+lib-$(CONFIG_AR) += get_header_ar.o unpack_ar_archive.o
+lib-$(CONFIG_BUNZIP2) += decompress_bunzip2.o
+lib-$(CONFIG_UNLZMA) += decompress_unlzma.o
+lib-$(CONFIG_CPIO) += get_header_cpio.o
+lib-$(CONFIG_DPKG) += $(DPKG_FILES)
+lib-$(CONFIG_DPKG_DEB) += $(DPKG_FILES)
+lib-$(CONFIG_FEATURE_DEB_TAR_GZ) += $(GUNZIP_FILES) get_header_tar_gz.o
+lib-$(CONFIG_FEATURE_DEB_TAR_BZ2) += decompress_bunzip2.o get_header_tar_bz2.o
+lib-$(CONFIG_FEATURE_DEB_TAR_LZMA) += decompress_unlzma.o get_header_tar_lzma.o
+lib-$(CONFIG_GUNZIP) += $(GUNZIP_FILES)
+lib-$(CONFIG_FEATURE_GUNZIP_UNCOMPRESS) += decompress_uncompress.o
+lib-$(CONFIG_RPM2CPIO) += $(GUNZIP_FILES) get_header_cpio.o
+lib-$(CONFIG_RPM) += $(GUNZIP_FILES) get_header_cpio.o
+lib-$(CONFIG_TAR) += get_header_tar.o
+lib-$(CONFIG_FEATURE_TAR_BZIP2) += decompress_bunzip2.o get_header_tar_bz2.o
+lib-$(CONFIG_FEATURE_TAR_LZMA) += decompress_unlzma.o get_header_tar_lzma.o
+lib-$(CONFIG_FEATURE_TAR_GZIP) += $(GUNZIP_FILES) get_header_tar_gz.o
+lib-$(CONFIG_FEATURE_TAR_COMPRESS) += decompress_uncompress.o
+lib-$(CONFIG_UNCOMPRESS) += decompress_uncompress.o
+lib-$(CONFIG_UNZIP) += $(GUNZIP_FILES)
+lib-$(CONFIG_FEATURE_COMPRESS_USAGE) += decompress_bunzip2.o
diff --git a/i/pc104/initrd/conf/busybox/archival/libunarchive/archive_xread_all_eof.c b/i/pc104/initrd/conf/busybox/archival/libunarchive/archive_xread_all_eof.c
new file mode 100644
index 0000000..007f68c
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/archival/libunarchive/archive_xread_all_eof.c
@@ -0,0 +1,20 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "unarchive.h"
+#include "libbb.h"
+
+ssize_t archive_xread_all_eof(archive_handle_t *archive_handle,
+ unsigned char *buf, size_t count)
+{
+ ssize_t size;
+
+ size = full_read(archive_handle->src_fd, buf, count);
+ if (size != 0 && size != count) {
+ bb_error_msg_and_die("short read: %u of %u",
+ (unsigned)size, (unsigned)count);
+ }
+ return size;
+}
diff --git a/i/pc104/initrd/conf/busybox/archival/libunarchive/check_header_gzip.c b/i/pc104/initrd/conf/busybox/archival/libunarchive/check_header_gzip.c
new file mode 100644
index 0000000..66aa574
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/archival/libunarchive/check_header_gzip.c
@@ -0,0 +1,59 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "libbb.h"
+#include "unarchive.h" /* for external decl of check_header_gzip_or_die */
+
+void check_header_gzip_or_die(int src_fd)
+{
+ union {
+ unsigned char raw[8];
+ struct {
+ unsigned char method;
+ unsigned char flags;
+ unsigned int mtime;
+ unsigned char xtra_flags;
+ unsigned char os_flags;
+ } formatted;
+ } header;
+
+ xread(src_fd, header.raw, 8);
+
+ /* Check the compression method */
+ if (header.formatted.method != 8) {
+ bb_error_msg_and_die("unknown compression method %d",
+ header.formatted.method);
+ }
+
+ if (header.formatted.flags & 0x04) {
+ /* bit 2 set: extra field present */
+ unsigned extra_short;
+
+ extra_short = xread_char(src_fd) + (xread_char(src_fd) << 8);
+ while (extra_short > 0) {
+ /* Ignore extra field */
+ xread_char(src_fd);
+ extra_short--;
+ }
+ }
+
+ /* Discard original name if any */
+ if (header.formatted.flags & 0x08) {
+ /* bit 3 set: original file name present */
+ while (xread_char(src_fd) != 0);
+ }
+
+ /* Discard file comment if any */
+ if (header.formatted.flags & 0x10) {
+ /* bit 4 set: file comment present */
+ while (xread_char(src_fd) != 0);
+ }
+
+ /* Read the header checksum */
+ if (header.formatted.flags & 0x02) {
+ xread_char(src_fd);
+ xread_char(src_fd);
+ }
+}
diff --git a/i/pc104/initrd/conf/busybox/archival/libunarchive/data_align.c b/i/pc104/initrd/conf/busybox/archival/libunarchive/data_align.c
new file mode 100644
index 0000000..946c94d
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/archival/libunarchive/data_align.c
@@ -0,0 +1,17 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include <sys/types.h>
+
+#include "libbb.h"
+#include "unarchive.h"
+
+void data_align(archive_handle_t *archive_handle, const unsigned short boundary)
+{
+ const unsigned short skip_amount = (boundary - (archive_handle->offset % boundary)) % boundary;
+
+ archive_handle->seek(archive_handle, skip_amount);
+ archive_handle->offset += skip_amount;
+}
diff --git a/i/pc104/initrd/conf/busybox/archival/libunarchive/data_extract_all.c b/i/pc104/initrd/conf/busybox/archival/libunarchive/data_extract_all.c
new file mode 100644
index 0000000..0bb5bfe
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/archival/libunarchive/data_extract_all.c
@@ -0,0 +1,127 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "libbb.h"
+#include "unarchive.h"
+
+void data_extract_all(archive_handle_t *archive_handle)
+{
+ file_header_t *file_header = archive_handle->file_header;
+ int dst_fd;
+ int res;
+
+ if (archive_handle->flags & ARCHIVE_CREATE_LEADING_DIRS) {
+ char *name = xstrdup(file_header->name);
+ bb_make_directory(dirname(name), -1, FILEUTILS_RECUR);
+ free(name);
+ }
+
+ /* Check if the file already exists */
+ if (archive_handle->flags & ARCHIVE_EXTRACT_UNCONDITIONAL) {
+ /* Remove the existing entry if it exists */
+ if (((file_header->mode & S_IFMT) != S_IFDIR)
+ && (unlink(file_header->name) == -1)
+ && (errno != ENOENT)
+ ) {
+ bb_perror_msg_and_die("cannot remove old file");
+ }
+ }
+ else if (archive_handle->flags & ARCHIVE_EXTRACT_NEWER) {
+ /* Remove the existing entry if its older than the extracted entry */
+ struct stat statbuf;
+ if (lstat(file_header->name, &statbuf) == -1) {
+ if (errno != ENOENT) {
+ bb_perror_msg_and_die("cannot stat old file");
+ }
+ }
+ else if (statbuf.st_mtime <= file_header->mtime) {
+ if (!(archive_handle->flags & ARCHIVE_EXTRACT_QUIET)) {
+ bb_error_msg("%s not created: newer or "
+ "same age file exists", file_header->name);
+ }
+ data_skip(archive_handle);
+ return;
+ }
+ else if ((unlink(file_header->name) == -1) && (errno != EISDIR)) {
+ bb_perror_msg_and_die("cannot remove old file %s",
+ file_header->name);
+ }
+ }
+
+ /* Handle hard links separately
+ * We identified hard links as regular files of size 0 with a symlink */
+ if (S_ISREG(file_header->mode) && (file_header->link_name)
+ && (file_header->size == 0)
+ ) {
+ /* hard link */
+ res = link(file_header->link_name, file_header->name);
+ if ((res == -1) && !(archive_handle->flags & ARCHIVE_EXTRACT_QUIET)) {
+ bb_perror_msg("cannot create hard link");
+ }
+ } else {
+ /* Create the filesystem entry */
+ switch (file_header->mode & S_IFMT) {
+ case S_IFREG: {
+ /* Regular file */
+ dst_fd = xopen3(file_header->name, O_WRONLY | O_CREAT | O_EXCL,
+ file_header->mode);
+ bb_copyfd_exact_size(archive_handle->src_fd, dst_fd, file_header->size);
+ close(dst_fd);
+ break;
+ }
+ case S_IFDIR:
+ res = mkdir(file_header->name, file_header->mode);
+ if ((errno != EISDIR) && (res == -1)
+ && !(archive_handle->flags & ARCHIVE_EXTRACT_QUIET)
+ ) {
+ bb_perror_msg("extract_archive: %s", file_header->name);
+ }
+ break;
+ case S_IFLNK:
+ /* Symlink */
+ res = symlink(file_header->link_name, file_header->name);
+ if ((res == -1)
+ && !(archive_handle->flags & ARCHIVE_EXTRACT_QUIET)
+ ) {
+ bb_perror_msg("cannot create symlink "
+ "from %s to '%s'",
+ file_header->name,
+ file_header->link_name);
+ }
+ break;
+ case S_IFSOCK:
+ case S_IFBLK:
+ case S_IFCHR:
+ case S_IFIFO:
+ res = mknod(file_header->name, file_header->mode, file_header->device);
+ if ((res == -1)
+ && !(archive_handle->flags & ARCHIVE_EXTRACT_QUIET)
+ ) {
+ bb_perror_msg("cannot create node %s", file_header->name);
+ }
+ break;
+ default:
+ bb_error_msg_and_die("unrecognized file type");
+ }
+ }
+
+ if (!(archive_handle->flags & ARCHIVE_NOPRESERVE_OWN)) {
+ lchown(file_header->name, file_header->uid, file_header->gid);
+ }
+ /* uclibc has no lchmod, glibc is even stranger -
+ * it has lchmod which seems to do nothing!
+ * so we use chmod... */
+ if (!(archive_handle->flags & ARCHIVE_NOPRESERVE_PERM)
+ && (file_header->mode & S_IFMT) != S_IFLNK
+ ) {
+ chmod(file_header->name, file_header->mode);
+ }
+
+ if (archive_handle->flags & ARCHIVE_PRESERVE_DATE) {
+ struct utimbuf t;
+ t.actime = t.modtime = file_header->mtime;
+ utime(file_header->name, &t);
+ }
+}
diff --git a/i/pc104/initrd/conf/busybox/archival/libunarchive/data_extract_to_buffer.c b/i/pc104/initrd/conf/busybox/archival/libunarchive/data_extract_to_buffer.c
new file mode 100644
index 0000000..d8fcdf3
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/archival/libunarchive/data_extract_to_buffer.c
@@ -0,0 +1,17 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Copyright 2002 Glenn McGrath
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "libbb.h"
+#include "unarchive.h"
+
+void data_extract_to_buffer(archive_handle_t *archive_handle)
+{
+ unsigned int size = archive_handle->file_header->size;
+
+ archive_handle->buffer = xzalloc(size + 1);
+ xread(archive_handle->src_fd, archive_handle->buffer, size);
+}
diff --git a/i/pc104/initrd/conf/busybox/archival/libunarchive/data_extract_to_stdout.c b/i/pc104/initrd/conf/busybox/archival/libunarchive/data_extract_to_stdout.c
new file mode 100644
index 0000000..d87a4a5
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/archival/libunarchive/data_extract_to_stdout.c
@@ -0,0 +1,13 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "unarchive.h"
+
+void data_extract_to_stdout(archive_handle_t *archive_handle)
+{
+ bb_copyfd_exact_size(archive_handle->src_fd,
+ STDOUT_FILENO,
+ archive_handle->file_header->size);
+}
diff --git a/i/pc104/initrd/conf/busybox/archival/libunarchive/data_skip.c b/i/pc104/initrd/conf/busybox/archival/libunarchive/data_skip.c
new file mode 100644
index 0000000..dc3505a
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/archival/libunarchive/data_skip.c
@@ -0,0 +1,16 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include <sys/types.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include "unarchive.h"
+#include "libbb.h"
+
+void data_skip(archive_handle_t *archive_handle)
+{
+ archive_handle->seek(archive_handle, archive_handle->file_header->size);
+}
diff --git a/i/pc104/initrd/conf/busybox/archival/libunarchive/decompress_bunzip2.c b/i/pc104/initrd/conf/busybox/archival/libunarchive/decompress_bunzip2.c
new file mode 100644
index 0000000..ff7d64d
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/archival/libunarchive/decompress_bunzip2.c
@@ -0,0 +1,731 @@
+/* vi: set sw=4 ts=4: */
+/* Small bzip2 deflate implementation, by Rob Landley (rob@landley.net).
+
+ Based on bzip2 decompression code by Julian R Seward (jseward@acm.org),
+ which also acknowledges contributions by Mike Burrows, David Wheeler,
+ Peter Fenwick, Alistair Moffat, Radford Neal, Ian H. Witten,
+ Robert Sedgewick, and Jon L. Bentley.
+
+ Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+*/
+
+/*
+ Size and speed optimizations by Manuel Novoa III (mjn3@codepoet.org).
+
+ More efficient reading of Huffman codes, a streamlined read_bunzip()
+ function, and various other tweaks. In (limited) tests, approximately
+ 20% faster than bzcat on x86 and about 10% faster on arm.
+
+ Note that about 2/3 of the time is spent in read_unzip() reversing
+ the Burrows-Wheeler transformation. Much of that time is delay
+ resulting from cache misses.
+
+ I would ask that anyone benefiting from this work, especially those
+ using it in commercial products, consider making a donation to my local
+ non-profit hospice organization (www.hospiceacadiana.com) in the name of
+ the woman I loved, Toni W. Hagan, who passed away Feb. 12, 2003.
+
+ Manuel
+ */
+
+#include "libbb.h"
+#include "unarchive.h"
+
+/* Constants for Huffman coding */
+#define MAX_GROUPS 6
+#define GROUP_SIZE 50 /* 64 would have been more efficient */
+#define MAX_HUFCODE_BITS 20 /* Longest Huffman code allowed */
+#define MAX_SYMBOLS 258 /* 256 literals + RUNA + RUNB */
+#define SYMBOL_RUNA 0
+#define SYMBOL_RUNB 1
+
+/* Status return values */
+#define RETVAL_OK 0
+#define RETVAL_LAST_BLOCK (-1)
+#define RETVAL_NOT_BZIP_DATA (-2)
+#define RETVAL_UNEXPECTED_INPUT_EOF (-3)
+#define RETVAL_UNEXPECTED_OUTPUT_EOF (-4)
+#define RETVAL_DATA_ERROR (-5)
+#define RETVAL_OUT_OF_MEMORY (-6)
+#define RETVAL_OBSOLETE_INPUT (-7)
+
+/* Other housekeeping constants */
+#define IOBUF_SIZE 4096
+
+/* This is what we know about each Huffman coding group */
+struct group_data {
+ /* We have an extra slot at the end of limit[] for a sentinal value. */
+ int limit[MAX_HUFCODE_BITS+1],base[MAX_HUFCODE_BITS],permute[MAX_SYMBOLS];
+ int minLen, maxLen;
+};
+
+/* Structure holding all the housekeeping data, including IO buffers and
+ memory that persists between calls to bunzip */
+
+typedef struct {
+ /* State for interrupting output loop */
+
+ int writeCopies,writePos,writeRunCountdown,writeCount,writeCurrent;
+
+ /* I/O tracking data (file handles, buffers, positions, etc.) */
+
+ int in_fd,out_fd,inbufCount,inbufPos /*,outbufPos*/;
+ unsigned char *inbuf /*,*outbuf*/;
+ unsigned int inbufBitCount, inbufBits;
+
+ /* The CRC values stored in the block header and calculated from the data */
+
+ uint32_t headerCRC, totalCRC, writeCRC;
+ uint32_t *crc32Table;
+ /* Intermediate buffer and its size (in bytes) */
+
+ unsigned int *dbuf, dbufSize;
+
+ /* These things are a bit too big to go on the stack */
+
+ unsigned char selectors[32768]; /* nSelectors=15 bits */
+ struct group_data groups[MAX_GROUPS]; /* Huffman coding tables */
+
+ /* For I/O error handling */
+
+ jmp_buf jmpbuf;
+} bunzip_data;
+
+/* Return the next nnn bits of input. All reads from the compressed input
+ are done through this function. All reads are big endian */
+
+static unsigned int get_bits(bunzip_data *bd, char bits_wanted)
+{
+ unsigned int bits=0;
+
+ /* If we need to get more data from the byte buffer, do so. (Loop getting
+ one byte at a time to enforce endianness and avoid unaligned access.) */
+
+ while (bd->inbufBitCount<bits_wanted) {
+
+ /* If we need to read more data from file into byte buffer, do so */
+
+ if(bd->inbufPos==bd->inbufCount) {
+ if((bd->inbufCount = read(bd->in_fd, bd->inbuf, IOBUF_SIZE)) <= 0)
+ longjmp(bd->jmpbuf,RETVAL_UNEXPECTED_INPUT_EOF);
+ bd->inbufPos=0;
+ }
+
+ /* Avoid 32-bit overflow (dump bit buffer to top of output) */
+
+ if(bd->inbufBitCount>=24) {
+ bits=bd->inbufBits&((1<<bd->inbufBitCount)-1);
+ bits_wanted-=bd->inbufBitCount;
+ bits<<=bits_wanted;
+ bd->inbufBitCount=0;
+ }
+
+ /* Grab next 8 bits of input from buffer. */
+
+ bd->inbufBits=(bd->inbufBits<<8)|bd->inbuf[bd->inbufPos++];
+ bd->inbufBitCount+=8;
+ }
+
+ /* Calculate result */
+
+ bd->inbufBitCount-=bits_wanted;
+ bits|=(bd->inbufBits>>bd->inbufBitCount)&((1<<bits_wanted)-1);
+
+ return bits;
+}
+
+/* Unpacks the next block and sets up for the inverse burrows-wheeler step. */
+
+static int get_next_block(bunzip_data *bd)
+{
+ struct group_data *hufGroup;
+ int dbufCount,nextSym,dbufSize,groupCount,*base,*limit,selector,
+ i,j,k,t,runPos,symCount,symTotal,nSelectors,byteCount[256];
+ unsigned char uc, symToByte[256], mtfSymbol[256], *selectors;
+ unsigned int *dbuf,origPtr;
+
+ dbuf=bd->dbuf;
+ dbufSize=bd->dbufSize;
+ selectors=bd->selectors;
+
+ /* Reset longjmp I/O error handling */
+
+ i=setjmp(bd->jmpbuf);
+ if (i) return i;
+
+ /* Read in header signature and CRC, then validate signature.
+ (last block signature means CRC is for whole file, return now) */
+
+ i = get_bits(bd,24);
+ j = get_bits(bd,24);
+ bd->headerCRC=get_bits(bd,32);
+ if ((i == 0x177245) && (j == 0x385090)) return RETVAL_LAST_BLOCK;
+ if ((i != 0x314159) || (j != 0x265359)) return RETVAL_NOT_BZIP_DATA;
+
+ /* We can add support for blockRandomised if anybody complains. There was
+ some code for this in busybox 1.0.0-pre3, but nobody ever noticed that
+ it didn't actually work. */
+
+ if (get_bits(bd,1)) return RETVAL_OBSOLETE_INPUT;
+ if ((origPtr=get_bits(bd,24)) > dbufSize) return RETVAL_DATA_ERROR;
+
+ /* mapping table: if some byte values are never used (encoding things
+ like ascii text), the compression code removes the gaps to have fewer
+ symbols to deal with, and writes a sparse bitfield indicating which
+ values were present. We make a translation table to convert the symbols
+ back to the corresponding bytes. */
+
+ t=get_bits(bd, 16);
+ symTotal=0;
+ for (i=0;i<16;i++) {
+ if(t&(1<<(15-i))) {
+ k=get_bits(bd,16);
+ for (j=0;j<16;j++)
+ if(k&(1<<(15-j))) symToByte[symTotal++]=(16*i)+j;
+ }
+ }
+
+ /* How many different Huffman coding groups does this block use? */
+
+ groupCount=get_bits(bd,3);
+ if (groupCount<2 || groupCount>MAX_GROUPS) return RETVAL_DATA_ERROR;
+
+ /* nSelectors: Every GROUP_SIZE many symbols we select a new Huffman coding
+ group. Read in the group selector list, which is stored as MTF encoded
+ bit runs. (MTF=Move To Front, as each value is used it's moved to the
+ start of the list.) */
+
+ if(!(nSelectors=get_bits(bd, 15))) return RETVAL_DATA_ERROR;
+ for (i=0; i<groupCount; i++) mtfSymbol[i] = i;
+ for (i=0; i<nSelectors; i++) {
+
+ /* Get next value */
+
+ for (j=0;get_bits(bd,1);j++) if (j>=groupCount) return RETVAL_DATA_ERROR;
+
+ /* Decode MTF to get the next selector */
+
+ uc = mtfSymbol[j];
+ for (;j;j--) mtfSymbol[j] = mtfSymbol[j-1];
+ mtfSymbol[0]=selectors[i]=uc;
+ }
+
+ /* Read the Huffman coding tables for each group, which code for symTotal
+ literal symbols, plus two run symbols (RUNA, RUNB) */
+
+ symCount=symTotal+2;
+ for (j=0; j<groupCount; j++) {
+ unsigned char length[MAX_SYMBOLS],temp[MAX_HUFCODE_BITS+1];
+ int minLen, maxLen, pp;
+
+ /* Read Huffman code lengths for each symbol. They're stored in
+ a way similar to mtf; record a starting value for the first symbol,
+ and an offset from the previous value for everys symbol after that.
+ (Subtracting 1 before the loop and then adding it back at the end is
+ an optimization that makes the test inside the loop simpler: symbol
+ length 0 becomes negative, so an unsigned inequality catches it.) */
+
+ t=get_bits(bd, 5)-1;
+ for (i = 0; i < symCount; i++) {
+ for (;;) {
+ if (((unsigned)t) > (MAX_HUFCODE_BITS-1))
+ return RETVAL_DATA_ERROR;
+
+ /* If first bit is 0, stop. Else second bit indicates whether
+ to increment or decrement the value. Optimization: grab 2
+ bits and unget the second if the first was 0. */
+
+ k = get_bits(bd,2);
+ if (k < 2) {
+ bd->inbufBitCount++;
+ break;
+ }
+
+ /* Add one if second bit 1, else subtract 1. Avoids if/else */
+
+ t+=(((k+1)&2)-1);
+ }
+
+ /* Correct for the initial -1, to get the final symbol length */
+
+ length[i]=t+1;
+ }
+
+ /* Find largest and smallest lengths in this group */
+
+ minLen=maxLen=length[0];
+ for (i = 1; i < symCount; i++) {
+ if(length[i] > maxLen) maxLen = length[i];
+ else if(length[i] < minLen) minLen = length[i];
+ }
+
+ /* Calculate permute[], base[], and limit[] tables from length[].
+ *
+ * permute[] is the lookup table for converting Huffman coded symbols
+ * into decoded symbols. base[] is the amount to subtract from the
+ * value of a Huffman symbol of a given length when using permute[].
+ *
+ * limit[] indicates the largest numerical value a symbol with a given
+ * number of bits can have. This is how the Huffman codes can vary in
+ * length: each code with a value>limit[length] needs another bit.
+ */
+
+ hufGroup=bd->groups+j;
+ hufGroup->minLen = minLen;
+ hufGroup->maxLen = maxLen;
+
+ /* Note that minLen can't be smaller than 1, so we adjust the base
+ and limit array pointers so we're not always wasting the first
+ entry. We do this again when using them (during symbol decoding).*/
+
+ base=hufGroup->base-1;
+ limit=hufGroup->limit-1;
+
+ /* Calculate permute[]. Concurently, initialize temp[] and limit[]. */
+
+ pp=0;
+ for (i=minLen;i<=maxLen;i++) {
+ temp[i]=limit[i]=0;
+ for (t=0;t<symCount;t++)
+ if(length[t]==i) hufGroup->permute[pp++] = t;
+ }
+
+ /* Count symbols coded for at each bit length */
+
+ for (i=0;i<symCount;i++) temp[length[i]]++;
+
+ /* Calculate limit[] (the largest symbol-coding value at each bit
+ * length, which is (previous limit<<1)+symbols at this level), and
+ * base[] (number of symbols to ignore at each bit length, which is
+ * limit minus the cumulative count of symbols coded for already). */
+
+ pp=t=0;
+ for (i=minLen; i<maxLen; i++) {
+ pp+=temp[i];
+
+ /* We read the largest possible symbol size and then unget bits
+ after determining how many we need, and those extra bits could
+ be set to anything. (They're noise from future symbols.) At
+ each level we're really only interested in the first few bits,
+ so here we set all the trailing to-be-ignored bits to 1 so they
+ don't affect the value>limit[length] comparison. */
+
+ limit[i]= (pp << (maxLen - i)) - 1;
+ pp<<=1;
+ base[i+1]=pp-(t+=temp[i]);
+ }
+ limit[maxLen+1] = INT_MAX; /* Sentinal value for reading next sym. */
+ limit[maxLen]=pp+temp[maxLen]-1;
+ base[minLen]=0;
+ }
+
+ /* We've finished reading and digesting the block header. Now read this
+ block's Huffman coded symbols from the file and undo the Huffman coding
+ and run length encoding, saving the result into dbuf[dbufCount++]=uc */
+
+ /* Initialize symbol occurrence counters and symbol Move To Front table */
+
+ for (i=0;i<256;i++) {
+ byteCount[i] = 0;
+ mtfSymbol[i]=(unsigned char)i;
+ }
+
+ /* Loop through compressed symbols. */
+
+ runPos=dbufCount=selector=0;
+ for (;;) {
+
+ /* fetch next Huffman coding group from list. */
+
+ symCount=GROUP_SIZE-1;
+ if(selector>=nSelectors) return RETVAL_DATA_ERROR;
+ hufGroup=bd->groups+selectors[selector++];
+ base=hufGroup->base-1;
+ limit=hufGroup->limit-1;
+continue_this_group:
+
+ /* Read next Huffman-coded symbol. */
+
+ /* Note: It is far cheaper to read maxLen bits and back up than it is
+ to read minLen bits and then an additional bit at a time, testing
+ as we go. Because there is a trailing last block (with file CRC),
+ there is no danger of the overread causing an unexpected EOF for a
+ valid compressed file. As a further optimization, we do the read
+ inline (falling back to a call to get_bits if the buffer runs
+ dry). The following (up to got_huff_bits:) is equivalent to
+ j=get_bits(bd,hufGroup->maxLen);
+ */
+
+ while (bd->inbufBitCount<hufGroup->maxLen) {
+ if(bd->inbufPos==bd->inbufCount) {
+ j = get_bits(bd,hufGroup->maxLen);
+ goto got_huff_bits;
+ }
+ bd->inbufBits=(bd->inbufBits<<8)|bd->inbuf[bd->inbufPos++];
+ bd->inbufBitCount+=8;
+ };
+ bd->inbufBitCount-=hufGroup->maxLen;
+ j = (bd->inbufBits>>bd->inbufBitCount)&((1<<hufGroup->maxLen)-1);
+
+got_huff_bits:
+
+ /* Figure how how many bits are in next symbol and unget extras */
+
+ i=hufGroup->minLen;
+ while (j>limit[i]) ++i;
+ bd->inbufBitCount += (hufGroup->maxLen - i);
+
+ /* Huffman decode value to get nextSym (with bounds checking) */
+
+ if ((i > hufGroup->maxLen)
+ || (((unsigned)(j=(j>>(hufGroup->maxLen-i))-base[i]))
+ >= MAX_SYMBOLS))
+ return RETVAL_DATA_ERROR;
+ nextSym = hufGroup->permute[j];
+
+ /* We have now decoded the symbol, which indicates either a new literal
+ byte, or a repeated run of the most recent literal byte. First,
+ check if nextSym indicates a repeated run, and if so loop collecting
+ how many times to repeat the last literal. */
+
+ if (((unsigned)nextSym) <= SYMBOL_RUNB) { /* RUNA or RUNB */
+
+ /* If this is the start of a new run, zero out counter */
+
+ if(!runPos) {
+ runPos = 1;
+ t = 0;
+ }
+
+ /* Neat trick that saves 1 symbol: instead of or-ing 0 or 1 at
+ each bit position, add 1 or 2 instead. For example,
+ 1011 is 1<<0 + 1<<1 + 2<<2. 1010 is 2<<0 + 2<<1 + 1<<2.
+ You can make any bit pattern that way using 1 less symbol than
+ the basic or 0/1 method (except all bits 0, which would use no
+ symbols, but a run of length 0 doesn't mean anything in this
+ context). Thus space is saved. */
+
+ t += (runPos << nextSym); /* +runPos if RUNA; +2*runPos if RUNB */
+ if(runPos < dbufSize) runPos <<= 1;
+ goto end_of_huffman_loop;
+ }
+
+ /* When we hit the first non-run symbol after a run, we now know
+ how many times to repeat the last literal, so append that many
+ copies to our buffer of decoded symbols (dbuf) now. (The last
+ literal used is the one at the head of the mtfSymbol array.) */
+
+ if(runPos) {
+ runPos=0;
+ if(dbufCount+t>=dbufSize) return RETVAL_DATA_ERROR;
+
+ uc = symToByte[mtfSymbol[0]];
+ byteCount[uc] += t;
+ while (t--) dbuf[dbufCount++]=uc;
+ }
+
+ /* Is this the terminating symbol? */
+
+ if(nextSym>symTotal) break;
+
+ /* At this point, nextSym indicates a new literal character. Subtract
+ one to get the position in the MTF array at which this literal is
+ currently to be found. (Note that the result can't be -1 or 0,
+ because 0 and 1 are RUNA and RUNB. But another instance of the
+ first symbol in the mtf array, position 0, would have been handled
+ as part of a run above. Therefore 1 unused mtf position minus
+ 2 non-literal nextSym values equals -1.) */
+
+ if(dbufCount>=dbufSize) return RETVAL_DATA_ERROR;
+ i = nextSym - 1;
+ uc = mtfSymbol[i];
+
+ /* Adjust the MTF array. Since we typically expect to move only a
+ * small number of symbols, and are bound by 256 in any case, using
+ * memmove here would typically be bigger and slower due to function
+ * call overhead and other assorted setup costs. */
+
+ do {
+ mtfSymbol[i] = mtfSymbol[i-1];
+ } while (--i);
+ mtfSymbol[0] = uc;
+ uc=symToByte[uc];
+
+ /* We have our literal byte. Save it into dbuf. */
+
+ byteCount[uc]++;
+ dbuf[dbufCount++] = (unsigned int)uc;
+
+ /* Skip group initialization if we're not done with this group. Done
+ * this way to avoid compiler warning. */
+
+end_of_huffman_loop:
+ if(symCount--) goto continue_this_group;
+ }
+
+ /* At this point, we've read all the Huffman-coded symbols (and repeated
+ runs) for this block from the input stream, and decoded them into the
+ intermediate buffer. There are dbufCount many decoded bytes in dbuf[].
+ Now undo the Burrows-Wheeler transform on dbuf.
+ See http://dogma.net/markn/articles/bwt/bwt.htm
+ */
+
+ /* Turn byteCount into cumulative occurrence counts of 0 to n-1. */
+
+ j=0;
+ for (i=0;i<256;i++) {
+ k=j+byteCount[i];
+ byteCount[i] = j;
+ j=k;
+ }
+
+ /* Figure out what order dbuf would be in if we sorted it. */
+
+ for (i=0;i<dbufCount;i++) {
+ uc=(unsigned char)(dbuf[i] & 0xff);
+ dbuf[byteCount[uc]] |= (i << 8);
+ byteCount[uc]++;
+ }
+
+ /* Decode first byte by hand to initialize "previous" byte. Note that it
+ doesn't get output, and if the first three characters are identical
+ it doesn't qualify as a run (hence writeRunCountdown=5). */
+
+ if(dbufCount) {
+ if(origPtr>=dbufCount) return RETVAL_DATA_ERROR;
+ bd->writePos=dbuf[origPtr];
+ bd->writeCurrent=(unsigned char)(bd->writePos&0xff);
+ bd->writePos>>=8;
+ bd->writeRunCountdown=5;
+ }
+ bd->writeCount=dbufCount;
+
+ return RETVAL_OK;
+}
+
+/* Undo burrows-wheeler transform on intermediate buffer to produce output.
+ If start_bunzip was initialized with out_fd=-1, then up to len bytes of
+ data are written to outbuf. Return value is number of bytes written or
+ error (all errors are negative numbers). If out_fd!=-1, outbuf and len
+ are ignored, data is written to out_fd and return is RETVAL_OK or error.
+*/
+
+static int read_bunzip(bunzip_data *bd, char *outbuf, int len)
+{
+ const unsigned int *dbuf;
+ int pos,current,previous,gotcount;
+
+ /* If last read was short due to end of file, return last block now */
+ if(bd->writeCount<0) return bd->writeCount;
+
+ gotcount = 0;
+ dbuf=bd->dbuf;
+ pos=bd->writePos;
+ current=bd->writeCurrent;
+
+ /* We will always have pending decoded data to write into the output
+ buffer unless this is the very first call (in which case we haven't
+ Huffman-decoded a block into the intermediate buffer yet). */
+
+ if (bd->writeCopies) {
+
+ /* Inside the loop, writeCopies means extra copies (beyond 1) */
+
+ --bd->writeCopies;
+
+ /* Loop outputting bytes */
+
+ for (;;) {
+
+ /* If the output buffer is full, snapshot state and return */
+
+ if(gotcount >= len) {
+ bd->writePos=pos;
+ bd->writeCurrent=current;
+ bd->writeCopies++;
+ return len;
+ }
+
+ /* Write next byte into output buffer, updating CRC */
+
+ outbuf[gotcount++] = current;
+ bd->writeCRC=(((bd->writeCRC)<<8)
+ ^bd->crc32Table[((bd->writeCRC)>>24)^current]);
+
+ /* Loop now if we're outputting multiple copies of this byte */
+
+ if (bd->writeCopies) {
+ --bd->writeCopies;
+ continue;
+ }
+decode_next_byte:
+ if (!bd->writeCount--) break;
+ /* Follow sequence vector to undo Burrows-Wheeler transform */
+ previous=current;
+ pos=dbuf[pos];
+ current=pos&0xff;
+ pos>>=8;
+
+ /* After 3 consecutive copies of the same byte, the 4th is a repeat
+ count. We count down from 4 instead
+ * of counting up because testing for non-zero is faster */
+
+ if(--bd->writeRunCountdown) {
+ if(current!=previous) bd->writeRunCountdown=4;
+ } else {
+
+ /* We have a repeated run, this byte indicates the count */
+
+ bd->writeCopies=current;
+ current=previous;
+ bd->writeRunCountdown=5;
+
+ /* Sometimes there are just 3 bytes (run length 0) */
+
+ if(!bd->writeCopies) goto decode_next_byte;
+
+ /* Subtract the 1 copy we'd output anyway to get extras */
+
+ --bd->writeCopies;
+ }
+ }
+
+ /* Decompression of this block completed successfully */
+
+ bd->writeCRC=~bd->writeCRC;
+ bd->totalCRC=((bd->totalCRC<<1) | (bd->totalCRC>>31)) ^ bd->writeCRC;
+
+ /* If this block had a CRC error, force file level CRC error. */
+
+ if(bd->writeCRC!=bd->headerCRC) {
+ bd->totalCRC=bd->headerCRC+1;
+ return RETVAL_LAST_BLOCK;
+ }
+ }
+
+ /* Refill the intermediate buffer by Huffman-decoding next block of input */
+ /* (previous is just a convenient unused temp variable here) */
+
+ previous=get_next_block(bd);
+ if(previous) {
+ bd->writeCount=previous;
+ return (previous!=RETVAL_LAST_BLOCK) ? previous : gotcount;
+ }
+ bd->writeCRC=~0;
+ pos=bd->writePos;
+ current=bd->writeCurrent;
+ goto decode_next_byte;
+}
+
+/* Allocate the structure, read file header. If in_fd==-1, inbuf must contain
+ a complete bunzip file (len bytes long). If in_fd!=-1, inbuf and len are
+ ignored, and data is read from file handle into temporary buffer. */
+
+static int start_bunzip(bunzip_data **bdp, int in_fd, unsigned char *inbuf,
+ int len)
+{
+ bunzip_data *bd;
+ unsigned int i;
+ const unsigned int BZh0=(((unsigned int)'B')<<24)+(((unsigned int)'Z')<<16)
+ +(((unsigned int)'h')<<8)+(unsigned int)'0';
+
+ /* Figure out how much data to allocate */
+
+ i=sizeof(bunzip_data);
+ if(in_fd!=-1) i+=IOBUF_SIZE;
+
+ /* Allocate bunzip_data. Most fields initialize to zero. */
+
+ bd=*bdp=xzalloc(i);
+
+ /* Setup input buffer */
+
+ if(-1==(bd->in_fd=in_fd)) {
+ bd->inbuf=inbuf;
+ bd->inbufCount=len;
+ } else bd->inbuf=(unsigned char *)(bd+1);
+
+ /* Init the CRC32 table (big endian) */
+
+ bd->crc32Table = crc32_filltable(1);
+
+ /* Setup for I/O error handling via longjmp */
+
+ i=setjmp(bd->jmpbuf);
+ if(i) return i;
+
+ /* Ensure that file starts with "BZh['1'-'9']." */
+
+ i = get_bits(bd,32);
+ if (((unsigned int)(i-BZh0-1)) >= 9) return RETVAL_NOT_BZIP_DATA;
+
+ /* Fourth byte (ascii '1'-'9'), indicates block size in units of 100k of
+ uncompressed data. Allocate intermediate buffer for block. */
+
+ bd->dbufSize=100000*(i-BZh0);
+
+ bd->dbuf=xmalloc(bd->dbufSize * sizeof(int));
+ return RETVAL_OK;
+}
+
+/* Example usage: decompress src_fd to dst_fd. (Stops at end of bzip data,
+ not end of file.) */
+
+USE_DESKTOP(long long) int
+uncompressStream(int src_fd, int dst_fd)
+{
+ USE_DESKTOP(long long total_written = 0;)
+ char *outbuf;
+ bunzip_data *bd;
+ int i;
+
+ outbuf=xmalloc(IOBUF_SIZE);
+ i=start_bunzip(&bd,src_fd,0,0);
+ if(!i) {
+ for (;;) {
+ if((i=read_bunzip(bd,outbuf,IOBUF_SIZE)) <= 0) break;
+ if(i!=write(dst_fd,outbuf,i)) {
+ i=RETVAL_UNEXPECTED_OUTPUT_EOF;
+ break;
+ }
+ USE_DESKTOP(total_written += i;)
+ }
+ }
+
+ /* Check CRC and release memory */
+
+ if(i==RETVAL_LAST_BLOCK) {
+ if (bd->headerCRC!=bd->totalCRC) {
+ bb_error_msg("data integrity error when decompressing");
+ } else {
+ i=RETVAL_OK;
+ }
+ } else if (i==RETVAL_UNEXPECTED_OUTPUT_EOF) {
+ bb_error_msg("compressed file ends unexpectedly");
+ } else {
+ bb_error_msg("decompression failed");
+ }
+ free(bd->dbuf);
+ free(bd);
+ free(outbuf);
+
+ return i ? i : USE_DESKTOP(total_written) + 0;
+}
+
+#ifdef TESTING
+
+static char * const bunzip_errors[]={NULL,"Bad file checksum","Not bzip data",
+ "Unexpected input EOF","Unexpected output EOF","Data error",
+ "Out of memory","Obsolete (pre 0.9.5) bzip format not supported."};
+
+/* Dumb little test thing, decompress stdin to stdout */
+int main(int argc, char *argv[])
+{
+ int i=uncompressStream(0,1);
+ char c;
+
+ if(i<0) fprintf(stderr,"%s\n", bunzip_errors[-i]);
+ else if(read(0,&c,1)) fprintf(stderr,"Trailing garbage ignored\n");
+ return -i;
+}
+#endif
diff --git a/i/pc104/initrd/conf/busybox/archival/libunarchive/decompress_uncompress.c b/i/pc104/initrd/conf/busybox/archival/libunarchive/decompress_uncompress.c
new file mode 100644
index 0000000..8ce3cba
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/archival/libunarchive/decompress_uncompress.c
@@ -0,0 +1,308 @@
+/* vi: set sw=4 ts=4: */
+#include "libbb.h"
+
+/* uncompress for busybox -- (c) 2002 Robert Griebl
+ *
+ * based on the original compress42.c source
+ * (see disclaimer below)
+ */
+
+
+/* (N)compress42.c - File compression ala IEEE Computer, Mar 1992.
+ *
+ * Authors:
+ * Spencer W. Thomas (decvax!harpo!utah-cs!utah-gr!thomas)
+ * Jim McKie (decvax!mcvax!jim)
+ * Steve Davies (decvax!vax135!petsd!peora!srd)
+ * Ken Turkowski (decvax!decwrl!turtlevax!ken)
+ * James A. Woods (decvax!ihnp4!ames!jaw)
+ * Joe Orost (decvax!vax135!petsd!joe)
+ * Dave Mack (csu@alembic.acs.com)
+ * Peter Jannesen, Network Communication Systems
+ * (peter@ncs.nl)
+ *
+ * marc@suse.de : a small security fix for a buffer overflow
+ *
+ * [... History snipped ...]
+ *
+ */
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+/* Default input buffer size */
+#define IBUFSIZ 2048
+
+/* Default output buffer size */
+#define OBUFSIZ 2048
+
+/* Defines for third byte of header */
+#define MAGIC_1 (char_type)'\037' /* First byte of compressed file */
+#define MAGIC_2 (char_type)'\235' /* Second byte of compressed file */
+#define BIT_MASK 0x1f /* Mask for 'number of compresssion bits' */
+ /* Masks 0x20 and 0x40 are free. */
+ /* I think 0x20 should mean that there is */
+ /* a fourth header byte (for expansion). */
+#define BLOCK_MODE 0x80 /* Block compresssion if table is full and */
+ /* compression rate is dropping flush tables */
+ /* the next two codes should not be changed lightly, as they must not */
+ /* lie within the contiguous general code space. */
+#define FIRST 257 /* first free entry */
+#define CLEAR 256 /* table clear output code */
+
+#define INIT_BITS 9 /* initial number of bits/code */
+
+
+/* machine variants which require cc -Dmachine: pdp11, z8000, DOS */
+#define FAST
+
+#define HBITS 17 /* 50% occupancy */
+#define HSIZE (1<<HBITS)
+#define HMASK (HSIZE-1)
+#define HPRIME 9941
+#define BITS 16
+#undef MAXSEG_64K
+#define MAXCODE(n) (1L << (n))
+
+#define htabof(i) htab[i]
+#define codetabof(i) codetab[i]
+#define tab_prefixof(i) codetabof(i)
+#define tab_suffixof(i) ((unsigned char *)(htab))[i]
+#define de_stack ((unsigned char *)&(htab[HSIZE-1]))
+#define clear_htab() memset(htab, -1, HSIZE)
+#define clear_tab_prefixof() memset(codetab, 0, 256);
+
+
+/*
+ * Decompress stdin to stdout. This routine adapts to the codes in the
+ * file building the "string" table on-the-fly; requiring no table to
+ * be stored in the compressed file. The tables used herein are shared
+ * with those of the compress() routine. See the definitions above.
+ */
+
+USE_DESKTOP(long long) int
+uncompress(int fd_in, int fd_out)
+{
+ USE_DESKTOP(long long total_written = 0;)
+ unsigned char *stackp;
+ long int code;
+ int finchar;
+ long int oldcode;
+ long int incode;
+ int inbits;
+ int posbits;
+ int outpos;
+ int insize;
+ int bitmask;
+ long int free_ent;
+ long int maxcode;
+ long int maxmaxcode;
+ int n_bits;
+ int rsize = 0;
+ RESERVE_CONFIG_UBUFFER(inbuf, IBUFSIZ + 64);
+ RESERVE_CONFIG_UBUFFER(outbuf, OBUFSIZ + 2048);
+ unsigned char htab[HSIZE];
+ unsigned short codetab[HSIZE];
+
+ /* Hmm, these were statics - why?! */
+ /* user settable max # bits/code */
+ int maxbits; /* = BITS; */
+ /* block compress mode -C compatible with 2.0 */
+ int block_mode; /* = BLOCK_MODE; */
+
+ memset(inbuf, 0, IBUFSIZ + 64);
+ memset(outbuf, 0, OBUFSIZ + 2048);
+
+ insize = 0;
+
+ /* xread isn't good here, we have to return - caller may want
+ * to do some cleanup (e.g. delete incomplete unpacked file etc) */
+ if (full_read(fd_in, inbuf, 1) != 1) {
+ bb_error_msg("short read");
+ return -1;
+ }
+
+ maxbits = inbuf[0] & BIT_MASK;
+ block_mode = inbuf[0] & BLOCK_MODE;
+ maxmaxcode = MAXCODE(maxbits);
+
+ if (maxbits > BITS) {
+ bb_error_msg("compressed with %d bits, can only handle "
+ "%d bits", maxbits, BITS);
+ return -1;
+ }
+
+ n_bits = INIT_BITS;
+ maxcode = MAXCODE(INIT_BITS) - 1;
+ bitmask = (1 << INIT_BITS) - 1;
+ oldcode = -1;
+ finchar = 0;
+ outpos = 0;
+ posbits = 0 << 3;
+
+ free_ent = ((block_mode) ? FIRST : 256);
+
+ /* As above, initialize the first 256 entries in the table. */
+ clear_tab_prefixof();
+
+ for (code = 255; code >= 0; --code) {
+ tab_suffixof(code) = (unsigned char) code;
+ }
+
+ do {
+ resetbuf:
+ {
+ int i;
+ int e;
+ int o;
+
+ o = posbits >> 3;
+ e = insize - o;
+
+ for (i = 0; i < e; ++i)
+ inbuf[i] = inbuf[i + o];
+
+ insize = e;
+ posbits = 0;
+ }
+
+ if (insize < (int) (IBUFSIZ + 64) - IBUFSIZ) {
+ rsize = safe_read(fd_in, inbuf + insize, IBUFSIZ);
+//error check??
+ insize += rsize;
+ }
+
+ inbits = ((rsize > 0) ? (insize - insize % n_bits) << 3 :
+ (insize << 3) - (n_bits - 1));
+
+ while (inbits > posbits) {
+ if (free_ent > maxcode) {
+ posbits =
+ ((posbits - 1) +
+ ((n_bits << 3) -
+ (posbits - 1 + (n_bits << 3)) % (n_bits << 3)));
+ ++n_bits;
+ if (n_bits == maxbits) {
+ maxcode = maxmaxcode;
+ } else {
+ maxcode = MAXCODE(n_bits) - 1;
+ }
+ bitmask = (1 << n_bits) - 1;
+ goto resetbuf;
+ }
+ {
+ unsigned char *p = &inbuf[posbits >> 3];
+
+ code = ((((long) (p[0])) | ((long) (p[1]) << 8) |
+ ((long) (p[2]) << 16)) >> (posbits & 0x7)) & bitmask;
+ }
+ posbits += n_bits;
+
+
+ if (oldcode == -1) {
+ oldcode = code;
+ finchar = (int) oldcode;
+ outbuf[outpos++] = (unsigned char) finchar;
+ continue;
+ }
+
+ if (code == CLEAR && block_mode) {
+ clear_tab_prefixof();
+ free_ent = FIRST - 1;
+ posbits =
+ ((posbits - 1) +
+ ((n_bits << 3) -
+ (posbits - 1 + (n_bits << 3)) % (n_bits << 3)));
+ n_bits = INIT_BITS;
+ maxcode = MAXCODE(INIT_BITS) - 1;
+ bitmask = (1 << INIT_BITS) - 1;
+ goto resetbuf;
+ }
+
+ incode = code;
+ stackp = de_stack;
+
+ /* Special case for KwKwK string. */
+ if (code >= free_ent) {
+ if (code > free_ent) {
+ unsigned char *p;
+
+ posbits -= n_bits;
+ p = &inbuf[posbits >> 3];
+
+ bb_error_msg
+ ("insize:%d posbits:%d inbuf:%02X %02X %02X %02X %02X (%d)",
+ insize, posbits, p[-1], p[0], p[1], p[2], p[3],
+ (posbits & 07));
+ bb_error_msg("uncompress: corrupt input");
+ return -1;
+ }
+
+ *--stackp = (unsigned char) finchar;
+ code = oldcode;
+ }
+
+ /* Generate output characters in reverse order */
+ while ((long int) code >= (long int) 256) {
+ *--stackp = tab_suffixof(code);
+ code = tab_prefixof(code);
+ }
+
+ finchar = tab_suffixof(code);
+ *--stackp = (unsigned char) finchar;
+
+ /* And put them out in forward order */
+ {
+ int i;
+
+ i = de_stack - stackp;
+ if (outpos + i >= OBUFSIZ) {
+ do {
+ if (i > OBUFSIZ - outpos) {
+ i = OBUFSIZ - outpos;
+ }
+
+ if (i > 0) {
+ memcpy(outbuf + outpos, stackp, i);
+ outpos += i;
+ }
+
+ if (outpos >= OBUFSIZ) {
+ full_write(fd_out, outbuf, outpos);
+//error check??
+ USE_DESKTOP(total_written += outpos;)
+ outpos = 0;
+ }
+ stackp += i;
+ i = de_stack - stackp;
+ } while (i > 0);
+ } else {
+ memcpy(outbuf + outpos, stackp, i);
+ outpos += i;
+ }
+ }
+
+ /* Generate the new entry. */
+ code = free_ent;
+ if (code < maxmaxcode) {
+ tab_prefixof(code) = (unsigned short) oldcode;
+ tab_suffixof(code) = (unsigned char) finchar;
+ free_ent = code + 1;
+ }
+
+ /* Remember previous code. */
+ oldcode = incode;
+ }
+
+ } while (rsize > 0);
+
+ if (outpos > 0) {
+ full_write(fd_out, outbuf, outpos);
+//error check??
+ USE_DESKTOP(total_written += outpos;)
+ }
+
+ RELEASE_CONFIG_BUFFER(inbuf);
+ RELEASE_CONFIG_BUFFER(outbuf);
+ return USE_DESKTOP(total_written) + 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/archival/libunarchive/decompress_unlzma.c b/i/pc104/initrd/conf/busybox/archival/libunarchive/decompress_unlzma.c
new file mode 100644
index 0000000..15a0275
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/archival/libunarchive/decompress_unlzma.c
@@ -0,0 +1,497 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Small lzma deflate implementation.
+ * Copyright (C) 2006 Aurelien Jacobs <aurel@gnuage.org>
+ *
+ * Based on LzmaDecode.c from the LZMA SDK 4.22 (http://www.7-zip.org/)
+ * Copyright (C) 1999-2005 Igor Pavlov
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "libbb.h"
+#include "unarchive.h"
+
+#ifdef CONFIG_FEATURE_LZMA_FAST
+# define speed_inline ATTRIBUTE_ALWAYS_INLINE
+#else
+# define speed_inline
+#endif
+
+
+typedef struct {
+ int fd;
+ uint8_t *ptr;
+
+/* Was keeping rc on stack in unlzma and separately allocating buffer,
+ * but with "buffer 'attached to' allocated rc" code is smaller: */
+ /* uint8_t *buffer; */
+#define RC_BUFFER ((uint8_t*)(rc+1))
+
+ uint8_t *buffer_end;
+
+/* Had provisions for variable buffer, but we don't need it here */
+ /* int buffer_size; */
+#define RC_BUFFER_SIZE 0x10000
+
+ uint32_t code;
+ uint32_t range;
+ uint32_t bound;
+} rc_t;
+
+#define RC_TOP_BITS 24
+#define RC_MOVE_BITS 5
+#define RC_MODEL_TOTAL_BITS 11
+
+
+/* Called twice: once at startup and once in rc_normalize() */
+static void rc_read(rc_t * rc)
+{
+ int buffer_size = safe_read(rc->fd, RC_BUFFER, RC_BUFFER_SIZE);
+ if (buffer_size <= 0)
+ bb_error_msg_and_die("unexpected EOF");
+ rc->ptr = RC_BUFFER;
+ rc->buffer_end = RC_BUFFER + buffer_size;
+}
+
+/* Called once */
+static rc_t* rc_init(int fd) /*, int buffer_size) */
+{
+ int i;
+ rc_t* rc;
+
+ rc = xmalloc(sizeof(rc_t) + RC_BUFFER_SIZE);
+
+ rc->fd = fd;
+ /* rc->buffer_size = buffer_size; */
+ rc->buffer_end = RC_BUFFER + RC_BUFFER_SIZE;
+ rc->ptr = rc->buffer_end;
+
+ rc->code = 0;
+ rc->range = 0xFFFFFFFF;
+ for (i = 0; i < 5; i++) {
+ if (rc->ptr >= rc->buffer_end)
+ rc_read(rc);
+ rc->code = (rc->code << 8) | *rc->ptr++;
+ }
+ return rc;
+}
+
+/* Called once */
+static ATTRIBUTE_ALWAYS_INLINE void rc_free(rc_t * rc)
+{
+ if (ENABLE_FEATURE_CLEAN_UP)
+ free(rc);
+}
+
+/* Called twice, but one callsite is in speed_inline'd rc_is_bit_0_helper() */
+static void rc_do_normalize(rc_t * rc)
+{
+ if (rc->ptr >= rc->buffer_end)
+ rc_read(rc);
+ rc->range <<= 8;
+ rc->code = (rc->code << 8) | *rc->ptr++;
+}
+static ATTRIBUTE_ALWAYS_INLINE void rc_normalize(rc_t * rc)
+{
+ if (rc->range < (1 << RC_TOP_BITS)) {
+ rc_do_normalize(rc);
+ }
+}
+
+/* Called 9 times */
+/* Why rc_is_bit_0_helper exists?
+ * Because we want to always expose (rc->code < rc->bound) to optimizer
+ */
+static speed_inline uint32_t rc_is_bit_0_helper(rc_t * rc, uint16_t * p)
+{
+ rc_normalize(rc);
+ rc->bound = *p * (rc->range >> RC_MODEL_TOTAL_BITS);
+ return rc->bound;
+}
+static ATTRIBUTE_ALWAYS_INLINE int rc_is_bit_0(rc_t * rc, uint16_t * p)
+{
+ uint32_t t = rc_is_bit_0_helper(rc, p);
+ return rc->code < t;
+}
+
+/* Called ~10 times, but very small, thus inlined */
+static speed_inline void rc_update_bit_0(rc_t * rc, uint16_t * p)
+{
+ rc->range = rc->bound;
+ *p += ((1 << RC_MODEL_TOTAL_BITS) - *p) >> RC_MOVE_BITS;
+}
+static speed_inline void rc_update_bit_1(rc_t * rc, uint16_t * p)
+{
+ rc->range -= rc->bound;
+ rc->code -= rc->bound;
+ *p -= *p >> RC_MOVE_BITS;
+}
+
+/* Called 4 times in unlzma loop */
+static int rc_get_bit(rc_t * rc, uint16_t * p, int *symbol)
+{
+ if (rc_is_bit_0(rc, p)) {
+ rc_update_bit_0(rc, p);
+ *symbol *= 2;
+ return 0;
+ } else {
+ rc_update_bit_1(rc, p);
+ *symbol = *symbol * 2 + 1;
+ return 1;
+ }
+}
+
+/* Called once */
+static ATTRIBUTE_ALWAYS_INLINE int rc_direct_bit(rc_t * rc)
+{
+ rc_normalize(rc);
+ rc->range >>= 1;
+ if (rc->code >= rc->range) {
+ rc->code -= rc->range;
+ return 1;
+ }
+ return 0;
+}
+
+/* Called twice */
+static speed_inline void
+rc_bit_tree_decode(rc_t * rc, uint16_t * p, int num_levels, int *symbol)
+{
+ int i = num_levels;
+
+ *symbol = 1;
+ while (i--)
+ rc_get_bit(rc, p + *symbol, symbol);
+ *symbol -= 1 << num_levels;
+}
+
+
+typedef struct {
+ uint8_t pos;
+ uint32_t dict_size;
+ uint64_t dst_size;
+} __attribute__ ((packed)) lzma_header_t;
+
+
+/* #defines will force compiler to compute/optimize each one with each usage.
+ * Have heart and use enum instead. */
+enum {
+ LZMA_BASE_SIZE = 1846,
+ LZMA_LIT_SIZE = 768,
+
+ LZMA_NUM_POS_BITS_MAX = 4,
+
+ LZMA_LEN_NUM_LOW_BITS = 3,
+ LZMA_LEN_NUM_MID_BITS = 3,
+ LZMA_LEN_NUM_HIGH_BITS = 8,
+
+ LZMA_LEN_CHOICE = 0,
+ LZMA_LEN_CHOICE_2 = (LZMA_LEN_CHOICE + 1),
+ LZMA_LEN_LOW = (LZMA_LEN_CHOICE_2 + 1),
+ LZMA_LEN_MID = (LZMA_LEN_LOW \
+ + (1 << (LZMA_NUM_POS_BITS_MAX + LZMA_LEN_NUM_LOW_BITS))),
+ LZMA_LEN_HIGH = (LZMA_LEN_MID \
+ + (1 << (LZMA_NUM_POS_BITS_MAX + LZMA_LEN_NUM_MID_BITS))),
+ LZMA_NUM_LEN_PROBS = (LZMA_LEN_HIGH + (1 << LZMA_LEN_NUM_HIGH_BITS)),
+
+ LZMA_NUM_STATES = 12,
+ LZMA_NUM_LIT_STATES = 7,
+
+ LZMA_START_POS_MODEL_INDEX = 4,
+ LZMA_END_POS_MODEL_INDEX = 14,
+ LZMA_NUM_FULL_DISTANCES = (1 << (LZMA_END_POS_MODEL_INDEX >> 1)),
+
+ LZMA_NUM_POS_SLOT_BITS = 6,
+ LZMA_NUM_LEN_TO_POS_STATES = 4,
+
+ LZMA_NUM_ALIGN_BITS = 4,
+
+ LZMA_MATCH_MIN_LEN = 2,
+
+ LZMA_IS_MATCH = 0,
+ LZMA_IS_REP = (LZMA_IS_MATCH + (LZMA_NUM_STATES << LZMA_NUM_POS_BITS_MAX)),
+ LZMA_IS_REP_G0 = (LZMA_IS_REP + LZMA_NUM_STATES),
+ LZMA_IS_REP_G1 = (LZMA_IS_REP_G0 + LZMA_NUM_STATES),
+ LZMA_IS_REP_G2 = (LZMA_IS_REP_G1 + LZMA_NUM_STATES),
+ LZMA_IS_REP_0_LONG = (LZMA_IS_REP_G2 + LZMA_NUM_STATES),
+ LZMA_POS_SLOT = (LZMA_IS_REP_0_LONG \
+ + (LZMA_NUM_STATES << LZMA_NUM_POS_BITS_MAX)),
+ LZMA_SPEC_POS = (LZMA_POS_SLOT \
+ + (LZMA_NUM_LEN_TO_POS_STATES << LZMA_NUM_POS_SLOT_BITS)),
+ LZMA_ALIGN = (LZMA_SPEC_POS \
+ + LZMA_NUM_FULL_DISTANCES - LZMA_END_POS_MODEL_INDEX),
+ LZMA_LEN_CODER = (LZMA_ALIGN + (1 << LZMA_NUM_ALIGN_BITS)),
+ LZMA_REP_LEN_CODER = (LZMA_LEN_CODER + LZMA_NUM_LEN_PROBS),
+ LZMA_LITERAL = (LZMA_REP_LEN_CODER + LZMA_NUM_LEN_PROBS),
+};
+
+
+USE_DESKTOP(long long) int
+unlzma(int src_fd, int dst_fd)
+{
+ USE_DESKTOP(long long total_written = 0;)
+ lzma_header_t header;
+ int lc, pb, lp;
+ uint32_t pos_state_mask;
+ uint32_t literal_pos_mask;
+ uint32_t pos;
+ uint16_t *p;
+ uint16_t *prob;
+ uint16_t *prob_lit;
+ int num_bits;
+ int num_probs;
+ rc_t *rc;
+ int i, mi;
+ uint8_t *buffer;
+ uint8_t previous_byte = 0;
+ size_t buffer_pos = 0, global_pos = 0;
+ int len = 0;
+ int state = 0;
+ uint32_t rep0 = 1, rep1 = 1, rep2 = 1, rep3 = 1;
+
+ xread(src_fd, &header, sizeof(header));
+
+ if (header.pos >= (9 * 5 * 5))
+ bb_error_msg_and_die("bad header");
+ mi = header.pos / 9;
+ lc = header.pos % 9;
+ pb = mi / 5;
+ lp = mi % 5;
+ pos_state_mask = (1 << pb) - 1;
+ literal_pos_mask = (1 << lp) - 1;
+
+ header.dict_size = SWAP_LE32(header.dict_size);
+ header.dst_size = SWAP_LE64(header.dst_size);
+
+ if (header.dict_size == 0)
+ header.dict_size = 1;
+
+ buffer = xmalloc(MIN(header.dst_size, header.dict_size));
+
+ num_probs = LZMA_BASE_SIZE + (LZMA_LIT_SIZE << (lc + lp));
+ p = xmalloc(num_probs * sizeof(*p));
+ num_probs = LZMA_LITERAL + (LZMA_LIT_SIZE << (lc + lp));
+ for (i = 0; i < num_probs; i++)
+ p[i] = (1 << RC_MODEL_TOTAL_BITS) >> 1;
+
+ rc = rc_init(src_fd); /*, RC_BUFFER_SIZE); */
+
+ while (global_pos + buffer_pos < header.dst_size) {
+ int pos_state = (buffer_pos + global_pos) & pos_state_mask;
+
+ prob =
+ p + LZMA_IS_MATCH + (state << LZMA_NUM_POS_BITS_MAX) + pos_state;
+ if (rc_is_bit_0(rc, prob)) {
+ mi = 1;
+ rc_update_bit_0(rc, prob);
+ prob = (p + LZMA_LITERAL + (LZMA_LIT_SIZE
+ * ((((buffer_pos + global_pos) & literal_pos_mask) << lc)
+ + (previous_byte >> (8 - lc)))));
+
+ if (state >= LZMA_NUM_LIT_STATES) {
+ int match_byte;
+
+ pos = buffer_pos - rep0;
+ while (pos >= header.dict_size)
+ pos += header.dict_size;
+ match_byte = buffer[pos];
+ do {
+ int bit;
+
+ match_byte <<= 1;
+ bit = match_byte & 0x100;
+ prob_lit = prob + 0x100 + bit + mi;
+ if (rc_get_bit(rc, prob_lit, &mi)) {
+ if (!bit)
+ break;
+ } else {
+ if (bit)
+ break;
+ }
+ } while (mi < 0x100);
+ }
+ while (mi < 0x100) {
+ prob_lit = prob + mi;
+ rc_get_bit(rc, prob_lit, &mi);
+ }
+ previous_byte = (uint8_t) mi;
+
+ buffer[buffer_pos++] = previous_byte;
+ if (buffer_pos == header.dict_size) {
+ buffer_pos = 0;
+ global_pos += header.dict_size;
+ if (full_write(dst_fd, buffer, header.dict_size) != header.dict_size)
+ goto bad;
+ USE_DESKTOP(total_written += header.dict_size;)
+ }
+ if (state < 4)
+ state = 0;
+ else if (state < 10)
+ state -= 3;
+ else
+ state -= 6;
+ } else {
+ int offset;
+ uint16_t *prob_len;
+
+ rc_update_bit_1(rc, prob);
+ prob = p + LZMA_IS_REP + state;
+ if (rc_is_bit_0(rc, prob)) {
+ rc_update_bit_0(rc, prob);
+ rep3 = rep2;
+ rep2 = rep1;
+ rep1 = rep0;
+ state = state < LZMA_NUM_LIT_STATES ? 0 : 3;
+ prob = p + LZMA_LEN_CODER;
+ } else {
+ rc_update_bit_1(rc, prob);
+ prob = p + LZMA_IS_REP_G0 + state;
+ if (rc_is_bit_0(rc, prob)) {
+ rc_update_bit_0(rc, prob);
+ prob = (p + LZMA_IS_REP_0_LONG
+ + (state << LZMA_NUM_POS_BITS_MAX) + pos_state);
+ if (rc_is_bit_0(rc, prob)) {
+ rc_update_bit_0(rc, prob);
+
+ state = state < LZMA_NUM_LIT_STATES ? 9 : 11;
+ pos = buffer_pos - rep0;
+ while (pos >= header.dict_size)
+ pos += header.dict_size;
+ previous_byte = buffer[pos];
+ buffer[buffer_pos++] = previous_byte;
+ if (buffer_pos == header.dict_size) {
+ buffer_pos = 0;
+ global_pos += header.dict_size;
+ if (full_write(dst_fd, buffer, header.dict_size) != header.dict_size)
+ goto bad;
+ USE_DESKTOP(total_written += header.dict_size;)
+ }
+ continue;
+ } else {
+ rc_update_bit_1(rc, prob);
+ }
+ } else {
+ uint32_t distance;
+
+ rc_update_bit_1(rc, prob);
+ prob = p + LZMA_IS_REP_G1 + state;
+ if (rc_is_bit_0(rc, prob)) {
+ rc_update_bit_0(rc, prob);
+ distance = rep1;
+ } else {
+ rc_update_bit_1(rc, prob);
+ prob = p + LZMA_IS_REP_G2 + state;
+ if (rc_is_bit_0(rc, prob)) {
+ rc_update_bit_0(rc, prob);
+ distance = rep2;
+ } else {
+ rc_update_bit_1(rc, prob);
+ distance = rep3;
+ rep3 = rep2;
+ }
+ rep2 = rep1;
+ }
+ rep1 = rep0;
+ rep0 = distance;
+ }
+ state = state < LZMA_NUM_LIT_STATES ? 8 : 11;
+ prob = p + LZMA_REP_LEN_CODER;
+ }
+
+ prob_len = prob + LZMA_LEN_CHOICE;
+ if (rc_is_bit_0(rc, prob_len)) {
+ rc_update_bit_0(rc, prob_len);
+ prob_len = (prob + LZMA_LEN_LOW
+ + (pos_state << LZMA_LEN_NUM_LOW_BITS));
+ offset = 0;
+ num_bits = LZMA_LEN_NUM_LOW_BITS;
+ } else {
+ rc_update_bit_1(rc, prob_len);
+ prob_len = prob + LZMA_LEN_CHOICE_2;
+ if (rc_is_bit_0(rc, prob_len)) {
+ rc_update_bit_0(rc, prob_len);
+ prob_len = (prob + LZMA_LEN_MID
+ + (pos_state << LZMA_LEN_NUM_MID_BITS));
+ offset = 1 << LZMA_LEN_NUM_LOW_BITS;
+ num_bits = LZMA_LEN_NUM_MID_BITS;
+ } else {
+ rc_update_bit_1(rc, prob_len);
+ prob_len = prob + LZMA_LEN_HIGH;
+ offset = ((1 << LZMA_LEN_NUM_LOW_BITS)
+ + (1 << LZMA_LEN_NUM_MID_BITS));
+ num_bits = LZMA_LEN_NUM_HIGH_BITS;
+ }
+ }
+ rc_bit_tree_decode(rc, prob_len, num_bits, &len);
+ len += offset;
+
+ if (state < 4) {
+ int pos_slot;
+
+ state += LZMA_NUM_LIT_STATES;
+ prob =
+ p + LZMA_POS_SLOT +
+ ((len <
+ LZMA_NUM_LEN_TO_POS_STATES ? len :
+ LZMA_NUM_LEN_TO_POS_STATES - 1)
+ << LZMA_NUM_POS_SLOT_BITS);
+ rc_bit_tree_decode(rc, prob, LZMA_NUM_POS_SLOT_BITS,
+ &pos_slot);
+ if (pos_slot >= LZMA_START_POS_MODEL_INDEX) {
+ num_bits = (pos_slot >> 1) - 1;
+ rep0 = 2 | (pos_slot & 1);
+ if (pos_slot < LZMA_END_POS_MODEL_INDEX) {
+ rep0 <<= num_bits;
+ prob = p + LZMA_SPEC_POS + rep0 - pos_slot - 1;
+ } else {
+ num_bits -= LZMA_NUM_ALIGN_BITS;
+ while (num_bits--)
+ rep0 = (rep0 << 1) | rc_direct_bit(rc);
+ prob = p + LZMA_ALIGN;
+ rep0 <<= LZMA_NUM_ALIGN_BITS;
+ num_bits = LZMA_NUM_ALIGN_BITS;
+ }
+ i = 1;
+ mi = 1;
+ while (num_bits--) {
+ if (rc_get_bit(rc, prob + mi, &mi))
+ rep0 |= i;
+ i <<= 1;
+ }
+ } else
+ rep0 = pos_slot;
+ if (++rep0 == 0)
+ break;
+ }
+
+ len += LZMA_MATCH_MIN_LEN;
+
+ do {
+ pos = buffer_pos - rep0;
+ while (pos >= header.dict_size)
+ pos += header.dict_size;
+ previous_byte = buffer[pos];
+ buffer[buffer_pos++] = previous_byte;
+ if (buffer_pos == header.dict_size) {
+ buffer_pos = 0;
+ global_pos += header.dict_size;
+ if (full_write(dst_fd, buffer, header.dict_size) != header.dict_size)
+ goto bad;
+ USE_DESKTOP(total_written += header.dict_size;)
+ }
+ len--;
+ } while (len != 0 && buffer_pos < header.dst_size);
+ }
+ }
+
+
+ if (full_write(dst_fd, buffer, buffer_pos) != buffer_pos) {
+ bad:
+ rc_free(rc);
+ return -1;
+ }
+ rc_free(rc);
+ USE_DESKTOP(total_written += buffer_pos;)
+ return USE_DESKTOP(total_written) + 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/archival/libunarchive/decompress_unzip.c b/i/pc104/initrd/conf/busybox/archival/libunarchive/decompress_unzip.c
new file mode 100644
index 0000000..331fe34
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/archival/libunarchive/decompress_unzip.c
@@ -0,0 +1,1104 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * gunzip implementation for busybox
+ *
+ * Based on GNU gzip v1.2.4 Copyright (C) 1992-1993 Jean-loup Gailly.
+ *
+ * Originally adjusted for busybox by Sven Rudolph <sr1@inf.tu-dresden.de>
+ * based on gzip sources
+ *
+ * Adjusted further by Erik Andersen <andersen@codepoet.org> to support
+ * files as well as stdin/stdout, and to generally behave itself wrt
+ * command line handling.
+ *
+ * General cleanup to better adhere to the style guide and make use of standard
+ * busybox functions by Glenn McGrath <bug1@iinet.net.au>
+ *
+ * read_gz interface + associated hacking by Laurence Anderson
+ *
+ * Fixed huft_build() so decoding end-of-block code does not grab more bits
+ * than necessary (this is required by unzip applet), added inflate_cleanup()
+ * to free leaked bytebuffer memory (used in unzip.c), and some minor style
+ * guide cleanups by Ed Clark
+ *
+ * gzip (GNU zip) -- compress files with zip algorithm and 'compress' interface
+ * Copyright (C) 1992-1993 Jean-loup Gailly
+ * The unzip code was written and put in the public domain by Mark Adler.
+ * Portions of the lzw code are derived from the public domain 'compress'
+ * written by Spencer Thomas, Joe Orost, James Woods, Jim McKie, Steve Davies,
+ * Ken Turkowski, Dave Mack and Peter Jannesen.
+ *
+ * See the file algorithm.doc for the compression algorithms and file formats.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "libbb.h"
+#include "unarchive.h"
+
+typedef struct huft_s {
+ unsigned char e; /* number of extra bits or operation */
+ unsigned char b; /* number of bits in this code or subcode */
+ union {
+ unsigned short n; /* literal, length base, or distance base */
+ struct huft_s *t; /* pointer to next level of table */
+ } v;
+} huft_t;
+
+enum {
+ /* gunzip_window size--must be a power of two, and
+ * at least 32K for zip's deflate method */
+ GUNZIP_WSIZE = 0x8000,
+ /* If BMAX needs to be larger than 16, then h and x[] should be ulg. */
+ BMAX = 16, /* maximum bit length of any code (16 for explode) */
+ N_MAX = 288, /* maximum number of codes in any set */
+};
+
+
+/* This is somewhat complex-looking arrangement, but it allows
+ * to place decompressor state either in bss or in
+ * malloc'ed space simply by changing #defines below.
+ * Sizes on i386:
+ * text data bss dec hex
+ * 5256 0 108 5364 14f4 - bss
+ * 4915 0 0 4915 1333 - malloc
+ */
+#define STATE_IN_BSS 0
+#define STATE_IN_MALLOC 1
+
+
+typedef struct state_t {
+ off_t gunzip_bytes_out; /* number of output bytes */
+ uint32_t gunzip_crc;
+
+ int gunzip_src_fd;
+ unsigned gunzip_outbuf_count; /* bytes in output buffer */
+
+ unsigned char *gunzip_window;
+
+ uint32_t *gunzip_crc_table;
+
+ /* bitbuffer */
+ unsigned gunzip_bb; /* bit buffer */
+ unsigned char gunzip_bk; /* bits in bit buffer */
+
+ /* These control the size of the STATE()bytebuffer */
+ unsigned bytebuffer_max;
+ unsigned char *bytebuffer;
+ unsigned bytebuffer_offset;
+ unsigned bytebuffer_size;
+
+ /* private data of inflate_codes() */
+ unsigned inflate_codes_ml; /* masks for bl and bd bits */
+ unsigned inflate_codes_md; /* masks for bl and bd bits */
+ unsigned inflate_codes_bb; /* bit buffer */
+ unsigned inflate_codes_k; /* number of bits in bit buffer */
+ unsigned inflate_codes_w; /* current gunzip_window position */
+ huft_t *inflate_codes_tl;
+ huft_t *inflate_codes_td;
+ unsigned inflate_codes_bl;
+ unsigned inflate_codes_bd;
+ unsigned inflate_codes_nn; /* length and index for copy */
+ unsigned inflate_codes_dd;
+ smallint resume_copy;
+
+ /* private data of inflate_get_next_window() */
+ smallint method; /* Method == -1 for stored, -2 for codes */
+ smallint need_another_block;
+ smallint end_reached;
+
+ /* private data of inflate_stored() */
+ unsigned inflate_stored_n;
+ unsigned inflate_stored_b;
+ unsigned inflate_stored_k;
+ unsigned inflate_stored_w;
+} state_t;
+#define gunzip_bytes_out (S()gunzip_bytes_out )
+#define gunzip_crc (S()gunzip_crc )
+#define gunzip_src_fd (S()gunzip_src_fd )
+#define gunzip_outbuf_count (S()gunzip_outbuf_count)
+#define gunzip_window (S()gunzip_window )
+#define gunzip_crc_table (S()gunzip_crc_table )
+#define gunzip_bb (S()gunzip_bb )
+#define gunzip_bk (S()gunzip_bk )
+#define bytebuffer_max (S()bytebuffer_max )
+#define bytebuffer (S()bytebuffer )
+#define bytebuffer_offset (S()bytebuffer_offset )
+#define bytebuffer_size (S()bytebuffer_size )
+#define inflate_codes_ml (S()inflate_codes_ml )
+#define inflate_codes_md (S()inflate_codes_md )
+#define inflate_codes_bb (S()inflate_codes_bb )
+#define inflate_codes_k (S()inflate_codes_k )
+#define inflate_codes_w (S()inflate_codes_w )
+#define inflate_codes_tl (S()inflate_codes_tl )
+#define inflate_codes_td (S()inflate_codes_td )
+#define inflate_codes_bl (S()inflate_codes_bl )
+#define inflate_codes_bd (S()inflate_codes_bd )
+#define inflate_codes_nn (S()inflate_codes_nn )
+#define inflate_codes_dd (S()inflate_codes_dd )
+#define resume_copy (S()resume_copy )
+#define method (S()method )
+#define need_another_block (S()need_another_block )
+#define end_reached (S()end_reached )
+#define inflate_stored_n (S()inflate_stored_n )
+#define inflate_stored_b (S()inflate_stored_b )
+#define inflate_stored_k (S()inflate_stored_k )
+#define inflate_stored_w (S()inflate_stored_w )
+#define INIT_STATE ({ bytebuffer_size = 0; method = -1; need_another_block = 1; })
+
+
+/* This is generic part */
+#if STATE_IN_BSS /* Use global data segment */
+#define DECLARE_STATE /*nothing*/
+#define ALLOC_STATE (init_state())
+#define DEALLOC_STATE ((void)0)
+#define S() state.
+#define PASS_STATE /*nothing*/
+#define PASS_STATE_ONLY /*nothing*/
+#define STATE_PARAM /*nothing*/
+#define STATE_PARAM_ONLY void
+static state_t state;
+static void init_state(void)
+{
+ INIT_STATE;
+}
+#endif
+
+#if STATE_IN_MALLOC /* Use malloc space */
+#define DECLARE_STATE state_t *state
+#define ALLOC_STATE (state = alloc_state())
+#define DEALLOC_STATE free(state)
+#define S() state->
+#define PASS_STATE state,
+#define PASS_STATE_ONLY state
+#define STATE_PARAM state_t *state,
+#define STATE_PARAM_ONLY state_t *state
+static state_t* alloc_state(void)
+{
+ state_t* state = xzalloc(sizeof(*state));
+ INIT_STATE;
+ return state;
+}
+#endif
+
+
+static const unsigned short mask_bits[] = {
+ 0x0000, 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff,
+ 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff
+};
+
+/* Copy lengths for literal codes 257..285 */
+static const unsigned short cplens[] = {
+ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59,
+ 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0
+};
+
+/* note: see note #13 above about the 258 in this list. */
+/* Extra bits for literal codes 257..285 */
+static const unsigned char cplext[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5,
+ 5, 5, 5, 0, 99, 99
+}; /* 99 == invalid */
+
+/* Copy offsets for distance codes 0..29 */
+static const unsigned short cpdist[] = {
+ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513,
+ 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577
+};
+
+/* Extra bits for distance codes */
+static const unsigned char cpdext[] = {
+ 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10,
+ 11, 11, 12, 12, 13, 13
+};
+
+/* Tables for deflate from PKZIP's appnote.txt. */
+/* Order of the bit length code lengths */
+static const unsigned char border[] = {
+ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
+};
+
+static unsigned fill_bitbuffer(STATE_PARAM unsigned bitbuffer, unsigned *current, const unsigned required)
+{
+ while (*current < required) {
+ if (bytebuffer_offset >= bytebuffer_size) {
+ /* Leave the first 4 bytes empty so we can always unwind the bitbuffer
+ * to the front of the bytebuffer, leave 4 bytes free at end of tail
+ * so we can easily top up buffer in check_trailer_gzip() */
+ bytebuffer_size = safe_read(gunzip_src_fd, &bytebuffer[4], bytebuffer_max - 8);
+ if (1 > bytebuffer_size)
+//shouldn't we propagate error?
+ bb_error_msg_and_die("unexpected end of file");
+ bytebuffer_size += 4;
+ bytebuffer_offset = 4;
+ }
+ bitbuffer |= ((unsigned) bytebuffer[bytebuffer_offset]) << *current;
+ bytebuffer_offset++;
+ *current += 8;
+ }
+ return bitbuffer;
+}
+
+/*
+ * Free the malloc'ed tables built by huft_build(), which makes a linked
+ * list of the tables it made, with the links in a dummy first entry of
+ * each table.
+ * t: table to free
+ */
+static void huft_free(huft_t * p)
+{
+ huft_t *q;
+
+ /* Go through linked list, freeing from the malloced (t[-1]) address. */
+ while (p) {
+ q = (--p)->v.t;
+ free(p);
+ p = q;
+ }
+}
+
+/* Given a list of code lengths and a maximum table size, make a set of
+ * tables to decode that set of codes. Return zero on success, one if
+ * the given code set is incomplete (the tables are still built in this
+ * case), two if the input is invalid (all zero length codes or an
+ * oversubscribed set of lengths), and three if not enough memory.
+ *
+ * b: code lengths in bits (all assumed <= BMAX)
+ * n: number of codes (assumed <= N_MAX)
+ * s: number of simple-valued codes (0..s-1)
+ * d: list of base values for non-simple codes
+ * e: list of extra bits for non-simple codes
+ * t: result: starting table
+ * m: maximum lookup bits, returns actual
+ */
+static int huft_build(unsigned *b, const unsigned n,
+ const unsigned s, const unsigned short *d,
+ const unsigned char *e, huft_t ** t, unsigned *m)
+{
+ unsigned a; /* counter for codes of length k */
+ unsigned c[BMAX + 1]; /* bit length count table */
+ unsigned eob_len; /* length of end-of-block code (value 256) */
+ unsigned f; /* i repeats in table every f entries */
+ int g; /* maximum code length */
+ int htl; /* table level */
+ unsigned i; /* counter, current code */
+ unsigned j; /* counter */
+ int k; /* number of bits in current code */
+ unsigned *p; /* pointer into c[], b[], or v[] */
+ huft_t *q; /* points to current table */
+ huft_t r; /* table entry for structure assignment */
+ huft_t *u[BMAX]; /* table stack */
+ unsigned v[N_MAX]; /* values in order of bit length */
+ int ws[BMAX+1]; /* bits decoded stack */
+ int w; /* bits decoded */
+ unsigned x[BMAX + 1]; /* bit offsets, then code stack */
+ unsigned *xp; /* pointer into x */
+ int y; /* number of dummy codes added */
+ unsigned z; /* number of entries in current table */
+
+ /* Length of EOB code, if any */
+ eob_len = n > 256 ? b[256] : BMAX;
+
+ /* Generate counts for each bit length */
+ memset(c, 0, sizeof(c));
+ p = b;
+ i = n;
+ do {
+ c[*p]++; /* assume all entries <= BMAX */
+ p++; /* Can't combine with above line (Solaris bug) */
+ } while (--i);
+ if (c[0] == n) { /* null input--all zero length codes */
+ *t = NULL;
+ *m = 0;
+ return 2;
+ }
+
+ /* Find minimum and maximum length, bound *m by those */
+ for (j = 1; (c[j] == 0) && (j <= BMAX); j++);
+ k = j; /* minimum code length */
+ for (i = BMAX; (c[i] == 0) && i; i--);
+ g = i; /* maximum code length */
+ *m = (*m < j) ? j : ((*m > i) ? i : *m);
+
+ /* Adjust last length count to fill out codes, if needed */
+ for (y = 1 << j; j < i; j++, y <<= 1) {
+ y -= c[j];
+ if (y < 0) {
+ return 2; /* bad input: more codes than bits */
+ }
+ }
+ y -= c[i];
+ if (y < 0) {
+ return 2;
+ }
+ c[i] += y;
+
+ /* Generate starting offsets into the value table for each length */
+ x[1] = j = 0;
+ p = c + 1;
+ xp = x + 2;
+ while (--i) { /* note that i == g from above */
+ j += *p++;
+ *xp++ = j;
+ }
+
+ /* Make a table of values in order of bit lengths */
+ p = b;
+ i = 0;
+ do {
+ j = *p++;
+ if (j != 0) {
+ v[x[j]++] = i;
+ }
+ } while (++i < n);
+
+ /* Generate the Huffman codes and for each, make the table entries */
+ x[0] = i = 0; /* first Huffman code is zero */
+ p = v; /* grab values in bit order */
+ htl = -1; /* no tables yet--level -1 */
+ w = ws[0] = 0; /* bits decoded */
+ u[0] = NULL; /* just to keep compilers happy */
+ q = NULL; /* ditto */
+ z = 0; /* ditto */
+
+ /* go through the bit lengths (k already is bits in shortest code) */
+ for (; k <= g; k++) {
+ a = c[k];
+ while (a--) {
+ /* here i is the Huffman code of length k bits for value *p */
+ /* make tables up to required level */
+ while (k > ws[htl + 1]) {
+ w = ws[++htl];
+
+ /* compute minimum size table less than or equal to *m bits */
+ z = g - w;
+ z = z > *m ? *m : z; /* upper limit on table size */
+ j = k - w;
+ f = 1 << j;
+ if (f > a + 1) { /* try a k-w bit table */
+ /* too few codes for k-w bit table */
+ f -= a + 1; /* deduct codes from patterns left */
+ xp = c + k;
+ while (++j < z) { /* try smaller tables up to z bits */
+ f <<= 1;
+ if (f <= *++xp) {
+ break; /* enough codes to use up j bits */
+ }
+ f -= *xp; /* else deduct codes from patterns */
+ }
+ }
+ j = (w + j > eob_len && w < eob_len) ? eob_len - w : j; /* make EOB code end at table */
+ z = 1 << j; /* table entries for j-bit table */
+ ws[htl+1] = w + j; /* set bits decoded in stack */
+
+ /* allocate and link in new table */
+ q = xzalloc((z + 1) * sizeof(huft_t));
+ *t = q + 1; /* link to list for huft_free() */
+ t = &(q->v.t);
+ u[htl] = ++q; /* table starts after link */
+
+ /* connect to last table, if there is one */
+ if (htl) {
+ x[htl] = i; /* save pattern for backing up */
+ r.b = (unsigned char) (w - ws[htl - 1]); /* bits to dump before this table */
+ r.e = (unsigned char) (16 + j); /* bits in this table */
+ r.v.t = q; /* pointer to this table */
+ j = (i & ((1 << w) - 1)) >> ws[htl - 1];
+ u[htl - 1][j] = r; /* connect to last table */
+ }
+ }
+
+ /* set up table entry in r */
+ r.b = (unsigned char) (k - w);
+ if (p >= v + n) {
+ r.e = 99; /* out of values--invalid code */
+ } else if (*p < s) {
+ r.e = (unsigned char) (*p < 256 ? 16 : 15); /* 256 is EOB code */
+ r.v.n = (unsigned short) (*p++); /* simple code is just the value */
+ } else {
+ r.e = (unsigned char) e[*p - s]; /* non-simple--look up in lists */
+ r.v.n = d[*p++ - s];
+ }
+
+ /* fill code-like entries with r */
+ f = 1 << (k - w);
+ for (j = i >> w; j < z; j += f) {
+ q[j] = r;
+ }
+
+ /* backwards increment the k-bit code i */
+ for (j = 1 << (k - 1); i & j; j >>= 1) {
+ i ^= j;
+ }
+ i ^= j;
+
+ /* backup over finished tables */
+ while ((i & ((1 << w) - 1)) != x[htl]) {
+ w = ws[--htl];
+ }
+ }
+ }
+
+ /* return actual size of base table */
+ *m = ws[1];
+
+ /* Return true (1) if we were given an incomplete table */
+ return y != 0 && g != 1;
+}
+
+
+/*
+ * inflate (decompress) the codes in a deflated (compressed) block.
+ * Return an error code or zero if it all goes ok.
+ *
+ * tl, td: literal/length and distance decoder tables
+ * bl, bd: number of bits decoded by tl[] and td[]
+ */
+/* called once from inflate_block */
+
+/* map formerly local static variables to globals */
+#define ml inflate_codes_ml
+#define md inflate_codes_md
+#define bb inflate_codes_bb
+#define k inflate_codes_k
+#define w inflate_codes_w
+#define tl inflate_codes_tl
+#define td inflate_codes_td
+#define bl inflate_codes_bl
+#define bd inflate_codes_bd
+#define nn inflate_codes_nn
+#define dd inflate_codes_dd
+static void inflate_codes_setup(STATE_PARAM huft_t * my_tl, huft_t * my_td, const unsigned my_bl, const unsigned my_bd)
+{
+ tl = my_tl;
+ td = my_td;
+ bl = my_bl;
+ bd = my_bd;
+ /* make local copies of globals */
+ bb = gunzip_bb; /* initialize bit buffer */
+ k = gunzip_bk;
+ w = gunzip_outbuf_count; /* initialize gunzip_window position */
+ /* inflate the coded data */
+ ml = mask_bits[bl]; /* precompute masks for speed */
+ md = mask_bits[bd];
+}
+/* called once from inflate_get_next_window */
+static int inflate_codes(STATE_PARAM_ONLY)
+{
+ unsigned e; /* table entry flag/number of extra bits */
+ huft_t *t; /* pointer to table entry */
+
+ if (resume_copy) goto do_copy;
+
+ while (1) { /* do until end of block */
+ bb = fill_bitbuffer(PASS_STATE bb, &k, bl);
+ t = tl + ((unsigned) bb & ml);
+ e = t->e;
+ if (e > 16)
+ do {
+ if (e == 99) {
+//shouldn't we propagate error?
+ bb_error_msg_and_die("inflate_codes error 1");
+ }
+ bb >>= t->b;
+ k -= t->b;
+ e -= 16;
+ bb = fill_bitbuffer(PASS_STATE bb, &k, e);
+ t = t->v.t + ((unsigned) bb & mask_bits[e]);
+ e = t->e;
+ } while (e > 16);
+ bb >>= t->b;
+ k -= t->b;
+ if (e == 16) { /* then it's a literal */
+ gunzip_window[w++] = (unsigned char) t->v.n;
+ if (w == GUNZIP_WSIZE) {
+ gunzip_outbuf_count = w;
+ //flush_gunzip_window();
+ w = 0;
+ return 1; // We have a block to read
+ }
+ } else { /* it's an EOB or a length */
+ /* exit if end of block */
+ if (e == 15) {
+ break;
+ }
+
+ /* get length of block to copy */
+ bb = fill_bitbuffer(PASS_STATE bb, &k, e);
+ nn = t->v.n + ((unsigned) bb & mask_bits[e]);
+ bb >>= e;
+ k -= e;
+
+ /* decode distance of block to copy */
+ bb = fill_bitbuffer(PASS_STATE bb, &k, bd);
+ t = td + ((unsigned) bb & md);
+ e = t->e;
+ if (e > 16)
+ do {
+ if (e == 99)
+//shouldn't we propagate error?
+ bb_error_msg_and_die("inflate_codes error 2");
+ bb >>= t->b;
+ k -= t->b;
+ e -= 16;
+ bb = fill_bitbuffer(PASS_STATE bb, &k, e);
+ t = t->v.t + ((unsigned) bb & mask_bits[e]);
+ e = t->e;
+ } while (e > 16);
+ bb >>= t->b;
+ k -= t->b;
+ bb = fill_bitbuffer(PASS_STATE bb, &k, e);
+ dd = w - t->v.n - ((unsigned) bb & mask_bits[e]);
+ bb >>= e;
+ k -= e;
+
+ /* do the copy */
+ do_copy:
+ do {
+ /* Was: nn -= (e = (e = GUNZIP_WSIZE - ((dd &= GUNZIP_WSIZE - 1) > w ? dd : w)) > nn ? nn : e); */
+ /* Who wrote THAT?? rewritten as: */
+ dd &= GUNZIP_WSIZE - 1;
+ e = GUNZIP_WSIZE - (dd > w ? dd : w);
+ if (e > nn) e = nn;
+ nn -= e;
+
+ /* copy to new buffer to prevent possible overwrite */
+ if (w - dd >= e) { /* (this test assumes unsigned comparison) */
+ memcpy(gunzip_window + w, gunzip_window + dd, e);
+ w += e;
+ dd += e;
+ } else {
+ /* do it slow to avoid memcpy() overlap */
+ /* !NOMEMCPY */
+ do {
+ gunzip_window[w++] = gunzip_window[dd++];
+ } while (--e);
+ }
+ if (w == GUNZIP_WSIZE) {
+ gunzip_outbuf_count = w;
+ resume_copy = (nn != 0);
+ //flush_gunzip_window();
+ w = 0;
+ return 1;
+ }
+ } while (nn);
+ resume_copy = 0;
+ }
+ }
+
+ /* restore the globals from the locals */
+ gunzip_outbuf_count = w; /* restore global gunzip_window pointer */
+ gunzip_bb = bb; /* restore global bit buffer */
+ gunzip_bk = k;
+
+ /* normally just after call to inflate_codes, but save code by putting it here */
+ /* free the decoding tables, return */
+ huft_free(tl);
+ huft_free(td);
+
+ /* done */
+ return 0;
+}
+#undef ml
+#undef md
+#undef bb
+#undef k
+#undef w
+#undef tl
+#undef td
+#undef bl
+#undef bd
+#undef nn
+#undef dd
+
+
+/* called once from inflate_block */
+static void inflate_stored_setup(STATE_PARAM int my_n, int my_b, int my_k)
+{
+ inflate_stored_n = my_n;
+ inflate_stored_b = my_b;
+ inflate_stored_k = my_k;
+ /* initialize gunzip_window position */
+ inflate_stored_w = gunzip_outbuf_count;
+}
+/* called once from inflate_get_next_window */
+static int inflate_stored(STATE_PARAM_ONLY)
+{
+ /* read and output the compressed data */
+ while (inflate_stored_n--) {
+ inflate_stored_b = fill_bitbuffer(PASS_STATE inflate_stored_b, &inflate_stored_k, 8);
+ gunzip_window[inflate_stored_w++] = (unsigned char) inflate_stored_b;
+ if (inflate_stored_w == GUNZIP_WSIZE) {
+ gunzip_outbuf_count = inflate_stored_w;
+ //flush_gunzip_window();
+ inflate_stored_w = 0;
+ inflate_stored_b >>= 8;
+ inflate_stored_k -= 8;
+ return 1; // We have a block
+ }
+ inflate_stored_b >>= 8;
+ inflate_stored_k -= 8;
+ }
+
+ /* restore the globals from the locals */
+ gunzip_outbuf_count = inflate_stored_w; /* restore global gunzip_window pointer */
+ gunzip_bb = inflate_stored_b; /* restore global bit buffer */
+ gunzip_bk = inflate_stored_k;
+ return 0; // Finished
+}
+
+
+/*
+ * decompress an inflated block
+ * e: last block flag
+ *
+ * GLOBAL VARIABLES: bb, kk,
+ */
+/* Return values: -1 = inflate_stored, -2 = inflate_codes */
+/* One callsite in inflate_get_next_window */
+static int inflate_block(STATE_PARAM smallint *e)
+{
+ unsigned t; /* block type */
+ unsigned b; /* bit buffer */
+ unsigned k; /* number of bits in bit buffer */
+
+ /* make local bit buffer */
+
+ b = gunzip_bb;
+ k = gunzip_bk;
+
+ /* read in last block bit */
+ b = fill_bitbuffer(PASS_STATE b, &k, 1);
+ *e = b & 1;
+ b >>= 1;
+ k -= 1;
+
+ /* read in block type */
+ b = fill_bitbuffer(PASS_STATE b, &k, 2);
+ t = (unsigned) b & 3;
+ b >>= 2;
+ k -= 2;
+
+ /* restore the global bit buffer */
+ gunzip_bb = b;
+ gunzip_bk = k;
+
+ /* inflate that block type */
+ switch (t) {
+ case 0: /* Inflate stored */
+ {
+ unsigned n; /* number of bytes in block */
+ unsigned b_stored; /* bit buffer */
+ unsigned k_stored; /* number of bits in bit buffer */
+
+ /* make local copies of globals */
+ b_stored = gunzip_bb; /* initialize bit buffer */
+ k_stored = gunzip_bk;
+
+ /* go to byte boundary */
+ n = k_stored & 7;
+ b_stored >>= n;
+ k_stored -= n;
+
+ /* get the length and its complement */
+ b_stored = fill_bitbuffer(PASS_STATE b_stored, &k_stored, 16);
+ n = ((unsigned) b_stored & 0xffff);
+ b_stored >>= 16;
+ k_stored -= 16;
+
+ b_stored = fill_bitbuffer(PASS_STATE b_stored, &k_stored, 16);
+ if (n != (unsigned) ((~b_stored) & 0xffff)) {
+ return 1; /* error in compressed data */
+ }
+ b_stored >>= 16;
+ k_stored -= 16;
+
+ inflate_stored_setup(PASS_STATE n, b_stored, k_stored); // Setup inflate_stored
+
+ return -1;
+ }
+ case 1:
+ /* Inflate fixed
+ * decompress an inflated type 1 (fixed Huffman codes) block. We should
+ * either replace this with a custom decoder, or at least precompute the
+ * Huffman tables. */
+ {
+ int i; /* temporary variable */
+ huft_t *tl; /* literal/length code table */
+ huft_t *td; /* distance code table */
+ unsigned bl; /* lookup bits for tl */
+ unsigned bd; /* lookup bits for td */
+ unsigned l[288]; /* length list for huft_build */
+
+ /* set up literal table */
+ for (i = 0; i < 144; i++) {
+ l[i] = 8;
+ }
+ for (; i < 256; i++) {
+ l[i] = 9;
+ }
+ for (; i < 280; i++) {
+ l[i] = 7;
+ }
+ for (; i < 288; i++) { /* make a complete, but wrong code set */
+ l[i] = 8;
+ }
+ bl = 7;
+ i = huft_build(l, 288, 257, cplens, cplext, &tl, &bl);
+ if (i != 0) {
+ return i;
+ }
+
+ /* set up distance table */
+ for (i = 0; i < 30; i++) { /* make an incomplete code set */
+ l[i] = 5;
+ }
+ bd = 5;
+ i = huft_build(l, 30, 0, cpdist, cpdext, &td, &bd);
+ if (i > 1) {
+ huft_free(tl);
+ return i;
+ }
+
+ /* decompress until an end-of-block code */
+ inflate_codes_setup(PASS_STATE tl, td, bl, bd); // Setup inflate_codes
+
+ /* huft_free code moved into inflate_codes */
+
+ return -2;
+ }
+ case 2: /* Inflate dynamic */
+ {
+ const int dbits = 6; /* bits in base distance lookup table */
+ const int lbits = 9; /* bits in base literal/length lookup table */
+
+ huft_t *tl; /* literal/length code table */
+ huft_t *td; /* distance code table */
+ unsigned i; /* temporary variables */
+ unsigned j;
+ unsigned l; /* last length */
+ unsigned m; /* mask for bit lengths table */
+ unsigned n; /* number of lengths to get */
+ unsigned bl; /* lookup bits for tl */
+ unsigned bd; /* lookup bits for td */
+ unsigned nb; /* number of bit length codes */
+ unsigned nl; /* number of literal/length codes */
+ unsigned nd; /* number of distance codes */
+
+ unsigned ll[286 + 30]; /* literal/length and distance code lengths */
+ unsigned b_dynamic; /* bit buffer */
+ unsigned k_dynamic; /* number of bits in bit buffer */
+
+ /* make local bit buffer */
+ b_dynamic = gunzip_bb;
+ k_dynamic = gunzip_bk;
+
+ /* read in table lengths */
+ b_dynamic = fill_bitbuffer(PASS_STATE b_dynamic, &k_dynamic, 5);
+ nl = 257 + ((unsigned) b_dynamic & 0x1f); /* number of literal/length codes */
+
+ b_dynamic >>= 5;
+ k_dynamic -= 5;
+ b_dynamic = fill_bitbuffer(PASS_STATE b_dynamic, &k_dynamic, 5);
+ nd = 1 + ((unsigned) b_dynamic & 0x1f); /* number of distance codes */
+
+ b_dynamic >>= 5;
+ k_dynamic -= 5;
+ b_dynamic = fill_bitbuffer(PASS_STATE b_dynamic, &k_dynamic, 4);
+ nb = 4 + ((unsigned) b_dynamic & 0xf); /* number of bit length codes */
+
+ b_dynamic >>= 4;
+ k_dynamic -= 4;
+ if (nl > 286 || nd > 30) {
+ return 1; /* bad lengths */
+ }
+
+ /* read in bit-length-code lengths */
+ for (j = 0; j < nb; j++) {
+ b_dynamic = fill_bitbuffer(PASS_STATE b_dynamic, &k_dynamic, 3);
+ ll[border[j]] = (unsigned) b_dynamic & 7;
+ b_dynamic >>= 3;
+ k_dynamic -= 3;
+ }
+ for (; j < 19; j++) {
+ ll[border[j]] = 0;
+ }
+
+ /* build decoding table for trees--single level, 7 bit lookup */
+ bl = 7;
+ i = huft_build(ll, 19, 19, NULL, NULL, &tl, &bl);
+ if (i != 0) {
+ if (i == 1) {
+ huft_free(tl);
+ }
+ return i; /* incomplete code set */
+ }
+
+ /* read in literal and distance code lengths */
+ n = nl + nd;
+ m = mask_bits[bl];
+ i = l = 0;
+ while ((unsigned) i < n) {
+ b_dynamic = fill_bitbuffer(PASS_STATE b_dynamic, &k_dynamic, (unsigned)bl);
+ j = (td = tl + ((unsigned) b_dynamic & m))->b;
+ b_dynamic >>= j;
+ k_dynamic -= j;
+ j = td->v.n;
+ if (j < 16) { /* length of code in bits (0..15) */
+ ll[i++] = l = j; /* save last length in l */
+ } else if (j == 16) { /* repeat last length 3 to 6 times */
+ b_dynamic = fill_bitbuffer(PASS_STATE b_dynamic, &k_dynamic, 2);
+ j = 3 + ((unsigned) b_dynamic & 3);
+ b_dynamic >>= 2;
+ k_dynamic -= 2;
+ if ((unsigned) i + j > n) {
+ return 1;
+ }
+ while (j--) {
+ ll[i++] = l;
+ }
+ } else if (j == 17) { /* 3 to 10 zero length codes */
+ b_dynamic = fill_bitbuffer(PASS_STATE b_dynamic, &k_dynamic, 3);
+ j = 3 + ((unsigned) b_dynamic & 7);
+ b_dynamic >>= 3;
+ k_dynamic -= 3;
+ if ((unsigned) i + j > n) {
+ return 1;
+ }
+ while (j--) {
+ ll[i++] = 0;
+ }
+ l = 0;
+ } else { /* j == 18: 11 to 138 zero length codes */
+ b_dynamic = fill_bitbuffer(PASS_STATE b_dynamic, &k_dynamic, 7);
+ j = 11 + ((unsigned) b_dynamic & 0x7f);
+ b_dynamic >>= 7;
+ k_dynamic -= 7;
+ if ((unsigned) i + j > n) {
+ return 1;
+ }
+ while (j--) {
+ ll[i++] = 0;
+ }
+ l = 0;
+ }
+ }
+
+ /* free decoding table for trees */
+ huft_free(tl);
+
+ /* restore the global bit buffer */
+ gunzip_bb = b_dynamic;
+ gunzip_bk = k_dynamic;
+
+ /* build the decoding tables for literal/length and distance codes */
+ bl = lbits;
+
+ i = huft_build(ll, nl, 257, cplens, cplext, &tl, &bl);
+ if (i != 0) {
+ if (i == 1) {
+//shouldn't we propagate error?
+ bb_error_msg_and_die("incomplete literal tree");
+ /* huft_free(tl); */
+ }
+ return i; /* incomplete code set */
+ }
+
+ bd = dbits;
+ i = huft_build(ll + nl, nd, 0, cpdist, cpdext, &td, &bd);
+ if (i != 0) {
+ if (i == 1) {
+//shouldn't we propagate error?
+ bb_error_msg_and_die("incomplete distance tree");
+ /* huft_free(td); */
+ }
+ huft_free(tl);
+ return i; /* incomplete code set */
+ }
+
+ /* decompress until an end-of-block code */
+ inflate_codes_setup(PASS_STATE tl, td, bl, bd); // Setup inflate_codes
+
+ /* huft_free code moved into inflate_codes */
+
+ return -2;
+ }
+ default:
+ /* bad block type */
+//shouldn't we propagate error?
+ bb_error_msg_and_die("bad block type %d", t);
+ }
+}
+
+/* Two callsites, both in inflate_get_next_window */
+static void calculate_gunzip_crc(STATE_PARAM_ONLY)
+{
+ int n;
+ for (n = 0; n < gunzip_outbuf_count; n++) {
+ gunzip_crc = gunzip_crc_table[((int) gunzip_crc ^ (gunzip_window[n])) & 0xff] ^ (gunzip_crc >> 8);
+ }
+ gunzip_bytes_out += gunzip_outbuf_count;
+}
+
+/* One callsite in inflate_unzip_internal */
+static int inflate_get_next_window(STATE_PARAM_ONLY)
+{
+ gunzip_outbuf_count = 0;
+
+ while (1) {
+ int ret;
+
+ if (need_another_block) {
+ if (end_reached) {
+ calculate_gunzip_crc(PASS_STATE_ONLY);
+ end_reached = 0;
+ need_another_block = 1;
+ return 0; /* Last block */
+ }
+ method = inflate_block(PASS_STATE &end_reached);
+ need_another_block = 0;
+ }
+
+ switch (method) {
+ case -1:
+ ret = inflate_stored(PASS_STATE_ONLY);
+ break;
+ case -2:
+ ret = inflate_codes(PASS_STATE_ONLY);
+ break;
+ default:
+//shouldn't we propagate error?
+ bb_error_msg_and_die("inflate error %d", method);
+ }
+
+ if (ret == 1) {
+ calculate_gunzip_crc(PASS_STATE_ONLY);
+ return 1; // More data left
+ }
+ need_another_block = 1; // End of that block
+ }
+ /* Doesnt get here */
+}
+
+
+/* Called from inflate_gunzip() and inflate_unzip() */
+/* NB: bytebuffer is allocated here but freeing it is left to the caller! */
+static USE_DESKTOP(long long) int
+inflate_unzip_internal(STATE_PARAM int in, int out)
+{
+ USE_DESKTOP(long long) int n = 0;
+ ssize_t nwrote;
+
+ /* Allocate all global buffers (for DYN_ALLOC option) */
+ gunzip_window = xmalloc(GUNZIP_WSIZE);
+ gunzip_outbuf_count = 0;
+ gunzip_bytes_out = 0;
+ gunzip_src_fd = in;
+
+ /* initialize gunzip_window, bit buffer */
+ gunzip_bk = 0;
+ gunzip_bb = 0;
+
+ /* Create the crc table */
+ gunzip_crc_table = crc32_filltable(0);
+ gunzip_crc = ~0;
+
+ /* Allocate space for buffer */
+ bytebuffer = xmalloc(bytebuffer_max);
+
+ while (1) {
+ int r = inflate_get_next_window(PASS_STATE_ONLY);
+ nwrote = full_write(out, gunzip_window, gunzip_outbuf_count);
+ if (nwrote != gunzip_outbuf_count) {
+ bb_perror_msg("write");
+ n = -1;
+ goto ret;
+ }
+ USE_DESKTOP(n += nwrote;)
+ if (r == 0) break;
+ }
+
+ /* Store unused bytes in a global buffer so calling applets can access it */
+ if (gunzip_bk >= 8) {
+ /* Undo too much lookahead. The next read will be byte aligned
+ * so we can discard unused bits in the last meaningful byte. */
+ bytebuffer_offset--;
+ bytebuffer[bytebuffer_offset] = gunzip_bb & 0xff;
+ gunzip_bb >>= 8;
+ gunzip_bk -= 8;
+ }
+ ret:
+ /* Cleanup */
+ free(gunzip_window);
+ free(gunzip_crc_table);
+ return n;
+}
+
+
+USE_DESKTOP(long long) int
+inflate_unzip(inflate_unzip_result *res, unsigned bufsize, int in, int out)
+{
+ USE_DESKTOP(long long) int n;
+ DECLARE_STATE;
+
+ ALLOC_STATE;
+
+ bytebuffer_max = bufsize + 8;
+ bytebuffer_offset = 4;
+ n = inflate_unzip_internal(PASS_STATE in, out);
+
+ res->crc = gunzip_crc;
+ res->bytes_out = gunzip_bytes_out;
+ free(bytebuffer);
+ DEALLOC_STATE;
+ return n;
+}
+
+
+USE_DESKTOP(long long) int
+inflate_gunzip(int in, int out)
+{
+ uint32_t stored_crc = 0;
+ unsigned count;
+ USE_DESKTOP(long long) int n;
+ DECLARE_STATE;
+
+ ALLOC_STATE;
+
+ bytebuffer_max = 0x8000;
+ n = inflate_unzip_internal(PASS_STATE in, out);
+
+ if (n < 0) goto ret;
+
+ /* top up the input buffer with the rest of the trailer */
+ count = bytebuffer_size - bytebuffer_offset;
+ if (count < 8) {
+ xread(in, &bytebuffer[bytebuffer_size], 8 - count);
+//shouldn't we propagate error?
+ bytebuffer_size += 8 - count;
+ }
+ for (count = 0; count != 4; count++) {
+ stored_crc |= (bytebuffer[bytebuffer_offset] << (count * 8));
+ bytebuffer_offset++;
+ }
+
+ /* Validate decompression - crc */
+ if (stored_crc != (~gunzip_crc)) {
+ bb_error_msg("crc error");
+ n = -1;
+ goto ret;
+ }
+
+ /* Validate decompression - size */
+ if (gunzip_bytes_out !=
+ (bytebuffer[bytebuffer_offset] | (bytebuffer[bytebuffer_offset+1] << 8) |
+ (bytebuffer[bytebuffer_offset+2] << 16) | (bytebuffer[bytebuffer_offset+3] << 24))
+ ) {
+ bb_error_msg("incorrect length");
+ n = -1;
+ }
+ ret:
+ free(bytebuffer);
+ DEALLOC_STATE;
+ return n;
+}
diff --git a/i/pc104/initrd/conf/busybox/archival/libunarchive/filter_accept_all.c b/i/pc104/initrd/conf/busybox/archival/libunarchive/filter_accept_all.c
new file mode 100644
index 0000000..5c991ca
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/archival/libunarchive/filter_accept_all.c
@@ -0,0 +1,17 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Copyright (C) 2002 by Glenn McGrath
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include <stdlib.h>
+#include "unarchive.h"
+
+/* Accept any non-null name, its not really a filter at all */
+char filter_accept_all(archive_handle_t *archive_handle)
+{
+ if (archive_handle->file_header->name)
+ return EXIT_SUCCESS;
+ return EXIT_FAILURE;
+}
diff --git a/i/pc104/initrd/conf/busybox/archival/libunarchive/filter_accept_list.c b/i/pc104/initrd/conf/busybox/archival/libunarchive/filter_accept_list.c
new file mode 100644
index 0000000..cfc1b0c
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/archival/libunarchive/filter_accept_list.c
@@ -0,0 +1,19 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Copyright (C) 2002 by Glenn McGrath
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include <stdlib.h>
+#include "unarchive.h"
+
+/*
+ * Accept names that are in the accept list, ignoring reject list.
+ */
+char filter_accept_list(archive_handle_t *archive_handle)
+{
+ if (find_list_entry(archive_handle->accept, archive_handle->file_header->name))
+ return EXIT_SUCCESS;
+ return EXIT_FAILURE;
+}
diff --git a/i/pc104/initrd/conf/busybox/archival/libunarchive/filter_accept_list_reassign.c b/i/pc104/initrd/conf/busybox/archival/libunarchive/filter_accept_list_reassign.c
new file mode 100644
index 0000000..0fb536f
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/archival/libunarchive/filter_accept_list_reassign.c
@@ -0,0 +1,44 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Copyright (C) 2002 by Glenn McGrath
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "libbb.h"
+#include "unarchive.h"
+
+/*
+ * Reassign the subarchive metadata parser based on the filename extension
+ * e.g. if its a .tar.gz modify archive_handle->sub_archive to process a .tar.gz
+ * or if its a .tar.bz2 make archive_handle->sub_archive handle that
+ */
+char filter_accept_list_reassign(archive_handle_t *archive_handle)
+{
+ /* Check the file entry is in the accept list */
+ if (find_list_entry(archive_handle->accept, archive_handle->file_header->name)) {
+ const char *name_ptr;
+
+ /* Extract the last 2 extensions */
+ name_ptr = strrchr(archive_handle->file_header->name, '.');
+
+ /* Modify the subarchive handler based on the extension */
+#ifdef CONFIG_FEATURE_DEB_TAR_GZ
+ if (strcmp(name_ptr, ".gz") == 0) {
+ archive_handle->action_data_subarchive = get_header_tar_gz;
+ return EXIT_SUCCESS;
+ }
+#endif
+#ifdef CONFIG_FEATURE_DEB_TAR_BZ2
+ if (strcmp(name_ptr, ".bz2") == 0) {
+ archive_handle->action_data_subarchive = get_header_tar_bz2;
+ return EXIT_SUCCESS;
+ }
+#endif
+ if (ENABLE_FEATURE_DEB_TAR_LZMA && !strcmp(name_ptr, ".lzma")) {
+ archive_handle->action_data_subarchive = get_header_tar_lzma;
+ return EXIT_SUCCESS;
+ }
+ }
+ return EXIT_FAILURE;
+}
diff --git a/i/pc104/initrd/conf/busybox/archival/libunarchive/filter_accept_reject_list.c b/i/pc104/initrd/conf/busybox/archival/libunarchive/filter_accept_reject_list.c
new file mode 100644
index 0000000..79da350
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/archival/libunarchive/filter_accept_reject_list.c
@@ -0,0 +1,33 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Copyright (C) 2002 by Glenn McGrath
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include <stdlib.h>
+#include "unarchive.h"
+
+/*
+ * Accept names that are in the accept list and not in the reject list
+ */
+char filter_accept_reject_list(archive_handle_t *archive_handle)
+{
+ const char *key = archive_handle->file_header->name;
+ const llist_t *reject_entry = find_list_entry2(archive_handle->reject, key);
+ const llist_t *accept_entry;
+
+ /* If the key is in a reject list fail */
+ if (reject_entry) {
+ return EXIT_FAILURE;
+ }
+ accept_entry = find_list_entry2(archive_handle->accept, key);
+
+ /* Fail if an accept list was specified and the key wasnt in there */
+ if ((accept_entry == NULL) && archive_handle->accept) {
+ return EXIT_FAILURE;
+ }
+
+ /* Accepted */
+ return EXIT_SUCCESS;
+}
diff --git a/i/pc104/initrd/conf/busybox/archival/libunarchive/find_list_entry.c b/i/pc104/initrd/conf/busybox/archival/libunarchive/find_list_entry.c
new file mode 100644
index 0000000..d1afc72
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/archival/libunarchive/find_list_entry.c
@@ -0,0 +1,54 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Copyright (C) 2002 by Glenn McGrath
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include <fnmatch.h>
+#include <stdlib.h>
+#include "unarchive.h"
+
+/* Find a string in a shell pattern list */
+const llist_t *find_list_entry(const llist_t *list, const char *filename)
+{
+ while (list) {
+ if (fnmatch(list->data, filename, 0) == 0) {
+ return list;
+ }
+ list = list->link;
+ }
+ return NULL;
+}
+
+/* Same, but compares only path components present in pattern
+ * (extra trailing path components in filename are assumed to match)
+ */
+const llist_t *find_list_entry2(const llist_t *list, const char *filename)
+{
+ char buf[PATH_MAX];
+ int pattern_slash_cnt;
+ const char *c;
+ char *d;
+
+ while (list) {
+ c = list->data;
+ pattern_slash_cnt = 0;
+ while (*c)
+ if (*c++ == '/') pattern_slash_cnt++;
+ c = filename;
+ d = buf;
+ /* paranoia is better that buffer overflows */
+ while (*c && d != buf + sizeof(buf)-1) {
+ if (*c == '/' && --pattern_slash_cnt < 0)
+ break;
+ *d++ = *c++;
+ }
+ *d = '\0';
+ if (fnmatch(list->data, buf, 0) == 0) {
+ return list;
+ }
+ list = list->link;
+ }
+ return NULL;
+}
diff --git a/i/pc104/initrd/conf/busybox/archival/libunarchive/get_header_ar.c b/i/pc104/initrd/conf/busybox/archival/libunarchive/get_header_ar.c
new file mode 100644
index 0000000..c691f1b
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/archival/libunarchive/get_header_ar.c
@@ -0,0 +1,126 @@
+/* vi: set sw=4 ts=4: */
+/* Copyright 2001 Glenn McGrath.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "libbb.h"
+#include "unarchive.h"
+
+char get_header_ar(archive_handle_t *archive_handle)
+{
+ int err;
+ file_header_t *typed = archive_handle->file_header;
+ union {
+ char raw[60];
+ struct {
+ char name[16];
+ char date[12];
+ char uid[6];
+ char gid[6];
+ char mode[8];
+ char size[10];
+ char magic[2];
+ } formatted;
+ } ar;
+#ifdef CONFIG_FEATURE_AR_LONG_FILENAMES
+ static char *ar_long_names;
+ static unsigned int ar_long_name_size;
+#endif
+
+ /* dont use xread as we want to handle the error ourself */
+ if (read(archive_handle->src_fd, ar.raw, 60) != 60) {
+ /* End Of File */
+ return EXIT_FAILURE;
+ }
+
+ /* ar header starts on an even byte (2 byte aligned)
+ * '\n' is used for padding
+ */
+ if (ar.raw[0] == '\n') {
+ /* fix up the header, we started reading 1 byte too early */
+ memmove(ar.raw, &ar.raw[1], 59);
+ ar.raw[59] = xread_char(archive_handle->src_fd);
+ archive_handle->offset++;
+ }
+ archive_handle->offset += 60;
+
+ /* align the headers based on the header magic */
+ if (ar.formatted.magic[0] != '`' || ar.formatted.magic[1] != '\n')
+ bb_error_msg_and_die("invalid ar header");
+
+ /* FIXME: more thorough routine would be in order here */
+ /* (we have something like that in tar) */
+ /* but for now we are lax. This code works because */
+ /* on misformatted numbers bb_strtou returns all-ones */
+ typed->mode = err = bb_strtou(ar.formatted.mode, NULL, 8);
+ if (err == -1) bb_error_msg_and_die("invalid ar header");
+ typed->mtime = err = bb_strtou(ar.formatted.date, NULL, 10);
+ if (err == -1) bb_error_msg_and_die("invalid ar header");
+ typed->uid = err = bb_strtou(ar.formatted.uid, NULL, 10);
+ if (err == -1) bb_error_msg_and_die("invalid ar header");
+ typed->gid = err = bb_strtou(ar.formatted.gid, NULL, 10);
+ if (err == -1) bb_error_msg_and_die("invalid ar header");
+ typed->size = err = bb_strtou(ar.formatted.size, NULL, 10);
+ if (err == -1) bb_error_msg_and_die("invalid ar header");
+
+ /* long filenames have '/' as the first character */
+ if (ar.formatted.name[0] == '/') {
+#if ENABLE_FEATURE_AR_LONG_FILENAMES
+ unsigned long_offset;
+
+ if (ar.formatted.name[1] == '/') {
+ /* If the second char is a '/' then this entries data section
+ * stores long filename for multiple entries, they are stored
+ * in static variable long_names for use in future entries */
+ ar_long_name_size = typed->size;
+ ar_long_names = xmalloc(ar_long_name_size);
+ xread(archive_handle->src_fd, ar_long_names, ar_long_name_size);
+ archive_handle->offset += ar_long_name_size;
+ /* This ar entries data section only contained filenames for other records
+ * they are stored in the static ar_long_names for future reference */
+ return get_header_ar(archive_handle); /* Return next header */
+ }
+
+ if (ar.formatted.name[1] == ' ') {
+ /* This is the index of symbols in the file for compilers */
+ data_skip(archive_handle);
+ archive_handle->offset += typed->size;
+ return get_header_ar(archive_handle); /* Return next header */
+ }
+
+ /* The number after the '/' indicates the offset in the ar data section
+ * (saved in variable long_name) that conatains the real filename */
+ long_offset = atoi(&ar.formatted.name[1]);
+ if (long_offset >= ar_long_name_size) {
+ bb_error_msg_and_die("can't resolve long filename");
+ }
+ typed->name = xstrdup(ar_long_names + long_offset);
+#else
+ bb_error_msg_and_die("long filenames not supported");
+#endif
+ } else {
+ /* short filenames */
+ typed->name = xstrndup(ar.formatted.name, 16);
+ }
+
+ typed->name[strcspn(typed->name, " /")] = '\0';
+
+ if (archive_handle->filter(archive_handle) == EXIT_SUCCESS) {
+ archive_handle->action_header(typed);
+ if (archive_handle->sub_archive) {
+ while (archive_handle->action_data_subarchive(archive_handle->sub_archive) == EXIT_SUCCESS)
+ /* repeat */;
+ } else {
+ archive_handle->action_data(archive_handle);
+ }
+ } else {
+ data_skip(archive_handle);
+ }
+
+ archive_handle->offset += typed->size;
+ /* Set the file pointer to the correct spot, we may have been reading a compressed file */
+ lseek(archive_handle->src_fd, archive_handle->offset, SEEK_SET);
+
+ return EXIT_SUCCESS;
+}
diff --git a/i/pc104/initrd/conf/busybox/archival/libunarchive/get_header_cpio.c b/i/pc104/initrd/conf/busybox/archival/libunarchive/get_header_cpio.c
new file mode 100644
index 0000000..6fd1340
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/archival/libunarchive/get_header_cpio.c
@@ -0,0 +1,160 @@
+/* vi: set sw=4 ts=4: */
+/* Copyright 2002 Laurence Anderson
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "libbb.h"
+#include "unarchive.h"
+
+typedef struct hardlinks_s {
+ char *name;
+ int inode;
+ struct hardlinks_s *next;
+} hardlinks_t;
+
+char get_header_cpio(archive_handle_t *archive_handle)
+{
+ static hardlinks_t *saved_hardlinks = NULL;
+ static unsigned short pending_hardlinks = 0;
+ static int inode;
+ file_header_t *file_header = archive_handle->file_header;
+ char cpio_header[110];
+ int namesize;
+ char dummy[16];
+ int major, minor, nlink;
+
+ if (pending_hardlinks) { /* Deal with any pending hardlinks */
+ hardlinks_t *tmp, *oldtmp;
+
+ tmp = saved_hardlinks;
+ oldtmp = NULL;
+
+ file_header->link_name = file_header->name;
+ file_header->size = 0;
+
+ while (tmp) {
+ if (tmp->inode != inode) {
+ tmp = tmp->next;
+ continue;
+ }
+
+ file_header->name = tmp->name;
+
+ if (archive_handle->filter(archive_handle) == EXIT_SUCCESS) {
+ archive_handle->action_data(archive_handle);
+ archive_handle->action_header(archive_handle->file_header);
+ }
+
+ pending_hardlinks--;
+
+ oldtmp = tmp;
+ tmp = tmp->next;
+ free(oldtmp->name);
+ free(oldtmp);
+ if (oldtmp == saved_hardlinks)
+ saved_hardlinks = tmp;
+ }
+
+ file_header->name = file_header->link_name;
+
+ if (pending_hardlinks > 1) {
+ bb_error_msg("error resolving hardlink: archive made by GNU cpio 2.0-2.2?");
+ }
+
+ /* No more pending hardlinks, read next file entry */
+ pending_hardlinks = 0;
+ }
+
+ /* There can be padding before archive header */
+ data_align(archive_handle, 4);
+
+ if (archive_xread_all_eof(archive_handle, (unsigned char*)cpio_header, 110) == 0) {
+ return EXIT_FAILURE;
+ }
+ archive_handle->offset += 110;
+
+ if (strncmp(&cpio_header[0], "07070", 5) != 0
+ || (cpio_header[5] != '1' && cpio_header[5] != '2')
+ ) {
+ bb_error_msg_and_die("unsupported cpio format, use newc or crc");
+ }
+
+ {
+ unsigned long tmpsize;
+ sscanf(cpio_header, "%6c%8x%8x%8x%8x%8x%8lx%8lx%16c%8x%8x%8x%8c",
+ dummy, &inode, (unsigned int*)&file_header->mode,
+ (unsigned int*)&file_header->uid, (unsigned int*)&file_header->gid,
+ &nlink, &file_header->mtime, &tmpsize,
+ dummy, &major, &minor, &namesize, dummy);
+ file_header->size = tmpsize;
+ }
+
+ free(file_header->name);
+ file_header->name = xzalloc(namesize + 1);
+ /* Read in filename */
+ xread(archive_handle->src_fd, file_header->name, namesize);
+ archive_handle->offset += namesize;
+
+ /* Update offset amount and skip padding before file contents */
+ data_align(archive_handle, 4);
+
+ if (strcmp(file_header->name, "TRAILER!!!") == 0) {
+ /* Always round up */
+ printf("%d blocks\n", (int) (archive_handle->offset % 512 ?
+ archive_handle->offset / 512 + 1 :
+ archive_handle->offset / 512
+ ));
+ if (saved_hardlinks) { /* Bummer - we still have unresolved hardlinks */
+ hardlinks_t *tmp = saved_hardlinks;
+ hardlinks_t *oldtmp = NULL;
+ while (tmp) {
+ bb_error_msg("%s not created: cannot resolve hardlink", tmp->name);
+ oldtmp = tmp;
+ tmp = tmp->next;
+ free(oldtmp->name);
+ free(oldtmp);
+ }
+ saved_hardlinks = NULL;
+ pending_hardlinks = 0;
+ }
+ return EXIT_FAILURE;
+ }
+
+ if (S_ISLNK(file_header->mode)) {
+ file_header->link_name = xzalloc(file_header->size + 1);
+ xread(archive_handle->src_fd, file_header->link_name, file_header->size);
+ archive_handle->offset += file_header->size;
+ file_header->size = 0; /* Stop possible seeks in future */
+ } else {
+ file_header->link_name = NULL;
+ }
+ if (nlink > 1 && !S_ISDIR(file_header->mode)) {
+ if (file_header->size == 0) { /* Put file on a linked list for later */
+ hardlinks_t *new = xmalloc(sizeof(hardlinks_t));
+ new->next = saved_hardlinks;
+ new->inode = inode;
+ /* name current allocated, freed later */
+ new->name = file_header->name;
+ file_header->name = NULL;
+ saved_hardlinks = new;
+ return EXIT_SUCCESS; /* Skip this one */
+ }
+ /* Found the file with data in */
+ pending_hardlinks = nlink;
+ }
+ file_header->device = makedev(major, minor);
+
+ if (archive_handle->filter(archive_handle) == EXIT_SUCCESS) {
+ archive_handle->action_data(archive_handle);
+ archive_handle->action_header(archive_handle->file_header);
+ } else {
+ data_skip(archive_handle);
+ }
+
+ archive_handle->offset += file_header->size;
+
+ free(file_header->link_name);
+
+ return EXIT_SUCCESS;
+}
diff --git a/i/pc104/initrd/conf/busybox/archival/libunarchive/get_header_tar.c b/i/pc104/initrd/conf/busybox/archival/libunarchive/get_header_tar.c
new file mode 100644
index 0000000..b3efdec
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/archival/libunarchive/get_header_tar.c
@@ -0,0 +1,284 @@
+/* vi: set sw=4 ts=4: */
+/* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ *
+ * FIXME:
+ * In privileged mode if uname and gname map to a uid and gid then use the
+ * mapped value instead of the uid/gid values in tar header
+ *
+ * References:
+ * GNU tar and star man pages,
+ * Opengroup's ustar interchange format,
+ * http://www.opengroup.org/onlinepubs/007904975/utilities/pax.html
+ */
+
+#include "libbb.h"
+#include "unarchive.h"
+
+#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
+static char *longname;
+static char *linkname;
+#else
+enum {
+ longname = 0,
+ linkname = 0,
+};
+#endif
+
+/* NB: _DESTROYS_ str[len] character! */
+static unsigned long long getOctal(char *str, int len)
+{
+ unsigned long long v;
+ /* Actually, tar header allows leading spaces also.
+ * Oh well, we will be liberal and skip this...
+ * The only downside probably is that we allow "-123" too :)
+ if (*str < '0' || *str > '7')
+ bb_error_msg_and_die("corrupted octal value in tar header");
+ */
+ str[len] = '\0';
+ v = strtoull(str, &str, 8);
+ if (*str)
+ bb_error_msg_and_die("corrupted octal value in tar header");
+ return v;
+}
+#define GET_OCTAL(a) getOctal((a), sizeof(a))
+
+void BUG_tar_header_size(void);
+char get_header_tar(archive_handle_t *archive_handle)
+{
+ static int end;
+
+ file_header_t *file_header = archive_handle->file_header;
+ struct {
+ /* ustar header, Posix 1003.1 */
+ char name[100]; /* 0-99 */
+ char mode[8]; /* 100-107 */
+ char uid[8]; /* 108-115 */
+ char gid[8]; /* 116-123 */
+ char size[12]; /* 124-135 */
+ char mtime[12]; /* 136-147 */
+ char chksum[8]; /* 148-155 */
+ char typeflag; /* 156-156 */
+ char linkname[100]; /* 157-256 */
+ char magic[6]; /* 257-262 */
+ char version[2]; /* 263-264 */
+ char uname[32]; /* 265-296 */
+ char gname[32]; /* 297-328 */
+ char devmajor[8]; /* 329-336 */
+ char devminor[8]; /* 337-344 */
+ char prefix[155]; /* 345-499 */
+ char padding[12]; /* 500-512 */
+ } tar;
+ char *cp;
+ int i, sum_u, sum_s, sum;
+ int parse_names;
+
+ if (sizeof(tar) != 512)
+ BUG_tar_header_size();
+
+#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
+ again:
+#endif
+ /* Align header */
+ data_align(archive_handle, 512);
+
+ again_after_align:
+
+ xread(archive_handle->src_fd, &tar, 512);
+ archive_handle->offset += 512;
+
+ /* If there is no filename its an empty header */
+ if (tar.name[0] == 0) {
+ if (end) {
+ /* This is the second consecutive empty header! End of archive!
+ * Read until the end to empty the pipe from gz or bz2
+ */
+ while (full_read(archive_handle->src_fd, &tar, 512) == 512)
+ /* repeat */;
+ return EXIT_FAILURE;
+ }
+ end = 1;
+ return EXIT_SUCCESS;
+ }
+ end = 0;
+
+ /* Check header has valid magic, "ustar" is for the proper tar
+ * 0's are for the old tar format
+ */
+ if (strncmp(tar.magic, "ustar", 5) != 0) {
+#if ENABLE_FEATURE_TAR_OLDGNU_COMPATIBILITY
+ if (memcmp(tar.magic, "\0\0\0\0", 5) != 0)
+#endif
+ bb_error_msg_and_die("invalid tar magic");
+ }
+
+ /* Do checksum on headers.
+ * POSIX says that checksum is done on unsigned bytes, but
+ * Sun and HP-UX gets it wrong... more details in
+ * GNU tar source. */
+ sum_s = sum_u = ' ' * sizeof(tar.chksum);
+ for (i = 0; i < 148 ; i++) {
+ sum_u += ((unsigned char*)&tar)[i];
+ sum_s += ((signed char*)&tar)[i];
+ }
+ for (i = 156; i < 512 ; i++) {
+ sum_u += ((unsigned char*)&tar)[i];
+ sum_s += ((signed char*)&tar)[i];
+ }
+ /* This field does not need special treatment (getOctal) */
+ sum = xstrtoul(tar.chksum, 8);
+ if (sum_u != sum && sum_s != sum) {
+ bb_error_msg_and_die("invalid tar header checksum");
+ }
+
+ /* 0 is reserved for high perf file, treat as normal file */
+ if (!tar.typeflag) tar.typeflag = '0';
+ parse_names = (tar.typeflag >= '0' && tar.typeflag <= '7');
+
+ /* getOctal trashes subsequent field, therefore we call it
+ * on fields in reverse order */
+ if (tar.devmajor[0]) {
+ unsigned minor = GET_OCTAL(tar.devminor);
+ unsigned major = GET_OCTAL(tar.devmajor);
+ file_header->device = makedev(major, minor);
+ }
+ file_header->link_name = NULL;
+ if (!linkname && parse_names && tar.linkname[0]) {
+ /* we trash magic[0] here, it's ok */
+ tar.linkname[sizeof(tar.linkname)] = '\0';
+ file_header->link_name = xstrdup(tar.linkname);
+ /* FIXME: what if we have non-link object with link_name? */
+ /* Will link_name be free()ed? */
+ }
+ file_header->mtime = GET_OCTAL(tar.mtime);
+ file_header->size = GET_OCTAL(tar.size);
+ file_header->gid = GET_OCTAL(tar.gid);
+ file_header->uid = GET_OCTAL(tar.uid);
+ /* Set bits 0-11 of the files mode */
+ file_header->mode = 07777 & GET_OCTAL(tar.mode);
+
+ file_header->name = NULL;
+ if (!longname && parse_names) {
+ /* we trash mode[0] here, it's ok */
+ tar.name[sizeof(tar.name)] = '\0';
+ if (tar.prefix[0]) {
+ /* and padding[0] */
+ tar.prefix[sizeof(tar.prefix)] = '\0';
+ file_header->name = concat_path_file(tar.prefix, tar.name);
+ } else
+ file_header->name = xstrdup(tar.name);
+ }
+
+ /* Set bits 12-15 of the files mode */
+ /* (typeflag was not trashed because chksum does not use getOctal) */
+ switch (tar.typeflag) {
+ /* busybox identifies hard links as being regular files with 0 size and a link name */
+ case '1':
+ file_header->mode |= S_IFREG;
+ break;
+ case '7':
+ /* case 0: */
+ case '0':
+#if ENABLE_FEATURE_TAR_OLDGNU_COMPATIBILITY
+ if (last_char_is(file_header->name, '/')) {
+ file_header->mode |= S_IFDIR;
+ } else
+#endif
+ file_header->mode |= S_IFREG;
+ break;
+ case '2':
+ file_header->mode |= S_IFLNK;
+ break;
+ case '3':
+ file_header->mode |= S_IFCHR;
+ break;
+ case '4':
+ file_header->mode |= S_IFBLK;
+ break;
+ case '5':
+ file_header->mode |= S_IFDIR;
+ break;
+ case '6':
+ file_header->mode |= S_IFIFO;
+ break;
+#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
+ case 'L':
+ /* free: paranoia: tar with several consecutive longnames */
+ free(longname);
+ /* For paranoia reasons we allocate extra NUL char */
+ longname = xzalloc(file_header->size + 1);
+ /* We read ASCIZ string, including NUL */
+ xread(archive_handle->src_fd, longname, file_header->size);
+ archive_handle->offset += file_header->size;
+ /* return get_header_tar(archive_handle); */
+ /* gcc 4.1.1 didn't optimize it into jump */
+ /* so we will do it ourself, this also saves stack */
+ goto again;
+ case 'K':
+ free(linkname);
+ linkname = xzalloc(file_header->size + 1);
+ xread(archive_handle->src_fd, linkname, file_header->size);
+ archive_handle->offset += file_header->size;
+ /* return get_header_tar(archive_handle); */
+ goto again;
+ case 'D': /* GNU dump dir */
+ case 'M': /* Continuation of multi volume archive */
+ case 'N': /* Old GNU for names > 100 characters */
+ case 'S': /* Sparse file */
+ case 'V': /* Volume header */
+#endif
+ case 'g': /* pax global header */
+ case 'x': { /* pax extended header */
+ off_t sz;
+ bb_error_msg("warning: skipping header '%c'", tar.typeflag);
+ sz = (file_header->size + 511) & ~(off_t)511;
+ archive_handle->offset += sz;
+ sz >>= 9; /* sz /= 512 but w/o contortions for signed div */
+ while (sz--)
+ xread(archive_handle->src_fd, &tar, 512);
+ /* return get_header_tar(archive_handle); */
+ goto again_after_align;
+ }
+ default:
+ bb_error_msg_and_die("unknown typeflag: 0x%x", tar.typeflag);
+ }
+
+#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
+ if (longname) {
+ file_header->name = longname;
+ longname = NULL;
+ }
+ if (linkname) {
+ file_header->link_name = linkname;
+ linkname = NULL;
+ }
+#endif
+ if (!strncmp(file_header->name, "/../"+1, 3)
+ || strstr(file_header->name, "/../")
+ ) {
+ bb_error_msg_and_die("name with '..' encountered: '%s'",
+ file_header->name);
+ }
+
+ /* Strip trailing '/' in directories */
+ /* Must be done after mode is set as '/' is used to check if it's a directory */
+ cp = last_char_is(file_header->name, '/');
+
+ if (archive_handle->filter(archive_handle) == EXIT_SUCCESS) {
+ archive_handle->action_header(archive_handle->file_header);
+ /* Note that we kill the '/' only after action_header() */
+ /* (like GNU tar 1.15.1: verbose mode outputs "dir/dir/") */
+ if (cp) *cp = '\0';
+ archive_handle->flags |= ARCHIVE_EXTRACT_QUIET;
+ archive_handle->action_data(archive_handle);
+ llist_add_to(&(archive_handle->passed), file_header->name);
+ } else {
+ data_skip(archive_handle);
+ free(file_header->name);
+ }
+ archive_handle->offset += file_header->size;
+
+ free(file_header->link_name);
+ /* Do not free(file_header->name)! */
+
+ return EXIT_SUCCESS;
+}
diff --git a/i/pc104/initrd/conf/busybox/archival/libunarchive/get_header_tar_bz2.c b/i/pc104/initrd/conf/busybox/archival/libunarchive/get_header_tar_bz2.c
new file mode 100644
index 0000000..d8715c0
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/archival/libunarchive/get_header_tar_bz2.c
@@ -0,0 +1,20 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "libbb.h"
+#include "unarchive.h"
+
+char get_header_tar_bz2(archive_handle_t *archive_handle)
+{
+ /* Can't lseek over pipes */
+ archive_handle->seek = seek_by_read;
+
+ archive_handle->src_fd = open_transformer(archive_handle->src_fd, uncompressStream);
+ archive_handle->offset = 0;
+ while (get_header_tar(archive_handle) == EXIT_SUCCESS) /**/;
+
+ /* Can only do one file at a time */
+ return EXIT_FAILURE;
+}
diff --git a/i/pc104/initrd/conf/busybox/archival/libunarchive/get_header_tar_gz.c b/i/pc104/initrd/conf/busybox/archival/libunarchive/get_header_tar_gz.c
new file mode 100644
index 0000000..1f07e4e
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/archival/libunarchive/get_header_tar_gz.c
@@ -0,0 +1,31 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include <stdlib.h>
+
+#include "libbb.h"
+#include "unarchive.h"
+
+char get_header_tar_gz(archive_handle_t *archive_handle)
+{
+ unsigned char magic[2];
+
+ /* Can't lseek over pipes */
+ archive_handle->seek = seek_by_read;
+
+ xread(archive_handle->src_fd, &magic, 2);
+ if ((magic[0] != 0x1f) || (magic[1] != 0x8b)) {
+ bb_error_msg_and_die("invalid gzip magic");
+ }
+
+ check_header_gzip_or_die(archive_handle->src_fd);
+
+ archive_handle->src_fd = open_transformer(archive_handle->src_fd, inflate_gunzip);
+ archive_handle->offset = 0;
+ while (get_header_tar(archive_handle) == EXIT_SUCCESS) /**/;
+
+ /* Can only do one file at a time */
+ return EXIT_FAILURE;
+}
diff --git a/i/pc104/initrd/conf/busybox/archival/libunarchive/get_header_tar_lzma.c b/i/pc104/initrd/conf/busybox/archival/libunarchive/get_header_tar_lzma.c
new file mode 100644
index 0000000..06b8daa
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/archival/libunarchive/get_header_tar_lzma.c
@@ -0,0 +1,22 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Small lzma deflate implementation.
+ * Copyright (C) 2006 Aurelien Jacobs <aurel@gnuage.org>
+ *
+ * Licensed under GPL v2, see file LICENSE in this tarball for details.
+ */
+
+#include "unarchive.h"
+
+char get_header_tar_lzma(archive_handle_t * archive_handle)
+{
+ /* Can't lseek over pipes */
+ archive_handle->seek = seek_by_read;
+
+ archive_handle->src_fd = open_transformer(archive_handle->src_fd, unlzma);
+ archive_handle->offset = 0;
+ while (get_header_tar(archive_handle) == EXIT_SUCCESS) /**/;
+
+ /* Can only do one file at a time */
+ return EXIT_FAILURE;
+}
diff --git a/i/pc104/initrd/conf/busybox/archival/libunarchive/header_list.c b/i/pc104/initrd/conf/busybox/archival/libunarchive/header_list.c
new file mode 100644
index 0000000..fb461a6
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/archival/libunarchive/header_list.c
@@ -0,0 +1,11 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+#include <stdio.h>
+#include "unarchive.h"
+
+void header_list(const file_header_t *file_header)
+{
+ puts(file_header->name);
+}
diff --git a/i/pc104/initrd/conf/busybox/archival/libunarchive/header_skip.c b/i/pc104/initrd/conf/busybox/archival/libunarchive/header_skip.c
new file mode 100644
index 0000000..53242e0
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/archival/libunarchive/header_skip.c
@@ -0,0 +1,10 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+#include <stdio.h>
+#include "unarchive.h"
+
+void header_skip(const file_header_t *file_header ATTRIBUTE_UNUSED)
+{
+}
diff --git a/i/pc104/initrd/conf/busybox/archival/libunarchive/header_verbose_list.c b/i/pc104/initrd/conf/busybox/archival/libunarchive/header_verbose_list.c
new file mode 100644
index 0000000..7b97e52
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/archival/libunarchive/header_verbose_list.c
@@ -0,0 +1,31 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "libbb.h"
+#include "unarchive.h"
+
+void header_verbose_list(const file_header_t *file_header)
+{
+ struct tm *mtime = localtime(&(file_header->mtime));
+
+ printf("%s %d/%d %9"OFF_FMT"u %4u-%02u-%02u %02u:%02u:%02u %s",
+ bb_mode_string(file_header->mode),
+ file_header->uid,
+ file_header->gid,
+ file_header->size,
+ 1900 + mtime->tm_year,
+ 1 + mtime->tm_mon,
+ mtime->tm_mday,
+ mtime->tm_hour,
+ mtime->tm_min,
+ mtime->tm_sec,
+ file_header->name);
+
+ if (file_header->link_name) {
+ printf(" -> %s", file_header->link_name);
+ }
+ /* putchar isnt used anywhere else i dont think */
+ puts("");
+}
diff --git a/i/pc104/initrd/conf/busybox/archival/libunarchive/init_handle.c b/i/pc104/initrd/conf/busybox/archival/libunarchive/init_handle.c
new file mode 100644
index 0000000..309d329
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/archival/libunarchive/init_handle.c
@@ -0,0 +1,22 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "libbb.h"
+#include "unarchive.h"
+
+archive_handle_t *init_handle(void)
+{
+ archive_handle_t *archive_handle;
+
+ /* Initialize default values */
+ archive_handle = xzalloc(sizeof(archive_handle_t));
+ archive_handle->file_header = xzalloc(sizeof(file_header_t));
+ archive_handle->action_header = header_skip;
+ archive_handle->action_data = data_skip;
+ archive_handle->filter = filter_accept_all;
+ archive_handle->seek = seek_by_jump;
+
+ return archive_handle;
+}
diff --git a/i/pc104/initrd/conf/busybox/archival/libunarchive/open_transformer.c b/i/pc104/initrd/conf/busybox/archival/libunarchive/open_transformer.c
new file mode 100644
index 0000000..456d3e9
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/archival/libunarchive/open_transformer.c
@@ -0,0 +1,44 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "libbb.h"
+
+#include "unarchive.h"
+
+/* transformer(), more than meets the eye */
+int open_transformer(int src_fd,
+ USE_DESKTOP(long long) int (*transformer)(int src_fd, int dst_fd))
+{
+ int fd_pipe[2];
+ int pid;
+
+ if (pipe(fd_pipe) != 0) {
+ bb_perror_msg_and_die("can't create pipe");
+ }
+
+ pid = fork();
+ if (pid == -1) {
+ bb_perror_msg_and_die("fork failed");
+ }
+
+ if (pid == 0) {
+ /* child process */
+ close(fd_pipe[0]); /* We don't wan't to read from the parent */
+ // FIXME: error check?
+ transformer(src_fd, fd_pipe[1]);
+ close(fd_pipe[1]); /* Send EOF */
+ close(src_fd);
+ exit(0);
+ /* notreached */
+ }
+
+ /* parent process */
+ close(fd_pipe[1]); /* Don't want to write to the child */
+
+ return fd_pipe[0];
+}
diff --git a/i/pc104/initrd/conf/busybox/archival/libunarchive/seek_by_jump.c b/i/pc104/initrd/conf/busybox/archival/libunarchive/seek_by_jump.c
new file mode 100644
index 0000000..d605f61
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/archival/libunarchive/seek_by_jump.c
@@ -0,0 +1,24 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include <sys/types.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include "libbb.h"
+#include "unarchive.h"
+
+void seek_by_jump(const archive_handle_t *archive_handle, const unsigned int amount)
+{
+ if (lseek(archive_handle->src_fd, (off_t) amount, SEEK_CUR) == (off_t) -1) {
+#ifdef CONFIG_FEATURE_UNARCHIVE_TAPE
+ if (errno == ESPIPE) {
+ seek_by_read(archive_handle, amount);
+ } else
+#endif
+ bb_perror_msg_and_die("seek failure");
+ }
+}
diff --git a/i/pc104/initrd/conf/busybox/archival/libunarchive/seek_by_read.c b/i/pc104/initrd/conf/busybox/archival/libunarchive/seek_by_read.c
new file mode 100644
index 0000000..e46af48
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/archival/libunarchive/seek_by_read.c
@@ -0,0 +1,18 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include <stdlib.h>
+
+#include "unarchive.h"
+#include "libbb.h"
+
+/* If we are reading through a pipe(), or from stdin then we can't lseek,
+ * we must read and discard the data to skip over it.
+ */
+void seek_by_read(const archive_handle_t *archive_handle, const unsigned int jump_size)
+{
+ if (jump_size)
+ bb_copyfd_exact_size(archive_handle->src_fd, -1, jump_size);
+}
diff --git a/i/pc104/initrd/conf/busybox/archival/libunarchive/unpack_ar_archive.c b/i/pc104/initrd/conf/busybox/archival/libunarchive/unpack_ar_archive.c
new file mode 100644
index 0000000..6a84ae8
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/archival/libunarchive/unpack_ar_archive.c
@@ -0,0 +1,22 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include "unarchive.h"
+#include "libbb.h"
+
+void unpack_ar_archive(archive_handle_t *ar_archive)
+{
+ char magic[7];
+
+ xread(ar_archive->src_fd, magic, 7);
+ if (strncmp(magic, "!<arch>", 7) != 0) {
+ bb_error_msg_and_die("invalid ar magic");
+ }
+ ar_archive->offset += 7;
+
+ while (get_header_ar(ar_archive) == EXIT_SUCCESS);
+}
diff --git a/i/pc104/initrd/conf/busybox/archival/rpm.c b/i/pc104/initrd/conf/busybox/archival/rpm.c
new file mode 100644
index 0000000..a48dda3
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/archival/rpm.c
@@ -0,0 +1,367 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini rpm applet for busybox
+ *
+ * Copyright (C) 2001,2002 by Laurence Anderson
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "busybox.h"
+#include "unarchive.h"
+
+#define RPM_HEADER_MAGIC "\216\255\350"
+#define RPM_CHAR_TYPE 1
+#define RPM_INT8_TYPE 2
+#define RPM_INT16_TYPE 3
+#define RPM_INT32_TYPE 4
+/* #define RPM_INT64_TYPE 5 ---- These aren't supported (yet) */
+#define RPM_STRING_TYPE 6
+#define RPM_BIN_TYPE 7
+#define RPM_STRING_ARRAY_TYPE 8
+#define RPM_I18NSTRING_TYPE 9
+
+#define TAG_NAME 1000
+#define TAG_VERSION 1001
+#define TAG_RELEASE 1002
+#define TAG_SUMMARY 1004
+#define TAG_DESCRIPTION 1005
+#define TAG_BUILDTIME 1006
+#define TAG_BUILDHOST 1007
+#define TAG_SIZE 1009
+#define TAG_VENDOR 1011
+#define TAG_LICENSE 1014
+#define TAG_PACKAGER 1015
+#define TAG_GROUP 1016
+#define TAG_URL 1020
+#define TAG_PREIN 1023
+#define TAG_POSTIN 1024
+#define TAG_FILEFLAGS 1037
+#define TAG_FILEUSERNAME 1039
+#define TAG_FILEGROUPNAME 1040
+#define TAG_SOURCERPM 1044
+#define TAG_PREINPROG 1085
+#define TAG_POSTINPROG 1086
+#define TAG_PREFIXS 1098
+#define TAG_DIRINDEXES 1116
+#define TAG_BASENAMES 1117
+#define TAG_DIRNAMES 1118
+#define RPMFILE_CONFIG (1 << 0)
+#define RPMFILE_DOC (1 << 1)
+
+enum rpm_functions_e {
+ rpm_query = 1,
+ rpm_install = 2,
+ rpm_query_info = 4,
+ rpm_query_package = 8,
+ rpm_query_list = 16,
+ rpm_query_list_doc = 32,
+ rpm_query_list_config = 64
+};
+
+typedef struct {
+ uint32_t tag; /* 4 byte tag */
+ uint32_t type; /* 4 byte type */
+ uint32_t offset; /* 4 byte offset */
+ uint32_t count; /* 4 byte count */
+} rpm_index;
+
+static void *map;
+static rpm_index **mytags;
+static int tagcount;
+
+static void extract_cpio_gz(int fd);
+static rpm_index **rpm_gettags(int fd, int *num_tags);
+static int bsearch_rpmtag(const void *key, const void *item);
+static char *rpm_getstr(int tag, int itemindex);
+static int rpm_getint(int tag, int itemindex);
+static int rpm_getcount(int tag);
+static void fileaction_dobackup(char *filename, int fileref);
+static void fileaction_setowngrp(char *filename, int fileref);
+static void loop_through_files(int filetag, void (*fileaction)(char *filename, int fileref));
+
+int rpm_main(int argc, char **argv);
+int rpm_main(int argc, char **argv)
+{
+ int opt = 0, func = 0, rpm_fd, offset;
+ const int pagesize = getpagesize();
+
+ while ((opt = getopt(argc, argv, "iqpldc")) != -1) {
+ switch (opt) {
+ case 'i': /* First arg: Install mode, with q: Information */
+ if (!func) func = rpm_install;
+ else func |= rpm_query_info;
+ break;
+ case 'q': /* First arg: Query mode */
+ if (func) bb_show_usage();
+ func = rpm_query;
+ break;
+ case 'p': /* Query a package */
+ func |= rpm_query_package;
+ break;
+ case 'l': /* List files in a package */
+ func |= rpm_query_list;
+ break;
+ case 'd': /* List doc files in a package (implies list) */
+ func |= rpm_query_list;
+ func |= rpm_query_list_doc;
+ break;
+ case 'c': /* List config files in a package (implies list) */
+ func |= rpm_query_list;
+ func |= rpm_query_list_config;
+ break;
+ default:
+ bb_show_usage();
+ }
+ }
+ argv += optind;
+ argc -= optind;
+ if (!argc) bb_show_usage();
+
+ while (*argv) {
+ rpm_fd = xopen(*argv++, O_RDONLY);
+ mytags = rpm_gettags(rpm_fd, &tagcount);
+ if (!mytags)
+ bb_error_msg_and_die("error reading rpm header");
+ offset = xlseek(rpm_fd, 0, SEEK_CUR);
+ /* Mimimum is one page */
+ map = mmap(0, offset > pagesize ? (offset + offset % pagesize) : pagesize, PROT_READ, MAP_PRIVATE, rpm_fd, 0);
+
+ if (func & rpm_install) {
+ /* Backup any config files */
+ loop_through_files(TAG_BASENAMES, fileaction_dobackup);
+ /* Extact the archive */
+ extract_cpio_gz(rpm_fd);
+ /* Set the correct file uid/gid's */
+ loop_through_files(TAG_BASENAMES, fileaction_setowngrp);
+ }
+ else if ((func & (rpm_query|rpm_query_package)) == (rpm_query|rpm_query_package)) {
+ if (!(func & (rpm_query_info|rpm_query_list))) {
+ /* If just a straight query, just give package name */
+ printf("%s-%s-%s\n", rpm_getstr(TAG_NAME, 0), rpm_getstr(TAG_VERSION, 0), rpm_getstr(TAG_RELEASE, 0));
+ }
+ if (func & rpm_query_info) {
+ /* Do the nice printout */
+ time_t bdate_time;
+ struct tm *bdate;
+ char bdatestring[50];
+ printf("Name : %-29sRelocations: %s\n", rpm_getstr(TAG_NAME, 0), rpm_getstr(TAG_PREFIXS, 0) ? rpm_getstr(TAG_PREFIXS, 0) : "(not relocateable)");
+ printf("Version : %-34sVendor: %s\n", rpm_getstr(TAG_VERSION, 0), rpm_getstr(TAG_VENDOR, 0) ? rpm_getstr(TAG_VENDOR, 0) : "(none)");
+ bdate_time = rpm_getint(TAG_BUILDTIME, 0);
+ bdate = localtime((time_t *) &bdate_time);
+ strftime(bdatestring, 50, "%a %d %b %Y %T %Z", bdate);
+ printf("Release : %-30sBuild Date: %s\n", rpm_getstr(TAG_RELEASE, 0), bdatestring);
+ printf("Install date: %-30sBuild Host: %s\n", "(not installed)", rpm_getstr(TAG_BUILDHOST, 0));
+ printf("Group : %-30sSource RPM: %s\n", rpm_getstr(TAG_GROUP, 0), rpm_getstr(TAG_SOURCERPM, 0));
+ printf("Size : %-33dLicense: %s\n", rpm_getint(TAG_SIZE, 0), rpm_getstr(TAG_LICENSE, 0));
+ printf("URL : %s\n", rpm_getstr(TAG_URL, 0));
+ printf("Summary : %s\n", rpm_getstr(TAG_SUMMARY, 0));
+ printf("Description :\n%s\n", rpm_getstr(TAG_DESCRIPTION, 0));
+ }
+ if (func & rpm_query_list) {
+ int count, it, flags;
+ count = rpm_getcount(TAG_BASENAMES);
+ for (it = 0; it < count; it++) {
+ flags = rpm_getint(TAG_FILEFLAGS, it);
+ switch (func & (rpm_query_list_doc|rpm_query_list_config)) {
+ case rpm_query_list_doc:
+ if (!(flags & RPMFILE_DOC)) continue;
+ break;
+ case rpm_query_list_config:
+ if (!(flags & RPMFILE_CONFIG)) continue;
+ break;
+ case rpm_query_list_doc|rpm_query_list_config:
+ if (!(flags & (RPMFILE_CONFIG|RPMFILE_DOC))) continue;
+ break;
+ }
+ printf("%s%s\n",
+ rpm_getstr(TAG_DIRNAMES, rpm_getint(TAG_DIRINDEXES, it)),
+ rpm_getstr(TAG_BASENAMES, it));
+ }
+ }
+ }
+ free(mytags);
+ }
+ return 0;
+}
+
+static void extract_cpio_gz(int fd) {
+ archive_handle_t *archive_handle;
+ unsigned char magic[2];
+
+ /* Initialise */
+ archive_handle = init_handle();
+ archive_handle->seek = seek_by_read;
+ //archive_handle->action_header = header_list;
+ archive_handle->action_data = data_extract_all;
+ archive_handle->flags |= ARCHIVE_PRESERVE_DATE;
+ archive_handle->flags |= ARCHIVE_CREATE_LEADING_DIRS;
+ archive_handle->src_fd = fd;
+ archive_handle->offset = 0;
+
+ xread(archive_handle->src_fd, &magic, 2);
+ if ((magic[0] != 0x1f) || (magic[1] != 0x8b)) {
+ bb_error_msg_and_die("invalid gzip magic");
+ }
+ check_header_gzip_or_die(archive_handle->src_fd);
+ xchdir("/"); /* Install RPM's to root */
+
+ archive_handle->src_fd = open_transformer(archive_handle->src_fd, inflate_gunzip);
+ archive_handle->offset = 0;
+ while (get_header_cpio(archive_handle) == EXIT_SUCCESS)
+ /* loop */;
+}
+
+
+static rpm_index **rpm_gettags(int fd, int *num_tags)
+{
+ /* We should never need mode than 200, and realloc later */
+ rpm_index **tags = xzalloc(200 * sizeof(struct rpmtag *));
+ int pass, tagindex = 0;
+
+ xlseek(fd, 96, SEEK_CUR); /* Seek past the unused lead */
+
+ /* 1st pass is the signature headers, 2nd is the main stuff */
+ for (pass = 0; pass < 2; pass++) {
+ struct {
+ char magic[3]; /* 3 byte magic: 0x8e 0xad 0xe8 */
+ uint8_t version; /* 1 byte version number */
+ uint32_t reserved; /* 4 bytes reserved */
+ uint32_t entries; /* Number of entries in header (4 bytes) */
+ uint32_t size; /* Size of store (4 bytes) */
+ } header;
+ rpm_index *tmpindex;
+ int storepos;
+
+ xread(fd, &header, sizeof(header));
+ if (strncmp((char *) &header.magic, RPM_HEADER_MAGIC, 3) != 0)
+ return NULL; /* Invalid magic */
+ if (header.version != 1)
+ return NULL; /* This program only supports v1 headers */
+ header.size = ntohl(header.size);
+ header.entries = ntohl(header.entries);
+ storepos = xlseek(fd,0,SEEK_CUR) + header.entries * 16;
+
+ while (header.entries--) {
+ tmpindex = tags[tagindex++] = xmalloc(sizeof(rpm_index));
+ xread(fd, tmpindex, sizeof(rpm_index));
+ tmpindex->tag = ntohl(tmpindex->tag);
+ tmpindex->type = ntohl(tmpindex->type);
+ tmpindex->count = ntohl(tmpindex->count);
+ tmpindex->offset = storepos + ntohl(tmpindex->offset);
+ if (pass==0)
+ tmpindex->tag -= 743;
+ }
+ xlseek(fd, header.size, SEEK_CUR); /* Seek past store */
+ /* Skip padding to 8 byte boundary after reading signature headers */
+ if (pass==0)
+ xlseek(fd, (8 - (xlseek(fd,0,SEEK_CUR) % 8)) % 8, SEEK_CUR);
+ }
+ tags = xrealloc(tags, tagindex * sizeof(struct rpmtag *)); /* realloc tags to save space */
+ *num_tags = tagindex;
+ return tags; /* All done, leave the file at the start of the gzipped cpio archive */
+}
+
+static int bsearch_rpmtag(const void *key, const void *item)
+{
+ int *tag = (int *)key;
+ rpm_index **tmp = (rpm_index **) item;
+ return (*tag - tmp[0]->tag);
+}
+
+static int rpm_getcount(int tag)
+{
+ rpm_index **found;
+ found = bsearch(&tag, mytags, tagcount, sizeof(struct rpmtag *), bsearch_rpmtag);
+ if (!found)
+ return 0;
+ return found[0]->count;
+}
+
+static char *rpm_getstr(int tag, int itemindex)
+{
+ rpm_index **found;
+ found = bsearch(&tag, mytags, tagcount, sizeof(struct rpmtag *), bsearch_rpmtag);
+ if (!found || itemindex >= found[0]->count)
+ return NULL;
+ if (found[0]->type == RPM_STRING_TYPE || found[0]->type == RPM_I18NSTRING_TYPE || found[0]->type == RPM_STRING_ARRAY_TYPE) {
+ int n;
+ char *tmpstr = (char *) (map + found[0]->offset);
+ for (n=0; n < itemindex; n++)
+ tmpstr = tmpstr + strlen(tmpstr) + 1;
+ return tmpstr;
+ }
+ return NULL;
+}
+
+static int rpm_getint(int tag, int itemindex)
+{
+ rpm_index **found;
+ int *tmpint; /* NB: using int8_t* would be easier to code */
+
+ /* gcc throws warnings here when sizeof(void*)!=sizeof(int) ...
+ * it's ok to ignore it because tag won't be used as a pointer */
+ found = bsearch(&tag, mytags, tagcount, sizeof(struct rpmtag *), bsearch_rpmtag);
+ if (!found || itemindex >= found[0]->count)
+ return -1;
+
+ tmpint = (int *) (map + found[0]->offset);
+
+ if (found[0]->type == RPM_INT32_TYPE) {
+ tmpint = (int *) ((char *) tmpint + itemindex*4);
+ /*return ntohl(*tmpint);*/
+ /* int can be != int32_t */
+ return ntohl(*(int32_t*)tmpint);
+ }
+ if (found[0]->type == RPM_INT16_TYPE) {
+ tmpint = (int *) ((char *) tmpint + itemindex*2);
+ /* ??? read int, and THEN ntohs() it?? */
+ /*return ntohs(*tmpint);*/
+ return ntohs(*(int16_t*)tmpint);
+ }
+ if (found[0]->type == RPM_INT8_TYPE) {
+ tmpint = (int *) ((char *) tmpint + itemindex);
+ /* ??? why we don't read byte here??? */
+ /*return ntohs(*tmpint);*/
+ return *(int8_t*)tmpint;
+ }
+ return -1;
+}
+
+static void fileaction_dobackup(char *filename, int fileref)
+{
+ struct stat oldfile;
+ int stat_res;
+ char *newname;
+ if (rpm_getint(TAG_FILEFLAGS, fileref) & RPMFILE_CONFIG) {
+ /* Only need to backup config files */
+ stat_res = lstat(filename, &oldfile);
+ if (stat_res == 0 && S_ISREG(oldfile.st_mode)) {
+ /* File already exists - really should check MD5's etc to see if different */
+ newname = xasprintf("%s.rpmorig", filename);
+ copy_file(filename, newname, FILEUTILS_RECUR | FILEUTILS_PRESERVE_STATUS);
+ remove_file(filename, FILEUTILS_RECUR | FILEUTILS_FORCE);
+ free(newname);
+ }
+ }
+}
+
+static void fileaction_setowngrp(char *filename, int fileref)
+{
+ int uid, gid;
+ uid = xuname2uid(rpm_getstr(TAG_FILEUSERNAME, fileref));
+ gid = xgroup2gid(rpm_getstr(TAG_FILEGROUPNAME, fileref));
+ chown(filename, uid, gid);
+}
+
+static void loop_through_files(int filetag, void (*fileaction)(char *filename, int fileref))
+{
+ int count = 0;
+ while (rpm_getstr(filetag, count)) {
+ char* filename = xasprintf("%s%s",
+ rpm_getstr(TAG_DIRNAMES, rpm_getint(TAG_DIRINDEXES, count)),
+ rpm_getstr(TAG_BASENAMES, count));
+ fileaction(filename, count++);
+ free(filename);
+ }
+}
diff --git a/i/pc104/initrd/conf/busybox/archival/rpm2cpio.c b/i/pc104/initrd/conf/busybox/archival/rpm2cpio.c
new file mode 100644
index 0000000..307d1a0
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/archival/rpm2cpio.c
@@ -0,0 +1,90 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini rpm2cpio implementation for busybox
+ *
+ * Copyright (C) 2001 by Laurence Anderson
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+#include "busybox.h"
+#include "unarchive.h"
+
+#define RPM_MAGIC "\355\253\356\333"
+#define RPM_HEADER_MAGIC "\216\255\350"
+
+struct rpm_lead {
+ unsigned char magic[4];
+ uint8_t major, minor;
+ uint16_t type;
+ uint16_t archnum;
+ char name[66];
+ uint16_t osnum;
+ uint16_t signature_type;
+ char reserved[16];
+};
+
+struct rpm_header {
+ char magic[3]; /* 3 byte magic: 0x8e 0xad 0xe8 */
+ uint8_t version; /* 1 byte version number */
+ uint32_t reserved; /* 4 bytes reserved */
+ uint32_t entries; /* Number of entries in header (4 bytes) */
+ uint32_t size; /* Size of store (4 bytes) */
+};
+
+static void skip_header(int rpm_fd)
+{
+ struct rpm_header header;
+
+ xread(rpm_fd, &header, sizeof(struct rpm_header));
+ if (strncmp((char *) &header.magic, RPM_HEADER_MAGIC, 3) != 0) {
+ bb_error_msg_and_die("invalid RPM header magic"); /* Invalid magic */
+ }
+ if (header.version != 1) {
+ bb_error_msg_and_die("unsupported RPM header version"); /* This program only supports v1 headers */
+ }
+ header.entries = ntohl(header.entries);
+ header.size = ntohl(header.size);
+ lseek (rpm_fd, 16 * header.entries, SEEK_CUR); /* Seek past index entries */
+ lseek (rpm_fd, header.size, SEEK_CUR); /* Seek past store */
+}
+
+/* No getopt required */
+int rpm2cpio_main(int argc, char **argv);
+int rpm2cpio_main(int argc, char **argv)
+{
+ struct rpm_lead lead;
+ int rpm_fd;
+ unsigned char magic[2];
+
+ if (argc == 1) {
+ rpm_fd = STDIN_FILENO;
+ } else {
+ rpm_fd = xopen(argv[1], O_RDONLY);
+ }
+
+ xread(rpm_fd, &lead, sizeof(struct rpm_lead));
+ if (strncmp((char *) &lead.magic, RPM_MAGIC, 4) != 0) {
+ bb_error_msg_and_die("invalid RPM magic"); /* Just check the magic, the rest is irrelevant */
+ }
+
+ /* Skip the signature header */
+ skip_header(rpm_fd);
+ lseek(rpm_fd, (8 - (lseek(rpm_fd, 0, SEEK_CUR) % 8)) % 8, SEEK_CUR);
+
+ /* Skip the main header */
+ skip_header(rpm_fd);
+
+ xread(rpm_fd, &magic, 2);
+ if ((magic[0] != 0x1f) || (magic[1] != 0x8b)) {
+ bb_error_msg_and_die("invalid gzip magic");
+ }
+
+ check_header_gzip_or_die(rpm_fd);
+ if (inflate_gunzip(rpm_fd, STDOUT_FILENO) < 0) {
+ bb_error_msg("error inflating");
+ }
+
+ close(rpm_fd);
+
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/archival/tar.c b/i/pc104/initrd/conf/busybox/archival/tar.c
new file mode 100644
index 0000000..176a7e2
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/archival/tar.c
@@ -0,0 +1,939 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini tar implementation for busybox
+ *
+ * Modified to use common extraction code used by ar, cpio, dpkg-deb, dpkg
+ * Glenn McGrath <bug1@iinet.net.au>
+ *
+ * Note, that as of BusyBox-0.43, tar has been completely rewritten from the
+ * ground up. It still has remnants of the old code lying about, but it is
+ * very different now (i.e., cleaner, less global variables, etc.)
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Based in part in the tar implementation in sash
+ * Copyright (c) 1999 by David I. Bell
+ * Permission is granted to use, distribute, or modify this source,
+ * provided that this copyright notice remains intact.
+ * Permission to distribute sash derived code under the GPL has been granted.
+ *
+ * Based in part on the tar implementation from busybox-0.28
+ * Copyright (C) 1995 Bruce Perens
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include <fnmatch.h>
+#include <getopt.h>
+#include "busybox.h"
+#include "unarchive.h"
+
+#if ENABLE_FEATURE_TAR_CREATE
+
+/* Tar file constants */
+
+#define TAR_BLOCK_SIZE 512
+
+/* POSIX tar Header Block, from POSIX 1003.1-1990 */
+#define NAME_SIZE 100
+#define NAME_SIZE_STR "100"
+typedef struct TarHeader TarHeader;
+struct TarHeader { /* byte offset */
+ char name[NAME_SIZE]; /* 0-99 */
+ char mode[8]; /* 100-107 */
+ char uid[8]; /* 108-115 */
+ char gid[8]; /* 116-123 */
+ char size[12]; /* 124-135 */
+ char mtime[12]; /* 136-147 */
+ char chksum[8]; /* 148-155 */
+ char typeflag; /* 156-156 */
+ char linkname[NAME_SIZE]; /* 157-256 */
+ char magic[6]; /* 257-262 */
+ char version[2]; /* 263-264 */
+ char uname[32]; /* 265-296 */
+ char gname[32]; /* 297-328 */
+ char devmajor[8]; /* 329-336 */
+ char devminor[8]; /* 337-344 */
+ char prefix[155]; /* 345-499 */
+ char padding[12]; /* 500-512 (pad to exactly the TAR_BLOCK_SIZE) */
+};
+
+/*
+** writeTarFile(), writeFileToTarball(), and writeTarHeader() are
+** the only functions that deal with the HardLinkInfo structure.
+** Even these functions use the xxxHardLinkInfo() functions.
+*/
+typedef struct HardLinkInfo HardLinkInfo;
+struct HardLinkInfo {
+ HardLinkInfo *next; /* Next entry in list */
+ dev_t dev; /* Device number */
+ ino_t ino; /* Inode number */
+ short linkCount; /* (Hard) Link Count */
+ char name[1]; /* Start of filename (must be last) */
+};
+
+/* Some info to be carried along when creating a new tarball */
+typedef struct TarBallInfo TarBallInfo;
+struct TarBallInfo {
+ int tarFd; /* Open-for-write file descriptor
+ for the tarball */
+ struct stat statBuf; /* Stat info for the tarball, letting
+ us know the inode and device that the
+ tarball lives, so we can avoid trying
+ to include the tarball into itself */
+ int verboseFlag; /* Whether to print extra stuff or not */
+ const llist_t *excludeList; /* List of files to not include */
+ HardLinkInfo *hlInfoHead; /* Hard Link Tracking Information */
+ HardLinkInfo *hlInfo; /* Hard Link Info for the current file */
+};
+
+/* A nice enum with all the possible tar file content types */
+enum TarFileType {
+ REGTYPE = '0', /* regular file */
+ REGTYPE0 = '\0', /* regular file (ancient bug compat) */
+ LNKTYPE = '1', /* hard link */
+ SYMTYPE = '2', /* symbolic link */
+ CHRTYPE = '3', /* character special */
+ BLKTYPE = '4', /* block special */
+ DIRTYPE = '5', /* directory */
+ FIFOTYPE = '6', /* FIFO special */
+ CONTTYPE = '7', /* reserved */
+ GNULONGLINK = 'K', /* GNU long (>100 chars) link name */
+ GNULONGNAME = 'L', /* GNU long (>100 chars) file name */
+};
+typedef enum TarFileType TarFileType;
+
+/* Might be faster (and bigger) if the dev/ino were stored in numeric order;) */
+static void addHardLinkInfo(HardLinkInfo ** hlInfoHeadPtr,
+ struct stat *statbuf,
+ const char *fileName)
+{
+ /* Note: hlInfoHeadPtr can never be NULL! */
+ HardLinkInfo *hlInfo;
+
+ hlInfo = xmalloc(sizeof(HardLinkInfo) + strlen(fileName));
+ hlInfo->next = *hlInfoHeadPtr;
+ *hlInfoHeadPtr = hlInfo;
+ hlInfo->dev = statbuf->st_dev;
+ hlInfo->ino = statbuf->st_ino;
+ hlInfo->linkCount = statbuf->st_nlink;
+ strcpy(hlInfo->name, fileName);
+}
+
+static void freeHardLinkInfo(HardLinkInfo ** hlInfoHeadPtr)
+{
+ HardLinkInfo *hlInfo;
+ HardLinkInfo *hlInfoNext;
+
+ if (hlInfoHeadPtr) {
+ hlInfo = *hlInfoHeadPtr;
+ while (hlInfo) {
+ hlInfoNext = hlInfo->next;
+ free(hlInfo);
+ hlInfo = hlInfoNext;
+ }
+ *hlInfoHeadPtr = NULL;
+ }
+}
+
+/* Might be faster (and bigger) if the dev/ino were stored in numeric order;) */
+static HardLinkInfo *findHardLinkInfo(HardLinkInfo * hlInfo, struct stat *statbuf)
+{
+ while (hlInfo) {
+ if ((statbuf->st_ino == hlInfo->ino) && (statbuf->st_dev == hlInfo->dev))
+ break;
+ hlInfo = hlInfo->next;
+ }
+ return hlInfo;
+}
+
+/* Put an octal string into the specified buffer.
+ * The number is zero padded and possibly null terminated.
+ * Stores low-order bits only if whole value does not fit. */
+static void putOctal(char *cp, int len, off_t value)
+{
+ char tempBuffer[sizeof(off_t)*3+1];
+ char *tempString = tempBuffer;
+ int width;
+
+ width = sprintf(tempBuffer, "%0*"OFF_FMT"o", len, value);
+ tempString += (width - len);
+
+ /* If string has leading zeroes, we can drop one */
+ /* and field will have trailing '\0' */
+ /* (increases chances of compat with other tars) */
+ if (tempString[0] == '0')
+ tempString++;
+
+ /* Copy the string to the field */
+ memcpy(cp, tempString, len);
+}
+#define PUT_OCTAL(a, b) putOctal((a), sizeof(a), (b))
+
+static void chksum_and_xwrite(int fd, struct TarHeader* hp)
+{
+ /* POSIX says that checksum is done on unsigned bytes
+ * (Sun and HP-UX gets it wrong... more details in
+ * GNU tar source) */
+ const unsigned char *cp;
+ int chksum, size;
+
+ strcpy(hp->magic, "ustar ");
+
+ /* Calculate and store the checksum (i.e., the sum of all of the bytes of
+ * the header). The checksum field must be filled with blanks for the
+ * calculation. The checksum field is formatted differently from the
+ * other fields: it has 6 digits, a null, then a space -- rather than
+ * digits, followed by a null like the other fields... */
+ memset(hp->chksum, ' ', sizeof(hp->chksum));
+ cp = (const unsigned char *) hp;
+ chksum = 0;
+ size = sizeof(*hp);
+ do { chksum += *cp++; } while (--size);
+ putOctal(hp->chksum, sizeof(hp->chksum)-1, chksum);
+
+ /* Now write the header out to disk */
+ xwrite(fd, hp, sizeof(*hp));
+}
+
+#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
+static void writeLongname(int fd, int type, const char *name, int dir)
+{
+ static const struct {
+ char mode[8]; /* 100-107 */
+ char uid[8]; /* 108-115 */
+ char gid[8]; /* 116-123 */
+ char size[12]; /* 124-135 */
+ char mtime[12]; /* 136-147 */
+ } prefilled = {
+ "0000000",
+ "0000000",
+ "0000000",
+ "00000000000",
+ "00000000000",
+ };
+ struct TarHeader header;
+ int size;
+
+ dir = !!dir; /* normalize: 0/1 */
+ size = strlen(name) + 1 + dir; /* GNU tar uses strlen+1 */
+ /* + dir: account for possible '/' */
+
+ memset(&header, 0, sizeof(header));
+ strcpy(header.name, "././@LongLink");
+ memcpy(header.mode, prefilled.mode, sizeof(prefilled));
+ PUT_OCTAL(header.size, size);
+ header.typeflag = type;
+ chksum_and_xwrite(fd, &header);
+
+ /* Write filename[/] and pad the block. */
+ /* dir=0: writes 'name<NUL>', pads */
+ /* dir=1: writes 'name', writes '/<NUL>', pads */
+ dir *= 2;
+ xwrite(fd, name, size - dir);
+ xwrite(fd, "/", dir);
+ size = (-size) & (TAR_BLOCK_SIZE-1);
+ memset(&header, 0, size);
+ xwrite(fd, &header, size);
+}
+#endif
+
+/* Write out a tar header for the specified file/directory/whatever */
+void BUG_tar_header_size(void);
+static int writeTarHeader(struct TarBallInfo *tbInfo,
+ const char *header_name, const char *fileName, struct stat *statbuf)
+{
+ struct TarHeader header;
+
+ if (sizeof(header) != 512)
+ BUG_tar_header_size();
+
+ memset(&header, 0, sizeof(struct TarHeader));
+
+ strncpy(header.name, header_name, sizeof(header.name));
+
+ /* POSIX says to mask mode with 07777. */
+ PUT_OCTAL(header.mode, statbuf->st_mode & 07777);
+ PUT_OCTAL(header.uid, statbuf->st_uid);
+ PUT_OCTAL(header.gid, statbuf->st_gid);
+ memset(header.size, '0', sizeof(header.size)-1); /* Regular file size is handled later */
+ PUT_OCTAL(header.mtime, statbuf->st_mtime);
+
+ /* Enter the user and group names */
+ safe_strncpy(header.uname, get_cached_username(statbuf->st_uid), sizeof(header.uname));
+ safe_strncpy(header.gname, get_cached_groupname(statbuf->st_gid), sizeof(header.gname));
+
+ if (tbInfo->hlInfo) {
+ /* This is a hard link */
+ header.typeflag = LNKTYPE;
+ strncpy(header.linkname, tbInfo->hlInfo->name,
+ sizeof(header.linkname));
+#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
+ /* Write out long linkname if needed */
+ if (header.linkname[sizeof(header.linkname)-1])
+ writeLongname(tbInfo->tarFd, GNULONGLINK,
+ tbInfo->hlInfo->name, 0);
+#endif
+ } else if (S_ISLNK(statbuf->st_mode)) {
+ char *lpath = xmalloc_readlink_or_warn(fileName);
+ if (!lpath)
+ return FALSE;
+ header.typeflag = SYMTYPE;
+ strncpy(header.linkname, lpath, sizeof(header.linkname));
+#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
+ /* Write out long linkname if needed */
+ if (header.linkname[sizeof(header.linkname)-1])
+ writeLongname(tbInfo->tarFd, GNULONGLINK, lpath, 0);
+#else
+ /* If it is larger than 100 bytes, bail out */
+ if (header.linkname[sizeof(header.linkname)-1]) {
+ free(lpath);
+ bb_error_msg("names longer than "NAME_SIZE_STR" chars not supported");
+ return FALSE;
+ }
+#endif
+ free(lpath);
+ } else if (S_ISDIR(statbuf->st_mode)) {
+ header.typeflag = DIRTYPE;
+ /* Append '/' only if there is a space for it */
+ if (!header.name[sizeof(header.name)-1])
+ header.name[strlen(header.name)] = '/';
+ } else if (S_ISCHR(statbuf->st_mode)) {
+ header.typeflag = CHRTYPE;
+ PUT_OCTAL(header.devmajor, major(statbuf->st_rdev));
+ PUT_OCTAL(header.devminor, minor(statbuf->st_rdev));
+ } else if (S_ISBLK(statbuf->st_mode)) {
+ header.typeflag = BLKTYPE;
+ PUT_OCTAL(header.devmajor, major(statbuf->st_rdev));
+ PUT_OCTAL(header.devminor, minor(statbuf->st_rdev));
+ } else if (S_ISFIFO(statbuf->st_mode)) {
+ header.typeflag = FIFOTYPE;
+ } else if (S_ISREG(statbuf->st_mode)) {
+ if (sizeof(statbuf->st_size) > 4
+ && statbuf->st_size > (off_t)0777777777777LL
+ ) {
+ bb_error_msg_and_die("cannot store file '%s' "
+ "of size %"OFF_FMT"d, aborting",
+ fileName, statbuf->st_size);
+ }
+ header.typeflag = REGTYPE;
+ PUT_OCTAL(header.size, statbuf->st_size);
+ } else {
+ bb_error_msg("%s: unknown file type", fileName);
+ return FALSE;
+ }
+
+#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
+ /* Write out long name if needed */
+ /* (we, like GNU tar, output long linkname *before* long name) */
+ if (header.name[sizeof(header.name)-1])
+ writeLongname(tbInfo->tarFd, GNULONGNAME,
+ header_name, S_ISDIR(statbuf->st_mode));
+#endif
+
+ /* Now write the header out to disk */
+ chksum_and_xwrite(tbInfo->tarFd, &header);
+
+ /* Now do the verbose thing (or not) */
+ if (tbInfo->verboseFlag) {
+ FILE *vbFd = stdout;
+
+ if (tbInfo->tarFd == STDOUT_FILENO) /* If the archive goes to stdout, verbose to stderr */
+ vbFd = stderr;
+ /* GNU "tar cvvf" prints "extended" listing a-la "ls -l" */
+ /* We don't have such excesses here: for us "v" == "vv" */
+ /* '/' is probably a GNUism */
+ fprintf(vbFd, "%s%s\n", header_name,
+ S_ISDIR(statbuf->st_mode) ? "/" : "");
+ }
+
+ return TRUE;
+}
+
+#if ENABLE_FEATURE_TAR_FROM
+static int exclude_file(const llist_t *excluded_files, const char *file)
+{
+ while (excluded_files) {
+ if (excluded_files->data[0] == '/') {
+ if (fnmatch(excluded_files->data, file,
+ FNM_PATHNAME | FNM_LEADING_DIR) == 0)
+ return 1;
+ } else {
+ const char *p;
+
+ for (p = file; p[0] != '\0'; p++) {
+ if ((p == file || p[-1] == '/') && p[0] != '/' &&
+ fnmatch(excluded_files->data, p,
+ FNM_PATHNAME | FNM_LEADING_DIR) == 0)
+ return 1;
+ }
+ }
+ excluded_files = excluded_files->link;
+ }
+
+ return 0;
+}
+#else
+#define exclude_file(excluded_files, file) 0
+#endif
+
+static int writeFileToTarball(const char *fileName, struct stat *statbuf,
+ void *userData, int depth ATTRIBUTE_UNUSED)
+{
+ struct TarBallInfo *tbInfo = (struct TarBallInfo *) userData;
+ const char *header_name;
+ int inputFileFd = -1;
+
+ /*
+ * Check to see if we are dealing with a hard link.
+ * If so -
+ * Treat the first occurance of a given dev/inode as a file while
+ * treating any additional occurances as hard links. This is done
+ * by adding the file information to the HardLinkInfo linked list.
+ */
+ tbInfo->hlInfo = NULL;
+ if (statbuf->st_nlink > 1) {
+ tbInfo->hlInfo = findHardLinkInfo(tbInfo->hlInfoHead, statbuf);
+ if (tbInfo->hlInfo == NULL)
+ addHardLinkInfo(&tbInfo->hlInfoHead, statbuf, fileName);
+ }
+
+ /* It is against the rules to archive a socket */
+ if (S_ISSOCK(statbuf->st_mode)) {
+ bb_error_msg("%s: socket ignored", fileName);
+ return TRUE;
+ }
+
+ /* It is a bad idea to store the archive we are in the process of creating,
+ * so check the device and inode to be sure that this particular file isn't
+ * the new tarball */
+ if (tbInfo->statBuf.st_dev == statbuf->st_dev &&
+ tbInfo->statBuf.st_ino == statbuf->st_ino) {
+ bb_error_msg("%s: file is the archive; skipping", fileName);
+ return TRUE;
+ }
+
+ header_name = fileName;
+ while (header_name[0] == '/') {
+ static int alreadyWarned = FALSE;
+
+ if (alreadyWarned == FALSE) {
+ bb_error_msg("removing leading '/' from member names");
+ alreadyWarned = TRUE;
+ }
+ header_name++;
+ }
+
+#if !ENABLE_FEATURE_TAR_GNU_EXTENSIONS
+ if (strlen(fileName) >= NAME_SIZE) {
+ bb_error_msg("names longer than "NAME_SIZE_STR" chars not supported");
+ return TRUE;
+ }
+#endif
+
+ if (header_name[0] == '\0')
+ return TRUE;
+
+ if (exclude_file(tbInfo->excludeList, header_name))
+ return SKIP;
+
+ /* Is this a regular file? */
+ if (tbInfo->hlInfo == NULL && S_ISREG(statbuf->st_mode)) {
+ /* open the file we want to archive, and make sure all is well */
+ inputFileFd = open(fileName, O_RDONLY);
+ if (inputFileFd < 0) {
+ bb_perror_msg("%s: cannot open", fileName);
+ return FALSE;
+ }
+ }
+
+ /* Add an entry to the tarball */
+ if (writeTarHeader(tbInfo, header_name, fileName, statbuf) == FALSE) {
+ return FALSE;
+ }
+
+ /* If it was a regular file, write out the body */
+ if (inputFileFd >= 0) {
+ size_t readSize;
+ /* Wwrite the file to the archive. */
+ /* We record size into header first, */
+ /* and then write out file. If file shrinks in between, */
+ /* tar will be corrupted. So we don't allow for that. */
+ /* NB: GNU tar 1.16 warns and pads with zeroes */
+ /* or even seeks back and updates header */
+ bb_copyfd_exact_size(inputFileFd, tbInfo->tarFd, statbuf->st_size);
+ ////off_t readSize;
+ ////readSize = bb_copyfd_size(inputFileFd, tbInfo->tarFd, statbuf->st_size);
+ ////if (readSize != statbuf->st_size && readSize >= 0) {
+ //// bb_error_msg_and_die("short read from %s, aborting", fileName);
+ ////}
+
+ /* Check that file did not grow in between? */
+ /* if (safe_read(inputFileFd, 1) == 1) warn but continue? */
+
+ close(inputFileFd);
+
+ /* Pad the file up to the tar block size */
+ /* (a few tricks here in the name of code size) */
+ readSize = (-(int)statbuf->st_size) & (TAR_BLOCK_SIZE-1);
+ memset(bb_common_bufsiz1, 0, readSize);
+ xwrite(tbInfo->tarFd, bb_common_bufsiz1, readSize);
+ }
+
+ return TRUE;
+}
+
+static int writeTarFile(const int tar_fd, const int verboseFlag,
+ const unsigned long dereferenceFlag, const llist_t *include,
+ const llist_t *exclude, const int gzip)
+{
+ pid_t gzipPid = 0;
+ int errorFlag = FALSE;
+ struct TarBallInfo tbInfo;
+
+ tbInfo.hlInfoHead = NULL;
+
+ fchmod(tar_fd, 0644);
+ tbInfo.tarFd = tar_fd;
+ tbInfo.verboseFlag = verboseFlag;
+
+ /* Store the stat info for the tarball's file, so
+ * can avoid including the tarball into itself.... */
+ if (fstat(tbInfo.tarFd, &tbInfo.statBuf) < 0)
+ bb_perror_msg_and_die("cannot stat tar file");
+
+ if ((ENABLE_FEATURE_TAR_GZIP || ENABLE_FEATURE_TAR_BZIP2) && gzip) {
+ int gzipDataPipe[2] = { -1, -1 };
+ int gzipStatusPipe[2] = { -1, -1 };
+ volatile int vfork_exec_errno = 0;
+ const char *zip_exec = (gzip == 1) ? "gzip" : "bzip2";
+
+ if (pipe(gzipDataPipe) < 0 || pipe(gzipStatusPipe) < 0)
+ bb_perror_msg_and_die("pipe");
+
+ signal(SIGPIPE, SIG_IGN); /* we only want EPIPE on errors */
+
+#if defined(__GNUC__) && __GNUC__
+ /* Avoid vfork clobbering */
+ (void) &include;
+ (void) &errorFlag;
+ (void) &zip_exec;
+#endif
+
+ gzipPid = vfork();
+
+ if (gzipPid == 0) {
+ dup2(gzipDataPipe[0], 0);
+ close(gzipDataPipe[1]);
+
+ dup2(tbInfo.tarFd, 1);
+
+ close(gzipStatusPipe[0]);
+ fcntl(gzipStatusPipe[1], F_SETFD, FD_CLOEXEC); /* close on exec shows success */
+
+ BB_EXECLP(zip_exec, zip_exec, "-f", NULL);
+ vfork_exec_errno = errno;
+
+ close(gzipStatusPipe[1]);
+ exit(-1);
+ } else if (gzipPid > 0) {
+ close(gzipDataPipe[0]);
+ close(gzipStatusPipe[1]);
+
+ while (1) {
+ char buf;
+
+ int n = full_read(gzipStatusPipe[0], &buf, 1);
+
+ if (n == 0 && vfork_exec_errno != 0) {
+ errno = vfork_exec_errno;
+ bb_perror_msg_and_die("cannot exec %s", zip_exec);
+ } else if ((n < 0) && (errno == EAGAIN || errno == EINTR))
+ continue; /* try it again */
+ break;
+ }
+ close(gzipStatusPipe[0]);
+
+ tbInfo.tarFd = gzipDataPipe[1];
+ } else bb_perror_msg_and_die("vfork gzip");
+ }
+
+ tbInfo.excludeList = exclude;
+
+ /* Read the directory/files and iterate over them one at a time */
+ while (include) {
+ if (!recursive_action(include->data, TRUE, dereferenceFlag,
+ FALSE, writeFileToTarball, writeFileToTarball, &tbInfo, 0))
+ {
+ errorFlag = TRUE;
+ }
+ include = include->link;
+ }
+ /* Write two empty blocks to the end of the archive */
+ memset(bb_common_bufsiz1, 0, 2*TAR_BLOCK_SIZE);
+ xwrite(tbInfo.tarFd, bb_common_bufsiz1, 2*TAR_BLOCK_SIZE);
+
+ /* To be pedantically correct, we would check if the tarball
+ * is smaller than 20 tar blocks, and pad it if it was smaller,
+ * but that isn't necessary for GNU tar interoperability, and
+ * so is considered a waste of space */
+
+ /* Close so the child process (if any) will exit */
+ close(tbInfo.tarFd);
+
+ /* Hang up the tools, close up shop, head home */
+ if (ENABLE_FEATURE_CLEAN_UP)
+ freeHardLinkInfo(&tbInfo.hlInfoHead);
+
+ if (errorFlag)
+ bb_error_msg("error exit delayed from previous errors");
+
+ if (gzipPid) {
+ int status;
+ if (waitpid(gzipPid, &status, 0) == -1)
+ bb_perror_msg("waitpid");
+ else if (!WIFEXITED(status) || WEXITSTATUS(status))
+ /* gzip was killed or has exited with nonzero! */
+ errorFlag = TRUE;
+ }
+ return errorFlag;
+}
+#else
+int writeTarFile(const int tar_fd, const int verboseFlag,
+ const unsigned long dereferenceFlag, const llist_t *include,
+ const llist_t *exclude, const int gzip);
+#endif /* FEATURE_TAR_CREATE */
+
+#if ENABLE_FEATURE_TAR_FROM
+static llist_t *append_file_list_to_list(llist_t *list)
+{
+ FILE *src_stream;
+ llist_t *cur = list;
+ llist_t *tmp;
+ char *line;
+ llist_t *newlist = NULL;
+
+ while (cur) {
+ src_stream = xfopen(cur->data, "r");
+ tmp = cur;
+ cur = cur->link;
+ free(tmp);
+ while ((line = xmalloc_getline(src_stream)) != NULL) {
+ /* kill trailing '/' unless the string is just "/" */
+ char *cp = last_char_is(line, '/');
+ if (cp > line)
+ *cp = '\0';
+ llist_add_to(&newlist, line);
+ }
+ fclose(src_stream);
+ }
+ return newlist;
+}
+#else
+#define append_file_list_to_list(x) 0
+#endif
+
+#if ENABLE_FEATURE_TAR_COMPRESS
+static char get_header_tar_Z(archive_handle_t *archive_handle)
+{
+ /* Can't lseek over pipes */
+ archive_handle->seek = seek_by_read;
+
+ /* do the decompression, and cleanup */
+ if (xread_char(archive_handle->src_fd) != 0x1f
+ || xread_char(archive_handle->src_fd) != 0x9d
+ ) {
+ bb_error_msg_and_die("invalid magic");
+ }
+
+ archive_handle->src_fd = open_transformer(archive_handle->src_fd, uncompress);
+ archive_handle->offset = 0;
+ while (get_header_tar(archive_handle) == EXIT_SUCCESS)
+ /* nothing */;
+
+ /* Can only do one file at a time */
+ return EXIT_FAILURE;
+}
+#else
+#define get_header_tar_Z NULL
+#endif
+
+#ifdef CHECK_FOR_CHILD_EXITCODE
+/* Looks like it isn't needed - tar detects malformed (truncated)
+ * archive if e.g. bunzip2 fails */
+static int child_error;
+
+static void handle_SIGCHLD(int status)
+{
+ /* Actually, 'status' is a signo. We reuse it for other needs */
+
+ /* Wait for any child without blocking */
+ if (waitpid(-1, &status, WNOHANG) < 0)
+ /* wait failed?! I'm confused... */
+ return;
+
+ if (WIFEXITED(status) && WEXITSTATUS(status)==0)
+ /* child exited with 0 */
+ return;
+ /* Cannot happen?
+ if(!WIFSIGNALED(status) && !WIFEXITED(status)) return; */
+ child_error = 1;
+}
+#endif
+
+enum {
+ OPTBIT_KEEP_OLD = 7,
+ USE_FEATURE_TAR_CREATE( OPTBIT_CREATE ,)
+ USE_FEATURE_TAR_CREATE( OPTBIT_DEREFERENCE ,)
+ USE_FEATURE_TAR_BZIP2( OPTBIT_BZIP2 ,)
+ USE_FEATURE_TAR_LZMA( OPTBIT_LZMA ,)
+ USE_FEATURE_TAR_FROM( OPTBIT_INCLUDE_FROM,)
+ USE_FEATURE_TAR_FROM( OPTBIT_EXCLUDE_FROM,)
+ USE_FEATURE_TAR_GZIP( OPTBIT_GZIP ,)
+ USE_FEATURE_TAR_COMPRESS(OPTBIT_COMPRESS ,)
+ OPTBIT_NOPRESERVE_OWN,
+ OPTBIT_NOPRESERVE_PERM,
+ OPT_TEST = 1 << 0, // t
+ OPT_EXTRACT = 1 << 1, // x
+ OPT_BASEDIR = 1 << 2, // C
+ OPT_TARNAME = 1 << 3, // f
+ OPT_2STDOUT = 1 << 4, // O
+ OPT_P = 1 << 5, // p
+ OPT_VERBOSE = 1 << 6, // v
+ OPT_KEEP_OLD = 1 << 7, // k
+ OPT_CREATE = USE_FEATURE_TAR_CREATE( (1<<OPTBIT_CREATE )) + 0, // c
+ OPT_DEREFERENCE = USE_FEATURE_TAR_CREATE( (1<<OPTBIT_DEREFERENCE )) + 0, // h
+ OPT_BZIP2 = USE_FEATURE_TAR_BZIP2( (1<<OPTBIT_BZIP2 )) + 0, // j
+ OPT_LZMA = USE_FEATURE_TAR_LZMA( (1<<OPTBIT_LZMA )) + 0, // a
+ OPT_INCLUDE_FROM = USE_FEATURE_TAR_FROM( (1<<OPTBIT_INCLUDE_FROM)) + 0, // T
+ OPT_EXCLUDE_FROM = USE_FEATURE_TAR_FROM( (1<<OPTBIT_EXCLUDE_FROM)) + 0, // X
+ OPT_GZIP = USE_FEATURE_TAR_GZIP( (1<<OPTBIT_GZIP )) + 0, // z
+ OPT_COMPRESS = USE_FEATURE_TAR_COMPRESS((1<<OPTBIT_COMPRESS )) + 0, // Z
+ OPT_NOPRESERVE_OWN = 1 << OPTBIT_NOPRESERVE_OWN , // no-same-owner
+ OPT_NOPRESERVE_PERM = 1 << OPTBIT_NOPRESERVE_PERM, // no-same-permissions
+};
+#if ENABLE_FEATURE_TAR_LONG_OPTIONS
+static const struct option tar_long_options[] = {
+ { "list", 0, NULL, 't' },
+ { "extract", 0, NULL, 'x' },
+ { "directory", 1, NULL, 'C' },
+ { "file", 1, NULL, 'f' },
+ { "to-stdout", 0, NULL, 'O' },
+ { "same-permissions", 0, NULL, 'p' },
+ { "verbose", 0, NULL, 'v' },
+ { "keep-old", 0, NULL, 'k' },
+# if ENABLE_FEATURE_TAR_CREATE
+ { "create", 0, NULL, 'c' },
+ { "dereference", 0, NULL, 'h' },
+# endif
+# if ENABLE_FEATURE_TAR_BZIP2
+ { "bzip2", 0, NULL, 'j' },
+# endif
+# if ENABLE_FEATURE_TAR_LZMA
+ { "lzma", 0, NULL, 'a' },
+# endif
+# if ENABLE_FEATURE_TAR_FROM
+ { "files-from", 1, NULL, 'T' },
+ { "exclude-from", 1, NULL, 'X' },
+# endif
+# if ENABLE_FEATURE_TAR_GZIP
+ { "gzip", 0, NULL, 'z' },
+# endif
+# if ENABLE_FEATURE_TAR_COMPRESS
+ { "compress", 0, NULL, 'Z' },
+# endif
+ { "no-same-owner", 0, NULL, 0xfd },
+ { "no-same-permissions",0, NULL, 0xfe },
+ /* --exclude takes next bit position in option mask, */
+ /* therefore we have to either put it _after_ --no-same-perm */
+ /* or add OPT[BIT]_EXCLUDE before OPT[BIT]_NOPRESERVE_OWN */
+# if ENABLE_FEATURE_TAR_FROM
+ { "exclude", 1, NULL, 0xff },
+# endif
+ { 0, 0, 0, 0 }
+};
+#endif
+
+int tar_main(int argc, char **argv);
+int tar_main(int argc, char **argv)
+{
+ char (*get_header_ptr)(archive_handle_t *) = get_header_tar;
+ archive_handle_t *tar_handle;
+ char *base_dir = NULL;
+ const char *tar_filename = "-";
+ unsigned opt;
+ int verboseFlag = 0;
+#if ENABLE_FEATURE_TAR_LONG_OPTIONS && ENABLE_FEATURE_TAR_FROM
+ llist_t *excludes = NULL;
+#endif
+
+ /* Initialise default values */
+ tar_handle = init_handle();
+ tar_handle->flags = ARCHIVE_CREATE_LEADING_DIRS
+ | ARCHIVE_PRESERVE_DATE
+ | ARCHIVE_EXTRACT_UNCONDITIONAL;
+
+ /* Prepend '-' to the first argument if required */
+ opt_complementary = "--:" // first arg is options
+ "tt:vv:" // count -t,-v
+ "?:" // bail out with usage instead of error return
+ "X::T::" // cumulative lists
+#if ENABLE_FEATURE_TAR_LONG_OPTIONS && ENABLE_FEATURE_TAR_FROM
+ "\xff::" // cumulative lists for --exclude
+#endif
+ USE_FEATURE_TAR_CREATE("c:") "t:x:" // at least one of these is reqd
+ USE_FEATURE_TAR_CREATE("c--tx:t--cx:x--ct") // mutually exclusive
+ SKIP_FEATURE_TAR_CREATE("t--x:x--t"); // mutually exclusive
+#if ENABLE_FEATURE_TAR_LONG_OPTIONS
+ applet_long_options = tar_long_options;
+#endif
+ opt = getopt32(argc, argv,
+ "txC:f:Opvk"
+ USE_FEATURE_TAR_CREATE( "ch" )
+ USE_FEATURE_TAR_BZIP2( "j" )
+ USE_FEATURE_TAR_LZMA( "a" )
+ USE_FEATURE_TAR_FROM( "T:X:")
+ USE_FEATURE_TAR_GZIP( "z" )
+ USE_FEATURE_TAR_COMPRESS("Z" )
+ , &base_dir // -C dir
+ , &tar_filename // -f filename
+ USE_FEATURE_TAR_FROM(, &(tar_handle->accept)) // T
+ USE_FEATURE_TAR_FROM(, &(tar_handle->reject)) // X
+#if ENABLE_FEATURE_TAR_LONG_OPTIONS && ENABLE_FEATURE_TAR_FROM
+ , &excludes // --exclude
+#endif
+ , &verboseFlag // combined count for -t and -v
+ , &verboseFlag // combined count for -t and -v
+ );
+
+ if (verboseFlag) tar_handle->action_header = header_verbose_list;
+ if (verboseFlag == 1) tar_handle->action_header = header_list;
+
+ if (opt & OPT_EXTRACT)
+ tar_handle->action_data = data_extract_all;
+
+ if (opt & OPT_2STDOUT)
+ tar_handle->action_data = data_extract_to_stdout;
+
+ if (opt & OPT_KEEP_OLD)
+ tar_handle->flags &= ~ARCHIVE_EXTRACT_UNCONDITIONAL;
+
+ if (opt & OPT_NOPRESERVE_OWN)
+ tar_handle->flags |= ARCHIVE_NOPRESERVE_OWN;
+
+ if (opt & OPT_NOPRESERVE_PERM)
+ tar_handle->flags |= ARCHIVE_NOPRESERVE_PERM;
+
+ if (opt & OPT_GZIP)
+ get_header_ptr = get_header_tar_gz;
+
+ if (opt & OPT_BZIP2)
+ get_header_ptr = get_header_tar_bz2;
+
+ if (opt & OPT_LZMA)
+ get_header_ptr = get_header_tar_lzma;
+
+ if (opt & OPT_COMPRESS)
+ get_header_ptr = get_header_tar_Z;
+
+#if ENABLE_FEATURE_TAR_FROM
+ tar_handle->reject = append_file_list_to_list(tar_handle->reject);
+#if ENABLE_FEATURE_TAR_LONG_OPTIONS
+ /* Append excludes to reject */
+ while (excludes) {
+ llist_t *next = excludes->link;
+ excludes->link = tar_handle->reject;
+ tar_handle->reject = excludes;
+ excludes = next;
+ }
+#endif
+ tar_handle->accept = append_file_list_to_list(tar_handle->accept);
+#endif
+
+ /* Check if we are reading from stdin */
+ if (argv[optind] && *argv[optind] == '-') {
+ /* Default is to read from stdin, so just skip to next arg */
+ optind++;
+ }
+
+ /* Setup an array of filenames to work with */
+ /* TODO: This is the same as in ar, separate function ? */
+ while (optind < argc) {
+ /* kill trailing '/' unless the string is just "/" */
+ char *cp = last_char_is(argv[optind], '/');
+ if (cp > argv[optind])
+ *cp = '\0';
+ llist_add_to(&tar_handle->accept, argv[optind]);
+ optind++;
+ }
+ tar_handle->accept = llist_rev(tar_handle->accept);
+
+ if (tar_handle->accept || tar_handle->reject)
+ tar_handle->filter = filter_accept_reject_list;
+
+ /* Open the tar file */
+ {
+ FILE *tar_stream;
+ int flags;
+
+ if (opt & OPT_CREATE) {
+ /* Make sure there is at least one file to tar up. */
+ if (tar_handle->accept == NULL)
+ bb_error_msg_and_die("empty archive");
+
+ tar_stream = stdout;
+ /* Mimicking GNU tar 1.15.1: */
+ flags = O_WRONLY|O_CREAT|O_TRUNC;
+ /* was doing unlink; open(O_WRONLY|O_CREAT|O_EXCL); why? */
+ } else {
+ tar_stream = stdin;
+ flags = O_RDONLY;
+ }
+
+ if (LONE_DASH(tar_filename)) {
+ tar_handle->src_fd = fileno(tar_stream);
+ tar_handle->seek = seek_by_read;
+ } else {
+ tar_handle->src_fd = xopen(tar_filename, flags);
+ }
+ }
+
+ if (base_dir)
+ xchdir(base_dir);
+
+#ifdef CHECK_FOR_CHILD_EXITCODE
+ /* We need to know whether child (gzip/bzip/etc) exits abnormally */
+ signal(SIGCHLD, handle_SIGCHLD);
+#endif
+
+ /* create an archive */
+ if (opt & OPT_CREATE) {
+ int zipMode = 0;
+ if (ENABLE_FEATURE_TAR_GZIP && get_header_ptr == get_header_tar_gz)
+ zipMode = 1;
+ if (ENABLE_FEATURE_TAR_BZIP2 && get_header_ptr == get_header_tar_bz2)
+ zipMode = 2;
+ /* NB: writeTarFile() closes tar_handle->src_fd */
+ return writeTarFile(tar_handle->src_fd, verboseFlag, opt & OPT_DEREFERENCE,
+ tar_handle->accept,
+ tar_handle->reject, zipMode);
+ }
+
+ while (get_header_ptr(tar_handle) == EXIT_SUCCESS)
+ /* nothing */;
+
+ /* Check that every file that should have been extracted was */
+ while (tar_handle->accept) {
+ if (!find_list_entry(tar_handle->reject, tar_handle->accept->data)
+ && !find_list_entry(tar_handle->passed, tar_handle->accept->data)
+ ) {
+ bb_error_msg_and_die("%s: not found in archive",
+ tar_handle->accept->data);
+ }
+ tar_handle->accept = tar_handle->accept->link;
+ }
+ if (ENABLE_FEATURE_CLEAN_UP /* && tar_handle->src_fd != STDIN_FILENO */)
+ close(tar_handle->src_fd);
+
+ return EXIT_SUCCESS;
+}
diff --git a/i/pc104/initrd/conf/busybox/archival/unzip.c b/i/pc104/initrd/conf/busybox/archival/unzip.c
new file mode 100644
index 0000000..5e63170
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/archival/unzip.c
@@ -0,0 +1,376 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini unzip implementation for busybox
+ *
+ * Copyright (C) 2004 by Ed Clark
+ *
+ * Loosely based on original busybox unzip applet by Laurence Anderson.
+ * All options and features should work in this version.
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ */
+
+/* For reference see
+ * http://www.pkware.com/company/standards/appnote/
+ * http://www.info-zip.org/pub/infozip/doc/appnote-iz-latest.zip
+ */
+
+/* TODO
+ * Endian issues
+ * Zip64 + other methods
+ * Improve handling of zip format, ie.
+ * - deferred CRC, comp. & uncomp. lengths (zip header flags bit 3)
+ * - unix file permissions, etc.
+ * - central directory
+ */
+
+#include "busybox.h"
+#include "unarchive.h"
+
+#define ZIP_FILEHEADER_MAGIC SWAP_LE32(0x04034b50)
+#define ZIP_CDS_MAGIC SWAP_LE32(0x02014b50)
+#define ZIP_CDS_END_MAGIC SWAP_LE32(0x06054b50)
+#define ZIP_DD_MAGIC SWAP_LE32(0x08074b50)
+
+typedef union {
+ unsigned char raw[26];
+ struct {
+ unsigned short version; /* 0-1 */
+ unsigned short flags; /* 2-3 */
+ unsigned short method; /* 4-5 */
+ unsigned short modtime; /* 6-7 */
+ unsigned short moddate; /* 8-9 */
+ unsigned int crc32 ATTRIBUTE_PACKED; /* 10-13 */
+ unsigned int cmpsize ATTRIBUTE_PACKED; /* 14-17 */
+ unsigned int ucmpsize ATTRIBUTE_PACKED; /* 18-21 */
+ unsigned short filename_len; /* 22-23 */
+ unsigned short extra_len; /* 24-25 */
+ } formatted ATTRIBUTE_PACKED;
+} zip_header_t;
+
+static void unzip_skip(int fd, off_t skip)
+{
+ if (lseek(fd, skip, SEEK_CUR) == (off_t)-1) {
+ if (errno != ESPIPE)
+ bb_error_msg_and_die("seek failure");
+ bb_copyfd_exact_size(fd, -1, skip);
+ }
+}
+
+static void unzip_create_leading_dirs(char *fn)
+{
+ /* Create all leading directories */
+ char *name = xstrdup(fn);
+ if (bb_make_directory(dirname(name), 0777, FILEUTILS_RECUR)) {
+ bb_error_msg_and_die("exiting"); /* bb_make_directory is noisy */
+ }
+ free(name);
+}
+
+static int unzip_extract(zip_header_t *zip_header, int src_fd, int dst_fd)
+{
+ if (zip_header->formatted.method == 0) {
+ /* Method 0 - stored (not compressed) */
+ off_t size = zip_header->formatted.ucmpsize;
+ if (size)
+ bb_copyfd_exact_size(src_fd, dst_fd, size);
+ } else {
+ /* Method 8 - inflate */
+ inflate_unzip_result res;
+ /* err = */ inflate_unzip(&res, zip_header->formatted.cmpsize, src_fd, dst_fd);
+// we should check for -1 error return
+ /* Validate decompression - crc */
+ if (zip_header->formatted.crc32 != (res.crc ^ 0xffffffffL)) {
+ bb_error_msg("invalid compressed data--%s error", "crc");
+ return 1;
+ }
+ /* Validate decompression - size */
+ if (zip_header->formatted.ucmpsize != res.bytes_out) {
+ bb_error_msg("invalid compressed data--%s error", "length");
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int unzip_main(int argc, char **argv);
+int unzip_main(int argc, char **argv)
+{
+ zip_header_t zip_header;
+ enum {v_silent, v_normal, v_list} verbosity = v_normal;
+ enum {o_prompt, o_never, o_always} overwrite = o_prompt;
+ unsigned int total_size = 0;
+ unsigned int total_entries = 0;
+ int src_fd = -1, dst_fd = -1;
+ char *src_fn = NULL, *dst_fn = NULL;
+ llist_t *zaccept = NULL;
+ llist_t *zreject = NULL;
+ char *base_dir = NULL;
+ int failed, i, opt, opt_range = 0, list_header_done = 0;
+ char key_buf[512];
+ struct stat stat_buf;
+
+ while ((opt = getopt(argc, argv, "-d:lnopqx")) != -1) {
+ switch (opt_range) {
+ case 0: /* Options */
+ switch (opt) {
+ case 'l': /* List */
+ verbosity = v_list;
+ break;
+
+ case 'n': /* Never overwrite existing files */
+ overwrite = o_never;
+ break;
+
+ case 'o': /* Always overwrite existing files */
+ overwrite = o_always;
+ break;
+
+ case 'p': /* Extract files to stdout and fall through to set verbosity */
+ dst_fd = STDOUT_FILENO;
+
+ case 'q': /* Be quiet */
+ verbosity = (verbosity == v_normal) ? v_silent : verbosity;
+ break;
+
+ case 1 : /* The zip file */
+ src_fn = xstrndup(optarg, strlen(optarg)+4);
+ opt_range++;
+ break;
+
+ default:
+ bb_show_usage();
+
+ }
+ break;
+
+ case 1: /* Include files */
+ if (opt == 1) {
+ llist_add_to(&zaccept, optarg);
+
+ } else if (opt == 'd') {
+ base_dir = optarg;
+ opt_range += 2;
+
+ } else if (opt == 'x') {
+ opt_range++;
+
+ } else {
+ bb_show_usage();
+ }
+ break;
+
+ case 2 : /* Exclude files */
+ if (opt == 1) {
+ llist_add_to(&zreject, optarg);
+
+ } else if (opt == 'd') { /* Extract to base directory */
+ base_dir = optarg;
+ opt_range++;
+
+ } else {
+ bb_show_usage();
+ }
+ break;
+
+ default:
+ bb_show_usage();
+ }
+ }
+
+ if (src_fn == NULL) {
+ bb_show_usage();
+ }
+
+ /* Open input file */
+ if (LONE_DASH(src_fn)) {
+ src_fd = STDIN_FILENO;
+ /* Cannot use prompt mode since zip data is arriving on STDIN */
+ overwrite = (overwrite == o_prompt) ? o_never : overwrite;
+ } else {
+ static const char *const extn[] = {"", ".zip", ".ZIP"};
+ int orig_src_fn_len = strlen(src_fn);
+ for (i = 0; (i < 3) && (src_fd == -1); i++) {
+ strcpy(src_fn + orig_src_fn_len, extn[i]);
+ src_fd = open(src_fn, O_RDONLY);
+ }
+ if (src_fd == -1) {
+ src_fn[orig_src_fn_len] = 0;
+ bb_error_msg_and_die("cannot open %s, %s.zip, %s.ZIP", src_fn, src_fn, src_fn);
+ }
+ }
+
+ /* Change dir if necessary */
+ if (base_dir)
+ xchdir(base_dir);
+
+ if (verbosity != v_silent)
+ printf("Archive: %s\n", src_fn);
+
+ failed = 0;
+
+ while (1) {
+ unsigned int magic;
+
+ /* Check magic number */
+ xread(src_fd, &magic, 4);
+ if (magic == ZIP_CDS_MAGIC) {
+ break;
+ } else if (magic != ZIP_FILEHEADER_MAGIC) {
+ bb_error_msg_and_die("invalid zip magic %08X", magic);
+ }
+
+ /* Read the file header */
+ xread(src_fd, zip_header.raw, 26);
+ zip_header.formatted.version = SWAP_LE32(zip_header.formatted.version);
+ zip_header.formatted.flags = SWAP_LE32(zip_header.formatted.flags);
+ zip_header.formatted.method = SWAP_LE32(zip_header.formatted.method);
+ zip_header.formatted.modtime = SWAP_LE32(zip_header.formatted.modtime);
+ zip_header.formatted.moddate = SWAP_LE32(zip_header.formatted.moddate);
+ zip_header.formatted.crc32 = SWAP_LE32(zip_header.formatted.crc32);
+ zip_header.formatted.cmpsize = SWAP_LE32(zip_header.formatted.cmpsize);
+ zip_header.formatted.ucmpsize = SWAP_LE32(zip_header.formatted.ucmpsize);
+ zip_header.formatted.filename_len = SWAP_LE32(zip_header.formatted.filename_len);
+ zip_header.formatted.extra_len = SWAP_LE32(zip_header.formatted.extra_len);
+ if ((zip_header.formatted.method != 0) && (zip_header.formatted.method != 8)) {
+ bb_error_msg_and_die("unsupported compression method %d", zip_header.formatted.method);
+ }
+
+ /* Read filename */
+ free(dst_fn);
+ dst_fn = xzalloc(zip_header.formatted.filename_len + 1);
+ xread(src_fd, dst_fn, zip_header.formatted.filename_len);
+
+ /* Skip extra header bytes */
+ unzip_skip(src_fd, zip_header.formatted.extra_len);
+
+ if ((verbosity == v_list) && !list_header_done){
+ puts(" Length Date Time Name\n"
+ " -------- ---- ---- ----");
+ list_header_done = 1;
+ }
+
+ /* Filter zip entries */
+ if (find_list_entry(zreject, dst_fn) ||
+ (zaccept && !find_list_entry(zaccept, dst_fn))) { /* Skip entry */
+ i = 'n';
+
+ } else { /* Extract entry */
+ total_size += zip_header.formatted.ucmpsize;
+
+ if (verbosity == v_list) { /* List entry */
+ unsigned int dostime = zip_header.formatted.modtime | (zip_header.formatted.moddate << 16);
+ printf("%9u %02u-%02u-%02u %02u:%02u %s\n",
+ zip_header.formatted.ucmpsize,
+ (dostime & 0x01e00000) >> 21,
+ (dostime & 0x001f0000) >> 16,
+ (((dostime & 0xfe000000) >> 25) + 1980) % 100,
+ (dostime & 0x0000f800) >> 11,
+ (dostime & 0x000007e0) >> 5,
+ dst_fn);
+ total_entries++;
+ i = 'n';
+ } else if (dst_fd == STDOUT_FILENO) { /* Extracting to STDOUT */
+ i = -1;
+ } else if (last_char_is(dst_fn, '/')) { /* Extract directory */
+ if (stat(dst_fn, &stat_buf) == -1) {
+ if (errno != ENOENT) {
+ bb_perror_msg_and_die("cannot stat '%s'",dst_fn);
+ }
+ if (verbosity == v_normal) {
+ printf(" creating: %s\n", dst_fn);
+ }
+ unzip_create_leading_dirs(dst_fn);
+ if (bb_make_directory(dst_fn, 0777, 0)) {
+ bb_error_msg_and_die("exiting");
+ }
+ } else {
+ if (!S_ISDIR(stat_buf.st_mode)) {
+ bb_error_msg_and_die("'%s' exists but is not directory", dst_fn);
+ }
+ }
+ i = 'n';
+
+ } else { /* Extract file */
+ _check_file:
+ if (stat(dst_fn, &stat_buf) == -1) { /* File does not exist */
+ if (errno != ENOENT) {
+ bb_perror_msg_and_die("cannot stat '%s'",dst_fn);
+ }
+ i = 'y';
+ } else { /* File already exists */
+ if (overwrite == o_never) {
+ i = 'n';
+ } else if (S_ISREG(stat_buf.st_mode)) { /* File is regular file */
+ if (overwrite == o_always) {
+ i = 'y';
+ } else {
+ printf("replace %s? [y]es, [n]o, [A]ll, [N]one, [r]ename: ", dst_fn);
+ if (!fgets(key_buf, 512, stdin)) {
+ bb_perror_msg_and_die("cannot read input");
+ }
+ i = key_buf[0];
+ }
+ } else { /* File is not regular file */
+ bb_error_msg_and_die("'%s' exists but is not regular file",dst_fn);
+ }
+ }
+ }
+ }
+
+ switch (i) {
+ case 'A':
+ overwrite = o_always;
+ case 'y': /* Open file and fall into unzip */
+ unzip_create_leading_dirs(dst_fn);
+ dst_fd = xopen(dst_fn, O_WRONLY | O_CREAT | O_TRUNC);
+ case -1: /* Unzip */
+ if (verbosity == v_normal) {
+ printf(" inflating: %s\n", dst_fn);
+ }
+ if (unzip_extract(&zip_header, src_fd, dst_fd)) {
+ failed = 1;
+ }
+ if (dst_fd != STDOUT_FILENO) {
+ /* closing STDOUT is potentially bad for future business */
+ close(dst_fd);
+ }
+ break;
+
+ case 'N':
+ overwrite = o_never;
+ case 'n':
+ /* Skip entry data */
+ unzip_skip(src_fd, zip_header.formatted.cmpsize);
+ break;
+
+ case 'r':
+ /* Prompt for new name */
+ printf("new name: ");
+ if (!fgets(key_buf, 512, stdin)) {
+ bb_perror_msg_and_die("cannot read input");
+ }
+ free(dst_fn);
+ dst_fn = xstrdup(key_buf);
+ chomp(dst_fn);
+ goto _check_file;
+
+ default:
+ printf("error: invalid response [%c]\n",(char)i);
+ goto _check_file;
+ }
+
+ /* Data descriptor section */
+ if (zip_header.formatted.flags & 4) {
+ /* skip over duplicate crc, compressed size and uncompressed size */
+ unzip_skip(src_fd, 12);
+ }
+ }
+
+ if (verbosity == v_list) {
+ printf(" -------- -------\n"
+ "%9d %d files\n", total_size, total_entries);
+ }
+
+ return failed;
+}
diff --git a/i/pc104/initrd/conf/busybox/busybox b/i/pc104/initrd/conf/busybox/busybox
new file mode 100755
index 0000000..942a5b9
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/busybox
Binary files differ
diff --git a/i/pc104/initrd/conf/busybox/busybox.links b/i/pc104/initrd/conf/busybox/busybox.links
new file mode 100644
index 0000000..de85a50
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/busybox.links
@@ -0,0 +1,18 @@
+/usr/bin/[
+/usr/bin/[[
+/bin/ash
+/usr/sbin/chroot
+/bin/echo
+/sbin/halt
+/bin/hostname
+/sbin/insmod
+/bin/mount
+/usr/bin/nc
+/sbin/pivot_root
+/sbin/reboot
+/bin/sh
+/bin/sleep
+/bin/sync
+/usr/bin/test
+/sbin/udhcpc
+/bin/umount
diff --git a/i/pc104/initrd/conf/busybox/console-tools/Config.in b/i/pc104/initrd/conf/busybox/console-tools/Config.in
new file mode 100644
index 0000000..4a5710d
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/console-tools/Config.in
@@ -0,0 +1,105 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+menu "Console Utilities"
+
+config CHVT
+ bool "chvt"
+ default n
+ help
+ This program is used to change to another terminal.
+ Example: chvt 4 (change to terminal /dev/tty4)
+
+config CLEAR
+ bool "clear"
+ default n
+ help
+ This program clears the terminal screen.
+
+config DEALLOCVT
+ bool "deallocvt"
+ default n
+ help
+ This program deallocates unused virtual consoles.
+
+config DUMPKMAP
+ bool "dumpkmap"
+ default n
+ help
+ This program dumps the kernel's keyboard translation table to
+ stdout, in binary format. You can then use loadkmap to load it.
+
+config LOADFONT
+ bool "loadfont"
+ default n
+ help
+ This program loads a console font from standard input.
+
+config LOADKMAP
+ bool "loadkmap"
+ default n
+ help
+ This program loads a keyboard translation table from
+ standard input.
+
+config OPENVT
+ bool "openvt"
+ default n
+ help
+ This program is used to start a command on an unused
+ virtual terminal.
+
+config RESET
+ bool "reset"
+ default n
+ help
+ This program is used to reset the terminal screen, if it
+ gets messed up.
+
+config RESIZE
+ bool "resize"
+ default n
+ help
+ This program is used to (re)set the width and height of your current
+ terminal.
+
+config FEATURE_RESIZE_PRINT
+ bool "print environment variables"
+ default n
+ depends on RESIZE
+ help
+ Prints the newly set size (number of columns and rows) of
+ the terminal.
+ E.g.:
+ COLUMNS=80;LINES=44;export COLUMNS LINES;
+
+config SETCONSOLE
+ bool "setconsole"
+ default n
+ help
+ This program redirects the system console to another device,
+ like the current tty while logged in via telnet.
+
+config FEATURE_SETCONSOLE_LONG_OPTIONS
+ bool "Enable long options"
+ default n
+ depends on SET_CONSOLE && GETOPT_LONG
+ help
+ Support long options for the setconsole applet.
+
+config SETKEYCODES
+ bool "setkeycodes"
+ default n
+ help
+ This program loads entries into the kernel's scancode-to-keycode
+ map, allowing unusual keyboards to generate usable keycodes.
+
+config SETLOGCONS
+ bool "setlogcons"
+ default n
+ help
+ This program redirects the output console of kernel messages.
+
+endmenu
diff --git a/i/pc104/initrd/conf/busybox/console-tools/Kbuild b/i/pc104/initrd/conf/busybox/console-tools/Kbuild
new file mode 100644
index 0000000..a55bc08
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/console-tools/Kbuild
@@ -0,0 +1,19 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under the GPL v2, see the file LICENSE in this tarball.
+
+lib-y:=
+lib-$(CONFIG_CHVT) += chvt.o
+lib-$(CONFIG_CLEAR) += clear.o
+lib-$(CONFIG_DEALLOCVT) += deallocvt.o
+lib-$(CONFIG_DUMPKMAP) += dumpkmap.o
+lib-$(CONFIG_SETCONSOLE) += setconsole.o
+lib-$(CONFIG_LOADFONT) += loadfont.o
+lib-$(CONFIG_LOADKMAP) += loadkmap.o
+lib-$(CONFIG_OPENVT) += openvt.o
+lib-$(CONFIG_RESET) += reset.o
+lib-$(CONFIG_RESIZE) += resize.o
+lib-$(CONFIG_SETKEYCODES) += setkeycodes.o
+lib-$(CONFIG_SETLOGCONS) += setlogcons.o
diff --git a/i/pc104/initrd/conf/busybox/console-tools/chvt.c b/i/pc104/initrd/conf/busybox/console-tools/chvt.c
new file mode 100644
index 0000000..5090811
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/console-tools/chvt.c
@@ -0,0 +1,34 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini chvt implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "busybox.h"
+
+/* From <linux/vt.h> */
+enum {
+ VT_ACTIVATE = 0x5606, /* make vt active */
+ VT_WAITACTIVE = 0x5607 /* wait for vt active */
+};
+
+int chvt_main(int argc, char **argv);
+int chvt_main(int argc, char **argv)
+{
+ int fd, num;
+
+ if (argc != 2) {
+ bb_show_usage();
+ }
+
+ fd = get_console_fd();
+ num = xatoul_range(argv[1], 1, 63);
+ if ((-1 == ioctl(fd, VT_ACTIVATE, num))
+ || (-1 == ioctl(fd, VT_WAITACTIVE, num))) {
+ bb_perror_msg_and_die("ioctl");
+ }
+ return EXIT_SUCCESS;
+}
diff --git a/i/pc104/initrd/conf/busybox/console-tools/clear.c b/i/pc104/initrd/conf/busybox/console-tools/clear.c
new file mode 100644
index 0000000..8e34d6d
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/console-tools/clear.c
@@ -0,0 +1,22 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini clear implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ *
+ */
+
+/* no options, no getopt */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "busybox.h"
+
+
+int clear_main(int argc, char **argv);
+int clear_main(int argc, char **argv)
+{
+ return printf("\033[H\033[J") != 6;
+}
diff --git a/i/pc104/initrd/conf/busybox/console-tools/deallocvt.c b/i/pc104/initrd/conf/busybox/console-tools/deallocvt.c
new file mode 100644
index 0000000..81ea68e
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/console-tools/deallocvt.c
@@ -0,0 +1,38 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Disallocate virtual terminal(s)
+ *
+ * Copyright (C) 2003 by Tito Ragusa <farmatito@tiscali.it>
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/* no options, no getopt */
+
+#include "busybox.h"
+
+/* From <linux/vt.h> */
+enum { VT_DISALLOCATE = 0x5608 }; /* free memory associated to vt */
+
+int deallocvt_main(int argc, char *argv[]);
+int deallocvt_main(int argc, char *argv[])
+{
+ /* num = 0 deallocate all unused consoles */
+ int num = 0;
+
+ switch (argc) {
+ case 2:
+ num = xatoul_range(argv[1], 1, 63);
+ /* Fallthrough */
+ case 1:
+ break;
+ default:
+ bb_show_usage();
+ }
+
+ if (-1 == ioctl(get_console_fd(), VT_DISALLOCATE, num)) {
+ bb_perror_msg_and_die("VT_DISALLOCATE");
+ }
+ return EXIT_SUCCESS;
+}
diff --git a/i/pc104/initrd/conf/busybox/console-tools/dumpkmap.c b/i/pc104/initrd/conf/busybox/console-tools/dumpkmap.c
new file mode 100644
index 0000000..0c19148
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/console-tools/dumpkmap.c
@@ -0,0 +1,66 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini dumpkmap implementation for busybox
+ *
+ * Copyright (C) Arne Bernin <arne@matrix.loopback.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ *
+ */
+
+#include "busybox.h"
+
+/* From <linux/kd.h> */
+struct kbentry {
+ unsigned char kb_table;
+ unsigned char kb_index;
+ unsigned short kb_value;
+};
+#define KDGKBENT 0x4B46 /* gets one entry in translation table */
+
+/* From <linux/keyboard.h> */
+#define NR_KEYS 128
+#define MAX_NR_KEYMAPS 256
+
+int dumpkmap_main(int argc, char **argv);
+int dumpkmap_main(int argc, char **argv)
+{
+ struct kbentry ke;
+ int i, j, fd;
+ char flags[MAX_NR_KEYMAPS];
+
+ if (argc >= 2 && argv[1][0] == '-')
+ bb_show_usage();
+
+ fd = xopen(CURRENT_VC, O_RDWR);
+
+ write(1, "bkeymap", 7);
+
+ /* Here we want to set everything to 0 except for indexes:
+ * [0-2] [4-6] [8-10] [12] */
+ memset(flags, 0x00, MAX_NR_KEYMAPS);
+ memset(flags, 0x01, 13);
+ flags[3] = flags[7] = flags[11] = 0;
+
+ /* dump flags */
+ write(1, flags, MAX_NR_KEYMAPS);
+
+ for (i = 0; i < MAX_NR_KEYMAPS; i++) {
+ if (flags[i] == 1) {
+ for (j = 0; j < NR_KEYS; j++) {
+ ke.kb_index = j;
+ ke.kb_table = i;
+ if (ioctl(fd, KDGKBENT, &ke) < 0) {
+ bb_perror_msg("ioctl failed with %s, %s, %p",
+ (char *)&ke.kb_index,
+ (char *)&ke.kb_table,
+ &ke.kb_value);
+ } else {
+ write(1, (void*)&ke.kb_value, 2);
+ }
+ }
+ }
+ }
+ close(fd);
+ return EXIT_SUCCESS;
+}
diff --git a/i/pc104/initrd/conf/busybox/console-tools/loadfont.c b/i/pc104/initrd/conf/busybox/console-tools/loadfont.c
new file mode 100644
index 0000000..f691b11
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/console-tools/loadfont.c
@@ -0,0 +1,194 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * loadfont.c - Eugene Crosser & Andries Brouwer
+ *
+ * Version 0.96bb
+ *
+ * Loads the console font, and possibly the corresponding screen map(s).
+ * (Adapted for busybox by Matej Vela.)
+ */
+#include "busybox.h"
+#include <sys/kd.h>
+
+enum {
+ PSF_MAGIC1 = 0x36,
+ PSF_MAGIC2 = 0x04,
+
+ PSF_MODE512 = 0x01,
+ PSF_MODEHASTAB = 0x02,
+ PSF_MAXMODE = 0x03,
+ PSF_SEPARATOR = 0xFFFF
+};
+
+struct psf_header {
+ unsigned char magic1, magic2; /* Magic number */
+ unsigned char mode; /* PSF font mode */
+ unsigned char charsize; /* Character size */
+};
+
+#define PSF_MAGIC_OK(x) ((x).magic1 == PSF_MAGIC1 && (x).magic2 == PSF_MAGIC2)
+
+static void loadnewfont(int fd);
+
+int loadfont_main(int argc, char **argv);
+int loadfont_main(int argc, char **argv)
+{
+ int fd;
+
+ if (argc != 1)
+ bb_show_usage();
+
+ fd = xopen(CURRENT_VC, O_RDWR);
+ loadnewfont(fd);
+
+ return EXIT_SUCCESS;
+}
+
+static void do_loadfont(int fd, unsigned char *inbuf, int unit, int fontsize)
+{
+ char buf[16384];
+ int i;
+
+ memset(buf, 0, sizeof(buf));
+
+ if (unit < 1 || unit > 32)
+ bb_error_msg_and_die("bad character size %d", unit);
+
+ for (i = 0; i < fontsize; i++)
+ memcpy(buf + (32 * i), inbuf + (unit * i), unit);
+
+#if defined( PIO_FONTX ) && !defined( __sparc__ )
+ {
+ struct consolefontdesc cfd;
+
+ cfd.charcount = fontsize;
+ cfd.charheight = unit;
+ cfd.chardata = buf;
+
+ if (ioctl(fd, PIO_FONTX, &cfd) == 0)
+ return; /* success */
+ bb_perror_msg("PIO_FONTX ioctl error (trying PIO_FONT)");
+ }
+#endif
+ if (ioctl(fd, PIO_FONT, buf))
+ bb_perror_msg_and_die("PIO_FONT ioctl error");
+}
+
+static void
+do_loadtable(int fd, unsigned char *inbuf, int tailsz, int fontsize)
+{
+ struct unimapinit advice;
+ struct unimapdesc ud;
+ struct unipair *up;
+ int ct = 0, maxct;
+ int glyph;
+ uint16_t unicode;
+
+ maxct = tailsz; /* more than enough */
+ up = xmalloc(maxct * sizeof(struct unipair));
+
+ for (glyph = 0; glyph < fontsize; glyph++) {
+ while (tailsz >= 2) {
+ unicode = (((uint16_t) inbuf[1]) << 8) + inbuf[0];
+ tailsz -= 2;
+ inbuf += 2;
+ if (unicode == PSF_SEPARATOR)
+ break;
+ up[ct].unicode = unicode;
+ up[ct].fontpos = glyph;
+ ct++;
+ }
+ }
+
+ /* Note: after PIO_UNIMAPCLR and before PIO_UNIMAP
+ this printf did not work on many kernels */
+
+ advice.advised_hashsize = 0;
+ advice.advised_hashstep = 0;
+ advice.advised_hashlevel = 0;
+ if (ioctl(fd, PIO_UNIMAPCLR, &advice)) {
+#ifdef ENOIOCTLCMD
+ if (errno == ENOIOCTLCMD) {
+ bb_error_msg("it seems this kernel is older than 1.1.92");
+ bb_error_msg_and_die("no Unicode mapping table loaded");
+ } else
+#endif
+ bb_perror_msg_and_die("PIO_UNIMAPCLR");
+ }
+ ud.entry_ct = ct;
+ ud.entries = up;
+ if (ioctl(fd, PIO_UNIMAP, &ud)) {
+ bb_perror_msg_and_die("PIO_UNIMAP");
+ }
+}
+
+static void loadnewfont(int fd)
+{
+ int unit;
+ unsigned char inbuf[32768]; /* primitive */
+ unsigned int inputlth, offset;
+
+ /*
+ * We used to look at the length of the input file
+ * with stat(); now that we accept compressed files,
+ * just read the entire file.
+ */
+ inputlth = fread(inbuf, 1, sizeof(inbuf), stdin);
+ if (ferror(stdin))
+ bb_perror_msg_and_die("error reading input font");
+ /* use malloc/realloc in case of giant files;
+ maybe these do not occur: 16kB for the font,
+ and 16kB for the map leaves 32 unicode values
+ for each font position */
+ if (!feof(stdin))
+ bb_perror_msg_and_die("font too large");
+
+ /* test for psf first */
+ {
+ struct psf_header psfhdr;
+ int fontsize;
+ int hastable;
+ unsigned int head0, head;
+
+ if (inputlth < sizeof(struct psf_header))
+ goto no_psf;
+
+ psfhdr = *(struct psf_header *) &inbuf[0];
+
+ if (!PSF_MAGIC_OK(psfhdr))
+ goto no_psf;
+
+ if (psfhdr.mode > PSF_MAXMODE)
+ bb_error_msg_and_die("unsupported psf file mode");
+ fontsize = ((psfhdr.mode & PSF_MODE512) ? 512 : 256);
+#if !defined( PIO_FONTX ) || defined( __sparc__ )
+ if (fontsize != 256)
+ bb_error_msg_and_die("only fontsize 256 supported");
+#endif
+ hastable = (psfhdr.mode & PSF_MODEHASTAB);
+ unit = psfhdr.charsize;
+ head0 = sizeof(struct psf_header);
+
+ head = head0 + fontsize * unit;
+ if (head > inputlth || (!hastable && head != inputlth))
+ bb_error_msg_and_die("input file: bad length");
+ do_loadfont(fd, inbuf + head0, unit, fontsize);
+ if (hastable)
+ do_loadtable(fd, inbuf + head, inputlth - head, fontsize);
+ return;
+ }
+ no_psf:
+
+ /* file with three code pages? */
+ if (inputlth == 9780) {
+ offset = 40;
+ unit = 16;
+ } else {
+ /* bare font */
+ if (inputlth & 0377)
+ bb_error_msg_and_die("bad input file size");
+ offset = 0;
+ unit = inputlth / 256;
+ }
+ do_loadfont(fd, inbuf + offset, unit, 256);
+}
diff --git a/i/pc104/initrd/conf/busybox/console-tools/loadkmap.c b/i/pc104/initrd/conf/busybox/console-tools/loadkmap.c
new file mode 100644
index 0000000..a141435
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/console-tools/loadkmap.c
@@ -0,0 +1,62 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini loadkmap implementation for busybox
+ *
+ * Copyright (C) 1998 Enrique Zanardi <ezanardi@ull.es>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ *
+ */
+
+#include "busybox.h"
+
+#define BINARY_KEYMAP_MAGIC "bkeymap"
+
+/* From <linux/kd.h> */
+struct kbentry {
+ unsigned char kb_table;
+ unsigned char kb_index;
+ unsigned short kb_value;
+};
+/* sets one entry in translation table */
+#define KDSKBENT 0x4B47
+
+/* From <linux/keyboard.h> */
+#define NR_KEYS 128
+#define MAX_NR_KEYMAPS 256
+
+int loadkmap_main(int argc, char **argv);
+int loadkmap_main(int argc, char **argv)
+{
+ struct kbentry ke;
+ int i, j, fd;
+ uint16_t ibuff[NR_KEYS];
+ char flags[MAX_NR_KEYMAPS];
+ char buff[7];
+
+ if (argc != 1)
+ bb_show_usage();
+
+ fd = xopen(CURRENT_VC, O_RDWR);
+
+ xread(0, buff, 7);
+ if (strncmp(buff, BINARY_KEYMAP_MAGIC, 7))
+ bb_error_msg_and_die("this is not a valid binary keymap");
+
+ xread(0, flags, MAX_NR_KEYMAPS);
+
+ for (i = 0; i < MAX_NR_KEYMAPS; i++) {
+ if (flags[i] == 1) {
+ xread(0, ibuff, NR_KEYS * sizeof(uint16_t));
+ for (j = 0; j < NR_KEYS; j++) {
+ ke.kb_index = j;
+ ke.kb_table = i;
+ ke.kb_value = ibuff[j];
+ ioctl(fd, KDSKBENT, &ke);
+ }
+ }
+ }
+
+ if (ENABLE_FEATURE_CLEAN_UP) close(fd);
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/console-tools/openvt.c b/i/pc104/initrd/conf/busybox/console-tools/openvt.c
new file mode 100644
index 0000000..eb9f49f
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/console-tools/openvt.c
@@ -0,0 +1,43 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * openvt.c - open a vt to run a command.
+ *
+ * busyboxed by Quy Tonthat <quy@signal3.com>
+ * hacked by Tito <farmatito@tiscali.it>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/* getopt not needed */
+
+#include "busybox.h"
+
+int openvt_main(int argc, char **argv);
+int openvt_main(int argc, char **argv)
+{
+ int fd;
+ char vtname[sizeof(VC_FORMAT) + 2];
+
+ if (argc < 3) {
+ bb_show_usage();
+ }
+ /* check for illegal vt number: < 1 or > 63 */
+ sprintf(vtname, VC_FORMAT, (int)xatoul_range(argv[1], 1, 63));
+
+ if (fork() == 0) {
+ /* child */
+ /* leave current vt (controlling tty) */
+ setsid();
+ /* and grab new one */
+ fd = xopen(vtname, O_RDWR);
+ /* Reassign stdin, stdout and sterr */
+ dup2(fd, STDIN_FILENO);
+ dup2(fd, STDOUT_FILENO);
+ dup2(fd, STDERR_FILENO);
+ while (fd > 2) close(fd--);
+
+ BB_EXECVP(argv[2], &argv[2]);
+ _exit(1);
+ }
+ return EXIT_SUCCESS;
+}
diff --git a/i/pc104/initrd/conf/busybox/console-tools/reset.c b/i/pc104/initrd/conf/busybox/console-tools/reset.c
new file mode 100644
index 0000000..03b078c
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/console-tools/reset.c
@@ -0,0 +1,33 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini reset implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ * Written by Erik Andersen and Kent Robotti <robotti@metconnect.com>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/* no options, no getopt */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "busybox.h"
+
+int reset_main(int argc, char **argv);
+int reset_main(int argc, char **argv)
+{
+ if (isatty(1)) {
+ /* See 'man 4 console_codes' for details:
+ * "ESC c" -- Reset
+ * "ESC ( K" -- Select user mapping
+ * "ESC [ J" -- Erase display
+ * "ESC [ 0 m" -- Reset all display attributes
+ * "ESC [ ? 25 h" -- Make cursor visible.
+ */
+ printf("\033c\033(K\033[J\033[0m\033[?25h");
+ }
+ return EXIT_SUCCESS;
+}
+
diff --git a/i/pc104/initrd/conf/busybox/console-tools/resize.c b/i/pc104/initrd/conf/busybox/console-tools/resize.c
new file mode 100644
index 0000000..64f318c
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/console-tools/resize.c
@@ -0,0 +1,70 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * resize - set terminal width and height.
+ *
+ * Copyright 2006 Bernhard Fischer
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+/* no options, no getopt */
+#include "busybox.h"
+
+#define ESC "\033"
+
+struct termios old;
+
+static void
+onintr(int sig ATTRIBUTE_UNUSED)
+{
+ tcsetattr(STDERR_FILENO, TCSANOW, &old);
+ exit(1);
+}
+
+
+int resize_main(int argc, char **argv);
+int resize_main(int argc, char **argv)
+{
+ struct termios new;
+ struct winsize w = { 0,0,0,0 };
+ int ret;
+
+ /* We use _stderr_ in order to make resize usable
+ * in shell backticks (those redirect stdout away from tty).
+ * NB: other versions of resize open "/dev/tty"
+ * and operate on it - should we do the same?
+ */
+
+ tcgetattr(STDERR_FILENO, &old); /* fiddle echo */
+ new = old;
+ new.c_cflag |= (CLOCAL | CREAD);
+ new.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
+ signal(SIGINT, onintr);
+ signal(SIGQUIT, onintr);
+ signal(SIGTERM, onintr);
+ signal(SIGALRM, onintr);
+ tcsetattr(STDERR_FILENO, TCSANOW, &new);
+
+ /* save_cursor_pos 7
+ * scroll_whole_screen [r
+ * put_cursor_waaaay_off [$x;$yH
+ * get_cursor_pos [6n
+ * restore_cursor_pos 8
+ */
+ fprintf(stderr, ESC"7" ESC"[r" ESC"[999;999H" ESC"[6n");
+ alarm(3); /* Just in case terminal won't answer */
+ scanf(ESC"[%hu;%huR", &w.ws_row, &w.ws_col);
+ fprintf(stderr, ESC"8");
+
+ /* BTW, other versions of resize recalculate w.ws_xpixel, ws.ws_ypixel
+ * by calculating character cell HxW from old values
+ * (gotten via TIOCGWINSZ) and recomputing *pixel values */
+ ret = ioctl(STDERR_FILENO, TIOCSWINSZ, &w);
+
+ tcsetattr(STDERR_FILENO, TCSANOW, &old);
+
+ if (ENABLE_FEATURE_RESIZE_PRINT)
+ printf("COLUMNS=%d;LINES=%d;export COLUMNS LINES;\n",
+ w.ws_col, w.ws_row);
+
+ return ret;
+}
diff --git a/i/pc104/initrd/conf/busybox/console-tools/setconsole.c b/i/pc104/initrd/conf/busybox/console-tools/setconsole.c
new file mode 100644
index 0000000..7ddef69
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/console-tools/setconsole.c
@@ -0,0 +1,48 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * setconsole.c - redirect system console output
+ *
+ * Copyright (C) 2004,2005 Enrik Berkhan <Enrik.Berkhan@inka.de>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "busybox.h"
+
+#if ENABLE_FEATURE_SETCONSOLE_LONG_OPTIONS
+static const struct option setconsole_long_options[] = {
+ { "reset", 0, NULL, 'r' },
+ { 0, 0, 0, 0 }
+};
+#endif
+
+#define OPT_SETCONS_RESET 1
+
+int setconsole_main(int argc, char **argv);
+int setconsole_main(int argc, char **argv)
+{
+ unsigned long flags;
+ const char *device = CURRENT_TTY;
+
+#if ENABLE_FEATURE_SETCONSOLE_LONG_OPTIONS
+ applet_long_options = setconsole_long_options;
+#endif
+ flags = getopt32(argc, argv, "r");
+
+ if (argc - optind > 1)
+ bb_show_usage();
+
+ if (argc - optind == 1) {
+ if (flags & OPT_SETCONS_RESET)
+ bb_show_usage();
+ device = argv[optind];
+ } else {
+ if (flags & OPT_SETCONS_RESET)
+ device = DEV_CONSOLE;
+ }
+
+ if (-1 == ioctl(xopen(device, O_RDONLY), TIOCCONS)) {
+ bb_perror_msg_and_die("TIOCCONS");
+ }
+ return EXIT_SUCCESS;
+}
diff --git a/i/pc104/initrd/conf/busybox/console-tools/setkeycodes.c b/i/pc104/initrd/conf/busybox/console-tools/setkeycodes.c
new file mode 100644
index 0000000..ff137d5
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/console-tools/setkeycodes.c
@@ -0,0 +1,50 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * setkeycodes
+ *
+ * Copyright (C) 1994-1998 Andries E. Brouwer <aeb@cwi.nl>
+ *
+ * Adjusted for BusyBox by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include <sys/ioctl.h>
+#include "busybox.h"
+
+
+/* From <linux/kd.h> */
+struct kbkeycode {
+ unsigned int scancode, keycode;
+};
+enum {
+ KDSETKEYCODE = 0x4B4D /* write kernel keycode table entry */
+};
+
+int setkeycodes_main(int argc, char** argv);
+int setkeycodes_main(int argc, char** argv)
+{
+ int fd, sc;
+ struct kbkeycode a;
+
+ if (argc % 2 != 1 || argc < 2) {
+ bb_show_usage();
+ }
+
+ fd = get_console_fd();
+
+ while (argc > 2) {
+ a.keycode = xatoul_range(argv[2], 0, 127);
+ a.scancode = sc = xstrtoul_range(argv[1], 16, 0, 255);
+ if (a.scancode > 127) {
+ a.scancode -= 0xe000;
+ a.scancode += 128;
+ }
+ if (ioctl(fd, KDSETKEYCODE, &a)) {
+ bb_perror_msg_and_die("failed to set SCANCODE %x to KEYCODE %d", sc, a.keycode);
+ }
+ argc -= 2;
+ argv += 2;
+ }
+ return EXIT_SUCCESS;
+}
diff --git a/i/pc104/initrd/conf/busybox/console-tools/setlogcons.c b/i/pc104/initrd/conf/busybox/console-tools/setlogcons.c
new file mode 100644
index 0000000..7b5addc
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/console-tools/setlogcons.c
@@ -0,0 +1,32 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * setlogcons: Send kernel messages to the current console or to console N
+ *
+ * Copyright (C) 2006 by Jan Kiszka <jan.kiszka@web.de>
+ *
+ * Based on setlogcons (kbd-1.12) by Andries E. Brouwer
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "busybox.h"
+
+int setlogcons_main(int argc, char **argv);
+int setlogcons_main(int argc, char **argv)
+{
+ struct {
+ char fn;
+ char subarg;
+ } arg;
+
+ arg.fn = 11; /* redirect kernel messages */
+ arg.subarg = 0; /* to specified console (current as default) */
+
+ if (argc == 2)
+ arg.subarg = xatoul_range(argv[1], 0, 63);
+
+ if (ioctl(xopen(VC_1, O_RDONLY), TIOCLINUX, &arg))
+ bb_perror_msg_and_die("TIOCLINUX");
+
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/Config.in b/i/pc104/initrd/conf/busybox/coreutils/Config.in
new file mode 100644
index 0000000..000f3a8
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/Config.in
@@ -0,0 +1,782 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+menu "Coreutils"
+
+config BASENAME
+ bool "basename"
+ default n
+ help
+ basename is used to strip the directory and suffix from filenames,
+ leaving just the filename itself. Enable this option if you wish
+ to enable the 'basename' utility.
+
+config CAL
+ bool "cal"
+ default n
+ help
+ cal is used to display a monthly calender.
+
+config CAT
+ bool "cat"
+ default n
+ help
+ cat is used to concatenate files and print them to the standard
+ output. Enable this option if you wish to enable the 'cat' utility.
+
+config CATV
+ bool "catv"
+ default n
+ help
+ Display nonprinting characters as escape sequences (like some
+ implementations' cat -v option).
+
+config CHGRP
+ bool "chgrp"
+ default n
+ help
+ chgrp is used to change the group ownership of files.
+
+config CHMOD
+ bool "chmod"
+ default n
+ help
+ chmod is used to change the access permission of files.
+
+config CHOWN
+ bool "chown"
+ default n
+ help
+ chown is used to change the user and/or group ownership
+ of files.
+
+config CHROOT
+ bool "chroot"
+ default n
+ help
+ chroot is used to change the root directory and run a command.
+ The default command is `/bin/sh'.
+
+config CKSUM
+ bool "cksum"
+ default n
+ help
+ cksum is used to calculate the CRC32 checksum of a file.
+
+config CMP
+ bool "cmp"
+ default n
+ help
+ cmp is used to compare two files and returns the result
+ to standard output.
+
+config COMM
+ bool "comm"
+ default n
+ help
+ comm is used to compare two files line by line and return
+ a three-column output.
+
+config CP
+ bool "cp"
+ default n
+ help
+ cp is used to copy files and directories.
+
+config CUT
+ bool "cut"
+ default n
+ help
+ cut is used to print selected parts of lines from
+ each file to stdout.
+
+config DATE
+ bool "date"
+ default n
+ help
+ date is used to set the system date or display the
+ current time in the given format.
+
+config FEATURE_DATE_ISOFMT
+ bool "Enable ISO date format output (-I)"
+ default y
+ depends on DATE
+ help
+ Enable option (-I) to output an ISO-8601 compliant
+ date/time string.
+
+config DD
+ bool "dd"
+ default n
+ help
+ dd copies a file (from standard input to standard output,
+ by default) using specific input and output blocksizes,
+ while optionally performing conversions on it.
+
+config FEATURE_DD_SIGNAL_HANDLING
+ bool "Enable DD signal handling for status reporting"
+ default y
+ depends on DD
+ help
+ sending a SIGUSR1 signal to a running `dd' process makes it
+ print to standard error the number of records read and written
+ so far, then to resume copying.
+
+ $ dd if=/dev/zero of=/dev/null& pid=$! $ kill -USR1 $pid; sleep 1; kill $pid
+ 10899206+0 records in 10899206+0 records out
+
+config FEATURE_DD_IBS_OBS
+ bool "Enable ibs, obs and conv options"
+ default n
+ depends on DD
+ help
+ Enables support for writing a certain number of bytes in and out,
+ at a time, and performing conversions on the data stream.
+
+config DF
+ bool "df"
+ default n
+ help
+ df reports the amount of disk space used and available
+ on filesystems.
+
+config DIFF
+ bool "diff"
+ default n
+ help
+ diff compares two files or directories and outputs the
+ differences between them in a form that can be given to
+ the patch command.
+
+config FEATURE_DIFF_BINARY
+ bool "Enable checks for binary files"
+ default y
+ depends on DIFF
+ help
+ This option enables support for checking for binary files
+ before a comparison is carried out.
+
+config FEATURE_DIFF_DIR
+ bool "Enable directory support"
+ default y
+ depends on DIFF
+ help
+ This option enables support for directory and subdirectory
+ comparison.
+
+config FEATURE_DIFF_MINIMAL
+ bool "Enable -d option to find smaller sets of changes"
+ default n
+ depends on DIFF
+ help
+ Enabling this option allows the use of -d to make diff
+ try hard to find the smallest possible set of changes.
+
+config DIRNAME
+ bool "dirname"
+ default n
+ help
+ dirname is used to strip a non-directory suffix from
+ a file name.
+
+config DOS2UNIX
+ bool "dos2unix/unix2dos"
+ default n
+ help
+ dos2unix is used to convert a text file from DOS format to
+ UNIX format, and vice versa.
+
+config UNIX2DOS
+ bool
+ default y
+ depends on DOS2UNIX
+ help
+ unix2dos is used to convert a text file from UNIX format to
+ DOS format, and vice versa.
+
+config DU
+ bool "du (default blocksize of 512 bytes)"
+ default n
+ help
+ du is used to report the amount of disk space used
+ for specified files.
+
+config FEATURE_DU_DEFAULT_BLOCKSIZE_1K
+ bool "Use a default blocksize of 1024 bytes (1K)"
+ default y
+ depends on DU
+ help
+ Use a blocksize of (1K) instead of the default 512b.
+
+config ECHO
+ bool "echo (basic SuSv3 version taking no options)"
+ default n
+ help
+ echo is used to print a specified string to stdout.
+
+# this entry also appears in shell/Config.in, next to the echo builtin
+config FEATURE_FANCY_ECHO
+ bool "Enable echo options (-n and -e)"
+ default y
+ depends on ECHO
+ help
+ This adds options (-n and -e) to echo.
+
+config ENV
+ bool "env"
+ default n
+ help
+ env is used to set an environment variable and run
+ a command; without options it displays the current
+ environment.
+
+config FEATURE_ENV_LONG_OPTIONS
+ bool "Enable long options"
+ default n
+ depends on ENV && GETOPT_LONG
+ help
+ Support long options for the env applet.
+
+config EXPR
+ bool "expr"
+ default n
+ help
+ expr is used to calculate numbers and print the result
+ to standard output.
+
+config EXPR_MATH_SUPPORT_64
+ bool "Extend Posix numbers support to 64 bit"
+ default n
+ depends on EXPR
+ help
+ Enable 64-bit math support in the expr applet. This will make
+ the applet slightly larger, but will allow computation with very
+ large numbers.
+
+config FALSE
+ bool "false"
+ default n
+ help
+ false returns an exit code of FALSE (1).
+
+config FOLD
+ bool "fold"
+ default n
+ help
+ Wrap text to fit a specific width.
+
+config HEAD
+ bool "head"
+ default n
+ help
+ head is used to print the first specified number of lines
+ from files.
+
+config FEATURE_FANCY_HEAD
+ bool "Enable head options (-c, -q, and -v)"
+ default n
+ depends on HEAD
+ help
+ This enables the head options (-c, -q, and -v).
+
+config HOSTID
+ bool "hostid"
+ default n
+ help
+ hostid prints the numeric identifier (in hexadecimal) for
+ the current host.
+
+config ID
+ bool "id"
+ default n
+ help
+ id displays the current user and group ID names.
+
+config INSTALL
+ bool "install"
+ default n
+ help
+ Copy files and set attributes.
+
+config FEATURE_INSTALL_LONG_OPTIONS
+ bool "Enable long options"
+ default n
+ depends on INSTALL && GETOPT_LONG
+ help
+ Support long options for the install applet.
+
+config LENGTH
+ bool "length"
+ default n
+ help
+ length is used to print out the length of a specified string.
+
+config LN
+ bool "ln"
+ default n
+ help
+ ln is used to create hard or soft links between files.
+
+config LOGNAME
+ bool "logname"
+ default n
+ help
+ logname is used to print the current user's login name.
+
+config LS
+ bool "ls"
+ default n
+ help
+ ls is used to list the contents of directories.
+
+config FEATURE_LS_FILETYPES
+ bool "Enable filetyping options (-p and -F)"
+ default y
+ depends on LS
+ help
+ Enable the ls options (-p and -F).
+
+config FEATURE_LS_FOLLOWLINKS
+ bool "Enable symlinks dereferencing (-L)"
+ default y
+ depends on LS
+ help
+ Enable the ls option (-L).
+
+config FEATURE_LS_RECURSIVE
+ bool "Enable recursion (-R)"
+ default y
+ depends on LS
+ help
+ Enable the ls option (-R).
+
+config FEATURE_LS_SORTFILES
+ bool "Sort the file names"
+ default y
+ depends on LS
+ help
+ Allow ls to sort file names alphabetically.
+
+config FEATURE_LS_TIMESTAMPS
+ bool "Show file timestamps"
+ default y
+ depends on LS
+ help
+ Allow ls to display timestamps for files.
+
+config FEATURE_LS_USERNAME
+ bool "Show username/groupnames"
+ default y
+ depends on LS
+ help
+ Allow ls to display username/groupname for files.
+
+config FEATURE_LS_COLOR
+ bool "Allow use of color to identify file types"
+ default y
+ depends on LS && GETOPT_LONG
+ help
+ This enables the --color option to ls.
+
+config FEATURE_LS_COLOR_IS_DEFAULT
+ bool "Produce colored ls output by default"
+ default n
+ depends on FEATURE_LS_COLOR
+ help
+ Saying yes here will turn coloring on by default,
+ even if no "--color" option is given to the ls command.
+ This is not recommended, since the colors are not
+ configurable, and the output may not be legible on
+ many output screens.
+
+config MD5SUM
+ bool "md5sum"
+ default n
+ help
+ md5sum is used to print or check MD5 checksums.
+
+config MKDIR
+ bool "mkdir"
+ default n
+ help
+ mkdir is used to create directories with the specified names.
+
+config FEATURE_MKDIR_LONG_OPTIONS
+ bool "Enable long options"
+ default n
+ depends on MKDIR && GETOPT_LONG
+ help
+ Support long options for the mkdir applet.
+
+config MKFIFO
+ bool "mkfifo"
+ default n
+ help
+ mkfifo is used to create FIFOs (named pipes).
+ The `mknod' program can also create FIFOs.
+
+config MKNOD
+ bool "mknod"
+ default n
+ help
+ mknod is used to create FIFOs or block/character special
+ files with the specified names.
+
+config MV
+ bool "mv"
+ default n
+ help
+ mv is used to move or rename files or directories.
+
+config FEATURE_MV_LONG_OPTIONS
+ bool "Enable long options"
+ default n
+ depends on MV && GETOPT_LONG
+ help
+ Support long options for the mv applet.
+
+config NICE
+ bool "nice"
+ default n
+ help
+ nice runs a program with modified scheduling priority.
+
+config NOHUP
+ bool "nohup"
+ default n
+ help
+ run a command immune to hangups, with output to a non-tty.
+
+config OD
+ bool "od"
+ default n
+ help
+ od is used to dump binary files in octal and other formats.
+
+config PRINTENV
+ bool "printenv"
+ default n
+ help
+ printenv is used to print all or part of environment.
+
+config PRINTF
+ bool "printf"
+ default n
+ help
+ printf is used to format and print specified strings.
+ It's similar to `echo' except it has more options.
+
+config PWD
+ bool "pwd"
+ default n
+ help
+ pwd is used to print the current directory.
+
+config REALPATH
+ bool "realpath"
+ default n
+ help
+ Return the canonicalized absolute pathname.
+ This isn't provided by GNU shellutils, but where else does it belong.
+
+config RM
+ bool "rm"
+ default n
+ help
+ rm is used to remove files or directories.
+
+config RMDIR
+ bool "rmdir"
+ default n
+ help
+ rmdir is used to remove empty directories.
+
+config SEQ
+ bool "seq"
+ default n
+ help
+ print a sequence of numbers
+
+config SHA1SUM
+ bool "sha1sum"
+ default n
+ help
+ Compute and check SHA1 message digest
+
+config SLEEP
+ bool "sleep (single integer arg with no suffix)"
+ default n
+ help
+ sleep is used to pause for a specified number of seconds,
+
+config FEATURE_FANCY_SLEEP
+ bool "Enable multiple integer args and optional time suffixes"
+ default n
+ depends on SLEEP
+ help
+ Allow sleep to pause for specified minutes, hours, and days.
+
+config SORT
+ bool "sort"
+ default n
+ help
+ sort is used to sort lines of text in specified files.
+
+config FEATURE_SORT_BIG
+ bool "full SuSv3 compliant sort (Support -ktcsbdfiozgM)"
+ default y
+ depends on SORT
+ help
+ Without this, sort only supports -r, -u, and an integer version
+ of -n. Selecting this adds sort keys, floating point support, and
+ more. This adds a little over 3k to a nonstatic build on x86.
+
+ The SuSv3 sort standard is available at:
+ http://www.opengroup.org/onlinepubs/007904975/utilities/sort.html
+
+config STAT
+ bool "stat"
+ default n
+ help
+ display file or filesystem status.
+
+config FEATURE_STAT_FORMAT
+ bool "Enable custom formats (-c)"
+ default n
+ depends on STAT
+ help
+ Without this, stat will not support the '-c format' option where
+ users can pass a custom format string for output. This adds about
+ 7k to a nonstatic build on amd64.
+
+config STTY
+ bool "stty"
+ default n
+ help
+ stty is used to change and print terminal line settings.
+
+config SUM
+ bool "sum"
+ default n
+ help
+ checksum and count the blocks in a file
+
+config SYNC
+ bool "sync"
+ default n
+ help
+ sync is used to flush filesystem buffers.
+
+config TAIL
+ bool "tail"
+ default n
+ help
+ tail is used to print the last specified number of lines
+ from files.
+
+config FEATURE_FANCY_TAIL
+ bool "Enable extra tail options (-q, -s, and -v)"
+ default y
+ depends on TAIL
+ help
+ The options (-q, -s, and -v) are provided by GNU tail, but
+ are not specific in the SUSv3 standard.
+
+config TEE
+ bool "tee"
+ default n
+ help
+ tee is used to read from standard input and write
+ to standard output and files.
+
+config FEATURE_TEE_USE_BLOCK_IO
+ bool "Enable block i/o (larger/faster) instead of byte i/o."
+ default n
+ depends on TEE
+ help
+ Enable this option for a faster tee, at expense of size.
+
+config TEST
+ bool "test"
+ default n
+ help
+ test is used to check file types and compare values,
+ returning an appropriate exit code. The bash shell
+ has test built in, ash can build it in optionally.
+
+config FEATURE_TEST_64
+ bool "Extend test to 64 bit"
+ default n
+ depends on TEST
+ help
+ Enable 64-bit support in test.
+
+config TOUCH
+ bool "touch"
+ default n
+ help
+ touch is used to create or change the access and/or
+ modification timestamp of specified files.
+
+config TR
+ bool "tr"
+ default n
+ help
+ tr is used to squeeze, and/or delete characters from standard
+ input, writing to standard output.
+
+config FEATURE_TR_CLASSES
+ bool "Enable character classes (such as [:upper:])"
+ default n
+ depends on TR
+ help
+ Enable character classes, enabling commands such as:
+ tr [:upper:] [:lower:] to convert input into lowercase.
+
+config FEATURE_TR_EQUIV
+ bool "Enable equivalence classes"
+ default n
+ depends on TR
+ help
+ Enable equivalence classes, which essentially add the enclosed
+ character to the current set. For instance, tr [=a=] xyz would
+ replace all instances of 'a' with 'xyz'. This option is mainly
+ useful for cases when no other way of expressing a character
+ is possible.
+
+config TRUE
+ bool "true"
+ default n
+ help
+ true returns an exit code of TRUE (0).
+
+config TTY
+ bool "tty"
+ default n
+ help
+ tty is used to print the name of the current terminal to
+ standard output.
+
+config UNAME
+ bool "uname"
+ default n
+ help
+ uname is used to print system information.
+
+config UNIQ
+ bool "uniq"
+ default n
+ help
+ uniq is used to remove duplicate lines from a sorted file.
+
+config USLEEP
+ bool "usleep"
+ default n
+ help
+ usleep is used to pause for a specified number of microseconds.
+
+config UUDECODE
+ bool "uudecode"
+ default n
+ help
+ uudecode is used to decode a uuencoded file.
+
+config UUENCODE
+ bool "uuencode"
+ default n
+ help
+ uuencode is used to uuencode a file.
+
+config WATCH
+ bool "watch"
+ default n
+ select DATE
+ help
+ watch is used to execute a program periodically, showing
+ output to the screen.
+
+config WC
+ bool "wc"
+ default n
+ help
+ wc is used to print the number of bytes, words, and lines,
+ in specified files.
+
+config FEATURE_WC_LARGE
+ bool "Support very large files in wc"
+ default n
+ depends on WC
+ help
+ Use "unsigned long long" in wc for count variables
+
+config WHO
+ bool "who"
+ default n
+ select FEATURE_UTMP
+ help
+ who is used to show who is logged on.
+
+config WHOAMI
+ bool "whoami"
+ default n
+ help
+ whoami is used to print the username of the current
+ user id (same as id -un).
+
+config YES
+ bool "yes"
+ default n
+ help
+ yes is used to repeatedly output a specific string, or
+ the default string `y'.
+
+comment "Common options for cp and mv"
+ depends on CP || MV
+
+config FEATURE_PRESERVE_HARDLINKS
+ bool "Preserve hard links"
+ default n
+ depends on CP || MV
+ help
+ Allow cp and mv to preserve hard links.
+
+comment "Common options for ls, more and telnet"
+ depends on LS || MORE || TELNET
+
+config FEATURE_AUTOWIDTH
+ bool "Calculate terminal & column widths"
+ default y
+ depends on LS || MORE || TELNET
+ help
+ This option allows utilities such as 'ls', 'more' and 'telnet'
+ to determine the width of the screen, which can allow them to
+ display additional text or avoid wrapping text onto the next line.
+ If you leave this disabled, your utilities will be especially
+ primitive and will be unable to determine the current screen width.
+
+comment "Common options for df, du, ls"
+ depends on DF || DU || LS
+
+config FEATURE_HUMAN_READABLE
+ bool "Support for human readable output (example 13k, 23M, 235G)"
+ default n
+ depends on DF || DU || LS
+ help
+ Allow df, du, and ls to have human readable output.
+
+comment "Common options for md5sum, sha1sum"
+ depends on MD5SUM || SHA1SUM
+
+config FEATURE_MD5_SHA1_SUM_CHECK
+ bool "Enable -c, -s and -w options"
+ default n
+ depends on MD5SUM || SHA1SUM
+ help
+ Enabling the -c options allows files to be checked
+ against pre-calculated hash values.
+
+ -s and -w are useful options when verifying checksums.
+
+endmenu
diff --git a/i/pc104/initrd/conf/busybox/coreutils/Kbuild b/i/pc104/initrd/conf/busybox/coreutils/Kbuild
new file mode 100644
index 0000000..dfdcbd4
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/Kbuild
@@ -0,0 +1,85 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under the GPL v2, see the file LICENSE in this tarball.
+
+libs-y += libcoreutils/
+
+lib-y:=
+lib-$(CONFIG_BASENAME) += basename.o
+lib-$(CONFIG_CAL) += cal.o
+lib-$(CONFIG_CAT) += cat.o
+lib-$(CONFIG_MORE) += cat.o # more uses it if stdout isn't a tty
+lib-$(CONFIG_LESS) += cat.o # less too
+lib-$(CONFIG_CATV) += catv.o
+lib-$(CONFIG_CHGRP) += chgrp.o chown.o
+lib-$(CONFIG_CHMOD) += chmod.o
+lib-$(CONFIG_CHOWN) += chown.o
+lib-$(CONFIG_CHROOT) += chroot.o
+lib-$(CONFIG_CKSUM) += cksum.o
+lib-$(CONFIG_CMP) += cmp.o
+lib-$(CONFIG_COMM) += comm.o
+lib-$(CONFIG_CP) += cp.o
+lib-$(CONFIG_CUT) += cut.o
+lib-$(CONFIG_DATE) += date.o
+lib-$(CONFIG_DD) += dd.o
+lib-$(CONFIG_DF) += df.o
+lib-$(CONFIG_DIFF) += diff.o
+lib-$(CONFIG_DIRNAME) += dirname.o
+lib-$(CONFIG_DOS2UNIX) += dos2unix.o
+lib-$(CONFIG_DU) += du.o
+lib-$(CONFIG_ECHO) += echo.o
+lib-$(CONFIG_ASH) += echo.o # used by ash
+lib-$(CONFIG_ENV) += env.o
+lib-$(CONFIG_EXPR) += expr.o
+lib-$(CONFIG_FALSE) += false.o
+lib-$(CONFIG_FOLD) += fold.o
+lib-$(CONFIG_HEAD) += head.o
+lib-$(CONFIG_HOSTID) += hostid.o
+lib-$(CONFIG_ID) += id.o
+lib-$(CONFIG_INSTALL) += install.o
+lib-$(CONFIG_LENGTH) += length.o
+lib-$(CONFIG_LN) += ln.o
+lib-$(CONFIG_LOGNAME) += logname.o
+lib-$(CONFIG_LS) += ls.o
+lib-$(CONFIG_MD5SUM) += md5_sha1_sum.o
+lib-$(CONFIG_MKDIR) += mkdir.o
+lib-$(CONFIG_MKFIFO) += mkfifo.o
+lib-$(CONFIG_MKNOD) += mknod.o
+lib-$(CONFIG_MV) += mv.o
+lib-$(CONFIG_NICE) += nice.o
+lib-$(CONFIG_NOHUP) += nohup.o
+lib-$(CONFIG_OD) += od.o
+lib-$(CONFIG_PRINTENV) += printenv.o
+lib-$(CONFIG_PRINTF) += printf.o
+lib-$(CONFIG_PWD) += pwd.o
+lib-$(CONFIG_REALPATH) += realpath.o
+lib-$(CONFIG_RM) += rm.o
+lib-$(CONFIG_RMDIR) += rmdir.o
+lib-$(CONFIG_SEQ) += seq.o
+lib-$(CONFIG_SHA1SUM) += md5_sha1_sum.o
+lib-$(CONFIG_SLEEP) += sleep.o
+lib-$(CONFIG_SORT) += sort.o
+lib-$(CONFIG_STAT) += stat.o
+lib-$(CONFIG_STTY) += stty.o
+lib-$(CONFIG_SUM) += sum.o
+lib-$(CONFIG_SYNC) += sync.o
+lib-$(CONFIG_TAIL) += tail.o
+lib-$(CONFIG_TEE) += tee.o
+lib-$(CONFIG_TEST) += test.o
+lib-$(CONFIG_ASH) += test.o # used by ash
+lib-$(CONFIG_TOUCH) += touch.o
+lib-$(CONFIG_TR) += tr.o
+lib-$(CONFIG_TRUE) += true.o
+lib-$(CONFIG_TTY) += tty.o
+lib-$(CONFIG_UNAME) += uname.o
+lib-$(CONFIG_UNIQ) += uniq.o
+lib-$(CONFIG_USLEEP) += usleep.o
+lib-$(CONFIG_UUDECODE) += uudecode.o
+lib-$(CONFIG_UUENCODE) += uuencode.o
+lib-$(CONFIG_WATCH) += watch.o
+lib-$(CONFIG_WC) += wc.o
+lib-$(CONFIG_WHO) += who.o
+lib-$(CONFIG_WHOAMI) += whoami.o
+lib-$(CONFIG_YES) += yes.o
diff --git a/i/pc104/initrd/conf/busybox/coreutils/basename.c b/i/pc104/initrd/conf/busybox/coreutils/basename.c
new file mode 100644
index 0000000..46f7122
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/basename.c
@@ -0,0 +1,51 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini basename implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ *
+ */
+
+/* BB_AUDIT SUSv3 compliant */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/basename.html */
+
+
+/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
+ *
+ * Changes:
+ * 1) Now checks for too many args. Need at least one and at most two.
+ * 2) Don't check for options, as per SUSv3.
+ * 3) Save some space by using strcmp(). Calling strncmp() here was silly.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "busybox.h"
+
+int basename_main(int argc, char **argv);
+int basename_main(int argc, char **argv)
+{
+ size_t m, n;
+ char *s;
+
+ if (((unsigned int)(argc-2)) >= 2) {
+ bb_show_usage();
+ }
+
+ s = bb_get_last_path_component(*++argv);
+
+ if (*++argv) {
+ n = strlen(*argv);
+ m = strlen(s);
+ if ((m > n) && ((strcmp)(s+m-n, *argv) == 0)) {
+ s[m-n] = '\0';
+ }
+ }
+
+ puts(s);
+
+ fflush_stdout_and_exit(EXIT_SUCCESS);
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/cal.c b/i/pc104/initrd/conf/busybox/coreutils/cal.c
new file mode 100644
index 0000000..3d61791
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/cal.c
@@ -0,0 +1,352 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Calendar implementation for busybox
+ *
+ * See original copyright at the end of this file
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/* BB_AUDIT SUSv3 compliant with -j and -y extensions (from util-linux). */
+/* BB_AUDIT BUG: The output of 'cal -j 1752' is incorrect. The upstream
+ * BB_AUDIT BUG: version in util-linux seems to be broken as well. */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/cal.html */
+
+/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
+ *
+ * Major size reduction... over 50% (>1.5k) on i386.
+ */
+
+#include "busybox.h"
+
+#define THURSDAY 4 /* for reformation */
+#define SATURDAY 6 /* 1 Jan 1 was a Saturday */
+
+#define FIRST_MISSING_DAY 639787 /* 3 Sep 1752 */
+#define NUMBER_MISSING_DAYS 11 /* 11 day correction */
+
+#define MAXDAYS 42 /* max slots in a month array */
+#define SPACE -1 /* used in day array */
+
+static const char days_in_month[] = {
+ 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+
+static const char sep1752[] = {
+ 1, 2, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30
+};
+
+static int julian;
+
+/* leap year -- account for Gregorian reformation in 1752 */
+#define leap_year(yr) \
+ ((yr) <= 1752 ? !((yr) % 4) : \
+ (!((yr) % 4) && ((yr) % 100)) || !((yr) % 400))
+
+static int is_leap_year(int year)
+{
+ return leap_year(year);
+}
+#undef leap_year
+#define leap_year(yr) is_leap_year(yr)
+
+/* number of centuries since 1700, not inclusive */
+#define centuries_since_1700(yr) \
+ ((yr) > 1700 ? (yr) / 100 - 17 : 0)
+
+/* number of centuries since 1700 whose modulo of 400 is 0 */
+#define quad_centuries_since_1700(yr) \
+ ((yr) > 1600 ? ((yr) - 1600) / 400 : 0)
+
+/* number of leap years between year 1 and this year, not inclusive */
+#define leap_years_since_year_1(yr) \
+ ((yr) / 4 - centuries_since_1700(yr) + quad_centuries_since_1700(yr))
+
+static void center (char *, int, int);
+static void day_array (int, int, int *);
+static void trim_trailing_spaces_and_print (char *);
+
+static void blank_string(char *buf, size_t buflen);
+static char *build_row(char *p, int *dp);
+
+#define DAY_LEN 3 /* 3 spaces per day */
+#define J_DAY_LEN (DAY_LEN + 1)
+#define WEEK_LEN 20 /* 7 * 3 - one space at the end */
+#define J_WEEK_LEN (WEEK_LEN + 7)
+#define HEAD_SEP 2 /* spaces between day headings */
+
+int cal_main(int argc, char **argv);
+int cal_main(int argc, char **argv)
+{
+ struct tm *local_time;
+ struct tm zero_tm;
+ time_t now;
+ int month, year, flags, i;
+ char *month_names[12];
+ char day_headings[28]; /* 28 for julian, 21 for nonjulian */
+ char buf[40];
+
+ flags = getopt32(argc, argv, "jy");
+
+ julian = flags & 1;
+
+ argv += optind;
+
+ month = 0;
+
+ if ((argc -= optind) > 2) {
+ bb_show_usage();
+ }
+
+ if (!argc) {
+ time(&now);
+ local_time = localtime(&now);
+ year = local_time->tm_year + 1900;
+ if (!(flags & 2)) {
+ month = local_time->tm_mon + 1;
+ }
+ } else {
+ if (argc == 2) {
+ month = xatoul_range(*argv++, 1, 12);
+ }
+ year = xatoul_range(*argv, 1, 9999);
+ }
+
+ blank_string(day_headings, sizeof(day_headings) - 7 + 7*julian);
+
+ i = 0;
+ do {
+ zero_tm.tm_mon = i;
+ strftime(buf, sizeof(buf), "%B", &zero_tm);
+ month_names[i] = xstrdup(buf);
+
+ if (i < 7) {
+ zero_tm.tm_wday = i;
+ strftime(buf, sizeof(buf), "%a", &zero_tm);
+ strncpy(day_headings + i * (3+julian) + julian, buf, 2);
+ }
+ } while (++i < 12);
+
+ if (month) {
+ int row, len, days[MAXDAYS];
+ int *dp = days;
+ char lineout[30];
+
+ day_array(month, year, dp);
+ len = sprintf(lineout, "%s %d", month_names[month - 1], year);
+ printf("%*s%s\n%s\n",
+ ((7*julian + WEEK_LEN) - len) / 2, "",
+ lineout, day_headings);
+ for (row = 0; row < 6; row++) {
+ build_row(lineout, dp)[0] = '\0';
+ dp += 7;
+ trim_trailing_spaces_and_print(lineout);
+ }
+ } else {
+ int row, which_cal, week_len, days[12][MAXDAYS];
+ int *dp;
+ char lineout[80];
+
+ sprintf(lineout, "%d", year);
+ center(lineout,
+ (WEEK_LEN * 3 + HEAD_SEP * 2)
+ + julian * (J_WEEK_LEN * 2 + HEAD_SEP
+ - (WEEK_LEN * 3 + HEAD_SEP * 2)),
+ 0);
+ puts("\n"); /* two \n's */
+ for (i = 0; i < 12; i++) {
+ day_array(i + 1, year, days[i]);
+ }
+ blank_string(lineout, sizeof(lineout));
+ week_len = WEEK_LEN + julian * (J_WEEK_LEN - WEEK_LEN);
+ for (month = 0; month < 12; month += 3-julian) {
+ center(month_names[month], week_len, HEAD_SEP);
+ if (!julian) {
+ center(month_names[month + 1], week_len, HEAD_SEP);
+ }
+ center(month_names[month + 2 - julian], week_len, 0);
+ printf("\n%s%*s%s", day_headings, HEAD_SEP, "", day_headings);
+ if (!julian) {
+ printf("%*s%s", HEAD_SEP, "", day_headings);
+ }
+ putchar('\n');
+ for (row = 0; row < (6*7); row += 7) {
+ for (which_cal = 0; which_cal < 3-julian; which_cal++) {
+ dp = days[month + which_cal] + row;
+ build_row(lineout + which_cal * (week_len + 2), dp);
+ }
+ /* blank_string took care of nul termination. */
+ trim_trailing_spaces_and_print(lineout);
+ }
+ }
+ }
+
+ fflush_stdout_and_exit(0);
+}
+
+/*
+ * day_array --
+ * Fill in an array of 42 integers with a calendar. Assume for a moment
+ * that you took the (maximum) 6 rows in a calendar and stretched them
+ * out end to end. You would have 42 numbers or spaces. This routine
+ * builds that array for any month from Jan. 1 through Dec. 9999.
+ */
+static void day_array(int month, int year, int *days)
+{
+ long temp;
+ int i;
+ int j_offset;
+ int day, dw, dm;
+
+ memset(days, SPACE, MAXDAYS * sizeof(int));
+
+ if ((month == 9) && (year == 1752)) {
+ size_t oday = 0;
+
+ j_offset = julian * 244;
+ do {
+ days[oday+2] = sep1752[oday] + j_offset;
+ } while (++oday < sizeof(sep1752));
+
+ return;
+ }
+
+ /* day_in_year
+ * return the 1 based day number within the year
+ */
+ day = 1;
+ if ((month > 2) && leap_year(year)) {
+ ++day;
+ }
+
+ i = month;
+ while (i) {
+ day += days_in_month[--i];
+ }
+
+ /* day_in_week
+ * return the 0 based day number for any date from 1 Jan. 1 to
+ * 31 Dec. 9999. Assumes the Gregorian reformation eliminates
+ * 3 Sep. 1752 through 13 Sep. 1752. Returns Thursday for all
+ * missing days.
+ */
+ dw = THURSDAY;
+ temp = (long)(year - 1) * 365 + leap_years_since_year_1(year - 1)
+ + day;
+ if (temp < FIRST_MISSING_DAY) {
+ dw = ((temp - 1 + SATURDAY) % 7);
+ } else if (temp >= (FIRST_MISSING_DAY + NUMBER_MISSING_DAYS)) {
+ dw = (((temp - 1 + SATURDAY) - NUMBER_MISSING_DAYS) % 7);
+ }
+
+ if (!julian) {
+ day = 1;
+ }
+
+ dm = days_in_month[month];
+ if ((month == 2) && leap_year(year)) {
+ ++dm;
+ }
+
+ while (dm) {
+ days[dw++] = day++;
+ --dm;
+ }
+}
+
+static void trim_trailing_spaces_and_print(char *s)
+{
+ char *p = s;
+
+ while (*p) {
+ ++p;
+ }
+ while (p > s) {
+ --p;
+ if (!(isspace)(*p)) { /* We want the function... not the inline. */
+ p[1] = '\0';
+ break;
+ }
+ }
+
+ puts(s);
+}
+
+static void center(char *str, int len, int separate)
+{
+ int n = strlen(str);
+ len -= n;
+ printf("%*s%*s", (len/2) + n, str, (len/2) + (len % 2) + separate, "");
+}
+
+static void blank_string(char *buf, size_t buflen)
+{
+ memset(buf, ' ', buflen);
+ buf[buflen-1] = '\0';
+}
+
+static char *build_row(char *p, int *dp)
+{
+ int col, val, day;
+
+ memset(p, ' ', (julian + DAY_LEN) * 7);
+
+ col = 0;
+ do {
+ if ((day = *dp++) != SPACE) {
+ if (julian) {
+ ++p;
+ if (day >= 100) {
+ *p = '0';
+ p[-1] = (day / 100) + '0';
+ day %= 100;
+ }
+ }
+ if ((val = day / 10) > 0) {
+ *p = val + '0';
+ }
+ *++p = day % 10 + '0';
+ p += 2;
+ } else {
+ p += DAY_LEN + julian;
+ }
+ } while (++col < 7);
+
+ return p;
+}
+
+/*
+ * Copyright (c) 1989, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kim Letkeman.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
diff --git a/i/pc104/initrd/conf/busybox/coreutils/cat.c b/i/pc104/initrd/conf/busybox/coreutils/cat.c
new file mode 100644
index 0000000..7bab325
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/cat.c
@@ -0,0 +1,43 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * cat implementation for busybox
+ *
+ * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
+ *
+ * Licensed under GPLv2, see file License in this tarball for details.
+ */
+
+/* BB_AUDIT SUSv3 compliant */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/cat.html */
+
+#include "busybox.h"
+
+int bb_cat(char **argv)
+{
+ static const char *const argv_dash[] = { "-", NULL };
+ FILE *f;
+ int retval = EXIT_SUCCESS;
+
+ if (!*argv) argv = (char**) &argv_dash;
+
+ do {
+ f = fopen_or_warn_stdin(*argv);
+ if (f) {
+ off_t r = bb_copyfd_eof(fileno(f), STDOUT_FILENO);
+ fclose_if_not_stdin(f);
+ if (r >= 0)
+ continue;
+ }
+ retval = EXIT_FAILURE;
+ } while (*++argv);
+
+ return retval;
+}
+
+int cat_main(int argc, char **argv);
+int cat_main(int argc, char **argv)
+{
+ getopt32(argc, argv, "u");
+ argv += optind;
+ return bb_cat(argv);
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/catv.c b/i/pc104/initrd/conf/busybox/coreutils/catv.c
new file mode 100644
index 0000000..826e0e9
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/catv.c
@@ -0,0 +1,70 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * cat -v implementation for busybox
+ *
+ * Copyright (C) 2006 Rob Landley <rob@landley.net>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/* See "Cat -v considered harmful" at
+ * http://cm.bell-labs.com/cm/cs/doc/84/kp.ps.gz */
+
+#include "busybox.h"
+
+int catv_main(int argc, char **argv);
+int catv_main(int argc, char **argv)
+{
+ int retval = EXIT_SUCCESS, fd;
+ unsigned flags;
+
+ flags = getopt32(argc, argv, "etv");
+#define CATV_OPT_e (1<<0)
+#define CATV_OPT_t (1<<1)
+#define CATV_OPT_v (1<<2)
+ flags ^= CATV_OPT_v;
+
+ argv += optind;
+ do {
+ /* Read from stdin if there's nothing else to do. */
+ fd = 0;
+ if (*argv && 0 > (fd = xopen(*argv, O_RDONLY)))
+ retval = EXIT_FAILURE;
+ else for (;;) {
+ int i, res;
+
+ res = read(fd, bb_common_bufsiz1, sizeof(bb_common_bufsiz1));
+ if (res < 0)
+ retval = EXIT_FAILURE;
+ if (res < 1)
+ break;
+ for (i = 0; i < res; i++) {
+ char c = bb_common_bufsiz1[i];
+
+ if (c > 126 && (flags & CATV_OPT_v)) {
+ if (c == 127) {
+ printf("^?");
+ continue;
+ } else {
+ printf("M-");
+ c -= 128;
+ }
+ }
+ if (c < 32) {
+ if (c == 10) {
+ if (flags & CATV_OPT_e)
+ putchar('$');
+ } else if (flags & (c==9 ? CATV_OPT_t : CATV_OPT_v)) {
+ printf("^%c", c+'@');
+ continue;
+ }
+ }
+ putchar(c);
+ }
+ }
+ if (ENABLE_FEATURE_CLEAN_UP && fd)
+ close(fd);
+ } while (*++argv);
+
+ fflush_stdout_and_exit(retval);
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/chgrp.c b/i/pc104/initrd/conf/busybox/coreutils/chgrp.c
new file mode 100644
index 0000000..cfb8c15
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/chgrp.c
@@ -0,0 +1,28 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini chgrp implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/* BB_AUDIT SUSv3 defects - none? */
+/* BB_AUDIT GNU defects - unsupported long options. */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/chgrp.html */
+
+#include "busybox.h"
+
+int chgrp_main(int argc, char **argv);
+int chgrp_main(int argc, char **argv)
+{
+ /* "chgrp [opts] abc file(s)" == "chown [opts] :abc file(s)" */
+ char **p = argv;
+ while (*++p) {
+ if (p[0][0] != '-') {
+ p[0] = xasprintf(":%s", p[0]);
+ break;
+ }
+ }
+ return chown_main(argc, argv);
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/chmod.c b/i/pc104/initrd/conf/busybox/coreutils/chmod.c
new file mode 100644
index 0000000..11c6731
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/chmod.c
@@ -0,0 +1,159 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini chmod implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Reworked by (C) 2002 Vladimir Oleynik <dzo@simtreas.ru>
+ * to correctly parse '-rwxgoa'
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/* BB_AUDIT SUSv3 compliant */
+/* BB_AUDIT GNU defects - unsupported long options. */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/chmod.html */
+
+#include "busybox.h"
+
+#define OPT_RECURSE (option_mask32 & 1)
+#define OPT_VERBOSE (USE_DESKTOP(option_mask32 & 2) SKIP_DESKTOP(0))
+#define OPT_CHANGED (USE_DESKTOP(option_mask32 & 4) SKIP_DESKTOP(0))
+#define OPT_QUIET (USE_DESKTOP(option_mask32 & 8) SKIP_DESKTOP(0))
+#define OPT_STR "R" USE_DESKTOP("vcf")
+
+/* coreutils:
+ * chmod never changes the permissions of symbolic links; the chmod
+ * system call cannot change their permissions. This is not a problem
+ * since the permissions of symbolic links are never used.
+ * However, for each symbolic link listed on the command line, chmod changes
+ * the permissions of the pointed-to file. In contrast, chmod ignores
+ * symbolic links encountered during recursive directory traversals.
+ */
+
+static int fileAction(const char *fileName, struct stat *statbuf, void* param, int depth)
+{
+ mode_t newmode;
+
+ /* match coreutils behavior */
+ if (depth == 0) {
+ /* statbuf holds lstat result, but we need stat (follow link) */
+ if (stat(fileName, statbuf))
+ goto err;
+ } else { /* depth > 0: skip links */
+ if (S_ISLNK(statbuf->st_mode))
+ return TRUE;
+ }
+ newmode = statbuf->st_mode;
+
+ if (!bb_parse_mode((char *)param, &newmode))
+ bb_error_msg_and_die("invalid mode: %s", (char *)param);
+
+ if (chmod(fileName, newmode) == 0) {
+ if (OPT_VERBOSE
+ || (OPT_CHANGED && statbuf->st_mode != newmode)
+ ) {
+ printf("mode of '%s' changed to %04o (%s)\n", fileName,
+ newmode & 07777, bb_mode_string(newmode)+1);
+ }
+ return TRUE;
+ }
+ err:
+ if (!OPT_QUIET)
+ bb_perror_msg("%s", fileName);
+ return FALSE;
+}
+
+int chmod_main(int argc, char **argv);
+int chmod_main(int argc, char **argv)
+{
+ int retval = EXIT_SUCCESS;
+ char *arg, **argp;
+ char *smode;
+
+ /* Convert first encountered -r into ar, -w into aw etc
+ * so that getopt would not eat it */
+ argp = argv;
+ while ((arg = *++argp)) {
+ /* Mode spec must be the first arg (sans -R etc) */
+ /* (protect against mishandling e.g. "chmod 644 -r") */
+ if (arg[0] != '-') {
+ arg = NULL;
+ break;
+ }
+ /* An option. Not a -- or valid option? */
+ if (arg[1] && !strchr("-"OPT_STR, arg[1])) {
+ arg[0] = 'a';
+ break;
+ }
+ }
+
+ /* Parse options */
+ opt_complementary = "-2";
+ getopt32(argc, argv, ("-"OPT_STR) + 1); /* Reuse string */
+ argv += optind;
+
+ /* Restore option-like mode if needed */
+ if (arg) arg[0] = '-';
+
+ /* Ok, ready to do the deed now */
+ smode = *argv++;
+ do {
+ if (!recursive_action(*argv,
+ OPT_RECURSE, // recurse
+ FALSE, // follow links: coreutils doesn't
+ FALSE, // depth first
+ fileAction, // file action
+ fileAction, // dir action
+ smode, // user data
+ 0) // depth
+ ) {
+ retval = EXIT_FAILURE;
+ }
+ } while (*++argv);
+
+ return retval;
+}
+
+/*
+Security: chmod is too important and too subtle.
+This is a test script (busybox chmod versus coreutils).
+Run it in empty directory.
+
+#!/bin/sh
+t1="/tmp/busybox chmod"
+t2="/usr/bin/chmod"
+create() {
+ rm -rf $1; mkdir $1
+ (
+ cd $1 || exit 1
+ mkdir dir
+ >up
+ >file
+ >dir/file
+ ln -s dir linkdir
+ ln -s file linkfile
+ ln -s ../up dir/up
+ )
+}
+tst() {
+ (cd test1; $t1 $1)
+ (cd test2; $t2 $1)
+ (cd test1; ls -lR) >out1
+ (cd test2; ls -lR) >out2
+ echo "chmod $1" >out.diff
+ if ! diff -u out1 out2 >>out.diff; then exit 1; fi
+ rm out.diff
+}
+echo "If script produced 'out.diff' file, then at least one testcase failed"
+create test1; create test2
+tst "a+w file"
+tst "a-w dir"
+tst "a+w linkfile"
+tst "a-w linkdir"
+tst "-R a+w file"
+tst "-R a-w dir"
+tst "-R a+w linkfile"
+tst "-R a-w linkdir"
+tst "a-r,a+x linkfile"
+*/
diff --git a/i/pc104/initrd/conf/busybox/coreutils/chown.c b/i/pc104/initrd/conf/busybox/coreutils/chown.c
new file mode 100644
index 0000000..f92299e
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/chown.c
@@ -0,0 +1,167 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini chown implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/* BB_AUDIT SUSv3 defects - none? */
+/* BB_AUDIT GNU defects - unsupported long options. */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/chown.html */
+
+#include "busybox.h"
+
+#define OPT_STR ("Rh" USE_DESKTOP("vcfLHP"))
+#define BIT_RECURSE 1
+#define OPT_RECURSE (option_mask32 & 1)
+#define OPT_NODEREF (option_mask32 & 2)
+#define OPT_VERBOSE (USE_DESKTOP(option_mask32 & 0x04) SKIP_DESKTOP(0))
+#define OPT_CHANGED (USE_DESKTOP(option_mask32 & 0x08) SKIP_DESKTOP(0))
+#define OPT_QUIET (USE_DESKTOP(option_mask32 & 0x10) SKIP_DESKTOP(0))
+/* POSIX options
+ * -L traverse every symbolic link to a directory encountered
+ * -H if a command line argument is a symbolic link to a directory, traverse it
+ * -P do not traverse any symbolic links (default)
+ * We do not conform to the following:
+ * "Specifying more than one of -H, -L, and -P is not an error.
+ * The last option specified shall determine the behavior of the utility." */
+/* -L */
+#define BIT_TRAVERSE 0x20
+#define OPT_TRAVERSE (USE_DESKTOP(option_mask32 & BIT_TRAVERSE) SKIP_DESKTOP(0))
+/* -H or -L */
+#define BIT_TRAVERSE_TOP (0x20|0x40)
+#define OPT_TRAVERSE_TOP (USE_DESKTOP(option_mask32 & BIT_TRAVERSE_TOP) SKIP_DESKTOP(0))
+
+typedef int (*chown_fptr)(const char *, uid_t, gid_t);
+
+static struct bb_uidgid_t ugid = { -1, -1 };
+
+static int fileAction(const char *fileName, struct stat *statbuf,
+ void *cf, int depth)
+{
+ uid_t u = (ugid.uid == (uid_t)-1) ? statbuf->st_uid : ugid.uid;
+ gid_t g = (ugid.gid == (gid_t)-1) ? statbuf->st_gid : ugid.gid;
+
+ if (!((chown_fptr)cf)(fileName, u, g)) {
+ if (OPT_VERBOSE
+ || (OPT_CHANGED && (statbuf->st_uid != u || statbuf->st_gid != g))
+ ) {
+ printf("changed ownership of '%s' to %u:%u\n",
+ fileName, (unsigned)u, (unsigned)g);
+ }
+ return TRUE;
+ }
+ if (!OPT_QUIET)
+ bb_perror_msg("%s", fileName); /* A filename can have % in it... */
+ return FALSE;
+}
+
+int chown_main(int argc, char **argv);
+int chown_main(int argc, char **argv)
+{
+ int retval = EXIT_SUCCESS;
+ chown_fptr chown_func;
+
+ opt_complementary = "-2";
+ getopt32(argc, argv, OPT_STR);
+ argv += optind;
+
+ /* This matches coreutils behavior (almost - see below) */
+ chown_func = chown;
+ if (OPT_NODEREF
+ /* || (OPT_RECURSE && !OPT_TRAVERSE_TOP): */
+ USE_DESKTOP( || (option_mask32 & (BIT_RECURSE|BIT_TRAVERSE_TOP)) == BIT_RECURSE)
+ ) {
+ chown_func = lchown;
+ }
+
+ parse_chown_usergroup_or_die(&ugid, argv[0]);
+
+ /* Ok, ready to do the deed now */
+ argv++;
+ do {
+ char *arg = *argv;
+
+ if (OPT_TRAVERSE_TOP) {
+ /* resolves symlink (even recursive) */
+ arg = xmalloc_realpath(arg);
+ if (!arg)
+ continue;
+ }
+
+ if (!recursive_action(arg,
+ OPT_RECURSE, // recurse
+ OPT_TRAVERSE, // follow links if -L
+ FALSE, // depth first
+ fileAction, // file action
+ fileAction, // dir action
+ chown_func, // user data
+ 0) // depth
+ ) {
+ retval = EXIT_FAILURE;
+ }
+
+ if (OPT_TRAVERSE_TOP)
+ free(arg);
+ } while (*++argv);
+
+ return retval;
+}
+
+/*
+Testcase. Run in empty directory.
+
+#!/bin/sh
+t1="/tmp/busybox chown"
+t2="/usr/bin/chown"
+create() {
+ rm -rf $1; mkdir $1
+ (
+ cd $1 || exit 1
+ mkdir dir dir2
+ >up
+ >file
+ >dir/file
+ >dir2/file
+ ln -s dir linkdir
+ ln -s file linkfile
+ ln -s ../up dir/linkup
+ ln -s ../dir2 dir/linkupdir2
+ )
+ chown -R 0:0 $1
+}
+tst() {
+ create test1
+ create test2
+ (cd test1; $t1 $1)
+ (cd test2; $t2 $1)
+ (cd test1; ls -lnR) >out1
+ (cd test2; ls -lnR) >out2
+ echo "chown $1" >out.diff
+ if ! diff -u out1 out2 >>out.diff; then exit 1; fi
+ rm out.diff
+}
+tst_for_each() {
+ tst "$1 1:1 file"
+ tst "$1 1:1 dir"
+ tst "$1 1:1 linkdir"
+ tst "$1 1:1 linkfile"
+}
+echo "If script produced 'out.diff' file, then at least one testcase failed"
+# These match coreutils 6.8:
+tst_for_each ""
+tst_for_each "-R"
+tst_for_each "-RP"
+tst_for_each "-RL"
+tst_for_each "-RH"
+tst_for_each "-h"
+tst_for_each "-hR"
+tst_for_each "-hRP"
+# Below: with "chown linkdir" coreutils 6.8 will chown linkdir _target_,
+# we lchown _the link_. I believe we are "more correct".
+#tst_for_each "-hRL"
+#tst_for_each "-hRH"
+
+*/
diff --git a/i/pc104/initrd/conf/busybox/coreutils/chroot.c b/i/pc104/initrd/conf/busybox/coreutils/chroot.c
new file mode 100644
index 0000000..fcd70f2
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/chroot.c
@@ -0,0 +1,38 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini chroot implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/* BB_AUDIT SUSv3 N/A -- Matches GNU behavior. */
+
+#include "busybox.h"
+
+int chroot_main(int argc, char **argv);
+int chroot_main(int argc, char **argv)
+{
+ if (argc < 2) {
+ bb_show_usage();
+ }
+
+ ++argv;
+ if (chroot(*argv)) {
+ bb_perror_msg_and_die("cannot change root directory to %s", *argv);
+ }
+ xchdir("/");
+
+ ++argv;
+ if (argc == 2) {
+ argv -= 2;
+ if (!(*argv = getenv("SHELL"))) {
+ *argv = (char *) DEFAULT_SHELL;
+ }
+ argv[1] = (char *) "-i";
+ }
+
+ BB_EXECVP(*argv, argv);
+ bb_perror_msg_and_die("cannot execute %s", *argv);
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/cksum.c b/i/pc104/initrd/conf/busybox/coreutils/cksum.c
new file mode 100644
index 0000000..66a255c
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/cksum.c
@@ -0,0 +1,54 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * cksum - calculate the CRC32 checksum of a file
+ *
+ * Copyright (C) 2006 by Rob Sullivan, with ideas from code by Walter Harms
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. */
+
+#include "busybox.h"
+
+int cksum_main(int argc, char **argv);
+int cksum_main(int argc, char **argv)
+{
+
+ uint32_t *crc32_table = crc32_filltable(1);
+
+ FILE *fp;
+ uint32_t crc;
+ long length, filesize;
+ int bytes_read;
+ char *cp;
+
+ int inp_stdin = (argc == optind) ? 1 : 0;
+
+ do {
+ fp = fopen_or_warn_stdin((inp_stdin) ? bb_msg_standard_input : *++argv);
+
+ crc = 0;
+ length = 0;
+
+ while ((bytes_read = fread(bb_common_bufsiz1, 1, BUFSIZ, fp)) > 0) {
+ cp = bb_common_bufsiz1;
+ length += bytes_read;
+ while (bytes_read--)
+ crc = (crc << 8) ^ crc32_table[((crc >> 24) ^ (*cp++)) & 0xffL];
+ }
+
+ filesize = length;
+
+ for (; length; length >>= 8)
+ crc = (crc << 8) ^ crc32_table[((crc >> 24) ^ length) & 0xffL];
+ crc ^= 0xffffffffL;
+
+ if (inp_stdin) {
+ printf("%" PRIu32 " %li\n", crc, filesize);
+ break;
+ }
+
+ printf("%" PRIu32 " %li %s\n", crc, filesize, *argv);
+ fclose(fp);
+ } while (*(argv + 1));
+
+ fflush_stdout_and_exit(EXIT_SUCCESS);
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/cmp.c b/i/pc104/initrd/conf/busybox/coreutils/cmp.c
new file mode 100644
index 0000000..cff1182
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/cmp.c
@@ -0,0 +1,145 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini cmp implementation for busybox
+ *
+ * Copyright (C) 2000,2001 by Matt Kraai <kraai@alumni.carnegiemellon.edu>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/* BB_AUDIT SUSv3 (virtually) compliant -- uses nicer GNU format for -l. */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/cmp.html */
+
+/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
+ *
+ * Original version majorly reworked for SUSv3 compliance, bug fixes, and
+ * size optimizations. Changes include:
+ * 1) Now correctly distinguishes between errors and actual file differences.
+ * 2) Proper handling of '-' args.
+ * 3) Actual error checking of i/o.
+ * 4) Accept SUSv3 -l option. Note that we use the slightly nicer gnu format
+ * in the '-l' case.
+ */
+
+#include "busybox.h"
+
+static FILE *cmp_xfopen_input(const char * const filename)
+{
+ FILE *fp;
+
+ fp = fopen_or_warn_stdin(filename);
+ if (fp)
+ return fp;
+ exit(xfunc_error_retval); /* We already output an error message. */
+}
+
+static const char fmt_eof[] = "cmp: EOF on %s\n";
+static const char fmt_differ[] = "%s %s differ: char %"OFF_FMT"d, line %d\n";
+// This fmt_l_opt uses gnu-isms. SUSv3 would be "%.0s%.0s%"OFF_FMT"d %o %o\n"
+static const char fmt_l_opt[] = "%.0s%.0s%"OFF_FMT"d %3o %3o\n";
+
+static const char opt_chars[] = "sl";
+#define CMP_OPT_s (1<<0)
+#define CMP_OPT_l (1<<1)
+
+int cmp_main(int argc, char **argv);
+int cmp_main(int argc, char **argv)
+{
+ FILE *fp1, *fp2, *outfile = stdout;
+ const char *filename1, *filename2 = "-";
+ USE_DESKTOP(off_t skip1 = 0, skip2 = 0;)
+ off_t char_pos = 0;
+ int line_pos = 1; /* Hopefully won't overflow... */
+ const char *fmt;
+ int c1, c2;
+ unsigned opt;
+ int retval = 0;
+
+ xfunc_error_retval = 2; /* 1 is returned if files are different. */
+
+ opt_complementary = "?:-1"
+ USE_DESKTOP(":?4")
+ SKIP_DESKTOP(":?2")
+ ":l--s:s--l";
+ opt = getopt32(argc, argv, opt_chars);
+ argv += optind;
+
+ filename1 = *argv;
+ fp1 = cmp_xfopen_input(filename1);
+
+ if (*++argv) {
+ filename2 = *argv;
+#if ENABLE_DESKTOP
+ if (*++argv) {
+ skip1 = XATOOFF(*argv);
+ if (*++argv) {
+ skip2 = XATOOFF(*argv);
+ }
+ }
+#endif
+ }
+
+ fp2 = cmp_xfopen_input(filename2);
+ if (fp1 == fp2) { /* Paranoia check... stdin == stdin? */
+ /* Note that we don't bother reading stdin. Neither does gnu wc.
+ * But perhaps we should, so that other apps down the chain don't
+ * get the input. Consider 'echo hello | (cmp - - && cat -)'.
+ */
+ return 0;
+ }
+
+ if (opt & CMP_OPT_l)
+ fmt = fmt_l_opt;
+ else
+ fmt = fmt_differ;
+
+#if ENABLE_DESKTOP
+ while (skip1) { getc(fp1); skip1--; }
+ while (skip2) { getc(fp2); skip2--; }
+#endif
+ do {
+ c1 = getc(fp1);
+ c2 = getc(fp2);
+ ++char_pos;
+ if (c1 != c2) { /* Remember: a read error may have occurred. */
+ retval = 1; /* But assume the files are different for now. */
+ if (c2 == EOF) {
+ /* We know that fp1 isn't at EOF or in an error state. But to
+ * save space below, things are setup to expect an EOF in fp1
+ * if an EOF occurred. So, swap things around.
+ */
+ fp1 = fp2;
+ filename1 = filename2;
+ c1 = c2;
+ }
+ if (c1 == EOF) {
+ die_if_ferror(fp1, filename1);
+ fmt = fmt_eof; /* Well, no error, so it must really be EOF. */
+ outfile = stderr;
+ /* There may have been output to stdout (option -l), so
+ * make sure we fflush before writing to stderr. */
+ xfflush_stdout();
+ }
+ if (!(opt & CMP_OPT_s)) {
+ if (opt & CMP_OPT_l) {
+ line_pos = c1; /* line_pos is unused in the -l case. */
+ }
+ fprintf(outfile, fmt, filename1, filename2, char_pos, line_pos, c2);
+ if (opt) { /* This must be -l since not -s. */
+ /* If we encountered an EOF,
+ * the while check will catch it. */
+ continue;
+ }
+ }
+ break;
+ }
+ if (c1 == '\n') {
+ ++line_pos;
+ }
+ } while (c1 != EOF);
+
+ die_if_ferror(fp1, filename1);
+ die_if_ferror(fp2, filename2);
+
+ fflush_stdout_and_exit(retval);
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/comm.c b/i/pc104/initrd/conf/busybox/coreutils/comm.c
new file mode 100644
index 0000000..b641296
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/comm.c
@@ -0,0 +1,127 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini comm implementation for busybox
+ *
+ * Copyright (C) 2005 by Robert Sullivan <cogito.ergo.cogito@gmail.com>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "busybox.h"
+
+#define COMM_OPT_1 0x01
+#define COMM_OPT_2 0x02
+#define COMM_OPT_3 0x04
+
+/* These three variables control behaviour if non-zero */
+
+static int only_file_1;
+static int only_file_2;
+static int both;
+
+/* writeline outputs the input given, appropriately aligned according to class */
+static void writeline(char *line, int class)
+{
+ if (class == 0) {
+ if (!only_file_1)
+ return;
+ } else if (class == 1) {
+ if (!only_file_2)
+ return;
+ if (only_file_1)
+ putchar('\t');
+ }
+ else /*if (class == 2)*/ {
+ if (!both)
+ return;
+ if (only_file_1)
+ putchar('\t');
+ if (only_file_2)
+ putchar('\t');
+ }
+ fputs(line, stdout);
+}
+
+/* This is the real core of the program - lines are compared here */
+static void cmp_files(char **infiles)
+{
+#define LINE_LEN 100
+#define BB_EOF_0 0x1
+#define BB_EOF_1 0x2
+ char thisline[2][LINE_LEN];
+ FILE *streams[2];
+ int i;
+
+ for (i = 0; i < 2; ++i) {
+ streams[i] = ((infiles[i][0] == '=' && infiles[i][1]) ? stdin : xfopen(infiles[i], "r"));
+ fgets(thisline[i], LINE_LEN, streams[i]);
+ }
+
+ while (*thisline[0] || *thisline[1]) {
+ int order = 0;
+
+ i = 0;
+ if (feof(streams[0])) i |= BB_EOF_0;
+ if (feof(streams[1])) i |= BB_EOF_1;
+
+ if (!*thisline[0])
+ order = 1;
+ else if (!*thisline[1])
+ order = -1;
+ else {
+ int tl0_len, tl1_len;
+ tl0_len = strlen(thisline[0]);
+ tl1_len = strlen(thisline[1]);
+ order = memcmp(thisline[0], thisline[1], tl0_len < tl1_len ? tl0_len : tl1_len);
+ if (!order)
+ order = tl0_len < tl1_len ? -1 : tl0_len != tl1_len;
+ }
+
+ if (order == 0 && !i)
+ writeline(thisline[1], 2);
+ else if (order > 0 && !(i & BB_EOF_1))
+ writeline(thisline[1], 1);
+ else if (order < 0 && !(i & BB_EOF_0))
+ writeline(thisline[0], 0);
+
+ if (i & BB_EOF_0 & BB_EOF_1) {
+ break;
+
+ } else if (i) {
+ i = (i & BB_EOF_0 ? 1 : 0);
+ while (!feof(streams[i])) {
+ if ((order < 0 && i) || (order > 0 && !i))
+ writeline(thisline[i], i);
+ fgets(thisline[i], LINE_LEN, streams[i]);
+ }
+ break;
+
+ } else {
+ if (order >= 0)
+ fgets(thisline[1], LINE_LEN, streams[1]);
+ if (order <= 0)
+ fgets(thisline[0], LINE_LEN, streams[0]);
+ }
+ }
+
+ fclose(streams[0]);
+ fclose(streams[1]);
+}
+
+int comm_main(int argc, char **argv);
+int comm_main(int argc, char **argv)
+{
+ unsigned long flags;
+
+ flags = getopt32(argc, argv, "123");
+
+ if (optind + 2 != argc)
+ bb_show_usage();
+
+ only_file_1 = !(flags & COMM_OPT_1);
+ only_file_2 = !(flags & COMM_OPT_2);
+ both = !(flags & COMM_OPT_3);
+
+ cmp_files(argv + optind);
+ exit(EXIT_SUCCESS);
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/cp.c b/i/pc104/initrd/conf/busybox/coreutils/cp.c
new file mode 100644
index 0000000..a80e0d2
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/cp.c
@@ -0,0 +1,100 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini cp implementation for busybox
+ *
+ * Copyright (C) 2000 by Matt Kraai <kraai@alumni.carnegiemellon.edu>
+ * SELinux support by Yuichi Nakamura <ynakam@hitachisoft.jp>
+ *
+ * Licensed under GPL v2 or later, see file LICENSE in this tarball for details.
+ */
+
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/cp.html */
+
+/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
+ *
+ * Size reduction.
+ */
+
+#include "busybox.h"
+#include "libcoreutils/coreutils.h"
+
+int cp_main(int argc, char **argv);
+int cp_main(int argc, char **argv)
+{
+ struct stat source_stat;
+ struct stat dest_stat;
+ const char *last;
+ const char *dest;
+ int s_flags;
+ int d_flags;
+ int flags;
+ int status = 0;
+ enum {
+ OPT_a = 1 << (sizeof(FILEUTILS_CP_OPTSTR)-1),
+ OPT_r = 1 << (sizeof(FILEUTILS_CP_OPTSTR)),
+ OPT_P = 1 << (sizeof(FILEUTILS_CP_OPTSTR)+1),
+ OPT_H = 1 << (sizeof(FILEUTILS_CP_OPTSTR)+2),
+ OPT_L = 1 << (sizeof(FILEUTILS_CP_OPTSTR)+3),
+ };
+
+ // Soft- and hardlinking don't mix
+ // -P and -d are the same (-P is POSIX, -d is GNU)
+ // -r and -R are the same
+ // -a = -pdR
+ opt_complementary = "?:l--s:s--l:Pd:rR:apdR";
+ flags = getopt32(argc, argv, FILEUTILS_CP_OPTSTR "arPHL");
+ /* Default behavior of cp is to dereference, so we don't have to do
+ * anything special when we are given -L.
+ * The behavior of -H is *almost* like -L, but not quite, so let's
+ * just ignore it too for fun.
+ if (flags & OPT_L) ...
+ if (flags & OPT_H) ... // deref command-line params only
+ */
+
+#if ENABLE_SELINUX
+ if (flags & FILEUTILS_PRESERVE_SECURITY_CONTEXT) {
+ selinux_or_die();
+ }
+#endif
+
+ flags ^= FILEUTILS_DEREFERENCE; /* The sense of this flag was reversed. */
+
+ if (optind + 2 > argc) {
+ bb_show_usage();
+ }
+
+ last = argv[argc - 1];
+ argv += optind;
+
+ /* If there are only two arguments and... */
+ if (optind + 2 == argc) {
+ s_flags = cp_mv_stat2(*argv, &source_stat,
+ (flags & FILEUTILS_DEREFERENCE) ? stat : lstat);
+ if (s_flags < 0)
+ return EXIT_FAILURE;
+ d_flags = cp_mv_stat(last, &dest_stat);
+ if (d_flags < 0)
+ return EXIT_FAILURE;
+
+ /* ...if neither is a directory or... */
+ if ( !((s_flags | d_flags) & 2) ||
+ /* ...recursing, the 1st is a directory, and the 2nd doesn't exist... */
+ ((flags & FILEUTILS_RECUR) && (s_flags & 2) && !d_flags)
+ ) {
+ /* ...do a simple copy. */
+ dest = xstrdup(last);
+ goto DO_COPY; /* Note: optind+2==argc implies argv[1]==last below. */
+ }
+ }
+
+ do {
+ dest = concat_path_file(last, bb_get_last_path_component(*argv));
+ DO_COPY:
+ if (copy_file(*argv, dest, flags) < 0) {
+ status = 1;
+ }
+ free((void*)dest);
+ } while (*++argv != last);
+
+ return status;
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/cut.c b/i/pc104/initrd/conf/busybox/coreutils/cut.c
new file mode 100644
index 0000000..22014fc
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/cut.c
@@ -0,0 +1,287 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * cut.c - minimalist version of cut
+ *
+ * Copyright (C) 1999,2000,2001 by Lineo, inc.
+ * Written by Mark Whitley <markw@codepoet.org>
+ * debloated by Bernhard Fischer
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "busybox.h"
+
+/* option vars */
+static const char optstring[] = "b:c:f:d:sn";
+#define CUT_OPT_BYTE_FLGS (1<<0)
+#define CUT_OPT_CHAR_FLGS (1<<1)
+#define CUT_OPT_FIELDS_FLGS (1<<2)
+#define CUT_OPT_DELIM_FLGS (1<<3)
+#define CUT_OPT_SUPPRESS_FLGS (1<<4)
+
+static char delim = '\t'; /* delimiter, default is tab */
+
+struct cut_list {
+ int startpos;
+ int endpos;
+};
+
+enum {
+ BOL = 0,
+ EOL = INT_MAX,
+ NON_RANGE = -1
+};
+
+/* growable array holding a series of lists */
+static struct cut_list *cut_lists;
+static unsigned int nlists; /* number of elements in above list */
+
+
+static int cmpfunc(const void *a, const void *b)
+{
+ return (((struct cut_list *) a)->startpos -
+ ((struct cut_list *) b)->startpos);
+
+}
+
+static void cut_file(FILE * file)
+{
+ char *line = NULL;
+ unsigned int linenum = 0; /* keep these zero-based to be consistent */
+
+ /* go through every line in the file */
+ while ((line = xmalloc_getline(file)) != NULL) {
+
+ /* set up a list so we can keep track of what's been printed */
+ char * printed = xzalloc(strlen(line) * sizeof(char));
+ char * orig_line = line;
+ unsigned int cl_pos = 0;
+ int spos;
+
+ /* cut based on chars/bytes XXX: only works when sizeof(char) == byte */
+ if (option_mask32 & (CUT_OPT_CHAR_FLGS | CUT_OPT_BYTE_FLGS)) {
+ /* print the chars specified in each cut list */
+ for (; cl_pos < nlists; cl_pos++) {
+ spos = cut_lists[cl_pos].startpos;
+ while (spos < strlen(line)) {
+ if (!printed[spos]) {
+ printed[spos] = 'X';
+ putchar(line[spos]);
+ }
+ spos++;
+ if (spos > cut_lists[cl_pos].endpos
+ || cut_lists[cl_pos].endpos == NON_RANGE)
+ break;
+ }
+ }
+ } else if (delim == '\n') { /* cut by lines */
+ spos = cut_lists[cl_pos].startpos;
+
+ /* get out if we have no more lists to process or if the lines
+ * are lower than what we're interested in */
+ if (linenum < spos || cl_pos >= nlists)
+ goto next_line;
+
+ /* if the line we're looking for is lower than the one we were
+ * passed, it means we displayed it already, so move on */
+ while (spos < linenum) {
+ spos++;
+ /* go to the next list if we're at the end of this one */
+ if (spos > cut_lists[cl_pos].endpos
+ || cut_lists[cl_pos].endpos == NON_RANGE) {
+ cl_pos++;
+ /* get out if there's no more lists to process */
+ if (cl_pos >= nlists)
+ goto next_line;
+ spos = cut_lists[cl_pos].startpos;
+ /* get out if the current line is lower than the one
+ * we just became interested in */
+ if (linenum < spos)
+ goto next_line;
+ }
+ }
+
+ /* If we made it here, it means we've found the line we're
+ * looking for, so print it */
+ puts(line);
+ goto next_line;
+ } else { /* cut by fields */
+ int ndelim = -1; /* zero-based / one-based problem */
+ int nfields_printed = 0;
+ char *field = NULL;
+ const char delimiter[2] = { delim, 0 };
+
+ /* does this line contain any delimiters? */
+ if (strchr(line, delim) == NULL) {
+ if (!(option_mask32 & CUT_OPT_SUPPRESS_FLGS))
+ puts(line);
+ goto next_line;
+ }
+
+ /* process each list on this line, for as long as we've got
+ * a line to process */
+ for (; cl_pos < nlists && line; cl_pos++) {
+ spos = cut_lists[cl_pos].startpos;
+ do {
+ /* find the field we're looking for */
+ while (line && ndelim < spos) {
+ field = strsep(&line, delimiter);
+ ndelim++;
+ }
+
+ /* we found it, and it hasn't been printed yet */
+ if (field && ndelim == spos && !printed[ndelim]) {
+ /* if this isn't our first time through, we need to
+ * print the delimiter after the last field that was
+ * printed */
+ if (nfields_printed > 0)
+ putchar(delim);
+ fputs(field, stdout);
+ printed[ndelim] = 'X';
+ nfields_printed++; /* shouldn't overflow.. */
+ }
+
+ spos++;
+
+ /* keep going as long as we have a line to work with,
+ * this is a list, and we're not at the end of that
+ * list */
+ } while (spos <= cut_lists[cl_pos].endpos && line
+ && cut_lists[cl_pos].endpos != NON_RANGE);
+ }
+ }
+ /* if we printed anything at all, we need to finish it with a
+ * newline cuz we were handed a chomped line */
+ putchar('\n');
+ next_line:
+ linenum++;
+ free(printed);
+ free(orig_line);
+ }
+}
+
+static const char _op_on_field[] = " only when operating on fields";
+
+int cut_main(int argc, char **argv);
+int cut_main(int argc, char **argv)
+{
+ char *sopt, *ltok;
+
+ opt_complementary = "b--bcf:c--bcf:f--bcf";
+ getopt32(argc, argv, optstring, &sopt, &sopt, &sopt, &ltok);
+// argc -= optind;
+ argv += optind;
+ if (!(option_mask32 & (CUT_OPT_BYTE_FLGS | CUT_OPT_CHAR_FLGS | CUT_OPT_FIELDS_FLGS)))
+ bb_error_msg_and_die("expected a list of bytes, characters, or fields");
+ if (option_mask32 & BB_GETOPT_ERROR)
+ bb_error_msg_and_die("only one type of list may be specified");
+
+ if (option_mask32 & CUT_OPT_DELIM_FLGS) {
+ if (strlen(ltok) > 1) {
+ bb_error_msg_and_die("the delimiter must be a single character");
+ }
+ delim = ltok[0];
+ }
+
+ /* non-field (char or byte) cutting has some special handling */
+ if (!(option_mask32 & CUT_OPT_FIELDS_FLGS)) {
+ if (option_mask32 & CUT_OPT_SUPPRESS_FLGS) {
+ bb_error_msg_and_die
+ ("suppressing non-delimited lines makes sense%s",
+ _op_on_field);
+ }
+ if (delim != '\t') {
+ bb_error_msg_and_die
+ ("a delimiter may be specified%s", _op_on_field);
+ }
+ }
+
+ /*
+ * parse list and put values into startpos and endpos.
+ * valid list formats: N, N-, N-M, -M
+ * more than one list can be separated by commas
+ */
+ {
+ char *ntok;
+ int s = 0, e = 0;
+
+ /* take apart the lists, one by one (they are separated with commas */
+ while ((ltok = strsep(&sopt, ",")) != NULL) {
+
+ /* it's actually legal to pass an empty list */
+ if (strlen(ltok) == 0)
+ continue;
+
+ /* get the start pos */
+ ntok = strsep(&ltok, "-");
+ if (ntok == NULL) {
+ bb_error_msg
+ ("internal error: ntok is null for start pos!?\n");
+ } else if (strlen(ntok) == 0) {
+ s = BOL;
+ } else {
+ s = xatoi_u(ntok);
+ /* account for the fact that arrays are zero based, while
+ * the user expects the first char on the line to be char #1 */
+ if (s != 0)
+ s--;
+ }
+
+ /* get the end pos */
+ ntok = strsep(&ltok, "-");
+ if (ntok == NULL) {
+ e = NON_RANGE;
+ } else if (strlen(ntok) == 0) {
+ e = EOL;
+ } else {
+ e = xatoi_u(ntok);
+ /* if the user specified and end position of 0, that means "til the
+ * end of the line */
+ if (e == 0)
+ e = EOL;
+ e--; /* again, arrays are zero based, lines are 1 based */
+ if (e == s)
+ e = NON_RANGE;
+ }
+
+ /* if there's something left to tokenize, the user passed
+ * an invalid list */
+ if (ltok)
+ bb_error_msg_and_die("invalid byte or field list");
+
+ /* add the new list */
+ cut_lists = xrealloc(cut_lists, sizeof(struct cut_list) * (++nlists));
+ cut_lists[nlists-1].startpos = s;
+ cut_lists[nlists-1].endpos = e;
+ }
+
+ /* make sure we got some cut positions out of all that */
+ if (nlists == 0)
+ bb_error_msg_and_die("missing list of positions");
+
+ /* now that the lists are parsed, we need to sort them to make life
+ * easier on us when it comes time to print the chars / fields / lines
+ */
+ qsort(cut_lists, nlists, sizeof(struct cut_list), cmpfunc);
+ }
+
+ /* argv[0..argc-1] should be names of file to process. If no
+ * files were specified or '-' was specified, take input from stdin.
+ * Otherwise, we process all the files specified. */
+ if (argv[0] == NULL || LONE_DASH(argv[0])) {
+ cut_file(stdin);
+ } else {
+ FILE *file;
+
+ do {
+ file = fopen_or_warn(argv[0], "r");
+ if (file) {
+ cut_file(file);
+ fclose(file);
+ }
+ } while (*++argv);
+ }
+ if (ENABLE_FEATURE_CLEAN_UP)
+ free(cut_lists);
+ return EXIT_SUCCESS;
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/date.c b/i/pc104/initrd/conf/busybox/coreutils/date.c
new file mode 100644
index 0000000..dfc8b2f
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/date.c
@@ -0,0 +1,241 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini date implementation for busybox
+ *
+ * by Matthew Grant <grantma@anathoth.gen.nz>
+ *
+ * iso-format handling added by Robert Griebl <griebl@gmx.de>
+ * bugfixes and cleanup by Bernhard Fischer
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+*/
+
+#include "busybox.h"
+
+/* This 'date' command supports only 2 time setting formats,
+ all the GNU strftime stuff (its in libc, lets use it),
+ setting time using UTC and displaying it, as well as
+ an RFC 2822 compliant date output for shell scripting
+ mail commands */
+
+/* Input parsing code is always bulky - used heavy duty libc stuff as
+ much as possible, missed out a lot of bounds checking */
+
+/* Default input handling to save surprising some people */
+
+
+#define DATE_OPT_RFC2822 0x01
+#define DATE_OPT_SET 0x02
+#define DATE_OPT_UTC 0x04
+#define DATE_OPT_DATE 0x08
+#define DATE_OPT_REFERENCE 0x10
+#define DATE_OPT_TIMESPEC 0x20
+#define DATE_OPT_HINT 0x40
+
+static void xputenv(char *s)
+{
+ if (putenv(s) != 0)
+ bb_error_msg_and_die(bb_msg_memory_exhausted);
+}
+
+static void maybe_set_utc(int opt)
+{
+ if (opt & DATE_OPT_UTC)
+ xputenv((char*)"TZ=UTC0");
+}
+
+int date_main(int argc, char **argv);
+int date_main(int argc, char **argv)
+{
+ time_t tm;
+ struct tm tm_time;
+ unsigned opt;
+ int ifmt = -1;
+ char *date_str = NULL;
+ char *date_fmt = NULL;
+ char *filename = NULL;
+ char *isofmt_arg;
+ char *hintfmt_arg;
+
+ opt_complementary = "?:d--s:s--d"
+ USE_FEATURE_DATE_ISOFMT(":R--I:I--R");
+ opt = getopt32(argc, argv, "Rs:ud:r:"
+ USE_FEATURE_DATE_ISOFMT("I::D:"),
+ &date_str, &date_str, &filename
+ USE_FEATURE_DATE_ISOFMT(, &isofmt_arg, &hintfmt_arg));
+ maybe_set_utc(opt);
+
+ if (ENABLE_FEATURE_DATE_ISOFMT && (opt & DATE_OPT_TIMESPEC)) {
+ if (!isofmt_arg) {
+ ifmt = 0; /* default is date */
+ } else {
+ const char * const isoformats[] =
+ {"date", "hours", "minutes", "seconds"};
+
+ for (ifmt = 0; ifmt < 4; ifmt++)
+ if (!strcmp(isofmt_arg, isoformats[ifmt])) {
+ break;
+ }
+ if (ifmt == 4) /* parse error */
+ bb_show_usage();
+ }
+ }
+
+ /* XXX, date_fmt == NULL from this always */
+ if ((date_fmt == NULL) && (optind < argc) && (argv[optind][0] == '+')) {
+ date_fmt = &argv[optind][1]; /* Skip over the '+' */
+ } else if (date_str == NULL) {
+ opt |= DATE_OPT_SET;
+ date_str = argv[optind];
+ }
+
+ /* Now we have parsed all the information except the date format
+ which depends on whether the clock is being set or read */
+
+ if (filename) {
+ struct stat statbuf;
+ xstat(filename, &statbuf);
+ tm = statbuf.st_mtime;
+ } else
+ time(&tm);
+ memcpy(&tm_time, localtime(&tm), sizeof(tm_time));
+ /* Zero out fields - take her back to midnight! */
+ if (date_str != NULL) {
+ tm_time.tm_sec = 0;
+ tm_time.tm_min = 0;
+ tm_time.tm_hour = 0;
+
+ /* Process any date input to UNIX time since 1 Jan 1970 */
+ if (ENABLE_FEATURE_DATE_ISOFMT && (opt & DATE_OPT_HINT)) {
+ strptime(date_str, hintfmt_arg, &tm_time);
+ } else if (strchr(date_str, ':') != NULL) {
+ /* Parse input and assign appropriately to tm_time */
+
+ if (sscanf(date_str, "%d:%d:%d", &tm_time.tm_hour, &tm_time.tm_min,
+ &tm_time.tm_sec) == 3) {
+ /* no adjustments needed */
+ } else if (sscanf(date_str, "%d:%d", &tm_time.tm_hour,
+ &tm_time.tm_min) == 2) {
+ /* no adjustments needed */
+ } else if (sscanf(date_str, "%d.%d-%d:%d:%d", &tm_time.tm_mon,
+ &tm_time.tm_mday, &tm_time.tm_hour,
+ &tm_time.tm_min, &tm_time.tm_sec) == 5) {
+ /* Adjust dates from 1-12 to 0-11 */
+ tm_time.tm_mon -= 1;
+ } else if (sscanf(date_str, "%d.%d-%d:%d", &tm_time.tm_mon,
+ &tm_time.tm_mday,
+ &tm_time.tm_hour, &tm_time.tm_min) == 4) {
+ /* Adjust dates from 1-12 to 0-11 */
+ tm_time.tm_mon -= 1;
+ } else if (sscanf(date_str, "%d.%d.%d-%d:%d:%d", &tm_time.tm_year,
+ &tm_time.tm_mon, &tm_time.tm_mday,
+ &tm_time.tm_hour, &tm_time.tm_min,
+ &tm_time.tm_sec) == 6) {
+ tm_time.tm_year -= 1900; /* Adjust years */
+ tm_time.tm_mon -= 1; /* Adjust dates from 1-12 to 0-11 */
+ } else if (sscanf(date_str, "%d.%d.%d-%d:%d", &tm_time.tm_year,
+ &tm_time.tm_mon, &tm_time.tm_mday,
+ &tm_time.tm_hour, &tm_time.tm_min) == 5) {
+ tm_time.tm_year -= 1900; /* Adjust years */
+ tm_time.tm_mon -= 1; /* Adjust dates from 1-12 to 0-11 */
+ } else {
+ bb_error_msg_and_die(bb_msg_invalid_date, date_str);
+ }
+ } else {
+ int nr;
+ char *cp;
+
+ nr = sscanf(date_str, "%2d%2d%2d%2d%d", &tm_time.tm_mon,
+ &tm_time.tm_mday, &tm_time.tm_hour, &tm_time.tm_min,
+ &tm_time.tm_year);
+
+ if (nr < 4 || nr > 5) {
+ bb_error_msg_and_die(bb_msg_invalid_date, date_str);
+ }
+
+ cp = strchr(date_str, '.');
+ if (cp) {
+ nr = sscanf(cp + 1, "%2d", &tm_time.tm_sec);
+ if (nr != 1) {
+ bb_error_msg_and_die(bb_msg_invalid_date, date_str);
+ }
+ }
+
+ /* correct for century - minor Y2K problem here? */
+ if (tm_time.tm_year >= 1900) {
+ tm_time.tm_year -= 1900;
+ }
+ /* adjust date */
+ tm_time.tm_mon -= 1;
+ }
+
+ /* Correct any day of week and day of year etc. fields */
+ tm_time.tm_isdst = -1; /* Be sure to recheck dst. */
+ tm = mktime(&tm_time);
+ if (tm < 0) {
+ bb_error_msg_and_die(bb_msg_invalid_date, date_str);
+ }
+ maybe_set_utc(opt);
+
+ /* if setting time, set it */
+ if ((opt & DATE_OPT_SET) && stime(&tm) < 0) {
+ bb_perror_msg("cannot set date");
+ }
+ }
+
+ /* Display output */
+
+ /* Deal with format string */
+
+ if (date_fmt == NULL) {
+ int i;
+ date_fmt = xzalloc(32);
+ if (ENABLE_FEATURE_DATE_ISOFMT && ifmt >= 0) {
+ strcpy(date_fmt, "%Y-%m-%d");
+ if (ifmt > 0) {
+ i = 8;
+ date_fmt[i++] = 'T';
+ date_fmt[i++] = '%';
+ date_fmt[i++] = 'H';
+ if (ifmt > 1) {
+ date_fmt[i++] = ':';
+ date_fmt[i++] = '%';
+ date_fmt[i++] = 'M';
+ }
+ if (ifmt > 2) {
+ date_fmt[i++] = ':';
+ date_fmt[i++] = '%';
+ date_fmt[i++] = 'S';
+ }
+format_utc:
+ date_fmt[i++] = '%';
+ date_fmt[i] = (opt & DATE_OPT_UTC) ? 'Z' : 'z';
+ }
+ } else if (opt & DATE_OPT_RFC2822) {
+ /* Undo busybox.c for date -R */
+ if (ENABLE_LOCALE_SUPPORT)
+ setlocale(LC_TIME, "C");
+ strcpy(date_fmt, "%a, %d %b %Y %H:%M:%S ");
+ i = 22;
+ goto format_utc;
+ } else /* default case */
+ date_fmt = (char*)"%a %b %e %H:%M:%S %Z %Y";
+ }
+
+ if (*date_fmt == '\0') {
+ /* With no format string, just print a blank line */
+ *bb_common_bufsiz1 = 0;
+ } else {
+ /* Handle special conversions */
+
+ if (strncmp(date_fmt, "%f", 2) == 0) {
+ date_fmt = (char*)"%Y.%m.%d-%H:%M:%S";
+ }
+
+ /* Generate output string */
+ strftime(bb_common_bufsiz1, 200, date_fmt, &tm_time);
+ }
+ puts(bb_common_bufsiz1);
+
+ return EXIT_SUCCESS;
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/dd.c b/i/pc104/initrd/conf/busybox/coreutils/dd.c
new file mode 100644
index 0000000..4a094e8
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/dd.c
@@ -0,0 +1,249 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini dd implementation for busybox
+ *
+ *
+ * Copyright (C) 2000,2001 Matt Kraai
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "busybox.h"
+#include <signal.h> /* For FEATURE_DD_SIGNAL_HANDLING */
+
+static const struct suffix_mult dd_suffixes[] = {
+ { "c", 1 },
+ { "w", 2 },
+ { "b", 512 },
+ { "kD", 1000 },
+ { "k", 1024 },
+ { "K", 1024 }, // compat with coreutils dd
+ { "MD", 1000000 },
+ { "M", 1048576 },
+ { "GD", 1000000000 },
+ { "G", 1073741824 },
+ { NULL, 0 }
+};
+
+static off_t out_full, out_part, in_full, in_part;
+
+static void dd_output_status(int ATTRIBUTE_UNUSED cur_signal)
+{
+ fprintf(stderr, "%"OFF_FMT"d+%"OFF_FMT"d records in\n"
+ "%"OFF_FMT"d+%"OFF_FMT"d records out\n",
+ in_full, in_part,
+ out_full, out_part);
+}
+
+static ssize_t full_write_or_warn(int fd, const void *buf, size_t len,
+ const char* filename)
+{
+ ssize_t n = full_write(fd, buf, len);
+ if (n < 0)
+ bb_perror_msg("writing '%s'", filename);
+ return n;
+}
+
+#if ENABLE_LFS
+#define XATOU_SFX xatoull_sfx
+#else
+#define XATOU_SFX xatoul_sfx
+#endif
+
+int dd_main(int argc, char **argv);
+int dd_main(int argc, char **argv)
+{
+ enum {
+ sync_flag = 1 << 0,
+ noerror = 1 << 1,
+ trunc_flag = 1 << 2,
+ twobufs_flag = 1 << 3,
+ };
+ int flags = trunc_flag;
+ size_t oc = 0, ibs = 512, obs = 512;
+ ssize_t n, w;
+ off_t seek = 0, skip = 0, count = OFF_T_MAX;
+ int oflag, ifd, ofd;
+ const char *infile = NULL, *outfile = NULL;
+ char *ibuf, *obuf;
+
+ if (ENABLE_FEATURE_DD_SIGNAL_HANDLING) {
+ struct sigaction sa;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = dd_output_status;
+ sa.sa_flags = SA_RESTART;
+ sigemptyset(&sa.sa_mask);
+ sigaction(SIGUSR1, &sa, 0);
+ }
+
+ for (n = 1; n < argc; n++) {
+ char *arg = argv[n];
+ /* Must fit into positive ssize_t */
+ if (ENABLE_FEATURE_DD_IBS_OBS && !strncmp("ibs=", arg, 4))
+ ibs = xatoul_range_sfx(arg+4, 0, ((size_t)-1L)/2, dd_suffixes);
+ else if (ENABLE_FEATURE_DD_IBS_OBS && !strncmp("obs=", arg, 4))
+ obs = xatoul_range_sfx(arg+4, 0, ((size_t)-1L)/2, dd_suffixes);
+ else if (!strncmp("bs=", arg, 3))
+ ibs = obs = xatoul_range_sfx(arg+3, 0, ((size_t)-1L)/2, dd_suffixes);
+ /* These can be large: */
+ else if (!strncmp("count=", arg, 6))
+ count = XATOU_SFX(arg+6, dd_suffixes);
+ else if (!strncmp("seek=", arg, 5))
+ seek = XATOU_SFX(arg+5, dd_suffixes);
+ else if (!strncmp("skip=", arg, 5))
+ skip = XATOU_SFX(arg+5, dd_suffixes);
+
+ else if (!strncmp("if=", arg, 3))
+ infile = arg+3;
+ else if (!strncmp("of=", arg, 3))
+ outfile = arg+3;
+ else if (ENABLE_FEATURE_DD_IBS_OBS && !strncmp("conv=", arg, 5)) {
+ arg += 5;
+ while (1) {
+ if (!strncmp("notrunc", arg, 7)) {
+ flags &= ~trunc_flag;
+ arg += 7;
+ } else if (!strncmp("sync", arg, 4)) {
+ flags |= sync_flag;
+ arg += 4;
+ } else if (!strncmp("noerror", arg, 7)) {
+ flags |= noerror;
+ arg += 7;
+ } else {
+ bb_error_msg_and_die(bb_msg_invalid_arg, arg, "conv");
+ }
+ if (arg[0] == '\0') break;
+ if (*arg++ != ',') bb_show_usage();
+ }
+ } else
+ bb_show_usage();
+ }
+
+ ibuf = obuf = xmalloc(ibs);
+ if (ibs != obs) {
+ flags |= twobufs_flag;
+ obuf = xmalloc(obs);
+ }
+
+ if (infile != NULL)
+ ifd = xopen(infile, O_RDONLY);
+ else {
+ ifd = STDIN_FILENO;
+ infile = bb_msg_standard_input;
+ }
+
+ if (outfile != NULL) {
+ oflag = O_WRONLY | O_CREAT;
+
+ if (!seek && (flags & trunc_flag))
+ oflag |= O_TRUNC;
+
+ ofd = xopen(outfile, oflag);
+
+ if (seek && (flags & trunc_flag)) {
+ if (ftruncate(ofd, seek * obs) < 0) {
+ struct stat st;
+
+ if (fstat(ofd, &st) < 0 || S_ISREG(st.st_mode) ||
+ S_ISDIR(st.st_mode))
+ goto die_outfile;
+ }
+ }
+ } else {
+ ofd = STDOUT_FILENO;
+ outfile = bb_msg_standard_output;
+ }
+
+ if (skip) {
+ if (lseek(ifd, skip * ibs, SEEK_CUR) < 0) {
+ while (skip-- > 0) {
+ n = safe_read(ifd, ibuf, ibs);
+ if (n < 0)
+ bb_perror_msg_and_die("%s", infile);
+ if (n == 0)
+ break;
+ }
+ }
+ }
+
+ if (seek) {
+ if (lseek(ofd, seek * obs, SEEK_CUR) < 0)
+ goto die_outfile;
+ }
+
+ while (in_full + in_part != count) {
+ if (flags & noerror) {
+ /* Pre-zero the buffer when doing the noerror thing */
+ memset(ibuf, '\0', ibs);
+ }
+
+ n = safe_read(ifd, ibuf, ibs);
+ if (n == 0)
+ break;
+ if (n < 0) {
+ if (flags & noerror) {
+ n = ibs;
+ bb_perror_msg("%s", infile);
+ } else
+ bb_perror_msg_and_die("%s", infile);
+ }
+ if ((size_t)n == ibs)
+ in_full++;
+ else {
+ in_part++;
+ if (flags & sync_flag) {
+ memset(ibuf + n, '\0', ibs - n);
+ n = ibs;
+ }
+ }
+ if (flags & twobufs_flag) {
+ char *tmp = ibuf;
+ while (n) {
+ size_t d = obs - oc;
+
+ if (d > n)
+ d = n;
+ memcpy(obuf + oc, tmp, d);
+ n -= d;
+ tmp += d;
+ oc += d;
+ if (oc == obs) {
+ w = full_write_or_warn(ofd, obuf, obs, outfile);
+ if (w < 0) goto out_status;
+ if (w == obs)
+ out_full++;
+ else if (w > 0)
+ out_part++;
+ oc = 0;
+ }
+ }
+ } else {
+ w = full_write_or_warn(ofd, ibuf, n, outfile);
+ if (w < 0) goto out_status;
+ if (w == obs)
+ out_full++;
+ else if (w > 0)
+ out_part++;
+ }
+ }
+
+ if (ENABLE_FEATURE_DD_IBS_OBS && oc) {
+ w = full_write_or_warn(ofd, obuf, oc, outfile);
+ if (w < 0) goto out_status;
+ if (w > 0)
+ out_part++;
+ }
+ if (close(ifd) < 0) {
+ bb_perror_msg_and_die("%s", infile);
+ }
+
+ if (close(ofd) < 0) {
+ die_outfile:
+ bb_perror_msg_and_die("%s", outfile);
+ }
+ out_status:
+ dd_output_status(0);
+
+ return EXIT_SUCCESS;
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/df.c b/i/pc104/initrd/conf/busybox/coreutils/df.c
new file mode 100644
index 0000000..484cabb
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/df.c
@@ -0,0 +1,155 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini df implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ * based on original code by (I think) Bruce Perens <bruce@pixar.com>.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/* BB_AUDIT SUSv3 _NOT_ compliant -- options -P and -t missing. Also blocksize. */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/df.html */
+
+/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
+ *
+ * Size reduction. Removed floating point dependency. Added error checking
+ * on output. Output stats on 0-sized filesystems if specifically listed on
+ * the command line. Properly round *-blocks, Used, and Available quantities.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <mntent.h>
+#include <sys/vfs.h>
+#include "busybox.h"
+
+#ifndef CONFIG_FEATURE_HUMAN_READABLE
+static long kscale(long b, long bs)
+{
+ return ( b * (long long) bs + 1024/2 ) / 1024;
+}
+#endif
+
+int df_main(int argc, char **argv);
+int df_main(int argc, char **argv)
+{
+ long blocks_used;
+ long blocks_percent_used;
+#ifdef CONFIG_FEATURE_HUMAN_READABLE
+ unsigned long df_disp_hr = 1024;
+#endif
+ int status = EXIT_SUCCESS;
+ unsigned opt;
+ FILE *mount_table;
+ struct mntent *mount_entry;
+ struct statfs s;
+ static const char hdr_1k[] = "1k-blocks"; /* default display is kilobytes */
+ const char *disp_units_hdr = hdr_1k;
+
+#ifdef CONFIG_FEATURE_HUMAN_READABLE
+ opt_complementary = "h-km:k-hm:m-hk";
+ opt = getopt32(argc, argv, "hmk");
+ if (opt & 1) {
+ df_disp_hr = 0;
+ disp_units_hdr = " Size";
+ }
+ if (opt & 2) {
+ df_disp_hr = 1024*1024;
+ disp_units_hdr = "1M-blocks";
+ }
+#else
+ opt = getopt32(argc, argv, "k");
+#endif
+
+ printf("Filesystem%11s%-15sUsed Available Use%% Mounted on\n",
+ "", disp_units_hdr);
+
+ mount_table = NULL;
+ argv += optind;
+ if (optind >= argc) {
+ mount_table = setmntent(bb_path_mtab_file, "r");
+ if (!mount_table) {
+ bb_perror_msg_and_die(bb_path_mtab_file);
+ }
+ }
+
+ do {
+ const char *device;
+ const char *mount_point;
+
+ if (mount_table) {
+ mount_entry = getmntent(mount_table);
+ if (!mount_entry) {
+ endmntent(mount_table);
+ break;
+ }
+ } else {
+ mount_point = *argv++;
+ if (!mount_point) {
+ break;
+ }
+ mount_entry = find_mount_point(mount_point, bb_path_mtab_file);
+ if (!mount_entry) {
+ bb_error_msg("%s: can't find mount point", mount_point);
+ SET_ERROR:
+ status = EXIT_FAILURE;
+ continue;
+ }
+ }
+
+ device = mount_entry->mnt_fsname;
+ mount_point = mount_entry->mnt_dir;
+
+ if (statfs(mount_point, &s) != 0) {
+ bb_perror_msg("%s", mount_point);
+ goto SET_ERROR;
+ }
+
+ if ((s.f_blocks > 0) || !mount_table){
+ blocks_used = s.f_blocks - s.f_bfree;
+ blocks_percent_used = 0;
+ if (blocks_used + s.f_bavail) {
+ blocks_percent_used = (((long long) blocks_used) * 100
+ + (blocks_used + s.f_bavail)/2
+ ) / (blocks_used + s.f_bavail);
+ }
+
+ if (strcmp(device, "rootfs") == 0) {
+ continue;
+ } else if (strcmp(device, "/dev/root") == 0) {
+ /* Adjusts device to be the real root device,
+ * or leaves device alone if it can't find it */
+ device = find_block_device("/");
+ if (!device) {
+ goto SET_ERROR;
+ }
+ }
+
+#ifdef CONFIG_FEATURE_HUMAN_READABLE
+ printf("%-20s %9s ", device,
+ make_human_readable_str(s.f_blocks, s.f_bsize, df_disp_hr));
+
+ printf("%9s ",
+ make_human_readable_str( (s.f_blocks - s.f_bfree),
+ s.f_bsize, df_disp_hr));
+
+ printf("%9s %3ld%% %s\n",
+ make_human_readable_str(s.f_bavail, s.f_bsize, df_disp_hr),
+ blocks_percent_used, mount_point);
+#else
+ printf("%-20s %9ld %9ld %9ld %3ld%% %s\n",
+ device,
+ kscale(s.f_blocks, s.f_bsize),
+ kscale(s.f_blocks-s.f_bfree, s.f_bsize),
+ kscale(s.f_bavail, s.f_bsize),
+ blocks_percent_used, mount_point);
+#endif
+ }
+
+ } while (1);
+
+ fflush_stdout_and_exit(status);
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/diff.c b/i/pc104/initrd/conf/busybox/coreutils/diff.c
new file mode 100644
index 0000000..fa6ef10
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/diff.c
@@ -0,0 +1,1227 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini diff implementation for busybox, adapted from OpenBSD diff.
+ *
+ * Copyright (C) 2006 by Robert Sullivan <cogito.ergo.cogito@hotmail.com>
+ * Copyright (c) 2003 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Sponsored in part by the Defense Advanced Research Projects
+ * Agency (DARPA) and Air Force Research Laboratory, Air Force
+ * Materiel Command, USAF, under agreement number F39502-99-1-0512.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "busybox.h"
+
+#define FSIZE_MAX 32768
+
+/*
+ * Output flags
+ */
+#define D_HEADER 1 /* Print a header/footer between files */
+#define D_EMPTY1 2 /* Treat first file as empty (/dev/null) */
+#define D_EMPTY2 4 /* Treat second file as empty (/dev/null) */
+
+/*
+ * Status values for print_status() and diffreg() return values
+ * Guide:
+ * D_SAME - files are the same
+ * D_DIFFER - files differ
+ * D_BINARY - binary files differ
+ * D_COMMON - subdirectory common to both dirs
+ * D_ONLY - file only exists in one dir
+ * D_MISMATCH1 - path1 a dir, path2 a file
+ * D_MISMATCH2 - path1 a file, path2 a dir
+ * D_ERROR - error occurred
+ * D_SKIPPED1 - skipped path1 as it is a special file
+ * D_SKIPPED2 - skipped path2 as it is a special file
+ */
+
+#define D_SAME 0
+#define D_DIFFER (1<<0)
+#define D_BINARY (1<<1)
+#define D_COMMON (1<<2)
+#define D_ONLY (1<<3)
+#define D_MISMATCH1 (1<<4)
+#define D_MISMATCH2 (1<<5)
+#define D_ERROR (1<<6)
+#define D_SKIPPED1 (1<<7)
+#define D_SKIPPED2 (1<<8)
+
+/* Command line options */
+#define FLAG_a (1<<0)
+#define FLAG_b (1<<1)
+#define FLAG_d (1<<2)
+#define FLAG_i (1<<3)
+#define FLAG_L (1<<4)
+#define FLAG_N (1<<5)
+#define FLAG_q (1<<6)
+#define FLAG_r (1<<7)
+#define FLAG_s (1<<8)
+#define FLAG_S (1<<9)
+#define FLAG_t (1<<10)
+#define FLAG_T (1<<11)
+#define FLAG_U (1<<12)
+#define FLAG_w (1<<13)
+
+/* The following variables should be static, but gcc currently
+ * creates a much bigger object if we do this. [which version of gcc? --vda] */
+/* 4.x, IIRC also 3.x --bernhard */
+/* Works for gcc 3.4.3. Sizes without and with "static":
+ # size busybox.t[34]/coreutils/diff.o
+ text data bss dec hex filename
+ 6969 8 305 7282 1c72 busybox.t3/coreutils/diff.o
+ 6969 8 305 7282 1c72 busybox.t4/coreutils/diff.o
+ --vda
+ */
+/* This is the default number of lines of context. */
+static int context = 3;
+static int status;
+static char *start;
+static const char *label1;
+static const char *label2;
+static struct stat stb1, stb2;
+USE_FEATURE_DIFF_DIR(static char **dl;)
+USE_FEATURE_DIFF_DIR(static int dl_count;)
+
+struct cand {
+ int x;
+ int y;
+ int pred;
+};
+
+static struct line {
+ int serial;
+ int value;
+} *file[2];
+
+/*
+ * The following struct is used to record change information
+ * doing a "context" or "unified" diff. (see routine "change" to
+ * understand the highly mnemonic field names)
+ */
+struct context_vec {
+ int a; /* start line in old file */
+ int b; /* end line in old file */
+ int c; /* start line in new file */
+ int d; /* end line in new file */
+};
+
+static int *J; /* will be overlaid on class */
+static int *class; /* will be overlaid on file[0] */
+static int *klist; /* will be overlaid on file[0] after class */
+static int *member; /* will be overlaid on file[1] */
+static int clen;
+static int len[2];
+static int pref, suff; /* length of prefix and suffix */
+static int slen[2];
+static bool anychange;
+static long *ixnew; /* will be overlaid on file[1] */
+static long *ixold; /* will be overlaid on klist */
+static struct cand *clist; /* merely a free storage pot for candidates */
+static int clistlen; /* the length of clist */
+static struct line *sfile[2]; /* shortened by pruning common prefix/suffix */
+static struct context_vec *context_vec_start;
+static struct context_vec *context_vec_end;
+static struct context_vec *context_vec_ptr;
+
+
+static void print_only(const char *path, size_t dirlen, const char *entry)
+{
+ if (dirlen > 1)
+ dirlen--;
+ printf("Only in %.*s: %s\n", (int) dirlen, path, entry);
+}
+
+
+static void print_status(int val, char *path1, char *path2, char *entry)
+{
+ const char * const _entry = entry ? entry : "";
+ char * const _path1 = entry ? concat_path_file(path1, _entry) : path1;
+ char * const _path2 = entry ? concat_path_file(path2, _entry) : path2;
+
+ switch (val) {
+ case D_ONLY:
+ print_only(path1, strlen(path1), entry);
+ break;
+ case D_COMMON:
+ printf("Common subdirectories: %s and %s\n", _path1, _path2);
+ break;
+ case D_BINARY:
+ printf("Binary files %s and %s differ\n", _path1, _path2);
+ break;
+ case D_DIFFER:
+ if (option_mask32 & FLAG_q)
+ printf("Files %s and %s differ\n", _path1, _path2);
+ break;
+ case D_SAME:
+ if (option_mask32 & FLAG_s)
+ printf("Files %s and %s are identical\n", _path1, _path2);
+ break;
+ case D_MISMATCH1:
+ printf("File %s is a %s while file %s is a %s\n",
+ _path1, "directory", _path2, "regular file");
+ break;
+ case D_MISMATCH2:
+ printf("File %s is a %s while file %s is a %s\n",
+ _path1, "regular file", _path2, "directory");
+ break;
+ case D_SKIPPED1:
+ printf("File %s is not a regular file or directory and was skipped\n",
+ _path1);
+ break;
+ case D_SKIPPED2:
+ printf("File %s is not a regular file or directory and was skipped\n",
+ _path2);
+ break;
+ }
+ if (entry) {
+ free(_path1);
+ free(_path2);
+ }
+}
+static void fiddle_sum(int *sum, int t)
+{
+ *sum = (int)(*sum * 127 + t);
+}
+/*
+ * Hash function taken from Robert Sedgewick, Algorithms in C, 3d ed., p 578.
+ */
+static int readhash(FILE * f)
+{
+ int i, t, space;
+ int sum;
+
+ sum = 1;
+ space = 0;
+ if (!(option_mask32 & (FLAG_b | FLAG_w))) {
+ for (i = 0; (t = getc(f)) != '\n'; i++) {
+ if (t == EOF) {
+ if (i == 0)
+ return 0;
+ break;
+ }
+ fiddle_sum(&sum, t);
+ }
+ } else {
+ for (i = 0;;) {
+ switch (t = getc(f)) {
+ case '\t':
+ case '\r':
+ case '\v':
+ case '\f':
+ case ' ':
+ space++;
+ continue;
+ default:
+ if (space && !(option_mask32 & FLAG_w)) {
+ i++;
+ space = 0;
+ }
+ fiddle_sum(&sum, t);
+ i++;
+ continue;
+ case EOF:
+ if (i == 0)
+ return 0;
+ /* FALLTHROUGH */
+ case '\n':
+ break;
+ }
+ break;
+ }
+ }
+ /*
+ * There is a remote possibility that we end up with a zero sum.
+ * Zero is used as an EOF marker, so return 1 instead.
+ */
+ return (sum == 0 ? 1 : sum);
+}
+
+
+/*
+ * Check to see if the given files differ.
+ * Returns 0 if they are the same, 1 if different, and -1 on error.
+ */
+static int files_differ(FILE * f1, FILE * f2, int flags)
+{
+ size_t i, j;
+
+ if ((flags & (D_EMPTY1 | D_EMPTY2)) || stb1.st_size != stb2.st_size
+ || (stb1.st_mode & S_IFMT) != (stb2.st_mode & S_IFMT)
+ ) {
+ return 1;
+ }
+ while (1) {
+ i = fread(bb_common_bufsiz1, 1, BUFSIZ/2, f1);
+ j = fread(bb_common_bufsiz1 + BUFSIZ/2, 1, BUFSIZ/2, f2);
+ if (i != j)
+ return 1;
+ if (i == 0)
+ return (ferror(f1) || ferror(f2));
+ if (memcmp(bb_common_bufsiz1,
+ bb_common_bufsiz1 + BUFSIZ/2, i) != 0)
+ return 1;
+ }
+}
+
+
+static void prepare(int i, FILE * fd, off_t filesize)
+{
+ struct line *p;
+ int h;
+ size_t j, sz;
+
+ rewind(fd);
+
+ sz = (filesize <= FSIZE_MAX ? filesize : FSIZE_MAX) / 25;
+ if (sz < 100)
+ sz = 100;
+
+ p = xmalloc((sz + 3) * sizeof(struct line));
+ j = 0;
+ while ((h = readhash(fd))) {
+ if (j == sz) {
+ sz = sz * 3 / 2;
+ p = xrealloc(p, (sz + 3) * sizeof(struct line));
+ }
+ p[++j].value = h;
+ }
+ len[i] = j;
+ file[i] = p;
+}
+
+
+static void prune(void)
+{
+ int i, j;
+
+ for (pref = 0; pref < len[0] && pref < len[1] &&
+ file[0][pref + 1].value == file[1][pref + 1].value; pref++)
+ ;
+ for (suff = 0; suff < len[0] - pref && suff < len[1] - pref &&
+ file[0][len[0] - suff].value == file[1][len[1] - suff].value;
+ suff++)
+ ;
+ for (j = 0; j < 2; j++) {
+ sfile[j] = file[j] + pref;
+ slen[j] = len[j] - pref - suff;
+ for (i = 0; i <= slen[j]; i++)
+ sfile[j][i].serial = i;
+ }
+}
+
+
+static void equiv(struct line *a, int n, struct line *b, int m, int *c)
+{
+ int i, j;
+
+ i = j = 1;
+ while (i <= n && j <= m) {
+ if (a[i].value < b[j].value)
+ a[i++].value = 0;
+ else if (a[i].value == b[j].value)
+ a[i++].value = j;
+ else
+ j++;
+ }
+ while (i <= n)
+ a[i++].value = 0;
+ b[m + 1].value = 0;
+ j = 0;
+ while (++j <= m) {
+ c[j] = -b[j].serial;
+ while (b[j + 1].value == b[j].value) {
+ j++;
+ c[j] = b[j].serial;
+ }
+ }
+ c[j] = -1;
+}
+
+
+static int isqrt(int n)
+{
+ int y, x;
+
+ if (n == 0)
+ return 0;
+ x = 1;
+ do {
+ y = x;
+ x = n / x;
+ x += y;
+ x /= 2;
+ } while ((x - y) > 1 || (x - y) < -1);
+
+ return x;
+}
+
+
+static int newcand(int x, int y, int pred)
+{
+ struct cand *q;
+
+ if (clen == clistlen) {
+ clistlen = clistlen * 11 / 10;
+ clist = xrealloc(clist, clistlen * sizeof(struct cand));
+ }
+ q = clist + clen;
+ q->x = x;
+ q->y = y;
+ q->pred = pred;
+ return clen++;
+}
+
+
+static int search(int *c, int k, int y)
+{
+ int i, j, l, t;
+
+ if (clist[c[k]].y < y) /* quick look for typical case */
+ return k + 1;
+ i = 0;
+ j = k + 1;
+ while (1) {
+ l = i + j;
+ if ((l >>= 1) <= i)
+ break;
+ t = clist[c[l]].y;
+ if (t > y)
+ j = l;
+ else if (t < y)
+ i = l;
+ else
+ return l;
+ }
+ return l + 1;
+}
+
+
+static int stone(int *a, int n, int *b, int *c)
+{
+ int i, k, y, j, l;
+ int oldc, tc, oldl;
+ unsigned int numtries;
+
+#if ENABLE_FEATURE_DIFF_MINIMAL
+ const unsigned int bound =
+ (option_mask32 & FLAG_d) ? UINT_MAX : MAX(256, isqrt(n));
+#else
+ const unsigned int bound = MAX(256, isqrt(n));
+#endif
+ k = 0;
+ c[0] = newcand(0, 0, 0);
+ for (i = 1; i <= n; i++) {
+ j = a[i];
+ if (j == 0)
+ continue;
+ y = -b[j];
+ oldl = 0;
+ oldc = c[0];
+ numtries = 0;
+ do {
+ if (y <= clist[oldc].y)
+ continue;
+ l = search(c, k, y);
+ if (l != oldl + 1)
+ oldc = c[l - 1];
+ if (l <= k) {
+ if (clist[c[l]].y <= y)
+ continue;
+ tc = c[l];
+ c[l] = newcand(i, y, oldc);
+ oldc = tc;
+ oldl = l;
+ numtries++;
+ } else {
+ c[l] = newcand(i, y, oldc);
+ k++;
+ break;
+ }
+ } while ((y = b[++j]) > 0 && numtries < bound);
+ }
+ return k;
+}
+
+
+static void unravel(int p)
+{
+ struct cand *q;
+ int i;
+
+ for (i = 0; i <= len[0]; i++)
+ J[i] = i <= pref ? i : i > len[0] - suff ? i + len[1] - len[0] : 0;
+ for (q = clist + p; q->y != 0; q = clist + q->pred)
+ J[q->x + pref] = q->y + pref;
+}
+
+
+static void unsort(struct line *f, int l, int *b)
+{
+ int *a, i;
+
+ a = xmalloc((l + 1) * sizeof(int));
+ for (i = 1; i <= l; i++)
+ a[f[i].serial] = f[i].value;
+ for (i = 1; i <= l; i++)
+ b[i] = a[i];
+ free(a);
+}
+
+
+static int skipline(FILE * f)
+{
+ int i, c;
+
+ for (i = 1; (c = getc(f)) != '\n' && c != EOF; i++)
+ continue;
+ return i;
+}
+
+
+/*
+ * Check does double duty:
+ * 1. ferret out any fortuitous correspondences due
+ * to confounding by hashing (which result in "jackpot")
+ * 2. collect random access indexes to the two files
+ */
+static void check(FILE * f1, FILE * f2)
+{
+ int i, j, jackpot, c, d;
+ long ctold, ctnew;
+
+ rewind(f1);
+ rewind(f2);
+ j = 1;
+ ixold[0] = ixnew[0] = 0;
+ jackpot = 0;
+ ctold = ctnew = 0;
+ for (i = 1; i <= len[0]; i++) {
+ if (J[i] == 0) {
+ ixold[i] = ctold += skipline(f1);
+ continue;
+ }
+ while (j < J[i]) {
+ ixnew[j] = ctnew += skipline(f2);
+ j++;
+ }
+ if ((option_mask32 & FLAG_b) || (option_mask32 & FLAG_w)
+ || (option_mask32 & FLAG_i)) {
+ while (1) {
+ c = getc(f1);
+ d = getc(f2);
+ /*
+ * GNU diff ignores a missing newline
+ * in one file if bflag || wflag.
+ */
+ if (((option_mask32 & FLAG_b) || (option_mask32 & FLAG_w)) &&
+ ((c == EOF && d == '\n') || (c == '\n' && d == EOF))) {
+ break;
+ }
+ ctold++;
+ ctnew++;
+ if ((option_mask32 & FLAG_b) && isspace(c) && isspace(d)) {
+ do {
+ if (c == '\n')
+ break;
+ ctold++;
+ } while (isspace(c = getc(f1)));
+ do {
+ if (d == '\n')
+ break;
+ ctnew++;
+ } while (isspace(d = getc(f2)));
+ } else if (option_mask32 & FLAG_w) {
+ while (isspace(c) && c != '\n') {
+ c = getc(f1);
+ ctold++;
+ }
+ while (isspace(d) && d != '\n') {
+ d = getc(f2);
+ ctnew++;
+ }
+ }
+ if (c != d) {
+ jackpot++;
+ J[i] = 0;
+ if (c != '\n' && c != EOF)
+ ctold += skipline(f1);
+ if (d != '\n' && c != EOF)
+ ctnew += skipline(f2);
+ break;
+ }
+ if (c == '\n' || c == EOF)
+ break;
+ }
+ } else {
+ while (1) {
+ ctold++;
+ ctnew++;
+ if ((c = getc(f1)) != (d = getc(f2))) {
+ J[i] = 0;
+ if (c != '\n' && c != EOF)
+ ctold += skipline(f1);
+ if (d != '\n' && c != EOF)
+ ctnew += skipline(f2);
+ break;
+ }
+ if (c == '\n' || c == EOF)
+ break;
+ }
+ }
+ ixold[i] = ctold;
+ ixnew[j] = ctnew;
+ j++;
+ }
+ for (; j <= len[1]; j++)
+ ixnew[j] = ctnew += skipline(f2);
+}
+
+
+/* shellsort CACM #201 */
+static void sort(struct line *a, int n)
+{
+ struct line *ai, *aim, w;
+ int j, m = 0, k;
+
+ if (n == 0)
+ return;
+ for (j = 1; j <= n; j *= 2)
+ m = 2 * j - 1;
+ for (m /= 2; m != 0; m /= 2) {
+ k = n - m;
+ for (j = 1; j <= k; j++) {
+ for (ai = &a[j]; ai > a; ai -= m) {
+ aim = &ai[m];
+ if (aim < ai)
+ break; /* wraparound */
+ if (aim->value > ai[0].value ||
+ (aim->value == ai[0].value && aim->serial > ai[0].serial))
+ break;
+ w.value = ai[0].value;
+ ai[0].value = aim->value;
+ aim->value = w.value;
+ w.serial = ai[0].serial;
+ ai[0].serial = aim->serial;
+ aim->serial = w.serial;
+ }
+ }
+ }
+}
+
+
+static void uni_range(int a, int b)
+{
+ if (a < b)
+ printf("%d,%d", a, b - a + 1);
+ else if (a == b)
+ printf("%d", b);
+ else
+ printf("%d,0", b);
+}
+
+
+static void fetch(long *f, int a, int b, FILE * lb, int ch)
+{
+ int i, j, c, lastc, col, nc;
+
+ if (a > b)
+ return;
+ for (i = a; i <= b; i++) {
+ fseek(lb, f[i - 1], SEEK_SET);
+ nc = f[i] - f[i - 1];
+ if (ch != '\0') {
+ putchar(ch);
+ if (option_mask32 & FLAG_T)
+ putchar('\t');
+ }
+ col = 0;
+ for (j = 0, lastc = '\0'; j < nc; j++, lastc = c) {
+ if ((c = getc(lb)) == EOF) {
+ printf("\n\\ No newline at end of file\n");
+ return;
+ }
+ if (c == '\t' && (option_mask32 & FLAG_t)) {
+ do {
+ putchar(' ');
+ } while (++col & 7);
+ } else {
+ putchar(c);
+ col++;
+ }
+ }
+ }
+}
+
+
+static int asciifile(FILE * f)
+{
+#if ENABLE_FEATURE_DIFF_BINARY
+ int i, cnt;
+#endif
+
+ if ((option_mask32 & FLAG_a) || f == NULL)
+ return 1;
+
+#if ENABLE_FEATURE_DIFF_BINARY
+ rewind(f);
+ cnt = fread(bb_common_bufsiz1, 1, BUFSIZ, f);
+ for (i = 0; i < cnt; i++) {
+ if (!isprint(bb_common_bufsiz1[i])
+ && !isspace(bb_common_bufsiz1[i])) {
+ return 0;
+ }
+ }
+#endif
+ return 1;
+}
+
+
+/* dump accumulated "unified" diff changes */
+static void dump_unified_vec(FILE * f1, FILE * f2)
+{
+ struct context_vec *cvp = context_vec_start;
+ int lowa, upb, lowc, upd;
+ int a, b, c, d;
+ char ch;
+
+ if (context_vec_start > context_vec_ptr)
+ return;
+
+ b = d = 0; /* gcc */
+ lowa = MAX(1, cvp->a - context);
+ upb = MIN(len[0], context_vec_ptr->b + context);
+ lowc = MAX(1, cvp->c - context);
+ upd = MIN(len[1], context_vec_ptr->d + context);
+
+ printf("@@ -");
+ uni_range(lowa, upb);
+ printf(" +");
+ uni_range(lowc, upd);
+ printf(" @@\n");
+
+ /*
+ * Output changes in "unified" diff format--the old and new lines
+ * are printed together.
+ */
+ for (; cvp <= context_vec_ptr; cvp++) {
+ a = cvp->a;
+ b = cvp->b;
+ c = cvp->c;
+ d = cvp->d;
+
+ /*
+ * c: both new and old changes
+ * d: only changes in the old file
+ * a: only changes in the new file
+ */
+ if (a <= b && c <= d)
+ ch = 'c';
+ else
+ ch = (a <= b) ? 'd' : 'a';
+ if (ch == 'c' || ch == 'd') {
+ fetch(ixold, lowa, a - 1, f1, ' ');
+ fetch(ixold, a, b, f1, '-');
+ }
+ if (ch == 'a')
+ fetch(ixnew, lowc, c - 1, f2, ' ');
+ if (ch == 'c' || ch == 'a')
+ fetch(ixnew, c, d, f2, '+');
+ lowa = b + 1;
+ lowc = d + 1;
+ }
+ fetch(ixnew, d + 1, upd, f2, ' ');
+
+ context_vec_ptr = context_vec_start - 1;
+}
+
+
+static void print_header(const char *file1, const char *file2)
+{
+ if (label1)
+ printf("--- %s\n", label1);
+ else
+ printf("--- %s\t%s", file1, ctime(&stb1.st_mtime));
+ if (label2)
+ printf("+++ %s\n", label2);
+ else
+ printf("+++ %s\t%s", file2, ctime(&stb2.st_mtime));
+}
+
+
+/*
+ * Indicate that there is a difference between lines a and b of the from file
+ * to get to lines c to d of the to file. If a is greater than b then there
+ * are no lines in the from file involved and this means that there were
+ * lines appended (beginning at b). If c is greater than d then there are
+ * lines missing from the to file.
+ */
+static void change(char *file1, FILE * f1, char *file2, FILE * f2, int a,
+ int b, int c, int d)
+{
+ static size_t max_context = 64;
+
+ if ((a > b && c > d) || (option_mask32 & FLAG_q)) {
+ anychange = 1;
+ return;
+ }
+
+ /*
+ * Allocate change records as needed.
+ */
+ if (context_vec_ptr == context_vec_end - 1) {
+ ptrdiff_t offset = context_vec_ptr - context_vec_start;
+
+ max_context <<= 1;
+ context_vec_start = xrealloc(context_vec_start,
+ max_context * sizeof(struct context_vec));
+ context_vec_end = context_vec_start + max_context;
+ context_vec_ptr = context_vec_start + offset;
+ }
+ if (anychange == 0) {
+ /*
+ * Print the context/unidiff header first time through.
+ */
+ print_header(file1, file2);
+ } else if (a > context_vec_ptr->b + (2 * context) + 1 &&
+ c > context_vec_ptr->d + (2 * context) + 1) {
+ /*
+ * If this change is more than 'context' lines from the
+ * previous change, dump the record and reset it.
+ */
+ dump_unified_vec(f1, f2);
+ }
+ context_vec_ptr++;
+ context_vec_ptr->a = a;
+ context_vec_ptr->b = b;
+ context_vec_ptr->c = c;
+ context_vec_ptr->d = d;
+ anychange = 1;
+}
+
+
+static void output(char *file1, FILE * f1, char *file2, FILE * f2)
+{
+ /* Note that j0 and j1 can't be used as they are defined in math.h.
+ * This also allows the rather amusing variable 'j00'... */
+ int m, i0, i1, j00, j01;
+
+ rewind(f1);
+ rewind(f2);
+ m = len[0];
+ J[0] = 0;
+ J[m + 1] = len[1] + 1;
+ for (i0 = 1; i0 <= m; i0 = i1 + 1) {
+ while (i0 <= m && J[i0] == J[i0 - 1] + 1)
+ i0++;
+ j00 = J[i0 - 1] + 1;
+ i1 = i0 - 1;
+ while (i1 < m && J[i1 + 1] == 0)
+ i1++;
+ j01 = J[i1 + 1] - 1;
+ J[i1] = j01;
+ change(file1, f1, file2, f2, i0, i1, j00, j01);
+ }
+ if (m == 0) {
+ change(file1, f1, file2, f2, 1, 0, 1, len[1]);
+ }
+ if (anychange != 0 && !(option_mask32 & FLAG_q)) {
+ dump_unified_vec(f1, f2);
+ }
+}
+
+/*
+ * The following code uses an algorithm due to Harold Stone,
+ * which finds a pair of longest identical subsequences in
+ * the two files.
+ *
+ * The major goal is to generate the match vector J.
+ * J[i] is the index of the line in file1 corresponding
+ * to line i file0. J[i] = 0 if there is no
+ * such line in file1.
+ *
+ * Lines are hashed so as to work in core. All potential
+ * matches are located by sorting the lines of each file
+ * on the hash (called ``value''). In particular, this
+ * collects the equivalence classes in file1 together.
+ * Subroutine equiv replaces the value of each line in
+ * file0 by the index of the first element of its
+ * matching equivalence in (the reordered) file1.
+ * To save space equiv squeezes file1 into a single
+ * array member in which the equivalence classes
+ * are simply concatenated, except that their first
+ * members are flagged by changing sign.
+ *
+ * Next the indices that point into member are unsorted into
+ * array class according to the original order of file0.
+ *
+ * The cleverness lies in routine stone. This marches
+ * through the lines of file0, developing a vector klist
+ * of "k-candidates". At step i a k-candidate is a matched
+ * pair of lines x,y (x in file0 y in file1) such that
+ * there is a common subsequence of length k
+ * between the first i lines of file0 and the first y
+ * lines of file1, but there is no such subsequence for
+ * any smaller y. x is the earliest possible mate to y
+ * that occurs in such a subsequence.
+ *
+ * Whenever any of the members of the equivalence class of
+ * lines in file1 matable to a line in file0 has serial number
+ * less than the y of some k-candidate, that k-candidate
+ * with the smallest such y is replaced. The new
+ * k-candidate is chained (via pred) to the current
+ * k-1 candidate so that the actual subsequence can
+ * be recovered. When a member has serial number greater
+ * that the y of all k-candidates, the klist is extended.
+ * At the end, the longest subsequence is pulled out
+ * and placed in the array J by unravel
+ *
+ * With J in hand, the matches there recorded are
+ * checked against reality to assure that no spurious
+ * matches have crept in due to hashing. If they have,
+ * they are broken, and "jackpot" is recorded--a harmless
+ * matter except that a true match for a spuriously
+ * mated line may now be unnecessarily reported as a change.
+ *
+ * Much of the complexity of the program comes simply
+ * from trying to minimize core utilization and
+ * maximize the range of doable problems by dynamically
+ * allocating what is needed and reusing what is not.
+ * The core requirements for problems larger than somewhat
+ * are (in words) 2*length(file0) + length(file1) +
+ * 3*(number of k-candidates installed), typically about
+ * 6n words for files of length n.
+ */
+static unsigned diffreg(char * ofile1, char * ofile2, int flags)
+{
+ char *file1 = ofile1;
+ char *file2 = ofile2;
+ FILE *f1 = stdin, *f2 = stdin;
+ unsigned rval;
+ int i;
+
+ anychange = 0;
+ context_vec_ptr = context_vec_start - 1;
+
+ if (S_ISDIR(stb1.st_mode) != S_ISDIR(stb2.st_mode))
+ return (S_ISDIR(stb1.st_mode) ? D_MISMATCH1 : D_MISMATCH2);
+
+ rval = D_SAME;
+
+ if (LONE_DASH(file1) && LONE_DASH(file2))
+ goto closem;
+
+ if (flags & D_EMPTY1)
+ f1 = xfopen(bb_dev_null, "r");
+ else if (NOT_LONE_DASH(file1))
+ f1 = xfopen(file1, "r");
+ if (flags & D_EMPTY2)
+ f2 = xfopen(bb_dev_null, "r");
+ else if (NOT_LONE_DASH(file2))
+ f2 = xfopen(file2, "r");
+
+/* We can't diff non-seekable stream - we use rewind(), fseek().
+ * This can be fixed (volunteers?).
+ * Meanwhile we should check it here by stat'ing input fds,
+ * but I am lazy and check that in main() instead.
+ * Check in main won't catch "diffing fifos buried in subdirectories"
+ * failure scenario - not very likely in real life... */
+
+ i = files_differ(f1, f2, flags);
+ if (i == 0)
+ goto closem;
+ else if (i != 1) { /* 1 == ok */
+ /* error */
+ status |= 2;
+ goto closem;
+ }
+
+ if (!asciifile(f1) || !asciifile(f2)) {
+ rval = D_BINARY;
+ status |= 1;
+ goto closem;
+ }
+
+ prepare(0, f1, stb1.st_size);
+ prepare(1, f2, stb2.st_size);
+ prune();
+ sort(sfile[0], slen[0]);
+ sort(sfile[1], slen[1]);
+
+ member = (int *) file[1];
+ equiv(sfile[0], slen[0], sfile[1], slen[1], member);
+ member = xrealloc(member, (slen[1] + 2) * sizeof(int));
+
+ class = (int *) file[0];
+ unsort(sfile[0], slen[0], class);
+ class = xrealloc(class, (slen[0] + 2) * sizeof(int));
+
+ klist = xmalloc((slen[0] + 2) * sizeof(int));
+ clen = 0;
+ clistlen = 100;
+ clist = xmalloc(clistlen * sizeof(struct cand));
+ i = stone(class, slen[0], member, klist);
+ free(member);
+ free(class);
+
+ J = xrealloc(J, (len[0] + 2) * sizeof(int));
+ unravel(klist[i]);
+ free(clist);
+ free(klist);
+
+ ixold = xrealloc(ixold, (len[0] + 2) * sizeof(long));
+ ixnew = xrealloc(ixnew, (len[1] + 2) * sizeof(long));
+ check(f1, f2);
+ output(file1, f1, file2, f2);
+
+ closem:
+ if (anychange) {
+ status |= 1;
+ if (rval == D_SAME)
+ rval = D_DIFFER;
+ }
+ fclose_if_not_stdin(f1);
+ fclose_if_not_stdin(f2);
+ if (file1 != ofile1)
+ free(file1);
+ if (file2 != ofile2)
+ free(file2);
+ return rval;
+}
+
+
+#if ENABLE_FEATURE_DIFF_DIR
+static void do_diff(char *dir1, char *path1, char *dir2, char *path2)
+{
+ int flags = D_HEADER;
+ int val;
+
+ char *fullpath1 = concat_path_file(dir1, path1);
+ char *fullpath2 = concat_path_file(dir2, path2);
+
+ if (stat(fullpath1, &stb1) != 0) {
+ flags |= D_EMPTY1;
+ memset(&stb1, 0, sizeof(stb1));
+ if (ENABLE_FEATURE_CLEAN_UP)
+ free(fullpath1);
+ fullpath1 = concat_path_file(dir1, path2);
+ }
+ if (stat(fullpath2, &stb2) != 0) {
+ flags |= D_EMPTY2;
+ memset(&stb2, 0, sizeof(stb2));
+ stb2.st_mode = stb1.st_mode;
+ if (ENABLE_FEATURE_CLEAN_UP)
+ free(fullpath2);
+ fullpath2 = concat_path_file(dir2, path1);
+ }
+
+ if (stb1.st_mode == 0)
+ stb1.st_mode = stb2.st_mode;
+
+ if (S_ISDIR(stb1.st_mode) && S_ISDIR(stb2.st_mode)) {
+ printf("Common subdirectories: %s and %s\n", fullpath1, fullpath2);
+ return;
+ }
+
+ if (!S_ISREG(stb1.st_mode) && !S_ISDIR(stb1.st_mode))
+ val = D_SKIPPED1;
+ else if (!S_ISREG(stb2.st_mode) && !S_ISDIR(stb2.st_mode))
+ val = D_SKIPPED2;
+ else
+ val = diffreg(fullpath1, fullpath2, flags);
+
+ print_status(val, fullpath1, fullpath2, NULL);
+}
+#endif
+
+
+#if ENABLE_FEATURE_DIFF_DIR
+static int dir_strcmp(const void *p1, const void *p2)
+{
+ return strcmp(*(char *const *) p1, *(char *const *) p2);
+}
+
+
+/* This function adds a filename to dl, the directory listing. */
+static int add_to_dirlist(const char *filename,
+ struct stat ATTRIBUTE_UNUSED * sb, void *userdata,
+ int depth ATTRIBUTE_UNUSED)
+{
+ /* +2: with space for eventual trailing NULL */
+ dl = xrealloc(dl, (dl_count+2) * sizeof(dl[0]));
+ dl[dl_count] = xstrdup(filename + (int)(ptrdiff_t)userdata);
+ dl_count++;
+ return TRUE;
+}
+
+
+/* This returns a sorted directory listing. */
+static char **get_dir(char *path)
+{
+ dl_count = 0;
+ dl = NULL;
+
+ /* If -r has been set, then the recursive_action function will be
+ * used. Unfortunately, this outputs the root directory along with
+ * the recursed paths, so use void *userdata to specify the string
+ * length of the root directory - '(void*)(strlen(path)+)'.
+ * add_to_dirlist then removes root dir prefix. */
+
+ if (option_mask32 & FLAG_r) {
+ recursive_action(path, TRUE, TRUE, FALSE, add_to_dirlist, NULL,
+ (void*)(strlen(path)+1), 0);
+ } else {
+ DIR *dp;
+ struct dirent *ep;
+
+ dp = warn_opendir(path);
+ while ((ep = readdir(dp))) {
+ if (!strcmp(ep->d_name, "..") || LONE_CHAR(ep->d_name, '.'))
+ continue;
+ add_to_dirlist(ep->d_name, NULL, (void*)(int)0, 0);
+ }
+ closedir(dp);
+ }
+
+ /* Sort dl alphabetically. */
+ qsort(dl, dl_count, sizeof(char *), dir_strcmp);
+
+ dl[dl_count] = NULL;
+ return dl;
+}
+
+
+static void diffdir(char *p1, char *p2)
+{
+ char **dirlist1, **dirlist2;
+ char *dp1, *dp2;
+ int pos;
+
+ /* Check for trailing slashes. */
+ dp1 = last_char_is(p1, '/');
+ if (dp1 != NULL)
+ *dp1 = '\0';
+ dp2 = last_char_is(p2, '/');
+ if (dp2 != NULL)
+ *dp2 = '\0';
+
+ /* Get directory listings for p1 and p2. */
+
+ dirlist1 = get_dir(p1);
+ dirlist2 = get_dir(p2);
+
+ /* If -S was set, find the starting point. */
+ if (start) {
+ while (*dirlist1 != NULL && strcmp(*dirlist1, start) < 0)
+ dirlist1++;
+ while (*dirlist2 != NULL && strcmp(*dirlist2, start) < 0)
+ dirlist2++;
+ if ((*dirlist1 == NULL) || (*dirlist2 == NULL))
+ bb_error_msg(bb_msg_invalid_arg, "NULL", "-S");
+ }
+
+ /* Now that both dirlist1 and dirlist2 contain sorted directory
+ * listings, we can start to go through dirlist1. If both listings
+ * contain the same file, then do a normal diff. Otherwise, behaviour
+ * is determined by whether the -N flag is set. */
+ while (*dirlist1 != NULL || *dirlist2 != NULL) {
+ dp1 = *dirlist1;
+ dp2 = *dirlist2;
+ pos = dp1 == NULL ? 1 : dp2 == NULL ? -1 : strcmp(dp1, dp2);
+ if (pos == 0) {
+ do_diff(p1, dp1, p2, dp2);
+ dirlist1++;
+ dirlist2++;
+ } else if (pos < 0) {
+ if (option_mask32 & FLAG_N)
+ do_diff(p1, dp1, p2, NULL);
+ else
+ print_only(p1, strlen(p1) + 1, dp1);
+ dirlist1++;
+ } else {
+ if (option_mask32 & FLAG_N)
+ do_diff(p1, NULL, p2, dp2);
+ else
+ print_only(p2, strlen(p2) + 1, dp2);
+ dirlist2++;
+ }
+ }
+}
+#endif
+
+
+int diff_main(int argc, char **argv);
+int diff_main(int argc, char **argv)
+{
+ bool gotstdin = 0;
+ char *U_opt;
+ char *f1, *f2;
+ llist_t *L_arg = NULL;
+
+ /* exactly 2 params; collect multiple -L <label> */
+ opt_complementary = "=2:L::";
+ getopt32(argc, argv, "abdiL:NqrsS:tTU:wu"
+ "p" /* ignored (for compatibility) */,
+ &L_arg, &start, &U_opt);
+ /*argc -= optind;*/
+ argv += optind;
+ while (L_arg) {
+ if (label1 && label2)
+ bb_show_usage();
+ if (!label1)
+ label1 = L_arg->data;
+ else { /* then label2 is NULL */
+ label2 = label1;
+ label1 = L_arg->data;
+ }
+ /* we leak L_arg here... */
+ L_arg = L_arg->link;
+ }
+ if (option_mask32 & FLAG_U)
+ context = xatoi_u(U_opt);
+
+ /*
+ * Do sanity checks, fill in stb1 and stb2 and call the appropriate
+ * driver routine. Both drivers use the contents of stb1 and stb2.
+ */
+
+ f1 = argv[0];
+ f2 = argv[1];
+ if (LONE_DASH(f1)) {
+ fstat(STDIN_FILENO, &stb1);
+ gotstdin = 1;
+ } else
+ xstat(f1, &stb1);
+ if (LONE_DASH(f2)) {
+ fstat(STDIN_FILENO, &stb2);
+ gotstdin = 1;
+ } else
+ xstat(f2, &stb2);
+ if (gotstdin && (S_ISDIR(stb1.st_mode) || S_ISDIR(stb2.st_mode)))
+ bb_error_msg_and_die("can't compare - to a directory");
+ if (S_ISDIR(stb1.st_mode) && S_ISDIR(stb2.st_mode)) {
+#if ENABLE_FEATURE_DIFF_DIR
+ diffdir(f1, f2);
+#else
+ bb_error_msg_and_die("directory comparison not supported");
+#endif
+ } else {
+ if (S_ISDIR(stb1.st_mode)) {
+ f1 = concat_path_file(f1, f2);
+ xstat(f1, &stb1);
+ }
+ if (S_ISDIR(stb2.st_mode)) {
+ f2 = concat_path_file(f2, f1);
+ xstat(f2, &stb2);
+ }
+/* XXX: FIXME: */
+/* We can't diff e.g. stdin supplied by a pipe - we use rewind(), fseek().
+ * This can be fixed (volunteers?) */
+ if (!S_ISREG(stb1.st_mode) || !S_ISREG(stb2.st_mode))
+ bb_error_msg_and_die("can't diff non-seekable stream");
+ print_status(diffreg(f1, f2, 0), f1, f2, NULL);
+ }
+ return status;
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/dirname.c b/i/pc104/initrd/conf/busybox/coreutils/dirname.c
new file mode 100644
index 0000000..4ecde31
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/dirname.c
@@ -0,0 +1,27 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini dirname implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/* BB_AUDIT SUSv3 compliant */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/dirname.html */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "busybox.h"
+
+int dirname_main(int argc, char **argv);
+int dirname_main(int argc, char **argv)
+{
+ if (argc != 2) {
+ bb_show_usage();
+ }
+
+ puts(dirname(argv[1]));
+
+ fflush_stdout_and_exit(EXIT_SUCCESS);
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/dos2unix.c b/i/pc104/initrd/conf/busybox/coreutils/dos2unix.c
new file mode 100644
index 0000000..62c1361
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/dos2unix.c
@@ -0,0 +1,116 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * dos2unix for BusyBox
+ *
+ * dos2unix '\n' convertor 0.5.0
+ * based on Unix2Dos 0.9.0 by Peter Hanecak (made 19.2.1997)
+ * Copyright 1997,.. by Peter Hanecak <hanecak@megaloman.sk>.
+ * All rights reserved.
+ *
+ * dos2unix filters reading input from stdin and writing output to stdout.
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+*/
+
+#include "busybox.h"
+
+enum ConvType {
+ CT_UNIX2DOS = 1,
+ CT_DOS2UNIX
+} ConvType;
+
+/* if fn is NULL then input is stdin and output is stdout */
+static int convert(char *fn)
+{
+ FILE *in, *out;
+ int i;
+
+ if (fn != NULL) {
+ in = xfopen(fn, "rw");
+ /*
+ The file is then created with mode read/write and
+ permissions 0666 for glibc 2.0.6 and earlier or
+ 0600 for glibc 2.0.7 and later.
+ */
+ snprintf(bb_common_bufsiz1, sizeof(bb_common_bufsiz1), "%sXXXXXX", fn);
+ /*
+ sizeof bb_common_bufsiz1 is 4096, so it should be big enough to
+ hold the full path. However if the output is truncated the
+ subsequent call to mkstemp would fail.
+ */
+ if ((i = mkstemp(&bb_common_bufsiz1[0])) == -1
+ || chmod(bb_common_bufsiz1, 0600) == -1) {
+ bb_perror_nomsg_and_die();
+ }
+ out = fdopen(i, "w+");
+ if (!out) {
+ close(i);
+ remove(bb_common_bufsiz1);
+ }
+ } else {
+ in = stdin;
+ out = stdout;
+ }
+
+ while ((i = fgetc(in)) != EOF) {
+ if (i == '\r')
+ continue;
+ if (i == '\n') {
+ if (ConvType == CT_UNIX2DOS)
+ fputc('\r', out);
+ fputc('\n', out);
+ continue;
+ }
+ fputc(i, out);
+ }
+
+ if (fn != NULL) {
+ if (fclose(in) < 0 || fclose(out) < 0) {
+ bb_perror_nomsg();
+ remove(bb_common_bufsiz1);
+ return -2;
+ }
+ /* Assume they are both on the same filesystem (which
+ * should be true since we put them into the same directory
+ * so we _should_ be ok, but you never know... */
+ if (rename(bb_common_bufsiz1, fn) < 0) {
+ bb_perror_msg("cannot rename '%s' as '%s'", bb_common_bufsiz1, fn);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int dos2unix_main(int argc, char *argv[]);
+int dos2unix_main(int argc, char *argv[])
+{
+ int o;
+
+ /* See if we are supposed to be doing dos2unix or unix2dos */
+ if (applet_name[0] == 'd') {
+ ConvType = CT_DOS2UNIX; /*2 */
+ } else {
+ ConvType = CT_UNIX2DOS; /*1 */
+ }
+ /* -u and -d are mutally exclusive */
+ opt_complementary = "?:u--d:d--u";
+ /* process parameters */
+ /* -u convert to unix */
+ /* -d convert to dos */
+ o = getopt32(argc, argv, "du");
+
+ /* Do the conversion requested by an argument else do the default
+ * conversion depending on our name. */
+ if (o)
+ ConvType = o;
+
+ if (optind < argc) {
+ while (optind < argc)
+ if ((o = convert(argv[optind++])) < 0)
+ break;
+ } else
+ o = convert(NULL);
+
+ return o;
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/du.c b/i/pc104/initrd/conf/busybox/coreutils/du.c
new file mode 100644
index 0000000..e5fe5ff
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/du.c
@@ -0,0 +1,247 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini du implementation for busybox
+ *
+ * Copyright (C) 1999,2000,2001 by Lineo, inc. and John Beppu
+ * Copyright (C) 1999,2000,2001 by John Beppu <beppu@codepoet.org>
+ * Copyright (C) 2002 Edward Betts <edward@debian.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/* BB_AUDIT SUSv3 compliant (unless default blocksize set to 1k) */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/du.html */
+
+/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
+ *
+ * Mostly rewritten for SUSv3 compliance and to fix bugs/defects.
+ * 1) Added support for SUSv3 -a, -H, -L, gnu -c, and (busybox) -d options.
+ * The -d option allows setting of max depth (similar to gnu --max-depth).
+ * 2) Fixed incorrect size calculations for links and directories, especially
+ * when errors occurred. Calculates sizes should now match gnu du output.
+ * 3) Added error checking of output.
+ * 4) Fixed busybox bug #1284 involving long overflow with human_readable.
+ */
+
+#include "busybox.h"
+
+#if ENABLE_FEATURE_HUMAN_READABLE
+# if ENABLE_FEATURE_DU_DEFAULT_BLOCKSIZE_1K
+static unsigned long disp_hr = 1024;
+# else
+static unsigned long disp_hr = 512;
+# endif
+#elif ENABLE_FEATURE_DU_DEFAULT_BLOCKSIZE_1K
+static unsigned disp_k = 1;
+#else
+static unsigned disp_k; /* bss inits to 0 */
+#endif
+
+static int max_print_depth = INT_MAX;
+static nlink_t count_hardlinks = 1;
+
+static int status;
+static int print_files;
+static int slink_depth;
+static int du_depth;
+static int one_file_system;
+static dev_t dir_dev;
+
+
+static void print(long size, const char * const filename)
+{
+ /* TODO - May not want to defer error checking here. */
+#if ENABLE_FEATURE_HUMAN_READABLE
+ printf("%s\t%s\n", make_human_readable_str(size, 512, disp_hr),
+ filename);
+#else
+ if (disp_k) {
+ size++;
+ size >>= 1;
+ }
+ printf("%ld\t%s\n", size, filename);
+#endif
+}
+
+/* tiny recursive du */
+static long du(const char * const filename)
+{
+ struct stat statbuf;
+ long sum;
+
+ if (lstat(filename, &statbuf) != 0) {
+ bb_perror_msg("%s", filename);
+ status = EXIT_FAILURE;
+ return 0;
+ }
+
+ if (one_file_system) {
+ if (du_depth == 0) {
+ dir_dev = statbuf.st_dev;
+ } else if (dir_dev != statbuf.st_dev) {
+ return 0;
+ }
+ }
+
+ sum = statbuf.st_blocks;
+
+ if (S_ISLNK(statbuf.st_mode)) {
+ if (slink_depth > du_depth) { /* -H or -L */
+ if (stat(filename, &statbuf) != 0) {
+ bb_perror_msg("%s", filename);
+ status = EXIT_FAILURE;
+ return 0;
+ }
+ sum = statbuf.st_blocks;
+ if (slink_depth == 1) {
+ slink_depth = INT_MAX; /* Convert -H to -L. */
+ }
+ }
+ }
+
+ if (statbuf.st_nlink > count_hardlinks) {
+ /* Add files/directories with links only once */
+ if (is_in_ino_dev_hashtable(&statbuf)) {
+ return 0;
+ }
+ add_to_ino_dev_hashtable(&statbuf, NULL);
+ }
+
+ if (S_ISDIR(statbuf.st_mode)) {
+ DIR *dir;
+ struct dirent *entry;
+ char *newfile;
+
+ dir = warn_opendir(filename);
+ if (!dir) {
+ status = EXIT_FAILURE;
+ return sum;
+ }
+
+ newfile = last_char_is(filename, '/');
+ if (newfile)
+ *newfile = '\0';
+
+ while ((entry = readdir(dir))) {
+ char *name = entry->d_name;
+
+ newfile = concat_subpath_file(filename, name);
+ if (newfile == NULL)
+ continue;
+ ++du_depth;
+ sum += du(newfile);
+ --du_depth;
+ free(newfile);
+ }
+ closedir(dir);
+ } else if (du_depth > print_files) {
+ return sum;
+ }
+ if (du_depth <= max_print_depth) {
+ print(sum, filename);
+ }
+ return sum;
+}
+
+int du_main(int argc, char **argv);
+int du_main(int argc, char **argv)
+{
+ long total;
+ int slink_depth_save;
+ int print_final_total;
+ char *smax_print_depth;
+ unsigned opt;
+
+#if ENABLE_FEATURE_DU_DEFAULT_BLOCKSIZE_1K
+ if (getenv("POSIXLY_CORRECT")) { /* TODO - a new libbb function? */
+#if ENABLE_FEATURE_HUMAN_READABLE
+ disp_hr = 512;
+#else
+ disp_k = 0;
+#endif
+ }
+#endif
+
+ /* Note: SUSv3 specifies that -a and -s options cannot be used together
+ * in strictly conforming applications. However, it also says that some
+ * du implementations may produce output when -a and -s are used together.
+ * gnu du exits with an error code in this case. We choose to simply
+ * ignore -a. This is consistent with -s being equivalent to -d 0.
+ */
+#if ENABLE_FEATURE_HUMAN_READABLE
+ opt_complementary = "h-km:k-hm:m-hk:H-L:L-H:s-d:d-s";
+ opt = getopt32(argc, argv, "aHkLsx" "d:" "lc" "hm", &smax_print_depth);
+ if (opt & (1 << 9)) {
+ /* -h opt */
+ disp_hr = 0;
+ }
+ if (opt & (1 << 10)) {
+ /* -m opt */
+ disp_hr = 1024*1024;
+ }
+ if (opt & (1 << 2)) {
+ /* -k opt */
+ disp_hr = 1024;
+ }
+#else
+ opt_complementary = "H-L:L-H:s-d:d-s";
+ opt = getopt32(argc, argv, "aHkLsx" "d:" "lc", &smax_print_depth);
+#if !ENABLE_FEATURE_DU_DEFAULT_BLOCKSIZE_1K
+ if (opt & (1 << 2)) {
+ /* -k opt */
+ disp_k = 1;
+ }
+#endif
+#endif
+ if (opt & (1 << 0)) {
+ /* -a opt */
+ print_files = INT_MAX;
+ }
+ if (opt & (1 << 1)) {
+ /* -H opt */
+ slink_depth = 1;
+ }
+ if (opt & (1 << 3)) {
+ /* -L opt */
+ slink_depth = INT_MAX;
+ }
+ if (opt & (1 << 4)) {
+ /* -s opt */
+ max_print_depth = 0;
+ }
+ one_file_system = opt & (1 << 5); /* -x opt */
+ if (opt & (1 << 6)) {
+ /* -d opt */
+ max_print_depth = xatoi_u(smax_print_depth);
+ }
+ if (opt & (1 << 7)) {
+ /* -l opt */
+ count_hardlinks = MAXINT(nlink_t);
+ }
+ print_final_total = opt & (1 << 8); /* -c opt */
+
+ /* go through remaining args (if any) */
+ argv += optind;
+ if (optind >= argc) {
+ *--argv = (char*)".";
+ if (slink_depth == 1) {
+ slink_depth = 0;
+ }
+ }
+
+ slink_depth_save = slink_depth;
+ total = 0;
+ do {
+ total += du(*argv);
+ slink_depth = slink_depth_save;
+ } while (*++argv);
+#if ENABLE_FEATURE_CLEAN_UP
+ reset_ino_dev_hashtable();
+#endif
+
+ if (print_final_total) {
+ print(total, "total");
+ }
+
+ fflush_stdout_and_exit(status);
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/echo.c b/i/pc104/initrd/conf/busybox/coreutils/echo.c
new file mode 100644
index 0000000..8270fc0
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/echo.c
@@ -0,0 +1,159 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * echo implementation for busybox
+ *
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ *
+ * Original copyright notice is retained at the end of this file.
+ */
+
+/* BB_AUDIT SUSv3 compliant -- unless configured as fancy echo. */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/echo.html */
+
+/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
+ *
+ * Because of behavioral differences, implemented configurable SUSv3
+ * or 'fancy' gnu-ish behaviors. Also, reduced size and fixed bugs.
+ * 1) In handling '\c' escape, the previous version only suppressed the
+ * trailing newline. SUSv3 specifies _no_ output after '\c'.
+ * 2) SUSv3 specifies that octal escapes are of the form \0{#{#{#}}}.
+ * The previous version version did not allow 4-digit octals.
+ */
+
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "busybox.h"
+
+int bb_echo(char **argv)
+{
+#ifndef CONFIG_FEATURE_FANCY_ECHO
+#define eflag '\\'
+ ++argv;
+#else
+ const char *p;
+ int nflag = 1;
+ int eflag = 0;
+
+ while (*++argv && (**argv == '-')) {
+ /* If it appears that we are handling options, then make sure
+ * that all of the options specified are actually valid.
+ * Otherwise, the string should just be echoed.
+ */
+
+ if (!*(p = *argv + 1)) { /* A single '-', so echo it. */
+ goto just_echo;
+ }
+
+ do {
+ if (strrchr("neE", *p) == 0) {
+ goto just_echo;
+ }
+ } while (*++p);
+
+ /* All of the options in this arg are valid, so handle them. */
+ p = *argv + 1;
+ do {
+ if (*p == 'n') {
+ nflag = 0;
+ } else if (*p == 'e') {
+ eflag = '\\';
+ } else {
+ eflag = 0;
+ }
+ } while (*++p);
+ }
+
+just_echo:
+#endif
+ while (*argv) {
+ int c;
+
+ while ((c = *(*argv)++)) {
+ if (c == eflag) { /* Check for escape seq. */
+ if (**argv == 'c') {
+ /* '\c' means cancel newline and
+ * ignore all subsequent chars. */
+ return 0;
+ }
+#ifndef CONFIG_FEATURE_FANCY_ECHO
+ /* SUSv3 specifies that octal escapes must begin with '0'. */
+ if (((unsigned int)(**argv - '1')) >= 7)
+#endif
+ {
+ /* Since SUSv3 mandates a first digit of 0, 4-digit octals
+ * of the form \0### are accepted. */
+ if ((**argv == '0') && (((unsigned int)(argv[0][1] - '0')) < 8)) {
+ (*argv)++;
+ }
+ /* bb_process_escape_sequence can handle nul correctly */
+ c = bb_process_escape_sequence((const char **) argv);
+ }
+ }
+ putchar(c);
+ }
+
+ if (*++argv) {
+ putchar(' ');
+ }
+ }
+
+#ifdef CONFIG_FEATURE_FANCY_ECHO
+ if (nflag) {
+ putchar('\n');
+ }
+#else
+ putchar('\n');
+#endif
+ return 0;
+}
+
+int echo_main(int argc, char** argv);
+int echo_main(int argc, char** argv)
+{
+ (void)bb_echo(argv);
+ fflush_stdout_and_exit(EXIT_SUCCESS);
+}
+
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change
+ * ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>
+ *
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)echo.c 8.1 (Berkeley) 5/31/93
+ */
diff --git a/i/pc104/initrd/conf/busybox/coreutils/env.c b/i/pc104/initrd/conf/busybox/coreutils/env.c
new file mode 100644
index 0000000..f47d450
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/env.c
@@ -0,0 +1,128 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * env implementation for busybox
+ *
+ * Copyright (c) 1988, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ *
+ * Original copyright notice is retained at the end of this file.
+ *
+ * Modified for BusyBox by Erik Andersen <andersen@codepoet.org>
+ */
+
+/* BB_AUDIT SUSv3 compliant */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/env.html */
+
+/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
+ *
+ * Fixed bug involving exit return codes if execvp fails. Also added
+ * output error checking.
+ */
+
+/*
+ * Modified by Vladimir Oleynik <dzo@simtreas.ru> (C) 2003
+ * - correct "-" option usage
+ * - multiple "-u unsetenv" support
+ * - GNU long option support
+ * - use xfunc_error_retval
+ */
+
+#include "busybox.h"
+#include <errno.h>
+#include <getopt.h> /* struct option */
+
+#if ENABLE_FEATURE_ENV_LONG_OPTIONS
+static const struct option env_long_options[] = {
+ { "ignore-environment", 0, NULL, 'i' },
+ { "unset", 1, NULL, 'u' },
+ { 0, 0, 0, 0 }
+};
+#endif
+
+int env_main(int argc, char** argv);
+int env_main(int argc, char** argv)
+{
+ /* cleanenv was static - why? */
+ char *cleanenv[1];
+ char **ep;
+ unsigned opt;
+ llist_t *unset_env = NULL;
+ extern char **environ;
+
+ opt_complementary = "u::";
+#if ENABLE_FEATURE_ENV_LONG_OPTIONS
+ applet_long_options = env_long_options;
+#endif
+ opt = getopt32(argc, argv, "+iu:", &unset_env);
+ argv += optind;
+ if (*argv && LONE_DASH(argv[0])) {
+ opt |= 1;
+ ++argv;
+ }
+ if (opt & 1) {
+ cleanenv[0] = NULL;
+ environ = cleanenv;
+ } else if (opt & 2) {
+ while (unset_env) {
+ unsetenv(unset_env->data);
+ unset_env = unset_env->link;
+ }
+ }
+
+ while (*argv && (strchr(*argv, '=') != NULL)) {
+ if (putenv(*argv) < 0) {
+ bb_perror_msg_and_die("putenv");
+ }
+ ++argv;
+ }
+
+ if (*argv) {
+ BB_EXECVP(*argv, argv);
+ /* SUSv3-mandated exit codes. */
+ xfunc_error_retval = (errno == ENOENT) ? 127 : 126;
+ bb_perror_msg_and_die("%s", *argv);
+ }
+
+ for (ep = environ; *ep; ep++) {
+ puts(*ep);
+ }
+
+ fflush_stdout_and_exit(0);
+}
+
+/*
+ * Copyright (c) 1988, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change
+ * ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>
+ *
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
diff --git a/i/pc104/initrd/conf/busybox/coreutils/expr.c b/i/pc104/initrd/conf/busybox/coreutils/expr.c
new file mode 100644
index 0000000..b7e33de
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/expr.c
@@ -0,0 +1,506 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini expr implementation for busybox
+ *
+ * based on GNU expr Mike Parker.
+ * Copyright (C) 86, 1991-1997, 1999 Free Software Foundation, Inc.
+ *
+ * Busybox modifications
+ * Copyright (c) 2000 Edward Betts <edward@debian.org>.
+ * Copyright (C) 2003-2005 Vladimir Oleynik <dzo@simtreas.ru>
+ * - reduced 464 bytes.
+ * - 64 math support
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/* This program evaluates expressions. Each token (operator, operand,
+ * parenthesis) of the expression must be a separate argument. The
+ * parser used is a reasonably general one, though any incarnation of
+ * it is language-specific. It is especially nice for expressions.
+ *
+ * No parse tree is needed; a new node is evaluated immediately.
+ * One function can handle multiple operators all of equal precedence,
+ * provided they all associate ((x op x) op x). */
+
+/* no getopt needed */
+
+#include "busybox.h"
+#include "xregex.h"
+
+/* The kinds of value we can have. */
+enum valtype {
+ integer,
+ string
+};
+typedef enum valtype TYPE;
+
+#if ENABLE_EXPR_MATH_SUPPORT_64
+typedef int64_t arith_t;
+
+#define PF_REZ "ll"
+#define PF_REZ_TYPE (long long)
+#define STRTOL(s, e, b) strtoll(s, e, b)
+#else
+typedef long arith_t;
+
+#define PF_REZ "l"
+#define PF_REZ_TYPE (long)
+#define STRTOL(s, e, b) strtol(s, e, b)
+#endif
+
+/* TODO: use bb_strtol[l]? It's easier to check for errors... */
+
+/* A value is.... */
+struct valinfo {
+ TYPE type; /* Which kind. */
+ union { /* The value itself. */
+ arith_t i;
+ char *s;
+ } u;
+};
+typedef struct valinfo VALUE;
+
+/* The arguments given to the program, minus the program name. */
+static char **args;
+
+static VALUE *docolon(VALUE * sv, VALUE * pv);
+static VALUE *eval(void);
+static VALUE *int_value(arith_t i);
+static VALUE *str_value(const char *s);
+static int nextarg(const char *str);
+static int null(VALUE * v);
+static int toarith(VALUE * v);
+static void freev(VALUE * v);
+static void tostring(VALUE * v);
+
+int expr_main(int argc, char **argv);
+int expr_main(int argc, char **argv)
+{
+ VALUE *v;
+
+ if (argc == 1) {
+ bb_error_msg_and_die("too few arguments");
+ }
+
+ args = argv + 1;
+
+ v = eval();
+ if (*args)
+ bb_error_msg_and_die("syntax error");
+
+ if (v->type == integer)
+ printf("%" PF_REZ "d\n", PF_REZ_TYPE v->u.i);
+ else
+ puts(v->u.s);
+
+ fflush_stdout_and_exit(null(v));
+}
+
+/* Return a VALUE for I. */
+
+static VALUE *int_value(arith_t i)
+{
+ VALUE *v;
+
+ v = xmalloc(sizeof(VALUE));
+ v->type = integer;
+ v->u.i = i;
+ return v;
+}
+
+/* Return a VALUE for S. */
+
+static VALUE *str_value(const char *s)
+{
+ VALUE *v;
+
+ v = xmalloc(sizeof(VALUE));
+ v->type = string;
+ v->u.s = xstrdup(s);
+ return v;
+}
+
+/* Free VALUE V, including structure components. */
+
+static void freev(VALUE * v)
+{
+ if (v->type == string)
+ free(v->u.s);
+ free(v);
+}
+
+/* Return nonzero if V is a null-string or zero-number. */
+
+static int null(VALUE * v)
+{
+ if (v->type == integer)
+ return v->u.i == 0;
+ /* string: */
+ return v->u.s[0] == '\0' || LONE_CHAR(v->u.s, '0');
+}
+
+/* Coerce V to a string value (can't fail). */
+
+static void tostring(VALUE * v)
+{
+ if (v->type == integer) {
+ v->u.s = xasprintf("%" PF_REZ "d", PF_REZ_TYPE v->u.i);
+ v->type = string;
+ }
+}
+
+/* Coerce V to an integer value. Return 1 on success, 0 on failure. */
+
+static int toarith(VALUE * v)
+{
+ if (v->type == string) {
+ arith_t i;
+ char *e;
+
+ /* Don't interpret the empty string as an integer. */
+ /* Currently does not worry about overflow or int/long differences. */
+ i = STRTOL(v->u.s, &e, 10);
+ if ((v->u.s == e) || *e)
+ return 0;
+ free(v->u.s);
+ v->u.i = i;
+ v->type = integer;
+ }
+ return 1;
+}
+
+/* Return nonzero if the next token matches STR exactly.
+ STR must not be NULL. */
+
+static int nextarg(const char *str)
+{
+ if (*args == NULL)
+ return 0;
+ return strcmp(*args, str) == 0;
+}
+
+/* The comparison operator handling functions. */
+
+static int cmp_common(VALUE * l, VALUE * r, int op)
+{
+ int cmpval;
+
+ if (l->type == string || r->type == string) {
+ tostring(l);
+ tostring(r);
+ cmpval = strcmp(l->u.s, r->u.s);
+ } else
+ cmpval = l->u.i - r->u.i;
+ if (op == '<')
+ return cmpval < 0;
+ if (op == ('L' + 'E'))
+ return cmpval <= 0;
+ if (op == '=')
+ return cmpval == 0;
+ if (op == '!')
+ return cmpval != 0;
+ if (op == '>')
+ return cmpval > 0;
+ /* >= */
+ return cmpval >= 0;
+}
+
+/* The arithmetic operator handling functions. */
+
+static arith_t arithmetic_common(VALUE * l, VALUE * r, int op)
+{
+ arith_t li, ri;
+
+ if (!toarith(l) || !toarith(r))
+ bb_error_msg_and_die("non-numeric argument");
+ li = l->u.i;
+ ri = r->u.i;
+ if ((op == '/' || op == '%') && ri == 0)
+ bb_error_msg_and_die("division by zero");
+ if (op == '+')
+ return li + ri;
+ else if (op == '-')
+ return li - ri;
+ else if (op == '*')
+ return li * ri;
+ else if (op == '/')
+ return li / ri;
+ else
+ return li % ri;
+}
+
+/* Do the : operator.
+ SV is the VALUE for the lhs (the string),
+ PV is the VALUE for the rhs (the pattern). */
+
+static VALUE *docolon(VALUE * sv, VALUE * pv)
+{
+ VALUE *v;
+ regex_t re_buffer;
+ const int NMATCH = 2;
+ regmatch_t re_regs[NMATCH];
+
+ tostring(sv);
+ tostring(pv);
+
+ if (pv->u.s[0] == '^') {
+ fprintf(stderr, "\
+warning: unportable BRE: `%s': using `^' as the first character\n\
+of a basic regular expression is not portable; it is being ignored", pv->u.s);
+ }
+
+ memset(&re_buffer, 0, sizeof(re_buffer));
+ memset(re_regs, 0, sizeof(*re_regs));
+ if (regcomp(&re_buffer, pv->u.s, 0) != 0)
+ bb_error_msg_and_die("invalid regular expression");
+
+ /* expr uses an anchored pattern match, so check that there was a
+ * match and that the match starts at offset 0. */
+ if (regexec(&re_buffer, sv->u.s, NMATCH, re_regs, 0) != REG_NOMATCH &&
+ re_regs[0].rm_so == 0) {
+ /* Were \(...\) used? */
+ if (re_buffer.re_nsub > 0) {
+ sv->u.s[re_regs[1].rm_eo] = '\0';
+ v = str_value(sv->u.s + re_regs[1].rm_so);
+ } else
+ v = int_value(re_regs[0].rm_eo);
+ } else {
+ /* Match failed -- return the right kind of null. */
+ if (re_buffer.re_nsub > 0)
+ v = str_value("");
+ else
+ v = int_value(0);
+ }
+ return v;
+}
+
+/* Handle bare operands and ( expr ) syntax. */
+
+static VALUE *eval7(void)
+{
+ VALUE *v;
+
+ if (!*args)
+ bb_error_msg_and_die("syntax error");
+
+ if (nextarg("(")) {
+ args++;
+ v = eval();
+ if (!nextarg(")"))
+ bb_error_msg_and_die("syntax error");
+ args++;
+ return v;
+ }
+
+ if (nextarg(")"))
+ bb_error_msg_and_die("syntax error");
+
+ return str_value(*args++);
+}
+
+/* Handle match, substr, index, length, and quote keywords. */
+
+static VALUE *eval6(void)
+{
+ VALUE *l, *r, *v, *i1, *i2;
+
+ if (nextarg("quote")) {
+ args++;
+ if (!*args)
+ bb_error_msg_and_die("syntax error");
+ return str_value(*args++);
+ } else if (nextarg("length")) {
+ args++;
+ r = eval6();
+ tostring(r);
+ v = int_value(strlen(r->u.s));
+ freev(r);
+ return v;
+ } else if (nextarg("match")) {
+ args++;
+ l = eval6();
+ r = eval6();
+ v = docolon(l, r);
+ freev(l);
+ freev(r);
+ return v;
+ } else if (nextarg("index")) {
+ args++;
+ l = eval6();
+ r = eval6();
+ tostring(l);
+ tostring(r);
+ v = int_value(strcspn(l->u.s, r->u.s) + 1);
+ if (v->u.i == (arith_t) strlen(l->u.s) + 1)
+ v->u.i = 0;
+ freev(l);
+ freev(r);
+ return v;
+ } else if (nextarg("substr")) {
+ args++;
+ l = eval6();
+ i1 = eval6();
+ i2 = eval6();
+ tostring(l);
+ if (!toarith(i1) || !toarith(i2)
+ || i1->u.i > (arith_t) strlen(l->u.s)
+ || i1->u.i <= 0 || i2->u.i <= 0)
+ v = str_value("");
+ else {
+ v = xmalloc(sizeof(VALUE));
+ v->type = string;
+ v->u.s = xstrndup(l->u.s + i1->u.i - 1, i2->u.i);
+ }
+ freev(l);
+ freev(i1);
+ freev(i2);
+ return v;
+ } else
+ return eval7();
+}
+
+/* Handle : operator (pattern matching).
+ Calls docolon to do the real work. */
+
+static VALUE *eval5(void)
+{
+ VALUE *l, *r, *v;
+
+ l = eval6();
+ while (nextarg(":")) {
+ args++;
+ r = eval6();
+ v = docolon(l, r);
+ freev(l);
+ freev(r);
+ l = v;
+ }
+ return l;
+}
+
+/* Handle *, /, % operators. */
+
+static VALUE *eval4(void)
+{
+ VALUE *l, *r;
+ int op;
+ arith_t val;
+
+ l = eval5();
+ while (1) {
+ if (nextarg("*"))
+ op = '*';
+ else if (nextarg("/"))
+ op = '/';
+ else if (nextarg("%"))
+ op = '%';
+ else
+ return l;
+ args++;
+ r = eval5();
+ val = arithmetic_common(l, r, op);
+ freev(l);
+ freev(r);
+ l = int_value(val);
+ }
+}
+
+/* Handle +, - operators. */
+
+static VALUE *eval3(void)
+{
+ VALUE *l, *r;
+ int op;
+ arith_t val;
+
+ l = eval4();
+ while (1) {
+ if (nextarg("+"))
+ op = '+';
+ else if (nextarg("-"))
+ op = '-';
+ else
+ return l;
+ args++;
+ r = eval4();
+ val = arithmetic_common(l, r, op);
+ freev(l);
+ freev(r);
+ l = int_value(val);
+ }
+}
+
+/* Handle comparisons. */
+
+static VALUE *eval2(void)
+{
+ VALUE *l, *r;
+ int op;
+ arith_t val;
+
+ l = eval3();
+ while (1) {
+ if (nextarg("<"))
+ op = '<';
+ else if (nextarg("<="))
+ op = 'L' + 'E';
+ else if (nextarg("=") || nextarg("=="))
+ op = '=';
+ else if (nextarg("!="))
+ op = '!';
+ else if (nextarg(">="))
+ op = 'G' + 'E';
+ else if (nextarg(">"))
+ op = '>';
+ else
+ return l;
+ args++;
+ r = eval3();
+ toarith(l);
+ toarith(r);
+ val = cmp_common(l, r, op);
+ freev(l);
+ freev(r);
+ l = int_value(val);
+ }
+}
+
+/* Handle &. */
+
+static VALUE *eval1(void)
+{
+ VALUE *l, *r;
+
+ l = eval2();
+ while (nextarg("&")) {
+ args++;
+ r = eval2();
+ if (null(l) || null(r)) {
+ freev(l);
+ freev(r);
+ l = int_value(0);
+ } else
+ freev(r);
+ }
+ return l;
+}
+
+/* Handle |. */
+
+static VALUE *eval(void)
+{
+ VALUE *l, *r;
+
+ l = eval1();
+ while (nextarg("|")) {
+ args++;
+ r = eval1();
+ if (null(l)) {
+ freev(l);
+ l = r;
+ } else
+ freev(r);
+ }
+ return l;
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/false.c b/i/pc104/initrd/conf/busybox/coreutils/false.c
new file mode 100644
index 0000000..2a26e0e
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/false.c
@@ -0,0 +1,20 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini false implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/* BB_AUDIT SUSv3 compliant */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/false.html */
+
+#include <stdlib.h>
+#include "busybox.h"
+
+int false_main(int ATTRIBUTE_UNUSED argc, char ATTRIBUTE_UNUSED **argv);
+int false_main(int ATTRIBUTE_UNUSED argc, char ATTRIBUTE_UNUSED **argv)
+{
+ return EXIT_FAILURE;
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/fold.c b/i/pc104/initrd/conf/busybox/coreutils/fold.c
new file mode 100644
index 0000000..4ce52cf
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/fold.c
@@ -0,0 +1,155 @@
+/* vi: set sw=4 ts=4: */
+/* fold -- wrap each input line to fit in specified width.
+
+ Written by David MacKenzie, djm@gnu.ai.mit.edu.
+ Copyright (C) 91, 1995-2002 Free Software Foundation, Inc.
+
+ Modified for busybox based on coreutils v 5.0
+ Copyright (C) 2003 Glenn McGrath <bug1@iinet.net.au>
+
+ Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+*/
+
+#include "busybox.h"
+
+static unsigned long flags;
+#define FLAG_COUNT_BYTES 1
+#define FLAG_BREAK_SPACES 2
+#define FLAG_WIDTH 4
+
+/* Assuming the current column is COLUMN, return the column that
+ printing C will move the cursor to.
+ The first column is 0. */
+
+static int adjust_column(int column, char c)
+{
+ if (!(flags & FLAG_COUNT_BYTES)) {
+ if (c == '\b') {
+ if (column > 0)
+ column--;
+ } else if (c == '\r')
+ column = 0;
+ else if (c == '\t')
+ column = column + 8 - column % 8;
+ else /* if (isprint (c)) */
+ column++;
+ } else
+ column++;
+ return column;
+}
+
+int fold_main(int argc, char **argv);
+int fold_main(int argc, char **argv)
+{
+ char *w_opt;
+ int width = 80;
+ int i;
+ int errs = 0;
+
+ if (ENABLE_INCLUDE_SUSv2) {
+ /* Turn any numeric options into -w options. */
+ for (i = 1; i < argc; i++) {
+ char const *a = argv[i];
+
+ if (*a++ == '-') {
+ if (*a == '-' && !a[1])
+ break;
+ if (isdigit(*a)) {
+ argv[i] = xasprintf("-w%s", a);
+ }
+ }
+ }
+ }
+
+ flags = getopt32(argc, argv, "bsw:", &w_opt);
+ if (flags & FLAG_WIDTH)
+ width = xatoul_range(w_opt, 1, 10000);
+
+ argv += optind;
+ if (!*argv) {
+ *--argv = (char*)"-";
+ }
+
+ do {
+ FILE *istream = fopen_or_warn_stdin(*argv);
+ int c;
+ int column = 0; /* Screen column where next char will go. */
+ int offset_out = 0; /* Index in `line_out' for next char. */
+ static char *line_out = NULL;
+ static int allocated_out = 0;
+
+ if (istream == NULL) {
+ errs |= EXIT_FAILURE;
+ continue;
+ }
+
+ while ((c = getc(istream)) != EOF) {
+ if (offset_out + 1 >= allocated_out) {
+ allocated_out += 1024;
+ line_out = xrealloc(line_out, allocated_out);
+ }
+
+ if (c == '\n') {
+ line_out[offset_out++] = c;
+ fwrite(line_out, sizeof(char), (size_t) offset_out, stdout);
+ column = offset_out = 0;
+ continue;
+ }
+
+rescan:
+ column = adjust_column(column, c);
+
+ if (column > width) {
+ /* This character would make the line too long.
+ Print the line plus a newline, and make this character
+ start the next line. */
+ if (flags & FLAG_BREAK_SPACES) {
+ /* Look for the last blank. */
+ int logical_end;
+
+ for (logical_end = offset_out - 1; logical_end >= 0; logical_end--) {
+ if (isblank(line_out[logical_end])) {
+ break;
+ }
+ }
+ if (logical_end >= 0) {
+ /* Found a blank. Don't output the part after it. */
+ logical_end++;
+ fwrite(line_out, sizeof(char), (size_t) logical_end, stdout);
+ putchar('\n');
+ /* Move the remainder to the beginning of the next line.
+ The areas being copied here might overlap. */
+ memmove(line_out, line_out + logical_end, offset_out - logical_end);
+ offset_out -= logical_end;
+ for (column = i = 0; i < offset_out; i++) {
+ column = adjust_column(column, line_out[i]);
+ }
+ goto rescan;
+ }
+ } else {
+ if (offset_out == 0) {
+ line_out[offset_out++] = c;
+ continue;
+ }
+ }
+ line_out[offset_out++] = '\n';
+ fwrite(line_out, sizeof(char), (size_t) offset_out, stdout);
+ column = offset_out = 0;
+ goto rescan;
+ }
+
+ line_out[offset_out++] = c;
+ }
+
+ if (offset_out) {
+ fwrite(line_out, sizeof(char), (size_t) offset_out, stdout);
+ }
+
+ if (ferror(istream) || fclose_if_not_stdin(istream)) {
+ bb_perror_msg("%s", *argv); /* Avoid multibyte problems. */
+ errs |= EXIT_FAILURE;
+ }
+ } while (*++argv);
+
+ fflush_stdout_and_exit(errs);
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/head.c b/i/pc104/initrd/conf/busybox/coreutils/head.c
new file mode 100644
index 0000000..ed1d01d
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/head.c
@@ -0,0 +1,140 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * head implementation for busybox
+ *
+ * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/* BB_AUDIT SUSv3 compliant */
+/* BB_AUDIT GNU compatible -c, -q, and -v options in 'fancy' configuration. */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/head.html */
+
+#include "busybox.h"
+
+static const char head_opts[] =
+ "n:"
+#if ENABLE_FEATURE_FANCY_HEAD
+ "c:qv"
+#endif
+ ;
+
+#if ENABLE_FEATURE_FANCY_HEAD
+static const struct suffix_mult head_suffixes[] = {
+ { "b", 512 },
+ { "k", 1024 },
+ { "m", 1024*1024 },
+ { NULL, 0 }
+};
+#endif
+
+static const char header_fmt_str[] = "\n==> %s <==\n";
+
+int head_main(int argc, char **argv);
+int head_main(int argc, char **argv)
+{
+ unsigned long count = 10;
+ unsigned long i;
+#if ENABLE_FEATURE_FANCY_HEAD
+ int count_bytes = 0;
+ int header_threshhold = 1;
+#endif
+
+ FILE *fp;
+ const char *fmt;
+ char *p;
+ int opt;
+ int c;
+ int retval = EXIT_SUCCESS;
+
+#if ENABLE_INCLUDE_SUSv2 || ENABLE_FEATURE_FANCY_HEAD
+ /* Allow legacy syntax of an initial numeric option without -n. */
+ if (argc > 1 && argv[1][0] == '-'
+ && isdigit(argv[1][1])
+ ) {
+ --argc;
+ ++argv;
+ p = (*argv) + 1;
+ goto GET_COUNT;
+ }
+#endif
+
+ /* No size benefit in converting this to getopt32 */
+ while ((opt = getopt(argc, argv, head_opts)) > 0) {
+ switch (opt) {
+#if ENABLE_FEATURE_FANCY_HEAD
+ case 'q':
+ header_threshhold = INT_MAX;
+ break;
+ case 'v':
+ header_threshhold = -1;
+ break;
+ case 'c':
+ count_bytes = 1;
+ /* fall through */
+#endif
+ case 'n':
+ p = optarg;
+#if ENABLE_INCLUDE_SUSv2 || ENABLE_FEATURE_FANCY_HEAD
+ GET_COUNT:
+#endif
+
+#if !ENABLE_FEATURE_FANCY_HEAD
+ count = xatoul(p);
+#else
+ count = xatoul_sfx(p, head_suffixes);
+#endif
+ break;
+ default:
+ bb_show_usage();
+ }
+ }
+
+ argv += optind;
+ if (!*argv) {
+ *--argv = (char*)"-";
+ }
+
+ fmt = header_fmt_str + 1;
+#if ENABLE_FEATURE_FANCY_HEAD
+ if (argc - optind <= header_threshhold) {
+ header_threshhold = 0;
+ }
+#else
+ if (argc <= optind + 1) {
+ fmt += 11;
+ }
+ /* Now define some things here to avoid #ifdefs in the code below.
+ * These should optimize out of the if conditions below. */
+#define header_threshhold 1
+#define count_bytes 0
+#endif
+
+ do {
+ fp = fopen_or_warn_stdin(*argv);
+ if (fp) {
+ if (fp == stdin) {
+ *argv = (char *) bb_msg_standard_input;
+ }
+ if (header_threshhold) {
+ printf(fmt, *argv);
+ }
+ i = count;
+ while (i && ((c = getc(fp)) != EOF)) {
+ if (count_bytes || (c == '\n')) {
+ --i;
+ }
+ putchar(c);
+ }
+ if (fclose_if_not_stdin(fp)) {
+ bb_perror_msg("%s", *argv); /* Avoid multibyte problems. */
+ retval = EXIT_FAILURE;
+ }
+ die_if_ferror_stdout();
+ }
+ fmt = header_fmt_str;
+ } while (*++argv);
+
+ fflush_stdout_and_exit(retval);
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/hostid.c b/i/pc104/initrd/conf/busybox/coreutils/hostid.c
new file mode 100644
index 0000000..51a76c6
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/hostid.c
@@ -0,0 +1,26 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini hostid implementation for busybox
+ *
+ * Copyright (C) 2000 Edward Betts <edward@debian.org>.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/* BB_AUDIT SUSv3 N/A -- Matches GNU behavior. */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include "busybox.h"
+
+int hostid_main(int argc, char ATTRIBUTE_UNUSED **argv);
+int hostid_main(int argc, char ATTRIBUTE_UNUSED **argv)
+{
+ if (argc > 1) {
+ bb_show_usage();
+ }
+
+ printf("%lx\n", gethostid());
+
+ fflush_stdout_and_exit(EXIT_SUCCESS);
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/id.c b/i/pc104/initrd/conf/busybox/coreutils/id.c
new file mode 100644
index 0000000..8a60419
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/id.c
@@ -0,0 +1,130 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini id implementation for busybox
+ *
+ * Copyright (C) 2000 by Randolph Chung <tausq@debian.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/* BB_AUDIT SUSv3 _NOT_ compliant -- option -G is not currently supported. */
+/* Hacked by Tito Ragusa (C) 2004 to handle usernames of whatever length and to
+ * be more similar to GNU id.
+ * -Z option support: by Yuichi Nakamura <ynakam@hitachisoft.jp>
+ */
+
+#include "busybox.h"
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+#define PRINT_REAL 1
+#define NAME_NOT_NUMBER 2
+#define JUST_USER 4
+#define JUST_GROUP 8
+#if ENABLE_SELINUX
+#define JUST_CONTEXT 16
+#endif
+
+static short printf_full(unsigned int id, const char *arg, const char prefix)
+{
+ const char *fmt = "%cid=%u";
+ short status = EXIT_FAILURE;
+
+ if (arg) {
+ fmt = "%cid=%u(%s)";
+ status = EXIT_SUCCESS;
+ }
+ printf(fmt, prefix, id, arg);
+ return status;
+}
+
+int id_main(int argc, char **argv);
+int id_main(int argc, char **argv)
+{
+ struct passwd *p;
+ uid_t uid;
+ gid_t gid;
+ unsigned long flags;
+ short status;
+#if ENABLE_SELINUX
+ security_context_t scontext;
+#endif
+ /* Don't allow -n -r -nr -ug -rug -nug -rnug */
+ /* Don't allow more than one username */
+ opt_complementary = "?1:?:u--g:g--u:r?ug:n?ug" USE_SELINUX(":u--Z:Z--u:g--Z:Z--g");
+ flags = getopt32(argc, argv, "rnug" USE_SELINUX("Z"));
+
+ /* This values could be overwritten later */
+ uid = geteuid();
+ gid = getegid();
+ if (flags & PRINT_REAL) {
+ uid = getuid();
+ gid = getgid();
+ }
+
+ if (argv[optind]) {
+ p = getpwnam(argv[optind]);
+ /* xuname2uid is needed because it exits on failure */
+ uid = xuname2uid(argv[optind]);
+ gid = p->pw_gid;
+ /* in this case PRINT_REAL is the same */
+ }
+
+ if (flags & (JUST_GROUP | JUST_USER USE_SELINUX(| JUST_CONTEXT))) {
+ /* JUST_GROUP and JUST_USER are mutually exclusive */
+ if (flags & NAME_NOT_NUMBER) {
+ /* bb_getpwuid and bb_getgrgid exit on failure so puts cannot segfault */
+ puts((flags & JUST_USER) ? bb_getpwuid(NULL, uid, -1 ) : bb_getgrgid(NULL, gid, -1 ));
+ } else {
+ if (flags & JUST_USER) {
+ printf("%u\n", uid);
+ }
+ if (flags & JUST_GROUP) {
+ printf("%u\n", gid);
+ }
+ }
+
+#if ENABLE_SELINUX
+ if (flags & JUST_CONTEXT) {
+ selinux_or_die();
+ if (argc - optind == 1) {
+ bb_error_msg_and_die("user name can't be passed with -Z");
+ }
+
+ if (getcon(&scontext)) {
+ bb_error_msg_and_die("can't get process context");
+ }
+ printf("%s\n", scontext);
+ }
+#endif
+ /* exit */
+ fflush_stdout_and_exit(EXIT_SUCCESS);
+ }
+
+ /* Print full info like GNU id */
+ /* bb_getpwuid doesn't exit on failure here */
+ status = printf_full(uid, bb_getpwuid(NULL, uid, 0), 'u');
+ putchar(' ');
+ /* bb_getgrgid doesn't exit on failure here */
+ status |= printf_full(gid, bb_getgrgid(NULL, gid, 0), 'g');
+
+#if ENABLE_SELINUX
+ if (is_selinux_enabled()) {
+ security_context_t mysid;
+ const char *context;
+
+ context = "unknown";
+ getcon(&mysid);
+ if (mysid) {
+ context = alloca(strlen(mysid) + 1);
+ strcpy((char*)context, mysid);
+ freecon(mysid);
+ }
+ printf(" context=%s", context);
+ }
+#endif
+
+ putchar('\n');
+ fflush_stdout_and_exit(status);
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/install.c b/i/pc104/initrd/conf/busybox/coreutils/install.c
new file mode 100644
index 0000000..c80be01
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/install.c
@@ -0,0 +1,202 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Copyright (C) 2003 by Glenn McGrath <bug1@iinet.net.au>
+ * SELinux support: by Yuichi Nakamura <ynakam@hitachisoft.jp>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ *
+ * TODO: -d option, need a way of recursively making directories and changing
+ * owner/group, will probably modify bb_make_directory(...)
+ */
+
+#include "busybox.h"
+#include "libcoreutils/coreutils.h"
+#include <libgen.h>
+#include <getopt.h> /* struct option */
+
+#if ENABLE_FEATURE_INSTALL_LONG_OPTIONS
+static const struct option install_long_options[] = {
+ { "directory", 0, NULL, 'd' },
+ { "preserve-timestamps", 0, NULL, 'p' },
+ { "strip", 0, NULL, 's' },
+ { "group", 0, NULL, 'g' },
+ { "mode", 0, NULL, 'm' },
+ { "owner", 0, NULL, 'o' },
+#if ENABLE_SELINUX
+ { "context", 1, NULL, 'Z' },
+ { "preserve_context", 0, NULL, 0xff },
+ { "preserve-context", 0, NULL, 0xff },
+#endif
+ { 0, 0, 0, 0 }
+};
+#endif
+
+
+#if ENABLE_SELINUX
+static bool use_default_selinux_context = 1;
+
+static void setdefaultfilecon(const char *path) {
+ struct stat s;
+ security_context_t scontext = NULL;
+
+ if (!is_selinux_enabled()) {
+ return;
+ }
+ if (lstat(path, &s) != 0) {
+ return;
+ }
+
+ if (matchpathcon(path, s.st_mode, &scontext) < 0) {
+ goto out;
+ }
+ if (strcmp(scontext, "<<none>>") == 0) {
+ goto out;
+ }
+
+ if (lsetfilecon(path, scontext) < 0) {
+ if (errno != ENOTSUP) {
+ bb_perror_msg("warning: failed to change context of %s to %s", path, scontext);
+ }
+ }
+
+ out:
+ freecon(scontext);
+}
+
+#endif
+
+int install_main(int argc, char **argv);
+int install_main(int argc, char **argv)
+{
+ struct stat statbuf;
+ mode_t mode;
+ uid_t uid;
+ gid_t gid;
+ const char *gid_str;
+ const char *uid_str;
+ const char *mode_str;
+ int copy_flags = FILEUTILS_DEREFERENCE | FILEUTILS_FORCE;
+ int ret = EXIT_SUCCESS, flags, i, isdir;
+#if ENABLE_SELINUX
+ security_context_t scontext;
+#endif
+ enum {
+ OPT_CMD = 0x1,
+ OPT_DIRECTORY = 0x2,
+ OPT_PRESERVE_TIME = 0x4,
+ OPT_STRIP = 0x8,
+ OPT_GROUP = 0x10,
+ OPT_MODE = 0x20,
+ OPT_OWNER = 0x40,
+#if ENABLE_SELINUX
+ OPT_SET_SECURITY_CONTEXT = 0x80,
+ OPT_PRESERVE_SECURITY_CONTEXT = 0x100,
+#endif
+ };
+
+#if ENABLE_FEATURE_INSTALL_LONG_OPTIONS
+ applet_long_options = install_long_options;
+#endif
+ opt_complementary = "?:s--d:d--s" USE_SELINUX(":Z--\xff:\xff--Z");
+ /* -c exists for backwards compatibility, it's needed */
+
+ flags = getopt32(argc, argv, "cdpsg:m:o:" USE_SELINUX("Z:"), &gid_str, &mode_str, &uid_str USE_SELINUX(, &scontext));
+
+#if ENABLE_SELINUX
+ if (flags & OPT_PRESERVE_SECURITY_CONTEXT) {
+ use_default_selinux_context = 0;
+ copy_flags |= FILEUTILS_PRESERVE_SECURITY_CONTEXT;
+ selinux_or_die();
+ }
+ if (flags & OPT_SET_SECURITY_CONTEXT) {
+ selinux_or_die();
+ setfscreatecon_or_die(scontext);
+ use_default_selinux_context = 0;
+ copy_flags |= FILEUTILS_SET_SECURITY_CONTEXT;
+ }
+#endif
+
+ /* preserve access and modification time, this is GNU behaviour, BSD only preserves modification time */
+ if (flags & OPT_PRESERVE_TIME) {
+ copy_flags |= FILEUTILS_PRESERVE_STATUS;
+ }
+ mode = 0666;
+ if (flags & OPT_MODE) bb_parse_mode(mode_str, &mode);
+ uid = (flags & OPT_OWNER) ? get_ug_id(uid_str, xuname2uid) : getuid();
+ gid = (flags & OPT_GROUP) ? get_ug_id(gid_str, xgroup2gid) : getgid();
+ if (flags & (OPT_OWNER|OPT_GROUP)) umask(0);
+
+ /* Create directories
+ * don't use bb_make_directory() as it can't change uid or gid
+ * perhaps bb_make_directory() should be improved.
+ */
+ if (flags & OPT_DIRECTORY) {
+ for (argv += optind; *argv; argv++) {
+ char *old_argv_ptr = *argv + 1;
+ char *argv_ptr;
+ do {
+ argv_ptr = strchr(old_argv_ptr, '/');
+ old_argv_ptr = argv_ptr;
+ if (argv_ptr) {
+ *argv_ptr = '\0';
+ old_argv_ptr++;
+ }
+ if (mkdir(*argv, mode | 0111) == -1) {
+ if (errno != EEXIST) {
+ bb_perror_msg("cannot create %s", *argv);
+ ret = EXIT_FAILURE;
+ break;
+ }
+ }
+ if ((flags & (OPT_OWNER|OPT_GROUP))
+ && lchown(*argv, uid, gid) == -1
+ ) {
+ bb_perror_msg("cannot change ownership of %s", *argv);
+ ret = EXIT_FAILURE;
+ break;
+ }
+ if (argv_ptr) {
+ *argv_ptr = '/';
+ }
+ } while (old_argv_ptr);
+ }
+ return ret;
+ }
+
+ isdir = lstat(argv[argc - 1], &statbuf) < 0 ? 0 : S_ISDIR(statbuf.st_mode);
+
+ for (i = optind; i < argc - 1; i++) {
+ char *dest;
+
+ dest = argv[argc - 1];
+ if (isdir)
+ dest = concat_path_file(argv[argc - 1], basename(argv[i]));
+ ret |= copy_file(argv[i], dest, copy_flags);
+
+ /* Set the file mode */
+ if ((flags & OPT_MODE) && chmod(dest, mode) == -1) {
+ bb_perror_msg("cannot change permissions of %s", dest);
+ ret = EXIT_FAILURE;
+ }
+#if ENABLE_SELINUX
+ if (use_default_selinux_context)
+ setdefaultfilecon(dest);
+#endif
+ /* Set the user and group id */
+ if ((flags & (OPT_OWNER|OPT_GROUP))
+ && lchown(dest, uid, gid) == -1
+ ) {
+ bb_perror_msg("cannot change ownership of %s", dest);
+ ret = EXIT_FAILURE;
+ }
+ if (flags & OPT_STRIP) {
+ if (BB_EXECLP("strip", "strip", dest, NULL) == -1) {
+ bb_perror_msg("strip");
+ ret = EXIT_FAILURE;
+ }
+ }
+ if (ENABLE_FEATURE_CLEAN_UP && isdir) free(dest);
+ }
+
+ return ret;
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/length.c b/i/pc104/initrd/conf/busybox/coreutils/length.c
new file mode 100644
index 0000000..1dc122c
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/length.c
@@ -0,0 +1,20 @@
+/* vi: set sw=4 ts=4: */
+
+/* BB_AUDIT SUSv3 N/A -- Apparently a busybox (obsolete?) extension. */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include "busybox.h"
+
+int length_main(int argc, char **argv);
+int length_main(int argc, char **argv)
+{
+ if ((argc != 2) || (**(++argv) == '-')) {
+ bb_show_usage();
+ }
+
+ printf("%lu\n", (unsigned long)strlen(*argv));
+
+ fflush_stdout_and_exit(EXIT_SUCCESS);
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/libcoreutils/Kbuild b/i/pc104/initrd/conf/busybox/coreutils/libcoreutils/Kbuild
new file mode 100644
index 0000000..755d01f
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/libcoreutils/Kbuild
@@ -0,0 +1,12 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+
+lib-y:=
+lib-$(CONFIG_MKFIFO) += getopt_mk_fifo_nod.o
+lib-$(CONFIG_MKNOD) += getopt_mk_fifo_nod.o
+lib-$(CONFIG_INSTALL) += cp_mv_stat.o
+lib-$(CONFIG_CP) += cp_mv_stat.o
+lib-$(CONFIG_MV) += cp_mv_stat.o
diff --git a/i/pc104/initrd/conf/busybox/coreutils/libcoreutils/coreutils.h b/i/pc104/initrd/conf/busybox/coreutils/libcoreutils/coreutils.h
new file mode 100644
index 0000000..102c7b5
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/libcoreutils/coreutils.h
@@ -0,0 +1,16 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ */
+
+#ifndef COREUTILS_H
+#define COREUTILS_H 1
+
+typedef int (*stat_func)(const char *fn, struct stat *ps);
+
+extern int cp_mv_stat2(const char *fn, struct stat *fn_stat, stat_func sf);
+extern int cp_mv_stat(const char *fn, struct stat *fn_stat);
+
+extern mode_t getopt_mk_fifo_nod(int argc, char **argv);
+
+#endif
diff --git a/i/pc104/initrd/conf/busybox/coreutils/libcoreutils/cp_mv_stat.c b/i/pc104/initrd/conf/busybox/coreutils/libcoreutils/cp_mv_stat.c
new file mode 100644
index 0000000..2e4f25e
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/libcoreutils/cp_mv_stat.c
@@ -0,0 +1,43 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * coreutils utility routine
+ *
+ * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include "libbb.h"
+#include "coreutils.h"
+
+int cp_mv_stat2(const char *fn, struct stat *fn_stat, stat_func sf)
+{
+ if (sf(fn, fn_stat) < 0) {
+ if (errno != ENOENT) {
+ bb_perror_msg("cannot stat '%s'", fn);
+ return -1;
+ }
+ return 0;
+ } else if (S_ISDIR(fn_stat->st_mode)) {
+ return 3;
+ }
+ return 1;
+}
+
+int cp_mv_stat(const char *fn, struct stat *fn_stat)
+{
+ return cp_mv_stat2(fn, fn_stat, stat);
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/libcoreutils/getopt_mk_fifo_nod.c b/i/pc104/initrd/conf/busybox/coreutils/libcoreutils/getopt_mk_fifo_nod.c
new file mode 100644
index 0000000..32fa9be
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/libcoreutils/getopt_mk_fifo_nod.c
@@ -0,0 +1,51 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * coreutils utility routine
+ *
+ * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include "libbb.h"
+#include "coreutils.h"
+
+mode_t getopt_mk_fifo_nod(int argc, char **argv)
+{
+ mode_t mode = 0666;
+ char *smode = NULL;
+#if ENABLE_SELINUX
+ security_context_t scontext;
+#endif
+ int opt;
+ opt = getopt32(argc, argv, "m:" USE_SELINUX("Z:"), &smode USE_SELINUX(,&scontext));
+ if (opt & 1) {
+ if (bb_parse_mode(smode, &mode))
+ umask(0);
+ }
+
+#if ENABLE_SELINUX
+ if (opt & 2) {
+ selinux_or_die();
+ setfscreatecon_or_die(scontext);
+ }
+#endif
+
+ return mode;
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/ln.c b/i/pc104/initrd/conf/busybox/coreutils/ln.c
new file mode 100644
index 0000000..7207134
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/ln.c
@@ -0,0 +1,105 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini ln implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/* BB_AUDIT SUSv3 compliant */
+/* BB_AUDIT GNU options missing: -d, -F, -i, and -v. */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/ln.html */
+
+#include "busybox.h"
+
+#define LN_SYMLINK 1
+#define LN_FORCE 2
+#define LN_NODEREFERENCE 4
+#define LN_BACKUP 8
+#define LN_SUFFIX 16
+
+int ln_main(int argc, char **argv);
+int ln_main(int argc, char **argv)
+{
+ int status = EXIT_SUCCESS;
+ int flag;
+ char *last;
+ char *src_name;
+ char *src;
+ char *suffix = (char*)"~";
+ struct stat statbuf;
+ int (*link_func)(const char *, const char *);
+
+ flag = getopt32(argc, argv, "sfnbS:", &suffix);
+
+ if (argc == optind) {
+ bb_show_usage();
+ }
+
+ last = argv[argc - 1];
+ argv += optind;
+
+ if (argc == optind + 1) {
+ *--argv = last;
+ last = bb_get_last_path_component(xstrdup(last));
+ }
+
+ do {
+ src_name = NULL;
+ src = last;
+
+ if (is_directory(src,
+ (flag & LN_NODEREFERENCE) ^ LN_NODEREFERENCE,
+ NULL)) {
+ src_name = xstrdup(*argv);
+ src = concat_path_file(src, bb_get_last_path_component(src_name));
+ free(src_name);
+ src_name = src;
+ }
+ if (!(flag & LN_SYMLINK) && stat(*argv, &statbuf)) {
+ // coreutils: "ln dangling_symlink new_hardlink" works
+ if (lstat(*argv, &statbuf) || !S_ISLNK(statbuf.st_mode)) {
+ bb_perror_msg("%s", *argv);
+ status = EXIT_FAILURE;
+ free(src_name);
+ continue;
+ }
+ }
+
+ if (flag & LN_BACKUP) {
+ char *backup;
+ backup = xasprintf("%s%s", src, suffix);
+ if (rename(src, backup) < 0 && errno != ENOENT) {
+ bb_perror_msg("%s", src);
+ status = EXIT_FAILURE;
+ free(backup);
+ continue;
+ }
+ free(backup);
+ /*
+ * When the source and dest are both hard links to the same
+ * inode, a rename may succeed even though nothing happened.
+ * Therefore, always unlink().
+ */
+ unlink(src);
+ } else if (flag & LN_FORCE) {
+ unlink(src);
+ }
+
+ link_func = link;
+ if (flag & LN_SYMLINK) {
+ link_func = symlink;
+ }
+
+ if (link_func(*argv, src) != 0) {
+ bb_perror_msg("%s", src);
+ status = EXIT_FAILURE;
+ }
+
+ free(src_name);
+
+ } while ((++argv)[1]);
+
+ return status;
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/logname.c b/i/pc104/initrd/conf/busybox/coreutils/logname.c
new file mode 100644
index 0000000..743e229
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/logname.c
@@ -0,0 +1,43 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini logname implementation for busybox
+ *
+ * Copyright (C) 2000 Edward Betts <edward@debian.org>.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/* BB_AUDIT SUSv3 compliant */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/logname.html */
+
+/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
+ *
+ * SUSv3 specifies the string used is that returned from getlogin().
+ * The previous implementation used getpwuid() for geteuid(), which
+ * is _not_ the same. Erik apparently made this change almost 3 years
+ * ago to avoid failing when no utmp was available. However, the
+ * correct course of action wrt SUSv3 for a failing getlogin() is
+ * a diagnostic message and an error return.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "busybox.h"
+
+int logname_main(int argc, char ATTRIBUTE_UNUSED **argv);
+int logname_main(int argc, char ATTRIBUTE_UNUSED **argv)
+{
+ const char *p;
+
+ if (argc > 1) {
+ bb_show_usage();
+ }
+
+ if ((p = getlogin()) != NULL) {
+ puts(p);
+ fflush_stdout_and_exit(EXIT_SUCCESS);
+ }
+
+ bb_perror_msg_and_die("getlogin");
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/ls.c b/i/pc104/initrd/conf/busybox/coreutils/ls.c
new file mode 100644
index 0000000..34836ee
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/ls.c
@@ -0,0 +1,957 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * tiny-ls.c version 0.1.0: A minimalist 'ls'
+ * Copyright (C) 1996 Brian Candler <B.Candler@pobox.com>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/*
+ * To achieve a small memory footprint, this version of 'ls' doesn't do any
+ * file sorting, and only has the most essential command line switches
+ * (i.e., the ones I couldn't live without :-) All features which involve
+ * linking in substantial chunks of libc can be disabled.
+ *
+ * Although I don't really want to add new features to this program to
+ * keep it small, I *am* interested to receive bug fixes and ways to make
+ * it more portable.
+ *
+ * KNOWN BUGS:
+ * 1. ls -l of a directory doesn't give "total <blocks>" header
+ * 2. ls of a symlink to a directory doesn't list directory contents
+ * 3. hidden files can make column width too large
+ *
+ * NON-OPTIMAL BEHAVIOUR:
+ * 1. autowidth reads directories twice
+ * 2. if you do a short directory listing without filetype characters
+ * appended, there's no need to stat each one
+ * PORTABILITY:
+ * 1. requires lstat (BSD) - how do you do it without?
+ */
+
+#include "busybox.h"
+#include <getopt.h>
+
+enum {
+
+TERMINAL_WIDTH = 80, /* use 79 if terminal has linefold bug */
+COLUMN_GAP = 2, /* includes the file type char */
+
+/* what is the overall style of the listing */
+STYLE_COLUMNS = 1 << 21, /* fill columns */
+STYLE_LONG = 2 << 21, /* one record per line, extended info */
+STYLE_SINGLE = 3 << 21, /* one record per line */
+STYLE_MASK = STYLE_SINGLE,
+
+/* 51306 lrwxrwxrwx 1 root root 2 May 11 01:43 /bin/view -> vi* */
+/* what file information will be listed */
+LIST_INO = 1 << 0,
+LIST_BLOCKS = 1 << 1,
+LIST_MODEBITS = 1 << 2,
+LIST_NLINKS = 1 << 3,
+LIST_ID_NAME = 1 << 4,
+LIST_ID_NUMERIC = 1 << 5,
+LIST_CONTEXT = 1 << 6,
+LIST_SIZE = 1 << 7,
+LIST_DEV = 1 << 8,
+LIST_DATE_TIME = 1 << 9,
+LIST_FULLTIME = 1 << 10,
+LIST_FILENAME = 1 << 11,
+LIST_SYMLINK = 1 << 12,
+LIST_FILETYPE = 1 << 13,
+LIST_EXEC = 1 << 14,
+LIST_MASK = (LIST_EXEC << 1) - 1,
+
+/* what files will be displayed */
+DISP_DIRNAME = 1 << 15, /* 2 or more items? label directories */
+DISP_HIDDEN = 1 << 16, /* show filenames starting with . */
+DISP_DOT = 1 << 17, /* show . and .. */
+DISP_NOLIST = 1 << 18, /* show directory as itself, not contents */
+DISP_RECURSIVE = 1 << 19, /* show directory and everything below it */
+DISP_ROWS = 1 << 20, /* print across rows */
+DISP_MASK = ((DISP_ROWS << 1) - 1) & ~(DISP_DIRNAME - 1),
+
+/* how will the files be sorted (CONFIG_FEATURE_LS_SORTFILES) */
+SORT_FORWARD = 0, /* sort in reverse order */
+SORT_REVERSE = 1 << 27, /* sort in reverse order */
+
+SORT_NAME = 0, /* sort by file name */
+SORT_SIZE = 1 << 28, /* sort by file size */
+SORT_ATIME = 2 << 28, /* sort by last access time */
+SORT_CTIME = 3 << 28, /* sort by last change time */
+SORT_MTIME = 4 << 28, /* sort by last modification time */
+SORT_VERSION = 5 << 28, /* sort by version */
+SORT_EXT = 6 << 28, /* sort by file name extension */
+SORT_DIR = 7 << 28, /* sort by file or directory */
+SORT_MASK = (7 << 28) * ENABLE_FEATURE_LS_SORTFILES,
+
+/* which of the three times will be used */
+TIME_CHANGE = (1 << 23) * ENABLE_FEATURE_LS_TIMESTAMPS,
+TIME_ACCESS = (1 << 24) * ENABLE_FEATURE_LS_TIMESTAMPS,
+TIME_MASK = (3 << 23) * ENABLE_FEATURE_LS_TIMESTAMPS,
+
+FOLLOW_LINKS = (1 << 25) * ENABLE_FEATURE_LS_FOLLOWLINKS,
+
+LS_DISP_HR = (1 << 26) * ENABLE_FEATURE_HUMAN_READABLE,
+
+LIST_SHORT = LIST_FILENAME,
+LIST_LONG = LIST_MODEBITS | LIST_NLINKS | LIST_ID_NAME | LIST_SIZE | \
+ LIST_DATE_TIME | LIST_FILENAME | LIST_SYMLINK,
+
+SPLIT_DIR = 1,
+SPLIT_FILE = 0,
+SPLIT_SUBDIR = 2,
+
+};
+
+#define TYPEINDEX(mode) (((mode) >> 12) & 0x0f)
+#define TYPECHAR(mode) ("0pcCd?bB-?l?s???" [TYPEINDEX(mode)])
+#define APPCHAR(mode) ("\0|\0\0/\0\0\0\0\0@\0=\0\0\0" [TYPEINDEX(mode)])
+#define COLOR(mode) ("\000\043\043\043\042\000\043\043"\
+ "\000\000\044\000\043\000\000\040" [TYPEINDEX(mode)])
+#define ATTR(mode) ("\00\00\01\00\01\00\01\00"\
+ "\00\00\01\00\01\00\00\01" [TYPEINDEX(mode)])
+
+/* colored LS support by JaWi, janwillem.janssen@lxtreme.nl */
+#if ENABLE_FEATURE_LS_COLOR
+static int show_color;
+/* long option entry used only for --color, which has no short option
+ * equivalent */
+static const struct option ls_color_opt[] = {
+ { "color", optional_argument, NULL, 1 },
+ { NULL, 0, NULL, 0 }
+};
+#else
+enum { show_color = 0 };
+#endif
+
+/*
+ * a directory entry and its stat info are stored here
+ */
+struct dnode { /* the basic node */
+ char *name; /* the dir entry name */
+ char *fullname; /* the dir entry name */
+ int allocated;
+ struct stat dstat; /* the file stat info */
+ USE_SELINUX(security_context_t sid;)
+ struct dnode *next; /* point at the next node */
+};
+typedef struct dnode dnode_t;
+
+static struct dnode **list_dir(const char *);
+static struct dnode **dnalloc(int);
+static int list_single(struct dnode *);
+
+static unsigned all_fmt;
+
+#if ENABLE_FEATURE_AUTOWIDTH
+static unsigned tabstops = COLUMN_GAP;
+static unsigned terminal_width = TERMINAL_WIDTH;
+#else
+enum {
+ tabstops = COLUMN_GAP,
+ terminal_width = TERMINAL_WIDTH,
+};
+#endif
+
+static int status = EXIT_SUCCESS;
+
+static struct dnode *my_stat(char *fullname, char *name, int force_follow)
+{
+ struct stat dstat;
+ struct dnode *cur;
+ USE_SELINUX(security_context_t sid = NULL;)
+
+ if ((all_fmt & FOLLOW_LINKS) || force_follow) {
+#if ENABLE_SELINUX
+ if (is_selinux_enabled()) {
+ getfilecon(fullname, &sid);
+ }
+#endif
+ if (stat(fullname, &dstat)) {
+ bb_perror_msg("%s", fullname);
+ status = EXIT_FAILURE;
+ return 0;
+ }
+ } else {
+#if ENABLE_SELINUX
+ if (is_selinux_enabled()) {
+ lgetfilecon(fullname, &sid);
+ }
+#endif
+ if (lstat(fullname, &dstat)) {
+ bb_perror_msg("%s", fullname);
+ status = EXIT_FAILURE;
+ return 0;
+ }
+ }
+
+ cur = xmalloc(sizeof(struct dnode));
+ cur->fullname = fullname;
+ cur->name = name;
+ cur->dstat = dstat;
+ USE_SELINUX(cur->sid = sid;)
+ return cur;
+}
+
+#if ENABLE_FEATURE_LS_COLOR
+static char fgcolor(mode_t mode)
+{
+ /* Check wheter the file is existing (if so, color it red!) */
+ if (errno == ENOENT)
+ return '\037';
+ if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
+ return COLOR(0xF000); /* File is executable ... */
+ return COLOR(mode);
+}
+
+static char bgcolor(mode_t mode)
+{
+ if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
+ return ATTR(0xF000); /* File is executable ... */
+ return ATTR(mode);
+}
+#endif
+
+#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
+static char append_char(mode_t mode)
+{
+ if (!(all_fmt & LIST_FILETYPE))
+ return '\0';
+ if (S_ISDIR(mode))
+ return '/';
+ if (!(all_fmt & LIST_EXEC))
+ return '\0';
+ if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
+ return '*';
+ return APPCHAR(mode);
+}
+#endif
+
+#define countdirs(A, B) count_dirs((A), (B), 1)
+#define countsubdirs(A, B) count_dirs((A), (B), 0)
+static int count_dirs(struct dnode **dn, int nfiles, int notsubdirs)
+{
+ int i, dirs;
+
+ if (!dn)
+ return 0;
+ dirs = 0;
+ for (i = 0; i < nfiles; i++) {
+ char *name;
+ if (!S_ISDIR(dn[i]->dstat.st_mode))
+ continue;
+ name = dn[i]->name;
+ if (notsubdirs
+ || name[0]!='.' || (name[1] && (name[1]!='.' || name[2]))
+ ) {
+ dirs++;
+ }
+ }
+ return dirs;
+}
+
+static int countfiles(struct dnode **dnp)
+{
+ int nfiles;
+ struct dnode *cur;
+
+ if (dnp == NULL)
+ return 0;
+ nfiles = 0;
+ for (cur = dnp[0]; cur->next; cur = cur->next)
+ nfiles++;
+ nfiles++;
+ return nfiles;
+}
+
+/* get memory to hold an array of pointers */
+static struct dnode **dnalloc(int num)
+{
+ if (num < 1)
+ return NULL;
+
+ return xzalloc(num * sizeof(struct dnode *));
+}
+
+#if ENABLE_FEATURE_LS_RECURSIVE
+static void dfree(struct dnode **dnp, int nfiles)
+{
+ int i;
+
+ if (dnp == NULL)
+ return;
+
+ for (i = 0; i < nfiles; i++) {
+ struct dnode *cur = dnp[i];
+ if (cur->allocated)
+ free(cur->fullname); /* free the filename */
+ free(cur); /* free the dnode */
+ }
+ free(dnp); /* free the array holding the dnode pointers */
+}
+#else
+#define dfree(...) ((void)0)
+#endif
+
+static struct dnode **splitdnarray(struct dnode **dn, int nfiles, int which)
+{
+ int dncnt, i, d;
+ struct dnode **dnp;
+
+ if (dn == NULL || nfiles < 1)
+ return NULL;
+
+ /* count how many dirs and regular files there are */
+ if (which == SPLIT_SUBDIR)
+ dncnt = countsubdirs(dn, nfiles);
+ else {
+ dncnt = countdirs(dn, nfiles); /* assume we are looking for dirs */
+ if (which == SPLIT_FILE)
+ dncnt = nfiles - dncnt; /* looking for files */
+ }
+
+ /* allocate a file array and a dir array */
+ dnp = dnalloc(dncnt);
+
+ /* copy the entrys into the file or dir array */
+ for (d = i = 0; i < nfiles; i++) {
+ if (S_ISDIR(dn[i]->dstat.st_mode)) {
+ char *name;
+ if (!(which & (SPLIT_DIR|SPLIT_SUBDIR)))
+ continue;
+ name = dn[i]->name;
+ if ((which & SPLIT_DIR)
+ || name[0]!='.' || (name[1] && (name[1]!='.' || name[2]))
+ ) {
+ dnp[d++] = dn[i];
+ }
+ } else if (!(which & (SPLIT_DIR|SPLIT_SUBDIR))) {
+ dnp[d++] = dn[i];
+ }
+ }
+ return dnp;
+}
+
+#if ENABLE_FEATURE_LS_SORTFILES
+static int sortcmp(const void *a, const void *b)
+{
+ struct dnode *d1 = *(struct dnode **)a;
+ struct dnode *d2 = *(struct dnode **)b;
+ unsigned sort_opts = all_fmt & SORT_MASK;
+ int dif;
+
+ dif = 0; /* assume SORT_NAME */
+ // TODO: use pre-initialized function pointer
+ // instead of branch forest
+ if (sort_opts == SORT_SIZE) {
+ dif = (int) (d2->dstat.st_size - d1->dstat.st_size);
+ } else if (sort_opts == SORT_ATIME) {
+ dif = (int) (d2->dstat.st_atime - d1->dstat.st_atime);
+ } else if (sort_opts == SORT_CTIME) {
+ dif = (int) (d2->dstat.st_ctime - d1->dstat.st_ctime);
+ } else if (sort_opts == SORT_MTIME) {
+ dif = (int) (d2->dstat.st_mtime - d1->dstat.st_mtime);
+ } else if (sort_opts == SORT_DIR) {
+ dif = S_ISDIR(d2->dstat.st_mode) - S_ISDIR(d1->dstat.st_mode);
+ /* } else if (sort_opts == SORT_VERSION) { */
+ /* } else if (sort_opts == SORT_EXT) { */
+ }
+
+ if (dif == 0) {
+ /* sort by name - may be a tie_breaker for time or size cmp */
+ if (ENABLE_LOCALE_SUPPORT) dif = strcoll(d1->name, d2->name);
+ else dif = strcmp(d1->name, d2->name);
+ }
+
+ if (all_fmt & SORT_REVERSE) {
+ dif = -dif;
+ }
+ return dif;
+}
+
+static void dnsort(struct dnode **dn, int size)
+{
+ qsort(dn, size, sizeof(*dn), sortcmp);
+}
+#else
+#define dnsort(dn, size) ((void)0)
+#endif
+
+
+static void showfiles(struct dnode **dn, int nfiles)
+{
+ int i, ncols, nrows, row, nc;
+ int column = 0;
+ int nexttab = 0;
+ int column_width = 0; /* for STYLE_LONG and STYLE_SINGLE not used */
+
+ if (dn == NULL || nfiles < 1)
+ return;
+
+ if (all_fmt & STYLE_LONG) {
+ ncols = 1;
+ } else {
+ /* find the longest file name, use that as the column width */
+ for (i = 0; i < nfiles; i++) {
+ int len = strlen(dn[i]->name);
+ if (column_width < len)
+ column_width = len;
+ }
+ column_width += tabstops +
+ USE_SELINUX( ((all_fmt & LIST_CONTEXT) ? 33 : 0) + )
+ ((all_fmt & LIST_INO) ? 8 : 0) +
+ ((all_fmt & LIST_BLOCKS) ? 5 : 0);
+ ncols = (int) (terminal_width / column_width);
+ }
+
+ if (ncols > 1) {
+ nrows = nfiles / ncols;
+ if (nrows * ncols < nfiles)
+ nrows++; /* round up fractionals */
+ } else {
+ nrows = nfiles;
+ ncols = 1;
+ }
+
+ for (row = 0; row < nrows; row++) {
+ for (nc = 0; nc < ncols; nc++) {
+ /* reach into the array based on the column and row */
+ i = (nc * nrows) + row; /* assume display by column */
+ if (all_fmt & DISP_ROWS)
+ i = (row * ncols) + nc; /* display across row */
+ if (i < nfiles) {
+ if (column > 0) {
+ nexttab -= column;
+ printf("%*s", nexttab, "");
+ column += nexttab;
+ }
+ nexttab = column + column_width;
+ column += list_single(dn[i]);
+ }
+ }
+ putchar('\n');
+ column = 0;
+ }
+}
+
+
+static void showdirs(struct dnode **dn, int ndirs, int first)
+{
+ int i, nfiles;
+ struct dnode **subdnp;
+ int dndirs;
+ struct dnode **dnd;
+
+ if (dn == NULL || ndirs < 1)
+ return;
+
+ for (i = 0; i < ndirs; i++) {
+ if (all_fmt & (DISP_DIRNAME | DISP_RECURSIVE)) {
+ if (!first)
+ puts("");
+ first = 0;
+ printf("%s:\n", dn[i]->fullname);
+ }
+ subdnp = list_dir(dn[i]->fullname);
+ nfiles = countfiles(subdnp);
+ if (nfiles > 0) {
+ /* list all files at this level */
+ dnsort(subdnp, nfiles);
+ showfiles(subdnp, nfiles);
+ if (ENABLE_FEATURE_LS_RECURSIVE) {
+ if (all_fmt & DISP_RECURSIVE) {
+ /* recursive- list the sub-dirs */
+ dnd = splitdnarray(subdnp, nfiles, SPLIT_SUBDIR);
+ dndirs = countsubdirs(subdnp, nfiles);
+ if (dndirs > 0) {
+ dnsort(dnd, dndirs);
+ showdirs(dnd, dndirs, 0);
+ /* free the array of dnode pointers to the dirs */
+ free(dnd);
+ }
+ }
+ /* free the dnodes and the fullname mem */
+ dfree(subdnp, nfiles);
+ }
+ }
+ }
+}
+
+
+static struct dnode **list_dir(const char *path)
+{
+ struct dnode *dn, *cur, **dnp;
+ struct dirent *entry;
+ DIR *dir;
+ int i, nfiles;
+
+ if (path == NULL)
+ return NULL;
+
+ dn = NULL;
+ nfiles = 0;
+ dir = warn_opendir(path);
+ if (dir == NULL) {
+ status = EXIT_FAILURE;
+ return NULL; /* could not open the dir */
+ }
+ while ((entry = readdir(dir)) != NULL) {
+ char *fullname;
+
+ /* are we going to list the file- it may be . or .. or a hidden file */
+ if (entry->d_name[0] == '.') {
+ if ((entry->d_name[1] == 0 || (
+ entry->d_name[1] == '.'
+ && entry->d_name[2] == 0))
+ && !(all_fmt & DISP_DOT))
+ continue;
+ if (!(all_fmt & DISP_HIDDEN))
+ continue;
+ }
+ fullname = concat_path_file(path, entry->d_name);
+ cur = my_stat(fullname, strrchr(fullname, '/') + 1, 0);
+ if (!cur) {
+ free(fullname);
+ continue;
+ }
+ cur->allocated = 1;
+ cur->next = dn;
+ dn = cur;
+ nfiles++;
+ }
+ closedir(dir);
+
+ /* now that we know how many files there are
+ * allocate memory for an array to hold dnode pointers
+ */
+ if (dn == NULL)
+ return NULL;
+ dnp = dnalloc(nfiles);
+ for (i = 0, cur = dn; i < nfiles; i++) {
+ dnp[i] = cur; /* save pointer to node in array */
+ cur = cur->next;
+ }
+
+ return dnp;
+}
+
+
+#if ENABLE_FEATURE_LS_TIMESTAMPS
+/* Do time() just once. Saves one syscall per file for "ls -l" */
+/* Initialized in main() */
+static time_t current_time_t;
+#endif
+
+static int list_single(struct dnode *dn)
+{
+ int i, column = 0;
+
+#if ENABLE_FEATURE_LS_TIMESTAMPS
+ char *filetime;
+ time_t ttime, age;
+#endif
+#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
+ struct stat info;
+ char append;
+#endif
+
+ if (dn->fullname == NULL)
+ return 0;
+
+#if ENABLE_FEATURE_LS_TIMESTAMPS
+ ttime = dn->dstat.st_mtime; /* the default time */
+ if (all_fmt & TIME_ACCESS)
+ ttime = dn->dstat.st_atime;
+ if (all_fmt & TIME_CHANGE)
+ ttime = dn->dstat.st_ctime;
+ filetime = ctime(&ttime);
+#endif
+#if ENABLE_FEATURE_LS_FILETYPES
+ append = append_char(dn->dstat.st_mode);
+#endif
+
+ for (i = 0; i <= 31; i++) {
+ switch (all_fmt & (1 << i)) {
+ case LIST_INO:
+ column += printf("%7ld ", (long) dn->dstat.st_ino);
+ break;
+ case LIST_BLOCKS:
+ column += printf("%4"OFF_FMT"d ", (off_t) dn->dstat.st_blocks >> 1);
+ break;
+ case LIST_MODEBITS:
+ column += printf("%-10s ", (char *) bb_mode_string(dn->dstat.st_mode));
+ break;
+ case LIST_NLINKS:
+ column += printf("%4ld ", (long) dn->dstat.st_nlink);
+ break;
+ case LIST_ID_NAME:
+#if ENABLE_FEATURE_LS_USERNAME
+ printf("%-8.8s %-8.8s",
+ get_cached_username(dn->dstat.st_uid),
+ get_cached_groupname(dn->dstat.st_gid));
+ column += 17;
+ break;
+#endif
+ case LIST_ID_NUMERIC:
+ column += printf("%-8d %-8d", dn->dstat.st_uid, dn->dstat.st_gid);
+ break;
+ case LIST_SIZE:
+ case LIST_DEV:
+ if (S_ISBLK(dn->dstat.st_mode) || S_ISCHR(dn->dstat.st_mode)) {
+ column += printf("%4d, %3d ", (int) major(dn->dstat.st_rdev),
+ (int) minor(dn->dstat.st_rdev));
+ } else {
+ if (all_fmt & LS_DISP_HR) {
+ column += printf("%9s ",
+ make_human_readable_str(dn->dstat.st_size, 1, 0));
+ } else {
+ column += printf("%9"OFF_FMT"d ", (off_t) dn->dstat.st_size);
+ }
+ }
+ break;
+#if ENABLE_FEATURE_LS_TIMESTAMPS
+ case LIST_FULLTIME:
+ printf("%24.24s ", filetime);
+ column += 25;
+ break;
+ case LIST_DATE_TIME:
+ if ((all_fmt & LIST_FULLTIME) == 0) {
+ /* current_time_t ~== time(NULL) */
+ age = current_time_t - ttime;
+ printf("%6.6s ", filetime + 4);
+ if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) {
+ /* hh:mm if less than 6 months old */
+ printf("%5.5s ", filetime + 11);
+ } else {
+ printf(" %4.4s ", filetime + 20);
+ }
+ column += 13;
+ }
+ break;
+#endif
+#if ENABLE_SELINUX
+ case LIST_CONTEXT:
+ {
+ char context[80];
+ int len = 0;
+
+ if (dn->sid) {
+ /* I assume sid initilized with NULL */
+ len = strlen(dn->sid) + 1;
+ safe_strncpy(context, dn->sid, len);
+ freecon(dn->sid);
+ } else {
+ safe_strncpy(context, "unknown", 8);
+ }
+ printf("%-32s ", context);
+ column += MAX(33, len);
+ }
+ break;
+#endif
+ case LIST_FILENAME:
+ errno = 0;
+#if ENABLE_FEATURE_LS_COLOR
+ if (show_color && !lstat(dn->fullname, &info)) {
+ printf("\033[%d;%dm", bgcolor(info.st_mode),
+ fgcolor(info.st_mode));
+ }
+#endif
+ column += printf("%s", dn->name);
+ if (show_color) {
+ printf("\033[0m");
+ }
+ break;
+ case LIST_SYMLINK:
+ if (S_ISLNK(dn->dstat.st_mode)) {
+ char *lpath = xmalloc_readlink_or_warn(dn->fullname);
+ if (!lpath) break;
+ printf(" -> ");
+#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
+ if (!stat(dn->fullname, &info)) {
+ append = append_char(info.st_mode);
+ }
+#endif
+#if ENABLE_FEATURE_LS_COLOR
+ if (show_color) {
+ errno = 0;
+ printf("\033[%d;%dm", bgcolor(info.st_mode),
+ fgcolor(info.st_mode));
+ }
+#endif
+ column += printf("%s", lpath) + 4;
+ if (show_color) {
+ printf("\033[0m");
+ }
+ free(lpath);
+ }
+ break;
+#if ENABLE_FEATURE_LS_FILETYPES
+ case LIST_FILETYPE:
+ if (append) {
+ putchar(append);
+ column++;
+ }
+ break;
+#endif
+ }
+ }
+
+ return column;
+}
+
+/* "[-]Cadil1", POSIX mandated options, busybox always supports */
+/* "[-]gnsx", POSIX non-mandated options, busybox always supports */
+/* "[-]Ak" GNU options, busybox always supports */
+/* "[-]FLRctur", POSIX mandated options, busybox optionally supports */
+/* "[-]p", POSIX non-mandated options, busybox optionally supports */
+/* "[-]SXvThw", GNU options, busybox optionally supports */
+/* "[-]K", SELinux mandated options, busybox optionally supports */
+/* "[-]e", I think we made this one up */
+static const char ls_options[] = "Cadil1gnsxAk"
+ USE_FEATURE_LS_TIMESTAMPS("cetu")
+ USE_FEATURE_LS_SORTFILES("SXrv")
+ USE_FEATURE_LS_FILETYPES("Fp")
+ USE_FEATURE_LS_FOLLOWLINKS("L")
+ USE_FEATURE_LS_RECURSIVE("R")
+ USE_FEATURE_HUMAN_READABLE("h")
+ USE_SELINUX("K")
+ USE_FEATURE_AUTOWIDTH("T:w:")
+ USE_SELINUX("Z");
+
+enum {
+ LIST_MASK_TRIGGER = 0,
+ STYLE_MASK_TRIGGER = STYLE_MASK,
+ DISP_MASK_TRIGGER = DISP_ROWS,
+ SORT_MASK_TRIGGER = SORT_MASK,
+};
+
+static const unsigned opt_flags[] = {
+ LIST_SHORT | STYLE_COLUMNS, /* C */
+ DISP_HIDDEN | DISP_DOT, /* a */
+ DISP_NOLIST, /* d */
+ LIST_INO, /* i */
+ LIST_LONG | STYLE_LONG, /* l - remember LS_DISP_HR in mask! */
+ LIST_SHORT | STYLE_SINGLE, /* 1 */
+ 0, /* g - ingored */
+ LIST_ID_NUMERIC, /* n */
+ LIST_BLOCKS, /* s */
+ DISP_ROWS, /* x */
+ DISP_HIDDEN, /* A */
+ ENABLE_SELINUX * LIST_CONTEXT, /* k (ignored if !SELINUX) */
+#if ENABLE_FEATURE_LS_TIMESTAMPS
+ TIME_CHANGE | (ENABLE_FEATURE_LS_SORTFILES * SORT_CTIME), /* c */
+ LIST_FULLTIME, /* e */
+ ENABLE_FEATURE_LS_SORTFILES * SORT_MTIME, /* t */
+ TIME_ACCESS | (ENABLE_FEATURE_LS_SORTFILES * SORT_ATIME), /* u */
+#endif
+#if ENABLE_FEATURE_LS_SORTFILES
+ SORT_SIZE, /* S */
+ SORT_EXT, /* X */
+ SORT_REVERSE, /* r */
+ SORT_VERSION, /* v */
+#endif
+#if ENABLE_FEATURE_LS_FILETYPES
+ LIST_FILETYPE | LIST_EXEC, /* F */
+ LIST_FILETYPE, /* p */
+#endif
+#if ENABLE_FEATURE_LS_FOLLOWLINKS
+ FOLLOW_LINKS, /* L */
+#endif
+#if ENABLE_FEATURE_LS_RECURSIVE
+ DISP_RECURSIVE, /* R */
+#endif
+#if ENABLE_FEATURE_HUMAN_READABLE
+ LS_DISP_HR, /* h */
+#endif
+#if ENABLE_SELINUX
+ LIST_MODEBITS|LIST_NLINKS|LIST_CONTEXT|LIST_SIZE|LIST_DATE_TIME, /* K */
+#endif
+#if ENABLE_FEATURE_AUTOWIDTH
+ 0, 0, /* T, w - ignored */
+#endif
+#if ENABLE_SELINUX
+ LIST_MODEBITS|LIST_ID_NAME|LIST_CONTEXT, /* Z */
+#endif
+ (1U<<31)
+};
+
+
+/* THIS IS A "SAFE" APPLET, main() MAY BE CALLED INTERNALLY FROM SHELL */
+/* BE CAREFUL! */
+
+int ls_main(int argc, char **argv);
+int ls_main(int argc, char **argv)
+{
+ struct dnode **dnd;
+ struct dnode **dnf;
+ struct dnode **dnp;
+ struct dnode *dn;
+ struct dnode *cur;
+ unsigned opt;
+ int nfiles = 0;
+ int dnfiles;
+ int dndirs;
+ int oi;
+ int ac;
+ int i;
+ char **av;
+ USE_FEATURE_AUTOWIDTH(char *tabstops_str = NULL;)
+ USE_FEATURE_AUTOWIDTH(char *terminal_width_str = NULL;)
+ USE_FEATURE_LS_COLOR(char *color_opt;)
+
+#if ENABLE_FEATURE_LS_TIMESTAMPS
+ time(&current_time_t);
+#endif
+
+ all_fmt = LIST_SHORT |
+ (ENABLE_FEATURE_LS_SORTFILES * (SORT_NAME | SORT_FORWARD));
+
+#if ENABLE_FEATURE_AUTOWIDTH
+ /* Obtain the terminal width */
+ get_terminal_width_height(STDOUT_FILENO, &terminal_width, NULL);
+ /* Go one less... */
+ terminal_width--;
+#endif
+
+ /* process options */
+ USE_FEATURE_LS_COLOR(applet_long_options = ls_color_opt;)
+#if ENABLE_FEATURE_AUTOWIDTH
+ opt = getopt32(argc, argv, ls_options, &tabstops_str, &terminal_width_str
+ USE_FEATURE_LS_COLOR(, &color_opt));
+ if (tabstops_str)
+ tabstops = xatou(tabstops_str);
+ if (terminal_width_str)
+ terminal_width = xatou(terminal_width_str);
+#else
+ opt = getopt32(argc, argv, ls_options USE_FEATURE_LS_COLOR(, &color_opt));
+#endif
+ for (i = 0; opt_flags[i] != (1U<<31); i++) {
+ if (opt & (1 << i)) {
+ unsigned flags = opt_flags[i];
+
+ if (flags & LIST_MASK_TRIGGER)
+ all_fmt &= ~LIST_MASK;
+ if (flags & STYLE_MASK_TRIGGER)
+ all_fmt &= ~STYLE_MASK;
+ if (flags & SORT_MASK_TRIGGER)
+ all_fmt &= ~SORT_MASK;
+ if (flags & DISP_MASK_TRIGGER)
+ all_fmt &= ~DISP_MASK;
+ if (flags & TIME_MASK)
+ all_fmt &= ~TIME_MASK;
+ if (flags & LIST_CONTEXT)
+ all_fmt |= STYLE_SINGLE;
+ /* huh?? opt cannot be 'l' */
+ //if (LS_DISP_HR && opt == 'l')
+ // all_fmt &= ~LS_DISP_HR;
+ all_fmt |= flags;
+ }
+ }
+
+#if ENABLE_FEATURE_LS_COLOR
+ /* find color bit value - last position for short getopt */
+ if (ENABLE_FEATURE_LS_COLOR_IS_DEFAULT && isatty(STDOUT_FILENO)) {
+ char *p = getenv("LS_COLORS");
+ /* LS_COLORS is unset, or (not empty && not "none") ? */
+ if (!p || (p[0] && strcmp(p, "none")))
+ show_color = 1;
+ }
+ if (opt & (1 << i)) { /* next flag after short options */
+ if (!color_opt || !strcmp("always", color_opt))
+ show_color = 1;
+ else if (color_opt && !strcmp("never", color_opt))
+ show_color = 0;
+ else if (color_opt && !strcmp("auto", color_opt) && isatty(STDOUT_FILENO))
+ show_color = 1;
+ }
+#endif
+
+ /* sort out which command line options take precedence */
+ if (ENABLE_FEATURE_LS_RECURSIVE && (all_fmt & DISP_NOLIST))
+ all_fmt &= ~DISP_RECURSIVE; /* no recurse if listing only dir */
+ if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) {
+ if (all_fmt & TIME_CHANGE)
+ all_fmt = (all_fmt & ~SORT_MASK) | SORT_CTIME;
+ if (all_fmt & TIME_ACCESS)
+ all_fmt = (all_fmt & ~SORT_MASK) | SORT_ATIME;
+ }
+ if ((all_fmt & STYLE_MASK) != STYLE_LONG) /* only for long list */
+ all_fmt &= ~(LIST_ID_NUMERIC|LIST_FULLTIME|LIST_ID_NAME|LIST_ID_NUMERIC);
+ if (ENABLE_FEATURE_LS_USERNAME)
+ if ((all_fmt & STYLE_MASK) == STYLE_LONG && (all_fmt & LIST_ID_NUMERIC))
+ all_fmt &= ~LIST_ID_NAME; /* don't list names if numeric uid */
+
+ /* choose a display format */
+ if (!(all_fmt & STYLE_MASK))
+ all_fmt |= (isatty(STDOUT_FILENO) ? STYLE_COLUMNS : STYLE_SINGLE);
+
+ /*
+ * when there are no cmd line args we have to supply a default "." arg.
+ * we will create a second argv array, "av" that will hold either
+ * our created "." arg, or the real cmd line args. The av array
+ * just holds the pointers- we don't move the date the pointers
+ * point to.
+ */
+ ac = argc - optind; /* how many cmd line args are left */
+ if (ac < 1) {
+ static const char *const dotdir[] = { "." };
+
+ av = (char **) dotdir;
+ ac = 1;
+ } else {
+ av = argv + optind;
+ }
+
+ /* now, everything is in the av array */
+ if (ac > 1)
+ all_fmt |= DISP_DIRNAME; /* 2 or more items? label directories */
+
+ /* stuff the command line file names into a dnode array */
+ dn = NULL;
+ for (oi = 0; oi < ac; oi++) {
+ /* ls w/o -l follows links on command line */
+ cur = my_stat(av[oi], av[oi], !(all_fmt & STYLE_LONG));
+ if (!cur)
+ continue;
+ cur->allocated = 0;
+ cur->next = dn;
+ dn = cur;
+ nfiles++;
+ }
+
+ /* now that we know how many files there are
+ * allocate memory for an array to hold dnode pointers
+ */
+ dnp = dnalloc(nfiles);
+ for (i = 0, cur = dn; i < nfiles; i++) {
+ dnp[i] = cur; /* save pointer to node in array */
+ cur = cur->next;
+ }
+
+ if (all_fmt & DISP_NOLIST) {
+ dnsort(dnp, nfiles);
+ if (nfiles > 0)
+ showfiles(dnp, nfiles);
+ } else {
+ dnd = splitdnarray(dnp, nfiles, SPLIT_DIR);
+ dnf = splitdnarray(dnp, nfiles, SPLIT_FILE);
+ dndirs = countdirs(dnp, nfiles);
+ dnfiles = nfiles - dndirs;
+ if (dnfiles > 0) {
+ dnsort(dnf, dnfiles);
+ showfiles(dnf, dnfiles);
+ if (ENABLE_FEATURE_CLEAN_UP)
+ free(dnf);
+ }
+ if (dndirs > 0) {
+ dnsort(dnd, dndirs);
+ showdirs(dnd, dndirs, dnfiles == 0);
+ if (ENABLE_FEATURE_CLEAN_UP)
+ free(dnd);
+ }
+ }
+ if (ENABLE_FEATURE_CLEAN_UP)
+ dfree(dnp, nfiles);
+ return status;
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/md5_sha1_sum.c b/i/pc104/initrd/conf/busybox/coreutils/md5_sha1_sum.c
new file mode 100644
index 0000000..417e90b
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/md5_sha1_sum.c
@@ -0,0 +1,185 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Copyright (C) 2003 Glenn L. McGrath
+ * Copyright (C) 2003-2004 Erik Andersen
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ */
+
+#include "busybox.h"
+
+typedef enum { HASH_SHA1, HASH_MD5 } hash_algo_t;
+
+#define FLAG_SILENT 1
+#define FLAG_CHECK 2
+#define FLAG_WARN 4
+
+/* This might be useful elsewhere */
+static unsigned char *hash_bin_to_hex(unsigned char *hash_value,
+ unsigned hash_length)
+{
+ /* xzalloc zero-terminates */
+ char *hex_value = xzalloc((hash_length * 2) + 1);
+ bin2hex(hex_value, (char*)hash_value, hash_length);
+ return hex_value;
+}
+
+static uint8_t *hash_file(const char *filename, hash_algo_t hash_algo)
+{
+ int src_fd, hash_len, count;
+ union _ctx_ {
+ sha1_ctx_t sha1;
+ md5_ctx_t md5;
+ } context;
+ uint8_t *hash_value = NULL;
+ RESERVE_CONFIG_UBUFFER(in_buf, 4096);
+ void (*update)(const void*, size_t, void*);
+ void (*final)(void*, void*);
+
+ src_fd = STDIN_FILENO;
+ if (NOT_LONE_DASH(filename)) {
+ src_fd = open(filename, O_RDONLY);
+ if (src_fd < 0) {
+ bb_perror_msg("%s", filename);
+ return NULL;
+ }
+ }
+
+ /* figure specific hash algorithims */
+ if (ENABLE_MD5SUM && hash_algo==HASH_MD5) {
+ md5_begin(&context.md5);
+ update = (void (*)(const void*, size_t, void*))md5_hash;
+ final = (void (*)(void*, void*))md5_end;
+ hash_len = 16;
+ } else if (ENABLE_SHA1SUM && hash_algo==HASH_SHA1) {
+ sha1_begin(&context.sha1);
+ update = (void (*)(const void*, size_t, void*))sha1_hash;
+ final = (void (*)(void*, void*))sha1_end;
+ hash_len = 20;
+ } else {
+ bb_error_msg_and_die("algorithm not supported");
+ }
+
+ while (0 < (count = safe_read(src_fd, in_buf, 4096))) {
+ update(in_buf, count, &context);
+ }
+
+ if (count == 0) {
+ final(in_buf, &context);
+ hash_value = hash_bin_to_hex(in_buf, hash_len);
+ }
+
+ RELEASE_CONFIG_BUFFER(in_buf);
+
+ if (src_fd != STDIN_FILENO) {
+ close(src_fd);
+ }
+
+ return hash_value;
+}
+
+int md5_sha1_sum_main(int argc, char **argv);
+int md5_sha1_sum_main(int argc, char **argv)
+{
+ int return_value = EXIT_SUCCESS;
+ uint8_t *hash_value;
+ unsigned flags;
+ hash_algo_t hash_algo = ENABLE_MD5SUM
+ ? (ENABLE_SHA1SUM ? (**argv=='m' ? HASH_MD5 : HASH_SHA1) : HASH_MD5)
+ : HASH_SHA1;
+
+ if (ENABLE_FEATURE_MD5_SHA1_SUM_CHECK)
+ flags = getopt32(argc, argv, "scw");
+ else optind = 1;
+
+ if (ENABLE_FEATURE_MD5_SHA1_SUM_CHECK && !(flags & FLAG_CHECK)) {
+ if (flags & FLAG_SILENT) {
+ bb_error_msg_and_die
+ ("-%c is meaningful only when verifying checksums", 's');
+ } else if (flags & FLAG_WARN) {
+ bb_error_msg_and_die
+ ("-%c is meaningful only when verifying checksums", 'w');
+ }
+ }
+
+ if (argc == optind) {
+ argv[argc++] = (char*)"-";
+ }
+
+ if (ENABLE_FEATURE_MD5_SHA1_SUM_CHECK && (flags & FLAG_CHECK)) {
+ FILE *pre_computed_stream;
+ int count_total = 0;
+ int count_failed = 0;
+ char *file_ptr = argv[optind];
+ char *line;
+
+ if (optind + 1 != argc) {
+ bb_error_msg_and_die
+ ("only one argument may be specified when using -c");
+ }
+
+ pre_computed_stream = stdin;
+ if (NOT_LONE_DASH(file_ptr)) {
+ pre_computed_stream = xfopen(file_ptr, "r");
+ }
+
+ while ((line = xmalloc_getline(pre_computed_stream)) != NULL) {
+ char *filename_ptr;
+
+ count_total++;
+ filename_ptr = strstr(line, " ");
+ /* handle format for binary checksums */
+ if (filename_ptr == NULL) {
+ filename_ptr = strstr(line, " *");
+ }
+ if (filename_ptr == NULL) {
+ if (flags & FLAG_WARN) {
+ bb_error_msg("invalid format");
+ }
+ count_failed++;
+ return_value = EXIT_FAILURE;
+ free(line);
+ continue;
+ }
+ *filename_ptr = '\0';
+ filename_ptr += 2;
+
+ hash_value = hash_file(filename_ptr, hash_algo);
+
+ if (hash_value && (strcmp((char*)hash_value, line) == 0)) {
+ if (!(flags & FLAG_SILENT))
+ printf("%s: OK\n", filename_ptr);
+ } else {
+ if (!(flags & FLAG_SILENT))
+ printf("%s: FAILED\n", filename_ptr);
+ count_failed++;
+ return_value = EXIT_FAILURE;
+ }
+ /* possible free(NULL) */
+ free(hash_value);
+ free(line);
+ }
+ if (count_failed && !(flags & FLAG_SILENT)) {
+ bb_error_msg("WARNING: %d of %d computed checksums did NOT match",
+ count_failed, count_total);
+ }
+ /*
+ if (fclose_if_not_stdin(pre_computed_stream) == EOF) {
+ bb_perror_msg_and_die("cannot close file %s", file_ptr);
+ }
+ */
+ } else {
+ while (optind < argc) {
+ char *file_ptr = argv[optind++];
+
+ hash_value = hash_file(file_ptr, hash_algo);
+ if (hash_value == NULL) {
+ return_value = EXIT_FAILURE;
+ } else {
+ printf("%s %s\n", hash_value, file_ptr);
+ free(hash_value);
+ }
+ }
+ }
+ return return_value;
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/mkdir.c b/i/pc104/initrd/conf/busybox/coreutils/mkdir.c
new file mode 100644
index 0000000..690e4ab
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/mkdir.c
@@ -0,0 +1,82 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini mkdir implementation for busybox
+ *
+ * Copyright (C) 2001 Matt Kraai <kraai@alumni.carnegiemellon.edu>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/* BB_AUDIT SUSv3 compliant */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/mkdir.html */
+
+/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
+ *
+ * Fixed broken permission setting when -p was used; especially in
+ * conjunction with -m.
+ */
+
+/* Nov 28, 2006 Yoshinori Sato <ysato@users.sourceforge.jp>: Add SELinux Support.
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <getopt.h> /* struct option */
+#include "busybox.h"
+
+#if ENABLE_FEATURE_MKDIR_LONG_OPTIONS
+static const struct option mkdir_long_options[] = {
+ { "mode", 1, NULL, 'm' },
+ { "parents", 0, NULL, 'p' },
+#if ENABLE_SELINUX
+ { "context", 1, NULL, 'Z' },
+#endif
+ { 0, 0, 0, 0 }
+};
+#endif
+
+int mkdir_main(int argc, char **argv);
+int mkdir_main(int argc, char **argv)
+{
+ mode_t mode = (mode_t)(-1);
+ int status = EXIT_SUCCESS;
+ int flags = 0;
+ unsigned opt;
+ char *smode;
+#if ENABLE_SELINUX
+ security_context_t scontext;
+#endif
+
+#if ENABLE_FEATURE_MKDIR_LONG_OPTIONS
+ applet_long_options = mkdir_long_options;
+#endif
+ opt = getopt32(argc, argv, "m:p" USE_SELINUX("Z:"), &smode USE_SELINUX(,&scontext));
+ if (opt & 1) {
+ mode = 0777;
+ if (!bb_parse_mode(smode, &mode)) {
+ bb_error_msg_and_die("invalid mode '%s'", smode);
+ }
+ }
+ if (opt & 2)
+ flags |= FILEUTILS_RECUR;
+#if ENABLE_SELINUX
+ if (opt & 4) {
+ selinux_or_die();
+ setfscreatecon_or_die(scontext);
+ }
+#endif
+
+ if (optind == argc) {
+ bb_show_usage();
+ }
+
+ argv += optind;
+
+ do {
+ if (bb_make_directory(*argv, mode, flags)) {
+ status = EXIT_FAILURE;
+ }
+ } while (*++argv);
+
+ return status;
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/mkfifo.c b/i/pc104/initrd/conf/busybox/coreutils/mkfifo.c
new file mode 100644
index 0000000..6d8aa41
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/mkfifo.c
@@ -0,0 +1,39 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * mkfifo implementation for busybox
+ *
+ * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/* BB_AUDIT SUSv3 compliant */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/mkfifo.html */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include "busybox.h"
+#include "libcoreutils/coreutils.h"
+
+int mkfifo_main(int argc, char **argv);
+int mkfifo_main(int argc, char **argv)
+{
+ mode_t mode;
+ int retval = EXIT_SUCCESS;
+
+ mode = getopt_mk_fifo_nod(argc, argv);
+
+ if (!*(argv += optind)) {
+ bb_show_usage();
+ }
+
+ do {
+ if (mkfifo(*argv, mode) < 0) {
+ bb_perror_msg("%s", *argv); /* Avoid multibyte problems. */
+ retval = EXIT_FAILURE;
+ }
+ } while (*++argv);
+
+ return retval;
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/mknod.c b/i/pc104/initrd/conf/busybox/coreutils/mknod.c
new file mode 100644
index 0000000..6fa6d83
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/mknod.c
@@ -0,0 +1,51 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * mknod implementation for busybox
+ *
+ * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/* BB_AUDIT SUSv3 N/A -- Matches GNU behavior. */
+
+#include <sys/sysmacros.h> // For makedev
+
+#include "busybox.h"
+#include "libcoreutils/coreutils.h"
+
+static const char modes_chars[] = { 'p', 'c', 'u', 'b', 0, 1, 1, 2 };
+static const mode_t modes_cubp[] = { S_IFIFO, S_IFCHR, S_IFBLK };
+
+int mknod_main(int argc, char **argv);
+int mknod_main(int argc, char **argv)
+{
+ mode_t mode;
+ dev_t dev;
+ const char *name;
+
+ mode = getopt_mk_fifo_nod(argc, argv);
+ argv += optind;
+ argc -= optind;
+
+ if ((argc >= 2) && ((name = strchr(modes_chars, argv[1][0])) != NULL)) {
+ mode |= modes_cubp[(int)(name[4])];
+
+ dev = 0;
+ if ((*name != 'p') && ((argc -= 2) == 2)) {
+ /* Autodetect what the system supports; these macros should
+ * optimize out to two constants. */
+ dev = makedev(xatoul_range(argv[2], 0, major(UINT_MAX)),
+ xatoul_range(argv[3], 0, minor(UINT_MAX)));
+ }
+
+ if (argc == 2) {
+ name = *argv;
+ if (mknod(name, mode, dev) == 0) {
+ return EXIT_SUCCESS;
+ }
+ bb_perror_msg_and_die("%s", name);
+ }
+ }
+ bb_show_usage();
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/mv.c b/i/pc104/initrd/conf/busybox/coreutils/mv.c
new file mode 100644
index 0000000..c08d232
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/mv.c
@@ -0,0 +1,137 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini mv implementation for busybox
+ *
+ * Copyright (C) 2000 by Matt Kraai <kraai@alumni.carnegiemellon.edu>
+ * SELinux support by Yuichi Nakamura <ynakam@hitachisoft.jp>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
+ *
+ * Size reduction and improved error checking.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <getopt.h> /* struct option */
+#include "busybox.h"
+#include "libcoreutils/coreutils.h"
+
+#if ENABLE_FEATURE_MV_LONG_OPTIONS
+static const struct option mv_long_options[] = {
+ { "interactive", 0, NULL, 'i' },
+ { "force", 0, NULL, 'f' },
+ { 0, 0, 0, 0 }
+};
+#endif
+
+#define OPT_FILEUTILS_FORCE 1
+#define OPT_FILEUTILS_INTERACTIVE 2
+
+static const char fmt[] = "cannot overwrite %sdirectory with %sdirectory";
+
+int mv_main(int argc, char **argv);
+int mv_main(int argc, char **argv)
+{
+ struct stat dest_stat;
+ const char *last;
+ const char *dest;
+ unsigned long flags;
+ int dest_exists;
+ int status = 0;
+ int copy_flag = 0;
+
+#if ENABLE_FEATURE_MV_LONG_OPTIONS
+ applet_long_options = mv_long_options;
+#endif
+ opt_complementary = "f-i:i-f";
+ flags = getopt32(argc, argv, "fi");
+ if (optind + 2 > argc) {
+ bb_show_usage();
+ }
+
+ last = argv[argc - 1];
+ argv += optind;
+
+ if (optind + 2 == argc) {
+ dest_exists = cp_mv_stat(last, &dest_stat);
+ if (dest_exists < 0) {
+ return 1;
+ }
+
+ if (!(dest_exists & 2)) {
+ dest = last;
+ goto DO_MOVE;
+ }
+ }
+
+ do {
+ dest = concat_path_file(last, bb_get_last_path_component(*argv));
+ dest_exists = cp_mv_stat(dest, &dest_stat);
+ if (dest_exists < 0) {
+ goto RET_1;
+ }
+
+DO_MOVE:
+
+ if (dest_exists && !(flags & OPT_FILEUTILS_FORCE) &&
+ ((access(dest, W_OK) < 0 && isatty(0)) ||
+ (flags & OPT_FILEUTILS_INTERACTIVE))) {
+ if (fprintf(stderr, "mv: overwrite '%s'? ", dest) < 0) {
+ goto RET_1; /* Ouch! fprintf failed! */
+ }
+ if (!bb_ask_confirmation()) {
+ goto RET_0;
+ }
+ }
+ if (rename(*argv, dest) < 0) {
+ struct stat source_stat;
+ int source_exists;
+
+ if (errno != EXDEV ||
+ (source_exists = cp_mv_stat(*argv, &source_stat)) < 1) {
+ bb_perror_msg("cannot rename '%s'", *argv);
+ } else {
+ if (dest_exists) {
+ if (dest_exists == 3) {
+ if (source_exists != 3) {
+ bb_error_msg(fmt, "", "non-");
+ goto RET_1;
+ }
+ } else {
+ if (source_exists == 3) {
+ bb_error_msg(fmt, "non-", "");
+ goto RET_1;
+ }
+ }
+ if (unlink(dest) < 0) {
+ bb_perror_msg("cannot remove '%s'", dest);
+ goto RET_1;
+ }
+ }
+ copy_flag = FILEUTILS_RECUR | FILEUTILS_PRESERVE_STATUS;
+#if ENABLE_SELINUX
+ copy_flag |= FILEUTILS_PRESERVE_SECURITY_CONTEXT;
+#endif
+ if ((copy_file(*argv, dest, copy_flag) >= 0) &&
+ (remove_file(*argv, FILEUTILS_RECUR | FILEUTILS_FORCE) >= 0)) {
+ goto RET_0;
+ }
+ }
+RET_1:
+ status = 1;
+ }
+RET_0:
+ if (dest != last) {
+ free((void *) dest);
+ }
+ } while (*++argv != last);
+
+ return status;
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/nice.c b/i/pc104/initrd/conf/busybox/coreutils/nice.c
new file mode 100644
index 0000000..3285315
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/nice.c
@@ -0,0 +1,55 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * nice implementation for busybox
+ *
+ * Copyright (C) 2005 Manuel Novoa III <mjn3@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include <sys/resource.h>
+#include "busybox.h"
+
+int nice_main(int argc, char **argv);
+int nice_main(int argc, char **argv)
+{
+ int old_priority, adjustment;
+
+ old_priority = getpriority(PRIO_PROCESS, 0);
+
+ if (!*++argv) { /* No args, so (GNU) output current nice value. */
+ printf("%d\n", old_priority);
+ fflush_stdout_and_exit(EXIT_SUCCESS);
+ }
+
+ adjustment = 10; /* Set default adjustment. */
+
+ if (argv[0][0] == '-') {
+ if (argv[0][1] == 'n') { /* -n */
+ if (argv[0][2]) { /* -nNNNN (w/o space) */
+ argv[0] += 2; argv--; argc++;
+ }
+ } else { /* -NNN (NNN may be negative) == -n NNN */
+ argv[0] += 1; argv--; argc++;
+ }
+ if (argc < 4) { /* Missing priority and/or utility! */
+ bb_show_usage();
+ }
+ adjustment = xatoi_range(argv[1], INT_MIN/2, INT_MAX/2);
+ argv += 2;
+ }
+
+ { /* Set our priority. */
+ int prio = old_priority + adjustment;
+
+ if (setpriority(PRIO_PROCESS, 0, prio) < 0) {
+ bb_perror_msg_and_die("setpriority(%d)", prio);
+ }
+ }
+
+ BB_EXECVP(*argv, argv); /* Now exec the desired program. */
+
+ /* The exec failed... */
+ xfunc_error_retval = (errno == ENOENT) ? 127 : 126; /* SUSv3 */
+ bb_perror_msg_and_die("%s", *argv);
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/nohup.c b/i/pc104/initrd/conf/busybox/coreutils/nohup.c
new file mode 100644
index 0000000..6a6c70e
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/nohup.c
@@ -0,0 +1,60 @@
+/* vi: set sw=4 ts=4: */
+/* nohup - invoke a utility immune to hangups.
+ *
+ * Busybox version based on nohup specification at
+ * http://www.opengroup.org/onlinepubs/007904975/utilities/nohup.html
+ *
+ * Copyright 2006 Rob Landley <rob@landley.net>
+ * Copyright 2006 Bernhard Fischer
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "busybox.h"
+
+int nohup_main(int argc, char **argv);
+int nohup_main(int argc, char **argv)
+{
+ int nullfd;
+ const char *nohupout;
+ char *home = NULL;
+
+ xfunc_error_retval = 127;
+
+ if (argc < 2) bb_show_usage();
+
+ nullfd = xopen(bb_dev_null, O_WRONLY|O_APPEND);
+ /* If stdin is a tty, detach from it. */
+ if (isatty(STDIN_FILENO))
+ dup2(nullfd, STDIN_FILENO);
+
+ nohupout = "nohup.out";
+ /* Redirect stdout to nohup.out, either in "." or in "$HOME". */
+ if (isatty(STDOUT_FILENO)) {
+ close(STDOUT_FILENO);
+ if (open(nohupout, O_CREAT|O_WRONLY|O_APPEND, S_IRUSR|S_IWUSR) < 0) {
+ home = getenv("HOME");
+ if (home) {
+ nohupout = concat_path_file(home, nohupout);
+ xopen3(nohupout, O_CREAT|O_WRONLY|O_APPEND, S_IRUSR|S_IWUSR);
+ }
+ }
+ } else dup2(nullfd, STDOUT_FILENO);
+
+ /* If we have a tty on stderr, announce filename and redirect to stdout.
+ * Else redirect to /dev/null.
+ */
+ if (isatty(STDERR_FILENO)) {
+ bb_error_msg("appending to %s", nohupout);
+ dup2(STDOUT_FILENO, STDERR_FILENO);
+ } else dup2(nullfd, STDERR_FILENO);
+
+ if (nullfd > 2)
+ close(nullfd);
+ signal(SIGHUP, SIG_IGN);
+
+ BB_EXECVP(argv[1], argv+1);
+ if (ENABLE_FEATURE_CLEAN_UP && home)
+ free((char*)nohupout);
+ bb_perror_msg_and_die("%s", argv[1]);
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/od.c b/i/pc104/initrd/conf/busybox/coreutils/od.c
new file mode 100644
index 0000000..0de9def
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/od.c
@@ -0,0 +1,225 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * od implementation for busybox
+ * Based on code from util-linux v 2.11l
+ *
+ * Copyright (c) 1990
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ *
+ * Original copyright notice is retained at the end of this file.
+ */
+
+
+#include "busybox.h"
+#if ENABLE_DESKTOP
+/* This one provides -t (busybox's own build script needs it) */
+#include "od_bloaty.c"
+#else
+#include <getopt.h>
+
+#include "dump.h"
+
+#define isdecdigit(c) isdigit(c)
+#define ishexdigit(c) (isxdigit)(c)
+
+static void
+odoffset(int argc, char ***argvp)
+{
+ char *num, *p;
+ int base;
+ char *end;
+
+ /*
+ * The offset syntax of od(1) was genuinely bizarre. First, if
+ * it started with a plus it had to be an offset. Otherwise, if
+ * there were at least two arguments, a number or lower-case 'x'
+ * followed by a number makes it an offset. By default it was
+ * octal; if it started with 'x' or '0x' it was hex. If it ended
+ * in a '.', it was decimal. If a 'b' or 'B' was appended, it
+ * multiplied the number by 512 or 1024 byte units. There was
+ * no way to assign a block count to a hex offset.
+ *
+ * We assumes it's a file if the offset is bad.
+ */
+ p = **argvp;
+
+ if (!p) {
+ /* hey someone is probably piping to us ... */
+ return;
+ }
+
+ if ((*p != '+')
+ && (argc < 2
+ || (!isdecdigit(p[0])
+ && ((p[0] != 'x') || !ishexdigit(p[1])))))
+ return;
+
+ base = 0;
+ /*
+ * bb_dump_skip over leading '+', 'x[0-9a-fA-f]' or '0x', and
+ * set base.
+ */
+ if (p[0] == '+')
+ ++p;
+ if (p[0] == 'x' && ishexdigit(p[1])) {
+ ++p;
+ base = 16;
+ } else if (p[0] == '0' && p[1] == 'x') {
+ p += 2;
+ base = 16;
+ }
+
+ /* bb_dump_skip over the number */
+ if (base == 16)
+ for (num = p; ishexdigit(*p); ++p);
+ else
+ for (num = p; isdecdigit(*p); ++p);
+
+ /* check for no number */
+ if (num == p)
+ return;
+
+ /* if terminates with a '.', base is decimal */
+ if (*p == '.') {
+ if (base)
+ return;
+ base = 10;
+ }
+
+ bb_dump_skip = strtol(num, &end, base ? base : 8);
+
+ /* if end isn't the same as p, we got a non-octal digit */
+ if (end != p)
+ bb_dump_skip = 0;
+ else {
+ if (*p) {
+ if (*p == 'b') {
+ bb_dump_skip *= 512;
+ ++p;
+ } else if (*p == 'B') {
+ bb_dump_skip *= 1024;
+ ++p;
+ }
+ }
+ if (*p)
+ bb_dump_skip = 0;
+ else {
+ ++*argvp;
+ /*
+ * If the offset uses a non-octal base, the base of
+ * the offset is changed as well. This isn't pretty,
+ * but it's easy.
+ */
+#define TYPE_OFFSET 7
+ {
+ char x_or_d;
+ if (base == 16) {
+ x_or_d = 'x';
+ goto DO_X_OR_D;
+ }
+ if (base == 10) {
+ x_or_d = 'd';
+ DO_X_OR_D:
+ bb_dump_fshead->nextfu->fmt[TYPE_OFFSET]
+ = bb_dump_fshead->nextfs->nextfu->fmt[TYPE_OFFSET]
+ = x_or_d;
+ }
+ }
+ }
+ }
+}
+
+static const char * const add_strings[] = {
+ "16/1 \"%3_u \" \"\\n\"", /* a */
+ "8/2 \" %06o \" \"\\n\"", /* B, o */
+ "16/1 \"%03o \" \"\\n\"", /* b */
+ "16/1 \"%3_c \" \"\\n\"", /* c */
+ "8/2 \" %05u \" \"\\n\"", /* d */
+ "4/4 \" %010u \" \"\\n\"", /* D */
+ "2/8 \" %21.14e \" \"\\n\"", /* e (undocumented in od), F */
+ "4/4 \" %14.7e \" \"\\n\"", /* f */
+ "4/4 \" %08x \" \"\\n\"", /* H, X */
+ "8/2 \" %04x \" \"\\n\"", /* h, x */
+ "4/4 \" %11d \" \"\\n\"", /* I, L, l */
+ "8/2 \" %6d \" \"\\n\"", /* i */
+ "4/4 \" %011o \" \"\\n\"", /* O */
+};
+
+static const char od_opts[] = "aBbcDdeFfHhIiLlOoXxv";
+
+static const char od_o2si[] = {
+ 0, 1, 2, 3, 5,
+ 4, 6, 6, 7, 8,
+ 9, 0xa, 0xb, 0xa, 0xa,
+ 0xb, 1, 8, 9,
+};
+
+int od_main(int argc, char **argv);
+int od_main(int argc, char **argv)
+{
+ int ch;
+ int first = 1;
+ char *p;
+ bb_dump_vflag = FIRST;
+ bb_dump_length = -1;
+
+ while ((ch = getopt(argc, argv, od_opts)) > 0) {
+ if (ch == 'v') {
+ bb_dump_vflag = ALL;
+ } else if (((p = strchr(od_opts, ch)) != NULL) && (*p != '\0')) {
+ if (first) {
+ first = 0;
+ bb_dump_add("\"%07.7_Ao\n\"");
+ bb_dump_add("\"%07.7_ao \"");
+ } else {
+ bb_dump_add("\" \"");
+ }
+ bb_dump_add(add_strings[(int)od_o2si[(p-od_opts)]]);
+ } else { /* P, p, s, w, or other unhandled */
+ bb_show_usage();
+ }
+ }
+ if (!bb_dump_fshead) {
+ bb_dump_add("\"%07.7_Ao\n\"");
+ bb_dump_add("\"%07.7_ao \" 8/2 \"%06o \" \"\\n\"");
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ odoffset(argc, &argv);
+
+ return bb_dump_dump(argv);
+}
+#endif /* ENABLE_DESKTOP */
+
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
diff --git a/i/pc104/initrd/conf/busybox/coreutils/od_bloaty.c b/i/pc104/initrd/conf/busybox/coreutils/od_bloaty.c
new file mode 100644
index 0000000..74765f8
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/od_bloaty.c
@@ -0,0 +1,1465 @@
+/* od -- dump files in octal and other formats
+ Copyright (C) 92, 1995-2004 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+/* Written by Jim Meyering. */
+
+/* Busyboxed by Denis Vlasenko
+
+Based on od.c from coreutils-5.2.1
+Top bloat sources:
+
+00000073 t parse_old_offset
+0000007b t get_lcm
+00000090 r long_options
+00000092 t print_named_ascii
+000000bf t print_ascii
+00000168 t write_block
+00000366 t decode_format_string
+00000a71 T od_main
+
+Tested for compat with coreutils 6.3
+using this script. Minor differences fixed.
+
+#!/bin/sh
+echo STD
+time /path/to/coreutils/od \
+...params... \
+>std
+echo Exit code $?
+echo BBOX
+time ./busybox od \
+...params... \
+>bbox
+echo Exit code $?
+diff -u -a std bbox >bbox.diff || { echo Different!; sleep 1; }
+
+*/
+
+
+#include "busybox.h"
+#include <getopt.h>
+
+#define assert(a) ((void)0)
+
+/* Check for 0x7f is a coreutils 6.3 addition */
+#define ISPRINT(c) (((c)>=' ') && (c) != 0x7f)
+
+typedef long double longdouble_t;
+typedef unsigned long long ulonglong_t;
+typedef long long llong;
+
+#if ENABLE_LFS
+# define xstrtooff_sfx xstrtoull_sfx
+#else
+# define xstrtooff_sfx xstrtoul_sfx
+#endif
+
+/* The default number of input bytes per output line. */
+#define DEFAULT_BYTES_PER_BLOCK 16
+
+/* The number of decimal digits of precision in a float. */
+#ifndef FLT_DIG
+# define FLT_DIG 7
+#endif
+
+/* The number of decimal digits of precision in a double. */
+#ifndef DBL_DIG
+# define DBL_DIG 15
+#endif
+
+/* The number of decimal digits of precision in a long double. */
+#ifndef LDBL_DIG
+# define LDBL_DIG DBL_DIG
+#endif
+
+enum size_spec {
+ NO_SIZE,
+ CHAR,
+ SHORT,
+ INT,
+ LONG,
+ LONG_LONG,
+ FLOAT_SINGLE,
+ FLOAT_DOUBLE,
+ FLOAT_LONG_DOUBLE,
+ N_SIZE_SPECS
+};
+
+enum output_format {
+ SIGNED_DECIMAL,
+ UNSIGNED_DECIMAL,
+ OCTAL,
+ HEXADECIMAL,
+ FLOATING_POINT,
+ NAMED_CHARACTER,
+ CHARACTER
+};
+
+/* Each output format specification (from '-t spec' or from
+ old-style options) is represented by one of these structures. */
+struct tspec {
+ enum output_format fmt;
+ enum size_spec size;
+ void (*print_function) (size_t, const char *, const char *);
+ char *fmt_string;
+ int hexl_mode_trailer;
+ int field_width;
+};
+
+/* Convert the number of 8-bit bytes of a binary representation to
+ the number of characters (digits + sign if the type is signed)
+ required to represent the same quantity in the specified base/type.
+ For example, a 32-bit (4-byte) quantity may require a field width
+ as wide as the following for these types:
+ 11 unsigned octal
+ 11 signed decimal
+ 10 unsigned decimal
+ 8 unsigned hexadecimal */
+
+static const uint8_t bytes_to_oct_digits[] =
+{0, 3, 6, 8, 11, 14, 16, 19, 22, 25, 27, 30, 32, 35, 38, 41, 43};
+
+static const uint8_t bytes_to_signed_dec_digits[] =
+{1, 4, 6, 8, 11, 13, 16, 18, 20, 23, 25, 28, 30, 33, 35, 37, 40};
+
+static const uint8_t bytes_to_unsigned_dec_digits[] =
+{0, 3, 5, 8, 10, 13, 15, 17, 20, 22, 25, 27, 29, 32, 34, 37, 39};
+
+static const uint8_t bytes_to_hex_digits[] =
+{0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32};
+
+/* Convert enum size_spec to the size of the named type. */
+static const signed char width_bytes[] = {
+ -1,
+ sizeof(char),
+ sizeof(short),
+ sizeof(int),
+ sizeof(long),
+ sizeof(ulonglong_t),
+ sizeof(float),
+ sizeof(double),
+ sizeof(longdouble_t)
+};
+
+/* Ensure that for each member of 'enum size_spec' there is an
+ initializer in the width_bytes array. */
+struct dummy {
+ int assert_width_bytes_matches_size_spec_decl
+ [sizeof width_bytes / sizeof width_bytes[0] == N_SIZE_SPECS ? 1 : -1];
+};
+
+static size_t string_min;
+static int flag_dump_strings;
+
+/* Non-zero if an old-style 'pseudo-address' was specified. */
+static int flag_pseudo_start;
+
+/* The difference between the old-style pseudo starting address and
+ the number of bytes to skip. */
+static off_t pseudo_offset;
+
+/* Function that accepts an address and an optional following char,
+ and prints the address and char to stdout. */
+static void (*format_address) (off_t, char);
+
+/* The number of input bytes to skip before formatting and writing. */
+static off_t n_bytes_to_skip; // = 0;
+
+/* When zero, MAX_BYTES_TO_FORMAT and END_OFFSET are ignored, and all
+ input is formatted. */
+static int limit_bytes_to_format; // = 0;
+
+/* The maximum number of bytes that will be formatted. */
+static off_t max_bytes_to_format;
+
+/* The offset of the first byte after the last byte to be formatted. */
+static off_t end_offset;
+
+/* When nonzero and two or more consecutive blocks are equal, format
+ only the first block and output an asterisk alone on the following
+ line to indicate that identical blocks have been elided. */
+static int abbreviate_duplicate_blocks = 1;
+
+/* An array of specs describing how to format each input block. */
+static size_t n_specs;
+static struct tspec *spec;
+
+/* The number of input bytes formatted per output line. It must be
+ a multiple of the least common multiple of the sizes associated with
+ the specified output types. It should be as large as possible, but
+ no larger than 16 -- unless specified with the -w option. */
+static size_t bytes_per_block;
+
+/* Human-readable representation of *file_list (for error messages).
+ It differs from *file_list only when *file_list is "-". */
+static char const *input_filename;
+
+/* A NULL-terminated list of the file-arguments from the command line. */
+static char const *const *file_list;
+
+/* Initializer for file_list if no file-arguments
+ were specified on the command line. */
+static char const *const default_file_list[] = { "-", NULL };
+
+/* The input stream associated with the current file. */
+static FILE *in_stream;
+
+static int ioerror;
+
+#define MAX_INTEGRAL_TYPE_SIZE sizeof(ulonglong_t)
+static unsigned char integral_type_size[MAX_INTEGRAL_TYPE_SIZE + 1] = {
+ [sizeof(char)] = CHAR,
+#if USHRT_MAX != UCHAR_MAX
+ [sizeof(short)] = SHORT,
+#endif
+#if UINT_MAX != USHRT_MAX
+ [sizeof(int)] = INT,
+#endif
+#if ULONG_MAX != UINT_MAX
+ [sizeof(long)] = LONG,
+#endif
+#if ULLONG_MAX != ULONG_MAX
+ [sizeof(ulonglong_t)] = LONG_LONG,
+#endif
+};
+
+#define MAX_FP_TYPE_SIZE sizeof(longdouble_t)
+static unsigned char fp_type_size[MAX_FP_TYPE_SIZE + 1] = {
+ /* gcc seems to allow repeated indexes. Last one stays */
+ [sizeof(longdouble_t)] = FLOAT_LONG_DOUBLE,
+ [sizeof(double)] = FLOAT_DOUBLE,
+ [sizeof(float)] = FLOAT_SINGLE,
+};
+
+
+static unsigned
+gcd(unsigned u, unsigned v)
+{
+ unsigned t;
+ while (v != 0) {
+ t = u % v;
+ u = v;
+ v = t;
+ }
+ return u;
+}
+
+/* Compute the least common multiple of U and V. */
+static unsigned
+lcm(unsigned u, unsigned v) {
+ unsigned t = gcd(u, v);
+ if (t == 0)
+ return 0;
+ return u * v / t;
+}
+
+static void
+print_s_char(size_t n_bytes, const char *block, const char *fmt_string)
+{
+ while (n_bytes--) {
+ int tmp = *(signed char *) block;
+ printf(fmt_string, tmp);
+ block += sizeof(unsigned char);
+ }
+}
+
+static void
+print_char(size_t n_bytes, const char *block, const char *fmt_string)
+{
+ while (n_bytes--) {
+ unsigned tmp = *(unsigned char *) block;
+ printf(fmt_string, tmp);
+ block += sizeof(unsigned char);
+ }
+}
+
+static void
+print_s_short(size_t n_bytes, const char *block, const char *fmt_string)
+{
+ n_bytes /= sizeof(signed short);
+ while (n_bytes--) {
+ int tmp = *(signed short *) block;
+ printf(fmt_string, tmp);
+ block += sizeof(unsigned short);
+ }
+}
+
+static void
+print_short(size_t n_bytes, const char *block, const char *fmt_string)
+{
+ n_bytes /= sizeof(unsigned short);
+ while (n_bytes--) {
+ unsigned tmp = *(unsigned short *) block;
+ printf(fmt_string, tmp);
+ block += sizeof(unsigned short);
+ }
+}
+
+static void
+print_int(size_t n_bytes, const char *block, const char *fmt_string)
+{
+ n_bytes /= sizeof(unsigned);
+ while (n_bytes--) {
+ unsigned tmp = *(unsigned *) block;
+ printf(fmt_string, tmp);
+ block += sizeof(unsigned);
+ }
+}
+
+#if UINT_MAX == ULONG_MAX
+# define print_long print_int
+#else
+static void
+print_long(size_t n_bytes, const char *block, const char *fmt_string)
+{
+ n_bytes /= sizeof(unsigned long);
+ while (n_bytes--) {
+ unsigned long tmp = *(unsigned long *) block;
+ printf(fmt_string, tmp);
+ block += sizeof(unsigned long);
+ }
+}
+#endif
+
+#if ULONG_MAX == ULLONG_MAX
+# define print_long_long print_long
+#else
+static void
+print_long_long(size_t n_bytes, const char *block, const char *fmt_string)
+{
+ n_bytes /= sizeof(ulonglong_t);
+ while (n_bytes--) {
+ ulonglong_t tmp = *(ulonglong_t *) block;
+ printf(fmt_string, tmp);
+ block += sizeof(ulonglong_t);
+ }
+}
+#endif
+
+static void
+print_float(size_t n_bytes, const char *block, const char *fmt_string)
+{
+ n_bytes /= sizeof(float);
+ while (n_bytes--) {
+ float tmp = *(float *) block;
+ printf(fmt_string, tmp);
+ block += sizeof(float);
+ }
+}
+
+static void
+print_double(size_t n_bytes, const char *block, const char *fmt_string)
+{
+ n_bytes /= sizeof(double);
+ while (n_bytes--) {
+ double tmp = *(double *) block;
+ printf(fmt_string, tmp);
+ block += sizeof(double);
+ }
+}
+
+static void
+print_long_double(size_t n_bytes, const char *block, const char *fmt_string)
+{
+ n_bytes /= sizeof(longdouble_t);
+ while (n_bytes--) {
+ longdouble_t tmp = *(longdouble_t *) block;
+ printf(fmt_string, tmp);
+ block += sizeof(longdouble_t);
+ }
+}
+
+/* print_[named]_ascii are optimized for speed.
+ * Remember, someday you may want to pump gigabytes thru this thing.
+ * Saving a dozen of .text bytes here is counter-productive */
+
+static void
+print_named_ascii(size_t n_bytes, const char *block,
+ const char *unused_fmt_string ATTRIBUTE_UNUSED)
+{
+ /* Names for some non-printing characters. */
+ static const char charname[33][3] = {
+ "nul", "soh", "stx", "etx", "eot", "enq", "ack", "bel",
+ " bs", " ht", " nl", " vt", " ff", " cr", " so", " si",
+ "dle", "dc1", "dc2", "dc3", "dc4", "nak", "syn", "etb",
+ "can", " em", "sub", "esc", " fs", " gs", " rs", " us",
+ " sp"
+ };
+ // buf[N] pos: 01234 56789
+ char buf[12] = " x\0 0xx\0";
+ // actually " x\0 xxx\0", but I want to share the string with below.
+ // [12] because we take three 32bit stack slots anyway, and
+ // gcc is too dumb to initialize with constant stores,
+ // it copies initializer from rodata. Oh well.
+
+ while (n_bytes--) {
+ unsigned masked_c = *(unsigned char *) block++;
+
+ masked_c &= 0x7f;
+ if (masked_c == 0x7f) {
+ fputs(" del", stdout);
+ continue;
+ }
+ if (masked_c > ' ') {
+ buf[3] = masked_c;
+ fputs(buf, stdout);
+ continue;
+ }
+ /* Why? Because printf(" %3.3s") is much slower... */
+ buf[6] = charname[masked_c][0];
+ buf[7] = charname[masked_c][1];
+ buf[8] = charname[masked_c][2];
+ fputs(buf+5, stdout);
+ }
+}
+
+static void
+print_ascii(size_t n_bytes, const char *block,
+ const char *unused_fmt_string ATTRIBUTE_UNUSED)
+{
+ // buf[N] pos: 01234 56789
+ char buf[12] = " x\0 0xx\0";
+
+ while (n_bytes--) {
+ const char *s;
+ unsigned c = *(unsigned char *) block++;
+
+ if (ISPRINT(c)) {
+ buf[3] = c;
+ fputs(buf, stdout);
+ continue;
+ }
+ switch (c) {
+ case '\0':
+ s = " \\0";
+ break;
+ case '\007':
+ s = " \\a";
+ break;
+ case '\b':
+ s = " \\b";
+ break;
+ case '\f':
+ s = " \\f";
+ break;
+ case '\n':
+ s = " \\n";
+ break;
+ case '\r':
+ s = " \\r";
+ break;
+ case '\t':
+ s = " \\t";
+ break;
+ case '\v':
+ s = " \\v";
+ break;
+ case '\x7f':
+ s = " 177";
+ break;
+ default: /* c is never larger than 040 */
+ buf[7] = (c >> 3) + '0';
+ buf[8] = (c & 7) + '0';
+ s = buf + 5;
+ }
+ fputs(s, stdout);
+ }
+}
+
+/* Given a list of one or more input filenames FILE_LIST, set the global
+ file pointer IN_STREAM and the global string INPUT_FILENAME to the
+ first one that can be successfully opened. Modify FILE_LIST to
+ reference the next filename in the list. A file name of "-" is
+ interpreted as standard input. If any file open fails, give an error
+ message and return nonzero. */
+
+static void
+open_next_file(void)
+{
+ while (1) {
+ input_filename = *file_list;
+ if (!input_filename)
+ return;
+ file_list++;
+ in_stream = fopen_or_warn_stdin(input_filename);
+ if (in_stream) {
+ if (in_stream == stdin)
+ input_filename = bb_msg_standard_input;
+ break;
+ }
+ ioerror = 1;
+ }
+
+ if (limit_bytes_to_format && !flag_dump_strings)
+ setbuf(in_stream, NULL);
+}
+
+/* Test whether there have been errors on in_stream, and close it if
+ it is not standard input. Return nonzero if there has been an error
+ on in_stream or stdout; return zero otherwise. This function will
+ report more than one error only if both a read and a write error
+ have occurred. IN_ERRNO, if nonzero, is the error number
+ corresponding to the most recent action for IN_STREAM. */
+
+static void
+check_and_close(void)
+{
+ if (in_stream) {
+ if (ferror(in_stream)) {
+ bb_error_msg("%s: read error", input_filename);
+ ioerror = 1;
+ }
+ fclose_if_not_stdin(in_stream);
+ in_stream = NULL;
+ }
+
+ if (ferror(stdout)) {
+ bb_error_msg("write error");
+ ioerror = 1;
+ }
+}
+
+/* If S points to a single valid modern od format string, put
+ a description of that format in *TSPEC, make *NEXT point at the
+ character following the just-decoded format (if *NEXT is non-NULL),
+ and return zero. For example, if S were "d4afL"
+ *NEXT would be set to "afL" and *TSPEC would be
+ {
+ fmt = SIGNED_DECIMAL;
+ size = INT or LONG; (whichever integral_type_size[4] resolves to)
+ print_function = print_int; (assuming size == INT)
+ fmt_string = "%011d%c";
+ }
+ S_ORIG is solely for reporting errors. It should be the full format
+ string argument. */
+
+static void
+decode_one_format(const char *s_orig, const char *s, const char **next,
+ struct tspec *tspec)
+{
+ enum size_spec size_spec;
+ unsigned size;
+ enum output_format fmt;
+ const char *p;
+ char *end;
+ char *fmt_string = NULL;
+ void (*print_function) (size_t, const char *, const char *);
+ unsigned c;
+ unsigned field_width = 0;
+ int pos;
+
+ assert(tspec != NULL);
+
+ switch (*s) {
+ case 'd':
+ case 'o':
+ case 'u':
+ case 'x': {
+ static const char CSIL[] = "CSIL";
+
+ c = *s++;
+ p = strchr(CSIL, *s);
+ if (!p) {
+ size = sizeof(int);
+ if (isdigit(s[0])) {
+ size = bb_strtou(s, &end, 0);
+ if (errno == ERANGE
+ || MAX_INTEGRAL_TYPE_SIZE < size
+ || integral_type_size[size] == NO_SIZE
+ ) {
+ bb_error_msg_and_die("invalid type string '%s'; "
+ "%u-byte %s type is not supported",
+ s_orig, size, "integral");
+ }
+ s = end;
+ }
+ } else {
+ static const uint8_t CSIL_sizeof[] = {
+ sizeof(char),
+ sizeof(short),
+ sizeof(int),
+ sizeof(long),
+ };
+ size = CSIL_sizeof[p - CSIL];
+ }
+
+#define ISPEC_TO_FORMAT(Spec, Min_format, Long_format, Max_format) \
+ ((Spec) == LONG_LONG ? (Max_format) \
+ : ((Spec) == LONG ? (Long_format) : (Min_format)))
+
+#define FMT_BYTES_ALLOCATED 9
+ size_spec = integral_type_size[size];
+
+ {
+ static const char doux[] = "doux";
+ static const char doux_fmt_letter[][4] = {
+ "lld", "llo", "llu", "llx"
+ };
+ static const enum output_format doux_fmt[] = {
+ SIGNED_DECIMAL,
+ OCTAL,
+ UNSIGNED_DECIMAL,
+ HEXADECIMAL,
+ };
+ static const uint8_t *const doux_bytes_to_XXX[] = {
+ bytes_to_signed_dec_digits,
+ bytes_to_oct_digits,
+ bytes_to_unsigned_dec_digits,
+ bytes_to_hex_digits,
+ };
+ static const char doux_fmtstring[][sizeof(" %%0%u%s")] = {
+ " %%%u%s",
+ " %%0%u%s",
+ " %%%u%s",
+ " %%0%u%s",
+ };
+
+ pos = strchr(doux, c) - doux;
+ fmt = doux_fmt[pos];
+ field_width = doux_bytes_to_XXX[pos][size];
+ p = doux_fmt_letter[pos] + 2;
+ if (size_spec == LONG) p--;
+ if (size_spec == LONG_LONG) p -= 2;
+ fmt_string = xasprintf(doux_fmtstring[pos], field_width, p);
+ }
+
+ switch (size_spec) {
+ case CHAR:
+ print_function = (fmt == SIGNED_DECIMAL
+ ? print_s_char
+ : print_char);
+ break;
+ case SHORT:
+ print_function = (fmt == SIGNED_DECIMAL
+ ? print_s_short
+ : print_short);
+ break;
+ case INT:
+ print_function = print_int;
+ break;
+ case LONG:
+ print_function = print_long;
+ break;
+ default: /* case LONG_LONG: */
+ print_function = print_long_long;
+ break;
+ }
+ break;
+ }
+
+ case 'f': {
+ static const char FDL[] = "FDL";
+
+ fmt = FLOATING_POINT;
+ ++s;
+ p = strchr(FDL, *s);
+ if (!p) {
+ size = sizeof(double);
+ if (isdigit(s[0])) {
+ size = bb_strtou(s, &end, 0);
+ if (errno == ERANGE || size > MAX_FP_TYPE_SIZE
+ || fp_type_size[size] == NO_SIZE
+ ) {
+ bb_error_msg_and_die("invalid type string '%s'; "
+ "%u-byte %s type is not supported",
+ s_orig, size, "floating point");
+ }
+ s = end;
+ }
+ } else {
+ static const uint8_t FDL_sizeof[] = {
+ sizeof(float),
+ sizeof(double),
+ sizeof(longdouble_t),
+ };
+
+ size = FDL_sizeof[p - FDL];
+ }
+
+ size_spec = fp_type_size[size];
+
+ switch (size_spec) {
+ case FLOAT_SINGLE:
+ print_function = print_float;
+ field_width = FLT_DIG + 8;
+ /* Don't use %#e; not all systems support it. */
+ fmt_string = xasprintf(" %%%d.%de", field_width, FLT_DIG);
+ break;
+ case FLOAT_DOUBLE:
+ print_function = print_double;
+ field_width = DBL_DIG + 8;
+ fmt_string = xasprintf(" %%%d.%de", field_width, DBL_DIG);
+ break;
+ default: /* case FLOAT_LONG_DOUBLE: */
+ print_function = print_long_double;
+ field_width = LDBL_DIG + 8;
+ fmt_string = xasprintf(" %%%d.%dLe", field_width, LDBL_DIG);
+ break;
+ }
+ break;
+ }
+
+ case 'a':
+ ++s;
+ fmt = NAMED_CHARACTER;
+ size_spec = CHAR;
+ print_function = print_named_ascii;
+ field_width = 3;
+ break;
+ case 'c':
+ ++s;
+ fmt = CHARACTER;
+ size_spec = CHAR;
+ print_function = print_ascii;
+ field_width = 3;
+ break;
+ default:
+ bb_error_msg_and_die("invalid character '%c' "
+ "in type string '%s'", *s, s_orig);
+ }
+
+ tspec->size = size_spec;
+ tspec->fmt = fmt;
+ tspec->print_function = print_function;
+ tspec->fmt_string = fmt_string;
+
+ tspec->field_width = field_width;
+ tspec->hexl_mode_trailer = (*s == 'z');
+ if (tspec->hexl_mode_trailer)
+ s++;
+
+ if (next != NULL)
+ *next = s;
+}
+
+/* Decode the modern od format string S. Append the decoded
+ representation to the global array SPEC, reallocating SPEC if
+ necessary. Return zero if S is valid, nonzero otherwise. */
+
+static void
+decode_format_string(const char *s)
+{
+ const char *s_orig = s;
+
+ while (*s != '\0') {
+ struct tspec tspec;
+ const char *next;
+
+ decode_one_format(s_orig, s, &next, &tspec);
+
+ assert(s != next);
+ s = next;
+ n_specs++;
+ spec = xrealloc(spec, n_specs * sizeof(*spec));
+ memcpy(&spec[n_specs-1], &tspec, sizeof *spec);
+ }
+}
+
+/* Given a list of one or more input filenames FILE_LIST, set the global
+ file pointer IN_STREAM to position N_SKIP in the concatenation of
+ those files. If any file operation fails or if there are fewer than
+ N_SKIP bytes in the combined input, give an error message and return
+ nonzero. When possible, use seek rather than read operations to
+ advance IN_STREAM. */
+
+static void
+skip(off_t n_skip)
+{
+ if (n_skip == 0)
+ return;
+
+ while (in_stream) { /* !EOF */
+ struct stat file_stats;
+
+ /* First try seeking. For large offsets, this extra work is
+ worthwhile. If the offset is below some threshold it may be
+ more efficient to move the pointer by reading. There are two
+ issues when trying to seek:
+ - the file must be seekable.
+ - before seeking to the specified position, make sure
+ that the new position is in the current file.
+ Try to do that by getting file's size using fstat.
+ But that will work only for regular files. */
+
+ /* The st_size field is valid only for regular files
+ (and for symbolic links, which cannot occur here).
+ If the number of bytes left to skip is at least
+ as large as the size of the current file, we can
+ decrement n_skip and go on to the next file. */
+ if (fstat(fileno(in_stream), &file_stats) == 0
+ && S_ISREG(file_stats.st_mode) && file_stats.st_size >= 0
+ ) {
+ if (file_stats.st_size < n_skip) {
+ n_skip -= file_stats.st_size;
+ /* take check&close / open_next route */
+ } else {
+ if (fseeko(in_stream, n_skip, SEEK_CUR) != 0)
+ ioerror = 1;
+ return;
+ }
+ } else {
+ /* If it's not a regular file with nonnegative size,
+ position the file pointer by reading. */
+ char buf[BUFSIZ];
+ size_t n_bytes_read, n_bytes_to_read = BUFSIZ;
+
+ while (n_skip > 0) {
+ if (n_skip < n_bytes_to_read)
+ n_bytes_to_read = n_skip;
+ n_bytes_read = fread(buf, 1, n_bytes_to_read, in_stream);
+ n_skip -= n_bytes_read;
+ if (n_bytes_read != n_bytes_to_read)
+ break; /* EOF on this file or error */
+ }
+ }
+ if (n_skip == 0)
+ return;
+
+ check_and_close();
+ open_next_file();
+ }
+
+ if (n_skip)
+ bb_error_msg_and_die("cannot skip past end of combined input");
+}
+
+
+typedef void FN_format_address(off_t address, char c);
+
+static void
+format_address_none(off_t address ATTRIBUTE_UNUSED, char c ATTRIBUTE_UNUSED)
+{
+}
+
+static char address_fmt[] = "%0n"OFF_FMT"xc";
+/* Corresponds to 'x' above */
+#define address_base_char address_fmt[sizeof(address_fmt)-3]
+/* Corresponds to 'n' above */
+#define address_pad_len_char address_fmt[2]
+
+static void
+format_address_std(off_t address, char c)
+{
+ /* Corresponds to 'c' */
+ address_fmt[sizeof(address_fmt)-2] = c;
+ printf(address_fmt, address);
+}
+
+#if ENABLE_GETOPT_LONG
+/* only used with --traditional */
+static void
+format_address_paren(off_t address, char c)
+{
+ putchar('(');
+ format_address_std(address, ')');
+ if (c) putchar(c);
+}
+
+static void
+format_address_label(off_t address, char c)
+{
+ format_address_std(address, ' ');
+ format_address_paren(address + pseudo_offset, c);
+}
+#endif
+
+static void
+dump_hexl_mode_trailer(size_t n_bytes, const char *block)
+{
+ fputs(" >", stdout);
+ while (n_bytes--) {
+ unsigned c = *(unsigned char *) block++;
+ c = (ISPRINT(c) ? c : '.');
+ putchar(c);
+ }
+ putchar('<');
+}
+
+/* Write N_BYTES bytes from CURR_BLOCK to standard output once for each
+ of the N_SPEC format specs. CURRENT_OFFSET is the byte address of
+ CURR_BLOCK in the concatenation of input files, and it is printed
+ (optionally) only before the output line associated with the first
+ format spec. When duplicate blocks are being abbreviated, the output
+ for a sequence of identical input blocks is the output for the first
+ block followed by an asterisk alone on a line. It is valid to compare
+ the blocks PREV_BLOCK and CURR_BLOCK only when N_BYTES == BYTES_PER_BLOCK.
+ That condition may be false only for the last input block -- and then
+ only when it has not been padded to length BYTES_PER_BLOCK. */
+
+static void
+write_block(off_t current_offset, size_t n_bytes,
+ const char *prev_block, const char *curr_block)
+{
+ static char first = 1;
+ static char prev_pair_equal = 0;
+ size_t i;
+
+ if (abbreviate_duplicate_blocks
+ && !first
+ && n_bytes == bytes_per_block
+ && memcmp(prev_block, curr_block, bytes_per_block) == 0
+ ) {
+ if (prev_pair_equal) {
+ /* The two preceding blocks were equal, and the current
+ block is the same as the last one, so print nothing. */
+ } else {
+ puts("*");
+ prev_pair_equal = 1;
+ }
+ } else {
+ first = 0;
+ prev_pair_equal = 0;
+ for (i = 0; i < n_specs; i++) {
+ if (i == 0)
+ format_address(current_offset, '\0');
+ else
+ printf("%*s", address_pad_len_char - '0', "");
+ (*spec[i].print_function) (n_bytes, curr_block, spec[i].fmt_string);
+ if (spec[i].hexl_mode_trailer) {
+ /* space-pad out to full line width, then dump the trailer */
+ int datum_width = width_bytes[spec[i].size];
+ int blank_fields = (bytes_per_block - n_bytes) / datum_width;
+ int field_width = spec[i].field_width + 1;
+ printf("%*s", blank_fields * field_width, "");
+ dump_hexl_mode_trailer(n_bytes, curr_block);
+ }
+ putchar('\n');
+ }
+ }
+}
+
+static void
+read_block(size_t n, char *block, size_t *n_bytes_in_buffer)
+{
+ assert(0 < n && n <= bytes_per_block);
+
+ *n_bytes_in_buffer = 0;
+
+ if (n == 0)
+ return;
+
+ while (in_stream != NULL) { /* EOF. */
+ size_t n_needed;
+ size_t n_read;
+
+ n_needed = n - *n_bytes_in_buffer;
+ n_read = fread(block + *n_bytes_in_buffer, 1, n_needed, in_stream);
+ *n_bytes_in_buffer += n_read;
+ if (n_read == n_needed)
+ break;
+ /* error check is done in check_and_close */
+ check_and_close();
+ open_next_file();
+ }
+}
+
+/* Return the least common multiple of the sizes associated
+ with the format specs. */
+
+static int
+get_lcm(void)
+{
+ size_t i;
+ int l_c_m = 1;
+
+ for (i = 0; i < n_specs; i++)
+ l_c_m = lcm(l_c_m, width_bytes[(int) spec[i].size]);
+ return l_c_m;
+}
+
+#if ENABLE_GETOPT_LONG
+/* If S is a valid traditional offset specification with an optional
+ leading '+' return nonzero and set *OFFSET to the offset it denotes. */
+
+static int
+parse_old_offset(const char *s, off_t *offset)
+{
+ static const struct suffix_mult Bb[] = {
+ { "B", 1024 },
+ { "b", 512 },
+ { NULL, 0 }
+ };
+ char *p;
+ int radix;
+
+ /* Skip over any leading '+'. */
+ if (s[0] == '+') ++s;
+
+ /* Determine the radix we'll use to interpret S. If there is a '.',
+ * it's decimal, otherwise, if the string begins with '0X'or '0x',
+ * it's hexadecimal, else octal. */
+ p = strchr(s, '.');
+ radix = 8;
+ if (p) {
+ p[0] = '\0'; /* cheating */
+ radix = 10;
+ } else if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X'))
+ radix = 16;
+
+ *offset = xstrtooff_sfx(s, radix, Bb);
+ if (p) p[0] = '.';
+
+ return (*offset >= 0);
+}
+#endif
+
+/* Read a chunk of size BYTES_PER_BLOCK from the input files, write the
+ formatted block to standard output, and repeat until the specified
+ maximum number of bytes has been read or until all input has been
+ processed. If the last block read is smaller than BYTES_PER_BLOCK
+ and its size is not a multiple of the size associated with a format
+ spec, extend the input block with zero bytes until its length is a
+ multiple of all format spec sizes. Write the final block. Finally,
+ write on a line by itself the offset of the byte after the last byte
+ read. Accumulate return values from calls to read_block and
+ check_and_close, and if any was nonzero, return nonzero.
+ Otherwise, return zero. */
+
+static void
+dump(void)
+{
+ char *block[2];
+ off_t current_offset;
+ int idx;
+ size_t n_bytes_read;
+
+ block[0] = xmalloc(2*bytes_per_block);
+ block[1] = block[0] + bytes_per_block;
+
+ current_offset = n_bytes_to_skip;
+
+ idx = 0;
+ if (limit_bytes_to_format) {
+ while (1) {
+ size_t n_needed;
+ if (current_offset >= end_offset) {
+ n_bytes_read = 0;
+ break;
+ }
+ n_needed = MIN(end_offset - current_offset,
+ (off_t) bytes_per_block);
+ read_block(n_needed, block[idx], &n_bytes_read);
+ if (n_bytes_read < bytes_per_block)
+ break;
+ assert(n_bytes_read == bytes_per_block);
+ write_block(current_offset, n_bytes_read,
+ block[!idx], block[idx]);
+ current_offset += n_bytes_read;
+ idx = !idx;
+ }
+ } else {
+ while (1) {
+ read_block(bytes_per_block, block[idx], &n_bytes_read);
+ if (n_bytes_read < bytes_per_block)
+ break;
+ assert(n_bytes_read == bytes_per_block);
+ write_block(current_offset, n_bytes_read,
+ block[!idx], block[idx]);
+ current_offset += n_bytes_read;
+ idx = !idx;
+ }
+ }
+
+ if (n_bytes_read > 0) {
+ int l_c_m;
+ size_t bytes_to_write;
+
+ l_c_m = get_lcm();
+
+ /* Make bytes_to_write the smallest multiple of l_c_m that
+ is at least as large as n_bytes_read. */
+ bytes_to_write = l_c_m * ((n_bytes_read + l_c_m - 1) / l_c_m);
+
+ memset(block[idx] + n_bytes_read, 0, bytes_to_write - n_bytes_read);
+ write_block(current_offset, bytes_to_write,
+ block[!idx], block[idx]);
+ current_offset += n_bytes_read;
+ }
+
+ format_address(current_offset, '\n');
+
+ if (limit_bytes_to_format && current_offset >= end_offset)
+ check_and_close();
+
+ free(block[0]);
+}
+
+/* Read a single byte into *C from the concatenation of the input files
+ named in the global array FILE_LIST. On the first call to this
+ function, the global variable IN_STREAM is expected to be an open
+ stream associated with the input file INPUT_FILENAME. If IN_STREAM
+ is at end-of-file, close it and update the global variables IN_STREAM
+ and INPUT_FILENAME so they correspond to the next file in the list.
+ Then try to read a byte from the newly opened file. Repeat if
+ necessary until EOF is reached for the last file in FILE_LIST, then
+ set *C to EOF and return. Subsequent calls do likewise. The return
+ value is nonzero if any errors occured, zero otherwise. */
+
+static void
+read_char(int *c)
+{
+ while (in_stream) { /* !EOF */
+ *c = fgetc(in_stream);
+ if (*c != EOF)
+ return;
+ check_and_close();
+ open_next_file();
+ }
+ *c = EOF;
+}
+
+/* Read N bytes into BLOCK from the concatenation of the input files
+ named in the global array FILE_LIST. On the first call to this
+ function, the global variable IN_STREAM is expected to be an open
+ stream associated with the input file INPUT_FILENAME. If all N
+ bytes cannot be read from IN_STREAM, close IN_STREAM and update
+ the global variables IN_STREAM and INPUT_FILENAME. Then try to
+ read the remaining bytes from the newly opened file. Repeat if
+ necessary until EOF is reached for the last file in FILE_LIST.
+ On subsequent calls, don't modify BLOCK and return zero. Set
+ *N_BYTES_IN_BUFFER to the number of bytes read. If an error occurs,
+ it will be detected through ferror when the stream is about to be
+ closed. If there is an error, give a message but continue reading
+ as usual and return nonzero. Otherwise return zero. */
+
+/* STRINGS mode. Find each "string constant" in the input.
+ A string constant is a run of at least 'string_min' ASCII
+ graphic (or formatting) characters terminated by a null.
+ Based on a function written by Richard Stallman for a
+ traditional version of od. Return nonzero if an error
+ occurs. Otherwise, return zero. */
+
+static void
+dump_strings(void)
+{
+ size_t bufsize = MAX(100, string_min);
+ char *buf = xmalloc(bufsize);
+ off_t address = n_bytes_to_skip;
+
+ while (1) {
+ size_t i;
+ int c;
+
+ /* See if the next 'string_min' chars are all printing chars. */
+ tryline:
+ if (limit_bytes_to_format && (end_offset - string_min <= address))
+ break;
+ i = 0;
+ while (!limit_bytes_to_format || address < end_offset) {
+ if (i == bufsize) {
+ bufsize += bufsize/8;
+ buf = xrealloc(buf, bufsize);
+ }
+ read_char(&c);
+ if (c < 0) { /* EOF */
+ free(buf);
+ return;
+ }
+ address++;
+ if (!c)
+ break;
+ if (!ISPRINT(c))
+ goto tryline; /* It isn't; give up on this string. */
+ buf[i++] = c; /* String continues; store it all. */
+ }
+
+ if (i < string_min) /* Too short! */
+ goto tryline;
+
+ /* If we get here, the string is all printable and null-terminated,
+ * so print it. It is all in 'buf' and 'i' is its length. */
+ buf[i] = 0;
+ format_address(address - i - 1, ' ');
+
+ for (i = 0; (c = buf[i]); i++) {
+ switch (c) {
+ case '\007': fputs("\\a", stdout); break;
+ case '\b': fputs("\\b", stdout); break;
+ case '\f': fputs("\\f", stdout); break;
+ case '\n': fputs("\\n", stdout); break;
+ case '\r': fputs("\\r", stdout); break;
+ case '\t': fputs("\\t", stdout); break;
+ case '\v': fputs("\\v", stdout); break;
+ default: putc(c, stdout);
+ }
+ }
+ putchar('\n');
+ }
+
+ /* We reach this point only if we search through
+ (max_bytes_to_format - string_min) bytes before reaching EOF. */
+ free(buf);
+
+ check_and_close();
+}
+
+int od_main(int argc, char **argv);
+int od_main(int argc, char **argv)
+{
+ static const struct suffix_mult bkm[] = {
+ { "b", 512 },
+ { "k", 1024 },
+ { "m", 1024*1024 },
+ { NULL, 0 }
+ };
+ unsigned opt;
+ int l_c_m;
+ /* The old-style 'pseudo starting address' to be printed in parentheses
+ after any true address. */
+ off_t pseudo_start = 0; // only for gcc
+ enum {
+ OPT_A = 1 << 0,
+ OPT_N = 1 << 1,
+ OPT_a = 1 << 2,
+ OPT_b = 1 << 3,
+ OPT_c = 1 << 4,
+ OPT_d = 1 << 5,
+ OPT_f = 1 << 6,
+ OPT_h = 1 << 7,
+ OPT_i = 1 << 8,
+ OPT_j = 1 << 9,
+ OPT_l = 1 << 10,
+ OPT_o = 1 << 11,
+ OPT_t = 1 << 12,
+ OPT_v = 1 << 13,
+ OPT_x = 1 << 14,
+ OPT_s = 1 << 15,
+ OPT_S = 1 << 16,
+ OPT_w = 1 << 17,
+ OPT_traditional = (1 << 18) * ENABLE_GETOPT_LONG,
+ };
+#if ENABLE_GETOPT_LONG
+ static const struct option long_options[] = {
+ { "skip-bytes", required_argument, NULL, 'j' },
+ { "address-radix", required_argument, NULL, 'A' },
+ { "read-bytes", required_argument, NULL, 'N' },
+ { "format", required_argument, NULL, 't' },
+ { "output-duplicates", no_argument, NULL, 'v' },
+ { "strings", optional_argument, NULL, 'S' },
+ { "width", optional_argument, NULL, 'w' },
+ { "traditional", no_argument, NULL, 0xff },
+ { NULL, 0, NULL, 0 }
+ };
+#endif
+ char *str_A, *str_N, *str_j, *str_S;
+ char *str_w = NULL;
+ llist_t *lst_t = NULL;
+
+ spec = NULL;
+ format_address = format_address_std;
+ address_base_char = 'o';
+ address_pad_len_char = '7';
+ flag_dump_strings = 0;
+
+ /* Parse command line */
+ opt_complementary = "t::"; // list
+#if ENABLE_GETOPT_LONG
+ applet_long_options = long_options;
+#endif
+ opt = getopt32(argc, argv, "A:N:abcdfhij:lot:vxsS:"
+ "w::", // -w with optional param
+ // -S was -s and also had optional parameter
+ // but in coreutils 6.3 it was renamed and now has
+ // _mandatory_ parameter
+ &str_A, &str_N, &str_j, &lst_t, &str_S, &str_w);
+ argc -= optind;
+ argv += optind;
+ if (opt & OPT_A) {
+ static const char doxn[] = "doxn";
+ static const char doxn_address_base_char[] = {
+ 'u', 'o', 'x', /* '?' fourth one is not important */
+ };
+ static const uint8_t doxn_address_pad_len_char[] = {
+ '7', '7', '6', /* '?' */
+ };
+ char *p;
+ int pos;
+ p = strchr(doxn, str_A[0]);
+ if (!p)
+ bb_error_msg_and_die("bad output address radix "
+ "'%c' (must be [doxn])", str_A[0]);
+ pos = p - doxn;
+ if (pos == 3) format_address = format_address_none;
+ address_base_char = doxn_address_base_char[pos];
+ address_pad_len_char = doxn_address_pad_len_char[pos];
+ }
+ if (opt & OPT_N) {
+ limit_bytes_to_format = 1;
+ max_bytes_to_format = xstrtooff_sfx(str_N, 0, bkm);
+ }
+ if (opt & OPT_a) decode_format_string("a");
+ if (opt & OPT_b) decode_format_string("oC");
+ if (opt & OPT_c) decode_format_string("c");
+ if (opt & OPT_d) decode_format_string("u2");
+ if (opt & OPT_f) decode_format_string("fF");
+ if (opt & OPT_h) decode_format_string("x2");
+ if (opt & OPT_i) decode_format_string("d2");
+ if (opt & OPT_j) n_bytes_to_skip = xstrtooff_sfx(str_j, 0, bkm);
+ if (opt & OPT_l) decode_format_string("d4");
+ if (opt & OPT_o) decode_format_string("o2");
+ //if (opt & OPT_t)...
+ lst_t = llist_rev(lst_t);
+ while (lst_t) {
+ decode_format_string(lst_t->data);
+ lst_t = lst_t->link;
+ }
+ if (opt & OPT_v) abbreviate_duplicate_blocks = 0;
+ if (opt & OPT_x) decode_format_string("x2");
+ if (opt & OPT_s) decode_format_string("d2");
+ if (opt & OPT_S) {
+ string_min = 3;
+ string_min = xstrtou_sfx(str_S, 0, bkm);
+ flag_dump_strings = 1;
+ }
+ //if (opt & OPT_w)...
+ //if (opt & OPT_traditional)...
+
+ if (flag_dump_strings && n_specs > 0)
+ bb_error_msg_and_die("no type may be specified when dumping strings");
+
+ /* If the --traditional option is used, there may be from
+ * 0 to 3 remaining command line arguments; handle each case
+ * separately.
+ * od [file] [[+]offset[.][b] [[+]label[.][b]]]
+ * The offset and pseudo_start have the same syntax.
+ *
+ * FIXME: POSIX 1003.1-2001 with XSI requires support for the
+ * traditional syntax even if --traditional is not given. */
+
+#if ENABLE_GETOPT_LONG
+ if (opt & OPT_traditional) {
+ off_t o1, o2;
+
+ if (argc == 1) {
+ if (parse_old_offset(argv[0], &o1)) {
+ n_bytes_to_skip = o1;
+ --argc;
+ ++argv;
+ }
+ } else if (argc == 2) {
+ if (parse_old_offset(argv[0], &o1)
+ && parse_old_offset(argv[1], &o2)
+ ) {
+ n_bytes_to_skip = o1;
+ flag_pseudo_start = 1;
+ pseudo_start = o2;
+ argv += 2;
+ argc -= 2;
+ } else if (parse_old_offset(argv[1], &o2)) {
+ n_bytes_to_skip = o2;
+ --argc;
+ argv[1] = argv[0];
+ ++argv;
+ } else {
+ bb_error_msg_and_die("invalid second operand "
+ "in compatibility mode '%s'", argv[1]);
+ }
+ } else if (argc == 3) {
+ if (parse_old_offset(argv[1], &o1)
+ && parse_old_offset(argv[2], &o2)
+ ) {
+ n_bytes_to_skip = o1;
+ flag_pseudo_start = 1;
+ pseudo_start = o2;
+ argv[2] = argv[0];
+ argv += 2;
+ argc -= 2;
+ } else {
+ bb_error_msg_and_die("in compatibility mode "
+ "the last two arguments must be offsets");
+ }
+ } else if (argc > 3) {
+ bb_error_msg_and_die("compatibility mode supports "
+ "at most three arguments");
+ }
+
+ if (flag_pseudo_start) {
+ if (format_address == format_address_none) {
+ address_base_char = 'o';
+ address_pad_len_char = '7';
+ format_address = format_address_paren;
+ } else
+ format_address = format_address_label;
+ }
+ }
+#endif
+
+ if (limit_bytes_to_format) {
+ end_offset = n_bytes_to_skip + max_bytes_to_format;
+ if (end_offset < n_bytes_to_skip)
+ bb_error_msg_and_die("skip-bytes + read-bytes is too large");
+ }
+
+ if (n_specs == 0) {
+ decode_format_string("o2");
+ n_specs = 1;
+ }
+
+ /* If no files were listed on the command line,
+ set the global pointer FILE_LIST so that it
+ references the null-terminated list of one name: "-". */
+ file_list = default_file_list;
+ if (argc > 0) {
+ /* Set the global pointer FILE_LIST so that it
+ references the first file-argument on the command-line. */
+ file_list = (char const *const *) argv;
+ }
+
+ /* open the first input file */
+ open_next_file();
+ /* skip over any unwanted header bytes */
+ skip(n_bytes_to_skip);
+ if (!in_stream)
+ return 1;
+
+ pseudo_offset = (flag_pseudo_start ? pseudo_start - n_bytes_to_skip : 0);
+
+ /* Compute output block length. */
+ l_c_m = get_lcm();
+
+ if (opt & OPT_w) { /* -w: width */
+ bytes_per_block = 32;
+ if (str_w)
+ bytes_per_block = xatou(str_w);
+ if (!bytes_per_block || bytes_per_block % l_c_m != 0) {
+ bb_error_msg("warning: invalid width %zu; using %d instead",
+ bytes_per_block, l_c_m);
+ bytes_per_block = l_c_m;
+ }
+ } else {
+ bytes_per_block = l_c_m;
+ if (l_c_m < DEFAULT_BYTES_PER_BLOCK)
+ bytes_per_block *= DEFAULT_BYTES_PER_BLOCK / l_c_m;
+ }
+
+#ifdef DEBUG
+ for (i = 0; i < n_specs; i++) {
+ printf("%d: fmt=\"%s\" width=%d\n",
+ i, spec[i].fmt_string, width_bytes[spec[i].size]);
+ }
+#endif
+
+ if (flag_dump_strings)
+ dump_strings();
+ else
+ dump();
+
+ if (fclose(stdin) == EOF)
+ bb_perror_msg_and_die(bb_msg_standard_input);
+
+ return (ioerror != 0); /* err != 0 - return 1 (failure) */
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/printenv.c b/i/pc104/initrd/conf/busybox/coreutils/printenv.c
new file mode 100644
index 0000000..1d41eeb
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/printenv.c
@@ -0,0 +1,42 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * printenv implementation for busybox
+ *
+ * Copyright (C) 2005 by Erik Andersen <andersen@codepoet.org>
+ * Copyright (C) 2005 by Mike Frysinger <vapier@gentoo.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "busybox.h"
+
+int printenv_main(int argc, char **argv);
+int printenv_main(int argc, char **argv)
+{
+ extern char **environ;
+ int e = 0;
+
+ /* no variables specified, show whole env */
+ if (argc == 1)
+ while (environ[e])
+ puts(environ[e++]);
+
+ /* search for specified variables and print them out if found */
+ else {
+ int i;
+ size_t l;
+ char *arg, *env;
+
+ for (i=1; (arg = argv[i]); ++i)
+ for (; (env = environ[e]); ++e) {
+ l = strlen(arg);
+ if (!strncmp(env, arg, l) && env[l] == '=')
+ puts(env + l + 1);
+ }
+ }
+
+ fflush_stdout_and_exit(0);
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/printf.c b/i/pc104/initrd/conf/busybox/coreutils/printf.c
new file mode 100644
index 0000000..a2a82d2
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/printf.c
@@ -0,0 +1,313 @@
+/* vi: set sw=4 ts=4: */
+/* printf - format and print data
+
+ Copyright 1999 Dave Cinege
+ Portions copyright (C) 1990-1996 Free Software Foundation, Inc.
+
+ Licensed under GPL v2 or later, see file LICENSE in this tarball for details.
+*/
+
+/* Usage: printf format [argument...]
+
+ A front end to the printf function that lets it be used from the shell.
+
+ Backslash escapes:
+
+ \" = double quote
+ \\ = backslash
+ \a = alert (bell)
+ \b = backspace
+ \c = produce no further output
+ \f = form feed
+ \n = new line
+ \r = carriage return
+ \t = horizontal tab
+ \v = vertical tab
+ \0ooo = octal number (ooo is 0 to 3 digits)
+ \xhhh = hexadecimal number (hhh is 1 to 3 digits)
+
+ Additional directive:
+
+ %b = print an argument string, interpreting backslash escapes
+
+ The 'format' argument is re-used as many times as necessary
+ to convert all of the given arguments.
+
+ David MacKenzie <djm@gnu.ai.mit.edu> */
+
+
+// 19990508 Busy Boxed! Dave Cinege
+
+#include "busybox.h"
+
+typedef void (*converter)(const char *arg, void *result);
+
+static void multiconvert(const char *arg, void *result, converter convert)
+{
+ char s[sizeof(int)*3 + 2];
+
+ if (*arg == '"' || *arg == '\'') {
+ sprintf(s, "%d", (unsigned char)arg[1]);
+ arg = s;
+ }
+ convert(arg, result);
+ /* if there was conversion error, print unconverted string */
+ if (errno)
+ fputs(arg, stderr);
+}
+
+static void conv_strtoul(const char *arg, void *result)
+{
+ *(unsigned long*)result = bb_strtoul(arg, NULL, 0);
+}
+static void conv_strtol(const char *arg, void *result)
+{
+ *(long*)result = bb_strtol(arg, NULL, 0);
+}
+static void conv_strtod(const char *arg, void *result)
+{
+ char *end;
+ /* Well, this one allows leading whitespace... so what */
+ /* What I like much less is that "-" is accepted too! :( */
+ *(double*)result = strtod(arg, &end);
+ if (end[0]) errno = ERANGE;
+}
+
+static unsigned long my_xstrtoul(const char *arg)
+{
+ unsigned long result;
+ multiconvert(arg, &result, conv_strtoul);
+ return result;
+}
+
+static long my_xstrtol(const char *arg)
+{
+ long result;
+ multiconvert(arg, &result, conv_strtol);
+ return result;
+}
+
+static double my_xstrtod(const char *arg)
+{
+ double result;
+ multiconvert(arg, &result, conv_strtod);
+ return result;
+}
+
+static void print_esc_string(char *str)
+{
+ for (; *str; str++) {
+ if (*str == '\\') {
+ str++;
+ putchar(bb_process_escape_sequence((const char **)&str));
+ } else {
+ putchar(*str);
+ }
+
+ }
+}
+
+static void print_direc(char *start, size_t length, int field_width, int precision,
+ const char *argument)
+{
+ char *p; /* Null-terminated copy of % directive. */
+
+ p = xmalloc((unsigned) (length + 1));
+ strncpy(p, start, length);
+ p[length] = 0;
+
+ switch (p[length - 1]) {
+ case 'd':
+ case 'i':
+ if (field_width < 0) {
+ if (precision < 0)
+ printf(p, my_xstrtol(argument));
+ else
+ printf(p, precision, my_xstrtol(argument));
+ } else {
+ if (precision < 0)
+ printf(p, field_width, my_xstrtol(argument));
+ else
+ printf(p, field_width, precision, my_xstrtol(argument));
+ }
+ break;
+ case 'o':
+ case 'u':
+ case 'x':
+ case 'X':
+ if (field_width < 0) {
+ if (precision < 0)
+ printf(p, my_xstrtoul(argument));
+ else
+ printf(p, precision, my_xstrtoul(argument));
+ } else {
+ if (precision < 0)
+ printf(p, field_width, my_xstrtoul(argument));
+ else
+ printf(p, field_width, precision, my_xstrtoul(argument));
+ }
+ break;
+ case 'f':
+ case 'e':
+ case 'E':
+ case 'g':
+ case 'G':
+ if (field_width < 0) {
+ if (precision < 0)
+ printf(p, my_xstrtod(argument));
+ else
+ printf(p, precision, my_xstrtod(argument));
+ } else {
+ if (precision < 0)
+ printf(p, field_width, my_xstrtod(argument));
+ else
+ printf(p, field_width, precision, my_xstrtod(argument));
+ }
+ break;
+ case 'c':
+ printf(p, *argument);
+ break;
+ case 's':
+ if (field_width < 0) {
+ if (precision < 0)
+ printf(p, argument);
+ else
+ printf(p, precision, argument);
+ } else {
+ if (precision < 0)
+ printf(p, field_width, argument);
+ else
+ printf(p, field_width, precision, argument);
+ }
+ break;
+ }
+
+ free(p);
+}
+
+/* Print the text in FORMAT, using ARGV (with ARGC elements) for
+ arguments to any '%' directives.
+ Return the number of elements of ARGV used. */
+
+static int print_formatted(char *format, int argc, char **argv)
+{
+ int save_argc = argc; /* Preserve original value. */
+ char *f; /* Pointer into 'format'. */
+ char *direc_start; /* Start of % directive. */
+ size_t direc_length; /* Length of % directive. */
+ int field_width; /* Arg to first '*', or -1 if none. */
+ int precision; /* Arg to second '*', or -1 if none. */
+
+ for (f = format; *f; ++f) {
+ switch (*f) {
+ case '%':
+ direc_start = f++;
+ direc_length = 1;
+ field_width = precision = -1;
+ if (*f == '%') {
+ putchar('%');
+ break;
+ }
+ if (*f == 'b') {
+ if (argc > 0) {
+ print_esc_string(*argv);
+ ++argv;
+ --argc;
+ }
+ break;
+ }
+ if (strchr("-+ #", *f)) {
+ ++f;
+ ++direc_length;
+ }
+ if (*f == '*') {
+ ++f;
+ ++direc_length;
+ if (argc > 0) {
+ field_width = my_xstrtoul(*argv);
+ ++argv;
+ --argc;
+ } else
+ field_width = 0;
+ } else {
+ while (isdigit(*f)) {
+ ++f;
+ ++direc_length;
+ }
+ }
+ if (*f == '.') {
+ ++f;
+ ++direc_length;
+ if (*f == '*') {
+ ++f;
+ ++direc_length;
+ if (argc > 0) {
+ precision = my_xstrtoul(*argv);
+ ++argv;
+ --argc;
+ } else
+ precision = 0;
+ } else
+ while (isdigit(*f)) {
+ ++f;
+ ++direc_length;
+ }
+ }
+ if (*f == 'l' || *f == 'L' || *f == 'h') {
+ ++f;
+ ++direc_length;
+ }
+ /*
+ if (!strchr ("diouxXfeEgGcs", *f))
+ fprintf(stderr, "%%%c: invalid directive", *f);
+ */
+ ++direc_length;
+ if (argc > 0) {
+ print_direc(direc_start, direc_length, field_width,
+ precision, *argv);
+ ++argv;
+ --argc;
+ } else
+ print_direc(direc_start, direc_length, field_width,
+ precision, "");
+ break;
+ case '\\':
+ if (*++f == 'c')
+ exit(0);
+ putchar(bb_process_escape_sequence((const char **)&f));
+ f--;
+ break;
+ default:
+ putchar(*f);
+ }
+ }
+
+ return save_argc - argc;
+}
+
+int printf_main(int argc, char **argv);
+int printf_main(int argc, char **argv)
+{
+ char *format;
+ int args_used;
+
+ if (argc <= 1 || argv[1][0] == '-') {
+ bb_show_usage();
+ }
+
+ format = argv[1];
+ argc -= 2;
+ argv += 2;
+
+ do {
+ args_used = print_formatted(format, argc, argv);
+ argc -= args_used;
+ argv += args_used;
+ } while (args_used > 0 && argc > 0);
+
+/* if (argc > 0)
+ fprintf(stderr, "excess args ignored");
+*/
+
+ return EXIT_SUCCESS;
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/pwd.c b/i/pc104/initrd/conf/busybox/coreutils/pwd.c
new file mode 100644
index 0000000..d96f6a8
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/pwd.c
@@ -0,0 +1,26 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini pwd implementation for busybox
+ *
+ * Copyright (C) 1995, 1996 by Bruce Perens <bruce@pixar.com>.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "busybox.h"
+
+int pwd_main(int argc, char **argv);
+int pwd_main(int argc, char **argv)
+{
+ char *buf;
+
+ buf = xrealloc_getcwd_or_warn(NULL);
+ if (buf != NULL) {
+ puts(buf);
+ fflush_stdout_and_exit(EXIT_SUCCESS);
+ }
+
+ return EXIT_FAILURE;
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/realpath.c b/i/pc104/initrd/conf/busybox/coreutils/realpath.c
new file mode 100644
index 0000000..b3f7e49
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/realpath.c
@@ -0,0 +1,47 @@
+/* vi: set sw=4 ts=4: */
+
+/* BB_AUDIT SUSv3 N/A -- Apparently a busybox extension. */
+
+/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
+ *
+ * Now does proper error checking on output and returns a failure exit code
+ * if one or more paths cannot be resolved.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "busybox.h"
+
+int realpath_main(int argc, char **argv);
+int realpath_main(int argc, char **argv)
+{
+ int retval = EXIT_SUCCESS;
+
+#if PATH_MAX > (BUFSIZ+1)
+ RESERVE_CONFIG_BUFFER(resolved_path, PATH_MAX);
+# define resolved_path_MUST_FREE 1
+#else
+#define resolved_path bb_common_bufsiz1
+# define resolved_path_MUST_FREE 0
+#endif
+
+ if (--argc == 0) {
+ bb_show_usage();
+ }
+
+ do {
+ argv++;
+ if (realpath(*argv, resolved_path) != NULL) {
+ puts(resolved_path);
+ } else {
+ retval = EXIT_FAILURE;
+ bb_perror_msg("%s", *argv);
+ }
+ } while (--argc);
+
+#if ENABLE_FEATURE_CLEAN_UP && resolved_path_MUST_FREE
+ RELEASE_CONFIG_BUFFER(resolved_path);
+#endif
+
+ fflush_stdout_and_exit(retval);
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/rm.c b/i/pc104/initrd/conf/busybox/coreutils/rm.c
new file mode 100644
index 0000000..1883fee
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/rm.c
@@ -0,0 +1,53 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini rm implementation for busybox
+ *
+ * Copyright (C) 2001 Matt Kraai <kraai@alumni.carnegiemellon.edu>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/* BB_AUDIT SUSv3 compliant */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/rm.html */
+
+/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
+ *
+ * Size reduction.
+ */
+
+#include <unistd.h>
+#include "busybox.h"
+
+int rm_main(int argc, char **argv);
+int rm_main(int argc, char **argv)
+{
+ int status = 0;
+ int flags = 0;
+ unsigned opt;
+
+ opt_complementary = "f-i:i-f";
+ opt = getopt32(argc, argv, "fiRr");
+ if(opt & 1)
+ flags |= FILEUTILS_FORCE;
+ if(opt & 2)
+ flags |= FILEUTILS_INTERACTIVE;
+ if(opt & 12)
+ flags |= FILEUTILS_RECUR;
+
+ if (*(argv += optind) != NULL) {
+ do {
+ const char *base = bb_get_last_path_component(*argv);
+
+ if ((base[0] == '.') && (!base[1] || ((base[1] == '.') && !base[2]))) {
+ bb_error_msg("cannot remove '.' or '..'");
+ } else if (remove_file(*argv, flags) >= 0) {
+ continue;
+ }
+ status = 1;
+ } while (*++argv);
+ } else if (!(flags & FILEUTILS_FORCE)) {
+ bb_show_usage();
+ }
+
+ return status;
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/rmdir.c b/i/pc104/initrd/conf/busybox/coreutils/rmdir.c
new file mode 100644
index 0000000..8cbd6f1
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/rmdir.c
@@ -0,0 +1,61 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * rmdir implementation for busybox
+ *
+ * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/* BB_AUDIT SUSv3 compliant */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/rmdir.html */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <libgen.h>
+#include "busybox.h"
+
+int rmdir_main(int argc, char **argv);
+int rmdir_main(int argc, char **argv)
+{
+ int status = EXIT_SUCCESS;
+ int flags;
+ int do_dot;
+ char *path;
+
+ flags = getopt32(argc, argv, "p");
+
+ argv += optind;
+
+ if (!*argv) {
+ bb_show_usage();
+ }
+
+ do {
+ path = *argv;
+
+ /* Record if the first char was a '.' so we can use dirname later. */
+ do_dot = (*path == '.');
+
+ do {
+ if (rmdir(path) < 0) {
+ bb_perror_msg("'%s'", path); /* Match gnu rmdir msg. */
+ status = EXIT_FAILURE;
+ } else if (flags) {
+ /* Note: path was not empty or null since rmdir succeeded. */
+ path = dirname(path);
+ /* Path is now just the parent component. Note that dirname
+ * returns "." if there are no parents. We must distinguish
+ * this from the case of the original path starting with '.'.
+ */
+ if (do_dot || (*path != '.') || path[1]) {
+ continue;
+ }
+ }
+ break;
+ } while (1);
+
+ } while (*++argv);
+
+ return status;
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/seq.c b/i/pc104/initrd/conf/busybox/coreutils/seq.c
new file mode 100644
index 0000000..e81a466
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/seq.c
@@ -0,0 +1,41 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * seq implementation for busybox
+ *
+ * Copyright (C) 2004, Glenn McGrath
+ *
+ * Licensed under the GPL v2, see the file LICENSE in this tarball.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "busybox.h"
+
+int seq_main(int argc, char **argv);
+int seq_main(int argc, char **argv)
+{
+ double last, first, increment, i;
+
+ first = increment = 1;
+ switch (argc) {
+ case 4:
+ increment = atof(argv[2]);
+ case 3:
+ first = atof(argv[1]);
+ case 2:
+ last = atof(argv[argc-1]);
+ break;
+ default:
+ bb_show_usage();
+ }
+
+ /* You should note that this is pos-5.0.91 semantics, -- FK. */
+ for (i = first;
+ (increment > 0 && i <= last) || (increment < 0 && i >=last);
+ i += increment)
+ {
+ printf("%g\n", i);
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/sleep.c b/i/pc104/initrd/conf/busybox/coreutils/sleep.c
new file mode 100644
index 0000000..b89b0fe
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/sleep.c
@@ -0,0 +1,68 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * sleep implementation for busybox
+ *
+ * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/* BB_AUDIT SUSv3 compliant */
+/* BB_AUDIT GNU issues -- fancy version matches except args must be ints. */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/sleep.html */
+
+/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
+ *
+ * Rewritten to do proper arg and error checking.
+ * Also, added a 'fancy' configuration to accept multiple args with
+ * time suffixes for seconds, minutes, hours, and days.
+ */
+
+#include <stdlib.h>
+#include <limits.h>
+#include <unistd.h>
+#include "busybox.h"
+
+#ifdef CONFIG_FEATURE_FANCY_SLEEP
+static const struct suffix_mult sfx[] = {
+ { "s", 1 },
+ { "m", 60 },
+ { "h", 60*60 },
+ { "d", 24*60*60 },
+ { NULL, 0 }
+};
+#endif
+
+int sleep_main(int argc, char **argv);
+int sleep_main(int argc, char **argv)
+{
+ unsigned int duration;
+
+#ifdef CONFIG_FEATURE_FANCY_SLEEP
+
+ if (argc < 2) {
+ bb_show_usage();
+ }
+
+ ++argv;
+ duration = 0;
+ do {
+ duration += xatoul_range_sfx(*argv, 0, UINT_MAX-duration, sfx);
+ } while (*++argv);
+
+#else /* CONFIG_FEATURE_FANCY_SLEEP */
+
+ if (argc != 2) {
+ bb_show_usage();
+ }
+
+ duration = xatou(argv[1]);
+
+#endif /* CONFIG_FEATURE_FANCY_SLEEP */
+
+ if (sleep(duration)) {
+ bb_perror_nomsg_and_die();
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/sort.c b/i/pc104/initrd/conf/busybox/coreutils/sort.c
new file mode 100644
index 0000000..311d0cb
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/sort.c
@@ -0,0 +1,401 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * SuS3 compliant sort implementation for busybox
+ *
+ * Copyright (C) 2004 by Rob Landley <rob@landley.net>
+ *
+ * MAINTAINER: Rob Landley <rob@landley.net>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ *
+ * See SuS3 sort standard at:
+ * http://www.opengroup.org/onlinepubs/007904975/utilities/sort.html
+ */
+
+#include "busybox.h"
+
+/*
+ sort [-m][-o output][-bdfinru][-t char][-k keydef]... [file...]
+ sort -c [-bdfinru][-t char][-k keydef][file]
+*/
+
+/* These are sort types */
+static const char OPT_STR[] = "ngMucszbrdfimS:T:o:k:t:";
+enum {
+ FLAG_n = 1, /* Numeric sort */
+ FLAG_g = 2, /* Sort using strtod() */
+ FLAG_M = 4, /* Sort date */
+/* ucsz apply to root level only, not keys. b at root level implies bb */
+ FLAG_u = 8, /* Unique */
+ FLAG_c = 0x10, /* Check: no output, exit(!ordered) */
+ FLAG_s = 0x20, /* Stable sort, no ascii fallback at end */
+ FLAG_z = 0x40, /* Input is null terminated, not \n */
+/* These can be applied to search keys, the previous four can't */
+ FLAG_b = 0x80, /* Ignore leading blanks */
+ FLAG_r = 0x100, /* Reverse */
+ FLAG_d = 0x200, /* Ignore !(isalnum()|isspace()) */
+ FLAG_f = 0x400, /* Force uppercase */
+ FLAG_i = 0x800, /* Ignore !isprint() */
+ FLAG_m = 0x1000, /* ignored: merge already sorted files; do not sort */
+ FLAG_S = 0x2000, /* ignored: -S, --buffer-size=SIZE */
+ FLAG_T = 0x4000, /* ignored: -T, --temporary-directory=DIR */
+ FLAG_o = 0x8000,
+ FLAG_k = 0x10000,
+ FLAG_t = 0x20000,
+ FLAG_bb = 0x80000000, /* Ignore trailing blanks */
+};
+
+#if ENABLE_FEATURE_SORT_BIG
+static char key_separator;
+
+static struct sort_key {
+ struct sort_key *next_key; /* linked list */
+ unsigned range[4]; /* start word, start char, end word, end char */
+ unsigned flags;
+} *key_list;
+
+static char *get_key(char *str, struct sort_key *key, int flags)
+{
+ int start = 0, end = 0, len, i, j;
+
+ /* Special case whole string, so we don't have to make a copy */
+ if (key->range[0] == 1 && !key->range[1] && !key->range[2] && !key->range[3]
+ && !(flags & (FLAG_b | FLAG_d | FLAG_f | FLAG_i | FLAG_bb))
+ ) {
+ return str;
+ }
+
+ /* Find start of key on first pass, end on second pass */
+ len = strlen(str);
+ for (j = 0; j < 2; j++) {
+ if (!key->range[2*j])
+ end = len;
+ /* Loop through fields */
+ else {
+ end = 0;
+ for (i = 1; i < key->range[2*j] + j; i++) {
+ if (key_separator) {
+ /* Skip body of key and separator */
+ while (str[end]) {
+ if (str[end++] == key_separator)
+ break;
+ }
+ } else {
+ /* Skip leading blanks */
+ while (isspace(str[end]))
+ end++;
+ /* Skip body of key */
+ while (str[end]) {
+ if (isspace(str[end]))
+ break;
+ end++;
+ }
+ }
+ }
+ }
+ if (!j) start = end;
+ }
+ /* Strip leading whitespace if necessary */
+//XXX: skip_whitespace()
+ if (flags & FLAG_b)
+ while (isspace(str[start])) start++;
+ /* Strip trailing whitespace if necessary */
+ if (flags & FLAG_bb)
+ while (end > start && isspace(str[end-1])) end--;
+ /* Handle offsets on start and end */
+ if (key->range[3]) {
+ end += key->range[3] - 1;
+ if (end > len) end = len;
+ }
+ if (key->range[1]) {
+ start += key->range[1] - 1;
+ if (start > len) start = len;
+ }
+ /* Make the copy */
+ if (end < start) end = start;
+ str = xstrndup(str+start, end-start);
+ /* Handle -d */
+ if (flags & FLAG_d) {
+ for (start = end = 0; str[end]; end++)
+ if (isspace(str[end]) || isalnum(str[end]))
+ str[start++] = str[end];
+ str[start] = '\0';
+ }
+ /* Handle -i */
+ if (flags & FLAG_i) {
+ for (start = end = 0; str[end]; end++)
+ if (isprint(str[end]))
+ str[start++] = str[end];
+ str[start] = '\0';
+ }
+ /* Handle -f */
+ if (flags & FLAG_f)
+ for (i = 0; str[i]; i++)
+ str[i] = toupper(str[i]);
+
+ return str;
+}
+
+static struct sort_key *add_key(void)
+{
+ struct sort_key **pkey = &key_list;
+ while (*pkey)
+ pkey = &((*pkey)->next_key);
+ return *pkey = xzalloc(sizeof(struct sort_key));
+}
+
+#define GET_LINE(fp) \
+ ((option_mask32 & FLAG_z) \
+ ? bb_get_chunk_from_file(fp, NULL) \
+ : xmalloc_getline(fp))
+#else
+#define GET_LINE(fp) xmalloc_getline(fp)
+#endif
+
+/* Iterate through keys list and perform comparisons */
+static int compare_keys(const void *xarg, const void *yarg)
+{
+ int flags = option_mask32, retval = 0;
+ char *x, *y;
+
+#if ENABLE_FEATURE_SORT_BIG
+ struct sort_key *key;
+
+ for (key = key_list; !retval && key; key = key->next_key) {
+ flags = key->flags ? key->flags : option_mask32;
+ /* Chop out and modify key chunks, handling -dfib */
+ x = get_key(*(char **)xarg, key, flags);
+ y = get_key(*(char **)yarg, key, flags);
+#else
+ /* This curly bracket serves no purpose but to match the nesting
+ level of the for () loop we're not using */
+ {
+ x = *(char **)xarg;
+ y = *(char **)yarg;
+#endif
+ /* Perform actual comparison */
+ switch (flags & 7) {
+ default:
+ bb_error_msg_and_die("unknown sort type");
+ break;
+ /* Ascii sort */
+ case 0:
+#if ENABLE_LOCALE_SUPPORT
+ retval = strcoll(x, y);
+#else
+ retval = strcmp(x, y);
+#endif
+ break;
+#if ENABLE_FEATURE_SORT_BIG
+ case FLAG_g: {
+ char *xx, *yy;
+ double dx = strtod(x, &xx);
+ double dy = strtod(y, &yy);
+ /* not numbers < NaN < -infinity < numbers < +infinity) */
+ if (x == xx)
+ retval = (y == yy ? 0 : -1);
+ else if (y == yy)
+ retval = 1;
+ /* Check for isnan */
+ else if (dx != dx)
+ retval = (dy != dy) ? 0 : -1;
+ else if (dy != dy)
+ retval = 1;
+ /* Check for infinity. Could underflow, but it avoids libm. */
+ else if (1.0 / dx == 0.0) {
+ if (dx < 0)
+ retval = (1.0 / dy == 0.0 && dy < 0) ? 0 : -1;
+ else
+ retval = (1.0 / dy == 0.0 && dy > 0) ? 0 : 1;
+ } else if (1.0 / dy == 0.0)
+ retval = (dy < 0) ? 1 : -1;
+ else
+ retval = (dx > dy) ? 1 : ((dx < dy) ? -1 : 0);
+ break;
+ }
+ case FLAG_M: {
+ struct tm thyme;
+ int dx;
+ char *xx, *yy;
+
+ xx = strptime(x, "%b", &thyme);
+ dx = thyme.tm_mon;
+ yy = strptime(y, "%b", &thyme);
+ if (!xx)
+ retval = (!yy) ? 0 : -1;
+ else if (!yy)
+ retval = 1;
+ else
+ retval = (dx == thyme.tm_mon) ? 0 : dx - thyme.tm_mon;
+ break;
+ }
+ /* Full floating point version of -n */
+ case FLAG_n: {
+ double dx = atof(x);
+ double dy = atof(y);
+ retval = (dx > dy) ? 1 : ((dx < dy) ? -1 : 0);
+ break;
+ }
+ } /* switch */
+ /* Free key copies. */
+ if (x != *(char **)xarg) free(x);
+ if (y != *(char **)yarg) free(y);
+ /* if (retval) break; - done by for () anyway */
+#else
+ /* Integer version of -n for tiny systems */
+ case FLAG_n:
+ retval = atoi(x) - atoi(y);
+ break;
+ } /* switch */
+#endif
+ } /* for */
+
+ /* Perform fallback sort if necessary */
+ if (!retval && !(option_mask32 & FLAG_s))
+ retval = strcmp(*(char **)xarg, *(char **)yarg);
+
+ if (flags & FLAG_r) return -retval;
+ return retval;
+}
+
+#if ENABLE_FEATURE_SORT_BIG
+static unsigned str2u(char **str)
+{
+ unsigned long lu;
+ if (!isdigit((*str)[0]))
+ bb_error_msg_and_die("bad field specification");
+ lu = strtoul(*str, str, 10);
+ if ((sizeof(long) > sizeof(int) && lu > INT_MAX) || !lu)
+ bb_error_msg_and_die("bad field specification");
+ return lu;
+}
+#endif
+
+int sort_main(int argc, char **argv);
+int sort_main(int argc, char **argv)
+{
+ FILE *fp, *outfile = stdout;
+ char *line, **lines = NULL;
+ char *str_ignored, *str_o, *str_t;
+ llist_t *lst_k = NULL;
+ int i, flag;
+ int linecount = 0;
+
+ xfunc_error_retval = 2;
+
+ /* Parse command line options */
+ /* -o and -t can be given at most once */
+ opt_complementary = "?:o--o:t--t:" /* -t, -o: maximum one of each */
+ "k::"; /* -k takes list */
+ getopt32(argc, argv, OPT_STR, &str_ignored, &str_ignored, &str_o, &lst_k, &str_t);
+#if ENABLE_FEATURE_SORT_BIG
+ if (option_mask32 & FLAG_o) outfile = xfopen(str_o, "w");
+ if (option_mask32 & FLAG_t) {
+ if (!str_t[0] || str_t[1])
+ bb_error_msg_and_die("bad -t parameter");
+ key_separator = str_t[0];
+ }
+ /* parse sort key */
+ lst_k = llist_rev(lst_k);
+ while (lst_k) {
+ enum {
+ FLAG_allowed_for_k =
+ FLAG_n | /* Numeric sort */
+ FLAG_g | /* Sort using strtod() */
+ FLAG_M | /* Sort date */
+ FLAG_b | /* Ignore leading blanks */
+ FLAG_r | /* Reverse */
+ FLAG_d | /* Ignore !(isalnum()|isspace()) */
+ FLAG_f | /* Force uppercase */
+ FLAG_i | /* Ignore !isprint() */
+ 0
+ };
+ struct sort_key *key = add_key();
+ char *str_k = lst_k->data;
+ const char *temp2;
+
+ i = 0; /* i==0 before comma, 1 after (-k3,6) */
+ while (*str_k) {
+ /* Start of range */
+ /* Cannot use bb_strtou - suffix can be a letter */
+ key->range[2*i] = str2u(&str_k);
+ if (*str_k == '.') {
+ str_k++;
+ key->range[2*i+1] = str2u(&str_k);
+ }
+ while (*str_k) {
+ if (*str_k == ',' && !i++) {
+ str_k++;
+ break;
+ } /* no else needed: fall through to syntax error
+ because comma isn't in OPT_STR */
+ temp2 = strchr(OPT_STR, *str_k);
+ if (!temp2)
+ bb_error_msg_and_die("unknown key option");
+ flag = 1 << (temp2 - OPT_STR);
+ if (flag & ~FLAG_allowed_for_k)
+ bb_error_msg_and_die("unknown sort type");
+ /* b after ',' means strip _trailing_ space */
+ if (i && flag == FLAG_b) flag = FLAG_bb;
+ key->flags |= flag;
+ str_k++;
+ }
+ }
+ /* leaking lst_k... */
+ lst_k = lst_k->link;
+ }
+#endif
+ /* global b strips leading and trailing spaces */
+ if (option_mask32 & FLAG_b) option_mask32 |= FLAG_bb;
+
+ /* Open input files and read data */
+ for (i = argv[optind] ? optind : optind-1; argv[i]; i++) {
+ fp = stdin;
+ if (i >= optind && NOT_LONE_DASH(argv[i]))
+ fp = xfopen(argv[i], "r");
+ for (;;) {
+ line = GET_LINE(fp);
+ if (!line) break;
+ if (!(linecount & 63))
+ lines = xrealloc(lines, sizeof(char *) * (linecount + 64));
+ lines[linecount++] = line;
+ }
+ fclose(fp);
+ }
+#if ENABLE_FEATURE_SORT_BIG
+ /* if no key, perform alphabetic sort */
+ if (!key_list)
+ add_key()->range[0] = 1;
+ /* handle -c */
+ if (option_mask32 & FLAG_c) {
+ int j = (option_mask32 & FLAG_u) ? -1 : 0;
+ for (i = 1; i < linecount; i++)
+ if (compare_keys(&lines[i-1], &lines[i]) > j) {
+ fprintf(stderr, "Check line %d\n", i);
+ return 1;
+ }
+ return 0;
+ }
+#endif
+ /* Perform the actual sort */
+ qsort(lines, linecount, sizeof(char *), compare_keys);
+ /* handle -u */
+ if (option_mask32 & FLAG_u) {
+ flag = 0;
+ /* coreutils 6.3 drop lines for which only key is the same */
+ /* -- disabling last-resort compare... */
+ option_mask32 |= FLAG_s;
+ for (i = 1; i < linecount; i++) {
+ if (!compare_keys(&lines[flag], &lines[i]))
+ free(lines[i]);
+ else
+ lines[++flag] = lines[i];
+ }
+ if (linecount) linecount = flag+1;
+ }
+ /* Print it */
+ for (i = 0; i < linecount; i++)
+ fprintf(outfile, "%s\n", lines[i]);
+
+ fflush_stdout_and_exit(EXIT_SUCCESS);
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/stat.c b/i/pc104/initrd/conf/busybox/coreutils/stat.c
new file mode 100644
index 0000000..a2cdbfb
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/stat.c
@@ -0,0 +1,676 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * stat -- display file or file system status
+ *
+ * Copyright (C) 2001, 2002, 2003, 2004, 2005 Free Software Foundation.
+ * Copyright (C) 2005 by Erik Andersen <andersen@codepoet.org>
+ * Copyright (C) 2005 by Mike Frysinger <vapier@gentoo.org>
+ * Copyright (C) 2006 by Yoshinori Sato <ysato@users.sourceforge.jp>
+ *
+ * Written by Michael Meskes
+ * Taken from coreutils and turned into a busybox applet by Mike Frysinger
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "busybox.h"
+
+/* vars to control behavior */
+#define OPT_TERSE 2
+#define OPT_DEREFERENCE 4
+#define OPT_SELINUX 8
+static long flags;
+
+static char const *file_type(struct stat const *st)
+{
+ /* See POSIX 1003.1-2001 XCU Table 4-8 lines 17093-17107
+ * for some of these formats.
+ * To keep diagnostics grammatical in English, the
+ * returned string must start with a consonant.
+ */
+ if (S_ISREG(st->st_mode)) return st->st_size == 0 ? "regular empty file" : "regular file";
+ if (S_ISDIR(st->st_mode)) return "directory";
+ if (S_ISBLK(st->st_mode)) return "block special file";
+ if (S_ISCHR(st->st_mode)) return "character special file";
+ if (S_ISFIFO(st->st_mode)) return "fifo";
+ if (S_ISLNK(st->st_mode)) return "symbolic link";
+ if (S_ISSOCK(st->st_mode)) return "socket";
+ if (S_TYPEISMQ(st)) return "message queue";
+ if (S_TYPEISSEM(st)) return "semaphore";
+ if (S_TYPEISSHM(st)) return "shared memory object";
+#ifdef S_TYPEISTMO
+ if (S_TYPEISTMO(st)) return "typed memory object";
+#endif
+ return "weird file";
+}
+
+static char const *human_time(time_t t)
+{
+ /* Old
+ static char *str;
+ str = ctime(&t);
+ str[strlen(str)-1] = '\0';
+ return str;
+ */
+ /* coreutils 6.3 compat: */
+ static char buf[sizeof("YYYY-MM-DD HH:MM:SS.000000000")];
+ strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S.000000000", localtime(&t));
+ return buf;
+}
+
+/* Return the type of the specified file system.
+ * Some systems have statfvs.f_basetype[FSTYPSZ]. (AIX, HP-UX, and Solaris)
+ * Others have statfs.f_fstypename[MFSNAMELEN]. (NetBSD 1.5.2)
+ * Still others have neither and have to get by with f_type (Linux).
+ */
+static char const *human_fstype(long f_type)
+{
+ int i;
+ static const struct types {
+ long type;
+ const char *fs;
+ } humantypes[] = {
+ { 0xADFF, "affs" },
+ { 0x1Cd1, "devpts" },
+ { 0x137D, "ext" },
+ { 0xEF51, "ext2" },
+ { 0xEF53, "ext2/ext3" },
+ { 0x3153464a, "jfs" },
+ { 0x58465342, "xfs" },
+ { 0xF995E849, "hpfs" },
+ { 0x9660, "isofs" },
+ { 0x4000, "isofs" },
+ { 0x4004, "isofs" },
+ { 0x137F, "minix" },
+ { 0x138F, "minix (30 char.)" },
+ { 0x2468, "minix v2" },
+ { 0x2478, "minix v2 (30 char.)" },
+ { 0x4d44, "msdos" },
+ { 0x4006, "fat" },
+ { 0x564c, "novell" },
+ { 0x6969, "nfs" },
+ { 0x9fa0, "proc" },
+ { 0x517B, "smb" },
+ { 0x012FF7B4, "xenix" },
+ { 0x012FF7B5, "sysv4" },
+ { 0x012FF7B6, "sysv2" },
+ { 0x012FF7B7, "coh" },
+ { 0x00011954, "ufs" },
+ { 0x012FD16D, "xia" },
+ { 0x5346544e, "ntfs" },
+ { 0x1021994, "tmpfs" },
+ { 0x52654973, "reiserfs" },
+ { 0x28cd3d45, "cramfs" },
+ { 0x7275, "romfs" },
+ { 0x858458f6, "romfs" },
+ { 0x73717368, "squashfs" },
+ { 0x62656572, "sysfs" },
+ { 0, "UNKNOWN" }
+ };
+ for (i=0; humantypes[i].type; ++i)
+ if (humantypes[i].type == f_type)
+ break;
+ return humantypes[i].fs;
+}
+
+#ifdef CONFIG_FEATURE_STAT_FORMAT
+/* print statfs info */
+static void print_statfs(char *pformat, size_t buf_len, char m,
+ char const *filename, void const *data
+ USE_SELINUX(,security_context_t scontext) )
+{
+ struct statfs const *statfsbuf = data;
+
+ switch (m) {
+ case 'n':
+ strncat(pformat, "s", buf_len);
+ printf(pformat, filename);
+ break;
+ case 'i':
+ strncat(pformat, "Lx", buf_len);
+ printf(pformat, statfsbuf->f_fsid);
+ break;
+ case 'l':
+ strncat(pformat, "lu", buf_len);
+ printf(pformat, statfsbuf->f_namelen);
+ break;
+ case 't':
+ strncat(pformat, "lx", buf_len);
+ printf(pformat, (unsigned long int) (statfsbuf->f_type)); /* no equiv. */
+ break;
+ case 'T':
+ strncat(pformat, "s", buf_len);
+ printf(pformat, human_fstype(statfsbuf->f_type));
+ break;
+ case 'b':
+ strncat(pformat, "jd", buf_len);
+ printf(pformat, (intmax_t) (statfsbuf->f_blocks));
+ break;
+ case 'f':
+ strncat(pformat, "jd", buf_len);
+ printf(pformat, (intmax_t) (statfsbuf->f_bfree));
+ break;
+ case 'a':
+ strncat(pformat, "jd", buf_len);
+ printf(pformat, (intmax_t) (statfsbuf->f_bavail));
+ break;
+ case 'S':
+ case 's':
+ strncat(pformat, "lu", buf_len);
+ printf(pformat, (unsigned long int) (statfsbuf->f_bsize));
+ break;
+ case 'c':
+ strncat(pformat, "jd", buf_len);
+ printf(pformat, (intmax_t) (statfsbuf->f_files));
+ break;
+ case 'd':
+ strncat(pformat, "jd", buf_len);
+ printf(pformat, (intmax_t) (statfsbuf->f_ffree));
+ break;
+#if ENABLE_SELINUX
+ case 'C':
+ if (flags & OPT_SELINUX) {
+ strncat(pformat, "s", buf_len);
+ printf(scontext);
+ }
+ break;
+#endif
+ default:
+ strncat(pformat, "c", buf_len);
+ printf(pformat, m);
+ break;
+ }
+}
+
+/* print stat info */
+static void print_stat(char *pformat, size_t buf_len, char m,
+ char const *filename, void const *data
+ USE_SELINUX(, security_context_t scontext))
+{
+#define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
+ struct stat *statbuf = (struct stat *) data;
+ struct passwd *pw_ent;
+ struct group *gw_ent;
+
+ switch (m) {
+ case 'n':
+ strncat(pformat, "s", buf_len);
+ printf(pformat, filename);
+ break;
+ case 'N':
+ strncat(pformat, "s", buf_len);
+ if (S_ISLNK(statbuf->st_mode)) {
+ char *linkname = xmalloc_readlink_or_warn(filename);
+ if (linkname == NULL) {
+ bb_perror_msg("cannot read symbolic link '%s'", filename);
+ return;
+ }
+ /*printf("\"%s\" -> \"%s\"", filename, linkname); */
+ printf(pformat, filename);
+ printf(" -> ");
+ printf(pformat, linkname);
+ } else {
+ printf(pformat, filename);
+ }
+ break;
+ case 'd':
+ strncat(pformat, "ju", buf_len);
+ printf(pformat, (uintmax_t) statbuf->st_dev);
+ break;
+ case 'D':
+ strncat(pformat, "jx", buf_len);
+ printf(pformat, (uintmax_t) statbuf->st_dev);
+ break;
+ case 'i':
+ strncat(pformat, "ju", buf_len);
+ printf(pformat, (uintmax_t) statbuf->st_ino);
+ break;
+ case 'a':
+ strncat(pformat, "lo", buf_len);
+ printf(pformat, (unsigned long int) (statbuf->st_mode & (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)));
+ break;
+ case 'A':
+ strncat(pformat, "s", buf_len);
+ printf(pformat, bb_mode_string(statbuf->st_mode));
+ break;
+ case 'f':
+ strncat(pformat, "lx", buf_len);
+ printf(pformat, (unsigned long int) statbuf->st_mode);
+ break;
+ case 'F':
+ strncat(pformat, "s", buf_len);
+ printf(pformat, file_type(statbuf));
+ break;
+ case 'h':
+ strncat(pformat, "lu", buf_len);
+ printf(pformat, (unsigned long int) statbuf->st_nlink);
+ break;
+ case 'u':
+ strncat(pformat, "lu", buf_len);
+ printf(pformat, (unsigned long int) statbuf->st_uid);
+ break;
+ case 'U':
+ strncat(pformat, "s", buf_len);
+ setpwent();
+ pw_ent = getpwuid(statbuf->st_uid);
+ printf(pformat, (pw_ent != 0L) ? pw_ent->pw_name : "UNKNOWN");
+ break;
+ case 'g':
+ strncat(pformat, "lu", buf_len);
+ printf(pformat, (unsigned long int) statbuf->st_gid);
+ break;
+ case 'G':
+ strncat(pformat, "s", buf_len);
+ setgrent();
+ gw_ent = getgrgid(statbuf->st_gid);
+ printf(pformat, (gw_ent != 0L) ? gw_ent->gr_name : "UNKNOWN");
+ break;
+ case 't':
+ strncat(pformat, "lx", buf_len);
+ printf(pformat, (unsigned long int) major(statbuf->st_rdev));
+ break;
+ case 'T':
+ strncat(pformat, "lx", buf_len);
+ printf(pformat, (unsigned long int) minor(statbuf->st_rdev));
+ break;
+ case 's':
+ strncat(pformat, "ju", buf_len);
+ printf(pformat, (uintmax_t) (statbuf->st_size));
+ break;
+ case 'B':
+ strncat(pformat, "lu", buf_len);
+ printf(pformat, (unsigned long int) 512); //ST_NBLOCKSIZE
+ break;
+ case 'b':
+ strncat(pformat, "ju", buf_len);
+ printf(pformat, (uintmax_t) statbuf->st_blocks);
+ break;
+ case 'o':
+ strncat(pformat, "lu", buf_len);
+ printf(pformat, (unsigned long int) statbuf->st_blksize);
+ break;
+ case 'x':
+ strncat(pformat, "s", buf_len);
+ printf(pformat, human_time(statbuf->st_atime));
+ break;
+ case 'X':
+ strncat(pformat, TYPE_SIGNED(time_t) ? "ld" : "lu", buf_len);
+ printf(pformat, (unsigned long int) statbuf->st_atime);
+ break;
+ case 'y':
+ strncat(pformat, "s", buf_len);
+ printf(pformat, human_time(statbuf->st_mtime));
+ break;
+ case 'Y':
+ strncat(pformat, TYPE_SIGNED(time_t) ? "ld" : "lu", buf_len);
+ printf(pformat, (unsigned long int) statbuf->st_mtime);
+ break;
+ case 'z':
+ strncat(pformat, "s", buf_len);
+ printf(pformat, human_time(statbuf->st_ctime));
+ break;
+ case 'Z':
+ strncat(pformat, TYPE_SIGNED(time_t) ? "ld" : "lu", buf_len);
+ printf(pformat, (unsigned long int) statbuf->st_ctime);
+ break;
+#if ENABLE_SELINUX
+ case 'C':
+ if (flags & OPT_SELINUX) {
+ strncat(pformat, "s", buf_len);
+ printf(pformat, scontext);
+ }
+ break;
+#endif
+ default:
+ strncat(pformat, "c", buf_len);
+ printf(pformat, m);
+ break;
+ }
+}
+
+static void print_it(char const *masterformat, char const *filename,
+ void (*print_func) (char *, size_t, char, char const *, void const *
+ USE_SELINUX(, security_context_t scontext)),
+ void const *data USE_SELINUX(, security_context_t scontext) )
+{
+ char *b;
+
+ /* create a working copy of the format string */
+ char *format = xstrdup(masterformat);
+
+ /* Add 2 to accomodate our conversion of the stat '%s' format string
+ * to the printf '%llu' one. */
+ size_t n_alloc = strlen(format) + 2 + 1;
+ char *dest = xmalloc(n_alloc);
+
+ b = format;
+ while (b) {
+ size_t len;
+ char *p = strchr(b, '%');
+ if (!p) {
+ /* coreutils 6.3 always print <cr> at the end */
+ /*fputs(b, stdout);*/
+ puts(b);
+ break;
+ }
+ *p++ = '\0';
+ fputs(b, stdout);
+
+ len = strspn(p, "#-+.I 0123456789");
+ dest[0] = '%';
+ memcpy(dest + 1, p, len);
+ dest[1 + len] = 0;
+ p += len;
+
+ b = p + 1;
+ switch (*p) {
+ case '\0':
+ b = NULL;
+ /* fall through */
+ case '%':
+ putchar('%');
+ break;
+ default:
+ print_func(dest, n_alloc, *p, filename, data USE_SELINUX(,scontext));
+ break;
+ }
+ }
+
+ free(format);
+ free(dest);
+}
+#endif
+
+/* Stat the file system and print what we find. */
+static int do_statfs(char const *filename, char const *format)
+{
+ struct statfs statfsbuf;
+
+#if ENABLE_SELINUX
+ security_context_t scontext = NULL;
+ if (flags & OPT_SELINUX) {
+ if ((flags & OPT_DEREFERENCE ? lgetfilecon(filename, scontext):
+ getfilecon(filename, scontext))< 0) {
+ bb_perror_msg(filename);
+ return 0;
+ }
+ }
+#endif
+ if (statfs(filename, &statfsbuf) != 0) {
+ bb_perror_msg("cannot read file system information for '%s'", filename);
+ return 0;
+ }
+
+#ifdef CONFIG_FEATURE_STAT_FORMAT
+ if (format == NULL)
+#ifndef ENABLE_SELINUX
+ format = (flags & OPT_TERSE
+ ? "%n %i %l %t %s %b %f %a %c %d\n"
+ : " File: \"%n\"\n"
+ " ID: %-8i Namelen: %-7l Type: %T\n"
+ "Block size: %-10s\n"
+ "Blocks: Total: %-10b Free: %-10f Available: %a\n"
+ "Inodes: Total: %-10c Free: %d");
+ print_it(format, filename, print_statfs, &statfsbuf USE_SELINUX(, scontext));
+#else
+ format = (flags & OPT_TERSE
+ ? (flags & OPT_SELINUX ? "%n %i %l %t %s %b %f %a %c %d %C\n":
+ "%n %i %l %t %s %b %f %a %c %d\n")
+ : (flags & OPT_SELINUX ?
+ " File: \"%n\"\n"
+ " ID: %-8i Namelen: %-7l Type: %T\n"
+ "Block size: %-10s\n"
+ "Blocks: Total: %-10b Free: %-10f Available: %a\n"
+ "Inodes: Total: %-10c Free: %d"
+ " S_context: %C\n":
+ " File: \"%n\"\n"
+ " ID: %-8i Namelen: %-7l Type: %T\n"
+ "Block size: %-10s\n"
+ "Blocks: Total: %-10b Free: %-10f Available: %a\n"
+ "Inodes: Total: %-10c Free: %d\n")
+ );
+ print_it(format, filename, print_statfs, &statfsbuf USE_SELINUX(, scontext));
+#endif /* SELINUX */
+#else /* FEATURE_STAT_FORMAT */
+ format = (flags & OPT_TERSE
+ ? "%s %llx %lu "
+ : " File: \"%s\"\n"
+ " ID: %-8Lx Namelen: %-7lu ");
+ printf(format,
+ filename,
+ statfsbuf.f_fsid,
+ statfsbuf.f_namelen);
+
+ if (flags & OPT_TERSE)
+ printf("%lx ", (unsigned long int) (statfsbuf.f_type));
+ else
+ printf("Type: %s\n", human_fstype(statfsbuf.f_type));
+
+#if !ENABLE_SELINUX
+ format = (flags & OPT_TERSE
+ ? "%lu %ld %ld %ld %ld %ld\n"
+ : "Block size: %-10lu\n"
+ "Blocks: Total: %-10jd Free: %-10jd Available: %jd\n"
+ "Inodes: Total: %-10jd Free: %jd\n");
+ printf(format,
+ (unsigned long int) (statfsbuf.f_bsize),
+ (intmax_t) (statfsbuf.f_blocks),
+ (intmax_t) (statfsbuf.f_bfree),
+ (intmax_t) (statfsbuf.f_bavail),
+ (intmax_t) (statfsbuf.f_files),
+ (intmax_t) (statfsbuf.f_ffree));
+#else
+ format = (flags & OPT_TERSE
+ ? (flags & OPT_SELINUX ? "%lu %ld %ld %ld %ld %ld %C\n":
+ "%lu %ld %ld %ld %ld %ld\n")
+ : (flags & OPT_SELINUX ?
+ "Block size: %-10lu\n"
+ "Blocks: Total: %-10jd Free: %-10jd Available: %jd\n"
+ "Inodes: Total: %-10jd Free: %jd"
+ "S_context: %C\n":
+ "Block size: %-10lu\n"
+ "Blocks: Total: %-10jd Free: %-10jd Available: %jd\n"
+ "Inodes: Total: %-10jd Free: %jd\n"));
+ printf(format,
+ (unsigned long int) (statfsbuf.f_bsize),
+ (intmax_t) (statfsbuf.f_blocks),
+ (intmax_t) (statfsbuf.f_bfree),
+ (intmax_t) (statfsbuf.f_bavail),
+ (intmax_t) (statfsbuf.f_files),
+ (intmax_t) (statfsbuf.f_ffree),
+ scontext);
+
+ if (scontext)
+ freecon(scontext);
+#endif
+#endif /* FEATURE_STAT_FORMAT */
+ return 1;
+}
+
+/* stat the file and print what we find */
+static int do_stat(char const *filename, char const *format)
+{
+ struct stat statbuf;
+#if ENABLE_SELINUX
+ security_context_t scontext = NULL;
+ if (flags & OPT_SELINUX) {
+ if ((flags & OPT_DEREFERENCE ? lgetfilecon(filename, scontext):
+ getfilecon(filename, scontext))< 0) {
+ bb_perror_msg (filename);
+ return 0;
+ }
+ }
+#endif
+ if ((flags & OPT_DEREFERENCE ? stat : lstat) (filename, &statbuf) != 0) {
+ bb_perror_msg("cannot stat '%s'", filename);
+ return 0;
+ }
+
+#ifdef CONFIG_FEATURE_STAT_FORMAT
+ if (format == NULL) {
+#ifndef ENABLE_SELINUX
+ if (flags & OPT_TERSE) {
+ format = "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o";
+ } else {
+ if (S_ISBLK(statbuf.st_mode) || S_ISCHR(statbuf.st_mode)) {
+ format =
+ " File: \"%N\"\n"
+ " Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
+ "Device: %Dh/%dd\tInode: %-10i Links: %-5h"
+ " Device type: %t,%T\n"
+ "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n"
+ "Access: %x\n" "Modify: %y\n" "Change: %z\n";
+ } else {
+ format =
+ " File: \"%N\"\n"
+ " Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
+ "Device: %Dh/%dd\tInode: %-10i Links: %h\n"
+ "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n"
+ "Access: %x\n" "Modify: %y\n" "Change: %z\n";
+ }
+ }
+#else
+ if (flags & OPT_TERSE) {
+ format = (flags & OPT_SELINUX ?
+ "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o %C\n":
+ "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o\n");
+ } else {
+ if (S_ISBLK(statbuf.st_mode) || S_ISCHR(statbuf.st_mode)) {
+ format = (flags & OPT_SELINUX ?
+ " File: \"%N\"\n"
+ " Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
+ "Device: %Dh/%dd\tInode: %-10i Links: %-5h"
+ " Device type: %t,%T\n"
+ "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n"
+ " S_Context: %C\n"
+ "Access: %x\n" "Modify: %y\n" "Change: %z\n":
+ " File: \"%N\"\n"
+ " Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
+ "Device: %Dh/%dd\tInode: %-10i Links: %-5h"
+ " Device type: %t,%T\n"
+ "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n"
+ "Access: %x\n" "Modify: %y\n" "Change: %z\n");
+ } else {
+ format = (flags & OPT_SELINUX ?
+ " File: \"%N\"\n"
+ " Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
+ "Device: %Dh/%dd\tInode: %-10i Links: %h\n"
+ "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n"
+ "S_Context: %C\n"
+ "Access: %x\n" "Modify: %y\n" "Change: %z\n":
+ " File: \"%N\"\n"
+ " Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
+ "Device: %Dh/%dd\tInode: %-10i Links: %h\n"
+ "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n"
+ "Access: %x\n" "Modify: %y\n" "Change: %z\n");
+ }
+ }
+#endif
+ }
+ print_it(format, filename, print_stat, &statbuf USE_SELINUX(, scontext));
+#else /* FEATURE_STAT_FORMAT */
+ if (flags & OPT_TERSE) {
+ printf("%s %ju %ju %lx %lu %lu %jx %ju %lu %lx %lx %lu %lu %lu %lu"
+ SKIP_SELINUX("\n"),
+ filename,
+ (uintmax_t) (statbuf.st_size),
+ (uintmax_t) statbuf.st_blocks,
+ (unsigned long int) statbuf.st_mode,
+ (unsigned long int) statbuf.st_uid,
+ (unsigned long int) statbuf.st_gid,
+ (uintmax_t) statbuf.st_dev,
+ (uintmax_t) statbuf.st_ino,
+ (unsigned long int) statbuf.st_nlink,
+ (unsigned long int) major(statbuf.st_rdev),
+ (unsigned long int) minor(statbuf.st_rdev),
+ (unsigned long int) statbuf.st_atime,
+ (unsigned long int) statbuf.st_mtime,
+ (unsigned long int) statbuf.st_ctime,
+ (unsigned long int) statbuf.st_blksize
+ );
+#if ENABLE_SELINUX
+ if (flags & OPT_SELINUX)
+ printf(" %lc\n", *scontext);
+ else
+ putchar('\n');
+#endif
+ } else {
+ char *linkname = NULL;
+
+ struct passwd *pw_ent;
+ struct group *gw_ent;
+ setgrent();
+ gw_ent = getgrgid(statbuf.st_gid);
+ setpwent();
+ pw_ent = getpwuid(statbuf.st_uid);
+
+ if (S_ISLNK(statbuf.st_mode))
+ linkname = xmalloc_readlink_or_warn(filename);
+ if (linkname)
+ printf(" File: \"%s\" -> \"%s\"\n", filename, linkname);
+ else
+ printf(" File: \"%s\"\n", filename);
+
+ printf(" Size: %-10ju\tBlocks: %-10ju IO Block: %-6lu %s\n"
+ "Device: %jxh/%jud\tInode: %-10ju Links: %-5lu",
+ (uintmax_t) (statbuf.st_size),
+ (uintmax_t) statbuf.st_blocks,
+ (unsigned long int) statbuf.st_blksize,
+ file_type(&statbuf),
+ (uintmax_t) statbuf.st_dev,
+ (uintmax_t) statbuf.st_dev,
+ (uintmax_t) statbuf.st_ino,
+ (unsigned long int) statbuf.st_nlink);
+ if (S_ISBLK(statbuf.st_mode) || S_ISCHR(statbuf.st_mode))
+ printf(" Device type: %lx,%lx\n",
+ (unsigned long int) major(statbuf.st_rdev),
+ (unsigned long int) minor(statbuf.st_rdev));
+ else
+ putchar('\n');
+ printf("Access: (%04lo/%10.10s) Uid: (%5lu/%8s) Gid: (%5lu/%8s)\n",
+ (unsigned long int) (statbuf.st_mode & (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)),
+ bb_mode_string(statbuf.st_mode),
+ (unsigned long int) statbuf.st_uid,
+ (pw_ent != 0L) ? pw_ent->pw_name : "UNKNOWN",
+ (unsigned long int) statbuf.st_gid,
+ (gw_ent != 0L) ? gw_ent->gr_name : "UNKNOWN");
+#if ENABLE_SELINUX
+ printf(" S_Context: %lc\n", *scontext);
+#endif
+ printf("Access: %s\n" "Modify: %s\n" "Change: %s\n",
+ human_time(statbuf.st_atime),
+ human_time(statbuf.st_mtime),
+ human_time(statbuf.st_ctime));
+ }
+#endif /* FEATURE_STAT_FORMAT */
+ return 1;
+}
+
+int stat_main(int argc, char **argv);
+int stat_main(int argc, char **argv)
+{
+ int i;
+ char *format = NULL;
+ int ok = 1;
+ int (*statfunc)(char const *, char const *) = do_stat;
+
+ flags = getopt32(argc, argv, "ftL"
+ USE_SELINUX("Z")
+ USE_FEATURE_STAT_FORMAT("c:", &format)
+ );
+
+ if (flags & 1) /* -f */
+ statfunc = do_statfs;
+ if (argc == optind) /* files */
+ bb_show_usage();
+
+#if ENABLE_SELINUX
+ if (flags & OPT_SELINUX) {
+ selinux_or_die();
+ }
+#endif /* ENABLE_SELINUX */
+ for (i = optind; i < argc; ++i)
+ ok &= statfunc(argv[i], format);
+
+ return (ok ? EXIT_SUCCESS : EXIT_FAILURE);
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/stty.c b/i/pc104/initrd/conf/busybox/coreutils/stty.c
new file mode 100644
index 0000000..15cb052
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/stty.c
@@ -0,0 +1,1189 @@
+/* vi: set sw=4 ts=4: */
+/* stty -- change and print terminal line settings
+ Copyright (C) 1990-1999 Free Software Foundation, Inc.
+
+ Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+*/
+/* Usage: stty [-ag] [-F device] [setting...]
+
+ Options:
+ -a Write all current settings to stdout in human-readable form.
+ -g Write all current settings to stdout in stty-readable form.
+ -F Open and use the specified device instead of stdin
+
+ If no args are given, write to stdout the baud rate and settings that
+ have been changed from their defaults. Mode reading and changes
+ are done on the specified device, or stdin if none was specified.
+
+ David MacKenzie <djm@gnu.ai.mit.edu>
+
+ Special for busybox ported by Vladimir Oleynik <dzo@simtreas.ru> 2001
+
+ */
+
+#include "busybox.h"
+
+#ifndef _POSIX_VDISABLE
+# define _POSIX_VDISABLE ((unsigned char) 0)
+#endif
+
+#define Control(c) ((c) & 0x1f)
+/* Canonical values for control characters */
+#ifndef CINTR
+# define CINTR Control('c')
+#endif
+#ifndef CQUIT
+# define CQUIT 28
+#endif
+#ifndef CERASE
+# define CERASE 127
+#endif
+#ifndef CKILL
+# define CKILL Control('u')
+#endif
+#ifndef CEOF
+# define CEOF Control('d')
+#endif
+#ifndef CEOL
+# define CEOL _POSIX_VDISABLE
+#endif
+#ifndef CSTART
+# define CSTART Control('q')
+#endif
+#ifndef CSTOP
+# define CSTOP Control('s')
+#endif
+#ifndef CSUSP
+# define CSUSP Control('z')
+#endif
+#if defined(VEOL2) && !defined(CEOL2)
+# define CEOL2 _POSIX_VDISABLE
+#endif
+/* ISC renamed swtch to susp for termios, but we'll accept either name */
+#if defined(VSUSP) && !defined(VSWTCH)
+# define VSWTCH VSUSP
+# define CSWTCH CSUSP
+#endif
+#if defined(VSWTCH) && !defined(CSWTCH)
+# define CSWTCH _POSIX_VDISABLE
+#endif
+
+/* SunOS 5.3 loses (^Z doesn't work) if 'swtch' is the same as 'susp'.
+ So the default is to disable 'swtch.' */
+#if defined (__sparc__) && defined (__svr4__)
+# undef CSWTCH
+# define CSWTCH _POSIX_VDISABLE
+#endif
+
+#if defined(VWERSE) && !defined (VWERASE) /* AIX-3.2.5 */
+# define VWERASE VWERSE
+#endif
+#if defined(VDSUSP) && !defined (CDSUSP)
+# define CDSUSP Control('y')
+#endif
+#if !defined(VREPRINT) && defined(VRPRNT) /* Irix 4.0.5 */
+# define VREPRINT VRPRNT
+#endif
+#if defined(VREPRINT) && !defined(CRPRNT)
+# define CRPRNT Control('r')
+#endif
+#if defined(VWERASE) && !defined(CWERASE)
+# define CWERASE Control('w')
+#endif
+#if defined(VLNEXT) && !defined(CLNEXT)
+# define CLNEXT Control('v')
+#endif
+#if defined(VDISCARD) && !defined(VFLUSHO)
+# define VFLUSHO VDISCARD
+#endif
+#if defined(VFLUSH) && !defined(VFLUSHO) /* Ultrix 4.2 */
+# define VFLUSHO VFLUSH
+#endif
+#if defined(CTLECH) && !defined(ECHOCTL) /* Ultrix 4.3 */
+# define ECHOCTL CTLECH
+#endif
+#if defined(TCTLECH) && !defined(ECHOCTL) /* Ultrix 4.2 */
+# define ECHOCTL TCTLECH
+#endif
+#if defined(CRTKIL) && !defined(ECHOKE) /* Ultrix 4.2 and 4.3 */
+# define ECHOKE CRTKIL
+#endif
+#if defined(VFLUSHO) && !defined(CFLUSHO)
+# define CFLUSHO Control('o')
+#endif
+#if defined(VSTATUS) && !defined(CSTATUS)
+# define CSTATUS Control('t')
+#endif
+
+/* Which speeds to set */
+enum speed_setting {
+ input_speed, output_speed, both_speeds
+};
+
+/* Which member(s) of 'struct termios' a mode uses */
+enum {
+ /* Do NOT change the order or values, as mode_type_flag()
+ * depends on them */
+ control, input, output, local, combination
+};
+
+static const char evenp [] = "evenp";
+static const char raw [] = "raw";
+static const char stty_min [] = "min";
+static const char stty_time [] = "time";
+static const char stty_swtch[] = "swtch";
+static const char stty_eol [] = "eol";
+static const char stty_eof [] = "eof";
+static const char parity [] = "parity";
+static const char stty_oddp [] = "oddp";
+static const char stty_nl [] = "nl";
+static const char stty_ek [] = "ek";
+static const char stty_sane [] = "sane";
+static const char cbreak [] = "cbreak";
+static const char stty_pass8[] = "pass8";
+static const char litout [] = "litout";
+static const char cooked [] = "cooked";
+static const char decctlq [] = "decctlq";
+static const char stty_tabs [] = "tabs";
+static const char stty_lcase[] = "lcase";
+static const char stty_LCASE[] = "LCASE";
+static const char stty_crt [] = "crt";
+static const char stty_dec [] = "dec";
+
+/* Flags for 'struct mode_info' */
+#define SANE_SET 1 /* Set in 'sane' mode */
+#define SANE_UNSET 2 /* Unset in 'sane' mode */
+#define REV 4 /* Can be turned off by prepending '-' */
+#define OMIT 8 /* Don't display value */
+
+/* Each mode */
+struct mode_info {
+ const char * const name; /* Name given on command line */
+ const unsigned char type; /* Which structure element to change */
+ const unsigned char flags; /* Setting and display options */
+ /* were using short here, but ppc32 was unhappy: */
+ const tcflag_t mask; /* Other bits to turn off for this mode */
+ const tcflag_t bits; /* Bits to set for this mode */
+};
+
+/* We can optimize it further by using name[8] instead of char *name */
+/* but beware of "if (info->name == evenp)" checks! */
+/* Need to replace them with "if (info == &mode_info[EVENP_INDX])" */
+
+#define MI_ENTRY(N,T,F,B,M) { N, T, F, M, B }
+
+static const struct mode_info mode_info[] = {
+ MI_ENTRY("parenb", control, REV, PARENB, 0 ),
+ MI_ENTRY("parodd", control, REV, PARODD, 0 ),
+ MI_ENTRY("cs5", control, 0, CS5, CSIZE),
+ MI_ENTRY("cs6", control, 0, CS6, CSIZE),
+ MI_ENTRY("cs7", control, 0, CS7, CSIZE),
+ MI_ENTRY("cs8", control, 0, CS8, CSIZE),
+ MI_ENTRY("hupcl", control, REV, HUPCL, 0 ),
+ MI_ENTRY("hup", control, REV | OMIT, HUPCL, 0 ),
+ MI_ENTRY("cstopb", control, REV, CSTOPB, 0 ),
+ MI_ENTRY("cread", control, SANE_SET | REV, CREAD, 0 ),
+ MI_ENTRY("clocal", control, REV, CLOCAL, 0 ),
+#ifdef CRTSCTS
+ MI_ENTRY("crtscts", control, REV, CRTSCTS, 0 ),
+#endif
+ MI_ENTRY("ignbrk", input, SANE_UNSET | REV, IGNBRK, 0 ),
+ MI_ENTRY("brkint", input, SANE_SET | REV, BRKINT, 0 ),
+ MI_ENTRY("ignpar", input, REV, IGNPAR, 0 ),
+ MI_ENTRY("parmrk", input, REV, PARMRK, 0 ),
+ MI_ENTRY("inpck", input, REV, INPCK, 0 ),
+ MI_ENTRY("istrip", input, REV, ISTRIP, 0 ),
+ MI_ENTRY("inlcr", input, SANE_UNSET | REV, INLCR, 0 ),
+ MI_ENTRY("igncr", input, SANE_UNSET | REV, IGNCR, 0 ),
+ MI_ENTRY("icrnl", input, SANE_SET | REV, ICRNL, 0 ),
+ MI_ENTRY("ixon", input, REV, IXON, 0 ),
+ MI_ENTRY("ixoff", input, SANE_UNSET | REV, IXOFF, 0 ),
+ MI_ENTRY("tandem", input, REV | OMIT, IXOFF, 0 ),
+#ifdef IUCLC
+ MI_ENTRY("iuclc", input, SANE_UNSET | REV, IUCLC, 0 ),
+#endif
+#ifdef IXANY
+ MI_ENTRY("ixany", input, SANE_UNSET | REV, IXANY, 0 ),
+#endif
+#ifdef IMAXBEL
+ MI_ENTRY("imaxbel", input, SANE_SET | REV, IMAXBEL, 0 ),
+#endif
+ MI_ENTRY("opost", output, SANE_SET | REV, OPOST, 0 ),
+#ifdef OLCUC
+ MI_ENTRY("olcuc", output, SANE_UNSET | REV, OLCUC, 0 ),
+#endif
+#ifdef OCRNL
+ MI_ENTRY("ocrnl", output, SANE_UNSET | REV, OCRNL, 0 ),
+#endif
+#ifdef ONLCR
+ MI_ENTRY("onlcr", output, SANE_SET | REV, ONLCR, 0 ),
+#endif
+#ifdef ONOCR
+ MI_ENTRY("onocr", output, SANE_UNSET | REV, ONOCR, 0 ),
+#endif
+#ifdef ONLRET
+ MI_ENTRY("onlret", output, SANE_UNSET | REV, ONLRET, 0 ),
+#endif
+#ifdef OFILL
+ MI_ENTRY("ofill", output, SANE_UNSET | REV, OFILL, 0 ),
+#endif
+#ifdef OFDEL
+ MI_ENTRY("ofdel", output, SANE_UNSET | REV, OFDEL, 0 ),
+#endif
+#ifdef NLDLY
+ MI_ENTRY("nl1", output, SANE_UNSET, NL1, NLDLY),
+ MI_ENTRY("nl0", output, SANE_SET, NL0, NLDLY),
+#endif
+#ifdef CRDLY
+ MI_ENTRY("cr3", output, SANE_UNSET, CR3, CRDLY),
+ MI_ENTRY("cr2", output, SANE_UNSET, CR2, CRDLY),
+ MI_ENTRY("cr1", output, SANE_UNSET, CR1, CRDLY),
+ MI_ENTRY("cr0", output, SANE_SET, CR0, CRDLY),
+#endif
+
+#ifdef TABDLY
+ MI_ENTRY("tab3", output, SANE_UNSET, TAB3, TABDLY),
+ MI_ENTRY("tab2", output, SANE_UNSET, TAB2, TABDLY),
+ MI_ENTRY("tab1", output, SANE_UNSET, TAB1, TABDLY),
+ MI_ENTRY("tab0", output, SANE_SET, TAB0, TABDLY),
+#else
+# ifdef OXTABS
+ MI_ENTRY("tab3", output, SANE_UNSET, OXTABS, 0 ),
+# endif
+#endif
+
+#ifdef BSDLY
+ MI_ENTRY("bs1", output, SANE_UNSET, BS1, BSDLY),
+ MI_ENTRY("bs0", output, SANE_SET, BS0, BSDLY),
+#endif
+#ifdef VTDLY
+ MI_ENTRY("vt1", output, SANE_UNSET, VT1, VTDLY),
+ MI_ENTRY("vt0", output, SANE_SET, VT0, VTDLY),
+#endif
+#ifdef FFDLY
+ MI_ENTRY("ff1", output, SANE_UNSET, FF1, FFDLY),
+ MI_ENTRY("ff0", output, SANE_SET, FF0, FFDLY),
+#endif
+ MI_ENTRY("isig", local, SANE_SET | REV, ISIG, 0 ),
+ MI_ENTRY("icanon", local, SANE_SET | REV, ICANON, 0 ),
+#ifdef IEXTEN
+ MI_ENTRY("iexten", local, SANE_SET | REV, IEXTEN, 0 ),
+#endif
+ MI_ENTRY("echo", local, SANE_SET | REV, ECHO, 0 ),
+ MI_ENTRY("echoe", local, SANE_SET | REV, ECHOE, 0 ),
+ MI_ENTRY("crterase", local, REV | OMIT, ECHOE, 0 ),
+ MI_ENTRY("echok", local, SANE_SET | REV, ECHOK, 0 ),
+ MI_ENTRY("echonl", local, SANE_UNSET | REV, ECHONL, 0 ),
+ MI_ENTRY("noflsh", local, SANE_UNSET | REV, NOFLSH, 0 ),
+#ifdef XCASE
+ MI_ENTRY("xcase", local, SANE_UNSET | REV, XCASE, 0 ),
+#endif
+#ifdef TOSTOP
+ MI_ENTRY("tostop", local, SANE_UNSET | REV, TOSTOP, 0 ),
+#endif
+#ifdef ECHOPRT
+ MI_ENTRY("echoprt", local, SANE_UNSET | REV, ECHOPRT, 0 ),
+ MI_ENTRY("prterase", local, REV | OMIT, ECHOPRT, 0 ),
+#endif
+#ifdef ECHOCTL
+ MI_ENTRY("echoctl", local, SANE_SET | REV, ECHOCTL, 0 ),
+ MI_ENTRY("ctlecho", local, REV | OMIT, ECHOCTL, 0 ),
+#endif
+#ifdef ECHOKE
+ MI_ENTRY("echoke", local, SANE_SET | REV, ECHOKE, 0 ),
+ MI_ENTRY("crtkill", local, REV | OMIT, ECHOKE, 0 ),
+#endif
+ MI_ENTRY(evenp, combination, REV | OMIT, 0, 0 ),
+ MI_ENTRY(parity, combination, REV | OMIT, 0, 0 ),
+ MI_ENTRY(stty_oddp, combination, REV | OMIT, 0, 0 ),
+ MI_ENTRY(stty_nl, combination, REV | OMIT, 0, 0 ),
+ MI_ENTRY(stty_ek, combination, OMIT, 0, 0 ),
+ MI_ENTRY(stty_sane, combination, OMIT, 0, 0 ),
+ MI_ENTRY(cooked, combination, REV | OMIT, 0, 0 ),
+ MI_ENTRY(raw, combination, REV | OMIT, 0, 0 ),
+ MI_ENTRY(stty_pass8, combination, REV | OMIT, 0, 0 ),
+ MI_ENTRY(litout, combination, REV | OMIT, 0, 0 ),
+ MI_ENTRY(cbreak, combination, REV | OMIT, 0, 0 ),
+#ifdef IXANY
+ MI_ENTRY(decctlq, combination, REV | OMIT, 0, 0 ),
+#endif
+#if defined(TABDLY) || defined(OXTABS)
+ MI_ENTRY(stty_tabs, combination, REV | OMIT, 0, 0 ),
+#endif
+#if defined(XCASE) && defined(IUCLC) && defined(OLCUC)
+ MI_ENTRY(stty_lcase, combination, REV | OMIT, 0, 0 ),
+ MI_ENTRY(stty_LCASE, combination, REV | OMIT, 0, 0 ),
+#endif
+ MI_ENTRY(stty_crt, combination, OMIT, 0, 0 ),
+ MI_ENTRY(stty_dec, combination, OMIT, 0, 0 ),
+};
+
+enum {
+ NUM_mode_info = (sizeof(mode_info) / sizeof(mode_info[0]))
+};
+
+/* Control character settings */
+struct control_info {
+ const char * const name; /* Name given on command line */
+ const unsigned char saneval; /* Value to set for 'stty sane' */
+ const unsigned char offset; /* Offset in c_cc */
+};
+
+/* Control characters */
+
+static const struct control_info control_info[] = {
+ {"intr", CINTR, VINTR},
+ {"quit", CQUIT, VQUIT},
+ {"erase", CERASE, VERASE},
+ {"kill", CKILL, VKILL},
+ {stty_eof, CEOF, VEOF},
+ {stty_eol, CEOL, VEOL},
+#ifdef VEOL2
+ {"eol2", CEOL2, VEOL2},
+#endif
+#ifdef VSWTCH
+ {stty_swtch, CSWTCH, VSWTCH},
+#endif
+ {"start", CSTART, VSTART},
+ {"stop", CSTOP, VSTOP},
+ {"susp", CSUSP, VSUSP},
+#ifdef VDSUSP
+ {"dsusp", CDSUSP, VDSUSP},
+#endif
+#ifdef VREPRINT
+ {"rprnt", CRPRNT, VREPRINT},
+#endif
+#ifdef VWERASE
+ {"werase", CWERASE, VWERASE},
+#endif
+#ifdef VLNEXT
+ {"lnext", CLNEXT, VLNEXT},
+#endif
+#ifdef VFLUSHO
+ {"flush", CFLUSHO, VFLUSHO},
+#endif
+#ifdef VSTATUS
+ {"status", CSTATUS, VSTATUS},
+#endif
+ /* These must be last because of the display routines */
+ {stty_min, 1, VMIN},
+ {stty_time, 0, VTIME},
+};
+
+enum {
+ NUM_control_info = (sizeof(control_info) / sizeof(control_info[0]))
+};
+
+/* The width of the screen, for output wrapping */
+static unsigned max_col = 80; /* default */
+/* Current position, to know when to wrap */
+static unsigned current_col;
+static const char *device_name = bb_msg_standard_input;
+
+/* Return a string that is the printable representation of character CH */
+/* Adapted from 'cat' by Torbjorn Granlund */
+static const char *visible(unsigned int ch)
+{
+ static char buf[10];
+ char *bpout = buf;
+
+ if (ch == _POSIX_VDISABLE)
+ return "<undef>";
+
+ if (ch >= 128) {
+ ch -= 128;
+ *bpout++ = 'M';
+ *bpout++ = '-';
+ }
+
+ if (ch < 32) {
+ *bpout++ = '^';
+ *bpout++ = ch + 64;
+ } else if (ch < 127) {
+ *bpout++ = ch;
+ } else {
+ *bpout++ = '^';
+ *bpout++ = '?';
+ }
+
+ *bpout = '\0';
+ return buf;
+}
+
+static tcflag_t *mode_type_flag(unsigned type, const struct termios *mode)
+{
+ static const unsigned char tcflag_offsets[] = {
+ offsetof(struct termios, c_cflag), /* control */
+ offsetof(struct termios, c_iflag), /* input */
+ offsetof(struct termios, c_oflag), /* output */
+ offsetof(struct termios, c_lflag), /* local */
+ };
+
+ if (type <= local) {
+ return (tcflag_t*) (((char*)mode) + tcflag_offsets[type]);
+ }
+ return NULL;
+}
+
+static void set_speed_or_die(enum speed_setting type, const char * const arg,
+ struct termios * const mode)
+{
+ speed_t baud;
+
+ baud = tty_value_to_baud(xatou(arg));
+
+ if (type != output_speed) { /* either input or both */
+ cfsetispeed(mode, baud);
+ }
+ if (type != input_speed) { /* either output or both */
+ cfsetospeed(mode, baud);
+ }
+}
+
+static ATTRIBUTE_NORETURN void perror_on_device_and_die(const char *fmt)
+{
+ bb_perror_msg_and_die(fmt, device_name);
+}
+
+static void perror_on_device(const char *fmt)
+{
+ bb_perror_msg(fmt, device_name);
+}
+
+/* Print format string MESSAGE and optional args.
+ Wrap to next line first if it won't fit.
+ Print a space first unless MESSAGE will start a new line */
+static void wrapf(const char *message, ...)
+{
+ char buf[128];
+ va_list args;
+ int buflen;
+
+ va_start(args, message);
+ buflen = vsnprintf(buf, sizeof(buf), message, args);
+ va_end(args);
+ /* We seem to be called only with suitable lengths, but check if
+ somebody failed to adhere to this assumption just to be sure. */
+ if (!buflen || buflen >= sizeof(buf)) return;
+
+ if (current_col > 0) {
+ current_col++;
+ if (buf[0] != '\n') {
+ if (current_col + buflen >= max_col) {
+ putchar('\n');
+ current_col = 0;
+ } else
+ putchar(' ');
+ }
+ }
+ fputs(buf, stdout);
+ current_col += buflen;
+ if (buf[buflen-1] == '\n')
+ current_col = 0;
+}
+
+static void set_window_size(const int rows, const int cols)
+{
+ struct winsize win = { 0, 0, 0, 0};
+
+ if (ioctl(STDIN_FILENO, TIOCGWINSZ, &win)) {
+ if (errno != EINVAL) {
+ goto bail;
+ }
+ memset(&win, 0, sizeof(win));
+ }
+
+ if (rows >= 0)
+ win.ws_row = rows;
+ if (cols >= 0)
+ win.ws_col = cols;
+
+ if (ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win))
+bail:
+ perror_on_device("%s");
+}
+
+static void display_window_size(const int fancy)
+{
+ const char *fmt_str = "%s\0%s: no size information for this device";
+ unsigned width, height;
+
+ if (get_terminal_width_height(STDIN_FILENO, &width, &height)) {
+ if ((errno != EINVAL) || ((fmt_str += 2), !fancy)) {
+ perror_on_device(fmt_str);
+ }
+ } else {
+ wrapf(fancy ? "rows %d; columns %d;" : "%d %d\n",
+ height, width);
+ }
+}
+
+static const struct suffix_mult stty_suffixes[] = {
+ {"b", 512 },
+ {"k", 1024},
+ {"B", 1024},
+ {NULL, 0 }
+};
+
+static const struct mode_info *find_mode(const char *name)
+{
+ int i;
+ for (i = 0; i < NUM_mode_info; ++i)
+ if (!strcmp(name, mode_info[i].name))
+ return &mode_info[i];
+ return 0;
+}
+
+static const struct control_info *find_control(const char *name)
+{
+ int i;
+ for (i = 0; i < NUM_control_info; ++i)
+ if (!strcmp(name, control_info[i].name))
+ return &control_info[i];
+ return 0;
+}
+
+enum {
+ param_need_arg = 0x80,
+ param_line = 1 | 0x80,
+ param_rows = 2 | 0x80,
+ param_cols = 3 | 0x80,
+ param_size = 4,
+ param_speed = 5,
+ param_ispeed = 6 | 0x80,
+ param_ospeed = 7 | 0x80,
+};
+
+static int find_param(const char * const name)
+{
+ const char * const params[] = {
+ "line",
+ "rows",
+ "cols",
+ "columns",
+ "size",
+ "speed",
+ "ispeed",
+ "ospeed",
+ NULL
+ };
+ int i = index_in_str_array(params, name);
+ if (i < 0)
+ return 0;
+ if (!(i == 4 || i == 5))
+ i |= 0x80;
+
+ return i;
+}
+
+static int recover_mode(const char *arg, struct termios *mode)
+{
+ int i, n;
+ unsigned int chr;
+ unsigned long iflag, oflag, cflag, lflag;
+
+ /* Scan into temporaries since it is too much trouble to figure out
+ the right format for 'tcflag_t' */
+ if (sscanf(arg, "%lx:%lx:%lx:%lx%n",
+ &iflag, &oflag, &cflag, &lflag, &n) != 4)
+ return 0;
+ mode->c_iflag = iflag;
+ mode->c_oflag = oflag;
+ mode->c_cflag = cflag;
+ mode->c_lflag = lflag;
+ arg += n;
+ for (i = 0; i < NCCS; ++i) {
+ if (sscanf(arg, ":%x%n", &chr, &n) != 1)
+ return 0;
+ mode->c_cc[i] = chr;
+ arg += n;
+ }
+
+ /* Fail if there are too many fields */
+ if (*arg != '\0')
+ return 0;
+
+ return 1;
+}
+
+static void display_recoverable(const struct termios *mode,
+ const int ATTRIBUTE_UNUSED dummy)
+{
+ int i;
+ printf("%lx:%lx:%lx:%lx",
+ (unsigned long) mode->c_iflag, (unsigned long) mode->c_oflag,
+ (unsigned long) mode->c_cflag, (unsigned long) mode->c_lflag);
+ for (i = 0; i < NCCS; ++i)
+ printf(":%x", (unsigned int) mode->c_cc[i]);
+ putchar('\n');
+}
+
+static void display_speed(const struct termios *mode, int fancy)
+{
+ //01234567 8 9
+ const char *fmt_str = "%lu %lu\n\0ispeed %lu baud; ospeed %lu baud;";
+ unsigned long ispeed, ospeed;
+
+ ospeed = ispeed = cfgetispeed(mode);
+ if (ispeed == 0 || ispeed == (ospeed = cfgetospeed(mode))) {
+ ispeed = ospeed; /* in case ispeed was 0 */
+ //0123 4 5 6 7 8 9
+ fmt_str = "%lu\n\0\0\0\0\0speed %lu baud;";
+ }
+ if (fancy) fmt_str += 9;
+ wrapf(fmt_str, tty_baud_to_value(ispeed), tty_baud_to_value(ospeed));
+}
+
+static void do_display(const struct termios *mode, const int all)
+{
+ int i;
+ tcflag_t *bitsp;
+ unsigned long mask;
+ int prev_type = control;
+
+ display_speed(mode, 1);
+ if (all)
+ display_window_size(1);
+#ifdef HAVE_C_LINE
+ wrapf("line = %d;\n", mode->c_line);
+#else
+ wrapf("\n");
+#endif
+
+ for (i = 0; control_info[i].name != stty_min; ++i) {
+ /* If swtch is the same as susp, don't print both */
+#if VSWTCH == VSUSP
+ if (control_info[i].name == stty_swtch)
+ continue;
+#endif
+ /* If eof uses the same slot as min, only print whichever applies */
+#if VEOF == VMIN
+ if ((mode->c_lflag & ICANON) == 0
+ && (control_info[i].name == stty_eof
+ || control_info[i].name == stty_eol)) continue;
+#endif
+ wrapf("%s = %s;", control_info[i].name,
+ visible(mode->c_cc[control_info[i].offset]));
+ }
+#if VEOF == VMIN
+ if ((mode->c_lflag & ICANON) == 0)
+#endif
+ wrapf("min = %d; time = %d;", mode->c_cc[VMIN], mode->c_cc[VTIME]);
+ if (current_col) wrapf("\n");
+
+ for (i = 0; i < NUM_mode_info; ++i) {
+ if (mode_info[i].flags & OMIT)
+ continue;
+ if (mode_info[i].type != prev_type) {
+ /* wrapf("\n"); */
+ if (current_col) wrapf("\n");
+ prev_type = mode_info[i].type;
+ }
+
+ bitsp = mode_type_flag(mode_info[i].type, mode);
+ mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
+ if ((*bitsp & mask) == mode_info[i].bits) {
+ if (all || (mode_info[i].flags & SANE_UNSET))
+ wrapf("%s", mode_info[i].name);
+ } else {
+ if ((all && mode_info[i].flags & REV) ||
+ (!all &&
+ (mode_info[i].flags & (SANE_SET | REV)) == (SANE_SET | REV)))
+ wrapf("-%s", mode_info[i].name);
+ }
+ }
+ if (current_col) wrapf("\n");
+}
+
+static void sane_mode(struct termios *mode)
+{
+ int i;
+ tcflag_t *bitsp;
+
+ for (i = 0; i < NUM_control_info; ++i) {
+#if VMIN == VEOF
+ if (control_info[i].name == stty_min)
+ break;
+#endif
+ mode->c_cc[control_info[i].offset] = control_info[i].saneval;
+ }
+
+ for (i = 0; i < NUM_mode_info; ++i) {
+ if (mode_info[i].flags & SANE_SET) {
+ bitsp = mode_type_flag(mode_info[i].type, mode);
+ *bitsp = (*bitsp & ~((unsigned long)mode_info[i].mask))
+ | mode_info[i].bits;
+ } else if (mode_info[i].flags & SANE_UNSET) {
+ bitsp = mode_type_flag(mode_info[i].type, mode);
+ *bitsp = *bitsp & ~((unsigned long)mode_info[i].mask)
+ & ~mode_info[i].bits;
+ }
+ }
+}
+
+/* Save set_mode from #ifdef forest plague */
+#ifndef ONLCR
+#define ONLCR 0
+#endif
+#ifndef OCRNL
+#define OCRNL 0
+#endif
+#ifndef ONLRET
+#define ONLRET 0
+#endif
+#ifndef XCASE
+#define XCASE 0
+#endif
+#ifndef IXANY
+#define IXANY 0
+#endif
+#ifndef TABDLY
+#define TABDLY 0
+#endif
+#ifndef OXTABS
+#define OXTABS 0
+#endif
+#ifndef IUCLC
+#define IUCLC 0
+#endif
+#ifndef OLCUC
+#define OLCUC 0
+#endif
+#ifndef ECHOCTL
+#define ECHOCTL 0
+#endif
+#ifndef ECHOKE
+#define ECHOKE 0
+#endif
+
+static void set_mode(const struct mode_info *info, int reversed,
+ struct termios *mode)
+{
+ tcflag_t *bitsp;
+
+ bitsp = mode_type_flag(info->type, mode);
+
+ if (bitsp) {
+ if (reversed)
+ *bitsp = *bitsp & ~info->mask & ~info->bits;
+ else
+ *bitsp = (*bitsp & ~info->mask) | info->bits;
+ return;
+ }
+
+ /* Combination mode */
+ if (info->name == evenp || info->name == parity) {
+ if (reversed)
+ mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
+ else
+ mode->c_cflag = (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7;
+ } else if (info->name == stty_oddp) {
+ if (reversed)
+ mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
+ else
+ mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB;
+ } else if (info->name == stty_nl) {
+ if (reversed) {
+ mode->c_iflag = (mode->c_iflag | ICRNL) & ~INLCR & ~IGNCR;
+ mode->c_oflag = (mode->c_oflag | ONLCR) & ~OCRNL & ~ONLRET;
+ } else {
+ mode->c_iflag = mode->c_iflag & ~ICRNL;
+ if (ONLCR) mode->c_oflag = mode->c_oflag & ~ONLCR;
+ }
+ } else if (info->name == stty_ek) {
+ mode->c_cc[VERASE] = CERASE;
+ mode->c_cc[VKILL] = CKILL;
+ } else if (info->name == stty_sane) {
+ sane_mode(mode);
+ }
+ else if (info->name == cbreak) {
+ if (reversed)
+ mode->c_lflag |= ICANON;
+ else
+ mode->c_lflag &= ~ICANON;
+ } else if (info->name == stty_pass8) {
+ if (reversed) {
+ mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
+ mode->c_iflag |= ISTRIP;
+ } else {
+ mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
+ mode->c_iflag &= ~ISTRIP;
+ }
+ } else if (info->name == litout) {
+ if (reversed) {
+ mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
+ mode->c_iflag |= ISTRIP;
+ mode->c_oflag |= OPOST;
+ } else {
+ mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
+ mode->c_iflag &= ~ISTRIP;
+ mode->c_oflag &= ~OPOST;
+ }
+ } else if (info->name == raw || info->name == cooked) {
+ if ((info->name[0] == 'r' && reversed)
+ || (info->name[0] == 'c' && !reversed)) {
+ /* Cooked mode */
+ mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
+ mode->c_oflag |= OPOST;
+ mode->c_lflag |= ISIG | ICANON;
+#if VMIN == VEOF
+ mode->c_cc[VEOF] = CEOF;
+#endif
+#if VTIME == VEOL
+ mode->c_cc[VEOL] = CEOL;
+#endif
+ } else {
+ /* Raw mode */
+ mode->c_iflag = 0;
+ mode->c_oflag &= ~OPOST;
+ mode->c_lflag &= ~(ISIG | ICANON | XCASE);
+ mode->c_cc[VMIN] = 1;
+ mode->c_cc[VTIME] = 0;
+ }
+ }
+ else if (IXANY && info->name == decctlq) {
+ if (reversed)
+ mode->c_iflag |= IXANY;
+ else
+ mode->c_iflag &= ~IXANY;
+ }
+ else if (TABDLY && info->name == stty_tabs) {
+ if (reversed)
+ mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3;
+ else
+ mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0;
+ }
+ else if (OXTABS && info->name == stty_tabs) {
+ if (reversed)
+ mode->c_oflag |= OXTABS;
+ else
+ mode->c_oflag &= ~OXTABS;
+ }
+ else if (XCASE && IUCLC && OLCUC
+ && (info->name == stty_lcase || info->name == stty_LCASE)) {
+ if (reversed) {
+ mode->c_lflag &= ~XCASE;
+ mode->c_iflag &= ~IUCLC;
+ mode->c_oflag &= ~OLCUC;
+ } else {
+ mode->c_lflag |= XCASE;
+ mode->c_iflag |= IUCLC;
+ mode->c_oflag |= OLCUC;
+ }
+ }
+ else if (info->name == stty_crt) {
+ mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
+ }
+ else if (info->name == stty_dec) {
+ mode->c_cc[VINTR] = 3; /* ^C */
+ mode->c_cc[VERASE] = 127; /* DEL */
+ mode->c_cc[VKILL] = 21; /* ^U */
+ mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
+ if (IXANY) mode->c_iflag &= ~IXANY;
+ }
+}
+
+static void set_control_char_or_die(const struct control_info *info,
+ const char *arg, struct termios *mode)
+{
+ unsigned char value;
+
+ if (info->name == stty_min || info->name == stty_time)
+ value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
+ else if (arg[0] == '\0' || arg[1] == '\0')
+ value = arg[0];
+ else if (!strcmp(arg, "^-") || !strcmp(arg, "undef"))
+ value = _POSIX_VDISABLE;
+ else if (arg[0] == '^') { /* Ignore any trailing junk (^Cjunk) */
+ value = arg[1] & 0x1f; /* Non-letters get weird results */
+ if (arg[1] == '?')
+ value = 127;
+ } else
+ value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
+ mode->c_cc[info->offset] = value;
+}
+
+#define STTY_require_set_attr (1<<0)
+#define STTY_speed_was_set (1<<1)
+#define STTY_verbose_output (1<<2)
+#define STTY_recoverable_output (1<<3)
+#define STTY_noargs (1<<4)
+int stty_main(int argc, char **argv);
+int stty_main(int argc, char **argv)
+{
+ struct termios mode;
+ void (*output_func)(const struct termios *, const int);
+ const char *file_name = NULL;
+ int display_all = 0;
+ int stty_state;
+ int k;
+
+ stty_state = STTY_noargs;
+ output_func = do_display;
+
+ /* First pass: only parse/verify command line params */
+ k = 0;
+ while (argv[++k]) {
+ const struct mode_info *mp;
+ const struct control_info *cp;
+ const char *arg = argv[k];
+ const char *argnext = argv[k+1];
+ int param;
+
+ if (arg[0] == '-') {
+ int i;
+ mp = find_mode(arg+1);
+ if (mp) {
+ if (!(mp->flags & REV))
+ goto invalid_argument;
+ stty_state &= ~STTY_noargs;
+ continue;
+ }
+ /* It is an option - parse it */
+ i = 0;
+ while (arg[++i]) {
+ switch (arg[i]) {
+ case 'a':
+ stty_state |= STTY_verbose_output;
+ output_func = do_display;
+ display_all = 1;
+ break;
+ case 'g':
+ stty_state |= STTY_recoverable_output;
+ output_func = display_recoverable;
+ break;
+ case 'F':
+ if (file_name)
+ bb_error_msg_and_die("only one device may be specified");
+ file_name = &arg[i+1]; /* "-Fdevice" ? */
+ if (!file_name[0]) { /* nope, "-F device" */
+ int p = k+1; /* argv[p] is argnext */
+ file_name = argnext;
+ if (!file_name)
+ bb_error_msg_and_die(bb_msg_requires_arg, "-F");
+ /* remove -F param from arg[vc] */
+ --argc;
+ while (argv[p]) { argv[p] = argv[p+1]; ++p; }
+ }
+ goto end_option;
+ default:
+ goto invalid_argument;
+ }
+ }
+end_option:
+ continue;
+ }
+
+ mp = find_mode(arg);
+ if (mp) {
+ stty_state &= ~STTY_noargs;
+ continue;
+ }
+
+ cp = find_control(arg);
+ if (cp) {
+ if (!argnext)
+ bb_error_msg_and_die(bb_msg_requires_arg, arg);
+ /* called for the side effect of xfunc death only */
+ set_control_char_or_die(cp, argnext, &mode);
+ stty_state &= ~STTY_noargs;
+ ++k;
+ continue;
+ }
+
+ param = find_param(arg);
+ if (param & param_need_arg) {
+ if (!argnext)
+ bb_error_msg_and_die(bb_msg_requires_arg, arg);
+ ++k;
+ }
+
+ switch (param) {
+#ifdef HAVE_C_LINE
+ case param_line:
+# ifndef TIOCGWINSZ
+ xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
+ break;
+# endif /* else fall-through */
+#endif
+#ifdef TIOCGWINSZ
+ case param_rows:
+ case param_cols:
+ xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
+ break;
+ case param_size:
+#endif
+ case param_speed:
+ break;
+ case param_ispeed:
+ /* called for the side effect of xfunc death only */
+ set_speed_or_die(input_speed, argnext, &mode);
+ break;
+ case param_ospeed:
+ /* called for the side effect of xfunc death only */
+ set_speed_or_die(output_speed, argnext, &mode);
+ break;
+ default:
+ if (recover_mode(arg, &mode) == 1) break;
+ if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) break;
+invalid_argument:
+ bb_error_msg_and_die("invalid argument '%s'", arg);
+ }
+ stty_state &= ~STTY_noargs;
+ }
+
+ /* Specifying both -a and -g is an error */
+ if ((stty_state & (STTY_verbose_output | STTY_recoverable_output)) ==
+ (STTY_verbose_output | STTY_recoverable_output))
+ bb_error_msg_and_die("verbose and stty-readable output styles are mutually exclusive");
+ /* Specifying -a or -g with non-options is an error */
+ if (!(stty_state & STTY_noargs) &&
+ (stty_state & (STTY_verbose_output | STTY_recoverable_output)))
+ bb_error_msg_and_die("modes may not be set when specifying an output style");
+
+ /* Now it is safe to start doing things */
+ if (file_name) {
+ int fd, fdflags;
+ device_name = file_name;
+ fd = xopen(device_name, O_RDONLY | O_NONBLOCK);
+ if (fd != STDIN_FILENO) {
+ dup2(fd, STDIN_FILENO);
+ close(fd);
+ }
+ fdflags = fcntl(STDIN_FILENO, F_GETFL);
+ if (fdflags < 0 ||
+ fcntl(STDIN_FILENO, F_SETFL, fdflags & ~O_NONBLOCK) < 0)
+ perror_on_device_and_die("%s: cannot reset non-blocking mode");
+ }
+
+ /* Initialize to all zeroes so there is no risk memcmp will report a
+ spurious difference in an uninitialized portion of the structure */
+ memset(&mode, 0, sizeof(mode));
+ if (tcgetattr(STDIN_FILENO, &mode))
+ perror_on_device_and_die("%s");
+
+ if (stty_state & (STTY_verbose_output | STTY_recoverable_output | STTY_noargs)) {
+ get_terminal_width_height(STDOUT_FILENO, &max_col, NULL);
+ output_func(&mode, display_all);
+ return EXIT_SUCCESS;
+ }
+
+ /* Second pass: perform actions */
+ k = 0;
+ while (argv[++k]) {
+ const struct mode_info *mp;
+ const struct control_info *cp;
+ const char *arg = argv[k];
+ const char *argnext = argv[k+1];
+ int param;
+
+ if (arg[0] == '-') {
+ mp = find_mode(arg+1);
+ if (mp) {
+ set_mode(mp, 1 /* reversed */, &mode);
+ stty_state |= STTY_require_set_attr;
+ }
+ /* It is an option - already parsed. Skip it */
+ continue;
+ }
+
+ mp = find_mode(arg);
+ if (mp) {
+ set_mode(mp, 0 /* non-reversed */, &mode);
+ stty_state |= STTY_require_set_attr;
+ continue;
+ }
+
+ cp = find_control(arg);
+ if (cp) {
+ ++k;
+ set_control_char_or_die(cp, argnext, &mode);
+ stty_state |= STTY_require_set_attr;
+ continue;
+ }
+
+ param = find_param(arg);
+ if (param & param_need_arg) {
+ ++k;
+ }
+
+ switch (param) {
+#ifdef HAVE_C_LINE
+ case param_line:
+ mode.c_line = xatoul_sfx(argnext, stty_suffixes);
+ stty_state |= STTY_require_set_attr;
+ break;
+#endif
+#ifdef TIOCGWINSZ
+ case param_cols:
+ set_window_size(-1, xatoul_sfx(argnext, stty_suffixes));
+ break;
+ case param_size:
+ display_window_size(0);
+ break;
+ case param_rows:
+ set_window_size(xatoul_sfx(argnext, stty_suffixes), -1);
+ break;
+#endif
+ case param_speed:
+ display_speed(&mode, 0);
+ break;
+ case param_ispeed:
+ set_speed_or_die(input_speed, argnext, &mode);
+ stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
+ break;
+ case param_ospeed:
+ set_speed_or_die(output_speed, argnext, &mode);
+ stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
+ break;
+ default:
+ if (recover_mode(arg, &mode) == 1)
+ stty_state |= STTY_require_set_attr;
+ else /* true: if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) */{
+ set_speed_or_die(both_speeds, arg, &mode);
+ stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
+ } /* else - impossible (caught in the first pass):
+ bb_error_msg_and_die("invalid argument '%s'", arg); */
+ }
+ }
+
+ if (stty_state & STTY_require_set_attr) {
+ struct termios new_mode;
+
+ if (tcsetattr(STDIN_FILENO, TCSADRAIN, &mode))
+ perror_on_device_and_die("%s");
+
+ /* POSIX (according to Zlotnick's book) tcsetattr returns zero if
+ it performs *any* of the requested operations. This means it
+ can report 'success' when it has actually failed to perform
+ some proper subset of the requested operations. To detect
+ this partial failure, get the current terminal attributes and
+ compare them to the requested ones */
+
+ /* Initialize to all zeroes so there is no risk memcmp will report a
+ spurious difference in an uninitialized portion of the structure */
+ memset(&new_mode, 0, sizeof(new_mode));
+ if (tcgetattr(STDIN_FILENO, &new_mode))
+ perror_on_device_and_die("%s");
+
+ if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) {
+#ifdef CIBAUD
+ /* SunOS 4.1.3 (at least) has the problem that after this sequence,
+ tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2);
+ sometimes (m1 != m2). The only difference is in the four bits
+ of the c_cflag field corresponding to the baud rate. To save
+ Sun users a little confusion, don't report an error if this
+ happens. But suppress the error only if we haven't tried to
+ set the baud rate explicitly -- otherwise we'd never give an
+ error for a true failure to set the baud rate */
+
+ new_mode.c_cflag &= (~CIBAUD);
+ if ((stty_state & STTY_speed_was_set)
+ || memcmp(&mode, &new_mode, sizeof(mode)) != 0)
+#endif
+ perror_on_device_and_die("%s: cannot perform all requested operations");
+ }
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/sum.c b/i/pc104/initrd/conf/busybox/coreutils/sum.c
new file mode 100644
index 0000000..9327ca5
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/sum.c
@@ -0,0 +1,100 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * sum -- checksum and count the blocks in a file
+ * Like BSD sum or SysV sum -r, except like SysV sum if -s option is given.
+ *
+ * Copyright (C) 86, 89, 91, 1995-2002, 2004 Free Software Foundation, Inc.
+ * Copyright (C) 2005 by Erik Andersen <andersen@codepoet.org>
+ * Copyright (C) 2005 by Mike Frysinger <vapier@gentoo.org>
+ *
+ * Written by Kayvan Aghaiepour and David MacKenzie
+ * Taken from coreutils and turned into a busybox applet by Mike Frysinger
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ */
+
+#include "busybox.h"
+
+enum { SUM_BSD, PRINT_NAME, SUM_SYSV };
+
+/* BSD: calculate and print the rotated checksum and the size in 1K blocks
+ The checksum varies depending on sizeof (int). */
+/* SYSV: calculate and print the checksum and the size in 512-byte blocks */
+/* Return 1 if successful. */
+static unsigned sum_file(const char *file, const unsigned type)
+{
+#define buf bb_common_bufsiz1
+ uintmax_t total_bytes = 0;
+ int fd = 0, r;
+
+ /* The sum of all the input bytes, modulo (UINT_MAX + 1). */
+ unsigned s = 0;
+
+ if (NOT_LONE_DASH(file)) {
+ fd = open(file, O_RDONLY);
+ if (fd == -1)
+ goto ret_bad;
+ }
+
+ while (1) {
+ size_t bytes_read = safe_read(fd, buf, BUFSIZ);
+
+ if ((ssize_t)bytes_read <= 0) {
+ r = (fd && close(fd) != 0);
+ if (!bytes_read && !r)
+ /* no error */
+ break;
+ ret_bad:
+ bb_perror_msg(file);
+ return 0;
+ }
+
+ total_bytes += bytes_read;
+ if (type >= SUM_SYSV) {
+ do s += buf[--bytes_read]; while (bytes_read);
+ } else {
+ r = 0;
+ do {
+ s = (s >> 1) + ((s & 1) << 15);
+ s += buf[r++];
+ s &= 0xffff; /* Keep it within bounds. */
+ } while (--bytes_read);
+ }
+ }
+
+ if (type < PRINT_NAME)
+ file = "";
+ if (type >= SUM_SYSV) {
+ r = (s & 0xffff) + ((s & 0xffffffff) >> 16);
+ s = (r & 0xffff) + (r >> 16);
+ printf("%d %ju %s\n", s, (total_bytes+511)/512, file);
+ } else
+ printf("%05d %5ju %s\n", s, (total_bytes+1023)/1024, file);
+ return 1;
+#undef buf
+}
+
+int sum_main(int argc, char **argv);
+int sum_main(int argc, char **argv)
+{
+ unsigned n;
+ unsigned type = SUM_BSD;
+
+ n = getopt32(argc, argv, "sr");
+ if (n & 1) type = SUM_SYSV;
+ /* give the bsd priority over sysv func */
+ if (n & 2) type = SUM_BSD;
+
+ if (argc == optind) {
+ /* Do not print the name */
+ n = sum_file("-", type);
+ } else {
+ /* Need to print the name if either
+ - more than one file given
+ - doing sysv */
+ type += argc - 1 > optind || type == SUM_SYSV;
+ for (n = 1; optind < argc; optind++)
+ n &= sum_file(argv[optind], type);
+ }
+ return !n;
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/sync.c b/i/pc104/initrd/conf/busybox/coreutils/sync.c
new file mode 100644
index 0000000..536c57a
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/sync.c
@@ -0,0 +1,24 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini sync implementation for busybox
+ *
+ * Copyright (C) 1995, 1996 by Bruce Perens <bruce@pixar.com>.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/* BB_AUDIT SUSv3 N/A -- Matches GNU behavior. */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include "busybox.h"
+
+int sync_main(int argc, char **argv);
+int sync_main(int argc, char **argv)
+{
+ bb_warn_ignoring_args(argc - 1);
+
+ sync();
+
+ return EXIT_SUCCESS;
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/tail.c b/i/pc104/initrd/conf/busybox/coreutils/tail.c
new file mode 100644
index 0000000..52bacb7
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/tail.c
@@ -0,0 +1,278 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini tail implementation for busybox
+ *
+ * Copyright (C) 2001 by Matt Kraai <kraai@alumni.carnegiemellon.edu>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/* BB_AUDIT SUSv3 compliant (need fancy for -c) */
+/* BB_AUDIT GNU compatible -c, -q, and -v options in 'fancy' configuration. */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/tail.html */
+
+/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
+ *
+ * Pretty much rewritten to fix numerous bugs and reduce realloc() calls.
+ * Bugs fixed (although I may have forgotten one or two... it was pretty bad)
+ * 1) mixing printf/write without fflush()ing stdout
+ * 2) no check that any open files are present
+ * 3) optstring had -q taking an arg
+ * 4) no error checking on write in some cases, and a warning even then
+ * 5) q and s interaction bug
+ * 6) no check for lseek error
+ * 7) lseek attempted when count==0 even if arg was +0 (from top)
+ */
+
+#include "busybox.h"
+
+static const struct suffix_mult tail_suffixes[] = {
+ { "b", 512 },
+ { "k", 1024 },
+ { "m", 1024*1024 },
+ { NULL, 0 }
+};
+
+static int status;
+
+static void tail_xprint_header(const char *fmt, const char *filename)
+{
+ if (fdprintf(STDOUT_FILENO, fmt, filename) < 0)
+ bb_perror_nomsg_and_die();
+}
+
+static ssize_t tail_read(int fd, char *buf, size_t count)
+{
+ ssize_t r;
+ off_t current, end;
+ struct stat sbuf;
+
+ end = current = lseek(fd, 0, SEEK_CUR);
+ if (!fstat(fd, &sbuf))
+ end = sbuf.st_size;
+ lseek(fd, end < current ? 0 : current, SEEK_SET);
+ r = safe_read(fd, buf, count);
+ if (r < 0) {
+ bb_perror_msg(bb_msg_read_error);
+ status = EXIT_FAILURE;
+ }
+
+ return r;
+}
+
+static const char header_fmt[] = "\n==> %s <==\n";
+
+static unsigned eat_num(const char *p) {
+ if (*p == '-') p++;
+ else if (*p == '+') { p++; status = 1; }
+ return xatou_sfx(p, tail_suffixes);
+}
+
+int tail_main(int argc, char **argv);
+int tail_main(int argc, char **argv)
+{
+ unsigned count = 10;
+ unsigned sleep_period = 1;
+ bool from_top;
+ int header_threshhold = 1;
+ const char *str_c, *str_n, *str_s;
+
+ char *tailbuf;
+ size_t tailbufsize;
+ int taillen = 0;
+ int newline = 0;
+ int nfiles, nread, nwrite, seen, i, opt;
+
+ int *fds;
+ char *s, *buf;
+ const char *fmt;
+
+#if ENABLE_INCLUDE_SUSv2 || ENABLE_FEATURE_FANCY_TAIL
+ /* Allow legacy syntax of an initial numeric option without -n. */
+ if (argc >= 2 && (argv[1][0] == '+' || argv[1][0] == '-')
+ && isdigit(argv[1][1])
+ ) {
+ argv[0] = (char*)"-n";
+ argv--;
+ argc++;
+ }
+#endif
+
+ opt = getopt32(argc, argv, "fc:n:" USE_FEATURE_FANCY_TAIL("qs:v"), &str_c, &str_n, &str_s);
+#define FOLLOW (opt & 0x1)
+#define COUNT_BYTES (opt & 0x2)
+ //if (opt & 0x1) // -f
+ if (opt & 0x2) count = eat_num(str_c); // -c
+ if (opt & 0x4) count = eat_num(str_n); // -n
+#if ENABLE_FEATURE_FANCY_TAIL
+ if (opt & 0x8) header_threshhold = INT_MAX; // -q
+ if (opt & 0x10) sleep_period = xatou(str_s); // -s
+ if (opt & 0x20) header_threshhold = 0; // -v
+#endif
+ argc -= optind;
+ argv += optind;
+ from_top = status;
+
+ /* open all the files */
+ fds = xmalloc(sizeof(int) * (argc + 1));
+ status = nfiles = i = 0;
+ if (argc == 0) {
+ struct stat statbuf;
+
+ if (!fstat(STDIN_FILENO, &statbuf) && S_ISFIFO(statbuf.st_mode)) {
+ opt &= ~1; /* clear FOLLOW */
+ }
+ *argv = (char *) bb_msg_standard_input;
+ goto DO_STDIN;
+ }
+
+ do {
+ if (NOT_LONE_DASH(argv[i])) {
+ fds[nfiles] = open(argv[i], O_RDONLY);
+ if (fds[nfiles] < 0) {
+ bb_perror_msg("%s", argv[i]);
+ status = EXIT_FAILURE;
+ continue;
+ }
+ } else {
+ DO_STDIN: /* "-" */
+ fds[nfiles] = STDIN_FILENO;
+ }
+ argv[nfiles] = argv[i];
+ ++nfiles;
+ } while (++i < argc);
+
+ if (!nfiles)
+ bb_error_msg_and_die("no files");
+
+ tailbufsize = BUFSIZ;
+
+ /* tail the files */
+ if (!from_top && COUNT_BYTES) {
+ if (tailbufsize < count) {
+ tailbufsize = count + BUFSIZ;
+ }
+ }
+
+ buf = tailbuf = xmalloc(tailbufsize);
+
+ fmt = header_fmt + 1; /* Skip header leading newline on first output. */
+ i = 0;
+ do {
+ /* Be careful. It would be possible to optimize the count-bytes
+ * case if the file is seekable. If you do though, remember that
+ * starting file position may not be the beginning of the file.
+ * Beware of backing up too far. See example in wc.c.
+ */
+ if (!(count | from_top) && lseek(fds[i], 0, SEEK_END) >= 0) {
+ continue;
+ }
+
+ if (nfiles > header_threshhold) {
+ tail_xprint_header(fmt, argv[i]);
+ fmt = header_fmt;
+ }
+
+ buf = tailbuf;
+ taillen = 0;
+ seen = 1;
+ newline = 0;
+
+ while ((nread = tail_read(fds[i], buf, tailbufsize-taillen)) > 0) {
+ if (from_top) {
+ nwrite = nread;
+ if (seen < count) {
+ if (COUNT_BYTES) {
+ nwrite -= (count - seen);
+ seen = count;
+ } else {
+ s = buf;
+ do {
+ --nwrite;
+ if (*s++ == '\n' && ++seen == count) {
+ break;
+ }
+ } while (nwrite);
+ }
+ }
+ xwrite(STDOUT_FILENO, buf + nread - nwrite, nwrite);
+ } else if (count) {
+ if (COUNT_BYTES) {
+ taillen += nread;
+ if (taillen > count) {
+ memmove(tailbuf, tailbuf + taillen - count, count);
+ taillen = count;
+ }
+ } else {
+ int k = nread;
+ int nbuf = 0;
+
+ while (k) {
+ --k;
+ if (buf[k] == '\n') {
+ ++nbuf;
+ }
+ }
+
+ if (newline + nbuf < count) {
+ newline += nbuf;
+ taillen += nread;
+
+ } else {
+ int extra = 0;
+ if (buf[nread-1] != '\n') {
+ extra = 1;
+ }
+
+ k = newline + nbuf + extra - count;
+ s = tailbuf;
+ while (k) {
+ if (*s == '\n') {
+ --k;
+ }
+ ++s;
+ }
+
+ taillen += nread - (s - tailbuf);
+ memmove(tailbuf, s, taillen);
+ newline = count - extra;
+ }
+ if (tailbufsize < taillen + BUFSIZ) {
+ tailbufsize = taillen + BUFSIZ;
+ tailbuf = xrealloc(tailbuf, tailbufsize);
+ }
+ }
+ buf = tailbuf + taillen;
+ }
+ }
+
+ if (!from_top) {
+ xwrite(STDOUT_FILENO, tailbuf, taillen);
+ }
+
+ taillen = 0;
+ } while (++i < nfiles);
+
+ buf = xrealloc(tailbuf, BUFSIZ);
+
+ fmt = NULL;
+
+ if (FOLLOW) while (1) {
+ sleep(sleep_period);
+ i = 0;
+ do {
+ if (nfiles > header_threshhold) {
+ fmt = header_fmt;
+ }
+ while ((nread = tail_read(fds[i], buf, sizeof(buf))) > 0) {
+ if (fmt) {
+ tail_xprint_header(fmt, argv[i]);
+ fmt = NULL;
+ }
+ xwrite(STDOUT_FILENO, buf, nread);
+ }
+ } while (++i < nfiles);
+ }
+
+ return status;
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/tee.c b/i/pc104/initrd/conf/busybox/coreutils/tee.c
new file mode 100644
index 0000000..338232c
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/tee.c
@@ -0,0 +1,101 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * tee implementation for busybox
+ *
+ * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/* BB_AUDIT SUSv3 compliant */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/tee.html */
+
+#include "busybox.h"
+#include <signal.h>
+
+int tee_main(int argc, char **argv);
+int tee_main(int argc, char **argv)
+{
+ const char *mode = "w\0a";
+ FILE **files;
+ FILE **fp;
+ char **names;
+ char **np;
+ char retval;
+#if ENABLE_FEATURE_TEE_USE_BLOCK_IO
+ ssize_t c;
+# define buf bb_common_bufsiz1
+#else
+ int c;
+#endif
+ retval = getopt32(argc, argv, "ia"); /* 'a' must be 2nd */
+ argc -= optind;
+ argv += optind;
+
+ mode += (retval & 2); /* Since 'a' is the 2nd option... */
+
+ if (retval & 1) {
+ signal(SIGINT, SIG_IGN); /* TODO - switch to sigaction. */
+ }
+ retval = EXIT_SUCCESS;
+ /* gnu tee ignores SIGPIPE in case one of the output files is a pipe
+ * that doesn't consume all its input. Good idea... */
+ signal(SIGPIPE, SIG_IGN); /* TODO - switch to sigaction. */
+
+ /* Allocate an array of FILE *'s, with one extra for a sentinal. */
+ fp = files = xzalloc(sizeof(FILE *) * (argc + 2));
+ np = names = argv - 1;
+
+ files[0] = stdout;
+ goto GOT_NEW_FILE;
+ do {
+ *fp = fopen_or_warn(*argv, mode);
+ if (*fp == NULL) {
+ retval = EXIT_FAILURE;
+ continue;
+ }
+ *np = *argv++;
+ GOT_NEW_FILE:
+ setbuf(*fp++, NULL); /* tee must not buffer output. */
+ np++;
+ } while (*argv);
+ /* names[0] will be filled later */
+
+#if ENABLE_FEATURE_TEE_USE_BLOCK_IO
+ while ((c = safe_read(STDIN_FILENO, buf, BUFSIZ)) > 0) {
+ fp = files;
+ do
+ fwrite(buf, 1, c, *fp++);
+ while (*fp);
+ }
+ if (c < 0) { /* Make sure read errors are signaled. */
+ retval = EXIT_FAILURE;
+ }
+#else
+ setvbuf(stdout, NULL, _IONBF, 0);
+ while ((c = getchar()) != EOF) {
+ fp = files;
+ do
+ putc(c, *fp++);
+ while (*fp);
+ }
+#endif
+
+ /* Now we need to check for i/o errors on stdin and the various
+ * output files. Since we know that the first entry in the output
+ * file table is stdout, we can save one "if ferror" test by
+ * setting the first entry to stdin and checking stdout error
+ * status with fflush_stdout_and_exit()... although fflush()ing
+ * is unnecessary here. */
+ np = names;
+ fp = files;
+ names[0] = (char *) bb_msg_standard_input;
+ files[0] = stdin;
+ do { /* Now check for input and output errors. */
+ /* Checking ferror should be sufficient, but we may want to fclose.
+ * If we do, remember not to close stdin! */
+ die_if_ferror(*fp++, *np++);
+ } while (*fp);
+
+ fflush_stdout_and_exit(retval);
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/test.c b/i/pc104/initrd/conf/busybox/coreutils/test.c
new file mode 100644
index 0000000..6e0d5d3
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/test.c
@@ -0,0 +1,586 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * test implementation for busybox
+ *
+ * Copyright (c) by a whole pile of folks:
+ *
+ * test(1); version 7-like -- author Erik Baalbergen
+ * modified by Eric Gisin to be used as built-in.
+ * modified by Arnold Robbins to add SVR3 compatibility
+ * (-x -c -b -p -u -g -k) plus Korn's -L -nt -ot -ef and new -S (socket).
+ * modified by J.T. Conklin for NetBSD.
+ * modified by Herbert Xu to be used as built-in in ash.
+ * modified by Erik Andersen <andersen@codepoet.org> to be used
+ * in busybox.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ *
+ * Original copyright notice states:
+ * "This program is in the Public Domain."
+ */
+
+#include "busybox.h"
+#include <unistd.h>
+#include <ctype.h>
+#include <errno.h>
+#include <string.h>
+#include <setjmp.h>
+
+/* test(1) accepts the following grammar:
+ oexpr ::= aexpr | aexpr "-o" oexpr ;
+ aexpr ::= nexpr | nexpr "-a" aexpr ;
+ nexpr ::= primary | "!" primary
+ primary ::= unary-operator operand
+ | operand binary-operator operand
+ | operand
+ | "(" oexpr ")"
+ ;
+ unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"|
+ "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S";
+
+ binary-operator ::= "="|"=="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
+ "-nt"|"-ot"|"-ef";
+ operand ::= <any legal UNIX file name>
+*/
+
+enum token {
+ EOI,
+ FILRD,
+ FILWR,
+ FILEX,
+ FILEXIST,
+ FILREG,
+ FILDIR,
+ FILCDEV,
+ FILBDEV,
+ FILFIFO,
+ FILSOCK,
+ FILSYM,
+ FILGZ,
+ FILTT,
+ FILSUID,
+ FILSGID,
+ FILSTCK,
+ FILNT,
+ FILOT,
+ FILEQ,
+ FILUID,
+ FILGID,
+ STREZ,
+ STRNZ,
+ STREQ,
+ STRNE,
+ STRLT,
+ STRGT,
+ INTEQ,
+ INTNE,
+ INTGE,
+ INTGT,
+ INTLE,
+ INTLT,
+ UNOT,
+ BAND,
+ BOR,
+ LPAREN,
+ RPAREN,
+ OPERAND
+};
+
+enum token_types {
+ UNOP,
+ BINOP,
+ BUNOP,
+ BBINOP,
+ PAREN
+};
+
+static const struct t_op {
+ const char *op_text;
+ short op_num, op_type;
+} ops[] = {
+ {
+ "-r", FILRD, UNOP}, {
+ "-w", FILWR, UNOP}, {
+ "-x", FILEX, UNOP}, {
+ "-e", FILEXIST, UNOP}, {
+ "-f", FILREG, UNOP}, {
+ "-d", FILDIR, UNOP}, {
+ "-c", FILCDEV, UNOP}, {
+ "-b", FILBDEV, UNOP}, {
+ "-p", FILFIFO, UNOP}, {
+ "-u", FILSUID, UNOP}, {
+ "-g", FILSGID, UNOP}, {
+ "-k", FILSTCK, UNOP}, {
+ "-s", FILGZ, UNOP}, {
+ "-t", FILTT, UNOP}, {
+ "-z", STREZ, UNOP}, {
+ "-n", STRNZ, UNOP}, {
+ "-h", FILSYM, UNOP}, /* for backwards compat */
+ {
+ "-O", FILUID, UNOP}, {
+ "-G", FILGID, UNOP}, {
+ "-L", FILSYM, UNOP}, {
+ "-S", FILSOCK, UNOP}, {
+ "=", STREQ, BINOP}, {
+ "==", STREQ, BINOP}, {
+ "!=", STRNE, BINOP}, {
+ "<", STRLT, BINOP}, {
+ ">", STRGT, BINOP}, {
+ "-eq", INTEQ, BINOP}, {
+ "-ne", INTNE, BINOP}, {
+ "-ge", INTGE, BINOP}, {
+ "-gt", INTGT, BINOP}, {
+ "-le", INTLE, BINOP}, {
+ "-lt", INTLT, BINOP}, {
+ "-nt", FILNT, BINOP}, {
+ "-ot", FILOT, BINOP}, {
+ "-ef", FILEQ, BINOP}, {
+ "!", UNOT, BUNOP}, {
+ "-a", BAND, BBINOP}, {
+ "-o", BOR, BBINOP}, {
+ "(", LPAREN, PAREN}, {
+ ")", RPAREN, PAREN}, {
+ 0, 0, 0}
+};
+
+#ifdef CONFIG_FEATURE_TEST_64
+typedef int64_t arith_t;
+#else
+typedef int arith_t;
+#endif
+
+static char **t_wp;
+static struct t_op const *t_wp_op;
+static gid_t *group_array;
+static int ngroups;
+
+static enum token t_lex(char *s);
+static arith_t oexpr(enum token n);
+static arith_t aexpr(enum token n);
+static arith_t nexpr(enum token n);
+static int binop(void);
+static arith_t primary(enum token n);
+static int filstat(char *nm, enum token mode);
+static arith_t getn(const char *s);
+static int newerf(const char *f1, const char *f2);
+static int olderf(const char *f1, const char *f2);
+static int equalf(const char *f1, const char *f2);
+static int test_eaccess(char *path, int mode);
+static int is_a_group_member(gid_t gid);
+static void initialize_group_array(void);
+
+static jmp_buf leaving;
+
+int bb_test(int argc, char **argv)
+{
+ int res;
+
+ if (LONE_CHAR(argv[0], '[')) {
+ --argc;
+ if (NOT_LONE_CHAR(argv[argc], ']')) {
+ bb_error_msg("missing ]");
+ return 2;
+ }
+ argv[argc] = NULL;
+ } else if (strcmp(argv[0], "[[") == 0) {
+ --argc;
+ if (strcmp(argv[argc], "]]")) {
+ bb_error_msg("missing ]]");
+ return 2;
+ }
+ argv[argc] = NULL;
+ }
+
+ res = setjmp(leaving);
+ if (res)
+ return res;
+
+ /* resetting ngroups is probably unnecessary. it will
+ * force a new call to getgroups(), which prevents using
+ * group data fetched during a previous call. but the
+ * only way the group data could be stale is if there's
+ * been an intervening call to setgroups(), and this
+ * isn't likely in the case of a shell. paranoia
+ * prevails...
+ */
+ ngroups = 0;
+
+ /* Implement special cases from POSIX.2, section 4.62.4 */
+ switch (argc) {
+ case 1:
+ return 1;
+ case 2:
+ return *argv[1] == '\0';
+ case 3:
+ if (argv[1][0] == '!' && argv[1][1] == '\0') {
+ return *argv[2] != '\0';
+ }
+ break;
+ case 4:
+ if (argv[1][0] != '!' || argv[1][1] != '\0') {
+ if (t_lex(argv[2]), t_wp_op && t_wp_op->op_type == BINOP) {
+ t_wp = &argv[1];
+ return binop() == 0;
+ }
+ }
+ break;
+ case 5:
+ if (argv[1][0] == '!' && argv[1][1] == '\0') {
+ if (t_lex(argv[3]), t_wp_op && t_wp_op->op_type == BINOP) {
+ t_wp = &argv[2];
+ return binop() != 0;
+ }
+ }
+ break;
+ }
+
+ t_wp = &argv[1];
+ res = !oexpr(t_lex(*t_wp));
+
+ if (*t_wp != NULL && *++t_wp != NULL) {
+ bb_error_msg("%s: unknown operand", *t_wp);
+ return 2;
+ }
+ return res;
+}
+
+static void syntax(const char *op, const char *msg)
+{
+ if (op && *op) {
+ bb_error_msg("%s: %s", op, msg);
+ } else {
+ bb_error_msg("%s", msg);
+ }
+ longjmp(leaving, 2);
+}
+
+static arith_t oexpr(enum token n)
+{
+ arith_t res;
+
+ res = aexpr(n);
+ if (t_lex(*++t_wp) == BOR) {
+ return oexpr(t_lex(*++t_wp)) || res;
+ }
+ t_wp--;
+ return res;
+}
+
+static arith_t aexpr(enum token n)
+{
+ arith_t res;
+
+ res = nexpr(n);
+ if (t_lex(*++t_wp) == BAND)
+ return aexpr(t_lex(*++t_wp)) && res;
+ t_wp--;
+ return res;
+}
+
+static arith_t nexpr(enum token n)
+{
+ if (n == UNOT)
+ return !nexpr(t_lex(*++t_wp));
+ return primary(n);
+}
+
+static arith_t primary(enum token n)
+{
+ arith_t res;
+
+ if (n == EOI) {
+ syntax(NULL, "argument expected");
+ }
+ if (n == LPAREN) {
+ res = oexpr(t_lex(*++t_wp));
+ if (t_lex(*++t_wp) != RPAREN)
+ syntax(NULL, "closing paren expected");
+ return res;
+ }
+ if (t_wp_op && t_wp_op->op_type == UNOP) {
+ /* unary expression */
+ if (*++t_wp == NULL)
+ syntax(t_wp_op->op_text, "argument expected");
+ switch (n) {
+ case STREZ:
+ return strlen(*t_wp) == 0;
+ case STRNZ:
+ return strlen(*t_wp) != 0;
+ case FILTT:
+ return isatty(getn(*t_wp));
+ default:
+ return filstat(*t_wp, n);
+ }
+ }
+
+ if (t_lex(t_wp[1]), t_wp_op && t_wp_op->op_type == BINOP) {
+ return binop();
+ }
+
+ return strlen(*t_wp) > 0;
+}
+
+static int binop(void)
+{
+ const char *opnd1, *opnd2;
+ struct t_op const *op;
+
+ opnd1 = *t_wp;
+ (void) t_lex(*++t_wp);
+ op = t_wp_op;
+
+ if ((opnd2 = *++t_wp) == (char *) 0)
+ syntax(op->op_text, "argument expected");
+
+ switch (op->op_num) {
+ case STREQ:
+ return strcmp(opnd1, opnd2) == 0;
+ case STRNE:
+ return strcmp(opnd1, opnd2) != 0;
+ case STRLT:
+ return strcmp(opnd1, opnd2) < 0;
+ case STRGT:
+ return strcmp(opnd1, opnd2) > 0;
+ case INTEQ:
+ return getn(opnd1) == getn(opnd2);
+ case INTNE:
+ return getn(opnd1) != getn(opnd2);
+ case INTGE:
+ return getn(opnd1) >= getn(opnd2);
+ case INTGT:
+ return getn(opnd1) > getn(opnd2);
+ case INTLE:
+ return getn(opnd1) <= getn(opnd2);
+ case INTLT:
+ return getn(opnd1) < getn(opnd2);
+ case FILNT:
+ return newerf(opnd1, opnd2);
+ case FILOT:
+ return olderf(opnd1, opnd2);
+ case FILEQ:
+ return equalf(opnd1, opnd2);
+ }
+ /* NOTREACHED */
+ return 1;
+}
+
+static int filstat(char *nm, enum token mode)
+{
+ struct stat s;
+ unsigned int i;
+
+ if (mode == FILSYM) {
+#ifdef S_IFLNK
+ if (lstat(nm, &s) == 0) {
+ i = S_IFLNK;
+ goto filetype;
+ }
+#endif
+ return 0;
+ }
+
+ if (stat(nm, &s) != 0)
+ return 0;
+
+ switch (mode) {
+ case FILRD:
+ return test_eaccess(nm, R_OK) == 0;
+ case FILWR:
+ return test_eaccess(nm, W_OK) == 0;
+ case FILEX:
+ return test_eaccess(nm, X_OK) == 0;
+ case FILEXIST:
+ return 1;
+ case FILREG:
+ i = S_IFREG;
+ goto filetype;
+ case FILDIR:
+ i = S_IFDIR;
+ goto filetype;
+ case FILCDEV:
+ i = S_IFCHR;
+ goto filetype;
+ case FILBDEV:
+ i = S_IFBLK;
+ goto filetype;
+ case FILFIFO:
+#ifdef S_IFIFO
+ i = S_IFIFO;
+ goto filetype;
+#else
+ return 0;
+#endif
+ case FILSOCK:
+#ifdef S_IFSOCK
+ i = S_IFSOCK;
+ goto filetype;
+#else
+ return 0;
+#endif
+ case FILSUID:
+ i = S_ISUID;
+ goto filebit;
+ case FILSGID:
+ i = S_ISGID;
+ goto filebit;
+ case FILSTCK:
+ i = S_ISVTX;
+ goto filebit;
+ case FILGZ:
+ return s.st_size > 0L;
+ case FILUID:
+ return s.st_uid == geteuid();
+ case FILGID:
+ return s.st_gid == getegid();
+ default:
+ return 1;
+ }
+
+ filetype:
+ return ((s.st_mode & S_IFMT) == i);
+
+ filebit:
+ return ((s.st_mode & i) != 0);
+}
+
+static enum token t_lex(char *s)
+{
+ struct t_op const *op = ops;
+
+ if (s == 0) {
+ t_wp_op = (struct t_op *) 0;
+ return EOI;
+ }
+ while (op->op_text) {
+ if (strcmp(s, op->op_text) == 0) {
+ t_wp_op = op;
+ return op->op_num;
+ }
+ op++;
+ }
+ t_wp_op = (struct t_op *) 0;
+ return OPERAND;
+}
+
+/* atoi with error detection */
+static arith_t getn(const char *s)
+{
+ char *p;
+#ifdef CONFIG_FEATURE_TEST_64
+ long long r;
+#else
+ long r;
+#endif
+
+ errno = 0;
+#ifdef CONFIG_FEATURE_TEST_64
+ r = strtoll(s, &p, 10);
+#else
+ r = strtol(s, &p, 10);
+#endif
+
+ if (errno != 0)
+ syntax(s, "out of range");
+
+ if (*(skip_whitespace(p)))
+ syntax(s, "bad number");
+
+ return r;
+}
+
+static int newerf(const char *f1, const char *f2)
+{
+ struct stat b1, b2;
+
+ return (stat(f1, &b1) == 0 &&
+ stat(f2, &b2) == 0 && b1.st_mtime > b2.st_mtime);
+}
+
+static int olderf(const char *f1, const char *f2)
+{
+ struct stat b1, b2;
+
+ return (stat(f1, &b1) == 0 &&
+ stat(f2, &b2) == 0 && b1.st_mtime < b2.st_mtime);
+}
+
+static int equalf(const char *f1, const char *f2)
+{
+ struct stat b1, b2;
+
+ return (stat(f1, &b1) == 0 &&
+ stat(f2, &b2) == 0 &&
+ b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino);
+}
+
+/* Do the same thing access(2) does, but use the effective uid and gid,
+ and don't make the mistake of telling root that any file is
+ executable. */
+static int test_eaccess(char *path, int mode)
+{
+ struct stat st;
+ unsigned int euid = geteuid();
+
+ if (stat(path, &st) < 0)
+ return -1;
+
+ if (euid == 0) {
+ /* Root can read or write any file. */
+ if (mode != X_OK)
+ return 0;
+
+ /* Root can execute any file that has any one of the execute
+ bits set. */
+ if (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
+ return 0;
+ }
+
+ if (st.st_uid == euid) /* owner */
+ mode <<= 6;
+ else if (is_a_group_member(st.st_gid))
+ mode <<= 3;
+
+ if (st.st_mode & mode)
+ return 0;
+
+ return -1;
+}
+
+static void initialize_group_array(void)
+{
+ ngroups = getgroups(0, NULL);
+ if (ngroups > 0) {
+ group_array = xmalloc(ngroups * sizeof(gid_t));
+ getgroups(ngroups, group_array);
+ }
+}
+
+/* Return non-zero if GID is one that we have in our groups list. */
+static int is_a_group_member(gid_t gid)
+{
+ int i;
+
+ /* Short-circuit if possible, maybe saving a call to getgroups(). */
+ if (gid == getgid() || gid == getegid())
+ return 1;
+
+ if (ngroups == 0)
+ initialize_group_array();
+
+ /* Search through the list looking for GID. */
+ for (i = 0; i < ngroups; i++)
+ if (gid == group_array[i])
+ return 1;
+
+ return 0;
+}
+
+
+/* applet entry point */
+
+int test_main(int argc, char **argv);
+int test_main(int argc, char **argv)
+{
+ return bb_test(argc, argv);
+}
+
diff --git a/i/pc104/initrd/conf/busybox/coreutils/touch.c b/i/pc104/initrd/conf/busybox/coreutils/touch.c
new file mode 100644
index 0000000..ae4e689
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/touch.c
@@ -0,0 +1,62 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini touch implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/* BB_AUDIT SUSv3 _NOT_ compliant -- options -a, -m, -r, -t not supported. */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/touch.html */
+
+/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
+ *
+ * Previous version called open() and then utime(). While this will be
+ * be necessary to implement -r and -t, it currently only makes things bigger.
+ * Also, exiting on a failure was a bug. All args should be processed.
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <utime.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include "busybox.h"
+
+int touch_main(int argc, char **argv);
+int touch_main(int argc, char **argv)
+{
+ int fd;
+ int status = EXIT_SUCCESS;
+ bool flags = (getopt32(argc, argv, "c") & 1);
+
+ argv += optind;
+
+ if (!*argv) {
+ bb_show_usage();
+ }
+
+ do {
+ if (utime(*argv, NULL)) {
+ if (errno == ENOENT) { /* no such file*/
+ if (flags) { /* Creation is disabled, so ignore. */
+ continue;
+ }
+ /* Try to create the file. */
+ fd = open(*argv, O_RDWR | O_CREAT,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH
+ );
+ if ((fd >= 0) && !close(fd)) {
+ continue;
+ }
+ }
+ status = EXIT_FAILURE;
+ bb_perror_msg("%s", *argv);
+ }
+ } while (*++argv);
+
+ return status;
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/tr.c b/i/pc104/initrd/conf/busybox/coreutils/tr.c
new file mode 100644
index 0000000..cea3b05
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/tr.c
@@ -0,0 +1,254 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini tr implementation for busybox
+ *
+ ** Copyright (c) 1987,1997, Prentice Hall All rights reserved.
+ *
+ * The name of Prentice Hall may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * Copyright (c) Michiel Huisjes
+ *
+ * This version of tr is adapted from Minix tr and was modified
+ * by Erik Andersen <andersen@codepoet.org> to be used in busybox.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "busybox.h"
+
+// Even with -funsigned-char, gcc still complains about char as an array index.
+
+#define GCC4_IS_STUPID int
+
+#define ASCII 0377
+
+/* some "globals" shared across this file */
+static char com_fl, del_fl, sq_fl;
+/* these last are pointers to static buffers declared in tr_main */
+static char *poutput, *pvector, *pinvec, *poutvec;
+
+static void convert(void)
+{
+ int read_chars = 0, in_index = 0, out_index = 0, c, coded, last = -1;
+
+ for (;;) {
+ // If we're out of input, flush output and read more input.
+
+ if (in_index == read_chars) {
+ if (out_index) {
+ if (write(1, (char *) poutput, out_index) != out_index)
+ bb_error_msg_and_die(bb_msg_write_error);
+ out_index = 0;
+ }
+
+ if ((read_chars = read(0, bb_common_bufsiz1, BUFSIZ)) <= 0) {
+ if (write(1, (char *) poutput, out_index) != out_index)
+ bb_error_msg(bb_msg_write_error);
+ exit(0);
+ }
+ in_index = 0;
+ }
+ c = bb_common_bufsiz1[in_index++];
+ coded = pvector[c];
+ if (del_fl && pinvec[c])
+ continue;
+ if (sq_fl && last == coded && (pinvec[c] || poutvec[coded]))
+ continue;
+ poutput[out_index++] = last = coded;
+ }
+
+ /* NOTREACHED */
+}
+
+static void map(char *string1, unsigned int string1_len,
+ char *string2, unsigned int string2_len)
+{
+ char last = '0';
+ unsigned int i, j;
+
+ for (j = 0, i = 0; i < string1_len; i++) {
+ if (string2_len <= j)
+ pvector[(GCC4_IS_STUPID)string1[i]] = last;
+ else
+ pvector[(GCC4_IS_STUPID)string1[i]] = last = string2[j++];
+ }
+}
+
+/* supported constructs:
+ * Ranges, e.g., [0-9] ==> 0123456789
+ * Escapes, e.g., \a ==> Control-G
+ * Character classes, e.g. [:upper:] ==> A ... Z
+ */
+static unsigned int expand(const char *arg, char *buffer)
+{
+ char *buffer_start = buffer;
+ int i, ac;
+
+ while (*arg) {
+ if (*arg == '\\') {
+ arg++;
+ *buffer++ = bb_process_escape_sequence(&arg);
+ } else if (*(arg+1) == '-') {
+ ac = *(arg+2);
+ if(ac == 0) {
+ *buffer++ = *arg++;
+ continue;
+ }
+ i = *arg;
+ while (i <= ac)
+ *buffer++ = i++;
+ arg += 3; /* Skip the assumed a-z */
+ } else if (*arg == '[') {
+ arg++;
+ i = *arg++;
+ if (ENABLE_FEATURE_TR_CLASSES && i == ':') {
+ if (strncmp(arg, "alpha", 5) == 0) {
+ for (i = 'A'; i <= 'Z'; i++)
+ *buffer++ = i;
+ for (i = 'a'; i <= 'z'; i++)
+ *buffer++ = i;
+ }
+ else if (strncmp(arg, "alnum", 5) == 0) {
+ for (i = '0'; i <= '9'; i++)
+ *buffer++ = i;
+ for (i = 'A'; i <= 'Z'; i++)
+ *buffer++ = i;
+ for (i = 'a'; i <= 'z'; i++)
+ *buffer++ = i;
+ }
+ else if (strncmp(arg, "digit", 5) == 0)
+ for (i = '0'; i <= '9'; i++)
+ *buffer++ = i;
+ else if (strncmp(arg, "lower", 5) == 0)
+ for (i = 'a'; i <= 'z'; i++)
+ *buffer++ = i;
+ else if (strncmp(arg, "upper", 5) == 0)
+ for (i = 'A'; i <= 'Z'; i++)
+ *buffer++ = i;
+ else if (strncmp(arg, "space", 5) == 0) {
+ const char s[] = "\t\n\v\f\r ";
+ strcat((char*)buffer, s);
+ buffer += sizeof(s) - 1;
+ }
+ else if (strncmp(arg, "blank", 5) == 0) {
+ *buffer++ = '\t';
+ *buffer++ = ' ';
+ }
+ /* gcc gives a warning if braces aren't used here */
+ else if (strncmp(arg, "punct", 5) == 0) {
+ for (i = 0; i <= ASCII; i++)
+ if (isprint(i) && (!isalnum(i)) && (!isspace(i)))
+ *buffer++ = i;
+ }
+ else if (strncmp(arg, "cntrl", 5) == 0) {
+ for (i = 0; i <= ASCII; i++)
+ if (iscntrl(i))
+ *buffer++ = i;
+ }
+ else {
+ *buffer++ = '[';
+ *buffer++ = ':';
+ continue;
+ }
+ break;
+ }
+ if (ENABLE_FEATURE_TR_EQUIV && i == '=') {
+ *buffer++ = *arg;
+ /* skip the closing =] */
+ arg += 3;
+ continue;
+ }
+ if (*arg++ != '-') {
+ *buffer++ = '[';
+ arg -= 2;
+ continue;
+ }
+ ac = *arg++;
+ while (i <= ac)
+ *buffer++ = i++;
+ arg++; /* Skip the assumed ']' */
+ } else
+ *buffer++ = *arg++;
+ }
+
+ return (buffer - buffer_start);
+}
+
+static int complement(char *buffer, int buffer_len)
+{
+ short i, j, ix;
+ char conv[ASCII + 2];
+
+ ix = 0;
+ for (i = 0; i <= ASCII; i++) {
+ for (j = 0; j < buffer_len; j++)
+ if (buffer[j] == i)
+ break;
+ if (j == buffer_len)
+ conv[ix++] = i & ASCII;
+ }
+ memcpy(buffer, conv, ix);
+ return ix;
+}
+
+int tr_main(int argc, char **argv);
+int tr_main(int argc, char **argv)
+{
+ unsigned char *ptr;
+ int output_length=0, input_length;
+ int idx = 1;
+ int i;
+ RESERVE_CONFIG_BUFFER(output, BUFSIZ);
+ RESERVE_CONFIG_BUFFER(vector, ASCII+1);
+ RESERVE_CONFIG_BUFFER(invec, ASCII+1);
+ RESERVE_CONFIG_BUFFER(outvec, ASCII+1);
+
+ /* ... but make them available globally */
+ poutput = output;
+ pvector = vector;
+ pinvec = invec;
+ poutvec = outvec;
+
+ if (argc > 1 && argv[idx][0] == '-') {
+ for (ptr = (unsigned char *) &argv[idx][1]; *ptr; ptr++) {
+ switch (*ptr) {
+ case 'c':
+ com_fl = TRUE;
+ break;
+ case 'd':
+ del_fl = TRUE;
+ break;
+ case 's':
+ sq_fl = TRUE;
+ break;
+ default:
+ bb_show_usage();
+ }
+ }
+ idx++;
+ }
+ for (i = 0; i <= ASCII; i++) {
+ vector[i] = i;
+ invec[i] = outvec[i] = FALSE;
+ }
+
+ if (argv[idx] != NULL) {
+ input_length = expand(argv[idx++], bb_common_bufsiz1);
+ if (com_fl)
+ input_length = complement(bb_common_bufsiz1, input_length);
+ if (argv[idx] != NULL) {
+ if (*argv[idx] == '\0')
+ bb_error_msg_and_die("STRING2 cannot be empty");
+ output_length = expand(argv[idx], output);
+ map(bb_common_bufsiz1, input_length, output, output_length);
+ }
+ for (i = 0; i < input_length; i++)
+ invec[(GCC4_IS_STUPID)bb_common_bufsiz1[i]] = TRUE;
+ for (i = 0; i < output_length; i++)
+ outvec[(GCC4_IS_STUPID)output[i]] = TRUE;
+ }
+ convert();
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/true.c b/i/pc104/initrd/conf/busybox/coreutils/true.c
new file mode 100644
index 0000000..b2f3a9b
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/true.c
@@ -0,0 +1,20 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini true implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/* BB_AUDIT SUSv3 compliant */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/true.html */
+
+#include <stdlib.h>
+#include "busybox.h"
+
+int true_main(int argc, char **argv);
+int true_main(int argc, char **argv)
+{
+ return EXIT_SUCCESS;
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/tty.c b/i/pc104/initrd/conf/busybox/coreutils/tty.c
new file mode 100644
index 0000000..c28aa33
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/tty.c
@@ -0,0 +1,44 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * tty implementation for busybox
+ *
+ * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/* BB_AUDIT SUSv3 compliant */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/tty.html */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "busybox.h"
+
+int tty_main(int argc, char **argv);
+int tty_main(int argc, char **argv)
+{
+ const char *s;
+ USE_INCLUDE_SUSv2(int silent;) /* Note: No longer relevant in SUSv3. */
+ int retval;
+
+ xfunc_error_retval = 2; /* SUSv3 requires > 1 for error. */
+
+ USE_INCLUDE_SUSv2(silent = getopt32(argc, argv, "s");)
+
+ /* gnu tty outputs a warning that it is ignoring all args. */
+ bb_warn_ignoring_args(argc - optind);
+
+ retval = 0;
+
+ if ((s = ttyname(0)) == NULL) {
+ /* According to SUSv3, ttyname can on fail with EBADF or ENOTTY.
+ * We know the file descriptor is good, so failure means not a tty. */
+ s = "not a tty";
+ retval = 1;
+ }
+ USE_INCLUDE_SUSv2(if (!silent) puts(s);)
+ SKIP_INCLUDE_SUSv2(puts(s);)
+
+ fflush_stdout_and_exit(retval);
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/uname.c b/i/pc104/initrd/conf/busybox/coreutils/uname.c
new file mode 100644
index 0000000..2faa893
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/uname.c
@@ -0,0 +1,108 @@
+/* vi: set sw=4 ts=4: */
+/* uname -- print system information
+ * Copyright (C) 1989-1999 Free Software Foundation, Inc.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/* BB_AUDIT SUSv3 compliant */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/uname.html */
+
+/* Option Example
+
+ -s, --sysname SunOS
+ -n, --nodename rocky8
+ -r, --release 4.0
+ -v, --version
+ -m, --machine sun
+ -a, --all SunOS rocky8 4.0 sun
+
+ The default behavior is equivalent to `-s'.
+
+ David MacKenzie <djm@gnu.ai.mit.edu> */
+
+/* Busyboxed by Erik Andersen */
+
+/* Further size reductions by Glenn McGrath and Manuel Novoa III. */
+
+/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
+ *
+ * Now does proper error checking on i/o. Plus some further space savings.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/utsname.h>
+#include "busybox.h"
+
+typedef struct {
+ struct utsname name;
+ char processor[8]; /* for "unknown" */
+} uname_info_t;
+
+static const char options[] = "snrvmpa";
+static const unsigned short int utsname_offset[] = {
+ offsetof(uname_info_t,name.sysname),
+ offsetof(uname_info_t,name.nodename),
+ offsetof(uname_info_t,name.release),
+ offsetof(uname_info_t,name.version),
+ offsetof(uname_info_t,name.machine),
+ offsetof(uname_info_t,processor)
+};
+
+int uname_main(int argc, char **argv);
+int uname_main(int argc, char **argv)
+{
+ uname_info_t uname_info;
+#if defined(__sparc__) && defined(__linux__)
+ char *fake_sparc = getenv("FAKE_SPARC");
+#endif
+ const unsigned short int *delta;
+ char toprint;
+
+ toprint = getopt32(argc, argv, options);
+
+ if (argc != optind) {
+ bb_show_usage();
+ }
+
+ if (toprint & (1 << 6)) {
+ toprint = 0x3f;
+ }
+
+ if (toprint == 0) {
+ toprint = 1; /* sysname */
+ }
+
+ if (uname(&uname_info.name) == -1) {
+ bb_error_msg_and_die("cannot get system name");
+ }
+
+#if defined(__sparc__) && defined(__linux__)
+ if ((fake_sparc != NULL)
+ && ((fake_sparc[0] == 'y')
+ || (fake_sparc[0] == 'Y'))) {
+ strcpy(uname_info.name.machine, "sparc");
+ }
+#endif
+
+ strcpy(uname_info.processor, "unknown");
+
+ delta = utsname_offset;
+ do {
+ if (toprint & 1) {
+ printf(((char *)(&uname_info)) + *delta);
+ if (toprint > 1) {
+ putchar(' ');
+ }
+ }
+ ++delta;
+ } while (toprint >>= 1);
+ putchar('\n');
+
+ fflush_stdout_and_exit(EXIT_SUCCESS);
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/uniq.c b/i/pc104/initrd/conf/busybox/coreutils/uniq.c
new file mode 100644
index 0000000..0cc0c83
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/uniq.c
@@ -0,0 +1,104 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * uniq implementation for busybox
+ *
+ * Copyright (C) 2005 Manuel Novoa III <mjn3@codepoet.org>
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ */
+
+/* BB_AUDIT SUSv3 compliant */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/uniq.html */
+
+#include "busybox.h"
+
+static const char uniq_opts[] = "f:s:" "cdu\0\1\2\4";
+
+static FILE *xgetoptfile_uniq_s(char **argv, int read0write2)
+{
+ const char *n;
+
+ if ((n = *argv) != NULL) {
+ if ((*n != '-') || n[1]) {
+ return xfopen(n, "r\0w" + read0write2);
+ }
+ }
+ return (read0write2) ? stdout : stdin;
+}
+
+int uniq_main(int argc, char **argv);
+int uniq_main(int argc, char **argv)
+{
+ FILE *in, *out;
+ unsigned long dups, skip_fields, skip_chars, i, uniq_flags;
+ const char *s0, *e0, *s1, *e1, *input_filename;
+ int opt;
+
+ uniq_flags = skip_fields = skip_chars = 0;
+
+ while ((opt = getopt(argc, argv, uniq_opts)) > 0) {
+ if ((opt == 'f') || (opt == 's')) {
+ unsigned long t = xatoul(optarg);
+ if (opt == 'f') {
+ skip_fields = t;
+ } else {
+ skip_chars = t;
+ }
+ } else if ((s0 = strchr(uniq_opts, opt)) != NULL) {
+ uniq_flags |= s0[4];
+ } else {
+ bb_show_usage();
+ }
+ }
+
+ input_filename = *(argv += optind);
+
+ in = xgetoptfile_uniq_s(argv, 0);
+ if (*argv) {
+ ++argv;
+ }
+ out = xgetoptfile_uniq_s(argv, 2);
+ if (*argv && argv[1]) {
+ bb_show_usage();
+ }
+
+ s1 = e1 = NULL; /* prime the pump */
+
+ do {
+ s0 = s1;
+ e0 = e1;
+ dups = 0;
+
+ /* gnu uniq ignores newlines */
+ while ((s1 = xmalloc_getline(in)) != NULL) {
+ e1 = s1;
+ for (i = skip_fields; i; i--) {
+ e1 = skip_whitespace(e1);
+ while (*e1 && !isspace(*e1)) {
+ ++e1;
+ }
+ }
+ for (i = skip_chars; *e1 && i; i--) {
+ ++e1;
+ }
+
+ if (!s0 || strcmp(e0, e1)) {
+ break;
+ }
+
+ ++dups; /* Note: Testing for overflow seems excessive. */
+ }
+
+ if (s0) {
+ if (!(uniq_flags & (2 << !!dups))) {
+ fprintf(out, "\0%d " + (uniq_flags & 1), dups + 1);
+ fprintf(out, "%s\n", s0);
+ }
+ free((void *)s0);
+ }
+ } while (s1);
+
+ die_if_ferror(in, input_filename);
+
+ fflush_stdout_and_exit(EXIT_SUCCESS);
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/usleep.c b/i/pc104/initrd/conf/busybox/coreutils/usleep.c
new file mode 100644
index 0000000..7dd9146
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/usleep.c
@@ -0,0 +1,29 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * usleep implementation for busybox
+ *
+ * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/* BB_AUDIT SUSv3 N/A -- Apparently a busybox extension. */
+
+#include <stdlib.h>
+#include <limits.h>
+#include <unistd.h>
+#include "busybox.h"
+
+int usleep_main(int argc, char **argv);
+int usleep_main(int argc, char **argv)
+{
+ if (argc != 2) {
+ bb_show_usage();
+ }
+
+ if (usleep(xatou(argv[1]))) {
+ bb_perror_nomsg_and_die();
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/uudecode.c b/i/pc104/initrd/conf/busybox/coreutils/uudecode.c
new file mode 100644
index 0000000..c195a3e
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/uudecode.c
@@ -0,0 +1,181 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Copyright 2003, Glenn McGrath <bug1@iinet.net.au>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ *
+ * Based on specification from
+ * http://www.opengroup.org/onlinepubs/007904975/utilities/uuencode.html
+ *
+ * Bugs: the spec doesn't mention anything about "`\n`\n" prior to the
+ * "end" line
+ */
+
+
+#include "busybox.h"
+
+static void read_stduu(FILE *src_stream, FILE *dst_stream)
+{
+ char *line;
+
+ while ((line = xmalloc_getline(src_stream)) != NULL) {
+ int length;
+ char *line_ptr = line;
+
+ if (strcmp(line, "end") == 0) {
+ return;
+ }
+ length = ((*line_ptr - 0x20) & 0x3f)* 4 / 3;
+
+ if (length <= 0) {
+ /* Ignore the "`\n" line, why is it even in the encode file ? */
+ continue;
+ }
+ if (length > 60) {
+ bb_error_msg_and_die("line too long");
+ }
+
+ line_ptr++;
+ /* Tolerate an overly long line to accomodate a possible exta '`' */
+ if (strlen(line_ptr) < (size_t)length) {
+ bb_error_msg_and_die("short file");
+ }
+
+ while (length > 0) {
+ /* Merge four 6 bit chars to three 8 bit chars */
+ fputc(((line_ptr[0] - 0x20) & 077) << 2 | ((line_ptr[1] - 0x20) & 077) >> 4, dst_stream);
+ line_ptr++;
+ length--;
+ if (length == 0) {
+ break;
+ }
+
+ fputc(((line_ptr[0] - 0x20) & 077) << 4 | ((line_ptr[1] - 0x20) & 077) >> 2, dst_stream);
+ line_ptr++;
+ length--;
+ if (length == 0) {
+ break;
+ }
+
+ fputc(((line_ptr[0] - 0x20) & 077) << 6 | ((line_ptr[1] - 0x20) & 077), dst_stream);
+ line_ptr += 2;
+ length -= 2;
+ }
+ free(line);
+ }
+ bb_error_msg_and_die("short file");
+}
+
+static void read_base64(FILE *src_stream, FILE *dst_stream)
+{
+ static const char base64_table[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\n";
+ int term_count = 0;
+
+ while (1) {
+ char translated[4];
+ int count = 0;
+
+ while (count < 4) {
+ char *table_ptr;
+ int ch;
+
+ /* Get next _valid_ character */
+ do {
+ ch = fgetc(src_stream);
+ if (ch == EOF) {
+ bb_error_msg_and_die("short file");
+ }
+ } while ((table_ptr = strchr(base64_table, ch)) == NULL);
+
+ /* Convert encoded charcter to decimal */
+ ch = table_ptr - base64_table;
+
+ if (*table_ptr == '=') {
+ if (term_count == 0) {
+ translated[count] = 0;
+ break;
+ }
+ term_count++;
+ }
+ else if (*table_ptr == '\n') {
+ /* Check for terminating line */
+ if (term_count == 5) {
+ return;
+ }
+ term_count = 1;
+ continue;
+ } else {
+ translated[count] = ch;
+ count++;
+ term_count = 0;
+ }
+ }
+
+ /* Merge 6 bit chars to 8 bit */
+ fputc(translated[0] << 2 | translated[1] >> 4, dst_stream);
+ if (count > 2) {
+ fputc(translated[1] << 4 | translated[2] >> 2, dst_stream);
+ }
+ if (count > 3) {
+ fputc(translated[2] << 6 | translated[3], dst_stream);
+ }
+ }
+}
+
+int uudecode_main(int argc, char **argv);
+int uudecode_main(int argc, char **argv)
+{
+ FILE *src_stream;
+ char *outname = NULL;
+ char *line;
+
+ getopt32(argc, argv, "o:", &outname);
+
+ if (optind == argc) {
+ src_stream = stdin;
+ } else if (optind + 1 == argc) {
+ src_stream = xfopen(argv[optind], "r");
+ } else {
+ bb_show_usage();
+ }
+
+ /* Search for the start of the encoding */
+ while ((line = xmalloc_getline(src_stream)) != NULL) {
+ void (*decode_fn_ptr)(FILE * src, FILE * dst);
+ char *line_ptr;
+ FILE *dst_stream;
+ int mode;
+
+ if (strncmp(line, "begin-base64 ", 13) == 0) {
+ line_ptr = line + 13;
+ decode_fn_ptr = read_base64;
+ } else if (strncmp(line, "begin ", 6) == 0) {
+ line_ptr = line + 6;
+ decode_fn_ptr = read_stduu;
+ } else {
+ free(line);
+ continue;
+ }
+
+ mode = strtoul(line_ptr, NULL, 8);
+ if (outname == NULL) {
+ outname = strchr(line_ptr, ' ');
+ if ((outname == NULL) || (*outname == '\0')) {
+ break;
+ }
+ outname++;
+ }
+ if (LONE_DASH(outname)) {
+ dst_stream = stdout;
+ } else {
+ dst_stream = xfopen(outname, "w");
+ chmod(outname, mode & (S_IRWXU | S_IRWXG | S_IRWXO));
+ }
+ free(line);
+ decode_fn_ptr(src_stream, dst_stream);
+ fclose_if_not_stdin(src_stream);
+ return EXIT_SUCCESS;
+ }
+ bb_error_msg_and_die("no 'begin' line");
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/uuencode.c b/i/pc104/initrd/conf/busybox/coreutils/uuencode.c
new file mode 100644
index 0000000..3207d72
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/uuencode.c
@@ -0,0 +1,76 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Copyright (C) 2000 by Glenn McGrath
+ *
+ * based on the function base64_encode from http.c in wget v1.6
+ * Copyright (C) 1995, 1996, 1997, 1998, 2000 Free Software Foundation, Inc.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "busybox.h"
+
+
+#define SRC_BUF_SIZE 45 // This *MUST* be a multiple of 3
+#define DST_BUF_SIZE 4 * ((SRC_BUF_SIZE + 2) / 3)
+int uuencode_main(int argc, char **argv);
+int uuencode_main(int argc, char **argv)
+{
+ const size_t src_buf_size = SRC_BUF_SIZE;
+ const size_t dst_buf_size = DST_BUF_SIZE;
+ size_t write_size = dst_buf_size;
+ struct stat stat_buf;
+ FILE *src_stream = stdin;
+ const char *tbl;
+ size_t size;
+ mode_t mode;
+ RESERVE_CONFIG_BUFFER(src_buf, SRC_BUF_SIZE + 1);
+ RESERVE_CONFIG_BUFFER(dst_buf, DST_BUF_SIZE + 1);
+
+ tbl = bb_uuenc_tbl_std;
+ if (getopt32(argc, argv, "m") & 1) {
+ tbl = bb_uuenc_tbl_base64;
+ }
+
+ switch (argc - optind) {
+ case 2:
+ src_stream = xfopen(argv[optind], "r");
+ xstat(argv[optind], &stat_buf);
+ mode = stat_buf.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO);
+ if (src_stream == stdout) {
+ puts("NULL");
+ }
+ break;
+ case 1:
+ mode = 0666 & ~umask(0666);
+ break;
+ default:
+ bb_show_usage();
+ }
+
+ printf("begin%s %o %s", tbl == bb_uuenc_tbl_std ? "" : "-base64", mode, argv[argc - 1]);
+
+ while ((size = fread(src_buf, 1, src_buf_size, src_stream)) > 0) {
+ if (size != src_buf_size) {
+ /* write_size is always 60 until the last line */
+ write_size = (4 * ((size + 2) / 3));
+ /* pad with 0s so we can just encode extra bits */
+ memset(&src_buf[size], 0, src_buf_size - size);
+ }
+ /* Encode the buffer we just read in */
+ bb_uuencode((unsigned char*)src_buf, dst_buf, size, tbl);
+
+ putchar('\n');
+ if (tbl == bb_uuenc_tbl_std) {
+ putchar(tbl[size]);
+ }
+ if (fwrite(dst_buf, 1, write_size, stdout) != write_size) {
+ bb_perror_msg_and_die(bb_msg_write_error);
+ }
+ }
+ printf(tbl == bb_uuenc_tbl_std ? "\n`\nend\n" : "\n====\n");
+
+ die_if_ferror(src_stream, "source"); /* TODO - Fix this! */
+
+ fflush_stdout_and_exit(EXIT_SUCCESS);
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/watch.c b/i/pc104/initrd/conf/busybox/coreutils/watch.c
new file mode 100644
index 0000000..60a4a71
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/watch.c
@@ -0,0 +1,80 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini watch implementation for busybox
+ *
+ * Copyright (C) 2001 by Michael Habermann <mhabermann@gmx.de>
+ * Copyrigjt (C) Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/* BB_AUDIT SUSv3 N/A */
+/* BB_AUDIT GNU defects -- only option -n is supported. */
+
+#include "busybox.h"
+
+// procps 2.0.18:
+// watch [-d] [-n seconds]
+// [--differences[=cumulative]] [--interval=seconds] command
+//
+// procps-3.2.3:
+// watch [-dt] [-n seconds]
+// [--differences[=cumulative]] [--interval=seconds] [--no-title] command
+//
+// (procps 3.x and procps 2.x are forks, not newer/older versions of the same)
+
+int watch_main(int argc, char **argv);
+int watch_main(int argc, char **argv)
+{
+ unsigned opt;
+ unsigned period = 2;
+ unsigned cmdlen = 1; // 1 for terminal NUL
+ char *header = NULL;
+ char *cmd;
+ char *tmp;
+ char **p;
+
+ opt_complementary = "-1"; // at least one param please
+ opt = getopt32(argc, argv, "+dtn:", &tmp);
+ //if (opt & 0x1) // -d (ignore)
+ //if (opt & 0x2) // -t
+ if (opt & 0x4) period = xatou(tmp);
+ argv += optind;
+
+ p = argv;
+ while (*p)
+ cmdlen += strlen(*p++) + 1;
+ tmp = cmd = xmalloc(cmdlen);
+ while (*argv) {
+ tmp += sprintf(tmp, " %s", *argv);
+ argv++;
+ }
+ cmd++; // skip initial space
+
+ while (1) {
+ printf("\033[H\033[J");
+ if (!(opt & 0x2)) { // no -t
+ int width, len;
+ char *thyme;
+ time_t t;
+
+ get_terminal_width_height(STDOUT_FILENO, &width, 0);
+ header = xrealloc(header, width--);
+ // '%-*s' pads header with spaces to the full width
+ snprintf(header, width, "Every %ds: %-*s", period, width, cmd);
+ time(&t);
+ thyme = ctime(&t);
+ len = strlen(thyme);
+ if (len < width)
+ strcpy(header + width - len, thyme);
+ puts(header);
+ }
+ fflush(stdout);
+ // TODO: 'real' watch pipes cmd's output to itself
+ // and does not allow it to overflow the screen
+ // (taking into account linewrap!)
+ system(cmd);
+ sleep(period);
+ }
+ return 0; // gcc thinks we can reach this :)
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/wc.c b/i/pc104/initrd/conf/busybox/coreutils/wc.c
new file mode 100644
index 0000000..926b3ac
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/wc.c
@@ -0,0 +1,205 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * wc implementation for busybox
+ *
+ * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/* BB_AUDIT SUSv3 _NOT_ compliant -- option -m is not currently supported. */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/wc.html */
+
+/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
+ *
+ * Rewritten to fix a number of problems and do some size optimizations.
+ * Problems in the previous busybox implementation (besides bloat) included:
+ * 1) broken 'wc -c' optimization (read note below)
+ * 2) broken handling of '-' args
+ * 3) no checking of ferror on EOF returns
+ * 4) isprint() wasn't considered when word counting.
+ *
+ * TODO:
+ *
+ * When locale support is enabled, count multibyte chars in the '-m' case.
+ *
+ * NOTES:
+ *
+ * The previous busybox wc attempted an optimization using stat for the
+ * case of counting chars only. I omitted that because it was broken.
+ * It didn't take into account the possibility of input coming from a
+ * pipe, or input from a file with file pointer not at the beginning.
+ *
+ * To implement such a speed optimization correctly, not only do you
+ * need the size, but also the file position. Note also that the
+ * file position may be past the end of file. Consider the example
+ * (adapted from example in gnu wc.c)
+ *
+ * echo hello > /tmp/testfile &&
+ * (dd ibs=1k skip=1 count=0 &> /dev/null ; wc -c) < /tmp/testfile
+ *
+ * for which 'wc -c' should output '0'.
+ */
+
+#include "busybox.h"
+
+#ifdef CONFIG_LOCALE_SUPPORT
+#define isspace_given_isprint(c) isspace(c)
+#else
+#undef isspace
+#undef isprint
+#define isspace(c) ((((c) == ' ') || (((unsigned int)((c) - 9)) <= (13 - 9))))
+#define isprint(c) (((unsigned int)((c) - 0x20)) <= (0x7e - 0x20))
+#define isspace_given_isprint(c) ((c) == ' ')
+#endif
+
+#if ENABLE_FEATURE_WC_LARGE
+#define COUNT_T unsigned long long
+#define COUNT_FMT "llu"
+#else
+#define COUNT_T unsigned
+#define COUNT_FMT "u"
+#endif
+
+enum {
+ WC_LINES = 0,
+ WC_WORDS = 1,
+ WC_CHARS = 2,
+ WC_LENGTH = 3
+};
+
+int wc_main(int argc, char **argv);
+int wc_main(int argc, char **argv)
+{
+ FILE *fp;
+ const char *s, *arg;
+ const char *start_fmt = "%9"COUNT_FMT;
+ const char *fname_fmt = " %s\n";
+ COUNT_T *pcounts;
+ COUNT_T counts[4];
+ COUNT_T totals[4];
+ unsigned linepos;
+ unsigned u;
+ int num_files = 0;
+ int c;
+ smallint status = EXIT_SUCCESS;
+ smallint in_word;
+ unsigned print_type;
+
+ print_type = getopt32(argc, argv, "lwcL");
+
+ if (print_type == 0) {
+ print_type = (1 << WC_LINES) | (1 << WC_WORDS) | (1 << WC_CHARS);
+ }
+
+ argv += optind;
+ if (!argv[0]) {
+ *--argv = (char *) bb_msg_standard_input;
+ fname_fmt = "\n";
+ if (!((print_type-1) & print_type)) /* exactly one option? */
+ start_fmt = "%"COUNT_FMT;
+ }
+
+ memset(totals, 0, sizeof(totals));
+
+ pcounts = counts;
+
+ while ((arg = *argv++) != 0) {
+ ++num_files;
+ fp = fopen_or_warn_stdin(arg);
+ if (!fp) {
+ status = EXIT_FAILURE;
+ continue;
+ }
+
+ memset(counts, 0, sizeof(counts));
+ linepos = 0;
+ in_word = 0;
+
+ do {
+ /* Our -w doesn't match GNU wc exactly... oh well */
+
+ ++counts[WC_CHARS];
+ c = getc(fp);
+ if (isprint(c)) {
+ ++linepos;
+ if (!isspace_given_isprint(c)) {
+ in_word = 1;
+ continue;
+ }
+ } else if (((unsigned int)(c - 9)) <= 4) {
+ /* \t 9
+ * \n 10
+ * \v 11
+ * \f 12
+ * \r 13
+ */
+ if (c == '\t') {
+ linepos = (linepos | 7) + 1;
+ } else { /* '\n', '\r', '\f', or '\v' */
+ DO_EOF:
+ if (linepos > counts[WC_LENGTH]) {
+ counts[WC_LENGTH] = linepos;
+ }
+ if (c == '\n') {
+ ++counts[WC_LINES];
+ }
+ if (c != '\v') {
+ linepos = 0;
+ }
+ }
+ } else if (c == EOF) {
+ if (ferror(fp)) {
+ bb_perror_msg("%s", arg);
+ status = EXIT_FAILURE;
+ }
+ --counts[WC_CHARS];
+ goto DO_EOF; /* Treat an EOF as '\r'. */
+ } else {
+ continue;
+ }
+
+ counts[WC_WORDS] += in_word;
+ in_word = 0;
+ if (c == EOF) {
+ break;
+ }
+ } while (1);
+
+ if (totals[WC_LENGTH] < counts[WC_LENGTH]) {
+ totals[WC_LENGTH] = counts[WC_LENGTH];
+ }
+ totals[WC_LENGTH] -= counts[WC_LENGTH];
+
+ fclose_if_not_stdin(fp);
+
+ OUTPUT:
+ /* coreutils wc tries hard to print pretty columns
+ * (saves results for all files, find max col len etc...)
+ * we won't try that hard, it will bloat us too much */
+ s = start_fmt;
+ u = 0;
+ do {
+ if (print_type & (1 << u)) {
+ printf(s, pcounts[u]);
+ s = " %9"COUNT_FMT; /* Ok... restore the leading space. */
+ }
+ totals[u] += pcounts[u];
+ } while (++u < 4);
+ printf(fname_fmt, arg);
+ }
+
+ /* If more than one file was processed, we want the totals. To save some
+ * space, we set the pcounts ptr to the totals array. This has the side
+ * effect of trashing the totals array after outputting it, but that's
+ * irrelavent since we no longer need it. */
+ if (num_files > 1) {
+ num_files = 0; /* Make sure we don't get here again. */
+ arg = "total";
+ pcounts = totals;
+ --argv;
+ goto OUTPUT;
+ }
+
+ fflush_stdout_and_exit(status);
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/who.c b/i/pc104/initrd/conf/busybox/coreutils/who.c
new file mode 100644
index 0000000..a5d3b02
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/who.c
@@ -0,0 +1,76 @@
+/* vi: set sw=4 ts=4: */
+/*----------------------------------------------------------------------
+ * Mini who is used to display user name, login time,
+ * idle time and host name.
+ *
+ * Author: Da Chen <dchen@ayrnetworks.com>
+ *
+ * This is a free document; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation:
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * Copyright (c) 2002 AYR Networks, Inc.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ *
+ *----------------------------------------------------------------------
+ */
+
+#include "busybox.h"
+#include <utmp.h>
+#include <time.h>
+
+static void idle_string(char *str6, time_t t)
+{
+ t = time(NULL) - t;
+
+ /*if (t < 60) {
+ str6[0] = '.';
+ str6[1] = '\0';
+ return;
+ }*/
+ if (t >= 0 && t < (24 * 60 * 60)) {
+ sprintf(str6, "%02d:%02d",
+ (int) (t / (60 * 60)),
+ (int) ((t % (60 * 60)) / 60));
+ return;
+ }
+ strcpy(str6, "old");
+}
+
+int who_main(int argc, char **argv);
+int who_main(int argc, char **argv)
+{
+ char str6[6];
+ struct utmp *ut;
+ struct stat st;
+ char *name;
+
+ if (argc > 1) {
+ bb_show_usage();
+ }
+
+ setutent();
+ printf("USER TTY IDLE TIME HOST\n");
+ while ((ut = getutent()) != NULL) {
+ if (ut->ut_user[0] && ut->ut_type == USER_PROCESS) {
+ time_t thyme = ut->ut_tv.tv_sec;
+
+ /* ut->ut_line is device name of tty - "/dev/" */
+ name = concat_path_file("/dev", ut->ut_line);
+ str6[0] = '?';
+ str6[1] = '\0';
+ if (stat(name, &st) == 0)
+ idle_string(str6, st.st_atime);
+ printf("%-10s %-8s %-9s %-14.14s %s\n",
+ ut->ut_user, ut->ut_line, str6,
+ ctime(&thyme) + 4, ut->ut_host);
+ if (ENABLE_FEATURE_CLEAN_UP)
+ free(name);
+ }
+ }
+ if (ENABLE_FEATURE_CLEAN_UP)
+ endutent();
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/whoami.c b/i/pc104/initrd/conf/busybox/coreutils/whoami.c
new file mode 100644
index 0000000..3185817
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/whoami.c
@@ -0,0 +1,26 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini whoami implementation for busybox
+ *
+ * Copyright (C) 2000 Edward Betts <edward@debian.org>.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/* BB_AUDIT SUSv3 N/A -- Matches GNU behavior. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "busybox.h"
+
+int whoami_main(int argc, char **argv);
+int whoami_main(int argc, char **argv)
+{
+ if (argc > 1)
+ bb_show_usage();
+
+ puts(bb_getpwuid(NULL, geteuid(), -1));
+ /* exits on error */
+ fflush_stdout_and_exit(EXIT_SUCCESS);
+}
diff --git a/i/pc104/initrd/conf/busybox/coreutils/yes.c b/i/pc104/initrd/conf/busybox/coreutils/yes.c
new file mode 100644
index 0000000..2611c3e
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/coreutils/yes.c
@@ -0,0 +1,42 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * yes implementation for busybox
+ *
+ * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/* BB_AUDIT SUSv3 N/A -- Matches GNU behavior. */
+
+/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
+ *
+ * Size reductions and removed redundant applet name prefix from error messages.
+ */
+
+#include "busybox.h"
+
+int yes_main(int argc, char **argv);
+int yes_main(int argc, char **argv)
+{
+ static const char fmt_str[] = " %s";
+ const char *fmt;
+ char **first_arg;
+
+ *argv = (char*)"y";
+ if (argc != 1) {
+ ++argv;
+ }
+
+ first_arg = argv;
+ do {
+ fmt = fmt_str + 1;
+ do {
+ printf(fmt, *argv);
+ fmt = fmt_str;
+ } while (*++argv);
+ argv = first_arg;
+ } while (putchar('\n') != EOF);
+
+ bb_perror_nomsg_and_die();
+}
diff --git a/i/pc104/initrd/conf/busybox/debianutils/Config.in b/i/pc104/initrd/conf/busybox/debianutils/Config.in
new file mode 100644
index 0000000..3d85999
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/debianutils/Config.in
@@ -0,0 +1,88 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+menu "Debian Utilities"
+
+config MKTEMP
+ bool "mktemp"
+ default n
+ help
+ mktemp is used to create unique temporary files
+
+config PIPE_PROGRESS
+ bool "pipe_progress"
+ default n
+ help
+ Display a dot to indicate pipe activity.
+
+config READLINK
+ bool "readlink"
+ default n
+ help
+ This program reads a symbolic link and returns the name
+ of the file it points to
+
+config FEATURE_READLINK_FOLLOW
+ bool "Enable canonicalization by following all symlinks (-f)"
+ default n
+ depends on READLINK
+ help
+ Enable the readlink option (-f).
+
+config RUN_PARTS
+ bool "run-parts"
+ default n
+ help
+ run-parts is a utility designed to run all the scripts in a directory.
+
+ It is useful to set up a directory like cron.daily, where you need to
+ execute all the scripts in that directory.
+
+ In this implementation of run-parts some features (such as report mode)
+ are not implemented.
+
+ Unless you know that run-parts is used in some of your scripts
+ you can safely say N here.
+
+config FEATURE_RUN_PARTS_LONG_OPTIONS
+ bool "Enable long options"
+ default n
+ depends on RUN_PARTS && GETOPT_LONG
+ help
+ Support long options for the run-parts applet.
+
+config START_STOP_DAEMON
+ bool "start-stop-daemon"
+ default y
+ help
+ start-stop-daemon is used to control the creation and
+ termination of system-level processes, usually the ones
+ started during the startup of the system.
+
+config FEATURE_START_STOP_DAEMON_FANCY
+ bool "Support additional arguments"
+ default y
+ depends on START_STOP_DAEMON
+ help
+ Support additional arguments.
+ -o|--oknodo ignored since we exit with 0 anyway
+ -v|--verbose
+
+config FEATURE_START_STOP_DAEMON_LONG_OPTIONS
+ bool "Enable long options"
+ default n
+ depends on START_STOP_DAEMON && GETOPT_LONG
+ help
+ Support long options for the start-stop-daemon applet.
+
+config WHICH
+ bool "which"
+ default n
+ help
+ which is used to find programs in your PATH and
+ print out their pathnames.
+
+endmenu
+
diff --git a/i/pc104/initrd/conf/busybox/debianutils/Kbuild b/i/pc104/initrd/conf/busybox/debianutils/Kbuild
new file mode 100644
index 0000000..99df6a5
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/debianutils/Kbuild
@@ -0,0 +1,13 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under the GPL v2, see the file LICENSE in this tarball.
+
+lib-y:=
+lib-$(CONFIG_MKTEMP) += mktemp.o
+lib-$(CONFIG_PIPE_PROGRESS) += pipe_progress.o
+lib-$(CONFIG_READLINK) += readlink.o
+lib-$(CONFIG_RUN_PARTS) += run_parts.o
+lib-$(CONFIG_START_STOP_DAEMON) += start_stop_daemon.o
+lib-$(CONFIG_WHICH) += which.o
diff --git a/i/pc104/initrd/conf/busybox/debianutils/mktemp.c b/i/pc104/initrd/conf/busybox/debianutils/mktemp.c
new file mode 100644
index 0000000..ce5bda5
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/debianutils/mktemp.c
@@ -0,0 +1,49 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini mktemp implementation for busybox
+ *
+ *
+ * Copyright (C) 2000 by Daniel Jacobowitz
+ * Written by Daniel Jacobowitz <dan@debian.org>
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ */
+
+#include "busybox.h"
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+int mktemp_main(int argc, char **argv);
+int mktemp_main(int argc, char **argv)
+{
+ unsigned long flags = getopt32(argc, argv, "dqt");
+ char *chp;
+
+ if (optind + 1 != argc)
+ bb_show_usage();
+
+ chp = argv[optind];
+
+ if (flags & 4) {
+ char *dir = getenv("TMPDIR");
+ if (dir && *dir != '\0')
+ chp = concat_path_file(dir, chp);
+ else
+ chp = concat_path_file("/tmp/", chp);
+ }
+
+ if (flags & 1) {
+ if (mkdtemp(chp) == NULL)
+ return EXIT_FAILURE;
+ } else {
+ if (mkstemp(chp) < 0)
+ return EXIT_FAILURE;
+ }
+
+ puts(chp);
+
+ return EXIT_SUCCESS;
+}
diff --git a/i/pc104/initrd/conf/busybox/debianutils/pipe_progress.c b/i/pc104/initrd/conf/busybox/debianutils/pipe_progress.c
new file mode 100644
index 0000000..51a017c
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/debianutils/pipe_progress.c
@@ -0,0 +1,43 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Monitor a pipe with a simple progress display.
+ *
+ * Copyright (C) 2003 by Rob Landley <rob@landley.net>, Joey Hess
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "busybox.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+
+#define PIPE_PROGRESS_SIZE 4096
+
+/* Read a block of data from stdin, write it to stdout.
+ * Activity is indicated by a '.' to stderr
+ */
+int pipe_progress_main(int argc, char **argv);
+int pipe_progress_main(int argc, char **argv)
+{
+ RESERVE_CONFIG_BUFFER(buf, PIPE_PROGRESS_SIZE);
+ time_t t = time(NULL);
+ size_t len;
+
+ while ((len = fread(buf, 1, PIPE_PROGRESS_SIZE, stdin)) > 0) {
+ time_t new_time = time(NULL);
+ if (new_time != t) {
+ t = new_time;
+ fputc('.', stderr);
+ }
+ fwrite(buf, len, 1, stdout);
+ }
+
+ fputc('\n', stderr);
+
+ if (ENABLE_FEATURE_CLEAN_UP)
+ RELEASE_CONFIG_BUFFER(buf);
+
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/debianutils/readlink.c b/i/pc104/initrd/conf/busybox/debianutils/readlink.c
new file mode 100644
index 0000000..1a2c1ef
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/debianutils/readlink.c
@@ -0,0 +1,52 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini readlink implementation for busybox
+ *
+ * Copyright (C) 2000,2001 Matt Kraai <kraai@alumni.carnegiemellon.edu>
+ *
+ * Licensed under GPL v2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "busybox.h"
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+int readlink_main(int argc, char **argv);
+int readlink_main(int argc, char **argv)
+{
+ char *buf;
+ char *fname;
+
+ USE_FEATURE_READLINK_FOLLOW(
+ unsigned opt;
+ /* We need exactly one non-option argument. */
+ opt_complementary = "=1";
+ opt = getopt32(argc, argv, "f");
+ fname = argv[optind];
+ )
+ SKIP_FEATURE_READLINK_FOLLOW(
+ const unsigned opt = 0;
+ if (argc != 2) bb_show_usage();
+ fname = argv[1];
+ )
+
+ /* compat: coreutils readlink reports errors silently via exit code */
+ logmode = LOGMODE_NONE;
+
+ if (opt) {
+ buf = realpath(fname, bb_common_bufsiz1);
+ } else {
+ buf = xmalloc_readlink_or_warn(fname);
+ }
+
+ if (!buf)
+ return EXIT_FAILURE;
+ puts(buf);
+
+ if (ENABLE_FEATURE_CLEAN_UP && buf != bb_common_bufsiz1)
+ free(buf);
+
+ fflush_stdout_and_exit(EXIT_SUCCESS);
+}
diff --git a/i/pc104/initrd/conf/busybox/debianutils/run_parts.c b/i/pc104/initrd/conf/busybox/debianutils/run_parts.c
new file mode 100644
index 0000000..a864a05
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/debianutils/run_parts.c
@@ -0,0 +1,192 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini run-parts implementation for busybox
+ *
+ *
+ * Copyright (C) 2001 by Emanuele Aina <emanuele.aina@tiscali.it>
+ *
+ * Based on the Debian run-parts program, version 1.15
+ * Copyright (C) 1996 Jeff Noxon <jeff@router.patch.net>,
+ * Copyright (C) 1996-1999 Guy Maor <maor@debian.org>
+ *
+ *
+ * Licensed under GPL v2 or later, see file LICENSE in this tarball for details.
+ */
+
+/* This is my first attempt to write a program in C (well, this is my first
+ * attempt to write a program! :-) . */
+
+/* This piece of code is heavily based on the original version of run-parts,
+ * taken from debian-utils. I've only removed the long options and a the
+ * report mode. As the original run-parts support only long options, I've
+ * broken compatibility because the BusyBox policy doesn't allow them.
+ * The supported options are:
+ * -t test. Print the name of the files to be executed, without
+ * execute them.
+ * -a ARG argument. Pass ARG as an argument the program executed. It can
+ * be repeated to pass multiple arguments.
+ * -u MASK umask. Set the umask of the program executed to MASK. */
+
+/* TODO
+ * done - convert calls to error in perror... and remove error()
+ * done - convert malloc/realloc to their x... counterparts
+ * done - remove catch_sigchld
+ * done - use bb's concat_path_file()
+ * done - declare run_parts_main() as extern and any other function as static?
+ */
+
+#include "busybox.h"
+#include <getopt.h>
+
+static const struct option runparts_long_options[] = {
+ { "test", 0, NULL, 't' },
+ { "umask", 1, NULL, 'u' },
+ { "arg", 1, NULL, 'a' },
+ { 0, 0, 0, 0 }
+};
+
+/* valid_name */
+/* True or false? Is this a valid filename (upper/lower alpha, digits,
+ * underscores, and hyphens only?)
+ */
+static int valid_name(const struct dirent *d)
+{
+ const char *c = d->d_name;
+
+ while (*c) {
+ if (!isalnum(*c) && (*c != '_') && (*c != '-')) {
+ return 0;
+ }
+ ++c;
+ }
+ return 1;
+}
+
+/* test mode = 1 is the same as official run_parts
+ * test_mode = 2 means to fail silently on missing directories
+ */
+static int run_parts(char **args, const unsigned char test_mode)
+{
+ struct dirent **namelist = 0;
+ struct stat st;
+ char *filename;
+ char *arg0 = args[0];
+ int entries;
+ int i;
+ int exitstatus = 0;
+
+#if __GNUC__
+ /* Avoid longjmp clobbering */
+ (void) &i;
+ (void) &exitstatus;
+#endif
+ /* scandir() isn't POSIX, but it makes things easy. */
+ entries = scandir(arg0, &namelist, valid_name, alphasort);
+
+ if (entries == -1) {
+ if (test_mode & 2) {
+ return 2;
+ }
+ bb_perror_msg_and_die("cannot open '%s'", arg0);
+ }
+
+ for (i = 0; i < entries; i++) {
+ filename = concat_path_file(arg0, namelist[i]->d_name);
+
+ xstat(filename, &st);
+ if (S_ISREG(st.st_mode) && !access(filename, X_OK)) {
+ if (test_mode) {
+ puts(filename);
+ } else {
+ /* exec_errno is common vfork variable */
+ volatile int exec_errno = 0;
+ int result;
+ int pid;
+
+ if ((pid = vfork()) < 0) {
+ bb_perror_msg_and_die("failed to fork");
+ } else if (!pid) {
+ args[0] = filename;
+ execve(filename, args, environ);
+ exec_errno = errno;
+ _exit(1);
+ }
+
+ waitpid(pid, &result, 0);
+ if (exec_errno) {
+ errno = exec_errno;
+ bb_perror_msg("failed to exec %s", filename);
+ exitstatus = 1;
+ }
+ if (WIFEXITED(result) && WEXITSTATUS(result)) {
+ bb_perror_msg("%s exited with return code %d", filename, WEXITSTATUS(result));
+ exitstatus = 1;
+ } else if (WIFSIGNALED(result)) {
+ bb_perror_msg("%s exited because of uncaught signal %d", filename, WTERMSIG(result));
+ exitstatus = 1;
+ }
+ }
+ } else if (!S_ISDIR(st.st_mode)) {
+ bb_error_msg("component %s is not an executable plain file", filename);
+ exitstatus = 1;
+ }
+
+ free(namelist[i]);
+ free(filename);
+ }
+ free(namelist);
+
+ return exitstatus;
+}
+
+
+/* run_parts_main */
+/* Process options */
+int run_parts_main(int argc, char **argv);
+int run_parts_main(int argc, char **argv)
+{
+ char **args = xmalloc(2 * sizeof(char *));
+ unsigned char test_mode = 0;
+ unsigned short argcount = 1;
+ int opt;
+
+ umask(022);
+
+ while ((opt = getopt_long(argc, argv, "tu:a:",
+ runparts_long_options, NULL)) > 0)
+ {
+ switch (opt) {
+ /* Enable test mode */
+ case 't':
+ test_mode++;
+ break;
+ /* Set the umask of the programs executed */
+ case 'u':
+ /* Check and set the umask of the program executed. As stated in the original
+ * run-parts, the octal conversion in libc is not foolproof; it will take the
+ * 8 and 9 digits under some circumstances. We'll just have to live with it.
+ */
+ umask(xstrtoul_range(optarg, 8, 0, 07777));
+ break;
+ /* Pass an argument to the programs */
+ case 'a':
+ /* Add an argument to the commands that we will call.
+ * Called once for every argument. */
+ args = xrealloc(args, (argcount + 2) * (sizeof(char *)));
+ args[argcount++] = optarg;
+ break;
+ default:
+ bb_show_usage();
+ }
+ }
+
+ /* We require exactly one argument: the directory name */
+ if (optind != (argc - 1)) {
+ bb_show_usage();
+ }
+
+ args[0] = argv[optind];
+ args[argcount] = 0;
+
+ return run_parts(args, test_mode);
+}
diff --git a/i/pc104/initrd/conf/busybox/debianutils/start_stop_daemon.c b/i/pc104/initrd/conf/busybox/debianutils/start_stop_daemon.c
new file mode 100644
index 0000000..1862f11
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/debianutils/start_stop_daemon.c
@@ -0,0 +1,330 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini start-stop-daemon implementation(s) for busybox
+ *
+ * Written by Marek Michalkiewicz <marekm@i17linuxb.ists.pwr.wroc.pl>,
+ * Adapted for busybox David Kimdon <dwhedon@gordian.com>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "busybox.h"
+#include <getopt.h>
+#include <sys/resource.h>
+
+static int signal_nr = 15;
+static int user_id = -1;
+static char *userspec;
+static char *cmdname;
+static char *execname;
+static char *pidfile;
+static smallint quiet;
+
+struct pid_list {
+ struct pid_list *next;
+ pid_t pid;
+};
+
+static struct pid_list *found;
+
+static inline void push(pid_t pid)
+{
+ struct pid_list *p;
+
+ p = xmalloc(sizeof(*p));
+ p->next = found;
+ p->pid = pid;
+ found = p;
+}
+
+static int pid_is_exec(pid_t pid, const char *name)
+{
+ char buf[sizeof("/proc//exe") + sizeof(int)*3];
+ char *execbuf;
+ int sz;
+ int equal;
+
+ sprintf(buf, "/proc/%d/exe", pid);
+ sz = strlen(name) + 1;
+ execbuf = xzalloc(sz);
+ readlink(buf, execbuf, sz);
+
+ /* if readlink fails, execbuf still contains "" */
+ equal = !strcmp(execbuf, name);
+ if (ENABLE_FEATURE_CLEAN_UP)
+ free(execbuf);
+ return equal;
+}
+
+static int pid_is_user(int pid, int uid)
+{
+ struct stat sb;
+ char buf[sizeof("/proc/") + sizeof(int)*3];
+
+ sprintf(buf, "/proc/%u", pid);
+ if (stat(buf, &sb) != 0)
+ return 0;
+ return (sb.st_uid == uid);
+}
+
+static int pid_is_cmd(pid_t pid, const char *name)
+{
+ char fname[sizeof("/proc//stat") + sizeof(int)*3];
+ char *buf;
+ int r = 0;
+
+ sprintf(fname, "/proc/%u/stat", pid);
+ buf = xmalloc_open_read_close(fname, NULL);
+ if (buf) {
+ char *p = strchr(buf, '(');
+ if (p) {
+ char *pe = strrchr(++p, ')');
+ if (pe) {
+ *pe = '\0';
+ r = !strcmp(p, name);
+ }
+ }
+ free(buf);
+ }
+ return r;
+}
+
+
+static void check(int pid)
+{
+ if (execname && !pid_is_exec(pid, execname)) {
+ return;
+ }
+ if (userspec && !pid_is_user(pid, user_id)) {
+ return;
+ }
+ if (cmdname && !pid_is_cmd(pid, cmdname)) {
+ return;
+ }
+ push(pid);
+}
+
+
+static void do_pidfile(void)
+{
+ FILE *f;
+ pid_t pid;
+
+ f = fopen(pidfile, "r");
+ if (f) {
+ if (fscanf(f, "%u", &pid) == 1)
+ check(pid);
+ fclose(f);
+ } else if (errno != ENOENT)
+ bb_perror_msg_and_die("open pidfile %s", pidfile);
+}
+
+static void do_procinit(void)
+{
+ DIR *procdir;
+ struct dirent *entry;
+ int foundany, pid;
+
+ if (pidfile) {
+ do_pidfile();
+ return;
+ }
+
+ procdir = xopendir("/proc");
+
+ foundany = 0;
+ while ((entry = readdir(procdir)) != NULL) {
+ pid = bb_strtou(entry->d_name, NULL, 10);
+ if (errno)
+ continue;
+ foundany++;
+ check(pid);
+ }
+ closedir(procdir);
+ if (!foundany)
+ bb_error_msg_and_die ("nothing in /proc - not mounted?");
+}
+
+
+static int do_stop(void)
+{
+ char *what;
+ struct pid_list *p;
+ int killed = 0;
+
+ do_procinit();
+
+ if (cmdname)
+ what = xstrdup(cmdname);
+ else if (execname)
+ what = xstrdup(execname);
+ else if (pidfile)
+ what = xasprintf("process in pidfile '%s'", pidfile);
+ else if (userspec)
+ what = xasprintf("process(es) owned by '%s'", userspec);
+ else
+ bb_error_msg_and_die("internal error, please report");
+
+ if (!found) {
+ if (!quiet)
+ printf("no %s found; none killed\n", what);
+ if (ENABLE_FEATURE_CLEAN_UP)
+ free(what);
+ return -1;
+ }
+ for (p = found; p; p = p->next) {
+ if (kill(p->pid, signal_nr) == 0) {
+ p->pid = -p->pid;
+ killed++;
+ } else {
+ bb_perror_msg("warning: failed to kill %d", p->pid);
+ }
+ }
+ if (!quiet && killed) {
+ printf("stopped %s (pid", what);
+ for (p = found; p; p = p->next)
+ if(p->pid < 0)
+ printf(" %d", -p->pid);
+ puts(")");
+ }
+ if (ENABLE_FEATURE_CLEAN_UP)
+ free(what);
+ return killed;
+}
+
+#if ENABLE_FEATURE_START_STOP_DAEMON_LONG_OPTIONS
+static const struct option long_options[] = {
+ { "stop", 0, NULL, 'K' },
+ { "start", 0, NULL, 'S' },
+ { "background", 0, NULL, 'b' },
+ { "quiet", 0, NULL, 'q' },
+ { "make-pidfile", 0, NULL, 'm' },
+#if ENABLE_FEATURE_START_STOP_DAEMON_FANCY
+ { "oknodo", 0, NULL, 'o' },
+ { "verbose", 0, NULL, 'v' },
+ { "nicelevel", 1, NULL, 'N' },
+#endif
+ { "startas", 1, NULL, 'a' },
+ { "name", 1, NULL, 'n' },
+ { "signal", 1, NULL, 's' },
+ { "user", 1, NULL, 'u' },
+ { "chuid", 1, NULL, 'c' },
+ { "exec", 1, NULL, 'x' },
+ { "pidfile", 1, NULL, 'p' },
+#if ENABLE_FEATURE_START_STOP_DAEMON_FANCY
+ { "retry", 1, NULL, 'R' },
+#endif
+ { 0, 0, 0, 0 }
+};
+#endif
+
+enum {
+ CTX_STOP = 0x1,
+ CTX_START = 0x2,
+ OPT_BACKGROUND = 0x4, // -b
+ OPT_QUIET = 0x8, // -q
+ OPT_MAKEPID = 0x10, // -m
+ OPT_a = 0x20, // -a
+ OPT_n = 0x40, // -n
+ OPT_s = 0x80, // -s
+ OPT_u = 0x100, // -u
+ OPT_c = 0x200, // -c
+ OPT_x = 0x400, // -x
+ OPT_p = 0x800, // -p
+ OPT_OKNODO = 0x1000 * ENABLE_FEATURE_START_STOP_DAEMON_FANCY, // -o
+ OPT_VERBOSE = 0x2000 * ENABLE_FEATURE_START_STOP_DAEMON_FANCY, // -v
+ OPT_NICELEVEL = 0x4000 * ENABLE_FEATURE_START_STOP_DAEMON_FANCY, // -N
+};
+
+int start_stop_daemon_main(int argc, char **argv);
+int start_stop_daemon_main(int argc, char **argv)
+{
+ unsigned opt;
+ char *signame;
+ char *startas;
+ char *chuid;
+#if ENABLE_FEATURE_START_STOP_DAEMON_FANCY
+// char *retry_arg = NULL;
+// int retries = -1;
+ char *opt_N;
+#endif
+#if ENABLE_FEATURE_START_STOP_DAEMON_LONG_OPTIONS
+ applet_long_options = long_options;
+#endif
+
+ /* Check required one context option was given */
+ opt_complementary = "K:S:?:K--S:S--K:m?p:K?xpun:S?xa";
+ opt = getopt32(argc, argv, "KSbqma:n:s:u:c:x:p:"
+ USE_FEATURE_START_STOP_DAEMON_FANCY("ovN:"),
+// USE_FEATURE_START_STOP_DAEMON_FANCY("ovN:R:"),
+ &startas, &cmdname, &signame, &userspec, &chuid, &execname, &pidfile
+ USE_FEATURE_START_STOP_DAEMON_FANCY(,&opt_N)
+// USE_FEATURE_START_STOP_DAEMON_FANCY(,&retry_arg)
+ );
+
+ quiet = (opt & OPT_QUIET) && !(opt & OPT_VERBOSE);
+
+ if (opt & OPT_s) {
+ signal_nr = get_signum(signame);
+ if (signal_nr < 0) bb_show_usage();
+ }
+
+ if (!(opt & OPT_a))
+ startas = execname;
+
+// USE_FEATURE_START_STOP_DAEMON_FANCY(
+// if (retry_arg)
+// retries = xatoi_u(retry_arg);
+// )
+ argc -= optind;
+ argv += optind;
+
+ if (userspec) {
+ user_id = bb_strtou(userspec, NULL, 10);
+ if (errno)
+ user_id = xuname2uid(userspec);
+ }
+
+ if (opt & CTX_STOP) {
+ int i = do_stop();
+ return (opt & OPT_OKNODO) ? 0 : (i<=0);
+ }
+
+ do_procinit();
+
+ if (found) {
+ if (!quiet)
+ printf("%s already running\n%d\n", execname, found->pid);
+ return !(opt & OPT_OKNODO);
+ }
+ *--argv = startas;
+ if (opt & OPT_BACKGROUND) {
+ setsid();
+ bb_daemonize();
+ }
+ if (opt & OPT_MAKEPID) {
+ /* user wants _us_ to make the pidfile */
+ FILE *pidf = xfopen(pidfile, "w");
+
+ pid_t pidt = getpid();
+ fprintf(pidf, "%d\n", pidt);
+ fclose(pidf);
+ }
+ if (opt & OPT_c) {
+ struct bb_uidgid_t ugid;
+ parse_chown_usergroup_or_die(&ugid, chuid);
+ if (ugid.gid != (gid_t) -1) xsetgid(ugid.gid);
+ if (ugid.uid != (uid_t) -1) xsetuid(ugid.uid);
+ }
+#if ENABLE_FEATURE_START_STOP_DAEMON_FANCY
+ if (opt & OPT_NICELEVEL) {
+ /* Set process priority */
+ int prio = getpriority(PRIO_PROCESS, 0) + xatoi_range(opt_N, INT_MIN/2, INT_MAX/2);
+ if (setpriority(PRIO_PROCESS, 0, prio) < 0) {
+ bb_perror_msg_and_die("setpriority(%d)", prio);
+ }
+ }
+#endif
+ execv(startas, argv);
+ bb_perror_msg_and_die("cannot start %s", startas);
+}
diff --git a/i/pc104/initrd/conf/busybox/debianutils/which.c b/i/pc104/initrd/conf/busybox/debianutils/which.c
new file mode 100644
index 0000000..02992d0
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/debianutils/which.c
@@ -0,0 +1,48 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Which implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ * Copyright (C) 2006 Gabriel Somlo <somlo at cmu.edu>
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ *
+ * Based on which from debianutils
+ */
+
+#include "busybox.h"
+
+int which_main(int argc, char **argv);
+int which_main(int argc, char **argv)
+{
+ int status = EXIT_SUCCESS;
+ char *p;
+
+ if (argc <= 1 || argv[1][0] == '-') {
+ bb_show_usage();
+ }
+
+ if (!getenv("PATH")) {
+ setenv("PATH", "/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin", 1);
+ }
+
+ while (--argc > 0) {
+ argv++;
+ if (strchr(*argv, '/')) {
+ if (execable_file(*argv)) {
+ puts(*argv);
+ continue;
+ }
+ } else {
+ p = find_execable(*argv);
+ if (p) {
+ puts(p);
+ free(p);
+ continue;
+ }
+ }
+ status = EXIT_FAILURE;
+ }
+
+ fflush_stdout_and_exit(status);
+}
diff --git a/i/pc104/initrd/conf/busybox/docs/autodocifier.pl b/i/pc104/initrd/conf/busybox/docs/autodocifier.pl
new file mode 100755
index 0000000..68b6f3c
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/docs/autodocifier.pl
@@ -0,0 +1,303 @@
+#!/usr/bin/perl -w
+
+use strict;
+use Getopt::Long;
+
+# collect lines continued with a '\' into an array
+sub continuation {
+ my $fh = shift;
+ my @line;
+
+ while (<$fh>) {
+ my $s = $_;
+ $s =~ s/\\\s*$//;
+ #$s =~ s/#.*$//;
+ push @line, $s;
+ last unless (/\\\s*$/);
+ }
+ return @line;
+}
+
+# regex && eval away unwanted strings from documentation
+sub beautify {
+ my $text = shift;
+ for (;;) {
+ my $text2 = $text;
+ $text =~ s/SKIP_\w+\(.*?"\s*\)//sxg;
+ $text =~ s/USE_\w+\(\s*?(.*?)"\s*\)/$1"/sxg;
+ $text =~ s/USAGE_\w+\(\s*?(.*?)"\s*\)/$1"/sxg;
+ last if ( $text2 eq $text );
+ }
+ $text =~ s/"\s*"//sg;
+ my @line = split("\n", $text);
+ $text = join('',
+ map {
+ s/^\s*"//;
+ s/"\s*$//;
+ s/%/%%/g;
+ s/\$/\\\$/g;
+ eval qq[ sprintf(qq{$_}) ]
+ } @line
+ );
+ return $text;
+}
+
+# generate POD for an applet
+sub pod_for_usage {
+ my $name = shift;
+ my $usage = shift;
+
+ # Sigh. Fixup the known odd-name applets.
+ $name =~ s/dpkg_deb/dpkg-deb/g;
+ $name =~ s/fsck_minix/fsck.minix/g;
+ $name =~ s/mkfs_minix/mkfs.minix/g;
+ $name =~ s/run_parts/run-parts/g;
+ $name =~ s/start_stop_daemon/start-stop-daemon/g;
+
+ # make options bold
+ my $trivial = $usage->{trivial};
+ if (!defined $usage->{trivial}) {
+ $trivial = "";
+ } else {
+ $trivial =~ s/(?<!\w)(-\w+)/B<$1>/sxg;
+ }
+ my @f0 =
+ map { $_ !~ /^\s/ && s/(?<!\w)(-\w+)/B<$1>/g; $_ }
+ split("\n", (defined $usage->{full} ? $usage->{full} : ""));
+
+ # add "\n" prior to certain lines to make indented
+ # lines look right
+ my @f1;
+ my $len = @f0;
+ for (my $i = 0; $i < $len; $i++) {
+ push @f1, $f0[$i];
+ if (($i+1) != $len && $f0[$i] !~ /^\s/ && $f0[$i+1] =~ /^\s/) {
+ next if ($f0[$i] =~ /^$/);
+ push(@f1, "") unless ($f0[$i+1] =~ /^\s*$/s);
+ }
+ }
+ my $full = join("\n", @f1);
+
+ # prepare notes if they exist
+ my $notes = (defined $usage->{notes})
+ ? "$usage->{notes}\n\n"
+ : "";
+
+ # prepare examples if they exist
+ my $example = (defined $usage->{example})
+ ?
+ "Example:\n\n" .
+ join ("\n",
+ map { "\t$_" }
+ split("\n", $usage->{example})) . "\n\n"
+ : "";
+
+ # Pad the name so that the applet name gets a line
+ # by itself in BusyBox.txt
+ my $spaces = 10 - length($name);
+ if ($spaces > 0) {
+ $name .= " " x $spaces;
+ }
+
+ return
+ "=item B<$name>".
+ "\n\n$name $trivial\n\n".
+ "$full\n\n" .
+ "$notes" .
+ "$example" .
+ "\n\n"
+ ;
+}
+
+# the keys are applet names, and
+# the values will contain hashrefs of the form:
+#
+# {
+# trivial => "...",
+# full => "...",
+# notes => "...",
+# example => "...",
+# }
+my %docs;
+
+
+# get command-line options
+
+my %opt;
+
+GetOptions(
+ \%opt,
+ "help|h",
+ "pod|p",
+ "verbose|v",
+);
+
+if (defined $opt{help}) {
+ print
+ "$0 [OPTION]... [FILE]...\n",
+ "\t--help\n",
+ "\t--pod\n",
+ "\t--verbose\n",
+ ;
+ exit 1;
+}
+
+
+# collect documenation into %docs
+
+foreach (@ARGV) {
+ open(USAGE, $_) || die("$0: $_: $!");
+ my $fh = *USAGE;
+ my ($applet, $type, @line);
+ while (<$fh>) {
+ if (/^#define (\w+)_(\w+)_usage/) {
+ $applet = $1;
+ $type = $2;
+ @line = continuation($fh);
+ my $doc = $docs{$applet} ||= { };
+ my $text = join("\n", @line);
+ $doc->{$type} = beautify($text);
+ }
+ }
+}
+
+
+# generate structured documentation
+
+my $generator = \&pod_for_usage;
+
+my @names = sort keys %docs;
+my $line = "\t[, [[, ";
+for (my $i = 0; $i < $#names; $i++) {
+ if (length ($line.$names[$i]) >= 65) {
+ print "$line\n\t";
+ $line = "";
+ }
+ $line .= "$names[$i], ";
+}
+print $line . $names[-1];
+
+print "\n\n=head1 COMMAND DESCRIPTIONS\n";
+print "\n=over 4\n\n";
+
+foreach my $applet (@names) {
+ print $generator->($applet, $docs{$applet});
+}
+
+exit 0;
+
+__END__
+
+=head1 NAME
+
+autodocifier.pl - generate docs for busybox based on usage.h
+
+=head1 SYNOPSIS
+
+autodocifier.pl [OPTION]... [FILE]...
+
+Example:
+
+ ( cat docs/busybox_header.pod; \
+ docs/autodocifier.pl usage.h; \
+ cat docs/busybox_footer.pod ) > docs/busybox.pod
+
+=head1 DESCRIPTION
+
+The purpose of this script is to automagically generate
+documentation for busybox using its usage.h as the original source
+for content. It used to be that same content has to be duplicated
+in 3 places in slightly different formats -- F<usage.h>,
+F<docs/busybox.pod>. This was tedious and error-prone, so it was
+decided that F<usage.h> would contain all the text in a
+machine-readable form, and scripts could be used to transform this
+text into other forms if necessary.
+
+F<autodocifier.pl> is one such script. It is based on a script by
+Erik Andersen <andersen@codepoet.org> which was in turn based on a
+script by Mark Whitley <markw@codepoet.org>
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<--help>
+
+This displays the help message.
+
+=item B<--pod>
+
+Generate POD (this is the default)
+
+=item B<--verbose>
+
+Be verbose (not implemented)
+
+=back
+
+=head1 FORMAT
+
+The following is an example of some data this script might parse.
+
+ #define length_trivial_usage \
+ "STRING"
+ #define length_full_usage \
+ "Prints out the length of the specified STRING."
+ #define length_example_usage \
+ "$ length Hello\n" \
+ "5\n"
+
+Each entry is a cpp macro that defines a string. The macros are
+named systematically in the form:
+
+ $name_$type_usage
+
+$name is the name of the applet. $type can be "trivial", "full", "notes",
+or "example". Every documentation macro must end with "_usage".
+
+The definition of the types is as follows:
+
+=over 4
+
+=item B<trivial>
+
+This should be a brief, one-line description of parameters that
+the command expects. This will be displayed when B<-h> is issued to
+a command. I<REQUIRED>
+
+=item B<full>
+
+This should contain descriptions of each option. This will also
+be displayed along with the trivial help if CONFIG_FEATURE_TRIVIAL_HELP
+is disabled. I<REQUIRED>
+
+=item B<notes>
+
+This is documentation that is intended to go in the POD or SGML, but
+not be printed when a B<-h> is given to a command. To see an example
+of notes being used, see init_notes_usage in F<usage.h>. I<OPTIONAL>
+
+=item B<example>
+
+This should be an example of how the command is actually used.
+This will not be printed when a B<-h> is given to a command -- it
+will only be included in the POD or SGML documentation. I<OPTIONAL>
+
+=back
+
+=head1 FILES
+
+F<usage.h>
+
+=head1 COPYRIGHT
+
+Copyright (c) 2001 John BEPPU. All rights reserved. This program is
+free software; you can redistribute it and/or modify it under the same
+terms as Perl itself.
+
+=head1 AUTHOR
+
+John BEPPU <b@ax9.org>
+
+=cut
+
diff --git a/i/pc104/initrd/conf/busybox/docs/busybox.net/FAQ.html b/i/pc104/initrd/conf/busybox/docs/busybox.net/FAQ.html
new file mode 100644
index 0000000..c07be90
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/docs/busybox.net/FAQ.html
@@ -0,0 +1,1108 @@
+<!--#include file="header.html" -->
+
+<h3>Frequently Asked Questions</h3>
+
+This is a collection of some of the more frequently asked questions
+about BusyBox. Some of the questions even have answers. If you
+have additions to this FAQ document, we would love to add them,
+
+<h2>General questions</h2>
+<ol>
+<li><a href="#getting_started">How can I get started using BusyBox?</a></li>
+<li><a href="#configure">How do I configure busybox?</a></li>
+<li><a href="#build_system">How do I build a BusyBox-based system?</a></li>
+<li><a href="#kernel">Which Linux kernel versions are supported?</a></li>
+<li><a href="#arch">Which architectures does BusyBox run on?</a></li>
+<li><a href="#libc">Which C libraries are supported?</a></li>
+<li><a href="#commercial">Can I include BusyBox as part of the software on my device?</a></li>
+<li><a href="#external">Where can I find other small utilities since busybox does not include the features I want?</a></li></li>
+<li><a href="#demanding">I demand that you to add &lt;favorite feature&gt; right now! How come you don't answer all my questions on the mailing list instantly? I demand that you help me with all of my problems <em>Right Now</em>!</a></li>
+<li><a href="#helpme">I need help with BusyBox! What should I do?</a></li>
+<li><a href="#contracts">I need you to add &lt;favorite feature&gt;! Are the BusyBox developers willing to be paid in order to fix bugs or add in &lt;favorite feature&gt;? Are you willing to provide support contracts?</a></li>
+</ol>
+
+<h2>Troubleshooting</h2>
+<ol>
+<li><a href="#bugs">I think I found a bug in BusyBox! What should I do?!</a></li>
+<li><a href="#backporting">I'm using an ancient version from the dawn of time and something's broken. Can you backport fixes for free?</a></li>
+<li><a href="#init">Busybox init isn't working!</a></li>
+<li><a href="#sed">I can't configure busybox on my system.</a></li>
+<li><a href="#job_control">Why do I keep getting "sh: can't access tty; job control turned off" errors? Why doesn't Control-C work within my shell?</a></li>
+</ol>
+
+<h2>Programming questions</h2>
+<ol>
+ <li><a href="#goals">What are the goals of busybox?</a></li>
+ <li><a href="#design">What is the design of busybox?</a></li>
+ <li><a href="#source">How is the source code organized?</a></li>
+ <ul>
+ <li><a href="#source_applets">The applet directories.</a></li>
+ <li><a href="#source_libbb">The busybox shared library (libbb)</a></li>
+ </ul>
+ <li><a href="#optimize">I want to make busybox even smaller, how do I go about it?</a></li>
+ <li><a href="#adding">Adding an applet to busybox</a></li>
+ <li><a href="#standards">What standards does busybox adhere to?</a></li>
+ <li><a href="#portability">Portability.</a></li>
+ <li><a href="#tips">Tips and tricks.</a></li>
+ <ul>
+ <li><a href="#tips_encrypted_passwords">Encrypted Passwords</a></li>
+ <li><a href="#tips_vfork">Fork and vfork</a></li>
+ <li><a href="#tips_short_read">Short reads and writes</a></li>
+ <li><a href="#tips_memory">Memory used by relocatable code, PIC, and static linking.</a></li>
+ <li><a href="#tips_kernel_headers">Including Linux kernel headers.</a></li>
+ </ul>
+ <li><a href="#who">Who are the BusyBox developers?</a></li>
+</ul>
+
+
+</ol>
+
+<h1>General questions</h1>
+
+<hr />
+<p>
+<h2><a name="getting_started">How can I get started using BusyBox?</a></h2>
+<p> If you just want to try out busybox without installing it, download the
+ tarball, extract it, run "make defconfig", and then run "make".
+</p>
+<p>
+ This will create a busybox binary with almost all features enabled. To try
+ out a busybox applet, type "./busybox [appletname] [options]", for
+ example "./busybox ls -l" or "./busybox cat LICENSE". Type "./busybox"
+ to see a command list, and "busybox appletname --help" to see a brief
+ usage message for a given applet.
+</p>
+<p>
+ BusyBox uses the name it was invoked under to determine which applet is
+ being invoked. (Try "mv busybox ls" and then "./ls -l".) Installing
+ busybox consists of creating symlinks (or hardlinks) to the busybox
+ binary for each applet in busybox, and making sure these links are in
+ the shell's command $PATH. The special applet name "busybox" (or with
+ any optional suffix, such as "busybox-static") uses the first argument
+ to determine which applet to run, as shown above.
+</p>
+<p>
+ BusyBox also has a feature called the
+ <a name="standalone_shell">"standalone shell"</a>, where the busybox
+ shell runs any built-in applets before checking the command path. This
+ feature is also enabled by "make allyesconfig", and to try it out run
+ the command line "PATH= ./busybox ash". This will blank your command path
+ and run busybox as your command shell, so the only commands it can find
+ (without an explicit path such as /bin/ls) are the built-in busybox ones.
+ This is another good way to see what's built into busybox.
+ Note that the standalone shell requires CONFIG_BUSYBOX_EXEC_PATH
+ to be set appropriately, depending on whether or not /proc/self/exe is
+ available or not. If you do not have /proc, then point that config option
+ to the location of your busybox binary, usually /bin/busybox.
+ (So if you set it to /proc/self/exe, and happen to be able to chroot into
+ your rootfs, you must mount /proc beforehand.)
+</p>
+<p>
+ A typical indication that you set CONFIG_BUSYBOX_EXEC_PATH to proc but
+ forgot to mount proc is:
+<pre>
+$ /bin/echo $PATH
+/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/bin/X11
+$ echo $PATH
+/bin/sh: echo: not found
+</pre>
+<hr />
+<p>
+<h2><a name="configure">How do I configure busybox?</a></h2>
+<p> Busybox is configured similarly to the linux kernel. Create a default
+ configuration and then run "make menuconfig" to modify it. The end
+ result is a .config file that tells the busybox build process what features
+ to include. So instead of "./configure; make; make install" the equivalent
+ busybox build would be "make defconfig; make; make install".
+</p>
+
+<p> Busybox configured with all features enabled is a little under a megabyte
+ dynamically linked on x86. To create a smaller busybox, configure it with
+ fewer features. Individual busybox applets cost anywhere from a few
+ hundred bytes to tens of kilobytes. Disable unneeded applets to save,
+ space, using menuconfig.
+</p>
+
+<p>The most important busybox configurators are:</p>
+
+<ul>
+<li><p>make <b>defconfig</b> - Create the maximum "sane" configuration. This
+enables almost all features, minus things like debugging options and features
+that require changes to the rest of the system to work (such as selinux or
+devfs device names). Use this if you want to start from a full-featured
+busybox and remove features until it's small enough.</p></li>
+<li><p>make <b>allnoconfig</b> - Disable everything. This creates a tiny version
+of busybox that doesn't do anything. Start here if you know exactly what
+you want and would like to select only those features.</p></li>
+<li><p>make <b>menuconfig</b> - Interactively modify a .config file through a
+multi-level menu interface. Use this after one of the previous two.</p></li>
+</ul>
+
+<p>Some other configuration options are:</p>
+<ul>
+<li><p>make <b>oldconfig</b> - Update an old .config file for a newer version
+of busybox.</p></li>
+<li><p>make <b>allyesconfig</b> - Select absolutely everything. This creates
+a statically linked version of busybox full of debug code, with dependencies on
+selinux, using devfs names... This makes sure everything compiles. Whether
+or not the result would do anything useful is an open question.</p></li>
+<li><p>make <b>allbareconfig</b> - Select all applets but disable all sub-features
+within each applet. More build coverage testing.</p></li>
+<li><p>make <b>randconfig</b> - Create a random configuration for test purposes.</p></li>
+</ul>
+
+<p> Menuconfig modifies your .config file through an interactive menu where you can enable or disable
+ busybox features, and get help about each feature.
+
+
+
+<p>
+ To build a smaller busybox binary, run "make menuconfig" and disable the
+ features you don't need. (Or run "make allnoconfig" and then use
+ menuconfig to add just the features you need. Don't forget to recompile
+ with "make" once you've finished configuring.)
+</p>
+<hr/>
+<p/>
+<h2><a name="build_system">How do I build a BusyBox-based system?</a></h2>
+<p>
+ BusyBox is a package that replaces a dozen standard packages, but it is
+ not by itself a complete bootable system. Building an entire Linux
+ distribution from source is a bit beyond the scope of this FAQ, but it
+ understandably keeps cropping up on the mailing list, so here are some
+ pointers.
+</p>
+<p>
+ Start by learning how to strip a working system down to the bare essentials
+ needed to run one or two commands, so you know what it is you actually
+ need. An excellent practical place to do
+ this is the <a href="http://www.tldp.org/HOWTO/Bootdisk-HOWTO/">Linux
+ BootDisk Howto</a>, or for a more theoretical approach try
+ <a href="http://www.tldp.org/HOWTO/From-PowerUp-To-Bash-Prompt-HOWTO.html">From
+ PowerUp to Bash Prompt</a>.
+</p>
+<p>
+ To learn how to build a working Linux system entirely from source code,
+ the place to go is the <a href="http://www.linuxfromscratch.org">Linux
+ From Scratch</a> project. They have an entire book of step-by-step
+ instructions you can
+ <a href="http://www.linuxfromscratch.org/lfs/view/stable/">read online</a>
+ or
+ <a href="http://www.linuxfromscratch.org/lfs/downloads/stable/">download</a>.
+ Be sure to check out the other sections of their main page, including
+ Beyond Linux From Scratch, Hardened Linux From Scratch, their Hints
+ directory, and their LiveCD project. (They also have mailing lists which
+ are better sources of answers to Linux-system building questions than
+ the busybox list.)
+</p>
+<p>
+ If you want an automated yet customizable system builder which produces
+ a BusyBox and uClibc based system, try
+ <a href="http://buildroot.uclibc.org">buildroot</a>, which is
+ another project by the maintainer of the uClibc (Erik Andersen).
+ Download the tarball, extract it, unset CC, make.
+ For more instructions, see the website.
+</p>
+
+<hr />
+<p>
+<h2><a name="kernel">Which Linux kernel versions are supported?</a></h2>
+<p>
+ Full functionality requires Linux 2.4.x or better. (Earlier versions may
+ still work, but are no longer regularly tested.) A large fraction of the
+ code should run on just about anything. While the current code is fairly
+ Linux specific, it should be fairly easy to port the majority of the code
+ to support, say, FreeBSD or Solaris, or Mac OS X, or even Windows (if you
+ are into that sort of thing).
+</p>
+<hr />
+<p>
+<h2><a name="arch">Which architectures does BusyBox run on?</a></h2>
+<p>
+ BusyBox in general will build on any architecture supported by gcc.
+ Kernel module loading for 2.4 Linux kernels is currently
+ limited to ARM, CRIS, H8/300, x86, ia64, x86_64, m68k, MIPS, PowerPC,
+ S390, SH3/4/5, Sparc, v850e, and x86_64 for 2.4.x kernels.
+</p>
+<p>
+ With 2.6.x kernels, module loading support should work on all architectures.
+</p>
+<hr />
+<p>
+<h2><a name="libc">Which C libraries are supported?</a></h2>
+<p>
+ On Linux, BusyBox releases are tested against uClibc (0.9.27 or later) and
+ glibc (2.2 or later). Both should provide full functionality with busybox,
+ and if you find a bug we want to hear about it.
+</p>
+<p>
+ Linux-libc5 is no longer maintained (and has no known advantages over
+ uClibc), dietlibc is known to have numerous unfixed bugs, and klibc is
+ missing too many features to build BusyBox. If you require a small C
+ library for Linux, the busybox developers recommend uClibc.
+</p>
+<p>
+ Some BusyBox applets have been built and run under a combination
+ of newlib and libgloss (see
+ <a href="http://www.busybox.net/lists/busybox/2005-March/013759.html">this thread</a>).
+ This is still experimental, but may be supported in a future release.
+</p>
+
+<hr />
+<p>
+<h2><a name="commercial">Can I include BusyBox as part of the software on my device?</a></h2>
+<p>
+
+<p>
+ Yes. As long as you <a href="http://busybox.net/license.html">fully comply
+ with the generous terms of the GPL BusyBox license</a> you can ship BusyBox
+ as part of the software on your device.
+</p>
+
+<hr />
+<p>
+<h2><a name="external">where can i find other small utilities since busybox
+ does not include the features i want?</a></h2>
+<p>
+ we maintain such a <a href="tinyutils.html">list</a> on this site!
+</p>
+
+<hr />
+<p>
+<h2><a name="demanding">I demand that you to add &lt;favorite feature&gt; right now! How come you don't answer all my questions on the mailing list instantly? I demand that you help me with all of my problems <em>Right Now</em>!</a></h2>
+<p>
+
+ You have not paid us a single cent and yet you still have the product of
+ many years of our work. We are not your slaves! We work on BusyBox
+ because we find it useful and interesting. If you go off flaming us, we
+ will ignore you.
+
+
+<hr />
+<p>
+<h2><a name="helpme">I need help with BusyBox! What should I do?</a></h2>
+<p>
+
+ If you find that you need help with BusyBox, you can ask for help on the
+ BusyBox mailing list at busybox@busybox.net.</p>
+
+<p> In addition to the mailing list, Erik Andersen (andersee), Manuel Nova
+ (mjn3), Rob Landley (landley), Mike Frysinger (SpanKY), Bernhard Fischer
+ (blindvt), and other long-time BusyBox developers are known to hang out
+ on the uClibc IRC channel: #uclibc on irc.freenode.net. There is a
+ <a href="http://ibot.Rikers.org/%23uclibc/">web archive of
+ daily logs of the #uclibc IRC channel</a> going back to 2002.
+</p>
+
+<p>
+ <b>Please do not send private email to Rob, Erik, Manuel, or the other
+ BusyBox contributors asking for private help unless you are planning on
+ paying for consulting services.</b>
+</p>
+
+<p>
+ When we answer questions on the BusyBox mailing list, it helps everyone
+ since people with similar problems in the future will be able to get help
+ by searching the mailing list archives. Private help is reserved as a paid
+ service. If you need to use private communication, or if you are serious
+ about getting timely assistance with BusyBox, you should seriously consider
+ paying for consulting services.
+</p>
+
+<hr />
+<p>
+<h2><a name="contracts">I need you to add &lt;favorite feature&gt;! Are the BusyBox developers willing to be paid in order to fix bugs or add in &lt;favorite feature&gt;? Are you willing to provide support contracts?</a></h2>
+</p>
+
+<p>
+ Yes we are. The easy way to sponsor a new feature is to post an offer on
+ the mailing list to see who's interested. You can also email the project's
+ maintainer and ask them to recommend someone.
+</p>
+
+<p> If you prefer to deal with an organization rather than an individual, Rob
+ Landley (the current BusyBox maintainer) works for
+ <a http://www.timesys.com>TimeSys</a>, and Eric Andersen (the previous
+ busybox maintainer and current uClibc maintainer) owns
+ <a href="http://codepoet-consulting.com/">CodePoet Consulting</a>. Both
+ companies offer support contracts and handle new development, and there
+ are plenty of other companies that do the same.
+</p>
+
+
+
+
+<h1>Troubleshooting</h1>
+
+<hr />
+<p></p>
+<h2><a name="bugs">I think I found a bug in BusyBox! What should I do?</a></h2>
+<p></p>
+
+<p>
+ If you simply need help with using or configuring BusyBox, please submit a
+ detailed description of your problem to the BusyBox mailing list at <a
+ href="mailto:busybox@busybox.net"> busybox@busybox.net</a>.
+ Please do not send email to individual developers asking
+ for private help unless you are planning on paying for consulting services.
+ When we answer questions on the BusyBox mailing list, it helps everyone,
+ while private answers help only you...
+</p>
+
+<p>
+ Bug reports and new feature patches sometimes get lost when posted to the
+ mailing list, because the developers of BusyBox are busy people and have
+ only so much they can keep in their brains at a time. You can post a
+ polite reminder after 2-3 days without offending anybody. If that doesn't
+ result in a solution, please use the
+ <a href="http://bugs.busybox.net/">BusyBox Bug
+ and Patch Tracking System</a> to submit a detailed explanation and we'll
+ get to it as soon as we can.
+</p>
+
+<p>
+ Note that bugs entered into the bug system without being mentioned on the
+ mailing list first may languish there for months before anyone even notices
+ them. We generally go through the bug system when preparing for new
+ development releases, to see what fell through the cracks while we were
+ off writing new features. (It's a fast/unreliable vs slow/reliable thing.
+ Saves retransits, but the latency sucks.)
+</p>
+
+<hr />
+<p></p>
+<h2><a name="backporting">I'm using an ancient version from the dawn of time and something's broken. Can you backport fixes for free?</h2>
+
+<p>Variants of this one get asked a lot.</p>
+
+<p>The purpose of the BusyBox mailing list is to develop and improve BusyBox,
+and we're happy to respond to our users' needs. But if you're coming to the
+list for free tech support we're going to ask you to upgrade to a current
+version before we try to diagnose your problem.</p>
+
+<p>If you're building BusyBox 0.50 with uClibc 0.9.19 and gcc 0.9.26 there's a
+fairly large chance that whatever problem you're seeing has already been fixed.
+To get that fix, all you have to do is upgrade to a newer version. If you
+don't at least _try_ that, you're wasting our time.</p>
+
+<p>The volunteers are happy to fix any bugs you point out in the current
+versions because doing so helps everybody and makes the project better. We
+want to make the current version work for you. But diagnosing, debugging, and
+backporting fixes to old versions isn't something we do for free, because it
+doesn't help anybody but you. The cost of volunteer tech support is using a
+reasonably current version of the project.</p>
+
+<p>If you don't want to upgrade, you have the complete source code and thus
+the ability to fix it yourself, or hire a consultant to do it for you. If you
+got your version from a vendor who still supports the older version, they can
+help you. But there are limits as to what the volunteers will feel obliged to
+do for you.</p>
+
+<p>As a rule of thumb, volunteers will generally answer polite questions about
+a given version for about three years after its release before it's so old
+we don't remember the answer off the top of our head. And if you want us to
+put any _effort_ into tracking it down, we want you to put in a little effort
+of your own by confirming it's still a problem with the current version. It's
+also hard for us to fix a problem of yours if we can't reproduce it because
+we don't have any systems running an environment that old.</p>
+
+<p>A consultant will happily set up a special environment just to reproduce
+your problem, and you can always ask on the list if any of the developers
+have consulting rates.</p>
+
+<hr />
+<p>
+<h2><a name="init">Busybox init isn't working!</a></h2>
+<p>
+ Init is the first program that runs, so it might be that no programs are
+ working on your new system because of a problem with your cross-compiler,
+ kernel, console settings, shared libraries, root filesystem... To rule all
+ that out, first build a statically linked version of the following "hello
+ world" program with your cross compiler toolchain:
+</p>
+<pre>
+#include &lt;stdio.h&gt;
+
+int main(int argc, char *argv)
+{
+ printf("Hello world!\n");
+ sleep(999999999);
+}
+</pre>
+
+<p>
+ Now try to boot your device with an "init=" argument pointing to your
+ hello world program. Did you see the hello world message? Until you
+ do, don't bother messing with busybox init.
+</p>
+
+<p>
+ Once you've got it working statically linked, try getting it to work
+ dynamically linked. Then read the FAQ entry <a href="#build_system">How
+ do I build a BusyBox-based system?</a>, and the
+ <a href="/downloads/BusyBox.html#item_init">documentation for BusyBox
+ init</a>.
+</p>
+
+<hr />
+<p>
+<h2><a name="sed">I can't configure busybox on my system.</a></h2>
+<p>
+ Configuring Busybox depends on a recent version of sed. Older
+ distributions (Red Hat 7.2, Debian 3.0) may not come with a
+ usable version. Luckily BusyBox can use its own sed to configure itself,
+ although this leads to a bit of a chicken and egg problem.
+ You can work around this by hand-configuring busybox to build with just
+ sed, then putting that sed in your path to configure the rest of busybox
+ with, like so:
+</p>
+
+<pre>
+ tar xvjf sources/busybox-x.x.x.tar.bz2
+ cd busybox-x.x.x
+ make allnoconfig
+ make include/bb_config.h
+ echo "CONFIG_SED=y" >> .config
+ echo "#undef ENABLE_SED" >> include/bb_config.h
+ echo "#define ENABLE_SED 1" >> include/bb_config.h
+ make
+ mv busybox sed
+ export PATH=`pwd`:"$PATH"
+</pre>
+
+<p>Then you can run "make defconfig" or "make menuconfig" normally.</p>
+
+<hr />
+<p>
+<h2><a name="job_control">Why do I keep getting "sh: can't access tty; job control turned off" errors? Why doesn't Control-C work within my shell?</a></h2>
+<p>
+
+ Job control will be turned off since your shell can not obtain a controlling
+ terminal. This typically happens when you run your shell on /dev/console.
+ The kernel will not provide a controlling terminal on the /dev/console
+ device. Your should run your shell on a normal tty such as tty1 or ttyS0
+ and everything will work perfectly. If you <em>REALLY</em> want your shell
+ to run on /dev/console, then you can hack your kernel (if you are into that
+ sortof thing) by changing drivers/char/tty_io.c to change the lines where
+ it sets "noctty = 1;" to instead set it to "0". I recommend you instead
+ run your shell on a real console...
+</p>
+
+<h1>Development</h1>
+
+<h2><b><a name="goals">What are the goals of busybox?</a></b></h2>
+
+<p>Busybox aims to be the smallest and simplest correct implementation of the
+standard Linux command line tools. First and foremost, this means the
+smallest executable size we can manage. We also want to have the simplest
+and cleanest implementation we can manage, be <a href="#standards">standards
+compliant</a>, minimize run-time memory usage (heap and stack), run fast, and
+take over the world.</p>
+
+<h2><b><a name="design">What is the design of busybox?</a></b></h2>
+
+<p>Busybox is like a swiss army knife: one thing with many functions.
+The busybox executable can act like many different programs depending on
+the name used to invoke it. Normal practice is to create a bunch of symlinks
+pointing to the busybox binary, each of which triggers a different busybox
+function. (See <a href="FAQ.html#getting_started">getting started</a> in the
+FAQ for more information on usage, and <a href="BusyBox.html">the
+busybox documentation</a> for a list of symlink names and what they do.)
+
+<p>The "one binary to rule them all" approach is primarily for size reasons: a
+single multi-purpose executable is smaller then many small files could be.
+This way busybox only has one set of ELF headers, it can easily share code
+between different apps even when statically linked, it has better packing
+efficiency by avoding gaps between files or compression dictionary resets,
+and so on.</p>
+
+<p>Work is underway on new options such as "make standalone" to build separate
+binaries for each applet, and a "libbb.so" to make the busybox common code
+available as a shared library. Neither is ready yet at the time of this
+writing.</p>
+
+<a name="source"></a>
+
+<h2><a name="source_applets"><b>The applet directories</b></a></h2>
+
+<p>The directory "applets" contains the busybox startup code (applets.c and
+busybox.c), and several subdirectories containing the code for the individual
+applets.</p>
+
+<p>Busybox execution starts with the main() function in applets/busybox.c,
+which sets the global variable applet_name to argv[0] and calls
+run_applet_by_name() in applets/applets.c. That uses the applets[] array
+(defined in include/busybox.h and filled out in include/applets.h) to
+transfer control to the appropriate APPLET_main() function (such as
+cat_main() or sed_main()). The individual applet takes it from there.</p>
+
+<p>This is why calling busybox under a different name triggers different
+functionality: main() looks up argv[0] in applets[] to get a function pointer
+to APPLET_main().</p>
+
+<p>Busybox applets may also be invoked through the multiplexor applet
+"busybox" (see busybox_main() in applets/busybox.c), and through the
+standalone shell (grep for STANDALONE_SHELL in applets/shell/*.c).
+See <a href="FAQ.html#getting_started">getting started</a> in the
+FAQ for more information on these alternate usage mechanisms, which are
+just different ways to reach the relevant APPLET_main() function.</p>
+
+<p>The applet subdirectories (archival, console-tools, coreutils,
+debianutils, e2fsprogs, editors, findutils, init, loginutils, miscutils,
+modutils, networking, procps, shell, sysklogd, and util-linux) correspond
+to the configuration sub-menus in menuconfig. Each subdirectory contains the
+code to implement the applets in that sub-menu, as well as a Config.in
+file defining that configuration sub-menu (with dependencies and help text
+for each applet), and the makefile segment (Makefile.in) for that
+subdirectory.</p>
+
+<p>The run-time --help is stored in usage_messages[], which is initialized at
+the start of applets/applets.c and gets its help text from usage.h. During the
+build this help text is also used to generate the BusyBox documentation (in
+html, txt, and man page formats) in the docs directory. See
+<a href="#adding">adding an applet to busybox</a> for more
+information.</p>
+
+<h2><a name="source_libbb"><b>libbb</b></a></h2>
+
+<p>Most non-setup code shared between busybox applets lives in the libbb
+directory. It's a mess that evolved over the years without much auditing
+or cleanup. For anybody looking for a great project to break into busybox
+development with, documenting libbb would be both incredibly useful and good
+experience.</p>
+
+<p>Common themes in libbb include allocation functions that test
+for failure and abort the program with an error message so the caller doesn't
+have to test the return value (xmalloc(), xstrdup(), etc), wrapped versions
+of open(), close(), read(), and write() that test for their own failures
+and/or retry automatically, linked list management functions (llist.c),
+command line argument parsing (getopt32.c), and a whole lot more.</p>
+
+<hr />
+<p>
+<h2><a name="optimize">I want to make busybox even smaller, how do I go about it?</a></h2>
+<p>
+ To conserve bytes it's good to know where they're being used, and the
+ size of the final executable isn't always a reliable indicator of
+ the size of the components (since various structures are rounded up,
+ so a small change may not even be visible by itself, but many small
+ savings add up).
+</p>
+
+<p> The busybox Makefile builds two versions of busybox, one of which
+ (busybox_unstripped) has extra information that various analysis tools
+ can use. (This has nothing to do with CONFIG_DEBUG, leave that off
+ when trying to optimize for size.)
+</p>
+
+<p> The <b>"make bloatcheck"</b> option uses Matt Mackall's bloat-o-meter
+ script to compare two versions of busybox (busybox_unstripped vs
+ busybox_old), and report which symbols changed size and by how much.
+ To use it, first build a base version with <b>"make baseline"</b>.
+ (This creates busybox_old, which should have the original sizes for
+ comparison purposes.) Then build the new version with your changes
+ and run "make bloatcheck" to see the size differences from the old
+ version.
+</p>
+<p>
+ The first line of output has totals: how many symbols were added or
+ removed, how many symbols grew or shrank, the number of bytes added
+ and number of bytes removed by these changes, and finally the total
+ number of bytes difference between the two files. The remaining
+ lines show each individual symbol, the old and new sizes, and the
+ increase or decrease in size (which results are sorted by).
+</p>
+<p>
+ The <b>"make sizes"</b> option produces raw symbol size information for
+ busybox_unstripped. This is the output from the "nm --size-sort"
+ command (see "man nm" for more information), and is the information
+ bloat-o-meter parses to produce the comparison report above. For
+ defconfig, this is a good way to find the largest symbols in the tree
+ (which is a good place to start when trying to shrink the code). To
+ take a closer look at individual applets, configure busybox with just
+ one applet (run "make allnoconfig" and then switch on a single applet
+ with menuconfig), and then use "make sizes" to see the size of that
+ applet's components.
+</p>
+<p>
+ The "showasm" command (in the scripts directory) produces an assembly
+ dump of a function, providing a closer look at what changed. Try
+ "scripts/showasm busybox_unstripped" to list available symbols, and
+ "scripts/showasm busybox_unstripped symbolname" to see the assembly
+ for a sepecific symbol.
+</p>
+<hr />
+
+
+
+<h2><a name="adding"><b>Adding an applet to busybox</b></a></h2>
+
+<p>To add a new applet to busybox, first pick a name for the applet and
+a corresponding CONFIG_NAME. Then do this:</p>
+
+<ul>
+<li>Figure out where in the busybox source tree your applet best fits,
+and put your source code there. Be sure to use APPLET_main() instead
+of main(), where APPLET is the name of your applet.</li>
+
+<li>Add your applet to the relevant Config.in file (which file you add
+it to determines where it shows up in "make menuconfig"). This uses
+the same general format as the linux kernel's configuration system.</li>
+
+<li>Add your applet to the relevant Makefile.in file (in the same
+directory as the Config.in you chose), using the existing entries as a
+template and the same CONFIG symbol as you used for Config.in. (Don't
+forget "needlibm" or "needcrypt" if your applet needs libm or
+libcrypt.)</li>
+
+<li>Add your applet to "include/applets.h", using one of the existing
+entries as a template. (Note: this is in alphabetical order. Applets
+are found via binary search, and if you add an applet out of order it
+won't work.)</li>
+
+<li>Add your applet's runtime help text to "include/usage.h". You need
+at least appname_trivial_usage (the minimal help text, always included
+in the busybox binary when this applet is enabled) and appname_full_usage
+(extra help text included in the busybox binary with
+CONFIG_FEATURE_VERBOSE_USAGE is enabled), or it won't compile.
+The other two help entry types (appname_example_usage and
+appname_notes_usage) are optional. They don't take up space in the binary,
+but instead show up in the generated documentation (BusyBox.html,
+BusyBox.txt, and the man page BusyBox.1).</li>
+
+<li>Run menuconfig, switch your applet on, compile, test, and fix the
+bugs. Be sure to try both "allyesconfig" and "allnoconfig" (and
+"allbareconfig" if relevant).</li>
+
+</ul>
+
+<h2><a name="standards">What standards does busybox adhere to?</a></h2>
+
+<p>The standard we're paying attention to is the "Shell and Utilities"
+portion of the <a href="http://www.opengroup.org/onlinepubs/009695399/">Open
+Group Base Standards</a> (also known as the Single Unix Specification version
+3 or SUSv3). Note that paying attention isn't necessarily the same thing as
+following it.</p>
+
+<p>SUSv3 doesn't even mention things like init, mount, tar, or losetup, nor
+commonly used options like echo's '-e' and '-n', or sed's '-i'. Busybox is
+driven by what real users actually need, not the fact the standard believes
+we should implement ed or sccs. For size reasons, we're unlikely to include
+much internationalization support beyond UTF-8, and on top of all that, our
+configuration menu lets developers chop out features to produce smaller but
+very non-standard utilities.</p>
+
+<p>Also, Busybox is aimed primarily at Linux. Unix standards are interesting
+because Linux tries to adhere to them, but portability to dozens of platforms
+is only interesting in terms of offering a restricted feature set that works
+everywhere, not growing dozens of platform-specific extensions. Busybox
+should be portable to all hardware platforms Linux supports, and any other
+similar operating systems that are easy to do and won't require much
+maintenance.</p>
+
+<p>In practice, standards compliance tends to be a clean-up step once an
+applet is otherwise finished. When polishing and testing a busybox applet,
+we ensure we have at least the option of full standards compliance, or else
+document where we (intentionally) fall short.</p>
+
+<h2><a name="portability">Portability.</a></h2>
+
+<p>Busybox is a Linux project, but that doesn't mean we don't have to worry
+about portability. First of all, there are different hardware platforms,
+different C library implementations, different versions of the kernel and
+build toolchain... The file "include/platform.h" exists to centralize and
+encapsulate various platform-specific things in one place, so most busybox
+code doesn't have to care where it's running.</p>
+
+<p>To start with, Linux runs on dozens of hardware platforms. We try to test
+each release on x86, x86-64, arm, power pc, and mips. (Since qemu can handle
+all of these, this isn't that hard.) This means we have to care about a number
+of portability issues like endianness, word size, and alignment, all of which
+belong in platform.h. That header handles conditional #includes and gives
+us macros we can use in the rest of our code. At some point in the future
+we might grow a platform.c, possibly even a platform subdirectory. As long
+as the applets themselves don't have to care.</p>
+
+<p>On a related note, we made the "default signedness of char varies" problem
+go away by feeding the compiler -funsigned-char. This gives us consistent
+behavior on all platforms, and defaults to 8-bit clean text processing (which
+gets us halfway to UTF-8 support). NOMMU support is less easily separated
+(see the tips section later in this document), but we're working on it.</p>
+
+<p>Another type of portability is build environments: we unapologetically use
+a number of gcc and glibc extensions (as does the Linux kernel), but these have
+been picked up by packages like uClibc, TCC, and Intel's C Compiler. As for
+gcc, we take advantage of newer compiler optimizations to get the smallest
+possible size, but we also regression test against an older build environment
+using the Red Hat 9 image at "http://busybox.net/downloads/qemu". This has a
+2.4 kernel, gcc 3.2, make 3.79.1, and glibc 2.3, and is the oldest
+build/deployment environment we still put any effort into maintaining. (If
+anyone takes an interest in older kernels you're welcome to submit patches,
+but the effort would probably be better spent
+<a href="http://www.selenic.com/linux-tiny/">trimming
+down the 2.6 kernel</a>.) Older gcc versions than that are uninteresting since
+we now use c99 features, although
+<a href="http://fabrice.bellard.free.fr/tcc/">tcc</a> might be worth a
+look.</p>
+
+<p>We also test busybox against the current release of uClibc. Older versions
+of uClibc aren't very interesting (they were buggy, and uClibc wasn't really
+usable as a general-purpose C library before version 0.9.26 anyway).</p>
+
+<p>Other unix implementations are mostly uninteresting, since Linux binaries
+have become the new standard for portable Unix programs. Specifically,
+the ubiquity of Linux was cited as the main reason the Intel Binary
+Compatability Standard 2 died, by the standards group organized to name a
+successor to ibcs2: <a href="http://www.telly.org/86open/">the 86open
+project</a>. That project disbanded in 1999 with the endorsement of an
+existing standard: Linux ELF binaries. Since then, the major players at the
+time (such as <a
+href=http://www-03.ibm.com/servers/aix/products/aixos/linux/index.html>AIX</a>, <a
+href=http://www.sun.com/software/solaris/ds/linux_interop.jsp#3>Solaris</a>, and
+<a href=http://www.onlamp.com/pub/a/bsd/2000/03/17/linuxapps.html>FreeBSD</a>)
+have all either grown Linux support or folded.</p>
+
+<p>The major exceptions are newcomer MacOS X, some embedded environments
+(such as newlib+libgloss) which provide a posix environment but not a full
+Linux environment, and environments like Cygwin that provide only partial Linux
+emulation. Also, some embedded Linux systems run a Linux kernel but amputate
+things like the /proc directory to save space.</p>
+
+<p>Supporting these systems is largely a question of providing a clean subset
+of BusyBox's functionality -- whichever applets can easily be made to
+work in that environment. Annotating the configuration system to
+indicate which applets require which prerequisites (such as procfs) is
+also welcome. Other efforts to support these systems (swapping #include
+files to build in different environments, adding adapter code to platform.h,
+adding more extensive special-case supporting infrastructure such as mount's
+legacy mtab support) are handled on a case-by-case basis. Support that can be
+cleanly hidden in platform.h is reasonably attractive, and failing that
+support that can be cleanly separated into a separate conditionally compiled
+file is at least worth a look. Special-case code in the body of an applet is
+something we're trying to avoid.</p>
+
+<h2><a name="tips" />Programming tips and tricks.</a></h2>
+
+<p>Various things busybox uses that aren't particularly well documented
+elsewhere.</p>
+
+<h2><a name="tips_encrypted_passwords">Encrypted Passwords</a></h2>
+
+<p>Password fields in /etc/passwd and /etc/shadow are in a special format.
+If the first character isn't '$', then it's an old DES style password. If
+the first character is '$' then the password is actually three fields
+separated by '$' characters:</p>
+<pre>
+ <b>$type$salt$encrypted_password</b>
+</pre>
+
+<p>The "type" indicates which encryption algorithm to use: 1 for MD5 and 2 for SHA1.</p>
+
+<p>The "salt" is a bunch of ramdom characters (generally 8) the encryption
+algorithm uses to perturb the password in a known and reproducible way (such
+as by appending the random data to the unencrypted password, or combining
+them with exclusive or). Salt is randomly generated when setting a password,
+and then the same salt value is re-used when checking the password. (Salt is
+thus stored unencrypted.)</p>
+
+<p>The advantage of using salt is that the same cleartext password encrypted
+with a different salt value produces a different encrypted value.
+If each encrypted password uses a different salt value, an attacker is forced
+to do the cryptographic math all over again for each password they want to
+check. Without salt, they could simply produce a big dictionary of commonly
+used passwords ahead of time, and look up each password in a stolen password
+file to see if it's a known value. (Even if there are billions of possible
+passwords in the dictionary, checking each one is just a binary search against
+a file only a few gigabytes long.) With salt they can't even tell if two
+different users share the same password without guessing what that password
+is and decrypting it. They also can't precompute the attack dictionary for
+a specific password until they know what the salt value is.</p>
+
+<p>The third field is the encrypted password (plus the salt). For md5 this
+is 22 bytes.</p>
+
+<p>The busybox function to handle all this is pw_encrypt(clear, salt) in
+"libbb/pw_encrypt.c". The first argument is the clear text password to be
+encrypted, and the second is a string in "$type$salt$password" format, from
+which the "type" and "salt" fields will be extracted to produce an encrypted
+value. (Only the first two fields are needed, the third $ is equivalent to
+the end of the string.) The return value is an encrypted password in
+/etc/passwd format, with all three $ separated fields. It's stored in
+a static buffer, 128 bytes long.</p>
+
+<p>So when checking an existing password, if pw_encrypt(text,
+old_encrypted_password) returns a string that compares identical to
+old_encrypted_password, you've got the right password. When setting a new
+password, generate a random 8 character salt string, put it in the right
+format with sprintf(buffer, "$%c$%s", type, salt), and feed buffer as the
+second argument to pw_encrypt(text,buffer).</p>
+
+<h2><a name="tips_vfork">Fork and vfork</a></h2>
+
+<p>On systems that haven't got a Memory Management Unit, fork() is unreasonably
+expensive to implement (and sometimes even impossible), so a less capable
+function called vfork() is used instead. (Using vfork() on a system with an
+MMU is like pounding a nail with a wrench. Not the best tool for the job, but
+it works.)</p>
+
+<p>Busybox hides the difference between fork() and vfork() in
+libbb/bb_fork_exec.c. If you ever want to fork and exec, use bb_fork_exec()
+(which returns a pid and takes the same arguments as execve(), although in
+this case envp can be NULL) and don't worry about it. This description is
+here in case you want to know why that does what it does.</p>
+
+<p>Implementing fork() depends on having a Memory Management Unit. With an
+MMU then you can simply set up a second set of page tables and share the
+physical memory via copy-on-write. So a fork() followed quickly by exec()
+only copies a few pages of the parent's memory, just the ones it changes
+before freeing them.</p>
+
+<p>With a very primitive MMU (using a base pointer plus length instead of page
+tables, which can provide virtual addresses and protect processes from each
+other, but no copy on write) you can still implement fork. But it's
+unreasonably expensive, because you have to copy all the parent process'
+memory into the new process (which could easily be several megabytes per fork).
+And you have to do this even though that memory gets freed again as soon as the
+exec happens. (This is not just slow and a waste of space but causes memory
+usage spikes that can easily cause the system to run out of memory.)</p>
+
+<p>Without even a primitive MMU, you have no virtual addresses. Every process
+can reach out and touch any other process' memory, because all pointers are to
+physical addresses with no protection. Even if you copy a process' memory to
+new physical addresses, all of its pointers point to the old objects in the
+old process. (Searching through the new copy's memory for pointers and
+redirect them to the new locations is not an easy problem.)</p>
+
+<p>So with a primitive or missing MMU, fork() is just not a good idea.</p>
+
+<p>In theory, vfork() is just a fork() that writeably shares the heap and stack
+rather than copying it (so what one process writes the other one sees). In
+practice, vfork() has to suspend the parent process until the child does exec,
+at which point the parent wakes up and resumes by returning from the call to
+vfork(). All modern kernel/libc combinations implement vfork() to put the
+parent to sleep until the child does its exec. There's just no other way to
+make it work: the parent has to know the child has done its exec() or exit()
+before it's safe to return from the function it's in, so it has to block
+until that happens. In fact without suspending the parent there's no way to
+even store separate copies of the return value (the pid) from the vfork() call
+itself: both assignments write into the same memory location.</p>
+
+<p>One way to understand (and in fact implement) vfork() is this: imagine
+the parent does a setjmp and then continues on (pretending to be the child)
+until the exec() comes around, then the _exec_ does the actual fork, and the
+parent does a longjmp back to the original vfork call and continues on from
+there. (It thus becomes obvious why the child can't return, or modify
+local variables it doesn't want the parent to see changed when it resumes.)
+
+<p>Note a common mistake: the need for vfork doesn't mean you can't have two
+processes running at the same time. It means you can't have two processes
+sharing the same memory without stomping all over each other. As soon as
+the child calls exec(), the parent resumes.</p>
+
+<p>If the child's attempt to call exec() fails, the child should call _exit()
+rather than a normal exit(). This avoids any atexit() code that might confuse
+the parent. (The parent should never call _exit(), only a vforked child that
+failed to exec.)</p>
+
+<p>(Now in theory, a nommu system could just copy the _stack_ when it forks
+(which presumably is much shorter than the heap), and leave the heap shared.
+Even with no MMU at all
+In practice, you've just wound up in a multi-threaded situation and you can't
+do a malloc() or free() on your heap without freeing the other process' memory
+(and if you don't have the proper locking for being threaded, corrupting the
+heap if both of you try to do it at the same time and wind up stomping on
+each other while traversing the free memory lists). The thing about vfork is
+that it's a big red flag warning "there be dragons here" rather than
+something subtle and thus even more dangerous.)</p>
+
+<h2><a name="tips_sort_read">Short reads and writes</a></h2>
+
+<p>Busybox has special functions, bb_full_read() and bb_full_write(), to
+check that all the data we asked for got read or written. Is this a real
+world consideration? Try the following:</p>
+
+<pre>while true; do echo hello; sleep 1; done | tee out.txt</pre>
+
+<p>If tee is implemented with bb_full_read(), tee doesn't display output
+in real time but blocks until its entire input buffer (generally a couple
+kilobytes) is read, then displays it all at once. In that case, we _want_
+the short read, for user interface reasons. (Note that read() should never
+return 0 unless it has hit the end of input, and an attempt to write 0
+bytes should be ignored by the OS.)</p>
+
+<p>As for short writes, play around with two processes piping data to each
+other on the command line (cat bigfile | gzip &gt; out.gz) and suspend and
+resume a few times (ctrl-z to suspend, "fg" to resume). The writer can
+experience short writes, which are especially dangerous because if you don't
+notice them you'll discard data. They can also happen when a system is under
+load and a fast process is piping to a slower one. (Such as an xterm waiting
+on x11 when the scheduler decides X is being a CPU hog with all that
+text console scrolling...)</p>
+
+<p>So will data always be read from the far end of a pipe at the
+same chunk sizes it was written in? Nope. Don't rely on that. For one
+counterexample, see <a href="http://www.faqs.org/rfcs/rfc896.html">rfc 896
+for Nagle's algorithm</a>, which waits a fraction of a second or so before
+sending out small amounts of data through a TCP/IP connection in case more
+data comes in that can be merged into the same packet. (In case you were
+wondering why action games that use TCP/IP set TCP_NODELAY to lower the latency
+on their their sockets, now you know.)</p>
+
+<h2><a name="tips_memory">Memory used by relocatable code, PIC, and static linking.</a></h2>
+
+<p>The downside of standard dynamic linking is that it results in self-modifying
+code. Although each executable's pages are mmaped() into a process' address
+space from the executable file and are thus naturally shared between processes
+out of the page cache, the library loader (ld-linux.so.2 or ld-uClibc.so.0)
+writes to these pages to supply addresses for relocatable symbols. This
+dirties the pages, triggering copy-on-write allocation of new memory for each
+processes' dirtied pages.</p>
+
+<p>One solution to this is Position Independent Code (PIC), a way of linking
+a file so all the relocations are grouped together. This dirties fewer
+pages (often just a single page) for each process' relocations. The down
+side is this results in larger executables, which take up more space on disk
+(and a correspondingly larger space in memory). But when many copies of the
+same program are running, PIC dynamic linking trades a larger disk footprint
+for a smaller memory footprint, by sharing more pages.</p>
+
+<p>A third solution is static linking. A statically linked program has no
+relocations, and thus the entire executable is shared between all running
+instances. This tends to have a significantly larger disk footprint, but
+on a system with only one or two executables, shared libraries aren't much
+of a win anyway.</p>
+
+<p>You can tell the glibc linker to display debugging information about its
+relocations with the environment variable "LD_DEBUG". Try
+"LD_DEBUG=help /bin/true" for a list of commands. Learning to interpret
+"LD_DEBUG=statistics cat /proc/self/statm" could be interesting.</p>
+
+<p>For more on this topic, here's Rich Felker:</p>
+<blockquote>
+<p>Dynamic linking (without fixed load addresses) fundamentally requires
+at least one dirty page per dso that uses symbols. Making calls (but
+never taking the address explicitly) to functions within the same dso
+does not require a dirty page by itself, but will with ELF unless you
+use -Bsymbolic or hidden symbols when linking.</p>
+
+<p>ELF uses significant additional stack space for the kernel to pass all
+the ELF data structures to the newly created process image. These are
+located above the argument list and environment. This normally adds 1
+dirty page to the process size.</p>
+
+<p>The ELF dynamic linker has its own data segment, adding one or more
+dirty pages. I believe it also performs relocations on itself.</p>
+
+<p>The ELF dynamic linker makes significant dynamic allocations to manage
+the global symbol table and the loaded dso's. This data is never
+freed. It will be needed again if libdl is used, so unconditionally
+freeing it is not possible, but normal programs do not use libdl. Of
+course with glibc all programs use libdl (due to nsswitch) so the
+issue was never addressed.</p>
+
+<p>ELF also has the issue that segments are not page-aligned on disk.
+This saves up to 4k on disk, but at the expense of using an additional
+dirty page in most cases, due to a large portion of the first data
+page being filled with a duplicate copy of the last text page.</p>
+
+<p>The above is just a partial list of the tiny memory penalties of ELF
+dynamic linking, which eventually add up to quite a bit. The smallest
+I've been able to get a process down to is 8 dirty pages, and the
+above factors seem to mostly account for it (but some were difficult
+to measure).</p>
+</blockquote>
+
+<h2><a name="tips_kernel_headers"></a>Including kernel headers</h2>
+
+<p>The "linux" or "asm" directories of /usr/include contain Linux kernel
+headers, so that the C library can talk directly to the Linux kernel. In
+a perfect world, applications shouldn't include these headers directly, but
+we don't live in a perfect world.</p>
+
+<p>For example, Busybox's losetup code wants linux/loop.c because nothing else
+#defines the structures to call the kernel's loopback device setup ioctls.
+Attempts to cut and paste the information into a local busybox header file
+proved incredibly painful, because portions of the loop_info structure vary by
+architecture, namely the type __kernel_dev_t has different sizes on alpha,
+arm, x86, and so on. Meaning we either #include <linux/posix_types.h> or
+we hardwire #ifdefs to check what platform we're building on and define this
+type appropriately for every single hardware architecture supported by
+Linux, which is simply unworkable.</p>
+
+<p>This is aside from the fact that the relevant type defined in
+posix_types.h was renamed to __kernel_old_dev_t during the 2.5 series, so
+to cut and paste the structure into our header we have to #include
+<linux/version.h> to figure out which name to use. (What we actually do is
+check if we're building on 2.6, and if so just use the new 64 bit structure
+instead to avoid the rename entirely.) But we still need the version
+check, since 2.4 didn't have the 64 bit structure.</p>
+
+<p>The BusyBox developers spent <u>two years</u> trying to figure
+out a clean way to do all this. There isn't one. The losetup in the
+util-linux package from kernel.org isn't doing it cleanly either, they just
+hide the ugliness by nesting #include files. Their mount/loop.h
+#includes "my_dev_t.h", which #includes <linux/posix_types.h> and
+<linux/version.h> just like we do. There simply is no alternative.</p>
+
+<p>Just because directly #including kernel headers is sometimes
+unavoidable doesn't me we should include them when there's a better
+way to do it. However, block copying information out of the kernel headers
+is not a better way.</p>
+
+<h2><a name="who">Who are the BusyBox developers?</a></h2>
+
+<p>The following login accounts currently exist on busybox.net. (I.E. these
+people can commit <a href="http://busybox.net/downloads/patches">patches</a>
+into subversion for the BusyBox, uClibc, and buildroot projects.)</p>
+
+<pre>
+aldot :Bernhard Fischer
+andersen :Erik Andersen - uClibc and BuildRoot maintainer.
+bug1 :Glenn McGrath
+davidm :David McCullough
+gkajmowi :Garrett Kajmowicz - uClibc++ maintainer
+jbglaw :Jan-Benedict Glaw
+jocke :Joakim Tjernlund
+landley :Rob Landley - BusyBox maintainer
+lethal :Paul Mundt
+mjn3 :Manuel Novoa III
+osuadmin :osuadmin
+pgf :Paul Fox
+pkj :Peter Kjellerstedt
+prpplague :David Anders
+psm :Peter S. Mazinger
+russ :Russ Dill
+sandman :Robert Griebl
+sjhill :Steven J. Hill
+solar :Ned Ludd
+timr :Tim Riker
+tobiasa :Tobias Anderberg
+vapier :Mike Frysinger
+</pre>
+
+<p>The following accounts used to exist on busybox.net, but don't anymore so
+I can't ask /etc/passwd for their names. Rob Wentworth <robwen@gmail.com>
+asked Google and recovered the names:</p>
+
+<pre>
+aaronl :Aaron Lehmann
+beppu :John Beppu
+dwhedon :David Whedon
+erik :Erik Andersen
+gfeldman :Gennady Feldman
+jimg :Jim Gleason
+kraai :Matt Kraai
+markw :Mark Whitley
+miles :Miles Bader
+proski :Pavel Roskin
+rjune :Richard June
+tausq :Randolph Chung
+vodz :Vladimir N. Oleynik
+</pre>
+
+
+<br>
+<br>
+<br>
+
+<!--#include file="footer.html" -->
diff --git a/i/pc104/initrd/conf/busybox/docs/busybox.net/about.html b/i/pc104/initrd/conf/busybox/docs/busybox.net/about.html
new file mode 100644
index 0000000..e3b937b
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/docs/busybox.net/about.html
@@ -0,0 +1,24 @@
+<!--#include file="header.html" -->
+
+<h3>BusyBox: The Swiss Army Knife of Embedded Linux</h3>
+
+<p>BusyBox combines tiny versions of many common UNIX utilities into a single
+small executable. It provides replacements for most of the utilities you
+usually find in GNU fileutils, shellutils, etc. The utilities in BusyBox
+generally have fewer options than their full-featured GNU cousins; however,
+the options that are included provide the expected functionality and behave
+very much like their GNU counterparts. BusyBox provides a fairly complete
+environment for any small or embedded system.</p>
+
+<p>BusyBox has been written with size-optimization and limited resources in
+mind. It is also extremely modular so you can easily include or exclude
+commands (or features) at compile time. This makes it easy to customize
+your embedded systems. To create a working system, just add some device
+nodes in /dev, a few configuration files in /etc, and a Linux kernel.</p>
+
+<p>BusyBox is maintained by
+<a href="mailto:vda.linux@googlemail.com">Denis Vlasenko</a>,
+and licensed under the <a href="/license.html">GNU GENERAL PUBLIC LICENSE</a>
+version 2 or later.</p>
+
+<!--#include file="footer.html" -->
diff --git a/i/pc104/initrd/conf/busybox/docs/busybox.net/busybox-growth.ps b/i/pc104/initrd/conf/busybox/docs/busybox.net/busybox-growth.ps
new file mode 100644
index 0000000..2379def
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/docs/busybox.net/busybox-growth.ps
@@ -0,0 +1,404 @@
+%!PS-Adobe-2.0
+%%Title: busybox-growth.ps
+%%Creator: gnuplot 3.5 (pre 3.6) patchlevel beta 347
+%%CreationDate: Tue Apr 10 14:03:36 2001
+%%DocumentFonts: (atend)
+%%BoundingBox: 50 40 554 770
+%%Orientation: Landscape
+%%Pages: (atend)
+%%EndComments
+/gnudict 120 dict def
+gnudict begin
+/Color true def
+/Solid true def
+/gnulinewidth 5.000 def
+/userlinewidth gnulinewidth def
+/vshift -46 def
+/dl {10 mul} def
+/hpt_ 31.5 def
+/vpt_ 31.5 def
+/hpt hpt_ def
+/vpt vpt_ def
+/M {moveto} bind def
+/L {lineto} bind def
+/R {rmoveto} bind def
+/V {rlineto} bind def
+/vpt2 vpt 2 mul def
+/hpt2 hpt 2 mul def
+/Lshow { currentpoint stroke M
+ 0 vshift R show } def
+/Rshow { currentpoint stroke M
+ dup stringwidth pop neg vshift R show } def
+/Cshow { currentpoint stroke M
+ dup stringwidth pop -2 div vshift R show } def
+/UP { dup vpt_ mul /vpt exch def hpt_ mul /hpt exch def
+ /hpt2 hpt 2 mul def /vpt2 vpt 2 mul def } def
+/DL { Color {setrgbcolor Solid {pop []} if 0 setdash }
+ {pop pop pop Solid {pop []} if 0 setdash} ifelse } def
+/BL { stroke gnulinewidth 2 mul setlinewidth } def
+/AL { stroke gnulinewidth 2 div setlinewidth } def
+/UL { gnulinewidth mul /userlinewidth exch def } def
+/PL { stroke userlinewidth setlinewidth } def
+/LTb { BL [] 0 0 0 DL } def
+/LTa { AL [1 dl 2 dl] 0 setdash 0 0 0 setrgbcolor } def
+/LT0 { PL [] 1 0 0 DL } def
+/LT1 { PL [4 dl 2 dl] 0 1 0 DL } def
+/LT2 { PL [2 dl 3 dl] 0 0 1 DL } def
+/LT3 { PL [1 dl 1.5 dl] 1 0 1 DL } def
+/LT4 { PL [5 dl 2 dl 1 dl 2 dl] 0 1 1 DL } def
+/LT5 { PL [4 dl 3 dl 1 dl 3 dl] 1 1 0 DL } def
+/LT6 { PL [2 dl 2 dl 2 dl 4 dl] 0 0 0 DL } def
+/LT7 { PL [2 dl 2 dl 2 dl 2 dl 2 dl 4 dl] 1 0.3 0 DL } def
+/LT8 { PL [2 dl 2 dl 2 dl 2 dl 2 dl 2 dl 2 dl 4 dl] 0.5 0.5 0.5 DL } def
+/Pnt { stroke [] 0 setdash
+ gsave 1 setlinecap M 0 0 V stroke grestore } def
+/Dia { stroke [] 0 setdash 2 copy vpt add M
+ hpt neg vpt neg V hpt vpt neg V
+ hpt vpt V hpt neg vpt V closepath stroke
+ Pnt } def
+/Pls { stroke [] 0 setdash vpt sub M 0 vpt2 V
+ currentpoint stroke M
+ hpt neg vpt neg R hpt2 0 V stroke
+ } def
+/Box { stroke [] 0 setdash 2 copy exch hpt sub exch vpt add M
+ 0 vpt2 neg V hpt2 0 V 0 vpt2 V
+ hpt2 neg 0 V closepath stroke
+ Pnt } def
+/Crs { stroke [] 0 setdash exch hpt sub exch vpt add M
+ hpt2 vpt2 neg V currentpoint stroke M
+ hpt2 neg 0 R hpt2 vpt2 V stroke } def
+/TriU { stroke [] 0 setdash 2 copy vpt 1.12 mul add M
+ hpt neg vpt -1.62 mul V
+ hpt 2 mul 0 V
+ hpt neg vpt 1.62 mul V closepath stroke
+ Pnt } def
+/Star { 2 copy Pls Crs } def
+/BoxF { stroke [] 0 setdash exch hpt sub exch vpt add M
+ 0 vpt2 neg V hpt2 0 V 0 vpt2 V
+ hpt2 neg 0 V closepath fill } def
+/TriUF { stroke [] 0 setdash vpt 1.12 mul add M
+ hpt neg vpt -1.62 mul V
+ hpt 2 mul 0 V
+ hpt neg vpt 1.62 mul V closepath fill } def
+/TriD { stroke [] 0 setdash 2 copy vpt 1.12 mul sub M
+ hpt neg vpt 1.62 mul V
+ hpt 2 mul 0 V
+ hpt neg vpt -1.62 mul V closepath stroke
+ Pnt } def
+/TriDF { stroke [] 0 setdash vpt 1.12 mul sub M
+ hpt neg vpt 1.62 mul V
+ hpt 2 mul 0 V
+ hpt neg vpt -1.62 mul V closepath fill} def
+/DiaF { stroke [] 0 setdash vpt add M
+ hpt neg vpt neg V hpt vpt neg V
+ hpt vpt V hpt neg vpt V closepath fill } def
+/Pent { stroke [] 0 setdash 2 copy gsave
+ translate 0 hpt M 4 {72 rotate 0 hpt L} repeat
+ closepath stroke grestore Pnt } def
+/PentF { stroke [] 0 setdash gsave
+ translate 0 hpt M 4 {72 rotate 0 hpt L} repeat
+ closepath fill grestore } def
+/Circle { stroke [] 0 setdash 2 copy
+ hpt 0 360 arc stroke Pnt } def
+/CircleF { stroke [] 0 setdash hpt 0 360 arc fill } def
+/C0 { BL [] 0 setdash 2 copy moveto vpt 90 450 arc } bind def
+/C1 { BL [] 0 setdash 2 copy moveto
+ 2 copy vpt 0 90 arc closepath fill
+ vpt 0 360 arc closepath } bind def
+/C2 { BL [] 0 setdash 2 copy moveto
+ 2 copy vpt 90 180 arc closepath fill
+ vpt 0 360 arc closepath } bind def
+/C3 { BL [] 0 setdash 2 copy moveto
+ 2 copy vpt 0 180 arc closepath fill
+ vpt 0 360 arc closepath } bind def
+/C4 { BL [] 0 setdash 2 copy moveto
+ 2 copy vpt 180 270 arc closepath fill
+ vpt 0 360 arc closepath } bind def
+/C5 { BL [] 0 setdash 2 copy moveto
+ 2 copy vpt 0 90 arc
+ 2 copy moveto
+ 2 copy vpt 180 270 arc closepath fill
+ vpt 0 360 arc } bind def
+/C6 { BL [] 0 setdash 2 copy moveto
+ 2 copy vpt 90 270 arc closepath fill
+ vpt 0 360 arc closepath } bind def
+/C7 { BL [] 0 setdash 2 copy moveto
+ 2 copy vpt 0 270 arc closepath fill
+ vpt 0 360 arc closepath } bind def
+/C8 { BL [] 0 setdash 2 copy moveto
+ 2 copy vpt 270 360 arc closepath fill
+ vpt 0 360 arc closepath } bind def
+/C9 { BL [] 0 setdash 2 copy moveto
+ 2 copy vpt 270 450 arc closepath fill
+ vpt 0 360 arc closepath } bind def
+/C10 { BL [] 0 setdash 2 copy 2 copy moveto vpt 270 360 arc closepath fill
+ 2 copy moveto
+ 2 copy vpt 90 180 arc closepath fill
+ vpt 0 360 arc closepath } bind def
+/C11 { BL [] 0 setdash 2 copy moveto
+ 2 copy vpt 0 180 arc closepath fill
+ 2 copy moveto
+ 2 copy vpt 270 360 arc closepath fill
+ vpt 0 360 arc closepath } bind def
+/C12 { BL [] 0 setdash 2 copy moveto
+ 2 copy vpt 180 360 arc closepath fill
+ vpt 0 360 arc closepath } bind def
+/C13 { BL [] 0 setdash 2 copy moveto
+ 2 copy vpt 0 90 arc closepath fill
+ 2 copy moveto
+ 2 copy vpt 180 360 arc closepath fill
+ vpt 0 360 arc closepath } bind def
+/C14 { BL [] 0 setdash 2 copy moveto
+ 2 copy vpt 90 360 arc closepath fill
+ vpt 0 360 arc } bind def
+/C15 { BL [] 0 setdash 2 copy vpt 0 360 arc closepath fill
+ vpt 0 360 arc closepath } bind def
+/Rec { newpath 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto
+ neg 0 rlineto closepath } bind def
+/Square { dup Rec } bind def
+/Bsquare { vpt sub exch vpt sub exch vpt2 Square } bind def
+/S0 { BL [] 0 setdash 2 copy moveto 0 vpt rlineto BL Bsquare } bind def
+/S1 { BL [] 0 setdash 2 copy vpt Square fill Bsquare } bind def
+/S2 { BL [] 0 setdash 2 copy exch vpt sub exch vpt Square fill Bsquare } bind def
+/S3 { BL [] 0 setdash 2 copy exch vpt sub exch vpt2 vpt Rec fill Bsquare } bind def
+/S4 { BL [] 0 setdash 2 copy exch vpt sub exch vpt sub vpt Square fill Bsquare } bind def
+/S5 { BL [] 0 setdash 2 copy 2 copy vpt Square fill
+ exch vpt sub exch vpt sub vpt Square fill Bsquare } bind def
+/S6 { BL [] 0 setdash 2 copy exch vpt sub exch vpt sub vpt vpt2 Rec fill Bsquare } bind def
+/S7 { BL [] 0 setdash 2 copy exch vpt sub exch vpt sub vpt vpt2 Rec fill
+ 2 copy vpt Square fill
+ Bsquare } bind def
+/S8 { BL [] 0 setdash 2 copy vpt sub vpt Square fill Bsquare } bind def
+/S9 { BL [] 0 setdash 2 copy vpt sub vpt vpt2 Rec fill Bsquare } bind def
+/S10 { BL [] 0 setdash 2 copy vpt sub vpt Square fill 2 copy exch vpt sub exch vpt Square fill
+ Bsquare } bind def
+/S11 { BL [] 0 setdash 2 copy vpt sub vpt Square fill 2 copy exch vpt sub exch vpt2 vpt Rec fill
+ Bsquare } bind def
+/S12 { BL [] 0 setdash 2 copy exch vpt sub exch vpt sub vpt2 vpt Rec fill Bsquare } bind def
+/S13 { BL [] 0 setdash 2 copy exch vpt sub exch vpt sub vpt2 vpt Rec fill
+ 2 copy vpt Square fill Bsquare } bind def
+/S14 { BL [] 0 setdash 2 copy exch vpt sub exch vpt sub vpt2 vpt Rec fill
+ 2 copy exch vpt sub exch vpt Square fill Bsquare } bind def
+/S15 { BL [] 0 setdash 2 copy Bsquare fill Bsquare } bind def
+/D0 { gsave translate 45 rotate 0 0 S0 stroke grestore } bind def
+/D1 { gsave translate 45 rotate 0 0 S1 stroke grestore } bind def
+/D2 { gsave translate 45 rotate 0 0 S2 stroke grestore } bind def
+/D3 { gsave translate 45 rotate 0 0 S3 stroke grestore } bind def
+/D4 { gsave translate 45 rotate 0 0 S4 stroke grestore } bind def
+/D5 { gsave translate 45 rotate 0 0 S5 stroke grestore } bind def
+/D6 { gsave translate 45 rotate 0 0 S6 stroke grestore } bind def
+/D7 { gsave translate 45 rotate 0 0 S7 stroke grestore } bind def
+/D8 { gsave translate 45 rotate 0 0 S8 stroke grestore } bind def
+/D9 { gsave translate 45 rotate 0 0 S9 stroke grestore } bind def
+/D10 { gsave translate 45 rotate 0 0 S10 stroke grestore } bind def
+/D11 { gsave translate 45 rotate 0 0 S11 stroke grestore } bind def
+/D12 { gsave translate 45 rotate 0 0 S12 stroke grestore } bind def
+/D13 { gsave translate 45 rotate 0 0 S13 stroke grestore } bind def
+/D14 { gsave translate 45 rotate 0 0 S14 stroke grestore } bind def
+/D15 { gsave translate 45 rotate 0 0 S15 stroke grestore } bind def
+/DiaE { stroke [] 0 setdash vpt add M
+ hpt neg vpt neg V hpt vpt neg V
+ hpt vpt V hpt neg vpt V closepath stroke } def
+/BoxE { stroke [] 0 setdash exch hpt sub exch vpt add M
+ 0 vpt2 neg V hpt2 0 V 0 vpt2 V
+ hpt2 neg 0 V closepath stroke } def
+/TriUE { stroke [] 0 setdash vpt 1.12 mul add M
+ hpt neg vpt -1.62 mul V
+ hpt 2 mul 0 V
+ hpt neg vpt 1.62 mul V closepath stroke } def
+/TriDE { stroke [] 0 setdash vpt 1.12 mul sub M
+ hpt neg vpt 1.62 mul V
+ hpt 2 mul 0 V
+ hpt neg vpt -1.62 mul V closepath stroke } def
+/PentE { stroke [] 0 setdash gsave
+ translate 0 hpt M 4 {72 rotate 0 hpt L} repeat
+ closepath stroke grestore } def
+/CircE { stroke [] 0 setdash
+ hpt 0 360 arc stroke } def
+/Opaque { gsave closepath 1 setgray fill grestore 0 setgray closepath } def
+/DiaW { stroke [] 0 setdash vpt add M
+ hpt neg vpt neg V hpt vpt neg V
+ hpt vpt V hpt neg vpt V Opaque stroke } def
+/BoxW { stroke [] 0 setdash exch hpt sub exch vpt add M
+ 0 vpt2 neg V hpt2 0 V 0 vpt2 V
+ hpt2 neg 0 V Opaque stroke } def
+/TriUW { stroke [] 0 setdash vpt 1.12 mul add M
+ hpt neg vpt -1.62 mul V
+ hpt 2 mul 0 V
+ hpt neg vpt 1.62 mul V Opaque stroke } def
+/TriDW { stroke [] 0 setdash vpt 1.12 mul sub M
+ hpt neg vpt 1.62 mul V
+ hpt 2 mul 0 V
+ hpt neg vpt -1.62 mul V Opaque stroke } def
+/PentW { stroke [] 0 setdash gsave
+ translate 0 hpt M 4 {72 rotate 0 hpt L} repeat
+ Opaque stroke grestore } def
+/CircW { stroke [] 0 setdash
+ hpt 0 360 arc Opaque stroke } def
+/BoxFill { gsave Rec 1 setgray fill grestore } def
+end
+%%EndProlog
+%%Page: 1 1
+gnudict begin
+gsave
+50 50 translate
+0.100 0.100 scale
+90 rotate
+0 -5040 translate
+0 setgray
+newpath
+(Helvetica) findfont 140 scalefont setfont
+1.000 UL
+LTb
+560 420 M
+63 0 V
+6409 0 R
+-63 0 V
+476 420 M
+(0) Rshow
+560 1056 M
+63 0 V
+6409 0 R
+-63 0 V
+-6493 0 R
+(100) Rshow
+560 1692 M
+63 0 V
+6409 0 R
+-63 0 V
+-6493 0 R
+(200) Rshow
+560 2328 M
+63 0 V
+6409 0 R
+-63 0 V
+-6493 0 R
+(300) Rshow
+560 2964 M
+63 0 V
+6409 0 R
+-63 0 V
+-6493 0 R
+(400) Rshow
+560 3600 M
+63 0 V
+6409 0 R
+-63 0 V
+-6493 0 R
+(500) Rshow
+560 4236 M
+63 0 V
+6409 0 R
+-63 0 V
+-6493 0 R
+(600) Rshow
+560 4872 M
+63 0 V
+6409 0 R
+-63 0 V
+-6493 0 R
+(700) Rshow
+1531 420 M
+0 63 V
+0 4389 R
+0 -63 V
+0 -4529 R
+(400) Cshow
+2825 420 M
+0 63 V
+0 4389 R
+0 -63 V
+0 -4529 R
+(600) Cshow
+4120 420 M
+0 63 V
+0 4389 R
+0 -63 V
+0 -4529 R
+(800) Cshow
+5414 420 M
+0 63 V
+0 4389 R
+0 -63 V
+0 -4529 R
+(1000) Cshow
+6708 420 M
+0 63 V
+0 4389 R
+0 -63 V
+0 -4529 R
+(1200) Cshow
+1.000 UL
+LTb
+560 420 M
+6472 0 V
+0 4452 V
+-6472 0 V
+560 420 L
+0 2646 M
+currentpoint gsave translate 90 rotate 0 0 M
+(tar.gz size \(Kb\)) Cshow
+grestore
+3796 140 M
+(time \(days since Jan 1, 1998\)) Cshow
+1.000 UL
+LT0
+696 420 M
+0 593 V
+1255 0 V
+0 15 V
+214 0 V
+0 6 V
+958 0 V
+0 1 V
+-84 0 V
+0 37 V
+168 0 V
+0 262 V
+13 0 V
+0 56 V
+91 0 V
+0 33 V
+6 0 V
+0 1 V
+19 0 V
+0 11 V
+20 0 V
+0 13 V
+32 0 V
+0 104 V
+52 0 V
+0 27 V
+65 0 V
+0 15 V
+39 0 V
+0 126 V
+174 0 V
+0 103 V
+52 0 V
+0 49 V
+175 0 V
+0 56 V
+433 0 V
+0 661 V
+415 0 V
+0 857 V
+123 0 V
+0 -291 V
+498 0 V
+0 208 V
+505 0 V
+0 66 V
+291 0 V
+0 115 V
+311 0 V
+0 449 V
+162 0 V
+0 309 V
+stroke
+grestore
+end
+showpage
+%%Trailer
+%%DocumentFonts: Helvetica
+%%Pages: 1
diff --git a/i/pc104/initrd/conf/busybox/docs/busybox.net/copyright.txt b/i/pc104/initrd/conf/busybox/docs/busybox.net/copyright.txt
new file mode 100644
index 0000000..3974756
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/docs/busybox.net/copyright.txt
@@ -0,0 +1,30 @@
+
+The code and graphics on this website (and it's mirror sites, if any) are
+Copyright (c) 1999-2004 by Erik Andersen. All rights reserved.
+Copyright (c) 2005-2006 Rob Landley.
+
+Documents on this Web site including their graphical elements, design, and
+layout are protected by trade dress and other laws and MAY BE COPIED OR
+IMITATED IN WHOLE OR IN PART. THIS WEBSITE IS LICENSED FREE OF CHARGE, THERE
+IS NO WARRANTY FOR THE WEBSITE TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+SHOULD THIS WEBSITE PROVE DEFECTIVE, YOU MAY ASSUME THAT SOMEONE MIGHT GET
+AROUND TO SERVICING, REPAIRING OR CORRECTING IT SOMETIME WHEN THEY HAVE NOTHING
+BETTER TO DO. REGARDLESS, YOU GET TO KEEP BOTH PIECES.
+
+IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY
+COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THIS
+WEBSITE AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR
+INABILITY TO USE THIS WEBSITE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR
+LOSS OF HAIR, LOSS OF LIFE, LOSS OF MEMORY, LOSS OF YOUR CARKEYS, MISPLACEMENT
+OF YOUR PAYCHECK, OR COMMANDER DATA BEING RENDERED UNABLE TO ASSIST THE
+STARFLEET OFFICERS ABORD THE STARSHIP ENTERPRISE TO RECALIBRATE THE MAIN
+DEFLECTOR ARRAY, LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE
+WEBSITE TO OPERATE WITH YOUR WEBBROWSER), EVEN IF SUCH HOLDER OR OTHER PARTY
+HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+You have been warned.
+
+You can contact the webmaster at <rob@landley.net> if you have some sort
+of problem with this.
+
diff --git a/i/pc104/initrd/conf/busybox/docs/busybox.net/developer.html b/i/pc104/initrd/conf/busybox/docs/busybox.net/developer.html
new file mode 100644
index 0000000..cdb68b7
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/docs/busybox.net/developer.html
@@ -0,0 +1,69 @@
+<!--#include file="header.html" -->
+
+<h3>Morris Dancing</h3>
+
+<p>Subversion commit access requires an account on Morris. The server
+behind busybox.net and uclibc.org. If you want to be able to commit things to
+Subversion, first contribute some stuff to show you are serious, can handle
+some responsibility, and that your patches don't generally need a lot of
+cleanup. Then, very nicely ask one of us (<a href="mailto:rob@landley.net">Rob
+Landley</a> for BusyBox, or <a href="mailto:andersen@codepoet.org">Erik
+Andersen</a> for uClibc) for an account.</p>
+
+<p>If you're approved for an account, you'll need to send an email from your
+preferred contact email address with the username you'd like to use when
+committing changes to SVN, and attach a public ssh key to access your account
+with.</p>
+
+<p>If you don't currently have an ssh version 2 DSA key at least 1024 bits
+long (the default), you can generate a key using the
+command <b>ssh-keygen -t dsa</b> and hitting enter at the prompts. This
+will create the files <b>~/.ssh/id_dsa</b> and <b>~/.ssh/id_dsa.pub</b>
+You must then send the content of 'id_dsa.pub' to me so I can set up your
+account. (The content of 'id_dsa' should of course be kept secret, anyone
+who has that can access any account that's installed your public key in
+its <b>.ssh/authorized_keys</b> file.)</p>
+
+<p>Note that if you would prefer to keep your communications with us
+private, you can encrypt your email using
+<a href="http://landley.net/pubkey.gpg">Rob's public key</a> or
+<a href="http://www.codepoet.org/andersen/erik/gpg.asc">Erik's public
+key</a>.</p>
+
+<p>Once you are setup with an account, you will need to use your account to
+checkout a copy of BusyBox from Subversion:</p>
+
+<p><b>svn checkout svn+ssh://username@busybox.net/svn/trunk/busybox</b></p>
+<p>or</p>
+<p><b>svn checkout svn+ssh://username@uclibc.org/svn/trunk/uclibc</b></p>
+
+<p>You must change <em>username</em> to your own username, or omit
+it if it's the same as your local username.</p>
+
+<p>You can then enter the newly checked out project directory, make changes,
+check your changes, diff your changes, revert your changes, and and commit your
+changes using commands such as:</p>
+
+<b><pre>
+svn diff
+svn status
+svn revert
+EDITOR=vi svn commit
+svn log -v -r PREV:HEAD
+svn help
+</pre></b>
+
+<p>For additional detail on how to use Subversion, please visit the
+<a href="http://subversion.tigris.org/">the Subversion website</a>.
+You might also want to read online or buy a copy of <a
+href="http://svnbook.red-bean.com/">the Subversion Book</a>...</p>
+
+<p>A morris account also gives you a personal web page
+(http://busybox.net/~username comes from ~/public_html on morris), and of
+course a shell prompt you can ssh into (as a regular user, root access is
+reserved for Erik and Rob). But keep in mind an account on Morris is a
+priviledge, not a requirement. Most contributors to busybox and uClibc
+haven't got one, and accounts are handed out to make the project maintainers'
+lives easier, not because "you deserve it".</p>
+
+<!--#include file="footer.html" -->
diff --git a/i/pc104/initrd/conf/busybox/docs/busybox.net/download.html b/i/pc104/initrd/conf/busybox/docs/busybox.net/download.html
new file mode 100644
index 0000000..f8746dd
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/docs/busybox.net/download.html
@@ -0,0 +1,29 @@
+<!--#include file="header.html" -->
+
+
+
+<h3>Download</h3>
+
+Source for the latest release can always be
+downloaded from <a href="downloads">http://www.busybox.net/downloads</a>.
+
+<p>
+You can also obtain <a href= "downloads/snapshots/">Daily Snapshots</a> of
+the latest development source tree for those wishing to follow BusyBox development,
+but cannot or do not wish to use Subversion (svn).
+
+<ul>
+ <li> Click here to <a href="/cgi-bin/viewcvs.cgi/trunk/busybox/">browse the source tree</a>.
+ </li>
+
+ <li>Anonymous <a href="subversion.html">Subversion access</a> is available.
+ </li>
+
+ <li>For those that are actively contributing obtaining
+ <a href="developer.html">Subversion read/write access</a> is also possible.
+ </li>
+
+</ul>
+
+<!--#include file="footer.html" -->
+
diff --git a/i/pc104/initrd/conf/busybox/docs/busybox.net/footer.html b/i/pc104/initrd/conf/busybox/docs/busybox.net/footer.html
new file mode 100644
index 0000000..5f2335a
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/docs/busybox.net/footer.html
@@ -0,0 +1,47 @@
+<!-- Footer -->
+
+
+ </td>
+ </tr>
+ </table>
+
+<hr />
+
+
+ <table width="100%">
+ <tr>
+ <td width="60%">
+ <font face="arial, helvetica, sans-serif" size="-1">
+ <a href="/copyright.txt">Copyright &copy; 1999-2005 Erik Andersen</a>
+ <br>
+ Mail all comments, insults, suggestions and bribes to
+ <br>
+ Denis Vlasenko <a href="mailto:vda.linux@googlemail.com">vda.linux@googlemail.com</a><br>
+ </font>
+ </td>
+
+ <td>
+ <a href="http://www.vim.org/"><img border=0 width=88 height=31
+ src="images/written.in.vi.png"
+ alt="This site created with the vi editor"></a>
+ </td>
+
+ <td>
+ <a href="http://osuosl.org/"><img border=0 width=114 height=63
+ src="images/osuosl.png"
+ alt="This site is kindly hosted by OSL"></a>
+ </td>
+<!--
+ <td>
+ <a href="http://validator.w3.org/check?uri=referer"><img
+ border="0" height="31" width="88"
+ src="images/valid-html401.png"
+ alt="Valid HTML"></a>
+ </td>
+-->
+ </TR>
+ </table>
+
+ </body>
+</html>
+
diff --git a/i/pc104/initrd/conf/busybox/docs/busybox.net/header.html b/i/pc104/initrd/conf/busybox/docs/busybox.net/header.html
new file mode 100644
index 0000000..081f18c
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/docs/busybox.net/header.html
@@ -0,0 +1,96 @@
+<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>
+
+<html>
+ <head>
+ <meta http-equiv='Content-Type' content='text/html; charset=iso-8859-1'>
+ <title>BusyBox</title>
+ <style type="text/css">
+ body {
+ background-color: #DEE2DE;
+ color: #000000;
+ }
+ :link { color: #660000 }
+ :visited { color: #660000 }
+ :active { color: #660000 }
+ td.c2 {font-family: arial, helvetica, sans-serif; font-size: 80%}
+ td.c1 {font-family: lucida, helvetica; font-size: 248%}
+ </style>
+ </head>
+
+ <body>
+ <basefont face="lucida, helvetica, arial" size="3">
+
+
+
+
+<table border="0" cellpadding="0" cellspacing="0">
+
+
+<tr>
+<td>
+ <div class="c3">
+ <table border="0" cellspacing="1" cellpadding="2">
+ <tr>
+ <td class="c1">BUSYBOX</td>
+ </tr>
+ </table>
+ </div>
+
+ <a href="/"><IMG SRC="images/busybox1.png" alt="BusyBox" border="0"></a><BR>
+</td>
+</tr>
+
+<tr>
+
+<td valign="TOP">
+ <b>About</b>
+ <ul>
+ <li><a href="about.html">About BusyBox</a></li>
+ <li><a href="screenshot.html">Screenshot</a></li>
+ <li><a href="news.html">Announcements</a></li>
+ <li><a href="downloads/news">BusyBox Weekly News</a></li>
+ </ul>
+ <b>Documentation</b>
+ <ul>
+ <li><a href="FAQ.html">FAQ</a></li>
+ <li><a href="downloads/BusyBox.html">Command Help</a></li>
+ <li><a href="downloads/README">README</a></li>
+ </ul>
+ <b>Get BusyBox</b>
+ <ul>
+ <li><a href="download.html">Download Source</a></li>
+ <li><a href="license.html">License</a></li>
+ <li><a href="products.html">Products</a></li>
+ </ul>
+ <b>Development</b>
+ <ul>
+ <li><a href="/cgi-bin/viewcvs.cgi/trunk/busybox/">Browse Source</a></li>
+ <li><a href="subversion.html">Source Control</a></li>
+ <li><a href="/downloads/patches/recent.html">Recent Changes</a></li>
+ <li><a href="lists.html">Mailing Lists</a></li>
+ <li><a href="http://bugs.busybox.net/">Bug Tracking</a></li>
+ </ul>
+ <p><b>Links</b>
+ <ul>
+ <li><a href="links.html">Related Sites</a></li>
+ <li><a href="tinyutils.html">Tiny Utilities</a></li>
+ <li><a href="sponsors.html">Sponsors</a></li>
+ </ul>
+ <p><b>Developer Pages</b>
+ <ul>
+ <li><a href="http://busybox.net/~landley">Rob</a></li>
+ <li><a href="http://busybox.net/~aldot">Bernhard</a></li>
+ <li><a href="http://busybox.net/~vda">Denis</a></li>
+ </ul>
+
+<!--
+ <a href="http://validator.w3.org/check/referer"><img
+ src="/images/vh40.gif" height=31 width=88
+ align=left border=0 alt="Valid HTML 4.0!"></a>
+-->
+
+</td>
+
+
+<td Valign="TOP">
+
diff --git a/i/pc104/initrd/conf/busybox/docs/busybox.net/images/back.png b/i/pc104/initrd/conf/busybox/docs/busybox.net/images/back.png
new file mode 100644
index 0000000..7992386
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/docs/busybox.net/images/back.png
Binary files differ
diff --git a/i/pc104/initrd/conf/busybox/docs/busybox.net/images/busybox.jpeg b/i/pc104/initrd/conf/busybox/docs/busybox.net/images/busybox.jpeg
new file mode 100644
index 0000000..37edc96
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/docs/busybox.net/images/busybox.jpeg
Binary files differ
diff --git a/i/pc104/initrd/conf/busybox/docs/busybox.net/images/busybox.png b/i/pc104/initrd/conf/busybox/docs/busybox.net/images/busybox.png
new file mode 100644
index 0000000..b1eb92f
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/docs/busybox.net/images/busybox.png
Binary files differ
diff --git a/i/pc104/initrd/conf/busybox/docs/busybox.net/images/busybox1.png b/i/pc104/initrd/conf/busybox/docs/busybox.net/images/busybox1.png
new file mode 100644
index 0000000..4d3126a
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/docs/busybox.net/images/busybox1.png
Binary files differ
diff --git a/i/pc104/initrd/conf/busybox/docs/busybox.net/images/busybox2.jpg b/i/pc104/initrd/conf/busybox/docs/busybox.net/images/busybox2.jpg
new file mode 100644
index 0000000..abf8f06
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/docs/busybox.net/images/busybox2.jpg
Binary files differ
diff --git a/i/pc104/initrd/conf/busybox/docs/busybox.net/images/busybox3.jpg b/i/pc104/initrd/conf/busybox/docs/busybox.net/images/busybox3.jpg
new file mode 100644
index 0000000..0fab84c
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/docs/busybox.net/images/busybox3.jpg
Binary files differ
diff --git a/i/pc104/initrd/conf/busybox/docs/busybox.net/images/dir.png b/i/pc104/initrd/conf/busybox/docs/busybox.net/images/dir.png
new file mode 100644
index 0000000..1d633ce
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/docs/busybox.net/images/dir.png
Binary files differ
diff --git a/i/pc104/initrd/conf/busybox/docs/busybox.net/images/donate.png b/i/pc104/initrd/conf/busybox/docs/busybox.net/images/donate.png
new file mode 100644
index 0000000..b55621b
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/docs/busybox.net/images/donate.png
Binary files differ
diff --git a/i/pc104/initrd/conf/busybox/docs/busybox.net/images/fm.mini.png b/i/pc104/initrd/conf/busybox/docs/busybox.net/images/fm.mini.png
new file mode 100644
index 0000000..c0883cd
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/docs/busybox.net/images/fm.mini.png
Binary files differ
diff --git a/i/pc104/initrd/conf/busybox/docs/busybox.net/images/gfx_by_gimp.png b/i/pc104/initrd/conf/busybox/docs/busybox.net/images/gfx_by_gimp.png
new file mode 100644
index 0000000..d583140
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/docs/busybox.net/images/gfx_by_gimp.png
Binary files differ
diff --git a/i/pc104/initrd/conf/busybox/docs/busybox.net/images/ltbutton2.png b/i/pc104/initrd/conf/busybox/docs/busybox.net/images/ltbutton2.png
new file mode 100644
index 0000000..9bad949
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/docs/busybox.net/images/ltbutton2.png
Binary files differ
diff --git a/i/pc104/initrd/conf/busybox/docs/busybox.net/images/osuosl.png b/i/pc104/initrd/conf/busybox/docs/busybox.net/images/osuosl.png
new file mode 100644
index 0000000..b00b500
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/docs/busybox.net/images/osuosl.png
Binary files differ
diff --git a/i/pc104/initrd/conf/busybox/docs/busybox.net/images/sdsmall.png b/i/pc104/initrd/conf/busybox/docs/busybox.net/images/sdsmall.png
new file mode 100644
index 0000000..b102450
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/docs/busybox.net/images/sdsmall.png
Binary files differ
diff --git a/i/pc104/initrd/conf/busybox/docs/busybox.net/images/text.png b/i/pc104/initrd/conf/busybox/docs/busybox.net/images/text.png
new file mode 100644
index 0000000..6034f89
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/docs/busybox.net/images/text.png
Binary files differ
diff --git a/i/pc104/initrd/conf/busybox/docs/busybox.net/images/valid-html401.png b/i/pc104/initrd/conf/busybox/docs/busybox.net/images/valid-html401.png
new file mode 100644
index 0000000..ec9bc0c
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/docs/busybox.net/images/valid-html401.png
Binary files differ
diff --git a/i/pc104/initrd/conf/busybox/docs/busybox.net/images/vh40.gif b/i/pc104/initrd/conf/busybox/docs/busybox.net/images/vh40.gif
new file mode 100644
index 0000000..c5e9402
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/docs/busybox.net/images/vh40.gif
Binary files differ
diff --git a/i/pc104/initrd/conf/busybox/docs/busybox.net/images/written.in.vi.png b/i/pc104/initrd/conf/busybox/docs/busybox.net/images/written.in.vi.png
new file mode 100644
index 0000000..84f59bc
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/docs/busybox.net/images/written.in.vi.png
Binary files differ
diff --git a/i/pc104/initrd/conf/busybox/docs/busybox.net/index.html b/i/pc104/initrd/conf/busybox/docs/busybox.net/index.html
new file mode 100644
index 0000000..1bab6b0
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/docs/busybox.net/index.html
@@ -0,0 +1 @@
+<!--#include file="news.html" -->
diff --git a/i/pc104/initrd/conf/busybox/docs/busybox.net/license.html b/i/pc104/initrd/conf/busybox/docs/busybox.net/license.html
new file mode 100644
index 0000000..76358bc
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/docs/busybox.net/license.html
@@ -0,0 +1,97 @@
+<!--#include file="header.html" -->
+
+<p>
+<h3>BusyBox is licensed under the GNU General Public License, version 2</h3>
+
+<p>BusyBox is licensed under <a href="http://www.gnu.org/licenses/gpl.html#SEC1">the
+GNU General Public License</a> version 2, which is often abbreviated as GPLv2.
+(This is the same license the Linux kernel is under, so you may be somewhat
+familiar with it by now.)</p>
+
+<p>A complete copy of the license text is included in the file LICENSE in
+the BusyBox source code.</p>
+
+<p><a href="/products.html">Anyone thinking of shipping BusyBox as part of a
+product</a> should be familiar with the licensing terms under which they are
+allowed to use and distribute BusyBox. Read the full test of the GPL (either
+through the above link, or in the file LICENSE in the busybox tarball), and
+also read the <a href="http://www.gnu.org/licenses/gpl-faq.html">Frequently
+Asked Questions about the GPL</a>.</p>
+
+<p>Basically, if you distribute GPL software the license requires that you also
+distribute the source code to that GPL-licensed software. So if you distribute
+BusyBox without making the source code to the version you distribute available,
+you violate the license terms, and thus infringe on the copyrights of BusyBox.
+(This requirement applies whether or not you modified BusyBox; either way the
+license terms still apply to you.) Read the license text for the details.</p>
+
+<h3>A note on GPL versions</h3>
+
+<p>Version 2 of the GPL is the only version of the GPL which current versions
+of BusyBox may be distributed under. New code added to the tree is licensed
+GPL version 2, and the project's license is GPL version 2.</p>
+
+<p>Older versions of BusyBox (versions 1.2.2 and earlier, up through about svn
+16112) included variants of the recommended "GPL version 2 or (at your option)
+later versions" boilerplate permission grant. Ancient versions of BusyBox
+(before svn 49) did not specify any version at all, and section 9 of GPLv2
+(the most recent version at the time) says those old versions may be
+redistributed under any version of GPL (including the obsolete V1). This was
+conceptually similar to a dual license, except that the different licenses were
+different versions of the GPL.</p>
+
+<p>However, BusyBox has apparently always contained chunks of code that were
+licensed under GPL version 2 only. Examples include applets written by Linus
+Torvalds (util-linux/mkfs_minix.c and util_linux/mkswap.c) which stated they
+"may be redistributed as per the Linux copyright" (which Linus clarified in the
+2.4.0-pre8 release announcement in 2000 was GPLv2 only), and Linux kernel code
+copied into libbb/loop.c (after Linus's announcement). There are probably
+more, because all we used to check was that the code was GPL, not which
+version. (Before the GPLv3 draft proceedings in 2006, it was a purely
+theoretical issue that didn't come up much.)</p>
+
+<p>To summarize: every version of BusyBox may be distributed under the terms of
+GPL version 2. New versions (after 1.2.2) may <b>only</b> be distributed under
+GPLv2, not under other versions of the GPL. Older versions of BusyBox might
+(or might not) be distributable under other versions of the GPL. If you
+want to use a GPL version other than 2, you should start with one of the old
+versions such as release 1.2.2 or SVN 16112, and do your own homework to
+identify and remove any code that can't be licensed under the GPL version you
+want to use. New development is all GPLv2.</p>
+
+<h3>License enforcement</h3>
+
+<p>BusyBox's copyrights are enforced by the <a
+href="http://www.softwarefreedom.org">Software Freedom Law Center</a>
+(you can contact them at gpl@busybox.net), which
+"accepts primary responsibility for enforcement of US copyrights on the
+software... and coordinates international copyright enforcement efforts for
+such works as necessary." If you distribute BusyBox in a way that doesn't
+comply with the terms of the license BusyBox is distributed under, expect to
+hear from these guys. Their entire reason for existing is to do pro-bono
+legal work for free/open source software projects. (We used to list people who
+violate the BusyBox license in <a href="/shame.html">The Hall of Shame</a>,
+but these days we find it much more effective to hand them over to the
+lawyers.)</p>
+
+<p>Our enforcement efforts are aimed at bringing people into compliance with
+the BusyBox license. Open source software is under a different license from
+proprietary software, but if you violate that license you're still a software
+pirate and the law gives the vendor (us) some big sticks to play with. We
+don't want monetary awards, injunctions, or to generate bad PR for a company,
+unless that's the only way to get somebody that repeatedly ignores us to comply
+with the license on our code.</p>
+
+<h3>A Good Example</h3>
+
+<p>These days, <a href="http://www.linksys.com/">Linksys</a> is
+doing a good job at complying with the GPL, they get to be an
+example of how to do things right. Please take a moment and
+check out what they do with
+<a href="http://www.linksys.com/servlet/Satellite?c=L_Content_C1&childpagename=US%2FLayout&cid=1115416836002&pagename=Linksys%2FCommon%2FVisitorWrapper">
+distributing the firmware for their WRT54G Router.</a>
+Following their example would be a fine way to ensure that you
+have also fulfilled your licensing obligations.</p>
+
+<!--#include file="footer.html" -->
+
diff --git a/i/pc104/initrd/conf/busybox/docs/busybox.net/links.html b/i/pc104/initrd/conf/busybox/docs/busybox.net/links.html
new file mode 100644
index 0000000..9cdbd7c
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/docs/busybox.net/links.html
@@ -0,0 +1,19 @@
+<!--#include file="header.html" -->
+
+<h3>Related Sites</h3>
+
+<br><a href="http://uclibc.org/">uClibc.org</a>
+<br><a href="http://cxx.uclibc.org/">uClibc++</a>
+<br><a href="http://udhcp.busybox.net/">udhcp</a>
+<br><a href="http://buildroot.uclibc.org/">buildroot</a>
+<br><a href="http://www.scratchbox.org/">Scratchbox</a>
+<br><a href="http://openembedded.org/">OpenEmbedded</a>
+<br><a href="http://www.ucdot.org/">uCdot</a>
+<br><a href="http://www.linuxdevices.com">LinuxDevices</a>
+<br><a href="http://slashdot.org/">Slashdot</a>
+<br><a href="http://freshmeat.net/">Freshmeat</a>
+<br><a href="http://linuxtoday.com/">Linux Today</a>
+<br><a href="http://lwn.net/">Linux Weekly News</a>
+<br><a href="http://www.tldp.org/HOWTO">Linux HOWTOs</a>
+
+<!--#include file="footer.html" -->
diff --git a/i/pc104/initrd/conf/busybox/docs/busybox.net/lists.html b/i/pc104/initrd/conf/busybox/docs/busybox.net/lists.html
new file mode 100644
index 0000000..3a28cc0
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/docs/busybox.net/lists.html
@@ -0,0 +1,46 @@
+<!--#include file="header.html" -->
+
+
+<!-- Begin Introduction section -->
+
+<h3>Mailing List Information</h3>
+BusyBox has a <a href="/lists/busybox/">mailing list</a> for discussion and
+development. You can subscribe by visiting
+<a href="http://busybox.net/mailman/listinfo/busybox">this page</a>.
+Only subscribers to the BusyBox mailing list are allowed to post
+to this list.
+
+<p>
+There is also a mailing list for <a href="/lists/busybox-cvs/">active developers</a>
+wishing to read the complete diff of each and every change to busybox -- not for the
+faint of heart. Active developers can subscribe by visiting
+<a href="http://busybox.net/mailman/listinfo/busybox-cvs">this page</a>.
+The Subversion server is the only one permtted to post to this list. And yes,
+this list name uses the word 'cvs' even though we don't use that anymore...
+
+<p>
+
+
+<h3>Search the List Archives</h3>
+Please search the mailing list archives before asking questions on the mailing
+list, since there is a good chance someone else has asked the same question
+before. Checking the archives is a great way to avoid annoying everyone on the
+list with frequently asked questions...
+<p>
+
+<center>
+<form method="GET" action="http://www.google.com/custom">
+<input type="hidden" name="domains" value="busybox.net">
+<input type="hidden" name="sitesearch" value="busybox.net">
+<input type="text" name="q" size="31" maxlength="255" value="">
+<br>
+<input type="submit" name="sa" value="search the mailing list archives">
+<br>
+<a href="http://www.google.com"><img src="http://www.google.com/logos/Logo_25wht.gif" border="0" alt="Google" height="32" width="75" align="middle"></a>
+<br>
+</form>
+</center>
+
+
+
+<!--#include file="footer.html" -->
diff --git a/i/pc104/initrd/conf/busybox/docs/busybox.net/news.html b/i/pc104/initrd/conf/busybox/docs/busybox.net/news.html
new file mode 100644
index 0000000..156b4f7
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/docs/busybox.net/news.html
@@ -0,0 +1,252 @@
+<!--#include file="header.html" -->
+
+
+<ul>
+ <li><b>18 March, 2007 -- BusyBox 1.4.2 (stable)</b>
+ <p><a href=http://busybox.net/downloads/busybox-1.4.2.tar.bz2>BusyBox 1.4.2</a>.
+ </p>
+
+ <p>This release includes only trivial fixes accumulated since 1.4.1.
+ </p>
+ </li>
+
+ <li><b>25 January, 2007 -- BusyBox 1.4.1 (stable)</b>
+ <p><a href=http://busybox.net/downloads/busybox-1.4.1.tar.bz2>BusyBox 1.4.1</a>.
+ (<a href=http://busybox.net/downloads/fixes-1.4.1/>patches</a>)</p>
+
+ <p>This release includes only trivial fixes accumulated since 1.4.0.
+ </p>
+ </li>
+
+ <li><b>20 January, 2007 -- BusyBox 1.4.0 (stable)</b>
+ <p><a href=http://busybox.net/downloads/busybox-1.4.0.tar.bz2>BusyBox 1.4.0</a>.
+ (<a href=http://busybox.net/downloads/fixes-1.4.0/>patches</a>)</p>
+
+ <p>Since this is a x.x.0 release, it probably is a bit less "stable"
+ than usual.</p>
+ <p>Changes since previous release:
+ <ul>
+ <li>e2fsprogs are mostly removed from busybox. Some smaller parts remain,
+ the rest of it sits disabled in e2fsprogs/old_e2fsprogs/*, because
+ it's too bloated. Really. I'm afraid it's about the only way we can
+ ever get e2fsprogs cleaned up.
+ <li>less: many improvements. Now can display binary files
+ (although I expect it to have trouble with displays where 8bit chars
+ don't have 1-to-1 char/glyph relationship). Regexp search is not buggy
+ anymore. Less does not read entire input up-front. Reads input
+ as it appears (yay!). Works rather nice as man pager. I recommend it
+ for general use now.
+ <li>IPv6: generic support is in place, many networking applets are
+ upgraded to be IPv6 capable. Probably some work remains, but it is
+ already much better than what we had previously.
+ <li>arp: new applet (thanks to Eric Spakman).
+ <li>fakeidentd: non-forking standalone server part was taking ~90%
+ of the applet. Factored it out (in fact, rewrote it).
+ <li>syslogd: mostly rewritten.
+ <li>decompress_unzip, gzip: sanitized a bit.
+ <li>sed: better hadling of NULs
+ <li>httpd: stop adding our own "Content-type:" to CGI output
+ <li>chown: user.grp works again.
+ <li>minor bugfixes to: passwd, date, tftp, start_stop_daemon, tar,
+ ps, ifupdown, time, su, stty, awk, ping[6], sort,...
+ </ul>
+ </p>
+ </li>
+
+ <li><b>20 January, 2007 -- BusyBox 1.3.2 (stable)</b>
+ <p><a href=http://busybox.net/downloads/busybox-1.3.2.tar.bz2>BusyBox 1.3.2</a>.</p>
+
+ <p>This release includes only one trivial fix accumulated since 1.3.1
+ </p>
+ </li>
+
+ <li><b>27 December, 2006 -- BusyBox 1.3.1 (stable)</b>
+ <p><a href=http://busybox.net/downloads/busybox-1.3.1.tar.bz2>BusyBox 1.3.1</a>.
+ (<a href=http://busybox.net/downloads/fixes-1.3.1/>patches</a>)</p>
+
+ <p>Closing 2006 with new release. It includes only trivial fixes accumulated since 1.3.0
+ </p>
+ </li>
+
+ <li><b>14 December, 2006 -- BusyBox 1.3.0 (stable)</b>
+ <p><a href=http://busybox.net/downloads/busybox-1.3.0.tar.bz2>BusyBox 1.3.0</a>.
+ (<a href=http://busybox.net/downloads/fixes-1.3.0/>patches</a>)</p>
+
+ <p>This release has CONFIG_DESKTOP option which enables features
+ needed for busybox usage on desktop machine. For example, find, chmod
+ and chown get several less frequently used options, od is significantly
+ bigger but matches GNU coreutils, etc. Intended to eventually make
+ busybox a viable alternative for "standard" utilities for slightly
+ adventurous desktop users.
+ <p>Changes since previous release:
+ <ul>
+ <li>find: taking many more of standard options
+ <li>ps: POSIX-compliant -o implemented
+ <li>cp: added -s, -l
+ <li>grep: added -r, fixed -h
+ <li>watch: make it exec child like standard one does (was totally
+ incompatible)
+ <li>tar: fix limitations which were preventing bbox tar usage
+ on big directories: long names and linknames, pax headers
+ (Linux kernel tarballs have that). Fixed a number of obscure bugs.
+ Raised max file limit (now 64Gb). Security fixes (/../ attacks).
+ <li>httpd: added -i (inetd), -f (foreground), support for
+ directory indexer CGI (example is included), bugfixes.
+ <li>telnetd: fixed/improved IPv6 support, inetd+standalone support,
+ other fixes. Useful IPv6 stuff factored out into libbb.
+ <li>runit/*: new applets adapted from http://smarden.sunsite.dk/runit/
+ (these are my personal favorite small-and-beautiful toys)
+ <li>minor bugfixes to: login, dd, mount, umount, chmod, chown, ln, udhcp,
+ fdisk, ifconfig, sort, tee, mkswap, wget, insmod.
+ </ul>
+ <p>Note that GnuPG key used to sign this release is different.
+ 1.2.2.1 is also signed post-factum now. Sorry for the mess.
+ </p>
+ </li>
+
+ <li><b>29 October, 2006 -- BusyBox 1.2.2.1 (fix)</b>
+ <p><a href=http://busybox.net/downloads/busybox-1.2.2.1.tar.bz2>BusyBox 1.2.2.1</a>.</p>
+
+ <p>Added compile-time warning that static linking against glibc
+ produces buggy executables.
+ </li>
+
+ <li><b>24 October, 2006 -- BusyBox 1.2.2 (stable)</b>
+ <p>It's a bit overdue, but
+ <a href=http://busybox.net/downloads/busybox-1.2.2.tar.bz2>here is
+ BusyBox 1.2.2</a>.</p>
+
+ <p>This release has dozens of fixes backported from the ongoing development
+ branch. There are a couple of bugfixes to sed, two fixes to documentation
+ generation (BusyBox.html shouldn't have USE() macros in it anymore), fix
+ umount to report the right errno on failure and to umount block devices by
+ name with newer kernels, fix mount to handle symlinks properly, make mdev
+ delete device nodes when called for hotplug remove, fix a segfault
+ in traceroute, a minor portability fix to md5sum option parsing, a build
+ fix for httpd with old gccs, an options parsing tweak to hdparm, make test
+ fail gracefully when getgroups() returns -1, fix a race condition in
+ modprobe when two instances run at once (hotplug does this), make "tar xf
+ foo.tar dir/dir" extract all subdirectories, make our getty initialize the
+ terminal more like mingetty, an selinux build fix, an endianness fix in
+ ping6, fix for zcip defending addresses, clean up some global variables in
+ gzip to save memory, fix sulogin -tNNN, a help text tweak, several warning
+ fixes and build fixes, fixup dnsd a bit, and a partridge in a pear tree.</p>
+
+ <p>As <a href=http://lwn.net/Articles/202106/>Linux Weekly News noted</a>,
+ this is my (Rob's) last release of BusyBox. The new maintainer is Denis
+ Vlasenko, I'm off to do <a href=http://landley.net/code>other things</a>.
+ </p>
+ </li>
+
+ <li><b>29 September, 2006 -- New license email address.</b>
+ <p>The email address gpl@busybox.net is now the recommended way to contact
+ the Software Freedom Law Center to report BusyBox license violations.</p>
+
+ <li><b>31 July 2006 -- BusyBox 1.2.1 (stable)</b>
+ <p>Since nobody seems to have objected too loudly over the weekend, I
+ might as well point you all at
+ <a href="http://busybox.net/downloads/busybox-1.2.1.tar.bz2">Busybox
+ 1.2.1</a>, a bugfix-only release with no new features.</p>
+
+ <p>It has three shell fixes (two to lash: going "var=value" without
+ saying "export" should now work, plus a missing null pointer check, and
+ one to ash when redirecting output to a file that fills up.) Fix three
+ embarassing thinkos in the new dmesg command. Two build tweaks
+ (dependencies for the compressed usage messages and running make in the
+ libbb subdirectory). One fix to tar so it can extract git-generated
+ tarballs (rather than barfing on the pax extensions). And a partridge
+ in a pear... Ahem.</p>
+
+ <p>But wait, there's more! A passwd changing fix so an empty
+ gecos field doesn't trigger a false objection that the new passwd contains
+ the gecos field. Make all our setuid() and setgid() calls check the return
+ value in case somebody's using per-process resource limits that prevent
+ a user from having too many processes (and thus prevent a process from
+ switching away from root, in which case the process will now _die_ rather
+ than continue with root privileges). A fix to adduser to make sure that
+ /etc/group gets updated. And a fix to modprobe to look for modules.conf
+ in the right place on 2.6 kernels.</p>
+
+ <li><b>30 June 2006 -- BusyBox 1.2.0</b>
+ <p>The -devel branch has been stabilized and the result is
+ <a href="http://busybox.net/downloads/busybox-1.2.0.tar.bz2">Busybox
+ 1.2.0</a>. Lots of stuff changed, I need to work up a decent changelog
+ over the weekend.</p>
+
+ <p>I'm still experimenting with how long is best for the development
+ cycle, and since we've got some largeish projects queued up I'm going to
+ try a longer one. Expect 1.3.0 in December. (Expect 1.2.1 any time
+ we fix enough bugs. :)</p>
+
+ <p>Update: Here are <a href="http://busybox.net/downloads/busybox-1.2.0.fixes.patch">the first few bug fixes</a> that will go into 1.2.1.</p>
+
+ <li><b>17 May 2006 -- BusyBox 1.1.3 (stable)</b>
+ <p><a href="http://busybox.net/downloads/busybox-1.1.3.tar.bz2">BusyBox
+ 1.1.3</a> is another bugfix release. It makes passwd use salt, fixes a
+ memory freeing bug in ls, fixes "build all sources at once" mode, makes
+ mount -a not abort on the first failure, fixes msh so ctrl-c doesn't kill
+ background processes, makes patch work with patch hunks that don't have a
+ timestamp, make less's text search a lot more robust (the old one could
+ segfault), and fixes readlink -f when built against uClibc.</p>
+
+ <p>Expect 1.2.0 sometime next month, which won't be a bugfix release.</p>
+
+ <li><b>10 April 2006 -- BusyBox 1.1.2 (stable)</b>
+ <p>You can now download <a href="http://busybox.net/downloads/busybox-1.1.2.tar.bz2">BusyBox 1.1.2</a>, a bug fix release consisting of 11 patches
+ backported from the development branch: Some build fixes, several fixes
+ for mount and nfsmount, a fix for insmod on big endian systems, a fix for
+ find -xdev, and a fix for comm. Check the file "changelog" in the tarball
+ for more info.</p>
+
+ <p>The next new development release (1.2.0) is slated for June. A 1.1.3
+ will be released before then if more bug fixes crop up. (The new plan is
+ to have a 1.x.0 new development release every 3 months, with 1.x.y stable
+ bugfix only releases based on that as appropriate.)</p>
+
+ <li><b>27 March 2006 -- Software Freedom Law Center representing BusyBox and uClibc</b>
+ <p>One issue Erik Andersen wanted to resolve when handing off BusyBox
+ maintainership to Rob Landley was license enforcement. BusyBox and
+ uClibc's existing license enforcement efforts (pro-bono representation
+ by Erik's father's law firm, and the
+ <a href="http://www.busybox.net/shame.html">Hall of Shame</a>), haven't
+ scaled to match the popularity of the projects. So we put our heads
+ together and did the obvious thing: ask Pamela Jones of
+ <a href="http://www.groklaw.net">Groklaw</a> for suggestions. She
+ referred us to the fine folks at softwarefreedom.org.</p>
+
+ <p>As a result, we're pleased to announce that the
+ <a href="http://www.softwarefreedom.org">Software Freedom Law Center</a>
+ has agreed to represent BusyBox and uClibc. We join a number of other
+ free and open source software projects (such as
+ <a href="http://lwn.net/Articles/141806/">X.org</a>,
+ <a href="http://lwn.net/Articles/135413/">Wine</a>, and
+ <a href="http://plone.org/foundation/newsitems/software-freedom-law-center-support/">Plone</a>
+ in being represented by a fairly cool bunch of lawyers, which is not a
+ phrase you get to use every day.</p>
+
+ <li><b>22 March 2006 -- BusyBox 1.1.1</b>
+ <p>The new maintainer is Rob Landley, and the new release is <a href="http://busybox.net/downloads/busybox-1.1.1.tar.bz2">BusyBox 1.1.1</a>. Expect a "what's new" document in a few days. (Also, Erik and I have have another announcement pending...)</p>
+ <p>Update: Rather than put out an endless stream of 1.1.1.x releases,
+ the various small fixes have been collected together into a
+ <a href="http://busybox.net/downloads/busybox-1.1.1.fixes.patch">patch</a>,
+ and new fixes will be appended to that as needed. Expect 1.1.2 around
+ June.</p>
+ </li>
+ <li><b>11 January 2006 -- 1.1.0 is out</b>
+ <p>The new stable release is
+ <a href="http://www.busybox.net/downloads/busybox-1.1.0.tar.bz2">BusyBox
+ 1.1.0</a>. It has a number of improvements, including several new applets.
+ (It also has <a href="http://www.busybox.net/lists/busybox/2006-January/017733.html">a few rough spots</a>,
+ but we're trying out a "release early, release often" strategy to see how
+ that works. Expect 1.1.1 sometime in March.)</p>
+
+ <li><b>Old News</b><p>
+ <a href="/oldnews.html">Click here to read older news</a>
+ </p>
+ </li>
+
+
+</ul>
+
+<!--#include file="footer.html" -->
+
diff --git a/i/pc104/initrd/conf/busybox/docs/busybox.net/oldnews.html b/i/pc104/initrd/conf/busybox/docs/busybox.net/oldnews.html
new file mode 100644
index 0000000..1017b69
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/docs/busybox.net/oldnews.html
@@ -0,0 +1,1140 @@
+<!--#include file="header.html" -->
+
+
+<ul>
+ <li><b>31 October 2005 -- 1.1.0-pre1</b>
+ <p>The development branch of busybox is stable enough for wider testing, so
+ you can now
+ <a href="http://www.busybox.net/downloads/busybox-1.1.0-pre1.tar.bz2">download</a>,
+ the first prerelease of 1.1.0. This prerelease includes a lot of
+ <a href="http://www.busybox.net/downloads/BusyBox.html">new
+ functionality</a>: new applets, new features, and extensive rewrites of
+ several existing applets. This prerelease should be noticeably more
+ <a href="http://www.opengroup.org/onlinepubs/009695399/">standards
+ compliant</a> than earlier versions of busybox, although we're
+ still working out the <a href="http://bugs.busybox.net">bugs</a>.</p>
+
+ <li><b>16 August 2005 -- 1.01 is out</b>
+
+ <p>A new stable release (<a href="http://www.busybox.net/downloads/busybox-1.01.tar.bz2">BusyBox
+ 1.01</a>) is now available for download, containing over a hundred
+ <a href="http://www.busybox.net/lists/busybox/2005-August/015424.html">small
+ fixes</a> that have cropped up since the 1.00 release.</p>
+
+ <li><b>13 January 2005 -- Bug and Patch Tracking</b><p>
+
+ Bug reports sometimes get lost when posted to the mailing list. The
+ developers of BusyBox are busy people, and have only so much they can keep
+ in their brains at a time. In my case, I'm lucky if I can remember my own
+ name, much less a bug report posted last week... To prevent your bug report
+ from getting lost, if you find a bug in BusyBox, please use the
+ <a href="http://bugs.busybox.net/">shiny new Bug and Patch Tracking System</a>
+ to post all the gory details.
+
+ <p>
+
+ The same applies to patches... Regardless of whether your patch
+ is a bug fix or adds spiffy new features, please post your patch
+ to the Bug and Patch Tracking System to make certain it is
+ properly considered.
+
+
+ <p>
+ <li><b>13 October 2004 -- BusyBox 1.00 released</b><p>
+
+ When you take a careful look at nearly every embedded Linux device or
+ software distribution shipping today, you will find a copy of BusyBox.
+ With countless routers, set top boxes, wireless access points, PDAs, and
+ who knows what else, the future for Linux and BusyBox on embedded devices
+ is looking very bright.
+
+ <p>
+
+ It is therefore with great satisfaction that I declare each and every
+ device already shipping with BusyBox is now officially out of date.
+ The highly anticipated release of BusyBox 1.00 has arrived!
+
+ <p>
+
+ Over three years in development, BusyBox 1.00 represents a tremendous
+ improvement over the old 0.60.x stable series. Now featuring a Linux
+ KernelConf based configuration system (as used by the Linux kernel),
+ Linux 2.6 kernel support, many many new applets, and the development
+ work and testing of thousands of people from around the world.
+
+ <p>
+
+ If you are already using BusyBox, you are strongly encouraged to upgrade to
+ BusyBox 1.00. If you are considering developing an embedded Linux device
+ or software distribution, you may wish to investigate if using BusyBox is
+ right for your application. If you need help getting started using
+ BusyBox, if you wish to donate to help cover expenses, or if you find a bug
+ and need help reporting it, you are invited to visit the <a
+ href="FAQ.html">BusyBox FAQ</a>.
+
+ <p>
+
+ As usual you can <a href="downloads">download busybox here</a>.
+
+ <p>Have Fun!
+
+ <p>
+ <li><b>Old News</b><p>
+ <a href="/oldnews.html">Click here to read older news</a>
+
+
+ <li><b>16 August 2004 -- BusyBox 1.0.0-rc3 released</b><p>
+
+ Here goes release candidate 3...
+ <p>
+ The <a href="downloads/Changelog">changelog</a> has all the details.
+ And as usual you can <a href="downloads">download busybox here</a>.
+
+ <p>Have Fun!
+
+ <p>
+ <li><b>26 July 2004 -- BusyBox 1.0.0-rc2 released</b><p>
+
+ Here goes release candidate 2...
+ <p>
+ The <a href="downloads/Changelog">changelog</a> has all the details.
+ And as usual you can <a href="downloads">download busybox here</a>.
+
+ <p>Have Fun!
+
+ <p>
+ <li><b>20 July 2004 -- BusyBox 1.0.0-rc1 released</b><p>
+
+ Here goes release candidate 1... This fixes all (most?) of the problems
+ that have turned up since -pre10. In particular, loading and unloading of
+ kernel modules with 2.6.x kernels should be working much better.
+ <p>
+
+ I <b>really</b> want to get BusyBox 1.0.0 released soon and I see no real
+ reason why the 1.0.0 release shouldn't happen with things pretty much as
+ is. BusyBox is in good shape at the moment, and it works nicely for
+ everything that I'm doing with it. And from the reports I've been getting,
+ it works nicely for what most everyone else is doing with it as well.
+ There will eventually be a 1.0.1 anyway, so we might as well get on with
+ it. No, BusyBox is not perfect. No piece of software ever is. And while
+ there is still plenty that can be done to improve things, most of that work
+ is waiting till we can get a solid 1.0.0 release out the door....
+ <p>
+
+ Please do not bother to send in patches adding cool new features at this
+ time. Only bug-fix patches will be accepted. If you have submitted a
+ bug-fixing patch to the busybox mailing list and no one has emailed you
+ explaining why your patch was rejected, it is safe to say that your patch
+ has been lost or forgotten. That happens sometimes. Please re-submit your
+ bug-fixing patch to the BusyBox mailing list, and be sure to put "[PATCH]"
+ at the beginning of the email subject line!
+
+ <p>
+ The <a href="downloads/Changelog">changelog</a> has all the details.
+ And as usual you can <a href="downloads">download busybox here</a>.
+
+ <p>Have Fun!
+
+ <p>
+ On a less happy note, My 92 year old grandmother (my dad's mom) passed away
+ yesterday (June 19th). The funeral will be Thursday in a little town about
+ 2 hours south of my home. I've checked and there is absolutely no way I
+ could be back in time for the funeral if I attend <a
+ href="http://www.linuxsymposium.org/2004/">OLS</a> and give my presentation
+ as scheduled.
+ <p>
+ As such, it is with great reluctance and sadness that I have come
+ to the conclusion I will have to make my appologies and skip OLS
+ this year.
+ <p>
+
+
+ <p>
+ <li><b>13 April 2004 -- BusyBox 1.0.0-pre10 released</b><p>
+
+ Ok, I lied. It turns out that -pre9 will not be the final BusyBox
+ pre-release. With any luck however -pre10 will be, since I <b>really</b>
+ want to get BusyBox 1.0.0 released very soon. As usual, please do not
+ bother to send in patches adding cool new features at this time. Only
+ bug-fix patches will be accepted. It would also be <b>very</b> helpful if
+ people could continue to review the BusyBox documentation and submit
+ improvements.
+
+ <p>
+ The <a href="downloads/Changelog">changelog</a> has all the details.
+ And as usual you can <a href="downloads">download busybox here</a>.
+
+ <p>Have Fun!
+ <p>
+
+
+ <p>
+ <li><b>6 April 2004 -- BusyBox 1.0.0-pre9 released</b><p>
+
+ Here goes the final BusyBox pre-release... This is your last chance for
+ bug fixes. With luck this will be released as BusyBox 1.0.0 later this
+ week. Please do not bother to send in patches adding cool new features at
+ this time. Only bug-fix patches will be accepted. It would also be
+ <b>very</b> helpful if people could help review the BusyBox documentation
+ and submit improvements. I've spent a lot of time updating the
+ documentation to make it better match reality, but I could really use some
+ assistance in checking that the features supported by the various applets
+ match the features listed in the documentation.
+
+ <p>
+ I had hoped to get this released a month ago, but
+ <a href="http://codepoet.org/gallery/baby_peter/img_1796">
+ another release on 1 March 2004</a> has kept me busy...
+
+ <p>
+ The <a href="downloads/Changelog">changelog</a> has all the details.
+ And as usual you can <a href="downloads">download busybox here</a>.
+
+ <p>Have Fun!
+ <p>
+
+
+ <p>
+ <li><b>23 February 2004 -- BusyBox 1.0.0-pre8 released</b><p>
+
+ Here goes yet another BusyBox pre-release... Please do not bother to send
+ in patches supplying new features at this time. Only bug-fix patches will
+ be accepted. If you have a cool new feature you would like to see
+ supported, or if you have an amazing new applet you would like to submit,
+ please wait and submit such things later. We really want to get a release
+ out we can all be proud of. We are still aiming to finish off the -pre
+ series in February and move on to the final 1.0.0 release... So if you
+ spot any bugs, now would be an excellent time to send in a fix to the
+ busybox mailing list. It would also be <b>very</b> helpful if people could
+ help review the BusyBox documentation and submit improvements. It would be
+ especially helpful if people could check that the features supported by the
+ various applets match the features listed in the documentation.
+
+ <p>
+
+ The <a href="downloads/Changelog">changelog</a> has all the details.
+ And as usual you can <a href="downloads">download busybox here</a>.
+
+ <p>Have Fun!
+ <p>
+
+
+ <li><b>4 February 2004 -- BusyBox 1.0.0-pre7 released</b><p>
+
+ There was a bug in -pre6 that broke argument parsing for a
+ number of applets, since a variable was not being zeroed out
+ properly. This release is primarily intended to fix that one
+ problem. In addition, this release fixes several other
+ problems, including a rewrite by mjn3 of the code for parsing
+ the busybox.conf file used for suid handling, some shell updates
+ from vodz, and a scattering of other small fixes. We are still
+ aiming to finish off the -pre series in February and move on to
+ the final 1.0.0 release... If you see any problems, of have
+ suggestions to make, as always, please feel free to email the
+ busybox mailing list.
+
+ <p>
+
+ The <a href="downloads/Changelog">changelog</a> has all
+ the details. And as usual you can
+ <a href="downloads">download busybox here</a>.
+
+ <p>Have Fun!
+ <p>
+
+
+ <p>
+ <li><b>30 January 2004 -- BusyBox 1.0.0-pre6 released</b><p>
+
+ Here goes the next pre-release for the new BusyBox stable
+ series. This release adds a number of size optimizations,
+ updates udhcp, fixes up 2.6 modutils support, updates ash
+ and the shell command line editing, and the usual pile of
+ bug fixes both large and small. Things appear to be
+ settling down now, so with a bit of luck and some testing
+ perhaps we can finish off the -pre series in February and
+ move on to the final 1.0.0 release... If you see any
+ problems, of have suggestions to make, as always, please
+ feel free to email the busybox mailing list.
+
+ <p>
+
+ People who rely on the <a href= "downloads/snapshots/">daily BusyBox snapshots</a>
+ should be aware that snapshots of the old busybox 0.60.x
+ series are no longer available. Daily snapshots are now
+ only available for the BusyBox 1.0.0 series and now use
+ the naming scheme "busybox-&lt;date&gt;.tar.bz2". Please
+ adjust any build scripts using the old naming scheme accordingly.
+
+ <p>
+
+ The <a href="downloads/Changelog">changelog</a> has all
+ the details. And as usual you can
+ <a href="downloads">download busybox here</a>.
+
+ <p>Have Fun!
+ <p>
+
+
+ <p>
+ <li><b>23 December 2003 -- BusyBox 1.0.0-pre5 released</b><p>
+
+ Here goes the next pre-release for the new BusyBox stable
+ series. The most obvious thing in this release is a fix for
+ a terribly stupid bug in mount that prevented it from working
+ properly unless you specified the filesystem type. This
+ release also fixes a few compile problems, updates udhcp,
+ fixes a silly bug in fdisk, fixes ifup/ifdown to behave like
+ the Debian version, updates devfsd, updates the 2.6.x
+ modutils support, add a new 'rx' applet, removes the obsolete
+ 'loadacm' applet, fixes a few tar bugs, fixes a sed bug, and
+ a few other odd fixes.
+
+ <p>
+
+ If you see any problems, of have suggestions to make, as
+ always, please feel free to send an email to the busybox
+ mailing list.
+
+ <p>
+
+ The <a href="downloads/Changelog">changelog</a> has all
+ the details. And as usual you can
+ <a href="downloads">download busybox here</a>.
+
+ <p>Have Fun!
+ <p>
+
+
+
+ <li><b>10 December 2003 -- BusyBox 1.0.0-pre4 released</b><p>
+
+ Here goes the fourth pre-release for the new BusyBox stable
+ series. This release includes major rework to sed, lots of
+ rework on tar, a new tiny implementation of bunzip2, a new
+ devfsd applet, support for 2.6.x kernel modules, updates to
+ the ash shell, sha1sum and md5sum have been merged into a
+ common applet, the dpkg applets has been cleaned up, and tons
+ of random bugs have been fixed. Thanks everyone for all the
+ testing, bug reports, and patches! Once again, a big
+ thank-you goes to Glenn McGrath (bug1) for stepping in and
+ helping get patches merged!
+
+ <p>
+
+ And of course, if you are reading this, you might have noticed
+ the busybox website has been completely reworked. Hopefully
+ things are now somewhat easier to navigate... If you see any
+ problems, of have suggestions to make, as always, please feel
+ free to send an email to the busybox mailing list.
+
+ <p>
+
+ The <a href="downloads/Changelog">changelog</a> has all
+ the details. And as usual you can
+ <a href="downloads">download busybox here</a>.
+
+ <p>Have Fun!
+
+
+
+ <p>
+ <li><b>12 Sept 2003 -- BusyBox 1.0.0-pre3 released</b><p>
+
+ Here goes the third pre-release for the new BusyBox stable
+ series. The last prerelease has held up quite well under
+ testing, but a number of problems have turned up as the number
+ of people using it has increased. Thanks everyone for all
+ the testing, bug reports, and patches!
+
+ <p>
+
+ If you have submitted a patch or a bug report to the busybox
+ mailing list and no one has emailed you explaining why your
+ patch was rejected, it is safe to say that your patch has
+ somehow gotten lost or forgotten. That happens sometimes.
+ Please re-submit your patch or bug report to the BusyBox
+ mailing list!
+
+ <p>
+
+ The point of the "-preX" versions is to get a larger group of
+ people and vendors testing, so any problems that turn up can be
+ fixed prior to the final 1.0.0 release. The main feature
+ (besides additional testing) that is still still on the TODO
+ list before the final BusyBox 1.0.0 release is sorting out the
+ modutils issues. For the new 2.6.x kernels, we already have
+ patches adding insmod and rmmod support and those need to be
+ integrated. For 2.4.x kernels, for which busybox only supports
+ a limited number of architectures, we may want to invest a bit
+ more work before we cut 1.0.0. Or we may just leave 2.4.x
+ module loading alone.
+
+ <p>
+
+ I had hoped this release would be out a month ago. And of
+ course, it wasn't since Erik became busy getting a release of
+ <a href="http://www.uclibc.org/">uClibc</a>
+ out the door. Many thanks to Glenn McGrath (bug1) for
+ stepping in and helping get a bunch of patches merged! I am
+ not even going to state a date for releasing BusyBox 1.0.0
+ -pre4 (or the final 1.0.0). We're aiming for late September...
+ But if this release proves as to be exceptionally stable (or
+ exceptionally unstable!), the next release may be very soon
+ indeed.
+
+ <p>
+
+ The <a href="downloads/Changelog">changelog</a> has all
+ the details. And as usual you can
+ <a href="downloads">download busybox here</a>.
+
+ <p>Have Fun!
+
+
+ <p>
+ <li><b>30 July 2003 -- BusyBox 1.0.0-pre2 released</b><p>
+
+ Here goes another pre release for the new BusyBox stable
+ series. The last prerelease (pre1) was given quite a lot of
+ testing (thanks everyone!) which has helped turn up a number of
+ bugs, and these problems have now been fixed.
+
+ <p>
+
+ Highlights of -pre2 include updating the 'ash' shell to sync up
+ with the Debian 'dash' shell, a new 'hdparm' applet was added,
+ init again supports pivot_root, The 'reboot' 'halt' and
+ 'poweroff' applets can now be used without using busybox init.
+ an ifconfig buffer overflow was fixed, losetup now allows
+ read-write loop devices, uClinux daemon support was added, the
+ 'watchdog', 'fdisk', and 'kill' applets were rewritten, there were
+ tons of doc updates, and there were many other bugs fixed.
+ <p>
+
+ If you have submitted a patch and it is not included in this
+ release and Erik has not emailed you explaining why your patch
+ was rejected, it is safe to say that he has lost your patch.
+ That happens sometimes. Please re-submit your patch to the
+ BusyBox mailing list.
+ <p>
+
+ The point of the "-preX" versions is to get a larger group of
+ people and vendors testing, so any problems that turn up can be
+ fixed prior to the final 1.0.0 release. The main feature that
+ is still still on the TODO list before the final BusyBox 1.0.0
+ release is adding module support for the new 2.6.x kernels. If
+ necessary, a -pre3 BusyBox release will happen on August 6th.
+ Hopefully (i.e. unless some horrible catastrophic problem
+ turns up) the final BusyBox 1.0.0 release will be ready by
+ then...
+ <p>
+
+ The <a href="downloads/Changelog">changelog</a> has all
+ the details. As usual you can <a href="downloads">download busybox here</a>.
+
+ <p>Have Fun!
+ <p>
+
+ <p>
+ <li><b>15 July 2003 -- BusyBox 1.0.0-pre1 released</b><p>
+
+ The busybox development series has been under construction for
+ nearly two years now. Which is just entirely too long... So
+ it is with great pleasure that I announce the imminent release
+ of a new stable series. Due to the huge number of changes
+ since the last stable release (and the usual mindless version
+ number inflation) I am branding this new stable series verison
+ 1.0.x...
+ <p>
+
+ The point of "-preX" versions is to get a larger group of
+ people and vendors testing, so any problems that turn up can be
+ fixed prior to the magic 1.0.0 release (which should happen
+ later this month)... I plan to release BusyBox 1.0.0-pre2 next
+ Monday (July 21st), and, if necessary, -pre3 on July 28th.
+ Hopefully (i.e. unless some horrible catastrophic problem turns
+ up) the final BusyBox 1.0.0 release should be ready by the end
+ of July.
+ <p>
+
+ If you have submitted patches, and they are not in this release
+ and I have not emailed you explaining why your patch was
+ rejected, it is safe to say that I have lost your patch. That
+ happens sometimes. Please do <B>NOT</b> send all your patches,
+ support questions, etc, directly to Erik. I get hundreds of
+ emails every day (which is why I end up losing patches
+ sometimes in the flood)... The busybox mailing list is the
+ right place to send your patches, support questions, etc.
+ <p>
+
+ I would like to especially thank Vladimir Oleynik (vodz), Glenn
+ McGrath (bug1), Robert Griebl (sandman), and Manuel Novoa III
+ (mjn3) for their significant efforts and contributions that
+ have made this release possible.
+ <p>
+
+ As usual you can <a href="downloads">download busybox here</a>.
+ You don't really need to bother with the
+ <a href="downloads/Changelog">changelog</a>, as the changes
+ vs the stable version are way too extensive to easily enumerate.
+ But you can take a look if you really want too.
+
+ <p>Have Fun!
+ <p>
+
+
+
+ <p>
+ <li><b>26 October 2002 -- BusyBox 0.60.5 released</b><p>
+
+ I am very pleased to announce that the BusyBox 0.60.5 (stable)
+ is now available for download. This is a bugfix release for
+ the stable series to address all the problems that have turned
+ up since the last release. Unfortunately, the previous release
+ had a few nasty bugs (i.e. init could deadlock, gunzip -c tried
+ to delete source files, cp -a wouldn't copy symlinks, and init
+ was not always providing controlling ttys when it should have).
+ I know I said that the previous release would be the end of the
+ 0.60.x series. Well, it turns out I'm a liar. But this time I
+ mean it (just like last time ;-). This will be the last
+ release for the 0.60.x series -- all further development work
+ will be done for the development busybox tree. Expect the development
+ version to have its first real release very very soon now...
+
+ <p>
+ The <a href="downloads/Changelog.full">changelog</a> has all
+ the details. As usual you can <a href="downloads">download busybox here</a>.
+ <p>Have Fun!
+ <p>
+
+ <p>
+ <li><b>18 September 2002 -- BusyBox 0.60.4 released</b><p>
+
+ I am very pleased to announce that the BusyBox 0.60.4
+ (stable) is now available for download. This is primarily
+ a bugfix release for the stable series to address all
+ the problems that have turned up since the last
+ release. This will be the last release for the 0.60.x series.
+ I mean it this time -- all further development work will be done
+ on the development busybox tree, which is quite solid now and
+ should soon be getting its first real release.
+
+ <p>
+ The <a href="downloads/Changelog.full">changelog</a> has all
+ the details. As usual you can <a href="downloads">download busybox here</a>.
+ <p>Have Fun!
+ <p>
+
+
+ <p>
+ <li><b>27 April 2002 -- BusyBox 0.60.3 released</b><p>
+
+ I am very pleased to announce that the BusyBox 0.60.3 (stable) is
+ now available for download. This is primarily a bugfix release
+ for the stable series. A number of problems have turned up since
+ the last release, and this should address most of those problems.
+ This should be the last release for the 0.60.x series. The
+ development busybox tree has been progressing nicely, and will
+ hopefully be ready to become the next stable release.
+
+ <p>
+ The <a href="downloads/Changelog">changelog</a> has all
+ the details. As usual you can <a href="downloads">download busybox here</a>.
+ <p>Have Fun!
+ <p>
+
+
+ <p>
+ <li><b>6 March 2002 -- busybox.net now has mirrors!</b><p>
+
+ Busybox.net is now much more available, thanks to
+ the fine folks at <a href= "http://i-netinnovations.com/">http://i-netinnovations.com/</a>
+ who are providing hosting for busybox.net and
+ uclibc.org. In addition, we now have two mirrors:
+ <a href= "http://busybox.linuxmagic.com/">http://busybox.linuxmagic.com/</a>
+ in Canada and
+ <a href= "http://busybox.csservers.de/">http://busybox.csservers.de/</a>
+ in Germany. I hope this makes things much more
+ accessible for everyone!
+
+
+<li>
+<b>3 January 2002 -- Welcome to busybox.net!</b>
+
+<p>Thanks to the generosity of a number of busybox
+users, we have been able to purchase busybox.net
+(which is where you are probably reading this).
+Right now, busybox.net and uclibc.org are both
+living on my home system (at the end of my DSL
+line). I apologize for the abrupt move off of
+busybox.lineo.com. Unfortunately, I no longer have
+the access needed to keep that system updated (for
+example, you might notice the daily snapshots there
+stopped some time ago).</p>
+
+<p>Busybox.net is currently hosted on my home
+server, at the end of a DSL line. Unfortunately,
+the load on them is quite heavy. To address this,
+I'm trying to make arrangements to get busybox.net
+co-located directly at an ISP. To assist in the
+co-location effort, <a href=
+"http://www.codepoet.org/~markw">Mark Whitley</a>
+(author of busybox sed, cut, and grep) has donated
+his <a href=
+"http://www.netwinder.org/">NetWinder</a> computer
+for hosting busybox.net and uclibc.org. Once this
+system is co-located, the current speed problems
+should be completely eliminated. Hopefully, too,
+some of you will volunteer to set up some mirror
+sites, to help to distribute the load a bit.</p>
+
+<p><!--
+ <center>
+ Click here to help support busybox.net!
+ <form action="https://www.paypal.com/cgi-bin/webscr" method="post">
+ <input type="hidden" name="cmd" value="_xclick">
+ <input type="hidden" name="business" value="andersen@codepoet.org">
+ <input type="hidden" name="item_name" value="Support Busybox">
+ <input type="hidden" name="image_url" value="https://codepoet-consulting.com/images/busybox2.jpg">
+ <input type="hidden" name="no_shipping" value="1">
+ <input type="image" src="images/donate.png" border="0" name="submit" alt="Make donation using PayPal">
+ </form>
+ </center>
+ -->
+ Since some people expressed concern over BusyBox
+donations, let me assure you that no one is getting
+rich here. All BusyBox and uClibc donations will be
+spent paying for bandwidth and needed hardware
+upgrades. For example, Mark's NetWinder currently
+has just 64Meg of memory. As demonstrated when
+google spidered the site the other day, 64 Megs in
+not enough, so I'm going to be ordering 256Megs of
+ram and a larger hard drive for the box today. So
+far, donations received have been sufficient to
+cover almost all expenses. In the future, we may
+have co-location fees to worry about, but for now
+we are ok. A <b>HUGE thank-you</b> goes out to
+everyone that has contributed!<br>
+ -Erik</p>
+</li>
+
+<li>
+<b>20 November 2001 -- BusyBox 0.60.2 released</b>
+
+<p>We am very pleased to announce that the BusyBox
+0.60.2 (stable) is now released to the world. This
+one is primarily a bugfix release for the stable
+series, and it should take care of most everyone's
+needs till we can get the nice new stuff we have
+been working on in CVS ready to release (with the
+wonderful new buildsystem). The biggest change in
+this release (beyond bugfixes) is the fact that msh
+(the minix shell) has been re-worked by Vladimir N.
+Oleynik (vodz) and so it no longer crashes when
+told to do complex things with backticks.</p>
+
+<p>This release has been tested on x86, ARM, and
+powerpc using glibc 2.2.4, libc5, and uClibc, so it
+should work with just about any Linux system you
+throw it at. See the <a href=
+"downloads/Changelog">changelog</a> for <small>most
+of</small> the details. The last release was
+<em>very</em> solid for people, and this one should
+be even better.</p>
+
+<p>As usual BusyBox 0.60.2 can be downloaded from
+<a href=
+"downloads">http://www.busybox.net/downloads</a>.</p>
+
+<p>Have Fun.<br>
+ -Erik</p>
+</li>
+
+<li> <b>18 November 2001 -- Help us buy busybox.net!</b>
+
+<!-- Begin PayPal Logo -->
+<center>
+Click here to help buy busybox.net!
+<form action="https://www.paypal.com/cgi-bin/webscr" method="post">
+<input type="hidden" name="cmd" value="_xclick">
+<input type="hidden" name="business" value="andersen@codepoet.org">
+<input type="hidden" name="item_name" value="Support Busybox">
+<input type="hidden" name="image_url" value="https://busybox.net/images/busybox2.jpg">
+<input type="hidden" name="no_shipping" value="1">
+<input type="image" src="images/donate.png" border="0" name="submit" alt="Make donation using PayPal">
+</form>
+</center>
+<!-- End PayPal Logo -->
+
+I've contacted the current owner of busybox.net and he is willing
+to sell the domain name -- for $250. He also owns busybox.org but
+will not part with it... I will then need to pay the registry fee
+for a couple of years and start paying for bandwidth, so this will
+initially cost about $300. I would like to host busybox.net on my
+home machine (codepoet.org) so I have full control over the system,
+but to do that would require that I increase the level of bandwidth
+I am paying for. Did you know that so far this month, there
+have been over 1.4 Gigabytes of busybox ftp downloads? I don't
+even <em>know</em> how much CVS bandwidth it requires. For the
+time being, Lineo has continued to graciously provide this
+bandwidth, despite the fact that I no longer work for them. If I
+start running this all on my home machine, paying for the needed bandwidth
+will start costing some money.
+<p>
+
+I was going to pay it all myself, but my wife didn't like that
+idea at all (big surprise). It turns out &lt;insert argument
+where she wins and I don't&gt; she has better ideas
+about what we should spend our money on that don't involve
+busybox. She suggested I should ask for contributions on the
+mailing list and web page. So...
+<p>
+
+I am hoping that if everyone could contribute a bit, we could pick
+up the busybox.net domain name and cover the bandwidth costs. I
+know that busybox is being used by a lot of companies as well as
+individuals -- hopefully people and companies that are willing to
+contribute back a bit. So if everyone could please help out, that
+would be wonderful!
+<p>
+
+
+<li> <b>23 August 2001 -- BusyBox 0.60.1 released</b>
+<br>
+
+ This is a relatively minor bug fixing release that fixes
+ up the bugs that have shown up in the stable release in
+ the last few weeks. Fortunately, nothing <em>too</em>
+ serious has shown up. This release only fixes bugs -- no
+ new features, no new applets. So without further ado,
+ here it is. Come and get it.
+ <p>
+ The
+ <a href="downloads/Changelog">changelog</a> has all
+ the details. As usual BusyBox 0.60.1 can be downloaded from
+ <a href="downloads">http://busybox.net/downloads</a>.
+ <p>Have Fun!
+ <p>
+
+
+<li> <b>2 August 2001 -- BusyBox 0.60.0 released</b>
+<br>
+ I am very pleased to announce the immediate availability of
+ BusyBox 0.60.0. I have personally tested this release with libc5, glibc,
+ and <a href="http://uclibc.org/">uClibc</a> on
+ x86, ARM, and powerpc using linux 2.2 and 2.4, and I know a number
+ of people using it on everything from ia64 to m68k with great success.
+ Everything seems to be working very nicely now, so getting a nice
+ stable bug-free(tm) release out seems to be in order. This releases fixes
+ a memory leak in syslogd, a number of bugs in the ash and msh shells, and
+ cleans up a number of things.
+
+ <p>
+
+ Those wanting an easy way to test the 0.60.0 release with uClibc can
+ use <a href="http://user-mode-linux.sourceforge.net/">User-Mode Linux</a>
+ to give it a try by downloading and compiling
+ <a href="ftp://busybox.net/buildroot.tar.gz">buildroot.tar.gz</a>.
+ You don't have to be root or reboot your machine to run test this way.
+ Preconfigured User-Mode Linux kernel source is also on busybox.net.
+ <p>
+ Another cool thing is the nifty <a href="downloads/tutorial/index.html">
+ BusyBox Tutorial</a> contributed by K Computing. This requires
+ a ShockWave plugin (or standalone viewer), so you may want to grab the
+ the GPLed shockwave viewer from <a href="http://www.swift-tools.com/Flash/flash-0.4.10.tgz">here</a>
+ to view the tutorial.
+ <p>
+
+ Finally, In case you didn't notice anything odd about the
+ version number of this release, let me point out that this release
+ is <em>not</em> 0.53, because I bumped the version number up a
+ bit. This reflects the fact that this release is intended to form
+ a new stable BusyBox release series. If you need to rely on a
+ stable version of BusyBox, you should plan on using the stable
+ 0.60.x series. If bugs show up then I will release 0.60.1, then
+ 0.60.2, etc... This is also intended to deal with the fact that
+ the BusyBox build system will be getting a major overhaul for the
+ next release and I don't want that to break products that people
+ are shipping. To avoid that, the new build system will be
+ released as part of a new BusyBox development series that will
+ have some not-yet-decided-on odd version number. Once things
+ stabilize and the new build system is working for everyone, then
+ I will release that as a new stable release series.
+
+ <p>
+ The
+ <a href="downloads/Changelog">changelog</a> has all
+ the details. As usual BusyBox 0.60.0 can be downloaded from
+ <a href="downloads">http://busybox.net/downloads</a>.
+ <p>Have Fun!
+ <p>
+
+
+<li> <b>7 July 2001 -- BusyBox 0.52 released</b>
+<br>
+
+ I am very pleased to announce the immediate availability of
+ BusyBox 0.52 (the "new-and-improved rock-solid release"). This
+ release is the result of <em>many</em> hours of work and has tons
+ of bugfixes, optimizations, and cleanups. This release adds
+ several new applets, including several new shells (such as hush, msh,
+ and ash).
+
+ <p>
+ The
+ <a href="downloads/Changelog">changelog</a> covers
+ some of the more obvious details, but there are many many things that
+ are not mentioned, but have been improved in subtle ways. As usual,
+ BusyBox 0.52 can be downloaded from
+ <a href="downloads">http://busybox.net/downloads</a>.
+ <p>Have Fun!
+ <p>
+
+
+<li> <b>10 April 2001 - Graph of Busybox Growth </b>
+<br>
+The illustrious Larry Doolittle has made a PostScript chart of the growth
+of the Busybox tarball size over time. It is available for downloading /
+viewing <a href= "busybox-growth.ps"> right here</a>.
+
+<p> (Note that while the number of applets in Busybox has increased, you
+can still configure Busybox to be as small as you want by selectively
+turning off whichever applets you don't need.)
+<p>
+
+
+<li> <b>10 April 2001 -- BusyBox 0.51 released</b>
+<br>
+
+ BusyBox 0.51 (the "rock-solid release") is now out there. This
+ release adds only 2 new applets: env and vi. The vi applet,
+ contributed by Sterling Huxley, is very functional, and is only
+ 22k. This release fixes 3 critical bugs in the 0.50 release.
+ There were 2 potential segfaults in lash (the busybox shell) in
+ the 0.50 release which are now fixed. Another critical bug in
+ 0.50 which is now fixed: syslogd from 0.50 could potentially
+ deadlock the init process and thereby break your entire system.
+ <p>
+
+ There are a number of improvements in this release as well. For
+ one thing, the wget applet is greatly improved. Dmitry Zakharov
+ added FTP support, and Laurence Anderson make wget fully RFC
+ compliant for HTTP 1.1. The mechanism for including utility
+ functions in previous releases was clumsy and error prone. Now
+ all utility functions are part of a new libbb library, which makes
+ maintaining utility functions much simpler. And BusyBox now
+ compiles on itanium systems (thanks to the Debian itanium porters
+ for letting me use their system!).
+ <p>
+ You can read the
+ <a href="downloads/Changelog">changelog</a> for
+ complete details. BusyBox 0.51 can be downloaded from
+ <a href="downloads">http://busybox.net/downloads</a>.
+ <p>Have Fun!
+ <p>
+
+<li> <b>Busybox Boot-Floppy Image</b>
+
+<p>Because you asked for it, we have made available a <a href=
+"downloads/busybox.floppy.img"> Busybox boot floppy
+image</a>. Here's how you use it:
+
+<ol>
+
+ <li> <a href= "downloads/busybox.floppy.img">
+ Download the image</a>
+
+ <li> dd it onto a floppy like so: <tt> dd if=busybox.floppy.img
+ of=/dev/fd0 ; sync </tt>
+
+ <li> Pop it in a machine and boot up.
+
+</ol>
+
+<p> If you want to look at the contents of the initrd image, do this:
+
+<pre>
+ mount ./busybox.floppy.img /mnt -o loop -t msdos
+ cp /mnt/initrd.gz /tmp
+ umount /mnt
+ gunzip /tmp/initrd.gz
+ mount /tmp/initrd /mnt -o loop -t minix
+</pre>
+
+
+<li> <b>15 March 2001 -- BusyBox 0.50 released</b>
+<br>
+
+ This release adds several new applets including ifconfig, route, pivot_root, stty,
+ and tftp, and also fixes tons of bugs. Tab completion in the
+ shell is now working very well, and the shell's environment variable
+ expansion was fixed. Tons of other things were fixed or made
+ smaller. For a fairly complete overview, see the
+ <a href="downloads/Changelog">changelog</a>.
+ <p>
+ lash (the busybox shell) is still with us, fixed up a bit so it
+ now behaves itself quite nicely. It really is quite usable as
+ long as you don't expect it to provide Bourne shell grammer.
+ Standard things like pipes, redirects, command line editing, and
+ environment variable expansion work great. But we have found that
+ this shell, while very usable, does not provide an extensible
+ framework for adding in full Bourne shell behavior. So the first order of
+ business as we begin working on the next BusyBox release will be to merge in the new shell
+ currently in progress at
+ <a href="http://doolittle.faludi.com/~larry/parser.html">Larry Doolittle's website</a>.
+ <p>
+
+
+<li> <b>27 January 2001 -- BusyBox 0.49 released</b>
+<br>
+
+ Several new applets, lots of bug fixes, cleanups, and many smaller
+ things made nicer. Several cleanups and improvements to the shell.
+ For a list of the most interesting changes
+ you might want to look at the <a href="downloads/Changelog">changelog</a>.
+ <p>
+ Special thanks go out to Matt Kraai and Larry Doolittle for all their
+ work on this release, and for keeping on top of things while I've been
+ out of town.
+ <p>
+ <em>Special Note</em><br>
+
+ BusyBox 0.49 was supposed to have replaced lash, the BusyBox
+ shell, with a new shell that understands full Bourne shell/Posix shell grammer.
+ Well, that simply didn't happen in time for this release. A new
+ shell that will eventually replace lash is already under
+ construction. This new shell is being developed by Larry
+ Doolittle, and could use all of our help. Please see the work in
+ progress on <a href="http://doolittle.faludi.com/~larry/parser.html">Larry's website</a>
+ and help out if you can. This shell will be included in the next
+ release of BusyBox.
+ <p>
+
+<li> <b>13 December 2000 -- BusyBox 0.48 released</b>
+<br>
+
+ This release fixes lots and lots of bugs. This has had some very
+ rigorous testing, and looks very, very clean. The usual tar
+ update of course: tar no longer breaks hardlinks, tar -xzf is
+ optionally supported, and the LRP folks will be pleased to know
+ that 'tar -X' and 'tar --exclude' are both now in. Applets are
+ now looked up using a binary search making lash (the busybox
+ shell) much faster. For the new debian-installer (for Debian
+ woody) a .udeb can now be generated.
+ <p>
+ The curious can get a list of some of the more interesting changes by reading
+ the <a href="downloads/Changelog">changelog</a>.
+ <p>
+ Many thanks go out to the many many people that have contributed to
+ this release, especially Matt Kraai, Larry Doolittle, and Kent Robotti.
+ <p>
+<p> <li> <b>26 September 2000 -- BusyBox 0.47 released</b>
+<br>
+
+ This release fixes lots of bugs (including an ugly bug in 0.46
+ syslogd that could fork-bomb your system). Added several new
+ apps: rdate, wget, getopt, dos2unix, unix2dos, reset, unrpm,
+ renice, xargs, and expr. syslogd now supports network logging.
+ There are the usual tar updates. Most apps now use getopt for
+ more correct option parsing.
+ See the <a href="downloads/Changelog">changelog</a>
+ for complete details.
+
+
+<p> <li> <b>11 July 2000 -- BusyBox 0.46 released</b>
+<br>
+
+ This release fixes several bugs (including a ugly bug in tar,
+ and fixes for NFSv3 mount support). Added a dumpkmap to allow
+ people to dump a binary keymaps for use with 'loadkmap', and a
+ completely reworked 'grep' and 'sed' which should behave better.
+ BusyBox shell can now also be used as a login shell.
+ See the <a href="downloads/Changelog">changelog</a>
+ for complete details.
+
+
+<p> <li> <b>21 June 2000 -- BusyBox 0.45 released</b>
+<br>
+
+ This release has been slow in coming, but is very solid at this
+ point. BusyBox now supports libc5 as well as GNU libc. This
+ release provides the following new apps: cut, tr, insmod, ar,
+ mktemp, setkeycodes, md5sum, uuencode, uudecode, which, and
+ telnet. There are bug fixes for just about every app as well (see
+ the <a href="downloads/Changelog">changelog</a> for
+ details).
+ <p>
+ Also, some exciting infrastructure news! Busybox now has its own
+ <a href="lists/busybox/">mailing list</a>,
+ publically browsable
+ <a href="/cgi-bin/viewcvs.cgi/trunk/busybox/">CVS tree</a>,
+ anonymous
+ <a href="cvs_anon.html">CVS access</a>, and
+ for those that are actively contributing there is even
+ <a href="cvs_write.html">CVS write access</a>.
+ I think this will be a huge help to the ongoing development of BusyBox.
+ <p>
+ Also, for the curious, there is no 0.44 release. Somehow 0.44 got announced
+ a few weeks ago prior to its actually being released. To avoid any confusion
+ we are just skipping 0.44.
+ <p>
+ Many thanks go out to the many people that have contributed to this release
+ of BusyBox (esp. Pavel Roskin)!
+
+
+<p> <li> <b>19 April 2000 -- syslogd bugfix</b>
+<br>
+Turns out that there was still a bug in busybox syslogd.
+For example, with the following test app:
+<pre>
+#include &lt;syslog.h&gt;
+
+int do_log(char* msg, int delay)
+{
+ openlog("testlog", LOG_PID, LOG_DAEMON);
+ while(1) {
+ syslog(LOG_ERR, "%s: testing one, two, three\n", msg);
+ sleep(delay);
+ }
+ closelog();
+ return(0);
+};
+
+int main(void)
+{
+ if (fork()==0)
+ do_log("A", 2);
+ do_log("B", 3);
+}
+</pre>
+it should be logging stuff from both "A" and "B". As released in 0.43 only stuff
+from "A" would have been logged. This means that if init tries to log something
+while say ppp has the syslog open, init would block (which is bad, bad, bad).
+<p>
+Karl M. Hegbloom has created a fix for the problem.
+Thanks Karl!
+
+
+<p> <li> <b>18 April 2000 -- BusyBox 0.43 released (finally!)</b>
+<br>
+I have finally gotten everything into a state where I feel pretty
+good about things. This is definitely the most stable, solid release
+so far. A lot of bugs have been fixed, and the following new apps
+have been added: sh, basename, dirname, killall, uptime,
+freeramdisk, tr, echo, test, and usleep. Tar has been completely
+rewritten from scratch. Bss size has also been greatly reduced.
+More details are available in the
+<a href="downloads/Changelog">changelog</a>.
+Oh, and as a special bonus, I wrote some fairly comprehensive
+<em>documentation</em>, complete with examples and full usage information.
+
+<p>
+Many thanks go out to the fine people that have helped by submitting patches
+and bug reports; particularly instrumental in helping for this release were
+Karl Hegbloom, Pavel Roskin, Friedrich Vedder, Emanuele Caratti,
+Bob Tinsley, Nicolas Pitre, Avery Pennarun, Arne Bernin, John Beppu, and Jim Gleason.
+There were others so if I somehow forgot to mention you, I'm very sorry.
+<p>
+
+You can grab BusyBox 0.43 tarballs <a href="downloads">here</a>.
+
+<p> <li> <b>9 April 2000 -- BusyBox 0.43 pre release</b>
+<br>
+Unfortunately, I have not yet finished all the things I want to
+do for BusyBox 0.43, so I am posting this pre-release for people
+to poke at. This contains my complete rewrite of tar, which now weighs in at
+5k (7k with all options turned on) and works for reading and writing
+tarballs (which it does correctly for everything I have been able to throw
+at it). Tar also (optionally) supports the "--exclude" option (mainly because
+the Linux Router Project folks asked for it). This also has a pre-release
+of the micro shell I have been writing. This pre-release should be stable
+enough for production use -- it just isn't a release since I have some structural
+changes I still want to make.
+<p>
+The pre-release can be found <a href="downloads">here</a>.
+Please let me know ASAP if you find <em>any</em> bugs.
+
+<p> <li> <b>28 March 2000 -- Andersen Baby Boy release</b>
+<br>
+I am pleased to announce that on Tuesday March 28th at 5:48pm, weighing in at 7
+lbs. 12 oz, Micah Erik Andersen was born at LDS Hospital here in Salt Lake City.
+He was born in the emergency room less then 5 minutes after we arrived -- and
+it was such a relief that we even made it to the hospital at all. Despite the
+fact that I was driving at an amazingly unlawful speed and honking at everybody
+and thinking decidedly unkind thoughts about the people in our way, my wife
+(inconsiderate of my feelings and complete lack of medical training) was lying
+down in the back seat saying things like "I think I need to start pushing now"
+(which she then proceeded to do despite my best encouraging statements to the
+contrary).
+<p>
+Anyway, I'm glad to note that despite the much-faster-than-we-were-expecting
+labor, both Shaunalei and our new baby boy are doing wonderfully.
+<p>
+So now that I am done with my excuse for the slow release cycle...
+Progress on the next release of BusyBox has been slow but steady. I expect
+to have a release sometime during the first week of April. This release will
+include a number of important changes, including the addition of a shell, a
+re-write of tar (to accommodate the Linux Router Project), and syslogd can now
+accept multiple concurrent connections, fixing lots of unexpected blocking
+problems.
+
+
+<p> <li> <b>11 February 2000 -- BusyBox 0.42 released</b>
+<br>
+
+ This is the most solid BusyBox release so far. Many, many
+ bugs have been fixed. See the
+ <a href="downloads/Changelog">changelog</a> for details.
+
+ Of particular interest, init will now cleanly unmount
+ filesystems on reboot, cp and mv have been rewritten and
+ behave much better, and mount and umount no longer leak
+ loop devices. Many thanks go out to Randolph Chung,
+ Karl M. Hegbloom, Taketoshi Sano, and Pavel Roskin for
+ their hard work on this release of BusyBox. Please pound
+ on it and let me know if you find any bugs.
+
+<p> <li> <b>19 January 2000 -- BusyBox 0.41 released</b>
+<br>
+
+ This release includes bugfixes to cp, mv, logger, true, false,
+ mkdir, syslogd, and init. New apps include wc, hostid,
+ logname, tty, whoami, and yes. New features include loop device
+ support in mount and umount, and better TERM handling by init.
+ The changelog can be found <a href="downloads/Changelog">here</a>.
+
+<p> <li> <b>7 January 2000 -- BusyBox 0.40 released</b>
+<br>
+
+ This release includes bugfixes to init (now includes inittab support),
+ syslogd, head, logger, du, grep, cp, mv, sed, dmesg, ls, kill, gunzip, and mknod.
+ New apps include sort, uniq, lsmod, rmmod, fbset, and loadacm.
+ In particular, this release fixes an important bug in tar which
+ in some cases produced serious security problems.
+ As always, the changelog can be found <a href="downloads/Changelog">here</a>.
+
+<p> <li> <b>11 December 1999 -- BusyBox Website</b>
+<br>
+ I have received permission from Bruce Perens (the original author of BusyBox)
+ to set up this site as the new primary website for BusyBox. This website
+ will always contain pointers to the latest and greatest, and will also
+ contain the latest documentation on how to use BusyBox, what it can do,
+ what arguments its apps support, etc.
+
+<p> <li> <b>10 December 1999 -- BusyBox 0.39 released</b>
+<br>
+ This release includes fixes to init, reboot, halt, kill, and ls, and contains
+ the new apps ping, hostname, mkfifo, free, tail, du, tee, and head. A full
+ changelog can be found <a href="downloads/Changelog">here</a>.
+<p> <li> <b>5 December 1999 -- BusyBox 0.38 released</b>
+<br>
+ This release includes fixes to tar, cat, ls, dd, rm, umount, find, df,
+ and make install, and includes new apps syslogd/klogd and logger.
+
+
+</ul>
+
+
+<!--#include file="footer.html" -->
+
diff --git a/i/pc104/initrd/conf/busybox/docs/busybox.net/products.html b/i/pc104/initrd/conf/busybox/docs/busybox.net/products.html
new file mode 100644
index 0000000..daf8add
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/docs/busybox.net/products.html
@@ -0,0 +1,170 @@
+<!--#include file="header.html" -->
+
+
+<h3>Products/Projects Using BusyBox</h3>
+
+Do you use BusyBox? I'd love to know about it and
+I'd be happy to link to you.
+
+<p>
+I know of the following products and/or projects that use BusyBox --
+listed in the order I happen to add them to the web page:
+
+<ul>
+
+<li><a href="http://buildroot.uclibc.org/">buildroot</a><br>A configurable
+means for building your own busybox/uClibc based system systems, maintained
+by the uClibc developers.
+
+<li><a href="http://openwrt.org">OpenWrt</a> a Linux distribution for embedded
+devices, based on buildroot.
+
+<li><a href="http://www.pengutronix.de/software/ptxdist_en.html">PTXdist</a><br>another
+configurable means for building your own busybox based system systems.
+
+</li><li><a href=
+"http://cvs.debian.org/boot-floppies/">
+Debian installer (boot floppies) project</a>
+
+</li><li><a href="http://redhat.com/">Red Hat installer</a>
+
+</li><li><a href=
+"http://distro.ibiblio.org/pub/Linux/distributions/slackware/slackware-current/source/rootdisks/">
+Slackware Installer</a>
+
+</li><li><a href="http://www.gentoo.org/">Gentoo Linux install/boot CDs</a>
+</li><li><a href="http://www.mandriva.com/">The Mandriva installer</a>
+
+</li><li><a href="http://Leaf.SourceForge.net">Linux Embedded Appliance Firewall</a><br>The sucessor of the Linux Router Project, supporting all sorts of embedded Linux gateways, routers, wireless routers, and firewalls.
+
+</li><li><a href=
+"http://www.toms.net/rb/">tomsrtbt</a>
+
+</li><li><a href="http://www.stormix.com/">Stormix
+Installer</a>
+
+</li><li><a href=
+"http://www.emacinc.com/linux2_sbc.htm">EMAC Linux
+2.0 SBC</a>
+
+</li><li><a href="http://www.trinux.org/">Trinux</a>
+
+</li><li><a href="http://oddas.sourceforge.net/">ODDAS
+project</a>
+
+</li><li><a href="http://byld.sourceforge.net/">Build Your
+Linux Disk</a>
+
+</li><li><a href=
+"http://ibiblio.org/pub/Linux/system/recovery">Zdisk</a>
+
+</li><li><a href="http://www.adtran.com">AdTran -
+VPN/firewall VPN Linux Distribution</a>
+
+</li><li><a href="http://mkcdrec.ota.be/">mkCDrec - make
+CD-ROM recovery</a>
+
+</li><li><a href=
+"http://recycle.lbl.gov/~ldoolitt/bse/">Linux on
+nanoEngine</a>
+
+</li><li><a href=
+"http://www.zelow.no/floppyfw/">Floppyfw</a>
+
+</li><li><a href="http://www.ltsp.org/">Linux Terminal
+Server Project</a>
+
+</li><li><a href="http://www.devil-linux.org/">Devil-Linux</a>
+
+</li><li><a href="http://dutnux.sourceforge.net/">DutNux</a>
+
+</li><li><a href="http://www.microwerks.net/~hugo/mindi/">Mindi</a>
+
+</li><li><a href="http://www.minimalinux.org/ttylinux/">ttylinux</a>
+
+</li><li><a href="http://www.coyotelinux.com/">Coyote Linux</a>
+
+</li><li><a href="http://www.partimage.org/">Partition
+Image</a>
+
+</li><li><a href="http://www.fli4l.de/">fli4l the on(e)-disk-router</a>
+
+</li><li><a href="http://tinfoilhat.cultists.net/">Tinfoil
+Hat Linux</a>
+
+</li><li><a href="http://sourceforge.net/projects/gp32linux/">gp32linux</a>
+</li><li><a href="http://familiar.handhelds.org/">Familiar Linux</a><br>A linux distribution for handheld computers
+</li><li><a href="http://rescuecd.sourceforge.net/">Timo's Rescue CD Set</a>
+</li><li><a href="http://sf.net/projects/netstation/">Netstation</a>
+</li><li><a href="http://www.fiwix.org/">GNU/Fiwix Operating System</a>
+</li><li><a href="http://www.softcraft.com/">Generations Linux</a>
+</li><li><a href="http://systemimager.org/relatedprojects/">SystemImager / System Installation Suite</a>
+</li><li><a href="http://www.bablokb.de/gendist/">GENDIST distribution generator</a>
+</li><li><a href="http://diet-pc.sourceforge.net/">DIET-PC embedded Linux thin client distribution</a>
+</li><li><a href="http://byzgl.sourceforge.net/">BYZantine Gnu/Linux</a>
+</li><li><a href="http://dban.sourceforge.net/">Darik's Boot and Nuke</a>
+</li><li><a href="http://www.timesys.com/">TimeSys real-time Linux</a>
+</li><li><a href="http://movix.sf.net/">MoviX</a><br>Boots from CD and automatically plays every video file on the CD
+</li><li><a href="http://katamaran.sourceforge.net">katamaran</a><br>Linux, X11, xfce windowmanager, based on BusyBox
+</li><li><a href="http://www.sourceforge.net/projects/simplygnustep">Prometheus SimplyGNUstep</a>
+</li><li><a href="http://www.renyi.hu/~ekho/lowlife/">lowlife</a><br>A documentation project on how to make your own uClibc-based systems and floppy.
+</li><li><a href="http://metadistros.hispalinux.es/">Metadistros</a><br>a project to allow you easily make Live-CD distributions.
+</li><li><a href="http://salvare.sourceforge.net/">Salvare</a><br>More Linux than tomsrtbt but less than Knoppix, aims to provide a useful workstation as well as a rescue disk.
+</li><li><a href="http://www.stresslinux.org/">stresslinux</a><br>minimal linux distribution running from a bootable cdrom or via PXE.
+</li><li><a href="http://thinstation.sourceforge.net/">thinstation</a><br>convert standard PCs into full-featured diskless thinclients.
+</li><li><a href="http://www.uhulinux.hu/">UHU-Linux Hungary</a>
+</li><li><a href="http://deep-water.berlios.de/">Deep-Water Linux</a>
+</li><li><a href="http://www.freesco.org/">Freesco router</a>
+</li><li><a href="http://Sentry.SourceForge.net/">Sentry Firewall CD</a>
+
+
+
+</li><li><a href="http://tuxscreen.net">Tuxscreen Linux Phone</a>
+</li><li><a href="http://www.kerbango.com/">The Kerbango Internet Radio</a>
+</li><li><a href="http://www.linuxmagic.com/vpn/">LinuxMagic VPN Firewall</a>
+</li><li><a href="http://www.isilver-inc.com/">I-Silver Linux appliance servers</a>
+</li><li><a href="http://zaurus.sourceforge.net/">Sharp Zaurus PDA</a>
+</li><li><a href="http://www.cyclades.com/">Cyclades-TS and other Cyclades products</a>
+</li><li><a href="http://www.linksys.com/products/product.asp?prid=508">Linksys WRT54G - Wireless-G Broadband Router</a>
+</li><li><a href="http://www.dell.com/us/en/biz/topics/sbtopic_005_truemobile.htm">Dell TrueMobile 1184</a>
+</li><li><a href="http://actiontec.com/products/modems/dual_pcmodem/dpm_overview.html">Actiontec Dual PC Modem</a>
+</li><li><a href="http://www.kiss-technology.com/">Kiss DP Series DVD players</a>
+</li><li><a href="http://www.netgear.com/products/prod_details.asp?prodID=170">NetGear WG602 wireless router</a>
+ <br>with sources <a href="http://www.netgear.com/support/support_details.asp?dnldID=453">here</a>
+</li><li><a href="http://www.trendware.com/products/TEW-411BRP.htm">TRENDnet TEW-411BRP 802.11g Wireless AP/Router/Switch</a>
+ <br>Source for busybox and udhcp <a href="http://www.trendware.com/asp/download/fileinfo.asp?file_id=277&amp;B1=Search">here</a> though no kernel source is provided.
+</li><li><a href="http://www.buffalo-technology.com/webcontent/products/wireless/wbr-g54.htm">Buffalo WBR-G54 wireless router</a>
+ </li><li><a href="http://www.asus.com/products/communication/wireless/wl-300g/overview.htm">ASUS WL-300g Wireless LAN Access Point</a>
+ <br>with source<a href="http://www.asus.com.tw/support/download/item.aspx?ModelName=WL-300G">here</a>
+ </li><li><a href="http://catalog.belkin.com/IWCatProductPage.process?Merchant_Id=&amp;Section_Id=201522&amp;pcount=&amp;Product_Id=136493">Belkin 54g Wireless DSL/Cable Gateway Router</a>
+ <br>with source<a href="http://web.belkin.com/support/gpl.asp">here</a>
+ <li><a href="http://www.acronis.com/products/partitionexpert/">Acronis PartitionExpert 2003</a>
+ <br>includes a heavily modified BusyBox v0.60.5 with built in
+ cardmgr, device detection, gpm, lspci, etc. Also includes udhcp,
+ uClibc 0.9.26, a heavily patched up linux kernel, etc. Source
+ can only be obtained <a href="http://www.acronis.com/files/gpl/linux.tar.bz2">here</a>
+
+</li><li><a href="http://www.usr.com/">U.S. Robotics Sureconnect 4-port ADSL router</a><br>
+ with source <a href="http://www.usr.com/support/s-gpl-code.asp">here</a>
+</li><li><a href="http://www.actiontec.com/products/broadband/54mbps_wireless_gateway_1p/index.html">
+ ActionTec GT701-WG Wireless Gateway/DSL Modem</a>
+ with source <a href="http://128.121.226.214/gtproducts/index.html">here</a>
+</li><li><a href="http://smartlinux.sourceforge.net/">S.M.A.R.T. Linux</a>
+</li><li><a href="http://www.dlink.com/">DLink - Model GSL-G604T, DSL-300T, and possibly other models</a>
+ with source <a href="ftp://ftp.dlink.co.uk/dsl_routers_modems/">here,</a>
+ with source <a href="ftp://ftp.dlink.de/dsl-products/">and here,</a>
+ and quite possibly other places as well. You may need to dig down a bit
+ to find the source, but it does seem to be there.
+</li><li><a href="http://www.siemens-mobile.de/cds/frontdoor/0,2241,de_de_0_42931_rArNrNrNrN,00.html">Siemens SE515 DSL router</a>
+ with source <a href="http://now-portal.c-lab.de/projects/gigaset/">here, I think...</a>
+ with some details <a href="http://heinz.hippenstiel.org/familie/hp/hobby/gigaset_se515dsl.html">here.</a>
+</li><li><a href="http://frwt.stim.ru/">Free Remote Windows Terminal</a>
+
+</li><li><a href="http://www.zyxel.com/">ZyXEL Routers</a>
+
+</li>
+</ul>
+
+
+<!--#include file="footer.html" -->
+
diff --git a/i/pc104/initrd/conf/busybox/docs/busybox.net/screenshot.html b/i/pc104/initrd/conf/busybox/docs/busybox.net/screenshot.html
new file mode 100644
index 0000000..4e07fda
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/docs/busybox.net/screenshot.html
@@ -0,0 +1,62 @@
+<!--#include file="header.html" -->
+
+
+<!-- Begin Screenshot -->
+
+<h3> Busybox Screenshot! </h3>
+
+
+Everybody loves to look at screenshots, so here is a live action screenshot of BusyBox.
+
+<pre style="background-color: black; color: lightgreen; padding: 5px;
+font-family: monospace; font-size: smaller;" width="100">
+
+
+$ ./busybox
+BusyBox v1.1.2 (2006.04.11-08:27+0000) multi-call binary
+
+Usage: busybox [function] [arguments]...
+ or: [function] [arguments]...
+
+ BusyBox is a multi-call binary that combines many common Unix
+ utilities into a single executable. Most people will create a
+ link to busybox for each function they wish to use and BusyBox
+ will act like whatever it was invoked as!
+
+Currently defined functions:
+ [, [[, addgroup, adduser, adjtimex, ar, arping, ash, awk, basename,
+ bbconfig, bunzip2, busybox, bzcat, cal, cat, chattr, chgrp, chmod,
+ chown, chroot, chvt, clear, cmp, comm, cp, cpio, crond, crontab,
+ cut, date, dc, dd, deallocvt, delgroup, deluser, devfsd, df, dirname,
+ dmesg, dnsd, dos2unix, dpkg, dpkg-deb, du, dumpkmap, dumpleases,
+ e2fsck, e2label, echo, egrep, eject, env, ether-wake, expr, fakeidentd,
+ false, fbset, fdflush, fdformat, fdisk, fgrep, find, findfs, fold,
+ free, freeramdisk, fsck, fsck.ext2, fsck.ext3, fsck.minix, ftpget,
+ ftpput, fuser, getopt, getty, grep, gunzip, gzip, halt, hdparm,
+ head, hexdump, hostid, hostname, httpd, hush, hwclock, id, ifconfig,
+ ifdown, ifup, inetd, init, insmod, install, ip, ipaddr, ipcalc,
+ ipcrm, ipcs, iplink, iproute, iptunnel, kill, killall, klogd,
+ lash, last, length, less, linux32, linux64, linuxrc, ln, loadfont,
+ loadkmap, logger, login, logname, logread, losetup, ls, lsattr,
+ lsmod, lzmacat, makedevs, md5sum, mdev, mesg, mkdir, mke2fs, mkfifo,
+ mkfs.ext2, mkfs.ext3, mkfs.minix, mknod, mkswap, mktemp, modprobe,
+ more, mount, mountpoint, msh, mt, mv, nameif, nc, netstat, nice,
+ nohup, nslookup, od, openvt, passwd, patch, pidof, ping, ping6,
+ pipe_progress, pivot_root, poweroff, printenv, printf, ps, pwd,
+ rdate, readlink, readprofile, realpath, reboot, renice, reset,
+ rm, rmdir, rmmod, route, rpm, rpm2cpio, run-parts, runlevel, rx,
+ sed, seq, setarch, setconsole, setkeycodes, setsid, sha1sum, sleep,
+ sort, start-stop-daemon, stat, strings, stty, su, sulogin, sum,
+ swapoff, swapon, switch_root, sync, sysctl, syslogd, tail, tar,
+ tee, telnet, telnetd, test, tftp, time, top, touch, tr, traceroute,
+ true, tty, tune2fs, udhcpc, udhcpd, umount, uname, uncompress,
+ uniq, unix2dos, unlzma, unzip, uptime, usleep, uudecode, uuencode,
+ vconfig, vi, vlock, watch, watchdog, wc, wget, which, who, whoami,
+ xargs, yes, zcat, zcip
+
+$ <span style="text-decoration:blink;">_</span>
+
+</pre>
+
+<!--#include file="footer.html" -->
+
diff --git a/i/pc104/initrd/conf/busybox/docs/busybox.net/shame.html b/i/pc104/initrd/conf/busybox/docs/busybox.net/shame.html
new file mode 100644
index 0000000..d9da44b
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/docs/busybox.net/shame.html
@@ -0,0 +1,82 @@
+<!--#include file="header.html" -->
+
+
+<h3>Hall of Shame!!!</h3>
+
+<p>This page is no longer updated, these days we forward this sort of
+thing to the <a href="http://www.softwarefreedom.org">Software Freedom Law
+Center</a> instead.</p>
+
+<p>The following products and/or projects appear to use BusyBox, but do not
+appear to release source code as required by the <a
+href="/license.html">BusyBox license</a>. This is a violation of the law!
+The distributors of these products are invited to contact <a href=
+"mailto:andersen@codepoet.org">Erik Andersen</a> if they have any confusion
+as to what is needed to bring their products into compliance, or if they have
+already brought their product into compliance and wish to be removed from the
+Hall of Shame.
+
+<p>
+
+Here are the details of <a href="/license.html">exactly how to comply
+with the BusyBox license</a>, so there should be no question as to
+exactly what is expected.
+Complying with the Busybox license is easy and completely free, so the
+companies listed below should be ashamed of themselves. Furthermore, each
+product listed here is subject to being legally ordered to cease and desist
+distribution for violation of copyright law, and the distributor of each
+product is subject to being sued for statutory copyright infringement damages
+of up to $150,000 per work plus legal fees. Nobody wants to be sued, and <a
+href="mailto:andersen@codepoet.org">Erik</a> certainly would prefer to spend
+his time doing better things than sue people. But he will sue if forced to
+do so to maintain compliance.
+
+<p>
+
+Do everyone a favor and don't break the law -- if you use busybox, comply with
+the busybox license by releasing the source code with your product.
+
+<p>
+
+<ul>
+
+ <li><a href="http://www.trittontechnologies.com/products.html">Tritton Technologies NAS120</a>
+ <br>see <a href="http://www.ussg.iu.edu/hypermail/linux/kernel/0404.0/1611.html">here for details</a>
+ <li><a href="http://www.macsense.com/product/homepod/">Macsense HomePod</a>
+ <br>with details
+ <a href="http://developer.gloolabs.com/modules.php?op=modload&amp;name=Forums&amp;file=viewtopic&amp;topic=123&amp;forum=7">here</a>
+ <li><a href="http://www.cpx.com/products.asp?c=Wireless+Products">Compex Wireless Products</a>
+ <br>appears to be running v0.60.5 with Linux version 2.4.20-uc0 on ColdFire,
+ but no source code is mentioned or offered.
+ <li><a href="http://www.inventel.com/en/product/datasheet/10/">Inventel DW 200 wireless/ADSL router</a>
+ <li><a href="http://www.sweex.com/product.asp">Sweex DSL router</a>
+ <br>appears to be running BusyBox v1.00-pre2 and udhcpd, but no source
+ code is mentioned or offered.
+ <li><a href="http://www.trendware.com/products/TEW-410APB.htm">TRENDnet TEW-410APB</a>
+ </li><li><a href="http://www.hauppauge.com/Pages/products/data_mediamvp.html">Hauppauge Media MVP</a>
+ <br>Hauppauge contacted me on 16 Dec 2003, and claims to be working on resolving this problem.
+ </li><li><a href="http://www.hitex.com/download/adescom/data/">TriCore</a>
+ </li><li><a href="http://www.allnet.de/">ALLNET 0186 wireless router</a>
+ </li><li><a href="http://www.dmmtv.com/">Dreambox DM7000S DVB Satellite Receiver</a>
+ <br> Dream Multimedia contacted me on 22 Dec 2003 and is working on resolving this problem.
+ <br> Source _may_ be here: http://cvs.tuxbox.org/cgi-bin/viewcvs.cgi/tuxbox/cdk/
+ </li><li><a href="http://testing.lkml.org/slashdot.php?mid=331690">Sigma Designs EM8500 based DVD players</a>
+ <br>Source for the Sigma Designs reference platform is found here<br>
+ <a href="http://www.uclinux.org/pub/uClinux/ports/arm/EM8500/uClinux-2.4-sigma.tar.gz">uClinux-2.4-sigma.tar.gz</a>, so while Sigma Designs itself appears to be in compliance, as far as I can tell,
+ no vendors of Sigma Designs EM8500 based devices actually comply with the GPL....
+ </li><li><a href="http://testing.lkml.org/slashdot.php?mid=433790">Liteon LVD2001 DVD player using the Sigma Designs EM8500</a>
+ </li><li><a href="http://www.rimax.net/">Rimax DVD players using the Sigma Designs EM8500</a>
+ </li><li><a href="http://www.vinc.us/">Bravo DVD players using the Sigma Designs EM8500</a>
+ </li><li><a href="http://www.hb-direct.com/">H&amp;B DX3110 Divx player based on Sigma Designs EM8500</a>
+ </li><li><a href="http://www.recospa.it/mdpro1/index.php">United *DVX4066 mpeg4 capable DVD players</a>
+ </li><li><a href="http://www.a-link.com/RR64AP.html">Avaks alink Roadrunner 64</a>
+ <br> Partial source available, based on source distributed under NDA from <a href="http://www.lsilogic.com/products/dsl_platform_solutions/hb_linuxr2_2.html"> LSILogic</a>. Why the NDA LSILogic, what are you hiding ?
+ <br>To verify the Avaks infrigment see my slashdot <a href="http://slashdot.org/~bug1/journal/">journal</a>.
+ <br>The ZipIt wireless IM device appears to be using Busybox-1.00-pre1 in the ramdisk, however no source has been made available.
+ </li><li>Undoubtedly there are others... Please report them so we can shame them (or if necessary sue them) into compliance.
+
+</ul>
+
+
+<!--#include file="footer.html" -->
+
diff --git a/i/pc104/initrd/conf/busybox/docs/busybox.net/sponsors.html b/i/pc104/initrd/conf/busybox/docs/busybox.net/sponsors.html
new file mode 100644
index 0000000..ba7920b
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/docs/busybox.net/sponsors.html
@@ -0,0 +1,35 @@
+<!--#include file="header.html" -->
+
+<h3>Sponsors</h3>
+
+<p>Please visit our sponsors and thank them for their support! They have
+provided money for equipment and bandwidth. Next time you need help with a
+project, consider these fine companies!</p>
+
+
+<ul>
+ <li><a href="http://osuosl.org/">OSU OSL</a><br>
+ OSU OSL kindly provides hosting for BusyBox and uClibc.
+ </li>
+
+ <li><a href="http://www.penguru.net">Penguru Consulting</a><br>
+ Custom development for embedded Linux systems and multimedia platforms
+ </li>
+
+ <li><a href="http://opensource.se/">opensource.se</a><br>
+ Embedded open source consulting in Europe.
+ </li>
+
+ <li><a href="http://www.codepoet-consulting.com">Codepoet Consulting</a><br>
+ Custom Linux, embedded Linux, BusyBox, and uClibc development.
+ </li>
+
+ <li><a href="http://www.timesys.com">TimeSys</a><br>
+ Embedded Linux development, cross-compilers, real-time, KGDB, tsrpm and cygwin.
+ </li>
+</ul>
+
+<p>If you wish to be a sponsor, or if you have already contributed and would
+like your name added here, email <a href="mailto:rob@landley.net">Rob</a>.</p>
+
+<!--#include file="footer.html" -->
diff --git a/i/pc104/initrd/conf/busybox/docs/busybox.net/subversion.html b/i/pc104/initrd/conf/busybox/docs/busybox.net/subversion.html
new file mode 100644
index 0000000..7fb620f
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/docs/busybox.net/subversion.html
@@ -0,0 +1,52 @@
+<!--#include file="header.html" -->
+
+<h3>Accessing Source</h3>
+
+
+
+<h3>Patches</h3>
+
+<p>If you don't want to mess with subversion, you can download
+<a href="/downloads/patches/">all BusyBox patches</a> or check the
+<a href="/downloads/patches/recent.html">most recent patches</a>.
+
+<h3>Anonymous Subversion Access</h3>
+
+We allow anonymous (read-only) Subversion (svn) access to everyone. To
+grab a copy of the latest version of BusyBox using anonymous svn access:
+
+<pre>
+svn co svn://busybox.net/trunk/busybox</pre>
+
+<p>
+The current <em>stable branch</em> can be obtained with
+<pre>
+svn co svn://busybox.net/branches/busybox_1_1_stable
+</pre>
+
+<p>
+
+If you are not already familiar with using Subversion, I recommend you visit <a
+href="http://subversion.tigris.org/">the Subversion website</a>. You might
+also want to read online or buy a copy of <a
+href="http://svnbook.red-bean.com/">the Subversion Book</a>. If you are
+already comfortable with using CVS, you may want to skip ahead to the <a
+href="http://svnbook.red-bean.com/en/1.1/apa.html">Subversion for CVS Users</a>
+part of the Subversion Book.
+
+<p>
+
+Once you've checked out a copy of the source tree, you can update your source
+tree at any time so it is in sync with the latest and greatest by entering your
+BusyBox directory and running the command:
+
+<pre>
+svn update</pre>
+
+Because you've only been granted anonymous access to the tree, you won't be
+able to commit any changes. Changes can be submitted for inclusion by posting
+them to the BusyBox mailing list. For those that are actively contributing
+<a href="developer.html">Subversion commit access</a> can be made available.
+
+<!--#include file="footer.html" -->
+
diff --git a/i/pc104/initrd/conf/busybox/docs/busybox.net/tinyutils.html b/i/pc104/initrd/conf/busybox/docs/busybox.net/tinyutils.html
new file mode 100644
index 0000000..9122d6e
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/docs/busybox.net/tinyutils.html
@@ -0,0 +1,86 @@
+<!--#include file="header.html" -->
+
+
+<h3>External Tiny Utilities</h3>
+
+This is a list of tiny utilities whose functionality is not provided by
+busybox. If you have additional suggestions, please send an e-mail to our
+dev mailing list.
+
+<br><br>
+
+<table border=1>
+<tr>
+ <th>Feature</th>
+ <th>Utilities</th>
+</tr>
+
+<tr>
+ <td>SSH</td>
+ <td><a href="http://matt.ucc.asn.au/dropbear/">Dropbear</a> has both an ssh server and an ssh client that together come in around 100k. It has no external
+dependencies (I.E. it does not depend on OpenSSL, using a built-in copy of
+LibTomCrypt instead). It's actively maintained, with a quiet but responsive
+mailing list.</td>
+</tr>
+
+<tr>
+ <td>SMTP</td>
+ <td><a href="ftp://ftp.debian.org/debian/pool/main/s/ssmtp/">ssmtp</a> is an extremely simple Mail Transfer Agent.</td>
+</tr>
+
+<tr>
+ <td>ntp</td>
+ <td><a href="http://doolittle.icarus.com/ntpclient/">ntpclient</a> is a
+tiny ntp client. BusyBox has rdate to set the date from a remote server, but
+if you want a daemon to repeatedly adjust the clock over time, try that.</td>
+</table>
+
+<p>In a gui environment, you'll probably want a web browser.
+<a href="http://www.konqueror.org/embedded/">Konqueror Embedded</a> requires QT
+(or QT Embedded), but not KDE. The <a href="http://www.dillo.org/">Dillo</a>
+requires GTK+, but not Gnome. Or you can try the <a href="http://links.twibright.com/">graphical
+version of links</a>.</p>
+
+<h3>SCRIPTING LANGUAGES</h3>
+<p>Although busybox has built-in support for shell scripts, plenty of other
+small scripting languages are available on the net. A few examples:</p>
+<table border=1>
+<tr>
+<th><language></th>
+<th><description></th>
+</tr>
+<tr>
+<td> <a href=http://www.foo.be/docs/tpj/issues/vol5_3/tpj0503-0003.html>microperl</a> </td>
+<td> A small standalone perl interpreter that can be built from the perl source
+s via "make -f Makefile.micro". If you really feel the need for perl on an embe
+dded system, this is where to start.
+</tr>
+<tr>
+
+<td><a href=http://www.lua.org/pil/>Lua</a></td>
+<td>If you just want a small embedded scripting language to write <em>new</en>
+code in, this Brazilian import is lightweight, fairly popular, and has
+a complete book about it online.</td>
+</tr>
+
+<tr>
+<td><a href= http://www.star.le.ac.uk/%7Etjg/rc/>rc</a></td>
+<td>The PLAN9 shell. Not compatible with conventional bourne shell syntax,
+but fairly lightweight and small.</td>
+</tr>
+
+</tr>
+<tr>
+<td><a href=http://www.forth.org>forth</a></td>
+<td>A well known language for fast and small programs, decades old but still
+in use for everything from OpenBIOS to computer controlled engine timing.</td>
+</tr>
+</table>
+
+<p>For more information, you probably want to look at
+<a href=http://buildroot.uclibc.org>buildroot</a> and
+<a href=http://gentoo-wiki.com/TinyGentoo>TinyGentoo</a>, which
+build and use tiny utilities for all sorts of things.</p>
+
+<!--#include file="footer.html" -->
+
diff --git a/i/pc104/initrd/conf/busybox/docs/busybox_footer.pod b/i/pc104/initrd/conf/busybox/docs/busybox_footer.pod
new file mode 100644
index 0000000..15e3a4f
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/docs/busybox_footer.pod
@@ -0,0 +1,258 @@
+=back
+
+=head1 LIBC NSS
+
+GNU Libc (glibc) uses the Name Service Switch (NSS) to configure the behavior
+of the C library for the local environment, and to configure how it reads
+system data, such as passwords and group information. This is implemented
+using an /etc/nsswitch.conf configuration file, and using one or more of the
+/lib/libnss_* libraries. BusyBox tries to avoid using any libc calls that make
+use of NSS. Some applets however, such as login and su, will use libc functions
+that require NSS.
+
+If you enable CONFIG_USE_BB_PWD_GRP, BusyBox will use internal functions to
+directly access the /etc/passwd, /etc/group, and /etc/shadow files without
+using NSS. This may allow you to run your system without the need for
+installing any of the NSS configuration files and libraries.
+
+When used with glibc, the BusyBox 'networking' applets will similarly require
+that you install at least some of the glibc NSS stuff (in particular,
+/etc/nsswitch.conf, /lib/libnss_dns*, /lib/libnss_files*, and /lib/libresolv*).
+
+Shameless Plug: As an alternative, one could use a C library such as uClibc. In
+addition to making your system significantly smaller, uClibc does not require the
+use of any NSS support files or libraries.
+
+=head1 MAINTAINER
+
+Denis Vlasenko <vda.linux@googlemail.com>
+
+=head1 AUTHORS
+
+The following people have contributed code to BusyBox whether they know it or
+not. If you have written code included in BusyBox, you should probably be
+listed here so you can obtain your bit of eternal glory. If you should be
+listed here, or the description of what you have done needs more detail, or is
+incorect, please send in an update.
+
+
+=for html <br>
+
+Emanuele Aina <emanuele.aina@tiscali.it>
+ run-parts
+
+=for html <br>
+
+Erik Andersen <andersen@codepoet.org>
+
+ Tons of new stuff, major rewrite of most of the
+ core apps, tons of new apps as noted in header files.
+ Lots of tedious effort writing these boring docs that
+ nobody is going to actually read.
+
+=for html <br>
+
+Laurence Anderson <l.d.anderson@warwick.ac.uk>
+
+ rpm2cpio, unzip, get_header_cpio, read_gz interface, rpm
+
+=for html <br>
+
+Jeff Angielski <jeff@theptrgroup.com>
+
+ ftpput, ftpget
+
+=for html <br>
+
+Edward Betts <edward@debian.org>
+
+ expr, hostid, logname, whoami
+
+=for html <br>
+
+John Beppu <beppu@codepoet.org>
+
+ du, nslookup, sort
+
+=for html <br>
+
+Brian Candler <B.Candler@pobox.com>
+
+ tiny-ls(ls)
+
+=for html <br>
+
+Randolph Chung <tausq@debian.org>
+
+ fbset, ping, hostname
+
+=for html <br>
+
+Dave Cinege <dcinege@psychosis.com>
+
+ more(v2), makedevs, dutmp, modularization, auto links file,
+ various fixes, Linux Router Project maintenance
+
+=for html <br>
+
+Jordan Crouse <jordan@cosmicpenguin.net>
+
+ ipcalc
+
+=for html <br>
+
+Magnus Damm <damm@opensource.se>
+
+ tftp client insmod powerpc support
+
+=for html <br>
+
+Larry Doolittle <ldoolitt@recycle.lbl.gov>
+
+ pristine source directory compilation, lots of patches and fixes.
+
+=for html <br>
+
+Glenn Engel <glenne@engel.org>
+
+ httpd
+
+=for html <br>
+
+Gennady Feldman <gfeldman@gena01.com>
+
+ Sysklogd (single threaded syslogd, IPC Circular buffer support,
+ logread), various fixes.
+
+=for html <br>
+
+Karl M. Hegbloom <karlheg@debian.org>
+
+ cp_mv.c, the test suite, various fixes to utility.c, &c.
+
+=for html <br>
+
+Daniel Jacobowitz <dan@debian.org>
+
+ mktemp.c
+
+=for html <br>
+
+Matt Kraai <kraai@alumni.cmu.edu>
+
+ documentation, bugfixes, test suite
+
+=for html <br>
+
+Stephan Linz <linz@li-pro.net>
+
+ ipcalc, Red Hat equivalence
+
+=for html <br>
+
+John Lombardo <john@deltanet.com>
+
+ tr
+
+=for html <br>
+
+Glenn McGrath <bug1@iinet.net.au>
+
+ Common unarchving code and unarchiving applets, ifupdown, ftpgetput,
+ nameif, sed, patch, fold, install, uudecode.
+ Various bugfixes, review and apply numerous patches.
+
+=for html <br>
+
+Manuel Novoa III <mjn3@codepoet.org>
+
+ cat, head, mkfifo, mknod, rmdir, sleep, tee, tty, uniq, usleep, wc, yes,
+ mesg, vconfig, make_directory, parse_mode, dirname, mode_string,
+ get_last_path_component, simplify_path, and a number trivial libbb routines
+
+ also bug fixes, partial rewrites, and size optimizations in
+ ash, basename, cal, cmp, cp, df, du, echo, env, ln, logname, md5sum, mkdir,
+ mv, realpath, rm, sort, tail, touch, uname, watch, arith, human_readable,
+ interface, dutmp, ifconfig, route
+
+=for html <br>
+
+Vladimir Oleynik <dzo@simtreas.ru>
+
+ cmdedit; xargs(current), httpd(current);
+ ports: ash, crond, fdisk, inetd, stty, traceroute, top;
+ locale, various fixes
+ and irreconcilable critic of everything not perfect.
+
+=for html <br>
+
+Bruce Perens <bruce@pixar.com>
+
+ Original author of BusyBox in 1995, 1996. Some of his code can
+ still be found hiding here and there...
+
+=for html <br>
+
+Tim Riker <Tim@Rikers.org>
+
+ bug fixes, member of fan club
+
+=for html <br>
+
+Kent Robotti <robotti@metconnect.com>
+
+ reset, tons and tons of bug reports and patches.
+
+=for html <br>
+
+Chip Rosenthal <chip@unicom.com>, <crosenth@covad.com>
+
+ wget - Contributed by permission of Covad Communications
+
+=for html <br>
+
+Pavel Roskin <proski@gnu.org>
+
+ Lots of bugs fixes and patches.
+
+=for html <br>
+
+Gyepi Sam <gyepi@praxis-sw.com>
+
+ Remote logging feature for syslogd
+
+=for html <br>
+
+Linus Torvalds <torvalds@transmeta.com>
+
+ mkswap, fsck.minix, mkfs.minix
+
+=for html <br>
+
+Mark Whitley <markw@codepoet.org>
+
+ grep, sed, cut, xargs(previous),
+ style-guide, new-applet-HOWTO, bug fixes, etc.
+
+=for html <br>
+
+Charles P. Wright <cpwright@villagenet.com>
+
+ gzip, mini-netcat(nc)
+
+=for html <br>
+
+Enrique Zanardi <ezanardi@ull.es>
+
+ tarcat (since removed), loadkmap, various fixes, Debian maintenance
+
+=for html <br>
+
+Tito Ragusa <farmatito@tiscali.it>
+
+ devfsd and size optimizations in strings, openvt and deallocvt.
+
+=cut
+
+# $LastChangedDate: 2007-01-22 18:12:56 +0100 (Пнд, 22 Янв 2007) $
+
diff --git a/i/pc104/initrd/conf/busybox/docs/busybox_header.pod b/i/pc104/initrd/conf/busybox/docs/busybox_header.pod
new file mode 100644
index 0000000..804b839
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/docs/busybox_header.pod
@@ -0,0 +1,82 @@
+# vi: set sw=4 ts=4:
+
+=head1 NAME
+
+BusyBox - The Swiss Army Knife of Embedded Linux
+
+=head1 SYNTAX
+
+ BusyBox <function> [arguments...] # or
+
+ <function> [arguments...] # if symlinked
+
+=head1 DESCRIPTION
+
+BusyBox combines tiny versions of many common UNIX utilities into a single
+small executable. It provides minimalist replacements for most of the utilities
+you usually find in GNU coreutils, util-linux, etc. The utilities in BusyBox
+generally have fewer options than their full-featured GNU cousins; however, the
+options that are included provide the expected functionality and behave very
+much like their GNU counterparts.
+
+BusyBox has been written with size-optimization and limited resources in mind.
+It is also extremely modular so you can easily include or exclude commands (or
+features) at compile time. This makes it easy to customize your embedded
+systems. To create a working system, just add /dev, /etc, and a Linux kernel.
+BusyBox provides a fairly complete POSIX environment for any small or embedded
+system.
+
+BusyBox is extremely configurable. This allows you to include only the
+components you need, thereby reducing binary size. Run 'make config' or 'make
+menuconfig' to select the functionality that you wish to enable. Then run
+'make' to compile BusyBox using your configuration.
+
+After the compile has finished, you should use 'make install' to install
+BusyBox. This will install the 'bin/busybox' binary, in the target directory
+specified by CONFIG_PREFIX. CONFIG_PREFIX can be set when configuring BusyBox,
+or you can specify an alternative location at install time (i.e., with a
+command line like 'make CONFIG_PREFIX=/tmp/foo install'). If you enabled
+any applet installation scheme (either as symlinks or hardlinks), these will
+also be installed in the location pointed to by CONFIG_PREFIX.
+
+=head1 USAGE
+
+BusyBox is a multi-call binary. A multi-call binary is an executable program
+that performs the same job as more than one utility program. That means there
+is just a single BusyBox binary, but that single binary acts like a large
+number of utilities. This allows BusyBox to be smaller since all the built-in
+utility programs (we call them applets) can share code for many common operations.
+
+You can also invoke BusyBox by issuing a command as an argument on the
+command line. For example, entering
+
+ /bin/busybox ls
+
+will also cause BusyBox to behave as 'ls'.
+
+Of course, adding '/bin/busybox' into every command would be painful. So most
+people will invoke BusyBox using links to the BusyBox binary.
+
+For example, entering
+
+ ln -s /bin/busybox ls
+ ./ls
+
+will cause BusyBox to behave as 'ls' (if the 'ls' command has been compiled
+into BusyBox). Generally speaking, you should never need to make all these
+links yourself, as the BusyBox build system will do this for you when you run
+the 'make install' command.
+
+If you invoke BusyBox with no arguments, it will provide you with a list of the
+applets that have been compiled into your BusyBox binary.
+
+=head1 COMMON OPTIONS
+
+Most BusyBox commands support the B<--help> argument to provide a terse runtime
+description of their behavior. If the CONFIG_FEATURE_VERBOSE_USAGE option has
+been enabled, more detailed usage information will also be available.
+
+=head1 COMMANDS
+
+Currently defined functions include:
+
diff --git a/i/pc104/initrd/conf/busybox/docs/cgi/cl.html b/i/pc104/initrd/conf/busybox/docs/cgi/cl.html
new file mode 100644
index 0000000..5779d62
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/docs/cgi/cl.html
@@ -0,0 +1,46 @@
+<html><head><title>CGI Command line options</title></head><body><h1><img alt="" src="cl_files/CGIlogo.gif"> CGI Command line options</h1>
+<hr> <p>
+
+</p><h2>Specification</h2>
+
+The command line is only used in the case of an ISINDEX query. It is
+not used in the case of an HTML form or any as yet undefined query
+type. The server should search the query information (the <code>QUERY_STRING</code> environment variable) for a non-encoded
+= character to determine if the command line is to be used, if it
+finds one, the command line is not to be used. This trusts the clients
+to encode the = sign in ISINDEX queries, a practice which was
+considered safe at the time of the design of this specification. <p>
+
+For example, use the <a href="http://hoohoo.ncsa.uiuc.edu/cgi-bin/finger">finger script</a> and the ISINDEX interface to look up "httpd". You will see that the script will call itself with <code>/cgi-bin/finger?httpd</code> and will actually execute "finger httpd" on the command line and output the results to you.
+</p><p>
+If the server does find a "=" in the <code>QUERY_STRING</code>,
+then the command line will not be used, and no decoding will be
+performed. The query then remains intact for processing by an
+appropriate FORM submission decoder.
+Again, as an example, use <a href="http://hoohoo.ncsa.uiuc.edu/cgi-bin/finger?httpd=name">this hyperlink</a> to submit <code>"httpd=name"</code> to the finger script. Since this <code>QUERY_STRING</code>
+contained an unencoded "=", nothing was decoded, the script didn't know
+it was being submitted a valid query, and just gave you the default
+finger form.
+</p><p>
+If the server finds that it cannot send the string due to internal
+limitations (such as exec() or /bin/sh command line restrictions) the
+server should include NO command line information and provide the
+non-decoded query information in the environment
+variable <a href="http://hoohoo.ncsa.uiuc.edu/cgi/env.html#query"><code>QUERY_STRING</code></a>. </p><p>
+</p><hr>
+<h2>Examples</h2>
+
+Examples of the command line usage are much better <a href="http://hoohoo.ncsa.uiuc.edu/cgi/examples.html">demonstrated</a> than explained. For these
+examples, pay close attention to the script output which says what
+argc and argv are. <p>
+
+</p><hr>
+
+<a href="http://hoohoo.ncsa.uiuc.edu/cgi/interface.html"><img alt="[Back]" src="cl_files/back.gif">Return to the
+interface specification</a> <p>
+
+CGI - Common Gateway Interface
+</p><address><a href="http://hoohoo.ncsa.uiuc.edu/cgi/mailtocgi.html">cgi@ncsa.uiuc.edu</a></address>
+
+
+</body></html> \ No newline at end of file
diff --git a/i/pc104/initrd/conf/busybox/docs/cgi/env.html b/i/pc104/initrd/conf/busybox/docs/cgi/env.html
new file mode 100644
index 0000000..924026b
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/docs/cgi/env.html
@@ -0,0 +1,149 @@
+<html><head><title>CGI Environment Variables</title></head><body><h1><img alt="" src="env_files/CGIlogo.gif"> CGI Environment Variables</h1>
+<hr>
+
+<p>
+
+In order to pass data about the information request from the server to
+the script, the server uses command line arguments as well as
+environment variables. These environment variables are set when the
+server executes the gateway program. </p><p>
+
+</p><hr>
+<h2>Specification</h2>
+
+ <p>
+The following environment variables are not request-specific and are
+set for all requests: </p><p>
+
+</p><ul>
+<li> <code>SERVER_SOFTWARE</code> <p>
+
+ The name and version of the information server software answering
+ the request (and running the gateway). Format: name/version </p><p>
+
+</p></li><li> <code>SERVER_NAME</code> <p>
+ The server's hostname, DNS alias, or IP address as it would appear
+ in self-referencing URLs. </p><p>
+
+</p></li><li> <code>GATEWAY_INTERFACE</code> <p>
+ The revision of the CGI specification to which this server
+ complies. Format: CGI/revision</p><p>
+
+</p></li></ul>
+
+<hr>
+
+The following environment variables are specific to the request being
+fulfilled by the gateway program: <p>
+
+</p><ul>
+<li> <a name="protocol"><code>SERVER_PROTOCOL</code></a> <p>
+ The name and revision of the information protcol this request came
+ in with. Format: protocol/revision </p><p>
+
+</p></li><li> <code>SERVER_PORT</code> <p>
+ The port number to which the request was sent. </p><p>
+
+</p></li><li> <code>REQUEST_METHOD</code> <p>
+ The method with which the request was made. For HTTP, this is
+ "GET", "HEAD", "POST", etc. </p><p>
+
+</p></li><li> <code>PATH_INFO</code> <p>
+ The extra path information, as given by the client. In other
+ words, scripts can be accessed by their virtual pathname, followed
+ by extra information at the end of this path. The extra
+ information is sent as PATH_INFO. This information should be
+ decoded by the server if it comes from a URL before it is passed
+ to the CGI script.</p><p>
+
+</p></li><li> <code>PATH_TRANSLATED</code> <p>
+ The server provides a translated version of PATH_INFO, which takes
+ the path and does any virtual-to-physical mapping to it. </p><p>
+
+</p></li><li> <code>SCRIPT_NAME</code> <p>
+ A virtual path to the script being executed, used for
+ self-referencing URLs. </p><p>
+
+</p></li><li> <a name="query"><code>QUERY_STRING</code></a> <p>
+ The information which follows the ? in the <a href="http://www.ncsa.uiuc.edu/demoweb/url-primer.html">URL</a>
+ which referenced this script. This is the query information. It
+ should not be decoded in any fashion. This variable should always
+ be set when there is query information, regardless of <a href="http://hoohoo.ncsa.uiuc.edu/cgi/cl.html">command line decoding</a>. </p><p>
+
+</p></li><li> <code>REMOTE_HOST</code> <p>
+ The hostname making the request. If the server does not have this
+ information, it should set REMOTE_ADDR and leave this unset.</p><p>
+
+</p></li><li> <code>REMOTE_ADDR</code> <p>
+ The IP address of the remote host making the request. </p><p>
+
+</p></li><li> <code>AUTH_TYPE</code> <p>
+ If the server supports user authentication, and the script is
+ protects, this is the protocol-specific authentication method used
+ to validate the user. </p><p>
+
+</p></li><li> <code>REMOTE_USER</code> <p>
+ If the server supports user authentication, and the script is
+ protected, this is the username they have authenticated as. </p><p>
+</p></li><li> <code>REMOTE_IDENT</code> <p>
+ If the HTTP server supports RFC 931 identification, then this
+ variable will be set to the remote user name retrieved from the
+ server. Usage of this variable should be limited to logging only.
+ </p><p>
+
+</p></li><li> <a name="ct"><code>CONTENT_TYPE</code></a> <p>
+ For queries which have attached information, such as HTTP POST and
+ PUT, this is the content type of the data. </p><p>
+
+</p></li><li> <a name="cl"><code>CONTENT_LENGTH</code></a> <p>
+ The length of the said content as given by the client. </p><p>
+
+</p></li></ul>
+
+
+<a name="headers"><hr></a>
+
+In addition to these, the header lines received from the client, if
+any, are placed into the environment with the prefix HTTP_ followed by
+the header name. Any - characters in the header name are changed to _
+characters. The server may exclude any headers which it has already
+processed, such as Authorization, Content-type, and Content-length. If
+necessary, the server may choose to exclude any or all of these
+headers if including them would exceed any system environment
+limits. <p>
+
+An example of this is the HTTP_ACCEPT variable which was defined in
+CGI/1.0. Another example is the header User-Agent.</p><p>
+
+</p><ul>
+<li> <code>HTTP_ACCEPT</code> <p>
+ The MIME types which the client will accept, as given by HTTP
+ headers. Other protocols may need to get this information from
+ elsewhere. Each item in this list should be separated by commas as
+ per the HTTP spec. </p><p>
+
+ Format: type/subtype, type/subtype </p><p>
+
+
+</p></li><li> <code>HTTP_USER_AGENT</code><p>
+
+ The browser the client is using to send the request. General
+format: <code>software/version library/version</code>.</p><p>
+
+</p></li></ul>
+
+<hr>
+<h2>Examples</h2>
+
+Examples of the setting of environment variables are really much better
+<a href="http://hoohoo.ncsa.uiuc.edu/cgi/examples.html">demonstrated</a> than explained. <p>
+
+</p><hr>
+
+<a href="http://hoohoo.ncsa.uiuc.edu/cgi/interface.html"><img alt="[Back]" src="env_files/back.gif">Return to the
+interface specification</a> <p>
+
+CGI - Common Gateway Interface
+</p><address><a href="http://hoohoo.ncsa.uiuc.edu/cgi/mailtocgi.html">cgi@ncsa.uiuc.edu</a></address>
+
+</body></html> \ No newline at end of file
diff --git a/i/pc104/initrd/conf/busybox/docs/cgi/in.html b/i/pc104/initrd/conf/busybox/docs/cgi/in.html
new file mode 100644
index 0000000..679306a
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/docs/cgi/in.html
@@ -0,0 +1,33 @@
+<html><head><title>CGI Script input</title></head><body><h1><img alt="" src="in_files/CGIlogo.gif"> CGI Script Input</h1>
+<hr>
+
+<h2>Specification</h2>
+
+For requests which have information attached after the header, such as
+HTTP POST or PUT, the information will be sent to the script on stdin.
+<p>
+
+The server will send <a href="http://hoohoo.ncsa.uiuc.edu/cgi/env.html#cl">CONTENT_LENGTH</a> bytes on
+this file descriptor. Remember that it will give the <a href="http://hoohoo.ncsa.uiuc.edu/cgi/env.html#ct">CONTENT_TYPE</a> of the data as well. The server is
+in no way obligated to send end-of-file after the script reads
+<code>CONTENT_LENGTH</code> bytes. </p><p>
+</p><hr>
+<h2>Example</h2>
+
+Let's take a form with METHOD="POST" as an example. Let's say the form
+results are 7 bytes encoded, and look like <code>a=b&amp;b=c</code>.
+<p>
+
+In this case, the server will set CONTENT_LENGTH to 7 and CONTENT_TYPE
+to application/x-www-form-urlencoded. The first byte on the script's
+standard input will be "a", followed by the rest of the encoded string.</p><p>
+
+</p><hr>
+
+<a href="http://hoohoo.ncsa.uiuc.edu/cgi/interface.html"><img alt="[Back]" src="in_files/back.gif">Return to the
+interface specification</a> <p>
+
+CGI - Common Gateway Interface
+</p><address><a href="http://hoohoo.ncsa.uiuc.edu/cgi/mailtocgi.html">cgi@ncsa.uiuc.edu</a></address>
+
+</body></html> \ No newline at end of file
diff --git a/i/pc104/initrd/conf/busybox/docs/cgi/interface.html b/i/pc104/initrd/conf/busybox/docs/cgi/interface.html
new file mode 100644
index 0000000..ea73ce3
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/docs/cgi/interface.html
@@ -0,0 +1,29 @@
+<html><head><title>The Common Gateway Interface Specification
+[http://hoohoo.ncsa.uiuc.edu/cgi/interface.html]
+</title></head><body><h1><img alt="" src="interface_files/CGIlogo.gif"> The CGI Specification</h1>
+
+<hr>
+
+This is the specification for CGI version 1.1, or CGI/1.1. Further
+revisions of this protocol are guaranteed to be backward compatible.
+<p>
+
+The server and the CGI script communicate in four major ways. Each of
+the following is a hotlink to graphic detail.</p><p>
+
+</p><ul>
+<li> <a href="env.html">Environment variables</a>
+</li><li> <a href="cl.html">The command line</a>
+</li><li> <a href="in.html">Standard input</a>
+</li><li> <a href="out.html">Standard output</a>
+</li></ul>
+<hr>
+
+
+<a href="http://hoohoo.ncsa.uiuc.edu/cgi/overview.html"><img alt="[Back]" src="interface_files/back.gif">Return to the overview</a> <p>
+
+
+
+CGI - Common Gateway Interface
+</p><address><a href="http://hoohoo.ncsa.uiuc.edu/cgi/mailtocgi.html">cgi@ncsa.uiuc.edu</a></address>
+</body></html> \ No newline at end of file
diff --git a/i/pc104/initrd/conf/busybox/docs/cgi/out.html b/i/pc104/initrd/conf/busybox/docs/cgi/out.html
new file mode 100644
index 0000000..2203ee5
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/docs/cgi/out.html
@@ -0,0 +1,126 @@
+<html><head><title>CGI Script output</title></head><body><h1><img alt="" src="out_files/CGIlogo.gif"> CGI Script Output</h1>
+<hr>
+
+<h2>Script output</h2>
+
+The script sends its output to stdout. This output can either be a
+document generated by the script, or instructions to the server for
+retrieving the desired output. <p>
+</p><hr>
+
+<h2>Script naming conventions</h2>
+
+Normally, scripts produce output which is interpreted and sent back to
+the client. An advantage of this is that the scripts do not need to
+send a full HTTP/1.0 header for every request. <p>
+<a name="nph">
+Some scripts may want to avoid the extra overhead of the server
+parsing their output, and talk directly to the client. In order to
+distinguish these scripts from the other scripts, CGI requires that
+the script name begins with nph- if a script does not want the server
+to parse its header. In this case, it is the script's responsibility
+to return a valid HTTP/1.0 (or HTTP/0.9) response to the client. </a></p><p>
+
+</p><hr>
+<h2><a name="nph">Parsed headers</a></h2>
+
+<a name="nph">The output of scripts begins with a small header. This header consists
+of text lines, in the same format as an </a><a href="http://www.w3.org/hypertext/WWW/Protocols/HTTP/Object_Headers.html">
+HTTP header</a>, terminated by a blank line (a line with only a
+linefeed or CR/LF). <p>
+
+Any headers which are not server directives are sent directly back to
+the client. Currently, this specification defines three server
+directives:</p><p>
+
+</p><ul>
+<li> <code>Content-type</code> <p>
+
+ This is the MIME type of the document you are returning. </p><p>
+
+</p></li><li> <code>Location</code> <p>
+
+ This is used to specify to the server that you are returning a
+ reference to a document rather than an actual document. </p><p>
+
+ If the argument to this is a URL, the server will issue a redirect
+ to the client. </p><p>
+
+ If the argument to this is a virtual path, the server will
+ retrieve the document specified as if the client had requested
+ that document originally. ? directives will work in here, but #
+ directives must be redirected back to the client.</p><p>
+
+
+</p></li><li> <a name="status"><code>Status</code></a><p>
+
+ This is used to give the server an HTTP/1.0 <a href="http://www.w3.org/hypertext/WWW/Protocols/HTTP/HTRESP.html">status
+line</a> to send to the client. The format is <code>nnn xxxxx</code>,
+where <code>nnn</code> is the 3-digit status code, and
+<code>xxxxx</code> is the reason string, such as "Forbidden".</p><p>
+
+</p></li></ul>
+
+<hr>
+<h2>Examples</h2>
+
+Let's say I have a fromgratz to HTML converter. When my converter is
+finished with its work, it will output the following on stdout (note
+that the lines beginning and ending with --- are just for illustration
+and would not be output): <p>
+
+</p><pre>--- start of output ---
+Content-type: text/html
+
+--- end of output ---
+</pre>
+
+Note the blank line after Content-type. <p>
+
+Now, let's say I have a script which, in certain instances, wants to
+return the document <code>/path/doc.txt</code> from this server just
+as if the user had actually requested
+<code>http://server:port/path/doc.txt</code> to begin with. In this
+case, the script would output: </p><p>
+</p><pre>--- start of output ---
+Location: /path/doc.txt
+
+--- end of output ---
+</pre>
+
+The server would then perform the request and send it to the client.
+<p>
+
+Let's say that I have a script which wants to reference our gopher
+server. In this case, if the script wanted to refer the user to
+<code>gopher://gopher.ncsa.uiuc.edu/</code>, it would output: </p><p>
+
+</p><pre>--- start of output ---
+Location: gopher://gopher.ncsa.uiuc.edu/
+
+--- end of output ---
+</pre>
+
+Finally, I have a script which wants to talk to the client directly.
+In this case, if the script is referenced with <a href="http://hoohoo.ncsa.uiuc.edu/cgi/env.html#protocol"><code>SERVER_PROTOCOL</code></a> of HTTP/1.0,
+the script would output the following HTTP/1.0 response: <p>
+
+</p><pre>--- start of output ---
+HTTP/1.0 200 OK
+Server: NCSA/1.0a6
+Content-type: text/plain
+
+This is a plaintext document generated on the fly just for you.
+
+--- end of output ---
+</pre>
+
+
+<hr>
+
+<a href="http://hoohoo.ncsa.uiuc.edu/cgi/interface.html"><img alt="[Back]" src="out_files/back.gif">Return to the
+interface specification</a> <p>
+
+CGI - Common Gateway Interface
+</p><address><a href="http://hoohoo.ncsa.uiuc.edu/cgi/mailtocgi.html">cgi@ncsa.uiuc.edu</a></address>
+</body></html> \ No newline at end of file
diff --git a/i/pc104/initrd/conf/busybox/docs/contributing.txt b/i/pc104/initrd/conf/busybox/docs/contributing.txt
new file mode 100644
index 0000000..aad4303
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/docs/contributing.txt
@@ -0,0 +1,449 @@
+Contributing To Busybox
+=======================
+
+This document describes what you need to do to contribute to Busybox, where
+you can help, guidelines on testing, and how to submit a well-formed patch
+that is more likely to be accepted.
+
+The Busybox home page is at: http://busybox.net/
+
+
+
+Pre-Contribution Checklist
+--------------------------
+
+So you want to contribute to Busybox, eh? Great, wonderful, glad you want to
+help. However, before you dive in, headlong and hotfoot, there are some things
+you need to do:
+
+
+Checkout the Latest Code from CVS
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This is a necessary first step. Please do not try to work with the last
+released version, as there is a good chance that somebody has already fixed
+the bug you found. Somebody might have even added the feature you had in mind.
+Don't make your work obsolete before you start!
+
+For information on how to check out Busybox from CVS, please look at the
+following links:
+
+ http://busybox.net/cvs_anon.html
+ http://busybox.net/cvs_howto.html
+
+
+Read the Mailing List
+~~~~~~~~~~~~~~~~~~~~~
+
+No one is required to read the entire archives of the mailing list, but you
+should at least read up on what people have been talking about lately. If
+you've recently discovered a problem, chances are somebody else has too. If
+you're the first to discover a problem, post a message and let the rest of us
+know.
+
+Archives can be found here:
+
+ http://busybox.net/lists/busybox/
+
+If you have a serious interest in Busybox, i.e., you are using it day-to-day or
+as part of an embedded project, it would be a good idea to join the mailing
+list.
+
+A web-based sign-up form can be found here:
+
+ http://busybox.net/mailman/listinfo/busybox
+
+
+Coordinate with the Applet Maintainer
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Some (not all) of the applets in Busybox are "owned" by a maintainer who has
+put significant effort into it and is probably more familiar with it than
+others. To find the maintainer of an applet, look at the top of the .c file
+for a name following the word 'Copyright' or 'Written by' or 'Maintainer'.
+
+Before plunging ahead, it's a good idea to send a message to the mailing list
+that says: "Hey, I was thinking about adding the 'transmogrify' feature to the
+'foo' applet. Would this be useful? Is anyone else working on it?" You might
+want to CC the maintainer (if any) with your question.
+
+
+
+Areas Where You Can Help
+------------------------
+
+Busybox can always use improvement! If you're looking for ways to help, there
+are a variety of areas where you could help.
+
+
+What Busybox Doesn't Need
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Before listing the areas where you _can_ help, it's worthwhile to mention the
+areas where you shouldn't bother. While Busybox strives to be the "Swiss Army
+Knife" of embedded Linux, there are some applets that will not be accepted:
+
+ - Any filesystem manipulation tools: Busybox is filesystem independent and
+ we do not want to start adding mkfs/fsck tools for every (or any)
+ filesystem under the sun. (fsck_minix.c and mkfs_minix.c are living on
+ borrowed time.) There are far too many of these tools out there. Use
+ the upstream version. Not everything has to be part of Busybox.
+
+ - Any partitioning tools: Partitioning a device is typically done once and
+ only once, and tools which do this generally do not need to reside on the
+ target device (esp a flash device). If you need a partitioning tool, grab
+ one (such as fdisk, sfdisk, or cfdisk from util-linux) and use that, but
+ don't try to merge it into busybox. These are nasty and complex and we
+ don't want to maintain them.
+
+ - Any disk, device, or media-specific tools: Use the -utils or -tools package
+ that was designed for your device; don't try to shoehorn them into Busybox.
+
+ - Any architecture specific tools: Busybox is (or should be) architecture
+ independent. Do not send us tools that cannot be used across multiple
+ platforms / arches.
+
+ - Any daemons that are not essential to basic system operation. To date, only
+ syslogd and klogd meet this requirement. We do not need a web server, an
+ ftp daemon, a dhcp server, a mail transport agent or a dns resolver. If you
+ need one of those, you are welcome to ask the folks on the mailing list for
+ recommendations, but please don't bloat up Busybox with any of these.
+
+
+Bug Reporting
+~~~~~~~~~~~~~
+
+If you find bugs, please submit a detailed bug report to the busybox mailing
+list at busybox@busybox.net. A well-written bug report should include a
+transcript of a shell session that demonstrates the bad behavior and enables
+anyone else to duplicate the bug on their own machine. The following is such
+an example:
+
+ To: busybox@busybox.net
+ From: diligent@testing.linux.org
+ Subject: /bin/date doesn't work
+
+ Package: busybox
+ Version: 1.00
+
+ When I execute Busybox 'date' it produces unexpected results.
+ With GNU date I get the following output:
+
+ $ date
+ Wed Mar 21 14:19:41 MST 2001
+
+ But when I use BusyBox date I get this instead:
+
+ $ date
+ llegal instruction
+
+ I am using Debian unstable, kernel version 2.4.19-rmk1 on an Netwinder,
+ and the latest uClibc from CVS. Thanks for the wonderful program!
+
+ -Diligent
+
+Note the careful description and use of examples showing not only what BusyBox
+does, but also a counter example showing what an equivalent GNU app does. Bug
+reports lacking such detail may never be fixed... Thanks for understanding.
+
+
+
+Write Documentation
+~~~~~~~~~~~~~~~~~~~
+
+Chances are, documentation in Busybox is either missing or needs improvement.
+Either way, help is welcome.
+
+Work is being done to automatically generate documentation from sources,
+especially from the usage.h file. If you want to correct the documentation,
+please make changes to the pre-generation parts, rather than the generated
+documentation. [More to come on this later...]
+
+It is preferred that modifications to documentation be submitted in patch
+format (more on this below), but we're a little more lenient when it comes to
+docs. You could, for example, just say "after the listing of the mount
+options, the following example would be helpful..."
+
+
+Consult Existing Sources
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+For a quick listing of "needs work" spots in the sources, cd into the Busybox
+directory and run the following:
+
+ for i in TODO FIXME XXX; do find -name '*.[ch]'|xargs grep $i; done
+
+This will show all of the trouble spots or 'questionable' code. Pick a spot,
+any spot, these are all invitations for you to contribute.
+
+
+Add a New Applet
+~~~~~~~~~~~~~~~~
+
+If you want to add a new applet to Busybox, we'd love to see it. However,
+before you write any code, please ask beforehand on the mailing list something
+like "Do you think applet 'foo' would be useful in Busybox?" or "Would you
+guys accept applet 'foo' into Busybox if I were to write it?" If the answer is
+"no" by the folks on the mailing list, then you've saved yourself some time.
+Conversely, you could get some positive responses from folks who might be
+interested in helping you implement it, or can recommend the best approach.
+Perhaps most importantly, this is your way of calling "dibs" on something and
+avoiding duplication of effort.
+
+Also, before you write a line of code, please read the 'new-applet-HOWTO.txt'
+file in the docs/ directory.
+
+
+Janitorial Work
+~~~~~~~~~~~~~~~
+
+These are dirty jobs, but somebody's gotta do 'em.
+
+ - Converting applets to use getopt() for option processing. Type 'find -name
+ '*.c'|grep -L getopt' to get a listing of the applets that currently don't
+ use getopt. If a .c file processes no options, it should have a line that
+ reads: /* no options, no getopt */ somewhere in the file.
+
+ - Replace any "naked" calls to malloc, calloc, realloc, str[n]dup, fopen with
+ the x* equivalents found in libbb/xfuncs.c.
+
+ - Security audits:
+ http://www.securityfocus.com/popups/forums/secprog/intro.shtml
+
+ - Synthetic code removal: http://www.perl.com/pub/2000/06/commify.html - This
+ is very Perl-specific, but the advice given in here applies equally well to
+ C.
+
+ - C library function use audits: Verifying that functions are being used
+ properly (called with the right args), replacing unsafe library functions
+ with safer versions, making sure return codes are being checked, etc.
+
+ - Where appropriate, replace preprocessor defined macros and values with
+ compile-time equivalents.
+
+ - Style guide compliance. See: docs/style-guide.txt
+
+ - Add testcases to tests/testcases.
+
+ - Makefile improvements:
+ http://www.canb.auug.org.au/~millerp/rmch/recu-make-cons-harm.html
+ (I think the recursive problems are pretty much taken care of at this point, non?)
+
+ - "Ten Commandments" compliance: (this is a "maybe", certainly not as
+ important as any of the previous items.)
+ http://www.lysator.liu.se/c/ten-commandments.html
+
+Other useful links:
+
+ - the comp.lang.c FAQ: http://web.onetelnet.ch/~twolf/tw/c/index.html#Sources
+
+
+
+Submitting Patches To Busybox
+-----------------------------
+
+Here are some guidelines on how to submit a patch to Busybox.
+
+
+Making A Patch
+~~~~~~~~~~~~~~
+
+If you've got anonymous CVS access set up, making a patch is simple. Just make
+sure you're in the busybox/ directory and type 'cvs diff -bwu > mychanges.patch'.
+You can send the resulting .patch file to the mailing list with a description
+of what it does. (But not before you test it! See the next section for some
+guidelines.) It is preferred that patches be sent as attachments, but it is
+not required.
+
+Also, feel free to help test other people's patches and reply to them with
+comments. You can apply a patch by saving it into your busybox/ directory and
+typing 'patch < mychanges.patch'. Then you can recompile, see if it runs, test
+if it works as advertised, and post your findings to the mailing list.
+
+NOTE: Please do not include extraneous or irrelevant changes in your patches.
+Please do not try to "bundle" two patches together into one. Make single,
+discreet changes on a per-patch basis. Sometimes you need to make a patch that
+touches code in many places, but these kind of patches are rare and should be
+coordinated with a maintainer.
+
+
+Testing Guidelines
+~~~~~~~~~~~~~~~~~~
+
+It's considered good form to test your new feature before you submit a patch
+to the mailing list, and especially before you commit a change to CVS. Here
+are some guidelines on how to test your changes.
+
+ - Always test Busybox applets against GNU counterparts and make sure the
+ behavior / output is identical between the two.
+
+ - Try several different permutations and combinations of the features you're
+ adding (i.e., different combinations of command-line switches) and make sure
+ they all work; make sure one feature does not interfere with another.
+
+ - Make sure you test compiling against the source both with the feature
+ turned on and turned off in Config.h and make sure Busybox compiles cleanly
+ both ways.
+
+ - Run the multibuild.pl script in the tests directory and make sure
+ everything checks out OK. (Do this from within the busybox/ directory by
+ typing: 'tests/multibuild.pl'.)
+
+
+Making Sure Your Patch Doesn't Get Lost
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If you don't want your patch to be lost or forgotten, send it to the busybox
+mailing list with a subject line something like this:
+
+ [PATCH] - Adds "transmogrify" feature to "foo"
+
+In the body, you should have a pseudo-header that looks like the following:
+
+ Package: busybox
+ Version: v1.01pre (or whatever the current version is)
+ Severity: wishlist
+
+The remainder of the body should read along these lines:
+
+ This patch adds the "transmogrify" feature to the "foo" applet. I have
+ tested this on [arch] system(s) and it works. I have tested it against the
+ GNU counterparts and the outputs are identical. I have run the scripts in
+ the 'tests' directory and nothing breaks.
+
+
+
+Improving Your Chances of Patch Acceptance
+------------------------------------------
+
+Even after you send a brilliant patch to the mailing list, sometimes it can go
+unnoticed, un-replied-to, and sometimes (sigh) even lost. This is an
+unfortunate fact of life, but there are steps you can take to help your patch
+get noticed and convince a maintainer that it should be added:
+
+
+Be Succinct
+~~~~~~~~~~~
+
+A patch that includes small, isolated, obvious changes is more likely to be
+accepted than a patch that touches code in lots of different places or makes
+sweeping, dubious changes.
+
+
+Back It Up
+~~~~~~~~~~
+
+Hard facts on why your patch is better than the existing code will go a long
+way toward convincing maintainers that your patch should be included.
+Specifically, patches are more likely to be accepted if they are provably more
+correct, smaller, faster, simpler, or more maintainable than the existing
+code.
+
+Conversely, any patch that is supported with nothing more than "I think this
+would be cool" or "this patch is good because I say it is and I've got a Phd
+in Computer Science" will likely be ignored.
+
+
+Follow The Style Guide
+~~~~~~~~~~~~~~~~~~~~~~
+
+It's considered good form to abide by the established coding style used in a
+project; Busybox is no exception. We have gone so far as to delineate the
+"elements of Busybox style" in the file docs/style-guide.txt. Please follow
+them.
+
+
+Work With Someone Else
+~~~~~~~~~~~~~~~~~~~~~~
+
+Working on a patch in isolation is less effective than working with someone
+else for a variety of reasons. If another Busybox user is interested in what
+you're doing, then it's two (or more) voices instead of one that can petition
+for inclusion of the patch. You'll also have more people that can test your
+changes, or even offer suggestions on better approaches you could take.
+
+Getting other folks interested follows as a natural course if you've received
+responses from queries to applet maintainer or positive responses from folks
+on the mailing list.
+
+We've made strident efforts to put a useful "collaboration" infrastructure in
+place in the form of mailing lists, the bug tracking system, and CVS. Please
+use these resources.
+
+
+Send Patches to the Bug Tracking System
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This was mentioned above in the "Making Sure Your Patch Doesn't Get Lost"
+section, but it is worth mentioning again. A patch sent to the mailing list
+might be unnoticed and forgotten. A patch sent to the bug tracking system will
+be stored and closely connected to the bug it fixes.
+
+
+Be Polite
+~~~~~~~~~
+
+The old saying "You'll catch more flies with honey than you will with vinegar"
+applies when submitting patches to the mailing list for approval. The way you
+present your patch is sometimes just as important as the actual patch itself
+(if not more so). Being rude to the maintainers is not an effective way to
+convince them that your patch should be included; it will likely have the
+opposite effect.
+
+
+
+Committing Changes to CVS
+-------------------------
+
+If you submit several patches that demonstrate that you are a skilled and wise
+coder, you may be invited to become a committer, thus enabling you to commit
+changes directly to CVS. This is nice because you don't have to wait for
+someone else to commit your change for you, you can just do it yourself.
+
+But note that this is a privilege that comes with some responsibilities. You
+should test your changes before you commit them. You should also talk to an
+applet maintainer before you make any kind of sweeping changes to somebody
+else's code. Big changes should still go to the mailing list first. Remember,
+being wise, polite, and discreet is more important than being clever.
+
+
+When To Commit
+~~~~~~~~~~~~~~
+
+Generally, you should feel free to commit a change if:
+
+ - Your changes are small and don't touch many files
+ - You are fixing a bug
+ - Somebody has told you that it's okay
+ - It's obviously the Right Thing
+
+The more of the above are true, the better it is to just commit a change
+directly to CVS.
+
+
+When Not To Commit
+~~~~~~~~~~~~~~~~~~
+
+Even if you have commit rights, you should probably still post a patch to the
+mailing list if:
+
+ - Your changes are broad and touch many different files
+ - You are adding a feature
+ - Your changes are speculative or experimental (i.e., trying a new algorithm)
+ - You are not the maintainer and your changes make the maintainer cringe
+
+The more of the above are true, the better it is to post a patch to the
+mailing list instead of committing.
+
+
+
+Final Words
+-----------
+
+If all of this seems complicated, don't panic, it's really not that tough. If
+you're having difficulty following some of the steps outlined in this
+document don't worry, the folks on the Busybox mailing list are a fairly
+good-natured bunch and will work with you to help get your patches into shape
+or help you make contributions.
+
+
diff --git a/i/pc104/initrd/conf/busybox/docs/draft-coar-cgi-v11-03-clean.html b/i/pc104/initrd/conf/busybox/docs/draft-coar-cgi-v11-03-clean.html
new file mode 100644
index 0000000..d52c9b8
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/docs/draft-coar-cgi-v11-03-clean.html
@@ -0,0 +1,2674 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"
+ "http://www.w3.org/TR/REC-html40/loose.dtd">
+<HTML>
+ <HEAD>
+ <TITLE>Common Gateway Interface - 1.1 *Draft 03* [http://cgi-spec.golux.com/draft-coar-cgi-v11-03-clean.html]
+ </TITLE>
+<!--#if expr="$HTTP_USER_AGENT != /Lynx/" -->
+ <!--#set var="GUI" value="1" -->
+<!--#endif -->
+ <LINK HREF="mailto:Ken.Coar@Golux.Com" rev="revised">
+ <LINK REL="STYLESHEET" HREF="cgip-style-rfc.css" TYPE="text/css">
+ <META name="latexstyle" content="rfc">
+ <META name="author" content="Ken A L Coar">
+ <META name="institute" content="IBM Corporation">
+ <META name="date" content="25 June 1999">
+ <META name="expires" content="Expires 31 December 1999">
+ <META name="document" content="INTERNET-DRAFT">
+ <META name="file" content="&lt;draft-coar-cgi-v11-03.txt&gt;">
+ <META name="group" content="INTERNET-DRAFT">
+<!--
+ There are a lot of BNF fragments in this document. To make it work
+ in all possible browsers (including Lynx, which is used to turn it
+ into text/plain), we handle these by using PREformatted blocks with
+ a universal internal margin of 2, inside one-level DL blocks.
+ -->
+ </HEAD>
+ <BODY>
+ <!--
+ HTML doesn't do paper pagination, so we need to fake it out. Basing
+ our formatting upon RFC2068, there are four (4) lines of header and
+ four (4) lines of footer for each page.
+
+<DIV ALIGN="CENTER">
+ <PRE>
+
+
+
+
+Coar, et al. CGI/1.1 Specification May, 1998
+INTERNET-DRAFT Expires 1 December 1998 [Page 2]
+
+
+ </PRE>
+</DIV>
+ -->
+ <!--
+ The following weirdness wrt non-breaking spaces is to get Lynx
+ (which is barely TABLE-aware) to line the left/right justified
+ text up properly.
+ -->
+ <DIV ALIGN="CENTER">
+ <TABLE WIDTH="100%" CELLPADDING=0 CELLSPACING=0>
+ <TR VALIGN="TOP">
+ <TD ALIGN="LEFT">
+ INTERNET-DRAFT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ </TD>
+ <TD ALIGN="RIGHT">
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Ken A L Coar
+ </TD>
+ </TR>
+ <TR VALIGN="TOP">
+ <TD ALIGN="LEFT">
+ draft-coar-cgi-v11-03.{html,txt}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ </TD>
+ <TD ALIGN="RIGHT">
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;IBM Corporation
+ </TD>
+ </TR>
+ <TR VALIGN="TOP">
+ <TD ALIGN="LEFT">
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ </TD>
+ <TD ALIGN="RIGHT">
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;D.R.T. Robinson
+ </TD>
+ </TR>
+ <TR VALIGN="TOP">
+ <TD ALIGN="LEFT">
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ </TD>
+ <TD ALIGN="RIGHT">
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;E*TRADE&nbsp;UK&nbsp;Ltd.
+ </TD>
+ </TR>
+ <TR VALIGN="TOP">
+ <TD ALIGN="LEFT">
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ </TD>
+ <TD ALIGN="RIGHT">
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;25 June 1999
+ </TD>
+ </TR>
+ </TABLE>
+ </DIV>
+
+ <H1 ALIGN="CENTER">
+ The WWW Common Gateway Interface
+ <BR>
+ Version 1.1
+ </H1>
+
+<!--#include virtual="I-D-statement" -->
+
+ <H2>
+ <A NAME="Abstract">
+ Abstract
+ </A>
+ </H2>
+ <P>
+ The Common Gateway Interface (CGI) is a simple interface for running
+ external programs, software or gateways under an information server
+ in a platform-independent manner. Currently, the supported information
+ servers are HTTP servers.
+ </P>
+ <P>
+ The interface has been in use by the World-Wide Web since 1993. This
+ specification defines the
+ "current practice" parameters of the
+ 'CGI/1.1' interface developed and documented at the U.S. National
+ Centre for Supercomputing Applications [NCSA-CGI].
+ This document also defines the use of the CGI/1.1 interface
+ on the Unix and AmigaDOS(tm) systems.
+ </P>
+ <P>
+ Discussion of this draft occurs on the CGI-WG mailing list; see the
+ project Web page at
+ <SAMP>&lt;URL:<A HREF="http://CGI-Spec.Golux.Com/"
+ >http://CGI-Spec.Golux.Com/</A>&gt;</SAMP>
+ for details on the mailing list and the status of the project.
+ </P>
+
+<!--#if expr="$GUI" -->
+ <H2>
+ Revision History
+ </H2>
+ <P>
+ The revision history of this draft is being maintained using Web-based
+ GUI notation, such as struck-through characters and colour-coded
+ sections. The following legend describes how to determine the origin
+ of a particular revision according to the colour of the text:
+ </P>
+ <DL COMPACT>
+ <DT>Black
+ </DT>
+ <DD>Revision 00, released 28 May 1998
+ </DD>
+ <DT>Green
+ </DT>
+ <DD>Revision 01, released 28 December 1998
+ <BR>
+ Major structure change: Section 4, "Request Metadata (Meta-Variables)"
+ was moved entirely under <A HREF="#7.0">Section 7</A>, "Data Input to the
+ CGI Script."
+ Due to the size of this change, it is noted here and the text in its
+ former location does <EM>not</EM> appear as struckthrough. This has
+ caused major <A HREF="#6.0">sections 5</A> and following to decrement
+ by one. Other
+ large text movements are likewise not marked up. References to RFC
+ 1738 were changed to 2396 (1738's replacement).
+ </DD>
+ <DT>Red
+ </DT>
+ <DD>Revision 02, released 2 April, 1999
+ <BR>
+ Added text to <A HREF="#8.3">section 8.3</A> defining correct handling
+ of HTTP/1.1
+ requests using "chunked" Transfer-Encoding. Labelled metavariable
+ names in <A HREF="#8.0">section 8</A> with the appropriate detail section
+ numbers.
+ Clarified allowed usage of <SAMP>Status</SAMP> and
+ <SAMP>Location</SAMP> response header fields. Included new
+ Internet-Draft language.
+ </DD>
+ <DT>Fuchsia
+ </DT>
+ <DD>Revision 03, released 25 June 1999
+ <BR>
+ Changed references from "HTTP" to "Protocol-Specific" for the listing of
+ things like HTTP_ACCEPT. Changed 'entity-body' and 'content-body' to
+ 'message-body.' Added a note that response headers must comply with
+ requirements of the protocol level in use. Added a lot of stuff about
+ security (section 11). Clarified a bunch of productions. Pointed out
+ that zero-length and omitted values are indistinguishable in this
+ specification. Clarified production describing order of fields in
+ script response header. Clarified issues surrounding encoding of
+ data. Acknowledged additional contributors, and changed one of
+ the authors' addresses.
+ </DD>
+ </DL>
+<!--#endif -->
+
+ <H2>
+ <A NAME="Contents">
+ Table of Contents
+ </A>
+ </H2>
+ <DIV ALIGN="CENTER">
+ <PRE>
+ 1 Introduction..............................................<A
+ HREF="#1.0"
+ >TBD</A>
+ 1.1 Purpose................................................<A
+ HREF="#1.1"
+ >TBD</A>
+ 1.2 Requirements...........................................<A
+ HREF="#1.2"
+ >TBD</A>
+ 1.3 Specifications.........................................<A
+ HREF="#1.3"
+ >TBD</A>
+ 1.4 Terminology............................................<A
+ HREF="#1.4"
+ >TBD</A>
+ 2 Notational Conventions and Generic Grammar................<A
+ HREF="#2.0"
+ >TBD</A>
+ 2.1 Augmented BNF..........................................<A
+ HREF="#2.1"
+ >TBD</A>
+ 2.2 Basic Rules............................................<A
+ HREF="#2.2"
+ >TBD</A>
+ 3 Protocol Parameters.......................................<A
+ HREF="#3.0"
+ >TBD</A>
+ 3.1 URL Encoding...........................................<A
+ HREF="#3.1"
+ >TBD</A>
+ 3.2 The Script-URI.........................................<A
+ HREF="#3.2"
+ >TBD</A>
+ 4 Invoking the Script.......................................<A
+ HREF="#4.0"
+ >TBD</A>
+ 5 The CGI Script Command Line...............................<A
+ HREF="#5.0"
+ >TBD</A>
+ 6 Data Input to the CGI Script..............................<A
+ HREF="#6.0"
+ >TBD</A>
+ 6.1 Request Metadata (Metavariables).......................<A
+ HREF="#6.1"
+ >TBD</A>
+ 6.1.1 AUTH_TYPE...........................................<A
+ HREF="#6.1.1"
+ >TBD</A>
+ 6.1.2 CONTENT_LENGTH......................................<A
+ HREF="#6.1.2"
+ >TBD</A>
+ 6.1.3 CONTENT_TYPE........................................<A
+ HREF="#6.1.3"
+ >TBD</A>
+ 6.1.4 GATEWAY_INTERFACE...................................<A
+ HREF="#6.1.4"
+ >TBD</A>
+ 6.1.5 Protocol-Specific Metavariables.....................<A
+ HREF="#6.1.5"
+ >TBD</A>
+ 6.1.6 PATH_INFO...........................................<A
+ HREF="#6.1.6"
+ >TBD</A>
+ 6.1.7 PATH_TRANSLATED.....................................<A
+ HREF="#6.1.7"
+ >TBD</A>
+ 6.1.8 QUERY_STRING........................................<A
+ HREF="#6.1.8"
+ >TBD</A>
+ 6.1.9 REMOTE_ADDR.........................................<A
+ HREF="#6.1.9"
+ >TBD</A>
+ 6.1.10 REMOTE_HOST........................................<A
+ HREF="#6.1.10"
+ >TBD</A>
+ 6.1.11 REMOTE_IDENT.......................................<A
+ HREF="#6.1.11"
+ >TBD</A>
+ 6.1.12 REMOTE_USER........................................<A
+ HREF="#6.1.12"
+ >TBD</A>
+ 6.1.13 REQUEST_METHOD.....................................<A
+ HREF="#6.1.13"
+ >TBD</A>
+ 6.1.14 SCRIPT_NAME........................................<A
+ HREF="#6.1.14"
+ >TBD</A>
+ 6.1.15 SERVER_NAME........................................<A
+ HREF="#6.1.15"
+ >TBD</A>
+ 6.1.16 SERVER_PORT........................................<A
+ HREF="#6.1.16"
+ >TBD</A>
+ 6.1.17 SERVER_PROTOCOL....................................<A
+ HREF="#6.1.17"
+ >TBD</A>
+ 6.1.18 SERVER_SOFTWARE....................................<A
+ HREF="#6.1.18"
+ >TBD</A>
+ 6.2 Request Message-Bodies................................<A
+ HREF="#6.2"
+ >TBD</A>
+ 7 Data Output from the CGI Script...........................<A
+ HREF="#7.0"
+ >TBD</A>
+ 7.1 Non-Parsed Header Output...............................<A
+ HREF="#7.1"
+ >TBD</A>
+ 7.2 Parsed Header Output...................................<A
+ HREF="#7.2"
+ >TBD</A>
+ 7.2.1 CGI header fields...................................<A
+ HREF="#7.2.1"
+ >TBD</A>
+ 7.2.1.1 Content-Type.....................................<A
+ HREF="#7.2.1.1"
+ >TBD</A>
+ 7.2.1.2 Location.........................................<A
+ HREF="#7.2.1.2"
+ >TBD</A>
+ 7.2.1.3 Status...........................................<A
+ HREF="#7.2.1.3"
+ >TBD</A>
+ 7.2.1.4 Extension header fields..........................<A
+ HREF="#7.2.1.3"
+ >TBD</A>
+ 7.2.2 HTTP header fields..................................<A
+ HREF="#7.2.2"
+ >TBD</A>
+ 8 Server Implementation.....................................<A
+ HREF="#8.0"
+ >TBD</A>
+ 8.1 Requirements for Servers...............................<A
+ HREF="#8.1"
+ >TBD</A>
+ 8.1.1 Script-URI..........................................<A
+ HREF="#8.1"
+ >TBD</A>
+ 8.1.2 Request Message-body Handling.......................<A
+ HREF="#8.1.2"
+ >TBD</A>
+ 8.1.3 Required Metavariables..............................<A
+ HREF="#8.1.3"
+ >TBD</A>
+ 8.1.4 Response Compliance.................................<A
+ HREF="#8.1.4"
+ >TBD</A>
+ 8.2 Recommendations for Servers............................<A
+ HREF="#8.2"
+ >TBD</A>
+ 8.3 Summary of Metavariables...............................<A
+ HREF="#8.3"
+ >TBD</A>
+ 9 Script Implementation.....................................<A
+ HREF="#9.0"
+ >TBD</A>
+ 9.1 Requirements for Scripts...............................<A
+ HREF="#9.1"
+ >TBD</A>
+ 9.2 Recommendations for Scripts............................<A
+ HREF="#9.2"
+ >TBD</A>
+ 10 System Specifications....................................<A
+ HREF="#10.0"
+ >TBD</A>
+ 10.1 AmigaDOS..............................................<A
+ HREF="#10.1"
+ >TBD</A>
+ 10.2 Unix..................................................<A
+ HREF="#10.2"
+ >TBD</A>
+ 11 Security Considerations..................................<A
+ HREF="#11.0"
+ >TBD</A>
+ 11.1 Safe Methods..........................................<A
+ HREF="#11.1"
+ >TBD</A>
+ 11.2 HTTP Header Fields Containing Sensitive Information...<A
+ HREF="#11.2"
+ >TBD</A>
+ 11.3 Script Interference with the Server...................<A
+ HREF="#11.3"
+ >TBD</A>
+ 11.4 Data Length and Buffering Considerations..............<A
+ HREF="#11.4"
+ >TBD</A>
+ 11.5 Stateless Processing..................................<A
+ HREF="#11.5"
+ >TBD</A>
+ 12 Acknowledgments..........................................<A
+ HREF="#12.0"
+ >TBD</A>
+ 13 References...............................................<A
+ HREF="#13.0"
+ >TBD</A>
+ 14 Authors' Addresses.......................................<A
+ HREF="#14.0"
+ >TBD</A>
+ </PRE>
+ </DIV>
+
+ <H2>
+ <A NAME="1.0">
+ 1. Introduction
+ </A>
+ </H2>
+
+ <H3>
+ <A NAME="1.1">
+ 1.1. Purpose
+ </A>
+ </H3>
+ <P>
+ Together the HTTP [<A HREF="#[3]">3</A>,<A HREF="#[8]">8</A>] server
+ and the CGI script are responsible
+ for servicing a client
+ request by sending back responses. The client
+ request comprises a Universal Resource Identifier (URI)
+ [<A HREF="#[1]">1</A>], a
+ request method, and various ancillary
+ information about the request
+ provided by the transport mechanism.
+ </P>
+ <P>
+ The CGI defines the abstract parameters, known as
+ metavariables,
+ which describe the client's
+ request. Together with a
+ concrete programmer interface this specifies a platform-independent
+ interface between the script and the HTTP server.
+ </P>
+
+ <H3>
+ <A NAME="1.2">
+ 1.2. Requirements
+ </A>
+ </H3>
+ <P>
+ This specification uses the same words as RFC 1123
+ [<A HREF="#[5]">5</A>] to define the
+ significance of each particular requirement. These are:
+ </P><!--#if expr="! $GUI" -->
+ <P></P><!--#endif -->
+ <DL>
+ <DT><EM>MUST</EM>
+ </DT>
+ <DD>
+ <P>
+ This word or the adjective 'required' means that the item is an
+ absolute requirement of the specification.
+ </P>
+ </DD>
+ <DT><EM>SHOULD</EM>
+ </DT>
+ <DD>
+ <P>
+ This word or the adjective 'recommended' means that there may
+ exist valid reasons in particular circumstances to ignore this
+ item, but the full implications should be understood and the case
+ carefully weighed before choosing a different course.
+ </P>
+ </DD>
+ <DT><EM>MAY</EM>
+ </DT>
+ <DD>
+ <P>
+ This word or the adjective 'optional' means that this item is
+ truly optional. One vendor may choose to include the item because
+ a particular marketplace requires it or because it enhances the
+ product, for example; another vendor may omit the same item.
+ </P>
+ </DD>
+ </DL>
+ <P>
+ An implementation is not compliant if it fails to satisfy one or more
+ of the 'must' requirements for the protocols it implements. An
+ implementation that satisfies all of the 'must' and all of the
+ 'should' requirements for its features is said to be 'unconditionally
+ compliant'; one that satisfies all of the 'must' requirements but not
+ all of the 'should' requirements for its features is said to be
+ 'conditionally compliant.'
+ </P>
+
+ <H3>
+ <A NAME="1.3">
+ 1.3. Specifications
+ </A>
+ </H3>
+ <P>
+ Not all of the functions and features of the CGI are defined in the
+ main part of this specification. The following phrases are used to
+ describe the features which are not specified:
+ </P>
+ <DL>
+ <DT><EM>system defined</EM>
+ </DT>
+ <DD>
+ <P>
+ The feature may differ between systems, but must be the same for
+ different implementations using the same system. A system will
+ usually identify a class of operating-systems. Some systems are
+ defined in
+ <A HREF="#10.0"
+ >section 10</A> of this document.
+ New systems may be defined
+ by new specifications without revision of this document.
+ </P>
+ </DD>
+ <DT><EM>implementation defined</EM>
+ </DT>
+ <DD>
+ <P>
+ The behaviour of the feature may vary from implementation to
+ implementation, but a particular implementation must document its
+ behaviour.
+ </P>
+ </DD>
+ </DL>
+
+ <H3>
+ <A NAME="1.4">
+ 1.4. Terminology
+ </A>
+ </H3>
+ <P>
+ This specification uses many terms defined in the HTTP/1.1
+ specification [<A HREF="#[8]">8</A>]; however, the following terms are
+ used here in a
+ sense which may not accord with their definitions in that document,
+ or with their common meaning.
+ </P>
+
+ <DL>
+ <DT><EM>metavariable</EM>
+ </DT>
+ <DD>
+ <P>
+ A named parameter that carries information from the server to the
+ script. It is not necessarily a variable in the operating-system's
+ environment, although that is the most common implementation.
+ </P>
+ </DD>
+
+ <DT><EM>script</EM>
+ </DT>
+ <DD>
+ <P>
+ The software which is invoked by the server <EM>via</EM> this
+ interface. It
+ need not be a standalone program, but could be a
+ dynamically-loaded or shared library, or even a subroutine in the
+ server. It <EM>may</EM> be a set of statements
+ interpreted at run-time, as the term 'script' is frequently
+ understood, but that is not a requirement and within the context
+ of this specification the term has the broader definition stated.
+ </P>
+ </DD>
+ <DT><EM>server</EM>
+ </DT>
+ <DD>
+ <P>
+ The application program which invokes the script in order to service
+ requests.
+ </P>
+ </DD>
+ </DL>
+
+ <H2>
+ <A NAME="2.0">
+ 2. Notational Conventions and Generic Grammar
+ </A>
+ </H2>
+
+ <H3>
+ <A NAME="2.1">
+ 2.1. Augmented BNF
+ </A>
+ </H3>
+ <P>
+ All of the mechanisms specified in this document are described in
+ both prose and an augmented Backus-Naur Form (BNF) similar to that
+ used by RFC 822 [<A HREF="#[6]">6</A>]. This augmented BNF contains
+ the following constructs:
+ </P>
+ <DL>
+ <DT>name = definition
+ </DT>
+ <DD>
+ <P>
+ The
+ definition by the equal character ("="). Whitespace is only
+ significant in that continuation lines of a definition are
+ indented.
+ </P>
+ </DD>
+ <DT>"literal"
+ </DT>
+ <DD>
+ <P>
+ Quotation marks (") surround literal text, except for a literal
+ quotation mark, which is surrounded by angle-brackets ("&lt;" and "&gt;").
+ Unless stated otherwise, the text is case-sensitive.
+ </P>
+ </DD>
+ <DT>rule1 | rule2
+ </DT>
+ <DD>
+ <P>
+ Alternative rules are separated by a vertical bar ("|").
+ </P>
+ </DD>
+ <DT>(rule1 rule2 rule3)
+ </DT>
+ <DD>
+ <P>
+ Elements enclosed in parentheses are treated as a single element.
+ </P>
+ </DD>
+ <DT>*rule
+ </DT>
+ <DD>
+ <P>
+ A rule preceded by an asterisk ("*") may have zero or more
+ occurrences. A rule preceded by an integer followed by an asterisk
+ must occur at least the specified number of times.
+ </P>
+ </DD>
+ <DT>[rule]
+ </DT>
+ <DD>
+ <P>
+ An element enclosed in square
+ brackets ("[" and "]") is optional.
+ </P>
+ </DD>
+ </DL>
+
+ <H3>
+ <A NAME="2.2">
+ 2.2. Basic Rules
+ </A>
+ </H3>
+ <P>
+ The following rules are used throughout this specification to
+ describe basic parsing constructs.
+ </P><!--#if expr="! $GUI" -->
+ <P></P><!--#endif -->
+ <PRE>
+ alpha = lowalpha | hialpha
+ alphanum = alpha | digit
+ lowalpha = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h"
+ | "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p"
+ | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x"
+ | "y" | "z"
+ hialpha = "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H"
+ | "I" | "J" | "K" | "L" | "M" | "N" | "O" | "P"
+ | "Q" | "R" | "S" | "T" | "U" | "V" | "W" | "X"
+ | "Y" | "Z"
+ digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7"
+ | "8" | "9"
+ hex = digit | "A" | "B" | "C" | "D" | "E" | "F" | "a"
+ | "b" | "c" | "d" | "e" | "f"
+ escaped = "%" hex hex
+ OCTET = &lt;any 8-bit sequence of data&gt;
+ CHAR = &lt;any US-ASCII character (octets 0 - 127)&gt;
+ CTL = &lt;any US-ASCII control character
+ (octets 0 - 31) and DEL (127)&gt;
+ CR = &lt;US-ASCII CR, carriage return (13)&gt;
+ LF = &lt;US-ASCII LF, linefeed (10)&gt;
+ SP = &lt;US-ASCII SP, space (32)&gt;
+ HT = &lt;US-ASCII HT, horizontal tab (9)&gt;
+ NL = CR | LF
+ LWSP = SP | HT | NL
+ tspecial = "(" | ")" | "@" | "," | ";" | ":" | "\" | &lt;"&gt;
+ | "/" | "[" | "]" | "?" | "&lt;" | "&gt;" | "{" | "}"
+ | SP | HT | NL
+ token = 1*&lt;any CHAR except CTLs or tspecials&gt;
+ quoted-string = ( &lt;"&gt; *qdtext &lt;"&gt; ) | ( "&lt;" *qatext "&gt;")
+ qdtext = &lt;any CHAR except &lt;"&gt; and CTLs but including LWSP&gt;
+ qatext = &lt;any CHAR except "&lt;", "&gt;" and CTLs but
+ including LWSP&gt;
+ mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")"
+ unreserved = alphanum | mark
+ reserved = ";" | "/" | "?" | ":" | "@" | "&amp;" | "=" |
+ "$" | ","
+ uric = reserved | unreserved | escaped
+ </PRE>
+ <P>
+ Note that newline (NL) need not be a single character, but can be a
+ character sequence.
+ </P>
+
+ <H2>
+ <A NAME="3.0">
+ 3. Protocol Parameters
+ </A>
+ </H2>
+
+ <H3>
+ <A NAME="3.1">
+ 3.1. URL Encoding
+ </A>
+ </H3>
+ <P>
+ Some variables and constructs used here are described as being
+ 'URL-encoded'. This encoding is described in section
+ 2 of RFC
+ 2396
+ [<A HREF="#[4]">4</A>].
+ </P>
+ <P>
+ An alternate "shortcut" encoding for representing the space
+ character exists and is in common use. Scripts MUST be prepared to
+ recognise both '+' and '%20' as an encoded space in a
+ URL-encoded value.
+ </P>
+ <P>
+ Note that some unsafe characters may have different semantics if
+ they are encoded. The definition of which characters are unsafe
+ depends on the context.
+ For example, the following two URLs do not
+ necessarily refer to the same resource:
+ </P><!--#if expr="! $GUI" -->
+ <P></P><!--#endif -->
+ <PRE>
+ http://somehost.com/somedir%2Fvalue
+ http://somehost.com/somedir/value
+ </PRE>
+ <P>
+ See section
+ 2 of RFC
+ 2396 [<A HREF="#[4]">4</A>]
+ for authoritative treatment of this issue.
+ </P>
+
+ <H3>
+ <A NAME="3.2">
+ 3.2. The Script-URI
+ </A>
+ </H3>
+ <P>
+ The 'Script-URI' is defined as the URI of the resource identified
+ by the metavariables. Often,
+ this URI will be the same as
+ the URI requested by the client (the 'Client-URI'); however, it need
+ not be. Instead, it could be a URI invented by the server, and so it
+ can only be used in the context of the server and its CGI interface.
+ </P>
+ <P>
+ The Script-URI has the syntax of generic-RL as defined in section 2.1
+ of RFC 1808 [<A HREF="#[7]">7</A>], with the exception that object
+ parameters and
+ fragment identifiers are not permitted:
+ </P><!--#if expr="! $GUI" -->
+ <P></P><!--#endif -->
+ <PRE>
+ &lt;scheme&gt;://&lt;host&gt;&lt;port&gt;/&lt;path&gt;?&lt;query&gt;
+ </PRE>
+ <P>
+ The various components of the
+ Script-URI
+ are defined by some of the
+ metavariables (see
+ <A HREF="#4.0">section 4</A>
+ below);
+ </P><!--#if expr="! $GUI" -->
+ <P></P><!--#endif -->
+ <PRE>
+ script-uri = protocol "://" SERVER_NAME ":" SERVER_PORT enc-script
+ enc-path-info "?" QUERY_STRING
+ </PRE>
+ <P>
+ where 'protocol' is obtained
+ from SERVER_PROTOCOL, 'enc-script' is a
+ URL-encoded version of SCRIPT_NAME and 'enc-path-info' is a
+ URL-encoded version of PATH_INFO. See
+ <A HREF="#4.6">section 4.6</A> for more information about the PATH_INFO
+ metavariable.
+ </P>
+ <P>
+ Note that the scheme and the protocol are <EM>not</EM> identical;
+ for instance, a resource accessed <EM>via</EM> an SSL mechanism
+ may have a Client-URI with a scheme of "<SAMP>https</SAMP>"
+ rather than "<SAMP>http</SAMP>". CGI/1.1 provides no means
+ for the script to reconstruct this, and therefore
+ the Script-URI includes the base protocol used.
+ </P>
+
+ <H2>
+ <A NAME="4.0">
+ 4. Invoking the Script
+ </A>
+ </H2>
+ <P>
+ The
+ script is invoked in a system defined manner. Unless specified
+ otherwise, the file containing the script will be invoked as an
+ executable program.
+ </P>
+
+ <H2>
+ <A NAME="5.0">
+ 5. The CGI Script Command Line
+ </A>
+ </H2>
+ <P>
+ Some systems support a method for supplying an array of strings to
+ the CGI script. This is only used in the case of an 'indexed' query.
+ This is identified by a "GET" or "HEAD" HTTP request with a URL
+ query
+ string not containing any unencoded "=" characters. For such a
+ request,
+ servers SHOULD parse the search string
+ into words, using the following rules:
+ </P><!--#if expr="! $GUI" -->
+ <P></P><!--#endif -->
+ <PRE>
+ search-string = search-word *( "+" search-word )
+ search-word = 1*schar
+ schar = xunreserved | escaped | xreserved
+ xunreserved = alpha | digit | xsafe | extra
+ xsafe = "$" | "-" | "_" | "."
+ xreserved = ";" | "/" | "?" | ":" | "@" | "&"
+ </PRE>
+ <P>
+ After parsing, each word is URL-decoded, optionally encoded in a
+ system defined manner,
+ and then the argument list is set to the list
+ of words.
+ </P>
+ <P>
+ If the server cannot create any part of the argument list, then the
+ server SHOULD NOT generate any command line information. For example, the
+ number of arguments may be greater than operating system or server
+ limitations permit, or one of the words may not be representable as an
+ argument.
+ </P>
+ <P>
+ Scripts SHOULD check to see if the QUERY_STRING value contains an
+ unencoded "=" character, and SHOULD NOT use the command line arguments
+ if it does.
+ </P>
+
+ <H2>
+ <A NAME="6.0">
+ 6. Data Input to the CGI Script
+ </A>
+ </H2>
+ <P>
+ Information about a request comes from two different sources: the
+ request header, and any associated
+ message-body.
+ Servers MUST
+ make portions of this information available to
+ scripts.
+ </P>
+
+ <H3>
+ <A NAME="6.1">
+ 6.1. Request Metadata
+ (Metavariables)
+ </A>
+ </H3>
+ <P>
+ Each CGI server
+ implementation MUST define a mechanism
+ to pass data about the request from
+ the server to the script.
+ The metavariables containing these
+ data
+ are accessed by the script in a system
+ defined manner.
+ The
+ representation of the characters in the
+ metavariables is
+ system defined.
+ </P>
+ <P>
+ This specification does not distinguish between the representation of
+ null values and missing ones. Whether null or missing values
+ (such as a query component of "?" or "", respectively) are represented
+ by undefined metavariables or by metavariables with values of "" is
+ implementation-defined.
+ </P>
+ <P>
+ Case is not significant in the
+ metavariable
+ names, in that there cannot be two
+ different variables
+ whose names differ in case only. Here they are
+ shown using a canonical representation of capitals plus underscore
+ ("_"). The actual representation of the names is system defined; for
+ a particular system the representation MAY be defined differently
+ than this.
+ </P>
+ <P>
+ Metavariable
+ values MUST be
+ considered case-sensitive except as noted
+ otherwise.
+ </P>
+ <P>
+ The canonical
+ metavariables
+ defined by this specification are:
+ </P><!--#if expr="! $GUI" -->
+ <P></P><!--#endif -->
+ <PRE>
+ AUTH_TYPE
+ CONTENT_LENGTH
+ CONTENT_TYPE
+ GATEWAY_INTERFACE
+ PATH_INFO
+ PATH_TRANSLATED
+ QUERY_STRING
+ REMOTE_ADDR
+ REMOTE_HOST
+ REMOTE_IDENT
+ REMOTE_USER
+ REQUEST_METHOD
+ SCRIPT_NAME
+ SERVER_NAME
+ SERVER_PORT
+ SERVER_PROTOCOL
+ SERVER_SOFTWARE
+ </PRE>
+ <P>
+ Metavariables with names beginning with the protocol name (<EM>e.g.</EM>,
+ "HTTP_ACCEPT") are also canonical in their description of request header
+ fields. The number and meaning of these fields may change independently
+ of this specification. (See also <A HREF="#6.1.5">section 6.1.5</A>.)
+ </P>
+
+ <H4>
+ <A NAME="6.1.1">
+ 6.1.1. AUTH_TYPE
+ </A>
+ </H4>
+ <P>
+ This variable is specific to requests made
+ <EM>via</EM> the
+ "<CODE>http</CODE>"
+ scheme.
+ </P>
+ <P>
+ If the Script-URI
+ required access authentication for external
+ access, then the server
+ MUST set
+ the value of
+ this variable
+ from the '<SAMP>auth-scheme</SAMP>' token in
+ the request's "<SAMP>Authorization</SAMP>" header
+ field.
+ Otherwise
+ it is
+ set to NULL.
+ </P><!--#if expr="! $GUI" -->
+ <P></P><!--#endif -->
+ <PRE>
+ AUTH_TYPE = "" | auth-scheme
+ auth-scheme = "Basic" | "Digest" | token
+ </PRE>
+ <P>
+ HTTP access authentication schemes are described in section 11 of the
+ HTTP/1.1 specification [<A HREF="#[8]">8</A>]. The auth-scheme is
+ not case-sensitive.
+ </P>
+ <P>
+ Servers
+ MUST
+ provide this metavariable
+ to scripts if the request
+ header included an "<SAMP>Authorization</SAMP>" field
+ that was authenticated.
+ </P>
+
+ <H4>
+ <A NAME="6.1.2">
+ 6.1.2. CONTENT_LENGTH
+ </A>
+ </H4>
+ <P>
+ This
+ metavariable
+ is set to the
+ size of the message-body
+ entity attached to the request, if any, in decimal
+ number of octets. If no data are attached, then this
+ metavariable
+ is either NULL or not
+ defined. The syntax is
+ the same as for
+ the HTTP "<SAMP>Content-Length</SAMP>" header field (section 14.14, HTTP/1.1
+ specification [<A HREF="#[8]">8</A>]).
+ </P><!--#if expr="! $GUI" -->
+ <P></P><!--#endif -->
+ <PRE>
+ CONTENT_LENGTH = "" | 1*digit
+ </PRE>
+ <P>
+ Servers MUST provide this metavariable
+ to scripts if the request
+ was accompanied by a
+ message-body entity.
+ </P>
+
+ <H4>
+ <A NAME="6.1.3">
+ 6.1.3. CONTENT_TYPE
+ </A>
+ </H4>
+ <P>
+ If the request includes a
+ message-body,
+ CONTENT_TYPE is set
+ to
+ the Internet Media Type
+ [<A HREF="#[9]">9</A>] of the attached
+ entity if the type was provided <EM>via</EM>
+ a "<SAMP>Content-type</SAMP>" field in the
+ request header, or if the server can determine it in the absence
+ of a supplied "<SAMP>Content-type</SAMP>" field. The syntax is the
+ same as for the HTTP
+ "<SAMP>Content-Type</SAMP>" header field.
+ </P><!--#if expr="! $GUI" -->
+ <P></P><!--#endif -->
+ <PRE>
+ CONTENT_TYPE = "" | media-type
+ media-type = type "/" subtype *( ";" parameter)
+ type = token
+ subtype = token
+ parameter = attribute "=" value
+ attribute = token
+ value = token | quoted-string
+ </PRE>
+ <P>
+ The type, subtype,
+ and parameter attribute names are not
+ case-sensitive. Parameter values MAY be case sensitive.
+ Media types and their use in HTTP are described
+ in section 3.7 of the
+ HTTP/1.1 specification [<A HREF="#[8]">8</A>].
+ </P>
+ <P>
+ Example:
+ </P><!--#if expr="! $GUI" -->
+ <P></P><!--#endif -->
+ <PRE>
+ application/x-www-form-urlencoded
+ </PRE>
+ <P>
+ There is no default value for this variable. If and only if it is
+ unset, then the script MAY attempt to determine the media type from
+ the data received. If the type remains unknown, then
+ the script MAY choose to either assume a
+ content-type of
+ <SAMP>application/octet-stream</SAMP>
+ or reject the request with a 415 ("Unsupported Media Type")
+ error. See <A HREF="#7.2.1.3">section 7.2.1.3</A>
+ for more information about returning error status values.
+ </P>
+ <P>
+ Servers MUST provide this metavariable
+ to scripts if
+ a "<SAMP>Content-Type</SAMP>" field was present
+ in the original request header. If the server receives a request
+ with an attached entity but no "<SAMP>Content-Type</SAMP>"
+ header field, it MAY attempt to
+ determine the correct datatype, or it MAY omit this
+ metavariable when
+ communicating the request information to the script.
+ </P>
+
+ <H4>
+ <A NAME="6.1.4">
+ 6.1.4. GATEWAY_INTERFACE
+ </A>
+ </H4>
+ <P>
+ This
+ metavariable
+ is set to
+ the dialect of CGI being used
+ by the server to communicate with the script.
+ Syntax:
+ </P><!--#if expr="! $GUI" -->
+ <P></P><!--#endif -->
+ <PRE>
+ GATEWAY_INTERFACE = "CGI" "/" major "." minor
+ major = 1*digit
+ minor = 1*digit
+ </PRE>
+ <P>
+ Note that the major and minor numbers are treated as separate
+ integers and hence each may be
+ more than a single
+ digit. Thus CGI/2.4 is a lower version than CGI/2.13 which in turn
+ is lower than CGI/12.3. Leading zeros in either
+ the major or the minor number MUST be ignored by scripts and
+ SHOULD NOT be generated by servers.
+ </P>
+ <P>
+ This document defines the 1.1 version of the CGI interface
+ ("CGI/1.1").
+ </P>
+ <P>
+ Servers MUST provide this metavariable
+ to scripts.
+ </P>
+
+ <H4>
+ <A NAME="6.1.5">
+ 6.1.5. Protocol-Specific Metavariables
+ </A>
+ </H4>
+ <P>
+ These metavariables are specific to
+ the protocol
+ <EM>via</EM> which the request is made.
+ Interpretation of these variables depends on the value of
+ the
+ SERVER_PROTOCOL
+ metavariable
+ (see
+ <A HREF="#6.1.17">section 6.1.17</A>).
+ </P>
+ <P>
+ Metavariables
+ with names beginning with "HTTP_" contain
+ values from the request header, if the
+ scheme used was HTTP.
+ Each
+ HTTP header field name is converted to upper case, has all occurrences of
+ "-" replaced with "_",
+ and has "HTTP_" prepended to form
+ the metavariable name.
+ Similar transformations are applied for other
+ protocols.
+ The header data MAY be presented as sent
+ by the client, or MAY be rewritten in ways which do not change its
+ semantics. If multiple header fields with the same field-name are received
+ then the server
+ MUST rewrite them as though they
+ had been received as a single header field having the same
+ semantics before being represented in a
+ metavariable.
+ Similarly, a header field that is received on more than one line
+ MUST be merged into a single line. The server MUST, if necessary,
+ change the representation of the data (for example, the character
+ set) to be appropriate for a CGI
+ metavariable.
+ <!-- ###NOTE: See if 2068 describes this thoroughly, and
+ point there if so. -->
+ </P>
+ <P>
+ Servers are
+ not required to create
+ metavariables for all
+ the request
+ header fields that they
+ receive. In particular,
+ they MAY
+ decline to make available any
+ header fields carrying authentication information, such as
+ "<SAMP>Authorization</SAMP>", or
+ which are available to the script
+ <EM>via</EM> other metavariables,
+ such as "<SAMP>Content-Length</SAMP>" and "<SAMP>Content-Type</SAMP>".
+ </P>
+
+ <H4>
+ <A NAME="6.1.6">
+ 6.1.6. PATH_INFO
+ </A>
+ </H4>
+ <P>
+ The PATH_INFO
+ metavariable
+ specifies
+ a path to be interpreted by the CGI script. It identifies the
+ resource or sub-resource to be returned
+ by the CGI
+ script, and it is derived from the portion
+ of the URI path following the script name but preceding
+ any query data.
+ The syntax
+ and semantics are similar to a decoded HTTP URL
+ 'path' token
+ (defined in
+ RFC 2396
+ [<A HREF="#[4]">4</A>]), with the exception
+ that a PATH_INFO of "/"
+ represents a single void path segment.
+ </P><!--#if expr="! $GUI" -->
+ <P></P><!--#endif -->
+ <PRE>
+ PATH_INFO = "" | ( "/" path )
+ path = segment *( "/" segment )
+ segment = *pchar
+ pchar = &lt;any CHAR except "/"&gt;
+ </PRE>
+ <P>
+ The PATH_INFO string is the trailing part of the &lt;path&gt; component of
+ the Script-URI
+ (see <A HREF="#3.2">section 3.2</A>)
+ that follows the SCRIPT_NAME
+ portion of the path.
+ </P>
+ <P>
+ Servers MAY impose their own restrictions and
+ limitations on what values they will accept for PATH_INFO, and MAY
+ reject or edit any values they
+ consider objectionable before passing
+ them to the script.
+ </P>
+ <P>
+ Servers MUST make this URI component available
+ to CGI scripts. The PATH_INFO
+ value is case-sensitive, and the
+ server MUST preserve the case of the PATH_INFO element of the URI
+ when making it available to scripts.
+ </P>
+
+ <H4>
+ <A NAME="6.1.7">
+ 6.1.7. PATH_TRANSLATED
+ </A>
+ </H4>
+ <P>
+ PATH_TRANSLATED is derived by taking any path-info component of the
+ request URI (see
+ <A HREF="#6.1.6">section 6.1.6</A>), decoding it
+ (see <A HREF="#3.1">section 3.1</A>), parsing it as a URI in its own
+ right, and performing any virtual-to-physical
+ translation appropriate to map it onto the
+ server's document repository structure.
+ If the request URI includes no path-info
+ component, the PATH_TRANSLATED metavariable SHOULD NOT be defined.
+ </P><!--#if expr="! $GUI" -->
+ <P></P><!--#endif -->
+ <PRE>
+ PATH_TRANSLATED = *CHAR
+ </PRE>
+ <P>
+ For a request such as the following:
+ </P><!--#if expr="! $GUI" -->
+ <P></P><!--#endif -->
+ <PRE>
+ http://somehost.com/cgi-bin/somescript/this%2eis%2epath%2einfo
+ </PRE>
+ <P>
+ the PATH_INFO component would be decoded, and the result
+ parsed as though it were a request for the following:
+ </P><!--#if expr="! $GUI" -->
+ <P></P><!--#endif -->
+ <PRE>
+ http://somehost.com/this.is.the.path.info
+ </PRE>
+ <P>
+ This would then be translated to a
+ location in the server's document repository,
+ perhaps a filesystem path something
+ like this:
+ </P><!--#if expr="! $GUI" -->
+ <P></P><!--#endif -->
+ <PRE>
+ /usr/local/www/htdocs/this.is.the.path.info
+ </PRE>
+ <P>
+ The result of the translation is the value of PATH_TRANSLATED.
+ </P>
+ <P>
+ The value of PATH_TRANSLATED may or may not map to a valid
+ repository
+ location.
+ Servers MUST preserve the case of the path-info
+ segment if and only if the underlying
+ repository
+ supports case-sensitive
+ names. If the
+ repository
+ is only case-aware, case-preserving, or case-blind
+ with regard to
+ document names,
+ servers are not required to preserve the
+ case of the original segment through the translation.
+ </P>
+ <P>
+ The
+ translation
+ algorithm the server uses to derive PATH_TRANSLATED is
+ implementation defined; CGI scripts which use this variable may
+ suffer limited portability.
+ </P>
+ <P>
+ Servers SHOULD provide this metavariable
+ to scripts if and only if the request URI includes a
+ path-info component.
+ </P>
+
+ <H4>
+ <A NAME="6.1.8">
+ 6.1.8. QUERY_STRING
+ </A>
+ </H4>
+ <P>
+ A URL-encoded
+ string; the &lt;query&gt; part of the
+ Script-URI.
+ (See
+ <A HREF="#3.2">section 3.2</A>.)
+ </P><!--#if expr="! $GUI" -->
+ <P></P><!--#endif -->
+ <PRE>
+ QUERY_STRING = query-string
+ query-string = *uric
+ </PRE>
+ <P>
+ The URL syntax for a query
+ string is described in
+ section 3 of
+ RFC 2396
+ [<A HREF="#[4]">4</A>].
+ </P>
+ <P>
+ Servers MUST supply this value to scripts.
+ The QUERY_STRING value is case-sensitive.
+ If the Script-URI does not include a query component,
+ the QUERY_STRING metavariable MUST be defined as an empty string ("").
+ </P>
+
+ <H4>
+ <A NAME="6.1.9">
+ 6.1.9. REMOTE_ADDR
+ </A>
+ </H4>
+ <P>
+ The IP address of the client
+ sending the request to the server. This
+ is not necessarily that of the user
+ agent
+ (such as if the request came through a proxy).
+ </P><!--#if expr="! $GUI" -->
+ <P></P><!--#endif -->
+ <PRE>
+ REMOTE_ADDR = hostnumber
+ hostnumber = ipv4-address | ipv6-address
+ </PRE>
+ <P>
+ The definitions of <SAMP>ipv4-address</SAMP> and <SAMP>ipv6-address</SAMP>
+ are provided in Appendix B of RFC 2373 [<A HREF="#[13]">13</A>].
+ </P>
+ <P>
+ Servers MUST supply this value to scripts.
+ </P>
+
+ <H4>
+ <A NAME="6.1.10">
+ 6.1.10. REMOTE_HOST
+ </A>
+ </H4>
+ <P>
+ The fully qualified domain name of the
+ client sending the request to
+ the server, if available, otherwise NULL.
+ (See <A HREF="#6.1.9">section 6.1.9</A>.)
+ Fully qualified domain names take the form as described in
+ section 3.5 of RFC 1034 [<A HREF="#[10]">10</A>] and section 2.1 of
+ RFC 1123 [<A HREF="#[5]">5</A>]. Domain names are not case sensitive.
+ </P>
+ <P>
+ Servers SHOULD provide this information to
+ scripts.
+ </P>
+
+ <H4>
+ <A NAME="6.1.11">
+ 6.1.11. REMOTE_IDENT
+ </A>
+ </H4>
+ <P>
+ The identity information reported about the connection by a
+ RFC 1413 [<A HREF="#[11]">11</A>] request to the remote agent, if
+ available. Servers
+ MAY choose not
+ to support this feature, or not to request the data
+ for efficiency reasons.
+ </P><!--#if expr="! $GUI" -->
+ <P></P><!--#endif -->
+ <PRE>
+ REMOTE_IDENT = *CHAR
+ </PRE>
+ <P>
+ The data returned
+ may be used for authentication purposes, but the level
+ of trust reposed in them should be minimal.
+ </P>
+ <P>
+ Servers MAY supply this information to scripts if the
+ RFC1413 [<A HREF="#[11]">11</A>] lookup is performed.
+ </P>
+
+ <H4>
+ <A NAME="6.1.12">
+ 6.1.12. REMOTE_USER
+ </A>
+ </H4>
+ <P>
+ If the request required authentication using the "Basic"
+ mechanism (<EM>i.e.</EM>, the AUTH_TYPE
+ metavariable is set
+ to "Basic"), then the value of the REMOTE_USER
+ metavariable is set to the
+ user-ID supplied. In all other cases
+ the value of this metavariable
+ is undefined.
+ </P><!--#if expr="! $GUI" -->
+ <P></P><!--#endif -->
+ <PRE>
+ REMOTE_USER = *OCTET
+ </PRE>
+ <P>
+ This variable is specific to requests made <EM>via</EM> the
+ HTTP protocol.
+ </P>
+ <P>
+ Servers SHOULD provide this metavariable
+ to scripts.
+ </P>
+
+ <H4>
+ <A NAME="6.1.13">
+ 6.1.13. REQUEST_METHOD
+ </A>
+ </H4>
+ <P>
+ The REQUEST_METHOD
+ metavariable
+ is set to the
+ method with which the request was made, as described in section
+ 5.1.1 of the HTTP/1.0 specification [<A HREF="#[3]">3</A>] and
+ section 5.1.1 of the
+ HTTP/1.1 specification [<A HREF="#[8]">8</A>].
+ </P><!--#if expr="! $GUI" -->
+ <P></P><!--#endif -->
+ <PRE>
+ REQUEST_METHOD = http-method
+ http-method = "GET" | "HEAD" | "POST" | "PUT" | "DELETE"
+ | "OPTIONS" | "TRACE" | extension-method
+ extension-method = token
+ </PRE>
+ <P>
+ The method is case sensitive.
+ CGI/1.1 servers MAY choose to process some methods
+ directly rather than passing them to scripts.
+ </P>
+ <P>
+ This variable is specific to requests made with HTTP.
+ </P>
+ <P>
+ Servers MUST provide this metavariable
+ to scripts.
+ </P>
+
+ <H4>
+ <A NAME="6.1.14">
+ 6.1.14. SCRIPT_NAME
+ </A>
+ </H4>
+ <P>
+ The SCRIPT_NAME
+ metavariable
+ is
+ set to a URL path that could identify the CGI script (rather than the
+ script's
+ output). The syntax and semantics are identical to a
+ decoded HTTP URL 'path' token
+ (see RFC 2396
+ [<A HREF="#[4]">4</A>]).
+ </P><!--#if expr="! $GUI" -->
+ <P></P><!--#endif -->
+ <PRE>
+ SCRIPT_NAME = "" | ( "/" [ path ] )
+ </PRE>
+ <P>
+ The SCRIPT_NAME string is some leading part of the &lt;path&gt; component
+ of the Script-URI derived in some
+ implementation defined manner.
+ No PATH_INFO or QUERY_STRING segments
+ (see sections <A HREF="#6.1.6">6.1.6</A> and
+ <A HREF="#6.1.8">6.1.8</A>) are included
+ in the SCRIPT_NAME value.
+ </P>
+ <P>
+ Servers MUST provide this metavariable
+ to scripts.
+ </P>
+
+ <H4>
+ <A NAME="6.1.15">
+ 6.1.15. SERVER_NAME
+ </A>
+ </H4>
+ <P>
+ The SERVER_NAME
+ metavariable
+ is set to the
+ name of the
+ server, as
+ derived from the &lt;host&gt; part of the
+ Script-URI
+ (see <A HREF="#3.2">section 3.2</A>).
+ </P><!--#if expr="! $GUI" -->
+ <P></P><!--#endif -->
+ <PRE>
+ SERVER_NAME = hostname | hostnumber
+ </PRE>
+ <P>
+ Servers MUST provide this metavariable
+ to scripts.
+ </P>
+
+ <H4>
+ <A NAME="6.1.16">
+ 6.1.16. SERVER_PORT
+ </A>
+ </H4>
+ <P>
+ The SERVER_PORT
+ metavariable
+ is set to the
+ port on which the
+ request was received, as used in the &lt;port&gt;
+ part of the Script-URI.
+ </P><!--#if expr="! $GUI" -->
+ <P></P><!--#endif -->
+ <PRE>
+ SERVER_PORT = 1*digit
+ </PRE>
+ <P>
+ If the &lt;port&gt; portion of the script-URI is blank, the actual
+ port number upon which the request was received MUST be supplied.
+ </P>
+ <P>
+ Servers MUST provide this metavariable
+ to scripts.
+ </P>
+
+ <H4>
+ <A NAME="6.1.17">
+ 6.1.17. SERVER_PROTOCOL
+ </A>
+ </H4>
+ <P>
+ The SERVER_PROTOCOL
+ metavariable
+ is set to
+ the
+ name and revision of the information protocol with which
+ the
+ request
+ arrived. This is not necessarily the same as the protocol version used by
+ the server in its response to the client.
+ </P><!--#if expr="! $GUI" -->
+ <P></P><!--#endif -->
+ <PRE>
+ SERVER_PROTOCOL = HTTP-Version | extension-version
+ | extension-token
+ HTTP-Version = "HTTP" "/" 1*digit "." 1*digit
+ extension-version = protocol "/" 1*digit "." 1*digit
+ protocol = 1*( alpha | digit | "+" | "-" | "." )
+ extension-token = token
+ </PRE>
+ <P>
+ 'protocol' is a version of the &lt;scheme&gt; part of the
+ Script-URI, but is
+ not identical to it. For example, the scheme of a request may be
+ "<SAMP>https</SAMP>" while the protocol remains "<SAMP>http</SAMP>".
+ The protocol is not case sensitive, but
+ by convention, 'protocol' is in
+ upper case.
+ </P>
+ <P>
+ A well-known extension token value is "INCLUDED",
+ which signals that the current document is being included as part of
+ a composite document, rather than being the direct target of the
+ client request.
+ </P>
+ <P>
+ Servers MUST provide this metavariable
+ to scripts.
+ </P>
+
+ <H4>
+ <A NAME="6.1.18">
+ 6.1.18. SERVER_SOFTWARE
+ </A>
+ </H4>
+ <P>
+ The SERVER_SOFTWARE
+ metavariable
+ is set to the
+ name and version of the information server software answering the
+ request (and running the gateway).
+ </P><!--#if expr="! $GUI" -->
+ <P></P><!--#endif -->
+ <PRE>
+ SERVER_SOFTWARE = 1*product
+ product = token [ "/" product-version ]
+ product-version = token
+ </PRE>
+ <P>
+ Servers MUST provide this metavariable
+ to scripts.
+ </P>
+
+ <H3>
+ <A NAME="6.2">
+ 6.2. Request Message-Bodies
+ </A>
+ </H3>
+ <P>
+ As there may be a data entity attached to the request, there MUST be
+ a system defined method for the script to read
+ these data. Unless
+ defined otherwise, this will be <EM>via</EM> the 'standard input' file
+ descriptor.
+ </P>
+ <P>
+ If the CONTENT_LENGTH value (see <A HREF="#6.1.2">section 6.1.2</A>)
+ is non-NULL, the server MUST supply at least that many bytes to
+ scripts on the standard input stream.
+ Scripts are
+ not obliged to read the data.
+ Servers MAY signal an EOF condition after CONTENT_LENGTH bytes have been
+ read, but are
+ not obligated to do so. Therefore, scripts
+ MUST NOT
+ attempt to read more than CONTENT_LENGTH bytes, even if more data
+ are available.
+ </P>
+ <P>
+ For non-parsed header (NPH) scripts (see
+ <A HREF="#7.1">section 7.1</A>
+ below),
+ servers SHOULD
+ attempt to ensure that the data
+ supplied to the script are precisely
+ as supplied by the client and unaltered by
+ the server.
+ </P>
+ <P>
+ <A HREF="#8.1.2">Section 8.1.2</A> describes the requirements of
+ servers with regard to requests that include
+ message-bodies.
+ </P>
+
+ <H2>
+ <A NAME="7.0">
+ 7. Data Output from the CGI Script
+ </A>
+ </H2>
+ <P>
+ There MUST be a system defined method for the script to send data
+ back to the server or client; a script MUST always return some data.
+ Unless defined otherwise, this will be <EM>via</EM> the 'standard
+ output' file descriptor.
+ </P>
+ <P>
+ There are two forms of output that scripts can supply to servers: non-parsed
+ header (NPH) output, and parsed header output.
+ Servers MUST support parsed header
+ output and MAY support NPH output. The method of
+ distinguishing between the two
+ types of output (or scripts) is implementation defined.
+ </P>
+ <P>
+ Servers MAY implement a timeout period within which data must be
+ received from scripts. If a server implementation defines such
+ a timeout and receives no data from a script within the timeout
+ period, the server MAY terminate the script process and SHOULD
+ abort the client request with
+ either a
+ '504 Gateway Timed Out' or a
+ '500 Internal Server Error' response.
+ </P>
+
+ <H3>
+ <A NAME="7.1">
+ 7.1. Non-Parsed Header Output
+ </A>
+ </H3>
+ <P>
+ Scripts using the NPH output form
+ MUST return a complete HTTP response message, as described
+ in Section 6 of the HTTP specifications
+ [<A HREF="#[3]">3</A>,<A HREF="#[8]">8</A>].
+ NPH scripts
+ MUST use the SERVER_PROTOCOL variable to determine the appropriate format
+ for a response.
+ </P>
+ <P>
+ Servers
+ SHOULD attempt to ensure that the script output is sent
+ directly to the client, with minimal
+ internal and no transport-visible
+ buffering.
+ </P>
+
+ <H3>
+ <A NAME="7.2">
+ 7.2. Parsed Header Output
+ </A>
+ </H3>
+ <P>
+ Scripts using the parsed header output form MUST supply
+ a CGI response message to the server
+ as follows:
+ </P><!--#if expr="! $GUI" -->
+ <P></P><!--#endif -->
+ <PRE>
+ CGI-Response = *optional-field CGI-Field *optional-field NL [ Message-Body ]
+ optional-field = ( CGI-Field | HTTP-Field )
+ CGI-Field = Content-type
+ | Location
+ | Status
+ | extension-header
+ </PRE>
+ <P><!-- ##### If HTTP defines x-headers, remove ours except x-cgi- -->
+ The response comprises a header and a body, separated by a blank line.
+ The body may be NULL.
+ The header fields are either CGI header fields to be interpreted by
+ the server, or HTTP header fields
+ to be included in the response returned
+ to the client
+ if the request method is HTTP. At least one
+ CGI-Field MUST be
+ supplied, but no CGI field name may be used more than once
+ in a response.
+ If a body is supplied, then a "<SAMP>Content-type</SAMP>"
+ header field MUST be
+ supplied by the script,
+ otherwise the script MUST send a "<SAMP>Location</SAMP>"
+ or "<SAMP>Status</SAMP>" header field. If a
+ <SAMP>Location</SAMP> CGI-Field
+ is returned, then the script MUST NOT supply
+ any HTTP-Fields.
+ </P>
+ <P>
+ Each header field in a CGI-Response MUST be specified on a single line;
+ CGI/1.1 does not support continuation lines.
+ </P>
+
+ <H4>
+ <A NAME="7.2.1">
+ 7.2.1. CGI header fields
+ </A>
+ </H4>
+ <P>
+ The CGI header fields have the generic syntax:
+ </P><!--#if expr="! $GUI" -->
+ <P></P><!--#endif -->
+ <PRE>
+ generic-field = field-name ":" [ field-value ] NL
+ field-name = token
+ field-value = *( field-content | LWSP )
+ field-content = *( token | tspecial | quoted-string )
+ </PRE>
+ <P>
+ The field-name is not case sensitive; a NULL field value is
+ equivalent to the header field not being sent.
+ </P>
+
+ <H4>
+ <A NAME="7.2.1.1">
+ 7.2.1.1. Content-Type
+ </A>
+ </H4>
+ <P>
+ The Internet Media Type [<A HREF="#[9]">9</A>] of the entity
+ body, which is to be sent unmodified to the client.
+ </P><!--#if expr="! $GUI" -->
+ <P></P><!--#endif -->
+ <PRE>
+ Content-Type = "Content-Type" ":" media-type NL
+ </PRE>
+ <P>
+ This is actually an HTTP-Field
+ rather than a CGI-Field, but
+ it is listed here because of its importance in the CGI dialogue as
+ a member of the "one of these is required" set of header
+ fields.
+ </P>
+
+ <H4>
+ <A NAME="7.2.1.2">
+ 7.2.1.2. Location
+ </A>
+ </H4>
+ <P>
+ This is used to specify to the server that the script is returning a
+ reference to a document rather than an actual document.
+ </P><!--#if expr="! $GUI" -->
+ <P></P><!--#endif -->
+ <PRE>
+ Location = "Location" ":"
+ ( fragment-URI | rel-URL-abs-path ) NL
+ fragment-URI = URI [ # fragmentid ]
+ URI = scheme ":" *qchar
+ fragmentid = *qchar
+ rel-URL-abs-path = "/" [ hpath ] [ "?" query-string ]
+ hpath = fpsegment *( "/" psegment )
+ fpsegment = 1*hchar
+ psegment = *hchar
+ hchar = alpha | digit | safe | extra
+ | ":" | "@" | "& | "="
+ </PRE>
+ <P>
+ The Location
+ value is either an absolute URI with optional fragment,
+ as defined in RFC 1630 [<A HREF="#[1]">1</A>], or an absolute path
+ within the server's URI space (<EM>i.e.</EM>,
+ omitting the scheme and network-related fields) and optional
+ query-string. If an absolute URI is returned by the script,
+ then the
+ server MUST generate a
+ '302 redirect' HTTP response
+ message unless the script has supplied an
+ explicit Status response header field.
+ Scripts returning an absolute URI MAY choose to
+ provide a message-body. Servers MUST make any appropriate modifications
+ to the script's output to ensure the response to the user-agent complies
+ with the response protocol version.
+ If the Location value is a path, then the server
+ MUST generate
+ the response that it would have produced in response to a request
+ containing the URL
+ </P><!--#if expr="! $GUI" -->
+ <P></P><!--#endif -->
+ <PRE>
+ scheme "://" SERVER_NAME ":" SERVER_PORT rel-URL-abs-path
+ </PRE>
+ <P>
+ Note: If the request was accompanied by a
+ message-body
+ (such as for a POST request), and the script
+ redirects the request with a Location field, the
+ message-body
+ may not be
+ available to the resource that is the target of the redirect.
+ </P>
+
+ <H4>
+ <A NAME="7.2.1.3">
+ 7.2.1.3. Status
+ </A>
+ </H4>
+ <P>
+ The "<SAMP>Status</SAMP>" header field is used to indicate to the server what
+ status code the server MUST use in the response message.
+ </P><!--#if expr="! $GUI" -->
+ <P></P><!--#endif -->
+ <PRE>
+ Status = "Status" ":" digit digit digit SP reason-phrase NL
+ reason-phrase = *&lt;CHAR, excluding CTLs, NL&gt;
+ </PRE>
+ <P>
+ The valid status codes are listed in section 6.1.1 of the HTTP/1.0
+ specifications [<A HREF="#[3]">3</A>]. If the SERVER_PROTOCOL is
+ "HTTP/1.1", then the status codes defined in the HTTP/1.1
+ specification [<A HREF="#[8]">8</A>] may
+ be used. If the script does not return a "<SAMP>Status</SAMP>" header
+ field, then "200 OK" SHOULD be assumed by the server.
+ </P>
+ <P>
+ If a script is being used to handle a particular error or condition
+ encountered by the server, such as a '404 Not Found' error, the script
+ SHOULD use the "<SAMP>Status</SAMP>" CGI header field to propagate the error
+ condition back to the client. <EM>E.g.</EM>, in the example mentioned it
+ SHOULD include a "Status:&nbsp;404&nbsp;Not&nbsp;Found" in the
+ header data returned to the server.
+ </P>
+
+ <H4>
+ <A NAME="7.2.1.4">
+ 7.2.1.4. Extension header fields
+ </A>
+ </H4>
+ <P>
+ Scripts MAY include in their CGI response header additional fields
+ not defined in this or the HTTP specification.
+ These are called "extension" fields,
+ and have the syntax of a <SAMP>generic-field</SAMP> as defined in
+ <A HREF="#7.2.1">section 7.2.1</A>. The name of an extension field
+ MUST NOT conflict with a field name defined in this or any other
+ specification; extension field names SHOULD begin with "X-CGI-"
+ to ensure uniqueness.
+ </P>
+
+ <H4>
+ <A NAME="7.2.2">
+ 7.2.2. HTTP header fields
+ </A>
+ </H4>
+ <P>
+ The script MAY return any other header fields defined by the
+ specification
+ for the SERVER_PROTOCOL (HTTP/1.0 [<A HREF="#[3]">3</A>] or HTTP/1.1
+ [<A HREF="#[8]">8</A>]).
+ Servers MUST resolve conflicts beteen CGI header
+ and HTTP header formats or names (see <A HREF="#8.0">section 8</A>).
+ </P>
+
+ <H2>
+ <A NAME="8.0">
+ 8. Server Implementation
+ </A>
+ </H2>
+ <P>
+ This section defines the requirements that must be met by HTTP
+ servers in order to provide a coherent and correct CGI/1.1
+ environment in which scripts may function. It is intended
+ primarily for server implementors, but it is useful for
+ script authors to be familiar with the information as well.
+ </P>
+
+ <H3>
+ <A NAME="8.1">
+ 8.1. Requirements for Servers
+ </A>
+ </H3>
+ <P>
+ In order to be considered CGI/1.1-compliant, a server must meet
+ certain basic criteria and provide certain minimal functionality.
+ The details of these requirements are described in the following sections.
+ </P>
+
+ <H3>
+ <A NAME="8.1.1">
+ 8.1.1. Script-URI
+ </A>
+ </H3>
+ <P>
+ Servers MUST support the standard mechanism (described below) which
+ allows
+ script authors to determine
+ what URL to use in documents
+ which reference the script;
+ specifically, what URL to use in order to
+ achieve particular settings of the
+ metavariables. This
+ mechanism is as follows:
+ </P>
+ <P>
+ The server
+ MUST translate the header data from the CGI header field syntax to
+ the HTTP
+ header field syntax if these differ. For example, the character
+ sequence for
+ newline (such as Unix's ASCII NL) used by CGI scripts may not be the
+ same as that used by HTTP (ASCII CR followed by LF). The server MUST
+ also resolve any conflicts between header fields returned by the script
+ and header fields that it would otherwise send itself.
+ </P>
+
+ <H3>
+ <A NAME="8.1.2">
+ 8.1.2. Request Message-body Handling
+ </A>
+ </H3>
+ <P>
+ These are the requirements for server handling of message-bodies directed
+ to CGI/1.1 resources:
+ </P>
+ <OL>
+ <LI>The message-body the server provides to the CGI script MUST
+ have any transfer encodings removed.
+ </LI>
+ <LI>The server MUST derive and provide a value for the CONTENT_LENGTH
+ metavariable that reflects the length of the message-body after any
+ transfer decoding.
+ </LI>
+ <LI>The server MUST leave intact any content-encodings of the message-body.
+ </LI>
+ </OL>
+
+ <H3>
+ <A NAME="8.1.3">
+ 8.1.3. Required Metavariables
+ </A>
+ </H3>
+ <P>
+ Servers MUST provide scripts with certain information and
+ metavariables
+ as described in <A HREF="#8.3">section 8.3</A>.
+ </P>
+
+ <H3>
+ <A NAME="8.1.4">
+ 8.1.4. Response Compliance
+ </A>
+ </H3>
+ <P>
+ Servers MUST ensure that responses sent to the user-agent meet all
+ requirements of the protocol level in effect. This may involve
+ modifying, deleting, or augmenting any header
+ fields and/or message-body supplied by the script.
+ </P>
+
+ <H3>
+ <A NAME="8.2">
+ 8.2. Recommendations for Servers
+ </A>
+ </H3>
+ <P>
+ Servers SHOULD provide the "<SAMP>query</SAMP>" component of the script-URI
+ as command-line arguments to scripts if it does not
+ contain any unencoded '=' characters and the command-line arguments can
+ be generated in an unambiguous manner.
+ (See <A HREF="#5.0">section 5</A>.)
+ </P>
+ <P>
+ Servers SHOULD set the AUTH_TYPE
+ metavariable to the value of the
+ '<SAMP>auth-scheme</SAMP>' token of the "<SAMP>Authorization</SAMP>"
+ field if it was supplied as part of the request header.
+ (See <A HREF="#6.1.1">section 6.1.1</A>.)
+ </P>
+ <P>
+ Where applicable, servers SHOULD set the current working directory
+ to the directory in which the script is located before invoking
+ it.
+ </P>
+ <P>
+ Servers MAY reject with error '404 Not Found'
+ any requests that would result in
+ an encoded "/" being decoded into PATH_INFO or SCRIPT_NAME, as this
+ might represent a loss of information to the script.
+ </P>
+ <P>
+ Although the server and the CGI script need not be consistent in
+ their handling of URL paths (client URLs and the PATH_INFO data,
+ respectively), server authors may wish to impose consistency.
+ So the server implementation SHOULD define its behaviour for the
+ following cases:
+ </P>
+ <OL>
+ <LI>define any restrictions on allowed characters, in particular
+ whether ASCII NUL is permitted;
+ </LI>
+ <LI>define any restrictions on allowed path segments, in particular
+ whether non-terminal NULL segments are permitted;
+ </LI>
+ <LI>define the behaviour for <SAMP>"."</SAMP> or <SAMP>".."</SAMP> path
+ segments; <EM>i.e.</EM>, whether they are prohibited, treated as
+ ordinary path
+ segments or interpreted in accordance with the relative URL
+ specification [<A HREF="#[7]">7</A>];
+ </LI>
+ <LI>define any limits of the implementation, including limits on path or
+ search string lengths, and limits on the volume of header data the server
+ will parse.
+ </LI><!-- ##### Move the field resolution/translation para below here -->
+ </OL>
+ <P>
+ Servers MAY generate the
+ Script-URI in
+ any way from the client URI,
+ or from any other data (but the behaviour SHOULD be documented).
+ </P>
+ <P>
+ For non-parsed header (NPH) scripts (see
+ <A HREF="#7.1">section 7.1</A>), servers SHOULD
+ attempt to ensure that the script input comes directly from the
+ client, with minimal buffering. For all scripts the data will be
+ as supplied by the client.
+ </P>
+
+ <H3>
+ <A NAME="8.3">
+ 8.3. Summary of
+ MetaVariables
+ </A>
+ </H3>
+ <P>
+ Servers MUST provide the following
+ metavariables to
+ scripts. See the individual descriptions for exceptions and semantics.
+ </P><!--#if expr="! $GUI" -->
+ <P></P><!--#endif -->
+ <PRE>
+ CONTENT_LENGTH (section <A HREF="#6.1.2">6.1.2</A>)
+ CONTENT_TYPE (section <A HREF="#6.1.3">6.1.3</A>)
+ GATEWAY_INTERFACE (section <A HREF="#6.1.4">6.1.4</A>)
+ PATH_INFO (section <A HREF="#6.1.6">6.1.6</A>)
+ QUERY_STRING (section <A HREF="#6.1.8">6.1.8</A>)
+ REMOTE_ADDR (section <A HREF="#6.1.9">6.1.9</A>)
+ REQUEST_METHOD (section <A HREF="#6.1.13">6.1.13</A>)
+ SCRIPT_NAME (section <A HREF="#6.1.14">6.1.14</A>)
+ SERVER_NAME (section <A HREF="#6.1.15">6.1.15</A>)
+ SERVER_PORT (section <A HREF="#6.1.16">6.1.16</A>)
+ SERVER_PROTOCOL (section <A HREF="#6.1.17">6.1.17</A>)
+ SERVER_SOFTWARE (section <A HREF="#6.1.18">6.1.18</A>)
+ </PRE>
+ <P>
+ Servers SHOULD define the following
+ metavariables for scripts.
+ See the individual descriptions for exceptions and semantics.
+ </P><!--#if expr="! $GUI" -->
+ <P></P><!--#endif -->
+ <PRE>
+ AUTH_TYPE (section <A HREF="#6.1.1">6.1.1</A>)
+ REMOTE_HOST (section <A HREF="#6.1.10">6.1.10</A>)
+ </PRE>
+ <P>
+ In addition, servers SHOULD provide
+ metavariables for all fields present
+ in the HTTP request header, with the exception of those involved with
+ access control. Servers MAY at their discretion provide
+ metavariables
+ for access control fields.
+ </P>
+ <P>
+ Servers MAY define the following
+ metavariables. See the individual
+ descriptions for exceptions and semantics.
+ </P><!--#if expr="! $GUI" -->
+ <P></P><!--#endif -->
+ <PRE>
+ PATH_TRANSLATED (section <A HREF="#6.1.7">6.1.7</A>)
+ REMOTE_IDENT (section <A HREF="#6.1.11">6.1.11</A>)
+ REMOTE_USER (section <A HREF="#6.1.12">6.1.12</A>)
+ </PRE>
+ <P>
+ Servers MAY
+ at their discretion define additional implementation-specific
+ extension metavariables
+ provided their names do not
+ conflict with defined header field names. Implementation-specific
+ metavariable names SHOULD
+ be prefixed with "X_" (<EM>e.g.</EM>,
+ "X_DBA") to avoid the potential for such conflicts.
+ </P>
+
+ <H2>
+ <A NAME="9.0">
+ 9.
+ Script Implementation
+ </A>
+ </H2>
+ <P>
+ This section defines the requirements and recommendations for scripts
+ that are intended to function in a CGI/1.1 environment. It is intended
+ primarily as a reference for script authors, but server implementors
+ should be familiar with these issues as well.
+ </P>
+
+ <H3>
+ <A NAME="9.1">
+ 9.1. Requirements for Scripts
+ </A>
+ </H3>
+ <P>
+ Scripts using the parsed-header method to communicate with servers
+ MUST supply a response header to the server.
+ (See <A HREF="#7.0">section 7</A>.)
+ </P>
+ <P>
+ Scripts using the NPH method to communicate with servers MUST
+ provide complete HTTP responses, and MUST use the value of the
+ SERVER_PROTOCOL metavariable
+ to determine the appropriate format.
+ (See <A HREF="#7.1">section 7.1</A>.)
+ </P>
+ <P>
+ Scripts MUST check the value of the REQUEST_METHOD
+ metavariable in order
+ to provide an appropriate response.
+ (See <A HREF="#6.1.13">section 6.1.13</A>.)
+ </P>
+ <P>
+ Scripts MUST be prepared to handled URL-encoded values in
+ metavariables.
+ In addition, they MUST recognise both "+" and "%20" in URL-encoded
+ quantities as representing the space character.
+ (See <A HREF="#3.1">section 3.1</A>.)
+ </P>
+ <P>
+ Scripts MUST ignore leading zeros in the major and minor version numbers
+ in the GATEWAY_INTERFACE
+ metavariable value. (See
+ <A HREF="#6.1.4">section 6.1.4</A>.)
+ </P>
+ <P>
+ When processing requests that include a
+ message-body, scripts
+ MUST NOT read more than CONTENT_LENGTH bytes from the input stream.
+ (See sections <A HREF="#6.1.2">6.1.2</A> and <A HREF="#6.2">6.2</A>.)
+ </P>
+
+ <H3>
+ <A NAME="9.2">
+ 9.2. Recommendations for Scripts
+ </A>
+ </H3>
+ <P>
+ Servers may interrupt or terminate script execution at any time
+ and without warning, so scripts SHOULD be prepared to deal with
+ abnormal termination.
+ </P>
+ <P>
+ Scripts MUST
+ reject with
+ error '405 Method Not
+ Allowed' requests
+ made using methods that they do not support. If the script does
+ not intend
+ processing the PATH_INFO data, then it SHOULD reject the request with
+ '404 Not
+ Found' if PATH_INFO is not NULL.
+ </P>
+ <P>
+ If a script is processing the output of a form, it SHOULD
+ verify that the CONTENT_TYPE
+ is "<SAMP>application/x-www-form-urlencoded</SAMP>" [<A HREF="#[2]">2</A>]
+ or whatever other media type is expected.
+ </P>
+ <P>
+ Scripts parsing PATH_INFO,
+ PATH_TRANSLATED, or SCRIPT_NAME
+ SHOULD be careful
+ of void path segments ("<SAMP>//</SAMP>") and special path segments
+ (<SAMP>"."</SAMP> and
+ <SAMP>".."</SAMP>). They SHOULD either be removed from the path before
+ use in OS
+ system calls, or the request SHOULD be rejected with
+ '404 Not Found'.
+ </P>
+ <P>
+ As it is impossible for
+ scripts to determine the client URI that
+ initiated a
+ request without knowledge of the specific server in
+ use, the script SHOULD NOT return "<SAMP>text/html</SAMP>"
+ documents containing
+ relative URL links without including a "<SAMP>&lt;BASE&gt;</SAMP>"
+ tag in the document.
+ </P>
+ <P>
+ When returning header fields,
+ scripts SHOULD try to send the CGI
+ header fields (see section
+ <A HREF="#7.2">7.2</A>) as soon as possible, and
+ SHOULD send them
+ before any HTTP header fields. This may
+ help reduce the server's memory requirements.
+ </P>
+
+ <H2>
+ <A NAME="10.0">
+ 10. System Specifications
+ </A>
+ </H2>
+
+ <H3>
+ <A NAME="10.1">
+ 10.1. AmigaDOS
+ </A>
+ </H3>
+ <P>
+ The implementation of the CGI on an AmigaDOS operating system platform
+ SHOULD use environment variables as the mechanism of providing
+ request metadata to CGI scripts.
+ </P>
+ <DL>
+ <DT><STRONG>Environment variables</STRONG>
+ </DT>
+ <DD>
+ <P>
+ These are accessed by the DOS library routine <SAMP>GetVar</SAMP>. The
+ flags argument SHOULD be 0. Case is ignored, but upper case is
+ recommended for compatibility with case-sensitive systems.
+ </P>
+ </DD>
+ <DT><STRONG>The current working directory</STRONG>
+ </DT>
+ <DD>
+ <P>
+ The current working directory for the script is set to the directory
+ containing the script.
+ </P>
+ </DD>
+ <DT><STRONG>Character set</STRONG>
+ </DT>
+ <DD>
+ <P>
+ The US-ASCII character set is used for the definition of environment
+ variable names and header
+ field names; the newline (NL) sequence is LF;
+ servers SHOULD also accept CR LF as a newline.
+ </P>
+ </DD>
+ </DL>
+
+ <H3>
+ <A NAME="10.2">
+ 10.2. Unix
+ </A>
+ </H3>
+ <P>
+ The implementation of the CGI on a UNIX operating system platform
+ SHOULD use environment variables as the mechanism of providing
+ request metadata to CGI scripts.
+ </P>
+ <P>
+ For Unix compatible operating systems, the following are defined:
+ </P>
+ <DL>
+ <DT><STRONG>Environment variables</STRONG>
+ </DT>
+ <DD>
+ <P>
+ These are accessed by the C library routine <SAMP>getenv</SAMP>.
+ </P>
+ </DD>
+ <DT><STRONG>The command line</STRONG>
+ </DT>
+ <DD>
+ <P>
+ This is accessed using the
+ <SAMP>argc</SAMP> and <SAMP>argv</SAMP>
+ arguments to <SAMP>main()</SAMP>. The words have any characters
+ that
+ are 'active' in the Bourne shell escaped with a backslash.
+ If the value of the QUERY_STRING
+ metavariable
+ contains an unencoded equals-sign '=', then the command line
+ SHOULD NOT be used by the script.
+ </P>
+ </DD>
+ <DT><STRONG>The current working directory</STRONG>
+ </DT>
+ <DD>
+ <P>
+ The current working directory for the script
+ SHOULD be set to the directory
+ containing the script.
+ </P>
+ </DD>
+ <DT><STRONG>Character set</STRONG>
+ </DT>
+ <DD>
+ <P>
+ The US-ASCII character set is used for the definition of environment
+ variable names and header field names; the newline (NL) sequence is LF;
+ servers SHOULD also accept CR LF as a newline.
+ </P>
+ </DD>
+ </DL>
+
+ <H2>
+ <A NAME="11.0">
+ 11. Security Considerations
+ </A>
+ </H2>
+
+ <H3>
+ <A NAME="11.1">
+ 11.1. Safe Methods
+ </A>
+ </H3>
+ <P>
+ As discussed in the security considerations of the HTTP
+ specifications [<A HREF="#[3]">3</A>,<A HREF="#[8]">8</A>], the
+ convention has been established that the
+ GET and HEAD methods should be 'safe'; they should cause no
+ side-effects and only have the significance of resource retrieval.
+ </P>
+ <P>
+ CGI scripts are responsible for enforcing any HTTP security considerations
+ [<A HREF="#[3]">3</A>,<A HREF="#[8]">8</A>]
+ with respect to the protocol version level of the request and
+ any side effects generated by the scripts on behalf of
+ the server. Primary
+ among these
+ are the considerations of safe and idempotent methods. Idempotent
+ requests are those that may be repeated an arbitrary number of times
+ and produce side effects identical to a single request.
+ </P>
+
+ <H3>
+ <A NAME="11.2">
+ 11.2. HTTP Header
+ Fields Containing Sensitive Information
+ </A>
+ </H3>
+ <P>
+ Some HTTP header fields may carry sensitive information which the server
+ SHOULD NOT pass on to the script unless explicitly configured to do
+ so. For example, if the server protects the script using the
+ "<SAMP>Basic</SAMP>"
+ authentication scheme, then the client will send an
+ "<SAMP>Authorization</SAMP>"
+ header field containing a username and password. If the server, rather
+ than the script, validates this information then the password SHOULD
+ NOT be passed on to the script <EM>via</EM> the HTTP_AUTHORIZATION
+ metavariable
+ without careful consideration.
+ This also applies to the
+ Proxy-Authorization header field and the corresponding
+ HTTP_PROXY_AUTHORIZATION
+ metavariable.
+ </P>
+
+ <H3>
+ <A NAME="11.3">
+ 11.3. Script
+ Interference with the Server
+ </A>
+ </H3>
+ <P>
+ The most common implementation of CGI invokes the script as a child
+ process using the same user and group as the server process. It
+ SHOULD therefore be ensured that the script cannot interfere with the
+ server process, its configuration, or documents.
+ </P>
+ <P>
+ If the script is executed by calling a function linked in to the
+ server software (either at compile-time or run-time) then precautions
+ SHOULD be taken to protect the core memory of the server, or to
+ ensure that untrusted code cannot be executed.
+ </P>
+
+ <H3>
+ <A NAME="11.4">
+ 11.4. Data Length and Buffering Considerations
+ </A>
+ </H3>
+ <P>
+ This specification places no limits on the length of message-bodies
+ presented to the script. Scripts should not assume that statically
+ allocated buffers of any size are sufficient to contain the entire
+ submission at one time. Use of a fixed length buffer without careful
+ overflow checking may result in an attacker exploiting 'stack-smashing'
+ or 'stack-overflow' vulnerabilities of the operating system.
+ Scripts may spool large submissions to disk or other buffering media,
+ but a rapid succession of large submissions may result in denial of
+ service conditions. If the CONTENT_LENGTH of a message-body is larger
+ than resource considerations allow, scripts should respond with an
+ error status appropriate for the protocol version; potentially applicable
+ status codes include '503 Service Unavailable' (HTTP/1.0 and HTTP/1.1),
+ '413 Request Entity Too Large' (HTTP/1.1), and
+ '414 Request-URI Too Long' (HTTP/1.1).
+ </P>
+
+ <H3>
+ <A NAME="11.5">
+ 11.5. Stateless Processing
+ </A>
+ </H3>
+ <P>
+ The stateless nature of the Web makes each script execution and resource
+ retrieval independent of all others even when multiple requests constitute a
+ single conceptual Web transaction. Because of this, a script should not
+ make any assumptions about the context of the user-agent submitting a
+ request. In particular, scripts should examine data obtained from the client
+ and verify that they are valid, both in form and content, before allowing
+ them to be used for sensitive purposes such as input to other
+ applications, commands, or operating system services. These uses
+ include, but are not
+ limited to: system call arguments, database writes, dynamically evaluated
+ source code, and input to billing or other secure processes. It is important
+ that applications be protected from invalid input regardless of whether
+ the invalidity is the result of user error, logic error, or malicious action.
+ </P>
+ <P>
+ Authors of scripts involved in multi-request transactions should be
+ particularly cautios about validating the state information;
+ undesirable effects may result from the substitution of dangerous
+ values for portions of the submission which might otherwise be
+ presumed safe. Subversion of this type occurs when alterations
+ are made to data from a prior stage of the transaction that were
+ not meant to be controlled by the client (<EM>e.g.</EM>, hidden
+ HTML form elements, cookies, embedded URLs, <EM>etc.</EM>).
+ </P>
+
+ <H2>
+ <A NAME="12.0">
+ 12. Acknowledgements
+ </A>
+ </H2>
+ <P>
+ This work is based on a draft published in 1997 by David R. Robinson,
+ which in turn was based on the original CGI interface that arose out of
+ discussions on the <EM>www-talk</EM> mailing list. In particular,
+ Rob McCool, John Franks, Ari Luotonen,
+ George Phillips and
+ Tony Sanders deserve special recognition for their efforts in
+ defining and implementing the early versions of this interface.
+ </P>
+ <P>
+ This document has also greatly benefited from the comments and
+ suggestions made by Chris Adie, Dave Kristol,
+ Mike Meyer, David Morris, Jeremy Madea,
+ Patrick M<SUP>c</SUP>Manus, Adam Donahue,
+ Ross Patterson, and Harald Alvestrand.
+ </P>
+
+ <H2>
+ <A NAME="13.0">
+ 13. References
+ </A>
+ </H2>
+ <DL COMPACT>
+ <DT><A NAME="[1]">[1]</A>
+ </DT>
+ <DD>Berners-Lee, T., 'Universal Resource Identifiers in WWW: A
+ Unifying Syntax for the Expression of Names and Addresses of
+ Objects on the Network as used in the World-Wide Web', RFC 1630,
+ CERN, June 1994.
+ <P>
+ </P>
+ </DD>
+ <DT><A NAME="[2]">[2]</A>
+ </DT>
+ <DD>Berners-Lee, T. and Connolly, D., 'Hypertext Markup Language -
+ 2.0', RFC 1866, MIT/W3C, November 1995.
+ <P>
+ </P>
+ </DD>
+ <DT><A NAME="[3]">[3]</A>
+ </DT>
+ <DD>Berners-Lee, T., Fielding, R. T. and Frystyk, H.,
+ 'Hypertext Transfer Protocol -- HTTP/1.0', RFC 1945, MIT/LCS,
+ UC Irvine, May 1996.
+ <P>
+ </P>
+ </DD>
+
+ <DT><A NAME="[4]">[4]</A>
+ </DT>
+ <DD>Berners-Lee, T., Fielding, R., and Masinter, L., Editors,
+ 'Uniform Resource Identifiers (URI): Generic Syntax', RFC 2396,
+ MIT, U.C. Irvine, Xerox Corporation, August 1996.
+ <P>
+ </P>
+ </DD>
+
+ <DT><A NAME="[5]">[5]</A>
+ </DT>
+ <DD>Braden, R., Editor, 'Requirements for Internet Hosts --
+ Application and Support', STD 3, RFC 1123, IETF, October 1989.
+ <P>
+ </P>
+ </DD>
+ <DT><A NAME="[6]">[6]</A>
+ </DT>
+ <DD>Crocker, D.H., 'Standard for the Format of ARPA Internet Text
+ Messages', STD 11, RFC 822, University of Delaware, August 1982.
+ <P>
+ </P>
+ </DD>
+ <DT><A NAME="[7]">[7]</A>
+ </DT>
+ <DD>Fielding, R., 'Relative Uniform Resource Locators', RFC 1808,
+ UC Irvine, June 1995.
+ <P>
+ </P>
+ </DD>
+ <DT><A NAME="[8]">[8]</A>
+ </DT>
+ <DD>Fielding, R., Gettys, J., Mogul, J., Frystyk, H. and
+ Berners-Lee, T., 'Hypertext Transfer Protocol -- HTTP/1.1',
+ RFC 2068, UC Irvine, DEC,
+ MIT/LCS, January 1997.
+ <P>
+ </P>
+ </DD>
+ <DT><A NAME="[9]">[9]</A>
+ </DT>
+ <DD>Freed, N. and Borenstein N., 'Multipurpose Internet Mail
+ Extensions (MIME) Part Two: Media Types', RFC 2046, Innosoft,
+ First Virtual, November 1996.
+ <P>
+ </P>
+ </DD>
+ <DT><A NAME="[10]">[10]</A>
+ </DT>
+ <DD>Mockapetris, P., 'Domain Names - Concepts and Facilities',
+ STD 13, RFC 1034, ISI, November 1987.
+ <P>
+ </P>
+ </DD>
+ <DT><A NAME="[11]">[11]</A>
+ </DT>
+ <DD>St. Johns, M., 'Identification Protocol', RFC 1431, US
+ Department of Defense, February 1993.
+ <P>
+ </P>
+ </DD>
+ <DT><A NAME="[12]">[12]</A>
+ </DT>
+ <DD>'Coded Character Set -- 7-bit American Standard Code for
+ Information Interchange', ANSI X3.4-1986.
+ <P>
+ </P>
+ </DD>
+ <DT><A NAME="[13]">[13]</A>
+ </DT>
+ <DD>Hinden, R. and Deering, S.,
+ 'IP Version 6 Addressing Architecture', RFC 2373,
+ Nokia, Cisco Systems,
+ July 1998.
+ <P>
+ </P>
+ </DD>
+ </DL>
+
+ <H2>
+ <A NAME="14.0">
+ 14. Authors' Addresses
+ </A>
+ </H2>
+ <ADDRESS>
+ <P>
+ Ken A L Coar
+ <BR>
+ MeepZor Consulting
+ <BR>
+ 7824 Mayfaire Crest Lane, Suite 202
+ <BR>
+ Raleigh, NC 27615-4875
+ <BR>
+ U.S.A.
+ </P>
+ <P>
+ Tel: +1 (919) 254.4237
+ <BR>
+ Fax: +1 (919) 254.5250
+ <BR>
+ Email:
+ <A
+ HREF="mailto:Ken.Coar@Golux.Com"
+ ><SAMP>Ken.Coar@Golux.Com</SAMP></A>
+ </P>
+ </ADDRESS>
+ <ADDRESS>
+ <P>
+ David Robinson
+ <BR>
+ E*TRADE UK Ltd
+ <BR>
+ Mount Pleasant House
+ <BR>
+ 2 Mount Pleasant
+ <BR>
+ Huntingdon Road
+ <BR>
+ Cambridge CB3 0RN
+ <BR>
+ UK
+ </P>
+ <P>
+ Tel: +44 (1223) 566926
+ <BR>
+ Fax: +44 (1223) 506288
+ <BR>
+ Email:
+ <A
+ HREF="mailto:drtr@etrade.co.uk"
+ ><SAMP>drtr@etrade.co.uk</SAMP></A>
+ </ADDRESS>
+
+ </BODY>
+</HTML>
diff --git a/i/pc104/initrd/conf/busybox/docs/ipv4_ipv6.txt b/i/pc104/initrd/conf/busybox/docs/ipv4_ipv6.txt
new file mode 100644
index 0000000..92010b5
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/docs/ipv4_ipv6.txt
@@ -0,0 +1,223 @@
+We need better network address conv helpers.
+This is what our applets want:
+
+ sockaddr -> hostname
+udhcp: hostname -> ipv4 addr
+nslookup: hostname -> list of names - done
+tftp: host,port -> sockaddr
+nc: host,port -> sockaddr
+inetd: ?
+traceroute: ?, hostname -> ipv4 addr
+arping hostname -> ipv4 addr
+ping6 hostname -> ipv6 addr
+ifconfig hostname -> ipv4 addr (FIXME error check?)
+ipcalc ipv4 addr -> hostname
+syslogd hostname -> sockaddr
+inet_common.c: buggy. hostname -> ipv4 addr
+mount hostname -> sockaddr_in
+
+==================
+HOWTO get rid of inet_ntoa/aton:
+
+foo.sin_addr.s_addr = inet_addr(cp);
+-
+inet_pton(AF_INET, cp, &foo.sin_addr);
+
+inet_aton(cp, &foo.sin_addr);
+-
+inet_pton(AF_INET, cp, &foo.sin_addr);
+
+ptr = inet_ntoa(foo.sin_addr);
+-
+char str[INET_ADDRSTRLEN];
+ptr = inet_ntop(AF_INET, &foo.sin_addr, str, sizeof(str));
+
+===================
+
+ struct addrinfo {
+ int ai_flags;
+ int ai_family;
+ int ai_socktype;
+ int ai_protocol;
+ size_t ai_addrlen;
+ struct sockaddr *ai_addr;
+ char *ai_canonname;
+ struct addrinfo *ai_next;
+ };
+ int getaddrinfo(const char *node, const char *service,
+ const struct addrinfo *hints,
+ struct addrinfo **res);
+
+ void freeaddrinfo(struct addrinfo *res);
+
+ const char *gai_strerror(int errcode);
+
+ The members ai_family, ai_socktype, and ai_protocol have the same meaning
+ as the corresponding parameters in the socket(2) system call. The getad-
+ drinfo(3) function returns socket addresses in either IPv4 or IPv6 address
+ family, (ai_family will be set to either AF_INET or AF_INET6).
+
+ The hints parameter specifies the preferred socket type, or protocol. A
+ NULL hints specifies that any network address or protocol is acceptable.
+ If this parameter is not NULL it points to an addrinfo structure whose
+ ai_family, ai_socktype, and ai_protocol members specify the preferred
+ socket type. AF_UNSPEC in ai_family specifies any protocol family (either
+ IPv4 or IPv6, for example). 0 in ai_socktype or ai_protocol specifies
+ that any socket type or protocol is acceptable as well. The ai_flags mem-
+ ber specifies additional options, defined below. Multiple flags are spec-
+ ified by logically OR-ing them together. All the other members in the
+ hints parameter must contain either 0, or a null pointer.
+
+ The node or service parameter, but not both, may be NULL. node specifies
+ either a numerical network address (dotted-decimal format for IPv4, hex-
+ adecimal format for IPv6) or a network hostname, whose network addresses
+ are looked up and resolved. If hints.ai_flags contains the AI_NUMERICHOST
+ flag then the node parameter must be a numerical network address. The
+ AI_NUMERICHOST flag suppresses any potentially lengthy network host
+ address lookups.
+
+ The getaddrinfo(3) function creates a linked list of addrinfo structures,
+ one for each network address subject to any restrictions imposed by the
+ hints parameter. The ai_canonname field of the first of these addrinfo
+ structures is set to point to the official name of the host, if
+ hints.ai_flags includes the AI_CANONNAME flag. ai_family, ai_socktype,
+ and ai_protocol specify the socket creation parameters. A pointer to the
+ socket address is placed in the ai_addr member, and the length of the
+ socket address, in bytes, is placed in the ai_addrlen member.
+
+ If node is NULL, the network address in each socket structure is initial-
+ ized according to the AI_PASSIVE flag, which is set in hints.ai_flags.
+ The network address in each socket structure will be left unspecified if
+ AI_PASSIVE flag is set. This is used by server applications, which intend
+ to accept client connections on any network address. The network address
+ will be set to the loopback interface address if the AI_PASSIVE flag is
+ not set. This is used by client applications, which intend to connect to
+ a server running on the same network host.
+
+ If hints.ai_flags includes the AI_ADDRCONFIG flag, then IPv4 addresses are
+ returned in the list pointed to by result only if the local system has at
+ least has at least one IPv4 address configured, and IPv6 addresses are
+ only returned if the local system has at least one IPv6 address config-
+ ured.
+
+ If hint.ai_flags specifies the AI_V4MAPPED flag, and hints.ai_family was
+ specified as AF_INET6, and no matching IPv6 addresses could be found, then
+ return IPv4-mapped IPv6 addresses in the list pointed to by result. If
+ both AI_V4MAPPED and AI_ALL are specified in hints.ai_family, then return
+ both IPv6 and IPv4-mapped IPv6 addresses in the list pointed to by result.
+ AI_ALL is ignored if AI_V4MAPPED is not also specified.
+
+ service sets the port number in the network address of each socket struc-
+ ture. If service is NULL the port number will be left uninitialized. If
+ AI_NUMERICSERV is specified in hints.ai_flags and service is not NULL,
+ then service must point to a string containing a numeric port number.
+ This flag is used to inhibit the invocation of a name resolution service
+ in cases where it is known not to be required.
+
+
+==============
+
+ int getnameinfo(const struct sockaddr *sa, socklen_t salen,
+ char *host, size_t hostlen,
+ char *serv, size_t servlen, int flags);
+
+ The getnameinfo(3) function is defined for protocol-independent
+ address-to-nodename translation. It combines the functionality
+ of gethostbyaddr(3) and getservbyport(3) and is the inverse of
+ getaddrinfo(3). The sa argument is a pointer to a generic socket address
+ structure (of type sockaddr_in or sockaddr_in6) of size salen that
+ holds the input IP address and port number. The arguments host and
+ serv are pointers to buffers (of size hostlen and servlen respectively)
+ to hold the return values.
+
+ The caller can specify that no hostname (or no service name) is required
+ by providing a NULL host (or serv) argument or a zero hostlen (or servlen)
+ parameter. However, at least one of hostname or service name must be requested.
+
+ The flags argument modifies the behaviour of getnameinfo(3) as follows:
+
+ NI_NOFQDN
+ If set, return only the hostname part of the FQDN for local hosts.
+
+ NI_NUMERICHOST
+ If set, then the numeric form of the hostname is returned.
+ (When not set, this will still happen in case the node's name
+ cannot be looked up.)
+
+ NI_NAMEREQD
+ If set, then a error is returned if the hostname cannot be looked up.
+
+ NI_NUMERICSERV
+ If set, then the service address is returned in numeric form,
+ for example by its port number.
+
+ NI_DGRAM
+ If set, then the service is datagram (UDP) based rather than stream
+ (TCP) based. This is required for the few ports (512-514) that have different
+ services for UDP and TCP.
+
+=================
+
+Modified IPv6-aware C code:
+
+ struct addrinfo *res, *aip;
+ struct addrinfo hints;
+ int sock = -1;
+ int error;
+
+ /* Get host address. Any type of address will do. */
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_ALL|AI_ADDRCONFIG;
+ hints.ai_socktype = SOCK_STREAM;
+
+ error = getaddrinfo(hostname, servicename, &hints, &res);
+ if (error != 0) {
+ (void) fprintf(stderr,
+ "getaddrinfo: %s for host %s service %s\n",
+ gai_strerror(error), hostname, servicename);
+ return -1;
+ }
+ /* Try all returned addresses until one works */
+ for (aip = res; aip != NULL; aip = aip->ai_next) {
+ /*
+ * Open socket. The address type depends on what
+ * getaddrinfo() gave us.
+ */
+ sock = socket(aip->ai_family, aip->ai_socktype, aip->ai_protocol);
+ if (sock == -1) {
+ perror("socket");
+ freeaddrinfo(res);
+ return -1;
+ }
+
+ /* Connect to the host. */
+ if (connect(sock, aip->ai_addr, aip->ai_addrlen) == -1) {
+ perror("connect");
+ (void) close(sock);
+ sock = -1;
+ continue;
+ }
+ break;
+ }
+ freeaddrinfo(res);
+
+Note that for new applications, if you write address-family-agnostic data structures,
+there is no need for porting.
+
+However, when it comes to server-side programming in C/C++, there is an additional wrinkle.
+Namely, depending on whether your application is written for a dual-stack platform, such
+as Solaris or Linux, or a single-stack platform, such as Windows, you would need to
+structure the code differently.
+
+Here's the corresponding server C code for a dual-stack platform:
+
+ int ServSock, csock;
+ /* struct sockaddr is too small! */
+ struct sockaddr_storage addr, from;
+ ...
+ ServSock = socket(AF_INET6, SOCK_STREAM, PF_INET6);
+ bind(ServSock, &addr, sizeof(addr));
+ do {
+ csock = accept(ServSocket, &from, sizeof(from));
+ doClientStuff(csock);
+ } while (!finished);
diff --git a/i/pc104/initrd/conf/busybox/docs/keep_data_small.txt b/i/pc104/initrd/conf/busybox/docs/keep_data_small.txt
new file mode 100644
index 0000000..3c3d273
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/docs/keep_data_small.txt
@@ -0,0 +1,206 @@
+ Keeping data small
+
+When many applets are compiled into busybox, all rw data and
+bss for each applet are concatenated. Including those from libc,
+if static busybox is built. When busybox is started, _all_ this data
+is allocated, not just that one part for selected applet.
+
+What "allocated" exactly means, depends on arch.
+On NOMMU it's probably bites the most, actually using real
+RAM for rwdata and bss. On i386, bss is lazily allocated
+by COWed zero pages. Not sure about rwdata - also COW?
+
+In order to keep busybox NOMMU and small-mem systems friendly
+we should avoid large global data in our applets, and should
+minimize usage of libc functions which implicitly use
+such structures.
+
+Small experiment to measure "parasitic" bbox memory consumption:
+here we start 1000 "busybox sleep 10" in parallel.
+busybox binary is practically allyesconfig static one,
+built against uclibc. Run on x86-64 machine with 64-bit kernel:
+
+bash-3.2# nmeter '%t %c %m %p %[pn]'
+23:17:28 .......... 168M 0 147
+23:17:29 .......... 168M 0 147
+23:17:30 U......... 168M 1 147
+23:17:31 SU........ 181M 244 391
+23:17:32 SSSSUUU... 223M 757 1147
+23:17:33 UUU....... 223M 0 1147
+23:17:34 U......... 223M 1 1147
+23:17:35 .......... 223M 0 1147
+23:17:36 .......... 223M 0 1147
+23:17:37 S......... 223M 0 1147
+23:17:38 .......... 223M 1 1147
+23:17:39 .......... 223M 0 1147
+23:17:40 .......... 223M 0 1147
+23:17:41 .......... 210M 0 906
+23:17:42 .......... 168M 1 147
+23:17:43 .......... 168M 0 147
+
+This requires 55M of memory. Thus 1 trivial busybox applet
+takes 55k of memory on 64-bit x86 kernel.
+
+On 32-bit kernel we need ~26k per applet.
+
+(Data from NOMMU arches are sought. Provide 'size busybox' output too)
+
+
+ Example 1
+
+One example how to reduce global data usage is in
+archival/libunarchive/decompress_unzip.c:
+
+/* This is somewhat complex-looking arrangement, but it allows
+ * to place decompressor state either in bss or in
+ * malloc'ed space simply by changing #defines below.
+ * Sizes on i386:
+ * text data bss dec hex
+ * 5256 0 108 5364 14f4 - bss
+ * 4915 0 0 4915 1333 - malloc
+ */
+#define STATE_IN_BSS 0
+#define STATE_IN_MALLOC 1
+
+(see the rest of the file to get the idea)
+
+This example completely eliminates globals in that module.
+Required memory is allocated in inflate_gunzip() [its main module]
+and then passed down to all subroutines which need to access 'globals'
+as a parameter.
+
+
+ Example 2
+
+In case you don't want to pass this additional parameter everywhere,
+take a look at archival/gzip.c. Here all global data is replaced by
+single global pointer (ptr_to_globals) to allocated storage.
+
+In order to not duplicate ptr_to_globals in every applet, you can
+reuse single common one. It is defined in libbb/messages.c
+as struct globals *const ptr_to_globals, but the struct globals is
+NOT defined in libbb.h. You first define your own struct:
+
+struct globals { int a; char buf[1000]; };
+
+and then declare that ptr_to_globals is a pointer to it:
+
+#define G (*ptr_to_globals)
+
+ptr_to_globals is declared as constant pointer.
+This helps gcc understand that it won't change, resulting in noticeably
+smaller code. In order to assign it, use PTR_TO_GLOBALS macro:
+
+ PTR_TO_GLOBALS = xzalloc(sizeof(G));
+
+Typically it is done in <applet>_main().
+
+Now you can reference "globals" by G.a, G.buf and so on, in any function.
+
+
+ bb_common_bufsiz1
+
+There is one big common buffer in bss - bb_common_bufsiz1. It is a much
+earlier mechanism to reduce bss usage. Each applet can use it for
+its needs. Library functions are prohibited from using it.
+
+'G.' trick can be done using bb_common_bufsiz1 instead of malloced buffer:
+
+#define G (*(struct globals*)&bb_common_bufsiz1)
+
+Be careful, though, and use it only if globals fit into bb_common_bufsiz1.
+Since bb_common_bufsiz1 is BUFSIZ + 1 bytes long and BUFSIZ can change
+from one libc to another, you have to add compile-time check for it:
+
+if(sizeof(struct globals) > sizeof(bb_common_bufsiz1))
+ BUG_<applet>_globals_too_big();
+
+
+ Drawbacks
+
+You have to initialize it by hand. xzalloc() can be helpful in clearing
+allocated storage to 0, but anything more must be done by hand.
+
+All global variables are prefixed by 'G.' now. If this makes code
+less readable, use #defines:
+
+#define dev_fd (G.dev_fd)
+#define sector (G.sector)
+
+
+ Word of caution
+
+If applet doesn't use much of global data, converting it to use
+one of above methods is not worth the resulting code obfuscation.
+If you have less than ~300 bytes of global data - don't bother.
+
+
+ gcc's data alignment problem
+
+The following attribute added in vi.c:
+
+static int tabstop;
+static struct termios term_orig __attribute__ ((aligned (4)));
+static struct termios term_vi __attribute__ ((aligned (4)));
+
+reduces bss size by 32 bytes, because gcc sometimes aligns structures to
+ridiculously large values. asm output diff for above example:
+
+ tabstop:
+ .zero 4
+ .section .bss.term_orig,"aw",@nobits
+- .align 32
++ .align 4
+ .type term_orig, @object
+ .size term_orig, 60
+ term_orig:
+ .zero 60
+ .section .bss.term_vi,"aw",@nobits
+- .align 32
++ .align 4
+ .type term_vi, @object
+ .size term_vi, 60
+
+gcc doesn't seem to have options for altering this behaviour.
+
+gcc 3.4.3 and 4.1.1 tested:
+char c = 1;
+// gcc aligns to 32 bytes if sizeof(struct) >= 32
+struct {
+ int a,b,c,d;
+ int i1,i2,i3;
+} s28 = { 1 }; // struct will be aligned to 4 bytes
+struct {
+ int a,b,c,d;
+ int i1,i2,i3,i4;
+} s32 = { 1 }; // struct will be aligned to 32 bytes
+// same for arrays
+char vc31[31] = { 1 }; // unaligned
+char vc32[32] = { 1 }; // aligned to 32 bytes
+
+-fpack-struct=1 reduces alignment of s28 to 1 (but probably
+will break layout of many libc structs) but s32 and vc32
+are still aligned to 32 bytes.
+
+I will try to cook up a patch to add a gcc option for disabling it.
+Meanwhile, this is where it can be disabled in gcc source:
+
+gcc/config/i386/i386.c
+int
+ix86_data_alignment (tree type, int align)
+{
+#if 0
+ if (AGGREGATE_TYPE_P (type)
+ && TYPE_SIZE (type)
+ && TREE_CODE (TYPE_SIZE (type)) == INTEGER_CST
+ && (TREE_INT_CST_LOW (TYPE_SIZE (type)) >= 256
+ || TREE_INT_CST_HIGH (TYPE_SIZE (type))) && align < 256)
+ return 256;
+#endif
+
+Result (non-static busybox built against glibc):
+
+# size /usr/srcdevel/bbox/fix/busybox.t0/busybox busybox
+ text data bss dec hex filename
+ 634416 2736 23856 661008 a1610 busybox
+ 632580 2672 22944 658196 a0b14 busybox_noalign
diff --git a/i/pc104/initrd/conf/busybox/docs/mdev.txt b/i/pc104/initrd/conf/busybox/docs/mdev.txt
new file mode 100644
index 0000000..51c3f0e
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/docs/mdev.txt
@@ -0,0 +1,68 @@
+-------------
+ MDEV Primer
+-------------
+
+For those of us who know how to use mdev, a primer might seem lame. For
+everyone else, mdev is a weird black box that they hear is awesome, but can't
+seem to get their head around how it works. Thus, a primer.
+
+-----------
+ Basic Use
+-----------
+
+Mdev has two primary uses: initial population and dynamic updates. Both
+require sysfs support in the kernel and have it mounted at /sys. For dynamic
+updates, you also need to have hotplugging enabled in your kernel.
+
+Here's a typical code snippet from the init script:
+[1] mount -t sysfs sysfs /sys
+[2] echo /bin/mdev > /proc/sys/kernel/hotplug
+[3] mdev -s
+
+Of course, a more "full" setup would entail executing this before the previous
+code snippet:
+[4] mount -t tmpfs mdev /dev
+[5] mkdir /dev/pts
+[6] mount -t devpts devpts /dev/pts
+
+The simple explanation here is that [1] you need to have /sys mounted before
+executing mdev. Then you [2] instruct the kernel to execute /bin/mdev whenever
+a device is added or removed so that the device node can be created or
+destroyed. Then you [3] seed /dev with all the device nodes that were created
+while the system was booting.
+
+For the "full" setup, you want to [4] make sure /dev is a tmpfs filesystem
+(assuming you're running out of flash). Then you want to [5] create the
+/dev/pts mount point and finally [6] mount the devpts filesystem on it.
+
+-------------
+ MDEV Config (/etc/mdev.conf)
+-------------
+
+Mdev has an optional config file for controlling ownership/permissions of
+device nodes if your system needs something more than the default root/root
+660 permissions.
+
+The file has the format:
+ <device regex> <uid>:<gid> <octal permissions>
+For example:
+ hd[a-z][0-9]* 0:3 660
+
+The config file parsing stops at the first matching line. If no line is
+matched, then the default of 0:0 660 is used. To set your own default, simply
+create your own total match like so:
+ .* 1:1 777
+
+If you also enable support for executing your own commands, then the file has
+the format:
+ <device regex> <uid>:<gid> <octal permissions> [<@|$|*> <command>]
+The special characters have the meaning:
+ @ Run after creating the device.
+ $ Run before removing the device.
+ * Run both after creating and before removing the device.
+
+The command is executed via the system() function (which means you're giving a
+command to the shell), so make sure you have a shell installed at /bin/sh.
+
+For your convenience, the shell env var $MDEV is set to the device name. So if
+the device 'hdc' was matched, MDEV would be set to "hdc".
diff --git a/i/pc104/initrd/conf/busybox/docs/new-applet-HOWTO.txt b/i/pc104/initrd/conf/busybox/docs/new-applet-HOWTO.txt
new file mode 100644
index 0000000..b1e1035
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/docs/new-applet-HOWTO.txt
@@ -0,0 +1,151 @@
+How to Add a New Applet to BusyBox
+==================================
+
+This document details the steps you must take to add a new applet to BusyBox.
+
+Credits:
+Matt Kraai - initial writeup
+Mark Whitley - the remix
+Thomas Lundquist - Added stuff for the new directory layout.
+
+Initial Write
+-------------
+
+First, write your applet. Be sure to include copyright information at the top,
+such as who you stole the code from and so forth. Also include the mini-GPL
+boilerplate. Be sure to name the main function <applet>_main instead of main.
+And be sure to put it in <applet>.c. Usage does not have to be taken care of by
+your applet.
+Make sure to #include "busybox.h" as the first include file in your applet so
+the bb_config.h and appropriate platform specific files are included properly.
+
+For a new applet mu, here is the code that would go in mu.c:
+
+----begin example code------
+
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini mu implementation for busybox
+ *
+ * Copyright (C) [YEAR] by [YOUR NAME] <YOUR EMAIL>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "busybox.h"
+#include <other.h>
+
+int mu_main(int argc, char **argv);
+int mu_main(int argc, char **argv)
+{
+ int fd;
+ char mu;
+
+ fd = xopen("/dev/random", O_RDONLY);
+
+ if ((n = safe_read(fd, &mu, 1)) < 1)
+ bb_perror_msg_and_die("/dev/random");
+
+ return mu;
+}
+
+----end example code------
+
+
+Coding Style
+------------
+
+Before you submit your applet for inclusion in BusyBox, (or better yet, before
+you _write_ your applet) please read through the style guide in the docs
+directory and make your program compliant.
+
+
+Some Words on libbb
+-------------------
+
+As you are writing your applet, please be aware of the body of pre-existing
+useful functions in libbb. Use these instead of reinventing the wheel.
+
+Additionally, if you have any useful, general-purpose functions in your
+applet that could be useful in other applets, consider putting them in libbb.
+
+
+Placement / Directory
+---------------------
+
+Find the appropriate directory for your new applet.
+
+Make sure you find the appropriate places in the files, the applets are
+sorted alphabetically.
+
+Add the applet to Makefile.in in the chosen directory:
+
+obj-$(CONFIG_MU) += mu.o
+
+Add the applet to Config.in in the chosen directory:
+
+config CONFIG_MU
+ bool "MU"
+ default n
+ help
+ Returns an indeterminate value.
+
+
+Usage String(s)
+---------------
+
+Next, add usage information for you applet to include/usage.h.
+This should look like the following:
+
+ #define mu_trivial_usage \
+ "-[abcde] FILES"
+ #define mu_full_usage \
+ "Returns an indeterminate value.\n\n" \
+ "Options:\n" \
+ "\t-a\t\tfirst function\n" \
+ "\t-b\t\tsecond function\n" \
+ ...
+
+If your program supports flags, the flags should be mentioned on the first
+line (-[abcde]) and a detailed description of each flag should go in the
+mu_full_usage section, one flag per line. (Numerous examples of this
+currently exist in usage.h.)
+
+
+Header Files
+------------
+
+Next, add an entry to include/applets.h. Be *sure* to keep the list
+in alphabetical order, or else it will break the binary-search lookup
+algorithm in busybox.c and the Gods of BusyBox smite you. Yea, verily:
+
+ /* all programs above here are alphabetically "less than" 'mu' */
+ #ifdef CONFIG_MU
+ APPLET("mu", mu_main, _BB_DIR_USR_BIN, mu_usage)
+ #endif
+ /* all programs below here are alphabetically "greater than" 'mu' */
+
+
+Documentation
+-------------
+
+If you're feeling especially nice, you should also document your applet in the
+docs directory (but nobody ever does that).
+
+Adding some text to docs/Configure.help is a nice start.
+
+
+The Grand Announcement
+----------------------
+
+Then create a diff -urN of the files you added and/or modified. Typically:
+ <appletdir>/<applet>.c
+ include/usage.c
+ include/applets.h
+ <appletdir>/Makefile.in
+ <appletdir>/config.in
+and send it to the mailing list:
+ busybox@busybox.net
+ http://busybox.net/mailman/listinfo/busybox
+
+Sending patches as attachments is preferred, but not required.
diff --git a/i/pc104/initrd/conf/busybox/docs/sigint.htm b/i/pc104/initrd/conf/busybox/docs/sigint.htm
new file mode 100644
index 0000000..e230f4d
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/docs/sigint.htm
@@ -0,0 +1,627 @@
+<HTML>
+<HEAD>
+<link rel="SHORTCUT ICON" href="http://www.cons.org/favicon.ico">
+<TITLE>Proper handling of SIGINT/SIGQUIT [http://www.cons.org/cracauer/sigint.html]</TITLE>
+<!-- Created by: GNU m4 using $Revision: 1.20 $ of crawww.m4lib on 11-Feb-2005 -->
+<BODY BGCOLOR="#fff8e1">
+<CENTER><H2>Proper handling of SIGINT/SIGQUIT</H2></CENTER>
+<img src=linie.png width="100%" alt=" ">
+<P>
+
+<table border=1 cellpadding=4>
+<tr><th valign=top align=left>Abstract: </th>
+<td valign=top align=left>
+In UNIX terminal sessions, you usually have a key like
+<code>C-c</code> (Control-C) to immediately end whatever program you
+have running in the foreground. This should work even when the program
+you called has called other programs in turn. Everything should be
+aborted, giving you your command prompt back, no matter how deep the
+call stack is.
+
+<p>Basically, it's trivial. But the existence of interactive
+applications that use SIGINT and/or SIGQUIT for other purposes than a
+complete immediate abort make matters complicated, and - as was to
+expect - left us with several ways to solve the problems. Of course,
+existing shells and applications follow different ways.
+
+<P>This Web pages outlines different ways to solve the problem and
+argues that only one of them can do everything right, although it
+means that we have to fix some existing software.
+
+
+
+</td></tr><tr><th valign=top align=left>Intended audience: </th>
+<td valign=top align=left>Programmers who implement programs that catch SIGINT/SIGQUIT.
+<BR>Programmers who implements shells or shell-like programs that
+execute batches of programs.
+
+<p>Users who have problems problems getting rid of runaway shell
+scripts using <code>Control-C</code>. Or have interactive applications
+that don't behave right when sending SIGINT. Examples are emacs'es
+that die on Control-g or shellscript statements that sometimes are
+executed and sometimes not, apparently not determined by the user's
+intention.
+
+
+</td></tr><tr><th valign=top align=left>Required knowledge: </th>
+<td valign=top align=left>You have to know what it means to catch SIGINT or SIGQUIT and how
+processes are waiting for other processes (childs) they spawned.
+
+
+</td></tr></table>
+<img src=linie.png width="100%" alt=" ">
+
+
+<H3>Basic concepts</H3>
+
+What technically happens when you press Control-C is that all programs
+running in the foreground in your current terminal (or virtual
+terminal) get the signal SIGINT sent.
+
+<p>You may change the key that triggers the signal using
+<code>stty</code> and running programs may remap the SIGINT-sending
+key at any time they like, without your intervention and without
+asking you first.
+
+<p>The usual reaction of a running program to SIGINT is to exit.
+However, not all program do an exit on SIGINT, programs are free to
+use the signal for other actions or to ignore it at all.
+
+<p>All programs running in the foreground receive the signal. This may
+be a nested "stack" of programs: You started a program that started
+another and the outer is waiting for the inner to exit. This nesting
+may be arbitrarily deep.
+
+<p>The innermost program is the one that decides what to do on SIGINT.
+It may exit, do something else or do nothing. Still, when the user hit
+SIGINT, all the outer programs are awaken, get the signal and may
+react on it.
+
+<H3>What we try to achieve</H3>
+
+The problem is with shell scripts (or similar programs that call
+several subprograms one after another).
+
+<p>Let us consider the most basic script:
+<PRE>
+#! /bin/sh
+program1
+program2
+</PRE>
+and the usual run looks like this:
+<PRE>
+$ sh myscript
+[output of program1]
+[output of program2]
+$
+</PRE>
+
+<p>Let us assume that both programs do nothing special on SIGINT, they
+just exit.
+
+<p>Now imagine the user hits C-c while a shellscript is executing its
+first program. The following programs receive SIGINT: program1 and
+also the shell executing the script. program1 exits.
+
+<p>But what should the shell do? If we say that it is only the
+innermost's programs business to react on SIGINT, the shell will do
+nothing special (not exit) and it will continue the execution of the
+script and run program2. But this is wrong: The user's intention in
+hitting C-c is to abort the whole script, to get his prompt back. If
+he hits C-c while the first program is running, he does not want
+program2 to be even started.
+
+<p>here is what would happen if the shell doesn't do anything:
+<PRE>
+$ sh myscript
+[first half of program1's output]
+C-c [users presses C-c]
+[second half of program1's output will not be displayed]
+[output of program2 will appear]
+</PRE>
+
+
+<p>Consider a more annoying example:
+<pre>
+#! /bin/sh
+# let's assume there are 300 *.dat files
+for file in *.dat ; do
+ dat2ascii $dat
+done
+</pre>
+
+If your shell wouldn't end if the user hits <code>C-c</code>,
+<code>C-c</code> would just end <strong>one</strong> dat2ascii run and
+the script would continue. Thus, you had to hit <code>C-c</code> up to
+300 times to end this script.
+
+<H3>Alternatives to do so</H3>
+
+<p>There are several ways to handle abortion of shell scripts when
+SIGINT is received while a foreground child runs:
+
+<menu>
+
+<li>As just outlined, the shellscript may just continue, ignoring the
+fact that the user hit <code>C-c</code>. That way, your shellscript -
+including any loops - would continue and you had no chance of aborting
+it except using the kill command after finding out the outermost
+shell's PID. This "solution" will not be discussed further, as it is
+obviously not desirable.
+
+<p><li>The shell itself exits immediately when it receives SIGINT. Not
+only the program called will exit, but the calling (the
+script-executing) shell. The first variant is to exit the shell (and
+therefore discontinuing execution of the script) immediately, while
+the background program may still be executing (remember that although
+the shell is just waiting for the called program to exit, it is woken
+up and may act). I will call the way of doing things the "IUE" (for
+"immediate unconditional exit") for the rest of this document.
+
+<p><li>As a variant of the former, when the shell receives SIGINT
+while it is waiting for a child to exit, the shell does not exit
+immediately. but it remembers the fact that a SIGINT happened. After
+the called program exits and the shell's wait ends, the shell will
+exit itself and hence discontinue the script. I will call the way of
+doing things the "WUE" (for "wait and unconditional exit") for the
+rest of this document.
+
+<p><li>There is also a way that the calling shell can tell whether the
+called program exited on SIGINT and if it ignored SIGINT (or used it
+for other purposes). As in the <sl>WUE</sl> way, the shell waits for
+the child to complete. It figures whether the program was ended on
+SIGINT and if so, it discontinue the script. If the program did any
+other exit, the script will be continued. I will call the way of doing
+things the "WCE" (for "wait and cooperative exit") for the rest of
+this document.
+
+</menu>
+
+<H3>The problem</H3>
+
+On first sight, all three solutions (IUE, WUE and WCE) all seem to do
+what we want: If C-c is hit while the first program of the shell
+script runs, the script is discontinued. The user gets his prompt back
+immediately. So what are the difference between these way of handling
+SIGINT?
+
+<p>There are programs that use the signal SIGINT for other purposes
+than exiting. They use it as a normal keystroke. The user is expected
+to use the key that sends SIGINT during a perfectly normal program
+run. As a result, the user sends SIGINT in situations where he/she
+does not want the program or the script to end.
+
+<p>The primary example is the emacs editor: C-g does what ESC does in
+other applications: It cancels a partially executed or prepared
+operation. Technically, emacs remaps the key that sends SIGINT from
+C-c to C-g and catches SIGINT.
+
+<p>Remember that the SIGINT is sent to all programs running in the
+foreground. If emacs is executing from a shell script, both emacs and
+the shell get SIGINT. emacs is the program that decides what to do:
+Exit on SIGINT or not. emacs decides not to exit. The problem arises
+when the shell draws its own conclusions from receiving SIGINT without
+consulting emacs for its opinion.
+
+<p>Consider this script:
+<PRE>
+#! /bin/sh
+emacs /tmp/foo
+cp /tmp/foo /home/user/mail/sent
+</PRE>
+
+<p>If C-g is used in emacs, both the shell and emacs will received
+SIGINT. Emacs will not exit, the user used C-g as a normal editing
+keystroke, he/she does not want the script to be aborted on C-g.
+
+<p>The central problem is that the second command (cp) may
+unintentionally be killed when the shell draws its own conclusion
+about the user's intention. The innermost program is the only one to
+judge.
+
+<H3>One more example</H3>
+
+<p>Imagine a mail session using a curses mailer in a tty. You called
+your mailer and started to compose a message. Your mailer calls emacs.
+<code>C-g</code> is a normal editing key in emacs. Technically it
+sends SIGINT (it was <code>C-c</code>, but emacs remapped the key) to
+<menu>
+<li>emacs
+<li>the shell between your mailer and emacs, the one from your mailers
+ system("emacs /tmp/bla.44") command
+<li>the mailer itself
+<li>possibly another shell if your mailer was called by a shell script
+or from another application using system(3)
+<li>your interactive shell (which ignores it since it is interactive
+and hence is not relevant to this discussion)
+</menu>
+
+<p>If everyone just exits on SIGINT, you will be left with nothing but
+your login shell, without asking.
+
+<p>But for sure you don't want to be dropped out of your editor and
+out of your mailer back to the commandline, having your edited data
+and mailer status deleted.
+
+<p>Understand the difference: While <code>C-g</code> is used an a kind
+of abort key in emacs, it isn't the major "abort everything" key. When
+you use <code>C-g</code> in emacs, you want to end some internal emacs
+command. You don't want your whole emacs and mailer session to end.
+
+<p>So, if the shell exits immediately if the user sends SIGINT (the
+second of the four ways shown above), the parent of emacs would die,
+leaving emacs without the controlling tty. The user will lose it's
+editing session immediately and unrecoverable. If the "main" shell of
+the operating system defaults to this behavior, every editor session
+that is spawned from a mailer or such will break (because it is
+usually executed by system(3), which calls /bin/sh). This was the case
+in FreeBSD before I and Bruce Evans changed it in 1998.
+
+<p>If the shell recognized that SIGINT was sent and exits after the
+current foreground process exited (the third way of the four), the
+editor session will not be disturbed, but things will still not work
+right.
+
+<H3>A further look at the alternatives</H3>
+
+<p>Still considering this script to examine the shell's actions in the
+IUE, WUE and ICE way of handling SIGINT:
+<PRE>
+#! /bin/sh
+emacs /tmp/foo
+cp /tmp/foo /home/user/mail/sent
+</PRE>
+
+<p>The IUE ("immediate unconditional exit") way does not work at all:
+emacs wants to survive the SIGINT (it's a normal editing key for
+emacs), but its parent shell unconditionally thinks "We received
+SIGINT. Abort everything. Now.". The shell will exit even before emacs
+exits. But this will leave emacs in an unusable state, since the death
+of its calling shell will leave it without required resources (file
+descriptors). This way does not work at all for shellscripts that call
+programs that use SIGINT for other purposes than immediate exit. Even
+for programs that exit on SIGINT, but want to do some cleanup between
+the signal and the exit, may fail before they complete their cleanup.
+
+<p>It should be noted that this way has one advantage: If a child
+blocks SIGINT and does not exit at all, this way will get control back
+to the user's terminal. Since such programs should be banned from your
+system anyway, I don't think that weighs against the disadvantages.
+
+<p>WUE ("wait and unconditional exit") is a little more clever: If C-g
+was used in emacs, the shell will get SIGINT. It will not immediately
+exit, but remember the fact that a SIGINT happened. When emacs ends
+(maybe a long time after the SIGINT), it will say "Ok, a SIGINT
+happened sometime while the child was executing, the user wants the
+script to be discontinued". It will then exit. The cp will not be
+executed. But that's bad. The "cp" will be executed when the emacs
+session ended without the C-g key ever used, but it will not be
+executed when the user used C-g at least one time. That is clearly not
+desired. Since C-g is a normal editing key in emacs, the user expects
+the rest of the script to behave identically no matter what keys he
+used.
+
+<p>As a result, the "WUE" way is better than the "IUE" way in that it
+does not break SIGINT-using programs completely. The emacs session
+will end undisturbed. But it still does not support scripts where
+other actions should be performed after a program that use SIGINT for
+non-exit purposes. Since the behavior is basically undeterminable for
+the user, this can lead to nasty surprises.
+
+<p>The "WCE" way fixes this by "asking" the called program whether it
+exited on SIGINT or not. While emacs receives SIGINT, it does not exit
+on it and a calling shell waiting for its exit will not be told that
+it exited on SIGINT. (Although it receives SIGINT at some point in
+time, the system does not enforce that emacs will exit with
+"I-exited-on-SIGINT" status. This is under emacs' control, see below).
+
+<p>this still work for the normal script without SIGINT-using
+programs:</p>
+<PRE>
+#! /bin/sh
+program1
+program2
+</PRE>
+
+Unless program1 and program2 mess around with signal handling, the
+system will tell the calling shell whether the programs exited
+normally or as a result of SIGINT.
+
+<p>The "WCE" way then has an easy way to things right: When one called
+program exited with "I-exited-on-SIGINT" status, it will discontinue
+the script after this program. If the program ends without this
+status, the next command in the script is started.
+
+<p>It is important to understand that a shell in "WCE" modus does not
+need to listen to the SIGINT signal at all. Both in the
+"emacs-then-cp" script and in the "several-normal-programs" script, it
+will be woken up and receive SIGINT when the user hits the
+corresponding key. But the shell does not need to react on this event
+and it doesn't need to remember the event of any SIGINT, either.
+Telling whether the user wants to end a script is done by asking that
+program that has to decide, that program that interprets keystrokes
+from the user, the innermost program.
+
+<H3>So everything is well with WCE?</H3>
+
+Well, almost.
+
+<p>The problem with the "WCE" modus is that there are broken programs
+that do not properly communicate the required information up to the
+calling program.
+
+<p>Unless a program messes with signal handling, the system does this
+automatically.
+
+<p>There are programs that want to exit on SIGINT, but they don't let
+the system do the automatic exit, because they want to do some
+cleanup. To do so, they catch SIGINT, do the cleanup and then exit by
+themselves.
+
+<p>And here is where the problem arises: Once they catch the signal,
+the system will no longer communicate the "I-exited-on-SIGINT" status
+to the calling program automatically. Even if the program exit
+immediately in the signal handler of SIGINT. Once it catches the
+signal, it has to take care of communicating the signal status
+itself.
+
+<p>Some programs don't do this. On SIGINT, they do cleanup and exit
+immediatly, but the calling shell isn't told about the non-normal exit
+and it will call the next program in the script.
+
+<p>As a result, the user hits SIGINT and while one program exits, the
+shellscript continues. To him/her it looks like the shell fails to
+obey to his abortion command.
+
+<p>Both IUE or WUE shell would not have this problem, since they
+discontinue the script on their own. But as I said, they don't support
+programs using SIGINT for non-exiting purposes, no matter whether
+these programs properly communicate their signal status to the calling
+shell or not.
+
+<p>Since some shell in wide use implement the WUE way (and some even
+IUE), there is a considerable number of broken programs out there that
+break WCE shells. The programmers just don't recognize it if their
+shell isn't WCE.
+
+<H3>How to be a proper program</H3>
+
+<p>(Short note in advance: What you need to achieve is that
+WIFSIGNALED(status) is true in the calling program and that
+WTERMSIG(status) returns SIGINT.)
+
+<p>If you don't catch SIGINT, the system automatically does the right
+thing for you: Your program exits and the calling program gets the
+right "I-exited-on-SIGINT" status after waiting for your exit.
+
+<p>But once you catch SIGINT, you have to act.
+
+<p>Decide whether the SIGINT is used for exit/abort purposes and hence
+a shellscript calling this program should discontinue. This is
+hopefully obvious. If you just need to do some cleanup on SIGINT, but
+then exit immediately, the answer is "yes".
+
+<p>If so, you have to tell the calling program about it by exiting
+with the "I-exited-on-SIGINT" status.
+
+<p>There is no other way of doing this than to kill yourself with a
+SIGINT signal. Do it by resetting the SIGINT handler to SIG_DFL, then
+send yourself the signal.
+
+<PRE>
+void sigint_handler(int sig)
+{
+ <do some cleanup>
+ signal(SIGINT, SIG_DFL);
+ kill(getpid(), SIGINT);
+}
+</PRE>
+
+Notes:
+
+<MENU>
+
+<LI>You cannot "fake" the proper exit status by an exit(3) with a
+special numeric value. People often assume this since the manuals for
+shells often list some return value for exactly this. But this is just
+a convention for your shell script. It does not work from one UNIX API
+program to another.
+
+<P>All that happens is that the shell sets the "$?" variable to a
+special numeric value for the convenience of your script, because your
+script does not have access to the lower-lever UNIX status evaluation
+functions. This is just an agreement between your script and the
+executing shell, it does not have any meaning in other contexts.
+
+<P><LI>Do not use kill(0, SIGINT) without consulting the manul for
+your OS implementation. I.e. on BSD, this would not send the signal to
+the current process, but to all processes in the group.
+
+<P><LI>POSIX 1003.1 allows all these calls to appear in signal
+handlers, so it is portable.
+
+</MENU>
+
+<p>In a bourne shell script, you can catch signals using the
+<code>trap</code> command. Here, the same as for C programs apply. If
+the intention of SIGINT is to end your program, you have to exit in a
+way that the calling programs "sees" that you have been killed. If
+you don't catch SIGINT, this happend automatically, but of you catch
+SIGINT, i.e. to do cleanup work, you have to end the program by
+killing yourself, not by calling exit.
+
+<p>Consider this example from FreeBSD's <code>mkdep</code>, which is a
+bourne shell script.
+
+<pre>
+TMP=_mkdep$$
+trap 'rm -f $TMP ; trap 2 ; kill -2 $$' 1 2 3 13 15
+</pre>
+
+Yes, you have to do it the hard way. It's even more annoying in shell
+scripts than in C programs since you can't "pre-delete" temporary
+files (which isn't really portable in C, though).
+
+<P>All this applies to programs in all languages, not only C and
+bourne shell. Every language implementation that lets you catch SIGINT
+should also give you the option to reset the signal and kill yourself.
+
+<P>It is always desireable to exit the right way, even if you don't
+expect your usual callers to depend on it, some unusual one will come
+along. This proper exit status will be needed for WCE and will not
+hurt when the calling shell uses IUE or WUE.
+
+<H3>How to be a proper shell</H3>
+
+All this applies only for the script-executing case. Most shells will
+also have interactive modes where things are different.
+
+<MENU>
+
+<LI>Do nothing special when SIGINT appears while you wait for a child.
+You don't even have to remember that one happened.
+
+<P><LI>Wait for child to exit, get the exit status. Do not truncate it
+to type char.
+
+<P><LI>Look at WIFSIGNALED(status) and WTERMSIG(status) to tell
+whether the child says "I exited on SIGINT: in my opinion the user
+wants the shellscript to be discontinued".
+
+<P><LI>If the latter applies, discontinue the script.
+
+<P><LI>Exit. But since a shellscript may in turn be called by a
+shellscript, you need to make sure that you properly communicate the
+discontinue intention to the calling program. As in any other program
+(see above), do
+
+<PRE>
+ signal(SIGINT, SIG_DFL);
+ kill(getpid(), SIGINT);
+</PRE>
+
+</MENU>
+
+<H3>Other remarks</H3>
+
+Although this web page talks about SIGINT only, almost the same issues
+apply to SIGQUIT, including proper exiting by killing yourself after
+catching the signal and proper reaction on the WIFSIGNALED(status)
+value. One notable difference for SIGQUIT is that you have to make
+sure that not the whole call tree dumps core.
+
+<H3>What to fight</H3>
+
+Make sure all programs <em>really</em> kill themselves if they react
+to SIGINT or SIGQUIT and intend to abort their operation as a result
+of this signal. Programs that don't use SIGINT/SIGQUIT as a
+termination trigger - but as part of normal operation - don't kill
+themselves, but do a normal exit instead.
+
+<p>Make sure people understand why you can't fake an exit-on-signal by
+doing exit(...) using any numerical status.
+
+<p>Make sure you use a shell that behaves right. Especially if you
+develop programs, since it will help seeing problems.
+
+<H3>Concrete examples how to fix programs:</H3>
+<ul>
+
+<li>The fix for FreeBSD's
+<A HREF="http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/time/time.c.diff?r1=1.10&r2=1.11">time(1)</A>. This fix is the best example, it's quite short and clear and
+it fixes a case where someone tried to fake signal exit status by a
+numerical value. And the complete program is small.
+
+<p><li>Fix for FreeBSD's
+<A HREF="http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/truss/main.c.diff?r1=1.9&r2=1.10">truss(1)</A>.
+
+<p><li>The fix for FreeBSD's
+<A HREF="http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/mkdep/mkdep.gcc.sh.diff?r1=1.8.2.1&r2=1.8.2.2">mkdep(1)</A>, a shell script.
+
+
+<p><li>Fix for FreeBSD's make(1), <A HREF="http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/make/job.c.diff?r1=1.9&r2=1.10">part 1</A>,
+<A HREF="http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/make/compat.c.diff?r1=1.10&r2=1.11">part 2</A>.
+
+</ul>
+
+<H3>Testsuite for shells</H3>
+
+I have a collection of shellscripts that test shells for the
+behavior. See my <A HREF="download/">download dir</A> to get the newest
+"sh-interrupt" files, either as a tarfile or as individual file for
+online browsing. This isn't really documented, besides from the
+comments the scripts echo.
+
+<H3>Appendix 1 - table of implementation choices</H3>
+
+<table border cellpadding=2>
+
+<tr valign=top>
+<th>Method sign</th>
+<th>Does what?</th>
+<th>Example shells that implement it:</th>
+<th>What happens when a shellscript called emacs, the user used
+<code>C-g</code> and the script has additional commands in it?</th>
+<th>What happens when a shellscript called emacs, the user did not use
+<code>C-c</code> and the script has additional commands in it?</th>
+<th>What happens if a non-interactive child catches SIGINT?</th>
+<th>To behave properly, childs must do what?</th>
+</tr>
+
+<tr valign=top align=left>
+<td>IUE</td>
+<td>The shell executing a script exits immediately if it receives
+SIGINT.</td>
+<td>4.4BSD ash (ash), NetBSD, FreeBSD prior to 3.0/22.8</td>
+<td>The editor session is lost and subsequent commands are not
+executed.</td>
+<td>The editor continues as normal and the subsequent commands are
+executed. </td>
+<td>The scripts ends immediately, returning to the caller even before
+the current foreground child of the shell exits. </td>
+<td>It doesn't matter what the child does or how it exits, even if the
+child continues to operate, the shell returns. </td>
+</tr>
+
+<tr valign=top align=left>
+<td>WUE</td>
+<td>If the shell executing a script received SIGINT while a foreground
+process was running, it will exit after that child's exit.</td>
+<td>pdksh (OpenBSD /bin/sh)</td>
+<td>The editor continues as normal, but subsequent commands from the
+script are not executed.</td>
+<td>The editor continues as normal and subsequent commands are
+executed. </td>
+<td>The scripts returns to its caller after the current foreground
+child exits, no matter how the child exited. </td>
+<td>It doesn't matter how the child exits (signal status or not), but
+if it doesn't return at all, the shell will not return. In no case
+will further commands from the script be executed. </td>
+</tr>
+
+<tr valign=top align=left>
+<td>WCE</td>
+<td>The shell exits if a child signaled that it was killed on a
+signal (either it had the default handler for SIGINT or it killed
+itself). </td>
+<td>bash (Linux /bin/sh), most commercial /bin/sh, FreeBSD /bin/sh
+from 3.0/2.2.8.</td>
+<td>The editor continues as normal and subsequent commands are
+executed. </td>
+<td>The editor continues as normal and subsequent commands are
+executed. </td>
+<td>The scripts returns to its caller after the current foreground
+child exits, but only if the child exited with signal status. If
+the child did a normal exit (even if it received SIGINT, but catches
+it), the script will continue. </td>
+<td>The child must be implemented right, or the user will not be able
+to break shell scripts reliably.</td>
+</tr>
+
+</table>
+
+<P><img src=linie.png width="100%" alt=" ">
+<BR>&copy;2005 Martin Cracauer &lt;cracauer @ cons.org&gt;
+<A HREF="http://www.cons.org/cracauer/">http://www.cons.org/cracauer/</A>
+<BR>Last changed: $Date: 2005/02/11 21:44:43 $
+</BODY></HTML>
diff --git a/i/pc104/initrd/conf/busybox/docs/style-guide.txt b/i/pc104/initrd/conf/busybox/docs/style-guide.txt
new file mode 100644
index 0000000..ba0cdba
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/docs/style-guide.txt
@@ -0,0 +1,689 @@
+Busybox Style Guide
+===================
+
+This document describes the coding style conventions used in Busybox. If you
+add a new file to Busybox or are editing an existing file, please format your
+code according to this style. If you are the maintainer of a file that does
+not follow these guidelines, please -- at your own convenience -- modify the
+file(s) you maintain to bring them into conformance with this style guide.
+Please note that this is a low priority task.
+
+To help you format the whitespace of your programs, an ".indent.pro" file is
+included in the main Busybox source directory that contains option flags to
+format code as per this style guide. This way you can run GNU indent on your
+files by typing 'indent myfile.c myfile.h' and it will magically apply all the
+right formatting rules to your file. Please _do_not_ run this on all the files
+in the directory, just your own.
+
+
+
+Declaration Order
+-----------------
+
+Here is the order in which code should be laid out in a file:
+
+ - commented program name and one-line description
+ - commented author name and email address(es)
+ - commented GPL boilerplate
+ - commented longer description / notes for the program (if needed)
+ - #includes of .h files with angle brackets (<>) around them
+ - #includes of .h files with quotes ("") around them
+ - #defines (if any, note the section below titled "Avoid the Preprocessor")
+ - const and global variables
+ - function declarations (if necessary)
+ - function implementations
+
+
+
+Whitespace and Formatting
+-------------------------
+
+This is everybody's favorite flame topic so let's get it out of the way right
+up front.
+
+
+Tabs vs. Spaces in Line Indentation
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The preference in Busybox is to indent lines with tabs. Do not indent lines
+with spaces and do not indents lines using a mixture of tabs and spaces. (The
+indentation style in the Apache and Postfix source does this sort of thing:
+\s\s\s\sif (expr) {\n\tstmt; --ick.) The only exception to this rule is
+multi-line comments that use an asterisk at the beginning of each line, i.e.:
+
+ \t/*
+ \t * This is a block comment.
+ \t * Note that it has multiple lines
+ \t * and that the beginning of each line has a tab plus a space
+ \t * except for the opening '/*' line where the slash
+ \t * is used instead of a space.
+ \t */
+
+Furthermore, The preference is that tabs be set to display at four spaces
+wide, but the beauty of using only tabs (and not spaces) at the beginning of
+lines is that you can set your editor to display tabs at *whatever* number of
+spaces is desired and the code will still look fine.
+
+
+Operator Spacing
+~~~~~~~~~~~~~~~~
+
+Put spaces between terms and operators. Example:
+
+ Don't do this:
+
+ for(i=0;i<num_items;i++){
+
+ Do this instead:
+
+ for (i = 0; i < num_items; i++) {
+
+ While it extends the line a bit longer, the spaced version is more
+ readable. An allowable exception to this rule is the situation where
+ excluding the spacing makes it more obvious that we are dealing with a
+ single term (even if it is a compound term) such as:
+
+ if (str[idx] == '/' && str[idx-1] != '\\')
+
+ or
+
+ if ((argc-1) - (optind+1) > 0)
+
+
+Bracket Spacing
+~~~~~~~~~~~~~~~
+
+If an opening bracket starts a function, it should be on the
+next line with no spacing before it. However, if a bracket follows an opening
+control block, it should be on the same line with a single space (not a tab)
+between it and the opening control block statement. Examples:
+
+ Don't do this:
+
+ while (!done)
+ {
+
+ do
+ {
+
+ Don't do this either:
+
+ while (!done){
+
+ do{
+
+ And for heaven's sake, don't do this:
+
+ while (!done)
+ {
+
+ do
+ {
+
+ Do this instead:
+
+ while (!done) {
+
+ do {
+
+Exceptions:
+
+ - if you have long logic statements that need to be wrapped, then uncuddling
+ the bracket to improve readability is allowed:
+
+ if (some_really_long_checks && some_other_really_long_checks \
+ && some_more_really_long_checks)
+ {
+ do_foo_now;
+
+Spacing around Parentheses
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Put a space between C keywords and left parens, but not between function names
+and the left paren that starts it's parameter list (whether it is being
+declared or called). Examples:
+
+ Don't do this:
+
+ while(foo) {
+ for(i = 0; i < n; i++) {
+
+ Do this instead:
+
+ while (foo) {
+ for (i = 0; i < n; i++) {
+
+ But do functions like this:
+
+ static int my_func(int foo, char bar)
+ ...
+ baz = my_func(1, 2);
+
+Also, don't put a space between the left paren and the first term, nor between
+the last arg and the right paren.
+
+ Don't do this:
+
+ if ( x < 1 )
+ strcmp( thisstr, thatstr )
+
+ Do this instead:
+
+ if (x < 1)
+ strcmp(thisstr, thatstr)
+
+
+Cuddled Elses
+~~~~~~~~~~~~~
+
+Also, please "cuddle" your else statements by putting the else keyword on the
+same line after the right bracket that closes an 'if' statement.
+
+ Don't do this:
+
+ if (foo) {
+ stmt;
+ }
+ else {
+ stmt;
+ }
+
+ Do this instead:
+
+ if (foo) {
+ stmt;
+ } else {
+ stmt;
+ }
+
+The exception to this rule is if you want to include a comment before the else
+block. Example:
+
+ if (foo) {
+ stmts...
+ }
+ /* otherwise, we're just kidding ourselves, so re-frob the input */
+ else {
+ other_stmts...
+ }
+
+
+
+Variable and Function Names
+---------------------------
+
+Use the K&R style with names in all lower-case and underscores occasionally
+used to separate words (e.g., "variable_name" and "numchars" are both
+acceptable). Using underscores makes variable and function names more readable
+because it looks like whitespace; using lower-case is easy on the eyes.
+
+ Frowned upon:
+
+ hitList
+ TotalChars
+ szFileName
+ pf_Nfol_TriState
+
+ Preferred:
+
+ hit_list
+ total_chars
+ file_name
+ sensible_name
+
+Exceptions:
+
+ - Enums, macros, and constant variables are occasionally written in all
+ upper-case with words optionally seperatedy by underscores (i.e. FIFOTYPE,
+ ISBLKDEV()).
+
+ - Nobody is going to get mad at you for using 'pvar' as the name of a
+ variable that is a pointer to 'var'.
+
+
+Converting to K&R
+~~~~~~~~~~~~~~~~~
+
+The Busybox codebase is very much a mixture of code gathered from a variety of
+sources. This explains why the current codebase contains such a hodge-podge of
+different naming styles (Java, Pascal, K&R, just-plain-weird, etc.). The K&R
+guideline explained above should therefore be used on new files that are added
+to the repository. Furthermore, the maintainer of an existing file that uses
+alternate naming conventions should, at his own convenience, convert those
+names over to K&R style. Converting variable names is a very low priority
+task.
+
+If you want to do a search-and-replace of a single variable name in different
+files, you can do the following in the busybox directory:
+
+ $ perl -pi -e 's/\bOldVar\b/new_var/g' *.[ch]
+
+If you want to convert all the non-K&R vars in your file all at once, follow
+these steps:
+
+ - In the busybox directory type 'examples/mk2knr.pl files-to-convert'. This
+ does not do the actual conversion, rather, it generates a script called
+ 'convertme.pl' that shows what will be converted, giving you a chance to
+ review the changes beforehand.
+
+ - Review the 'convertme.pl' script that gets generated in the busybox
+ directory and remove / edit any of the substitutions in there. Please
+ especially check for false positives (strings that should not be
+ converted).
+
+ - Type './convertme.pl same-files-as-before' to perform the actual
+ conversion.
+
+ - Compile and see if everything still works.
+
+Please be aware of changes that have cascading effects into other files. For
+example, if you're changing the name of something in, say utility.c, you
+should probably run 'examples/mk2knr.pl utility.c' at first, but when you run
+the 'convertme.pl' script you should run it on _all_ files like so:
+'./convertme.pl *.[ch]'.
+
+
+
+Avoid The Preprocessor
+----------------------
+
+At best, the preprocessor is a necessary evil, helping us account for platform
+and architecture differences. Using the preprocessor unnecessarily is just
+plain evil.
+
+
+The Folly of #define
+~~~~~~~~~~~~~~~~~~~~
+
+Use 'const <type> var' for declaring constants.
+
+ Don't do this:
+
+ #define var 80
+
+ Do this instead, when the variable is in a header file and will be used in
+ several source files:
+
+ const int var = 80;
+
+ Or do this when the variable is used only in a single source file:
+
+ static const int var = 80;
+
+Declaring variables as '[static] const' gives variables an actual type and
+makes the compiler do type checking for you; the preprocessor does _no_ type
+checking whatsoever, making it much more error prone. Declaring variables with
+'[static] const' also makes debugging programs much easier since the value of
+the variable can be easily queried and displayed.
+
+
+The Folly of Macros
+~~~~~~~~~~~~~~~~~~~
+
+Use 'static inline' instead of a macro.
+
+ Don't do this:
+
+ #define mini_func(param1, param2) (param1 << param2)
+
+ Do this instead:
+
+ static inline int mini_func(int param1, param2)
+ {
+ return (param1 << param2);
+ }
+
+Static inline functions are greatly preferred over macros. They provide type
+safety, have no length limitations, no formatting limitations, have an actual
+return value, and under gcc they are as cheap as macros. Besides, really long
+macros with backslashes at the end of each line are ugly as sin.
+
+
+The Folly of #ifdef
+~~~~~~~~~~~~~~~~~~~
+
+Code cluttered with ifdefs is difficult to read and maintain. Don't do it.
+Instead, put your ifdefs at the top of your .c file (or in a header), and
+conditionally define 'static inline' functions, (or *maybe* macros), which are
+used in the code.
+
+ Don't do this:
+
+ ret = my_func(bar, baz);
+ if (!ret)
+ return -1;
+ #ifdef CONFIG_FEATURE_FUNKY
+ maybe_do_funky_stuff(bar, baz);
+ #endif
+
+ Do this instead:
+
+ (in .h header file)
+
+ #ifdef CONFIG_FEATURE_FUNKY
+ static inline void maybe_do_funky_stuff (int bar, int baz)
+ {
+ /* lotsa code in here */
+ }
+ #else
+ static inline void maybe_do_funky_stuff (int bar, int baz) {}
+ #endif
+
+ (in the .c source file)
+
+ ret = my_func(bar, baz);
+ if (!ret)
+ return -1;
+ maybe_do_funky_stuff(bar, baz);
+
+The great thing about this approach is that the compiler will optimize away
+the "no-op" case (the empty function) when the feature is turned off.
+
+Note also the use of the word 'maybe' in the function name to indicate
+conditional execution.
+
+
+
+Notes on Strings
+----------------
+
+Strings in C can get a little thorny. Here's some guidelines for dealing with
+strings in Busybox. (There is surely more that could be added to this
+section.)
+
+
+String Files
+~~~~~~~~~~~~
+
+Put all help/usage messages in usage.c. Put other strings in messages.c.
+Putting these strings into their own file is a calculated decision designed to
+confine spelling errors to a single place and aid internationalization
+efforts, if needed. (Side Note: we might want to use a single file - maybe
+called 'strings.c' - instead of two, food for thought).
+
+
+Testing String Equivalence
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+There's a right way and a wrong way to test for sting equivalence with
+strcmp():
+
+ The wrong way:
+
+ if (!strcmp(string, "foo")) {
+ ...
+
+ The right way:
+
+ if (strcmp(string, "foo") == 0){
+ ...
+
+The use of the "equals" (==) operator in the latter example makes it much more
+obvious that you are testing for equivalence. The former example with the
+"not" (!) operator makes it look like you are testing for an error. In a more
+perfect world, we would have a streq() function in the string library, but
+that ain't the world we're living in.
+
+
+Avoid Dangerous String Functions
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Unfortunately, the way C handles strings makes them prone to overruns when
+certain library functions are (mis)used. The following table offers a summary
+of some of the more notorious troublemakers:
+
+function overflows preferred
+----------------------------------------
+strcpy dest string strncpy
+strcat dest string strncat
+gets string it gets fgets
+getwd buf string getcwd
+[v]sprintf str buffer [v]snprintf
+realpath path buffer use with pathconf
+[vf]scanf its arguments just avoid it
+
+
+The above is by no means a complete list. Be careful out there.
+
+
+
+Avoid Big Static Buffers
+------------------------
+
+First, some background to put this discussion in context: Static buffers look
+like this in code:
+
+ /* in a .c file outside any functions */
+ static char buffer[BUFSIZ]; /* happily used by any function in this file,
+ but ick! big! */
+
+The problem with these is that any time any busybox app is run, you pay a
+memory penalty for this buffer, even if the applet that uses said buffer is
+not run. This can be fixed, thusly:
+
+ static char *buffer;
+ ...
+ other_func()
+ {
+ strcpy(buffer, lotsa_chars); /* happily uses global *buffer */
+ ...
+ foo_main()
+ {
+ buffer = xmalloc(sizeof(char)*BUFSIZ);
+ ...
+
+However, this approach trades bss segment for text segment. Rather than
+mallocing the buffers (and thus growing the text size), buffers can be
+declared on the stack in the *_main() function and made available globally by
+assigning them to a global pointer thusly:
+
+ static char *pbuffer;
+ ...
+ other_func()
+ {
+ strcpy(pbuffer, lotsa_chars); /* happily uses global *pbuffer */
+ ...
+ foo_main()
+ {
+ char *buffer[BUFSIZ]; /* declared locally, on stack */
+ pbuffer = buffer; /* but available globally */
+ ...
+
+This last approach has some advantages (low code size, space not used until
+it's needed), but can be a problem in some low resource machines that have
+very limited stack space (e.g., uCLinux).
+
+A macro is declared in busybox.h that implements compile-time selection
+between xmalloc() and stack creation, so you can code the line in question as
+
+ RESERVE_CONFIG_BUFFER(buffer, BUFSIZ);
+
+and the right thing will happen, based on your configuration.
+
+
+
+Miscellaneous Coding Guidelines
+-------------------------------
+
+The following are important items that don't fit into any of the above
+sections.
+
+
+Model Busybox Applets After GNU Counterparts
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+When in doubt about the proper behavior of a Busybox program (output,
+formatting, options, etc.), model it after the equivalent GNU program.
+Doesn't matter how that program behaves on some other flavor of *NIX; doesn't
+matter what the POSIX standard says or doesn't say, just model Busybox
+programs after their GNU counterparts and it will make life easier on (nearly)
+everyone.
+
+The only time we deviate from emulating the GNU behavior is when:
+
+ - We are deliberately not supporting a feature (such as a command line
+ switch)
+ - Emulating the GNU behavior is prohibitively expensive (lots more code
+ would be required, lots more memory would be used, etc.)
+ - The difference is minor or cosmetic
+
+A note on the 'cosmetic' case: Output differences might be considered
+cosmetic, but if the output is significant enough to break other scripts that
+use the output, it should really be fixed.
+
+
+Scope
+~~~~~
+
+If a const variable is used only in a single source file, put it in the source
+file and not in a header file. Likewise, if a const variable is used in only
+one function, do not make it global to the file. Instead, declare it inside
+the function body. Bottom line: Make a conscious effort to limit declarations
+to the smallest scope possible.
+
+Inside applet files, all functions should be declared static so as to keep the
+global name space clean. The only exception to this rule is the "applet_main"
+function which must be declared extern.
+
+If you write a function that performs a task that could be useful outside the
+immediate file, turn it into a general-purpose function with no ties to any
+applet and put it in the utility.c file instead.
+
+
+Brackets Are Your Friends
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Please use brackets on all if and else statements, even if it is only one
+line. Example:
+
+ Don't do this:
+
+ if (foo)
+ stmt1;
+ stmt2
+ stmt3;
+
+ Do this instead:
+
+ if (foo) {
+ stmt1;
+ }
+ stmt2
+ stmt3;
+
+The "bracketless" approach is error prone because someday you might add a line
+like this:
+
+ if (foo)
+ stmt1;
+ new_line();
+ stmt2
+ stmt3;
+
+And the resulting behavior of your program would totally bewilder you. (Don't
+laugh, it happens to us all.) Remember folks, this is C, not Python.
+
+
+Function Declarations
+~~~~~~~~~~~~~~~~~~~~~
+
+Do not use old-style function declarations that declare variable types between
+the parameter list and opening bracket. Example:
+
+ Don't do this:
+
+ int foo(parm1, parm2)
+ char parm1;
+ float parm2;
+ {
+ ....
+
+ Do this instead:
+
+ int foo(char parm1, float parm2)
+ {
+ ....
+
+The only time you would ever need to use the old declaration syntax is to
+support ancient, antediluvian compilers. To our good fortune, we have access
+to more modern compilers and the old declaration syntax is neither necessary
+nor desired.
+
+
+Emphasizing Logical Blocks
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Organization and readability are improved by putting extra newlines around
+blocks of code that perform a single task. These are typically blocks that
+begin with a C keyword, but not always.
+
+Furthermore, you should put a single comment (not necessarily one line, just
+one comment) before the block, rather than commenting each and every line.
+There is an optimal amount of commenting that a program can have; you can
+comment too much as well as too little.
+
+A picture is really worth a thousand words here, the following example
+illustrates how to emphasize logical blocks:
+
+ while (line = get_line_from_file(fp)) {
+
+ /* eat the newline, if any */
+ chomp(line);
+
+ /* ignore blank lines */
+ if (strlen(file_to_act_on) == 0) {
+ continue;
+ }
+
+ /* if the search string is in this line, print it,
+ * unless we were told to be quiet */
+ if (strstr(line, search) && !be_quiet) {
+ puts(line);
+ }
+
+ /* clean up */
+ free(line);
+ }
+
+
+Processing Options with getopt
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If your applet needs to process command-line switches, please use getopt() to
+do so. Numerous examples can be seen in many of the existing applets, but
+basically it boils down to two things: at the top of the .c file, have this
+line in the midst of your #includes:
+
+ #include <getopt.h>
+
+And a code block similar to the following near the top of your applet_main()
+routine:
+
+ while ((opt = getopt(argc, argv, "abc")) > 0) {
+ switch (opt) {
+ case 'a':
+ do_a_opt = 1;
+ break;
+ case 'b':
+ do_b_opt = 1;
+ break;
+ case 'c':
+ do_c_opt = 1;
+ break;
+ default:
+ show_usage(); /* in utility.c */
+ }
+ }
+
+If your applet takes no options (such as 'init'), there should be a line
+somewhere in the file reads:
+
+ /* no options, no getopt */
+
+That way, when people go grepping to see which applets need to be converted to
+use getopt, they won't get false positives.
+
+Additional Note: Do not use the getopt_long library function and do not try to
+hand-roll your own long option parsing. Busybox applets should only support
+short options. Explanations and examples of the short options should be
+documented in usage.h.
diff --git a/i/pc104/initrd/conf/busybox/docs/tar_pax.txt b/i/pc104/initrd/conf/busybox/docs/tar_pax.txt
new file mode 100644
index 0000000..e56c27b
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/docs/tar_pax.txt
@@ -0,0 +1,239 @@
+'pax headers' is POSIX 2003 (iirc) addition designed to fix
+tar format limitations - older tar format has fixed fields
+for everything (filename, uid, filesize etc) which can overflow.
+
+pax Header Block
+
+The pax header block shall be identical to the ustar header block
+described in ustar Interchange Format, except that two additional
+typeflag values are defined:
+
+x
+ Represents extended header records for the following file in
+the archive (which shall have its own ustar header block).
+
+g
+ Represents global extended header records for the following
+files in the archive. Each value shall affect all subsequent files
+that do not override that value in their own extended header
+record and until another global extended header record is reached
+that provides another value for the same field. The typeflag g
+global headers should not be used with interchange media that
+could suffer partial data loss in transporting the archive.
+
+For both of these types, the size field shall be the size of the
+extended header records in octets. The other fields in the header
+block are not meaningful to this version of the pax utility.
+However, if this archive is read by a pax utility conforming to
+the ISO POSIX-2:1993 standard, the header block fields are used to
+create a regular file that contains the extended header records as
+data. Therefore, header block field values should be selected to
+provide reasonable file access to this regular file.
+
+A further difference from the ustar header block is that data
+blocks for files of typeflag 1 (the digit one) (hard link) may be
+included, which means that the size field may be greater than
+zero.
+
+pax Extended Header
+
+An extended header shall consist of one or more records, each
+constructed as follows:
+
+"%d %s=%s\n", <length>, <keyword>, <value>
+
+The <length> field shall be the decimal length of the extended
+header record in octets, including length string itself and the
+trailing <newline>.
+
+[skip]
+
+atime
+ The file access time for the following file(s), equivalent to
+the value of the st_atime member of the stat structure for a file,
+as described by the stat() function. The access time shall be
+restored if the process has the appropriate privilege required to
+do so. The format of the <value> shall be as described in pax
+Extended Header File Times.
+
+charset
+ The name of the character set used to encode the data in the
+following file(s).
+
+ The encoding is included in an extended header for information
+only; when pax is used as described in IEEE Std 1003.1-2001, it
+shall not translate the file data into any other encoding. The
+BINARY entry indicates unencoded binary data.
+
+ When used in write or copy mode, it is implementation-defined
+whether pax includes a charset extended header record for a file.
+
+comment
+ A series of characters used as a comment. All characters in
+the <value> field shall be ignored by pax.
+
+gid
+ The group ID of the group that owns the file, expressed as a
+decimal number using digits from the ISO/IEC 646:1991 standard.
+This record shall override the gid field in the following header
+block(s). When used in write or copy mode, pax shall include a gid
+extended header record for each file whose group ID is greater
+than 2097151 (octal 7777777).
+
+gname
+ The group of the file(s), formatted as a group name in the
+group database. This record shall override the gid and gname
+fields in the following header block(s), and any gid extended
+header record. When used in read, copy, or list mode, pax shall
+translate the name from the UTF-8 encoding in the header record to
+the character set appropriate for the group database on the
+receiving system. If any of the UTF-8 characters cannot be
+translated, and if the -o invalid= UTF-8 option is not specified,
+the results are implementation-defined. When used in write or copy
+mode, pax shall include a gname extended header record for each
+file whose group name cannot be represented entirely with the
+letters and digits of the portable character set.
+
+linkpath
+ The pathname of a link being created to another file, of any
+type, previously archived. This record shall override the linkname
+field in the following ustar header block(s). The following ustar
+header block shall determine the type of link created. If typeflag
+of the following header block is 1, it shall be a hard link. If
+typeflag is 2, it shall be a symbolic link and the linkpath value
+shall be the contents of the symbolic link. The pax utility shall
+translate the name of the link (contents of the symbolic link)
+from the UTF-8 encoding to the character set appropriate for the
+local file system. When used in write or copy mode, pax shall
+include a linkpath extended header record for each link whose
+pathname cannot be represented entirely with the members of the
+portable character set other than NUL.
+
+mtime
+ The file modification time of the following file(s),
+equivalent to the value of the st_mtime member of the stat
+structure for a file, as described in the stat() function. This
+record shall override the mtime field in the following header
+block(s). The modification time shall be restored if the process
+has the appropriate privilege required to do so. The format of the
+<value> shall be as described in pax Extended Header File Times.
+
+path
+ The pathname of the following file(s). This record shall
+override the name and prefix fields in the following header
+block(s). The pax utility shall translate the pathname of the file
+from the UTF-8 encoding to the character set appropriate for the
+local file system.
+
+ When used in write or copy mode, pax shall include a path
+extended header record for each file whose pathname cannot be
+represented entirely with the members of the portable character
+set other than NUL.
+
+realtime.any
+ The keywords prefixed by "realtime." are reserved for future
+standardization.
+
+security.any
+ The keywords prefixed by "security." are reserved for future
+standardization.
+
+size
+ The size of the file in octets, expressed as a decimal number
+using digits from the ISO/IEC 646:1991 standard. This record shall
+override the size field in the following header block(s). When
+used in write or copy mode, pax shall include a size extended
+header record for each file with a size value greater than
+8589934591 (octal 77777777777).
+
+uid
+ The user ID of the file owner, expressed as a decimal number
+using digits from the ISO/IEC 646:1991 standard. This record shall
+override the uid field in the following header block(s). When used
+in write or copy mode, pax shall include a uid extended header
+record for each file whose owner ID is greater than 2097151 (octal
+7777777).
+
+uname
+ The owner of the following file(s), formatted as a user name
+in the user database. This record shall override the uid and uname
+fields in the following header block(s), and any uid extended
+header record. When used in read, copy, or list mode, pax shall
+translate the name from the UTF-8 encoding in the header record to
+the character set appropriate for the user database on the
+receiving system. If any of the UTF-8 characters cannot be
+translated, and if the -o invalid= UTF-8 option is not specified,
+the results are implementation-defined. When used in write or copy
+mode, pax shall include a uname extended header record for each
+file whose user name cannot be represented entirely with the
+letters and digits of the portable character set.
+
+If the <value> field is zero length, it shall delete any header
+block field, previously entered extended header value, or global
+extended header value of the same name.
+
+If a keyword in an extended header record (or in a -o
+option-argument) overrides or deletes a corresponding field in the
+ustar header block, pax shall ignore the contents of that header
+block field.
+
+Unlike the ustar header block fields, NULs shall not delimit
+<value>s; all characters within the <value> field shall be
+considered data for the field. None of the length limitations of
+the ustar header block fields in ustar Header Block shall apply to
+the extended header records.
+
+pax Extended Header File Times
+
+Time records shall be formatted as a decimal representation of the
+time in seconds since the Epoch. If a period ( '.' ) decimal point
+character is present, the digits to the right of the point shall
+represent the units of a subsecond timing granularity. In read or
+copy mode, the pax utility shall truncate the time of a file to
+the greatest value that is not greater than the input header
+file time. In write or copy mode, the pax utility shall output a
+time exactly if it can be represented exactly as a decimal number,
+and otherwise shall generate only enough digits so that the same
+time shall be recovered if the file is extracted on a system whose
+underlying implementation supports the same time granularity.
+
+Example from Linux kernel archive tarball:
+
+00000000 70 61 78 5f 67 6c 6f 62 61 6c 5f 68 65 61 64 65 |pax_global_heade|
+00000010 72 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |r...............|
+00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+00000040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+00000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+00000060 00 00 00 00 30 30 30 30 36 36 36 00 30 30 30 30 |....0000666.0000|
+00000070 30 30 30 00 30 30 30 30 30 30 30 00 30 30 30 30 |000.0000000.0000|
+00000080 30 30 30 30 30 36 34 00 30 30 30 30 30 30 30 30 |0000064.00000000|
+00000090 30 30 30 00 30 30 31 34 30 35 33 00 67 00 00 00 |000.0014053.g...|
+000000a0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+000000b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+000000c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+000000d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+000000e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+000000f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+00000100 00 75 73 74 61 72 00 30 30 67 69 74 00 00 00 00 |.ustar.00git....|
+00000110 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+00000120 00 00 00 00 00 00 00 00 00 67 69 74 00 00 00 00 |.........git....|
+00000130 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+00000140 00 00 00 00 00 00 00 00 00 30 30 30 30 30 30 30 |.........0000000|
+00000150 00 30 30 30 30 30 30 30 00 00 00 00 00 00 00 00 |.0000000........|
+00000160 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+00000170 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+00000180 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+00000190 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+000001a0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+000001b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+000001c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+000001d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+000001e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+000001f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+00000200 35 32 20 63 6f 6d 6d 65 6e 74 3d 62 31 30 35 30 |52 comment=b1050|
+00000210 32 62 32 32 61 31 32 30 39 64 36 62 34 37 36 33 |2b22a1209d6b4763|
+00000220 39 64 38 38 62 38 31 32 62 32 31 66 62 35 39 34 |9d88b812b21fb594|
+00000230 39 65 34 0a 00 00 00 00 00 00 00 00 00 00 00 00 |9e4.............|
+00000240 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+...
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/Config.in b/i/pc104/initrd/conf/busybox/e2fsprogs/Config.in
new file mode 100644
index 0000000..521273e
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/Config.in
@@ -0,0 +1,67 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+menu "Linux Ext2 FS Progs"
+
+config CHATTR
+ bool "chattr"
+ default n
+ help
+ chattr changes the file attributes on a second extended file system.
+
+### config E2FSCK
+### bool "e2fsck"
+### default n
+### help
+### e2fsck is used to check Linux second extended file systems (ext2fs).
+### e2fsck also supports ext2 filesystems countaining a journal (ext3).
+### The normal compat symlinks 'fsck.ext2' and 'fsck.ext3' are also
+### provided.
+
+config FSCK
+ bool "fsck"
+ default n
+ help
+ fsck is used to check and optionally repair one or more filesystems.
+ In actuality, fsck is simply a front-end for the various file system
+ checkers (fsck.fstype) available under Linux.
+
+config LSATTR
+ bool "lsattr"
+ default n
+ help
+ lsattr lists the file attributes on a second extended file system.
+
+### config MKE2FS
+### bool "mke2fs"
+### default n
+### help
+### mke2fs is used to create an ext2/ext3 filesystem. The normal compat
+### symlinks 'mkfs.ext2' and 'mkfs.ext3' are also provided.
+
+### config TUNE2FS
+### bool "tune2fs"
+### default n
+### help
+### tune2fs allows the system administrator to adjust various tunable
+### filesystem parameters on Linux ext2/ext3 filesystems.
+
+### config E2LABEL
+### bool "e2label"
+### default n
+### depends on TUNE2FS
+### help
+### e2label will display or change the filesystem label on the ext2
+### filesystem located on device.
+
+### config FINDFS
+### bool "findfs"
+### default n
+### depends on TUNE2FS
+### help
+### findfs will search the disks in the system looking for a filesystem
+### which has a label matching label or a UUID equal to uuid.
+
+endmenu
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/Kbuild b/i/pc104/initrd/conf/busybox/e2fsprogs/Kbuild
new file mode 100644
index 0000000..9f58ce0
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/Kbuild
@@ -0,0 +1,12 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under the GPL v2, see the file LICENSE in this tarball.
+
+lib-y:=
+
+lib-$(CONFIG_CHATTR) += chattr.o e2fs_lib.o
+lib-$(CONFIG_LSATTR) += lsattr.o e2fs_lib.o
+
+lib-$(CONFIG_FSCK) += fsck.o
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/README b/i/pc104/initrd/conf/busybox/e2fsprogs/README
new file mode 100644
index 0000000..eb158e5
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/README
@@ -0,0 +1,12 @@
+Authors and contributors of original e2fsprogs:
+
+Remy Card <card@masi.ibp.fr>
+Theodore Ts'o <tytso@mit.edu>
+Stephen C. Tweedie <sct@redhat.com>
+Andreas Gruenbacher, <a.gruenbacher@computer.org>
+Kaz Kylheku <kaz@ashi.footprints.net>
+F.W. ten Wolde <franky@duteca.et.tudelft.nl>
+Jeremy Fitzhardinge <jeremy@zip.com.au>
+M.J.E. Mol <marcel@duteca.et.tudelft.nl>
+Miquel van Smoorenburg <miquels@drinkel.ow.org>
+Uwe Ohse <uwe@tirka.gun.de>
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/chattr.c b/i/pc104/initrd/conf/busybox/e2fsprogs/chattr.c
new file mode 100644
index 0000000..1b7942e
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/chattr.c
@@ -0,0 +1,190 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * chattr.c - Change file attributes on an ext2 file system
+ *
+ * Copyright (C) 1993, 1994 Remy Card <card@masi.ibp.fr>
+ * Laboratoire MASI, Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ * This file can be redistributed under the terms of the GNU General
+ * Public License
+ */
+
+/*
+ * History:
+ * 93/10/30 - Creation
+ * 93/11/13 - Replace stat() calls by lstat() to avoid loops
+ * 94/02/27 - Integrated in Ted's distribution
+ * 98/12/29 - Ignore symlinks when working recursively (G M Sipe)
+ * 98/12/29 - Display version info only when -V specified (G M Sipe)
+ */
+
+#include "busybox.h"
+#include "e2fs_lib.h"
+
+#define OPT_ADD 1
+#define OPT_REM 2
+#define OPT_SET 4
+#define OPT_SET_VER 8
+static int flags;
+static int recursive;
+
+static unsigned long version;
+
+static unsigned long af;
+static unsigned long rf;
+static unsigned long sf;
+
+struct flags_char {
+ unsigned long flag;
+ char optchar;
+};
+
+static const struct flags_char flags_array[] = {
+ { EXT2_NOATIME_FL, 'A' },
+ { EXT2_SYNC_FL, 'S' },
+ { EXT2_DIRSYNC_FL, 'D' },
+ { EXT2_APPEND_FL, 'a' },
+ { EXT2_COMPR_FL, 'c' },
+ { EXT2_NODUMP_FL, 'd' },
+ { EXT2_IMMUTABLE_FL, 'i' },
+ { EXT3_JOURNAL_DATA_FL, 'j' },
+ { EXT2_SECRM_FL, 's' },
+ { EXT2_UNRM_FL, 'u' },
+ { EXT2_NOTAIL_FL, 't' },
+ { EXT2_TOPDIR_FL, 'T' },
+ { 0, 0 }
+};
+
+static unsigned long get_flag(char c)
+{
+ const struct flags_char *fp;
+ for (fp = flags_array; fp->optchar; fp++)
+ if (fp->optchar == c)
+ return fp->flag;
+ bb_show_usage();
+}
+
+static int decode_arg(const char *arg)
+{
+ unsigned long *fl;
+ char opt = *arg++;
+
+ if (opt == '-') {
+ flags |= OPT_REM;
+ fl = &rf;
+ } else if (opt == '+') {
+ flags |= OPT_ADD;
+ fl = &af;
+ } else if (opt == '=') {
+ flags |= OPT_SET;
+ fl = &sf;
+ } else
+ return 0;
+
+ while (*arg)
+ *fl |= get_flag(*arg++);
+
+ return 1;
+}
+
+static void change_attributes(const char *name);
+
+static int chattr_dir_proc(const char *dir_name, struct dirent *de,
+ void *private ATTRIBUTE_UNUSED)
+{
+ char *path = concat_subpath_file(dir_name, de->d_name);
+ /* path is NULL if de->d_name is "." or "..", else... */
+ if (path) {
+ change_attributes(path);
+ free(path);
+ }
+ return 0;
+}
+
+static void change_attributes(const char *name)
+{
+ unsigned long fsflags;
+ struct stat st;
+
+ if (lstat(name, &st) == -1) {
+ bb_perror_msg("stat %s", name);
+ return;
+ }
+ if (S_ISLNK(st.st_mode) && recursive)
+ return;
+
+ /* Don't try to open device files, fifos etc. We probably
+ * ought to display an error if the file was explicitly given
+ * on the command line (whether or not recursive was
+ * requested). */
+ if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode) && !S_ISDIR(st.st_mode))
+ return;
+
+ if (flags & OPT_SET_VER)
+ if (fsetversion(name, version) == -1)
+ bb_perror_msg("setting version on %s", name);
+
+ if (flags & OPT_SET) {
+ fsflags = sf;
+ } else {
+ if (fgetflags(name, &fsflags) == -1) {
+ bb_perror_msg("reading flags on %s", name);
+ goto skip_setflags;
+ }
+ if (flags & OPT_REM)
+ fsflags &= ~rf;
+ if (flags & OPT_ADD)
+ fsflags |= af;
+ if (!S_ISDIR(st.st_mode))
+ fsflags &= ~EXT2_DIRSYNC_FL;
+ }
+ if (fsetflags(name, fsflags) == -1)
+ bb_perror_msg("setting flags on %s", name);
+
+ skip_setflags:
+ if (recursive && S_ISDIR(st.st_mode))
+ iterate_on_dir(name, chattr_dir_proc, NULL);
+}
+
+int chattr_main(int argc, char **argv);
+int chattr_main(int argc, char **argv)
+{
+ char *arg;
+
+ /* parse the args */
+ while ((arg = *++argv)) {
+ /* take care of -R and -v <version> */
+ if (arg[0] == '-') {
+ if (arg[1] == 'R' && arg[2] == '\0') {
+ recursive = 1;
+ continue;
+ }
+ if (arg[1] == 'v' && arg[2] == '\0') {
+ if (!*++argv)
+ bb_show_usage();
+ version = xatoul(*argv);
+ flags |= OPT_SET_VER;
+ continue;
+ }
+ }
+
+ if (!decode_arg(arg))
+ break;
+ }
+
+ /* run sanity checks on all the arguments given us */
+ if (!*argv)
+ bb_show_usage();
+ if ((flags & OPT_SET) && (flags & (OPT_ADD|OPT_REM)))
+ bb_error_msg_and_die("= is incompatible with - and +");
+ if (rf & af)
+ bb_error_msg_and_die("can't set and unset a flag");
+ if (!flags)
+ bb_error_msg_and_die("must use '-v', =, - or +");
+
+ /* now run chattr on all the files passed to us */
+ do change_attributes(*argv); while (*++argv);
+
+ return EXIT_SUCCESS;
+}
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/e2fs_defs.h b/i/pc104/initrd/conf/busybox/e2fsprogs/e2fs_defs.h
new file mode 100644
index 0000000..b3ea3ae
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/e2fs_defs.h
@@ -0,0 +1,561 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * linux/include/linux/ext2_fs.h
+ *
+ * Copyright (C) 1992, 1993, 1994, 1995
+ * Remy Card (card@masi.ibp.fr)
+ * Laboratoire MASI - Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ */
+
+#ifndef _LINUX_EXT2_FS_H
+#define _LINUX_EXT2_FS_H
+
+/*
+ * Special inode numbers
+ */
+#define EXT2_BAD_INO 1 /* Bad blocks inode */
+#define EXT2_ROOT_INO 2 /* Root inode */
+#define EXT2_ACL_IDX_INO 3 /* ACL inode */
+#define EXT2_ACL_DATA_INO 4 /* ACL inode */
+#define EXT2_BOOT_LOADER_INO 5 /* Boot loader inode */
+#define EXT2_UNDEL_DIR_INO 6 /* Undelete directory inode */
+#define EXT2_RESIZE_INO 7 /* Reserved group descriptors inode */
+#define EXT2_JOURNAL_INO 8 /* Journal inode */
+
+/* First non-reserved inode for old ext2 filesystems */
+#define EXT2_GOOD_OLD_FIRST_INO 11
+
+/*
+ * The second extended file system magic number
+ */
+#define EXT2_SUPER_MAGIC 0xEF53
+
+/* Assume that user mode programs are passing in an ext2fs superblock, not
+ * a kernel struct super_block. This will allow us to call the feature-test
+ * macros from user land. */
+#define EXT2_SB(sb) (sb)
+
+/*
+ * Maximal count of links to a file
+ */
+#define EXT2_LINK_MAX 32000
+
+/*
+ * Macro-instructions used to manage several block sizes
+ */
+#define EXT2_MIN_BLOCK_LOG_SIZE 10 /* 1024 */
+#define EXT2_MAX_BLOCK_LOG_SIZE 16 /* 65536 */
+#define EXT2_MIN_BLOCK_SIZE (1 << EXT2_MIN_BLOCK_LOG_SIZE)
+#define EXT2_MAX_BLOCK_SIZE (1 << EXT2_MAX_BLOCK_LOG_SIZE)
+#define EXT2_BLOCK_SIZE(s) (EXT2_MIN_BLOCK_SIZE << (s)->s_log_block_size)
+#define EXT2_BLOCK_SIZE_BITS(s) ((s)->s_log_block_size + 10)
+#define EXT2_INODE_SIZE(s) (((s)->s_rev_level == EXT2_GOOD_OLD_REV) ? \
+ EXT2_GOOD_OLD_INODE_SIZE : (s)->s_inode_size)
+#define EXT2_FIRST_INO(s) (((s)->s_rev_level == EXT2_GOOD_OLD_REV) ? \
+ EXT2_GOOD_OLD_FIRST_INO : (s)->s_first_ino)
+#define EXT2_ADDR_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof(uint32_t))
+
+/*
+ * Macro-instructions used to manage fragments
+ */
+#define EXT2_MIN_FRAG_SIZE EXT2_MIN_BLOCK_SIZE
+#define EXT2_MAX_FRAG_SIZE EXT2_MAX_BLOCK_SIZE
+#define EXT2_MIN_FRAG_LOG_SIZE EXT2_MIN_BLOCK_LOG_SIZE
+#define EXT2_FRAG_SIZE(s) (EXT2_MIN_FRAG_SIZE << (s)->s_log_frag_size)
+#define EXT2_FRAGS_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / EXT2_FRAG_SIZE(s))
+
+/*
+ * ACL structures
+ */
+struct ext2_acl_header { /* Header of Access Control Lists */
+ uint32_t aclh_size;
+ uint32_t aclh_file_count;
+ uint32_t aclh_acle_count;
+ uint32_t aclh_first_acle;
+};
+
+struct ext2_acl_entry { /* Access Control List Entry */
+ uint32_t acle_size;
+ uint16_t acle_perms; /* Access permissions */
+ uint16_t acle_type; /* Type of entry */
+ uint16_t acle_tag; /* User or group identity */
+ uint16_t acle_pad1;
+ uint32_t acle_next; /* Pointer on next entry for the */
+ /* same inode or on next free entry */
+};
+
+/*
+ * Structure of a blocks group descriptor
+ */
+struct ext2_group_desc {
+ uint32_t bg_block_bitmap; /* Blocks bitmap block */
+ uint32_t bg_inode_bitmap; /* Inodes bitmap block */
+ uint32_t bg_inode_table; /* Inodes table block */
+ uint16_t bg_free_blocks_count; /* Free blocks count */
+ uint16_t bg_free_inodes_count; /* Free inodes count */
+ uint16_t bg_used_dirs_count; /* Directories count */
+ uint16_t bg_pad;
+ uint32_t bg_reserved[3];
+};
+
+/*
+ * Data structures used by the directory indexing feature
+ *
+ * Note: all of the multibyte integer fields are little endian.
+ */
+
+/*
+ * Note: dx_root_info is laid out so that if it should somehow get
+ * overlaid by a dirent the two low bits of the hash version will be
+ * zero. Therefore, the hash version mod 4 should never be 0.
+ * Sincerely, the paranoia department.
+ */
+struct ext2_dx_root_info {
+ uint32_t reserved_zero;
+ uint8_t hash_version; /* 0 now, 1 at release */
+ uint8_t info_length; /* 8 */
+ uint8_t indirect_levels;
+ uint8_t unused_flags;
+};
+
+#define EXT2_HASH_LEGACY 0
+#define EXT2_HASH_HALF_MD4 1
+#define EXT2_HASH_TEA 2
+
+#define EXT2_HASH_FLAG_INCOMPAT 0x1
+
+struct ext2_dx_entry {
+ uint32_t hash;
+ uint32_t block;
+};
+
+struct ext2_dx_countlimit {
+ uint16_t limit;
+ uint16_t count;
+};
+
+
+/*
+ * Macro-instructions used to manage group descriptors
+ */
+#define EXT2_BLOCKS_PER_GROUP(s) (EXT2_SB(s)->s_blocks_per_group)
+#define EXT2_INODES_PER_GROUP(s) (EXT2_SB(s)->s_inodes_per_group)
+#define EXT2_INODES_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s)/EXT2_INODE_SIZE(s))
+/* limits imposed by 16-bit value gd_free_{blocks,inode}_count */
+#define EXT2_MAX_BLOCKS_PER_GROUP(s) ((1 << 16) - 8)
+#define EXT2_MAX_INODES_PER_GROUP(s) ((1 << 16) - EXT2_INODES_PER_BLOCK(s))
+#define EXT2_DESC_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof (struct ext2_group_desc))
+
+/*
+ * Constants relative to the data blocks
+ */
+#define EXT2_NDIR_BLOCKS 12
+#define EXT2_IND_BLOCK EXT2_NDIR_BLOCKS
+#define EXT2_DIND_BLOCK (EXT2_IND_BLOCK + 1)
+#define EXT2_TIND_BLOCK (EXT2_DIND_BLOCK + 1)
+#define EXT2_N_BLOCKS (EXT2_TIND_BLOCK + 1)
+
+/*
+ * Inode flags
+ */
+#define EXT2_SECRM_FL 0x00000001 /* Secure deletion */
+#define EXT2_UNRM_FL 0x00000002 /* Undelete */
+#define EXT2_COMPR_FL 0x00000004 /* Compress file */
+#define EXT2_SYNC_FL 0x00000008 /* Synchronous updates */
+#define EXT2_IMMUTABLE_FL 0x00000010 /* Immutable file */
+#define EXT2_APPEND_FL 0x00000020 /* writes to file may only append */
+#define EXT2_NODUMP_FL 0x00000040 /* do not dump file */
+#define EXT2_NOATIME_FL 0x00000080 /* do not update atime */
+/* Reserved for compression usage... */
+#define EXT2_DIRTY_FL 0x00000100
+#define EXT2_COMPRBLK_FL 0x00000200 /* One or more compressed clusters */
+#define EXT2_NOCOMPR_FL 0x00000400 /* Access raw compressed data */
+#define EXT2_ECOMPR_FL 0x00000800 /* Compression error */
+/* End compression flags --- maybe not all used */
+#define EXT2_BTREE_FL 0x00001000 /* btree format dir */
+#define EXT2_INDEX_FL 0x00001000 /* hash-indexed directory */
+#define EXT2_IMAGIC_FL 0x00002000
+#define EXT3_JOURNAL_DATA_FL 0x00004000 /* file data should be journaled */
+#define EXT2_NOTAIL_FL 0x00008000 /* file tail should not be merged */
+#define EXT2_DIRSYNC_FL 0x00010000 /* Synchronous directory modifications */
+#define EXT2_TOPDIR_FL 0x00020000 /* Top of directory hierarchies*/
+#define EXT3_EXTENTS_FL 0x00080000 /* Inode uses extents */
+#define EXT2_RESERVED_FL 0x80000000 /* reserved for ext2 lib */
+
+#define EXT2_FL_USER_VISIBLE 0x0003DFFF /* User visible flags */
+#define EXT2_FL_USER_MODIFIABLE 0x000080FF /* User modifiable flags */
+
+/*
+ * ioctl commands
+ */
+#define EXT2_IOC_GETFLAGS _IOR('f', 1, long)
+#define EXT2_IOC_SETFLAGS _IOW('f', 2, long)
+#define EXT2_IOC_GETVERSION _IOR('v', 1, long)
+#define EXT2_IOC_SETVERSION _IOW('v', 2, long)
+
+/*
+ * Structure of an inode on the disk
+ */
+struct ext2_inode {
+ uint16_t i_mode; /* File mode */
+ uint16_t i_uid; /* Low 16 bits of Owner Uid */
+ uint32_t i_size; /* Size in bytes */
+ uint32_t i_atime; /* Access time */
+ uint32_t i_ctime; /* Creation time */
+ uint32_t i_mtime; /* Modification time */
+ uint32_t i_dtime; /* Deletion Time */
+ uint16_t i_gid; /* Low 16 bits of Group Id */
+ uint16_t i_links_count; /* Links count */
+ uint32_t i_blocks; /* Blocks count */
+ uint32_t i_flags; /* File flags */
+ union {
+ struct {
+ uint32_t l_i_reserved1;
+ } linux1;
+ struct {
+ uint32_t h_i_translator;
+ } hurd1;
+ struct {
+ uint32_t m_i_reserved1;
+ } masix1;
+ } osd1; /* OS dependent 1 */
+ uint32_t i_block[EXT2_N_BLOCKS];/* Pointers to blocks */
+ uint32_t i_generation; /* File version (for NFS) */
+ uint32_t i_file_acl; /* File ACL */
+ uint32_t i_dir_acl; /* Directory ACL */
+ uint32_t i_faddr; /* Fragment address */
+ union {
+ struct {
+ uint8_t l_i_frag; /* Fragment number */
+ uint8_t l_i_fsize; /* Fragment size */
+ uint16_t i_pad1;
+ uint16_t l_i_uid_high; /* these 2 fields */
+ uint16_t l_i_gid_high; /* were reserved2[0] */
+ uint32_t l_i_reserved2;
+ } linux2;
+ struct {
+ uint8_t h_i_frag; /* Fragment number */
+ uint8_t h_i_fsize; /* Fragment size */
+ uint16_t h_i_mode_high;
+ uint16_t h_i_uid_high;
+ uint16_t h_i_gid_high;
+ uint32_t h_i_author;
+ } hurd2;
+ struct {
+ uint8_t m_i_frag; /* Fragment number */
+ uint8_t m_i_fsize; /* Fragment size */
+ uint16_t m_pad1;
+ uint32_t m_i_reserved2[2];
+ } masix2;
+ } osd2; /* OS dependent 2 */
+};
+
+/*
+ * Permanent part of an large inode on the disk
+ */
+struct ext2_inode_large {
+ uint16_t i_mode; /* File mode */
+ uint16_t i_uid; /* Low 16 bits of Owner Uid */
+ uint32_t i_size; /* Size in bytes */
+ uint32_t i_atime; /* Access time */
+ uint32_t i_ctime; /* Creation time */
+ uint32_t i_mtime; /* Modification time */
+ uint32_t i_dtime; /* Deletion Time */
+ uint16_t i_gid; /* Low 16 bits of Group Id */
+ uint16_t i_links_count; /* Links count */
+ uint32_t i_blocks; /* Blocks count */
+ uint32_t i_flags; /* File flags */
+ union {
+ struct {
+ uint32_t l_i_reserved1;
+ } linux1;
+ struct {
+ uint32_t h_i_translator;
+ } hurd1;
+ struct {
+ uint32_t m_i_reserved1;
+ } masix1;
+ } osd1; /* OS dependent 1 */
+ uint32_t i_block[EXT2_N_BLOCKS];/* Pointers to blocks */
+ uint32_t i_generation; /* File version (for NFS) */
+ uint32_t i_file_acl; /* File ACL */
+ uint32_t i_dir_acl; /* Directory ACL */
+ uint32_t i_faddr; /* Fragment address */
+ union {
+ struct {
+ uint8_t l_i_frag; /* Fragment number */
+ uint8_t l_i_fsize; /* Fragment size */
+ uint16_t i_pad1;
+ uint16_t l_i_uid_high; /* these 2 fields */
+ uint16_t l_i_gid_high; /* were reserved2[0] */
+ uint32_t l_i_reserved2;
+ } linux2;
+ struct {
+ uint8_t h_i_frag; /* Fragment number */
+ uint8_t h_i_fsize; /* Fragment size */
+ uint16_t h_i_mode_high;
+ uint16_t h_i_uid_high;
+ uint16_t h_i_gid_high;
+ uint32_t h_i_author;
+ } hurd2;
+ struct {
+ uint8_t m_i_frag; /* Fragment number */
+ uint8_t m_i_fsize; /* Fragment size */
+ uint16_t m_pad1;
+ uint32_t m_i_reserved2[2];
+ } masix2;
+ } osd2; /* OS dependent 2 */
+ uint16_t i_extra_isize;
+ uint16_t i_pad1;
+};
+
+#define i_size_high i_dir_acl
+
+/*
+ * File system states
+ */
+#define EXT2_VALID_FS 0x0001 /* Unmounted cleanly */
+#define EXT2_ERROR_FS 0x0002 /* Errors detected */
+
+/*
+ * Mount flags
+ */
+#define EXT2_MOUNT_CHECK 0x0001 /* Do mount-time checks */
+#define EXT2_MOUNT_GRPID 0x0004 /* Create files with directory's group */
+#define EXT2_MOUNT_DEBUG 0x0008 /* Some debugging messages */
+#define EXT2_MOUNT_ERRORS_CONT 0x0010 /* Continue on errors */
+#define EXT2_MOUNT_ERRORS_RO 0x0020 /* Remount fs ro on errors */
+#define EXT2_MOUNT_ERRORS_PANIC 0x0040 /* Panic on errors */
+#define EXT2_MOUNT_MINIX_DF 0x0080 /* Mimics the Minix statfs */
+#define EXT2_MOUNT_NO_UID32 0x0200 /* Disable 32-bit UIDs */
+
+#define clear_opt(o, opt) o &= ~EXT2_MOUNT_##opt
+#define set_opt(o, opt) o |= EXT2_MOUNT_##opt
+#define test_opt(sb, opt) (EXT2_SB(sb)->s_mount_opt & \
+ EXT2_MOUNT_##opt)
+/*
+ * Maximal mount counts between two filesystem checks
+ */
+#define EXT2_DFL_MAX_MNT_COUNT 20 /* Allow 20 mounts */
+#define EXT2_DFL_CHECKINTERVAL 0 /* Don't use interval check */
+
+/*
+ * Behaviour when detecting errors
+ */
+#define EXT2_ERRORS_CONTINUE 1 /* Continue execution */
+#define EXT2_ERRORS_RO 2 /* Remount fs read-only */
+#define EXT2_ERRORS_PANIC 3 /* Panic */
+#define EXT2_ERRORS_DEFAULT EXT2_ERRORS_CONTINUE
+
+/*
+ * Structure of the super block
+ */
+struct ext2_super_block {
+ uint32_t s_inodes_count; /* Inodes count */
+ uint32_t s_blocks_count; /* Blocks count */
+ uint32_t s_r_blocks_count; /* Reserved blocks count */
+ uint32_t s_free_blocks_count; /* Free blocks count */
+ uint32_t s_free_inodes_count; /* Free inodes count */
+ uint32_t s_first_data_block; /* First Data Block */
+ uint32_t s_log_block_size; /* Block size */
+ int32_t s_log_frag_size; /* Fragment size */
+ uint32_t s_blocks_per_group; /* # Blocks per group */
+ uint32_t s_frags_per_group; /* # Fragments per group */
+ uint32_t s_inodes_per_group; /* # Inodes per group */
+ uint32_t s_mtime; /* Mount time */
+ uint32_t s_wtime; /* Write time */
+ uint16_t s_mnt_count; /* Mount count */
+ int16_t s_max_mnt_count; /* Maximal mount count */
+ uint16_t s_magic; /* Magic signature */
+ uint16_t s_state; /* File system state */
+ uint16_t s_errors; /* Behaviour when detecting errors */
+ uint16_t s_minor_rev_level; /* minor revision level */
+ uint32_t s_lastcheck; /* time of last check */
+ uint32_t s_checkinterval; /* max. time between checks */
+ uint32_t s_creator_os; /* OS */
+ uint32_t s_rev_level; /* Revision level */
+ uint16_t s_def_resuid; /* Default uid for reserved blocks */
+ uint16_t s_def_resgid; /* Default gid for reserved blocks */
+ /*
+ * These fields are for EXT2_DYNAMIC_REV superblocks only.
+ *
+ * Note: the difference between the compatible feature set and
+ * the incompatible feature set is that if there is a bit set
+ * in the incompatible feature set that the kernel doesn't
+ * know about, it should refuse to mount the filesystem.
+ *
+ * e2fsck's requirements are more strict; if it doesn't know
+ * about a feature in either the compatible or incompatible
+ * feature set, it must abort and not try to meddle with
+ * things it doesn't understand...
+ */
+ uint32_t s_first_ino; /* First non-reserved inode */
+ uint16_t s_inode_size; /* size of inode structure */
+ uint16_t s_block_group_nr; /* block group # of this superblock */
+ uint32_t s_feature_compat; /* compatible feature set */
+ uint32_t s_feature_incompat; /* incompatible feature set */
+ uint32_t s_feature_ro_compat; /* readonly-compatible feature set */
+ uint8_t s_uuid[16]; /* 128-bit uuid for volume */
+ char s_volume_name[16]; /* volume name */
+ char s_last_mounted[64]; /* directory where last mounted */
+ uint32_t s_algorithm_usage_bitmap; /* For compression */
+ /*
+ * Performance hints. Directory preallocation should only
+ * happen if the EXT2_FEATURE_COMPAT_DIR_PREALLOC flag is on.
+ */
+ uint8_t s_prealloc_blocks; /* Nr of blocks to try to preallocate*/
+ uint8_t s_prealloc_dir_blocks; /* Nr to preallocate for dirs */
+ uint16_t s_reserved_gdt_blocks; /* Per group table for online growth */
+ /*
+ * Journaling support valid if EXT2_FEATURE_COMPAT_HAS_JOURNAL set.
+ */
+ uint8_t s_journal_uuid[16]; /* uuid of journal superblock */
+ uint32_t s_journal_inum; /* inode number of journal file */
+ uint32_t s_journal_dev; /* device number of journal file */
+ uint32_t s_last_orphan; /* start of list of inodes to delete */
+ uint32_t s_hash_seed[4]; /* HTREE hash seed */
+ uint8_t s_def_hash_version; /* Default hash version to use */
+ uint8_t s_jnl_backup_type; /* Default type of journal backup */
+ uint16_t s_reserved_word_pad;
+ uint32_t s_default_mount_opts;
+ uint32_t s_first_meta_bg; /* First metablock group */
+ uint32_t s_mkfs_time; /* When the filesystem was created */
+ uint32_t s_jnl_blocks[17]; /* Backup of the journal inode */
+ uint32_t s_reserved[172]; /* Padding to the end of the block */
+};
+
+/*
+ * Codes for operating systems
+ */
+#define EXT2_OS_LINUX 0
+#define EXT2_OS_HURD 1
+#define EXT2_OS_MASIX 2
+#define EXT2_OS_FREEBSD 3
+#define EXT2_OS_LITES 4
+
+/*
+ * Revision levels
+ */
+#define EXT2_GOOD_OLD_REV 0 /* The good old (original) format */
+#define EXT2_DYNAMIC_REV 1 /* V2 format w/ dynamic inode sizes */
+
+#define EXT2_CURRENT_REV EXT2_GOOD_OLD_REV
+#define EXT2_MAX_SUPP_REV EXT2_DYNAMIC_REV
+
+#define EXT2_GOOD_OLD_INODE_SIZE 128
+
+/*
+ * Journal inode backup types
+ */
+#define EXT3_JNL_BACKUP_BLOCKS 1
+
+/*
+ * Feature set definitions
+ */
+
+#define EXT2_HAS_COMPAT_FEATURE(sb,mask) \
+ ( EXT2_SB(sb)->s_feature_compat & (mask) )
+#define EXT2_HAS_RO_COMPAT_FEATURE(sb,mask) \
+ ( EXT2_SB(sb)->s_feature_ro_compat & (mask) )
+#define EXT2_HAS_INCOMPAT_FEATURE(sb,mask) \
+ ( EXT2_SB(sb)->s_feature_incompat & (mask) )
+
+#define EXT2_FEATURE_COMPAT_DIR_PREALLOC 0x0001
+#define EXT2_FEATURE_COMPAT_IMAGIC_INODES 0x0002
+#define EXT3_FEATURE_COMPAT_HAS_JOURNAL 0x0004
+#define EXT2_FEATURE_COMPAT_EXT_ATTR 0x0008
+#define EXT2_FEATURE_COMPAT_RESIZE_INODE 0x0010
+#define EXT2_FEATURE_COMPAT_DIR_INDEX 0x0020
+
+#define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER 0x0001
+#define EXT2_FEATURE_RO_COMPAT_LARGE_FILE 0x0002
+/* #define EXT2_FEATURE_RO_COMPAT_BTREE_DIR 0x0004 not used */
+
+#define EXT2_FEATURE_INCOMPAT_COMPRESSION 0x0001
+#define EXT2_FEATURE_INCOMPAT_FILETYPE 0x0002
+#define EXT3_FEATURE_INCOMPAT_RECOVER 0x0004 /* Needs recovery */
+#define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV 0x0008 /* Journal device */
+#define EXT2_FEATURE_INCOMPAT_META_BG 0x0010
+#define EXT3_FEATURE_INCOMPAT_EXTENTS 0x0040
+
+
+#define EXT2_FEATURE_COMPAT_SUPP 0
+#define EXT2_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE)
+#define EXT2_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \
+ EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \
+ EXT2_FEATURE_RO_COMPAT_BTREE_DIR)
+
+/*
+ * Default values for user and/or group using reserved blocks
+ */
+#define EXT2_DEF_RESUID 0
+#define EXT2_DEF_RESGID 0
+
+/*
+ * Default mount options
+ */
+#define EXT2_DEFM_DEBUG 0x0001
+#define EXT2_DEFM_BSDGROUPS 0x0002
+#define EXT2_DEFM_XATTR_USER 0x0004
+#define EXT2_DEFM_ACL 0x0008
+#define EXT2_DEFM_UID16 0x0010
+#define EXT3_DEFM_JMODE 0x0060
+#define EXT3_DEFM_JMODE_DATA 0x0020
+#define EXT3_DEFM_JMODE_ORDERED 0x0040
+#define EXT3_DEFM_JMODE_WBACK 0x0060
+
+/*
+ * Structure of a directory entry
+ */
+#define EXT2_NAME_LEN 255
+
+struct ext2_dir_entry {
+ uint32_t inode; /* Inode number */
+ uint16_t rec_len; /* Directory entry length */
+ uint16_t name_len; /* Name length */
+ char name[EXT2_NAME_LEN]; /* File name */
+};
+
+/*
+ * The new version of the directory entry. Since EXT2 structures are
+ * stored in intel byte order, and the name_len field could never be
+ * bigger than 255 chars, it's safe to reclaim the extra byte for the
+ * file_type field.
+ */
+struct ext2_dir_entry_2 {
+ uint32_t inode; /* Inode number */
+ uint16_t rec_len; /* Directory entry length */
+ uint8_t name_len; /* Name length */
+ uint8_t file_type;
+ char name[EXT2_NAME_LEN]; /* File name */
+};
+
+/*
+ * Ext2 directory file types. Only the low 3 bits are used. The
+ * other bits are reserved for now.
+ */
+#define EXT2_FT_UNKNOWN 0
+#define EXT2_FT_REG_FILE 1
+#define EXT2_FT_DIR 2
+#define EXT2_FT_CHRDEV 3
+#define EXT2_FT_BLKDEV 4
+#define EXT2_FT_FIFO 5
+#define EXT2_FT_SOCK 6
+#define EXT2_FT_SYMLINK 7
+
+#define EXT2_FT_MAX 8
+
+/*
+ * EXT2_DIR_PAD defines the directory entries boundaries
+ *
+ * NOTE: It must be a multiple of 4
+ */
+#define EXT2_DIR_PAD 4
+#define EXT2_DIR_ROUND (EXT2_DIR_PAD - 1)
+#define EXT2_DIR_REC_LEN(name_len) (((name_len) + 8 + EXT2_DIR_ROUND) & \
+ ~EXT2_DIR_ROUND)
+
+#endif /* _LINUX_EXT2_FS_H */
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/e2fs_lib.c b/i/pc104/initrd/conf/busybox/e2fsprogs/e2fs_lib.c
new file mode 100644
index 0000000..ea5c802
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/e2fs_lib.c
@@ -0,0 +1,197 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * See README for additional information
+ *
+ * This file can be redistributed under the terms of the GNU Library General
+ * Public License
+ */
+
+#include "libbb.h"
+#include "e2fs_lib.h"
+
+#define HAVE_EXT2_IOCTLS 1
+
+#if INT_MAX == LONG_MAX
+#define IF_LONG_IS_SAME(x) x
+#define IF_LONG_IS_WIDER(x)
+#else
+#define IF_LONG_IS_SAME(x)
+#define IF_LONG_IS_WIDER(x) x
+#endif
+
+static void close_silently(int fd)
+{
+ int e = errno;
+ close(fd);
+ errno = e;
+}
+
+
+/* Iterate a function on each entry of a directory */
+int iterate_on_dir(const char *dir_name,
+ int (*func)(const char *, struct dirent *, void *),
+ void * private)
+{
+ DIR *dir;
+ struct dirent *de, *dep;
+ int max_len, len;
+
+ max_len = PATH_MAX + sizeof(struct dirent);
+ de = xmalloc(max_len+1);
+ memset(de, 0, max_len+1);
+
+ dir = opendir(dir_name);
+ if (dir == NULL) {
+ free(de);
+ return -1;
+ }
+ while ((dep = readdir(dir))) {
+ len = sizeof(struct dirent);
+ if (len < dep->d_reclen)
+ len = dep->d_reclen;
+ if (len > max_len)
+ len = max_len;
+ memcpy(de, dep, len);
+ func(dir_name, de, private);
+ }
+ closedir(dir);
+ free(de);
+ return 0;
+}
+
+
+/* Get/set a file version on an ext2 file system */
+int fgetsetversion(const char *name, unsigned long *get_version, unsigned long set_version)
+{
+#if HAVE_EXT2_IOCTLS
+ int fd, r;
+ IF_LONG_IS_WIDER(int ver;)
+
+ fd = open(name, O_NONBLOCK);
+ if (fd == -1)
+ return -1;
+ if (!get_version) {
+ IF_LONG_IS_WIDER(
+ ver = (int) set_version;
+ r = ioctl(fd, EXT2_IOC_SETVERSION, &ver);
+ )
+ IF_LONG_IS_SAME(
+ r = ioctl(fd, EXT2_IOC_SETVERSION, (void*)&set_version);
+ )
+ } else {
+ IF_LONG_IS_WIDER(
+ r = ioctl(fd, EXT2_IOC_GETVERSION, &ver);
+ *get_version = ver;
+ )
+ IF_LONG_IS_SAME(
+ r = ioctl(fd, EXT2_IOC_GETVERSION, (void*)get_version);
+ )
+ }
+ close_silently(fd);
+ return r;
+#else /* ! HAVE_EXT2_IOCTLS */
+ errno = EOPNOTSUPP;
+ return -1;
+#endif /* ! HAVE_EXT2_IOCTLS */
+}
+
+
+/* Get/set a file flags on an ext2 file system */
+int fgetsetflags(const char *name, unsigned long *get_flags, unsigned long set_flags)
+{
+#if HAVE_EXT2_IOCTLS
+ struct stat buf;
+ int fd, r;
+ IF_LONG_IS_WIDER(int f;)
+
+ if (stat(name, &buf) == 0 /* stat is ok */
+ && !S_ISREG(buf.st_mode) && !S_ISDIR(buf.st_mode)
+ ) {
+ goto notsupp;
+ }
+ fd = open(name, O_NONBLOCK); /* neither read nor write asked for */
+ if (fd == -1)
+ return -1;
+
+ if (!get_flags) {
+ IF_LONG_IS_WIDER(
+ f = (int) set_flags;
+ r = ioctl(fd, EXT2_IOC_SETFLAGS, &f);
+ )
+ IF_LONG_IS_SAME(
+ r = ioctl(fd, EXT2_IOC_SETFLAGS, (void*)&set_flags);
+ )
+ } else {
+ IF_LONG_IS_WIDER(
+ r = ioctl(fd, EXT2_IOC_GETFLAGS, &f);
+ *get_flags = f;
+ )
+ IF_LONG_IS_SAME(
+ r = ioctl(fd, EXT2_IOC_GETFLAGS, (void*)get_flags);
+ )
+ }
+
+ close_silently(fd);
+ return r;
+ notsupp:
+#endif /* HAVE_EXT2_IOCTLS */
+ errno = EOPNOTSUPP;
+ return -1;
+}
+
+
+/* Print file attributes on an ext2 file system */
+struct flags_name {
+ unsigned long flag;
+ char short_name;
+ const char *long_name;
+};
+
+static const struct flags_name flags_array[] = {
+ { EXT2_SECRM_FL, 's', "Secure_Deletion" },
+ { EXT2_UNRM_FL, 'u' , "Undelete" },
+ { EXT2_SYNC_FL, 'S', "Synchronous_Updates" },
+ { EXT2_DIRSYNC_FL, 'D', "Synchronous_Directory_Updates" },
+ { EXT2_IMMUTABLE_FL, 'i', "Immutable" },
+ { EXT2_APPEND_FL, 'a', "Append_Only" },
+ { EXT2_NODUMP_FL, 'd', "No_Dump" },
+ { EXT2_NOATIME_FL, 'A', "No_Atime" },
+ { EXT2_COMPR_FL, 'c', "Compression_Requested" },
+#ifdef ENABLE_COMPRESSION
+ { EXT2_COMPRBLK_FL, 'B', "Compressed_File" },
+ { EXT2_DIRTY_FL, 'Z', "Compressed_Dirty_File" },
+ { EXT2_NOCOMPR_FL, 'X', "Compression_Raw_Access" },
+ { EXT2_ECOMPR_FL, 'E', "Compression_Error" },
+#endif
+ { EXT3_JOURNAL_DATA_FL, 'j', "Journaled_Data" },
+ { EXT2_INDEX_FL, 'I', "Indexed_directory" },
+ { EXT2_NOTAIL_FL, 't', "No_Tailmerging" },
+ { EXT2_TOPDIR_FL, 'T', "Top_of_Directory_Hierarchies" },
+ { 0, '\0', NULL }
+};
+
+void print_flags(FILE *f, unsigned long flags, unsigned options)
+{
+ const struct flags_name *fp;
+
+ if (options & PFOPT_LONG) {
+ int first = 1;
+ for (fp = flags_array; fp->short_name; fp++) {
+ if (flags & fp->flag) {
+ if (!first)
+ fputs(", ", f);
+ fputs(fp->long_name, f);
+ first = 0;
+ }
+ }
+ if (first)
+ fputs("---", f);
+ } else {
+ for (fp = flags_array; fp->short_name; fp++) {
+ char c = '-';
+ if (flags & fp->flag)
+ c = fp->short_name;
+ fputc(c, f);
+ }
+ }
+}
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/e2fs_lib.h b/i/pc104/initrd/conf/busybox/e2fsprogs/e2fs_lib.h
new file mode 100644
index 0000000..1a7d3a1
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/e2fs_lib.h
@@ -0,0 +1,30 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * See README for additional information
+ *
+ * This file can be redistributed under the terms of the GNU Library General
+ * Public License
+ */
+
+/* Constants and structures */
+#include "e2fs_defs.h"
+
+/* Iterate a function on each entry of a directory */
+int iterate_on_dir(const char *dir_name,
+ int (*func)(const char *, struct dirent *, void *),
+ void *private);
+
+/* Get/set a file version on an ext2 file system */
+int fgetsetversion(const char *name, unsigned long *get_version, unsigned long set_version);
+#define fgetversion(name, version) fgetsetversion(name, version, 0)
+#define fsetversion(name, version) fgetsetversion(name, NULL, version)
+
+/* Get/set a file flags on an ext2 file system */
+int fgetsetflags(const char *name, unsigned long *get_flags, unsigned long set_flags);
+#define fgetflags(name, flags) fgetsetflags(name, flags, 0)
+#define fsetflags(name, flags) fgetsetflags(name, NULL, flags)
+
+/* Must be 1 for compatibility with `int long_format'. */
+#define PFOPT_LONG 1
+/* Print file attributes on an ext2 file system */
+void print_flags(FILE *f, unsigned long flags, unsigned options);
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/fsck.c b/i/pc104/initrd/conf/busybox/e2fsprogs/fsck.c
new file mode 100644
index 0000000..ad22fcd
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/fsck.c
@@ -0,0 +1,1243 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * fsck --- A generic, parallelizing front-end for the fsck program.
+ * It will automatically try to run fsck programs in parallel if the
+ * devices are on separate spindles. It is based on the same ideas as
+ * the generic front end for fsck by David Engel and Fred van Kempen,
+ * but it has been completely rewritten from scratch to support
+ * parallel execution.
+ *
+ * Written by Theodore Ts'o, <tytso@mit.edu>
+ *
+ * Miquel van Smoorenburg (miquels@drinkel.ow.org) 20-Oct-1994:
+ * o Changed -t fstype to behave like with mount when -A (all file
+ * systems) or -M (like mount) is specified.
+ * o fsck looks if it can find the fsck.type program to decide
+ * if it should ignore the fs type. This way more fsck programs
+ * can be added without changing this front-end.
+ * o -R flag skip root file system.
+ *
+ * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
+ * 2001, 2002, 2003, 2004, 2005 by Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+/* All filesystem specific hooks have been removed.
+ * If filesystem cannot be determined, we will execute
+ * "fsck.auto". Currently this also happens if you specify
+ * UUID=xxx or LABEL=xxx as an object to check.
+ * Detection code for that is also probably has to be in fsck.auto.
+ *
+ * In other words, this is _really_ is just a driver program which
+ * spawns actual fsck.something for each filesystem to check.
+ * It doesn't guess filesystem types from on-disk format.
+ */
+
+#include "busybox.h"
+
+#define EXIT_OK 0
+#define EXIT_NONDESTRUCT 1
+#define EXIT_DESTRUCT 2
+#define EXIT_UNCORRECTED 4
+#define EXIT_ERROR 8
+#define EXIT_USAGE 16
+#define FSCK_CANCELED 32 /* Aborted with a signal or ^C */
+
+/*
+ * Internal structure for mount table entries.
+ */
+
+struct fs_info {
+ struct fs_info *next;
+ char *device;
+ char *mountpt;
+ char *type;
+ char *opts;
+ int freq;
+ int passno;
+ int flags;
+};
+
+#define FLAG_DONE 1
+#define FLAG_PROGRESS 2
+/*
+ * Structure to allow exit codes to be stored
+ */
+struct fsck_instance {
+ struct fsck_instance *next;
+ int pid;
+ int flags;
+ int exit_status;
+ time_t start_time;
+ char *prog;
+ char *type;
+ char *device;
+ char *base_device; /* /dev/hda for /dev/hdaN etc */
+};
+
+static const char *const ignored_types[] = {
+ "ignore",
+ "iso9660",
+ "nfs",
+ "proc",
+ "sw",
+ "swap",
+ "tmpfs",
+ "devpts",
+ NULL
+};
+
+#if 0
+static const char *const really_wanted[] = {
+ "minix",
+ "ext2",
+ "ext3",
+ "jfs",
+ "reiserfs",
+ "xiafs",
+ "xfs",
+ NULL
+};
+#endif
+
+#define BASE_MD "/dev/md"
+
+static char **devices;
+static char **args;
+static int num_devices;
+static int num_args;
+static int verbose;
+
+#define FS_TYPE_FLAG_NORMAL 0
+#define FS_TYPE_FLAG_OPT 1
+#define FS_TYPE_FLAG_NEGOPT 2
+static char **fs_type_list;
+static uint8_t *fs_type_flag;
+static smallint fs_type_negated;
+
+static volatile smallint cancel_requested;
+static smallint doall;
+static smallint noexecute;
+static smallint serialize;
+static smallint skip_root;
+/* static smallint like_mount; */
+static smallint notitle;
+static smallint parallel_root;
+static smallint force_all_parallel;
+
+/* "progress indicator" code is somewhat buggy and ext[23] specific.
+ * We should be filesystem agnostic. IOW: there should be a well-defined
+ * API for fsck.something, NOT ad-hoc hacks in generic fsck. */
+#define DO_PROGRESS_INDICATOR 0
+#if DO_PROGRESS_INDICATOR
+static smallint progress;
+static int progress_fd;
+#endif
+
+static int num_running;
+static int max_running;
+static char *fstype;
+static struct fs_info *filesys_info;
+static struct fs_info *filesys_last;
+static struct fsck_instance *instance_list;
+
+/*
+ * Return the "base device" given a particular device; this is used to
+ * assure that we only fsck one partition on a particular drive at any
+ * one time. Otherwise, the disk heads will be seeking all over the
+ * place. If the base device cannot be determined, return NULL.
+ *
+ * The base_device() function returns an allocated string which must
+ * be freed.
+ */
+#if ENABLE_FEATURE_DEVFS
+/*
+ * Required for the uber-silly devfs /dev/ide/host1/bus2/target3/lun3
+ * pathames.
+ */
+static const char *const devfs_hier[] = {
+ "host", "bus", "target", "lun", NULL
+};
+#endif
+
+static char *base_device(const char *device)
+{
+ char *str, *cp;
+#if ENABLE_FEATURE_DEVFS
+ const char *const *hier;
+ const char *disk;
+ int len;
+#endif
+ cp = str = xstrdup(device);
+
+ /* Skip over /dev/; if it's not present, give up. */
+ if (strncmp(cp, "/dev/", 5) != 0)
+ goto errout;
+ cp += 5;
+
+ /*
+ * For md devices, we treat them all as if they were all
+ * on one disk, since we don't know how to parallelize them.
+ */
+ if (cp[0] == 'm' && cp[1] == 'd') {
+ cp[2] = 0;
+ return str;
+ }
+
+ /* Handle DAC 960 devices */
+ if (strncmp(cp, "rd/", 3) == 0) {
+ cp += 3;
+ if (cp[0] != 'c' || !isdigit(cp[1])
+ || cp[2] != 'd' || !isdigit(cp[3]))
+ goto errout;
+ cp[4] = 0;
+ return str;
+ }
+
+ /* Now let's handle /dev/hd* and /dev/sd* devices.... */
+ if ((cp[0] == 'h' || cp[0] == 's') && cp[1] == 'd') {
+ cp += 2;
+ /* If there's a single number after /dev/hd, skip it */
+ if (isdigit(*cp))
+ cp++;
+ /* What follows must be an alpha char, or give up */
+ if (!isalpha(*cp))
+ goto errout;
+ cp[1] = 0;
+ return str;
+ }
+
+#if ENABLE_FEATURE_DEVFS
+ /* Now let's handle devfs (ugh) names */
+ len = 0;
+ if (strncmp(cp, "ide/", 4) == 0)
+ len = 4;
+ if (strncmp(cp, "scsi/", 5) == 0)
+ len = 5;
+ if (len) {
+ cp += len;
+ /*
+ * Now we proceed down the expected devfs hierarchy.
+ * i.e., .../host1/bus2/target3/lun4/...
+ * If we don't find the expected token, followed by
+ * some number of digits at each level, abort.
+ */
+ for (hier = devfs_hier; *hier; hier++) {
+ len = strlen(*hier);
+ if (strncmp(cp, *hier, len) != 0)
+ goto errout;
+ cp += len;
+ while (*cp != '/' && *cp != 0) {
+ if (!isdigit(*cp))
+ goto errout;
+ cp++;
+ }
+ cp++;
+ }
+ cp[-1] = 0;
+ return str;
+ }
+
+ /* Now handle devfs /dev/disc or /dev/disk names */
+ disk = 0;
+ if (strncmp(cp, "discs/", 6) == 0)
+ disk = "disc";
+ else if (strncmp(cp, "disks/", 6) == 0)
+ disk = "disk";
+ if (disk) {
+ cp += 6;
+ if (strncmp(cp, disk, 4) != 0)
+ goto errout;
+ cp += 4;
+ while (*cp != '/' && *cp != 0) {
+ if (!isdigit(*cp))
+ goto errout;
+ cp++;
+ }
+ *cp = 0;
+ return str;
+ }
+#endif
+ errout:
+ free(str);
+ return NULL;
+}
+
+static void free_instance(struct fsck_instance *p)
+{
+ free(p->prog);
+ free(p->device);
+ free(p->base_device);
+ free(p);
+}
+
+static struct fs_info *create_fs_device(const char *device, const char *mntpnt,
+ const char *type, const char *opts,
+ int freq, int passno)
+{
+ struct fs_info *fs;
+
+ fs = xzalloc(sizeof(*fs));
+ fs->device = xstrdup(device);
+ fs->mountpt = xstrdup(mntpnt);
+ fs->type = xstrdup(type);
+ fs->opts = xstrdup(opts ? opts : "");
+ fs->freq = freq;
+ fs->passno = passno;
+ /*fs->flags = 0; */
+ /*fs->next = NULL; */
+
+ if (!filesys_info)
+ filesys_info = fs;
+ else
+ filesys_last->next = fs;
+ filesys_last = fs;
+
+ return fs;
+}
+
+static void strip_line(char *line)
+{
+ char *p = line + strlen(line) - 1;
+
+ while (*line) {
+ if (*p != '\n' && *p != '\r')
+ break;
+ *p-- = '\0';
+ }
+}
+
+static char *parse_word(char **buf)
+{
+ char *word, *next;
+
+ word = *buf;
+ if (*word == '\0')
+ return NULL;
+
+ word = skip_whitespace(word);
+ next = skip_non_whitespace(word);
+ if (*next)
+ *next++ = '\0';
+ *buf = next;
+ return word;
+}
+
+static void parse_escape(char *word)
+{
+ char *q, c;
+ const char *p;
+
+ if (!word)
+ return;
+
+ for (p = q = word; *p; q++) {
+ c = *p++;
+ if (c != '\\') {
+ *q = c;
+ } else {
+ *q = bb_process_escape_sequence(&p);
+ }
+ }
+ *q = '\0';
+}
+
+static int parse_fstab_line(char *line, struct fs_info **ret_fs)
+{
+ char *device, *mntpnt, *type, *opts, *freq, *passno, *cp;
+ struct fs_info *fs;
+
+ *ret_fs = 0;
+ strip_line(line);
+ cp = strchr(line, '#');
+ if (cp)
+ *cp = '\0'; /* Ignore everything after the comment char */
+ cp = line;
+
+ device = parse_word(&cp);
+ if (!device) return 0; /* Allow blank lines */
+ mntpnt = parse_word(&cp);
+ type = parse_word(&cp);
+ opts = parse_word(&cp);
+ freq = parse_word(&cp);
+ passno = parse_word(&cp);
+
+ if (!mntpnt || !type)
+ return -1;
+
+ parse_escape(device);
+ parse_escape(mntpnt);
+ parse_escape(type);
+ parse_escape(opts);
+ parse_escape(freq);
+ parse_escape(passno);
+
+ if (strchr(type, ','))
+ type = NULL;
+
+ fs = create_fs_device(device, mntpnt, type ? type : "auto", opts,
+ freq ? atoi(freq) : -1,
+ passno ? atoi(passno) : -1);
+ *ret_fs = fs;
+ return 0;
+}
+
+/* Load the filesystem database from /etc/fstab */
+static void load_fs_info(const char *filename)
+{
+ FILE *f;
+ int lineno = 0;
+ int old_fstab = 1;
+ struct fs_info *fs;
+
+ f = fopen_or_warn(filename, "r");
+ if (f == NULL) {
+ /*bb_perror_msg("WARNING: cannot open %s", filename);*/
+ return;
+ }
+ while (1) {
+ int r;
+ char *buf = xmalloc_getline(f);
+ if (!buf) break;
+ r = parse_fstab_line(buf, &fs);
+ free(buf);
+ lineno++;
+ if (r < 0) {
+ bb_error_msg("WARNING: bad format "
+ "on line %d of %s", lineno, filename);
+ continue;
+ }
+ if (!fs)
+ continue;
+ if (fs->passno < 0)
+ fs->passno = 0;
+ else
+ old_fstab = 0;
+ }
+ fclose(f);
+
+ if (old_fstab) {
+ fputs("\007"
+"WARNING: Your /etc/fstab does not contain the fsck passno field.\n"
+"I will kludge around things for you, but you should fix\n"
+"your /etc/fstab file as soon as you can.\n\n", stderr);
+ for (fs = filesys_info; fs; fs = fs->next) {
+ fs->passno = 1;
+ }
+ }
+}
+
+/* Lookup filesys in /etc/fstab and return the corresponding entry. */
+static struct fs_info *lookup(char *filesys)
+{
+ struct fs_info *fs;
+
+ for (fs = filesys_info; fs; fs = fs->next) {
+ if (strcmp(filesys, fs->device) == 0
+ || (fs->mountpt && strcmp(filesys, fs->mountpt) == 0)
+ )
+ break;
+ }
+
+ return fs;
+}
+
+#if DO_PROGRESS_INDICATOR
+static int progress_active(void)
+{
+ struct fsck_instance *inst;
+
+ for (inst = instance_list; inst; inst = inst->next) {
+ if (inst->flags & FLAG_DONE)
+ continue;
+ if (inst->flags & FLAG_PROGRESS)
+ return 1;
+ }
+ return 0;
+}
+#endif
+
+
+/*
+ * Send a signal to all outstanding fsck child processes
+ */
+static void kill_all_if_cancel_requested(void)
+{
+ static smallint kill_sent;
+
+ struct fsck_instance *inst;
+
+ if (!cancel_requested || kill_sent)
+ return;
+
+ for (inst = instance_list; inst; inst = inst->next) {
+ if (inst->flags & FLAG_DONE)
+ continue;
+ kill(inst->pid, SIGTERM);
+ }
+ kill_sent = 1;
+}
+
+/*
+ * Wait for one child process to exit; when it does, unlink it from
+ * the list of executing child processes, and return it.
+ */
+static struct fsck_instance *wait_one(int flags)
+{
+ int status;
+ int sig;
+ struct fsck_instance *inst, *prev;
+ pid_t pid;
+
+ if (!instance_list)
+ return NULL;
+
+ if (noexecute) {
+ inst = instance_list;
+ prev = NULL;
+#ifdef RANDOM_DEBUG
+ while (inst->next && (random() & 1)) {
+ prev = inst;
+ inst = inst->next;
+ }
+#endif
+ inst->exit_status = 0;
+ goto ret_inst;
+ }
+
+ /*
+ * gcc -Wall fails saving throw against stupidity
+ * (inst and prev are thought to be uninitialized variables)
+ */
+ inst = prev = NULL;
+
+ do {
+ pid = waitpid(-1, &status, flags);
+ kill_all_if_cancel_requested();
+ if (pid == 0 && (flags & WNOHANG))
+ return NULL;
+ if (pid < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ if (errno == ECHILD) {
+ bb_error_msg("wait: no more child process?!?");
+ return NULL;
+ }
+ bb_perror_msg("wait");
+ continue;
+ }
+ prev = NULL;
+ inst = instance_list;
+ while (inst) {
+ if (inst->pid == pid)
+ break;
+ prev = inst;
+ inst = inst->next;
+ }
+ } while (!inst);
+
+ if (WIFEXITED(status))
+ status = WEXITSTATUS(status);
+ else if (WIFSIGNALED(status)) {
+ sig = WTERMSIG(status);
+ status = EXIT_UNCORRECTED;
+ if (sig != SIGINT) {
+ printf("Warning... %s %s exited "
+ "with signal %d\n",
+ inst->prog, inst->device, sig);
+ status = EXIT_ERROR;
+ }
+ } else {
+ printf("%s %s: status is %x, should never happen\n",
+ inst->prog, inst->device, status);
+ status = EXIT_ERROR;
+ }
+ inst->exit_status = status;
+
+#if DO_PROGRESS_INDICATOR
+ if (progress && (inst->flags & FLAG_PROGRESS) && !progress_active()) {
+ struct fsck_instance *inst2;
+ for (inst2 = instance_list; inst2; inst2 = inst2->next) {
+ if (inst2->flags & FLAG_DONE)
+ continue;
+ if (strcmp(inst2->type, "ext2") != 0
+ && strcmp(inst2->type, "ext3") != 0
+ ) {
+ continue;
+ }
+ /* ext[23], we will send USR1
+ * (request to start displaying progress bar)
+ *
+ * If we've just started the fsck, wait a tiny
+ * bit before sending the kill, to give it
+ * time to set up the signal handler
+ */
+ if (inst2->start_time >= time(NULL) - 1)
+ sleep(1);
+ kill(inst2->pid, SIGUSR1);
+ inst2->flags |= FLAG_PROGRESS;
+ break;
+ }
+ }
+#endif
+
+ ret_inst:
+ if (prev)
+ prev->next = inst->next;
+ else
+ instance_list = inst->next;
+ if (verbose > 1)
+ printf("Finished with %s (exit status %d)\n",
+ inst->device, inst->exit_status);
+ num_running--;
+ return inst;
+}
+
+#define FLAG_WAIT_ALL 0
+#define FLAG_WAIT_ATLEAST_ONE 1
+/*
+ * Wait until all executing child processes have exited; return the
+ * logical OR of all of their exit code values.
+ */
+static int wait_many(int flags)
+{
+ struct fsck_instance *inst;
+ int global_status = 0;
+ int wait_flags = 0;
+
+ while ((inst = wait_one(wait_flags))) {
+ global_status |= inst->exit_status;
+ free_instance(inst);
+#ifdef RANDOM_DEBUG
+ if (noexecute && (flags & WNOHANG) && !(random() % 3))
+ break;
+#endif
+ if (flags & FLAG_WAIT_ATLEAST_ONE)
+ wait_flags = WNOHANG;
+ }
+ return global_status;
+}
+
+/*
+ * Execute a particular fsck program, and link it into the list of
+ * child processes we are waiting for.
+ */
+static void execute(const char *type, const char *device, const char *mntpt,
+ int interactive)
+{
+ char *argv[num_args + 4]; /* see count below: */
+ int argc;
+ int i;
+ struct fsck_instance *inst;
+ pid_t pid;
+
+ inst = xzalloc(sizeof(*inst));
+
+ argv[0] = xasprintf("fsck.%s", type); /* 1 */
+ for (i = 0; i < num_args; i++)
+ argv[i+1] = args[i]; /* num_args */
+ argc = num_args + 1;
+
+#if DO_PROGRESS_INDICATOR
+ if (progress && !progress_active()) {
+ if (strcmp(type, "ext2") == 0
+ || strcmp(type, "ext3") == 0
+ ) {
+ argv[argc++] = xasprintf("-C%d", progress_fd); /* 1 */
+ inst->flags |= FLAG_PROGRESS;
+ }
+ }
+#endif
+
+ argv[argc++] = xstrdup(device); /* 1 */
+ argv[argc] = NULL; /* 1 */
+
+ if (verbose || noexecute) {
+ printf("[%s (%d) -- %s]", argv[0], num_running,
+ mntpt ? mntpt : device);
+ for (i = 0; i < argc; i++)
+ printf(" %s", argv[i]);
+ puts("");
+ }
+
+ /* Fork and execute the correct program. */
+ pid = -1;
+ if (!noexecute) {
+ pid = fork(); /* TODO: NOMMU friendly way (vfork)? */
+ if (pid < 0)
+ bb_perror_msg_and_die("fork");
+ if (pid == 0) {
+ /* Child */
+ if (!interactive) {
+ /* NB: e2fsck will complain because of this!
+ * Use "fsck -s" to avoid... */
+ close(0);
+ }
+ BB_EXECVP(argv[0], argv);
+ bb_perror_msg_and_die("%s", argv[0]);
+ }
+ }
+
+ for (i = num_args+1; i < argc; i++)
+ free(argv[i]);
+
+ inst->pid = pid;
+ inst->prog = argv[0];
+ inst->type = xstrdup(type);
+ inst->device = xstrdup(device);
+ inst->base_device = base_device(device);
+ inst->start_time = time(NULL);
+
+ /* Add to the list of running fsck's.
+ * (was adding to the end, but adding to the front is simpler...) */
+ inst->next = instance_list;
+ instance_list = inst;
+}
+
+/*
+ * Run the fsck program on a particular device
+ *
+ * If the type is specified using -t, and it isn't prefixed with "no"
+ * (as in "noext2") and only one filesystem type is specified, then
+ * use that type regardless of what is specified in /etc/fstab.
+ *
+ * If the type isn't specified by the user, then use either the type
+ * specified in /etc/fstab, or "auto".
+ */
+static void fsck_device(struct fs_info *fs, int interactive)
+{
+ const char *type;
+
+ if (strcmp(fs->type, "auto") != 0) {
+ type = fs->type;
+ if (verbose > 2)
+ bb_info_msg("using filesystem type '%s' %s",
+ type, "from fstab");
+ } else if (fstype
+ && (fstype[0] != 'n' || fstype[1] != 'o') /* != "no" */
+ && strncmp(fstype, "opts=", 5) != 0
+ && strncmp(fstype, "loop", 4) != 0
+ && !strchr(fstype, ',')
+ ) {
+ type = fstype;
+ if (verbose > 2)
+ bb_info_msg("using filesystem type '%s' %s",
+ type, "from -t");
+ } else {
+ type = "auto";
+ if (verbose > 2)
+ bb_info_msg("using filesystem type '%s' %s",
+ type, "(default)");
+ }
+
+ num_running++;
+ execute(type, fs->device, fs->mountpt, interactive);
+}
+
+/*
+ * Returns TRUE if a partition on the same disk is already being
+ * checked.
+ */
+static int device_already_active(char *device)
+{
+ struct fsck_instance *inst;
+ char *base;
+
+ if (force_all_parallel)
+ return 0;
+
+#ifdef BASE_MD
+ /* Don't check a soft raid disk with any other disk */
+ if (instance_list
+ && (!strncmp(instance_list->device, BASE_MD, sizeof(BASE_MD)-1)
+ || !strncmp(device, BASE_MD, sizeof(BASE_MD)-1))
+ ) {
+ return 1;
+ }
+#endif
+
+ base = base_device(device);
+ /*
+ * If we don't know the base device, assume that the device is
+ * already active if there are any fsck instances running.
+ */
+ if (!base)
+ return (instance_list != NULL);
+
+ for (inst = instance_list; inst; inst = inst->next) {
+ if (!inst->base_device || !strcmp(base, inst->base_device)) {
+ free(base);
+ return 1;
+ }
+ }
+
+ free(base);
+ return 0;
+}
+
+/*
+ * This function returns true if a particular option appears in a
+ * comma-delimited options list
+ */
+static int opt_in_list(char *opt, char *optlist)
+{
+ char *s;
+ int len;
+
+ if (!optlist)
+ return 0;
+
+ len = strlen(opt);
+ s = optlist - 1;
+ while (1) {
+ s = strstr(s + 1, opt);
+ if (!s)
+ return 0;
+ /* neither "opt.." nor "xxx,opt.."? */
+ if (s != optlist && s[-1] != ',')
+ continue;
+ /* neither "..opt" nor "..opt,xxx"? */
+ if (s[len] != '\0' && s[len] != ',')
+ continue;
+ return 1;
+ }
+}
+
+/* See if the filesystem matches the criteria given by the -t option */
+static int fs_match(struct fs_info *fs)
+{
+ int n, ret, checked_type;
+ char *cp;
+
+ if (!fs_type_list)
+ return 1;
+
+ ret = 0;
+ checked_type = 0;
+ n = 0;
+ while (1) {
+ cp = fs_type_list[n];
+ if (!cp)
+ break;
+ switch (fs_type_flag[n]) {
+ case FS_TYPE_FLAG_NORMAL:
+ checked_type++;
+ if (strcmp(cp, fs->type) == 0)
+ ret = 1;
+ break;
+ case FS_TYPE_FLAG_NEGOPT:
+ if (opt_in_list(cp, fs->opts))
+ return 0;
+ break;
+ case FS_TYPE_FLAG_OPT:
+ if (!opt_in_list(cp, fs->opts))
+ return 0;
+ break;
+ }
+ n++;
+ }
+ if (checked_type == 0)
+ return 1;
+
+ return (fs_type_negated ? !ret : ret);
+}
+
+/* Check if we should ignore this filesystem. */
+static int ignore(struct fs_info *fs)
+{
+ /*
+ * If the pass number is 0, ignore it.
+ */
+ if (fs->passno == 0)
+ return 1;
+
+ /*
+ * If a specific fstype is specified, and it doesn't match,
+ * ignore it.
+ */
+ if (!fs_match(fs))
+ return 1;
+
+ /* Are we ignoring this type? */
+ if (index_in_str_array(ignored_types, fs->type) >= 0)
+ return 1;
+
+ /* We can and want to check this file system type. */
+ return 0;
+}
+
+/* Check all file systems, using the /etc/fstab table. */
+static int check_all(void)
+{
+ struct fs_info *fs;
+ int status = EXIT_OK;
+ smallint not_done_yet;
+ smallint pass_done;
+ int passno;
+
+ if (verbose)
+ puts("Checking all filesystems");
+
+ /*
+ * Do an initial scan over the filesystem; mark filesystems
+ * which should be ignored as done, and resolve any "auto"
+ * filesystem types (done as a side-effect of calling ignore()).
+ */
+ for (fs = filesys_info; fs; fs = fs->next) {
+ if (ignore(fs))
+ fs->flags |= FLAG_DONE;
+ }
+
+ /*
+ * Find and check the root filesystem.
+ */
+ if (!parallel_root) {
+ for (fs = filesys_info; fs; fs = fs->next) {
+ if (LONE_CHAR(fs->mountpt, '/'))
+ break;
+ }
+ if (fs) {
+ if (!skip_root && !ignore(fs)) {
+ fsck_device(fs, 1);
+ status |= wait_many(FLAG_WAIT_ALL);
+ if (status > EXIT_NONDESTRUCT)
+ return status;
+ }
+ fs->flags |= FLAG_DONE;
+ }
+ }
+ /*
+ * This is for the bone-headed user who enters the root
+ * filesystem twice. Skip root will skip all root entries.
+ */
+ if (skip_root)
+ for (fs = filesys_info; fs; fs = fs->next)
+ if (LONE_CHAR(fs->mountpt, '/'))
+ fs->flags |= FLAG_DONE;
+
+ not_done_yet = 1;
+ passno = 1;
+ while (not_done_yet) {
+ not_done_yet = 0;
+ pass_done = 1;
+
+ for (fs = filesys_info; fs; fs = fs->next) {
+ if (cancel_requested)
+ break;
+ if (fs->flags & FLAG_DONE)
+ continue;
+ /*
+ * If the filesystem's pass number is higher
+ * than the current pass number, then we don't
+ * do it yet.
+ */
+ if (fs->passno > passno) {
+ not_done_yet = 1;
+ continue;
+ }
+ /*
+ * If a filesystem on a particular device has
+ * already been spawned, then we need to defer
+ * this to another pass.
+ */
+ if (device_already_active(fs->device)) {
+ pass_done = 0;
+ continue;
+ }
+ /*
+ * Spawn off the fsck process
+ */
+ fsck_device(fs, serialize);
+ fs->flags |= FLAG_DONE;
+
+ /*
+ * Only do one filesystem at a time, or if we
+ * have a limit on the number of fsck's extant
+ * at one time, apply that limit.
+ */
+ if (serialize
+ || (max_running && (num_running >= max_running))
+ ) {
+ pass_done = 0;
+ break;
+ }
+ }
+ if (cancel_requested)
+ break;
+ if (verbose > 1)
+ printf("--waiting-- (pass %d)\n", passno);
+ status |= wait_many(pass_done ? FLAG_WAIT_ALL :
+ FLAG_WAIT_ATLEAST_ONE);
+ if (pass_done) {
+ if (verbose > 1)
+ puts("----------------------------------");
+ passno++;
+ } else
+ not_done_yet = 1;
+ }
+ kill_all_if_cancel_requested();
+ status |= wait_many(FLAG_WAIT_ATLEAST_ONE);
+ return status;
+}
+
+/*
+ * Deal with the fsck -t argument.
+ * Huh, for mount "-t novfat,nfs" means "neither vfat nor nfs"!
+ * Why here we require "-t novfat,nonfs" ??
+ */
+static void compile_fs_type(char *fs_type)
+{
+ char *s;
+ int num = 2;
+ smallint negate;
+
+ if (fs_type) {
+ s = fs_type;
+ while ((s = strchr(s, ','))) {
+ num++;
+ s++;
+ }
+ }
+
+ fs_type_list = xzalloc(num * sizeof(fs_type_list[0]));
+ fs_type_flag = xzalloc(num * sizeof(fs_type_flag[0]));
+ fs_type_negated = -1; /* not yet known is it negated or not */
+
+ if (!fs_type)
+ return;
+
+ num = 0;
+ s = fs_type;
+ while (1) {
+ char *comma;
+
+ negate = 0;
+ if (s[0] == 'n' && s[1] == 'o') { /* "no.." */
+ s += 2;
+ negate = 1;
+ } else if (s[0] == '!') {
+ s++;
+ negate = 1;
+ }
+
+ if (strcmp(s, "loop") == 0)
+ /* loop is really short-hand for opts=loop */
+ goto loop_special_case;
+ if (strncmp(s, "opts=", 5) == 0) {
+ s += 5;
+ loop_special_case:
+ fs_type_flag[num] = negate ? FS_TYPE_FLAG_NEGOPT : FS_TYPE_FLAG_OPT;
+ } else {
+ if (fs_type_negated == -1)
+ fs_type_negated = negate;
+ if (fs_type_negated != negate)
+ bb_error_msg_and_die(
+"either all or none of the filesystem types passed to -t must be prefixed "
+"with 'no' or '!'");
+ }
+ comma = strchr(s, ',');
+ fs_type_list[num++] = comma ? xstrndup(s, comma-s) : xstrdup(s);
+ if (!comma)
+ break;
+ s = comma + 1;
+ }
+}
+
+static void parse_args(int argc, char *argv[])
+{
+ int i, j;
+ char *arg, *tmp;
+ char *options = NULL;
+ int optpos = 0;
+ int opts_for_fsck = 0;
+
+ /* in bss, so already zeroed
+ num_devices = 0;
+ num_args = 0;
+ instance_list = NULL;
+ */
+
+/* TODO: getopt32 */
+ for (i = 1; i < argc; i++) {
+ arg = argv[i];
+
+ /* "/dev/blk" or "/path" or "UUID=xxx" or "LABEL=xxx" */
+ if ((arg[0] == '/' && !opts_for_fsck) || strchr(arg, '=')) {
+// FIXME: must check that arg is a blkdev, or resolve
+// "/path", "UUID=xxx" or "LABEL=xxx" into block device name
+// ("UUID=xxx"/"LABEL=xxx" can probably shifted to fsck.auto duties)
+ devices = xrealloc(devices, (num_devices+1) * sizeof(devices[0]));
+ devices[num_devices++] = xstrdup(arg);
+ continue;
+ }
+
+ if (arg[0] != '-' || opts_for_fsck) {
+ args = xrealloc(args, (num_args+1) * sizeof(args[0]));
+ args[num_args++] = xstrdup(arg);
+ continue;
+ }
+
+ for (j = 1; arg[j]; j++) {
+ if (opts_for_fsck) {
+ optpos++;
+ /* one extra for '\0' */
+ options = xrealloc(options, optpos + 2);
+ options[optpos] = arg[j];
+ continue;
+ }
+ switch (arg[j]) {
+ case 'A':
+ doall = 1;
+ break;
+#if DO_PROGRESS_INDICATOR
+ case 'C':
+ progress = 1;
+ if (arg[++j]) { /* -Cn */
+ progress_fd = xatoi_u(&arg[j]);
+ goto next_arg;
+ }
+ /* -C n */
+ progress_fd = xatoi_u(argv[++i]);
+ goto next_arg;
+#endif
+ case 'V':
+ verbose++;
+ break;
+ case 'N':
+ noexecute = 1;
+ break;
+ case 'R':
+ skip_root = 1;
+ break;
+ case 'T':
+ notitle = 1;
+ break;
+/* case 'M':
+ like_mount = 1;
+ break; */
+ case 'P':
+ parallel_root = 1;
+ break;
+ case 's':
+ serialize = 1;
+ break;
+ case 't':
+ if (fstype)
+ bb_show_usage();
+ if (arg[++j])
+ tmp = &arg[j];
+ else if (++i < argc)
+ tmp = argv[i];
+ else
+ bb_show_usage();
+ fstype = xstrdup(tmp);
+ compile_fs_type(fstype);
+ goto next_arg;
+ case '-':
+ opts_for_fsck++;
+ break;
+ case '?':
+ bb_show_usage();
+ break;
+ default:
+ optpos++;
+ /* one extra for '\0' */
+ options = xrealloc(options, optpos + 2);
+ options[optpos] = arg[j];
+ break;
+ }
+ }
+ next_arg:
+ if (optpos) {
+ options[0] = '-';
+ options[optpos + 1] = '\0';
+ args = xrealloc(args, (num_args+1) * sizeof(args[0]));
+ args[num_args++] = options;
+ optpos = 0;
+ options = NULL;
+ }
+ }
+ if (getenv("FSCK_FORCE_ALL_PARALLEL"))
+ force_all_parallel = 1;
+ tmp = getenv("FSCK_MAX_INST");
+ if (tmp)
+ max_running = xatoi(tmp);
+}
+
+static void signal_cancel(int sig ATTRIBUTE_UNUSED)
+{
+ cancel_requested = 1;
+}
+
+int fsck_main(int argc, char *argv[]);
+int fsck_main(int argc, char *argv[])
+{
+ int i, status = 0;
+ int interactive;
+ const char *fstab;
+ struct fs_info *fs;
+ struct sigaction sa;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = signal_cancel;
+ sigaction(SIGINT, &sa, 0);
+ sigaction(SIGTERM, &sa, 0);
+
+ setbuf(stdout, NULL);
+
+ parse_args(argc, argv);
+
+ if (!notitle)
+ puts("fsck (busybox "BB_VER", "BB_BT")");
+
+ /* Even plain "fsck /dev/hda1" needs fstab to get fs type,
+ * so we are scanning it anyway */
+ fstab = getenv("FSTAB_FILE");
+ if (!fstab)
+ fstab = "/etc/fstab";
+ load_fs_info(fstab);
+
+ interactive = (num_devices == 1) | serialize;
+
+ /* If -A was specified ("check all"), do that! */
+ if (doall)
+ return check_all();
+
+ if (num_devices == 0) {
+ serialize = 1;
+ interactive = 1;
+ return check_all();
+ }
+
+ for (i = 0; i < num_devices; i++) {
+ if (cancel_requested) {
+ kill_all_if_cancel_requested();
+ break;
+ }
+
+ fs = lookup(devices[i]);
+ if (!fs)
+ fs = create_fs_device(devices[i], 0, "auto", 0, -1, -1);
+ fsck_device(fs, interactive);
+
+ if (serialize
+ || (max_running && (num_running >= max_running))
+ ) {
+ struct fsck_instance *inst;
+
+ inst = wait_one(0);
+ if (inst) {
+ status |= inst->exit_status;
+ free_instance(inst);
+ }
+ if (verbose > 1)
+ puts("----------------------------------");
+ }
+ }
+ status |= wait_many(FLAG_WAIT_ALL);
+ return status;
+}
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/lsattr.c b/i/pc104/initrd/conf/busybox/e2fsprogs/lsattr.c
new file mode 100644
index 0000000..8945b53
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/lsattr.c
@@ -0,0 +1,115 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * lsattr.c - List file attributes on an ext2 file system
+ *
+ * Copyright (C) 1993, 1994 Remy Card <card@masi.ibp.fr>
+ * Laboratoire MASI, Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ * This file can be redistributed under the terms of the GNU General
+ * Public License
+ */
+
+/*
+ * History:
+ * 93/10/30 - Creation
+ * 93/11/13 - Replace stat() calls by lstat() to avoid loops
+ * 94/02/27 - Integrated in Ted's distribution
+ * 98/12/29 - Display version info only when -V specified (G M Sipe)
+ */
+
+#include "busybox.h"
+#include "e2fs_lib.h"
+
+enum {
+ OPT_RECUR = 0x1,
+ OPT_ALL = 0x2,
+ OPT_DIRS_OPT = 0x4,
+ OPT_PF_LONG = 0x8,
+ OPT_GENERATION = 0x10,
+};
+
+static void list_attributes(const char *name)
+{
+ unsigned long fsflags;
+ unsigned long generation;
+
+ if (fgetflags(name, &fsflags) == -1)
+ goto read_err;
+
+ if (option_mask32 & OPT_GENERATION) {
+ if (fgetversion(name, &generation) == -1)
+ goto read_err;
+ printf("%5lu ", generation);
+ }
+
+ if (option_mask32 & OPT_PF_LONG) {
+ printf("%-28s ", name);
+ print_flags(stdout, fsflags, PFOPT_LONG);
+ puts("");
+ } else {
+ print_flags(stdout, fsflags, 0);
+ printf(" %s\n", name);
+ }
+
+ return;
+ read_err:
+ bb_perror_msg("reading %s", name);
+}
+
+static int lsattr_dir_proc(const char *dir_name, struct dirent *de,
+ void *private)
+{
+ struct stat st;
+ char *path;
+
+ path = concat_path_file(dir_name, de->d_name);
+
+ if (lstat(path, &st) == -1)
+ bb_perror_msg("stat %s", path);
+
+ else if (de->d_name[0] != '.' || (option_mask32 & OPT_ALL)) {
+ list_attributes(path);
+ if (S_ISDIR(st.st_mode) && (option_mask32 & OPT_RECUR)
+ && (de->d_name[0] != '.'
+ || (de->d_name[1] != '\0' && NOT_LONE_CHAR(de->d_name+1, '.')))
+ ) {
+ printf("\n%s:\n", path);
+ iterate_on_dir(path, lsattr_dir_proc, NULL);
+ puts("");
+ }
+ }
+
+ free(path);
+
+ return 0;
+}
+
+static void lsattr_args(const char *name)
+{
+ struct stat st;
+
+ if (lstat(name, &st) == -1) {
+ bb_perror_msg("stat %s", name);
+ } else if (S_ISDIR(st.st_mode) && !(option_mask32 & OPT_DIRS_OPT)) {
+ iterate_on_dir(name, lsattr_dir_proc, NULL);
+ } else {
+ list_attributes(name);
+ }
+}
+
+int lsattr_main(int argc, char **argv);
+int lsattr_main(int argc, char **argv)
+{
+ getopt32(argc, argv, "Radlv");
+ argv += optind;
+
+ if (!*argv)
+ lsattr_args(".");
+ else {
+ while (*argv)
+ lsattr_args(*argv++);
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/Config.in b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/Config.in
new file mode 100644
index 0000000..0062b2f
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/Config.in
@@ -0,0 +1,67 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+menu "Linux Ext2 FS Progs"
+
+config CHATTR
+ bool "chattr"
+ default n
+ help
+ chattr changes the file attributes on a second extended file system.
+
+config E2FSCK
+ bool "e2fsck"
+ default n
+ help
+ e2fsck is used to check Linux second extended file systems (ext2fs).
+ e2fsck also supports ext2 filesystems countaining a journal (ext3).
+ The normal compat symlinks 'fsck.ext2' and 'fsck.ext3' are also
+ provided.
+
+config FSCK
+ bool "fsck"
+ default n
+ help
+ fsck is used to check and optionally repair one or more filesystems.
+ In actuality, fsck is simply a front-end for the various file system
+ checkers (fsck.fstype) available under Linux.
+
+config LSATTR
+ bool "lsattr"
+ default n
+ help
+ lsattr lists the file attributes on a second extended file system.
+
+config MKE2FS
+ bool "mke2fs"
+ default n
+ help
+ mke2fs is used to create an ext2/ext3 filesystem. The normal compat
+ symlinks 'mkfs.ext2' and 'mkfs.ext3' are also provided.
+
+config TUNE2FS
+ bool "tune2fs"
+ default n
+ help
+ tune2fs allows the system administrator to adjust various tunable
+ filesystem parameters on Linux ext2/ext3 filesystems.
+
+config E2LABEL
+ bool "e2label"
+ default n
+ depends on TUNE2FS
+ help
+ e2label will display or change the filesystem label on the ext2
+ filesystem located on device.
+
+config FINDFS
+ bool "findfs"
+ default n
+ depends on TUNE2FS
+ help
+ findfs will search the disks in the system looking for a filesystem
+ which has a label matching label or a UUID equal to uuid.
+
+endmenu
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/Kbuild b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/Kbuild
new file mode 100644
index 0000000..b05bb92
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/Kbuild
@@ -0,0 +1,16 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under the GPL v2, see the file LICENSE in this tarball.
+
+lib-y:=
+
+lib-$(CONFIG_CHATTR) += chattr.o
+lib-$(CONFIG_E2FSCK) += e2fsck.o util.o
+lib-$(CONFIG_FSCK) += fsck.o util.o
+lib-$(CONFIG_LSATTR) += lsattr.o
+lib-$(CONFIG_MKE2FS) += mke2fs.o util.o
+lib-$(CONFIG_TUNE2FS) += tune2fs.o util.o
+
+CFLAGS += -include $(srctree)/e2fsprogs/e2fsbb.h
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/README b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/README
new file mode 100644
index 0000000..fac0901
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/README
@@ -0,0 +1,3 @@
+This is a pretty straight rip from the e2fsprogs pkg.
+
+See README's in subdirs for specific info.
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/blkid/Kbuild b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/blkid/Kbuild
new file mode 100644
index 0000000..ddcfdfd
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/blkid/Kbuild
@@ -0,0 +1,23 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under the GPL v2, see the file LICENSE in this tarball.
+
+NEEDED-$(CONFIG_E2FSCK) = y
+NEEDED-$(CONFIG_FSCK) = y
+NEEDED-$(CONFIG_MKE2FS) = y
+NEEDED-$(CONFIG_TUNE2FS) = y
+
+lib-y:=
+lib-$(NEEDED-y) += cache.o dev.o devname.o devno.o blkid_getsize.o \
+ probe.o read.o resolve.o save.o tag.o list.o
+
+CFLAGS_dev.o := -include $(srctree)/include/busybox.h
+CFLAGS_devname.o := -include $(srctree)/include/busybox.h
+CFLAGS_devno.o := -include $(srctree)/include/busybox.h
+CFLAGS_blkid_getsize.o := -include $(srctree)/include/busybox.h
+CFLAGS_probe.o := -include $(srctree)/include/busybox.h
+CFLAGS_save.o := -include $(srctree)/include/busybox.h
+CFLAGS_tag.o := -include $(srctree)/include/busybox.h
+CFLAGS_list.o := -include $(srctree)/include/busybox.h
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/blkid/blkid.h b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/blkid/blkid.h
new file mode 100644
index 0000000..4fa9f6f
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/blkid/blkid.h
@@ -0,0 +1,105 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * blkid.h - Interface for libblkid, a library to identify block devices
+ *
+ * Copyright (C) 2001 Andreas Dilger
+ * Copyright (C) 2003 Theodore Ts'o
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+
+#ifndef _BLKID_BLKID_H
+#define _BLKID_BLKID_H
+
+#include <sys/types.h>
+#include <linux/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define BLKID_VERSION "1.0.0"
+#define BLKID_DATE "12-Feb-2003"
+
+typedef struct blkid_struct_dev *blkid_dev;
+typedef struct blkid_struct_cache *blkid_cache;
+typedef __s64 blkid_loff_t;
+
+typedef struct blkid_struct_tag_iterate *blkid_tag_iterate;
+typedef struct blkid_struct_dev_iterate *blkid_dev_iterate;
+
+/*
+ * Flags for blkid_get_dev
+ *
+ * BLKID_DEV_CREATE Create an empty device structure if not found
+ * in the cache.
+ * BLKID_DEV_VERIFY Make sure the device structure corresponds
+ * with reality.
+ * BLKID_DEV_FIND Just look up a device entry, and return NULL
+ * if it is not found.
+ * BLKID_DEV_NORMAL Get a valid device structure, either from the
+ * cache or by probing the device.
+ */
+#define BLKID_DEV_FIND 0x0000
+#define BLKID_DEV_CREATE 0x0001
+#define BLKID_DEV_VERIFY 0x0002
+#define BLKID_DEV_NORMAL (BLKID_DEV_CREATE | BLKID_DEV_VERIFY)
+
+/* cache.c */
+extern void blkid_put_cache(blkid_cache cache);
+extern int blkid_get_cache(blkid_cache *cache, const char *filename);
+
+/* dev.c */
+extern const char *blkid_dev_devname(blkid_dev dev);
+
+extern blkid_dev_iterate blkid_dev_iterate_begin(blkid_cache cache);
+extern int blkid_dev_set_search(blkid_dev_iterate iter,
+ char *search_type, char *search_value);
+extern int blkid_dev_next(blkid_dev_iterate iterate, blkid_dev *dev);
+extern void blkid_dev_iterate_end(blkid_dev_iterate iterate);
+
+/* devno.c */
+extern char *blkid_devno_to_devname(dev_t devno);
+
+/* devname.c */
+extern int blkid_probe_all(blkid_cache cache);
+extern int blkid_probe_all_new(blkid_cache cache);
+extern blkid_dev blkid_get_dev(blkid_cache cache, const char *devname,
+ int flags);
+
+/* getsize.c */
+extern blkid_loff_t blkid_get_dev_size(int fd);
+
+/* probe.c */
+int blkid_known_fstype(const char *fstype);
+extern blkid_dev blkid_verify(blkid_cache cache, blkid_dev dev);
+
+/* read.c */
+
+/* resolve.c */
+extern char *blkid_get_tag_value(blkid_cache cache, const char *tagname,
+ const char *devname);
+extern char *blkid_get_devname(blkid_cache cache, const char *token,
+ const char *value);
+
+/* tag.c */
+extern blkid_tag_iterate blkid_tag_iterate_begin(blkid_dev dev);
+extern int blkid_tag_next(blkid_tag_iterate iterate,
+ const char **type, const char **value);
+extern void blkid_tag_iterate_end(blkid_tag_iterate iterate);
+extern int blkid_dev_has_tag(blkid_dev dev, const char *type,
+ const char *value);
+extern blkid_dev blkid_find_dev_with_tag(blkid_cache cache,
+ const char *type,
+ const char *value);
+extern int blkid_parse_tag_string(const char *token, char **ret_type,
+ char **ret_val);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _BLKID_BLKID_H */
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/blkid/blkidP.h b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/blkid/blkidP.h
new file mode 100644
index 0000000..c7cb8ab
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/blkid/blkidP.h
@@ -0,0 +1,187 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * blkidP.h - Internal interfaces for libblkid
+ *
+ * Copyright (C) 2001 Andreas Dilger
+ * Copyright (C) 2003 Theodore Ts'o
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+
+#ifndef _BLKID_BLKIDP_H
+#define _BLKID_BLKIDP_H
+
+#include <sys/types.h>
+#include <stdio.h>
+
+#include "blkid.h"
+#include "list.h"
+
+#ifdef __GNUC__
+#define __BLKID_ATTR(x) __attribute__(x)
+#else
+#define __BLKID_ATTR(x)
+#endif
+
+
+/*
+ * This describes the attributes of a specific device.
+ * We can traverse all of the tags by bid_tags (linking to the tag bit_names).
+ * The bid_label and bid_uuid fields are shortcuts to the LABEL and UUID tag
+ * values, if they exist.
+ */
+struct blkid_struct_dev
+{
+ struct list_head bid_devs; /* All devices in the cache */
+ struct list_head bid_tags; /* All tags for this device */
+ blkid_cache bid_cache; /* Dev belongs to this cache */
+ char *bid_name; /* Device inode pathname */
+ char *bid_type; /* Preferred device TYPE */
+ int bid_pri; /* Device priority */
+ dev_t bid_devno; /* Device major/minor number */
+ time_t bid_time; /* Last update time of device */
+ unsigned int bid_flags; /* Device status bitflags */
+ char *bid_label; /* Shortcut to device LABEL */
+ char *bid_uuid; /* Shortcut to binary UUID */
+};
+
+#define BLKID_BID_FL_VERIFIED 0x0001 /* Device data validated from disk */
+#define BLKID_BID_FL_INVALID 0x0004 /* Device is invalid */
+
+/*
+ * Each tag defines a NAME=value pair for a particular device. The tags
+ * are linked via bit_names for a single device, so that traversing the
+ * names list will get you a list of all tags associated with a device.
+ * They are also linked via bit_values for all devices, so one can easily
+ * search all tags with a given NAME for a specific value.
+ */
+struct blkid_struct_tag
+{
+ struct list_head bit_tags; /* All tags for this device */
+ struct list_head bit_names; /* All tags with given NAME */
+ char *bit_name; /* NAME of tag (shared) */
+ char *bit_val; /* value of tag */
+ blkid_dev bit_dev; /* pointer to device */
+};
+typedef struct blkid_struct_tag *blkid_tag;
+
+/*
+ * Minimum number of seconds between device probes, even when reading
+ * from the cache. This is to avoid re-probing all devices which were
+ * just probed by another program that does not share the cache.
+ */
+#define BLKID_PROBE_MIN 2
+
+/*
+ * Time in seconds an entry remains verified in the in-memory cache
+ * before being reverified (in case of long-running processes that
+ * keep a cache in memory and continue to use it for a long time).
+ */
+#define BLKID_PROBE_INTERVAL 200
+
+/* This describes an entire blkid cache file and probed devices.
+ * We can traverse all of the found devices via bic_list.
+ * We can traverse all of the tag types by bic_tags, which hold empty tags
+ * for each tag type. Those tags can be used as list_heads for iterating
+ * through all devices with a specific tag type (e.g. LABEL).
+ */
+struct blkid_struct_cache
+{
+ struct list_head bic_devs; /* List head of all devices */
+ struct list_head bic_tags; /* List head of all tag types */
+ time_t bic_time; /* Last probe time */
+ time_t bic_ftime; /* Mod time of the cachefile */
+ unsigned int bic_flags; /* Status flags of the cache */
+ char *bic_filename; /* filename of cache */
+};
+
+#define BLKID_BIC_FL_PROBED 0x0002 /* We probed /proc/partition devices */
+#define BLKID_BIC_FL_CHANGED 0x0004 /* Cache has changed from disk */
+
+extern char *blkid_strdup(const char *s);
+extern char *blkid_strndup(const char *s, const int length);
+
+#define BLKID_CACHE_FILE "/etc/blkid.tab"
+extern const char *blkid_devdirs[];
+
+#define BLKID_ERR_IO 5
+#define BLKID_ERR_PROC 9
+#define BLKID_ERR_MEM 12
+#define BLKID_ERR_CACHE 14
+#define BLKID_ERR_DEV 19
+#define BLKID_ERR_PARAM 22
+#define BLKID_ERR_BIG 27
+
+/*
+ * Priority settings for different types of devices
+ */
+#define BLKID_PRI_EVMS 30
+#define BLKID_PRI_LVM 20
+#define BLKID_PRI_MD 10
+
+#if defined(TEST_PROGRAM) && !defined(CONFIG_BLKID_DEBUG)
+#define CONFIG_BLKID_DEBUG
+#endif
+
+#define DEBUG_CACHE 0x0001
+#define DEBUG_DUMP 0x0002
+#define DEBUG_DEV 0x0004
+#define DEBUG_DEVNAME 0x0008
+#define DEBUG_DEVNO 0x0010
+#define DEBUG_PROBE 0x0020
+#define DEBUG_READ 0x0040
+#define DEBUG_RESOLVE 0x0080
+#define DEBUG_SAVE 0x0100
+#define DEBUG_TAG 0x0200
+#define DEBUG_INIT 0x8000
+#define DEBUG_ALL 0xFFFF
+
+#ifdef CONFIG_BLKID_DEBUG
+#include <stdio.h>
+extern int blkid_debug_mask;
+#define DBG(m,x) if ((m) & blkid_debug_mask) x;
+#else
+#define DBG(m,x)
+#endif
+
+#ifdef CONFIG_BLKID_DEBUG
+extern void blkid_debug_dump_dev(blkid_dev dev);
+extern void blkid_debug_dump_tag(blkid_tag tag);
+#endif
+
+/* lseek.c */
+/* extern blkid_loff_t blkid_llseek(int fd, blkid_loff_t offset, int whence); */
+#ifdef CONFIG_LFS
+# define blkid_llseek lseek64
+#else
+# define blkid_llseek lseek
+#endif
+
+/* read.c */
+extern void blkid_read_cache(blkid_cache cache);
+
+/* save.c */
+extern int blkid_flush_cache(blkid_cache cache);
+
+/*
+ * Functions to create and find a specific tag type: tag.c
+ */
+extern void blkid_free_tag(blkid_tag tag);
+extern blkid_tag blkid_find_tag_dev(blkid_dev dev, const char *type);
+extern int blkid_set_tag(blkid_dev dev, const char *name,
+ const char *value, const int vlength);
+
+/*
+ * Functions to create and find a specific tag type: dev.c
+ */
+extern blkid_dev blkid_new_dev(void);
+extern void blkid_free_dev(blkid_dev dev);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _BLKID_BLKIDP_H */
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/blkid/blkid_getsize.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/blkid/blkid_getsize.c
new file mode 100644
index 0000000..941efa4
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/blkid/blkid_getsize.c
@@ -0,0 +1,179 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * getsize.c --- get the size of a partition.
+ *
+ * Copyright (C) 1995, 1995 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+
+/* include this before sys/queues.h! */
+#include "blkidP.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <fcntl.h>
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#ifdef HAVE_LINUX_FD_H
+#include <linux/fd.h>
+#endif
+#ifdef HAVE_SYS_DISKLABEL_H
+#include <sys/disklabel.h>
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_SYS_DISK_H
+#ifdef HAVE_SYS_QUEUE_H
+#include <sys/queue.h> /* for LIST_HEAD */
+#endif
+#include <sys/disk.h>
+#endif
+#ifdef __linux__
+#include <sys/utsname.h>
+#endif
+
+#if defined(__linux__) && defined(_IO) && !defined(BLKGETSIZE)
+#define BLKGETSIZE _IO(0x12,96) /* return device size */
+#endif
+
+#if defined(__linux__) && defined(_IOR) && !defined(BLKGETSIZE64)
+#define BLKGETSIZE64 _IOR(0x12,114,size_t) /* return device size in bytes (u64 *arg) */
+#endif
+
+#ifdef APPLE_DARWIN
+#define BLKGETSIZE DKIOCGETBLOCKCOUNT32
+#endif /* APPLE_DARWIN */
+
+static int valid_offset(int fd, blkid_loff_t offset)
+{
+ char ch;
+
+ if (blkid_llseek(fd, offset, 0) < 0)
+ return 0;
+ if (read(fd, &ch, 1) < 1)
+ return 0;
+ return 1;
+}
+
+/*
+ * Returns the number of blocks in a partition
+ */
+blkid_loff_t blkid_get_dev_size(int fd)
+{
+ int valid_blkgetsize64 = 1;
+#ifdef __linux__
+ struct utsname ut;
+#endif
+ unsigned long long size64;
+ unsigned long size;
+ blkid_loff_t high, low;
+#ifdef FDGETPRM
+ struct floppy_struct this_floppy;
+#endif
+#ifdef HAVE_SYS_DISKLABEL_H
+ int part = -1;
+ struct disklabel lab;
+ struct partition *pp;
+ char ch;
+ struct stat st;
+#endif /* HAVE_SYS_DISKLABEL_H */
+
+#ifdef DKIOCGETBLOCKCOUNT /* For Apple Darwin */
+ if (ioctl(fd, DKIOCGETBLOCKCOUNT, &size64) >= 0) {
+ if ((sizeof(blkid_loff_t) < sizeof(unsigned long long))
+ && (size64 << 9 > 0xFFFFFFFF))
+ return 0; /* EFBIG */
+ return (blkid_loff_t) size64 << 9;
+ }
+#endif
+
+#ifdef BLKGETSIZE64
+#ifdef __linux__
+ if ((uname(&ut) == 0) &&
+ ((ut.release[0] == '2') && (ut.release[1] == '.') &&
+ (ut.release[2] < '6') && (ut.release[3] == '.')))
+ valid_blkgetsize64 = 0;
+#endif
+ if (valid_blkgetsize64 &&
+ ioctl(fd, BLKGETSIZE64, &size64) >= 0) {
+ if ((sizeof(blkid_loff_t) < sizeof(unsigned long long))
+ && ((size64) > 0xFFFFFFFF))
+ return 0; /* EFBIG */
+ return size64;
+ }
+#endif
+
+#ifdef BLKGETSIZE
+ if (ioctl(fd, BLKGETSIZE, &size) >= 0)
+ return (blkid_loff_t)size << 9;
+#endif
+
+#ifdef FDGETPRM
+ if (ioctl(fd, FDGETPRM, &this_floppy) >= 0)
+ return (blkid_loff_t)this_floppy.size << 9;
+#endif
+#ifdef HAVE_SYS_DISKLABEL_H
+#if 0
+ /*
+ * This should work in theory but I haven't tested it. Anyone
+ * on a BSD system want to test this for me? In the meantime,
+ * binary search mechanism should work just fine.
+ */
+ if ((fstat(fd, &st) >= 0) && S_ISBLK(st.st_mode))
+ part = st.st_rdev & 7;
+ if (part >= 0 && (ioctl(fd, DIOCGDINFO, (char *)&lab) >= 0)) {
+ pp = &lab.d_partitions[part];
+ if (pp->p_size)
+ return pp->p_size << 9;
+ }
+#endif
+#endif /* HAVE_SYS_DISKLABEL_H */
+
+ /*
+ * OK, we couldn't figure it out by using a specialized ioctl,
+ * which is generally the best way. So do binary search to
+ * find the size of the partition.
+ */
+ low = 0;
+ for (high = 1024; valid_offset(fd, high); high *= 2)
+ low = high;
+ while (low < high - 1)
+ {
+ const blkid_loff_t mid = (low + high) / 2;
+
+ if (valid_offset(fd, mid))
+ low = mid;
+ else
+ high = mid;
+ }
+ return low + 1;
+}
+
+#ifdef TEST_PROGRAM
+int main(int argc, char **argv)
+{
+ blkid_loff_t bytes;
+ int fd;
+
+ if (argc < 2) {
+ fprintf(stderr, "Usage: %s device\n"
+ "Determine the size of a device\n", argv[0]);
+ return 1;
+ }
+
+ if ((fd = open(argv[1], O_RDONLY)) < 0)
+ perror(argv[0]);
+
+ bytes = blkid_get_dev_size(fd);
+ printf("Device %s has %lld 1k blocks.\n", argv[1], bytes >> 10);
+
+ return 0;
+}
+#endif
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/blkid/cache.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/blkid/cache.c
new file mode 100644
index 0000000..9bae6fb
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/blkid/cache.c
@@ -0,0 +1,126 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * cache.c - allocation/initialization/free routines for cache
+ *
+ * Copyright (C) 2001 Andreas Dilger
+ * Copyright (C) 2003 Theodore Ts'o
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "blkidP.h"
+
+int blkid_debug_mask = 0;
+
+int blkid_get_cache(blkid_cache *ret_cache, const char *filename)
+{
+ blkid_cache cache;
+
+#ifdef CONFIG_BLKID_DEBUG
+ if (!(blkid_debug_mask & DEBUG_INIT)) {
+ char *dstr = getenv("BLKID_DEBUG");
+
+ if (dstr)
+ blkid_debug_mask = strtoul(dstr, 0, 0);
+ blkid_debug_mask |= DEBUG_INIT;
+ }
+#endif
+
+ DBG(DEBUG_CACHE, printf("creating blkid cache (using %s)\n",
+ filename ? filename : "default cache"));
+
+ if (!(cache = (blkid_cache) calloc(1, sizeof(struct blkid_struct_cache))))
+ return -BLKID_ERR_MEM;
+
+ INIT_LIST_HEAD(&cache->bic_devs);
+ INIT_LIST_HEAD(&cache->bic_tags);
+
+ if (filename && !strlen(filename))
+ filename = 0;
+ if (!filename && (getuid() == geteuid()))
+ filename = getenv("BLKID_FILE");
+ if (!filename)
+ filename = BLKID_CACHE_FILE;
+ cache->bic_filename = blkid_strdup(filename);
+
+ blkid_read_cache(cache);
+
+ *ret_cache = cache;
+ return 0;
+}
+
+void blkid_put_cache(blkid_cache cache)
+{
+ if (!cache)
+ return;
+
+ (void) blkid_flush_cache(cache);
+
+ DBG(DEBUG_CACHE, printf("freeing cache struct\n"));
+
+ /* DBG(DEBUG_CACHE, blkid_debug_dump_cache(cache)); */
+
+ while (!list_empty(&cache->bic_devs)) {
+ blkid_dev dev = list_entry(cache->bic_devs.next,
+ struct blkid_struct_dev,
+ bid_devs);
+ blkid_free_dev(dev);
+ }
+
+ while (!list_empty(&cache->bic_tags)) {
+ blkid_tag tag = list_entry(cache->bic_tags.next,
+ struct blkid_struct_tag,
+ bit_tags);
+
+ while (!list_empty(&tag->bit_names)) {
+ blkid_tag bad = list_entry(tag->bit_names.next,
+ struct blkid_struct_tag,
+ bit_names);
+
+ DBG(DEBUG_CACHE, printf("warning: unfreed tag %s=%s\n",
+ bad->bit_name, bad->bit_val));
+ blkid_free_tag(bad);
+ }
+ blkid_free_tag(tag);
+ }
+ free(cache->bic_filename);
+
+ free(cache);
+}
+
+#ifdef TEST_PROGRAM
+int main(int argc, char** argv)
+{
+ blkid_cache cache = NULL;
+ int ret;
+
+ blkid_debug_mask = DEBUG_ALL;
+ if ((argc > 2)) {
+ fprintf(stderr, "Usage: %s [filename]\n", argv[0]);
+ exit(1);
+ }
+
+ if ((ret = blkid_get_cache(&cache, argv[1])) < 0) {
+ fprintf(stderr, "error %d parsing cache file %s\n", ret,
+ argv[1] ? argv[1] : BLKID_CACHE_FILE);
+ exit(1);
+ }
+ if ((ret = blkid_get_cache(&cache, bb_dev_null)) != 0) {
+ fprintf(stderr, "%s: error creating cache (%d)\n",
+ argv[0], ret);
+ exit(1);
+ }
+ if ((ret = blkid_probe_all(cache) < 0))
+ fprintf(stderr, "error probing devices\n");
+
+ blkid_put_cache(cache);
+
+ return ret;
+}
+#endif
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/blkid/dev.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/blkid/dev.c
new file mode 100644
index 0000000..eddbd02
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/blkid/dev.c
@@ -0,0 +1,214 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * dev.c - allocation/initialization/free routines for dev
+ *
+ * Copyright (C) 2001 Andreas Dilger
+ * Copyright (C) 2003 Theodore Ts'o
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "blkidP.h"
+
+blkid_dev blkid_new_dev(void)
+{
+ blkid_dev dev;
+
+ if (!(dev = (blkid_dev) calloc(1, sizeof(struct blkid_struct_dev))))
+ return NULL;
+
+ INIT_LIST_HEAD(&dev->bid_devs);
+ INIT_LIST_HEAD(&dev->bid_tags);
+
+ return dev;
+}
+
+void blkid_free_dev(blkid_dev dev)
+{
+ if (!dev)
+ return;
+
+ DBG(DEBUG_DEV,
+ printf(" freeing dev %s (%s)\n", dev->bid_name, dev->bid_type));
+ DBG(DEBUG_DEV, blkid_debug_dump_dev(dev));
+
+ list_del(&dev->bid_devs);
+ while (!list_empty(&dev->bid_tags)) {
+ blkid_tag tag = list_entry(dev->bid_tags.next,
+ struct blkid_struct_tag,
+ bit_tags);
+ blkid_free_tag(tag);
+ }
+ if (dev->bid_name)
+ free(dev->bid_name);
+ free(dev);
+}
+
+/*
+ * Given a blkid device, return its name
+ */
+const char *blkid_dev_devname(blkid_dev dev)
+{
+ return dev->bid_name;
+}
+
+#ifdef CONFIG_BLKID_DEBUG
+void blkid_debug_dump_dev(blkid_dev dev)
+{
+ struct list_head *p;
+
+ if (!dev) {
+ printf(" dev: NULL\n");
+ return;
+ }
+
+ printf(" dev: name = %s\n", dev->bid_name);
+ printf(" dev: DEVNO=\"0x%0llx\"\n", dev->bid_devno);
+ printf(" dev: TIME=\"%lu\"\n", dev->bid_time);
+ printf(" dev: PRI=\"%d\"\n", dev->bid_pri);
+ printf(" dev: flags = 0x%08X\n", dev->bid_flags);
+
+ list_for_each(p, &dev->bid_tags) {
+ blkid_tag tag = list_entry(p, struct blkid_struct_tag, bit_tags);
+ if (tag)
+ printf(" tag: %s=\"%s\"\n", tag->bit_name,
+ tag->bit_val);
+ else
+ printf(" tag: NULL\n");
+ }
+ puts("");
+}
+#endif
+
+/*
+ * dev iteration routines for the public libblkid interface.
+ *
+ * These routines do not expose the list.h implementation, which are a
+ * contamination of the namespace, and which force us to reveal far, far
+ * too much of our internal implemenation. I'm not convinced I want
+ * to keep list.h in the long term, anyway. It's fine for kernel
+ * programming, but performance is not the #1 priority for this
+ * library, and I really don't like the tradeoff of type-safety for
+ * performance for this application. [tytso:20030125.2007EST]
+ */
+
+/*
+ * This series of functions iterate over all devices in a blkid cache
+ */
+#define DEV_ITERATE_MAGIC 0x01a5284c
+
+struct blkid_struct_dev_iterate {
+ int magic;
+ blkid_cache cache;
+ struct list_head *p;
+};
+
+blkid_dev_iterate blkid_dev_iterate_begin(blkid_cache cache)
+{
+ blkid_dev_iterate iter;
+
+ iter = xmalloc(sizeof(struct blkid_struct_dev_iterate));
+ iter->magic = DEV_ITERATE_MAGIC;
+ iter->cache = cache;
+ iter->p = cache->bic_devs.next;
+ return iter;
+}
+
+/*
+ * Return 0 on success, -1 on error
+ */
+extern int blkid_dev_next(blkid_dev_iterate iter,
+ blkid_dev *dev)
+{
+ *dev = 0;
+ if (!iter || iter->magic != DEV_ITERATE_MAGIC ||
+ iter->p == &iter->cache->bic_devs)
+ return -1;
+ *dev = list_entry(iter->p, struct blkid_struct_dev, bid_devs);
+ iter->p = iter->p->next;
+ return 0;
+}
+
+void blkid_dev_iterate_end(blkid_dev_iterate iter)
+{
+ if (!iter || iter->magic != DEV_ITERATE_MAGIC)
+ return;
+ iter->magic = 0;
+ free(iter);
+}
+
+#ifdef TEST_PROGRAM
+#ifdef HAVE_GETOPT_H
+#include <getopt.h>
+#else
+extern char *optarg;
+extern int optind;
+#endif
+
+void usage(char *prog)
+{
+ fprintf(stderr, "Usage: %s [-f blkid_file] [-m debug_mask]\n", prog);
+ fprintf(stderr, "\tList all devices and exit\n", prog);
+ exit(1);
+}
+
+int main(int argc, char **argv)
+{
+ blkid_dev_iterate iter;
+ blkid_cache cache = NULL;
+ blkid_dev dev;
+ int c, ret;
+ char *tmp;
+ char *file = NULL;
+ char *search_type = NULL;
+ char *search_value = NULL;
+
+ while ((c = getopt (argc, argv, "m:f:")) != EOF)
+ switch (c) {
+ case 'f':
+ file = optarg;
+ break;
+ case 'm':
+ blkid_debug_mask = strtoul (optarg, &tmp, 0);
+ if (*tmp) {
+ fprintf(stderr, "Invalid debug mask: %d\n",
+ optarg);
+ exit(1);
+ }
+ break;
+ case '?':
+ usage(argv[0]);
+ }
+ if (argc >= optind+2) {
+ search_type = argv[optind];
+ search_value = argv[optind+1];
+ optind += 2;
+ }
+ if (argc != optind)
+ usage(argv[0]);
+
+ if ((ret = blkid_get_cache(&cache, file)) != 0) {
+ fprintf(stderr, "%s: error creating cache (%d)\n",
+ argv[0], ret);
+ exit(1);
+ }
+
+ iter = blkid_dev_iterate_begin(cache);
+ if (search_type)
+ blkid_dev_set_search(iter, search_type, search_value);
+ while (blkid_dev_next(iter, &dev) == 0) {
+ printf("Device: %s\n", blkid_dev_devname(dev));
+ }
+ blkid_dev_iterate_end(iter);
+
+
+ blkid_put_cache(cache);
+ return 0;
+}
+#endif
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/blkid/devname.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/blkid/devname.c
new file mode 100644
index 0000000..071aa5a
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/blkid/devname.c
@@ -0,0 +1,367 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * devname.c - get a dev by its device inode name
+ *
+ * Copyright (C) Andries Brouwer
+ * Copyright (C) 1999, 2000, 2001, 2002, 2003 Theodore Ts'o
+ * Copyright (C) 2001 Andreas Dilger
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#include <sys/stat.h>
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_SYS_MKDEV_H
+#include <sys/mkdev.h>
+#endif
+#include <time.h>
+
+#include "blkidP.h"
+
+/*
+ * Find a dev struct in the cache by device name, if available.
+ *
+ * If there is no entry with the specified device name, and the create
+ * flag is set, then create an empty device entry.
+ */
+blkid_dev blkid_get_dev(blkid_cache cache, const char *devname, int flags)
+{
+ blkid_dev dev = NULL, tmp;
+ struct list_head *p;
+
+ if (!cache || !devname)
+ return NULL;
+
+ list_for_each(p, &cache->bic_devs) {
+ tmp = list_entry(p, struct blkid_struct_dev, bid_devs);
+ if (strcmp(tmp->bid_name, devname))
+ continue;
+
+ DBG(DEBUG_DEVNAME,
+ printf("found devname %s in cache\n", tmp->bid_name));
+ dev = tmp;
+ break;
+ }
+
+ if (!dev && (flags & BLKID_DEV_CREATE)) {
+ dev = blkid_new_dev();
+ if (!dev)
+ return NULL;
+ dev->bid_name = blkid_strdup(devname);
+ dev->bid_cache = cache;
+ list_add_tail(&dev->bid_devs, &cache->bic_devs);
+ cache->bic_flags |= BLKID_BIC_FL_CHANGED;
+ }
+
+ if (flags & BLKID_DEV_VERIFY)
+ dev = blkid_verify(cache, dev);
+ return dev;
+}
+
+/*
+ * Probe a single block device to add to the device cache.
+ */
+static void probe_one(blkid_cache cache, const char *ptname,
+ dev_t devno, int pri)
+{
+ blkid_dev dev = NULL;
+ struct list_head *p;
+ const char **dir;
+ char *devname = NULL;
+
+ /* See if we already have this device number in the cache. */
+ list_for_each(p, &cache->bic_devs) {
+ blkid_dev tmp = list_entry(p, struct blkid_struct_dev,
+ bid_devs);
+ if (tmp->bid_devno == devno) {
+ dev = blkid_verify(cache, tmp);
+ break;
+ }
+ }
+ if (dev && dev->bid_devno == devno)
+ goto set_pri;
+
+ /*
+ * Take a quick look at /dev/ptname for the device number. We check
+ * all of the likely device directories. If we don't find it, or if
+ * the stat information doesn't check out, use blkid_devno_to_devname()
+ * to find it via an exhaustive search for the device major/minor.
+ */
+ for (dir = blkid_devdirs; *dir; dir++) {
+ struct stat st;
+ char device[256];
+
+ sprintf(device, "%s/%s", *dir, ptname);
+ if ((dev = blkid_get_dev(cache, device, BLKID_DEV_FIND)) &&
+ dev->bid_devno == devno)
+ goto set_pri;
+
+ if (stat(device, &st) == 0 && S_ISBLK(st.st_mode) &&
+ st.st_rdev == devno) {
+ devname = blkid_strdup(device);
+ break;
+ }
+ }
+ if (!devname) {
+ devname = blkid_devno_to_devname(devno);
+ if (!devname)
+ return;
+ }
+ dev = blkid_get_dev(cache, devname, BLKID_DEV_NORMAL);
+ free(devname);
+
+set_pri:
+ if (!pri && !strncmp(ptname, "md", 2))
+ pri = BLKID_PRI_MD;
+ if (dev)
+ dev->bid_pri = pri;
+}
+
+#define PROC_PARTITIONS "/proc/partitions"
+#define VG_DIR "/proc/lvm/VGs"
+
+/*
+ * This function initializes the UUID cache with devices from the LVM
+ * proc hierarchy. We currently depend on the names of the LVM
+ * hierarchy giving us the device structure in /dev. (XXX is this a
+ * safe thing to do?)
+ */
+#ifdef VG_DIR
+#include <dirent.h>
+static dev_t lvm_get_devno(const char *lvm_device)
+{
+ FILE *lvf;
+ char buf[1024];
+ int ma, mi;
+ dev_t ret = 0;
+
+ DBG(DEBUG_DEVNAME, printf("opening %s\n", lvm_device));
+ if ((lvf = fopen(lvm_device, "r")) == NULL) {
+ DBG(DEBUG_DEVNAME, printf("%s: (%d) %s\n", lvm_device, errno,
+ strerror(errno)));
+ return 0;
+ }
+
+ while (fgets(buf, sizeof(buf), lvf)) {
+ if (sscanf(buf, "device: %d:%d", &ma, &mi) == 2) {
+ ret = makedev(ma, mi);
+ break;
+ }
+ }
+ fclose(lvf);
+
+ return ret;
+}
+
+static void lvm_probe_all(blkid_cache cache)
+{
+ DIR *vg_list;
+ struct dirent *vg_iter;
+ int vg_len = strlen(VG_DIR);
+ dev_t dev;
+
+ if ((vg_list = opendir(VG_DIR)) == NULL)
+ return;
+
+ DBG(DEBUG_DEVNAME, printf("probing LVM devices under %s\n", VG_DIR));
+
+ while ((vg_iter = readdir(vg_list)) != NULL) {
+ DIR *lv_list;
+ char *vdirname;
+ char *vg_name;
+ struct dirent *lv_iter;
+
+ vg_name = vg_iter->d_name;
+ if (LONE_CHAR(vg_name, '.') || !strcmp(vg_name, ".."))
+ continue;
+ vdirname = xmalloc(vg_len + strlen(vg_name) + 8);
+ sprintf(vdirname, "%s/%s/LVs", VG_DIR, vg_name);
+
+ lv_list = opendir(vdirname);
+ free(vdirname);
+ if (lv_list == NULL)
+ continue;
+
+ while ((lv_iter = readdir(lv_list)) != NULL) {
+ char *lv_name, *lvm_device;
+
+ lv_name = lv_iter->d_name;
+ if (LONE_CHAR(lv_name, '.') || !strcmp(lv_name, ".."))
+ continue;
+
+ lvm_device = xmalloc(vg_len + strlen(vg_name) +
+ strlen(lv_name) + 8);
+ sprintf(lvm_device, "%s/%s/LVs/%s", VG_DIR, vg_name,
+ lv_name);
+ dev = lvm_get_devno(lvm_device);
+ sprintf(lvm_device, "%s/%s", vg_name, lv_name);
+ DBG(DEBUG_DEVNAME, printf("LVM dev %s: devno 0x%04X\n",
+ lvm_device,
+ (unsigned int) dev));
+ probe_one(cache, lvm_device, dev, BLKID_PRI_LVM);
+ free(lvm_device);
+ }
+ closedir(lv_list);
+ }
+ closedir(vg_list);
+}
+#endif
+
+#define PROC_EVMS_VOLUMES "/proc/evms/volumes"
+
+static int
+evms_probe_all(blkid_cache cache)
+{
+ char line[100];
+ int ma, mi, sz, num = 0;
+ FILE *procpt;
+ char device[110];
+
+ procpt = fopen(PROC_EVMS_VOLUMES, "r");
+ if (!procpt)
+ return 0;
+ while (fgets(line, sizeof(line), procpt)) {
+ if (sscanf (line, " %d %d %d %*s %*s %[^\n ]",
+ &ma, &mi, &sz, device) != 4)
+ continue;
+
+ DBG(DEBUG_DEVNAME, printf("Checking partition %s (%d, %d)\n",
+ device, ma, mi));
+
+ probe_one(cache, device, makedev(ma, mi), BLKID_PRI_EVMS);
+ num++;
+ }
+ fclose(procpt);
+ return num;
+}
+
+/*
+ * Read the device data for all available block devices in the system.
+ */
+int blkid_probe_all(blkid_cache cache)
+{
+ FILE *proc;
+ char line[1024];
+ char ptname0[128], ptname1[128], *ptname = 0;
+ char *ptnames[2];
+ dev_t devs[2];
+ int ma, mi;
+ unsigned long long sz;
+ int lens[2] = { 0, 0 };
+ int which = 0, last = 0;
+
+ ptnames[0] = ptname0;
+ ptnames[1] = ptname1;
+
+ if (!cache)
+ return -BLKID_ERR_PARAM;
+
+ if (cache->bic_flags & BLKID_BIC_FL_PROBED &&
+ time(0) - cache->bic_time < BLKID_PROBE_INTERVAL)
+ return 0;
+
+ blkid_read_cache(cache);
+ evms_probe_all(cache);
+#ifdef VG_DIR
+ lvm_probe_all(cache);
+#endif
+
+ proc = fopen(PROC_PARTITIONS, "r");
+ if (!proc)
+ return -BLKID_ERR_PROC;
+
+ while (fgets(line, sizeof(line), proc)) {
+ last = which;
+ which ^= 1;
+ ptname = ptnames[which];
+
+ if (sscanf(line, " %d %d %llu %128[^\n ]",
+ &ma, &mi, &sz, ptname) != 4)
+ continue;
+ devs[which] = makedev(ma, mi);
+
+ DBG(DEBUG_DEVNAME, printf("read partition name %s\n", ptname));
+
+ /* Skip whole disk devs unless they have no partitions
+ * If we don't have a partition on this dev, also
+ * check previous dev to see if it didn't have a partn.
+ * heuristic: partition name ends in a digit.
+ *
+ * Skip extended partitions.
+ * heuristic: size is 1
+ *
+ * FIXME: skip /dev/{ida,cciss,rd} whole-disk devs
+ */
+
+ lens[which] = strlen(ptname);
+ if (isdigit(ptname[lens[which] - 1])) {
+ DBG(DEBUG_DEVNAME,
+ printf("partition dev %s, devno 0x%04X\n",
+ ptname, (unsigned int) devs[which]));
+
+ if (sz > 1)
+ probe_one(cache, ptname, devs[which], 0);
+ lens[which] = 0;
+ lens[last] = 0;
+ } else if (lens[last] && strncmp(ptnames[last], ptname,
+ lens[last])) {
+ DBG(DEBUG_DEVNAME,
+ printf("whole dev %s, devno 0x%04X\n",
+ ptnames[last], (unsigned int) devs[last]));
+ probe_one(cache, ptnames[last], devs[last], 0);
+ lens[last] = 0;
+ }
+ }
+
+ /* Handle the last device if it wasn't partitioned */
+ if (lens[which])
+ probe_one(cache, ptname, devs[which], 0);
+
+ fclose(proc);
+
+ cache->bic_time = time(0);
+ cache->bic_flags |= BLKID_BIC_FL_PROBED;
+ blkid_flush_cache(cache);
+ return 0;
+}
+
+#ifdef TEST_PROGRAM
+int main(int argc, char **argv)
+{
+ blkid_cache cache = NULL;
+ int ret;
+
+ blkid_debug_mask = DEBUG_ALL;
+ if (argc != 1) {
+ fprintf(stderr, "Usage: %s\n"
+ "Probe all devices and exit\n", argv[0]);
+ exit(1);
+ }
+ if ((ret = blkid_get_cache(&cache, bb_dev_null)) != 0) {
+ fprintf(stderr, "%s: error creating cache (%d)\n",
+ argv[0], ret);
+ exit(1);
+ }
+ if (blkid_probe_all(cache) < 0)
+ printf("%s: error probing devices\n", argv[0]);
+
+ blkid_put_cache(cache);
+ return 0;
+}
+#endif
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/blkid/devno.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/blkid/devno.c
new file mode 100644
index 0000000..ae326f8
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/blkid/devno.c
@@ -0,0 +1,222 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * devno.c - find a particular device by its device number (major/minor)
+ *
+ * Copyright (C) 2000, 2001, 2003 Theodore Ts'o
+ * Copyright (C) 2001 Andreas Dilger
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#include <sys/stat.h>
+#include <dirent.h>
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_SYS_MKDEV_H
+#include <sys/mkdev.h>
+#endif
+
+#include "blkidP.h"
+
+struct dir_list {
+ char *name;
+ struct dir_list *next;
+};
+
+char *blkid_strndup(const char *s, int length)
+{
+ char *ret;
+
+ if (!s)
+ return NULL;
+
+ if (!length)
+ length = strlen(s);
+
+ ret = xmalloc(length + 1);
+ strncpy(ret, s, length);
+ ret[length] = '\0';
+ return ret;
+}
+
+char *blkid_strdup(const char *s)
+{
+ return blkid_strndup(s, 0);
+}
+
+/*
+ * This function adds an entry to the directory list
+ */
+static void add_to_dirlist(const char *name, struct dir_list **list)
+{
+ struct dir_list *dp;
+
+ dp = xmalloc(sizeof(struct dir_list));
+ dp->name = blkid_strdup(name);
+ dp->next = *list;
+ *list = dp;
+}
+
+/*
+ * This function frees a directory list
+ */
+static void free_dirlist(struct dir_list **list)
+{
+ struct dir_list *dp, *next;
+
+ for (dp = *list; dp; dp = next) {
+ next = dp->next;
+ free(dp->name);
+ free(dp);
+ }
+ *list = NULL;
+}
+
+static void scan_dir(char *dir_name, dev_t devno, struct dir_list **list,
+ char **devname)
+{
+ DIR *dir;
+ struct dirent *dp;
+ char path[1024];
+ int dirlen;
+ struct stat st;
+
+ if ((dir = opendir(dir_name)) == NULL)
+ return;
+ dirlen = strlen(dir_name) + 2;
+ while ((dp = readdir(dir)) != 0) {
+ if (dirlen + strlen(dp->d_name) >= sizeof(path))
+ continue;
+
+ if (dp->d_name[0] == '.' &&
+ ((dp->d_name[1] == 0) ||
+ ((dp->d_name[1] == '.') && (dp->d_name[2] == 0))))
+ continue;
+
+ sprintf(path, "%s/%s", dir_name, dp->d_name);
+ if (stat(path, &st) < 0)
+ continue;
+
+ if (S_ISDIR(st.st_mode))
+ add_to_dirlist(path, list);
+ else if (S_ISBLK(st.st_mode) && st.st_rdev == devno) {
+ *devname = blkid_strdup(path);
+ DBG(DEBUG_DEVNO,
+ printf("found 0x%llx at %s (%p)\n", devno,
+ path, *devname));
+ break;
+ }
+ }
+ closedir(dir);
+}
+
+/* Directories where we will try to search for device numbers */
+const char *blkid_devdirs[] = { "/devices", "/devfs", "/dev", NULL };
+
+/*
+ * This function finds the pathname to a block device with a given
+ * device number. It returns a pointer to allocated memory to the
+ * pathname on success, and NULL on failure.
+ */
+char *blkid_devno_to_devname(dev_t devno)
+{
+ struct dir_list *list = NULL, *new_list = NULL;
+ char *devname = NULL;
+ const char **dir;
+
+ /*
+ * Add the starting directories to search in reverse order of
+ * importance, since we are using a stack...
+ */
+ for (dir = blkid_devdirs; *dir; dir++)
+ add_to_dirlist(*dir, &list);
+
+ while (list) {
+ struct dir_list *current = list;
+
+ list = list->next;
+ DBG(DEBUG_DEVNO, printf("directory %s\n", current->name));
+ scan_dir(current->name, devno, &new_list, &devname);
+ free(current->name);
+ free(current);
+ if (devname)
+ break;
+ /*
+ * If we're done checking at this level, descend to
+ * the next level of subdirectories. (breadth-first)
+ */
+ if (list == NULL) {
+ list = new_list;
+ new_list = NULL;
+ }
+ }
+ free_dirlist(&list);
+ free_dirlist(&new_list);
+
+ if (!devname) {
+ DBG(DEBUG_DEVNO,
+ printf("blkid: cannot find devno 0x%04lx\n",
+ (unsigned long) devno));
+ } else {
+ DBG(DEBUG_DEVNO,
+ printf("found devno 0x%04llx as %s\n", devno, devname));
+ }
+
+
+ return devname;
+}
+
+#ifdef TEST_PROGRAM
+int main(int argc, char** argv)
+{
+ char *devname, *tmp;
+ int major, minor;
+ dev_t devno;
+ const char *errmsg = "Cannot parse %s: %s\n";
+
+ blkid_debug_mask = DEBUG_ALL;
+ if ((argc != 2) && (argc != 3)) {
+ fprintf(stderr, "Usage:\t%s device_number\n\t%s major minor\n"
+ "Resolve a device number to a device name\n",
+ argv[0], argv[0]);
+ exit(1);
+ }
+ if (argc == 2) {
+ devno = strtoul(argv[1], &tmp, 0);
+ if (*tmp) {
+ fprintf(stderr, errmsg, "device number", argv[1]);
+ exit(1);
+ }
+ } else {
+ major = strtoul(argv[1], &tmp, 0);
+ if (*tmp) {
+ fprintf(stderr, errmsg, "major number", argv[1]);
+ exit(1);
+ }
+ minor = strtoul(argv[2], &tmp, 0);
+ if (*tmp) {
+ fprintf(stderr, errmsg, "minor number", argv[2]);
+ exit(1);
+ }
+ devno = makedev(major, minor);
+ }
+ printf("Looking for device 0x%04Lx\n", devno);
+ devname = blkid_devno_to_devname(devno);
+ free(devname);
+ return 0;
+}
+#endif
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/blkid/list.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/blkid/list.c
new file mode 100644
index 0000000..04d61a1
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/blkid/list.c
@@ -0,0 +1,110 @@
+/* vi: set sw=4 ts=4: */
+
+#include "list.h"
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+void __list_add(struct list_head * add,
+ struct list_head * prev,
+ struct list_head * next)
+{
+ next->prev = add;
+ add->next = next;
+ add->prev = prev;
+ prev->next = add;
+}
+
+/*
+ * list_add - add a new entry
+ * @add: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+void list_add(struct list_head *add, struct list_head *head)
+{
+ __list_add(add, head, head->next);
+}
+
+/*
+ * list_add_tail - add a new entry
+ * @add: new entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ */
+void list_add_tail(struct list_head *add, struct list_head *head)
+{
+ __list_add(add, head->prev, head);
+}
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+void __list_del(struct list_head * prev, struct list_head * next)
+{
+ next->prev = prev;
+ prev->next = next;
+}
+
+/*
+ * list_del - deletes entry from list.
+ * @entry: the element to delete from the list.
+ *
+ * list_empty() on @entry does not return true after this, @entry is
+ * in an undefined state.
+ */
+void list_del(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+}
+
+/*
+ * list_del_init - deletes entry from list and reinitialize it.
+ * @entry: the element to delete from the list.
+ */
+void list_del_init(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+ INIT_LIST_HEAD(entry);
+}
+
+/*
+ * list_empty - tests whether a list is empty
+ * @head: the list to test.
+ */
+int list_empty(struct list_head *head)
+{
+ return head->next == head;
+}
+
+/*
+ * list_splice - join two lists
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ */
+void list_splice(struct list_head *list, struct list_head *head)
+{
+ struct list_head *first = list->next;
+
+ if (first != list) {
+ struct list_head *last = list->prev;
+ struct list_head *at = head->next;
+
+ first->prev = head;
+ head->next = first;
+
+ last->next = at;
+ at->prev = last;
+ }
+}
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/blkid/list.h b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/blkid/list.h
new file mode 100644
index 0000000..8b06d85
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/blkid/list.h
@@ -0,0 +1,73 @@
+/* vi: set sw=4 ts=4: */
+#if !defined(_BLKID_LIST_H) && !defined(LIST_HEAD)
+#define _BLKID_LIST_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Simple doubly linked list implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+struct list_head {
+ struct list_head *next, *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+ struct list_head name = LIST_HEAD_INIT(name)
+
+#define INIT_LIST_HEAD(ptr) do { \
+ (ptr)->next = (ptr); (ptr)->prev = (ptr); \
+} while (0)
+
+void __list_add(struct list_head * add, struct list_head * prev, struct list_head * next);
+void list_add(struct list_head *add, struct list_head *head);
+void list_add_tail(struct list_head *add, struct list_head *head);
+void __list_del(struct list_head * prev, struct list_head * next);
+void list_del(struct list_head *entry);
+void list_del_init(struct list_head *entry);
+int list_empty(struct list_head *head);
+void list_splice(struct list_head *list, struct list_head *head);
+
+/**
+ * list_entry - get the struct for this entry
+ * @ptr: the &struct list_head pointer.
+ * @type: the type of the struct this is embedded in.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_entry(ptr, type, member) \
+ ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
+
+/**
+ * list_for_each - iterate over elements in a list
+ * @pos: the &struct list_head to use as a loop counter.
+ * @head: the head for your list.
+ */
+#define list_for_each(pos, head) \
+ for (pos = (head)->next; pos != (head); pos = pos->next)
+
+/**
+ * list_for_each_safe - iterate over elements in a list, but don't dereference
+ * pos after the body is done (in case it is freed)
+ * @pos: the &struct list_head to use as a loop counter.
+ * @pnext: the &struct list_head to use as a pointer to the next item.
+ * @head: the head for your list (not included in iteration).
+ */
+#define list_for_each_safe(pos, pnext, head) \
+ for (pos = (head)->next, pnext = pos->next; pos != (head); \
+ pos = pnext, pnext = pos->next)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _BLKID_LIST_H */
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/blkid/probe.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/blkid/probe.c
new file mode 100644
index 0000000..8c6e2aa
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/blkid/probe.c
@@ -0,0 +1,721 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * probe.c - identify a block device by its contents, and return a dev
+ * struct with the details
+ *
+ * Copyright (C) 1999 by Andries Brouwer
+ * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o
+ * Copyright (C) 2001 by Andreas Dilger
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_SYS_MKDEV_H
+#include <sys/mkdev.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include "blkidP.h"
+#include "../uuid/uuid.h"
+#include "probe.h"
+
+/*
+ * This is a special case code to check for an MDRAID device. We do
+ * this special since it requires checking for a superblock at the end
+ * of the device.
+ */
+static int check_mdraid(int fd, unsigned char *ret_uuid)
+{
+ struct mdp_superblock_s *md;
+ blkid_loff_t offset;
+ char buf[4096];
+
+ if (fd < 0)
+ return -BLKID_ERR_PARAM;
+
+ offset = (blkid_get_dev_size(fd) & ~((blkid_loff_t)65535)) - 65536;
+
+ if (blkid_llseek(fd, offset, 0) < 0 ||
+ read(fd, buf, 4096) != 4096)
+ return -BLKID_ERR_IO;
+
+ /* Check for magic number */
+ if (memcmp("\251+N\374", buf, 4))
+ return -BLKID_ERR_PARAM;
+
+ if (!ret_uuid)
+ return 0;
+ *ret_uuid = 0;
+
+ /* The MD UUID is not contiguous in the superblock, make it so */
+ md = (struct mdp_superblock_s *)buf;
+ if (md->set_uuid0 || md->set_uuid1 || md->set_uuid2 || md->set_uuid3) {
+ memcpy(ret_uuid, &md->set_uuid0, 4);
+ memcpy(ret_uuid, &md->set_uuid1, 12);
+ }
+ return 0;
+}
+
+static void set_uuid(blkid_dev dev, uuid_t uuid)
+{
+ char str[37];
+
+ if (!uuid_is_null(uuid)) {
+ uuid_unparse(uuid, str);
+ blkid_set_tag(dev, "UUID", str, sizeof(str));
+ }
+}
+
+static void get_ext2_info(blkid_dev dev, unsigned char *buf)
+{
+ struct ext2_super_block *es = (struct ext2_super_block *) buf;
+ const char *label = 0;
+
+ DBG(DEBUG_PROBE, printf("ext2_sb.compat = %08X:%08X:%08X\n",
+ blkid_le32(es->s_feature_compat),
+ blkid_le32(es->s_feature_incompat),
+ blkid_le32(es->s_feature_ro_compat)));
+
+ if (strlen(es->s_volume_name))
+ label = es->s_volume_name;
+ blkid_set_tag(dev, "LABEL", label, sizeof(es->s_volume_name));
+
+ set_uuid(dev, es->s_uuid);
+}
+
+static int probe_ext3(int fd __BLKID_ATTR((unused)),
+ blkid_cache cache __BLKID_ATTR((unused)),
+ blkid_dev dev,
+ const struct blkid_magic *id __BLKID_ATTR((unused)),
+ unsigned char *buf)
+{
+ struct ext2_super_block *es;
+
+ es = (struct ext2_super_block *)buf;
+
+ /* Distinguish between jbd and ext2/3 fs */
+ if (blkid_le32(es->s_feature_incompat) &
+ EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)
+ return -BLKID_ERR_PARAM;
+
+ /* Distinguish between ext3 and ext2 */
+ if (!(blkid_le32(es->s_feature_compat) &
+ EXT3_FEATURE_COMPAT_HAS_JOURNAL))
+ return -BLKID_ERR_PARAM;
+
+ get_ext2_info(dev, buf);
+
+ blkid_set_tag(dev, "SEC_TYPE", "ext2", sizeof("ext2"));
+
+ return 0;
+}
+
+static int probe_ext2(int fd __BLKID_ATTR((unused)),
+ blkid_cache cache __BLKID_ATTR((unused)),
+ blkid_dev dev,
+ const struct blkid_magic *id __BLKID_ATTR((unused)),
+ unsigned char *buf)
+{
+ struct ext2_super_block *es;
+
+ es = (struct ext2_super_block *)buf;
+
+ /* Distinguish between jbd and ext2/3 fs */
+ if (blkid_le32(es->s_feature_incompat) &
+ EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)
+ return -BLKID_ERR_PARAM;
+
+ get_ext2_info(dev, buf);
+
+ return 0;
+}
+
+static int probe_jbd(int fd __BLKID_ATTR((unused)),
+ blkid_cache cache __BLKID_ATTR((unused)),
+ blkid_dev dev,
+ const struct blkid_magic *id __BLKID_ATTR((unused)),
+ unsigned char *buf)
+{
+ struct ext2_super_block *es = (struct ext2_super_block *) buf;
+
+ if (!(blkid_le32(es->s_feature_incompat) &
+ EXT3_FEATURE_INCOMPAT_JOURNAL_DEV))
+ return -BLKID_ERR_PARAM;
+
+ get_ext2_info(dev, buf);
+
+ return 0;
+}
+
+static int probe_vfat(int fd __BLKID_ATTR((unused)),
+ blkid_cache cache __BLKID_ATTR((unused)),
+ blkid_dev dev,
+ const struct blkid_magic *id __BLKID_ATTR((unused)),
+ unsigned char *buf)
+{
+ struct vfat_super_block *vs;
+ char serno[10];
+ const char *label = 0;
+ int label_len = 0;
+
+ vs = (struct vfat_super_block *)buf;
+
+ if (strncmp(vs->vs_label, "NO NAME", 7)) {
+ char *end = vs->vs_label + sizeof(vs->vs_label) - 1;
+
+ while (*end == ' ' && end >= vs->vs_label)
+ --end;
+ if (end >= vs->vs_label) {
+ label = vs->vs_label;
+ label_len = end - vs->vs_label + 1;
+ }
+ }
+
+ /* We can't just print them as %04X, because they are unaligned */
+ sprintf(serno, "%02X%02X-%02X%02X", vs->vs_serno[3], vs->vs_serno[2],
+ vs->vs_serno[1], vs->vs_serno[0]);
+ blkid_set_tag(dev, "LABEL", label, label_len);
+ blkid_set_tag(dev, "UUID", serno, sizeof(serno));
+
+ return 0;
+}
+
+static int probe_msdos(int fd __BLKID_ATTR((unused)),
+ blkid_cache cache __BLKID_ATTR((unused)),
+ blkid_dev dev,
+ const struct blkid_magic *id __BLKID_ATTR((unused)),
+ unsigned char *buf)
+{
+ struct msdos_super_block *ms = (struct msdos_super_block *) buf;
+ char serno[10];
+ const char *label = 0;
+ int label_len = 0;
+
+ if (strncmp(ms->ms_label, "NO NAME", 7)) {
+ char *end = ms->ms_label + sizeof(ms->ms_label) - 1;
+
+ while (*end == ' ' && end >= ms->ms_label)
+ --end;
+ if (end >= ms->ms_label) {
+ label = ms->ms_label;
+ label_len = end - ms->ms_label + 1;
+ }
+ }
+
+ /* We can't just print them as %04X, because they are unaligned */
+ sprintf(serno, "%02X%02X-%02X%02X", ms->ms_serno[3], ms->ms_serno[2],
+ ms->ms_serno[1], ms->ms_serno[0]);
+ blkid_set_tag(dev, "UUID", serno, 0);
+ blkid_set_tag(dev, "LABEL", label, label_len);
+ blkid_set_tag(dev, "SEC_TYPE", "msdos", sizeof("msdos"));
+
+ return 0;
+}
+
+static int probe_xfs(int fd __BLKID_ATTR((unused)),
+ blkid_cache cache __BLKID_ATTR((unused)),
+ blkid_dev dev,
+ const struct blkid_magic *id __BLKID_ATTR((unused)),
+ unsigned char *buf)
+{
+ struct xfs_super_block *xs;
+ const char *label = 0;
+
+ xs = (struct xfs_super_block *)buf;
+
+ if (strlen(xs->xs_fname))
+ label = xs->xs_fname;
+ blkid_set_tag(dev, "LABEL", label, sizeof(xs->xs_fname));
+ set_uuid(dev, xs->xs_uuid);
+ return 0;
+}
+
+static int probe_reiserfs(int fd __BLKID_ATTR((unused)),
+ blkid_cache cache __BLKID_ATTR((unused)),
+ blkid_dev dev,
+ const struct blkid_magic *id, unsigned char *buf)
+{
+ struct reiserfs_super_block *rs = (struct reiserfs_super_block *) buf;
+ unsigned int blocksize;
+ const char *label = 0;
+
+ blocksize = blkid_le16(rs->rs_blocksize);
+
+ /* If the superblock is inside the journal, we have the wrong one */
+ if (id->bim_kboff/(blocksize>>10) > blkid_le32(rs->rs_journal_block))
+ return -BLKID_ERR_BIG;
+
+ /* LABEL/UUID are only valid for later versions of Reiserfs v3.6. */
+ if (!strcmp(id->bim_magic, "ReIsEr2Fs") ||
+ !strcmp(id->bim_magic, "ReIsEr3Fs")) {
+ if (strlen(rs->rs_label))
+ label = rs->rs_label;
+ set_uuid(dev, rs->rs_uuid);
+ }
+ blkid_set_tag(dev, "LABEL", label, sizeof(rs->rs_label));
+
+ return 0;
+}
+
+static int probe_jfs(int fd __BLKID_ATTR((unused)),
+ blkid_cache cache __BLKID_ATTR((unused)),
+ blkid_dev dev,
+ const struct blkid_magic *id __BLKID_ATTR((unused)),
+ unsigned char *buf)
+{
+ struct jfs_super_block *js;
+ const char *label = 0;
+
+ js = (struct jfs_super_block *)buf;
+
+ if (strlen((char *) js->js_label))
+ label = (char *) js->js_label;
+ blkid_set_tag(dev, "LABEL", label, sizeof(js->js_label));
+ set_uuid(dev, js->js_uuid);
+ return 0;
+}
+
+static int probe_romfs(int fd __BLKID_ATTR((unused)),
+ blkid_cache cache __BLKID_ATTR((unused)),
+ blkid_dev dev,
+ const struct blkid_magic *id __BLKID_ATTR((unused)),
+ unsigned char *buf)
+{
+ struct romfs_super_block *ros;
+ const char *label = 0;
+
+ ros = (struct romfs_super_block *)buf;
+
+ if (strlen((char *) ros->ros_volume))
+ label = (char *) ros->ros_volume;
+ blkid_set_tag(dev, "LABEL", label, 0);
+ return 0;
+}
+
+static int probe_cramfs(int fd __BLKID_ATTR((unused)),
+ blkid_cache cache __BLKID_ATTR((unused)),
+ blkid_dev dev,
+ const struct blkid_magic *id __BLKID_ATTR((unused)),
+ unsigned char *buf)
+{
+ struct cramfs_super_block *csb;
+ const char *label = 0;
+
+ csb = (struct cramfs_super_block *)buf;
+
+ if (strlen((char *) csb->name))
+ label = (char *) csb->name;
+ blkid_set_tag(dev, "LABEL", label, 0);
+ return 0;
+}
+
+static int probe_swap0(int fd __BLKID_ATTR((unused)),
+ blkid_cache cache __BLKID_ATTR((unused)),
+ blkid_dev dev,
+ const struct blkid_magic *id __BLKID_ATTR((unused)),
+ unsigned char *buf __BLKID_ATTR((unused)))
+{
+ blkid_set_tag(dev, "UUID", 0, 0);
+ blkid_set_tag(dev, "LABEL", 0, 0);
+ return 0;
+}
+
+static int probe_swap1(int fd,
+ blkid_cache cache __BLKID_ATTR((unused)),
+ blkid_dev dev,
+ const struct blkid_magic *id __BLKID_ATTR((unused)),
+ unsigned char *buf __BLKID_ATTR((unused)))
+{
+ struct swap_id_block *sws;
+
+ probe_swap0(fd, cache, dev, id, buf);
+ /*
+ * Version 1 swap headers are always located at offset of 1024
+ * bytes, although the swap signature itself is located at the
+ * end of the page (which may vary depending on hardware
+ * pagesize).
+ */
+ if (lseek(fd, 1024, SEEK_SET) < 0) return 1;
+ sws = xmalloc(1024);
+ if (read(fd, sws, 1024) != 1024) {
+ free(sws);
+ return 1;
+ }
+
+ /* arbitrary sanity check.. is there any garbage down there? */
+ if (sws->sws_pad[32] == 0 && sws->sws_pad[33] == 0) {
+ if (sws->sws_volume[0])
+ blkid_set_tag(dev, "LABEL", (const char*)sws->sws_volume,
+ sizeof(sws->sws_volume));
+ if (sws->sws_uuid[0])
+ set_uuid(dev, sws->sws_uuid);
+ }
+ free(sws);
+
+ return 0;
+}
+
+static const char
+* const udf_magic[] = { "BEA01", "BOOT2", "CD001", "CDW02", "NSR02",
+ "NSR03", "TEA01", 0 };
+
+static int probe_udf(int fd, blkid_cache cache __BLKID_ATTR((unused)),
+ blkid_dev dev __BLKID_ATTR((unused)),
+ const struct blkid_magic *id __BLKID_ATTR((unused)),
+ unsigned char *buf __BLKID_ATTR((unused)))
+{
+ int j, bs;
+ struct iso_volume_descriptor isosb;
+ const char * const * m;
+
+ /* determine the block size by scanning in 2K increments
+ (block sizes larger than 2K will be null padded) */
+ for (bs = 1; bs < 16; bs++) {
+ lseek(fd, bs*2048+32768, SEEK_SET);
+ if (read(fd, (char *)&isosb, sizeof(isosb)) != sizeof(isosb))
+ return 1;
+ if (isosb.id[0])
+ break;
+ }
+
+ /* Scan up to another 64 blocks looking for additional VSD's */
+ for (j = 1; j < 64; j++) {
+ if (j > 1) {
+ lseek(fd, j*bs*2048+32768, SEEK_SET);
+ if (read(fd, (char *)&isosb, sizeof(isosb))
+ != sizeof(isosb))
+ return 1;
+ }
+ /* If we find NSR0x then call it udf:
+ NSR01 for UDF 1.00
+ NSR02 for UDF 1.50
+ NSR03 for UDF 2.00 */
+ if (!strncmp(isosb.id, "NSR0", 4))
+ return 0;
+ for (m = udf_magic; *m; m++)
+ if (!strncmp(*m, isosb.id, 5))
+ break;
+ if (*m == 0)
+ return 1;
+ }
+ return 1;
+}
+
+static int probe_ocfs(int fd __BLKID_ATTR((unused)),
+ blkid_cache cache __BLKID_ATTR((unused)),
+ blkid_dev dev,
+ const struct blkid_magic *id __BLKID_ATTR((unused)),
+ unsigned char *buf)
+{
+ struct ocfs_volume_header ovh;
+ struct ocfs_volume_label ovl;
+ __u32 major;
+
+ memcpy(&ovh, buf, sizeof(ovh));
+ memcpy(&ovl, buf+512, sizeof(ovl));
+
+ major = ocfsmajor(ovh);
+ if (major == 1)
+ blkid_set_tag(dev,"SEC_TYPE","ocfs1",sizeof("ocfs1"));
+ else if (major >= 9)
+ blkid_set_tag(dev,"SEC_TYPE","ntocfs",sizeof("ntocfs"));
+
+ blkid_set_tag(dev, "LABEL", (const char*)ovl.label, ocfslabellen(ovl));
+ blkid_set_tag(dev, "MOUNT", (const char*)ovh.mount, ocfsmountlen(ovh));
+ set_uuid(dev, ovl.vol_id);
+ return 0;
+}
+
+static int probe_ocfs2(int fd __BLKID_ATTR((unused)),
+ blkid_cache cache __BLKID_ATTR((unused)),
+ blkid_dev dev,
+ const struct blkid_magic *id __BLKID_ATTR((unused)),
+ unsigned char *buf)
+{
+ struct ocfs2_super_block *osb;
+
+ osb = (struct ocfs2_super_block *)buf;
+
+ blkid_set_tag(dev, "LABEL", (const char*)osb->s_label, sizeof(osb->s_label));
+ set_uuid(dev, osb->s_uuid);
+ return 0;
+}
+
+static int probe_oracleasm(int fd __BLKID_ATTR((unused)),
+ blkid_cache cache __BLKID_ATTR((unused)),
+ blkid_dev dev,
+ const struct blkid_magic *id __BLKID_ATTR((unused)),
+ unsigned char *buf)
+{
+ struct oracle_asm_disk_label *dl;
+
+ dl = (struct oracle_asm_disk_label *)buf;
+
+ blkid_set_tag(dev, "LABEL", dl->dl_id, sizeof(dl->dl_id));
+ return 0;
+}
+
+/*
+ * BLKID_BLK_OFFS is at least as large as the highest bim_kboff defined
+ * in the type_array table below + bim_kbalign.
+ *
+ * When probing for a lot of magics, we handle everything in 1kB buffers so
+ * that we don't have to worry about reading each combination of block sizes.
+ */
+#define BLKID_BLK_OFFS 64 /* currently reiserfs */
+
+/*
+ * Various filesystem magics that we can check for. Note that kboff and
+ * sboff are in kilobytes and bytes respectively. All magics are in
+ * byte strings so we don't worry about endian issues.
+ */
+static const struct blkid_magic type_array[] = {
+/* type kboff sboff len magic probe */
+ { "oracleasm", 0, 32, 8, "ORCLDISK", probe_oracleasm },
+ { "ntfs", 0, 3, 8, "NTFS ", 0 },
+ { "jbd", 1, 0x38, 2, "\123\357", probe_jbd },
+ { "ext3", 1, 0x38, 2, "\123\357", probe_ext3 },
+ { "ext2", 1, 0x38, 2, "\123\357", probe_ext2 },
+ { "reiserfs", 8, 0x34, 8, "ReIsErFs", probe_reiserfs },
+ { "reiserfs", 64, 0x34, 9, "ReIsEr2Fs", probe_reiserfs },
+ { "reiserfs", 64, 0x34, 9, "ReIsEr3Fs", probe_reiserfs },
+ { "reiserfs", 64, 0x34, 8, "ReIsErFs", probe_reiserfs },
+ { "reiserfs", 8, 20, 8, "ReIsErFs", probe_reiserfs },
+ { "vfat", 0, 0x52, 5, "MSWIN", probe_vfat },
+ { "vfat", 0, 0x52, 8, "FAT32 ", probe_vfat },
+ { "vfat", 0, 0x36, 5, "MSDOS", probe_msdos },
+ { "vfat", 0, 0x36, 8, "FAT16 ", probe_msdos },
+ { "vfat", 0, 0x36, 8, "FAT12 ", probe_msdos },
+ { "minix", 1, 0x10, 2, "\177\023", 0 },
+ { "minix", 1, 0x10, 2, "\217\023", 0 },
+ { "minix", 1, 0x10, 2, "\150\044", 0 },
+ { "minix", 1, 0x10, 2, "\170\044", 0 },
+ { "vxfs", 1, 0, 4, "\365\374\001\245", 0 },
+ { "xfs", 0, 0, 4, "XFSB", probe_xfs },
+ { "romfs", 0, 0, 8, "-rom1fs-", probe_romfs },
+ { "bfs", 0, 0, 4, "\316\372\173\033", 0 },
+ { "cramfs", 0, 0, 4, "E=\315\050", probe_cramfs },
+ { "qnx4", 0, 4, 6, "QNX4FS", 0 },
+ { "udf", 32, 1, 5, "BEA01", probe_udf },
+ { "udf", 32, 1, 5, "BOOT2", probe_udf },
+ { "udf", 32, 1, 5, "CD001", probe_udf },
+ { "udf", 32, 1, 5, "CDW02", probe_udf },
+ { "udf", 32, 1, 5, "NSR02", probe_udf },
+ { "udf", 32, 1, 5, "NSR03", probe_udf },
+ { "udf", 32, 1, 5, "TEA01", probe_udf },
+ { "iso9660", 32, 1, 5, "CD001", 0 },
+ { "iso9660", 32, 9, 5, "CDROM", 0 },
+ { "jfs", 32, 0, 4, "JFS1", probe_jfs },
+ { "hfs", 1, 0, 2, "BD", 0 },
+ { "ufs", 8, 0x55c, 4, "T\031\001\000", 0 },
+ { "hpfs", 8, 0, 4, "I\350\225\371", 0 },
+ { "sysv", 0, 0x3f8, 4, "\020~\030\375", 0 },
+ { "swap", 0, 0xff6, 10, "SWAP-SPACE", probe_swap0 },
+ { "swap", 0, 0xff6, 10, "SWAPSPACE2", probe_swap1 },
+ { "swap", 0, 0x1ff6, 10, "SWAP-SPACE", probe_swap0 },
+ { "swap", 0, 0x1ff6, 10, "SWAPSPACE2", probe_swap1 },
+ { "swap", 0, 0x3ff6, 10, "SWAP-SPACE", probe_swap0 },
+ { "swap", 0, 0x3ff6, 10, "SWAPSPACE2", probe_swap1 },
+ { "swap", 0, 0x7ff6, 10, "SWAP-SPACE", probe_swap0 },
+ { "swap", 0, 0x7ff6, 10, "SWAPSPACE2", probe_swap1 },
+ { "swap", 0, 0xfff6, 10, "SWAP-SPACE", probe_swap0 },
+ { "swap", 0, 0xfff6, 10, "SWAPSPACE2", probe_swap1 },
+ { "ocfs", 0, 8, 9, "OracleCFS", probe_ocfs },
+ { "ocfs2", 1, 0, 6, "OCFSV2", probe_ocfs2 },
+ { "ocfs2", 2, 0, 6, "OCFSV2", probe_ocfs2 },
+ { "ocfs2", 4, 0, 6, "OCFSV2", probe_ocfs2 },
+ { "ocfs2", 8, 0, 6, "OCFSV2", probe_ocfs2 },
+ { NULL, 0, 0, 0, NULL, NULL }
+};
+
+/*
+ * Verify that the data in dev is consistent with what is on the actual
+ * block device (using the devname field only). Normally this will be
+ * called when finding items in the cache, but for long running processes
+ * is also desirable to revalidate an item before use.
+ *
+ * If we are unable to revalidate the data, we return the old data and
+ * do not set the BLKID_BID_FL_VERIFIED flag on it.
+ */
+blkid_dev blkid_verify(blkid_cache cache, blkid_dev dev)
+{
+ const struct blkid_magic *id;
+ unsigned char *bufs[BLKID_BLK_OFFS + 1], *buf;
+ const char *type;
+ struct stat st;
+ time_t diff, now;
+ int fd, idx;
+
+ if (!dev)
+ return NULL;
+
+ now = time(0);
+ diff = now - dev->bid_time;
+
+ if ((now < dev->bid_time) ||
+ (diff < BLKID_PROBE_MIN) ||
+ (dev->bid_flags & BLKID_BID_FL_VERIFIED &&
+ diff < BLKID_PROBE_INTERVAL))
+ return dev;
+
+ DBG(DEBUG_PROBE,
+ printf("need to revalidate %s (time since last check %lu)\n",
+ dev->bid_name, diff));
+
+ if (((fd = open(dev->bid_name, O_RDONLY)) < 0) ||
+ (fstat(fd, &st) < 0)) {
+ if (errno == ENXIO || errno == ENODEV || errno == ENOENT) {
+ blkid_free_dev(dev);
+ return NULL;
+ }
+ /* We don't have read permission, just return cache data. */
+ DBG(DEBUG_PROBE,
+ printf("returning unverified data for %s\n",
+ dev->bid_name));
+ return dev;
+ }
+
+ memset(bufs, 0, sizeof(bufs));
+
+ /*
+ * Iterate over the type array. If we already know the type,
+ * then try that first. If it doesn't work, then blow away
+ * the type information, and try again.
+ *
+ */
+try_again:
+ type = 0;
+ if (!dev->bid_type || !strcmp(dev->bid_type, "mdraid")) {
+ uuid_t uuid;
+
+ if (check_mdraid(fd, uuid) == 0) {
+ set_uuid(dev, uuid);
+ type = "mdraid";
+ goto found_type;
+ }
+ }
+ for (id = type_array; id->bim_type; id++) {
+ if (dev->bid_type &&
+ strcmp(id->bim_type, dev->bid_type))
+ continue;
+
+ idx = id->bim_kboff + (id->bim_sboff >> 10);
+ if (idx > BLKID_BLK_OFFS || idx < 0)
+ continue;
+ buf = bufs[idx];
+ if (!buf) {
+ if (lseek(fd, idx << 10, SEEK_SET) < 0)
+ continue;
+
+ buf = xmalloc(1024);
+
+ if (read(fd, buf, 1024) != 1024) {
+ free(buf);
+ continue;
+ }
+ bufs[idx] = buf;
+ }
+
+ if (memcmp(id->bim_magic, buf + (id->bim_sboff&0x3ff),
+ id->bim_len))
+ continue;
+
+ if ((id->bim_probe == NULL) ||
+ (id->bim_probe(fd, cache, dev, id, buf) == 0)) {
+ type = id->bim_type;
+ goto found_type;
+ }
+ }
+
+ if (!id->bim_type && dev->bid_type) {
+ /*
+ * Zap the device filesystem type and try again
+ */
+ blkid_set_tag(dev, "TYPE", 0, 0);
+ blkid_set_tag(dev, "SEC_TYPE", 0, 0);
+ blkid_set_tag(dev, "LABEL", 0, 0);
+ blkid_set_tag(dev, "UUID", 0, 0);
+ goto try_again;
+ }
+
+ if (!dev->bid_type) {
+ blkid_free_dev(dev);
+ return NULL;
+ }
+
+found_type:
+ if (dev && type) {
+ dev->bid_devno = st.st_rdev;
+ dev->bid_time = time(0);
+ dev->bid_flags |= BLKID_BID_FL_VERIFIED;
+ cache->bic_flags |= BLKID_BIC_FL_CHANGED;
+
+ blkid_set_tag(dev, "TYPE", type, 0);
+
+ DBG(DEBUG_PROBE, printf("%s: devno 0x%04llx, type %s\n",
+ dev->bid_name, st.st_rdev, type));
+ }
+
+ close(fd);
+
+ return dev;
+}
+
+int blkid_known_fstype(const char *fstype)
+{
+ const struct blkid_magic *id;
+
+ for (id = type_array; id->bim_type; id++) {
+ if (strcmp(fstype, id->bim_type) == 0)
+ return 1;
+ }
+ return 0;
+}
+
+#ifdef TEST_PROGRAM
+int main(int argc, char **argv)
+{
+ blkid_dev dev;
+ blkid_cache cache;
+ int ret;
+
+ blkid_debug_mask = DEBUG_ALL;
+ if (argc != 2) {
+ fprintf(stderr, "Usage: %s device\n"
+ "Probe a single device to determine type\n", argv[0]);
+ exit(1);
+ }
+ if ((ret = blkid_get_cache(&cache, bb_dev_null)) != 0) {
+ fprintf(stderr, "%s: error creating cache (%d)\n",
+ argv[0], ret);
+ exit(1);
+ }
+ dev = blkid_get_dev(cache, argv[1], BLKID_DEV_NORMAL);
+ if (!dev) {
+ printf("%s: %s has an unsupported type\n", argv[0], argv[1]);
+ return 1;
+ }
+ printf("%s is type %s\n", argv[1], dev->bid_type ?
+ dev->bid_type : "(null)");
+ if (dev->bid_label)
+ printf("\tlabel is '%s'\n", dev->bid_label);
+ if (dev->bid_uuid)
+ printf("\tuuid is %s\n", dev->bid_uuid);
+
+ blkid_free_dev(dev);
+ return 0;
+}
+#endif
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/blkid/probe.h b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/blkid/probe.h
new file mode 100644
index 0000000..0fd16a7
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/blkid/probe.h
@@ -0,0 +1,375 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * probe.h - constants and on-disk structures for extracting device data
+ *
+ * Copyright (C) 1999 by Andries Brouwer
+ * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o
+ * Copyright (C) 2001 by Andreas Dilger
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+
+#ifndef _BLKID_PROBE_H
+#define _BLKID_PROBE_H
+
+#include <linux/types.h>
+
+struct blkid_magic;
+
+typedef int (*blkid_probe_t)(int fd, blkid_cache cache, blkid_dev dev,
+ const struct blkid_magic *id, unsigned char *buf);
+
+struct blkid_magic {
+ const char *bim_type; /* type name for this magic */
+ long bim_kboff; /* kilobyte offset of superblock */
+ unsigned bim_sboff; /* byte offset within superblock */
+ unsigned bim_len; /* length of magic */
+ const char *bim_magic; /* magic string */
+ blkid_probe_t bim_probe; /* probe function */
+};
+
+/*
+ * Structures for each of the content types we want to extract information
+ * from. We do not necessarily need the magic field here, because we have
+ * already identified the content type before we get this far. It may still
+ * be useful if there are probe functions which handle multiple content types.
+ */
+struct ext2_super_block {
+ __u32 s_inodes_count;
+ __u32 s_blocks_count;
+ __u32 s_r_blocks_count;
+ __u32 s_free_blocks_count;
+ __u32 s_free_inodes_count;
+ __u32 s_first_data_block;
+ __u32 s_log_block_size;
+ __u32 s_dummy3[7];
+ unsigned char s_magic[2];
+ __u16 s_state;
+ __u32 s_dummy5[8];
+ __u32 s_feature_compat;
+ __u32 s_feature_incompat;
+ __u32 s_feature_ro_compat;
+ unsigned char s_uuid[16];
+ char s_volume_name[16];
+};
+#define EXT3_FEATURE_COMPAT_HAS_JOURNAL 0x00000004
+#define EXT3_FEATURE_INCOMPAT_RECOVER 0x00000004
+#define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV 0x00000008
+
+struct xfs_super_block {
+ unsigned char xs_magic[4];
+ __u32 xs_blocksize;
+ __u64 xs_dblocks;
+ __u64 xs_rblocks;
+ __u32 xs_dummy1[2];
+ unsigned char xs_uuid[16];
+ __u32 xs_dummy2[15];
+ char xs_fname[12];
+ __u32 xs_dummy3[2];
+ __u64 xs_icount;
+ __u64 xs_ifree;
+ __u64 xs_fdblocks;
+};
+
+struct reiserfs_super_block {
+ __u32 rs_blocks_count;
+ __u32 rs_free_blocks;
+ __u32 rs_root_block;
+ __u32 rs_journal_block;
+ __u32 rs_journal_dev;
+ __u32 rs_orig_journal_size;
+ __u32 rs_dummy2[5];
+ __u16 rs_blocksize;
+ __u16 rs_dummy3[3];
+ unsigned char rs_magic[12];
+ __u32 rs_dummy4[5];
+ unsigned char rs_uuid[16];
+ char rs_label[16];
+};
+
+struct jfs_super_block {
+ unsigned char js_magic[4];
+ __u32 js_version;
+ __u64 js_size;
+ __u32 js_bsize;
+ __u32 js_dummy1;
+ __u32 js_pbsize;
+ __u32 js_dummy2[27];
+ unsigned char js_uuid[16];
+ unsigned char js_label[16];
+ unsigned char js_loguuid[16];
+};
+
+struct romfs_super_block {
+ unsigned char ros_magic[8];
+ __u32 ros_dummy1[2];
+ unsigned char ros_volume[16];
+};
+
+struct cramfs_super_block {
+ __u8 magic[4];
+ __u32 size;
+ __u32 flags;
+ __u32 future;
+ __u8 signature[16];
+ struct cramfs_info {
+ __u32 crc;
+ __u32 edition;
+ __u32 blocks;
+ __u32 files;
+ } info;
+ __u8 name[16];
+};
+
+struct swap_id_block {
+/* unsigned char sws_boot[1024]; */
+ __u32 sws_version;
+ __u32 sws_lastpage;
+ __u32 sws_nrbad;
+ unsigned char sws_uuid[16];
+ char sws_volume[16];
+ unsigned char sws_pad[117];
+ __u32 sws_badpg;
+};
+
+/* Yucky misaligned values */
+struct vfat_super_block {
+/* 00*/ unsigned char vs_ignored[3];
+/* 03*/ unsigned char vs_sysid[8];
+/* 0b*/ unsigned char vs_sector_size[2];
+/* 0d*/ __u8 vs_cluster_size;
+/* 0e*/ __u16 vs_reserved;
+/* 10*/ __u8 vs_fats;
+/* 11*/ unsigned char vs_dir_entries[2];
+/* 13*/ unsigned char vs_sectors[2];
+/* 15*/ unsigned char vs_media;
+/* 16*/ __u16 vs_fat_length;
+/* 18*/ __u16 vs_secs_track;
+/* 1a*/ __u16 vs_heads;
+/* 1c*/ __u32 vs_hidden;
+/* 20*/ __u32 vs_total_sect;
+/* 24*/ __u32 vs_fat32_length;
+/* 28*/ __u16 vs_flags;
+/* 2a*/ __u8 vs_version[2];
+/* 2c*/ __u32 vs_root_cluster;
+/* 30*/ __u16 vs_insfo_sector;
+/* 32*/ __u16 vs_backup_boot;
+/* 34*/ __u16 vs_reserved2[6];
+/* 40*/ unsigned char vs_unknown[3];
+/* 43*/ unsigned char vs_serno[4];
+/* 47*/ char vs_label[11];
+/* 52*/ unsigned char vs_magic[8];
+/* 5a*/ unsigned char vs_dummy2[164];
+/*1fe*/ unsigned char vs_pmagic[2];
+};
+
+/* Yucky misaligned values */
+struct msdos_super_block {
+/* 00*/ unsigned char ms_ignored[3];
+/* 03*/ unsigned char ms_sysid[8];
+/* 0b*/ unsigned char ms_sector_size[2];
+/* 0d*/ __u8 ms_cluster_size;
+/* 0e*/ __u16 ms_reserved;
+/* 10*/ __u8 ms_fats;
+/* 11*/ unsigned char ms_dir_entries[2];
+/* 13*/ unsigned char ms_sectors[2];
+/* 15*/ unsigned char ms_media;
+/* 16*/ __u16 ms_fat_length;
+/* 18*/ __u16 ms_secs_track;
+/* 1a*/ __u16 ms_heads;
+/* 1c*/ __u32 ms_hidden;
+/* 20*/ __u32 ms_total_sect;
+/* 24*/ unsigned char ms_unknown[3];
+/* 27*/ unsigned char ms_serno[4];
+/* 2b*/ char ms_label[11];
+/* 36*/ unsigned char ms_magic[8];
+/* 3d*/ unsigned char ms_dummy2[192];
+/*1fe*/ unsigned char ms_pmagic[2];
+};
+
+struct minix_super_block {
+ __u16 ms_ninodes;
+ __u16 ms_nzones;
+ __u16 ms_imap_blocks;
+ __u16 ms_zmap_blocks;
+ __u16 ms_firstdatazone;
+ __u16 ms_log_zone_size;
+ __u32 ms_max_size;
+ unsigned char ms_magic[2];
+ __u16 ms_state;
+ __u32 ms_zones;
+};
+
+struct mdp_superblock_s {
+ __u32 md_magic;
+ __u32 major_version;
+ __u32 minor_version;
+ __u32 patch_version;
+ __u32 gvalid_words;
+ __u32 set_uuid0;
+ __u32 ctime;
+ __u32 level;
+ __u32 size;
+ __u32 nr_disks;
+ __u32 raid_disks;
+ __u32 md_minor;
+ __u32 not_persistent;
+ __u32 set_uuid1;
+ __u32 set_uuid2;
+ __u32 set_uuid3;
+};
+
+struct hfs_super_block {
+ char h_magic[2];
+ char h_dummy[18];
+ __u32 h_blksize;
+};
+
+struct ocfs_volume_header {
+ unsigned char minor_version[4];
+ unsigned char major_version[4];
+ unsigned char signature[128];
+ char mount[128];
+ unsigned char mount_len[2];
+};
+
+struct ocfs_volume_label {
+ unsigned char disk_lock[48];
+ char label[64];
+ unsigned char label_len[2];
+ unsigned char vol_id[16];
+ unsigned char vol_id_len[2];
+};
+
+#define ocfsmajor(o) ((__u32)o.major_version[0] \
+ + (((__u32) o.major_version[1]) << 8) \
+ + (((__u32) o.major_version[2]) << 16) \
+ + (((__u32) o.major_version[3]) << 24))
+#define ocfslabellen(o) ((__u32)o.label_len[0] + (((__u32) o.label_len[1]) << 8))
+#define ocfsmountlen(o) ((__u32)o.mount_len[0] + (((__u32) o.mount_len[1])<<8))
+
+#define OCFS_MAGIC "OracleCFS"
+
+struct ocfs2_super_block {
+ unsigned char signature[8];
+ unsigned char s_dummy1[184];
+ unsigned char s_dummy2[80];
+ char s_label[64];
+ unsigned char s_uuid[16];
+};
+
+#define OCFS2_MIN_BLOCKSIZE 512
+#define OCFS2_MAX_BLOCKSIZE 4096
+
+#define OCFS2_SUPER_BLOCK_BLKNO 2
+
+#define OCFS2_SUPER_BLOCK_SIGNATURE "OCFSV2"
+
+struct oracle_asm_disk_label {
+ char dummy[32];
+ char dl_tag[8];
+ char dl_id[24];
+};
+
+#define ORACLE_ASM_DISK_LABEL_MARKED "ORCLDISK"
+#define ORACLE_ASM_DISK_LABEL_OFFSET 32
+
+#define ISODCL(from, to) (to - from + 1)
+struct iso_volume_descriptor {
+ char type[ISODCL(1,1)]; /* 711 */
+ char id[ISODCL(2,6)];
+ char version[ISODCL(7,7)];
+ char data[ISODCL(8,2048)];
+};
+
+/*
+ * Byte swap functions
+ */
+#ifdef __GNUC__
+#define _INLINE_ static __inline__
+#else /* For Watcom C */
+#define _INLINE_ static inline
+#endif
+
+static __u16 blkid_swab16(__u16 val);
+static __u32 blkid_swab32(__u32 val);
+static __u64 blkid_swab64(__u64 val);
+
+#if ((defined __GNUC__) && \
+ (defined(__i386__) || defined(__i486__) || defined(__i586__)))
+
+#define _BLKID_HAVE_ASM_BITOPS_
+
+_INLINE_ __u32 blkid_swab32(__u32 val)
+{
+#ifdef EXT2FS_REQUIRE_486
+ __asm__("bswap %0" : "=r" (val) : "0" (val));
+#else
+ __asm__("xchgb %b0,%h0\n\t" /* swap lower bytes */
+ "rorl $16,%0\n\t" /* swap words */
+ "xchgb %b0,%h0" /* swap higher bytes */
+ :"=q" (val)
+ : "0" (val));
+#endif
+ return val;
+}
+
+_INLINE_ __u16 blkid_swab16(__u16 val)
+{
+ __asm__("xchgb %b0,%h0" /* swap bytes */ \
+ : "=q" (val) \
+ : "0" (val)); \
+ return val;
+}
+
+_INLINE_ __u64 blkid_swab64(__u64 val)
+{
+ return blkid_swab32(val >> 32) |
+ ( ((__u64)blkid_swab32((__u32)val)) << 32 );
+}
+#endif
+
+#if !defined(_BLKID_HAVE_ASM_BITOPS_)
+
+_INLINE_ __u16 blkid_swab16(__u16 val)
+{
+ return (val >> 8) | (val << 8);
+}
+
+_INLINE_ __u32 blkid_swab32(__u32 val)
+{
+ return (val>>24) | ((val>>8) & 0xFF00) |
+ ((val<<8) & 0xFF0000) | (val<<24);
+}
+
+_INLINE_ __u64 blkid_swab64(__u64 val)
+{
+ return blkid_swab32(val >> 32) |
+ ( ((__u64)blkid_swab32((__u32)val)) << 32 );
+}
+#endif
+
+
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+#define blkid_le16(x) blkid_swab16(x)
+#define blkid_le32(x) blkid_swab32(x)
+#define blkid_le64(x) blkid_swab64(x)
+#define blkid_be16(x) (x)
+#define blkid_be32(x) (x)
+#define blkid_be64(x) (x)
+#else
+#define blkid_le16(x) (x)
+#define blkid_le32(x) (x)
+#define blkid_le64(x) (x)
+#define blkid_be16(x) blkid_swab16(x)
+#define blkid_be32(x) blkid_swab32(x)
+#define blkid_be64(x) blkid_swab64(x)
+#endif
+
+#undef _INLINE_
+
+#endif /* _BLKID_PROBE_H */
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/blkid/read.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/blkid/read.c
new file mode 100644
index 0000000..276e62a
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/blkid/read.c
@@ -0,0 +1,461 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * read.c - read the blkid cache from disk, to avoid scanning all devices
+ *
+ * Copyright (C) 2001, 2003 Theodore Y. Ts'o
+ * Copyright (C) 2001 Andreas Dilger
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "blkidP.h"
+#include "../uuid/uuid.h"
+
+#ifdef HAVE_STRTOULL
+#define __USE_ISOC9X
+#define STRTOULL strtoull /* defined in stdlib.h if you try hard enough */
+#else
+/* FIXME: need to support real strtoull here */
+#define STRTOULL strtoul
+#endif
+
+#include <stdlib.h>
+
+#ifdef TEST_PROGRAM
+#define blkid_debug_dump_dev(dev) (debug_dump_dev(dev))
+static void debug_dump_dev(blkid_dev dev);
+#endif
+
+/*
+ * File format:
+ *
+ * <device [<NAME="value"> ...]>device_name</device>
+ *
+ * The following tags are required for each entry:
+ * <ID="id"> unique (within this file) ID number of this device
+ * <TIME="time"> (ascii time_t) time this entry was last read from disk
+ * <TYPE="type"> (detected) type of filesystem/data for this partition
+ *
+ * The following tags may be present, depending on the device contents
+ * <LABEL="label"> (user supplied) label (volume name, etc)
+ * <UUID="uuid"> (generated) universally unique identifier (serial no)
+ */
+
+static char *skip_over_blank(char *cp)
+{
+ while (*cp && isspace(*cp))
+ cp++;
+ return cp;
+}
+
+static char *skip_over_word(char *cp)
+{
+ char ch;
+
+ while ((ch = *cp)) {
+ /* If we see a backslash, skip the next character */
+ if (ch == '\\') {
+ cp++;
+ if (*cp == '\0')
+ break;
+ cp++;
+ continue;
+ }
+ if (isspace(ch) || ch == '<' || ch == '>')
+ break;
+ cp++;
+ }
+ return cp;
+}
+
+static char *strip_line(char *line)
+{
+ char *p;
+
+ line = skip_over_blank(line);
+
+ p = line + strlen(line) - 1;
+
+ while (*line) {
+ if (isspace(*p))
+ *p-- = '\0';
+ else
+ break;
+ }
+
+ return line;
+}
+
+/*
+ * Start parsing a new line from the cache.
+ *
+ * line starts with "<device" return 1 -> continue parsing line
+ * line starts with "<foo", empty, or # return 0 -> skip line
+ * line starts with other, return -BLKID_ERR_CACHE -> error
+ */
+static int parse_start(char **cp)
+{
+ char *p;
+
+ p = strip_line(*cp);
+
+ /* Skip comment or blank lines. We can't just NUL the first '#' char,
+ * in case it is inside quotes, or escaped.
+ */
+ if (*p == '\0' || *p == '#')
+ return 0;
+
+ if (!strncmp(p, "<device", 7)) {
+ DBG(DEBUG_READ, printf("found device header: %8s\n", p));
+ p += 7;
+
+ *cp = p;
+ return 1;
+ }
+
+ if (*p == '<')
+ return 0;
+
+ return -BLKID_ERR_CACHE;
+}
+
+/* Consume the remaining XML on the line (cosmetic only) */
+static int parse_end(char **cp)
+{
+ *cp = skip_over_blank(*cp);
+
+ if (!strncmp(*cp, "</device>", 9)) {
+ DBG(DEBUG_READ, printf("found device trailer %9s\n", *cp));
+ *cp += 9;
+ return 0;
+ }
+
+ return -BLKID_ERR_CACHE;
+}
+
+/*
+ * Allocate a new device struct with device name filled in. Will handle
+ * finding the device on lines of the form:
+ * <device foo=bar>devname</device>
+ * <device>devname<foo>bar</foo></device>
+ */
+static int parse_dev(blkid_cache cache, blkid_dev *dev, char **cp)
+{
+ char *start, *tmp, *end, *name;
+ int ret;
+
+ if ((ret = parse_start(cp)) <= 0)
+ return ret;
+
+ start = tmp = strchr(*cp, '>');
+ if (!start) {
+ DBG(DEBUG_READ,
+ printf("blkid: short line parsing dev: %s\n", *cp));
+ return -BLKID_ERR_CACHE;
+ }
+ start = skip_over_blank(start + 1);
+ end = skip_over_word(start);
+
+ DBG(DEBUG_READ, printf("device should be %*s\n", end - start, start));
+
+ if (**cp == '>')
+ *cp = end;
+ else
+ (*cp)++;
+
+ *tmp = '\0';
+
+ if (!(tmp = strrchr(end, '<')) || parse_end(&tmp) < 0) {
+ DBG(DEBUG_READ,
+ printf("blkid: missing </device> ending: %s\n", end));
+ } else if (tmp)
+ *tmp = '\0';
+
+ if (end - start <= 1) {
+ DBG(DEBUG_READ, printf("blkid: empty device name: %s\n", *cp));
+ return -BLKID_ERR_CACHE;
+ }
+
+ name = blkid_strndup(start, end-start);
+ if (name == NULL)
+ return -BLKID_ERR_MEM;
+
+ DBG(DEBUG_READ, printf("found dev %s\n", name));
+
+ if (!(*dev = blkid_get_dev(cache, name, BLKID_DEV_CREATE)))
+ return -BLKID_ERR_MEM;
+
+ free(name);
+ return 1;
+}
+
+/*
+ * Extract a tag of the form NAME="value" from the line.
+ */
+static int parse_token(char **name, char **value, char **cp)
+{
+ char *end;
+
+ if (!name || !value || !cp)
+ return -BLKID_ERR_PARAM;
+
+ if (!(*value = strchr(*cp, '=')))
+ return 0;
+
+ **value = '\0';
+ *name = strip_line(*cp);
+ *value = skip_over_blank(*value + 1);
+
+ if (**value == '"') {
+ end = strchr(*value + 1, '"');
+ if (!end) {
+ DBG(DEBUG_READ,
+ printf("unbalanced quotes at: %s\n", *value));
+ *cp = *value;
+ return -BLKID_ERR_CACHE;
+ }
+ (*value)++;
+ *end = '\0';
+ end++;
+ } else {
+ end = skip_over_word(*value);
+ if (*end) {
+ *end = '\0';
+ end++;
+ }
+ }
+ *cp = end;
+
+ return 1;
+}
+
+/*
+ * Extract a tag of the form <NAME>value</NAME> from the line.
+ */
+/*
+static int parse_xml(char **name, char **value, char **cp)
+{
+ char *end;
+
+ if (!name || !value || !cp)
+ return -BLKID_ERR_PARAM;
+
+ *name = strip_line(*cp);
+
+ if ((*name)[0] != '<' || (*name)[1] == '/')
+ return 0;
+
+ FIXME: finish this.
+}
+*/
+
+/*
+ * Extract a tag from the line.
+ *
+ * Return 1 if a valid tag was found.
+ * Return 0 if no tag found.
+ * Return -ve error code.
+ */
+static int parse_tag(blkid_cache cache, blkid_dev dev, char **cp)
+{
+ char *name;
+ char *value;
+ int ret;
+
+ if (!cache || !dev)
+ return -BLKID_ERR_PARAM;
+
+ if ((ret = parse_token(&name, &value, cp)) <= 0 /* &&
+ (ret = parse_xml(&name, &value, cp)) <= 0 */)
+ return ret;
+
+ /* Some tags are stored directly in the device struct */
+ if (!strcmp(name, "DEVNO"))
+ dev->bid_devno = STRTOULL(value, 0, 0);
+ else if (!strcmp(name, "PRI"))
+ dev->bid_pri = strtol(value, 0, 0);
+ else if (!strcmp(name, "TIME"))
+ /* FIXME: need to parse a long long eventually */
+ dev->bid_time = strtol(value, 0, 0);
+ else
+ ret = blkid_set_tag(dev, name, value, strlen(value));
+
+ DBG(DEBUG_READ, printf(" tag: %s=\"%s\"\n", name, value));
+
+ return ret < 0 ? ret : 1;
+}
+
+/*
+ * Parse a single line of data, and return a newly allocated dev struct.
+ * Add the new device to the cache struct, if one was read.
+ *
+ * Lines are of the form <device [TAG="value" ...]>/dev/foo</device>
+ *
+ * Returns -ve value on error.
+ * Returns 0 otherwise.
+ * If a valid device was read, *dev_p is non-NULL, otherwise it is NULL
+ * (e.g. comment lines, unknown XML content, etc).
+ */
+static int blkid_parse_line(blkid_cache cache, blkid_dev *dev_p, char *cp)
+{
+ blkid_dev dev;
+ int ret;
+
+ if (!cache || !dev_p)
+ return -BLKID_ERR_PARAM;
+
+ *dev_p = NULL;
+
+ DBG(DEBUG_READ, printf("line: %s\n", cp));
+
+ if ((ret = parse_dev(cache, dev_p, &cp)) <= 0)
+ return ret;
+
+ dev = *dev_p;
+
+ while ((ret = parse_tag(cache, dev, &cp)) > 0) {
+ ;
+ }
+
+ if (dev->bid_type == NULL) {
+ DBG(DEBUG_READ,
+ printf("blkid: device %s has no TYPE\n",dev->bid_name));
+ blkid_free_dev(dev);
+ }
+
+ DBG(DEBUG_READ, blkid_debug_dump_dev(dev));
+
+ return ret;
+}
+
+/*
+ * Parse the specified filename, and return the data in the supplied or
+ * a newly allocated cache struct. If the file doesn't exist, return a
+ * new empty cache struct.
+ */
+void blkid_read_cache(blkid_cache cache)
+{
+ FILE *file;
+ char buf[4096];
+ int fd, lineno = 0;
+ struct stat st;
+
+ if (!cache)
+ return;
+
+ /*
+ * If the file doesn't exist, then we just return an empty
+ * struct so that the cache can be populated.
+ */
+ if ((fd = open(cache->bic_filename, O_RDONLY)) < 0)
+ return;
+ if (fstat(fd, &st) < 0)
+ goto errout;
+ if ((st.st_mtime == cache->bic_ftime) ||
+ (cache->bic_flags & BLKID_BIC_FL_CHANGED)) {
+ DBG(DEBUG_CACHE, printf("skipping re-read of %s\n",
+ cache->bic_filename));
+ goto errout;
+ }
+
+ DBG(DEBUG_CACHE, printf("reading cache file %s\n",
+ cache->bic_filename));
+
+ file = fdopen(fd, "r");
+ if (!file)
+ goto errout;
+
+ while (fgets(buf, sizeof(buf), file)) {
+ blkid_dev dev;
+ unsigned int end;
+
+ lineno++;
+ if (buf[0] == 0)
+ continue;
+ end = strlen(buf) - 1;
+ /* Continue reading next line if it ends with a backslash */
+ while (buf[end] == '\\' && end < sizeof(buf) - 2 &&
+ fgets(buf + end, sizeof(buf) - end, file)) {
+ end = strlen(buf) - 1;
+ lineno++;
+ }
+
+ if (blkid_parse_line(cache, &dev, buf) < 0) {
+ DBG(DEBUG_READ,
+ printf("blkid: bad format on line %d\n", lineno));
+ continue;
+ }
+ }
+ fclose(file);
+
+ /*
+ * Initially we do not need to write out the cache file.
+ */
+ cache->bic_flags &= ~BLKID_BIC_FL_CHANGED;
+ cache->bic_ftime = st.st_mtime;
+
+ return;
+errout:
+ close(fd);
+}
+
+#ifdef TEST_PROGRAM
+static void debug_dump_dev(blkid_dev dev)
+{
+ struct list_head *p;
+
+ if (!dev) {
+ printf(" dev: NULL\n");
+ return;
+ }
+
+ printf(" dev: name = %s\n", dev->bid_name);
+ printf(" dev: DEVNO=\"0x%0llx\"\n", dev->bid_devno);
+ printf(" dev: TIME=\"%lu\"\n", dev->bid_time);
+ printf(" dev: PRI=\"%d\"\n", dev->bid_pri);
+ printf(" dev: flags = 0x%08X\n", dev->bid_flags);
+
+ list_for_each(p, &dev->bid_tags) {
+ blkid_tag tag = list_entry(p, struct blkid_struct_tag, bit_tags);
+ if (tag)
+ printf(" tag: %s=\"%s\"\n", tag->bit_name,
+ tag->bit_val);
+ else
+ printf(" tag: NULL\n");
+ }
+ puts("");
+}
+
+int main(int argc, char**argv)
+{
+ blkid_cache cache = NULL;
+ int ret;
+
+ blkid_debug_mask = DEBUG_ALL;
+ if (argc > 2) {
+ fprintf(stderr, "Usage: %s [filename]\n"
+ "Test parsing of the cache (filename)\n", argv[0]);
+ exit(1);
+ }
+ if ((ret = blkid_get_cache(&cache, argv[1])) < 0)
+ fprintf(stderr, "error %d reading cache file %s\n", ret,
+ argv[1] ? argv[1] : BLKID_CACHE_FILE);
+
+ blkid_put_cache(cache);
+
+ return ret;
+}
+#endif
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/blkid/resolve.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/blkid/resolve.c
new file mode 100644
index 0000000..7942de2
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/blkid/resolve.c
@@ -0,0 +1,139 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * resolve.c - resolve names and tags into specific devices
+ *
+ * Copyright (C) 2001, 2003 Theodore Ts'o.
+ * Copyright (C) 2001 Andreas Dilger
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "blkidP.h"
+#include "probe.h"
+
+/*
+ * Find a tagname (e.g. LABEL or UUID) on a specific device.
+ */
+char *blkid_get_tag_value(blkid_cache cache, const char *tagname,
+ const char *devname)
+{
+ blkid_tag found;
+ blkid_dev dev;
+ blkid_cache c = cache;
+ char *ret = NULL;
+
+ DBG(DEBUG_RESOLVE, printf("looking for %s on %s\n", tagname, devname));
+
+ if (!devname)
+ return NULL;
+
+ if (!cache) {
+ if (blkid_get_cache(&c, NULL) < 0)
+ return NULL;
+ }
+
+ if ((dev = blkid_get_dev(c, devname, BLKID_DEV_NORMAL)) &&
+ (found = blkid_find_tag_dev(dev, tagname)))
+ ret = blkid_strdup(found->bit_val);
+
+ if (!cache)
+ blkid_put_cache(c);
+
+ return ret;
+}
+
+/*
+ * Locate a device name from a token (NAME=value string), or (name, value)
+ * pair. In the case of a token, value is ignored. If the "token" is not
+ * of the form "NAME=value" and there is no value given, then it is assumed
+ * to be the actual devname and a copy is returned.
+ */
+char *blkid_get_devname(blkid_cache cache, const char *token,
+ const char *value)
+{
+ blkid_dev dev;
+ blkid_cache c = cache;
+ char *t = 0, *v = 0;
+ char *ret = NULL;
+
+ if (!token)
+ return NULL;
+
+ if (!cache) {
+ if (blkid_get_cache(&c, NULL) < 0)
+ return NULL;
+ }
+
+ DBG(DEBUG_RESOLVE,
+ printf("looking for %s%s%s %s\n", token, value ? "=" : "",
+ value ? value : "", cache ? "in cache" : "from disk"));
+
+ if (!value) {
+ if (!strchr(token, '='))
+ return blkid_strdup(token);
+ blkid_parse_tag_string(token, &t, &v);
+ if (!t || !v)
+ goto errout;
+ token = t;
+ value = v;
+ }
+
+ dev = blkid_find_dev_with_tag(c, token, value);
+ if (!dev)
+ goto errout;
+
+ ret = blkid_strdup(blkid_dev_devname(dev));
+
+errout:
+ free(t);
+ free(v);
+ if (!cache) {
+ blkid_put_cache(c);
+ }
+ return ret;
+}
+
+#ifdef TEST_PROGRAM
+int main(int argc, char **argv)
+{
+ char *value;
+ blkid_cache cache;
+
+ blkid_debug_mask = DEBUG_ALL;
+ if (argc != 2 && argc != 3) {
+ fprintf(stderr, "Usage:\t%s tagname=value\n"
+ "\t%s tagname devname\n"
+ "Find which device holds a given token or\n"
+ "Find what the value of a tag is in a device\n",
+ argv[0], argv[0]);
+ exit(1);
+ }
+ if (blkid_get_cache(&cache, bb_dev_null) < 0) {
+ fprintf(stderr, "cannot get blkid cache\n");
+ exit(1);
+ }
+
+ if (argv[2]) {
+ value = blkid_get_tag_value(cache, argv[1], argv[2]);
+ printf("%s has tag %s=%s\n", argv[2], argv[1],
+ value ? value : "<missing>");
+ } else {
+ value = blkid_get_devname(cache, argv[1], NULL);
+ printf("%s has tag %s\n", value ? value : "<none>", argv[1]);
+ }
+ blkid_put_cache(cache);
+ return value ? 0 : 1;
+}
+#endif
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/blkid/save.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/blkid/save.c
new file mode 100644
index 0000000..cdbaabc
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/blkid/save.c
@@ -0,0 +1,189 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * save.c - write the cache struct to disk
+ *
+ * Copyright (C) 2001 by Andreas Dilger
+ * Copyright (C) 2003 Theodore Ts'o
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_SYS_MKDEV_H
+#include <sys/mkdev.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include "blkidP.h"
+
+static int save_dev(blkid_dev dev, FILE *file)
+{
+ struct list_head *p;
+
+ if (!dev || dev->bid_name[0] != '/')
+ return 0;
+
+ DBG(DEBUG_SAVE,
+ printf("device %s, type %s\n", dev->bid_name, dev->bid_type));
+
+ fprintf(file,
+ "<device DEVNO=\"0x%04lx\" TIME=\"%lu\"",
+ (unsigned long) dev->bid_devno, dev->bid_time);
+ if (dev->bid_pri)
+ fprintf(file, " PRI=\"%d\"", dev->bid_pri);
+ list_for_each(p, &dev->bid_tags) {
+ blkid_tag tag = list_entry(p, struct blkid_struct_tag, bit_tags);
+ fprintf(file, " %s=\"%s\"", tag->bit_name,tag->bit_val);
+ }
+ fprintf(file, ">%s</device>\n", dev->bid_name);
+
+ return 0;
+}
+
+/*
+ * Write out the cache struct to the cache file on disk.
+ */
+int blkid_flush_cache(blkid_cache cache)
+{
+ struct list_head *p;
+ char *tmp = NULL;
+ const char *opened = NULL;
+ const char *filename;
+ FILE *file = NULL;
+ int fd, ret = 0;
+ struct stat st;
+
+ if (!cache)
+ return -BLKID_ERR_PARAM;
+
+ if (list_empty(&cache->bic_devs) ||
+ !(cache->bic_flags & BLKID_BIC_FL_CHANGED)) {
+ DBG(DEBUG_SAVE, printf("skipping cache file write\n"));
+ return 0;
+ }
+
+ filename = cache->bic_filename ? cache->bic_filename: BLKID_CACHE_FILE;
+
+ /* If we can't write to the cache file, then don't even try */
+ if (((ret = stat(filename, &st)) < 0 && errno != ENOENT) ||
+ (ret == 0 && access(filename, W_OK) < 0)) {
+ DBG(DEBUG_SAVE,
+ printf("can't write to cache file %s\n", filename));
+ return 0;
+ }
+
+ /*
+ * Try and create a temporary file in the same directory so
+ * that in case of error we don't overwrite the cache file.
+ * If the cache file doesn't yet exist, it isn't a regular
+ * file (e.g. /dev/null or a socket), or we couldn't create
+ * a temporary file then we open it directly.
+ */
+ if (ret == 0 && S_ISREG(st.st_mode)) {
+ tmp = xmalloc(strlen(filename) + 8);
+ sprintf(tmp, "%s-XXXXXX", filename);
+ fd = mkstemp(tmp);
+ if (fd >= 0) {
+ file = fdopen(fd, "w");
+ opened = tmp;
+ }
+ fchmod(fd, 0644);
+ }
+
+ if (!file) {
+ file = fopen(filename, "w");
+ opened = filename;
+ }
+
+ DBG(DEBUG_SAVE,
+ printf("writing cache file %s (really %s)\n",
+ filename, opened));
+
+ if (!file) {
+ ret = errno;
+ goto errout;
+ }
+
+ list_for_each(p, &cache->bic_devs) {
+ blkid_dev dev = list_entry(p, struct blkid_struct_dev, bid_devs);
+ if (!dev->bid_type)
+ continue;
+ if ((ret = save_dev(dev, file)) < 0)
+ break;
+ }
+
+ if (ret >= 0) {
+ cache->bic_flags &= ~BLKID_BIC_FL_CHANGED;
+ ret = 1;
+ }
+
+ fclose(file);
+ if (opened != filename) {
+ if (ret < 0) {
+ unlink(opened);
+ DBG(DEBUG_SAVE,
+ printf("unlinked temp cache %s\n", opened));
+ } else {
+ char *backup;
+
+ backup = xmalloc(strlen(filename) + 5);
+ sprintf(backup, "%s.old", filename);
+ unlink(backup);
+ link(filename, backup);
+ free(backup);
+ rename(opened, filename);
+ DBG(DEBUG_SAVE,
+ printf("moved temp cache %s\n", opened));
+ }
+ }
+
+errout:
+ free(tmp);
+ return ret;
+}
+
+#ifdef TEST_PROGRAM
+int main(int argc, char **argv)
+{
+ blkid_cache cache = NULL;
+ int ret;
+
+ blkid_debug_mask = DEBUG_ALL;
+ if (argc > 2) {
+ fprintf(stderr, "Usage: %s [filename]\n"
+ "Test loading/saving a cache (filename)\n", argv[0]);
+ exit(1);
+ }
+
+ if ((ret = blkid_get_cache(&cache, bb_dev_null)) != 0) {
+ fprintf(stderr, "%s: error creating cache (%d)\n",
+ argv[0], ret);
+ exit(1);
+ }
+ if ((ret = blkid_probe_all(cache)) < 0) {
+ fprintf(stderr, "error (%d) probing devices\n", ret);
+ exit(1);
+ }
+ cache->bic_filename = blkid_strdup(argv[1]);
+
+ if ((ret = blkid_flush_cache(cache)) < 0) {
+ fprintf(stderr, "error (%d) saving cache\n", ret);
+ exit(1);
+ }
+
+ blkid_put_cache(cache);
+
+ return ret;
+}
+#endif
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/blkid/tag.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/blkid/tag.c
new file mode 100644
index 0000000..9e862a5
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/blkid/tag.c
@@ -0,0 +1,432 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * tag.c - allocation/initialization/free routines for tag structs
+ *
+ * Copyright (C) 2001 Andreas Dilger
+ * Copyright (C) 2003 Theodore Ts'o
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "blkidP.h"
+
+static blkid_tag blkid_new_tag(void)
+{
+ blkid_tag tag;
+
+ if (!(tag = (blkid_tag) calloc(1, sizeof(struct blkid_struct_tag))))
+ return NULL;
+
+ INIT_LIST_HEAD(&tag->bit_tags);
+ INIT_LIST_HEAD(&tag->bit_names);
+
+ return tag;
+}
+
+#ifdef CONFIG_BLKID_DEBUG
+void blkid_debug_dump_tag(blkid_tag tag)
+{
+ if (!tag) {
+ printf(" tag: NULL\n");
+ return;
+ }
+
+ printf(" tag: %s=\"%s\"\n", tag->bit_name, tag->bit_val);
+}
+#endif
+
+void blkid_free_tag(blkid_tag tag)
+{
+ if (!tag)
+ return;
+
+ DBG(DEBUG_TAG, printf(" freeing tag %s=%s\n", tag->bit_name,
+ tag->bit_val ? tag->bit_val : "(NULL)"));
+ DBG(DEBUG_TAG, blkid_debug_dump_tag(tag));
+
+ list_del(&tag->bit_tags); /* list of tags for this device */
+ list_del(&tag->bit_names); /* list of tags with this type */
+
+ free(tag->bit_name);
+ free(tag->bit_val);
+ free(tag);
+}
+
+/*
+ * Find the desired tag on a device. If value is NULL, then the
+ * first such tag is returned, otherwise return only exact tag if found.
+ */
+blkid_tag blkid_find_tag_dev(blkid_dev dev, const char *type)
+{
+ struct list_head *p;
+
+ if (!dev || !type)
+ return NULL;
+
+ list_for_each(p, &dev->bid_tags) {
+ blkid_tag tmp = list_entry(p, struct blkid_struct_tag,
+ bit_tags);
+
+ if (!strcmp(tmp->bit_name, type))
+ return tmp;
+ }
+ return NULL;
+}
+
+/*
+ * Find the desired tag type in the cache.
+ * We return the head tag for this tag type.
+ */
+static blkid_tag blkid_find_head_cache(blkid_cache cache, const char *type)
+{
+ blkid_tag head = NULL, tmp;
+ struct list_head *p;
+
+ if (!cache || !type)
+ return NULL;
+
+ list_for_each(p, &cache->bic_tags) {
+ tmp = list_entry(p, struct blkid_struct_tag, bit_tags);
+ if (!strcmp(tmp->bit_name, type)) {
+ DBG(DEBUG_TAG,
+ printf(" found cache tag head %s\n", type));
+ head = tmp;
+ break;
+ }
+ }
+ return head;
+}
+
+/*
+ * Set a tag on an existing device.
+ *
+ * If value is NULL, then delete the tagsfrom the device.
+ */
+int blkid_set_tag(blkid_dev dev, const char *name,
+ const char *value, const int vlength)
+{
+ blkid_tag t = 0, head = 0;
+ char *val = 0;
+
+ if (!dev || !name)
+ return -BLKID_ERR_PARAM;
+
+ if (!(val = blkid_strndup(value, vlength)) && value)
+ return -BLKID_ERR_MEM;
+ t = blkid_find_tag_dev(dev, name);
+ if (!value) {
+ blkid_free_tag(t);
+ } else if (t) {
+ if (!strcmp(t->bit_val, val)) {
+ /* Same thing, exit */
+ free(val);
+ return 0;
+ }
+ free(t->bit_val);
+ t->bit_val = val;
+ } else {
+ /* Existing tag not present, add to device */
+ if (!(t = blkid_new_tag()))
+ goto errout;
+ t->bit_name = blkid_strdup(name);
+ t->bit_val = val;
+ t->bit_dev = dev;
+
+ list_add_tail(&t->bit_tags, &dev->bid_tags);
+
+ if (dev->bid_cache) {
+ head = blkid_find_head_cache(dev->bid_cache,
+ t->bit_name);
+ if (!head) {
+ head = blkid_new_tag();
+ if (!head)
+ goto errout;
+
+ DBG(DEBUG_TAG,
+ printf(" creating new cache tag head %s\n", name));
+ head->bit_name = blkid_strdup(name);
+ if (!head->bit_name)
+ goto errout;
+ list_add_tail(&head->bit_tags,
+ &dev->bid_cache->bic_tags);
+ }
+ list_add_tail(&t->bit_names, &head->bit_names);
+ }
+ }
+
+ /* Link common tags directly to the device struct */
+ if (!strcmp(name, "TYPE"))
+ dev->bid_type = val;
+ else if (!strcmp(name, "LABEL"))
+ dev->bid_label = val;
+ else if (!strcmp(name, "UUID"))
+ dev->bid_uuid = val;
+
+ if (dev->bid_cache)
+ dev->bid_cache->bic_flags |= BLKID_BIC_FL_CHANGED;
+ return 0;
+
+errout:
+ blkid_free_tag(t);
+ if (!t)
+ free(val);
+ blkid_free_tag(head);
+ return -BLKID_ERR_MEM;
+}
+
+
+/*
+ * Parse a "NAME=value" string. This is slightly different than
+ * parse_token, because that will end an unquoted value at a space, while
+ * this will assume that an unquoted value is the rest of the token (e.g.
+ * if we are passed an already quoted string from the command-line we don't
+ * have to both quote and escape quote so that the quotes make it to
+ * us).
+ *
+ * Returns 0 on success, and -1 on failure.
+ */
+int blkid_parse_tag_string(const char *token, char **ret_type, char **ret_val)
+{
+ char *name, *value, *cp;
+
+ DBG(DEBUG_TAG, printf("trying to parse '%s' as a tag\n", token));
+
+ if (!token || !(cp = strchr(token, '=')))
+ return -1;
+
+ name = blkid_strdup(token);
+ if (!name)
+ return -1;
+ value = name + (cp - token);
+ *value++ = '\0';
+ if (*value == '"' || *value == '\'') {
+ char c = *value++;
+ if (!(cp = strrchr(value, c)))
+ goto errout; /* missing closing quote */
+ *cp = '\0';
+ }
+ value = blkid_strdup(value);
+ if (!value)
+ goto errout;
+
+ *ret_type = name;
+ *ret_val = value;
+
+ return 0;
+
+errout:
+ free(name);
+ return -1;
+}
+
+/*
+ * Tag iteration routines for the public libblkid interface.
+ *
+ * These routines do not expose the list.h implementation, which are a
+ * contamination of the namespace, and which force us to reveal far, far
+ * too much of our internal implemenation. I'm not convinced I want
+ * to keep list.h in the long term, anyway. It's fine for kernel
+ * programming, but performance is not the #1 priority for this
+ * library, and I really don't like the tradeoff of type-safety for
+ * performance for this application. [tytso:20030125.2007EST]
+ */
+
+/*
+ * This series of functions iterate over all tags in a device
+ */
+#define TAG_ITERATE_MAGIC 0x01a5284c
+
+struct blkid_struct_tag_iterate {
+ int magic;
+ blkid_dev dev;
+ struct list_head *p;
+};
+
+blkid_tag_iterate blkid_tag_iterate_begin(blkid_dev dev)
+{
+ blkid_tag_iterate iter;
+
+ iter = xmalloc(sizeof(struct blkid_struct_tag_iterate));
+ iter->magic = TAG_ITERATE_MAGIC;
+ iter->dev = dev;
+ iter->p = dev->bid_tags.next;
+ return iter;
+}
+
+/*
+ * Return 0 on success, -1 on error
+ */
+extern int blkid_tag_next(blkid_tag_iterate iter,
+ const char **type, const char **value)
+{
+ blkid_tag tag;
+
+ *type = 0;
+ *value = 0;
+ if (!iter || iter->magic != TAG_ITERATE_MAGIC ||
+ iter->p == &iter->dev->bid_tags)
+ return -1;
+ tag = list_entry(iter->p, struct blkid_struct_tag, bit_tags);
+ *type = tag->bit_name;
+ *value = tag->bit_val;
+ iter->p = iter->p->next;
+ return 0;
+}
+
+void blkid_tag_iterate_end(blkid_tag_iterate iter)
+{
+ if (!iter || iter->magic != TAG_ITERATE_MAGIC)
+ return;
+ iter->magic = 0;
+ free(iter);
+}
+
+/*
+ * This function returns a device which matches a particular
+ * type/value pair. If there is more than one device that matches the
+ * search specification, it returns the one with the highest priority
+ * value. This allows us to give preference to EVMS or LVM devices.
+ *
+ * XXX there should also be an interface which uses an iterator so we
+ * can get all of the devices which match a type/value search parameter.
+ */
+extern blkid_dev blkid_find_dev_with_tag(blkid_cache cache,
+ const char *type,
+ const char *value)
+{
+ blkid_tag head;
+ blkid_dev dev;
+ int pri;
+ struct list_head *p;
+
+ if (!cache || !type || !value)
+ return NULL;
+
+ blkid_read_cache(cache);
+
+ DBG(DEBUG_TAG, printf("looking for %s=%s in cache\n", type, value));
+
+try_again:
+ pri = -1;
+ dev = 0;
+ head = blkid_find_head_cache(cache, type);
+
+ if (head) {
+ list_for_each(p, &head->bit_names) {
+ blkid_tag tmp = list_entry(p, struct blkid_struct_tag,
+ bit_names);
+
+ if (!strcmp(tmp->bit_val, value) &&
+ tmp->bit_dev->bid_pri > pri) {
+ dev = tmp->bit_dev;
+ pri = dev->bid_pri;
+ }
+ }
+ }
+ if (dev && !(dev->bid_flags & BLKID_BID_FL_VERIFIED)) {
+ dev = blkid_verify(cache, dev);
+ if (dev && (dev->bid_flags & BLKID_BID_FL_VERIFIED))
+ goto try_again;
+ }
+
+ if (!dev && !(cache->bic_flags & BLKID_BIC_FL_PROBED)) {
+ if (blkid_probe_all(cache) < 0)
+ return NULL;
+ goto try_again;
+ }
+ return dev;
+}
+
+#ifdef TEST_PROGRAM
+#ifdef HAVE_GETOPT_H
+#include <getopt.h>
+#else
+extern char *optarg;
+extern int optind;
+#endif
+
+void usage(char *prog)
+{
+ fprintf(stderr, "Usage: %s [-f blkid_file] [-m debug_mask] device "
+ "[type value]\n",
+ prog);
+ fprintf(stderr, "\tList all tags for a device and exit\n", prog);
+ exit(1);
+}
+
+int main(int argc, char **argv)
+{
+ blkid_tag_iterate iter;
+ blkid_cache cache = NULL;
+ blkid_dev dev;
+ int c, ret, found;
+ int flags = BLKID_DEV_FIND;
+ char *tmp;
+ char *file = NULL;
+ char *devname = NULL;
+ char *search_type = NULL;
+ char *search_value = NULL;
+ const char *type, *value;
+
+ while ((c = getopt (argc, argv, "m:f:")) != EOF)
+ switch (c) {
+ case 'f':
+ file = optarg;
+ break;
+ case 'm':
+ blkid_debug_mask = strtoul (optarg, &tmp, 0);
+ if (*tmp) {
+ fprintf(stderr, "Invalid debug mask: %d\n",
+ optarg);
+ exit(1);
+ }
+ break;
+ case '?':
+ usage(argv[0]);
+ }
+ if (argc > optind)
+ devname = argv[optind++];
+ if (argc > optind)
+ search_type = argv[optind++];
+ if (argc > optind)
+ search_value = argv[optind++];
+ if (!devname || (argc != optind))
+ usage(argv[0]);
+
+ if ((ret = blkid_get_cache(&cache, file)) != 0) {
+ fprintf(stderr, "%s: error creating cache (%d)\n",
+ argv[0], ret);
+ exit(1);
+ }
+
+ dev = blkid_get_dev(cache, devname, flags);
+ if (!dev) {
+ fprintf(stderr, "%s: cannot find device in blkid cache\n");
+ exit(1);
+ }
+ if (search_type) {
+ found = blkid_dev_has_tag(dev, search_type, search_value);
+ printf("Device %s: (%s, %s) %s\n", blkid_dev_devname(dev),
+ search_type, search_value ? search_value : "NULL",
+ found ? "FOUND" : "NOT FOUND");
+ return !found;
+ }
+ printf("Device %s...\n", blkid_dev_devname(dev));
+
+ iter = blkid_tag_iterate_begin(dev);
+ while (blkid_tag_next(iter, &type, &value) == 0) {
+ printf("\tTag %s has value %s\n", type, value);
+ }
+ blkid_tag_iterate_end(iter);
+
+ blkid_put_cache(cache);
+ return 0;
+}
+#endif
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/chattr.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/chattr.c
new file mode 100644
index 0000000..8f62a5d
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/chattr.c
@@ -0,0 +1,220 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * chattr.c - Change file attributes on an ext2 file system
+ *
+ * Copyright (C) 1993, 1994 Remy Card <card@masi.ibp.fr>
+ * Laboratoire MASI, Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ * This file can be redistributed under the terms of the GNU General
+ * Public License
+ */
+
+/*
+ * History:
+ * 93/10/30 - Creation
+ * 93/11/13 - Replace stat() calls by lstat() to avoid loops
+ * 94/02/27 - Integrated in Ted's distribution
+ * 98/12/29 - Ignore symlinks when working recursively (G M Sipe)
+ * 98/12/29 - Display version info only when -V specified (G M Sipe)
+ */
+
+#include <sys/types.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include "ext2fs/ext2_fs.h"
+
+#ifdef __GNUC__
+# define EXT2FS_ATTR(x) __attribute__(x)
+#else
+# define EXT2FS_ATTR(x)
+#endif
+
+#include "e2fsbb.h"
+#include "e2p/e2p.h"
+
+#define OPT_ADD 1
+#define OPT_REM 2
+#define OPT_SET 4
+#define OPT_SET_VER 8
+static int flags;
+static int recursive;
+
+static unsigned long version;
+
+static unsigned long af;
+static unsigned long rf;
+static unsigned long sf;
+
+struct flags_char {
+ unsigned long flag;
+ char optchar;
+};
+
+static const struct flags_char flags_array[] = {
+ { EXT2_NOATIME_FL, 'A' },
+ { EXT2_SYNC_FL, 'S' },
+ { EXT2_DIRSYNC_FL, 'D' },
+ { EXT2_APPEND_FL, 'a' },
+ { EXT2_COMPR_FL, 'c' },
+ { EXT2_NODUMP_FL, 'd' },
+ { EXT2_IMMUTABLE_FL, 'i' },
+ { EXT3_JOURNAL_DATA_FL, 'j' },
+ { EXT2_SECRM_FL, 's' },
+ { EXT2_UNRM_FL, 'u' },
+ { EXT2_NOTAIL_FL, 't' },
+ { EXT2_TOPDIR_FL, 'T' },
+ { 0, 0 }
+};
+
+static unsigned long get_flag(char c)
+{
+ const struct flags_char *fp;
+ for (fp = flags_array; fp->flag; fp++)
+ if (fp->optchar == c)
+ return fp->flag;
+ bb_show_usage();
+ return 0;
+}
+
+static int decode_arg(char *arg)
+{
+ unsigned long *fl;
+ char opt = *arg++;
+
+ if (opt == '-') {
+ flags |= OPT_REM;
+ fl = &rf;
+ } else if (opt == '+') {
+ flags |= OPT_ADD;
+ fl = &af;
+ } else if (opt == '=') {
+ flags |= OPT_SET;
+ fl = &sf;
+ } else
+ return EOF;
+
+ for (; *arg ; ++arg)
+ (*fl) |= get_flag(*arg);
+
+ return 1;
+}
+
+static int chattr_dir_proc(const char *, struct dirent *, void *);
+
+static void change_attributes(const char * name)
+{
+ unsigned long fsflags;
+ struct stat st;
+
+ if (lstat(name, &st) == -1) {
+ bb_error_msg("stat %s failed", name);
+ return;
+ }
+ if (S_ISLNK(st.st_mode) && recursive)
+ return;
+
+ /* Don't try to open device files, fifos etc. We probably
+ * ought to display an error if the file was explicitly given
+ * on the command line (whether or not recursive was
+ * requested). */
+ if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode) && !S_ISDIR(st.st_mode))
+ return;
+
+ if (flags & OPT_SET_VER)
+ if (fsetversion(name, version) == -1)
+ bb_error_msg("setting version on %s", name);
+
+ if (flags & OPT_SET) {
+ fsflags = sf;
+ } else {
+ if (fgetflags(name, &fsflags) == -1) {
+ bb_error_msg("reading flags on %s", name);
+ goto skip_setflags;
+ }
+ if (flags & OPT_REM)
+ fsflags &= ~rf;
+ if (flags & OPT_ADD)
+ fsflags |= af;
+ if (!S_ISDIR(st.st_mode))
+ fsflags &= ~EXT2_DIRSYNC_FL;
+ }
+ if (fsetflags(name, fsflags) == -1)
+ bb_error_msg("setting flags on %s", name);
+
+skip_setflags:
+ if (S_ISDIR(st.st_mode) && recursive)
+ iterate_on_dir(name, chattr_dir_proc, NULL);
+}
+
+static int chattr_dir_proc(const char *dir_name, struct dirent *de,
+ void *private EXT2FS_ATTR((unused)))
+{
+ /*if (strcmp(de->d_name, ".") || strcmp(de->d_name, "..")) {*/
+ if (de->d_name[0] == '.'
+ && (!de->d_name[1] || (de->d_name[1] == '.' && !de->d_name[2]))
+ ) {
+ char *path = concat_subpath_file(dir_name, de->d_name);
+ if (path) {
+ change_attributes(path);
+ free(path);
+ }
+ }
+ return 0;
+}
+
+int chattr_main(int argc, char **argv);
+int chattr_main(int argc, char **argv)
+{
+ int i;
+ char *arg;
+
+ /* parse the args */
+ for (i = 1; i < argc; ++i) {
+ arg = argv[i];
+
+ /* take care of -R and -v <version> */
+ if (arg[0] == '-') {
+ if (arg[1] == 'R' && arg[2] == '\0') {
+ recursive = 1;
+ continue;
+ } else if (arg[1] == 'v' && arg[2] == '\0') {
+ char *tmp;
+ ++i;
+ if (i >= argc)
+ bb_show_usage();
+ version = strtol(argv[i], &tmp, 0);
+ if (*tmp)
+ bb_error_msg_and_die("bad version '%s'", arg);
+ flags |= OPT_SET_VER;
+ continue;
+ }
+ }
+
+ if (decode_arg(arg) == EOF)
+ break;
+ }
+
+ /* run sanity checks on all the arguments given us */
+ if (i >= argc)
+ bb_show_usage();
+ if ((flags & OPT_SET) && ((flags & OPT_ADD) || (flags & OPT_REM)))
+ bb_error_msg_and_die("= is incompatible with - and +");
+ if ((rf & af) != 0)
+ bb_error_msg_and_die("Can't set and unset a flag");
+ if (!flags)
+ bb_error_msg_and_die("Must use '-v', =, - or +");
+
+ /* now run chattr on all the files passed to us */
+ while (i < argc)
+ change_attributes(argv[i++]);
+
+ return EXIT_SUCCESS;
+}
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2fsbb.h b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2fsbb.h
new file mode 100644
index 0000000..78e7cbd
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2fsbb.h
@@ -0,0 +1,43 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * File: e2fsbb.h
+ *
+ * Redefine a bunch of e2fsprogs stuff to use busybox routines
+ * instead. This makes upgrade between e2fsprogs versions easy.
+ */
+
+#ifndef __E2FSBB_H__
+#define __E2FSBB_H__ 1
+
+#include "libbb.h"
+
+/* version we've last synced against */
+#define E2FSPROGS_VERSION "1.38"
+#define E2FSPROGS_DATE "30-Jun-2005"
+
+typedef long errcode_t;
+#define ERRCODE_RANGE 8
+#define error_message(code) strerror((int) (code & ((1<<ERRCODE_RANGE)-1)))
+
+/* header defines */
+#define ENABLE_HTREE 1
+#define HAVE_ERRNO_H 1
+#define HAVE_EXT2_IOCTLS 1
+#define HAVE_LINUX_FD_H 1
+#define HAVE_MNTENT_H 1
+#define HAVE_NETINET_IN_H 1
+#define HAVE_NET_IF_H 1
+#define HAVE_SYS_IOCTL_H 1
+#define HAVE_SYS_MOUNT_H 1
+#define HAVE_SYS_QUEUE_H 1
+#define HAVE_SYS_STAT_H 1
+#define HAVE_SYS_TYPES_H 1
+#define HAVE_UNISTD_H 1
+
+/* Endianness */
+#if BB_BIG_ENDIAN
+#define ENABLE_SWAPFS 1
+#define WORDS_BIGENDIAN 1
+#endif
+
+#endif /* __E2FSBB_H__ */
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2fsck.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2fsck.c
new file mode 100644
index 0000000..a0f6916
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2fsck.c
@@ -0,0 +1,13548 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * e2fsck
+ *
+ * Copyright (C) 1993, 1994, 1995, 1996, 1997 Theodore Ts'o.
+ * Copyright (C) 2006 Garrett Kajmowicz
+ *
+ * Dictionary Abstract Data Type
+ * Copyright (C) 1997 Kaz Kylheku <kaz@ashi.footprints.net>
+ * Free Software License:
+ * All rights are reserved by the author, with the following exceptions:
+ * Permission is granted to freely reproduce and distribute this software,
+ * possibly in exchange for a fee, provided that this copyright notice appears
+ * intact. Permission is also granted to adapt this software to produce
+ * derivative works, as long as the modified versions carry this copyright
+ * notice and additional notices stating that the work has been modified.
+ * This source code may be translated into executable form and incorporated
+ * into proprietary software; there is no requirement for such software to
+ * contain a copyright notice related to this source.
+ *
+ * linux/fs/recovery and linux/fs/revoke
+ * Written by Stephen C. Tweedie <sct@redhat.com>, 1999
+ *
+ * Copyright 1999-2000 Red Hat Software --- All Rights Reserved
+ *
+ * Journal recovery routines for the generic filesystem journaling code;
+ * part of the ext2fs journaling system.
+ *
+ * Licensed under GPLv2 or later, see file License in this tarball for details.
+ */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE 1 /* get strnlen() */
+#endif
+
+#include "e2fsck.h" /*Put all of our defines here to clean things up*/
+
+#define _(x) x
+#define N_(x) x
+
+/*
+ * Procedure declarations
+ */
+
+static void e2fsck_pass1_dupblocks(e2fsck_t ctx, char *block_buf);
+
+/* pass1.c */
+static void e2fsck_use_inode_shortcuts(e2fsck_t ctx, int bool);
+
+/* pass2.c */
+static int e2fsck_process_bad_inode(e2fsck_t ctx, ext2_ino_t dir,
+ ext2_ino_t ino, char *buf);
+
+/* pass3.c */
+static int e2fsck_reconnect_file(e2fsck_t ctx, ext2_ino_t inode);
+static errcode_t e2fsck_expand_directory(e2fsck_t ctx, ext2_ino_t dir,
+ int num, int gauranteed_size);
+static ext2_ino_t e2fsck_get_lost_and_found(e2fsck_t ctx, int fix);
+static errcode_t e2fsck_adjust_inode_count(e2fsck_t ctx, ext2_ino_t ino,
+ int adj);
+
+/* rehash.c */
+static void e2fsck_rehash_directories(e2fsck_t ctx);
+
+/* util.c */
+static void *e2fsck_allocate_memory(e2fsck_t ctx, unsigned int size,
+ const char *description);
+static int ask(e2fsck_t ctx, const char * string, int def);
+static void e2fsck_read_bitmaps(e2fsck_t ctx);
+static void preenhalt(e2fsck_t ctx);
+static void e2fsck_read_inode(e2fsck_t ctx, unsigned long ino,
+ struct ext2_inode * inode, const char * proc);
+static void e2fsck_write_inode(e2fsck_t ctx, unsigned long ino,
+ struct ext2_inode * inode, const char * proc);
+static blk_t get_backup_sb(e2fsck_t ctx, ext2_filsys fs,
+ const char *name, io_manager manager);
+
+/* unix.c */
+static void e2fsck_clear_progbar(e2fsck_t ctx);
+static int e2fsck_simple_progress(e2fsck_t ctx, const char *label,
+ float percent, unsigned int dpynum);
+
+
+/*
+ * problem.h --- e2fsck problem error codes
+ */
+
+typedef __u32 problem_t;
+
+struct problem_context {
+ errcode_t errcode;
+ ext2_ino_t ino, ino2, dir;
+ struct ext2_inode *inode;
+ struct ext2_dir_entry *dirent;
+ blk_t blk, blk2;
+ e2_blkcnt_t blkcount;
+ int group;
+ __u64 num;
+ const char *str;
+};
+
+
+/*
+ * Function declarations
+ */
+static int fix_problem(e2fsck_t ctx, problem_t code, struct problem_context *pctx);
+static int end_problem_latch(e2fsck_t ctx, int mask);
+static int set_latch_flags(int mask, int setflags, int clearflags);
+static void clear_problem_context(struct problem_context *ctx);
+
+/*
+ * Dictionary Abstract Data Type
+ * Copyright (C) 1997 Kaz Kylheku <kaz@ashi.footprints.net>
+ *
+ * dict.h v 1.22.2.6 2000/11/13 01:36:44 kaz
+ * kazlib_1_20
+ */
+
+#ifndef DICT_H
+#define DICT_H
+
+/*
+ * Blurb for inclusion into C++ translation units
+ */
+
+typedef unsigned long dictcount_t;
+#define DICTCOUNT_T_MAX ULONG_MAX
+
+/*
+ * The dictionary is implemented as a red-black tree
+ */
+
+typedef enum { dnode_red, dnode_black } dnode_color_t;
+
+typedef struct dnode_t {
+ struct dnode_t *dict_left;
+ struct dnode_t *dict_right;
+ struct dnode_t *dict_parent;
+ dnode_color_t dict_color;
+ const void *dict_key;
+ void *dict_data;
+} dnode_t;
+
+typedef int (*dict_comp_t)(const void *, const void *);
+typedef void (*dnode_free_t)(dnode_t *);
+
+typedef struct dict_t {
+ dnode_t dict_nilnode;
+ dictcount_t dict_nodecount;
+ dictcount_t dict_maxcount;
+ dict_comp_t dict_compare;
+ dnode_free_t dict_freenode;
+ int dict_dupes;
+} dict_t;
+
+typedef void (*dnode_process_t)(dict_t *, dnode_t *, void *);
+
+typedef struct dict_load_t {
+ dict_t *dict_dictptr;
+ dnode_t dict_nilnode;
+} dict_load_t;
+
+#define dict_count(D) ((D)->dict_nodecount)
+#define dnode_get(N) ((N)->dict_data)
+#define dnode_getkey(N) ((N)->dict_key)
+
+#endif
+
+/*
+ * Compatibility header file for e2fsck which should be included
+ * instead of linux/jfs.h
+ *
+ * Copyright (C) 2000 Stephen C. Tweedie
+ */
+
+/*
+ * Pull in the definition of the e2fsck context structure
+ */
+
+struct buffer_head {
+ char b_data[8192];
+ e2fsck_t b_ctx;
+ io_channel b_io;
+ int b_size;
+ blk_t b_blocknr;
+ int b_dirty;
+ int b_uptodate;
+ int b_err;
+};
+
+
+#define K_DEV_FS 1
+#define K_DEV_JOURNAL 2
+
+#define lock_buffer(bh) do {} while(0)
+#define unlock_buffer(bh) do {} while(0)
+#define buffer_req(bh) 1
+#define do_readahead(journal, start) do {} while(0)
+
+static e2fsck_t e2fsck_global_ctx; /* Try your very best not to use this! */
+
+typedef struct {
+ int object_length;
+} kmem_cache_t;
+
+#define kmem_cache_alloc(cache,flags) malloc((cache)->object_length)
+
+/*
+ * We use the standard libext2fs portability tricks for inline
+ * functions.
+ */
+
+static kmem_cache_t * do_cache_create(int len)
+{
+ kmem_cache_t *new_cache;
+
+ new_cache = malloc(sizeof(*new_cache));
+ if (new_cache)
+ new_cache->object_length = len;
+ return new_cache;
+}
+
+static void do_cache_destroy(kmem_cache_t *cache)
+{
+ free(cache);
+}
+
+
+/*
+ * Dictionary Abstract Data Type
+ */
+
+
+/*
+ * These macros provide short convenient names for structure members,
+ * which are embellished with dict_ prefixes so that they are
+ * properly confined to the documented namespace. It's legal for a
+ * program which uses dict to define, for instance, a macro called ``parent''.
+ * Such a macro would interfere with the dnode_t struct definition.
+ * In general, highly portable and reusable C modules which expose their
+ * structures need to confine structure member names to well-defined spaces.
+ * The resulting identifiers aren't necessarily convenient to use, nor
+ * readable, in the implementation, however!
+ */
+
+#define left dict_left
+#define right dict_right
+#define parent dict_parent
+#define color dict_color
+#define key dict_key
+#define data dict_data
+
+#define nilnode dict_nilnode
+#define maxcount dict_maxcount
+#define compare dict_compare
+#define dupes dict_dupes
+
+#define dict_root(D) ((D)->nilnode.left)
+#define dict_nil(D) (&(D)->nilnode)
+
+static void dnode_free(dnode_t *node);
+
+/*
+ * Perform a ``left rotation'' adjustment on the tree. The given node P and
+ * its right child C are rearranged so that the P instead becomes the left
+ * child of C. The left subtree of C is inherited as the new right subtree
+ * for P. The ordering of the keys within the tree is thus preserved.
+ */
+
+static void rotate_left(dnode_t *upper)
+{
+ dnode_t *lower, *lowleft, *upparent;
+
+ lower = upper->right;
+ upper->right = lowleft = lower->left;
+ lowleft->parent = upper;
+
+ lower->parent = upparent = upper->parent;
+
+ /* don't need to check for root node here because root->parent is
+ the sentinel nil node, and root->parent->left points back to root */
+
+ if (upper == upparent->left) {
+ upparent->left = lower;
+ } else {
+ assert (upper == upparent->right);
+ upparent->right = lower;
+ }
+
+ lower->left = upper;
+ upper->parent = lower;
+}
+
+/*
+ * This operation is the ``mirror'' image of rotate_left. It is
+ * the same procedure, but with left and right interchanged.
+ */
+
+static void rotate_right(dnode_t *upper)
+{
+ dnode_t *lower, *lowright, *upparent;
+
+ lower = upper->left;
+ upper->left = lowright = lower->right;
+ lowright->parent = upper;
+
+ lower->parent = upparent = upper->parent;
+
+ if (upper == upparent->right) {
+ upparent->right = lower;
+ } else {
+ assert (upper == upparent->left);
+ upparent->left = lower;
+ }
+
+ lower->right = upper;
+ upper->parent = lower;
+}
+
+/*
+ * Do a postorder traversal of the tree rooted at the specified
+ * node and free everything under it. Used by dict_free().
+ */
+
+static void free_nodes(dict_t *dict, dnode_t *node, dnode_t *nil)
+{
+ if (node == nil)
+ return;
+ free_nodes(dict, node->left, nil);
+ free_nodes(dict, node->right, nil);
+ dict->dict_freenode(node);
+}
+
+/*
+ * Verify that the tree contains the given node. This is done by
+ * traversing all of the nodes and comparing their pointers to the
+ * given pointer. Returns 1 if the node is found, otherwise
+ * returns zero. It is intended for debugging purposes.
+ */
+
+static int verify_dict_has_node(dnode_t *nil, dnode_t *root, dnode_t *node)
+{
+ if (root != nil) {
+ return root == node
+ || verify_dict_has_node(nil, root->left, node)
+ || verify_dict_has_node(nil, root->right, node);
+ }
+ return 0;
+}
+
+
+/*
+ * Select a different set of node allocator routines.
+ */
+
+static void dict_set_allocator(dict_t *dict, dnode_free_t fr)
+{
+ assert (dict_count(dict) == 0);
+ dict->dict_freenode = fr;
+}
+
+/*
+ * Free all the nodes in the dictionary by using the dictionary's
+ * installed free routine. The dictionary is emptied.
+ */
+
+static void dict_free_nodes(dict_t *dict)
+{
+ dnode_t *nil = dict_nil(dict), *root = dict_root(dict);
+ free_nodes(dict, root, nil);
+ dict->dict_nodecount = 0;
+ dict->nilnode.left = &dict->nilnode;
+ dict->nilnode.right = &dict->nilnode;
+}
+
+/*
+ * Initialize a user-supplied dictionary object.
+ */
+
+static dict_t *dict_init(dict_t *dict, dictcount_t maxcount, dict_comp_t comp)
+{
+ dict->compare = comp;
+ dict->dict_freenode = dnode_free;
+ dict->dict_nodecount = 0;
+ dict->maxcount = maxcount;
+ dict->nilnode.left = &dict->nilnode;
+ dict->nilnode.right = &dict->nilnode;
+ dict->nilnode.parent = &dict->nilnode;
+ dict->nilnode.color = dnode_black;
+ dict->dupes = 0;
+ return dict;
+}
+
+/*
+ * Locate a node in the dictionary having the given key.
+ * If the node is not found, a null a pointer is returned (rather than
+ * a pointer that dictionary's nil sentinel node), otherwise a pointer to the
+ * located node is returned.
+ */
+
+static dnode_t *dict_lookup(dict_t *dict, const void *key)
+{
+ dnode_t *root = dict_root(dict);
+ dnode_t *nil = dict_nil(dict);
+ dnode_t *saved;
+ int result;
+
+ /* simple binary search adapted for trees that contain duplicate keys */
+
+ while (root != nil) {
+ result = dict->compare(key, root->key);
+ if (result < 0)
+ root = root->left;
+ else if (result > 0)
+ root = root->right;
+ else {
+ if (!dict->dupes) { /* no duplicates, return match */
+ return root;
+ } else { /* could be dupes, find leftmost one */
+ do {
+ saved = root;
+ root = root->left;
+ while (root != nil && dict->compare(key, root->key))
+ root = root->right;
+ } while (root != nil);
+ return saved;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * Insert a node into the dictionary. The node should have been
+ * initialized with a data field. All other fields are ignored.
+ * The behavior is undefined if the user attempts to insert into
+ * a dictionary that is already full (for which the dict_isfull()
+ * function returns true).
+ */
+
+static void dict_insert(dict_t *dict, dnode_t *node, const void *key)
+{
+ dnode_t *where = dict_root(dict), *nil = dict_nil(dict);
+ dnode_t *parent = nil, *uncle, *grandpa;
+ int result = -1;
+
+ node->key = key;
+
+ /* basic binary tree insert */
+
+ while (where != nil) {
+ parent = where;
+ result = dict->compare(key, where->key);
+ /* trap attempts at duplicate key insertion unless it's explicitly allowed */
+ assert (dict->dupes || result != 0);
+ if (result < 0)
+ where = where->left;
+ else
+ where = where->right;
+ }
+
+ assert (where == nil);
+
+ if (result < 0)
+ parent->left = node;
+ else
+ parent->right = node;
+
+ node->parent = parent;
+ node->left = nil;
+ node->right = nil;
+
+ dict->dict_nodecount++;
+
+ /* red black adjustments */
+
+ node->color = dnode_red;
+
+ while (parent->color == dnode_red) {
+ grandpa = parent->parent;
+ if (parent == grandpa->left) {
+ uncle = grandpa->right;
+ if (uncle->color == dnode_red) { /* red parent, red uncle */
+ parent->color = dnode_black;
+ uncle->color = dnode_black;
+ grandpa->color = dnode_red;
+ node = grandpa;
+ parent = grandpa->parent;
+ } else { /* red parent, black uncle */
+ if (node == parent->right) {
+ rotate_left(parent);
+ parent = node;
+ assert (grandpa == parent->parent);
+ /* rotation between parent and child preserves grandpa */
+ }
+ parent->color = dnode_black;
+ grandpa->color = dnode_red;
+ rotate_right(grandpa);
+ break;
+ }
+ } else { /* symmetric cases: parent == parent->parent->right */
+ uncle = grandpa->left;
+ if (uncle->color == dnode_red) {
+ parent->color = dnode_black;
+ uncle->color = dnode_black;
+ grandpa->color = dnode_red;
+ node = grandpa;
+ parent = grandpa->parent;
+ } else {
+ if (node == parent->left) {
+ rotate_right(parent);
+ parent = node;
+ assert (grandpa == parent->parent);
+ }
+ parent->color = dnode_black;
+ grandpa->color = dnode_red;
+ rotate_left(grandpa);
+ break;
+ }
+ }
+ }
+
+ dict_root(dict)->color = dnode_black;
+
+}
+
+/*
+ * Allocate a node using the dictionary's allocator routine, give it
+ * the data item.
+ */
+
+static dnode_t *dnode_init(dnode_t *dnode, void *data)
+{
+ dnode->data = data;
+ dnode->parent = NULL;
+ dnode->left = NULL;
+ dnode->right = NULL;
+ return dnode;
+}
+
+static int dict_alloc_insert(dict_t *dict, const void *key, void *data)
+{
+ dnode_t *node = malloc(sizeof(dnode_t));
+
+ if (node) {
+ dnode_init(node, data);
+ dict_insert(dict, node, key);
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Return the node with the lowest (leftmost) key. If the dictionary is empty
+ * (that is, dict_isempty(dict) returns 1) a null pointer is returned.
+ */
+
+static dnode_t *dict_first(dict_t *dict)
+{
+ dnode_t *nil = dict_nil(dict), *root = dict_root(dict), *left;
+
+ if (root != nil)
+ while ((left = root->left) != nil)
+ root = left;
+
+ return (root == nil) ? NULL : root;
+}
+
+/*
+ * Return the given node's successor node---the node which has the
+ * next key in the the left to right ordering. If the node has
+ * no successor, a null pointer is returned rather than a pointer to
+ * the nil node.
+ */
+
+static dnode_t *dict_next(dict_t *dict, dnode_t *curr)
+{
+ dnode_t *nil = dict_nil(dict), *parent, *left;
+
+ if (curr->right != nil) {
+ curr = curr->right;
+ while ((left = curr->left) != nil)
+ curr = left;
+ return curr;
+ }
+
+ parent = curr->parent;
+
+ while (parent != nil && curr == parent->right) {
+ curr = parent;
+ parent = curr->parent;
+ }
+
+ return (parent == nil) ? NULL : parent;
+}
+
+
+static void dnode_free(dnode_t *node)
+{
+ free(node);
+}
+
+
+#undef left
+#undef right
+#undef parent
+#undef color
+#undef key
+#undef data
+
+#undef nilnode
+#undef maxcount
+#undef compare
+#undef dupes
+
+
+/*
+ * dirinfo.c --- maintains the directory information table for e2fsck.
+ */
+
+/*
+ * This subroutine is called during pass1 to create a directory info
+ * entry. During pass1, the passed-in parent is 0; it will get filled
+ * in during pass2.
+ */
+static void e2fsck_add_dir_info(e2fsck_t ctx, ext2_ino_t ino, ext2_ino_t parent)
+{
+ struct dir_info *dir;
+ int i, j;
+ ext2_ino_t num_dirs;
+ errcode_t retval;
+ unsigned long old_size;
+
+ if (!ctx->dir_info) {
+ ctx->dir_info_count = 0;
+ retval = ext2fs_get_num_dirs(ctx->fs, &num_dirs);
+ if (retval)
+ num_dirs = 1024; /* Guess */
+ ctx->dir_info_size = num_dirs + 10;
+ ctx->dir_info = (struct dir_info *)
+ e2fsck_allocate_memory(ctx, ctx->dir_info_size
+ * sizeof (struct dir_info),
+ "directory map");
+ }
+
+ if (ctx->dir_info_count >= ctx->dir_info_size) {
+ old_size = ctx->dir_info_size * sizeof(struct dir_info);
+ ctx->dir_info_size += 10;
+ retval = ext2fs_resize_mem(old_size, ctx->dir_info_size *
+ sizeof(struct dir_info),
+ &ctx->dir_info);
+ if (retval) {
+ ctx->dir_info_size -= 10;
+ return;
+ }
+ }
+
+ /*
+ * Normally, add_dir_info is called with each inode in
+ * sequential order; but once in a while (like when pass 3
+ * needs to recreate the root directory or lost+found
+ * directory) it is called out of order. In those cases, we
+ * need to move the dir_info entries down to make room, since
+ * the dir_info array needs to be sorted by inode number for
+ * get_dir_info()'s sake.
+ */
+ if (ctx->dir_info_count &&
+ ctx->dir_info[ctx->dir_info_count-1].ino >= ino) {
+ for (i = ctx->dir_info_count-1; i > 0; i--)
+ if (ctx->dir_info[i-1].ino < ino)
+ break;
+ dir = &ctx->dir_info[i];
+ if (dir->ino != ino)
+ for (j = ctx->dir_info_count++; j > i; j--)
+ ctx->dir_info[j] = ctx->dir_info[j-1];
+ } else
+ dir = &ctx->dir_info[ctx->dir_info_count++];
+
+ dir->ino = ino;
+ dir->dotdot = parent;
+ dir->parent = parent;
+}
+
+/*
+ * get_dir_info() --- given an inode number, try to find the directory
+ * information entry for it.
+ */
+static struct dir_info *e2fsck_get_dir_info(e2fsck_t ctx, ext2_ino_t ino)
+{
+ int low, high, mid;
+
+ low = 0;
+ high = ctx->dir_info_count-1;
+ if (!ctx->dir_info)
+ return 0;
+ if (ino == ctx->dir_info[low].ino)
+ return &ctx->dir_info[low];
+ if (ino == ctx->dir_info[high].ino)
+ return &ctx->dir_info[high];
+
+ while (low < high) {
+ mid = (low+high)/2;
+ if (mid == low || mid == high)
+ break;
+ if (ino == ctx->dir_info[mid].ino)
+ return &ctx->dir_info[mid];
+ if (ino < ctx->dir_info[mid].ino)
+ high = mid;
+ else
+ low = mid;
+ }
+ return 0;
+}
+
+/*
+ * Free the dir_info structure when it isn't needed any more.
+ */
+static void e2fsck_free_dir_info(e2fsck_t ctx)
+{
+ ext2fs_free_mem(&ctx->dir_info);
+ ctx->dir_info_size = 0;
+ ctx->dir_info_count = 0;
+}
+
+/*
+ * Return the count of number of directories in the dir_info structure
+ */
+static int e2fsck_get_num_dirinfo(e2fsck_t ctx)
+{
+ return ctx->dir_info_count;
+}
+
+/*
+ * A simple interator function
+ */
+static struct dir_info *e2fsck_dir_info_iter(e2fsck_t ctx, int *control)
+{
+ if (*control >= ctx->dir_info_count)
+ return 0;
+
+ return ctx->dir_info + (*control)++;
+}
+
+/*
+ * dirinfo.c --- maintains the directory information table for e2fsck.
+ *
+ */
+
+#ifdef ENABLE_HTREE
+
+/*
+ * This subroutine is called during pass1 to create a directory info
+ * entry. During pass1, the passed-in parent is 0; it will get filled
+ * in during pass2.
+ */
+static void e2fsck_add_dx_dir(e2fsck_t ctx, ext2_ino_t ino, int num_blocks)
+{
+ struct dx_dir_info *dir;
+ int i, j;
+ errcode_t retval;
+ unsigned long old_size;
+
+ if (!ctx->dx_dir_info) {
+ ctx->dx_dir_info_count = 0;
+ ctx->dx_dir_info_size = 100; /* Guess */
+ ctx->dx_dir_info = (struct dx_dir_info *)
+ e2fsck_allocate_memory(ctx, ctx->dx_dir_info_size
+ * sizeof (struct dx_dir_info),
+ "directory map");
+ }
+
+ if (ctx->dx_dir_info_count >= ctx->dx_dir_info_size) {
+ old_size = ctx->dx_dir_info_size * sizeof(struct dx_dir_info);
+ ctx->dx_dir_info_size += 10;
+ retval = ext2fs_resize_mem(old_size, ctx->dx_dir_info_size *
+ sizeof(struct dx_dir_info),
+ &ctx->dx_dir_info);
+ if (retval) {
+ ctx->dx_dir_info_size -= 10;
+ return;
+ }
+ }
+
+ /*
+ * Normally, add_dx_dir_info is called with each inode in
+ * sequential order; but once in a while (like when pass 3
+ * needs to recreate the root directory or lost+found
+ * directory) it is called out of order. In those cases, we
+ * need to move the dx_dir_info entries down to make room, since
+ * the dx_dir_info array needs to be sorted by inode number for
+ * get_dx_dir_info()'s sake.
+ */
+ if (ctx->dx_dir_info_count &&
+ ctx->dx_dir_info[ctx->dx_dir_info_count-1].ino >= ino) {
+ for (i = ctx->dx_dir_info_count-1; i > 0; i--)
+ if (ctx->dx_dir_info[i-1].ino < ino)
+ break;
+ dir = &ctx->dx_dir_info[i];
+ if (dir->ino != ino)
+ for (j = ctx->dx_dir_info_count++; j > i; j--)
+ ctx->dx_dir_info[j] = ctx->dx_dir_info[j-1];
+ } else
+ dir = &ctx->dx_dir_info[ctx->dx_dir_info_count++];
+
+ dir->ino = ino;
+ dir->numblocks = num_blocks;
+ dir->hashversion = 0;
+ dir->dx_block = e2fsck_allocate_memory(ctx, num_blocks
+ * sizeof (struct dx_dirblock_info),
+ "dx_block info array");
+
+}
+
+/*
+ * get_dx_dir_info() --- given an inode number, try to find the directory
+ * information entry for it.
+ */
+static struct dx_dir_info *e2fsck_get_dx_dir_info(e2fsck_t ctx, ext2_ino_t ino)
+{
+ int low, high, mid;
+
+ low = 0;
+ high = ctx->dx_dir_info_count-1;
+ if (!ctx->dx_dir_info)
+ return 0;
+ if (ino == ctx->dx_dir_info[low].ino)
+ return &ctx->dx_dir_info[low];
+ if (ino == ctx->dx_dir_info[high].ino)
+ return &ctx->dx_dir_info[high];
+
+ while (low < high) {
+ mid = (low+high)/2;
+ if (mid == low || mid == high)
+ break;
+ if (ino == ctx->dx_dir_info[mid].ino)
+ return &ctx->dx_dir_info[mid];
+ if (ino < ctx->dx_dir_info[mid].ino)
+ high = mid;
+ else
+ low = mid;
+ }
+ return 0;
+}
+
+/*
+ * Free the dx_dir_info structure when it isn't needed any more.
+ */
+static void e2fsck_free_dx_dir_info(e2fsck_t ctx)
+{
+ int i;
+ struct dx_dir_info *dir;
+
+ if (ctx->dx_dir_info) {
+ dir = ctx->dx_dir_info;
+ for (i=0; i < ctx->dx_dir_info_count; i++) {
+ ext2fs_free_mem(&dir->dx_block);
+ }
+ ext2fs_free_mem(&ctx->dx_dir_info);
+ }
+ ctx->dx_dir_info_size = 0;
+ ctx->dx_dir_info_count = 0;
+}
+
+/*
+ * A simple interator function
+ */
+static struct dx_dir_info *e2fsck_dx_dir_info_iter(e2fsck_t ctx, int *control)
+{
+ if (*control >= ctx->dx_dir_info_count)
+ return 0;
+
+ return ctx->dx_dir_info + (*control)++;
+}
+
+#endif /* ENABLE_HTREE */
+/*
+ * e2fsck.c - a consistency checker for the new extended file system.
+ *
+ */
+
+/*
+ * This function allocates an e2fsck context
+ */
+static errcode_t e2fsck_allocate_context(e2fsck_t *ret)
+{
+ e2fsck_t context;
+ errcode_t retval;
+
+ retval = ext2fs_get_mem(sizeof(struct e2fsck_struct), &context);
+ if (retval)
+ return retval;
+
+ memset(context, 0, sizeof(struct e2fsck_struct));
+
+ context->process_inode_size = 256;
+ context->ext_attr_ver = 2;
+
+ *ret = context;
+ return 0;
+}
+
+struct ea_refcount_el {
+ blk_t ea_blk;
+ int ea_count;
+};
+
+struct ea_refcount {
+ blk_t count;
+ blk_t size;
+ blk_t cursor;
+ struct ea_refcount_el *list;
+};
+
+static void ea_refcount_free(ext2_refcount_t refcount)
+{
+ if (!refcount)
+ return;
+
+ ext2fs_free_mem(&refcount->list);
+ ext2fs_free_mem(&refcount);
+}
+
+/*
+ * This function resets an e2fsck context; it is called when e2fsck
+ * needs to be restarted.
+ */
+static errcode_t e2fsck_reset_context(e2fsck_t ctx)
+{
+ ctx->flags = 0;
+ ctx->lost_and_found = 0;
+ ctx->bad_lost_and_found = 0;
+ ext2fs_free_inode_bitmap(ctx->inode_used_map);
+ ctx->inode_used_map = 0;
+ ext2fs_free_inode_bitmap(ctx->inode_dir_map);
+ ctx->inode_dir_map = 0;
+ ext2fs_free_inode_bitmap(ctx->inode_reg_map);
+ ctx->inode_reg_map = 0;
+ ext2fs_free_block_bitmap(ctx->block_found_map);
+ ctx->block_found_map = 0;
+ ext2fs_free_icount(ctx->inode_link_info);
+ ctx->inode_link_info = 0;
+ if (ctx->journal_io) {
+ if (ctx->fs && ctx->fs->io != ctx->journal_io)
+ io_channel_close(ctx->journal_io);
+ ctx->journal_io = 0;
+ }
+ if (ctx->fs) {
+ ext2fs_free_dblist(ctx->fs->dblist);
+ ctx->fs->dblist = 0;
+ }
+ e2fsck_free_dir_info(ctx);
+#ifdef ENABLE_HTREE
+ e2fsck_free_dx_dir_info(ctx);
+#endif
+ ea_refcount_free(ctx->refcount);
+ ctx->refcount = 0;
+ ea_refcount_free(ctx->refcount_extra);
+ ctx->refcount_extra = 0;
+ ext2fs_free_block_bitmap(ctx->block_dup_map);
+ ctx->block_dup_map = 0;
+ ext2fs_free_block_bitmap(ctx->block_ea_map);
+ ctx->block_ea_map = 0;
+ ext2fs_free_inode_bitmap(ctx->inode_bad_map);
+ ctx->inode_bad_map = 0;
+ ext2fs_free_inode_bitmap(ctx->inode_imagic_map);
+ ctx->inode_imagic_map = 0;
+ ext2fs_u32_list_free(ctx->dirs_to_hash);
+ ctx->dirs_to_hash = 0;
+
+ /*
+ * Clear the array of invalid meta-data flags
+ */
+ ext2fs_free_mem(&ctx->invalid_inode_bitmap_flag);
+ ext2fs_free_mem(&ctx->invalid_block_bitmap_flag);
+ ext2fs_free_mem(&ctx->invalid_inode_table_flag);
+
+ /* Clear statistic counters */
+ ctx->fs_directory_count = 0;
+ ctx->fs_regular_count = 0;
+ ctx->fs_blockdev_count = 0;
+ ctx->fs_chardev_count = 0;
+ ctx->fs_links_count = 0;
+ ctx->fs_symlinks_count = 0;
+ ctx->fs_fast_symlinks_count = 0;
+ ctx->fs_fifo_count = 0;
+ ctx->fs_total_count = 0;
+ ctx->fs_sockets_count = 0;
+ ctx->fs_ind_count = 0;
+ ctx->fs_dind_count = 0;
+ ctx->fs_tind_count = 0;
+ ctx->fs_fragmented = 0;
+ ctx->large_files = 0;
+
+ /* Reset the superblock to the user's requested value */
+ ctx->superblock = ctx->use_superblock;
+
+ return 0;
+}
+
+static void e2fsck_free_context(e2fsck_t ctx)
+{
+ if (!ctx)
+ return;
+
+ e2fsck_reset_context(ctx);
+ if (ctx->blkid)
+ blkid_put_cache(ctx->blkid);
+
+ ext2fs_free_mem(&ctx);
+}
+
+/*
+ * ea_refcount.c
+ */
+
+/*
+ * The strategy we use for keeping track of EA refcounts is as
+ * follows. We keep a sorted array of first EA blocks and its
+ * reference counts. Once the refcount has dropped to zero, it is
+ * removed from the array to save memory space. Once the EA block is
+ * checked, its bit is set in the block_ea_map bitmap.
+ */
+
+
+static errcode_t ea_refcount_create(int size, ext2_refcount_t *ret)
+{
+ ext2_refcount_t refcount;
+ errcode_t retval;
+ size_t bytes;
+
+ retval = ext2fs_get_mem(sizeof(struct ea_refcount), &refcount);
+ if (retval)
+ return retval;
+ memset(refcount, 0, sizeof(struct ea_refcount));
+
+ if (!size)
+ size = 500;
+ refcount->size = size;
+ bytes = (size_t) (size * sizeof(struct ea_refcount_el));
+#ifdef DEBUG
+ printf("Refcount allocated %d entries, %d bytes.\n",
+ refcount->size, bytes);
+#endif
+ retval = ext2fs_get_mem(bytes, &refcount->list);
+ if (retval)
+ goto errout;
+ memset(refcount->list, 0, bytes);
+
+ refcount->count = 0;
+ refcount->cursor = 0;
+
+ *ret = refcount;
+ return 0;
+
+errout:
+ ea_refcount_free(refcount);
+ return retval;
+}
+
+/*
+ * collapse_refcount() --- go through the refcount array, and get rid
+ * of any count == zero entries
+ */
+static void refcount_collapse(ext2_refcount_t refcount)
+{
+ unsigned int i, j;
+ struct ea_refcount_el *list;
+
+ list = refcount->list;
+ for (i = 0, j = 0; i < refcount->count; i++) {
+ if (list[i].ea_count) {
+ if (i != j)
+ list[j] = list[i];
+ j++;
+ }
+ }
+#if defined(DEBUG) || defined(TEST_PROGRAM)
+ printf("Refcount_collapse: size was %d, now %d\n",
+ refcount->count, j);
+#endif
+ refcount->count = j;
+}
+
+
+/*
+ * insert_refcount_el() --- Insert a new entry into the sorted list at a
+ * specified position.
+ */
+static struct ea_refcount_el *insert_refcount_el(ext2_refcount_t refcount,
+ blk_t blk, int pos)
+{
+ struct ea_refcount_el *el;
+ errcode_t retval;
+ blk_t new_size = 0;
+ int num;
+
+ if (refcount->count >= refcount->size) {
+ new_size = refcount->size + 100;
+#ifdef DEBUG
+ printf("Reallocating refcount %d entries...\n", new_size);
+#endif
+ retval = ext2fs_resize_mem((size_t) refcount->size *
+ sizeof(struct ea_refcount_el),
+ (size_t) new_size *
+ sizeof(struct ea_refcount_el),
+ &refcount->list);
+ if (retval)
+ return 0;
+ refcount->size = new_size;
+ }
+ num = (int) refcount->count - pos;
+ if (num < 0)
+ return 0; /* should never happen */
+ if (num) {
+ memmove(&refcount->list[pos+1], &refcount->list[pos],
+ sizeof(struct ea_refcount_el) * num);
+ }
+ refcount->count++;
+ el = &refcount->list[pos];
+ el->ea_count = 0;
+ el->ea_blk = blk;
+ return el;
+}
+
+
+/*
+ * get_refcount_el() --- given an block number, try to find refcount
+ * information in the sorted list. If the create flag is set,
+ * and we can't find an entry, create one in the sorted list.
+ */
+static struct ea_refcount_el *get_refcount_el(ext2_refcount_t refcount,
+ blk_t blk, int create)
+{
+ float range;
+ int low, high, mid;
+ blk_t lowval, highval;
+
+ if (!refcount || !refcount->list)
+ return 0;
+retry:
+ low = 0;
+ high = (int) refcount->count-1;
+ if (create && ((refcount->count == 0) ||
+ (blk > refcount->list[high].ea_blk))) {
+ if (refcount->count >= refcount->size)
+ refcount_collapse(refcount);
+
+ return insert_refcount_el(refcount, blk,
+ (unsigned) refcount->count);
+ }
+ if (refcount->count == 0)
+ return 0;
+
+ if (refcount->cursor >= refcount->count)
+ refcount->cursor = 0;
+ if (blk == refcount->list[refcount->cursor].ea_blk)
+ return &refcount->list[refcount->cursor++];
+#ifdef DEBUG
+ printf("Non-cursor get_refcount_el: %u\n", blk);
+#endif
+ while (low <= high) {
+ if (low == high)
+ mid = low;
+ else {
+ /* Interpolate for efficiency */
+ lowval = refcount->list[low].ea_blk;
+ highval = refcount->list[high].ea_blk;
+
+ if (blk < lowval)
+ range = 0;
+ else if (blk > highval)
+ range = 1;
+ else
+ range = ((float) (blk - lowval)) /
+ (highval - lowval);
+ mid = low + ((int) (range * (high-low)));
+ }
+
+ if (blk == refcount->list[mid].ea_blk) {
+ refcount->cursor = mid+1;
+ return &refcount->list[mid];
+ }
+ if (blk < refcount->list[mid].ea_blk)
+ high = mid-1;
+ else
+ low = mid+1;
+ }
+ /*
+ * If we need to create a new entry, it should be right at
+ * low (where high will be left at low-1).
+ */
+ if (create) {
+ if (refcount->count >= refcount->size) {
+ refcount_collapse(refcount);
+ if (refcount->count < refcount->size)
+ goto retry;
+ }
+ return insert_refcount_el(refcount, blk, low);
+ }
+ return 0;
+}
+
+static errcode_t
+ea_refcount_increment(ext2_refcount_t refcount, blk_t blk, int *ret)
+{
+ struct ea_refcount_el *el;
+
+ el = get_refcount_el(refcount, blk, 1);
+ if (!el)
+ return EXT2_ET_NO_MEMORY;
+ el->ea_count++;
+
+ if (ret)
+ *ret = el->ea_count;
+ return 0;
+}
+
+static errcode_t
+ea_refcount_decrement(ext2_refcount_t refcount, blk_t blk, int *ret)
+{
+ struct ea_refcount_el *el;
+
+ el = get_refcount_el(refcount, blk, 0);
+ if (!el || el->ea_count == 0)
+ return EXT2_ET_INVALID_ARGUMENT;
+
+ el->ea_count--;
+
+ if (ret)
+ *ret = el->ea_count;
+ return 0;
+}
+
+static errcode_t
+ea_refcount_store(ext2_refcount_t refcount, blk_t blk, int count)
+{
+ struct ea_refcount_el *el;
+
+ /*
+ * Get the refcount element
+ */
+ el = get_refcount_el(refcount, blk, count ? 1 : 0);
+ if (!el)
+ return count ? EXT2_ET_NO_MEMORY : 0;
+ el->ea_count = count;
+ return 0;
+}
+
+static inline void ea_refcount_intr_begin(ext2_refcount_t refcount)
+{
+ refcount->cursor = 0;
+}
+
+
+static blk_t ea_refcount_intr_next(ext2_refcount_t refcount, int *ret)
+{
+ struct ea_refcount_el *list;
+
+ while (1) {
+ if (refcount->cursor >= refcount->count)
+ return 0;
+ list = refcount->list;
+ if (list[refcount->cursor].ea_count) {
+ if (ret)
+ *ret = list[refcount->cursor].ea_count;
+ return list[refcount->cursor++].ea_blk;
+ }
+ refcount->cursor++;
+ }
+}
+
+
+/*
+ * ehandler.c --- handle bad block errors which come up during the
+ * course of an e2fsck session.
+ */
+
+
+static const char *operation;
+
+static errcode_t
+e2fsck_handle_read_error(io_channel channel, unsigned long block, int count,
+ void *data, size_t size FSCK_ATTR((unused)),
+ int actual FSCK_ATTR((unused)), errcode_t error)
+{
+ int i;
+ char *p;
+ ext2_filsys fs = (ext2_filsys) channel->app_data;
+ e2fsck_t ctx;
+
+ ctx = (e2fsck_t) fs->priv_data;
+
+ /*
+ * If more than one block was read, try reading each block
+ * separately. We could use the actual bytes read to figure
+ * out where to start, but we don't bother.
+ */
+ if (count > 1) {
+ p = (char *) data;
+ for (i=0; i < count; i++, p += channel->block_size, block++) {
+ error = io_channel_read_blk(channel, block,
+ 1, p);
+ if (error)
+ return error;
+ }
+ return 0;
+ }
+ if (operation)
+ printf(_("Error reading block %lu (%s) while %s. "), block,
+ error_message(error), operation);
+ else
+ printf(_("Error reading block %lu (%s). "), block,
+ error_message(error));
+ preenhalt(ctx);
+ if (ask(ctx, _("Ignore error"), 1)) {
+ if (ask(ctx, _("Force rewrite"), 1))
+ io_channel_write_blk(channel, block, 1, data);
+ return 0;
+ }
+
+ return error;
+}
+
+static errcode_t
+e2fsck_handle_write_error(io_channel channel, unsigned long block, int count,
+ const void *data, size_t size FSCK_ATTR((unused)),
+ int actual FSCK_ATTR((unused)), errcode_t error)
+{
+ int i;
+ const char *p;
+ ext2_filsys fs = (ext2_filsys) channel->app_data;
+ e2fsck_t ctx;
+
+ ctx = (e2fsck_t) fs->priv_data;
+
+ /*
+ * If more than one block was written, try writing each block
+ * separately. We could use the actual bytes read to figure
+ * out where to start, but we don't bother.
+ */
+ if (count > 1) {
+ p = (const char *) data;
+ for (i=0; i < count; i++, p += channel->block_size, block++) {
+ error = io_channel_write_blk(channel, block,
+ 1, p);
+ if (error)
+ return error;
+ }
+ return 0;
+ }
+
+ if (operation)
+ printf(_("Error writing block %lu (%s) while %s. "), block,
+ error_message(error), operation);
+ else
+ printf(_("Error writing block %lu (%s). "), block,
+ error_message(error));
+ preenhalt(ctx);
+ if (ask(ctx, _("Ignore error"), 1))
+ return 0;
+
+ return error;
+}
+
+static const char *ehandler_operation(const char *op)
+{
+ const char *ret = operation;
+
+ operation = op;
+ return ret;
+}
+
+static void ehandler_init(io_channel channel)
+{
+ channel->read_error = e2fsck_handle_read_error;
+ channel->write_error = e2fsck_handle_write_error;
+}
+
+/*
+ * journal.c --- code for handling the "ext3" journal
+ *
+ * Copyright (C) 2000 Andreas Dilger
+ * Copyright (C) 2000 Theodore Ts'o
+ *
+ * Parts of the code are based on fs/jfs/journal.c by Stephen C. Tweedie
+ * Copyright (C) 1999 Red Hat Software
+ *
+ * This file may be redistributed under the terms of the
+ * GNU General Public License version 2 or at your discretion
+ * any later version.
+ */
+
+/*
+ * Define USE_INODE_IO to use the inode_io.c / fileio.c codepaths.
+ * This creates a larger static binary, and a smaller binary using
+ * shared libraries. It's also probably slightly less CPU-efficient,
+ * which is why it's not on by default. But, it's a good way of
+ * testing the functions in inode_io.c and fileio.c.
+ */
+#undef USE_INODE_IO
+
+/* Kernel compatibility functions for handling the journal. These allow us
+ * to use the recovery.c file virtually unchanged from the kernel, so we
+ * don't have to do much to keep kernel and user recovery in sync.
+ */
+static int journal_bmap(journal_t *journal, blk_t block, unsigned long *phys)
+{
+#ifdef USE_INODE_IO
+ *phys = block;
+ return 0;
+#else
+ struct inode *inode = journal->j_inode;
+ errcode_t retval;
+ blk_t pblk;
+
+ if (!inode) {
+ *phys = block;
+ return 0;
+ }
+
+ retval= ext2fs_bmap(inode->i_ctx->fs, inode->i_ino,
+ &inode->i_ext2, NULL, 0, block, &pblk);
+ *phys = pblk;
+ return retval;
+#endif
+}
+
+static struct buffer_head *getblk(kdev_t kdev, blk_t blocknr, int blocksize)
+{
+ struct buffer_head *bh;
+
+ bh = e2fsck_allocate_memory(kdev->k_ctx, sizeof(*bh), "block buffer");
+ if (!bh)
+ return NULL;
+
+ bh->b_ctx = kdev->k_ctx;
+ if (kdev->k_dev == K_DEV_FS)
+ bh->b_io = kdev->k_ctx->fs->io;
+ else
+ bh->b_io = kdev->k_ctx->journal_io;
+ bh->b_size = blocksize;
+ bh->b_blocknr = blocknr;
+
+ return bh;
+}
+
+static void sync_blockdev(kdev_t kdev)
+{
+ io_channel io;
+
+ if (kdev->k_dev == K_DEV_FS)
+ io = kdev->k_ctx->fs->io;
+ else
+ io = kdev->k_ctx->journal_io;
+
+ io_channel_flush(io);
+}
+
+static void ll_rw_block(int rw, int nr, struct buffer_head *bhp[])
+{
+ int retval;
+ struct buffer_head *bh;
+
+ for (; nr > 0; --nr) {
+ bh = *bhp++;
+ if (rw == READ && !bh->b_uptodate) {
+ retval = io_channel_read_blk(bh->b_io,
+ bh->b_blocknr,
+ 1, bh->b_data);
+ if (retval) {
+ bb_error_msg("while reading block %lu",
+ (unsigned long) bh->b_blocknr);
+ bh->b_err = retval;
+ continue;
+ }
+ bh->b_uptodate = 1;
+ } else if (rw == WRITE && bh->b_dirty) {
+ retval = io_channel_write_blk(bh->b_io,
+ bh->b_blocknr,
+ 1, bh->b_data);
+ if (retval) {
+ bb_error_msg("while writing block %lu",
+ (unsigned long) bh->b_blocknr);
+ bh->b_err = retval;
+ continue;
+ }
+ bh->b_dirty = 0;
+ bh->b_uptodate = 1;
+ }
+ }
+}
+
+static void mark_buffer_dirty(struct buffer_head *bh)
+{
+ bh->b_dirty = 1;
+}
+
+static inline void mark_buffer_clean(struct buffer_head * bh)
+{
+ bh->b_dirty = 0;
+}
+
+static void brelse(struct buffer_head *bh)
+{
+ if (bh->b_dirty)
+ ll_rw_block(WRITE, 1, &bh);
+ ext2fs_free_mem(&bh);
+}
+
+static int buffer_uptodate(struct buffer_head *bh)
+{
+ return bh->b_uptodate;
+}
+
+static inline void mark_buffer_uptodate(struct buffer_head *bh, int val)
+{
+ bh->b_uptodate = val;
+}
+
+static void wait_on_buffer(struct buffer_head *bh)
+{
+ if (!bh->b_uptodate)
+ ll_rw_block(READ, 1, &bh);
+}
+
+
+static void e2fsck_clear_recover(e2fsck_t ctx, int error)
+{
+ ctx->fs->super->s_feature_incompat &= ~EXT3_FEATURE_INCOMPAT_RECOVER;
+
+ /* if we had an error doing journal recovery, we need a full fsck */
+ if (error)
+ ctx->fs->super->s_state &= ~EXT2_VALID_FS;
+ ext2fs_mark_super_dirty(ctx->fs);
+}
+
+static errcode_t e2fsck_get_journal(e2fsck_t ctx, journal_t **ret_journal)
+{
+ struct ext2_super_block *sb = ctx->fs->super;
+ struct ext2_super_block jsuper;
+ struct problem_context pctx;
+ struct buffer_head *bh;
+ struct inode *j_inode = NULL;
+ struct kdev_s *dev_fs = NULL, *dev_journal;
+ const char *journal_name = 0;
+ journal_t *journal = NULL;
+ errcode_t retval = 0;
+ io_manager io_ptr = 0;
+ unsigned long start = 0;
+ blk_t blk;
+ int ext_journal = 0;
+ int tried_backup_jnl = 0;
+ int i;
+
+ clear_problem_context(&pctx);
+
+ journal = e2fsck_allocate_memory(ctx, sizeof(journal_t), "journal");
+ if (!journal) {
+ return EXT2_ET_NO_MEMORY;
+ }
+
+ dev_fs = e2fsck_allocate_memory(ctx, 2*sizeof(struct kdev_s), "kdev");
+ if (!dev_fs) {
+ retval = EXT2_ET_NO_MEMORY;
+ goto errout;
+ }
+ dev_journal = dev_fs+1;
+
+ dev_fs->k_ctx = dev_journal->k_ctx = ctx;
+ dev_fs->k_dev = K_DEV_FS;
+ dev_journal->k_dev = K_DEV_JOURNAL;
+
+ journal->j_dev = dev_journal;
+ journal->j_fs_dev = dev_fs;
+ journal->j_inode = NULL;
+ journal->j_blocksize = ctx->fs->blocksize;
+
+ if (uuid_is_null(sb->s_journal_uuid)) {
+ if (!sb->s_journal_inum)
+ return EXT2_ET_BAD_INODE_NUM;
+ j_inode = e2fsck_allocate_memory(ctx, sizeof(*j_inode),
+ "journal inode");
+ if (!j_inode) {
+ retval = EXT2_ET_NO_MEMORY;
+ goto errout;
+ }
+
+ j_inode->i_ctx = ctx;
+ j_inode->i_ino = sb->s_journal_inum;
+
+ if ((retval = ext2fs_read_inode(ctx->fs,
+ sb->s_journal_inum,
+ &j_inode->i_ext2))) {
+ try_backup_journal:
+ if (sb->s_jnl_backup_type != EXT3_JNL_BACKUP_BLOCKS ||
+ tried_backup_jnl)
+ goto errout;
+ memset(&j_inode->i_ext2, 0, sizeof(struct ext2_inode));
+ memcpy(&j_inode->i_ext2.i_block[0], sb->s_jnl_blocks,
+ EXT2_N_BLOCKS*4);
+ j_inode->i_ext2.i_size = sb->s_jnl_blocks[16];
+ j_inode->i_ext2.i_links_count = 1;
+ j_inode->i_ext2.i_mode = LINUX_S_IFREG | 0600;
+ tried_backup_jnl++;
+ }
+ if (!j_inode->i_ext2.i_links_count ||
+ !LINUX_S_ISREG(j_inode->i_ext2.i_mode)) {
+ retval = EXT2_ET_NO_JOURNAL;
+ goto try_backup_journal;
+ }
+ if (j_inode->i_ext2.i_size / journal->j_blocksize <
+ JFS_MIN_JOURNAL_BLOCKS) {
+ retval = EXT2_ET_JOURNAL_TOO_SMALL;
+ goto try_backup_journal;
+ }
+ for (i=0; i < EXT2_N_BLOCKS; i++) {
+ blk = j_inode->i_ext2.i_block[i];
+ if (!blk) {
+ if (i < EXT2_NDIR_BLOCKS) {
+ retval = EXT2_ET_JOURNAL_TOO_SMALL;
+ goto try_backup_journal;
+ }
+ continue;
+ }
+ if (blk < sb->s_first_data_block ||
+ blk >= sb->s_blocks_count) {
+ retval = EXT2_ET_BAD_BLOCK_NUM;
+ goto try_backup_journal;
+ }
+ }
+ journal->j_maxlen = j_inode->i_ext2.i_size / journal->j_blocksize;
+
+#ifdef USE_INODE_IO
+ retval = ext2fs_inode_io_intern2(ctx->fs, sb->s_journal_inum,
+ &j_inode->i_ext2,
+ &journal_name);
+ if (retval)
+ goto errout;
+
+ io_ptr = inode_io_manager;
+#else
+ journal->j_inode = j_inode;
+ ctx->journal_io = ctx->fs->io;
+ if ((retval = journal_bmap(journal, 0, &start)) != 0)
+ goto errout;
+#endif
+ } else {
+ ext_journal = 1;
+ if (!ctx->journal_name) {
+ char uuid[37];
+
+ uuid_unparse(sb->s_journal_uuid, uuid);
+ ctx->journal_name = blkid_get_devname(ctx->blkid,
+ "UUID", uuid);
+ if (!ctx->journal_name)
+ ctx->journal_name = blkid_devno_to_devname(sb->s_journal_dev);
+ }
+ journal_name = ctx->journal_name;
+
+ if (!journal_name) {
+ fix_problem(ctx, PR_0_CANT_FIND_JOURNAL, &pctx);
+ return EXT2_ET_LOAD_EXT_JOURNAL;
+ }
+
+ io_ptr = unix_io_manager;
+ }
+
+#ifndef USE_INODE_IO
+ if (ext_journal)
+#endif
+ retval = io_ptr->open(journal_name, IO_FLAG_RW,
+ &ctx->journal_io);
+ if (retval)
+ goto errout;
+
+ io_channel_set_blksize(ctx->journal_io, ctx->fs->blocksize);
+
+ if (ext_journal) {
+ if (ctx->fs->blocksize == 1024)
+ start = 1;
+ bh = getblk(dev_journal, start, ctx->fs->blocksize);
+ if (!bh) {
+ retval = EXT2_ET_NO_MEMORY;
+ goto errout;
+ }
+ ll_rw_block(READ, 1, &bh);
+ if ((retval = bh->b_err) != 0)
+ goto errout;
+ memcpy(&jsuper, start ? bh->b_data : bh->b_data + 1024,
+ sizeof(jsuper));
+ brelse(bh);
+#if BB_BIG_ENDIAN
+ if (jsuper.s_magic == ext2fs_swab16(EXT2_SUPER_MAGIC))
+ ext2fs_swap_super(&jsuper);
+#endif
+ if (jsuper.s_magic != EXT2_SUPER_MAGIC ||
+ !(jsuper.s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)) {
+ fix_problem(ctx, PR_0_EXT_JOURNAL_BAD_SUPER, &pctx);
+ retval = EXT2_ET_LOAD_EXT_JOURNAL;
+ goto errout;
+ }
+ /* Make sure the journal UUID is correct */
+ if (memcmp(jsuper.s_uuid, ctx->fs->super->s_journal_uuid,
+ sizeof(jsuper.s_uuid))) {
+ fix_problem(ctx, PR_0_JOURNAL_BAD_UUID, &pctx);
+ retval = EXT2_ET_LOAD_EXT_JOURNAL;
+ goto errout;
+ }
+
+ journal->j_maxlen = jsuper.s_blocks_count;
+ start++;
+ }
+
+ if (!(bh = getblk(dev_journal, start, journal->j_blocksize))) {
+ retval = EXT2_ET_NO_MEMORY;
+ goto errout;
+ }
+
+ journal->j_sb_buffer = bh;
+ journal->j_superblock = (journal_superblock_t *)bh->b_data;
+
+#ifdef USE_INODE_IO
+ ext2fs_free_mem(&j_inode);
+#endif
+
+ *ret_journal = journal;
+ return 0;
+
+errout:
+ ext2fs_free_mem(&dev_fs);
+ ext2fs_free_mem(&j_inode);
+ ext2fs_free_mem(&journal);
+ return retval;
+
+}
+
+static errcode_t e2fsck_journal_fix_bad_inode(e2fsck_t ctx,
+ struct problem_context *pctx)
+{
+ struct ext2_super_block *sb = ctx->fs->super;
+ int recover = ctx->fs->super->s_feature_incompat &
+ EXT3_FEATURE_INCOMPAT_RECOVER;
+ int has_journal = ctx->fs->super->s_feature_compat &
+ EXT3_FEATURE_COMPAT_HAS_JOURNAL;
+
+ if (has_journal || sb->s_journal_inum) {
+ /* The journal inode is bogus, remove and force full fsck */
+ pctx->ino = sb->s_journal_inum;
+ if (fix_problem(ctx, PR_0_JOURNAL_BAD_INODE, pctx)) {
+ if (has_journal && sb->s_journal_inum)
+ printf("*** ext3 journal has been deleted - "
+ "filesystem is now ext2 only ***\n\n");
+ sb->s_feature_compat &= ~EXT3_FEATURE_COMPAT_HAS_JOURNAL;
+ sb->s_journal_inum = 0;
+ ctx->flags |= E2F_FLAG_JOURNAL_INODE; /* FIXME: todo */
+ e2fsck_clear_recover(ctx, 1);
+ return 0;
+ }
+ return EXT2_ET_BAD_INODE_NUM;
+ } else if (recover) {
+ if (fix_problem(ctx, PR_0_JOURNAL_RECOVER_SET, pctx)) {
+ e2fsck_clear_recover(ctx, 1);
+ return 0;
+ }
+ return EXT2_ET_UNSUPP_FEATURE;
+ }
+ return 0;
+}
+
+#define V1_SB_SIZE 0x0024
+static void clear_v2_journal_fields(journal_t *journal)
+{
+ e2fsck_t ctx = journal->j_dev->k_ctx;
+ struct problem_context pctx;
+
+ clear_problem_context(&pctx);
+
+ if (!fix_problem(ctx, PR_0_CLEAR_V2_JOURNAL, &pctx))
+ return;
+
+ memset(((char *) journal->j_superblock) + V1_SB_SIZE, 0,
+ ctx->fs->blocksize-V1_SB_SIZE);
+ mark_buffer_dirty(journal->j_sb_buffer);
+}
+
+
+static errcode_t e2fsck_journal_load(journal_t *journal)
+{
+ e2fsck_t ctx = journal->j_dev->k_ctx;
+ journal_superblock_t *jsb;
+ struct buffer_head *jbh = journal->j_sb_buffer;
+ struct problem_context pctx;
+
+ clear_problem_context(&pctx);
+
+ ll_rw_block(READ, 1, &jbh);
+ if (jbh->b_err) {
+ bb_error_msg(_("reading journal superblock"));
+ return jbh->b_err;
+ }
+
+ jsb = journal->j_superblock;
+ /* If we don't even have JFS_MAGIC, we probably have a wrong inode */
+ if (jsb->s_header.h_magic != htonl(JFS_MAGIC_NUMBER))
+ return e2fsck_journal_fix_bad_inode(ctx, &pctx);
+
+ switch (ntohl(jsb->s_header.h_blocktype)) {
+ case JFS_SUPERBLOCK_V1:
+ journal->j_format_version = 1;
+ if (jsb->s_feature_compat ||
+ jsb->s_feature_incompat ||
+ jsb->s_feature_ro_compat ||
+ jsb->s_nr_users)
+ clear_v2_journal_fields(journal);
+ break;
+
+ case JFS_SUPERBLOCK_V2:
+ journal->j_format_version = 2;
+ if (ntohl(jsb->s_nr_users) > 1 &&
+ uuid_is_null(ctx->fs->super->s_journal_uuid))
+ clear_v2_journal_fields(journal);
+ if (ntohl(jsb->s_nr_users) > 1) {
+ fix_problem(ctx, PR_0_JOURNAL_UNSUPP_MULTIFS, &pctx);
+ return EXT2_ET_JOURNAL_UNSUPP_VERSION;
+ }
+ break;
+
+ /*
+ * These should never appear in a journal super block, so if
+ * they do, the journal is badly corrupted.
+ */
+ case JFS_DESCRIPTOR_BLOCK:
+ case JFS_COMMIT_BLOCK:
+ case JFS_REVOKE_BLOCK:
+ return EXT2_ET_CORRUPT_SUPERBLOCK;
+
+ /* If we don't understand the superblock major type, but there
+ * is a magic number, then it is likely to be a new format we
+ * just don't understand, so leave it alone. */
+ default:
+ return EXT2_ET_JOURNAL_UNSUPP_VERSION;
+ }
+
+ if (JFS_HAS_INCOMPAT_FEATURE(journal, ~JFS_KNOWN_INCOMPAT_FEATURES))
+ return EXT2_ET_UNSUPP_FEATURE;
+
+ if (JFS_HAS_RO_COMPAT_FEATURE(journal, ~JFS_KNOWN_ROCOMPAT_FEATURES))
+ return EXT2_ET_RO_UNSUPP_FEATURE;
+
+ /* We have now checked whether we know enough about the journal
+ * format to be able to proceed safely, so any other checks that
+ * fail we should attempt to recover from. */
+ if (jsb->s_blocksize != htonl(journal->j_blocksize)) {
+ bb_error_msg(_("%s: no valid journal superblock found"),
+ ctx->device_name);
+ return EXT2_ET_CORRUPT_SUPERBLOCK;
+ }
+
+ if (ntohl(jsb->s_maxlen) < journal->j_maxlen)
+ journal->j_maxlen = ntohl(jsb->s_maxlen);
+ else if (ntohl(jsb->s_maxlen) > journal->j_maxlen) {
+ bb_error_msg(_("%s: journal too short"),
+ ctx->device_name);
+ return EXT2_ET_CORRUPT_SUPERBLOCK;
+ }
+
+ journal->j_tail_sequence = ntohl(jsb->s_sequence);
+ journal->j_transaction_sequence = journal->j_tail_sequence;
+ journal->j_tail = ntohl(jsb->s_start);
+ journal->j_first = ntohl(jsb->s_first);
+ journal->j_last = ntohl(jsb->s_maxlen);
+
+ return 0;
+}
+
+static void e2fsck_journal_reset_super(e2fsck_t ctx, journal_superblock_t *jsb,
+ journal_t *journal)
+{
+ char *p;
+ union {
+ uuid_t uuid;
+ __u32 val[4];
+ } u;
+ __u32 new_seq = 0;
+ int i;
+
+ /* Leave a valid existing V1 superblock signature alone.
+ * Anything unrecognisable we overwrite with a new V2
+ * signature. */
+
+ if (jsb->s_header.h_magic != htonl(JFS_MAGIC_NUMBER) ||
+ jsb->s_header.h_blocktype != htonl(JFS_SUPERBLOCK_V1)) {
+ jsb->s_header.h_magic = htonl(JFS_MAGIC_NUMBER);
+ jsb->s_header.h_blocktype = htonl(JFS_SUPERBLOCK_V2);
+ }
+
+ /* Zero out everything else beyond the superblock header */
+
+ p = ((char *) jsb) + sizeof(journal_header_t);
+ memset (p, 0, ctx->fs->blocksize-sizeof(journal_header_t));
+
+ jsb->s_blocksize = htonl(ctx->fs->blocksize);
+ jsb->s_maxlen = htonl(journal->j_maxlen);
+ jsb->s_first = htonl(1);
+
+ /* Initialize the journal sequence number so that there is "no"
+ * chance we will find old "valid" transactions in the journal.
+ * This avoids the need to zero the whole journal (slow to do,
+ * and risky when we are just recovering the filesystem).
+ */
+ uuid_generate(u.uuid);
+ for (i = 0; i < 4; i ++)
+ new_seq ^= u.val[i];
+ jsb->s_sequence = htonl(new_seq);
+
+ mark_buffer_dirty(journal->j_sb_buffer);
+ ll_rw_block(WRITE, 1, &journal->j_sb_buffer);
+}
+
+static errcode_t e2fsck_journal_fix_corrupt_super(e2fsck_t ctx,
+ journal_t *journal,
+ struct problem_context *pctx)
+{
+ struct ext2_super_block *sb = ctx->fs->super;
+ int recover = ctx->fs->super->s_feature_incompat &
+ EXT3_FEATURE_INCOMPAT_RECOVER;
+
+ if (sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL) {
+ if (fix_problem(ctx, PR_0_JOURNAL_BAD_SUPER, pctx)) {
+ e2fsck_journal_reset_super(ctx, journal->j_superblock,
+ journal);
+ journal->j_transaction_sequence = 1;
+ e2fsck_clear_recover(ctx, recover);
+ return 0;
+ }
+ return EXT2_ET_CORRUPT_SUPERBLOCK;
+ } else if (e2fsck_journal_fix_bad_inode(ctx, pctx))
+ return EXT2_ET_CORRUPT_SUPERBLOCK;
+
+ return 0;
+}
+
+static void e2fsck_journal_release(e2fsck_t ctx, journal_t *journal,
+ int reset, int drop)
+{
+ journal_superblock_t *jsb;
+
+ if (drop)
+ mark_buffer_clean(journal->j_sb_buffer);
+ else if (!(ctx->options & E2F_OPT_READONLY)) {
+ jsb = journal->j_superblock;
+ jsb->s_sequence = htonl(journal->j_transaction_sequence);
+ if (reset)
+ jsb->s_start = 0; /* this marks the journal as empty */
+ mark_buffer_dirty(journal->j_sb_buffer);
+ }
+ brelse(journal->j_sb_buffer);
+
+ if (ctx->journal_io) {
+ if (ctx->fs && ctx->fs->io != ctx->journal_io)
+ io_channel_close(ctx->journal_io);
+ ctx->journal_io = 0;
+ }
+
+#ifndef USE_INODE_IO
+ ext2fs_free_mem(&journal->j_inode);
+#endif
+ ext2fs_free_mem(&journal->j_fs_dev);
+ ext2fs_free_mem(&journal);
+}
+
+/*
+ * This function makes sure that the superblock fields regarding the
+ * journal are consistent.
+ */
+static int e2fsck_check_ext3_journal(e2fsck_t ctx)
+{
+ struct ext2_super_block *sb = ctx->fs->super;
+ journal_t *journal;
+ int recover = ctx->fs->super->s_feature_incompat &
+ EXT3_FEATURE_INCOMPAT_RECOVER;
+ struct problem_context pctx;
+ problem_t problem;
+ int reset = 0, force_fsck = 0;
+ int retval;
+
+ /* If we don't have any journal features, don't do anything more */
+ if (!(sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL) &&
+ !recover && sb->s_journal_inum == 0 && sb->s_journal_dev == 0 &&
+ uuid_is_null(sb->s_journal_uuid))
+ return 0;
+
+ clear_problem_context(&pctx);
+ pctx.num = sb->s_journal_inum;
+
+ retval = e2fsck_get_journal(ctx, &journal);
+ if (retval) {
+ if ((retval == EXT2_ET_BAD_INODE_NUM) ||
+ (retval == EXT2_ET_BAD_BLOCK_NUM) ||
+ (retval == EXT2_ET_JOURNAL_TOO_SMALL) ||
+ (retval == EXT2_ET_NO_JOURNAL))
+ return e2fsck_journal_fix_bad_inode(ctx, &pctx);
+ return retval;
+ }
+
+ retval = e2fsck_journal_load(journal);
+ if (retval) {
+ if ((retval == EXT2_ET_CORRUPT_SUPERBLOCK) ||
+ ((retval == EXT2_ET_UNSUPP_FEATURE) &&
+ (!fix_problem(ctx, PR_0_JOURNAL_UNSUPP_INCOMPAT,
+ &pctx))) ||
+ ((retval == EXT2_ET_RO_UNSUPP_FEATURE) &&
+ (!fix_problem(ctx, PR_0_JOURNAL_UNSUPP_ROCOMPAT,
+ &pctx))) ||
+ ((retval == EXT2_ET_JOURNAL_UNSUPP_VERSION) &&
+ (!fix_problem(ctx, PR_0_JOURNAL_UNSUPP_VERSION, &pctx))))
+ retval = e2fsck_journal_fix_corrupt_super(ctx, journal,
+ &pctx);
+ e2fsck_journal_release(ctx, journal, 0, 1);
+ return retval;
+ }
+
+ /*
+ * We want to make the flags consistent here. We will not leave with
+ * needs_recovery set but has_journal clear. We can't get in a loop
+ * with -y, -n, or -p, only if a user isn't making up their mind.
+ */
+no_has_journal:
+ if (!(sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL)) {
+ recover = sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER;
+ pctx.str = "inode";
+ if (fix_problem(ctx, PR_0_JOURNAL_HAS_JOURNAL, &pctx)) {
+ if (recover &&
+ !fix_problem(ctx, PR_0_JOURNAL_RECOVER_SET, &pctx))
+ goto no_has_journal;
+ /*
+ * Need a full fsck if we are releasing a
+ * journal stored on a reserved inode.
+ */
+ force_fsck = recover ||
+ (sb->s_journal_inum < EXT2_FIRST_INODE(sb));
+ /* Clear all of the journal fields */
+ sb->s_journal_inum = 0;
+ sb->s_journal_dev = 0;
+ memset(sb->s_journal_uuid, 0,
+ sizeof(sb->s_journal_uuid));
+ e2fsck_clear_recover(ctx, force_fsck);
+ } else if (!(ctx->options & E2F_OPT_READONLY)) {
+ sb->s_feature_compat |= EXT3_FEATURE_COMPAT_HAS_JOURNAL;
+ ext2fs_mark_super_dirty(ctx->fs);
+ }
+ }
+
+ if (sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL &&
+ !(sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER) &&
+ journal->j_superblock->s_start != 0) {
+ /* Print status information */
+ fix_problem(ctx, PR_0_JOURNAL_RECOVERY_CLEAR, &pctx);
+ if (ctx->superblock)
+ problem = PR_0_JOURNAL_RUN_DEFAULT;
+ else
+ problem = PR_0_JOURNAL_RUN;
+ if (fix_problem(ctx, problem, &pctx)) {
+ ctx->options |= E2F_OPT_FORCE;
+ sb->s_feature_incompat |=
+ EXT3_FEATURE_INCOMPAT_RECOVER;
+ ext2fs_mark_super_dirty(ctx->fs);
+ } else if (fix_problem(ctx,
+ PR_0_JOURNAL_RESET_JOURNAL, &pctx)) {
+ reset = 1;
+ sb->s_state &= ~EXT2_VALID_FS;
+ ext2fs_mark_super_dirty(ctx->fs);
+ }
+ /*
+ * If the user answers no to the above question, we
+ * ignore the fact that journal apparently has data;
+ * accidentally replaying over valid data would be far
+ * worse than skipping a questionable recovery.
+ *
+ * XXX should we abort with a fatal error here? What
+ * will the ext3 kernel code do if a filesystem with
+ * !NEEDS_RECOVERY but with a non-zero
+ * journal->j_superblock->s_start is mounted?
+ */
+ }
+
+ e2fsck_journal_release(ctx, journal, reset, 0);
+ return retval;
+}
+
+static errcode_t recover_ext3_journal(e2fsck_t ctx)
+{
+ journal_t *journal;
+ int retval;
+
+ journal_init_revoke_caches();
+ retval = e2fsck_get_journal(ctx, &journal);
+ if (retval)
+ return retval;
+
+ retval = e2fsck_journal_load(journal);
+ if (retval)
+ goto errout;
+
+ retval = journal_init_revoke(journal, 1024);
+ if (retval)
+ goto errout;
+
+ retval = -journal_recover(journal);
+ if (retval)
+ goto errout;
+
+ if (journal->j_superblock->s_errno) {
+ ctx->fs->super->s_state |= EXT2_ERROR_FS;
+ ext2fs_mark_super_dirty(ctx->fs);
+ journal->j_superblock->s_errno = 0;
+ mark_buffer_dirty(journal->j_sb_buffer);
+ }
+
+errout:
+ journal_destroy_revoke(journal);
+ journal_destroy_revoke_caches();
+ e2fsck_journal_release(ctx, journal, 1, 0);
+ return retval;
+}
+
+static int e2fsck_run_ext3_journal(e2fsck_t ctx)
+{
+ io_manager io_ptr = ctx->fs->io->manager;
+ int blocksize = ctx->fs->blocksize;
+ errcode_t retval, recover_retval;
+
+ printf(_("%s: recovering journal\n"), ctx->device_name);
+ if (ctx->options & E2F_OPT_READONLY) {
+ printf(_("%s: won't do journal recovery while read-only\n"),
+ ctx->device_name);
+ return EXT2_ET_FILE_RO;
+ }
+
+ if (ctx->fs->flags & EXT2_FLAG_DIRTY)
+ ext2fs_flush(ctx->fs); /* Force out any modifications */
+
+ recover_retval = recover_ext3_journal(ctx);
+
+ /*
+ * Reload the filesystem context to get up-to-date data from disk
+ * because journal recovery will change the filesystem under us.
+ */
+ ext2fs_close(ctx->fs);
+ retval = ext2fs_open(ctx->filesystem_name, EXT2_FLAG_RW,
+ ctx->superblock, blocksize, io_ptr,
+ &ctx->fs);
+
+ if (retval) {
+ bb_error_msg(_("while trying to re-open %s"),
+ ctx->device_name);
+ bb_error_msg_and_die(0);
+ }
+ ctx->fs->priv_data = ctx;
+
+ /* Set the superblock flags */
+ e2fsck_clear_recover(ctx, recover_retval);
+ return recover_retval;
+}
+
+/*
+ * This function will move the journal inode from a visible file in
+ * the filesystem directory hierarchy to the reserved inode if necessary.
+ */
+static const char * const journal_names[] = {
+ ".journal", "journal", ".journal.dat", "journal.dat", 0 };
+
+static void e2fsck_move_ext3_journal(e2fsck_t ctx)
+{
+ struct ext2_super_block *sb = ctx->fs->super;
+ struct problem_context pctx;
+ struct ext2_inode inode;
+ ext2_filsys fs = ctx->fs;
+ ext2_ino_t ino;
+ errcode_t retval;
+ const char * const * cpp;
+ int group, mount_flags;
+
+ clear_problem_context(&pctx);
+
+ /*
+ * If the filesystem is opened read-only, or there is no
+ * journal, then do nothing.
+ */
+ if ((ctx->options & E2F_OPT_READONLY) ||
+ (sb->s_journal_inum == 0) ||
+ !(sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL))
+ return;
+
+ /*
+ * Read in the journal inode
+ */
+ if (ext2fs_read_inode(fs, sb->s_journal_inum, &inode) != 0)
+ return;
+
+ /*
+ * If it's necessary to backup the journal inode, do so.
+ */
+ if ((sb->s_jnl_backup_type == 0) ||
+ ((sb->s_jnl_backup_type == EXT3_JNL_BACKUP_BLOCKS) &&
+ memcmp(inode.i_block, sb->s_jnl_blocks, EXT2_N_BLOCKS*4))) {
+ if (fix_problem(ctx, PR_0_BACKUP_JNL, &pctx)) {
+ memcpy(sb->s_jnl_blocks, inode.i_block,
+ EXT2_N_BLOCKS*4);
+ sb->s_jnl_blocks[16] = inode.i_size;
+ sb->s_jnl_backup_type = EXT3_JNL_BACKUP_BLOCKS;
+ ext2fs_mark_super_dirty(fs);
+ fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
+ }
+ }
+
+ /*
+ * If the journal is already the hidden inode, then do nothing
+ */
+ if (sb->s_journal_inum == EXT2_JOURNAL_INO)
+ return;
+
+ /*
+ * The journal inode had better have only one link and not be readable.
+ */
+ if (inode.i_links_count != 1)
+ return;
+
+ /*
+ * If the filesystem is mounted, or we can't tell whether
+ * or not it's mounted, do nothing.
+ */
+ retval = ext2fs_check_if_mounted(ctx->filesystem_name, &mount_flags);
+ if (retval || (mount_flags & EXT2_MF_MOUNTED))
+ return;
+
+ /*
+ * If we can't find the name of the journal inode, then do
+ * nothing.
+ */
+ for (cpp = journal_names; *cpp; cpp++) {
+ retval = ext2fs_lookup(fs, EXT2_ROOT_INO, *cpp,
+ strlen(*cpp), 0, &ino);
+ if ((retval == 0) && (ino == sb->s_journal_inum))
+ break;
+ }
+ if (*cpp == 0)
+ return;
+
+ /* We need the inode bitmap to be loaded */
+ retval = ext2fs_read_bitmaps(fs);
+ if (retval)
+ return;
+
+ pctx.str = *cpp;
+ if (!fix_problem(ctx, PR_0_MOVE_JOURNAL, &pctx))
+ return;
+
+ /*
+ * OK, we've done all the checks, let's actually move the
+ * journal inode. Errors at this point mean we need to force
+ * an ext2 filesystem check.
+ */
+ if ((retval = ext2fs_unlink(fs, EXT2_ROOT_INO, *cpp, ino, 0)) != 0)
+ goto err_out;
+ if ((retval = ext2fs_write_inode(fs, EXT2_JOURNAL_INO, &inode)) != 0)
+ goto err_out;
+ sb->s_journal_inum = EXT2_JOURNAL_INO;
+ ext2fs_mark_super_dirty(fs);
+ fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
+ inode.i_links_count = 0;
+ inode.i_dtime = time(0);
+ if ((retval = ext2fs_write_inode(fs, ino, &inode)) != 0)
+ goto err_out;
+
+ group = ext2fs_group_of_ino(fs, ino);
+ ext2fs_unmark_inode_bitmap(fs->inode_map, ino);
+ ext2fs_mark_ib_dirty(fs);
+ fs->group_desc[group].bg_free_inodes_count++;
+ fs->super->s_free_inodes_count++;
+ return;
+
+err_out:
+ pctx.errcode = retval;
+ fix_problem(ctx, PR_0_ERR_MOVE_JOURNAL, &pctx);
+ fs->super->s_state &= ~EXT2_VALID_FS;
+ ext2fs_mark_super_dirty(fs);
+}
+
+/*
+ * message.c --- print e2fsck messages (with compression)
+ *
+ * print_e2fsck_message() prints a message to the user, using
+ * compression techniques and expansions of abbreviations.
+ *
+ * The following % expansions are supported:
+ *
+ * %b <blk> block number
+ * %B <blkcount> integer
+ * %c <blk2> block number
+ * %Di <dirent>->ino inode number
+ * %Dn <dirent>->name string
+ * %Dr <dirent>->rec_len
+ * %Dl <dirent>->name_len
+ * %Dt <dirent>->filetype
+ * %d <dir> inode number
+ * %g <group> integer
+ * %i <ino> inode number
+ * %Is <inode> -> i_size
+ * %IS <inode> -> i_extra_isize
+ * %Ib <inode> -> i_blocks
+ * %Il <inode> -> i_links_count
+ * %Im <inode> -> i_mode
+ * %IM <inode> -> i_mtime
+ * %IF <inode> -> i_faddr
+ * %If <inode> -> i_file_acl
+ * %Id <inode> -> i_dir_acl
+ * %Iu <inode> -> i_uid
+ * %Ig <inode> -> i_gid
+ * %j <ino2> inode number
+ * %m <com_err error message>
+ * %N <num>
+ * %p ext2fs_get_pathname of directory <ino>
+ * %P ext2fs_get_pathname of <dirent>->ino with <ino2> as
+ * the containing directory. (If dirent is NULL
+ * then return the pathname of directory <ino2>)
+ * %q ext2fs_get_pathname of directory <dir>
+ * %Q ext2fs_get_pathname of directory <ino> with <dir> as
+ * the containing directory.
+ * %s <str> miscellaneous string
+ * %S backup superblock
+ * %X <num> hexadecimal format
+ *
+ * The following '@' expansions are supported:
+ *
+ * @a extended attribute
+ * @A error allocating
+ * @b block
+ * @B bitmap
+ * @c compress
+ * @C conflicts with some other fs block
+ * @D deleted
+ * @d directory
+ * @e entry
+ * @E Entry '%Dn' in %p (%i)
+ * @f filesystem
+ * @F for @i %i (%Q) is
+ * @g group
+ * @h HTREE directory inode
+ * @i inode
+ * @I illegal
+ * @j journal
+ * @l lost+found
+ * @L is a link
+ * @m multiply-claimed
+ * @n invalid
+ * @o orphaned
+ * @p problem in
+ * @r root inode
+ * @s should be
+ * @S superblock
+ * @u unattached
+ * @v device
+ * @z zero-length
+ */
+
+
+/*
+ * This structure defines the abbreviations used by the text strings
+ * below. The first character in the string is the index letter. An
+ * abbreviation of the form '@<i>' is expanded by looking up the index
+ * letter <i> in the table below.
+ */
+static const char * const abbrevs[] = {
+ N_("aextended attribute"),
+ N_("Aerror allocating"),
+ N_("bblock"),
+ N_("Bbitmap"),
+ N_("ccompress"),
+ N_("Cconflicts with some other fs @b"),
+ N_("iinode"),
+ N_("Iillegal"),
+ N_("jjournal"),
+ N_("Ddeleted"),
+ N_("ddirectory"),
+ N_("eentry"),
+ N_("E@e '%Dn' in %p (%i)"),
+ N_("ffilesystem"),
+ N_("Ffor @i %i (%Q) is"),
+ N_("ggroup"),
+ N_("hHTREE @d @i"),
+ N_("llost+found"),
+ N_("Lis a link"),
+ N_("mmultiply-claimed"),
+ N_("ninvalid"),
+ N_("oorphaned"),
+ N_("pproblem in"),
+ N_("rroot @i"),
+ N_("sshould be"),
+ N_("Ssuper@b"),
+ N_("uunattached"),
+ N_("vdevice"),
+ N_("zzero-length"),
+ "@@",
+ 0
+ };
+
+/*
+ * Give more user friendly names to the "special" inodes.
+ */
+#define num_special_inodes 11
+static const char * const special_inode_name[] =
+{
+ N_("<The NULL inode>"), /* 0 */
+ N_("<The bad blocks inode>"), /* 1 */
+ "/", /* 2 */
+ N_("<The ACL index inode>"), /* 3 */
+ N_("<The ACL data inode>"), /* 4 */
+ N_("<The boot loader inode>"), /* 5 */
+ N_("<The undelete directory inode>"), /* 6 */
+ N_("<The group descriptor inode>"), /* 7 */
+ N_("<The journal inode>"), /* 8 */
+ N_("<Reserved inode 9>"), /* 9 */
+ N_("<Reserved inode 10>"), /* 10 */
+};
+
+/*
+ * This function does "safe" printing. It will convert non-printable
+ * ASCII characters using '^' and M- notation.
+ */
+static void safe_print(const char *cp, int len)
+{
+ unsigned char ch;
+
+ if (len < 0)
+ len = strlen(cp);
+
+ while (len--) {
+ ch = *cp++;
+ if (ch > 128) {
+ fputs("M-", stdout);
+ ch -= 128;
+ }
+ if ((ch < 32) || (ch == 0x7f)) {
+ fputc('^', stdout);
+ ch ^= 0x40; /* ^@, ^A, ^B; ^? for DEL */
+ }
+ fputc(ch, stdout);
+ }
+}
+
+
+/*
+ * This function prints a pathname, using the ext2fs_get_pathname
+ * function
+ */
+static void print_pathname(ext2_filsys fs, ext2_ino_t dir, ext2_ino_t ino)
+{
+ errcode_t retval;
+ char *path;
+
+ if (!dir && (ino < num_special_inodes)) {
+ fputs(_(special_inode_name[ino]), stdout);
+ return;
+ }
+
+ retval = ext2fs_get_pathname(fs, dir, ino, &path);
+ if (retval)
+ fputs("???", stdout);
+ else {
+ safe_print(path, -1);
+ ext2fs_free_mem(&path);
+ }
+}
+
+static void print_e2fsck_message(e2fsck_t ctx, const char *msg,
+ struct problem_context *pctx, int first);
+/*
+ * This function handles the '@' expansion. We allow recursive
+ * expansion; an @ expression can contain further '@' and '%'
+ * expressions.
+ */
+static void expand_at_expression(e2fsck_t ctx, char ch,
+ struct problem_context *pctx,
+ int *first)
+{
+ const char * const *cpp;
+ const char *str;
+
+ /* Search for the abbreviation */
+ for (cpp = abbrevs; *cpp; cpp++) {
+ if (ch == *cpp[0])
+ break;
+ }
+ if (*cpp) {
+ str = _(*cpp) + 1;
+ if (*first && islower(*str)) {
+ *first = 0;
+ fputc(toupper(*str++), stdout);
+ }
+ print_e2fsck_message(ctx, str, pctx, *first);
+ } else
+ printf("@%c", ch);
+}
+
+/*
+ * This function expands '%IX' expressions
+ */
+static void expand_inode_expression(char ch,
+ struct problem_context *ctx)
+{
+ struct ext2_inode *inode;
+ struct ext2_inode_large *large_inode;
+ char * time_str;
+ time_t t;
+ int do_gmt = -1;
+
+ if (!ctx || !ctx->inode)
+ goto no_inode;
+
+ inode = ctx->inode;
+ large_inode = (struct ext2_inode_large *) inode;
+
+ switch (ch) {
+ case 's':
+ if (LINUX_S_ISDIR(inode->i_mode))
+ printf("%u", inode->i_size);
+ else {
+ printf("%"PRIu64, (inode->i_size |
+ ((uint64_t) inode->i_size_high << 32)));
+ }
+ break;
+ case 'S':
+ printf("%u", large_inode->i_extra_isize);
+ break;
+ case 'b':
+ printf("%u", inode->i_blocks);
+ break;
+ case 'l':
+ printf("%d", inode->i_links_count);
+ break;
+ case 'm':
+ printf("0%o", inode->i_mode);
+ break;
+ case 'M':
+ /* The diet libc doesn't respect the TZ environemnt variable */
+ if (do_gmt == -1) {
+ time_str = getenv("TZ");
+ if (!time_str)
+ time_str = "";
+ do_gmt = !strcmp(time_str, "GMT");
+ }
+ t = inode->i_mtime;
+ time_str = asctime(do_gmt ? gmtime(&t) : localtime(&t));
+ printf("%.24s", time_str);
+ break;
+ case 'F':
+ printf("%u", inode->i_faddr);
+ break;
+ case 'f':
+ printf("%u", inode->i_file_acl);
+ break;
+ case 'd':
+ printf("%u", (LINUX_S_ISDIR(inode->i_mode) ?
+ inode->i_dir_acl : 0));
+ break;
+ case 'u':
+ printf("%d", (inode->i_uid |
+ (inode->osd2.linux2.l_i_uid_high << 16)));
+ break;
+ case 'g':
+ printf("%d", (inode->i_gid |
+ (inode->osd2.linux2.l_i_gid_high << 16)));
+ break;
+ default:
+ no_inode:
+ printf("%%I%c", ch);
+ break;
+ }
+}
+
+/*
+ * This function expands '%dX' expressions
+ */
+static void expand_dirent_expression(char ch,
+ struct problem_context *ctx)
+{
+ struct ext2_dir_entry *dirent;
+ int len;
+
+ if (!ctx || !ctx->dirent)
+ goto no_dirent;
+
+ dirent = ctx->dirent;
+
+ switch (ch) {
+ case 'i':
+ printf("%u", dirent->inode);
+ break;
+ case 'n':
+ len = dirent->name_len & 0xFF;
+ if (len > EXT2_NAME_LEN)
+ len = EXT2_NAME_LEN;
+ if (len > dirent->rec_len)
+ len = dirent->rec_len;
+ safe_print(dirent->name, len);
+ break;
+ case 'r':
+ printf("%u", dirent->rec_len);
+ break;
+ case 'l':
+ printf("%u", dirent->name_len & 0xFF);
+ break;
+ case 't':
+ printf("%u", dirent->name_len >> 8);
+ break;
+ default:
+ no_dirent:
+ printf("%%D%c", ch);
+ break;
+ }
+}
+
+static void expand_percent_expression(ext2_filsys fs, char ch,
+ struct problem_context *ctx)
+{
+ if (!ctx)
+ goto no_context;
+
+ switch (ch) {
+ case '%':
+ fputc('%', stdout);
+ break;
+ case 'b':
+ printf("%u", ctx->blk);
+ break;
+ case 'B':
+ printf("%"PRIi64, ctx->blkcount);
+ break;
+ case 'c':
+ printf("%u", ctx->blk2);
+ break;
+ case 'd':
+ printf("%u", ctx->dir);
+ break;
+ case 'g':
+ printf("%d", ctx->group);
+ break;
+ case 'i':
+ printf("%u", ctx->ino);
+ break;
+ case 'j':
+ printf("%u", ctx->ino2);
+ break;
+ case 'm':
+ printf("%s", error_message(ctx->errcode));
+ break;
+ case 'N':
+ printf("%"PRIi64, ctx->num);
+ break;
+ case 'p':
+ print_pathname(fs, ctx->ino, 0);
+ break;
+ case 'P':
+ print_pathname(fs, ctx->ino2,
+ ctx->dirent ? ctx->dirent->inode : 0);
+ break;
+ case 'q':
+ print_pathname(fs, ctx->dir, 0);
+ break;
+ case 'Q':
+ print_pathname(fs, ctx->dir, ctx->ino);
+ break;
+ case 'S':
+ printf("%d", get_backup_sb(NULL, fs, NULL, NULL));
+ break;
+ case 's':
+ printf("%s", ctx->str ? ctx->str : "NULL");
+ break;
+ case 'X':
+ printf("0x%"PRIi64, ctx->num);
+ break;
+ default:
+ no_context:
+ printf("%%%c", ch);
+ break;
+ }
+}
+
+
+static void print_e2fsck_message(e2fsck_t ctx, const char *msg,
+ struct problem_context *pctx, int first)
+{
+ ext2_filsys fs = ctx->fs;
+ const char * cp;
+ int i;
+
+ e2fsck_clear_progbar(ctx);
+ for (cp = msg; *cp; cp++) {
+ if (cp[0] == '@') {
+ cp++;
+ expand_at_expression(ctx, *cp, pctx, &first);
+ } else if (cp[0] == '%' && cp[1] == 'I') {
+ cp += 2;
+ expand_inode_expression(*cp, pctx);
+ } else if (cp[0] == '%' && cp[1] == 'D') {
+ cp += 2;
+ expand_dirent_expression(*cp, pctx);
+ } else if ((cp[0] == '%')) {
+ cp++;
+ expand_percent_expression(fs, *cp, pctx);
+ } else {
+ for (i=0; cp[i]; i++)
+ if ((cp[i] == '@') || cp[i] == '%')
+ break;
+ printf("%.*s", i, cp);
+ cp += i-1;
+ }
+ first = 0;
+ }
+}
+
+
+/*
+ * region.c --- code which manages allocations within a region.
+ */
+
+struct region_el {
+ region_addr_t start;
+ region_addr_t end;
+ struct region_el *next;
+};
+
+struct region_struct {
+ region_addr_t min;
+ region_addr_t max;
+ struct region_el *allocated;
+};
+
+static region_t region_create(region_addr_t min, region_addr_t max)
+{
+ region_t region;
+
+ region = malloc(sizeof(struct region_struct));
+ if (!region)
+ return NULL;
+ memset(region, 0, sizeof(struct region_struct));
+ region->min = min;
+ region->max = max;
+ return region;
+}
+
+static void region_free(region_t region)
+{
+ struct region_el *r, *next;
+
+ for (r = region->allocated; r; r = next) {
+ next = r->next;
+ free(r);
+ }
+ memset(region, 0, sizeof(struct region_struct));
+ free(region);
+}
+
+static int region_allocate(region_t region, region_addr_t start, int n)
+{
+ struct region_el *r, *new_region, *prev, *next;
+ region_addr_t end;
+
+ end = start+n;
+ if ((start < region->min) || (end > region->max))
+ return -1;
+ if (n == 0)
+ return 1;
+
+ /*
+ * Search through the linked list. If we find that it
+ * conflicts witih something that's already allocated, return
+ * 1; if we can find an existing region which we can grow, do
+ * so. Otherwise, stop when we find the appropriate place
+ * insert a new region element into the linked list.
+ */
+ for (r = region->allocated, prev=NULL; r; prev = r, r = r->next) {
+ if (((start >= r->start) && (start < r->end)) ||
+ ((end > r->start) && (end <= r->end)) ||
+ ((start <= r->start) && (end >= r->end)))
+ return 1;
+ if (end == r->start) {
+ r->start = start;
+ return 0;
+ }
+ if (start == r->end) {
+ if ((next = r->next)) {
+ if (end > next->start)
+ return 1;
+ if (end == next->start) {
+ r->end = next->end;
+ r->next = next->next;
+ free(next);
+ return 0;
+ }
+ }
+ r->end = end;
+ return 0;
+ }
+ if (start < r->start)
+ break;
+ }
+ /*
+ * Insert a new region element structure into the linked list
+ */
+ new_region = malloc(sizeof(struct region_el));
+ if (!new_region)
+ return -1;
+ new_region->start = start;
+ new_region->end = start + n;
+ new_region->next = r;
+ if (prev)
+ prev->next = new_region;
+ else
+ region->allocated = new_region;
+ return 0;
+}
+
+/*
+ * pass1.c -- pass #1 of e2fsck: sequential scan of the inode table
+ *
+ * Pass 1 of e2fsck iterates over all the inodes in the filesystems,
+ * and applies the following tests to each inode:
+ *
+ * - The mode field of the inode must be legal.
+ * - The size and block count fields of the inode are correct.
+ * - A data block must not be used by another inode
+ *
+ * Pass 1 also gathers the collects the following information:
+ *
+ * - A bitmap of which inodes are in use. (inode_used_map)
+ * - A bitmap of which inodes are directories. (inode_dir_map)
+ * - A bitmap of which inodes are regular files. (inode_reg_map)
+ * - A bitmap of which inodes have bad fields. (inode_bad_map)
+ * - A bitmap of which inodes are imagic inodes. (inode_imagic_map)
+ * - A bitmap of which blocks are in use. (block_found_map)
+ * - A bitmap of which blocks are in use by two inodes (block_dup_map)
+ * - The data blocks of the directory inodes. (dir_map)
+ *
+ * Pass 1 is designed to stash away enough information so that the
+ * other passes should not need to read in the inode information
+ * during the normal course of a filesystem check. (Althogh if an
+ * inconsistency is detected, other passes may need to read in an
+ * inode to fix it.)
+ *
+ * Note that pass 1B will be invoked if there are any duplicate blocks
+ * found.
+ */
+
+
+static int process_block(ext2_filsys fs, blk_t *blocknr,
+ e2_blkcnt_t blockcnt, blk_t ref_blk,
+ int ref_offset, void *priv_data);
+static int process_bad_block(ext2_filsys fs, blk_t *block_nr,
+ e2_blkcnt_t blockcnt, blk_t ref_blk,
+ int ref_offset, void *priv_data);
+static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
+ char *block_buf);
+static void mark_table_blocks(e2fsck_t ctx);
+static void alloc_imagic_map(e2fsck_t ctx);
+static void mark_inode_bad(e2fsck_t ctx, ino_t ino);
+static void handle_fs_bad_blocks(e2fsck_t ctx);
+static void process_inodes(e2fsck_t ctx, char *block_buf);
+static int process_inode_cmp(const void *a, const void *b);
+static errcode_t scan_callback(ext2_filsys fs,
+ dgrp_t group, void * priv_data);
+static void adjust_extattr_refcount(e2fsck_t ctx, ext2_refcount_t refcount,
+ char *block_buf, int adjust_sign);
+/* static char *describe_illegal_block(ext2_filsys fs, blk_t block); */
+
+static void e2fsck_write_inode_full(e2fsck_t ctx, unsigned long ino,
+ struct ext2_inode * inode, int bufsize,
+ const char *proc);
+
+struct process_block_struct_1 {
+ ext2_ino_t ino;
+ unsigned is_dir:1, is_reg:1, clear:1, suppress:1,
+ fragmented:1, compressed:1, bbcheck:1;
+ blk_t num_blocks;
+ blk_t max_blocks;
+ e2_blkcnt_t last_block;
+ int num_illegal_blocks;
+ blk_t previous_block;
+ struct ext2_inode *inode;
+ struct problem_context *pctx;
+ ext2fs_block_bitmap fs_meta_blocks;
+ e2fsck_t ctx;
+};
+
+struct process_inode_block {
+ ext2_ino_t ino;
+ struct ext2_inode inode;
+};
+
+struct scan_callback_struct {
+ e2fsck_t ctx;
+ char *block_buf;
+};
+
+/*
+ * For the inodes to process list.
+ */
+static struct process_inode_block *inodes_to_process;
+static int process_inode_count;
+
+static __u64 ext2_max_sizes[EXT2_MAX_BLOCK_LOG_SIZE -
+ EXT2_MIN_BLOCK_LOG_SIZE + 1];
+
+/*
+ * Free all memory allocated by pass1 in preparation for restarting
+ * things.
+ */
+static void unwind_pass1(void)
+{
+ ext2fs_free_mem(&inodes_to_process);
+}
+
+/*
+ * Check to make sure a device inode is real. Returns 1 if the device
+ * checks out, 0 if not.
+ *
+ * Note: this routine is now also used to check FIFO's and Sockets,
+ * since they have the same requirement; the i_block fields should be
+ * zero.
+ */
+static int
+e2fsck_pass1_check_device_inode(ext2_filsys fs, struct ext2_inode *inode)
+{
+ int i;
+
+ /*
+ * If i_blocks is non-zero, or the index flag is set, then
+ * this is a bogus device/fifo/socket
+ */
+ if ((ext2fs_inode_data_blocks(fs, inode) != 0) ||
+ (inode->i_flags & EXT2_INDEX_FL))
+ return 0;
+
+ /*
+ * We should be able to do the test below all the time, but
+ * because the kernel doesn't forcibly clear the device
+ * inode's additional i_block fields, there are some rare
+ * occasions when a legitimate device inode will have non-zero
+ * additional i_block fields. So for now, we only complain
+ * when the immutable flag is set, which should never happen
+ * for devices. (And that's when the problem is caused, since
+ * you can't set or clear immutable flags for devices.) Once
+ * the kernel has been fixed we can change this...
+ */
+ if (inode->i_flags & (EXT2_IMMUTABLE_FL | EXT2_APPEND_FL)) {
+ for (i=4; i < EXT2_N_BLOCKS; i++)
+ if (inode->i_block[i])
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * Check to make sure a symlink inode is real. Returns 1 if the symlink
+ * checks out, 0 if not.
+ */
+static int
+e2fsck_pass1_check_symlink(ext2_filsys fs, struct ext2_inode *inode, char *buf)
+{
+ unsigned int len;
+ int i;
+ blk_t blocks;
+
+ if ((inode->i_size_high || inode->i_size == 0) ||
+ (inode->i_flags & EXT2_INDEX_FL))
+ return 0;
+
+ blocks = ext2fs_inode_data_blocks(fs, inode);
+ if (blocks) {
+ if ((inode->i_size >= fs->blocksize) ||
+ (blocks != fs->blocksize >> 9) ||
+ (inode->i_block[0] < fs->super->s_first_data_block) ||
+ (inode->i_block[0] >= fs->super->s_blocks_count))
+ return 0;
+
+ for (i = 1; i < EXT2_N_BLOCKS; i++)
+ if (inode->i_block[i])
+ return 0;
+
+ if (io_channel_read_blk(fs->io, inode->i_block[0], 1, buf))
+ return 0;
+
+ len = strnlen(buf, fs->blocksize);
+ if (len == fs->blocksize)
+ return 0;
+ } else {
+ if (inode->i_size >= sizeof(inode->i_block))
+ return 0;
+
+ len = strnlen((char *)inode->i_block, sizeof(inode->i_block));
+ if (len == sizeof(inode->i_block))
+ return 0;
+ }
+ if (len != inode->i_size)
+ return 0;
+ return 1;
+}
+
+/*
+ * If the immutable (or append-only) flag is set on the inode, offer
+ * to clear it.
+ */
+#define BAD_SPECIAL_FLAGS (EXT2_IMMUTABLE_FL | EXT2_APPEND_FL)
+static void check_immutable(e2fsck_t ctx, struct problem_context *pctx)
+{
+ if (!(pctx->inode->i_flags & BAD_SPECIAL_FLAGS))
+ return;
+
+ if (!fix_problem(ctx, PR_1_SET_IMMUTABLE, pctx))
+ return;
+
+ pctx->inode->i_flags &= ~BAD_SPECIAL_FLAGS;
+ e2fsck_write_inode(ctx, pctx->ino, pctx->inode, "pass1");
+}
+
+/*
+ * If device, fifo or socket, check size is zero -- if not offer to
+ * clear it
+ */
+static void check_size(e2fsck_t ctx, struct problem_context *pctx)
+{
+ struct ext2_inode *inode = pctx->inode;
+
+ if ((inode->i_size == 0) && (inode->i_size_high == 0))
+ return;
+
+ if (!fix_problem(ctx, PR_1_SET_NONZSIZE, pctx))
+ return;
+
+ inode->i_size = 0;
+ inode->i_size_high = 0;
+ e2fsck_write_inode(ctx, pctx->ino, pctx->inode, "pass1");
+}
+
+static void check_ea_in_inode(e2fsck_t ctx, struct problem_context *pctx)
+{
+ struct ext2_super_block *sb = ctx->fs->super;
+ struct ext2_inode_large *inode;
+ struct ext2_ext_attr_entry *entry;
+ char *start, *end;
+ int storage_size, remain, offs;
+ int problem = 0;
+
+ inode = (struct ext2_inode_large *) pctx->inode;
+ storage_size = EXT2_INODE_SIZE(ctx->fs->super) - EXT2_GOOD_OLD_INODE_SIZE -
+ inode->i_extra_isize;
+ start = ((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
+ inode->i_extra_isize + sizeof(__u32);
+ end = (char *) inode + EXT2_INODE_SIZE(ctx->fs->super);
+ entry = (struct ext2_ext_attr_entry *) start;
+
+ /* scan all entry's headers first */
+
+ /* take finish entry 0UL into account */
+ remain = storage_size - sizeof(__u32);
+ offs = end - start;
+
+ while (!EXT2_EXT_IS_LAST_ENTRY(entry)) {
+
+ /* header eats this space */
+ remain -= sizeof(struct ext2_ext_attr_entry);
+
+ /* is attribute name valid? */
+ if (EXT2_EXT_ATTR_SIZE(entry->e_name_len) > remain) {
+ pctx->num = entry->e_name_len;
+ problem = PR_1_ATTR_NAME_LEN;
+ goto fix;
+ }
+
+ /* attribute len eats this space */
+ remain -= EXT2_EXT_ATTR_SIZE(entry->e_name_len);
+
+ /* check value size */
+ if (entry->e_value_size == 0 || entry->e_value_size > remain) {
+ pctx->num = entry->e_value_size;
+ problem = PR_1_ATTR_VALUE_SIZE;
+ goto fix;
+ }
+
+ /* check value placement */
+ if (entry->e_value_offs +
+ EXT2_XATTR_SIZE(entry->e_value_size) != offs) {
+ printf("(entry->e_value_offs + entry->e_value_size: %d, offs: %d)\n", entry->e_value_offs + entry->e_value_size, offs);
+ pctx->num = entry->e_value_offs;
+ problem = PR_1_ATTR_VALUE_OFFSET;
+ goto fix;
+ }
+
+ /* e_value_block must be 0 in inode's ea */
+ if (entry->e_value_block != 0) {
+ pctx->num = entry->e_value_block;
+ problem = PR_1_ATTR_VALUE_BLOCK;
+ goto fix;
+ }
+
+ /* e_hash must be 0 in inode's ea */
+ if (entry->e_hash != 0) {
+ pctx->num = entry->e_hash;
+ problem = PR_1_ATTR_HASH;
+ goto fix;
+ }
+
+ remain -= entry->e_value_size;
+ offs -= EXT2_XATTR_SIZE(entry->e_value_size);
+
+ entry = EXT2_EXT_ATTR_NEXT(entry);
+ }
+fix:
+ /*
+ * it seems like a corruption. it's very unlikely we could repair
+ * EA(s) in automatic fashion -bzzz
+ */
+ if (problem == 0 || !fix_problem(ctx, problem, pctx))
+ return;
+
+ /* simple remove all possible EA(s) */
+ *((__u32 *)start) = 0UL;
+ e2fsck_write_inode_full(ctx, pctx->ino, (struct ext2_inode *)inode,
+ EXT2_INODE_SIZE(sb), "pass1");
+}
+
+static void check_inode_extra_space(e2fsck_t ctx, struct problem_context *pctx)
+{
+ struct ext2_super_block *sb = ctx->fs->super;
+ struct ext2_inode_large *inode;
+ __u32 *eamagic;
+ int min, max;
+
+ inode = (struct ext2_inode_large *) pctx->inode;
+ if (EXT2_INODE_SIZE(sb) == EXT2_GOOD_OLD_INODE_SIZE) {
+ /* this isn't large inode. so, nothing to check */
+ return;
+ }
+
+ /* i_extra_isize must cover i_extra_isize + i_pad1 at least */
+ min = sizeof(inode->i_extra_isize) + sizeof(inode->i_pad1);
+ max = EXT2_INODE_SIZE(sb) - EXT2_GOOD_OLD_INODE_SIZE;
+ /*
+ * For now we will allow i_extra_isize to be 0, but really
+ * implementations should never allow i_extra_isize to be 0
+ */
+ if (inode->i_extra_isize &&
+ (inode->i_extra_isize < min || inode->i_extra_isize > max)) {
+ if (!fix_problem(ctx, PR_1_EXTRA_ISIZE, pctx))
+ return;
+ inode->i_extra_isize = min;
+ e2fsck_write_inode_full(ctx, pctx->ino, pctx->inode,
+ EXT2_INODE_SIZE(sb), "pass1");
+ return;
+ }
+
+ eamagic = (__u32 *) (((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
+ inode->i_extra_isize);
+ if (*eamagic == EXT2_EXT_ATTR_MAGIC) {
+ /* it seems inode has an extended attribute(s) in body */
+ check_ea_in_inode(ctx, pctx);
+ }
+}
+
+static void e2fsck_pass1(e2fsck_t ctx)
+{
+ int i;
+ __u64 max_sizes;
+ ext2_filsys fs = ctx->fs;
+ ext2_ino_t ino;
+ struct ext2_inode *inode;
+ ext2_inode_scan scan;
+ char *block_buf;
+ unsigned char frag, fsize;
+ struct problem_context pctx;
+ struct scan_callback_struct scan_struct;
+ struct ext2_super_block *sb = ctx->fs->super;
+ int imagic_fs;
+ int busted_fs_time = 0;
+ int inode_size;
+
+ clear_problem_context(&pctx);
+
+ if (!(ctx->options & E2F_OPT_PREEN))
+ fix_problem(ctx, PR_1_PASS_HEADER, &pctx);
+
+ if ((fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) &&
+ !(ctx->options & E2F_OPT_NO)) {
+ if (ext2fs_u32_list_create(&ctx->dirs_to_hash, 50))
+ ctx->dirs_to_hash = 0;
+ }
+
+ /* Pass 1 */
+
+#define EXT2_BPP(bits) (1ULL << ((bits) - 2))
+
+ for (i = EXT2_MIN_BLOCK_LOG_SIZE; i <= EXT2_MAX_BLOCK_LOG_SIZE; i++) {
+ max_sizes = EXT2_NDIR_BLOCKS + EXT2_BPP(i);
+ max_sizes = max_sizes + EXT2_BPP(i) * EXT2_BPP(i);
+ max_sizes = max_sizes + EXT2_BPP(i) * EXT2_BPP(i) * EXT2_BPP(i);
+ max_sizes = (max_sizes * (1UL << i)) - 1;
+ ext2_max_sizes[i - EXT2_MIN_BLOCK_LOG_SIZE] = max_sizes;
+ }
+#undef EXT2_BPP
+
+ imagic_fs = (sb->s_feature_compat & EXT2_FEATURE_COMPAT_IMAGIC_INODES);
+
+ /*
+ * Allocate bitmaps structures
+ */
+ pctx.errcode = ext2fs_allocate_inode_bitmap(fs, _("in-use inode map"),
+ &ctx->inode_used_map);
+ if (pctx.errcode) {
+ pctx.num = 1;
+ fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
+ ctx->flags |= E2F_FLAG_ABORT;
+ return;
+ }
+ pctx.errcode = ext2fs_allocate_inode_bitmap(fs,
+ _("directory inode map"), &ctx->inode_dir_map);
+ if (pctx.errcode) {
+ pctx.num = 2;
+ fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
+ ctx->flags |= E2F_FLAG_ABORT;
+ return;
+ }
+ pctx.errcode = ext2fs_allocate_inode_bitmap(fs,
+ _("regular file inode map"), &ctx->inode_reg_map);
+ if (pctx.errcode) {
+ pctx.num = 6;
+ fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
+ ctx->flags |= E2F_FLAG_ABORT;
+ return;
+ }
+ pctx.errcode = ext2fs_allocate_block_bitmap(fs, _("in-use block map"),
+ &ctx->block_found_map);
+ if (pctx.errcode) {
+ pctx.num = 1;
+ fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR, &pctx);
+ ctx->flags |= E2F_FLAG_ABORT;
+ return;
+ }
+ pctx.errcode = ext2fs_create_icount2(fs, 0, 0, 0,
+ &ctx->inode_link_info);
+ if (pctx.errcode) {
+ fix_problem(ctx, PR_1_ALLOCATE_ICOUNT, &pctx);
+ ctx->flags |= E2F_FLAG_ABORT;
+ return;
+ }
+ inode_size = EXT2_INODE_SIZE(fs->super);
+ inode = (struct ext2_inode *)
+ e2fsck_allocate_memory(ctx, inode_size, "scratch inode");
+
+ inodes_to_process = (struct process_inode_block *)
+ e2fsck_allocate_memory(ctx,
+ (ctx->process_inode_size *
+ sizeof(struct process_inode_block)),
+ "array of inodes to process");
+ process_inode_count = 0;
+
+ pctx.errcode = ext2fs_init_dblist(fs, 0);
+ if (pctx.errcode) {
+ fix_problem(ctx, PR_1_ALLOCATE_DBCOUNT, &pctx);
+ ctx->flags |= E2F_FLAG_ABORT;
+ return;
+ }
+
+ /*
+ * If the last orphan field is set, clear it, since the pass1
+ * processing will automatically find and clear the orphans.
+ * In the future, we may want to try using the last_orphan
+ * linked list ourselves, but for now, we clear it so that the
+ * ext3 mount code won't get confused.
+ */
+ if (!(ctx->options & E2F_OPT_READONLY)) {
+ if (fs->super->s_last_orphan) {
+ fs->super->s_last_orphan = 0;
+ ext2fs_mark_super_dirty(fs);
+ }
+ }
+
+ mark_table_blocks(ctx);
+ block_buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize * 3,
+ "block interate buffer");
+ e2fsck_use_inode_shortcuts(ctx, 1);
+ ehandler_operation(_("doing inode scan"));
+ pctx.errcode = ext2fs_open_inode_scan(fs, ctx->inode_buffer_blocks,
+ &scan);
+ if (pctx.errcode) {
+ fix_problem(ctx, PR_1_ISCAN_ERROR, &pctx);
+ ctx->flags |= E2F_FLAG_ABORT;
+ return;
+ }
+ ext2fs_inode_scan_flags(scan, EXT2_SF_SKIP_MISSING_ITABLE, 0);
+ ctx->stashed_inode = inode;
+ scan_struct.ctx = ctx;
+ scan_struct.block_buf = block_buf;
+ ext2fs_set_inode_callback(scan, scan_callback, &scan_struct);
+ if (ctx->progress)
+ if ((ctx->progress)(ctx, 1, 0, ctx->fs->group_desc_count))
+ return;
+ if ((fs->super->s_wtime < fs->super->s_inodes_count) ||
+ (fs->super->s_mtime < fs->super->s_inodes_count))
+ busted_fs_time = 1;
+
+ while (1) {
+ pctx.errcode = ext2fs_get_next_inode_full(scan, &ino,
+ inode, inode_size);
+ if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+ return;
+ if (pctx.errcode == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE) {
+ continue;
+ }
+ if (pctx.errcode) {
+ fix_problem(ctx, PR_1_ISCAN_ERROR, &pctx);
+ ctx->flags |= E2F_FLAG_ABORT;
+ return;
+ }
+ if (!ino)
+ break;
+ pctx.ino = ino;
+ pctx.inode = inode;
+ ctx->stashed_ino = ino;
+ if (inode->i_links_count) {
+ pctx.errcode = ext2fs_icount_store(ctx->inode_link_info,
+ ino, inode->i_links_count);
+ if (pctx.errcode) {
+ pctx.num = inode->i_links_count;
+ fix_problem(ctx, PR_1_ICOUNT_STORE, &pctx);
+ ctx->flags |= E2F_FLAG_ABORT;
+ return;
+ }
+ }
+ if (ino == EXT2_BAD_INO) {
+ struct process_block_struct_1 pb;
+
+ pctx.errcode = ext2fs_copy_bitmap(ctx->block_found_map,
+ &pb.fs_meta_blocks);
+ if (pctx.errcode) {
+ pctx.num = 4;
+ fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR, &pctx);
+ ctx->flags |= E2F_FLAG_ABORT;
+ return;
+ }
+ pb.ino = EXT2_BAD_INO;
+ pb.num_blocks = pb.last_block = 0;
+ pb.num_illegal_blocks = 0;
+ pb.suppress = 0; pb.clear = 0; pb.is_dir = 0;
+ pb.is_reg = 0; pb.fragmented = 0; pb.bbcheck = 0;
+ pb.inode = inode;
+ pb.pctx = &pctx;
+ pb.ctx = ctx;
+ pctx.errcode = ext2fs_block_iterate2(fs, ino, 0,
+ block_buf, process_bad_block, &pb);
+ ext2fs_free_block_bitmap(pb.fs_meta_blocks);
+ if (pctx.errcode) {
+ fix_problem(ctx, PR_1_BLOCK_ITERATE, &pctx);
+ ctx->flags |= E2F_FLAG_ABORT;
+ return;
+ }
+ if (pb.bbcheck)
+ if (!fix_problem(ctx, PR_1_BBINODE_BAD_METABLOCK_PROMPT, &pctx)) {
+ ctx->flags |= E2F_FLAG_ABORT;
+ return;
+ }
+ ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino);
+ clear_problem_context(&pctx);
+ continue;
+ } else if (ino == EXT2_ROOT_INO) {
+ /*
+ * Make sure the root inode is a directory; if
+ * not, offer to clear it. It will be
+ * regnerated in pass #3.
+ */
+ if (!LINUX_S_ISDIR(inode->i_mode)) {
+ if (fix_problem(ctx, PR_1_ROOT_NO_DIR, &pctx)) {
+ inode->i_dtime = time(0);
+ inode->i_links_count = 0;
+ ext2fs_icount_store(ctx->inode_link_info,
+ ino, 0);
+ e2fsck_write_inode(ctx, ino, inode,
+ "pass1");
+ }
+
+ }
+ /*
+ * If dtime is set, offer to clear it. mke2fs
+ * version 0.2b created filesystems with the
+ * dtime field set for the root and lost+found
+ * directories. We won't worry about
+ * /lost+found, since that can be regenerated
+ * easily. But we will fix the root directory
+ * as a special case.
+ */
+ if (inode->i_dtime && inode->i_links_count) {
+ if (fix_problem(ctx, PR_1_ROOT_DTIME, &pctx)) {
+ inode->i_dtime = 0;
+ e2fsck_write_inode(ctx, ino, inode,
+ "pass1");
+ }
+ }
+ } else if (ino == EXT2_JOURNAL_INO) {
+ ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino);
+ if (fs->super->s_journal_inum == EXT2_JOURNAL_INO) {
+ if (!LINUX_S_ISREG(inode->i_mode) &&
+ fix_problem(ctx, PR_1_JOURNAL_BAD_MODE,
+ &pctx)) {
+ inode->i_mode = LINUX_S_IFREG;
+ e2fsck_write_inode(ctx, ino, inode,
+ "pass1");
+ }
+ check_blocks(ctx, &pctx, block_buf);
+ continue;
+ }
+ if ((inode->i_links_count || inode->i_blocks ||
+ inode->i_blocks || inode->i_block[0]) &&
+ fix_problem(ctx, PR_1_JOURNAL_INODE_NOT_CLEAR,
+ &pctx)) {
+ memset(inode, 0, inode_size);
+ ext2fs_icount_store(ctx->inode_link_info,
+ ino, 0);
+ e2fsck_write_inode_full(ctx, ino, inode,
+ inode_size, "pass1");
+ }
+ } else if (ino < EXT2_FIRST_INODE(fs->super)) {
+ int problem = 0;
+
+ ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino);
+ if (ino == EXT2_BOOT_LOADER_INO) {
+ if (LINUX_S_ISDIR(inode->i_mode))
+ problem = PR_1_RESERVED_BAD_MODE;
+ } else if (ino == EXT2_RESIZE_INO) {
+ if (inode->i_mode &&
+ !LINUX_S_ISREG(inode->i_mode))
+ problem = PR_1_RESERVED_BAD_MODE;
+ } else {
+ if (inode->i_mode != 0)
+ problem = PR_1_RESERVED_BAD_MODE;
+ }
+ if (problem) {
+ if (fix_problem(ctx, problem, &pctx)) {
+ inode->i_mode = 0;
+ e2fsck_write_inode(ctx, ino, inode,
+ "pass1");
+ }
+ }
+ check_blocks(ctx, &pctx, block_buf);
+ continue;
+ }
+ /*
+ * Check for inodes who might have been part of the
+ * orphaned list linked list. They should have gotten
+ * dealt with by now, unless the list had somehow been
+ * corrupted.
+ *
+ * FIXME: In the future, inodes which are still in use
+ * (and which are therefore) pending truncation should
+ * be handled specially. Right now we just clear the
+ * dtime field, and the normal e2fsck handling of
+ * inodes where i_size and the inode blocks are
+ * inconsistent is to fix i_size, instead of releasing
+ * the extra blocks. This won't catch the inodes that
+ * was at the end of the orphan list, but it's better
+ * than nothing. The right answer is that there
+ * shouldn't be any bugs in the orphan list handling. :-)
+ */
+ if (inode->i_dtime && !busted_fs_time &&
+ inode->i_dtime < ctx->fs->super->s_inodes_count) {
+ if (fix_problem(ctx, PR_1_LOW_DTIME, &pctx)) {
+ inode->i_dtime = inode->i_links_count ?
+ 0 : time(0);
+ e2fsck_write_inode(ctx, ino, inode,
+ "pass1");
+ }
+ }
+
+ /*
+ * This code assumes that deleted inodes have
+ * i_links_count set to 0.
+ */
+ if (!inode->i_links_count) {
+ if (!inode->i_dtime && inode->i_mode) {
+ if (fix_problem(ctx,
+ PR_1_ZERO_DTIME, &pctx)) {
+ inode->i_dtime = time(0);
+ e2fsck_write_inode(ctx, ino, inode,
+ "pass1");
+ }
+ }
+ continue;
+ }
+ /*
+ * n.b. 0.3c ext2fs code didn't clear i_links_count for
+ * deleted files. Oops.
+ *
+ * Since all new ext2 implementations get this right,
+ * we now assume that the case of non-zero
+ * i_links_count and non-zero dtime means that we
+ * should keep the file, not delete it.
+ *
+ */
+ if (inode->i_dtime) {
+ if (fix_problem(ctx, PR_1_SET_DTIME, &pctx)) {
+ inode->i_dtime = 0;
+ e2fsck_write_inode(ctx, ino, inode, "pass1");
+ }
+ }
+
+ ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino);
+ switch (fs->super->s_creator_os) {
+ case EXT2_OS_LINUX:
+ frag = inode->osd2.linux2.l_i_frag;
+ fsize = inode->osd2.linux2.l_i_fsize;
+ break;
+ case EXT2_OS_HURD:
+ frag = inode->osd2.hurd2.h_i_frag;
+ fsize = inode->osd2.hurd2.h_i_fsize;
+ break;
+ case EXT2_OS_MASIX:
+ frag = inode->osd2.masix2.m_i_frag;
+ fsize = inode->osd2.masix2.m_i_fsize;
+ break;
+ default:
+ frag = fsize = 0;
+ }
+
+ if (inode->i_faddr || frag || fsize ||
+ (LINUX_S_ISDIR(inode->i_mode) && inode->i_dir_acl))
+ mark_inode_bad(ctx, ino);
+ if (inode->i_flags & EXT2_IMAGIC_FL) {
+ if (imagic_fs) {
+ if (!ctx->inode_imagic_map)
+ alloc_imagic_map(ctx);
+ ext2fs_mark_inode_bitmap(ctx->inode_imagic_map,
+ ino);
+ } else {
+ if (fix_problem(ctx, PR_1_SET_IMAGIC, &pctx)) {
+ inode->i_flags &= ~EXT2_IMAGIC_FL;
+ e2fsck_write_inode(ctx, ino,
+ inode, "pass1");
+ }
+ }
+ }
+
+ check_inode_extra_space(ctx, &pctx);
+
+ if (LINUX_S_ISDIR(inode->i_mode)) {
+ ext2fs_mark_inode_bitmap(ctx->inode_dir_map, ino);
+ e2fsck_add_dir_info(ctx, ino, 0);
+ ctx->fs_directory_count++;
+ } else if (LINUX_S_ISREG (inode->i_mode)) {
+ ext2fs_mark_inode_bitmap(ctx->inode_reg_map, ino);
+ ctx->fs_regular_count++;
+ } else if (LINUX_S_ISCHR (inode->i_mode) &&
+ e2fsck_pass1_check_device_inode(fs, inode)) {
+ check_immutable(ctx, &pctx);
+ check_size(ctx, &pctx);
+ ctx->fs_chardev_count++;
+ } else if (LINUX_S_ISBLK (inode->i_mode) &&
+ e2fsck_pass1_check_device_inode(fs, inode)) {
+ check_immutable(ctx, &pctx);
+ check_size(ctx, &pctx);
+ ctx->fs_blockdev_count++;
+ } else if (LINUX_S_ISLNK (inode->i_mode) &&
+ e2fsck_pass1_check_symlink(fs, inode, block_buf)) {
+ check_immutable(ctx, &pctx);
+ ctx->fs_symlinks_count++;
+ if (ext2fs_inode_data_blocks(fs, inode) == 0) {
+ ctx->fs_fast_symlinks_count++;
+ check_blocks(ctx, &pctx, block_buf);
+ continue;
+ }
+ }
+ else if (LINUX_S_ISFIFO (inode->i_mode) &&
+ e2fsck_pass1_check_device_inode(fs, inode)) {
+ check_immutable(ctx, &pctx);
+ check_size(ctx, &pctx);
+ ctx->fs_fifo_count++;
+ } else if ((LINUX_S_ISSOCK (inode->i_mode)) &&
+ e2fsck_pass1_check_device_inode(fs, inode)) {
+ check_immutable(ctx, &pctx);
+ check_size(ctx, &pctx);
+ ctx->fs_sockets_count++;
+ } else
+ mark_inode_bad(ctx, ino);
+ if (inode->i_block[EXT2_IND_BLOCK])
+ ctx->fs_ind_count++;
+ if (inode->i_block[EXT2_DIND_BLOCK])
+ ctx->fs_dind_count++;
+ if (inode->i_block[EXT2_TIND_BLOCK])
+ ctx->fs_tind_count++;
+ if (inode->i_block[EXT2_IND_BLOCK] ||
+ inode->i_block[EXT2_DIND_BLOCK] ||
+ inode->i_block[EXT2_TIND_BLOCK] ||
+ inode->i_file_acl) {
+ inodes_to_process[process_inode_count].ino = ino;
+ inodes_to_process[process_inode_count].inode = *inode;
+ process_inode_count++;
+ } else
+ check_blocks(ctx, &pctx, block_buf);
+
+ if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+ return;
+
+ if (process_inode_count >= ctx->process_inode_size) {
+ process_inodes(ctx, block_buf);
+
+ if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+ return;
+ }
+ }
+ process_inodes(ctx, block_buf);
+ ext2fs_close_inode_scan(scan);
+ ehandler_operation(0);
+
+ /*
+ * If any extended attribute blocks' reference counts need to
+ * be adjusted, either up (ctx->refcount_extra), or down
+ * (ctx->refcount), then fix them.
+ */
+ if (ctx->refcount) {
+ adjust_extattr_refcount(ctx, ctx->refcount, block_buf, -1);
+ ea_refcount_free(ctx->refcount);
+ ctx->refcount = 0;
+ }
+ if (ctx->refcount_extra) {
+ adjust_extattr_refcount(ctx, ctx->refcount_extra,
+ block_buf, +1);
+ ea_refcount_free(ctx->refcount_extra);
+ ctx->refcount_extra = 0;
+ }
+
+ if (ctx->invalid_bitmaps)
+ handle_fs_bad_blocks(ctx);
+
+ /* We don't need the block_ea_map any more */
+ ext2fs_free_block_bitmap(ctx->block_ea_map);
+ ctx->block_ea_map = 0;
+
+ if (ctx->flags & E2F_FLAG_RESIZE_INODE) {
+ ext2fs_block_bitmap save_bmap;
+
+ save_bmap = fs->block_map;
+ fs->block_map = ctx->block_found_map;
+ clear_problem_context(&pctx);
+ pctx.errcode = ext2fs_create_resize_inode(fs);
+ if (pctx.errcode) {
+ fix_problem(ctx, PR_1_RESIZE_INODE_CREATE, &pctx);
+ /* Should never get here */
+ ctx->flags |= E2F_FLAG_ABORT;
+ return;
+ }
+ e2fsck_read_inode(ctx, EXT2_RESIZE_INO, inode,
+ "recreate inode");
+ inode->i_mtime = time(0);
+ e2fsck_write_inode(ctx, EXT2_RESIZE_INO, inode,
+ "recreate inode");
+ fs->block_map = save_bmap;
+ ctx->flags &= ~E2F_FLAG_RESIZE_INODE;
+ }
+
+ if (ctx->flags & E2F_FLAG_RESTART) {
+ /*
+ * Only the master copy of the superblock and block
+ * group descriptors are going to be written during a
+ * restart, so set the superblock to be used to be the
+ * master superblock.
+ */
+ ctx->use_superblock = 0;
+ unwind_pass1();
+ goto endit;
+ }
+
+ if (ctx->block_dup_map) {
+ if (ctx->options & E2F_OPT_PREEN) {
+ clear_problem_context(&pctx);
+ fix_problem(ctx, PR_1_DUP_BLOCKS_PREENSTOP, &pctx);
+ }
+ e2fsck_pass1_dupblocks(ctx, block_buf);
+ }
+ ext2fs_free_mem(&inodes_to_process);
+endit:
+ e2fsck_use_inode_shortcuts(ctx, 0);
+
+ ext2fs_free_mem(&block_buf);
+ ext2fs_free_mem(&inode);
+
+}
+
+/*
+ * When the inode_scan routines call this callback at the end of the
+ * glock group, call process_inodes.
+ */
+static errcode_t scan_callback(ext2_filsys fs,
+ dgrp_t group, void * priv_data)
+{
+ struct scan_callback_struct *scan_struct;
+ e2fsck_t ctx;
+
+ scan_struct = (struct scan_callback_struct *) priv_data;
+ ctx = scan_struct->ctx;
+
+ process_inodes((e2fsck_t) fs->priv_data, scan_struct->block_buf);
+
+ if (ctx->progress)
+ if ((ctx->progress)(ctx, 1, group+1,
+ ctx->fs->group_desc_count))
+ return EXT2_ET_CANCEL_REQUESTED;
+
+ return 0;
+}
+
+/*
+ * Process the inodes in the "inodes to process" list.
+ */
+static void process_inodes(e2fsck_t ctx, char *block_buf)
+{
+ int i;
+ struct ext2_inode *old_stashed_inode;
+ ext2_ino_t old_stashed_ino;
+ const char *old_operation;
+ char buf[80];
+ struct problem_context pctx;
+
+ /* begin process_inodes */
+ if (process_inode_count == 0)
+ return;
+ old_operation = ehandler_operation(0);
+ old_stashed_inode = ctx->stashed_inode;
+ old_stashed_ino = ctx->stashed_ino;
+ qsort(inodes_to_process, process_inode_count,
+ sizeof(struct process_inode_block), process_inode_cmp);
+ clear_problem_context(&pctx);
+ for (i=0; i < process_inode_count; i++) {
+ pctx.inode = ctx->stashed_inode = &inodes_to_process[i].inode;
+ pctx.ino = ctx->stashed_ino = inodes_to_process[i].ino;
+ sprintf(buf, _("reading indirect blocks of inode %u"),
+ pctx.ino);
+ ehandler_operation(buf);
+ check_blocks(ctx, &pctx, block_buf);
+ if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+ break;
+ }
+ ctx->stashed_inode = old_stashed_inode;
+ ctx->stashed_ino = old_stashed_ino;
+ process_inode_count = 0;
+ /* end process inodes */
+
+ ehandler_operation(old_operation);
+}
+
+static int process_inode_cmp(const void *a, const void *b)
+{
+ const struct process_inode_block *ib_a =
+ (const struct process_inode_block *) a;
+ const struct process_inode_block *ib_b =
+ (const struct process_inode_block *) b;
+ int ret;
+
+ ret = (ib_a->inode.i_block[EXT2_IND_BLOCK] -
+ ib_b->inode.i_block[EXT2_IND_BLOCK]);
+ if (ret == 0)
+ ret = ib_a->inode.i_file_acl - ib_b->inode.i_file_acl;
+ return ret;
+}
+
+/*
+ * Mark an inode as being bad in some what
+ */
+static void mark_inode_bad(e2fsck_t ctx, ino_t ino)
+{
+ struct problem_context pctx;
+
+ if (!ctx->inode_bad_map) {
+ clear_problem_context(&pctx);
+
+ pctx.errcode = ext2fs_allocate_inode_bitmap(ctx->fs,
+ _("bad inode map"), &ctx->inode_bad_map);
+ if (pctx.errcode) {
+ pctx.num = 3;
+ fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
+ /* Should never get here */
+ ctx->flags |= E2F_FLAG_ABORT;
+ return;
+ }
+ }
+ ext2fs_mark_inode_bitmap(ctx->inode_bad_map, ino);
+}
+
+
+/*
+ * This procedure will allocate the inode imagic table
+ */
+static void alloc_imagic_map(e2fsck_t ctx)
+{
+ struct problem_context pctx;
+
+ clear_problem_context(&pctx);
+ pctx.errcode = ext2fs_allocate_inode_bitmap(ctx->fs,
+ _("imagic inode map"),
+ &ctx->inode_imagic_map);
+ if (pctx.errcode) {
+ pctx.num = 5;
+ fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
+ /* Should never get here */
+ ctx->flags |= E2F_FLAG_ABORT;
+ return;
+ }
+}
+
+/*
+ * Marks a block as in use, setting the dup_map if it's been set
+ * already. Called by process_block and process_bad_block.
+ *
+ * WARNING: Assumes checks have already been done to make sure block
+ * is valid. This is true in both process_block and process_bad_block.
+ */
+static void mark_block_used(e2fsck_t ctx, blk_t block)
+{
+ struct problem_context pctx;
+
+ clear_problem_context(&pctx);
+
+ if (ext2fs_fast_test_block_bitmap(ctx->block_found_map, block)) {
+ if (!ctx->block_dup_map) {
+ pctx.errcode = ext2fs_allocate_block_bitmap(ctx->fs,
+ _("multiply claimed block map"),
+ &ctx->block_dup_map);
+ if (pctx.errcode) {
+ pctx.num = 3;
+ fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR,
+ &pctx);
+ /* Should never get here */
+ ctx->flags |= E2F_FLAG_ABORT;
+ return;
+ }
+ }
+ ext2fs_fast_mark_block_bitmap(ctx->block_dup_map, block);
+ } else {
+ ext2fs_fast_mark_block_bitmap(ctx->block_found_map, block);
+ }
+}
+
+/*
+ * Adjust the extended attribute block's reference counts at the end
+ * of pass 1, either by subtracting out references for EA blocks that
+ * are still referenced in ctx->refcount, or by adding references for
+ * EA blocks that had extra references as accounted for in
+ * ctx->refcount_extra.
+ */
+static void adjust_extattr_refcount(e2fsck_t ctx, ext2_refcount_t refcount,
+ char *block_buf, int adjust_sign)
+{
+ struct ext2_ext_attr_header *header;
+ struct problem_context pctx;
+ ext2_filsys fs = ctx->fs;
+ blk_t blk;
+ __u32 should_be;
+ int count;
+
+ clear_problem_context(&pctx);
+
+ ea_refcount_intr_begin(refcount);
+ while (1) {
+ if ((blk = ea_refcount_intr_next(refcount, &count)) == 0)
+ break;
+ pctx.blk = blk;
+ pctx.errcode = ext2fs_read_ext_attr(fs, blk, block_buf);
+ if (pctx.errcode) {
+ fix_problem(ctx, PR_1_EXTATTR_READ_ABORT, &pctx);
+ return;
+ }
+ header = (struct ext2_ext_attr_header *) block_buf;
+ pctx.blkcount = header->h_refcount;
+ should_be = header->h_refcount + adjust_sign * count;
+ pctx.num = should_be;
+ if (fix_problem(ctx, PR_1_EXTATTR_REFCOUNT, &pctx)) {
+ header->h_refcount = should_be;
+ pctx.errcode = ext2fs_write_ext_attr(fs, blk,
+ block_buf);
+ if (pctx.errcode) {
+ fix_problem(ctx, PR_1_EXTATTR_WRITE, &pctx);
+ continue;
+ }
+ }
+ }
+}
+
+/*
+ * Handle processing the extended attribute blocks
+ */
+static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx,
+ char *block_buf)
+{
+ ext2_filsys fs = ctx->fs;
+ ext2_ino_t ino = pctx->ino;
+ struct ext2_inode *inode = pctx->inode;
+ blk_t blk;
+ char * end;
+ struct ext2_ext_attr_header *header;
+ struct ext2_ext_attr_entry *entry;
+ int count;
+ region_t region;
+
+ blk = inode->i_file_acl;
+ if (blk == 0)
+ return 0;
+
+ /*
+ * If the Extended attribute flag isn't set, then a non-zero
+ * file acl means that the inode is corrupted.
+ *
+ * Or if the extended attribute block is an invalid block,
+ * then the inode is also corrupted.
+ */
+ if (!(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR) ||
+ (blk < fs->super->s_first_data_block) ||
+ (blk >= fs->super->s_blocks_count)) {
+ mark_inode_bad(ctx, ino);
+ return 0;
+ }
+
+ /* If ea bitmap hasn't been allocated, create it */
+ if (!ctx->block_ea_map) {
+ pctx->errcode = ext2fs_allocate_block_bitmap(fs,
+ _("ext attr block map"),
+ &ctx->block_ea_map);
+ if (pctx->errcode) {
+ pctx->num = 2;
+ fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR, pctx);
+ ctx->flags |= E2F_FLAG_ABORT;
+ return 0;
+ }
+ }
+
+ /* Create the EA refcount structure if necessary */
+ if (!ctx->refcount) {
+ pctx->errcode = ea_refcount_create(0, &ctx->refcount);
+ if (pctx->errcode) {
+ pctx->num = 1;
+ fix_problem(ctx, PR_1_ALLOCATE_REFCOUNT, pctx);
+ ctx->flags |= E2F_FLAG_ABORT;
+ return 0;
+ }
+ }
+
+ /* Have we seen this EA block before? */
+ if (ext2fs_fast_test_block_bitmap(ctx->block_ea_map, blk)) {
+ if (ea_refcount_decrement(ctx->refcount, blk, 0) == 0)
+ return 1;
+ /* Ooops, this EA was referenced more than it stated */
+ if (!ctx->refcount_extra) {
+ pctx->errcode = ea_refcount_create(0,
+ &ctx->refcount_extra);
+ if (pctx->errcode) {
+ pctx->num = 2;
+ fix_problem(ctx, PR_1_ALLOCATE_REFCOUNT, pctx);
+ ctx->flags |= E2F_FLAG_ABORT;
+ return 0;
+ }
+ }
+ ea_refcount_increment(ctx->refcount_extra, blk, 0);
+ return 1;
+ }
+
+ /*
+ * OK, we haven't seen this EA block yet. So we need to
+ * validate it
+ */
+ pctx->blk = blk;
+ pctx->errcode = ext2fs_read_ext_attr(fs, blk, block_buf);
+ if (pctx->errcode && fix_problem(ctx, PR_1_READ_EA_BLOCK, pctx))
+ goto clear_extattr;
+ header = (struct ext2_ext_attr_header *) block_buf;
+ pctx->blk = inode->i_file_acl;
+ if (((ctx->ext_attr_ver == 1) &&
+ (header->h_magic != EXT2_EXT_ATTR_MAGIC_v1)) ||
+ ((ctx->ext_attr_ver == 2) &&
+ (header->h_magic != EXT2_EXT_ATTR_MAGIC))) {
+ if (fix_problem(ctx, PR_1_BAD_EA_BLOCK, pctx))
+ goto clear_extattr;
+ }
+
+ if (header->h_blocks != 1) {
+ if (fix_problem(ctx, PR_1_EA_MULTI_BLOCK, pctx))
+ goto clear_extattr;
+ }
+
+ region = region_create(0, fs->blocksize);
+ if (!region) {
+ fix_problem(ctx, PR_1_EA_ALLOC_REGION, pctx);
+ ctx->flags |= E2F_FLAG_ABORT;
+ return 0;
+ }
+ if (region_allocate(region, 0, sizeof(struct ext2_ext_attr_header))) {
+ if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION, pctx))
+ goto clear_extattr;
+ }
+
+ entry = (struct ext2_ext_attr_entry *)(header+1);
+ end = block_buf + fs->blocksize;
+ while ((char *)entry < end && *(__u32 *)entry) {
+ if (region_allocate(region, (char *)entry - (char *)header,
+ EXT2_EXT_ATTR_LEN(entry->e_name_len))) {
+ if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION, pctx))
+ goto clear_extattr;
+ }
+ if ((ctx->ext_attr_ver == 1 &&
+ (entry->e_name_len == 0 || entry->e_name_index != 0)) ||
+ (ctx->ext_attr_ver == 2 &&
+ entry->e_name_index == 0)) {
+ if (fix_problem(ctx, PR_1_EA_BAD_NAME, pctx))
+ goto clear_extattr;
+ }
+ if (entry->e_value_block != 0) {
+ if (fix_problem(ctx, PR_1_EA_BAD_VALUE, pctx))
+ goto clear_extattr;
+ }
+ if (entry->e_value_size &&
+ region_allocate(region, entry->e_value_offs,
+ EXT2_EXT_ATTR_SIZE(entry->e_value_size))) {
+ if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION, pctx))
+ goto clear_extattr;
+ }
+ entry = EXT2_EXT_ATTR_NEXT(entry);
+ }
+ if (region_allocate(region, (char *)entry - (char *)header, 4)) {
+ if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION, pctx))
+ goto clear_extattr;
+ }
+ region_free(region);
+
+ count = header->h_refcount - 1;
+ if (count)
+ ea_refcount_store(ctx->refcount, blk, count);
+ mark_block_used(ctx, blk);
+ ext2fs_fast_mark_block_bitmap(ctx->block_ea_map, blk);
+
+ return 1;
+
+clear_extattr:
+ inode->i_file_acl = 0;
+ e2fsck_write_inode(ctx, ino, inode, "check_ext_attr");
+ return 0;
+}
+
+/* Returns 1 if bad htree, 0 if OK */
+static int handle_htree(e2fsck_t ctx, struct problem_context *pctx,
+ ext2_ino_t ino FSCK_ATTR((unused)),
+ struct ext2_inode *inode,
+ char *block_buf)
+{
+ struct ext2_dx_root_info *root;
+ ext2_filsys fs = ctx->fs;
+ errcode_t retval;
+ blk_t blk;
+
+ if ((!LINUX_S_ISDIR(inode->i_mode) &&
+ fix_problem(ctx, PR_1_HTREE_NODIR, pctx)) ||
+ (!(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) &&
+ fix_problem(ctx, PR_1_HTREE_SET, pctx)))
+ return 1;
+
+ blk = inode->i_block[0];
+ if (((blk == 0) ||
+ (blk < fs->super->s_first_data_block) ||
+ (blk >= fs->super->s_blocks_count)) &&
+ fix_problem(ctx, PR_1_HTREE_BADROOT, pctx))
+ return 1;
+
+ retval = io_channel_read_blk(fs->io, blk, 1, block_buf);
+ if (retval && fix_problem(ctx, PR_1_HTREE_BADROOT, pctx))
+ return 1;
+
+ /* XXX should check that beginning matches a directory */
+ root = (struct ext2_dx_root_info *) (block_buf + 24);
+
+ if ((root->reserved_zero || root->info_length < 8) &&
+ fix_problem(ctx, PR_1_HTREE_BADROOT, pctx))
+ return 1;
+
+ pctx->num = root->hash_version;
+ if ((root->hash_version != EXT2_HASH_LEGACY) &&
+ (root->hash_version != EXT2_HASH_HALF_MD4) &&
+ (root->hash_version != EXT2_HASH_TEA) &&
+ fix_problem(ctx, PR_1_HTREE_HASHV, pctx))
+ return 1;
+
+ if ((root->unused_flags & EXT2_HASH_FLAG_INCOMPAT) &&
+ fix_problem(ctx, PR_1_HTREE_INCOMPAT, pctx))
+ return 1;
+
+ pctx->num = root->indirect_levels;
+ if ((root->indirect_levels > 1) &&
+ fix_problem(ctx, PR_1_HTREE_DEPTH, pctx))
+ return 1;
+
+ return 0;
+}
+
+/*
+ * This subroutine is called on each inode to account for all of the
+ * blocks used by that inode.
+ */
+static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
+ char *block_buf)
+{
+ ext2_filsys fs = ctx->fs;
+ struct process_block_struct_1 pb;
+ ext2_ino_t ino = pctx->ino;
+ struct ext2_inode *inode = pctx->inode;
+ int bad_size = 0;
+ int dirty_inode = 0;
+ __u64 size;
+
+ pb.ino = ino;
+ pb.num_blocks = 0;
+ pb.last_block = -1;
+ pb.num_illegal_blocks = 0;
+ pb.suppress = 0; pb.clear = 0;
+ pb.fragmented = 0;
+ pb.compressed = 0;
+ pb.previous_block = 0;
+ pb.is_dir = LINUX_S_ISDIR(inode->i_mode);
+ pb.is_reg = LINUX_S_ISREG(inode->i_mode);
+ pb.max_blocks = 1 << (31 - fs->super->s_log_block_size);
+ pb.inode = inode;
+ pb.pctx = pctx;
+ pb.ctx = ctx;
+ pctx->ino = ino;
+ pctx->errcode = 0;
+
+ if (inode->i_flags & EXT2_COMPRBLK_FL) {
+ if (fs->super->s_feature_incompat &
+ EXT2_FEATURE_INCOMPAT_COMPRESSION)
+ pb.compressed = 1;
+ else {
+ if (fix_problem(ctx, PR_1_COMPR_SET, pctx)) {
+ inode->i_flags &= ~EXT2_COMPRBLK_FL;
+ dirty_inode++;
+ }
+ }
+ }
+
+ if (inode->i_file_acl && check_ext_attr(ctx, pctx, block_buf))
+ pb.num_blocks++;
+
+ if (ext2fs_inode_has_valid_blocks(inode))
+ pctx->errcode = ext2fs_block_iterate2(fs, ino,
+ pb.is_dir ? BLOCK_FLAG_HOLE : 0,
+ block_buf, process_block, &pb);
+ end_problem_latch(ctx, PR_LATCH_BLOCK);
+ end_problem_latch(ctx, PR_LATCH_TOOBIG);
+ if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+ goto out;
+ if (pctx->errcode)
+ fix_problem(ctx, PR_1_BLOCK_ITERATE, pctx);
+
+ if (pb.fragmented && pb.num_blocks < fs->super->s_blocks_per_group)
+ ctx->fs_fragmented++;
+
+ if (pb.clear) {
+ inode->i_links_count = 0;
+ ext2fs_icount_store(ctx->inode_link_info, ino, 0);
+ inode->i_dtime = time(0);
+ dirty_inode++;
+ ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, ino);
+ ext2fs_unmark_inode_bitmap(ctx->inode_reg_map, ino);
+ ext2fs_unmark_inode_bitmap(ctx->inode_used_map, ino);
+ /*
+ * The inode was probably partially accounted for
+ * before processing was aborted, so we need to
+ * restart the pass 1 scan.
+ */
+ ctx->flags |= E2F_FLAG_RESTART;
+ goto out;
+ }
+
+ if (inode->i_flags & EXT2_INDEX_FL) {
+ if (handle_htree(ctx, pctx, ino, inode, block_buf)) {
+ inode->i_flags &= ~EXT2_INDEX_FL;
+ dirty_inode++;
+ } else {
+#ifdef ENABLE_HTREE
+ e2fsck_add_dx_dir(ctx, ino, pb.last_block+1);
+#endif
+ }
+ }
+ if (ctx->dirs_to_hash && pb.is_dir &&
+ !(inode->i_flags & EXT2_INDEX_FL) &&
+ ((inode->i_size / fs->blocksize) >= 3))
+ ext2fs_u32_list_add(ctx->dirs_to_hash, ino);
+
+ if (!pb.num_blocks && pb.is_dir) {
+ if (fix_problem(ctx, PR_1_ZERO_LENGTH_DIR, pctx)) {
+ inode->i_links_count = 0;
+ ext2fs_icount_store(ctx->inode_link_info, ino, 0);
+ inode->i_dtime = time(0);
+ dirty_inode++;
+ ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, ino);
+ ext2fs_unmark_inode_bitmap(ctx->inode_reg_map, ino);
+ ext2fs_unmark_inode_bitmap(ctx->inode_used_map, ino);
+ ctx->fs_directory_count--;
+ goto out;
+ }
+ }
+
+ pb.num_blocks *= (fs->blocksize / 512);
+
+ if (pb.is_dir) {
+ int nblock = inode->i_size >> EXT2_BLOCK_SIZE_BITS(fs->super);
+ if (nblock > (pb.last_block + 1))
+ bad_size = 1;
+ else if (nblock < (pb.last_block + 1)) {
+ if (((pb.last_block + 1) - nblock) >
+ fs->super->s_prealloc_dir_blocks)
+ bad_size = 2;
+ }
+ } else {
+ size = EXT2_I_SIZE(inode);
+ if ((pb.last_block >= 0) &&
+ (size < (__u64) pb.last_block * fs->blocksize))
+ bad_size = 3;
+ else if (size > ext2_max_sizes[fs->super->s_log_block_size])
+ bad_size = 4;
+ }
+ /* i_size for symlinks is checked elsewhere */
+ if (bad_size && !LINUX_S_ISLNK(inode->i_mode)) {
+ pctx->num = (pb.last_block+1) * fs->blocksize;
+ if (fix_problem(ctx, PR_1_BAD_I_SIZE, pctx)) {
+ inode->i_size = pctx->num;
+ if (!LINUX_S_ISDIR(inode->i_mode))
+ inode->i_size_high = pctx->num >> 32;
+ dirty_inode++;
+ }
+ pctx->num = 0;
+ }
+ if (LINUX_S_ISREG(inode->i_mode) &&
+ (inode->i_size_high || inode->i_size & 0x80000000UL))
+ ctx->large_files++;
+ if (pb.num_blocks != inode->i_blocks) {
+ pctx->num = pb.num_blocks;
+ if (fix_problem(ctx, PR_1_BAD_I_BLOCKS, pctx)) {
+ inode->i_blocks = pb.num_blocks;
+ dirty_inode++;
+ }
+ pctx->num = 0;
+ }
+out:
+ if (dirty_inode)
+ e2fsck_write_inode(ctx, ino, inode, "check_blocks");
+}
+
+
+/*
+ * This is a helper function for check_blocks().
+ */
+static int process_block(ext2_filsys fs,
+ blk_t *block_nr,
+ e2_blkcnt_t blockcnt,
+ blk_t ref_block FSCK_ATTR((unused)),
+ int ref_offset FSCK_ATTR((unused)),
+ void *priv_data)
+{
+ struct process_block_struct_1 *p;
+ struct problem_context *pctx;
+ blk_t blk = *block_nr;
+ int ret_code = 0;
+ int problem = 0;
+ e2fsck_t ctx;
+
+ p = (struct process_block_struct_1 *) priv_data;
+ pctx = p->pctx;
+ ctx = p->ctx;
+
+ if (p->compressed && (blk == EXT2FS_COMPRESSED_BLKADDR)) {
+ /* todo: Check that the comprblk_fl is high, that the
+ blkaddr pattern looks right (all non-holes up to
+ first EXT2FS_COMPRESSED_BLKADDR, then all
+ EXT2FS_COMPRESSED_BLKADDR up to end of cluster),
+ that the feature_incompat bit is high, and that the
+ inode is a regular file. If we're doing a "full
+ check" (a concept introduced to e2fsck by e2compr,
+ meaning that we look at data blocks as well as
+ metadata) then call some library routine that
+ checks the compressed data. I'll have to think
+ about this, because one particularly important
+ problem to be able to fix is to recalculate the
+ cluster size if necessary. I think that perhaps
+ we'd better do most/all e2compr-specific checks
+ separately, after the non-e2compr checks. If not
+ doing a full check, it may be useful to test that
+ the personality is linux; e.g. if it isn't then
+ perhaps this really is just an illegal block. */
+ return 0;
+ }
+
+ if (blk == 0) {
+ if (p->is_dir == 0) {
+ /*
+ * Should never happen, since only directories
+ * get called with BLOCK_FLAG_HOLE
+ */
+#ifdef DEBUG_E2FSCK
+ printf("process_block() called with blk == 0, "
+ "blockcnt=%d, inode %lu???\n",
+ blockcnt, p->ino);
+#endif
+ return 0;
+ }
+ if (blockcnt < 0)
+ return 0;
+ if (blockcnt * fs->blocksize < p->inode->i_size) {
+ goto mark_dir;
+ }
+ return 0;
+ }
+
+ /*
+ * Simplistic fragmentation check. We merely require that the
+ * file be contiguous. (Which can never be true for really
+ * big files that are greater than a block group.)
+ */
+ if (!HOLE_BLKADDR(p->previous_block)) {
+ if (p->previous_block+1 != blk)
+ p->fragmented = 1;
+ }
+ p->previous_block = blk;
+
+ if (p->is_dir && blockcnt > (1 << (21 - fs->super->s_log_block_size)))
+ problem = PR_1_TOOBIG_DIR;
+ if (p->is_reg && p->num_blocks+1 >= p->max_blocks)
+ problem = PR_1_TOOBIG_REG;
+ if (!p->is_dir && !p->is_reg && blockcnt > 0)
+ problem = PR_1_TOOBIG_SYMLINK;
+
+ if (blk < fs->super->s_first_data_block ||
+ blk >= fs->super->s_blocks_count)
+ problem = PR_1_ILLEGAL_BLOCK_NUM;
+
+ if (problem) {
+ p->num_illegal_blocks++;
+ if (!p->suppress && (p->num_illegal_blocks % 12) == 0) {
+ if (fix_problem(ctx, PR_1_TOO_MANY_BAD_BLOCKS, pctx)) {
+ p->clear = 1;
+ return BLOCK_ABORT;
+ }
+ if (fix_problem(ctx, PR_1_SUPPRESS_MESSAGES, pctx)) {
+ p->suppress = 1;
+ set_latch_flags(PR_LATCH_BLOCK,
+ PRL_SUPPRESS, 0);
+ }
+ }
+ pctx->blk = blk;
+ pctx->blkcount = blockcnt;
+ if (fix_problem(ctx, problem, pctx)) {
+ blk = *block_nr = 0;
+ ret_code = BLOCK_CHANGED;
+ goto mark_dir;
+ } else
+ return 0;
+ }
+
+ if (p->ino == EXT2_RESIZE_INO) {
+ /*
+ * The resize inode has already be sanity checked
+ * during pass #0 (the superblock checks). All we
+ * have to do is mark the double indirect block as
+ * being in use; all of the other blocks are handled
+ * by mark_table_blocks()).
+ */
+ if (blockcnt == BLOCK_COUNT_DIND)
+ mark_block_used(ctx, blk);
+ } else
+ mark_block_used(ctx, blk);
+ p->num_blocks++;
+ if (blockcnt >= 0)
+ p->last_block = blockcnt;
+mark_dir:
+ if (p->is_dir && (blockcnt >= 0)) {
+ pctx->errcode = ext2fs_add_dir_block(fs->dblist, p->ino,
+ blk, blockcnt);
+ if (pctx->errcode) {
+ pctx->blk = blk;
+ pctx->num = blockcnt;
+ fix_problem(ctx, PR_1_ADD_DBLOCK, pctx);
+ /* Should never get here */
+ ctx->flags |= E2F_FLAG_ABORT;
+ return BLOCK_ABORT;
+ }
+ }
+ return ret_code;
+}
+
+static int process_bad_block(ext2_filsys fs FSCK_ATTR((unused)),
+ blk_t *block_nr,
+ e2_blkcnt_t blockcnt,
+ blk_t ref_block FSCK_ATTR((unused)),
+ int ref_offset FSCK_ATTR((unused)),
+ void *priv_data EXT2FS_ATTR((unused)))
+{
+ /*
+ * Note: This function processes blocks for the bad blocks
+ * inode, which is never compressed. So we don't use HOLE_BLKADDR().
+ */
+
+ printf("Unrecoverable Error: Found %"PRIi64" bad blocks starting at block number: %u\n", blockcnt, *block_nr);
+ return BLOCK_ERROR;
+}
+
+/*
+ * This routine gets called at the end of pass 1 if bad blocks are
+ * detected in the superblock, group descriptors, inode_bitmaps, or
+ * block bitmaps. At this point, all of the blocks have been mapped
+ * out, so we can try to allocate new block(s) to replace the bad
+ * blocks.
+ */
+static void handle_fs_bad_blocks(e2fsck_t ctx)
+{
+ printf("Bad blocks detected on your filesystem\n"
+ "You should get your data off as the device will soon die\n");
+}
+
+/*
+ * This routine marks all blocks which are used by the superblock,
+ * group descriptors, inode bitmaps, and block bitmaps.
+ */
+static void mark_table_blocks(e2fsck_t ctx)
+{
+ ext2_filsys fs = ctx->fs;
+ blk_t block, b;
+ dgrp_t i;
+ int j;
+ struct problem_context pctx;
+
+ clear_problem_context(&pctx);
+
+ block = fs->super->s_first_data_block;
+ for (i = 0; i < fs->group_desc_count; i++) {
+ pctx.group = i;
+
+ ext2fs_reserve_super_and_bgd(fs, i, ctx->block_found_map);
+
+ /*
+ * Mark the blocks used for the inode table
+ */
+ if (fs->group_desc[i].bg_inode_table) {
+ for (j = 0, b = fs->group_desc[i].bg_inode_table;
+ j < fs->inode_blocks_per_group;
+ j++, b++) {
+ if (ext2fs_test_block_bitmap(ctx->block_found_map,
+ b)) {
+ pctx.blk = b;
+ if (fix_problem(ctx,
+ PR_1_ITABLE_CONFLICT, &pctx)) {
+ ctx->invalid_inode_table_flag[i]++;
+ ctx->invalid_bitmaps++;
+ }
+ } else {
+ ext2fs_mark_block_bitmap(ctx->block_found_map,
+ b);
+ }
+ }
+ }
+
+ /*
+ * Mark block used for the block bitmap
+ */
+ if (fs->group_desc[i].bg_block_bitmap) {
+ if (ext2fs_test_block_bitmap(ctx->block_found_map,
+ fs->group_desc[i].bg_block_bitmap)) {
+ pctx.blk = fs->group_desc[i].bg_block_bitmap;
+ if (fix_problem(ctx, PR_1_BB_CONFLICT, &pctx)) {
+ ctx->invalid_block_bitmap_flag[i]++;
+ ctx->invalid_bitmaps++;
+ }
+ } else {
+ ext2fs_mark_block_bitmap(ctx->block_found_map,
+ fs->group_desc[i].bg_block_bitmap);
+ }
+
+ }
+ /*
+ * Mark block used for the inode bitmap
+ */
+ if (fs->group_desc[i].bg_inode_bitmap) {
+ if (ext2fs_test_block_bitmap(ctx->block_found_map,
+ fs->group_desc[i].bg_inode_bitmap)) {
+ pctx.blk = fs->group_desc[i].bg_inode_bitmap;
+ if (fix_problem(ctx, PR_1_IB_CONFLICT, &pctx)) {
+ ctx->invalid_inode_bitmap_flag[i]++;
+ ctx->invalid_bitmaps++;
+ }
+ } else {
+ ext2fs_mark_block_bitmap(ctx->block_found_map,
+ fs->group_desc[i].bg_inode_bitmap);
+ }
+ }
+ block += fs->super->s_blocks_per_group;
+ }
+}
+
+/*
+ * Thes subroutines short circuits ext2fs_get_blocks and
+ * ext2fs_check_directory; we use them since we already have the inode
+ * structure, so there's no point in letting the ext2fs library read
+ * the inode again.
+ */
+static errcode_t pass1_get_blocks(ext2_filsys fs, ext2_ino_t ino,
+ blk_t *blocks)
+{
+ e2fsck_t ctx = (e2fsck_t) fs->priv_data;
+ int i;
+
+ if ((ino != ctx->stashed_ino) || !ctx->stashed_inode)
+ return EXT2_ET_CALLBACK_NOTHANDLED;
+
+ for (i=0; i < EXT2_N_BLOCKS; i++)
+ blocks[i] = ctx->stashed_inode->i_block[i];
+ return 0;
+}
+
+static errcode_t pass1_read_inode(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode *inode)
+{
+ e2fsck_t ctx = (e2fsck_t) fs->priv_data;
+
+ if ((ino != ctx->stashed_ino) || !ctx->stashed_inode)
+ return EXT2_ET_CALLBACK_NOTHANDLED;
+ *inode = *ctx->stashed_inode;
+ return 0;
+}
+
+static errcode_t pass1_write_inode(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode *inode)
+{
+ e2fsck_t ctx = (e2fsck_t) fs->priv_data;
+
+ if ((ino == ctx->stashed_ino) && ctx->stashed_inode)
+ *ctx->stashed_inode = *inode;
+ return EXT2_ET_CALLBACK_NOTHANDLED;
+}
+
+static errcode_t pass1_check_directory(ext2_filsys fs, ext2_ino_t ino)
+{
+ e2fsck_t ctx = (e2fsck_t) fs->priv_data;
+
+ if ((ino != ctx->stashed_ino) || !ctx->stashed_inode)
+ return EXT2_ET_CALLBACK_NOTHANDLED;
+
+ if (!LINUX_S_ISDIR(ctx->stashed_inode->i_mode))
+ return EXT2_ET_NO_DIRECTORY;
+ return 0;
+}
+
+void e2fsck_use_inode_shortcuts(e2fsck_t ctx, int bool)
+{
+ ext2_filsys fs = ctx->fs;
+
+ if (bool) {
+ fs->get_blocks = pass1_get_blocks;
+ fs->check_directory = pass1_check_directory;
+ fs->read_inode = pass1_read_inode;
+ fs->write_inode = pass1_write_inode;
+ ctx->stashed_ino = 0;
+ } else {
+ fs->get_blocks = 0;
+ fs->check_directory = 0;
+ fs->read_inode = 0;
+ fs->write_inode = 0;
+ }
+}
+
+/*
+ * pass1b.c --- Pass #1b of e2fsck
+ *
+ * This file contains pass1B, pass1C, and pass1D of e2fsck. They are
+ * only invoked if pass 1 discovered blocks which are in use by more
+ * than one inode.
+ *
+ * Pass1B scans the data blocks of all the inodes again, generating a
+ * complete list of duplicate blocks and which inodes have claimed
+ * them.
+ *
+ * Pass1C does a tree-traversal of the filesystem, to determine the
+ * parent directories of these inodes. This step is necessary so that
+ * e2fsck can print out the pathnames of affected inodes.
+ *
+ * Pass1D is a reconciliation pass. For each inode with duplicate
+ * blocks, the user is prompted if s/he would like to clone the file
+ * (so that the file gets a fresh copy of the duplicated blocks) or
+ * simply to delete the file.
+ *
+ */
+
+
+/* Needed for architectures where sizeof(int) != sizeof(void *) */
+#define INT_TO_VOIDPTR(val) ((void *)(intptr_t)(val))
+#define VOIDPTR_TO_INT(ptr) ((int)(intptr_t)(ptr))
+
+/* Define an extension to the ext2 library's block count information */
+#define BLOCK_COUNT_EXTATTR (-5)
+
+struct block_el {
+ blk_t block;
+ struct block_el *next;
+};
+
+struct inode_el {
+ ext2_ino_t inode;
+ struct inode_el *next;
+};
+
+struct dup_block {
+ int num_bad;
+ struct inode_el *inode_list;
+};
+
+/*
+ * This structure stores information about a particular inode which
+ * is sharing blocks with other inodes. This information is collected
+ * to display to the user, so that the user knows what files he or she
+ * is dealing with, when trying to decide how to resolve the conflict
+ * of multiply-claimed blocks.
+ */
+struct dup_inode {
+ ext2_ino_t dir;
+ int num_dupblocks;
+ struct ext2_inode inode;
+ struct block_el *block_list;
+};
+
+static int process_pass1b_block(ext2_filsys fs, blk_t *blocknr,
+ e2_blkcnt_t blockcnt, blk_t ref_blk,
+ int ref_offset, void *priv_data);
+static void delete_file(e2fsck_t ctx, ext2_ino_t ino,
+ struct dup_inode *dp, char *block_buf);
+static int clone_file(e2fsck_t ctx, ext2_ino_t ino,
+ struct dup_inode *dp, char* block_buf);
+static int check_if_fs_block(e2fsck_t ctx, blk_t test_blk);
+
+static void pass1b(e2fsck_t ctx, char *block_buf);
+static void pass1c(e2fsck_t ctx, char *block_buf);
+static void pass1d(e2fsck_t ctx, char *block_buf);
+
+static int dup_inode_count = 0;
+
+static dict_t blk_dict, ino_dict;
+
+static ext2fs_inode_bitmap inode_dup_map;
+
+static int dict_int_cmp(const void *a, const void *b)
+{
+ intptr_t ia, ib;
+
+ ia = (intptr_t)a;
+ ib = (intptr_t)b;
+
+ return (ia-ib);
+}
+
+/*
+ * Add a duplicate block record
+ */
+static void add_dupe(e2fsck_t ctx, ext2_ino_t ino, blk_t blk,
+ struct ext2_inode *inode)
+{
+ dnode_t *n;
+ struct dup_block *db;
+ struct dup_inode *di;
+ struct block_el *blk_el;
+ struct inode_el *ino_el;
+
+ n = dict_lookup(&blk_dict, INT_TO_VOIDPTR(blk));
+ if (n)
+ db = (struct dup_block *) dnode_get(n);
+ else {
+ db = (struct dup_block *) e2fsck_allocate_memory(ctx,
+ sizeof(struct dup_block), "duplicate block header");
+ db->num_bad = 0;
+ db->inode_list = 0;
+ dict_alloc_insert(&blk_dict, INT_TO_VOIDPTR(blk), db);
+ }
+ ino_el = (struct inode_el *) e2fsck_allocate_memory(ctx,
+ sizeof(struct inode_el), "inode element");
+ ino_el->inode = ino;
+ ino_el->next = db->inode_list;
+ db->inode_list = ino_el;
+ db->num_bad++;
+
+ n = dict_lookup(&ino_dict, INT_TO_VOIDPTR(ino));
+ if (n)
+ di = (struct dup_inode *) dnode_get(n);
+ else {
+ di = (struct dup_inode *) e2fsck_allocate_memory(ctx,
+ sizeof(struct dup_inode), "duplicate inode header");
+ di->dir = (ino == EXT2_ROOT_INO) ? EXT2_ROOT_INO : 0 ;
+ di->num_dupblocks = 0;
+ di->block_list = 0;
+ di->inode = *inode;
+ dict_alloc_insert(&ino_dict, INT_TO_VOIDPTR(ino), di);
+ }
+ blk_el = (struct block_el *) e2fsck_allocate_memory(ctx,
+ sizeof(struct block_el), "block element");
+ blk_el->block = blk;
+ blk_el->next = di->block_list;
+ di->block_list = blk_el;
+ di->num_dupblocks++;
+}
+
+/*
+ * Free a duplicate inode record
+ */
+static void inode_dnode_free(dnode_t *node)
+{
+ struct dup_inode *di;
+ struct block_el *p, *next;
+
+ di = (struct dup_inode *) dnode_get(node);
+ for (p = di->block_list; p; p = next) {
+ next = p->next;
+ free(p);
+ }
+ free(node);
+}
+
+/*
+ * Free a duplicate block record
+ */
+static void block_dnode_free(dnode_t *node)
+{
+ struct dup_block *db;
+ struct inode_el *p, *next;
+
+ db = (struct dup_block *) dnode_get(node);
+ for (p = db->inode_list; p; p = next) {
+ next = p->next;
+ free(p);
+ }
+ free(node);
+}
+
+
+/*
+ * Main procedure for handling duplicate blocks
+ */
+void e2fsck_pass1_dupblocks(e2fsck_t ctx, char *block_buf)
+{
+ ext2_filsys fs = ctx->fs;
+ struct problem_context pctx;
+
+ clear_problem_context(&pctx);
+
+ pctx.errcode = ext2fs_allocate_inode_bitmap(fs,
+ _("multiply claimed inode map"), &inode_dup_map);
+ if (pctx.errcode) {
+ fix_problem(ctx, PR_1B_ALLOCATE_IBITMAP_ERROR, &pctx);
+ ctx->flags |= E2F_FLAG_ABORT;
+ return;
+ }
+
+ dict_init(&ino_dict, DICTCOUNT_T_MAX, dict_int_cmp);
+ dict_init(&blk_dict, DICTCOUNT_T_MAX, dict_int_cmp);
+ dict_set_allocator(&ino_dict, inode_dnode_free);
+ dict_set_allocator(&blk_dict, block_dnode_free);
+
+ pass1b(ctx, block_buf);
+ pass1c(ctx, block_buf);
+ pass1d(ctx, block_buf);
+
+ /*
+ * Time to free all of the accumulated data structures that we
+ * don't need anymore.
+ */
+ dict_free_nodes(&ino_dict);
+ dict_free_nodes(&blk_dict);
+}
+
+/*
+ * Scan the inodes looking for inodes that contain duplicate blocks.
+ */
+struct process_block_struct_1b {
+ e2fsck_t ctx;
+ ext2_ino_t ino;
+ int dup_blocks;
+ struct ext2_inode *inode;
+ struct problem_context *pctx;
+};
+
+static void pass1b(e2fsck_t ctx, char *block_buf)
+{
+ ext2_filsys fs = ctx->fs;
+ ext2_ino_t ino;
+ struct ext2_inode inode;
+ ext2_inode_scan scan;
+ struct process_block_struct_1b pb;
+ struct problem_context pctx;
+
+ clear_problem_context(&pctx);
+
+ if (!(ctx->options & E2F_OPT_PREEN))
+ fix_problem(ctx, PR_1B_PASS_HEADER, &pctx);
+ pctx.errcode = ext2fs_open_inode_scan(fs, ctx->inode_buffer_blocks,
+ &scan);
+ if (pctx.errcode) {
+ fix_problem(ctx, PR_1B_ISCAN_ERROR, &pctx);
+ ctx->flags |= E2F_FLAG_ABORT;
+ return;
+ }
+ ctx->stashed_inode = &inode;
+ pb.ctx = ctx;
+ pb.pctx = &pctx;
+ pctx.str = "pass1b";
+ while (1) {
+ pctx.errcode = ext2fs_get_next_inode(scan, &ino, &inode);
+ if (pctx.errcode == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE)
+ continue;
+ if (pctx.errcode) {
+ fix_problem(ctx, PR_1B_ISCAN_ERROR, &pctx);
+ ctx->flags |= E2F_FLAG_ABORT;
+ return;
+ }
+ if (!ino)
+ break;
+ pctx.ino = ctx->stashed_ino = ino;
+ if ((ino != EXT2_BAD_INO) &&
+ !ext2fs_test_inode_bitmap(ctx->inode_used_map, ino))
+ continue;
+
+ pb.ino = ino;
+ pb.dup_blocks = 0;
+ pb.inode = &inode;
+
+ if (ext2fs_inode_has_valid_blocks(&inode) ||
+ (ino == EXT2_BAD_INO))
+ pctx.errcode = ext2fs_block_iterate2(fs, ino,
+ 0, block_buf, process_pass1b_block, &pb);
+ if (inode.i_file_acl)
+ process_pass1b_block(fs, &inode.i_file_acl,
+ BLOCK_COUNT_EXTATTR, 0, 0, &pb);
+ if (pb.dup_blocks) {
+ end_problem_latch(ctx, PR_LATCH_DBLOCK);
+ if (ino >= EXT2_FIRST_INODE(fs->super) ||
+ ino == EXT2_ROOT_INO)
+ dup_inode_count++;
+ }
+ if (pctx.errcode)
+ fix_problem(ctx, PR_1B_BLOCK_ITERATE, &pctx);
+ }
+ ext2fs_close_inode_scan(scan);
+ e2fsck_use_inode_shortcuts(ctx, 0);
+}
+
+static int process_pass1b_block(ext2_filsys fs FSCK_ATTR((unused)),
+ blk_t *block_nr,
+ e2_blkcnt_t blockcnt FSCK_ATTR((unused)),
+ blk_t ref_blk FSCK_ATTR((unused)),
+ int ref_offset FSCK_ATTR((unused)),
+ void *priv_data)
+{
+ struct process_block_struct_1b *p;
+ e2fsck_t ctx;
+
+ if (HOLE_BLKADDR(*block_nr))
+ return 0;
+ p = (struct process_block_struct_1b *) priv_data;
+ ctx = p->ctx;
+
+ if (!ext2fs_test_block_bitmap(ctx->block_dup_map, *block_nr))
+ return 0;
+
+ /* OK, this is a duplicate block */
+ if (p->ino != EXT2_BAD_INO) {
+ p->pctx->blk = *block_nr;
+ fix_problem(ctx, PR_1B_DUP_BLOCK, p->pctx);
+ }
+ p->dup_blocks++;
+ ext2fs_mark_inode_bitmap(inode_dup_map, p->ino);
+
+ add_dupe(ctx, p->ino, *block_nr, p->inode);
+
+ return 0;
+}
+
+/*
+ * Pass 1c: Scan directories for inodes with duplicate blocks. This
+ * is used so that we can print pathnames when prompting the user for
+ * what to do.
+ */
+struct search_dir_struct {
+ int count;
+ ext2_ino_t first_inode;
+ ext2_ino_t max_inode;
+};
+
+static int search_dirent_proc(ext2_ino_t dir, int entry,
+ struct ext2_dir_entry *dirent,
+ int offset FSCK_ATTR((unused)),
+ int blocksize FSCK_ATTR((unused)),
+ char *buf FSCK_ATTR((unused)),
+ void *priv_data)
+{
+ struct search_dir_struct *sd;
+ struct dup_inode *p;
+ dnode_t *n;
+
+ sd = (struct search_dir_struct *) priv_data;
+
+ if (dirent->inode > sd->max_inode)
+ /* Should abort this inode, but not everything */
+ return 0;
+
+ if ((dirent->inode < sd->first_inode) || (entry < DIRENT_OTHER_FILE) ||
+ !ext2fs_test_inode_bitmap(inode_dup_map, dirent->inode))
+ return 0;
+
+ n = dict_lookup(&ino_dict, INT_TO_VOIDPTR(dirent->inode));
+ if (!n)
+ return 0;
+ p = (struct dup_inode *) dnode_get(n);
+ p->dir = dir;
+ sd->count--;
+
+ return sd->count ? 0 : DIRENT_ABORT;
+}
+
+
+static void pass1c(e2fsck_t ctx, char *block_buf)
+{
+ ext2_filsys fs = ctx->fs;
+ struct search_dir_struct sd;
+ struct problem_context pctx;
+
+ clear_problem_context(&pctx);
+
+ if (!(ctx->options & E2F_OPT_PREEN))
+ fix_problem(ctx, PR_1C_PASS_HEADER, &pctx);
+
+ /*
+ * Search through all directories to translate inodes to names
+ * (by searching for the containing directory for that inode.)
+ */
+ sd.count = dup_inode_count;
+ sd.first_inode = EXT2_FIRST_INODE(fs->super);
+ sd.max_inode = fs->super->s_inodes_count;
+ ext2fs_dblist_dir_iterate(fs->dblist, 0, block_buf,
+ search_dirent_proc, &sd);
+}
+
+static void pass1d(e2fsck_t ctx, char *block_buf)
+{
+ ext2_filsys fs = ctx->fs;
+ struct dup_inode *p, *t;
+ struct dup_block *q;
+ ext2_ino_t *shared, ino;
+ int shared_len;
+ int i;
+ int file_ok;
+ int meta_data = 0;
+ struct problem_context pctx;
+ dnode_t *n, *m;
+ struct block_el *s;
+ struct inode_el *r;
+
+ clear_problem_context(&pctx);
+
+ if (!(ctx->options & E2F_OPT_PREEN))
+ fix_problem(ctx, PR_1D_PASS_HEADER, &pctx);
+ e2fsck_read_bitmaps(ctx);
+
+ pctx.num = dup_inode_count; /* dict_count(&ino_dict); */
+ fix_problem(ctx, PR_1D_NUM_DUP_INODES, &pctx);
+ shared = (ext2_ino_t *) e2fsck_allocate_memory(ctx,
+ sizeof(ext2_ino_t) * dict_count(&ino_dict),
+ "Shared inode list");
+ for (n = dict_first(&ino_dict); n; n = dict_next(&ino_dict, n)) {
+ p = (struct dup_inode *) dnode_get(n);
+ shared_len = 0;
+ file_ok = 1;
+ ino = (ext2_ino_t)VOIDPTR_TO_INT(dnode_getkey(n));
+ if (ino == EXT2_BAD_INO || ino == EXT2_RESIZE_INO)
+ continue;
+
+ /*
+ * Find all of the inodes which share blocks with this
+ * one. First we find all of the duplicate blocks
+ * belonging to this inode, and then search each block
+ * get the list of inodes, and merge them together.
+ */
+ for (s = p->block_list; s; s = s->next) {
+ m = dict_lookup(&blk_dict, INT_TO_VOIDPTR(s->block));
+ if (!m)
+ continue; /* Should never happen... */
+ q = (struct dup_block *) dnode_get(m);
+ if (q->num_bad > 1)
+ file_ok = 0;
+ if (check_if_fs_block(ctx, s->block)) {
+ file_ok = 0;
+ meta_data = 1;
+ }
+
+ /*
+ * Add all inodes used by this block to the
+ * shared[] --- which is a unique list, so
+ * if an inode is already in shared[], don't
+ * add it again.
+ */
+ for (r = q->inode_list; r; r = r->next) {
+ if (r->inode == ino)
+ continue;
+ for (i = 0; i < shared_len; i++)
+ if (shared[i] == r->inode)
+ break;
+ if (i == shared_len) {
+ shared[shared_len++] = r->inode;
+ }
+ }
+ }
+
+ /*
+ * Report the inode that we are working on
+ */
+ pctx.inode = &p->inode;
+ pctx.ino = ino;
+ pctx.dir = p->dir;
+ pctx.blkcount = p->num_dupblocks;
+ pctx.num = meta_data ? shared_len+1 : shared_len;
+ fix_problem(ctx, PR_1D_DUP_FILE, &pctx);
+ pctx.blkcount = 0;
+ pctx.num = 0;
+
+ if (meta_data)
+ fix_problem(ctx, PR_1D_SHARE_METADATA, &pctx);
+
+ for (i = 0; i < shared_len; i++) {
+ m = dict_lookup(&ino_dict, INT_TO_VOIDPTR(shared[i]));
+ if (!m)
+ continue; /* should never happen */
+ t = (struct dup_inode *) dnode_get(m);
+ /*
+ * Report the inode that we are sharing with
+ */
+ pctx.inode = &t->inode;
+ pctx.ino = shared[i];
+ pctx.dir = t->dir;
+ fix_problem(ctx, PR_1D_DUP_FILE_LIST, &pctx);
+ }
+ if (file_ok) {
+ fix_problem(ctx, PR_1D_DUP_BLOCKS_DEALT, &pctx);
+ continue;
+ }
+ if (fix_problem(ctx, PR_1D_CLONE_QUESTION, &pctx)) {
+ pctx.errcode = clone_file(ctx, ino, p, block_buf);
+ if (pctx.errcode)
+ fix_problem(ctx, PR_1D_CLONE_ERROR, &pctx);
+ else
+ continue;
+ }
+ if (fix_problem(ctx, PR_1D_DELETE_QUESTION, &pctx))
+ delete_file(ctx, ino, p, block_buf);
+ else
+ ext2fs_unmark_valid(fs);
+ }
+ ext2fs_free_mem(&shared);
+}
+
+/*
+ * Drop the refcount on the dup_block structure, and clear the entry
+ * in the block_dup_map if appropriate.
+ */
+static void decrement_badcount(e2fsck_t ctx, blk_t block, struct dup_block *p)
+{
+ p->num_bad--;
+ if (p->num_bad <= 0 ||
+ (p->num_bad == 1 && !check_if_fs_block(ctx, block)))
+ ext2fs_unmark_block_bitmap(ctx->block_dup_map, block);
+}
+
+static int delete_file_block(ext2_filsys fs,
+ blk_t *block_nr,
+ e2_blkcnt_t blockcnt FSCK_ATTR((unused)),
+ blk_t ref_block FSCK_ATTR((unused)),
+ int ref_offset FSCK_ATTR((unused)),
+ void *priv_data)
+{
+ struct process_block_struct_1b *pb;
+ struct dup_block *p;
+ dnode_t *n;
+ e2fsck_t ctx;
+
+ pb = (struct process_block_struct_1b *) priv_data;
+ ctx = pb->ctx;
+
+ if (HOLE_BLKADDR(*block_nr))
+ return 0;
+
+ if (ext2fs_test_block_bitmap(ctx->block_dup_map, *block_nr)) {
+ n = dict_lookup(&blk_dict, INT_TO_VOIDPTR(*block_nr));
+ if (n) {
+ p = (struct dup_block *) dnode_get(n);
+ decrement_badcount(ctx, *block_nr, p);
+ } else
+ bb_error_msg(_("internal error; can't find dup_blk for %d"),
+ *block_nr);
+ } else {
+ ext2fs_unmark_block_bitmap(ctx->block_found_map, *block_nr);
+ ext2fs_block_alloc_stats(fs, *block_nr, -1);
+ }
+
+ return 0;
+}
+
+static void delete_file(e2fsck_t ctx, ext2_ino_t ino,
+ struct dup_inode *dp, char* block_buf)
+{
+ ext2_filsys fs = ctx->fs;
+ struct process_block_struct_1b pb;
+ struct ext2_inode inode;
+ struct problem_context pctx;
+ unsigned int count;
+
+ clear_problem_context(&pctx);
+ pctx.ino = pb.ino = ino;
+ pb.dup_blocks = dp->num_dupblocks;
+ pb.ctx = ctx;
+ pctx.str = "delete_file";
+
+ e2fsck_read_inode(ctx, ino, &inode, "delete_file");
+ if (ext2fs_inode_has_valid_blocks(&inode))
+ pctx.errcode = ext2fs_block_iterate2(fs, ino, 0, block_buf,
+ delete_file_block, &pb);
+ if (pctx.errcode)
+ fix_problem(ctx, PR_1B_BLOCK_ITERATE, &pctx);
+ ext2fs_unmark_inode_bitmap(ctx->inode_used_map, ino);
+ ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, ino);
+ if (ctx->inode_bad_map)
+ ext2fs_unmark_inode_bitmap(ctx->inode_bad_map, ino);
+ ext2fs_inode_alloc_stats2(fs, ino, -1, LINUX_S_ISDIR(inode.i_mode));
+
+ /* Inode may have changed by block_iterate, so reread it */
+ e2fsck_read_inode(ctx, ino, &inode, "delete_file");
+ inode.i_links_count = 0;
+ inode.i_dtime = time(0);
+ if (inode.i_file_acl &&
+ (fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR)) {
+ count = 1;
+ pctx.errcode = ext2fs_adjust_ea_refcount(fs, inode.i_file_acl,
+ block_buf, -1, &count);
+ if (pctx.errcode == EXT2_ET_BAD_EA_BLOCK_NUM) {
+ pctx.errcode = 0;
+ count = 1;
+ }
+ if (pctx.errcode) {
+ pctx.blk = inode.i_file_acl;
+ fix_problem(ctx, PR_1B_ADJ_EA_REFCOUNT, &pctx);
+ }
+ /*
+ * If the count is zero, then arrange to have the
+ * block deleted. If the block is in the block_dup_map,
+ * also call delete_file_block since it will take care
+ * of keeping the accounting straight.
+ */
+ if ((count == 0) ||
+ ext2fs_test_block_bitmap(ctx->block_dup_map,
+ inode.i_file_acl))
+ delete_file_block(fs, &inode.i_file_acl,
+ BLOCK_COUNT_EXTATTR, 0, 0, &pb);
+ }
+ e2fsck_write_inode(ctx, ino, &inode, "delete_file");
+}
+
+struct clone_struct {
+ errcode_t errcode;
+ ext2_ino_t dir;
+ char *buf;
+ e2fsck_t ctx;
+};
+
+static int clone_file_block(ext2_filsys fs,
+ blk_t *block_nr,
+ e2_blkcnt_t blockcnt,
+ blk_t ref_block FSCK_ATTR((unused)),
+ int ref_offset FSCK_ATTR((unused)),
+ void *priv_data)
+{
+ struct dup_block *p;
+ blk_t new_block;
+ errcode_t retval;
+ struct clone_struct *cs = (struct clone_struct *) priv_data;
+ dnode_t *n;
+ e2fsck_t ctx;
+
+ ctx = cs->ctx;
+
+ if (HOLE_BLKADDR(*block_nr))
+ return 0;
+
+ if (ext2fs_test_block_bitmap(ctx->block_dup_map, *block_nr)) {
+ n = dict_lookup(&blk_dict, INT_TO_VOIDPTR(*block_nr));
+ if (n) {
+ p = (struct dup_block *) dnode_get(n);
+ retval = ext2fs_new_block(fs, 0, ctx->block_found_map,
+ &new_block);
+ if (retval) {
+ cs->errcode = retval;
+ return BLOCK_ABORT;
+ }
+ if (cs->dir && (blockcnt >= 0)) {
+ retval = ext2fs_set_dir_block(fs->dblist,
+ cs->dir, new_block, blockcnt);
+ if (retval) {
+ cs->errcode = retval;
+ return BLOCK_ABORT;
+ }
+ }
+
+ retval = io_channel_read_blk(fs->io, *block_nr, 1,
+ cs->buf);
+ if (retval) {
+ cs->errcode = retval;
+ return BLOCK_ABORT;
+ }
+ retval = io_channel_write_blk(fs->io, new_block, 1,
+ cs->buf);
+ if (retval) {
+ cs->errcode = retval;
+ return BLOCK_ABORT;
+ }
+ decrement_badcount(ctx, *block_nr, p);
+ *block_nr = new_block;
+ ext2fs_mark_block_bitmap(ctx->block_found_map,
+ new_block);
+ ext2fs_mark_block_bitmap(fs->block_map, new_block);
+ return BLOCK_CHANGED;
+ } else
+ bb_error_msg(_("internal error; can't find dup_blk for %d"),
+ *block_nr);
+ }
+ return 0;
+}
+
+static int clone_file(e2fsck_t ctx, ext2_ino_t ino,
+ struct dup_inode *dp, char* block_buf)
+{
+ ext2_filsys fs = ctx->fs;
+ errcode_t retval;
+ struct clone_struct cs;
+ struct problem_context pctx;
+ blk_t blk;
+ dnode_t *n;
+ struct inode_el *ino_el;
+ struct dup_block *db;
+ struct dup_inode *di;
+
+ clear_problem_context(&pctx);
+ cs.errcode = 0;
+ cs.dir = 0;
+ cs.ctx = ctx;
+ retval = ext2fs_get_mem(fs->blocksize, &cs.buf);
+ if (retval)
+ return retval;
+
+ if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, ino))
+ cs.dir = ino;
+
+ pctx.ino = ino;
+ pctx.str = "clone_file";
+ if (ext2fs_inode_has_valid_blocks(&dp->inode))
+ pctx.errcode = ext2fs_block_iterate2(fs, ino, 0, block_buf,
+ clone_file_block, &cs);
+ ext2fs_mark_bb_dirty(fs);
+ if (pctx.errcode) {
+ fix_problem(ctx, PR_1B_BLOCK_ITERATE, &pctx);
+ retval = pctx.errcode;
+ goto errout;
+ }
+ if (cs.errcode) {
+ bb_error_msg(_("returned from clone_file_block"));
+ retval = cs.errcode;
+ goto errout;
+ }
+ /* The inode may have changed on disk, so we have to re-read it */
+ e2fsck_read_inode(ctx, ino, &dp->inode, "clone file EA");
+ blk = dp->inode.i_file_acl;
+ if (blk && (clone_file_block(fs, &dp->inode.i_file_acl,
+ BLOCK_COUNT_EXTATTR, 0, 0, &cs) ==
+ BLOCK_CHANGED)) {
+ e2fsck_write_inode(ctx, ino, &dp->inode, "clone file EA");
+ /*
+ * If we cloned the EA block, find all other inodes
+ * which refered to that EA block, and modify
+ * them to point to the new EA block.
+ */
+ n = dict_lookup(&blk_dict, INT_TO_VOIDPTR(blk));
+ db = (struct dup_block *) dnode_get(n);
+ for (ino_el = db->inode_list; ino_el; ino_el = ino_el->next) {
+ if (ino_el->inode == ino)
+ continue;
+ n = dict_lookup(&ino_dict, INT_TO_VOIDPTR(ino_el->inode));
+ di = (struct dup_inode *) dnode_get(n);
+ if (di->inode.i_file_acl == blk) {
+ di->inode.i_file_acl = dp->inode.i_file_acl;
+ e2fsck_write_inode(ctx, ino_el->inode,
+ &di->inode, "clone file EA");
+ decrement_badcount(ctx, blk, db);
+ }
+ }
+ }
+ retval = 0;
+errout:
+ ext2fs_free_mem(&cs.buf);
+ return retval;
+}
+
+/*
+ * This routine returns 1 if a block overlaps with one of the superblocks,
+ * group descriptors, inode bitmaps, or block bitmaps.
+ */
+static int check_if_fs_block(e2fsck_t ctx, blk_t test_block)
+{
+ ext2_filsys fs = ctx->fs;
+ blk_t block;
+ dgrp_t i;
+
+ block = fs->super->s_first_data_block;
+ for (i = 0; i < fs->group_desc_count; i++) {
+
+ /* Check superblocks/block group descriptros */
+ if (ext2fs_bg_has_super(fs, i)) {
+ if (test_block >= block &&
+ (test_block <= block + fs->desc_blocks))
+ return 1;
+ }
+
+ /* Check the inode table */
+ if ((fs->group_desc[i].bg_inode_table) &&
+ (test_block >= fs->group_desc[i].bg_inode_table) &&
+ (test_block < (fs->group_desc[i].bg_inode_table +
+ fs->inode_blocks_per_group)))
+ return 1;
+
+ /* Check the bitmap blocks */
+ if ((test_block == fs->group_desc[i].bg_block_bitmap) ||
+ (test_block == fs->group_desc[i].bg_inode_bitmap))
+ return 1;
+
+ block += fs->super->s_blocks_per_group;
+ }
+ return 0;
+}
+/*
+ * pass2.c --- check directory structure
+ *
+ * Pass 2 of e2fsck iterates through all active directory inodes, and
+ * applies to following tests to each directory entry in the directory
+ * blocks in the inodes:
+ *
+ * - The length of the directory entry (rec_len) should be at
+ * least 8 bytes, and no more than the remaining space
+ * left in the directory block.
+ * - The length of the name in the directory entry (name_len)
+ * should be less than (rec_len - 8).
+ * - The inode number in the directory entry should be within
+ * legal bounds.
+ * - The inode number should refer to a in-use inode.
+ * - The first entry should be '.', and its inode should be
+ * the inode of the directory.
+ * - The second entry should be '..'.
+ *
+ * To minimize disk seek time, the directory blocks are processed in
+ * sorted order of block numbers.
+ *
+ * Pass 2 also collects the following information:
+ * - The inode numbers of the subdirectories for each directory.
+ *
+ * Pass 2 relies on the following information from previous passes:
+ * - The directory information collected in pass 1.
+ * - The inode_used_map bitmap
+ * - The inode_bad_map bitmap
+ * - The inode_dir_map bitmap
+ *
+ * Pass 2 frees the following data structures
+ * - The inode_bad_map bitmap
+ * - The inode_reg_map bitmap
+ */
+
+/*
+ * Keeps track of how many times an inode is referenced.
+ */
+static void deallocate_inode(e2fsck_t ctx, ext2_ino_t ino, char* block_buf);
+static int check_dir_block(ext2_filsys fs,
+ struct ext2_db_entry *dir_blocks_info,
+ void *priv_data);
+static int allocate_dir_block(e2fsck_t ctx, struct ext2_db_entry *dir_blocks_info,
+ struct problem_context *pctx);
+static int update_dir_block(ext2_filsys fs,
+ blk_t *block_nr,
+ e2_blkcnt_t blockcnt,
+ blk_t ref_block,
+ int ref_offset,
+ void *priv_data);
+static void clear_htree(e2fsck_t ctx, ext2_ino_t ino);
+static int htree_depth(struct dx_dir_info *dx_dir,
+ struct dx_dirblock_info *dx_db);
+static int special_dir_block_cmp(const void *a, const void *b);
+
+struct check_dir_struct {
+ char *buf;
+ struct problem_context pctx;
+ int count, max;
+ e2fsck_t ctx;
+};
+
+static void e2fsck_pass2(e2fsck_t ctx)
+{
+ struct ext2_super_block *sb = ctx->fs->super;
+ struct problem_context pctx;
+ ext2_filsys fs = ctx->fs;
+ char *buf;
+ struct dir_info *dir;
+ struct check_dir_struct cd;
+ struct dx_dir_info *dx_dir;
+ struct dx_dirblock_info *dx_db, *dx_parent;
+ int b;
+ int i, depth;
+ problem_t code;
+ int bad_dir;
+
+ clear_problem_context(&cd.pctx);
+
+ /* Pass 2 */
+
+ if (!(ctx->options & E2F_OPT_PREEN))
+ fix_problem(ctx, PR_2_PASS_HEADER, &cd.pctx);
+
+ cd.pctx.errcode = ext2fs_create_icount2(fs, EXT2_ICOUNT_OPT_INCREMENT,
+ 0, ctx->inode_link_info,
+ &ctx->inode_count);
+ if (cd.pctx.errcode) {
+ fix_problem(ctx, PR_2_ALLOCATE_ICOUNT, &cd.pctx);
+ ctx->flags |= E2F_FLAG_ABORT;
+ return;
+ }
+ buf = (char *) e2fsck_allocate_memory(ctx, 2*fs->blocksize,
+ "directory scan buffer");
+
+ /*
+ * Set up the parent pointer for the root directory, if
+ * present. (If the root directory is not present, we will
+ * create it in pass 3.)
+ */
+ dir = e2fsck_get_dir_info(ctx, EXT2_ROOT_INO);
+ if (dir)
+ dir->parent = EXT2_ROOT_INO;
+
+ cd.buf = buf;
+ cd.ctx = ctx;
+ cd.count = 1;
+ cd.max = ext2fs_dblist_count(fs->dblist);
+
+ if (ctx->progress)
+ (void) (ctx->progress)(ctx, 2, 0, cd.max);
+
+ if (fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX)
+ ext2fs_dblist_sort(fs->dblist, special_dir_block_cmp);
+
+ cd.pctx.errcode = ext2fs_dblist_iterate(fs->dblist, check_dir_block,
+ &cd);
+ if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+ return;
+ if (cd.pctx.errcode) {
+ fix_problem(ctx, PR_2_DBLIST_ITERATE, &cd.pctx);
+ ctx->flags |= E2F_FLAG_ABORT;
+ return;
+ }
+
+#ifdef ENABLE_HTREE
+ for (i=0; (dx_dir = e2fsck_dx_dir_info_iter(ctx, &i)) != 0;) {
+ if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+ return;
+ if (dx_dir->numblocks == 0)
+ continue;
+ clear_problem_context(&pctx);
+ bad_dir = 0;
+ pctx.dir = dx_dir->ino;
+ dx_db = dx_dir->dx_block;
+ if (dx_db->flags & DX_FLAG_REFERENCED)
+ dx_db->flags |= DX_FLAG_DUP_REF;
+ else
+ dx_db->flags |= DX_FLAG_REFERENCED;
+ /*
+ * Find all of the first and last leaf blocks, and
+ * update their parent's min and max hash values
+ */
+ for (b=0, dx_db = dx_dir->dx_block;
+ b < dx_dir->numblocks;
+ b++, dx_db++) {
+ if ((dx_db->type != DX_DIRBLOCK_LEAF) ||
+ !(dx_db->flags & (DX_FLAG_FIRST | DX_FLAG_LAST)))
+ continue;
+ dx_parent = &dx_dir->dx_block[dx_db->parent];
+ /*
+ * XXX Make sure dx_parent->min_hash > dx_db->min_hash
+ */
+ if (dx_db->flags & DX_FLAG_FIRST)
+ dx_parent->min_hash = dx_db->min_hash;
+ /*
+ * XXX Make sure dx_parent->max_hash < dx_db->max_hash
+ */
+ if (dx_db->flags & DX_FLAG_LAST)
+ dx_parent->max_hash = dx_db->max_hash;
+ }
+
+ for (b=0, dx_db = dx_dir->dx_block;
+ b < dx_dir->numblocks;
+ b++, dx_db++) {
+ pctx.blkcount = b;
+ pctx.group = dx_db->parent;
+ code = 0;
+ if (!(dx_db->flags & DX_FLAG_FIRST) &&
+ (dx_db->min_hash < dx_db->node_min_hash)) {
+ pctx.blk = dx_db->min_hash;
+ pctx.blk2 = dx_db->node_min_hash;
+ code = PR_2_HTREE_MIN_HASH;
+ fix_problem(ctx, code, &pctx);
+ bad_dir++;
+ }
+ if (dx_db->type == DX_DIRBLOCK_LEAF) {
+ depth = htree_depth(dx_dir, dx_db);
+ if (depth != dx_dir->depth) {
+ code = PR_2_HTREE_BAD_DEPTH;
+ fix_problem(ctx, code, &pctx);
+ bad_dir++;
+ }
+ }
+ /*
+ * This test doesn't apply for the root block
+ * at block #0
+ */
+ if (b &&
+ (dx_db->max_hash > dx_db->node_max_hash)) {
+ pctx.blk = dx_db->max_hash;
+ pctx.blk2 = dx_db->node_max_hash;
+ code = PR_2_HTREE_MAX_HASH;
+ fix_problem(ctx, code, &pctx);
+ bad_dir++;
+ }
+ if (!(dx_db->flags & DX_FLAG_REFERENCED)) {
+ code = PR_2_HTREE_NOTREF;
+ fix_problem(ctx, code, &pctx);
+ bad_dir++;
+ } else if (dx_db->flags & DX_FLAG_DUP_REF) {
+ code = PR_2_HTREE_DUPREF;
+ fix_problem(ctx, code, &pctx);
+ bad_dir++;
+ }
+ if (code == 0)
+ continue;
+ }
+ if (bad_dir && fix_problem(ctx, PR_2_HTREE_CLEAR, &pctx)) {
+ clear_htree(ctx, dx_dir->ino);
+ dx_dir->numblocks = 0;
+ }
+ }
+#endif
+ ext2fs_free_mem(&buf);
+ ext2fs_free_dblist(fs->dblist);
+
+ ext2fs_free_inode_bitmap(ctx->inode_bad_map);
+ ctx->inode_bad_map = 0;
+ ext2fs_free_inode_bitmap(ctx->inode_reg_map);
+ ctx->inode_reg_map = 0;
+
+ clear_problem_context(&pctx);
+ if (ctx->large_files) {
+ if (!(sb->s_feature_ro_compat &
+ EXT2_FEATURE_RO_COMPAT_LARGE_FILE) &&
+ fix_problem(ctx, PR_2_FEATURE_LARGE_FILES, &pctx)) {
+ sb->s_feature_ro_compat |=
+ EXT2_FEATURE_RO_COMPAT_LARGE_FILE;
+ ext2fs_mark_super_dirty(fs);
+ }
+ if (sb->s_rev_level == EXT2_GOOD_OLD_REV &&
+ fix_problem(ctx, PR_1_FS_REV_LEVEL, &pctx)) {
+ ext2fs_update_dynamic_rev(fs);
+ ext2fs_mark_super_dirty(fs);
+ }
+ } else if (!ctx->large_files &&
+ (sb->s_feature_ro_compat &
+ EXT2_FEATURE_RO_COMPAT_LARGE_FILE)) {
+ if (fs->flags & EXT2_FLAG_RW) {
+ sb->s_feature_ro_compat &=
+ ~EXT2_FEATURE_RO_COMPAT_LARGE_FILE;
+ ext2fs_mark_super_dirty(fs);
+ }
+ }
+
+}
+
+#define MAX_DEPTH 32000
+static int htree_depth(struct dx_dir_info *dx_dir,
+ struct dx_dirblock_info *dx_db)
+{
+ int depth = 0;
+
+ while (dx_db->type != DX_DIRBLOCK_ROOT && depth < MAX_DEPTH) {
+ dx_db = &dx_dir->dx_block[dx_db->parent];
+ depth++;
+ }
+ return depth;
+}
+
+static int dict_de_cmp(const void *a, const void *b)
+{
+ const struct ext2_dir_entry *de_a, *de_b;
+ int a_len, b_len;
+
+ de_a = (const struct ext2_dir_entry *) a;
+ a_len = de_a->name_len & 0xFF;
+ de_b = (const struct ext2_dir_entry *) b;
+ b_len = de_b->name_len & 0xFF;
+
+ if (a_len != b_len)
+ return (a_len - b_len);
+
+ return strncmp(de_a->name, de_b->name, a_len);
+}
+
+/*
+ * This is special sort function that makes sure that directory blocks
+ * with a dirblock of zero are sorted to the beginning of the list.
+ * This guarantees that the root node of the htree directories are
+ * processed first, so we know what hash version to use.
+ */
+static int special_dir_block_cmp(const void *a, const void *b)
+{
+ const struct ext2_db_entry *db_a =
+ (const struct ext2_db_entry *) a;
+ const struct ext2_db_entry *db_b =
+ (const struct ext2_db_entry *) b;
+
+ if (db_a->blockcnt && !db_b->blockcnt)
+ return 1;
+
+ if (!db_a->blockcnt && db_b->blockcnt)
+ return -1;
+
+ if (db_a->blk != db_b->blk)
+ return (int) (db_a->blk - db_b->blk);
+
+ if (db_a->ino != db_b->ino)
+ return (int) (db_a->ino - db_b->ino);
+
+ return (int) (db_a->blockcnt - db_b->blockcnt);
+}
+
+
+/*
+ * Make sure the first entry in the directory is '.', and that the
+ * directory entry is sane.
+ */
+static int check_dot(e2fsck_t ctx,
+ struct ext2_dir_entry *dirent,
+ ext2_ino_t ino, struct problem_context *pctx)
+{
+ struct ext2_dir_entry *nextdir;
+ int status = 0;
+ int created = 0;
+ int new_len;
+ int problem = 0;
+
+ if (!dirent->inode)
+ problem = PR_2_MISSING_DOT;
+ else if (((dirent->name_len & 0xFF) != 1) ||
+ (dirent->name[0] != '.'))
+ problem = PR_2_1ST_NOT_DOT;
+ else if (dirent->name[1] != '\0')
+ problem = PR_2_DOT_NULL_TERM;
+
+ if (problem) {
+ if (fix_problem(ctx, problem, pctx)) {
+ if (dirent->rec_len < 12)
+ dirent->rec_len = 12;
+ dirent->inode = ino;
+ dirent->name_len = 1;
+ dirent->name[0] = '.';
+ dirent->name[1] = '\0';
+ status = 1;
+ created = 1;
+ }
+ }
+ if (dirent->inode != ino) {
+ if (fix_problem(ctx, PR_2_BAD_INODE_DOT, pctx)) {
+ dirent->inode = ino;
+ status = 1;
+ }
+ }
+ if (dirent->rec_len > 12) {
+ new_len = dirent->rec_len - 12;
+ if (new_len > 12) {
+ if (created ||
+ fix_problem(ctx, PR_2_SPLIT_DOT, pctx)) {
+ nextdir = (struct ext2_dir_entry *)
+ ((char *) dirent + 12);
+ dirent->rec_len = 12;
+ nextdir->rec_len = new_len;
+ nextdir->inode = 0;
+ nextdir->name_len = 0;
+ status = 1;
+ }
+ }
+ }
+ return status;
+}
+
+/*
+ * Make sure the second entry in the directory is '..', and that the
+ * directory entry is sane. We do not check the inode number of '..'
+ * here; this gets done in pass 3.
+ */
+static int check_dotdot(e2fsck_t ctx,
+ struct ext2_dir_entry *dirent,
+ struct dir_info *dir, struct problem_context *pctx)
+{
+ int problem = 0;
+
+ if (!dirent->inode)
+ problem = PR_2_MISSING_DOT_DOT;
+ else if (((dirent->name_len & 0xFF) != 2) ||
+ (dirent->name[0] != '.') ||
+ (dirent->name[1] != '.'))
+ problem = PR_2_2ND_NOT_DOT_DOT;
+ else if (dirent->name[2] != '\0')
+ problem = PR_2_DOT_DOT_NULL_TERM;
+
+ if (problem) {
+ if (fix_problem(ctx, problem, pctx)) {
+ if (dirent->rec_len < 12)
+ dirent->rec_len = 12;
+ /*
+ * Note: we don't have the parent inode just
+ * yet, so we will fill it in with the root
+ * inode. This will get fixed in pass 3.
+ */
+ dirent->inode = EXT2_ROOT_INO;
+ dirent->name_len = 2;
+ dirent->name[0] = '.';
+ dirent->name[1] = '.';
+ dirent->name[2] = '\0';
+ return 1;
+ }
+ return 0;
+ }
+ dir->dotdot = dirent->inode;
+ return 0;
+}
+
+/*
+ * Check to make sure a directory entry doesn't contain any illegal
+ * characters.
+ */
+static int check_name(e2fsck_t ctx,
+ struct ext2_dir_entry *dirent,
+ struct problem_context *pctx)
+{
+ int i;
+ int fixup = -1;
+ int ret = 0;
+
+ for ( i = 0; i < (dirent->name_len & 0xFF); i++) {
+ if (dirent->name[i] == '/' || dirent->name[i] == '\0') {
+ if (fixup < 0) {
+ fixup = fix_problem(ctx, PR_2_BAD_NAME, pctx);
+ }
+ if (fixup) {
+ dirent->name[i] = '.';
+ ret = 1;
+ }
+ }
+ }
+ return ret;
+}
+
+/*
+ * Check the directory filetype (if present)
+ */
+
+/*
+ * Given a mode, return the ext2 file type
+ */
+static int ext2_file_type(unsigned int mode)
+{
+ if (LINUX_S_ISREG(mode))
+ return EXT2_FT_REG_FILE;
+
+ if (LINUX_S_ISDIR(mode))
+ return EXT2_FT_DIR;
+
+ if (LINUX_S_ISCHR(mode))
+ return EXT2_FT_CHRDEV;
+
+ if (LINUX_S_ISBLK(mode))
+ return EXT2_FT_BLKDEV;
+
+ if (LINUX_S_ISLNK(mode))
+ return EXT2_FT_SYMLINK;
+
+ if (LINUX_S_ISFIFO(mode))
+ return EXT2_FT_FIFO;
+
+ if (LINUX_S_ISSOCK(mode))
+ return EXT2_FT_SOCK;
+
+ return 0;
+}
+
+static int check_filetype(e2fsck_t ctx,
+ struct ext2_dir_entry *dirent,
+ struct problem_context *pctx)
+{
+ int filetype = dirent->name_len >> 8;
+ int should_be = EXT2_FT_UNKNOWN;
+ struct ext2_inode inode;
+
+ if (!(ctx->fs->super->s_feature_incompat &
+ EXT2_FEATURE_INCOMPAT_FILETYPE)) {
+ if (filetype == 0 ||
+ !fix_problem(ctx, PR_2_CLEAR_FILETYPE, pctx))
+ return 0;
+ dirent->name_len = dirent->name_len & 0xFF;
+ return 1;
+ }
+
+ if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, dirent->inode)) {
+ should_be = EXT2_FT_DIR;
+ } else if (ext2fs_test_inode_bitmap(ctx->inode_reg_map,
+ dirent->inode)) {
+ should_be = EXT2_FT_REG_FILE;
+ } else if (ctx->inode_bad_map &&
+ ext2fs_test_inode_bitmap(ctx->inode_bad_map,
+ dirent->inode))
+ should_be = 0;
+ else {
+ e2fsck_read_inode(ctx, dirent->inode, &inode,
+ "check_filetype");
+ should_be = ext2_file_type(inode.i_mode);
+ }
+ if (filetype == should_be)
+ return 0;
+ pctx->num = should_be;
+
+ if (fix_problem(ctx, filetype ? PR_2_BAD_FILETYPE : PR_2_SET_FILETYPE,
+ pctx) == 0)
+ return 0;
+
+ dirent->name_len = (dirent->name_len & 0xFF) | should_be << 8;
+ return 1;
+}
+
+#ifdef ENABLE_HTREE
+static void parse_int_node(ext2_filsys fs,
+ struct ext2_db_entry *db,
+ struct check_dir_struct *cd,
+ struct dx_dir_info *dx_dir,
+ char *block_buf)
+{
+ struct ext2_dx_root_info *root;
+ struct ext2_dx_entry *ent;
+ struct ext2_dx_countlimit *limit;
+ struct dx_dirblock_info *dx_db;
+ int i, expect_limit, count;
+ blk_t blk;
+ ext2_dirhash_t min_hash = 0xffffffff;
+ ext2_dirhash_t max_hash = 0;
+ ext2_dirhash_t hash = 0, prev_hash;
+
+ if (db->blockcnt == 0) {
+ root = (struct ext2_dx_root_info *) (block_buf + 24);
+ ent = (struct ext2_dx_entry *) (block_buf + 24 + root->info_length);
+ } else {
+ ent = (struct ext2_dx_entry *) (block_buf+8);
+ }
+ limit = (struct ext2_dx_countlimit *) ent;
+
+ count = ext2fs_le16_to_cpu(limit->count);
+ expect_limit = (fs->blocksize - ((char *) ent - block_buf)) /
+ sizeof(struct ext2_dx_entry);
+ if (ext2fs_le16_to_cpu(limit->limit) != expect_limit) {
+ cd->pctx.num = ext2fs_le16_to_cpu(limit->limit);
+ if (fix_problem(cd->ctx, PR_2_HTREE_BAD_LIMIT, &cd->pctx))
+ goto clear_and_exit;
+ }
+ if (count > expect_limit) {
+ cd->pctx.num = count;
+ if (fix_problem(cd->ctx, PR_2_HTREE_BAD_COUNT, &cd->pctx))
+ goto clear_and_exit;
+ count = expect_limit;
+ }
+
+ for (i=0; i < count; i++) {
+ prev_hash = hash;
+ hash = i ? (ext2fs_le32_to_cpu(ent[i].hash) & ~1) : 0;
+ blk = ext2fs_le32_to_cpu(ent[i].block) & 0x0ffffff;
+ /* Check to make sure the block is valid */
+ if (blk > (blk_t) dx_dir->numblocks) {
+ cd->pctx.blk = blk;
+ if (fix_problem(cd->ctx, PR_2_HTREE_BADBLK,
+ &cd->pctx))
+ goto clear_and_exit;
+ }
+ if (hash < prev_hash &&
+ fix_problem(cd->ctx, PR_2_HTREE_HASH_ORDER, &cd->pctx))
+ goto clear_and_exit;
+ dx_db = &dx_dir->dx_block[blk];
+ if (dx_db->flags & DX_FLAG_REFERENCED) {
+ dx_db->flags |= DX_FLAG_DUP_REF;
+ } else {
+ dx_db->flags |= DX_FLAG_REFERENCED;
+ dx_db->parent = db->blockcnt;
+ }
+ if (hash < min_hash)
+ min_hash = hash;
+ if (hash > max_hash)
+ max_hash = hash;
+ dx_db->node_min_hash = hash;
+ if ((i+1) < count)
+ dx_db->node_max_hash =
+ ext2fs_le32_to_cpu(ent[i+1].hash) & ~1;
+ else {
+ dx_db->node_max_hash = 0xfffffffe;
+ dx_db->flags |= DX_FLAG_LAST;
+ }
+ if (i == 0)
+ dx_db->flags |= DX_FLAG_FIRST;
+ }
+ dx_db = &dx_dir->dx_block[db->blockcnt];
+ dx_db->min_hash = min_hash;
+ dx_db->max_hash = max_hash;
+ return;
+
+clear_and_exit:
+ clear_htree(cd->ctx, cd->pctx.ino);
+ dx_dir->numblocks = 0;
+}
+#endif /* ENABLE_HTREE */
+
+/*
+ * Given a busted directory, try to salvage it somehow.
+ *
+ */
+static void salvage_directory(ext2_filsys fs,
+ struct ext2_dir_entry *dirent,
+ struct ext2_dir_entry *prev,
+ unsigned int *offset)
+{
+ char *cp = (char *) dirent;
+ int left = fs->blocksize - *offset - dirent->rec_len;
+ int name_len = dirent->name_len & 0xFF;
+
+ /*
+ * Special case of directory entry of size 8: copy what's left
+ * of the directory block up to cover up the invalid hole.
+ */
+ if ((left >= 12) && (dirent->rec_len == 8)) {
+ memmove(cp, cp+8, left);
+ memset(cp + left, 0, 8);
+ return;
+ }
+ /*
+ * If the directory entry overruns the end of the directory
+ * block, and the name is small enough to fit, then adjust the
+ * record length.
+ */
+ if ((left < 0) &&
+ (name_len + 8 <= dirent->rec_len + left) &&
+ dirent->inode <= fs->super->s_inodes_count &&
+ strnlen(dirent->name, name_len) == name_len) {
+ dirent->rec_len += left;
+ return;
+ }
+ /*
+ * If the directory entry is a multiple of four, so it is
+ * valid, let the previous directory entry absorb the invalid
+ * one.
+ */
+ if (prev && dirent->rec_len && (dirent->rec_len % 4) == 0) {
+ prev->rec_len += dirent->rec_len;
+ *offset += dirent->rec_len;
+ return;
+ }
+ /*
+ * Default salvage method --- kill all of the directory
+ * entries for the rest of the block. We will either try to
+ * absorb it into the previous directory entry, or create a
+ * new empty directory entry the rest of the directory block.
+ */
+ if (prev) {
+ prev->rec_len += fs->blocksize - *offset;
+ *offset = fs->blocksize;
+ } else {
+ dirent->rec_len = fs->blocksize - *offset;
+ dirent->name_len = 0;
+ dirent->inode = 0;
+ }
+}
+
+static int check_dir_block(ext2_filsys fs,
+ struct ext2_db_entry *db,
+ void *priv_data)
+{
+ struct dir_info *subdir, *dir;
+ struct dx_dir_info *dx_dir;
+#ifdef ENABLE_HTREE
+ struct dx_dirblock_info *dx_db = 0;
+#endif /* ENABLE_HTREE */
+ struct ext2_dir_entry *dirent, *prev;
+ ext2_dirhash_t hash;
+ unsigned int offset = 0;
+ int dir_modified = 0;
+ int dot_state;
+ blk_t block_nr = db->blk;
+ ext2_ino_t ino = db->ino;
+ __u16 links;
+ struct check_dir_struct *cd;
+ char *buf;
+ e2fsck_t ctx;
+ int problem;
+ struct ext2_dx_root_info *root;
+ struct ext2_dx_countlimit *limit;
+ static dict_t de_dict;
+ struct problem_context pctx;
+ int dups_found = 0;
+
+ cd = (struct check_dir_struct *) priv_data;
+ buf = cd->buf;
+ ctx = cd->ctx;
+
+ if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+ return DIRENT_ABORT;
+
+ if (ctx->progress && (ctx->progress)(ctx, 2, cd->count++, cd->max))
+ return DIRENT_ABORT;
+
+ /*
+ * Make sure the inode is still in use (could have been
+ * deleted in the duplicate/bad blocks pass.
+ */
+ if (!(ext2fs_test_inode_bitmap(ctx->inode_used_map, ino)))
+ return 0;
+
+ cd->pctx.ino = ino;
+ cd->pctx.blk = block_nr;
+ cd->pctx.blkcount = db->blockcnt;
+ cd->pctx.ino2 = 0;
+ cd->pctx.dirent = 0;
+ cd->pctx.num = 0;
+
+ if (db->blk == 0) {
+ if (allocate_dir_block(ctx, db, &cd->pctx))
+ return 0;
+ block_nr = db->blk;
+ }
+
+ if (db->blockcnt)
+ dot_state = 2;
+ else
+ dot_state = 0;
+
+ if (ctx->dirs_to_hash &&
+ ext2fs_u32_list_test(ctx->dirs_to_hash, ino))
+ dups_found++;
+
+ cd->pctx.errcode = ext2fs_read_dir_block(fs, block_nr, buf);
+ if (cd->pctx.errcode == EXT2_ET_DIR_CORRUPTED)
+ cd->pctx.errcode = 0; /* We'll handle this ourselves */
+ if (cd->pctx.errcode) {
+ if (!fix_problem(ctx, PR_2_READ_DIRBLOCK, &cd->pctx)) {
+ ctx->flags |= E2F_FLAG_ABORT;
+ return DIRENT_ABORT;
+ }
+ memset(buf, 0, fs->blocksize);
+ }
+#ifdef ENABLE_HTREE
+ dx_dir = e2fsck_get_dx_dir_info(ctx, ino);
+ if (dx_dir && dx_dir->numblocks) {
+ if (db->blockcnt >= dx_dir->numblocks) {
+ printf("XXX should never happen!!!\n");
+ abort();
+ }
+ dx_db = &dx_dir->dx_block[db->blockcnt];
+ dx_db->type = DX_DIRBLOCK_LEAF;
+ dx_db->phys = block_nr;
+ dx_db->min_hash = ~0;
+ dx_db->max_hash = 0;
+
+ dirent = (struct ext2_dir_entry *) buf;
+ limit = (struct ext2_dx_countlimit *) (buf+8);
+ if (db->blockcnt == 0) {
+ root = (struct ext2_dx_root_info *) (buf + 24);
+ dx_db->type = DX_DIRBLOCK_ROOT;
+ dx_db->flags |= DX_FLAG_FIRST | DX_FLAG_LAST;
+ if ((root->reserved_zero ||
+ root->info_length < 8 ||
+ root->indirect_levels > 1) &&
+ fix_problem(ctx, PR_2_HTREE_BAD_ROOT, &cd->pctx)) {
+ clear_htree(ctx, ino);
+ dx_dir->numblocks = 0;
+ dx_db = 0;
+ }
+ dx_dir->hashversion = root->hash_version;
+ dx_dir->depth = root->indirect_levels + 1;
+ } else if ((dirent->inode == 0) &&
+ (dirent->rec_len == fs->blocksize) &&
+ (dirent->name_len == 0) &&
+ (ext2fs_le16_to_cpu(limit->limit) ==
+ ((fs->blocksize-8) /
+ sizeof(struct ext2_dx_entry))))
+ dx_db->type = DX_DIRBLOCK_NODE;
+ }
+#endif /* ENABLE_HTREE */
+
+ dict_init(&de_dict, DICTCOUNT_T_MAX, dict_de_cmp);
+ prev = 0;
+ do {
+ problem = 0;
+ dirent = (struct ext2_dir_entry *) (buf + offset);
+ cd->pctx.dirent = dirent;
+ cd->pctx.num = offset;
+ if (((offset + dirent->rec_len) > fs->blocksize) ||
+ (dirent->rec_len < 12) ||
+ ((dirent->rec_len % 4) != 0) ||
+ (((dirent->name_len & 0xFF)+8) > dirent->rec_len)) {
+ if (fix_problem(ctx, PR_2_DIR_CORRUPTED, &cd->pctx)) {
+ salvage_directory(fs, dirent, prev, &offset);
+ dir_modified++;
+ continue;
+ } else
+ goto abort_free_dict;
+ }
+ if ((dirent->name_len & 0xFF) > EXT2_NAME_LEN) {
+ if (fix_problem(ctx, PR_2_FILENAME_LONG, &cd->pctx)) {
+ dirent->name_len = EXT2_NAME_LEN;
+ dir_modified++;
+ }
+ }
+
+ if (dot_state == 0) {
+ if (check_dot(ctx, dirent, ino, &cd->pctx))
+ dir_modified++;
+ } else if (dot_state == 1) {
+ dir = e2fsck_get_dir_info(ctx, ino);
+ if (!dir) {
+ fix_problem(ctx, PR_2_NO_DIRINFO, &cd->pctx);
+ goto abort_free_dict;
+ }
+ if (check_dotdot(ctx, dirent, dir, &cd->pctx))
+ dir_modified++;
+ } else if (dirent->inode == ino) {
+ problem = PR_2_LINK_DOT;
+ if (fix_problem(ctx, PR_2_LINK_DOT, &cd->pctx)) {
+ dirent->inode = 0;
+ dir_modified++;
+ goto next;
+ }
+ }
+ if (!dirent->inode)
+ goto next;
+
+ /*
+ * Make sure the inode listed is a legal one.
+ */
+ if (((dirent->inode != EXT2_ROOT_INO) &&
+ (dirent->inode < EXT2_FIRST_INODE(fs->super))) ||
+ (dirent->inode > fs->super->s_inodes_count)) {
+ problem = PR_2_BAD_INO;
+ } else if (!(ext2fs_test_inode_bitmap(ctx->inode_used_map,
+ dirent->inode))) {
+ /*
+ * If the inode is unused, offer to clear it.
+ */
+ problem = PR_2_UNUSED_INODE;
+ } else if ((dot_state > 1) &&
+ ((dirent->name_len & 0xFF) == 1) &&
+ (dirent->name[0] == '.')) {
+ /*
+ * If there's a '.' entry in anything other
+ * than the first directory entry, it's a
+ * duplicate entry that should be removed.
+ */
+ problem = PR_2_DUP_DOT;
+ } else if ((dot_state > 1) &&
+ ((dirent->name_len & 0xFF) == 2) &&
+ (dirent->name[0] == '.') &&
+ (dirent->name[1] == '.')) {
+ /*
+ * If there's a '..' entry in anything other
+ * than the second directory entry, it's a
+ * duplicate entry that should be removed.
+ */
+ problem = PR_2_DUP_DOT_DOT;
+ } else if ((dot_state > 1) &&
+ (dirent->inode == EXT2_ROOT_INO)) {
+ /*
+ * Don't allow links to the root directory.
+ * We check this specially to make sure we
+ * catch this error case even if the root
+ * directory hasn't been created yet.
+ */
+ problem = PR_2_LINK_ROOT;
+ } else if ((dot_state > 1) &&
+ (dirent->name_len & 0xFF) == 0) {
+ /*
+ * Don't allow zero-length directory names.
+ */
+ problem = PR_2_NULL_NAME;
+ }
+
+ if (problem) {
+ if (fix_problem(ctx, problem, &cd->pctx)) {
+ dirent->inode = 0;
+ dir_modified++;
+ goto next;
+ } else {
+ ext2fs_unmark_valid(fs);
+ if (problem == PR_2_BAD_INO)
+ goto next;
+ }
+ }
+
+ /*
+ * If the inode was marked as having bad fields in
+ * pass1, process it and offer to fix/clear it.
+ * (We wait until now so that we can display the
+ * pathname to the user.)
+ */
+ if (ctx->inode_bad_map &&
+ ext2fs_test_inode_bitmap(ctx->inode_bad_map,
+ dirent->inode)) {
+ if (e2fsck_process_bad_inode(ctx, ino,
+ dirent->inode,
+ buf + fs->blocksize)) {
+ dirent->inode = 0;
+ dir_modified++;
+ goto next;
+ }
+ if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+ return DIRENT_ABORT;
+ }
+
+ if (check_name(ctx, dirent, &cd->pctx))
+ dir_modified++;
+
+ if (check_filetype(ctx, dirent, &cd->pctx))
+ dir_modified++;
+
+#ifdef ENABLE_HTREE
+ if (dx_db) {
+ ext2fs_dirhash(dx_dir->hashversion, dirent->name,
+ (dirent->name_len & 0xFF),
+ fs->super->s_hash_seed, &hash, 0);
+ if (hash < dx_db->min_hash)
+ dx_db->min_hash = hash;
+ if (hash > dx_db->max_hash)
+ dx_db->max_hash = hash;
+ }
+#endif
+
+ /*
+ * If this is a directory, then mark its parent in its
+ * dir_info structure. If the parent field is already
+ * filled in, then this directory has more than one
+ * hard link. We assume the first link is correct,
+ * and ask the user if he/she wants to clear this one.
+ */
+ if ((dot_state > 1) &&
+ (ext2fs_test_inode_bitmap(ctx->inode_dir_map,
+ dirent->inode))) {
+ subdir = e2fsck_get_dir_info(ctx, dirent->inode);
+ if (!subdir) {
+ cd->pctx.ino = dirent->inode;
+ fix_problem(ctx, PR_2_NO_DIRINFO, &cd->pctx);
+ goto abort_free_dict;
+ }
+ if (subdir->parent) {
+ cd->pctx.ino2 = subdir->parent;
+ if (fix_problem(ctx, PR_2_LINK_DIR,
+ &cd->pctx)) {
+ dirent->inode = 0;
+ dir_modified++;
+ goto next;
+ }
+ cd->pctx.ino2 = 0;
+ } else
+ subdir->parent = ino;
+ }
+
+ if (dups_found) {
+ ;
+ } else if (dict_lookup(&de_dict, dirent)) {
+ clear_problem_context(&pctx);
+ pctx.ino = ino;
+ pctx.dirent = dirent;
+ fix_problem(ctx, PR_2_REPORT_DUP_DIRENT, &pctx);
+ if (!ctx->dirs_to_hash)
+ ext2fs_u32_list_create(&ctx->dirs_to_hash, 50);
+ if (ctx->dirs_to_hash)
+ ext2fs_u32_list_add(ctx->dirs_to_hash, ino);
+ dups_found++;
+ } else
+ dict_alloc_insert(&de_dict, dirent, dirent);
+
+ ext2fs_icount_increment(ctx->inode_count, dirent->inode,
+ &links);
+ if (links > 1)
+ ctx->fs_links_count++;
+ ctx->fs_total_count++;
+ next:
+ prev = dirent;
+ offset += dirent->rec_len;
+ dot_state++;
+ } while (offset < fs->blocksize);
+#ifdef ENABLE_HTREE
+ if (dx_db) {
+ cd->pctx.dir = cd->pctx.ino;
+ if ((dx_db->type == DX_DIRBLOCK_ROOT) ||
+ (dx_db->type == DX_DIRBLOCK_NODE))
+ parse_int_node(fs, db, cd, dx_dir, buf);
+ }
+#endif /* ENABLE_HTREE */
+ if (offset != fs->blocksize) {
+ cd->pctx.num = dirent->rec_len - fs->blocksize + offset;
+ if (fix_problem(ctx, PR_2_FINAL_RECLEN, &cd->pctx)) {
+ dirent->rec_len = cd->pctx.num;
+ dir_modified++;
+ }
+ }
+ if (dir_modified) {
+ cd->pctx.errcode = ext2fs_write_dir_block(fs, block_nr, buf);
+ if (cd->pctx.errcode) {
+ if (!fix_problem(ctx, PR_2_WRITE_DIRBLOCK,
+ &cd->pctx))
+ goto abort_free_dict;
+ }
+ ext2fs_mark_changed(fs);
+ }
+ dict_free_nodes(&de_dict);
+ return 0;
+abort_free_dict:
+ dict_free_nodes(&de_dict);
+ ctx->flags |= E2F_FLAG_ABORT;
+ return DIRENT_ABORT;
+}
+
+/*
+ * This function is called to deallocate a block, and is an interator
+ * functioned called by deallocate inode via ext2fs_iterate_block().
+ */
+static int deallocate_inode_block(ext2_filsys fs, blk_t *block_nr,
+ e2_blkcnt_t blockcnt FSCK_ATTR((unused)),
+ blk_t ref_block FSCK_ATTR((unused)),
+ int ref_offset FSCK_ATTR((unused)),
+ void *priv_data)
+{
+ e2fsck_t ctx = (e2fsck_t) priv_data;
+
+ if (HOLE_BLKADDR(*block_nr))
+ return 0;
+ if ((*block_nr < fs->super->s_first_data_block) ||
+ (*block_nr >= fs->super->s_blocks_count))
+ return 0;
+ ext2fs_unmark_block_bitmap(ctx->block_found_map, *block_nr);
+ ext2fs_block_alloc_stats(fs, *block_nr, -1);
+ return 0;
+}
+
+/*
+ * This fuction deallocates an inode
+ */
+static void deallocate_inode(e2fsck_t ctx, ext2_ino_t ino, char* block_buf)
+{
+ ext2_filsys fs = ctx->fs;
+ struct ext2_inode inode;
+ struct problem_context pctx;
+ __u32 count;
+
+ ext2fs_icount_store(ctx->inode_link_info, ino, 0);
+ e2fsck_read_inode(ctx, ino, &inode, "deallocate_inode");
+ inode.i_links_count = 0;
+ inode.i_dtime = time(0);
+ e2fsck_write_inode(ctx, ino, &inode, "deallocate_inode");
+ clear_problem_context(&pctx);
+ pctx.ino = ino;
+
+ /*
+ * Fix up the bitmaps...
+ */
+ e2fsck_read_bitmaps(ctx);
+ ext2fs_unmark_inode_bitmap(ctx->inode_used_map, ino);
+ ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, ino);
+ if (ctx->inode_bad_map)
+ ext2fs_unmark_inode_bitmap(ctx->inode_bad_map, ino);
+ ext2fs_inode_alloc_stats2(fs, ino, -1, LINUX_S_ISDIR(inode.i_mode));
+
+ if (inode.i_file_acl &&
+ (fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR)) {
+ pctx.errcode = ext2fs_adjust_ea_refcount(fs, inode.i_file_acl,
+ block_buf, -1, &count);
+ if (pctx.errcode == EXT2_ET_BAD_EA_BLOCK_NUM) {
+ pctx.errcode = 0;
+ count = 1;
+ }
+ if (pctx.errcode) {
+ pctx.blk = inode.i_file_acl;
+ fix_problem(ctx, PR_2_ADJ_EA_REFCOUNT, &pctx);
+ ctx->flags |= E2F_FLAG_ABORT;
+ return;
+ }
+ if (count == 0) {
+ ext2fs_unmark_block_bitmap(ctx->block_found_map,
+ inode.i_file_acl);
+ ext2fs_block_alloc_stats(fs, inode.i_file_acl, -1);
+ }
+ inode.i_file_acl = 0;
+ }
+
+ if (!ext2fs_inode_has_valid_blocks(&inode))
+ return;
+
+ if (LINUX_S_ISREG(inode.i_mode) &&
+ (inode.i_size_high || inode.i_size & 0x80000000UL))
+ ctx->large_files--;
+
+ pctx.errcode = ext2fs_block_iterate2(fs, ino, 0, block_buf,
+ deallocate_inode_block, ctx);
+ if (pctx.errcode) {
+ fix_problem(ctx, PR_2_DEALLOC_INODE, &pctx);
+ ctx->flags |= E2F_FLAG_ABORT;
+ return;
+ }
+}
+
+/*
+ * This fuction clears the htree flag on an inode
+ */
+static void clear_htree(e2fsck_t ctx, ext2_ino_t ino)
+{
+ struct ext2_inode inode;
+
+ e2fsck_read_inode(ctx, ino, &inode, "clear_htree");
+ inode.i_flags = inode.i_flags & ~EXT2_INDEX_FL;
+ e2fsck_write_inode(ctx, ino, &inode, "clear_htree");
+ if (ctx->dirs_to_hash)
+ ext2fs_u32_list_add(ctx->dirs_to_hash, ino);
+}
+
+
+static int e2fsck_process_bad_inode(e2fsck_t ctx, ext2_ino_t dir,
+ ext2_ino_t ino, char *buf)
+{
+ ext2_filsys fs = ctx->fs;
+ struct ext2_inode inode;
+ int inode_modified = 0;
+ int not_fixed = 0;
+ unsigned char *frag, *fsize;
+ struct problem_context pctx;
+ int problem = 0;
+
+ e2fsck_read_inode(ctx, ino, &inode, "process_bad_inode");
+
+ clear_problem_context(&pctx);
+ pctx.ino = ino;
+ pctx.dir = dir;
+ pctx.inode = &inode;
+
+ if (inode.i_file_acl &&
+ !(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR) &&
+ fix_problem(ctx, PR_2_FILE_ACL_ZERO, &pctx)) {
+ inode.i_file_acl = 0;
+#if BB_BIG_ENDIAN
+ /*
+ * This is a special kludge to deal with long symlinks
+ * on big endian systems. i_blocks had already been
+ * decremented earlier in pass 1, but since i_file_acl
+ * hadn't yet been cleared, ext2fs_read_inode()
+ * assumed that the file was short symlink and would
+ * not have byte swapped i_block[0]. Hence, we have
+ * to byte-swap it here.
+ */
+ if (LINUX_S_ISLNK(inode.i_mode) &&
+ (fs->flags & EXT2_FLAG_SWAP_BYTES) &&
+ (inode.i_blocks == fs->blocksize >> 9))
+ inode.i_block[0] = ext2fs_swab32(inode.i_block[0]);
+#endif
+ inode_modified++;
+ } else
+ not_fixed++;
+
+ if (!LINUX_S_ISDIR(inode.i_mode) && !LINUX_S_ISREG(inode.i_mode) &&
+ !LINUX_S_ISCHR(inode.i_mode) && !LINUX_S_ISBLK(inode.i_mode) &&
+ !LINUX_S_ISLNK(inode.i_mode) && !LINUX_S_ISFIFO(inode.i_mode) &&
+ !(LINUX_S_ISSOCK(inode.i_mode)))
+ problem = PR_2_BAD_MODE;
+ else if (LINUX_S_ISCHR(inode.i_mode)
+ && !e2fsck_pass1_check_device_inode(fs, &inode))
+ problem = PR_2_BAD_CHAR_DEV;
+ else if (LINUX_S_ISBLK(inode.i_mode)
+ && !e2fsck_pass1_check_device_inode(fs, &inode))
+ problem = PR_2_BAD_BLOCK_DEV;
+ else if (LINUX_S_ISFIFO(inode.i_mode)
+ && !e2fsck_pass1_check_device_inode(fs, &inode))
+ problem = PR_2_BAD_FIFO;
+ else if (LINUX_S_ISSOCK(inode.i_mode)
+ && !e2fsck_pass1_check_device_inode(fs, &inode))
+ problem = PR_2_BAD_SOCKET;
+ else if (LINUX_S_ISLNK(inode.i_mode)
+ && !e2fsck_pass1_check_symlink(fs, &inode, buf)) {
+ problem = PR_2_INVALID_SYMLINK;
+ }
+
+ if (problem) {
+ if (fix_problem(ctx, problem, &pctx)) {
+ deallocate_inode(ctx, ino, 0);
+ if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+ return 0;
+ return 1;
+ } else
+ not_fixed++;
+ problem = 0;
+ }
+
+ if (inode.i_faddr) {
+ if (fix_problem(ctx, PR_2_FADDR_ZERO, &pctx)) {
+ inode.i_faddr = 0;
+ inode_modified++;
+ } else
+ not_fixed++;
+ }
+
+ switch (fs->super->s_creator_os) {
+ case EXT2_OS_LINUX:
+ frag = &inode.osd2.linux2.l_i_frag;
+ fsize = &inode.osd2.linux2.l_i_fsize;
+ break;
+ case EXT2_OS_HURD:
+ frag = &inode.osd2.hurd2.h_i_frag;
+ fsize = &inode.osd2.hurd2.h_i_fsize;
+ break;
+ case EXT2_OS_MASIX:
+ frag = &inode.osd2.masix2.m_i_frag;
+ fsize = &inode.osd2.masix2.m_i_fsize;
+ break;
+ default:
+ frag = fsize = 0;
+ }
+ if (frag && *frag) {
+ pctx.num = *frag;
+ if (fix_problem(ctx, PR_2_FRAG_ZERO, &pctx)) {
+ *frag = 0;
+ inode_modified++;
+ } else
+ not_fixed++;
+ pctx.num = 0;
+ }
+ if (fsize && *fsize) {
+ pctx.num = *fsize;
+ if (fix_problem(ctx, PR_2_FSIZE_ZERO, &pctx)) {
+ *fsize = 0;
+ inode_modified++;
+ } else
+ not_fixed++;
+ pctx.num = 0;
+ }
+
+ if (inode.i_file_acl &&
+ ((inode.i_file_acl < fs->super->s_first_data_block) ||
+ (inode.i_file_acl >= fs->super->s_blocks_count))) {
+ if (fix_problem(ctx, PR_2_FILE_ACL_BAD, &pctx)) {
+ inode.i_file_acl = 0;
+ inode_modified++;
+ } else
+ not_fixed++;
+ }
+ if (inode.i_dir_acl &&
+ LINUX_S_ISDIR(inode.i_mode)) {
+ if (fix_problem(ctx, PR_2_DIR_ACL_ZERO, &pctx)) {
+ inode.i_dir_acl = 0;
+ inode_modified++;
+ } else
+ not_fixed++;
+ }
+
+ if (inode_modified)
+ e2fsck_write_inode(ctx, ino, &inode, "process_bad_inode");
+ if (!not_fixed)
+ ext2fs_unmark_inode_bitmap(ctx->inode_bad_map, ino);
+ return 0;
+}
+
+
+/*
+ * allocate_dir_block --- this function allocates a new directory
+ * block for a particular inode; this is done if a directory has
+ * a "hole" in it, or if a directory has a illegal block number
+ * that was zeroed out and now needs to be replaced.
+ */
+static int allocate_dir_block(e2fsck_t ctx, struct ext2_db_entry *db,
+ struct problem_context *pctx)
+{
+ ext2_filsys fs = ctx->fs;
+ blk_t blk;
+ char *block;
+ struct ext2_inode inode;
+
+ if (fix_problem(ctx, PR_2_DIRECTORY_HOLE, pctx) == 0)
+ return 1;
+
+ /*
+ * Read the inode and block bitmaps in; we'll be messing with
+ * them.
+ */
+ e2fsck_read_bitmaps(ctx);
+
+ /*
+ * First, find a free block
+ */
+ pctx->errcode = ext2fs_new_block(fs, 0, ctx->block_found_map, &blk);
+ if (pctx->errcode) {
+ pctx->str = "ext2fs_new_block";
+ fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx);
+ return 1;
+ }
+ ext2fs_mark_block_bitmap(ctx->block_found_map, blk);
+ ext2fs_mark_block_bitmap(fs->block_map, blk);
+ ext2fs_mark_bb_dirty(fs);
+
+ /*
+ * Now let's create the actual data block for the inode
+ */
+ if (db->blockcnt)
+ pctx->errcode = ext2fs_new_dir_block(fs, 0, 0, &block);
+ else
+ pctx->errcode = ext2fs_new_dir_block(fs, db->ino,
+ EXT2_ROOT_INO, &block);
+
+ if (pctx->errcode) {
+ pctx->str = "ext2fs_new_dir_block";
+ fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx);
+ return 1;
+ }
+
+ pctx->errcode = ext2fs_write_dir_block(fs, blk, block);
+ ext2fs_free_mem(&block);
+ if (pctx->errcode) {
+ pctx->str = "ext2fs_write_dir_block";
+ fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx);
+ return 1;
+ }
+
+ /*
+ * Update the inode block count
+ */
+ e2fsck_read_inode(ctx, db->ino, &inode, "allocate_dir_block");
+ inode.i_blocks += fs->blocksize / 512;
+ if (inode.i_size < (db->blockcnt+1) * fs->blocksize)
+ inode.i_size = (db->blockcnt+1) * fs->blocksize;
+ e2fsck_write_inode(ctx, db->ino, &inode, "allocate_dir_block");
+
+ /*
+ * Finally, update the block pointers for the inode
+ */
+ db->blk = blk;
+ pctx->errcode = ext2fs_block_iterate2(fs, db->ino, BLOCK_FLAG_HOLE,
+ 0, update_dir_block, db);
+ if (pctx->errcode) {
+ pctx->str = "ext2fs_block_iterate";
+ fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx);
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * This is a helper function for allocate_dir_block().
+ */
+static int update_dir_block(ext2_filsys fs FSCK_ATTR((unused)),
+ blk_t *block_nr,
+ e2_blkcnt_t blockcnt,
+ blk_t ref_block FSCK_ATTR((unused)),
+ int ref_offset FSCK_ATTR((unused)),
+ void *priv_data)
+{
+ struct ext2_db_entry *db;
+
+ db = (struct ext2_db_entry *) priv_data;
+ if (db->blockcnt == (int) blockcnt) {
+ *block_nr = db->blk;
+ return BLOCK_CHANGED;
+ }
+ return 0;
+}
+
+/*
+ * pass3.c -- pass #3 of e2fsck: Check for directory connectivity
+ *
+ * Pass #3 assures that all directories are connected to the
+ * filesystem tree, using the following algorithm:
+ *
+ * First, the root directory is checked to make sure it exists; if
+ * not, e2fsck will offer to create a new one. It is then marked as
+ * "done".
+ *
+ * Then, pass3 interates over all directory inodes; for each directory
+ * it attempts to trace up the filesystem tree, using dirinfo.parent
+ * until it reaches a directory which has been marked "done". If it
+ * cannot do so, then the directory must be disconnected, and e2fsck
+ * will offer to reconnect it to /lost+found. While it is chasing
+ * parent pointers up the filesystem tree, if pass3 sees a directory
+ * twice, then it has detected a filesystem loop, and it will again
+ * offer to reconnect the directory to /lost+found in to break the
+ * filesystem loop.
+ *
+ * Pass 3 also contains the subroutine, e2fsck_reconnect_file() to
+ * reconnect inodes to /lost+found; this subroutine is also used by
+ * pass 4. e2fsck_reconnect_file() calls get_lost_and_found(), which
+ * is responsible for creating /lost+found if it does not exist.
+ *
+ * Pass 3 frees the following data structures:
+ * - The dirinfo directory information cache.
+ */
+
+static void check_root(e2fsck_t ctx);
+static int check_directory(e2fsck_t ctx, struct dir_info *dir,
+ struct problem_context *pctx);
+static void fix_dotdot(e2fsck_t ctx, struct dir_info *dir, ext2_ino_t parent);
+
+static ext2fs_inode_bitmap inode_loop_detect;
+static ext2fs_inode_bitmap inode_done_map;
+
+static void e2fsck_pass3(e2fsck_t ctx)
+{
+ ext2_filsys fs = ctx->fs;
+ int i;
+ struct problem_context pctx;
+ struct dir_info *dir;
+ unsigned long maxdirs, count;
+
+ clear_problem_context(&pctx);
+
+ /* Pass 3 */
+
+ if (!(ctx->options & E2F_OPT_PREEN))
+ fix_problem(ctx, PR_3_PASS_HEADER, &pctx);
+
+ /*
+ * Allocate some bitmaps to do loop detection.
+ */
+ pctx.errcode = ext2fs_allocate_inode_bitmap(fs, _("inode done bitmap"),
+ &inode_done_map);
+ if (pctx.errcode) {
+ pctx.num = 2;
+ fix_problem(ctx, PR_3_ALLOCATE_IBITMAP_ERROR, &pctx);
+ ctx->flags |= E2F_FLAG_ABORT;
+ goto abort_exit;
+ }
+ check_root(ctx);
+ if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+ goto abort_exit;
+
+ ext2fs_mark_inode_bitmap(inode_done_map, EXT2_ROOT_INO);
+
+ maxdirs = e2fsck_get_num_dirinfo(ctx);
+ count = 1;
+
+ if (ctx->progress)
+ if ((ctx->progress)(ctx, 3, 0, maxdirs))
+ goto abort_exit;
+
+ for (i=0; (dir = e2fsck_dir_info_iter(ctx, &i)) != 0;) {
+ if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+ goto abort_exit;
+ if (ctx->progress && (ctx->progress)(ctx, 3, count++, maxdirs))
+ goto abort_exit;
+ if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, dir->ino))
+ if (check_directory(ctx, dir, &pctx))
+ goto abort_exit;
+ }
+
+ /*
+ * Force the creation of /lost+found if not present
+ */
+ if ((ctx->flags & E2F_OPT_READONLY) == 0)
+ e2fsck_get_lost_and_found(ctx, 1);
+
+ /*
+ * If there are any directories that need to be indexed or
+ * optimized, do it here.
+ */
+ e2fsck_rehash_directories(ctx);
+
+abort_exit:
+ e2fsck_free_dir_info(ctx);
+ ext2fs_free_inode_bitmap(inode_loop_detect);
+ inode_loop_detect = 0;
+ ext2fs_free_inode_bitmap(inode_done_map);
+ inode_done_map = 0;
+}
+
+/*
+ * This makes sure the root inode is present; if not, we ask if the
+ * user wants us to create it. Not creating it is a fatal error.
+ */
+static void check_root(e2fsck_t ctx)
+{
+ ext2_filsys fs = ctx->fs;
+ blk_t blk;
+ struct ext2_inode inode;
+ char * block;
+ struct problem_context pctx;
+
+ clear_problem_context(&pctx);
+
+ if (ext2fs_test_inode_bitmap(ctx->inode_used_map, EXT2_ROOT_INO)) {
+ /*
+ * If the root inode is not a directory, die here. The
+ * user must have answered 'no' in pass1 when we
+ * offered to clear it.
+ */
+ if (!(ext2fs_test_inode_bitmap(ctx->inode_dir_map,
+ EXT2_ROOT_INO))) {
+ fix_problem(ctx, PR_3_ROOT_NOT_DIR_ABORT, &pctx);
+ ctx->flags |= E2F_FLAG_ABORT;
+ }
+ return;
+ }
+
+ if (!fix_problem(ctx, PR_3_NO_ROOT_INODE, &pctx)) {
+ fix_problem(ctx, PR_3_NO_ROOT_INODE_ABORT, &pctx);
+ ctx->flags |= E2F_FLAG_ABORT;
+ return;
+ }
+
+ e2fsck_read_bitmaps(ctx);
+
+ /*
+ * First, find a free block
+ */
+ pctx.errcode = ext2fs_new_block(fs, 0, ctx->block_found_map, &blk);
+ if (pctx.errcode) {
+ pctx.str = "ext2fs_new_block";
+ fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx);
+ ctx->flags |= E2F_FLAG_ABORT;
+ return;
+ }
+ ext2fs_mark_block_bitmap(ctx->block_found_map, blk);
+ ext2fs_mark_block_bitmap(fs->block_map, blk);
+ ext2fs_mark_bb_dirty(fs);
+
+ /*
+ * Now let's create the actual data block for the inode
+ */
+ pctx.errcode = ext2fs_new_dir_block(fs, EXT2_ROOT_INO, EXT2_ROOT_INO,
+ &block);
+ if (pctx.errcode) {
+ pctx.str = "ext2fs_new_dir_block";
+ fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx);
+ ctx->flags |= E2F_FLAG_ABORT;
+ return;
+ }
+
+ pctx.errcode = ext2fs_write_dir_block(fs, blk, block);
+ if (pctx.errcode) {
+ pctx.str = "ext2fs_write_dir_block";
+ fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx);
+ ctx->flags |= E2F_FLAG_ABORT;
+ return;
+ }
+ ext2fs_free_mem(&block);
+
+ /*
+ * Set up the inode structure
+ */
+ memset(&inode, 0, sizeof(inode));
+ inode.i_mode = 040755;
+ inode.i_size = fs->blocksize;
+ inode.i_atime = inode.i_ctime = inode.i_mtime = time(0);
+ inode.i_links_count = 2;
+ inode.i_blocks = fs->blocksize / 512;
+ inode.i_block[0] = blk;
+
+ /*
+ * Write out the inode.
+ */
+ pctx.errcode = ext2fs_write_new_inode(fs, EXT2_ROOT_INO, &inode);
+ if (pctx.errcode) {
+ pctx.str = "ext2fs_write_inode";
+ fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx);
+ ctx->flags |= E2F_FLAG_ABORT;
+ return;
+ }
+
+ /*
+ * Miscellaneous bookkeeping...
+ */
+ e2fsck_add_dir_info(ctx, EXT2_ROOT_INO, EXT2_ROOT_INO);
+ ext2fs_icount_store(ctx->inode_count, EXT2_ROOT_INO, 2);
+ ext2fs_icount_store(ctx->inode_link_info, EXT2_ROOT_INO, 2);
+
+ ext2fs_mark_inode_bitmap(ctx->inode_used_map, EXT2_ROOT_INO);
+ ext2fs_mark_inode_bitmap(ctx->inode_dir_map, EXT2_ROOT_INO);
+ ext2fs_mark_inode_bitmap(fs->inode_map, EXT2_ROOT_INO);
+ ext2fs_mark_ib_dirty(fs);
+}
+
+/*
+ * This subroutine is responsible for making sure that a particular
+ * directory is connected to the root; if it isn't we trace it up as
+ * far as we can go, and then offer to connect the resulting parent to
+ * the lost+found. We have to do loop detection; if we ever discover
+ * a loop, we treat that as a disconnected directory and offer to
+ * reparent it to lost+found.
+ *
+ * However, loop detection is expensive, because for very large
+ * filesystems, the inode_loop_detect bitmap is huge, and clearing it
+ * is non-trivial. Loops in filesystems are also a rare error case,
+ * and we shouldn't optimize for error cases. So we try two passes of
+ * the algorithm. The first time, we ignore loop detection and merely
+ * increment a counter; if the counter exceeds some extreme threshold,
+ * then we try again with the loop detection bitmap enabled.
+ */
+static int check_directory(e2fsck_t ctx, struct dir_info *dir,
+ struct problem_context *pctx)
+{
+ ext2_filsys fs = ctx->fs;
+ struct dir_info *p = dir;
+ int loop_pass = 0, parent_count = 0;
+
+ if (!p)
+ return 0;
+
+ while (1) {
+ /*
+ * Mark this inode as being "done"; by the time we
+ * return from this function, the inode we either be
+ * verified as being connected to the directory tree,
+ * or we will have offered to reconnect this to
+ * lost+found.
+ *
+ * If it was marked done already, then we've reached a
+ * parent we've already checked.
+ */
+ if (ext2fs_mark_inode_bitmap(inode_done_map, p->ino))
+ break;
+
+ /*
+ * If this directory doesn't have a parent, or we've
+ * seen the parent once already, then offer to
+ * reparent it to lost+found
+ */
+ if (!p->parent ||
+ (loop_pass &&
+ (ext2fs_test_inode_bitmap(inode_loop_detect,
+ p->parent)))) {
+ pctx->ino = p->ino;
+ if (fix_problem(ctx, PR_3_UNCONNECTED_DIR, pctx)) {
+ if (e2fsck_reconnect_file(ctx, pctx->ino))
+ ext2fs_unmark_valid(fs);
+ else {
+ p = e2fsck_get_dir_info(ctx, pctx->ino);
+ p->parent = ctx->lost_and_found;
+ fix_dotdot(ctx, p, ctx->lost_and_found);
+ }
+ }
+ break;
+ }
+ p = e2fsck_get_dir_info(ctx, p->parent);
+ if (!p) {
+ fix_problem(ctx, PR_3_NO_DIRINFO, pctx);
+ return 0;
+ }
+ if (loop_pass) {
+ ext2fs_mark_inode_bitmap(inode_loop_detect,
+ p->ino);
+ } else if (parent_count++ > 2048) {
+ /*
+ * If we've run into a path depth that's
+ * greater than 2048, try again with the inode
+ * loop bitmap turned on and start from the
+ * top.
+ */
+ loop_pass = 1;
+ if (inode_loop_detect)
+ ext2fs_clear_inode_bitmap(inode_loop_detect);
+ else {
+ pctx->errcode = ext2fs_allocate_inode_bitmap(fs, _("inode loop detection bitmap"), &inode_loop_detect);
+ if (pctx->errcode) {
+ pctx->num = 1;
+ fix_problem(ctx,
+ PR_3_ALLOCATE_IBITMAP_ERROR, pctx);
+ ctx->flags |= E2F_FLAG_ABORT;
+ return -1;
+ }
+ }
+ p = dir;
+ }
+ }
+
+ /*
+ * Make sure that .. and the parent directory are the same;
+ * offer to fix it if not.
+ */
+ if (dir->parent != dir->dotdot) {
+ pctx->ino = dir->ino;
+ pctx->ino2 = dir->dotdot;
+ pctx->dir = dir->parent;
+ if (fix_problem(ctx, PR_3_BAD_DOT_DOT, pctx))
+ fix_dotdot(ctx, dir, dir->parent);
+ }
+ return 0;
+}
+
+/*
+ * This routine gets the lost_and_found inode, making it a directory
+ * if necessary
+ */
+ext2_ino_t e2fsck_get_lost_and_found(e2fsck_t ctx, int fix)
+{
+ ext2_filsys fs = ctx->fs;
+ ext2_ino_t ino;
+ blk_t blk;
+ errcode_t retval;
+ struct ext2_inode inode;
+ char * block;
+ static const char name[] = "lost+found";
+ struct problem_context pctx;
+ struct dir_info *dirinfo;
+
+ if (ctx->lost_and_found)
+ return ctx->lost_and_found;
+
+ clear_problem_context(&pctx);
+
+ retval = ext2fs_lookup(fs, EXT2_ROOT_INO, name,
+ sizeof(name)-1, 0, &ino);
+ if (retval && !fix)
+ return 0;
+ if (!retval) {
+ if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, ino)) {
+ ctx->lost_and_found = ino;
+ return ino;
+ }
+
+ /* Lost+found isn't a directory! */
+ if (!fix)
+ return 0;
+ pctx.ino = ino;
+ if (!fix_problem(ctx, PR_3_LPF_NOTDIR, &pctx))
+ return 0;
+
+ /* OK, unlink the old /lost+found file. */
+ pctx.errcode = ext2fs_unlink(fs, EXT2_ROOT_INO, name, ino, 0);
+ if (pctx.errcode) {
+ pctx.str = "ext2fs_unlink";
+ fix_problem(ctx, PR_3_CREATE_LPF_ERROR, &pctx);
+ return 0;
+ }
+ dirinfo = e2fsck_get_dir_info(ctx, ino);
+ if (dirinfo)
+ dirinfo->parent = 0;
+ e2fsck_adjust_inode_count(ctx, ino, -1);
+ } else if (retval != EXT2_ET_FILE_NOT_FOUND) {
+ pctx.errcode = retval;
+ fix_problem(ctx, PR_3_ERR_FIND_LPF, &pctx);
+ }
+ if (!fix_problem(ctx, PR_3_NO_LF_DIR, 0))
+ return 0;
+
+ /*
+ * Read the inode and block bitmaps in; we'll be messing with
+ * them.
+ */
+ e2fsck_read_bitmaps(ctx);
+
+ /*
+ * First, find a free block
+ */
+ retval = ext2fs_new_block(fs, 0, ctx->block_found_map, &blk);
+ if (retval) {
+ pctx.errcode = retval;
+ fix_problem(ctx, PR_3_ERR_LPF_NEW_BLOCK, &pctx);
+ return 0;
+ }
+ ext2fs_mark_block_bitmap(ctx->block_found_map, blk);
+ ext2fs_block_alloc_stats(fs, blk, +1);
+
+ /*
+ * Next find a free inode.
+ */
+ retval = ext2fs_new_inode(fs, EXT2_ROOT_INO, 040700,
+ ctx->inode_used_map, &ino);
+ if (retval) {
+ pctx.errcode = retval;
+ fix_problem(ctx, PR_3_ERR_LPF_NEW_INODE, &pctx);
+ return 0;
+ }
+ ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino);
+ ext2fs_mark_inode_bitmap(ctx->inode_dir_map, ino);
+ ext2fs_inode_alloc_stats2(fs, ino, +1, 1);
+
+ /*
+ * Now let's create the actual data block for the inode
+ */
+ retval = ext2fs_new_dir_block(fs, ino, EXT2_ROOT_INO, &block);
+ if (retval) {
+ pctx.errcode = retval;
+ fix_problem(ctx, PR_3_ERR_LPF_NEW_DIR_BLOCK, &pctx);
+ return 0;
+ }
+
+ retval = ext2fs_write_dir_block(fs, blk, block);
+ ext2fs_free_mem(&block);
+ if (retval) {
+ pctx.errcode = retval;
+ fix_problem(ctx, PR_3_ERR_LPF_WRITE_BLOCK, &pctx);
+ return 0;
+ }
+
+ /*
+ * Set up the inode structure
+ */
+ memset(&inode, 0, sizeof(inode));
+ inode.i_mode = 040700;
+ inode.i_size = fs->blocksize;
+ inode.i_atime = inode.i_ctime = inode.i_mtime = time(0);
+ inode.i_links_count = 2;
+ inode.i_blocks = fs->blocksize / 512;
+ inode.i_block[0] = blk;
+
+ /*
+ * Next, write out the inode.
+ */
+ pctx.errcode = ext2fs_write_new_inode(fs, ino, &inode);
+ if (pctx.errcode) {
+ pctx.str = "ext2fs_write_inode";
+ fix_problem(ctx, PR_3_CREATE_LPF_ERROR, &pctx);
+ return 0;
+ }
+ /*
+ * Finally, create the directory link
+ */
+ pctx.errcode = ext2fs_link(fs, EXT2_ROOT_INO, name, ino, EXT2_FT_DIR);
+ if (pctx.errcode) {
+ pctx.str = "ext2fs_link";
+ fix_problem(ctx, PR_3_CREATE_LPF_ERROR, &pctx);
+ return 0;
+ }
+
+ /*
+ * Miscellaneous bookkeeping that needs to be kept straight.
+ */
+ e2fsck_add_dir_info(ctx, ino, EXT2_ROOT_INO);
+ e2fsck_adjust_inode_count(ctx, EXT2_ROOT_INO, 1);
+ ext2fs_icount_store(ctx->inode_count, ino, 2);
+ ext2fs_icount_store(ctx->inode_link_info, ino, 2);
+ ctx->lost_and_found = ino;
+ return ino;
+}
+
+/*
+ * This routine will connect a file to lost+found
+ */
+int e2fsck_reconnect_file(e2fsck_t ctx, ext2_ino_t ino)
+{
+ ext2_filsys fs = ctx->fs;
+ errcode_t retval;
+ char name[80];
+ struct problem_context pctx;
+ struct ext2_inode inode;
+ int file_type = 0;
+
+ clear_problem_context(&pctx);
+ pctx.ino = ino;
+
+ if (!ctx->bad_lost_and_found && !ctx->lost_and_found) {
+ if (e2fsck_get_lost_and_found(ctx, 1) == 0)
+ ctx->bad_lost_and_found++;
+ }
+ if (ctx->bad_lost_and_found) {
+ fix_problem(ctx, PR_3_NO_LPF, &pctx);
+ return 1;
+ }
+
+ sprintf(name, "#%u", ino);
+ if (ext2fs_read_inode(fs, ino, &inode) == 0)
+ file_type = ext2_file_type(inode.i_mode);
+ retval = ext2fs_link(fs, ctx->lost_and_found, name, ino, file_type);
+ if (retval == EXT2_ET_DIR_NO_SPACE) {
+ if (!fix_problem(ctx, PR_3_EXPAND_LF_DIR, &pctx))
+ return 1;
+ retval = e2fsck_expand_directory(ctx, ctx->lost_and_found,
+ 1, 0);
+ if (retval) {
+ pctx.errcode = retval;
+ fix_problem(ctx, PR_3_CANT_EXPAND_LPF, &pctx);
+ return 1;
+ }
+ retval = ext2fs_link(fs, ctx->lost_and_found, name,
+ ino, file_type);
+ }
+ if (retval) {
+ pctx.errcode = retval;
+ fix_problem(ctx, PR_3_CANT_RECONNECT, &pctx);
+ return 1;
+ }
+ e2fsck_adjust_inode_count(ctx, ino, 1);
+
+ return 0;
+}
+
+/*
+ * Utility routine to adjust the inode counts on an inode.
+ */
+errcode_t e2fsck_adjust_inode_count(e2fsck_t ctx, ext2_ino_t ino, int adj)
+{
+ ext2_filsys fs = ctx->fs;
+ errcode_t retval;
+ struct ext2_inode inode;
+
+ if (!ino)
+ return 0;
+
+ retval = ext2fs_read_inode(fs, ino, &inode);
+ if (retval)
+ return retval;
+
+ if (adj == 1) {
+ ext2fs_icount_increment(ctx->inode_count, ino, 0);
+ if (inode.i_links_count == (__u16) ~0)
+ return 0;
+ ext2fs_icount_increment(ctx->inode_link_info, ino, 0);
+ inode.i_links_count++;
+ } else if (adj == -1) {
+ ext2fs_icount_decrement(ctx->inode_count, ino, 0);
+ if (inode.i_links_count == 0)
+ return 0;
+ ext2fs_icount_decrement(ctx->inode_link_info, ino, 0);
+ inode.i_links_count--;
+ }
+
+ retval = ext2fs_write_inode(fs, ino, &inode);
+ if (retval)
+ return retval;
+
+ return 0;
+}
+
+/*
+ * Fix parent --- this routine fixes up the parent of a directory.
+ */
+struct fix_dotdot_struct {
+ ext2_filsys fs;
+ ext2_ino_t parent;
+ int done;
+ e2fsck_t ctx;
+};
+
+static int fix_dotdot_proc(struct ext2_dir_entry *dirent,
+ int offset FSCK_ATTR((unused)),
+ int blocksize FSCK_ATTR((unused)),
+ char *buf FSCK_ATTR((unused)),
+ void *priv_data)
+{
+ struct fix_dotdot_struct *fp = (struct fix_dotdot_struct *) priv_data;
+ errcode_t retval;
+ struct problem_context pctx;
+
+ if ((dirent->name_len & 0xFF) != 2)
+ return 0;
+ if (strncmp(dirent->name, "..", 2))
+ return 0;
+
+ clear_problem_context(&pctx);
+
+ retval = e2fsck_adjust_inode_count(fp->ctx, dirent->inode, -1);
+ if (retval) {
+ pctx.errcode = retval;
+ fix_problem(fp->ctx, PR_3_ADJUST_INODE, &pctx);
+ }
+ retval = e2fsck_adjust_inode_count(fp->ctx, fp->parent, 1);
+ if (retval) {
+ pctx.errcode = retval;
+ fix_problem(fp->ctx, PR_3_ADJUST_INODE, &pctx);
+ }
+ dirent->inode = fp->parent;
+
+ fp->done++;
+ return DIRENT_ABORT | DIRENT_CHANGED;
+}
+
+static void fix_dotdot(e2fsck_t ctx, struct dir_info *dir, ext2_ino_t parent)
+{
+ ext2_filsys fs = ctx->fs;
+ errcode_t retval;
+ struct fix_dotdot_struct fp;
+ struct problem_context pctx;
+
+ fp.fs = fs;
+ fp.parent = parent;
+ fp.done = 0;
+ fp.ctx = ctx;
+
+ retval = ext2fs_dir_iterate(fs, dir->ino, DIRENT_FLAG_INCLUDE_EMPTY,
+ 0, fix_dotdot_proc, &fp);
+ if (retval || !fp.done) {
+ clear_problem_context(&pctx);
+ pctx.ino = dir->ino;
+ pctx.errcode = retval;
+ fix_problem(ctx, retval ? PR_3_FIX_PARENT_ERR :
+ PR_3_FIX_PARENT_NOFIND, &pctx);
+ ext2fs_unmark_valid(fs);
+ }
+ dir->dotdot = parent;
+}
+
+/*
+ * These routines are responsible for expanding a /lost+found if it is
+ * too small.
+ */
+
+struct expand_dir_struct {
+ int num;
+ int guaranteed_size;
+ int newblocks;
+ int last_block;
+ errcode_t err;
+ e2fsck_t ctx;
+};
+
+static int expand_dir_proc(ext2_filsys fs,
+ blk_t *blocknr,
+ e2_blkcnt_t blockcnt,
+ blk_t ref_block FSCK_ATTR((unused)),
+ int ref_offset FSCK_ATTR((unused)),
+ void *priv_data)
+{
+ struct expand_dir_struct *es = (struct expand_dir_struct *) priv_data;
+ blk_t new_blk;
+ static blk_t last_blk = 0;
+ char *block;
+ errcode_t retval;
+ e2fsck_t ctx;
+
+ ctx = es->ctx;
+
+ if (es->guaranteed_size && blockcnt >= es->guaranteed_size)
+ return BLOCK_ABORT;
+
+ if (blockcnt > 0)
+ es->last_block = blockcnt;
+ if (*blocknr) {
+ last_blk = *blocknr;
+ return 0;
+ }
+ retval = ext2fs_new_block(fs, last_blk, ctx->block_found_map,
+ &new_blk);
+ if (retval) {
+ es->err = retval;
+ return BLOCK_ABORT;
+ }
+ if (blockcnt > 0) {
+ retval = ext2fs_new_dir_block(fs, 0, 0, &block);
+ if (retval) {
+ es->err = retval;
+ return BLOCK_ABORT;
+ }
+ es->num--;
+ retval = ext2fs_write_dir_block(fs, new_blk, block);
+ } else {
+ retval = ext2fs_get_mem(fs->blocksize, &block);
+ if (retval) {
+ es->err = retval;
+ return BLOCK_ABORT;
+ }
+ memset(block, 0, fs->blocksize);
+ retval = io_channel_write_blk(fs->io, new_blk, 1, block);
+ }
+ if (retval) {
+ es->err = retval;
+ return BLOCK_ABORT;
+ }
+ ext2fs_free_mem(&block);
+ *blocknr = new_blk;
+ ext2fs_mark_block_bitmap(ctx->block_found_map, new_blk);
+ ext2fs_block_alloc_stats(fs, new_blk, +1);
+ es->newblocks++;
+
+ if (es->num == 0)
+ return (BLOCK_CHANGED | BLOCK_ABORT);
+ else
+ return BLOCK_CHANGED;
+}
+
+errcode_t e2fsck_expand_directory(e2fsck_t ctx, ext2_ino_t dir,
+ int num, int guaranteed_size)
+{
+ ext2_filsys fs = ctx->fs;
+ errcode_t retval;
+ struct expand_dir_struct es;
+ struct ext2_inode inode;
+
+ if (!(fs->flags & EXT2_FLAG_RW))
+ return EXT2_ET_RO_FILSYS;
+
+ /*
+ * Read the inode and block bitmaps in; we'll be messing with
+ * them.
+ */
+ e2fsck_read_bitmaps(ctx);
+
+ retval = ext2fs_check_directory(fs, dir);
+ if (retval)
+ return retval;
+
+ es.num = num;
+ es.guaranteed_size = guaranteed_size;
+ es.last_block = 0;
+ es.err = 0;
+ es.newblocks = 0;
+ es.ctx = ctx;
+
+ retval = ext2fs_block_iterate2(fs, dir, BLOCK_FLAG_APPEND,
+ 0, expand_dir_proc, &es);
+
+ if (es.err)
+ return es.err;
+
+ /*
+ * Update the size and block count fields in the inode.
+ */
+ retval = ext2fs_read_inode(fs, dir, &inode);
+ if (retval)
+ return retval;
+
+ inode.i_size = (es.last_block + 1) * fs->blocksize;
+ inode.i_blocks += (fs->blocksize / 512) * es.newblocks;
+
+ e2fsck_write_inode(ctx, dir, &inode, "expand_directory");
+
+ return 0;
+}
+
+/*
+ * pass4.c -- pass #4 of e2fsck: Check reference counts
+ *
+ * Pass 4 frees the following data structures:
+ * - A bitmap of which inodes are imagic inodes. (inode_imagic_map)
+ */
+
+/*
+ * This routine is called when an inode is not connected to the
+ * directory tree.
+ *
+ * This subroutine returns 1 then the caller shouldn't bother with the
+ * rest of the pass 4 tests.
+ */
+static int disconnect_inode(e2fsck_t ctx, ext2_ino_t i)
+{
+ ext2_filsys fs = ctx->fs;
+ struct ext2_inode inode;
+ struct problem_context pctx;
+
+ e2fsck_read_inode(ctx, i, &inode, "pass4: disconnect_inode");
+ clear_problem_context(&pctx);
+ pctx.ino = i;
+ pctx.inode = &inode;
+
+ /*
+ * Offer to delete any zero-length files that does not have
+ * blocks. If there is an EA block, it might have useful
+ * information, so we won't prompt to delete it, but let it be
+ * reconnected to lost+found.
+ */
+ if (!inode.i_blocks && (LINUX_S_ISREG(inode.i_mode) ||
+ LINUX_S_ISDIR(inode.i_mode))) {
+ if (fix_problem(ctx, PR_4_ZERO_LEN_INODE, &pctx)) {
+ ext2fs_icount_store(ctx->inode_link_info, i, 0);
+ inode.i_links_count = 0;
+ inode.i_dtime = time(0);
+ e2fsck_write_inode(ctx, i, &inode,
+ "disconnect_inode");
+ /*
+ * Fix up the bitmaps...
+ */
+ e2fsck_read_bitmaps(ctx);
+ ext2fs_unmark_inode_bitmap(ctx->inode_used_map, i);
+ ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, i);
+ ext2fs_inode_alloc_stats2(fs, i, -1,
+ LINUX_S_ISDIR(inode.i_mode));
+ return 0;
+ }
+ }
+
+ /*
+ * Prompt to reconnect.
+ */
+ if (fix_problem(ctx, PR_4_UNATTACHED_INODE, &pctx)) {
+ if (e2fsck_reconnect_file(ctx, i))
+ ext2fs_unmark_valid(fs);
+ } else {
+ /*
+ * If we don't attach the inode, then skip the
+ * i_links_test since there's no point in trying to
+ * force i_links_count to zero.
+ */
+ ext2fs_unmark_valid(fs);
+ return 1;
+ }
+ return 0;
+}
+
+
+static void e2fsck_pass4(e2fsck_t ctx)
+{
+ ext2_filsys fs = ctx->fs;
+ ext2_ino_t i;
+ struct ext2_inode inode;
+ struct problem_context pctx;
+ __u16 link_count, link_counted;
+ char *buf = 0;
+ int group, maxgroup;
+
+ /* Pass 4 */
+
+ clear_problem_context(&pctx);
+
+ if (!(ctx->options & E2F_OPT_PREEN))
+ fix_problem(ctx, PR_4_PASS_HEADER, &pctx);
+
+ group = 0;
+ maxgroup = fs->group_desc_count;
+ if (ctx->progress)
+ if ((ctx->progress)(ctx, 4, 0, maxgroup))
+ return;
+
+ for (i=1; i <= fs->super->s_inodes_count; i++) {
+ if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+ return;
+ if ((i % fs->super->s_inodes_per_group) == 0) {
+ group++;
+ if (ctx->progress)
+ if ((ctx->progress)(ctx, 4, group, maxgroup))
+ return;
+ }
+ if (i == EXT2_BAD_INO ||
+ (i > EXT2_ROOT_INO && i < EXT2_FIRST_INODE(fs->super)))
+ continue;
+ if (!(ext2fs_test_inode_bitmap(ctx->inode_used_map, i)) ||
+ (ctx->inode_imagic_map &&
+ ext2fs_test_inode_bitmap(ctx->inode_imagic_map, i)))
+ continue;
+ ext2fs_icount_fetch(ctx->inode_link_info, i, &link_count);
+ ext2fs_icount_fetch(ctx->inode_count, i, &link_counted);
+ if (link_counted == 0) {
+ if (!buf)
+ buf = e2fsck_allocate_memory(ctx,
+ fs->blocksize, "bad_inode buffer");
+ if (e2fsck_process_bad_inode(ctx, 0, i, buf))
+ continue;
+ if (disconnect_inode(ctx, i))
+ continue;
+ ext2fs_icount_fetch(ctx->inode_link_info, i,
+ &link_count);
+ ext2fs_icount_fetch(ctx->inode_count, i,
+ &link_counted);
+ }
+ if (link_counted != link_count) {
+ e2fsck_read_inode(ctx, i, &inode, "pass4");
+ pctx.ino = i;
+ pctx.inode = &inode;
+ if (link_count != inode.i_links_count) {
+ pctx.num = link_count;
+ fix_problem(ctx,
+ PR_4_INCONSISTENT_COUNT, &pctx);
+ }
+ pctx.num = link_counted;
+ if (fix_problem(ctx, PR_4_BAD_REF_COUNT, &pctx)) {
+ inode.i_links_count = link_counted;
+ e2fsck_write_inode(ctx, i, &inode, "pass4");
+ }
+ }
+ }
+ ext2fs_free_icount(ctx->inode_link_info); ctx->inode_link_info = 0;
+ ext2fs_free_icount(ctx->inode_count); ctx->inode_count = 0;
+ ext2fs_free_inode_bitmap(ctx->inode_imagic_map);
+ ctx->inode_imagic_map = 0;
+ ext2fs_free_mem(&buf);
+}
+
+/*
+ * pass5.c --- check block and inode bitmaps against on-disk bitmaps
+ */
+
+#define NO_BLK ((blk_t) -1)
+
+static void print_bitmap_problem(e2fsck_t ctx, int problem,
+ struct problem_context *pctx)
+{
+ switch (problem) {
+ case PR_5_BLOCK_UNUSED:
+ if (pctx->blk == pctx->blk2)
+ pctx->blk2 = 0;
+ else
+ problem = PR_5_BLOCK_RANGE_UNUSED;
+ break;
+ case PR_5_BLOCK_USED:
+ if (pctx->blk == pctx->blk2)
+ pctx->blk2 = 0;
+ else
+ problem = PR_5_BLOCK_RANGE_USED;
+ break;
+ case PR_5_INODE_UNUSED:
+ if (pctx->ino == pctx->ino2)
+ pctx->ino2 = 0;
+ else
+ problem = PR_5_INODE_RANGE_UNUSED;
+ break;
+ case PR_5_INODE_USED:
+ if (pctx->ino == pctx->ino2)
+ pctx->ino2 = 0;
+ else
+ problem = PR_5_INODE_RANGE_USED;
+ break;
+ }
+ fix_problem(ctx, problem, pctx);
+ pctx->blk = pctx->blk2 = NO_BLK;
+ pctx->ino = pctx->ino2 = 0;
+}
+
+static void check_block_bitmaps(e2fsck_t ctx)
+{
+ ext2_filsys fs = ctx->fs;
+ blk_t i;
+ int *free_array;
+ int group = 0;
+ unsigned int blocks = 0;
+ unsigned int free_blocks = 0;
+ int group_free = 0;
+ int actual, bitmap;
+ struct problem_context pctx;
+ int problem, save_problem, fixit, had_problem;
+ errcode_t retval;
+
+ clear_problem_context(&pctx);
+ free_array = (int *) e2fsck_allocate_memory(ctx,
+ fs->group_desc_count * sizeof(int), "free block count array");
+
+ if ((fs->super->s_first_data_block <
+ ext2fs_get_block_bitmap_start(ctx->block_found_map)) ||
+ (fs->super->s_blocks_count-1 >
+ ext2fs_get_block_bitmap_end(ctx->block_found_map))) {
+ pctx.num = 1;
+ pctx.blk = fs->super->s_first_data_block;
+ pctx.blk2 = fs->super->s_blocks_count -1;
+ pctx.ino = ext2fs_get_block_bitmap_start(ctx->block_found_map);
+ pctx.ino2 = ext2fs_get_block_bitmap_end(ctx->block_found_map);
+ fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx);
+
+ ctx->flags |= E2F_FLAG_ABORT; /* fatal */
+ return;
+ }
+
+ if ((fs->super->s_first_data_block <
+ ext2fs_get_block_bitmap_start(fs->block_map)) ||
+ (fs->super->s_blocks_count-1 >
+ ext2fs_get_block_bitmap_end(fs->block_map))) {
+ pctx.num = 2;
+ pctx.blk = fs->super->s_first_data_block;
+ pctx.blk2 = fs->super->s_blocks_count -1;
+ pctx.ino = ext2fs_get_block_bitmap_start(fs->block_map);
+ pctx.ino2 = ext2fs_get_block_bitmap_end(fs->block_map);
+ fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx);
+
+ ctx->flags |= E2F_FLAG_ABORT; /* fatal */
+ return;
+ }
+
+redo_counts:
+ had_problem = 0;
+ save_problem = 0;
+ pctx.blk = pctx.blk2 = NO_BLK;
+ for (i = fs->super->s_first_data_block;
+ i < fs->super->s_blocks_count;
+ i++) {
+ actual = ext2fs_fast_test_block_bitmap(ctx->block_found_map, i);
+ bitmap = ext2fs_fast_test_block_bitmap(fs->block_map, i);
+
+ if (actual == bitmap)
+ goto do_counts;
+
+ if (!actual && bitmap) {
+ /*
+ * Block not used, but marked in use in the bitmap.
+ */
+ problem = PR_5_BLOCK_UNUSED;
+ } else {
+ /*
+ * Block used, but not marked in use in the bitmap.
+ */
+ problem = PR_5_BLOCK_USED;
+ }
+ if (pctx.blk == NO_BLK) {
+ pctx.blk = pctx.blk2 = i;
+ save_problem = problem;
+ } else {
+ if ((problem == save_problem) &&
+ (pctx.blk2 == i-1))
+ pctx.blk2++;
+ else {
+ print_bitmap_problem(ctx, save_problem, &pctx);
+ pctx.blk = pctx.blk2 = i;
+ save_problem = problem;
+ }
+ }
+ ctx->flags |= E2F_FLAG_PROG_SUPPRESS;
+ had_problem++;
+
+ do_counts:
+ if (!bitmap) {
+ group_free++;
+ free_blocks++;
+ }
+ blocks ++;
+ if ((blocks == fs->super->s_blocks_per_group) ||
+ (i == fs->super->s_blocks_count-1)) {
+ free_array[group] = group_free;
+ group ++;
+ blocks = 0;
+ group_free = 0;
+ if (ctx->progress)
+ if ((ctx->progress)(ctx, 5, group,
+ fs->group_desc_count*2))
+ return;
+ }
+ }
+ if (pctx.blk != NO_BLK)
+ print_bitmap_problem(ctx, save_problem, &pctx);
+ if (had_problem)
+ fixit = end_problem_latch(ctx, PR_LATCH_BBITMAP);
+ else
+ fixit = -1;
+ ctx->flags &= ~E2F_FLAG_PROG_SUPPRESS;
+
+ if (fixit == 1) {
+ ext2fs_free_block_bitmap(fs->block_map);
+ retval = ext2fs_copy_bitmap(ctx->block_found_map,
+ &fs->block_map);
+ if (retval) {
+ clear_problem_context(&pctx);
+ fix_problem(ctx, PR_5_COPY_BBITMAP_ERROR, &pctx);
+ ctx->flags |= E2F_FLAG_ABORT;
+ return;
+ }
+ ext2fs_set_bitmap_padding(fs->block_map);
+ ext2fs_mark_bb_dirty(fs);
+
+ /* Redo the counts */
+ blocks = 0; free_blocks = 0; group_free = 0; group = 0;
+ memset(free_array, 0, fs->group_desc_count * sizeof(int));
+ goto redo_counts;
+ } else if (fixit == 0)
+ ext2fs_unmark_valid(fs);
+
+ for (i = 0; i < fs->group_desc_count; i++) {
+ if (free_array[i] != fs->group_desc[i].bg_free_blocks_count) {
+ pctx.group = i;
+ pctx.blk = fs->group_desc[i].bg_free_blocks_count;
+ pctx.blk2 = free_array[i];
+
+ if (fix_problem(ctx, PR_5_FREE_BLOCK_COUNT_GROUP,
+ &pctx)) {
+ fs->group_desc[i].bg_free_blocks_count =
+ free_array[i];
+ ext2fs_mark_super_dirty(fs);
+ } else
+ ext2fs_unmark_valid(fs);
+ }
+ }
+ if (free_blocks != fs->super->s_free_blocks_count) {
+ pctx.group = 0;
+ pctx.blk = fs->super->s_free_blocks_count;
+ pctx.blk2 = free_blocks;
+
+ if (fix_problem(ctx, PR_5_FREE_BLOCK_COUNT, &pctx)) {
+ fs->super->s_free_blocks_count = free_blocks;
+ ext2fs_mark_super_dirty(fs);
+ } else
+ ext2fs_unmark_valid(fs);
+ }
+ ext2fs_free_mem(&free_array);
+}
+
+static void check_inode_bitmaps(e2fsck_t ctx)
+{
+ ext2_filsys fs = ctx->fs;
+ ext2_ino_t i;
+ unsigned int free_inodes = 0;
+ int group_free = 0;
+ int dirs_count = 0;
+ int group = 0;
+ unsigned int inodes = 0;
+ int *free_array;
+ int *dir_array;
+ int actual, bitmap;
+ errcode_t retval;
+ struct problem_context pctx;
+ int problem, save_problem, fixit, had_problem;
+
+ clear_problem_context(&pctx);
+ free_array = (int *) e2fsck_allocate_memory(ctx,
+ fs->group_desc_count * sizeof(int), "free inode count array");
+
+ dir_array = (int *) e2fsck_allocate_memory(ctx,
+ fs->group_desc_count * sizeof(int), "directory count array");
+
+ if ((1 < ext2fs_get_inode_bitmap_start(ctx->inode_used_map)) ||
+ (fs->super->s_inodes_count >
+ ext2fs_get_inode_bitmap_end(ctx->inode_used_map))) {
+ pctx.num = 3;
+ pctx.blk = 1;
+ pctx.blk2 = fs->super->s_inodes_count;
+ pctx.ino = ext2fs_get_inode_bitmap_start(ctx->inode_used_map);
+ pctx.ino2 = ext2fs_get_inode_bitmap_end(ctx->inode_used_map);
+ fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx);
+
+ ctx->flags |= E2F_FLAG_ABORT; /* fatal */
+ return;
+ }
+ if ((1 < ext2fs_get_inode_bitmap_start(fs->inode_map)) ||
+ (fs->super->s_inodes_count >
+ ext2fs_get_inode_bitmap_end(fs->inode_map))) {
+ pctx.num = 4;
+ pctx.blk = 1;
+ pctx.blk2 = fs->super->s_inodes_count;
+ pctx.ino = ext2fs_get_inode_bitmap_start(fs->inode_map);
+ pctx.ino2 = ext2fs_get_inode_bitmap_end(fs->inode_map);
+ fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx);
+
+ ctx->flags |= E2F_FLAG_ABORT; /* fatal */
+ return;
+ }
+
+redo_counts:
+ had_problem = 0;
+ save_problem = 0;
+ pctx.ino = pctx.ino2 = 0;
+ for (i = 1; i <= fs->super->s_inodes_count; i++) {
+ actual = ext2fs_fast_test_inode_bitmap(ctx->inode_used_map, i);
+ bitmap = ext2fs_fast_test_inode_bitmap(fs->inode_map, i);
+
+ if (actual == bitmap)
+ goto do_counts;
+
+ if (!actual && bitmap) {
+ /*
+ * Inode wasn't used, but marked in bitmap
+ */
+ problem = PR_5_INODE_UNUSED;
+ } else /* if (actual && !bitmap) */ {
+ /*
+ * Inode used, but not in bitmap
+ */
+ problem = PR_5_INODE_USED;
+ }
+ if (pctx.ino == 0) {
+ pctx.ino = pctx.ino2 = i;
+ save_problem = problem;
+ } else {
+ if ((problem == save_problem) &&
+ (pctx.ino2 == i-1))
+ pctx.ino2++;
+ else {
+ print_bitmap_problem(ctx, save_problem, &pctx);
+ pctx.ino = pctx.ino2 = i;
+ save_problem = problem;
+ }
+ }
+ ctx->flags |= E2F_FLAG_PROG_SUPPRESS;
+ had_problem++;
+
+do_counts:
+ if (!bitmap) {
+ group_free++;
+ free_inodes++;
+ } else {
+ if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, i))
+ dirs_count++;
+ }
+ inodes++;
+ if ((inodes == fs->super->s_inodes_per_group) ||
+ (i == fs->super->s_inodes_count)) {
+ free_array[group] = group_free;
+ dir_array[group] = dirs_count;
+ group ++;
+ inodes = 0;
+ group_free = 0;
+ dirs_count = 0;
+ if (ctx->progress)
+ if ((ctx->progress)(ctx, 5,
+ group + fs->group_desc_count,
+ fs->group_desc_count*2))
+ return;
+ }
+ }
+ if (pctx.ino)
+ print_bitmap_problem(ctx, save_problem, &pctx);
+
+ if (had_problem)
+ fixit = end_problem_latch(ctx, PR_LATCH_IBITMAP);
+ else
+ fixit = -1;
+ ctx->flags &= ~E2F_FLAG_PROG_SUPPRESS;
+
+ if (fixit == 1) {
+ ext2fs_free_inode_bitmap(fs->inode_map);
+ retval = ext2fs_copy_bitmap(ctx->inode_used_map,
+ &fs->inode_map);
+ if (retval) {
+ clear_problem_context(&pctx);
+ fix_problem(ctx, PR_5_COPY_IBITMAP_ERROR, &pctx);
+ ctx->flags |= E2F_FLAG_ABORT;
+ return;
+ }
+ ext2fs_set_bitmap_padding(fs->inode_map);
+ ext2fs_mark_ib_dirty(fs);
+
+ /* redo counts */
+ inodes = 0; free_inodes = 0; group_free = 0;
+ dirs_count = 0; group = 0;
+ memset(free_array, 0, fs->group_desc_count * sizeof(int));
+ memset(dir_array, 0, fs->group_desc_count * sizeof(int));
+ goto redo_counts;
+ } else if (fixit == 0)
+ ext2fs_unmark_valid(fs);
+
+ for (i = 0; i < fs->group_desc_count; i++) {
+ if (free_array[i] != fs->group_desc[i].bg_free_inodes_count) {
+ pctx.group = i;
+ pctx.ino = fs->group_desc[i].bg_free_inodes_count;
+ pctx.ino2 = free_array[i];
+ if (fix_problem(ctx, PR_5_FREE_INODE_COUNT_GROUP,
+ &pctx)) {
+ fs->group_desc[i].bg_free_inodes_count =
+ free_array[i];
+ ext2fs_mark_super_dirty(fs);
+ } else
+ ext2fs_unmark_valid(fs);
+ }
+ if (dir_array[i] != fs->group_desc[i].bg_used_dirs_count) {
+ pctx.group = i;
+ pctx.ino = fs->group_desc[i].bg_used_dirs_count;
+ pctx.ino2 = dir_array[i];
+
+ if (fix_problem(ctx, PR_5_FREE_DIR_COUNT_GROUP,
+ &pctx)) {
+ fs->group_desc[i].bg_used_dirs_count =
+ dir_array[i];
+ ext2fs_mark_super_dirty(fs);
+ } else
+ ext2fs_unmark_valid(fs);
+ }
+ }
+ if (free_inodes != fs->super->s_free_inodes_count) {
+ pctx.group = -1;
+ pctx.ino = fs->super->s_free_inodes_count;
+ pctx.ino2 = free_inodes;
+
+ if (fix_problem(ctx, PR_5_FREE_INODE_COUNT, &pctx)) {
+ fs->super->s_free_inodes_count = free_inodes;
+ ext2fs_mark_super_dirty(fs);
+ } else
+ ext2fs_unmark_valid(fs);
+ }
+ ext2fs_free_mem(&free_array);
+ ext2fs_free_mem(&dir_array);
+}
+
+static void check_inode_end(e2fsck_t ctx)
+{
+ ext2_filsys fs = ctx->fs;
+ ext2_ino_t end, save_inodes_count, i;
+ struct problem_context pctx;
+
+ clear_problem_context(&pctx);
+
+ end = EXT2_INODES_PER_GROUP(fs->super) * fs->group_desc_count;
+ pctx.errcode = ext2fs_fudge_inode_bitmap_end(fs->inode_map, end,
+ &save_inodes_count);
+ if (pctx.errcode) {
+ pctx.num = 1;
+ fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx);
+ ctx->flags |= E2F_FLAG_ABORT; /* fatal */
+ return;
+ }
+ if (save_inodes_count == end)
+ return;
+
+ for (i = save_inodes_count + 1; i <= end; i++) {
+ if (!ext2fs_test_inode_bitmap(fs->inode_map, i)) {
+ if (fix_problem(ctx, PR_5_INODE_BMAP_PADDING, &pctx)) {
+ for (i = save_inodes_count + 1; i <= end; i++)
+ ext2fs_mark_inode_bitmap(fs->inode_map,
+ i);
+ ext2fs_mark_ib_dirty(fs);
+ } else
+ ext2fs_unmark_valid(fs);
+ break;
+ }
+ }
+
+ pctx.errcode = ext2fs_fudge_inode_bitmap_end(fs->inode_map,
+ save_inodes_count, 0);
+ if (pctx.errcode) {
+ pctx.num = 2;
+ fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx);
+ ctx->flags |= E2F_FLAG_ABORT; /* fatal */
+ return;
+ }
+}
+
+static void check_block_end(e2fsck_t ctx)
+{
+ ext2_filsys fs = ctx->fs;
+ blk_t end, save_blocks_count, i;
+ struct problem_context pctx;
+
+ clear_problem_context(&pctx);
+
+ end = fs->block_map->start +
+ (EXT2_BLOCKS_PER_GROUP(fs->super) * fs->group_desc_count) - 1;
+ pctx.errcode = ext2fs_fudge_block_bitmap_end(fs->block_map, end,
+ &save_blocks_count);
+ if (pctx.errcode) {
+ pctx.num = 3;
+ fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx);
+ ctx->flags |= E2F_FLAG_ABORT; /* fatal */
+ return;
+ }
+ if (save_blocks_count == end)
+ return;
+
+ for (i = save_blocks_count + 1; i <= end; i++) {
+ if (!ext2fs_test_block_bitmap(fs->block_map, i)) {
+ if (fix_problem(ctx, PR_5_BLOCK_BMAP_PADDING, &pctx)) {
+ for (i = save_blocks_count + 1; i <= end; i++)
+ ext2fs_mark_block_bitmap(fs->block_map,
+ i);
+ ext2fs_mark_bb_dirty(fs);
+ } else
+ ext2fs_unmark_valid(fs);
+ break;
+ }
+ }
+
+ pctx.errcode = ext2fs_fudge_block_bitmap_end(fs->block_map,
+ save_blocks_count, 0);
+ if (pctx.errcode) {
+ pctx.num = 4;
+ fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx);
+ ctx->flags |= E2F_FLAG_ABORT; /* fatal */
+ return;
+ }
+}
+
+static void e2fsck_pass5(e2fsck_t ctx)
+{
+ struct problem_context pctx;
+
+ /* Pass 5 */
+
+ clear_problem_context(&pctx);
+
+ if (!(ctx->options & E2F_OPT_PREEN))
+ fix_problem(ctx, PR_5_PASS_HEADER, &pctx);
+
+ if (ctx->progress)
+ if ((ctx->progress)(ctx, 5, 0, ctx->fs->group_desc_count*2))
+ return;
+
+ e2fsck_read_bitmaps(ctx);
+
+ check_block_bitmaps(ctx);
+ if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+ return;
+ check_inode_bitmaps(ctx);
+ if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+ return;
+ check_inode_end(ctx);
+ if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+ return;
+ check_block_end(ctx);
+ if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+ return;
+
+ ext2fs_free_inode_bitmap(ctx->inode_used_map);
+ ctx->inode_used_map = 0;
+ ext2fs_free_inode_bitmap(ctx->inode_dir_map);
+ ctx->inode_dir_map = 0;
+ ext2fs_free_block_bitmap(ctx->block_found_map);
+ ctx->block_found_map = 0;
+}
+
+/*
+ * problem.c --- report filesystem problems to the user
+ */
+
+#define PR_PREEN_OK 0x000001 /* Don't need to do preenhalt */
+#define PR_NO_OK 0x000002 /* If user answers no, don't make fs invalid */
+#define PR_NO_DEFAULT 0x000004 /* Default to no */
+#define PR_MSG_ONLY 0x000008 /* Print message only */
+
+/* Bit positions 0x000ff0 are reserved for the PR_LATCH flags */
+
+#define PR_FATAL 0x001000 /* Fatal error */
+#define PR_AFTER_CODE 0x002000 /* After asking the first question, */
+ /* ask another */
+#define PR_PREEN_NOMSG 0x004000 /* Don't print a message if we're preening */
+#define PR_NOCOLLATE 0x008000 /* Don't collate answers for this latch */
+#define PR_NO_NOMSG 0x010000 /* Don't print a message if e2fsck -n */
+#define PR_PREEN_NO 0x020000 /* Use No as an answer if preening */
+#define PR_PREEN_NOHDR 0x040000 /* Don't print the preen header */
+
+
+#define PROMPT_NONE 0
+#define PROMPT_FIX 1
+#define PROMPT_CLEAR 2
+#define PROMPT_RELOCATE 3
+#define PROMPT_ALLOCATE 4
+#define PROMPT_EXPAND 5
+#define PROMPT_CONNECT 6
+#define PROMPT_CREATE 7
+#define PROMPT_SALVAGE 8
+#define PROMPT_TRUNCATE 9
+#define PROMPT_CLEAR_INODE 10
+#define PROMPT_ABORT 11
+#define PROMPT_SPLIT 12
+#define PROMPT_CONTINUE 13
+#define PROMPT_CLONE 14
+#define PROMPT_DELETE 15
+#define PROMPT_SUPPRESS 16
+#define PROMPT_UNLINK 17
+#define PROMPT_CLEAR_HTREE 18
+#define PROMPT_RECREATE 19
+#define PROMPT_NULL 20
+
+struct e2fsck_problem {
+ problem_t e2p_code;
+ const char * e2p_description;
+ char prompt;
+ int flags;
+ problem_t second_code;
+};
+
+struct latch_descr {
+ int latch_code;
+ problem_t question;
+ problem_t end_message;
+ int flags;
+};
+
+/*
+ * These are the prompts which are used to ask the user if they want
+ * to fix a problem.
+ */
+static const char * const prompt[] = {
+ N_("(no prompt)"), /* 0 */
+ N_("Fix"), /* 1 */
+ N_("Clear"), /* 2 */
+ N_("Relocate"), /* 3 */
+ N_("Allocate"), /* 4 */
+ N_("Expand"), /* 5 */
+ N_("Connect to /lost+found"), /* 6 */
+ N_("Create"), /* 7 */
+ N_("Salvage"), /* 8 */
+ N_("Truncate"), /* 9 */
+ N_("Clear inode"), /* 10 */
+ N_("Abort"), /* 11 */
+ N_("Split"), /* 12 */
+ N_("Continue"), /* 13 */
+ N_("Clone multiply-claimed blocks"), /* 14 */
+ N_("Delete file"), /* 15 */
+ N_("Suppress messages"),/* 16 */
+ N_("Unlink"), /* 17 */
+ N_("Clear HTree index"),/* 18 */
+ N_("Recreate"), /* 19 */
+ "", /* 20 */
+};
+
+/*
+ * These messages are printed when we are preen mode and we will be
+ * automatically fixing the problem.
+ */
+static const char * const preen_msg[] = {
+ N_("(NONE)"), /* 0 */
+ N_("FIXED"), /* 1 */
+ N_("CLEARED"), /* 2 */
+ N_("RELOCATED"), /* 3 */
+ N_("ALLOCATED"), /* 4 */
+ N_("EXPANDED"), /* 5 */
+ N_("RECONNECTED"), /* 6 */
+ N_("CREATED"), /* 7 */
+ N_("SALVAGED"), /* 8 */
+ N_("TRUNCATED"), /* 9 */
+ N_("INODE CLEARED"), /* 10 */
+ N_("ABORTED"), /* 11 */
+ N_("SPLIT"), /* 12 */
+ N_("CONTINUING"), /* 13 */
+ N_("MULTIPLY-CLAIMED BLOCKS CLONED"), /* 14 */
+ N_("FILE DELETED"), /* 15 */
+ N_("SUPPRESSED"), /* 16 */
+ N_("UNLINKED"), /* 17 */
+ N_("HTREE INDEX CLEARED"),/* 18 */
+ N_("WILL RECREATE"), /* 19 */
+ "", /* 20 */
+};
+
+static const struct e2fsck_problem problem_table[] = {
+
+ /* Pre-Pass 1 errors */
+
+ /* Block bitmap not in group */
+ { PR_0_BB_NOT_GROUP, N_("@b @B for @g %g is not in @g. (@b %b)\n"),
+ PROMPT_RELOCATE, PR_LATCH_RELOC },
+
+ /* Inode bitmap not in group */
+ { PR_0_IB_NOT_GROUP, N_("@i @B for @g %g is not in @g. (@b %b)\n"),
+ PROMPT_RELOCATE, PR_LATCH_RELOC },
+
+ /* Inode table not in group */
+ { PR_0_ITABLE_NOT_GROUP,
+ N_("@i table for @g %g is not in @g. (@b %b)\n"
+ "WARNING: SEVERE DATA LOSS POSSIBLE.\n"),
+ PROMPT_RELOCATE, PR_LATCH_RELOC },
+
+ /* Superblock corrupt */
+ { PR_0_SB_CORRUPT,
+ N_("\nThe @S could not be read or does not describe a correct ext2\n"
+ "@f. If the @v is valid and it really contains an ext2\n"
+ "@f (and not swap or ufs or something else), then the @S\n"
+ "is corrupt, and you might try running e2fsck with an alternate @S:\n"
+ " e2fsck -b %S <@v>\n\n"),
+ PROMPT_NONE, PR_FATAL },
+
+ /* Filesystem size is wrong */
+ { PR_0_FS_SIZE_WRONG,
+ N_("The @f size (according to the @S) is %b @bs\n"
+ "The physical size of the @v is %c @bs\n"
+ "Either the @S or the partition table is likely to be corrupt!\n"),
+ PROMPT_ABORT, 0 },
+
+ /* Fragments not supported */
+ { PR_0_NO_FRAGMENTS,
+ N_("@S @b_size = %b, fragsize = %c.\n"
+ "This version of e2fsck does not support fragment sizes different\n"
+ "from the @b size.\n"),
+ PROMPT_NONE, PR_FATAL },
+
+ /* Bad blocks_per_group */
+ { PR_0_BLOCKS_PER_GROUP,
+ N_("@S @bs_per_group = %b, should have been %c\n"),
+ PROMPT_NONE, PR_AFTER_CODE, PR_0_SB_CORRUPT },
+
+ /* Bad first_data_block */
+ { PR_0_FIRST_DATA_BLOCK,
+ N_("@S first_data_@b = %b, should have been %c\n"),
+ PROMPT_NONE, PR_AFTER_CODE, PR_0_SB_CORRUPT },
+
+ /* Adding UUID to filesystem */
+ { PR_0_ADD_UUID,
+ N_("@f did not have a UUID; generating one.\n\n"),
+ PROMPT_NONE, 0 },
+
+ /* Relocate hint */
+ { PR_0_RELOCATE_HINT,
+ N_("Note: if several inode or block bitmap blocks or part\n"
+ "of the inode table require relocation, you may wish to try\n"
+ "running e2fsck with the '-b %S' option first. The problem\n"
+ "may lie only with the primary block group descriptors, and\n"
+ "the backup block group descriptors may be OK.\n\n"),
+ PROMPT_NONE, PR_PREEN_OK | PR_NOCOLLATE },
+
+ /* Miscellaneous superblock corruption */
+ { PR_0_MISC_CORRUPT_SUPER,
+ N_("Corruption found in @S. (%s = %N).\n"),
+ PROMPT_NONE, PR_AFTER_CODE, PR_0_SB_CORRUPT },
+
+ /* Error determing physical device size of filesystem */
+ { PR_0_GETSIZE_ERROR,
+ N_("Error determining size of the physical @v: %m\n"),
+ PROMPT_NONE, PR_FATAL },
+
+ /* Inode count in superblock is incorrect */
+ { PR_0_INODE_COUNT_WRONG,
+ N_("@i count in @S is %i, @s %j.\n"),
+ PROMPT_FIX, 0 },
+
+ { PR_0_HURD_CLEAR_FILETYPE,
+ N_("The Hurd does not support the filetype feature.\n"),
+ PROMPT_CLEAR, 0 },
+
+ /* Journal inode is invalid */
+ { PR_0_JOURNAL_BAD_INODE,
+ N_("@S has an @n ext3 @j (@i %i).\n"),
+ PROMPT_CLEAR, PR_PREEN_OK },
+
+ /* The external journal has (unsupported) multiple filesystems */
+ { PR_0_JOURNAL_UNSUPP_MULTIFS,
+ N_("External @j has multiple @f users (unsupported).\n"),
+ PROMPT_NONE, PR_FATAL },
+
+ /* Can't find external journal */
+ { PR_0_CANT_FIND_JOURNAL,
+ N_("Can't find external @j\n"),
+ PROMPT_NONE, PR_FATAL },
+
+ /* External journal has bad superblock */
+ { PR_0_EXT_JOURNAL_BAD_SUPER,
+ N_("External @j has bad @S\n"),
+ PROMPT_NONE, PR_FATAL },
+
+ /* Superblock has a bad journal UUID */
+ { PR_0_JOURNAL_BAD_UUID,
+ N_("External @j does not support this @f\n"),
+ PROMPT_NONE, PR_FATAL },
+
+ /* Journal has an unknown superblock type */
+ { PR_0_JOURNAL_UNSUPP_SUPER,
+ N_("Ext3 @j @S is unknown type %N (unsupported).\n"
+ "It is likely that your copy of e2fsck is old and/or doesn't "
+ "support this @j format.\n"
+ "It is also possible the @j @S is corrupt.\n"),
+ PROMPT_ABORT, PR_NO_OK | PR_AFTER_CODE, PR_0_JOURNAL_BAD_SUPER },
+
+ /* Journal superblock is corrupt */
+ { PR_0_JOURNAL_BAD_SUPER,
+ N_("Ext3 @j @S is corrupt.\n"),
+ PROMPT_FIX, PR_PREEN_OK },
+
+ /* Superblock flag should be cleared */
+ { PR_0_JOURNAL_HAS_JOURNAL,
+ N_("@S doesn't have has_@j flag, but has ext3 @j %s.\n"),
+ PROMPT_CLEAR, PR_PREEN_OK },
+
+ /* Superblock flag is incorrect */
+ { PR_0_JOURNAL_RECOVER_SET,
+ N_("@S has ext3 needs_recovery flag set, but no @j.\n"),
+ PROMPT_CLEAR, PR_PREEN_OK },
+
+ /* Journal has data, but recovery flag is clear */
+ { PR_0_JOURNAL_RECOVERY_CLEAR,
+ N_("ext3 recovery flag is clear, but @j has data.\n"),
+ PROMPT_NONE, 0 },
+
+ /* Ask if we should clear the journal */
+ { PR_0_JOURNAL_RESET_JOURNAL,
+ N_("Clear @j"),
+ PROMPT_NULL, PR_PREEN_NOMSG },
+
+ /* Ask if we should run the journal anyway */
+ { PR_0_JOURNAL_RUN,
+ N_("Run @j anyway"),
+ PROMPT_NULL, 0 },
+
+ /* Run the journal by default */
+ { PR_0_JOURNAL_RUN_DEFAULT,
+ N_("Recovery flag not set in backup @S, so running @j anyway.\n"),
+ PROMPT_NONE, 0 },
+
+ /* Clearing orphan inode */
+ { PR_0_ORPHAN_CLEAR_INODE,
+ N_("%s @o @i %i (uid=%Iu, gid=%Ig, mode=%Im, size=%Is)\n"),
+ PROMPT_NONE, 0 },
+
+ /* Illegal block found in orphaned inode */
+ { PR_0_ORPHAN_ILLEGAL_BLOCK_NUM,
+ N_("@I @b #%B (%b) found in @o @i %i.\n"),
+ PROMPT_NONE, 0 },
+
+ /* Already cleared block found in orphaned inode */
+ { PR_0_ORPHAN_ALREADY_CLEARED_BLOCK,
+ N_("Already cleared @b #%B (%b) found in @o @i %i.\n"),
+ PROMPT_NONE, 0 },
+
+ /* Illegal orphan inode in superblock */
+ { PR_0_ORPHAN_ILLEGAL_HEAD_INODE,
+ N_("@I @o @i %i in @S.\n"),
+ PROMPT_NONE, 0 },
+
+ /* Illegal inode in orphaned inode list */
+ { PR_0_ORPHAN_ILLEGAL_INODE,
+ N_("@I @i %i in @o @i list.\n"),
+ PROMPT_NONE, 0 },
+
+ /* Filesystem revision is 0, but feature flags are set */
+ { PR_0_FS_REV_LEVEL,
+ N_("@f has feature flag(s) set, but is a revision 0 @f. "),
+ PROMPT_FIX, PR_PREEN_OK | PR_NO_OK },
+
+ /* Journal superblock has an unknown read-only feature flag set */
+ { PR_0_JOURNAL_UNSUPP_ROCOMPAT,
+ N_("Ext3 @j @S has an unknown read-only feature flag set.\n"),
+ PROMPT_ABORT, 0 },
+
+ /* Journal superblock has an unknown incompatible feature flag set */
+ { PR_0_JOURNAL_UNSUPP_INCOMPAT,
+ N_("Ext3 @j @S has an unknown incompatible feature flag set.\n"),
+ PROMPT_ABORT, 0 },
+
+ /* Journal has unsupported version number */
+ { PR_0_JOURNAL_UNSUPP_VERSION,
+ N_("@j version not supported by this e2fsck.\n"),
+ PROMPT_ABORT, 0 },
+
+ /* Moving journal to hidden file */
+ { PR_0_MOVE_JOURNAL,
+ N_("Moving @j from /%s to hidden @i.\n\n"),
+ PROMPT_NONE, 0 },
+
+ /* Error moving journal to hidden file */
+ { PR_0_ERR_MOVE_JOURNAL,
+ N_("Error moving @j: %m\n\n"),
+ PROMPT_NONE, 0 },
+
+ /* Clearing V2 journal superblock */
+ { PR_0_CLEAR_V2_JOURNAL,
+ N_("Found @n V2 @j @S fields (from V1 @j).\n"
+ "Clearing fields beyond the V1 @j @S...\n\n"),
+ PROMPT_NONE, 0 },
+
+ /* Backup journal inode blocks */
+ { PR_0_BACKUP_JNL,
+ N_("Backing up @j @i @b information.\n\n"),
+ PROMPT_NONE, 0 },
+
+ /* Reserved blocks w/o resize_inode */
+ { PR_0_NONZERO_RESERVED_GDT_BLOCKS,
+ N_("@f does not have resize_@i enabled, but s_reserved_gdt_@bs\n"
+ "is %N; @s zero. "),
+ PROMPT_FIX, 0 },
+
+ /* Resize_inode not enabled, but resize inode is non-zero */
+ { PR_0_CLEAR_RESIZE_INODE,
+ N_("Resize_@i not enabled, but the resize @i is non-zero. "),
+ PROMPT_CLEAR, 0 },
+
+ /* Resize inode invalid */
+ { PR_0_RESIZE_INODE_INVALID,
+ N_("Resize @i not valid. "),
+ PROMPT_RECREATE, 0 },
+
+ /* Pass 1 errors */
+
+ /* Pass 1: Checking inodes, blocks, and sizes */
+ { PR_1_PASS_HEADER,
+ N_("Pass 1: Checking @is, @bs, and sizes\n"),
+ PROMPT_NONE, 0 },
+
+ /* Root directory is not an inode */
+ { PR_1_ROOT_NO_DIR, N_("@r is not a @d. "),
+ PROMPT_CLEAR, 0 },
+
+ /* Root directory has dtime set */
+ { PR_1_ROOT_DTIME,
+ N_("@r has dtime set (probably due to old mke2fs). "),
+ PROMPT_FIX, PR_PREEN_OK },
+
+ /* Reserved inode has bad mode */
+ { PR_1_RESERVED_BAD_MODE,
+ N_("Reserved @i %i (%Q) has @n mode. "),
+ PROMPT_CLEAR, PR_PREEN_OK },
+
+ /* Deleted inode has zero dtime */
+ { PR_1_ZERO_DTIME,
+ N_("@D @i %i has zero dtime. "),
+ PROMPT_FIX, PR_PREEN_OK },
+
+ /* Inode in use, but dtime set */
+ { PR_1_SET_DTIME,
+ N_("@i %i is in use, but has dtime set. "),
+ PROMPT_FIX, PR_PREEN_OK },
+
+ /* Zero-length directory */
+ { PR_1_ZERO_LENGTH_DIR,
+ N_("@i %i is a @z @d. "),
+ PROMPT_CLEAR, PR_PREEN_OK },
+
+ /* Block bitmap conflicts with some other fs block */
+ { PR_1_BB_CONFLICT,
+ N_("@g %g's @b @B at %b @C.\n"),
+ PROMPT_RELOCATE, 0 },
+
+ /* Inode bitmap conflicts with some other fs block */
+ { PR_1_IB_CONFLICT,
+ N_("@g %g's @i @B at %b @C.\n"),
+ PROMPT_RELOCATE, 0 },
+
+ /* Inode table conflicts with some other fs block */
+ { PR_1_ITABLE_CONFLICT,
+ N_("@g %g's @i table at %b @C.\n"),
+ PROMPT_RELOCATE, 0 },
+
+ /* Block bitmap is on a bad block */
+ { PR_1_BB_BAD_BLOCK,
+ N_("@g %g's @b @B (%b) is bad. "),
+ PROMPT_RELOCATE, 0 },
+
+ /* Inode bitmap is on a bad block */
+ { PR_1_IB_BAD_BLOCK,
+ N_("@g %g's @i @B (%b) is bad. "),
+ PROMPT_RELOCATE, 0 },
+
+ /* Inode has incorrect i_size */
+ { PR_1_BAD_I_SIZE,
+ N_("@i %i, i_size is %Is, @s %N. "),
+ PROMPT_FIX, PR_PREEN_OK },
+
+ /* Inode has incorrect i_blocks */
+ { PR_1_BAD_I_BLOCKS,
+ N_("@i %i, i_@bs is %Ib, @s %N. "),
+ PROMPT_FIX, PR_PREEN_OK },
+
+ /* Illegal blocknumber in inode */
+ { PR_1_ILLEGAL_BLOCK_NUM,
+ N_("@I @b #%B (%b) in @i %i. "),
+ PROMPT_CLEAR, PR_LATCH_BLOCK },
+
+ /* Block number overlaps fs metadata */
+ { PR_1_BLOCK_OVERLAPS_METADATA,
+ N_("@b #%B (%b) overlaps @f metadata in @i %i. "),
+ PROMPT_CLEAR, PR_LATCH_BLOCK },
+
+ /* Inode has illegal blocks (latch question) */
+ { PR_1_INODE_BLOCK_LATCH,
+ N_("@i %i has illegal @b(s). "),
+ PROMPT_CLEAR, 0 },
+
+ /* Too many bad blocks in inode */
+ { PR_1_TOO_MANY_BAD_BLOCKS,
+ N_("Too many illegal @bs in @i %i.\n"),
+ PROMPT_CLEAR_INODE, PR_NO_OK },
+
+ /* Illegal block number in bad block inode */
+ { PR_1_BB_ILLEGAL_BLOCK_NUM,
+ N_("@I @b #%B (%b) in bad @b @i. "),
+ PROMPT_CLEAR, PR_LATCH_BBLOCK },
+
+ /* Bad block inode has illegal blocks (latch question) */
+ { PR_1_INODE_BBLOCK_LATCH,
+ N_("Bad @b @i has illegal @b(s). "),
+ PROMPT_CLEAR, 0 },
+
+ /* Duplicate or bad blocks in use! */
+ { PR_1_DUP_BLOCKS_PREENSTOP,
+ N_("Duplicate or bad @b in use!\n"),
+ PROMPT_NONE, 0 },
+
+ /* Bad block used as bad block indirect block */
+ { PR_1_BBINODE_BAD_METABLOCK,
+ N_("Bad @b %b used as bad @b @i indirect @b. "),
+ PROMPT_CLEAR, PR_LATCH_BBLOCK },
+
+ /* Inconsistency can't be fixed prompt */
+ { PR_1_BBINODE_BAD_METABLOCK_PROMPT,
+ N_("\nThe bad @b @i has probably been corrupted. You probably\n"
+ "should stop now and run ""e2fsck -c"" to scan for bad blocks\n"
+ "in the @f.\n"),
+ PROMPT_CONTINUE, PR_PREEN_NOMSG },
+
+ /* Bad primary block */
+ { PR_1_BAD_PRIMARY_BLOCK,
+ N_("\nIf the @b is really bad, the @f cannot be fixed.\n"),
+ PROMPT_NONE, PR_AFTER_CODE, PR_1_BAD_PRIMARY_BLOCK_PROMPT },
+
+ /* Bad primary block prompt */
+ { PR_1_BAD_PRIMARY_BLOCK_PROMPT,
+ N_("You can remove this @b from the bad @b list and hope\n"
+ "that the @b is really OK. But there are no guarantees.\n\n"),
+ PROMPT_CLEAR, PR_PREEN_NOMSG },
+
+ /* Bad primary superblock */
+ { PR_1_BAD_PRIMARY_SUPERBLOCK,
+ N_("The primary @S (%b) is on the bad @b list.\n"),
+ PROMPT_NONE, PR_AFTER_CODE, PR_1_BAD_PRIMARY_BLOCK },
+
+ /* Bad primary block group descriptors */
+ { PR_1_BAD_PRIMARY_GROUP_DESCRIPTOR,
+ N_("Block %b in the primary @g descriptors "
+ "is on the bad @b list\n"),
+ PROMPT_NONE, PR_AFTER_CODE, PR_1_BAD_PRIMARY_BLOCK },
+
+ /* Bad superblock in group */
+ { PR_1_BAD_SUPERBLOCK,
+ N_("Warning: Group %g's @S (%b) is bad.\n"),
+ PROMPT_NONE, PR_PREEN_OK | PR_PREEN_NOMSG },
+
+ /* Bad block group descriptors in group */
+ { PR_1_BAD_GROUP_DESCRIPTORS,
+ N_("Warning: Group %g's copy of the @g descriptors has a bad "
+ "@b (%b).\n"),
+ PROMPT_NONE, PR_PREEN_OK | PR_PREEN_NOMSG },
+
+ /* Block claimed for no reason */
+ { PR_1_PROGERR_CLAIMED_BLOCK,
+ N_("Programming error? @b #%b claimed for no reason in "
+ "process_bad_@b.\n"),
+ PROMPT_NONE, PR_PREEN_OK },
+
+ /* Error allocating blocks for relocating metadata */
+ { PR_1_RELOC_BLOCK_ALLOCATE,
+ N_("@A %N contiguous @b(s) in @b @g %g for %s: %m\n"),
+ PROMPT_NONE, PR_PREEN_OK },
+
+ /* Error allocating block buffer during relocation process */
+ { PR_1_RELOC_MEMORY_ALLOCATE,
+ N_("@A @b buffer for relocating %s\n"),
+ PROMPT_NONE, PR_PREEN_OK },
+
+ /* Relocating metadata group information from X to Y */
+ { PR_1_RELOC_FROM_TO,
+ N_("Relocating @g %g's %s from %b to %c...\n"),
+ PROMPT_NONE, PR_PREEN_OK },
+
+ /* Relocating metatdata group information to X */
+ { PR_1_RELOC_TO,
+ N_("Relocating @g %g's %s to %c...\n"), /* xgettext:no-c-format */
+ PROMPT_NONE, PR_PREEN_OK },
+
+ /* Block read error during relocation process */
+ { PR_1_RELOC_READ_ERR,
+ N_("Warning: could not read @b %b of %s: %m\n"),
+ PROMPT_NONE, PR_PREEN_OK },
+
+ /* Block write error during relocation process */
+ { PR_1_RELOC_WRITE_ERR,
+ N_("Warning: could not write @b %b for %s: %m\n"),
+ PROMPT_NONE, PR_PREEN_OK },
+
+ /* Error allocating inode bitmap */
+ { PR_1_ALLOCATE_IBITMAP_ERROR,
+ N_("@A @i @B (%N): %m\n"),
+ PROMPT_NONE, PR_FATAL },
+
+ /* Error allocating block bitmap */
+ { PR_1_ALLOCATE_BBITMAP_ERROR,
+ N_("@A @b @B (%N): %m\n"),
+ PROMPT_NONE, PR_FATAL },
+
+ /* Error allocating icount structure */
+ { PR_1_ALLOCATE_ICOUNT,
+ N_("@A icount link information: %m\n"),
+ PROMPT_NONE, PR_FATAL },
+
+ /* Error allocating dbcount */
+ { PR_1_ALLOCATE_DBCOUNT,
+ N_("@A @d @b array: %m\n"),
+ PROMPT_NONE, PR_FATAL },
+
+ /* Error while scanning inodes */
+ { PR_1_ISCAN_ERROR,
+ N_("Error while scanning @is (%i): %m\n"),
+ PROMPT_NONE, PR_FATAL },
+
+ /* Error while iterating over blocks */
+ { PR_1_BLOCK_ITERATE,
+ N_("Error while iterating over @bs in @i %i: %m\n"),
+ PROMPT_NONE, PR_FATAL },
+
+ /* Error while storing inode count information */
+ { PR_1_ICOUNT_STORE,
+ N_("Error storing @i count information (@i=%i, count=%N): %m\n"),
+ PROMPT_NONE, PR_FATAL },
+
+ /* Error while storing directory block information */
+ { PR_1_ADD_DBLOCK,
+ N_("Error storing @d @b information "
+ "(@i=%i, @b=%b, num=%N): %m\n"),
+ PROMPT_NONE, PR_FATAL },
+
+ /* Error while reading inode (for clearing) */
+ { PR_1_READ_INODE,
+ N_("Error reading @i %i: %m\n"),
+ PROMPT_NONE, PR_FATAL },
+
+ /* Suppress messages prompt */
+ { PR_1_SUPPRESS_MESSAGES, "", PROMPT_SUPPRESS, PR_NO_OK },
+
+ /* Imagic flag set on an inode when filesystem doesn't support it */
+ { PR_1_SET_IMAGIC,
+ N_("@i %i has imagic flag set. "),
+ PROMPT_CLEAR, 0 },
+
+ /* Immutable flag set on a device or socket inode */
+ { PR_1_SET_IMMUTABLE,
+ N_("Special (@v/socket/fifo/symlink) file (@i %i) has immutable\n"
+ "or append-only flag set. "),
+ PROMPT_CLEAR, PR_PREEN_OK | PR_PREEN_NO | PR_NO_OK },
+
+ /* Compression flag set on an inode when filesystem doesn't support it */
+ { PR_1_COMPR_SET,
+ N_("@i %i has @cion flag set on @f without @cion support. "),
+ PROMPT_CLEAR, 0 },
+
+ /* Non-zero size for device, fifo or socket inode */
+ { PR_1_SET_NONZSIZE,
+ N_("Special (@v/socket/fifo) @i %i has non-zero size. "),
+ PROMPT_FIX, PR_PREEN_OK },
+
+ /* Filesystem revision is 0, but feature flags are set */
+ { PR_1_FS_REV_LEVEL,
+ N_("@f has feature flag(s) set, but is a revision 0 @f. "),
+ PROMPT_FIX, PR_PREEN_OK | PR_NO_OK },
+
+ /* Journal inode is not in use, but contains data */
+ { PR_1_JOURNAL_INODE_NOT_CLEAR,
+ N_("@j @i is not in use, but contains data. "),
+ PROMPT_CLEAR, PR_PREEN_OK },
+
+ /* Journal has bad mode */
+ { PR_1_JOURNAL_BAD_MODE,
+ N_("@j is not regular file. "),
+ PROMPT_FIX, PR_PREEN_OK },
+
+ /* Deal with inodes that were part of orphan linked list */
+ { PR_1_LOW_DTIME,
+ N_("@i %i was part of the @o @i list. "),
+ PROMPT_FIX, PR_LATCH_LOW_DTIME, 0 },
+
+ /* Deal with inodes that were part of corrupted orphan linked
+ list (latch question) */
+ { PR_1_ORPHAN_LIST_REFUGEES,
+ N_("@is that were part of a corrupted orphan linked list found. "),
+ PROMPT_FIX, 0 },
+
+ /* Error allocating refcount structure */
+ { PR_1_ALLOCATE_REFCOUNT,
+ N_("@A refcount structure (%N): %m\n"),
+ PROMPT_NONE, PR_FATAL },
+
+ /* Error reading extended attribute block */
+ { PR_1_READ_EA_BLOCK,
+ N_("Error reading @a @b %b for @i %i. "),
+ PROMPT_CLEAR, 0 },
+
+ /* Invalid extended attribute block */
+ { PR_1_BAD_EA_BLOCK,
+ N_("@i %i has a bad @a @b %b. "),
+ PROMPT_CLEAR, 0 },
+
+ /* Error reading Extended Attribute block while fixing refcount */
+ { PR_1_EXTATTR_READ_ABORT,
+ N_("Error reading @a @b %b (%m). "),
+ PROMPT_ABORT, 0 },
+
+ /* Extended attribute reference count incorrect */
+ { PR_1_EXTATTR_REFCOUNT,
+ N_("@a @b %b has reference count %B, @s %N. "),
+ PROMPT_FIX, 0 },
+
+ /* Error writing Extended Attribute block while fixing refcount */
+ { PR_1_EXTATTR_WRITE,
+ N_("Error writing @a @b %b (%m). "),
+ PROMPT_ABORT, 0 },
+
+ /* Multiple EA blocks not supported */
+ { PR_1_EA_MULTI_BLOCK,
+ N_("@a @b %b has h_@bs > 1. "),
+ PROMPT_CLEAR, 0},
+
+ /* Error allocating EA region allocation structure */
+ { PR_1_EA_ALLOC_REGION,
+ N_("@A @a @b %b. "),
+ PROMPT_ABORT, 0},
+
+ /* Error EA allocation collision */
+ { PR_1_EA_ALLOC_COLLISION,
+ N_("@a @b %b is corrupt (allocation collision). "),
+ PROMPT_CLEAR, 0},
+
+ /* Bad extended attribute name */
+ { PR_1_EA_BAD_NAME,
+ N_("@a @b %b is corrupt (@n name). "),
+ PROMPT_CLEAR, 0},
+
+ /* Bad extended attribute value */
+ { PR_1_EA_BAD_VALUE,
+ N_("@a @b %b is corrupt (@n value). "),
+ PROMPT_CLEAR, 0},
+
+ /* Inode too big (latch question) */
+ { PR_1_INODE_TOOBIG,
+ N_("@i %i is too big. "), PROMPT_TRUNCATE, 0 },
+
+ /* Directory too big */
+ { PR_1_TOOBIG_DIR,
+ N_("@b #%B (%b) causes @d to be too big. "),
+ PROMPT_CLEAR, PR_LATCH_TOOBIG },
+
+ /* Regular file too big */
+ { PR_1_TOOBIG_REG,
+ N_("@b #%B (%b) causes file to be too big. "),
+ PROMPT_CLEAR, PR_LATCH_TOOBIG },
+
+ /* Symlink too big */
+ { PR_1_TOOBIG_SYMLINK,
+ N_("@b #%B (%b) causes symlink to be too big. "),
+ PROMPT_CLEAR, PR_LATCH_TOOBIG },
+
+ /* INDEX_FL flag set on a non-HTREE filesystem */
+ { PR_1_HTREE_SET,
+ N_("@i %i has INDEX_FL flag set on @f without htree support.\n"),
+ PROMPT_CLEAR_HTREE, PR_PREEN_OK },
+
+ /* INDEX_FL flag set on a non-directory */
+ { PR_1_HTREE_NODIR,
+ N_("@i %i has INDEX_FL flag set but is not a @d.\n"),
+ PROMPT_CLEAR_HTREE, PR_PREEN_OK },
+
+ /* Invalid root node in HTREE directory */
+ { PR_1_HTREE_BADROOT,
+ N_("@h %i has an @n root node.\n"),
+ PROMPT_CLEAR_HTREE, PR_PREEN_OK },
+
+ /* Unsupported hash version in HTREE directory */
+ { PR_1_HTREE_HASHV,
+ N_("@h %i has an unsupported hash version (%N)\n"),
+ PROMPT_CLEAR_HTREE, PR_PREEN_OK },
+
+ /* Incompatible flag in HTREE root node */
+ { PR_1_HTREE_INCOMPAT,
+ N_("@h %i uses an incompatible htree root node flag.\n"),
+ PROMPT_CLEAR_HTREE, PR_PREEN_OK },
+
+ /* HTREE too deep */
+ { PR_1_HTREE_DEPTH,
+ N_("@h %i has a tree depth (%N) which is too big\n"),
+ PROMPT_CLEAR_HTREE, PR_PREEN_OK },
+
+ /* Bad block has indirect block that conflicts with filesystem block */
+ { PR_1_BB_FS_BLOCK,
+ N_("Bad @b @i has an indirect @b (%b) that conflicts with\n"
+ "@f metadata. "),
+ PROMPT_CLEAR, PR_LATCH_BBLOCK },
+
+ /* Resize inode failed */
+ { PR_1_RESIZE_INODE_CREATE,
+ N_("Resize @i (re)creation failed: %m."),
+ PROMPT_ABORT, 0 },
+
+ /* invalid inode->i_extra_isize */
+ { PR_1_EXTRA_ISIZE,
+ N_("@i %i has a extra size (%IS) which is @n\n"),
+ PROMPT_FIX, PR_PREEN_OK },
+
+ /* invalid ea entry->e_name_len */
+ { PR_1_ATTR_NAME_LEN,
+ N_("@a in @i %i has a namelen (%N) which is @n\n"),
+ PROMPT_CLEAR, PR_PREEN_OK },
+
+ /* invalid ea entry->e_value_size */
+ { PR_1_ATTR_VALUE_SIZE,
+ N_("@a in @i %i has a value size (%N) which is @n\n"),
+ PROMPT_CLEAR, PR_PREEN_OK },
+
+ /* invalid ea entry->e_value_offs */
+ { PR_1_ATTR_VALUE_OFFSET,
+ N_("@a in @i %i has a value offset (%N) which is @n\n"),
+ PROMPT_CLEAR, PR_PREEN_OK },
+
+ /* invalid ea entry->e_value_block */
+ { PR_1_ATTR_VALUE_BLOCK,
+ N_("@a in @i %i has a value @b (%N) which is @n (must be 0)\n"),
+ PROMPT_CLEAR, PR_PREEN_OK },
+
+ /* invalid ea entry->e_hash */
+ { PR_1_ATTR_HASH,
+ N_("@a in @i %i has a hash (%N) which is @n (must be 0)\n"),
+ PROMPT_CLEAR, PR_PREEN_OK },
+
+ /* Pass 1b errors */
+
+ /* Pass 1B: Rescan for duplicate/bad blocks */
+ { PR_1B_PASS_HEADER,
+ N_("\nRunning additional passes to resolve @bs claimed by more than one @i...\n"
+ "Pass 1B: Rescanning for @m @bs\n"),
+ PROMPT_NONE, 0 },
+
+ /* Duplicate/bad block(s) header */
+ { PR_1B_DUP_BLOCK_HEADER,
+ N_("@m @b(s) in @i %i:"),
+ PROMPT_NONE, 0 },
+
+ /* Duplicate/bad block(s) in inode */
+ { PR_1B_DUP_BLOCK,
+ " %b",
+ PROMPT_NONE, PR_LATCH_DBLOCK | PR_PREEN_NOHDR },
+
+ /* Duplicate/bad block(s) end */
+ { PR_1B_DUP_BLOCK_END,
+ "\n",
+ PROMPT_NONE, PR_PREEN_NOHDR },
+
+ /* Error while scanning inodes */
+ { PR_1B_ISCAN_ERROR,
+ N_("Error while scanning inodes (%i): %m\n"),
+ PROMPT_NONE, PR_FATAL },
+
+ /* Error allocating inode bitmap */
+ { PR_1B_ALLOCATE_IBITMAP_ERROR,
+ N_("@A @i @B (@i_dup_map): %m\n"),
+ PROMPT_NONE, PR_FATAL },
+
+ /* Error while iterating over blocks */
+ { PR_1B_BLOCK_ITERATE,
+ N_("Error while iterating over @bs in @i %i (%s): %m\n"),
+ PROMPT_NONE, 0 },
+
+ /* Error adjusting EA refcount */
+ { PR_1B_ADJ_EA_REFCOUNT,
+ N_("Error adjusting refcount for @a @b %b (@i %i): %m\n"),
+ PROMPT_NONE, 0 },
+
+
+ /* Pass 1C: Scan directories for inodes with multiply-claimed blocks. */
+ { PR_1C_PASS_HEADER,
+ N_("Pass 1C: Scanning directories for @is with @m @bs.\n"),
+ PROMPT_NONE, 0 },
+
+
+ /* Pass 1D: Reconciling multiply-claimed blocks */
+ { PR_1D_PASS_HEADER,
+ N_("Pass 1D: Reconciling @m @bs\n"),
+ PROMPT_NONE, 0 },
+
+ /* File has duplicate blocks */
+ { PR_1D_DUP_FILE,
+ N_("File %Q (@i #%i, mod time %IM)\n"
+ " has %B @m @b(s), shared with %N file(s):\n"),
+ PROMPT_NONE, 0 },
+
+ /* List of files sharing duplicate blocks */
+ { PR_1D_DUP_FILE_LIST,
+ N_("\t%Q (@i #%i, mod time %IM)\n"),
+ PROMPT_NONE, 0 },
+
+ /* File sharing blocks with filesystem metadata */
+ { PR_1D_SHARE_METADATA,
+ N_("\t<@f metadata>\n"),
+ PROMPT_NONE, 0 },
+
+ /* Report of how many duplicate/bad inodes */
+ { PR_1D_NUM_DUP_INODES,
+ N_("(There are %N @is containing @m @bs.)\n\n"),
+ PROMPT_NONE, 0 },
+
+ /* Duplicated blocks already reassigned or cloned. */
+ { PR_1D_DUP_BLOCKS_DEALT,
+ N_("@m @bs already reassigned or cloned.\n\n"),
+ PROMPT_NONE, 0 },
+
+ /* Clone duplicate/bad blocks? */
+ { PR_1D_CLONE_QUESTION,
+ "", PROMPT_CLONE, PR_NO_OK },
+
+ /* Delete file? */
+ { PR_1D_DELETE_QUESTION,
+ "", PROMPT_DELETE, 0 },
+
+ /* Couldn't clone file (error) */
+ { PR_1D_CLONE_ERROR,
+ N_("Couldn't clone file: %m\n"), PROMPT_NONE, 0 },
+
+ /* Pass 2 errors */
+
+ /* Pass 2: Checking directory structure */
+ { PR_2_PASS_HEADER,
+ N_("Pass 2: Checking @d structure\n"),
+ PROMPT_NONE, 0 },
+
+ /* Bad inode number for '.' */
+ { PR_2_BAD_INODE_DOT,
+ N_("@n @i number for '.' in @d @i %i.\n"),
+ PROMPT_FIX, 0 },
+
+ /* Directory entry has bad inode number */
+ { PR_2_BAD_INO,
+ N_("@E has @n @i #: %Di.\n"),
+ PROMPT_CLEAR, 0 },
+
+ /* Directory entry has deleted or unused inode */
+ { PR_2_UNUSED_INODE,
+ N_("@E has @D/unused @i %Di. "),
+ PROMPT_CLEAR, PR_PREEN_OK },
+
+ /* Directry entry is link to '.' */
+ { PR_2_LINK_DOT,
+ N_("@E @L to '.' "),
+ PROMPT_CLEAR, 0 },
+
+ /* Directory entry points to inode now located in a bad block */
+ { PR_2_BB_INODE,
+ N_("@E points to @i (%Di) located in a bad @b.\n"),
+ PROMPT_CLEAR, 0 },
+
+ /* Directory entry contains a link to a directory */
+ { PR_2_LINK_DIR,
+ N_("@E @L to @d %P (%Di).\n"),
+ PROMPT_CLEAR, 0 },
+
+ /* Directory entry contains a link to the root directry */
+ { PR_2_LINK_ROOT,
+ N_("@E @L to the @r.\n"),
+ PROMPT_CLEAR, 0 },
+
+ /* Directory entry has illegal characters in its name */
+ { PR_2_BAD_NAME,
+ N_("@E has illegal characters in its name.\n"),
+ PROMPT_FIX, 0 },
+
+ /* Missing '.' in directory inode */
+ { PR_2_MISSING_DOT,
+ N_("Missing '.' in @d @i %i.\n"),
+ PROMPT_FIX, 0 },
+
+ /* Missing '..' in directory inode */
+ { PR_2_MISSING_DOT_DOT,
+ N_("Missing '..' in @d @i %i.\n"),
+ PROMPT_FIX, 0 },
+
+ /* First entry in directory inode doesn't contain '.' */
+ { PR_2_1ST_NOT_DOT,
+ N_("First @e '%Dn' (@i=%Di) in @d @i %i (%p) @s '.'\n"),
+ PROMPT_FIX, 0 },
+
+ /* Second entry in directory inode doesn't contain '..' */
+ { PR_2_2ND_NOT_DOT_DOT,
+ N_("Second @e '%Dn' (@i=%Di) in @d @i %i @s '..'\n"),
+ PROMPT_FIX, 0 },
+
+ /* i_faddr should be zero */
+ { PR_2_FADDR_ZERO,
+ N_("i_faddr @F %IF, @s zero.\n"),
+ PROMPT_CLEAR, 0 },
+
+ /* i_file_acl should be zero */
+ { PR_2_FILE_ACL_ZERO,
+ N_("i_file_acl @F %If, @s zero.\n"),
+ PROMPT_CLEAR, 0 },
+
+ /* i_dir_acl should be zero */
+ { PR_2_DIR_ACL_ZERO,
+ N_("i_dir_acl @F %Id, @s zero.\n"),
+ PROMPT_CLEAR, 0 },
+
+ /* i_frag should be zero */
+ { PR_2_FRAG_ZERO,
+ N_("i_frag @F %N, @s zero.\n"),
+ PROMPT_CLEAR, 0 },
+
+ /* i_fsize should be zero */
+ { PR_2_FSIZE_ZERO,
+ N_("i_fsize @F %N, @s zero.\n"),
+ PROMPT_CLEAR, 0 },
+
+ /* inode has bad mode */
+ { PR_2_BAD_MODE,
+ N_("@i %i (%Q) has @n mode (%Im).\n"),
+ PROMPT_CLEAR, 0 },
+
+ /* directory corrupted */
+ { PR_2_DIR_CORRUPTED,
+ N_("@d @i %i, @b %B, offset %N: @d corrupted\n"),
+ PROMPT_SALVAGE, 0 },
+
+ /* filename too long */
+ { PR_2_FILENAME_LONG,
+ N_("@d @i %i, @b %B, offset %N: filename too long\n"),
+ PROMPT_TRUNCATE, 0 },
+
+ /* Directory inode has a missing block (hole) */
+ { PR_2_DIRECTORY_HOLE,
+ N_("@d @i %i has an unallocated @b #%B. "),
+ PROMPT_ALLOCATE, 0 },
+
+ /* '.' is not NULL terminated */
+ { PR_2_DOT_NULL_TERM,
+ N_("'.' @d @e in @d @i %i is not NULL terminated\n"),
+ PROMPT_FIX, 0 },
+
+ /* '..' is not NULL terminated */
+ { PR_2_DOT_DOT_NULL_TERM,
+ N_("'..' @d @e in @d @i %i is not NULL terminated\n"),
+ PROMPT_FIX, 0 },
+
+ /* Illegal character device inode */
+ { PR_2_BAD_CHAR_DEV,
+ N_("@i %i (%Q) is an @I character @v.\n"),
+ PROMPT_CLEAR, 0 },
+
+ /* Illegal block device inode */
+ { PR_2_BAD_BLOCK_DEV,
+ N_("@i %i (%Q) is an @I @b @v.\n"),
+ PROMPT_CLEAR, 0 },
+
+ /* Duplicate '.' entry */
+ { PR_2_DUP_DOT,
+ N_("@E is duplicate '.' @e.\n"),
+ PROMPT_FIX, 0 },
+
+ /* Duplicate '..' entry */
+ { PR_2_DUP_DOT_DOT,
+ N_("@E is duplicate '..' @e.\n"),
+ PROMPT_FIX, 0 },
+
+ /* Internal error: couldn't find dir_info */
+ { PR_2_NO_DIRINFO,
+ N_("Internal error: cannot find dir_info for %i.\n"),
+ PROMPT_NONE, PR_FATAL },
+
+ /* Final rec_len is wrong */
+ { PR_2_FINAL_RECLEN,
+ N_("@E has rec_len of %Dr, @s %N.\n"),
+ PROMPT_FIX, 0 },
+
+ /* Error allocating icount structure */
+ { PR_2_ALLOCATE_ICOUNT,
+ N_("@A icount structure: %m\n"),
+ PROMPT_NONE, PR_FATAL },
+
+ /* Error iterating over directory blocks */
+ { PR_2_DBLIST_ITERATE,
+ N_("Error iterating over @d @bs: %m\n"),
+ PROMPT_NONE, PR_FATAL },
+
+ /* Error reading directory block */
+ { PR_2_READ_DIRBLOCK,
+ N_("Error reading @d @b %b (@i %i): %m\n"),
+ PROMPT_CONTINUE, 0 },
+
+ /* Error writing directory block */
+ { PR_2_WRITE_DIRBLOCK,
+ N_("Error writing @d @b %b (@i %i): %m\n"),
+ PROMPT_CONTINUE, 0 },
+
+ /* Error allocating new directory block */
+ { PR_2_ALLOC_DIRBOCK,
+ N_("@A new @d @b for @i %i (%s): %m\n"),
+ PROMPT_NONE, 0 },
+
+ /* Error deallocating inode */
+ { PR_2_DEALLOC_INODE,
+ N_("Error deallocating @i %i: %m\n"),
+ PROMPT_NONE, PR_FATAL },
+
+ /* Directory entry for '.' is big. Split? */
+ { PR_2_SPLIT_DOT,
+ N_("@d @e for '.' is big. "),
+ PROMPT_SPLIT, PR_NO_OK },
+
+ /* Illegal FIFO inode */
+ { PR_2_BAD_FIFO,
+ N_("@i %i (%Q) is an @I FIFO.\n"),
+ PROMPT_CLEAR, 0 },
+
+ /* Illegal socket inode */
+ { PR_2_BAD_SOCKET,
+ N_("@i %i (%Q) is an @I socket.\n"),
+ PROMPT_CLEAR, 0 },
+
+ /* Directory filetype not set */
+ { PR_2_SET_FILETYPE,
+ N_("Setting filetype for @E to %N.\n"),
+ PROMPT_NONE, PR_PREEN_OK | PR_NO_OK | PR_NO_NOMSG },
+
+ /* Directory filetype incorrect */
+ { PR_2_BAD_FILETYPE,
+ N_("@E has an incorrect filetype (was %Dt, @s %N).\n"),
+ PROMPT_FIX, 0 },
+
+ /* Directory filetype set on filesystem */
+ { PR_2_CLEAR_FILETYPE,
+ N_("@E has filetype set.\n"),
+ PROMPT_CLEAR, PR_PREEN_OK },
+
+ /* Directory filename is null */
+ { PR_2_NULL_NAME,
+ N_("@E has a @z name.\n"),
+ PROMPT_CLEAR, 0 },
+
+ /* Invalid symlink */
+ { PR_2_INVALID_SYMLINK,
+ N_("Symlink %Q (@i #%i) is @n.\n"),
+ PROMPT_CLEAR, 0 },
+
+ /* i_file_acl (extended attribute block) is bad */
+ { PR_2_FILE_ACL_BAD,
+ N_("@a @b @F @n (%If).\n"),
+ PROMPT_CLEAR, 0 },
+
+ /* Filesystem contains large files, but has no such flag in sb */
+ { PR_2_FEATURE_LARGE_FILES,
+ N_("@f contains large files, but lacks LARGE_FILE flag in @S.\n"),
+ PROMPT_FIX, 0 },
+
+ /* Node in HTREE directory not referenced */
+ { PR_2_HTREE_NOTREF,
+ N_("@p @h %d: node (%B) not referenced\n"),
+ PROMPT_NONE, 0 },
+
+ /* Node in HTREE directory referenced twice */
+ { PR_2_HTREE_DUPREF,
+ N_("@p @h %d: node (%B) referenced twice\n"),
+ PROMPT_NONE, 0 },
+
+ /* Node in HTREE directory has bad min hash */
+ { PR_2_HTREE_MIN_HASH,
+ N_("@p @h %d: node (%B) has bad min hash\n"),
+ PROMPT_NONE, 0 },
+
+ /* Node in HTREE directory has bad max hash */
+ { PR_2_HTREE_MAX_HASH,
+ N_("@p @h %d: node (%B) has bad max hash\n"),
+ PROMPT_NONE, 0 },
+
+ /* Clear invalid HTREE directory */
+ { PR_2_HTREE_CLEAR,
+ N_("@n @h %d (%q). "), PROMPT_CLEAR, 0 },
+
+ /* Bad block in htree interior node */
+ { PR_2_HTREE_BADBLK,
+ N_("@p @h %d (%q): bad @b number %b.\n"),
+ PROMPT_CLEAR_HTREE, 0 },
+
+ /* Error adjusting EA refcount */
+ { PR_2_ADJ_EA_REFCOUNT,
+ N_("Error adjusting refcount for @a @b %b (@i %i): %m\n"),
+ PROMPT_NONE, PR_FATAL },
+
+ /* Invalid HTREE root node */
+ { PR_2_HTREE_BAD_ROOT,
+ N_("@p @h %d: root node is @n\n"),
+ PROMPT_CLEAR_HTREE, PR_PREEN_OK },
+
+ /* Invalid HTREE limit */
+ { PR_2_HTREE_BAD_LIMIT,
+ N_("@p @h %d: node (%B) has @n limit (%N)\n"),
+ PROMPT_CLEAR_HTREE, PR_PREEN_OK },
+
+ /* Invalid HTREE count */
+ { PR_2_HTREE_BAD_COUNT,
+ N_("@p @h %d: node (%B) has @n count (%N)\n"),
+ PROMPT_CLEAR_HTREE, PR_PREEN_OK },
+
+ /* HTREE interior node has out-of-order hashes in table */
+ { PR_2_HTREE_HASH_ORDER,
+ N_("@p @h %d: node (%B) has an unordered hash table\n"),
+ PROMPT_CLEAR_HTREE, PR_PREEN_OK },
+
+ /* Node in HTREE directory has invalid depth */
+ { PR_2_HTREE_BAD_DEPTH,
+ N_("@p @h %d: node (%B) has @n depth\n"),
+ PROMPT_NONE, 0 },
+
+ /* Duplicate directory entry found */
+ { PR_2_DUPLICATE_DIRENT,
+ N_("Duplicate @E found. "),
+ PROMPT_CLEAR, 0 },
+
+ /* Non-unique filename found */
+ { PR_2_NON_UNIQUE_FILE, /* xgettext: no-c-format */
+ N_("@E has a non-unique filename.\nRename to %s"),
+ PROMPT_NULL, 0 },
+
+ /* Duplicate directory entry found */
+ { PR_2_REPORT_DUP_DIRENT,
+ N_("Duplicate @e '%Dn' found.\n\tMarking %p (%i) to be rebuilt.\n\n"),
+ PROMPT_NONE, 0 },
+
+ /* Pass 3 errors */
+
+ /* Pass 3: Checking directory connectivity */
+ { PR_3_PASS_HEADER,
+ N_("Pass 3: Checking @d connectivity\n"),
+ PROMPT_NONE, 0 },
+
+ /* Root inode not allocated */
+ { PR_3_NO_ROOT_INODE,
+ N_("@r not allocated. "),
+ PROMPT_ALLOCATE, 0 },
+
+ /* No room in lost+found */
+ { PR_3_EXPAND_LF_DIR,
+ N_("No room in @l @d. "),
+ PROMPT_EXPAND, 0 },
+
+ /* Unconnected directory inode */
+ { PR_3_UNCONNECTED_DIR,
+ N_("Unconnected @d @i %i (%p)\n"),
+ PROMPT_CONNECT, 0 },
+
+ /* /lost+found not found */
+ { PR_3_NO_LF_DIR,
+ N_("/@l not found. "),
+ PROMPT_CREATE, PR_PREEN_OK },
+
+ /* .. entry is incorrect */
+ { PR_3_BAD_DOT_DOT,
+ N_("'..' in %Q (%i) is %P (%j), @s %q (%d).\n"),
+ PROMPT_FIX, 0 },
+
+ /* Bad or non-existent /lost+found. Cannot reconnect */
+ { PR_3_NO_LPF,
+ N_("Bad or non-existent /@l. Cannot reconnect.\n"),
+ PROMPT_NONE, 0 },
+
+ /* Could not expand /lost+found */
+ { PR_3_CANT_EXPAND_LPF,
+ N_("Could not expand /@l: %m\n"),
+ PROMPT_NONE, 0 },
+
+ /* Could not reconnect inode */
+ { PR_3_CANT_RECONNECT,
+ N_("Could not reconnect %i: %m\n"),
+ PROMPT_NONE, 0 },
+
+ /* Error while trying to find /lost+found */
+ { PR_3_ERR_FIND_LPF,
+ N_("Error while trying to find /@l: %m\n"),
+ PROMPT_NONE, 0 },
+
+ /* Error in ext2fs_new_block while creating /lost+found */
+ { PR_3_ERR_LPF_NEW_BLOCK,
+ N_("ext2fs_new_@b: %m while trying to create /@l @d\n"),
+ PROMPT_NONE, 0 },
+
+ /* Error in ext2fs_new_inode while creating /lost+found */
+ { PR_3_ERR_LPF_NEW_INODE,
+ N_("ext2fs_new_@i: %m while trying to create /@l @d\n"),
+ PROMPT_NONE, 0 },
+
+ /* Error in ext2fs_new_dir_block while creating /lost+found */
+ { PR_3_ERR_LPF_NEW_DIR_BLOCK,
+ N_("ext2fs_new_dir_@b: %m while creating new @d @b\n"),
+ PROMPT_NONE, 0 },
+
+ /* Error while writing directory block for /lost+found */
+ { PR_3_ERR_LPF_WRITE_BLOCK,
+ N_("ext2fs_write_dir_@b: %m while writing the @d @b for /@l\n"),
+ PROMPT_NONE, 0 },
+
+ /* Error while adjusting inode count */
+ { PR_3_ADJUST_INODE,
+ N_("Error while adjusting @i count on @i %i\n"),
+ PROMPT_NONE, 0 },
+
+ /* Couldn't fix parent directory -- error */
+ { PR_3_FIX_PARENT_ERR,
+ N_("Couldn't fix parent of @i %i: %m\n\n"),
+ PROMPT_NONE, 0 },
+
+ /* Couldn't fix parent directory -- couldn't find it */
+ { PR_3_FIX_PARENT_NOFIND,
+ N_("Couldn't fix parent of @i %i: Couldn't find parent @d @e\n\n"),
+ PROMPT_NONE, 0 },
+
+ /* Error allocating inode bitmap */
+ { PR_3_ALLOCATE_IBITMAP_ERROR,
+ N_("@A @i @B (%N): %m\n"),
+ PROMPT_NONE, PR_FATAL },
+
+ /* Error creating root directory */
+ { PR_3_CREATE_ROOT_ERROR,
+ N_("Error creating root @d (%s): %m\n"),
+ PROMPT_NONE, PR_FATAL },
+
+ /* Error creating lost and found directory */
+ { PR_3_CREATE_LPF_ERROR,
+ N_("Error creating /@l @d (%s): %m\n"),
+ PROMPT_NONE, PR_FATAL },
+
+ /* Root inode is not directory; aborting */
+ { PR_3_ROOT_NOT_DIR_ABORT,
+ N_("@r is not a @d; aborting.\n"),
+ PROMPT_NONE, PR_FATAL },
+
+ /* Cannot proceed without a root inode. */
+ { PR_3_NO_ROOT_INODE_ABORT,
+ N_("Cannot proceed without a @r.\n"),
+ PROMPT_NONE, PR_FATAL },
+
+ /* Internal error: couldn't find dir_info */
+ { PR_3_NO_DIRINFO,
+ N_("Internal error: cannot find dir_info for %i.\n"),
+ PROMPT_NONE, PR_FATAL },
+
+ /* Lost+found not a directory */
+ { PR_3_LPF_NOTDIR,
+ N_("/@l is not a @d (ino=%i)\n"),
+ PROMPT_UNLINK, 0 },
+
+ /* Pass 3A Directory Optimization */
+
+ /* Pass 3A: Optimizing directories */
+ { PR_3A_PASS_HEADER,
+ N_("Pass 3A: Optimizing directories\n"),
+ PROMPT_NONE, PR_PREEN_NOMSG },
+
+ /* Error iterating over directories */
+ { PR_3A_OPTIMIZE_ITER,
+ N_("Failed to create dirs_to_hash iterator: %m"),
+ PROMPT_NONE, 0 },
+
+ /* Error rehash directory */
+ { PR_3A_OPTIMIZE_DIR_ERR,
+ N_("Failed to optimize directory %q (%d): %m"),
+ PROMPT_NONE, 0 },
+
+ /* Rehashing dir header */
+ { PR_3A_OPTIMIZE_DIR_HEADER,
+ N_("Optimizing directories: "),
+ PROMPT_NONE, PR_MSG_ONLY },
+
+ /* Rehashing directory %d */
+ { PR_3A_OPTIMIZE_DIR,
+ " %d",
+ PROMPT_NONE, PR_LATCH_OPTIMIZE_DIR | PR_PREEN_NOHDR},
+
+ /* Rehashing dir end */
+ { PR_3A_OPTIMIZE_DIR_END,
+ "\n",
+ PROMPT_NONE, PR_PREEN_NOHDR },
+
+ /* Pass 4 errors */
+
+ /* Pass 4: Checking reference counts */
+ { PR_4_PASS_HEADER,
+ N_("Pass 4: Checking reference counts\n"),
+ PROMPT_NONE, 0 },
+
+ /* Unattached zero-length inode */
+ { PR_4_ZERO_LEN_INODE,
+ N_("@u @z @i %i. "),
+ PROMPT_CLEAR, PR_PREEN_OK|PR_NO_OK },
+
+ /* Unattached inode */
+ { PR_4_UNATTACHED_INODE,
+ N_("@u @i %i\n"),
+ PROMPT_CONNECT, 0 },
+
+ /* Inode ref count wrong */
+ { PR_4_BAD_REF_COUNT,
+ N_("@i %i ref count is %Il, @s %N. "),
+ PROMPT_FIX, PR_PREEN_OK },
+
+ { PR_4_INCONSISTENT_COUNT,
+ N_("WARNING: PROGRAMMING BUG IN E2FSCK!\n"
+ "\tOR SOME BONEHEAD (YOU) IS CHECKING A MOUNTED (LIVE) FILESYSTEM.\n"
+ "@i_link_info[%i] is %N, @i.i_links_count is %Il. "
+ "They @s the same!\n"),
+ PROMPT_NONE, 0 },
+
+ /* Pass 5 errors */
+
+ /* Pass 5: Checking group summary information */
+ { PR_5_PASS_HEADER,
+ N_("Pass 5: Checking @g summary information\n"),
+ PROMPT_NONE, 0 },
+
+ /* Padding at end of inode bitmap is not set. */
+ { PR_5_INODE_BMAP_PADDING,
+ N_("Padding at end of @i @B is not set. "),
+ PROMPT_FIX, PR_PREEN_OK },
+
+ /* Padding at end of block bitmap is not set. */
+ { PR_5_BLOCK_BMAP_PADDING,
+ N_("Padding at end of @b @B is not set. "),
+ PROMPT_FIX, PR_PREEN_OK },
+
+ /* Block bitmap differences header */
+ { PR_5_BLOCK_BITMAP_HEADER,
+ N_("@b @B differences: "),
+ PROMPT_NONE, PR_PREEN_OK | PR_PREEN_NOMSG},
+
+ /* Block not used, but marked in bitmap */
+ { PR_5_BLOCK_UNUSED,
+ " -%b",
+ PROMPT_NONE, PR_LATCH_BBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
+
+ /* Block used, but not marked used in bitmap */
+ { PR_5_BLOCK_USED,
+ " +%b",
+ PROMPT_NONE, PR_LATCH_BBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
+
+ /* Block bitmap differences end */
+ { PR_5_BLOCK_BITMAP_END,
+ "\n",
+ PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
+
+ /* Inode bitmap differences header */
+ { PR_5_INODE_BITMAP_HEADER,
+ N_("@i @B differences: "),
+ PROMPT_NONE, PR_PREEN_OK | PR_PREEN_NOMSG },
+
+ /* Inode not used, but marked in bitmap */
+ { PR_5_INODE_UNUSED,
+ " -%i",
+ PROMPT_NONE, PR_LATCH_IBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
+
+ /* Inode used, but not marked used in bitmap */
+ { PR_5_INODE_USED,
+ " +%i",
+ PROMPT_NONE, PR_LATCH_IBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
+
+ /* Inode bitmap differences end */
+ { PR_5_INODE_BITMAP_END,
+ "\n",
+ PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
+
+ /* Free inodes count for group wrong */
+ { PR_5_FREE_INODE_COUNT_GROUP,
+ N_("Free @is count wrong for @g #%g (%i, counted=%j).\n"),
+ PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
+
+ /* Directories count for group wrong */
+ { PR_5_FREE_DIR_COUNT_GROUP,
+ N_("Directories count wrong for @g #%g (%i, counted=%j).\n"),
+ PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
+
+ /* Free inodes count wrong */
+ { PR_5_FREE_INODE_COUNT,
+ N_("Free @is count wrong (%i, counted=%j).\n"),
+ PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
+
+ /* Free blocks count for group wrong */
+ { PR_5_FREE_BLOCK_COUNT_GROUP,
+ N_("Free @bs count wrong for @g #%g (%b, counted=%c).\n"),
+ PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
+
+ /* Free blocks count wrong */
+ { PR_5_FREE_BLOCK_COUNT,
+ N_("Free @bs count wrong (%b, counted=%c).\n"),
+ PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
+
+ /* Programming error: bitmap endpoints don't match */
+ { PR_5_BMAP_ENDPOINTS,
+ N_("PROGRAMMING ERROR: @f (#%N) @B endpoints (%b, %c) don't "
+ "match calculated @B endpoints (%i, %j)\n"),
+ PROMPT_NONE, PR_FATAL },
+
+ /* Internal error: fudging end of bitmap */
+ { PR_5_FUDGE_BITMAP_ERROR,
+ N_("Internal error: fudging end of bitmap (%N)\n"),
+ PROMPT_NONE, PR_FATAL },
+
+ /* Error copying in replacement inode bitmap */
+ { PR_5_COPY_IBITMAP_ERROR,
+ N_("Error copying in replacement @i @B: %m\n"),
+ PROMPT_NONE, PR_FATAL },
+
+ /* Error copying in replacement block bitmap */
+ { PR_5_COPY_BBITMAP_ERROR,
+ N_("Error copying in replacement @b @B: %m\n"),
+ PROMPT_NONE, PR_FATAL },
+
+ /* Block range not used, but marked in bitmap */
+ { PR_5_BLOCK_RANGE_UNUSED,
+ " -(%b--%c)",
+ PROMPT_NONE, PR_LATCH_BBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
+
+ /* Block range used, but not marked used in bitmap */
+ { PR_5_BLOCK_RANGE_USED,
+ " +(%b--%c)",
+ PROMPT_NONE, PR_LATCH_BBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
+
+ /* Inode range not used, but marked in bitmap */
+ { PR_5_INODE_RANGE_UNUSED,
+ " -(%i--%j)",
+ PROMPT_NONE, PR_LATCH_IBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
+
+ /* Inode range used, but not marked used in bitmap */
+ { PR_5_INODE_RANGE_USED,
+ " +(%i--%j)",
+ PROMPT_NONE, PR_LATCH_IBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
+
+ { 0 }
+};
+
+/*
+ * This is the latch flags register. It allows several problems to be
+ * "latched" together. This means that the user has to answer but one
+ * question for the set of problems, and all of the associated
+ * problems will be either fixed or not fixed.
+ */
+static struct latch_descr pr_latch_info[] = {
+ { PR_LATCH_BLOCK, PR_1_INODE_BLOCK_LATCH, 0 },
+ { PR_LATCH_BBLOCK, PR_1_INODE_BBLOCK_LATCH, 0 },
+ { PR_LATCH_IBITMAP, PR_5_INODE_BITMAP_HEADER, PR_5_INODE_BITMAP_END },
+ { PR_LATCH_BBITMAP, PR_5_BLOCK_BITMAP_HEADER, PR_5_BLOCK_BITMAP_END },
+ { PR_LATCH_RELOC, PR_0_RELOCATE_HINT, 0 },
+ { PR_LATCH_DBLOCK, PR_1B_DUP_BLOCK_HEADER, PR_1B_DUP_BLOCK_END },
+ { PR_LATCH_LOW_DTIME, PR_1_ORPHAN_LIST_REFUGEES, 0 },
+ { PR_LATCH_TOOBIG, PR_1_INODE_TOOBIG, 0 },
+ { PR_LATCH_OPTIMIZE_DIR, PR_3A_OPTIMIZE_DIR_HEADER, PR_3A_OPTIMIZE_DIR_END },
+ { -1, 0, 0 },
+};
+
+static const struct e2fsck_problem *find_problem(problem_t code)
+{
+ int i;
+
+ for (i=0; problem_table[i].e2p_code; i++) {
+ if (problem_table[i].e2p_code == code)
+ return &problem_table[i];
+ }
+ return 0;
+}
+
+static struct latch_descr *find_latch(int code)
+{
+ int i;
+
+ for (i=0; pr_latch_info[i].latch_code >= 0; i++) {
+ if (pr_latch_info[i].latch_code == code)
+ return &pr_latch_info[i];
+ }
+ return 0;
+}
+
+int end_problem_latch(e2fsck_t ctx, int mask)
+{
+ struct latch_descr *ldesc;
+ struct problem_context pctx;
+ int answer = -1;
+
+ ldesc = find_latch(mask);
+ if (ldesc->end_message && (ldesc->flags & PRL_LATCHED)) {
+ clear_problem_context(&pctx);
+ answer = fix_problem(ctx, ldesc->end_message, &pctx);
+ }
+ ldesc->flags &= ~(PRL_VARIABLE);
+ return answer;
+}
+
+int set_latch_flags(int mask, int setflags, int clearflags)
+{
+ struct latch_descr *ldesc;
+
+ ldesc = find_latch(mask);
+ if (!ldesc)
+ return -1;
+ ldesc->flags |= setflags;
+ ldesc->flags &= ~clearflags;
+ return 0;
+}
+
+void clear_problem_context(struct problem_context *ctx)
+{
+ memset(ctx, 0, sizeof(struct problem_context));
+ ctx->blkcount = -1;
+ ctx->group = -1;
+}
+
+int fix_problem(e2fsck_t ctx, problem_t code, struct problem_context *pctx)
+{
+ ext2_filsys fs = ctx->fs;
+ const struct e2fsck_problem *ptr;
+ struct latch_descr *ldesc = 0;
+ const char *message;
+ int def_yn, answer, ans;
+ int print_answer = 0;
+ int suppress = 0;
+
+ ptr = find_problem(code);
+ if (!ptr) {
+ printf(_("Unhandled error code (0x%x)!\n"), code);
+ return 0;
+ }
+ def_yn = 1;
+ if ((ptr->flags & PR_NO_DEFAULT) ||
+ ((ptr->flags & PR_PREEN_NO) && (ctx->options & E2F_OPT_PREEN)) ||
+ (ctx->options & E2F_OPT_NO))
+ def_yn= 0;
+
+ /*
+ * Do special latch processing. This is where we ask the
+ * latch question, if it exists
+ */
+ if (ptr->flags & PR_LATCH_MASK) {
+ ldesc = find_latch(ptr->flags & PR_LATCH_MASK);
+ if (ldesc->question && !(ldesc->flags & PRL_LATCHED)) {
+ ans = fix_problem(ctx, ldesc->question, pctx);
+ if (ans == 1)
+ ldesc->flags |= PRL_YES;
+ if (ans == 0)
+ ldesc->flags |= PRL_NO;
+ ldesc->flags |= PRL_LATCHED;
+ }
+ if (ldesc->flags & PRL_SUPPRESS)
+ suppress++;
+ }
+ if ((ptr->flags & PR_PREEN_NOMSG) &&
+ (ctx->options & E2F_OPT_PREEN))
+ suppress++;
+ if ((ptr->flags & PR_NO_NOMSG) &&
+ (ctx->options & E2F_OPT_NO))
+ suppress++;
+ if (!suppress) {
+ message = ptr->e2p_description;
+ if ((ctx->options & E2F_OPT_PREEN) &&
+ !(ptr->flags & PR_PREEN_NOHDR)) {
+ printf("%s: ", ctx->device_name ?
+ ctx->device_name : ctx->filesystem_name);
+ }
+ if (*message)
+ print_e2fsck_message(ctx, _(message), pctx, 1);
+ }
+ if (!(ptr->flags & PR_PREEN_OK) && (ptr->prompt != PROMPT_NONE))
+ preenhalt(ctx);
+
+ if (ptr->flags & PR_FATAL)
+ bb_error_msg_and_die(0);
+
+ if (ptr->prompt == PROMPT_NONE) {
+ if (ptr->flags & PR_NOCOLLATE)
+ answer = -1;
+ else
+ answer = def_yn;
+ } else {
+ if (ctx->options & E2F_OPT_PREEN) {
+ answer = def_yn;
+ if (!(ptr->flags & PR_PREEN_NOMSG))
+ print_answer = 1;
+ } else if ((ptr->flags & PR_LATCH_MASK) &&
+ (ldesc->flags & (PRL_YES | PRL_NO))) {
+ if (!suppress)
+ print_answer = 1;
+ if (ldesc->flags & PRL_YES)
+ answer = 1;
+ else
+ answer = 0;
+ } else
+ answer = ask(ctx, _(prompt[(int) ptr->prompt]), def_yn);
+ if (!answer && !(ptr->flags & PR_NO_OK))
+ ext2fs_unmark_valid(fs);
+
+ if (print_answer)
+ printf("%s.\n", answer ?
+ _(preen_msg[(int) ptr->prompt]) : _("IGNORED"));
+
+ }
+
+ if ((ptr->prompt == PROMPT_ABORT) && answer)
+ bb_error_msg_and_die(0);
+
+ if (ptr->flags & PR_AFTER_CODE)
+ answer = fix_problem(ctx, ptr->second_code, pctx);
+
+ return answer;
+}
+
+/*
+ * linux/fs/recovery.c
+ *
+ * Written by Stephen C. Tweedie <sct@redhat.com>, 1999
+ */
+
+/*
+ * Maintain information about the progress of the recovery job, so that
+ * the different passes can carry information between them.
+ */
+struct recovery_info
+{
+ tid_t start_transaction;
+ tid_t end_transaction;
+
+ int nr_replays;
+ int nr_revokes;
+ int nr_revoke_hits;
+};
+
+enum passtype {PASS_SCAN, PASS_REVOKE, PASS_REPLAY};
+static int do_one_pass(journal_t *journal,
+ struct recovery_info *info, enum passtype pass);
+static int scan_revoke_records(journal_t *, struct buffer_head *,
+ tid_t, struct recovery_info *);
+
+/*
+ * Read a block from the journal
+ */
+
+static int jread(struct buffer_head **bhp, journal_t *journal,
+ unsigned int offset)
+{
+ int err;
+ unsigned long blocknr;
+ struct buffer_head *bh;
+
+ *bhp = NULL;
+
+ err = journal_bmap(journal, offset, &blocknr);
+
+ if (err) {
+ printf("JBD: bad block at offset %u\n", offset);
+ return err;
+ }
+
+ bh = getblk(journal->j_dev, blocknr, journal->j_blocksize);
+ if (!bh)
+ return -ENOMEM;
+
+ if (!buffer_uptodate(bh)) {
+ /* If this is a brand new buffer, start readahead.
+ Otherwise, we assume we are already reading it. */
+ if (!buffer_req(bh))
+ do_readahead(journal, offset);
+ wait_on_buffer(bh);
+ }
+
+ if (!buffer_uptodate(bh)) {
+ printf("JBD: Failed to read block at offset %u\n", offset);
+ brelse(bh);
+ return -EIO;
+ }
+
+ *bhp = bh;
+ return 0;
+}
+
+
+/*
+ * Count the number of in-use tags in a journal descriptor block.
+ */
+
+static int count_tags(struct buffer_head *bh, int size)
+{
+ char * tagp;
+ journal_block_tag_t * tag;
+ int nr = 0;
+
+ tagp = &bh->b_data[sizeof(journal_header_t)];
+
+ while ((tagp - bh->b_data + sizeof(journal_block_tag_t)) <= size) {
+ tag = (journal_block_tag_t *) tagp;
+
+ nr++;
+ tagp += sizeof(journal_block_tag_t);
+ if (!(tag->t_flags & htonl(JFS_FLAG_SAME_UUID)))
+ tagp += 16;
+
+ if (tag->t_flags & htonl(JFS_FLAG_LAST_TAG))
+ break;
+ }
+
+ return nr;
+}
+
+
+/* Make sure we wrap around the log correctly! */
+#define wrap(journal, var) \
+do { \
+ if (var >= (journal)->j_last) \
+ var -= ((journal)->j_last - (journal)->j_first); \
+} while (0)
+
+/**
+ * int journal_recover(journal_t *journal) - recovers a on-disk journal
+ * @journal: the journal to recover
+ *
+ * The primary function for recovering the log contents when mounting a
+ * journaled device.
+ *
+ * Recovery is done in three passes. In the first pass, we look for the
+ * end of the log. In the second, we assemble the list of revoke
+ * blocks. In the third and final pass, we replay any un-revoked blocks
+ * in the log.
+ */
+int journal_recover(journal_t *journal)
+{
+ int err;
+ journal_superblock_t * sb;
+
+ struct recovery_info info;
+
+ memset(&info, 0, sizeof(info));
+ sb = journal->j_superblock;
+
+ /*
+ * The journal superblock's s_start field (the current log head)
+ * is always zero if, and only if, the journal was cleanly
+ * unmounted.
+ */
+
+ if (!sb->s_start) {
+ journal->j_transaction_sequence = ntohl(sb->s_sequence) + 1;
+ return 0;
+ }
+
+ err = do_one_pass(journal, &info, PASS_SCAN);
+ if (!err)
+ err = do_one_pass(journal, &info, PASS_REVOKE);
+ if (!err)
+ err = do_one_pass(journal, &info, PASS_REPLAY);
+
+ /* Restart the log at the next transaction ID, thus invalidating
+ * any existing commit records in the log. */
+ journal->j_transaction_sequence = ++info.end_transaction;
+
+ journal_clear_revoke(journal);
+ sync_blockdev(journal->j_fs_dev);
+ return err;
+}
+
+static int do_one_pass(journal_t *journal,
+ struct recovery_info *info, enum passtype pass)
+{
+ unsigned int first_commit_ID, next_commit_ID;
+ unsigned long next_log_block;
+ int err, success = 0;
+ journal_superblock_t * sb;
+ journal_header_t * tmp;
+ struct buffer_head * bh;
+ unsigned int sequence;
+ int blocktype;
+
+ /* Precompute the maximum metadata descriptors in a descriptor block */
+ int MAX_BLOCKS_PER_DESC;
+ MAX_BLOCKS_PER_DESC = ((journal->j_blocksize-sizeof(journal_header_t))
+ / sizeof(journal_block_tag_t));
+
+ /*
+ * First thing is to establish what we expect to find in the log
+ * (in terms of transaction IDs), and where (in terms of log
+ * block offsets): query the superblock.
+ */
+
+ sb = journal->j_superblock;
+ next_commit_ID = ntohl(sb->s_sequence);
+ next_log_block = ntohl(sb->s_start);
+
+ first_commit_ID = next_commit_ID;
+ if (pass == PASS_SCAN)
+ info->start_transaction = first_commit_ID;
+
+ /*
+ * Now we walk through the log, transaction by transaction,
+ * making sure that each transaction has a commit block in the
+ * expected place. Each complete transaction gets replayed back
+ * into the main filesystem.
+ */
+
+ while (1) {
+ int flags;
+ char * tagp;
+ journal_block_tag_t * tag;
+ struct buffer_head * obh;
+ struct buffer_head * nbh;
+
+ /* If we already know where to stop the log traversal,
+ * check right now that we haven't gone past the end of
+ * the log. */
+
+ if (pass != PASS_SCAN)
+ if (tid_geq(next_commit_ID, info->end_transaction))
+ break;
+
+ /* Skip over each chunk of the transaction looking
+ * either the next descriptor block or the final commit
+ * record. */
+
+ err = jread(&bh, journal, next_log_block);
+ if (err)
+ goto failed;
+
+ next_log_block++;
+ wrap(journal, next_log_block);
+
+ /* What kind of buffer is it?
+ *
+ * If it is a descriptor block, check that it has the
+ * expected sequence number. Otherwise, we're all done
+ * here. */
+
+ tmp = (journal_header_t *)bh->b_data;
+
+ if (tmp->h_magic != htonl(JFS_MAGIC_NUMBER)) {
+ brelse(bh);
+ break;
+ }
+
+ blocktype = ntohl(tmp->h_blocktype);
+ sequence = ntohl(tmp->h_sequence);
+
+ if (sequence != next_commit_ID) {
+ brelse(bh);
+ break;
+ }
+
+ /* OK, we have a valid descriptor block which matches
+ * all of the sequence number checks. What are we going
+ * to do with it? That depends on the pass... */
+
+ switch(blocktype) {
+ case JFS_DESCRIPTOR_BLOCK:
+ /* If it is a valid descriptor block, replay it
+ * in pass REPLAY; otherwise, just skip over the
+ * blocks it describes. */
+ if (pass != PASS_REPLAY) {
+ next_log_block +=
+ count_tags(bh, journal->j_blocksize);
+ wrap(journal, next_log_block);
+ brelse(bh);
+ continue;
+ }
+
+ /* A descriptor block: we can now write all of
+ * the data blocks. Yay, useful work is finally
+ * getting done here! */
+
+ tagp = &bh->b_data[sizeof(journal_header_t)];
+ while ((tagp - bh->b_data +sizeof(journal_block_tag_t))
+ <= journal->j_blocksize) {
+ unsigned long io_block;
+
+ tag = (journal_block_tag_t *) tagp;
+ flags = ntohl(tag->t_flags);
+
+ io_block = next_log_block++;
+ wrap(journal, next_log_block);
+ err = jread(&obh, journal, io_block);
+ if (err) {
+ /* Recover what we can, but
+ * report failure at the end. */
+ success = err;
+ printf("JBD: IO error %d recovering "
+ "block %ld in log\n",
+ err, io_block);
+ } else {
+ unsigned long blocknr;
+
+ blocknr = ntohl(tag->t_blocknr);
+
+ /* If the block has been
+ * revoked, then we're all done
+ * here. */
+ if (journal_test_revoke
+ (journal, blocknr,
+ next_commit_ID)) {
+ brelse(obh);
+ ++info->nr_revoke_hits;
+ goto skip_write;
+ }
+
+ /* Find a buffer for the new
+ * data being restored */
+ nbh = getblk(journal->j_fs_dev,
+ blocknr,
+ journal->j_blocksize);
+ if (nbh == NULL) {
+ printf("JBD: Out of memory "
+ "during recovery.\n");
+ err = -ENOMEM;
+ brelse(bh);
+ brelse(obh);
+ goto failed;
+ }
+
+ lock_buffer(nbh);
+ memcpy(nbh->b_data, obh->b_data,
+ journal->j_blocksize);
+ if (flags & JFS_FLAG_ESCAPE) {
+ *((unsigned int *)bh->b_data) =
+ htonl(JFS_MAGIC_NUMBER);
+ }
+
+ mark_buffer_uptodate(nbh, 1);
+ mark_buffer_dirty(nbh);
+ ++info->nr_replays;
+ /* ll_rw_block(WRITE, 1, &nbh); */
+ unlock_buffer(nbh);
+ brelse(obh);
+ brelse(nbh);
+ }
+
+ skip_write:
+ tagp += sizeof(journal_block_tag_t);
+ if (!(flags & JFS_FLAG_SAME_UUID))
+ tagp += 16;
+
+ if (flags & JFS_FLAG_LAST_TAG)
+ break;
+ }
+
+ brelse(bh);
+ continue;
+
+ case JFS_COMMIT_BLOCK:
+ /* Found an expected commit block: not much to
+ * do other than move on to the next sequence
+ * number. */
+ brelse(bh);
+ next_commit_ID++;
+ continue;
+
+ case JFS_REVOKE_BLOCK:
+ /* If we aren't in the REVOKE pass, then we can
+ * just skip over this block. */
+ if (pass != PASS_REVOKE) {
+ brelse(bh);
+ continue;
+ }
+
+ err = scan_revoke_records(journal, bh,
+ next_commit_ID, info);
+ brelse(bh);
+ if (err)
+ goto failed;
+ continue;
+
+ default:
+ goto done;
+ }
+ }
+
+ done:
+ /*
+ * We broke out of the log scan loop: either we came to the
+ * known end of the log or we found an unexpected block in the
+ * log. If the latter happened, then we know that the "current"
+ * transaction marks the end of the valid log.
+ */
+
+ if (pass == PASS_SCAN)
+ info->end_transaction = next_commit_ID;
+ else {
+ /* It's really bad news if different passes end up at
+ * different places (but possible due to IO errors). */
+ if (info->end_transaction != next_commit_ID) {
+ printf("JBD: recovery pass %d ended at "
+ "transaction %u, expected %u\n",
+ pass, next_commit_ID, info->end_transaction);
+ if (!success)
+ success = -EIO;
+ }
+ }
+
+ return success;
+
+ failed:
+ return err;
+}
+
+
+/* Scan a revoke record, marking all blocks mentioned as revoked. */
+
+static int scan_revoke_records(journal_t *journal, struct buffer_head *bh,
+ tid_t sequence, struct recovery_info *info)
+{
+ journal_revoke_header_t *header;
+ int offset, max;
+
+ header = (journal_revoke_header_t *) bh->b_data;
+ offset = sizeof(journal_revoke_header_t);
+ max = ntohl(header->r_count);
+
+ while (offset < max) {
+ unsigned long blocknr;
+ int err;
+
+ blocknr = ntohl(* ((unsigned int *) (bh->b_data+offset)));
+ offset += 4;
+ err = journal_set_revoke(journal, blocknr, sequence);
+ if (err)
+ return err;
+ ++info->nr_revokes;
+ }
+ return 0;
+}
+
+
+/*
+ * rehash.c --- rebuild hash tree directories
+ *
+ * This algorithm is designed for simplicity of implementation and to
+ * pack the directory as much as possible. It however requires twice
+ * as much memory as the size of the directory. The maximum size
+ * directory supported using a 4k blocksize is roughly a gigabyte, and
+ * so there may very well be problems with machines that don't have
+ * virtual memory, and obscenely large directories.
+ *
+ * An alternate algorithm which is much more disk intensive could be
+ * written, and probably will need to be written in the future. The
+ * design goals of such an algorithm are: (a) use (roughly) constant
+ * amounts of memory, no matter how large the directory, (b) the
+ * directory must be safe at all times, even if e2fsck is interrupted
+ * in the middle, (c) we must use minimal amounts of extra disk
+ * blocks. This pretty much requires an incremental approach, where
+ * we are reading from one part of the directory, and inserting into
+ * the front half. So the algorithm will have to keep track of a
+ * moving block boundary between the new tree and the old tree, and
+ * files will need to be moved from the old directory and inserted
+ * into the new tree. If the new directory requires space which isn't
+ * yet available, blocks from the beginning part of the old directory
+ * may need to be moved to the end of the directory to make room for
+ * the new tree:
+ *
+ * --------------------------------------------------------
+ * | new tree | | old tree |
+ * --------------------------------------------------------
+ * ^ ptr ^ptr
+ * tail new head old
+ *
+ * This is going to be a pain in the tuckus to implement, and will
+ * require a lot more disk accesses. So I'm going to skip it for now;
+ * it's only really going to be an issue for really, really big
+ * filesystems (when we reach the level of tens of millions of files
+ * in a single directory). It will probably be easier to simply
+ * require that e2fsck use VM first.
+ */
+
+struct fill_dir_struct {
+ char *buf;
+ struct ext2_inode *inode;
+ int err;
+ e2fsck_t ctx;
+ struct hash_entry *harray;
+ int max_array, num_array;
+ int dir_size;
+ int compress;
+ ino_t parent;
+};
+
+struct hash_entry {
+ ext2_dirhash_t hash;
+ ext2_dirhash_t minor_hash;
+ struct ext2_dir_entry *dir;
+};
+
+struct out_dir {
+ int num;
+ int max;
+ char *buf;
+ ext2_dirhash_t *hashes;
+};
+
+static int fill_dir_block(ext2_filsys fs,
+ blk_t *block_nr,
+ e2_blkcnt_t blockcnt,
+ blk_t ref_block FSCK_ATTR((unused)),
+ int ref_offset FSCK_ATTR((unused)),
+ void *priv_data)
+{
+ struct fill_dir_struct *fd = (struct fill_dir_struct *) priv_data;
+ struct hash_entry *new_array, *ent;
+ struct ext2_dir_entry *dirent;
+ char *dir;
+ unsigned int offset, dir_offset;
+
+ if (blockcnt < 0)
+ return 0;
+
+ offset = blockcnt * fs->blocksize;
+ if (offset + fs->blocksize > fd->inode->i_size) {
+ fd->err = EXT2_ET_DIR_CORRUPTED;
+ return BLOCK_ABORT;
+ }
+ dir = (fd->buf+offset);
+ if (HOLE_BLKADDR(*block_nr)) {
+ memset(dir, 0, fs->blocksize);
+ dirent = (struct ext2_dir_entry *) dir;
+ dirent->rec_len = fs->blocksize;
+ } else {
+ fd->err = ext2fs_read_dir_block(fs, *block_nr, dir);
+ if (fd->err)
+ return BLOCK_ABORT;
+ }
+ /* While the directory block is "hot", index it. */
+ dir_offset = 0;
+ while (dir_offset < fs->blocksize) {
+ dirent = (struct ext2_dir_entry *) (dir + dir_offset);
+ if (((dir_offset + dirent->rec_len) > fs->blocksize) ||
+ (dirent->rec_len < 8) ||
+ ((dirent->rec_len % 4) != 0) ||
+ (((dirent->name_len & 0xFF)+8) > dirent->rec_len)) {
+ fd->err = EXT2_ET_DIR_CORRUPTED;
+ return BLOCK_ABORT;
+ }
+ dir_offset += dirent->rec_len;
+ if (dirent->inode == 0)
+ continue;
+ if (!fd->compress && ((dirent->name_len&0xFF) == 1) &&
+ (dirent->name[0] == '.'))
+ continue;
+ if (!fd->compress && ((dirent->name_len&0xFF) == 2) &&
+ (dirent->name[0] == '.') && (dirent->name[1] == '.')) {
+ fd->parent = dirent->inode;
+ continue;
+ }
+ if (fd->num_array >= fd->max_array) {
+ new_array = realloc(fd->harray,
+ sizeof(struct hash_entry) * (fd->max_array+500));
+ if (!new_array) {
+ fd->err = ENOMEM;
+ return BLOCK_ABORT;
+ }
+ fd->harray = new_array;
+ fd->max_array += 500;
+ }
+ ent = fd->harray + fd->num_array++;
+ ent->dir = dirent;
+ fd->dir_size += EXT2_DIR_REC_LEN(dirent->name_len & 0xFF);
+ if (fd->compress)
+ ent->hash = ent->minor_hash = 0;
+ else {
+ fd->err = ext2fs_dirhash(fs->super->s_def_hash_version,
+ dirent->name,
+ dirent->name_len & 0xFF,
+ fs->super->s_hash_seed,
+ &ent->hash, &ent->minor_hash);
+ if (fd->err)
+ return BLOCK_ABORT;
+ }
+ }
+
+ return 0;
+}
+
+/* Used for sorting the hash entry */
+static int name_cmp(const void *a, const void *b)
+{
+ const struct hash_entry *he_a = (const struct hash_entry *) a;
+ const struct hash_entry *he_b = (const struct hash_entry *) b;
+ int ret;
+ int min_len;
+
+ min_len = he_a->dir->name_len;
+ if (min_len > he_b->dir->name_len)
+ min_len = he_b->dir->name_len;
+
+ ret = strncmp(he_a->dir->name, he_b->dir->name, min_len);
+ if (ret == 0) {
+ if (he_a->dir->name_len > he_b->dir->name_len)
+ ret = 1;
+ else if (he_a->dir->name_len < he_b->dir->name_len)
+ ret = -1;
+ else
+ ret = he_b->dir->inode - he_a->dir->inode;
+ }
+ return ret;
+}
+
+/* Used for sorting the hash entry */
+static int hash_cmp(const void *a, const void *b)
+{
+ const struct hash_entry *he_a = (const struct hash_entry *) a;
+ const struct hash_entry *he_b = (const struct hash_entry *) b;
+ int ret;
+
+ if (he_a->hash > he_b->hash)
+ ret = 1;
+ else if (he_a->hash < he_b->hash)
+ ret = -1;
+ else {
+ if (he_a->minor_hash > he_b->minor_hash)
+ ret = 1;
+ else if (he_a->minor_hash < he_b->minor_hash)
+ ret = -1;
+ else
+ ret = name_cmp(a, b);
+ }
+ return ret;
+}
+
+static errcode_t alloc_size_dir(ext2_filsys fs, struct out_dir *outdir,
+ int blocks)
+{
+ void *new_mem;
+
+ if (outdir->max) {
+ new_mem = realloc(outdir->buf, blocks * fs->blocksize);
+ if (!new_mem)
+ return ENOMEM;
+ outdir->buf = new_mem;
+ new_mem = realloc(outdir->hashes,
+ blocks * sizeof(ext2_dirhash_t));
+ if (!new_mem)
+ return ENOMEM;
+ outdir->hashes = new_mem;
+ } else {
+ outdir->buf = malloc(blocks * fs->blocksize);
+ outdir->hashes = malloc(blocks * sizeof(ext2_dirhash_t));
+ outdir->num = 0;
+ }
+ outdir->max = blocks;
+ return 0;
+}
+
+static void free_out_dir(struct out_dir *outdir)
+{
+ free(outdir->buf);
+ free(outdir->hashes);
+ outdir->max = 0;
+ outdir->num =0;
+}
+
+static errcode_t get_next_block(ext2_filsys fs, struct out_dir *outdir,
+ char ** ret)
+{
+ errcode_t retval;
+
+ if (outdir->num >= outdir->max) {
+ retval = alloc_size_dir(fs, outdir, outdir->max + 50);
+ if (retval)
+ return retval;
+ }
+ *ret = outdir->buf + (outdir->num++ * fs->blocksize);
+ memset(*ret, 0, fs->blocksize);
+ return 0;
+}
+
+/*
+ * This function is used to make a unique filename. We do this by
+ * appending ~0, and then incrementing the number. However, we cannot
+ * expand the length of the filename beyond the padding available in
+ * the directory entry.
+ */
+static void mutate_name(char *str, __u16 *len)
+{
+ int i;
+ __u16 l = *len & 0xFF, h = *len & 0xff00;
+
+ /*
+ * First check to see if it looks the name has been mutated
+ * already
+ */
+ for (i = l-1; i > 0; i--) {
+ if (!isdigit(str[i]))
+ break;
+ }
+ if ((i == l-1) || (str[i] != '~')) {
+ if (((l-1) & 3) < 2)
+ l += 2;
+ else
+ l = (l+3) & ~3;
+ str[l-2] = '~';
+ str[l-1] = '0';
+ *len = l | h;
+ return;
+ }
+ for (i = l-1; i >= 0; i--) {
+ if (isdigit(str[i])) {
+ if (str[i] == '9')
+ str[i] = '0';
+ else {
+ str[i]++;
+ return;
+ }
+ continue;
+ }
+ if (i == 1) {
+ if (str[0] == 'z')
+ str[0] = 'A';
+ else if (str[0] == 'Z') {
+ str[0] = '~';
+ str[1] = '0';
+ } else
+ str[0]++;
+ } else if (i > 0) {
+ str[i] = '1';
+ str[i-1] = '~';
+ } else {
+ if (str[0] == '~')
+ str[0] = 'a';
+ else
+ str[0]++;
+ }
+ break;
+ }
+}
+
+static int duplicate_search_and_fix(e2fsck_t ctx, ext2_filsys fs,
+ ext2_ino_t ino,
+ struct fill_dir_struct *fd)
+{
+ struct problem_context pctx;
+ struct hash_entry *ent, *prev;
+ int i, j;
+ int fixed = 0;
+ char new_name[256];
+ __u16 new_len;
+
+ clear_problem_context(&pctx);
+ pctx.ino = ino;
+
+ for (i=1; i < fd->num_array; i++) {
+ ent = fd->harray + i;
+ prev = ent - 1;
+ if (!ent->dir->inode ||
+ ((ent->dir->name_len & 0xFF) !=
+ (prev->dir->name_len & 0xFF)) ||
+ (strncmp(ent->dir->name, prev->dir->name,
+ ent->dir->name_len & 0xFF)))
+ continue;
+ pctx.dirent = ent->dir;
+ if ((ent->dir->inode == prev->dir->inode) &&
+ fix_problem(ctx, PR_2_DUPLICATE_DIRENT, &pctx)) {
+ e2fsck_adjust_inode_count(ctx, ent->dir->inode, -1);
+ ent->dir->inode = 0;
+ fixed++;
+ continue;
+ }
+ memcpy(new_name, ent->dir->name, ent->dir->name_len & 0xFF);
+ new_len = ent->dir->name_len;
+ mutate_name(new_name, &new_len);
+ for (j=0; j < fd->num_array; j++) {
+ if ((i==j) ||
+ ((ent->dir->name_len & 0xFF) !=
+ (fd->harray[j].dir->name_len & 0xFF)) ||
+ (strncmp(new_name, fd->harray[j].dir->name,
+ new_len & 0xFF)))
+ continue;
+ mutate_name(new_name, &new_len);
+
+ j = -1;
+ }
+ new_name[new_len & 0xFF] = 0;
+ pctx.str = new_name;
+ if (fix_problem(ctx, PR_2_NON_UNIQUE_FILE, &pctx)) {
+ memcpy(ent->dir->name, new_name, new_len & 0xFF);
+ ent->dir->name_len = new_len;
+ ext2fs_dirhash(fs->super->s_def_hash_version,
+ ent->dir->name,
+ ent->dir->name_len & 0xFF,
+ fs->super->s_hash_seed,
+ &ent->hash, &ent->minor_hash);
+ fixed++;
+ }
+ }
+ return fixed;
+}
+
+
+static errcode_t copy_dir_entries(ext2_filsys fs,
+ struct fill_dir_struct *fd,
+ struct out_dir *outdir)
+{
+ errcode_t retval;
+ char *block_start;
+ struct hash_entry *ent;
+ struct ext2_dir_entry *dirent;
+ int i, rec_len, left;
+ ext2_dirhash_t prev_hash;
+ int offset;
+
+ outdir->max = 0;
+ retval = alloc_size_dir(fs, outdir,
+ (fd->dir_size / fs->blocksize) + 2);
+ if (retval)
+ return retval;
+ outdir->num = fd->compress ? 0 : 1;
+ offset = 0;
+ outdir->hashes[0] = 0;
+ prev_hash = 1;
+ if ((retval = get_next_block(fs, outdir, &block_start)))
+ return retval;
+ dirent = (struct ext2_dir_entry *) block_start;
+ left = fs->blocksize;
+ for (i=0; i < fd->num_array; i++) {
+ ent = fd->harray + i;
+ if (ent->dir->inode == 0)
+ continue;
+ rec_len = EXT2_DIR_REC_LEN(ent->dir->name_len & 0xFF);
+ if (rec_len > left) {
+ if (left)
+ dirent->rec_len += left;
+ if ((retval = get_next_block(fs, outdir,
+ &block_start)))
+ return retval;
+ offset = 0;
+ }
+ left = fs->blocksize - offset;
+ dirent = (struct ext2_dir_entry *) (block_start + offset);
+ if (offset == 0) {
+ if (ent->hash == prev_hash)
+ outdir->hashes[outdir->num-1] = ent->hash | 1;
+ else
+ outdir->hashes[outdir->num-1] = ent->hash;
+ }
+ dirent->inode = ent->dir->inode;
+ dirent->name_len = ent->dir->name_len;
+ dirent->rec_len = rec_len;
+ memcpy(dirent->name, ent->dir->name, dirent->name_len & 0xFF);
+ offset += rec_len;
+ left -= rec_len;
+ if (left < 12) {
+ dirent->rec_len += left;
+ offset += left;
+ left = 0;
+ }
+ prev_hash = ent->hash;
+ }
+ if (left)
+ dirent->rec_len += left;
+
+ return 0;
+}
+
+
+static struct ext2_dx_root_info *set_root_node(ext2_filsys fs, char *buf,
+ ext2_ino_t ino, ext2_ino_t parent)
+{
+ struct ext2_dir_entry *dir;
+ struct ext2_dx_root_info *root;
+ struct ext2_dx_countlimit *limits;
+ int filetype = 0;
+
+ if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE)
+ filetype = EXT2_FT_DIR << 8;
+
+ memset(buf, 0, fs->blocksize);
+ dir = (struct ext2_dir_entry *) buf;
+ dir->inode = ino;
+ dir->name[0] = '.';
+ dir->name_len = 1 | filetype;
+ dir->rec_len = 12;
+ dir = (struct ext2_dir_entry *) (buf + 12);
+ dir->inode = parent;
+ dir->name[0] = '.';
+ dir->name[1] = '.';
+ dir->name_len = 2 | filetype;
+ dir->rec_len = fs->blocksize - 12;
+
+ root = (struct ext2_dx_root_info *) (buf+24);
+ root->reserved_zero = 0;
+ root->hash_version = fs->super->s_def_hash_version;
+ root->info_length = 8;
+ root->indirect_levels = 0;
+ root->unused_flags = 0;
+
+ limits = (struct ext2_dx_countlimit *) (buf+32);
+ limits->limit = (fs->blocksize - 32) / sizeof(struct ext2_dx_entry);
+ limits->count = 0;
+
+ return root;
+}
+
+
+static struct ext2_dx_entry *set_int_node(ext2_filsys fs, char *buf)
+{
+ struct ext2_dir_entry *dir;
+ struct ext2_dx_countlimit *limits;
+
+ memset(buf, 0, fs->blocksize);
+ dir = (struct ext2_dir_entry *) buf;
+ dir->inode = 0;
+ dir->rec_len = fs->blocksize;
+
+ limits = (struct ext2_dx_countlimit *) (buf+8);
+ limits->limit = (fs->blocksize - 8) / sizeof(struct ext2_dx_entry);
+ limits->count = 0;
+
+ return (struct ext2_dx_entry *) limits;
+}
+
+/*
+ * This function takes the leaf nodes which have been written in
+ * outdir, and populates the root node and any necessary interior nodes.
+ */
+static errcode_t calculate_tree(ext2_filsys fs,
+ struct out_dir *outdir,
+ ext2_ino_t ino,
+ ext2_ino_t parent)
+{
+ struct ext2_dx_root_info *root_info;
+ struct ext2_dx_entry *root, *dx_ent = 0;
+ struct ext2_dx_countlimit *root_limit, *limit;
+ errcode_t retval;
+ char * block_start;
+ int i, c1, c2, nblks;
+ int limit_offset, root_offset;
+
+ root_info = set_root_node(fs, outdir->buf, ino, parent);
+ root_offset = limit_offset = ((char *) root_info - outdir->buf) +
+ root_info->info_length;
+ root_limit = (struct ext2_dx_countlimit *) (outdir->buf + limit_offset);
+ c1 = root_limit->limit;
+ nblks = outdir->num;
+
+ /* Write out the pointer blocks */
+ if (nblks-1 <= c1) {
+ /* Just write out the root block, and we're done */
+ root = (struct ext2_dx_entry *) (outdir->buf + root_offset);
+ for (i=1; i < nblks; i++) {
+ root->block = ext2fs_cpu_to_le32(i);
+ if (i != 1)
+ root->hash =
+ ext2fs_cpu_to_le32(outdir->hashes[i]);
+ root++;
+ c1--;
+ }
+ } else {
+ c2 = 0;
+ limit = 0;
+ root_info->indirect_levels = 1;
+ for (i=1; i < nblks; i++) {
+ if (c1 == 0)
+ return ENOSPC;
+ if (c2 == 0) {
+ if (limit)
+ limit->limit = limit->count =
+ ext2fs_cpu_to_le16(limit->limit);
+ root = (struct ext2_dx_entry *)
+ (outdir->buf + root_offset);
+ root->block = ext2fs_cpu_to_le32(outdir->num);
+ if (i != 1)
+ root->hash =
+ ext2fs_cpu_to_le32(outdir->hashes[i]);
+ if ((retval = get_next_block(fs, outdir,
+ &block_start)))
+ return retval;
+ dx_ent = set_int_node(fs, block_start);
+ limit = (struct ext2_dx_countlimit *) dx_ent;
+ c2 = limit->limit;
+ root_offset += sizeof(struct ext2_dx_entry);
+ c1--;
+ }
+ dx_ent->block = ext2fs_cpu_to_le32(i);
+ if (c2 != limit->limit)
+ dx_ent->hash =
+ ext2fs_cpu_to_le32(outdir->hashes[i]);
+ dx_ent++;
+ c2--;
+ }
+ limit->count = ext2fs_cpu_to_le16(limit->limit - c2);
+ limit->limit = ext2fs_cpu_to_le16(limit->limit);
+ }
+ root_limit = (struct ext2_dx_countlimit *) (outdir->buf + limit_offset);
+ root_limit->count = ext2fs_cpu_to_le16(root_limit->limit - c1);
+ root_limit->limit = ext2fs_cpu_to_le16(root_limit->limit);
+
+ return 0;
+}
+
+struct write_dir_struct {
+ struct out_dir *outdir;
+ errcode_t err;
+ e2fsck_t ctx;
+ int cleared;
+};
+
+/*
+ * Helper function which writes out a directory block.
+ */
+static int write_dir_block(ext2_filsys fs,
+ blk_t *block_nr,
+ e2_blkcnt_t blockcnt,
+ blk_t ref_block FSCK_ATTR((unused)),
+ int ref_offset FSCK_ATTR((unused)),
+ void *priv_data)
+{
+ struct write_dir_struct *wd = (struct write_dir_struct *) priv_data;
+ blk_t blk;
+ char *dir;
+
+ if (*block_nr == 0)
+ return 0;
+ if (blockcnt >= wd->outdir->num) {
+ e2fsck_read_bitmaps(wd->ctx);
+ blk = *block_nr;
+ ext2fs_unmark_block_bitmap(wd->ctx->block_found_map, blk);
+ ext2fs_block_alloc_stats(fs, blk, -1);
+ *block_nr = 0;
+ wd->cleared++;
+ return BLOCK_CHANGED;
+ }
+ if (blockcnt < 0)
+ return 0;
+
+ dir = wd->outdir->buf + (blockcnt * fs->blocksize);
+ wd->err = ext2fs_write_dir_block(fs, *block_nr, dir);
+ if (wd->err)
+ return BLOCK_ABORT;
+ return 0;
+}
+
+static errcode_t write_directory(e2fsck_t ctx, ext2_filsys fs,
+ struct out_dir *outdir,
+ ext2_ino_t ino, int compress)
+{
+ struct write_dir_struct wd;
+ errcode_t retval;
+ struct ext2_inode inode;
+
+ retval = e2fsck_expand_directory(ctx, ino, -1, outdir->num);
+ if (retval)
+ return retval;
+
+ wd.outdir = outdir;
+ wd.err = 0;
+ wd.ctx = ctx;
+ wd.cleared = 0;
+
+ retval = ext2fs_block_iterate2(fs, ino, 0, 0,
+ write_dir_block, &wd);
+ if (retval)
+ return retval;
+ if (wd.err)
+ return wd.err;
+
+ e2fsck_read_inode(ctx, ino, &inode, "rehash_dir");
+ if (compress)
+ inode.i_flags &= ~EXT2_INDEX_FL;
+ else
+ inode.i_flags |= EXT2_INDEX_FL;
+ inode.i_size = outdir->num * fs->blocksize;
+ inode.i_blocks -= (fs->blocksize / 512) * wd.cleared;
+ e2fsck_write_inode(ctx, ino, &inode, "rehash_dir");
+
+ return 0;
+}
+
+static errcode_t e2fsck_rehash_dir(e2fsck_t ctx, ext2_ino_t ino)
+{
+ ext2_filsys fs = ctx->fs;
+ errcode_t retval;
+ struct ext2_inode inode;
+ char *dir_buf = 0;
+ struct fill_dir_struct fd;
+ struct out_dir outdir;
+
+ outdir.max = outdir.num = 0;
+ outdir.buf = 0;
+ outdir.hashes = 0;
+ e2fsck_read_inode(ctx, ino, &inode, "rehash_dir");
+
+ retval = ENOMEM;
+ fd.harray = 0;
+ dir_buf = malloc(inode.i_size);
+ if (!dir_buf)
+ goto errout;
+
+ fd.max_array = inode.i_size / 32;
+ fd.num_array = 0;
+ fd.harray = malloc(fd.max_array * sizeof(struct hash_entry));
+ if (!fd.harray)
+ goto errout;
+
+ fd.ctx = ctx;
+ fd.buf = dir_buf;
+ fd.inode = &inode;
+ fd.err = 0;
+ fd.dir_size = 0;
+ fd.compress = 0;
+ if (!(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) ||
+ (inode.i_size / fs->blocksize) < 2)
+ fd.compress = 1;
+ fd.parent = 0;
+
+ /* Read in the entire directory into memory */
+ retval = ext2fs_block_iterate2(fs, ino, 0, 0,
+ fill_dir_block, &fd);
+ if (fd.err) {
+ retval = fd.err;
+ goto errout;
+ }
+
+ /* Sort the list */
+resort:
+ if (fd.compress)
+ qsort(fd.harray+2, fd.num_array-2,
+ sizeof(struct hash_entry), name_cmp);
+ else
+ qsort(fd.harray, fd.num_array,
+ sizeof(struct hash_entry), hash_cmp);
+
+ /*
+ * Look for duplicates
+ */
+ if (duplicate_search_and_fix(ctx, fs, ino, &fd))
+ goto resort;
+
+ if (ctx->options & E2F_OPT_NO) {
+ retval = 0;
+ goto errout;
+ }
+
+ /*
+ * Copy the directory entries. In a htree directory these
+ * will become the leaf nodes.
+ */
+ retval = copy_dir_entries(fs, &fd, &outdir);
+ if (retval)
+ goto errout;
+
+ free(dir_buf); dir_buf = 0;
+
+ if (!fd.compress) {
+ /* Calculate the interior nodes */
+ retval = calculate_tree(fs, &outdir, ino, fd.parent);
+ if (retval)
+ goto errout;
+ }
+
+ retval = write_directory(ctx, fs, &outdir, ino, fd.compress);
+
+errout:
+ free(dir_buf);
+ free(fd.harray);
+
+ free_out_dir(&outdir);
+ return retval;
+}
+
+void e2fsck_rehash_directories(e2fsck_t ctx)
+{
+ struct problem_context pctx;
+ struct dir_info *dir;
+ ext2_u32_iterate iter;
+ ext2_ino_t ino;
+ errcode_t retval;
+ int i, cur, max, all_dirs, dir_index, first = 1;
+
+ all_dirs = ctx->options & E2F_OPT_COMPRESS_DIRS;
+
+ if (!ctx->dirs_to_hash && !all_dirs)
+ return;
+
+ e2fsck_get_lost_and_found(ctx, 0);
+
+ clear_problem_context(&pctx);
+
+ dir_index = ctx->fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX;
+ cur = 0;
+ if (all_dirs) {
+ i = 0;
+ max = e2fsck_get_num_dirinfo(ctx);
+ } else {
+ retval = ext2fs_u32_list_iterate_begin(ctx->dirs_to_hash,
+ &iter);
+ if (retval) {
+ pctx.errcode = retval;
+ fix_problem(ctx, PR_3A_OPTIMIZE_ITER, &pctx);
+ return;
+ }
+ max = ext2fs_u32_list_count(ctx->dirs_to_hash);
+ }
+ while (1) {
+ if (all_dirs) {
+ if ((dir = e2fsck_dir_info_iter(ctx, &i)) == 0)
+ break;
+ ino = dir->ino;
+ } else {
+ if (!ext2fs_u32_list_iterate(iter, &ino))
+ break;
+ }
+ if (ino == ctx->lost_and_found)
+ continue;
+ pctx.dir = ino;
+ if (first) {
+ fix_problem(ctx, PR_3A_PASS_HEADER, &pctx);
+ first = 0;
+ }
+ pctx.errcode = e2fsck_rehash_dir(ctx, ino);
+ if (pctx.errcode) {
+ end_problem_latch(ctx, PR_LATCH_OPTIMIZE_DIR);
+ fix_problem(ctx, PR_3A_OPTIMIZE_DIR_ERR, &pctx);
+ }
+ if (ctx->progress && !ctx->progress_fd)
+ e2fsck_simple_progress(ctx, "Rebuilding directory",
+ 100.0 * (float) (++cur) / (float) max, ino);
+ }
+ end_problem_latch(ctx, PR_LATCH_OPTIMIZE_DIR);
+ if (!all_dirs)
+ ext2fs_u32_list_iterate_end(iter);
+
+ ext2fs_u32_list_free(ctx->dirs_to_hash);
+ ctx->dirs_to_hash = 0;
+}
+
+/*
+ * linux/fs/revoke.c
+ *
+ * Journal revoke routines for the generic filesystem journaling code;
+ * part of the ext2fs journaling system.
+ *
+ * Revoke is the mechanism used to prevent old log records for deleted
+ * metadata from being replayed on top of newer data using the same
+ * blocks. The revoke mechanism is used in two separate places:
+ *
+ * + Commit: during commit we write the entire list of the current
+ * transaction's revoked blocks to the journal
+ *
+ * + Recovery: during recovery we record the transaction ID of all
+ * revoked blocks. If there are multiple revoke records in the log
+ * for a single block, only the last one counts, and if there is a log
+ * entry for a block beyond the last revoke, then that log entry still
+ * gets replayed.
+ *
+ * We can get interactions between revokes and new log data within a
+ * single transaction:
+ *
+ * Block is revoked and then journaled:
+ * The desired end result is the journaling of the new block, so we
+ * cancel the revoke before the transaction commits.
+ *
+ * Block is journaled and then revoked:
+ * The revoke must take precedence over the write of the block, so we
+ * need either to cancel the journal entry or to write the revoke
+ * later in the log than the log block. In this case, we choose the
+ * latter: journaling a block cancels any revoke record for that block
+ * in the current transaction, so any revoke for that block in the
+ * transaction must have happened after the block was journaled and so
+ * the revoke must take precedence.
+ *
+ * Block is revoked and then written as data:
+ * The data write is allowed to succeed, but the revoke is _not_
+ * cancelled. We still need to prevent old log records from
+ * overwriting the new data. We don't even need to clear the revoke
+ * bit here.
+ *
+ * Revoke information on buffers is a tri-state value:
+ *
+ * RevokeValid clear: no cached revoke status, need to look it up
+ * RevokeValid set, Revoked clear:
+ * buffer has not been revoked, and cancel_revoke
+ * need do nothing.
+ * RevokeValid set, Revoked set:
+ * buffer has been revoked.
+ */
+
+static kmem_cache_t *revoke_record_cache;
+static kmem_cache_t *revoke_table_cache;
+
+/* Each revoke record represents one single revoked block. During
+ journal replay, this involves recording the transaction ID of the
+ last transaction to revoke this block. */
+
+struct jbd_revoke_record_s
+{
+ struct list_head hash;
+ tid_t sequence; /* Used for recovery only */
+ unsigned long blocknr;
+};
+
+
+/* The revoke table is just a simple hash table of revoke records. */
+struct jbd_revoke_table_s
+{
+ /* It is conceivable that we might want a larger hash table
+ * for recovery. Must be a power of two. */
+ int hash_size;
+ int hash_shift;
+ struct list_head *hash_table;
+};
+
+
+/* Utility functions to maintain the revoke table */
+
+/* Borrowed from buffer.c: this is a tried and tested block hash function */
+static int hash(journal_t *journal, unsigned long block)
+{
+ struct jbd_revoke_table_s *table = journal->j_revoke;
+ int hash_shift = table->hash_shift;
+
+ return ((block << (hash_shift - 6)) ^
+ (block >> 13) ^
+ (block << (hash_shift - 12))) & (table->hash_size - 1);
+}
+
+static int insert_revoke_hash(journal_t *journal, unsigned long blocknr,
+ tid_t seq)
+{
+ struct list_head *hash_list;
+ struct jbd_revoke_record_s *record;
+
+ record = kmem_cache_alloc(revoke_record_cache, GFP_NOFS);
+ if (!record)
+ goto oom;
+
+ record->sequence = seq;
+ record->blocknr = blocknr;
+ hash_list = &journal->j_revoke->hash_table[hash(journal, blocknr)];
+ list_add(&record->hash, hash_list);
+ return 0;
+
+oom:
+ return -ENOMEM;
+}
+
+/* Find a revoke record in the journal's hash table. */
+
+static struct jbd_revoke_record_s *find_revoke_record(journal_t *journal,
+ unsigned long blocknr)
+{
+ struct list_head *hash_list;
+ struct jbd_revoke_record_s *record;
+
+ hash_list = &journal->j_revoke->hash_table[hash(journal, blocknr)];
+
+ record = (struct jbd_revoke_record_s *) hash_list->next;
+ while (&(record->hash) != hash_list) {
+ if (record->blocknr == blocknr)
+ return record;
+ record = (struct jbd_revoke_record_s *) record->hash.next;
+ }
+ return NULL;
+}
+
+int journal_init_revoke_caches(void)
+{
+ revoke_record_cache = do_cache_create(sizeof(struct jbd_revoke_record_s));
+ if (revoke_record_cache == 0)
+ return -ENOMEM;
+
+ revoke_table_cache = do_cache_create(sizeof(struct jbd_revoke_table_s));
+ if (revoke_table_cache == 0) {
+ do_cache_destroy(revoke_record_cache);
+ revoke_record_cache = NULL;
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+void journal_destroy_revoke_caches(void)
+{
+ do_cache_destroy(revoke_record_cache);
+ revoke_record_cache = 0;
+ do_cache_destroy(revoke_table_cache);
+ revoke_table_cache = 0;
+}
+
+/* Initialise the revoke table for a given journal to a given size. */
+
+int journal_init_revoke(journal_t *journal, int hash_size)
+{
+ int shift, tmp;
+
+ journal->j_revoke = kmem_cache_alloc(revoke_table_cache, GFP_KERNEL);
+ if (!journal->j_revoke)
+ return -ENOMEM;
+
+ /* Check that the hash_size is a power of two */
+ journal->j_revoke->hash_size = hash_size;
+
+ shift = 0;
+ tmp = hash_size;
+ while((tmp >>= 1UL) != 0UL)
+ shift++;
+ journal->j_revoke->hash_shift = shift;
+
+ journal->j_revoke->hash_table = malloc(hash_size * sizeof(struct list_head));
+ if (!journal->j_revoke->hash_table) {
+ free(journal->j_revoke);
+ journal->j_revoke = NULL;
+ return -ENOMEM;
+ }
+
+ for (tmp = 0; tmp < hash_size; tmp++)
+ INIT_LIST_HEAD(&journal->j_revoke->hash_table[tmp]);
+
+ return 0;
+}
+
+/* Destoy a journal's revoke table. The table must already be empty! */
+
+void journal_destroy_revoke(journal_t *journal)
+{
+ struct jbd_revoke_table_s *table;
+ struct list_head *hash_list;
+ int i;
+
+ table = journal->j_revoke;
+ if (!table)
+ return;
+
+ for (i=0; i<table->hash_size; i++) {
+ hash_list = &table->hash_table[i];
+ }
+
+ free(table->hash_table);
+ free(table);
+ journal->j_revoke = NULL;
+}
+
+/*
+ * Revoke support for recovery.
+ *
+ * Recovery needs to be able to:
+ *
+ * record all revoke records, including the tid of the latest instance
+ * of each revoke in the journal
+ *
+ * check whether a given block in a given transaction should be replayed
+ * (ie. has not been revoked by a revoke record in that or a subsequent
+ * transaction)
+ *
+ * empty the revoke table after recovery.
+ */
+
+/*
+ * First, setting revoke records. We create a new revoke record for
+ * every block ever revoked in the log as we scan it for recovery, and
+ * we update the existing records if we find multiple revokes for a
+ * single block.
+ */
+
+int journal_set_revoke(journal_t *journal, unsigned long blocknr,
+ tid_t sequence)
+{
+ struct jbd_revoke_record_s *record;
+
+ record = find_revoke_record(journal, blocknr);
+ if (record) {
+ /* If we have multiple occurences, only record the
+ * latest sequence number in the hashed record */
+ if (tid_gt(sequence, record->sequence))
+ record->sequence = sequence;
+ return 0;
+ }
+ return insert_revoke_hash(journal, blocknr, sequence);
+}
+
+/*
+ * Test revoke records. For a given block referenced in the log, has
+ * that block been revoked? A revoke record with a given transaction
+ * sequence number revokes all blocks in that transaction and earlier
+ * ones, but later transactions still need replayed.
+ */
+
+int journal_test_revoke(journal_t *journal, unsigned long blocknr,
+ tid_t sequence)
+{
+ struct jbd_revoke_record_s *record;
+
+ record = find_revoke_record(journal, blocknr);
+ if (!record)
+ return 0;
+ if (tid_gt(sequence, record->sequence))
+ return 0;
+ return 1;
+}
+
+/*
+ * Finally, once recovery is over, we need to clear the revoke table so
+ * that it can be reused by the running filesystem.
+ */
+
+void journal_clear_revoke(journal_t *journal)
+{
+ int i;
+ struct list_head *hash_list;
+ struct jbd_revoke_record_s *record;
+ struct jbd_revoke_table_s *revoke_var;
+
+ revoke_var = journal->j_revoke;
+
+ for (i = 0; i < revoke_var->hash_size; i++) {
+ hash_list = &revoke_var->hash_table[i];
+ while (!list_empty(hash_list)) {
+ record = (struct jbd_revoke_record_s*) hash_list->next;
+ list_del(&record->hash);
+ free(record);
+ }
+ }
+}
+
+/*
+ * e2fsck.c - superblock checks
+ */
+
+#define MIN_CHECK 1
+#define MAX_CHECK 2
+
+static void check_super_value(e2fsck_t ctx, const char *descr,
+ unsigned long value, int flags,
+ unsigned long min_val, unsigned long max_val)
+{
+ struct problem_context pctx;
+
+ if (((flags & MIN_CHECK) && (value < min_val)) ||
+ ((flags & MAX_CHECK) && (value > max_val))) {
+ clear_problem_context(&pctx);
+ pctx.num = value;
+ pctx.str = descr;
+ fix_problem(ctx, PR_0_MISC_CORRUPT_SUPER, &pctx);
+ ctx->flags |= E2F_FLAG_ABORT; /* never get here! */
+ }
+}
+
+/*
+ * This routine may get stubbed out in special compilations of the
+ * e2fsck code..
+ */
+#ifndef EXT2_SPECIAL_DEVICE_SIZE
+static errcode_t e2fsck_get_device_size(e2fsck_t ctx)
+{
+ return (ext2fs_get_device_size(ctx->filesystem_name,
+ EXT2_BLOCK_SIZE(ctx->fs->super),
+ &ctx->num_blocks));
+}
+#endif
+
+/*
+ * helper function to release an inode
+ */
+struct process_block_struct {
+ e2fsck_t ctx;
+ char *buf;
+ struct problem_context *pctx;
+ int truncating;
+ int truncate_offset;
+ e2_blkcnt_t truncate_block;
+ int truncated_blocks;
+ int abort;
+ errcode_t errcode;
+};
+
+static int release_inode_block(ext2_filsys fs, blk_t *block_nr,
+ e2_blkcnt_t blockcnt,
+ blk_t ref_blk FSCK_ATTR((unused)),
+ int ref_offset FSCK_ATTR((unused)),
+ void *priv_data)
+{
+ struct process_block_struct *pb;
+ e2fsck_t ctx;
+ struct problem_context *pctx;
+ blk_t blk = *block_nr;
+ int retval = 0;
+
+ pb = (struct process_block_struct *) priv_data;
+ ctx = pb->ctx;
+ pctx = pb->pctx;
+
+ pctx->blk = blk;
+ pctx->blkcount = blockcnt;
+
+ if (HOLE_BLKADDR(blk))
+ return 0;
+
+ if ((blk < fs->super->s_first_data_block) ||
+ (blk >= fs->super->s_blocks_count)) {
+ fix_problem(ctx, PR_0_ORPHAN_ILLEGAL_BLOCK_NUM, pctx);
+ return_abort:
+ pb->abort = 1;
+ return BLOCK_ABORT;
+ }
+
+ if (!ext2fs_test_block_bitmap(fs->block_map, blk)) {
+ fix_problem(ctx, PR_0_ORPHAN_ALREADY_CLEARED_BLOCK, pctx);
+ goto return_abort;
+ }
+
+ /*
+ * If we are deleting an orphan, then we leave the fields alone.
+ * If we are truncating an orphan, then update the inode fields
+ * and clean up any partial block data.
+ */
+ if (pb->truncating) {
+ /*
+ * We only remove indirect blocks if they are
+ * completely empty.
+ */
+ if (blockcnt < 0) {
+ int i, limit;
+ blk_t *bp;
+
+ pb->errcode = io_channel_read_blk(fs->io, blk, 1,
+ pb->buf);
+ if (pb->errcode)
+ goto return_abort;
+
+ limit = fs->blocksize >> 2;
+ for (i = 0, bp = (blk_t *) pb->buf;
+ i < limit; i++, bp++)
+ if (*bp)
+ return 0;
+ }
+ /*
+ * We don't remove direct blocks until we've reached
+ * the truncation block.
+ */
+ if (blockcnt >= 0 && blockcnt < pb->truncate_block)
+ return 0;
+ /*
+ * If part of the last block needs truncating, we do
+ * it here.
+ */
+ if ((blockcnt == pb->truncate_block) && pb->truncate_offset) {
+ pb->errcode = io_channel_read_blk(fs->io, blk, 1,
+ pb->buf);
+ if (pb->errcode)
+ goto return_abort;
+ memset(pb->buf + pb->truncate_offset, 0,
+ fs->blocksize - pb->truncate_offset);
+ pb->errcode = io_channel_write_blk(fs->io, blk, 1,
+ pb->buf);
+ if (pb->errcode)
+ goto return_abort;
+ }
+ pb->truncated_blocks++;
+ *block_nr = 0;
+ retval |= BLOCK_CHANGED;
+ }
+
+ ext2fs_block_alloc_stats(fs, blk, -1);
+ return retval;
+}
+
+/*
+ * This function releases an inode. Returns 1 if an inconsistency was
+ * found. If the inode has a link count, then it is being truncated and
+ * not deleted.
+ */
+static int release_inode_blocks(e2fsck_t ctx, ext2_ino_t ino,
+ struct ext2_inode *inode, char *block_buf,
+ struct problem_context *pctx)
+{
+ struct process_block_struct pb;
+ ext2_filsys fs = ctx->fs;
+ errcode_t retval;
+ __u32 count;
+
+ if (!ext2fs_inode_has_valid_blocks(inode))
+ return 0;
+
+ pb.buf = block_buf + 3 * ctx->fs->blocksize;
+ pb.ctx = ctx;
+ pb.abort = 0;
+ pb.errcode = 0;
+ pb.pctx = pctx;
+ if (inode->i_links_count) {
+ pb.truncating = 1;
+ pb.truncate_block = (e2_blkcnt_t)
+ ((((long long)inode->i_size_high << 32) +
+ inode->i_size + fs->blocksize - 1) /
+ fs->blocksize);
+ pb.truncate_offset = inode->i_size % fs->blocksize;
+ } else {
+ pb.truncating = 0;
+ pb.truncate_block = 0;
+ pb.truncate_offset = 0;
+ }
+ pb.truncated_blocks = 0;
+ retval = ext2fs_block_iterate2(fs, ino, BLOCK_FLAG_DEPTH_TRAVERSE,
+ block_buf, release_inode_block, &pb);
+ if (retval) {
+ bb_error_msg(_("while calling ext2fs_block_iterate for inode %d"),
+ ino);
+ return 1;
+ }
+ if (pb.abort)
+ return 1;
+
+ /* Refresh the inode since ext2fs_block_iterate may have changed it */
+ e2fsck_read_inode(ctx, ino, inode, "release_inode_blocks");
+
+ if (pb.truncated_blocks)
+ inode->i_blocks -= pb.truncated_blocks *
+ (fs->blocksize / 512);
+
+ if (inode->i_file_acl) {
+ retval = ext2fs_adjust_ea_refcount(fs, inode->i_file_acl,
+ block_buf, -1, &count);
+ if (retval == EXT2_ET_BAD_EA_BLOCK_NUM) {
+ retval = 0;
+ count = 1;
+ }
+ if (retval) {
+ bb_error_msg(_("while calling ext2fs_adjust_ea_refocunt for inode %d"),
+ ino);
+ return 1;
+ }
+ if (count == 0)
+ ext2fs_block_alloc_stats(fs, inode->i_file_acl, -1);
+ inode->i_file_acl = 0;
+ }
+ return 0;
+}
+
+/*
+ * This function releases all of the orphan inodes. It returns 1 if
+ * it hit some error, and 0 on success.
+ */
+static int release_orphan_inodes(e2fsck_t ctx)
+{
+ ext2_filsys fs = ctx->fs;
+ ext2_ino_t ino, next_ino;
+ struct ext2_inode inode;
+ struct problem_context pctx;
+ char *block_buf;
+
+ if ((ino = fs->super->s_last_orphan) == 0)
+ return 0;
+
+ /*
+ * Win or lose, we won't be using the head of the orphan inode
+ * list again.
+ */
+ fs->super->s_last_orphan = 0;
+ ext2fs_mark_super_dirty(fs);
+
+ /*
+ * If the filesystem contains errors, don't run the orphan
+ * list, since the orphan list can't be trusted; and we're
+ * going to be running a full e2fsck run anyway...
+ */
+ if (fs->super->s_state & EXT2_ERROR_FS)
+ return 0;
+
+ if ((ino < EXT2_FIRST_INODE(fs->super)) ||
+ (ino > fs->super->s_inodes_count)) {
+ clear_problem_context(&pctx);
+ pctx.ino = ino;
+ fix_problem(ctx, PR_0_ORPHAN_ILLEGAL_HEAD_INODE, &pctx);
+ return 1;
+ }
+
+ block_buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize * 4,
+ "block iterate buffer");
+ e2fsck_read_bitmaps(ctx);
+
+ while (ino) {
+ e2fsck_read_inode(ctx, ino, &inode, "release_orphan_inodes");
+ clear_problem_context(&pctx);
+ pctx.ino = ino;
+ pctx.inode = &inode;
+ pctx.str = inode.i_links_count ? _("Truncating") :
+ _("Clearing");
+
+ fix_problem(ctx, PR_0_ORPHAN_CLEAR_INODE, &pctx);
+
+ next_ino = inode.i_dtime;
+ if (next_ino &&
+ ((next_ino < EXT2_FIRST_INODE(fs->super)) ||
+ (next_ino > fs->super->s_inodes_count))) {
+ pctx.ino = next_ino;
+ fix_problem(ctx, PR_0_ORPHAN_ILLEGAL_INODE, &pctx);
+ goto return_abort;
+ }
+
+ if (release_inode_blocks(ctx, ino, &inode, block_buf, &pctx))
+ goto return_abort;
+
+ if (!inode.i_links_count) {
+ ext2fs_inode_alloc_stats2(fs, ino, -1,
+ LINUX_S_ISDIR(inode.i_mode));
+ inode.i_dtime = time(0);
+ } else {
+ inode.i_dtime = 0;
+ }
+ e2fsck_write_inode(ctx, ino, &inode, "delete_file");
+ ino = next_ino;
+ }
+ ext2fs_free_mem(&block_buf);
+ return 0;
+return_abort:
+ ext2fs_free_mem(&block_buf);
+ return 1;
+}
+
+/*
+ * Check the resize inode to make sure it is sane. We check both for
+ * the case where on-line resizing is not enabled (in which case the
+ * resize inode should be cleared) as well as the case where on-line
+ * resizing is enabled.
+ */
+static void check_resize_inode(e2fsck_t ctx)
+{
+ ext2_filsys fs = ctx->fs;
+ struct ext2_inode inode;
+ struct problem_context pctx;
+ int i, j, gdt_off, ind_off;
+ blk_t blk, pblk, expect;
+ __u32 *dind_buf = 0, *ind_buf;
+ errcode_t retval;
+
+ clear_problem_context(&pctx);
+
+ /*
+ * If the resize inode feature isn't set, then
+ * s_reserved_gdt_blocks must be zero.
+ */
+ if (!(fs->super->s_feature_compat &
+ EXT2_FEATURE_COMPAT_RESIZE_INODE)) {
+ if (fs->super->s_reserved_gdt_blocks) {
+ pctx.num = fs->super->s_reserved_gdt_blocks;
+ if (fix_problem(ctx, PR_0_NONZERO_RESERVED_GDT_BLOCKS,
+ &pctx)) {
+ fs->super->s_reserved_gdt_blocks = 0;
+ ext2fs_mark_super_dirty(fs);
+ }
+ }
+ }
+
+ /* Read the resize inode */
+ pctx.ino = EXT2_RESIZE_INO;
+ retval = ext2fs_read_inode(fs, EXT2_RESIZE_INO, &inode);
+ if (retval) {
+ if (fs->super->s_feature_compat &
+ EXT2_FEATURE_COMPAT_RESIZE_INODE)
+ ctx->flags |= E2F_FLAG_RESIZE_INODE;
+ return;
+ }
+
+ /*
+ * If the resize inode feature isn't set, check to make sure
+ * the resize inode is cleared; then we're done.
+ */
+ if (!(fs->super->s_feature_compat &
+ EXT2_FEATURE_COMPAT_RESIZE_INODE)) {
+ for (i=0; i < EXT2_N_BLOCKS; i++) {
+ if (inode.i_block[i])
+ break;
+ }
+ if ((i < EXT2_N_BLOCKS) &&
+ fix_problem(ctx, PR_0_CLEAR_RESIZE_INODE, &pctx)) {
+ memset(&inode, 0, sizeof(inode));
+ e2fsck_write_inode(ctx, EXT2_RESIZE_INO, &inode,
+ "clear_resize");
+ }
+ return;
+ }
+
+ /*
+ * The resize inode feature is enabled; check to make sure the
+ * only block in use is the double indirect block
+ */
+ blk = inode.i_block[EXT2_DIND_BLOCK];
+ for (i=0; i < EXT2_N_BLOCKS; i++) {
+ if (i != EXT2_DIND_BLOCK && inode.i_block[i])
+ break;
+ }
+ if ((i < EXT2_N_BLOCKS) || !blk || !inode.i_links_count ||
+ !(inode.i_mode & LINUX_S_IFREG) ||
+ (blk < fs->super->s_first_data_block ||
+ blk >= fs->super->s_blocks_count)) {
+ resize_inode_invalid:
+ if (fix_problem(ctx, PR_0_RESIZE_INODE_INVALID, &pctx)) {
+ memset(&inode, 0, sizeof(inode));
+ e2fsck_write_inode(ctx, EXT2_RESIZE_INO, &inode,
+ "clear_resize");
+ ctx->flags |= E2F_FLAG_RESIZE_INODE;
+ }
+ if (!(ctx->options & E2F_OPT_READONLY)) {
+ fs->super->s_state &= ~EXT2_VALID_FS;
+ ext2fs_mark_super_dirty(fs);
+ }
+ goto cleanup;
+ }
+ dind_buf = (__u32 *) e2fsck_allocate_memory(ctx, fs->blocksize * 2,
+ "resize dind buffer");
+ ind_buf = (__u32 *) ((char *) dind_buf + fs->blocksize);
+
+ retval = ext2fs_read_ind_block(fs, blk, dind_buf);
+ if (retval)
+ goto resize_inode_invalid;
+
+ gdt_off = fs->desc_blocks;
+ pblk = fs->super->s_first_data_block + 1 + fs->desc_blocks;
+ for (i = 0; i < fs->super->s_reserved_gdt_blocks / 4;
+ i++, gdt_off++, pblk++) {
+ gdt_off %= fs->blocksize/4;
+ if (dind_buf[gdt_off] != pblk)
+ goto resize_inode_invalid;
+ retval = ext2fs_read_ind_block(fs, pblk, ind_buf);
+ if (retval)
+ goto resize_inode_invalid;
+ ind_off = 0;
+ for (j = 1; j < fs->group_desc_count; j++) {
+ if (!ext2fs_bg_has_super(fs, j))
+ continue;
+ expect = pblk + (j * fs->super->s_blocks_per_group);
+ if (ind_buf[ind_off] != expect)
+ goto resize_inode_invalid;
+ ind_off++;
+ }
+ }
+
+cleanup:
+ ext2fs_free_mem(&dind_buf);
+
+ }
+
+static void check_super_block(e2fsck_t ctx)
+{
+ ext2_filsys fs = ctx->fs;
+ blk_t first_block, last_block;
+ struct ext2_super_block *sb = fs->super;
+ struct ext2_group_desc *gd;
+ blk_t blocks_per_group = fs->super->s_blocks_per_group;
+ blk_t bpg_max;
+ int inodes_per_block;
+ int ipg_max;
+ int inode_size;
+ dgrp_t i;
+ blk_t should_be;
+ struct problem_context pctx;
+ __u32 free_blocks = 0, free_inodes = 0;
+
+ inodes_per_block = EXT2_INODES_PER_BLOCK(fs->super);
+ ipg_max = inodes_per_block * (blocks_per_group - 4);
+ if (ipg_max > EXT2_MAX_INODES_PER_GROUP(sb))
+ ipg_max = EXT2_MAX_INODES_PER_GROUP(sb);
+ bpg_max = 8 * EXT2_BLOCK_SIZE(sb);
+ if (bpg_max > EXT2_MAX_BLOCKS_PER_GROUP(sb))
+ bpg_max = EXT2_MAX_BLOCKS_PER_GROUP(sb);
+
+ ctx->invalid_inode_bitmap_flag = (int *) e2fsck_allocate_memory(ctx,
+ sizeof(int) * fs->group_desc_count, "invalid_inode_bitmap");
+ ctx->invalid_block_bitmap_flag = (int *) e2fsck_allocate_memory(ctx,
+ sizeof(int) * fs->group_desc_count, "invalid_block_bitmap");
+ ctx->invalid_inode_table_flag = (int *) e2fsck_allocate_memory(ctx,
+ sizeof(int) * fs->group_desc_count, "invalid_inode_table");
+
+ clear_problem_context(&pctx);
+
+ /*
+ * Verify the super block constants...
+ */
+ check_super_value(ctx, "inodes_count", sb->s_inodes_count,
+ MIN_CHECK, 1, 0);
+ check_super_value(ctx, "blocks_count", sb->s_blocks_count,
+ MIN_CHECK, 1, 0);
+ check_super_value(ctx, "first_data_block", sb->s_first_data_block,
+ MAX_CHECK, 0, sb->s_blocks_count);
+ check_super_value(ctx, "log_block_size", sb->s_log_block_size,
+ MIN_CHECK | MAX_CHECK, 0,
+ EXT2_MAX_BLOCK_LOG_SIZE - EXT2_MIN_BLOCK_LOG_SIZE);
+ check_super_value(ctx, "log_frag_size", sb->s_log_frag_size,
+ MIN_CHECK | MAX_CHECK, 0, sb->s_log_block_size);
+ check_super_value(ctx, "frags_per_group", sb->s_frags_per_group,
+ MIN_CHECK | MAX_CHECK, sb->s_blocks_per_group,
+ bpg_max);
+ check_super_value(ctx, "blocks_per_group", sb->s_blocks_per_group,
+ MIN_CHECK | MAX_CHECK, 8, bpg_max);
+ check_super_value(ctx, "inodes_per_group", sb->s_inodes_per_group,
+ MIN_CHECK | MAX_CHECK, inodes_per_block, ipg_max);
+ check_super_value(ctx, "r_blocks_count", sb->s_r_blocks_count,
+ MAX_CHECK, 0, sb->s_blocks_count / 2);
+ check_super_value(ctx, "reserved_gdt_blocks",
+ sb->s_reserved_gdt_blocks, MAX_CHECK, 0,
+ fs->blocksize/4);
+ inode_size = EXT2_INODE_SIZE(sb);
+ check_super_value(ctx, "inode_size",
+ inode_size, MIN_CHECK | MAX_CHECK,
+ EXT2_GOOD_OLD_INODE_SIZE, fs->blocksize);
+ if (inode_size & (inode_size - 1)) {
+ pctx.num = inode_size;
+ pctx.str = "inode_size";
+ fix_problem(ctx, PR_0_MISC_CORRUPT_SUPER, &pctx);
+ ctx->flags |= E2F_FLAG_ABORT; /* never get here! */
+ return;
+ }
+
+ if (!ctx->num_blocks) {
+ pctx.errcode = e2fsck_get_device_size(ctx);
+ if (pctx.errcode && pctx.errcode != EXT2_ET_UNIMPLEMENTED) {
+ fix_problem(ctx, PR_0_GETSIZE_ERROR, &pctx);
+ ctx->flags |= E2F_FLAG_ABORT;
+ return;
+ }
+ if ((pctx.errcode != EXT2_ET_UNIMPLEMENTED) &&
+ (ctx->num_blocks < sb->s_blocks_count)) {
+ pctx.blk = sb->s_blocks_count;
+ pctx.blk2 = ctx->num_blocks;
+ if (fix_problem(ctx, PR_0_FS_SIZE_WRONG, &pctx)) {
+ ctx->flags |= E2F_FLAG_ABORT;
+ return;
+ }
+ }
+ }
+
+ if (sb->s_log_block_size != (__u32) sb->s_log_frag_size) {
+ pctx.blk = EXT2_BLOCK_SIZE(sb);
+ pctx.blk2 = EXT2_FRAG_SIZE(sb);
+ fix_problem(ctx, PR_0_NO_FRAGMENTS, &pctx);
+ ctx->flags |= E2F_FLAG_ABORT;
+ return;
+ }
+
+ should_be = sb->s_frags_per_group >>
+ (sb->s_log_block_size - sb->s_log_frag_size);
+ if (sb->s_blocks_per_group != should_be) {
+ pctx.blk = sb->s_blocks_per_group;
+ pctx.blk2 = should_be;
+ fix_problem(ctx, PR_0_BLOCKS_PER_GROUP, &pctx);
+ ctx->flags |= E2F_FLAG_ABORT;
+ return;
+ }
+
+ should_be = (sb->s_log_block_size == 0) ? 1 : 0;
+ if (sb->s_first_data_block != should_be) {
+ pctx.blk = sb->s_first_data_block;
+ pctx.blk2 = should_be;
+ fix_problem(ctx, PR_0_FIRST_DATA_BLOCK, &pctx);
+ ctx->flags |= E2F_FLAG_ABORT;
+ return;
+ }
+
+ should_be = sb->s_inodes_per_group * fs->group_desc_count;
+ if (sb->s_inodes_count != should_be) {
+ pctx.ino = sb->s_inodes_count;
+ pctx.ino2 = should_be;
+ if (fix_problem(ctx, PR_0_INODE_COUNT_WRONG, &pctx)) {
+ sb->s_inodes_count = should_be;
+ ext2fs_mark_super_dirty(fs);
+ }
+ }
+
+ /*
+ * Verify the group descriptors....
+ */
+ first_block = sb->s_first_data_block;
+ last_block = first_block + blocks_per_group;
+
+ for (i = 0, gd=fs->group_desc; i < fs->group_desc_count; i++, gd++) {
+ pctx.group = i;
+
+ if (i == fs->group_desc_count - 1)
+ last_block = sb->s_blocks_count;
+ if ((gd->bg_block_bitmap < first_block) ||
+ (gd->bg_block_bitmap >= last_block)) {
+ pctx.blk = gd->bg_block_bitmap;
+ if (fix_problem(ctx, PR_0_BB_NOT_GROUP, &pctx))
+ gd->bg_block_bitmap = 0;
+ }
+ if (gd->bg_block_bitmap == 0) {
+ ctx->invalid_block_bitmap_flag[i]++;
+ ctx->invalid_bitmaps++;
+ }
+ if ((gd->bg_inode_bitmap < first_block) ||
+ (gd->bg_inode_bitmap >= last_block)) {
+ pctx.blk = gd->bg_inode_bitmap;
+ if (fix_problem(ctx, PR_0_IB_NOT_GROUP, &pctx))
+ gd->bg_inode_bitmap = 0;
+ }
+ if (gd->bg_inode_bitmap == 0) {
+ ctx->invalid_inode_bitmap_flag[i]++;
+ ctx->invalid_bitmaps++;
+ }
+ if ((gd->bg_inode_table < first_block) ||
+ ((gd->bg_inode_table +
+ fs->inode_blocks_per_group - 1) >= last_block)) {
+ pctx.blk = gd->bg_inode_table;
+ if (fix_problem(ctx, PR_0_ITABLE_NOT_GROUP, &pctx))
+ gd->bg_inode_table = 0;
+ }
+ if (gd->bg_inode_table == 0) {
+ ctx->invalid_inode_table_flag[i]++;
+ ctx->invalid_bitmaps++;
+ }
+ free_blocks += gd->bg_free_blocks_count;
+ free_inodes += gd->bg_free_inodes_count;
+ first_block += sb->s_blocks_per_group;
+ last_block += sb->s_blocks_per_group;
+
+ if ((gd->bg_free_blocks_count > sb->s_blocks_per_group) ||
+ (gd->bg_free_inodes_count > sb->s_inodes_per_group) ||
+ (gd->bg_used_dirs_count > sb->s_inodes_per_group))
+ ext2fs_unmark_valid(fs);
+
+ }
+
+ /*
+ * Update the global counts from the block group counts. This
+ * is needed for an experimental patch which eliminates
+ * locking the entire filesystem when allocating blocks or
+ * inodes; if the filesystem is not unmounted cleanly, the
+ * global counts may not be accurate.
+ */
+ if ((free_blocks != sb->s_free_blocks_count) ||
+ (free_inodes != sb->s_free_inodes_count)) {
+ if (ctx->options & E2F_OPT_READONLY)
+ ext2fs_unmark_valid(fs);
+ else {
+ sb->s_free_blocks_count = free_blocks;
+ sb->s_free_inodes_count = free_inodes;
+ ext2fs_mark_super_dirty(fs);
+ }
+ }
+
+ if ((sb->s_free_blocks_count > sb->s_blocks_count) ||
+ (sb->s_free_inodes_count > sb->s_inodes_count))
+ ext2fs_unmark_valid(fs);
+
+
+ /*
+ * If we have invalid bitmaps, set the error state of the
+ * filesystem.
+ */
+ if (ctx->invalid_bitmaps && !(ctx->options & E2F_OPT_READONLY)) {
+ sb->s_state &= ~EXT2_VALID_FS;
+ ext2fs_mark_super_dirty(fs);
+ }
+
+ clear_problem_context(&pctx);
+
+ /*
+ * If the UUID field isn't assigned, assign it.
+ */
+ if (!(ctx->options & E2F_OPT_READONLY) && uuid_is_null(sb->s_uuid)) {
+ if (fix_problem(ctx, PR_0_ADD_UUID, &pctx)) {
+ uuid_generate(sb->s_uuid);
+ ext2fs_mark_super_dirty(fs);
+ fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
+ }
+ }
+
+ /* FIXME - HURD support?
+ * For the Hurd, check to see if the filetype option is set,
+ * since it doesn't support it.
+ */
+ if (!(ctx->options & E2F_OPT_READONLY) &&
+ fs->super->s_creator_os == EXT2_OS_HURD &&
+ (fs->super->s_feature_incompat &
+ EXT2_FEATURE_INCOMPAT_FILETYPE)) {
+ if (fix_problem(ctx, PR_0_HURD_CLEAR_FILETYPE, &pctx)) {
+ fs->super->s_feature_incompat &=
+ ~EXT2_FEATURE_INCOMPAT_FILETYPE;
+ ext2fs_mark_super_dirty(fs);
+
+ }
+ }
+
+ /*
+ * If we have any of the compatibility flags set, we need to have a
+ * revision 1 filesystem. Most kernels will not check the flags on
+ * a rev 0 filesystem and we may have corruption issues because of
+ * the incompatible changes to the filesystem.
+ */
+ if (!(ctx->options & E2F_OPT_READONLY) &&
+ fs->super->s_rev_level == EXT2_GOOD_OLD_REV &&
+ (fs->super->s_feature_compat ||
+ fs->super->s_feature_ro_compat ||
+ fs->super->s_feature_incompat) &&
+ fix_problem(ctx, PR_0_FS_REV_LEVEL, &pctx)) {
+ ext2fs_update_dynamic_rev(fs);
+ ext2fs_mark_super_dirty(fs);
+ }
+
+ check_resize_inode(ctx);
+
+ /*
+ * Clean up any orphan inodes, if present.
+ */
+ if (!(ctx->options & E2F_OPT_READONLY) && release_orphan_inodes(ctx)) {
+ fs->super->s_state &= ~EXT2_VALID_FS;
+ ext2fs_mark_super_dirty(fs);
+ }
+
+ /*
+ * Move the ext3 journal file, if necessary.
+ */
+ e2fsck_move_ext3_journal(ctx);
+}
+
+/*
+ * swapfs.c --- byte-swap an ext2 filesystem
+ */
+
+#ifdef ENABLE_SWAPFS
+
+struct swap_block_struct {
+ ext2_ino_t ino;
+ int isdir;
+ errcode_t errcode;
+ char *dir_buf;
+ struct ext2_inode *inode;
+};
+
+/*
+ * This is a helper function for block_iterate. We mark all of the
+ * indirect and direct blocks as changed, so that block_iterate will
+ * write them out.
+ */
+static int swap_block(ext2_filsys fs, blk_t *block_nr, int blockcnt,
+ void *priv_data)
+{
+ errcode_t retval;
+
+ struct swap_block_struct *sb = (struct swap_block_struct *) priv_data;
+
+ if (sb->isdir && (blockcnt >= 0) && *block_nr) {
+ retval = ext2fs_read_dir_block(fs, *block_nr, sb->dir_buf);
+ if (retval) {
+ sb->errcode = retval;
+ return BLOCK_ABORT;
+ }
+ retval = ext2fs_write_dir_block(fs, *block_nr, sb->dir_buf);
+ if (retval) {
+ sb->errcode = retval;
+ return BLOCK_ABORT;
+ }
+ }
+ if (blockcnt >= 0) {
+ if (blockcnt < EXT2_NDIR_BLOCKS)
+ return 0;
+ return BLOCK_CHANGED;
+ }
+ if (blockcnt == BLOCK_COUNT_IND) {
+ if (*block_nr == sb->inode->i_block[EXT2_IND_BLOCK])
+ return 0;
+ return BLOCK_CHANGED;
+ }
+ if (blockcnt == BLOCK_COUNT_DIND) {
+ if (*block_nr == sb->inode->i_block[EXT2_DIND_BLOCK])
+ return 0;
+ return BLOCK_CHANGED;
+ }
+ if (blockcnt == BLOCK_COUNT_TIND) {
+ if (*block_nr == sb->inode->i_block[EXT2_TIND_BLOCK])
+ return 0;
+ return BLOCK_CHANGED;
+ }
+ return BLOCK_CHANGED;
+}
+
+/*
+ * This function is responsible for byte-swapping all of the indirect,
+ * block pointers. It is also responsible for byte-swapping directories.
+ */
+static void swap_inode_blocks(e2fsck_t ctx, ext2_ino_t ino, char *block_buf,
+ struct ext2_inode *inode)
+{
+ errcode_t retval;
+ struct swap_block_struct sb;
+
+ sb.ino = ino;
+ sb.inode = inode;
+ sb.dir_buf = block_buf + ctx->fs->blocksize*3;
+ sb.errcode = 0;
+ sb.isdir = 0;
+ if (LINUX_S_ISDIR(inode->i_mode))
+ sb.isdir = 1;
+
+ retval = ext2fs_block_iterate(ctx->fs, ino, 0, block_buf,
+ swap_block, &sb);
+ if (retval) {
+ bb_error_msg(_("while calling ext2fs_block_iterate"));
+ ctx->flags |= E2F_FLAG_ABORT;
+ return;
+ }
+ if (sb.errcode) {
+ bb_error_msg(_("while calling iterator function"));
+ ctx->flags |= E2F_FLAG_ABORT;
+ return;
+ }
+}
+
+static void swap_inodes(e2fsck_t ctx)
+{
+ ext2_filsys fs = ctx->fs;
+ dgrp_t group;
+ unsigned int i;
+ ext2_ino_t ino = 1;
+ char *buf, *block_buf;
+ errcode_t retval;
+ struct ext2_inode * inode;
+
+ e2fsck_use_inode_shortcuts(ctx, 1);
+
+ retval = ext2fs_get_mem(fs->blocksize * fs->inode_blocks_per_group,
+ &buf);
+ if (retval) {
+ bb_error_msg(_("while allocating inode buffer"));
+ ctx->flags |= E2F_FLAG_ABORT;
+ return;
+ }
+ block_buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize * 4,
+ "block interate buffer");
+ for (group = 0; group < fs->group_desc_count; group++) {
+ retval = io_channel_read_blk(fs->io,
+ fs->group_desc[group].bg_inode_table,
+ fs->inode_blocks_per_group, buf);
+ if (retval) {
+ bb_error_msg(_("while reading inode table (group %d)"),
+ group);
+ ctx->flags |= E2F_FLAG_ABORT;
+ return;
+ }
+ inode = (struct ext2_inode *) buf;
+ for (i=0; i < fs->super->s_inodes_per_group;
+ i++, ino++, inode++) {
+ ctx->stashed_ino = ino;
+ ctx->stashed_inode = inode;
+
+ if (fs->flags & EXT2_FLAG_SWAP_BYTES_READ)
+ ext2fs_swap_inode(fs, inode, inode, 0);
+
+ /*
+ * Skip deleted files.
+ */
+ if (inode->i_links_count == 0)
+ continue;
+
+ if (LINUX_S_ISDIR(inode->i_mode) ||
+ ((inode->i_block[EXT2_IND_BLOCK] ||
+ inode->i_block[EXT2_DIND_BLOCK] ||
+ inode->i_block[EXT2_TIND_BLOCK]) &&
+ ext2fs_inode_has_valid_blocks(inode)))
+ swap_inode_blocks(ctx, ino, block_buf, inode);
+
+ if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+ return;
+
+ if (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE)
+ ext2fs_swap_inode(fs, inode, inode, 1);
+ }
+ retval = io_channel_write_blk(fs->io,
+ fs->group_desc[group].bg_inode_table,
+ fs->inode_blocks_per_group, buf);
+ if (retval) {
+ bb_error_msg(_("while writing inode table (group %d)"),
+ group);
+ ctx->flags |= E2F_FLAG_ABORT;
+ return;
+ }
+ }
+ ext2fs_free_mem(&buf);
+ ext2fs_free_mem(&block_buf);
+ e2fsck_use_inode_shortcuts(ctx, 0);
+ ext2fs_flush_icache(fs);
+}
+
+#if defined(__powerpc__) && BB_BIG_ENDIAN
+/*
+ * On the PowerPC, the big-endian variant of the ext2 filesystem
+ * has its bitmaps stored as 32-bit words with bit 0 as the LSB
+ * of each word. Thus a bitmap with only bit 0 set would be, as
+ * a string of bytes, 00 00 00 01 00 ...
+ * To cope with this, we byte-reverse each word of a bitmap if
+ * we have a big-endian filesystem, that is, if we are *not*
+ * byte-swapping other word-sized numbers.
+ */
+#define EXT2_BIG_ENDIAN_BITMAPS
+#endif
+
+#ifdef EXT2_BIG_ENDIAN_BITMAPS
+static void ext2fs_swap_bitmap(ext2fs_generic_bitmap bmap)
+{
+ __u32 *p = (__u32 *) bmap->bitmap;
+ int n, nbytes = (bmap->end - bmap->start + 7) / 8;
+
+ for (n = nbytes / sizeof(__u32); n > 0; --n, ++p)
+ *p = ext2fs_swab32(*p);
+}
+#endif
+
+
+#ifdef ENABLE_SWAPFS
+static void swap_filesys(e2fsck_t ctx)
+{
+ ext2_filsys fs = ctx->fs;
+ if (!(ctx->options & E2F_OPT_PREEN))
+ printf(_("Pass 0: Doing byte-swap of filesystem\n"));
+
+ /* Byte swap */
+
+ if (fs->super->s_mnt_count) {
+ fprintf(stderr, _("%s: the filesystem must be freshly "
+ "checked using fsck\n"
+ "and not mounted before trying to "
+ "byte-swap it.\n"), ctx->device_name);
+ ctx->flags |= E2F_FLAG_ABORT;
+ return;
+ }
+ if (fs->flags & EXT2_FLAG_SWAP_BYTES) {
+ fs->flags &= ~(EXT2_FLAG_SWAP_BYTES|
+ EXT2_FLAG_SWAP_BYTES_WRITE);
+ fs->flags |= EXT2_FLAG_SWAP_BYTES_READ;
+ } else {
+ fs->flags &= ~EXT2_FLAG_SWAP_BYTES_READ;
+ fs->flags |= EXT2_FLAG_SWAP_BYTES_WRITE;
+ }
+ swap_inodes(ctx);
+ if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+ return;
+ if (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE)
+ fs->flags |= EXT2_FLAG_SWAP_BYTES;
+ fs->flags &= ~(EXT2_FLAG_SWAP_BYTES_READ|
+ EXT2_FLAG_SWAP_BYTES_WRITE);
+
+#ifdef EXT2_BIG_ENDIAN_BITMAPS
+ e2fsck_read_bitmaps(ctx);
+ ext2fs_swap_bitmap(fs->inode_map);
+ ext2fs_swap_bitmap(fs->block_map);
+ fs->flags |= EXT2_FLAG_BB_DIRTY | EXT2_FLAG_IB_DIRTY;
+#endif
+ fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
+ ext2fs_flush(fs);
+ fs->flags |= EXT2_FLAG_MASTER_SB_ONLY;
+}
+#endif /* ENABLE_SWAPFS */
+
+#endif
+
+/*
+ * util.c --- miscellaneous utilities
+ */
+
+
+void *e2fsck_allocate_memory(e2fsck_t ctx, unsigned int size,
+ const char *description)
+{
+ void *ret;
+ char buf[256];
+
+ ret = malloc(size);
+ if (!ret) {
+ sprintf(buf, "Can't allocate %s\n", description);
+ bb_error_msg_and_die(buf);
+ }
+ memset(ret, 0, size);
+ return ret;
+}
+
+static char *string_copy(const char *str, int len)
+{
+ char *ret;
+
+ if (!str)
+ return NULL;
+ if (!len)
+ len = strlen(str);
+ ret = malloc(len+1);
+ if (ret) {
+ strncpy(ret, str, len);
+ ret[len] = 0;
+ }
+ return ret;
+}
+
+#ifndef HAVE_CONIO_H
+static int read_a_char(void)
+{
+ char c;
+ int r;
+ int fail = 0;
+
+ while(1) {
+ if (e2fsck_global_ctx &&
+ (e2fsck_global_ctx->flags & E2F_FLAG_CANCEL)) {
+ return 3;
+ }
+ r = read(0, &c, 1);
+ if (r == 1)
+ return c;
+ if (fail++ > 100)
+ break;
+ }
+ return EOF;
+}
+#endif
+
+static int ask_yn(const char * string, int def)
+{
+ int c;
+ const char *defstr;
+ static const char short_yes[] = "yY";
+ static const char short_no[] = "nN";
+
+#ifdef HAVE_TERMIOS_H
+ struct termios termios, tmp;
+
+ tcgetattr (0, &termios);
+ tmp = termios;
+ tmp.c_lflag &= ~(ICANON | ECHO);
+ tmp.c_cc[VMIN] = 1;
+ tmp.c_cc[VTIME] = 0;
+ tcsetattr (0, TCSANOW, &tmp);
+#endif
+
+ if (def == 1)
+ defstr = "<y>";
+ else if (def == 0)
+ defstr = "<n>";
+ else
+ defstr = " (y/n)";
+ printf("%s%s? ", string, defstr);
+ while (1) {
+ fflush (stdout);
+ if ((c = read_a_char()) == EOF)
+ break;
+ if (c == 3) {
+#ifdef HAVE_TERMIOS_H
+ tcsetattr (0, TCSANOW, &termios);
+#endif
+ if (e2fsck_global_ctx &&
+ e2fsck_global_ctx->flags & E2F_FLAG_SETJMP_OK) {
+ puts("\n");
+ longjmp(e2fsck_global_ctx->abort_loc, 1);
+ }
+ puts(_("cancelled!\n"));
+ return 0;
+ }
+ if (strchr(short_yes, (char) c)) {
+ def = 1;
+ break;
+ }
+ else if (strchr(short_no, (char) c)) {
+ def = 0;
+ break;
+ }
+ else if ((c == ' ' || c == '\n') && (def != -1))
+ break;
+ }
+ if (def)
+ puts("yes\n");
+ else
+ puts ("no\n");
+#ifdef HAVE_TERMIOS_H
+ tcsetattr (0, TCSANOW, &termios);
+#endif
+ return def;
+}
+
+int ask (e2fsck_t ctx, const char * string, int def)
+{
+ if (ctx->options & E2F_OPT_NO) {
+ printf(_("%s? no\n\n"), string);
+ return 0;
+ }
+ if (ctx->options & E2F_OPT_YES) {
+ printf(_("%s? yes\n\n"), string);
+ return 1;
+ }
+ if (ctx->options & E2F_OPT_PREEN) {
+ printf("%s? %s\n\n", string, def ? _("yes") : _("no"));
+ return def;
+ }
+ return ask_yn(string, def);
+}
+
+void e2fsck_read_bitmaps(e2fsck_t ctx)
+{
+ ext2_filsys fs = ctx->fs;
+ errcode_t retval;
+
+ if (ctx->invalid_bitmaps) {
+ bb_error_msg(_("e2fsck_read_bitmaps: illegal bitmap block(s) for %s"),
+ ctx->device_name);
+ bb_error_msg_and_die(0);
+ }
+
+ ehandler_operation(_("reading inode and block bitmaps"));
+ retval = ext2fs_read_bitmaps(fs);
+ ehandler_operation(0);
+ if (retval) {
+ bb_error_msg(_("while retrying to read bitmaps for %s"),
+ ctx->device_name);
+ bb_error_msg_and_die(0);
+ }
+}
+
+static void e2fsck_write_bitmaps(e2fsck_t ctx)
+{
+ ext2_filsys fs = ctx->fs;
+ errcode_t retval;
+
+ if (ext2fs_test_bb_dirty(fs)) {
+ ehandler_operation(_("writing block bitmaps"));
+ retval = ext2fs_write_block_bitmap(fs);
+ ehandler_operation(0);
+ if (retval) {
+ bb_error_msg(_("while retrying to write block bitmaps for %s"),
+ ctx->device_name);
+ bb_error_msg_and_die(0);
+ }
+ }
+
+ if (ext2fs_test_ib_dirty(fs)) {
+ ehandler_operation(_("writing inode bitmaps"));
+ retval = ext2fs_write_inode_bitmap(fs);
+ ehandler_operation(0);
+ if (retval) {
+ bb_error_msg(_("while retrying to write inode bitmaps for %s"),
+ ctx->device_name);
+ bb_error_msg_and_die(0);
+ }
+ }
+}
+
+void preenhalt(e2fsck_t ctx)
+{
+ ext2_filsys fs = ctx->fs;
+
+ if (!(ctx->options & E2F_OPT_PREEN))
+ return;
+ fprintf(stderr, _("\n\n%s: UNEXPECTED INCONSISTENCY; "
+ "RUN fsck MANUALLY.\n\t(i.e., without -a or -p options)\n"),
+ ctx->device_name);
+ if (fs != NULL) {
+ fs->super->s_state |= EXT2_ERROR_FS;
+ ext2fs_mark_super_dirty(fs);
+ ext2fs_close(fs);
+ }
+ exit(EXIT_UNCORRECTED);
+}
+
+void e2fsck_read_inode(e2fsck_t ctx, unsigned long ino,
+ struct ext2_inode * inode, const char *proc)
+{
+ int retval;
+
+ retval = ext2fs_read_inode(ctx->fs, ino, inode);
+ if (retval) {
+ bb_error_msg(_("while reading inode %ld in %s"), ino, proc);
+ bb_error_msg_and_die(0);
+ }
+}
+
+extern void e2fsck_write_inode_full(e2fsck_t ctx, unsigned long ino,
+ struct ext2_inode * inode, int bufsize,
+ const char *proc)
+{
+ int retval;
+
+ retval = ext2fs_write_inode_full(ctx->fs, ino, inode, bufsize);
+ if (retval) {
+ bb_error_msg(_("while writing inode %ld in %s"), ino, proc);
+ bb_error_msg_and_die(0);
+ }
+}
+
+extern void e2fsck_write_inode(e2fsck_t ctx, unsigned long ino,
+ struct ext2_inode * inode, const char *proc)
+{
+ int retval;
+
+ retval = ext2fs_write_inode(ctx->fs, ino, inode);
+ if (retval) {
+ bb_error_msg(_("while writing inode %ld in %s"), ino, proc);
+ bb_error_msg_and_die(0);
+ }
+}
+
+blk_t get_backup_sb(e2fsck_t ctx, ext2_filsys fs, const char *name,
+ io_manager manager)
+{
+ struct ext2_super_block *sb;
+ io_channel io = NULL;
+ void *buf = NULL;
+ int blocksize;
+ blk_t superblock, ret_sb = 8193;
+
+ if (fs && fs->super) {
+ ret_sb = (fs->super->s_blocks_per_group +
+ fs->super->s_first_data_block);
+ if (ctx) {
+ ctx->superblock = ret_sb;
+ ctx->blocksize = fs->blocksize;
+ }
+ return ret_sb;
+ }
+
+ if (ctx) {
+ if (ctx->blocksize) {
+ ret_sb = ctx->blocksize * 8;
+ if (ctx->blocksize == 1024)
+ ret_sb++;
+ ctx->superblock = ret_sb;
+ return ret_sb;
+ }
+ ctx->superblock = ret_sb;
+ ctx->blocksize = 1024;
+ }
+
+ if (!name || !manager)
+ goto cleanup;
+
+ if (manager->open(name, 0, &io) != 0)
+ goto cleanup;
+
+ if (ext2fs_get_mem(SUPERBLOCK_SIZE, &buf))
+ goto cleanup;
+ sb = (struct ext2_super_block *) buf;
+
+ for (blocksize = EXT2_MIN_BLOCK_SIZE;
+ blocksize <= EXT2_MAX_BLOCK_SIZE ; blocksize *= 2) {
+ superblock = blocksize*8;
+ if (blocksize == 1024)
+ superblock++;
+ io_channel_set_blksize(io, blocksize);
+ if (io_channel_read_blk(io, superblock,
+ -SUPERBLOCK_SIZE, buf))
+ continue;
+#if BB_BIG_ENDIAN
+ if (sb->s_magic == ext2fs_swab16(EXT2_SUPER_MAGIC))
+ ext2fs_swap_super(sb);
+#endif
+ if (sb->s_magic == EXT2_SUPER_MAGIC) {
+ ret_sb = superblock;
+ if (ctx) {
+ ctx->superblock = superblock;
+ ctx->blocksize = blocksize;
+ }
+ break;
+ }
+ }
+
+cleanup:
+ if (io)
+ io_channel_close(io);
+ ext2fs_free_mem(&buf);
+ return ret_sb;
+}
+
+
+/*
+ * This function runs through the e2fsck passes and calls them all,
+ * returning restart, abort, or cancel as necessary...
+ */
+typedef void (*pass_t)(e2fsck_t ctx);
+
+static const pass_t e2fsck_passes[] = {
+ e2fsck_pass1, e2fsck_pass2, e2fsck_pass3, e2fsck_pass4,
+ e2fsck_pass5, 0 };
+
+#define E2F_FLAG_RUN_RETURN (E2F_FLAG_SIGNAL_MASK|E2F_FLAG_RESTART)
+
+static int e2fsck_run(e2fsck_t ctx)
+{
+ int i;
+ pass_t e2fsck_pass;
+
+ if (setjmp(ctx->abort_loc)) {
+ ctx->flags &= ~E2F_FLAG_SETJMP_OK;
+ return (ctx->flags & E2F_FLAG_RUN_RETURN);
+ }
+ ctx->flags |= E2F_FLAG_SETJMP_OK;
+
+ for (i=0; (e2fsck_pass = e2fsck_passes[i]); i++) {
+ if (ctx->flags & E2F_FLAG_RUN_RETURN)
+ break;
+ e2fsck_pass(ctx);
+ if (ctx->progress)
+ (void) (ctx->progress)(ctx, 0, 0, 0);
+ }
+ ctx->flags &= ~E2F_FLAG_SETJMP_OK;
+
+ if (ctx->flags & E2F_FLAG_RUN_RETURN)
+ return (ctx->flags & E2F_FLAG_RUN_RETURN);
+ return 0;
+}
+
+
+/*
+ * unix.c - The unix-specific code for e2fsck
+ */
+
+
+/* Command line options */
+static int swapfs;
+#ifdef ENABLE_SWAPFS
+static int normalize_swapfs;
+#endif
+static int cflag; /* check disk */
+static int show_version_only;
+static int verbose;
+
+#define P_E2(singular, plural, n) n, ((n) == 1 ? singular : plural)
+
+static void show_stats(e2fsck_t ctx)
+{
+ ext2_filsys fs = ctx->fs;
+ int inodes, inodes_used, blocks, blocks_used;
+ int dir_links;
+ int num_files, num_links;
+ int frag_percent;
+
+ dir_links = 2 * ctx->fs_directory_count - 1;
+ num_files = ctx->fs_total_count - dir_links;
+ num_links = ctx->fs_links_count - dir_links;
+ inodes = fs->super->s_inodes_count;
+ inodes_used = (fs->super->s_inodes_count -
+ fs->super->s_free_inodes_count);
+ blocks = fs->super->s_blocks_count;
+ blocks_used = (fs->super->s_blocks_count -
+ fs->super->s_free_blocks_count);
+
+ frag_percent = (10000 * ctx->fs_fragmented) / inodes_used;
+ frag_percent = (frag_percent + 5) / 10;
+
+ if (!verbose) {
+ printf("%s: %d/%d files (%0d.%d%% non-contiguous), %d/%d blocks\n",
+ ctx->device_name, inodes_used, inodes,
+ frag_percent / 10, frag_percent % 10,
+ blocks_used, blocks);
+ return;
+ }
+ printf("\n%8d inode%s used (%d%%)\n", P_E2("", "s", inodes_used),
+ 100 * inodes_used / inodes);
+ printf("%8d non-contiguous inode%s (%0d.%d%%)\n",
+ P_E2("", "s", ctx->fs_fragmented),
+ frag_percent / 10, frag_percent % 10);
+ printf(_(" # of inodes with ind/dind/tind blocks: %d/%d/%d\n"),
+ ctx->fs_ind_count, ctx->fs_dind_count, ctx->fs_tind_count);
+ printf("%8d block%s used (%d%%)\n", P_E2("", "s", blocks_used),
+ (int) ((long long) 100 * blocks_used / blocks));
+ printf("%8d large file%s\n", P_E2("", "s", ctx->large_files));
+ printf("\n%8d regular file%s\n", P_E2("", "s", ctx->fs_regular_count));
+ printf("%8d director%s\n", P_E2("y", "ies", ctx->fs_directory_count));
+ printf("%8d character device file%s\n", P_E2("", "s", ctx->fs_chardev_count));
+ printf("%8d block device file%s\n", P_E2("", "s", ctx->fs_blockdev_count));
+ printf("%8d fifo%s\n", P_E2("", "s", ctx->fs_fifo_count));
+ printf("%8d link%s\n", P_E2("", "s", ctx->fs_links_count - dir_links));
+ printf("%8d symbolic link%s", P_E2("", "s", ctx->fs_symlinks_count));
+ printf(" (%d fast symbolic link%s)\n", P_E2("", "s", ctx->fs_fast_symlinks_count));
+ printf("%8d socket%s--------\n\n", P_E2("", "s", ctx->fs_sockets_count));
+ printf("%8d file%s\n", P_E2("", "s", ctx->fs_total_count - dir_links));
+}
+
+static void check_mount(e2fsck_t ctx)
+{
+ errcode_t retval;
+ int cont;
+
+ retval = ext2fs_check_if_mounted(ctx->filesystem_name,
+ &ctx->mount_flags);
+ if (retval) {
+ bb_error_msg(_("while determining whether %s is mounted."),
+ ctx->filesystem_name);
+ return;
+ }
+
+ /*
+ * If the filesystem isn't mounted, or it's the root filesystem
+ * and it's mounted read-only, then everything's fine.
+ */
+ if ((!(ctx->mount_flags & EXT2_MF_MOUNTED)) ||
+ ((ctx->mount_flags & EXT2_MF_ISROOT) &&
+ (ctx->mount_flags & EXT2_MF_READONLY)))
+ return;
+
+ if (ctx->options & E2F_OPT_READONLY) {
+ printf(_("Warning! %s is mounted.\n"), ctx->filesystem_name);
+ return;
+ }
+
+ printf(_("%s is mounted. "), ctx->filesystem_name);
+ if (!ctx->interactive)
+ bb_error_msg_and_die(_("Cannot continue, aborting."));
+ printf(_("\n\n\007\007\007\007WARNING!!! "
+ "Running e2fsck on a mounted filesystem may cause\n"
+ "SEVERE filesystem damage.\007\007\007\n\n"));
+ cont = ask_yn(_("Do you really want to continue"), -1);
+ if (!cont) {
+ printf(_("check aborted.\n"));
+ exit (0);
+ }
+}
+
+static int is_on_batt(void)
+{
+ FILE *f;
+ DIR *d;
+ char tmp[80], tmp2[80], fname[80];
+ unsigned int acflag;
+ struct dirent* de;
+
+ f = fopen("/proc/apm", "r");
+ if (f) {
+ if (fscanf(f, "%s %s %s %x", tmp, tmp, tmp, &acflag) != 4)
+ acflag = 1;
+ fclose(f);
+ return (acflag != 1);
+ }
+ d = opendir("/proc/acpi/ac_adapter");
+ if (d) {
+ while ((de=readdir(d)) != NULL) {
+ if (!strncmp(".", de->d_name, 1))
+ continue;
+ snprintf(fname, 80, "/proc/acpi/ac_adapter/%s/state",
+ de->d_name);
+ f = fopen(fname, "r");
+ if (!f)
+ continue;
+ if (fscanf(f, "%s %s", tmp2, tmp) != 2)
+ tmp[0] = 0;
+ fclose(f);
+ if (strncmp(tmp, "off-line", 8) == 0) {
+ closedir(d);
+ return 1;
+ }
+ }
+ closedir(d);
+ }
+ return 0;
+}
+
+/*
+ * This routine checks to see if a filesystem can be skipped; if so,
+ * it will exit with EXIT_OK. Under some conditions it will print a
+ * message explaining why a check is being forced.
+ */
+static void check_if_skip(e2fsck_t ctx)
+{
+ ext2_filsys fs = ctx->fs;
+ const char *reason = NULL;
+ unsigned int reason_arg = 0;
+ long next_check;
+ int batt = is_on_batt();
+ time_t now = time(0);
+
+ if ((ctx->options & E2F_OPT_FORCE) || cflag || swapfs)
+ return;
+
+ if ((fs->super->s_state & EXT2_ERROR_FS) ||
+ !ext2fs_test_valid(fs))
+ reason = _(" contains a file system with errors");
+ else if ((fs->super->s_state & EXT2_VALID_FS) == 0)
+ reason = _(" was not cleanly unmounted");
+ else if ((fs->super->s_max_mnt_count > 0) &&
+ (fs->super->s_mnt_count >=
+ (unsigned) fs->super->s_max_mnt_count)) {
+ reason = _(" has been mounted %u times without being checked");
+ reason_arg = fs->super->s_mnt_count;
+ if (batt && (fs->super->s_mnt_count <
+ (unsigned) fs->super->s_max_mnt_count*2))
+ reason = 0;
+ } else if (fs->super->s_checkinterval &&
+ ((now - fs->super->s_lastcheck) >=
+ fs->super->s_checkinterval)) {
+ reason = _(" has gone %u days without being checked");
+ reason_arg = (now - fs->super->s_lastcheck)/(3600*24);
+ if (batt && ((now - fs->super->s_lastcheck) <
+ fs->super->s_checkinterval*2))
+ reason = 0;
+ }
+ if (reason) {
+ fputs(ctx->device_name, stdout);
+ printf(reason, reason_arg);
+ fputs(_(", check forced.\n"), stdout);
+ return;
+ }
+ printf(_("%s: clean, %d/%d files, %d/%d blocks"), ctx->device_name,
+ fs->super->s_inodes_count - fs->super->s_free_inodes_count,
+ fs->super->s_inodes_count,
+ fs->super->s_blocks_count - fs->super->s_free_blocks_count,
+ fs->super->s_blocks_count);
+ next_check = 100000;
+ if (fs->super->s_max_mnt_count > 0) {
+ next_check = fs->super->s_max_mnt_count - fs->super->s_mnt_count;
+ if (next_check <= 0)
+ next_check = 1;
+ }
+ if (fs->super->s_checkinterval &&
+ ((now - fs->super->s_lastcheck) >= fs->super->s_checkinterval))
+ next_check = 1;
+ if (next_check <= 5) {
+ if (next_check == 1)
+ fputs(_(" (check after next mount)"), stdout);
+ else
+ printf(_(" (check in %ld mounts)"), next_check);
+ }
+ fputc('\n', stdout);
+ ext2fs_close(fs);
+ ctx->fs = NULL;
+ e2fsck_free_context(ctx);
+ exit(EXIT_OK);
+}
+
+/*
+ * For completion notice
+ */
+struct percent_tbl {
+ int max_pass;
+ int table[32];
+};
+static const struct percent_tbl e2fsck_tbl = {
+ 5, { 0, 70, 90, 92, 95, 100 }
+};
+
+static char bar[128], spaces[128];
+
+static float calc_percent(const struct percent_tbl *tbl, int pass, int curr,
+ int max)
+{
+ float percent;
+
+ if (pass <= 0)
+ return 0.0;
+ if (pass > tbl->max_pass || max == 0)
+ return 100.0;
+ percent = ((float) curr) / ((float) max);
+ return ((percent * (tbl->table[pass] - tbl->table[pass-1]))
+ + tbl->table[pass-1]);
+}
+
+void e2fsck_clear_progbar(e2fsck_t ctx)
+{
+ if (!(ctx->flags & E2F_FLAG_PROG_BAR))
+ return;
+
+ printf("%s%s\r%s", ctx->start_meta, spaces + (sizeof(spaces) - 80),
+ ctx->stop_meta);
+ fflush(stdout);
+ ctx->flags &= ~E2F_FLAG_PROG_BAR;
+}
+
+int e2fsck_simple_progress(e2fsck_t ctx, const char *label, float percent,
+ unsigned int dpynum)
+{
+ static const char spinner[] = "\\|/-";
+ int i;
+ unsigned int tick;
+ struct timeval tv;
+ int dpywidth;
+ int fixed_percent;
+
+ if (ctx->flags & E2F_FLAG_PROG_SUPPRESS)
+ return 0;
+
+ /*
+ * Calculate the new progress position. If the
+ * percentage hasn't changed, then we skip out right
+ * away.
+ */
+ fixed_percent = (int) ((10 * percent) + 0.5);
+ if (ctx->progress_last_percent == fixed_percent)
+ return 0;
+ ctx->progress_last_percent = fixed_percent;
+
+ /*
+ * If we've already updated the spinner once within
+ * the last 1/8th of a second, no point doing it
+ * again.
+ */
+ gettimeofday(&tv, NULL);
+ tick = (tv.tv_sec << 3) + (tv.tv_usec / (1000000 / 8));
+ if ((tick == ctx->progress_last_time) &&
+ (fixed_percent != 0) && (fixed_percent != 1000))
+ return 0;
+ ctx->progress_last_time = tick;
+
+ /*
+ * Advance the spinner, and note that the progress bar
+ * will be on the screen
+ */
+ ctx->progress_pos = (ctx->progress_pos+1) & 3;
+ ctx->flags |= E2F_FLAG_PROG_BAR;
+
+ dpywidth = 66 - strlen(label);
+ dpywidth = 8 * (dpywidth / 8);
+ if (dpynum)
+ dpywidth -= 8;
+
+ i = ((percent * dpywidth) + 50) / 100;
+ printf("%s%s: |%s%s", ctx->start_meta, label,
+ bar + (sizeof(bar) - (i+1)),
+ spaces + (sizeof(spaces) - (dpywidth - i + 1)));
+ if (fixed_percent == 1000)
+ fputc('|', stdout);
+ else
+ fputc(spinner[ctx->progress_pos & 3], stdout);
+ printf(" %4.1f%% ", percent);
+ if (dpynum)
+ printf("%u\r", dpynum);
+ else
+ fputs(" \r", stdout);
+ fputs(ctx->stop_meta, stdout);
+
+ if (fixed_percent == 1000)
+ e2fsck_clear_progbar(ctx);
+ fflush(stdout);
+
+ return 0;
+}
+
+static int e2fsck_update_progress(e2fsck_t ctx, int pass,
+ unsigned long cur, unsigned long max)
+{
+ char buf[80];
+ float percent;
+
+ if (pass == 0)
+ return 0;
+
+ if (ctx->progress_fd) {
+ sprintf(buf, "%d %lu %lu\n", pass, cur, max);
+ write(ctx->progress_fd, buf, strlen(buf));
+ } else {
+ percent = calc_percent(&e2fsck_tbl, pass, cur, max);
+ e2fsck_simple_progress(ctx, ctx->device_name,
+ percent, 0);
+ }
+ return 0;
+}
+
+static void reserve_stdio_fds(void)
+{
+ int fd;
+
+ while (1) {
+ fd = open(bb_dev_null, O_RDWR);
+ if (fd > 2)
+ break;
+ if (fd < 0) {
+ fprintf(stderr, _("ERROR: Cannot open "
+ "/dev/null (%s)\n"),
+ strerror(errno));
+ break;
+ }
+ }
+ close(fd);
+}
+
+static void signal_progress_on(int sig FSCK_ATTR((unused)))
+{
+ e2fsck_t ctx = e2fsck_global_ctx;
+
+ if (!ctx)
+ return;
+
+ ctx->progress = e2fsck_update_progress;
+ ctx->progress_fd = 0;
+}
+
+static void signal_progress_off(int sig FSCK_ATTR((unused)))
+{
+ e2fsck_t ctx = e2fsck_global_ctx;
+
+ if (!ctx)
+ return;
+
+ e2fsck_clear_progbar(ctx);
+ ctx->progress = 0;
+}
+
+static void signal_cancel(int sig FSCK_ATTR((unused)))
+{
+ e2fsck_t ctx = e2fsck_global_ctx;
+
+ if (!ctx)
+ exit(FSCK_CANCELED);
+
+ ctx->flags |= E2F_FLAG_CANCEL;
+}
+
+static void parse_extended_opts(e2fsck_t ctx, const char *opts)
+{
+ char *buf, *token, *next, *p, *arg;
+ int ea_ver;
+ int extended_usage = 0;
+
+ buf = string_copy(opts, 0);
+ for (token = buf; token && *token; token = next) {
+ p = strchr(token, ',');
+ next = 0;
+ if (p) {
+ *p = 0;
+ next = p+1;
+ }
+ arg = strchr(token, '=');
+ if (arg) {
+ *arg = 0;
+ arg++;
+ }
+ if (strcmp(token, "ea_ver") == 0) {
+ if (!arg) {
+ extended_usage++;
+ continue;
+ }
+ ea_ver = strtoul(arg, &p, 0);
+ if (*p ||
+ ((ea_ver != 1) && (ea_ver != 2))) {
+ fprintf(stderr,
+ _("Invalid EA version.\n"));
+ extended_usage++;
+ continue;
+ }
+ ctx->ext_attr_ver = ea_ver;
+ } else {
+ fprintf(stderr, _("Unknown extended option: %s\n"),
+ token);
+ extended_usage++;
+ }
+ }
+ if (extended_usage) {
+ bb_error_msg_and_die(
+ "Extended options are separated by commas, "
+ "and may take an argument which\n"
+ "is set off by an equals ('=') sign. "
+ "Valid extended options are:\n"
+ "\tea_ver=<ea_version (1 or 2)>\n\n");
+ }
+}
+
+
+static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx)
+{
+ int flush = 0;
+ int c, fd;
+ e2fsck_t ctx;
+ errcode_t retval;
+ struct sigaction sa;
+ char *extended_opts = 0;
+
+ retval = e2fsck_allocate_context(&ctx);
+ if (retval)
+ return retval;
+
+ *ret_ctx = ctx;
+
+ setvbuf(stdout, NULL, _IONBF, BUFSIZ);
+ setvbuf(stderr, NULL, _IONBF, BUFSIZ);
+ if (isatty(0) && isatty(1)) {
+ ctx->interactive = 1;
+ } else {
+ ctx->start_meta[0] = '\001';
+ ctx->stop_meta[0] = '\002';
+ }
+ memset(bar, '=', sizeof(bar)-1);
+ memset(spaces, ' ', sizeof(spaces)-1);
+ blkid_get_cache(&ctx->blkid, NULL);
+
+ if (argc && *argv)
+ ctx->program_name = *argv;
+ else
+ ctx->program_name = "e2fsck";
+ while ((c = getopt (argc, argv, "panyrcC:B:dE:fvtFVM:b:I:j:P:l:L:N:SsDk")) != EOF)
+ switch (c) {
+ case 'C':
+ ctx->progress = e2fsck_update_progress;
+ ctx->progress_fd = atoi(optarg);
+ if (!ctx->progress_fd)
+ break;
+ /* Validate the file descriptor to avoid disasters */
+ fd = dup(ctx->progress_fd);
+ if (fd < 0) {
+ fprintf(stderr,
+ _("Error validating file descriptor %d: %s\n"),
+ ctx->progress_fd,
+ error_message(errno));
+ bb_error_msg_and_die(_("Invalid completion information file descriptor"));
+ } else
+ close(fd);
+ break;
+ case 'D':
+ ctx->options |= E2F_OPT_COMPRESS_DIRS;
+ break;
+ case 'E':
+ extended_opts = optarg;
+ break;
+ case 'p':
+ case 'a':
+ if (ctx->options & (E2F_OPT_YES|E2F_OPT_NO)) {
+ conflict_opt:
+ bb_error_msg_and_die(_("Only one the options -p/-a, -n or -y may be specified."));
+ }
+ ctx->options |= E2F_OPT_PREEN;
+ break;
+ case 'n':
+ if (ctx->options & (E2F_OPT_YES|E2F_OPT_PREEN))
+ goto conflict_opt;
+ ctx->options |= E2F_OPT_NO;
+ break;
+ case 'y':
+ if (ctx->options & (E2F_OPT_PREEN|E2F_OPT_NO))
+ goto conflict_opt;
+ ctx->options |= E2F_OPT_YES;
+ break;
+ case 't':
+ /* FIXME - This needs to go away in a future path - will change binary */
+ fprintf(stderr, _("The -t option is not "
+ "supported on this version of e2fsck.\n"));
+ break;
+ case 'c':
+ if (cflag++)
+ ctx->options |= E2F_OPT_WRITECHECK;
+ ctx->options |= E2F_OPT_CHECKBLOCKS;
+ break;
+ case 'r':
+ /* What we do by default, anyway! */
+ break;
+ case 'b':
+ ctx->use_superblock = atoi(optarg);
+ ctx->flags |= E2F_FLAG_SB_SPECIFIED;
+ break;
+ case 'B':
+ ctx->blocksize = atoi(optarg);
+ break;
+ case 'I':
+ ctx->inode_buffer_blocks = atoi(optarg);
+ break;
+ case 'j':
+ ctx->journal_name = string_copy(optarg, 0);
+ break;
+ case 'P':
+ ctx->process_inode_size = atoi(optarg);
+ break;
+ case 'd':
+ ctx->options |= E2F_OPT_DEBUG;
+ break;
+ case 'f':
+ ctx->options |= E2F_OPT_FORCE;
+ break;
+ case 'F':
+ flush = 1;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case 'V':
+ show_version_only = 1;
+ break;
+ case 'N':
+ ctx->device_name = optarg;
+ break;
+#ifdef ENABLE_SWAPFS
+ case 's':
+ normalize_swapfs = 1;
+ case 'S':
+ swapfs = 1;
+ break;
+#else
+ case 's':
+ case 'S':
+ fprintf(stderr, _("Byte-swapping filesystems "
+ "not compiled in this version "
+ "of e2fsck\n"));
+ exit(1);
+#endif
+ default:
+ bb_show_usage();
+ }
+ if (show_version_only)
+ return 0;
+ if (optind != argc - 1)
+ bb_show_usage();
+ if ((ctx->options & E2F_OPT_NO) &&
+ !cflag && !swapfs && !(ctx->options & E2F_OPT_COMPRESS_DIRS))
+ ctx->options |= E2F_OPT_READONLY;
+ ctx->io_options = strchr(argv[optind], '?');
+ if (ctx->io_options)
+ *ctx->io_options++ = 0;
+ ctx->filesystem_name = blkid_get_devname(ctx->blkid, argv[optind], 0);
+ if (!ctx->filesystem_name) {
+ bb_error_msg(_("Unable to resolve '%s'"), argv[optind]);
+ bb_error_msg_and_die(0);
+ }
+ if (extended_opts)
+ parse_extended_opts(ctx, extended_opts);
+
+ if (flush) {
+ fd = open(ctx->filesystem_name, O_RDONLY, 0);
+ if (fd < 0) {
+ bb_error_msg(_("while opening %s for flushing"),
+ ctx->filesystem_name);
+ bb_error_msg_and_die(0);
+ }
+ if ((retval = ext2fs_sync_device(fd, 1))) {
+ bb_error_msg(_("while trying to flush %s"),
+ ctx->filesystem_name);
+ bb_error_msg_and_die(0);
+ }
+ close(fd);
+ }
+#ifdef ENABLE_SWAPFS
+ if (swapfs && cflag) {
+ fprintf(stderr, _("Incompatible options not "
+ "allowed when byte-swapping.\n"));
+ exit(EXIT_USAGE);
+ }
+#endif
+ /*
+ * Set up signal action
+ */
+ memset(&sa, 0, sizeof(struct sigaction));
+ sa.sa_handler = signal_cancel;
+ sigaction(SIGINT, &sa, 0);
+ sigaction(SIGTERM, &sa, 0);
+#ifdef SA_RESTART
+ sa.sa_flags = SA_RESTART;
+#endif
+ e2fsck_global_ctx = ctx;
+ sa.sa_handler = signal_progress_on;
+ sigaction(SIGUSR1, &sa, 0);
+ sa.sa_handler = signal_progress_off;
+ sigaction(SIGUSR2, &sa, 0);
+
+ /* Update our PATH to include /sbin if we need to run badblocks */
+ if (cflag)
+ e2fs_set_sbin_path();
+ return 0;
+}
+
+static const char my_ver_string[] = E2FSPROGS_VERSION;
+static const char my_ver_date[] = E2FSPROGS_DATE;
+
+int e2fsck_main (int argc, char *argv[]);
+int e2fsck_main (int argc, char *argv[])
+{
+ errcode_t retval;
+ int exit_value = EXIT_OK;
+ ext2_filsys fs = 0;
+ io_manager io_ptr;
+ struct ext2_super_block *sb;
+ const char *lib_ver_date;
+ int my_ver, lib_ver;
+ e2fsck_t ctx;
+ struct problem_context pctx;
+ int flags, run_result;
+
+ clear_problem_context(&pctx);
+
+ my_ver = ext2fs_parse_version_string(my_ver_string);
+ lib_ver = ext2fs_get_library_version(0, &lib_ver_date);
+ if (my_ver > lib_ver) {
+ fprintf( stderr, _("Error: ext2fs library version "
+ "out of date!\n"));
+ show_version_only++;
+ }
+
+ retval = PRS(argc, argv, &ctx);
+ if (retval) {
+ bb_error_msg(_("while trying to initialize program"));
+ exit(EXIT_ERROR);
+ }
+ reserve_stdio_fds();
+
+ if (!(ctx->options & E2F_OPT_PREEN) || show_version_only)
+ fprintf(stderr, "e2fsck %s (%s)\n", my_ver_string,
+ my_ver_date);
+
+ if (show_version_only) {
+ fprintf(stderr, _("\tUsing %s, %s\n"),
+ error_message(EXT2_ET_BASE), lib_ver_date);
+ exit(EXIT_OK);
+ }
+
+ check_mount(ctx);
+
+ if (!(ctx->options & E2F_OPT_PREEN) &&
+ !(ctx->options & E2F_OPT_NO) &&
+ !(ctx->options & E2F_OPT_YES)) {
+ if (!ctx->interactive)
+ bb_error_msg_and_die(_("need terminal for interactive repairs"));
+ }
+ ctx->superblock = ctx->use_superblock;
+restart:
+#ifdef CONFIG_TESTIO_DEBUG
+ io_ptr = test_io_manager;
+ test_io_backing_manager = unix_io_manager;
+#else
+ io_ptr = unix_io_manager;
+#endif
+ flags = 0;
+ if ((ctx->options & E2F_OPT_READONLY) == 0)
+ flags |= EXT2_FLAG_RW;
+
+ if (ctx->superblock && ctx->blocksize) {
+ retval = ext2fs_open2(ctx->filesystem_name, ctx->io_options,
+ flags, ctx->superblock, ctx->blocksize,
+ io_ptr, &fs);
+ } else if (ctx->superblock) {
+ int blocksize;
+ for (blocksize = EXT2_MIN_BLOCK_SIZE;
+ blocksize <= EXT2_MAX_BLOCK_SIZE; blocksize *= 2) {
+ retval = ext2fs_open2(ctx->filesystem_name,
+ ctx->io_options, flags,
+ ctx->superblock, blocksize,
+ io_ptr, &fs);
+ if (!retval)
+ break;
+ }
+ } else
+ retval = ext2fs_open2(ctx->filesystem_name, ctx->io_options,
+ flags, 0, 0, io_ptr, &fs);
+ if (!ctx->superblock && !(ctx->options & E2F_OPT_PREEN) &&
+ !(ctx->flags & E2F_FLAG_SB_SPECIFIED) &&
+ ((retval == EXT2_ET_BAD_MAGIC) ||
+ ((retval == 0) && ext2fs_check_desc(fs)))) {
+ if (!fs || (fs->group_desc_count > 1)) {
+ printf(_("%s trying backup blocks...\n"),
+ retval ? _("Couldn't find ext2 superblock,") :
+ _("Group descriptors look bad..."));
+ get_backup_sb(ctx, fs, ctx->filesystem_name, io_ptr);
+ if (fs)
+ ext2fs_close(fs);
+ goto restart;
+ }
+ }
+ if (retval) {
+ bb_error_msg(_("while trying to open %s"),
+ ctx->filesystem_name);
+ if (retval == EXT2_ET_REV_TOO_HIGH) {
+ printf(_("The filesystem revision is apparently "
+ "too high for this version of e2fsck.\n"
+ "(Or the filesystem superblock "
+ "is corrupt)\n\n"));
+ fix_problem(ctx, PR_0_SB_CORRUPT, &pctx);
+ } else if (retval == EXT2_ET_SHORT_READ)
+ printf(_("Could this be a zero-length partition?\n"));
+ else if ((retval == EPERM) || (retval == EACCES))
+ printf(_("You must have %s access to the "
+ "filesystem or be root\n"),
+ (ctx->options & E2F_OPT_READONLY) ?
+ "r/o" : "r/w");
+ else if (retval == ENXIO)
+ printf(_("Possibly non-existent or swap device?\n"));
+#ifdef EROFS
+ else if (retval == EROFS)
+ printf(_("Disk write-protected; use the -n option "
+ "to do a read-only\n"
+ "check of the device.\n"));
+#endif
+ else
+ fix_problem(ctx, PR_0_SB_CORRUPT, &pctx);
+ bb_error_msg_and_die(0);
+ }
+ ctx->fs = fs;
+ fs->priv_data = ctx;
+ sb = fs->super;
+ if (sb->s_rev_level > E2FSCK_CURRENT_REV) {
+ bb_error_msg(_("while trying to open %s"),
+ ctx->filesystem_name);
+ get_newer:
+ bb_error_msg_and_die(_("Get a newer version of e2fsck!"));
+ }
+
+ /*
+ * Set the device name, which is used whenever we print error
+ * or informational messages to the user.
+ */
+ if (ctx->device_name == 0 &&
+ (sb->s_volume_name[0] != 0)) {
+ ctx->device_name = string_copy(sb->s_volume_name,
+ sizeof(sb->s_volume_name));
+ }
+ if (ctx->device_name == 0)
+ ctx->device_name = ctx->filesystem_name;
+
+ /*
+ * Make sure the ext3 superblock fields are consistent.
+ */
+ retval = e2fsck_check_ext3_journal(ctx);
+ if (retval) {
+ bb_error_msg(_("while checking ext3 journal for %s"),
+ ctx->device_name);
+ bb_error_msg_and_die(0);
+ }
+
+ /*
+ * Check to see if we need to do ext3-style recovery. If so,
+ * do it, and then restart the fsck.
+ */
+ if (sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER) {
+ if (ctx->options & E2F_OPT_READONLY) {
+ printf(_("Warning: skipping journal recovery "
+ "because doing a read-only filesystem "
+ "check.\n"));
+ io_channel_flush(ctx->fs->io);
+ } else {
+ if (ctx->flags & E2F_FLAG_RESTARTED) {
+ /*
+ * Whoops, we attempted to run the
+ * journal twice. This should never
+ * happen, unless the hardware or
+ * device driver is being bogus.
+ */
+ bb_error_msg(_("cannot set superblock flags on %s"), ctx->device_name);
+ bb_error_msg_and_die(0);
+ }
+ retval = e2fsck_run_ext3_journal(ctx);
+ if (retval) {
+ bb_error_msg(_("while recovering ext3 journal of %s"),
+ ctx->device_name);
+ bb_error_msg_and_die(0);
+ }
+ ext2fs_close(ctx->fs);
+ ctx->fs = 0;
+ ctx->flags |= E2F_FLAG_RESTARTED;
+ goto restart;
+ }
+ }
+
+ /*
+ * Check for compatibility with the feature sets. We need to
+ * be more stringent than ext2fs_open().
+ */
+ if ((sb->s_feature_compat & ~EXT2_LIB_FEATURE_COMPAT_SUPP) ||
+ (sb->s_feature_incompat & ~EXT2_LIB_FEATURE_INCOMPAT_SUPP)) {
+ bb_error_msg("(%s)", ctx->device_name);
+ goto get_newer;
+ }
+ if (sb->s_feature_ro_compat & ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP) {
+ bb_error_msg("(%s)", ctx->device_name);
+ goto get_newer;
+ }
+#ifdef ENABLE_COMPRESSION
+ /* FIXME - do we support this at all? */
+ if (sb->s_feature_incompat & EXT2_FEATURE_INCOMPAT_COMPRESSION)
+ bb_error_msg(_("Warning: compression support is experimental."));
+#endif
+#ifndef ENABLE_HTREE
+ if (sb->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) {
+ bb_error_msg(_("E2fsck not compiled with HTREE support,\n\t"
+ "but filesystem %s has HTREE directories."),
+ ctx->device_name);
+ goto get_newer;
+ }
+#endif
+
+ /*
+ * If the user specified a specific superblock, presumably the
+ * master superblock has been trashed. So we mark the
+ * superblock as dirty, so it can be written out.
+ */
+ if (ctx->superblock &&
+ !(ctx->options & E2F_OPT_READONLY))
+ ext2fs_mark_super_dirty(fs);
+
+ /*
+ * We only update the master superblock because (a) paranoia;
+ * we don't want to corrupt the backup superblocks, and (b) we
+ * don't need to update the mount count and last checked
+ * fields in the backup superblock (the kernel doesn't
+ * update the backup superblocks anyway).
+ */
+ fs->flags |= EXT2_FLAG_MASTER_SB_ONLY;
+
+ ehandler_init(fs->io);
+
+ if (ctx->superblock)
+ set_latch_flags(PR_LATCH_RELOC, PRL_LATCHED, 0);
+ ext2fs_mark_valid(fs);
+ check_super_block(ctx);
+ if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+ bb_error_msg_and_die(0);
+ check_if_skip(ctx);
+ if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+ bb_error_msg_and_die(0);
+#ifdef ENABLE_SWAPFS
+
+#ifdef WORDS_BIGENDIAN
+#define NATIVE_FLAG EXT2_FLAG_SWAP_BYTES
+#else
+#define NATIVE_FLAG 0
+#endif
+
+
+ if (normalize_swapfs) {
+ if ((fs->flags & EXT2_FLAG_SWAP_BYTES) == NATIVE_FLAG) {
+ fprintf(stderr, _("%s: Filesystem byte order "
+ "already normalized.\n"), ctx->device_name);
+ bb_error_msg_and_die(0);
+ }
+ }
+ if (swapfs) {
+ swap_filesys(ctx);
+ if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+ bb_error_msg_and_die(0);
+ }
+#endif
+
+ /*
+ * Mark the system as valid, 'til proven otherwise
+ */
+ ext2fs_mark_valid(fs);
+
+ retval = ext2fs_read_bb_inode(fs, &fs->badblocks);
+ if (retval) {
+ bb_error_msg(_("while reading bad blocks inode"));
+ preenhalt(ctx);
+ printf(_("This doesn't bode well,"
+ " but we'll try to go on...\n"));
+ }
+
+ run_result = e2fsck_run(ctx);
+ e2fsck_clear_progbar(ctx);
+ if (run_result == E2F_FLAG_RESTART) {
+ printf(_("Restarting e2fsck from the beginning...\n"));
+ retval = e2fsck_reset_context(ctx);
+ if (retval) {
+ bb_error_msg(_("while resetting context"));
+ bb_error_msg_and_die(0);
+ }
+ ext2fs_close(fs);
+ goto restart;
+ }
+ if (run_result & E2F_FLAG_CANCEL) {
+ printf(_("%s: e2fsck canceled.\n"), ctx->device_name ?
+ ctx->device_name : ctx->filesystem_name);
+ exit_value |= FSCK_CANCELED;
+ }
+ if (run_result & E2F_FLAG_ABORT)
+ bb_error_msg_and_die(_("aborted"));
+
+ /* Cleanup */
+ if (ext2fs_test_changed(fs)) {
+ exit_value |= EXIT_NONDESTRUCT;
+ if (!(ctx->options & E2F_OPT_PREEN))
+ printf(_("\n%s: ***** FILE SYSTEM WAS MODIFIED *****\n"),
+ ctx->device_name);
+ if (ctx->mount_flags & EXT2_MF_ISROOT) {
+ printf(_("%s: ***** REBOOT LINUX *****\n"),
+ ctx->device_name);
+ exit_value |= EXIT_DESTRUCT;
+ }
+ }
+ if (!ext2fs_test_valid(fs)) {
+ printf(_("\n%s: ********** WARNING: Filesystem still has "
+ "errors **********\n\n"), ctx->device_name);
+ exit_value |= EXIT_UNCORRECTED;
+ exit_value &= ~EXIT_NONDESTRUCT;
+ }
+ if (exit_value & FSCK_CANCELED)
+ exit_value &= ~EXIT_NONDESTRUCT;
+ else {
+ show_stats(ctx);
+ if (!(ctx->options & E2F_OPT_READONLY)) {
+ if (ext2fs_test_valid(fs)) {
+ if (!(sb->s_state & EXT2_VALID_FS))
+ exit_value |= EXIT_NONDESTRUCT;
+ sb->s_state = EXT2_VALID_FS;
+ } else
+ sb->s_state &= ~EXT2_VALID_FS;
+ sb->s_mnt_count = 0;
+ sb->s_lastcheck = time(NULL);
+ ext2fs_mark_super_dirty(fs);
+ }
+ }
+
+ e2fsck_write_bitmaps(ctx);
+
+ ext2fs_close(fs);
+ ctx->fs = NULL;
+ free(ctx->filesystem_name);
+ free(ctx->journal_name);
+ e2fsck_free_context(ctx);
+
+ return exit_value;
+}
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2fsck.h b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2fsck.h
new file mode 100644
index 0000000..e520632
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2fsck.h
@@ -0,0 +1,640 @@
+/* vi: set sw=4 ts=4: */
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <time.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <setjmp.h>
+#include <errno.h>
+#include <getopt.h>
+#include <limits.h>
+#include <stddef.h>
+#include <assert.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/resource.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/ioctl.h>
+#include <termios.h>
+#include <mntent.h>
+#include <dirent.h>
+#include "ext2fs/kernel-list.h"
+#include <sys/types.h>
+#include <linux/types.h>
+
+/*
+ * Now pull in the real linux/jfs.h definitions.
+ */
+#include "ext2fs/kernel-jbd.h"
+
+
+
+#include "fsck.h"
+
+#include "ext2fs/ext2_fs.h"
+#include "blkid/blkid.h"
+#include "ext2fs/ext2_ext_attr.h"
+#include "uuid/uuid.h"
+#include "busybox.h"
+
+#ifdef HAVE_CONIO_H
+#undef HAVE_TERMIOS_H
+#include <conio.h>
+#define read_a_char() getch()
+#else
+#ifdef HAVE_TERMIOS_H
+#include <termios.h>
+#endif
+#endif
+
+
+/*
+ * The last ext2fs revision level that this version of e2fsck is able to
+ * support
+ */
+#define E2FSCK_CURRENT_REV 1
+
+/* Used by the region allocation code */
+typedef __u32 region_addr_t;
+typedef struct region_struct *region_t;
+
+struct dx_dirblock_info {
+ int type;
+ blk_t phys;
+ int flags;
+ blk_t parent;
+ ext2_dirhash_t min_hash;
+ ext2_dirhash_t max_hash;
+ ext2_dirhash_t node_min_hash;
+ ext2_dirhash_t node_max_hash;
+};
+
+/*
+These defines are used in the type field of dx_dirblock_info
+*/
+
+#define DX_DIRBLOCK_ROOT 1
+#define DX_DIRBLOCK_LEAF 2
+#define DX_DIRBLOCK_NODE 3
+
+
+/*
+The following defines are used in the 'flags' field of a dx_dirblock_info
+*/
+#define DX_FLAG_REFERENCED 1
+#define DX_FLAG_DUP_REF 2
+#define DX_FLAG_FIRST 4
+#define DX_FLAG_LAST 8
+
+/*
+ * E2fsck options
+ */
+#define E2F_OPT_READONLY 0x0001
+#define E2F_OPT_PREEN 0x0002
+#define E2F_OPT_YES 0x0004
+#define E2F_OPT_NO 0x0008
+#define E2F_OPT_TIME 0x0010
+#define E2F_OPT_CHECKBLOCKS 0x0040
+#define E2F_OPT_DEBUG 0x0080
+#define E2F_OPT_FORCE 0x0100
+#define E2F_OPT_WRITECHECK 0x0200
+#define E2F_OPT_COMPRESS_DIRS 0x0400
+
+/*
+ * E2fsck flags
+ */
+#define E2F_FLAG_ABORT 0x0001 /* Abort signaled */
+#define E2F_FLAG_CANCEL 0x0002 /* Cancel signaled */
+#define E2F_FLAG_SIGNAL_MASK 0x0003
+#define E2F_FLAG_RESTART 0x0004 /* Restart signaled */
+
+#define E2F_FLAG_SETJMP_OK 0x0010 /* Setjmp valid for abort */
+
+#define E2F_FLAG_PROG_BAR 0x0020 /* Progress bar on screen */
+#define E2F_FLAG_PROG_SUPPRESS 0x0040 /* Progress suspended */
+#define E2F_FLAG_JOURNAL_INODE 0x0080 /* Create a new ext3 journal inode */
+#define E2F_FLAG_SB_SPECIFIED 0x0100 /* The superblock was explicitly
+ * specified by the user */
+#define E2F_FLAG_RESTARTED 0x0200 /* E2fsck has been restarted */
+#define E2F_FLAG_RESIZE_INODE 0x0400 /* Request to recreate resize inode */
+
+
+/*Don't know where these come from*/
+#define READ 0
+#define WRITE 1
+#define cpu_to_be32(n) htonl(n)
+#define be32_to_cpu(n) ntohl(n)
+
+/*
+ * We define a set of "latch groups"; these are problems which are
+ * handled as a set. The user answers once for a particular latch
+ * group.
+ */
+#define PR_LATCH_MASK 0x0ff0 /* Latch mask */
+#define PR_LATCH_BLOCK 0x0010 /* Latch for illegal blocks (pass 1) */
+#define PR_LATCH_BBLOCK 0x0020 /* Latch for bad block inode blocks (pass 1) */
+#define PR_LATCH_IBITMAP 0x0030 /* Latch for pass 5 inode bitmap proc. */
+#define PR_LATCH_BBITMAP 0x0040 /* Latch for pass 5 inode bitmap proc. */
+#define PR_LATCH_RELOC 0x0050 /* Latch for superblock relocate hint */
+#define PR_LATCH_DBLOCK 0x0060 /* Latch for pass 1b dup block headers */
+#define PR_LATCH_LOW_DTIME 0x0070 /* Latch for pass1 orphaned list refugees */
+#define PR_LATCH_TOOBIG 0x0080 /* Latch for file to big errors */
+#define PR_LATCH_OPTIMIZE_DIR 0x0090 /* Latch for optimize directories */
+
+#define PR_LATCH(x) ((((x) & PR_LATCH_MASK) >> 4) - 1)
+
+/*
+ * Latch group descriptor flags
+ */
+#define PRL_YES 0x0001 /* Answer yes */
+#define PRL_NO 0x0002 /* Answer no */
+#define PRL_LATCHED 0x0004 /* The latch group is latched */
+#define PRL_SUPPRESS 0x0008 /* Suppress all latch group questions */
+
+#define PRL_VARIABLE 0x000f /* All the flags that need to be reset */
+
+/*
+ * Pre-Pass 1 errors
+ */
+
+#define PR_0_BB_NOT_GROUP 0x000001 /* Block bitmap not in group */
+#define PR_0_IB_NOT_GROUP 0x000002 /* Inode bitmap not in group */
+#define PR_0_ITABLE_NOT_GROUP 0x000003 /* Inode table not in group */
+#define PR_0_SB_CORRUPT 0x000004 /* Superblock corrupt */
+#define PR_0_FS_SIZE_WRONG 0x000005 /* Filesystem size is wrong */
+#define PR_0_NO_FRAGMENTS 0x000006 /* Fragments not supported */
+#define PR_0_BLOCKS_PER_GROUP 0x000007 /* Bad blocks_per_group */
+#define PR_0_FIRST_DATA_BLOCK 0x000008 /* Bad first_data_block */
+#define PR_0_ADD_UUID 0x000009 /* Adding UUID to filesystem */
+#define PR_0_RELOCATE_HINT 0x00000A /* Relocate hint */
+#define PR_0_MISC_CORRUPT_SUPER 0x00000B /* Miscellaneous superblock corruption */
+#define PR_0_GETSIZE_ERROR 0x00000C /* Error determing physical device size of filesystem */
+#define PR_0_INODE_COUNT_WRONG 0x00000D /* Inode count in the superblock incorrect */
+#define PR_0_HURD_CLEAR_FILETYPE 0x00000E /* The Hurd does not support the filetype feature */
+#define PR_0_JOURNAL_BAD_INODE 0x00000F /* The Hurd does not support the filetype feature */
+#define PR_0_JOURNAL_UNSUPP_MULTIFS 0x000010 /* The external journal has multiple filesystems (which we can't handle yet) */
+#define PR_0_CANT_FIND_JOURNAL 0x000011 /* Can't find external journal */
+#define PR_0_EXT_JOURNAL_BAD_SUPER 0x000012/* External journal has bad superblock */
+#define PR_0_JOURNAL_BAD_UUID 0x000013 /* Superblock has a bad journal UUID */
+#define PR_0_JOURNAL_UNSUPP_SUPER 0x000014 /* Journal has an unknown superblock type */
+#define PR_0_JOURNAL_BAD_SUPER 0x000015 /* Journal superblock is corrupt */
+#define PR_0_JOURNAL_HAS_JOURNAL 0x000016 /* Journal superblock is corrupt */
+#define PR_0_JOURNAL_RECOVER_SET 0x000017 /* Superblock has recovery flag set but no journal */
+#define PR_0_JOURNAL_RECOVERY_CLEAR 0x000018 /* Journal has data, but recovery flag is clear */
+#define PR_0_JOURNAL_RESET_JOURNAL 0x000019 /* Ask if we should clear the journal */
+#define PR_0_FS_REV_LEVEL 0x00001A /* Filesystem revision is 0, but feature flags are set */
+#define PR_0_ORPHAN_CLEAR_INODE 0x000020 /* Clearing orphan inode */
+#define PR_0_ORPHAN_ILLEGAL_BLOCK_NUM 0x000021 /* Illegal block found in orphaned inode */
+#define PR_0_ORPHAN_ALREADY_CLEARED_BLOCK 0x000022 /* Already cleared block found in orphaned inode */
+#define PR_0_ORPHAN_ILLEGAL_HEAD_INODE 0x000023 /* Illegal orphan inode in superblock */
+#define PR_0_ORPHAN_ILLEGAL_INODE 0x000024 /* Illegal inode in orphaned inode list */
+#define PR_0_JOURNAL_UNSUPP_ROCOMPAT 0x000025 /* Journal has unsupported read-only feature - abort */
+#define PR_0_JOURNAL_UNSUPP_INCOMPAT 0x000026 /* Journal has unsupported incompatible feature - abort */
+#define PR_0_JOURNAL_UNSUPP_VERSION 0x000027 /* Journal has unsupported version number */
+#define PR_0_MOVE_JOURNAL 0x000028 /* Moving journal to hidden file */
+#define PR_0_ERR_MOVE_JOURNAL 0x000029 /* Error moving journal */
+#define PR_0_CLEAR_V2_JOURNAL 0x00002A /* Clearing V2 journal superblock */
+#define PR_0_JOURNAL_RUN 0x00002B /* Run journal anyway */
+#define PR_0_JOURNAL_RUN_DEFAULT 0x00002C /* Run journal anyway by default */
+#define PR_0_BACKUP_JNL 0x00002D /* Backup journal inode blocks */
+#define PR_0_NONZERO_RESERVED_GDT_BLOCKS 0x00002E /* Reserved blocks w/o resize_inode */
+#define PR_0_CLEAR_RESIZE_INODE 0x00002F /* Resize_inode not enabled, but resize inode is non-zero */
+#define PR_0_RESIZE_INODE_INVALID 0x000030 /* Resize inode invalid */
+
+/*
+ * Pass 1 errors
+ */
+
+#define PR_1_PASS_HEADER 0x010000 /* Pass 1: Checking inodes, blocks, and sizes */
+#define PR_1_ROOT_NO_DIR 0x010001 /* Root directory is not an inode */
+#define PR_1_ROOT_DTIME 0x010002 /* Root directory has dtime set */
+#define PR_1_RESERVED_BAD_MODE 0x010003 /* Reserved inode has bad mode */
+#define PR_1_ZERO_DTIME 0x010004 /* Deleted inode has zero dtime */
+#define PR_1_SET_DTIME 0x010005 /* Inode in use, but dtime set */
+#define PR_1_ZERO_LENGTH_DIR 0x010006 /* Zero-length directory */
+#define PR_1_BB_CONFLICT 0x010007 /* Block bitmap conflicts with some other fs block */
+#define PR_1_IB_CONFLICT 0x010008 /* Inode bitmap conflicts with some other fs block */
+#define PR_1_ITABLE_CONFLICT 0x010009 /* Inode table conflicts with some other fs block */
+#define PR_1_BB_BAD_BLOCK 0x01000A /* Block bitmap is on a bad block */
+#define PR_1_IB_BAD_BLOCK 0x01000B /* Inode bitmap is on a bad block */
+#define PR_1_BAD_I_SIZE 0x01000C /* Inode has incorrect i_size */
+#define PR_1_BAD_I_BLOCKS 0x01000D /* Inode has incorrect i_blocks */
+#define PR_1_ILLEGAL_BLOCK_NUM 0x01000E /* Illegal block number in inode */
+#define PR_1_BLOCK_OVERLAPS_METADATA 0x01000F /* Block number overlaps fs metadata */
+#define PR_1_INODE_BLOCK_LATCH 0x010010 /* Inode has illegal blocks (latch question) */
+#define PR_1_TOO_MANY_BAD_BLOCKS 0x010011 /* Too many bad blocks in inode */
+#define PR_1_BB_ILLEGAL_BLOCK_NUM 0x010012 /* Illegal block number in bad block inode */
+#define PR_1_INODE_BBLOCK_LATCH 0x010013 /* Bad block inode has illegal blocks (latch question) */
+#define PR_1_DUP_BLOCKS_PREENSTOP 0x010014 /* Duplicate or bad blocks in use! */
+#define PR_1_BBINODE_BAD_METABLOCK 0x010015 /* Bad block used as bad block indirect block */
+#define PR_1_BBINODE_BAD_METABLOCK_PROMPT 0x010016 /* Inconsistency can't be fixed prompt */
+#define PR_1_BAD_PRIMARY_BLOCK 0x010017 /* Bad primary block */
+#define PR_1_BAD_PRIMARY_BLOCK_PROMPT 0x010018 /* Bad primary block prompt */
+#define PR_1_BAD_PRIMARY_SUPERBLOCK 0x010019 /* Bad primary superblock */
+#define PR_1_BAD_PRIMARY_GROUP_DESCRIPTOR 0x01001A /* Bad primary block group descriptors */
+#define PR_1_BAD_SUPERBLOCK 0x01001B /* Bad superblock in group */
+#define PR_1_BAD_GROUP_DESCRIPTORS 0x01001C /* Bad block group descriptors in group */
+#define PR_1_PROGERR_CLAIMED_BLOCK 0x01001D /* Block claimed for no reason */
+#define PR_1_RELOC_BLOCK_ALLOCATE 0x01001E /* Error allocating blocks for relocating metadata */
+#define PR_1_RELOC_MEMORY_ALLOCATE 0x01001F /* Error allocating block buffer during relocation process */
+#define PR_1_RELOC_FROM_TO 0x010020 /* Relocating metadata group information from X to Y */
+#define PR_1_RELOC_TO 0x010021 /* Relocating metatdata group information to X */
+#define PR_1_RELOC_READ_ERR 0x010022 /* Block read error during relocation process */
+#define PR_1_RELOC_WRITE_ERR 0x010023 /* Block write error during relocation process */
+#define PR_1_ALLOCATE_IBITMAP_ERROR 0x010024 /* Error allocating inode bitmap */
+#define PR_1_ALLOCATE_BBITMAP_ERROR 0x010025 /* Error allocating block bitmap */
+#define PR_1_ALLOCATE_ICOUNT 0x010026 /* Error allocating icount structure */
+#define PR_1_ALLOCATE_DBCOUNT 0x010027 /* Error allocating dbcount */
+#define PR_1_ISCAN_ERROR 0x010028 /* Error while scanning inodes */
+#define PR_1_BLOCK_ITERATE 0x010029 /* Error while iterating over blocks */
+#define PR_1_ICOUNT_STORE 0x01002A /* Error while storing inode count information */
+#define PR_1_ADD_DBLOCK 0x01002B /* Error while storing directory block information */
+#define PR_1_READ_INODE 0x01002C /* Error while reading inode (for clearing) */
+#define PR_1_SUPPRESS_MESSAGES 0x01002D /* Suppress messages prompt */
+#define PR_1_SET_IMAGIC 0x01002F /* Imagic flag set on an inode when filesystem doesn't support it */
+#define PR_1_SET_IMMUTABLE 0x010030 /* Immutable flag set on a device or socket inode */
+#define PR_1_COMPR_SET 0x010031 /* Compression flag set on a non-compressed filesystem */
+#define PR_1_SET_NONZSIZE 0x010032 /* Non-zero size on on device, fifo or socket inode */
+#define PR_1_FS_REV_LEVEL 0x010033 /* Filesystem revision is 0, but feature flags are set */
+#define PR_1_JOURNAL_INODE_NOT_CLEAR 0x010034 /* Journal inode not in use, needs clearing */
+#define PR_1_JOURNAL_BAD_MODE 0x010035 /* Journal inode has wrong mode */
+#define PR_1_LOW_DTIME 0x010036 /* Inode that was part of orphan linked list */
+#define PR_1_ORPHAN_LIST_REFUGEES 0x010037 /* Latch question which asks how to deal with low dtime inodes */
+#define PR_1_ALLOCATE_REFCOUNT 0x010038 /* Error allocating refcount structure */
+#define PR_1_READ_EA_BLOCK 0x010039 /* Error reading Extended Attribute block */
+#define PR_1_BAD_EA_BLOCK 0x01003A /* Invalid Extended Attribute block */
+#define PR_1_EXTATTR_READ_ABORT 0x01003B /* Error reading Extended Attribute block while fixing refcount -- abort */
+#define PR_1_EXTATTR_REFCOUNT 0x01003C /* Extended attribute reference count incorrect */
+#define PR_1_EXTATTR_WRITE 0x01003D /* Error writing Extended Attribute block while fixing refcount */
+#define PR_1_EA_MULTI_BLOCK 0x01003E /* Multiple EA blocks not supported */
+#define PR_1_EA_ALLOC_REGION 0x01003F /* Error allocating EA region allocation structure */
+#define PR_1_EA_ALLOC_COLLISION 0x010040 /* Error EA allocation collision */
+#define PR_1_EA_BAD_NAME 0x010041 /* Bad extended attribute name */
+#define PR_1_EA_BAD_VALUE 0x010042 /* Bad extended attribute value */
+#define PR_1_INODE_TOOBIG 0x010043 /* Inode too big (latch question) */
+#define PR_1_TOOBIG_DIR 0x010044 /* Directory too big */
+#define PR_1_TOOBIG_REG 0x010045 /* Regular file too big */
+#define PR_1_TOOBIG_SYMLINK 0x010046 /* Symlink too big */
+#define PR_1_HTREE_SET 0x010047 /* INDEX_FL flag set on a non-HTREE filesystem */
+#define PR_1_HTREE_NODIR 0x010048 /* INDEX_FL flag set on a non-directory */
+#define PR_1_HTREE_BADROOT 0x010049 /* Invalid root node in HTREE directory */
+#define PR_1_HTREE_HASHV 0x01004A /* Unsupported hash version in HTREE directory */
+#define PR_1_HTREE_INCOMPAT 0x01004B /* Incompatible flag in HTREE root node */
+#define PR_1_HTREE_DEPTH 0x01004C /* HTREE too deep */
+#define PR_1_BB_FS_BLOCK 0x01004D /* Bad block has indirect block that conflicts with filesystem block */
+#define PR_1_RESIZE_INODE_CREATE 0x01004E /* Resize inode failed */
+#define PR_1_EXTRA_ISIZE 0x01004F /* inode->i_size is too long */
+#define PR_1_ATTR_NAME_LEN 0x010050 /* attribute name is too long */
+#define PR_1_ATTR_VALUE_OFFSET 0x010051 /* wrong EA value offset */
+#define PR_1_ATTR_VALUE_BLOCK 0x010052 /* wrong EA blocknumber */
+#define PR_1_ATTR_VALUE_SIZE 0x010053 /* wrong EA value size */
+#define PR_1_ATTR_HASH 0x010054 /* wrong EA hash value */
+
+/*
+ * Pass 1b errors
+ */
+
+#define PR_1B_PASS_HEADER 0x011000 /* Pass 1B: Rescan for duplicate/bad blocks */
+#define PR_1B_DUP_BLOCK_HEADER 0x011001 /* Duplicate/bad block(s) header */
+#define PR_1B_DUP_BLOCK 0x011002 /* Duplicate/bad block(s) in inode */
+#define PR_1B_DUP_BLOCK_END 0x011003 /* Duplicate/bad block(s) end */
+#define PR_1B_ISCAN_ERROR 0x011004 /* Error while scanning inodes */
+#define PR_1B_ALLOCATE_IBITMAP_ERROR 0x011005 /* Error allocating inode bitmap */
+#define PR_1B_BLOCK_ITERATE 0x0110006 /* Error while iterating over blocks */
+#define PR_1B_ADJ_EA_REFCOUNT 0x0110007 /* Error adjusting EA refcount */
+#define PR_1C_PASS_HEADER 0x012000 /* Pass 1C: Scan directories for inodes with dup blocks. */
+#define PR_1D_PASS_HEADER 0x013000 /* Pass 1D: Reconciling duplicate blocks */
+#define PR_1D_DUP_FILE 0x013001 /* File has duplicate blocks */
+#define PR_1D_DUP_FILE_LIST 0x013002 /* List of files sharing duplicate blocks */
+#define PR_1D_SHARE_METADATA 0x013003 /* File sharing blocks with filesystem metadata */
+#define PR_1D_NUM_DUP_INODES 0x013004 /* Report of how many duplicate/bad inodes */
+#define PR_1D_DUP_BLOCKS_DEALT 0x013005 /* Duplicated blocks already reassigned or cloned. */
+#define PR_1D_CLONE_QUESTION 0x013006 /* Clone duplicate/bad blocks? */
+#define PR_1D_DELETE_QUESTION 0x013007 /* Delete file? */
+#define PR_1D_CLONE_ERROR 0x013008 /* Couldn't clone file (error) */
+
+/*
+ * Pass 2 errors
+ */
+
+#define PR_2_PASS_HEADER 0x020000 /* Pass 2: Checking directory structure */
+#define PR_2_BAD_INODE_DOT 0x020001 /* Bad inode number for '.' */
+#define PR_2_BAD_INO 0x020002 /* Directory entry has bad inode number */
+#define PR_2_UNUSED_INODE 0x020003 /* Directory entry has deleted or unused inode */
+#define PR_2_LINK_DOT 0x020004 /* Directry entry is link to '.' */
+#define PR_2_BB_INODE 0x020005 /* Directory entry points to inode now located in a bad block */
+#define PR_2_LINK_DIR 0x020006 /* Directory entry contains a link to a directory */
+#define PR_2_LINK_ROOT 0x020007 /* Directory entry contains a link to the root directry */
+#define PR_2_BAD_NAME 0x020008 /* Directory entry has illegal characters in its name */
+#define PR_2_MISSING_DOT 0x020009 /* Missing '.' in directory inode */
+#define PR_2_MISSING_DOT_DOT 0x02000A /* Missing '..' in directory inode */
+#define PR_2_1ST_NOT_DOT 0x02000B /* First entry in directory inode doesn't contain '.' */
+#define PR_2_2ND_NOT_DOT_DOT 0x02000C /* Second entry in directory inode doesn't contain '..' */
+#define PR_2_FADDR_ZERO 0x02000D /* i_faddr should be zero */
+#define PR_2_FILE_ACL_ZERO 0x02000E /* i_file_acl should be zero */
+#define PR_2_DIR_ACL_ZERO 0x02000F /* i_dir_acl should be zero */
+#define PR_2_FRAG_ZERO 0x020010 /* i_frag should be zero */
+#define PR_2_FSIZE_ZERO 0x020011 /* i_fsize should be zero */
+#define PR_2_BAD_MODE 0x020012 /* inode has bad mode */
+#define PR_2_DIR_CORRUPTED 0x020013 /* directory corrupted */
+#define PR_2_FILENAME_LONG 0x020014 /* filename too long */
+#define PR_2_DIRECTORY_HOLE 0x020015 /* Directory inode has a missing block (hole) */
+#define PR_2_DOT_NULL_TERM 0x020016 /* '.' is not NULL terminated */
+#define PR_2_DOT_DOT_NULL_TERM 0x020017 /* '..' is not NULL terminated */
+#define PR_2_BAD_CHAR_DEV 0x020018 /* Illegal character device in inode */
+#define PR_2_BAD_BLOCK_DEV 0x020019 /* Illegal block device in inode */
+#define PR_2_DUP_DOT 0x02001A /* Duplicate '.' entry */
+#define PR_2_DUP_DOT_DOT 0x02001B /* Duplicate '..' entry */
+#define PR_2_NO_DIRINFO 0x02001C /* Internal error: couldn't find dir_info */
+#define PR_2_FINAL_RECLEN 0x02001D /* Final rec_len is wrong */
+#define PR_2_ALLOCATE_ICOUNT 0x02001E /* Error allocating icount structure */
+#define PR_2_DBLIST_ITERATE 0x02001F /* Error iterating over directory blocks */
+#define PR_2_READ_DIRBLOCK 0x020020 /* Error reading directory block */
+#define PR_2_WRITE_DIRBLOCK 0x020021 /* Error writing directory block */
+#define PR_2_ALLOC_DIRBOCK 0x020022 /* Error allocating new directory block */
+#define PR_2_DEALLOC_INODE 0x020023 /* Error deallocating inode */
+#define PR_2_SPLIT_DOT 0x020024 /* Directory entry for '.' is big. Split? */
+#define PR_2_BAD_FIFO 0x020025 /* Illegal FIFO */
+#define PR_2_BAD_SOCKET 0x020026 /* Illegal socket */
+#define PR_2_SET_FILETYPE 0x020027 /* Directory filetype not set */
+#define PR_2_BAD_FILETYPE 0x020028 /* Directory filetype incorrect */
+#define PR_2_CLEAR_FILETYPE 0x020029 /* Directory filetype set when it shouldn't be */
+#define PR_2_NULL_NAME 0x020030 /* Directory filename can't be zero-length */
+#define PR_2_INVALID_SYMLINK 0x020031 /* Invalid symlink */
+#define PR_2_FILE_ACL_BAD 0x020032 /* i_file_acl (extended attribute) is bad */
+#define PR_2_FEATURE_LARGE_FILES 0x020033 /* Filesystem contains large files, but has no such flag in sb */
+#define PR_2_HTREE_NOTREF 0x020034 /* Node in HTREE directory not referenced */
+#define PR_2_HTREE_DUPREF 0x020035 /* Node in HTREE directory referenced twice */
+#define PR_2_HTREE_MIN_HASH 0x020036 /* Node in HTREE directory has bad min hash */
+#define PR_2_HTREE_MAX_HASH 0x020037 /* Node in HTREE directory has bad max hash */
+#define PR_2_HTREE_CLEAR 0x020038 /* Clear invalid HTREE directory */
+#define PR_2_HTREE_BADBLK 0x02003A /* Bad block in htree interior node */
+#define PR_2_ADJ_EA_REFCOUNT 0x02003B /* Error adjusting EA refcount */
+#define PR_2_HTREE_BAD_ROOT 0x02003C /* Invalid HTREE root node */
+#define PR_2_HTREE_BAD_LIMIT 0x02003D /* Invalid HTREE limit */
+#define PR_2_HTREE_BAD_COUNT 0x02003E /* Invalid HTREE count */
+#define PR_2_HTREE_HASH_ORDER 0x02003F /* HTREE interior node has out-of-order hashes in table */
+#define PR_2_HTREE_BAD_DEPTH 0x020040 /* Node in HTREE directory has bad depth */
+#define PR_2_DUPLICATE_DIRENT 0x020041 /* Duplicate directory entry found */
+#define PR_2_NON_UNIQUE_FILE 0x020042 /* Non-unique filename found */
+#define PR_2_REPORT_DUP_DIRENT 0x020043 /* Duplicate directory entry found */
+
+/*
+ * Pass 3 errors
+ */
+
+#define PR_3_PASS_HEADER 0x030000 /* Pass 3: Checking directory connectivity */
+#define PR_3_NO_ROOT_INODE 0x030001 /* Root inode not allocated */
+#define PR_3_EXPAND_LF_DIR 0x030002 /* No room in lost+found */
+#define PR_3_UNCONNECTED_DIR 0x030003 /* Unconnected directory inode */
+#define PR_3_NO_LF_DIR 0x030004 /* /lost+found not found */
+#define PR_3_BAD_DOT_DOT 0x030005 /* .. entry is incorrect */
+#define PR_3_NO_LPF 0x030006 /* Bad or non-existent /lost+found. Cannot reconnect */
+#define PR_3_CANT_EXPAND_LPF 0x030007 /* Could not expand /lost+found */
+#define PR_3_CANT_RECONNECT 0x030008 /* Could not reconnect inode */
+#define PR_3_ERR_FIND_LPF 0x030009 /* Error while trying to find /lost+found */
+#define PR_3_ERR_LPF_NEW_BLOCK 0x03000A /* Error in ext2fs_new_block while creating /lost+found */
+#define PR_3_ERR_LPF_NEW_INODE 0x03000B /* Error in ext2fs_new_inode while creating /lost+found */
+#define PR_3_ERR_LPF_NEW_DIR_BLOCK 0x03000C /* Error in ext2fs_new_dir_block while creating /lost+found */
+#define PR_3_ERR_LPF_WRITE_BLOCK 0x03000D /* Error while writing directory block for /lost+found */
+#define PR_3_ADJUST_INODE 0x03000E /* Error while adjusting inode count */
+#define PR_3_FIX_PARENT_ERR 0x03000F /* Couldn't fix parent directory -- error */
+#define PR_3_FIX_PARENT_NOFIND 0x030010 /* Couldn't fix parent directory -- couldn't find it */
+#define PR_3_ALLOCATE_IBITMAP_ERROR 0x030011 /* Error allocating inode bitmap */
+#define PR_3_CREATE_ROOT_ERROR 0x030012 /* Error creating root directory */
+#define PR_3_CREATE_LPF_ERROR 0x030013 /* Error creating lost and found directory */
+#define PR_3_ROOT_NOT_DIR_ABORT 0x030014 /* Root inode is not directory; aborting */
+#define PR_3_NO_ROOT_INODE_ABORT 0x030015 /* Cannot proceed without a root inode. */
+#define PR_3_NO_DIRINFO 0x030016 /* Internal error: couldn't find dir_info */
+#define PR_3_LPF_NOTDIR 0x030017 /* Lost+found is not a directory */
+
+/*
+ * Pass 3a --- rehashing diretories
+ */
+#define PR_3A_PASS_HEADER 0x031000 /* Pass 3a: Reindexing directories */
+#define PR_3A_OPTIMIZE_ITER 0x031001 /* Error iterating over directories */
+#define PR_3A_OPTIMIZE_DIR_ERR 0x031002 /* Error rehash directory */
+#define PR_3A_OPTIMIZE_DIR_HEADER 0x031003 /* Rehashing dir header */
+#define PR_3A_OPTIMIZE_DIR 0x031004 /* Rehashing directory %d */
+#define PR_3A_OPTIMIZE_DIR_END 0x031005 /* Rehashing dir end */
+
+/*
+ * Pass 4 errors
+ */
+
+#define PR_4_PASS_HEADER 0x040000 /* Pass 4: Checking reference counts */
+#define PR_4_ZERO_LEN_INODE 0x040001 /* Unattached zero-length inode */
+#define PR_4_UNATTACHED_INODE 0x040002 /* Unattached inode */
+#define PR_4_BAD_REF_COUNT 0x040003 /* Inode ref count wrong */
+#define PR_4_INCONSISTENT_COUNT 0x040004 /* Inconsistent inode count information cached */
+
+/*
+ * Pass 5 errors
+ */
+
+#define PR_5_PASS_HEADER 0x050000 /* Pass 5: Checking group summary information */
+#define PR_5_INODE_BMAP_PADDING 0x050001 /* Padding at end of inode bitmap is not set. */
+#define PR_5_BLOCK_BMAP_PADDING 0x050002 /* Padding at end of block bitmap is not set. */
+#define PR_5_BLOCK_BITMAP_HEADER 0x050003 /* Block bitmap differences header */
+#define PR_5_BLOCK_UNUSED 0x050004 /* Block not used, but marked in bitmap */
+#define PR_5_BLOCK_USED 0x050005 /* Block used, but not marked used in bitmap */
+#define PR_5_BLOCK_BITMAP_END 0x050006 /* Block bitmap differences end */
+#define PR_5_INODE_BITMAP_HEADER 0x050007 /* Inode bitmap differences header */
+#define PR_5_INODE_UNUSED 0x050008 /* Inode not used, but marked in bitmap */
+#define PR_5_INODE_USED 0x050009 /* Inode used, but not marked used in bitmap */
+#define PR_5_INODE_BITMAP_END 0x05000A /* Inode bitmap differences end */
+#define PR_5_FREE_INODE_COUNT_GROUP 0x05000B /* Free inodes count for group wrong */
+#define PR_5_FREE_DIR_COUNT_GROUP 0x05000C /* Directories count for group wrong */
+#define PR_5_FREE_INODE_COUNT 0x05000D /* Free inodes count wrong */
+#define PR_5_FREE_BLOCK_COUNT_GROUP 0x05000E /* Free blocks count for group wrong */
+#define PR_5_FREE_BLOCK_COUNT 0x05000F /* Free blocks count wrong */
+#define PR_5_BMAP_ENDPOINTS 0x050010 /* Programming error: bitmap endpoints don't match */
+#define PR_5_FUDGE_BITMAP_ERROR 0x050011 /* Internal error: fudging end of bitmap */
+#define PR_5_COPY_IBITMAP_ERROR 0x050012 /* Error copying in replacement inode bitmap */
+#define PR_5_COPY_BBITMAP_ERROR 0x050013 /* Error copying in replacement block bitmap */
+#define PR_5_BLOCK_RANGE_UNUSED 0x050014 /* Block range not used, but marked in bitmap */
+#define PR_5_BLOCK_RANGE_USED 0x050015 /* Block range used, but not marked used in bitmap */
+#define PR_5_INODE_RANGE_UNUSED 0x050016 /* Inode range not used, but marked in bitmap */
+#define PR_5_INODE_RANGE_USED 0x050017 /* Inode rangeused, but not marked used in bitmap */
+
+
+/*
+ * The directory information structure; stores directory information
+ * collected in earlier passes, to avoid disk i/o in fetching the
+ * directory information.
+ */
+struct dir_info {
+ ext2_ino_t ino; /* Inode number */
+ ext2_ino_t dotdot; /* Parent according to '..' */
+ ext2_ino_t parent; /* Parent according to treewalk */
+};
+
+
+
+/*
+ * The indexed directory information structure; stores information for
+ * directories which contain a hash tree index.
+ */
+struct dx_dir_info {
+ ext2_ino_t ino; /* Inode number */
+ int numblocks; /* number of blocks */
+ int hashversion;
+ short depth; /* depth of tree */
+ struct dx_dirblock_info *dx_block; /* Array of size numblocks */
+};
+
+/*
+ * Define the extended attribute refcount structure
+ */
+typedef struct ea_refcount *ext2_refcount_t;
+
+struct e2fsck_struct {
+ ext2_filsys fs;
+ const char *program_name;
+ char *filesystem_name;
+ char *device_name;
+ char *io_options;
+ int flags; /* E2fsck internal flags */
+ int options;
+ blk_t use_superblock; /* sb requested by user */
+ blk_t superblock; /* sb used to open fs */
+ int blocksize; /* blocksize */
+ blk_t num_blocks; /* Total number of blocks */
+ int mount_flags;
+ blkid_cache blkid; /* blkid cache */
+
+ jmp_buf abort_loc;
+
+ unsigned long abort_code;
+
+ int (*progress)(e2fsck_t ctx, int pass, unsigned long cur,
+ unsigned long max);
+
+ ext2fs_inode_bitmap inode_used_map; /* Inodes which are in use */
+ ext2fs_inode_bitmap inode_bad_map; /* Inodes which are bad somehow */
+ ext2fs_inode_bitmap inode_dir_map; /* Inodes which are directories */
+ ext2fs_inode_bitmap inode_imagic_map; /* AFS inodes */
+ ext2fs_inode_bitmap inode_reg_map; /* Inodes which are regular files*/
+
+ ext2fs_block_bitmap block_found_map; /* Blocks which are in use */
+ ext2fs_block_bitmap block_dup_map; /* Blks referenced more than once */
+ ext2fs_block_bitmap block_ea_map; /* Blocks which are used by EA's */
+
+ /*
+ * Inode count arrays
+ */
+ ext2_icount_t inode_count;
+ ext2_icount_t inode_link_info;
+
+ ext2_refcount_t refcount;
+ ext2_refcount_t refcount_extra;
+
+ /*
+ * Array of flags indicating whether an inode bitmap, block
+ * bitmap, or inode table is invalid
+ */
+ int *invalid_inode_bitmap_flag;
+ int *invalid_block_bitmap_flag;
+ int *invalid_inode_table_flag;
+ int invalid_bitmaps; /* There are invalid bitmaps/itable */
+
+ /*
+ * Block buffer
+ */
+ char *block_buf;
+
+ /*
+ * For pass1_check_directory and pass1_get_blocks
+ */
+ ext2_ino_t stashed_ino;
+ struct ext2_inode *stashed_inode;
+
+ /*
+ * Location of the lost and found directory
+ */
+ ext2_ino_t lost_and_found;
+ int bad_lost_and_found;
+
+ /*
+ * Directory information
+ */
+ int dir_info_count;
+ int dir_info_size;
+ struct dir_info *dir_info;
+
+ /*
+ * Indexed directory information
+ */
+ int dx_dir_info_count;
+ int dx_dir_info_size;
+ struct dx_dir_info *dx_dir_info;
+
+ /*
+ * Directories to hash
+ */
+ ext2_u32_list dirs_to_hash;
+
+ /*
+ * Tuning parameters
+ */
+ int process_inode_size;
+ int inode_buffer_blocks;
+
+ /*
+ * ext3 journal support
+ */
+ io_channel journal_io;
+ char *journal_name;
+
+ /*
+ * How we display the progress update (for unix)
+ */
+ int progress_fd;
+ int progress_pos;
+ int progress_last_percent;
+ unsigned int progress_last_time;
+ int interactive; /* Are we connected directly to a tty? */
+ char start_meta[2], stop_meta[2];
+
+ /* File counts */
+ int fs_directory_count;
+ int fs_regular_count;
+ int fs_blockdev_count;
+ int fs_chardev_count;
+ int fs_links_count;
+ int fs_symlinks_count;
+ int fs_fast_symlinks_count;
+ int fs_fifo_count;
+ int fs_total_count;
+ int fs_sockets_count;
+ int fs_ind_count;
+ int fs_dind_count;
+ int fs_tind_count;
+ int fs_fragmented;
+ int large_files;
+ int fs_ext_attr_inodes;
+ int fs_ext_attr_blocks;
+
+ int ext_attr_ver;
+
+ /*
+ * For the use of callers of the e2fsck functions; not used by
+ * e2fsck functions themselves.
+ */
+ void *priv_data;
+};
+
+
+#define tid_gt(x, y) ((x - y) > 0)
+
+static inline int tid_geq(tid_t x, tid_t y)
+{
+ int difference = (x - y);
+ return (difference >= 0);
+}
+
+
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2p/Kbuild b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2p/Kbuild
new file mode 100644
index 0000000..c0ff824
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2p/Kbuild
@@ -0,0 +1,15 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under the GPL v2, see the file LICENSE in this tarball.
+
+NEEDED-$(CONFIG_CHATTR) = y
+NEEDED-$(CONFIG_LSATTR) = y
+NEEDED-$(CONFIG_MKE2FS) = y
+NEEDED-$(CONFIG_TUNE2FS) = y
+
+lib-y:=
+lib-$(NEEDED-y) += fgetsetflags.o fgetsetversion.o pf.o iod.o mntopts.o \
+ feature.o ls.o uuid.o pe.o ostype.o ps.o hashstr.o \
+ parse_num.o
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2p/e2p.h b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2p/e2p.h
new file mode 100644
index 0000000..2a2367b
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2p/e2p.h
@@ -0,0 +1,64 @@
+/* vi: set sw=4 ts=4: */
+#include "busybox.h"
+#include <sys/types.h> /* Needed by dirent.h on netbsd */
+#include <stdio.h>
+#include <dirent.h>
+
+#include "../ext2fs/ext2_fs.h"
+
+#define E2P_FEATURE_COMPAT 0
+#define E2P_FEATURE_INCOMPAT 1
+#define E2P_FEATURE_RO_INCOMPAT 2
+#ifndef EXT3_FEATURE_INCOMPAT_EXTENTS
+#define EXT3_FEATURE_INCOMPAT_EXTENTS 0x0040
+#endif
+
+/* `options' for print_flags() */
+
+#define PFOPT_LONG 1 /* Must be 1 for compatibility with `int long_format'. */
+
+/*int fgetversion (const char * name, unsigned long * version);*/
+/*int fsetversion (const char * name, unsigned long version);*/
+int fgetsetversion(const char * name, unsigned long * get_version, unsigned long set_version);
+#define fgetversion(name, version) fgetsetversion(name, version, 0)
+#define fsetversion(name, version) fgetsetversion(name, NULL, version)
+
+/*int fgetflags (const char * name, unsigned long * flags);*/
+/*int fsetflags (const char * name, unsigned long flags);*/
+int fgetsetflags(const char * name, unsigned long * get_flags, unsigned long set_flags);
+#define fgetflags(name, flags) fgetsetflags(name, flags, 0)
+#define fsetflags(name, flags) fgetsetflags(name, NULL, flags)
+
+int getflags (int fd, unsigned long * flags);
+int getversion (int fd, unsigned long * version);
+int iterate_on_dir (const char * dir_name,
+ int (*func) (const char *, struct dirent *, void *),
+ void * private);
+/*void list_super(struct ext2_super_block * s);*/
+void list_super2(struct ext2_super_block * s, FILE *f);
+#define list_super(s) list_super2(s, stdout)
+void print_fs_errors (FILE * f, unsigned short errors);
+void print_flags (FILE * f, unsigned long flags, unsigned options);
+void print_fs_state (FILE * f, unsigned short state);
+int setflags (int fd, unsigned long flags);
+int setversion (int fd, unsigned long version);
+
+const char *e2p_feature2string(int compat, unsigned int mask);
+int e2p_string2feature(char *string, int *compat, unsigned int *mask);
+int e2p_edit_feature(const char *str, __u32 *compat_array, __u32 *ok_array);
+
+int e2p_is_null_uuid(void *uu);
+void e2p_uuid_to_str(void *uu, char *out);
+const char *e2p_uuid2str(void *uu);
+
+const char *e2p_hash2string(int num);
+int e2p_string2hash(char *string);
+
+const char *e2p_mntopt2string(unsigned int mask);
+int e2p_string2mntopt(char *string, unsigned int *mask);
+int e2p_edit_mntopts(const char *str, __u32 *mntopts, __u32 ok);
+
+unsigned long parse_num_blocks(const char *arg, int log_block_size);
+
+char *e2p_os2string(int os_type);
+int e2p_string2os(char *str);
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2p/feature.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2p/feature.c
new file mode 100644
index 0000000..b45754f
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2p/feature.c
@@ -0,0 +1,187 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * feature.c --- convert between features and strings
+ *
+ * Copyright (C) 1999 Theodore Ts'o <tytso@mit.edu>
+ *
+ * This file can be redistributed under the terms of the GNU Library General
+ * Public License
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include "e2p.h"
+
+struct feature {
+ int compat;
+ unsigned int mask;
+ const char *string;
+};
+
+static const struct feature feature_list[] = {
+ { E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_DIR_PREALLOC,
+ "dir_prealloc" },
+ { E2P_FEATURE_COMPAT, EXT3_FEATURE_COMPAT_HAS_JOURNAL,
+ "has_journal" },
+ { E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_IMAGIC_INODES,
+ "imagic_inodes" },
+ { E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_EXT_ATTR,
+ "ext_attr" },
+ { E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_DIR_INDEX,
+ "dir_index" },
+ { E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_RESIZE_INODE,
+ "resize_inode" },
+ { E2P_FEATURE_RO_INCOMPAT, EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER,
+ "sparse_super" },
+ { E2P_FEATURE_RO_INCOMPAT, EXT2_FEATURE_RO_COMPAT_LARGE_FILE,
+ "large_file" },
+ { E2P_FEATURE_INCOMPAT, EXT2_FEATURE_INCOMPAT_COMPRESSION,
+ "compression" },
+ { E2P_FEATURE_INCOMPAT, EXT2_FEATURE_INCOMPAT_FILETYPE,
+ "filetype" },
+ { E2P_FEATURE_INCOMPAT, EXT3_FEATURE_INCOMPAT_RECOVER,
+ "needs_recovery" },
+ { E2P_FEATURE_INCOMPAT, EXT3_FEATURE_INCOMPAT_JOURNAL_DEV,
+ "journal_dev" },
+ { E2P_FEATURE_INCOMPAT, EXT3_FEATURE_INCOMPAT_EXTENTS,
+ "extents" },
+ { E2P_FEATURE_INCOMPAT, EXT2_FEATURE_INCOMPAT_META_BG,
+ "meta_bg" },
+ { 0, 0, 0 },
+};
+
+const char *e2p_feature2string(int compat, unsigned int mask)
+{
+ const struct feature *f;
+ static char buf[20];
+ char fchar;
+ int fnum;
+
+ for (f = feature_list; f->string; f++) {
+ if ((compat == f->compat) &&
+ (mask == f->mask))
+ return f->string;
+ }
+ switch (compat) {
+ case E2P_FEATURE_COMPAT:
+ fchar = 'C';
+ break;
+ case E2P_FEATURE_INCOMPAT:
+ fchar = 'I';
+ break;
+ case E2P_FEATURE_RO_INCOMPAT:
+ fchar = 'R';
+ break;
+ default:
+ fchar = '?';
+ break;
+ }
+ for (fnum = 0; mask >>= 1; fnum++);
+ sprintf(buf, "FEATURE_%c%d", fchar, fnum);
+ return buf;
+}
+
+int e2p_string2feature(char *string, int *compat_type, unsigned int *mask)
+{
+ const struct feature *f;
+ char *eptr;
+ int num;
+
+ for (f = feature_list; f->string; f++) {
+ if (!strcasecmp(string, f->string)) {
+ *compat_type = f->compat;
+ *mask = f->mask;
+ return 0;
+ }
+ }
+ if (strncasecmp(string, "FEATURE_", 8))
+ return 1;
+
+ switch (string[8]) {
+ case 'c':
+ case 'C':
+ *compat_type = E2P_FEATURE_COMPAT;
+ break;
+ case 'i':
+ case 'I':
+ *compat_type = E2P_FEATURE_INCOMPAT;
+ break;
+ case 'r':
+ case 'R':
+ *compat_type = E2P_FEATURE_RO_INCOMPAT;
+ break;
+ default:
+ return 1;
+ }
+ if (string[9] == 0)
+ return 1;
+ num = strtol(string+9, &eptr, 10);
+ if (num > 32 || num < 0)
+ return 1;
+ if (*eptr)
+ return 1;
+ *mask = 1 << num;
+ return 0;
+}
+
+static inline char *skip_over_blanks(char *cp)
+{
+ while (*cp && isspace(*cp))
+ cp++;
+ return cp;
+}
+
+static inline char *skip_over_word(char *cp)
+{
+ while (*cp && !isspace(*cp) && *cp != ',')
+ cp++;
+ return cp;
+}
+
+/*
+ * Edit a feature set array as requested by the user. The ok_array,
+ * if set, allows the application to limit what features the user is
+ * allowed to set or clear using this function.
+ */
+int e2p_edit_feature(const char *str, __u32 *compat_array, __u32 *ok_array)
+{
+ char *cp, *buf, *next;
+ int neg;
+ unsigned int mask;
+ int compat_type;
+
+ buf = xstrdup(str);
+ cp = buf;
+ while (cp && *cp) {
+ neg = 0;
+ cp = skip_over_blanks(cp);
+ next = skip_over_word(cp);
+ if (*next == 0)
+ next = 0;
+ else
+ *next = 0;
+ switch (*cp) {
+ case '-':
+ case '^':
+ neg++;
+ case '+':
+ cp++;
+ break;
+ }
+ if (e2p_string2feature(cp, &compat_type, &mask))
+ return 1;
+ if (ok_array && !(ok_array[compat_type] & mask))
+ return 1;
+ if (neg)
+ compat_array[compat_type] &= ~mask;
+ else
+ compat_array[compat_type] |= mask;
+ cp = next ? next+1 : 0;
+ }
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2p/fgetsetflags.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2p/fgetsetflags.c
new file mode 100644
index 0000000..008b798
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2p/fgetsetflags.c
@@ -0,0 +1,70 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * fgetflags.c - Get a file flags on an ext2 file system
+ * fsetflags.c - Set a file flags on an ext2 file system
+ *
+ * Copyright (C) 1993, 1994 Remy Card <card@masi.ibp.fr>
+ * Laboratoire MASI, Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ * This file can be redistributed under the terms of the GNU Library General
+ * Public License
+ */
+
+/*
+ * History:
+ * 93/10/30 - Creation
+ */
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef HAVE_EXT2_IOCTLS
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#endif
+
+#include "e2p.h"
+
+#ifdef O_LARGEFILE
+#define OPEN_FLAGS (O_RDONLY|O_NONBLOCK|O_LARGEFILE)
+#else
+#define OPEN_FLAGS (O_RDONLY|O_NONBLOCK)
+#endif
+
+int fgetsetflags (const char * name, unsigned long * get_flags, unsigned long set_flags)
+{
+#ifdef HAVE_EXT2_IOCTLS
+ struct stat buf;
+ int fd, r, f, save_errno = 0;
+
+ if (!stat(name, &buf) &&
+ !S_ISREG(buf.st_mode) && !S_ISDIR(buf.st_mode)) {
+ goto notsupp;
+ }
+ fd = open (name, OPEN_FLAGS);
+ if (fd == -1)
+ return -1;
+ if (!get_flags) {
+ f = (int) set_flags;
+ r = ioctl (fd, EXT2_IOC_SETFLAGS, &f);
+ } else {
+ r = ioctl (fd, EXT2_IOC_GETFLAGS, &f);
+ *get_flags = f;
+ }
+ if (r == -1)
+ save_errno = errno;
+ close (fd);
+ if (save_errno)
+ errno = save_errno;
+ return r;
+notsupp:
+#endif /* HAVE_EXT2_IOCTLS */
+ errno = EOPNOTSUPP;
+ return -1;
+}
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2p/fgetsetversion.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2p/fgetsetversion.c
new file mode 100644
index 0000000..8d79054
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2p/fgetsetversion.c
@@ -0,0 +1,70 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * fgetversion.c - Get a file version on an ext2 file system
+ * fsetversion.c - Set a file version on an ext2 file system
+ *
+ *
+ * Copyright (C) 1993, 1994 Remy Card <card@masi.ibp.fr>
+ * Laboratoire MASI, Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ * This file can be redistributed under the terms of the GNU Library General
+ * Public License
+ */
+
+/*
+ * History:
+ * 93/10/30 - Creation
+ */
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <sys/ioctl.h>
+
+#include "e2p.h"
+
+#ifdef O_LARGEFILE
+#define OPEN_FLAGS (O_RDONLY|O_NONBLOCK|O_LARGEFILE)
+#else
+#define OPEN_FLAGS (O_RDONLY|O_NONBLOCK)
+#endif
+
+/*
+ To do fsetversion: unsigned long *ptr_version must be set to NULL.
+ and unsigned long version must be set to a value
+ To do fgetversion: unsigned long *ptr_version must NOT be set to NULL
+ and unsigned long version is ignored.
+ TITO.
+*/
+
+int fgetsetversion (const char * name, unsigned long * get_version, unsigned long set_version)
+{
+#ifdef HAVE_EXT2_IOCTLS
+ int fd, r, ver, save_errno = 0;
+
+ fd = open (name, OPEN_FLAGS);
+ if (fd == -1)
+ return -1;
+ if (!get_version) {
+ ver = (int) set_version;
+ r = ioctl (fd, EXT2_IOC_SETVERSION, &ver);
+ } else {
+ r = ioctl (fd, EXT2_IOC_GETVERSION, &ver);
+ *get_version = ver;
+ }
+ if (r == -1)
+ save_errno = errno;
+ close (fd);
+ if (save_errno)
+ errno = save_errno;
+ return r;
+#else /* ! HAVE_EXT2_IOCTLS */
+ errno = EOPNOTSUPP;
+ return -1;
+#endif /* ! HAVE_EXT2_IOCTLS */
+}
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2p/hashstr.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2p/hashstr.c
new file mode 100644
index 0000000..697ffad
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2p/hashstr.c
@@ -0,0 +1,70 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * feature.c --- convert between features and strings
+ *
+ * Copyright (C) 1999 Theodore Ts'o <tytso@mit.edu>
+ *
+ * This file can be redistributed under the terms of the GNU Library General
+ * Public License
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include "e2p.h"
+
+struct hash {
+ int num;
+ const char *string;
+};
+
+static const struct hash hash_list[] = {
+ { EXT2_HASH_LEGACY, "legacy" },
+ { EXT2_HASH_HALF_MD4, "half_md4" },
+ { EXT2_HASH_TEA, "tea" },
+ { 0, 0 },
+};
+
+const char *e2p_hash2string(int num)
+{
+ const struct hash *p;
+ static char buf[20];
+
+ for (p = hash_list; p->string; p++) {
+ if (num == p->num)
+ return p->string;
+ }
+ sprintf(buf, "HASHALG_%d", num);
+ return buf;
+}
+
+/*
+ * Returns the hash algorithm, or -1 on error
+ */
+int e2p_string2hash(char *string)
+{
+ const struct hash *p;
+ char *eptr;
+ int num;
+
+ for (p = hash_list; p->string; p++) {
+ if (!strcasecmp(string, p->string)) {
+ return p->num;
+ }
+ }
+ if (strncasecmp(string, "HASHALG_", 8))
+ return -1;
+
+ if (string[8] == 0)
+ return -1;
+ num = strtol(string+8, &eptr, 10);
+ if (num > 255 || num < 0)
+ return -1;
+ if (*eptr)
+ return -1;
+ return num;
+}
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2p/iod.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2p/iod.c
new file mode 100644
index 0000000..23ab8d5
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2p/iod.c
@@ -0,0 +1,52 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * iod.c - Iterate a function on each entry of a directory
+ *
+ * Copyright (C) 1993, 1994 Remy Card <card@masi.ibp.fr>
+ * Laboratoire MASI, Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ * This file can be redistributed under the terms of the GNU Library General
+ * Public License
+ */
+
+/*
+ * History:
+ * 93/10/30 - Creation
+ */
+
+#include "e2p.h"
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+int iterate_on_dir (const char * dir_name,
+ int (*func) (const char *, struct dirent *, void *),
+ void * private)
+{
+ DIR * dir;
+ struct dirent *de, *dep;
+ int max_len, len;
+
+ max_len = PATH_MAX + sizeof(struct dirent);
+ de = xmalloc(max_len+1);
+ memset(de, 0, max_len+1);
+
+ dir = opendir (dir_name);
+ if (dir == NULL) {
+ free(de);
+ return -1;
+ }
+ while ((dep = readdir (dir))) {
+ len = sizeof(struct dirent);
+ if (len < dep->d_reclen)
+ len = dep->d_reclen;
+ if (len > max_len)
+ len = max_len;
+ memcpy(de, dep, len);
+ (*func) (dir_name, de, private);
+ }
+ free(de);
+ closedir(dir);
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2p/ls.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2p/ls.c
new file mode 100644
index 0000000..9d29db6
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2p/ls.c
@@ -0,0 +1,273 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ls.c - List the contents of an ext2fs superblock
+ *
+ * Copyright (C) 1992, 1993, 1994 Remy Card <card@masi.ibp.fr>
+ * Laboratoire MASI, Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ * Copyright (C) 1995, 1996, 1997 Theodore Ts'o <tytso@mit.edu>
+ *
+ * This file can be redistributed under the terms of the GNU Library General
+ * Public License
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <string.h>
+#include <grp.h>
+#include <pwd.h>
+#include <time.h>
+
+#include "e2p.h"
+
+static void print_user(unsigned short uid, FILE *f)
+{
+ struct passwd *pw = getpwuid(uid);
+ fprintf(f, "%u (user %s)\n", uid,
+ (pw == NULL ? "unknown" : pw->pw_name));
+}
+
+static void print_group(unsigned short gid, FILE *f)
+{
+ struct group *gr = getgrgid(gid);
+ fprintf(f, "%u (group %s)\n", gid,
+ (gr == NULL ? "unknown" : gr->gr_name));
+}
+
+#define MONTH_INT (86400 * 30)
+#define WEEK_INT (86400 * 7)
+#define DAY_INT (86400)
+#define HOUR_INT (60 * 60)
+#define MINUTE_INT (60)
+
+static const char *interval_string(unsigned int secs)
+{
+ static char buf[256], tmp[80];
+ int hr, min, num;
+
+ buf[0] = 0;
+
+ if (secs == 0)
+ return "<none>";
+
+ if (secs >= MONTH_INT) {
+ num = secs / MONTH_INT;
+ secs -= num*MONTH_INT;
+ sprintf(buf, "%d month%s", num, (num>1) ? "s" : "");
+ }
+ if (secs >= WEEK_INT) {
+ num = secs / WEEK_INT;
+ secs -= num*WEEK_INT;
+ sprintf(tmp, "%s%d week%s", buf[0] ? ", " : "",
+ num, (num>1) ? "s" : "");
+ strcat(buf, tmp);
+ }
+ if (secs >= DAY_INT) {
+ num = secs / DAY_INT;
+ secs -= num*DAY_INT;
+ sprintf(tmp, "%s%d day%s", buf[0] ? ", " : "",
+ num, (num>1) ? "s" : "");
+ strcat(buf, tmp);
+ }
+ if (secs > 0) {
+ hr = secs / HOUR_INT;
+ secs -= hr*HOUR_INT;
+ min = secs / MINUTE_INT;
+ secs -= min*MINUTE_INT;
+ sprintf(tmp, "%s%d:%02d:%02d", buf[0] ? ", " : "",
+ hr, min, secs);
+ strcat(buf, tmp);
+ }
+ return buf;
+}
+
+static void print_features(struct ext2_super_block * s, FILE *f)
+{
+#ifdef EXT2_DYNAMIC_REV
+ int i, j, printed=0;
+ __u32 *mask = &s->s_feature_compat, m;
+
+ fprintf(f, "Filesystem features: ");
+ for (i=0; i <3; i++,mask++) {
+ for (j=0,m=1; j < 32; j++, m<<=1) {
+ if (*mask & m) {
+ fprintf(f, " %s", e2p_feature2string(i, m));
+ printed++;
+ }
+ }
+ }
+ if (printed == 0)
+ fprintf(f, " (none)");
+ fprintf(f, "\n");
+#endif
+}
+
+static void print_mntopts(struct ext2_super_block * s, FILE *f)
+{
+#ifdef EXT2_DYNAMIC_REV
+ int i, printed=0;
+ __u32 mask = s->s_default_mount_opts, m;
+
+ fprintf(f, "Default mount options: ");
+ if (mask & EXT3_DEFM_JMODE) {
+ fprintf(f, " %s", e2p_mntopt2string(mask & EXT3_DEFM_JMODE));
+ printed++;
+ }
+ for (i=0,m=1; i < 32; i++, m<<=1) {
+ if (m & EXT3_DEFM_JMODE)
+ continue;
+ if (mask & m) {
+ fprintf(f, " %s", e2p_mntopt2string(m));
+ printed++;
+ }
+ }
+ if (printed == 0)
+ fprintf(f, " (none)");
+ fprintf(f, "\n");
+#endif
+}
+
+
+#ifndef EXT2_INODE_SIZE
+#define EXT2_INODE_SIZE(s) sizeof(struct ext2_inode)
+#endif
+
+#ifndef EXT2_GOOD_OLD_REV
+#define EXT2_GOOD_OLD_REV 0
+#endif
+
+void list_super2(struct ext2_super_block * sb, FILE *f)
+{
+ int inode_blocks_per_group;
+ char buf[80], *str;
+ time_t tm;
+
+ inode_blocks_per_group = (((sb->s_inodes_per_group *
+ EXT2_INODE_SIZE(sb)) +
+ EXT2_BLOCK_SIZE(sb) - 1) /
+ EXT2_BLOCK_SIZE(sb));
+ if (sb->s_volume_name[0]) {
+ memset(buf, 0, sizeof(buf));
+ strncpy(buf, sb->s_volume_name, sizeof(sb->s_volume_name));
+ } else
+ strcpy(buf, "<none>");
+ fprintf(f, "Filesystem volume name: %s\n", buf);
+ if (sb->s_last_mounted[0]) {
+ memset(buf, 0, sizeof(buf));
+ strncpy(buf, sb->s_last_mounted, sizeof(sb->s_last_mounted));
+ } else
+ strcpy(buf, "<not available>");
+ fprintf(f,
+ "Last mounted on: %s\n"
+ "Filesystem UUID: %s\n"
+ "Filesystem magic number: 0x%04X\n"
+ "Filesystem revision #: %d",
+ buf, e2p_uuid2str(sb->s_uuid), sb->s_magic, sb->s_rev_level);
+ if (sb->s_rev_level == EXT2_GOOD_OLD_REV) {
+ fprintf(f, " (original)\n");
+#ifdef EXT2_DYNAMIC_REV
+ } else if (sb->s_rev_level == EXT2_DYNAMIC_REV) {
+ fprintf(f, " (dynamic)\n");
+#endif
+ } else
+ fprintf(f, " (unknown)\n");
+ print_features(sb, f);
+ print_mntopts(sb, f);
+ fprintf(f, "Filesystem state: ");
+ print_fs_state (f, sb->s_state);
+ fprintf(f, "\nErrors behavior: ");
+ print_fs_errors(f, sb->s_errors);
+ str = e2p_os2string(sb->s_creator_os);
+ fprintf(f,
+ "\n"
+ "Filesystem OS type: %s\n"
+ "Inode count: %u\n"
+ "Block count: %u\n"
+ "Reserved block count: %u\n"
+ "Free blocks: %u\n"
+ "Free inodes: %u\n"
+ "First block: %u\n"
+ "Block size: %u\n"
+ "Fragment size: %u\n",
+ str, sb->s_inodes_count, sb->s_blocks_count, sb->s_r_blocks_count,
+ sb->s_free_blocks_count, sb->s_free_inodes_count,
+ sb->s_first_data_block, EXT2_BLOCK_SIZE(sb), EXT2_FRAG_SIZE(sb));
+ free(str);
+ if (sb->s_reserved_gdt_blocks)
+ fprintf(f, "Reserved GDT blocks: %u\n",
+ sb->s_reserved_gdt_blocks);
+ fprintf(f,
+ "Blocks per group: %u\n"
+ "Fragments per group: %u\n"
+ "Inodes per group: %u\n"
+ "Inode blocks per group: %u\n",
+ sb->s_blocks_per_group, sb->s_frags_per_group,
+ sb->s_inodes_per_group, inode_blocks_per_group);
+ if (sb->s_first_meta_bg)
+ fprintf(f, "First meta block group: %u\n",
+ sb->s_first_meta_bg);
+ if (sb->s_mkfs_time) {
+ tm = sb->s_mkfs_time;
+ fprintf(f, "Filesystem created: %s", ctime(&tm));
+ }
+ tm = sb->s_mtime;
+ fprintf(f, "Last mount time: %s",
+ sb->s_mtime ? ctime(&tm) : "n/a\n");
+ tm = sb->s_wtime;
+ fprintf(f,
+ "Last write time: %s"
+ "Mount count: %u\n"
+ "Maximum mount count: %d\n",
+ ctime(&tm), sb->s_mnt_count, sb->s_max_mnt_count);
+ tm = sb->s_lastcheck;
+ fprintf(f,
+ "Last checked: %s"
+ "Check interval: %u (%s)\n",
+ ctime(&tm),
+ sb->s_checkinterval, interval_string(sb->s_checkinterval));
+ if (sb->s_checkinterval)
+ {
+ time_t next;
+
+ next = sb->s_lastcheck + sb->s_checkinterval;
+ fprintf(f, "Next check after: %s", ctime(&next));
+ }
+ fprintf(f, "Reserved blocks uid: ");
+ print_user(sb->s_def_resuid, f);
+ fprintf(f, "Reserved blocks gid: ");
+ print_group(sb->s_def_resgid, f);
+ if (sb->s_rev_level >= EXT2_DYNAMIC_REV) {
+ fprintf(f,
+ "First inode: %d\n"
+ "Inode size: %d\n",
+ sb->s_first_ino, sb->s_inode_size);
+ }
+ if (!e2p_is_null_uuid(sb->s_journal_uuid))
+ fprintf(f, "Journal UUID: %s\n",
+ e2p_uuid2str(sb->s_journal_uuid));
+ if (sb->s_journal_inum)
+ fprintf(f, "Journal inode: %u\n",
+ sb->s_journal_inum);
+ if (sb->s_journal_dev)
+ fprintf(f, "Journal device: 0x%04x\n",
+ sb->s_journal_dev);
+ if (sb->s_last_orphan)
+ fprintf(f, "First orphan inode: %u\n",
+ sb->s_last_orphan);
+ if ((sb->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) ||
+ sb->s_def_hash_version)
+ fprintf(f, "Default directory hash: %s\n",
+ e2p_hash2string(sb->s_def_hash_version));
+ if (!e2p_is_null_uuid(sb->s_hash_seed))
+ fprintf(f, "Directory Hash Seed: %s\n",
+ e2p_uuid2str(sb->s_hash_seed));
+ if (sb->s_jnl_backup_type) {
+ fprintf(f, "Journal backup: ");
+ if (sb->s_jnl_backup_type == 1)
+ fprintf(f, "inode blocks\n");
+ else
+ fprintf(f, "type %u\n", sb->s_jnl_backup_type);
+ }
+}
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2p/mntopts.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2p/mntopts.c
new file mode 100644
index 0000000..17c26c4
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2p/mntopts.c
@@ -0,0 +1,134 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * mountopts.c --- convert between default mount options and strings
+ *
+ * Copyright (C) 2002 Theodore Ts'o <tytso@mit.edu>
+ *
+ * This file can be redistributed under the terms of the GNU Library General
+ * Public License
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include "e2p.h"
+
+struct mntopt {
+ unsigned int mask;
+ const char *string;
+};
+
+static const struct mntopt mntopt_list[] = {
+ { EXT2_DEFM_DEBUG, "debug" },
+ { EXT2_DEFM_BSDGROUPS, "bsdgroups" },
+ { EXT2_DEFM_XATTR_USER, "user_xattr" },
+ { EXT2_DEFM_ACL, "acl" },
+ { EXT2_DEFM_UID16, "uid16" },
+ { EXT3_DEFM_JMODE_DATA, "journal_data" },
+ { EXT3_DEFM_JMODE_ORDERED, "journal_data_ordered" },
+ { EXT3_DEFM_JMODE_WBACK, "journal_data_writeback" },
+ { 0, 0 },
+};
+
+const char *e2p_mntopt2string(unsigned int mask)
+{
+ const struct mntopt *f;
+ static char buf[20];
+ int fnum;
+
+ for (f = mntopt_list; f->string; f++) {
+ if (mask == f->mask)
+ return f->string;
+ }
+ for (fnum = 0; mask >>= 1; fnum++);
+ sprintf(buf, "MNTOPT_%d", fnum);
+ return buf;
+}
+
+int e2p_string2mntopt(char *string, unsigned int *mask)
+{
+ const struct mntopt *f;
+ char *eptr;
+ int num;
+
+ for (f = mntopt_list; f->string; f++) {
+ if (!strcasecmp(string, f->string)) {
+ *mask = f->mask;
+ return 0;
+ }
+ }
+ if (strncasecmp(string, "MNTOPT_", 8))
+ return 1;
+
+ if (string[8] == 0)
+ return 1;
+ num = strtol(string+8, &eptr, 10);
+ if (num > 32 || num < 0)
+ return 1;
+ if (*eptr)
+ return 1;
+ *mask = 1 << num;
+ return 0;
+}
+
+static char *skip_over_blanks(char *cp)
+{
+ while (*cp && isspace(*cp))
+ cp++;
+ return cp;
+}
+
+static char *skip_over_word(char *cp)
+{
+ while (*cp && !isspace(*cp) && *cp != ',')
+ cp++;
+ return cp;
+}
+
+/*
+ * Edit a mntopt set array as requested by the user. The ok
+ * parameter, if non-zero, allows the application to limit what
+ * mntopts the user is allowed to set or clear using this function.
+ */
+int e2p_edit_mntopts(const char *str, __u32 *mntopts, __u32 ok)
+{
+ char *cp, *buf, *next;
+ int neg;
+ unsigned int mask;
+
+ buf = xstrdup(str);
+ cp = buf;
+ while (cp && *cp) {
+ neg = 0;
+ cp = skip_over_blanks(cp);
+ next = skip_over_word(cp);
+ if (*next == 0)
+ next = 0;
+ else
+ *next = 0;
+ switch (*cp) {
+ case '-':
+ case '^':
+ neg++;
+ case '+':
+ cp++;
+ break;
+ }
+ if (e2p_string2mntopt(cp, &mask))
+ return 1;
+ if (ok && !(ok & mask))
+ return 1;
+ if (mask & EXT3_DEFM_JMODE)
+ *mntopts &= ~EXT3_DEFM_JMODE;
+ if (neg)
+ *mntopts &= ~mask;
+ else
+ *mntopts |= mask;
+ cp = next ? next+1 : 0;
+ }
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2p/ostype.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2p/ostype.c
new file mode 100644
index 0000000..0e111d4
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2p/ostype.c
@@ -0,0 +1,74 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * getostype.c - Get the Filesystem OS type
+ *
+ * Copyright (C) 2004,2005 Theodore Ts'o <tytso@mit.edu>
+ *
+ * This file can be redistributed under the terms of the GNU Library General
+ * Public License
+ */
+
+#include "e2p.h"
+#include <string.h>
+#include <stdlib.h>
+
+static const char * const os_tab[] =
+ { "Linux",
+ "Hurd",
+ "Masix",
+ "FreeBSD",
+ "Lites",
+ 0 };
+
+/*
+ * Convert an os_type to a string
+ */
+char *e2p_os2string(int os_type)
+{
+ const char *os;
+ char *ret;
+
+ if (os_type <= EXT2_OS_LITES)
+ os = os_tab[os_type];
+ else
+ os = "(unknown os)";
+
+ ret = xstrdup(os);
+ return ret;
+}
+
+/*
+ * Convert an os_type to a string
+ */
+int e2p_string2os(char *str)
+{
+ const char * const *cpp;
+ int i = 0;
+
+ for (cpp = os_tab; *cpp; cpp++, i++) {
+ if (!strcasecmp(str, *cpp))
+ return i;
+ }
+ return -1;
+}
+
+#ifdef TEST_PROGRAM
+int main(int argc, char **argv)
+{
+ char *s;
+ int i, os;
+
+ for (i=0; i <= EXT2_OS_LITES; i++) {
+ s = e2p_os2string(i);
+ os = e2p_string2os(s);
+ printf("%d: %s (%d)\n", i, s, os);
+ if (i != os) {
+ fprintf(stderr, "Failure!\n");
+ exit(1);
+ }
+ }
+ exit(0);
+}
+#endif
+
+
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2p/parse_num.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2p/parse_num.c
new file mode 100644
index 0000000..6db076f
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2p/parse_num.c
@@ -0,0 +1,65 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * parse_num.c - Parse the number of blocks
+ *
+ * Copyright (C) 2004,2005 Theodore Ts'o <tytso@mit.edu>
+ *
+ * This file can be redistributed under the terms of the GNU Library General
+ * Public License
+ */
+
+#include "e2p.h"
+
+#include <stdlib.h>
+
+unsigned long parse_num_blocks(const char *arg, int log_block_size)
+{
+ char *p;
+ unsigned long long num;
+
+ num = strtoull(arg, &p, 0);
+
+ if (p[0] && p[1])
+ return 0;
+
+ switch (*p) { /* Using fall-through logic */
+ case 'T': case 't':
+ num <<= 10;
+ case 'G': case 'g':
+ num <<= 10;
+ case 'M': case 'm':
+ num <<= 10;
+ case 'K': case 'k':
+ num >>= log_block_size;
+ break;
+ case 's':
+ num >>= 1;
+ break;
+ case '\0':
+ break;
+ default:
+ return 0;
+ }
+ return num;
+}
+
+#ifdef DEBUG
+#include <unistd.h>
+#include <stdio.h>
+
+main(int argc, char **argv)
+{
+ unsigned long num;
+ int log_block_size = 0;
+
+ if (argc != 2) {
+ fprintf(stderr, "Usage: %s arg\n", argv[0]);
+ exit(1);
+ }
+
+ num = parse_num_blocks(argv[1], log_block_size);
+
+ printf("Parsed number: %lu\n", num);
+ exit(0);
+}
+#endif
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2p/pe.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2p/pe.c
new file mode 100644
index 0000000..835274b
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2p/pe.c
@@ -0,0 +1,32 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * pe.c - Print a second extended filesystem errors behavior
+ *
+ * Copyright (C) 1992, 1993, 1994 Remy Card <card@masi.ibp.fr>
+ * Laboratoire MASI, Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ * This file can be redistributed under the terms of the GNU Library General
+ * Public License
+ */
+
+/*
+ * History:
+ * 94/01/09 - Creation
+ */
+
+#include <stdio.h>
+
+#include "e2p.h"
+
+void print_fs_errors(FILE *f, unsigned short errors)
+{
+ char *disp = NULL;
+ switch (errors) {
+ case EXT2_ERRORS_CONTINUE: disp = "Continue"; break;
+ case EXT2_ERRORS_RO: disp = "Remount read-only"; break;
+ case EXT2_ERRORS_PANIC: disp = "Panic"; break;
+ default: disp = "Unknown (continue)";
+ }
+ fprintf(f, disp);
+}
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2p/pf.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2p/pf.c
new file mode 100644
index 0000000..55d4bc4
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2p/pf.c
@@ -0,0 +1,74 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * pf.c - Print file attributes on an ext2 file system
+ *
+ * Copyright (C) 1993, 1994 Remy Card <card@masi.ibp.fr>
+ * Laboratoire MASI, Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ * This file can be redistributed under the terms of the GNU Library General
+ * Public License
+ */
+
+/*
+ * History:
+ * 93/10/30 - Creation
+ */
+
+#include <stdio.h>
+
+#include "e2p.h"
+
+struct flags_name {
+ unsigned long flag;
+ const char *short_name;
+ const char *long_name;
+};
+
+static const struct flags_name flags_array[] = {
+ { EXT2_SECRM_FL, "s", "Secure_Deletion" },
+ { EXT2_UNRM_FL, "u" , "Undelete" },
+ { EXT2_SYNC_FL, "S", "Synchronous_Updates" },
+ { EXT2_DIRSYNC_FL, "D", "Synchronous_Directory_Updates" },
+ { EXT2_IMMUTABLE_FL, "i", "Immutable" },
+ { EXT2_APPEND_FL, "a", "Append_Only" },
+ { EXT2_NODUMP_FL, "d", "No_Dump" },
+ { EXT2_NOATIME_FL, "A", "No_Atime" },
+ { EXT2_COMPR_FL, "c", "Compression_Requested" },
+#ifdef ENABLE_COMPRESSION
+ { EXT2_COMPRBLK_FL, "B", "Compressed_File" },
+ { EXT2_DIRTY_FL, "Z", "Compressed_Dirty_File" },
+ { EXT2_NOCOMPR_FL, "X", "Compression_Raw_Access" },
+ { EXT2_ECOMPR_FL, "E", "Compression_Error" },
+#endif
+ { EXT3_JOURNAL_DATA_FL, "j", "Journaled_Data" },
+ { EXT2_INDEX_FL, "I", "Indexed_direcctory" },
+ { EXT2_NOTAIL_FL, "t", "No_Tailmerging" },
+ { EXT2_TOPDIR_FL, "T", "Top_of_Directory_Hierarchies" },
+ { 0, NULL, NULL }
+};
+
+void print_flags (FILE * f, unsigned long flags, unsigned options)
+{
+ int long_opt = (options & PFOPT_LONG);
+ const struct flags_name *fp;
+ int first = 1;
+
+ for (fp = flags_array; fp->flag != 0; fp++) {
+ if (flags & fp->flag) {
+ if (long_opt) {
+ if (first)
+ first = 0;
+ else
+ fputs(", ", f);
+ fputs(fp->long_name, f);
+ } else
+ fputs(fp->short_name, f);
+ } else {
+ if (!long_opt)
+ fputs("-", f);
+ }
+ }
+ if (long_opt && first)
+ fputs("---", f);
+}
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2p/ps.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2p/ps.c
new file mode 100644
index 0000000..a6b4099
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2p/ps.c
@@ -0,0 +1,27 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ps.c - Print filesystem state
+ *
+ * Copyright (C) 1993, 1994 Remy Card <card@masi.ibp.fr>
+ * Laboratoire MASI, Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ * This file can be redistributed under the terms of the GNU Library General
+ * Public License
+ */
+
+/*
+ * History:
+ * 93/12/22 - Creation
+ */
+
+#include <stdio.h>
+
+#include "e2p.h"
+
+void print_fs_state(FILE *f, unsigned short state)
+{
+ fprintf(f, (state & EXT2_VALID_FS ? " clean" : " not clean"));
+ if (state & EXT2_ERROR_FS)
+ fprintf(f, " with errors");
+}
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2p/uuid.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2p/uuid.c
new file mode 100644
index 0000000..474d64a
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/e2p/uuid.c
@@ -0,0 +1,78 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * uuid.c -- utility routines for manipulating UUID's.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "../ext2fs/ext2_types.h"
+
+#include "e2p.h"
+
+struct uuid {
+ __u32 time_low;
+ __u16 time_mid;
+ __u16 time_hi_and_version;
+ __u16 clock_seq;
+ __u8 node[6];
+};
+
+/* Returns 1 if the uuid is the NULL uuid */
+int e2p_is_null_uuid(void *uu)
+{
+ __u8 *cp;
+ int i;
+
+ for (i=0, cp = uu; i < 16; i++)
+ if (*cp)
+ return 0;
+ return 1;
+}
+
+static void e2p_unpack_uuid(void *in, struct uuid *uu)
+{
+ __u8 *ptr = in;
+ __u32 tmp;
+
+ tmp = *ptr++;
+ tmp = (tmp << 8) | *ptr++;
+ tmp = (tmp << 8) | *ptr++;
+ tmp = (tmp << 8) | *ptr++;
+ uu->time_low = tmp;
+
+ tmp = *ptr++;
+ tmp = (tmp << 8) | *ptr++;
+ uu->time_mid = tmp;
+
+ tmp = *ptr++;
+ tmp = (tmp << 8) | *ptr++;
+ uu->time_hi_and_version = tmp;
+
+ tmp = *ptr++;
+ tmp = (tmp << 8) | *ptr++;
+ uu->clock_seq = tmp;
+
+ memcpy(uu->node, ptr, 6);
+}
+
+void e2p_uuid_to_str(void *uu, char *out)
+{
+ struct uuid uuid;
+
+ e2p_unpack_uuid(uu, &uuid);
+ sprintf(out,
+ "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+ uuid.time_low, uuid.time_mid, uuid.time_hi_and_version,
+ uuid.clock_seq >> 8, uuid.clock_seq & 0xFF,
+ uuid.node[0], uuid.node[1], uuid.node[2],
+ uuid.node[3], uuid.node[4], uuid.node[5]);
+}
+
+const char *e2p_uuid2str(void *uu)
+{
+ static char buf[80];
+ if (e2p_is_null_uuid(uu))
+ return "<none>";
+ e2p_uuid_to_str(uu, buf);
+ return buf;
+}
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/Kbuild b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/Kbuild
new file mode 100644
index 0000000..185887a
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/Kbuild
@@ -0,0 +1,23 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under the GPL v2, see the file LICENSE in this tarball.
+
+NEEDED-$(CONFIG_E2FSCK) = y
+NEEDED-$(CONFIG_FSCK) = y
+NEEDED-$(CONFIG_MKE2FS) = y
+NEEDED-$(CONFIG_TUNE2FS) = y
+
+lib-y:=
+lib-$(NEEDED-y) += gen_bitmap.o bitops.o ismounted.o mkjournal.o unix_io.o \
+ rw_bitmaps.o initialize.o bitmaps.o block.o \
+ ind_block.o inode.o freefs.o alloc_stats.o closefs.o \
+ openfs.o io_manager.o finddev.o read_bb.o alloc.o badblocks.o \
+ getsize.o getsectsize.o alloc_tables.o read_bb_file.o mkdir.o \
+ bb_inode.o newdir.o alloc_sb.o lookup.o dirblock.o expanddir.o \
+ dir_iterate.o link.o res_gdt.o icount.o get_pathname.o dblist.o \
+ dirhash.o version.o flushb.o unlink.o check_desc.o valid_blk.o \
+ ext_attr.o bmap.o dblist_dir.o ext2fs_inline.o swapfs.o
+
+CFLAGS += -include $(srctree)/e2fsprogs/e2fsbb.h
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/alloc.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/alloc.c
new file mode 100644
index 0000000..590f23a
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/alloc.c
@@ -0,0 +1,174 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * alloc.c --- allocate new inodes, blocks for ext2fs
+ *
+ * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ *
+ */
+
+#include <stdio.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <time.h>
+#include <string.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+/*
+ * Right now, just search forward from the parent directory's block
+ * group to find the next free inode.
+ *
+ * Should have a special policy for directories.
+ */
+errcode_t ext2fs_new_inode(ext2_filsys fs, ext2_ino_t dir,
+ int mode EXT2FS_ATTR((unused)),
+ ext2fs_inode_bitmap map, ext2_ino_t *ret)
+{
+ ext2_ino_t dir_group = 0;
+ ext2_ino_t i;
+ ext2_ino_t start_inode;
+
+ EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+ if (!map)
+ map = fs->inode_map;
+ if (!map)
+ return EXT2_ET_NO_INODE_BITMAP;
+
+ if (dir > 0)
+ dir_group = (dir - 1) / EXT2_INODES_PER_GROUP(fs->super);
+
+ start_inode = (dir_group * EXT2_INODES_PER_GROUP(fs->super)) + 1;
+ if (start_inode < EXT2_FIRST_INODE(fs->super))
+ start_inode = EXT2_FIRST_INODE(fs->super);
+ i = start_inode;
+
+ do {
+ if (!ext2fs_fast_test_inode_bitmap(map, i))
+ break;
+ i++;
+ if (i > fs->super->s_inodes_count)
+ i = EXT2_FIRST_INODE(fs->super);
+ } while (i != start_inode);
+
+ if (ext2fs_test_inode_bitmap(map, i))
+ return EXT2_ET_INODE_ALLOC_FAIL;
+ *ret = i;
+ return 0;
+}
+
+/*
+ * Stupid algorithm --- we now just search forward starting from the
+ * goal. Should put in a smarter one someday....
+ */
+errcode_t ext2fs_new_block(ext2_filsys fs, blk_t goal,
+ ext2fs_block_bitmap map, blk_t *ret)
+{
+ blk_t i;
+
+ EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+ if (!map)
+ map = fs->block_map;
+ if (!map)
+ return EXT2_ET_NO_BLOCK_BITMAP;
+ if (!goal || (goal >= fs->super->s_blocks_count))
+ goal = fs->super->s_first_data_block;
+ i = goal;
+ do {
+ if (!ext2fs_fast_test_block_bitmap(map, i)) {
+ *ret = i;
+ return 0;
+ }
+ i++;
+ if (i >= fs->super->s_blocks_count)
+ i = fs->super->s_first_data_block;
+ } while (i != goal);
+ return EXT2_ET_BLOCK_ALLOC_FAIL;
+}
+
+/*
+ * This function zeros out the allocated block, and updates all of the
+ * appropriate filesystem records.
+ */
+errcode_t ext2fs_alloc_block(ext2_filsys fs, blk_t goal,
+ char *block_buf, blk_t *ret)
+{
+ errcode_t retval;
+ blk_t block;
+ char *buf = 0;
+
+ if (!block_buf) {
+ retval = ext2fs_get_mem(fs->blocksize, &buf);
+ if (retval)
+ return retval;
+ block_buf = buf;
+ }
+ memset(block_buf, 0, fs->blocksize);
+
+ if (!fs->block_map) {
+ retval = ext2fs_read_block_bitmap(fs);
+ if (retval)
+ goto fail;
+ }
+
+ retval = ext2fs_new_block(fs, goal, 0, &block);
+ if (retval)
+ goto fail;
+
+ retval = io_channel_write_blk(fs->io, block, 1, block_buf);
+ if (retval)
+ goto fail;
+
+ ext2fs_block_alloc_stats(fs, block, +1);
+ *ret = block;
+ return 0;
+
+fail:
+ if (buf)
+ ext2fs_free_mem(&buf);
+ return retval;
+}
+
+errcode_t ext2fs_get_free_blocks(ext2_filsys fs, blk_t start, blk_t finish,
+ int num, ext2fs_block_bitmap map, blk_t *ret)
+{
+ blk_t b = start;
+
+ EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+ if (!map)
+ map = fs->block_map;
+ if (!map)
+ return EXT2_ET_NO_BLOCK_BITMAP;
+ if (!b)
+ b = fs->super->s_first_data_block;
+ if (!finish)
+ finish = start;
+ if (!num)
+ num = 1;
+ do {
+ if (b+num-1 > fs->super->s_blocks_count)
+ b = fs->super->s_first_data_block;
+ if (ext2fs_fast_test_block_bitmap_range(map, b, num)) {
+ *ret = b;
+ return 0;
+ }
+ b++;
+ } while (b != finish);
+ return EXT2_ET_BLOCK_ALLOC_FAIL;
+}
+
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/alloc_sb.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/alloc_sb.c
new file mode 100644
index 0000000..a7437c9
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/alloc_sb.c
@@ -0,0 +1,58 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * alloc_sb.c --- Allocate the superblock and block group descriptors for a
+ * newly initialized filesystem. Used by mke2fs when initializing a filesystem
+ *
+ * Copyright (C) 1994, 1995, 1996, 2003 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+int ext2fs_reserve_super_and_bgd(ext2_filsys fs,
+ dgrp_t group,
+ ext2fs_block_bitmap bmap)
+{
+ blk_t super_blk, old_desc_blk, new_desc_blk;
+ int j, old_desc_blocks, num_blocks;
+
+ num_blocks = ext2fs_super_and_bgd_loc(fs, group, &super_blk,
+ &old_desc_blk, &new_desc_blk, 0);
+
+ if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG)
+ old_desc_blocks = fs->super->s_first_meta_bg;
+ else
+ old_desc_blocks =
+ fs->desc_blocks + fs->super->s_reserved_gdt_blocks;
+
+ if (super_blk || (group == 0))
+ ext2fs_mark_block_bitmap(bmap, super_blk);
+
+ if (old_desc_blk) {
+ for (j=0; j < old_desc_blocks; j++)
+ ext2fs_mark_block_bitmap(bmap, old_desc_blk + j);
+ }
+ if (new_desc_blk)
+ ext2fs_mark_block_bitmap(bmap, new_desc_blk);
+
+ return num_blocks;
+}
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/alloc_stats.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/alloc_stats.c
new file mode 100644
index 0000000..f3ab06a
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/alloc_stats.c
@@ -0,0 +1,53 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * alloc_stats.c --- Update allocation statistics for ext2fs
+ *
+ * Copyright (C) 2001 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ *
+ */
+
+#include <stdio.h>
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+void ext2fs_inode_alloc_stats2(ext2_filsys fs, ext2_ino_t ino,
+ int inuse, int isdir)
+{
+ int group = ext2fs_group_of_ino(fs, ino);
+
+ if (inuse > 0)
+ ext2fs_mark_inode_bitmap(fs->inode_map, ino);
+ else
+ ext2fs_unmark_inode_bitmap(fs->inode_map, ino);
+ fs->group_desc[group].bg_free_inodes_count -= inuse;
+ if (isdir)
+ fs->group_desc[group].bg_used_dirs_count += inuse;
+ fs->super->s_free_inodes_count -= inuse;
+ ext2fs_mark_super_dirty(fs);
+ ext2fs_mark_ib_dirty(fs);
+}
+
+void ext2fs_inode_alloc_stats(ext2_filsys fs, ext2_ino_t ino, int inuse)
+{
+ ext2fs_inode_alloc_stats2(fs, ino, inuse, 0);
+}
+
+void ext2fs_block_alloc_stats(ext2_filsys fs, blk_t blk, int inuse)
+{
+ int group = ext2fs_group_of_blk(fs, blk);
+
+ if (inuse > 0)
+ ext2fs_mark_block_bitmap(fs->block_map, blk);
+ else
+ ext2fs_unmark_block_bitmap(fs->block_map, blk);
+ fs->group_desc[group].bg_free_blocks_count -= inuse;
+ fs->super->s_free_blocks_count -= inuse;
+ ext2fs_mark_super_dirty(fs);
+ ext2fs_mark_bb_dirty(fs);
+}
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/alloc_tables.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/alloc_tables.c
new file mode 100644
index 0000000..b2d786e
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/alloc_tables.c
@@ -0,0 +1,118 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * alloc_tables.c --- Allocate tables for a newly initialized
+ * filesystem. Used by mke2fs when initializing a filesystem
+ *
+ * Copyright (C) 1996 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+errcode_t ext2fs_allocate_group_table(ext2_filsys fs, dgrp_t group,
+ ext2fs_block_bitmap bmap)
+{
+ errcode_t retval;
+ blk_t group_blk, start_blk, last_blk, new_blk, blk;
+ int j;
+
+ group_blk = fs->super->s_first_data_block +
+ (group * fs->super->s_blocks_per_group);
+
+ last_blk = group_blk + fs->super->s_blocks_per_group;
+ if (last_blk >= fs->super->s_blocks_count)
+ last_blk = fs->super->s_blocks_count - 1;
+
+ if (!bmap)
+ bmap = fs->block_map;
+
+ /*
+ * Allocate the block and inode bitmaps, if necessary
+ */
+ if (fs->stride) {
+ start_blk = group_blk + fs->inode_blocks_per_group;
+ start_blk += ((fs->stride * group) %
+ (last_blk - start_blk));
+ if (start_blk > last_blk)
+ start_blk = group_blk;
+ } else
+ start_blk = group_blk;
+
+ if (!fs->group_desc[group].bg_block_bitmap) {
+ retval = ext2fs_get_free_blocks(fs, start_blk, last_blk,
+ 1, bmap, &new_blk);
+ if (retval == EXT2_ET_BLOCK_ALLOC_FAIL)
+ retval = ext2fs_get_free_blocks(fs, group_blk,
+ last_blk, 1, bmap, &new_blk);
+ if (retval)
+ return retval;
+ ext2fs_mark_block_bitmap(bmap, new_blk);
+ fs->group_desc[group].bg_block_bitmap = new_blk;
+ }
+
+ if (!fs->group_desc[group].bg_inode_bitmap) {
+ retval = ext2fs_get_free_blocks(fs, start_blk, last_blk,
+ 1, bmap, &new_blk);
+ if (retval == EXT2_ET_BLOCK_ALLOC_FAIL)
+ retval = ext2fs_get_free_blocks(fs, group_blk,
+ last_blk, 1, bmap, &new_blk);
+ if (retval)
+ return retval;
+ ext2fs_mark_block_bitmap(bmap, new_blk);
+ fs->group_desc[group].bg_inode_bitmap = new_blk;
+ }
+
+ /*
+ * Allocate the inode table
+ */
+ if (!fs->group_desc[group].bg_inode_table) {
+ retval = ext2fs_get_free_blocks(fs, group_blk, last_blk,
+ fs->inode_blocks_per_group,
+ bmap, &new_blk);
+ if (retval)
+ return retval;
+ for (j=0, blk = new_blk;
+ j < fs->inode_blocks_per_group;
+ j++, blk++)
+ ext2fs_mark_block_bitmap(bmap, blk);
+ fs->group_desc[group].bg_inode_table = new_blk;
+ }
+
+
+ return 0;
+}
+
+
+
+errcode_t ext2fs_allocate_tables(ext2_filsys fs)
+{
+ errcode_t retval;
+ dgrp_t i;
+
+ for (i = 0; i < fs->group_desc_count; i++) {
+ retval = ext2fs_allocate_group_table(fs, i, fs->block_map);
+ if (retval)
+ return retval;
+ }
+ return 0;
+}
+
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/badblocks.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/badblocks.c
new file mode 100644
index 0000000..7804396
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/badblocks.c
@@ -0,0 +1,328 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * badblocks.c --- routines to manipulate the bad block structure
+ *
+ * Copyright (C) 1994, 1995, 1996 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fsP.h"
+
+/*
+ * Helper function for making a badblocks list
+ */
+static errcode_t make_u32_list(int size, int num, __u32 *list,
+ ext2_u32_list *ret)
+{
+ ext2_u32_list bb;
+ errcode_t retval;
+
+ retval = ext2fs_get_mem(sizeof(struct ext2_struct_u32_list), &bb);
+ if (retval)
+ return retval;
+ memset(bb, 0, sizeof(struct ext2_struct_u32_list));
+ bb->magic = EXT2_ET_MAGIC_BADBLOCKS_LIST;
+ bb->size = size ? size : 10;
+ bb->num = num;
+ retval = ext2fs_get_mem(bb->size * sizeof(blk_t), &bb->list);
+ if (!bb->list) {
+ ext2fs_free_mem(&bb);
+ return retval;
+ }
+ if (list)
+ memcpy(bb->list, list, bb->size * sizeof(blk_t));
+ else
+ memset(bb->list, 0, bb->size * sizeof(blk_t));
+ *ret = bb;
+ return 0;
+}
+
+
+/*
+ * This procedure creates an empty u32 list.
+ */
+errcode_t ext2fs_u32_list_create(ext2_u32_list *ret, int size)
+{
+ return make_u32_list(size, 0, 0, ret);
+}
+
+/*
+ * This procedure creates an empty badblocks list.
+ */
+errcode_t ext2fs_badblocks_list_create(ext2_badblocks_list *ret, int size)
+{
+ return make_u32_list(size, 0, 0, (ext2_badblocks_list *) ret);
+}
+
+
+/*
+ * This procedure copies a badblocks list
+ */
+errcode_t ext2fs_u32_copy(ext2_u32_list src, ext2_u32_list *dest)
+{
+ errcode_t retval;
+
+ retval = make_u32_list(src->size, src->num, src->list, dest);
+ if (retval)
+ return retval;
+ (*dest)->badblocks_flags = src->badblocks_flags;
+ return 0;
+}
+
+errcode_t ext2fs_badblocks_copy(ext2_badblocks_list src,
+ ext2_badblocks_list *dest)
+{
+ return ext2fs_u32_copy((ext2_u32_list) src,
+ (ext2_u32_list *) dest);
+}
+
+/*
+ * This procedure frees a badblocks list.
+ *
+ * (note: moved to closefs.c)
+ */
+
+
+/*
+ * This procedure adds a block to a badblocks list.
+ */
+errcode_t ext2fs_u32_list_add(ext2_u32_list bb, __u32 blk)
+{
+ errcode_t retval;
+ int i, j;
+ unsigned long old_size;
+
+ EXT2_CHECK_MAGIC(bb, EXT2_ET_MAGIC_BADBLOCKS_LIST);
+
+ if (bb->num >= bb->size) {
+ old_size = bb->size * sizeof(__u32);
+ bb->size += 100;
+ retval = ext2fs_resize_mem(old_size, bb->size * sizeof(__u32),
+ &bb->list);
+ if (retval) {
+ bb->size -= 100;
+ return retval;
+ }
+ }
+
+ /*
+ * Add special case code for appending to the end of the list
+ */
+ i = bb->num-1;
+ if ((bb->num != 0) && (bb->list[i] == blk))
+ return 0;
+ if ((bb->num == 0) || (bb->list[i] < blk)) {
+ bb->list[bb->num++] = blk;
+ return 0;
+ }
+
+ j = bb->num;
+ for (i=0; i < bb->num; i++) {
+ if (bb->list[i] == blk)
+ return 0;
+ if (bb->list[i] > blk) {
+ j = i;
+ break;
+ }
+ }
+ for (i=bb->num; i > j; i--)
+ bb->list[i] = bb->list[i-1];
+ bb->list[j] = blk;
+ bb->num++;
+ return 0;
+}
+
+errcode_t ext2fs_badblocks_list_add(ext2_badblocks_list bb, blk_t blk)
+{
+ return ext2fs_u32_list_add((ext2_u32_list) bb, (__u32) blk);
+}
+
+/*
+ * This procedure finds a particular block is on a badblocks
+ * list.
+ */
+int ext2fs_u32_list_find(ext2_u32_list bb, __u32 blk)
+{
+ int low, high, mid;
+
+ if (bb->magic != EXT2_ET_MAGIC_BADBLOCKS_LIST)
+ return -1;
+
+ if (bb->num == 0)
+ return -1;
+
+ low = 0;
+ high = bb->num-1;
+ if (blk == bb->list[low])
+ return low;
+ if (blk == bb->list[high])
+ return high;
+
+ while (low < high) {
+ mid = (low+high)/2;
+ if (mid == low || mid == high)
+ break;
+ if (blk == bb->list[mid])
+ return mid;
+ if (blk < bb->list[mid])
+ high = mid;
+ else
+ low = mid;
+ }
+ return -1;
+}
+
+/*
+ * This procedure tests to see if a particular block is on a badblocks
+ * list.
+ */
+int ext2fs_u32_list_test(ext2_u32_list bb, __u32 blk)
+{
+ if (ext2fs_u32_list_find(bb, blk) < 0)
+ return 0;
+ else
+ return 1;
+}
+
+int ext2fs_badblocks_list_test(ext2_badblocks_list bb, blk_t blk)
+{
+ return ext2fs_u32_list_test((ext2_u32_list) bb, (__u32) blk);
+}
+
+
+/*
+ * Remove a block from the badblock list
+ */
+int ext2fs_u32_list_del(ext2_u32_list bb, __u32 blk)
+{
+ int remloc, i;
+
+ if (bb->num == 0)
+ return -1;
+
+ remloc = ext2fs_u32_list_find(bb, blk);
+ if (remloc < 0)
+ return -1;
+
+ for (i = remloc ; i < bb->num-1; i++)
+ bb->list[i] = bb->list[i+1];
+ bb->num--;
+ return 0;
+}
+
+void ext2fs_badblocks_list_del(ext2_u32_list bb, __u32 blk)
+{
+ ext2fs_u32_list_del(bb, blk);
+}
+
+errcode_t ext2fs_u32_list_iterate_begin(ext2_u32_list bb,
+ ext2_u32_iterate *ret)
+{
+ ext2_u32_iterate iter;
+ errcode_t retval;
+
+ EXT2_CHECK_MAGIC(bb, EXT2_ET_MAGIC_BADBLOCKS_LIST);
+
+ retval = ext2fs_get_mem(sizeof(struct ext2_struct_u32_iterate), &iter);
+ if (retval)
+ return retval;
+
+ iter->magic = EXT2_ET_MAGIC_BADBLOCKS_ITERATE;
+ iter->bb = bb;
+ iter->ptr = 0;
+ *ret = iter;
+ return 0;
+}
+
+errcode_t ext2fs_badblocks_list_iterate_begin(ext2_badblocks_list bb,
+ ext2_badblocks_iterate *ret)
+{
+ return ext2fs_u32_list_iterate_begin((ext2_u32_list) bb,
+ (ext2_u32_iterate *) ret);
+}
+
+
+int ext2fs_u32_list_iterate(ext2_u32_iterate iter, __u32 *blk)
+{
+ ext2_u32_list bb;
+
+ if (iter->magic != EXT2_ET_MAGIC_BADBLOCKS_ITERATE)
+ return 0;
+
+ bb = iter->bb;
+
+ if (bb->magic != EXT2_ET_MAGIC_BADBLOCKS_LIST)
+ return 0;
+
+ if (iter->ptr < bb->num) {
+ *blk = bb->list[iter->ptr++];
+ return 1;
+ }
+ *blk = 0;
+ return 0;
+}
+
+int ext2fs_badblocks_list_iterate(ext2_badblocks_iterate iter, blk_t *blk)
+{
+ return ext2fs_u32_list_iterate((ext2_u32_iterate) iter,
+ (__u32 *) blk);
+}
+
+
+void ext2fs_u32_list_iterate_end(ext2_u32_iterate iter)
+{
+ if (!iter || (iter->magic != EXT2_ET_MAGIC_BADBLOCKS_ITERATE))
+ return;
+
+ iter->bb = 0;
+ ext2fs_free_mem(&iter);
+}
+
+void ext2fs_badblocks_list_iterate_end(ext2_badblocks_iterate iter)
+{
+ ext2fs_u32_list_iterate_end((ext2_u32_iterate) iter);
+}
+
+
+int ext2fs_u32_list_equal(ext2_u32_list bb1, ext2_u32_list bb2)
+{
+ EXT2_CHECK_MAGIC(bb1, EXT2_ET_MAGIC_BADBLOCKS_LIST);
+ EXT2_CHECK_MAGIC(bb2, EXT2_ET_MAGIC_BADBLOCKS_LIST);
+
+ if (bb1->num != bb2->num)
+ return 0;
+
+ if (memcmp(bb1->list, bb2->list, bb1->num * sizeof(blk_t)) != 0)
+ return 0;
+ return 1;
+}
+
+int ext2fs_badblocks_equal(ext2_badblocks_list bb1, ext2_badblocks_list bb2)
+{
+ return ext2fs_u32_list_equal((ext2_u32_list) bb1,
+ (ext2_u32_list) bb2);
+}
+
+int ext2fs_u32_list_count(ext2_u32_list bb)
+{
+ return bb->num;
+}
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/bb_compat.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/bb_compat.c
new file mode 100644
index 0000000..419ac77
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/bb_compat.c
@@ -0,0 +1,64 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * bb_compat.c --- compatibility badblocks routines
+ *
+ * Copyright (C) 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fsP.h"
+
+errcode_t badblocks_list_create(badblocks_list *ret, int size)
+{
+ return ext2fs_badblocks_list_create(ret, size);
+}
+
+void badblocks_list_free(badblocks_list bb)
+{
+ ext2fs_badblocks_list_free(bb);
+}
+
+errcode_t badblocks_list_add(badblocks_list bb, blk_t blk)
+{
+ return ext2fs_badblocks_list_add(bb, blk);
+}
+
+int badblocks_list_test(badblocks_list bb, blk_t blk)
+{
+ return ext2fs_badblocks_list_test(bb, blk);
+}
+
+errcode_t badblocks_list_iterate_begin(badblocks_list bb,
+ badblocks_iterate *ret)
+{
+ return ext2fs_badblocks_list_iterate_begin(bb, ret);
+}
+
+int badblocks_list_iterate(badblocks_iterate iter, blk_t *blk)
+{
+ return ext2fs_badblocks_list_iterate(iter, blk);
+}
+
+void badblocks_list_iterate_end(badblocks_iterate iter)
+{
+ ext2fs_badblocks_list_iterate_end(iter);
+}
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/bb_inode.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/bb_inode.c
new file mode 100644
index 0000000..855f86e
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/bb_inode.c
@@ -0,0 +1,268 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * bb_inode.c --- routines to update the bad block inode.
+ *
+ * WARNING: This routine modifies a lot of state in the filesystem; if
+ * this routine returns an error, the bad block inode may be in an
+ * inconsistent state.
+ *
+ * Copyright (C) 1994, 1995 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+struct set_badblock_record {
+ ext2_badblocks_iterate bb_iter;
+ int bad_block_count;
+ blk_t *ind_blocks;
+ int max_ind_blocks;
+ int ind_blocks_size;
+ int ind_blocks_ptr;
+ char *block_buf;
+ errcode_t err;
+};
+
+static int set_bad_block_proc(ext2_filsys fs, blk_t *block_nr,
+ e2_blkcnt_t blockcnt,
+ blk_t ref_block, int ref_offset,
+ void *priv_data);
+static int clear_bad_block_proc(ext2_filsys fs, blk_t *block_nr,
+ e2_blkcnt_t blockcnt,
+ blk_t ref_block, int ref_offset,
+ void *priv_data);
+
+/*
+ * Given a bad blocks bitmap, update the bad blocks inode to reflect
+ * the map.
+ */
+errcode_t ext2fs_update_bb_inode(ext2_filsys fs, ext2_badblocks_list bb_list)
+{
+ errcode_t retval;
+ struct set_badblock_record rec;
+ struct ext2_inode inode;
+
+ EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+ if (!fs->block_map)
+ return EXT2_ET_NO_BLOCK_BITMAP;
+
+ rec.bad_block_count = 0;
+ rec.ind_blocks_size = rec.ind_blocks_ptr = 0;
+ rec.max_ind_blocks = 10;
+ retval = ext2fs_get_mem(rec.max_ind_blocks * sizeof(blk_t),
+ &rec.ind_blocks);
+ if (retval)
+ return retval;
+ memset(rec.ind_blocks, 0, rec.max_ind_blocks * sizeof(blk_t));
+ retval = ext2fs_get_mem(fs->blocksize, &rec.block_buf);
+ if (retval)
+ goto cleanup;
+ memset(rec.block_buf, 0, fs->blocksize);
+ rec.err = 0;
+
+ /*
+ * First clear the old bad blocks (while saving the indirect blocks)
+ */
+ retval = ext2fs_block_iterate2(fs, EXT2_BAD_INO,
+ BLOCK_FLAG_DEPTH_TRAVERSE, 0,
+ clear_bad_block_proc, &rec);
+ if (retval)
+ goto cleanup;
+ if (rec.err) {
+ retval = rec.err;
+ goto cleanup;
+ }
+
+ /*
+ * Now set the bad blocks!
+ *
+ * First, mark the bad blocks as used. This prevents a bad
+ * block from being used as an indirecto block for the bad
+ * block inode (!).
+ */
+ if (bb_list) {
+ retval = ext2fs_badblocks_list_iterate_begin(bb_list,
+ &rec.bb_iter);
+ if (retval)
+ goto cleanup;
+ retval = ext2fs_block_iterate2(fs, EXT2_BAD_INO,
+ BLOCK_FLAG_APPEND, 0,
+ set_bad_block_proc, &rec);
+ ext2fs_badblocks_list_iterate_end(rec.bb_iter);
+ if (retval)
+ goto cleanup;
+ if (rec.err) {
+ retval = rec.err;
+ goto cleanup;
+ }
+ }
+
+ /*
+ * Update the bad block inode's mod time and block count
+ * field.
+ */
+ retval = ext2fs_read_inode(fs, EXT2_BAD_INO, &inode);
+ if (retval)
+ goto cleanup;
+
+ inode.i_atime = inode.i_mtime = time(0);
+ if (!inode.i_ctime)
+ inode.i_ctime = time(0);
+ inode.i_blocks = rec.bad_block_count * (fs->blocksize / 512);
+ inode.i_size = rec.bad_block_count * fs->blocksize;
+
+ retval = ext2fs_write_inode(fs, EXT2_BAD_INO, &inode);
+ if (retval)
+ goto cleanup;
+
+cleanup:
+ ext2fs_free_mem(&rec.ind_blocks);
+ ext2fs_free_mem(&rec.block_buf);
+ return retval;
+}
+
+/*
+ * Helper function for update_bb_inode()
+ *
+ * Clear the bad blocks in the bad block inode, while saving the
+ * indirect blocks.
+ */
+#ifdef __TURBOC__
+# pragma argsused
+#endif
+static int clear_bad_block_proc(ext2_filsys fs, blk_t *block_nr,
+ e2_blkcnt_t blockcnt,
+ blk_t ref_block EXT2FS_ATTR((unused)),
+ int ref_offset EXT2FS_ATTR((unused)),
+ void *priv_data)
+{
+ struct set_badblock_record *rec = (struct set_badblock_record *)
+ priv_data;
+ errcode_t retval;
+ unsigned long old_size;
+
+ if (!*block_nr)
+ return 0;
+
+ /*
+ * If the block number is outrageous, clear it and ignore it.
+ */
+ if (*block_nr >= fs->super->s_blocks_count ||
+ *block_nr < fs->super->s_first_data_block) {
+ *block_nr = 0;
+ return BLOCK_CHANGED;
+ }
+
+ if (blockcnt < 0) {
+ if (rec->ind_blocks_size >= rec->max_ind_blocks) {
+ old_size = rec->max_ind_blocks * sizeof(blk_t);
+ rec->max_ind_blocks += 10;
+ retval = ext2fs_resize_mem(old_size,
+ rec->max_ind_blocks * sizeof(blk_t),
+ &rec->ind_blocks);
+ if (retval) {
+ rec->max_ind_blocks -= 10;
+ rec->err = retval;
+ return BLOCK_ABORT;
+ }
+ }
+ rec->ind_blocks[rec->ind_blocks_size++] = *block_nr;
+ }
+
+ /*
+ * Mark the block as unused, and update accounting information
+ */
+ ext2fs_block_alloc_stats(fs, *block_nr, -1);
+
+ *block_nr = 0;
+ return BLOCK_CHANGED;
+}
+
+
+/*
+ * Helper function for update_bb_inode()
+ *
+ * Set the block list in the bad block inode, using the supplied bitmap.
+ */
+#ifdef __TURBOC__
+ #pragma argsused
+#endif
+static int set_bad_block_proc(ext2_filsys fs, blk_t *block_nr,
+ e2_blkcnt_t blockcnt,
+ blk_t ref_block EXT2FS_ATTR((unused)),
+ int ref_offset EXT2FS_ATTR((unused)),
+ void *priv_data)
+{
+ struct set_badblock_record *rec = (struct set_badblock_record *)
+ priv_data;
+ errcode_t retval;
+ blk_t blk;
+
+ if (blockcnt >= 0) {
+ /*
+ * Get the next bad block.
+ */
+ if (!ext2fs_badblocks_list_iterate(rec->bb_iter, &blk))
+ return BLOCK_ABORT;
+ rec->bad_block_count++;
+ } else {
+ /*
+ * An indirect block; fetch a block from the
+ * previously used indirect block list. The block
+ * most be not marked as used; if so, get another one.
+ * If we run out of reserved indirect blocks, allocate
+ * a new one.
+ */
+ retry:
+ if (rec->ind_blocks_ptr < rec->ind_blocks_size) {
+ blk = rec->ind_blocks[rec->ind_blocks_ptr++];
+ if (ext2fs_test_block_bitmap(fs->block_map, blk))
+ goto retry;
+ } else {
+ retval = ext2fs_new_block(fs, 0, 0, &blk);
+ if (retval) {
+ rec->err = retval;
+ return BLOCK_ABORT;
+ }
+ }
+ retval = io_channel_write_blk(fs->io, blk, 1, rec->block_buf);
+ if (retval) {
+ rec->err = retval;
+ return BLOCK_ABORT;
+ }
+ }
+
+ /*
+ * Update block counts
+ */
+ ext2fs_block_alloc_stats(fs, blk, +1);
+
+ *block_nr = blk;
+ return BLOCK_CHANGED;
+}
+
+
+
+
+
+
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/bitmaps.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/bitmaps.c
new file mode 100644
index 0000000..637ed27
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/bitmaps.c
@@ -0,0 +1,211 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * bitmaps.c --- routines to read, write, and manipulate the inode and
+ * block bitmaps.
+ *
+ * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+static errcode_t make_bitmap(__u32 start, __u32 end, __u32 real_end,
+ const char *descr, char *init_map,
+ ext2fs_generic_bitmap *ret)
+{
+ ext2fs_generic_bitmap bitmap;
+ errcode_t retval;
+ size_t size;
+
+ retval = ext2fs_get_mem(sizeof(struct ext2fs_struct_generic_bitmap),
+ &bitmap);
+ if (retval)
+ return retval;
+
+ bitmap->magic = EXT2_ET_MAGIC_GENERIC_BITMAP;
+ bitmap->fs = NULL;
+ bitmap->start = start;
+ bitmap->end = end;
+ bitmap->real_end = real_end;
+ bitmap->base_error_code = EXT2_ET_BAD_GENERIC_MARK;
+ if (descr) {
+ retval = ext2fs_get_mem(strlen(descr)+1, &bitmap->description);
+ if (retval) {
+ ext2fs_free_mem(&bitmap);
+ return retval;
+ }
+ strcpy(bitmap->description, descr);
+ } else
+ bitmap->description = 0;
+
+ size = (size_t) (((bitmap->real_end - bitmap->start) / 8) + 1);
+ retval = ext2fs_get_mem(size, &bitmap->bitmap);
+ if (retval) {
+ ext2fs_free_mem(&bitmap->description);
+ ext2fs_free_mem(&bitmap);
+ return retval;
+ }
+
+ if (init_map)
+ memcpy(bitmap->bitmap, init_map, size);
+ else
+ memset(bitmap->bitmap, 0, size);
+ *ret = bitmap;
+ return 0;
+}
+
+errcode_t ext2fs_allocate_generic_bitmap(__u32 start,
+ __u32 end,
+ __u32 real_end,
+ const char *descr,
+ ext2fs_generic_bitmap *ret)
+{
+ return make_bitmap(start, end, real_end, descr, 0, ret);
+}
+
+errcode_t ext2fs_copy_bitmap(ext2fs_generic_bitmap src,
+ ext2fs_generic_bitmap *dest)
+{
+ errcode_t retval;
+ ext2fs_generic_bitmap new_map;
+
+ retval = make_bitmap(src->start, src->end, src->real_end,
+ src->description, src->bitmap, &new_map);
+ if (retval)
+ return retval;
+ new_map->magic = src->magic;
+ new_map->fs = src->fs;
+ new_map->base_error_code = src->base_error_code;
+ *dest = new_map;
+ return 0;
+}
+
+void ext2fs_set_bitmap_padding(ext2fs_generic_bitmap map)
+{
+ __u32 i, j;
+
+ for (i=map->end+1, j = i - map->start; i <= map->real_end; i++, j++)
+ ext2fs_set_bit(j, map->bitmap);
+}
+
+errcode_t ext2fs_allocate_inode_bitmap(ext2_filsys fs,
+ const char *descr,
+ ext2fs_inode_bitmap *ret)
+{
+ ext2fs_inode_bitmap bitmap;
+ errcode_t retval;
+ __u32 start, end, real_end;
+
+ EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+ fs->write_bitmaps = ext2fs_write_bitmaps;
+
+ start = 1;
+ end = fs->super->s_inodes_count;
+ real_end = (EXT2_INODES_PER_GROUP(fs->super) * fs->group_desc_count);
+
+ retval = ext2fs_allocate_generic_bitmap(start, end, real_end,
+ descr, &bitmap);
+ if (retval)
+ return retval;
+
+ bitmap->magic = EXT2_ET_MAGIC_INODE_BITMAP;
+ bitmap->fs = fs;
+ bitmap->base_error_code = EXT2_ET_BAD_INODE_MARK;
+
+ *ret = bitmap;
+ return 0;
+}
+
+errcode_t ext2fs_allocate_block_bitmap(ext2_filsys fs,
+ const char *descr,
+ ext2fs_block_bitmap *ret)
+{
+ ext2fs_block_bitmap bitmap;
+ errcode_t retval;
+ __u32 start, end, real_end;
+
+ EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+ fs->write_bitmaps = ext2fs_write_bitmaps;
+
+ start = fs->super->s_first_data_block;
+ end = fs->super->s_blocks_count-1;
+ real_end = (EXT2_BLOCKS_PER_GROUP(fs->super)
+ * fs->group_desc_count)-1 + start;
+
+ retval = ext2fs_allocate_generic_bitmap(start, end, real_end,
+ descr, &bitmap);
+ if (retval)
+ return retval;
+
+ bitmap->magic = EXT2_ET_MAGIC_BLOCK_BITMAP;
+ bitmap->fs = fs;
+ bitmap->base_error_code = EXT2_ET_BAD_BLOCK_MARK;
+
+ *ret = bitmap;
+ return 0;
+}
+
+errcode_t ext2fs_fudge_inode_bitmap_end(ext2fs_inode_bitmap bitmap,
+ ext2_ino_t end, ext2_ino_t *oend)
+{
+ EXT2_CHECK_MAGIC(bitmap, EXT2_ET_MAGIC_INODE_BITMAP);
+
+ if (end > bitmap->real_end)
+ return EXT2_ET_FUDGE_INODE_BITMAP_END;
+ if (oend)
+ *oend = bitmap->end;
+ bitmap->end = end;
+ return 0;
+}
+
+errcode_t ext2fs_fudge_block_bitmap_end(ext2fs_block_bitmap bitmap,
+ blk_t end, blk_t *oend)
+{
+ EXT2_CHECK_MAGIC(bitmap, EXT2_ET_MAGIC_BLOCK_BITMAP);
+
+ if (end > bitmap->real_end)
+ return EXT2_ET_FUDGE_BLOCK_BITMAP_END;
+ if (oend)
+ *oend = bitmap->end;
+ bitmap->end = end;
+ return 0;
+}
+
+void ext2fs_clear_inode_bitmap(ext2fs_inode_bitmap bitmap)
+{
+ if (!bitmap || (bitmap->magic != EXT2_ET_MAGIC_INODE_BITMAP))
+ return;
+
+ memset(bitmap->bitmap, 0,
+ (size_t) (((bitmap->real_end - bitmap->start) / 8) + 1));
+}
+
+void ext2fs_clear_block_bitmap(ext2fs_block_bitmap bitmap)
+{
+ if (!bitmap || (bitmap->magic != EXT2_ET_MAGIC_BLOCK_BITMAP))
+ return;
+
+ memset(bitmap->bitmap, 0,
+ (size_t) (((bitmap->real_end - bitmap->start) / 8) + 1));
+}
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/bitops.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/bitops.c
new file mode 100644
index 0000000..9870611
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/bitops.c
@@ -0,0 +1,91 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * bitops.c --- Bitmap frobbing code. See bitops.h for the inlined
+ * routines.
+ *
+ * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+#ifndef _EXT2_HAVE_ASM_BITOPS_
+
+/*
+ * For the benefit of those who are trying to port Linux to another
+ * architecture, here are some C-language equivalents. You should
+ * recode these in the native assmebly language, if at all possible.
+ *
+ * C language equivalents written by Theodore Ts'o, 9/26/92.
+ * Modified by Pete A. Zaitcev 7/14/95 to be portable to big endian
+ * systems, as well as non-32 bit systems.
+ */
+
+int ext2fs_set_bit(unsigned int nr,void * addr)
+{
+ int mask, retval;
+ unsigned char *ADDR = (unsigned char *) addr;
+
+ ADDR += nr >> 3;
+ mask = 1 << (nr & 0x07);
+ retval = mask & *ADDR;
+ *ADDR |= mask;
+ return retval;
+}
+
+int ext2fs_clear_bit(unsigned int nr, void * addr)
+{
+ int mask, retval;
+ unsigned char *ADDR = (unsigned char *) addr;
+
+ ADDR += nr >> 3;
+ mask = 1 << (nr & 0x07);
+ retval = mask & *ADDR;
+ *ADDR &= ~mask;
+ return retval;
+}
+
+int ext2fs_test_bit(unsigned int nr, const void * addr)
+{
+ int mask;
+ const unsigned char *ADDR = (const unsigned char *) addr;
+
+ ADDR += nr >> 3;
+ mask = 1 << (nr & 0x07);
+ return (mask & *ADDR);
+}
+
+#endif /* !_EXT2_HAVE_ASM_BITOPS_ */
+
+void ext2fs_warn_bitmap(errcode_t errcode, unsigned long arg,
+ const char *description)
+{
+#ifndef OMIT_COM_ERR
+ if (description)
+ bb_error_msg("#%lu for %s", arg, description);
+ else
+ bb_error_msg("#%lu", arg);
+#endif
+}
+
+void ext2fs_warn_bitmap2(ext2fs_generic_bitmap bitmap,
+ int code, unsigned long arg)
+{
+#ifndef OMIT_COM_ERR
+ if (bitmap->description)
+ bb_error_msg("#%lu for %s", arg, bitmap->description);
+ else
+ bb_error_msg("#%lu", arg);
+#endif
+}
+
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/bitops.h b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/bitops.h
new file mode 100644
index 0000000..6dd3086
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/bitops.h
@@ -0,0 +1,107 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * bitops.h --- Bitmap frobbing code. The byte swapping routines are
+ * also included here.
+ *
+ * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ *
+ * i386 bitops operations taken from <asm/bitops.h>, Copyright 1992,
+ * Linus Torvalds.
+ */
+
+#include <string.h>
+#include <strings.h>
+
+extern int ext2fs_set_bit(unsigned int nr,void * addr);
+extern int ext2fs_clear_bit(unsigned int nr, void * addr);
+extern int ext2fs_test_bit(unsigned int nr, const void * addr);
+extern __u16 ext2fs_swab16(__u16 val);
+extern __u32 ext2fs_swab32(__u32 val);
+
+#ifdef WORDS_BIGENDIAN
+#define ext2fs_cpu_to_le32(x) ext2fs_swab32((x))
+#define ext2fs_le32_to_cpu(x) ext2fs_swab32((x))
+#define ext2fs_cpu_to_le16(x) ext2fs_swab16((x))
+#define ext2fs_le16_to_cpu(x) ext2fs_swab16((x))
+#define ext2fs_cpu_to_be32(x) ((__u32)(x))
+#define ext2fs_be32_to_cpu(x) ((__u32)(x))
+#define ext2fs_cpu_to_be16(x) ((__u16)(x))
+#define ext2fs_be16_to_cpu(x) ((__u16)(x))
+#else
+#define ext2fs_cpu_to_le32(x) ((__u32)(x))
+#define ext2fs_le32_to_cpu(x) ((__u32)(x))
+#define ext2fs_cpu_to_le16(x) ((__u16)(x))
+#define ext2fs_le16_to_cpu(x) ((__u16)(x))
+#define ext2fs_cpu_to_be32(x) ext2fs_swab32((x))
+#define ext2fs_be32_to_cpu(x) ext2fs_swab32((x))
+#define ext2fs_cpu_to_be16(x) ext2fs_swab16((x))
+#define ext2fs_be16_to_cpu(x) ext2fs_swab16((x))
+#endif
+
+/*
+ * EXT2FS bitmap manipulation routines.
+ */
+
+/* Support for sending warning messages from the inline subroutines */
+extern const char *ext2fs_block_string;
+extern const char *ext2fs_inode_string;
+extern const char *ext2fs_mark_string;
+extern const char *ext2fs_unmark_string;
+extern const char *ext2fs_test_string;
+extern void ext2fs_warn_bitmap(errcode_t errcode, unsigned long arg,
+ const char *description);
+extern void ext2fs_warn_bitmap2(ext2fs_generic_bitmap bitmap,
+ int code, unsigned long arg);
+
+extern int ext2fs_mark_block_bitmap(ext2fs_block_bitmap bitmap, blk_t block);
+extern int ext2fs_unmark_block_bitmap(ext2fs_block_bitmap bitmap,
+ blk_t block);
+extern int ext2fs_test_block_bitmap(ext2fs_block_bitmap bitmap, blk_t block);
+
+extern int ext2fs_mark_inode_bitmap(ext2fs_inode_bitmap bitmap, ext2_ino_t inode);
+extern int ext2fs_unmark_inode_bitmap(ext2fs_inode_bitmap bitmap,
+ ext2_ino_t inode);
+extern int ext2fs_test_inode_bitmap(ext2fs_inode_bitmap bitmap, ext2_ino_t inode);
+
+extern void ext2fs_fast_mark_block_bitmap(ext2fs_block_bitmap bitmap,
+ blk_t block);
+extern void ext2fs_fast_unmark_block_bitmap(ext2fs_block_bitmap bitmap,
+ blk_t block);
+extern int ext2fs_fast_test_block_bitmap(ext2fs_block_bitmap bitmap,
+ blk_t block);
+
+extern void ext2fs_fast_mark_inode_bitmap(ext2fs_inode_bitmap bitmap,
+ ext2_ino_t inode);
+extern void ext2fs_fast_unmark_inode_bitmap(ext2fs_inode_bitmap bitmap,
+ ext2_ino_t inode);
+extern int ext2fs_fast_test_inode_bitmap(ext2fs_inode_bitmap bitmap,
+ ext2_ino_t inode);
+extern blk_t ext2fs_get_block_bitmap_start(ext2fs_block_bitmap bitmap);
+extern ext2_ino_t ext2fs_get_inode_bitmap_start(ext2fs_inode_bitmap bitmap);
+extern blk_t ext2fs_get_block_bitmap_end(ext2fs_block_bitmap bitmap);
+extern ext2_ino_t ext2fs_get_inode_bitmap_end(ext2fs_inode_bitmap bitmap);
+
+extern void ext2fs_mark_block_bitmap_range(ext2fs_block_bitmap bitmap,
+ blk_t block, int num);
+extern void ext2fs_unmark_block_bitmap_range(ext2fs_block_bitmap bitmap,
+ blk_t block, int num);
+extern int ext2fs_test_block_bitmap_range(ext2fs_block_bitmap bitmap,
+ blk_t block, int num);
+extern void ext2fs_fast_mark_block_bitmap_range(ext2fs_block_bitmap bitmap,
+ blk_t block, int num);
+extern void ext2fs_fast_unmark_block_bitmap_range(ext2fs_block_bitmap bitmap,
+ blk_t block, int num);
+extern int ext2fs_fast_test_block_bitmap_range(ext2fs_block_bitmap bitmap,
+ blk_t block, int num);
+extern void ext2fs_set_bitmap_padding(ext2fs_generic_bitmap map);
+
+/* These two routines moved to gen_bitmap.c */
+extern int ext2fs_mark_generic_bitmap(ext2fs_generic_bitmap bitmap,
+ __u32 bitno);
+extern int ext2fs_unmark_generic_bitmap(ext2fs_generic_bitmap bitmap,
+ blk_t bitno);
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/block.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/block.c
new file mode 100644
index 0000000..0a757b2
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/block.c
@@ -0,0 +1,438 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * block.c --- iterate over all blocks in an inode
+ *
+ * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+struct block_context {
+ ext2_filsys fs;
+ int (*func)(ext2_filsys fs,
+ blk_t *blocknr,
+ e2_blkcnt_t bcount,
+ blk_t ref_blk,
+ int ref_offset,
+ void *priv_data);
+ e2_blkcnt_t bcount;
+ int bsize;
+ int flags;
+ errcode_t errcode;
+ char *ind_buf;
+ char *dind_buf;
+ char *tind_buf;
+ void *priv_data;
+};
+
+static int block_iterate_ind(blk_t *ind_block, blk_t ref_block,
+ int ref_offset, struct block_context *ctx)
+{
+ int ret = 0, changed = 0;
+ int i, flags, limit, offset;
+ blk_t *block_nr;
+
+ limit = ctx->fs->blocksize >> 2;
+ if (!(ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) &&
+ !(ctx->flags & BLOCK_FLAG_DATA_ONLY))
+ ret = (*ctx->func)(ctx->fs, ind_block,
+ BLOCK_COUNT_IND, ref_block,
+ ref_offset, ctx->priv_data);
+ if (!*ind_block || (ret & BLOCK_ABORT)) {
+ ctx->bcount += limit;
+ return ret;
+ }
+ if (*ind_block >= ctx->fs->super->s_blocks_count ||
+ *ind_block < ctx->fs->super->s_first_data_block) {
+ ctx->errcode = EXT2_ET_BAD_IND_BLOCK;
+ ret |= BLOCK_ERROR;
+ return ret;
+ }
+ ctx->errcode = ext2fs_read_ind_block(ctx->fs, *ind_block,
+ ctx->ind_buf);
+ if (ctx->errcode) {
+ ret |= BLOCK_ERROR;
+ return ret;
+ }
+
+ block_nr = (blk_t *) ctx->ind_buf;
+ offset = 0;
+ if (ctx->flags & BLOCK_FLAG_APPEND) {
+ for (i = 0; i < limit; i++, ctx->bcount++, block_nr++) {
+ flags = (*ctx->func)(ctx->fs, block_nr, ctx->bcount,
+ *ind_block, offset,
+ ctx->priv_data);
+ changed |= flags;
+ if (flags & BLOCK_ABORT) {
+ ret |= BLOCK_ABORT;
+ break;
+ }
+ offset += sizeof(blk_t);
+ }
+ } else {
+ for (i = 0; i < limit; i++, ctx->bcount++, block_nr++) {
+ if (*block_nr == 0)
+ continue;
+ flags = (*ctx->func)(ctx->fs, block_nr, ctx->bcount,
+ *ind_block, offset,
+ ctx->priv_data);
+ changed |= flags;
+ if (flags & BLOCK_ABORT) {
+ ret |= BLOCK_ABORT;
+ break;
+ }
+ offset += sizeof(blk_t);
+ }
+ }
+ if (changed & BLOCK_CHANGED) {
+ ctx->errcode = ext2fs_write_ind_block(ctx->fs, *ind_block,
+ ctx->ind_buf);
+ if (ctx->errcode)
+ ret |= BLOCK_ERROR | BLOCK_ABORT;
+ }
+ if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) &&
+ !(ctx->flags & BLOCK_FLAG_DATA_ONLY) &&
+ !(ret & BLOCK_ABORT))
+ ret |= (*ctx->func)(ctx->fs, ind_block,
+ BLOCK_COUNT_IND, ref_block,
+ ref_offset, ctx->priv_data);
+ return ret;
+}
+
+static int block_iterate_dind(blk_t *dind_block, blk_t ref_block,
+ int ref_offset, struct block_context *ctx)
+{
+ int ret = 0, changed = 0;
+ int i, flags, limit, offset;
+ blk_t *block_nr;
+
+ limit = ctx->fs->blocksize >> 2;
+ if (!(ctx->flags & (BLOCK_FLAG_DEPTH_TRAVERSE |
+ BLOCK_FLAG_DATA_ONLY)))
+ ret = (*ctx->func)(ctx->fs, dind_block,
+ BLOCK_COUNT_DIND, ref_block,
+ ref_offset, ctx->priv_data);
+ if (!*dind_block || (ret & BLOCK_ABORT)) {
+ ctx->bcount += limit*limit;
+ return ret;
+ }
+ if (*dind_block >= ctx->fs->super->s_blocks_count ||
+ *dind_block < ctx->fs->super->s_first_data_block) {
+ ctx->errcode = EXT2_ET_BAD_DIND_BLOCK;
+ ret |= BLOCK_ERROR;
+ return ret;
+ }
+ ctx->errcode = ext2fs_read_ind_block(ctx->fs, *dind_block,
+ ctx->dind_buf);
+ if (ctx->errcode) {
+ ret |= BLOCK_ERROR;
+ return ret;
+ }
+
+ block_nr = (blk_t *) ctx->dind_buf;
+ offset = 0;
+ if (ctx->flags & BLOCK_FLAG_APPEND) {
+ for (i = 0; i < limit; i++, block_nr++) {
+ flags = block_iterate_ind(block_nr,
+ *dind_block, offset,
+ ctx);
+ changed |= flags;
+ if (flags & (BLOCK_ABORT | BLOCK_ERROR)) {
+ ret |= flags & (BLOCK_ABORT | BLOCK_ERROR);
+ break;
+ }
+ offset += sizeof(blk_t);
+ }
+ } else {
+ for (i = 0; i < limit; i++, block_nr++) {
+ if (*block_nr == 0) {
+ ctx->bcount += limit;
+ continue;
+ }
+ flags = block_iterate_ind(block_nr,
+ *dind_block, offset,
+ ctx);
+ changed |= flags;
+ if (flags & (BLOCK_ABORT | BLOCK_ERROR)) {
+ ret |= flags & (BLOCK_ABORT | BLOCK_ERROR);
+ break;
+ }
+ offset += sizeof(blk_t);
+ }
+ }
+ if (changed & BLOCK_CHANGED) {
+ ctx->errcode = ext2fs_write_ind_block(ctx->fs, *dind_block,
+ ctx->dind_buf);
+ if (ctx->errcode)
+ ret |= BLOCK_ERROR | BLOCK_ABORT;
+ }
+ if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) &&
+ !(ctx->flags & BLOCK_FLAG_DATA_ONLY) &&
+ !(ret & BLOCK_ABORT))
+ ret |= (*ctx->func)(ctx->fs, dind_block,
+ BLOCK_COUNT_DIND, ref_block,
+ ref_offset, ctx->priv_data);
+ return ret;
+}
+
+static int block_iterate_tind(blk_t *tind_block, blk_t ref_block,
+ int ref_offset, struct block_context *ctx)
+{
+ int ret = 0, changed = 0;
+ int i, flags, limit, offset;
+ blk_t *block_nr;
+
+ limit = ctx->fs->blocksize >> 2;
+ if (!(ctx->flags & (BLOCK_FLAG_DEPTH_TRAVERSE |
+ BLOCK_FLAG_DATA_ONLY)))
+ ret = (*ctx->func)(ctx->fs, tind_block,
+ BLOCK_COUNT_TIND, ref_block,
+ ref_offset, ctx->priv_data);
+ if (!*tind_block || (ret & BLOCK_ABORT)) {
+ ctx->bcount += limit*limit*limit;
+ return ret;
+ }
+ if (*tind_block >= ctx->fs->super->s_blocks_count ||
+ *tind_block < ctx->fs->super->s_first_data_block) {
+ ctx->errcode = EXT2_ET_BAD_TIND_BLOCK;
+ ret |= BLOCK_ERROR;
+ return ret;
+ }
+ ctx->errcode = ext2fs_read_ind_block(ctx->fs, *tind_block,
+ ctx->tind_buf);
+ if (ctx->errcode) {
+ ret |= BLOCK_ERROR;
+ return ret;
+ }
+
+ block_nr = (blk_t *) ctx->tind_buf;
+ offset = 0;
+ if (ctx->flags & BLOCK_FLAG_APPEND) {
+ for (i = 0; i < limit; i++, block_nr++) {
+ flags = block_iterate_dind(block_nr,
+ *tind_block,
+ offset, ctx);
+ changed |= flags;
+ if (flags & (BLOCK_ABORT | BLOCK_ERROR)) {
+ ret |= flags & (BLOCK_ABORT | BLOCK_ERROR);
+ break;
+ }
+ offset += sizeof(blk_t);
+ }
+ } else {
+ for (i = 0; i < limit; i++, block_nr++) {
+ if (*block_nr == 0) {
+ ctx->bcount += limit*limit;
+ continue;
+ }
+ flags = block_iterate_dind(block_nr,
+ *tind_block,
+ offset, ctx);
+ changed |= flags;
+ if (flags & (BLOCK_ABORT | BLOCK_ERROR)) {
+ ret |= flags & (BLOCK_ABORT | BLOCK_ERROR);
+ break;
+ }
+ offset += sizeof(blk_t);
+ }
+ }
+ if (changed & BLOCK_CHANGED) {
+ ctx->errcode = ext2fs_write_ind_block(ctx->fs, *tind_block,
+ ctx->tind_buf);
+ if (ctx->errcode)
+ ret |= BLOCK_ERROR | BLOCK_ABORT;
+ }
+ if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) &&
+ !(ctx->flags & BLOCK_FLAG_DATA_ONLY) &&
+ !(ret & BLOCK_ABORT))
+ ret |= (*ctx->func)(ctx->fs, tind_block,
+ BLOCK_COUNT_TIND, ref_block,
+ ref_offset, ctx->priv_data);
+
+ return ret;
+}
+
+errcode_t ext2fs_block_iterate2(ext2_filsys fs,
+ ext2_ino_t ino,
+ int flags,
+ char *block_buf,
+ int (*func)(ext2_filsys fs,
+ blk_t *blocknr,
+ e2_blkcnt_t blockcnt,
+ blk_t ref_blk,
+ int ref_offset,
+ void *priv_data),
+ void *priv_data)
+{
+ int i;
+ int got_inode = 0;
+ int ret = 0;
+ blk_t blocks[EXT2_N_BLOCKS]; /* directory data blocks */
+ struct ext2_inode inode;
+ errcode_t retval;
+ struct block_context ctx;
+ int limit;
+
+ EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+ /*
+ * Check to see if we need to limit large files
+ */
+ if (flags & BLOCK_FLAG_NO_LARGE) {
+ ctx.errcode = ext2fs_read_inode(fs, ino, &inode);
+ if (ctx.errcode)
+ return ctx.errcode;
+ got_inode = 1;
+ if (!LINUX_S_ISDIR(inode.i_mode) &&
+ (inode.i_size_high != 0))
+ return EXT2_ET_FILE_TOO_BIG;
+ }
+
+ retval = ext2fs_get_blocks(fs, ino, blocks);
+ if (retval)
+ return retval;
+
+ limit = fs->blocksize >> 2;
+
+ ctx.fs = fs;
+ ctx.func = func;
+ ctx.priv_data = priv_data;
+ ctx.flags = flags;
+ ctx.bcount = 0;
+ if (block_buf) {
+ ctx.ind_buf = block_buf;
+ } else {
+ retval = ext2fs_get_mem(fs->blocksize * 3, &ctx.ind_buf);
+ if (retval)
+ return retval;
+ }
+ ctx.dind_buf = ctx.ind_buf + fs->blocksize;
+ ctx.tind_buf = ctx.dind_buf + fs->blocksize;
+
+ /*
+ * Iterate over the HURD translator block (if present)
+ */
+ if ((fs->super->s_creator_os == EXT2_OS_HURD) &&
+ !(flags & BLOCK_FLAG_DATA_ONLY)) {
+ ctx.errcode = ext2fs_read_inode(fs, ino, &inode);
+ if (ctx.errcode)
+ goto abort_exit;
+ got_inode = 1;
+ if (inode.osd1.hurd1.h_i_translator) {
+ ret |= (*ctx.func)(fs,
+ &inode.osd1.hurd1.h_i_translator,
+ BLOCK_COUNT_TRANSLATOR,
+ 0, 0, priv_data);
+ if (ret & BLOCK_ABORT)
+ goto abort_exit;
+ }
+ }
+
+ /*
+ * Iterate over normal data blocks
+ */
+ for (i = 0; i < EXT2_NDIR_BLOCKS ; i++, ctx.bcount++) {
+ if (blocks[i] || (flags & BLOCK_FLAG_APPEND)) {
+ ret |= (*ctx.func)(fs, &blocks[i],
+ ctx.bcount, 0, i, priv_data);
+ if (ret & BLOCK_ABORT)
+ goto abort_exit;
+ }
+ }
+ if (*(blocks + EXT2_IND_BLOCK) || (flags & BLOCK_FLAG_APPEND)) {
+ ret |= block_iterate_ind(blocks + EXT2_IND_BLOCK,
+ 0, EXT2_IND_BLOCK, &ctx);
+ if (ret & BLOCK_ABORT)
+ goto abort_exit;
+ } else
+ ctx.bcount += limit;
+ if (*(blocks + EXT2_DIND_BLOCK) || (flags & BLOCK_FLAG_APPEND)) {
+ ret |= block_iterate_dind(blocks + EXT2_DIND_BLOCK,
+ 0, EXT2_DIND_BLOCK, &ctx);
+ if (ret & BLOCK_ABORT)
+ goto abort_exit;
+ } else
+ ctx.bcount += limit * limit;
+ if (*(blocks + EXT2_TIND_BLOCK) || (flags & BLOCK_FLAG_APPEND)) {
+ ret |= block_iterate_tind(blocks + EXT2_TIND_BLOCK,
+ 0, EXT2_TIND_BLOCK, &ctx);
+ if (ret & BLOCK_ABORT)
+ goto abort_exit;
+ }
+
+abort_exit:
+ if (ret & BLOCK_CHANGED) {
+ if (!got_inode) {
+ retval = ext2fs_read_inode(fs, ino, &inode);
+ if (retval)
+ return retval;
+ }
+ for (i=0; i < EXT2_N_BLOCKS; i++)
+ inode.i_block[i] = blocks[i];
+ retval = ext2fs_write_inode(fs, ino, &inode);
+ if (retval)
+ return retval;
+ }
+
+ if (!block_buf)
+ ext2fs_free_mem(&ctx.ind_buf);
+
+ return (ret & BLOCK_ERROR) ? ctx.errcode : 0;
+}
+
+/*
+ * Emulate the old ext2fs_block_iterate function!
+ */
+
+struct xlate {
+ int (*func)(ext2_filsys fs,
+ blk_t *blocknr,
+ int bcount,
+ void *priv_data);
+ void *real_private;
+};
+
+#ifdef __TURBOC__
+# pragma argsused
+#endif
+static int xlate_func(ext2_filsys fs, blk_t *blocknr, e2_blkcnt_t blockcnt,
+ blk_t ref_block EXT2FS_ATTR((unused)),
+ int ref_offset EXT2FS_ATTR((unused)),
+ void *priv_data)
+{
+ struct xlate *xl = (struct xlate *) priv_data;
+
+ return (*xl->func)(fs, blocknr, (int) blockcnt, xl->real_private);
+}
+
+errcode_t ext2fs_block_iterate(ext2_filsys fs,
+ ext2_ino_t ino,
+ int flags,
+ char *block_buf,
+ int (*func)(ext2_filsys fs,
+ blk_t *blocknr,
+ int blockcnt,
+ void *priv_data),
+ void *priv_data)
+{
+ struct xlate xl;
+
+ xl.real_private = priv_data;
+ xl.func = func;
+
+ return ext2fs_block_iterate2(fs, ino, BLOCK_FLAG_NO_LARGE | flags,
+ block_buf, xlate_func, &xl);
+}
+
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/bmap.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/bmap.c
new file mode 100644
index 0000000..b22fe3d
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/bmap.c
@@ -0,0 +1,264 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * bmap.c --- logical to physical block mapping
+ *
+ * Copyright (C) 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+extern errcode_t ext2fs_bmap(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode *inode,
+ char *block_buf, int bmap_flags,
+ blk_t block, blk_t *phys_blk);
+
+#define inode_bmap(inode, nr) ((inode)->i_block[(nr)])
+
+static errcode_t block_ind_bmap(ext2_filsys fs, int flags,
+ blk_t ind, char *block_buf,
+ int *blocks_alloc,
+ blk_t nr, blk_t *ret_blk)
+{
+ errcode_t retval;
+ blk_t b;
+
+ if (!ind) {
+ if (flags & BMAP_SET)
+ return EXT2_ET_SET_BMAP_NO_IND;
+ *ret_blk = 0;
+ return 0;
+ }
+ retval = io_channel_read_blk(fs->io, ind, 1, block_buf);
+ if (retval)
+ return retval;
+
+ if (flags & BMAP_SET) {
+ b = *ret_blk;
+#if BB_BIG_ENDIAN
+ if ((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
+ (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE))
+ b = ext2fs_swab32(b);
+#endif
+ ((blk_t *) block_buf)[nr] = b;
+ return io_channel_write_blk(fs->io, ind, 1, block_buf);
+ }
+
+ b = ((blk_t *) block_buf)[nr];
+
+#if BB_BIG_ENDIAN
+ if ((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
+ (fs->flags & EXT2_FLAG_SWAP_BYTES_READ))
+ b = ext2fs_swab32(b);
+#endif
+
+ if (!b && (flags & BMAP_ALLOC)) {
+ b = nr ? ((blk_t *) block_buf)[nr-1] : 0;
+ retval = ext2fs_alloc_block(fs, b,
+ block_buf + fs->blocksize, &b);
+ if (retval)
+ return retval;
+
+#if BB_BIG_ENDIAN
+ if ((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
+ (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE))
+ ((blk_t *) block_buf)[nr] = ext2fs_swab32(b);
+ else
+#endif
+ ((blk_t *) block_buf)[nr] = b;
+
+ retval = io_channel_write_blk(fs->io, ind, 1, block_buf);
+ if (retval)
+ return retval;
+
+ (*blocks_alloc)++;
+ }
+
+ *ret_blk = b;
+ return 0;
+}
+
+static errcode_t block_dind_bmap(ext2_filsys fs, int flags,
+ blk_t dind, char *block_buf,
+ int *blocks_alloc,
+ blk_t nr, blk_t *ret_blk)
+{
+ blk_t b;
+ errcode_t retval;
+ blk_t addr_per_block;
+
+ addr_per_block = (blk_t) fs->blocksize >> 2;
+
+ retval = block_ind_bmap(fs, flags & ~BMAP_SET, dind, block_buf,
+ blocks_alloc, nr / addr_per_block, &b);
+ if (retval)
+ return retval;
+ retval = block_ind_bmap(fs, flags, b, block_buf, blocks_alloc,
+ nr % addr_per_block, ret_blk);
+ return retval;
+}
+
+static errcode_t block_tind_bmap(ext2_filsys fs, int flags,
+ blk_t tind, char *block_buf,
+ int *blocks_alloc,
+ blk_t nr, blk_t *ret_blk)
+{
+ blk_t b;
+ errcode_t retval;
+ blk_t addr_per_block;
+
+ addr_per_block = (blk_t) fs->blocksize >> 2;
+
+ retval = block_dind_bmap(fs, flags & ~BMAP_SET, tind, block_buf,
+ blocks_alloc, nr / addr_per_block, &b);
+ if (retval)
+ return retval;
+ retval = block_ind_bmap(fs, flags, b, block_buf, blocks_alloc,
+ nr % addr_per_block, ret_blk);
+ return retval;
+}
+
+errcode_t ext2fs_bmap(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode,
+ char *block_buf, int bmap_flags, blk_t block,
+ blk_t *phys_blk)
+{
+ struct ext2_inode inode_buf;
+ blk_t addr_per_block;
+ blk_t b;
+ char *buf = 0;
+ errcode_t retval = 0;
+ int blocks_alloc = 0, inode_dirty = 0;
+
+ if (!(bmap_flags & BMAP_SET))
+ *phys_blk = 0;
+
+ /* Read inode structure if necessary */
+ if (!inode) {
+ retval = ext2fs_read_inode(fs, ino, &inode_buf);
+ if (retval)
+ return retval;
+ inode = &inode_buf;
+ }
+ addr_per_block = (blk_t) fs->blocksize >> 2;
+
+ if (!block_buf) {
+ retval = ext2fs_get_mem(fs->blocksize * 2, &buf);
+ if (retval)
+ return retval;
+ block_buf = buf;
+ }
+
+ if (block < EXT2_NDIR_BLOCKS) {
+ if (bmap_flags & BMAP_SET) {
+ b = *phys_blk;
+#if BB_BIG_ENDIAN
+ if ((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
+ (fs->flags & EXT2_FLAG_SWAP_BYTES_READ))
+ b = ext2fs_swab32(b);
+#endif
+ inode_bmap(inode, block) = b;
+ inode_dirty++;
+ goto done;
+ }
+
+ *phys_blk = inode_bmap(inode, block);
+ b = block ? inode_bmap(inode, block-1) : 0;
+
+ if ((*phys_blk == 0) && (bmap_flags & BMAP_ALLOC)) {
+ retval = ext2fs_alloc_block(fs, b, block_buf, &b);
+ if (retval)
+ goto done;
+ inode_bmap(inode, block) = b;
+ blocks_alloc++;
+ *phys_blk = b;
+ }
+ goto done;
+ }
+
+ /* Indirect block */
+ block -= EXT2_NDIR_BLOCKS;
+ if (block < addr_per_block) {
+ b = inode_bmap(inode, EXT2_IND_BLOCK);
+ if (!b) {
+ if (!(bmap_flags & BMAP_ALLOC)) {
+ if (bmap_flags & BMAP_SET)
+ retval = EXT2_ET_SET_BMAP_NO_IND;
+ goto done;
+ }
+
+ b = inode_bmap(inode, EXT2_IND_BLOCK-1);
+ retval = ext2fs_alloc_block(fs, b, block_buf, &b);
+ if (retval)
+ goto done;
+ inode_bmap(inode, EXT2_IND_BLOCK) = b;
+ blocks_alloc++;
+ }
+ retval = block_ind_bmap(fs, bmap_flags, b, block_buf,
+ &blocks_alloc, block, phys_blk);
+ goto done;
+ }
+
+ /* Doubly indirect block */
+ block -= addr_per_block;
+ if (block < addr_per_block * addr_per_block) {
+ b = inode_bmap(inode, EXT2_DIND_BLOCK);
+ if (!b) {
+ if (!(bmap_flags & BMAP_ALLOC)) {
+ if (bmap_flags & BMAP_SET)
+ retval = EXT2_ET_SET_BMAP_NO_IND;
+ goto done;
+ }
+
+ b = inode_bmap(inode, EXT2_IND_BLOCK);
+ retval = ext2fs_alloc_block(fs, b, block_buf, &b);
+ if (retval)
+ goto done;
+ inode_bmap(inode, EXT2_DIND_BLOCK) = b;
+ blocks_alloc++;
+ }
+ retval = block_dind_bmap(fs, bmap_flags, b, block_buf,
+ &blocks_alloc, block, phys_blk);
+ goto done;
+ }
+
+ /* Triply indirect block */
+ block -= addr_per_block * addr_per_block;
+ b = inode_bmap(inode, EXT2_TIND_BLOCK);
+ if (!b) {
+ if (!(bmap_flags & BMAP_ALLOC)) {
+ if (bmap_flags & BMAP_SET)
+ retval = EXT2_ET_SET_BMAP_NO_IND;
+ goto done;
+ }
+
+ b = inode_bmap(inode, EXT2_DIND_BLOCK);
+ retval = ext2fs_alloc_block(fs, b, block_buf, &b);
+ if (retval)
+ goto done;
+ inode_bmap(inode, EXT2_TIND_BLOCK) = b;
+ blocks_alloc++;
+ }
+ retval = block_tind_bmap(fs, bmap_flags, b, block_buf,
+ &blocks_alloc, block, phys_blk);
+done:
+ ext2fs_free_mem(&buf);
+ if ((retval == 0) && (blocks_alloc || inode_dirty)) {
+ inode->i_blocks += (blocks_alloc * fs->blocksize) / 512;
+ retval = ext2fs_write_inode(fs, ino, inode);
+ }
+ return retval;
+}
+
+
+
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/bmove.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/bmove.c
new file mode 100644
index 0000000..635410d
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/bmove.c
@@ -0,0 +1,156 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * bmove.c --- Move blocks around to make way for a particular
+ * filesystem structure.
+ *
+ * Copyright (C) 1997 Theodore Ts'o. This file may be redistributed
+ * under the terms of the GNU Public License.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fsP.h"
+
+struct process_block_struct {
+ ext2_ino_t ino;
+ struct ext2_inode * inode;
+ ext2fs_block_bitmap reserve;
+ ext2fs_block_bitmap alloc_map;
+ errcode_t error;
+ char *buf;
+ int add_dir;
+ int flags;
+};
+
+static int process_block(ext2_filsys fs, blk_t *block_nr,
+ e2_blkcnt_t blockcnt, blk_t ref_block,
+ int ref_offset, void *priv_data)
+{
+ struct process_block_struct *pb;
+ errcode_t retval;
+ int ret;
+ blk_t block, orig;
+
+ pb = (struct process_block_struct *) priv_data;
+ block = orig = *block_nr;
+ ret = 0;
+
+ /*
+ * Let's see if this is one which we need to relocate
+ */
+ if (ext2fs_test_block_bitmap(pb->reserve, block)) {
+ do {
+ if (++block >= fs->super->s_blocks_count)
+ block = fs->super->s_first_data_block;
+ if (block == orig) {
+ pb->error = EXT2_ET_BLOCK_ALLOC_FAIL;
+ return BLOCK_ABORT;
+ }
+ } while (ext2fs_test_block_bitmap(pb->reserve, block) ||
+ ext2fs_test_block_bitmap(pb->alloc_map, block));
+
+ retval = io_channel_read_blk(fs->io, orig, 1, pb->buf);
+ if (retval) {
+ pb->error = retval;
+ return BLOCK_ABORT;
+ }
+ retval = io_channel_write_blk(fs->io, block, 1, pb->buf);
+ if (retval) {
+ pb->error = retval;
+ return BLOCK_ABORT;
+ }
+ *block_nr = block;
+ ext2fs_mark_block_bitmap(pb->alloc_map, block);
+ ret = BLOCK_CHANGED;
+ if (pb->flags & EXT2_BMOVE_DEBUG)
+ printf("ino=%ld, blockcnt=%lld, %d->%d\n", pb->ino,
+ blockcnt, orig, block);
+ }
+ if (pb->add_dir) {
+ retval = ext2fs_add_dir_block(fs->dblist, pb->ino,
+ block, (int) blockcnt);
+ if (retval) {
+ pb->error = retval;
+ ret |= BLOCK_ABORT;
+ }
+ }
+ return ret;
+}
+
+errcode_t ext2fs_move_blocks(ext2_filsys fs,
+ ext2fs_block_bitmap reserve,
+ ext2fs_block_bitmap alloc_map,
+ int flags)
+{
+ ext2_ino_t ino;
+ struct ext2_inode inode;
+ errcode_t retval;
+ struct process_block_struct pb;
+ ext2_inode_scan scan;
+ char *block_buf;
+
+ retval = ext2fs_open_inode_scan(fs, 0, &scan);
+ if (retval)
+ return retval;
+
+ pb.reserve = reserve;
+ pb.error = 0;
+ pb.alloc_map = alloc_map ? alloc_map : fs->block_map;
+ pb.flags = flags;
+
+ retval = ext2fs_get_mem(fs->blocksize * 4, &block_buf);
+ if (retval)
+ return retval;
+ pb.buf = block_buf + fs->blocksize * 3;
+
+ /*
+ * If GET_DBLIST is set in the flags field, then we should
+ * gather directory block information while we're doing the
+ * block move.
+ */
+ if (flags & EXT2_BMOVE_GET_DBLIST) {
+ ext2fs_free_dblist(fs->dblist);
+ fs->dblist = NULL;
+ retval = ext2fs_init_dblist(fs, 0);
+ if (retval)
+ return retval;
+ }
+
+ retval = ext2fs_get_next_inode(scan, &ino, &inode);
+ if (retval)
+ return retval;
+
+ while (ino) {
+ if ((inode.i_links_count == 0) ||
+ !ext2fs_inode_has_valid_blocks(&inode))
+ goto next;
+
+ pb.ino = ino;
+ pb.inode = &inode;
+
+ pb.add_dir = (LINUX_S_ISDIR(inode.i_mode) &&
+ flags & EXT2_BMOVE_GET_DBLIST);
+
+ retval = ext2fs_block_iterate2(fs, ino, 0, block_buf,
+ process_block, &pb);
+ if (retval)
+ return retval;
+ if (pb.error)
+ return pb.error;
+
+ next:
+ retval = ext2fs_get_next_inode(scan, &ino, &inode);
+ if (retval == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE)
+ goto next;
+ }
+ return 0;
+}
+
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/brel.h b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/brel.h
new file mode 100644
index 0000000..216fd13
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/brel.h
@@ -0,0 +1,87 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * brel.h
+ *
+ * Copyright (C) 1996, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+struct ext2_block_relocate_entry {
+ blk_t new;
+ __s16 offset;
+ __u16 flags;
+ union {
+ blk_t block_ref;
+ ext2_ino_t inode_ref;
+ } owner;
+};
+
+#define RELOCATE_TYPE_REF 0x0007
+#define RELOCATE_BLOCK_REF 0x0001
+#define RELOCATE_INODE_REF 0x0002
+
+typedef struct ext2_block_relocation_table *ext2_brel;
+
+struct ext2_block_relocation_table {
+ __u32 magic;
+ char *name;
+ blk_t current;
+ void *priv_data;
+
+ /*
+ * Add a block relocation entry.
+ */
+ errcode_t (*put)(ext2_brel brel, blk_t old,
+ struct ext2_block_relocate_entry *ent);
+
+ /*
+ * Get a block relocation entry.
+ */
+ errcode_t (*get)(ext2_brel brel, blk_t old,
+ struct ext2_block_relocate_entry *ent);
+
+ /*
+ * Initialize for iterating over the block relocation entries.
+ */
+ errcode_t (*start_iter)(ext2_brel brel);
+
+ /*
+ * The iterator function for the inode relocation entries.
+ * Returns an inode number of 0 when out of entries.
+ */
+ errcode_t (*next)(ext2_brel brel, blk_t *old,
+ struct ext2_block_relocate_entry *ent);
+
+ /*
+ * Move the inode relocation table from one block number to
+ * another.
+ */
+ errcode_t (*move)(ext2_brel brel, blk_t old, blk_t new);
+
+ /*
+ * Remove a block relocation entry.
+ */
+ errcode_t (*delete)(ext2_brel brel, blk_t old);
+
+
+ /*
+ * Free the block relocation table.
+ */
+ errcode_t (*free)(ext2_brel brel);
+};
+
+errcode_t ext2fs_brel_memarray_create(char *name, blk_t max_block,
+ ext2_brel *brel);
+
+#define ext2fs_brel_put(brel, old, ent) ((brel)->put((brel), old, ent))
+#define ext2fs_brel_get(brel, old, ent) ((brel)->get((brel), old, ent))
+#define ext2fs_brel_start_iter(brel) ((brel)->start_iter((brel)))
+#define ext2fs_brel_next(brel, old, ent) ((brel)->next((brel), old, ent))
+#define ext2fs_brel_move(brel, old, new) ((brel)->move((brel), old, new))
+#define ext2fs_brel_delete(brel, old) ((brel)->delete((brel), old))
+#define ext2fs_brel_free(brel) ((brel)->free((brel)))
+
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/brel_ma.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/brel_ma.c
new file mode 100644
index 0000000..652a350
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/brel_ma.c
@@ -0,0 +1,196 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * brel_ma.c
+ *
+ * Copyright (C) 1996, 1997 Theodore Ts'o.
+ *
+ * TODO: rewrite to not use a direct array!!! (Fortunately this
+ * module isn't really used yet.)
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+#include "brel.h"
+
+static errcode_t bma_put(ext2_brel brel, blk_t old,
+ struct ext2_block_relocate_entry *ent);
+static errcode_t bma_get(ext2_brel brel, blk_t old,
+ struct ext2_block_relocate_entry *ent);
+static errcode_t bma_start_iter(ext2_brel brel);
+static errcode_t bma_next(ext2_brel brel, blk_t *old,
+ struct ext2_block_relocate_entry *ent);
+static errcode_t bma_move(ext2_brel brel, blk_t old, blk_t new);
+static errcode_t bma_delete(ext2_brel brel, blk_t old);
+static errcode_t bma_free(ext2_brel brel);
+
+struct brel_ma {
+ __u32 magic;
+ blk_t max_block;
+ struct ext2_block_relocate_entry *entries;
+};
+
+errcode_t ext2fs_brel_memarray_create(char *name, blk_t max_block,
+ ext2_brel *new_brel)
+{
+ ext2_brel brel = 0;
+ errcode_t retval;
+ struct brel_ma *ma = 0;
+ size_t size;
+
+ *new_brel = 0;
+
+ /*
+ * Allocate memory structures
+ */
+ retval = ext2fs_get_mem(sizeof(struct ext2_block_relocation_table),
+ &brel);
+ if (retval)
+ goto errout;
+ memset(brel, 0, sizeof(struct ext2_block_relocation_table));
+
+ retval = ext2fs_get_mem(strlen(name)+1, &brel->name);
+ if (retval)
+ goto errout;
+ strcpy(brel->name, name);
+
+ retval = ext2fs_get_mem(sizeof(struct brel_ma), &ma);
+ if (retval)
+ goto errout;
+ memset(ma, 0, sizeof(struct brel_ma));
+ brel->priv_data = ma;
+
+ size = (size_t) (sizeof(struct ext2_block_relocate_entry) *
+ (max_block+1));
+ retval = ext2fs_get_mem(size, &ma->entries);
+ if (retval)
+ goto errout;
+ memset(ma->entries, 0, size);
+ ma->max_block = max_block;
+
+ /*
+ * Fill in the brel data structure
+ */
+ brel->put = bma_put;
+ brel->get = bma_get;
+ brel->start_iter = bma_start_iter;
+ brel->next = bma_next;
+ brel->move = bma_move;
+ brel->delete = bma_delete;
+ brel->free = bma_free;
+
+ *new_brel = brel;
+ return 0;
+
+errout:
+ bma_free(brel);
+ return retval;
+}
+
+static errcode_t bma_put(ext2_brel brel, blk_t old,
+ struct ext2_block_relocate_entry *ent)
+{
+ struct brel_ma *ma;
+
+ ma = brel->priv_data;
+ if (old > ma->max_block)
+ return EXT2_ET_INVALID_ARGUMENT;
+ ma->entries[(unsigned)old] = *ent;
+ return 0;
+}
+
+static errcode_t bma_get(ext2_brel brel, blk_t old,
+ struct ext2_block_relocate_entry *ent)
+{
+ struct brel_ma *ma;
+
+ ma = brel->priv_data;
+ if (old > ma->max_block)
+ return EXT2_ET_INVALID_ARGUMENT;
+ if (ma->entries[(unsigned)old].new == 0)
+ return ENOENT;
+ *ent = ma->entries[old];
+ return 0;
+}
+
+static errcode_t bma_start_iter(ext2_brel brel)
+{
+ brel->current = 0;
+ return 0;
+}
+
+static errcode_t bma_next(ext2_brel brel, blk_t *old,
+ struct ext2_block_relocate_entry *ent)
+{
+ struct brel_ma *ma;
+
+ ma = brel->priv_data;
+ while (++brel->current < ma->max_block) {
+ if (ma->entries[(unsigned)brel->current].new == 0)
+ continue;
+ *old = brel->current;
+ *ent = ma->entries[(unsigned)brel->current];
+ return 0;
+ }
+ *old = 0;
+ return 0;
+}
+
+static errcode_t bma_move(ext2_brel brel, blk_t old, blk_t new)
+{
+ struct brel_ma *ma;
+
+ ma = brel->priv_data;
+ if ((old > ma->max_block) || (new > ma->max_block))
+ return EXT2_ET_INVALID_ARGUMENT;
+ if (ma->entries[(unsigned)old].new == 0)
+ return ENOENT;
+ ma->entries[(unsigned)new] = ma->entries[old];
+ ma->entries[(unsigned)old].new = 0;
+ return 0;
+}
+
+static errcode_t bma_delete(ext2_brel brel, blk_t old)
+{
+ struct brel_ma *ma;
+
+ ma = brel->priv_data;
+ if (old > ma->max_block)
+ return EXT2_ET_INVALID_ARGUMENT;
+ if (ma->entries[(unsigned)old].new == 0)
+ return ENOENT;
+ ma->entries[(unsigned)old].new = 0;
+ return 0;
+}
+
+static errcode_t bma_free(ext2_brel brel)
+{
+ struct brel_ma *ma;
+
+ if (!brel)
+ return 0;
+
+ ma = brel->priv_data;
+
+ if (ma) {
+ ext2fs_free_mem(&ma->entries);
+ ext2fs_free_mem(&ma);
+ }
+ ext2fs_free_mem(&brel->name);
+ ext2fs_free_mem(&brel);
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/check_desc.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/check_desc.c
new file mode 100644
index 0000000..dd4b0e9
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/check_desc.c
@@ -0,0 +1,69 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * check_desc.c --- Check the group descriptors of an ext2 filesystem
+ *
+ * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+/*
+ * This routine sanity checks the group descriptors
+ */
+errcode_t ext2fs_check_desc(ext2_filsys fs)
+{
+ dgrp_t i;
+ blk_t block = fs->super->s_first_data_block;
+ blk_t next;
+
+ EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+ for (i = 0; i < fs->group_desc_count; i++) {
+ next = block + fs->super->s_blocks_per_group;
+ /*
+ * Check to make sure block bitmap for group is
+ * located within the group.
+ */
+ if (fs->group_desc[i].bg_block_bitmap < block ||
+ fs->group_desc[i].bg_block_bitmap >= next)
+ return EXT2_ET_GDESC_BAD_BLOCK_MAP;
+ /*
+ * Check to make sure inode bitmap for group is
+ * located within the group
+ */
+ if (fs->group_desc[i].bg_inode_bitmap < block ||
+ fs->group_desc[i].bg_inode_bitmap >= next)
+ return EXT2_ET_GDESC_BAD_INODE_MAP;
+ /*
+ * Check to make sure inode table for group is located
+ * within the group
+ */
+ if (fs->group_desc[i].bg_inode_table < block ||
+ ((fs->group_desc[i].bg_inode_table +
+ fs->inode_blocks_per_group) >= next))
+ return EXT2_ET_GDESC_BAD_INODE_TABLE;
+
+ block = next;
+ }
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/closefs.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/closefs.c
new file mode 100644
index 0000000..008d5f3
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/closefs.c
@@ -0,0 +1,381 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * closefs.c --- close an ext2 filesystem
+ *
+ * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <time.h>
+#include <string.h>
+
+#include "ext2_fs.h"
+#include "ext2fsP.h"
+
+static int test_root(int a, int b)
+{
+ if (a == 0)
+ return 1;
+ while (1) {
+ if (a == 1)
+ return 1;
+ if (a % b)
+ return 0;
+ a = a / b;
+ }
+}
+
+int ext2fs_bg_has_super(ext2_filsys fs, int group_block)
+{
+ if (!(fs->super->s_feature_ro_compat &
+ EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER))
+ return 1;
+
+ if (test_root(group_block, 3) || (test_root(group_block, 5)) ||
+ test_root(group_block, 7))
+ return 1;
+
+ return 0;
+}
+
+int ext2fs_super_and_bgd_loc(ext2_filsys fs,
+ dgrp_t group,
+ blk_t *ret_super_blk,
+ blk_t *ret_old_desc_blk,
+ blk_t *ret_new_desc_blk,
+ int *ret_meta_bg)
+{
+ blk_t group_block, super_blk = 0, old_desc_blk = 0, new_desc_blk = 0;
+ unsigned int meta_bg, meta_bg_size;
+ int numblocks, has_super;
+ int old_desc_blocks;
+
+ group_block = fs->super->s_first_data_block +
+ (group * fs->super->s_blocks_per_group);
+
+ if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG)
+ old_desc_blocks = fs->super->s_first_meta_bg;
+ else
+ old_desc_blocks =
+ fs->desc_blocks + fs->super->s_reserved_gdt_blocks;
+
+ if (group == fs->group_desc_count-1) {
+ numblocks = (fs->super->s_blocks_count -
+ fs->super->s_first_data_block) %
+ fs->super->s_blocks_per_group;
+ if (!numblocks)
+ numblocks = fs->super->s_blocks_per_group;
+ } else
+ numblocks = fs->super->s_blocks_per_group;
+
+ has_super = ext2fs_bg_has_super(fs, group);
+
+ if (has_super) {
+ super_blk = group_block;
+ numblocks--;
+ }
+ meta_bg_size = (fs->blocksize / sizeof (struct ext2_group_desc));
+ meta_bg = group / meta_bg_size;
+
+ if (!(fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) ||
+ (meta_bg < fs->super->s_first_meta_bg)) {
+ if (has_super) {
+ old_desc_blk = group_block + 1;
+ numblocks -= old_desc_blocks;
+ }
+ } else {
+ if (((group % meta_bg_size) == 0) ||
+ ((group % meta_bg_size) == 1) ||
+ ((group % meta_bg_size) == (meta_bg_size-1))) {
+ if (has_super)
+ has_super = 1;
+ new_desc_blk = group_block + has_super;
+ numblocks--;
+ }
+ }
+
+ numblocks -= 2 + fs->inode_blocks_per_group;
+
+ if (ret_super_blk)
+ *ret_super_blk = super_blk;
+ if (ret_old_desc_blk)
+ *ret_old_desc_blk = old_desc_blk;
+ if (ret_new_desc_blk)
+ *ret_new_desc_blk = new_desc_blk;
+ if (ret_meta_bg)
+ *ret_meta_bg = meta_bg;
+ return numblocks;
+}
+
+
+/*
+ * This function forces out the primary superblock. We need to only
+ * write out those fields which we have changed, since if the
+ * filesystem is mounted, it may have changed some of the other
+ * fields.
+ *
+ * It takes as input a superblock which has already been byte swapped
+ * (if necessary).
+ *
+ */
+static errcode_t write_primary_superblock(ext2_filsys fs,
+ struct ext2_super_block *super)
+{
+ __u16 *old_super, *new_super;
+ int check_idx, write_idx, size;
+ errcode_t retval;
+
+ if (!fs->io->manager->write_byte || !fs->orig_super) {
+ io_channel_set_blksize(fs->io, SUPERBLOCK_OFFSET);
+ retval = io_channel_write_blk(fs->io, 1, -SUPERBLOCK_SIZE,
+ super);
+ io_channel_set_blksize(fs->io, fs->blocksize);
+ return retval;
+ }
+
+ old_super = (__u16 *) fs->orig_super;
+ new_super = (__u16 *) super;
+
+ for (check_idx = 0; check_idx < SUPERBLOCK_SIZE/2; check_idx++) {
+ if (old_super[check_idx] == new_super[check_idx])
+ continue;
+ write_idx = check_idx;
+ for (check_idx++; check_idx < SUPERBLOCK_SIZE/2; check_idx++)
+ if (old_super[check_idx] == new_super[check_idx])
+ break;
+ size = 2 * (check_idx - write_idx);
+ retval = io_channel_write_byte(fs->io,
+ SUPERBLOCK_OFFSET + (2 * write_idx), size,
+ new_super + write_idx);
+ if (retval)
+ return retval;
+ }
+ memcpy(fs->orig_super, super, SUPERBLOCK_SIZE);
+ return 0;
+}
+
+
+/*
+ * Updates the revision to EXT2_DYNAMIC_REV
+ */
+void ext2fs_update_dynamic_rev(ext2_filsys fs)
+{
+ struct ext2_super_block *sb = fs->super;
+
+ if (sb->s_rev_level > EXT2_GOOD_OLD_REV)
+ return;
+
+ sb->s_rev_level = EXT2_DYNAMIC_REV;
+ sb->s_first_ino = EXT2_GOOD_OLD_FIRST_INO;
+ sb->s_inode_size = EXT2_GOOD_OLD_INODE_SIZE;
+ /* s_uuid is handled by e2fsck already */
+ /* other fields should be left alone */
+}
+
+static errcode_t write_backup_super(ext2_filsys fs, dgrp_t group,
+ blk_t group_block,
+ struct ext2_super_block *super_shadow)
+{
+ dgrp_t sgrp = group;
+
+ if (sgrp > ((1 << 16) - 1))
+ sgrp = (1 << 16) - 1;
+#if BB_BIG_ENDIAN
+ if (fs->flags & EXT2_FLAG_SWAP_BYTES)
+ super_shadow->s_block_group_nr = ext2fs_swab16(sgrp);
+ else
+#endif
+ fs->super->s_block_group_nr = sgrp;
+
+ return io_channel_write_blk(fs->io, group_block, -SUPERBLOCK_SIZE,
+ super_shadow);
+}
+
+
+errcode_t ext2fs_flush(ext2_filsys fs)
+{
+ dgrp_t i;
+ blk_t group_block;
+ errcode_t retval;
+ unsigned long fs_state;
+ struct ext2_super_block *super_shadow = 0;
+ struct ext2_group_desc *group_shadow = 0;
+ char *group_ptr;
+ int old_desc_blocks;
+#if BB_BIG_ENDIAN
+ dgrp_t j;
+ struct ext2_group_desc *s, *t;
+#endif
+
+ EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+ fs_state = fs->super->s_state;
+
+ fs->super->s_wtime = time(NULL);
+ fs->super->s_block_group_nr = 0;
+#if BB_BIG_ENDIAN
+ if (fs->flags & EXT2_FLAG_SWAP_BYTES) {
+ retval = EXT2_ET_NO_MEMORY;
+ retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &super_shadow);
+ if (retval)
+ goto errout;
+ retval = ext2fs_get_mem((size_t)(fs->blocksize *
+ fs->desc_blocks),
+ &group_shadow);
+ if (retval)
+ goto errout;
+ memset(group_shadow, 0, (size_t) fs->blocksize *
+ fs->desc_blocks);
+
+ /* swap the group descriptors */
+ for (j=0, s=fs->group_desc, t=group_shadow;
+ j < fs->group_desc_count; j++, t++, s++) {
+ *t = *s;
+ ext2fs_swap_group_desc(t);
+ }
+ } else {
+ super_shadow = fs->super;
+ group_shadow = fs->group_desc;
+ }
+#else
+ super_shadow = fs->super;
+ group_shadow = fs->group_desc;
+#endif
+
+ /*
+ * If this is an external journal device, don't write out the
+ * block group descriptors or any of the backup superblocks
+ */
+ if (fs->super->s_feature_incompat &
+ EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)
+ goto write_primary_superblock_only;
+
+ /*
+ * Set the state of the FS to be non-valid. (The state has
+ * already been backed up earlier, and will be restored after
+ * we write out the backup superblocks.)
+ */
+ fs->super->s_state &= ~EXT2_VALID_FS;
+#if BB_BIG_ENDIAN
+ if (fs->flags & EXT2_FLAG_SWAP_BYTES) {
+ *super_shadow = *fs->super;
+ ext2fs_swap_super(super_shadow);
+ }
+#endif
+
+ /*
+ * Write out the master group descriptors, and the backup
+ * superblocks and group descriptors.
+ */
+ group_block = fs->super->s_first_data_block;
+ group_ptr = (char *) group_shadow;
+ if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG)
+ old_desc_blocks = fs->super->s_first_meta_bg;
+ else
+ old_desc_blocks = fs->desc_blocks;
+
+ for (i = 0; i < fs->group_desc_count; i++) {
+ blk_t super_blk, old_desc_blk, new_desc_blk;
+ int meta_bg;
+
+ ext2fs_super_and_bgd_loc(fs, i, &super_blk, &old_desc_blk,
+ &new_desc_blk, &meta_bg);
+
+ if (!(fs->flags & EXT2_FLAG_MASTER_SB_ONLY) &&i && super_blk) {
+ retval = write_backup_super(fs, i, super_blk,
+ super_shadow);
+ if (retval)
+ goto errout;
+ }
+ if (fs->flags & EXT2_FLAG_SUPER_ONLY)
+ continue;
+ if ((old_desc_blk) &&
+ (!(fs->flags & EXT2_FLAG_MASTER_SB_ONLY) || (i == 0))) {
+ retval = io_channel_write_blk(fs->io,
+ old_desc_blk, old_desc_blocks, group_ptr);
+ if (retval)
+ goto errout;
+ }
+ if (new_desc_blk) {
+ retval = io_channel_write_blk(fs->io, new_desc_blk,
+ 1, group_ptr + (meta_bg*fs->blocksize));
+ if (retval)
+ goto errout;
+ }
+ }
+ fs->super->s_block_group_nr = 0;
+ fs->super->s_state = fs_state;
+#if BB_BIG_ENDIAN
+ if (fs->flags & EXT2_FLAG_SWAP_BYTES) {
+ *super_shadow = *fs->super;
+ ext2fs_swap_super(super_shadow);
+ }
+#endif
+
+ /*
+ * If the write_bitmaps() function is present, call it to
+ * flush the bitmaps. This is done this way so that a simple
+ * program that doesn't mess with the bitmaps doesn't need to
+ * drag in the bitmaps.c code.
+ */
+ if (fs->write_bitmaps) {
+ retval = fs->write_bitmaps(fs);
+ if (retval)
+ goto errout;
+ }
+
+write_primary_superblock_only:
+ /*
+ * Write out master superblock. This has to be done
+ * separately, since it is located at a fixed location
+ * (SUPERBLOCK_OFFSET). We flush all other pending changes
+ * out to disk first, just to avoid a race condition with an
+ * insy-tinsy window....
+ */
+ retval = io_channel_flush(fs->io);
+ retval = write_primary_superblock(fs, super_shadow);
+ if (retval)
+ goto errout;
+
+ fs->flags &= ~EXT2_FLAG_DIRTY;
+
+ retval = io_channel_flush(fs->io);
+errout:
+ fs->super->s_state = fs_state;
+ if (fs->flags & EXT2_FLAG_SWAP_BYTES) {
+ if (super_shadow)
+ ext2fs_free_mem(&super_shadow);
+ if (group_shadow)
+ ext2fs_free_mem(&group_shadow);
+ }
+ return retval;
+}
+
+errcode_t ext2fs_close(ext2_filsys fs)
+{
+ errcode_t retval;
+
+ EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+ if (fs->flags & EXT2_FLAG_DIRTY) {
+ retval = ext2fs_flush(fs);
+ if (retval)
+ return retval;
+ }
+ if (fs->write_bitmaps) {
+ retval = fs->write_bitmaps(fs);
+ if (retval)
+ return retval;
+ }
+ ext2fs_free(fs);
+ return 0;
+}
+
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/cmp_bitmaps.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/cmp_bitmaps.c
new file mode 100644
index 0000000..05b8eb8
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/cmp_bitmaps.c
@@ -0,0 +1,73 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * cmp_bitmaps.c --- routines to compare inode and block bitmaps.
+ *
+ * Copyright (C) 1995 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+errcode_t ext2fs_compare_block_bitmap(ext2fs_block_bitmap bm1,
+ ext2fs_block_bitmap bm2)
+{
+ blk_t i;
+
+ EXT2_CHECK_MAGIC(bm1, EXT2_ET_MAGIC_BLOCK_BITMAP);
+ EXT2_CHECK_MAGIC(bm2, EXT2_ET_MAGIC_BLOCK_BITMAP);
+
+ if ((bm1->start != bm2->start) ||
+ (bm1->end != bm2->end) ||
+ (memcmp(bm1->bitmap, bm2->bitmap,
+ (size_t) (bm1->end - bm1->start)/8)))
+ return EXT2_ET_NEQ_BLOCK_BITMAP;
+
+ for (i = bm1->end - ((bm1->end - bm1->start) % 8); i <= bm1->end; i++)
+ if (ext2fs_fast_test_block_bitmap(bm1, i) !=
+ ext2fs_fast_test_block_bitmap(bm2, i))
+ return EXT2_ET_NEQ_BLOCK_BITMAP;
+
+ return 0;
+}
+
+errcode_t ext2fs_compare_inode_bitmap(ext2fs_inode_bitmap bm1,
+ ext2fs_inode_bitmap bm2)
+{
+ ext2_ino_t i;
+
+ EXT2_CHECK_MAGIC(bm1, EXT2_ET_MAGIC_INODE_BITMAP);
+ EXT2_CHECK_MAGIC(bm2, EXT2_ET_MAGIC_INODE_BITMAP);
+
+ if ((bm1->start != bm2->start) ||
+ (bm1->end != bm2->end) ||
+ (memcmp(bm1->bitmap, bm2->bitmap,
+ (size_t) (bm1->end - bm1->start)/8)))
+ return EXT2_ET_NEQ_INODE_BITMAP;
+
+ for (i = bm1->end - ((bm1->end - bm1->start) % 8); i <= bm1->end; i++)
+ if (ext2fs_fast_test_inode_bitmap(bm1, i) !=
+ ext2fs_fast_test_inode_bitmap(bm2, i))
+ return EXT2_ET_NEQ_INODE_BITMAP;
+
+ return 0;
+}
+
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/dblist.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/dblist.c
new file mode 100644
index 0000000..06ff6d8
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/dblist.c
@@ -0,0 +1,260 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * dblist.c -- directory block list functions
+ *
+ * Copyright 1997 by Theodore Ts'o
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ *
+ */
+
+#include <stdio.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <string.h>
+#include <time.h>
+
+#include "ext2_fs.h"
+#include "ext2fsP.h"
+
+static int dir_block_cmp(const void *a, const void *b);
+
+/*
+ * Returns the number of directories in the filesystem as reported by
+ * the group descriptors. Of course, the group descriptors could be
+ * wrong!
+ */
+errcode_t ext2fs_get_num_dirs(ext2_filsys fs, ext2_ino_t *ret_num_dirs)
+{
+ dgrp_t i;
+ ext2_ino_t num_dirs, max_dirs;
+
+ EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+ num_dirs = 0;
+ max_dirs = fs->super->s_inodes_per_group;
+ for (i = 0; i < fs->group_desc_count; i++) {
+ if (fs->group_desc[i].bg_used_dirs_count > max_dirs)
+ num_dirs += max_dirs / 8;
+ else
+ num_dirs += fs->group_desc[i].bg_used_dirs_count;
+ }
+ if (num_dirs > fs->super->s_inodes_count)
+ num_dirs = fs->super->s_inodes_count;
+
+ *ret_num_dirs = num_dirs;
+
+ return 0;
+}
+
+/*
+ * helper function for making a new directory block list (for
+ * initialize and copy).
+ */
+static errcode_t make_dblist(ext2_filsys fs, ext2_ino_t size, ext2_ino_t count,
+ struct ext2_db_entry *list,
+ ext2_dblist *ret_dblist)
+{
+ ext2_dblist dblist;
+ errcode_t retval;
+ size_t len;
+
+ EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+ if ((ret_dblist == 0) && fs->dblist &&
+ (fs->dblist->magic == EXT2_ET_MAGIC_DBLIST))
+ return 0;
+
+ retval = ext2fs_get_mem(sizeof(struct ext2_struct_dblist), &dblist);
+ if (retval)
+ return retval;
+ memset(dblist, 0, sizeof(struct ext2_struct_dblist));
+
+ dblist->magic = EXT2_ET_MAGIC_DBLIST;
+ dblist->fs = fs;
+ if (size)
+ dblist->size = size;
+ else {
+ retval = ext2fs_get_num_dirs(fs, &dblist->size);
+ if (retval)
+ goto cleanup;
+ dblist->size = (dblist->size * 2) + 12;
+ }
+ len = (size_t) sizeof(struct ext2_db_entry) * dblist->size;
+ dblist->count = count;
+ retval = ext2fs_get_mem(len, &dblist->list);
+ if (retval)
+ goto cleanup;
+
+ if (list)
+ memcpy(dblist->list, list, len);
+ else
+ memset(dblist->list, 0, len);
+ if (ret_dblist)
+ *ret_dblist = dblist;
+ else
+ fs->dblist = dblist;
+ return 0;
+cleanup:
+ ext2fs_free_mem(&dblist);
+ return retval;
+}
+
+/*
+ * Initialize a directory block list
+ */
+errcode_t ext2fs_init_dblist(ext2_filsys fs, ext2_dblist *ret_dblist)
+{
+ ext2_dblist dblist;
+ errcode_t retval;
+
+ retval = make_dblist(fs, 0, 0, 0, &dblist);
+ if (retval)
+ return retval;
+
+ dblist->sorted = 1;
+ if (ret_dblist)
+ *ret_dblist = dblist;
+ else
+ fs->dblist = dblist;
+
+ return 0;
+}
+
+/*
+ * Copy a directory block list
+ */
+errcode_t ext2fs_copy_dblist(ext2_dblist src, ext2_dblist *dest)
+{
+ ext2_dblist dblist;
+ errcode_t retval;
+
+ retval = make_dblist(src->fs, src->size, src->count, src->list,
+ &dblist);
+ if (retval)
+ return retval;
+ dblist->sorted = src->sorted;
+ *dest = dblist;
+ return 0;
+}
+
+/*
+ * Close a directory block list
+ *
+ * (moved to closefs.c)
+ */
+
+
+/*
+ * Add a directory block to the directory block list
+ */
+errcode_t ext2fs_add_dir_block(ext2_dblist dblist, ext2_ino_t ino, blk_t blk,
+ int blockcnt)
+{
+ struct ext2_db_entry *new_entry;
+ errcode_t retval;
+ unsigned long old_size;
+
+ EXT2_CHECK_MAGIC(dblist, EXT2_ET_MAGIC_DBLIST);
+
+ if (dblist->count >= dblist->size) {
+ old_size = dblist->size * sizeof(struct ext2_db_entry);
+ dblist->size += 100;
+ retval = ext2fs_resize_mem(old_size, (size_t) dblist->size *
+ sizeof(struct ext2_db_entry),
+ &dblist->list);
+ if (retval) {
+ dblist->size -= 100;
+ return retval;
+ }
+ }
+ new_entry = dblist->list + ( (int) dblist->count++);
+ new_entry->blk = blk;
+ new_entry->ino = ino;
+ new_entry->blockcnt = blockcnt;
+
+ dblist->sorted = 0;
+
+ return 0;
+}
+
+/*
+ * Change the directory block to the directory block list
+ */
+errcode_t ext2fs_set_dir_block(ext2_dblist dblist, ext2_ino_t ino, blk_t blk,
+ int blockcnt)
+{
+ dgrp_t i;
+
+ EXT2_CHECK_MAGIC(dblist, EXT2_ET_MAGIC_DBLIST);
+
+ for (i=0; i < dblist->count; i++) {
+ if ((dblist->list[i].ino != ino) ||
+ (dblist->list[i].blockcnt != blockcnt))
+ continue;
+ dblist->list[i].blk = blk;
+ dblist->sorted = 0;
+ return 0;
+ }
+ return EXT2_ET_DB_NOT_FOUND;
+}
+
+void ext2fs_dblist_sort(ext2_dblist dblist,
+ int (*sortfunc)(const void *,
+ const void *))
+{
+ if (!sortfunc)
+ sortfunc = dir_block_cmp;
+ qsort(dblist->list, (size_t) dblist->count,
+ sizeof(struct ext2_db_entry), sortfunc);
+ dblist->sorted = 1;
+}
+
+/*
+ * This function iterates over the directory block list
+ */
+errcode_t ext2fs_dblist_iterate(ext2_dblist dblist,
+ int (*func)(ext2_filsys fs,
+ struct ext2_db_entry *db_info,
+ void *priv_data),
+ void *priv_data)
+{
+ ext2_ino_t i;
+ int ret;
+
+ EXT2_CHECK_MAGIC(dblist, EXT2_ET_MAGIC_DBLIST);
+
+ if (!dblist->sorted)
+ ext2fs_dblist_sort(dblist, 0);
+ for (i=0; i < dblist->count; i++) {
+ ret = (*func)(dblist->fs, &dblist->list[(int)i], priv_data);
+ if (ret & DBLIST_ABORT)
+ return 0;
+ }
+ return 0;
+}
+
+static int dir_block_cmp(const void *a, const void *b)
+{
+ const struct ext2_db_entry *db_a =
+ (const struct ext2_db_entry *) a;
+ const struct ext2_db_entry *db_b =
+ (const struct ext2_db_entry *) b;
+
+ if (db_a->blk != db_b->blk)
+ return (int) (db_a->blk - db_b->blk);
+
+ if (db_a->ino != db_b->ino)
+ return (int) (db_a->ino - db_b->ino);
+
+ return (int) (db_a->blockcnt - db_b->blockcnt);
+}
+
+int ext2fs_dblist_count(ext2_dblist dblist)
+{
+ return (int) dblist->count;
+}
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/dblist_dir.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/dblist_dir.c
new file mode 100644
index 0000000..b239204
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/dblist_dir.c
@@ -0,0 +1,76 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * dblist_dir.c --- iterate by directory entry
+ *
+ * Copyright 1997 by Theodore Ts'o
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ *
+ */
+
+#include <stdio.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <string.h>
+#include <time.h>
+
+#include "ext2_fs.h"
+#include "ext2fsP.h"
+
+static int db_dir_proc(ext2_filsys fs, struct ext2_db_entry *db_info,
+ void *priv_data);
+
+errcode_t ext2fs_dblist_dir_iterate(ext2_dblist dblist,
+ int flags,
+ char *block_buf,
+ int (*func)(ext2_ino_t dir,
+ int entry,
+ struct ext2_dir_entry *dirent,
+ int offset,
+ int blocksize,
+ char *buf,
+ void *priv_data),
+ void *priv_data)
+{
+ errcode_t retval;
+ struct dir_context ctx;
+
+ EXT2_CHECK_MAGIC(dblist, EXT2_ET_MAGIC_DBLIST);
+
+ ctx.dir = 0;
+ ctx.flags = flags;
+ if (block_buf)
+ ctx.buf = block_buf;
+ else {
+ retval = ext2fs_get_mem(dblist->fs->blocksize, &ctx.buf);
+ if (retval)
+ return retval;
+ }
+ ctx.func = func;
+ ctx.priv_data = priv_data;
+ ctx.errcode = 0;
+
+ retval = ext2fs_dblist_iterate(dblist, db_dir_proc, &ctx);
+
+ if (!block_buf)
+ ext2fs_free_mem(&ctx.buf);
+ if (retval)
+ return retval;
+ return ctx.errcode;
+}
+
+static int db_dir_proc(ext2_filsys fs, struct ext2_db_entry *db_info,
+ void *priv_data)
+{
+ struct dir_context *ctx;
+
+ ctx = (struct dir_context *) priv_data;
+ ctx->dir = db_info->ino;
+
+ return ext2fs_process_dir_block(fs, &db_info->blk,
+ db_info->blockcnt, 0, 0, priv_data);
+}
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/dir_iterate.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/dir_iterate.c
new file mode 100644
index 0000000..b7d8735
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/dir_iterate.c
@@ -0,0 +1,220 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * dir_iterate.c --- ext2fs directory iteration operations
+ *
+ * Copyright (C) 1993, 1994, 1994, 1995, 1996, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fsP.h"
+
+/*
+ * This function checks to see whether or not a potential deleted
+ * directory entry looks valid. What we do is check the deleted entry
+ * and each successive entry to make sure that they all look valid and
+ * that the last deleted entry ends at the beginning of the next
+ * undeleted entry. Returns 1 if the deleted entry looks valid, zero
+ * if not valid.
+ */
+static int ext2fs_validate_entry(char *buf, int offset, int final_offset)
+{
+ struct ext2_dir_entry *dirent;
+
+ while (offset < final_offset) {
+ dirent = (struct ext2_dir_entry *)(buf + offset);
+ offset += dirent->rec_len;
+ if ((dirent->rec_len < 8) ||
+ ((dirent->rec_len % 4) != 0) ||
+ (((dirent->name_len & 0xFF)+8) > dirent->rec_len))
+ return 0;
+ }
+ return (offset == final_offset);
+}
+
+errcode_t ext2fs_dir_iterate2(ext2_filsys fs,
+ ext2_ino_t dir,
+ int flags,
+ char *block_buf,
+ int (*func)(ext2_ino_t dir,
+ int entry,
+ struct ext2_dir_entry *dirent,
+ int offset,
+ int blocksize,
+ char *buf,
+ void *priv_data),
+ void *priv_data)
+{
+ struct dir_context ctx;
+ errcode_t retval;
+
+ EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+ retval = ext2fs_check_directory(fs, dir);
+ if (retval)
+ return retval;
+
+ ctx.dir = dir;
+ ctx.flags = flags;
+ if (block_buf)
+ ctx.buf = block_buf;
+ else {
+ retval = ext2fs_get_mem(fs->blocksize, &ctx.buf);
+ if (retval)
+ return retval;
+ }
+ ctx.func = func;
+ ctx.priv_data = priv_data;
+ ctx.errcode = 0;
+ retval = ext2fs_block_iterate2(fs, dir, 0, 0,
+ ext2fs_process_dir_block, &ctx);
+ if (!block_buf)
+ ext2fs_free_mem(&ctx.buf);
+ if (retval)
+ return retval;
+ return ctx.errcode;
+}
+
+struct xlate {
+ int (*func)(struct ext2_dir_entry *dirent,
+ int offset,
+ int blocksize,
+ char *buf,
+ void *priv_data);
+ void *real_private;
+};
+
+static int xlate_func(ext2_ino_t dir EXT2FS_ATTR((unused)),
+ int entry EXT2FS_ATTR((unused)),
+ struct ext2_dir_entry *dirent, int offset,
+ int blocksize, char *buf, void *priv_data)
+{
+ struct xlate *xl = (struct xlate *) priv_data;
+
+ return (*xl->func)(dirent, offset, blocksize, buf, xl->real_private);
+}
+
+extern errcode_t ext2fs_dir_iterate(ext2_filsys fs,
+ ext2_ino_t dir,
+ int flags,
+ char *block_buf,
+ int (*func)(struct ext2_dir_entry *dirent,
+ int offset,
+ int blocksize,
+ char *buf,
+ void *priv_data),
+ void *priv_data)
+{
+ struct xlate xl;
+
+ xl.real_private = priv_data;
+ xl.func = func;
+
+ return ext2fs_dir_iterate2(fs, dir, flags, block_buf,
+ xlate_func, &xl);
+}
+
+
+/*
+ * Helper function which is private to this module. Used by
+ * ext2fs_dir_iterate() and ext2fs_dblist_dir_iterate()
+ */
+int ext2fs_process_dir_block(ext2_filsys fs,
+ blk_t *blocknr,
+ e2_blkcnt_t blockcnt,
+ blk_t ref_block EXT2FS_ATTR((unused)),
+ int ref_offset EXT2FS_ATTR((unused)),
+ void *priv_data)
+{
+ struct dir_context *ctx = (struct dir_context *) priv_data;
+ unsigned int offset = 0;
+ unsigned int next_real_entry = 0;
+ int ret = 0;
+ int changed = 0;
+ int do_abort = 0;
+ int entry, size;
+ struct ext2_dir_entry *dirent;
+
+ if (blockcnt < 0)
+ return 0;
+
+ entry = blockcnt ? DIRENT_OTHER_FILE : DIRENT_DOT_FILE;
+
+ ctx->errcode = ext2fs_read_dir_block(fs, *blocknr, ctx->buf);
+ if (ctx->errcode)
+ return BLOCK_ABORT;
+
+ while (offset < fs->blocksize) {
+ dirent = (struct ext2_dir_entry *) (ctx->buf + offset);
+ if (((offset + dirent->rec_len) > fs->blocksize) ||
+ (dirent->rec_len < 8) ||
+ ((dirent->rec_len % 4) != 0) ||
+ (((dirent->name_len & 0xFF)+8) > dirent->rec_len)) {
+ ctx->errcode = EXT2_ET_DIR_CORRUPTED;
+ return BLOCK_ABORT;
+ }
+ if (!dirent->inode &&
+ !(ctx->flags & DIRENT_FLAG_INCLUDE_EMPTY))
+ goto next;
+
+ ret = (ctx->func)(ctx->dir,
+ (next_real_entry > offset) ?
+ DIRENT_DELETED_FILE : entry,
+ dirent, offset,
+ fs->blocksize, ctx->buf,
+ ctx->priv_data);
+ if (entry < DIRENT_OTHER_FILE)
+ entry++;
+
+ if (ret & DIRENT_CHANGED)
+ changed++;
+ if (ret & DIRENT_ABORT) {
+ do_abort++;
+ break;
+ }
+next:
+ if (next_real_entry == offset)
+ next_real_entry += dirent->rec_len;
+
+ if (ctx->flags & DIRENT_FLAG_INCLUDE_REMOVED) {
+ size = ((dirent->name_len & 0xFF) + 11) & ~3;
+
+ if (dirent->rec_len != size) {
+ unsigned int final_offset;
+
+ final_offset = offset + dirent->rec_len;
+ offset += size;
+ while (offset < final_offset &&
+ !ext2fs_validate_entry(ctx->buf,
+ offset,
+ final_offset))
+ offset += 4;
+ continue;
+ }
+ }
+ offset += dirent->rec_len;
+ }
+
+ if (changed) {
+ ctx->errcode = ext2fs_write_dir_block(fs, *blocknr, ctx->buf);
+ if (ctx->errcode)
+ return BLOCK_ABORT;
+ }
+ if (do_abort)
+ return BLOCK_ABORT;
+ return 0;
+}
+
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/dirblock.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/dirblock.c
new file mode 100644
index 0000000..5d3f6a1
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/dirblock.c
@@ -0,0 +1,133 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * dirblock.c --- directory block routines.
+ *
+ * Copyright (C) 1995, 1996 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <string.h>
+#include <time.h>
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+errcode_t ext2fs_read_dir_block2(ext2_filsys fs, blk_t block,
+ void *buf, int flags EXT2FS_ATTR((unused)))
+{
+ errcode_t retval;
+ char *p, *end;
+ struct ext2_dir_entry *dirent;
+ unsigned int name_len, rec_len;
+#if BB_BIG_ENDIAN
+ unsigned int do_swap;
+#endif
+
+ retval = io_channel_read_blk(fs->io, block, 1, buf);
+ if (retval)
+ return retval;
+#if BB_BIG_ENDIAN
+ do_swap = (fs->flags & (EXT2_FLAG_SWAP_BYTES|
+ EXT2_FLAG_SWAP_BYTES_READ)) != 0;
+#endif
+ p = (char *) buf;
+ end = (char *) buf + fs->blocksize;
+ while (p < end-8) {
+ dirent = (struct ext2_dir_entry *) p;
+#if BB_BIG_ENDIAN
+ if (do_swap) {
+ dirent->inode = ext2fs_swab32(dirent->inode);
+ dirent->rec_len = ext2fs_swab16(dirent->rec_len);
+ dirent->name_len = ext2fs_swab16(dirent->name_len);
+ }
+#endif
+ name_len = dirent->name_len;
+#ifdef WORDS_BIGENDIAN
+ if (flags & EXT2_DIRBLOCK_V2_STRUCT)
+ dirent->name_len = ext2fs_swab16(dirent->name_len);
+#endif
+ rec_len = dirent->rec_len;
+ if ((rec_len < 8) || (rec_len % 4)) {
+ rec_len = 8;
+ retval = EXT2_ET_DIR_CORRUPTED;
+ }
+ if (((name_len & 0xFF) + 8) > dirent->rec_len)
+ retval = EXT2_ET_DIR_CORRUPTED;
+ p += rec_len;
+ }
+ return retval;
+}
+
+errcode_t ext2fs_read_dir_block(ext2_filsys fs, blk_t block,
+ void *buf)
+{
+ return ext2fs_read_dir_block2(fs, block, buf, 0);
+}
+
+
+errcode_t ext2fs_write_dir_block2(ext2_filsys fs, blk_t block,
+ void *inbuf, int flags EXT2FS_ATTR((unused)))
+{
+#if BB_BIG_ENDIAN
+ int do_swap = 0;
+ errcode_t retval;
+ char *p, *end;
+ char *buf = 0;
+ struct ext2_dir_entry *dirent;
+
+ if ((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
+ (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE))
+ do_swap = 1;
+
+#ifndef WORDS_BIGENDIAN
+ if (!do_swap)
+ return io_channel_write_blk(fs->io, block, 1, (char *) inbuf);
+#endif
+
+ retval = ext2fs_get_mem(fs->blocksize, &buf);
+ if (retval)
+ return retval;
+ memcpy(buf, inbuf, fs->blocksize);
+ p = buf;
+ end = buf + fs->blocksize;
+ while (p < end) {
+ dirent = (struct ext2_dir_entry *) p;
+ if ((dirent->rec_len < 8) ||
+ (dirent->rec_len % 4)) {
+ ext2fs_free_mem(&buf);
+ return EXT2_ET_DIR_CORRUPTED;
+ }
+ p += dirent->rec_len;
+ if (do_swap) {
+ dirent->inode = ext2fs_swab32(dirent->inode);
+ dirent->rec_len = ext2fs_swab16(dirent->rec_len);
+ dirent->name_len = ext2fs_swab16(dirent->name_len);
+ }
+#ifdef WORDS_BIGENDIAN
+ if (flags & EXT2_DIRBLOCK_V2_STRUCT)
+ dirent->name_len = ext2fs_swab16(dirent->name_len);
+#endif
+ }
+ retval = io_channel_write_blk(fs->io, block, 1, buf);
+ ext2fs_free_mem(&buf);
+ return retval;
+#else
+ return io_channel_write_blk(fs->io, block, 1, (char *) inbuf);
+#endif
+}
+
+
+errcode_t ext2fs_write_dir_block(ext2_filsys fs, blk_t block,
+ void *inbuf)
+{
+ return ext2fs_write_dir_block2(fs, block, inbuf, 0);
+}
+
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/dirhash.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/dirhash.c
new file mode 100644
index 0000000..ab3243f
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/dirhash.c
@@ -0,0 +1,234 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * dirhash.c -- Calculate the hash of a directory entry
+ *
+ * Copyright (c) 2001 Daniel Phillips
+ *
+ * Copyright (c) 2002 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+/*
+ * Keyed 32-bit hash function using TEA in a Davis-Meyer function
+ * H0 = Key
+ * Hi = E Mi(Hi-1) + Hi-1
+ *
+ * (see Applied Cryptography, 2nd edition, p448).
+ *
+ * Jeremy Fitzhardinge <jeremy@zip.com.au> 1998
+ *
+ * This code is made available under the terms of the GPL
+ */
+#define DELTA 0x9E3779B9
+
+static void TEA_transform(__u32 buf[4], __u32 const in[])
+{
+ __u32 sum = 0;
+ __u32 b0 = buf[0], b1 = buf[1];
+ __u32 a = in[0], b = in[1], c = in[2], d = in[3];
+ int n = 16;
+
+ do {
+ sum += DELTA;
+ b0 += ((b1 << 4)+a) ^ (b1+sum) ^ ((b1 >> 5)+b);
+ b1 += ((b0 << 4)+c) ^ (b0+sum) ^ ((b0 >> 5)+d);
+ } while(--n);
+
+ buf[0] += b0;
+ buf[1] += b1;
+}
+
+/* F, G and H are basic MD4 functions: selection, majority, parity */
+#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
+#define G(x, y, z) (((x) & (y)) + (((x) ^ (y)) & (z)))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+
+/*
+ * The generic round function. The application is so specific that
+ * we don't bother protecting all the arguments with parens, as is generally
+ * good macro practice, in favor of extra legibility.
+ * Rotation is separate from addition to prevent recomputation
+ */
+#define ROUND(f, a, b, c, d, x, s) \
+ (a += f(b, c, d) + x, a = (a << s) | (a >> (32-s)))
+#define K1 0
+#define K2 013240474631UL
+#define K3 015666365641UL
+
+/*
+ * Basic cut-down MD4 transform. Returns only 32 bits of result.
+ */
+static void halfMD4Transform (__u32 buf[4], __u32 const in[])
+{
+ __u32 a = buf[0], b = buf[1], c = buf[2], d = buf[3];
+
+ /* Round 1 */
+ ROUND(F, a, b, c, d, in[0] + K1, 3);
+ ROUND(F, d, a, b, c, in[1] + K1, 7);
+ ROUND(F, c, d, a, b, in[2] + K1, 11);
+ ROUND(F, b, c, d, a, in[3] + K1, 19);
+ ROUND(F, a, b, c, d, in[4] + K1, 3);
+ ROUND(F, d, a, b, c, in[5] + K1, 7);
+ ROUND(F, c, d, a, b, in[6] + K1, 11);
+ ROUND(F, b, c, d, a, in[7] + K1, 19);
+
+ /* Round 2 */
+ ROUND(G, a, b, c, d, in[1] + K2, 3);
+ ROUND(G, d, a, b, c, in[3] + K2, 5);
+ ROUND(G, c, d, a, b, in[5] + K2, 9);
+ ROUND(G, b, c, d, a, in[7] + K2, 13);
+ ROUND(G, a, b, c, d, in[0] + K2, 3);
+ ROUND(G, d, a, b, c, in[2] + K2, 5);
+ ROUND(G, c, d, a, b, in[4] + K2, 9);
+ ROUND(G, b, c, d, a, in[6] + K2, 13);
+
+ /* Round 3 */
+ ROUND(H, a, b, c, d, in[3] + K3, 3);
+ ROUND(H, d, a, b, c, in[7] + K3, 9);
+ ROUND(H, c, d, a, b, in[2] + K3, 11);
+ ROUND(H, b, c, d, a, in[6] + K3, 15);
+ ROUND(H, a, b, c, d, in[1] + K3, 3);
+ ROUND(H, d, a, b, c, in[5] + K3, 9);
+ ROUND(H, c, d, a, b, in[0] + K3, 11);
+ ROUND(H, b, c, d, a, in[4] + K3, 15);
+
+ buf[0] += a;
+ buf[1] += b;
+ buf[2] += c;
+ buf[3] += d;
+}
+
+#undef ROUND
+#undef F
+#undef G
+#undef H
+#undef K1
+#undef K2
+#undef K3
+
+/* The old legacy hash */
+static ext2_dirhash_t dx_hack_hash (const char *name, int len)
+{
+ __u32 hash0 = 0x12a3fe2d, hash1 = 0x37abe8f9;
+ while (len--) {
+ __u32 hash = hash1 + (hash0 ^ (*name++ * 7152373));
+
+ if (hash & 0x80000000) hash -= 0x7fffffff;
+ hash1 = hash0;
+ hash0 = hash;
+ }
+ return (hash0 << 1);
+}
+
+static void str2hashbuf(const char *msg, int len, __u32 *buf, int num)
+{
+ __u32 pad, val;
+ int i;
+
+ pad = (__u32)len | ((__u32)len << 8);
+ pad |= pad << 16;
+
+ val = pad;
+ if (len > num*4)
+ len = num * 4;
+ for (i=0; i < len; i++) {
+ if ((i % 4) == 0)
+ val = pad;
+ val = msg[i] + (val << 8);
+ if ((i % 4) == 3) {
+ *buf++ = val;
+ val = pad;
+ num--;
+ }
+ }
+ if (--num >= 0)
+ *buf++ = val;
+ while (--num >= 0)
+ *buf++ = pad;
+}
+
+/*
+ * Returns the hash of a filename. If len is 0 and name is NULL, then
+ * this function can be used to test whether or not a hash version is
+ * supported.
+ *
+ * The seed is an 4 longword (32 bits) "secret" which can be used to
+ * uniquify a hash. If the seed is all zero's, then some default seed
+ * may be used.
+ *
+ * A particular hash version specifies whether or not the seed is
+ * represented, and whether or not the returned hash is 32 bits or 64
+ * bits. 32 bit hashes will return 0 for the minor hash.
+ */
+errcode_t ext2fs_dirhash(int version, const char *name, int len,
+ const __u32 *seed,
+ ext2_dirhash_t *ret_hash,
+ ext2_dirhash_t *ret_minor_hash)
+{
+ __u32 hash;
+ __u32 minor_hash = 0;
+ const char *p;
+ int i;
+ __u32 in[8], buf[4];
+
+ /* Initialize the default seed for the hash checksum functions */
+ buf[0] = 0x67452301;
+ buf[1] = 0xefcdab89;
+ buf[2] = 0x98badcfe;
+ buf[3] = 0x10325476;
+
+ /* Check to see if the seed is all zero's */
+ if (seed) {
+ for (i=0; i < 4; i++) {
+ if (seed[i])
+ break;
+ }
+ if (i < 4)
+ memcpy(buf, seed, sizeof(buf));
+ }
+
+ switch (version) {
+ case EXT2_HASH_LEGACY:
+ hash = dx_hack_hash(name, len);
+ break;
+ case EXT2_HASH_HALF_MD4:
+ p = name;
+ while (len > 0) {
+ str2hashbuf(p, len, in, 8);
+ halfMD4Transform(buf, in);
+ len -= 32;
+ p += 32;
+ }
+ minor_hash = buf[2];
+ hash = buf[1];
+ break;
+ case EXT2_HASH_TEA:
+ p = name;
+ while (len > 0) {
+ str2hashbuf(p, len, in, 4);
+ TEA_transform(buf, in);
+ len -= 16;
+ p += 16;
+ }
+ hash = buf[0];
+ minor_hash = buf[1];
+ break;
+ default:
+ *ret_hash = 0;
+ return EXT2_ET_DIRHASH_UNSUPP;
+ }
+ *ret_hash = hash & ~1;
+ if (ret_minor_hash)
+ *ret_minor_hash = minor_hash;
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/dupfs.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/dupfs.c
new file mode 100644
index 0000000..203c29f
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/dupfs.c
@@ -0,0 +1,97 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * dupfs.c --- duplicate a ext2 filesystem handle
+ *
+ * Copyright (C) 1997, 1998, 2001, 2003, 2005 by Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <time.h>
+#include <string.h>
+
+#include "ext2_fs.h"
+#include "ext2fsP.h"
+
+errcode_t ext2fs_dup_handle(ext2_filsys src, ext2_filsys *dest)
+{
+ ext2_filsys fs;
+ errcode_t retval;
+
+ EXT2_CHECK_MAGIC(src, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+ retval = ext2fs_get_mem(sizeof(struct struct_ext2_filsys), &fs);
+ if (retval)
+ return retval;
+
+ *fs = *src;
+ fs->device_name = 0;
+ fs->super = 0;
+ fs->orig_super = 0;
+ fs->group_desc = 0;
+ fs->inode_map = 0;
+ fs->block_map = 0;
+ fs->badblocks = 0;
+ fs->dblist = 0;
+
+ io_channel_bumpcount(fs->io);
+ if (fs->icache)
+ fs->icache->refcount++;
+
+ retval = ext2fs_get_mem(strlen(src->device_name)+1, &fs->device_name);
+ if (retval)
+ goto errout;
+ strcpy(fs->device_name, src->device_name);
+
+ retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &fs->super);
+ if (retval)
+ goto errout;
+ memcpy(fs->super, src->super, SUPERBLOCK_SIZE);
+
+ retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &fs->orig_super);
+ if (retval)
+ goto errout;
+ memcpy(fs->orig_super, src->orig_super, SUPERBLOCK_SIZE);
+
+ retval = ext2fs_get_mem((size_t) fs->desc_blocks * fs->blocksize,
+ &fs->group_desc);
+ if (retval)
+ goto errout;
+ memcpy(fs->group_desc, src->group_desc,
+ (size_t) fs->desc_blocks * fs->blocksize);
+
+ if (src->inode_map) {
+ retval = ext2fs_copy_bitmap(src->inode_map, &fs->inode_map);
+ if (retval)
+ goto errout;
+ }
+ if (src->block_map) {
+ retval = ext2fs_copy_bitmap(src->block_map, &fs->block_map);
+ if (retval)
+ goto errout;
+ }
+ if (src->badblocks) {
+ retval = ext2fs_badblocks_copy(src->badblocks, &fs->badblocks);
+ if (retval)
+ goto errout;
+ }
+ if (src->dblist) {
+ retval = ext2fs_copy_dblist(src->dblist, &fs->dblist);
+ if (retval)
+ goto errout;
+ }
+ *dest = fs;
+ return 0;
+errout:
+ ext2fs_free(fs);
+ return retval;
+
+}
+
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/e2image.h b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/e2image.h
new file mode 100644
index 0000000..8d38ecc
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/e2image.h
@@ -0,0 +1,52 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * e2image.h --- header file describing the ext2 image format
+ *
+ * Copyright (C) 2000 Theodore Ts'o.
+ *
+ * Note: this uses the POSIX IO interfaces, unlike most of the other
+ * functions in this library. So sue me.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+
+struct ext2_image_hdr {
+ __u32 magic_number; /* This must be EXT2_ET_MAGIC_E2IMAGE */
+ char magic_descriptor[16]; /* "Ext2 Image 1.0", w/ null padding */
+ char fs_hostname[64];/* Hostname of machine of image */
+ char fs_netaddr[32]; /* Network address */
+ __u32 fs_netaddr_type;/* 0 = IPV4, 1 = IPV6, etc. */
+ __u32 fs_device; /* Device number of image */
+ char fs_device_name[64]; /* Device name */
+ char fs_uuid[16]; /* UUID of filesystem */
+ __u32 fs_blocksize; /* Block size of the filesystem */
+ __u32 fs_reserved[8];
+
+ __u32 image_device; /* Device number of image file */
+ __u32 image_inode; /* Inode number of image file */
+ __u32 image_time; /* Time of image creation */
+ __u32 image_reserved[8];
+
+ __u32 offset_super; /* Byte offset of the sb and descriptors */
+ __u32 offset_inode; /* Byte offset of the inode table */
+ __u32 offset_inodemap; /* Byte offset of the inode bitmaps */
+ __u32 offset_blockmap; /* Byte offset of the inode bitmaps */
+ __u32 offset_reserved[8];
+};
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/expanddir.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/expanddir.c
new file mode 100644
index 0000000..8a29ae5
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/expanddir.c
@@ -0,0 +1,127 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * expand.c --- expand an ext2fs directory
+ *
+ * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+struct expand_dir_struct {
+ int done;
+ int newblocks;
+ errcode_t err;
+};
+
+static int expand_dir_proc(ext2_filsys fs,
+ blk_t *blocknr,
+ e2_blkcnt_t blockcnt,
+ blk_t ref_block EXT2FS_ATTR((unused)),
+ int ref_offset EXT2FS_ATTR((unused)),
+ void *priv_data)
+{
+ struct expand_dir_struct *es = (struct expand_dir_struct *) priv_data;
+ blk_t new_blk;
+ static blk_t last_blk = 0;
+ char *block;
+ errcode_t retval;
+
+ if (*blocknr) {
+ last_blk = *blocknr;
+ return 0;
+ }
+ retval = ext2fs_new_block(fs, last_blk, 0, &new_blk);
+ if (retval) {
+ es->err = retval;
+ return BLOCK_ABORT;
+ }
+ if (blockcnt > 0) {
+ retval = ext2fs_new_dir_block(fs, 0, 0, &block);
+ if (retval) {
+ es->err = retval;
+ return BLOCK_ABORT;
+ }
+ es->done = 1;
+ retval = ext2fs_write_dir_block(fs, new_blk, block);
+ } else {
+ retval = ext2fs_get_mem(fs->blocksize, &block);
+ if (retval) {
+ es->err = retval;
+ return BLOCK_ABORT;
+ }
+ memset(block, 0, fs->blocksize);
+ retval = io_channel_write_blk(fs->io, new_blk, 1, block);
+ }
+ if (retval) {
+ es->err = retval;
+ return BLOCK_ABORT;
+ }
+ ext2fs_free_mem(&block);
+ *blocknr = new_blk;
+ ext2fs_block_alloc_stats(fs, new_blk, +1);
+ es->newblocks++;
+
+ if (es->done)
+ return (BLOCK_CHANGED | BLOCK_ABORT);
+ else
+ return BLOCK_CHANGED;
+}
+
+errcode_t ext2fs_expand_dir(ext2_filsys fs, ext2_ino_t dir)
+{
+ errcode_t retval;
+ struct expand_dir_struct es;
+ struct ext2_inode inode;
+
+ EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+ if (!(fs->flags & EXT2_FLAG_RW))
+ return EXT2_ET_RO_FILSYS;
+
+ if (!fs->block_map)
+ return EXT2_ET_NO_BLOCK_BITMAP;
+
+ retval = ext2fs_check_directory(fs, dir);
+ if (retval)
+ return retval;
+
+ es.done = 0;
+ es.err = 0;
+ es.newblocks = 0;
+
+ retval = ext2fs_block_iterate2(fs, dir, BLOCK_FLAG_APPEND,
+ 0, expand_dir_proc, &es);
+
+ if (es.err)
+ return es.err;
+ if (!es.done)
+ return EXT2_ET_EXPAND_DIR_ERR;
+
+ /*
+ * Update the size and block count fields in the inode.
+ */
+ retval = ext2fs_read_inode(fs, dir, &inode);
+ if (retval)
+ return retval;
+
+ inode.i_size += fs->blocksize;
+ inode.i_blocks += (fs->blocksize / 512) * es.newblocks;
+
+ retval = ext2fs_write_inode(fs, dir, &inode);
+ if (retval)
+ return retval;
+
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/ext2_err.h b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/ext2_err.h
new file mode 100644
index 0000000..ead3528
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/ext2_err.h
@@ -0,0 +1,116 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ext2_err.h:
+ * This file is automatically generated; please do not edit it.
+ */
+
+#define EXT2_ET_BASE (2133571328L)
+#define EXT2_ET_MAGIC_EXT2FS_FILSYS (2133571329L)
+#define EXT2_ET_MAGIC_BADBLOCKS_LIST (2133571330L)
+#define EXT2_ET_MAGIC_BADBLOCKS_ITERATE (2133571331L)
+#define EXT2_ET_MAGIC_INODE_SCAN (2133571332L)
+#define EXT2_ET_MAGIC_IO_CHANNEL (2133571333L)
+#define EXT2_ET_MAGIC_UNIX_IO_CHANNEL (2133571334L)
+#define EXT2_ET_MAGIC_IO_MANAGER (2133571335L)
+#define EXT2_ET_MAGIC_BLOCK_BITMAP (2133571336L)
+#define EXT2_ET_MAGIC_INODE_BITMAP (2133571337L)
+#define EXT2_ET_MAGIC_GENERIC_BITMAP (2133571338L)
+#define EXT2_ET_MAGIC_TEST_IO_CHANNEL (2133571339L)
+#define EXT2_ET_MAGIC_DBLIST (2133571340L)
+#define EXT2_ET_MAGIC_ICOUNT (2133571341L)
+#define EXT2_ET_MAGIC_PQ_IO_CHANNEL (2133571342L)
+#define EXT2_ET_MAGIC_EXT2_FILE (2133571343L)
+#define EXT2_ET_MAGIC_E2IMAGE (2133571344L)
+#define EXT2_ET_MAGIC_INODE_IO_CHANNEL (2133571345L)
+#define EXT2_ET_MAGIC_RESERVED_9 (2133571346L)
+#define EXT2_ET_BAD_MAGIC (2133571347L)
+#define EXT2_ET_REV_TOO_HIGH (2133571348L)
+#define EXT2_ET_RO_FILSYS (2133571349L)
+#define EXT2_ET_GDESC_READ (2133571350L)
+#define EXT2_ET_GDESC_WRITE (2133571351L)
+#define EXT2_ET_GDESC_BAD_BLOCK_MAP (2133571352L)
+#define EXT2_ET_GDESC_BAD_INODE_MAP (2133571353L)
+#define EXT2_ET_GDESC_BAD_INODE_TABLE (2133571354L)
+#define EXT2_ET_INODE_BITMAP_WRITE (2133571355L)
+#define EXT2_ET_INODE_BITMAP_READ (2133571356L)
+#define EXT2_ET_BLOCK_BITMAP_WRITE (2133571357L)
+#define EXT2_ET_BLOCK_BITMAP_READ (2133571358L)
+#define EXT2_ET_INODE_TABLE_WRITE (2133571359L)
+#define EXT2_ET_INODE_TABLE_READ (2133571360L)
+#define EXT2_ET_NEXT_INODE_READ (2133571361L)
+#define EXT2_ET_UNEXPECTED_BLOCK_SIZE (2133571362L)
+#define EXT2_ET_DIR_CORRUPTED (2133571363L)
+#define EXT2_ET_SHORT_READ (2133571364L)
+#define EXT2_ET_SHORT_WRITE (2133571365L)
+#define EXT2_ET_DIR_NO_SPACE (2133571366L)
+#define EXT2_ET_NO_INODE_BITMAP (2133571367L)
+#define EXT2_ET_NO_BLOCK_BITMAP (2133571368L)
+#define EXT2_ET_BAD_INODE_NUM (2133571369L)
+#define EXT2_ET_BAD_BLOCK_NUM (2133571370L)
+#define EXT2_ET_EXPAND_DIR_ERR (2133571371L)
+#define EXT2_ET_TOOSMALL (2133571372L)
+#define EXT2_ET_BAD_BLOCK_MARK (2133571373L)
+#define EXT2_ET_BAD_BLOCK_UNMARK (2133571374L)
+#define EXT2_ET_BAD_BLOCK_TEST (2133571375L)
+#define EXT2_ET_BAD_INODE_MARK (2133571376L)
+#define EXT2_ET_BAD_INODE_UNMARK (2133571377L)
+#define EXT2_ET_BAD_INODE_TEST (2133571378L)
+#define EXT2_ET_FUDGE_BLOCK_BITMAP_END (2133571379L)
+#define EXT2_ET_FUDGE_INODE_BITMAP_END (2133571380L)
+#define EXT2_ET_BAD_IND_BLOCK (2133571381L)
+#define EXT2_ET_BAD_DIND_BLOCK (2133571382L)
+#define EXT2_ET_BAD_TIND_BLOCK (2133571383L)
+#define EXT2_ET_NEQ_BLOCK_BITMAP (2133571384L)
+#define EXT2_ET_NEQ_INODE_BITMAP (2133571385L)
+#define EXT2_ET_BAD_DEVICE_NAME (2133571386L)
+#define EXT2_ET_MISSING_INODE_TABLE (2133571387L)
+#define EXT2_ET_CORRUPT_SUPERBLOCK (2133571388L)
+#define EXT2_ET_BAD_GENERIC_MARK (2133571389L)
+#define EXT2_ET_BAD_GENERIC_UNMARK (2133571390L)
+#define EXT2_ET_BAD_GENERIC_TEST (2133571391L)
+#define EXT2_ET_SYMLINK_LOOP (2133571392L)
+#define EXT2_ET_CALLBACK_NOTHANDLED (2133571393L)
+#define EXT2_ET_BAD_BLOCK_IN_INODE_TABLE (2133571394L)
+#define EXT2_ET_UNSUPP_FEATURE (2133571395L)
+#define EXT2_ET_RO_UNSUPP_FEATURE (2133571396L)
+#define EXT2_ET_LLSEEK_FAILED (2133571397L)
+#define EXT2_ET_NO_MEMORY (2133571398L)
+#define EXT2_ET_INVALID_ARGUMENT (2133571399L)
+#define EXT2_ET_BLOCK_ALLOC_FAIL (2133571400L)
+#define EXT2_ET_INODE_ALLOC_FAIL (2133571401L)
+#define EXT2_ET_NO_DIRECTORY (2133571402L)
+#define EXT2_ET_TOO_MANY_REFS (2133571403L)
+#define EXT2_ET_FILE_NOT_FOUND (2133571404L)
+#define EXT2_ET_FILE_RO (2133571405L)
+#define EXT2_ET_DB_NOT_FOUND (2133571406L)
+#define EXT2_ET_DIR_EXISTS (2133571407L)
+#define EXT2_ET_UNIMPLEMENTED (2133571408L)
+#define EXT2_ET_CANCEL_REQUESTED (2133571409L)
+#define EXT2_ET_FILE_TOO_BIG (2133571410L)
+#define EXT2_ET_JOURNAL_NOT_BLOCK (2133571411L)
+#define EXT2_ET_NO_JOURNAL_SB (2133571412L)
+#define EXT2_ET_JOURNAL_TOO_SMALL (2133571413L)
+#define EXT2_ET_JOURNAL_UNSUPP_VERSION (2133571414L)
+#define EXT2_ET_LOAD_EXT_JOURNAL (2133571415L)
+#define EXT2_ET_NO_JOURNAL (2133571416L)
+#define EXT2_ET_DIRHASH_UNSUPP (2133571417L)
+#define EXT2_ET_BAD_EA_BLOCK_NUM (2133571418L)
+#define EXT2_ET_TOO_MANY_INODES (2133571419L)
+#define EXT2_ET_NOT_IMAGE_FILE (2133571420L)
+#define EXT2_ET_RES_GDT_BLOCKS (2133571421L)
+#define EXT2_ET_RESIZE_INODE_CORRUPT (2133571422L)
+#define EXT2_ET_SET_BMAP_NO_IND (2133571423L)
+
+#if 0
+extern const struct error_table et_ext2_error_table;
+extern void initialize_ext2_error_table(void);
+
+/* For compatibility with Heimdal */
+extern void initialize_ext2_error_table_r(struct et_list **list);
+
+#define ERROR_TABLE_BASE_ext2 (2133571328L)
+
+/* for compatibility with older versions... */
+#define init_ext2_err_tbl initialize_ext2_error_table
+#define ext2_err_base ERROR_TABLE_BASE_ext2
+#endif
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/ext2_ext_attr.h b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/ext2_ext_attr.h
new file mode 100644
index 0000000..cc91bb8
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/ext2_ext_attr.h
@@ -0,0 +1,53 @@
+/* vi: set sw=4 ts=4: */
+/*
+ File: linux/ext2_ext_attr.h
+
+ On-disk format of extended attributes for the ext2 filesystem.
+
+ (C) 2000 Andreas Gruenbacher, <a.gruenbacher@computer.org>
+*/
+
+/* Magic value in attribute blocks */
+#define EXT2_EXT_ATTR_MAGIC_v1 0xEA010000
+#define EXT2_EXT_ATTR_MAGIC 0xEA020000
+
+/* Maximum number of references to one attribute block */
+#define EXT2_EXT_ATTR_REFCOUNT_MAX 1024
+
+struct ext2_ext_attr_header {
+ __u32 h_magic; /* magic number for identification */
+ __u32 h_refcount; /* reference count */
+ __u32 h_blocks; /* number of disk blocks used */
+ __u32 h_hash; /* hash value of all attributes */
+ __u32 h_reserved[4]; /* zero right now */
+};
+
+struct ext2_ext_attr_entry {
+ __u8 e_name_len; /* length of name */
+ __u8 e_name_index; /* attribute name index */
+ __u16 e_value_offs; /* offset in disk block of value */
+ __u32 e_value_block; /* disk block attribute is stored on (n/i) */
+ __u32 e_value_size; /* size of attribute value */
+ __u32 e_hash; /* hash value of name and value */
+};
+
+#define EXT2_EXT_ATTR_PAD_BITS 2
+#define EXT2_EXT_ATTR_PAD (1<<EXT2_EXT_ATTR_PAD_BITS)
+#define EXT2_EXT_ATTR_ROUND (EXT2_EXT_ATTR_PAD-1)
+#define EXT2_EXT_ATTR_LEN(name_len) \
+ (((name_len) + EXT2_EXT_ATTR_ROUND + \
+ sizeof(struct ext2_ext_attr_entry)) & ~EXT2_EXT_ATTR_ROUND)
+#define EXT2_EXT_ATTR_NEXT(entry) \
+ ( (struct ext2_ext_attr_entry *)( \
+ (char *)(entry) + EXT2_EXT_ATTR_LEN((entry)->e_name_len)) )
+#define EXT2_EXT_ATTR_SIZE(size) \
+ (((size) + EXT2_EXT_ATTR_ROUND) & ~EXT2_EXT_ATTR_ROUND)
+#define EXT2_EXT_IS_LAST_ENTRY(entry) (*((__u32 *)(entry)) == 0UL)
+#define EXT2_EXT_ATTR_NAME(entry) \
+ (((char *) (entry)) + sizeof(struct ext2_ext_attr_entry))
+#define EXT2_XATTR_LEN(name_len) \
+ (((name_len) + EXT2_EXT_ATTR_ROUND + \
+ sizeof(struct ext2_xattr_entry)) & ~EXT2_EXT_ATTR_ROUND)
+#define EXT2_XATTR_SIZE(size) \
+ (((size) + EXT2_EXT_ATTR_ROUND) & ~EXT2_EXT_ATTR_ROUND)
+
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/ext2_fs.h b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/ext2_fs.h
new file mode 100644
index 0000000..cb49d7a
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/ext2_fs.h
@@ -0,0 +1,570 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * linux/include/linux/ext2_fs.h
+ *
+ * Copyright (C) 1992, 1993, 1994, 1995
+ * Remy Card (card@masi.ibp.fr)
+ * Laboratoire MASI - Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ * from
+ *
+ * linux/include/linux/minix_fs.h
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ */
+
+#ifndef _LINUX_EXT2_FS_H
+#define _LINUX_EXT2_FS_H
+
+#include "ext2_types.h" /* Changed from linux/types.h */
+
+/*
+ * Special inode numbers
+ */
+#define EXT2_BAD_INO 1 /* Bad blocks inode */
+#define EXT2_ROOT_INO 2 /* Root inode */
+#define EXT2_ACL_IDX_INO 3 /* ACL inode */
+#define EXT2_ACL_DATA_INO 4 /* ACL inode */
+#define EXT2_BOOT_LOADER_INO 5 /* Boot loader inode */
+#define EXT2_UNDEL_DIR_INO 6 /* Undelete directory inode */
+#define EXT2_RESIZE_INO 7 /* Reserved group descriptors inode */
+#define EXT2_JOURNAL_INO 8 /* Journal inode */
+
+/* First non-reserved inode for old ext2 filesystems */
+#define EXT2_GOOD_OLD_FIRST_INO 11
+
+/*
+ * The second extended file system magic number
+ */
+#define EXT2_SUPER_MAGIC 0xEF53
+
+/* Assume that user mode programs are passing in an ext2fs superblock, not
+ * a kernel struct super_block. This will allow us to call the feature-test
+ * macros from user land. */
+#define EXT2_SB(sb) (sb)
+
+/*
+ * Maximal count of links to a file
+ */
+#define EXT2_LINK_MAX 32000
+
+/*
+ * Macro-instructions used to manage several block sizes
+ */
+#define EXT2_MIN_BLOCK_LOG_SIZE 10 /* 1024 */
+#define EXT2_MAX_BLOCK_LOG_SIZE 16 /* 65536 */
+#define EXT2_MIN_BLOCK_SIZE (1 << EXT2_MIN_BLOCK_LOG_SIZE)
+#define EXT2_MAX_BLOCK_SIZE (1 << EXT2_MAX_BLOCK_LOG_SIZE)
+#define EXT2_BLOCK_SIZE(s) (EXT2_MIN_BLOCK_SIZE << (s)->s_log_block_size)
+#define EXT2_BLOCK_SIZE_BITS(s) ((s)->s_log_block_size + 10)
+#define EXT2_INODE_SIZE(s) (((s)->s_rev_level == EXT2_GOOD_OLD_REV) ? \
+ EXT2_GOOD_OLD_INODE_SIZE : (s)->s_inode_size)
+#define EXT2_FIRST_INO(s) (((s)->s_rev_level == EXT2_GOOD_OLD_REV) ? \
+ EXT2_GOOD_OLD_FIRST_INO : (s)->s_first_ino)
+#define EXT2_ADDR_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof(__u32))
+
+/*
+ * Macro-instructions used to manage fragments
+ */
+#define EXT2_MIN_FRAG_SIZE EXT2_MIN_BLOCK_SIZE
+#define EXT2_MAX_FRAG_SIZE EXT2_MAX_BLOCK_SIZE
+#define EXT2_MIN_FRAG_LOG_SIZE EXT2_MIN_BLOCK_LOG_SIZE
+# define EXT2_FRAG_SIZE(s) (EXT2_MIN_FRAG_SIZE << (s)->s_log_frag_size)
+# define EXT2_FRAGS_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / EXT2_FRAG_SIZE(s))
+
+/*
+ * ACL structures
+ */
+struct ext2_acl_header /* Header of Access Control Lists */
+{
+ __u32 aclh_size;
+ __u32 aclh_file_count;
+ __u32 aclh_acle_count;
+ __u32 aclh_first_acle;
+};
+
+struct ext2_acl_entry /* Access Control List Entry */
+{
+ __u32 acle_size;
+ __u16 acle_perms; /* Access permissions */
+ __u16 acle_type; /* Type of entry */
+ __u16 acle_tag; /* User or group identity */
+ __u16 acle_pad1;
+ __u32 acle_next; /* Pointer on next entry for the */
+ /* same inode or on next free entry */
+};
+
+/*
+ * Structure of a blocks group descriptor
+ */
+struct ext2_group_desc
+{
+ __u32 bg_block_bitmap; /* Blocks bitmap block */
+ __u32 bg_inode_bitmap; /* Inodes bitmap block */
+ __u32 bg_inode_table; /* Inodes table block */
+ __u16 bg_free_blocks_count; /* Free blocks count */
+ __u16 bg_free_inodes_count; /* Free inodes count */
+ __u16 bg_used_dirs_count; /* Directories count */
+ __u16 bg_pad;
+ __u32 bg_reserved[3];
+};
+
+/*
+ * Data structures used by the directory indexing feature
+ *
+ * Note: all of the multibyte integer fields are little endian.
+ */
+
+/*
+ * Note: dx_root_info is laid out so that if it should somehow get
+ * overlaid by a dirent the two low bits of the hash version will be
+ * zero. Therefore, the hash version mod 4 should never be 0.
+ * Sincerely, the paranoia department.
+ */
+struct ext2_dx_root_info {
+ __u32 reserved_zero;
+ __u8 hash_version; /* 0 now, 1 at release */
+ __u8 info_length; /* 8 */
+ __u8 indirect_levels;
+ __u8 unused_flags;
+};
+
+#define EXT2_HASH_LEGACY 0
+#define EXT2_HASH_HALF_MD4 1
+#define EXT2_HASH_TEA 2
+
+#define EXT2_HASH_FLAG_INCOMPAT 0x1
+
+struct ext2_dx_entry {
+ __u32 hash;
+ __u32 block;
+};
+
+struct ext2_dx_countlimit {
+ __u16 limit;
+ __u16 count;
+};
+
+
+/*
+ * Macro-instructions used to manage group descriptors
+ */
+#define EXT2_BLOCKS_PER_GROUP(s) (EXT2_SB(s)->s_blocks_per_group)
+#define EXT2_INODES_PER_GROUP(s) (EXT2_SB(s)->s_inodes_per_group)
+#define EXT2_INODES_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s)/EXT2_INODE_SIZE(s))
+/* limits imposed by 16-bit value gd_free_{blocks,inode}_count */
+#define EXT2_MAX_BLOCKS_PER_GROUP(s) ((1 << 16) - 8)
+#define EXT2_MAX_INODES_PER_GROUP(s) ((1 << 16) - EXT2_INODES_PER_BLOCK(s))
+#define EXT2_DESC_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof (struct ext2_group_desc))
+
+/*
+ * Constants relative to the data blocks
+ */
+#define EXT2_NDIR_BLOCKS 12
+#define EXT2_IND_BLOCK EXT2_NDIR_BLOCKS
+#define EXT2_DIND_BLOCK (EXT2_IND_BLOCK + 1)
+#define EXT2_TIND_BLOCK (EXT2_DIND_BLOCK + 1)
+#define EXT2_N_BLOCKS (EXT2_TIND_BLOCK + 1)
+
+/*
+ * Inode flags
+ */
+#define EXT2_SECRM_FL 0x00000001 /* Secure deletion */
+#define EXT2_UNRM_FL 0x00000002 /* Undelete */
+#define EXT2_COMPR_FL 0x00000004 /* Compress file */
+#define EXT2_SYNC_FL 0x00000008 /* Synchronous updates */
+#define EXT2_IMMUTABLE_FL 0x00000010 /* Immutable file */
+#define EXT2_APPEND_FL 0x00000020 /* writes to file may only append */
+#define EXT2_NODUMP_FL 0x00000040 /* do not dump file */
+#define EXT2_NOATIME_FL 0x00000080 /* do not update atime */
+/* Reserved for compression usage... */
+#define EXT2_DIRTY_FL 0x00000100
+#define EXT2_COMPRBLK_FL 0x00000200 /* One or more compressed clusters */
+#define EXT2_NOCOMPR_FL 0x00000400 /* Access raw compressed data */
+#define EXT2_ECOMPR_FL 0x00000800 /* Compression error */
+/* End compression flags --- maybe not all used */
+#define EXT2_BTREE_FL 0x00001000 /* btree format dir */
+#define EXT2_INDEX_FL 0x00001000 /* hash-indexed directory */
+#define EXT2_IMAGIC_FL 0x00002000
+#define EXT3_JOURNAL_DATA_FL 0x00004000 /* file data should be journaled */
+#define EXT2_NOTAIL_FL 0x00008000 /* file tail should not be merged */
+#define EXT2_DIRSYNC_FL 0x00010000 /* Synchronous directory modifications */
+#define EXT2_TOPDIR_FL 0x00020000 /* Top of directory hierarchies*/
+#define EXT3_EXTENTS_FL 0x00080000 /* Inode uses extents */
+#define EXT2_RESERVED_FL 0x80000000 /* reserved for ext2 lib */
+
+#define EXT2_FL_USER_VISIBLE 0x0003DFFF /* User visible flags */
+#define EXT2_FL_USER_MODIFIABLE 0x000080FF /* User modifiable flags */
+
+/*
+ * ioctl commands
+ */
+#define EXT2_IOC_GETFLAGS _IOR('f', 1, long)
+#define EXT2_IOC_SETFLAGS _IOW('f', 2, long)
+#define EXT2_IOC_GETVERSION _IOR('v', 1, long)
+#define EXT2_IOC_SETVERSION _IOW('v', 2, long)
+
+/*
+ * Structure of an inode on the disk
+ */
+struct ext2_inode {
+ __u16 i_mode; /* File mode */
+ __u16 i_uid; /* Low 16 bits of Owner Uid */
+ __u32 i_size; /* Size in bytes */
+ __u32 i_atime; /* Access time */
+ __u32 i_ctime; /* Creation time */
+ __u32 i_mtime; /* Modification time */
+ __u32 i_dtime; /* Deletion Time */
+ __u16 i_gid; /* Low 16 bits of Group Id */
+ __u16 i_links_count; /* Links count */
+ __u32 i_blocks; /* Blocks count */
+ __u32 i_flags; /* File flags */
+ union {
+ struct {
+ __u32 l_i_reserved1;
+ } linux1;
+ struct {
+ __u32 h_i_translator;
+ } hurd1;
+ struct {
+ __u32 m_i_reserved1;
+ } masix1;
+ } osd1; /* OS dependent 1 */
+ __u32 i_block[EXT2_N_BLOCKS];/* Pointers to blocks */
+ __u32 i_generation; /* File version (for NFS) */
+ __u32 i_file_acl; /* File ACL */
+ __u32 i_dir_acl; /* Directory ACL */
+ __u32 i_faddr; /* Fragment address */
+ union {
+ struct {
+ __u8 l_i_frag; /* Fragment number */
+ __u8 l_i_fsize; /* Fragment size */
+ __u16 i_pad1;
+ __u16 l_i_uid_high; /* these 2 fields */
+ __u16 l_i_gid_high; /* were reserved2[0] */
+ __u32 l_i_reserved2;
+ } linux2;
+ struct {
+ __u8 h_i_frag; /* Fragment number */
+ __u8 h_i_fsize; /* Fragment size */
+ __u16 h_i_mode_high;
+ __u16 h_i_uid_high;
+ __u16 h_i_gid_high;
+ __u32 h_i_author;
+ } hurd2;
+ struct {
+ __u8 m_i_frag; /* Fragment number */
+ __u8 m_i_fsize; /* Fragment size */
+ __u16 m_pad1;
+ __u32 m_i_reserved2[2];
+ } masix2;
+ } osd2; /* OS dependent 2 */
+};
+
+/*
+ * Permanent part of an large inode on the disk
+ */
+struct ext2_inode_large {
+ __u16 i_mode; /* File mode */
+ __u16 i_uid; /* Low 16 bits of Owner Uid */
+ __u32 i_size; /* Size in bytes */
+ __u32 i_atime; /* Access time */
+ __u32 i_ctime; /* Creation time */
+ __u32 i_mtime; /* Modification time */
+ __u32 i_dtime; /* Deletion Time */
+ __u16 i_gid; /* Low 16 bits of Group Id */
+ __u16 i_links_count; /* Links count */
+ __u32 i_blocks; /* Blocks count */
+ __u32 i_flags; /* File flags */
+ union {
+ struct {
+ __u32 l_i_reserved1;
+ } linux1;
+ struct {
+ __u32 h_i_translator;
+ } hurd1;
+ struct {
+ __u32 m_i_reserved1;
+ } masix1;
+ } osd1; /* OS dependent 1 */
+ __u32 i_block[EXT2_N_BLOCKS];/* Pointers to blocks */
+ __u32 i_generation; /* File version (for NFS) */
+ __u32 i_file_acl; /* File ACL */
+ __u32 i_dir_acl; /* Directory ACL */
+ __u32 i_faddr; /* Fragment address */
+ union {
+ struct {
+ __u8 l_i_frag; /* Fragment number */
+ __u8 l_i_fsize; /* Fragment size */
+ __u16 i_pad1;
+ __u16 l_i_uid_high; /* these 2 fields */
+ __u16 l_i_gid_high; /* were reserved2[0] */
+ __u32 l_i_reserved2;
+ } linux2;
+ struct {
+ __u8 h_i_frag; /* Fragment number */
+ __u8 h_i_fsize; /* Fragment size */
+ __u16 h_i_mode_high;
+ __u16 h_i_uid_high;
+ __u16 h_i_gid_high;
+ __u32 h_i_author;
+ } hurd2;
+ struct {
+ __u8 m_i_frag; /* Fragment number */
+ __u8 m_i_fsize; /* Fragment size */
+ __u16 m_pad1;
+ __u32 m_i_reserved2[2];
+ } masix2;
+ } osd2; /* OS dependent 2 */
+ __u16 i_extra_isize;
+ __u16 i_pad1;
+};
+
+#define i_size_high i_dir_acl
+
+/*
+ * File system states
+ */
+#define EXT2_VALID_FS 0x0001 /* Unmounted cleanly */
+#define EXT2_ERROR_FS 0x0002 /* Errors detected */
+
+/*
+ * Mount flags
+ */
+#define EXT2_MOUNT_CHECK 0x0001 /* Do mount-time checks */
+#define EXT2_MOUNT_GRPID 0x0004 /* Create files with directory's group */
+#define EXT2_MOUNT_DEBUG 0x0008 /* Some debugging messages */
+#define EXT2_MOUNT_ERRORS_CONT 0x0010 /* Continue on errors */
+#define EXT2_MOUNT_ERRORS_RO 0x0020 /* Remount fs ro on errors */
+#define EXT2_MOUNT_ERRORS_PANIC 0x0040 /* Panic on errors */
+#define EXT2_MOUNT_MINIX_DF 0x0080 /* Mimics the Minix statfs */
+#define EXT2_MOUNT_NO_UID32 0x0200 /* Disable 32-bit UIDs */
+
+#define clear_opt(o, opt) o &= ~EXT2_MOUNT_##opt
+#define set_opt(o, opt) o |= EXT2_MOUNT_##opt
+#define test_opt(sb, opt) (EXT2_SB(sb)->s_mount_opt & \
+ EXT2_MOUNT_##opt)
+/*
+ * Maximal mount counts between two filesystem checks
+ */
+#define EXT2_DFL_MAX_MNT_COUNT 20 /* Allow 20 mounts */
+#define EXT2_DFL_CHECKINTERVAL 0 /* Don't use interval check */
+
+/*
+ * Behaviour when detecting errors
+ */
+#define EXT2_ERRORS_CONTINUE 1 /* Continue execution */
+#define EXT2_ERRORS_RO 2 /* Remount fs read-only */
+#define EXT2_ERRORS_PANIC 3 /* Panic */
+#define EXT2_ERRORS_DEFAULT EXT2_ERRORS_CONTINUE
+
+/*
+ * Structure of the super block
+ */
+struct ext2_super_block {
+ __u32 s_inodes_count; /* Inodes count */
+ __u32 s_blocks_count; /* Blocks count */
+ __u32 s_r_blocks_count; /* Reserved blocks count */
+ __u32 s_free_blocks_count; /* Free blocks count */
+ __u32 s_free_inodes_count; /* Free inodes count */
+ __u32 s_first_data_block; /* First Data Block */
+ __u32 s_log_block_size; /* Block size */
+ __s32 s_log_frag_size; /* Fragment size */
+ __u32 s_blocks_per_group; /* # Blocks per group */
+ __u32 s_frags_per_group; /* # Fragments per group */
+ __u32 s_inodes_per_group; /* # Inodes per group */
+ __u32 s_mtime; /* Mount time */
+ __u32 s_wtime; /* Write time */
+ __u16 s_mnt_count; /* Mount count */
+ __s16 s_max_mnt_count; /* Maximal mount count */
+ __u16 s_magic; /* Magic signature */
+ __u16 s_state; /* File system state */
+ __u16 s_errors; /* Behaviour when detecting errors */
+ __u16 s_minor_rev_level; /* minor revision level */
+ __u32 s_lastcheck; /* time of last check */
+ __u32 s_checkinterval; /* max. time between checks */
+ __u32 s_creator_os; /* OS */
+ __u32 s_rev_level; /* Revision level */
+ __u16 s_def_resuid; /* Default uid for reserved blocks */
+ __u16 s_def_resgid; /* Default gid for reserved blocks */
+ /*
+ * These fields are for EXT2_DYNAMIC_REV superblocks only.
+ *
+ * Note: the difference between the compatible feature set and
+ * the incompatible feature set is that if there is a bit set
+ * in the incompatible feature set that the kernel doesn't
+ * know about, it should refuse to mount the filesystem.
+ *
+ * e2fsck's requirements are more strict; if it doesn't know
+ * about a feature in either the compatible or incompatible
+ * feature set, it must abort and not try to meddle with
+ * things it doesn't understand...
+ */
+ __u32 s_first_ino; /* First non-reserved inode */
+ __u16 s_inode_size; /* size of inode structure */
+ __u16 s_block_group_nr; /* block group # of this superblock */
+ __u32 s_feature_compat; /* compatible feature set */
+ __u32 s_feature_incompat; /* incompatible feature set */
+ __u32 s_feature_ro_compat; /* readonly-compatible feature set */
+ __u8 s_uuid[16]; /* 128-bit uuid for volume */
+ char s_volume_name[16]; /* volume name */
+ char s_last_mounted[64]; /* directory where last mounted */
+ __u32 s_algorithm_usage_bitmap; /* For compression */
+ /*
+ * Performance hints. Directory preallocation should only
+ * happen if the EXT2_FEATURE_COMPAT_DIR_PREALLOC flag is on.
+ */
+ __u8 s_prealloc_blocks; /* Nr of blocks to try to preallocate*/
+ __u8 s_prealloc_dir_blocks; /* Nr to preallocate for dirs */
+ __u16 s_reserved_gdt_blocks; /* Per group table for online growth */
+ /*
+ * Journaling support valid if EXT2_FEATURE_COMPAT_HAS_JOURNAL set.
+ */
+ __u8 s_journal_uuid[16]; /* uuid of journal superblock */
+ __u32 s_journal_inum; /* inode number of journal file */
+ __u32 s_journal_dev; /* device number of journal file */
+ __u32 s_last_orphan; /* start of list of inodes to delete */
+ __u32 s_hash_seed[4]; /* HTREE hash seed */
+ __u8 s_def_hash_version; /* Default hash version to use */
+ __u8 s_jnl_backup_type; /* Default type of journal backup */
+ __u16 s_reserved_word_pad;
+ __u32 s_default_mount_opts;
+ __u32 s_first_meta_bg; /* First metablock group */
+ __u32 s_mkfs_time; /* When the filesystem was created */
+ __u32 s_jnl_blocks[17]; /* Backup of the journal inode */
+ __u32 s_reserved[172]; /* Padding to the end of the block */
+};
+
+/*
+ * Codes for operating systems
+ */
+#define EXT2_OS_LINUX 0
+#define EXT2_OS_HURD 1
+#define EXT2_OS_MASIX 2
+#define EXT2_OS_FREEBSD 3
+#define EXT2_OS_LITES 4
+
+/*
+ * Revision levels
+ */
+#define EXT2_GOOD_OLD_REV 0 /* The good old (original) format */
+#define EXT2_DYNAMIC_REV 1 /* V2 format w/ dynamic inode sizes */
+
+#define EXT2_CURRENT_REV EXT2_GOOD_OLD_REV
+#define EXT2_MAX_SUPP_REV EXT2_DYNAMIC_REV
+
+#define EXT2_GOOD_OLD_INODE_SIZE 128
+
+/*
+ * Journal inode backup types
+ */
+#define EXT3_JNL_BACKUP_BLOCKS 1
+
+/*
+ * Feature set definitions
+ */
+
+#define EXT2_HAS_COMPAT_FEATURE(sb,mask) \
+ ( EXT2_SB(sb)->s_feature_compat & (mask) )
+#define EXT2_HAS_RO_COMPAT_FEATURE(sb,mask) \
+ ( EXT2_SB(sb)->s_feature_ro_compat & (mask) )
+#define EXT2_HAS_INCOMPAT_FEATURE(sb,mask) \
+ ( EXT2_SB(sb)->s_feature_incompat & (mask) )
+
+#define EXT2_FEATURE_COMPAT_DIR_PREALLOC 0x0001
+#define EXT2_FEATURE_COMPAT_IMAGIC_INODES 0x0002
+#define EXT3_FEATURE_COMPAT_HAS_JOURNAL 0x0004
+#define EXT2_FEATURE_COMPAT_EXT_ATTR 0x0008
+#define EXT2_FEATURE_COMPAT_RESIZE_INODE 0x0010
+#define EXT2_FEATURE_COMPAT_DIR_INDEX 0x0020
+
+#define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER 0x0001
+#define EXT2_FEATURE_RO_COMPAT_LARGE_FILE 0x0002
+/* #define EXT2_FEATURE_RO_COMPAT_BTREE_DIR 0x0004 not used */
+
+#define EXT2_FEATURE_INCOMPAT_COMPRESSION 0x0001
+#define EXT2_FEATURE_INCOMPAT_FILETYPE 0x0002
+#define EXT3_FEATURE_INCOMPAT_RECOVER 0x0004 /* Needs recovery */
+#define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV 0x0008 /* Journal device */
+#define EXT2_FEATURE_INCOMPAT_META_BG 0x0010
+#define EXT3_FEATURE_INCOMPAT_EXTENTS 0x0040
+
+
+#define EXT2_FEATURE_COMPAT_SUPP 0
+#define EXT2_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE)
+#define EXT2_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \
+ EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \
+ EXT2_FEATURE_RO_COMPAT_BTREE_DIR)
+
+/*
+ * Default values for user and/or group using reserved blocks
+ */
+#define EXT2_DEF_RESUID 0
+#define EXT2_DEF_RESGID 0
+
+/*
+ * Default mount options
+ */
+#define EXT2_DEFM_DEBUG 0x0001
+#define EXT2_DEFM_BSDGROUPS 0x0002
+#define EXT2_DEFM_XATTR_USER 0x0004
+#define EXT2_DEFM_ACL 0x0008
+#define EXT2_DEFM_UID16 0x0010
+#define EXT3_DEFM_JMODE 0x0060
+#define EXT3_DEFM_JMODE_DATA 0x0020
+#define EXT3_DEFM_JMODE_ORDERED 0x0040
+#define EXT3_DEFM_JMODE_WBACK 0x0060
+
+/*
+ * Structure of a directory entry
+ */
+#define EXT2_NAME_LEN 255
+
+struct ext2_dir_entry {
+ __u32 inode; /* Inode number */
+ __u16 rec_len; /* Directory entry length */
+ __u16 name_len; /* Name length */
+ char name[EXT2_NAME_LEN]; /* File name */
+};
+
+/*
+ * The new version of the directory entry. Since EXT2 structures are
+ * stored in intel byte order, and the name_len field could never be
+ * bigger than 255 chars, it's safe to reclaim the extra byte for the
+ * file_type field.
+ */
+struct ext2_dir_entry_2 {
+ __u32 inode; /* Inode number */
+ __u16 rec_len; /* Directory entry length */
+ __u8 name_len; /* Name length */
+ __u8 file_type;
+ char name[EXT2_NAME_LEN]; /* File name */
+};
+
+/*
+ * Ext2 directory file types. Only the low 3 bits are used. The
+ * other bits are reserved for now.
+ */
+#define EXT2_FT_UNKNOWN 0
+#define EXT2_FT_REG_FILE 1
+#define EXT2_FT_DIR 2
+#define EXT2_FT_CHRDEV 3
+#define EXT2_FT_BLKDEV 4
+#define EXT2_FT_FIFO 5
+#define EXT2_FT_SOCK 6
+#define EXT2_FT_SYMLINK 7
+
+#define EXT2_FT_MAX 8
+
+/*
+ * EXT2_DIR_PAD defines the directory entries boundaries
+ *
+ * NOTE: It must be a multiple of 4
+ */
+#define EXT2_DIR_PAD 4
+#define EXT2_DIR_ROUND (EXT2_DIR_PAD - 1)
+#define EXT2_DIR_REC_LEN(name_len) (((name_len) + 8 + EXT2_DIR_ROUND) & \
+ ~EXT2_DIR_ROUND)
+
+#endif /* _LINUX_EXT2_FS_H */
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/ext2_io.h b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/ext2_io.h
new file mode 100644
index 0000000..e6c9630
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/ext2_io.h
@@ -0,0 +1,114 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * io.h --- the I/O manager abstraction
+ *
+ * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#ifndef _EXT2FS_EXT2_IO_H
+#define _EXT2FS_EXT2_IO_H
+
+/*
+ * ext2_loff_t is defined here since unix_io.c needs it.
+ */
+#if defined(__GNUC__) || defined(HAS_LONG_LONG)
+typedef long long ext2_loff_t;
+#else
+typedef long ext2_loff_t;
+#endif
+
+/* llseek.c */
+/* ext2_loff_t ext2fs_llseek (int, ext2_loff_t, int); */
+#ifdef CONFIG_LFS
+# define ext2fs_llseek lseek64
+#else
+# define ext2fs_llseek lseek
+#endif
+
+typedef struct struct_io_manager *io_manager;
+typedef struct struct_io_channel *io_channel;
+
+#define CHANNEL_FLAGS_WRITETHROUGH 0x01
+
+struct struct_io_channel {
+ errcode_t magic;
+ io_manager manager;
+ char *name;
+ int block_size;
+ errcode_t (*read_error)(io_channel channel,
+ unsigned long block,
+ int count,
+ void *data,
+ size_t size,
+ int actual_bytes_read,
+ errcode_t error);
+ errcode_t (*write_error)(io_channel channel,
+ unsigned long block,
+ int count,
+ const void *data,
+ size_t size,
+ int actual_bytes_written,
+ errcode_t error);
+ int refcount;
+ int flags;
+ int reserved[14];
+ void *private_data;
+ void *app_data;
+};
+
+struct struct_io_manager {
+ errcode_t magic;
+ const char *name;
+ errcode_t (*open)(const char *name, int flags, io_channel *channel);
+ errcode_t (*close)(io_channel channel);
+ errcode_t (*set_blksize)(io_channel channel, int blksize);
+ errcode_t (*read_blk)(io_channel channel, unsigned long block,
+ int count, void *data);
+ errcode_t (*write_blk)(io_channel channel, unsigned long block,
+ int count, const void *data);
+ errcode_t (*flush)(io_channel channel);
+ errcode_t (*write_byte)(io_channel channel, unsigned long offset,
+ int count, const void *data);
+ errcode_t (*set_option)(io_channel channel, const char *option,
+ const char *arg);
+ int reserved[14];
+};
+
+#define IO_FLAG_RW 1
+
+/*
+ * Convenience functions....
+ */
+#define io_channel_close(c) ((c)->manager->close((c)))
+#define io_channel_set_blksize(c,s) ((c)->manager->set_blksize((c),s))
+#define io_channel_read_blk(c,b,n,d) ((c)->manager->read_blk((c),b,n,d))
+#define io_channel_write_blk(c,b,n,d) ((c)->manager->write_blk((c),b,n,d))
+#define io_channel_flush(c) ((c)->manager->flush((c)))
+#define io_channel_bumpcount(c) ((c)->refcount++)
+
+/* io_manager.c */
+extern errcode_t io_channel_set_options(io_channel channel,
+ const char *options);
+extern errcode_t io_channel_write_byte(io_channel channel,
+ unsigned long offset,
+ int count, const void *data);
+
+/* unix_io.c */
+extern io_manager unix_io_manager;
+
+/* test_io.c */
+extern io_manager test_io_manager, test_io_backing_manager;
+extern void (*test_io_cb_read_blk)
+ (unsigned long block, int count, errcode_t err);
+extern void (*test_io_cb_write_blk)
+ (unsigned long block, int count, errcode_t err);
+extern void (*test_io_cb_set_blksize)
+ (int blksize, errcode_t err);
+
+#endif /* _EXT2FS_EXT2_IO_H */
+
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/ext2_types.h b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/ext2_types.h
new file mode 100644
index 0000000..2c1196b
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/ext2_types.h
@@ -0,0 +1,2 @@
+/* vi: set sw=4 ts=4: */
+#include <linux/types.h>
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/ext2fs.h b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/ext2fs.h
new file mode 100644
index 0000000..133fb1f
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/ext2fs.h
@@ -0,0 +1,923 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ext2fs.h --- ext2fs
+ *
+ * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#ifndef _EXT2FS_EXT2FS_H
+#define _EXT2FS_EXT2FS_H
+
+
+#define EXT2FS_ATTR(x)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Where the master copy of the superblock is located, and how big
+ * superblocks are supposed to be. We define SUPERBLOCK_SIZE because
+ * the size of the superblock structure is not necessarily trustworthy
+ * (some versions have the padding set up so that the superblock is
+ * 1032 bytes long).
+ */
+#define SUPERBLOCK_OFFSET 1024
+#define SUPERBLOCK_SIZE 1024
+
+/*
+ * The last ext2fs revision level that this version of the library is
+ * able to support.
+ */
+#define EXT2_LIB_CURRENT_REV EXT2_DYNAMIC_REV
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "ext2_types.h"
+#include "ext2_fs.h"
+
+typedef __u32 ext2_ino_t;
+typedef __u32 blk_t;
+typedef __u32 dgrp_t;
+typedef __u32 ext2_off_t;
+typedef __s64 e2_blkcnt_t;
+typedef __u32 ext2_dirhash_t;
+
+#include "ext2_io.h"
+#include "ext2_err.h"
+
+typedef struct struct_ext2_filsys *ext2_filsys;
+
+struct ext2fs_struct_generic_bitmap {
+ errcode_t magic;
+ ext2_filsys fs;
+ __u32 start, end;
+ __u32 real_end;
+ char * description;
+ char * bitmap;
+ errcode_t base_error_code;
+ __u32 reserved[7];
+};
+
+#define EXT2FS_MARK_ERROR 0
+#define EXT2FS_UNMARK_ERROR 1
+#define EXT2FS_TEST_ERROR 2
+
+typedef struct ext2fs_struct_generic_bitmap *ext2fs_generic_bitmap;
+typedef struct ext2fs_struct_generic_bitmap *ext2fs_inode_bitmap;
+typedef struct ext2fs_struct_generic_bitmap *ext2fs_block_bitmap;
+
+#define EXT2_FIRST_INODE(s) EXT2_FIRST_INO(s)
+
+/*
+ * badblocks list definitions
+ */
+
+typedef struct ext2_struct_u32_list *ext2_badblocks_list;
+typedef struct ext2_struct_u32_iterate *ext2_badblocks_iterate;
+
+typedef struct ext2_struct_u32_list *ext2_u32_list;
+typedef struct ext2_struct_u32_iterate *ext2_u32_iterate;
+
+/* old */
+typedef struct ext2_struct_u32_list *badblocks_list;
+typedef struct ext2_struct_u32_iterate *badblocks_iterate;
+
+#define BADBLOCKS_FLAG_DIRTY 1
+
+/*
+ * ext2_dblist structure and abstractions (see dblist.c)
+ */
+struct ext2_db_entry {
+ ext2_ino_t ino;
+ blk_t blk;
+ int blockcnt;
+};
+
+typedef struct ext2_struct_dblist *ext2_dblist;
+
+#define DBLIST_ABORT 1
+
+/*
+ * ext2_fileio definitions
+ */
+
+#define EXT2_FILE_WRITE 0x0001
+#define EXT2_FILE_CREATE 0x0002
+
+#define EXT2_FILE_MASK 0x00FF
+
+#define EXT2_FILE_BUF_DIRTY 0x4000
+#define EXT2_FILE_BUF_VALID 0x2000
+
+typedef struct ext2_file *ext2_file_t;
+
+#define EXT2_SEEK_SET 0
+#define EXT2_SEEK_CUR 1
+#define EXT2_SEEK_END 2
+
+/*
+ * Flags for the ext2_filsys structure and for ext2fs_open()
+ */
+#define EXT2_FLAG_RW 0x01
+#define EXT2_FLAG_CHANGED 0x02
+#define EXT2_FLAG_DIRTY 0x04
+#define EXT2_FLAG_VALID 0x08
+#define EXT2_FLAG_IB_DIRTY 0x10
+#define EXT2_FLAG_BB_DIRTY 0x20
+#define EXT2_FLAG_SWAP_BYTES 0x40
+#define EXT2_FLAG_SWAP_BYTES_READ 0x80
+#define EXT2_FLAG_SWAP_BYTES_WRITE 0x100
+#define EXT2_FLAG_MASTER_SB_ONLY 0x200
+#define EXT2_FLAG_FORCE 0x400
+#define EXT2_FLAG_SUPER_ONLY 0x800
+#define EXT2_FLAG_JOURNAL_DEV_OK 0x1000
+#define EXT2_FLAG_IMAGE_FILE 0x2000
+
+/*
+ * Special flag in the ext2 inode i_flag field that means that this is
+ * a new inode. (So that ext2_write_inode() can clear extra fields.)
+ */
+#define EXT2_NEW_INODE_FL 0x80000000
+
+/*
+ * Flags for mkjournal
+ *
+ * EXT2_MKJOURNAL_V1_SUPER Make a (deprecated) V1 journal superblock
+ */
+#define EXT2_MKJOURNAL_V1_SUPER 0x0000001
+
+struct struct_ext2_filsys {
+ errcode_t magic;
+ io_channel io;
+ int flags;
+ char * device_name;
+ struct ext2_super_block * super;
+ unsigned int blocksize;
+ int fragsize;
+ dgrp_t group_desc_count;
+ unsigned long desc_blocks;
+ struct ext2_group_desc * group_desc;
+ int inode_blocks_per_group;
+ ext2fs_inode_bitmap inode_map;
+ ext2fs_block_bitmap block_map;
+ errcode_t (*get_blocks)(ext2_filsys fs, ext2_ino_t ino, blk_t *blocks);
+ errcode_t (*check_directory)(ext2_filsys fs, ext2_ino_t ino);
+ errcode_t (*write_bitmaps)(ext2_filsys fs);
+ errcode_t (*read_inode)(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode *inode);
+ errcode_t (*write_inode)(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode *inode);
+ ext2_badblocks_list badblocks;
+ ext2_dblist dblist;
+ __u32 stride; /* for mke2fs */
+ struct ext2_super_block * orig_super;
+ struct ext2_image_hdr * image_header;
+ __u32 umask;
+ /*
+ * Reserved for future expansion
+ */
+ __u32 reserved[8];
+
+ /*
+ * Reserved for the use of the calling application.
+ */
+ void * priv_data;
+
+ /*
+ * Inode cache
+ */
+ struct ext2_inode_cache *icache;
+ io_channel image_io;
+};
+
+#include "bitops.h"
+
+/*
+ * Return flags for the block iterator functions
+ */
+#define BLOCK_CHANGED 1
+#define BLOCK_ABORT 2
+#define BLOCK_ERROR 4
+
+/*
+ * Block interate flags
+ *
+ * BLOCK_FLAG_APPEND, or BLOCK_FLAG_HOLE, indicates that the interator
+ * function should be called on blocks where the block number is zero.
+ * This is used by ext2fs_expand_dir() to be able to add a new block
+ * to an inode. It can also be used for programs that want to be able
+ * to deal with files that contain "holes".
+ *
+ * BLOCK_FLAG_TRAVERSE indicates that the iterator function for the
+ * indirect, doubly indirect, etc. blocks should be called after all
+ * of the blocks containined in the indirect blocks are processed.
+ * This is useful if you are going to be deallocating blocks from an
+ * inode.
+ *
+ * BLOCK_FLAG_DATA_ONLY indicates that the iterator function should be
+ * called for data blocks only.
+ *
+ * BLOCK_FLAG_NO_LARGE is for internal use only. It informs
+ * ext2fs_block_iterate2 that large files won't be accepted.
+ */
+#define BLOCK_FLAG_APPEND 1
+#define BLOCK_FLAG_HOLE 1
+#define BLOCK_FLAG_DEPTH_TRAVERSE 2
+#define BLOCK_FLAG_DATA_ONLY 4
+
+#define BLOCK_FLAG_NO_LARGE 0x1000
+
+/*
+ * Magic "block count" return values for the block iterator function.
+ */
+#define BLOCK_COUNT_IND (-1)
+#define BLOCK_COUNT_DIND (-2)
+#define BLOCK_COUNT_TIND (-3)
+#define BLOCK_COUNT_TRANSLATOR (-4)
+
+#if 0
+/*
+ * Flags for ext2fs_move_blocks
+ */
+#define EXT2_BMOVE_GET_DBLIST 0x0001
+#define EXT2_BMOVE_DEBUG 0x0002
+#endif
+
+/*
+ * Flags for directory block reading and writing functions
+ */
+#define EXT2_DIRBLOCK_V2_STRUCT 0x0001
+
+/*
+ * Return flags for the directory iterator functions
+ */
+#define DIRENT_CHANGED 1
+#define DIRENT_ABORT 2
+#define DIRENT_ERROR 3
+
+/*
+ * Directory iterator flags
+ */
+
+#define DIRENT_FLAG_INCLUDE_EMPTY 1
+#define DIRENT_FLAG_INCLUDE_REMOVED 2
+
+#define DIRENT_DOT_FILE 1
+#define DIRENT_DOT_DOT_FILE 2
+#define DIRENT_OTHER_FILE 3
+#define DIRENT_DELETED_FILE 4
+
+/*
+ * Inode scan definitions
+ */
+typedef struct ext2_struct_inode_scan *ext2_inode_scan;
+
+/*
+ * ext2fs_scan flags
+ */
+#define EXT2_SF_CHK_BADBLOCKS 0x0001
+#define EXT2_SF_BAD_INODE_BLK 0x0002
+#define EXT2_SF_BAD_EXTRA_BYTES 0x0004
+#define EXT2_SF_SKIP_MISSING_ITABLE 0x0008
+
+/*
+ * ext2fs_check_if_mounted flags
+ */
+#define EXT2_MF_MOUNTED 1
+#define EXT2_MF_ISROOT 2
+#define EXT2_MF_READONLY 4
+#define EXT2_MF_SWAP 8
+#define EXT2_MF_BUSY 16
+
+/*
+ * Ext2/linux mode flags. We define them here so that we don't need
+ * to depend on the OS's sys/stat.h, since we may be compiling on a
+ * non-Linux system.
+ */
+#define LINUX_S_IFMT 00170000
+#define LINUX_S_IFSOCK 0140000
+#define LINUX_S_IFLNK 0120000
+#define LINUX_S_IFREG 0100000
+#define LINUX_S_IFBLK 0060000
+#define LINUX_S_IFDIR 0040000
+#define LINUX_S_IFCHR 0020000
+#define LINUX_S_IFIFO 0010000
+#define LINUX_S_ISUID 0004000
+#define LINUX_S_ISGID 0002000
+#define LINUX_S_ISVTX 0001000
+
+#define LINUX_S_IRWXU 00700
+#define LINUX_S_IRUSR 00400
+#define LINUX_S_IWUSR 00200
+#define LINUX_S_IXUSR 00100
+
+#define LINUX_S_IRWXG 00070
+#define LINUX_S_IRGRP 00040
+#define LINUX_S_IWGRP 00020
+#define LINUX_S_IXGRP 00010
+
+#define LINUX_S_IRWXO 00007
+#define LINUX_S_IROTH 00004
+#define LINUX_S_IWOTH 00002
+#define LINUX_S_IXOTH 00001
+
+#define LINUX_S_ISLNK(m) (((m) & LINUX_S_IFMT) == LINUX_S_IFLNK)
+#define LINUX_S_ISREG(m) (((m) & LINUX_S_IFMT) == LINUX_S_IFREG)
+#define LINUX_S_ISDIR(m) (((m) & LINUX_S_IFMT) == LINUX_S_IFDIR)
+#define LINUX_S_ISCHR(m) (((m) & LINUX_S_IFMT) == LINUX_S_IFCHR)
+#define LINUX_S_ISBLK(m) (((m) & LINUX_S_IFMT) == LINUX_S_IFBLK)
+#define LINUX_S_ISFIFO(m) (((m) & LINUX_S_IFMT) == LINUX_S_IFIFO)
+#define LINUX_S_ISSOCK(m) (((m) & LINUX_S_IFMT) == LINUX_S_IFSOCK)
+
+/*
+ * ext2 size of an inode
+ */
+#define EXT2_I_SIZE(i) ((i)->i_size | ((__u64) (i)->i_size_high << 32))
+
+/*
+ * ext2_icount_t abstraction
+ */
+#define EXT2_ICOUNT_OPT_INCREMENT 0x01
+
+typedef struct ext2_icount *ext2_icount_t;
+
+/*
+ * Flags for ext2fs_bmap
+ */
+#define BMAP_ALLOC 0x0001
+#define BMAP_SET 0x0002
+
+/*
+ * Flags for imager.c functions
+ */
+#define IMAGER_FLAG_INODEMAP 1
+#define IMAGER_FLAG_SPARSEWRITE 2
+
+/*
+ * For checking structure magic numbers...
+ */
+
+#define EXT2_CHECK_MAGIC(struct, code) \
+ if ((struct)->magic != (code)) return (code)
+
+
+/*
+ * For ext2 compression support
+ */
+#define EXT2FS_COMPRESSED_BLKADDR ((blk_t) 0xffffffff)
+#define HOLE_BLKADDR(_b) ((_b) == 0 || (_b) == EXT2FS_COMPRESSED_BLKADDR)
+
+/*
+ * Features supported by this version of the library
+ */
+#define EXT2_LIB_FEATURE_COMPAT_SUPP (EXT2_FEATURE_COMPAT_DIR_PREALLOC|\
+ EXT2_FEATURE_COMPAT_IMAGIC_INODES|\
+ EXT3_FEATURE_COMPAT_HAS_JOURNAL|\
+ EXT2_FEATURE_COMPAT_RESIZE_INODE|\
+ EXT2_FEATURE_COMPAT_DIR_INDEX|\
+ EXT2_FEATURE_COMPAT_EXT_ATTR)
+
+/* This #ifdef is temporary until compression is fully supported */
+#ifdef ENABLE_COMPRESSION
+#ifndef I_KNOW_THAT_COMPRESSION_IS_EXPERIMENTAL
+/* If the below warning bugs you, then have
+ `CPPFLAGS=-DI_KNOW_THAT_COMPRESSION_IS_EXPERIMENTAL' in your
+ environment at configure time. */
+ #warning "Compression support is experimental"
+#endif
+#define EXT2_LIB_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE|\
+ EXT2_FEATURE_INCOMPAT_COMPRESSION|\
+ EXT3_FEATURE_INCOMPAT_JOURNAL_DEV|\
+ EXT2_FEATURE_INCOMPAT_META_BG|\
+ EXT3_FEATURE_INCOMPAT_RECOVER)
+#else
+#define EXT2_LIB_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE|\
+ EXT3_FEATURE_INCOMPAT_JOURNAL_DEV|\
+ EXT2_FEATURE_INCOMPAT_META_BG|\
+ EXT3_FEATURE_INCOMPAT_RECOVER)
+#endif
+#define EXT2_LIB_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER|\
+ EXT2_FEATURE_RO_COMPAT_LARGE_FILE)
+/*
+ * function prototypes
+ */
+
+/* alloc.c */
+extern errcode_t ext2fs_new_inode(ext2_filsys fs, ext2_ino_t dir, int mode,
+ ext2fs_inode_bitmap map, ext2_ino_t *ret);
+extern errcode_t ext2fs_new_block(ext2_filsys fs, blk_t goal,
+ ext2fs_block_bitmap map, blk_t *ret);
+extern errcode_t ext2fs_get_free_blocks(ext2_filsys fs, blk_t start,
+ blk_t finish, int num,
+ ext2fs_block_bitmap map,
+ blk_t *ret);
+extern errcode_t ext2fs_alloc_block(ext2_filsys fs, blk_t goal,
+ char *block_buf, blk_t *ret);
+
+/* alloc_sb.c */
+extern int ext2fs_reserve_super_and_bgd(ext2_filsys fs,
+ dgrp_t group,
+ ext2fs_block_bitmap bmap);
+
+/* alloc_stats.c */
+void ext2fs_inode_alloc_stats(ext2_filsys fs, ext2_ino_t ino, int inuse);
+void ext2fs_inode_alloc_stats2(ext2_filsys fs, ext2_ino_t ino,
+ int inuse, int isdir);
+void ext2fs_block_alloc_stats(ext2_filsys fs, blk_t blk, int inuse);
+
+/* alloc_tables.c */
+extern errcode_t ext2fs_allocate_tables(ext2_filsys fs);
+extern errcode_t ext2fs_allocate_group_table(ext2_filsys fs, dgrp_t group,
+ ext2fs_block_bitmap bmap);
+
+/* badblocks.c */
+extern errcode_t ext2fs_u32_list_create(ext2_u32_list *ret, int size);
+extern errcode_t ext2fs_u32_list_add(ext2_u32_list bb, __u32 blk);
+extern int ext2fs_u32_list_find(ext2_u32_list bb, __u32 blk);
+extern int ext2fs_u32_list_test(ext2_u32_list bb, blk_t blk);
+extern errcode_t ext2fs_u32_list_iterate_begin(ext2_u32_list bb,
+ ext2_u32_iterate *ret);
+extern int ext2fs_u32_list_iterate(ext2_u32_iterate iter, blk_t *blk);
+extern void ext2fs_u32_list_iterate_end(ext2_u32_iterate iter);
+extern errcode_t ext2fs_u32_copy(ext2_u32_list src, ext2_u32_list *dest);
+extern int ext2fs_u32_list_equal(ext2_u32_list bb1, ext2_u32_list bb2);
+
+extern errcode_t ext2fs_badblocks_list_create(ext2_badblocks_list *ret,
+ int size);
+extern errcode_t ext2fs_badblocks_list_add(ext2_badblocks_list bb,
+ blk_t blk);
+extern int ext2fs_badblocks_list_test(ext2_badblocks_list bb,
+ blk_t blk);
+extern int ext2fs_u32_list_del(ext2_u32_list bb, __u32 blk);
+extern void ext2fs_badblocks_list_del(ext2_u32_list bb, __u32 blk);
+extern errcode_t
+ ext2fs_badblocks_list_iterate_begin(ext2_badblocks_list bb,
+ ext2_badblocks_iterate *ret);
+extern int ext2fs_badblocks_list_iterate(ext2_badblocks_iterate iter,
+ blk_t *blk);
+extern void ext2fs_badblocks_list_iterate_end(ext2_badblocks_iterate iter);
+extern errcode_t ext2fs_badblocks_copy(ext2_badblocks_list src,
+ ext2_badblocks_list *dest);
+extern int ext2fs_badblocks_equal(ext2_badblocks_list bb1,
+ ext2_badblocks_list bb2);
+extern int ext2fs_u32_list_count(ext2_u32_list bb);
+
+/* bb_compat */
+extern errcode_t badblocks_list_create(badblocks_list *ret, int size);
+extern errcode_t badblocks_list_add(badblocks_list bb, blk_t blk);
+extern int badblocks_list_test(badblocks_list bb, blk_t blk);
+extern errcode_t badblocks_list_iterate_begin(badblocks_list bb,
+ badblocks_iterate *ret);
+extern int badblocks_list_iterate(badblocks_iterate iter, blk_t *blk);
+extern void badblocks_list_iterate_end(badblocks_iterate iter);
+extern void badblocks_list_free(badblocks_list bb);
+
+/* bb_inode.c */
+extern errcode_t ext2fs_update_bb_inode(ext2_filsys fs,
+ ext2_badblocks_list bb_list);
+
+/* bitmaps.c */
+extern errcode_t ext2fs_write_inode_bitmap(ext2_filsys fs);
+extern errcode_t ext2fs_write_block_bitmap (ext2_filsys fs);
+extern errcode_t ext2fs_read_inode_bitmap (ext2_filsys fs);
+extern errcode_t ext2fs_read_block_bitmap(ext2_filsys fs);
+extern errcode_t ext2fs_allocate_generic_bitmap(__u32 start,
+ __u32 end,
+ __u32 real_end,
+ const char *descr,
+ ext2fs_generic_bitmap *ret);
+extern errcode_t ext2fs_allocate_block_bitmap(ext2_filsys fs,
+ const char *descr,
+ ext2fs_block_bitmap *ret);
+extern errcode_t ext2fs_allocate_inode_bitmap(ext2_filsys fs,
+ const char *descr,
+ ext2fs_inode_bitmap *ret);
+extern errcode_t ext2fs_fudge_inode_bitmap_end(ext2fs_inode_bitmap bitmap,
+ ext2_ino_t end, ext2_ino_t *oend);
+extern errcode_t ext2fs_fudge_block_bitmap_end(ext2fs_block_bitmap bitmap,
+ blk_t end, blk_t *oend);
+extern void ext2fs_clear_inode_bitmap(ext2fs_inode_bitmap bitmap);
+extern void ext2fs_clear_block_bitmap(ext2fs_block_bitmap bitmap);
+extern errcode_t ext2fs_read_bitmaps(ext2_filsys fs);
+extern errcode_t ext2fs_write_bitmaps(ext2_filsys fs);
+
+/* block.c */
+extern errcode_t ext2fs_block_iterate(ext2_filsys fs,
+ ext2_ino_t ino,
+ int flags,
+ char *block_buf,
+ int (*func)(ext2_filsys fs,
+ blk_t *blocknr,
+ int blockcnt,
+ void *priv_data),
+ void *priv_data);
+errcode_t ext2fs_block_iterate2(ext2_filsys fs,
+ ext2_ino_t ino,
+ int flags,
+ char *block_buf,
+ int (*func)(ext2_filsys fs,
+ blk_t *blocknr,
+ e2_blkcnt_t blockcnt,
+ blk_t ref_blk,
+ int ref_offset,
+ void *priv_data),
+ void *priv_data);
+
+/* bmap.c */
+extern errcode_t ext2fs_bmap(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode *inode,
+ char *block_buf, int bmap_flags,
+ blk_t block, blk_t *phys_blk);
+
+
+#if 0
+/* bmove.c */
+extern errcode_t ext2fs_move_blocks(ext2_filsys fs,
+ ext2fs_block_bitmap reserve,
+ ext2fs_block_bitmap alloc_map,
+ int flags);
+#endif
+
+/* check_desc.c */
+extern errcode_t ext2fs_check_desc(ext2_filsys fs);
+
+/* closefs.c */
+extern errcode_t ext2fs_close(ext2_filsys fs);
+extern errcode_t ext2fs_flush(ext2_filsys fs);
+extern int ext2fs_bg_has_super(ext2_filsys fs, int group_block);
+extern int ext2fs_super_and_bgd_loc(ext2_filsys fs,
+ dgrp_t group,
+ blk_t *ret_super_blk,
+ blk_t *ret_old_desc_blk,
+ blk_t *ret_new_desc_blk,
+ int *ret_meta_bg);
+extern void ext2fs_update_dynamic_rev(ext2_filsys fs);
+
+/* cmp_bitmaps.c */
+extern errcode_t ext2fs_compare_block_bitmap(ext2fs_block_bitmap bm1,
+ ext2fs_block_bitmap bm2);
+extern errcode_t ext2fs_compare_inode_bitmap(ext2fs_inode_bitmap bm1,
+ ext2fs_inode_bitmap bm2);
+
+/* dblist.c */
+
+extern errcode_t ext2fs_get_num_dirs(ext2_filsys fs, ext2_ino_t *ret_num_dirs);
+extern errcode_t ext2fs_init_dblist(ext2_filsys fs, ext2_dblist *ret_dblist);
+extern errcode_t ext2fs_add_dir_block(ext2_dblist dblist, ext2_ino_t ino,
+ blk_t blk, int blockcnt);
+extern void ext2fs_dblist_sort(ext2_dblist dblist,
+ int (*sortfunc)(const void *,
+ const void *));
+extern errcode_t ext2fs_dblist_iterate(ext2_dblist dblist,
+ int (*func)(ext2_filsys fs, struct ext2_db_entry *db_info,
+ void *priv_data),
+ void *priv_data);
+extern errcode_t ext2fs_set_dir_block(ext2_dblist dblist, ext2_ino_t ino,
+ blk_t blk, int blockcnt);
+extern errcode_t ext2fs_copy_dblist(ext2_dblist src,
+ ext2_dblist *dest);
+extern int ext2fs_dblist_count(ext2_dblist dblist);
+
+/* dblist_dir.c */
+extern errcode_t
+ ext2fs_dblist_dir_iterate(ext2_dblist dblist,
+ int flags,
+ char *block_buf,
+ int (*func)(ext2_ino_t dir,
+ int entry,
+ struct ext2_dir_entry *dirent,
+ int offset,
+ int blocksize,
+ char *buf,
+ void *priv_data),
+ void *priv_data);
+
+/* dirblock.c */
+extern errcode_t ext2fs_read_dir_block(ext2_filsys fs, blk_t block,
+ void *buf);
+extern errcode_t ext2fs_read_dir_block2(ext2_filsys fs, blk_t block,
+ void *buf, int flags);
+extern errcode_t ext2fs_write_dir_block(ext2_filsys fs, blk_t block,
+ void *buf);
+extern errcode_t ext2fs_write_dir_block2(ext2_filsys fs, blk_t block,
+ void *buf, int flags);
+
+/* dirhash.c */
+extern errcode_t ext2fs_dirhash(int version, const char *name, int len,
+ const __u32 *seed,
+ ext2_dirhash_t *ret_hash,
+ ext2_dirhash_t *ret_minor_hash);
+
+
+/* dir_iterate.c */
+extern errcode_t ext2fs_dir_iterate(ext2_filsys fs,
+ ext2_ino_t dir,
+ int flags,
+ char *block_buf,
+ int (*func)(struct ext2_dir_entry *dirent,
+ int offset,
+ int blocksize,
+ char *buf,
+ void *priv_data),
+ void *priv_data);
+extern errcode_t ext2fs_dir_iterate2(ext2_filsys fs,
+ ext2_ino_t dir,
+ int flags,
+ char *block_buf,
+ int (*func)(ext2_ino_t dir,
+ int entry,
+ struct ext2_dir_entry *dirent,
+ int offset,
+ int blocksize,
+ char *buf,
+ void *priv_data),
+ void *priv_data);
+
+/* dupfs.c */
+extern errcode_t ext2fs_dup_handle(ext2_filsys src, ext2_filsys *dest);
+
+/* expanddir.c */
+extern errcode_t ext2fs_expand_dir(ext2_filsys fs, ext2_ino_t dir);
+
+/* ext_attr.c */
+extern errcode_t ext2fs_read_ext_attr(ext2_filsys fs, blk_t block, void *buf);
+extern errcode_t ext2fs_write_ext_attr(ext2_filsys fs, blk_t block,
+ void *buf);
+extern errcode_t ext2fs_adjust_ea_refcount(ext2_filsys fs, blk_t blk,
+ char *block_buf,
+ int adjust, __u32 *newcount);
+
+/* fileio.c */
+extern errcode_t ext2fs_file_open2(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode *inode,
+ int flags, ext2_file_t *ret);
+extern errcode_t ext2fs_file_open(ext2_filsys fs, ext2_ino_t ino,
+ int flags, ext2_file_t *ret);
+extern ext2_filsys ext2fs_file_get_fs(ext2_file_t file);
+extern errcode_t ext2fs_file_close(ext2_file_t file);
+extern errcode_t ext2fs_file_flush(ext2_file_t file);
+extern errcode_t ext2fs_file_read(ext2_file_t file, void *buf,
+ unsigned int wanted, unsigned int *got);
+extern errcode_t ext2fs_file_write(ext2_file_t file, const void *buf,
+ unsigned int nbytes, unsigned int *written);
+extern errcode_t ext2fs_file_llseek(ext2_file_t file, __u64 offset,
+ int whence, __u64 *ret_pos);
+extern errcode_t ext2fs_file_lseek(ext2_file_t file, ext2_off_t offset,
+ int whence, ext2_off_t *ret_pos);
+errcode_t ext2fs_file_get_lsize(ext2_file_t file, __u64 *ret_size);
+extern ext2_off_t ext2fs_file_get_size(ext2_file_t file);
+extern errcode_t ext2fs_file_set_size(ext2_file_t file, ext2_off_t size);
+
+/* finddev.c */
+extern char *ext2fs_find_block_device(dev_t device);
+
+/* flushb.c */
+extern errcode_t ext2fs_sync_device(int fd, int flushb);
+
+/* freefs.c */
+extern void ext2fs_free(ext2_filsys fs);
+extern void ext2fs_free_generic_bitmap(ext2fs_inode_bitmap bitmap);
+extern void ext2fs_free_block_bitmap(ext2fs_block_bitmap bitmap);
+extern void ext2fs_free_inode_bitmap(ext2fs_inode_bitmap bitmap);
+extern void ext2fs_free_dblist(ext2_dblist dblist);
+extern void ext2fs_badblocks_list_free(ext2_badblocks_list bb);
+extern void ext2fs_u32_list_free(ext2_u32_list bb);
+
+/* getsize.c */
+extern errcode_t ext2fs_get_device_size(const char *file, int blocksize,
+ blk_t *retblocks);
+
+/* getsectsize.c */
+errcode_t ext2fs_get_device_sectsize(const char *file, int *sectsize);
+
+/* imager.c */
+extern errcode_t ext2fs_image_inode_write(ext2_filsys fs, int fd, int flags);
+extern errcode_t ext2fs_image_inode_read(ext2_filsys fs, int fd, int flags);
+extern errcode_t ext2fs_image_super_write(ext2_filsys fs, int fd, int flags);
+extern errcode_t ext2fs_image_super_read(ext2_filsys fs, int fd, int flags);
+extern errcode_t ext2fs_image_bitmap_write(ext2_filsys fs, int fd, int flags);
+extern errcode_t ext2fs_image_bitmap_read(ext2_filsys fs, int fd, int flags);
+
+/* ind_block.c */
+errcode_t ext2fs_read_ind_block(ext2_filsys fs, blk_t blk, void *buf);
+errcode_t ext2fs_write_ind_block(ext2_filsys fs, blk_t blk, void *buf);
+
+/* initialize.c */
+extern errcode_t ext2fs_initialize(const char *name, int flags,
+ struct ext2_super_block *param,
+ io_manager manager, ext2_filsys *ret_fs);
+
+/* icount.c */
+extern void ext2fs_free_icount(ext2_icount_t icount);
+extern errcode_t ext2fs_create_icount2(ext2_filsys fs, int flags,
+ unsigned int size,
+ ext2_icount_t hint, ext2_icount_t *ret);
+extern errcode_t ext2fs_create_icount(ext2_filsys fs, int flags,
+ unsigned int size,
+ ext2_icount_t *ret);
+extern errcode_t ext2fs_icount_fetch(ext2_icount_t icount, ext2_ino_t ino,
+ __u16 *ret);
+extern errcode_t ext2fs_icount_increment(ext2_icount_t icount, ext2_ino_t ino,
+ __u16 *ret);
+extern errcode_t ext2fs_icount_decrement(ext2_icount_t icount, ext2_ino_t ino,
+ __u16 *ret);
+extern errcode_t ext2fs_icount_store(ext2_icount_t icount, ext2_ino_t ino,
+ __u16 count);
+extern ext2_ino_t ext2fs_get_icount_size(ext2_icount_t icount);
+errcode_t ext2fs_icount_validate(ext2_icount_t icount, FILE *);
+
+/* inode.c */
+extern errcode_t ext2fs_flush_icache(ext2_filsys fs);
+extern errcode_t ext2fs_get_next_inode_full(ext2_inode_scan scan,
+ ext2_ino_t *ino,
+ struct ext2_inode *inode,
+ int bufsize);
+extern errcode_t ext2fs_open_inode_scan(ext2_filsys fs, int buffer_blocks,
+ ext2_inode_scan *ret_scan);
+extern void ext2fs_close_inode_scan(ext2_inode_scan scan);
+extern errcode_t ext2fs_get_next_inode(ext2_inode_scan scan, ext2_ino_t *ino,
+ struct ext2_inode *inode);
+extern errcode_t ext2fs_inode_scan_goto_blockgroup(ext2_inode_scan scan,
+ int group);
+extern void ext2fs_set_inode_callback
+ (ext2_inode_scan scan,
+ errcode_t (*done_group)(ext2_filsys fs,
+ dgrp_t group,
+ void * priv_data),
+ void *done_group_data);
+extern int ext2fs_inode_scan_flags(ext2_inode_scan scan, int set_flags,
+ int clear_flags);
+extern errcode_t ext2fs_read_inode_full(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode * inode,
+ int bufsize);
+extern errcode_t ext2fs_read_inode (ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode * inode);
+extern errcode_t ext2fs_write_inode_full(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode * inode,
+ int bufsize);
+extern errcode_t ext2fs_write_inode(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode * inode);
+extern errcode_t ext2fs_write_new_inode(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode * inode);
+extern errcode_t ext2fs_get_blocks(ext2_filsys fs, ext2_ino_t ino, blk_t *blocks);
+extern errcode_t ext2fs_check_directory(ext2_filsys fs, ext2_ino_t ino);
+
+/* inode_io.c */
+extern io_manager inode_io_manager;
+extern errcode_t ext2fs_inode_io_intern(ext2_filsys fs, ext2_ino_t ino,
+ char **name);
+extern errcode_t ext2fs_inode_io_intern2(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode *inode,
+ char **name);
+
+/* ismounted.c */
+extern errcode_t ext2fs_check_if_mounted(const char *file, int *mount_flags);
+extern errcode_t ext2fs_check_mount_point(const char *device, int *mount_flags,
+ char *mtpt, int mtlen);
+
+/* namei.c */
+extern errcode_t ext2fs_lookup(ext2_filsys fs, ext2_ino_t dir, const char *name,
+ int namelen, char *buf, ext2_ino_t *inode);
+extern errcode_t ext2fs_namei(ext2_filsys fs, ext2_ino_t root, ext2_ino_t cwd,
+ const char *name, ext2_ino_t *inode);
+errcode_t ext2fs_namei_follow(ext2_filsys fs, ext2_ino_t root, ext2_ino_t cwd,
+ const char *name, ext2_ino_t *inode);
+extern errcode_t ext2fs_follow_link(ext2_filsys fs, ext2_ino_t root, ext2_ino_t cwd,
+ ext2_ino_t inode, ext2_ino_t *res_inode);
+
+/* native.c */
+int ext2fs_native_flag(void);
+
+/* newdir.c */
+extern errcode_t ext2fs_new_dir_block(ext2_filsys fs, ext2_ino_t dir_ino,
+ ext2_ino_t parent_ino, char **block);
+
+/* mkdir.c */
+extern errcode_t ext2fs_mkdir(ext2_filsys fs, ext2_ino_t parent, ext2_ino_t inum,
+ const char *name);
+
+/* mkjournal.c */
+extern errcode_t ext2fs_create_journal_superblock(ext2_filsys fs,
+ __u32 size, int flags,
+ char **ret_jsb);
+extern errcode_t ext2fs_add_journal_device(ext2_filsys fs,
+ ext2_filsys journal_dev);
+extern errcode_t ext2fs_add_journal_inode(ext2_filsys fs, blk_t size,
+ int flags);
+
+/* openfs.c */
+extern errcode_t ext2fs_open(const char *name, int flags, int superblock,
+ unsigned int block_size, io_manager manager,
+ ext2_filsys *ret_fs);
+extern errcode_t ext2fs_open2(const char *name, const char *io_options,
+ int flags, int superblock,
+ unsigned int block_size, io_manager manager,
+ ext2_filsys *ret_fs);
+extern blk_t ext2fs_descriptor_block_loc(ext2_filsys fs, blk_t group_block,
+ dgrp_t i);
+errcode_t ext2fs_get_data_io(ext2_filsys fs, io_channel *old_io);
+errcode_t ext2fs_set_data_io(ext2_filsys fs, io_channel new_io);
+errcode_t ext2fs_rewrite_to_io(ext2_filsys fs, io_channel new_io);
+
+/* get_pathname.c */
+extern errcode_t ext2fs_get_pathname(ext2_filsys fs, ext2_ino_t dir, ext2_ino_t ino,
+ char **name);
+
+/* link.c */
+errcode_t ext2fs_link(ext2_filsys fs, ext2_ino_t dir, const char *name,
+ ext2_ino_t ino, int flags);
+errcode_t ext2fs_unlink(ext2_filsys fs, ext2_ino_t dir, const char *name,
+ ext2_ino_t ino, int flags);
+
+/* read_bb.c */
+extern errcode_t ext2fs_read_bb_inode(ext2_filsys fs,
+ ext2_badblocks_list *bb_list);
+
+/* read_bb_file.c */
+extern errcode_t ext2fs_read_bb_FILE2(ext2_filsys fs, FILE *f,
+ ext2_badblocks_list *bb_list,
+ void *priv_data,
+ void (*invalid)(ext2_filsys fs,
+ blk_t blk,
+ char *badstr,
+ void *priv_data));
+extern errcode_t ext2fs_read_bb_FILE(ext2_filsys fs, FILE *f,
+ ext2_badblocks_list *bb_list,
+ void (*invalid)(ext2_filsys fs,
+ blk_t blk));
+
+/* res_gdt.c */
+extern errcode_t ext2fs_create_resize_inode(ext2_filsys fs);
+
+/* rs_bitmap.c */
+extern errcode_t ext2fs_resize_generic_bitmap(__u32 new_end,
+ __u32 new_real_end,
+ ext2fs_generic_bitmap bmap);
+extern errcode_t ext2fs_resize_inode_bitmap(__u32 new_end, __u32 new_real_end,
+ ext2fs_inode_bitmap bmap);
+extern errcode_t ext2fs_resize_block_bitmap(__u32 new_end, __u32 new_real_end,
+ ext2fs_block_bitmap bmap);
+extern errcode_t ext2fs_copy_bitmap(ext2fs_generic_bitmap src,
+ ext2fs_generic_bitmap *dest);
+
+/* swapfs.c */
+extern void ext2fs_swap_ext_attr(char *to, char *from, int bufsize,
+ int has_header);
+extern void ext2fs_swap_super(struct ext2_super_block * super);
+extern void ext2fs_swap_group_desc(struct ext2_group_desc *gdp);
+extern void ext2fs_swap_inode_full(ext2_filsys fs, struct ext2_inode_large *t,
+ struct ext2_inode_large *f, int hostorder,
+ int bufsize);
+extern void ext2fs_swap_inode(ext2_filsys fs,struct ext2_inode *t,
+ struct ext2_inode *f, int hostorder);
+
+/* valid_blk.c */
+extern int ext2fs_inode_has_valid_blocks(struct ext2_inode *inode);
+
+/* version.c */
+extern int ext2fs_parse_version_string(const char *ver_string);
+extern int ext2fs_get_library_version(const char **ver_string,
+ const char **date_string);
+
+/* write_bb_file.c */
+extern errcode_t ext2fs_write_bb_FILE(ext2_badblocks_list bb_list,
+ unsigned int flags,
+ FILE *f);
+
+
+/* inline functions */
+extern errcode_t ext2fs_get_mem(unsigned long size, void *ptr);
+extern errcode_t ext2fs_free_mem(void *ptr);
+extern errcode_t ext2fs_resize_mem(unsigned long old_size,
+ unsigned long size, void *ptr);
+extern void ext2fs_mark_super_dirty(ext2_filsys fs);
+extern void ext2fs_mark_changed(ext2_filsys fs);
+extern int ext2fs_test_changed(ext2_filsys fs);
+extern void ext2fs_mark_valid(ext2_filsys fs);
+extern void ext2fs_unmark_valid(ext2_filsys fs);
+extern int ext2fs_test_valid(ext2_filsys fs);
+extern void ext2fs_mark_ib_dirty(ext2_filsys fs);
+extern void ext2fs_mark_bb_dirty(ext2_filsys fs);
+extern int ext2fs_test_ib_dirty(ext2_filsys fs);
+extern int ext2fs_test_bb_dirty(ext2_filsys fs);
+extern int ext2fs_group_of_blk(ext2_filsys fs, blk_t blk);
+extern int ext2fs_group_of_ino(ext2_filsys fs, ext2_ino_t ino);
+extern blk_t ext2fs_inode_data_blocks(ext2_filsys fs,
+ struct ext2_inode *inode);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _EXT2FS_EXT2FS_H */
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/ext2fsP.h b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/ext2fsP.h
new file mode 100644
index 0000000..908b5d9
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/ext2fsP.h
@@ -0,0 +1,89 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ext2fsP.h --- private header file for ext2 library
+ *
+ * Copyright (C) 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include "ext2fs.h"
+
+/*
+ * Badblocks list
+ */
+struct ext2_struct_u32_list {
+ int magic;
+ int num;
+ int size;
+ __u32 *list;
+ int badblocks_flags;
+};
+
+struct ext2_struct_u32_iterate {
+ int magic;
+ ext2_u32_list bb;
+ int ptr;
+};
+
+
+/*
+ * Directory block iterator definition
+ */
+struct ext2_struct_dblist {
+ int magic;
+ ext2_filsys fs;
+ ext2_ino_t size;
+ ext2_ino_t count;
+ int sorted;
+ struct ext2_db_entry * list;
+};
+
+/*
+ * For directory iterators
+ */
+struct dir_context {
+ ext2_ino_t dir;
+ int flags;
+ char *buf;
+ int (*func)(ext2_ino_t dir,
+ int entry,
+ struct ext2_dir_entry *dirent,
+ int offset,
+ int blocksize,
+ char *buf,
+ void *priv_data);
+ void *priv_data;
+ errcode_t errcode;
+};
+
+/*
+ * Inode cache structure
+ */
+struct ext2_inode_cache {
+ void * buffer;
+ blk_t buffer_blk;
+ int cache_last;
+ int cache_size;
+ int refcount;
+ struct ext2_inode_cache_ent *cache;
+};
+
+struct ext2_inode_cache_ent {
+ ext2_ino_t ino;
+ struct ext2_inode inode;
+};
+
+/* Function prototypes */
+
+extern int ext2fs_process_dir_block(ext2_filsys fs,
+ blk_t *blocknr,
+ e2_blkcnt_t blockcnt,
+ blk_t ref_block,
+ int ref_offset,
+ void *priv_data);
+
+
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/ext2fs_inline.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/ext2fs_inline.c
new file mode 100644
index 0000000..da1cf5b
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/ext2fs_inline.c
@@ -0,0 +1,367 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ext2fs.h --- ext2fs
+ *
+ * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include "ext2fs.h"
+#include "bitops.h"
+#include <string.h>
+
+/*
+ * Allocate memory
+ */
+errcode_t ext2fs_get_mem(unsigned long size, void *ptr)
+{
+ void **pp = (void **)ptr;
+
+ *pp = malloc(size);
+ if (!*pp)
+ return EXT2_ET_NO_MEMORY;
+ return 0;
+}
+
+/*
+ * Free memory
+ */
+errcode_t ext2fs_free_mem(void *ptr)
+{
+ void **pp = (void **)ptr;
+
+ free(*pp);
+ *pp = 0;
+ return 0;
+}
+
+/*
+ * Resize memory
+ */
+errcode_t ext2fs_resize_mem(unsigned long EXT2FS_ATTR((unused)) old_size,
+ unsigned long size, void *ptr)
+{
+ void *p;
+
+ /* Use "memcpy" for pointer assignments here to avoid problems
+ * with C99 strict type aliasing rules. */
+ memcpy(&p, ptr, sizeof (p));
+ p = realloc(p, size);
+ if (!p)
+ return EXT2_ET_NO_MEMORY;
+ memcpy(ptr, &p, sizeof (p));
+ return 0;
+}
+
+/*
+ * Mark a filesystem superblock as dirty
+ */
+void ext2fs_mark_super_dirty(ext2_filsys fs)
+{
+ fs->flags |= EXT2_FLAG_DIRTY | EXT2_FLAG_CHANGED;
+}
+
+/*
+ * Mark a filesystem as changed
+ */
+void ext2fs_mark_changed(ext2_filsys fs)
+{
+ fs->flags |= EXT2_FLAG_CHANGED;
+}
+
+/*
+ * Check to see if a filesystem has changed
+ */
+int ext2fs_test_changed(ext2_filsys fs)
+{
+ return (fs->flags & EXT2_FLAG_CHANGED);
+}
+
+/*
+ * Mark a filesystem as valid
+ */
+void ext2fs_mark_valid(ext2_filsys fs)
+{
+ fs->flags |= EXT2_FLAG_VALID;
+}
+
+/*
+ * Mark a filesystem as NOT valid
+ */
+void ext2fs_unmark_valid(ext2_filsys fs)
+{
+ fs->flags &= ~EXT2_FLAG_VALID;
+}
+
+/*
+ * Check to see if a filesystem is valid
+ */
+int ext2fs_test_valid(ext2_filsys fs)
+{
+ return (fs->flags & EXT2_FLAG_VALID);
+}
+
+/*
+ * Mark the inode bitmap as dirty
+ */
+void ext2fs_mark_ib_dirty(ext2_filsys fs)
+{
+ fs->flags |= EXT2_FLAG_IB_DIRTY | EXT2_FLAG_CHANGED;
+}
+
+/*
+ * Mark the block bitmap as dirty
+ */
+void ext2fs_mark_bb_dirty(ext2_filsys fs)
+{
+ fs->flags |= EXT2_FLAG_BB_DIRTY | EXT2_FLAG_CHANGED;
+}
+
+/*
+ * Check to see if a filesystem's inode bitmap is dirty
+ */
+int ext2fs_test_ib_dirty(ext2_filsys fs)
+{
+ return (fs->flags & EXT2_FLAG_IB_DIRTY);
+}
+
+/*
+ * Check to see if a filesystem's block bitmap is dirty
+ */
+int ext2fs_test_bb_dirty(ext2_filsys fs)
+{
+ return (fs->flags & EXT2_FLAG_BB_DIRTY);
+}
+
+/*
+ * Return the group # of a block
+ */
+int ext2fs_group_of_blk(ext2_filsys fs, blk_t blk)
+{
+ return (blk - fs->super->s_first_data_block) /
+ fs->super->s_blocks_per_group;
+}
+
+/*
+ * Return the group # of an inode number
+ */
+int ext2fs_group_of_ino(ext2_filsys fs, ext2_ino_t ino)
+{
+ return (ino - 1) / fs->super->s_inodes_per_group;
+}
+
+blk_t ext2fs_inode_data_blocks(ext2_filsys fs,
+ struct ext2_inode *inode)
+{
+ return inode->i_blocks -
+ (inode->i_file_acl ? fs->blocksize >> 9 : 0);
+}
+
+
+
+
+
+
+
+
+
+__u16 ext2fs_swab16(__u16 val)
+{
+ return (val >> 8) | (val << 8);
+}
+
+__u32 ext2fs_swab32(__u32 val)
+{
+ return ((val>>24) | ((val>>8)&0xFF00) |
+ ((val<<8)&0xFF0000) | (val<<24));
+}
+
+int ext2fs_test_generic_bitmap(ext2fs_generic_bitmap bitmap,
+ blk_t bitno);
+
+int ext2fs_test_generic_bitmap(ext2fs_generic_bitmap bitmap,
+ blk_t bitno)
+{
+ if ((bitno < bitmap->start) || (bitno > bitmap->end)) {
+ ext2fs_warn_bitmap2(bitmap, EXT2FS_TEST_ERROR, bitno);
+ return 0;
+ }
+ return ext2fs_test_bit(bitno - bitmap->start, bitmap->bitmap);
+}
+
+int ext2fs_mark_block_bitmap(ext2fs_block_bitmap bitmap,
+ blk_t block)
+{
+ return ext2fs_mark_generic_bitmap((ext2fs_generic_bitmap)
+ bitmap,
+ block);
+}
+
+int ext2fs_unmark_block_bitmap(ext2fs_block_bitmap bitmap,
+ blk_t block)
+{
+ return ext2fs_unmark_generic_bitmap((ext2fs_generic_bitmap) bitmap,
+ block);
+}
+
+int ext2fs_test_block_bitmap(ext2fs_block_bitmap bitmap,
+ blk_t block)
+{
+ return ext2fs_test_generic_bitmap((ext2fs_generic_bitmap) bitmap,
+ block);
+}
+
+int ext2fs_mark_inode_bitmap(ext2fs_inode_bitmap bitmap,
+ ext2_ino_t inode)
+{
+ return ext2fs_mark_generic_bitmap((ext2fs_generic_bitmap) bitmap,
+ inode);
+}
+
+int ext2fs_unmark_inode_bitmap(ext2fs_inode_bitmap bitmap,
+ ext2_ino_t inode)
+{
+ return ext2fs_unmark_generic_bitmap((ext2fs_generic_bitmap) bitmap,
+ inode);
+}
+
+int ext2fs_test_inode_bitmap(ext2fs_inode_bitmap bitmap,
+ ext2_ino_t inode)
+{
+ return ext2fs_test_generic_bitmap((ext2fs_generic_bitmap) bitmap,
+ inode);
+}
+
+void ext2fs_fast_mark_block_bitmap(ext2fs_block_bitmap bitmap,
+ blk_t block)
+{
+ ext2fs_set_bit(block - bitmap->start, bitmap->bitmap);
+}
+
+void ext2fs_fast_unmark_block_bitmap(ext2fs_block_bitmap bitmap,
+ blk_t block)
+{
+ ext2fs_clear_bit(block - bitmap->start, bitmap->bitmap);
+}
+
+int ext2fs_fast_test_block_bitmap(ext2fs_block_bitmap bitmap,
+ blk_t block)
+{
+ return ext2fs_test_bit(block - bitmap->start, bitmap->bitmap);
+}
+
+void ext2fs_fast_mark_inode_bitmap(ext2fs_inode_bitmap bitmap,
+ ext2_ino_t inode)
+{
+ ext2fs_set_bit(inode - bitmap->start, bitmap->bitmap);
+}
+
+void ext2fs_fast_unmark_inode_bitmap(ext2fs_inode_bitmap bitmap,
+ ext2_ino_t inode)
+{
+ ext2fs_clear_bit(inode - bitmap->start, bitmap->bitmap);
+}
+
+int ext2fs_fast_test_inode_bitmap(ext2fs_inode_bitmap bitmap,
+ ext2_ino_t inode)
+{
+ return ext2fs_test_bit(inode - bitmap->start, bitmap->bitmap);
+}
+
+blk_t ext2fs_get_block_bitmap_start(ext2fs_block_bitmap bitmap)
+{
+ return bitmap->start;
+}
+
+ext2_ino_t ext2fs_get_inode_bitmap_start(ext2fs_inode_bitmap bitmap)
+{
+ return bitmap->start;
+}
+
+blk_t ext2fs_get_block_bitmap_end(ext2fs_block_bitmap bitmap)
+{
+ return bitmap->end;
+}
+
+ext2_ino_t ext2fs_get_inode_bitmap_end(ext2fs_inode_bitmap bitmap)
+{
+ return bitmap->end;
+}
+
+int ext2fs_test_block_bitmap_range(ext2fs_block_bitmap bitmap,
+ blk_t block, int num)
+{
+ int i;
+
+ if ((block < bitmap->start) || (block+num-1 > bitmap->end)) {
+ ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_TEST,
+ block, bitmap->description);
+ return 0;
+ }
+ for (i=0; i < num; i++) {
+ if (ext2fs_fast_test_block_bitmap(bitmap, block+i))
+ return 0;
+ }
+ return 1;
+}
+
+int ext2fs_fast_test_block_bitmap_range(ext2fs_block_bitmap bitmap,
+ blk_t block, int num)
+{
+ int i;
+
+ for (i=0; i < num; i++) {
+ if (ext2fs_fast_test_block_bitmap(bitmap, block+i))
+ return 0;
+ }
+ return 1;
+}
+
+void ext2fs_mark_block_bitmap_range(ext2fs_block_bitmap bitmap,
+ blk_t block, int num)
+{
+ int i;
+
+ if ((block < bitmap->start) || (block+num-1 > bitmap->end)) {
+ ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_MARK, block,
+ bitmap->description);
+ return;
+ }
+ for (i=0; i < num; i++)
+ ext2fs_set_bit(block + i - bitmap->start, bitmap->bitmap);
+}
+
+void ext2fs_fast_mark_block_bitmap_range(ext2fs_block_bitmap bitmap,
+ blk_t block, int num)
+{
+ int i;
+
+ for (i=0; i < num; i++)
+ ext2fs_set_bit(block + i - bitmap->start, bitmap->bitmap);
+}
+
+void ext2fs_unmark_block_bitmap_range(ext2fs_block_bitmap bitmap,
+ blk_t block, int num)
+{
+ int i;
+
+ if ((block < bitmap->start) || (block+num-1 > bitmap->end)) {
+ ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_UNMARK, block,
+ bitmap->description);
+ return;
+ }
+ for (i=0; i < num; i++)
+ ext2fs_clear_bit(block + i - bitmap->start, bitmap->bitmap);
+}
+
+void ext2fs_fast_unmark_block_bitmap_range(ext2fs_block_bitmap bitmap,
+ blk_t block, int num)
+{
+ int i;
+ for (i=0; i < num; i++)
+ ext2fs_clear_bit(block + i - bitmap->start, bitmap->bitmap);
+}
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/ext_attr.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/ext_attr.c
new file mode 100644
index 0000000..7ee41f2
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/ext_attr.c
@@ -0,0 +1,101 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ext_attr.c --- extended attribute blocks
+ *
+ * Copyright (C) 2001 Andreas Gruenbacher, <a.gruenbacher@computer.org>
+ *
+ * Copyright (C) 2002 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <time.h>
+
+#include "ext2_fs.h"
+#include "ext2_ext_attr.h"
+#include "ext2fs.h"
+
+errcode_t ext2fs_read_ext_attr(ext2_filsys fs, blk_t block, void *buf)
+{
+ errcode_t retval;
+
+ retval = io_channel_read_blk(fs->io, block, 1, buf);
+ if (retval)
+ return retval;
+#if BB_BIG_ENDIAN
+ if ((fs->flags & (EXT2_FLAG_SWAP_BYTES|
+ EXT2_FLAG_SWAP_BYTES_READ)) != 0)
+ ext2fs_swap_ext_attr(buf, buf, fs->blocksize, 1);
+#endif
+ return 0;
+}
+
+errcode_t ext2fs_write_ext_attr(ext2_filsys fs, blk_t block, void *inbuf)
+{
+ errcode_t retval;
+ char *write_buf;
+ char *buf = NULL;
+
+ if (BB_BIG_ENDIAN && ((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
+ (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE))) {
+ retval = ext2fs_get_mem(fs->blocksize, &buf);
+ if (retval)
+ return retval;
+ write_buf = buf;
+ ext2fs_swap_ext_attr(buf, inbuf, fs->blocksize, 1);
+ } else
+ write_buf = (char *) inbuf;
+ retval = io_channel_write_blk(fs->io, block, 1, write_buf);
+ if (buf)
+ ext2fs_free_mem(&buf);
+ if (!retval)
+ ext2fs_mark_changed(fs);
+ return retval;
+}
+
+/*
+ * This function adjusts the reference count of the EA block.
+ */
+errcode_t ext2fs_adjust_ea_refcount(ext2_filsys fs, blk_t blk,
+ char *block_buf, int adjust,
+ __u32 *newcount)
+{
+ errcode_t retval;
+ struct ext2_ext_attr_header *header;
+ char *buf = 0;
+
+ if ((blk >= fs->super->s_blocks_count) ||
+ (blk < fs->super->s_first_data_block))
+ return EXT2_ET_BAD_EA_BLOCK_NUM;
+
+ if (!block_buf) {
+ retval = ext2fs_get_mem(fs->blocksize, &buf);
+ if (retval)
+ return retval;
+ block_buf = buf;
+ }
+
+ retval = ext2fs_read_ext_attr(fs, blk, block_buf);
+ if (retval)
+ goto errout;
+
+ header = (struct ext2_ext_attr_header *) block_buf;
+ header->h_refcount += adjust;
+ if (newcount)
+ *newcount = header->h_refcount;
+
+ retval = ext2fs_write_ext_attr(fs, blk, block_buf);
+ if (retval)
+ goto errout;
+
+errout:
+ if (buf)
+ ext2fs_free_mem(&buf);
+ return retval;
+}
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/fileio.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/fileio.c
new file mode 100644
index 0000000..c56a21a
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/fileio.c
@@ -0,0 +1,377 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * fileio.c --- Simple file I/O routines
+ *
+ * Copyright (C) 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+struct ext2_file {
+ errcode_t magic;
+ ext2_filsys fs;
+ ext2_ino_t ino;
+ struct ext2_inode inode;
+ int flags;
+ __u64 pos;
+ blk_t blockno;
+ blk_t physblock;
+ char *buf;
+};
+
+#define BMAP_BUFFER (file->buf + fs->blocksize)
+
+errcode_t ext2fs_file_open2(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode *inode,
+ int flags, ext2_file_t *ret)
+{
+ ext2_file_t file;
+ errcode_t retval;
+
+ /*
+ * Don't let caller create or open a file for writing if the
+ * filesystem is read-only.
+ */
+ if ((flags & (EXT2_FILE_WRITE | EXT2_FILE_CREATE)) &&
+ !(fs->flags & EXT2_FLAG_RW))
+ return EXT2_ET_RO_FILSYS;
+
+ retval = ext2fs_get_mem(sizeof(struct ext2_file), &file);
+ if (retval)
+ return retval;
+
+ memset(file, 0, sizeof(struct ext2_file));
+ file->magic = EXT2_ET_MAGIC_EXT2_FILE;
+ file->fs = fs;
+ file->ino = ino;
+ file->flags = flags & EXT2_FILE_MASK;
+
+ if (inode) {
+ memcpy(&file->inode, inode, sizeof(struct ext2_inode));
+ } else {
+ retval = ext2fs_read_inode(fs, ino, &file->inode);
+ if (retval)
+ goto fail;
+ }
+
+ retval = ext2fs_get_mem(fs->blocksize * 3, &file->buf);
+ if (retval)
+ goto fail;
+
+ *ret = file;
+ return 0;
+
+fail:
+ ext2fs_free_mem(&file->buf);
+ ext2fs_free_mem(&file);
+ return retval;
+}
+
+errcode_t ext2fs_file_open(ext2_filsys fs, ext2_ino_t ino,
+ int flags, ext2_file_t *ret)
+{
+ return ext2fs_file_open2(fs, ino, NULL, flags, ret);
+}
+
+/*
+ * This function returns the filesystem handle of a file from the structure
+ */
+ext2_filsys ext2fs_file_get_fs(ext2_file_t file)
+{
+ if (file->magic != EXT2_ET_MAGIC_EXT2_FILE)
+ return 0;
+ return file->fs;
+}
+
+/*
+ * This function flushes the dirty block buffer out to disk if
+ * necessary.
+ */
+errcode_t ext2fs_file_flush(ext2_file_t file)
+{
+ errcode_t retval;
+ ext2_filsys fs;
+
+ EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
+ fs = file->fs;
+
+ if (!(file->flags & EXT2_FILE_BUF_VALID) ||
+ !(file->flags & EXT2_FILE_BUF_DIRTY))
+ return 0;
+
+ /*
+ * OK, the physical block hasn't been allocated yet.
+ * Allocate it.
+ */
+ if (!file->physblock) {
+ retval = ext2fs_bmap(fs, file->ino, &file->inode,
+ BMAP_BUFFER, file->ino ? BMAP_ALLOC : 0,
+ file->blockno, &file->physblock);
+ if (retval)
+ return retval;
+ }
+
+ retval = io_channel_write_blk(fs->io, file->physblock,
+ 1, file->buf);
+ if (retval)
+ return retval;
+
+ file->flags &= ~EXT2_FILE_BUF_DIRTY;
+
+ return retval;
+}
+
+/*
+ * This function synchronizes the file's block buffer and the current
+ * file position, possibly invalidating block buffer if necessary
+ */
+static errcode_t sync_buffer_position(ext2_file_t file)
+{
+ blk_t b;
+ errcode_t retval;
+
+ b = file->pos / file->fs->blocksize;
+ if (b != file->blockno) {
+ retval = ext2fs_file_flush(file);
+ if (retval)
+ return retval;
+ file->flags &= ~EXT2_FILE_BUF_VALID;
+ }
+ file->blockno = b;
+ return 0;
+}
+
+/*
+ * This function loads the file's block buffer with valid data from
+ * the disk as necessary.
+ *
+ * If dontfill is true, then skip initializing the buffer since we're
+ * going to be replacing its entire contents anyway. If set, then the
+ * function basically only sets file->physblock and EXT2_FILE_BUF_VALID
+ */
+#define DONTFILL 1
+static errcode_t load_buffer(ext2_file_t file, int dontfill)
+{
+ ext2_filsys fs = file->fs;
+ errcode_t retval;
+
+ if (!(file->flags & EXT2_FILE_BUF_VALID)) {
+ retval = ext2fs_bmap(fs, file->ino, &file->inode,
+ BMAP_BUFFER, 0, file->blockno,
+ &file->physblock);
+ if (retval)
+ return retval;
+ if (!dontfill) {
+ if (file->physblock) {
+ retval = io_channel_read_blk(fs->io,
+ file->physblock,
+ 1, file->buf);
+ if (retval)
+ return retval;
+ } else
+ memset(file->buf, 0, fs->blocksize);
+ }
+ file->flags |= EXT2_FILE_BUF_VALID;
+ }
+ return 0;
+}
+
+
+errcode_t ext2fs_file_close(ext2_file_t file)
+{
+ errcode_t retval;
+
+ EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
+
+ retval = ext2fs_file_flush(file);
+
+ ext2fs_free_mem(&file->buf);
+ ext2fs_free_mem(&file);
+
+ return retval;
+}
+
+
+errcode_t ext2fs_file_read(ext2_file_t file, void *buf,
+ unsigned int wanted, unsigned int *got)
+{
+ ext2_filsys fs;
+ errcode_t retval = 0;
+ unsigned int start, c, count = 0;
+ __u64 left;
+ char *ptr = (char *) buf;
+
+ EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
+ fs = file->fs;
+
+ while ((file->pos < EXT2_I_SIZE(&file->inode)) && (wanted > 0)) {
+ retval = sync_buffer_position(file);
+ if (retval)
+ goto fail;
+ retval = load_buffer(file, 0);
+ if (retval)
+ goto fail;
+
+ start = file->pos % fs->blocksize;
+ c = fs->blocksize - start;
+ if (c > wanted)
+ c = wanted;
+ left = EXT2_I_SIZE(&file->inode) - file->pos ;
+ if (c > left)
+ c = left;
+
+ memcpy(ptr, file->buf+start, c);
+ file->pos += c;
+ ptr += c;
+ count += c;
+ wanted -= c;
+ }
+
+fail:
+ if (got)
+ *got = count;
+ return retval;
+}
+
+
+errcode_t ext2fs_file_write(ext2_file_t file, const void *buf,
+ unsigned int nbytes, unsigned int *written)
+{
+ ext2_filsys fs;
+ errcode_t retval = 0;
+ unsigned int start, c, count = 0;
+ const char *ptr = (const char *) buf;
+
+ EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
+ fs = file->fs;
+
+ if (!(file->flags & EXT2_FILE_WRITE))
+ return EXT2_ET_FILE_RO;
+
+ while (nbytes > 0) {
+ retval = sync_buffer_position(file);
+ if (retval)
+ goto fail;
+
+ start = file->pos % fs->blocksize;
+ c = fs->blocksize - start;
+ if (c > nbytes)
+ c = nbytes;
+
+ /*
+ * We only need to do a read-modify-update cycle if
+ * we're doing a partial write.
+ */
+ retval = load_buffer(file, (c == fs->blocksize));
+ if (retval)
+ goto fail;
+
+ file->flags |= EXT2_FILE_BUF_DIRTY;
+ memcpy(file->buf+start, ptr, c);
+ file->pos += c;
+ ptr += c;
+ count += c;
+ nbytes -= c;
+ }
+
+fail:
+ if (written)
+ *written = count;
+ return retval;
+}
+
+errcode_t ext2fs_file_llseek(ext2_file_t file, __u64 offset,
+ int whence, __u64 *ret_pos)
+{
+ EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
+
+ if (whence == EXT2_SEEK_SET)
+ file->pos = offset;
+ else if (whence == EXT2_SEEK_CUR)
+ file->pos += offset;
+ else if (whence == EXT2_SEEK_END)
+ file->pos = EXT2_I_SIZE(&file->inode) + offset;
+ else
+ return EXT2_ET_INVALID_ARGUMENT;
+
+ if (ret_pos)
+ *ret_pos = file->pos;
+
+ return 0;
+}
+
+errcode_t ext2fs_file_lseek(ext2_file_t file, ext2_off_t offset,
+ int whence, ext2_off_t *ret_pos)
+{
+ __u64 loffset, ret_loffset;
+ errcode_t retval;
+
+ loffset = offset;
+ retval = ext2fs_file_llseek(file, loffset, whence, &ret_loffset);
+ if (ret_pos)
+ *ret_pos = (ext2_off_t) ret_loffset;
+ return retval;
+}
+
+
+/*
+ * This function returns the size of the file, according to the inode
+ */
+errcode_t ext2fs_file_get_lsize(ext2_file_t file, __u64 *ret_size)
+{
+ if (file->magic != EXT2_ET_MAGIC_EXT2_FILE)
+ return EXT2_ET_MAGIC_EXT2_FILE;
+ *ret_size = EXT2_I_SIZE(&file->inode);
+ return 0;
+}
+
+/*
+ * This function returns the size of the file, according to the inode
+ */
+ext2_off_t ext2fs_file_get_size(ext2_file_t file)
+{
+ __u64 size;
+
+ if (ext2fs_file_get_lsize(file, &size))
+ return 0;
+ if ((size >> 32) != 0)
+ return 0;
+ return size;
+}
+
+/*
+ * This function sets the size of the file, truncating it if necessary
+ *
+ * XXX still need to call truncate
+ */
+errcode_t ext2fs_file_set_size(ext2_file_t file, ext2_off_t size)
+{
+ errcode_t retval;
+ EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
+
+ file->inode.i_size = size;
+ file->inode.i_size_high = 0;
+ if (file->ino) {
+ retval = ext2fs_write_inode(file->fs, file->ino, &file->inode);
+ if (retval)
+ return retval;
+ }
+
+ /*
+ * XXX truncate inode if necessary
+ */
+
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/finddev.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/finddev.c
new file mode 100644
index 0000000..5e2cce9
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/finddev.c
@@ -0,0 +1,199 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * finddev.c -- this routine attempts to find a particular device in
+ * /dev
+ *
+ * Copyright (C) 2000 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#include <dirent.h>
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_SYS_MKDEV_H
+#include <sys/mkdev.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+struct dir_list {
+ char *name;
+ struct dir_list *next;
+};
+
+/*
+ * This function adds an entry to the directory list
+ */
+static void add_to_dirlist(const char *name, struct dir_list **list)
+{
+ struct dir_list *dp;
+
+ dp = xmalloc(sizeof(struct dir_list));
+ dp->name = xmalloc(strlen(name)+1);
+ strcpy(dp->name, name);
+ dp->next = *list;
+ *list = dp;
+}
+
+/*
+ * This function frees a directory list
+ */
+static void free_dirlist(struct dir_list **list)
+{
+ struct dir_list *dp, *next;
+
+ for (dp = *list; dp; dp = next) {
+ next = dp->next;
+ free(dp->name);
+ free(dp);
+ }
+ *list = 0;
+}
+
+static int scan_dir(char *dir_name, dev_t device, struct dir_list **list,
+ char **ret_path)
+{
+ DIR *dir;
+ struct dirent *dp;
+ char path[1024], *cp;
+ int dirlen;
+ struct stat st;
+
+ dirlen = strlen(dir_name);
+ if ((dir = opendir(dir_name)) == NULL)
+ return errno;
+ dp = readdir(dir);
+ while (dp) {
+ if (dirlen + strlen(dp->d_name) + 2 >= sizeof(path))
+ goto skip_to_next;
+ if (dp->d_name[0] == '.' &&
+ ((dp->d_name[1] == 0) ||
+ ((dp->d_name[1] == '.') && (dp->d_name[2] == 0))))
+ goto skip_to_next;
+ sprintf(path, "%s/%s", dir_name, dp->d_name);
+ if (stat(path, &st) < 0)
+ goto skip_to_next;
+ if (S_ISDIR(st.st_mode))
+ add_to_dirlist(path, list);
+ if (S_ISBLK(st.st_mode) && st.st_rdev == device) {
+ cp = xmalloc(strlen(path)+1);
+ strcpy(cp, path);
+ *ret_path = cp;
+ goto success;
+ }
+ skip_to_next:
+ dp = readdir(dir);
+ }
+success:
+ closedir(dir);
+ return 0;
+}
+
+/*
+ * This function finds the pathname to a block device with a given
+ * device number. It returns a pointer to allocated memory to the
+ * pathname on success, and NULL on failure.
+ */
+char *ext2fs_find_block_device(dev_t device)
+{
+ struct dir_list *list = 0, *new_list = 0;
+ struct dir_list *current;
+ char *ret_path = 0;
+
+ /*
+ * Add the starting directories to search...
+ */
+ add_to_dirlist("/devices", &list);
+ add_to_dirlist("/devfs", &list);
+ add_to_dirlist("/dev", &list);
+
+ while (list) {
+ current = list;
+ list = list->next;
+#ifdef DEBUG
+ printf("Scanning directory %s\n", current->name);
+#endif
+ scan_dir(current->name, device, &new_list, &ret_path);
+ free(current->name);
+ free(current);
+ if (ret_path)
+ break;
+ /*
+ * If we're done checking at this level, descend to
+ * the next level of subdirectories. (breadth-first)
+ */
+ if (list == 0) {
+ list = new_list;
+ new_list = 0;
+ }
+ }
+ free_dirlist(&list);
+ free_dirlist(&new_list);
+ return ret_path;
+}
+
+
+#ifdef DEBUG
+int main(int argc, char** argv)
+{
+ char *devname, *tmp;
+ int major, minor;
+ dev_t device;
+ const char *errmsg = "Cannot parse %s: %s\n";
+
+ if ((argc != 2) && (argc != 3)) {
+ fprintf(stderr, "Usage: %s device_number\n", argv[0]);
+ fprintf(stderr, "\t: %s major minor\n", argv[0]);
+ exit(1);
+ }
+ if (argc == 2) {
+ device = strtoul(argv[1], &tmp, 0);
+ if (*tmp) {
+ fprintf(stderr, errmsg, "device number", argv[1]);
+ exit(1);
+ }
+ } else {
+ major = strtoul(argv[1], &tmp, 0);
+ if (*tmp) {
+ fprintf(stderr, errmsg, "major number", argv[1]);
+ exit(1);
+ }
+ minor = strtoul(argv[2], &tmp, 0);
+ if (*tmp) {
+ fprintf(stderr, errmsg, "minor number", argv[2]);
+ exit(1);
+ }
+ device = makedev(major, minor);
+ printf("Looking for device 0x%04x (%d:%d)\n", device,
+ major, minor);
+ }
+ devname = ext2fs_find_block_device(device);
+ if (devname) {
+ printf("Found device! %s\n", devname);
+ free(devname);
+ } else {
+ printf("Cannot find device.\n");
+ }
+ return 0;
+}
+
+#endif
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/flushb.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/flushb.c
new file mode 100644
index 0000000..e429826
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/flushb.c
@@ -0,0 +1,83 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * flushb.c --- Hides system-dependent information for both syncing a
+ * device to disk and to flush any buffers from disk cache.
+ *
+ * Copyright (C) 2000 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#if HAVE_SYS_MOUNT_H
+#include <sys/param.h>
+#include <sys/mount.h> /* This may define BLKFLSBUF */
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+/*
+ * For Linux, define BLKFLSBUF and FDFLUSH if necessary, since
+ * not all portable header file does so for us. This really should be
+ * fixed in the glibc header files. (Recent glibcs appear to define
+ * BLKFLSBUF in sys/mount.h, but FDFLUSH still doesn't seem to be
+ * defined anywhere portable.) Until then....
+ */
+#ifdef __linux__
+#ifndef BLKFLSBUF
+#define BLKFLSBUF _IO(0x12,97) /* flush buffer cache */
+#endif
+#ifndef FDFLUSH
+#define FDFLUSH _IO(2,0x4b) /* flush floppy disk */
+#endif
+#endif
+
+/*
+ * This function will sync a device/file, and optionally attempt to
+ * flush the buffer cache. The latter is basically only useful for
+ * system benchmarks and for torturing systems in burn-in tests. :)
+ */
+errcode_t ext2fs_sync_device(int fd, int flushb)
+{
+ /*
+ * We always sync the device in case we're running on old
+ * kernels for which we can lose data if we don't. (There
+ * still is a race condition for those kernels, but this
+ * reduces it greatly.)
+ */
+ if (fsync (fd) == -1)
+ return errno;
+
+ if (flushb) {
+
+#ifdef BLKFLSBUF
+ if (ioctl (fd, BLKFLSBUF, 0) == 0)
+ return 0;
+#else
+#ifdef __GNUC__
+# warning BLKFLSBUF not defined
+#endif /* __GNUC__ */
+#endif
+#ifdef FDFLUSH
+ ioctl (fd, FDFLUSH, 0); /* In case this is a floppy */
+#else
+#ifdef __GNUC__
+# warning FDFLUSH not defined
+#endif /* __GNUC__ */
+#endif
+ }
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/freefs.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/freefs.c
new file mode 100644
index 0000000..65c4ee7
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/freefs.c
@@ -0,0 +1,128 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * freefs.c --- free an ext2 filesystem
+ *
+ * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fsP.h"
+
+static void ext2fs_free_inode_cache(struct ext2_inode_cache *icache);
+
+void ext2fs_free(ext2_filsys fs)
+{
+ if (!fs || (fs->magic != EXT2_ET_MAGIC_EXT2FS_FILSYS))
+ return;
+ if (fs->image_io != fs->io) {
+ if (fs->image_io)
+ io_channel_close(fs->image_io);
+ }
+ if (fs->io) {
+ io_channel_close(fs->io);
+ }
+ ext2fs_free_mem(&fs->device_name);
+ ext2fs_free_mem(&fs->super);
+ ext2fs_free_mem(&fs->orig_super);
+ ext2fs_free_mem(&fs->group_desc);
+ ext2fs_free_block_bitmap(fs->block_map);
+ ext2fs_free_inode_bitmap(fs->inode_map);
+
+ ext2fs_badblocks_list_free(fs->badblocks);
+ fs->badblocks = 0;
+
+ ext2fs_free_dblist(fs->dblist);
+
+ if (fs->icache)
+ ext2fs_free_inode_cache(fs->icache);
+
+ fs->magic = 0;
+
+ ext2fs_free_mem(&fs);
+}
+
+void ext2fs_free_generic_bitmap(ext2fs_inode_bitmap bitmap)
+{
+ if (!bitmap || (bitmap->magic != EXT2_ET_MAGIC_GENERIC_BITMAP))
+ return;
+
+ bitmap->magic = 0;
+ ext2fs_free_mem(&bitmap->description);
+ ext2fs_free_mem(&bitmap->bitmap);
+ ext2fs_free_mem(&bitmap);
+}
+
+void ext2fs_free_inode_bitmap(ext2fs_inode_bitmap bitmap)
+{
+ if (!bitmap || (bitmap->magic != EXT2_ET_MAGIC_INODE_BITMAP))
+ return;
+
+ bitmap->magic = EXT2_ET_MAGIC_GENERIC_BITMAP;
+ ext2fs_free_generic_bitmap(bitmap);
+}
+
+void ext2fs_free_block_bitmap(ext2fs_block_bitmap bitmap)
+{
+ if (!bitmap || (bitmap->magic != EXT2_ET_MAGIC_BLOCK_BITMAP))
+ return;
+
+ bitmap->magic = EXT2_ET_MAGIC_GENERIC_BITMAP;
+ ext2fs_free_generic_bitmap(bitmap);
+}
+
+/*
+ * Free the inode cache structure
+ */
+static void ext2fs_free_inode_cache(struct ext2_inode_cache *icache)
+{
+ if (--icache->refcount)
+ return;
+ ext2fs_free_mem(&icache->buffer);
+ ext2fs_free_mem(&icache->cache);
+ icache->buffer_blk = 0;
+ ext2fs_free_mem(&icache);
+}
+
+/*
+ * This procedure frees a badblocks list.
+ */
+void ext2fs_u32_list_free(ext2_u32_list bb)
+{
+ if (!bb || bb->magic != EXT2_ET_MAGIC_BADBLOCKS_LIST)
+ return;
+
+ ext2fs_free_mem(&bb->list);
+ ext2fs_free_mem(&bb);
+}
+
+void ext2fs_badblocks_list_free(ext2_badblocks_list bb)
+{
+ ext2fs_u32_list_free((ext2_u32_list) bb);
+}
+
+
+/*
+ * Free a directory block list
+ */
+void ext2fs_free_dblist(ext2_dblist dblist)
+{
+ if (!dblist || (dblist->magic != EXT2_ET_MAGIC_DBLIST))
+ return;
+
+ ext2fs_free_mem(&dblist->list);
+ if (dblist->fs && dblist->fs->dblist == dblist)
+ dblist->fs->dblist = 0;
+ dblist->magic = 0;
+ ext2fs_free_mem(&dblist);
+}
+
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/gen_bitmap.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/gen_bitmap.c
new file mode 100644
index 0000000..d0869c9
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/gen_bitmap.c
@@ -0,0 +1,49 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * gen_bitmap.c --- Generic bitmap routines that used to be inlined.
+ *
+ * Copyright (C) 2001 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+int ext2fs_mark_generic_bitmap(ext2fs_generic_bitmap bitmap,
+ __u32 bitno)
+{
+ if ((bitno < bitmap->start) || (bitno > bitmap->end)) {
+ ext2fs_warn_bitmap2(bitmap, EXT2FS_MARK_ERROR, bitno);
+ return 0;
+ }
+ return ext2fs_set_bit(bitno - bitmap->start, bitmap->bitmap);
+}
+
+int ext2fs_unmark_generic_bitmap(ext2fs_generic_bitmap bitmap,
+ blk_t bitno)
+{
+ if ((bitno < bitmap->start) || (bitno > bitmap->end)) {
+ ext2fs_warn_bitmap2(bitmap, EXT2FS_UNMARK_ERROR, bitno);
+ return 0;
+ }
+ return ext2fs_clear_bit(bitno - bitmap->start, bitmap->bitmap);
+}
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/get_pathname.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/get_pathname.c
new file mode 100644
index 0000000..a98b2b9
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/get_pathname.c
@@ -0,0 +1,157 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * get_pathname.c --- do directry/inode -> name translation
+ *
+ * Copyright (C) 1993, 1994, 1995 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ *
+ * ext2fs_get_pathname(fs, dir, ino, name)
+ *
+ * This function translates takes two inode numbers into a
+ * string, placing the result in <name>. <dir> is the containing
+ * directory inode, and <ino> is the inode number itself. If
+ * <ino> is zero, then ext2fs_get_pathname will return pathname
+ * of the the directory <dir>.
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+struct get_pathname_struct {
+ ext2_ino_t search_ino;
+ ext2_ino_t parent;
+ char *name;
+ errcode_t errcode;
+};
+
+#ifdef __TURBOC__
+# pragma argsused
+#endif
+static int get_pathname_proc(struct ext2_dir_entry *dirent,
+ int offset EXT2FS_ATTR((unused)),
+ int blocksize EXT2FS_ATTR((unused)),
+ char *buf EXT2FS_ATTR((unused)),
+ void *priv_data)
+{
+ struct get_pathname_struct *gp;
+ errcode_t retval;
+
+ gp = (struct get_pathname_struct *) priv_data;
+
+ if (((dirent->name_len & 0xFF) == 2) &&
+ !strncmp(dirent->name, "..", 2))
+ gp->parent = dirent->inode;
+ if (dirent->inode == gp->search_ino) {
+ retval = ext2fs_get_mem((dirent->name_len & 0xFF) + 1,
+ &gp->name);
+ if (retval) {
+ gp->errcode = retval;
+ return DIRENT_ABORT;
+ }
+ strncpy(gp->name, dirent->name, (dirent->name_len & 0xFF));
+ gp->name[dirent->name_len & 0xFF] = '\0';
+ return DIRENT_ABORT;
+ }
+ return 0;
+}
+
+static errcode_t ext2fs_get_pathname_int(ext2_filsys fs, ext2_ino_t dir,
+ ext2_ino_t ino, int maxdepth,
+ char *buf, char **name)
+{
+ struct get_pathname_struct gp;
+ char *parent_name, *ret;
+ errcode_t retval;
+
+ if (dir == ino) {
+ retval = ext2fs_get_mem(2, name);
+ if (retval)
+ return retval;
+ strcpy(*name, (dir == EXT2_ROOT_INO) ? "/" : ".");
+ return 0;
+ }
+
+ if (!dir || (maxdepth < 0)) {
+ retval = ext2fs_get_mem(4, name);
+ if (retval)
+ return retval;
+ strcpy(*name, "...");
+ return 0;
+ }
+
+ gp.search_ino = ino;
+ gp.parent = 0;
+ gp.name = 0;
+ gp.errcode = 0;
+
+ retval = ext2fs_dir_iterate(fs, dir, 0, buf, get_pathname_proc, &gp);
+ if (retval)
+ goto cleanup;
+ if (gp.errcode) {
+ retval = gp.errcode;
+ goto cleanup;
+ }
+
+ retval = ext2fs_get_pathname_int(fs, gp.parent, dir, maxdepth-1,
+ buf, &parent_name);
+ if (retval)
+ goto cleanup;
+ if (!ino) {
+ *name = parent_name;
+ return 0;
+ }
+
+ if (gp.name)
+ retval = ext2fs_get_mem(strlen(parent_name)+strlen(gp.name)+2,
+ &ret);
+ else
+ retval = ext2fs_get_mem(strlen(parent_name)+5, &ret);
+ if (retval)
+ goto cleanup;
+
+ ret[0] = 0;
+ if (parent_name[1])
+ strcat(ret, parent_name);
+ strcat(ret, "/");
+ if (gp.name)
+ strcat(ret, gp.name);
+ else
+ strcat(ret, "???");
+ *name = ret;
+ ext2fs_free_mem(&parent_name);
+ retval = 0;
+
+cleanup:
+ ext2fs_free_mem(&gp.name);
+ return retval;
+}
+
+errcode_t ext2fs_get_pathname(ext2_filsys fs, ext2_ino_t dir, ext2_ino_t ino,
+ char **name)
+{
+ char *buf;
+ errcode_t retval;
+
+ EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+ retval = ext2fs_get_mem(fs->blocksize, &buf);
+ if (retval)
+ return retval;
+ if (dir == ino)
+ ino = 0;
+ retval = ext2fs_get_pathname_int(fs, dir, ino, 32, buf, name);
+ ext2fs_free_mem(&buf);
+ return retval;
+
+}
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/getsectsize.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/getsectsize.c
new file mode 100644
index 0000000..163ec65
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/getsectsize.c
@@ -0,0 +1,58 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * getsectsize.c --- get the sector size of a device.
+ *
+ * Copyright (C) 1995, 1995 Theodore Ts'o.
+ * Copyright (C) 2003 VMware, Inc.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <fcntl.h>
+#ifdef HAVE_LINUX_FD_H
+#include <sys/ioctl.h>
+#include <linux/fd.h>
+#endif
+
+#if defined(__linux__) && defined(_IO) && !defined(BLKSSZGET)
+#define BLKSSZGET _IO(0x12,104)/* get block device sector size */
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+/*
+ * Returns the number of blocks in a partition
+ */
+errcode_t ext2fs_get_device_sectsize(const char *file, int *sectsize)
+{
+ int fd;
+
+#ifdef CONFIG_LFS
+ fd = open64(file, O_RDONLY);
+#else
+ fd = open(file, O_RDONLY);
+#endif
+ if (fd < 0)
+ return errno;
+
+#ifdef BLKSSZGET
+ if (ioctl(fd, BLKSSZGET, sectsize) >= 0) {
+ close(fd);
+ return 0;
+ }
+#endif
+ *sectsize = 0;
+ close(fd);
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/getsize.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/getsize.c
new file mode 100644
index 0000000..516886c
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/getsize.c
@@ -0,0 +1,291 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * getsize.c --- get the size of a partition.
+ *
+ * Copyright (C) 1995, 1995 Theodore Ts'o.
+ * Copyright (C) 2003 VMware, Inc.
+ *
+ * Windows version of ext2fs_get_device_size by Chris Li, VMware.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <fcntl.h>
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#ifdef HAVE_LINUX_FD_H
+#include <linux/fd.h>
+#endif
+#ifdef HAVE_SYS_DISKLABEL_H
+#include <sys/disklabel.h>
+#endif
+#ifdef HAVE_SYS_DISK_H
+#ifdef HAVE_SYS_QUEUE_H
+#include <sys/queue.h> /* for LIST_HEAD */
+#endif
+#include <sys/disk.h>
+#endif
+#ifdef __linux__
+#include <sys/utsname.h>
+#endif
+
+#if defined(__linux__) && defined(_IO) && !defined(BLKGETSIZE)
+#define BLKGETSIZE _IO(0x12,96) /* return device size */
+#endif
+
+#if defined(__linux__) && defined(_IOR) && !defined(BLKGETSIZE64)
+#define BLKGETSIZE64 _IOR(0x12,114,size_t) /* return device size in bytes (u64 *arg) */
+#endif
+
+#ifdef APPLE_DARWIN
+#define BLKGETSIZE DKIOCGETBLOCKCOUNT32
+#endif /* APPLE_DARWIN */
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+#if defined(__CYGWIN__) || defined (WIN32)
+#include <windows.h>
+#include <winioctl.h>
+
+#if (_WIN32_WINNT >= 0x0500)
+#define HAVE_GET_FILE_SIZE_EX 1
+#endif
+
+errcode_t ext2fs_get_device_size(const char *file, int blocksize,
+ blk_t *retblocks)
+{
+ HANDLE dev;
+ PARTITION_INFORMATION pi;
+ DISK_GEOMETRY gi;
+ DWORD retbytes;
+#ifdef HAVE_GET_FILE_SIZE_EX
+ LARGE_INTEGER filesize;
+#else
+ DWORD filesize;
+#endif /* HAVE_GET_FILE_SIZE_EX */
+
+ dev = CreateFile(file, GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE ,
+ NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+
+ if (dev == INVALID_HANDLE_VALUE)
+ return EBADF;
+ if (DeviceIoControl(dev, IOCTL_DISK_GET_PARTITION_INFO,
+ &pi, sizeof(PARTITION_INFORMATION),
+ &pi, sizeof(PARTITION_INFORMATION),
+ &retbytes, NULL)) {
+
+ *retblocks = pi.PartitionLength.QuadPart / blocksize;
+
+ } else if (DeviceIoControl(dev, IOCTL_DISK_GET_DRIVE_GEOMETRY,
+ &gi, sizeof(DISK_GEOMETRY),
+ &gi, sizeof(DISK_GEOMETRY),
+ &retbytes, NULL)) {
+
+ *retblocks = gi.BytesPerSector *
+ gi.SectorsPerTrack *
+ gi.TracksPerCylinder *
+ gi.Cylinders.QuadPart / blocksize;
+
+#ifdef HAVE_GET_FILE_SIZE_EX
+ } else if (GetFileSizeEx(dev, &filesize)) {
+ *retblocks = filesize.QuadPart / blocksize;
+ }
+#else
+ } else {
+ filesize = GetFileSize(dev, NULL);
+ if (INVALID_FILE_SIZE != filesize) {
+ *retblocks = filesize / blocksize;
+ }
+ }
+#endif /* HAVE_GET_FILE_SIZE_EX */
+
+ CloseHandle(dev);
+ return 0;
+}
+
+#else
+
+static int valid_offset (int fd, ext2_loff_t offset)
+{
+ char ch;
+
+ if (ext2fs_llseek (fd, offset, 0) < 0)
+ return 0;
+ if (read (fd, &ch, 1) < 1)
+ return 0;
+ return 1;
+}
+
+/*
+ * Returns the number of blocks in a partition
+ */
+errcode_t ext2fs_get_device_size(const char *file, int blocksize,
+ blk_t *retblocks)
+{
+ int fd;
+ int valid_blkgetsize64 = 1;
+#ifdef __linux__
+ struct utsname ut;
+#endif
+ unsigned long long size64;
+ unsigned long size;
+ ext2_loff_t high, low;
+#ifdef FDGETPRM
+ struct floppy_struct this_floppy;
+#endif
+#ifdef HAVE_SYS_DISKLABEL_H
+ int part;
+ struct disklabel lab;
+ struct partition *pp;
+ char ch;
+#endif /* HAVE_SYS_DISKLABEL_H */
+
+#ifdef CONFIG_LFS
+ fd = open64(file, O_RDONLY);
+#else
+ fd = open(file, O_RDONLY);
+#endif
+ if (fd < 0)
+ return errno;
+
+#ifdef DKIOCGETBLOCKCOUNT /* For Apple Darwin */
+ if (ioctl(fd, DKIOCGETBLOCKCOUNT, &size64) >= 0) {
+ if ((sizeof(*retblocks) < sizeof(unsigned long long))
+ && ((size64 / (blocksize / 512)) > 0xFFFFFFFF))
+ return EFBIG;
+ close(fd);
+ *retblocks = size64 / (blocksize / 512);
+ return 0;
+ }
+#endif
+
+#ifdef BLKGETSIZE64
+#ifdef __linux__
+ if ((uname(&ut) == 0) &&
+ ((ut.release[0] == '2') && (ut.release[1] == '.') &&
+ (ut.release[2] < '6') && (ut.release[3] == '.')))
+ valid_blkgetsize64 = 0;
+#endif
+ if (valid_blkgetsize64 &&
+ ioctl(fd, BLKGETSIZE64, &size64) >= 0) {
+ if ((sizeof(*retblocks) < sizeof(unsigned long long))
+ && ((size64 / blocksize) > 0xFFFFFFFF))
+ return EFBIG;
+ close(fd);
+ *retblocks = size64 / blocksize;
+ return 0;
+ }
+#endif
+
+#ifdef BLKGETSIZE
+ if (ioctl(fd, BLKGETSIZE, &size) >= 0) {
+ close(fd);
+ *retblocks = size / (blocksize / 512);
+ return 0;
+ }
+#endif
+
+#ifdef FDGETPRM
+ if (ioctl(fd, FDGETPRM, &this_floppy) >= 0) {
+ close(fd);
+ *retblocks = this_floppy.size / (blocksize / 512);
+ return 0;
+ }
+#endif
+
+#ifdef HAVE_SYS_DISKLABEL_H
+#if defined(DIOCGMEDIASIZE)
+ {
+ off_t ms;
+ u_int bs;
+ if (ioctl(fd, DIOCGMEDIASIZE, &ms) >= 0) {
+ *retblocks = ms / blocksize;
+ return 0;
+ }
+ }
+#elif defined(DIOCGDINFO)
+ /* old disklabel interface */
+ part = strlen(file) - 1;
+ if (part >= 0) {
+ ch = file[part];
+ if (isdigit(ch))
+ part = 0;
+ else if (ch >= 'a' && ch <= 'h')
+ part = ch - 'a';
+ else
+ part = -1;
+ }
+ if (part >= 0 && (ioctl(fd, DIOCGDINFO, (char *)&lab) >= 0)) {
+ pp = &lab.d_partitions[part];
+ if (pp->p_size) {
+ close(fd);
+ *retblocks = pp->p_size / (blocksize / 512);
+ return 0;
+ }
+ }
+#endif /* defined(DIOCG*) */
+#endif /* HAVE_SYS_DISKLABEL_H */
+
+ /*
+ * OK, we couldn't figure it out by using a specialized ioctl,
+ * which is generally the best way. So do binary search to
+ * find the size of the partition.
+ */
+ low = 0;
+ for (high = 1024; valid_offset (fd, high); high *= 2)
+ low = high;
+ while (low < high - 1)
+ {
+ const ext2_loff_t mid = (low + high) / 2;
+
+ if (valid_offset (fd, mid))
+ low = mid;
+ else
+ high = mid;
+ }
+ valid_offset (fd, 0);
+ close(fd);
+ size64 = low + 1;
+ if ((sizeof(*retblocks) < sizeof(unsigned long long))
+ && ((size64 / blocksize) > 0xFFFFFFFF))
+ return EFBIG;
+ *retblocks = size64 / blocksize;
+ return 0;
+}
+
+#endif /* WIN32 */
+
+#ifdef DEBUG
+int main(int argc, char **argv)
+{
+ blk_t blocks;
+ int retval;
+
+ if (argc < 2) {
+ fprintf(stderr, "Usage: %s device\n", argv[0]);
+ exit(1);
+ }
+
+ retval = ext2fs_get_device_size(argv[1], 1024, &blocks);
+ if (retval) {
+ com_err(argv[0], retval,
+ "while calling ext2fs_get_device_size");
+ exit(1);
+ }
+ printf("Device %s has %d 1k blocks.\n", argv[1], blocks);
+ exit(0);
+}
+#endif
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/icount.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/icount.c
new file mode 100644
index 0000000..7ab5f51
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/icount.c
@@ -0,0 +1,467 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * icount.c --- an efficient inode count abstraction
+ *
+ * Copyright (C) 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <string.h>
+#include <stdio.h>
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+/*
+ * The data storage strategy used by icount relies on the observation
+ * that most inode counts are either zero (for non-allocated inodes),
+ * one (for most files), and only a few that are two or more
+ * (directories and files that are linked to more than one directory).
+ *
+ * Also, e2fsck tends to load the icount data sequentially.
+ *
+ * So, we use an inode bitmap to indicate which inodes have a count of
+ * one, and then use a sorted list to store the counts for inodes
+ * which are greater than one.
+ *
+ * We also use an optional bitmap to indicate which inodes are already
+ * in the sorted list, to speed up the use of this abstraction by
+ * e2fsck's pass 2. Pass 2 increments inode counts as it finds them,
+ * so this extra bitmap avoids searching the sorted list to see if a
+ * particular inode is on the sorted list already.
+ */
+
+struct ext2_icount_el {
+ ext2_ino_t ino;
+ __u16 count;
+};
+
+struct ext2_icount {
+ errcode_t magic;
+ ext2fs_inode_bitmap single;
+ ext2fs_inode_bitmap multiple;
+ ext2_ino_t count;
+ ext2_ino_t size;
+ ext2_ino_t num_inodes;
+ ext2_ino_t cursor;
+ struct ext2_icount_el *list;
+};
+
+void ext2fs_free_icount(ext2_icount_t icount)
+{
+ if (!icount)
+ return;
+
+ icount->magic = 0;
+ ext2fs_free_mem(&icount->list);
+ ext2fs_free_inode_bitmap(icount->single);
+ ext2fs_free_inode_bitmap(icount->multiple);
+ ext2fs_free_mem(&icount);
+}
+
+errcode_t ext2fs_create_icount2(ext2_filsys fs, int flags, unsigned int size,
+ ext2_icount_t hint, ext2_icount_t *ret)
+{
+ ext2_icount_t icount;
+ errcode_t retval;
+ size_t bytes;
+ ext2_ino_t i;
+
+ if (hint) {
+ EXT2_CHECK_MAGIC(hint, EXT2_ET_MAGIC_ICOUNT);
+ if (hint->size > size)
+ size = (size_t) hint->size;
+ }
+
+ retval = ext2fs_get_mem(sizeof(struct ext2_icount), &icount);
+ if (retval)
+ return retval;
+ memset(icount, 0, sizeof(struct ext2_icount));
+
+ retval = ext2fs_allocate_inode_bitmap(fs, 0,
+ &icount->single);
+ if (retval)
+ goto errout;
+
+ if (flags & EXT2_ICOUNT_OPT_INCREMENT) {
+ retval = ext2fs_allocate_inode_bitmap(fs, 0,
+ &icount->multiple);
+ if (retval)
+ goto errout;
+ } else
+ icount->multiple = 0;
+
+ if (size) {
+ icount->size = size;
+ } else {
+ /*
+ * Figure out how many special case inode counts we will
+ * have. We know we will need one for each directory;
+ * we also need to reserve some extra room for file links
+ */
+ retval = ext2fs_get_num_dirs(fs, &icount->size);
+ if (retval)
+ goto errout;
+ icount->size += fs->super->s_inodes_count / 50;
+ }
+
+ bytes = (size_t) (icount->size * sizeof(struct ext2_icount_el));
+ retval = ext2fs_get_mem(bytes, &icount->list);
+ if (retval)
+ goto errout;
+ memset(icount->list, 0, bytes);
+
+ icount->magic = EXT2_ET_MAGIC_ICOUNT;
+ icount->count = 0;
+ icount->cursor = 0;
+ icount->num_inodes = fs->super->s_inodes_count;
+
+ /*
+ * Populate the sorted list with those entries which were
+ * found in the hint icount (since those are ones which will
+ * likely need to be in the sorted list this time around).
+ */
+ if (hint) {
+ for (i=0; i < hint->count; i++)
+ icount->list[i].ino = hint->list[i].ino;
+ icount->count = hint->count;
+ }
+
+ *ret = icount;
+ return 0;
+
+errout:
+ ext2fs_free_icount(icount);
+ return retval;
+}
+
+errcode_t ext2fs_create_icount(ext2_filsys fs, int flags,
+ unsigned int size,
+ ext2_icount_t *ret)
+{
+ return ext2fs_create_icount2(fs, flags, size, 0, ret);
+}
+
+/*
+ * insert_icount_el() --- Insert a new entry into the sorted list at a
+ * specified position.
+ */
+static struct ext2_icount_el *insert_icount_el(ext2_icount_t icount,
+ ext2_ino_t ino, int pos)
+{
+ struct ext2_icount_el *el;
+ errcode_t retval;
+ ext2_ino_t new_size = 0;
+ int num;
+
+ if (icount->count >= icount->size) {
+ if (icount->count) {
+ new_size = icount->list[(unsigned)icount->count-1].ino;
+ new_size = (ext2_ino_t) (icount->count *
+ ((float) icount->num_inodes / new_size));
+ }
+ if (new_size < (icount->size + 100))
+ new_size = icount->size + 100;
+ retval = ext2fs_resize_mem((size_t) icount->size *
+ sizeof(struct ext2_icount_el),
+ (size_t) new_size *
+ sizeof(struct ext2_icount_el),
+ &icount->list);
+ if (retval)
+ return 0;
+ icount->size = new_size;
+ }
+ num = (int) icount->count - pos;
+ if (num < 0)
+ return 0; /* should never happen */
+ if (num) {
+ memmove(&icount->list[pos+1], &icount->list[pos],
+ sizeof(struct ext2_icount_el) * num);
+ }
+ icount->count++;
+ el = &icount->list[pos];
+ el->count = 0;
+ el->ino = ino;
+ return el;
+}
+
+/*
+ * get_icount_el() --- given an inode number, try to find icount
+ * information in the sorted list. If the create flag is set,
+ * and we can't find an entry, create one in the sorted list.
+ */
+static struct ext2_icount_el *get_icount_el(ext2_icount_t icount,
+ ext2_ino_t ino, int create)
+{
+ float range;
+ int low, high, mid;
+ ext2_ino_t lowval, highval;
+
+ if (!icount || !icount->list)
+ return 0;
+
+ if (create && ((icount->count == 0) ||
+ (ino > icount->list[(unsigned)icount->count-1].ino))) {
+ return insert_icount_el(icount, ino, (unsigned) icount->count);
+ }
+ if (icount->count == 0)
+ return 0;
+
+ if (icount->cursor >= icount->count)
+ icount->cursor = 0;
+ if (ino == icount->list[icount->cursor].ino)
+ return &icount->list[icount->cursor++];
+ low = 0;
+ high = (int) icount->count-1;
+ while (low <= high) {
+ if (low == high)
+ mid = low;
+ else {
+ /* Interpolate for efficiency */
+ lowval = icount->list[low].ino;
+ highval = icount->list[high].ino;
+
+ if (ino < lowval)
+ range = 0;
+ else if (ino > highval)
+ range = 1;
+ else
+ range = ((float) (ino - lowval)) /
+ (highval - lowval);
+ mid = low + ((int) (range * (high-low)));
+ }
+ if (ino == icount->list[mid].ino) {
+ icount->cursor = mid+1;
+ return &icount->list[mid];
+ }
+ if (ino < icount->list[mid].ino)
+ high = mid-1;
+ else
+ low = mid+1;
+ }
+ /*
+ * If we need to create a new entry, it should be right at
+ * low (where high will be left at low-1).
+ */
+ if (create)
+ return insert_icount_el(icount, ino, low);
+ return 0;
+}
+
+errcode_t ext2fs_icount_validate(ext2_icount_t icount, FILE *out)
+{
+ errcode_t ret = 0;
+ unsigned int i;
+ const char *bad = "bad icount";
+
+ EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);
+
+ if (icount->count > icount->size) {
+ fprintf(out, "%s: count > size\n", bad);
+ return EXT2_ET_INVALID_ARGUMENT;
+ }
+ for (i=1; i < icount->count; i++) {
+ if (icount->list[i-1].ino >= icount->list[i].ino) {
+ fprintf(out, "%s: list[%d].ino=%u, list[%d].ino=%u\n",
+ bad, i-1, icount->list[i-1].ino,
+ i, icount->list[i].ino);
+ ret = EXT2_ET_INVALID_ARGUMENT;
+ }
+ }
+ return ret;
+}
+
+errcode_t ext2fs_icount_fetch(ext2_icount_t icount, ext2_ino_t ino, __u16 *ret)
+{
+ struct ext2_icount_el *el;
+
+ EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);
+
+ if (!ino || (ino > icount->num_inodes))
+ return EXT2_ET_INVALID_ARGUMENT;
+
+ if (ext2fs_test_inode_bitmap(icount->single, ino)) {
+ *ret = 1;
+ return 0;
+ }
+ if (icount->multiple &&
+ !ext2fs_test_inode_bitmap(icount->multiple, ino)) {
+ *ret = 0;
+ return 0;
+ }
+ el = get_icount_el(icount, ino, 0);
+ if (!el) {
+ *ret = 0;
+ return 0;
+ }
+ *ret = el->count;
+ return 0;
+}
+
+errcode_t ext2fs_icount_increment(ext2_icount_t icount, ext2_ino_t ino,
+ __u16 *ret)
+{
+ struct ext2_icount_el *el;
+
+ EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);
+
+ if (!ino || (ino > icount->num_inodes))
+ return EXT2_ET_INVALID_ARGUMENT;
+
+ if (ext2fs_test_inode_bitmap(icount->single, ino)) {
+ /*
+ * If the existing count is 1, then we know there is
+ * no entry in the list.
+ */
+ el = get_icount_el(icount, ino, 1);
+ if (!el)
+ return EXT2_ET_NO_MEMORY;
+ ext2fs_unmark_inode_bitmap(icount->single, ino);
+ el->count = 2;
+ } else if (icount->multiple) {
+ /*
+ * The count is either zero or greater than 1; if the
+ * inode is set in icount->multiple, then there should
+ * be an entry in the list, so find it using
+ * get_icount_el().
+ */
+ if (ext2fs_test_inode_bitmap(icount->multiple, ino)) {
+ el = get_icount_el(icount, ino, 1);
+ if (!el)
+ return EXT2_ET_NO_MEMORY;
+ el->count++;
+ } else {
+ /*
+ * The count was zero; mark the single bitmap
+ * and return.
+ */
+ zero_count:
+ ext2fs_mark_inode_bitmap(icount->single, ino);
+ if (ret)
+ *ret = 1;
+ return 0;
+ }
+ } else {
+ /*
+ * The count is either zero or greater than 1; try to
+ * find an entry in the list to determine which.
+ */
+ el = get_icount_el(icount, ino, 0);
+ if (!el) {
+ /* No entry means the count was zero */
+ goto zero_count;
+ }
+ el = get_icount_el(icount, ino, 1);
+ if (!el)
+ return EXT2_ET_NO_MEMORY;
+ el->count++;
+ }
+ if (icount->multiple)
+ ext2fs_mark_inode_bitmap(icount->multiple, ino);
+ if (ret)
+ *ret = el->count;
+ return 0;
+}
+
+errcode_t ext2fs_icount_decrement(ext2_icount_t icount, ext2_ino_t ino,
+ __u16 *ret)
+{
+ struct ext2_icount_el *el;
+
+ if (!ino || (ino > icount->num_inodes))
+ return EXT2_ET_INVALID_ARGUMENT;
+
+ EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);
+
+ if (ext2fs_test_inode_bitmap(icount->single, ino)) {
+ ext2fs_unmark_inode_bitmap(icount->single, ino);
+ if (icount->multiple)
+ ext2fs_unmark_inode_bitmap(icount->multiple, ino);
+ else {
+ el = get_icount_el(icount, ino, 0);
+ if (el)
+ el->count = 0;
+ }
+ if (ret)
+ *ret = 0;
+ return 0;
+ }
+
+ if (icount->multiple &&
+ !ext2fs_test_inode_bitmap(icount->multiple, ino))
+ return EXT2_ET_INVALID_ARGUMENT;
+
+ el = get_icount_el(icount, ino, 0);
+ if (!el || el->count == 0)
+ return EXT2_ET_INVALID_ARGUMENT;
+
+ el->count--;
+ if (el->count == 1)
+ ext2fs_mark_inode_bitmap(icount->single, ino);
+ if ((el->count == 0) && icount->multiple)
+ ext2fs_unmark_inode_bitmap(icount->multiple, ino);
+
+ if (ret)
+ *ret = el->count;
+ return 0;
+}
+
+errcode_t ext2fs_icount_store(ext2_icount_t icount, ext2_ino_t ino,
+ __u16 count)
+{
+ struct ext2_icount_el *el;
+
+ if (!ino || (ino > icount->num_inodes))
+ return EXT2_ET_INVALID_ARGUMENT;
+
+ EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);
+
+ if (count == 1) {
+ ext2fs_mark_inode_bitmap(icount->single, ino);
+ if (icount->multiple)
+ ext2fs_unmark_inode_bitmap(icount->multiple, ino);
+ return 0;
+ }
+ if (count == 0) {
+ ext2fs_unmark_inode_bitmap(icount->single, ino);
+ if (icount->multiple) {
+ /*
+ * If the icount->multiple bitmap is enabled,
+ * we can just clear both bitmaps and we're done
+ */
+ ext2fs_unmark_inode_bitmap(icount->multiple, ino);
+ } else {
+ el = get_icount_el(icount, ino, 0);
+ if (el)
+ el->count = 0;
+ }
+ return 0;
+ }
+
+ /*
+ * Get the icount element
+ */
+ el = get_icount_el(icount, ino, 1);
+ if (!el)
+ return EXT2_ET_NO_MEMORY;
+ el->count = count;
+ ext2fs_unmark_inode_bitmap(icount->single, ino);
+ if (icount->multiple)
+ ext2fs_mark_inode_bitmap(icount->multiple, ino);
+ return 0;
+}
+
+ext2_ino_t ext2fs_get_icount_size(ext2_icount_t icount)
+{
+ if (!icount || icount->magic != EXT2_ET_MAGIC_ICOUNT)
+ return 0;
+
+ return icount->size;
+}
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/imager.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/imager.c
new file mode 100644
index 0000000..e82321e
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/imager.c
@@ -0,0 +1,377 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * image.c --- writes out the critical parts of the filesystem as a
+ * flat file.
+ *
+ * Copyright (C) 2000 Theodore Ts'o.
+ *
+ * Note: this uses the POSIX IO interfaces, unlike most of the other
+ * functions in this library. So sue me.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+#ifndef HAVE_TYPE_SSIZE_T
+typedef int ssize_t;
+#endif
+
+/*
+ * This function returns 1 if the specified block is all zeros
+ */
+static int check_zero_block(char *buf, int blocksize)
+{
+ char *cp = buf;
+ int left = blocksize;
+
+ while (left > 0) {
+ if (*cp++)
+ return 0;
+ left--;
+ }
+ return 1;
+}
+
+/*
+ * Write the inode table out as a single block.
+ */
+#define BUF_BLOCKS 32
+
+errcode_t ext2fs_image_inode_write(ext2_filsys fs, int fd, int flags)
+{
+ unsigned int group, left, c, d;
+ char *buf, *cp;
+ blk_t blk;
+ ssize_t actual;
+ errcode_t retval;
+
+ buf = xmalloc(fs->blocksize * BUF_BLOCKS);
+
+ for (group = 0; group < fs->group_desc_count; group++) {
+ blk = fs->group_desc[(unsigned)group].bg_inode_table;
+ if (!blk)
+ return EXT2_ET_MISSING_INODE_TABLE;
+ left = fs->inode_blocks_per_group;
+ while (left) {
+ c = BUF_BLOCKS;
+ if (c > left)
+ c = left;
+ retval = io_channel_read_blk(fs->io, blk, c, buf);
+ if (retval)
+ goto errout;
+ cp = buf;
+ while (c) {
+ if (!(flags & IMAGER_FLAG_SPARSEWRITE)) {
+ d = c;
+ goto skip_sparse;
+ }
+ /* Skip zero blocks */
+ if (check_zero_block(cp, fs->blocksize)) {
+ c--;
+ blk++;
+ left--;
+ cp += fs->blocksize;
+ lseek(fd, fs->blocksize, SEEK_CUR);
+ continue;
+ }
+ /* Find non-zero blocks */
+ for (d=1; d < c; d++) {
+ if (check_zero_block(cp + d*fs->blocksize, fs->blocksize))
+ break;
+ }
+ skip_sparse:
+ actual = write(fd, cp, fs->blocksize * d);
+ if (actual == -1) {
+ retval = errno;
+ goto errout;
+ }
+ if (actual != (ssize_t) (fs->blocksize * d)) {
+ retval = EXT2_ET_SHORT_WRITE;
+ goto errout;
+ }
+ blk += d;
+ left -= d;
+ cp += fs->blocksize * d;
+ c -= d;
+ }
+ }
+ }
+ retval = 0;
+
+errout:
+ free(buf);
+ return retval;
+}
+
+/*
+ * Read in the inode table and stuff it into place
+ */
+errcode_t ext2fs_image_inode_read(ext2_filsys fs, int fd,
+ int flags EXT2FS_ATTR((unused)))
+{
+ unsigned int group, c, left;
+ char *buf;
+ blk_t blk;
+ ssize_t actual;
+ errcode_t retval;
+
+ buf = xmalloc(fs->blocksize * BUF_BLOCKS);
+
+ for (group = 0; group < fs->group_desc_count; group++) {
+ blk = fs->group_desc[(unsigned)group].bg_inode_table;
+ if (!blk) {
+ retval = EXT2_ET_MISSING_INODE_TABLE;
+ goto errout;
+ }
+ left = fs->inode_blocks_per_group;
+ while (left) {
+ c = BUF_BLOCKS;
+ if (c > left)
+ c = left;
+ actual = read(fd, buf, fs->blocksize * c);
+ if (actual == -1) {
+ retval = errno;
+ goto errout;
+ }
+ if (actual != (ssize_t) (fs->blocksize * c)) {
+ retval = EXT2_ET_SHORT_READ;
+ goto errout;
+ }
+ retval = io_channel_write_blk(fs->io, blk, c, buf);
+ if (retval)
+ goto errout;
+
+ blk += c;
+ left -= c;
+ }
+ }
+ retval = ext2fs_flush_icache(fs);
+
+errout:
+ free(buf);
+ return retval;
+}
+
+/*
+ * Write out superblock and group descriptors
+ */
+errcode_t ext2fs_image_super_write(ext2_filsys fs, int fd,
+ int flags EXT2FS_ATTR((unused)))
+{
+ char *buf, *cp;
+ ssize_t actual;
+ errcode_t retval;
+
+ buf = xmalloc(fs->blocksize);
+
+ /*
+ * Write out the superblock
+ */
+ memset(buf, 0, fs->blocksize);
+ memcpy(buf, fs->super, SUPERBLOCK_SIZE);
+ actual = write(fd, buf, fs->blocksize);
+ if (actual == -1) {
+ retval = errno;
+ goto errout;
+ }
+ if (actual != (ssize_t) fs->blocksize) {
+ retval = EXT2_ET_SHORT_WRITE;
+ goto errout;
+ }
+
+ /*
+ * Now write out the block group descriptors
+ */
+ cp = (char *) fs->group_desc;
+ actual = write(fd, cp, fs->blocksize * fs->desc_blocks);
+ if (actual == -1) {
+ retval = errno;
+ goto errout;
+ }
+ if (actual != (ssize_t) (fs->blocksize * fs->desc_blocks)) {
+ retval = EXT2_ET_SHORT_WRITE;
+ goto errout;
+ }
+
+ retval = 0;
+
+errout:
+ free(buf);
+ return retval;
+}
+
+/*
+ * Read the superblock and group descriptors and overwrite them.
+ */
+errcode_t ext2fs_image_super_read(ext2_filsys fs, int fd,
+ int flags EXT2FS_ATTR((unused)))
+{
+ char *buf;
+ ssize_t actual, size;
+ errcode_t retval;
+
+ size = fs->blocksize * (fs->group_desc_count + 1);
+ buf = xmalloc(size);
+
+ /*
+ * Read it all in.
+ */
+ actual = read(fd, buf, size);
+ if (actual == -1) {
+ retval = errno;
+ goto errout;
+ }
+ if (actual != size) {
+ retval = EXT2_ET_SHORT_READ;
+ goto errout;
+ }
+
+ /*
+ * Now copy in the superblock and group descriptors
+ */
+ memcpy(fs->super, buf, SUPERBLOCK_SIZE);
+
+ memcpy(fs->group_desc, buf + fs->blocksize,
+ fs->blocksize * fs->group_desc_count);
+
+ retval = 0;
+
+errout:
+ free(buf);
+ return retval;
+}
+
+/*
+ * Write the block/inode bitmaps.
+ */
+errcode_t ext2fs_image_bitmap_write(ext2_filsys fs, int fd, int flags)
+{
+ char *ptr;
+ int c, size;
+ char zero_buf[1024];
+ ssize_t actual;
+ errcode_t retval;
+
+ if (flags & IMAGER_FLAG_INODEMAP) {
+ if (!fs->inode_map) {
+ retval = ext2fs_read_inode_bitmap(fs);
+ if (retval)
+ return retval;
+ }
+ ptr = fs->inode_map->bitmap;
+ size = (EXT2_INODES_PER_GROUP(fs->super) / 8);
+ } else {
+ if (!fs->block_map) {
+ retval = ext2fs_read_block_bitmap(fs);
+ if (retval)
+ return retval;
+ }
+ ptr = fs->block_map->bitmap;
+ size = EXT2_BLOCKS_PER_GROUP(fs->super) / 8;
+ }
+ size = size * fs->group_desc_count;
+
+ actual = write(fd, ptr, size);
+ if (actual == -1) {
+ retval = errno;
+ goto errout;
+ }
+ if (actual != size) {
+ retval = EXT2_ET_SHORT_WRITE;
+ goto errout;
+ }
+ size = size % fs->blocksize;
+ memset(zero_buf, 0, sizeof(zero_buf));
+ if (size) {
+ size = fs->blocksize - size;
+ while (size) {
+ c = size;
+ if (c > (int) sizeof(zero_buf))
+ c = sizeof(zero_buf);
+ actual = write(fd, zero_buf, c);
+ if (actual == -1) {
+ retval = errno;
+ goto errout;
+ }
+ if (actual != c) {
+ retval = EXT2_ET_SHORT_WRITE;
+ goto errout;
+ }
+ size -= c;
+ }
+ }
+ retval = 0;
+errout:
+ return retval;
+}
+
+
+/*
+ * Read the block/inode bitmaps.
+ */
+errcode_t ext2fs_image_bitmap_read(ext2_filsys fs, int fd, int flags)
+{
+ char *ptr, *buf = 0;
+ int size;
+ ssize_t actual;
+ errcode_t retval;
+
+ if (flags & IMAGER_FLAG_INODEMAP) {
+ if (!fs->inode_map) {
+ retval = ext2fs_read_inode_bitmap(fs);
+ if (retval)
+ return retval;
+ }
+ ptr = fs->inode_map->bitmap;
+ size = (EXT2_INODES_PER_GROUP(fs->super) / 8);
+ } else {
+ if (!fs->block_map) {
+ retval = ext2fs_read_block_bitmap(fs);
+ if (retval)
+ return retval;
+ }
+ ptr = fs->block_map->bitmap;
+ size = EXT2_BLOCKS_PER_GROUP(fs->super) / 8;
+ }
+ size = size * fs->group_desc_count;
+
+ buf = xmalloc(size);
+
+ actual = read(fd, buf, size);
+ if (actual == -1) {
+ retval = errno;
+ goto errout;
+ }
+ if (actual != size) {
+ retval = EXT2_ET_SHORT_WRITE;
+ goto errout;
+ }
+ memcpy(ptr, buf, size);
+
+ retval = 0;
+errout:
+ free(buf);
+ return retval;
+}
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/ind_block.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/ind_block.c
new file mode 100644
index 0000000..c86a1c5
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/ind_block.c
@@ -0,0 +1,71 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ind_block.c --- indirect block I/O routines
+ *
+ * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
+ * 2001, 2002, 2003, 2004, 2005 by Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+errcode_t ext2fs_read_ind_block(ext2_filsys fs, blk_t blk, void *buf)
+{
+ errcode_t retval;
+#if BB_BIG_ENDIAN
+ blk_t *block_nr;
+ int i;
+ int limit = fs->blocksize >> 2;
+#endif
+
+ if ((fs->flags & EXT2_FLAG_IMAGE_FILE) &&
+ (fs->io != fs->image_io))
+ memset(buf, 0, fs->blocksize);
+ else {
+ retval = io_channel_read_blk(fs->io, blk, 1, buf);
+ if (retval)
+ return retval;
+ }
+#if BB_BIG_ENDIAN
+ if (fs->flags & (EXT2_FLAG_SWAP_BYTES | EXT2_FLAG_SWAP_BYTES_READ)) {
+ block_nr = (blk_t *) buf;
+ for (i = 0; i < limit; i++, block_nr++)
+ *block_nr = ext2fs_swab32(*block_nr);
+ }
+#endif
+ return 0;
+}
+
+errcode_t ext2fs_write_ind_block(ext2_filsys fs, blk_t blk, void *buf)
+{
+#if BB_BIG_ENDIAN
+ blk_t *block_nr;
+ int i;
+ int limit = fs->blocksize >> 2;
+#endif
+
+ if (fs->flags & EXT2_FLAG_IMAGE_FILE)
+ return 0;
+
+#if BB_BIG_ENDIAN
+ if (fs->flags & (EXT2_FLAG_SWAP_BYTES | EXT2_FLAG_SWAP_BYTES_WRITE)) {
+ block_nr = (blk_t *) buf;
+ for (i = 0; i < limit; i++, block_nr++)
+ *block_nr = ext2fs_swab32(*block_nr);
+ }
+#endif
+ return io_channel_write_blk(fs->io, blk, 1, buf);
+}
+
+
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/initialize.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/initialize.c
new file mode 100644
index 0000000..ef1d343
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/initialize.c
@@ -0,0 +1,388 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * initialize.c --- initialize a filesystem handle given superblock
+ * parameters. Used by mke2fs when initializing a filesystem.
+ *
+ * Copyright (C) 1994, 1995, 1996 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+#if defined(__linux__) && defined(EXT2_OS_LINUX)
+#define CREATOR_OS EXT2_OS_LINUX
+#else
+#if defined(__GNU__) && defined(EXT2_OS_HURD)
+#define CREATOR_OS EXT2_OS_HURD
+#else
+#if defined(__FreeBSD__) && defined(EXT2_OS_FREEBSD)
+#define CREATOR_OS EXT2_OS_FREEBSD
+#else
+#if defined(LITES) && defined(EXT2_OS_LITES)
+#define CREATOR_OS EXT2_OS_LITES
+#else
+#define CREATOR_OS EXT2_OS_LINUX /* by default */
+#endif /* defined(LITES) && defined(EXT2_OS_LITES) */
+#endif /* defined(__FreeBSD__) && defined(EXT2_OS_FREEBSD) */
+#endif /* defined(__GNU__) && defined(EXT2_OS_HURD) */
+#endif /* defined(__linux__) && defined(EXT2_OS_LINUX) */
+
+/*
+ * Note we override the kernel include file's idea of what the default
+ * check interval (never) should be. It's a good idea to check at
+ * least *occasionally*, specially since servers will never rarely get
+ * to reboot, since Linux is so robust these days. :-)
+ *
+ * 180 days (six months) seems like a good value.
+ */
+#ifdef EXT2_DFL_CHECKINTERVAL
+#undef EXT2_DFL_CHECKINTERVAL
+#endif
+#define EXT2_DFL_CHECKINTERVAL (86400L * 180L)
+
+/*
+ * Calculate the number of GDT blocks to reserve for online filesystem growth.
+ * The absolute maximum number of GDT blocks we can reserve is determined by
+ * the number of block pointers that can fit into a single block.
+ */
+static int calc_reserved_gdt_blocks(ext2_filsys fs)
+{
+ struct ext2_super_block *sb = fs->super;
+ unsigned long bpg = sb->s_blocks_per_group;
+ unsigned int gdpb = fs->blocksize / sizeof(struct ext2_group_desc);
+ unsigned long max_blocks = 0xffffffff;
+ unsigned long rsv_groups;
+ int rsv_gdb;
+
+ /* We set it at 1024x the current filesystem size, or
+ * the upper block count limit (2^32), whichever is lower.
+ */
+ if (sb->s_blocks_count < max_blocks / 1024)
+ max_blocks = sb->s_blocks_count * 1024;
+ rsv_groups = (max_blocks - sb->s_first_data_block + bpg - 1) / bpg;
+ rsv_gdb = (rsv_groups + gdpb - 1) / gdpb - fs->desc_blocks;
+ if (rsv_gdb > EXT2_ADDR_PER_BLOCK(sb))
+ rsv_gdb = EXT2_ADDR_PER_BLOCK(sb);
+#ifdef RES_GDT_DEBUG
+ printf("max_blocks %lu, rsv_groups = %lu, rsv_gdb = %lu\n",
+ max_blocks, rsv_groups, rsv_gdb);
+#endif
+
+ return rsv_gdb;
+}
+
+errcode_t ext2fs_initialize(const char *name, int flags,
+ struct ext2_super_block *param,
+ io_manager manager, ext2_filsys *ret_fs)
+{
+ ext2_filsys fs;
+ errcode_t retval;
+ struct ext2_super_block *super;
+ int frags_per_block;
+ unsigned int rem;
+ unsigned int overhead = 0;
+ blk_t group_block;
+ unsigned int ipg;
+ dgrp_t i;
+ blk_t numblocks;
+ int rsv_gdt;
+ char *buf;
+
+ if (!param || !param->s_blocks_count)
+ return EXT2_ET_INVALID_ARGUMENT;
+
+ retval = ext2fs_get_mem(sizeof(struct struct_ext2_filsys), &fs);
+ if (retval)
+ return retval;
+
+ memset(fs, 0, sizeof(struct struct_ext2_filsys));
+ fs->magic = EXT2_ET_MAGIC_EXT2FS_FILSYS;
+ fs->flags = flags | EXT2_FLAG_RW;
+ fs->umask = 022;
+#ifdef WORDS_BIGENDIAN
+ fs->flags |= EXT2_FLAG_SWAP_BYTES;
+#endif
+ retval = manager->open(name, IO_FLAG_RW, &fs->io);
+ if (retval)
+ goto cleanup;
+ fs->image_io = fs->io;
+ fs->io->app_data = fs;
+ retval = ext2fs_get_mem(strlen(name)+1, &fs->device_name);
+ if (retval)
+ goto cleanup;
+
+ strcpy(fs->device_name, name);
+ retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &super);
+ if (retval)
+ goto cleanup;
+ fs->super = super;
+
+ memset(super, 0, SUPERBLOCK_SIZE);
+
+#define set_field(field, default) (super->field = param->field ? \
+ param->field : (default))
+
+ super->s_magic = EXT2_SUPER_MAGIC;
+ super->s_state = EXT2_VALID_FS;
+
+ set_field(s_log_block_size, 0); /* default blocksize: 1024 bytes */
+ set_field(s_log_frag_size, 0); /* default fragsize: 1024 bytes */
+ set_field(s_first_data_block, super->s_log_block_size ? 0 : 1);
+ set_field(s_max_mnt_count, EXT2_DFL_MAX_MNT_COUNT);
+ set_field(s_errors, EXT2_ERRORS_DEFAULT);
+ set_field(s_feature_compat, 0);
+ set_field(s_feature_incompat, 0);
+ set_field(s_feature_ro_compat, 0);
+ set_field(s_first_meta_bg, 0);
+ if (super->s_feature_incompat & ~EXT2_LIB_FEATURE_INCOMPAT_SUPP) {
+ retval = EXT2_ET_UNSUPP_FEATURE;
+ goto cleanup;
+ }
+ if (super->s_feature_ro_compat & ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP) {
+ retval = EXT2_ET_RO_UNSUPP_FEATURE;
+ goto cleanup;
+ }
+
+ set_field(s_rev_level, EXT2_GOOD_OLD_REV);
+ if (super->s_rev_level >= EXT2_DYNAMIC_REV) {
+ set_field(s_first_ino, EXT2_GOOD_OLD_FIRST_INO);
+ set_field(s_inode_size, EXT2_GOOD_OLD_INODE_SIZE);
+ }
+
+ set_field(s_checkinterval, EXT2_DFL_CHECKINTERVAL);
+ super->s_mkfs_time = super->s_lastcheck = time(NULL);
+
+ super->s_creator_os = CREATOR_OS;
+
+ fs->blocksize = EXT2_BLOCK_SIZE(super);
+ fs->fragsize = EXT2_FRAG_SIZE(super);
+ frags_per_block = fs->blocksize / fs->fragsize;
+
+ /* default: (fs->blocksize*8) blocks/group, up to 2^16 (GDT limit) */
+ set_field(s_blocks_per_group, fs->blocksize * 8);
+ if (super->s_blocks_per_group > EXT2_MAX_BLOCKS_PER_GROUP(super))
+ super->s_blocks_per_group = EXT2_MAX_BLOCKS_PER_GROUP(super);
+ super->s_frags_per_group = super->s_blocks_per_group * frags_per_block;
+
+ super->s_blocks_count = param->s_blocks_count;
+ super->s_r_blocks_count = param->s_r_blocks_count;
+ if (super->s_r_blocks_count >= param->s_blocks_count) {
+ retval = EXT2_ET_INVALID_ARGUMENT;
+ goto cleanup;
+ }
+
+ /*
+ * If we're creating an external journal device, we don't need
+ * to bother with the rest.
+ */
+ if (super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) {
+ fs->group_desc_count = 0;
+ ext2fs_mark_super_dirty(fs);
+ *ret_fs = fs;
+ return 0;
+ }
+
+retry:
+ fs->group_desc_count = (super->s_blocks_count -
+ super->s_first_data_block +
+ EXT2_BLOCKS_PER_GROUP(super) - 1)
+ / EXT2_BLOCKS_PER_GROUP(super);
+ if (fs->group_desc_count == 0) {
+ retval = EXT2_ET_TOOSMALL;
+ goto cleanup;
+ }
+ fs->desc_blocks = (fs->group_desc_count +
+ EXT2_DESC_PER_BLOCK(super) - 1)
+ / EXT2_DESC_PER_BLOCK(super);
+
+ i = fs->blocksize >= 4096 ? 1 : 4096 / fs->blocksize;
+ set_field(s_inodes_count, super->s_blocks_count / i);
+
+ /*
+ * Make sure we have at least EXT2_FIRST_INO + 1 inodes, so
+ * that we have enough inodes for the filesystem(!)
+ */
+ if (super->s_inodes_count < EXT2_FIRST_INODE(super)+1)
+ super->s_inodes_count = EXT2_FIRST_INODE(super)+1;
+
+ /*
+ * There should be at least as many inodes as the user
+ * requested. Figure out how many inodes per group that
+ * should be. But make sure that we don't allocate more than
+ * one bitmap's worth of inodes each group.
+ */
+ ipg = (super->s_inodes_count + fs->group_desc_count - 1) /
+ fs->group_desc_count;
+ if (ipg > fs->blocksize * 8) {
+ if (super->s_blocks_per_group >= 256) {
+ /* Try again with slightly different parameters */
+ super->s_blocks_per_group -= 8;
+ super->s_blocks_count = param->s_blocks_count;
+ super->s_frags_per_group = super->s_blocks_per_group *
+ frags_per_block;
+ goto retry;
+ } else
+ return EXT2_ET_TOO_MANY_INODES;
+ }
+
+ if (ipg > (unsigned) EXT2_MAX_INODES_PER_GROUP(super))
+ ipg = EXT2_MAX_INODES_PER_GROUP(super);
+
+ super->s_inodes_per_group = ipg;
+ if (super->s_inodes_count > ipg * fs->group_desc_count)
+ super->s_inodes_count = ipg * fs->group_desc_count;
+
+ /*
+ * Make sure the number of inodes per group completely fills
+ * the inode table blocks in the descriptor. If not, add some
+ * additional inodes/group. Waste not, want not...
+ */
+ fs->inode_blocks_per_group = (((super->s_inodes_per_group *
+ EXT2_INODE_SIZE(super)) +
+ EXT2_BLOCK_SIZE(super) - 1) /
+ EXT2_BLOCK_SIZE(super));
+ super->s_inodes_per_group = ((fs->inode_blocks_per_group *
+ EXT2_BLOCK_SIZE(super)) /
+ EXT2_INODE_SIZE(super));
+ /*
+ * Finally, make sure the number of inodes per group is a
+ * multiple of 8. This is needed to simplify the bitmap
+ * splicing code.
+ */
+ super->s_inodes_per_group &= ~7;
+ fs->inode_blocks_per_group = (((super->s_inodes_per_group *
+ EXT2_INODE_SIZE(super)) +
+ EXT2_BLOCK_SIZE(super) - 1) /
+ EXT2_BLOCK_SIZE(super));
+
+ /*
+ * adjust inode count to reflect the adjusted inodes_per_group
+ */
+ super->s_inodes_count = super->s_inodes_per_group *
+ fs->group_desc_count;
+ super->s_free_inodes_count = super->s_inodes_count;
+
+ /*
+ * check the number of reserved group descriptor table blocks
+ */
+ if (super->s_feature_compat & EXT2_FEATURE_COMPAT_RESIZE_INODE)
+ rsv_gdt = calc_reserved_gdt_blocks(fs);
+ else
+ rsv_gdt = 0;
+ set_field(s_reserved_gdt_blocks, rsv_gdt);
+ if (super->s_reserved_gdt_blocks > EXT2_ADDR_PER_BLOCK(super)) {
+ retval = EXT2_ET_RES_GDT_BLOCKS;
+ goto cleanup;
+ }
+
+ /*
+ * Overhead is the number of bookkeeping blocks per group. It
+ * includes the superblock backup, the group descriptor
+ * backups, the inode bitmap, the block bitmap, and the inode
+ * table.
+ */
+
+ overhead = (int) (2 + fs->inode_blocks_per_group);
+
+ if (ext2fs_bg_has_super(fs, fs->group_desc_count - 1))
+ overhead += 1 + fs->desc_blocks + super->s_reserved_gdt_blocks;
+
+ /* This can only happen if the user requested too many inodes */
+ if (overhead > super->s_blocks_per_group)
+ return EXT2_ET_TOO_MANY_INODES;
+
+ /*
+ * See if the last group is big enough to support the
+ * necessary data structures. If not, we need to get rid of
+ * it.
+ */
+ rem = ((super->s_blocks_count - super->s_first_data_block) %
+ super->s_blocks_per_group);
+ if ((fs->group_desc_count == 1) && rem && (rem < overhead))
+ return EXT2_ET_TOOSMALL;
+ if (rem && (rem < overhead+50)) {
+ super->s_blocks_count -= rem;
+ goto retry;
+ }
+
+ /*
+ * At this point we know how big the filesystem will be. So
+ * we can do any and all allocations that depend on the block
+ * count.
+ */
+
+ retval = ext2fs_get_mem(strlen(fs->device_name) + 80, &buf);
+ if (retval)
+ goto cleanup;
+
+ sprintf(buf, "block bitmap for %s", fs->device_name);
+ retval = ext2fs_allocate_block_bitmap(fs, buf, &fs->block_map);
+ if (retval)
+ goto cleanup;
+
+ sprintf(buf, "inode bitmap for %s", fs->device_name);
+ retval = ext2fs_allocate_inode_bitmap(fs, buf, &fs->inode_map);
+ if (retval)
+ goto cleanup;
+
+ ext2fs_free_mem(&buf);
+
+ retval = ext2fs_get_mem((size_t) fs->desc_blocks * fs->blocksize,
+ &fs->group_desc);
+ if (retval)
+ goto cleanup;
+
+ memset(fs->group_desc, 0, (size_t) fs->desc_blocks * fs->blocksize);
+
+ /*
+ * Reserve the superblock and group descriptors for each
+ * group, and fill in the correct group statistics for group.
+ * Note that although the block bitmap, inode bitmap, and
+ * inode table have not been allocated (and in fact won't be
+ * by this routine), they are accounted for nevertheless.
+ */
+ group_block = super->s_first_data_block;
+ super->s_free_blocks_count = 0;
+ for (i = 0; i < fs->group_desc_count; i++) {
+ numblocks = ext2fs_reserve_super_and_bgd(fs, i, fs->block_map);
+
+ super->s_free_blocks_count += numblocks;
+ fs->group_desc[i].bg_free_blocks_count = numblocks;
+ fs->group_desc[i].bg_free_inodes_count =
+ fs->super->s_inodes_per_group;
+ fs->group_desc[i].bg_used_dirs_count = 0;
+
+ group_block += super->s_blocks_per_group;
+ }
+
+ ext2fs_mark_super_dirty(fs);
+ ext2fs_mark_bb_dirty(fs);
+ ext2fs_mark_ib_dirty(fs);
+
+ io_channel_set_blksize(fs->io, fs->blocksize);
+
+ *ret_fs = fs;
+ return 0;
+cleanup:
+ ext2fs_free(fs);
+ return retval;
+}
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/inline.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/inline.c
new file mode 100644
index 0000000..9b620a7
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/inline.c
@@ -0,0 +1,33 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * inline.c --- Includes the inlined functions defined in the header
+ * files as standalone functions, in case the application program
+ * is compiled with inlining turned off.
+ *
+ * Copyright (C) 1993, 1994 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#define INCLUDE_INLINE_FUNCS
+#include "ext2fs.h"
+
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/inode.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/inode.c
new file mode 100644
index 0000000..5e0d081
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/inode.c
@@ -0,0 +1,767 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * inode.c --- utility routines to read and write inodes
+ *
+ * Copyright (C) 1993, 1994, 1995, 1996, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fsP.h"
+#include "e2image.h"
+
+struct ext2_struct_inode_scan {
+ errcode_t magic;
+ ext2_filsys fs;
+ ext2_ino_t current_inode;
+ blk_t current_block;
+ dgrp_t current_group;
+ ext2_ino_t inodes_left;
+ blk_t blocks_left;
+ dgrp_t groups_left;
+ blk_t inode_buffer_blocks;
+ char * inode_buffer;
+ int inode_size;
+ char * ptr;
+ int bytes_left;
+ char *temp_buffer;
+ errcode_t (*done_group)(ext2_filsys fs,
+ dgrp_t group,
+ void * priv_data);
+ void * done_group_data;
+ int bad_block_ptr;
+ int scan_flags;
+ int reserved[6];
+};
+
+/*
+ * This routine flushes the icache, if it exists.
+ */
+errcode_t ext2fs_flush_icache(ext2_filsys fs)
+{
+ int i;
+
+ if (!fs->icache)
+ return 0;
+
+ for (i=0; i < fs->icache->cache_size; i++)
+ fs->icache->cache[i].ino = 0;
+
+ fs->icache->buffer_blk = 0;
+ return 0;
+}
+
+static errcode_t create_icache(ext2_filsys fs)
+{
+ errcode_t retval;
+
+ if (fs->icache)
+ return 0;
+ retval = ext2fs_get_mem(sizeof(struct ext2_inode_cache), &fs->icache);
+ if (retval)
+ return retval;
+
+ memset(fs->icache, 0, sizeof(struct ext2_inode_cache));
+ retval = ext2fs_get_mem(fs->blocksize, &fs->icache->buffer);
+ if (retval) {
+ ext2fs_free_mem(&fs->icache);
+ return retval;
+ }
+ fs->icache->buffer_blk = 0;
+ fs->icache->cache_last = -1;
+ fs->icache->cache_size = 4;
+ fs->icache->refcount = 1;
+ retval = ext2fs_get_mem(sizeof(struct ext2_inode_cache_ent)
+ * fs->icache->cache_size,
+ &fs->icache->cache);
+ if (retval) {
+ ext2fs_free_mem(&fs->icache->buffer);
+ ext2fs_free_mem(&fs->icache);
+ return retval;
+ }
+ ext2fs_flush_icache(fs);
+ return 0;
+}
+
+errcode_t ext2fs_open_inode_scan(ext2_filsys fs, int buffer_blocks,
+ ext2_inode_scan *ret_scan)
+{
+ ext2_inode_scan scan;
+ errcode_t retval;
+ errcode_t (*save_get_blocks)(ext2_filsys f, ext2_ino_t ino, blk_t *blocks);
+
+ EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+ /*
+ * If fs->badblocks isn't set, then set it --- since the inode
+ * scanning functions require it.
+ */
+ if (fs->badblocks == 0) {
+ /*
+ * Temporarly save fs->get_blocks and set it to zero,
+ * for compatibility with old e2fsck's.
+ */
+ save_get_blocks = fs->get_blocks;
+ fs->get_blocks = 0;
+ retval = ext2fs_read_bb_inode(fs, &fs->badblocks);
+ if (retval) {
+ ext2fs_badblocks_list_free(fs->badblocks);
+ fs->badblocks = 0;
+ }
+ fs->get_blocks = save_get_blocks;
+ }
+
+ retval = ext2fs_get_mem(sizeof(struct ext2_struct_inode_scan), &scan);
+ if (retval)
+ return retval;
+ memset(scan, 0, sizeof(struct ext2_struct_inode_scan));
+
+ scan->magic = EXT2_ET_MAGIC_INODE_SCAN;
+ scan->fs = fs;
+ scan->inode_size = EXT2_INODE_SIZE(fs->super);
+ scan->bytes_left = 0;
+ scan->current_group = 0;
+ scan->groups_left = fs->group_desc_count - 1;
+ scan->inode_buffer_blocks = buffer_blocks ? buffer_blocks : 8;
+ scan->current_block = scan->fs->
+ group_desc[scan->current_group].bg_inode_table;
+ scan->inodes_left = EXT2_INODES_PER_GROUP(scan->fs->super);
+ scan->blocks_left = scan->fs->inode_blocks_per_group;
+ retval = ext2fs_get_mem((size_t) (scan->inode_buffer_blocks *
+ fs->blocksize),
+ &scan->inode_buffer);
+ scan->done_group = 0;
+ scan->done_group_data = 0;
+ scan->bad_block_ptr = 0;
+ if (retval) {
+ ext2fs_free_mem(&scan);
+ return retval;
+ }
+ retval = ext2fs_get_mem(scan->inode_size, &scan->temp_buffer);
+ if (retval) {
+ ext2fs_free_mem(&scan->inode_buffer);
+ ext2fs_free_mem(&scan);
+ return retval;
+ }
+ if (scan->fs->badblocks && scan->fs->badblocks->num)
+ scan->scan_flags |= EXT2_SF_CHK_BADBLOCKS;
+ *ret_scan = scan;
+ return 0;
+}
+
+void ext2fs_close_inode_scan(ext2_inode_scan scan)
+{
+ if (!scan || (scan->magic != EXT2_ET_MAGIC_INODE_SCAN))
+ return;
+
+ ext2fs_free_mem(&scan->inode_buffer);
+ scan->inode_buffer = NULL;
+ ext2fs_free_mem(&scan->temp_buffer);
+ scan->temp_buffer = NULL;
+ ext2fs_free_mem(&scan);
+}
+
+void ext2fs_set_inode_callback(ext2_inode_scan scan,
+ errcode_t (*done_group)(ext2_filsys fs,
+ dgrp_t group,
+ void * priv_data),
+ void *done_group_data)
+{
+ if (!scan || (scan->magic != EXT2_ET_MAGIC_INODE_SCAN))
+ return;
+
+ scan->done_group = done_group;
+ scan->done_group_data = done_group_data;
+}
+
+int ext2fs_inode_scan_flags(ext2_inode_scan scan, int set_flags,
+ int clear_flags)
+{
+ int old_flags;
+
+ if (!scan || (scan->magic != EXT2_ET_MAGIC_INODE_SCAN))
+ return 0;
+
+ old_flags = scan->scan_flags;
+ scan->scan_flags &= ~clear_flags;
+ scan->scan_flags |= set_flags;
+ return old_flags;
+}
+
+/*
+ * This function is called by ext2fs_get_next_inode when it needs to
+ * get ready to read in a new blockgroup.
+ */
+static errcode_t get_next_blockgroup(ext2_inode_scan scan)
+{
+ scan->current_group++;
+ scan->groups_left--;
+
+ scan->current_block = scan->fs->
+ group_desc[scan->current_group].bg_inode_table;
+
+ scan->current_inode = scan->current_group *
+ EXT2_INODES_PER_GROUP(scan->fs->super);
+
+ scan->bytes_left = 0;
+ scan->inodes_left = EXT2_INODES_PER_GROUP(scan->fs->super);
+ scan->blocks_left = scan->fs->inode_blocks_per_group;
+ return 0;
+}
+
+errcode_t ext2fs_inode_scan_goto_blockgroup(ext2_inode_scan scan,
+ int group)
+{
+ scan->current_group = group - 1;
+ scan->groups_left = scan->fs->group_desc_count - group;
+ return get_next_blockgroup(scan);
+}
+
+/*
+ * This function is called by get_next_blocks() to check for bad
+ * blocks in the inode table.
+ *
+ * This function assumes that badblocks_list->list is sorted in
+ * increasing order.
+ */
+static errcode_t check_for_inode_bad_blocks(ext2_inode_scan scan,
+ blk_t *num_blocks)
+{
+ blk_t blk = scan->current_block;
+ badblocks_list bb = scan->fs->badblocks;
+
+ /*
+ * If the inode table is missing, then obviously there are no
+ * bad blocks. :-)
+ */
+ if (blk == 0)
+ return 0;
+
+ /*
+ * If the current block is greater than the bad block listed
+ * in the bad block list, then advance the pointer until this
+ * is no longer the case. If we run out of bad blocks, then
+ * we don't need to do any more checking!
+ */
+ while (blk > bb->list[scan->bad_block_ptr]) {
+ if (++scan->bad_block_ptr >= bb->num) {
+ scan->scan_flags &= ~EXT2_SF_CHK_BADBLOCKS;
+ return 0;
+ }
+ }
+
+ /*
+ * If the current block is equal to the bad block listed in
+ * the bad block list, then handle that one block specially.
+ * (We could try to handle runs of bad blocks, but that
+ * only increases CPU efficiency by a small amount, at the
+ * expense of a huge expense of code complexity, and for an
+ * uncommon case at that.)
+ */
+ if (blk == bb->list[scan->bad_block_ptr]) {
+ scan->scan_flags |= EXT2_SF_BAD_INODE_BLK;
+ *num_blocks = 1;
+ if (++scan->bad_block_ptr >= bb->num)
+ scan->scan_flags &= ~EXT2_SF_CHK_BADBLOCKS;
+ return 0;
+ }
+
+ /*
+ * If there is a bad block in the range that we're about to
+ * read in, adjust the number of blocks to read so that we we
+ * don't read in the bad block. (Then the next block to read
+ * will be the bad block, which is handled in the above case.)
+ */
+ if ((blk + *num_blocks) > bb->list[scan->bad_block_ptr])
+ *num_blocks = (int) (bb->list[scan->bad_block_ptr] - blk);
+
+ return 0;
+}
+
+/*
+ * This function is called by ext2fs_get_next_inode when it needs to
+ * read in more blocks from the current blockgroup's inode table.
+ */
+static errcode_t get_next_blocks(ext2_inode_scan scan)
+{
+ blk_t num_blocks;
+ errcode_t retval;
+
+ /*
+ * Figure out how many blocks to read; we read at most
+ * inode_buffer_blocks, and perhaps less if there aren't that
+ * many blocks left to read.
+ */
+ num_blocks = scan->inode_buffer_blocks;
+ if (num_blocks > scan->blocks_left)
+ num_blocks = scan->blocks_left;
+
+ /*
+ * If the past block "read" was a bad block, then mark the
+ * left-over extra bytes as also being bad.
+ */
+ if (scan->scan_flags & EXT2_SF_BAD_INODE_BLK) {
+ if (scan->bytes_left)
+ scan->scan_flags |= EXT2_SF_BAD_EXTRA_BYTES;
+ scan->scan_flags &= ~EXT2_SF_BAD_INODE_BLK;
+ }
+
+ /*
+ * Do inode bad block processing, if necessary.
+ */
+ if (scan->scan_flags & EXT2_SF_CHK_BADBLOCKS) {
+ retval = check_for_inode_bad_blocks(scan, &num_blocks);
+ if (retval)
+ return retval;
+ }
+
+ if ((scan->scan_flags & EXT2_SF_BAD_INODE_BLK) ||
+ (scan->current_block == 0)) {
+ memset(scan->inode_buffer, 0,
+ (size_t) num_blocks * scan->fs->blocksize);
+ } else {
+ retval = io_channel_read_blk(scan->fs->io,
+ scan->current_block,
+ (int) num_blocks,
+ scan->inode_buffer);
+ if (retval)
+ return EXT2_ET_NEXT_INODE_READ;
+ }
+ scan->ptr = scan->inode_buffer;
+ scan->bytes_left = num_blocks * scan->fs->blocksize;
+
+ scan->blocks_left -= num_blocks;
+ if (scan->current_block)
+ scan->current_block += num_blocks;
+ return 0;
+}
+
+errcode_t ext2fs_get_next_inode_full(ext2_inode_scan scan, ext2_ino_t *ino,
+ struct ext2_inode *inode, int bufsize)
+{
+ errcode_t retval;
+ int extra_bytes = 0;
+
+ EXT2_CHECK_MAGIC(scan, EXT2_ET_MAGIC_INODE_SCAN);
+
+ /*
+ * Do we need to start reading a new block group?
+ */
+ if (scan->inodes_left <= 0) {
+ force_new_group:
+ if (scan->done_group) {
+ retval = (scan->done_group)
+ (scan->fs, scan->current_group,
+ scan->done_group_data);
+ if (retval)
+ return retval;
+ }
+ if (scan->groups_left <= 0) {
+ *ino = 0;
+ return 0;
+ }
+ retval = get_next_blockgroup(scan);
+ if (retval)
+ return retval;
+ }
+ /*
+ * This is done outside the above if statement so that the
+ * check can be done for block group #0.
+ */
+ if (scan->current_block == 0) {
+ if (scan->scan_flags & EXT2_SF_SKIP_MISSING_ITABLE) {
+ goto force_new_group;
+ } else
+ return EXT2_ET_MISSING_INODE_TABLE;
+ }
+
+
+ /*
+ * Have we run out of space in the inode buffer? If so, we
+ * need to read in more blocks.
+ */
+ if (scan->bytes_left < scan->inode_size) {
+ memcpy(scan->temp_buffer, scan->ptr, scan->bytes_left);
+ extra_bytes = scan->bytes_left;
+
+ retval = get_next_blocks(scan);
+ if (retval)
+ return retval;
+#if 0
+ /*
+ * XXX test Need check for used inode somehow.
+ * (Note: this is hard.)
+ */
+ if (is_empty_scan(scan))
+ goto force_new_group;
+#endif
+ }
+
+ retval = 0;
+ if (extra_bytes) {
+ memcpy(scan->temp_buffer+extra_bytes, scan->ptr,
+ scan->inode_size - extra_bytes);
+ scan->ptr += scan->inode_size - extra_bytes;
+ scan->bytes_left -= scan->inode_size - extra_bytes;
+
+#if BB_BIG_ENDIAN
+ if ((scan->fs->flags & EXT2_FLAG_SWAP_BYTES) ||
+ (scan->fs->flags & EXT2_FLAG_SWAP_BYTES_READ))
+ ext2fs_swap_inode_full(scan->fs,
+ (struct ext2_inode_large *) inode,
+ (struct ext2_inode_large *) scan->temp_buffer,
+ 0, bufsize);
+ else
+#endif
+ *inode = *((struct ext2_inode *) scan->temp_buffer);
+ if (scan->scan_flags & EXT2_SF_BAD_EXTRA_BYTES)
+ retval = EXT2_ET_BAD_BLOCK_IN_INODE_TABLE;
+ scan->scan_flags &= ~EXT2_SF_BAD_EXTRA_BYTES;
+ } else {
+#if BB_BIG_ENDIAN
+ if ((scan->fs->flags & EXT2_FLAG_SWAP_BYTES) ||
+ (scan->fs->flags & EXT2_FLAG_SWAP_BYTES_READ))
+ ext2fs_swap_inode_full(scan->fs,
+ (struct ext2_inode_large *) inode,
+ (struct ext2_inode_large *) scan->ptr,
+ 0, bufsize);
+ else
+#endif
+ memcpy(inode, scan->ptr, bufsize);
+ scan->ptr += scan->inode_size;
+ scan->bytes_left -= scan->inode_size;
+ if (scan->scan_flags & EXT2_SF_BAD_INODE_BLK)
+ retval = EXT2_ET_BAD_BLOCK_IN_INODE_TABLE;
+ }
+
+ scan->inodes_left--;
+ scan->current_inode++;
+ *ino = scan->current_inode;
+ return retval;
+}
+
+errcode_t ext2fs_get_next_inode(ext2_inode_scan scan, ext2_ino_t *ino,
+ struct ext2_inode *inode)
+{
+ return ext2fs_get_next_inode_full(scan, ino, inode,
+ sizeof(struct ext2_inode));
+}
+
+/*
+ * Functions to read and write a single inode.
+ */
+errcode_t ext2fs_read_inode_full(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode * inode, int bufsize)
+{
+ unsigned long group, block, block_nr, offset;
+ char *ptr;
+ errcode_t retval;
+ int clen, i, inodes_per_block, length;
+ io_channel io;
+
+ EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+ /* Check to see if user has an override function */
+ if (fs->read_inode) {
+ retval = (fs->read_inode)(fs, ino, inode);
+ if (retval != EXT2_ET_CALLBACK_NOTHANDLED)
+ return retval;
+ }
+ /* Create inode cache if not present */
+ if (!fs->icache) {
+ retval = create_icache(fs);
+ if (retval)
+ return retval;
+ }
+ /* Check to see if it's in the inode cache */
+ if (bufsize == sizeof(struct ext2_inode)) {
+ /* only old good inode can be retrieve from the cache */
+ for (i=0; i < fs->icache->cache_size; i++) {
+ if (fs->icache->cache[i].ino == ino) {
+ *inode = fs->icache->cache[i].inode;
+ return 0;
+ }
+ }
+ }
+ if ((ino == 0) || (ino > fs->super->s_inodes_count))
+ return EXT2_ET_BAD_INODE_NUM;
+ if (fs->flags & EXT2_FLAG_IMAGE_FILE) {
+ inodes_per_block = fs->blocksize / EXT2_INODE_SIZE(fs->super);
+ block_nr = fs->image_header->offset_inode / fs->blocksize;
+ block_nr += (ino - 1) / inodes_per_block;
+ offset = ((ino - 1) % inodes_per_block) *
+ EXT2_INODE_SIZE(fs->super);
+ io = fs->image_io;
+ } else {
+ group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super);
+ offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) *
+ EXT2_INODE_SIZE(fs->super);
+ block = offset >> EXT2_BLOCK_SIZE_BITS(fs->super);
+ if (!fs->group_desc[(unsigned)group].bg_inode_table)
+ return EXT2_ET_MISSING_INODE_TABLE;
+ block_nr = fs->group_desc[(unsigned)group].bg_inode_table +
+ block;
+ io = fs->io;
+ }
+ offset &= (EXT2_BLOCK_SIZE(fs->super) - 1);
+
+ length = EXT2_INODE_SIZE(fs->super);
+ if (bufsize < length)
+ length = bufsize;
+
+ ptr = (char *) inode;
+ while (length) {
+ clen = length;
+ if ((offset + length) > fs->blocksize)
+ clen = fs->blocksize - offset;
+
+ if (block_nr != fs->icache->buffer_blk) {
+ retval = io_channel_read_blk(io, block_nr, 1,
+ fs->icache->buffer);
+ if (retval)
+ return retval;
+ fs->icache->buffer_blk = block_nr;
+ }
+
+ memcpy(ptr, ((char *) fs->icache->buffer) + (unsigned) offset,
+ clen);
+
+ offset = 0;
+ length -= clen;
+ ptr += clen;
+ block_nr++;
+ }
+
+#if BB_BIG_ENDIAN
+ if ((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
+ (fs->flags & EXT2_FLAG_SWAP_BYTES_READ))
+ ext2fs_swap_inode_full(fs, (struct ext2_inode_large *) inode,
+ (struct ext2_inode_large *) inode,
+ 0, length);
+#endif
+
+ /* Update the inode cache */
+ fs->icache->cache_last = (fs->icache->cache_last + 1) %
+ fs->icache->cache_size;
+ fs->icache->cache[fs->icache->cache_last].ino = ino;
+ fs->icache->cache[fs->icache->cache_last].inode = *inode;
+
+ return 0;
+}
+
+errcode_t ext2fs_read_inode(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode * inode)
+{
+ return ext2fs_read_inode_full(fs, ino, inode,
+ sizeof(struct ext2_inode));
+}
+
+errcode_t ext2fs_write_inode_full(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode * inode, int bufsize)
+{
+ unsigned long group, block, block_nr, offset;
+ errcode_t retval = 0;
+ struct ext2_inode_large temp_inode, *w_inode;
+ char *ptr;
+ int clen, i, length;
+
+ EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+ /* Check to see if user provided an override function */
+ if (fs->write_inode) {
+ retval = (fs->write_inode)(fs, ino, inode);
+ if (retval != EXT2_ET_CALLBACK_NOTHANDLED)
+ return retval;
+ }
+
+ /* Check to see if the inode cache needs to be updated */
+ if (fs->icache) {
+ for (i=0; i < fs->icache->cache_size; i++) {
+ if (fs->icache->cache[i].ino == ino) {
+ fs->icache->cache[i].inode = *inode;
+ break;
+ }
+ }
+ } else {
+ retval = create_icache(fs);
+ if (retval)
+ return retval;
+ }
+
+ if (!(fs->flags & EXT2_FLAG_RW))
+ return EXT2_ET_RO_FILSYS;
+
+ if ((ino == 0) || (ino > fs->super->s_inodes_count))
+ return EXT2_ET_BAD_INODE_NUM;
+
+ length = bufsize;
+ if (length < EXT2_INODE_SIZE(fs->super))
+ length = EXT2_INODE_SIZE(fs->super);
+
+ if (length > (int) sizeof(struct ext2_inode_large)) {
+ w_inode = xmalloc(length);
+ } else
+ w_inode = &temp_inode;
+ memset(w_inode, 0, length);
+
+#if BB_BIG_ENDIAN
+ if ((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
+ (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE))
+ ext2fs_swap_inode_full(fs, w_inode,
+ (struct ext2_inode_large *) inode,
+ 1, bufsize);
+ else
+#endif
+ memcpy(w_inode, inode, bufsize);
+
+ group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super);
+ offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) *
+ EXT2_INODE_SIZE(fs->super);
+ block = offset >> EXT2_BLOCK_SIZE_BITS(fs->super);
+ if (!fs->group_desc[(unsigned) group].bg_inode_table)
+ return EXT2_ET_MISSING_INODE_TABLE;
+ block_nr = fs->group_desc[(unsigned) group].bg_inode_table + block;
+
+ offset &= (EXT2_BLOCK_SIZE(fs->super) - 1);
+
+ length = EXT2_INODE_SIZE(fs->super);
+ if (length > bufsize)
+ length = bufsize;
+
+ ptr = (char *) w_inode;
+
+ while (length) {
+ clen = length;
+ if ((offset + length) > fs->blocksize)
+ clen = fs->blocksize - offset;
+
+ if (fs->icache->buffer_blk != block_nr) {
+ retval = io_channel_read_blk(fs->io, block_nr, 1,
+ fs->icache->buffer);
+ if (retval)
+ goto errout;
+ fs->icache->buffer_blk = block_nr;
+ }
+
+
+ memcpy((char *) fs->icache->buffer + (unsigned) offset,
+ ptr, clen);
+
+ retval = io_channel_write_blk(fs->io, block_nr, 1,
+ fs->icache->buffer);
+ if (retval)
+ goto errout;
+
+ offset = 0;
+ ptr += clen;
+ length -= clen;
+ block_nr++;
+ }
+
+ fs->flags |= EXT2_FLAG_CHANGED;
+errout:
+ if (w_inode && w_inode != &temp_inode)
+ free(w_inode);
+ return retval;
+}
+
+errcode_t ext2fs_write_inode(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode *inode)
+{
+ return ext2fs_write_inode_full(fs, ino, inode,
+ sizeof(struct ext2_inode));
+}
+
+/*
+ * This function should be called when writing a new inode. It makes
+ * sure that extra part of large inodes is initialized properly.
+ */
+errcode_t ext2fs_write_new_inode(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode *inode)
+{
+ struct ext2_inode *buf;
+ int size = EXT2_INODE_SIZE(fs->super);
+ struct ext2_inode_large *large_inode;
+
+ if (size == sizeof(struct ext2_inode))
+ return ext2fs_write_inode_full(fs, ino, inode,
+ sizeof(struct ext2_inode));
+
+ buf = xmalloc(size);
+
+ memset(buf, 0, size);
+ *buf = *inode;
+
+ large_inode = (struct ext2_inode_large *) buf;
+ large_inode->i_extra_isize = sizeof(struct ext2_inode_large) -
+ EXT2_GOOD_OLD_INODE_SIZE;
+
+ return ext2fs_write_inode_full(fs, ino, buf, size);
+}
+
+
+errcode_t ext2fs_get_blocks(ext2_filsys fs, ext2_ino_t ino, blk_t *blocks)
+{
+ struct ext2_inode inode;
+ int i;
+ errcode_t retval;
+
+ EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+ if (ino > fs->super->s_inodes_count)
+ return EXT2_ET_BAD_INODE_NUM;
+
+ if (fs->get_blocks) {
+ if (!(*fs->get_blocks)(fs, ino, blocks))
+ return 0;
+ }
+ retval = ext2fs_read_inode(fs, ino, &inode);
+ if (retval)
+ return retval;
+ for (i=0; i < EXT2_N_BLOCKS; i++)
+ blocks[i] = inode.i_block[i];
+ return 0;
+}
+
+errcode_t ext2fs_check_directory(ext2_filsys fs, ext2_ino_t ino)
+{
+ struct ext2_inode inode;
+ errcode_t retval;
+
+ EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+ if (ino > fs->super->s_inodes_count)
+ return EXT2_ET_BAD_INODE_NUM;
+
+ if (fs->check_directory) {
+ retval = (fs->check_directory)(fs, ino);
+ if (retval != EXT2_ET_CALLBACK_NOTHANDLED)
+ return retval;
+ }
+ retval = ext2fs_read_inode(fs, ino, &inode);
+ if (retval)
+ return retval;
+ if (!LINUX_S_ISDIR(inode.i_mode))
+ return EXT2_ET_NO_DIRECTORY;
+ return 0;
+}
+
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/inode_io.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/inode_io.c
new file mode 100644
index 0000000..4bfa93a
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/inode_io.c
@@ -0,0 +1,271 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * inode_io.c --- This is allows an inode in an ext2 filesystem image
+ * to be accessed via the I/O manager interface.
+ *
+ * Copyright (C) 2002 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <time.h>
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+/*
+ * For checking structure magic numbers...
+ */
+
+#define EXT2_CHECK_MAGIC(struct, code) \
+ if ((struct)->magic != (code)) return (code)
+
+struct inode_private_data {
+ int magic;
+ char name[32];
+ ext2_file_t file;
+ ext2_filsys fs;
+ ext2_ino_t ino;
+ struct ext2_inode inode;
+ int flags;
+ struct inode_private_data *next;
+};
+
+#define CHANNEL_HAS_INODE 0x8000
+
+static struct inode_private_data *top_intern;
+static int ino_unique = 0;
+
+static errcode_t inode_open(const char *name, int flags, io_channel *channel);
+static errcode_t inode_close(io_channel channel);
+static errcode_t inode_set_blksize(io_channel channel, int blksize);
+static errcode_t inode_read_blk(io_channel channel, unsigned long block,
+ int count, void *data);
+static errcode_t inode_write_blk(io_channel channel, unsigned long block,
+ int count, const void *data);
+static errcode_t inode_flush(io_channel channel);
+static errcode_t inode_write_byte(io_channel channel, unsigned long offset,
+ int size, const void *data);
+
+static struct struct_io_manager struct_inode_manager = {
+ EXT2_ET_MAGIC_IO_MANAGER,
+ "Inode I/O Manager",
+ inode_open,
+ inode_close,
+ inode_set_blksize,
+ inode_read_blk,
+ inode_write_blk,
+ inode_flush,
+ inode_write_byte
+};
+
+io_manager inode_io_manager = &struct_inode_manager;
+
+errcode_t ext2fs_inode_io_intern2(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode *inode,
+ char **name)
+{
+ struct inode_private_data *data;
+ errcode_t retval;
+
+ if ((retval = ext2fs_get_mem(sizeof(struct inode_private_data),
+ &data)))
+ return retval;
+ data->magic = EXT2_ET_MAGIC_INODE_IO_CHANNEL;
+ sprintf(data->name, "%u:%d", ino, ino_unique++);
+ data->file = 0;
+ data->fs = fs;
+ data->ino = ino;
+ data->flags = 0;
+ if (inode) {
+ memcpy(&data->inode, inode, sizeof(struct ext2_inode));
+ data->flags |= CHANNEL_HAS_INODE;
+ }
+ data->next = top_intern;
+ top_intern = data;
+ *name = data->name;
+ return 0;
+}
+
+errcode_t ext2fs_inode_io_intern(ext2_filsys fs, ext2_ino_t ino,
+ char **name)
+{
+ return ext2fs_inode_io_intern2(fs, ino, NULL, name);
+}
+
+
+static errcode_t inode_open(const char *name, int flags, io_channel *channel)
+{
+ io_channel io = NULL;
+ struct inode_private_data *prev, *data = NULL;
+ errcode_t retval;
+ int open_flags;
+
+ if (name == 0)
+ return EXT2_ET_BAD_DEVICE_NAME;
+
+ for (data = top_intern, prev = NULL; data;
+ prev = data, data = data->next)
+ if (strcmp(name, data->name) == 0)
+ break;
+ if (!data)
+ return ENOENT;
+ if (prev)
+ prev->next = data->next;
+ else
+ top_intern = data->next;
+
+ retval = ext2fs_get_mem(sizeof(struct struct_io_channel), &io);
+ if (retval)
+ goto cleanup;
+ memset(io, 0, sizeof(struct struct_io_channel));
+
+ io->magic = EXT2_ET_MAGIC_IO_CHANNEL;
+ io->manager = inode_io_manager;
+ retval = ext2fs_get_mem(strlen(name)+1, &io->name);
+ if (retval)
+ goto cleanup;
+
+ strcpy(io->name, name);
+ io->private_data = data;
+ io->block_size = 1024;
+ io->read_error = 0;
+ io->write_error = 0;
+ io->refcount = 1;
+
+ open_flags = (flags & IO_FLAG_RW) ? EXT2_FILE_WRITE : 0;
+ retval = ext2fs_file_open2(data->fs, data->ino,
+ (data->flags & CHANNEL_HAS_INODE) ?
+ &data->inode : 0, open_flags,
+ &data->file);
+ if (retval)
+ goto cleanup;
+
+ *channel = io;
+ return 0;
+
+cleanup:
+ if (data) {
+ ext2fs_free_mem(&data);
+ }
+ if (io)
+ ext2fs_free_mem(&io);
+ return retval;
+}
+
+static errcode_t inode_close(io_channel channel)
+{
+ struct inode_private_data *data;
+ errcode_t retval = 0;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct inode_private_data *) channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL);
+
+ if (--channel->refcount > 0)
+ return 0;
+
+ retval = ext2fs_file_close(data->file);
+
+ ext2fs_free_mem(&channel->private_data);
+ if (channel->name)
+ ext2fs_free_mem(&channel->name);
+ ext2fs_free_mem(&channel);
+ return retval;
+}
+
+static errcode_t inode_set_blksize(io_channel channel, int blksize)
+{
+ struct inode_private_data *data;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct inode_private_data *) channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL);
+
+ channel->block_size = blksize;
+ return 0;
+}
+
+
+static errcode_t inode_read_blk(io_channel channel, unsigned long block,
+ int count, void *buf)
+{
+ struct inode_private_data *data;
+ errcode_t retval;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct inode_private_data *) channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL);
+
+ if ((retval = ext2fs_file_lseek(data->file,
+ block * channel->block_size,
+ EXT2_SEEK_SET, 0)))
+ return retval;
+
+ count = (count < 0) ? -count : (count * channel->block_size);
+
+ return ext2fs_file_read(data->file, buf, count, 0);
+}
+
+static errcode_t inode_write_blk(io_channel channel, unsigned long block,
+ int count, const void *buf)
+{
+ struct inode_private_data *data;
+ errcode_t retval;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct inode_private_data *) channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL);
+
+ if ((retval = ext2fs_file_lseek(data->file,
+ block * channel->block_size,
+ EXT2_SEEK_SET, 0)))
+ return retval;
+
+ count = (count < 0) ? -count : (count * channel->block_size);
+
+ return ext2fs_file_write(data->file, buf, count, 0);
+}
+
+static errcode_t inode_write_byte(io_channel channel, unsigned long offset,
+ int size, const void *buf)
+{
+ struct inode_private_data *data;
+ errcode_t retval = 0;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct inode_private_data *) channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL);
+
+ if ((retval = ext2fs_file_lseek(data->file, offset,
+ EXT2_SEEK_SET, 0)))
+ return retval;
+
+ return ext2fs_file_write(data->file, buf, size, 0);
+}
+
+/*
+ * Flush data buffers to disk.
+ */
+static errcode_t inode_flush(io_channel channel)
+{
+ struct inode_private_data *data;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct inode_private_data *) channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL);
+
+ return ext2fs_file_flush(data->file);
+}
+
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/io_manager.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/io_manager.c
new file mode 100644
index 0000000..b470386
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/io_manager.c
@@ -0,0 +1,70 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * io_manager.c --- the I/O manager abstraction
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+errcode_t io_channel_set_options(io_channel channel, const char *opts)
+{
+ errcode_t retval = 0;
+ char *next, *ptr, *options, *arg;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+
+ if (!opts)
+ return 0;
+
+ if (!channel->manager->set_option)
+ return EXT2_ET_INVALID_ARGUMENT;
+
+ options = malloc(strlen(opts)+1);
+ if (!options)
+ return EXT2_ET_NO_MEMORY;
+ strcpy(options, opts);
+ ptr = options;
+
+ while (ptr && *ptr) {
+ next = strchr(ptr, '&');
+ if (next)
+ *next++ = 0;
+
+ arg = strchr(ptr, '=');
+ if (arg)
+ *arg++ = 0;
+
+ retval = (channel->manager->set_option)(channel, ptr, arg);
+ if (retval)
+ break;
+ ptr = next;
+ }
+ free(options);
+ return retval;
+}
+
+errcode_t io_channel_write_byte(io_channel channel, unsigned long offset,
+ int count, const void *data)
+{
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+
+ if (channel->manager->write_byte)
+ return channel->manager->write_byte(channel, offset,
+ count, data);
+
+ return EXT2_ET_UNIMPLEMENTED;
+}
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/irel.h b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/irel.h
new file mode 100644
index 0000000..91d1d89
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/irel.h
@@ -0,0 +1,115 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * irel.h
+ *
+ * Copyright (C) 1996, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+struct ext2_inode_reference {
+ blk_t block;
+ __u16 offset;
+};
+
+struct ext2_inode_relocate_entry {
+ ext2_ino_t new;
+ ext2_ino_t orig;
+ __u16 flags;
+ __u16 max_refs;
+};
+
+typedef struct ext2_inode_relocation_table *ext2_irel;
+
+struct ext2_inode_relocation_table {
+ __u32 magic;
+ char *name;
+ ext2_ino_t current;
+ void *priv_data;
+
+ /*
+ * Add an inode relocation entry.
+ */
+ errcode_t (*put)(ext2_irel irel, ext2_ino_t old,
+ struct ext2_inode_relocate_entry *ent);
+ /*
+ * Get an inode relocation entry.
+ */
+ errcode_t (*get)(ext2_irel irel, ext2_ino_t old,
+ struct ext2_inode_relocate_entry *ent);
+
+ /*
+ * Get an inode relocation entry by its original inode number
+ */
+ errcode_t (*get_by_orig)(ext2_irel irel, ext2_ino_t orig, ext2_ino_t *old,
+ struct ext2_inode_relocate_entry *ent);
+
+ /*
+ * Initialize for iterating over the inode relocation entries.
+ */
+ errcode_t (*start_iter)(ext2_irel irel);
+
+ /*
+ * The iterator function for the inode relocation entries.
+ * Returns an inode number of 0 when out of entries.
+ */
+ errcode_t (*next)(ext2_irel irel, ext2_ino_t *old,
+ struct ext2_inode_relocate_entry *ent);
+
+ /*
+ * Add an inode reference (i.e., note the fact that a
+ * particular block/offset contains a reference to an inode)
+ */
+ errcode_t (*add_ref)(ext2_irel irel, ext2_ino_t ino,
+ struct ext2_inode_reference *ref);
+
+ /*
+ * Initialize for iterating over the inode references for a
+ * particular inode.
+ */
+ errcode_t (*start_iter_ref)(ext2_irel irel, ext2_ino_t ino);
+
+ /*
+ * The iterator function for the inode references for an
+ * inode. The references for only one inode can be interator
+ * over at a time, as the iterator state is stored in ext2_irel.
+ */
+ errcode_t (*next_ref)(ext2_irel irel,
+ struct ext2_inode_reference *ref);
+
+ /*
+ * Move the inode relocation table from one inode number to
+ * another. Note that the inode references also must move.
+ */
+ errcode_t (*move)(ext2_irel irel, ext2_ino_t old, ext2_ino_t new);
+
+ /*
+ * Remove an inode relocation entry, along with all of the
+ * inode references.
+ */
+ errcode_t (*delete)(ext2_irel irel, ext2_ino_t old);
+
+ /*
+ * Free the inode relocation table.
+ */
+ errcode_t (*free)(ext2_irel irel);
+};
+
+errcode_t ext2fs_irel_memarray_create(char *name, ext2_ino_t max_inode,
+ ext2_irel *irel);
+
+#define ext2fs_irel_put(irel, old, ent) ((irel)->put((irel), old, ent))
+#define ext2fs_irel_get(irel, old, ent) ((irel)->get((irel), old, ent))
+#define ext2fs_irel_get_by_orig(irel, orig, old, ent) \
+ ((irel)->get_by_orig((irel), orig, old, ent))
+#define ext2fs_irel_start_iter(irel) ((irel)->start_iter((irel)))
+#define ext2fs_irel_next(irel, old, ent) ((irel)->next((irel), old, ent))
+#define ext2fs_irel_add_ref(irel, ino, ref) ((irel)->add_ref((irel), ino, ref))
+#define ext2fs_irel_start_iter_ref(irel, ino) ((irel)->start_iter_ref((irel), ino))
+#define ext2fs_irel_next_ref(irel, ref) ((irel)->next_ref((irel), ref))
+#define ext2fs_irel_move(irel, old, new) ((irel)->move((irel), old, new))
+#define ext2fs_irel_delete(irel, old) ((irel)->delete((irel), old))
+#define ext2fs_irel_free(irel) ((irel)->free((irel)))
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/irel_ma.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/irel_ma.c
new file mode 100644
index 0000000..c871b18
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/irel_ma.c
@@ -0,0 +1,367 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * irel_ma.c
+ *
+ * Copyright (C) 1996, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+#include "irel.h"
+
+static errcode_t ima_put(ext2_irel irel, ext2_ino_t old,
+ struct ext2_inode_relocate_entry *ent);
+static errcode_t ima_get(ext2_irel irel, ext2_ino_t old,
+ struct ext2_inode_relocate_entry *ent);
+static errcode_t ima_get_by_orig(ext2_irel irel, ext2_ino_t orig, ext2_ino_t *old,
+ struct ext2_inode_relocate_entry *ent);
+static errcode_t ima_start_iter(ext2_irel irel);
+static errcode_t ima_next(ext2_irel irel, ext2_ino_t *old,
+ struct ext2_inode_relocate_entry *ent);
+static errcode_t ima_add_ref(ext2_irel irel, ext2_ino_t ino,
+ struct ext2_inode_reference *ref);
+static errcode_t ima_start_iter_ref(ext2_irel irel, ext2_ino_t ino);
+static errcode_t ima_next_ref(ext2_irel irel, struct ext2_inode_reference *ref);
+static errcode_t ima_move(ext2_irel irel, ext2_ino_t old, ext2_ino_t new);
+static errcode_t ima_delete(ext2_irel irel, ext2_ino_t old);
+static errcode_t ima_free(ext2_irel irel);
+
+/*
+ * This data structure stores the array of inode references; there is
+ * a structure for each inode.
+ */
+struct inode_reference_entry {
+ __u16 num;
+ struct ext2_inode_reference *refs;
+};
+
+struct irel_ma {
+ __u32 magic;
+ ext2_ino_t max_inode;
+ ext2_ino_t ref_current;
+ int ref_iter;
+ ext2_ino_t *orig_map;
+ struct ext2_inode_relocate_entry *entries;
+ struct inode_reference_entry *ref_entries;
+};
+
+errcode_t ext2fs_irel_memarray_create(char *name, ext2_ino_t max_inode,
+ ext2_irel *new_irel)
+{
+ ext2_irel irel = 0;
+ errcode_t retval;
+ struct irel_ma *ma = 0;
+ size_t size;
+
+ *new_irel = 0;
+
+ /*
+ * Allocate memory structures
+ */
+ retval = ext2fs_get_mem(sizeof(struct ext2_inode_relocation_table),
+ &irel);
+ if (retval)
+ goto errout;
+ memset(irel, 0, sizeof(struct ext2_inode_relocation_table));
+
+ retval = ext2fs_get_mem(strlen(name)+1, &irel->name);
+ if (retval)
+ goto errout;
+ strcpy(irel->name, name);
+
+ retval = ext2fs_get_mem(sizeof(struct irel_ma), &ma);
+ if (retval)
+ goto errout;
+ memset(ma, 0, sizeof(struct irel_ma));
+ irel->priv_data = ma;
+
+ size = (size_t) (sizeof(ext2_ino_t) * (max_inode+1));
+ retval = ext2fs_get_mem(size, &ma->orig_map);
+ if (retval)
+ goto errout;
+ memset(ma->orig_map, 0, size);
+
+ size = (size_t) (sizeof(struct ext2_inode_relocate_entry) *
+ (max_inode+1));
+ retval = ext2fs_get_mem(size, &ma->entries);
+ if (retval)
+ goto errout;
+ memset(ma->entries, 0, size);
+
+ size = (size_t) (sizeof(struct inode_reference_entry) *
+ (max_inode+1));
+ retval = ext2fs_get_mem(size, &ma->ref_entries);
+ if (retval)
+ goto errout;
+ memset(ma->ref_entries, 0, size);
+ ma->max_inode = max_inode;
+
+ /*
+ * Fill in the irel data structure
+ */
+ irel->put = ima_put;
+ irel->get = ima_get;
+ irel->get_by_orig = ima_get_by_orig;
+ irel->start_iter = ima_start_iter;
+ irel->next = ima_next;
+ irel->add_ref = ima_add_ref;
+ irel->start_iter_ref = ima_start_iter_ref;
+ irel->next_ref = ima_next_ref;
+ irel->move = ima_move;
+ irel->delete = ima_delete;
+ irel->free = ima_free;
+
+ *new_irel = irel;
+ return 0;
+
+errout:
+ ima_free(irel);
+ return retval;
+}
+
+static errcode_t ima_put(ext2_irel irel, ext2_ino_t old,
+ struct ext2_inode_relocate_entry *ent)
+{
+ struct inode_reference_entry *ref_ent;
+ struct irel_ma *ma;
+ errcode_t retval;
+ size_t size, old_size;
+
+ ma = irel->priv_data;
+ if (old > ma->max_inode)
+ return EXT2_ET_INVALID_ARGUMENT;
+
+ /*
+ * Force the orig field to the correct value; the application
+ * program shouldn't be messing with this field.
+ */
+ if (ma->entries[(unsigned) old].new == 0)
+ ent->orig = old;
+ else
+ ent->orig = ma->entries[(unsigned) old].orig;
+
+ /*
+ * If max_refs has changed, reallocate the refs array
+ */
+ ref_ent = ma->ref_entries + (unsigned) old;
+ if (ref_ent->refs && ent->max_refs !=
+ ma->entries[(unsigned) old].max_refs) {
+ size = (sizeof(struct ext2_inode_reference) * ent->max_refs);
+ old_size = (sizeof(struct ext2_inode_reference) *
+ ma->entries[(unsigned) old].max_refs);
+ retval = ext2fs_resize_mem(old_size, size, &ref_ent->refs);
+ if (retval)
+ return retval;
+ }
+
+ ma->entries[(unsigned) old] = *ent;
+ ma->orig_map[(unsigned) ent->orig] = old;
+ return 0;
+}
+
+static errcode_t ima_get(ext2_irel irel, ext2_ino_t old,
+ struct ext2_inode_relocate_entry *ent)
+{
+ struct irel_ma *ma;
+
+ ma = irel->priv_data;
+ if (old > ma->max_inode)
+ return EXT2_ET_INVALID_ARGUMENT;
+ if (ma->entries[(unsigned) old].new == 0)
+ return ENOENT;
+ *ent = ma->entries[(unsigned) old];
+ return 0;
+}
+
+static errcode_t ima_get_by_orig(ext2_irel irel, ext2_ino_t orig, ext2_ino_t *old,
+ struct ext2_inode_relocate_entry *ent)
+{
+ struct irel_ma *ma;
+ ext2_ino_t ino;
+
+ ma = irel->priv_data;
+ if (orig > ma->max_inode)
+ return EXT2_ET_INVALID_ARGUMENT;
+ ino = ma->orig_map[(unsigned) orig];
+ if (ino == 0)
+ return ENOENT;
+ *old = ino;
+ *ent = ma->entries[(unsigned) ino];
+ return 0;
+}
+
+static errcode_t ima_start_iter(ext2_irel irel)
+{
+ irel->current = 0;
+ return 0;
+}
+
+static errcode_t ima_next(ext2_irel irel, ext2_ino_t *old,
+ struct ext2_inode_relocate_entry *ent)
+{
+ struct irel_ma *ma;
+
+ ma = irel->priv_data;
+ while (++irel->current < ma->max_inode) {
+ if (ma->entries[(unsigned) irel->current].new == 0)
+ continue;
+ *old = irel->current;
+ *ent = ma->entries[(unsigned) irel->current];
+ return 0;
+ }
+ *old = 0;
+ return 0;
+}
+
+static errcode_t ima_add_ref(ext2_irel irel, ext2_ino_t ino,
+ struct ext2_inode_reference *ref)
+{
+ struct irel_ma *ma;
+ size_t size;
+ struct inode_reference_entry *ref_ent;
+ struct ext2_inode_relocate_entry *ent;
+ errcode_t retval;
+
+ ma = irel->priv_data;
+ if (ino > ma->max_inode)
+ return EXT2_ET_INVALID_ARGUMENT;
+
+ ref_ent = ma->ref_entries + (unsigned) ino;
+ ent = ma->entries + (unsigned) ino;
+
+ /*
+ * If the inode reference array doesn't exist, create it.
+ */
+ if (ref_ent->refs == 0) {
+ size = (size_t) ((sizeof(struct ext2_inode_reference) *
+ ent->max_refs));
+ retval = ext2fs_get_mem(size, &ref_ent->refs);
+ if (retval)
+ return retval;
+ memset(ref_ent->refs, 0, size);
+ ref_ent->num = 0;
+ }
+
+ if (ref_ent->num >= ent->max_refs)
+ return EXT2_ET_TOO_MANY_REFS;
+
+ ref_ent->refs[(unsigned) ref_ent->num++] = *ref;
+ return 0;
+}
+
+static errcode_t ima_start_iter_ref(ext2_irel irel, ext2_ino_t ino)
+{
+ struct irel_ma *ma;
+
+ ma = irel->priv_data;
+ if (ino > ma->max_inode)
+ return EXT2_ET_INVALID_ARGUMENT;
+ if (ma->entries[(unsigned) ino].new == 0)
+ return ENOENT;
+ ma->ref_current = ino;
+ ma->ref_iter = 0;
+ return 0;
+}
+
+static errcode_t ima_next_ref(ext2_irel irel,
+ struct ext2_inode_reference *ref)
+{
+ struct irel_ma *ma;
+ struct inode_reference_entry *ref_ent;
+
+ ma = irel->priv_data;
+
+ ref_ent = ma->ref_entries + ma->ref_current;
+
+ if ((ref_ent->refs == NULL) ||
+ (ma->ref_iter >= ref_ent->num)) {
+ ref->block = 0;
+ ref->offset = 0;
+ return 0;
+ }
+ *ref = ref_ent->refs[ma->ref_iter++];
+ return 0;
+}
+
+
+static errcode_t ima_move(ext2_irel irel, ext2_ino_t old, ext2_ino_t new)
+{
+ struct irel_ma *ma;
+
+ ma = irel->priv_data;
+ if ((old > ma->max_inode) || (new > ma->max_inode))
+ return EXT2_ET_INVALID_ARGUMENT;
+ if (ma->entries[(unsigned) old].new == 0)
+ return ENOENT;
+
+ ma->entries[(unsigned) new] = ma->entries[(unsigned) old];
+ ext2fs_free_mem(&ma->ref_entries[(unsigned) new].refs);
+ ma->ref_entries[(unsigned) new] = ma->ref_entries[(unsigned) old];
+
+ ma->entries[(unsigned) old].new = 0;
+ ma->ref_entries[(unsigned) old].num = 0;
+ ma->ref_entries[(unsigned) old].refs = 0;
+
+ ma->orig_map[ma->entries[new].orig] = new;
+ return 0;
+}
+
+static errcode_t ima_delete(ext2_irel irel, ext2_ino_t old)
+{
+ struct irel_ma *ma;
+
+ ma = irel->priv_data;
+ if (old > ma->max_inode)
+ return EXT2_ET_INVALID_ARGUMENT;
+ if (ma->entries[(unsigned) old].new == 0)
+ return ENOENT;
+
+ ma->entries[old].new = 0;
+ ext2fs_free_mem(&ma->ref_entries[(unsigned) old].refs);
+ ma->orig_map[ma->entries[(unsigned) old].orig] = 0;
+
+ ma->ref_entries[(unsigned) old].num = 0;
+ ma->ref_entries[(unsigned) old].refs = 0;
+ return 0;
+}
+
+static errcode_t ima_free(ext2_irel irel)
+{
+ struct irel_ma *ma;
+ ext2_ino_t ino;
+
+ if (!irel)
+ return 0;
+
+ ma = irel->priv_data;
+
+ if (ma) {
+ ext2fs_free_mem(&ma->orig_map);
+ ext2fs_free_mem(&ma->entries);
+ if (ma->ref_entries) {
+ for (ino = 0; ino <= ma->max_inode; ino++) {
+ ext2fs_free_mem(&ma->ref_entries[(unsigned) ino].refs);
+ }
+ ext2fs_free_mem(&ma->ref_entries);
+ }
+ ext2fs_free_mem(&ma);
+ }
+ ext2fs_free_mem(&irel->name);
+ ext2fs_free_mem(&irel);
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/ismounted.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/ismounted.c
new file mode 100644
index 0000000..d943f11
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/ismounted.c
@@ -0,0 +1,357 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ismounted.c --- Check to see if the filesystem was mounted
+ *
+ * Copyright (C) 1995,1996,1997,1998,1999,2000 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <fcntl.h>
+#ifdef HAVE_LINUX_FD_H
+#include <linux/fd.h>
+#endif
+#ifdef HAVE_MNTENT_H
+#include <mntent.h>
+#endif
+#ifdef HAVE_GETMNTINFO
+#include <paths.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+#endif /* HAVE_GETMNTINFO */
+#include <string.h>
+#include <sys/stat.h>
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+#ifdef HAVE_MNTENT_H
+/*
+ * Helper function which checks a file in /etc/mtab format to see if a
+ * filesystem is mounted. Returns an error if the file doesn't exist
+ * or can't be opened.
+ */
+static errcode_t check_mntent_file(const char *mtab_file, const char *file,
+ int *mount_flags, char *mtpt, int mtlen)
+{
+ struct mntent *mnt;
+ struct stat st_buf;
+ errcode_t retval = 0;
+ dev_t file_dev=0, file_rdev=0;
+ ino_t file_ino=0;
+ FILE *f;
+ int fd;
+
+ *mount_flags = 0;
+ if ((f = setmntent (mtab_file, "r")) == NULL)
+ return errno;
+ if (stat(file, &st_buf) == 0) {
+ if (S_ISBLK(st_buf.st_mode)) {
+#ifndef __GNU__ /* The GNU hurd is broken with respect to stat devices */
+ file_rdev = st_buf.st_rdev;
+#endif /* __GNU__ */
+ } else {
+ file_dev = st_buf.st_dev;
+ file_ino = st_buf.st_ino;
+ }
+ }
+ while ((mnt = getmntent (f)) != NULL) {
+ if (strcmp(file, mnt->mnt_fsname) == 0)
+ break;
+ if (stat(mnt->mnt_fsname, &st_buf) == 0) {
+ if (S_ISBLK(st_buf.st_mode)) {
+#ifndef __GNU__
+ if (file_rdev && (file_rdev == st_buf.st_rdev))
+ break;
+#endif /* __GNU__ */
+ } else {
+ if (file_dev && ((file_dev == st_buf.st_dev) &&
+ (file_ino == st_buf.st_ino)))
+ break;
+ }
+ }
+ }
+
+ if (mnt == 0) {
+#ifndef __GNU__ /* The GNU hurd is broken with respect to stat devices */
+ /*
+ * Do an extra check to see if this is the root device. We
+ * can't trust /etc/mtab, and /proc/mounts will only list
+ * /dev/root for the root filesystem. Argh. Instead we
+ * check if the given device has the same major/minor number
+ * as the device that the root directory is on.
+ */
+ if (file_rdev && stat("/", &st_buf) == 0) {
+ if (st_buf.st_dev == file_rdev) {
+ *mount_flags = EXT2_MF_MOUNTED;
+ if (mtpt)
+ strncpy(mtpt, "/", mtlen);
+ goto is_root;
+ }
+ }
+#endif /* __GNU__ */
+ goto errout;
+ }
+#ifndef __GNU__ /* The GNU hurd is deficient; what else is new? */
+ /* Validate the entry in case /etc/mtab is out of date */
+ /*
+ * We need to be paranoid, because some broken distributions
+ * (read: Slackware) don't initialize /etc/mtab before checking
+ * all of the non-root filesystems on the disk.
+ */
+ if (stat(mnt->mnt_dir, &st_buf) < 0) {
+ retval = errno;
+ if (retval == ENOENT) {
+#ifdef DEBUG
+ printf("Bogus entry in %s! (%s does not exist)\n",
+ mtab_file, mnt->mnt_dir);
+#endif /* DEBUG */
+ retval = 0;
+ }
+ goto errout;
+ }
+ if (file_rdev && (st_buf.st_dev != file_rdev)) {
+#ifdef DEBUG
+ printf("Bogus entry in %s! (%s not mounted on %s)\n",
+ mtab_file, file, mnt->mnt_dir);
+#endif /* DEBUG */
+ goto errout;
+ }
+#endif /* __GNU__ */
+ *mount_flags = EXT2_MF_MOUNTED;
+
+#ifdef MNTOPT_RO
+ /* Check to see if the ro option is set */
+ if (hasmntopt(mnt, MNTOPT_RO))
+ *mount_flags |= EXT2_MF_READONLY;
+#endif
+
+ if (mtpt)
+ strncpy(mtpt, mnt->mnt_dir, mtlen);
+ /*
+ * Check to see if we're referring to the root filesystem.
+ * If so, do a manual check to see if we can open /etc/mtab
+ * read/write, since if the root is mounted read/only, the
+ * contents of /etc/mtab may not be accurate.
+ */
+ if (LONE_CHAR(mnt->mnt_dir, '/')) {
+is_root:
+#define TEST_FILE "/.ismount-test-file"
+ *mount_flags |= EXT2_MF_ISROOT;
+ fd = open(TEST_FILE, O_RDWR|O_CREAT);
+ if (fd < 0) {
+ if (errno == EROFS)
+ *mount_flags |= EXT2_MF_READONLY;
+ } else
+ close(fd);
+ (void) unlink(TEST_FILE);
+ }
+ retval = 0;
+errout:
+ endmntent (f);
+ return retval;
+}
+
+static errcode_t check_mntent(const char *file, int *mount_flags,
+ char *mtpt, int mtlen)
+{
+ errcode_t retval;
+
+#ifdef DEBUG
+ retval = check_mntent_file("/tmp/mtab", file, mount_flags,
+ mtpt, mtlen);
+ if (retval == 0)
+ return 0;
+#endif /* DEBUG */
+#ifdef __linux__
+ retval = check_mntent_file("/proc/mounts", file, mount_flags,
+ mtpt, mtlen);
+ if (retval == 0 && (*mount_flags != 0))
+ return 0;
+#endif /* __linux__ */
+#if defined(MOUNTED) || defined(_PATH_MOUNTED)
+#ifndef MOUNTED
+#define MOUNTED _PATH_MOUNTED
+#endif /* MOUNTED */
+ retval = check_mntent_file(MOUNTED, file, mount_flags, mtpt, mtlen);
+ return retval;
+#else
+ *mount_flags = 0;
+ return 0;
+#endif /* defined(MOUNTED) || defined(_PATH_MOUNTED) */
+}
+
+#else
+#if defined(HAVE_GETMNTINFO)
+
+static errcode_t check_getmntinfo(const char *file, int *mount_flags,
+ char *mtpt, int mtlen)
+{
+ struct statfs *mp;
+ int len, n;
+ const char *s1;
+ char *s2;
+
+ n = getmntinfo(&mp, MNT_NOWAIT);
+ if (n == 0)
+ return errno;
+
+ len = sizeof(_PATH_DEV) - 1;
+ s1 = file;
+ if (strncmp(_PATH_DEV, s1, len) == 0)
+ s1 += len;
+
+ *mount_flags = 0;
+ while (--n >= 0) {
+ s2 = mp->f_mntfromname;
+ if (strncmp(_PATH_DEV, s2, len) == 0) {
+ s2 += len - 1;
+ *s2 = 'r';
+ }
+ if (strcmp(s1, s2) == 0 || strcmp(s1, &s2[1]) == 0) {
+ *mount_flags = EXT2_MF_MOUNTED;
+ break;
+ }
+ ++mp;
+ }
+ if (mtpt)
+ strncpy(mtpt, mp->f_mntonname, mtlen);
+ return 0;
+}
+#endif /* HAVE_GETMNTINFO */
+#endif /* HAVE_MNTENT_H */
+
+/*
+ * Check to see if we're dealing with the swap device.
+ */
+static int is_swap_device(const char *file)
+{
+ FILE *f;
+ char buf[1024], *cp;
+ dev_t file_dev;
+ struct stat st_buf;
+ int ret = 0;
+
+ file_dev = 0;
+#ifndef __GNU__ /* The GNU hurd is broken with respect to stat devices */
+ if ((stat(file, &st_buf) == 0) &&
+ S_ISBLK(st_buf.st_mode))
+ file_dev = st_buf.st_rdev;
+#endif /* __GNU__ */
+
+ if (!(f = fopen("/proc/swaps", "r")))
+ return 0;
+ /* Skip the first line */
+ fgets(buf, sizeof(buf), f);
+ while (!feof(f)) {
+ if (!fgets(buf, sizeof(buf), f))
+ break;
+ if ((cp = strchr(buf, ' ')) != NULL)
+ *cp = 0;
+ if ((cp = strchr(buf, '\t')) != NULL)
+ *cp = 0;
+ if (strcmp(buf, file) == 0) {
+ ret++;
+ break;
+ }
+#ifndef __GNU__
+ if (file_dev && (stat(buf, &st_buf) == 0) &&
+ S_ISBLK(st_buf.st_mode) &&
+ file_dev == st_buf.st_rdev) {
+ ret++;
+ break;
+ }
+#endif /* __GNU__ */
+ }
+ fclose(f);
+ return ret;
+}
+
+
+/*
+ * ext2fs_check_mount_point() returns 1 if the device is mounted, 0
+ * otherwise. If mtpt is non-NULL, the directory where the device is
+ * mounted is copied to where mtpt is pointing, up to mtlen
+ * characters.
+ */
+#ifdef __TURBOC__
+# pragma argsused
+#endif
+errcode_t ext2fs_check_mount_point(const char *device, int *mount_flags,
+ char *mtpt, int mtlen)
+{
+ if (is_swap_device(device)) {
+ *mount_flags = EXT2_MF_MOUNTED | EXT2_MF_SWAP;
+ strncpy(mtpt, "<swap>", mtlen);
+ return 0;
+ }
+#ifdef HAVE_MNTENT_H
+ return check_mntent(device, mount_flags, mtpt, mtlen);
+#else
+#ifdef HAVE_GETMNTINFO
+ return check_getmntinfo(device, mount_flags, mtpt, mtlen);
+#else
+#ifdef __GNUC__
+ #warning "Can't use getmntent or getmntinfo to check for mounted filesystems!"
+#endif
+ *mount_flags = 0;
+ return 0;
+#endif /* HAVE_GETMNTINFO */
+#endif /* HAVE_MNTENT_H */
+}
+
+/*
+ * ext2fs_check_if_mounted() sets the mount_flags EXT2_MF_MOUNTED,
+ * EXT2_MF_READONLY, and EXT2_MF_ROOT
+ *
+ */
+errcode_t ext2fs_check_if_mounted(const char *file, int *mount_flags)
+{
+ return ext2fs_check_mount_point(file, mount_flags, NULL, 0);
+}
+
+#ifdef DEBUG
+int main(int argc, char **argv)
+{
+ int retval, mount_flags;
+ char mntpt[80];
+
+ if (argc < 2) {
+ fprintf(stderr, "Usage: %s device\n", argv[0]);
+ exit(1);
+ }
+
+ mntpt[0] = 0;
+ retval = ext2fs_check_mount_point(argv[1], &mount_flags,
+ mntpt, sizeof(mntpt));
+ if (retval) {
+ com_err(argv[0], retval,
+ "while calling ext2fs_check_if_mounted");
+ exit(1);
+ }
+ printf("Device %s reports flags %02x\n", argv[1], mount_flags);
+ if (mount_flags & EXT2_MF_BUSY)
+ printf("\t%s is apparently in use.\n", argv[1]);
+ if (mount_flags & EXT2_MF_MOUNTED)
+ printf("\t%s is mounted.\n", argv[1]);
+ if (mount_flags & EXT2_MF_SWAP)
+ printf("\t%s is a swap device.\n", argv[1]);
+ if (mount_flags & EXT2_MF_READONLY)
+ printf("\t%s is read-only.\n", argv[1]);
+ if (mount_flags & EXT2_MF_ISROOT)
+ printf("\t%s is the root filesystem.\n", argv[1]);
+ if (mntpt[0])
+ printf("\t%s is mounted on %s.\n", argv[1], mntpt);
+ exit(0);
+}
+#endif /* DEBUG */
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/jfs_dat.h b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/jfs_dat.h
new file mode 100644
index 0000000..136635d
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/jfs_dat.h
@@ -0,0 +1,65 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * jfs_dat.h --- stripped down header file which only contains the JFS
+ * on-disk data structures
+ */
+
+#define JFS_MAGIC_NUMBER 0xc03b3998U /* The first 4 bytes of /dev/random! */
+
+/*
+ * On-disk structures
+ */
+
+/*
+ * Descriptor block types:
+ */
+
+#define JFS_DESCRIPTOR_BLOCK 1
+#define JFS_COMMIT_BLOCK 2
+#define JFS_SUPERBLOCK 3
+
+/*
+ * Standard header for all descriptor blocks:
+ */
+typedef struct journal_header_s
+{
+ __u32 h_magic;
+ __u32 h_blocktype;
+ __u32 h_sequence;
+} journal_header_t;
+
+
+/*
+ * The block tag: used to describe a single buffer in the journal
+ */
+typedef struct journal_block_tag_s
+{
+ __u32 t_blocknr; /* The on-disk block number */
+ __u32 t_flags; /* See below */
+} journal_block_tag_t;
+
+/* Definitions for the journal tag flags word: */
+#define JFS_FLAG_ESCAPE 1 /* on-disk block is escaped */
+#define JFS_FLAG_SAME_UUID 2 /* block has same uuid as previous */
+#define JFS_FLAG_DELETED 4 /* block deleted by this transaction */
+#define JFS_FLAG_LAST_TAG 8 /* last tag in this descriptor block */
+
+
+/*
+ * The journal superblock
+ */
+typedef struct journal_superblock_s
+{
+ journal_header_t s_header;
+
+ /* Static information describing the journal */
+ __u32 s_blocksize; /* journal device blocksize */
+ __u32 s_maxlen; /* total blocks in journal file */
+ __u32 s_first; /* first block of log information */
+
+ /* Dynamic information describing the current state of the log */
+ __u32 s_sequence; /* first commit ID expected in log */
+ __u32 s_start; /* blocknr of start of log */
+
+} journal_superblock_t;
+
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/kernel-jbd.h b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/kernel-jbd.h
new file mode 100644
index 0000000..4c6c7de
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/kernel-jbd.h
@@ -0,0 +1,236 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * linux/include/linux/jbd.h
+ *
+ * Written by Stephen C. Tweedie <sct@redhat.com>
+ *
+ * Copyright 1998-2000 Red Hat, Inc --- All Rights Reserved
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ *
+ * Definitions for transaction data structures for the buffer cache
+ * filesystem journaling support.
+ */
+
+#ifndef _LINUX_JBD_H
+#define _LINUX_JBD_H
+
+#include <sys/types.h>
+#include <linux/types.h>
+#include "ext2fs.h"
+
+/*
+ * Standard header for all descriptor blocks:
+ */
+
+typedef struct journal_header_s
+{
+ __u32 h_magic;
+ __u32 h_blocktype;
+ __u32 h_sequence;
+} journal_header_t;
+
+/*
+ * This is the global e2fsck structure.
+ */
+typedef struct e2fsck_struct *e2fsck_t;
+
+
+struct inode {
+ e2fsck_t i_ctx;
+ ext2_ino_t i_ino;
+ struct ext2_inode i_ext2;
+};
+
+
+/*
+ * The journal superblock. All fields are in big-endian byte order.
+ */
+typedef struct journal_superblock_s
+{
+/* 0x0000 */
+ journal_header_t s_header;
+
+/* 0x000C */
+ /* Static information describing the journal */
+ __u32 s_blocksize; /* journal device blocksize */
+ __u32 s_maxlen; /* total blocks in journal file */
+ __u32 s_first; /* first block of log information */
+
+/* 0x0018 */
+ /* Dynamic information describing the current state of the log */
+ __u32 s_sequence; /* first commit ID expected in log */
+ __u32 s_start; /* blocknr of start of log */
+
+/* 0x0020 */
+ /* Error value, as set by journal_abort(). */
+ __s32 s_errno;
+
+/* 0x0024 */
+ /* Remaining fields are only valid in a version-2 superblock */
+ __u32 s_feature_compat; /* compatible feature set */
+ __u32 s_feature_incompat; /* incompatible feature set */
+ __u32 s_feature_ro_compat; /* readonly-compatible feature set */
+/* 0x0030 */
+ __u8 s_uuid[16]; /* 128-bit uuid for journal */
+
+/* 0x0040 */
+ __u32 s_nr_users; /* Nr of filesystems sharing log */
+
+ __u32 s_dynsuper; /* Blocknr of dynamic superblock copy*/
+
+/* 0x0048 */
+ __u32 s_max_transaction; /* Limit of journal blocks per trans.*/
+ __u32 s_max_trans_data; /* Limit of data blocks per trans. */
+
+/* 0x0050 */
+ __u32 s_padding[44];
+
+/* 0x0100 */
+ __u8 s_users[16*48]; /* ids of all fs'es sharing the log */
+/* 0x0400 */
+} journal_superblock_t;
+
+
+extern int journal_blocks_per_page(struct inode *inode);
+extern int jbd_blocks_per_page(struct inode *inode);
+
+#define JFS_MIN_JOURNAL_BLOCKS 1024
+
+
+/*
+ * Internal structures used by the logging mechanism:
+ */
+
+#define JFS_MAGIC_NUMBER 0xc03b3998U /* The first 4 bytes of /dev/random! */
+
+/*
+ * Descriptor block types:
+ */
+
+#define JFS_DESCRIPTOR_BLOCK 1
+#define JFS_COMMIT_BLOCK 2
+#define JFS_SUPERBLOCK_V1 3
+#define JFS_SUPERBLOCK_V2 4
+#define JFS_REVOKE_BLOCK 5
+
+/*
+ * The block tag: used to describe a single buffer in the journal
+ */
+typedef struct journal_block_tag_s
+{
+ __u32 t_blocknr; /* The on-disk block number */
+ __u32 t_flags; /* See below */
+} journal_block_tag_t;
+
+/*
+ * The revoke descriptor: used on disk to describe a series of blocks to
+ * be revoked from the log
+ */
+typedef struct journal_revoke_header_s
+{
+ journal_header_t r_header;
+ int r_count; /* Count of bytes used in the block */
+} journal_revoke_header_t;
+
+
+/* Definitions for the journal tag flags word: */
+#define JFS_FLAG_ESCAPE 1 /* on-disk block is escaped */
+#define JFS_FLAG_SAME_UUID 2 /* block has same uuid as previous */
+#define JFS_FLAG_DELETED 4 /* block deleted by this transaction */
+#define JFS_FLAG_LAST_TAG 8 /* last tag in this descriptor block */
+
+
+
+
+#define JFS_HAS_COMPAT_FEATURE(j,mask) \
+ ((j)->j_format_version >= 2 && \
+ ((j)->j_superblock->s_feature_compat & cpu_to_be32((mask))))
+#define JFS_HAS_RO_COMPAT_FEATURE(j,mask) \
+ ((j)->j_format_version >= 2 && \
+ ((j)->j_superblock->s_feature_ro_compat & cpu_to_be32((mask))))
+#define JFS_HAS_INCOMPAT_FEATURE(j,mask) \
+ ((j)->j_format_version >= 2 && \
+ ((j)->j_superblock->s_feature_incompat & cpu_to_be32((mask))))
+
+#define JFS_FEATURE_INCOMPAT_REVOKE 0x00000001
+
+/* Features known to this kernel version: */
+#define JFS_KNOWN_COMPAT_FEATURES 0
+#define JFS_KNOWN_ROCOMPAT_FEATURES 0
+#define JFS_KNOWN_INCOMPAT_FEATURES JFS_FEATURE_INCOMPAT_REVOKE
+
+/* Comparison functions for transaction IDs: perform comparisons using
+ * modulo arithmetic so that they work over sequence number wraps. */
+
+
+/*
+ * Definitions which augment the buffer_head layer
+ */
+
+/* journaling buffer types */
+#define BJ_None 0 /* Not journaled */
+#define BJ_SyncData 1 /* Normal data: flush before commit */
+#define BJ_AsyncData 2 /* writepage data: wait on it before commit */
+#define BJ_Metadata 3 /* Normal journaled metadata */
+#define BJ_Forget 4 /* Buffer superceded by this transaction */
+#define BJ_IO 5 /* Buffer is for temporary IO use */
+#define BJ_Shadow 6 /* Buffer contents being shadowed to the log */
+#define BJ_LogCtl 7 /* Buffer contains log descriptors */
+#define BJ_Reserved 8 /* Buffer is reserved for access by journal */
+#define BJ_Types 9
+
+
+struct kdev_s {
+ e2fsck_t k_ctx;
+ int k_dev;
+};
+
+typedef struct kdev_s *kdev_t;
+typedef unsigned int tid_t;
+
+struct journal_s
+{
+ unsigned long j_flags;
+ int j_errno;
+ struct buffer_head * j_sb_buffer;
+ struct journal_superblock_s *j_superblock;
+ int j_format_version;
+ unsigned long j_head;
+ unsigned long j_tail;
+ unsigned long j_free;
+ unsigned long j_first, j_last;
+ kdev_t j_dev;
+ kdev_t j_fs_dev;
+ int j_blocksize;
+ unsigned int j_blk_offset;
+ unsigned int j_maxlen;
+ struct inode * j_inode;
+ tid_t j_tail_sequence;
+ tid_t j_transaction_sequence;
+ __u8 j_uuid[16];
+ struct jbd_revoke_table_s *j_revoke;
+};
+
+typedef struct journal_s journal_t;
+
+extern int journal_recover (journal_t *journal);
+extern int journal_skip_recovery (journal_t *);
+
+/* Primary revoke support */
+extern int journal_init_revoke(journal_t *, int);
+extern void journal_destroy_revoke_caches(void);
+extern int journal_init_revoke_caches(void);
+
+/* Recovery revoke support */
+extern int journal_set_revoke(journal_t *, unsigned long, tid_t);
+extern int journal_test_revoke(journal_t *, unsigned long, tid_t);
+extern void journal_clear_revoke(journal_t *);
+extern void journal_brelse_array(struct buffer_head *b[], int n);
+
+extern void journal_destroy_revoke(journal_t *);
+
+
+#endif /* _LINUX_JBD_H */
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/kernel-list.h b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/kernel-list.h
new file mode 100644
index 0000000..3392596
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/kernel-list.h
@@ -0,0 +1,113 @@
+/* vi: set sw=4 ts=4: */
+#ifndef _LINUX_LIST_H
+#define _LINUX_LIST_H
+
+/*
+ * Simple doubly linked list implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+struct list_head {
+ struct list_head *next, *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+ struct list_head name = { &name, &name }
+
+#define INIT_LIST_HEAD(ptr) do { \
+ (ptr)->next = (ptr); (ptr)->prev = (ptr); \
+} while (0)
+
+#if (!defined(__GNUC__) && !defined(__WATCOMC__))
+#define __inline__
+#endif
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static __inline__ void __list_add(struct list_head * new,
+ struct list_head * prev,
+ struct list_head * next)
+{
+ next->prev = new;
+ new->next = next;
+ new->prev = prev;
+ prev->next = new;
+}
+
+/*
+ * Insert a new entry after the specified head..
+ */
+static __inline__ void list_add(struct list_head *new, struct list_head *head)
+{
+ __list_add(new, head, head->next);
+}
+
+/*
+ * Insert a new entry at the tail
+ */
+static __inline__ void list_add_tail(struct list_head *new, struct list_head *head)
+{
+ __list_add(new, head->prev, head);
+}
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static __inline__ void __list_del(struct list_head * prev,
+ struct list_head * next)
+{
+ next->prev = prev;
+ prev->next = next;
+}
+
+static __inline__ void list_del(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+}
+
+static __inline__ int list_empty(struct list_head *head)
+{
+ return head->next == head;
+}
+
+/*
+ * Splice in "list" into "head"
+ */
+static __inline__ void list_splice(struct list_head *list, struct list_head *head)
+{
+ struct list_head *first = list->next;
+
+ if (first != list) {
+ struct list_head *last = list->prev;
+ struct list_head *at = head->next;
+
+ first->prev = head;
+ head->next = first;
+
+ last->next = at;
+ at->prev = last;
+ }
+}
+
+#define list_entry(ptr, type, member) \
+ ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
+
+#define list_for_each(pos, head) \
+ for (pos = (head)->next; pos != (head); pos = pos->next)
+
+#endif
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/link.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/link.c
new file mode 100644
index 0000000..08b2e96
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/link.c
@@ -0,0 +1,135 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * link.c --- create links in a ext2fs directory
+ *
+ * Copyright (C) 1993, 1994 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+struct link_struct {
+ const char *name;
+ int namelen;
+ ext2_ino_t inode;
+ int flags;
+ int done;
+ struct ext2_super_block *sb;
+};
+
+static int link_proc(struct ext2_dir_entry *dirent,
+ int offset,
+ int blocksize,
+ char *buf,
+ void *priv_data)
+{
+ struct link_struct *ls = (struct link_struct *) priv_data;
+ struct ext2_dir_entry *next;
+ int rec_len, min_rec_len;
+ int ret = 0;
+
+ rec_len = EXT2_DIR_REC_LEN(ls->namelen);
+
+ /*
+ * See if the following directory entry (if any) is unused;
+ * if so, absorb it into this one.
+ */
+ next = (struct ext2_dir_entry *) (buf + offset + dirent->rec_len);
+ if ((offset + dirent->rec_len < blocksize - 8) &&
+ (next->inode == 0) &&
+ (offset + dirent->rec_len + next->rec_len <= blocksize)) {
+ dirent->rec_len += next->rec_len;
+ ret = DIRENT_CHANGED;
+ }
+
+ /*
+ * If the directory entry is used, see if we can split the
+ * directory entry to make room for the new name. If so,
+ * truncate it and return.
+ */
+ if (dirent->inode) {
+ min_rec_len = EXT2_DIR_REC_LEN(dirent->name_len & 0xFF);
+ if (dirent->rec_len < (min_rec_len + rec_len))
+ return ret;
+ rec_len = dirent->rec_len - min_rec_len;
+ dirent->rec_len = min_rec_len;
+ next = (struct ext2_dir_entry *) (buf + offset +
+ dirent->rec_len);
+ next->inode = 0;
+ next->name_len = 0;
+ next->rec_len = rec_len;
+ return DIRENT_CHANGED;
+ }
+
+ /*
+ * If we get this far, then the directory entry is not used.
+ * See if we can fit the request entry in. If so, do it.
+ */
+ if (dirent->rec_len < rec_len)
+ return ret;
+ dirent->inode = ls->inode;
+ dirent->name_len = ls->namelen;
+ strncpy(dirent->name, ls->name, ls->namelen);
+ if (ls->sb->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE)
+ dirent->name_len |= (ls->flags & 0x7) << 8;
+
+ ls->done++;
+ return DIRENT_ABORT|DIRENT_CHANGED;
+}
+
+/*
+ * Note: the low 3 bits of the flags field are used as the directory
+ * entry filetype.
+ */
+#ifdef __TURBOC__
+# pragma argsused
+#endif
+errcode_t ext2fs_link(ext2_filsys fs, ext2_ino_t dir, const char *name,
+ ext2_ino_t ino, int flags)
+{
+ errcode_t retval;
+ struct link_struct ls;
+ struct ext2_inode inode;
+
+ EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+ if (!(fs->flags & EXT2_FLAG_RW))
+ return EXT2_ET_RO_FILSYS;
+
+ ls.name = name;
+ ls.namelen = name ? strlen(name) : 0;
+ ls.inode = ino;
+ ls.flags = flags;
+ ls.done = 0;
+ ls.sb = fs->super;
+
+ retval = ext2fs_dir_iterate(fs, dir, DIRENT_FLAG_INCLUDE_EMPTY,
+ 0, link_proc, &ls);
+ if (retval)
+ return retval;
+
+ if (!ls.done)
+ return EXT2_ET_DIR_NO_SPACE;
+
+ if ((retval = ext2fs_read_inode(fs, dir, &inode)) != 0)
+ return retval;
+
+ if (inode.i_flags & EXT2_INDEX_FL) {
+ inode.i_flags &= ~EXT2_INDEX_FL;
+ if ((retval = ext2fs_write_inode(fs, dir, &inode)) != 0)
+ return retval;
+ }
+
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/lookup.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/lookup.c
new file mode 100644
index 0000000..31b30a1
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/lookup.c
@@ -0,0 +1,70 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * lookup.c --- ext2fs directory lookup operations
+ *
+ * Copyright (C) 1993, 1994, 1994, 1995 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+struct lookup_struct {
+ const char *name;
+ int len;
+ ext2_ino_t *inode;
+ int found;
+};
+
+#ifdef __TURBOC__
+# pragma argsused
+#endif
+static int lookup_proc(struct ext2_dir_entry *dirent,
+ int offset EXT2FS_ATTR((unused)),
+ int blocksize EXT2FS_ATTR((unused)),
+ char *buf EXT2FS_ATTR((unused)),
+ void *priv_data)
+{
+ struct lookup_struct *ls = (struct lookup_struct *) priv_data;
+
+ if (ls->len != (dirent->name_len & 0xFF))
+ return 0;
+ if (strncmp(ls->name, dirent->name, (dirent->name_len & 0xFF)))
+ return 0;
+ *ls->inode = dirent->inode;
+ ls->found++;
+ return DIRENT_ABORT;
+}
+
+
+errcode_t ext2fs_lookup(ext2_filsys fs, ext2_ino_t dir, const char *name,
+ int namelen, char *buf, ext2_ino_t *inode)
+{
+ errcode_t retval;
+ struct lookup_struct ls;
+
+ EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+ ls.name = name;
+ ls.len = namelen;
+ ls.inode = inode;
+ ls.found = 0;
+
+ retval = ext2fs_dir_iterate(fs, dir, 0, buf, lookup_proc, &ls);
+ if (retval)
+ return retval;
+
+ return (ls.found) ? 0 : EXT2_ET_FILE_NOT_FOUND;
+}
+
+
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/mkdir.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/mkdir.c
new file mode 100644
index 0000000..b63c5d7
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/mkdir.c
@@ -0,0 +1,142 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * mkdir.c --- make a directory in the filesystem
+ *
+ * Copyright (C) 1994, 1995 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+#ifndef EXT2_FT_DIR
+#define EXT2_FT_DIR 2
+#endif
+
+errcode_t ext2fs_mkdir(ext2_filsys fs, ext2_ino_t parent, ext2_ino_t inum,
+ const char *name)
+{
+ errcode_t retval;
+ struct ext2_inode parent_inode, inode;
+ ext2_ino_t ino = inum;
+ ext2_ino_t scratch_ino;
+ blk_t blk;
+ char *block = 0;
+
+ EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+ /*
+ * Allocate an inode, if necessary
+ */
+ if (!ino) {
+ retval = ext2fs_new_inode(fs, parent, LINUX_S_IFDIR | 0755,
+ 0, &ino);
+ if (retval)
+ goto cleanup;
+ }
+
+ /*
+ * Allocate a data block for the directory
+ */
+ retval = ext2fs_new_block(fs, 0, 0, &blk);
+ if (retval)
+ goto cleanup;
+
+ /*
+ * Create a scratch template for the directory
+ */
+ retval = ext2fs_new_dir_block(fs, ino, parent, &block);
+ if (retval)
+ goto cleanup;
+
+ /*
+ * Get the parent's inode, if necessary
+ */
+ if (parent != ino) {
+ retval = ext2fs_read_inode(fs, parent, &parent_inode);
+ if (retval)
+ goto cleanup;
+ } else
+ memset(&parent_inode, 0, sizeof(parent_inode));
+
+ /*
+ * Create the inode structure....
+ */
+ memset(&inode, 0, sizeof(struct ext2_inode));
+ inode.i_mode = LINUX_S_IFDIR | (0777 & ~fs->umask);
+ inode.i_uid = inode.i_gid = 0;
+ inode.i_blocks = fs->blocksize / 512;
+ inode.i_block[0] = blk;
+ inode.i_links_count = 2;
+ inode.i_ctime = inode.i_atime = inode.i_mtime = time(NULL);
+ inode.i_size = fs->blocksize;
+
+ /*
+ * Write out the inode and inode data block
+ */
+ retval = ext2fs_write_dir_block(fs, blk, block);
+ if (retval)
+ goto cleanup;
+ retval = ext2fs_write_new_inode(fs, ino, &inode);
+ if (retval)
+ goto cleanup;
+
+ /*
+ * Link the directory into the filesystem hierarchy
+ */
+ if (name) {
+ retval = ext2fs_lookup(fs, parent, name, strlen(name), 0,
+ &scratch_ino);
+ if (!retval) {
+ retval = EXT2_ET_DIR_EXISTS;
+ name = 0;
+ goto cleanup;
+ }
+ if (retval != EXT2_ET_FILE_NOT_FOUND)
+ goto cleanup;
+ retval = ext2fs_link(fs, parent, name, ino, EXT2_FT_DIR);
+ if (retval)
+ goto cleanup;
+ }
+
+ /*
+ * Update parent inode's counts
+ */
+ if (parent != ino) {
+ parent_inode.i_links_count++;
+ retval = ext2fs_write_inode(fs, parent, &parent_inode);
+ if (retval)
+ goto cleanup;
+ }
+
+ /*
+ * Update accounting....
+ */
+ ext2fs_block_alloc_stats(fs, blk, +1);
+ ext2fs_inode_alloc_stats2(fs, ino, +1, 1);
+
+cleanup:
+ ext2fs_free_mem(&block);
+ return retval;
+
+}
+
+
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/mkjournal.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/mkjournal.c
new file mode 100644
index 0000000..5bdd346
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/mkjournal.c
@@ -0,0 +1,428 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * mkjournal.c --- make a journal for a filesystem
+ *
+ * Copyright (C) 2000 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#if HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#if HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+#include "ext2_fs.h"
+#include "../e2p/e2p.h"
+#include "../e2fsck.h"
+#include "ext2fs.h"
+#include "kernel-jbd.h"
+
+/*
+ * This function automatically sets up the journal superblock and
+ * returns it as an allocated block.
+ */
+errcode_t ext2fs_create_journal_superblock(ext2_filsys fs,
+ __u32 size, int flags,
+ char **ret_jsb)
+{
+ errcode_t retval;
+ journal_superblock_t *jsb;
+
+ if (size < 1024)
+ return EXT2_ET_JOURNAL_TOO_SMALL;
+
+ if ((retval = ext2fs_get_mem(fs->blocksize, &jsb)))
+ return retval;
+
+ memset (jsb, 0, fs->blocksize);
+
+ jsb->s_header.h_magic = htonl(JFS_MAGIC_NUMBER);
+ if (flags & EXT2_MKJOURNAL_V1_SUPER)
+ jsb->s_header.h_blocktype = htonl(JFS_SUPERBLOCK_V1);
+ else
+ jsb->s_header.h_blocktype = htonl(JFS_SUPERBLOCK_V2);
+ jsb->s_blocksize = htonl(fs->blocksize);
+ jsb->s_maxlen = htonl(size);
+ jsb->s_nr_users = htonl(1);
+ jsb->s_first = htonl(1);
+ jsb->s_sequence = htonl(1);
+ memcpy(jsb->s_uuid, fs->super->s_uuid, sizeof(fs->super->s_uuid));
+ /*
+ * If we're creating an external journal device, we need to
+ * adjust these fields.
+ */
+ if (fs->super->s_feature_incompat &
+ EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) {
+ jsb->s_nr_users = 0;
+ if (fs->blocksize == 1024)
+ jsb->s_first = htonl(3);
+ else
+ jsb->s_first = htonl(2);
+ }
+
+ *ret_jsb = (char *) jsb;
+ return 0;
+}
+
+/*
+ * This function writes a journal using POSIX routines. It is used
+ * for creating external journals and creating journals on live
+ * filesystems.
+ */
+static errcode_t write_journal_file(ext2_filsys fs, char *filename,
+ blk_t size, int flags)
+{
+ errcode_t retval;
+ char *buf = 0;
+ int fd, ret_size;
+ blk_t i;
+
+ if ((retval = ext2fs_create_journal_superblock(fs, size, flags, &buf)))
+ return retval;
+
+ /* Open the device or journal file */
+ if ((fd = open(filename, O_WRONLY)) < 0) {
+ retval = errno;
+ goto errout;
+ }
+
+ /* Write the superblock out */
+ retval = EXT2_ET_SHORT_WRITE;
+ ret_size = write(fd, buf, fs->blocksize);
+ if (ret_size < 0) {
+ retval = errno;
+ goto errout;
+ }
+ if (ret_size != (int) fs->blocksize)
+ goto errout;
+ memset(buf, 0, fs->blocksize);
+
+ for (i = 1; i < size; i++) {
+ ret_size = write(fd, buf, fs->blocksize);
+ if (ret_size < 0) {
+ retval = errno;
+ goto errout;
+ }
+ if (ret_size != (int) fs->blocksize)
+ goto errout;
+ }
+ close(fd);
+
+ retval = 0;
+errout:
+ ext2fs_free_mem(&buf);
+ return retval;
+}
+
+/*
+ * Helper function for creating the journal using direct I/O routines
+ */
+struct mkjournal_struct {
+ int num_blocks;
+ int newblocks;
+ char *buf;
+ errcode_t err;
+};
+
+static int mkjournal_proc(ext2_filsys fs,
+ blk_t *blocknr,
+ e2_blkcnt_t blockcnt,
+ blk_t ref_block EXT2FS_ATTR((unused)),
+ int ref_offset EXT2FS_ATTR((unused)),
+ void *priv_data)
+{
+ struct mkjournal_struct *es = (struct mkjournal_struct *) priv_data;
+ blk_t new_blk;
+ static blk_t last_blk = 0;
+ errcode_t retval;
+
+ if (*blocknr) {
+ last_blk = *blocknr;
+ return 0;
+ }
+ retval = ext2fs_new_block(fs, last_blk, 0, &new_blk);
+ if (retval) {
+ es->err = retval;
+ return BLOCK_ABORT;
+ }
+ if (blockcnt > 0)
+ es->num_blocks--;
+
+ es->newblocks++;
+ retval = io_channel_write_blk(fs->io, new_blk, 1, es->buf);
+
+ if (blockcnt == 0)
+ memset(es->buf, 0, fs->blocksize);
+
+ if (retval) {
+ es->err = retval;
+ return BLOCK_ABORT;
+ }
+ *blocknr = new_blk;
+ last_blk = new_blk;
+ ext2fs_block_alloc_stats(fs, new_blk, +1);
+
+ if (es->num_blocks == 0)
+ return (BLOCK_CHANGED | BLOCK_ABORT);
+ else
+ return BLOCK_CHANGED;
+
+}
+
+/*
+ * This function creates a journal using direct I/O routines.
+ */
+static errcode_t write_journal_inode(ext2_filsys fs, ext2_ino_t journal_ino,
+ blk_t size, int flags)
+{
+ char *buf;
+ errcode_t retval;
+ struct ext2_inode inode;
+ struct mkjournal_struct es;
+
+ if ((retval = ext2fs_create_journal_superblock(fs, size, flags, &buf)))
+ return retval;
+
+ if ((retval = ext2fs_read_bitmaps(fs)))
+ return retval;
+
+ if ((retval = ext2fs_read_inode(fs, journal_ino, &inode)))
+ return retval;
+
+ if (inode.i_blocks > 0)
+ return EEXIST;
+
+ es.num_blocks = size;
+ es.newblocks = 0;
+ es.buf = buf;
+ es.err = 0;
+
+ retval = ext2fs_block_iterate2(fs, journal_ino, BLOCK_FLAG_APPEND,
+ 0, mkjournal_proc, &es);
+ if (es.err) {
+ retval = es.err;
+ goto errout;
+ }
+
+ if ((retval = ext2fs_read_inode(fs, journal_ino, &inode)))
+ goto errout;
+
+ inode.i_size += fs->blocksize * size;
+ inode.i_blocks += (fs->blocksize / 512) * es.newblocks;
+ inode.i_mtime = inode.i_ctime = time(0);
+ inode.i_links_count = 1;
+ inode.i_mode = LINUX_S_IFREG | 0600;
+
+ if ((retval = ext2fs_write_inode(fs, journal_ino, &inode)))
+ goto errout;
+ retval = 0;
+
+ memcpy(fs->super->s_jnl_blocks, inode.i_block, EXT2_N_BLOCKS*4);
+ fs->super->s_jnl_blocks[16] = inode.i_size;
+ fs->super->s_jnl_backup_type = EXT3_JNL_BACKUP_BLOCKS;
+ ext2fs_mark_super_dirty(fs);
+
+errout:
+ ext2fs_free_mem(&buf);
+ return retval;
+}
+
+/*
+ * This function adds a journal device to a filesystem
+ */
+errcode_t ext2fs_add_journal_device(ext2_filsys fs, ext2_filsys journal_dev)
+{
+ struct stat st;
+ errcode_t retval;
+ char buf[1024];
+ journal_superblock_t *jsb;
+ int start;
+ __u32 i, nr_users;
+
+ /* Make sure the device exists and is a block device */
+ if (stat(journal_dev->device_name, &st) < 0)
+ return errno;
+
+ if (!S_ISBLK(st.st_mode))
+ return EXT2_ET_JOURNAL_NOT_BLOCK; /* Must be a block device */
+
+ /* Get the journal superblock */
+ start = 1;
+ if (journal_dev->blocksize == 1024)
+ start++;
+ if ((retval = io_channel_read_blk(journal_dev->io, start, -1024, buf)))
+ return retval;
+
+ jsb = (journal_superblock_t *) buf;
+ if ((jsb->s_header.h_magic != (unsigned) ntohl(JFS_MAGIC_NUMBER)) ||
+ (jsb->s_header.h_blocktype != (unsigned) ntohl(JFS_SUPERBLOCK_V2)))
+ return EXT2_ET_NO_JOURNAL_SB;
+
+ if (ntohl(jsb->s_blocksize) != (unsigned long) fs->blocksize)
+ return EXT2_ET_UNEXPECTED_BLOCK_SIZE;
+
+ /* Check and see if this filesystem has already been added */
+ nr_users = ntohl(jsb->s_nr_users);
+ for (i=0; i < nr_users; i++) {
+ if (memcmp(fs->super->s_uuid,
+ &jsb->s_users[i*16], 16) == 0)
+ break;
+ }
+ if (i >= nr_users) {
+ memcpy(&jsb->s_users[nr_users*16],
+ fs->super->s_uuid, 16);
+ jsb->s_nr_users = htonl(nr_users+1);
+ }
+
+ /* Writeback the journal superblock */
+ if ((retval = io_channel_write_blk(journal_dev->io, start, -1024, buf)))
+ return retval;
+
+ fs->super->s_journal_inum = 0;
+ fs->super->s_journal_dev = st.st_rdev;
+ memcpy(fs->super->s_journal_uuid, jsb->s_uuid,
+ sizeof(fs->super->s_journal_uuid));
+ fs->super->s_feature_compat |= EXT3_FEATURE_COMPAT_HAS_JOURNAL;
+ ext2fs_mark_super_dirty(fs);
+ return 0;
+}
+
+/*
+ * This function adds a journal inode to a filesystem, using either
+ * POSIX routines if the filesystem is mounted, or using direct I/O
+ * functions if it is not.
+ */
+errcode_t ext2fs_add_journal_inode(ext2_filsys fs, blk_t size, int flags)
+{
+ errcode_t retval;
+ ext2_ino_t journal_ino;
+ struct stat st;
+ char jfile[1024];
+ int fd, mount_flags, f;
+
+ retval = ext2fs_check_mount_point(fs->device_name, &mount_flags,
+ jfile, sizeof(jfile)-10);
+ if (retval)
+ return retval;
+
+ if (mount_flags & EXT2_MF_MOUNTED) {
+ strcat(jfile, "/.journal");
+
+ /*
+ * If .../.journal already exists, make sure any
+ * immutable or append-only flags are cleared.
+ */
+#if defined(HAVE_CHFLAGS) && defined(UF_NODUMP)
+ (void) chflags (jfile, 0);
+#else
+#if HAVE_EXT2_IOCTLS
+ fd = open(jfile, O_RDONLY);
+ if (fd >= 0) {
+ f = 0;
+ ioctl(fd, EXT2_IOC_SETFLAGS, &f);
+ close(fd);
+ }
+#endif
+#endif
+
+ /* Create the journal file */
+ if ((fd = open(jfile, O_CREAT|O_WRONLY, 0600)) < 0)
+ return errno;
+
+ if ((retval = write_journal_file(fs, jfile, size, flags)))
+ goto errout;
+
+ /* Get inode number of the journal file */
+ if (fstat(fd, &st) < 0)
+ goto errout;
+
+#if defined(HAVE_CHFLAGS) && defined(UF_NODUMP)
+ retval = fchflags (fd, UF_NODUMP|UF_IMMUTABLE);
+#else
+#if HAVE_EXT2_IOCTLS
+ f = EXT2_NODUMP_FL | EXT2_IMMUTABLE_FL;
+ retval = ioctl(fd, EXT2_IOC_SETFLAGS, &f);
+#endif
+#endif
+ if (retval)
+ goto errout;
+
+ close(fd);
+ journal_ino = st.st_ino;
+ } else {
+ journal_ino = EXT2_JOURNAL_INO;
+ if ((retval = write_journal_inode(fs, journal_ino,
+ size, flags)))
+ return retval;
+ }
+
+ fs->super->s_journal_inum = journal_ino;
+ fs->super->s_journal_dev = 0;
+ memset(fs->super->s_journal_uuid, 0,
+ sizeof(fs->super->s_journal_uuid));
+ fs->super->s_feature_compat |= EXT3_FEATURE_COMPAT_HAS_JOURNAL;
+
+ ext2fs_mark_super_dirty(fs);
+ return 0;
+errout:
+ close(fd);
+ return retval;
+}
+
+#ifdef DEBUG
+main(int argc, char **argv)
+{
+ errcode_t retval;
+ char *device_name;
+ ext2_filsys fs;
+
+ if (argc < 2) {
+ fprintf(stderr, "Usage: %s filesystem\n", argv[0]);
+ exit(1);
+ }
+ device_name = argv[1];
+
+ retval = ext2fs_open (device_name, EXT2_FLAG_RW, 0, 0,
+ unix_io_manager, &fs);
+ if (retval) {
+ com_err(argv[0], retval, "while opening %s", device_name);
+ exit(1);
+ }
+
+ retval = ext2fs_add_journal_inode(fs, 1024);
+ if (retval) {
+ com_err(argv[0], retval, "while adding journal to %s",
+ device_name);
+ exit(1);
+ }
+ retval = ext2fs_flush(fs);
+ if (retval) {
+ printf("Warning, had trouble writing out superblocks.\n");
+ }
+ ext2fs_close(fs);
+ exit(0);
+
+}
+#endif
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/namei.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/namei.c
new file mode 100644
index 0000000..9889670
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/namei.c
@@ -0,0 +1,205 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * namei.c --- ext2fs directory lookup operations
+ *
+ * Copyright (C) 1993, 1994, 1994, 1995 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+/* #define NAMEI_DEBUG */
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+static errcode_t open_namei(ext2_filsys fs, ext2_ino_t root, ext2_ino_t base,
+ const char *pathname, size_t pathlen, int follow,
+ int link_count, char *buf, ext2_ino_t *res_inode);
+
+static errcode_t follow_link(ext2_filsys fs, ext2_ino_t root, ext2_ino_t dir,
+ ext2_ino_t inode, int link_count,
+ char *buf, ext2_ino_t *res_inode)
+{
+ char *pathname;
+ char *buffer = 0;
+ errcode_t retval;
+ struct ext2_inode ei;
+
+#ifdef NAMEI_DEBUG
+ printf("follow_link: root=%lu, dir=%lu, inode=%lu, lc=%d\n",
+ root, dir, inode, link_count);
+
+#endif
+ retval = ext2fs_read_inode (fs, inode, &ei);
+ if (retval) return retval;
+ if (!LINUX_S_ISLNK (ei.i_mode)) {
+ *res_inode = inode;
+ return 0;
+ }
+ if (link_count++ > 5) {
+ return EXT2_ET_SYMLINK_LOOP;
+ }
+ if (ext2fs_inode_data_blocks(fs,&ei)) {
+ retval = ext2fs_get_mem(fs->blocksize, &buffer);
+ if (retval)
+ return retval;
+ retval = io_channel_read_blk(fs->io, ei.i_block[0], 1, buffer);
+ if (retval) {
+ ext2fs_free_mem(&buffer);
+ return retval;
+ }
+ pathname = buffer;
+ } else
+ pathname = (char *)&(ei.i_block[0]);
+ retval = open_namei(fs, root, dir, pathname, ei.i_size, 1,
+ link_count, buf, res_inode);
+ ext2fs_free_mem(&buffer);
+ return retval;
+}
+
+/*
+ * This routine interprets a pathname in the context of the current
+ * directory and the root directory, and returns the inode of the
+ * containing directory, and a pointer to the filename of the file
+ * (pointing into the pathname) and the length of the filename.
+ */
+static errcode_t dir_namei(ext2_filsys fs, ext2_ino_t root, ext2_ino_t dir,
+ const char *pathname, int pathlen,
+ int link_count, char *buf,
+ const char **name, int *namelen,
+ ext2_ino_t *res_inode)
+{
+ char c;
+ const char *thisname;
+ int len;
+ ext2_ino_t inode;
+ errcode_t retval;
+
+ if ((c = *pathname) == '/') {
+ dir = root;
+ pathname++;
+ pathlen--;
+ }
+ while (1) {
+ thisname = pathname;
+ for (len=0; --pathlen >= 0;len++) {
+ c = *(pathname++);
+ if (c == '/')
+ break;
+ }
+ if (pathlen < 0)
+ break;
+ retval = ext2fs_lookup (fs, dir, thisname, len, buf, &inode);
+ if (retval) return retval;
+ retval = follow_link (fs, root, dir, inode,
+ link_count, buf, &dir);
+ if (retval) return retval;
+ }
+ *name = thisname;
+ *namelen = len;
+ *res_inode = dir;
+ return 0;
+}
+
+static errcode_t open_namei(ext2_filsys fs, ext2_ino_t root, ext2_ino_t base,
+ const char *pathname, size_t pathlen, int follow,
+ int link_count, char *buf, ext2_ino_t *res_inode)
+{
+ const char *basename;
+ int namelen;
+ ext2_ino_t dir, inode;
+ errcode_t retval;
+
+#ifdef NAMEI_DEBUG
+ printf("open_namei: root=%lu, dir=%lu, path=%*s, lc=%d\n",
+ root, base, pathlen, pathname, link_count);
+#endif
+ retval = dir_namei(fs, root, base, pathname, pathlen,
+ link_count, buf, &basename, &namelen, &dir);
+ if (retval) return retval;
+ if (!namelen) { /* special case: '/usr/' etc */
+ *res_inode=dir;
+ return 0;
+ }
+ retval = ext2fs_lookup (fs, dir, basename, namelen, buf, &inode);
+ if (retval)
+ return retval;
+ if (follow) {
+ retval = follow_link(fs, root, dir, inode, link_count,
+ buf, &inode);
+ if (retval)
+ return retval;
+ }
+#ifdef NAMEI_DEBUG
+ printf("open_namei: (link_count=%d) returns %lu\n",
+ link_count, inode);
+#endif
+ *res_inode = inode;
+ return 0;
+}
+
+errcode_t ext2fs_namei(ext2_filsys fs, ext2_ino_t root, ext2_ino_t cwd,
+ const char *name, ext2_ino_t *inode)
+{
+ char *buf;
+ errcode_t retval;
+
+ EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+ retval = ext2fs_get_mem(fs->blocksize, &buf);
+ if (retval)
+ return retval;
+
+ retval = open_namei(fs, root, cwd, name, strlen(name), 0, 0,
+ buf, inode);
+
+ ext2fs_free_mem(&buf);
+ return retval;
+}
+
+errcode_t ext2fs_namei_follow(ext2_filsys fs, ext2_ino_t root, ext2_ino_t cwd,
+ const char *name, ext2_ino_t *inode)
+{
+ char *buf;
+ errcode_t retval;
+
+ EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+ retval = ext2fs_get_mem(fs->blocksize, &buf);
+ if (retval)
+ return retval;
+
+ retval = open_namei(fs, root, cwd, name, strlen(name), 1, 0,
+ buf, inode);
+
+ ext2fs_free_mem(&buf);
+ return retval;
+}
+
+errcode_t ext2fs_follow_link(ext2_filsys fs, ext2_ino_t root, ext2_ino_t cwd,
+ ext2_ino_t inode, ext2_ino_t *res_inode)
+{
+ char *buf;
+ errcode_t retval;
+
+ EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+ retval = ext2fs_get_mem(fs->blocksize, &buf);
+ if (retval)
+ return retval;
+
+ retval = follow_link(fs, root, cwd, inode, 0, buf, res_inode);
+
+ ext2fs_free_mem(&buf);
+ return retval;
+}
+
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/newdir.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/newdir.c
new file mode 100644
index 0000000..9470e7f
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/newdir.c
@@ -0,0 +1,73 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * newdir.c --- create a new directory block
+ *
+ * Copyright (C) 1994, 1995 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+#ifndef EXT2_FT_DIR
+#define EXT2_FT_DIR 2
+#endif
+
+/*
+ * Create new directory block
+ */
+errcode_t ext2fs_new_dir_block(ext2_filsys fs, ext2_ino_t dir_ino,
+ ext2_ino_t parent_ino, char **block)
+{
+ struct ext2_dir_entry *dir = NULL;
+ errcode_t retval;
+ char *buf;
+ int rec_len;
+ int filetype = 0;
+
+ EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+ retval = ext2fs_get_mem(fs->blocksize, &buf);
+ if (retval)
+ return retval;
+ memset(buf, 0, fs->blocksize);
+ dir = (struct ext2_dir_entry *) buf;
+ dir->rec_len = fs->blocksize;
+
+ if (dir_ino) {
+ if (fs->super->s_feature_incompat &
+ EXT2_FEATURE_INCOMPAT_FILETYPE)
+ filetype = EXT2_FT_DIR << 8;
+ /*
+ * Set up entry for '.'
+ */
+ dir->inode = dir_ino;
+ dir->name_len = 1 | filetype;
+ dir->name[0] = '.';
+ rec_len = dir->rec_len - EXT2_DIR_REC_LEN(1);
+ dir->rec_len = EXT2_DIR_REC_LEN(1);
+
+ /*
+ * Set up entry for '..'
+ */
+ dir = (struct ext2_dir_entry *) (buf + dir->rec_len);
+ dir->rec_len = rec_len;
+ dir->inode = parent_ino;
+ dir->name_len = 2 | filetype;
+ dir->name[0] = '.';
+ dir->name[1] = '.';
+
+ }
+ *block = buf;
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/openfs.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/openfs.c
new file mode 100644
index 0000000..716be06
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/openfs.c
@@ -0,0 +1,330 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * openfs.c --- open an ext2 filesystem
+ *
+ * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+
+
+#include "ext2fs.h"
+#include "e2image.h"
+
+blk_t ext2fs_descriptor_block_loc(ext2_filsys fs, blk_t group_block, dgrp_t i)
+{
+ int bg;
+ int has_super = 0;
+ int ret_blk;
+
+ if (!(fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) ||
+ (i < fs->super->s_first_meta_bg))
+ return (group_block + i + 1);
+
+ bg = (fs->blocksize / sizeof (struct ext2_group_desc)) * i;
+ if (ext2fs_bg_has_super(fs, bg))
+ has_super = 1;
+ ret_blk = (fs->super->s_first_data_block + has_super +
+ (bg * fs->super->s_blocks_per_group));
+ /*
+ * If group_block is not the normal value, we're trying to use
+ * the backup group descriptors and superblock --- so use the
+ * alternate location of the second block group in the
+ * metablock group. Ideally we should be testing each bg
+ * descriptor block individually for correctness, but we don't
+ * have the infrastructure in place to do that.
+ */
+ if (group_block != fs->super->s_first_data_block &&
+ ((ret_blk + fs->super->s_blocks_per_group) <
+ fs->super->s_blocks_count))
+ ret_blk += fs->super->s_blocks_per_group;
+ return ret_blk;
+}
+
+errcode_t ext2fs_open(const char *name, int flags, int superblock,
+ unsigned int block_size, io_manager manager,
+ ext2_filsys *ret_fs)
+{
+ return ext2fs_open2(name, 0, flags, superblock, block_size,
+ manager, ret_fs);
+}
+
+/*
+ * Note: if superblock is non-zero, block-size must also be non-zero.
+ * Superblock and block_size can be zero to use the default size.
+ *
+ * Valid flags for ext2fs_open()
+ *
+ * EXT2_FLAG_RW - Open the filesystem for read/write.
+ * EXT2_FLAG_FORCE - Open the filesystem even if some of the
+ * features aren't supported.
+ * EXT2_FLAG_JOURNAL_DEV_OK - Open an ext3 journal device
+ */
+errcode_t ext2fs_open2(const char *name, const char *io_options,
+ int flags, int superblock,
+ unsigned int block_size, io_manager manager,
+ ext2_filsys *ret_fs)
+{
+ ext2_filsys fs;
+ errcode_t retval;
+ unsigned long i;
+ int groups_per_block, blocks_per_group;
+ blk_t group_block, blk;
+ char *dest, *cp;
+#if BB_BIG_ENDIAN
+ int j;
+ struct ext2_group_desc *gdp;
+#endif
+
+ EXT2_CHECK_MAGIC(manager, EXT2_ET_MAGIC_IO_MANAGER);
+
+ retval = ext2fs_get_mem(sizeof(struct struct_ext2_filsys), &fs);
+ if (retval)
+ return retval;
+
+ memset(fs, 0, sizeof(struct struct_ext2_filsys));
+ fs->magic = EXT2_ET_MAGIC_EXT2FS_FILSYS;
+ fs->flags = flags;
+ fs->umask = 022;
+ retval = ext2fs_get_mem(strlen(name)+1, &fs->device_name);
+ if (retval)
+ goto cleanup;
+ strcpy(fs->device_name, name);
+ cp = strchr(fs->device_name, '?');
+ if (!io_options && cp) {
+ *cp++ = 0;
+ io_options = cp;
+ }
+
+ retval = manager->open(fs->device_name,
+ (flags & EXT2_FLAG_RW) ? IO_FLAG_RW : 0,
+ &fs->io);
+ if (retval)
+ goto cleanup;
+ if (io_options &&
+ (retval = io_channel_set_options(fs->io, io_options)))
+ goto cleanup;
+ fs->image_io = fs->io;
+ fs->io->app_data = fs;
+ retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &fs->super);
+ if (retval)
+ goto cleanup;
+ if (flags & EXT2_FLAG_IMAGE_FILE) {
+ retval = ext2fs_get_mem(sizeof(struct ext2_image_hdr),
+ &fs->image_header);
+ if (retval)
+ goto cleanup;
+ retval = io_channel_read_blk(fs->io, 0,
+ -(int)sizeof(struct ext2_image_hdr),
+ fs->image_header);
+ if (retval)
+ goto cleanup;
+ if (fs->image_header->magic_number != EXT2_ET_MAGIC_E2IMAGE)
+ return EXT2_ET_MAGIC_E2IMAGE;
+ superblock = 1;
+ block_size = fs->image_header->fs_blocksize;
+ }
+
+ /*
+ * If the user specifies a specific block # for the
+ * superblock, then he/she must also specify the block size!
+ * Otherwise, read the master superblock located at offset
+ * SUPERBLOCK_OFFSET from the start of the partition.
+ *
+ * Note: we only save a backup copy of the superblock if we
+ * are reading the superblock from the primary superblock location.
+ */
+ if (superblock) {
+ if (!block_size) {
+ retval = EXT2_ET_INVALID_ARGUMENT;
+ goto cleanup;
+ }
+ io_channel_set_blksize(fs->io, block_size);
+ group_block = superblock;
+ fs->orig_super = 0;
+ } else {
+ io_channel_set_blksize(fs->io, SUPERBLOCK_OFFSET);
+ superblock = 1;
+ group_block = 0;
+ retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &fs->orig_super);
+ if (retval)
+ goto cleanup;
+ }
+ retval = io_channel_read_blk(fs->io, superblock, -SUPERBLOCK_SIZE,
+ fs->super);
+ if (retval)
+ goto cleanup;
+ if (fs->orig_super)
+ memcpy(fs->orig_super, fs->super, SUPERBLOCK_SIZE);
+
+#if BB_BIG_ENDIAN
+ if ((fs->super->s_magic == ext2fs_swab16(EXT2_SUPER_MAGIC)) ||
+ (fs->flags & EXT2_FLAG_SWAP_BYTES)) {
+ fs->flags |= EXT2_FLAG_SWAP_BYTES;
+
+ ext2fs_swap_super(fs->super);
+ }
+#endif
+
+ if (fs->super->s_magic != EXT2_SUPER_MAGIC) {
+ retval = EXT2_ET_BAD_MAGIC;
+ goto cleanup;
+ }
+ if (fs->super->s_rev_level > EXT2_LIB_CURRENT_REV) {
+ retval = EXT2_ET_REV_TOO_HIGH;
+ goto cleanup;
+ }
+
+ /*
+ * Check for feature set incompatibility
+ */
+ if (!(flags & EXT2_FLAG_FORCE)) {
+ if (fs->super->s_feature_incompat &
+ ~EXT2_LIB_FEATURE_INCOMPAT_SUPP) {
+ retval = EXT2_ET_UNSUPP_FEATURE;
+ goto cleanup;
+ }
+ if ((flags & EXT2_FLAG_RW) &&
+ (fs->super->s_feature_ro_compat &
+ ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP)) {
+ retval = EXT2_ET_RO_UNSUPP_FEATURE;
+ goto cleanup;
+ }
+ if (!(flags & EXT2_FLAG_JOURNAL_DEV_OK) &&
+ (fs->super->s_feature_incompat &
+ EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)) {
+ retval = EXT2_ET_UNSUPP_FEATURE;
+ goto cleanup;
+ }
+ }
+
+ fs->blocksize = EXT2_BLOCK_SIZE(fs->super);
+ if (fs->blocksize == 0) {
+ retval = EXT2_ET_CORRUPT_SUPERBLOCK;
+ goto cleanup;
+ }
+ fs->fragsize = EXT2_FRAG_SIZE(fs->super);
+ fs->inode_blocks_per_group = ((fs->super->s_inodes_per_group *
+ EXT2_INODE_SIZE(fs->super) +
+ EXT2_BLOCK_SIZE(fs->super) - 1) /
+ EXT2_BLOCK_SIZE(fs->super));
+ if (block_size) {
+ if (block_size != fs->blocksize) {
+ retval = EXT2_ET_UNEXPECTED_BLOCK_SIZE;
+ goto cleanup;
+ }
+ }
+ /*
+ * Set the blocksize to the filesystem's blocksize.
+ */
+ io_channel_set_blksize(fs->io, fs->blocksize);
+
+ /*
+ * If this is an external journal device, don't try to read
+ * the group descriptors, because they're not there.
+ */
+ if (fs->super->s_feature_incompat &
+ EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) {
+ fs->group_desc_count = 0;
+ *ret_fs = fs;
+ return 0;
+ }
+
+ /*
+ * Read group descriptors
+ */
+ blocks_per_group = EXT2_BLOCKS_PER_GROUP(fs->super);
+ if (blocks_per_group == 0 ||
+ blocks_per_group > EXT2_MAX_BLOCKS_PER_GROUP(fs->super) ||
+ fs->inode_blocks_per_group > EXT2_MAX_INODES_PER_GROUP(fs->super)) {
+ retval = EXT2_ET_CORRUPT_SUPERBLOCK;
+ goto cleanup;
+ }
+ fs->group_desc_count = (fs->super->s_blocks_count -
+ fs->super->s_first_data_block +
+ blocks_per_group - 1) / blocks_per_group;
+ fs->desc_blocks = (fs->group_desc_count +
+ EXT2_DESC_PER_BLOCK(fs->super) - 1)
+ / EXT2_DESC_PER_BLOCK(fs->super);
+ retval = ext2fs_get_mem(fs->desc_blocks * fs->blocksize,
+ &fs->group_desc);
+ if (retval)
+ goto cleanup;
+ if (!group_block)
+ group_block = fs->super->s_first_data_block;
+ dest = (char *) fs->group_desc;
+ groups_per_block = fs->blocksize / sizeof(struct ext2_group_desc);
+ for (i=0 ; i < fs->desc_blocks; i++) {
+ blk = ext2fs_descriptor_block_loc(fs, group_block, i);
+ retval = io_channel_read_blk(fs->io, blk, 1, dest);
+ if (retval)
+ goto cleanup;
+#if BB_BIG_ENDIAN
+ if (fs->flags & EXT2_FLAG_SWAP_BYTES) {
+ gdp = (struct ext2_group_desc *) dest;
+ for (j=0; j < groups_per_block; j++)
+ ext2fs_swap_group_desc(gdp++);
+ }
+#endif
+ dest += fs->blocksize;
+ }
+
+ *ret_fs = fs;
+ return 0;
+cleanup:
+ ext2fs_free(fs);
+ return retval;
+}
+
+/*
+ * Set/get the filesystem data I/O channel.
+ *
+ * These functions are only valid if EXT2_FLAG_IMAGE_FILE is true.
+ */
+errcode_t ext2fs_get_data_io(ext2_filsys fs, io_channel *old_io)
+{
+ if ((fs->flags & EXT2_FLAG_IMAGE_FILE) == 0)
+ return EXT2_ET_NOT_IMAGE_FILE;
+ if (old_io) {
+ *old_io = (fs->image_io == fs->io) ? 0 : fs->io;
+ }
+ return 0;
+}
+
+errcode_t ext2fs_set_data_io(ext2_filsys fs, io_channel new_io)
+{
+ if ((fs->flags & EXT2_FLAG_IMAGE_FILE) == 0)
+ return EXT2_ET_NOT_IMAGE_FILE;
+ fs->io = new_io ? new_io : fs->image_io;
+ return 0;
+}
+
+errcode_t ext2fs_rewrite_to_io(ext2_filsys fs, io_channel new_io)
+{
+ if ((fs->flags & EXT2_FLAG_IMAGE_FILE) == 0)
+ return EXT2_ET_NOT_IMAGE_FILE;
+ fs->io = fs->image_io = new_io;
+ fs->flags |= EXT2_FLAG_DIRTY | EXT2_FLAG_RW |
+ EXT2_FLAG_BB_DIRTY | EXT2_FLAG_IB_DIRTY;
+ fs->flags &= ~EXT2_FLAG_IMAGE_FILE;
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/read_bb.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/read_bb.c
new file mode 100644
index 0000000..4766157
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/read_bb.c
@@ -0,0 +1,98 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * read_bb --- read the bad blocks inode
+ *
+ * Copyright (C) 1994 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+struct read_bb_record {
+ ext2_badblocks_list bb_list;
+ errcode_t err;
+};
+
+/*
+ * Helper function for ext2fs_read_bb_inode()
+ */
+#ifdef __TURBOC__
+# pragma argsused
+#endif
+static int mark_bad_block(ext2_filsys fs, blk_t *block_nr,
+ e2_blkcnt_t blockcnt EXT2FS_ATTR((unused)),
+ blk_t ref_block EXT2FS_ATTR((unused)),
+ int ref_offset EXT2FS_ATTR((unused)),
+ void *priv_data)
+{
+ struct read_bb_record *rb = (struct read_bb_record *) priv_data;
+
+ if (blockcnt < 0)
+ return 0;
+
+ if ((*block_nr < fs->super->s_first_data_block) ||
+ (*block_nr >= fs->super->s_blocks_count))
+ return 0; /* Ignore illegal blocks */
+
+ rb->err = ext2fs_badblocks_list_add(rb->bb_list, *block_nr);
+ if (rb->err)
+ return BLOCK_ABORT;
+ return 0;
+}
+
+/*
+ * Reads the current bad blocks from the bad blocks inode.
+ */
+errcode_t ext2fs_read_bb_inode(ext2_filsys fs, ext2_badblocks_list *bb_list)
+{
+ errcode_t retval;
+ struct read_bb_record rb;
+ struct ext2_inode inode;
+ blk_t numblocks;
+
+ EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+ if (!*bb_list) {
+ retval = ext2fs_read_inode(fs, EXT2_BAD_INO, &inode);
+ if (retval)
+ return retval;
+ if (inode.i_blocks < 500)
+ numblocks = (inode.i_blocks /
+ (fs->blocksize / 512)) + 20;
+ else
+ numblocks = 500;
+ retval = ext2fs_badblocks_list_create(bb_list, numblocks);
+ if (retval)
+ return retval;
+ }
+
+ rb.bb_list = *bb_list;
+ rb.err = 0;
+ retval = ext2fs_block_iterate2(fs, EXT2_BAD_INO, 0, 0,
+ mark_bad_block, &rb);
+ if (retval)
+ return retval;
+
+ return rb.err;
+}
+
+
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/read_bb_file.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/read_bb_file.c
new file mode 100644
index 0000000..831adcc
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/read_bb_file.c
@@ -0,0 +1,98 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * read_bb_file.c --- read a list of bad blocks from a FILE *
+ *
+ * Copyright (C) 1994, 1995, 2000 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+/*
+ * Reads a list of bad blocks from a FILE *
+ */
+errcode_t ext2fs_read_bb_FILE2(ext2_filsys fs, FILE *f,
+ ext2_badblocks_list *bb_list,
+ void *priv_data,
+ void (*invalid)(ext2_filsys fs,
+ blk_t blk,
+ char *badstr,
+ void *priv_data))
+{
+ errcode_t retval;
+ blk_t blockno;
+ int count;
+ char buf[128];
+
+ if (fs)
+ EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+ if (!*bb_list) {
+ retval = ext2fs_badblocks_list_create(bb_list, 10);
+ if (retval)
+ return retval;
+ }
+
+ while (!feof (f)) {
+ if (fgets(buf, sizeof(buf), f) == NULL)
+ break;
+ count = sscanf(buf, "%u", &blockno);
+ if (count <= 0)
+ continue;
+ if (fs &&
+ ((blockno < fs->super->s_first_data_block) ||
+ (blockno >= fs->super->s_blocks_count))) {
+ if (invalid)
+ (invalid)(fs, blockno, buf, priv_data);
+ continue;
+ }
+ retval = ext2fs_badblocks_list_add(*bb_list, blockno);
+ if (retval)
+ return retval;
+ }
+ return 0;
+}
+
+static void call_compat_invalid(ext2_filsys fs, blk_t blk,
+ char *badstr EXT2FS_ATTR((unused)),
+ void *priv_data)
+{
+ void (*invalid)(ext2_filsys, blk_t);
+
+ invalid = (void (*)(ext2_filsys, blk_t)) priv_data;
+ if (invalid)
+ invalid(fs, blk);
+}
+
+
+/*
+ * Reads a list of bad blocks from a FILE *
+ */
+errcode_t ext2fs_read_bb_FILE(ext2_filsys fs, FILE *f,
+ ext2_badblocks_list *bb_list,
+ void (*invalid)(ext2_filsys fs, blk_t blk))
+{
+ return ext2fs_read_bb_FILE2(fs, f, bb_list, (void *) invalid,
+ call_compat_invalid);
+}
+
+
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/res_gdt.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/res_gdt.c
new file mode 100644
index 0000000..f7ee8b1
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/res_gdt.c
@@ -0,0 +1,221 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * res_gdt.c --- reserve blocks for growing the group descriptor table
+ * during online resizing.
+ *
+ * Copyright (C) 2002 Andreas Dilger
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+/*
+ * Iterate through the groups which hold BACKUP superblock/GDT copies in an
+ * ext3 filesystem. The counters should be initialized to 1, 5, and 7 before
+ * calling this for the first time. In a sparse filesystem it will be the
+ * sequence of powers of 3, 5, and 7: 1, 3, 5, 7, 9, 25, 27, 49, 81, ...
+ * For a non-sparse filesystem it will be every group: 1, 2, 3, 4, ...
+ */
+static unsigned int list_backups(ext2_filsys fs, unsigned int *three,
+ unsigned int *five, unsigned int *seven)
+{
+ unsigned int *min = three;
+ int mult = 3;
+ unsigned int ret;
+
+ if (!(fs->super->s_feature_ro_compat &
+ EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)) {
+ ret = *min;
+ *min += 1;
+ return ret;
+ }
+
+ if (*five < *min) {
+ min = five;
+ mult = 5;
+ }
+ if (*seven < *min) {
+ min = seven;
+ mult = 7;
+ }
+
+ ret = *min;
+ *min *= mult;
+
+ return ret;
+}
+
+/*
+ * This code assumes that the reserved blocks have already been marked in-use
+ * during ext2fs_initialize(), so that they are not allocated for other
+ * uses before we can add them to the resize inode (which has to come
+ * after the creation of the inode table).
+ */
+errcode_t ext2fs_create_resize_inode(ext2_filsys fs)
+{
+ errcode_t retval, retval2;
+ struct ext2_super_block *sb;
+ struct ext2_inode inode;
+ __u32 *dindir_buf, *gdt_buf;
+ int rsv_add;
+ unsigned long long apb, inode_size;
+ blk_t dindir_blk, rsv_off, gdt_off, gdt_blk;
+ int dindir_dirty = 0, inode_dirty = 0;
+
+ EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+ sb = fs->super;
+
+ retval = ext2fs_get_mem(2 * fs->blocksize, (void *)&dindir_buf);
+ if (retval)
+ goto out_free;
+ gdt_buf = (__u32 *)((char *)dindir_buf + fs->blocksize);
+
+ retval = ext2fs_read_inode(fs, EXT2_RESIZE_INO, &inode);
+ if (retval)
+ goto out_free;
+
+ /* Maximum possible file size (we donly use the dindirect blocks) */
+ apb = EXT2_ADDR_PER_BLOCK(sb);
+ rsv_add = fs->blocksize / 512;
+ if ((dindir_blk = inode.i_block[EXT2_DIND_BLOCK])) {
+#ifdef RES_GDT_DEBUG
+ printf("reading GDT dindir %u\n", dindir_blk);
+#endif
+ retval = ext2fs_read_ind_block(fs, dindir_blk, dindir_buf);
+ if (retval)
+ goto out_inode;
+ } else {
+ blk_t goal = 3 + sb->s_reserved_gdt_blocks +
+ fs->desc_blocks + fs->inode_blocks_per_group;
+
+ retval = ext2fs_alloc_block(fs, goal, 0, &dindir_blk);
+ if (retval)
+ goto out_free;
+ inode.i_mode = LINUX_S_IFREG | 0600;
+ inode.i_links_count = 1;
+ inode.i_block[EXT2_DIND_BLOCK] = dindir_blk;
+ inode.i_blocks = rsv_add;
+ memset(dindir_buf, 0, fs->blocksize);
+#ifdef RES_GDT_DEBUG
+ printf("allocated GDT dindir %u\n", dindir_blk);
+#endif
+ dindir_dirty = inode_dirty = 1;
+ inode_size = apb*apb + apb + EXT2_NDIR_BLOCKS;
+ inode_size *= fs->blocksize;
+ inode.i_size = inode_size & 0xFFFFFFFF;
+ inode.i_size_high = (inode_size >> 32) & 0xFFFFFFFF;
+ if(inode.i_size_high) {
+ sb->s_feature_ro_compat |=
+ EXT2_FEATURE_RO_COMPAT_LARGE_FILE;
+ }
+ inode.i_ctime = time(0);
+ }
+
+ for (rsv_off = 0, gdt_off = fs->desc_blocks,
+ gdt_blk = sb->s_first_data_block + 1 + fs->desc_blocks;
+ rsv_off < sb->s_reserved_gdt_blocks;
+ rsv_off++, gdt_off++, gdt_blk++) {
+ unsigned int three = 1, five = 5, seven = 7;
+ unsigned int grp, last = 0;
+ int gdt_dirty = 0;
+
+ gdt_off %= apb;
+ if (!dindir_buf[gdt_off]) {
+ /* FIXME XXX XXX
+ blk_t new_blk;
+
+ retval = ext2fs_new_block(fs, gdt_blk, 0, &new_blk);
+ if (retval)
+ goto out_free;
+ if (new_blk != gdt_blk) {
+ // XXX free block
+ retval = -1; // XXX
+ }
+ */
+ gdt_dirty = dindir_dirty = inode_dirty = 1;
+ memset(gdt_buf, 0, fs->blocksize);
+ dindir_buf[gdt_off] = gdt_blk;
+ inode.i_blocks += rsv_add;
+#ifdef RES_GDT_DEBUG
+ printf("added primary GDT block %u at %u[%u]\n",
+ gdt_blk, dindir_blk, gdt_off);
+#endif
+ } else if (dindir_buf[gdt_off] == gdt_blk) {
+#ifdef RES_GDT_DEBUG
+ printf("reading primary GDT block %u\n", gdt_blk);
+#endif
+ retval = ext2fs_read_ind_block(fs, gdt_blk, gdt_buf);
+ if (retval)
+ goto out_dindir;
+ } else {
+#ifdef RES_GDT_DEBUG
+ printf("bad primary GDT %u != %u at %u[%u]\n",
+ dindir_buf[gdt_off], gdt_blk,dindir_blk,gdt_off);
+#endif
+ retval = EXT2_ET_RESIZE_INODE_CORRUPT;
+ goto out_dindir;
+ }
+
+ while ((grp = list_backups(fs, &three, &five, &seven)) <
+ fs->group_desc_count) {
+ blk_t expect = gdt_blk + grp * sb->s_blocks_per_group;
+
+ if (!gdt_buf[last]) {
+#ifdef RES_GDT_DEBUG
+ printf("added backup GDT %u grp %u@%u[%u]\n",
+ expect, grp, gdt_blk, last);
+#endif
+ gdt_buf[last] = expect;
+ inode.i_blocks += rsv_add;
+ gdt_dirty = inode_dirty = 1;
+ } else if (gdt_buf[last] != expect) {
+#ifdef RES_GDT_DEBUG
+ printf("bad backup GDT %u != %u at %u[%u]\n",
+ gdt_buf[last], expect, gdt_blk, last);
+#endif
+ retval = EXT2_ET_RESIZE_INODE_CORRUPT;
+ goto out_dindir;
+ }
+ last++;
+ }
+ if (gdt_dirty) {
+#ifdef RES_GDT_DEBUG
+ printf("writing primary GDT block %u\n", gdt_blk);
+#endif
+ retval = ext2fs_write_ind_block(fs, gdt_blk, gdt_buf);
+ if (retval)
+ goto out_dindir;
+ }
+ }
+
+out_dindir:
+ if (dindir_dirty) {
+ retval2 = ext2fs_write_ind_block(fs, dindir_blk, dindir_buf);
+ if (!retval)
+ retval = retval2;
+ }
+out_inode:
+#ifdef RES_GDT_DEBUG
+ printf("inode.i_blocks = %u, i_size = %u\n", inode.i_blocks,
+ inode.i_size);
+#endif
+ if (inode_dirty) {
+ inode.i_atime = inode.i_mtime = time(0);
+ retval2 = ext2fs_write_inode(fs, EXT2_RESIZE_INO, &inode);
+ if (!retval)
+ retval = retval2;
+ }
+out_free:
+ ext2fs_free_mem((void *)&dindir_buf);
+ return retval;
+}
+
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/rs_bitmap.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/rs_bitmap.c
new file mode 100644
index 0000000..e932b3c
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/rs_bitmap.c
@@ -0,0 +1,107 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * rs_bitmap.c --- routine for changing the size of a bitmap
+ *
+ * Copyright (C) 1996, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+errcode_t ext2fs_resize_generic_bitmap(__u32 new_end, __u32 new_real_end,
+ ext2fs_generic_bitmap bmap)
+{
+ errcode_t retval;
+ size_t size, new_size;
+ __u32 bitno;
+
+ if (!bmap)
+ return EXT2_ET_INVALID_ARGUMENT;
+
+ EXT2_CHECK_MAGIC(bmap, EXT2_ET_MAGIC_GENERIC_BITMAP);
+
+ /*
+ * If we're expanding the bitmap, make sure all of the new
+ * parts of the bitmap are zero.
+ */
+ if (new_end > bmap->end) {
+ bitno = bmap->real_end;
+ if (bitno > new_end)
+ bitno = new_end;
+ for (; bitno > bmap->end; bitno--)
+ ext2fs_clear_bit(bitno - bmap->start, bmap->bitmap);
+ }
+ if (new_real_end == bmap->real_end) {
+ bmap->end = new_end;
+ return 0;
+ }
+
+ size = ((bmap->real_end - bmap->start) / 8) + 1;
+ new_size = ((new_real_end - bmap->start) / 8) + 1;
+
+ if (size != new_size) {
+ retval = ext2fs_resize_mem(size, new_size, &bmap->bitmap);
+ if (retval)
+ return retval;
+ }
+ if (new_size > size)
+ memset(bmap->bitmap + size, 0, new_size - size);
+
+ bmap->end = new_end;
+ bmap->real_end = new_real_end;
+ return 0;
+}
+
+errcode_t ext2fs_resize_inode_bitmap(__u32 new_end, __u32 new_real_end,
+ ext2fs_inode_bitmap bmap)
+{
+ errcode_t retval;
+
+ if (!bmap)
+ return EXT2_ET_INVALID_ARGUMENT;
+
+ EXT2_CHECK_MAGIC(bmap, EXT2_ET_MAGIC_INODE_BITMAP);
+
+ bmap->magic = EXT2_ET_MAGIC_GENERIC_BITMAP;
+ retval = ext2fs_resize_generic_bitmap(new_end, new_real_end,
+ bmap);
+ bmap->magic = EXT2_ET_MAGIC_INODE_BITMAP;
+ return retval;
+}
+
+errcode_t ext2fs_resize_block_bitmap(__u32 new_end, __u32 new_real_end,
+ ext2fs_block_bitmap bmap)
+{
+ errcode_t retval;
+
+ if (!bmap)
+ return EXT2_ET_INVALID_ARGUMENT;
+
+ EXT2_CHECK_MAGIC(bmap, EXT2_ET_MAGIC_BLOCK_BITMAP);
+
+ bmap->magic = EXT2_ET_MAGIC_GENERIC_BITMAP;
+ retval = ext2fs_resize_generic_bitmap(new_end, new_real_end,
+ bmap);
+ bmap->magic = EXT2_ET_MAGIC_BLOCK_BITMAP;
+ return retval;
+}
+
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/rw_bitmaps.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/rw_bitmaps.c
new file mode 100644
index 0000000..a5782db
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/rw_bitmaps.c
@@ -0,0 +1,296 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * rw_bitmaps.c --- routines to read and write the inode and block bitmaps.
+ *
+ * Copyright (C) 1993, 1994, 1994, 1996 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+#include "e2image.h"
+
+#if defined(__powerpc__) && BB_BIG_ENDIAN
+/*
+ * On the PowerPC, the big-endian variant of the ext2 filesystem
+ * has its bitmaps stored as 32-bit words with bit 0 as the LSB
+ * of each word. Thus a bitmap with only bit 0 set would be, as
+ * a string of bytes, 00 00 00 01 00 ...
+ * To cope with this, we byte-reverse each word of a bitmap if
+ * we have a big-endian filesystem, that is, if we are *not*
+ * byte-swapping other word-sized numbers.
+ */
+#define EXT2_BIG_ENDIAN_BITMAPS
+#endif
+
+#ifdef EXT2_BIG_ENDIAN_BITMAPS
+static void ext2fs_swap_bitmap(ext2_filsys fs, char *bitmap, int nbytes)
+{
+ __u32 *p = (__u32 *) bitmap;
+ int n;
+
+ for (n = nbytes / sizeof(__u32); n > 0; --n, ++p)
+ *p = ext2fs_swab32(*p);
+}
+#endif
+
+errcode_t ext2fs_write_inode_bitmap(ext2_filsys fs)
+{
+ dgrp_t i;
+ size_t nbytes;
+ errcode_t retval;
+ char * inode_bitmap = fs->inode_map->bitmap;
+ char * bitmap_block = NULL;
+ blk_t blk;
+
+ EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+ if (!(fs->flags & EXT2_FLAG_RW))
+ return EXT2_ET_RO_FILSYS;
+ if (!inode_bitmap)
+ return 0;
+ nbytes = (size_t) ((EXT2_INODES_PER_GROUP(fs->super)+7) / 8);
+
+ retval = ext2fs_get_mem(fs->blocksize, &bitmap_block);
+ if (retval)
+ return retval;
+ memset(bitmap_block, 0xff, fs->blocksize);
+ for (i = 0; i < fs->group_desc_count; i++) {
+ memcpy(bitmap_block, inode_bitmap, nbytes);
+ blk = fs->group_desc[i].bg_inode_bitmap;
+ if (blk) {
+#ifdef EXT2_BIG_ENDIAN_BITMAPS
+ if (!((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
+ (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE)))
+ ext2fs_swap_bitmap(fs, bitmap_block, nbytes);
+#endif
+ retval = io_channel_write_blk(fs->io, blk, 1,
+ bitmap_block);
+ if (retval)
+ return EXT2_ET_INODE_BITMAP_WRITE;
+ }
+ inode_bitmap += nbytes;
+ }
+ fs->flags &= ~EXT2_FLAG_IB_DIRTY;
+ ext2fs_free_mem(&bitmap_block);
+ return 0;
+}
+
+errcode_t ext2fs_write_block_bitmap (ext2_filsys fs)
+{
+ dgrp_t i;
+ unsigned int j;
+ int nbytes;
+ unsigned int nbits;
+ errcode_t retval;
+ char * block_bitmap = fs->block_map->bitmap;
+ char * bitmap_block = NULL;
+ blk_t blk;
+
+ EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+ if (!(fs->flags & EXT2_FLAG_RW))
+ return EXT2_ET_RO_FILSYS;
+ if (!block_bitmap)
+ return 0;
+ nbytes = EXT2_BLOCKS_PER_GROUP(fs->super) / 8;
+ retval = ext2fs_get_mem(fs->blocksize, &bitmap_block);
+ if (retval)
+ return retval;
+ memset(bitmap_block, 0xff, fs->blocksize);
+ for (i = 0; i < fs->group_desc_count; i++) {
+ memcpy(bitmap_block, block_bitmap, nbytes);
+ if (i == fs->group_desc_count - 1) {
+ /* Force bitmap padding for the last group */
+ nbits = ((fs->super->s_blocks_count
+ - fs->super->s_first_data_block)
+ % EXT2_BLOCKS_PER_GROUP(fs->super));
+ if (nbits)
+ for (j = nbits; j < fs->blocksize * 8; j++)
+ ext2fs_set_bit(j, bitmap_block);
+ }
+ blk = fs->group_desc[i].bg_block_bitmap;
+ if (blk) {
+#ifdef EXT2_BIG_ENDIAN_BITMAPS
+ if (!((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
+ (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE)))
+ ext2fs_swap_bitmap(fs, bitmap_block, nbytes);
+#endif
+ retval = io_channel_write_blk(fs->io, blk, 1,
+ bitmap_block);
+ if (retval)
+ return EXT2_ET_BLOCK_BITMAP_WRITE;
+ }
+ block_bitmap += nbytes;
+ }
+ fs->flags &= ~EXT2_FLAG_BB_DIRTY;
+ ext2fs_free_mem(&bitmap_block);
+ return 0;
+}
+
+static errcode_t read_bitmaps(ext2_filsys fs, int do_inode, int do_block)
+{
+ dgrp_t i;
+ char *block_bitmap = 0, *inode_bitmap = 0;
+ char *buf;
+ errcode_t retval;
+ int block_nbytes = (int) EXT2_BLOCKS_PER_GROUP(fs->super) / 8;
+ int inode_nbytes = (int) EXT2_INODES_PER_GROUP(fs->super) / 8;
+ blk_t blk;
+
+ EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+ fs->write_bitmaps = ext2fs_write_bitmaps;
+
+ retval = ext2fs_get_mem(strlen(fs->device_name) + 80, &buf);
+ if (retval)
+ return retval;
+ if (do_block) {
+ ext2fs_free_block_bitmap(fs->block_map);
+ sprintf(buf, "block bitmap for %s", fs->device_name);
+ retval = ext2fs_allocate_block_bitmap(fs, buf, &fs->block_map);
+ if (retval)
+ goto cleanup;
+ block_bitmap = fs->block_map->bitmap;
+ }
+ if (do_inode) {
+ ext2fs_free_inode_bitmap(fs->inode_map);
+ sprintf(buf, "inode bitmap for %s", fs->device_name);
+ retval = ext2fs_allocate_inode_bitmap(fs, buf, &fs->inode_map);
+ if (retval)
+ goto cleanup;
+ inode_bitmap = fs->inode_map->bitmap;
+ }
+ ext2fs_free_mem(&buf);
+
+ if (fs->flags & EXT2_FLAG_IMAGE_FILE) {
+ if (inode_bitmap) {
+ blk = (fs->image_header->offset_inodemap /
+ fs->blocksize);
+ retval = io_channel_read_blk(fs->image_io, blk,
+ -(inode_nbytes * fs->group_desc_count),
+ inode_bitmap);
+ if (retval)
+ goto cleanup;
+ }
+ if (block_bitmap) {
+ blk = (fs->image_header->offset_blockmap /
+ fs->blocksize);
+ retval = io_channel_read_blk(fs->image_io, blk,
+ -(block_nbytes * fs->group_desc_count),
+ block_bitmap);
+ if (retval)
+ goto cleanup;
+ }
+ return 0;
+ }
+
+ for (i = 0; i < fs->group_desc_count; i++) {
+ if (block_bitmap) {
+ blk = fs->group_desc[i].bg_block_bitmap;
+ if (blk) {
+ retval = io_channel_read_blk(fs->io, blk,
+ -block_nbytes, block_bitmap);
+ if (retval) {
+ retval = EXT2_ET_BLOCK_BITMAP_READ;
+ goto cleanup;
+ }
+#ifdef EXT2_BIG_ENDIAN_BITMAPS
+ if (!((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
+ (fs->flags & EXT2_FLAG_SWAP_BYTES_READ)))
+ ext2fs_swap_bitmap(fs, block_bitmap, block_nbytes);
+#endif
+ } else
+ memset(block_bitmap, 0, block_nbytes);
+ block_bitmap += block_nbytes;
+ }
+ if (inode_bitmap) {
+ blk = fs->group_desc[i].bg_inode_bitmap;
+ if (blk) {
+ retval = io_channel_read_blk(fs->io, blk,
+ -inode_nbytes, inode_bitmap);
+ if (retval) {
+ retval = EXT2_ET_INODE_BITMAP_READ;
+ goto cleanup;
+ }
+#ifdef EXT2_BIG_ENDIAN_BITMAPS
+ if (!((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
+ (fs->flags & EXT2_FLAG_SWAP_BYTES_READ)))
+ ext2fs_swap_bitmap(fs, inode_bitmap, inode_nbytes);
+#endif
+ } else
+ memset(inode_bitmap, 0, inode_nbytes);
+ inode_bitmap += inode_nbytes;
+ }
+ }
+ return 0;
+
+cleanup:
+ if (do_block) {
+ ext2fs_free_mem(&fs->block_map);
+ }
+ if (do_inode) {
+ ext2fs_free_mem(&fs->inode_map);
+ }
+ ext2fs_free_mem(&buf);
+ return retval;
+}
+
+errcode_t ext2fs_read_inode_bitmap (ext2_filsys fs)
+{
+ return read_bitmaps(fs, 1, 0);
+}
+
+errcode_t ext2fs_read_block_bitmap(ext2_filsys fs)
+{
+ return read_bitmaps(fs, 0, 1);
+}
+
+errcode_t ext2fs_read_bitmaps(ext2_filsys fs)
+{
+
+ EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+ if (fs->inode_map && fs->block_map)
+ return 0;
+
+ return read_bitmaps(fs, !fs->inode_map, !fs->block_map);
+}
+
+errcode_t ext2fs_write_bitmaps(ext2_filsys fs)
+{
+ errcode_t retval;
+
+ EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+ if (fs->block_map && ext2fs_test_bb_dirty(fs)) {
+ retval = ext2fs_write_block_bitmap(fs);
+ if (retval)
+ return retval;
+ }
+ if (fs->inode_map && ext2fs_test_ib_dirty(fs)) {
+ retval = ext2fs_write_inode_bitmap(fs);
+ if (retval)
+ return retval;
+ }
+ return 0;
+}
+
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/sparse.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/sparse.c
new file mode 100644
index 0000000..b3d3071
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/sparse.c
@@ -0,0 +1,79 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * sparse.c --- find the groups in an ext2 filesystem with metadata backups
+ *
+ * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
+ * Copyright (C) 2002 Andreas Dilger.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+
+#include "ext2_fs.h"
+#include "ext2fsP.h"
+
+static int test_root(int a, int b)
+{
+ if (a == 0)
+ return 1;
+ while (1) {
+ if (a == 1)
+ return 1;
+ if (a % b)
+ return 0;
+ a = a / b;
+ }
+}
+
+int ext2fs_bg_has_super(ext2_filsys fs, int group_block)
+{
+ if (!(fs->super->s_feature_ro_compat &
+ EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER))
+ return 1;
+
+ if (test_root(group_block, 3) || (test_root(group_block, 5)) ||
+ test_root(group_block, 7))
+ return 1;
+
+ return 0;
+}
+
+/*
+ * Iterate through the groups which hold BACKUP superblock/GDT copies in an
+ * ext3 filesystem. The counters should be initialized to 1, 5, and 7 before
+ * calling this for the first time. In a sparse filesystem it will be the
+ * sequence of powers of 3, 5, and 7: 1, 3, 5, 7, 9, 25, 27, 49, 81, ...
+ * For a non-sparse filesystem it will be every group: 1, 2, 3, 4, ...
+ */
+unsigned int ext2fs_list_backups(ext2_filsys fs, unsigned int *three,
+ unsigned int *five, unsigned int *seven)
+{
+ unsigned int *min = three;
+ int mult = 3;
+ unsigned int ret;
+
+ if (!(fs->super->s_feature_ro_compat &
+ EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)) {
+ ret = *min;
+ *min += 1;
+ return ret;
+ }
+
+ if (*five < *min) {
+ min = five;
+ mult = 5;
+ }
+ if (*seven < *min) {
+ min = seven;
+ mult = 7;
+ }
+
+ ret = *min;
+ *min *= mult;
+
+ return ret;
+}
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/swapfs.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/swapfs.c
new file mode 100644
index 0000000..2fca3cf
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/swapfs.c
@@ -0,0 +1,236 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * swapfs.c --- swap ext2 filesystem data structures
+ *
+ * Copyright (C) 1995, 1996, 2002 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <time.h>
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+#include "ext2_ext_attr.h"
+
+#if BB_BIG_ENDIAN
+void ext2fs_swap_super(struct ext2_super_block * sb)
+{
+ int i;
+ sb->s_inodes_count = ext2fs_swab32(sb->s_inodes_count);
+ sb->s_blocks_count = ext2fs_swab32(sb->s_blocks_count);
+ sb->s_r_blocks_count = ext2fs_swab32(sb->s_r_blocks_count);
+ sb->s_free_blocks_count = ext2fs_swab32(sb->s_free_blocks_count);
+ sb->s_free_inodes_count = ext2fs_swab32(sb->s_free_inodes_count);
+ sb->s_first_data_block = ext2fs_swab32(sb->s_first_data_block);
+ sb->s_log_block_size = ext2fs_swab32(sb->s_log_block_size);
+ sb->s_log_frag_size = ext2fs_swab32(sb->s_log_frag_size);
+ sb->s_blocks_per_group = ext2fs_swab32(sb->s_blocks_per_group);
+ sb->s_frags_per_group = ext2fs_swab32(sb->s_frags_per_group);
+ sb->s_inodes_per_group = ext2fs_swab32(sb->s_inodes_per_group);
+ sb->s_mtime = ext2fs_swab32(sb->s_mtime);
+ sb->s_wtime = ext2fs_swab32(sb->s_wtime);
+ sb->s_mnt_count = ext2fs_swab16(sb->s_mnt_count);
+ sb->s_max_mnt_count = ext2fs_swab16(sb->s_max_mnt_count);
+ sb->s_magic = ext2fs_swab16(sb->s_magic);
+ sb->s_state = ext2fs_swab16(sb->s_state);
+ sb->s_errors = ext2fs_swab16(sb->s_errors);
+ sb->s_minor_rev_level = ext2fs_swab16(sb->s_minor_rev_level);
+ sb->s_lastcheck = ext2fs_swab32(sb->s_lastcheck);
+ sb->s_checkinterval = ext2fs_swab32(sb->s_checkinterval);
+ sb->s_creator_os = ext2fs_swab32(sb->s_creator_os);
+ sb->s_rev_level = ext2fs_swab32(sb->s_rev_level);
+ sb->s_def_resuid = ext2fs_swab16(sb->s_def_resuid);
+ sb->s_def_resgid = ext2fs_swab16(sb->s_def_resgid);
+ sb->s_first_ino = ext2fs_swab32(sb->s_first_ino);
+ sb->s_inode_size = ext2fs_swab16(sb->s_inode_size);
+ sb->s_block_group_nr = ext2fs_swab16(sb->s_block_group_nr);
+ sb->s_feature_compat = ext2fs_swab32(sb->s_feature_compat);
+ sb->s_feature_incompat = ext2fs_swab32(sb->s_feature_incompat);
+ sb->s_feature_ro_compat = ext2fs_swab32(sb->s_feature_ro_compat);
+ sb->s_algorithm_usage_bitmap = ext2fs_swab32(sb->s_algorithm_usage_bitmap);
+ sb->s_reserved_gdt_blocks = ext2fs_swab16(sb->s_reserved_gdt_blocks);
+ sb->s_journal_inum = ext2fs_swab32(sb->s_journal_inum);
+ sb->s_journal_dev = ext2fs_swab32(sb->s_journal_dev);
+ sb->s_last_orphan = ext2fs_swab32(sb->s_last_orphan);
+ sb->s_default_mount_opts = ext2fs_swab32(sb->s_default_mount_opts);
+ sb->s_first_meta_bg = ext2fs_swab32(sb->s_first_meta_bg);
+ sb->s_mkfs_time = ext2fs_swab32(sb->s_mkfs_time);
+ for (i=0; i < 4; i++)
+ sb->s_hash_seed[i] = ext2fs_swab32(sb->s_hash_seed[i]);
+ for (i=0; i < 17; i++)
+ sb->s_jnl_blocks[i] = ext2fs_swab32(sb->s_jnl_blocks[i]);
+
+}
+
+void ext2fs_swap_group_desc(struct ext2_group_desc *gdp)
+{
+ gdp->bg_block_bitmap = ext2fs_swab32(gdp->bg_block_bitmap);
+ gdp->bg_inode_bitmap = ext2fs_swab32(gdp->bg_inode_bitmap);
+ gdp->bg_inode_table = ext2fs_swab32(gdp->bg_inode_table);
+ gdp->bg_free_blocks_count = ext2fs_swab16(gdp->bg_free_blocks_count);
+ gdp->bg_free_inodes_count = ext2fs_swab16(gdp->bg_free_inodes_count);
+ gdp->bg_used_dirs_count = ext2fs_swab16(gdp->bg_used_dirs_count);
+}
+
+void ext2fs_swap_ext_attr(char *to, char *from, int bufsize, int has_header)
+{
+ struct ext2_ext_attr_header *from_header =
+ (struct ext2_ext_attr_header *)from;
+ struct ext2_ext_attr_header *to_header =
+ (struct ext2_ext_attr_header *)to;
+ struct ext2_ext_attr_entry *from_entry, *to_entry;
+ char *from_end = (char *)from_header + bufsize;
+ int n;
+
+ if (to_header != from_header)
+ memcpy(to_header, from_header, bufsize);
+
+ from_entry = (struct ext2_ext_attr_entry *)from_header;
+ to_entry = (struct ext2_ext_attr_entry *)to_header;
+
+ if (has_header) {
+ to_header->h_magic = ext2fs_swab32(from_header->h_magic);
+ to_header->h_blocks = ext2fs_swab32(from_header->h_blocks);
+ to_header->h_refcount = ext2fs_swab32(from_header->h_refcount);
+ for (n=0; n<4; n++)
+ to_header->h_reserved[n] =
+ ext2fs_swab32(from_header->h_reserved[n]);
+ from_entry = (struct ext2_ext_attr_entry *)(from_header+1);
+ to_entry = (struct ext2_ext_attr_entry *)(to_header+1);
+ }
+
+ while ((char *)from_entry < from_end && *(__u32 *)from_entry) {
+ to_entry->e_value_offs =
+ ext2fs_swab16(from_entry->e_value_offs);
+ to_entry->e_value_block =
+ ext2fs_swab32(from_entry->e_value_block);
+ to_entry->e_value_size =
+ ext2fs_swab32(from_entry->e_value_size);
+ from_entry = EXT2_EXT_ATTR_NEXT(from_entry);
+ to_entry = EXT2_EXT_ATTR_NEXT(to_entry);
+ }
+}
+
+void ext2fs_swap_inode_full(ext2_filsys fs, struct ext2_inode_large *t,
+ struct ext2_inode_large *f, int hostorder,
+ int bufsize)
+{
+ unsigned i;
+ int islnk = 0;
+ __u32 *eaf, *eat;
+
+ if (hostorder && LINUX_S_ISLNK(f->i_mode))
+ islnk = 1;
+ t->i_mode = ext2fs_swab16(f->i_mode);
+ if (!hostorder && LINUX_S_ISLNK(t->i_mode))
+ islnk = 1;
+ t->i_uid = ext2fs_swab16(f->i_uid);
+ t->i_size = ext2fs_swab32(f->i_size);
+ t->i_atime = ext2fs_swab32(f->i_atime);
+ t->i_ctime = ext2fs_swab32(f->i_ctime);
+ t->i_mtime = ext2fs_swab32(f->i_mtime);
+ t->i_dtime = ext2fs_swab32(f->i_dtime);
+ t->i_gid = ext2fs_swab16(f->i_gid);
+ t->i_links_count = ext2fs_swab16(f->i_links_count);
+ t->i_blocks = ext2fs_swab32(f->i_blocks);
+ t->i_flags = ext2fs_swab32(f->i_flags);
+ t->i_file_acl = ext2fs_swab32(f->i_file_acl);
+ t->i_dir_acl = ext2fs_swab32(f->i_dir_acl);
+ if (!islnk || ext2fs_inode_data_blocks(fs, (struct ext2_inode *)t)) {
+ for (i = 0; i < EXT2_N_BLOCKS; i++)
+ t->i_block[i] = ext2fs_swab32(f->i_block[i]);
+ } else if (t != f) {
+ for (i = 0; i < EXT2_N_BLOCKS; i++)
+ t->i_block[i] = f->i_block[i];
+ }
+ t->i_generation = ext2fs_swab32(f->i_generation);
+ t->i_faddr = ext2fs_swab32(f->i_faddr);
+
+ switch (fs->super->s_creator_os) {
+ case EXT2_OS_LINUX:
+ t->osd1.linux1.l_i_reserved1 =
+ ext2fs_swab32(f->osd1.linux1.l_i_reserved1);
+ t->osd2.linux2.l_i_frag = f->osd2.linux2.l_i_frag;
+ t->osd2.linux2.l_i_fsize = f->osd2.linux2.l_i_fsize;
+ t->osd2.linux2.i_pad1 = ext2fs_swab16(f->osd2.linux2.i_pad1);
+ t->osd2.linux2.l_i_uid_high =
+ ext2fs_swab16 (f->osd2.linux2.l_i_uid_high);
+ t->osd2.linux2.l_i_gid_high =
+ ext2fs_swab16 (f->osd2.linux2.l_i_gid_high);
+ t->osd2.linux2.l_i_reserved2 =
+ ext2fs_swab32(f->osd2.linux2.l_i_reserved2);
+ break;
+ case EXT2_OS_HURD:
+ t->osd1.hurd1.h_i_translator =
+ ext2fs_swab32 (f->osd1.hurd1.h_i_translator);
+ t->osd2.hurd2.h_i_frag = f->osd2.hurd2.h_i_frag;
+ t->osd2.hurd2.h_i_fsize = f->osd2.hurd2.h_i_fsize;
+ t->osd2.hurd2.h_i_mode_high =
+ ext2fs_swab16 (f->osd2.hurd2.h_i_mode_high);
+ t->osd2.hurd2.h_i_uid_high =
+ ext2fs_swab16 (f->osd2.hurd2.h_i_uid_high);
+ t->osd2.hurd2.h_i_gid_high =
+ ext2fs_swab16 (f->osd2.hurd2.h_i_gid_high);
+ t->osd2.hurd2.h_i_author =
+ ext2fs_swab32 (f->osd2.hurd2.h_i_author);
+ break;
+ case EXT2_OS_MASIX:
+ t->osd1.masix1.m_i_reserved1 =
+ ext2fs_swab32(f->osd1.masix1.m_i_reserved1);
+ t->osd2.masix2.m_i_frag = f->osd2.masix2.m_i_frag;
+ t->osd2.masix2.m_i_fsize = f->osd2.masix2.m_i_fsize;
+ t->osd2.masix2.m_pad1 = ext2fs_swab16(f->osd2.masix2.m_pad1);
+ t->osd2.masix2.m_i_reserved2[0] =
+ ext2fs_swab32(f->osd2.masix2.m_i_reserved2[0]);
+ t->osd2.masix2.m_i_reserved2[1] =
+ ext2fs_swab32(f->osd2.masix2.m_i_reserved2[1]);
+ break;
+ }
+
+ if (bufsize < (int) (sizeof(struct ext2_inode) + sizeof(__u16)))
+ return; /* no i_extra_isize field */
+
+ t->i_extra_isize = ext2fs_swab16(f->i_extra_isize);
+ if (t->i_extra_isize > EXT2_INODE_SIZE(fs->super) -
+ sizeof(struct ext2_inode)) {
+ /* this is error case: i_extra_size is too large */
+ return;
+ }
+
+ i = sizeof(struct ext2_inode) + t->i_extra_isize + sizeof(__u32);
+ if (bufsize < (int) i)
+ return; /* no space for EA magic */
+
+ eaf = (__u32 *) (((char *) f) + sizeof(struct ext2_inode) +
+ f->i_extra_isize);
+
+ if (ext2fs_swab32(*eaf) != EXT2_EXT_ATTR_MAGIC)
+ return; /* it seems no magic here */
+
+ eat = (__u32 *) (((char *) t) + sizeof(struct ext2_inode) +
+ f->i_extra_isize);
+ *eat = ext2fs_swab32(*eaf);
+
+ /* convert EA(s) */
+ ext2fs_swap_ext_attr((char *) (eat + 1), (char *) (eaf + 1),
+ bufsize - sizeof(struct ext2_inode) -
+ t->i_extra_isize - sizeof(__u32), 0);
+
+}
+
+void ext2fs_swap_inode(ext2_filsys fs, struct ext2_inode *t,
+ struct ext2_inode *f, int hostorder)
+{
+ ext2fs_swap_inode_full(fs, (struct ext2_inode_large *) t,
+ (struct ext2_inode_large *) f, hostorder,
+ sizeof(struct ext2_inode));
+}
+
+#endif
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/test_io.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/test_io.c
new file mode 100644
index 0000000..bd74225
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/test_io.c
@@ -0,0 +1,380 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * test_io.c --- This is the Test I/O interface.
+ *
+ * Copyright (C) 1996 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+/*
+ * For checking structure magic numbers...
+ */
+
+#define EXT2_CHECK_MAGIC(struct, code) \
+ if ((struct)->magic != (code)) return (code)
+
+struct test_private_data {
+ int magic;
+ io_channel real;
+ int flags;
+ FILE *outfile;
+ unsigned long block;
+ int read_abort_count, write_abort_count;
+ void (*read_blk)(unsigned long block, int count, errcode_t err);
+ void (*write_blk)(unsigned long block, int count, errcode_t err);
+ void (*set_blksize)(int blksize, errcode_t err);
+ void (*write_byte)(unsigned long block, int count, errcode_t err);
+};
+
+static errcode_t test_open(const char *name, int flags, io_channel *channel);
+static errcode_t test_close(io_channel channel);
+static errcode_t test_set_blksize(io_channel channel, int blksize);
+static errcode_t test_read_blk(io_channel channel, unsigned long block,
+ int count, void *data);
+static errcode_t test_write_blk(io_channel channel, unsigned long block,
+ int count, const void *data);
+static errcode_t test_flush(io_channel channel);
+static errcode_t test_write_byte(io_channel channel, unsigned long offset,
+ int count, const void *buf);
+static errcode_t test_set_option(io_channel channel, const char *option,
+ const char *arg);
+
+static struct struct_io_manager struct_test_manager = {
+ EXT2_ET_MAGIC_IO_MANAGER,
+ "Test I/O Manager",
+ test_open,
+ test_close,
+ test_set_blksize,
+ test_read_blk,
+ test_write_blk,
+ test_flush,
+ test_write_byte,
+ test_set_option
+};
+
+io_manager test_io_manager = &struct_test_manager;
+
+/*
+ * These global variable can be set by the test program as
+ * necessary *before* calling test_open
+ */
+io_manager test_io_backing_manager = 0;
+void (*test_io_cb_read_blk)
+ (unsigned long block, int count, errcode_t err) = 0;
+void (*test_io_cb_write_blk)
+ (unsigned long block, int count, errcode_t err) = 0;
+void (*test_io_cb_set_blksize)
+ (int blksize, errcode_t err) = 0;
+void (*test_io_cb_write_byte)
+ (unsigned long block, int count, errcode_t err) = 0;
+
+/*
+ * Test flags
+ */
+#define TEST_FLAG_READ 0x01
+#define TEST_FLAG_WRITE 0x02
+#define TEST_FLAG_SET_BLKSIZE 0x04
+#define TEST_FLAG_FLUSH 0x08
+#define TEST_FLAG_DUMP 0x10
+#define TEST_FLAG_SET_OPTION 0x20
+
+static void test_dump_block(io_channel channel,
+ struct test_private_data *data,
+ unsigned long block, const void *buf)
+{
+ const unsigned char *cp;
+ FILE *f = data->outfile;
+ int i;
+ unsigned long cksum = 0;
+
+ for (i=0, cp = buf; i < channel->block_size; i++, cp++) {
+ cksum += *cp;
+ }
+ fprintf(f, "Contents of block %lu, checksum %08lu:\n", block, cksum);
+ for (i=0, cp = buf; i < channel->block_size; i++, cp++) {
+ if ((i % 16) == 0)
+ fprintf(f, "%04x: ", i);
+ fprintf(f, "%02x%c", *cp, ((i % 16) == 15) ? '\n' : ' ');
+ }
+}
+
+static void test_abort(io_channel channel, unsigned long block)
+{
+ struct test_private_data *data;
+ FILE *f;
+
+ data = (struct test_private_data *) channel->private_data;
+ f = data->outfile;
+ test_flush(channel);
+
+ fprintf(f, "Aborting due to I/O to block %lu\n", block);
+ fflush(f);
+ abort();
+}
+
+static errcode_t test_open(const char *name, int flags, io_channel *channel)
+{
+ io_channel io = NULL;
+ struct test_private_data *data = NULL;
+ errcode_t retval;
+ char *value;
+
+ if (name == 0)
+ return EXT2_ET_BAD_DEVICE_NAME;
+ retval = ext2fs_get_mem(sizeof(struct struct_io_channel), &io);
+ if (retval)
+ return retval;
+ memset(io, 0, sizeof(struct struct_io_channel));
+ io->magic = EXT2_ET_MAGIC_IO_CHANNEL;
+ retval = ext2fs_get_mem(sizeof(struct test_private_data), &data);
+ if (retval) {
+ retval = EXT2_ET_NO_MEMORY;
+ goto cleanup;
+ }
+ io->manager = test_io_manager;
+ retval = ext2fs_get_mem(strlen(name)+1, &io->name);
+ if (retval)
+ goto cleanup;
+
+ strcpy(io->name, name);
+ io->private_data = data;
+ io->block_size = 1024;
+ io->read_error = 0;
+ io->write_error = 0;
+ io->refcount = 1;
+
+ memset(data, 0, sizeof(struct test_private_data));
+ data->magic = EXT2_ET_MAGIC_TEST_IO_CHANNEL;
+ if (test_io_backing_manager) {
+ retval = test_io_backing_manager->open(name, flags,
+ &data->real);
+ if (retval)
+ goto cleanup;
+ } else
+ data->real = 0;
+ data->read_blk = test_io_cb_read_blk;
+ data->write_blk = test_io_cb_write_blk;
+ data->set_blksize = test_io_cb_set_blksize;
+ data->write_byte = test_io_cb_write_byte;
+
+ data->outfile = NULL;
+ if ((value = getenv("TEST_IO_LOGFILE")) != NULL)
+ data->outfile = fopen(value, "w");
+ if (!data->outfile)
+ data->outfile = stderr;
+
+ data->flags = 0;
+ if ((value = getenv("TEST_IO_FLAGS")) != NULL)
+ data->flags = strtoul(value, NULL, 0);
+
+ data->block = 0;
+ if ((value = getenv("TEST_IO_BLOCK")) != NULL)
+ data->block = strtoul(value, NULL, 0);
+
+ data->read_abort_count = 0;
+ if ((value = getenv("TEST_IO_READ_ABORT")) != NULL)
+ data->read_abort_count = strtoul(value, NULL, 0);
+
+ data->write_abort_count = 0;
+ if ((value = getenv("TEST_IO_WRITE_ABORT")) != NULL)
+ data->write_abort_count = strtoul(value, NULL, 0);
+
+ *channel = io;
+ return 0;
+
+cleanup:
+ ext2fs_free_mem(&io);
+ ext2fs_free_mem(&data);
+ return retval;
+}
+
+static errcode_t test_close(io_channel channel)
+{
+ struct test_private_data *data;
+ errcode_t retval = 0;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct test_private_data *) channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
+
+ if (--channel->refcount > 0)
+ return 0;
+
+ if (data->real)
+ retval = io_channel_close(data->real);
+
+ if (data->outfile && data->outfile != stderr)
+ fclose(data->outfile);
+
+ ext2fs_free_mem(&channel->private_data);
+ ext2fs_free_mem(&channel->name);
+ ext2fs_free_mem(&channel);
+ return retval;
+}
+
+static errcode_t test_set_blksize(io_channel channel, int blksize)
+{
+ struct test_private_data *data;
+ errcode_t retval = 0;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct test_private_data *) channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
+
+ if (data->real)
+ retval = io_channel_set_blksize(data->real, blksize);
+ if (data->set_blksize)
+ data->set_blksize(blksize, retval);
+ if (data->flags & TEST_FLAG_SET_BLKSIZE)
+ fprintf(data->outfile,
+ "Test_io: set_blksize(%d) returned %s\n",
+ blksize, retval ? error_message(retval) : "OK");
+ channel->block_size = blksize;
+ return retval;
+}
+
+
+static errcode_t test_read_blk(io_channel channel, unsigned long block,
+ int count, void *buf)
+{
+ struct test_private_data *data;
+ errcode_t retval = 0;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct test_private_data *) channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
+
+ if (data->real)
+ retval = io_channel_read_blk(data->real, block, count, buf);
+ if (data->read_blk)
+ data->read_blk(block, count, retval);
+ if (data->flags & TEST_FLAG_READ)
+ fprintf(data->outfile,
+ "Test_io: read_blk(%lu, %d) returned %s\n",
+ block, count, retval ? error_message(retval) : "OK");
+ if (data->block && data->block == block) {
+ if (data->flags & TEST_FLAG_DUMP)
+ test_dump_block(channel, data, block, buf);
+ if (--data->read_abort_count == 0)
+ test_abort(channel, block);
+ }
+ return retval;
+}
+
+static errcode_t test_write_blk(io_channel channel, unsigned long block,
+ int count, const void *buf)
+{
+ struct test_private_data *data;
+ errcode_t retval = 0;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct test_private_data *) channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
+
+ if (data->real)
+ retval = io_channel_write_blk(data->real, block, count, buf);
+ if (data->write_blk)
+ data->write_blk(block, count, retval);
+ if (data->flags & TEST_FLAG_WRITE)
+ fprintf(data->outfile,
+ "Test_io: write_blk(%lu, %d) returned %s\n",
+ block, count, retval ? error_message(retval) : "OK");
+ if (data->block && data->block == block) {
+ if (data->flags & TEST_FLAG_DUMP)
+ test_dump_block(channel, data, block, buf);
+ if (--data->write_abort_count == 0)
+ test_abort(channel, block);
+ }
+ return retval;
+}
+
+static errcode_t test_write_byte(io_channel channel, unsigned long offset,
+ int count, const void *buf)
+{
+ struct test_private_data *data;
+ errcode_t retval = 0;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct test_private_data *) channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
+
+ if (data->real && data->real->manager->write_byte)
+ retval = io_channel_write_byte(data->real, offset, count, buf);
+ if (data->write_byte)
+ data->write_byte(offset, count, retval);
+ if (data->flags & TEST_FLAG_WRITE)
+ fprintf(data->outfile,
+ "Test_io: write_byte(%lu, %d) returned %s\n",
+ offset, count, retval ? error_message(retval) : "OK");
+ return retval;
+}
+
+/*
+ * Flush data buffers to disk.
+ */
+static errcode_t test_flush(io_channel channel)
+{
+ struct test_private_data *data;
+ errcode_t retval = 0;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct test_private_data *) channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
+
+ if (data->real)
+ retval = io_channel_flush(data->real);
+
+ if (data->flags & TEST_FLAG_FLUSH)
+ fprintf(data->outfile, "Test_io: flush() returned %s\n",
+ retval ? error_message(retval) : "OK");
+
+ return retval;
+}
+
+static errcode_t test_set_option(io_channel channel, const char *option,
+ const char *arg)
+{
+ struct test_private_data *data;
+ errcode_t retval = 0;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct test_private_data *) channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
+
+
+ if (data->flags & TEST_FLAG_SET_OPTION)
+ fprintf(data->outfile, "Test_io: set_option(%s, %s) ",
+ option, arg);
+ if (data->real && data->real->manager->set_option) {
+ retval = (data->real->manager->set_option)(data->real,
+ option, arg);
+ if (data->flags & TEST_FLAG_SET_OPTION)
+ fprintf(data->outfile, "returned %s\n",
+ retval ? error_message(retval) : "OK");
+ } else {
+ if (data->flags & TEST_FLAG_SET_OPTION)
+ fprintf(data->outfile, "not implemented\n");
+ }
+ return retval;
+}
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/unix_io.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/unix_io.c
new file mode 100644
index 0000000..474f073
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/unix_io.c
@@ -0,0 +1,703 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * unix_io.c --- This is the Unix (well, really POSIX) implementation
+ * of the I/O manager.
+ *
+ * Implements a one-block write-through cache.
+ *
+ * Includes support for Windows NT support under Cygwin.
+ *
+ * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
+ * 2002 by Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#ifdef __linux__
+#include <sys/utsname.h>
+#endif
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#include <sys/resource.h>
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+/*
+ * For checking structure magic numbers...
+ */
+
+#define EXT2_CHECK_MAGIC(struct, code) \
+ if ((struct)->magic != (code)) return (code)
+
+struct unix_cache {
+ char *buf;
+ unsigned long block;
+ int access_time;
+ unsigned dirty:1;
+ unsigned in_use:1;
+};
+
+#define CACHE_SIZE 8
+#define WRITE_DIRECT_SIZE 4 /* Must be smaller than CACHE_SIZE */
+#define READ_DIRECT_SIZE 4 /* Should be smaller than CACHE_SIZE */
+
+struct unix_private_data {
+ int magic;
+ int dev;
+ int flags;
+ int access_time;
+ ext2_loff_t offset;
+ struct unix_cache cache[CACHE_SIZE];
+};
+
+static errcode_t unix_open(const char *name, int flags, io_channel *channel);
+static errcode_t unix_close(io_channel channel);
+static errcode_t unix_set_blksize(io_channel channel, int blksize);
+static errcode_t unix_read_blk(io_channel channel, unsigned long block,
+ int count, void *data);
+static errcode_t unix_write_blk(io_channel channel, unsigned long block,
+ int count, const void *data);
+static errcode_t unix_flush(io_channel channel);
+static errcode_t unix_write_byte(io_channel channel, unsigned long offset,
+ int size, const void *data);
+static errcode_t unix_set_option(io_channel channel, const char *option,
+ const char *arg);
+
+static void reuse_cache(io_channel channel, struct unix_private_data *data,
+ struct unix_cache *cache, unsigned long block);
+
+/* __FreeBSD_kernel__ is defined by GNU/kFreeBSD - the FreeBSD kernel
+ * does not know buffered block devices - everything is raw. */
+#if defined(__CYGWIN__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+#define NEED_BOUNCE_BUFFER
+#else
+#undef NEED_BOUNCE_BUFFER
+#endif
+
+static struct struct_io_manager struct_unix_manager = {
+ EXT2_ET_MAGIC_IO_MANAGER,
+ "Unix I/O Manager",
+ unix_open,
+ unix_close,
+ unix_set_blksize,
+ unix_read_blk,
+ unix_write_blk,
+ unix_flush,
+#ifdef NEED_BOUNCE_BUFFER
+ 0,
+#else
+ unix_write_byte,
+#endif
+ unix_set_option
+};
+
+io_manager unix_io_manager = &struct_unix_manager;
+
+/*
+ * Here are the raw I/O functions
+ */
+#ifndef NEED_BOUNCE_BUFFER
+static errcode_t raw_read_blk(io_channel channel,
+ struct unix_private_data *data,
+ unsigned long block,
+ int count, void *buf)
+{
+ errcode_t retval;
+ ssize_t size;
+ ext2_loff_t location;
+ int actual = 0;
+
+ size = (count < 0) ? -count : count * channel->block_size;
+ location = ((ext2_loff_t) block * channel->block_size) + data->offset;
+ if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) {
+ retval = errno ? errno : EXT2_ET_LLSEEK_FAILED;
+ goto error_out;
+ }
+ actual = read(data->dev, buf, size);
+ if (actual != size) {
+ if (actual < 0)
+ actual = 0;
+ retval = EXT2_ET_SHORT_READ;
+ goto error_out;
+ }
+ return 0;
+
+error_out:
+ memset((char *) buf+actual, 0, size-actual);
+ if (channel->read_error)
+ retval = (channel->read_error)(channel, block, count, buf,
+ size, actual, retval);
+ return retval;
+}
+#else /* NEED_BOUNCE_BUFFER */
+/*
+ * Windows and FreeBSD block devices only allow sector alignment IO in offset and size
+ */
+static errcode_t raw_read_blk(io_channel channel,
+ struct unix_private_data *data,
+ unsigned long block,
+ int count, void *buf)
+{
+ errcode_t retval;
+ size_t size, alignsize, fragment;
+ ext2_loff_t location;
+ int total = 0, actual;
+#define BLOCKALIGN 512
+ char sector[BLOCKALIGN];
+
+ size = (count < 0) ? -count : count * channel->block_size;
+ location = ((ext2_loff_t) block * channel->block_size) + data->offset;
+#ifdef DEBUG
+ printf("count=%d, size=%d, block=%d, blk_size=%d, location=%lx\n",
+ count, size, block, channel->block_size, location);
+#endif
+ if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) {
+ retval = errno ? errno : EXT2_ET_LLSEEK_FAILED;
+ goto error_out;
+ }
+ fragment = size % BLOCKALIGN;
+ alignsize = size - fragment;
+ if (alignsize) {
+ actual = read(data->dev, buf, alignsize);
+ if (actual != alignsize)
+ goto short_read;
+ }
+ if (fragment) {
+ actual = read(data->dev, sector, BLOCKALIGN);
+ if (actual != BLOCKALIGN)
+ goto short_read;
+ memcpy(buf+alignsize, sector, fragment);
+ }
+ return 0;
+
+short_read:
+ if (actual>0)
+ total += actual;
+ retval = EXT2_ET_SHORT_READ;
+
+error_out:
+ memset((char *) buf+total, 0, size-actual);
+ if (channel->read_error)
+ retval = (channel->read_error)(channel, block, count, buf,
+ size, actual, retval);
+ return retval;
+}
+#endif
+
+static errcode_t raw_write_blk(io_channel channel,
+ struct unix_private_data *data,
+ unsigned long block,
+ int count, const void *buf)
+{
+ ssize_t size;
+ ext2_loff_t location;
+ int actual = 0;
+ errcode_t retval;
+
+ if (count == 1)
+ size = channel->block_size;
+ else {
+ if (count < 0)
+ size = -count;
+ else
+ size = count * channel->block_size;
+ }
+
+ location = ((ext2_loff_t) block * channel->block_size) + data->offset;
+ if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) {
+ retval = errno ? errno : EXT2_ET_LLSEEK_FAILED;
+ goto error_out;
+ }
+
+ actual = write(data->dev, buf, size);
+ if (actual != size) {
+ retval = EXT2_ET_SHORT_WRITE;
+ goto error_out;
+ }
+ return 0;
+
+error_out:
+ if (channel->write_error)
+ retval = (channel->write_error)(channel, block, count, buf,
+ size, actual, retval);
+ return retval;
+}
+
+
+/*
+ * Here we implement the cache functions
+ */
+
+/* Allocate the cache buffers */
+static errcode_t alloc_cache(io_channel channel,
+ struct unix_private_data *data)
+{
+ errcode_t retval;
+ struct unix_cache *cache;
+ int i;
+
+ data->access_time = 0;
+ for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) {
+ cache->block = 0;
+ cache->access_time = 0;
+ cache->dirty = 0;
+ cache->in_use = 0;
+ if ((retval = ext2fs_get_mem(channel->block_size,
+ &cache->buf)))
+ return retval;
+ }
+ return 0;
+}
+
+/* Free the cache buffers */
+static void free_cache(struct unix_private_data *data)
+{
+ struct unix_cache *cache;
+ int i;
+
+ data->access_time = 0;
+ for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) {
+ cache->block = 0;
+ cache->access_time = 0;
+ cache->dirty = 0;
+ cache->in_use = 0;
+ ext2fs_free_mem(&cache->buf);
+ cache->buf = 0;
+ }
+}
+
+#ifndef NO_IO_CACHE
+/*
+ * Try to find a block in the cache. If the block is not found, and
+ * eldest is a non-zero pointer, then fill in eldest with the cache
+ * entry to that should be reused.
+ */
+static struct unix_cache *find_cached_block(struct unix_private_data *data,
+ unsigned long block,
+ struct unix_cache **eldest)
+{
+ struct unix_cache *cache, *unused_cache, *oldest_cache;
+ int i;
+
+ unused_cache = oldest_cache = 0;
+ for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) {
+ if (!cache->in_use) {
+ if (!unused_cache)
+ unused_cache = cache;
+ continue;
+ }
+ if (cache->block == block) {
+ cache->access_time = ++data->access_time;
+ return cache;
+ }
+ if (!oldest_cache ||
+ (cache->access_time < oldest_cache->access_time))
+ oldest_cache = cache;
+ }
+ if (eldest)
+ *eldest = (unused_cache) ? unused_cache : oldest_cache;
+ return 0;
+}
+
+/*
+ * Reuse a particular cache entry for another block.
+ */
+static void reuse_cache(io_channel channel, struct unix_private_data *data,
+ struct unix_cache *cache, unsigned long block)
+{
+ if (cache->dirty && cache->in_use)
+ raw_write_blk(channel, data, cache->block, 1, cache->buf);
+
+ cache->in_use = 1;
+ cache->dirty = 0;
+ cache->block = block;
+ cache->access_time = ++data->access_time;
+}
+
+/*
+ * Flush all of the blocks in the cache
+ */
+static errcode_t flush_cached_blocks(io_channel channel,
+ struct unix_private_data *data,
+ int invalidate)
+
+{
+ struct unix_cache *cache;
+ errcode_t retval, retval2;
+ int i;
+
+ retval2 = 0;
+ for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) {
+ if (!cache->in_use)
+ continue;
+
+ if (invalidate)
+ cache->in_use = 0;
+
+ if (!cache->dirty)
+ continue;
+
+ retval = raw_write_blk(channel, data,
+ cache->block, 1, cache->buf);
+ if (retval)
+ retval2 = retval;
+ else
+ cache->dirty = 0;
+ }
+ return retval2;
+}
+#endif /* NO_IO_CACHE */
+
+static errcode_t unix_open(const char *name, int flags, io_channel *channel)
+{
+ io_channel io = NULL;
+ struct unix_private_data *data = NULL;
+ errcode_t retval;
+ int open_flags;
+ struct stat st;
+#ifdef __linux__
+ struct utsname ut;
+#endif
+
+ if (name == 0)
+ return EXT2_ET_BAD_DEVICE_NAME;
+ retval = ext2fs_get_mem(sizeof(struct struct_io_channel), &io);
+ if (retval)
+ return retval;
+ memset(io, 0, sizeof(struct struct_io_channel));
+ io->magic = EXT2_ET_MAGIC_IO_CHANNEL;
+ retval = ext2fs_get_mem(sizeof(struct unix_private_data), &data);
+ if (retval)
+ goto cleanup;
+
+ io->manager = unix_io_manager;
+ retval = ext2fs_get_mem(strlen(name)+1, &io->name);
+ if (retval)
+ goto cleanup;
+
+ strcpy(io->name, name);
+ io->private_data = data;
+ io->block_size = 1024;
+ io->read_error = 0;
+ io->write_error = 0;
+ io->refcount = 1;
+
+ memset(data, 0, sizeof(struct unix_private_data));
+ data->magic = EXT2_ET_MAGIC_UNIX_IO_CHANNEL;
+
+ if ((retval = alloc_cache(io, data)))
+ goto cleanup;
+
+ open_flags = (flags & IO_FLAG_RW) ? O_RDWR : O_RDONLY;
+#ifdef CONFIG_LFS
+ data->dev = open64(io->name, open_flags);
+#else
+ data->dev = open(io->name, open_flags);
+#endif
+ if (data->dev < 0) {
+ retval = errno;
+ goto cleanup;
+ }
+
+#ifdef __linux__
+#undef RLIM_INFINITY
+#if (defined(__alpha__) || ((defined(__sparc__) || defined(__mips__)) && (SIZEOF_LONG == 4)))
+#define RLIM_INFINITY ((unsigned long)(~0UL>>1))
+#else
+#define RLIM_INFINITY (~0UL)
+#endif
+ /*
+ * Work around a bug in 2.4.10-2.4.18 kernels where writes to
+ * block devices are wrongly getting hit by the filesize
+ * limit. This workaround isn't perfect, since it won't work
+ * if glibc wasn't built against 2.2 header files. (Sigh.)
+ *
+ */
+ if ((flags & IO_FLAG_RW) &&
+ (uname(&ut) == 0) &&
+ ((ut.release[0] == '2') && (ut.release[1] == '.') &&
+ (ut.release[2] == '4') && (ut.release[3] == '.') &&
+ (ut.release[4] == '1') && (ut.release[5] >= '0') &&
+ (ut.release[5] < '8')) &&
+ (fstat(data->dev, &st) == 0) &&
+ (S_ISBLK(st.st_mode))) {
+ struct rlimit rlim;
+
+ rlim.rlim_cur = rlim.rlim_max = (unsigned long) RLIM_INFINITY;
+ setrlimit(RLIMIT_FSIZE, &rlim);
+ getrlimit(RLIMIT_FSIZE, &rlim);
+ if (((unsigned long) rlim.rlim_cur) <
+ ((unsigned long) rlim.rlim_max)) {
+ rlim.rlim_cur = rlim.rlim_max;
+ setrlimit(RLIMIT_FSIZE, &rlim);
+ }
+ }
+#endif
+ *channel = io;
+ return 0;
+
+cleanup:
+ if (data) {
+ free_cache(data);
+ ext2fs_free_mem(&data);
+ }
+ ext2fs_free_mem(&io);
+ return retval;
+}
+
+static errcode_t unix_close(io_channel channel)
+{
+ struct unix_private_data *data;
+ errcode_t retval = 0;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct unix_private_data *) channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
+
+ if (--channel->refcount > 0)
+ return 0;
+
+#ifndef NO_IO_CACHE
+ retval = flush_cached_blocks(channel, data, 0);
+#endif
+
+ if (close(data->dev) < 0)
+ retval = errno;
+ free_cache(data);
+
+ ext2fs_free_mem(&channel->private_data);
+ ext2fs_free_mem(&channel->name);
+ ext2fs_free_mem(&channel);
+ return retval;
+}
+
+static errcode_t unix_set_blksize(io_channel channel, int blksize)
+{
+ struct unix_private_data *data;
+ errcode_t retval;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct unix_private_data *) channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
+
+ if (channel->block_size != blksize) {
+#ifndef NO_IO_CACHE
+ if ((retval = flush_cached_blocks(channel, data, 0)))
+ return retval;
+#endif
+
+ channel->block_size = blksize;
+ free_cache(data);
+ if ((retval = alloc_cache(channel, data)))
+ return retval;
+ }
+ return 0;
+}
+
+
+static errcode_t unix_read_blk(io_channel channel, unsigned long block,
+ int count, void *buf)
+{
+ struct unix_private_data *data;
+ struct unix_cache *cache, *reuse[READ_DIRECT_SIZE];
+ errcode_t retval;
+ char *cp;
+ int i, j;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct unix_private_data *) channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
+
+#ifdef NO_IO_CACHE
+ return raw_read_blk(channel, data, block, count, buf);
+#else
+ /*
+ * If we're doing an odd-sized read or a very large read,
+ * flush out the cache and then do a direct read.
+ */
+ if (count < 0 || count > WRITE_DIRECT_SIZE) {
+ if ((retval = flush_cached_blocks(channel, data, 0)))
+ return retval;
+ return raw_read_blk(channel, data, block, count, buf);
+ }
+
+ cp = buf;
+ while (count > 0) {
+ /* If it's in the cache, use it! */
+ if ((cache = find_cached_block(data, block, &reuse[0]))) {
+#ifdef DEBUG
+ printf("Using cached block %d\n", block);
+#endif
+ memcpy(cp, cache->buf, channel->block_size);
+ count--;
+ block++;
+ cp += channel->block_size;
+ continue;
+ }
+ /*
+ * Find the number of uncached blocks so we can do a
+ * single read request
+ */
+ for (i=1; i < count; i++)
+ if (find_cached_block(data, block+i, &reuse[i]))
+ break;
+#ifdef DEBUG
+ printf("Reading %d blocks starting at %d\n", i, block);
+#endif
+ if ((retval = raw_read_blk(channel, data, block, i, cp)))
+ return retval;
+
+ /* Save the results in the cache */
+ for (j=0; j < i; j++) {
+ count--;
+ cache = reuse[j];
+ reuse_cache(channel, data, cache, block++);
+ memcpy(cache->buf, cp, channel->block_size);
+ cp += channel->block_size;
+ }
+ }
+ return 0;
+#endif /* NO_IO_CACHE */
+}
+
+static errcode_t unix_write_blk(io_channel channel, unsigned long block,
+ int count, const void *buf)
+{
+ struct unix_private_data *data;
+ struct unix_cache *cache, *reuse;
+ errcode_t retval = 0;
+ const char *cp;
+ int writethrough;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct unix_private_data *) channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
+
+#ifdef NO_IO_CACHE
+ return raw_write_blk(channel, data, block, count, buf);
+#else
+ /*
+ * If we're doing an odd-sized write or a very large write,
+ * flush out the cache completely and then do a direct write.
+ */
+ if (count < 0 || count > WRITE_DIRECT_SIZE) {
+ if ((retval = flush_cached_blocks(channel, data, 1)))
+ return retval;
+ return raw_write_blk(channel, data, block, count, buf);
+ }
+
+ /*
+ * For a moderate-sized multi-block write, first force a write
+ * if we're in write-through cache mode, and then fill the
+ * cache with the blocks.
+ */
+ writethrough = channel->flags & CHANNEL_FLAGS_WRITETHROUGH;
+ if (writethrough)
+ retval = raw_write_blk(channel, data, block, count, buf);
+
+ cp = buf;
+ while (count > 0) {
+ cache = find_cached_block(data, block, &reuse);
+ if (!cache) {
+ cache = reuse;
+ reuse_cache(channel, data, cache, block);
+ }
+ memcpy(cache->buf, cp, channel->block_size);
+ cache->dirty = !writethrough;
+ count--;
+ block++;
+ cp += channel->block_size;
+ }
+ return retval;
+#endif /* NO_IO_CACHE */
+}
+
+static errcode_t unix_write_byte(io_channel channel, unsigned long offset,
+ int size, const void *buf)
+{
+ struct unix_private_data *data;
+ errcode_t retval = 0;
+ ssize_t actual;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct unix_private_data *) channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
+
+#ifndef NO_IO_CACHE
+ /*
+ * Flush out the cache completely
+ */
+ if ((retval = flush_cached_blocks(channel, data, 1)))
+ return retval;
+#endif
+
+ if (lseek(data->dev, offset + data->offset, SEEK_SET) < 0)
+ return errno;
+
+ actual = write(data->dev, buf, size);
+ if (actual != size)
+ return EXT2_ET_SHORT_WRITE;
+
+ return 0;
+}
+
+/*
+ * Flush data buffers to disk.
+ */
+static errcode_t unix_flush(io_channel channel)
+{
+ struct unix_private_data *data;
+ errcode_t retval = 0;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct unix_private_data *) channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
+
+#ifndef NO_IO_CACHE
+ retval = flush_cached_blocks(channel, data, 0);
+#endif
+ fsync(data->dev);
+ return retval;
+}
+
+static errcode_t unix_set_option(io_channel channel, const char *option,
+ const char *arg)
+{
+ struct unix_private_data *data;
+ unsigned long tmp;
+ char *end;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct unix_private_data *) channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
+
+ if (!strcmp(option, "offset")) {
+ if (!arg)
+ return EXT2_ET_INVALID_ARGUMENT;
+
+ tmp = strtoul(arg, &end, 0);
+ if (*end)
+ return EXT2_ET_INVALID_ARGUMENT;
+ data->offset = tmp;
+ return 0;
+ }
+ return EXT2_ET_INVALID_ARGUMENT;
+}
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/unlink.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/unlink.c
new file mode 100644
index 0000000..83ac271
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/unlink.c
@@ -0,0 +1,100 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * unlink.c --- delete links in a ext2fs directory
+ *
+ * Copyright (C) 1993, 1994, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+struct link_struct {
+ const char *name;
+ int namelen;
+ ext2_ino_t inode;
+ int flags;
+ struct ext2_dir_entry *prev;
+ int done;
+};
+
+#ifdef __TURBOC__
+# pragma argsused
+#endif
+static int unlink_proc(struct ext2_dir_entry *dirent,
+ int offset EXT2FS_ATTR((unused)),
+ int blocksize EXT2FS_ATTR((unused)),
+ char *buf EXT2FS_ATTR((unused)),
+ void *priv_data)
+{
+ struct link_struct *ls = (struct link_struct *) priv_data;
+ struct ext2_dir_entry *prev;
+
+ prev = ls->prev;
+ ls->prev = dirent;
+
+ if (ls->name) {
+ if ((dirent->name_len & 0xFF) != ls->namelen)
+ return 0;
+ if (strncmp(ls->name, dirent->name, dirent->name_len & 0xFF))
+ return 0;
+ }
+ if (ls->inode) {
+ if (dirent->inode != ls->inode)
+ return 0;
+ } else {
+ if (!dirent->inode)
+ return 0;
+ }
+
+ if (prev)
+ prev->rec_len += dirent->rec_len;
+ else
+ dirent->inode = 0;
+ ls->done++;
+ return DIRENT_ABORT|DIRENT_CHANGED;
+}
+
+#ifdef __TURBOC__
+ #pragma argsused
+#endif
+errcode_t ext2fs_unlink(ext2_filsys fs, ext2_ino_t dir,
+ const char *name, ext2_ino_t ino,
+ int flags EXT2FS_ATTR((unused)))
+{
+ errcode_t retval;
+ struct link_struct ls;
+
+ EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+ if (!name && !ino)
+ return EXT2_ET_INVALID_ARGUMENT;
+
+ if (!(fs->flags & EXT2_FLAG_RW))
+ return EXT2_ET_RO_FILSYS;
+
+ ls.name = name;
+ ls.namelen = name ? strlen(name) : 0;
+ ls.inode = ino;
+ ls.flags = 0;
+ ls.done = 0;
+ ls.prev = 0;
+
+ retval = ext2fs_dir_iterate(fs, dir, DIRENT_FLAG_INCLUDE_EMPTY,
+ 0, unlink_proc, &ls);
+ if (retval)
+ return retval;
+
+ return (ls.done) ? 0 : EXT2_ET_DIR_NO_SPACE;
+}
+
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/valid_blk.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/valid_blk.c
new file mode 100644
index 0000000..8ed77ae
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/valid_blk.c
@@ -0,0 +1,57 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * valid_blk.c --- does the inode have valid blocks?
+ *
+ * Copyright 1997 by Theodore Ts'o
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ *
+ */
+
+#include <stdio.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <string.h>
+#include <time.h>
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+/*
+ * This function returns 1 if the inode's block entries actually
+ * contain block entries.
+ */
+int ext2fs_inode_has_valid_blocks(struct ext2_inode *inode)
+{
+ /*
+ * Only directories, regular files, and some symbolic links
+ * have valid block entries.
+ */
+ if (!LINUX_S_ISDIR(inode->i_mode) && !LINUX_S_ISREG(inode->i_mode) &&
+ !LINUX_S_ISLNK(inode->i_mode))
+ return 0;
+
+ /*
+ * If the symbolic link is a "fast symlink", then the symlink
+ * target is stored in the block entries.
+ */
+ if (LINUX_S_ISLNK (inode->i_mode)) {
+ if (inode->i_file_acl == 0) {
+ /* With no EA block, we can rely on i_blocks */
+ if (inode->i_blocks == 0)
+ return 0;
+ } else {
+ /* With an EA block, life gets more tricky */
+ if (inode->i_size >= EXT2_N_BLOCKS*4)
+ return 1; /* definitely using i_block[] */
+ if (inode->i_size > 4 && inode->i_block[1] == 0)
+ return 1; /* definitely using i_block[] */
+ return 0; /* Probably a fast symlink */
+ }
+ }
+ return 1;
+}
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/version.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/version.c
new file mode 100644
index 0000000..d2981e8
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/version.c
@@ -0,0 +1,51 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * version.c --- Return the version of the ext2 library
+ *
+ * Copyright (C) 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+static const char *lib_version = E2FSPROGS_VERSION;
+static const char *lib_date = E2FSPROGS_DATE;
+
+int ext2fs_parse_version_string(const char *ver_string)
+{
+ const char *cp;
+ int version = 0;
+
+ for (cp = ver_string; *cp; cp++) {
+ if (*cp == '.')
+ continue;
+ if (!isdigit(*cp))
+ break;
+ version = (version * 10) + (*cp - '0');
+ }
+ return version;
+}
+
+
+int ext2fs_get_library_version(const char **ver_string,
+ const char **date_string)
+{
+ if (ver_string)
+ *ver_string = lib_version;
+ if (date_string)
+ *date_string = lib_date;
+
+ return ext2fs_parse_version_string(lib_version);
+}
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/write_bb_file.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/write_bb_file.c
new file mode 100644
index 0000000..5b19eef
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/ext2fs/write_bb_file.c
@@ -0,0 +1,35 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * write_bb_file.c --- write a list of bad blocks to a FILE *
+ *
+ * Copyright (C) 1994, 1995 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+errcode_t ext2fs_write_bb_FILE(ext2_badblocks_list bb_list,
+ unsigned int flags EXT2FS_ATTR((unused)),
+ FILE *f)
+{
+ badblocks_iterate bb_iter;
+ blk_t blk;
+ errcode_t retval;
+
+ retval = ext2fs_badblocks_list_iterate_begin(bb_list, &bb_iter);
+ if (retval)
+ return retval;
+
+ while (ext2fs_badblocks_list_iterate(bb_iter, &blk)) {
+ fprintf(f, "%d\n", blk);
+ }
+ ext2fs_badblocks_list_iterate_end(bb_iter);
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/fsck.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/fsck.c
new file mode 100644
index 0000000..6e8a637
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/fsck.c
@@ -0,0 +1,1392 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * pfsck --- A generic, parallelizing front-end for the fsck program.
+ * It will automatically try to run fsck programs in parallel if the
+ * devices are on separate spindles. It is based on the same ideas as
+ * the generic front end for fsck by David Engel and Fred van Kempen,
+ * but it has been completely rewritten from scratch to support
+ * parallel execution.
+ *
+ * Written by Theodore Ts'o, <tytso@mit.edu>
+ *
+ * Miquel van Smoorenburg (miquels@drinkel.ow.org) 20-Oct-1994:
+ * o Changed -t fstype to behave like with mount when -A (all file
+ * systems) or -M (like mount) is specified.
+ * o fsck looks if it can find the fsck.type program to decide
+ * if it should ignore the fs type. This way more fsck programs
+ * can be added without changing this front-end.
+ * o -R flag skip root file system.
+ *
+ * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
+ * 2001, 2002, 2003, 2004, 2005 by Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <limits.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <time.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <paths.h>
+#include <unistd.h>
+#include <errno.h>
+#include <signal.h>
+
+#include "fsck.h"
+#include "blkid/blkid.h"
+
+#include "e2fsbb.h"
+
+#include "busybox.h"
+
+#ifndef _PATH_MNTTAB
+#define _PATH_MNTTAB "/etc/fstab"
+#endif
+
+/*
+ * fsck.h
+ */
+
+#ifndef DEFAULT_FSTYPE
+#define DEFAULT_FSTYPE "ext2"
+#endif
+
+#define MAX_DEVICES 32
+#define MAX_ARGS 32
+
+/*
+ * Internal structure for mount tabel entries.
+ */
+
+struct fs_info {
+ char *device;
+ char *mountpt;
+ char *type;
+ char *opts;
+ int freq;
+ int passno;
+ int flags;
+ struct fs_info *next;
+};
+
+#define FLAG_DONE 1
+#define FLAG_PROGRESS 2
+
+/*
+ * Structure to allow exit codes to be stored
+ */
+struct fsck_instance {
+ int pid;
+ int flags;
+ int exit_status;
+ time_t start_time;
+ char * prog;
+ char * type;
+ char * device;
+ char * base_device;
+ struct fsck_instance *next;
+};
+
+/*
+ * base_device.c
+ *
+ * Return the "base device" given a particular device; this is used to
+ * assure that we only fsck one partition on a particular drive at any
+ * one time. Otherwise, the disk heads will be seeking all over the
+ * place. If the base device cannot be determined, return NULL.
+ *
+ * The base_device() function returns an allocated string which must
+ * be freed.
+ *
+ */
+
+
+#ifdef CONFIG_FEATURE_DEVFS
+/*
+ * Required for the uber-silly devfs /dev/ide/host1/bus2/target3/lun3
+ * pathames.
+ */
+static const char * const devfs_hier[] = {
+ "host", "bus", "target", "lun", 0
+};
+#endif
+
+static char *base_device(const char *device)
+{
+ char *str, *cp;
+#ifdef CONFIG_FEATURE_DEVFS
+ const char * const *hier;
+ const char *disk;
+ int len;
+#endif
+
+ cp = str = xstrdup(device);
+
+ /* Skip over /dev/; if it's not present, give up. */
+ if (strncmp(cp, "/dev/", 5) != 0)
+ goto errout;
+ cp += 5;
+
+ /*
+ * For md devices, we treat them all as if they were all
+ * on one disk, since we don't know how to parallelize them.
+ */
+ if (cp[0] == 'm' && cp[1] == 'd') {
+ *(cp+2) = 0;
+ return str;
+ }
+
+ /* Handle DAC 960 devices */
+ if (strncmp(cp, "rd/", 3) == 0) {
+ cp += 3;
+ if (cp[0] != 'c' || cp[2] != 'd' ||
+ !isdigit(cp[1]) || !isdigit(cp[3]))
+ goto errout;
+ *(cp+4) = 0;
+ return str;
+ }
+
+ /* Now let's handle /dev/hd* and /dev/sd* devices.... */
+ if ((cp[0] == 'h' || cp[0] == 's') && (cp[1] == 'd')) {
+ cp += 2;
+ /* If there's a single number after /dev/hd, skip it */
+ if (isdigit(*cp))
+ cp++;
+ /* What follows must be an alpha char, or give up */
+ if (!isalpha(*cp))
+ goto errout;
+ *(cp + 1) = 0;
+ return str;
+ }
+
+#ifdef CONFIG_FEATURE_DEVFS
+ /* Now let's handle devfs (ugh) names */
+ len = 0;
+ if (strncmp(cp, "ide/", 4) == 0)
+ len = 4;
+ if (strncmp(cp, "scsi/", 5) == 0)
+ len = 5;
+ if (len) {
+ cp += len;
+ /*
+ * Now we proceed down the expected devfs hierarchy.
+ * i.e., .../host1/bus2/target3/lun4/...
+ * If we don't find the expected token, followed by
+ * some number of digits at each level, abort.
+ */
+ for (hier = devfs_hier; *hier; hier++) {
+ len = strlen(*hier);
+ if (strncmp(cp, *hier, len) != 0)
+ goto errout;
+ cp += len;
+ while (*cp != '/' && *cp != 0) {
+ if (!isdigit(*cp))
+ goto errout;
+ cp++;
+ }
+ cp++;
+ }
+ *(cp - 1) = 0;
+ return str;
+ }
+
+ /* Now handle devfs /dev/disc or /dev/disk names */
+ disk = 0;
+ if (strncmp(cp, "discs/", 6) == 0)
+ disk = "disc";
+ else if (strncmp(cp, "disks/", 6) == 0)
+ disk = "disk";
+ if (disk) {
+ cp += 6;
+ if (strncmp(cp, disk, 4) != 0)
+ goto errout;
+ cp += 4;
+ while (*cp != '/' && *cp != 0) {
+ if (!isdigit(*cp))
+ goto errout;
+ cp++;
+ }
+ *cp = 0;
+ return str;
+ }
+#endif
+
+errout:
+ free(str);
+ return NULL;
+}
+
+
+static const char * const ignored_types[] = {
+ "ignore",
+ "iso9660",
+ "nfs",
+ "proc",
+ "sw",
+ "swap",
+ "tmpfs",
+ "devpts",
+ NULL
+};
+
+static const char * const really_wanted[] = {
+ "minix",
+ "ext2",
+ "ext3",
+ "jfs",
+ "reiserfs",
+ "xiafs",
+ "xfs",
+ NULL
+};
+
+#define BASE_MD "/dev/md"
+
+/*
+ * Global variables for options
+ */
+static char *devices[MAX_DEVICES];
+static char *args[MAX_ARGS];
+static int num_devices, num_args;
+
+static int verbose;
+static int doall;
+static int noexecute;
+static int serialize;
+static int skip_root;
+static int like_mount;
+static int notitle;
+static int parallel_root;
+static int progress;
+static int progress_fd;
+static int force_all_parallel;
+static int num_running;
+static int max_running;
+static volatile int cancel_requested;
+static int kill_sent;
+static char *fstype;
+static struct fs_info *filesys_info, *filesys_last;
+static struct fsck_instance *instance_list;
+static char *fsck_path;
+static blkid_cache cache;
+
+static char *string_copy(const char *s)
+{
+ char *ret;
+
+ if (!s)
+ return 0;
+ ret = strdup(s);
+ return ret;
+}
+
+static int string_to_int(const char *s)
+{
+ long l;
+ char *p;
+
+ l = strtol(s, &p, 0);
+ if (*p || l == LONG_MIN || l == LONG_MAX || l < 0 || l > INT_MAX)
+ return -1;
+ else
+ return (int) l;
+}
+
+static char *skip_over_blank(char *cp)
+{
+ while (*cp && isspace(*cp))
+ cp++;
+ return cp;
+}
+
+static char *skip_over_word(char *cp)
+{
+ while (*cp && !isspace(*cp))
+ cp++;
+ return cp;
+}
+
+static void strip_line(char *line)
+{
+ char *p;
+
+ while (*line) {
+ p = line + strlen(line) - 1;
+ if ((*p == '\n') || (*p == '\r'))
+ *p = 0;
+ else
+ break;
+ }
+}
+
+static char *parse_word(char **buf)
+{
+ char *word, *next;
+
+ word = *buf;
+ if (*word == 0)
+ return 0;
+
+ word = skip_over_blank(word);
+ next = skip_over_word(word);
+ if (*next)
+ *next++ = 0;
+ *buf = next;
+ return word;
+}
+
+static void parse_escape(char *word)
+{
+ char *q, c;
+ const char *p;
+
+ if (!word)
+ return;
+
+ for (p = q = word; *p; q++) {
+ c = *p++;
+ if (c != '\\') {
+ *q = c;
+ } else {
+ *q = bb_process_escape_sequence(&p);
+ }
+ }
+ *q = 0;
+}
+
+static void free_instance(struct fsck_instance *i)
+{
+ if (i->prog)
+ free(i->prog);
+ if (i->device)
+ free(i->device);
+ if (i->base_device)
+ free(i->base_device);
+ free(i);
+}
+
+static struct fs_info *create_fs_device(const char *device, const char *mntpnt,
+ const char *type, const char *opts,
+ int freq, int passno)
+{
+ struct fs_info *fs;
+
+ if (!(fs = malloc(sizeof(struct fs_info))))
+ return NULL;
+
+ fs->device = string_copy(device);
+ fs->mountpt = string_copy(mntpnt);
+ fs->type = string_copy(type);
+ fs->opts = string_copy(opts ? opts : "");
+ fs->freq = freq;
+ fs->passno = passno;
+ fs->flags = 0;
+ fs->next = NULL;
+
+ if (!filesys_info)
+ filesys_info = fs;
+ else
+ filesys_last->next = fs;
+ filesys_last = fs;
+
+ return fs;
+}
+
+
+
+static int parse_fstab_line(char *line, struct fs_info **ret_fs)
+{
+ char *dev, *device, *mntpnt, *type, *opts, *freq, *passno, *cp;
+ struct fs_info *fs;
+
+ *ret_fs = 0;
+ strip_line(line);
+ if ((cp = strchr(line, '#')))
+ *cp = 0; /* Ignore everything after the comment char */
+ cp = line;
+
+ device = parse_word(&cp);
+ mntpnt = parse_word(&cp);
+ type = parse_word(&cp);
+ opts = parse_word(&cp);
+ freq = parse_word(&cp);
+ passno = parse_word(&cp);
+
+ if (!device)
+ return 0; /* Allow blank lines */
+
+ if (!mntpnt || !type)
+ return -1;
+
+ parse_escape(device);
+ parse_escape(mntpnt);
+ parse_escape(type);
+ parse_escape(opts);
+ parse_escape(freq);
+ parse_escape(passno);
+
+ dev = blkid_get_devname(cache, device, NULL);
+ if (dev)
+ device = dev;
+
+ if (strchr(type, ','))
+ type = 0;
+
+ fs = create_fs_device(device, mntpnt, type ? type : "auto", opts,
+ freq ? atoi(freq) : -1,
+ passno ? atoi(passno) : -1);
+ if (dev)
+ free(dev);
+
+ if (!fs)
+ return -1;
+ *ret_fs = fs;
+ return 0;
+}
+
+static void interpret_type(struct fs_info *fs)
+{
+ char *t;
+
+ if (strcmp(fs->type, "auto") != 0)
+ return;
+ t = blkid_get_tag_value(cache, "TYPE", fs->device);
+ if (t) {
+ free(fs->type);
+ fs->type = t;
+ }
+}
+
+/*
+ * Load the filesystem database from /etc/fstab
+ */
+static void load_fs_info(const char *filename)
+{
+ FILE *f;
+ char buf[1024];
+ int lineno = 0;
+ int old_fstab = 1;
+ struct fs_info *fs;
+
+ if ((f = fopen(filename, "r")) == NULL) {
+ bb_perror_msg("WARNING: cannot open %s", filename);
+ return;
+ }
+ while (!feof(f)) {
+ lineno++;
+ if (!fgets(buf, sizeof(buf), f))
+ break;
+ buf[sizeof(buf)-1] = 0;
+ if (parse_fstab_line(buf, &fs) < 0) {
+ bb_error_msg("WARNING: bad format "
+ "on line %d of %s\n", lineno, filename);
+ continue;
+ }
+ if (!fs)
+ continue;
+ if (fs->passno < 0)
+ fs->passno = 0;
+ else
+ old_fstab = 0;
+ }
+
+ fclose(f);
+
+ if (old_fstab) {
+ fputs("\007\007\007"
+ "WARNING: Your /etc/fstab does not contain the fsck passno\n"
+ " field. I will kludge around things for you, but you\n"
+ " should fix your /etc/fstab file as soon as you can.\n\n", stderr);
+
+ for (fs = filesys_info; fs; fs = fs->next) {
+ fs->passno = 1;
+ }
+ }
+}
+
+/* Lookup filesys in /etc/fstab and return the corresponding entry. */
+static struct fs_info *lookup(char *filesys)
+{
+ struct fs_info *fs;
+
+ /* No filesys name given. */
+ if (filesys == NULL)
+ return NULL;
+
+ for (fs = filesys_info; fs; fs = fs->next) {
+ if (!strcmp(filesys, fs->device) ||
+ (fs->mountpt && !strcmp(filesys, fs->mountpt)))
+ break;
+ }
+
+ return fs;
+}
+
+/* Find fsck program for a given fs type. */
+static char *find_fsck(char *type)
+{
+ char *s;
+ const char *tpl;
+ char *p = string_copy(fsck_path);
+ struct stat st;
+
+ /* Are we looking for a program or just a type? */
+ tpl = (strncmp(type, "fsck.", 5) ? "%s/fsck.%s" : "%s/%s");
+
+ for(s = strtok(p, ":"); s; s = strtok(NULL, ":")) {
+ s = xasprintf(tpl, s, type);
+ if (stat(s, &st) == 0) break;
+ free(s);
+ }
+ free(p);
+ return s;
+}
+
+static int progress_active(void)
+{
+ struct fsck_instance *inst;
+
+ for (inst = instance_list; inst; inst = inst->next) {
+ if (inst->flags & FLAG_DONE)
+ continue;
+ if (inst->flags & FLAG_PROGRESS)
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Execute a particular fsck program, and link it into the list of
+ * child processes we are waiting for.
+ */
+static int execute(const char *type, const char *device, const char *mntpt,
+ int interactive)
+{
+ char *s, *argv[80];
+ char *prog;
+ int argc, i;
+ struct fsck_instance *inst, *p;
+ pid_t pid;
+
+ inst = malloc(sizeof(struct fsck_instance));
+ if (!inst)
+ return ENOMEM;
+ memset(inst, 0, sizeof(struct fsck_instance));
+
+ prog = xasprintf("fsck.%s", type);
+ argv[0] = prog;
+ argc = 1;
+
+ for (i=0; i <num_args; i++)
+ argv[argc++] = string_copy(args[i]);
+
+ if (progress && !progress_active()) {
+ if ((strcmp(type, "ext2") == 0) ||
+ (strcmp(type, "ext3") == 0)) {
+ char tmp[80];
+ snprintf(tmp, 80, "-C%d", progress_fd);
+ argv[argc++] = string_copy(tmp);
+ inst->flags |= FLAG_PROGRESS;
+ }
+ }
+
+ argv[argc++] = string_copy(device);
+ argv[argc] = 0;
+
+ s = find_fsck(prog);
+ if (s == NULL) {
+ bb_error_msg("%s: not found", prog);
+ return ENOENT;
+ }
+
+ if (verbose || noexecute) {
+ printf("[%s (%d) -- %s] ", s, num_running,
+ mntpt ? mntpt : device);
+ for (i=0; i < argc; i++)
+ printf("%s ", argv[i]);
+ puts("");
+ }
+
+ /* Fork and execute the correct program. */
+ if (noexecute)
+ pid = -1;
+ else if ((pid = fork()) < 0) {
+ perror("fork");
+ return errno;
+ } else if (pid == 0) {
+ if (!interactive)
+ close(0);
+ (void) execv(s, argv);
+ bb_perror_msg_and_die("%s", argv[0]);
+ }
+
+ for (i = 1; i < argc; i++)
+ free(argv[i]);
+
+ free(s);
+ inst->pid = pid;
+ inst->prog = prog;
+ inst->type = string_copy(type);
+ inst->device = string_copy(device);
+ inst->base_device = base_device(device);
+ inst->start_time = time(0);
+ inst->next = NULL;
+
+ /*
+ * Find the end of the list, so we add the instance on at the end.
+ */
+ for (p = instance_list; p && p->next; p = p->next);
+
+ if (p)
+ p->next = inst;
+ else
+ instance_list = inst;
+
+ return 0;
+}
+
+/*
+ * Send a signal to all outstanding fsck child processes
+ */
+static int kill_all(int signum)
+{
+ struct fsck_instance *inst;
+ int n = 0;
+
+ for (inst = instance_list; inst; inst = inst->next) {
+ if (inst->flags & FLAG_DONE)
+ continue;
+ kill(inst->pid, signum);
+ n++;
+ }
+ return n;
+}
+
+/*
+ * Wait for one child process to exit; when it does, unlink it from
+ * the list of executing child processes, and return it.
+ */
+static struct fsck_instance *wait_one(int flags)
+{
+ int status;
+ int sig;
+ struct fsck_instance *inst, *inst2, *prev;
+ pid_t pid;
+
+ if (!instance_list)
+ return NULL;
+
+ if (noexecute) {
+ inst = instance_list;
+ prev = 0;
+#ifdef RANDOM_DEBUG
+ while (inst->next && (random() & 1)) {
+ prev = inst;
+ inst = inst->next;
+ }
+#endif
+ inst->exit_status = 0;
+ goto ret_inst;
+ }
+
+ /*
+ * gcc -Wall fails saving throw against stupidity
+ * (inst and prev are thought to be uninitialized variables)
+ */
+ inst = prev = NULL;
+
+ do {
+ pid = waitpid(-1, &status, flags);
+ if (cancel_requested && !kill_sent) {
+ kill_all(SIGTERM);
+ kill_sent++;
+ }
+ if ((pid == 0) && (flags & WNOHANG))
+ return NULL;
+ if (pid < 0) {
+ if ((errno == EINTR) || (errno == EAGAIN))
+ continue;
+ if (errno == ECHILD) {
+ bb_error_msg("wait: no more child process?!?");
+ return NULL;
+ }
+ perror("wait");
+ continue;
+ }
+ for (prev = 0, inst = instance_list;
+ inst;
+ prev = inst, inst = inst->next) {
+ if (inst->pid == pid)
+ break;
+ }
+ } while (!inst);
+
+ if (WIFEXITED(status))
+ status = WEXITSTATUS(status);
+ else if (WIFSIGNALED(status)) {
+ sig = WTERMSIG(status);
+ if (sig == SIGINT) {
+ status = EXIT_UNCORRECTED;
+ } else {
+ printf("Warning... %s for device %s exited "
+ "with signal %d.\n",
+ inst->prog, inst->device, sig);
+ status = EXIT_ERROR;
+ }
+ } else {
+ printf("%s %s: status is %x, should never happen.\n",
+ inst->prog, inst->device, status);
+ status = EXIT_ERROR;
+ }
+ inst->exit_status = status;
+ if (progress && (inst->flags & FLAG_PROGRESS) &&
+ !progress_active()) {
+ for (inst2 = instance_list; inst2; inst2 = inst2->next) {
+ if (inst2->flags & FLAG_DONE)
+ continue;
+ if (strcmp(inst2->type, "ext2") &&
+ strcmp(inst2->type, "ext3"))
+ continue;
+ /*
+ * If we've just started the fsck, wait a tiny
+ * bit before sending the kill, to give it
+ * time to set up the signal handler
+ */
+ if (inst2->start_time < time(0)+2) {
+ if (fork() == 0) {
+ sleep(1);
+ kill(inst2->pid, SIGUSR1);
+ exit(0);
+ }
+ } else
+ kill(inst2->pid, SIGUSR1);
+ inst2->flags |= FLAG_PROGRESS;
+ break;
+ }
+ }
+ret_inst:
+ if (prev)
+ prev->next = inst->next;
+ else
+ instance_list = inst->next;
+ if (verbose > 1)
+ printf("Finished with %s (exit status %d)\n",
+ inst->device, inst->exit_status);
+ num_running--;
+ return inst;
+}
+
+#define FLAG_WAIT_ALL 0
+#define FLAG_WAIT_ATLEAST_ONE 1
+/*
+ * Wait until all executing child processes have exited; return the
+ * logical OR of all of their exit code values.
+ */
+static int wait_many(int flags)
+{
+ struct fsck_instance *inst;
+ int global_status = 0;
+ int wait_flags = 0;
+
+ while ((inst = wait_one(wait_flags))) {
+ global_status |= inst->exit_status;
+ free_instance(inst);
+#ifdef RANDOM_DEBUG
+ if (noexecute && (flags & WNOHANG) && !(random() % 3))
+ break;
+#endif
+ if (flags & FLAG_WAIT_ATLEAST_ONE)
+ wait_flags = WNOHANG;
+ }
+ return global_status;
+}
+
+/*
+ * Run the fsck program on a particular device
+ *
+ * If the type is specified using -t, and it isn't prefixed with "no"
+ * (as in "noext2") and only one filesystem type is specified, then
+ * use that type regardless of what is specified in /etc/fstab.
+ *
+ * If the type isn't specified by the user, then use either the type
+ * specified in /etc/fstab, or DEFAULT_FSTYPE.
+ */
+static void fsck_device(struct fs_info *fs, int interactive)
+{
+ const char *type;
+ int retval;
+
+ interpret_type(fs);
+
+ if (strcmp(fs->type, "auto") != 0)
+ type = fs->type;
+ else if (fstype && strncmp(fstype, "no", 2) &&
+ strncmp(fstype, "opts=", 5) && strncmp(fstype, "loop", 4) &&
+ !strchr(fstype, ','))
+ type = fstype;
+ else
+ type = DEFAULT_FSTYPE;
+
+ num_running++;
+ retval = execute(type, fs->device, fs->mountpt, interactive);
+ if (retval) {
+ bb_error_msg("error %d while executing fsck.%s for %s",
+ retval, type, fs->device);
+ num_running--;
+ }
+}
+
+
+/*
+ * Deal with the fsck -t argument.
+ */
+struct fs_type_compile {
+ char **list;
+ int *type;
+ int negate;
+} fs_type_compiled;
+
+#define FS_TYPE_NORMAL 0
+#define FS_TYPE_OPT 1
+#define FS_TYPE_NEGOPT 2
+
+static const char fs_type_syntax_error[] =
+"Either all or none of the filesystem types passed to -t must be prefixed\n"
+ "with 'no' or '!'.";
+
+static void compile_fs_type(char *fs_type, struct fs_type_compile *cmp)
+{
+ char *cp, *list, *s;
+ int num = 2;
+ int negate, first_negate = 1;
+
+ if (fs_type) {
+ for (cp=fs_type; *cp; cp++) {
+ if (*cp == ',')
+ num++;
+ }
+ }
+
+ cmp->list = xzalloc(num * sizeof(char *));
+ cmp->type = xzalloc(num * sizeof(int));
+ cmp->negate = 0;
+
+ if (!fs_type)
+ return;
+
+ list = string_copy(fs_type);
+ num = 0;
+ s = strtok(list, ",");
+ while(s) {
+ negate = 0;
+ if (strncmp(s, "no", 2) == 0) {
+ s += 2;
+ negate = 1;
+ } else if (*s == '!') {
+ s++;
+ negate = 1;
+ }
+ if (strcmp(s, "loop") == 0)
+ /* loop is really short-hand for opts=loop */
+ goto loop_special_case;
+ else if (strncmp(s, "opts=", 5) == 0) {
+ s += 5;
+ loop_special_case:
+ cmp->type[num] = negate ? FS_TYPE_NEGOPT : FS_TYPE_OPT;
+ } else {
+ if (first_negate) {
+ cmp->negate = negate;
+ first_negate = 0;
+ }
+ if ((negate && !cmp->negate) ||
+ (!negate && cmp->negate)) {
+ bb_error_msg_and_die("%s", fs_type_syntax_error);
+ }
+ }
+ cmp->list[num++] = string_copy(s);
+ s = strtok(NULL, ",");
+ }
+ free(list);
+}
+
+/*
+ * This function returns true if a particular option appears in a
+ * comma-delimited options list
+ */
+static int opt_in_list(char *opt, char *optlist)
+{
+ char *list, *s;
+
+ if (!optlist)
+ return 0;
+ list = string_copy(optlist);
+
+ s = strtok(list, ",");
+ while(s) {
+ if (strcmp(s, opt) == 0) {
+ free(list);
+ return 1;
+ }
+ s = strtok(NULL, ",");
+ }
+ free(list);
+ return 0;
+}
+
+/* See if the filesystem matches the criteria given by the -t option */
+static int fs_match(struct fs_info *fs, struct fs_type_compile *cmp)
+{
+ int n, ret = 0, checked_type = 0;
+ char *cp;
+
+ if (cmp->list == 0 || cmp->list[0] == 0)
+ return 1;
+
+ for (n=0; (cp = cmp->list[n]); n++) {
+ switch (cmp->type[n]) {
+ case FS_TYPE_NORMAL:
+ checked_type++;
+ if (strcmp(cp, fs->type) == 0) {
+ ret = 1;
+ }
+ break;
+ case FS_TYPE_NEGOPT:
+ if (opt_in_list(cp, fs->opts))
+ return 0;
+ break;
+ case FS_TYPE_OPT:
+ if (!opt_in_list(cp, fs->opts))
+ return 0;
+ break;
+ }
+ }
+ if (checked_type == 0)
+ return 1;
+ return (cmp->negate ? !ret : ret);
+}
+
+/* Check if we should ignore this filesystem. */
+static int ignore(struct fs_info *fs)
+{
+ int wanted;
+ char *s;
+
+ /*
+ * If the pass number is 0, ignore it.
+ */
+ if (fs->passno == 0)
+ return 1;
+
+ interpret_type(fs);
+
+ /*
+ * If a specific fstype is specified, and it doesn't match,
+ * ignore it.
+ */
+ if (!fs_match(fs, &fs_type_compiled)) return 1;
+
+ /* Are we ignoring this type? */
+ if (index_in_str_array(ignored_types, fs->type) >= 0)
+ return 1;
+
+ /* Do we really really want to check this fs? */
+ wanted = index_in_str_array(really_wanted, fs->type) >= 0;
+
+ /* See if the <fsck.fs> program is available. */
+ s = find_fsck(fs->type);
+ if (s == NULL) {
+ if (wanted)
+ bb_error_msg("cannot check %s: fsck.%s not found",
+ fs->device, fs->type);
+ return 1;
+ }
+ free(s);
+
+ /* We can and want to check this file system type. */
+ return 0;
+}
+
+/*
+ * Returns TRUE if a partition on the same disk is already being
+ * checked.
+ */
+static int device_already_active(char *device)
+{
+ struct fsck_instance *inst;
+ char *base;
+
+ if (force_all_parallel)
+ return 0;
+
+#ifdef BASE_MD
+ /* Don't check a soft raid disk with any other disk */
+ if (instance_list &&
+ (!strncmp(instance_list->device, BASE_MD, sizeof(BASE_MD)-1) ||
+ !strncmp(device, BASE_MD, sizeof(BASE_MD)-1)))
+ return 1;
+#endif
+
+ base = base_device(device);
+ /*
+ * If we don't know the base device, assume that the device is
+ * already active if there are any fsck instances running.
+ */
+ if (!base)
+ return (instance_list != 0);
+ for (inst = instance_list; inst; inst = inst->next) {
+ if (!inst->base_device || !strcmp(base, inst->base_device)) {
+ free(base);
+ return 1;
+ }
+ }
+ free(base);
+ return 0;
+}
+
+/* Check all file systems, using the /etc/fstab table. */
+static int check_all(void)
+{
+ struct fs_info *fs = NULL;
+ int status = EXIT_OK;
+ int not_done_yet = 1;
+ int passno = 1;
+ int pass_done;
+
+ if (verbose)
+ fputs("Checking all file systems.\n", stdout);
+
+ /*
+ * Do an initial scan over the filesystem; mark filesystems
+ * which should be ignored as done, and resolve any "auto"
+ * filesystem types (done as a side-effect of calling ignore()).
+ */
+ for (fs = filesys_info; fs; fs = fs->next) {
+ if (ignore(fs))
+ fs->flags |= FLAG_DONE;
+ }
+
+ /*
+ * Find and check the root filesystem.
+ */
+ if (!parallel_root) {
+ for (fs = filesys_info; fs; fs = fs->next) {
+ if (LONE_CHAR(fs->mountpt, '/'))
+ break;
+ }
+ if (fs) {
+ if (!skip_root && !ignore(fs)) {
+ fsck_device(fs, 1);
+ status |= wait_many(FLAG_WAIT_ALL);
+ if (status > EXIT_NONDESTRUCT)
+ return status;
+ }
+ fs->flags |= FLAG_DONE;
+ }
+ }
+ /*
+ * This is for the bone-headed user who enters the root
+ * filesystem twice. Skip root will skep all root entries.
+ */
+ if (skip_root)
+ for (fs = filesys_info; fs; fs = fs->next)
+ if (LONE_CHAR(fs->mountpt, '/'))
+ fs->flags |= FLAG_DONE;
+
+ while (not_done_yet) {
+ not_done_yet = 0;
+ pass_done = 1;
+
+ for (fs = filesys_info; fs; fs = fs->next) {
+ if (cancel_requested)
+ break;
+ if (fs->flags & FLAG_DONE)
+ continue;
+ /*
+ * If the filesystem's pass number is higher
+ * than the current pass number, then we don't
+ * do it yet.
+ */
+ if (fs->passno > passno) {
+ not_done_yet++;
+ continue;
+ }
+ /*
+ * If a filesystem on a particular device has
+ * already been spawned, then we need to defer
+ * this to another pass.
+ */
+ if (device_already_active(fs->device)) {
+ pass_done = 0;
+ continue;
+ }
+ /*
+ * Spawn off the fsck process
+ */
+ fsck_device(fs, serialize);
+ fs->flags |= FLAG_DONE;
+
+ /*
+ * Only do one filesystem at a time, or if we
+ * have a limit on the number of fsck's extant
+ * at one time, apply that limit.
+ */
+ if (serialize ||
+ (max_running && (num_running >= max_running))) {
+ pass_done = 0;
+ break;
+ }
+ }
+ if (cancel_requested)
+ break;
+ if (verbose > 1)
+ printf("--waiting-- (pass %d)\n", passno);
+ status |= wait_many(pass_done ? FLAG_WAIT_ALL :
+ FLAG_WAIT_ATLEAST_ONE);
+ if (pass_done) {
+ if (verbose > 1)
+ printf("----------------------------------\n");
+ passno++;
+ } else
+ not_done_yet++;
+ }
+ if (cancel_requested && !kill_sent) {
+ kill_all(SIGTERM);
+ kill_sent++;
+ }
+ status |= wait_many(FLAG_WAIT_ATLEAST_ONE);
+ return status;
+}
+
+static void signal_cancel(int sig FSCK_ATTR((unused)))
+{
+ cancel_requested++;
+}
+
+static void PRS(int argc, char *argv[])
+{
+ int i, j;
+ char *arg, *dev, *tmp = 0;
+ char options[128];
+ int opt = 0;
+ int opts_for_fsck = 0;
+ struct sigaction sa;
+
+ /*
+ * Set up signal action
+ */
+ memset(&sa, 0, sizeof(struct sigaction));
+ sa.sa_handler = signal_cancel;
+ sigaction(SIGINT, &sa, 0);
+ sigaction(SIGTERM, &sa, 0);
+
+ num_devices = 0;
+ num_args = 0;
+ instance_list = 0;
+
+ for (i=1; i < argc; i++) {
+ arg = argv[i];
+ if (!arg)
+ continue;
+ if ((arg[0] == '/' && !opts_for_fsck) || strchr(arg, '=')) {
+ if (num_devices >= MAX_DEVICES) {
+ bb_error_msg_and_die("too many devices");
+ }
+ dev = blkid_get_devname(cache, arg, NULL);
+ if (!dev && strchr(arg, '=')) {
+ /*
+ * Check to see if we failed because
+ * /proc/partitions isn't found.
+ */
+ if (access("/proc/partitions", R_OK) < 0) {
+ bb_perror_msg_and_die("cannot open /proc/partitions "
+ "(is /proc mounted?)");
+ }
+ /*
+ * Check to see if this is because
+ * we're not running as root
+ */
+ if (geteuid())
+ bb_error_msg_and_die(
+ "must be root to scan for matching filesystems: %s\n", arg);
+ else
+ bb_error_msg_and_die(
+ "cannot find matching filesystem: %s", arg);
+ }
+ devices[num_devices++] = dev ? dev : string_copy(arg);
+ continue;
+ }
+ if (arg[0] != '-' || opts_for_fsck) {
+ if (num_args >= MAX_ARGS) {
+ bb_error_msg_and_die("too many arguments");
+ }
+ args[num_args++] = string_copy(arg);
+ continue;
+ }
+ for (j=1; arg[j]; j++) {
+ if (opts_for_fsck) {
+ options[++opt] = arg[j];
+ continue;
+ }
+ switch (arg[j]) {
+ case 'A':
+ doall++;
+ break;
+ case 'C':
+ progress++;
+ if (arg[j+1]) {
+ progress_fd = string_to_int(arg+j+1);
+ if (progress_fd < 0)
+ progress_fd = 0;
+ else
+ goto next_arg;
+ } else if ((i+1) < argc
+ && argv[i+1][0] != '-') {
+ progress_fd = string_to_int(argv[i]);
+ if (progress_fd < 0)
+ progress_fd = 0;
+ else {
+ goto next_arg;
+ i++;
+ }
+ }
+ break;
+ case 'V':
+ verbose++;
+ break;
+ case 'N':
+ noexecute++;
+ break;
+ case 'R':
+ skip_root++;
+ break;
+ case 'T':
+ notitle++;
+ break;
+ case 'M':
+ like_mount++;
+ break;
+ case 'P':
+ parallel_root++;
+ break;
+ case 's':
+ serialize++;
+ break;
+ case 't':
+ tmp = 0;
+ if (fstype)
+ bb_show_usage();
+ if (arg[j+1])
+ tmp = arg+j+1;
+ else if ((i+1) < argc)
+ tmp = argv[++i];
+ else
+ bb_show_usage();
+ fstype = string_copy(tmp);
+ compile_fs_type(fstype, &fs_type_compiled);
+ goto next_arg;
+ case '-':
+ opts_for_fsck++;
+ break;
+ case '?':
+ bb_show_usage();
+ break;
+ default:
+ options[++opt] = arg[j];
+ break;
+ }
+ }
+ next_arg:
+ if (opt) {
+ options[0] = '-';
+ options[++opt] = '\0';
+ if (num_args >= MAX_ARGS) {
+ bb_error_msg("too many arguments");
+ }
+ args[num_args++] = string_copy(options);
+ opt = 0;
+ }
+ }
+ if (getenv("FSCK_FORCE_ALL_PARALLEL"))
+ force_all_parallel++;
+ if ((tmp = getenv("FSCK_MAX_INST")))
+ max_running = atoi(tmp);
+}
+
+int fsck_main(int argc, char *argv[]);
+int fsck_main(int argc, char *argv[])
+{
+ int i, status = 0;
+ int interactive = 0;
+ const char *fstab;
+ struct fs_info *fs;
+
+ setvbuf(stdout, NULL, _IONBF, BUFSIZ);
+ setvbuf(stderr, NULL, _IONBF, BUFSIZ);
+
+ blkid_get_cache(&cache, NULL);
+ PRS(argc, argv);
+
+ if (!notitle)
+ printf("fsck %s (%s)\n", E2FSPROGS_VERSION, E2FSPROGS_DATE);
+
+ fstab = getenv("FSTAB_FILE");
+ if (!fstab)
+ fstab = _PATH_MNTTAB;
+ load_fs_info(fstab);
+
+ fsck_path = e2fs_set_sbin_path();
+
+ if ((num_devices == 1) || (serialize))
+ interactive = 1;
+
+ /* If -A was specified ("check all"), do that! */
+ if (doall)
+ return check_all();
+
+ if (num_devices == 0) {
+ serialize++;
+ interactive++;
+ return check_all();
+ }
+ for (i = 0 ; i < num_devices; i++) {
+ if (cancel_requested) {
+ if (!kill_sent) {
+ kill_all(SIGTERM);
+ kill_sent++;
+ }
+ break;
+ }
+ fs = lookup(devices[i]);
+ if (!fs) {
+ fs = create_fs_device(devices[i], 0, "auto",
+ 0, -1, -1);
+ if (!fs)
+ continue;
+ }
+ fsck_device(fs, interactive);
+ if (serialize ||
+ (max_running && (num_running >= max_running))) {
+ struct fsck_instance *inst;
+
+ inst = wait_one(0);
+ if (inst) {
+ status |= inst->exit_status;
+ free_instance(inst);
+ }
+ if (verbose > 1)
+ printf("----------------------------------\n");
+ }
+ }
+ status |= wait_many(FLAG_WAIT_ALL);
+ blkid_put_cache(cache);
+ return status;
+}
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/fsck.h b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/fsck.h
new file mode 100644
index 0000000..2ca2af7
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/fsck.h
@@ -0,0 +1,16 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * fsck.h
+ */
+
+#define FSCK_ATTR(x) __attribute__(x)
+
+#define EXIT_OK 0
+#define EXIT_NONDESTRUCT 1
+#define EXIT_DESTRUCT 2
+#define EXIT_UNCORRECTED 4
+#define EXIT_ERROR 8
+#define EXIT_USAGE 16
+#define FSCK_CANCELED 32 /* Aborted with a signal or ^C */
+
+extern char *e2fs_set_sbin_path(void);
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/lsattr.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/lsattr.c
new file mode 100644
index 0000000..b418f6b
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/lsattr.c
@@ -0,0 +1,129 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * lsattr.c - List file attributes on an ext2 file system
+ *
+ * Copyright (C) 1993, 1994 Remy Card <card@masi.ibp.fr>
+ * Laboratoire MASI, Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ * This file can be redistributed under the terms of the GNU General
+ * Public License
+ */
+
+/*
+ * History:
+ * 93/10/30 - Creation
+ * 93/11/13 - Replace stat() calls by lstat() to avoid loops
+ * 94/02/27 - Integrated in Ted's distribution
+ * 98/12/29 - Display version info only when -V specified (G M Sipe)
+ */
+
+#include <sys/types.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include "ext2fs/ext2_fs.h"
+#include "e2fsbb.h"
+#include "e2p/e2p.h"
+
+#define OPT_RECUR 1
+#define OPT_ALL 2
+#define OPT_DIRS_OPT 4
+#define OPT_PF_LONG 8
+#define OPT_GENERATION 16
+static int flags;
+
+static void list_attributes(const char *name)
+{
+ unsigned long fsflags;
+ unsigned long generation;
+
+ if (fgetflags(name, &fsflags) == -1)
+ goto read_err;
+ if (flags & OPT_GENERATION) {
+ if (fgetversion(name, &generation) == -1)
+ goto read_err;
+ printf("%5lu ", generation);
+ }
+
+ if (flags & OPT_PF_LONG) {
+ printf("%-28s ", name);
+ print_flags(stdout, fsflags, PFOPT_LONG);
+ puts("");
+ } else {
+ print_flags(stdout, fsflags, 0);
+ printf(" %s\n", name);
+ }
+
+ return;
+read_err:
+ bb_perror_msg("reading %s", name);
+}
+
+static int lsattr_dir_proc(const char *, struct dirent *, void *);
+
+static void lsattr_args(const char *name)
+{
+ struct stat st;
+
+ if (lstat(name, &st) == -1) {
+ bb_perror_msg("stating %s", name);
+ } else {
+ if (S_ISDIR(st.st_mode) && !(flags & OPT_DIRS_OPT))
+ iterate_on_dir(name, lsattr_dir_proc, NULL);
+ else
+ list_attributes(name);
+ }
+}
+
+static int lsattr_dir_proc(const char *dir_name, struct dirent *de,
+ void *private)
+{
+ struct stat st;
+ char *path;
+
+ path = concat_path_file(dir_name, de->d_name);
+
+ if (lstat(path, &st) == -1)
+ bb_perror_msg(path);
+ else {
+ if (de->d_name[0] != '.' || (flags & OPT_ALL)) {
+ list_attributes(path);
+ if (S_ISDIR(st.st_mode) && (flags & OPT_RECUR) &&
+ (de->d_name[0] != '.' && (de->d_name[1] != '\0' ||
+ (de->d_name[1] != '.' && de->d_name[2] != '\0')))) {
+ printf("\n%s:\n", path);
+ iterate_on_dir(path, lsattr_dir_proc, NULL);
+ puts("");
+ }
+ }
+ }
+
+ free(path);
+
+ return 0;
+}
+
+int lsattr_main(int argc, char **argv);
+int lsattr_main(int argc, char **argv)
+{
+ int i;
+
+ flags = getopt32(argc, argv, "Radlv");
+
+ if (optind > argc - 1)
+ lsattr_args(".");
+ else
+ for (i = optind; i < argc; i++)
+ lsattr_args(argv[i]);
+
+ return EXIT_SUCCESS;
+}
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/mke2fs.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/mke2fs.c
new file mode 100644
index 0000000..6cdb16c
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/mke2fs.c
@@ -0,0 +1,1336 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * mke2fs.c - Make a ext2fs filesystem.
+ *
+ * Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+ * 2003, 2004, 2005 by Theodore Ts'o.
+ *
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ */
+
+/* Usage: mke2fs [options] device
+ *
+ * The device may be a block device or a image of one, but this isn't
+ * enforced (but it's not much fun on a character device :-).
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <time.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <mntent.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+
+#include "e2fsbb.h"
+#include "ext2fs/ext2_fs.h"
+#include "uuid/uuid.h"
+#include "e2p/e2p.h"
+#include "ext2fs/ext2fs.h"
+#include "util.h"
+
+#define STRIDE_LENGTH 8
+
+#ifndef __sparc__
+#define ZAP_BOOTBLOCK
+#endif
+
+static const char * device_name;
+
+/* Command line options */
+static int cflag;
+static int quiet;
+static int super_only;
+static int force;
+static int noaction;
+static int journal_size;
+static int journal_flags;
+static const char *bad_blocks_filename;
+static __u32 fs_stride;
+
+static struct ext2_super_block param;
+static char *creator_os;
+static char *volume_label;
+static char *mount_dir;
+static char *journal_device = NULL;
+static int sync_kludge; /* Set using the MKE2FS_SYNC env. option */
+
+static int sys_page_size = 4096;
+static int linux_version_code = 0;
+
+static int int_log2(int arg)
+{
+ int l = 0;
+
+ arg >>= 1;
+ while (arg) {
+ l++;
+ arg >>= 1;
+ }
+ return l;
+}
+
+static int int_log10(unsigned int arg)
+{
+ int l;
+
+ for (l=0; arg ; l++)
+ arg = arg / 10;
+ return l;
+}
+
+/*
+ * This function sets the default parameters for a filesystem
+ *
+ * The type is specified by the user. The size is the maximum size
+ * (in megabytes) for which a set of parameters applies, with a size
+ * of zero meaning that it is the default parameter for the type.
+ * Note that order is important in the table below.
+ */
+#define DEF_MAX_BLOCKSIZE -1
+static const char default_str[] = "default";
+struct mke2fs_defaults {
+ const char *type;
+ int size;
+ int blocksize;
+ int inode_ratio;
+};
+
+static const struct mke2fs_defaults settings[] = {
+ { default_str, 0, 4096, 8192 },
+ { default_str, 512, 1024, 4096 },
+ { default_str, 3, 1024, 8192 },
+ { "journal", 0, 4096, 8192 },
+ { "news", 0, 4096, 4096 },
+ { "largefile", 0, 4096, 1024 * 1024 },
+ { "largefile4", 0, 4096, 4096 * 1024 },
+ { 0, 0, 0, 0},
+};
+
+static void set_fs_defaults(const char *fs_type,
+ struct ext2_super_block *super,
+ int blocksize, int sector_size,
+ int *inode_ratio)
+{
+ int megs;
+ int ratio = 0;
+ const struct mke2fs_defaults *p;
+ int use_bsize = 1024;
+
+ megs = super->s_blocks_count * (EXT2_BLOCK_SIZE(super) / 1024) / 1024;
+ if (inode_ratio)
+ ratio = *inode_ratio;
+ if (!fs_type)
+ fs_type = default_str;
+ for (p = settings; p->type; p++) {
+ if ((strcmp(p->type, fs_type) != 0) &&
+ (strcmp(p->type, default_str) != 0))
+ continue;
+ if ((p->size != 0) && (megs > p->size))
+ continue;
+ if (ratio == 0)
+ *inode_ratio = p->inode_ratio < blocksize ?
+ blocksize : p->inode_ratio;
+ use_bsize = p->blocksize;
+ }
+ if (blocksize <= 0) {
+ if (use_bsize == DEF_MAX_BLOCKSIZE) {
+ use_bsize = sys_page_size;
+ if ((linux_version_code < (2*65536 + 6*256)) &&
+ (use_bsize > 4096))
+ use_bsize = 4096;
+ }
+ if (sector_size && use_bsize < sector_size)
+ use_bsize = sector_size;
+ if ((blocksize < 0) && (use_bsize < (-blocksize)))
+ use_bsize = -blocksize;
+ blocksize = use_bsize;
+ super->s_blocks_count /= blocksize / 1024;
+ }
+ super->s_log_frag_size = super->s_log_block_size =
+ int_log2(blocksize >> EXT2_MIN_BLOCK_LOG_SIZE);
+}
+
+
+/*
+ * Helper function for read_bb_file and test_disk
+ */
+static void invalid_block(ext2_filsys fs EXT2FS_ATTR((unused)), blk_t blk)
+{
+ bb_error_msg("Bad block %u out of range; ignored", blk);
+}
+
+/*
+ * Busybox stuff
+ */
+static void mke2fs_error_msg_and_die(int retval, const char *fmt, ...)__attribute__ ((format (printf, 2, 3)));
+static void mke2fs_error_msg_and_die(int retval, const char *fmt, ...)
+{
+ va_list ap;
+
+ if (retval) {
+ va_start(ap, fmt);
+ fprintf(stderr,"\nCould not ");
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ va_end(ap);
+ exit(EXIT_FAILURE);
+ }
+}
+
+static void mke2fs_verbose(const char *fmt, ...)__attribute__ ((format (printf, 1, 2)));
+static void mke2fs_verbose(const char *fmt, ...)
+{
+ va_list ap;
+
+ if (!quiet) {
+ va_start(ap, fmt);
+ vfprintf(stdout, fmt, ap);
+ fflush(stdout);
+ va_end(ap);
+ }
+}
+
+static void mke2fs_verbose_done(void)
+{
+ mke2fs_verbose("done\n");
+}
+
+static void mke2fs_warning_msg(int retval, char *fmt, ... )__attribute__ ((format (printf, 2, 3)));
+static void mke2fs_warning_msg(int retval, char *fmt, ... )
+{
+ va_list ap;
+
+ if (retval) {
+ va_start(ap, fmt);
+ fprintf(stderr,"\nWarning: ");
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ va_end(ap);
+ }
+}
+
+/*
+ * Reads the bad blocks list from a file
+ */
+static void read_bb_file(ext2_filsys fs, badblocks_list *bb_list,
+ const char *bad_blocks_file)
+{
+ FILE *f;
+ errcode_t retval;
+
+ f = xfopen(bad_blocks_file, "r");
+ retval = ext2fs_read_bb_FILE(fs, f, bb_list, invalid_block);
+ fclose (f);
+ mke2fs_error_msg_and_die(retval, "read bad blocks from list");
+}
+
+/*
+ * Runs the badblocks program to test the disk
+ */
+static void test_disk(ext2_filsys fs, badblocks_list *bb_list)
+{
+ FILE *f;
+ errcode_t retval;
+ char buf[1024];
+
+ sprintf(buf, "badblocks -b %d %s%s%s %d", fs->blocksize,
+ quiet ? "" : "-s ", (cflag > 1) ? "-w " : "",
+ fs->device_name, fs->super->s_blocks_count);
+ mke2fs_verbose("Running command: %s\n", buf);
+ f = popen(buf, "r");
+ if (!f) {
+ bb_perror_msg_and_die("cannot run '%s'", buf);
+ }
+ retval = ext2fs_read_bb_FILE(fs, f, bb_list, invalid_block);
+ pclose(f);
+ mke2fs_error_msg_and_die(retval, "read bad blocks from program");
+}
+
+static void handle_bad_blocks(ext2_filsys fs, badblocks_list bb_list)
+{
+ dgrp_t i;
+ blk_t j;
+ unsigned must_be_good;
+ blk_t blk;
+ badblocks_iterate bb_iter;
+ errcode_t retval;
+ blk_t group_block;
+ int group;
+ int group_bad;
+
+ if (!bb_list)
+ return;
+
+ /*
+ * The primary superblock and group descriptors *must* be
+ * good; if not, abort.
+ */
+ must_be_good = fs->super->s_first_data_block + 1 + fs->desc_blocks;
+ for (i = fs->super->s_first_data_block; i <= must_be_good; i++) {
+ if (ext2fs_badblocks_list_test(bb_list, i)) {
+ bb_error_msg_and_die(
+ "Block %d in primary superblock/group descriptor area bad\n"
+ "Blocks %d through %d must be good in order to build a filesystem\n"
+ "Aborting ...", i, fs->super->s_first_data_block, must_be_good);
+ }
+ }
+
+ /*
+ * See if any of the bad blocks are showing up in the backup
+ * superblocks and/or group descriptors. If so, issue a
+ * warning and adjust the block counts appropriately.
+ */
+ group_block = fs->super->s_first_data_block +
+ fs->super->s_blocks_per_group;
+
+ for (i = 1; i < fs->group_desc_count; i++) {
+ group_bad = 0;
+ for (j=0; j < fs->desc_blocks+1; j++) {
+ if (ext2fs_badblocks_list_test(bb_list,
+ group_block + j)) {
+ mke2fs_warning_msg(!group_bad,
+ "the backup superblock/group descriptors at block %d contain\n"
+ "bad blocks\n", group_block);
+ group_bad++;
+ group = ext2fs_group_of_blk(fs, group_block+j);
+ fs->group_desc[group].bg_free_blocks_count++;
+ fs->super->s_free_blocks_count++;
+ }
+ }
+ group_block += fs->super->s_blocks_per_group;
+ }
+
+ /*
+ * Mark all the bad blocks as used...
+ */
+ retval = ext2fs_badblocks_list_iterate_begin(bb_list, &bb_iter);
+ mke2fs_error_msg_and_die(retval, "mark bad blocks as used");
+
+ while (ext2fs_badblocks_list_iterate(bb_iter, &blk))
+ ext2fs_mark_block_bitmap(fs->block_map, blk);
+ ext2fs_badblocks_list_iterate_end(bb_iter);
+}
+
+/*
+ * These functions implement a generalized progress meter.
+ */
+struct progress_struct {
+ char format[20];
+ char backup[80];
+ __u32 max;
+ int skip_progress;
+};
+
+static void progress_init(struct progress_struct *progress,
+ const char *label,__u32 max)
+{
+ int i;
+
+ memset(progress, 0, sizeof(struct progress_struct));
+ if (quiet)
+ return;
+
+ /*
+ * Figure out how many digits we need
+ */
+ i = int_log10(max);
+ sprintf(progress->format, "%%%dd/%%%dld", i, i);
+ memset(progress->backup, '\b', sizeof(progress->backup)-1);
+ progress->backup[sizeof(progress->backup)-1] = 0;
+ if ((2*i)+1 < (int) sizeof(progress->backup))
+ progress->backup[(2*i)+1] = 0;
+ progress->max = max;
+
+ progress->skip_progress = 0;
+ if (getenv("MKE2FS_SKIP_PROGRESS"))
+ progress->skip_progress++;
+
+ fputs(label, stdout);
+ fflush(stdout);
+}
+
+static void progress_update(struct progress_struct *progress, __u32 val)
+{
+ if ((progress->format[0] == 0) || progress->skip_progress)
+ return;
+ printf(progress->format, val, progress->max);
+ fputs(progress->backup, stdout);
+}
+
+static void progress_close(struct progress_struct *progress)
+{
+ if (progress->format[0] == 0)
+ return;
+ printf("%-28s\n", "done");
+}
+
+
+/*
+ * Helper function which zeros out _num_ blocks starting at _blk_. In
+ * case of an error, the details of the error is returned via _ret_blk_
+ * and _ret_count_ if they are non-NULL pointers. Returns 0 on
+ * success, and an error code on an error.
+ *
+ * As a special case, if the first argument is NULL, then it will
+ * attempt to free the static zeroizing buffer. (This is to keep
+ * programs that check for memory leaks happy.)
+ */
+static errcode_t zero_blocks(ext2_filsys fs, blk_t blk, int num,
+ struct progress_struct *progress,
+ blk_t *ret_blk, int *ret_count)
+{
+ int j, count, next_update, next_update_incr;
+ static char *buf;
+ errcode_t retval;
+
+ /* If fs is null, clean up the static buffer and return */
+ if (!fs) {
+ if (buf) {
+ free(buf);
+ buf = 0;
+ }
+ return 0;
+ }
+ /* Allocate the zeroizing buffer if necessary */
+ if (!buf) {
+ buf = xzalloc(fs->blocksize * STRIDE_LENGTH);
+ }
+ /* OK, do the write loop */
+ next_update = 0;
+ next_update_incr = num / 100;
+ if (next_update_incr < 1)
+ next_update_incr = 1;
+ for (j=0; j < num; j += STRIDE_LENGTH, blk += STRIDE_LENGTH) {
+ count = num - j;
+ if (count > STRIDE_LENGTH)
+ count = STRIDE_LENGTH;
+ retval = io_channel_write_blk(fs->io, blk, count, buf);
+ if (retval) {
+ if (ret_count)
+ *ret_count = count;
+ if (ret_blk)
+ *ret_blk = blk;
+ return retval;
+ }
+ if (progress && j > next_update) {
+ next_update += num / 100;
+ progress_update(progress, blk);
+ }
+ }
+ return 0;
+}
+
+static void write_inode_tables(ext2_filsys fs)
+{
+ errcode_t retval;
+ blk_t blk;
+ dgrp_t i;
+ int num;
+ struct progress_struct progress;
+
+ if (quiet)
+ memset(&progress, 0, sizeof(progress));
+ else
+ progress_init(&progress, "Writing inode tables: ",
+ fs->group_desc_count);
+
+ for (i = 0; i < fs->group_desc_count; i++) {
+ progress_update(&progress, i);
+
+ blk = fs->group_desc[i].bg_inode_table;
+ num = fs->inode_blocks_per_group;
+
+ retval = zero_blocks(fs, blk, num, 0, &blk, &num);
+ mke2fs_error_msg_and_die(retval,
+ "write %d blocks in inode table starting at %d.",
+ num, blk);
+ if (sync_kludge) {
+ if (sync_kludge == 1)
+ sync();
+ else if ((i % sync_kludge) == 0)
+ sync();
+ }
+ }
+ zero_blocks(0, 0, 0, 0, 0, 0);
+ progress_close(&progress);
+}
+
+static void create_root_dir(ext2_filsys fs)
+{
+ errcode_t retval;
+ struct ext2_inode inode;
+
+ retval = ext2fs_mkdir(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, 0);
+ mke2fs_error_msg_and_die(retval, "create root dir");
+ if (geteuid()) {
+ retval = ext2fs_read_inode(fs, EXT2_ROOT_INO, &inode);
+ mke2fs_error_msg_and_die(retval, "read root inode");
+ inode.i_uid = getuid();
+ if (inode.i_uid)
+ inode.i_gid = getgid();
+ retval = ext2fs_write_new_inode(fs, EXT2_ROOT_INO, &inode);
+ mke2fs_error_msg_and_die(retval, "set root inode ownership");
+ }
+}
+
+static void create_lost_and_found(ext2_filsys fs)
+{
+ errcode_t retval;
+ ext2_ino_t ino;
+ const char *name = "lost+found";
+ int i = 1;
+ char *msg = "create";
+ int lpf_size = 0;
+
+ fs->umask = 077;
+ retval = ext2fs_mkdir(fs, EXT2_ROOT_INO, 0, name);
+ if (retval) {
+ goto CREATE_LOST_AND_FOUND_ERROR;
+ }
+
+ retval = ext2fs_lookup(fs, EXT2_ROOT_INO, name, strlen(name), 0, &ino);
+ if (retval) {
+ msg = "lookup";
+ goto CREATE_LOST_AND_FOUND_ERROR;
+ }
+
+ for (; i < EXT2_NDIR_BLOCKS; i++) {
+ if ((lpf_size += fs->blocksize) >= 16*1024)
+ break;
+ retval = ext2fs_expand_dir(fs, ino);
+ msg = "expand";
+CREATE_LOST_AND_FOUND_ERROR:
+ mke2fs_error_msg_and_die(retval, "%s %s", msg, name);
+ }
+}
+
+static void create_bad_block_inode(ext2_filsys fs, badblocks_list bb_list)
+{
+ errcode_t retval;
+
+ ext2fs_mark_inode_bitmap(fs->inode_map, EXT2_BAD_INO);
+ fs->group_desc[0].bg_free_inodes_count--;
+ fs->super->s_free_inodes_count--;
+ retval = ext2fs_update_bb_inode(fs, bb_list);
+ mke2fs_error_msg_and_die(retval, "set bad block inode");
+}
+
+static void reserve_inodes(ext2_filsys fs)
+{
+ ext2_ino_t i;
+ int group;
+
+ for (i = EXT2_ROOT_INO + 1; i < EXT2_FIRST_INODE(fs->super); i++) {
+ ext2fs_mark_inode_bitmap(fs->inode_map, i);
+ group = ext2fs_group_of_ino(fs, i);
+ fs->group_desc[group].bg_free_inodes_count--;
+ fs->super->s_free_inodes_count--;
+ }
+ ext2fs_mark_ib_dirty(fs);
+}
+
+#define BSD_DISKMAGIC (0x82564557UL) /* The disk magic number */
+#define BSD_MAGICDISK (0x57455682UL) /* The disk magic number reversed */
+#define BSD_LABEL_OFFSET 64
+
+static void zap_sector(ext2_filsys fs, int sect, int nsect)
+{
+ char *buf;
+ char *fmt = "could not %s %d";
+ int retval;
+ unsigned int *magic;
+
+ buf = xmalloc(512*nsect);
+
+ if (sect == 0) {
+ /* Check for a BSD disklabel, and don't erase it if so */
+ retval = io_channel_read_blk(fs->io, 0, -512, buf);
+ if (retval)
+ mke2fs_warning_msg(retval, fmt, "read block", 0);
+ else {
+ magic = (unsigned int *) (buf + BSD_LABEL_OFFSET);
+ if ((*magic == BSD_DISKMAGIC) ||
+ (*magic == BSD_MAGICDISK))
+ return;
+ }
+ }
+
+ memset(buf, 0, 512*nsect);
+ io_channel_set_blksize(fs->io, 512);
+ retval = io_channel_write_blk(fs->io, sect, -512*nsect, buf);
+ io_channel_set_blksize(fs->io, fs->blocksize);
+ free(buf);
+ mke2fs_warning_msg(retval, fmt, "erase sector", sect);
+}
+
+static void create_journal_dev(ext2_filsys fs)
+{
+ struct progress_struct progress;
+ errcode_t retval;
+ char *buf;
+ char *fmt = "%s journal superblock";
+ blk_t blk;
+ int count;
+
+ retval = ext2fs_create_journal_superblock(fs,
+ fs->super->s_blocks_count, 0, &buf);
+ mke2fs_error_msg_and_die(retval, fmt, "init");
+ if (quiet)
+ memset(&progress, 0, sizeof(progress));
+ else
+ progress_init(&progress, "Zeroing journal device: ",
+ fs->super->s_blocks_count);
+
+ retval = zero_blocks(fs, 0, fs->super->s_blocks_count,
+ &progress, &blk, &count);
+ mke2fs_error_msg_and_die(retval, "zero journal device (block %u, count %d)",
+ blk, count);
+ zero_blocks(0, 0, 0, 0, 0, 0);
+
+ retval = io_channel_write_blk(fs->io,
+ fs->super->s_first_data_block+1,
+ 1, buf);
+ mke2fs_error_msg_and_die(retval, fmt, "write");
+ progress_close(&progress);
+}
+
+static void show_stats(ext2_filsys fs)
+{
+ struct ext2_super_block *s = fs->super;
+ char *os;
+ blk_t group_block;
+ dgrp_t i;
+ int need, col_left;
+
+ mke2fs_warning_msg((param.s_blocks_count != s->s_blocks_count),
+ "%d blocks unused\n", param.s_blocks_count - s->s_blocks_count);
+ os = e2p_os2string(fs->super->s_creator_os);
+ printf( "Filesystem label=%.*s\n"
+ "OS type: %s\n"
+ "Block size=%u (log=%u)\n"
+ "Fragment size=%u (log=%u)\n"
+ "%u inodes, %u blocks\n"
+ "%u blocks (%2.2f%%) reserved for the super user\n"
+ "First data block=%u\n",
+ (int) sizeof(s->s_volume_name),
+ s->s_volume_name,
+ os,
+ fs->blocksize, s->s_log_block_size,
+ fs->fragsize, s->s_log_frag_size,
+ s->s_inodes_count, s->s_blocks_count,
+ s->s_r_blocks_count, 100.0 * s->s_r_blocks_count / s->s_blocks_count,
+ s->s_first_data_block);
+ free(os);
+ if (s->s_reserved_gdt_blocks) {
+ printf("Maximum filesystem blocks=%lu\n",
+ (s->s_reserved_gdt_blocks + fs->desc_blocks) *
+ (fs->blocksize / sizeof(struct ext2_group_desc)) *
+ s->s_blocks_per_group);
+ }
+ printf( "%u block group%s\n"
+ "%u blocks per group, %u fragments per group\n"
+ "%u inodes per group\n",
+ fs->group_desc_count, (fs->group_desc_count > 1) ? "s" : "",
+ s->s_blocks_per_group, s->s_frags_per_group,
+ s->s_inodes_per_group);
+ if (fs->group_desc_count == 1) {
+ puts("");
+ return;
+ }
+
+ printf("Superblock backups stored on blocks: ");
+ group_block = s->s_first_data_block;
+ col_left = 0;
+ for (i = 1; i < fs->group_desc_count; i++) {
+ group_block += s->s_blocks_per_group;
+ if (!ext2fs_bg_has_super(fs, i))
+ continue;
+ if (i != 1)
+ printf(", ");
+ need = int_log10(group_block) + 2;
+ if (need > col_left) {
+ printf("\n\t");
+ col_left = 72;
+ }
+ col_left -= need;
+ printf("%u", group_block);
+ }
+ puts("\n");
+}
+
+/*
+ * Set the S_CREATOR_OS field. Return true if OS is known,
+ * otherwise, 0.
+ */
+static int set_os(struct ext2_super_block *sb, char *os)
+{
+ if (isdigit (*os)) {
+ sb->s_creator_os = atoi(os);
+ return 1;
+ }
+
+ if((sb->s_creator_os = e2p_string2os(os)) >= 0) {
+ return 1;
+ } else if (!strcasecmp("GNU", os)) {
+ sb->s_creator_os = EXT2_OS_HURD;
+ return 1;
+ }
+ return 0;
+}
+
+static void parse_extended_opts(struct ext2_super_block *sb_param,
+ const char *opts)
+{
+ char *buf, *token, *next, *p, *arg;
+ int r_usage = 0;
+
+ buf = xstrdup(opts);
+ for (token = buf; token && *token; token = next) {
+ p = strchr(token, ',');
+ next = 0;
+ if (p) {
+ *p = 0;
+ next = p+1;
+ }
+ arg = strchr(token, '=');
+ if (arg) {
+ *arg = 0;
+ arg++;
+ }
+ if (strcmp(token, "stride") == 0) {
+ if (!arg) {
+ r_usage++;
+ continue;
+ }
+ fs_stride = strtoul(arg, &p, 0);
+ if (*p || (fs_stride == 0)) {
+ bb_error_msg("Invalid stride parameter: %s", arg);
+ r_usage++;
+ continue;
+ }
+ } else if (!strcmp(token, "resize")) {
+ unsigned long resize, bpg, rsv_groups;
+ unsigned long group_desc_count, desc_blocks;
+ unsigned int gdpb, blocksize;
+ int rsv_gdb;
+
+ if (!arg) {
+ r_usage++;
+ continue;
+ }
+
+ resize = parse_num_blocks(arg,
+ sb_param->s_log_block_size);
+
+ if (resize == 0) {
+ bb_error_msg("Invalid resize parameter: %s", arg);
+ r_usage++;
+ continue;
+ }
+ if (resize <= sb_param->s_blocks_count) {
+ bb_error_msg("The resize maximum must be greater "
+ "than the filesystem size");
+ r_usage++;
+ continue;
+ }
+
+ blocksize = EXT2_BLOCK_SIZE(sb_param);
+ bpg = sb_param->s_blocks_per_group;
+ if (!bpg)
+ bpg = blocksize * 8;
+ gdpb = blocksize / sizeof(struct ext2_group_desc);
+ group_desc_count = (sb_param->s_blocks_count +
+ bpg - 1) / bpg;
+ desc_blocks = (group_desc_count +
+ gdpb - 1) / gdpb;
+ rsv_groups = (resize + bpg - 1) / bpg;
+ rsv_gdb = (rsv_groups + gdpb - 1) / gdpb -
+ desc_blocks;
+ if (rsv_gdb > EXT2_ADDR_PER_BLOCK(sb_param))
+ rsv_gdb = EXT2_ADDR_PER_BLOCK(sb_param);
+
+ if (rsv_gdb > 0) {
+ sb_param->s_feature_compat |=
+ EXT2_FEATURE_COMPAT_RESIZE_INODE;
+
+ sb_param->s_reserved_gdt_blocks = rsv_gdb;
+ }
+ } else
+ r_usage++;
+ }
+ if (r_usage) {
+ bb_error_msg_and_die(
+ "\nBad options specified.\n\n"
+ "Extended options are separated by commas, "
+ "and may take an argument which\n"
+ "\tis set off by an equals ('=') sign.\n\n"
+ "Valid extended options are:\n"
+ "\tstride=<stride length in blocks>\n"
+ "\tresize=<resize maximum size in blocks>\n");
+ }
+}
+
+static __u32 ok_features[3] = {
+ EXT3_FEATURE_COMPAT_HAS_JOURNAL |
+ EXT2_FEATURE_COMPAT_RESIZE_INODE |
+ EXT2_FEATURE_COMPAT_DIR_INDEX, /* Compat */
+ EXT2_FEATURE_INCOMPAT_FILETYPE| /* Incompat */
+ EXT3_FEATURE_INCOMPAT_JOURNAL_DEV|
+ EXT2_FEATURE_INCOMPAT_META_BG,
+ EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER /* R/O compat */
+};
+
+static int PRS(int argc, char *argv[])
+{
+ int c;
+ int size;
+ char * tmp;
+ int blocksize = 0;
+ int inode_ratio = 0;
+ int inode_size = 0;
+ int reserved_ratio = 5;
+ int sector_size = 0;
+ int show_version_only = 0;
+ ext2_ino_t num_inodes = 0;
+ errcode_t retval;
+ char * extended_opts = 0;
+ const char * fs_type = 0;
+ blk_t dev_size;
+ long sysval;
+
+ /* Update our PATH to include /sbin */
+ e2fs_set_sbin_path();
+
+ tmp = getenv("MKE2FS_SYNC");
+ if (tmp)
+ sync_kludge = atoi(tmp);
+
+ /* Determine the system page size if possible */
+#if (!defined(_SC_PAGESIZE) && defined(_SC_PAGE_SIZE))
+#define _SC_PAGESIZE _SC_PAGE_SIZE
+#endif
+#ifdef _SC_PAGESIZE
+ sysval = sysconf(_SC_PAGESIZE);
+ if (sysval > 0)
+ sys_page_size = sysval;
+#endif /* _SC_PAGESIZE */
+
+ setbuf(stdout, NULL);
+ setbuf(stderr, NULL);
+ memset(&param, 0, sizeof(struct ext2_super_block));
+ param.s_rev_level = 1; /* Create revision 1 filesystems now */
+ param.s_feature_incompat |= EXT2_FEATURE_INCOMPAT_FILETYPE;
+ param.s_feature_ro_compat |= EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER;
+
+#ifdef __linux__
+ linux_version_code = get_linux_version_code();
+ if (linux_version_code && linux_version_code < KERNEL_VERSION(2,2,0)) {
+ param.s_rev_level = 0;
+ param.s_feature_incompat = 0;
+ param.s_feature_compat = 0;
+ param.s_feature_ro_compat = 0;
+ }
+#endif
+
+ /* If called as mkfs.ext3, create a journal inode */
+ if (last_char_is(applet_name, '3'))
+ journal_size = -1;
+
+ while ((c = getopt (argc, argv,
+ "b:cE:f:g:i:jl:m:no:qr:R:s:tvI:J:ST:FL:M:N:O:V")) != EOF) {
+ switch (c) {
+ case 'b':
+ blocksize = xatou_range(optarg, EXT2_MIN_BLOCK_SIZE, EXT2_MAX_BLOCK_SIZE);
+ mke2fs_warning_msg((blocksize > 4096),
+ "blocksize %d not usable on most systems",
+ blocksize);
+ param.s_log_block_size =
+ int_log2(blocksize >> EXT2_MIN_BLOCK_LOG_SIZE);
+ break;
+ case 'c': /* Check for bad blocks */
+ case 't': /* deprecated */
+ cflag++;
+ break;
+ case 'f':
+ size = xatou_range(optarg, EXT2_MIN_BLOCK_SIZE, EXT2_MAX_BLOCK_SIZE);
+ param.s_log_frag_size =
+ int_log2(size >> EXT2_MIN_BLOCK_LOG_SIZE);
+ mke2fs_warning_msg(1, "fragments not supported. Ignoring -f option");
+ break;
+ case 'g':
+ param.s_blocks_per_group = xatou32(optarg);
+ if ((param.s_blocks_per_group % 8) != 0) {
+ bb_error_msg_and_die("blocks per group must be multiple of 8");
+ }
+ break;
+ case 'i':
+ /* Huh? is "* 1024" correct? */
+ inode_ratio = xatou_range(optarg, EXT2_MIN_BLOCK_SIZE, EXT2_MAX_BLOCK_SIZE * 1024);
+ break;
+ case 'J':
+ parse_journal_opts(&journal_device, &journal_flags, &journal_size, optarg);
+ break;
+ case 'j':
+ param.s_feature_compat |=
+ EXT3_FEATURE_COMPAT_HAS_JOURNAL;
+ if (!journal_size)
+ journal_size = -1;
+ break;
+ case 'l':
+ bad_blocks_filename = optarg;
+ break;
+ case 'm':
+ reserved_ratio = xatou_range(optarg, 0, 50);
+ break;
+ case 'n':
+ noaction++;
+ break;
+ case 'o':
+ creator_os = optarg;
+ break;
+ case 'r':
+ param.s_rev_level = xatoi_u(optarg);
+ if (param.s_rev_level == EXT2_GOOD_OLD_REV) {
+ param.s_feature_incompat = 0;
+ param.s_feature_compat = 0;
+ param.s_feature_ro_compat = 0;
+ }
+ break;
+ case 's': /* deprecated */
+ if (xatou(optarg))
+ param.s_feature_ro_compat |=
+ EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER;
+ else
+ param.s_feature_ro_compat &=
+ ~EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER;
+ break;
+#ifdef EXT2_DYNAMIC_REV
+ case 'I':
+ inode_size = xatoi_u(optarg);
+ break;
+#endif
+ case 'N':
+ num_inodes = xatoi_u(optarg);
+ break;
+ case 'v':
+ quiet = 0;
+ break;
+ case 'q':
+ quiet = 1;
+ break;
+ case 'F':
+ force = 1;
+ break;
+ case 'L':
+ volume_label = optarg;
+ break;
+ case 'M':
+ mount_dir = optarg;
+ break;
+ case 'O':
+ if (!strcmp(optarg, "none")) {
+ param.s_feature_compat = 0;
+ param.s_feature_incompat = 0;
+ param.s_feature_ro_compat = 0;
+ break;
+ }
+ if (e2p_edit_feature(optarg,
+ &param.s_feature_compat,
+ ok_features)) {
+ bb_error_msg_and_die("Invalid filesystem option set: %s", optarg);
+ }
+ break;
+ case 'E':
+ case 'R':
+ extended_opts = optarg;
+ break;
+ case 'S':
+ super_only = 1;
+ break;
+ case 'T':
+ fs_type = optarg;
+ break;
+ case 'V':
+ /* Print version number and exit */
+ show_version_only = 1;
+ quiet = 0;
+ break;
+ default:
+ bb_show_usage();
+ }
+ }
+ if ((optind == argc) /*&& !show_version_only*/)
+ bb_show_usage();
+ device_name = argv[optind++];
+
+ mke2fs_verbose("mke2fs %s (%s)\n", E2FSPROGS_VERSION, E2FSPROGS_DATE);
+
+ if (show_version_only) {
+ return 0;
+ }
+
+ /*
+ * If there's no blocksize specified and there is a journal
+ * device, use it to figure out the blocksize
+ */
+ if (blocksize <= 0 && journal_device) {
+ ext2_filsys jfs;
+ io_manager io_ptr;
+
+#ifdef CONFIG_TESTIO_DEBUG
+ io_ptr = test_io_manager;
+ test_io_backing_manager = unix_io_manager;
+#else
+ io_ptr = unix_io_manager;
+#endif
+ retval = ext2fs_open(journal_device,
+ EXT2_FLAG_JOURNAL_DEV_OK, 0,
+ 0, io_ptr, &jfs);
+ mke2fs_error_msg_and_die(retval, "open journal device %s", journal_device);
+ if ((blocksize < 0) && (jfs->blocksize < (unsigned) (-blocksize))) {
+ bb_error_msg_and_die(
+ "Journal dev blocksize (%d) smaller than "
+ "minimum blocksize %d\n", jfs->blocksize,
+ -blocksize);
+ }
+ blocksize = jfs->blocksize;
+ param.s_log_block_size =
+ int_log2(blocksize >> EXT2_MIN_BLOCK_LOG_SIZE);
+ ext2fs_close(jfs);
+ }
+
+ if (blocksize > sys_page_size) {
+ mke2fs_warning_msg(1, "%d-byte blocks too big for system (max %d)",
+ blocksize, sys_page_size);
+ if (!force) {
+ proceed_question();
+ }
+ bb_error_msg("Forced to continue");
+ }
+ mke2fs_warning_msg(((blocksize > 4096) &&
+ (param.s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL)),
+ "some 2.4 kernels do not support "
+ "blocksizes greater than 4096 using ext3.\n"
+ "Use -b 4096 if this is an issue for you\n");
+
+ if (optind < argc) {
+ param.s_blocks_count = parse_num_blocks(argv[optind++],
+ param.s_log_block_size);
+ mke2fs_error_msg_and_die(!param.s_blocks_count, "invalid blocks count - %s", argv[optind - 1]);
+ }
+ if (optind < argc)
+ bb_show_usage();
+
+ if (param.s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) {
+ if (!fs_type)
+ fs_type = "journal";
+ reserved_ratio = 0;
+ param.s_feature_incompat = EXT3_FEATURE_INCOMPAT_JOURNAL_DEV;
+ param.s_feature_compat = 0;
+ param.s_feature_ro_compat = 0;
+ }
+ if (param.s_rev_level == EXT2_GOOD_OLD_REV &&
+ (param.s_feature_compat || param.s_feature_ro_compat ||
+ param.s_feature_incompat))
+ param.s_rev_level = 1; /* Create a revision 1 filesystem */
+
+ check_plausibility(device_name , force);
+ check_mount(device_name, force, "filesystem");
+
+ param.s_log_frag_size = param.s_log_block_size;
+
+ if (noaction && param.s_blocks_count) {
+ dev_size = param.s_blocks_count;
+ retval = 0;
+ } else {
+ retry:
+ retval = ext2fs_get_device_size(device_name,
+ EXT2_BLOCK_SIZE(&param),
+ &dev_size);
+ if ((retval == EFBIG) &&
+ (blocksize == 0) &&
+ (param.s_log_block_size == 0)) {
+ param.s_log_block_size = 2;
+ blocksize = 4096;
+ goto retry;
+ }
+ }
+
+ mke2fs_error_msg_and_die((retval && (retval != EXT2_ET_UNIMPLEMENTED)),"determine filesystem size");
+
+ if (!param.s_blocks_count) {
+ if (retval == EXT2_ET_UNIMPLEMENTED) {
+ mke2fs_error_msg_and_die(1,
+ "determine device size; you "
+ "must specify\nthe size of the "
+ "filesystem");
+ } else {
+ if (dev_size == 0) {
+ bb_error_msg_and_die(
+ "Device size reported to be zero. "
+ "Invalid partition specified, or\n\t"
+ "partition table wasn't reread "
+ "after running fdisk, due to\n\t"
+ "a modified partition being busy "
+ "and in use. You may need to reboot\n\t"
+ "to re-read your partition table.\n"
+ );
+ }
+ param.s_blocks_count = dev_size;
+ if (sys_page_size > EXT2_BLOCK_SIZE(&param))
+ param.s_blocks_count &= ~((sys_page_size /
+ EXT2_BLOCK_SIZE(&param))-1);
+ }
+
+ } else if (!force && (param.s_blocks_count > dev_size)) {
+ bb_error_msg("Filesystem larger than apparent device size");
+ proceed_question();
+ }
+
+ /*
+ * If the user asked for HAS_JOURNAL, then make sure a journal
+ * gets created.
+ */
+ if ((param.s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL) &&
+ !journal_size)
+ journal_size = -1;
+
+ /* Set first meta blockgroup via an environment variable */
+ /* (this is mostly for debugging purposes) */
+ if ((param.s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) &&
+ ((tmp = getenv("MKE2FS_FIRST_META_BG"))))
+ param.s_first_meta_bg = atoi(tmp);
+
+ /* Get the hardware sector size, if available */
+ retval = ext2fs_get_device_sectsize(device_name, &sector_size);
+ mke2fs_error_msg_and_die(retval, "determine hardware sector size");
+
+ if ((tmp = getenv("MKE2FS_DEVICE_SECTSIZE")) != NULL)
+ sector_size = atoi(tmp);
+
+ set_fs_defaults(fs_type, &param, blocksize, sector_size, &inode_ratio);
+ blocksize = EXT2_BLOCK_SIZE(&param);
+
+ if (extended_opts)
+ parse_extended_opts(&param, extended_opts);
+
+ /* Since sparse_super is the default, we would only have a problem
+ * here if it was explicitly disabled.
+ */
+ if ((param.s_feature_compat & EXT2_FEATURE_COMPAT_RESIZE_INODE) &&
+ !(param.s_feature_ro_compat&EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)) {
+ bb_error_msg_and_die("reserved online resize blocks not supported "
+ "on non-sparse filesystem");
+ }
+
+ if (param.s_blocks_per_group) {
+ if (param.s_blocks_per_group < 256 ||
+ param.s_blocks_per_group > 8 * (unsigned) blocksize) {
+ bb_error_msg_and_die("blocks per group count out of range");
+ }
+ }
+
+ if (!force && param.s_blocks_count >= (1 << 31)) {
+ bb_error_msg_and_die("Filesystem too large. No more than 2**31-1 blocks\n"
+ "\t (8TB using a blocksize of 4k) are currently supported.");
+ }
+
+ if (inode_size) {
+ if (inode_size < EXT2_GOOD_OLD_INODE_SIZE ||
+ inode_size > EXT2_BLOCK_SIZE(&param) ||
+ inode_size & (inode_size - 1)) {
+ bb_error_msg_and_die("invalid inode size %d (min %d/max %d)",
+ inode_size, EXT2_GOOD_OLD_INODE_SIZE,
+ blocksize);
+ }
+ mke2fs_warning_msg((inode_size != EXT2_GOOD_OLD_INODE_SIZE),
+ "%d-byte inodes not usable on most systems",
+ inode_size);
+ param.s_inode_size = inode_size;
+ }
+
+ /*
+ * Calculate number of inodes based on the inode ratio
+ */
+ param.s_inodes_count = num_inodes ? num_inodes :
+ ((__u64) param.s_blocks_count * blocksize)
+ / inode_ratio;
+
+ /*
+ * Calculate number of blocks to reserve
+ */
+ param.s_r_blocks_count = (param.s_blocks_count * reserved_ratio) / 100;
+ return 1;
+}
+
+static void mke2fs_clean_up(void)
+{
+ if (ENABLE_FEATURE_CLEAN_UP && journal_device) free(journal_device);
+}
+
+int mke2fs_main (int argc, char *argv[]);
+int mke2fs_main (int argc, char *argv[])
+{
+ errcode_t retval;
+ ext2_filsys fs;
+ badblocks_list bb_list = 0;
+ unsigned int i;
+ int val;
+ io_manager io_ptr;
+
+ if (ENABLE_FEATURE_CLEAN_UP)
+ atexit(mke2fs_clean_up);
+ if(!PRS(argc, argv))
+ return 0;
+
+#ifdef CONFIG_TESTIO_DEBUG
+ io_ptr = test_io_manager;
+ test_io_backing_manager = unix_io_manager;
+#else
+ io_ptr = unix_io_manager;
+#endif
+
+ /*
+ * Initialize the superblock....
+ */
+ retval = ext2fs_initialize(device_name, 0, &param,
+ io_ptr, &fs);
+ mke2fs_error_msg_and_die(retval, "set up superblock");
+
+ /*
+ * Wipe out the old on-disk superblock
+ */
+ if (!noaction)
+ zap_sector(fs, 2, 6);
+
+ /*
+ * Generate a UUID for it...
+ */
+ uuid_generate(fs->super->s_uuid);
+
+ /*
+ * Initialize the directory index variables
+ */
+ fs->super->s_def_hash_version = EXT2_HASH_TEA;
+ uuid_generate((unsigned char *) fs->super->s_hash_seed);
+
+ /*
+ * Add "jitter" to the superblock's check interval so that we
+ * don't check all the filesystems at the same time. We use a
+ * kludgy hack of using the UUID to derive a random jitter value.
+ */
+ for (i = 0, val = 0 ; i < sizeof(fs->super->s_uuid); i++)
+ val += fs->super->s_uuid[i];
+ fs->super->s_max_mnt_count += val % EXT2_DFL_MAX_MNT_COUNT;
+
+ /*
+ * Override the creator OS, if applicable
+ */
+ if (creator_os && !set_os(fs->super, creator_os)) {
+ bb_error_msg_and_die("unknown os - %s", creator_os);
+ }
+
+ /*
+ * For the Hurd, we will turn off filetype since it doesn't
+ * support it.
+ */
+ if (fs->super->s_creator_os == EXT2_OS_HURD)
+ fs->super->s_feature_incompat &=
+ ~EXT2_FEATURE_INCOMPAT_FILETYPE;
+
+ /*
+ * Set the volume label...
+ */
+ if (volume_label) {
+ snprintf(fs->super->s_volume_name, sizeof(fs->super->s_volume_name), "%s", volume_label);
+ }
+
+ /*
+ * Set the last mount directory
+ */
+ if (mount_dir) {
+ snprintf(fs->super->s_last_mounted, sizeof(fs->super->s_last_mounted), "%s", mount_dir);
+ }
+
+ if (!quiet || noaction)
+ show_stats(fs);
+
+ if (noaction)
+ return 0;
+
+ if (fs->super->s_feature_incompat &
+ EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) {
+ create_journal_dev(fs);
+ return (ext2fs_close(fs) ? 1 : 0);
+ }
+
+ if (bad_blocks_filename)
+ read_bb_file(fs, &bb_list, bad_blocks_filename);
+ if (cflag)
+ test_disk(fs, &bb_list);
+
+ handle_bad_blocks(fs, bb_list);
+ fs->stride = fs_stride;
+ retval = ext2fs_allocate_tables(fs);
+ mke2fs_error_msg_and_die(retval, "allocate filesystem tables");
+ if (super_only) {
+ fs->super->s_state |= EXT2_ERROR_FS;
+ fs->flags &= ~(EXT2_FLAG_IB_DIRTY|EXT2_FLAG_BB_DIRTY);
+ } else {
+ /* rsv must be a power of two (64kB is MD RAID sb alignment) */
+ unsigned int rsv = 65536 / fs->blocksize;
+ unsigned long blocks = fs->super->s_blocks_count;
+ unsigned long start;
+ blk_t ret_blk;
+
+#ifdef ZAP_BOOTBLOCK
+ zap_sector(fs, 0, 2);
+#endif
+
+ /*
+ * Wipe out any old MD RAID (or other) metadata at the end
+ * of the device. This will also verify that the device is
+ * as large as we think. Be careful with very small devices.
+ */
+ start = (blocks & ~(rsv - 1));
+ if (start > rsv)
+ start -= rsv;
+ if (start > 0)
+ retval = zero_blocks(fs, start, blocks - start,
+ NULL, &ret_blk, NULL);
+
+ mke2fs_warning_msg(retval, "cannot zero block %u at end of filesystem", ret_blk);
+ write_inode_tables(fs);
+ create_root_dir(fs);
+ create_lost_and_found(fs);
+ reserve_inodes(fs);
+ create_bad_block_inode(fs, bb_list);
+ if (fs->super->s_feature_compat &
+ EXT2_FEATURE_COMPAT_RESIZE_INODE) {
+ retval = ext2fs_create_resize_inode(fs);
+ mke2fs_error_msg_and_die(retval, "reserve blocks for online resize");
+ }
+ }
+
+ if (journal_device) {
+ make_journal_device(journal_device, fs, quiet, force);
+ } else if (journal_size) {
+ make_journal_blocks(fs, journal_size, journal_flags, quiet);
+ }
+
+ mke2fs_verbose("Writing superblocks and filesystem accounting information: ");
+ retval = ext2fs_flush(fs);
+ mke2fs_warning_msg(retval, "had trouble writing out superblocks");
+ mke2fs_verbose_done();
+ if (!quiet && !getenv("MKE2FS_SKIP_CHECK_MSG"))
+ print_check_message(fs);
+ val = ext2fs_close(fs);
+ return (retval || val) ? 1 : 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/tune2fs.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/tune2fs.c
new file mode 100644
index 0000000..e5a9337
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/tune2fs.c
@@ -0,0 +1,729 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * tune2fs.c - Change the file system parameters on an ext2 file system
+ *
+ * Copyright (C) 1992, 1993, 1994 Remy Card <card@masi.ibp.fr>
+ * Laboratoire MASI, Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ * Copyright 1995, 1996, 1997, 1998, 1999, 2000 by Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+/*
+ * History:
+ * 93/06/01 - Creation
+ * 93/10/31 - Added the -c option to change the maximal mount counts
+ * 93/12/14 - Added -l flag to list contents of superblock
+ * M.J.E. Mol (marcel@duteca.et.tudelft.nl)
+ * F.W. ten Wolde (franky@duteca.et.tudelft.nl)
+ * 93/12/29 - Added the -e option to change errors behavior
+ * 94/02/27 - Ported to use the ext2fs library
+ * 94/03/06 - Added the checks interval from Uwe Ohse (uwe@tirka.gun.de)
+ */
+
+#include <sys/types.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <getopt.h>
+
+#include "e2fsbb.h"
+#include "ext2fs/ext2_fs.h"
+#include "ext2fs/ext2fs.h"
+#include "uuid/uuid.h"
+#include "e2p/e2p.h"
+#include "ext2fs/kernel-jbd.h"
+#include "util.h"
+#include "blkid/blkid.h"
+
+#include "busybox.h"
+
+static char * device_name = NULL;
+static char * new_label, *new_last_mounted, *new_UUID;
+static char * io_options;
+static int c_flag, C_flag, e_flag, f_flag, g_flag, i_flag, l_flag, L_flag;
+static int m_flag, M_flag, r_flag, s_flag = -1, u_flag, U_flag, T_flag;
+static time_t last_check_time;
+static int print_label;
+static int max_mount_count, mount_count, mount_flags;
+static unsigned long interval, reserved_blocks;
+static unsigned reserved_ratio;
+static unsigned long resgid, resuid;
+static unsigned short errors;
+static int open_flag;
+static char *features_cmd;
+static char *mntopts_cmd;
+
+static int journal_size, journal_flags;
+static char *journal_device = NULL;
+
+static const char *please_fsck = "Please run e2fsck on the filesystem\n";
+
+static __u32 ok_features[3] = {
+ EXT3_FEATURE_COMPAT_HAS_JOURNAL | EXT2_FEATURE_COMPAT_DIR_INDEX,
+ EXT2_FEATURE_INCOMPAT_FILETYPE,
+ EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER
+};
+
+/*
+ * Remove an external journal from the filesystem
+ */
+static void remove_journal_device(ext2_filsys fs)
+{
+ char *journal_path;
+ ext2_filsys jfs;
+ char buf[1024];
+ journal_superblock_t *jsb;
+ int i, nr_users;
+ errcode_t retval;
+ int commit_remove_journal = 0;
+ io_manager io_ptr;
+
+ if (f_flag)
+ commit_remove_journal = 1; /* force removal even if error */
+
+ uuid_unparse(fs->super->s_journal_uuid, buf);
+ journal_path = blkid_get_devname(NULL, "UUID", buf);
+
+ if (!journal_path) {
+ journal_path =
+ ext2fs_find_block_device(fs->super->s_journal_dev);
+ if (!journal_path)
+ return;
+ }
+
+ io_ptr = unix_io_manager;
+ retval = ext2fs_open(journal_path, EXT2_FLAG_RW|
+ EXT2_FLAG_JOURNAL_DEV_OK, 0,
+ fs->blocksize, io_ptr, &jfs);
+ if (retval) {
+ bb_error_msg("Failed to open external journal");
+ goto no_valid_journal;
+ }
+ if (!(jfs->super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)) {
+ bb_error_msg("%s is not a journal device", journal_path);
+ goto no_valid_journal;
+ }
+
+ /* Get the journal superblock */
+ if ((retval = io_channel_read_blk(jfs->io, 1, -1024, buf))) {
+ bb_error_msg("Failed to read journal superblock");
+ goto no_valid_journal;
+ }
+
+ jsb = (journal_superblock_t *) buf;
+ if ((jsb->s_header.h_magic != (unsigned) ntohl(JFS_MAGIC_NUMBER)) ||
+ (jsb->s_header.h_blocktype != (unsigned) ntohl(JFS_SUPERBLOCK_V2))) {
+ bb_error_msg("Journal superblock not found!");
+ goto no_valid_journal;
+ }
+
+ /* Find the filesystem UUID */
+ nr_users = ntohl(jsb->s_nr_users);
+ for (i=0; i < nr_users; i++) {
+ if (memcmp(fs->super->s_uuid,
+ &jsb->s_users[i*16], 16) == 0)
+ break;
+ }
+ if (i >= nr_users) {
+ bb_error_msg("Filesystem's UUID not found on journal device");
+ commit_remove_journal = 1;
+ goto no_valid_journal;
+ }
+ nr_users--;
+ for (i=0; i < nr_users; i++)
+ memcpy(&jsb->s_users[i*16], &jsb->s_users[(i+1)*16], 16);
+ jsb->s_nr_users = htonl(nr_users);
+
+ /* Write back the journal superblock */
+ if ((retval = io_channel_write_blk(jfs->io, 1, -1024, buf))) {
+ bb_error_msg("Failed to write journal superblock");
+ goto no_valid_journal;
+ }
+
+ commit_remove_journal = 1;
+
+no_valid_journal:
+ if (commit_remove_journal == 0)
+ bb_error_msg_and_die("Journal NOT removed");
+ fs->super->s_journal_dev = 0;
+ uuid_clear(fs->super->s_journal_uuid);
+ ext2fs_mark_super_dirty(fs);
+ puts("Journal removed");
+ free(journal_path);
+}
+
+/* Helper function for remove_journal_inode */
+static int release_blocks_proc(ext2_filsys fs, blk_t *blocknr,
+ int blockcnt EXT2FS_ATTR((unused)),
+ void *private EXT2FS_ATTR((unused)))
+{
+ blk_t block;
+ int group;
+
+ block = *blocknr;
+ ext2fs_unmark_block_bitmap(fs->block_map,block);
+ group = ext2fs_group_of_blk(fs, block);
+ fs->group_desc[group].bg_free_blocks_count++;
+ fs->super->s_free_blocks_count++;
+ return 0;
+}
+
+/*
+ * Remove the journal inode from the filesystem
+ */
+static void remove_journal_inode(ext2_filsys fs)
+{
+ struct ext2_inode inode;
+ errcode_t retval;
+ ino_t ino = fs->super->s_journal_inum;
+ char *msg = "to read";
+ char *s = "journal inode";
+
+ retval = ext2fs_read_inode(fs, ino, &inode);
+ if (retval)
+ goto REMOVE_JOURNAL_INODE_ERROR;
+ if (ino == EXT2_JOURNAL_INO) {
+ retval = ext2fs_read_bitmaps(fs);
+ if (retval) {
+ msg = "to read bitmaps";
+ s = "";
+ goto REMOVE_JOURNAL_INODE_ERROR;
+ }
+ retval = ext2fs_block_iterate(fs, ino, 0, NULL,
+ release_blocks_proc, NULL);
+ if (retval) {
+ msg = "clearing";
+ goto REMOVE_JOURNAL_INODE_ERROR;
+ }
+ memset(&inode, 0, sizeof(inode));
+ ext2fs_mark_bb_dirty(fs);
+ fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
+ } else
+ inode.i_flags &= ~EXT2_IMMUTABLE_FL;
+ retval = ext2fs_write_inode(fs, ino, &inode);
+ if (retval) {
+ msg = "writing";
+REMOVE_JOURNAL_INODE_ERROR:
+ bb_error_msg_and_die("Failed %s %s", msg, s);
+ }
+ fs->super->s_journal_inum = 0;
+ ext2fs_mark_super_dirty(fs);
+}
+
+/*
+ * Update the default mount options
+ */
+static void update_mntopts(ext2_filsys fs, char *mntopts)
+{
+ struct ext2_super_block *sb= fs->super;
+
+ if (e2p_edit_mntopts(mntopts, &sb->s_default_mount_opts, ~0))
+ bb_error_msg_and_die("Invalid mount option set: %s", mntopts);
+ ext2fs_mark_super_dirty(fs);
+}
+
+/*
+ * Update the feature set as provided by the user.
+ */
+static void update_feature_set(ext2_filsys fs, char *features)
+{
+ int sparse, old_sparse, filetype, old_filetype;
+ int journal, old_journal, dxdir, old_dxdir;
+ struct ext2_super_block *sb= fs->super;
+ __u32 old_compat, old_incompat, old_ro_compat;
+
+ old_compat = sb->s_feature_compat;
+ old_ro_compat = sb->s_feature_ro_compat;
+ old_incompat = sb->s_feature_incompat;
+
+ old_sparse = sb->s_feature_ro_compat &
+ EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER;
+ old_filetype = sb->s_feature_incompat &
+ EXT2_FEATURE_INCOMPAT_FILETYPE;
+ old_journal = sb->s_feature_compat &
+ EXT3_FEATURE_COMPAT_HAS_JOURNAL;
+ old_dxdir = sb->s_feature_compat &
+ EXT2_FEATURE_COMPAT_DIR_INDEX;
+ if (e2p_edit_feature(features, &sb->s_feature_compat, ok_features))
+ bb_error_msg_and_die("Invalid filesystem option set: %s", features);
+ sparse = sb->s_feature_ro_compat &
+ EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER;
+ filetype = sb->s_feature_incompat &
+ EXT2_FEATURE_INCOMPAT_FILETYPE;
+ journal = sb->s_feature_compat &
+ EXT3_FEATURE_COMPAT_HAS_JOURNAL;
+ dxdir = sb->s_feature_compat &
+ EXT2_FEATURE_COMPAT_DIR_INDEX;
+ if (old_journal && !journal) {
+ if ((mount_flags & EXT2_MF_MOUNTED) &&
+ !(mount_flags & EXT2_MF_READONLY)) {
+ bb_error_msg_and_die(
+ "The has_journal flag may only be "
+ "cleared when the filesystem is\n"
+ "unmounted or mounted "
+ "read-only");
+ }
+ if (sb->s_feature_incompat &
+ EXT3_FEATURE_INCOMPAT_RECOVER) {
+ bb_error_msg_and_die(
+ "The needs_recovery flag is set. "
+ "%s before clearing the has_journal flag.",
+ please_fsck);
+ }
+ if (sb->s_journal_inum) {
+ remove_journal_inode(fs);
+ }
+ if (sb->s_journal_dev) {
+ remove_journal_device(fs);
+ }
+ }
+ if (journal && !old_journal) {
+ /*
+ * If adding a journal flag, let the create journal
+ * code below handle creating setting the flag and
+ * creating the journal. We supply a default size if
+ * necessary.
+ */
+ if (!journal_size)
+ journal_size = -1;
+ sb->s_feature_compat &= ~EXT3_FEATURE_COMPAT_HAS_JOURNAL;
+ }
+ if (dxdir && !old_dxdir) {
+ if (!sb->s_def_hash_version)
+ sb->s_def_hash_version = EXT2_HASH_TEA;
+ if (uuid_is_null((unsigned char *) sb->s_hash_seed))
+ uuid_generate((unsigned char *) sb->s_hash_seed);
+ }
+
+ if (sb->s_rev_level == EXT2_GOOD_OLD_REV &&
+ (sb->s_feature_compat || sb->s_feature_ro_compat ||
+ sb->s_feature_incompat))
+ ext2fs_update_dynamic_rev(fs);
+ if ((sparse != old_sparse) ||
+ (filetype != old_filetype)) {
+ sb->s_state &= ~EXT2_VALID_FS;
+ printf("\n%s\n", please_fsck);
+ }
+ if ((old_compat != sb->s_feature_compat) ||
+ (old_ro_compat != sb->s_feature_ro_compat) ||
+ (old_incompat != sb->s_feature_incompat))
+ ext2fs_mark_super_dirty(fs);
+}
+
+/*
+ * Add a journal to the filesystem.
+ */
+static void add_journal(ext2_filsys fs)
+{
+ if (fs->super->s_feature_compat &
+ EXT3_FEATURE_COMPAT_HAS_JOURNAL) {
+ bb_error_msg_and_die("The filesystem already has a journal");
+ }
+ if (journal_device) {
+ make_journal_device(journal_device, fs, 0, 0);
+ } else if (journal_size) {
+ make_journal_blocks(fs, journal_size, journal_flags, 0);
+ /*
+ * If the filesystem wasn't mounted, we need to force
+ * the block group descriptors out.
+ */
+ if ((mount_flags & EXT2_MF_MOUNTED) == 0)
+ fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
+ }
+ print_check_message(fs);
+}
+
+/*
+ * Busybox stuff
+ */
+static char * x_blkid_get_devname(const char *token)
+{
+ char * dev_name;
+
+ if (!(dev_name = blkid_get_devname(NULL, token, NULL)))
+ bb_error_msg_and_die("Unable to resolve '%s'", token);
+ return dev_name;
+}
+
+#ifdef CONFIG_E2LABEL
+static void parse_e2label_options(int argc, char ** argv)
+{
+ if ((argc < 2) || (argc > 3))
+ bb_show_usage();
+ io_options = strchr(argv[1], '?');
+ if (io_options)
+ *io_options++ = 0;
+ device_name = x_blkid_get_devname(argv[1]);
+ if (argc == 3) {
+ open_flag = EXT2_FLAG_RW | EXT2_FLAG_JOURNAL_DEV_OK;
+ L_flag = 1;
+ new_label = argv[2];
+ } else
+ print_label++;
+}
+#else
+#define parse_e2label_options(x,y)
+#endif
+
+static time_t parse_time(char *str)
+{
+ struct tm ts;
+
+ if (strcmp(str, "now") == 0) {
+ return time(0);
+ }
+ memset(&ts, 0, sizeof(ts));
+#ifdef HAVE_STRPTIME
+ strptime(str, "%Y%m%d%H%M%S", &ts);
+#else
+ sscanf(str, "%4d%2d%2d%2d%2d%2d", &ts.tm_year, &ts.tm_mon,
+ &ts.tm_mday, &ts.tm_hour, &ts.tm_min, &ts.tm_sec);
+ ts.tm_year -= 1900;
+ ts.tm_mon -= 1;
+ if (ts.tm_year < 0 || ts.tm_mon < 0 || ts.tm_mon > 11 ||
+ ts.tm_mday < 0 || ts.tm_mday > 31 || ts.tm_hour > 23 ||
+ ts.tm_min > 59 || ts.tm_sec > 61)
+ ts.tm_mday = 0;
+#endif
+ if (ts.tm_mday == 0) {
+ bb_error_msg_and_die("Cannot parse date/time specifier: %s", str);
+ }
+ return mktime(&ts);
+}
+
+static void parse_tune2fs_options(int argc, char **argv)
+{
+ int c;
+ char * tmp;
+
+ printf("tune2fs %s (%s)\n", E2FSPROGS_VERSION, E2FSPROGS_DATE);
+ while ((c = getopt(argc, argv, "c:e:fg:i:jlm:o:r:s:u:C:J:L:M:O:T:U:")) != EOF)
+ switch (c)
+ {
+ case 'c':
+ max_mount_count = xatou_range(optarg, 0, 16000);
+ if (max_mount_count == 0)
+ max_mount_count = -1;
+ c_flag = 1;
+ open_flag = EXT2_FLAG_RW;
+ break;
+ case 'C':
+ mount_count = xatou_range(optarg, 0, 16000);
+ C_flag = 1;
+ open_flag = EXT2_FLAG_RW;
+ break;
+ case 'e':
+ if (strcmp (optarg, "continue") == 0)
+ errors = EXT2_ERRORS_CONTINUE;
+ else if (strcmp (optarg, "remount-ro") == 0)
+ errors = EXT2_ERRORS_RO;
+ else if (strcmp (optarg, "panic") == 0)
+ errors = EXT2_ERRORS_PANIC;
+ else {
+ bb_error_msg_and_die("bad error behavior - %s", optarg);
+ }
+ e_flag = 1;
+ open_flag = EXT2_FLAG_RW;
+ break;
+ case 'f': /* Force */
+ f_flag = 1;
+ break;
+ case 'g':
+ resgid = bb_strtoul(optarg, NULL, 10);
+ if (errno)
+ resgid = xgroup2gid(optarg);
+ g_flag = 1;
+ open_flag = EXT2_FLAG_RW;
+ break;
+ case 'i':
+ interval = strtoul(optarg, &tmp, 0);
+ switch (*tmp) {
+ case 's':
+ tmp++;
+ break;
+ case '\0':
+ case 'd':
+ case 'D': /* days */
+ interval *= 86400;
+ if (*tmp != '\0')
+ tmp++;
+ break;
+ case 'm':
+ case 'M': /* months! */
+ interval *= 86400 * 30;
+ tmp++;
+ break;
+ case 'w':
+ case 'W': /* weeks */
+ interval *= 86400 * 7;
+ tmp++;
+ break;
+ }
+ if (*tmp || interval > (365 * 86400)) {
+ bb_error_msg_and_die("bad interval - %s", optarg);
+ }
+ i_flag = 1;
+ open_flag = EXT2_FLAG_RW;
+ break;
+ case 'j':
+ if (!journal_size)
+ journal_size = -1;
+ open_flag = EXT2_FLAG_RW;
+ break;
+ case 'J':
+ parse_journal_opts(&journal_device, &journal_flags, &journal_size, optarg);
+ open_flag = EXT2_FLAG_RW;
+ break;
+ case 'l':
+ l_flag = 1;
+ break;
+ case 'L':
+ new_label = optarg;
+ L_flag = 1;
+ open_flag = EXT2_FLAG_RW |
+ EXT2_FLAG_JOURNAL_DEV_OK;
+ break;
+ case 'm':
+ reserved_ratio = xatou_range(optarg, 0, 50);
+ m_flag = 1;
+ open_flag = EXT2_FLAG_RW;
+ break;
+ case 'M':
+ new_last_mounted = optarg;
+ M_flag = 1;
+ open_flag = EXT2_FLAG_RW;
+ break;
+ case 'o':
+ if (mntopts_cmd) {
+ bb_error_msg_and_die("-o may only be specified once");
+ }
+ mntopts_cmd = optarg;
+ open_flag = EXT2_FLAG_RW;
+ break;
+
+ case 'O':
+ if (features_cmd) {
+ bb_error_msg_and_die("-O may only be specified once");
+ }
+ features_cmd = optarg;
+ open_flag = EXT2_FLAG_RW;
+ break;
+ case 'r':
+ reserved_blocks = xatoul(optarg);
+ r_flag = 1;
+ open_flag = EXT2_FLAG_RW;
+ break;
+ case 's':
+ s_flag = atoi(optarg);
+ open_flag = EXT2_FLAG_RW;
+ break;
+ case 'T':
+ T_flag = 1;
+ last_check_time = parse_time(optarg);
+ open_flag = EXT2_FLAG_RW;
+ break;
+ case 'u':
+ resuid = bb_strtoul(optarg, NULL, 10);
+ if (errno)
+ resuid = xuname2uid(optarg);
+ u_flag = 1;
+ open_flag = EXT2_FLAG_RW;
+ break;
+ case 'U':
+ new_UUID = optarg;
+ U_flag = 1;
+ open_flag = EXT2_FLAG_RW |
+ EXT2_FLAG_JOURNAL_DEV_OK;
+ break;
+ default:
+ bb_show_usage();
+ }
+ if (optind < argc - 1 || optind == argc)
+ bb_show_usage();
+ if (!open_flag && !l_flag)
+ bb_show_usage();
+ io_options = strchr(argv[optind], '?');
+ if (io_options)
+ *io_options++ = 0;
+ device_name = x_blkid_get_devname(argv[optind]);
+}
+
+#ifdef CONFIG_FINDFS
+static ATTRIBUTE_NORETURN void do_findfs(int argc, char **argv)
+{
+ if ((argc != 2) ||
+ (strncmp(argv[1], "LABEL=", 6) && strncmp(argv[1], "UUID=", 5)))
+ bb_show_usage();
+ device_name = x_blkid_get_devname(argv[1]);
+ puts(device_name);
+ exit(0);
+}
+#else
+#define do_findfs(x, y)
+#endif
+
+static void tune2fs_clean_up(void)
+{
+ if (ENABLE_FEATURE_CLEAN_UP && device_name) free(device_name);
+ if (ENABLE_FEATURE_CLEAN_UP && journal_device) free(journal_device);
+}
+
+int tune2fs_main(int argc, char **argv);
+int tune2fs_main(int argc, char **argv)
+{
+ errcode_t retval;
+ ext2_filsys fs;
+ struct ext2_super_block *sb;
+ io_manager io_ptr;
+
+ if (ENABLE_FEATURE_CLEAN_UP)
+ atexit(tune2fs_clean_up);
+
+ if (ENABLE_FINDFS && (applet_name[0] == 'f')) /* findfs */
+ do_findfs(argc, argv); /* no return */
+ else if (ENABLE_E2LABEL && (applet_name[0] == 'e')) /* e2label */
+ parse_e2label_options(argc, argv);
+ else
+ parse_tune2fs_options(argc, argv); /* tune2fs */
+
+ io_ptr = unix_io_manager;
+ retval = ext2fs_open2(device_name, io_options, open_flag,
+ 0, 0, io_ptr, &fs);
+ if (retval)
+ bb_error_msg_and_die("No valid superblock on %s", device_name);
+ sb = fs->super;
+ if (print_label) {
+ /* For e2label emulation */
+ printf("%.*s\n", (int) sizeof(sb->s_volume_name),
+ sb->s_volume_name);
+ return 0;
+ }
+ retval = ext2fs_check_if_mounted(device_name, &mount_flags);
+ if (retval)
+ bb_error_msg_and_die("cannot determine if %s is mounted", device_name);
+ /* Normally we only need to write out the superblock */
+ fs->flags |= EXT2_FLAG_SUPER_ONLY;
+
+ if (c_flag) {
+ sb->s_max_mnt_count = max_mount_count;
+ ext2fs_mark_super_dirty(fs);
+ printf("Setting maximal mount count to %d\n", max_mount_count);
+ }
+ if (C_flag) {
+ sb->s_mnt_count = mount_count;
+ ext2fs_mark_super_dirty(fs);
+ printf("Setting current mount count to %d\n", mount_count);
+ }
+ if (e_flag) {
+ sb->s_errors = errors;
+ ext2fs_mark_super_dirty(fs);
+ printf("Setting error behavior to %d\n", errors);
+ }
+ if (g_flag) {
+ sb->s_def_resgid = resgid;
+ ext2fs_mark_super_dirty(fs);
+ printf("Setting reserved blocks gid to %lu\n", resgid);
+ }
+ if (i_flag) {
+ sb->s_checkinterval = interval;
+ ext2fs_mark_super_dirty(fs);
+ printf("Setting interval between check %lu seconds\n", interval);
+ }
+ if (m_flag) {
+ sb->s_r_blocks_count = (sb->s_blocks_count / 100)
+ * reserved_ratio;
+ ext2fs_mark_super_dirty(fs);
+ printf("Setting reserved blocks percentage to %u (%u blocks)\n",
+ reserved_ratio, sb->s_r_blocks_count);
+ }
+ if (r_flag) {
+ if (reserved_blocks >= sb->s_blocks_count/2)
+ bb_error_msg_and_die("reserved blocks count is too big (%lu)", reserved_blocks);
+ sb->s_r_blocks_count = reserved_blocks;
+ ext2fs_mark_super_dirty(fs);
+ printf("Setting reserved blocks count to %lu\n", reserved_blocks);
+ }
+ if (s_flag == 1) {
+ if (sb->s_feature_ro_compat &
+ EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)
+ bb_error_msg("\nThe filesystem already has sparse superblocks");
+ else {
+ sb->s_feature_ro_compat |=
+ EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER;
+ sb->s_state &= ~EXT2_VALID_FS;
+ ext2fs_mark_super_dirty(fs);
+ printf("\nSparse superblock flag set. %s", please_fsck);
+ }
+ }
+ if (s_flag == 0) {
+ if (!(sb->s_feature_ro_compat &
+ EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER))
+ bb_error_msg("\nThe filesystem already has sparse superblocks disabled");
+ else {
+ sb->s_feature_ro_compat &=
+ ~EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER;
+ sb->s_state &= ~EXT2_VALID_FS;
+ fs->flags |= EXT2_FLAG_MASTER_SB_ONLY;
+ ext2fs_mark_super_dirty(fs);
+ printf("\nSparse superblock flag cleared. %s", please_fsck);
+ }
+ }
+ if (T_flag) {
+ sb->s_lastcheck = last_check_time;
+ ext2fs_mark_super_dirty(fs);
+ printf("Setting time filesystem last checked to %s\n",
+ ctime(&last_check_time));
+ }
+ if (u_flag) {
+ sb->s_def_resuid = resuid;
+ ext2fs_mark_super_dirty(fs);
+ printf("Setting reserved blocks uid to %lu\n", resuid);
+ }
+ if (L_flag) {
+ if (strlen(new_label) > sizeof(sb->s_volume_name))
+ bb_error_msg("Warning: label too long, truncating");
+ memset(sb->s_volume_name, 0, sizeof(sb->s_volume_name));
+ safe_strncpy(sb->s_volume_name, new_label,
+ sizeof(sb->s_volume_name));
+ ext2fs_mark_super_dirty(fs);
+ }
+ if (M_flag) {
+ memset(sb->s_last_mounted, 0, sizeof(sb->s_last_mounted));
+ safe_strncpy(sb->s_last_mounted, new_last_mounted,
+ sizeof(sb->s_last_mounted));
+ ext2fs_mark_super_dirty(fs);
+ }
+ if (mntopts_cmd)
+ update_mntopts(fs, mntopts_cmd);
+ if (features_cmd)
+ update_feature_set(fs, features_cmd);
+ if (journal_size || journal_device)
+ add_journal(fs);
+
+ if (U_flag) {
+ if ((strcasecmp(new_UUID, "null") == 0) ||
+ (strcasecmp(new_UUID, "clear") == 0)) {
+ uuid_clear(sb->s_uuid);
+ } else if (strcasecmp(new_UUID, "time") == 0) {
+ uuid_generate_time(sb->s_uuid);
+ } else if (strcasecmp(new_UUID, "random") == 0) {
+ uuid_generate(sb->s_uuid);
+ } else if (uuid_parse(new_UUID, sb->s_uuid)) {
+ bb_error_msg_and_die("Invalid UUID format");
+ }
+ ext2fs_mark_super_dirty(fs);
+ }
+
+ if (l_flag)
+ list_super (sb);
+ return (ext2fs_close (fs) ? 1 : 0);
+}
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/util.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/util.c
new file mode 100644
index 0000000..b30c294
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/util.c
@@ -0,0 +1,267 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * util.c --- helper functions used by tune2fs and mke2fs
+ *
+ * Copyright 1995, 1996, 1997, 1998, 1999, 2000 by Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <linux/major.h>
+#include <sys/stat.h>
+
+#include "e2fsbb.h"
+#include "e2p/e2p.h"
+#include "ext2fs/ext2_fs.h"
+#include "ext2fs/ext2fs.h"
+#include "blkid/blkid.h"
+#include "util.h"
+
+void proceed_question(void)
+{
+ fputs("Proceed anyway? (y,n) ", stdout);
+ if (bb_ask_confirmation() == 0)
+ exit(1);
+}
+
+void check_plausibility(const char *device, int force)
+{
+ int val;
+ struct stat s;
+ val = stat(device, &s);
+ if (force)
+ return;
+ if(val == -1)
+ bb_perror_msg_and_die("cannot stat %s", device);
+ if (!S_ISBLK(s.st_mode)) {
+ printf("%s is not a block special device.\n", device);
+ proceed_question();
+ return;
+ }
+
+#ifdef HAVE_LINUX_MAJOR_H
+#ifndef MAJOR
+#define MAJOR(dev) ((dev)>>8)
+#define MINOR(dev) ((dev) & 0xff)
+#endif
+#ifndef SCSI_BLK_MAJOR
+#ifdef SCSI_DISK0_MAJOR
+#ifdef SCSI_DISK8_MAJOR
+#define SCSI_DISK_MAJOR(M) ((M) == SCSI_DISK0_MAJOR || \
+ ((M) >= SCSI_DISK1_MAJOR && (M) <= SCSI_DISK7_MAJOR) || \
+ ((M) >= SCSI_DISK8_MAJOR && (M) <= SCSI_DISK15_MAJOR))
+#else
+#define SCSI_DISK_MAJOR(M) ((M) == SCSI_DISK0_MAJOR || \
+ ((M) >= SCSI_DISK1_MAJOR && (M) <= SCSI_DISK7_MAJOR))
+#endif /* defined(SCSI_DISK8_MAJOR) */
+#define SCSI_BLK_MAJOR(M) (SCSI_DISK_MAJOR((M)) || (M) == SCSI_CDROM_MAJOR)
+#else
+#define SCSI_BLK_MAJOR(M) ((M) == SCSI_DISK_MAJOR || (M) == SCSI_CDROM_MAJOR)
+#endif /* defined(SCSI_DISK0_MAJOR) */
+#endif /* defined(SCSI_BLK_MAJOR) */
+ if (((MAJOR(s.st_rdev) == HD_MAJOR &&
+ MINOR(s.st_rdev)%64 == 0) ||
+ (SCSI_BLK_MAJOR(MAJOR(s.st_rdev)) &&
+ MINOR(s.st_rdev)%16 == 0))) {
+ printf("%s is entire device, not just one partition!\n", device);
+ proceed_question();
+ }
+#endif
+}
+
+void check_mount(const char *device, int force, const char *type)
+{
+ errcode_t retval;
+ int mount_flags;
+
+ retval = ext2fs_check_if_mounted(device, &mount_flags);
+ if (retval) {
+ bb_error_msg("cannot determine if %s is mounted", device);
+ return;
+ }
+ if (mount_flags & EXT2_MF_MOUNTED) {
+ bb_error_msg("%s is mounted !", device);
+force_check:
+ if (force)
+ bb_error_msg("badblocks forced anyways");
+ else
+ bb_error_msg_and_die("it's not safe to run badblocks!");
+ }
+
+ if (mount_flags & EXT2_MF_BUSY) {
+ bb_error_msg("%s is apparently in use by the system", device);
+ goto force_check;
+ }
+
+}
+
+void parse_journal_opts(char **journal_device, int *journal_flags,
+ int *journal_size, const char *opts)
+{
+ char *buf, *token, *next, *p, *arg;
+ int journal_usage = 0;
+ buf = xstrdup(opts);
+ for (token = buf; token && *token; token = next) {
+ p = strchr(token, ',');
+ next = 0;
+ if (p) {
+ *p = 0;
+ next = p+1;
+ }
+ arg = strchr(token, '=');
+ if (arg) {
+ *arg = 0;
+ arg++;
+ }
+ if (strcmp(token, "device") == 0) {
+ *journal_device = blkid_get_devname(NULL, arg, NULL);
+ if (!journal_device) {
+ journal_usage++;
+ continue;
+ }
+ } else if (strcmp(token, "size") == 0) {
+ if (!arg) {
+ journal_usage++;
+ continue;
+ }
+ (*journal_size) = strtoul(arg, &p, 0);
+ if (*p)
+ journal_usage++;
+ } else if (strcmp(token, "v1_superblock") == 0) {
+ (*journal_flags) |= EXT2_MKJOURNAL_V1_SUPER;
+ continue;
+ } else
+ journal_usage++;
+ }
+ if (journal_usage)
+ bb_error_msg_and_die(
+ "\nBad journal options specified.\n\n"
+ "Journal options are separated by commas, "
+ "and may take an argument which\n"
+ "\tis set off by an equals ('=') sign.\n\n"
+ "Valid journal options are:\n"
+ "\tsize=<journal size in megabytes>\n"
+ "\tdevice=<journal device>\n\n"
+ "The journal size must be between "
+ "1024 and 102400 filesystem blocks.\n\n");
+}
+
+/*
+ * Determine the number of journal blocks to use, either via
+ * user-specified # of megabytes, or via some intelligently selected
+ * defaults.
+ *
+ * Find a reasonable journal file size (in blocks) given the number of blocks
+ * in the filesystem. For very small filesystems, it is not reasonable to
+ * have a journal that fills more than half of the filesystem.
+ */
+int figure_journal_size(int size, ext2_filsys fs)
+{
+ blk_t j_blocks;
+
+ if (fs->super->s_blocks_count < 2048) {
+ bb_error_msg("Filesystem too small for a journal");
+ return 0;
+ }
+
+ if (size >= 0) {
+ j_blocks = size * 1024 / (fs->blocksize / 1024);
+ if (j_blocks < 1024 || j_blocks > 102400)
+ bb_error_msg_and_die("\nThe requested journal "
+ "size is %d blocks;\n it must be "
+ "between 1024 and 102400 blocks; Aborting",
+ j_blocks);
+ if (j_blocks > fs->super->s_free_blocks_count)
+ bb_error_msg_and_die("Journal size too big for filesystem");
+ return j_blocks;
+ }
+
+ if (fs->super->s_blocks_count < 32768)
+ j_blocks = 1024;
+ else if (fs->super->s_blocks_count < 256*1024)
+ j_blocks = 4096;
+ else if (fs->super->s_blocks_count < 512*1024)
+ j_blocks = 8192;
+ else if (fs->super->s_blocks_count < 1024*1024)
+ j_blocks = 16384;
+ else
+ j_blocks = 32768;
+
+ return j_blocks;
+}
+
+void print_check_message(ext2_filsys fs)
+{
+ printf("This filesystem will be automatically "
+ "checked every %d mounts or\n"
+ "%g days, whichever comes first. "
+ "Use tune2fs -c or -i to override.\n",
+ fs->super->s_max_mnt_count,
+ (double)fs->super->s_checkinterval / (3600 * 24));
+}
+
+void make_journal_device(char *journal_device, ext2_filsys fs, int quiet, int force)
+{
+ errcode_t retval;
+ ext2_filsys jfs;
+ io_manager io_ptr;
+
+ check_plausibility(journal_device, force);
+ check_mount(journal_device, force, "journal");
+ io_ptr = unix_io_manager;
+ retval = ext2fs_open(journal_device, EXT2_FLAG_RW|
+ EXT2_FLAG_JOURNAL_DEV_OK, 0,
+ fs->blocksize, io_ptr, &jfs);
+ if (retval)
+ bb_error_msg_and_die("cannot journal device %s", journal_device);
+ if(!quiet)
+ printf("Adding journal to device %s: ", journal_device);
+ fflush(stdout);
+ retval = ext2fs_add_journal_device(fs, jfs);
+ if(retval)
+ bb_error_msg_and_die("\nFailed to add journal to device %s", journal_device);
+ if(!quiet)
+ puts("done");
+ ext2fs_close(jfs);
+}
+
+void make_journal_blocks(ext2_filsys fs, int journal_size, int journal_flags, int quiet)
+{
+ unsigned long journal_blocks;
+ errcode_t retval;
+
+ journal_blocks = figure_journal_size(journal_size, fs);
+ if (!journal_blocks) {
+ fs->super->s_feature_compat &=
+ ~EXT3_FEATURE_COMPAT_HAS_JOURNAL;
+ return;
+ }
+ if(!quiet)
+ printf("Creating journal (%ld blocks): ", journal_blocks);
+ fflush(stdout);
+ retval = ext2fs_add_journal_inode(fs, journal_blocks,
+ journal_flags);
+ if(retval)
+ bb_error_msg_and_die("cannot create journal");
+ if(!quiet)
+ puts("done");
+}
+
+char *e2fs_set_sbin_path(void)
+{
+ char *oldpath = getenv("PATH");
+ /* Update our PATH to include /sbin */
+#define PATH_SET "/sbin"
+ if (oldpath)
+ oldpath = xasprintf("%s:%s", PATH_SET, oldpath);
+ else
+ oldpath = PATH_SET;
+ putenv (oldpath);
+ return oldpath;
+}
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/util.h b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/util.h
new file mode 100644
index 0000000..80d2417
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/util.h
@@ -0,0 +1,22 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * util.h --- header file defining prototypes for helper functions
+ * used by tune2fs and mke2fs
+ *
+ * Copyright 2000 by Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+extern void proceed_question(void);
+extern void check_plausibility(const char *device, int force);
+extern void parse_journal_opts(char **, int *, int *, const char *opts);
+extern void check_mount(const char *device, int force, const char *type);
+extern int figure_journal_size(int size, ext2_filsys fs);
+extern void print_check_message(ext2_filsys fs);
+extern void make_journal_device(char *journal_device, ext2_filsys fs, int quiet, int force);
+extern void make_journal_blocks(ext2_filsys fs, int journal_size, int journal_flags, int quiet);
+extern char *e2fs_set_sbin_path(void);
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/uuid/Kbuild b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/uuid/Kbuild
new file mode 100644
index 0000000..dde9818
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/uuid/Kbuild
@@ -0,0 +1,14 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under the GPL v2, see the file LICENSE in this tarball.
+
+NEEDED-$(CONFIG_E2FSCK) = y
+NEEDED-$(CONFIG_FSCK) = y
+NEEDED-$(CONFIG_MKE2FS) = y
+NEEDED-$(CONFIG_TUNE2FS) = y
+
+lib-y:=
+lib-$(NEEDED-y) += compare.o gen_uuid.o pack.o parse.o unpack.o unparse.o \
+ uuid_time.o
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/uuid/compare.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/uuid/compare.c
new file mode 100644
index 0000000..348ea7c
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/uuid/compare.c
@@ -0,0 +1,55 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * compare.c --- compare whether or not two UUID's are the same
+ *
+ * Returns 0 if the two UUID's are different, and 1 if they are the same.
+ *
+ * Copyright (C) 1996, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * %End-Header%
+ */
+
+#include "uuidP.h"
+#include <string.h>
+
+#define UUCMP(u1,u2) if (u1 != u2) return (u1 < u2) ? -1 : 1;
+
+int uuid_compare(const uuid_t uu1, const uuid_t uu2)
+{
+ struct uuid uuid1, uuid2;
+
+ uuid_unpack(uu1, &uuid1);
+ uuid_unpack(uu2, &uuid2);
+
+ UUCMP(uuid1.time_low, uuid2.time_low);
+ UUCMP(uuid1.time_mid, uuid2.time_mid);
+ UUCMP(uuid1.time_hi_and_version, uuid2.time_hi_and_version);
+ UUCMP(uuid1.clock_seq, uuid2.clock_seq);
+ return memcmp(uuid1.node, uuid2.node, 6);
+}
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/uuid/gen_uuid.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/uuid/gen_uuid.c
new file mode 100644
index 0000000..03a9f37
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/uuid/gen_uuid.c
@@ -0,0 +1,304 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * gen_uuid.c --- generate a DCE-compatible uuid
+ *
+ * Copyright (C) 1996, 1997, 1998, 1999 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * %End-Header%
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#include <sys/socket.h>
+#ifdef HAVE_SYS_SOCKIO_H
+#include <sys/sockio.h>
+#endif
+#ifdef HAVE_NET_IF_H
+#include <net/if.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_NET_IF_DL_H
+#include <net/if_dl.h>
+#endif
+
+#include "uuidP.h"
+
+#ifdef HAVE_SRANDOM
+#define srand(x) srandom(x)
+#define rand() random()
+#endif
+
+static int get_random_fd(void)
+{
+ struct timeval tv;
+ static int fd = -2;
+ int i;
+
+ if (fd == -2) {
+ gettimeofday(&tv, 0);
+ fd = open("/dev/urandom", O_RDONLY);
+ if (fd == -1)
+ fd = open("/dev/random", O_RDONLY | O_NONBLOCK);
+ srand((getpid() << 16) ^ getuid() ^ tv.tv_sec ^ tv.tv_usec);
+ }
+ /* Crank the random number generator a few times */
+ gettimeofday(&tv, 0);
+ for (i = (tv.tv_sec ^ tv.tv_usec) & 0x1F; i > 0; i--)
+ rand();
+ return fd;
+}
+
+
+/*
+ * Generate a series of random bytes. Use /dev/urandom if possible,
+ * and if not, use srandom/random.
+ */
+static void get_random_bytes(void *buf, int nbytes)
+{
+ int i, n = nbytes, fd = get_random_fd();
+ int lose_counter = 0;
+ unsigned char *cp = (unsigned char *) buf;
+
+ if (fd >= 0) {
+ while (n > 0) {
+ i = read(fd, cp, n);
+ if (i <= 0) {
+ if (lose_counter++ > 16)
+ break;
+ continue;
+ }
+ n -= i;
+ cp += i;
+ lose_counter = 0;
+ }
+ }
+
+ /*
+ * We do this all the time, but this is the only source of
+ * randomness if /dev/random/urandom is out to lunch.
+ */
+ for (cp = buf, i = 0; i < nbytes; i++)
+ *cp++ ^= (rand() >> 7) & 0xFF;
+}
+
+/*
+ * Get the ethernet hardware address, if we can find it...
+ */
+static int get_node_id(unsigned char *node_id)
+{
+#ifdef HAVE_NET_IF_H
+ int sd;
+ struct ifreq ifr, *ifrp;
+ struct ifconf ifc;
+ char buf[1024];
+ int n, i;
+ unsigned char *a;
+#ifdef HAVE_NET_IF_DL_H
+ struct sockaddr_dl *sdlp;
+#endif
+
+/*
+ * BSD 4.4 defines the size of an ifreq to be
+ * max(sizeof(ifreq), sizeof(ifreq.ifr_name)+ifreq.ifr_addr.sa_len
+ * However, under earlier systems, sa_len isn't present, so the size is
+ * just sizeof(struct ifreq)
+ */
+#ifdef HAVE_SA_LEN
+#ifndef max
+#define max(a,b) ((a) > (b) ? (a) : (b))
+#endif
+#define ifreq_size(i) max(sizeof(struct ifreq),\
+ sizeof((i).ifr_name)+(i).ifr_addr.sa_len)
+#else
+#define ifreq_size(i) sizeof(struct ifreq)
+#endif /* HAVE_SA_LEN*/
+
+ sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
+ if (sd < 0) {
+ return -1;
+ }
+ memset(buf, 0, sizeof(buf));
+ ifc.ifc_len = sizeof(buf);
+ ifc.ifc_buf = buf;
+ if (ioctl (sd, SIOCGIFCONF, (char *)&ifc) < 0) {
+ close(sd);
+ return -1;
+ }
+ n = ifc.ifc_len;
+ for (i = 0; i < n; i+= ifreq_size(*ifrp) ) {
+ ifrp = (struct ifreq *)((char *) ifc.ifc_buf+i);
+ strncpy(ifr.ifr_name, ifrp->ifr_name, IFNAMSIZ);
+#ifdef SIOCGIFHWADDR
+ if (ioctl(sd, SIOCGIFHWADDR, &ifr) < 0)
+ continue;
+ a = (unsigned char *) &ifr.ifr_hwaddr.sa_data;
+#else
+#ifdef SIOCGENADDR
+ if (ioctl(sd, SIOCGENADDR, &ifr) < 0)
+ continue;
+ a = (unsigned char *) ifr.ifr_enaddr;
+#else
+#ifdef HAVE_NET_IF_DL_H
+ sdlp = (struct sockaddr_dl *) &ifrp->ifr_addr;
+ if ((sdlp->sdl_family != AF_LINK) || (sdlp->sdl_alen != 6))
+ continue;
+ a = (unsigned char *) &sdlp->sdl_data[sdlp->sdl_nlen];
+#else
+ /*
+ * XXX we don't have a way of getting the hardware
+ * address
+ */
+ close(sd);
+ return 0;
+#endif /* HAVE_NET_IF_DL_H */
+#endif /* SIOCGENADDR */
+#endif /* SIOCGIFHWADDR */
+ if (!a[0] && !a[1] && !a[2] && !a[3] && !a[4] && !a[5])
+ continue;
+ if (node_id) {
+ memcpy(node_id, a, 6);
+ close(sd);
+ return 1;
+ }
+ }
+ close(sd);
+#endif
+ return 0;
+}
+
+/* Assume that the gettimeofday() has microsecond granularity */
+#define MAX_ADJUSTMENT 10
+
+static int get_clock(uint32_t *clock_high, uint32_t *clock_low, uint16_t *ret_clock_seq)
+{
+ static int adjustment = 0;
+ static struct timeval last = {0, 0};
+ static uint16_t clock_seq;
+ struct timeval tv;
+ unsigned long long clock_reg;
+
+try_again:
+ gettimeofday(&tv, 0);
+ if ((last.tv_sec == 0) && (last.tv_usec == 0)) {
+ get_random_bytes(&clock_seq, sizeof(clock_seq));
+ clock_seq &= 0x3FFF;
+ last = tv;
+ last.tv_sec--;
+ }
+ if ((tv.tv_sec < last.tv_sec) ||
+ ((tv.tv_sec == last.tv_sec) &&
+ (tv.tv_usec < last.tv_usec))) {
+ clock_seq = (clock_seq+1) & 0x3FFF;
+ adjustment = 0;
+ last = tv;
+ } else if ((tv.tv_sec == last.tv_sec) &&
+ (tv.tv_usec == last.tv_usec)) {
+ if (adjustment >= MAX_ADJUSTMENT)
+ goto try_again;
+ adjustment++;
+ } else {
+ adjustment = 0;
+ last = tv;
+ }
+
+ clock_reg = tv.tv_usec*10 + adjustment;
+ clock_reg += ((unsigned long long) tv.tv_sec)*10000000;
+ clock_reg += (((unsigned long long) 0x01B21DD2) << 32) + 0x13814000;
+
+ *clock_high = clock_reg >> 32;
+ *clock_low = clock_reg;
+ *ret_clock_seq = clock_seq;
+ return 0;
+}
+
+void uuid_generate_time(uuid_t out)
+{
+ static unsigned char node_id[6];
+ static int has_init = 0;
+ struct uuid uu;
+ uint32_t clock_mid;
+
+ if (!has_init) {
+ if (get_node_id(node_id) <= 0) {
+ get_random_bytes(node_id, 6);
+ /*
+ * Set multicast bit, to prevent conflicts
+ * with IEEE 802 addresses obtained from
+ * network cards
+ */
+ node_id[0] |= 0x01;
+ }
+ has_init = 1;
+ }
+ get_clock(&clock_mid, &uu.time_low, &uu.clock_seq);
+ uu.clock_seq |= 0x8000;
+ uu.time_mid = (uint16_t) clock_mid;
+ uu.time_hi_and_version = ((clock_mid >> 16) & 0x0FFF) | 0x1000;
+ memcpy(uu.node, node_id, 6);
+ uuid_pack(&uu, out);
+}
+
+void uuid_generate_random(uuid_t out)
+{
+ uuid_t buf;
+ struct uuid uu;
+
+ get_random_bytes(buf, sizeof(buf));
+ uuid_unpack(buf, &uu);
+
+ uu.clock_seq = (uu.clock_seq & 0x3FFF) | 0x8000;
+ uu.time_hi_and_version = (uu.time_hi_and_version & 0x0FFF) | 0x4000;
+ uuid_pack(&uu, out);
+}
+
+/*
+ * This is the generic front-end to uuid_generate_random and
+ * uuid_generate_time. It uses uuid_generate_random only if
+ * /dev/urandom is available, since otherwise we won't have
+ * high-quality randomness.
+ */
+void uuid_generate(uuid_t out)
+{
+ if (get_random_fd() >= 0)
+ uuid_generate_random(out);
+ else
+ uuid_generate_time(out);
+}
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/uuid/pack.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/uuid/pack.c
new file mode 100644
index 0000000..217cfce
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/uuid/pack.c
@@ -0,0 +1,69 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Internal routine for packing UUID's
+ *
+ * Copyright (C) 1996, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * %End-Header%
+ */
+
+#include <string.h>
+#include "uuidP.h"
+
+void uuid_pack(const struct uuid *uu, uuid_t ptr)
+{
+ uint32_t tmp;
+ unsigned char *out = ptr;
+
+ tmp = uu->time_low;
+ out[3] = (unsigned char) tmp;
+ tmp >>= 8;
+ out[2] = (unsigned char) tmp;
+ tmp >>= 8;
+ out[1] = (unsigned char) tmp;
+ tmp >>= 8;
+ out[0] = (unsigned char) tmp;
+
+ tmp = uu->time_mid;
+ out[5] = (unsigned char) tmp;
+ tmp >>= 8;
+ out[4] = (unsigned char) tmp;
+
+ tmp = uu->time_hi_and_version;
+ out[7] = (unsigned char) tmp;
+ tmp >>= 8;
+ out[6] = (unsigned char) tmp;
+
+ tmp = uu->clock_seq;
+ out[9] = (unsigned char) tmp;
+ tmp >>= 8;
+ out[8] = (unsigned char) tmp;
+
+ memcpy(out+10, uu->node, 6);
+}
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/uuid/parse.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/uuid/parse.c
new file mode 100644
index 0000000..9a3f9cb
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/uuid/parse.c
@@ -0,0 +1,80 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * parse.c --- UUID parsing
+ *
+ * Copyright (C) 1996, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * %End-Header%
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+
+#include "uuidP.h"
+
+int uuid_parse(const char *in, uuid_t uu)
+{
+ struct uuid uuid;
+ int i;
+ const char *cp;
+ char buf[3];
+
+ if (strlen(in) != 36)
+ return -1;
+ for (i=0, cp = in; i <= 36; i++,cp++) {
+ if ((i == 8) || (i == 13) || (i == 18) ||
+ (i == 23)) {
+ if (*cp == '-')
+ continue;
+ else
+ return -1;
+ }
+ if (i== 36)
+ if (*cp == 0)
+ continue;
+ if (!isxdigit(*cp))
+ return -1;
+ }
+ uuid.time_low = strtoul(in, NULL, 16);
+ uuid.time_mid = strtoul(in+9, NULL, 16);
+ uuid.time_hi_and_version = strtoul(in+14, NULL, 16);
+ uuid.clock_seq = strtoul(in+19, NULL, 16);
+ cp = in+24;
+ buf[2] = 0;
+ for (i=0; i < 6; i++) {
+ buf[0] = *cp++;
+ buf[1] = *cp++;
+ uuid.node[i] = strtoul(buf, NULL, 16);
+ }
+
+ uuid_pack(&uuid, uu);
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/uuid/unpack.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/uuid/unpack.c
new file mode 100644
index 0000000..95d3aab
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/uuid/unpack.c
@@ -0,0 +1,63 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Internal routine for unpacking UUID
+ *
+ * Copyright (C) 1996, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * %End-Header%
+ */
+
+#include <string.h>
+#include "uuidP.h"
+
+void uuid_unpack(const uuid_t in, struct uuid *uu)
+{
+ const uint8_t *ptr = in;
+ uint32_t tmp;
+
+ tmp = *ptr++;
+ tmp = (tmp << 8) | *ptr++;
+ tmp = (tmp << 8) | *ptr++;
+ tmp = (tmp << 8) | *ptr++;
+ uu->time_low = tmp;
+
+ tmp = *ptr++;
+ tmp = (tmp << 8) | *ptr++;
+ uu->time_mid = tmp;
+
+ tmp = *ptr++;
+ tmp = (tmp << 8) | *ptr++;
+ uu->time_hi_and_version = tmp;
+
+ tmp = *ptr++;
+ tmp = (tmp << 8) | *ptr++;
+ uu->clock_seq = tmp;
+
+ memcpy(uu->node, ptr, 6);
+}
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/uuid/unparse.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/uuid/unparse.c
new file mode 100644
index 0000000..d2948fe
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/uuid/unparse.c
@@ -0,0 +1,77 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * unparse.c -- convert a UUID to string
+ *
+ * Copyright (C) 1996, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+
+#include "uuidP.h"
+
+static const char *fmt_lower =
+ "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x";
+
+static const char *fmt_upper =
+ "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X";
+
+#ifdef UUID_UNPARSE_DEFAULT_UPPER
+#define FMT_DEFAULT fmt_upper
+#else
+#define FMT_DEFAULT fmt_lower
+#endif
+
+static void uuid_unparse_x(const uuid_t uu, char *out, const char *fmt)
+{
+ struct uuid uuid;
+
+ uuid_unpack(uu, &uuid);
+ sprintf(out, fmt,
+ uuid.time_low, uuid.time_mid, uuid.time_hi_and_version,
+ uuid.clock_seq >> 8, uuid.clock_seq & 0xFF,
+ uuid.node[0], uuid.node[1], uuid.node[2],
+ uuid.node[3], uuid.node[4], uuid.node[5]);
+}
+
+void uuid_unparse_lower(const uuid_t uu, char *out)
+{
+ uuid_unparse_x(uu, out, fmt_lower);
+}
+
+void uuid_unparse_upper(const uuid_t uu, char *out)
+{
+ uuid_unparse_x(uu, out, fmt_upper);
+}
+
+void uuid_unparse(const uuid_t uu, char *out)
+{
+ uuid_unparse_x(uu, out, FMT_DEFAULT);
+}
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/uuid/uuid.h b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/uuid/uuid.h
new file mode 100644
index 0000000..bd53b15
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/uuid/uuid.h
@@ -0,0 +1,104 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Public include file for the UUID library
+ *
+ * Copyright (C) 1996, 1997, 1998 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * %End-Header%
+ */
+
+#ifndef _UUID_UUID_H
+#define _UUID_UUID_H
+
+#include <sys/types.h>
+#include <time.h>
+
+typedef unsigned char uuid_t[16];
+
+/* UUID Variant definitions */
+#define UUID_VARIANT_NCS 0
+#define UUID_VARIANT_DCE 1
+#define UUID_VARIANT_MICROSOFT 2
+#define UUID_VARIANT_OTHER 3
+
+/* UUID Type definitions */
+#define UUID_TYPE_DCE_TIME 1
+#define UUID_TYPE_DCE_RANDOM 4
+
+/* Allow UUID constants to be defined */
+#ifdef __GNUC__
+#define UUID_DEFINE(name,u0,u1,u2,u3,u4,u5,u6,u7,u8,u9,u10,u11,u12,u13,u14,u15) \
+ static const uuid_t name ATTRIBUTE_UNUSED = {u0,u1,u2,u3,u4,u5,u6,u7,u8,u9,u10,u11,u12,u13,u14,u15}
+#else
+#define UUID_DEFINE(name,u0,u1,u2,u3,u4,u5,u6,u7,u8,u9,u10,u11,u12,u13,u14,u15) \
+ static const uuid_t name = {u0,u1,u2,u3,u4,u5,u6,u7,u8,u9,u10,u11,u12,u13,u14,u15}
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* clear.c */
+/*void uuid_clear(uuid_t uu);*/
+#define uuid_clear(uu) memset(uu, 0, sizeof(uu))
+
+/* compare.c */
+int uuid_compare(const uuid_t uu1, const uuid_t uu2);
+
+/* copy.c */
+/*void uuid_copy(uuid_t dst, const uuid_t src);*/
+#define uuid_copy(dst,src) memcpy(dst, src, sizeof(dst))
+
+/* gen_uuid.c */
+void uuid_generate(uuid_t out);
+void uuid_generate_random(uuid_t out);
+void uuid_generate_time(uuid_t out);
+
+/* isnull.c */
+/*int uuid_is_null(const uuid_t uu);*/
+#define uuid_is_null(uu) (!memcmp(uu, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", sizeof(uu)))
+
+/* parse.c */
+int uuid_parse(const char *in, uuid_t uu);
+
+/* unparse.c */
+void uuid_unparse(const uuid_t uu, char *out);
+void uuid_unparse_lower(const uuid_t uu, char *out);
+void uuid_unparse_upper(const uuid_t uu, char *out);
+
+/* uuid_time.c */
+time_t uuid_time(const uuid_t uu, struct timeval *ret_tv);
+int uuid_type(const uuid_t uu);
+int uuid_variant(const uuid_t uu);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _UUID_UUID_H */
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/uuid/uuidP.h b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/uuid/uuidP.h
new file mode 100644
index 0000000..87041ef
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/uuid/uuidP.h
@@ -0,0 +1,60 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * uuid.h -- private header file for uuids
+ *
+ * Copyright (C) 1996, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * %End-Header%
+ */
+
+#include <inttypes.h>
+#include <sys/types.h>
+
+#include "uuid.h"
+
+/*
+ * Offset between 15-Oct-1582 and 1-Jan-70
+ */
+#define TIME_OFFSET_HIGH 0x01B21DD2
+#define TIME_OFFSET_LOW 0x13814000
+
+struct uuid {
+ uint32_t time_low;
+ uint16_t time_mid;
+ uint16_t time_hi_and_version;
+ uint16_t clock_seq;
+ uint8_t node[6];
+};
+
+
+/*
+ * prototypes
+ */
+void uuid_pack(const struct uuid *uu, uuid_t ptr);
+void uuid_unpack(const uuid_t in, struct uuid *uu);
diff --git a/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/uuid/uuid_time.c b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/uuid/uuid_time.c
new file mode 100644
index 0000000..b54d673
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/e2fsprogs/old_e2fsprogs/uuid/uuid_time.c
@@ -0,0 +1,161 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * uuid_time.c --- Interpret the time field from a uuid. This program
+ * violates the UUID abstraction barrier by reaching into the guts
+ * of a UUID and interpreting it.
+ *
+ * Copyright (C) 1998, 1999 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <time.h>
+
+#include "uuidP.h"
+
+time_t uuid_time(const uuid_t uu, struct timeval *ret_tv)
+{
+ struct uuid uuid;
+ uint32_t high;
+ struct timeval tv;
+ unsigned long long clock_reg;
+
+ uuid_unpack(uu, &uuid);
+
+ high = uuid.time_mid | ((uuid.time_hi_and_version & 0xFFF) << 16);
+ clock_reg = uuid.time_low | ((unsigned long long) high << 32);
+
+ clock_reg -= (((unsigned long long) 0x01B21DD2) << 32) + 0x13814000;
+ tv.tv_sec = clock_reg / 10000000;
+ tv.tv_usec = (clock_reg % 10000000) / 10;
+
+ if (ret_tv)
+ *ret_tv = tv;
+
+ return tv.tv_sec;
+}
+
+int uuid_type(const uuid_t uu)
+{
+ struct uuid uuid;
+
+ uuid_unpack(uu, &uuid);
+ return ((uuid.time_hi_and_version >> 12) & 0xF);
+}
+
+int uuid_variant(const uuid_t uu)
+{
+ struct uuid uuid;
+ int var;
+
+ uuid_unpack(uu, &uuid);
+ var = uuid.clock_seq;
+
+ if ((var & 0x8000) == 0)
+ return UUID_VARIANT_NCS;
+ if ((var & 0x4000) == 0)
+ return UUID_VARIANT_DCE;
+ if ((var & 0x2000) == 0)
+ return UUID_VARIANT_MICROSOFT;
+ return UUID_VARIANT_OTHER;
+}
+
+#ifdef DEBUG
+static const char *variant_string(int variant)
+{
+ switch (variant) {
+ case UUID_VARIANT_NCS:
+ return "NCS";
+ case UUID_VARIANT_DCE:
+ return "DCE";
+ case UUID_VARIANT_MICROSOFT:
+ return "Microsoft";
+ default:
+ return "Other";
+ }
+}
+
+
+int
+main(int argc, char **argv)
+{
+ uuid_t buf;
+ time_t time_reg;
+ struct timeval tv;
+ int type, variant;
+
+ if (argc != 2) {
+ fprintf(stderr, "Usage: %s uuid\n", argv[0]);
+ exit(1);
+ }
+ if (uuid_parse(argv[1], buf)) {
+ fprintf(stderr, "Invalid UUID: %s\n", argv[1]);
+ exit(1);
+ }
+ variant = uuid_variant(buf);
+ type = uuid_type(buf);
+ time_reg = uuid_time(buf, &tv);
+
+ printf("UUID variant is %d (%s)\n", variant, variant_string(variant));
+ if (variant != UUID_VARIANT_DCE) {
+ printf("Warning: This program only knows how to interpret "
+ "DCE UUIDs.\n\tThe rest of the output is likely "
+ "to be incorrect!!\n");
+ }
+ printf("UUID type is %d", type);
+ switch (type) {
+ case 1:
+ printf(" (time based)\n");
+ break;
+ case 2:
+ printf(" (DCE)\n");
+ break;
+ case 3:
+ printf(" (name-based)\n");
+ break;
+ case 4:
+ printf(" (random)\n");
+ break;
+ default:
+ puts("");
+ }
+ if (type != 1) {
+ printf("Warning: not a time-based UUID, so UUID time "
+ "decoding will likely not work!\n");
+ }
+ printf("UUID time is: (%ld, %ld): %s\n", tv.tv_sec, tv.tv_usec,
+ ctime(&time_reg));
+
+ return 0;
+}
+#endif
diff --git a/i/pc104/initrd/conf/busybox/editors/Config.in b/i/pc104/initrd/conf/busybox/editors/Config.in
new file mode 100644
index 0000000..fd840ae
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/editors/Config.in
@@ -0,0 +1,138 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+menu "Editors"
+
+config AWK
+ bool "awk"
+ default n
+ help
+ Awk is used as a pattern scanning and processing language. This is
+ the BusyBox implementation of that programming language.
+
+config FEATURE_AWK_MATH
+ bool "Enable math functions (requires libm)"
+ default y
+ depends on AWK
+ help
+ Enable math functions of the Awk programming language.
+ NOTE: This will require libm to be present for linking.
+
+config ED
+ bool "ed"
+ default n
+ help
+ The original 1970's Unix text editor, from the days of teletypes.
+ Small, simple, evil. Part of SUSv3. If you're not already using
+ this, you don't need it.
+
+config PATCH
+ bool "patch"
+ default n
+ help
+ Apply a unified diff formatted patch.
+
+config SED
+ bool "sed"
+ default n
+ help
+ sed is used to perform text transformations on a file
+ or input from a pipeline.
+
+config VI
+ bool "vi"
+ default n
+ help
+ 'vi' is a text editor. More specifically, it is the One True
+ text editor <grin>. It does, however, have a rather steep
+ learning curve. If you are not already comfortable with 'vi'
+ you may wish to use something else.
+
+config FEATURE_VI_COLON
+ bool "Enable \":\" colon commands (no \"ex\" mode)"
+ default y
+ depends on VI
+ help
+ Enable a limited set of colon commands for vi. This does not
+ provide an "ex" mode.
+
+config FEATURE_VI_YANKMARK
+ bool "Enable yank/put commands and mark cmds"
+ default y
+ depends on VI
+ help
+ This will enable you to use yank and put, as well as mark in
+ busybox vi.
+
+config FEATURE_VI_SEARCH
+ bool "Enable search and replace cmds"
+ default y
+ depends on VI
+ help
+ Select this if you wish to be able to do search and replace in
+ busybox vi.
+
+config FEATURE_VI_USE_SIGNALS
+ bool "Catch signals"
+ default y
+ depends on VI
+ help
+ Selecting this option will make busybox vi signal aware. This will
+ make busybox vi support SIGWINCH to deal with Window Changes, catch
+ Ctrl-Z and Ctrl-C and alarms.
+
+config FEATURE_VI_DOT_CMD
+ bool "Remember previous cmd and \".\" cmd"
+ default y
+ depends on VI
+ help
+ Make busybox vi remember the last command and be able to repeat it.
+
+config FEATURE_VI_READONLY
+ bool "Enable -R option and \"view\" mode"
+ default y
+ depends on VI
+ help
+ Enable the read-only command line option, which allows the user to
+ open a file in read-only mode.
+
+config FEATURE_VI_SETOPTS
+ bool "Enable set-able options, ai ic showmatch"
+ default y
+ depends on VI
+ help
+ Enable the editor to set some (ai, ic, showmatch) options.
+
+config FEATURE_VI_SET
+ bool "Support for :set"
+ default y
+ depends on VI
+ help
+ Support for ":set".
+
+config FEATURE_VI_WIN_RESIZE
+ bool "Handle window resize"
+ default y
+ depends on VI
+ help
+ Make busybox vi behave nicely with terminals that get resized.
+
+config FEATURE_VI_OPTIMIZE_CURSOR
+ bool "Optimize cursor movement"
+ default y
+ depends on VI
+ help
+ This will make the cursor movement faster, but requires more memory
+ and it makes the applet a tiny bit larger.
+
+config FEATURE_ALLOW_EXEC
+ bool "Allow vi and awk to execute shell commands"
+ default y
+ depends on VI || AWK
+ help
+ Enables vi and awk features which allows user to execute
+ shell commands (using system() C call).
+
+endmenu
diff --git a/i/pc104/initrd/conf/busybox/editors/Kbuild b/i/pc104/initrd/conf/busybox/editors/Kbuild
new file mode 100644
index 0000000..d991e1f
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/editors/Kbuild
@@ -0,0 +1,12 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under the GPL v2, see the file LICENSE in this tarball.
+
+lib-y:=
+lib-$(CONFIG_AWK) += awk.o
+lib-$(CONFIG_ED) += ed.o
+lib-$(CONFIG_PATCH) += patch.o
+lib-$(CONFIG_SED) += sed.o
+lib-$(CONFIG_VI) += vi.o
diff --git a/i/pc104/initrd/conf/busybox/editors/awk.c b/i/pc104/initrd/conf/busybox/editors/awk.c
new file mode 100644
index 0000000..a18025e
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/editors/awk.c
@@ -0,0 +1,2781 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * awk implementation for busybox
+ *
+ * Copyright (C) 2002 by Dmitry Zakharov <dmit@crp.bank.gov.ua>
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ */
+
+#include "busybox.h"
+#include "xregex.h"
+#include <math.h>
+
+
+#define MAXVARFMT 240
+#define MINNVBLOCK 64
+
+/* variable flags */
+#define VF_NUMBER 0x0001 /* 1 = primary type is number */
+#define VF_ARRAY 0x0002 /* 1 = it's an array */
+
+#define VF_CACHED 0x0100 /* 1 = num/str value has cached str/num eq */
+#define VF_USER 0x0200 /* 1 = user input (may be numeric string) */
+#define VF_SPECIAL 0x0400 /* 1 = requires extra handling when changed */
+#define VF_WALK 0x0800 /* 1 = variable has alloc'd x.walker list */
+#define VF_FSTR 0x1000 /* 1 = var::string points to fstring buffer */
+#define VF_CHILD 0x2000 /* 1 = function arg; x.parent points to source */
+#define VF_DIRTY 0x4000 /* 1 = variable was set explicitly */
+
+/* these flags are static, don't change them when value is changed */
+#define VF_DONTTOUCH (VF_ARRAY | VF_SPECIAL | VF_WALK | VF_CHILD | VF_DIRTY)
+
+/* Variable */
+typedef struct var_s {
+ unsigned short type; /* flags */
+ double number;
+ char *string;
+ union {
+ int aidx; /* func arg idx (for compilation stage) */
+ struct xhash_s *array; /* array ptr */
+ struct var_s *parent; /* for func args, ptr to actual parameter */
+ char **walker; /* list of array elements (for..in) */
+ } x;
+} var;
+
+/* Node chain (pattern-action chain, BEGIN, END, function bodies) */
+typedef struct chain_s {
+ struct node_s *first;
+ struct node_s *last;
+ const char *programname;
+} chain;
+
+/* Function */
+typedef struct func_s {
+ unsigned short nargs;
+ struct chain_s body;
+} func;
+
+/* I/O stream */
+typedef struct rstream_s {
+ FILE *F;
+ char *buffer;
+ int adv;
+ int size;
+ int pos;
+ unsigned short is_pipe;
+} rstream;
+
+typedef struct hash_item_s {
+ union {
+ struct var_s v; /* variable/array hash */
+ struct rstream_s rs; /* redirect streams hash */
+ struct func_s f; /* functions hash */
+ } data;
+ struct hash_item_s *next; /* next in chain */
+ char name[1]; /* really it's longer */
+} hash_item;
+
+typedef struct xhash_s {
+ unsigned nel; /* num of elements */
+ unsigned csize; /* current hash size */
+ unsigned nprime; /* next hash size in PRIMES[] */
+ unsigned glen; /* summary length of item names */
+ struct hash_item_s **items;
+} xhash;
+
+/* Tree node */
+typedef struct node_s {
+ uint32_t info;
+ unsigned short lineno;
+ union {
+ struct node_s *n;
+ var *v;
+ int i;
+ char *s;
+ regex_t *re;
+ } l;
+ union {
+ struct node_s *n;
+ regex_t *ire;
+ func *f;
+ int argno;
+ } r;
+ union {
+ struct node_s *n;
+ } a;
+} node;
+
+/* Block of temporary variables */
+typedef struct nvblock_s {
+ int size;
+ var *pos;
+ struct nvblock_s *prev;
+ struct nvblock_s *next;
+ var nv[0];
+} nvblock;
+
+typedef struct tsplitter_s {
+ node n;
+ regex_t re[2];
+} tsplitter;
+
+/* simple token classes */
+/* Order and hex values are very important!!! See next_token() */
+#define TC_SEQSTART 1 /* ( */
+#define TC_SEQTERM (1 << 1) /* ) */
+#define TC_REGEXP (1 << 2) /* /.../ */
+#define TC_OUTRDR (1 << 3) /* | > >> */
+#define TC_UOPPOST (1 << 4) /* unary postfix operator */
+#define TC_UOPPRE1 (1 << 5) /* unary prefix operator */
+#define TC_BINOPX (1 << 6) /* two-opnd operator */
+#define TC_IN (1 << 7)
+#define TC_COMMA (1 << 8)
+#define TC_PIPE (1 << 9) /* input redirection pipe */
+#define TC_UOPPRE2 (1 << 10) /* unary prefix operator */
+#define TC_ARRTERM (1 << 11) /* ] */
+#define TC_GRPSTART (1 << 12) /* { */
+#define TC_GRPTERM (1 << 13) /* } */
+#define TC_SEMICOL (1 << 14)
+#define TC_NEWLINE (1 << 15)
+#define TC_STATX (1 << 16) /* ctl statement (for, next...) */
+#define TC_WHILE (1 << 17)
+#define TC_ELSE (1 << 18)
+#define TC_BUILTIN (1 << 19)
+#define TC_GETLINE (1 << 20)
+#define TC_FUNCDECL (1 << 21) /* `function' `func' */
+#define TC_BEGIN (1 << 22)
+#define TC_END (1 << 23)
+#define TC_EOF (1 << 24)
+#define TC_VARIABLE (1 << 25)
+#define TC_ARRAY (1 << 26)
+#define TC_FUNCTION (1 << 27)
+#define TC_STRING (1 << 28)
+#define TC_NUMBER (1 << 29)
+
+#define TC_UOPPRE (TC_UOPPRE1 | TC_UOPPRE2)
+
+/* combined token classes */
+#define TC_BINOP (TC_BINOPX | TC_COMMA | TC_PIPE | TC_IN)
+#define TC_UNARYOP (TC_UOPPRE | TC_UOPPOST)
+#define TC_OPERAND (TC_VARIABLE | TC_ARRAY | TC_FUNCTION | \
+ TC_BUILTIN | TC_GETLINE | TC_SEQSTART | TC_STRING | TC_NUMBER)
+
+#define TC_STATEMNT (TC_STATX | TC_WHILE)
+#define TC_OPTERM (TC_SEMICOL | TC_NEWLINE)
+
+/* word tokens, cannot mean something else if not expected */
+#define TC_WORD (TC_IN | TC_STATEMNT | TC_ELSE | TC_BUILTIN | \
+ TC_GETLINE | TC_FUNCDECL | TC_BEGIN | TC_END)
+
+/* discard newlines after these */
+#define TC_NOTERM (TC_COMMA | TC_GRPSTART | TC_GRPTERM | \
+ TC_BINOP | TC_OPTERM)
+
+/* what can expression begin with */
+#define TC_OPSEQ (TC_OPERAND | TC_UOPPRE | TC_REGEXP)
+/* what can group begin with */
+#define TC_GRPSEQ (TC_OPSEQ | TC_OPTERM | TC_STATEMNT | TC_GRPSTART)
+
+/* if previous token class is CONCAT1 and next is CONCAT2, concatenation */
+/* operator is inserted between them */
+#define TC_CONCAT1 (TC_VARIABLE | TC_ARRTERM | TC_SEQTERM | \
+ TC_STRING | TC_NUMBER | TC_UOPPOST)
+#define TC_CONCAT2 (TC_OPERAND | TC_UOPPRE)
+
+#define OF_RES1 0x010000
+#define OF_RES2 0x020000
+#define OF_STR1 0x040000
+#define OF_STR2 0x080000
+#define OF_NUM1 0x100000
+#define OF_CHECKED 0x200000
+
+/* combined operator flags */
+#define xx 0
+#define xV OF_RES2
+#define xS (OF_RES2 | OF_STR2)
+#define Vx OF_RES1
+#define VV (OF_RES1 | OF_RES2)
+#define Nx (OF_RES1 | OF_NUM1)
+#define NV (OF_RES1 | OF_NUM1 | OF_RES2)
+#define Sx (OF_RES1 | OF_STR1)
+#define SV (OF_RES1 | OF_STR1 | OF_RES2)
+#define SS (OF_RES1 | OF_STR1 | OF_RES2 | OF_STR2)
+
+#define OPCLSMASK 0xFF00
+#define OPNMASK 0x007F
+
+/* operator priority is a highest byte (even: r->l, odd: l->r grouping)
+ * For builtins it has different meaning: n n s3 s2 s1 v3 v2 v1,
+ * n - min. number of args, vN - resolve Nth arg to var, sN - resolve to string
+ */
+#define P(x) (x << 24)
+#define PRIMASK 0x7F000000
+#define PRIMASK2 0x7E000000
+
+/* Operation classes */
+
+#define SHIFT_TIL_THIS 0x0600
+#define RECUR_FROM_THIS 0x1000
+
+enum {
+ OC_DELETE=0x0100, OC_EXEC=0x0200, OC_NEWSOURCE=0x0300,
+ OC_PRINT=0x0400, OC_PRINTF=0x0500, OC_WALKINIT=0x0600,
+
+ OC_BR=0x0700, OC_BREAK=0x0800, OC_CONTINUE=0x0900,
+ OC_EXIT=0x0a00, OC_NEXT=0x0b00, OC_NEXTFILE=0x0c00,
+ OC_TEST=0x0d00, OC_WALKNEXT=0x0e00,
+
+ OC_BINARY=0x1000, OC_BUILTIN=0x1100, OC_COLON=0x1200,
+ OC_COMMA=0x1300, OC_COMPARE=0x1400, OC_CONCAT=0x1500,
+ OC_FBLTIN=0x1600, OC_FIELD=0x1700, OC_FNARG=0x1800,
+ OC_FUNC=0x1900, OC_GETLINE=0x1a00, OC_IN=0x1b00,
+ OC_LAND=0x1c00, OC_LOR=0x1d00, OC_MATCH=0x1e00,
+ OC_MOVE=0x1f00, OC_PGETLINE=0x2000, OC_REGEXP=0x2100,
+ OC_REPLACE=0x2200, OC_RETURN=0x2300, OC_SPRINTF=0x2400,
+ OC_TERNARY=0x2500, OC_UNARY=0x2600, OC_VAR=0x2700,
+ OC_DONE=0x2800,
+
+ ST_IF=0x3000, ST_DO=0x3100, ST_FOR=0x3200,
+ ST_WHILE=0x3300
+};
+
+/* simple builtins */
+enum {
+ F_in=0, F_rn, F_co, F_ex, F_lg, F_si, F_sq, F_sr,
+ F_ti, F_le, F_sy, F_ff, F_cl
+};
+
+/* builtins */
+enum {
+ B_a2=0, B_ix, B_ma, B_sp, B_ss, B_ti, B_lo, B_up,
+ B_ge, B_gs, B_su,
+ B_an, B_co, B_ls, B_or, B_rs, B_xo,
+};
+
+/* tokens and their corresponding info values */
+
+#define NTC "\377" /* switch to next token class (tc<<1) */
+#define NTCC '\377'
+
+#define OC_B OC_BUILTIN
+
+static const char tokenlist[] =
+ "\1(" NTC
+ "\1)" NTC
+ "\1/" NTC /* REGEXP */
+ "\2>>" "\1>" "\1|" NTC /* OUTRDR */
+ "\2++" "\2--" NTC /* UOPPOST */
+ "\2++" "\2--" "\1$" NTC /* UOPPRE1 */
+ "\2==" "\1=" "\2+=" "\2-=" /* BINOPX */
+ "\2*=" "\2/=" "\2%=" "\2^="
+ "\1+" "\1-" "\3**=" "\2**"
+ "\1/" "\1%" "\1^" "\1*"
+ "\2!=" "\2>=" "\2<=" "\1>"
+ "\1<" "\2!~" "\1~" "\2&&"
+ "\2||" "\1?" "\1:" NTC
+ "\2in" NTC
+ "\1," NTC
+ "\1|" NTC
+ "\1+" "\1-" "\1!" NTC /* UOPPRE2 */
+ "\1]" NTC
+ "\1{" NTC
+ "\1}" NTC
+ "\1;" NTC
+ "\1\n" NTC
+ "\2if" "\2do" "\3for" "\5break" /* STATX */
+ "\10continue" "\6delete" "\5print"
+ "\6printf" "\4next" "\10nextfile"
+ "\6return" "\4exit" NTC
+ "\5while" NTC
+ "\4else" NTC
+
+ "\3and" "\5compl" "\6lshift" "\2or"
+ "\6rshift" "\3xor"
+ "\5close" "\6system" "\6fflush" "\5atan2" /* BUILTIN */
+ "\3cos" "\3exp" "\3int" "\3log"
+ "\4rand" "\3sin" "\4sqrt" "\5srand"
+ "\6gensub" "\4gsub" "\5index" "\6length"
+ "\5match" "\5split" "\7sprintf" "\3sub"
+ "\6substr" "\7systime" "\10strftime"
+ "\7tolower" "\7toupper" NTC
+ "\7getline" NTC
+ "\4func" "\10function" NTC
+ "\5BEGIN" NTC
+ "\3END" "\0"
+ ;
+
+static const uint32_t tokeninfo[] = {
+ 0,
+ 0,
+ OC_REGEXP,
+ xS|'a', xS|'w', xS|'|',
+ OC_UNARY|xV|P(9)|'p', OC_UNARY|xV|P(9)|'m',
+ OC_UNARY|xV|P(9)|'P', OC_UNARY|xV|P(9)|'M',
+ OC_FIELD|xV|P(5),
+ OC_COMPARE|VV|P(39)|5, OC_MOVE|VV|P(74),
+ OC_REPLACE|NV|P(74)|'+', OC_REPLACE|NV|P(74)|'-',
+ OC_REPLACE|NV|P(74)|'*', OC_REPLACE|NV|P(74)|'/',
+ OC_REPLACE|NV|P(74)|'%', OC_REPLACE|NV|P(74)|'&',
+ OC_BINARY|NV|P(29)|'+', OC_BINARY|NV|P(29)|'-',
+ OC_REPLACE|NV|P(74)|'&', OC_BINARY|NV|P(15)|'&',
+ OC_BINARY|NV|P(25)|'/', OC_BINARY|NV|P(25)|'%',
+ OC_BINARY|NV|P(15)|'&', OC_BINARY|NV|P(25)|'*',
+ OC_COMPARE|VV|P(39)|4, OC_COMPARE|VV|P(39)|3,
+ OC_COMPARE|VV|P(39)|0, OC_COMPARE|VV|P(39)|1,
+ OC_COMPARE|VV|P(39)|2, OC_MATCH|Sx|P(45)|'!',
+ OC_MATCH|Sx|P(45)|'~', OC_LAND|Vx|P(55),
+ OC_LOR|Vx|P(59), OC_TERNARY|Vx|P(64)|'?',
+ OC_COLON|xx|P(67)|':',
+ OC_IN|SV|P(49),
+ OC_COMMA|SS|P(80),
+ OC_PGETLINE|SV|P(37),
+ OC_UNARY|xV|P(19)|'+', OC_UNARY|xV|P(19)|'-',
+ OC_UNARY|xV|P(19)|'!',
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ ST_IF, ST_DO, ST_FOR, OC_BREAK,
+ OC_CONTINUE, OC_DELETE|Vx, OC_PRINT,
+ OC_PRINTF, OC_NEXT, OC_NEXTFILE,
+ OC_RETURN|Vx, OC_EXIT|Nx,
+ ST_WHILE,
+ 0,
+
+ OC_B|B_an|P(0x83), OC_B|B_co|P(0x41), OC_B|B_ls|P(0x83), OC_B|B_or|P(0x83),
+ OC_B|B_rs|P(0x83), OC_B|B_xo|P(0x83),
+ OC_FBLTIN|Sx|F_cl, OC_FBLTIN|Sx|F_sy, OC_FBLTIN|Sx|F_ff, OC_B|B_a2|P(0x83),
+ OC_FBLTIN|Nx|F_co, OC_FBLTIN|Nx|F_ex, OC_FBLTIN|Nx|F_in, OC_FBLTIN|Nx|F_lg,
+ OC_FBLTIN|F_rn, OC_FBLTIN|Nx|F_si, OC_FBLTIN|Nx|F_sq, OC_FBLTIN|Nx|F_sr,
+ OC_B|B_ge|P(0xd6), OC_B|B_gs|P(0xb6), OC_B|B_ix|P(0x9b), OC_FBLTIN|Sx|F_le,
+ OC_B|B_ma|P(0x89), OC_B|B_sp|P(0x8b), OC_SPRINTF, OC_B|B_su|P(0xb6),
+ OC_B|B_ss|P(0x8f), OC_FBLTIN|F_ti, OC_B|B_ti|P(0x0b),
+ OC_B|B_lo|P(0x49), OC_B|B_up|P(0x49),
+ OC_GETLINE|SV|P(0),
+ 0, 0,
+ 0,
+ 0
+};
+
+/* internal variable names and their initial values */
+/* asterisk marks SPECIAL vars; $ is just no-named Field0 */
+enum {
+ CONVFMT=0, OFMT, FS, OFS,
+ ORS, RS, RT, FILENAME,
+ SUBSEP, ARGIND, ARGC, ARGV,
+ ERRNO, FNR,
+ NR, NF, IGNORECASE,
+ ENVIRON, F0, _intvarcount_
+};
+
+static const char vNames[] =
+ "CONVFMT\0" "OFMT\0" "FS\0*" "OFS\0"
+ "ORS\0" "RS\0*" "RT\0" "FILENAME\0"
+ "SUBSEP\0" "ARGIND\0" "ARGC\0" "ARGV\0"
+ "ERRNO\0" "FNR\0"
+ "NR\0" "NF\0*" "IGNORECASE\0*"
+ "ENVIRON\0" "$\0*" "\0";
+
+static const char vValues[] =
+ "%.6g\0" "%.6g\0" " \0" " \0"
+ "\n\0" "\n\0" "\0" "\0"
+ "\034\0"
+ "\377";
+
+/* hash size may grow to these values */
+#define FIRST_PRIME 61;
+static const unsigned PRIMES[] = { 251, 1021, 4093, 16381, 65521 };
+enum { NPRIMES = sizeof(PRIMES) / sizeof(unsigned) };
+
+/* globals */
+
+extern char **environ;
+
+static var * V[_intvarcount_];
+static chain beginseq, mainseq, endseq, *seq;
+static int nextrec, nextfile;
+static node *break_ptr, *continue_ptr;
+static rstream *iF;
+static xhash *vhash, *ahash, *fdhash, *fnhash;
+static const char *programname;
+static short lineno;
+static int is_f0_split;
+static int nfields;
+static var *Fields;
+static tsplitter fsplitter, rsplitter;
+static nvblock *cb;
+static char *pos;
+static char *buf;
+static int icase;
+static int exiting;
+
+static struct {
+ uint32_t tclass;
+ uint32_t info;
+ char *string;
+ double number;
+ short lineno;
+ int rollback;
+} t;
+
+/* function prototypes */
+static void handle_special(var *);
+static node *parse_expr(uint32_t);
+static void chain_group(void);
+static var *evaluate(node *, var *);
+static rstream *next_input_file(void);
+static int fmt_num(char *, int, const char *, double, int);
+static int awk_exit(int) ATTRIBUTE_NORETURN;
+
+/* ---- error handling ---- */
+
+static const char EMSG_INTERNAL_ERROR[] = "Internal error";
+static const char EMSG_UNEXP_EOS[] = "Unexpected end of string";
+static const char EMSG_UNEXP_TOKEN[] = "Unexpected token";
+static const char EMSG_DIV_BY_ZERO[] = "Division by zero";
+static const char EMSG_INV_FMT[] = "Invalid format specifier";
+static const char EMSG_TOO_FEW_ARGS[] = "Too few arguments for builtin";
+static const char EMSG_NOT_ARRAY[] = "Not an array";
+static const char EMSG_POSSIBLE_ERROR[] = "Possible syntax error";
+static const char EMSG_UNDEF_FUNC[] = "Call to undefined function";
+#if !ENABLE_FEATURE_AWK_MATH
+static const char EMSG_NO_MATH[] = "Math support is not compiled in";
+#endif
+
+static void zero_out_var(var * vp)
+{
+ memset(vp, 0, sizeof(*vp));
+}
+
+static void syntax_error(const char * const message) ATTRIBUTE_NORETURN;
+static void syntax_error(const char * const message)
+{
+ bb_error_msg_and_die("%s:%i: %s", programname, lineno, message);
+}
+
+#define runtime_error(x) syntax_error(x)
+
+
+/* ---- hash stuff ---- */
+
+static unsigned hashidx(const char *name)
+{
+ unsigned idx = 0;
+
+ while (*name) idx = *name++ + (idx << 6) - idx;
+ return idx;
+}
+
+/* create new hash */
+static xhash *hash_init(void)
+{
+ xhash *newhash;
+
+ newhash = xzalloc(sizeof(xhash));
+ newhash->csize = FIRST_PRIME;
+ newhash->items = xzalloc(newhash->csize * sizeof(hash_item *));
+
+ return newhash;
+}
+
+/* find item in hash, return ptr to data, NULL if not found */
+static void *hash_search(xhash *hash, const char *name)
+{
+ hash_item *hi;
+
+ hi = hash->items [ hashidx(name) % hash->csize ];
+ while (hi) {
+ if (strcmp(hi->name, name) == 0)
+ return &(hi->data);
+ hi = hi->next;
+ }
+ return NULL;
+}
+
+/* grow hash if it becomes too big */
+static void hash_rebuild(xhash *hash)
+{
+ unsigned newsize, i, idx;
+ hash_item **newitems, *hi, *thi;
+
+ if (hash->nprime == NPRIMES)
+ return;
+
+ newsize = PRIMES[hash->nprime++];
+ newitems = xzalloc(newsize * sizeof(hash_item *));
+
+ for (i=0; i<hash->csize; i++) {
+ hi = hash->items[i];
+ while (hi) {
+ thi = hi;
+ hi = thi->next;
+ idx = hashidx(thi->name) % newsize;
+ thi->next = newitems[idx];
+ newitems[idx] = thi;
+ }
+ }
+
+ free(hash->items);
+ hash->csize = newsize;
+ hash->items = newitems;
+}
+
+/* find item in hash, add it if necessary. Return ptr to data */
+static void *hash_find(xhash *hash, const char *name)
+{
+ hash_item *hi;
+ unsigned idx;
+ int l;
+
+ hi = hash_search(hash, name);
+ if (! hi) {
+ if (++hash->nel / hash->csize > 10)
+ hash_rebuild(hash);
+
+ l = strlen(name) + 1;
+ hi = xzalloc(sizeof(hash_item) + l);
+ memcpy(hi->name, name, l);
+
+ idx = hashidx(name) % hash->csize;
+ hi->next = hash->items[idx];
+ hash->items[idx] = hi;
+ hash->glen += l;
+ }
+ return &(hi->data);
+}
+
+#define findvar(hash, name) ((var*) hash_find((hash) , (name)))
+#define newvar(name) ((var*) hash_find(vhash , (name)))
+#define newfile(name) ((rstream*)hash_find(fdhash ,(name)))
+#define newfunc(name) ((func*) hash_find(fnhash , (name)))
+
+static void hash_remove(xhash *hash, const char *name)
+{
+ hash_item *hi, **phi;
+
+ phi = &(hash->items[ hashidx(name) % hash->csize ]);
+ while (*phi) {
+ hi = *phi;
+ if (strcmp(hi->name, name) == 0) {
+ hash->glen -= (strlen(name) + 1);
+ hash->nel--;
+ *phi = hi->next;
+ free(hi);
+ break;
+ }
+ phi = &(hi->next);
+ }
+}
+
+/* ------ some useful functions ------ */
+
+static void skip_spaces(char **s)
+{
+ char *p = *s;
+
+ while (*p == ' ' || *p == '\t' ||
+ (*p == '\\' && *(p+1) == '\n' && (++p, ++t.lineno))) {
+ p++;
+ }
+ *s = p;
+}
+
+static char *nextword(char **s)
+{
+ char *p = *s;
+
+ while (*(*s)++) /* */;
+
+ return p;
+}
+
+static char nextchar(char **s)
+{
+ char c, *pps;
+
+ c = *((*s)++);
+ pps = *s;
+ if (c == '\\') c = bb_process_escape_sequence((const char**)s);
+ if (c == '\\' && *s == pps) c = *((*s)++);
+ return c;
+}
+
+static int ATTRIBUTE_ALWAYS_INLINE isalnum_(int c)
+{
+ return (isalnum(c) || c == '_');
+}
+
+static FILE *afopen(const char *path, const char *mode)
+{
+ return (*path == '-' && *(path+1) == '\0') ? stdin : xfopen(path, mode);
+}
+
+/* -------- working with variables (set/get/copy/etc) -------- */
+
+static xhash *iamarray(var *v)
+{
+ var *a = v;
+
+ while (a->type & VF_CHILD)
+ a = a->x.parent;
+
+ if (! (a->type & VF_ARRAY)) {
+ a->type |= VF_ARRAY;
+ a->x.array = hash_init();
+ }
+ return a->x.array;
+}
+
+static void clear_array(xhash *array)
+{
+ unsigned i;
+ hash_item *hi, *thi;
+
+ for (i=0; i<array->csize; i++) {
+ hi = array->items[i];
+ while (hi) {
+ thi = hi;
+ hi = hi->next;
+ free(thi->data.v.string);
+ free(thi);
+ }
+ array->items[i] = NULL;
+ }
+ array->glen = array->nel = 0;
+}
+
+/* clear a variable */
+static var *clrvar(var *v)
+{
+ if (!(v->type & VF_FSTR))
+ free(v->string);
+
+ v->type &= VF_DONTTOUCH;
+ v->type |= VF_DIRTY;
+ v->string = NULL;
+ return v;
+}
+
+/* assign string value to variable */
+static var *setvar_p(var *v, char *value)
+{
+ clrvar(v);
+ v->string = value;
+ handle_special(v);
+
+ return v;
+}
+
+/* same as setvar_p but make a copy of string */
+static var *setvar_s(var *v, const char *value)
+{
+ return setvar_p(v, (value && *value) ? xstrdup(value) : NULL);
+}
+
+/* same as setvar_s but set USER flag */
+static var *setvar_u(var *v, const char *value)
+{
+ setvar_s(v, value);
+ v->type |= VF_USER;
+ return v;
+}
+
+/* set array element to user string */
+static void setari_u(var *a, int idx, const char *s)
+{
+ var *v;
+ static char sidx[12];
+
+ sprintf(sidx, "%d", idx);
+ v = findvar(iamarray(a), sidx);
+ setvar_u(v, s);
+}
+
+/* assign numeric value to variable */
+static var *setvar_i(var *v, double value)
+{
+ clrvar(v);
+ v->type |= VF_NUMBER;
+ v->number = value;
+ handle_special(v);
+ return v;
+}
+
+static const char *getvar_s(var *v)
+{
+ /* if v is numeric and has no cached string, convert it to string */
+ if ((v->type & (VF_NUMBER | VF_CACHED)) == VF_NUMBER) {
+ fmt_num(buf, MAXVARFMT, getvar_s(V[CONVFMT]), v->number, TRUE);
+ v->string = xstrdup(buf);
+ v->type |= VF_CACHED;
+ }
+ return (v->string == NULL) ? "" : v->string;
+}
+
+static double getvar_i(var *v)
+{
+ char *s;
+
+ if ((v->type & (VF_NUMBER | VF_CACHED)) == 0) {
+ v->number = 0;
+ s = v->string;
+ if (s && *s) {
+ v->number = strtod(s, &s);
+ if (v->type & VF_USER) {
+ skip_spaces(&s);
+ if (*s != '\0')
+ v->type &= ~VF_USER;
+ }
+ } else {
+ v->type &= ~VF_USER;
+ }
+ v->type |= VF_CACHED;
+ }
+ return v->number;
+}
+
+static var *copyvar(var *dest, const var *src)
+{
+ if (dest != src) {
+ clrvar(dest);
+ dest->type |= (src->type & ~(VF_DONTTOUCH | VF_FSTR));
+ dest->number = src->number;
+ if (src->string)
+ dest->string = xstrdup(src->string);
+ }
+ handle_special(dest);
+ return dest;
+}
+
+static var *incvar(var *v)
+{
+ return setvar_i(v, getvar_i(v)+1.);
+}
+
+/* return true if v is number or numeric string */
+static int is_numeric(var *v)
+{
+ getvar_i(v);
+ return ((v->type ^ VF_DIRTY) & (VF_NUMBER | VF_USER | VF_DIRTY));
+}
+
+/* return 1 when value of v corresponds to true, 0 otherwise */
+static int istrue(var *v)
+{
+ if (is_numeric(v))
+ return (v->number == 0) ? 0 : 1;
+ else
+ return (v->string && *(v->string)) ? 1 : 0;
+}
+
+/* temporary variables allocator. Last allocated should be first freed */
+static var *nvalloc(int n)
+{
+ nvblock *pb = NULL;
+ var *v, *r;
+ int size;
+
+ while (cb) {
+ pb = cb;
+ if ((cb->pos - cb->nv) + n <= cb->size) break;
+ cb = cb->next;
+ }
+
+ if (! cb) {
+ size = (n <= MINNVBLOCK) ? MINNVBLOCK : n;
+ cb = xmalloc(sizeof(nvblock) + size * sizeof(var));
+ cb->size = size;
+ cb->pos = cb->nv;
+ cb->prev = pb;
+ cb->next = NULL;
+ if (pb) pb->next = cb;
+ }
+
+ v = r = cb->pos;
+ cb->pos += n;
+
+ while (v < cb->pos) {
+ v->type = 0;
+ v->string = NULL;
+ v++;
+ }
+
+ return r;
+}
+
+static void nvfree(var *v)
+{
+ var *p;
+
+ if (v < cb->nv || v >= cb->pos)
+ runtime_error(EMSG_INTERNAL_ERROR);
+
+ for (p=v; p<cb->pos; p++) {
+ if ((p->type & (VF_ARRAY|VF_CHILD)) == VF_ARRAY) {
+ clear_array(iamarray(p));
+ free(p->x.array->items);
+ free(p->x.array);
+ }
+ if (p->type & VF_WALK)
+ free(p->x.walker);
+
+ clrvar(p);
+ }
+
+ cb->pos = v;
+ while (cb->prev && cb->pos == cb->nv) {
+ cb = cb->prev;
+ }
+}
+
+/* ------- awk program text parsing ------- */
+
+/* Parse next token pointed by global pos, place results into global t.
+ * If token isn't expected, give away. Return token class
+ */
+static uint32_t next_token(uint32_t expected)
+{
+ static int concat_inserted;
+ static uint32_t save_tclass, save_info;
+ static uint32_t ltclass = TC_OPTERM;
+
+ char *p, *pp, *s;
+ const char *tl;
+ uint32_t tc;
+ const uint32_t *ti;
+ int l;
+
+ if (t.rollback) {
+ t.rollback = FALSE;
+
+ } else if (concat_inserted) {
+ concat_inserted = FALSE;
+ t.tclass = save_tclass;
+ t.info = save_info;
+
+ } else {
+ p = pos;
+ readnext:
+ skip_spaces(&p);
+ lineno = t.lineno;
+ if (*p == '#')
+ while (*p != '\n' && *p != '\0') p++;
+
+ if (*p == '\n')
+ t.lineno++;
+
+ if (*p == '\0') {
+ tc = TC_EOF;
+
+ } else if (*p == '\"') {
+ /* it's a string */
+ t.string = s = ++p;
+ while (*p != '\"') {
+ if (*p == '\0' || *p == '\n')
+ syntax_error(EMSG_UNEXP_EOS);
+ *(s++) = nextchar(&p);
+ }
+ p++;
+ *s = '\0';
+ tc = TC_STRING;
+
+ } else if ((expected & TC_REGEXP) && *p == '/') {
+ /* it's regexp */
+ t.string = s = ++p;
+ while (*p != '/') {
+ if (*p == '\0' || *p == '\n')
+ syntax_error(EMSG_UNEXP_EOS);
+ if ((*s++ = *p++) == '\\') {
+ pp = p;
+ *(s-1) = bb_process_escape_sequence((const char **)&p);
+ if (*pp == '\\') *s++ = '\\';
+ if (p == pp) *s++ = *p++;
+ }
+ }
+ p++;
+ *s = '\0';
+ tc = TC_REGEXP;
+
+ } else if (*p == '.' || isdigit(*p)) {
+ /* it's a number */
+ t.number = strtod(p, &p);
+ if (*p == '.')
+ syntax_error(EMSG_UNEXP_TOKEN);
+ tc = TC_NUMBER;
+
+ } else {
+ /* search for something known */
+ tl = tokenlist;
+ tc = 0x00000001;
+ ti = tokeninfo;
+ while (*tl) {
+ l = *(tl++);
+ if (l == NTCC) {
+ tc <<= 1;
+ continue;
+ }
+ /* if token class is expected, token
+ * matches and it's not a longer word,
+ * then this is what we are looking for
+ */
+ if ((tc & (expected | TC_WORD | TC_NEWLINE)) &&
+ *tl == *p && strncmp(p, tl, l) == 0 &&
+ !((tc & TC_WORD) && isalnum_(*(p + l)))) {
+ t.info = *ti;
+ p += l;
+ break;
+ }
+ ti++;
+ tl += l;
+ }
+
+ if (!*tl) {
+ /* it's a name (var/array/function),
+ * otherwise it's something wrong
+ */
+ if (!isalnum_(*p))
+ syntax_error(EMSG_UNEXP_TOKEN);
+
+ t.string = --p;
+ while (isalnum_(*(++p))) {
+ *(p-1) = *p;
+ }
+ *(p-1) = '\0';
+ tc = TC_VARIABLE;
+ /* also consume whitespace between functionname and bracket */
+ if (!(expected & TC_VARIABLE)) skip_spaces(&p);
+ if (*p == '(') {
+ tc = TC_FUNCTION;
+ } else {
+ if (*p == '[') {
+ p++;
+ tc = TC_ARRAY;
+ }
+ }
+ }
+ }
+ pos = p;
+
+ /* skipping newlines in some cases */
+ if ((ltclass & TC_NOTERM) && (tc & TC_NEWLINE))
+ goto readnext;
+
+ /* insert concatenation operator when needed */
+ if ((ltclass&TC_CONCAT1) && (tc&TC_CONCAT2) && (expected&TC_BINOP)) {
+ concat_inserted = TRUE;
+ save_tclass = tc;
+ save_info = t.info;
+ tc = TC_BINOP;
+ t.info = OC_CONCAT | SS | P(35);
+ }
+
+ t.tclass = tc;
+ }
+ ltclass = t.tclass;
+
+ /* Are we ready for this? */
+ if (! (ltclass & expected))
+ syntax_error((ltclass & (TC_NEWLINE | TC_EOF)) ?
+ EMSG_UNEXP_EOS : EMSG_UNEXP_TOKEN);
+
+ return ltclass;
+}
+
+static void rollback_token(void) { t.rollback = TRUE; }
+
+static node *new_node(uint32_t info)
+{
+ node *n;
+
+ n = xzalloc(sizeof(node));
+ n->info = info;
+ n->lineno = lineno;
+ return n;
+}
+
+static node *mk_re_node(const char *s, node *n, regex_t *re)
+{
+ n->info = OC_REGEXP;
+ n->l.re = re;
+ n->r.ire = re + 1;
+ xregcomp(re, s, REG_EXTENDED);
+ xregcomp(re+1, s, REG_EXTENDED | REG_ICASE);
+
+ return n;
+}
+
+static node *condition(void)
+{
+ next_token(TC_SEQSTART);
+ return parse_expr(TC_SEQTERM);
+}
+
+/* parse expression terminated by given argument, return ptr
+ * to built subtree. Terminator is eaten by parse_expr */
+static node *parse_expr(uint32_t iexp)
+{
+ node sn;
+ node *cn = &sn;
+ node *vn, *glptr;
+ uint32_t tc, xtc;
+ var *v;
+
+ sn.info = PRIMASK;
+ sn.r.n = glptr = NULL;
+ xtc = TC_OPERAND | TC_UOPPRE | TC_REGEXP | iexp;
+
+ while (! ((tc = next_token(xtc)) & iexp)) {
+ if (glptr && (t.info == (OC_COMPARE|VV|P(39)|2))) {
+ /* input redirection (<) attached to glptr node */
+ cn = glptr->l.n = new_node(OC_CONCAT|SS|P(37));
+ cn->a.n = glptr;
+ xtc = TC_OPERAND | TC_UOPPRE;
+ glptr = NULL;
+
+ } else if (tc & (TC_BINOP | TC_UOPPOST)) {
+ /* for binary and postfix-unary operators, jump back over
+ * previous operators with higher priority */
+ vn = cn;
+ while ( ((t.info & PRIMASK) > (vn->a.n->info & PRIMASK2)) ||
+ ((t.info == vn->info) && ((t.info & OPCLSMASK) == OC_COLON)) )
+ vn = vn->a.n;
+ if ((t.info & OPCLSMASK) == OC_TERNARY)
+ t.info += P(6);
+ cn = vn->a.n->r.n = new_node(t.info);
+ cn->a.n = vn->a.n;
+ if (tc & TC_BINOP) {
+ cn->l.n = vn;
+ xtc = TC_OPERAND | TC_UOPPRE | TC_REGEXP;
+ if ((t.info & OPCLSMASK) == OC_PGETLINE) {
+ /* it's a pipe */
+ next_token(TC_GETLINE);
+ /* give maximum priority to this pipe */
+ cn->info &= ~PRIMASK;
+ xtc = TC_OPERAND | TC_UOPPRE | TC_BINOP | iexp;
+ }
+ } else {
+ cn->r.n = vn;
+ xtc = TC_OPERAND | TC_UOPPRE | TC_BINOP | iexp;
+ }
+ vn->a.n = cn;
+
+ } else {
+ /* for operands and prefix-unary operators, attach them
+ * to last node */
+ vn = cn;
+ cn = vn->r.n = new_node(t.info);
+ cn->a.n = vn;
+ xtc = TC_OPERAND | TC_UOPPRE | TC_REGEXP;
+ if (tc & (TC_OPERAND | TC_REGEXP)) {
+ xtc = TC_UOPPRE | TC_UOPPOST | TC_BINOP | TC_OPERAND | iexp;
+ /* one should be very careful with switch on tclass -
+ * only simple tclasses should be used! */
+ switch (tc) {
+ case TC_VARIABLE:
+ case TC_ARRAY:
+ cn->info = OC_VAR;
+ if ((v = hash_search(ahash, t.string)) != NULL) {
+ cn->info = OC_FNARG;
+ cn->l.i = v->x.aidx;
+ } else {
+ cn->l.v = newvar(t.string);
+ }
+ if (tc & TC_ARRAY) {
+ cn->info |= xS;
+ cn->r.n = parse_expr(TC_ARRTERM);
+ }
+ break;
+
+ case TC_NUMBER:
+ case TC_STRING:
+ cn->info = OC_VAR;
+ v = cn->l.v = xzalloc(sizeof(var));
+ if (tc & TC_NUMBER)
+ setvar_i(v, t.number);
+ else
+ setvar_s(v, t.string);
+ break;
+
+ case TC_REGEXP:
+ mk_re_node(t.string, cn, xzalloc(sizeof(regex_t)*2));
+ break;
+
+ case TC_FUNCTION:
+ cn->info = OC_FUNC;
+ cn->r.f = newfunc(t.string);
+ cn->l.n = condition();
+ break;
+
+ case TC_SEQSTART:
+ cn = vn->r.n = parse_expr(TC_SEQTERM);
+ cn->a.n = vn;
+ break;
+
+ case TC_GETLINE:
+ glptr = cn;
+ xtc = TC_OPERAND | TC_UOPPRE | TC_BINOP | iexp;
+ break;
+
+ case TC_BUILTIN:
+ cn->l.n = condition();
+ break;
+ }
+ }
+ }
+ }
+ return sn.r.n;
+}
+
+/* add node to chain. Return ptr to alloc'd node */
+static node *chain_node(uint32_t info)
+{
+ node *n;
+
+ if (! seq->first)
+ seq->first = seq->last = new_node(0);
+
+ if (seq->programname != programname) {
+ seq->programname = programname;
+ n = chain_node(OC_NEWSOURCE);
+ n->l.s = xstrdup(programname);
+ }
+
+ n = seq->last;
+ n->info = info;
+ seq->last = n->a.n = new_node(OC_DONE);
+
+ return n;
+}
+
+static void chain_expr(uint32_t info)
+{
+ node *n;
+
+ n = chain_node(info);
+ n->l.n = parse_expr(TC_OPTERM | TC_GRPTERM);
+ if (t.tclass & TC_GRPTERM)
+ rollback_token();
+}
+
+static node *chain_loop(node *nn)
+{
+ node *n, *n2, *save_brk, *save_cont;
+
+ save_brk = break_ptr;
+ save_cont = continue_ptr;
+
+ n = chain_node(OC_BR | Vx);
+ continue_ptr = new_node(OC_EXEC);
+ break_ptr = new_node(OC_EXEC);
+ chain_group();
+ n2 = chain_node(OC_EXEC | Vx);
+ n2->l.n = nn;
+ n2->a.n = n;
+ continue_ptr->a.n = n2;
+ break_ptr->a.n = n->r.n = seq->last;
+
+ continue_ptr = save_cont;
+ break_ptr = save_brk;
+
+ return n;
+}
+
+/* parse group and attach it to chain */
+static void chain_group(void)
+{
+ uint32_t c;
+ node *n, *n2, *n3;
+
+ do {
+ c = next_token(TC_GRPSEQ);
+ } while (c & TC_NEWLINE);
+
+ if (c & TC_GRPSTART) {
+ while (next_token(TC_GRPSEQ | TC_GRPTERM) != TC_GRPTERM) {
+ if (t.tclass & TC_NEWLINE) continue;
+ rollback_token();
+ chain_group();
+ }
+ } else if (c & (TC_OPSEQ | TC_OPTERM)) {
+ rollback_token();
+ chain_expr(OC_EXEC | Vx);
+ } else { /* TC_STATEMNT */
+ switch (t.info & OPCLSMASK) {
+ case ST_IF:
+ n = chain_node(OC_BR | Vx);
+ n->l.n = condition();
+ chain_group();
+ n2 = chain_node(OC_EXEC);
+ n->r.n = seq->last;
+ if (next_token(TC_GRPSEQ | TC_GRPTERM | TC_ELSE)==TC_ELSE) {
+ chain_group();
+ n2->a.n = seq->last;
+ } else {
+ rollback_token();
+ }
+ break;
+
+ case ST_WHILE:
+ n2 = condition();
+ n = chain_loop(NULL);
+ n->l.n = n2;
+ break;
+
+ case ST_DO:
+ n2 = chain_node(OC_EXEC);
+ n = chain_loop(NULL);
+ n2->a.n = n->a.n;
+ next_token(TC_WHILE);
+ n->l.n = condition();
+ break;
+
+ case ST_FOR:
+ next_token(TC_SEQSTART);
+ n2 = parse_expr(TC_SEMICOL | TC_SEQTERM);
+ if (t.tclass & TC_SEQTERM) { /* for-in */
+ if ((n2->info & OPCLSMASK) != OC_IN)
+ syntax_error(EMSG_UNEXP_TOKEN);
+ n = chain_node(OC_WALKINIT | VV);
+ n->l.n = n2->l.n;
+ n->r.n = n2->r.n;
+ n = chain_loop(NULL);
+ n->info = OC_WALKNEXT | Vx;
+ n->l.n = n2->l.n;
+ } else { /* for (;;) */
+ n = chain_node(OC_EXEC | Vx);
+ n->l.n = n2;
+ n2 = parse_expr(TC_SEMICOL);
+ n3 = parse_expr(TC_SEQTERM);
+ n = chain_loop(n3);
+ n->l.n = n2;
+ if (! n2)
+ n->info = OC_EXEC;
+ }
+ break;
+
+ case OC_PRINT:
+ case OC_PRINTF:
+ n = chain_node(t.info);
+ n->l.n = parse_expr(TC_OPTERM | TC_OUTRDR | TC_GRPTERM);
+ if (t.tclass & TC_OUTRDR) {
+ n->info |= t.info;
+ n->r.n = parse_expr(TC_OPTERM | TC_GRPTERM);
+ }
+ if (t.tclass & TC_GRPTERM)
+ rollback_token();
+ break;
+
+ case OC_BREAK:
+ n = chain_node(OC_EXEC);
+ n->a.n = break_ptr;
+ break;
+
+ case OC_CONTINUE:
+ n = chain_node(OC_EXEC);
+ n->a.n = continue_ptr;
+ break;
+
+ /* delete, next, nextfile, return, exit */
+ default:
+ chain_expr(t.info);
+ }
+ }
+}
+
+static void parse_program(char *p)
+{
+ uint32_t tclass;
+ node *cn;
+ func *f;
+ var *v;
+
+ pos = p;
+ t.lineno = 1;
+ while ((tclass = next_token(TC_EOF | TC_OPSEQ | TC_GRPSTART |
+ TC_OPTERM | TC_BEGIN | TC_END | TC_FUNCDECL)) != TC_EOF) {
+
+ if (tclass & TC_OPTERM)
+ continue;
+
+ seq = &mainseq;
+ if (tclass & TC_BEGIN) {
+ seq = &beginseq;
+ chain_group();
+
+ } else if (tclass & TC_END) {
+ seq = &endseq;
+ chain_group();
+
+ } else if (tclass & TC_FUNCDECL) {
+ next_token(TC_FUNCTION);
+ pos++;
+ f = newfunc(t.string);
+ f->body.first = NULL;
+ f->nargs = 0;
+ while (next_token(TC_VARIABLE | TC_SEQTERM) & TC_VARIABLE) {
+ v = findvar(ahash, t.string);
+ v->x.aidx = (f->nargs)++;
+
+ if (next_token(TC_COMMA | TC_SEQTERM) & TC_SEQTERM)
+ break;
+ }
+ seq = &(f->body);
+ chain_group();
+ clear_array(ahash);
+
+ } else if (tclass & TC_OPSEQ) {
+ rollback_token();
+ cn = chain_node(OC_TEST);
+ cn->l.n = parse_expr(TC_OPTERM | TC_EOF | TC_GRPSTART);
+ if (t.tclass & TC_GRPSTART) {
+ rollback_token();
+ chain_group();
+ } else {
+ chain_node(OC_PRINT);
+ }
+ cn->r.n = mainseq.last;
+
+ } else /* if (tclass & TC_GRPSTART) */ {
+ rollback_token();
+ chain_group();
+ }
+ }
+}
+
+
+/* -------- program execution part -------- */
+
+static node *mk_splitter(const char *s, tsplitter *spl)
+{
+ regex_t *re, *ire;
+ node *n;
+
+ re = &spl->re[0];
+ ire = &spl->re[1];
+ n = &spl->n;
+ if ((n->info & OPCLSMASK) == OC_REGEXP) {
+ regfree(re);
+ regfree(ire);
+ }
+ if (strlen(s) > 1) {
+ mk_re_node(s, n, re);
+ } else {
+ n->info = (uint32_t) *s;
+ }
+
+ return n;
+}
+
+/* use node as a regular expression. Supplied with node ptr and regex_t
+ * storage space. Return ptr to regex (if result points to preg, it should
+ * be later regfree'd manually
+ */
+static regex_t *as_regex(node *op, regex_t *preg)
+{
+ var *v;
+ const char *s;
+
+ if ((op->info & OPCLSMASK) == OC_REGEXP) {
+ return icase ? op->r.ire : op->l.re;
+ } else {
+ v = nvalloc(1);
+ s = getvar_s(evaluate(op, v));
+ xregcomp(preg, s, icase ? REG_EXTENDED | REG_ICASE : REG_EXTENDED);
+ nvfree(v);
+ return preg;
+ }
+}
+
+/* gradually increasing buffer */
+static void qrealloc(char **b, int n, int *size)
+{
+ if (!*b || n >= *size)
+ *b = xrealloc(*b, *size = n + (n>>1) + 80);
+}
+
+/* resize field storage space */
+static void fsrealloc(int size)
+{
+ static int maxfields; /* = 0;*/
+ int i;
+
+ if (size >= maxfields) {
+ i = maxfields;
+ maxfields = size + 16;
+ Fields = xrealloc(Fields, maxfields * sizeof(var));
+ for (; i < maxfields; i++) {
+ Fields[i].type = VF_SPECIAL;
+ Fields[i].string = NULL;
+ }
+ }
+
+ if (size < nfields) {
+ for (i = size; i < nfields; i++) {
+ clrvar(Fields + i);
+ }
+ }
+ nfields = size;
+}
+
+static int awk_split(const char *s, node *spl, char **slist)
+{
+ int l, n = 0;
+ char c[4];
+ char *s1;
+ regmatch_t pmatch[2];
+
+ /* in worst case, each char would be a separate field */
+ *slist = s1 = xzalloc(strlen(s) * 2 + 3);
+ strcpy(s1, s);
+
+ c[0] = c[1] = (char)spl->info;
+ c[2] = c[3] = '\0';
+ if (*getvar_s(V[RS]) == '\0') c[2] = '\n';
+
+ if ((spl->info & OPCLSMASK) == OC_REGEXP) { /* regex split */
+ while (*s) {
+ l = strcspn(s, c+2);
+ if (regexec(icase ? spl->r.ire : spl->l.re, s, 1, pmatch, 0) == 0
+ && pmatch[0].rm_so <= l
+ ) {
+ l = pmatch[0].rm_so;
+ if (pmatch[0].rm_eo == 0) { l++; pmatch[0].rm_eo++; }
+ } else {
+ pmatch[0].rm_eo = l;
+ if (s[l]) pmatch[0].rm_eo++;
+ }
+
+ memcpy(s1, s, l);
+ s1[l] = '\0';
+ nextword(&s1);
+ s += pmatch[0].rm_eo;
+ n++;
+ }
+ } else if (c[0] == '\0') { /* null split */
+ while (*s) {
+ *s1++ = *s++;
+ *s1++ = '\0';
+ n++;
+ }
+ } else if (c[0] != ' ') { /* single-character split */
+ if (icase) {
+ c[0] = toupper(c[0]);
+ c[1] = tolower(c[1]);
+ }
+ if (*s1) n++;
+ while ((s1 = strpbrk(s1, c))) {
+ *s1++ = '\0';
+ n++;
+ }
+ } else { /* space split */
+ while (*s) {
+ s = skip_whitespace(s);
+ if (!*s) break;
+ n++;
+ while (*s && !isspace(*s))
+ *s1++ = *s++;
+ *s1++ = '\0';
+ }
+ }
+ return n;
+}
+
+static void split_f0(void)
+{
+ static char *fstrings = NULL;
+ int i, n;
+ char *s;
+
+ if (is_f0_split)
+ return;
+
+ is_f0_split = TRUE;
+ free(fstrings);
+ fsrealloc(0);
+ n = awk_split(getvar_s(V[F0]), &fsplitter.n, &fstrings);
+ fsrealloc(n);
+ s = fstrings;
+ for (i = 0; i < n; i++) {
+ Fields[i].string = nextword(&s);
+ Fields[i].type |= (VF_FSTR | VF_USER | VF_DIRTY);
+ }
+
+ /* set NF manually to avoid side effects */
+ clrvar(V[NF]);
+ V[NF]->type = VF_NUMBER | VF_SPECIAL;
+ V[NF]->number = nfields;
+}
+
+/* perform additional actions when some internal variables changed */
+static void handle_special(var *v)
+{
+ int n;
+ char *b;
+ const char *sep, *s;
+ int sl, l, len, i, bsize;
+
+ if (!(v->type & VF_SPECIAL))
+ return;
+
+ if (v == V[NF]) {
+ n = (int)getvar_i(v);
+ fsrealloc(n);
+
+ /* recalculate $0 */
+ sep = getvar_s(V[OFS]);
+ sl = strlen(sep);
+ b = NULL;
+ len = 0;
+ for (i=0; i<n; i++) {
+ s = getvar_s(&Fields[i]);
+ l = strlen(s);
+ if (b) {
+ memcpy(b+len, sep, sl);
+ len += sl;
+ }
+ qrealloc(&b, len+l+sl, &bsize);
+ memcpy(b+len, s, l);
+ len += l;
+ }
+ if (b)
+ b[len] = '\0';
+ setvar_p(V[F0], b);
+ is_f0_split = TRUE;
+
+ } else if (v == V[F0]) {
+ is_f0_split = FALSE;
+
+ } else if (v == V[FS]) {
+ mk_splitter(getvar_s(v), &fsplitter);
+
+ } else if (v == V[RS]) {
+ mk_splitter(getvar_s(v), &rsplitter);
+
+ } else if (v == V[IGNORECASE]) {
+ icase = istrue(v);
+
+ } else { /* $n */
+ n = getvar_i(V[NF]);
+ setvar_i(V[NF], n > v-Fields ? n : v-Fields+1);
+ /* right here v is invalid. Just to note... */
+ }
+}
+
+/* step through func/builtin/etc arguments */
+static node *nextarg(node **pn)
+{
+ node *n;
+
+ n = *pn;
+ if (n && (n->info & OPCLSMASK) == OC_COMMA) {
+ *pn = n->r.n;
+ n = n->l.n;
+ } else {
+ *pn = NULL;
+ }
+ return n;
+}
+
+static void hashwalk_init(var *v, xhash *array)
+{
+ char **w;
+ hash_item *hi;
+ int i;
+
+ if (v->type & VF_WALK)
+ free(v->x.walker);
+
+ v->type |= VF_WALK;
+ w = v->x.walker = xzalloc(2 + 2*sizeof(char *) + array->glen);
+ *w = *(w+1) = (char *)(w + 2);
+ for (i=0; i<array->csize; i++) {
+ hi = array->items[i];
+ while (hi) {
+ strcpy(*w, hi->name);
+ nextword(w);
+ hi = hi->next;
+ }
+ }
+}
+
+static int hashwalk_next(var *v)
+{
+ char **w;
+
+ w = v->x.walker;
+ if (*(w+1) == *w)
+ return FALSE;
+
+ setvar_s(v, nextword(w+1));
+ return TRUE;
+}
+
+/* evaluate node, return 1 when result is true, 0 otherwise */
+static int ptest(node *pattern)
+{
+ static var v; /* static: to save stack space? */
+
+ return istrue(evaluate(pattern, &v));
+}
+
+/* read next record from stream rsm into a variable v */
+static int awk_getline(rstream *rsm, var *v)
+{
+ char *b;
+ regmatch_t pmatch[2];
+ int a, p, pp=0, size;
+ int fd, so, eo, r, rp;
+ char c, *m, *s;
+
+ /* we're using our own buffer since we need access to accumulating
+ * characters
+ */
+ fd = fileno(rsm->F);
+ m = rsm->buffer;
+ a = rsm->adv;
+ p = rsm->pos;
+ size = rsm->size;
+ c = (char) rsplitter.n.info;
+ rp = 0;
+
+ if (! m) qrealloc(&m, 256, &size);
+ do {
+ b = m + a;
+ so = eo = p;
+ r = 1;
+ if (p > 0) {
+ if ((rsplitter.n.info & OPCLSMASK) == OC_REGEXP) {
+ if (regexec(icase ? rsplitter.n.r.ire : rsplitter.n.l.re,
+ b, 1, pmatch, 0) == 0) {
+ so = pmatch[0].rm_so;
+ eo = pmatch[0].rm_eo;
+ if (b[eo] != '\0')
+ break;
+ }
+ } else if (c != '\0') {
+ s = strchr(b+pp, c);
+ if (! s) s = memchr(b+pp, '\0', p - pp);
+ if (s) {
+ so = eo = s-b;
+ eo++;
+ break;
+ }
+ } else {
+ while (b[rp] == '\n')
+ rp++;
+ s = strstr(b+rp, "\n\n");
+ if (s) {
+ so = eo = s-b;
+ while (b[eo] == '\n') eo++;
+ if (b[eo] != '\0')
+ break;
+ }
+ }
+ }
+
+ if (a > 0) {
+ memmove(m, (const void *)(m+a), p+1);
+ b = m;
+ a = 0;
+ }
+
+ qrealloc(&m, a+p+128, &size);
+ b = m + a;
+ pp = p;
+ p += safe_read(fd, b+p, size-p-1);
+ if (p < pp) {
+ p = 0;
+ r = 0;
+ setvar_i(V[ERRNO], errno);
+ }
+ b[p] = '\0';
+
+ } while (p > pp);
+
+ if (p == 0) {
+ r--;
+ } else {
+ c = b[so]; b[so] = '\0';
+ setvar_s(v, b+rp);
+ v->type |= VF_USER;
+ b[so] = c;
+ c = b[eo]; b[eo] = '\0';
+ setvar_s(V[RT], b+so);
+ b[eo] = c;
+ }
+
+ rsm->buffer = m;
+ rsm->adv = a + eo;
+ rsm->pos = p - eo;
+ rsm->size = size;
+
+ return r;
+}
+
+static int fmt_num(char *b, int size, const char *format, double n, int int_as_int)
+{
+ int r = 0;
+ char c;
+ const char *s = format;
+
+ if (int_as_int && n == (int)n) {
+ r = snprintf(b, size, "%d", (int)n);
+ } else {
+ do { c = *s; } while (c && *++s);
+ if (strchr("diouxX", c)) {
+ r = snprintf(b, size, format, (int)n);
+ } else if (strchr("eEfgG", c)) {
+ r = snprintf(b, size, format, n);
+ } else {
+ runtime_error(EMSG_INV_FMT);
+ }
+ }
+ return r;
+}
+
+
+/* formatted output into an allocated buffer, return ptr to buffer */
+static char *awk_printf(node *n)
+{
+ char *b = NULL;
+ char *fmt, *s, *f;
+ const char *s1;
+ int i, j, incr, bsize;
+ char c, c1;
+ var *v, *arg;
+
+ v = nvalloc(1);
+ fmt = f = xstrdup(getvar_s(evaluate(nextarg(&n), v)));
+
+ i = 0;
+ while (*f) {
+ s = f;
+ while (*f && (*f != '%' || *(++f) == '%'))
+ f++;
+ while (*f && !isalpha(*f))
+ f++;
+
+ incr = (f - s) + MAXVARFMT;
+ qrealloc(&b, incr + i, &bsize);
+ c = *f;
+ if (c != '\0') f++;
+ c1 = *f;
+ *f = '\0';
+ arg = evaluate(nextarg(&n), v);
+
+ j = i;
+ if (c == 'c' || !c) {
+ i += sprintf(b+i, s, is_numeric(arg) ?
+ (char)getvar_i(arg) : *getvar_s(arg));
+
+ } else if (c == 's') {
+ s1 = getvar_s(arg);
+ qrealloc(&b, incr+i+strlen(s1), &bsize);
+ i += sprintf(b+i, s, s1);
+
+ } else {
+ i += fmt_num(b+i, incr, s, getvar_i(arg), FALSE);
+ }
+ *f = c1;
+
+ /* if there was an error while sprintf, return value is negative */
+ if (i < j) i = j;
+ }
+
+ b = xrealloc(b, i + 1);
+ free(fmt);
+ nvfree(v);
+ b[i] = '\0';
+ return b;
+}
+
+/* common substitution routine
+ * replace (nm) substring of (src) that match (n) with (repl), store
+ * result into (dest), return number of substitutions. If nm=0, replace
+ * all matches. If src or dst is NULL, use $0. If ex=TRUE, enable
+ * subexpression matching (\1-\9)
+ */
+static int awk_sub(node *rn, const char *repl, int nm, var *src, var *dest, int ex)
+{
+ char *ds = NULL;
+ const char *s;
+ const char *sp;
+ int c, i, j, di, rl, so, eo, nbs, n, dssize;
+ regmatch_t pmatch[10];
+ regex_t sreg, *re;
+
+ re = as_regex(rn, &sreg);
+ if (! src) src = V[F0];
+ if (! dest) dest = V[F0];
+
+ i = di = 0;
+ sp = getvar_s(src);
+ rl = strlen(repl);
+ while (regexec(re, sp, 10, pmatch, sp==getvar_s(src) ? 0:REG_NOTBOL) == 0) {
+ so = pmatch[0].rm_so;
+ eo = pmatch[0].rm_eo;
+
+ qrealloc(&ds, di + eo + rl, &dssize);
+ memcpy(ds + di, sp, eo);
+ di += eo;
+ if (++i >= nm) {
+ /* replace */
+ di -= (eo - so);
+ nbs = 0;
+ for (s = repl; *s; s++) {
+ ds[di++] = c = *s;
+ if (c == '\\') {
+ nbs++;
+ continue;
+ }
+ if (c == '&' || (ex && c >= '0' && c <= '9')) {
+ di -= ((nbs + 3) >> 1);
+ j = 0;
+ if (c != '&') {
+ j = c - '0';
+ nbs++;
+ }
+ if (nbs % 2) {
+ ds[di++] = c;
+ } else {
+ n = pmatch[j].rm_eo - pmatch[j].rm_so;
+ qrealloc(&ds, di + rl + n, &dssize);
+ memcpy(ds + di, sp + pmatch[j].rm_so, n);
+ di += n;
+ }
+ }
+ nbs = 0;
+ }
+ }
+
+ sp += eo;
+ if (i == nm) break;
+ if (eo == so) {
+ if (! (ds[di++] = *sp++)) break;
+ }
+ }
+
+ qrealloc(&ds, di + strlen(sp), &dssize);
+ strcpy(ds + di, sp);
+ setvar_p(dest, ds);
+ if (re == &sreg) regfree(re);
+ return i;
+}
+
+static var *exec_builtin(node *op, var *res)
+{
+ int (*to_xxx)(int);
+ var *tv;
+ node *an[4];
+ var *av[4];
+ const char *as[4];
+ regmatch_t pmatch[2];
+ regex_t sreg, *re;
+ static tsplitter tspl;
+ node *spl;
+ uint32_t isr, info;
+ int nargs;
+ time_t tt;
+ char *s, *s1;
+ int i, l, ll, n;
+
+ tv = nvalloc(4);
+ isr = info = op->info;
+ op = op->l.n;
+
+ av[2] = av[3] = NULL;
+ for (i=0 ; i<4 && op ; i++) {
+ an[i] = nextarg(&op);
+ if (isr & 0x09000000) av[i] = evaluate(an[i], &tv[i]);
+ if (isr & 0x08000000) as[i] = getvar_s(av[i]);
+ isr >>= 1;
+ }
+
+ nargs = i;
+ if (nargs < (info >> 30))
+ runtime_error(EMSG_TOO_FEW_ARGS);
+
+ switch (info & OPNMASK) {
+
+ case B_a2:
+#if ENABLE_FEATURE_AWK_MATH
+ setvar_i(res, atan2(getvar_i(av[i]), getvar_i(av[1])));
+#else
+ runtime_error(EMSG_NO_MATH);
+#endif
+ break;
+
+ case B_sp:
+ if (nargs > 2) {
+ spl = (an[2]->info & OPCLSMASK) == OC_REGEXP ?
+ an[2] : mk_splitter(getvar_s(evaluate(an[2], &tv[2])), &tspl);
+ } else {
+ spl = &fsplitter.n;
+ }
+
+ n = awk_split(as[0], spl, &s);
+ s1 = s;
+ clear_array(iamarray(av[1]));
+ for (i=1; i<=n; i++)
+ setari_u(av[1], i, nextword(&s1));
+ free(s);
+ setvar_i(res, n);
+ break;
+
+ case B_ss:
+ l = strlen(as[0]);
+ i = getvar_i(av[1]) - 1;
+ if (i>l) i=l; if (i<0) i=0;
+ n = (nargs > 2) ? getvar_i(av[2]) : l-i;
+ if (n<0) n=0;
+ s = xmalloc(n+1);
+ strncpy(s, as[0]+i, n);
+ s[n] = '\0';
+ setvar_p(res, s);
+ break;
+
+ case B_an:
+ setvar_i(res, (long)getvar_i(av[0]) & (long)getvar_i(av[1]));
+ break;
+
+ case B_co:
+ setvar_i(res, ~(long)getvar_i(av[0]));
+ break;
+
+ case B_ls:
+ setvar_i(res, (long)getvar_i(av[0]) << (long)getvar_i(av[1]));
+ break;
+
+ case B_or:
+ setvar_i(res, (long)getvar_i(av[0]) | (long)getvar_i(av[1]));
+ break;
+
+ case B_rs:
+ setvar_i(res, (long)((unsigned long)getvar_i(av[0]) >> (unsigned long)getvar_i(av[1])));
+ break;
+
+ case B_xo:
+ setvar_i(res, (long)getvar_i(av[0]) ^ (long)getvar_i(av[1]));
+ break;
+
+ case B_lo:
+ to_xxx = tolower;
+ goto lo_cont;
+
+ case B_up:
+ to_xxx = toupper;
+ lo_cont:
+ s1 = s = xstrdup(as[0]);
+ while (*s1) {
+ *s1 = (*to_xxx)(*s1);
+ s1++;
+ }
+ setvar_p(res, s);
+ break;
+
+ case B_ix:
+ n = 0;
+ ll = strlen(as[1]);
+ l = strlen(as[0]) - ll;
+ if (ll > 0 && l >= 0) {
+ if (! icase) {
+ s = strstr(as[0], as[1]);
+ if (s) n = (s - as[0]) + 1;
+ } else {
+ /* this piece of code is terribly slow and
+ * really should be rewritten
+ */
+ for (i=0; i<=l; i++) {
+ if (strncasecmp(as[0]+i, as[1], ll) == 0) {
+ n = i+1;
+ break;
+ }
+ }
+ }
+ }
+ setvar_i(res, n);
+ break;
+
+ case B_ti:
+ if (nargs > 1)
+ tt = getvar_i(av[1]);
+ else
+ time(&tt);
+ //s = (nargs > 0) ? as[0] : "%a %b %d %H:%M:%S %Z %Y";
+ i = strftime(buf, MAXVARFMT,
+ ((nargs > 0) ? as[0] : "%a %b %d %H:%M:%S %Z %Y"),
+ localtime(&tt));
+ buf[i] = '\0';
+ setvar_s(res, buf);
+ break;
+
+ case B_ma:
+ re = as_regex(an[1], &sreg);
+ n = regexec(re, as[0], 1, pmatch, 0);
+ if (n == 0) {
+ pmatch[0].rm_so++;
+ pmatch[0].rm_eo++;
+ } else {
+ pmatch[0].rm_so = 0;
+ pmatch[0].rm_eo = -1;
+ }
+ setvar_i(newvar("RSTART"), pmatch[0].rm_so);
+ setvar_i(newvar("RLENGTH"), pmatch[0].rm_eo - pmatch[0].rm_so);
+ setvar_i(res, pmatch[0].rm_so);
+ if (re == &sreg) regfree(re);
+ break;
+
+ case B_ge:
+ awk_sub(an[0], as[1], getvar_i(av[2]), av[3], res, TRUE);
+ break;
+
+ case B_gs:
+ setvar_i(res, awk_sub(an[0], as[1], 0, av[2], av[2], FALSE));
+ break;
+
+ case B_su:
+ setvar_i(res, awk_sub(an[0], as[1], 1, av[2], av[2], FALSE));
+ break;
+ }
+
+ nvfree(tv);
+ return res;
+}
+
+/*
+ * Evaluate node - the heart of the program. Supplied with subtree
+ * and place where to store result. returns ptr to result.
+ */
+#define XC(n) ((n) >> 8)
+
+static var *evaluate(node *op, var *res)
+{
+ /* This procedure is recursive so we should count every byte */
+ static var *fnargs = NULL;
+ static unsigned seed = 1;
+ static regex_t sreg;
+
+ node *op1;
+ var *v1;
+ union {
+ var *v;
+ const char *s;
+ double d;
+ int i;
+ } L, R;
+ uint32_t opinfo;
+ short opn;
+ union {
+ char *s;
+ rstream *rsm;
+ FILE *F;
+ var *v;
+ regex_t *re;
+ uint32_t info;
+ } X;
+
+ if (!op)
+ return setvar_s(res, NULL);
+
+ v1 = nvalloc(2);
+
+ while (op) {
+
+ opinfo = op->info;
+ opn = (short)(opinfo & OPNMASK);
+ lineno = op->lineno;
+
+ /* execute inevitable things */
+ op1 = op->l.n;
+ if (opinfo & OF_RES1) X.v = L.v = evaluate(op1, v1);
+ if (opinfo & OF_RES2) R.v = evaluate(op->r.n, v1+1);
+ if (opinfo & OF_STR1) L.s = getvar_s(L.v);
+ if (opinfo & OF_STR2) R.s = getvar_s(R.v);
+ if (opinfo & OF_NUM1) L.d = getvar_i(L.v);
+
+ switch (XC(opinfo & OPCLSMASK)) {
+
+ /* -- iterative node type -- */
+
+ /* test pattern */
+ case XC( OC_TEST ):
+ if ((op1->info & OPCLSMASK) == OC_COMMA) {
+ /* it's range pattern */
+ if ((opinfo & OF_CHECKED) || ptest(op1->l.n)) {
+ op->info |= OF_CHECKED;
+ if (ptest(op1->r.n))
+ op->info &= ~OF_CHECKED;
+
+ op = op->a.n;
+ } else {
+ op = op->r.n;
+ }
+ } else {
+ op = (ptest(op1)) ? op->a.n : op->r.n;
+ }
+ break;
+
+ /* just evaluate an expression, also used as unconditional jump */
+ case XC( OC_EXEC ):
+ break;
+
+ /* branch, used in if-else and various loops */
+ case XC( OC_BR ):
+ op = istrue(L.v) ? op->a.n : op->r.n;
+ break;
+
+ /* initialize for-in loop */
+ case XC( OC_WALKINIT ):
+ hashwalk_init(L.v, iamarray(R.v));
+ break;
+
+ /* get next array item */
+ case XC( OC_WALKNEXT ):
+ op = hashwalk_next(L.v) ? op->a.n : op->r.n;
+ break;
+
+ case XC( OC_PRINT ):
+ case XC( OC_PRINTF ):
+ X.F = stdout;
+ if (op->r.n) {
+ X.rsm = newfile(R.s);
+ if (! X.rsm->F) {
+ if (opn == '|') {
+ if((X.rsm->F = popen(R.s, "w")) == NULL)
+ bb_perror_msg_and_die("popen");
+ X.rsm->is_pipe = 1;
+ } else {
+ X.rsm->F = xfopen(R.s, opn=='w' ? "w" : "a");
+ }
+ }
+ X.F = X.rsm->F;
+ }
+
+ if ((opinfo & OPCLSMASK) == OC_PRINT) {
+ if (! op1) {
+ fputs(getvar_s(V[F0]), X.F);
+ } else {
+ while (op1) {
+ L.v = evaluate(nextarg(&op1), v1);
+ if (L.v->type & VF_NUMBER) {
+ fmt_num(buf, MAXVARFMT, getvar_s(V[OFMT]),
+ getvar_i(L.v), TRUE);
+ fputs(buf, X.F);
+ } else {
+ fputs(getvar_s(L.v), X.F);
+ }
+
+ if (op1) fputs(getvar_s(V[OFS]), X.F);
+ }
+ }
+ fputs(getvar_s(V[ORS]), X.F);
+
+ } else { /* OC_PRINTF */
+ L.s = awk_printf(op1);
+ fputs(L.s, X.F);
+ free((char*)L.s);
+ }
+ fflush(X.F);
+ break;
+
+ case XC( OC_DELETE ):
+ X.info = op1->info & OPCLSMASK;
+ if (X.info == OC_VAR) {
+ R.v = op1->l.v;
+ } else if (X.info == OC_FNARG) {
+ R.v = &fnargs[op1->l.i];
+ } else {
+ runtime_error(EMSG_NOT_ARRAY);
+ }
+
+ if (op1->r.n) {
+ clrvar(L.v);
+ L.s = getvar_s(evaluate(op1->r.n, v1));
+ hash_remove(iamarray(R.v), L.s);
+ } else {
+ clear_array(iamarray(R.v));
+ }
+ break;
+
+ case XC( OC_NEWSOURCE ):
+ programname = op->l.s;
+ break;
+
+ case XC( OC_RETURN ):
+ copyvar(res, L.v);
+ break;
+
+ case XC( OC_NEXTFILE ):
+ nextfile = TRUE;
+ case XC( OC_NEXT ):
+ nextrec = TRUE;
+ case XC( OC_DONE ):
+ clrvar(res);
+ break;
+
+ case XC( OC_EXIT ):
+ awk_exit(L.d);
+
+ /* -- recursive node type -- */
+
+ case XC( OC_VAR ):
+ L.v = op->l.v;
+ if (L.v == V[NF])
+ split_f0();
+ goto v_cont;
+
+ case XC( OC_FNARG ):
+ L.v = &fnargs[op->l.i];
+ v_cont:
+ res = op->r.n ? findvar(iamarray(L.v), R.s) : L.v;
+ break;
+
+ case XC( OC_IN ):
+ setvar_i(res, hash_search(iamarray(R.v), L.s) ? 1 : 0);
+ break;
+
+ case XC( OC_REGEXP ):
+ op1 = op;
+ L.s = getvar_s(V[F0]);
+ goto re_cont;
+
+ case XC( OC_MATCH ):
+ op1 = op->r.n;
+ re_cont:
+ X.re = as_regex(op1, &sreg);
+ R.i = regexec(X.re, L.s, 0, NULL, 0);
+ if (X.re == &sreg) regfree(X.re);
+ setvar_i(res, (R.i == 0 ? 1 : 0) ^ (opn == '!' ? 1 : 0));
+ break;
+
+ case XC( OC_MOVE ):
+ /* if source is a temporary string, jusk relink it to dest */
+ if (R.v == v1+1 && R.v->string) {
+ res = setvar_p(L.v, R.v->string);
+ R.v->string = NULL;
+ } else {
+ res = copyvar(L.v, R.v);
+ }
+ break;
+
+ case XC( OC_TERNARY ):
+ if ((op->r.n->info & OPCLSMASK) != OC_COLON)
+ runtime_error(EMSG_POSSIBLE_ERROR);
+ res = evaluate(istrue(L.v) ? op->r.n->l.n : op->r.n->r.n, res);
+ break;
+
+ case XC( OC_FUNC ):
+ if (! op->r.f->body.first)
+ runtime_error(EMSG_UNDEF_FUNC);
+
+ X.v = R.v = nvalloc(op->r.f->nargs+1);
+ while (op1) {
+ L.v = evaluate(nextarg(&op1), v1);
+ copyvar(R.v, L.v);
+ R.v->type |= VF_CHILD;
+ R.v->x.parent = L.v;
+ if (++R.v - X.v >= op->r.f->nargs)
+ break;
+ }
+
+ R.v = fnargs;
+ fnargs = X.v;
+
+ L.s = programname;
+ res = evaluate(op->r.f->body.first, res);
+ programname = L.s;
+
+ nvfree(fnargs);
+ fnargs = R.v;
+ break;
+
+ case XC( OC_GETLINE ):
+ case XC( OC_PGETLINE ):
+ if (op1) {
+ X.rsm = newfile(L.s);
+ if (! X.rsm->F) {
+ if ((opinfo & OPCLSMASK) == OC_PGETLINE) {
+ X.rsm->F = popen(L.s, "r");
+ X.rsm->is_pipe = TRUE;
+ } else {
+ X.rsm->F = fopen(L.s, "r"); /* not xfopen! */
+ }
+ }
+ } else {
+ if (! iF) iF = next_input_file();
+ X.rsm = iF;
+ }
+
+ if (! X.rsm->F) {
+ setvar_i(V[ERRNO], errno);
+ setvar_i(res, -1);
+ break;
+ }
+
+ if (! op->r.n)
+ R.v = V[F0];
+
+ L.i = awk_getline(X.rsm, R.v);
+ if (L.i > 0) {
+ if (! op1) {
+ incvar(V[FNR]);
+ incvar(V[NR]);
+ }
+ }
+ setvar_i(res, L.i);
+ break;
+
+ /* simple builtins */
+ case XC( OC_FBLTIN ):
+ switch (opn) {
+
+ case F_in:
+ R.d = (int)L.d;
+ break;
+
+ case F_rn:
+ R.d = (double)rand() / (double)RAND_MAX;
+ break;
+
+#if ENABLE_FEATURE_AWK_MATH
+ case F_co:
+ R.d = cos(L.d);
+ break;
+
+ case F_ex:
+ R.d = exp(L.d);
+ break;
+
+ case F_lg:
+ R.d = log(L.d);
+ break;
+
+ case F_si:
+ R.d = sin(L.d);
+ break;
+
+ case F_sq:
+ R.d = sqrt(L.d);
+ break;
+#else
+ case F_co:
+ case F_ex:
+ case F_lg:
+ case F_si:
+ case F_sq:
+ runtime_error(EMSG_NO_MATH);
+ break;
+#endif
+
+ case F_sr:
+ R.d = (double)seed;
+ seed = op1 ? (unsigned)L.d : (unsigned)time(NULL);
+ srand(seed);
+ break;
+
+ case F_ti:
+ R.d = time(NULL);
+ break;
+
+ case F_le:
+ if (! op1)
+ L.s = getvar_s(V[F0]);
+ R.d = strlen(L.s);
+ break;
+
+ case F_sy:
+ fflush(NULL);
+ R.d = (ENABLE_FEATURE_ALLOW_EXEC && L.s && *L.s)
+ ? (system(L.s) >> 8) : 0;
+ break;
+
+ case F_ff:
+ if (! op1)
+ fflush(stdout);
+ else {
+ if (L.s && *L.s) {
+ X.rsm = newfile(L.s);
+ fflush(X.rsm->F);
+ } else {
+ fflush(NULL);
+ }
+ }
+ break;
+
+ case F_cl:
+ X.rsm = (rstream *)hash_search(fdhash, L.s);
+ if (X.rsm) {
+ R.i = X.rsm->is_pipe ? pclose(X.rsm->F) : fclose(X.rsm->F);
+ free(X.rsm->buffer);
+ hash_remove(fdhash, L.s);
+ }
+ if (R.i != 0)
+ setvar_i(V[ERRNO], errno);
+ R.d = (double)R.i;
+ break;
+ }
+ setvar_i(res, R.d);
+ break;
+
+ case XC( OC_BUILTIN ):
+ res = exec_builtin(op, res);
+ break;
+
+ case XC( OC_SPRINTF ):
+ setvar_p(res, awk_printf(op1));
+ break;
+
+ case XC( OC_UNARY ):
+ X.v = R.v;
+ L.d = R.d = getvar_i(R.v);
+ switch (opn) {
+ case 'P':
+ L.d = ++R.d;
+ goto r_op_change;
+ case 'p':
+ R.d++;
+ goto r_op_change;
+ case 'M':
+ L.d = --R.d;
+ goto r_op_change;
+ case 'm':
+ R.d--;
+ goto r_op_change;
+ case '!':
+ L.d = istrue(X.v) ? 0 : 1;
+ break;
+ case '-':
+ L.d = -R.d;
+ break;
+ r_op_change:
+ setvar_i(X.v, R.d);
+ }
+ setvar_i(res, L.d);
+ break;
+
+ case XC( OC_FIELD ):
+ R.i = (int)getvar_i(R.v);
+ if (R.i == 0) {
+ res = V[F0];
+ } else {
+ split_f0();
+ if (R.i > nfields)
+ fsrealloc(R.i);
+
+ res = &Fields[R.i-1];
+ }
+ break;
+
+ /* concatenation (" ") and index joining (",") */
+ case XC( OC_CONCAT ):
+ case XC( OC_COMMA ):
+ opn = strlen(L.s) + strlen(R.s) + 2;
+ X.s = xmalloc(opn);
+ strcpy(X.s, L.s);
+ if ((opinfo & OPCLSMASK) == OC_COMMA) {
+ L.s = getvar_s(V[SUBSEP]);
+ X.s = xrealloc(X.s, opn + strlen(L.s));
+ strcat(X.s, L.s);
+ }
+ strcat(X.s, R.s);
+ setvar_p(res, X.s);
+ break;
+
+ case XC( OC_LAND ):
+ setvar_i(res, istrue(L.v) ? ptest(op->r.n) : 0);
+ break;
+
+ case XC( OC_LOR ):
+ setvar_i(res, istrue(L.v) ? 1 : ptest(op->r.n));
+ break;
+
+ case XC( OC_BINARY ):
+ case XC( OC_REPLACE ):
+ R.d = getvar_i(R.v);
+ switch (opn) {
+ case '+':
+ L.d += R.d;
+ break;
+ case '-':
+ L.d -= R.d;
+ break;
+ case '*':
+ L.d *= R.d;
+ break;
+ case '/':
+ if (R.d == 0) runtime_error(EMSG_DIV_BY_ZERO);
+ L.d /= R.d;
+ break;
+ case '&':
+#if ENABLE_FEATURE_AWK_MATH
+ L.d = pow(L.d, R.d);
+#else
+ runtime_error(EMSG_NO_MATH);
+#endif
+ break;
+ case '%':
+ if (R.d == 0) runtime_error(EMSG_DIV_BY_ZERO);
+ L.d -= (int)(L.d / R.d) * R.d;
+ break;
+ }
+ res = setvar_i(((opinfo&OPCLSMASK) == OC_BINARY) ? res : X.v, L.d);
+ break;
+
+ case XC( OC_COMPARE ):
+ if (is_numeric(L.v) && is_numeric(R.v)) {
+ L.d = getvar_i(L.v) - getvar_i(R.v);
+ } else {
+ L.s = getvar_s(L.v);
+ R.s = getvar_s(R.v);
+ L.d = icase ? strcasecmp(L.s, R.s) : strcmp(L.s, R.s);
+ }
+ switch (opn & 0xfe) {
+ case 0:
+ R.i = (L.d > 0);
+ break;
+ case 2:
+ R.i = (L.d >= 0);
+ break;
+ case 4:
+ R.i = (L.d == 0);
+ break;
+ }
+ setvar_i(res, (opn & 0x1 ? R.i : !R.i) ? 1 : 0);
+ break;
+
+ default:
+ runtime_error(EMSG_POSSIBLE_ERROR);
+ }
+ if ((opinfo & OPCLSMASK) <= SHIFT_TIL_THIS)
+ op = op->a.n;
+ if ((opinfo & OPCLSMASK) >= RECUR_FROM_THIS)
+ break;
+ if (nextrec)
+ break;
+ }
+ nvfree(v1);
+ return res;
+}
+
+
+/* -------- main & co. -------- */
+
+static int awk_exit(int r)
+{
+ var tv;
+ unsigned i;
+ hash_item *hi;
+
+ zero_out_var(&tv);
+
+ if (!exiting) {
+ exiting = TRUE;
+ nextrec = FALSE;
+ evaluate(endseq.first, &tv);
+ }
+
+ /* waiting for children */
+ for (i = 0; i < fdhash->csize; i++) {
+ hi = fdhash->items[i];
+ while (hi) {
+ if (hi->data.rs.F && hi->data.rs.is_pipe)
+ pclose(hi->data.rs.F);
+ hi = hi->next;
+ }
+ }
+
+ exit(r);
+}
+
+/* if expr looks like "var=value", perform assignment and return 1,
+ * otherwise return 0 */
+static int is_assignment(const char *expr)
+{
+ char *exprc, *s, *s0, *s1;
+
+ exprc = xstrdup(expr);
+ if (!isalnum_(*exprc) || (s = strchr(exprc, '=')) == NULL) {
+ free(exprc);
+ return FALSE;
+ }
+
+ *(s++) = '\0';
+ s0 = s1 = s;
+ while (*s)
+ *(s1++) = nextchar(&s);
+
+ *s1 = '\0';
+ setvar_u(newvar(exprc), s0);
+ free(exprc);
+ return TRUE;
+}
+
+/* switch to next input file */
+static rstream *next_input_file(void)
+{
+ static rstream rsm;
+ FILE *F = NULL;
+ const char *fname, *ind;
+ static int files_happen = FALSE;
+
+ if (rsm.F) fclose(rsm.F);
+ rsm.F = NULL;
+ rsm.pos = rsm.adv = 0;
+
+ do {
+ if (getvar_i(V[ARGIND])+1 >= getvar_i(V[ARGC])) {
+ if (files_happen)
+ return NULL;
+ fname = "-";
+ F = stdin;
+ } else {
+ ind = getvar_s(incvar(V[ARGIND]));
+ fname = getvar_s(findvar(iamarray(V[ARGV]), ind));
+ if (fname && *fname && !is_assignment(fname))
+ F = afopen(fname, "r");
+ }
+ } while (!F);
+
+ files_happen = TRUE;
+ setvar_s(V[FILENAME], fname);
+ rsm.F = F;
+ return &rsm;
+}
+
+int awk_main(int argc, char **argv);
+int awk_main(int argc, char **argv)
+{
+ unsigned opt;
+ char *opt_F, *opt_W;
+ llist_t *opt_v = NULL;
+ int i, j, flen;
+ var *v;
+ var tv;
+ char **envp;
+ char *vnames = (char *)vNames; /* cheat */
+ char *vvalues = (char *)vValues;
+
+ /* Undo busybox.c, or else strtod may eat ','! This breaks parsing:
+ * $1,$2 == '$1,' '$2', NOT '$1' ',' '$2' */
+ if (ENABLE_LOCALE_SUPPORT)
+ setlocale(LC_NUMERIC, "C");
+
+ zero_out_var(&tv);
+
+ /* allocate global buffer */
+ buf = xmalloc(MAXVARFMT + 1);
+
+ vhash = hash_init();
+ ahash = hash_init();
+ fdhash = hash_init();
+ fnhash = hash_init();
+
+ /* initialize variables */
+ for (i = 0; *vnames; i++) {
+ V[i] = v = newvar(nextword(&vnames));
+ if (*vvalues != '\377')
+ setvar_s(v, nextword(&vvalues));
+ else
+ setvar_i(v, 0);
+
+ if (*vnames == '*') {
+ v->type |= VF_SPECIAL;
+ vnames++;
+ }
+ }
+
+ handle_special(V[FS]);
+ handle_special(V[RS]);
+
+ newfile("/dev/stdin")->F = stdin;
+ newfile("/dev/stdout")->F = stdout;
+ newfile("/dev/stderr")->F = stderr;
+
+ for (envp = environ; *envp; envp++) {
+ char *s = xstrdup(*envp);
+ char *s1 = strchr(s, '=');
+ if (s1) {
+ *s1++ = '\0';
+ setvar_u(findvar(iamarray(V[ENVIRON]), s), s1);
+ }
+ free(s);
+ }
+ opt_complementary = "v::";
+ opt = getopt32(argc, argv, "F:v:f:W:", &opt_F, &opt_v, &programname, &opt_W);
+ argv += optind;
+ argc -= optind;
+ if (opt & 0x1) setvar_s(V[FS], opt_F); // -F
+ opt_v = llist_rev(opt_v);
+ while (opt_v) { /* -v */
+ if (!is_assignment(llist_pop(&opt_v)))
+ bb_show_usage();
+ }
+ if (opt & 0x4) { // -f
+ char *s = s; /* die, gcc, die */
+ FILE *from_file = afopen(programname, "r");
+ /* one byte is reserved for some trick in next_token */
+ if (fseek(from_file, 0, SEEK_END) == 0) {
+ flen = ftell(from_file);
+ s = xmalloc(flen + 4);
+ fseek(from_file, 0, SEEK_SET);
+ i = 1 + fread(s + 1, 1, flen, from_file);
+ } else {
+ for (i = j = 1; j > 0; i += j) {
+ s = xrealloc(s, i + 4096);
+ j = fread(s + i, 1, 4094, from_file);
+ }
+ }
+ s[i] = '\0';
+ fclose(from_file);
+ parse_program(s + 1);
+ free(s);
+ } else { // no -f: take program from 1st parameter
+ if (!argc)
+ bb_show_usage();
+ programname = "cmd. line";
+ parse_program(*argv++);
+ argc--;
+ }
+ if (opt & 0x8) // -W
+ bb_error_msg("warning: unrecognized option '-W %s' ignored", opt_W);
+
+ /* fill in ARGV array */
+ setvar_i(V[ARGC], argc + 1);
+ setari_u(V[ARGV], 0, "awk");
+ i = 0;
+ while (*argv)
+ setari_u(V[ARGV], ++i, *argv++);
+
+ evaluate(beginseq.first, &tv);
+ if (!mainseq.first && !endseq.first)
+ awk_exit(EXIT_SUCCESS);
+
+ /* input file could already be opened in BEGIN block */
+ if (!iF) iF = next_input_file();
+
+ /* passing through input files */
+ while (iF) {
+ nextfile = FALSE;
+ setvar_i(V[FNR], 0);
+
+ while ((i = awk_getline(iF, V[F0])) > 0) {
+ nextrec = FALSE;
+ incvar(V[NR]);
+ incvar(V[FNR]);
+ evaluate(mainseq.first, &tv);
+
+ if (nextfile)
+ break;
+ }
+
+ if (i < 0)
+ runtime_error(strerror(errno));
+
+ iF = next_input_file();
+ }
+
+ awk_exit(EXIT_SUCCESS);
+ /*return 0;*/
+}
diff --git a/i/pc104/initrd/conf/busybox/editors/ed.c b/i/pc104/initrd/conf/busybox/editors/ed.c
new file mode 100644
index 0000000..42adca4
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/editors/ed.c
@@ -0,0 +1,1232 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Copyright (c) 2002 by David I. Bell
+ * Permission is granted to use, distribute, or modify this source,
+ * provided that this copyright notice remains intact.
+ *
+ * The "ed" built-in command (much simplified)
+ */
+
+#include "busybox.h"
+
+#define USERSIZE 1024 /* max line length typed in by user */
+#define INITBUF_SIZE 1024 /* initial buffer size */
+typedef struct LINE {
+ struct LINE *next;
+ struct LINE *prev;
+ int len;
+ char data[1];
+} LINE;
+
+static LINE lines, *curLine;
+static int curNum, lastNum, marks[26], dirty;
+static char *bufBase, *bufPtr, *fileName, searchString[USERSIZE];
+static int bufUsed, bufSize;
+
+static void doCommands(void);
+static void subCommand(const char *cmd, int num1, int num2);
+static int getNum(const char **retcp, int *retHaveNum, int *retNum);
+static int setCurNum(int num);
+static int initEdit(void);
+static void termEdit(void);
+static void addLines(int num);
+static int insertLine(int num, const char *data, int len);
+static int deleteLines(int num1, int num2);
+static int printLines(int num1, int num2, int expandFlag);
+static int writeLines(const char *file, int num1, int num2);
+static int readLines(const char *file, int num);
+static int searchLines(const char *str, int num1, int num2);
+static LINE *findLine(int num);
+
+static int findString(const LINE *lp, const char * str, int len, int offset);
+
+int ed_main(int argc, char **argv);
+int ed_main(int argc, char **argv)
+{
+ if (!initEdit())
+ return EXIT_FAILURE;
+
+ if (argc > 1) {
+ fileName = strdup(argv[1]);
+
+ if (fileName == NULL) {
+ bb_error_msg("no memory");
+ termEdit();
+ return EXIT_SUCCESS;
+ }
+
+ if (!readLines(fileName, 1)) {
+ termEdit();
+ return EXIT_SUCCESS;
+ }
+
+ if (lastNum)
+ setCurNum(1);
+
+ dirty = FALSE;
+ }
+
+ doCommands();
+
+ termEdit();
+ return EXIT_SUCCESS;
+}
+
+/*
+ * Read commands until we are told to stop.
+ */
+static void doCommands(void)
+{
+ const char *cp;
+ char *endbuf, *newname, buf[USERSIZE];
+ int len, num1, num2, have1, have2;
+
+ while (TRUE) {
+ printf(": ");
+ fflush(stdout);
+
+ if (fgets(buf, sizeof(buf), stdin) == NULL)
+ return;
+
+ len = strlen(buf);
+
+ if (len == 0)
+ return;
+
+ endbuf = &buf[len - 1];
+
+ if (*endbuf != '\n') {
+ bb_error_msg("command line too long");
+
+ do {
+ len = fgetc(stdin);
+ } while ((len != EOF) && (len != '\n'));
+
+ continue;
+ }
+
+ while ((endbuf > buf) && isblank(endbuf[-1]))
+ endbuf--;
+
+ *endbuf = '\0';
+
+ cp = buf;
+
+ while (isblank(*cp))
+ cp++;
+
+ have1 = FALSE;
+ have2 = FALSE;
+
+ if ((curNum == 0) && (lastNum > 0)) {
+ curNum = 1;
+ curLine = lines.next;
+ }
+
+ if (!getNum(&cp, &have1, &num1))
+ continue;
+
+ while (isblank(*cp))
+ cp++;
+
+ if (*cp == ',') {
+ cp++;
+
+ if (!getNum(&cp, &have2, &num2))
+ continue;
+
+ if (!have1)
+ num1 = 1;
+
+ if (!have2)
+ num2 = lastNum;
+
+ have1 = TRUE;
+ have2 = TRUE;
+ }
+
+ if (!have1)
+ num1 = curNum;
+
+ if (!have2)
+ num2 = num1;
+
+ switch (*cp++) {
+ case 'a':
+ addLines(num1 + 1);
+ break;
+
+ case 'c':
+ deleteLines(num1, num2);
+ addLines(num1);
+ break;
+
+ case 'd':
+ deleteLines(num1, num2);
+ break;
+
+ case 'f':
+ if (*cp && !isblank(*cp)) {
+ bb_error_msg("bad file command");
+ break;
+ }
+
+ while (isblank(*cp))
+ cp++;
+
+ if (*cp == '\0') {
+ if (fileName)
+ printf("\"%s\"\n", fileName);
+ else
+ printf("No file name\n");
+ break;
+ }
+
+ newname = strdup(cp);
+
+ if (newname == NULL) {
+ bb_error_msg("no memory for file name");
+ break;
+ }
+
+ if (fileName)
+ free(fileName);
+
+ fileName = newname;
+ break;
+
+ case 'i':
+ addLines(num1);
+ break;
+
+ case 'k':
+ while (isblank(*cp))
+ cp++;
+
+ if ((*cp < 'a') || (*cp > 'a') || cp[1]) {
+ bb_error_msg("bad mark name");
+ break;
+ }
+
+ marks[*cp - 'a'] = num2;
+ break;
+
+ case 'l':
+ printLines(num1, num2, TRUE);
+ break;
+
+ case 'p':
+ printLines(num1, num2, FALSE);
+ break;
+
+ case 'q':
+ while (isblank(*cp))
+ cp++;
+
+ if (have1 || *cp) {
+ bb_error_msg("bad quit command");
+ break;
+ }
+
+ if (!dirty)
+ return;
+
+ printf("Really quit? ");
+ fflush(stdout);
+
+ buf[0] = '\0';
+ fgets(buf, sizeof(buf), stdin);
+ cp = buf;
+
+ while (isblank(*cp))
+ cp++;
+
+ if ((*cp == 'y') || (*cp == 'Y'))
+ return;
+
+ break;
+
+ case 'r':
+ if (*cp && !isblank(*cp)) {
+ bb_error_msg("bad read command");
+ break;
+ }
+
+ while (isblank(*cp))
+ cp++;
+
+ if (*cp == '\0') {
+ bb_error_msg("no file name");
+ break;
+ }
+
+ if (!have1)
+ num1 = lastNum;
+
+ if (readLines(cp, num1 + 1))
+ break;
+
+ if (fileName == NULL)
+ fileName = strdup(cp);
+
+ break;
+
+ case 's':
+ subCommand(cp, num1, num2);
+ break;
+
+ case 'w':
+ if (*cp && !isblank(*cp)) {
+ bb_error_msg("bad write command");
+ break;
+ }
+
+ while (isblank(*cp))
+ cp++;
+
+ if (!have1) {
+ num1 = 1;
+ num2 = lastNum;
+ }
+
+ if (*cp == '\0')
+ cp = fileName;
+
+ if (cp == NULL) {
+ bb_error_msg("no file name specified");
+ break;
+ }
+
+ writeLines(cp, num1, num2);
+ break;
+
+ case 'z':
+ switch (*cp) {
+ case '-':
+ printLines(curNum-21, curNum, FALSE);
+ break;
+ case '.':
+ printLines(curNum-11, curNum+10, FALSE);
+ break;
+ default:
+ printLines(curNum, curNum+21, FALSE);
+ break;
+ }
+ break;
+
+ case '.':
+ if (have1) {
+ bb_error_msg("no arguments allowed");
+ break;
+ }
+
+ printLines(curNum, curNum, FALSE);
+ break;
+
+ case '-':
+ if (setCurNum(curNum - 1))
+ printLines(curNum, curNum, FALSE);
+
+ break;
+
+ case '=':
+ printf("%d\n", num1);
+ break;
+
+ case '\0':
+ if (have1) {
+ printLines(num2, num2, FALSE);
+ break;
+ }
+
+ if (setCurNum(curNum + 1))
+ printLines(curNum, curNum, FALSE);
+
+ break;
+
+ default:
+ bb_error_msg("unimplemented command");
+ break;
+ }
+ }
+}
+
+
+/*
+ * Do the substitute command.
+ * The current line is set to the last substitution done.
+ */
+static void subCommand(const char * cmd, int num1, int num2)
+{
+ char *cp, *oldStr, *newStr, buf[USERSIZE];
+ int delim, oldLen, newLen, deltaLen, offset;
+ LINE *lp, *nlp;
+ int globalFlag, printFlag, didSub, needPrint;
+
+ if ((num1 < 1) || (num2 > lastNum) || (num1 > num2)) {
+ bb_error_msg("bad line range for substitute");
+ return;
+ }
+
+ globalFlag = FALSE;
+ printFlag = FALSE;
+ didSub = FALSE;
+ needPrint = FALSE;
+
+ /*
+ * Copy the command so we can modify it.
+ */
+ strcpy(buf, cmd);
+ cp = buf;
+
+ if (isblank(*cp) || (*cp == '\0')) {
+ bb_error_msg("bad delimiter for substitute");
+ return;
+ }
+
+ delim = *cp++;
+ oldStr = cp;
+
+ cp = strchr(cp, delim);
+
+ if (cp == NULL) {
+ bb_error_msg("missing 2nd delimiter for substitute");
+ return;
+ }
+
+ *cp++ = '\0';
+
+ newStr = cp;
+ cp = strchr(cp, delim);
+
+ if (cp)
+ *cp++ = '\0';
+ else
+ cp = (char*)"";
+
+ while (*cp) switch (*cp++) {
+ case 'g':
+ globalFlag = TRUE;
+ break;
+
+ case 'p':
+ printFlag = TRUE;
+ break;
+
+ default:
+ bb_error_msg("unknown option for substitute");
+ return;
+ }
+
+ if (*oldStr == '\0') {
+ if (searchString[0] == '\0') {
+ bb_error_msg("no previous search string");
+ return;
+ }
+
+ oldStr = searchString;
+ }
+
+ if (oldStr != searchString)
+ strcpy(searchString, oldStr);
+
+ lp = findLine(num1);
+
+ if (lp == NULL)
+ return;
+
+ oldLen = strlen(oldStr);
+ newLen = strlen(newStr);
+ deltaLen = newLen - oldLen;
+ offset = 0;
+ nlp = NULL;
+
+ while (num1 <= num2) {
+ offset = findString(lp, oldStr, oldLen, offset);
+
+ if (offset < 0) {
+ if (needPrint) {
+ printLines(num1, num1, FALSE);
+ needPrint = FALSE;
+ }
+
+ offset = 0;
+ lp = lp->next;
+ num1++;
+
+ continue;
+ }
+
+ needPrint = printFlag;
+ didSub = TRUE;
+ dirty = TRUE;
+
+ /*
+ * If the replacement string is the same size or shorter
+ * than the old string, then the substitution is easy.
+ */
+ if (deltaLen <= 0) {
+ memcpy(&lp->data[offset], newStr, newLen);
+
+ if (deltaLen) {
+ memcpy(&lp->data[offset + newLen],
+ &lp->data[offset + oldLen],
+ lp->len - offset - oldLen);
+
+ lp->len += deltaLen;
+ }
+
+ offset += newLen;
+
+ if (globalFlag)
+ continue;
+
+ if (needPrint) {
+ printLines(num1, num1, FALSE);
+ needPrint = FALSE;
+ }
+
+ lp = lp->next;
+ num1++;
+
+ continue;
+ }
+
+ /*
+ * The new string is larger, so allocate a new line
+ * structure and use that. Link it in in place of
+ * the old line structure.
+ */
+ nlp = (LINE *) malloc(sizeof(LINE) + lp->len + deltaLen);
+
+ if (nlp == NULL) {
+ bb_error_msg("cannot get memory for line");
+ return;
+ }
+
+ nlp->len = lp->len + deltaLen;
+
+ memcpy(nlp->data, lp->data, offset);
+
+ memcpy(&nlp->data[offset], newStr, newLen);
+
+ memcpy(&nlp->data[offset + newLen],
+ &lp->data[offset + oldLen],
+ lp->len - offset - oldLen);
+
+ nlp->next = lp->next;
+ nlp->prev = lp->prev;
+ nlp->prev->next = nlp;
+ nlp->next->prev = nlp;
+
+ if (curLine == lp)
+ curLine = nlp;
+
+ free(lp);
+ lp = nlp;
+
+ offset += newLen;
+
+ if (globalFlag)
+ continue;
+
+ if (needPrint) {
+ printLines(num1, num1, FALSE);
+ needPrint = FALSE;
+ }
+
+ lp = lp->next;
+ num1++;
+ }
+
+ if (!didSub)
+ bb_error_msg("no substitutions found for \"%s\"", oldStr);
+}
+
+
+/*
+ * Search a line for the specified string starting at the specified
+ * offset in the line. Returns the offset of the found string, or -1.
+ */
+static int findString( const LINE * lp, const char * str, int len, int offset)
+{
+ int left;
+ const char *cp, *ncp;
+
+ cp = &lp->data[offset];
+ left = lp->len - offset;
+
+ while (left >= len) {
+ ncp = memchr(cp, *str, left);
+
+ if (ncp == NULL)
+ return -1;
+
+ left -= (ncp - cp);
+
+ if (left < len)
+ return -1;
+
+ cp = ncp;
+
+ if (memcmp(cp, str, len) == 0)
+ return (cp - lp->data);
+
+ cp++;
+ left--;
+ }
+
+ return -1;
+}
+
+
+/*
+ * Add lines which are typed in by the user.
+ * The lines are inserted just before the specified line number.
+ * The lines are terminated by a line containing a single dot (ugly!),
+ * or by an end of file.
+ */
+static void addLines(int num)
+{
+ int len;
+ char buf[USERSIZE + 1];
+
+ while (fgets(buf, sizeof(buf), stdin)) {
+ if ((buf[0] == '.') && (buf[1] == '\n') && (buf[2] == '\0'))
+ return;
+
+ len = strlen(buf);
+
+ if (len == 0)
+ return;
+
+ if (buf[len - 1] != '\n') {
+ bb_error_msg("line too long");
+ do {
+ len = fgetc(stdin);
+ } while ((len != EOF) && (len != '\n'));
+ return;
+ }
+
+ if (!insertLine(num++, buf, len))
+ return;
+ }
+}
+
+
+/*
+ * Parse a line number argument if it is present. This is a sum
+ * or difference of numbers, '.', '$', 'x, or a search string.
+ * Returns TRUE if successful (whether or not there was a number).
+ * Returns FALSE if there was a parsing error, with a message output.
+ * Whether there was a number is returned indirectly, as is the number.
+ * The character pointer which stopped the scan is also returned.
+ */
+static int getNum(const char **retcp, int *retHaveNum, int *retNum)
+{
+ const char *cp;
+ char *endStr, str[USERSIZE];
+ int haveNum, value, num, sign;
+
+ cp = *retcp;
+ haveNum = FALSE;
+ value = 0;
+ sign = 1;
+
+ while (TRUE) {
+ while (isblank(*cp))
+ cp++;
+
+ switch (*cp) {
+ case '.':
+ haveNum = TRUE;
+ num = curNum;
+ cp++;
+ break;
+
+ case '$':
+ haveNum = TRUE;
+ num = lastNum;
+ cp++;
+ break;
+
+ case '\'':
+ cp++;
+
+ if ((*cp < 'a') || (*cp > 'z')) {
+ bb_error_msg("bad mark name");
+ return FALSE;
+ }
+
+ haveNum = TRUE;
+ num = marks[*cp++ - 'a'];
+ break;
+
+ case '/':
+ strcpy(str, ++cp);
+ endStr = strchr(str, '/');
+
+ if (endStr) {
+ *endStr++ = '\0';
+ cp += (endStr - str);
+ }
+ else
+ cp = "";
+
+ num = searchLines(str, curNum, lastNum);
+
+ if (num == 0)
+ return FALSE;
+
+ haveNum = TRUE;
+ break;
+
+ default:
+ if (!isdigit(*cp)) {
+ *retcp = cp;
+ *retHaveNum = haveNum;
+ *retNum = value;
+ return TRUE;
+ }
+
+ num = 0;
+
+ while (isdigit(*cp))
+ num = num * 10 + *cp++ - '0';
+
+ haveNum = TRUE;
+ break;
+ }
+
+ value += num * sign;
+
+ while (isblank(*cp))
+ cp++;
+
+ switch (*cp) {
+ case '-':
+ sign = -1;
+ cp++;
+ break;
+
+ case '+':
+ sign = 1;
+ cp++;
+ break;
+
+ default:
+ *retcp = cp;
+ *retHaveNum = haveNum;
+ *retNum = value;
+ return TRUE;
+ }
+ }
+}
+
+
+/*
+ * Initialize everything for editing.
+ */
+static int initEdit(void)
+{
+ int i;
+
+ bufSize = INITBUF_SIZE;
+ bufBase = malloc(bufSize);
+
+ if (bufBase == NULL) {
+ bb_error_msg("no memory for buffer");
+ return FALSE;
+ }
+
+ bufPtr = bufBase;
+ bufUsed = 0;
+
+ lines.next = &lines;
+ lines.prev = &lines;
+
+ curLine = NULL;
+ curNum = 0;
+ lastNum = 0;
+ dirty = FALSE;
+ fileName = NULL;
+ searchString[0] = '\0';
+
+ for (i = 0; i < 26; i++)
+ marks[i] = 0;
+
+ return TRUE;
+}
+
+
+/*
+ * Finish editing.
+ */
+static void termEdit(void)
+{
+ if (bufBase)
+ free(bufBase);
+
+ bufBase = NULL;
+ bufPtr = NULL;
+ bufSize = 0;
+ bufUsed = 0;
+
+ if (fileName)
+ free(fileName);
+
+ fileName = NULL;
+
+ searchString[0] = '\0';
+
+ if (lastNum)
+ deleteLines(1, lastNum);
+
+ lastNum = 0;
+ curNum = 0;
+ curLine = NULL;
+}
+
+
+/*
+ * Read lines from a file at the specified line number.
+ * Returns TRUE if the file was successfully read.
+ */
+static int readLines(const char * file, int num)
+{
+ int fd, cc;
+ int len, lineCount, charCount;
+ char *cp;
+
+ if ((num < 1) || (num > lastNum + 1)) {
+ bb_error_msg("bad line for read");
+ return FALSE;
+ }
+
+ fd = open(file, 0);
+
+ if (fd < 0) {
+ perror(file);
+ return FALSE;
+ }
+
+ bufPtr = bufBase;
+ bufUsed = 0;
+ lineCount = 0;
+ charCount = 0;
+ cc = 0;
+
+ printf("\"%s\", ", file);
+ fflush(stdout);
+
+ do {
+ cp = memchr(bufPtr, '\n', bufUsed);
+
+ if (cp) {
+ len = (cp - bufPtr) + 1;
+
+ if (!insertLine(num, bufPtr, len)) {
+ close(fd);
+ return FALSE;
+ }
+
+ bufPtr += len;
+ bufUsed -= len;
+ charCount += len;
+ lineCount++;
+ num++;
+
+ continue;
+ }
+
+ if (bufPtr != bufBase) {
+ memcpy(bufBase, bufPtr, bufUsed);
+ bufPtr = bufBase + bufUsed;
+ }
+
+ if (bufUsed >= bufSize) {
+ len = (bufSize * 3) / 2;
+ cp = realloc(bufBase, len);
+
+ if (cp == NULL) {
+ bb_error_msg("no memory for buffer");
+ close(fd);
+ return FALSE;
+ }
+
+ bufBase = cp;
+ bufPtr = bufBase + bufUsed;
+ bufSize = len;
+ }
+
+ cc = read(fd, bufPtr, bufSize - bufUsed);
+ bufUsed += cc;
+ bufPtr = bufBase;
+
+ } while (cc > 0);
+
+ if (cc < 0) {
+ perror(file);
+ close(fd);
+ return FALSE;
+ }
+
+ if (bufUsed) {
+ if (!insertLine(num, bufPtr, bufUsed)) {
+ close(fd);
+ return -1;
+ }
+
+ lineCount++;
+ charCount += bufUsed;
+ }
+
+ close(fd);
+
+ printf("%d lines%s, %d chars\n", lineCount,
+ (bufUsed ? " (incomplete)" : ""), charCount);
+
+ return TRUE;
+}
+
+
+/*
+ * Write the specified lines out to the specified file.
+ * Returns TRUE if successful, or FALSE on an error with a message output.
+ */
+static int writeLines(const char * file, int num1, int num2)
+{
+ LINE *lp;
+ int fd, lineCount, charCount;
+
+ if ((num1 < 1) || (num2 > lastNum) || (num1 > num2)) {
+ bb_error_msg("bad line range for write");
+ return FALSE;
+ }
+
+ lineCount = 0;
+ charCount = 0;
+
+ fd = creat(file, 0666);
+
+ if (fd < 0) {
+ perror(file);
+ return FALSE;
+ }
+
+ printf("\"%s\", ", file);
+ fflush(stdout);
+
+ lp = findLine(num1);
+
+ if (lp == NULL) {
+ close(fd);
+ return FALSE;
+ }
+
+ while (num1++ <= num2) {
+ if (write(fd, lp->data, lp->len) != lp->len) {
+ perror(file);
+ close(fd);
+ return FALSE;
+ }
+
+ charCount += lp->len;
+ lineCount++;
+ lp = lp->next;
+ }
+
+ if (close(fd) < 0) {
+ perror(file);
+ return FALSE;
+ }
+
+ printf("%d lines, %d chars\n", lineCount, charCount);
+ return TRUE;
+}
+
+
+/*
+ * Print lines in a specified range.
+ * The last line printed becomes the current line.
+ * If expandFlag is TRUE, then the line is printed specially to
+ * show magic characters.
+ */
+static int printLines(int num1, int num2, int expandFlag)
+{
+ const LINE *lp;
+ const char *cp;
+ int ch, count;
+
+ if ((num1 < 1) || (num2 > lastNum) || (num1 > num2)) {
+ bb_error_msg("bad line range for print");
+ return FALSE;
+ }
+
+ lp = findLine(num1);
+
+ if (lp == NULL)
+ return FALSE;
+
+ while (num1 <= num2) {
+ if (!expandFlag) {
+ write(1, lp->data, lp->len);
+ setCurNum(num1++);
+ lp = lp->next;
+
+ continue;
+ }
+
+ /*
+ * Show control characters and characters with the
+ * high bit set specially.
+ */
+ cp = lp->data;
+ count = lp->len;
+
+ if ((count > 0) && (cp[count - 1] == '\n'))
+ count--;
+
+ while (count-- > 0) {
+ ch = *cp++;
+
+ if (ch & 0x80) {
+ fputs("M-", stdout);
+ ch &= 0x7f;
+ }
+
+ if (ch < ' ') {
+ fputc('^', stdout);
+ ch += '@';
+ }
+
+ if (ch == 0x7f) {
+ fputc('^', stdout);
+ ch = '?';
+ }
+
+ fputc(ch, stdout);
+ }
+
+ fputs("$\n", stdout);
+
+ setCurNum(num1++);
+ lp = lp->next;
+ }
+
+ return TRUE;
+}
+
+
+/*
+ * Insert a new line with the specified text.
+ * The line is inserted so as to become the specified line,
+ * thus pushing any existing and further lines down one.
+ * The inserted line is also set to become the current line.
+ * Returns TRUE if successful.
+ */
+static int insertLine(int num, const char * data, int len)
+{
+ LINE *newLp, *lp;
+
+ if ((num < 1) || (num > lastNum + 1)) {
+ bb_error_msg("inserting at bad line number");
+ return FALSE;
+ }
+
+ newLp = malloc(sizeof(LINE) + len - 1);
+
+ if (newLp == NULL) {
+ bb_error_msg("failed to allocate memory for line");
+ return FALSE;
+ }
+
+ memcpy(newLp->data, data, len);
+ newLp->len = len;
+
+ if (num > lastNum)
+ lp = &lines;
+ else {
+ lp = findLine(num);
+
+ if (lp == NULL) {
+ free((char *) newLp);
+ return FALSE;
+ }
+ }
+
+ newLp->next = lp;
+ newLp->prev = lp->prev;
+ lp->prev->next = newLp;
+ lp->prev = newLp;
+
+ lastNum++;
+ dirty = TRUE;
+ return setCurNum(num);
+}
+
+
+/*
+ * Delete lines from the given range.
+ */
+static int deleteLines(int num1, int num2)
+{
+ LINE *lp, *nlp, *plp;
+ int count;
+
+ if ((num1 < 1) || (num2 > lastNum) || (num1 > num2)) {
+ bb_error_msg("bad line numbers for delete");
+ return FALSE;
+ }
+
+ lp = findLine(num1);
+
+ if (lp == NULL)
+ return FALSE;
+
+ if ((curNum >= num1) && (curNum <= num2)) {
+ if (num2 < lastNum)
+ setCurNum(num2 + 1);
+ else if (num1 > 1)
+ setCurNum(num1 - 1);
+ else
+ curNum = 0;
+ }
+
+ count = num2 - num1 + 1;
+
+ if (curNum > num2)
+ curNum -= count;
+
+ lastNum -= count;
+
+ while (count-- > 0) {
+ nlp = lp->next;
+ plp = lp->prev;
+ plp->next = nlp;
+ nlp->prev = plp;
+ lp->next = NULL;
+ lp->prev = NULL;
+ lp->len = 0;
+ free(lp);
+ lp = nlp;
+ }
+
+ dirty = TRUE;
+
+ return TRUE;
+}
+
+
+/*
+ * Search for a line which contains the specified string.
+ * If the string is NULL, then the previously searched for string
+ * is used. The currently searched for string is saved for future use.
+ * Returns the line number which matches, or 0 if there was no match
+ * with an error printed.
+ */
+static int searchLines(const char *str, int num1, int num2)
+{
+ const LINE *lp;
+ int len;
+
+ if ((num1 < 1) || (num2 > lastNum) || (num1 > num2)) {
+ bb_error_msg("bad line numbers for search");
+ return 0;
+ }
+
+ if (*str == '\0') {
+ if (searchString[0] == '\0') {
+ bb_error_msg("no previous search string");
+ return 0;
+ }
+
+ str = searchString;
+ }
+
+ if (str != searchString)
+ strcpy(searchString, str);
+
+ len = strlen(str);
+
+ lp = findLine(num1);
+
+ if (lp == NULL)
+ return 0;
+
+ while (num1 <= num2) {
+ if (findString(lp, str, len, 0) >= 0)
+ return num1;
+
+ num1++;
+ lp = lp->next;
+ }
+
+ bb_error_msg("cannot find string \"%s\"", str);
+ return 0;
+}
+
+
+/*
+ * Return a pointer to the specified line number.
+ */
+static LINE *findLine(int num)
+{
+ LINE *lp;
+ int lnum;
+
+ if ((num < 1) || (num > lastNum)) {
+ bb_error_msg("line number %d does not exist", num);
+ return NULL;
+ }
+
+ if (curNum <= 0) {
+ curNum = 1;
+ curLine = lines.next;
+ }
+
+ if (num == curNum)
+ return curLine;
+
+ lp = curLine;
+ lnum = curNum;
+
+ if (num < (curNum / 2)) {
+ lp = lines.next;
+ lnum = 1;
+ }
+ else if (num > ((curNum + lastNum) / 2)) {
+ lp = lines.prev;
+ lnum = lastNum;
+ }
+
+ while (lnum < num) {
+ lp = lp->next;
+ lnum++;
+ }
+
+ while (lnum > num) {
+ lp = lp->prev;
+ lnum--;
+ }
+ return lp;
+}
+
+
+/*
+ * Set the current line number.
+ * Returns TRUE if successful.
+ */
+static int setCurNum(int num)
+{
+ LINE *lp;
+
+ lp = findLine(num);
+
+ if (lp == NULL)
+ return FALSE;
+
+ curNum = num;
+ curLine = lp;
+ return TRUE;
+}
diff --git a/i/pc104/initrd/conf/busybox/editors/patch.c b/i/pc104/initrd/conf/busybox/editors/patch.c
new file mode 100644
index 0000000..2c90804
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/editors/patch.c
@@ -0,0 +1,278 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * busybox patch applet to handle the unified diff format.
+ * Copyright (C) 2003 Glenn McGrath <bug1@iinet.net.au>
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ *
+ * This applet is written to work with patches generated by GNU diff,
+ * where there is equivalent functionality busybox patch shall behave
+ * as per GNU patch.
+ *
+ * There is a SUSv3 specification for patch, however it looks to be
+ * incomplete, it doesnt even mention unified diff format.
+ * http://www.opengroup.org/onlinepubs/007904975/utilities/patch.html
+ *
+ * Issues
+ * - Non-interactive
+ * - Patches must apply cleanly or patch (not just one hunk) will fail.
+ * - Reject file isnt saved
+ */
+
+#include <getopt.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "busybox.h"
+
+static unsigned int copy_lines(FILE *src_stream, FILE *dest_stream, const unsigned int lines_count)
+{
+ unsigned int i = 0;
+
+ while (src_stream && (i < lines_count)) {
+ char *line;
+ line = xmalloc_fgets(src_stream);
+ if (line == NULL) {
+ break;
+ }
+ if (fputs(line, dest_stream) == EOF) {
+ bb_perror_msg_and_die("error writing to new file");
+ }
+ free(line);
+
+ i++;
+ }
+ return i;
+}
+
+/* If patch_level is -1 it will remove all directory names
+ * char *line must be greater than 4 chars
+ * returns NULL if the file doesnt exist or error
+ * returns malloc'ed filename
+ */
+
+static char *extract_filename(char *line, int patch_level)
+{
+ char *temp, *filename_start_ptr = line + 4;
+ int i;
+
+ /* Terminate string at end of source filename */
+ temp = strchr(filename_start_ptr, '\t');
+ if (temp) *temp = 0;
+
+ /* skip over (patch_level) number of leading directories */
+ for (i = 0; i < patch_level; i++) {
+ if(!(temp = strchr(filename_start_ptr, '/'))) break;
+ filename_start_ptr = temp + 1;
+ }
+
+ return xstrdup(filename_start_ptr);
+}
+
+static int file_doesnt_exist(const char *filename)
+{
+ struct stat statbuf;
+ return stat(filename, &statbuf);
+}
+
+int patch_main(int argc, char **argv);
+int patch_main(int argc, char **argv)
+{
+ int patch_level = -1;
+ char *patch_line;
+ int ret;
+ FILE *patch_file = NULL;
+
+ {
+ char *p, *i;
+ ret = getopt32(argc, argv, "p:i:", &p, &i);
+ if (ret & 1)
+ patch_level = xatol_range(p, -1, USHRT_MAX);
+ if (ret & 2) {
+ patch_file = xfopen(i, "r");
+ } else {
+ patch_file = stdin;
+ }
+ ret = 0;
+ }
+
+ patch_line = xmalloc_fgets(patch_file);
+ while (patch_line) {
+ FILE *src_stream;
+ FILE *dst_stream;
+ char *original_filename;
+ char *new_filename;
+ char *backup_filename;
+ unsigned int src_cur_line = 1;
+ unsigned int dest_cur_line = 0;
+ unsigned int dest_beg_line;
+ unsigned int bad_hunk_count = 0;
+ unsigned int hunk_count = 0;
+ char copy_trailing_lines_flag = 0;
+
+ /* Skip everything upto the "---" marker
+ * No need to parse the lines "Only in <dir>", and "diff <args>"
+ */
+ while (patch_line && strncmp(patch_line, "--- ", 4) != 0) {
+ free(patch_line);
+ patch_line = xmalloc_fgets(patch_file);
+ }
+ /* FIXME: patch_line NULL check?? */
+
+ /* Extract the filename used before the patch was generated */
+ original_filename = extract_filename(patch_line, patch_level);
+ free(patch_line);
+
+ patch_line = xmalloc_fgets(patch_file);
+ /* FIXME: NULL check?? */
+ if (strncmp(patch_line, "+++ ", 4) != 0) {
+ ret = 2;
+ bb_error_msg("invalid patch");
+ continue;
+ }
+ new_filename = extract_filename(patch_line, patch_level);
+ free(patch_line);
+
+ if (file_doesnt_exist(new_filename)) {
+ char *line_ptr;
+ /* Create leading directories */
+ line_ptr = strrchr(new_filename, '/');
+ if (line_ptr) {
+ *line_ptr = '\0';
+ bb_make_directory(new_filename, -1, FILEUTILS_RECUR);
+ *line_ptr = '/';
+ }
+ dst_stream = xfopen(new_filename, "w+");
+ backup_filename = NULL;
+ } else {
+ backup_filename = xmalloc(strlen(new_filename) + 6);
+ strcpy(backup_filename, new_filename);
+ strcat(backup_filename, ".orig");
+ if (rename(new_filename, backup_filename) == -1) {
+ bb_perror_msg_and_die("cannot create file %s",
+ backup_filename);
+ }
+ dst_stream = xfopen(new_filename, "w");
+ }
+
+ if ((backup_filename == NULL) || file_doesnt_exist(original_filename)) {
+ src_stream = NULL;
+ } else {
+ if (strcmp(original_filename, new_filename) == 0) {
+ src_stream = xfopen(backup_filename, "r");
+ } else {
+ src_stream = xfopen(original_filename, "r");
+ }
+ }
+
+ printf("patching file %s\n", new_filename);
+
+ /* Handle each hunk */
+ patch_line = xmalloc_fgets(patch_file);
+ while (patch_line) {
+ unsigned int count;
+ unsigned int src_beg_line;
+ unsigned int unused;
+ unsigned int hunk_offset_start = 0;
+ int hunk_error = 0;
+
+ /* This bit should be improved */
+ if ((sscanf(patch_line, "@@ -%d,%d +%d,%d @@", &src_beg_line, &unused, &dest_beg_line, &unused) != 4) &&
+ (sscanf(patch_line, "@@ -%d,%d +%d @@", &src_beg_line, &unused, &dest_beg_line) != 3) &&
+ (sscanf(patch_line, "@@ -%d +%d,%d @@", &src_beg_line, &dest_beg_line, &unused) != 3)) {
+ /* No more hunks for this file */
+ break;
+ }
+ free(patch_line);
+ hunk_count++;
+
+ if (src_beg_line && dest_beg_line) {
+ /* Copy unmodified lines upto start of hunk */
+ /* src_beg_line will be 0 if its a new file */
+ count = src_beg_line - src_cur_line;
+ if (copy_lines(src_stream, dst_stream, count) != count) {
+ bb_error_msg_and_die("bad src file");
+ }
+ src_cur_line += count;
+ dest_cur_line += count;
+ copy_trailing_lines_flag = 1;
+ }
+ hunk_offset_start = src_cur_line;
+
+ while ((patch_line = xmalloc_fgets(patch_file)) != NULL) {
+ if ((*patch_line == '-') || (*patch_line == ' ')) {
+ char *src_line = NULL;
+ if (src_stream) {
+ src_line = xmalloc_fgets(src_stream);
+ if (!src_line) {
+ hunk_error++;
+ break;
+ } else {
+ src_cur_line++;
+ }
+ if (strcmp(src_line, patch_line + 1) != 0) {
+ bb_error_msg("hunk #%d FAILED at %d", hunk_count, hunk_offset_start);
+ hunk_error++;
+ free(patch_line);
+ /* Probably need to find next hunk, etc... */
+ /* but for now we just bail out */
+ patch_line = NULL;
+ break;
+ }
+ free(src_line);
+ }
+ if (*patch_line == ' ') {
+ fputs(patch_line + 1, dst_stream);
+ dest_cur_line++;
+ }
+ } else if (*patch_line == '+') {
+ fputs(patch_line + 1, dst_stream);
+ dest_cur_line++;
+ } else {
+ break;
+ }
+ free(patch_line);
+ }
+ if (hunk_error) {
+ bad_hunk_count++;
+ }
+ }
+
+ /* Cleanup last patched file */
+ if (copy_trailing_lines_flag) {
+ copy_lines(src_stream, dst_stream, -1);
+ }
+ if (src_stream) {
+ fclose(src_stream);
+ }
+ if (dst_stream) {
+ fclose(dst_stream);
+ }
+ if (bad_hunk_count) {
+ if (!ret) {
+ ret = 1;
+ }
+ bb_error_msg("%d out of %d hunk FAILED", bad_hunk_count, hunk_count);
+ } else {
+ /* It worked, we can remove the backup */
+ if (backup_filename) {
+ unlink(backup_filename);
+ }
+ if ((dest_cur_line == 0) || (dest_beg_line == 0)) {
+ /* The new patched file is empty, remove it */
+ if (unlink(new_filename) == -1) {
+ bb_perror_msg_and_die("cannot remove file %s", new_filename);
+ }
+ if (unlink(original_filename) == -1) {
+ bb_perror_msg_and_die("cannot remove original file %s", new_filename);
+ }
+ }
+ }
+ }
+
+ /* 0 = SUCCESS
+ * 1 = Some hunks failed
+ * 2 = More serious problems
+ */
+ return ret;
+}
diff --git a/i/pc104/initrd/conf/busybox/editors/sed.c b/i/pc104/initrd/conf/busybox/editors/sed.c
new file mode 100644
index 0000000..f7f22f7
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/editors/sed.c
@@ -0,0 +1,1347 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * sed.c - very minimalist version of sed
+ *
+ * Copyright (C) 1999,2000,2001 by Lineo, inc. and Mark Whitley
+ * Copyright (C) 1999,2000,2001 by Mark Whitley <markw@codepoet.org>
+ * Copyright (C) 2002 Matt Kraai
+ * Copyright (C) 2003 by Glenn McGrath <bug1@iinet.net.au>
+ * Copyright (C) 2003,2004 by Rob Landley <rob@landley.net>
+ *
+ * MAINTAINER: Rob Landley <rob@landley.net>
+ *
+ * Licensed under GPL version 2, see file LICENSE in this tarball for details.
+ */
+
+/* Code overview.
+
+ Files are laid out to avoid unnecessary function declarations. So for
+ example, every function add_cmd calls occurs before add_cmd in this file.
+
+ add_cmd() is called on each line of sed command text (from a file or from
+ the command line). It calls get_address() and parse_cmd_args(). The
+ resulting sed_cmd_t structures are appended to a linked list
+ (bbg.sed_cmd_head/bbg.sed_cmd_tail).
+
+ add_input_file() adds a FILE * to the list of input files. We need to
+ know all input sources ahead of time to find the last line for the $ match.
+
+ process_files() does actual sedding, reading data lines from each input FILE *
+ (which could be stdin) and applying the sed command list (sed_cmd_head) to
+ each of the resulting lines.
+
+ sed_main() is where external code calls into this, with a command line.
+*/
+
+
+/*
+ Supported features and commands in this version of sed:
+
+ - comments ('#')
+ - address matching: num|/matchstr/[,num|/matchstr/|$]command
+ - commands: (p)rint, (d)elete, (s)ubstitue (with g & I flags)
+ - edit commands: (a)ppend, (i)nsert, (c)hange
+ - file commands: (r)ead
+ - backreferences in substitution expressions (\0, \1, \2...\9)
+ - grouped commands: {cmd1;cmd2}
+ - transliteration (y/source-chars/dest-chars/)
+ - pattern space hold space storing / swapping (g, h, x)
+ - labels / branching (: label, b, t, T)
+
+ (Note: Specifying an address (range) to match is *optional*; commands
+ default to the whole pattern space if no specific address match was
+ requested.)
+
+ Todo:
+ - Create a wrapper around regex to make libc's regex conform with sed
+
+ Reference http://www.opengroup.org/onlinepubs/007904975/utilities/sed.html
+*/
+
+#include "busybox.h"
+#include "xregex.h"
+
+/* Each sed command turns into one of these structures. */
+typedef struct sed_cmd_s {
+ /* Ordered by alignment requirements: currently 36 bytes on x86 */
+ struct sed_cmd_s *next; /* Next command (linked list, NULL terminated) */
+
+ /* address storage */
+ regex_t *beg_match; /* sed -e '/match/cmd' */
+ regex_t *end_match; /* sed -e '/match/,/end_match/cmd' */
+ regex_t *sub_match; /* For 's/sub_match/string/' */
+ int beg_line; /* 'sed 1p' 0 == apply commands to all lines */
+ int end_line; /* 'sed 1,3p' 0 == one line only. -1 = last line ($) */
+
+ FILE *sw_file; /* File (sw) command writes to, -1 for none. */
+ char *string; /* Data string for (saicytb) commands. */
+
+ unsigned short which_match; /* (s) Which match to replace (0 for all) */
+
+ /* Bitfields (gcc won't group them if we don't) */
+ unsigned invert:1; /* the '!' after the address */
+ unsigned in_match:1; /* Next line also included in match? */
+ unsigned sub_p:1; /* (s) print option */
+
+ char sw_last_char; /* Last line written by (sw) had no '\n' */
+
+ /* GENERAL FIELDS */
+ char cmd; /* The command char: abcdDgGhHilnNpPqrstwxy:={} */
+} sed_cmd_t;
+
+static const char semicolon_whitespace[] = "; \n\r\t\v";
+
+struct sed_globals {
+ /* options */
+ int be_quiet, regex_type;
+ FILE *nonstdout;
+ char *outname, *hold_space;
+
+ /* List of input files */
+ int input_file_count, current_input_file;
+ FILE **input_file_list;
+
+ regmatch_t regmatch[10];
+ regex_t *previous_regex_ptr;
+
+ /* linked list of sed commands */
+ sed_cmd_t sed_cmd_head, *sed_cmd_tail;
+
+ /* Linked list of append lines */
+ llist_t *append_head;
+
+ char *add_cmd_line;
+
+ struct pipeline {
+ char *buf; /* Space to hold string */
+ int idx; /* Space used */
+ int len; /* Space allocated */
+ } pipeline;
+} bbg;
+
+
+#if ENABLE_FEATURE_CLEAN_UP
+static void sed_free_and_close_stuff(void)
+{
+ sed_cmd_t *sed_cmd = bbg.sed_cmd_head.next;
+
+ llist_free(bbg.append_head, free);
+
+ while (sed_cmd) {
+ sed_cmd_t *sed_cmd_next = sed_cmd->next;
+
+ if (sed_cmd->sw_file)
+ xprint_and_close_file(sed_cmd->sw_file);
+
+ if (sed_cmd->beg_match) {
+ regfree(sed_cmd->beg_match);
+ free(sed_cmd->beg_match);
+ }
+ if (sed_cmd->end_match) {
+ regfree(sed_cmd->end_match);
+ free(sed_cmd->end_match);
+ }
+ if (sed_cmd->sub_match) {
+ regfree(sed_cmd->sub_match);
+ free(sed_cmd->sub_match);
+ }
+ free(sed_cmd->string);
+ free(sed_cmd);
+ sed_cmd = sed_cmd_next;
+ }
+
+ if (bbg.hold_space) free(bbg.hold_space);
+
+ while (bbg.current_input_file < bbg.input_file_count)
+ fclose(bbg.input_file_list[bbg.current_input_file++]);
+}
+#else
+void sed_free_and_close_stuff(void);
+#endif
+
+/* If something bad happens during -i operation, delete temp file */
+
+static void cleanup_outname(void)
+{
+ if (bbg.outname) unlink(bbg.outname);
+}
+
+/* strdup, replacing "\n" with '\n', and "\delimiter" with 'delimiter' */
+
+static void parse_escapes(char *dest, const char *string, int len, char from, char to)
+{
+ int i = 0;
+
+ while (i < len) {
+ if (string[i] == '\\') {
+ if (!to || string[i+1] == from) {
+ *dest++ = to ? to : string[i+1];
+ i += 2;
+ continue;
+ }
+ *dest++ = string[i++];
+ }
+ *dest++ = string[i++];
+ }
+ *dest = 0;
+}
+
+static char *copy_parsing_escapes(const char *string, int len)
+{
+ char *dest = xmalloc(len + 1);
+
+ parse_escapes(dest, string, len, 'n', '\n');
+ return dest;
+}
+
+
+/*
+ * index_of_next_unescaped_regexp_delim - walks left to right through a string
+ * beginning at a specified index and returns the index of the next regular
+ * expression delimiter (typically a forward * slash ('/')) not preceded by
+ * a backslash ('\'). A negative delimiter disables square bracket checking.
+ */
+static int index_of_next_unescaped_regexp_delim(int delimiter, const char *str)
+{
+ int bracket = -1;
+ int escaped = 0;
+ int idx = 0;
+ char ch;
+
+ if (delimiter < 0) {
+ bracket--;
+ delimiter = -delimiter;
+ }
+
+ for (; (ch = str[idx]); idx++) {
+ if (bracket >= 0) {
+ if (ch == ']' && !(bracket == idx - 1 || (bracket == idx - 2
+ && str[idx - 1] == '^')))
+ bracket = -1;
+ } else if (escaped)
+ escaped = 0;
+ else if (ch == '\\')
+ escaped = 1;
+ else if (bracket == -1 && ch == '[')
+ bracket = idx;
+ else if (ch == delimiter)
+ return idx;
+ }
+
+ /* if we make it to here, we've hit the end of the string */
+ bb_error_msg_and_die("unmatched '%c'", delimiter);
+}
+
+/*
+ * Returns the index of the third delimiter
+ */
+static int parse_regex_delim(const char *cmdstr, char **match, char **replace)
+{
+ const char *cmdstr_ptr = cmdstr;
+ char delimiter;
+ int idx = 0;
+
+ /* verify that the 's' or 'y' is followed by something. That something
+ * (typically a 'slash') is now our regexp delimiter... */
+ if (*cmdstr == '\0')
+ bb_error_msg_and_die("bad format in substitution expression");
+ delimiter = *cmdstr_ptr++;
+
+ /* save the match string */
+ idx = index_of_next_unescaped_regexp_delim(delimiter, cmdstr_ptr);
+ *match = copy_parsing_escapes(cmdstr_ptr, idx);
+
+ /* save the replacement string */
+ cmdstr_ptr += idx + 1;
+ idx = index_of_next_unescaped_regexp_delim(-delimiter, cmdstr_ptr);
+ *replace = copy_parsing_escapes(cmdstr_ptr, idx);
+
+ return ((cmdstr_ptr - cmdstr) + idx);
+}
+
+/*
+ * returns the index in the string just past where the address ends.
+ */
+static int get_address(const char *my_str, int *linenum, regex_t ** regex)
+{
+ const char *pos = my_str;
+
+ if (isdigit(*my_str)) {
+ *linenum = strtol(my_str, (char**)&pos, 10);
+ /* endstr shouldnt ever equal NULL */
+ } else if (*my_str == '$') {
+ *linenum = -1;
+ pos++;
+ } else if (*my_str == '/' || *my_str == '\\') {
+ int next;
+ char delimiter;
+ char *temp;
+
+ delimiter = '/';
+ if (*my_str == '\\') delimiter = *++pos;
+ next = index_of_next_unescaped_regexp_delim(delimiter, ++pos);
+ temp = copy_parsing_escapes(pos, next);
+ *regex = xmalloc(sizeof(regex_t));
+ xregcomp(*regex, temp, bbg.regex_type|REG_NEWLINE);
+ free(temp);
+ /* Move position to next character after last delimiter */
+ pos += (next+1);
+ }
+ return pos - my_str;
+}
+
+/* Grab a filename. Whitespace at start is skipped, then goes to EOL. */
+static int parse_file_cmd(sed_cmd_t *sed_cmd, const char *filecmdstr, char **retval)
+{
+ int start = 0, idx, hack = 0;
+
+ /* Skip whitespace, then grab filename to end of line */
+ while (isspace(filecmdstr[start]))
+ start++;
+ idx = start;
+ while (filecmdstr[idx] && filecmdstr[idx] != '\n')
+ idx++;
+
+ /* If lines glued together, put backslash back. */
+ if (filecmdstr[idx] == '\n')
+ hack = 1;
+ if (idx == start)
+ bb_error_msg_and_die("empty filename");
+ *retval = xstrndup(filecmdstr+start, idx-start+hack+1);
+ if (hack)
+ (*retval)[idx] = '\\';
+
+ return idx;
+}
+
+static int parse_subst_cmd(sed_cmd_t *sed_cmd, const char *substr)
+{
+ int cflags = bbg.regex_type;
+ char *match;
+ int idx;
+
+ /*
+ * A substitution command should look something like this:
+ * s/match/replace/ #gIpw
+ * || | |||
+ * mandatory optional
+ */
+ idx = parse_regex_delim(substr, &match, &sed_cmd->string);
+
+ /* determine the number of back references in the match string */
+ /* Note: we compute this here rather than in the do_subst_command()
+ * function to save processor time, at the expense of a little more memory
+ * (4 bits) per sed_cmd */
+
+ /* process the flags */
+
+ sed_cmd->which_match = 1;
+ while (substr[++idx]) {
+ /* Parse match number */
+ if (isdigit(substr[idx])) {
+ if (match[0] != '^') {
+ /* Match 0 treated as all, multiple matches we take the last one. */
+ const char *pos = substr + idx;
+/* FIXME: error check? */
+ sed_cmd->which_match = (unsigned short)strtol(substr+idx, (char**) &pos, 10);
+ idx = pos - substr;
+ }
+ continue;
+ }
+ /* Skip spaces */
+ if (isspace(substr[idx])) continue;
+
+ switch (substr[idx]) {
+ /* Replace all occurrences */
+ case 'g':
+ if (match[0] != '^') sed_cmd->which_match = 0;
+ break;
+ /* Print pattern space */
+ case 'p':
+ sed_cmd->sub_p = 1;
+ break;
+ /* Write to file */
+ case 'w':
+ {
+ char *temp;
+ idx += parse_file_cmd(sed_cmd, substr+idx, &temp);
+ break;
+ }
+ /* Ignore case (gnu exension) */
+ case 'I':
+ cflags |= REG_ICASE;
+ break;
+ /* Comment */
+ case '#':
+ while (substr[++idx]) /*skip all*/;
+ /* Fall through */
+ /* End of command */
+ case ';':
+ case '}':
+ goto out;
+ default:
+ bb_error_msg_and_die("bad option in substitution expression");
+ }
+ }
+out:
+ /* compile the match string into a regex */
+ if (*match != '\0') {
+ /* If match is empty, we use last regex used at runtime */
+ sed_cmd->sub_match = xmalloc(sizeof(regex_t));
+ xregcomp(sed_cmd->sub_match, match, cflags);
+ }
+ free(match);
+
+ return idx;
+}
+
+/*
+ * Process the commands arguments
+ */
+static const char *parse_cmd_args(sed_cmd_t *sed_cmd, const char *cmdstr)
+{
+ /* handle (s)ubstitution command */
+ if (sed_cmd->cmd == 's')
+ cmdstr += parse_subst_cmd(sed_cmd, cmdstr);
+ /* handle edit cmds: (a)ppend, (i)nsert, and (c)hange */
+ else if (strchr("aic", sed_cmd->cmd)) {
+ if ((sed_cmd->end_line || sed_cmd->end_match) && sed_cmd->cmd != 'c')
+ bb_error_msg_and_die
+ ("only a beginning address can be specified for edit commands");
+ for (;;) {
+ if (*cmdstr == '\n' || *cmdstr == '\\') {
+ cmdstr++;
+ break;
+ } else if (isspace(*cmdstr))
+ cmdstr++;
+ else
+ break;
+ }
+ sed_cmd->string = xstrdup(cmdstr);
+ parse_escapes(sed_cmd->string, sed_cmd->string, strlen(cmdstr), 0, 0);
+ cmdstr += strlen(cmdstr);
+ /* handle file cmds: (r)ead */
+ } else if (strchr("rw", sed_cmd->cmd)) {
+ if (sed_cmd->end_line || sed_cmd->end_match)
+ bb_error_msg_and_die("command only uses one address");
+ cmdstr += parse_file_cmd(sed_cmd, cmdstr, &sed_cmd->string);
+ if (sed_cmd->cmd == 'w') {
+ sed_cmd->sw_file = xfopen(sed_cmd->string, "w");
+ sed_cmd->sw_last_char = '\n';
+ }
+ /* handle branch commands */
+ } else if (strchr(":btT", sed_cmd->cmd)) {
+ int length;
+
+ cmdstr = skip_whitespace(cmdstr);
+ length = strcspn(cmdstr, semicolon_whitespace);
+ if (length) {
+ sed_cmd->string = xstrndup(cmdstr, length);
+ cmdstr += length;
+ }
+ }
+ /* translation command */
+ else if (sed_cmd->cmd == 'y') {
+ char *match, *replace;
+ int i = cmdstr[0];
+
+ cmdstr += parse_regex_delim(cmdstr, &match, &replace)+1;
+ /* \n already parsed, but \delimiter needs unescaping. */
+ parse_escapes(match, match, strlen(match), i, i);
+ parse_escapes(replace, replace, strlen(replace), i, i);
+
+ sed_cmd->string = xzalloc((strlen(match) + 1) * 2);
+ for (i = 0; match[i] && replace[i]; i++) {
+ sed_cmd->string[i*2] = match[i];
+ sed_cmd->string[i*2+1] = replace[i];
+ }
+ free(match);
+ free(replace);
+ }
+ /* if it wasnt a single-letter command that takes no arguments
+ * then it must be an invalid command.
+ */
+ else if (strchr("dDgGhHlnNpPqx={}", sed_cmd->cmd) == 0) {
+ bb_error_msg_and_die("unsupported command %c", sed_cmd->cmd);
+ }
+
+ /* give back whatever's left over */
+ return cmdstr;
+}
+
+
+/* Parse address+command sets, skipping comment lines. */
+
+static void add_cmd(const char *cmdstr)
+{
+ sed_cmd_t *sed_cmd;
+ int temp;
+
+ /* Append this line to any unfinished line from last time. */
+ if (bbg.add_cmd_line) {
+ char *tp = xasprintf("%s\n%s", bbg.add_cmd_line, cmdstr);
+ free(bbg.add_cmd_line);
+ bbg.add_cmd_line = tp;
+ }
+
+ /* If this line ends with backslash, request next line. */
+ temp = strlen(cmdstr);
+ if (temp && cmdstr[temp-1] == '\\') {
+ if (!bbg.add_cmd_line)
+ bbg.add_cmd_line = xstrdup(cmdstr);
+ bbg.add_cmd_line[temp-1] = 0;
+ return;
+ }
+
+ /* Loop parsing all commands in this line. */
+ while (*cmdstr) {
+ /* Skip leading whitespace and semicolons */
+ cmdstr += strspn(cmdstr, semicolon_whitespace);
+
+ /* If no more commands, exit. */
+ if (!*cmdstr) break;
+
+ /* if this is a comment, jump past it and keep going */
+ if (*cmdstr == '#') {
+ /* "#n" is the same as using -n on the command line */
+ if (cmdstr[1] == 'n')
+ bbg.be_quiet++;
+ cmdstr = strpbrk(cmdstr, "\n\r");
+ if (!cmdstr) break;
+ continue;
+ }
+
+ /* parse the command
+ * format is: [addr][,addr][!]cmd
+ * |----||-----||-|
+ * part1 part2 part3
+ */
+
+ sed_cmd = xzalloc(sizeof(sed_cmd_t));
+
+ /* first part (if present) is an address: either a '$', a number or a /regex/ */
+ cmdstr += get_address(cmdstr, &sed_cmd->beg_line, &sed_cmd->beg_match);
+
+ /* second part (if present) will begin with a comma */
+ if (*cmdstr == ',') {
+ int idx;
+
+ cmdstr++;
+ idx = get_address(cmdstr, &sed_cmd->end_line, &sed_cmd->end_match);
+ if (!idx)
+ bb_error_msg_and_die("no address after comma");
+ cmdstr += idx;
+ }
+
+ /* skip whitespace before the command */
+ cmdstr = skip_whitespace(cmdstr);
+
+ /* Check for inversion flag */
+ if (*cmdstr == '!') {
+ sed_cmd->invert = 1;
+ cmdstr++;
+
+ /* skip whitespace before the command */
+ cmdstr = skip_whitespace(cmdstr);
+ }
+
+ /* last part (mandatory) will be a command */
+ if (!*cmdstr)
+ bb_error_msg_and_die("missing command");
+ sed_cmd->cmd = *(cmdstr++);
+ cmdstr = parse_cmd_args(sed_cmd, cmdstr);
+
+ /* Add the command to the command array */
+ bbg.sed_cmd_tail->next = sed_cmd;
+ bbg.sed_cmd_tail = bbg.sed_cmd_tail->next;
+ }
+
+ /* If we glued multiple lines together, free the memory. */
+ free(bbg.add_cmd_line);
+ bbg.add_cmd_line = NULL;
+}
+
+/* Append to a string, reallocating memory as necessary. */
+
+#define PIPE_GROW 64
+
+static void pipe_putc(char c)
+{
+ if (bbg.pipeline.idx == bbg.pipeline.len) {
+ bbg.pipeline.buf = xrealloc(bbg.pipeline.buf,
+ bbg.pipeline.len + PIPE_GROW);
+ bbg.pipeline.len += PIPE_GROW;
+ }
+ bbg.pipeline.buf[bbg.pipeline.idx++] = c;
+}
+
+static void do_subst_w_backrefs(char *line, char *replace)
+{
+ int i,j;
+
+ /* go through the replacement string */
+ for (i = 0; replace[i]; i++) {
+ /* if we find a backreference (\1, \2, etc.) print the backref'ed * text */
+ if (replace[i] == '\\') {
+ unsigned backref = replace[++i] - '0';
+ if (backref <= 9) {
+ /* print out the text held in bbg.regmatch[backref] */
+ if (bbg.regmatch[backref].rm_so != -1) {
+ j = bbg.regmatch[backref].rm_so;
+ while (j < bbg.regmatch[backref].rm_eo)
+ pipe_putc(line[j++]);
+ }
+ continue;
+ }
+ /* I _think_ it is impossible to get '\' to be
+ * the last char in replace string. Thus we dont check
+ * for replace[i] == NUL. (counterexample anyone?) */
+ /* if we find a backslash escaped character, print the character */
+ pipe_putc(replace[i]);
+ continue;
+ }
+ /* if we find an unescaped '&' print out the whole matched text. */
+ if (replace[i] == '&') {
+ j = bbg.regmatch[0].rm_so;
+ while (j < bbg.regmatch[0].rm_eo)
+ pipe_putc(line[j++]);
+ continue;
+ }
+ /* Otherwise just output the character. */
+ pipe_putc(replace[i]);
+ }
+}
+
+static int do_subst_command(sed_cmd_t *sed_cmd, char **line)
+{
+ char *oldline = *line;
+ int altered = 0;
+ int match_count = 0;
+ regex_t *current_regex;
+
+ /* Handle empty regex. */
+ if (sed_cmd->sub_match == NULL) {
+ current_regex = bbg.previous_regex_ptr;
+ if (!current_regex)
+ bb_error_msg_and_die("no previous regexp");
+ } else
+ bbg.previous_regex_ptr = current_regex = sed_cmd->sub_match;
+
+ /* Find the first match */
+ if (REG_NOMATCH == regexec(current_regex, oldline, 10, bbg.regmatch, 0))
+ return 0;
+
+ /* Initialize temporary output buffer. */
+ bbg.pipeline.buf = xmalloc(PIPE_GROW);
+ bbg.pipeline.len = PIPE_GROW;
+ bbg.pipeline.idx = 0;
+
+ /* Now loop through, substituting for matches */
+ do {
+ int i;
+
+ /* Work around bug in glibc regexec, demonstrated by:
+ echo " a.b" | busybox sed 's [^ .]* x g'
+ The match_count check is so not to break
+ echo "hi" | busybox sed 's/^/!/g' */
+ if (!bbg.regmatch[0].rm_so && !bbg.regmatch[0].rm_eo && match_count) {
+ pipe_putc(*oldline++);
+ continue;
+ }
+
+ match_count++;
+
+ /* If we aren't interested in this match, output old line to
+ end of match and continue */
+ if (sed_cmd->which_match && sed_cmd->which_match != match_count) {
+ for (i = 0; i < bbg.regmatch[0].rm_eo; i++)
+ pipe_putc(*oldline++);
+ continue;
+ }
+
+ /* print everything before the match */
+ for (i = 0; i < bbg.regmatch[0].rm_so; i++)
+ pipe_putc(oldline[i]);
+
+ /* then print the substitution string */
+ do_subst_w_backrefs(oldline, sed_cmd->string);
+
+ /* advance past the match */
+ oldline += bbg.regmatch[0].rm_eo;
+ /* flag that something has changed */
+ altered++;
+
+ /* if we're not doing this globally, get out now */
+ if (sed_cmd->which_match) break;
+ } while (*oldline && (regexec(current_regex, oldline, 10, bbg.regmatch, 0) != REG_NOMATCH));
+
+ /* Copy rest of string into output pipeline */
+
+ while (*oldline)
+ pipe_putc(*oldline++);
+ pipe_putc(0);
+
+ free(*line);
+ *line = bbg.pipeline.buf;
+ return altered;
+}
+
+/* Set command pointer to point to this label. (Does not handle null label.) */
+static sed_cmd_t *branch_to(char *label)
+{
+ sed_cmd_t *sed_cmd;
+
+ for (sed_cmd = bbg.sed_cmd_head.next; sed_cmd; sed_cmd = sed_cmd->next) {
+ if (sed_cmd->cmd == ':' && sed_cmd->string && !strcmp(sed_cmd->string, label)) {
+ return sed_cmd;
+ }
+ }
+ bb_error_msg_and_die("can't find label for jump to '%s'", label);
+}
+
+static void append(char *s)
+{
+ llist_add_to_end(&bbg.append_head, xstrdup(s));
+}
+
+static void flush_append(void)
+{
+ char *data;
+
+ /* Output appended lines. */
+ while ((data = (char *)llist_pop(&bbg.append_head))) {
+ fprintf(bbg.nonstdout, "%s\n", data);
+ free(data);
+ }
+}
+
+static void add_input_file(FILE *file)
+{
+ bbg.input_file_list = xrealloc(bbg.input_file_list,
+ (bbg.input_file_count + 1) * sizeof(FILE *));
+ bbg.input_file_list[bbg.input_file_count++] = file;
+}
+
+/* Get next line of input from bbg.input_file_list, flushing append buffer and
+ * noting if we ran out of files without a newline on the last line we read.
+ */
+enum {
+ NO_EOL_CHAR = 1,
+ LAST_IS_NUL = 2,
+};
+static char *get_next_line(char *gets_char)
+{
+ char *temp = NULL;
+ int len;
+ char gc;
+
+ flush_append();
+
+ /* will be returned if last line in the file
+ * doesn't end with either '\n' or '\0' */
+ gc = NO_EOL_CHAR;
+ while (bbg.current_input_file < bbg.input_file_count) {
+ FILE *fp = bbg.input_file_list[bbg.current_input_file];
+ /* Read line up to a newline or NUL byte, inclusive,
+ * return malloc'ed char[]. length of the chunk read
+ * is stored in len. NULL if EOF/error */
+ temp = bb_get_chunk_from_file(fp, &len);
+ if (temp) {
+ /* len > 0 here, it's ok to do temp[len-1] */
+ char c = temp[len-1];
+ if (c == '\n' || c == '\0') {
+ temp[len-1] = '\0';
+ gc = c;
+ if (c == '\0') {
+ int ch = fgetc(fp);
+ if (ch != EOF)
+ ungetc(ch, fp);
+ else
+ gc = LAST_IS_NUL;
+ }
+ }
+ /* else we put NO_EOL_CHAR into *gets_char */
+ break;
+
+ /* NB: I had the idea of peeking next file(s) and returning
+ * NO_EOL_CHAR only if it is the *last* non-empty
+ * input file. But there is a case where this won't work:
+ * file1: "a woo\nb woo"
+ * file2: "c no\nd no"
+ * sed -ne 's/woo/bang/p' input1 input2 => "a bang\nb bang"
+ * (note: *no* newline after "b bang"!) */
+ }
+ /* Close this file and advance to next one */
+ fclose(fp);
+ bbg.current_input_file++;
+ }
+ *gets_char = gc;
+ return temp;
+}
+
+/* Output line of text. */
+/* Note:
+ * The tricks with NO_EOL_CHAR and last_puts_char are there to emulate gnu sed.
+ * Without them, we had this:
+ * echo -n thingy >z1
+ * echo -n again >z2
+ * >znull
+ * sed "s/i/z/" z1 z2 znull | hexdump -vC
+ * output:
+ * gnu sed 4.1.5:
+ * 00000000 74 68 7a 6e 67 79 0a 61 67 61 7a 6e |thzngy.agazn|
+ * bbox:
+ * 00000000 74 68 7a 6e 67 79 61 67 61 7a 6e |thzngyagazn|
+ */
+static void puts_maybe_newline(char *s, FILE *file, char *last_puts_char, char last_gets_char)
+{
+ char lpc = *last_puts_char;
+
+ /* Need to insert a '\n' between two files because first file's
+ * last line wasn't terminated? */
+ if (lpc != '\n' && lpc != '\0') {
+ fputc('\n', file);
+ lpc = '\n';
+ }
+ fputs(s, file);
+
+ /* 'x' - just something which is not '\n', '\0' or NO_EOL_CHAR */
+ if (s[0])
+ lpc = 'x';
+
+ /* had trailing '\0' and it was last char of file? */
+ if (last_gets_char == LAST_IS_NUL) {
+ fputc('\0', file);
+ lpc = 'x'; /* */
+ } else
+ /* had trailing '\n' or '\0'? */
+ if (last_gets_char != NO_EOL_CHAR) {
+ fputc(last_gets_char, file);
+ lpc = last_gets_char;
+ }
+
+ if (ferror(file)) {
+ xfunc_error_retval = 4; /* It's what gnu sed exits with... */
+ bb_error_msg_and_die(bb_msg_write_error);
+ }
+ *last_puts_char = lpc;
+}
+
+#define sed_puts(s, n) (puts_maybe_newline(s, bbg.nonstdout, &last_puts_char, n))
+
+/* Process all the lines in all the files */
+
+static void process_files(void)
+{
+ char *pattern_space, *next_line;
+ int linenum = 0;
+ char last_puts_char = '\n';
+ char last_gets_char, next_gets_char;
+ sed_cmd_t *sed_cmd;
+ int substituted;
+
+ /* Prime the pump */
+ next_line = get_next_line(&next_gets_char);
+
+ /* go through every line in each file */
+again:
+ substituted = 0;
+
+ /* Advance to next line. Stop if out of lines. */
+ pattern_space = next_line;
+ if (!pattern_space) return;
+ last_gets_char = next_gets_char;
+
+ /* Read one line in advance so we can act on the last line,
+ * the '$' address */
+ next_line = get_next_line(&next_gets_char);
+ linenum++;
+restart:
+ /* for every line, go through all the commands */
+ for (sed_cmd = bbg.sed_cmd_head.next; sed_cmd; sed_cmd = sed_cmd->next) {
+ int old_matched, matched;
+
+ old_matched = sed_cmd->in_match;
+
+ /* Determine if this command matches this line: */
+
+ /* Are we continuing a previous multi-line match? */
+ sed_cmd->in_match = sed_cmd->in_match
+ /* Or is no range necessary? */
+ || (!sed_cmd->beg_line && !sed_cmd->end_line
+ && !sed_cmd->beg_match && !sed_cmd->end_match)
+ /* Or did we match the start of a numerical range? */
+ || (sed_cmd->beg_line > 0 && (sed_cmd->beg_line == linenum))
+ /* Or does this line match our begin address regex? */
+ || (sed_cmd->beg_match &&
+ !regexec(sed_cmd->beg_match, pattern_space, 0, NULL, 0))
+ /* Or did we match last line of input? */
+ || (sed_cmd->beg_line == -1 && next_line == NULL);
+
+ /* Snapshot the value */
+
+ matched = sed_cmd->in_match;
+
+ /* Is this line the end of the current match? */
+
+ if (matched) {
+ sed_cmd->in_match = !(
+ /* has the ending line come, or is this a single address command? */
+ (sed_cmd->end_line ?
+ sed_cmd->end_line == -1 ?
+ !next_line
+ : (sed_cmd->end_line <= linenum)
+ : !sed_cmd->end_match
+ )
+ /* or does this line matches our last address regex */
+ || (sed_cmd->end_match && old_matched
+ && (regexec(sed_cmd->end_match,
+ pattern_space, 0, NULL, 0) == 0))
+ );
+ }
+
+ /* Skip blocks of commands we didn't match. */
+ if (sed_cmd->cmd == '{') {
+ if (sed_cmd->invert ? matched : !matched) {
+ while (sed_cmd->cmd != '}') {
+ sed_cmd = sed_cmd->next;
+ if (!sed_cmd)
+ bb_error_msg_and_die("unterminated {");
+ }
+ }
+ continue;
+ }
+
+ /* Okay, so did this line match? */
+ if (sed_cmd->invert ? !matched : matched) {
+ /* Update last used regex in case a blank substitute BRE is found */
+ if (sed_cmd->beg_match) {
+ bbg.previous_regex_ptr = sed_cmd->beg_match;
+ }
+
+ /* actual sedding */
+ switch (sed_cmd->cmd) {
+
+ /* Print line number */
+ case '=':
+ fprintf(bbg.nonstdout, "%d\n", linenum);
+ break;
+
+ /* Write the current pattern space up to the first newline */
+ case 'P':
+ {
+ char *tmp = strchr(pattern_space, '\n');
+
+ if (tmp) {
+ *tmp = '\0';
+ /* TODO: explain why '\n' below */
+ sed_puts(pattern_space, '\n');
+ *tmp = '\n';
+ break;
+ }
+ /* Fall Through */
+ }
+
+ /* Write the current pattern space to output */
+ case 'p':
+ /* NB: we print this _before_ the last line
+ * (of current file) is printed. Even if
+ * that line is nonterminated, we print
+ * '\n' here (gnu sed does the same) */
+ sed_puts(pattern_space, '\n');
+ break;
+ /* Delete up through first newline */
+ case 'D':
+ {
+ char *tmp = strchr(pattern_space, '\n');
+
+ if (tmp) {
+ tmp = xstrdup(tmp+1);
+ free(pattern_space);
+ pattern_space = tmp;
+ goto restart;
+ }
+ }
+ /* discard this line. */
+ case 'd':
+ goto discard_line;
+
+ /* Substitute with regex */
+ case 's':
+ if (!do_subst_command(sed_cmd, &pattern_space))
+ break;
+ substituted |= 1;
+
+ /* handle p option */
+ if (sed_cmd->sub_p)
+ sed_puts(pattern_space, last_gets_char);
+ /* handle w option */
+ if (sed_cmd->sw_file)
+ puts_maybe_newline(
+ pattern_space, sed_cmd->sw_file,
+ &sed_cmd->sw_last_char, last_gets_char);
+ break;
+
+ /* Append line to linked list to be printed later */
+ case 'a':
+ append(sed_cmd->string);
+ break;
+
+ /* Insert text before this line */
+ case 'i':
+ sed_puts(sed_cmd->string, '\n');
+ break;
+
+ /* Cut and paste text (replace) */
+ case 'c':
+ /* Only triggers on last line of a matching range. */
+ if (!sed_cmd->in_match)
+ sed_puts(sed_cmd->string, NO_EOL_CHAR);
+ goto discard_line;
+
+ /* Read file, append contents to output */
+ case 'r':
+ {
+ FILE *rfile;
+
+ rfile = fopen(sed_cmd->string, "r");
+ if (rfile) {
+ char *line;
+
+ while ((line = xmalloc_getline(rfile))
+ != NULL)
+ append(line);
+ xprint_and_close_file(rfile);
+ }
+
+ break;
+ }
+
+ /* Write pattern space to file. */
+ case 'w':
+ puts_maybe_newline(
+ pattern_space, sed_cmd->sw_file,
+ &sed_cmd->sw_last_char, last_gets_char);
+ break;
+
+ /* Read next line from input */
+ case 'n':
+ if (!bbg.be_quiet)
+ sed_puts(pattern_space, last_gets_char);
+ if (next_line) {
+ free(pattern_space);
+ pattern_space = next_line;
+ last_gets_char = next_gets_char;
+ next_line = get_next_line(&next_gets_char);
+ linenum++;
+ break;
+ }
+ /* fall through */
+
+ /* Quit. End of script, end of input. */
+ case 'q':
+ /* Exit the outer while loop */
+ free(next_line);
+ next_line = NULL;
+ goto discard_commands;
+
+ /* Append the next line to the current line */
+ case 'N':
+ {
+ int len;
+ /* If no next line, jump to end of script and exit. */
+ if (next_line == NULL) {
+ /* Jump to end of script and exit */
+ free(next_line);
+ next_line = NULL;
+ goto discard_line;
+ /* append next_line, read new next_line. */
+ }
+ len = strlen(pattern_space);
+ pattern_space = realloc(pattern_space, len + strlen(next_line) + 2);
+ pattern_space[len] = '\n';
+ strcpy(pattern_space + len+1, next_line);
+ last_gets_char = next_gets_char;
+ next_line = get_next_line(&next_gets_char);
+ linenum++;
+ break;
+ }
+
+ /* Test/branch if substitution occurred */
+ case 't':
+ if (!substituted) break;
+ substituted = 0;
+ /* Fall through */
+ /* Test/branch if substitution didn't occur */
+ case 'T':
+ if (substituted) break;
+ /* Fall through */
+ /* Branch to label */
+ case 'b':
+ if (!sed_cmd->string) goto discard_commands;
+ else sed_cmd = branch_to(sed_cmd->string);
+ break;
+ /* Transliterate characters */
+ case 'y':
+ {
+ int i, j;
+
+ for (i = 0; pattern_space[i]; i++) {
+ for (j = 0; sed_cmd->string[j]; j += 2) {
+ if (pattern_space[i] == sed_cmd->string[j]) {
+ pattern_space[i] = sed_cmd->string[j + 1];
+ break;
+ }
+ }
+ }
+
+ break;
+ }
+ case 'g': /* Replace pattern space with hold space */
+ free(pattern_space);
+ pattern_space = xstrdup(bbg.hold_space ? bbg.hold_space : "");
+ break;
+ case 'G': /* Append newline and hold space to pattern space */
+ {
+ int pattern_space_size = 2;
+ int hold_space_size = 0;
+
+ if (pattern_space)
+ pattern_space_size += strlen(pattern_space);
+ if (bbg.hold_space)
+ hold_space_size = strlen(bbg.hold_space);
+ pattern_space = xrealloc(pattern_space,
+ pattern_space_size + hold_space_size);
+ if (pattern_space_size == 2)
+ pattern_space[0] = 0;
+ strcat(pattern_space, "\n");
+ if (bbg.hold_space)
+ strcat(pattern_space, bbg.hold_space);
+ last_gets_char = '\n';
+
+ break;
+ }
+ case 'h': /* Replace hold space with pattern space */
+ free(bbg.hold_space);
+ bbg.hold_space = xstrdup(pattern_space);
+ break;
+ case 'H': /* Append newline and pattern space to hold space */
+ {
+ int hold_space_size = 2;
+ int pattern_space_size = 0;
+
+ if (bbg.hold_space)
+ hold_space_size += strlen(bbg.hold_space);
+ if (pattern_space)
+ pattern_space_size = strlen(pattern_space);
+ bbg.hold_space = xrealloc(bbg.hold_space,
+ hold_space_size + pattern_space_size);
+
+ if (hold_space_size == 2)
+ *bbg.hold_space = 0;
+ strcat(bbg.hold_space, "\n");
+ if (pattern_space)
+ strcat(bbg.hold_space, pattern_space);
+
+ break;
+ }
+ case 'x': /* Exchange hold and pattern space */
+ {
+ char *tmp = pattern_space;
+ pattern_space = bbg.hold_space ? : xzalloc(1);
+ last_gets_char = '\n';
+ bbg.hold_space = tmp;
+ break;
+ }
+ }
+ }
+ }
+
+ /*
+ * exit point from sedding...
+ */
+ discard_commands:
+ /* we will print the line unless we were told to be quiet ('-n')
+ or if the line was suppressed (ala 'd'elete) */
+ if (!bbg.be_quiet)
+ sed_puts(pattern_space, last_gets_char);
+
+ /* Delete and such jump here. */
+ discard_line:
+ flush_append();
+ free(pattern_space);
+
+ goto again;
+}
+
+/* It is possible to have a command line argument with embedded
+ * newlines. This counts as multiple command lines.
+ * However, newline can be escaped: 's/e/z\<newline>z/'
+ * We check for this.
+ */
+
+static void add_cmd_block(char *cmdstr)
+{
+ char *sv, *eol;
+
+ cmdstr = sv = xstrdup(cmdstr);
+ do {
+ eol = strchr(cmdstr, '\n');
+ next:
+ if (eol) {
+ /* Count preceding slashes */
+ int slashes = 0;
+ char *sl = eol;
+
+ while (sl != cmdstr && *--sl == '\\')
+ slashes++;
+ /* Odd number of preceding slashes - newline is escaped */
+ if (slashes & 1) {
+ strcpy(eol-1, eol);
+ eol = strchr(eol, '\n');
+ goto next;
+ }
+ *eol = '\0';
+ }
+ add_cmd(cmdstr);
+ cmdstr = eol + 1;
+ } while (eol);
+ free(sv);
+}
+
+static void add_cmds_link(llist_t *opt_e)
+{
+ if (!opt_e) return;
+ add_cmds_link(opt_e->link);
+ add_cmd_block(opt_e->data);
+ free(opt_e);
+}
+
+static void add_files_link(llist_t *opt_f)
+{
+ char *line;
+ FILE *cmdfile;
+ if (!opt_f) return;
+ add_files_link(opt_f->link);
+ cmdfile = xfopen(opt_f->data, "r");
+ while ((line = xmalloc_getline(cmdfile)) != NULL) {
+ add_cmd(line);
+ free(line);
+ }
+ xprint_and_close_file(cmdfile);
+ free(opt_f);
+}
+
+int sed_main(int argc, char **argv);
+int sed_main(int argc, char **argv)
+{
+ enum {
+ OPT_in_place = 1 << 0,
+ };
+ unsigned opt;
+ llist_t *opt_e, *opt_f;
+ int status = EXIT_SUCCESS;
+
+ bbg.sed_cmd_tail = &bbg.sed_cmd_head;
+
+ /* destroy command strings on exit */
+ if (ENABLE_FEATURE_CLEAN_UP) atexit(sed_free_and_close_stuff);
+
+ /* Lie to autoconf when it starts asking stupid questions. */
+ if (argc == 2 && !strcmp(argv[1], "--version")) {
+ puts("This is not GNU sed version 4.0");
+ return 0;
+ }
+
+ /* do normal option parsing */
+ opt_e = opt_f = NULL;
+ opt_complementary = "e::f::" /* can occur multiple times */
+ "nn"; /* count -n */
+ opt = getopt32(argc, argv, "irne:f:", &opt_e, &opt_f,
+ &bbg.be_quiet); /* counter for -n */
+ argc -= optind;
+ argv += optind;
+ if (opt & OPT_in_place) { // -i
+ atexit(cleanup_outname);
+ }
+ if (opt & 0x2) bbg.regex_type |= REG_EXTENDED; // -r
+ //if (opt & 0x4) bbg.be_quiet++; // -n
+ if (opt & 0x8) { // -e
+ /* getopt32 reverses order of arguments, handle it */
+ add_cmds_link(opt_e);
+ }
+ if (opt & 0x10) { // -f
+ /* getopt32 reverses order of arguments, handle it */
+ add_files_link(opt_f);
+ }
+ /* if we didn't get a pattern from -e or -f, use argv[0] */
+ if (!(opt & 0x18)) {
+ if (!argc)
+ bb_show_usage();
+ add_cmd_block(*argv++);
+ argc--;
+ }
+ /* Flush any unfinished commands. */
+ add_cmd("");
+
+ /* By default, we write to stdout */
+ bbg.nonstdout = stdout;
+
+ /* argv[0..(argc-1)] should be names of file to process. If no
+ * files were specified or '-' was specified, take input from stdin.
+ * Otherwise, we process all the files specified. */
+ if (argv[0] == NULL) {
+ if (opt & OPT_in_place)
+ bb_error_msg_and_die(bb_msg_requires_arg, "-i");
+ add_input_file(stdin);
+ process_files();
+ } else {
+ int i;
+ FILE *file;
+
+ for (i = 0; i < argc; i++) {
+ struct stat statbuf;
+ int nonstdoutfd;
+
+ if (LONE_DASH(argv[i]) && !(opt & OPT_in_place)) {
+ add_input_file(stdin);
+ process_files();
+ continue;
+ }
+ file = fopen_or_warn(argv[i], "r");
+ if (!file) {
+ status = EXIT_FAILURE;
+ continue;
+ }
+ if (!(opt & OPT_in_place)) {
+ add_input_file(file);
+ continue;
+ }
+
+ bbg.outname = xasprintf("%sXXXXXX", argv[i]);
+ nonstdoutfd = mkstemp(bbg.outname);
+ if (-1 == nonstdoutfd)
+ bb_error_msg_and_die("no temp file");
+ bbg.nonstdout = fdopen(nonstdoutfd, "w");
+
+ /* Set permissions of output file */
+
+ fstat(fileno(file), &statbuf);
+ fchmod(nonstdoutfd, statbuf.st_mode);
+ add_input_file(file);
+ process_files();
+ fclose(bbg.nonstdout);
+
+ bbg.nonstdout = stdout;
+ /* unlink(argv[i]); */
+ // FIXME: error check / message?
+ rename(bbg.outname, argv[i]);
+ free(bbg.outname);
+ bbg.outname = 0;
+ }
+ if (bbg.input_file_count > bbg.current_input_file)
+ process_files();
+ }
+
+ return status;
+}
diff --git a/i/pc104/initrd/conf/busybox/editors/vi.c b/i/pc104/initrd/conf/busybox/editors/vi.c
new file mode 100644
index 0000000..8533032
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/editors/vi.c
@@ -0,0 +1,3999 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * tiny vi.c: A small 'vi' clone
+ * Copyright (C) 2000, 2001 Sterling Huxley <sterling@europa.com>
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ */
+
+/*
+ * Things To Do:
+ * EXINIT
+ * $HOME/.exrc and ./.exrc
+ * add magic to search /foo.*bar
+ * add :help command
+ * :map macros
+ * if mark[] values were line numbers rather than pointers
+ * it would be easier to change the mark when add/delete lines
+ * More intelligence in refresh()
+ * ":r !cmd" and "!cmd" to filter text through an external command
+ * A true "undo" facility
+ * An "ex" line oriented mode- maybe using "cmdedit"
+ */
+
+#include "busybox.h"
+
+#define ENABLE_FEATURE_VI_CRASHME 0
+
+#if ENABLE_LOCALE_SUPPORT
+#define Isprint(c) isprint((c))
+#else
+/* 0x9b is Meta-ESC */
+#define Isprint(c) ((unsigned char)(c) >= ' ' && (c) != 0x7f && (unsigned char)(c) != 0x9b)
+#endif
+
+#define MAX_SCR_COLS BUFSIZ
+
+// Misc. non-Ascii keys that report an escape sequence
+#define VI_K_UP (char)128 // cursor key Up
+#define VI_K_DOWN (char)129 // cursor key Down
+#define VI_K_RIGHT (char)130 // Cursor Key Right
+#define VI_K_LEFT (char)131 // cursor key Left
+#define VI_K_HOME (char)132 // Cursor Key Home
+#define VI_K_END (char)133 // Cursor Key End
+#define VI_K_INSERT (char)134 // Cursor Key Insert
+#define VI_K_PAGEUP (char)135 // Cursor Key Page Up
+#define VI_K_PAGEDOWN (char)136 // Cursor Key Page Down
+#define VI_K_FUN1 (char)137 // Function Key F1
+#define VI_K_FUN2 (char)138 // Function Key F2
+#define VI_K_FUN3 (char)139 // Function Key F3
+#define VI_K_FUN4 (char)140 // Function Key F4
+#define VI_K_FUN5 (char)141 // Function Key F5
+#define VI_K_FUN6 (char)142 // Function Key F6
+#define VI_K_FUN7 (char)143 // Function Key F7
+#define VI_K_FUN8 (char)144 // Function Key F8
+#define VI_K_FUN9 (char)145 // Function Key F9
+#define VI_K_FUN10 (char)146 // Function Key F10
+#define VI_K_FUN11 (char)147 // Function Key F11
+#define VI_K_FUN12 (char)148 // Function Key F12
+
+/* vt102 typical ESC sequence */
+/* terminal standout start/normal ESC sequence */
+static const char SOs[] = "\033[7m";
+static const char SOn[] = "\033[0m";
+/* terminal bell sequence */
+static const char bell[] = "\007";
+/* Clear-end-of-line and Clear-end-of-screen ESC sequence */
+static const char Ceol[] = "\033[0K";
+static const char Ceos [] = "\033[0J";
+/* Cursor motion arbitrary destination ESC sequence */
+static const char CMrc[] = "\033[%d;%dH";
+/* Cursor motion up and down ESC sequence */
+static const char CMup[] = "\033[A";
+static const char CMdown[] = "\n";
+
+
+enum {
+ YANKONLY = FALSE,
+ YANKDEL = TRUE,
+ FORWARD = 1, // code depends on "1" for array index
+ BACK = -1, // code depends on "-1" for array index
+ LIMITED = 0, // how much of text[] in char_search
+ FULL = 1, // how much of text[] in char_search
+
+ S_BEFORE_WS = 1, // used in skip_thing() for moving "dot"
+ S_TO_WS = 2, // used in skip_thing() for moving "dot"
+ S_OVER_WS = 3, // used in skip_thing() for moving "dot"
+ S_END_PUNCT = 4, // used in skip_thing() for moving "dot"
+ S_END_ALNUM = 5, // used in skip_thing() for moving "dot"
+};
+
+/* vi.c expects chars to be unsigned. */
+/* busybox build system provides that, but it's better */
+/* to audit and fix the source */
+
+static int vi_setops;
+#define VI_AUTOINDENT 1
+#define VI_SHOWMATCH 2
+#define VI_IGNORECASE 4
+#define VI_ERR_METHOD 8
+#define autoindent (vi_setops & VI_AUTOINDENT)
+#define showmatch (vi_setops & VI_SHOWMATCH )
+#define ignorecase (vi_setops & VI_IGNORECASE)
+/* indicate error with beep or flash */
+#define err_method (vi_setops & VI_ERR_METHOD)
+
+
+static int editing; // >0 while we are editing a file
+static int cmd_mode; // 0=command 1=insert 2=replace
+static int file_modified; // buffer contents changed
+static int last_file_modified = -1;
+static int fn_start; // index of first cmd line file name
+static int save_argc; // how many file names on cmd line
+static int cmdcnt; // repetition count
+static fd_set rfds; // use select() for small sleeps
+static struct timeval tv; // use select() for small sleeps
+static int rows, columns; // the terminal screen is this size
+static int crow, ccol, offset; // cursor is on Crow x Ccol with Horz Ofset
+static char *status_buffer; // mesages to the user
+#define STATUS_BUFFER_LEN 200
+static int have_status_msg; // is default edit status needed?
+static int last_status_cksum; // hash of current status line
+static char *cfn; // previous, current, and next file name
+static char *text, *end; // pointers to the user data in memory
+static char *screen; // pointer to the virtual screen buffer
+static int screensize; // and its size
+static char *screenbegin; // index into text[], of top line on the screen
+static char *dot; // where all the action takes place
+static int tabstop;
+static struct termios term_orig, term_vi; // remember what the cooked mode was
+static char erase_char; // the users erase character
+static char last_input_char; // last char read from user
+static char last_forward_char; // last char searched for with 'f'
+
+#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
+static int last_row; // where the cursor was last moved to
+#endif
+#if ENABLE_FEATURE_VI_USE_SIGNALS
+static jmp_buf restart; // catch_sig()
+#endif
+#if ENABLE_FEATURE_VI_USE_SIGNALS || ENABLE_FEATURE_VI_CRASHME
+static int my_pid;
+#endif
+#if ENABLE_FEATURE_VI_DOT_CMD
+static int adding2q; // are we currently adding user input to q
+static char *last_modifying_cmd; // last modifying cmd for "."
+static char *ioq, *ioq_start; // pointer to string for get_one_char to "read"
+#endif
+#if ENABLE_FEATURE_VI_DOT_CMD || ENABLE_FEATURE_VI_YANKMARK
+static char *modifying_cmds; // cmds that modify text[]
+#endif
+#if ENABLE_FEATURE_VI_READONLY
+static int vi_readonly, readonly;
+#endif
+#if ENABLE_FEATURE_VI_YANKMARK
+static char *reg[28]; // named register a-z, "D", and "U" 0-25,26,27
+static int YDreg, Ureg; // default delete register and orig line for "U"
+static char *mark[28]; // user marks points somewhere in text[]- a-z and previous context ''
+static char *context_start, *context_end;
+#endif
+#if ENABLE_FEATURE_VI_SEARCH
+static char *last_search_pattern; // last pattern from a '/' or '?' search
+#endif
+
+
+static void edit_file(char *); // edit one file
+static void do_cmd(char); // execute a command
+static void sync_cursor(char *, int *, int *); // synchronize the screen cursor to dot
+static char *begin_line(char *); // return pointer to cur line B-o-l
+static char *end_line(char *); // return pointer to cur line E-o-l
+static char *prev_line(char *); // return pointer to prev line B-o-l
+static char *next_line(char *); // return pointer to next line B-o-l
+static char *end_screen(void); // get pointer to last char on screen
+static int count_lines(char *, char *); // count line from start to stop
+static char *find_line(int); // find begining of line #li
+static char *move_to_col(char *, int); // move "p" to column l
+static int isblnk(char); // is the char a blank or tab
+static void dot_left(void); // move dot left- dont leave line
+static void dot_right(void); // move dot right- dont leave line
+static void dot_begin(void); // move dot to B-o-l
+static void dot_end(void); // move dot to E-o-l
+static void dot_next(void); // move dot to next line B-o-l
+static void dot_prev(void); // move dot to prev line B-o-l
+static void dot_scroll(int, int); // move the screen up or down
+static void dot_skip_over_ws(void); // move dot pat WS
+static void dot_delete(void); // delete the char at 'dot'
+static char *bound_dot(char *); // make sure text[0] <= P < "end"
+static char *new_screen(int, int); // malloc virtual screen memory
+static char *new_text(int); // malloc memory for text[] buffer
+static char *char_insert(char *, char); // insert the char c at 'p'
+static char *stupid_insert(char *, char); // stupidly insert the char c at 'p'
+static char find_range(char **, char **, char); // return pointers for an object
+static int st_test(char *, int, int, char *); // helper for skip_thing()
+static char *skip_thing(char *, int, int, int); // skip some object
+static char *find_pair(char *, char); // find matching pair () [] {}
+static char *text_hole_delete(char *, char *); // at "p", delete a 'size' byte hole
+static char *text_hole_make(char *, int); // at "p", make a 'size' byte hole
+static char *yank_delete(char *, char *, int, int); // yank text[] into register then delete
+static void show_help(void); // display some help info
+static void rawmode(void); // set "raw" mode on tty
+static void cookmode(void); // return to "cooked" mode on tty
+static int mysleep(int); // sleep for 'h' 1/100 seconds
+static char readit(void); // read (maybe cursor) key from stdin
+static char get_one_char(void); // read 1 char from stdin
+static int file_size(const char *); // what is the byte size of "fn"
+static int file_insert(char *, char *, int);
+static int file_write(char *, char *, char *);
+static void place_cursor(int, int, int);
+static void screen_erase(void);
+static void clear_to_eol(void);
+static void clear_to_eos(void);
+static void standout_start(void); // send "start reverse video" sequence
+static void standout_end(void); // send "end reverse video" sequence
+static void flash(int); // flash the terminal screen
+static void show_status_line(void); // put a message on the bottom line
+static void psb(const char *, ...); // Print Status Buf
+static void psbs(const char *, ...); // Print Status Buf in standout mode
+static void ni(const char *); // display messages
+static int format_edit_status(void); // format file status on status line
+static void redraw(int); // force a full screen refresh
+static void format_line(char*, char*, int);
+static void refresh(int); // update the terminal from screen[]
+
+static void Indicate_Error(void); // use flash or beep to indicate error
+#define indicate_error(c) Indicate_Error()
+static void Hit_Return(void);
+
+#if ENABLE_FEATURE_VI_SEARCH
+static char *char_search(char *, const char *, int, int); // search for pattern starting at p
+static int mycmp(const char *, const char *, int); // string cmp based in "ignorecase"
+#endif
+#if ENABLE_FEATURE_VI_COLON
+static char *get_one_address(char *, int *); // get colon addr, if present
+static char *get_address(char *, int *, int *); // get two colon addrs, if present
+static void colon(char *); // execute the "colon" mode cmds
+#endif
+#if ENABLE_FEATURE_VI_USE_SIGNALS
+static void winch_sig(int); // catch window size changes
+static void suspend_sig(int); // catch ctrl-Z
+static void catch_sig(int); // catch ctrl-C and alarm time-outs
+#endif
+#if ENABLE_FEATURE_VI_DOT_CMD
+static void start_new_cmd_q(char); // new queue for command
+static void end_cmd_q(void); // stop saving input chars
+#else
+#define end_cmd_q() ((void)0)
+#endif
+#if ENABLE_FEATURE_VI_SETOPTS
+static void showmatching(char *); // show the matching pair () [] {}
+#endif
+#if ENABLE_FEATURE_VI_YANKMARK || (ENABLE_FEATURE_VI_COLON && ENABLE_FEATURE_VI_SEARCH) || ENABLE_FEATURE_VI_CRASHME
+static char *string_insert(char *, char *); // insert the string at 'p'
+#endif
+#if ENABLE_FEATURE_VI_YANKMARK
+static char *text_yank(char *, char *, int); // save copy of "p" into a register
+static char what_reg(void); // what is letter of current YDreg
+static void check_context(char); // remember context for '' command
+#endif
+#if ENABLE_FEATURE_VI_CRASHME
+static void crash_dummy();
+static void crash_test();
+static int crashme = 0;
+#endif
+#if ENABLE_FEATURE_VI_COLON
+static char *initial_cmds[] = { NULL, NULL , NULL }; // currently 2 entries, NULL terminated
+#endif
+
+
+static void write1(const char *out)
+{
+ fputs(out, stdout);
+}
+
+int vi_main(int argc, char **argv);
+int vi_main(int argc, char **argv)
+{
+ int c;
+ RESERVE_CONFIG_BUFFER(STATUS_BUFFER, STATUS_BUFFER_LEN);
+
+#if ENABLE_FEATURE_VI_YANKMARK
+ int i;
+#endif
+#if defined(CONFIG_FEATURE_VI_USE_SIGNALS) || defined(CONFIG_FEATURE_VI_CRASHME)
+ my_pid = getpid();
+#endif
+#if ENABLE_FEATURE_VI_CRASHME
+ srand((long) my_pid);
+#endif
+
+ status_buffer = STATUS_BUFFER;
+ last_status_cksum = 0;
+
+#if ENABLE_FEATURE_VI_READONLY
+ vi_readonly = readonly = FALSE;
+ if (strncmp(argv[0], "view", 4) == 0) {
+ readonly = TRUE;
+ vi_readonly = TRUE;
+ }
+#endif
+ vi_setops = VI_AUTOINDENT | VI_SHOWMATCH | VI_IGNORECASE | VI_ERR_METHOD;
+#if ENABLE_FEATURE_VI_YANKMARK
+ for (i = 0; i < 28; i++) {
+ reg[i] = 0;
+ } // init the yank regs
+#endif
+#if ENABLE_FEATURE_VI_DOT_CMD || ENABLE_FEATURE_VI_YANKMARK
+ modifying_cmds = (char *) "aAcCdDiIJoOpPrRsxX<>~"; // cmds modifying text[]
+#endif
+
+ // 1- process $HOME/.exrc file (not inplemented yet)
+ // 2- process EXINIT variable from environment
+ // 3- process command line args
+#if ENABLE_FEATURE_VI_COLON
+ {
+ char *p = getenv("EXINIT");
+ if (p && *p)
+ initial_cmds[0] = xstrdup(p);
+ }
+#endif
+ while ((c = getopt(argc, argv, "hCR" USE_FEATURE_VI_COLON("c:"))) != -1) {
+ switch (c) {
+#if ENABLE_FEATURE_VI_CRASHME
+ case 'C':
+ crashme = 1;
+ break;
+#endif
+#if ENABLE_FEATURE_VI_READONLY
+ case 'R': // Read-only flag
+ readonly = TRUE;
+ vi_readonly = TRUE;
+ break;
+#endif
+ //case 'r': // recover flag- ignore- we don't use tmp file
+ //case 'x': // encryption flag- ignore
+ //case 'c': // execute command first
+#if ENABLE_FEATURE_VI_COLON
+ case 'c': // cmd line vi command
+ if (*optarg)
+ initial_cmds[initial_cmds[0] != 0] = xstrdup(optarg);
+ break;
+ //case 'h': // help -- just use default
+#endif
+ default:
+ show_help();
+ return 1;
+ }
+ }
+
+ // The argv array can be used by the ":next" and ":rewind" commands
+ // save optind.
+ fn_start = optind; // remember first file name for :next and :rew
+ save_argc = argc;
+
+ //----- This is the main file handling loop --------------
+ if (optind >= argc) {
+ editing = 1; // 0= exit, 1= one file, 2= multiple files
+ edit_file(0);
+ } else {
+ for (; optind < argc; optind++) {
+ editing = 1; // 0=exit, 1=one file, 2+ =many files
+ free(cfn);
+ cfn = xstrdup(argv[optind]);
+ edit_file(cfn);
+ }
+ }
+ //-----------------------------------------------------------
+
+ return 0;
+}
+
+static void edit_file(char * fn)
+{
+ char c;
+ int cnt, size, ch;
+
+#if ENABLE_FEATURE_VI_USE_SIGNALS
+ int sig;
+#endif
+#if ENABLE_FEATURE_VI_YANKMARK
+ static char *cur_line;
+#endif
+
+ rawmode();
+ rows = 24;
+ columns = 80;
+ ch = -1;
+ if (ENABLE_FEATURE_VI_WIN_RESIZE)
+ get_terminal_width_height(0, &columns, &rows);
+ new_screen(rows, columns); // get memory for virtual screen
+
+ cnt = file_size(fn); // file size
+ size = 2 * cnt; // 200% of file size
+ new_text(size); // get a text[] buffer
+ screenbegin = dot = end = text;
+ if (fn != 0) {
+ ch = file_insert(fn, text, cnt);
+ }
+ if (ch < 1) {
+ char_insert(text, '\n'); // start empty buf with dummy line
+ }
+ file_modified = 0;
+ last_file_modified = -1;
+#if ENABLE_FEATURE_VI_YANKMARK
+ YDreg = 26; // default Yank/Delete reg
+ Ureg = 27; // hold orig line for "U" cmd
+ for (cnt = 0; cnt < 28; cnt++) {
+ mark[cnt] = 0;
+ } // init the marks
+ mark[26] = mark[27] = text; // init "previous context"
+#endif
+
+ last_forward_char = last_input_char = '\0';
+ crow = 0;
+ ccol = 0;
+
+#if ENABLE_FEATURE_VI_USE_SIGNALS
+ catch_sig(0);
+ signal(SIGWINCH, winch_sig);
+ signal(SIGTSTP, suspend_sig);
+ sig = setjmp(restart);
+ if (sig != 0) {
+ screenbegin = dot = text;
+ }
+#endif
+
+ editing = 1;
+ cmd_mode = 0; // 0=command 1=insert 2='R'eplace
+ cmdcnt = 0;
+ tabstop = 8;
+ offset = 0; // no horizontal offset
+ c = '\0';
+#if ENABLE_FEATURE_VI_DOT_CMD
+ free(last_modifying_cmd);
+ free(ioq_start);
+ ioq = ioq_start = last_modifying_cmd = NULL;
+ adding2q = 0;
+#endif
+ redraw(FALSE); // dont force every col re-draw
+ show_status_line();
+
+#if ENABLE_FEATURE_VI_COLON
+ {
+ char *p, *q;
+ int n = 0;
+
+ while ((p = initial_cmds[n])) {
+ do {
+ q = p;
+ p = strchr(q,'\n');
+ if (p)
+ while(*p == '\n')
+ *p++ = '\0';
+ if (*q)
+ colon(q);
+ } while (p);
+ free(initial_cmds[n]);
+ initial_cmds[n] = NULL;
+ n++;
+ }
+ }
+#endif
+ //------This is the main Vi cmd handling loop -----------------------
+ while (editing > 0) {
+#if ENABLE_FEATURE_VI_CRASHME
+ if (crashme > 0) {
+ if ((end - text) > 1) {
+ crash_dummy(); // generate a random command
+ } else {
+ crashme = 0;
+ dot = string_insert(text, "\n\n##### Ran out of text to work on. #####\n\n"); // insert the string
+ refresh(FALSE);
+ }
+ }
+#endif
+ last_input_char = c = get_one_char(); // get a cmd from user
+#if ENABLE_FEATURE_VI_YANKMARK
+ // save a copy of the current line- for the 'U" command
+ if (begin_line(dot) != cur_line) {
+ cur_line = begin_line(dot);
+ text_yank(begin_line(dot), end_line(dot), Ureg);
+ }
+#endif
+#if ENABLE_FEATURE_VI_DOT_CMD
+ // These are commands that change text[].
+ // Remember the input for the "." command
+ if (!adding2q && ioq_start == 0
+ && strchr(modifying_cmds, c)
+ ) {
+ start_new_cmd_q(c);
+ }
+#endif
+ do_cmd(c); // execute the user command
+ //
+ // poll to see if there is input already waiting. if we are
+ // not able to display output fast enough to keep up, skip
+ // the display update until we catch up with input.
+ if (mysleep(0) == 0) {
+ // no input pending- so update output
+ refresh(FALSE);
+ show_status_line();
+ }
+#if ENABLE_FEATURE_VI_CRASHME
+ if (crashme > 0)
+ crash_test(); // test editor variables
+#endif
+ }
+ //-------------------------------------------------------------------
+
+ place_cursor(rows, 0, FALSE); // go to bottom of screen
+ clear_to_eol(); // Erase to end of line
+ cookmode();
+}
+
+//----- The Colon commands -------------------------------------
+#if ENABLE_FEATURE_VI_COLON
+static char *get_one_address(char * p, int *addr) // get colon addr, if present
+{
+ int st;
+ char *q;
+
+#if ENABLE_FEATURE_VI_YANKMARK
+ char c;
+#endif
+#if ENABLE_FEATURE_VI_SEARCH
+ char *pat, buf[BUFSIZ];
+#endif
+
+ *addr = -1; // assume no addr
+ if (*p == '.') { // the current line
+ p++;
+ q = begin_line(dot);
+ *addr = count_lines(text, q);
+#if ENABLE_FEATURE_VI_YANKMARK
+ } else if (*p == '\'') { // is this a mark addr
+ p++;
+ c = tolower(*p);
+ p++;
+ if (c >= 'a' && c <= 'z') {
+ // we have a mark
+ c = c - 'a';
+ q = mark[(unsigned char) c];
+ if (q != NULL) { // is mark valid
+ *addr = count_lines(text, q); // count lines
+ }
+ }
+#endif
+#if ENABLE_FEATURE_VI_SEARCH
+ } else if (*p == '/') { // a search pattern
+ q = buf;
+ for (p++; *p; p++) {
+ if (*p == '/')
+ break;
+ *q++ = *p;
+ *q = '\0';
+ }
+ pat = xstrdup(buf); // save copy of pattern
+ if (*p == '/')
+ p++;
+ q = char_search(dot, pat, FORWARD, FULL);
+ if (q != NULL) {
+ *addr = count_lines(text, q);
+ }
+ free(pat);
+#endif
+ } else if (*p == '$') { // the last line in file
+ p++;
+ q = begin_line(end - 1);
+ *addr = count_lines(text, q);
+ } else if (isdigit(*p)) { // specific line number
+ sscanf(p, "%d%n", addr, &st);
+ p += st;
+ } else { // I don't reconise this
+ // unrecognised address- assume -1
+ *addr = -1;
+ }
+ return p;
+}
+
+static char *get_address(char *p, int *b, int *e) // get two colon addrs, if present
+{
+ //----- get the address' i.e., 1,3 'a,'b -----
+ // get FIRST addr, if present
+ while (isblnk(*p))
+ p++; // skip over leading spaces
+ if (*p == '%') { // alias for 1,$
+ p++;
+ *b = 1;
+ *e = count_lines(text, end-1);
+ goto ga0;
+ }
+ p = get_one_address(p, b);
+ while (isblnk(*p))
+ p++;
+ if (*p == ',') { // is there a address separator
+ p++;
+ while (isblnk(*p))
+ p++;
+ // get SECOND addr, if present
+ p = get_one_address(p, e);
+ }
+ ga0:
+ while (isblnk(*p))
+ p++; // skip over trailing spaces
+ return p;
+}
+
+#if ENABLE_FEATURE_VI_SET && ENABLE_FEATURE_VI_SETOPTS
+static void setops(const char *args, const char *opname, int flg_no,
+ const char *short_opname, int opt)
+{
+ const char *a = args + flg_no;
+ int l = strlen(opname) - 1; /* opname have + ' ' */
+
+ if (strncasecmp(a, opname, l) == 0
+ || strncasecmp(a, short_opname, 2) == 0
+ ) {
+ if (flg_no)
+ vi_setops &= ~opt;
+ else
+ vi_setops |= opt;
+ }
+}
+#endif
+
+static void colon(char * buf)
+{
+ char c, *orig_buf, *buf1, *q, *r;
+ char *fn, cmd[BUFSIZ], args[BUFSIZ];
+ int i, l, li, ch, b, e;
+ int useforce = FALSE, forced = FALSE;
+ struct stat st_buf;
+
+ // :3154 // if (-e line 3154) goto it else stay put
+ // :4,33w! foo // write a portion of buffer to file "foo"
+ // :w // write all of buffer to current file
+ // :q // quit
+ // :q! // quit- dont care about modified file
+ // :'a,'z!sort -u // filter block through sort
+ // :'f // goto mark "f"
+ // :'fl // list literal the mark "f" line
+ // :.r bar // read file "bar" into buffer before dot
+ // :/123/,/abc/d // delete lines from "123" line to "abc" line
+ // :/xyz/ // goto the "xyz" line
+ // :s/find/replace/ // substitute pattern "find" with "replace"
+ // :!<cmd> // run <cmd> then return
+ //
+
+ if (!buf[0])
+ goto vc1;
+ if (*buf == ':')
+ buf++; // move past the ':'
+
+ li = ch = i = 0;
+ b = e = -1;
+ q = text; // assume 1,$ for the range
+ r = end - 1;
+ li = count_lines(text, end - 1);
+ fn = cfn; // default to current file
+ memset(cmd, '\0', BUFSIZ); // clear cmd[]
+ memset(args, '\0', BUFSIZ); // clear args[]
+
+ // look for optional address(es) :. :1 :1,9 :'q,'a :%
+ buf = get_address(buf, &b, &e);
+
+ // remember orig command line
+ orig_buf = buf;
+
+ // get the COMMAND into cmd[]
+ buf1 = cmd;
+ while (*buf != '\0') {
+ if (isspace(*buf))
+ break;
+ *buf1++ = *buf++;
+ }
+ // get any ARGuments
+ while (isblnk(*buf))
+ buf++;
+ strcpy(args, buf);
+ buf1 = last_char_is(cmd, '!');
+ if (buf1) {
+ useforce = TRUE;
+ *buf1 = '\0'; // get rid of !
+ }
+ if (b >= 0) {
+ // if there is only one addr, then the addr
+ // is the line number of the single line the
+ // user wants. So, reset the end
+ // pointer to point at end of the "b" line
+ q = find_line(b); // what line is #b
+ r = end_line(q);
+ li = 1;
+ }
+ if (e >= 0) {
+ // we were given two addrs. change the
+ // end pointer to the addr given by user.
+ r = find_line(e); // what line is #e
+ r = end_line(r);
+ li = e - b + 1;
+ }
+ // ------------ now look for the command ------------
+ i = strlen(cmd);
+ if (i == 0) { // :123CR goto line #123
+ if (b >= 0) {
+ dot = find_line(b); // what line is #b
+ dot_skip_over_ws();
+ }
+ }
+#if ENABLE_FEATURE_ALLOW_EXEC
+ else if (strncmp(cmd, "!", 1) == 0) { // run a cmd
+ // :!ls run the <cmd>
+ alarm(0); // wait for input- no alarms
+ place_cursor(rows - 1, 0, FALSE); // go to Status line
+ clear_to_eol(); // clear the line
+ cookmode();
+ system(orig_buf + 1); // run the cmd
+ rawmode();
+ Hit_Return(); // let user see results
+ alarm(3); // done waiting for input
+ }
+#endif
+ else if (strncmp(cmd, "=", i) == 0) { // where is the address
+ if (b < 0) { // no addr given- use defaults
+ b = e = count_lines(text, dot);
+ }
+ psb("%d", b);
+ } else if (strncasecmp(cmd, "delete", i) == 0) { // delete lines
+ if (b < 0) { // no addr given- use defaults
+ q = begin_line(dot); // assume .,. for the range
+ r = end_line(dot);
+ }
+ dot = yank_delete(q, r, 1, YANKDEL); // save, then delete lines
+ dot_skip_over_ws();
+ } else if (strncasecmp(cmd, "edit", i) == 0) { // Edit a file
+ int sr;
+ sr= 0;
+ // don't edit, if the current file has been modified
+ if (file_modified && ! useforce) {
+ psbs("No write since last change (:edit! overrides)");
+ goto vc1;
+ }
+ if (args[0]) {
+ // the user supplied a file name
+ fn= args;
+ } else if (cfn && cfn[0]) {
+ // no user supplied name- use the current filename
+ fn= cfn;
+ goto vc5;
+ } else {
+ // no user file name, no current name- punt
+ psbs("No current filename");
+ goto vc1;
+ }
+
+ // see if file exists- if not, its just a new file request
+ sr = stat(fn, &st_buf);
+ if (sr < 0) {
+ // This is just a request for a new file creation.
+ // The file_insert below will fail but we get
+ // an empty buffer with a file name. Then the "write"
+ // command can do the create.
+ } else {
+ if ((st_buf.st_mode & S_IFREG) == 0) {
+ // This is not a regular file
+ psbs("\"%s\" is not a regular file", fn);
+ goto vc1;
+ }
+ if ((st_buf.st_mode & (S_IRUSR | S_IRGRP | S_IROTH)) == 0) {
+ // dont have any read permissions
+ psbs("\"%s\" is not readable", fn);
+ goto vc1;
+ }
+ }
+
+ // There is a read-able regular file
+ // make this the current file
+ q = xstrdup(fn); // save the cfn
+ free(cfn); // free the old name
+ cfn = q; // remember new cfn
+
+ vc5:
+ // delete all the contents of text[]
+ new_text(2 * file_size(fn));
+ screenbegin = dot = end = text;
+
+ // insert new file
+ ch = file_insert(fn, text, file_size(fn));
+
+ if (ch < 1) {
+ // start empty buf with dummy line
+ char_insert(text, '\n');
+ ch = 1;
+ }
+ file_modified = 0;
+ last_file_modified = -1;
+#if ENABLE_FEATURE_VI_YANKMARK
+ if (Ureg >= 0 && Ureg < 28 && reg[Ureg] != 0) {
+ free(reg[Ureg]); // free orig line reg- for 'U'
+ reg[Ureg]= 0;
+ }
+ if (YDreg >= 0 && YDreg < 28 && reg[YDreg] != 0) {
+ free(reg[YDreg]); // free default yank/delete register
+ reg[YDreg]= 0;
+ }
+ for (li = 0; li < 28; li++) {
+ mark[li] = 0;
+ } // init the marks
+#endif
+ // how many lines in text[]?
+ li = count_lines(text, end - 1);
+ psb("\"%s\"%s"
+#if ENABLE_FEATURE_VI_READONLY
+ "%s"
+#endif
+ " %dL, %dC", cfn,
+ (sr < 0 ? " [New file]" : ""),
+#if ENABLE_FEATURE_VI_READONLY
+ ((vi_readonly || readonly) ? " [Read only]" : ""),
+#endif
+ li, ch);
+ } else if (strncasecmp(cmd, "file", i) == 0) { // what File is this
+ if (b != -1 || e != -1) {
+ ni("No address allowed on this command");
+ goto vc1;
+ }
+ if (args[0]) {
+ // user wants a new filename
+ free(cfn);
+ cfn = xstrdup(args);
+ } else {
+ // user wants file status info
+ last_status_cksum = 0; // force status update
+ }
+ } else if (strncasecmp(cmd, "features", i) == 0) { // what features are available
+ // print out values of all features
+ place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
+ clear_to_eol(); // clear the line
+ cookmode();
+ show_help();
+ rawmode();
+ Hit_Return();
+ } else if (strncasecmp(cmd, "list", i) == 0) { // literal print line
+ if (b < 0) { // no addr given- use defaults
+ q = begin_line(dot); // assume .,. for the range
+ r = end_line(dot);
+ }
+ place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
+ clear_to_eol(); // clear the line
+ puts("\r");
+ for (; q <= r; q++) {
+ int c_is_no_print;
+
+ c = *q;
+ c_is_no_print = (c & 0x80) && !Isprint(c);
+ if (c_is_no_print) {
+ c = '.';
+ standout_start();
+ }
+ if (c == '\n') {
+ write1("$\r");
+ } else if (c < ' ' || c == 127) {
+ putchar('^');
+ if (c == 127)
+ c = '?';
+ else
+ c += '@';
+ }
+ putchar(c);
+ if (c_is_no_print)
+ standout_end();
+ }
+#if ENABLE_FEATURE_VI_SET
+ vc2:
+#endif
+ Hit_Return();
+ } else if (strncasecmp(cmd, "quit", i) == 0 // Quit
+ || strncasecmp(cmd, "next", i) == 0 // edit next file
+ ) {
+ if (useforce) {
+ // force end of argv list
+ if (*cmd == 'q') {
+ optind = save_argc;
+ }
+ editing = 0;
+ goto vc1;
+ }
+ // don't exit if the file been modified
+ if (file_modified) {
+ psbs("No write since last change (:%s! overrides)",
+ (*cmd == 'q' ? "quit" : "next"));
+ goto vc1;
+ }
+ // are there other file to edit
+ if (*cmd == 'q' && optind < save_argc - 1) {
+ psbs("%d more file to edit", (save_argc - optind - 1));
+ goto vc1;
+ }
+ if (*cmd == 'n' && optind >= save_argc - 1) {
+ psbs("No more files to edit");
+ goto vc1;
+ }
+ editing = 0;
+ } else if (strncasecmp(cmd, "read", i) == 0) { // read file into text[]
+ fn = args;
+ if (!fn[0]) {
+ psbs("No filename given");
+ goto vc1;
+ }
+ if (b < 0) { // no addr given- use defaults
+ q = begin_line(dot); // assume "dot"
+ }
+ // read after current line- unless user said ":0r foo"
+ if (b != 0)
+ q = next_line(q);
+#if ENABLE_FEATURE_VI_READONLY
+ l = readonly; // remember current files' status
+#endif
+ ch = file_insert(fn, q, file_size(fn));
+#if ENABLE_FEATURE_VI_READONLY
+ readonly= l;
+#endif
+ if (ch < 0)
+ goto vc1; // nothing was inserted
+ // how many lines in text[]?
+ li = count_lines(q, q + ch - 1);
+ psb("\"%s\""
+#if ENABLE_FEATURE_VI_READONLY
+ "%s"
+#endif
+ " %dL, %dC", fn,
+#if ENABLE_FEATURE_VI_READONLY
+ ((vi_readonly || readonly) ? " [Read only]" : ""),
+#endif
+ li, ch);
+ if (ch > 0) {
+ // if the insert is before "dot" then we need to update
+ if (q <= dot)
+ dot += ch;
+ file_modified++;
+ }
+ } else if (strncasecmp(cmd, "rewind", i) == 0) { // rewind cmd line args
+ if (file_modified && ! useforce) {
+ psbs("No write since last change (:rewind! overrides)");
+ } else {
+ // reset the filenames to edit
+ optind = fn_start - 1;
+ editing = 0;
+ }
+#if ENABLE_FEATURE_VI_SET
+ } else if (strncasecmp(cmd, "set", i) == 0) { // set or clear features
+#if ENABLE_FEATURE_VI_SETOPTS
+ char *argp;
+#endif
+ i = 0; // offset into args
+ // only blank is regarded as args delmiter. What about tab '\t' ?
+ if (!args[0] || strcasecmp(args, "all") == 0) {
+ // print out values of all options
+ place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
+ clear_to_eol(); // clear the line
+ printf("----------------------------------------\r\n");
+#if ENABLE_FEATURE_VI_SETOPTS
+ if (!autoindent)
+ printf("no");
+ printf("autoindent ");
+ if (!err_method)
+ printf("no");
+ printf("flash ");
+ if (!ignorecase)
+ printf("no");
+ printf("ignorecase ");
+ if (!showmatch)
+ printf("no");
+ printf("showmatch ");
+ printf("tabstop=%d ", tabstop);
+#endif
+ printf("\r\n");
+ goto vc2;
+ }
+#if ENABLE_FEATURE_VI_SETOPTS
+ argp = args;
+ while (*argp) {
+ if (strncasecmp(argp, "no", 2) == 0)
+ i = 2; // ":set noautoindent"
+ setops(argp, "autoindent ", i, "ai", VI_AUTOINDENT);
+ setops(argp, "flash ", i, "fl", VI_ERR_METHOD);
+ setops(argp, "ignorecase ", i, "ic", VI_IGNORECASE);
+ setops(argp, "showmatch ", i, "ic", VI_SHOWMATCH);
+ /* tabstopXXXX */
+ if (strncasecmp(argp + i, "tabstop=%d ", 7) == 0) {
+ sscanf(strchr(argp + i, '='), "tabstop=%d" + 7, &ch);
+ if (ch > 0 && ch < columns - 1)
+ tabstop = ch;
+ }
+ while (*argp && *argp != ' ')
+ argp++; // skip to arg delimiter (i.e. blank)
+ while (*argp && *argp == ' ')
+ argp++; // skip all delimiting blanks
+ }
+#endif /* FEATURE_VI_SETOPTS */
+#endif /* FEATURE_VI_SET */
+#if ENABLE_FEATURE_VI_SEARCH
+ } else if (strncasecmp(cmd, "s", 1) == 0) { // substitute a pattern with a replacement pattern
+ char *ls, *F, *R;
+ int gflag;
+
+ // F points to the "find" pattern
+ // R points to the "replace" pattern
+ // replace the cmd line delimiters "/" with NULLs
+ gflag = 0; // global replace flag
+ c = orig_buf[1]; // what is the delimiter
+ F = orig_buf + 2; // start of "find"
+ R = strchr(F, c); // middle delimiter
+ if (!R) goto colon_s_fail;
+ *R++ = '\0'; // terminate "find"
+ buf1 = strchr(R, c);
+ if (!buf1) goto colon_s_fail;
+ *buf1++ = '\0'; // terminate "replace"
+ if (*buf1 == 'g') { // :s/foo/bar/g
+ buf1++;
+ gflag++; // turn on gflag
+ }
+ q = begin_line(q);
+ if (b < 0) { // maybe :s/foo/bar/
+ q = begin_line(dot); // start with cur line
+ b = count_lines(text, q); // cur line number
+ }
+ if (e < 0)
+ e = b; // maybe :.s/foo/bar/
+ for (i = b; i <= e; i++) { // so, :20,23 s \0 find \0 replace \0
+ ls = q; // orig line start
+ vc4:
+ buf1 = char_search(q, F, FORWARD, LIMITED); // search cur line only for "find"
+ if (buf1) {
+ // we found the "find" pattern - delete it
+ text_hole_delete(buf1, buf1 + strlen(F) - 1);
+ // inset the "replace" patern
+ string_insert(buf1, R); // insert the string
+ // check for "global" :s/foo/bar/g
+ if (gflag == 1) {
+ if ((buf1 + strlen(R)) < end_line(ls)) {
+ q = buf1 + strlen(R);
+ goto vc4; // don't let q move past cur line
+ }
+ }
+ }
+ q = next_line(ls);
+ }
+#endif /* FEATURE_VI_SEARCH */
+ } else if (strncasecmp(cmd, "version", i) == 0) { // show software version
+ psb("%s", BB_VER " " BB_BT);
+ } else if (strncasecmp(cmd, "write", i) == 0 // write text to file
+ || strncasecmp(cmd, "wq", i) == 0
+ || strncasecmp(cmd, "wn", i) == 0
+ || strncasecmp(cmd, "x", i) == 0
+ ) {
+ // is there a file name to write to?
+ if (args[0]) {
+ fn = args;
+ }
+#if ENABLE_FEATURE_VI_READONLY
+ if ((vi_readonly || readonly) && ! useforce) {
+ psbs("\"%s\" File is read only", fn);
+ goto vc3;
+ }
+#endif
+ // how many lines in text[]?
+ li = count_lines(q, r);
+ ch = r - q + 1;
+ // see if file exists- if not, its just a new file request
+ if (useforce) {
+ // if "fn" is not write-able, chmod u+w
+ // sprintf(syscmd, "chmod u+w %s", fn);
+ // system(syscmd);
+ forced = TRUE;
+ }
+ l = file_write(fn, q, r);
+ if (useforce && forced) {
+ // chmod u-w
+ // sprintf(syscmd, "chmod u-w %s", fn);
+ // system(syscmd);
+ forced = FALSE;
+ }
+ if (l < 0) {
+ if (l == -1)
+ psbs("Write error: %s", strerror(errno));
+ } else {
+ psb("\"%s\" %dL, %dC", fn, li, l);
+ if (q == text && r == end - 1 && l == ch) {
+ file_modified = 0;
+ last_file_modified = -1;
+ }
+ if ((cmd[0] == 'x' || cmd[1] == 'q' || cmd[1] == 'n' ||
+ cmd[0] == 'X' || cmd[1] == 'Q' || cmd[1] == 'N')
+ && l == ch) {
+ editing = 0;
+ }
+ }
+#if ENABLE_FEATURE_VI_READONLY
+ vc3:;
+#endif
+#if ENABLE_FEATURE_VI_YANKMARK
+ } else if (strncasecmp(cmd, "yank", i) == 0) { // yank lines
+ if (b < 0) { // no addr given- use defaults
+ q = begin_line(dot); // assume .,. for the range
+ r = end_line(dot);
+ }
+ text_yank(q, r, YDreg);
+ li = count_lines(q, r);
+ psb("Yank %d lines (%d chars) into [%c]",
+ li, strlen(reg[YDreg]), what_reg());
+#endif
+ } else {
+ // cmd unknown
+ ni(cmd);
+ }
+ vc1:
+ dot = bound_dot(dot); // make sure "dot" is valid
+ return;
+#if ENABLE_FEATURE_VI_SEARCH
+ colon_s_fail:
+ psb(":s expression missing delimiters");
+#endif
+}
+
+#endif /* FEATURE_VI_COLON */
+
+static void Hit_Return(void)
+{
+ char c;
+
+ standout_start(); // start reverse video
+ write1("[Hit return to continue]");
+ standout_end(); // end reverse video
+ while ((c = get_one_char()) != '\n' && c != '\r') /*do nothing */
+ ;
+ redraw(TRUE); // force redraw all
+}
+
+//----- Synchronize the cursor to Dot --------------------------
+static void sync_cursor(char * d, int *row, int *col)
+{
+ char *beg_cur; // begin and end of "d" line
+ char *end_scr; // begin and end of screen
+ char *tp;
+ int cnt, ro, co;
+
+ beg_cur = begin_line(d); // first char of cur line
+
+ end_scr = end_screen(); // last char of screen
+
+ if (beg_cur < screenbegin) {
+ // "d" is before top line on screen
+ // how many lines do we have to move
+ cnt = count_lines(beg_cur, screenbegin);
+ sc1:
+ screenbegin = beg_cur;
+ if (cnt > (rows - 1) / 2) {
+ // we moved too many lines. put "dot" in middle of screen
+ for (cnt = 0; cnt < (rows - 1) / 2; cnt++) {
+ screenbegin = prev_line(screenbegin);
+ }
+ }
+ } else if (beg_cur > end_scr) {
+ // "d" is after bottom line on screen
+ // how many lines do we have to move
+ cnt = count_lines(end_scr, beg_cur);
+ if (cnt > (rows - 1) / 2)
+ goto sc1; // too many lines
+ for (ro = 0; ro < cnt - 1; ro++) {
+ // move screen begin the same amount
+ screenbegin = next_line(screenbegin);
+ // now, move the end of screen
+ end_scr = next_line(end_scr);
+ end_scr = end_line(end_scr);
+ }
+ }
+ // "d" is on screen- find out which row
+ tp = screenbegin;
+ for (ro = 0; ro < rows - 1; ro++) { // drive "ro" to correct row
+ if (tp == beg_cur)
+ break;
+ tp = next_line(tp);
+ }
+
+ // find out what col "d" is on
+ co = 0;
+ do { // drive "co" to correct column
+ if (*tp == '\n' || *tp == '\0')
+ break;
+ if (*tp == '\t') {
+ // 7 - (co % 8 )
+ co += ((tabstop - 1) - (co % tabstop));
+ } else if (*tp < ' ' || *tp == 127) {
+ co++; // display as ^X, use 2 columns
+ }
+ } while (tp++ < d && ++co);
+
+ // "co" is the column where "dot" is.
+ // The screen has "columns" columns.
+ // The currently displayed columns are 0+offset -- columns+ofset
+ // |-------------------------------------------------------------|
+ // ^ ^ ^
+ // offset | |------- columns ----------------|
+ //
+ // If "co" is already in this range then we do not have to adjust offset
+ // but, we do have to subtract the "offset" bias from "co".
+ // If "co" is outside this range then we have to change "offset".
+ // If the first char of a line is a tab the cursor will try to stay
+ // in column 7, but we have to set offset to 0.
+
+ if (co < 0 + offset) {
+ offset = co;
+ }
+ if (co >= columns + offset) {
+ offset = co - columns + 1;
+ }
+ // if the first char of the line is a tab, and "dot" is sitting on it
+ // force offset to 0.
+ if (d == beg_cur && *d == '\t') {
+ offset = 0;
+ }
+ co -= offset;
+
+ *row = ro;
+ *col = co;
+}
+
+//----- Text Movement Routines ---------------------------------
+static char *begin_line(char * p) // return pointer to first char cur line
+{
+ while (p > text && p[-1] != '\n')
+ p--; // go to cur line B-o-l
+ return p;
+}
+
+static char *end_line(char * p) // return pointer to NL of cur line line
+{
+ while (p < end - 1 && *p != '\n')
+ p++; // go to cur line E-o-l
+ return p;
+}
+
+static inline char *dollar_line(char * p) // return pointer to just before NL line
+{
+ while (p < end - 1 && *p != '\n')
+ p++; // go to cur line E-o-l
+ // Try to stay off of the Newline
+ if (*p == '\n' && (p - begin_line(p)) > 0)
+ p--;
+ return p;
+}
+
+static char *prev_line(char * p) // return pointer first char prev line
+{
+ p = begin_line(p); // goto begining of cur line
+ if (p[-1] == '\n' && p > text)
+ p--; // step to prev line
+ p = begin_line(p); // goto begining of prev line
+ return p;
+}
+
+static char *next_line(char * p) // return pointer first char next line
+{
+ p = end_line(p);
+ if (*p == '\n' && p < end - 1)
+ p++; // step to next line
+ return p;
+}
+
+//----- Text Information Routines ------------------------------
+static char *end_screen(void)
+{
+ char *q;
+ int cnt;
+
+ // find new bottom line
+ q = screenbegin;
+ for (cnt = 0; cnt < rows - 2; cnt++)
+ q = next_line(q);
+ q = end_line(q);
+ return q;
+}
+
+static int count_lines(char * start, char * stop) // count line from start to stop
+{
+ char *q;
+ int cnt;
+
+ if (stop < start) { // start and stop are backwards- reverse them
+ q = start;
+ start = stop;
+ stop = q;
+ }
+ cnt = 0;
+ stop = end_line(stop); // get to end of this line
+ for (q = start; q <= stop && q <= end - 1; q++) {
+ if (*q == '\n')
+ cnt++;
+ }
+ return cnt;
+}
+
+static char *find_line(int li) // find begining of line #li
+{
+ char *q;
+
+ for (q = text; li > 1; li--) {
+ q = next_line(q);
+ }
+ return q;
+}
+
+//----- Dot Movement Routines ----------------------------------
+static void dot_left(void)
+{
+ if (dot > text && dot[-1] != '\n')
+ dot--;
+}
+
+static void dot_right(void)
+{
+ if (dot < end - 1 && *dot != '\n')
+ dot++;
+}
+
+static void dot_begin(void)
+{
+ dot = begin_line(dot); // return pointer to first char cur line
+}
+
+static void dot_end(void)
+{
+ dot = end_line(dot); // return pointer to last char cur line
+}
+
+static char *move_to_col(char * p, int l)
+{
+ int co;
+
+ p = begin_line(p);
+ co = 0;
+ do {
+ if (*p == '\n' || *p == '\0')
+ break;
+ if (*p == '\t') {
+ // 7 - (co % 8 )
+ co += ((tabstop - 1) - (co % tabstop));
+ } else if (*p < ' ' || *p == 127) {
+ co++; // display as ^X, use 2 columns
+ }
+ } while (++co <= l && p++ < end);
+ return p;
+}
+
+static void dot_next(void)
+{
+ dot = next_line(dot);
+}
+
+static void dot_prev(void)
+{
+ dot = prev_line(dot);
+}
+
+static void dot_scroll(int cnt, int dir)
+{
+ char *q;
+
+ for (; cnt > 0; cnt--) {
+ if (dir < 0) {
+ // scroll Backwards
+ // ctrl-Y scroll up one line
+ screenbegin = prev_line(screenbegin);
+ } else {
+ // scroll Forwards
+ // ctrl-E scroll down one line
+ screenbegin = next_line(screenbegin);
+ }
+ }
+ // make sure "dot" stays on the screen so we dont scroll off
+ if (dot < screenbegin)
+ dot = screenbegin;
+ q = end_screen(); // find new bottom line
+ if (dot > q)
+ dot = begin_line(q); // is dot is below bottom line?
+ dot_skip_over_ws();
+}
+
+static void dot_skip_over_ws(void)
+{
+ // skip WS
+ while (isspace(*dot) && *dot != '\n' && dot < end - 1)
+ dot++;
+}
+
+static void dot_delete(void) // delete the char at 'dot'
+{
+ text_hole_delete(dot, dot);
+}
+
+static char *bound_dot(char * p) // make sure text[0] <= P < "end"
+{
+ if (p >= end && end > text) {
+ p = end - 1;
+ indicate_error('1');
+ }
+ if (p < text) {
+ p = text;
+ indicate_error('2');
+ }
+ return p;
+}
+
+//----- Helper Utility Routines --------------------------------
+
+//----------------------------------------------------------------
+//----- Char Routines --------------------------------------------
+/* Chars that are part of a word-
+ * 0123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
+ * Chars that are Not part of a word (stoppers)
+ * !"#$%&'()*+,-./:;<=>?@[\]^`{|}~
+ * Chars that are WhiteSpace
+ * TAB NEWLINE VT FF RETURN SPACE
+ * DO NOT COUNT NEWLINE AS WHITESPACE
+ */
+
+static char *new_screen(int ro, int co)
+{
+ int li;
+
+ free(screen);
+ screensize = ro * co + 8;
+ screen = xmalloc(screensize);
+ // initialize the new screen. assume this will be a empty file.
+ screen_erase();
+ // non-existent text[] lines start with a tilde (~).
+ for (li = 1; li < ro - 1; li++) {
+ screen[(li * co) + 0] = '~';
+ }
+ return screen;
+}
+
+static char *new_text(int size)
+{
+ if (size < 10240)
+ size = 10240; // have a minimum size for new files
+ free(text);
+ text = xmalloc(size + 8);
+ memset(text, '\0', size); // clear new text[]
+ //text += 4; // leave some room for "oops"
+ return text;
+}
+
+#if ENABLE_FEATURE_VI_SEARCH
+static int mycmp(const char * s1, const char * s2, int len)
+{
+ int i;
+
+ i = strncmp(s1, s2, len);
+#if ENABLE_FEATURE_VI_SETOPTS
+ if (ignorecase) {
+ i = strncasecmp(s1, s2, len);
+ }
+#endif
+ return i;
+}
+
+// search for pattern starting at p
+static char *char_search(char * p, const char * pat, int dir, int range)
+{
+#ifndef REGEX_SEARCH
+ char *start, *stop;
+ int len;
+
+ len = strlen(pat);
+ if (dir == FORWARD) {
+ stop = end - 1; // assume range is p - end-1
+ if (range == LIMITED)
+ stop = next_line(p); // range is to next line
+ for (start = p; start < stop; start++) {
+ if (mycmp(start, pat, len) == 0) {
+ return start;
+ }
+ }
+ } else if (dir == BACK) {
+ stop = text; // assume range is text - p
+ if (range == LIMITED)
+ stop = prev_line(p); // range is to prev line
+ for (start = p - len; start >= stop; start--) {
+ if (mycmp(start, pat, len) == 0) {
+ return start;
+ }
+ }
+ }
+ // pattern not found
+ return NULL;
+#else /* REGEX_SEARCH */
+ char *q;
+ struct re_pattern_buffer preg;
+ int i;
+ int size, range;
+
+ re_syntax_options = RE_SYNTAX_POSIX_EXTENDED;
+ preg.translate = 0;
+ preg.fastmap = 0;
+ preg.buffer = 0;
+ preg.allocated = 0;
+
+ // assume a LIMITED forward search
+ q = next_line(p);
+ q = end_line(q);
+ q = end - 1;
+ if (dir == BACK) {
+ q = prev_line(p);
+ q = text;
+ }
+ // count the number of chars to search over, forward or backward
+ size = q - p;
+ if (size < 0)
+ size = p - q;
+ // RANGE could be negative if we are searching backwards
+ range = q - p;
+
+ q = re_compile_pattern(pat, strlen(pat), &preg);
+ if (q != 0) {
+ // The pattern was not compiled
+ psbs("bad search pattern: \"%s\": %s", pat, q);
+ i = 0; // return p if pattern not compiled
+ goto cs1;
+ }
+
+ q = p;
+ if (range < 0) {
+ q = p - size;
+ if (q < text)
+ q = text;
+ }
+ // search for the compiled pattern, preg, in p[]
+ // range < 0- search backward
+ // range > 0- search forward
+ // 0 < start < size
+ // re_search() < 0 not found or error
+ // re_search() > 0 index of found pattern
+ // struct pattern char int int int struct reg
+ // re_search (*pattern_buffer, *string, size, start, range, *regs)
+ i = re_search(&preg, q, size, 0, range, 0);
+ if (i == -1) {
+ p = 0;
+ i = 0; // return NULL if pattern not found
+ }
+ cs1:
+ if (dir == FORWARD) {
+ p = p + i;
+ } else {
+ p = p - i;
+ }
+ return p;
+#endif /* REGEX_SEARCH */
+}
+#endif /* FEATURE_VI_SEARCH */
+
+static char *char_insert(char * p, char c) // insert the char c at 'p'
+{
+ if (c == 22) { // Is this an ctrl-V?
+ p = stupid_insert(p, '^'); // use ^ to indicate literal next
+ p--; // backup onto ^
+ refresh(FALSE); // show the ^
+ c = get_one_char();
+ *p = c;
+ p++;
+ file_modified++; // has the file been modified
+ } else if (c == 27) { // Is this an ESC?
+ cmd_mode = 0;
+ cmdcnt = 0;
+ end_cmd_q(); // stop adding to q
+ last_status_cksum = 0; // force status update
+ if ((p[-1] != '\n') && (dot > text)) {
+ p--;
+ }
+ } else if (c == erase_char || c == 8 || c == 127) { // Is this a BS
+ // 123456789
+ if ((p[-1] != '\n') && (dot>text)) {
+ p--;
+ p = text_hole_delete(p, p); // shrink buffer 1 char
+ }
+ } else {
+ // insert a char into text[]
+ char *sp; // "save p"
+
+ if (c == 13)
+ c = '\n'; // translate \r to \n
+ sp = p; // remember addr of insert
+ p = stupid_insert(p, c); // insert the char
+#if ENABLE_FEATURE_VI_SETOPTS
+ if (showmatch && strchr(")]}", *sp) != NULL) {
+ showmatching(sp);
+ }
+ if (autoindent && c == '\n') { // auto indent the new line
+ char *q;
+
+ q = prev_line(p); // use prev line as templet
+ for (; isblnk(*q); q++) {
+ p = stupid_insert(p, *q); // insert the char
+ }
+ }
+#endif
+ }
+ return p;
+}
+
+static char *stupid_insert(char * p, char c) // stupidly insert the char c at 'p'
+{
+ p = text_hole_make(p, 1);
+ if (p != 0) {
+ *p = c;
+ file_modified++; // has the file been modified
+ p++;
+ }
+ return p;
+}
+
+static char find_range(char ** start, char ** stop, char c)
+{
+ char *save_dot, *p, *q;
+ int cnt;
+
+ save_dot = dot;
+ p = q = dot;
+
+ if (strchr("cdy><", c)) {
+ // these cmds operate on whole lines
+ p = q = begin_line(p);
+ for (cnt = 1; cnt < cmdcnt; cnt++) {
+ q = next_line(q);
+ }
+ q = end_line(q);
+ } else if (strchr("^%$0bBeEft", c)) {
+ // These cmds operate on char positions
+ do_cmd(c); // execute movement cmd
+ q = dot;
+ } else if (strchr("wW", c)) {
+ do_cmd(c); // execute movement cmd
+ // if we are at the next word's first char
+ // step back one char
+ // but check the possibilities when it is true
+ if (dot > text && ((isspace(dot[-1]) && !isspace(dot[0]))
+ || (ispunct(dot[-1]) && !ispunct(dot[0]))
+ || (isalnum(dot[-1]) && !isalnum(dot[0]))))
+ dot--; // move back off of next word
+ if (dot > text && *dot == '\n')
+ dot--; // stay off NL
+ q = dot;
+ } else if (strchr("H-k{", c)) {
+ // these operate on multi-lines backwards
+ q = end_line(dot); // find NL
+ do_cmd(c); // execute movement cmd
+ dot_begin();
+ p = dot;
+ } else if (strchr("L+j}\r\n", c)) {
+ // these operate on multi-lines forwards
+ p = begin_line(dot);
+ do_cmd(c); // execute movement cmd
+ dot_end(); // find NL
+ q = dot;
+ } else {
+ c = 27; // error- return an ESC char
+ //break;
+ }
+ *start = p;
+ *stop = q;
+ if (q < p) {
+ *start = q;
+ *stop = p;
+ }
+ dot = save_dot;
+ return c;
+}
+
+static int st_test(char * p, int type, int dir, char * tested)
+{
+ char c, c0, ci;
+ int test, inc;
+
+ inc = dir;
+ c = c0 = p[0];
+ ci = p[inc];
+ test = 0;
+
+ if (type == S_BEFORE_WS) {
+ c = ci;
+ test = ((!isspace(c)) || c == '\n');
+ }
+ if (type == S_TO_WS) {
+ c = c0;
+ test = ((!isspace(c)) || c == '\n');
+ }
+ if (type == S_OVER_WS) {
+ c = c0;
+ test = ((isspace(c)));
+ }
+ if (type == S_END_PUNCT) {
+ c = ci;
+ test = ((ispunct(c)));
+ }
+ if (type == S_END_ALNUM) {
+ c = ci;
+ test = ((isalnum(c)) || c == '_');
+ }
+ *tested = c;
+ return test;
+}
+
+static char *skip_thing(char * p, int linecnt, int dir, int type)
+{
+ char c;
+
+ while (st_test(p, type, dir, &c)) {
+ // make sure we limit search to correct number of lines
+ if (c == '\n' && --linecnt < 1)
+ break;
+ if (dir >= 0 && p >= end - 1)
+ break;
+ if (dir < 0 && p <= text)
+ break;
+ p += dir; // move to next char
+ }
+ return p;
+}
+
+// find matching char of pair () [] {}
+static char *find_pair(char * p, char c)
+{
+ char match, *q;
+ int dir, level;
+
+ match = ')';
+ level = 1;
+ dir = 1; // assume forward
+ switch (c) {
+ case '(':
+ match = ')';
+ break;
+ case '[':
+ match = ']';
+ break;
+ case '{':
+ match = '}';
+ break;
+ case ')':
+ match = '(';
+ dir = -1;
+ break;
+ case ']':
+ match = '[';
+ dir = -1;
+ break;
+ case '}':
+ match = '{';
+ dir = -1;
+ break;
+ }
+ for (q = p + dir; text <= q && q < end; q += dir) {
+ // look for match, count levels of pairs (( ))
+ if (*q == c)
+ level++; // increase pair levels
+ if (*q == match)
+ level--; // reduce pair level
+ if (level == 0)
+ break; // found matching pair
+ }
+ if (level != 0)
+ q = NULL; // indicate no match
+ return q;
+}
+
+#if ENABLE_FEATURE_VI_SETOPTS
+// show the matching char of a pair, () [] {}
+static void showmatching(char * p)
+{
+ char *q, *save_dot;
+
+ // we found half of a pair
+ q = find_pair(p, *p); // get loc of matching char
+ if (q == NULL) {
+ indicate_error('3'); // no matching char
+ } else {
+ // "q" now points to matching pair
+ save_dot = dot; // remember where we are
+ dot = q; // go to new loc
+ refresh(FALSE); // let the user see it
+ mysleep(40); // give user some time
+ dot = save_dot; // go back to old loc
+ refresh(FALSE);
+ }
+}
+#endif /* FEATURE_VI_SETOPTS */
+
+// open a hole in text[]
+static char *text_hole_make(char * p, int size) // at "p", make a 'size' byte hole
+{
+ char *src, *dest;
+ int cnt;
+
+ if (size <= 0)
+ goto thm0;
+ src = p;
+ dest = p + size;
+ cnt = end - src; // the rest of buffer
+ if (memmove(dest, src, cnt) != dest) {
+ psbs("can't create room for new characters");
+ }
+ memset(p, ' ', size); // clear new hole
+ end = end + size; // adjust the new END
+ file_modified++; // has the file been modified
+ thm0:
+ return p;
+}
+
+// close a hole in text[]
+static char *text_hole_delete(char * p, char * q) // delete "p" thru "q", inclusive
+{
+ char *src, *dest;
+ int cnt, hole_size;
+
+ // move forwards, from beginning
+ // assume p <= q
+ src = q + 1;
+ dest = p;
+ if (q < p) { // they are backward- swap them
+ src = p + 1;
+ dest = q;
+ }
+ hole_size = q - p + 1;
+ cnt = end - src;
+ if (src < text || src > end)
+ goto thd0;
+ if (dest < text || dest >= end)
+ goto thd0;
+ if (src >= end)
+ goto thd_atend; // just delete the end of the buffer
+ if (memmove(dest, src, cnt) != dest) {
+ psbs("can't delete the character");
+ }
+ thd_atend:
+ end = end - hole_size; // adjust the new END
+ if (dest >= end)
+ dest = end - 1; // make sure dest in below end-1
+ if (end <= text)
+ dest = end = text; // keep pointers valid
+ file_modified++; // has the file been modified
+ thd0:
+ return dest;
+}
+
+// copy text into register, then delete text.
+// if dist <= 0, do not include, or go past, a NewLine
+//
+static char *yank_delete(char * start, char * stop, int dist, int yf)
+{
+ char *p;
+
+ // make sure start <= stop
+ if (start > stop) {
+ // they are backwards, reverse them
+ p = start;
+ start = stop;
+ stop = p;
+ }
+ if (dist <= 0) {
+ // we cannot cross NL boundaries
+ p = start;
+ if (*p == '\n')
+ return p;
+ // dont go past a NewLine
+ for (; p + 1 <= stop; p++) {
+ if (p[1] == '\n') {
+ stop = p; // "stop" just before NewLine
+ break;
+ }
+ }
+ }
+ p = start;
+#if ENABLE_FEATURE_VI_YANKMARK
+ text_yank(start, stop, YDreg);
+#endif
+ if (yf == YANKDEL) {
+ p = text_hole_delete(start, stop);
+ } // delete lines
+ return p;
+}
+
+static void show_help(void)
+{
+ puts("These features are available:"
+#if ENABLE_FEATURE_VI_SEARCH
+ "\n\tPattern searches with / and ?"
+#endif
+#if ENABLE_FEATURE_VI_DOT_CMD
+ "\n\tLast command repeat with \'.\'"
+#endif
+#if ENABLE_FEATURE_VI_YANKMARK
+ "\n\tLine marking with 'x"
+ "\n\tNamed buffers with \"x"
+#endif
+#if ENABLE_FEATURE_VI_READONLY
+ "\n\tReadonly if vi is called as \"view\""
+ "\n\tReadonly with -R command line arg"
+#endif
+#if ENABLE_FEATURE_VI_SET
+ "\n\tSome colon mode commands with \':\'"
+#endif
+#if ENABLE_FEATURE_VI_SETOPTS
+ "\n\tSettable options with \":set\""
+#endif
+#if ENABLE_FEATURE_VI_USE_SIGNALS
+ "\n\tSignal catching- ^C"
+ "\n\tJob suspend and resume with ^Z"
+#endif
+#if ENABLE_FEATURE_VI_WIN_RESIZE
+ "\n\tAdapt to window re-sizes"
+#endif
+ );
+}
+
+static inline void print_literal(char * buf, const char * s) // copy s to buf, convert unprintable
+{
+ unsigned char c;
+ char b[2];
+
+ b[1] = '\0';
+ buf[0] = '\0';
+ if (!s[0])
+ s = "(NULL)";
+ for (; *s; s++) {
+ int c_is_no_print;
+
+ c = *s;
+ c_is_no_print = (c & 0x80) && !Isprint(c);
+ if (c_is_no_print) {
+ strcat(buf, SOn);
+ c = '.';
+ }
+ if (c < ' ' || c == 127) {
+ strcat(buf, "^");
+ if (c == 127)
+ c = '?';
+ else
+ c += '@';
+ }
+ b[0] = c;
+ strcat(buf, b);
+ if (c_is_no_print)
+ strcat(buf, SOs);
+ if (*s == '\n')
+ strcat(buf, "$");
+ }
+}
+
+#if ENABLE_FEATURE_VI_DOT_CMD
+static void start_new_cmd_q(char c)
+{
+ // release old cmd
+ free(last_modifying_cmd);
+ // get buffer for new cmd
+ last_modifying_cmd = xmalloc(BUFSIZ);
+ memset(last_modifying_cmd, '\0', BUFSIZ); // clear new cmd queue
+ // if there is a current cmd count put it in the buffer first
+ if (cmdcnt > 0)
+ sprintf(last_modifying_cmd, "%d%c", cmdcnt, c);
+ else // just save char c onto queue
+ last_modifying_cmd[0] = c;
+ adding2q = 1;
+}
+
+static void end_cmd_q(void)
+{
+#if ENABLE_FEATURE_VI_YANKMARK
+ YDreg = 26; // go back to default Yank/Delete reg
+#endif
+ adding2q = 0;
+}
+#endif /* FEATURE_VI_DOT_CMD */
+
+#if ENABLE_FEATURE_VI_YANKMARK \
+ || (ENABLE_FEATURE_VI_COLON && ENABLE_FEATURE_VI_SEARCH) \
+ || ENABLE_FEATURE_VI_CRASHME
+static char *string_insert(char * p, char * s) // insert the string at 'p'
+{
+ int cnt, i;
+
+ i = strlen(s);
+ p = text_hole_make(p, i);
+ strncpy(p, s, i);
+ for (cnt = 0; *s != '\0'; s++) {
+ if (*s == '\n')
+ cnt++;
+ }
+#if ENABLE_FEATURE_VI_YANKMARK
+ psb("Put %d lines (%d chars) from [%c]", cnt, i, what_reg());
+#endif
+ return p;
+}
+#endif
+
+#if ENABLE_FEATURE_VI_YANKMARK
+static char *text_yank(char * p, char * q, int dest) // copy text into a register
+{
+ char *t;
+ int cnt;
+
+ if (q < p) { // they are backwards- reverse them
+ t = q;
+ q = p;
+ p = t;
+ }
+ cnt = q - p + 1;
+ t = reg[dest];
+ free(t); // if already a yank register, free it
+ t = xmalloc(cnt + 1); // get a new register
+ memset(t, '\0', cnt + 1); // clear new text[]
+ strncpy(t, p, cnt); // copy text[] into bufer
+ reg[dest] = t;
+ return p;
+}
+
+static char what_reg(void)
+{
+ char c;
+
+ c = 'D'; // default to D-reg
+ if (0 <= YDreg && YDreg <= 25)
+ c = 'a' + (char) YDreg;
+ if (YDreg == 26)
+ c = 'D';
+ if (YDreg == 27)
+ c = 'U';
+ return c;
+}
+
+static void check_context(char cmd)
+{
+ // A context is defined to be "modifying text"
+ // Any modifying command establishes a new context.
+
+ if (dot < context_start || dot > context_end) {
+ if (strchr(modifying_cmds, cmd) != NULL) {
+ // we are trying to modify text[]- make this the current context
+ mark[27] = mark[26]; // move cur to prev
+ mark[26] = dot; // move local to cur
+ context_start = prev_line(prev_line(dot));
+ context_end = next_line(next_line(dot));
+ //loiter= start_loiter= now;
+ }
+ }
+}
+
+static inline char *swap_context(char * p) // goto new context for '' command make this the current context
+{
+ char *tmp;
+
+ // the current context is in mark[26]
+ // the previous context is in mark[27]
+ // only swap context if other context is valid
+ if (text <= mark[27] && mark[27] <= end - 1) {
+ tmp = mark[27];
+ mark[27] = mark[26];
+ mark[26] = tmp;
+ p = mark[26]; // where we are going- previous context
+ context_start = prev_line(prev_line(prev_line(p)));
+ context_end = next_line(next_line(next_line(p)));
+ }
+ return p;
+}
+#endif /* FEATURE_VI_YANKMARK */
+
+static int isblnk(char c) // is the char a blank or tab
+{
+ return (c == ' ' || c == '\t');
+}
+
+//----- Set terminal attributes --------------------------------
+static void rawmode(void)
+{
+ tcgetattr(0, &term_orig);
+ term_vi = term_orig;
+ term_vi.c_lflag &= (~ICANON & ~ECHO); // leave ISIG ON- allow intr's
+ term_vi.c_iflag &= (~IXON & ~ICRNL);
+ term_vi.c_oflag &= (~ONLCR);
+ term_vi.c_cc[VMIN] = 1;
+ term_vi.c_cc[VTIME] = 0;
+ erase_char = term_vi.c_cc[VERASE];
+ tcsetattr(0, TCSANOW, &term_vi);
+}
+
+static void cookmode(void)
+{
+ fflush(stdout);
+ tcsetattr(0, TCSANOW, &term_orig);
+}
+
+//----- Come here when we get a window resize signal ---------
+#if ENABLE_FEATURE_VI_USE_SIGNALS
+static void winch_sig(int sig ATTRIBUTE_UNUSED)
+{
+ signal(SIGWINCH, winch_sig);
+ if (ENABLE_FEATURE_VI_WIN_RESIZE)
+ get_terminal_width_height(0, &columns, &rows);
+ new_screen(rows, columns); // get memory for virtual screen
+ redraw(TRUE); // re-draw the screen
+}
+
+//----- Come here when we get a continue signal -------------------
+static void cont_sig(int sig ATTRIBUTE_UNUSED)
+{
+ rawmode(); // terminal to "raw"
+ last_status_cksum = 0; // force status update
+ redraw(TRUE); // re-draw the screen
+
+ signal(SIGTSTP, suspend_sig);
+ signal(SIGCONT, SIG_DFL);
+ kill(my_pid, SIGCONT);
+}
+
+//----- Come here when we get a Suspend signal -------------------
+static void suspend_sig(int sig ATTRIBUTE_UNUSED)
+{
+ place_cursor(rows - 1, 0, FALSE); // go to bottom of screen
+ clear_to_eol(); // Erase to end of line
+ cookmode(); // terminal to "cooked"
+
+ signal(SIGCONT, cont_sig);
+ signal(SIGTSTP, SIG_DFL);
+ kill(my_pid, SIGTSTP);
+}
+
+//----- Come here when we get a signal ---------------------------
+static void catch_sig(int sig)
+{
+ signal(SIGINT, catch_sig);
+ if (sig)
+ longjmp(restart, sig);
+}
+#endif /* FEATURE_VI_USE_SIGNALS */
+
+static int mysleep(int hund) // sleep for 'h' 1/100 seconds
+{
+ // Don't hang- Wait 5/100 seconds- 1 Sec= 1000000
+ fflush(stdout);
+ FD_ZERO(&rfds);
+ FD_SET(0, &rfds);
+ tv.tv_sec = 0;
+ tv.tv_usec = hund * 10000;
+ select(1, &rfds, NULL, NULL, &tv);
+ return FD_ISSET(0, &rfds);
+}
+
+#define readbuffer bb_common_bufsiz1
+
+static int readed_for_parse;
+
+//----- IO Routines --------------------------------------------
+static char readit(void) // read (maybe cursor) key from stdin
+{
+ char c;
+ int n;
+ struct esc_cmds {
+ const char *seq;
+ char val;
+ };
+
+ static const struct esc_cmds esccmds[] = {
+ {"OA", VI_K_UP}, // cursor key Up
+ {"OB", VI_K_DOWN}, // cursor key Down
+ {"OC", VI_K_RIGHT}, // Cursor Key Right
+ {"OD", VI_K_LEFT}, // cursor key Left
+ {"OH", VI_K_HOME}, // Cursor Key Home
+ {"OF", VI_K_END}, // Cursor Key End
+ {"[A", VI_K_UP}, // cursor key Up
+ {"[B", VI_K_DOWN}, // cursor key Down
+ {"[C", VI_K_RIGHT}, // Cursor Key Right
+ {"[D", VI_K_LEFT}, // cursor key Left
+ {"[H", VI_K_HOME}, // Cursor Key Home
+ {"[F", VI_K_END}, // Cursor Key End
+ {"[1~", VI_K_HOME}, // Cursor Key Home
+ {"[2~", VI_K_INSERT}, // Cursor Key Insert
+ {"[4~", VI_K_END}, // Cursor Key End
+ {"[5~", VI_K_PAGEUP}, // Cursor Key Page Up
+ {"[6~", VI_K_PAGEDOWN},// Cursor Key Page Down
+ {"OP", VI_K_FUN1}, // Function Key F1
+ {"OQ", VI_K_FUN2}, // Function Key F2
+ {"OR", VI_K_FUN3}, // Function Key F3
+ {"OS", VI_K_FUN4}, // Function Key F4
+ {"[15~", VI_K_FUN5}, // Function Key F5
+ {"[17~", VI_K_FUN6}, // Function Key F6
+ {"[18~", VI_K_FUN7}, // Function Key F7
+ {"[19~", VI_K_FUN8}, // Function Key F8
+ {"[20~", VI_K_FUN9}, // Function Key F9
+ {"[21~", VI_K_FUN10}, // Function Key F10
+ {"[23~", VI_K_FUN11}, // Function Key F11
+ {"[24~", VI_K_FUN12}, // Function Key F12
+ {"[11~", VI_K_FUN1}, // Function Key F1
+ {"[12~", VI_K_FUN2}, // Function Key F2
+ {"[13~", VI_K_FUN3}, // Function Key F3
+ {"[14~", VI_K_FUN4}, // Function Key F4
+ };
+
+#define ESCCMDS_COUNT (sizeof(esccmds)/sizeof(struct esc_cmds))
+
+ alarm(0); // turn alarm OFF while we wait for input
+ fflush(stdout);
+ n = readed_for_parse;
+ // get input from User- are there already input chars in Q?
+ if (n <= 0) {
+ ri0:
+ // the Q is empty, wait for a typed char
+ n = read(0, readbuffer, BUFSIZ - 1);
+ if (n < 0) {
+ if (errno == EINTR)
+ goto ri0; // interrupted sys call
+ if (errno == EBADF)
+ editing = 0;
+ if (errno == EFAULT)
+ editing = 0;
+ if (errno == EINVAL)
+ editing = 0;
+ if (errno == EIO)
+ editing = 0;
+ errno = 0;
+ }
+ if (n <= 0)
+ return 0; // error
+ if (readbuffer[0] == 27) {
+ // This is an ESC char. Is this Esc sequence?
+ // Could be bare Esc key. See if there are any
+ // more chars to read after the ESC. This would
+ // be a Function or Cursor Key sequence.
+ FD_ZERO(&rfds);
+ FD_SET(0, &rfds);
+ tv.tv_sec = 0;
+ tv.tv_usec = 50000; // Wait 5/100 seconds- 1 Sec=1000000
+
+ // keep reading while there are input chars and room in buffer
+ while (select(1, &rfds, NULL, NULL, &tv) > 0 && n <= (BUFSIZ - 5)) {
+ // read the rest of the ESC string
+ int r = read(0, (void *) (readbuffer + n), BUFSIZ - n);
+ if (r > 0) {
+ n += r;
+ }
+ }
+ }
+ readed_for_parse = n;
+ }
+ c = readbuffer[0];
+ if (c == 27 && n > 1) {
+ // Maybe cursor or function key?
+ const struct esc_cmds *eindex;
+
+ for (eindex = esccmds; eindex < &esccmds[ESCCMDS_COUNT]; eindex++) {
+ int cnt = strlen(eindex->seq);
+
+ if (n <= cnt)
+ continue;
+ if (strncmp(eindex->seq, readbuffer + 1, cnt))
+ continue;
+ // is a Cursor key- put derived value back into Q
+ c = eindex->val;
+ // for squeeze out the ESC sequence
+ n = cnt + 1;
+ break;
+ }
+ if (eindex == &esccmds[ESCCMDS_COUNT]) {
+ /* defined ESC sequence not found, set only one ESC */
+ n = 1;
+ }
+ } else {
+ n = 1;
+ }
+ // remove key sequence from Q
+ readed_for_parse -= n;
+ memmove(readbuffer, readbuffer + n, BUFSIZ - n);
+ alarm(3); // we are done waiting for input, turn alarm ON
+ return c;
+}
+
+//----- IO Routines --------------------------------------------
+static char get_one_char(void)
+{
+ static char c;
+
+#if ENABLE_FEATURE_VI_DOT_CMD
+ // ! adding2q && ioq == 0 read()
+ // ! adding2q && ioq != 0 *ioq
+ // adding2q *last_modifying_cmd= read()
+ if (!adding2q) {
+ // we are not adding to the q.
+ // but, we may be reading from a q
+ if (ioq == 0) {
+ // there is no current q, read from STDIN
+ c = readit(); // get the users input
+ } else {
+ // there is a queue to get chars from first
+ c = *ioq++;
+ if (c == '\0') {
+ // the end of the q, read from STDIN
+ free(ioq_start);
+ ioq_start = ioq = 0;
+ c = readit(); // get the users input
+ }
+ }
+ } else {
+ // adding STDIN chars to q
+ c = readit(); // get the users input
+ if (last_modifying_cmd != 0) {
+ int len = strlen(last_modifying_cmd);
+ if (len + 1 >= BUFSIZ) {
+ psbs("last_modifying_cmd overrun");
+ } else {
+ // add new char to q
+ last_modifying_cmd[len] = c;
+ }
+ }
+ }
+#else
+ c = readit(); // get the users input
+#endif /* FEATURE_VI_DOT_CMD */
+ return c; // return the char, where ever it came from
+}
+
+static char *get_input_line(const char * prompt) // get input line- use "status line"
+{
+ static char *obufp;
+
+ char buf[BUFSIZ];
+ char c;
+ int i;
+
+ strcpy(buf, prompt);
+ last_status_cksum = 0; // force status update
+ place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
+ clear_to_eol(); // clear the line
+ write1(prompt); // write out the :, /, or ? prompt
+
+ i = strlen(buf);
+ while (i < BUFSIZ) {
+ c = get_one_char(); // read user input
+ if (c == '\n' || c == '\r' || c == 27)
+ break; // is this end of input
+ if (c == erase_char || c == 8 || c == 127) {
+ // user wants to erase prev char
+ i--; // backup to prev char
+ buf[i] = '\0'; // erase the char
+ buf[i + 1] = '\0'; // null terminate buffer
+ write1("\b \b"); // erase char on screen
+ if (i <= 0) { // user backs up before b-o-l, exit
+ break;
+ }
+ } else {
+ buf[i] = c; // save char in buffer
+ buf[i + 1] = '\0'; // make sure buffer is null terminated
+ putchar(c); // echo the char back to user
+ i++;
+ }
+ }
+ refresh(FALSE);
+ free(obufp);
+ obufp = xstrdup(buf);
+ return obufp;
+}
+
+static int file_size(const char * fn) // what is the byte size of "fn"
+{
+ struct stat st_buf;
+ int cnt, sr;
+
+ if (!fn || !fn[0])
+ return -1;
+ cnt = -1;
+ sr = stat(fn, &st_buf); // see if file exists
+ if (sr >= 0) {
+ cnt = (int) st_buf.st_size;
+ }
+ return cnt;
+}
+
+static int file_insert(char * fn, char * p, int size)
+{
+ int fd, cnt;
+
+ cnt = -1;
+#if ENABLE_FEATURE_VI_READONLY
+ readonly = FALSE;
+#endif
+ if (!fn || !fn[0]) {
+ psbs("No filename given");
+ goto fi0;
+ }
+ if (size == 0) {
+ // OK- this is just a no-op
+ cnt = 0;
+ goto fi0;
+ }
+ if (size < 0) {
+ psbs("Trying to insert a negative number (%d) of characters", size);
+ goto fi0;
+ }
+ if (p < text || p > end) {
+ psbs("Trying to insert file outside of memory");
+ goto fi0;
+ }
+
+ // see if we can open the file
+#if ENABLE_FEATURE_VI_READONLY
+ if (vi_readonly) goto fi1; // do not try write-mode
+#endif
+ fd = open(fn, O_RDWR); // assume read & write
+ if (fd < 0) {
+ // could not open for writing- maybe file is read only
+#if ENABLE_FEATURE_VI_READONLY
+ fi1:
+#endif
+ fd = open(fn, O_RDONLY); // try read-only
+ if (fd < 0) {
+ psbs("\"%s\" %s", fn, "cannot open file");
+ goto fi0;
+ }
+#if ENABLE_FEATURE_VI_READONLY
+ // got the file- read-only
+ readonly = TRUE;
+#endif
+ }
+ p = text_hole_make(p, size);
+ cnt = read(fd, p, size);
+ close(fd);
+ if (cnt < 0) {
+ cnt = -1;
+ p = text_hole_delete(p, p + size - 1); // un-do buffer insert
+ psbs("cannot read file \"%s\"", fn);
+ } else if (cnt < size) {
+ // There was a partial read, shrink unused space text[]
+ p = text_hole_delete(p + cnt, p + (size - cnt) - 1); // un-do buffer insert
+ psbs("cannot read all of file \"%s\"", fn);
+ }
+ if (cnt >= size)
+ file_modified++;
+ fi0:
+ return cnt;
+}
+
+static int file_write(char * fn, char * first, char * last)
+{
+ int fd, cnt, charcnt;
+
+ if (fn == 0) {
+ psbs("No current filename");
+ return -2;
+ }
+ charcnt = 0;
+ // FIXIT- use the correct umask()
+ fd = open(fn, (O_WRONLY | O_CREAT | O_TRUNC), 0664);
+ if (fd < 0)
+ return -1;
+ cnt = last - first + 1;
+ charcnt = write(fd, first, cnt);
+ if (charcnt == cnt) {
+ // good write
+ //file_modified = FALSE; // the file has not been modified
+ } else {
+ charcnt = 0;
+ }
+ close(fd);
+ return charcnt;
+}
+
+//----- Terminal Drawing ---------------------------------------
+// The terminal is made up of 'rows' line of 'columns' columns.
+// classically this would be 24 x 80.
+// screen coordinates
+// 0,0 ... 0,79
+// 1,0 ... 1,79
+// . ... .
+// . ... .
+// 22,0 ... 22,79
+// 23,0 ... 23,79 status line
+//
+
+//----- Move the cursor to row x col (count from 0, not 1) -------
+static void place_cursor(int row, int col, int opti)
+{
+ char cm1[BUFSIZ];
+ char *cm;
+#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
+ char cm2[BUFSIZ];
+ char *screenp;
+ // char cm3[BUFSIZ];
+ int Rrow = last_row;
+#endif
+
+ memset(cm1, '\0', BUFSIZ - 1); // clear the buffer
+
+ if (row < 0) row = 0;
+ if (row >= rows) row = rows - 1;
+ if (col < 0) col = 0;
+ if (col >= columns) col = columns - 1;
+
+ //----- 1. Try the standard terminal ESC sequence
+ sprintf(cm1, CMrc, row + 1, col + 1);
+ cm = cm1;
+ if (!opti)
+ goto pc0;
+
+#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
+ //----- find the minimum # of chars to move cursor -------------
+ //----- 2. Try moving with discreet chars (Newline, [back]space, ...)
+ memset(cm2, '\0', BUFSIZ - 1); // clear the buffer
+
+ // move to the correct row
+ while (row < Rrow) {
+ // the cursor has to move up
+ strcat(cm2, CMup);
+ Rrow--;
+ }
+ while (row > Rrow) {
+ // the cursor has to move down
+ strcat(cm2, CMdown);
+ Rrow++;
+ }
+
+ // now move to the correct column
+ strcat(cm2, "\r"); // start at col 0
+ // just send out orignal source char to get to correct place
+ screenp = &screen[row * columns]; // start of screen line
+ strncat(cm2, screenp, col);
+
+ //----- 3. Try some other way of moving cursor
+ //---------------------------------------------
+
+ // pick the shortest cursor motion to send out
+ cm = cm1;
+ if (strlen(cm2) < strlen(cm)) {
+ cm = cm2;
+ } /* else if (strlen(cm3) < strlen(cm)) {
+ cm= cm3;
+ } */
+#endif /* FEATURE_VI_OPTIMIZE_CURSOR */
+ pc0:
+ write1(cm); // move the cursor
+}
+
+//----- Erase from cursor to end of line -----------------------
+static void clear_to_eol(void)
+{
+ write1(Ceol); // Erase from cursor to end of line
+}
+
+//----- Erase from cursor to end of screen -----------------------
+static void clear_to_eos(void)
+{
+ write1(Ceos); // Erase from cursor to end of screen
+}
+
+//----- Start standout mode ------------------------------------
+static void standout_start(void) // send "start reverse video" sequence
+{
+ write1(SOs); // Start reverse video mode
+}
+
+//----- End standout mode --------------------------------------
+static void standout_end(void) // send "end reverse video" sequence
+{
+ write1(SOn); // End reverse video mode
+}
+
+//----- Flash the screen --------------------------------------
+static void flash(int h)
+{
+ standout_start(); // send "start reverse video" sequence
+ redraw(TRUE);
+ mysleep(h);
+ standout_end(); // send "end reverse video" sequence
+ redraw(TRUE);
+}
+
+static void Indicate_Error(void)
+{
+#if ENABLE_FEATURE_VI_CRASHME
+ if (crashme > 0)
+ return; // generate a random command
+#endif
+ if (!err_method) {
+ write1(bell); // send out a bell character
+ } else {
+ flash(10);
+ }
+}
+
+//----- Screen[] Routines --------------------------------------
+//----- Erase the Screen[] memory ------------------------------
+static void screen_erase(void)
+{
+ memset(screen, ' ', screensize); // clear new screen
+}
+
+static int bufsum(char *buf, int count)
+{
+ int sum = 0;
+ char *e = buf + count;
+
+ while (buf < e)
+ sum += (unsigned char) *buf++;
+ return sum;
+}
+
+//----- Draw the status line at bottom of the screen -------------
+static void show_status_line(void)
+{
+ int cnt = 0, cksum = 0;
+
+ // either we already have an error or status message, or we
+ // create one.
+ if (!have_status_msg) {
+ cnt = format_edit_status();
+ cksum = bufsum(status_buffer, cnt);
+ }
+ if (have_status_msg || ((cnt > 0 && last_status_cksum != cksum))) {
+ last_status_cksum= cksum; // remember if we have seen this line
+ place_cursor(rows - 1, 0, FALSE); // put cursor on status line
+ write1(status_buffer);
+ clear_to_eol();
+ if (have_status_msg) {
+ if (((int)strlen(status_buffer) - (have_status_msg - 1)) >
+ (columns - 1) ) {
+ have_status_msg = 0;
+ Hit_Return();
+ }
+ have_status_msg = 0;
+ }
+ place_cursor(crow, ccol, FALSE); // put cursor back in correct place
+ }
+ fflush(stdout);
+}
+
+//----- format the status buffer, the bottom line of screen ------
+// format status buffer, with STANDOUT mode
+static void psbs(const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ strcpy(status_buffer, SOs); // Terminal standout mode on
+ vsprintf(status_buffer + strlen(status_buffer), format, args);
+ strcat(status_buffer, SOn); // Terminal standout mode off
+ va_end(args);
+
+ have_status_msg = 1 + sizeof(SOs) + sizeof(SOn) - 2;
+}
+
+// format status buffer
+static void psb(const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ vsprintf(status_buffer, format, args);
+ va_end(args);
+
+ have_status_msg = 1;
+}
+
+static void ni(const char * s) // display messages
+{
+ char buf[BUFSIZ];
+
+ print_literal(buf, s);
+ psbs("\'%s\' is not implemented", buf);
+}
+
+static int format_edit_status(void) // show file status on status line
+{
+ static int tot;
+
+ int cur, percent, ret, trunc_at;
+
+ // file_modified is now a counter rather than a flag. this
+ // helps reduce the amount of line counting we need to do.
+ // (this will cause a mis-reporting of modified status
+ // once every MAXINT editing operations.)
+
+ // it would be nice to do a similar optimization here -- if
+ // we haven't done a motion that could have changed which line
+ // we're on, then we shouldn't have to do this count_lines()
+ cur = count_lines(text, dot);
+
+ // reduce counting -- the total lines can't have
+ // changed if we haven't done any edits.
+ if (file_modified != last_file_modified) {
+ tot = cur + count_lines(dot, end - 1) - 1;
+ last_file_modified = file_modified;
+ }
+
+ // current line percent
+ // ------------- ~~ ----------
+ // total lines 100
+ if (tot > 0) {
+ percent = (100 * cur) / tot;
+ } else {
+ cur = tot = 0;
+ percent = 100;
+ }
+
+ trunc_at = columns < STATUS_BUFFER_LEN-1 ?
+ columns : STATUS_BUFFER_LEN-1;
+
+ ret = snprintf(status_buffer, trunc_at+1,
+#if ENABLE_FEATURE_VI_READONLY
+ "%c %s%s%s %d/%d %d%%",
+#else
+ "%c %s%s %d/%d %d%%",
+#endif
+ (cmd_mode ? (cmd_mode == 2 ? 'R':'I'):'-'),
+ (cfn != 0 ? cfn : "No file"),
+#if ENABLE_FEATURE_VI_READONLY
+ ((vi_readonly || readonly) ? " [Read-only]" : ""),
+#endif
+ (file_modified ? " [modified]" : ""),
+ cur, tot, percent);
+
+ if (ret >= 0 && ret < trunc_at)
+ return ret; /* it all fit */
+
+ return trunc_at; /* had to truncate */
+}
+
+//----- Force refresh of all Lines -----------------------------
+static void redraw(int full_screen)
+{
+ place_cursor(0, 0, FALSE); // put cursor in correct place
+ clear_to_eos(); // tel terminal to erase display
+ screen_erase(); // erase the internal screen buffer
+ last_status_cksum = 0; // force status update
+ refresh(full_screen); // this will redraw the entire display
+ show_status_line();
+}
+
+//----- Format a text[] line into a buffer ---------------------
+static void format_line(char *dest, char *src, int li)
+{
+ int co;
+ char c;
+
+ for (co = 0; co < MAX_SCR_COLS; co++) {
+ c = ' '; // assume blank
+ if (li > 0 && co == 0) {
+ c = '~'; // not first line, assume Tilde
+ }
+ // are there chars in text[] and have we gone past the end
+ if (text < end && src < end) {
+ c = *src++;
+ }
+ if (c == '\n')
+ break;
+ if ((c & 0x80) && !Isprint(c)) {
+ c = '.';
+ }
+ if ((unsigned char)(c) < ' ' || c == 0x7f) {
+ if (c == '\t') {
+ c = ' ';
+ // co % 8 != 7
+ for (; (co % tabstop) != (tabstop - 1); co++) {
+ dest[co] = c;
+ }
+ } else {
+ dest[co++] = '^';
+ if (c == 0x7f)
+ c = '?';
+ else
+ c += '@'; // make it visible
+ }
+ }
+ // the co++ is done here so that the column will
+ // not be overwritten when we blank-out the rest of line
+ dest[co] = c;
+ if (src >= end)
+ break;
+ }
+}
+
+//----- Refresh the changed screen lines -----------------------
+// Copy the source line from text[] into the buffer and note
+// if the current screenline is different from the new buffer.
+// If they differ then that line needs redrawing on the terminal.
+//
+static void refresh(int full_screen)
+{
+ static int old_offset;
+
+ int li, changed;
+ char buf[MAX_SCR_COLS];
+ char *tp, *sp; // pointer into text[] and screen[]
+#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
+ int last_li = -2; // last line that changed- for optimizing cursor movement
+#endif
+
+ if (ENABLE_FEATURE_VI_WIN_RESIZE)
+ get_terminal_width_height(0, &columns, &rows);
+ sync_cursor(dot, &crow, &ccol); // where cursor will be (on "dot")
+ tp = screenbegin; // index into text[] of top line
+
+ // compare text[] to screen[] and mark screen[] lines that need updating
+ for (li = 0; li < rows - 1; li++) {
+ int cs, ce; // column start & end
+ memset(buf, ' ', MAX_SCR_COLS); // blank-out the buffer
+ buf[MAX_SCR_COLS-1] = 0; // NULL terminate the buffer
+ // format current text line into buf
+ format_line(buf, tp, li);
+
+ // skip to the end of the current text[] line
+ while (tp < end && *tp++ != '\n') /*no-op*/ ;
+
+ // see if there are any changes between vitual screen and buf
+ changed = FALSE; // assume no change
+ cs= 0;
+ ce= columns-1;
+ sp = &screen[li * columns]; // start of screen line
+ if (full_screen) {
+ // force re-draw of every single column from 0 - columns-1
+ goto re0;
+ }
+ // compare newly formatted buffer with virtual screen
+ // look forward for first difference between buf and screen
+ for (; cs <= ce; cs++) {
+ if (buf[cs + offset] != sp[cs]) {
+ changed = TRUE; // mark for redraw
+ break;
+ }
+ }
+
+ // look backward for last difference between buf and screen
+ for ( ; ce >= cs; ce--) {
+ if (buf[ce + offset] != sp[ce]) {
+ changed = TRUE; // mark for redraw
+ break;
+ }
+ }
+ // now, cs is index of first diff, and ce is index of last diff
+
+ // if horz offset has changed, force a redraw
+ if (offset != old_offset) {
+ re0:
+ changed = TRUE;
+ }
+
+ // make a sanity check of columns indexes
+ if (cs < 0) cs= 0;
+ if (ce > columns-1) ce= columns-1;
+ if (cs > ce) { cs= 0; ce= columns-1; }
+ // is there a change between vitual screen and buf
+ if (changed) {
+ // copy changed part of buffer to virtual screen
+ memmove(sp+cs, buf+(cs+offset), ce-cs+1);
+
+ // move cursor to column of first change
+ if (offset != old_offset) {
+ // opti_cur_move is still too stupid
+ // to handle offsets correctly
+ place_cursor(li, cs, FALSE);
+ } else {
+#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
+ // if this just the next line
+ // try to optimize cursor movement
+ // otherwise, use standard ESC sequence
+ place_cursor(li, cs, li == (last_li+1) ? TRUE : FALSE);
+ last_li= li;
+#else
+ place_cursor(li, cs, FALSE); // use standard ESC sequence
+#endif /* FEATURE_VI_OPTIMIZE_CURSOR */
+ }
+
+ // write line out to terminal
+ {
+ int nic = ce - cs + 1;
+ char *out = sp + cs;
+
+ while (nic-- > 0) {
+ putchar(*out);
+ out++;
+ }
+ }
+#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
+ last_row = li;
+#endif
+ }
+ }
+
+#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
+ place_cursor(crow, ccol, (crow == last_row) ? TRUE : FALSE);
+ last_row = crow;
+#else
+ place_cursor(crow, ccol, FALSE);
+#endif
+
+ if (offset != old_offset)
+ old_offset = offset;
+}
+
+//---------------------------------------------------------------------
+//----- the Ascii Chart -----------------------------------------------
+//
+// 00 nul 01 soh 02 stx 03 etx 04 eot 05 enq 06 ack 07 bel
+// 08 bs 09 ht 0a nl 0b vt 0c np 0d cr 0e so 0f si
+// 10 dle 11 dc1 12 dc2 13 dc3 14 dc4 15 nak 16 syn 17 etb
+// 18 can 19 em 1a sub 1b esc 1c fs 1d gs 1e rs 1f us
+// 20 sp 21 ! 22 " 23 # 24 $ 25 % 26 & 27 '
+// 28 ( 29 ) 2a * 2b + 2c , 2d - 2e . 2f /
+// 30 0 31 1 32 2 33 3 34 4 35 5 36 6 37 7
+// 38 8 39 9 3a : 3b ; 3c < 3d = 3e > 3f ?
+// 40 @ 41 A 42 B 43 C 44 D 45 E 46 F 47 G
+// 48 H 49 I 4a J 4b K 4c L 4d M 4e N 4f O
+// 50 P 51 Q 52 R 53 S 54 T 55 U 56 V 57 W
+// 58 X 59 Y 5a Z 5b [ 5c \ 5d ] 5e ^ 5f _
+// 60 ` 61 a 62 b 63 c 64 d 65 e 66 f 67 g
+// 68 h 69 i 6a j 6b k 6c l 6d m 6e n 6f o
+// 70 p 71 q 72 r 73 s 74 t 75 u 76 v 77 w
+// 78 x 79 y 7a z 7b { 7c | 7d } 7e ~ 7f del
+//---------------------------------------------------------------------
+
+//----- Execute a Vi Command -----------------------------------
+static void do_cmd(char c)
+{
+ const char *msg;
+ char c1, *p, *q, buf[9], *save_dot;
+ int cnt, i, j, dir, yf;
+
+ c1 = c; // quiet the compiler
+ cnt = yf = dir = 0; // quiet the compiler
+ msg = p = q = save_dot = buf; // quiet the compiler
+ memset(buf, '\0', 9); // clear buf
+
+ show_status_line();
+
+ /* if this is a cursor key, skip these checks */
+ switch (c) {
+ case VI_K_UP:
+ case VI_K_DOWN:
+ case VI_K_LEFT:
+ case VI_K_RIGHT:
+ case VI_K_HOME:
+ case VI_K_END:
+ case VI_K_PAGEUP:
+ case VI_K_PAGEDOWN:
+ goto key_cmd_mode;
+ }
+
+ if (cmd_mode == 2) {
+ // flip-flop Insert/Replace mode
+ if (c == VI_K_INSERT)
+ goto dc_i;
+ // we are 'R'eplacing the current *dot with new char
+ if (*dot == '\n') {
+ // don't Replace past E-o-l
+ cmd_mode = 1; // convert to insert
+ } else {
+ if (1 <= c || Isprint(c)) {
+ if (c != 27)
+ dot = yank_delete(dot, dot, 0, YANKDEL); // delete char
+ dot = char_insert(dot, c); // insert new char
+ }
+ goto dc1;
+ }
+ }
+ if (cmd_mode == 1) {
+ // hitting "Insert" twice means "R" replace mode
+ if (c == VI_K_INSERT) goto dc5;
+ // insert the char c at "dot"
+ if (1 <= c || Isprint(c)) {
+ dot = char_insert(dot, c);
+ }
+ goto dc1;
+ }
+
+ key_cmd_mode:
+ switch (c) {
+ //case 0x01: // soh
+ //case 0x09: // ht
+ //case 0x0b: // vt
+ //case 0x0e: // so
+ //case 0x0f: // si
+ //case 0x10: // dle
+ //case 0x11: // dc1
+ //case 0x13: // dc3
+#if ENABLE_FEATURE_VI_CRASHME
+ case 0x14: // dc4 ctrl-T
+ crashme = (crashme == 0) ? 1 : 0;
+ break;
+#endif
+ //case 0x16: // syn
+ //case 0x17: // etb
+ //case 0x18: // can
+ //case 0x1c: // fs
+ //case 0x1d: // gs
+ //case 0x1e: // rs
+ //case 0x1f: // us
+ //case '!': // !-
+ //case '#': // #-
+ //case '&': // &-
+ //case '(': // (-
+ //case ')': // )-
+ //case '*': // *-
+ //case ',': // ,-
+ //case '=': // =-
+ //case '@': // @-
+ //case 'F': // F-
+ //case 'K': // K-
+ //case 'Q': // Q-
+ //case 'S': // S-
+ //case 'T': // T-
+ //case 'V': // V-
+ //case '[': // [-
+ //case '\\': // \-
+ //case ']': // ]-
+ //case '_': // _-
+ //case '`': // `-
+ //case 'g': // g-
+ //case 'u': // u- FIXME- there is no undo
+ //case 'v': // v-
+ default: // unrecognised command
+ buf[0] = c;
+ buf[1] = '\0';
+ if (c < ' ') {
+ buf[0] = '^';
+ buf[1] = c + '@';
+ buf[2] = '\0';
+ }
+ ni(buf);
+ end_cmd_q(); // stop adding to q
+ case 0x00: // nul- ignore
+ break;
+ case 2: // ctrl-B scroll up full screen
+ case VI_K_PAGEUP: // Cursor Key Page Up
+ dot_scroll(rows - 2, -1);
+ break;
+#if ENABLE_FEATURE_VI_USE_SIGNALS
+ case 0x03: // ctrl-C interrupt
+ longjmp(restart, 1);
+ break;
+ case 26: // ctrl-Z suspend
+ suspend_sig(SIGTSTP);
+ break;
+#endif
+ case 4: // ctrl-D scroll down half screen
+ dot_scroll((rows - 2) / 2, 1);
+ break;
+ case 5: // ctrl-E scroll down one line
+ dot_scroll(1, 1);
+ break;
+ case 6: // ctrl-F scroll down full screen
+ case VI_K_PAGEDOWN: // Cursor Key Page Down
+ dot_scroll(rows - 2, 1);
+ break;
+ case 7: // ctrl-G show current status
+ last_status_cksum = 0; // force status update
+ break;
+ case 'h': // h- move left
+ case VI_K_LEFT: // cursor key Left
+ case 8: // ctrl-H- move left (This may be ERASE char)
+ case 0x7f: // DEL- move left (This may be ERASE char)
+ if (cmdcnt-- > 1) {
+ do_cmd(c);
+ } // repeat cnt
+ dot_left();
+ break;
+ case 10: // Newline ^J
+ case 'j': // j- goto next line, same col
+ case VI_K_DOWN: // cursor key Down
+ if (cmdcnt-- > 1) {
+ do_cmd(c);
+ } // repeat cnt
+ dot_next(); // go to next B-o-l
+ dot = move_to_col(dot, ccol + offset); // try stay in same col
+ break;
+ case 12: // ctrl-L force redraw whole screen
+ case 18: // ctrl-R force redraw
+ place_cursor(0, 0, FALSE); // put cursor in correct place
+ clear_to_eos(); // tel terminal to erase display
+ mysleep(10);
+ screen_erase(); // erase the internal screen buffer
+ last_status_cksum = 0; // force status update
+ refresh(TRUE); // this will redraw the entire display
+ break;
+ case 13: // Carriage Return ^M
+ case '+': // +- goto next line
+ if (cmdcnt-- > 1) {
+ do_cmd(c);
+ } // repeat cnt
+ dot_next();
+ dot_skip_over_ws();
+ break;
+ case 21: // ctrl-U scroll up half screen
+ dot_scroll((rows - 2) / 2, -1);
+ break;
+ case 25: // ctrl-Y scroll up one line
+ dot_scroll(1, -1);
+ break;
+ case 27: // esc
+ if (cmd_mode == 0)
+ indicate_error(c);
+ cmd_mode = 0; // stop insrting
+ end_cmd_q();
+ last_status_cksum = 0; // force status update
+ break;
+ case ' ': // move right
+ case 'l': // move right
+ case VI_K_RIGHT: // Cursor Key Right
+ if (cmdcnt-- > 1) {
+ do_cmd(c);
+ } // repeat cnt
+ dot_right();
+ break;
+#if ENABLE_FEATURE_VI_YANKMARK
+ case '"': // "- name a register to use for Delete/Yank
+ c1 = get_one_char();
+ c1 = tolower(c1);
+ if (islower(c1)) {
+ YDreg = c1 - 'a';
+ } else {
+ indicate_error(c);
+ }
+ break;
+ case '\'': // '- goto a specific mark
+ c1 = get_one_char();
+ c1 = tolower(c1);
+ if (islower(c1)) {
+ c1 = c1 - 'a';
+ // get the b-o-l
+ q = mark[(unsigned char) c1];
+ if (text <= q && q < end) {
+ dot = q;
+ dot_begin(); // go to B-o-l
+ dot_skip_over_ws();
+ }
+ } else if (c1 == '\'') { // goto previous context
+ dot = swap_context(dot); // swap current and previous context
+ dot_begin(); // go to B-o-l
+ dot_skip_over_ws();
+ } else {
+ indicate_error(c);
+ }
+ break;
+ case 'm': // m- Mark a line
+ // this is really stupid. If there are any inserts or deletes
+ // between text[0] and dot then this mark will not point to the
+ // correct location! It could be off by many lines!
+ // Well..., at least its quick and dirty.
+ c1 = get_one_char();
+ c1 = tolower(c1);
+ if (islower(c1)) {
+ c1 = c1 - 'a';
+ // remember the line
+ mark[(int) c1] = dot;
+ } else {
+ indicate_error(c);
+ }
+ break;
+ case 'P': // P- Put register before
+ case 'p': // p- put register after
+ p = reg[YDreg];
+ if (p == 0) {
+ psbs("Nothing in register %c", what_reg());
+ break;
+ }
+ // are we putting whole lines or strings
+ if (strchr(p, '\n') != NULL) {
+ if (c == 'P') {
+ dot_begin(); // putting lines- Put above
+ }
+ if (c == 'p') {
+ // are we putting after very last line?
+ if (end_line(dot) == (end - 1)) {
+ dot = end; // force dot to end of text[]
+ } else {
+ dot_next(); // next line, then put before
+ }
+ }
+ } else {
+ if (c == 'p')
+ dot_right(); // move to right, can move to NL
+ }
+ dot = string_insert(dot, p); // insert the string
+ end_cmd_q(); // stop adding to q
+ break;
+ case 'U': // U- Undo; replace current line with original version
+ if (reg[Ureg] != 0) {
+ p = begin_line(dot);
+ q = end_line(dot);
+ p = text_hole_delete(p, q); // delete cur line
+ p = string_insert(p, reg[Ureg]); // insert orig line
+ dot = p;
+ dot_skip_over_ws();
+ }
+ break;
+#endif /* FEATURE_VI_YANKMARK */
+ case '$': // $- goto end of line
+ case VI_K_END: // Cursor Key End
+ if (cmdcnt-- > 1) {
+ do_cmd(c);
+ } // repeat cnt
+ dot = end_line(dot);
+ break;
+ case '%': // %- find matching char of pair () [] {}
+ for (q = dot; q < end && *q != '\n'; q++) {
+ if (strchr("()[]{}", *q) != NULL) {
+ // we found half of a pair
+ p = find_pair(q, *q);
+ if (p == NULL) {
+ indicate_error(c);
+ } else {
+ dot = p;
+ }
+ break;
+ }
+ }
+ if (*q == '\n')
+ indicate_error(c);
+ break;
+ case 'f': // f- forward to a user specified char
+ last_forward_char = get_one_char(); // get the search char
+ //
+ // dont separate these two commands. 'f' depends on ';'
+ //
+ //**** fall thru to ... ';'
+ case ';': // ;- look at rest of line for last forward char
+ if (cmdcnt-- > 1) {
+ do_cmd(';');
+ } // repeat cnt
+ if (last_forward_char == 0)
+ break;
+ q = dot + 1;
+ while (q < end - 1 && *q != '\n' && *q != last_forward_char) {
+ q++;
+ }
+ if (*q == last_forward_char)
+ dot = q;
+ break;
+ case '-': // -- goto prev line
+ if (cmdcnt-- > 1) {
+ do_cmd(c);
+ } // repeat cnt
+ dot_prev();
+ dot_skip_over_ws();
+ break;
+#if ENABLE_FEATURE_VI_DOT_CMD
+ case '.': // .- repeat the last modifying command
+ // Stuff the last_modifying_cmd back into stdin
+ // and let it be re-executed.
+ if (last_modifying_cmd != 0) {
+ ioq = ioq_start = xstrdup(last_modifying_cmd);
+ }
+ break;
+#endif
+#if ENABLE_FEATURE_VI_SEARCH
+ case '?': // /- search for a pattern
+ case '/': // /- search for a pattern
+ buf[0] = c;
+ buf[1] = '\0';
+ q = get_input_line(buf); // get input line- use "status line"
+ if (q[0] && !q[1])
+ goto dc3; // if no pat re-use old pat
+ if (q[0]) { // strlen(q) > 1: new pat- save it and find
+ // there is a new pat
+ free(last_search_pattern);
+ last_search_pattern = xstrdup(q);
+ goto dc3; // now find the pattern
+ }
+ // user changed mind and erased the "/"- do nothing
+ break;
+ case 'N': // N- backward search for last pattern
+ if (cmdcnt-- > 1) {
+ do_cmd(c);
+ } // repeat cnt
+ dir = BACK; // assume BACKWARD search
+ p = dot - 1;
+ if (last_search_pattern[0] == '?') {
+ dir = FORWARD;
+ p = dot + 1;
+ }
+ goto dc4; // now search for pattern
+ break;
+ case 'n': // n- repeat search for last pattern
+ // search rest of text[] starting at next char
+ // if search fails return orignal "p" not the "p+1" address
+ if (cmdcnt-- > 1) {
+ do_cmd(c);
+ } // repeat cnt
+ dc3:
+ if (last_search_pattern == 0) {
+ msg = "No previous regular expression";
+ goto dc2;
+ }
+ if (last_search_pattern[0] == '/') {
+ dir = FORWARD; // assume FORWARD search
+ p = dot + 1;
+ }
+ if (last_search_pattern[0] == '?') {
+ dir = BACK;
+ p = dot - 1;
+ }
+ dc4:
+ q = char_search(p, last_search_pattern + 1, dir, FULL);
+ if (q != NULL) {
+ dot = q; // good search, update "dot"
+ msg = "";
+ goto dc2;
+ }
+ // no pattern found between "dot" and "end"- continue at top
+ p = text;
+ if (dir == BACK) {
+ p = end - 1;
+ }
+ q = char_search(p, last_search_pattern + 1, dir, FULL);
+ if (q != NULL) { // found something
+ dot = q; // found new pattern- goto it
+ msg = "search hit BOTTOM, continuing at TOP";
+ if (dir == BACK) {
+ msg = "search hit TOP, continuing at BOTTOM";
+ }
+ } else {
+ msg = "Pattern not found";
+ }
+ dc2:
+ if (*msg)
+ psbs("%s", msg);
+ break;
+ case '{': // {- move backward paragraph
+ q = char_search(dot, "\n\n", BACK, FULL);
+ if (q != NULL) { // found blank line
+ dot = next_line(q); // move to next blank line
+ }
+ break;
+ case '}': // }- move forward paragraph
+ q = char_search(dot, "\n\n", FORWARD, FULL);
+ if (q != NULL) { // found blank line
+ dot = next_line(q); // move to next blank line
+ }
+ break;
+#endif /* FEATURE_VI_SEARCH */
+ case '0': // 0- goto begining of line
+ case '1': // 1-
+ case '2': // 2-
+ case '3': // 3-
+ case '4': // 4-
+ case '5': // 5-
+ case '6': // 6-
+ case '7': // 7-
+ case '8': // 8-
+ case '9': // 9-
+ if (c == '0' && cmdcnt < 1) {
+ dot_begin(); // this was a standalone zero
+ } else {
+ cmdcnt = cmdcnt * 10 + (c - '0'); // this 0 is part of a number
+ }
+ break;
+ case ':': // :- the colon mode commands
+ p = get_input_line(":"); // get input line- use "status line"
+#if ENABLE_FEATURE_VI_COLON
+ colon(p); // execute the command
+#else
+ if (*p == ':')
+ p++; // move past the ':'
+ cnt = strlen(p);
+ if (cnt <= 0)
+ break;
+ if (strncasecmp(p, "quit", cnt) == 0
+ || strncasecmp(p, "q!", cnt) == 0 // delete lines
+ ) {
+ if (file_modified && p[1] != '!') {
+ psbs("No write since last change (:quit! overrides)");
+ } else {
+ editing = 0;
+ }
+ } else if (strncasecmp(p, "write", cnt) == 0
+ || strncasecmp(p, "wq", cnt) == 0
+ || strncasecmp(p, "wn", cnt) == 0
+ || strncasecmp(p, "x", cnt) == 0
+ ) {
+ cnt = file_write(cfn, text, end - 1);
+ if (cnt < 0) {
+ if (cnt == -1)
+ psbs("Write error: %s", strerror(errno));
+ } else {
+ file_modified = 0;
+ last_file_modified = -1;
+ psb("\"%s\" %dL, %dC", cfn, count_lines(text, end - 1), cnt);
+ if (p[0] == 'x' || p[1] == 'q' || p[1] == 'n'
+ || p[0] == 'X' || p[1] == 'Q' || p[1] == 'N'
+ ) {
+ editing = 0;
+ }
+ }
+ } else if (strncasecmp(p, "file", cnt) == 0 ) {
+ last_status_cksum = 0; // force status update
+ } else if (sscanf(p, "%d", &j) > 0) {
+ dot = find_line(j); // go to line # j
+ dot_skip_over_ws();
+ } else { // unrecognised cmd
+ ni(p);
+ }
+#endif /* !FEATURE_VI_COLON */
+ break;
+ case '<': // <- Left shift something
+ case '>': // >- Right shift something
+ cnt = count_lines(text, dot); // remember what line we are on
+ c1 = get_one_char(); // get the type of thing to delete
+ find_range(&p, &q, c1);
+ yank_delete(p, q, 1, YANKONLY); // save copy before change
+ p = begin_line(p);
+ q = end_line(q);
+ i = count_lines(p, q); // # of lines we are shifting
+ for ( ; i > 0; i--, p = next_line(p)) {
+ if (c == '<') {
+ // shift left- remove tab or 8 spaces
+ if (*p == '\t') {
+ // shrink buffer 1 char
+ text_hole_delete(p, p);
+ } else if (*p == ' ') {
+ // we should be calculating columns, not just SPACE
+ for (j = 0; *p == ' ' && j < tabstop; j++) {
+ text_hole_delete(p, p);
+ }
+ }
+ } else if (c == '>') {
+ // shift right -- add tab or 8 spaces
+ char_insert(p, '\t');
+ }
+ }
+ dot = find_line(cnt); // what line were we on
+ dot_skip_over_ws();
+ end_cmd_q(); // stop adding to q
+ break;
+ case 'A': // A- append at e-o-l
+ dot_end(); // go to e-o-l
+ //**** fall thru to ... 'a'
+ case 'a': // a- append after current char
+ if (*dot != '\n')
+ dot++;
+ goto dc_i;
+ break;
+ case 'B': // B- back a blank-delimited Word
+ case 'E': // E- end of a blank-delimited word
+ case 'W': // W- forward a blank-delimited word
+ if (cmdcnt-- > 1) {
+ do_cmd(c);
+ } // repeat cnt
+ dir = FORWARD;
+ if (c == 'B')
+ dir = BACK;
+ if (c == 'W' || isspace(dot[dir])) {
+ dot = skip_thing(dot, 1, dir, S_TO_WS);
+ dot = skip_thing(dot, 2, dir, S_OVER_WS);
+ }
+ if (c != 'W')
+ dot = skip_thing(dot, 1, dir, S_BEFORE_WS);
+ break;
+ case 'C': // C- Change to e-o-l
+ case 'D': // D- delete to e-o-l
+ save_dot = dot;
+ dot = dollar_line(dot); // move to before NL
+ // copy text into a register and delete
+ dot = yank_delete(save_dot, dot, 0, YANKDEL); // delete to e-o-l
+ if (c == 'C')
+ goto dc_i; // start inserting
+#if ENABLE_FEATURE_VI_DOT_CMD
+ if (c == 'D')
+ end_cmd_q(); // stop adding to q
+#endif
+ break;
+ case 'G': // G- goto to a line number (default= E-O-F)
+ dot = end - 1; // assume E-O-F
+ if (cmdcnt > 0) {
+ dot = find_line(cmdcnt); // what line is #cmdcnt
+ }
+ dot_skip_over_ws();
+ break;
+ case 'H': // H- goto top line on screen
+ dot = screenbegin;
+ if (cmdcnt > (rows - 1)) {
+ cmdcnt = (rows - 1);
+ }
+ if (cmdcnt-- > 1) {
+ do_cmd('+');
+ } // repeat cnt
+ dot_skip_over_ws();
+ break;
+ case 'I': // I- insert before first non-blank
+ dot_begin(); // 0
+ dot_skip_over_ws();
+ //**** fall thru to ... 'i'
+ case 'i': // i- insert before current char
+ case VI_K_INSERT: // Cursor Key Insert
+ dc_i:
+ cmd_mode = 1; // start insrting
+ break;
+ case 'J': // J- join current and next lines together
+ if (cmdcnt-- > 2) {
+ do_cmd(c);
+ } // repeat cnt
+ dot_end(); // move to NL
+ if (dot < end - 1) { // make sure not last char in text[]
+ *dot++ = ' '; // replace NL with space
+ file_modified++;
+ while (isblnk(*dot)) { // delete leading WS
+ dot_delete();
+ }
+ }
+ end_cmd_q(); // stop adding to q
+ break;
+ case 'L': // L- goto bottom line on screen
+ dot = end_screen();
+ if (cmdcnt > (rows - 1)) {
+ cmdcnt = (rows - 1);
+ }
+ if (cmdcnt-- > 1) {
+ do_cmd('-');
+ } // repeat cnt
+ dot_begin();
+ dot_skip_over_ws();
+ break;
+ case 'M': // M- goto middle line on screen
+ dot = screenbegin;
+ for (cnt = 0; cnt < (rows-1) / 2; cnt++)
+ dot = next_line(dot);
+ break;
+ case 'O': // O- open a empty line above
+ // 0i\n ESC -i
+ p = begin_line(dot);
+ if (p[-1] == '\n') {
+ dot_prev();
+ case 'o': // o- open a empty line below; Yes, I know it is in the middle of the "if (..."
+ dot_end();
+ dot = char_insert(dot, '\n');
+ } else {
+ dot_begin(); // 0
+ dot = char_insert(dot, '\n'); // i\n ESC
+ dot_prev(); // -
+ }
+ goto dc_i;
+ break;
+ case 'R': // R- continuous Replace char
+ dc5:
+ cmd_mode = 2;
+ break;
+ case 'X': // X- delete char before dot
+ case 'x': // x- delete the current char
+ case 's': // s- substitute the current char
+ if (cmdcnt-- > 1) {
+ do_cmd(c);
+ } // repeat cnt
+ dir = 0;
+ if (c == 'X')
+ dir = -1;
+ if (dot[dir] != '\n') {
+ if (c == 'X')
+ dot--; // delete prev char
+ dot = yank_delete(dot, dot, 0, YANKDEL); // delete char
+ }
+ if (c == 's')
+ goto dc_i; // start insrting
+ end_cmd_q(); // stop adding to q
+ break;
+ case 'Z': // Z- if modified, {write}; exit
+ // ZZ means to save file (if necessary), then exit
+ c1 = get_one_char();
+ if (c1 != 'Z') {
+ indicate_error(c);
+ break;
+ }
+ if (file_modified) {
+#if ENABLE_FEATURE_VI_READONLY
+ if (vi_readonly || readonly) {
+ psbs("\"%s\" File is read only", cfn);
+ break;
+ }
+#endif
+ cnt = file_write(cfn, text, end - 1);
+ if (cnt < 0) {
+ if (cnt == -1)
+ psbs("Write error: %s", strerror(errno));
+ } else if (cnt == (end - 1 - text + 1)) {
+ editing = 0;
+ }
+ } else {
+ editing = 0;
+ }
+ break;
+ case '^': // ^- move to first non-blank on line
+ dot_begin();
+ dot_skip_over_ws();
+ break;
+ case 'b': // b- back a word
+ case 'e': // e- end of word
+ if (cmdcnt-- > 1) {
+ do_cmd(c);
+ } // repeat cnt
+ dir = FORWARD;
+ if (c == 'b')
+ dir = BACK;
+ if ((dot + dir) < text || (dot + dir) > end - 1)
+ break;
+ dot += dir;
+ if (isspace(*dot)) {
+ dot = skip_thing(dot, (c == 'e') ? 2 : 1, dir, S_OVER_WS);
+ }
+ if (isalnum(*dot) || *dot == '_') {
+ dot = skip_thing(dot, 1, dir, S_END_ALNUM);
+ } else if (ispunct(*dot)) {
+ dot = skip_thing(dot, 1, dir, S_END_PUNCT);
+ }
+ break;
+ case 'c': // c- change something
+ case 'd': // d- delete something
+#if ENABLE_FEATURE_VI_YANKMARK
+ case 'y': // y- yank something
+ case 'Y': // Y- Yank a line
+#endif
+ yf = YANKDEL; // assume either "c" or "d"
+#if ENABLE_FEATURE_VI_YANKMARK
+ if (c == 'y' || c == 'Y')
+ yf = YANKONLY;
+#endif
+ c1 = 'y';
+ if (c != 'Y')
+ c1 = get_one_char(); // get the type of thing to delete
+ find_range(&p, &q, c1);
+ if (c1 == 27) { // ESC- user changed mind and wants out
+ c = c1 = 27; // Escape- do nothing
+ } else if (strchr("wW", c1)) {
+ if (c == 'c') {
+ // don't include trailing WS as part of word
+ while (isblnk(*q)) {
+ if (q <= text || q[-1] == '\n')
+ break;
+ q--;
+ }
+ }
+ dot = yank_delete(p, q, 0, yf); // delete word
+ } else if (strchr("^0bBeEft$", c1)) {
+ // single line copy text into a register and delete
+ dot = yank_delete(p, q, 0, yf); // delete word
+ } else if (strchr("cdykjHL%+-{}\r\n", c1)) {
+ // multiple line copy text into a register and delete
+ dot = yank_delete(p, q, 1, yf); // delete lines
+ if (c == 'c') {
+ dot = char_insert(dot, '\n');
+ // on the last line of file don't move to prev line
+ if (dot != (end-1)) {
+ dot_prev();
+ }
+ } else if (c == 'd') {
+ dot_begin();
+ dot_skip_over_ws();
+ }
+ } else {
+ // could not recognize object
+ c = c1 = 27; // error-
+ indicate_error(c);
+ }
+ if (c1 != 27) {
+ // if CHANGING, not deleting, start inserting after the delete
+ if (c == 'c') {
+ strcpy(buf, "Change");
+ goto dc_i; // start inserting
+ }
+ if (c == 'd') {
+ strcpy(buf, "Delete");
+ }
+#if ENABLE_FEATURE_VI_YANKMARK
+ if (c == 'y' || c == 'Y') {
+ strcpy(buf, "Yank");
+ }
+ p = reg[YDreg];
+ q = p + strlen(p);
+ for (cnt = 0; p <= q; p++) {
+ if (*p == '\n')
+ cnt++;
+ }
+ psb("%s %d lines (%d chars) using [%c]",
+ buf, cnt, strlen(reg[YDreg]), what_reg());
+#endif
+ end_cmd_q(); // stop adding to q
+ }
+ break;
+ case 'k': // k- goto prev line, same col
+ case VI_K_UP: // cursor key Up
+ if (cmdcnt-- > 1) {
+ do_cmd(c);
+ } // repeat cnt
+ dot_prev();
+ dot = move_to_col(dot, ccol + offset); // try stay in same col
+ break;
+ case 'r': // r- replace the current char with user input
+ c1 = get_one_char(); // get the replacement char
+ if (*dot != '\n') {
+ *dot = c1;
+ file_modified++; // has the file been modified
+ }
+ end_cmd_q(); // stop adding to q
+ break;
+ case 't': // t- move to char prior to next x
+ last_forward_char = get_one_char();
+ do_cmd(';');
+ if (*dot == last_forward_char)
+ dot_left();
+ last_forward_char= 0;
+ break;
+ case 'w': // w- forward a word
+ if (cmdcnt-- > 1) {
+ do_cmd(c);
+ } // repeat cnt
+ if (isalnum(*dot) || *dot == '_') { // we are on ALNUM
+ dot = skip_thing(dot, 1, FORWARD, S_END_ALNUM);
+ } else if (ispunct(*dot)) { // we are on PUNCT
+ dot = skip_thing(dot, 1, FORWARD, S_END_PUNCT);
+ }
+ if (dot < end - 1)
+ dot++; // move over word
+ if (isspace(*dot)) {
+ dot = skip_thing(dot, 2, FORWARD, S_OVER_WS);
+ }
+ break;
+ case 'z': // z-
+ c1 = get_one_char(); // get the replacement char
+ cnt = 0;
+ if (c1 == '.')
+ cnt = (rows - 2) / 2; // put dot at center
+ if (c1 == '-')
+ cnt = rows - 2; // put dot at bottom
+ screenbegin = begin_line(dot); // start dot at top
+ dot_scroll(cnt, -1);
+ break;
+ case '|': // |- move to column "cmdcnt"
+ dot = move_to_col(dot, cmdcnt - 1); // try to move to column
+ break;
+ case '~': // ~- flip the case of letters a-z -> A-Z
+ if (cmdcnt-- > 1) {
+ do_cmd(c);
+ } // repeat cnt
+ if (islower(*dot)) {
+ *dot = toupper(*dot);
+ file_modified++; // has the file been modified
+ } else if (isupper(*dot)) {
+ *dot = tolower(*dot);
+ file_modified++; // has the file been modified
+ }
+ dot_right();
+ end_cmd_q(); // stop adding to q
+ break;
+ //----- The Cursor and Function Keys -----------------------------
+ case VI_K_HOME: // Cursor Key Home
+ dot_begin();
+ break;
+ // The Fn keys could point to do_macro which could translate them
+ case VI_K_FUN1: // Function Key F1
+ case VI_K_FUN2: // Function Key F2
+ case VI_K_FUN3: // Function Key F3
+ case VI_K_FUN4: // Function Key F4
+ case VI_K_FUN5: // Function Key F5
+ case VI_K_FUN6: // Function Key F6
+ case VI_K_FUN7: // Function Key F7
+ case VI_K_FUN8: // Function Key F8
+ case VI_K_FUN9: // Function Key F9
+ case VI_K_FUN10: // Function Key F10
+ case VI_K_FUN11: // Function Key F11
+ case VI_K_FUN12: // Function Key F12
+ break;
+ }
+
+ dc1:
+ // if text[] just became empty, add back an empty line
+ if (end == text) {
+ char_insert(text, '\n'); // start empty buf with dummy line
+ dot = text;
+ }
+ // it is OK for dot to exactly equal to end, otherwise check dot validity
+ if (dot != end) {
+ dot = bound_dot(dot); // make sure "dot" is valid
+ }
+#if ENABLE_FEATURE_VI_YANKMARK
+ check_context(c); // update the current context
+#endif
+
+ if (!isdigit(c))
+ cmdcnt = 0; // cmd was not a number, reset cmdcnt
+ cnt = dot - begin_line(dot);
+ // Try to stay off of the Newline
+ if (*dot == '\n' && cnt > 0 && cmd_mode == 0)
+ dot--;
+}
+
+#if ENABLE_FEATURE_VI_CRASHME
+static int totalcmds = 0;
+static int Mp = 85; // Movement command Probability
+static int Np = 90; // Non-movement command Probability
+static int Dp = 96; // Delete command Probability
+static int Ip = 97; // Insert command Probability
+static int Yp = 98; // Yank command Probability
+static int Pp = 99; // Put command Probability
+static int M = 0, N = 0, I = 0, D = 0, Y = 0, P = 0, U = 0;
+const char chars[20] = "\t012345 abcdABCD-=.$";
+const char *const words[20] = {
+ "this", "is", "a", "test",
+ "broadcast", "the", "emergency", "of",
+ "system", "quick", "brown", "fox",
+ "jumped", "over", "lazy", "dogs",
+ "back", "January", "Febuary", "March"
+};
+const char *const lines[20] = {
+ "You should have received a copy of the GNU General Public License\n",
+ "char c, cm, *cmd, *cmd1;\n",
+ "generate a command by percentages\n",
+ "Numbers may be typed as a prefix to some commands.\n",
+ "Quit, discarding changes!\n",
+ "Forced write, if permission originally not valid.\n",
+ "In general, any ex or ed command (such as substitute or delete).\n",
+ "I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
+ "Please get w/ me and I will go over it with you.\n",
+ "The following is a list of scheduled, committed changes.\n",
+ "1. Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
+ "Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
+ "Any question about transactions please contact Sterling Huxley.\n",
+ "I will try to get back to you by Friday, December 31.\n",
+ "This Change will be implemented on Friday.\n",
+ "Let me know if you have problems accessing this;\n",
+ "Sterling Huxley recently added you to the access list.\n",
+ "Would you like to go to lunch?\n",
+ "The last command will be automatically run.\n",
+ "This is too much english for a computer geek.\n",
+};
+char *multilines[20] = {
+ "You should have received a copy of the GNU General Public License\n",
+ "char c, cm, *cmd, *cmd1;\n",
+ "generate a command by percentages\n",
+ "Numbers may be typed as a prefix to some commands.\n",
+ "Quit, discarding changes!\n",
+ "Forced write, if permission originally not valid.\n",
+ "In general, any ex or ed command (such as substitute or delete).\n",
+ "I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
+ "Please get w/ me and I will go over it with you.\n",
+ "The following is a list of scheduled, committed changes.\n",
+ "1. Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
+ "Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
+ "Any question about transactions please contact Sterling Huxley.\n",
+ "I will try to get back to you by Friday, December 31.\n",
+ "This Change will be implemented on Friday.\n",
+ "Let me know if you have problems accessing this;\n",
+ "Sterling Huxley recently added you to the access list.\n",
+ "Would you like to go to lunch?\n",
+ "The last command will be automatically run.\n",
+ "This is too much english for a computer geek.\n",
+};
+
+// create a random command to execute
+static void crash_dummy()
+{
+ static int sleeptime; // how long to pause between commands
+ char c, cm, *cmd, *cmd1;
+ int i, cnt, thing, rbi, startrbi, percent;
+
+ // "dot" movement commands
+ cmd1 = " \n\r\002\004\005\006\025\0310^$-+wWeEbBhjklHL";
+
+ // is there already a command running?
+ if (readed_for_parse > 0)
+ goto cd1;
+ cd0:
+ startrbi = rbi = 0;
+ sleeptime = 0; // how long to pause between commands
+ memset(readbuffer, '\0', BUFSIZ); // clear the read buffer
+ // generate a command by percentages
+ percent = (int) lrand48() % 100; // get a number from 0-99
+ if (percent < Mp) { // Movement commands
+ // available commands
+ cmd = cmd1;
+ M++;
+ } else if (percent < Np) { // non-movement commands
+ cmd = "mz<>\'\""; // available commands
+ N++;
+ } else if (percent < Dp) { // Delete commands
+ cmd = "dx"; // available commands
+ D++;
+ } else if (percent < Ip) { // Inset commands
+ cmd = "iIaAsrJ"; // available commands
+ I++;
+ } else if (percent < Yp) { // Yank commands
+ cmd = "yY"; // available commands
+ Y++;
+ } else if (percent < Pp) { // Put commands
+ cmd = "pP"; // available commands
+ P++;
+ } else {
+ // We do not know how to handle this command, try again
+ U++;
+ goto cd0;
+ }
+ // randomly pick one of the available cmds from "cmd[]"
+ i = (int) lrand48() % strlen(cmd);
+ cm = cmd[i];
+ if (strchr(":\024", cm))
+ goto cd0; // dont allow colon or ctrl-T commands
+ readbuffer[rbi++] = cm; // put cmd into input buffer
+
+ // now we have the command-
+ // there are 1, 2, and multi char commands
+ // find out which and generate the rest of command as necessary
+ if (strchr("dmryz<>\'\"", cm)) { // 2-char commands
+ cmd1 = " \n\r0$^-+wWeEbBhjklHL";
+ if (cm == 'm' || cm == '\'' || cm == '\"') { // pick a reg[]
+ cmd1 = "abcdefghijklmnopqrstuvwxyz";
+ }
+ thing = (int) lrand48() % strlen(cmd1); // pick a movement command
+ c = cmd1[thing];
+ readbuffer[rbi++] = c; // add movement to input buffer
+ }
+ if (strchr("iIaAsc", cm)) { // multi-char commands
+ if (cm == 'c') {
+ // change some thing
+ thing = (int) lrand48() % strlen(cmd1); // pick a movement command
+ c = cmd1[thing];
+ readbuffer[rbi++] = c; // add movement to input buffer
+ }
+ thing = (int) lrand48() % 4; // what thing to insert
+ cnt = (int) lrand48() % 10; // how many to insert
+ for (i = 0; i < cnt; i++) {
+ if (thing == 0) { // insert chars
+ readbuffer[rbi++] = chars[((int) lrand48() % strlen(chars))];
+ } else if (thing == 1) { // insert words
+ strcat(readbuffer, words[(int) lrand48() % 20]);
+ strcat(readbuffer, " ");
+ sleeptime = 0; // how fast to type
+ } else if (thing == 2) { // insert lines
+ strcat(readbuffer, lines[(int) lrand48() % 20]);
+ sleeptime = 0; // how fast to type
+ } else { // insert multi-lines
+ strcat(readbuffer, multilines[(int) lrand48() % 20]);
+ sleeptime = 0; // how fast to type
+ }
+ }
+ strcat(readbuffer, "\033");
+ }
+ readed_for_parse = strlen(readbuffer);
+ cd1:
+ totalcmds++;
+ if (sleeptime > 0)
+ mysleep(sleeptime); // sleep 1/100 sec
+}
+
+// test to see if there are any errors
+static void crash_test()
+{
+ static time_t oldtim;
+
+ time_t tim;
+ char d[2], msg[BUFSIZ];
+
+ msg[0] = '\0';
+ if (end < text) {
+ strcat(msg, "end<text ");
+ }
+ if (end > textend) {
+ strcat(msg, "end>textend ");
+ }
+ if (dot < text) {
+ strcat(msg, "dot<text ");
+ }
+ if (dot > end) {
+ strcat(msg, "dot>end ");
+ }
+ if (screenbegin < text) {
+ strcat(msg, "screenbegin<text ");
+ }
+ if (screenbegin > end - 1) {
+ strcat(msg, "screenbegin>end-1 ");
+ }
+
+ if (msg[0]) {
+ alarm(0);
+ printf("\n\n%d: \'%c\' %s\n\n\n%s[Hit return to continue]%s",
+ totalcmds, last_input_char, msg, SOs, SOn);
+ fflush(stdout);
+ while (read(0, d, 1) > 0) {
+ if (d[0] == '\n' || d[0] == '\r')
+ break;
+ }
+ alarm(3);
+ }
+ tim = (time_t) time((time_t *) 0);
+ if (tim >= (oldtim + 3)) {
+ sprintf(status_buffer,
+ "Tot=%d: M=%d N=%d I=%d D=%d Y=%d P=%d U=%d size=%d",
+ totalcmds, M, N, I, D, Y, P, U, end - text + 1);
+ oldtim = tim;
+ }
+}
+#endif
diff --git a/i/pc104/initrd/conf/busybox/examples/bootfloppy/bootfloppy.txt b/i/pc104/initrd/conf/busybox/examples/bootfloppy/bootfloppy.txt
new file mode 100644
index 0000000..6866b72
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/examples/bootfloppy/bootfloppy.txt
@@ -0,0 +1,180 @@
+Building a Busybox Boot Floppy
+==============================
+
+This document describes how to buid a boot floppy using the following
+components:
+
+ - Linux Kernel (http://www.kernel.org)
+ - uClibc: C library (http://www.uclibc.org/)
+ - Busybox: Unix utilities (http://busybox.net/)
+ - Syslinux: bootloader (http://syslinux.zytor.com)
+
+It is based heavily on a paper presented by Erik Andersen at the 2001 Embedded
+Systems Conference.
+
+
+
+Building The Software Components
+--------------------------------
+
+Detailed instructions on how to build Busybox, uClibc, or a working Linux
+kernel are beyond the scope of this document. The following guidelines will
+help though:
+
+ - Stock Busybox from CVS or a tarball will work with no modifications to
+ any files. Just extract and go.
+ - Ditto uClibc.
+ - Your Linux kernel must include support for initrd or else the floppy
+ won't be able to mount it's root file system.
+
+If you require further information on building Busybox uClibc or Linux, please
+refer to the web pages and documentation for those individual programs.
+
+
+
+Making a Root File System
+-------------------------
+
+The following steps will create a root file system.
+
+ - Create an empty file that you can format as a filesystem:
+
+ dd if=/dev/zero of=rootfs bs=1k count=4000
+
+ - Set up the rootfs file we just created to be used as a loop device (may not
+ be necessary)
+
+ losetup /dev/loop0 rootfs
+
+ - Format the rootfs file with a filesystem:
+
+ mkfs.ext2 -F -i 2000 rootfs
+
+ - Mount the file on a mountpoint so we can place files in it:
+
+ mkdir loop
+ mount -o loop rootfs loop/
+
+ (you will probably need to be root to do this)
+
+ - Copy on the C library, the dynamic linking library, and other necessary
+ libraries. For this example, we copy the following files from the uClibc
+ tree:
+
+ mkdir loop/lib
+ (chdir to uClibc directory)
+ cp -a libc.so* uClibc*.so \
+ ld.so-1/d-link/ld-linux-uclibc.so* \
+ ld.so-1/libdl/libdl.so* \
+ crypt/libcrypt.so* \
+ (path to)loop/lib
+
+ - Install the Busybox binary and accompanying symlinks:
+
+ (chdir to busybox directory)
+ make CONFIG_PREFIX=(path to)loop/ install
+
+ - Make device files in /dev:
+
+ This can be done by running the 'mkdevs.sh' script. If you want the gory
+ details, you can read the script.
+
+ - Make necessary files in /etc:
+
+ For this, just cp -a the etc/ directory onto rootfs. Again, if you want
+ all the details, you can just look at the files in the dir.
+
+ - Unmount the rootfs from the mountpoint:
+
+ umount loop
+
+ - Compress it:
+
+ gzip -9 rootfs
+
+
+Making a SYSLINUX boot floppy
+-----------------------------
+
+The following steps will create the boot floppy.
+
+Note: You will need to have the mtools package installed beforehand.
+
+ - Insert a floppy in the drive and format it with an MSDOS filesystem:
+
+ mformat a:
+
+ (if the system doesn't know what device 'a:' is, look at /etc/mtools.conf)
+
+ - Run syslinux on the floppy:
+
+ syslinux -s /dev/fd0
+
+ (the -s stands for "safe, slow, and stupid" and should work better with
+ buggy BIOSes; it can be omitted)
+
+ - Put on a syslinux.cfg file:
+
+ mcopy syslinux.cfg a:
+
+ (more on syslinux.cfg below)
+
+ - Copy the root file system you made onto the MSDOS formatted floppy
+
+ mcopy rootfs.gz a:
+
+ - Build a linux kernel and copy it onto the disk with the filename 'linux'
+
+ mcopy bzImage a:linux
+
+
+Sample syslinux.cfg
+~~~~~~~~~~~~~~~~~~~
+
+The following simple syslinux.cfg file should work. You can tweak it if you
+like.
+
+----begin-syslinux.cfg---------------
+DEFAULT linux
+APPEND initrd=rootfs.gz root=/dev/ram0
+TIMEOUT 10
+PROMPT 1
+----end-syslinux.cfg---------------
+
+Some changes you could make to syslinux.cfg:
+
+ - This value is the number seconds it will wait before booting. You can set
+ the timeout to 0 (or omit) to boot instantly, or you can set it as high as
+ 10 to wait awhile.
+
+ - PROMPT can be set to 0 to disable the 'boot:' prompt.
+
+ - you can add this line to display the contents of a file as a welcome
+ message:
+
+ DISPLAY display.txt
+
+
+
+Additional Resources
+--------------------
+
+Other useful information on making a Linux bootfloppy is available at the
+following URLs:
+
+http://www.linuxdoc.org/HOWTO/Bootdisk-HOWTO/index.html
+http://www.linux-embedded.com/howto/Embedded-Linux-Howto.html
+http://linux-embedded.org/howto/LFS-HOWTO.html
+http://linux-embedded.org/pmhowto.html
+http://recycle.lbl.gov/~ldoolitt/embedded/ (Larry Doolittle's stuff)
+
+
+
+Possible TODOs
+--------------
+
+The following features that we might want to add later:
+
+ - support for additional filesystems besides ext2, i.e. minix
+ - different libc, static vs dynamic loading
+ - maybe using an alternate bootloader
diff --git a/i/pc104/initrd/conf/busybox/examples/bootfloppy/display.txt b/i/pc104/initrd/conf/busybox/examples/bootfloppy/display.txt
new file mode 100644
index 0000000..399d326
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/examples/bootfloppy/display.txt
@@ -0,0 +1,4 @@
+
+This boot floppy is made with Busybox, uClibc, and the Linux kernel.
+Hit RETURN to boot or enter boot parameters at the prompt below.
+
diff --git a/i/pc104/initrd/conf/busybox/examples/bootfloppy/etc/fstab b/i/pc104/initrd/conf/busybox/examples/bootfloppy/etc/fstab
new file mode 100644
index 0000000..ef14ca2
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/examples/bootfloppy/etc/fstab
@@ -0,0 +1,2 @@
+proc /proc proc defaults 0 0
+
diff --git a/i/pc104/initrd/conf/busybox/examples/bootfloppy/etc/init.d/rcS b/i/pc104/initrd/conf/busybox/examples/bootfloppy/etc/init.d/rcS
new file mode 100755
index 0000000..4f29b92
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/examples/bootfloppy/etc/init.d/rcS
@@ -0,0 +1,3 @@
+#! /bin/sh
+
+/bin/mount -a
diff --git a/i/pc104/initrd/conf/busybox/examples/bootfloppy/etc/inittab b/i/pc104/initrd/conf/busybox/examples/bootfloppy/etc/inittab
new file mode 100644
index 0000000..eb3e979
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/examples/bootfloppy/etc/inittab
@@ -0,0 +1,5 @@
+::sysinit:/etc/init.d/rcS
+::respawn:-/bin/sh
+tty2::askfirst:-/bin/sh
+::ctrlaltdel:/bin/umount -a -r
+
diff --git a/i/pc104/initrd/conf/busybox/examples/bootfloppy/etc/profile b/i/pc104/initrd/conf/busybox/examples/bootfloppy/etc/profile
new file mode 100644
index 0000000..8a7c77d
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/examples/bootfloppy/etc/profile
@@ -0,0 +1,8 @@
+# /etc/profile: system-wide .profile file for the Bourne shells
+
+echo
+echo -n "Processing /etc/profile... "
+# no-op
+echo "Done"
+echo
+
diff --git a/i/pc104/initrd/conf/busybox/examples/bootfloppy/mkdevs.sh b/i/pc104/initrd/conf/busybox/examples/bootfloppy/mkdevs.sh
new file mode 100755
index 0000000..03a1a85
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/examples/bootfloppy/mkdevs.sh
@@ -0,0 +1,62 @@
+#!/bin/sh
+#
+# makedev.sh - creates device files for a busybox boot floppy image
+
+
+# we do our work in the dev/ directory
+if [ -z "$1" ]; then
+ echo "usage: `basename $0` path/to/dev/dir"
+ exit 1
+fi
+
+cd $1
+
+
+# miscellaneous one-of-a-kind stuff
+mknod console c 5 1
+mknod full c 1 7
+mknod kmem c 1 2
+mknod mem c 1 1
+mknod null c 1 3
+mknod port c 1 4
+mknod random c 1 8
+mknod urandom c 1 9
+mknod zero c 1 5
+ln -s /proc/kcore core
+
+# IDE HD devs
+# note: not going to bother creating all concievable partitions; you can do
+# that yourself as you need 'em.
+mknod hda b 3 0
+mknod hdb b 3 64
+mknod hdc b 22 0
+mknod hdd b 22 64
+
+# loop devs
+for i in `seq 0 7`; do
+ mknod loop$i b 7 $i
+done
+
+# ram devs
+for i in `seq 0 9`; do
+ mknod ram$i b 1 $i
+done
+ln -s ram1 ram
+
+# ttys
+mknod tty c 5 0
+for i in `seq 0 9`; do
+ mknod tty$i c 4 $i
+done
+
+# virtual console screen devs
+for i in `seq 0 9`; do
+ mknod vcs$i b 7 $i
+done
+ln -s vcs0 vcs
+
+# virtual console screen w/ attributes devs
+for i in `seq 0 9`; do
+ mknod vcsa$i b 7 $i
+done
+ln -s vcsa0 vcsa
diff --git a/i/pc104/initrd/conf/busybox/examples/bootfloppy/mkrootfs.sh b/i/pc104/initrd/conf/busybox/examples/bootfloppy/mkrootfs.sh
new file mode 100755
index 0000000..5cdff21
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/examples/bootfloppy/mkrootfs.sh
@@ -0,0 +1,105 @@
+#!/bin/bash
+#
+# mkrootfs.sh - creates a root file system
+#
+
+# TODO: need to add checks here to verify that busybox, uClibc and bzImage
+# exist
+
+
+# command-line settable variables
+BUSYBOX_DIR=..
+UCLIBC_DIR=../../uClibc
+TARGET_DIR=./loop
+FSSIZE=4000
+CLEANUP=1
+MKFS='mkfs.ext2 -F'
+
+# don't-touch variables
+BASE_DIR=`pwd`
+
+
+while getopts 'b:u:s:t:Cm' opt
+do
+ case $opt in
+ b) BUSYBOX_DIR=$OPTARG ;;
+ u) UCLIBC_DIR=$OPTARG ;;
+ t) TARGET_DIR=$OPTARG ;;
+ s) FSSIZE=$OPTARG ;;
+ C) CLEANUP=0 ;;
+ m) MKFS='mkfs.minix' ;;
+ *)
+ echo "usage: `basename $0` [-bu]"
+ echo " -b DIR path to busybox direcory (default ..)"
+ echo " -u DIR path to uClibc direcory (default ../../uClibc)"
+ echo " -t DIR path to target direcory (default ./loop)"
+ echo " -s SIZE size of root filesystem in Kbytes (default 4000)"
+ echo " -C don't perform cleanup (umount target dir, gzip rootfs, etc.)"
+ echo " (this allows you to 'chroot loop/ /bin/sh' to test it)"
+ echo " -m use minix filesystem (default is ext2)"
+ exit 1
+ ;;
+ esac
+done
+
+
+
+
+# clean up from any previous work
+mount | grep -q loop
+[ $? -eq 0 ] && umount $TARGET_DIR
+[ -d $TARGET_DIR ] && rm -rf $TARGET_DIR/
+[ -f rootfs ] && rm -f rootfs
+[ -f rootfs.gz ] && rm -f rootfs.gz
+
+
+# prepare root file system and mount as loopback
+dd if=/dev/zero of=rootfs bs=1k count=$FSSIZE
+$MKFS -i 2000 rootfs
+mkdir $TARGET_DIR
+mount -o loop,exec rootfs $TARGET_DIR # must be root
+
+
+# install uClibc
+mkdir -p $TARGET_DIR/lib
+cd $UCLIBC_DIR
+make INSTALL_DIR=
+cp -a libc.so* $BASE_DIR/$TARGET_DIR/lib
+cp -a uClibc*.so $BASE_DIR/$TARGET_DIR/lib
+cp -a ld.so-1/d-link/ld-linux-uclibc.so* $BASE_DIR/$TARGET_DIR/lib
+cp -a ld.so-1/libdl/libdl.so* $BASE_DIR/$TARGET_DIR/lib
+cp -a crypt/libcrypt.so* $BASE_DIR/$TARGET_DIR/lib
+cd $BASE_DIR
+
+
+# install busybox and components
+cd $BUSYBOX_DIR
+make distclean
+make CC=$BASE_DIR/$UCLIBC_DIR/extra/gcc-uClibc/i386-uclibc-gcc
+make CONFIG_PREFIX=$BASE_DIR/$TARGET_DIR install
+cd $BASE_DIR
+
+
+# make files in /dev
+mkdir $TARGET_DIR/dev
+./mkdevs.sh $TARGET_DIR/dev
+
+
+# make files in /etc
+cp -a etc $TARGET_DIR
+ln -s /proc/mounts $TARGET_DIR/etc/mtab
+
+
+# other miscellaneous setup
+mkdir $TARGET_DIR/initrd
+mkdir $TARGET_DIR/proc
+
+
+# Done. Maybe do cleanup.
+if [ $CLEANUP -eq 1 ]
+then
+ umount $TARGET_DIR
+ rmdir $TARGET_DIR
+ gzip -9 rootfs
+fi
+
diff --git a/i/pc104/initrd/conf/busybox/examples/bootfloppy/mksyslinux.sh b/i/pc104/initrd/conf/busybox/examples/bootfloppy/mksyslinux.sh
new file mode 100755
index 0000000..e254173
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/examples/bootfloppy/mksyslinux.sh
@@ -0,0 +1,48 @@
+#!/bin/bash
+#
+# Formats a floppy to use Syslinux
+
+dummy=""
+
+
+# need to have mtools installed
+if [ -z `which mformat` -o -z `which mcopy` ]; then
+ echo "You must have the mtools package installed to run this script"
+ exit 1
+fi
+
+
+# need an arg for the location of the kernel
+if [ -z "$1" ]; then
+ echo "usage: `basename $0` path/to/linux/kernel"
+ exit 1
+fi
+
+
+# need to have a root file system built
+if [ ! -f rootfs.gz ]; then
+ echo "You need to have a rootfs built first."
+ echo "Hit RETURN to make one now or Control-C to quit."
+ read dummy
+ ./mkrootfs.sh
+fi
+
+
+# prepare the floppy
+echo "Please insert a blank floppy in the drive and press RETURN to format"
+echo "(WARNING: All data will be erased! Hit Control-C to abort)"
+read dummy
+
+echo "Formatting the floppy..."
+mformat a:
+echo "Making it bootable with Syslinux..."
+syslinux -s /dev/fd0
+echo "Copying Syslinux configuration files..."
+mcopy syslinux.cfg display.txt a:
+echo "Copying root filesystem file..."
+mcopy rootfs.gz a:
+# XXX: maybe check for "no space on device" errors here
+echo "Copying linux kernel..."
+mcopy $1 a:linux
+# XXX: maybe check for "no space on device" errors here too
+echo "Finished: boot floppy created"
diff --git a/i/pc104/initrd/conf/busybox/examples/bootfloppy/quickstart.txt b/i/pc104/initrd/conf/busybox/examples/bootfloppy/quickstart.txt
new file mode 100644
index 0000000..0d80908
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/examples/bootfloppy/quickstart.txt
@@ -0,0 +1,15 @@
+Quickstart on making the Busybox boot-floppy:
+
+ 1) Download Busybox and uClibc from CVS or tarballs. Make sure they share a
+ common parent directory. (i.e. busybox/ and uclibc/ are both right off of
+ /tmp, or wherever.)
+
+ 2) Build a Linux kernel. Make sure you include support for initrd.
+
+ 3) Put a floppy in the drive. Make sure it is a floppy you don't care about
+ because the contents will be overwritten.
+
+ 4) As root, type ./mksyslinux.sh path/to/linux/kernel from this directory.
+ Wait patiently while the magic happens.
+
+ 5) Boot up on the floppy.
diff --git a/i/pc104/initrd/conf/busybox/examples/bootfloppy/syslinux.cfg b/i/pc104/initrd/conf/busybox/examples/bootfloppy/syslinux.cfg
new file mode 100644
index 0000000..fa2677c
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/examples/bootfloppy/syslinux.cfg
@@ -0,0 +1,7 @@
+display display.txt
+default linux
+timeout 10
+prompt 1
+label linux
+ kernel linux
+ append initrd=rootfs.gz root=/dev/ram0
diff --git a/i/pc104/initrd/conf/busybox/examples/busybox.spec b/i/pc104/initrd/conf/busybox/examples/busybox.spec
new file mode 100644
index 0000000..494eed9
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/examples/busybox.spec
@@ -0,0 +1,44 @@
+%define name busybox
+%define epoch 0
+%define version 0.61.pre
+%define release %(date -I | sed -e 's/-/_/g')
+%define serial 1
+
+Name: %{name}
+#Epoch: %{epoch}
+Version: %{version}
+Release: %{release}
+Serial: %{serial}
+Copyright: GPL
+Group: System/Utilities
+Summary: BusyBox is a tiny suite of Unix utilities in a multi-call binary.
+URL: http://busybox.net/
+Source: ftp://busybox.net/busybox/%{name}-%{version}.tar.gz
+Buildroot: /var/tmp/%{name}-%{version}
+Packager : Erik Andersen <andersen@codepoet.org>
+
+%Description
+BusyBox combines tiny versions of many common UNIX utilities into a single
+small executable. It provides minimalist replacements for most of the utilities
+you usually find in fileutils, shellutils, findutils, textutils, grep, gzip,
+tar, etc. BusyBox provides a fairly complete POSIX environment for any small
+or emdedded system. The utilities in BusyBox generally have fewer options then
+their full featured GNU cousins; however, the options that are provided behave
+very much like their GNU counterparts.
+
+%Prep
+%setup -q -n %{name}-%{version}
+
+%Build
+make
+
+%Install
+rm -rf $RPM_BUILD_ROOT
+make CONFIG_PREFIX=$RPM_BUILD_ROOT install
+
+%Clean
+rm -rf $RPM_BUILD_ROOT
+
+%Files
+%defattr(-,root,root)
+/
diff --git a/i/pc104/initrd/conf/busybox/examples/depmod.pl b/i/pc104/initrd/conf/busybox/examples/depmod.pl
new file mode 100755
index 0000000..b2bf547
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/examples/depmod.pl
@@ -0,0 +1,289 @@
+#!/usr/bin/perl -w
+# vi: set sw=4 ts=4:
+# Copyright (c) 2001 David Schleef <ds@schleef.org>
+# Copyright (c) 2001 Erik Andersen <andersen@codepoet.org>
+# Copyright (c) 2001 Stuart Hughes <seh@zee2.com>
+# Copyright (c) 2002 Steven J. Hill <shill@broadcom.com>
+# Copyright (c) 2006 Freescale Semiconductor, Inc <stuarth@freescale.com>
+#
+# History:
+# March 2006: Stuart Hughes <stuarth@freescale.com>.
+# Significant updates, including implementing the '-F' option
+# and adding support for 2.6 kernels.
+
+# This program is free software; you can redistribute it and/or modify it
+# under the same terms as Perl itself.
+use Getopt::Long;
+use File::Find;
+use strict;
+
+# Set up some default values
+my $kdir="";
+my $basedir="";
+my $kernel="";
+my $kernelsyms="";
+my $stdout=0;
+my $verbose=0;
+my $help=0;
+my $nm = $ENV{'NM'} || "nm";
+
+# more globals
+my (@liblist) = ();
+my $exp = {};
+my $dep = {};
+my $mod = {};
+
+my $usage = <<TXT;
+$0 -b basedir { -k <vmlinux> | -F <System.map> } [options]...
+ Where:
+ -h --help : Show this help screen
+ -b --basedir : Modules base directory (e.g /lib/modules/<2.x.y>)
+ -k --kernel : Kernel binary for the target (e.g. vmlinux)
+ -F --kernelsyms : Kernel symbol file (e.g. System.map)
+ -n --stdout : Write to stdout instead of <basedir>/modules.dep
+ -v --verbose : Print out lots of debugging stuff
+TXT
+
+# get command-line options
+GetOptions(
+ "help|h" => \$help,
+ "basedir|b=s" => \$basedir,
+ "kernel|k=s" => \$kernel,
+ "kernelsyms|F=s" => \$kernelsyms,
+ "stdout|n" => \$stdout,
+ "verbose|v" => \$verbose,
+);
+
+die $usage if $help;
+die $usage unless $basedir && ( $kernel || $kernelsyms );
+die "can't use both -k and -F\n\n$usage" if $kernel && $kernelsyms;
+
+# Strip any trailing or multiple slashes from basedir
+$basedir =~ s-(/)\1+-/-g;
+
+# The base directory should contain /lib/modules somewhere
+if($basedir !~ m-/lib/modules-) {
+ warn "WARNING: base directory does not match ..../lib/modules\n";
+}
+
+# if no kernel version is contained in the basedir, try to find one
+if($basedir !~ m-/lib/modules/\d\.\d-) {
+ opendir(BD, $basedir) or die "can't open basedir $basedir : $!\n";
+ foreach ( readdir(BD) ) {
+ next if /^\.\.?$/;
+ next unless -d "$basedir/$_";
+ warn "dir = $_\n" if $verbose;
+ if( /^\d\.\d/ ) {
+ $kdir = $_;
+ warn("Guessed module directory as $basedir/$kdir\n");
+ last;
+ }
+ }
+ closedir(BD);
+ die "Cannot find a kernel version under $basedir\n" unless $kdir;
+ $basedir = "$basedir/$kdir";
+}
+
+# Find the list of .o or .ko files living under $basedir
+warn "**** Locating all modules\n" if $verbose;
+find sub {
+ my $file;
+ if ( -f $_ && ! -d $_ ) {
+ $file = $File::Find::name;
+ if ( $file =~ /\.k?o$/ ) {
+ push(@liblist, $file);
+ warn "$file\n" if $verbose;
+ }
+ }
+}, $basedir;
+warn "**** Finished locating modules\n" if $verbose;
+
+foreach my $obj ( @liblist ){
+ # turn the input file name into a target tag name
+ my ($tgtname) = $obj =~ m-(/lib/modules/.*)$-;
+
+ warn "\nMODULE = $tgtname\n" if $verbose;
+
+ # get a list of symbols
+ my @output=`$nm $obj`;
+
+ build_ref_tables($tgtname, \@output, $exp, $dep);
+}
+
+
+# vmlinux is a special name that is only used to resolve symbols
+my $tgtname = 'vmlinux';
+my @output = $kernelsyms ? `cat $kernelsyms` : `$nm $kernel`;
+warn "\nMODULE = $tgtname\n" if $verbose;
+build_ref_tables($tgtname, \@output, $exp, $dep);
+
+# resolve the dependencies for each module
+# reduce dependencies: remove unresolvable and resolved from vmlinux/System.map
+# remove duplicates
+foreach my $module (keys %$dep) {
+ warn "reducing module: $module\n" if $verbose;
+ $mod->{$module} = {};
+ foreach (@{$dep->{$module}}) {
+ if( $exp->{$_} ) {
+ warn "resolved symbol $_ in file $exp->{$_}\n" if $verbose;
+ next if $exp->{$_} =~ /vmlinux/;
+ $mod->{$module}{$exp->{$_}} = 1;
+ } else {
+ warn "unresolved symbol $_ in file $module\n";
+ }
+ }
+}
+
+# figure out where the output should go
+if ($stdout == 0) {
+ open(STDOUT, ">$basedir/modules.dep")
+ or die "cannot open $basedir/modules.dep: $!";
+}
+my $kseries = $basedir =~ m,/2\.6\.[^/]*, ? '2.6' : '2.4';
+
+foreach my $module ( keys %$mod ) {
+ if($kseries eq '2.4') {
+ print "$module:\t";
+ my @sorted = sort bydep keys %{$mod->{$module}};
+ print join(" \\\n\t",@sorted);
+ print "\n\n";
+ } else {
+ print "$module: ";
+ my @sorted = sort bydep keys %{$mod->{$module}};
+ print join(" ",@sorted);
+ print "\n";
+ }
+}
+
+
+sub build_ref_tables
+{
+ my ($name, $sym_ar, $exp, $dep) = @_;
+
+ my $ksymtab = grep m/ __ksymtab/, @$sym_ar;
+
+ # gather the exported symbols
+ if($ksymtab){
+ # explicitly exported
+ foreach ( @$sym_ar ) {
+ / __ksymtab_(.*)$/ and do {
+ warn "sym = $1\n" if $verbose;
+ $exp->{$1} = $name;
+ };
+ }
+ } else {
+ # exporting all symbols
+ foreach ( @$sym_ar ) {
+ / [ABCDGRST] (.*)$/ and do {
+ warn "syma = $1\n" if $verbose;
+ $exp->{$1} = $name;
+ };
+ }
+ }
+
+ # this takes makes sure modules with no dependencies get listed
+ push @{$dep->{$name}}, 'printk' unless $name eq 'vmlinux';
+
+ # gather the unresolved symbols
+ foreach ( @$sym_ar ) {
+ !/ __this_module/ && / U (.*)$/ and do {
+ warn "und = $1\n" if $verbose;
+ push @{$dep->{$name}}, $1;
+ };
+ }
+}
+
+sub bydep
+{
+ foreach my $f ( keys %{$mod->{$b}} ) {
+ if($f eq $a) {
+ return 1;
+ }
+ }
+ return -1;
+}
+
+
+
+__END__
+
+=head1 NAME
+
+depmod.pl - a cross platform script to generate kernel module
+dependency lists (modules.conf) which can then be used by modprobe
+on the target platform.
+
+It supports Linux 2.4 and 2.6 styles of modules.conf (auto-detected)
+
+=head1 SYNOPSIS
+
+depmod.pl [OPTION]... [basedir]...
+
+Example:
+
+ depmod.pl -F linux/System.map -b target/lib/modules/2.6.11
+
+=head1 DESCRIPTION
+
+The purpose of this script is to automagically generate a list of of kernel
+module dependencies. This script produces dependency lists that should be
+identical to the depmod program from the modutils package. Unlike the depmod
+binary, however, depmod.pl is designed to be run on your host system, not
+on your target system.
+
+This script was written by David Schleef <ds@schleef.org> to be used in
+conjunction with the BusyBox modprobe applet.
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<-h --help>
+
+This displays the help message.
+
+=item B<-b --basedir>
+
+The base directory uner which the target's modules will be found. This
+defaults to the /lib/modules directory.
+
+If you don't specify the kernel version, this script will search for
+one under the specified based directory and use the first thing that
+looks like a kernel version.
+
+=item B<-k --kernel>
+
+Kernel binary for the target (vmlinux). You must either supply a kernel binary
+or a kernel symbol file (using the -F option).
+
+=item B<-F --kernelsyms>
+
+Kernel symbol file for the target (System.map).
+
+=item B<-n --stdout>
+
+Write to stdout instead of modules.dep
+kernel binary for the target (using the -k option).
+
+=item B<--verbose>
+
+Verbose (debug) output
+
+=back
+
+=head1 COPYRIGHT AND LICENSE
+
+ Copyright (c) 2001 David Schleef <ds@schleef.org>
+ Copyright (c) 2001 Erik Andersen <andersen@codepoet.org>
+ Copyright (c) 2001 Stuart Hughes <seh@zee2.com>
+ Copyright (c) 2002 Steven J. Hill <shill@broadcom.com>
+ Copyright (c) 2006 Freescale Semiconductor, Inc <stuarth@freescale.com>
+
+This program is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 AUTHOR
+
+David Schleef <ds@schleef.org>
+
+=cut
diff --git a/i/pc104/initrd/conf/busybox/examples/devfsd.conf b/i/pc104/initrd/conf/busybox/examples/devfsd.conf
new file mode 100644
index 0000000..e90e710
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/examples/devfsd.conf
@@ -0,0 +1,133 @@
+# Sample /etc/devfsd.conf configuration file.
+# Richard Gooch <rgooch@atnf.csiro.au> 17-FEB-2002
+#
+# adapted for busybox devfsd implementation by Tito <farmatito@tiscali.it>
+#
+# Enable full compatibility mode for old device names. You may comment these
+# out if you don't use the old device names. Make sure you know what you're
+# doing!
+REGISTER .* MKOLDCOMPAT
+UNREGISTER .* RMOLDCOMPAT
+
+# You may comment out the above and uncomment the following if you've
+# configured your system to use the original "new" devfs names or the really
+# new names
+#REGISTER ^vc/ MKOLDCOMPAT
+#UNREGISTER ^vc/ RMOLDCOMPAT
+#REGISTER ^pty/ MKOLDCOMPAT
+#UNREGISTER ^pty/ RMOLDCOMPAT
+#REGISTER ^misc/ MKOLDCOMPAT
+#UNREGISTER ^misc/ RMOLDCOMPAT
+
+# You may comment these out if you don't use the original "new" names
+REGISTER .* MKNEWCOMPAT
+UNREGISTER .* RMNEWCOMPAT
+
+# Enable module autoloading. You may comment this out if you don't use
+# autoloading
+# Supported by busybox when CONFIG_DEVFSD_MODLOAD is set.
+# This actually doesn't work with busybox modutils but needs
+# the real modutils' modprobe
+LOOKUP .* MODLOAD
+
+# Uncomment the following if you want to set the group to "tty" for the
+# pseudo-tty devices. This is necessary so that mesg(1) can later be used to
+# enable/disable talk requests and wall(1) messages.
+REGISTER ^pty/s.* PERMISSIONS -1.tty 0600
+#REGISTER ^pts/.* PERMISSIONS -1.tty 0600
+
+# Restoring /dev/log on startup would trigger the minilogd/initlog deadlock
+# (minilogd falsely assuming syslogd has been started).
+REGISTER ^log$ IGNORE
+CREATE ^log$ IGNORE
+CHANGE ^log$ IGNORE
+DELETE ^log$ IGNORE
+
+#
+# Uncomment this if you want permissions to be saved and restored
+# Do not do this for pseudo-terminal devices
+REGISTER ^pt[sy] IGNORE
+CREATE ^pt[sy] IGNORE
+CHANGE ^pt[sy] IGNORE
+DELETE ^pt[sy] IGNORE
+REGISTER .* COPY /lib/dev-state/$devname $devpath
+CREATE .* COPY $devpath /lib/dev-state/$devname
+CHANGE .* COPY $devpath /lib/dev-state/$devname
+#DELETE .* CFUNCTION GLOBAL unlink /lib/dev-state/$devname
+# Busybox
+DELETE .* EXECUTE /bin/rm -f /lib/dev-state/$devname
+
+RESTORE /lib/dev-state
+
+#
+# Uncomment this if you want the old /dev/cdrom symlink
+#REGISTER ^cdroms/cdrom0$ CFUNCTION GLOBAL mksymlink $devname cdrom
+#UNREGISTER ^cdroms/cdrom0$ CFUNCTION GLOBAL unlink cdrom
+# busybox
+REGISTER ^cdroms/cdrom0$ EXECUTE /bin/ln -sf $devname cdrom
+UNREGISTER ^cdroms/cdrom0$ EXECUTE /bin/rm -f cdrom
+
+#REGISTER ^v4l/video0$ CFUNCTION GLOBAL mksymlink v4l/video0 video
+#UNREGISTER ^v4l/video0$ CFUNCTION GLOBAL unlink video
+#REGISTER ^radio0$ CFUNCTION GLOBAL mksymlink radio0 radio
+#UNREGISTER ^radio0$ CFUNCTION GLOBAL unlink radio
+# Busybox
+REGISTER ^v4l/video0$ EXECUTE /bin/ln -sf v4l/video0 video
+UNREGISTER ^v4l/video0$ EXECUTE /bin/rm -f video
+REGISTER ^radio0$ EXECUTE /bin/ln -sf radio0 radio
+UNREGISTER ^radio0$ EXECUTE /bin/rm -f radio
+
+# ALSA stuff
+#LOOKUP snd MODLOAD ACTION snd
+
+# Uncomment this to let PAM manage devfs
+# Not supported by busybox
+#REGISTER .* CFUNCTION /lib/security/pam_console_apply_devfsd.so pam_console_apply_single $devpath
+
+# Uncomment this to manage USB mouse
+# Not supported by busybox
+#REGISTER ^input/mouse0$ CFUNCTION GLOBAL mksymlink $devname usbmouse
+#UNREGISTER ^input/mouse0$ CFUNCTION GLOBAL unlink usbmouse
+# Busybox
+#REGISTER ^input/mouse0$ EXECUTE /bin/ln -sf $devname usbmouse
+#UNREGISTER ^input/mouse0$ EXECUTE /bin/rm -f usbmouse
+# Not supported by busybox
+#REGISTER ^input/mice$ CFUNCTION GLOBAL mksymlink $devname usbmouse
+#UNREGISTER ^input/mice$ CFUNCTION GLOBAL unlink usbmouse
+# Busybox
+REGISTER ^input/mice$ EXECUTE /bin/ln -sf $devname usbmouse
+UNREGISTER ^input/mice$ EXECUTE /bin/rm -f usbmouse
+
+# If you have removable media and want to force media revalidation when looking
+# up new or old compatibility names, uncomment the following lines
+# SCSI NEWCOMPAT /dev/sd/* names
+LOOKUP ^(sd/c[0-9]+b[0-9]+t[0-9]+u[0-9]+)p[0-9]+$ EXECUTE /bin/dd if=$mntpnt/\1 of=/dev/null count=1
+# SCSI OLDCOMPAT /dev/sd?? names
+LOOKUP ^(sd[a-z]+)[0-9]+$ EXECUTE /bin/dd if=$mntpnt/\1 of=/dev/null count=1
+# IDE NEWCOMPAT /dev/ide/hd/* names
+LOOKUP ^(ide/hd/c[0-9]+b[0-9]+t[0-9]+u[0-9]+)p[0-9]+$ EXECUTE /bin/dd if=$mntpnt/\1 of=/dev/null count=1
+# IDE OLDCOMPAT /dev/hd?? names
+LOOKUP ^(hd[a-z])[0-9]+$ EXECUTE /bin/dd if=$mntpnt/\1 of=/dev/null count=1
+# IDE-SCSI NEWCOMPAT /dev/sd/* names
+#LOOKUP ^(sd/c[0-9]+b[0-9]+t[0-9]+u[0-9]+)p[0-9]+$ EXECUTE /bin/dd if=$mntpnt/\1 of=/dev/null count=1
+#SCSI OLDCOMPAT /dev/scd? names
+LOOKUP ^(scd+)[0-9]+$ EXECUTE /bin/dd if=$mntpnt/\1 of=/dev/null count=1
+
+
+REGISTER ^dvb/card[0-9]+/[^/]+$ PERMISSIONS root.video 0660
+# Not supported by busybox
+#REGISTER ^dvb/card([0-9]+)/([^/0-9]*)[0-9]+$ CFUNCTION GLOBAL mksymlink /dev/$devname ost/\2\1
+#UNREGISTER ^dvb/card([0-9]+)/([^/0-9]*)[0-9]+$ CFUNCTION GLOBAL unlink ost/\2\1
+# Busybox
+REGISTER ^dvb/card([0-9]+)/([^/0-9]*)[0-9]+$ EXECUTE /bin/ln -sf /dev/$devname ost/\2\1
+UNREGISTER ^dvb/card([0-9]+)/([^/0-9]*)[0-9]+$ EXECUTE /bin/rm -f ost/\2\1
+
+# Include package-generated files from /etc/devfs/conf.d
+# Supported by busybox
+# INCLUDE /etc/devfs/conf.d/
+INCLUDE /etc/devfs/busybox/
+# Busybox: just for testing
+#INCLUDE /etc/devfs/nothing/
+#INCLUDE /etc/devfs/nothing/nothing
+#OPTIONAL_INCLUDE /etc/devfs/nothing/
+#OPTIONAL_INCLUDE /etc/devfs/nothing/nothing
diff --git a/i/pc104/initrd/conf/busybox/examples/dnsd.conf b/i/pc104/initrd/conf/busybox/examples/dnsd.conf
new file mode 100644
index 0000000..8af622b
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/examples/dnsd.conf
@@ -0,0 +1 @@
+thebox 192.168.1.5
diff --git a/i/pc104/initrd/conf/busybox/examples/inetd.conf b/i/pc104/initrd/conf/busybox/examples/inetd.conf
new file mode 100644
index 0000000..ca7e3d8
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/examples/inetd.conf
@@ -0,0 +1,73 @@
+# /etc/inetd.conf: see inetd(8) for further informations.
+#
+# Internet server configuration database
+#
+#
+# If you want to disable an entry so it isn't touched during
+# package updates just comment it out with a single '#' character.
+#
+# If you make changes to this file, either reboot your machine or
+# send the inetd process a HUP signal:
+# Do a "ps x" as root and look up the pid of inetd. Then do a
+# kill -HUP <pid of inetd>
+# inetd will re-read this file whenever it gets that signal.
+# <service_name> <sock_type> <proto> <flags> <user> <server_path> <args>
+#
+#:INTERNAL: Internal services
+# It is generally considered safer to keep these off.
+echo stream tcp nowait root internal
+echo dgram udp wait root internal
+#discard stream tcp nowait root internal
+#discard dgram udp wait root internal
+daytime stream tcp nowait root internal
+daytime dgram udp wait root internal
+#chargen stream tcp nowait root internal
+#chargen dgram udp wait root internal
+time stream tcp nowait root internal
+time dgram udp wait root internal
+
+# These are standard services.
+#
+#ftp stream tcp nowait root /usr/sbin/tcpd in.ftpd
+#telnet stream tcp nowait root /sbin/telnetd /sbin/telnetd
+#nntp stream tcp nowait root tcpd in.nntpd
+#smtp stream tcp nowait root tcpd sendmail -v
+#
+# Shell, login, exec and talk are BSD protocols.
+#
+# If you run an ntalk daemon (such as netkit-ntalk) on the old talk
+# port, that is, "talk" as opposed to "ntalk", it won't work and may
+# cause certain broken talk clients to malfunction.
+#
+# The talkd from netkit-ntalk 0.12 and higher, however, can speak the
+# old talk protocol and can be used safely.
+#
+#shell stream tcp nowait root /usr/sbin/tcpd in.rshd -L
+#login stream tcp nowait root /usr/sbin/tcpd in.rlogind -L
+#exec stream tcp nowait root /usr/sbin/tcpd in.rexecd
+#talk dgram udp wait root /usr/sbin/tcpd in.talkd
+#ntalk dgram udp wait root /usr/sbin/tcpd in.talkd
+#
+# Pop et al
+# Leave these off unless you're using them.
+#pop2 stream tcp nowait root /usr/sbin/tcpd in.pop2d
+#pop3 stream tcp nowait root /usr/sbin/tcpd in.pop3d
+#
+# The Internet UUCP service.
+# uucp stream tcp nowait uucp /usr/sbin/tcpd /usr/lib/uucp/uucico -l
+#
+# Tftp service is provided primarily for booting. Most sites
+# run this only on machines acting as "boot servers." If you don't
+# need it, don't use it.
+#
+#tftp dgram udp wait nobody /usr/sbin/tcpd in.tftpd
+#bootps dgram udp wait root /usr/sbin/in.bootpd in.bootpd
+#
+# Finger, systat and netstat give out user information which may be
+# valuable to potential "system crackers." Many sites choose to disable
+# some or all of these services to improve security.
+#
+#finger stream tcp nowait nobody /usr/sbin/tcpd in.fingerd -w
+#systat stream tcp nowait nobody /usr/sbin/tcpd /bin/ps -auwwx
+#netstat stream tcp nowait root /bin/netstat /bin/netstat -a
+#ident stream tcp nowait root /usr/sbin/in.identd in.identd
diff --git a/i/pc104/initrd/conf/busybox/examples/inittab b/i/pc104/initrd/conf/busybox/examples/inittab
new file mode 100644
index 0000000..5f2af87
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/examples/inittab
@@ -0,0 +1,90 @@
+# /etc/inittab init(8) configuration for BusyBox
+#
+# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+#
+#
+# Note, BusyBox init doesn't support runlevels. The runlevels field is
+# completely ignored by BusyBox init. If you want runlevels, use sysvinit.
+#
+#
+# Format for each entry: <id>:<runlevels>:<action>:<process>
+#
+# <id>: WARNING: This field has a non-traditional meaning for BusyBox init!
+#
+# The id field is used by BusyBox init to specify the controlling tty for
+# the specified process to run on. The contents of this field are
+# appended to "/dev/" and used as-is. There is no need for this field to
+# be unique, although if it isn't you may have strange results. If this
+# field is left blank, it is completely ignored. Also note that if
+# BusyBox detects that a serial console is in use, then all entries
+# containing non-empty id fields will be ignored. BusyBox init does
+# nothing with utmp. We don't need no stinkin' utmp.
+#
+# <runlevels>: The runlevels field is completely ignored.
+#
+# <action>: Valid actions include: sysinit, respawn, askfirst, wait, once,
+# restart, ctrlaltdel, and shutdown.
+#
+# Note: askfirst acts just like respawn, but before running the specified
+# process it displays the line "Please press Enter to activate this
+# console." and then waits for the user to press enter before starting
+# the specified process.
+#
+# Note: unrecognised actions (like initdefault) will cause init to emit
+# an error message, and then go along with its business.
+#
+# <process>: Specifies the process to be executed and it's command line.
+#
+# Note: BusyBox init works just fine without an inittab. If no inittab is
+# found, it has the following default behavior:
+# ::sysinit:/etc/init.d/rcS
+# ::askfirst:/bin/sh
+# ::ctrlaltdel:/sbin/reboot
+# ::shutdown:/sbin/swapoff -a
+# ::shutdown:/bin/umount -a -r
+# ::restart:/sbin/init
+#
+# if it detects that /dev/console is _not_ a serial console, it will
+# also run:
+# tty2::askfirst:/bin/sh
+# tty3::askfirst:/bin/sh
+# tty4::askfirst:/bin/sh
+#
+# Boot-time system configuration/initialization script.
+# This is run first except when booting in single-user mode.
+#
+::sysinit:/etc/init.d/rcS
+
+# /bin/sh invocations on selected ttys
+#
+# Note below that we prefix the shell commands with a "-" to indicate to the
+# shell that it is supposed to be a login shell. Normally this is handled by
+# login, but since we are bypassing login in this case, BusyBox lets you do
+# this yourself...
+#
+# Start an "askfirst" shell on the console (whatever that may be)
+::askfirst:-/bin/sh
+# Start an "askfirst" shell on /dev/tty2-4
+tty2::askfirst:-/bin/sh
+tty3::askfirst:-/bin/sh
+tty4::askfirst:-/bin/sh
+
+# /sbin/getty invocations for selected ttys
+tty4::respawn:/sbin/getty 38400 tty5
+tty5::respawn:/sbin/getty 38400 tty6
+
+# Example of how to put a getty on a serial line (for a terminal)
+#::respawn:/sbin/getty -L ttyS0 9600 vt100
+#::respawn:/sbin/getty -L ttyS1 9600 vt100
+#
+# Example how to put a getty on a modem line.
+#::respawn:/sbin/getty 57600 ttyS2
+
+# Stuff to do when restarting the init process
+::restart:/sbin/init
+
+# Stuff to do before rebooting
+::ctrlaltdel:/sbin/reboot
+::shutdown:/bin/umount -a -r
+::shutdown:/sbin/swapoff -a
+
diff --git a/i/pc104/initrd/conf/busybox/examples/mk2knr.pl b/i/pc104/initrd/conf/busybox/examples/mk2knr.pl
new file mode 100755
index 0000000..1259b84
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/examples/mk2knr.pl
@@ -0,0 +1,84 @@
+#!/usr/bin/perl -w
+#
+# @(#) mk2knr.pl - generates a perl script that converts lexemes to K&R-style
+#
+# How to use this script:
+# - In the busybox directory type 'examples/mk2knr.pl files-to-convert'
+# - Review the 'convertme.pl' script generated and remove / edit any of the
+# substitutions in there (please especially check for false positives)
+# - Type './convertme.pl same-files-as-before'
+# - Compile and see if it works
+#
+# BUGS: This script does not ignore strings inside comments or strings inside
+# quotes (it probably should).
+
+# set this to something else if you want
+$convertme = 'convertme.pl';
+
+# internal-use variables (don't touch)
+$convert = 0;
+%converted = ();
+
+# if no files were specified, print usage
+die "usage: $0 file.c | file.h\n" if scalar(@ARGV) == 0;
+
+# prepare the "convert me" file
+open(CM, ">$convertme") or die "convertme.pl $!";
+print CM "#!/usr/bin/perl -p -i\n\n";
+
+# process each file passed on the cmd line
+while (<>) {
+
+ # if the line says "getopt" in it anywhere, we don't want to muck with it
+ # because option lists tend to include strings like "cxtzvOf:" which get
+ # matched by the "check for mixed case" regexps below
+ next if /getopt/;
+
+ # tokenize the string into just the variables
+ while (/([a-zA-Z_][a-zA-Z0-9_]*)/g) {
+ $var = $1;
+
+ # ignore the word "BusyBox"
+ next if ($var =~ /BusyBox/);
+
+ # this checks for javaStyle or szHungarianNotation
+ $convert++ if ($var =~ /^[a-z]+[A-Z][a-z]+/);
+
+ # this checks for PascalStyle
+ $convert++ if ($var =~ /^[A-Z][a-z]+[A-Z][a-z]+/);
+
+ # if we want to add more checks, we can add 'em here, but the above
+ # checks catch "just enough" and not too much, so prolly not.
+
+ if ($convert) {
+ $convert = 0;
+
+ # skip ahead if we've already dealt with this one
+ next if ($converted{$var});
+
+ # record that we've dealt with this var
+ $converted{$var} = 1;
+
+ print CM "s/\\b$var\\b/"; # more to come in just a minute
+
+ # change the first letter to lower-case
+ $var = lcfirst($var);
+
+ # put underscores before all remaining upper-case letters
+ $var =~ s/([A-Z])/_$1/g;
+
+ # now change the remaining characters to lower-case
+ $var = lc($var);
+
+ print CM "$var/g;\n";
+ }
+ }
+}
+
+# tidy up and make the $convertme script executable
+close(CM);
+chmod 0755, $convertme;
+
+# print a helpful help message
+print "Done. Scheduled name changes are in $convertme.\n";
+print "Please review/modify it and then type ./$convertme to do the search & replace.\n";
diff --git a/i/pc104/initrd/conf/busybox/examples/udhcp/sample.bound b/i/pc104/initrd/conf/busybox/examples/udhcp/sample.bound
new file mode 100755
index 0000000..2a95d8b
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/examples/udhcp/sample.bound
@@ -0,0 +1,31 @@
+#!/bin/sh
+# Sample udhcpc renew script
+
+RESOLV_CONF="/etc/udhcpc/resolv.conf"
+
+[ -n "$broadcast" ] && BROADCAST="broadcast $broadcast"
+[ -n "$subnet" ] && NETMASK="netmask $subnet"
+
+/sbin/ifconfig $interface $ip $BROADCAST $NETMASK
+
+if [ -n "$router" ]
+then
+ echo "deleting routers"
+ while /sbin/route del default gw 0.0.0.0 dev $interface
+ do :
+ done
+
+ metric=0
+ for i in $router
+ do
+ /sbin/route add default gw $i dev $interface metric $((metric++))
+ done
+fi
+
+echo -n > $RESOLV_CONF
+[ -n "$domain" ] && echo domain $domain >> $RESOLV_CONF
+for i in $dns
+do
+ echo adding dns $i
+ echo nameserver $i >> $RESOLV_CONF
+done \ No newline at end of file
diff --git a/i/pc104/initrd/conf/busybox/examples/udhcp/sample.deconfig b/i/pc104/initrd/conf/busybox/examples/udhcp/sample.deconfig
new file mode 100755
index 0000000..b221bcf
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/examples/udhcp/sample.deconfig
@@ -0,0 +1,4 @@
+#!/bin/sh
+# Sample udhcpc deconfig script
+
+/sbin/ifconfig $interface 0.0.0.0
diff --git a/i/pc104/initrd/conf/busybox/examples/udhcp/sample.nak b/i/pc104/initrd/conf/busybox/examples/udhcp/sample.nak
new file mode 100755
index 0000000..f4d08e6
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/examples/udhcp/sample.nak
@@ -0,0 +1,4 @@
+#!/bin/sh
+# Sample udhcpc nak script
+
+echo Received a NAK: $message
diff --git a/i/pc104/initrd/conf/busybox/examples/udhcp/sample.renew b/i/pc104/initrd/conf/busybox/examples/udhcp/sample.renew
new file mode 100755
index 0000000..842bafe
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/examples/udhcp/sample.renew
@@ -0,0 +1,31 @@
+#!/bin/sh
+# Sample udhcpc bound script
+
+RESOLV_CONF="/etc/udhcpc/resolv.conf"
+
+[ -n "$broadcast" ] && BROADCAST="broadcast $broadcast"
+[ -n "$subnet" ] && NETMASK="netmask $subnet"
+
+/sbin/ifconfig $interface $ip $BROADCAST $NETMASK
+
+if [ -n "$router" ]
+then
+ echo "deleting routers"
+ while /sbin/route del default gw 0.0.0.0 dev $interface
+ do :
+ done
+
+ metric=0
+ for i in $router
+ do
+ /sbin/route add default gw $i dev $interface metric $((metric++))
+ done
+fi
+
+echo -n > $RESOLV_CONF
+[ -n "$domain" ] && echo domain $domain >> $RESOLV_CONF
+for i in $dns
+do
+ echo adding dns $i
+ echo nameserver $i >> $RESOLV_CONF
+done \ No newline at end of file
diff --git a/i/pc104/initrd/conf/busybox/examples/udhcp/sample.script b/i/pc104/initrd/conf/busybox/examples/udhcp/sample.script
new file mode 100644
index 0000000..9b717ac
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/examples/udhcp/sample.script
@@ -0,0 +1,7 @@
+#!/bin/sh
+# Currently, we only dispatch according to command. However, a more
+# elaborate system might dispatch by command and interface or do some
+# common initialization first, especially if more dhcp event notifications
+# are added.
+
+exec /usr/share/udhcpc/sample.$1
diff --git a/i/pc104/initrd/conf/busybox/examples/udhcp/simple.script b/i/pc104/initrd/conf/busybox/examples/udhcp/simple.script
new file mode 100644
index 0000000..98ebc15
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/examples/udhcp/simple.script
@@ -0,0 +1,40 @@
+#!/bin/sh
+
+# udhcpc script edited by Tim Riker <Tim@Rikers.org>
+
+[ -z "$1" ] && echo "Error: should be called from udhcpc" && exit 1
+
+RESOLV_CONF="/etc/resolv.conf"
+[ -n "$broadcast" ] && BROADCAST="broadcast $broadcast"
+[ -n "$subnet" ] && NETMASK="netmask $subnet"
+
+case "$1" in
+ deconfig)
+ /sbin/ifconfig $interface 0.0.0.0
+ ;;
+
+ renew|bound)
+ /sbin/ifconfig $interface $ip $BROADCAST $NETMASK
+
+ if [ -n "$router" ] ; then
+ echo "deleting routers"
+ while route del default gw 0.0.0.0 dev $interface ; do
+ :
+ done
+
+ metric=0
+ for i in $router ; do
+ route add default gw $i dev $interface metric $((metric++))
+ done
+ fi
+
+ echo -n > $RESOLV_CONF
+ [ -n "$domain" ] && echo search $domain >> $RESOLV_CONF
+ for i in $dns ; do
+ echo adding dns $i
+ echo nameserver $i >> $RESOLV_CONF
+ done
+ ;;
+esac
+
+exit 0
diff --git a/i/pc104/initrd/conf/busybox/examples/udhcp/udhcpd.conf b/i/pc104/initrd/conf/busybox/examples/udhcp/udhcpd.conf
new file mode 100644
index 0000000..34e47f7
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/examples/udhcp/udhcpd.conf
@@ -0,0 +1,123 @@
+# Sample udhcpd configuration file (/etc/udhcpd.conf)
+
+# The start and end of the IP lease block
+
+start 192.168.0.20 #default: 192.168.0.20
+end 192.168.0.254 #default: 192.168.0.254
+
+
+# The interface that udhcpd will use
+
+interface eth0 #default: eth0
+
+
+# The maximim number of leases (includes addressesd reserved
+# by OFFER's, DECLINE's, and ARP conficts
+
+#max_leases 254 #default: 254
+
+
+# If remaining is true (default), udhcpd will store the time
+# remaining for each lease in the udhcpd leases file. This is
+# for embedded systems that cannot keep time between reboots.
+# If you set remaining to no, the absolute time that the lease
+# expires at will be stored in the dhcpd.leases file.
+
+#remaining yes #default: yes
+
+
+# The time period at which udhcpd will write out a dhcpd.leases
+# file. If this is 0, udhcpd will never automatically write a
+# lease file. (specified in seconds)
+
+#auto_time 7200 #default: 7200 (2 hours)
+
+
+# The amount of time that an IP will be reserved (leased) for if a
+# DHCP decline message is received (seconds).
+
+#decline_time 3600 #default: 3600 (1 hour)
+
+
+# The amount of time that an IP will be reserved (leased) for if an
+# ARP conflct occurs. (seconds
+
+#conflict_time 3600 #default: 3600 (1 hour)
+
+
+# How long an offered address is reserved (leased) in seconds
+
+#offer_time 60 #default: 60 (1 minute)
+
+# If a lease to be given is below this value, the full lease time is
+# instead used (seconds).
+
+#min_lease 60 #defult: 60
+
+
+# The location of the leases file
+
+#lease_file /var/lib/misc/udhcpd.leases #defualt: /var/lib/misc/udhcpd.leases
+
+# The location of the pid file
+#pidfile /var/run/udhcpd.pid #default: /var/run/udhcpd.pid
+
+# Everytime udhcpd writes a leases file, the below script will be called.
+# Useful for writing the lease file to flash every few hours.
+
+#notify_file #default: (no script)
+
+#notify_file dumpleases # <--- useful for debugging
+
+# The following are bootp specific options, setable by udhcpd.
+
+#siaddr 192.168.0.22 #default: 0.0.0.0
+
+#sname zorak #default: (none)
+
+#boot_file /var/nfs_root #default: (none)
+
+# The remainer of options are DHCP options and can be specifed with the
+# keyword 'opt' or 'option'. If an option can take multiple items, such
+# as the dns option, they can be listed on the same line, or multiple
+# lines. The only option with a default is 'lease'.
+
+#Examles
+opt dns 192.168.10.2 192.168.10.10
+option subnet 255.255.255.0
+opt router 192.168.10.2
+opt wins 192.168.10.10
+option dns 129.219.13.81 # appened to above DNS servers for a total of 3
+option domain local
+option lease 864000 # 10 days of seconds
+
+
+# Currently supported options, for more info, see options.c
+#opt subnet
+#opt timezone
+#opt router
+#opt timesvr
+#opt namesvr
+#opt dns
+#opt logsvr
+#opt cookiesvr
+#opt lprsvr
+#opt bootsize
+#opt domain
+#opt swapsvr
+#opt rootpath
+#opt ipttl
+#opt mtu
+#opt broadcast
+#opt wins
+#opt lease
+#opt ntpsrv
+#opt tftp
+#opt bootfile
+
+
+# Static leases map
+#static_lease 00:60:08:11:CE:4E 192.168.0.54
+#static_lease 00:60:08:11:CE:3E 192.168.0.44
+
+
diff --git a/i/pc104/initrd/conf/busybox/examples/undeb b/i/pc104/initrd/conf/busybox/examples/undeb
new file mode 100644
index 0000000..37104e9
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/examples/undeb
@@ -0,0 +1,53 @@
+#!/bin/sh
+#
+# This should work with the GNU version of tar and gzip!
+# This should work with the bash or ash shell!
+# Requires the programs (ar, tar, gzip, and the pager more or less).
+#
+usage() {
+echo "Usage: undeb -c package.deb <Print control file info>"
+echo " undeb -l package.deb <List contents of deb package>"
+echo " undeb -x package.deb /foo/boo <Extract deb package to this directory,"
+echo " put . for current directory>"
+exit
+}
+
+deb=$2
+
+exist() {
+if [ "$deb" = "" ]; then
+usage
+elif [ ! -s "$deb" ]; then
+echo "Can't find $deb!"
+exit
+fi
+}
+
+if [ "$1" = "" ]; then
+usage
+elif [ "$1" = "-l" ]; then
+exist
+type more >/dev/null 2>&1 && pager=more
+type less >/dev/null 2>&1 && pager=less
+[ "$pager" = "" ] && echo "No pager found!" && exit
+(ar -p $deb control.tar.gz | tar -xzO *control ; echo -e "\nPress enter to scroll, q to Quit!\n" ; ar -p $deb data.tar.gz | tar -tzv) | $pager
+exit
+elif [ "$1" = "-c" ]; then
+exist
+ar -p $deb control.tar.gz | tar -xzO *control
+exit
+elif [ "$1" = "-x" ]; then
+exist
+if [ "$3" = "" ]; then
+usage
+elif [ ! -d "$3" ]; then
+echo "No such directory $3!"
+exit
+fi
+ar -p $deb data.tar.gz | tar -xzvpf - -C $3 || exit
+echo
+echo "Extracted $deb to $3!"
+exit
+else
+usage
+fi
diff --git a/i/pc104/initrd/conf/busybox/examples/unrpm b/i/pc104/initrd/conf/busybox/examples/unrpm
new file mode 100644
index 0000000..7fd3676
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/examples/unrpm
@@ -0,0 +1,48 @@
+#!/bin/sh
+#
+# This should work with the GNU version of cpio and gzip!
+# This should work with the bash or ash shell!
+# Requires the programs (cpio, gzip, and the pager more or less).
+#
+usage() {
+echo "Usage: unrpm -l package.rpm <List contents of rpm package>"
+echo " unrpm -x package.rpm /foo/boo <Extract rpm package to this directory,"
+echo " put . for current directory>"
+exit
+}
+
+rpm=$2
+
+exist() {
+if [ "$rpm" = "" ]; then
+usage
+elif [ ! -s "$rpm" ]; then
+echo "Can't find $rpm!"
+exit
+fi
+}
+
+if [ "$1" = "" ]; then
+usage
+elif [ "$1" = "-l" ]; then
+exist
+type more >/dev/null 2>&1 && pager=more
+type less >/dev/null 2>&1 && pager=less
+[ "$pager" = "" ] && echo "No pager found!" && exit
+(echo -e "\nPress enter to scroll, q to Quit!\n" ; rpm2cpio $rpm | cpio -tv --quiet) | $pager
+exit
+elif [ "$1" = "-x" ]; then
+exist
+if [ "$3" = "" ]; then
+usage
+elif [ ! -d "$3" ]; then
+echo "No such directory $3!"
+exit
+fi
+rpm2cpio $rpm | (umask 0 ; cd $3 ; cpio -idmuv) || exit
+echo
+echo "Extracted $rpm to $3!"
+exit
+else
+usage
+fi
diff --git a/i/pc104/initrd/conf/busybox/examples/zcip.script b/i/pc104/initrd/conf/busybox/examples/zcip.script
new file mode 100644
index 0000000..988e542
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/examples/zcip.script
@@ -0,0 +1,38 @@
+#!/bin/sh
+
+# only for use as a "zcip" callback script
+if [ "x$interface" = x ]
+then
+ exit 1
+fi
+
+# zcip should start on boot/resume and various media changes
+case "$1" in
+init)
+ # for now, zcip requires the link to be already up,
+ # and it drops links when they go down. that isn't
+ # the most robust model...
+ exit 0
+ ;;
+config)
+ if [ "x$ip" = x ]
+ then
+ exit 1
+ fi
+ # remember $ip for $interface, to use on restart
+ if [ "x$IP" != x -a -w "$IP.$interface" ]
+ then
+ echo $ip > "$IP.$interface"
+ fi
+ exec ip address add dev $interface \
+ scope link local "$ip/16" broadcast +
+ ;;
+deconfig)
+ if [ x$ip = x ]
+ then
+ exit 1
+ fi
+ exec ip address del dev $interface local $ip
+ ;;
+esac
+exit 1
diff --git a/i/pc104/initrd/conf/busybox/findutils/Config.in b/i/pc104/initrd/conf/busybox/findutils/Config.in
new file mode 100644
index 0000000..dd8a8c9
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/findutils/Config.in
@@ -0,0 +1,175 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+menu "Finding Utilities"
+
+config FIND
+ bool "find"
+ default n
+ help
+ find is used to search your system to find specified files.
+
+config FEATURE_FIND_PRINT0
+ bool "Enable -print0 option"
+ default y
+ depends on FIND
+ help
+ Causes output names to be separated by a null character
+ rather than a newline. This allows names that contain
+ newlines and other whitespace to be more easily
+ interpreted by other programs.
+
+config FEATURE_FIND_MTIME
+ bool "Enable modified time matching (-mtime) option"
+ default y
+ depends on FIND
+ help
+ Allow searching based on the modification time of
+ files, in days.
+
+config FEATURE_FIND_MMIN
+ bool "Enable modified time matching (-min) option"
+ default y
+ depends on FIND
+ help
+ Allow searching based on the modification time of
+ files, in minutes.
+
+config FEATURE_FIND_PERM
+ bool "Enable permissions matching (-perm) option"
+ default y
+ depends on FIND
+ help
+ Enable searching based on file permissions.
+
+config FEATURE_FIND_TYPE
+ bool "Enable filetype matching (-type) option"
+ default y
+ depends on FIND
+ help
+ Enable searching based on file type (file,
+ directory, socket, device, etc.).
+
+config FEATURE_FIND_XDEV
+ bool "Enable stay in filesystem (-xdev) option"
+ default y
+ depends on FIND
+ help
+ This option will allow find to restrict searches to a single
+ filesystem.
+
+config FEATURE_FIND_NEWER
+ bool "Enable -newer option for comparing file mtimes"
+ default y
+ depends on FIND
+ help
+ Support the 'find -newer' option for finding any files which have
+ a modified time that is more recent than the specified FILE.
+
+config FEATURE_FIND_INUM
+ bool "Enable inode number matching (-inum) option"
+ default y
+ depends on FIND
+ help
+ Support the 'find -inum' option for searching by inode number.
+
+config FEATURE_FIND_EXEC
+ bool "Enable (-exec) option allowing execution of commands"
+ default y
+ depends on FIND
+ help
+ Support the 'find -exec' option for executing commands based upon
+ the files matched.
+
+config FEATURE_FIND_USER
+ bool "Enable username/uid matching (-user) option"
+ default y
+ depends on FIND
+ help
+ Support the 'find -user' option for searching by username or uid.
+
+config FEATURE_FIND_NOT
+ bool "Enable the 'not' (!) operator"
+ default y
+ depends on FIND
+ help
+ Support the '!' operator to invert the test results. If 'Enable
+ full-blown desktop' is enabled, then will also support the
+ non-POSIX notation '-not'.
+
+config GREP
+ bool "grep"
+ default n
+ help
+ grep is used to search files for a specified pattern.
+
+config FEATURE_GREP_EGREP_ALIAS
+ bool "Support extended regular expressions (egrep & grep -E)"
+ default y
+ depends on GREP
+ help
+ Enabled support for extended regular expressions. Extended
+ regular expressions allow for alternation (foo|bar), grouping,
+ and various repetition operators.
+
+config FEATURE_GREP_FGREP_ALIAS
+ bool "Alias fgrep to grep -F"
+ default y
+ depends on GREP
+ help
+ fgrep sees the search pattern as a normal string rather than
+ regular expressions.
+ grep -F is always builtin, this just creates the fgrep alias.
+
+config FEATURE_GREP_CONTEXT
+ bool "Enable before and after context flags (-A, -B and -C)"
+ default y
+ depends on GREP
+ help
+ Print the specified number of leading (-B) and/or trailing (-A)
+ context surrounding our matching lines.
+ Print the specified number of context lines (-C).
+
+config XARGS
+ bool "xargs"
+ default n
+ help
+ xargs is used to execute a specified command on
+ every item from standard input.
+
+config FEATURE_XARGS_SUPPORT_CONFIRMATION
+ bool "Enable prompt and confirmation option -p"
+ default n
+ depends on XARGS
+ help
+ Support prompt the user about whether to run each command
+ line and read a line from the terminal.
+
+config FEATURE_XARGS_SUPPORT_QUOTES
+ bool "Enable support single and double quotes and backslash"
+ default n
+ depends on XARGS
+ help
+ Default xargs unsupport single and double quotes
+ and backslash for can use aruments with spaces.
+
+config FEATURE_XARGS_SUPPORT_TERMOPT
+ bool "Enable support options -x"
+ default n
+ depends on XARGS
+ help
+ Enable support exit if the size (see the -s or -n option)
+ is exceeded.
+
+config FEATURE_XARGS_SUPPORT_ZERO_TERM
+ bool "Enable null terminated option -0"
+ default n
+ depends on XARGS
+ help
+ Enable input filenames are terminated by a null character
+ instead of by whitespace, and the quotes and backslash
+ are not special.
+
+endmenu
diff --git a/i/pc104/initrd/conf/busybox/findutils/Kbuild b/i/pc104/initrd/conf/busybox/findutils/Kbuild
new file mode 100644
index 0000000..7b504ba
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/findutils/Kbuild
@@ -0,0 +1,10 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under the GPL v2, see the file LICENSE in this tarball.
+
+lib-y:=
+lib-$(CONFIG_FIND) += find.o
+lib-$(CONFIG_GREP) += grep.o
+lib-$(CONFIG_XARGS) += xargs.o
diff --git a/i/pc104/initrd/conf/busybox/findutils/find.c b/i/pc104/initrd/conf/busybox/findutils/find.c
new file mode 100644
index 0000000..e98d995
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/findutils/find.c
@@ -0,0 +1,645 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini find implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Reworked by David Douthitt <n9ubh@callsign.net> and
+ * Matt Kraai <kraai@alumni.carnegiemellon.edu>.
+ *
+ * Licensed under the GPL version 2, see the file LICENSE in this tarball.
+ */
+
+/* findutils-4.1.20:
+ *
+ * # find file.txt -exec 'echo {}' '{} {}' ';'
+ * find: echo file.txt: No such file or directory
+ * # find file.txt -exec 'echo' '{} {}' '; '
+ * find: missing argument to `-exec'
+ * # find file.txt -exec 'echo {}' '{} {}' ';' junk
+ * find: paths must precede expression
+ * # find file.txt -exec 'echo {}' '{} {}' ';' junk ';'
+ * find: paths must precede expression
+ * # find file.txt -exec 'echo' '{} {}' ';'
+ * file.txt file.txt
+ * (strace: execve("/bin/echo", ["echo", "file.txt file.txt"], [ 30 vars ]))
+ * # find file.txt -exec 'echo' '{} {}' ';' -print -exec pwd ';'
+ * file.txt file.txt
+ * file.txt
+ * /tmp
+ * # find -name '*.c' -o -name '*.h'
+ * [shows files, *.c and *.h intermixed]
+ * # find file.txt -name '*f*' -o -name '*t*'
+ * file.txt
+ * # find file.txt -name '*z*' -o -name '*t*'
+ * file.txt
+ * # find file.txt -name '*f*' -o -name '*z*'
+ * file.txt
+ *
+ * # find t z -name '*t*' -print -o -name '*z*'
+ * t
+ * # find t z t z -name '*t*' -o -name '*z*' -print
+ * z
+ * z
+ * # find t z t z '(' -name '*t*' -o -name '*z*' ')' -o -print
+ * (no output)
+ */
+
+#include <fnmatch.h>
+#include "busybox.h"
+
+USE_FEATURE_FIND_XDEV(static dev_t *xdev_dev;)
+USE_FEATURE_FIND_XDEV(static int xdev_count;)
+
+typedef int (*action_fp)(const char *fileName, struct stat *statbuf, void *);
+
+typedef struct {
+ action_fp f;
+#if ENABLE_FEATURE_FIND_NOT
+ smallint invert;
+#endif
+} action;
+#define ACTS(name, arg...) typedef struct { action a; arg; } action_##name;
+#define ACTF(name) static int func_##name(const char *fileName, struct stat *statbuf, action_##name* ap)
+ ACTS(print)
+ ACTS(name, const char *pattern;)
+USE_FEATURE_FIND_PRINT0(ACTS(print0))
+USE_FEATURE_FIND_TYPE( ACTS(type, int type_mask;))
+USE_FEATURE_FIND_PERM( ACTS(perm, char perm_char; mode_t perm_mask;))
+USE_FEATURE_FIND_MTIME( ACTS(mtime, char mtime_char; unsigned mtime_days;))
+USE_FEATURE_FIND_MMIN( ACTS(mmin, char mmin_char; unsigned mmin_mins;))
+USE_FEATURE_FIND_NEWER( ACTS(newer, time_t newer_mtime;))
+USE_FEATURE_FIND_INUM( ACTS(inum, ino_t inode_num;))
+USE_FEATURE_FIND_EXEC( ACTS(exec, char **exec_argv; unsigned int *subst_count; int exec_argc;))
+USE_FEATURE_FIND_USER( ACTS(user, int uid;))
+USE_DESKTOP( ACTS(paren, action ***subexpr;))
+USE_DESKTOP( ACTS(size, off_t size;))
+USE_DESKTOP( ACTS(prune))
+
+static action ***actions;
+static smalluint need_print = 1;
+
+
+#if ENABLE_FEATURE_FIND_EXEC
+static unsigned int count_subst(const char *str)
+{
+ unsigned int count = 0;
+ while ((str = strstr(str, "{}"))) {
+ count++;
+ str++;
+ }
+ return count;
+}
+
+
+static char* subst(const char *src, unsigned int count, const char* filename)
+{
+ char *buf, *dst, *end;
+ int flen = strlen(filename);
+ /* we replace each '{}' with filename: growth by strlen-2 */
+ buf = dst = xmalloc(strlen(src) + count*(flen-2) + 1);
+ while ((end = strstr(src, "{}"))) {
+ memcpy(dst, src, end - src);
+ dst += end - src;
+ src = end + 2;
+ memcpy(dst, filename, flen);
+ dst += flen;
+ }
+ strcpy(dst, src);
+ return buf;
+}
+#endif
+
+
+static int exec_actions(action ***appp, const char *fileName, struct stat *statbuf)
+{
+ int cur_group;
+ int cur_action;
+ int rc = TRUE;
+ action **app, *ap;
+
+ cur_group = -1;
+ while ((app = appp[++cur_group])) {
+ cur_action = -1;
+ while (1) {
+ ap = app[++cur_action];
+ if (!ap) {
+ /* all actions in group were successful */
+ return rc;
+ }
+ rc = ap->f(fileName, statbuf, ap);
+#if ENABLE_FEATURE_FIND_NOT
+ if (ap->invert) rc = !rc;
+#endif
+ if (!rc) {
+ /* current group failed, try next */
+ break;
+ }
+ }
+ }
+ return rc;
+}
+
+
+ACTF(name)
+{
+ const char *tmp = strrchr(fileName, '/');
+ if (tmp == NULL)
+ tmp = fileName;
+ else
+ tmp++;
+ return fnmatch(ap->pattern, tmp, FNM_PERIOD) == 0;
+}
+#if ENABLE_FEATURE_FIND_TYPE
+ACTF(type)
+{
+ return ((statbuf->st_mode & S_IFMT) == ap->type_mask);
+}
+#endif
+#if ENABLE_FEATURE_FIND_PERM
+ACTF(perm)
+{
+ /* -perm +mode: at least one of perm_mask bits are set */
+ if (ap->perm_char == '+')
+ return (statbuf->st_mode & ap->perm_mask) != 0;
+ /* -perm -mode: all of perm_mask are set */
+ if (ap->perm_char == '-')
+ return (statbuf->st_mode & ap->perm_mask) == ap->perm_mask;
+ /* -perm mode: file mode must match perm_mask */
+ return (statbuf->st_mode & 07777) == ap->perm_mask;
+}
+#endif
+#if ENABLE_FEATURE_FIND_MTIME
+ACTF(mtime)
+{
+ time_t file_age = time(NULL) - statbuf->st_mtime;
+ time_t mtime_secs = ap->mtime_days * 24*60*60;
+ if (ap->mtime_char == '+')
+ return file_age >= mtime_secs + 24*60*60;
+ if (ap->mtime_char == '-')
+ return file_age < mtime_secs;
+ /* just numeric mtime */
+ return file_age >= mtime_secs && file_age < (mtime_secs + 24*60*60);
+}
+#endif
+#if ENABLE_FEATURE_FIND_MMIN
+ACTF(mmin)
+{
+ time_t file_age = time(NULL) - statbuf->st_mtime;
+ time_t mmin_secs = ap->mmin_mins * 60;
+ if (ap->mmin_char == '+')
+ return file_age >= mmin_secs + 60;
+ if (ap->mmin_char == '-')
+ return file_age < mmin_secs;
+ /* just numeric mmin */
+ return file_age >= mmin_secs && file_age < (mmin_secs + 60);
+}
+#endif
+#if ENABLE_FEATURE_FIND_NEWER
+ACTF(newer)
+{
+ return (ap->newer_mtime < statbuf->st_mtime);
+}
+#endif
+#if ENABLE_FEATURE_FIND_INUM
+ACTF(inum)
+{
+ return (statbuf->st_ino == ap->inode_num);
+}
+#endif
+#if ENABLE_FEATURE_FIND_EXEC
+ACTF(exec)
+{
+ int i, rc;
+ char *argv[ap->exec_argc+1];
+ for (i = 0; i < ap->exec_argc; i++)
+ argv[i] = subst(ap->exec_argv[i], ap->subst_count[i], fileName);
+ argv[i] = NULL; /* terminate the list */
+ rc = wait4pid(spawn(argv));
+ if (rc)
+ bb_perror_msg("%s", argv[0]);
+ for (i = 0; i < ap->exec_argc; i++)
+ free(argv[i]);
+ return rc == 0; /* return 1 if success */
+}
+#endif
+
+#if ENABLE_FEATURE_FIND_USER
+ACTF(user)
+{
+ return (statbuf->st_uid == ap->uid);
+}
+#endif
+
+#if ENABLE_FEATURE_FIND_PRINT0
+ACTF(print0)
+{
+ printf("%s%c", fileName, '\0');
+ return TRUE;
+}
+#endif
+
+ACTF(print)
+{
+ puts(fileName);
+ return TRUE;
+}
+
+#if ENABLE_DESKTOP
+ACTF(paren)
+{
+ return exec_actions(ap->subexpr, fileName, statbuf);
+}
+
+/*
+ * -prune: if -depth is not given, return true and do not descend
+ * current dir; if -depth is given, return false with no effect.
+ * Example:
+ * find dir -name 'asm-*' -prune -o -name '*.[chS]' -print
+ */
+ACTF(prune)
+{
+ return SKIP;
+}
+
+ACTF(size)
+{
+ return statbuf->st_size == ap->size;
+}
+#endif
+
+
+static int fileAction(const char *fileName, struct stat *statbuf, void* junk, int depth)
+{
+ int rc;
+#ifdef CONFIG_FEATURE_FIND_XDEV
+ if (S_ISDIR(statbuf->st_mode) && xdev_count) {
+ int i;
+ for (i = 0; i < xdev_count; i++) {
+ if (xdev_dev[i] != statbuf->st_dev)
+ return SKIP;
+ }
+ }
+#endif
+ rc = exec_actions(actions, fileName, statbuf);
+ /* Had no explicit -print[0] or -exec? then print */
+ if (rc && need_print)
+ puts(fileName);
+ /* Cannot return 0: our caller, recursive_action(),
+ * will perror() and skip dirs (if called on dir) */
+ return rc == 0 ? TRUE : rc;
+}
+
+
+#if ENABLE_FEATURE_FIND_TYPE
+static int find_type(const char *type)
+{
+ int mask = 0;
+
+ switch (type[0]) {
+ case 'b':
+ mask = S_IFBLK;
+ break;
+ case 'c':
+ mask = S_IFCHR;
+ break;
+ case 'd':
+ mask = S_IFDIR;
+ break;
+ case 'p':
+ mask = S_IFIFO;
+ break;
+ case 'f':
+ mask = S_IFREG;
+ break;
+ case 'l':
+ mask = S_IFLNK;
+ break;
+ case 's':
+ mask = S_IFSOCK;
+ break;
+ }
+
+ if (mask == 0 || type[1] != '\0')
+ bb_error_msg_and_die(bb_msg_invalid_arg, type, "-type");
+
+ return mask;
+}
+#endif
+
+#if ENABLE_FEATURE_FIND_PERM || ENABLE_FEATURE_FIND_MTIME \
+ || ENABLE_FEATURE_FIND_MMIN
+static const char* plus_minus_num(const char* str)
+{
+ if (*str == '-' || *str == '+')
+ str++;
+ return str;
+}
+#endif
+
+static action*** parse_params(char **argv)
+{
+ action*** appp;
+ unsigned cur_group = 0;
+ unsigned cur_action = 0;
+ USE_FEATURE_FIND_NOT( smallint invert_flag = 0; )
+
+ action* alloc_action(int sizeof_struct, action_fp f)
+ {
+ action *ap;
+ appp[cur_group] = xrealloc(appp[cur_group], (cur_action+2) * sizeof(*appp));
+ appp[cur_group][cur_action++] = ap = xmalloc(sizeof_struct);
+ appp[cur_group][cur_action] = NULL;
+ ap->f = f;
+ USE_FEATURE_FIND_NOT( ap->invert = invert_flag; )
+ USE_FEATURE_FIND_NOT( invert_flag = 0; )
+ return ap;
+ }
+#define ALLOC_ACTION(name) (action_##name*)alloc_action(sizeof(action_##name), (action_fp) func_##name)
+
+ appp = xzalloc(2 * sizeof(appp[0])); /* appp[0],[1] == NULL */
+
+// Actions have side effects and return a true or false value
+// We implement: -print, -print0, -exec
+
+// The rest are tests.
+
+// Tests and actions are grouped by operators
+// ( expr ) Force precedence
+// ! expr True if expr is false
+// -not expr Same as ! expr
+// expr1 [-a[nd]] expr2 And; expr2 is not evaluated if expr1 is false
+// expr1 -o[r] expr2 Or; expr2 is not evaluated if expr1 is true
+// expr1 , expr2 List; both expr1 and expr2 are always evaluated
+// We implement: (), -a, -o
+
+//XXX: TODO: Use index_in_str_array here
+ while (*argv) {
+ const char *arg = argv[0];
+ const char *arg1 = argv[1];
+ /* --- Operators --- */
+ if (strcmp(arg, "-a") == 0
+ USE_DESKTOP(|| strcmp(arg, "-and") == 0)
+ ) {
+ /* no further special handling required */
+ }
+ else if (strcmp(arg, "-o") == 0
+ USE_DESKTOP(|| strcmp(arg, "-or") == 0)
+ ) {
+ /* start new OR group */
+ cur_group++;
+ appp = xrealloc(appp, (cur_group+2) * sizeof(*appp));
+ /*appp[cur_group] = NULL; - already NULL */
+ appp[cur_group+1] = NULL;
+ cur_action = 0;
+ }
+#if ENABLE_FEATURE_FIND_NOT
+ else if (LONE_CHAR(arg, '!')
+ USE_DESKTOP(|| strcmp(arg, "-not") == 0)
+ ) {
+ /* also handles "find ! ! -name 'foo*'" */
+ invert_flag ^= 1;
+ }
+#endif
+
+ /* --- Tests and actions --- */
+ else if (strcmp(arg, "-print") == 0) {
+ need_print = 0;
+ /* GNU find ignores '!' here: "find ! -print" */
+ USE_FEATURE_FIND_NOT( invert_flag = 0; )
+ (void) ALLOC_ACTION(print);
+ }
+#if ENABLE_FEATURE_FIND_PRINT0
+ else if (strcmp(arg, "-print0") == 0) {
+ need_print = 0;
+ USE_FEATURE_FIND_NOT( invert_flag = 0; )
+ (void) ALLOC_ACTION(print0);
+ }
+#endif
+ else if (strcmp(arg, "-name") == 0) {
+ action_name *ap;
+ if (!*++argv)
+ bb_error_msg_and_die(bb_msg_requires_arg, arg);
+ ap = ALLOC_ACTION(name);
+ ap->pattern = arg1;
+ }
+#if ENABLE_FEATURE_FIND_TYPE
+ else if (strcmp(arg, "-type") == 0) {
+ action_type *ap;
+ if (!*++argv)
+ bb_error_msg_and_die(bb_msg_requires_arg, arg);
+ ap = ALLOC_ACTION(type);
+ ap->type_mask = find_type(arg1);
+ }
+#endif
+#if ENABLE_FEATURE_FIND_PERM
+/* TODO:
+ * -perm mode File's permission bits are exactly mode (octal or symbolic).
+ * Symbolic modes use mode 0 as a point of departure.
+ * -perm -mode All of the permission bits mode are set for the file.
+ * -perm +mode Any of the permission bits mode are set for the file.
+ */
+ else if (strcmp(arg, "-perm") == 0) {
+ action_perm *ap;
+ if (!*++argv)
+ bb_error_msg_and_die(bb_msg_requires_arg, arg);
+ ap = ALLOC_ACTION(perm);
+ ap->perm_char = arg1[0];
+ arg1 = plus_minus_num(arg1);
+ ap->perm_mask = 0;
+ if (!bb_parse_mode(arg1, &ap->perm_mask))
+ bb_error_msg_and_die("invalid mode: %s", arg1);
+ }
+#endif
+#if ENABLE_FEATURE_FIND_MTIME
+ else if (strcmp(arg, "-mtime") == 0) {
+ action_mtime *ap;
+ if (!*++argv)
+ bb_error_msg_and_die(bb_msg_requires_arg, arg);
+ ap = ALLOC_ACTION(mtime);
+ ap->mtime_char = arg1[0];
+ ap->mtime_days = xatoul(plus_minus_num(arg1));
+ }
+#endif
+#if ENABLE_FEATURE_FIND_MMIN
+ else if (strcmp(arg, "-mmin") == 0) {
+ action_mmin *ap;
+ if (!*++argv)
+ bb_error_msg_and_die(bb_msg_requires_arg, arg);
+ ap = ALLOC_ACTION(mmin);
+ ap->mmin_char = arg1[0];
+ ap->mmin_mins = xatoul(plus_minus_num(arg1));
+ }
+#endif
+#if ENABLE_FEATURE_FIND_NEWER
+ else if (strcmp(arg, "-newer") == 0) {
+ action_newer *ap;
+ struct stat stat_newer;
+ if (!*++argv)
+ bb_error_msg_and_die(bb_msg_requires_arg, arg);
+ xstat(arg1, &stat_newer);
+ ap = ALLOC_ACTION(newer);
+ ap->newer_mtime = stat_newer.st_mtime;
+ }
+#endif
+#if ENABLE_FEATURE_FIND_INUM
+ else if (strcmp(arg, "-inum") == 0) {
+ action_inum *ap;
+ if (!*++argv)
+ bb_error_msg_and_die(bb_msg_requires_arg, arg);
+ ap = ALLOC_ACTION(inum);
+ ap->inode_num = xatoul(arg1);
+ }
+#endif
+#if ENABLE_FEATURE_FIND_EXEC
+ else if (strcmp(arg, "-exec") == 0) {
+ int i;
+ action_exec *ap;
+ need_print = 0;
+ USE_FEATURE_FIND_NOT( invert_flag = 0; )
+ ap = ALLOC_ACTION(exec);
+ ap->exec_argv = ++argv; /* first arg after -exec */
+ ap->exec_argc = 0;
+ while (1) {
+ if (!*argv) /* did not see ';' till end */
+ bb_error_msg_and_die(bb_msg_requires_arg, arg);
+ if (LONE_CHAR(argv[0], ';'))
+ break;
+ argv++;
+ ap->exec_argc++;
+ }
+ if (ap->exec_argc == 0)
+ bb_error_msg_and_die(bb_msg_requires_arg, arg);
+ ap->subst_count = xmalloc(ap->exec_argc * sizeof(int));
+ i = ap->exec_argc;
+ while (i--)
+ ap->subst_count[i] = count_subst(ap->exec_argv[i]);
+ }
+#endif
+#if ENABLE_FEATURE_FIND_USER
+ else if (strcmp(arg, "-user") == 0) {
+ action_user *ap;
+ if (!*++argv)
+ bb_error_msg_and_die(bb_msg_requires_arg, arg);
+ ap = ALLOC_ACTION(user);
+ ap->uid = bb_strtou(arg1, NULL, 10);
+ if (errno)
+ ap->uid = xuname2uid(arg1);
+ }
+#endif
+#if ENABLE_DESKTOP
+ else if (LONE_CHAR(arg, '(')) {
+ action_paren *ap;
+ char **endarg;
+ unsigned nested = 1;
+
+ endarg = argv;
+ while (1) {
+ if (!*++endarg)
+ bb_error_msg_and_die("unpaired '('");
+ if (LONE_CHAR(*endarg, '('))
+ nested++;
+ else if (LONE_CHAR(*endarg, ')') && !--nested) {
+ *endarg = NULL;
+ break;
+ }
+ }
+ ap = ALLOC_ACTION(paren);
+ ap->subexpr = parse_params(argv + 1);
+ *endarg = (char*) ")"; /* restore NULLed parameter */
+ argv = endarg;
+ }
+ else if (strcmp(arg, "-prune") == 0) {
+ USE_FEATURE_FIND_NOT( invert_flag = 0; )
+ (void) ALLOC_ACTION(prune);
+ }
+ else if (strcmp(arg, "-size") == 0) {
+ action_size *ap;
+ if (!*++argv)
+ bb_error_msg_and_die(bb_msg_requires_arg, arg);
+ ap = ALLOC_ACTION(size);
+ ap->size = XATOOFF(arg1);
+ }
+#endif
+ else
+ bb_show_usage();
+ argv++;
+ }
+
+ return appp;
+#undef ALLOC_ACTION
+}
+
+
+int find_main(int argc, char **argv);
+int find_main(int argc, char **argv)
+{
+ int dereference = FALSE;
+ char *arg;
+ char **argp;
+ int i, firstopt, status = EXIT_SUCCESS;
+
+ for (firstopt = 1; firstopt < argc; firstopt++) {
+ if (argv[firstopt][0] == '-')
+ break;
+ if (ENABLE_FEATURE_FIND_NOT && LONE_CHAR(argv[firstopt], '!'))
+ break;
+#if ENABLE_DESKTOP
+ if (LONE_CHAR(argv[firstopt], '('))
+ break;
+#endif
+ }
+ if (firstopt == 1) {
+ argv[0] = (char*)".";
+ argv--;
+ firstopt++;
+ }
+
+// All options always return true. They always take effect,
+// rather than being processed only when their place in the
+// expression is reached
+// We implement: -follow, -xdev
+
+ /* Process options, and replace then with -a */
+ /* (-a will be ignored by recursive parser later) */
+ argp = &argv[firstopt];
+ while ((arg = argp[0])) {
+ if (strcmp(arg, "-follow") == 0) {
+ dereference = TRUE;
+ argp[0] = (char*)"-a";
+ }
+#if ENABLE_FEATURE_FIND_XDEV
+ else if (strcmp(arg, "-xdev") == 0) {
+ struct stat stbuf;
+ if (!xdev_count) {
+ xdev_count = firstopt - 1;
+ xdev_dev = xmalloc(xdev_count * sizeof(dev_t));
+ for (i = 1; i < firstopt; i++) {
+ /* not xstat(): shouldn't bomb out on
+ * "find not_exist exist -xdev" */
+ if (stat(argv[i], &stbuf))
+ stbuf.st_dev = -1L;
+ xdev_dev[i-1] = stbuf.st_dev;
+ }
+ }
+ argp[0] = (char*)"-a";
+ }
+#endif
+ argp++;
+ }
+
+ actions = parse_params(&argv[firstopt]);
+
+ for (i = 1; i < firstopt; i++) {
+ if (!recursive_action(argv[i],
+ TRUE, // recurse
+ dereference, // follow links
+ FALSE, // depth first
+ fileAction, // file action
+ fileAction, // dir action
+ NULL, // user data
+ 0)) // depth
+ status = EXIT_FAILURE;
+ }
+ return status;
+}
diff --git a/i/pc104/initrd/conf/busybox/findutils/grep.c b/i/pc104/initrd/conf/busybox/findutils/grep.c
new file mode 100644
index 0000000..f6ea54e
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/findutils/grep.c
@@ -0,0 +1,507 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini grep implementation for busybox using libc regex.
+ *
+ * Copyright (C) 1999,2000,2001 by Lineo, inc. and Mark Whitley
+ * Copyright (C) 1999,2000,2001 by Mark Whitley <markw@codepoet.org>
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ */
+/* BB_AUDIT SUSv3 defects - unsupported option -x. */
+/* BB_AUDIT GNU defects - always acts as -a. */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/grep.html */
+/*
+ * 2004,2006 (C) Vladimir Oleynik <dzo@simtreas.ru> -
+ * correction "-e pattern1 -e pattern2" logic and more optimizations.
+ * precompiled regex
+ */
+/*
+ * (C) 2006 Jac Goudsmit added -o option
+ */
+
+#include "busybox.h"
+#include "xregex.h"
+
+/* options */
+#define OPTSTR_GREP \
+ "lnqvscFiHhe:f:Lor" \
+ USE_FEATURE_GREP_CONTEXT("A:B:C:") \
+ USE_FEATURE_GREP_EGREP_ALIAS("E") \
+ USE_DESKTOP("w") \
+ "aI"
+/* ignored: -a "assume all files to be text" */
+/* ignored: -I "assume binary files have no matches" */
+
+enum {
+ OPTBIT_l,
+ OPTBIT_n,
+ OPTBIT_q,
+ OPTBIT_v,
+ OPTBIT_s,
+ OPTBIT_c,
+ OPTBIT_F,
+ OPTBIT_i,
+ OPTBIT_H,
+ OPTBIT_h,
+ OPTBIT_e,
+ OPTBIT_f,
+ OPTBIT_L,
+ OPTBIT_o,
+ OPTBIT_r,
+ USE_FEATURE_GREP_CONTEXT(OPTBIT_A ,)
+ USE_FEATURE_GREP_CONTEXT(OPTBIT_B ,)
+ USE_FEATURE_GREP_CONTEXT(OPTBIT_C ,)
+ USE_FEATURE_GREP_CONTEXT(OPTBIT_E ,)
+ USE_DESKTOP( OPTBIT_w ,)
+ OPT_l = 1 << OPTBIT_l,
+ OPT_n = 1 << OPTBIT_n,
+ OPT_q = 1 << OPTBIT_q,
+ OPT_v = 1 << OPTBIT_v,
+ OPT_s = 1 << OPTBIT_s,
+ OPT_c = 1 << OPTBIT_c,
+ OPT_F = 1 << OPTBIT_F,
+ OPT_i = 1 << OPTBIT_i,
+ OPT_H = 1 << OPTBIT_H,
+ OPT_h = 1 << OPTBIT_h,
+ OPT_e = 1 << OPTBIT_e,
+ OPT_f = 1 << OPTBIT_f,
+ OPT_L = 1 << OPTBIT_L,
+ OPT_o = 1 << OPTBIT_o,
+ OPT_r = 1 << OPTBIT_r,
+ OPT_A = USE_FEATURE_GREP_CONTEXT((1 << OPTBIT_A)) + 0,
+ OPT_B = USE_FEATURE_GREP_CONTEXT((1 << OPTBIT_B)) + 0,
+ OPT_C = USE_FEATURE_GREP_CONTEXT((1 << OPTBIT_C)) + 0,
+ OPT_E = USE_FEATURE_GREP_CONTEXT((1 << OPTBIT_E)) + 0,
+ OPT_w = USE_DESKTOP( (1 << OPTBIT_w)) + 0,
+};
+
+#define PRINT_FILES_WITH_MATCHES (option_mask32 & OPT_l)
+#define PRINT_LINE_NUM (option_mask32 & OPT_n)
+#define BE_QUIET (option_mask32 & OPT_q)
+#define SUPPRESS_ERR_MSGS (option_mask32 & OPT_s)
+#define PRINT_MATCH_COUNTS (option_mask32 & OPT_c)
+#define FGREP_FLAG (option_mask32 & OPT_F)
+#define PRINT_FILES_WITHOUT_MATCHES (option_mask32 & OPT_L)
+
+typedef unsigned char byte_t;
+
+static int reflags;
+static byte_t invert_search;
+static byte_t print_filename;
+static byte_t open_errors;
+
+#if ENABLE_FEATURE_GREP_CONTEXT
+static int lines_before;
+static int lines_after;
+static char **before_buf;
+static int last_line_printed;
+#endif /* ENABLE_FEATURE_GREP_CONTEXT */
+
+/* globals used internally */
+static llist_t *pattern_head; /* growable list of patterns to match */
+static const char *cur_file; /* the current file we are reading */
+
+typedef struct grep_list_data_t {
+ char *pattern;
+ regex_t preg;
+#define PATTERN_MEM_A 1
+#define COMPILED 2
+ int flg_mem_alocated_compiled;
+} grep_list_data_t;
+
+static void print_line(const char *line, int linenum, char decoration)
+{
+#if ENABLE_FEATURE_GREP_CONTEXT
+ /* possibly print the little '--' separator */
+ if ((lines_before || lines_after) && last_line_printed &&
+ last_line_printed < linenum - 1) {
+ puts("--");
+ }
+ last_line_printed = linenum;
+#endif
+ if (print_filename)
+ printf("%s%c", cur_file, decoration);
+ if (PRINT_LINE_NUM)
+ printf("%i%c", linenum, decoration);
+ /* Emulate weird GNU grep behavior with -ov */
+ if ((option_mask32 & (OPT_v+OPT_o)) != (OPT_v+OPT_o))
+ puts(line);
+}
+
+static int grep_file(FILE *file)
+{
+ char *line;
+ byte_t ret;
+ int linenum = 0;
+ int nmatches = 0;
+ regmatch_t regmatch;
+#if ENABLE_FEATURE_GREP_CONTEXT
+ int print_n_lines_after = 0;
+ int curpos = 0; /* track where we are in the circular 'before' buffer */
+ int idx = 0; /* used for iteration through the circular buffer */
+#endif /* ENABLE_FEATURE_GREP_CONTEXT */
+
+ while ((line = xmalloc_getline(file)) != NULL) {
+ llist_t *pattern_ptr = pattern_head;
+ grep_list_data_t * gl;
+
+ linenum++;
+ ret = 0;
+ while (pattern_ptr) {
+ gl = (grep_list_data_t *)pattern_ptr->data;
+ if (FGREP_FLAG) {
+ ret = strstr(line, gl->pattern) != NULL;
+ } else {
+ /*
+ * test for a postitive-assertion match (regexec returns success (0)
+ * and the user did not specify invert search), or a negative-assertion
+ * match (regexec returns failure (REG_NOMATCH) and the user specified
+ * invert search)
+ */
+ if (!(gl->flg_mem_alocated_compiled & COMPILED)) {
+ gl->flg_mem_alocated_compiled |= COMPILED;
+ xregcomp(&(gl->preg), gl->pattern, reflags);
+ }
+ regmatch.rm_so = 0;
+ regmatch.rm_eo = 0;
+ if (regexec(&(gl->preg), line, 1, &regmatch, 0) == 0) {
+ if (!(option_mask32 & OPT_w))
+ ret = 1;
+ else {
+ char c = ' ';
+ if (regmatch.rm_so)
+ c = line[regmatch.rm_so - 1];
+ if (!isalnum(c) && c != '_') {
+ c = line[regmatch.rm_eo];
+ if (!c || (!isalnum(c) && c != '_'))
+ ret = 1;
+ }
+ }
+ }
+ }
+ pattern_ptr = pattern_ptr->link;
+ } /* while (pattern_ptr) */
+
+ if (ret ^ invert_search) {
+ if (PRINT_FILES_WITH_MATCHES || BE_QUIET)
+ free(line);
+
+ /* if we found a match but were told to be quiet, stop here */
+ if (BE_QUIET || PRINT_FILES_WITHOUT_MATCHES)
+ return -1;
+
+ /* keep track of matches */
+ nmatches++;
+
+ /* if we're just printing filenames, we stop after the first match */
+ if (PRINT_FILES_WITH_MATCHES)
+ break;
+
+ /* print the matched line */
+ if (PRINT_MATCH_COUNTS == 0) {
+#if ENABLE_FEATURE_GREP_CONTEXT
+ int prevpos = (curpos == 0) ? lines_before - 1 : curpos - 1;
+
+ /* if we were told to print 'before' lines and there is at least
+ * one line in the circular buffer, print them */
+ if (lines_before && before_buf[prevpos] != NULL) {
+ int first_buf_entry_line_num = linenum - lines_before;
+
+ /* advance to the first entry in the circular buffer, and
+ * figure out the line number is of the first line in the
+ * buffer */
+ idx = curpos;
+ while (before_buf[idx] == NULL) {
+ idx = (idx + 1) % lines_before;
+ first_buf_entry_line_num++;
+ }
+
+ /* now print each line in the buffer, clearing them as we go */
+ while (before_buf[idx] != NULL) {
+ print_line(before_buf[idx], first_buf_entry_line_num, '-');
+ free(before_buf[idx]);
+ before_buf[idx] = NULL;
+ idx = (idx + 1) % lines_before;
+ first_buf_entry_line_num++;
+ }
+ }
+
+ /* make a note that we need to print 'after' lines */
+ print_n_lines_after = lines_after;
+#endif
+ if (option_mask32 & OPT_o) {
+ line[regmatch.rm_eo] = '\0';
+ print_line(line + regmatch.rm_so, linenum, ':');
+ } else {
+ print_line(line, linenum, ':');
+ }
+ }
+ }
+#if ENABLE_FEATURE_GREP_CONTEXT
+ else { /* no match */
+ /* Add the line to the circular 'before' buffer */
+ if (lines_before) {
+ free(before_buf[curpos]);
+ before_buf[curpos] = xstrdup(line);
+ curpos = (curpos + 1) % lines_before;
+ }
+ }
+
+ /* if we need to print some context lines after the last match, do so */
+ if (print_n_lines_after && (last_line_printed != linenum)) {
+ print_line(line, linenum, '-');
+ print_n_lines_after--;
+ }
+#endif /* ENABLE_FEATURE_GREP_CONTEXT */
+ free(line);
+ }
+
+ /* special-case file post-processing for options where we don't print line
+ * matches, just filenames and possibly match counts */
+
+ /* grep -c: print [filename:]count, even if count is zero */
+ if (PRINT_MATCH_COUNTS) {
+ if (print_filename)
+ printf("%s:", cur_file);
+ printf("%d\n", nmatches);
+ }
+
+ /* grep -l: print just the filename, but only if we grepped the line in the file */
+ if (PRINT_FILES_WITH_MATCHES && nmatches > 0) {
+ puts(cur_file);
+ }
+
+ /* grep -L: print just the filename, but only if we didn't grep the line in the file */
+ if (PRINT_FILES_WITHOUT_MATCHES && nmatches == 0) {
+ puts(cur_file);
+ }
+
+ return nmatches;
+}
+
+#if ENABLE_FEATURE_CLEAN_UP
+#define new_grep_list_data(p, m) add_grep_list_data(p, m)
+static char * add_grep_list_data(char *pattern, int flg_used_mem)
+#else
+#define new_grep_list_data(p, m) add_grep_list_data(p)
+static char * add_grep_list_data(char *pattern)
+#endif
+{
+ grep_list_data_t *gl = xmalloc(sizeof(grep_list_data_t));
+ gl->pattern = pattern;
+#if ENABLE_FEATURE_CLEAN_UP
+ gl->flg_mem_alocated_compiled = flg_used_mem;
+#else
+ gl->flg_mem_alocated_compiled = 0;
+#endif
+ return (char *)gl;
+}
+
+
+static void load_regexes_from_file(llist_t *fopt)
+{
+ char *line;
+ FILE *f;
+
+ while (fopt) {
+ llist_t *cur = fopt;
+ char *ffile = cur->data;
+
+ fopt = cur->link;
+ free(cur);
+ f = xfopen(ffile, "r");
+ while ((line = xmalloc_getline(f)) != NULL) {
+ llist_add_to(&pattern_head,
+ new_grep_list_data(line, PATTERN_MEM_A));
+ }
+ }
+}
+
+static int file_action_grep(const char *filename, struct stat *statbuf, void* matched, int depth)
+{
+ FILE *file = fopen(filename, "r");
+ if (file == NULL) {
+ if (!SUPPRESS_ERR_MSGS)
+ bb_perror_msg("%s", cur_file);
+ open_errors = 1;
+ return 0;
+ }
+ cur_file = filename;
+ *(int*)matched += grep_file(file);
+ fclose(file);
+ return 1;
+}
+
+static int grep_dir(const char *dir)
+{
+ int matched = 0;
+ recursive_action(dir,
+ /* recurse= */ 1,
+ /* followLinks= */ 0,
+ /* depthFirst= */ 1,
+ /* fileAction= */ file_action_grep,
+ /* dirAction= */ NULL,
+ /* userData= */ &matched,
+ /* depth= */ 0);
+ return matched;
+}
+
+int grep_main(int argc, char **argv);
+int grep_main(int argc, char **argv)
+{
+ FILE *file;
+ int matched;
+ llist_t *fopt = NULL;
+
+ /* do normal option parsing */
+#if ENABLE_FEATURE_GREP_CONTEXT
+ char *slines_after;
+ char *slines_before;
+ char *Copt;
+
+ opt_complementary = "H-h:e::f::C-AB";
+ getopt32(argc, argv,
+ OPTSTR_GREP,
+ &pattern_head, &fopt,
+ &slines_after, &slines_before, &Copt);
+
+ if (option_mask32 & OPT_C) {
+ /* -C unsets prev -A and -B, but following -A or -B
+ may override it */
+ if (!(option_mask32 & OPT_A)) /* not overridden */
+ slines_after = Copt;
+ if (!(option_mask32 & OPT_B)) /* not overridden */
+ slines_before = Copt;
+ option_mask32 |= OPT_A|OPT_B; /* for parser */
+ }
+ if (option_mask32 & OPT_A) {
+ lines_after = xatoi_u(slines_after);
+ }
+ if (option_mask32 & OPT_B) {
+ lines_before = xatoi_u(slines_before);
+ }
+ /* sanity checks */
+ if (option_mask32 & (OPT_c|OPT_q|OPT_l|OPT_L)) {
+ option_mask32 &= ~OPT_n;
+ lines_before = 0;
+ lines_after = 0;
+ } else if (lines_before > 0)
+ before_buf = xzalloc(lines_before * sizeof(char *));
+#else
+ /* with auto sanity checks */
+ opt_complementary = "H-h:e::f::c-n:q-n:l-n";
+ getopt32(argc, argv, OPTSTR_GREP,
+ &pattern_head, &fopt);
+#endif
+ invert_search = ((option_mask32 & OPT_v) != 0); /* 0 | 1 */
+
+ if (pattern_head != NULL) {
+ /* convert char *argv[] to grep_list_data_t */
+ llist_t *cur;
+
+ for (cur = pattern_head; cur; cur = cur->link)
+ cur->data = new_grep_list_data(cur->data, 0);
+ }
+ if (option_mask32 & OPT_f)
+ load_regexes_from_file(fopt);
+
+ if (ENABLE_FEATURE_GREP_FGREP_ALIAS && applet_name[0] == 'f')
+ option_mask32 |= OPT_F;
+
+ if (!(option_mask32 & (OPT_o | OPT_w)))
+ reflags = REG_NOSUB;
+
+ if (ENABLE_FEATURE_GREP_EGREP_ALIAS &&
+ (applet_name[0] == 'e' || (option_mask32 & OPT_E)))
+ reflags |= REG_EXTENDED;
+
+ if (option_mask32 & OPT_i)
+ reflags |= REG_ICASE;
+
+ argv += optind;
+ argc -= optind;
+
+ /* if we didn't get a pattern from a -e and no command file was specified,
+ * argv[optind] should be the pattern. no pattern, no worky */
+ if (pattern_head == NULL) {
+ char *pattern;
+ if (*argv == NULL)
+ bb_show_usage();
+ pattern = new_grep_list_data(*argv++, 0);
+ llist_add_to(&pattern_head, pattern);
+ argc--;
+ }
+
+ /* argv[(optind)..(argc-1)] should be names of file to grep through. If
+ * there is more than one file to grep, we will print the filenames. */
+ if (argc > 1)
+ print_filename = 1;
+ /* -H / -h of course override */
+ if (option_mask32 & OPT_H)
+ print_filename = 1;
+ if (option_mask32 & OPT_h)
+ print_filename = 0;
+
+ /* If no files were specified, or '-' was specified, take input from
+ * stdin. Otherwise, we grep through all the files specified. */
+ if (argc == 0)
+ argc++;
+ matched = 0;
+ while (argc--) {
+ cur_file = *argv++;
+ file = stdin;
+ if (!cur_file || (*cur_file == '-' && !cur_file[1])) {
+ cur_file = "(standard input)";
+ } else {
+ if (option_mask32 & OPT_r) {
+ struct stat st;
+ if (stat(cur_file, &st) == 0 && S_ISDIR(st.st_mode)) {
+ if (!(option_mask32 & OPT_h))
+ print_filename = 1;
+ matched += grep_dir(cur_file);
+ goto grep_done;
+ }
+ }
+ /* else: fopen(dir) will succeed, but reading won't */
+ file = fopen(cur_file, "r");
+ if (file == NULL) {
+ if (!SUPPRESS_ERR_MSGS)
+ bb_perror_msg("%s", cur_file);
+ open_errors = 1;
+ continue;
+ }
+ }
+ matched += grep_file(file);
+ fclose_if_not_stdin(file);
+ grep_done:
+ if (matched < 0) {
+ /* we found a match but were told to be quiet, stop here and
+ * return success */
+ break;
+ }
+ }
+
+ /* destroy all the elments in the pattern list */
+ if (ENABLE_FEATURE_CLEAN_UP) {
+ while (pattern_head) {
+ llist_t *pattern_head_ptr = pattern_head;
+ grep_list_data_t *gl =
+ (grep_list_data_t *)pattern_head_ptr->data;
+
+ pattern_head = pattern_head->link;
+ if ((gl->flg_mem_alocated_compiled & PATTERN_MEM_A))
+ free(gl->pattern);
+ if ((gl->flg_mem_alocated_compiled & COMPILED))
+ regfree(&(gl->preg));
+ free(pattern_head_ptr);
+ }
+ }
+ /* 0 = success, 1 = failed, 2 = error */
+ /* If the -q option is specified, the exit status shall be zero
+ * if an input line is selected, even if an error was detected. */
+ if (BE_QUIET && matched)
+ return 0;
+ if (open_errors)
+ return 2;
+ return !matched; /* invert return value 0 = success, 1 = failed */
+}
diff --git a/i/pc104/initrd/conf/busybox/findutils/xargs.c b/i/pc104/initrd/conf/busybox/findutils/xargs.c
new file mode 100644
index 0000000..44dbcda
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/findutils/xargs.c
@@ -0,0 +1,540 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini xargs implementation for busybox
+ * Options are supported: "-prtx -n max_arg -s max_chars -e[ouf_str]"
+ *
+ * (C) 2002,2003 by Vladimir Oleynik <dzo@simtreas.ru>
+ *
+ * Special thanks
+ * - Mark Whitley and Glenn McGrath for stimulus to rewrite :)
+ * - Mike Rendell <michael@cs.mun.ca>
+ * and David MacKenzie <djm@gnu.ai.mit.edu>.
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ *
+ * xargs is described in the Single Unix Specification v3 at
+ * http://www.opengroup.org/onlinepubs/007904975/utilities/xargs.html
+ *
+ */
+
+#include "busybox.h"
+
+/* COMPAT: SYSV version defaults size (and has a max value of) to 470.
+ We try to make it as large as possible. */
+#if !defined(ARG_MAX) && defined(_SC_ARG_MAX)
+#define ARG_MAX sysconf (_SC_ARG_MAX)
+#endif
+#ifndef ARG_MAX
+#define ARG_MAX 470
+#endif
+
+
+#ifdef TEST
+# ifndef CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION
+# define CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION
+# endif
+# ifndef CONFIG_FEATURE_XARGS_SUPPORT_QUOTES
+# define CONFIG_FEATURE_XARGS_SUPPORT_QUOTES
+# endif
+# ifndef CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT
+# define CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT
+# endif
+# ifndef CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM
+# define CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM
+# endif
+#endif
+
+/*
+ This function has special algorithm.
+ Don't use fork and include to main!
+*/
+static int xargs_exec(char *const *args)
+{
+ pid_t p;
+ volatile int exec_errno = 0; /* shared vfork stack */
+ int status;
+
+ p = vfork();
+ if (p < 0)
+ bb_perror_msg_and_die("vfork");
+
+ if (p == 0) {
+ /* vfork -- child */
+ BB_EXECVP(args[0], args);
+ exec_errno = errno; /* set error to shared stack */
+ _exit(1);
+ }
+
+ /* vfork -- parent */
+ while (wait(&status) == (pid_t) -1)
+ if (errno != EINTR)
+ break;
+ if (exec_errno) {
+ errno = exec_errno;
+ bb_perror_msg("%s", args[0]);
+ return exec_errno == ENOENT ? 127 : 126;
+ }
+ if (WEXITSTATUS(status) == 255) {
+ bb_error_msg("%s: exited with status 255; aborting", args[0]);
+ return 124;
+ }
+ if (WIFSTOPPED(status)) {
+ bb_error_msg("%s: stopped by signal %d",
+ args[0], WSTOPSIG(status));
+ return 125;
+ }
+ if (WIFSIGNALED(status)) {
+ bb_error_msg("%s: terminated by signal %d",
+ args[0], WTERMSIG(status));
+ return 125;
+ }
+ if (WEXITSTATUS(status))
+ return 123;
+ return 0;
+}
+
+
+typedef struct xlist_s {
+ char *data;
+ size_t lenght;
+ struct xlist_s *link;
+} xlist_t;
+
+static int eof_stdin_detected;
+
+#define ISBLANK(c) ((c) == ' ' || (c) == '\t')
+#define ISSPACE(c) (ISBLANK(c) || (c) == '\n' || (c) == '\r' \
+ || (c) == '\f' || (c) == '\v')
+
+#ifdef CONFIG_FEATURE_XARGS_SUPPORT_QUOTES
+static xlist_t *process_stdin(xlist_t * list_arg,
+ const char *eof_str, size_t mc, char *buf)
+{
+#define NORM 0
+#define QUOTE 1
+#define BACKSLASH 2
+#define SPACE 4
+
+ char *s = NULL; /* start word */
+ char *p = NULL; /* pointer to end word */
+ char q = 0; /* quote char */
+ char state = NORM;
+ char eof_str_detected = 0;
+ size_t line_l = 0; /* size loaded args line */
+ int c; /* current char */
+ xlist_t *cur;
+ xlist_t *prev;
+
+ for (prev = cur = list_arg; cur; cur = cur->link) {
+ line_l += cur->lenght; /* previous allocated */
+ if (prev != cur)
+ prev = prev->link;
+ }
+
+ while (!eof_stdin_detected) {
+ c = getchar();
+ if (c == EOF) {
+ eof_stdin_detected++;
+ if (s)
+ goto unexpected_eof;
+ break;
+ }
+ if (eof_str_detected)
+ continue;
+ if (state == BACKSLASH) {
+ state = NORM;
+ goto set;
+ } else if (state == QUOTE) {
+ if (c == q) {
+ q = 0;
+ state = NORM;
+ } else {
+ goto set;
+ }
+ } else { /* if(state == NORM) */
+
+ if (ISSPACE(c)) {
+ if (s) {
+unexpected_eof:
+ state = SPACE;
+ c = 0;
+ goto set;
+ }
+ } else {
+ if (s == NULL)
+ s = p = buf;
+ if (c == '\\') {
+ state = BACKSLASH;
+ } else if (c == '\'' || c == '"') {
+ q = c;
+ state = QUOTE;
+ } else {
+set:
+ if ((size_t)(p - buf) >= mc)
+ bb_error_msg_and_die("argument line too long");
+ *p++ = c;
+ }
+ }
+ }
+ if (state == SPACE) { /* word's delimiter or EOF detected */
+ if (q) {
+ bb_error_msg_and_die("unmatched %s quote",
+ q == '\'' ? "single" : "double");
+ }
+ /* word loaded */
+ if (eof_str) {
+ eof_str_detected = strcmp(s, eof_str) == 0;
+ }
+ if (!eof_str_detected) {
+ size_t lenght = (p - buf);
+
+ cur = xmalloc(sizeof(xlist_t) + lenght);
+ cur->data = memcpy(cur + 1, s, lenght);
+ cur->lenght = lenght;
+ cur->link = NULL;
+ if (prev == NULL) {
+ list_arg = cur;
+ } else {
+ prev->link = cur;
+ }
+ prev = cur;
+ line_l += lenght;
+ if (line_l > mc) {
+ /* stop memory usage :-) */
+ break;
+ }
+ }
+ s = NULL;
+ state = NORM;
+ }
+ }
+ return list_arg;
+}
+#else
+/* The variant does not support single quotes, double quotes or backslash */
+static xlist_t *process_stdin(xlist_t * list_arg,
+ const char *eof_str, size_t mc, char *buf)
+{
+
+ int c; /* current char */
+ int eof_str_detected = 0;
+ char *s = NULL; /* start word */
+ char *p = NULL; /* pointer to end word */
+ size_t line_l = 0; /* size loaded args line */
+ xlist_t *cur;
+ xlist_t *prev;
+
+ for (prev = cur = list_arg; cur; cur = cur->link) {
+ line_l += cur->lenght; /* previous allocated */
+ if (prev != cur)
+ prev = prev->link;
+ }
+
+ while (!eof_stdin_detected) {
+ c = getchar();
+ if (c == EOF) {
+ eof_stdin_detected++;
+ }
+ if (eof_str_detected)
+ continue;
+ if (c == EOF || ISSPACE(c)) {
+ if (s == NULL)
+ continue;
+ c = EOF;
+ }
+ if (s == NULL)
+ s = p = buf;
+ if ((p - buf) >= mc)
+ bb_error_msg_and_die("argument line too long");
+ *p++ = c == EOF ? 0 : c;
+ if (c == EOF) { /* word's delimiter or EOF detected */
+ /* word loaded */
+ if (eof_str) {
+ eof_str_detected = strcmp(s, eof_str) == 0;
+ }
+ if (!eof_str_detected) {
+ size_t lenght = (p - buf);
+
+ cur = xmalloc(sizeof(xlist_t) + lenght);
+ cur->data = memcpy(cur + 1, s, lenght);
+ cur->lenght = lenght;
+ cur->link = NULL;
+ if (prev == NULL) {
+ list_arg = cur;
+ } else {
+ prev->link = cur;
+ }
+ prev = cur;
+ line_l += lenght;
+ if (line_l > mc) {
+ /* stop memory usage :-) */
+ break;
+ }
+ s = NULL;
+ }
+ }
+ }
+ return list_arg;
+}
+#endif /* CONFIG_FEATURE_XARGS_SUPPORT_QUOTES */
+
+
+#ifdef CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION
+/* Prompt the user for a response, and
+ if the user responds affirmatively, return true;
+ otherwise, return false. Used "/dev/tty", not stdin. */
+static int xargs_ask_confirmation(void)
+{
+ static FILE *tty_stream;
+ int c, savec;
+
+ if (!tty_stream) {
+ tty_stream = xfopen(CURRENT_TTY, "r");
+ /* pranoidal security by vodz */
+ fcntl(fileno(tty_stream), F_SETFD, FD_CLOEXEC);
+ }
+ fputs(" ?...", stderr);
+ fflush(stderr);
+ c = savec = getc(tty_stream);
+ while (c != EOF && c != '\n')
+ c = getc(tty_stream);
+ if (savec == 'y' || savec == 'Y')
+ return 1;
+ return 0;
+}
+#else
+# define xargs_ask_confirmation() 1
+#endif /* CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION */
+
+#ifdef CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM
+static xlist_t *process0_stdin(xlist_t * list_arg, const char *eof_str ATTRIBUTE_UNUSED,
+ size_t mc, char *buf)
+{
+ int c; /* current char */
+ char *s = NULL; /* start word */
+ char *p = NULL; /* pointer to end word */
+ size_t line_l = 0; /* size loaded args line */
+ xlist_t *cur;
+ xlist_t *prev;
+
+ for (prev = cur = list_arg; cur; cur = cur->link) {
+ line_l += cur->lenght; /* previous allocated */
+ if (prev != cur)
+ prev = prev->link;
+ }
+
+ while (!eof_stdin_detected) {
+ c = getchar();
+ if (c == EOF) {
+ eof_stdin_detected++;
+ if (s == NULL)
+ break;
+ c = 0;
+ }
+ if (s == NULL)
+ s = p = buf;
+ if ((size_t)(p - buf) >= mc)
+ bb_error_msg_and_die("argument line too long");
+ *p++ = c;
+ if (c == 0) { /* word's delimiter or EOF detected */
+ /* word loaded */
+ size_t lenght = (p - buf);
+
+ cur = xmalloc(sizeof(xlist_t) + lenght);
+ cur->data = memcpy(cur + 1, s, lenght);
+ cur->lenght = lenght;
+ cur->link = NULL;
+ if (prev == NULL) {
+ list_arg = cur;
+ } else {
+ prev->link = cur;
+ }
+ prev = cur;
+ line_l += lenght;
+ if (line_l > mc) {
+ /* stop memory usage :-) */
+ break;
+ }
+ s = NULL;
+ }
+ }
+ return list_arg;
+}
+#endif /* CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM */
+
+/* Correct regardless of combination of CONFIG_xxx */
+enum {
+ OPTBIT_VERBOSE = 0,
+ OPTBIT_NO_EMPTY,
+ OPTBIT_UPTO_NUMBER,
+ OPTBIT_UPTO_SIZE,
+ OPTBIT_EOF_STRING,
+ USE_FEATURE_XARGS_SUPPORT_CONFIRMATION(OPTBIT_INTERACTIVE,)
+ USE_FEATURE_XARGS_SUPPORT_TERMOPT( OPTBIT_TERMINATE ,)
+ USE_FEATURE_XARGS_SUPPORT_ZERO_TERM( OPTBIT_ZEROTERM ,)
+
+ OPT_VERBOSE = 1<<OPTBIT_VERBOSE ,
+ OPT_NO_EMPTY = 1<<OPTBIT_NO_EMPTY ,
+ OPT_UPTO_NUMBER = 1<<OPTBIT_UPTO_NUMBER,
+ OPT_UPTO_SIZE = 1<<OPTBIT_UPTO_SIZE ,
+ OPT_EOF_STRING = 1<<OPTBIT_EOF_STRING ,
+ OPT_INTERACTIVE = USE_FEATURE_XARGS_SUPPORT_CONFIRMATION((1<<OPTBIT_INTERACTIVE)) + 0,
+ OPT_TERMINATE = USE_FEATURE_XARGS_SUPPORT_TERMOPT( (1<<OPTBIT_TERMINATE )) + 0,
+ OPT_ZEROTERM = USE_FEATURE_XARGS_SUPPORT_ZERO_TERM( (1<<OPTBIT_ZEROTERM )) + 0,
+};
+#define OPTION_STR "+trn:s:e::" \
+ USE_FEATURE_XARGS_SUPPORT_CONFIRMATION("p") \
+ USE_FEATURE_XARGS_SUPPORT_TERMOPT( "x") \
+ USE_FEATURE_XARGS_SUPPORT_ZERO_TERM( "0")
+
+int xargs_main(int argc, char **argv);
+int xargs_main(int argc, char **argv)
+{
+ char **args;
+ int i, n;
+ xlist_t *list = NULL;
+ xlist_t *cur;
+ int child_error = 0;
+ char *max_args, *max_chars;
+ int n_max_arg;
+ size_t n_chars = 0;
+ long orig_arg_max;
+ const char *eof_str = "_";
+ unsigned opt;
+ size_t n_max_chars;
+#if ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM
+ xlist_t* (*read_args)(xlist_t*, const char*, size_t, char*) = process_stdin;
+#else
+#define read_args process_stdin
+#endif
+
+ opt = getopt32(argc, argv, OPTION_STR, &max_args, &max_chars, &eof_str);
+
+ if (opt & OPT_ZEROTERM)
+ USE_FEATURE_XARGS_SUPPORT_ZERO_TERM(read_args = process0_stdin);
+
+ argc -= optind;
+ argv += optind;
+ if (!argc) {
+ /* default behavior is to echo all the filenames */
+ *argv = (char*)"echo";
+ argc++;
+ }
+
+ orig_arg_max = ARG_MAX;
+ if (orig_arg_max == -1)
+ orig_arg_max = LONG_MAX;
+ orig_arg_max -= 2048; /* POSIX.2 requires subtracting 2048 */
+
+ if (opt & OPT_UPTO_SIZE) {
+ n_max_chars = xatoul_range(max_chars, 1, orig_arg_max);
+ for (i = 0; i < argc; i++) {
+ n_chars += strlen(*argv) + 1;
+ }
+ if (n_max_chars < n_chars) {
+ bb_error_msg_and_die("cannot fit single argument within argument list size limit");
+ }
+ n_max_chars -= n_chars;
+ } else {
+ /* Sanity check for systems with huge ARG_MAX defines (e.g., Suns which
+ have it at 1 meg). Things will work fine with a large ARG_MAX but it
+ will probably hurt the system more than it needs to; an array of this
+ size is allocated. */
+ if (orig_arg_max > 20 * 1024)
+ orig_arg_max = 20 * 1024;
+ n_max_chars = orig_arg_max;
+ }
+ max_chars = xmalloc(n_max_chars);
+
+ if (opt & OPT_UPTO_NUMBER) {
+ n_max_arg = xatoul_range(max_args, 1, INT_MAX);
+ } else {
+ n_max_arg = n_max_chars;
+ }
+
+ while ((list = read_args(list, eof_str, n_max_chars, max_chars)) != NULL ||
+ !(opt & OPT_NO_EMPTY))
+ {
+ opt |= OPT_NO_EMPTY;
+ n = 0;
+ n_chars = 0;
+#ifdef CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT
+ for (cur = list; cur;) {
+ n_chars += cur->lenght;
+ n++;
+ cur = cur->link;
+ if (n_chars > n_max_chars || (n == n_max_arg && cur)) {
+ if (opt & OPT_TERMINATE)
+ bb_error_msg_and_die("argument list too long");
+ break;
+ }
+ }
+#else
+ for (cur = list; cur; cur = cur->link) {
+ n_chars += cur->lenght;
+ n++;
+ if (n_chars > n_max_chars || n == n_max_arg) {
+ break;
+ }
+ }
+#endif /* CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT */
+
+ /* allocate pointers for execvp:
+ argc*arg, n*arg from stdin, NULL */
+ args = xzalloc((n + argc + 1) * sizeof(char *));
+
+ /* store the command to be executed
+ (taken from the command line) */
+ for (i = 0; i < argc; i++)
+ args[i] = argv[i];
+ /* (taken from stdin) */
+ for (cur = list; n; cur = cur->link) {
+ args[i++] = cur->data;
+ n--;
+ }
+
+ if (opt & (OPT_INTERACTIVE | OPT_VERBOSE)) {
+ for (i = 0; args[i]; i++) {
+ if (i)
+ fputc(' ', stderr);
+ fputs(args[i], stderr);
+ }
+ if (!(opt & OPT_INTERACTIVE))
+ fputc('\n', stderr);
+ }
+ if (!(opt & OPT_INTERACTIVE) || xargs_ask_confirmation()) {
+ child_error = xargs_exec(args);
+ }
+
+ /* clean up */
+ for (i = argc; args[i]; i++) {
+ cur = list;
+ list = list->link;
+ free(cur);
+ }
+ free(args);
+ if (child_error > 0 && child_error != 123) {
+ break;
+ }
+ }
+ if (ENABLE_FEATURE_CLEAN_UP) free(max_chars);
+ return child_error;
+}
+
+
+#ifdef TEST
+
+const char *applet_name = "debug stuff usage";
+
+void bb_show_usage(void)
+{
+ fprintf(stderr, "Usage: %s [-p] [-r] [-t] -[x] [-n max_arg] [-s max_chars]\n",
+ applet_name);
+ exit(1);
+}
+
+int main(int argc, char **argv)
+{
+ return xargs_main(argc, argv);
+}
+#endif /* TEST */
diff --git a/i/pc104/initrd/conf/busybox/include/applets.h b/i/pc104/initrd/conf/busybox/include/applets.h
new file mode 100644
index 0000000..3aaf011
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/include/applets.h
@@ -0,0 +1,341 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * applets.h - a listing of all busybox applets.
+ *
+ * If you write a new applet, you need to add an entry to this list to make
+ * busybox aware of it.
+ *
+ * It is CRUCIAL that this listing be kept in ascii order, otherwise the binary
+ * search lookup contributed by Gaute B Strokkenes stops working. If you value
+ * your kneecaps, you'll be sure to *make sure* that any changes made to this
+ * file result in the listing remaining in ascii order. You have been warned.
+ */
+
+#undef APPLET
+#undef APPLET_ODDNAME
+#undef APPLET_NOUSAGE
+
+#if defined(PROTOTYPES)
+# define APPLET(a,b,c) extern int a##_main(int argc, char **argv);
+# define APPLET_NOUSAGE(a,b,c,d) extern int b##_main(int argc, char **argv);
+# define APPLET_ODDNAME(a,b,c,d,e) extern int b##_main(int argc, char **argv);
+#elif defined(MAKE_USAGE)
+# ifdef CONFIG_FEATURE_VERBOSE_USAGE
+# define APPLET(a,b,c) a##_trivial_usage "\n\n" a##_full_usage "\0"
+# define APPLET_NOUSAGE(a,b,c,d) "\b\0"
+# define APPLET_ODDNAME(a,b,c,d,e) e##_trivial_usage "\n\n" e##_full_usage "\0"
+# else
+# define APPLET(a,b,c) a##_trivial_usage "\0"
+# define APPLET_NOUSAGE(a,b,c,d) "\b\0"
+# define APPLET_ODDNAME(a,b,c,d,e) e##_trivial_usage "\0"
+# endif
+#elif defined(MAKE_LINKS)
+# define APPLET(a,b,c) LINK b a
+# define APPLET_NOUSAGE(a,b,c,d) LINK c a
+# define APPLET_ODDNAME(a,b,c,d,e) LINK c a
+#else
+ const struct BB_applet applets[] = {
+# define APPLET(a,b,c) {#a,a##_main,b,c},
+# define APPLET_NOUSAGE(a,b,c,d) {#a,b##_main,c,d},
+# define APPLET_ODDNAME(a,b,c,d,e) {#a,b##_main,c,d},
+#endif
+
+#ifdef CONFIG_INSTALL_NO_USR
+# define _BB_DIR_USR_BIN _BB_DIR_BIN
+# define _BB_DIR_USR_SBIN _BB_DIR_SBIN
+#endif
+
+// _BB_SUID_ALWAYS: will complain if busybox isn't suid
+// and is run by non-root (applet_main() will not be called at all)
+// _BB_SUID_NEVER: will drop suid prior to applet_main()
+// _BB_SUID_MAYBE: neither of the above
+
+USE_TEST(APPLET_NOUSAGE([, test, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_TEST(APPLET_NOUSAGE([[, test, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_ADDGROUP(APPLET(addgroup, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_ADDUSER(APPLET(adduser, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_ADJTIMEX(APPLET(adjtimex, _BB_DIR_SBIN, _BB_SUID_NEVER))
+USE_AR(APPLET(ar, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_ARP(APPLET(arp, _BB_DIR_SBIN, _BB_SUID_NEVER))
+USE_ARPING(APPLET(arping, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_ASH(APPLET_NOUSAGE(ash, ash, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_AWK(APPLET(awk, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_BASENAME(APPLET(basename, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_BBCONFIG(APPLET(bbconfig, _BB_DIR_BIN, _BB_SUID_NEVER))
+//USE_BBSH(APPLET(bbsh, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_BUNZIP2(APPLET(bunzip2, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_BUNZIP2(APPLET_ODDNAME(bzcat, bunzip2, _BB_DIR_USR_BIN, _BB_SUID_NEVER, bzcat))
+USE_CAL(APPLET(cal, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_CAT(APPLET(cat, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_CATV(APPLET(catv, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_CHATTR(APPLET(chattr, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_CHCON(APPLET(chcon, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_CHGRP(APPLET(chgrp, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_CHMOD(APPLET(chmod, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_CHOWN(APPLET(chown, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_CHPST(APPLET(chpst, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_CHROOT(APPLET(chroot, _BB_DIR_USR_SBIN, _BB_SUID_NEVER))
+USE_CHRT(APPLET(chrt, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_CHVT(APPLET(chvt, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_CKSUM(APPLET(cksum, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_CLEAR(APPLET(clear, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_CMP(APPLET(cmp, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_COMM(APPLET(comm, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_CP(APPLET(cp, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_CPIO(APPLET(cpio, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_CROND(APPLET(crond, _BB_DIR_USR_SBIN, _BB_SUID_NEVER))
+USE_CRONTAB(APPLET(crontab, _BB_DIR_USR_BIN, _BB_SUID_ALWAYS))
+USE_CUT(APPLET(cut, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_DATE(APPLET(date, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_DC(APPLET(dc, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_DD(APPLET(dd, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_DEALLOCVT(APPLET(deallocvt, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_DELGROUP(APPLET_ODDNAME(delgroup, deluser, _BB_DIR_BIN, _BB_SUID_NEVER, delgroup))
+USE_DELUSER(APPLET(deluser, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_DEVFSD(APPLET(devfsd, _BB_DIR_SBIN, _BB_SUID_NEVER))
+USE_DF(APPLET(df, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_APP_DHCPRELAY(APPLET(dhcprelay, _BB_DIR_USR_SBIN, _BB_SUID_NEVER))
+USE_DIFF(APPLET(diff, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_DIRNAME(APPLET(dirname, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_DMESG(APPLET(dmesg, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_DNSD(APPLET(dnsd, _BB_DIR_USR_SBIN, _BB_SUID_ALWAYS))
+USE_DOS2UNIX(APPLET(dos2unix, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_DPKG(APPLET(dpkg, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_DPKG_DEB(APPLET_ODDNAME(dpkg-deb, dpkg_deb, _BB_DIR_USR_BIN, _BB_SUID_NEVER, dpkg_deb))
+USE_DU(APPLET(du, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_DUMPKMAP(APPLET(dumpkmap, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_APP_DUMPLEASES(APPLET(dumpleases, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+//USE_E2FSCK(APPLET(e2fsck, _BB_DIR_SBIN, _BB_SUID_NEVER))
+//USE_E2LABEL(APPLET_NOUSAGE(e2label, tune2fs, _BB_DIR_SBIN, _BB_SUID_NEVER))
+USE_ECHO(APPLET(echo, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_ED(APPLET(ed, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_FEATURE_GREP_EGREP_ALIAS(APPLET_NOUSAGE(egrep, grep, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_EJECT(APPLET(eject, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_ENV(APPLET(env, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_ENVDIR(APPLET_ODDNAME(envdir, chpst, _BB_DIR_USR_BIN, _BB_SUID_NEVER, envdir))
+USE_ENVUIDGID(APPLET_ODDNAME(envuidgid, chpst, _BB_DIR_USR_BIN, _BB_SUID_NEVER, envuidgid))
+USE_ETHER_WAKE(APPLET_ODDNAME(ether-wake, ether_wake, _BB_DIR_USR_BIN, _BB_SUID_NEVER, ether_wake))
+USE_EXPR(APPLET(expr, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_FAKEIDENTD(APPLET(fakeidentd, _BB_DIR_USR_SBIN, _BB_SUID_NEVER))
+USE_FALSE(APPLET(false, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_FBSET(APPLET(fbset, _BB_DIR_USR_SBIN, _BB_SUID_NEVER))
+USE_FDFLUSH(APPLET_ODDNAME(fdflush, freeramdisk, _BB_DIR_BIN, _BB_SUID_NEVER, fdflush))
+USE_FDFORMAT(APPLET(fdformat, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_FDISK(APPLET(fdisk, _BB_DIR_SBIN, _BB_SUID_NEVER))
+USE_FEATURE_GREP_FGREP_ALIAS(APPLET_NOUSAGE(fgrep, grep, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_FIND(APPLET(find, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+//USE_FINDFS(APPLET_NOUSAGE(findfs, tune2fs, _BB_DIR_SBIN, _BB_SUID_NEVER))
+USE_FOLD(APPLET(fold, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_FREE(APPLET(free, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_FREERAMDISK(APPLET(freeramdisk, _BB_DIR_SBIN, _BB_SUID_NEVER))
+USE_FSCK(APPLET(fsck, _BB_DIR_SBIN, _BB_SUID_NEVER))
+//USE_E2FSCK(APPLET_NOUSAGE(fsck.ext2, e2fsck, _BB_DIR_SBIN, _BB_SUID_NEVER))
+//USE_E2FSCK(APPLET_NOUSAGE(fsck.ext3, e2fsck, _BB_DIR_SBIN, _BB_SUID_NEVER))
+USE_FSCK_MINIX(APPLET_ODDNAME(fsck.minix, fsck_minix, _BB_DIR_SBIN, _BB_SUID_NEVER, fsck_minix))
+USE_FTPGET(APPLET_ODDNAME(ftpget, ftpgetput, _BB_DIR_USR_BIN, _BB_SUID_NEVER,ftpget))
+USE_FTPPUT(APPLET_ODDNAME(ftpput, ftpgetput, _BB_DIR_USR_BIN, _BB_SUID_NEVER,ftpput))
+USE_FUSER(APPLET(fuser, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_GETENFORCE(APPLET(getenforce, _BB_DIR_USR_SBIN, _BB_SUID_NEVER))
+USE_GETOPT(APPLET(getopt, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_GETSEBOOL(APPLET(getsebool, _BB_DIR_USR_SBIN, _BB_SUID_NEVER))
+USE_GETTY(APPLET(getty, _BB_DIR_SBIN, _BB_SUID_NEVER))
+USE_GREP(APPLET(grep, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_GUNZIP(APPLET(gunzip, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_GZIP(APPLET(gzip, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_HALT(APPLET(halt, _BB_DIR_SBIN, _BB_SUID_NEVER))
+USE_HDPARM(APPLET(hdparm, _BB_DIR_SBIN, _BB_SUID_NEVER))
+USE_HEAD(APPLET(head, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_HEXDUMP(APPLET(hexdump, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_HOSTID(APPLET(hostid, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_HOSTNAME(APPLET(hostname, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_HTTPD(APPLET(httpd, _BB_DIR_USR_SBIN, _BB_SUID_NEVER))
+USE_HUSH(APPLET_NOUSAGE(hush, hush, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_HWCLOCK(APPLET(hwclock, _BB_DIR_SBIN, _BB_SUID_NEVER))
+USE_ID(APPLET(id, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_IFCONFIG(APPLET(ifconfig, _BB_DIR_SBIN, _BB_SUID_NEVER))
+USE_IFUPDOWN(APPLET_ODDNAME(ifdown, ifupdown, _BB_DIR_SBIN, _BB_SUID_NEVER, ifdown))
+USE_IFUPDOWN(APPLET_ODDNAME(ifup, ifupdown, _BB_DIR_SBIN, _BB_SUID_NEVER, ifup))
+USE_INETD(APPLET(inetd, _BB_DIR_USR_SBIN, _BB_SUID_NEVER))
+USE_INIT(APPLET(init, _BB_DIR_SBIN, _BB_SUID_NEVER))
+USE_INSMOD(APPLET(insmod, _BB_DIR_SBIN, _BB_SUID_NEVER))
+USE_INSTALL(APPLET(install, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_IP(APPLET(ip, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_IPADDR(APPLET(ipaddr, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_IPCALC(APPLET(ipcalc, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_IPCRM(APPLET(ipcrm, _BB_DIR_USR_BIN, _BB_SUID_ALWAYS))
+USE_IPCS(APPLET(ipcs, _BB_DIR_USR_BIN, _BB_SUID_ALWAYS))
+USE_IPLINK(APPLET(iplink, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_IPROUTE(APPLET(iproute, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_IPRULE(APPLET(iprule, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_IPTUNNEL(APPLET(iptunnel, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_KILL(APPLET(kill, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_KILLALL(APPLET_ODDNAME(killall, kill, _BB_DIR_USR_BIN, _BB_SUID_NEVER, killall))
+USE_KILLALL5(APPLET_ODDNAME(killall5, kill, _BB_DIR_USR_BIN, _BB_SUID_NEVER, killall5))
+USE_KLOGD(APPLET(klogd, _BB_DIR_SBIN, _BB_SUID_NEVER))
+USE_LASH(APPLET(lash, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_LAST(APPLET(last, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_LENGTH(APPLET(length, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_LESS(APPLET(less, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_SETARCH(APPLET_NOUSAGE(linux32, setarch, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_SETARCH(APPLET_NOUSAGE(linux64, setarch, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_FEATURE_INITRD(APPLET_NOUSAGE(linuxrc, init, _BB_DIR_ROOT, _BB_SUID_NEVER))
+USE_LN(APPLET(ln, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_LOADFONT(APPLET(loadfont, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_LOADKMAP(APPLET(loadkmap, _BB_DIR_SBIN, _BB_SUID_NEVER))
+USE_LOGGER(APPLET(logger, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_LOGIN(APPLET(login, _BB_DIR_BIN, _BB_SUID_ALWAYS))
+USE_LOGNAME(APPLET(logname, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_LOGREAD(APPLET(logread, _BB_DIR_SBIN, _BB_SUID_NEVER))
+USE_LOSETUP(APPLET(losetup, _BB_DIR_SBIN, _BB_SUID_NEVER))
+USE_LS(APPLET(ls, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_LSATTR(APPLET(lsattr, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_LSMOD(APPLET(lsmod, _BB_DIR_SBIN, _BB_SUID_NEVER))
+USE_UNLZMA(APPLET_ODDNAME(lzmacat, unlzma, _BB_DIR_USR_BIN, _BB_SUID_NEVER, lzmacat))
+USE_MATCHPATHCON(APPLET(matchpathcon, _BB_DIR_USR_SBIN, _BB_SUID_NEVER))
+USE_MAKEDEVS(APPLET(makedevs, _BB_DIR_SBIN, _BB_SUID_NEVER))
+USE_MD5SUM(APPLET_ODDNAME(md5sum, md5_sha1_sum, _BB_DIR_USR_BIN, _BB_SUID_NEVER, md5sum))
+USE_MDEV(APPLET(mdev, _BB_DIR_SBIN, _BB_SUID_NEVER))
+USE_MESG(APPLET(mesg, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_MKDIR(APPLET(mkdir, _BB_DIR_BIN, _BB_SUID_NEVER))
+//USE_MKE2FS(APPLET(mke2fs, _BB_DIR_SBIN, _BB_SUID_NEVER))
+USE_MKFIFO(APPLET(mkfifo, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+//USE_MKE2FS(APPLET_NOUSAGE(mkfs.ext2, mke2fs, _BB_DIR_SBIN, _BB_SUID_NEVER))
+//USE_MKE2FS(APPLET_NOUSAGE(mkfs.ext3, mke2fs, _BB_DIR_SBIN, _BB_SUID_NEVER))
+USE_MKFS_MINIX(APPLET_ODDNAME(mkfs.minix, mkfs_minix, _BB_DIR_SBIN, _BB_SUID_NEVER, mkfs_minix))
+USE_MKNOD(APPLET(mknod, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_MKSWAP(APPLET(mkswap, _BB_DIR_SBIN, _BB_SUID_NEVER))
+USE_MKTEMP(APPLET(mktemp, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_MODPROBE(APPLET(modprobe, _BB_DIR_SBIN, _BB_SUID_NEVER))
+USE_MORE(APPLET(more, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_MOUNT(APPLET(mount, _BB_DIR_BIN, USE_DESKTOP(_BB_SUID_MAYBE) SKIP_DESKTOP(_BB_SUID_NEVER)))
+USE_MOUNTPOINT(APPLET(mountpoint, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_MSH(APPLET_NOUSAGE(msh, msh, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_MT(APPLET(mt, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_MV(APPLET(mv, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_NAMEIF(APPLET(nameif, _BB_DIR_SBIN, _BB_SUID_NEVER))
+USE_NC(APPLET(nc, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_NETSTAT(APPLET(netstat, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_NICE(APPLET(nice, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_NMETER(APPLET(nmeter, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_NOHUP(APPLET(nohup, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_NSLOOKUP(APPLET(nslookup, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_OD(APPLET(od, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_OPENVT(APPLET(openvt, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_PASSWD(APPLET(passwd, _BB_DIR_USR_BIN, _BB_SUID_ALWAYS))
+USE_PATCH(APPLET(patch, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_PIDOF(APPLET(pidof, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_PING(APPLET(ping, _BB_DIR_BIN, _BB_SUID_MAYBE))
+USE_PING6(APPLET(ping6, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_PIPE_PROGRESS(APPLET_NOUSAGE(pipe_progress, pipe_progress, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_PIVOT_ROOT(APPLET(pivot_root, _BB_DIR_SBIN, _BB_SUID_NEVER))
+USE_HALT(APPLET_ODDNAME(poweroff, halt, _BB_DIR_SBIN, _BB_SUID_NEVER, poweroff))
+USE_PRINTENV(APPLET(printenv, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_PRINTF(APPLET(printf, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_PS(APPLET(ps, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_PWD(APPLET(pwd, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_RAIDAUTORUN(APPLET(raidautorun, _BB_DIR_SBIN, _BB_SUID_NEVER))
+USE_RDATE(APPLET(rdate, _BB_DIR_USR_SBIN, _BB_SUID_NEVER))
+USE_READAHEAD(APPLET(readahead, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_READLINK(APPLET(readlink, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_READPROFILE(APPLET(readprofile, _BB_DIR_USR_SBIN, _BB_SUID_NEVER))
+USE_REALPATH(APPLET(realpath, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_HALT(APPLET_ODDNAME(reboot, halt, _BB_DIR_SBIN, _BB_SUID_NEVER, reboot))
+USE_RENICE(APPLET(renice, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_RESET(APPLET(reset, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_RESIZE(APPLET(resize, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_RM(APPLET(rm, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_RMDIR(APPLET(rmdir, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_RMMOD(APPLET(rmmod, _BB_DIR_SBIN, _BB_SUID_NEVER))
+USE_ROUTE(APPLET(route, _BB_DIR_SBIN, _BB_SUID_NEVER))
+USE_RPM(APPLET(rpm, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_RPM2CPIO(APPLET(rpm2cpio, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_RUN_PARTS(APPLET_ODDNAME(run-parts, run_parts, _BB_DIR_BIN, _BB_SUID_NEVER, run_parts))
+USE_RUNCON(APPLET(runcon, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_RUNLEVEL(APPLET(runlevel, _BB_DIR_SBIN, _BB_SUID_NEVER))
+USE_RUNSV(APPLET(runsv, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_RUNSVDIR(APPLET(runsvdir, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_RX(APPLET(rx, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_SELINUXENABLED(APPLET(selinuxenabled, _BB_DIR_USR_SBIN, _BB_SUID_NEVER))
+USE_SED(APPLET(sed, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_SEQ(APPLET(seq, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_SETARCH(APPLET(setarch, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_SETCONSOLE(APPLET(setconsole, _BB_DIR_SBIN, _BB_SUID_NEVER))
+USE_SETENFORCE(APPLET(setenforce, _BB_DIR_USR_SBIN, _BB_SUID_NEVER))
+USE_SETKEYCODES(APPLET(setkeycodes, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_SETLOGCONS(APPLET(setlogcons, _BB_DIR_USR_SBIN, _BB_SUID_NEVER))
+USE_SETSID(APPLET(setsid, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_SETUIDGID(APPLET_ODDNAME(setuidgid, chpst, _BB_DIR_USR_BIN, _BB_SUID_NEVER, setuidgid))
+USE_FEATURE_SH_IS_ASH(APPLET_NOUSAGE(sh, ash, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_FEATURE_SH_IS_HUSH(APPLET_NOUSAGE(sh, hush, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_FEATURE_SH_IS_LASH(APPLET_NOUSAGE(sh, lash, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_FEATURE_SH_IS_MSH(APPLET_NOUSAGE(sh, msh, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_SHA1SUM(APPLET_ODDNAME(sha1sum, md5_sha1_sum, _BB_DIR_USR_BIN, _BB_SUID_NEVER, sha1sum))
+USE_SLEEP(APPLET(sleep, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_SOFTLIMIT(APPLET_ODDNAME(softlimit, chpst, _BB_DIR_USR_BIN, _BB_SUID_NEVER, softlimit))
+USE_SORT(APPLET(sort, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_START_STOP_DAEMON(APPLET_ODDNAME(start-stop-daemon, start_stop_daemon, _BB_DIR_SBIN, _BB_SUID_NEVER, start_stop_daemon))
+USE_STAT(APPLET(stat, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_STRINGS(APPLET(strings, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_STTY(APPLET(stty, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_SU(APPLET(su, _BB_DIR_BIN, _BB_SUID_ALWAYS))
+USE_SULOGIN(APPLET(sulogin, _BB_DIR_SBIN, _BB_SUID_NEVER))
+USE_SUM(APPLET(sum, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_SV(APPLET(sv, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_SVLOGD(APPLET(svlogd, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_SWAPONOFF(APPLET_ODDNAME(swapoff, swap_on_off, _BB_DIR_SBIN, _BB_SUID_NEVER,swapoff))
+USE_SWAPONOFF(APPLET_ODDNAME(swapon, swap_on_off, _BB_DIR_SBIN, _BB_SUID_NEVER, swapon))
+USE_SWITCH_ROOT(APPLET(switch_root, _BB_DIR_SBIN, _BB_SUID_NEVER))
+USE_SYNC(APPLET(sync, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_BB_SYSCTL(APPLET(sysctl, _BB_DIR_SBIN, _BB_SUID_NEVER))
+USE_SYSLOGD(APPLET(syslogd, _BB_DIR_SBIN, _BB_SUID_NEVER))
+USE_TAIL(APPLET(tail, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_TAR(APPLET(tar, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_TASKSET(APPLET(taskset, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_TEE(APPLET(tee, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_TELNET(APPLET(telnet, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_TELNETD(APPLET(telnetd, _BB_DIR_USR_SBIN, _BB_SUID_NEVER))
+USE_TEST(APPLET(test, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+#if ENABLE_FEATURE_TFTP_GET || ENABLE_FEATURE_TFTP_PUT
+USE_TFTP(APPLET(tftp, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+#endif
+USE_TIME(APPLET(time, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_TOP(APPLET(top, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_TOUCH(APPLET(touch, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_TR(APPLET(tr, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_TRACEROUTE(APPLET(traceroute, _BB_DIR_USR_BIN, _BB_SUID_MAYBE))
+USE_TRUE(APPLET(true, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_TTY(APPLET(tty, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+//USE_TUNE2FS(APPLET(tune2fs, _BB_DIR_SBIN, _BB_SUID_NEVER))
+USE_APP_UDHCPC(APPLET(udhcpc, _BB_DIR_SBIN, _BB_SUID_NEVER))
+USE_APP_UDHCPD(APPLET(udhcpd, _BB_DIR_USR_SBIN, _BB_SUID_NEVER))
+USE_UMOUNT(APPLET(umount, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_UNAME(APPLET(uname, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_UNCOMPRESS(APPLET(uncompress, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_UNIQ(APPLET(uniq, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_UNIX2DOS(APPLET_ODDNAME(unix2dos, dos2unix, _BB_DIR_USR_BIN, _BB_SUID_NEVER, unix2dos))
+USE_UNLZMA(APPLET(unlzma, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_UNZIP(APPLET(unzip, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_UPTIME(APPLET(uptime, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_USLEEP(APPLET(usleep, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_UUDECODE(APPLET(uudecode, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_UUENCODE(APPLET(uuencode, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_VCONFIG(APPLET(vconfig, _BB_DIR_SBIN, _BB_SUID_NEVER))
+USE_VI(APPLET(vi, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_VLOCK(APPLET(vlock, _BB_DIR_USR_BIN, _BB_SUID_ALWAYS))
+USE_WATCH(APPLET(watch, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_WATCHDOG(APPLET(watchdog, _BB_DIR_SBIN, _BB_SUID_NEVER))
+USE_WC(APPLET(wc, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_WGET(APPLET(wget, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_WHICH(APPLET(which, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_WHO(APPLET(who, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_WHOAMI(APPLET(whoami, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_XARGS(APPLET(xargs, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_YES(APPLET(yes, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_GUNZIP(APPLET_ODDNAME(zcat, gunzip, _BB_DIR_BIN, _BB_SUID_NEVER, zcat))
+USE_ZCIP(APPLET(zcip, _BB_DIR_SBIN, _BB_SUID_NEVER))
+
+#if !defined(PROTOTYPES) && !defined(MAKE_USAGE)
+ { 0,NULL,0,0 }
+};
+
+#endif
diff --git a/i/pc104/initrd/conf/busybox/include/busybox.h b/i/pc104/initrd/conf/busybox/include/busybox.h
new file mode 100644
index 0000000..76ff7b7
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/include/busybox.h
@@ -0,0 +1,39 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Busybox main internal header file
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ */
+#ifndef _BB_INTERNAL_H_
+#define _BB_INTERNAL_H_ 1
+
+#include "libbb.h"
+
+/* order matters: used as index into "install_dir[]" in busybox.c */
+enum Location {
+ _BB_DIR_ROOT = 0,
+ _BB_DIR_BIN,
+ _BB_DIR_SBIN,
+ _BB_DIR_USR_BIN,
+ _BB_DIR_USR_SBIN
+};
+
+enum SUIDRoot {
+ _BB_SUID_NEVER = 0,
+ _BB_SUID_MAYBE,
+ _BB_SUID_ALWAYS
+};
+
+struct BB_applet {
+ const char *name;
+ int (*main) (int argc, char **argv);
+ __extension__ enum Location location:4;
+ __extension__ enum SUIDRoot need_suid:4;
+};
+
+/* Defined in busybox.c and applet.c */
+extern int busybox_main(int argc, char **argv);
+extern const struct BB_applet applets[];
+extern const unsigned short NUM_APPLETS;
+
+#endif /* _BB_INTERNAL_H_ */
diff --git a/i/pc104/initrd/conf/busybox/include/dump.h b/i/pc104/initrd/conf/busybox/include/dump.h
new file mode 100644
index 0000000..7e17154
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/include/dump.h
@@ -0,0 +1,50 @@
+/* vi: set sw=4 ts=4: */
+#define F_IGNORE 0x01 /* %_A */
+#define F_SETREP 0x02 /* rep count set, not default */
+#define F_ADDRESS 0x001 /* print offset */
+#define F_BPAD 0x002 /* blank pad */
+#define F_C 0x004 /* %_c */
+#define F_CHAR 0x008 /* %c */
+#define F_DBL 0x010 /* %[EefGf] */
+#define F_INT 0x020 /* %[di] */
+#define F_P 0x040 /* %_p */
+#define F_STR 0x080 /* %s */
+#define F_U 0x100 /* %_u */
+#define F_UINT 0x200 /* %[ouXx] */
+#define F_TEXT 0x400 /* no conversions */
+
+enum _vflag { ALL, DUP, FIRST, WAIT }; /* -v values */
+
+typedef struct _pr {
+ struct _pr *nextpr; /* next print unit */
+ unsigned int flags; /* flag values */
+ int bcnt; /* byte count */
+ char *cchar; /* conversion character */
+ char *fmt; /* printf format */
+ char *nospace; /* no whitespace version */
+} PR;
+
+typedef struct _fu {
+ struct _fu *nextfu; /* next format unit */
+ struct _pr *nextpr; /* next print unit */
+ unsigned int flags; /* flag values */
+ int reps; /* repetition count */
+ int bcnt; /* byte count */
+ char *fmt; /* format string */
+} FU;
+
+typedef struct _fs { /* format strings */
+ struct _fs *nextfs; /* linked list of format strings */
+ struct _fu *nextfu; /* linked list of format units */
+ int bcnt;
+} FS;
+
+extern void bb_dump_add(const char *fmt);
+extern int bb_dump_dump(char **argv);
+extern int bb_dump_size(FS * fs);
+
+extern FS *bb_dump_fshead; /* head of format strings */
+extern int bb_dump_blocksize; /* data block size */
+extern int bb_dump_length; /* max bytes to read */
+extern enum _vflag bb_dump_vflag;
+extern off_t bb_dump_skip; /* bytes to skip */
diff --git a/i/pc104/initrd/conf/busybox/include/grp_.h b/i/pc104/initrd/conf/busybox/include/grp_.h
new file mode 100644
index 0000000..061b86e
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/include/grp_.h
@@ -0,0 +1,133 @@
+/* vi: set sw=4 ts=4: */
+/* Copyright (C) 1991,92,95,96,97,98,99,2000,01 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+/*
+ * POSIX Standard: 9.2.1 Group Database Access <grp.h>
+ */
+
+#if !ENABLE_USE_BB_PWD_GRP
+
+#include <grp.h>
+
+#else
+
+#ifndef _GRP_H
+#define _GRP_H 1
+
+/* The group structure. */
+struct group {
+ char *gr_name; /* Group name. */
+ char *gr_passwd; /* Password. */
+ gid_t gr_gid; /* Group ID. */
+ char **gr_mem; /* Member list. */
+};
+
+/* We don't reimplement this, just supplying prototype */
+/* The function itself is in libc */
+/* Set the group set for the current user to GROUPS (N of them). */
+extern int setgroups(size_t __n, __const gid_t *__groups);
+
+
+#define setgrent bb_internal_setgrent
+#define endgrent bb_internal_endgrent
+#define getgrent bb_internal_getgrent
+#define fgetgrent bb_internal_fgetgrent
+#define putgrent bb_internal_putgrent
+#define getgrgid bb_internal_getgrgid
+#define getgrnam bb_internal_getgrnam
+#define getgrent_r bb_internal_getgrent_r
+#define getgrgid_r bb_internal_getgrgid_r
+#define getgrnam_r bb_internal_getgrnam_r
+#define fgetgrent_r bb_internal_fgetgrent_r
+#define getgrouplist bb_internal_getgrouplist
+#define initgroups bb_internal_initgroups
+
+
+/* All function names below should be remapped by #defines above
+ * in order to not collide with libc names.
+ * In theory it isn't necessary, but I saw weird interactions at link time.
+ * Let's play safe */
+
+
+/* Rewind the group-file stream. */
+extern void setgrent(void);
+
+/* Close the group-file stream. */
+extern void endgrent(void);
+
+/* Read an entry from the group-file stream, opening it if necessary. */
+extern struct group *getgrent(void);
+
+/* Read a group entry from STREAM. */
+extern struct group *fgetgrent(FILE *__stream);
+
+/* Write the given entry onto the given stream. */
+extern int putgrent(__const struct group *__restrict __p,
+ FILE *__restrict __f);
+
+/* Search for an entry with a matching group ID. */
+extern struct group *getgrgid(gid_t __gid);
+
+/* Search for an entry with a matching group name. */
+extern struct group *getgrnam(__const char *__name);
+
+/* Reentrant versions of some of the functions above.
+
+ PLEASE NOTE: the `getgrent_r' function is not (yet) standardized.
+ The interface may change in later versions of this library. But
+ the interface is designed following the principals used for the
+ other reentrant functions so the chances are good this is what the
+ POSIX people would choose. */
+
+extern int getgrent_r(struct group *__restrict __resultbuf,
+ char *__restrict __buffer, size_t __buflen,
+ struct group **__restrict __result);
+
+/* Search for an entry with a matching group ID. */
+extern int getgrgid_r(gid_t __gid, struct group *__restrict __resultbuf,
+ char *__restrict __buffer, size_t __buflen,
+ struct group **__restrict __result);
+
+/* Search for an entry with a matching group name. */
+extern int getgrnam_r(__const char *__restrict __name,
+ struct group *__restrict __resultbuf,
+ char *__restrict __buffer, size_t __buflen,
+ struct group **__restrict __result);
+
+/* Read a group entry from STREAM. This function is not standardized
+ an probably never will. */
+extern int fgetgrent_r(FILE *__restrict __stream,
+ struct group *__restrict __resultbuf,
+ char *__restrict __buffer, size_t __buflen,
+ struct group **__restrict __result);
+
+/* Store at most *NGROUPS members of the group set for USER into
+ *GROUPS. Also include GROUP. The actual number of groups found is
+ returned in *NGROUPS. Return -1 if the if *NGROUPS is too small. */
+extern int getgrouplist(__const char *__user, gid_t __group,
+ gid_t *__groups, int *__ngroups);
+
+/* Initialize the group set for the current user
+ by reading the group database and using all groups
+ of which USER is a member. Also include GROUP. */
+extern int initgroups(__const char *__user, gid_t __group);
+
+
+#endif /* grp.h */
+#endif
diff --git a/i/pc104/initrd/conf/busybox/include/inet_common.h b/i/pc104/initrd/conf/busybox/include/inet_common.h
new file mode 100644
index 0000000..bda6211
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/include/inet_common.h
@@ -0,0 +1,25 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * stolen from net-tools-1.59 and stripped down for busybox by
+ * Erik Andersen <andersen@codepoet.org>
+ *
+ * Heavily modified by Manuel Novoa III Mar 12, 2001
+ *
+ */
+
+#include "platform.h"
+
+/* hostfirst!=0 If we expect this to be a hostname,
+ try hostname database first
+ */
+extern int INET_resolve(const char *name, struct sockaddr_in *s_in, int hostfirst);
+
+/* numeric: & 0x8000: "default" instead of "*",
+ * & 0x4000: host instead of net,
+ * & 0x0fff: don't resolve
+ */
+extern int INET_rresolve(char *name, size_t len, struct sockaddr_in *s_in,
+ int numeric, unsigned int netmask);
+
+extern int INET6_resolve(const char *name, struct sockaddr_in6 *sin6);
+extern int INET6_rresolve(char *name, size_t len, struct sockaddr_in6 *sin6, int numeric);
diff --git a/i/pc104/initrd/conf/busybox/include/libbb.h b/i/pc104/initrd/conf/busybox/include/libbb.h
new file mode 100644
index 0000000..659bfca
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/include/libbb.h
@@ -0,0 +1,902 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Busybox main internal header file
+ *
+ * Based in part on code from sash, Copyright (c) 1999 by David I. Bell
+ * Permission has been granted to redistribute this code under the GPL.
+ *
+ * Licensed under the GPL version 2, see the file LICENSE in this tarball.
+ */
+#ifndef __LIBBUSYBOX_H__
+#define __LIBBUSYBOX_H__ 1
+
+#include "platform.h"
+
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <netdb.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/statfs.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <termios.h>
+#include <time.h>
+#include <unistd.h>
+#include <utime.h>
+
+#if ENABLE_SELINUX
+#include <selinux/selinux.h>
+#include <selinux/context.h>
+#endif
+
+#if ENABLE_LOCALE_SUPPORT
+#include <locale.h>
+#else
+#define setlocale(x,y) ((void)0)
+#endif
+
+#include "pwd_.h"
+#include "grp_.h"
+/* ifdef it out, because it may include <shadow.h> */
+/* and we may not even _have_ <shadow.h>! */
+#if ENABLE_FEATURE_SHADOWPASSWDS
+#include "shadow_.h"
+#endif
+
+/* Try to pull in PATH_MAX */
+#include <limits.h>
+#include <sys/param.h>
+#ifndef PATH_MAX
+#define PATH_MAX 256
+#endif
+
+/* Tested to work correctly (IIRC :]) */
+#define MAXINT(T) (T)( \
+ ((T)-1) > 0 \
+ ? (T)-1 \
+ : (T)~((T)1 << (sizeof(T)*8-1)) \
+ )
+
+#define MININT(T) (T)( \
+ ((T)-1) > 0 \
+ ? (T)0 \
+ : ((T)1 << (sizeof(T)*8-1)) \
+ )
+
+/* Large file support */
+/* Note that CONFIG_LFS forces bbox to be built with all common ops
+ * (stat, lseek etc) mapped to "largefile" variants by libc.
+ * Practically it means that open() automatically has O_LARGEFILE added
+ * and all filesize/file_offset parameters and struct members are "large"
+ * (in today's world - signed 64bit). For full support of large files,
+ * we need a few helper #defines (below) and careful use of off_t
+ * instead of int/ssize_t. No lseek64(), O_LARGEFILE etc necessary */
+#if ENABLE_LFS
+/* CONFIG_LFS is on */
+# if ULONG_MAX > 0xffffffff
+/* "long" is long enough on this system */
+# define XATOOFF(a) xatoul_range(a, 0, LONG_MAX)
+/* usage: sz = BB_STRTOOFF(s, NULL, 10); if (errno || sz < 0) die(); */
+# define BB_STRTOOFF bb_strtoul
+# define STRTOOFF strtoul
+/* usage: printf("size: %"OFF_FMT"d (%"OFF_FMT"x)\n", sz, sz); */
+# define OFF_FMT "l"
+# else
+/* "long" is too short, need "long long" */
+# define XATOOFF(a) xatoull_range(a, 0, LLONG_MAX)
+# define BB_STRTOOFF bb_strtoull
+# define STRTOOFF strtoull
+# define OFF_FMT "ll"
+# endif
+#else
+/* CONFIG_LFS is off */
+# if UINT_MAX == 0xffffffff
+/* While sizeof(off_t) == sizeof(int), off_t is typedef'ed to long anyway.
+ * gcc will throw warnings on printf("%d", off_t). Crap... */
+# define XATOOFF(a) xatoi_u(a)
+# define BB_STRTOOFF bb_strtou
+# define STRTOOFF strtol
+# define OFF_FMT "l"
+# else
+# define XATOOFF(a) xatoul_range(a, 0, LONG_MAX)
+# define BB_STRTOOFF bb_strtoul
+# define STRTOOFF strtol
+# define OFF_FMT "l"
+# endif
+#endif
+/* scary. better ideas? (but do *test* them first!) */
+#define OFF_T_MAX ((off_t)~((off_t)1 << (sizeof(off_t)*8-1)))
+
+/* This structure defines protocol families and their handlers. */
+struct aftype {
+ const char *name;
+ const char *title;
+ int af;
+ int alen;
+ char *(*print) (unsigned char *);
+ const char *(*sprint) (struct sockaddr *, int numeric);
+ int (*input) (/*int type,*/ const char *bufp, struct sockaddr *);
+ void (*herror) (char *text);
+ int (*rprint) (int options);
+ int (*rinput) (int typ, int ext, char **argv);
+
+ /* may modify src */
+ int (*getmask) (char *src, struct sockaddr * mask, char *name);
+};
+
+/* This structure defines hardware protocols and their handlers. */
+struct hwtype {
+ const char *name;
+ const char *title;
+ int type;
+ int alen;
+ char *(*print) (unsigned char *);
+ int (*input) (const char *, struct sockaddr *);
+ int (*activate) (int fd);
+ int suppress_null_addr;
+};
+
+/* Some useful definitions */
+#undef FALSE
+#define FALSE ((int) 0)
+#undef TRUE
+#define TRUE ((int) 1)
+#undef SKIP
+#define SKIP ((int) 2)
+
+/* for mtab.c */
+#define MTAB_GETMOUNTPT '1'
+#define MTAB_GETDEVICE '2'
+
+#define BUF_SIZE 8192
+#define EXPAND_ALLOC 1024
+
+/* Macros for min/max. */
+#ifndef MIN
+#define MIN(a,b) (((a)<(b))?(a):(b))
+#endif
+
+#ifndef MAX
+#define MAX(a,b) (((a)>(b))?(a):(b))
+#endif
+
+/* buffer allocation schemes */
+#if ENABLE_FEATURE_BUFFERS_GO_ON_STACK
+#define RESERVE_CONFIG_BUFFER(buffer,len) char buffer[len]
+#define RESERVE_CONFIG_UBUFFER(buffer,len) unsigned char buffer[len]
+#define RELEASE_CONFIG_BUFFER(buffer) ((void)0)
+#else
+#if ENABLE_FEATURE_BUFFERS_GO_IN_BSS
+#define RESERVE_CONFIG_BUFFER(buffer,len) static char buffer[len]
+#define RESERVE_CONFIG_UBUFFER(buffer,len) static unsigned char buffer[len]
+#define RELEASE_CONFIG_BUFFER(buffer) ((void)0)
+#else
+#define RESERVE_CONFIG_BUFFER(buffer,len) char *buffer=xmalloc(len)
+#define RESERVE_CONFIG_UBUFFER(buffer,len) unsigned char *buffer=xmalloc(len)
+#define RELEASE_CONFIG_BUFFER(buffer) free (buffer)
+#endif
+#endif
+
+
+#if defined(__GLIBC__) && __GLIBC__ < 2
+int vdprintf(int d, const char *format, va_list ap);
+#endif
+// This is declared here rather than #including <libgen.h> in order to avoid
+// confusing the two versions of basename. See the dirname/basename man page
+// for details.
+char *dirname(char *path);
+/* Include our own copy of struct sysinfo to avoid binary compatibility
+ * problems with Linux 2.4, which changed things. Grumble, grumble. */
+struct sysinfo {
+ long uptime; /* Seconds since boot */
+ unsigned long loads[3]; /* 1, 5, and 15 minute load averages */
+ unsigned long totalram; /* Total usable main memory size */
+ unsigned long freeram; /* Available memory size */
+ unsigned long sharedram; /* Amount of shared memory */
+ unsigned long bufferram; /* Memory used by buffers */
+ unsigned long totalswap; /* Total swap space size */
+ unsigned long freeswap; /* swap space still available */
+ unsigned short procs; /* Number of current processes */
+ unsigned short pad; /* Padding needed for m68k */
+ unsigned long totalhigh; /* Total high memory size */
+ unsigned long freehigh; /* Available high memory size */
+ unsigned int mem_unit; /* Memory unit size in bytes */
+ char _f[20-2*sizeof(long)-sizeof(int)]; /* Padding: libc5 uses this.. */
+};
+extern int sysinfo(struct sysinfo* info);
+
+
+extern void chomp(char *s);
+extern void trim(char *s);
+extern char *skip_whitespace(const char *);
+extern char *skip_non_whitespace(const char *);
+
+//TODO: supply a pointer to char[11] buffer (avoid statics)?
+extern const char *bb_mode_string(mode_t mode);
+extern int is_directory(const char *name, int followLinks, struct stat *statBuf);
+extern int remove_file(const char *path, int flags);
+extern int copy_file(const char *source, const char *dest, int flags);
+extern int recursive_action(const char *fileName, int recurse,
+ int followLinks, int depthFirst,
+ int (*fileAction) (const char *fileName, struct stat* statbuf, void* userData, int depth),
+ int (*dirAction) (const char *fileName, struct stat* statbuf, void* userData, int depth),
+ void* userData, int depth);
+extern int device_open(const char *device, int mode);
+extern int get_console_fd(void);
+extern char *find_block_device(const char *path);
+/* bb_copyfd_XX print read/write errors and return -1 if they occur */
+extern off_t bb_copyfd_eof(int fd1, int fd2);
+extern off_t bb_copyfd_size(int fd1, int fd2, off_t size);
+extern void bb_copyfd_exact_size(int fd1, int fd2, off_t size);
+/* "short" copy can be detected by return value < size */
+/* this helper yells "short read!" if param is not -1 */
+extern void complain_copyfd_and_die(off_t sz) ATTRIBUTE_NORETURN;
+extern char bb_process_escape_sequence(const char **ptr);
+/* TODO: sometimes modifies its parameter, which
+ * makes it rather inconvenient at times: */
+extern char *bb_get_last_path_component(char *path);
+extern int ndelay_on(int fd);
+extern int ndelay_off(int fd);
+
+
+extern DIR *xopendir(const char *path);
+extern DIR *warn_opendir(const char *path);
+
+char *xrealloc_getcwd_or_warn(char *cwd);
+char *xmalloc_readlink_or_warn(const char *path);
+char *xmalloc_realpath(const char *path);
+extern void xstat(const char *filename, struct stat *buf);
+extern pid_t spawn(char **argv);
+extern pid_t xspawn(char **argv);
+extern int wait4pid(int pid);
+extern void xsetgid(gid_t gid);
+extern void xsetuid(uid_t uid);
+extern void xdaemon(int nochdir, int noclose);
+/* More clever/thorough xdaemon */
+extern void bb_sanitize_stdio_maybe_daemonize(int daemonize);
+extern void bb_sanitize_stdio(void);
+/* NB: be careful: dont open syslog/network sockets before bb_daemonize */
+extern void bb_daemonize(void);
+extern void xchdir(const char *path);
+extern void xsetenv(const char *key, const char *value);
+extern int xopen(const char *pathname, int flags);
+extern int xopen3(const char *pathname, int flags, int mode);
+extern off_t xlseek(int fd, off_t offset, int whence);
+extern off_t fdlength(int fd);
+
+
+int xsocket(int domain, int type, int protocol);
+void xbind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen);
+void xlisten(int s, int backlog);
+void xconnect(int s, const struct sockaddr *s_addr, socklen_t addrlen);
+int setsockopt_reuseaddr(int fd);
+int setsockopt_broadcast(int fd);
+/* NB: returns port in host byte order */
+unsigned bb_lookup_port(const char *port, const char *protocol, unsigned default_port);
+typedef struct len_and_sockaddr {
+ socklen_t len;
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in sin;
+#if ENABLE_FEATURE_IPV6
+ struct sockaddr_in6 sin6;
+#endif
+ };
+} len_and_sockaddr;
+/* Create stream socket, and allocated suitable lsa
+ * (lsa of correct size and lsa->sa.sa_family (AF_INET/AF_INET6)) */
+int xsocket_stream(len_and_sockaddr **lsap);
+/* Create server TCP socket bound to bindaddr:port. bindaddr can be NULL,
+ * numeric IP ("N.N.N.N") or numeric IPv6 address,
+ * and can have ":PORT" suffix (for IPv6 use "[X:X:...:X]:PORT").
+ * If there is no suffix, port argument is used */
+int create_and_bind_stream_or_die(const char *bindaddr, int port);
+/* Create client TCP socket connected to peer:port. Peer cannot be NULL.
+ * Peer can be numeric IP ("N.N.N.N"), numeric IPv6 address or hostname,
+ * and can have ":PORT" suffix (for IPv6 use "[X:X:...:X]:PORT").
+ * If there is no suffix, port argument is used */
+int create_and_connect_stream_or_die(const char *peer, int port);
+/* Connect to peer identified by lsa */
+int xconnect_stream(const len_and_sockaddr *lsa);
+/* Return malloc'ed len_and_sockaddr with socket address of host:port
+ * Currently will return IPv4 or IPv6 sockaddrs only
+ * (depending on host), but in theory nothing prevents e.g.
+ * UNIX socket address being returned, IPX sockaddr etc...
+ * On error does bb_error_msg and returns NULL */
+len_and_sockaddr* host2sockaddr(const char *host, int port);
+/* Version which dies on error */
+len_and_sockaddr* xhost2sockaddr(const char *host, int port);
+len_and_sockaddr* xdotted2sockaddr(const char *host, int port);
+#if ENABLE_FEATURE_IPV6
+/* Same, useful if you want to force family (e.g. IPv6) */
+len_and_sockaddr* host_and_af2sockaddr(const char *host, int port, sa_family_t af);
+len_and_sockaddr* xhost_and_af2sockaddr(const char *host, int port, sa_family_t af);
+#else
+/* [we evaluate af: think about "host_and_af2sockaddr(..., af++)"] */
+#define host_and_af2sockaddr(host, port, af) ((void)(af), host2sockaddr((host), (port)))
+#define xhost_and_af2sockaddr(host, port, af) ((void)(af), xhost2sockaddr((host), (port)))
+#endif
+/* Assign sin[6]_port member if the socket is of corresponding type,
+ * otherwise no-op. Useful for ftp.
+ * NB: does NOT do htons() internally, just direct assignment. */
+void set_nport(len_and_sockaddr *lsa, unsigned port);
+/* Retrieve sin[6]_port or return -1 for non-INET[6] lsa's */
+int get_nport(const len_and_sockaddr *lsa);
+/* Reverse DNS. Returns NULL on failure. */
+char* xmalloc_sockaddr2host(const struct sockaddr *sa, socklen_t salen);
+/* This one doesn't append :PORTNUM */
+char* xmalloc_sockaddr2host_noport(const struct sockaddr *sa, socklen_t salen);
+/* This one also doesn't fall back to dotted IP (returns NULL) */
+char* xmalloc_sockaddr2hostonly_noport(const struct sockaddr *sa, socklen_t salen);
+/* inet_[ap]ton on steroids */
+char* xmalloc_sockaddr2dotted(const struct sockaddr *sa, socklen_t salen);
+char* xmalloc_sockaddr2dotted_noport(const struct sockaddr *sa, socklen_t salen);
+// "old" (ipv4 only) API
+// users: traceroute.c hostname.c
+struct hostent *xgethostbyname(const char *name);
+// Also inetd.c and inetd.c are using gethostbyname(),
+// + inet_common.c has additional IPv4-only stuff
+
+
+extern char *xstrdup(const char *s);
+extern char *xstrndup(const char *s, int n);
+extern char *safe_strncpy(char *dst, const char *src, size_t size);
+extern char *xasprintf(const char *format, ...) __attribute__ ((format (printf, 1, 2)));
+// gcc-4.1.1 still isn't good enough at optimizing it
+// (+200 bytes compared to macro)
+//static ATTRIBUTE_ALWAYS_INLINE
+//int LONE_DASH(const char *s) { return s[0] == '-' && !s[1]; }
+//static ATTRIBUTE_ALWAYS_INLINE
+//int NOT_LONE_DASH(const char *s) { return s[0] != '-' || s[1]; }
+#define LONE_DASH(s) ((s)[0] == '-' && !(s)[1])
+#define NOT_LONE_DASH(s) ((s)[0] != '-' || (s)[1])
+#define LONE_CHAR(s,c) ((s)[0] == (c) && !(s)[1])
+#define NOT_LONE_CHAR(s,c) ((s)[0] != (c) || (s)[1])
+
+/* dmalloc will redefine these to it's own implementation. It is safe
+ * to have the prototypes here unconditionally. */
+extern void *xmalloc(size_t size);
+extern void *xrealloc(void *old, size_t size);
+extern void *xzalloc(size_t size);
+
+extern ssize_t safe_read(int fd, void *buf, size_t count);
+extern ssize_t full_read(int fd, void *buf, size_t count);
+extern void xread(int fd, void *buf, size_t count);
+extern unsigned char xread_char(int fd);
+extern char *reads(int fd, char *buf, size_t count);
+extern ssize_t read_close(int fd, void *buf, size_t count);
+extern ssize_t open_read_close(const char *filename, void *buf, size_t count);
+extern void *xmalloc_open_read_close(const char *filename, size_t *sizep);
+
+extern ssize_t safe_write(int fd, const void *buf, size_t count);
+extern ssize_t full_write(int fd, const void *buf, size_t count);
+extern void xwrite(int fd, const void *buf, size_t count);
+
+/* Reads and prints to stdout till eof, then closes FILE. Exits on error: */
+extern void xprint_and_close_file(FILE *file);
+extern char *xmalloc_fgets(FILE *file);
+/* Read up to (and including) TERMINATING_STRING: */
+extern char *xmalloc_fgets_str(FILE *file, const char *terminating_string);
+/* Chops off '\n' from the end, unlike fgets: */
+extern char *xmalloc_getline(FILE *file);
+extern char *bb_get_chunk_from_file(FILE *file, int *end);
+extern void die_if_ferror(FILE *file, const char *msg);
+extern void die_if_ferror_stdout(void);
+extern void xfflush_stdout(void);
+extern void fflush_stdout_and_exit(int retval) ATTRIBUTE_NORETURN;
+extern int fclose_if_not_stdin(FILE *file);
+extern FILE *xfopen(const char *filename, const char *mode);
+/* Prints warning to stderr and returns NULL on failure: */
+extern FILE *fopen_or_warn(const char *filename, const char *mode);
+/* "Opens" stdin if filename is special, else just opens file: */
+extern FILE *fopen_or_warn_stdin(const char *filename);
+
+
+extern void utoa_to_buf(unsigned n, char *buf, unsigned buflen);
+extern char *utoa(unsigned n);
+extern void itoa_to_buf(int n, char *buf, unsigned buflen);
+extern char *itoa(int n);
+extern void smart_ulltoa5(unsigned long long ul, char buf[5]);
+/* Put a string of hex bytes (ala "1b"), return advanced pointer */
+extern char *bin2hex(char *buf, const char *cp, int count);
+
+struct suffix_mult {
+ const char *suffix;
+ unsigned mult;
+};
+#include "xatonum.h"
+/* Specialized: */
+/* Using xatoi() instead of naive atoi() is not always convenient -
+ * in many places people want *non-negative* values, but store them
+ * in signed int. Therefore we need this one:
+ * dies if input is not in [0, INT_MAX] range. Also will reject '-0' etc */
+int xatoi_u(const char *numstr);
+/* Useful for reading port numbers */
+uint16_t xatou16(const char *numstr);
+
+
+/* These parse entries in /etc/passwd and /etc/group. This is desirable
+ * for BusyBox since we want to avoid using the glibc NSS stuff, which
+ * increases target size and is often not needed on embedded systems. */
+long xuname2uid(const char *name);
+long xgroup2gid(const char *name);
+/* wrapper: allows string to contain numeric uid or gid */
+unsigned long get_ug_id(const char *s, long (*xname2id)(const char *));
+/* from chpst. Does not die, returns 0 on failure */
+struct bb_uidgid_t {
+ uid_t uid;
+ gid_t gid;
+};
+/* always sets uid and gid */
+int get_uidgid(struct bb_uidgid_t*, const char*, int numeric_ok);
+/* chown-like handling of "user[:[group]" */
+void parse_chown_usergroup_or_die(struct bb_uidgid_t *u, char *user_group);
+/* what is this? */
+/*extern char *bb_getug(char *buffer, char *idname, long id, int bufsize, char prefix);*/
+char *bb_getpwuid(char *name, long uid, int bufsize);
+char *bb_getgrgid(char *group, long gid, int bufsize);
+/* versions which cache results (useful for ps, ls etc) */
+const char* get_cached_username(uid_t uid);
+const char* get_cached_groupname(gid_t gid);
+void clear_username_cache(void);
+/* internally usernames are saved in fixed-sized char[] buffers */
+enum { USERNAME_MAX_SIZE = 16 - sizeof(int) };
+
+
+enum { BB_GETOPT_ERROR = 0x80000000 };
+extern const char *opt_complementary;
+#if ENABLE_GETOPT_LONG
+extern const struct option *applet_long_options;
+#endif
+extern uint32_t option_mask32;
+extern uint32_t getopt32(int argc, char **argv, const char *applet_opts, ...);
+
+
+typedef struct llist_s {
+ char *data;
+ struct llist_s *link;
+} llist_t;
+extern void llist_add_to(llist_t **old_head, void *data);
+extern void llist_add_to_end(llist_t **list_head, void *data);
+extern void *llist_pop(llist_t **elm);
+extern void llist_unlink(llist_t **head, llist_t *elm);
+extern void llist_free(llist_t *elm, void (*freeit)(void *data));
+extern llist_t* llist_rev(llist_t *list);
+
+enum {
+ LOGMODE_NONE = 0,
+ LOGMODE_STDIO = 1<<0,
+ LOGMODE_SYSLOG = 1<<1,
+ LOGMODE_BOTH = LOGMODE_SYSLOG + LOGMODE_STDIO,
+};
+extern const char *msg_eol;
+extern int logmode;
+extern int die_sleep;
+extern int xfunc_error_retval;
+extern void sleep_and_die(void) ATTRIBUTE_NORETURN;
+extern void bb_show_usage(void) ATTRIBUTE_NORETURN ATTRIBUTE_EXTERNALLY_VISIBLE;
+extern void bb_error_msg(const char *s, ...) __attribute__ ((format (printf, 1, 2)));
+extern void bb_error_msg_and_die(const char *s, ...) __attribute__ ((noreturn, format (printf, 1, 2)));
+extern void bb_perror_msg(const char *s, ...) __attribute__ ((format (printf, 1, 2)));
+extern void bb_perror_msg_and_die(const char *s, ...) __attribute__ ((noreturn, format (printf, 1, 2)));
+extern void bb_vherror_msg(const char *s, va_list p);
+extern void bb_herror_msg(const char *s, ...) __attribute__ ((format (printf, 1, 2)));
+extern void bb_herror_msg_and_die(const char *s, ...) __attribute__ ((noreturn, format (printf, 1, 2)));
+extern void bb_perror_nomsg_and_die(void) ATTRIBUTE_NORETURN;
+extern void bb_perror_nomsg(void);
+extern void bb_info_msg(const char *s, ...) __attribute__ ((format (printf, 1, 2)));
+/* These are used internally -- you shouldn't need to use them */
+extern void bb_verror_msg(const char *s, va_list p, const char *strerr);
+extern void bb_vperror_msg(const char *s, va_list p);
+extern void bb_vinfo_msg(const char *s, va_list p);
+
+
+/* applets which are useful from another applets */
+int bb_cat(char** argv);
+int bb_echo(char** argv);
+int bb_test(int argc, char** argv);
+#if ENABLE_ROUTE
+void bb_displayroutes(int noresolve, int netstatfmt);
+#endif
+int chown_main(int argc, char **argv);
+#if ENABLE_GUNZIP
+int gunzip_main(int argc, char **argv);
+#endif
+int bbunpack(char **argv,
+ char* (*make_new_name)(char *filename),
+ USE_DESKTOP(long long) int (*unpacker)(void)
+);
+
+
+/* Networking */
+int create_icmp_socket(void);
+int create_icmp6_socket(void);
+/* interface.c */
+extern int interface_opt_a;
+int display_interfaces(char *ifname);
+const struct aftype *get_aftype(const char *name);
+const struct hwtype *get_hwtype(const char *name);
+const struct hwtype *get_hwntype(int type);
+
+
+#ifndef BUILD_INDIVIDUAL
+extern struct BB_applet *find_applet_by_name(const char *name);
+extern void run_applet_by_name(const char *name, int argc, char **argv);
+#endif
+
+extern struct mntent *find_mount_point(const char *name, const char *table);
+extern void erase_mtab(const char * name);
+extern unsigned int tty_baud_to_value(speed_t speed);
+extern speed_t tty_value_to_baud(unsigned int value);
+extern void bb_warn_ignoring_args(int n);
+
+extern int get_linux_version_code(void);
+
+extern char *query_loop(const char *device);
+extern int del_loop(const char *device);
+extern int set_loop(char **device, const char *file, unsigned long long offset);
+
+
+//TODO: provide pointer to buf (avoid statics)?
+const char *make_human_readable_str(unsigned long long size,
+ unsigned long block_size, unsigned long display_unit);
+
+//TODO: pass buf pointer or return allocated buf (avoid statics)?
+char *bb_askpass(int timeout, const char * prompt);
+int bb_ask_confirmation(void);
+int klogctl(int type, char * b, int len);
+
+extern int bb_parse_mode(const char* s, mode_t* theMode);
+
+char *concat_path_file(const char *path, const char *filename);
+char *concat_subpath_file(const char *path, const char *filename);
+char *last_char_is(const char *s, int c);
+
+int execable_file(const char *name);
+char *find_execable(const char *filename);
+int exists_execable(const char *filename);
+
+#if ENABLE_FEATURE_EXEC_PREFER_APPLETS
+int bb_execvp(const char *file, char *const argv[]);
+#define BB_EXECVP(prog,cmd) bb_execvp(prog,cmd)
+#define BB_EXECLP(prog,cmd,...) \
+ execlp((find_applet_by_name(prog)) ? CONFIG_BUSYBOX_EXEC_PATH : prog, \
+ cmd, __VA_ARGS__)
+#else
+#define BB_EXECVP(prog,cmd) execvp(prog,cmd)
+#define BB_EXECLP(prog,cmd,...) execlp(prog,cmd, __VA_ARGS__)
+#endif
+
+USE_DESKTOP(long long) int uncompress(int fd_in, int fd_out);
+int inflate(int in, int out);
+
+
+int bb_make_directory(char *path, long mode, int flags);
+
+int get_signum(const char *name);
+const char *get_signame(int number);
+
+char *bb_simplify_path(const char *path);
+
+#define FAIL_DELAY 3
+extern void bb_do_delay(int seconds);
+extern void change_identity(const struct passwd *pw);
+extern const char *change_identity_e2str(const struct passwd *pw);
+extern void run_shell(const char *shell, int loginshell, const char *command, const char **additional_args);
+#if ENABLE_SELINUX
+extern void renew_current_security_context(void);
+extern void set_current_security_context(security_context_t sid);
+extern context_t set_security_context_component(security_context_t cur_context,
+ char *user, char *role, char *type, char *range);
+extern void setfscreatecon_or_die(security_context_t scontext);
+#endif
+extern void selinux_or_die(void);
+extern int restricted_shell(const char *shell);
+extern void setup_environment(const char *shell, int loginshell, int changeenv, const struct passwd *pw);
+extern int correct_password(const struct passwd *pw);
+extern char *pw_encrypt(const char *clear, const char *salt);
+extern int obscure(const char *old, const char *newval, const struct passwd *pwdp);
+extern int index_in_str_array(const char * const string_array[], const char *key);
+extern int index_in_substr_array(const char * const string_array[], const char *key);
+extern void print_login_issue(const char *issue_file, const char *tty);
+extern void print_login_prompt(void);
+#ifdef BB_NOMMU
+extern void vfork_daemon(int nochdir, int noclose);
+extern void vfork_daemon_rexec(int nochdir, int noclose,
+ int argc, char **argv, char *foreground_opt);
+#endif
+extern int get_terminal_width_height(const int fd, int *width, int *height);
+
+char *is_in_ino_dev_hashtable(const struct stat *statbuf);
+void add_to_ino_dev_hashtable(const struct stat *statbuf, const char *name);
+void reset_ino_dev_hashtable(void);
+#ifdef __GLIBC__
+/* At least glibc has horrendously large inline for this, so wrap it */
+extern unsigned long long bb_makedev(unsigned int major, unsigned int minor);
+#undef makedev
+#define makedev(a,b) bb_makedev(a,b)
+#endif
+
+
+#if ENABLE_FEATURE_EDITING
+/* It's NOT just ENABLEd or disabled. It's a number: */
+#ifdef CONFIG_FEATURE_EDITING_HISTORY
+#define MAX_HISTORY (CONFIG_FEATURE_EDITING_HISTORY + 0)
+#else
+#define MAX_HISTORY 0
+#endif
+struct line_input_t {
+ int flags;
+ const char *path_lookup;
+#if MAX_HISTORY
+ int cnt_history;
+ int cur_history;
+ USE_FEATURE_EDITING_SAVEHISTORY(const char *hist_file;)
+ char *history[MAX_HISTORY + 1];
+#endif
+};
+enum {
+ DO_HISTORY = 1 * (MAX_HISTORY > 0),
+ SAVE_HISTORY = 2 * (MAX_HISTORY > 0) * ENABLE_FEATURE_EDITING_SAVEHISTORY,
+ TAB_COMPLETION = 4 * ENABLE_FEATURE_TAB_COMPLETION,
+ USERNAME_COMPLETION = 8 * ENABLE_FEATURE_USERNAME_COMPLETION,
+ VI_MODE = 0x10 * ENABLE_FEATURE_EDITING_VI,
+ WITH_PATH_LOOKUP = 0x20,
+ FOR_SHELL = DO_HISTORY | SAVE_HISTORY | TAB_COMPLETION | USERNAME_COMPLETION,
+};
+typedef struct line_input_t line_input_t;
+line_input_t *new_line_input_t(int flags);
+int read_line_input(const char* prompt, char* command, int maxsize, line_input_t *state);
+#else
+int read_line_input(const char* prompt, char* command, int maxsize);
+#define read_line_input(prompt, command, maxsize, state) \
+ read_line_input(prompt, command, maxsize)
+#endif
+
+
+#ifndef COMM_LEN
+#ifdef TASK_COMM_LEN
+enum { COMM_LEN = TASK_COMM_LEN };
+#else
+/* synchronize with sizeof(task_struct.comm) in /usr/include/linux/sched.h */
+enum { COMM_LEN = 16 };
+#endif
+#endif
+typedef struct {
+ DIR *dir;
+/* Fields are set to 0/NULL if failed to determine (or not requested) */
+ char *cmd;
+ unsigned long vsz;
+ unsigned long stime, utime;
+ unsigned pid;
+ unsigned ppid;
+ unsigned pgid;
+ unsigned sid;
+ unsigned uid;
+ unsigned gid;
+ /* basename of executable file in call to exec(2), size from */
+ /* sizeof(task_struct.comm) in /usr/include/linux/sched.h */
+ char state[4];
+ char comm[COMM_LEN];
+// user/group? - use passwd/group parsing functions
+} procps_status_t;
+enum {
+ PSSCAN_PID = 1 << 0,
+ PSSCAN_PPID = 1 << 1,
+ PSSCAN_PGID = 1 << 2,
+ PSSCAN_SID = 1 << 3,
+ PSSCAN_UIDGID = 1 << 4,
+ PSSCAN_COMM = 1 << 5,
+ PSSCAN_CMD = 1 << 6,
+ PSSCAN_STATE = 1 << 7,
+ PSSCAN_VSZ = 1 << 8,
+ PSSCAN_STIME = 1 << 9,
+ PSSCAN_UTIME = 1 << 10,
+ /* These are all retrieved from proc/NN/stat in one go: */
+ PSSCAN_STAT = PSSCAN_PPID | PSSCAN_PGID | PSSCAN_SID
+ | PSSCAN_COMM | PSSCAN_STATE
+ | PSSCAN_VSZ | PSSCAN_STIME | PSSCAN_UTIME,
+};
+procps_status_t* alloc_procps_scan(int flags);
+void free_procps_scan(procps_status_t* sp);
+procps_status_t* procps_scan(procps_status_t* sp, int flags);
+pid_t *find_pid_by_name(const char* procName);
+pid_t *pidlist_reverse(pid_t *pidList);
+
+
+extern const char bb_uuenc_tbl_base64[];
+extern const char bb_uuenc_tbl_std[];
+void bb_uuencode(const unsigned char *s, char *store, const int length, const char *tbl);
+
+typedef struct sha1_ctx_t {
+ uint32_t count[2];
+ uint32_t hash[5];
+ uint32_t wbuf[16];
+} sha1_ctx_t;
+void sha1_begin(sha1_ctx_t *ctx);
+void sha1_hash(const void *data, size_t length, sha1_ctx_t *ctx);
+void *sha1_end(void *resbuf, sha1_ctx_t *ctx);
+
+typedef struct md5_ctx_t {
+ uint32_t A;
+ uint32_t B;
+ uint32_t C;
+ uint32_t D;
+ uint64_t total;
+ uint32_t buflen;
+ char buffer[128];
+} md5_ctx_t;
+void md5_begin(md5_ctx_t *ctx);
+void md5_hash(const void *data, size_t length, md5_ctx_t *ctx);
+void *md5_end(void *resbuf, md5_ctx_t *ctx);
+
+uint32_t *crc32_filltable(int endian);
+
+
+enum { /* DO NOT CHANGE THESE VALUES! cp.c, mv.c, install.c depend on them. */
+ FILEUTILS_PRESERVE_STATUS = 1,
+ FILEUTILS_DEREFERENCE = 2,
+ FILEUTILS_RECUR = 4,
+ FILEUTILS_FORCE = 8,
+ FILEUTILS_INTERACTIVE = 0x10,
+ FILEUTILS_MAKE_HARDLINK = 0x20,
+ FILEUTILS_MAKE_SOFTLINK = 0x40,
+#if ENABLE_SELINUX
+ FILEUTILS_PRESERVE_SECURITY_CONTEXT = 0x80,
+ FILEUTILS_SET_SECURITY_CONTEXT = 0x100
+#endif
+};
+
+#define FILEUTILS_CP_OPTSTR "pdRfils" USE_SELINUX("c")
+extern const char *applet_name;
+extern const char BB_BANNER[];
+
+extern const char bb_msg_full_version[];
+extern const char bb_msg_memory_exhausted[];
+extern const char bb_msg_invalid_date[];
+extern const char bb_msg_read_error[];
+extern const char bb_msg_write_error[];
+extern const char bb_msg_unknown[];
+extern const char bb_msg_can_not_create_raw_socket[];
+extern const char bb_msg_perm_denied_are_you_root[];
+extern const char bb_msg_requires_arg[];
+extern const char bb_msg_invalid_arg[];
+extern const char bb_msg_standard_input[];
+extern const char bb_msg_standard_output[];
+
+extern const char bb_str_default[];
+/* NB: (bb_hexdigits_upcase[i] | 0x20) -> lowercase hex digit */
+extern const char bb_hexdigits_upcase[];
+
+extern const char bb_path_mtab_file[];
+extern const char bb_path_nologin_file[];
+extern const char bb_path_passwd_file[];
+extern const char bb_path_shadow_file[];
+extern const char bb_path_gshadow_file[];
+extern const char bb_path_group_file[];
+extern const char bb_path_securetty_file[];
+extern const char bb_path_motd_file[];
+extern const char bb_path_wtmp_file[];
+extern const char bb_dev_null[];
+
+extern const int const_int_0;
+extern const int const_int_1;
+
+#ifndef BUFSIZ
+#define BUFSIZ 4096
+#endif
+extern char bb_common_bufsiz1[BUFSIZ+1];
+/* This struct is deliberately not defined. */
+/* See docs/keep_data_small.txt */
+struct globals;
+/* Magic prevents this from going into rodata */
+/* If you want to assign a value, use PTR_TO_GLOBALS = xxx */
+extern struct globals *const ptr_to_globals;
+#define PTR_TO_GLOBALS (*(struct globals**)&ptr_to_globals)
+
+/* You can change LIBBB_DEFAULT_LOGIN_SHELL, but don't use it,
+ * use bb_default_login_shell and following defines.
+ * If you change LIBBB_DEFAULT_LOGIN_SHELL,
+ * don't forget to change increment constant. */
+#define LIBBB_DEFAULT_LOGIN_SHELL "-/bin/sh"
+extern const char bb_default_login_shell[];
+/* "/bin/sh" */
+#define DEFAULT_SHELL (bb_default_login_shell+1)
+/* "sh" */
+#define DEFAULT_SHELL_SHORT_NAME (bb_default_login_shell+6)
+
+
+#if ENABLE_FEATURE_DEVFS
+# define CURRENT_VC "/dev/vc/0"
+# define VC_1 "/dev/vc/1"
+# define VC_2 "/dev/vc/2"
+# define VC_3 "/dev/vc/3"
+# define VC_4 "/dev/vc/4"
+# define VC_5 "/dev/vc/5"
+#if defined(__sh__) || defined(__H8300H__) || defined(__H8300S__)
+/* Yes, this sucks, but both SH (including sh64) and H8 have a SCI(F) for their
+ respective serial ports .. as such, we can't use the common device paths for
+ these. -- PFM */
+# define SC_0 "/dev/ttsc/0"
+# define SC_1 "/dev/ttsc/1"
+# define SC_FORMAT "/dev/ttsc/%d"
+#else
+# define SC_0 "/dev/tts/0"
+# define SC_1 "/dev/tts/1"
+# define SC_FORMAT "/dev/tts/%d"
+#endif
+# define VC_FORMAT "/dev/vc/%d"
+# define LOOP_FORMAT "/dev/loop/%d"
+# define LOOP_NAME "/dev/loop/"
+# define FB_0 "/dev/fb/0"
+#else
+# define CURRENT_VC "/dev/tty0"
+# define VC_1 "/dev/tty1"
+# define VC_2 "/dev/tty2"
+# define VC_3 "/dev/tty3"
+# define VC_4 "/dev/tty4"
+# define VC_5 "/dev/tty5"
+#if defined(__sh__) || defined(__H8300H__) || defined(__H8300S__)
+# define SC_0 "/dev/ttySC0"
+# define SC_1 "/dev/ttySC1"
+# define SC_FORMAT "/dev/ttySC%d"
+#else
+# define SC_0 "/dev/ttyS0"
+# define SC_1 "/dev/ttyS1"
+# define SC_FORMAT "/dev/ttyS%d"
+#endif
+# define VC_FORMAT "/dev/tty%d"
+# define LOOP_FORMAT "/dev/loop%d"
+# define LOOP_NAME "/dev/loop"
+# define FB_0 "/dev/fb0"
+#endif
+
+/* The following devices are the same on devfs and non-devfs systems. */
+#define CURRENT_TTY "/dev/tty"
+#define DEV_CONSOLE "/dev/console"
+
+
+#ifndef RB_POWER_OFF
+/* Stop system and switch power off if possible. */
+#define RB_POWER_OFF 0x4321fedc
+#endif
+
+/* Make sure we call functions instead of macros. */
+#undef isalnum
+#undef isalpha
+#undef isascii
+#undef isblank
+#undef iscntrl
+#undef isgraph
+#undef islower
+#undef isprint
+#undef ispunct
+#undef isspace
+#undef isupper
+#undef isxdigit
+
+/* This one is more efficient - we save ~400 bytes */
+#undef isdigit
+#define isdigit(a) ((unsigned)((a) - '0') <= 9)
+
+
+#ifdef DMALLOC
+#include <dmalloc.h>
+#endif
+
+#endif /* __LIBBUSYBOX_H__ */
diff --git a/i/pc104/initrd/conf/busybox/include/platform.h b/i/pc104/initrd/conf/busybox/include/platform.h
new file mode 100644
index 0000000..69d031d
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/include/platform.h
@@ -0,0 +1,311 @@
+/* vi: set sw=4 ts=4: */
+/*
+ Copyright 2006, Bernhard Fischer
+
+ Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+*/
+#ifndef __PLATFORM_H
+#define __PLATFORM_H 1
+
+/* Convenience macros to test the version of gcc. */
+#undef __GNUC_PREREQ
+#if defined __GNUC__ && defined __GNUC_MINOR__
+# define __GNUC_PREREQ(maj, min) \
+ ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min))
+#else
+# define __GNUC_PREREQ(maj, min) 0
+#endif
+
+/* __restrict is known in EGCS 1.2 and above. */
+#if !__GNUC_PREREQ (2,92)
+# ifndef __restrict
+# define __restrict /* Ignore */
+# endif
+#endif
+
+/* Define macros for some gcc attributes. This permits us to use the
+ macros freely, and know that they will come into play for the
+ version of gcc in which they are supported. */
+
+#if !__GNUC_PREREQ (2,7)
+# ifndef __attribute__
+# define __attribute__(x)
+# endif
+#endif
+
+#undef inline
+#if defined(__STDC_VERSION__) && __STDC_VERSION__ > 199901L
+/* it's a keyword */
+#else
+# if __GNUC_PREREQ (2,7)
+# define inline __inline__
+# else
+# define inline
+# endif
+#endif
+
+#ifndef __const
+# define __const const
+#endif
+
+# define ATTRIBUTE_UNUSED __attribute__ ((__unused__))
+# define ATTRIBUTE_NORETURN __attribute__ ((__noreturn__))
+# define ATTRIBUTE_PACKED __attribute__ ((__packed__))
+# define ATTRIBUTE_ALIGNED(m) __attribute__ ((__aligned__(m)))
+# if __GNUC_PREREQ (3,0)
+# define ATTRIBUTE_ALWAYS_INLINE __attribute__ ((always_inline)) inline
+# else
+# define ATTRIBUTE_ALWAYS_INLINE inline
+# endif
+
+/* -fwhole-program makes all symbols local. The attribute externally_visible
+ forces a symbol global. */
+# if __GNUC_PREREQ (4,1)
+# define ATTRIBUTE_EXTERNALLY_VISIBLE __attribute__ ((__externally_visible__))
+# else
+# define ATTRIBUTE_EXTERNALLY_VISIBLE
+# endif /* GNUC >= 4.1 */
+
+/* We use __extension__ in some places to suppress -pedantic warnings
+ about GCC extensions. This feature didn't work properly before
+ gcc 2.8. */
+#if !__GNUC_PREREQ (2,8)
+# ifndef __extension__
+# define __extension__
+# endif
+#endif
+
+/* gcc-2.95 had no va_copy but only __va_copy. */
+#if !__GNUC_PREREQ (3,0)
+# include <stdarg.h>
+# if !defined va_copy && defined __va_copy
+# define va_copy(d,s) __va_copy((d),(s))
+# endif
+#endif
+
+/* ---- Endian Detection ------------------------------------ */
+
+#if (defined __digital__ && defined __unix__)
+# include <sex.h>
+# define __BIG_ENDIAN__ (BYTE_ORDER == BIG_ENDIAN)
+# define __BYTE_ORDER BYTE_ORDER
+#elif !defined __APPLE__
+# include <byteswap.h>
+# include <endian.h>
+#endif
+
+#ifdef __BIG_ENDIAN__
+# define BB_BIG_ENDIAN 1
+# define BB_LITTLE_ENDIAN 0
+#elif __BYTE_ORDER == __BIG_ENDIAN
+# define BB_BIG_ENDIAN 1
+# define BB_LITTLE_ENDIAN 0
+#else
+# define BB_BIG_ENDIAN 0
+# define BB_LITTLE_ENDIAN 1
+#endif
+
+#if BB_BIG_ENDIAN
+#define SWAP_BE16(x) (x)
+#define SWAP_BE32(x) (x)
+#define SWAP_BE64(x) (x)
+#define SWAP_LE16(x) bswap_16(x)
+#define SWAP_LE32(x) bswap_32(x)
+#define SWAP_LE64(x) bswap_64(x)
+#else
+#define SWAP_BE16(x) bswap_16(x)
+#define SWAP_BE32(x) bswap_32(x)
+#define SWAP_BE64(x) bswap_64(x)
+#define SWAP_LE16(x) (x)
+#define SWAP_LE32(x) (x)
+#define SWAP_LE64(x) (x)
+#endif
+
+/* ---- Networking ------------------------------------------ */
+#ifndef __APPLE__
+# include <arpa/inet.h>
+#else
+# include <netinet/in.h>
+#endif
+
+#ifndef __socklen_t_defined
+typedef int socklen_t;
+#endif
+
+/* ---- Compiler dependent settings ------------------------- */
+#if (defined __digital__ && defined __unix__)
+# undef HAVE_MNTENT_H
+#else
+# define HAVE_MNTENT_H 1
+#endif /* ___digital__ && __unix__ */
+
+/* linux/loop.h relies on __u64. Make sure we have that as a proper type
+ * until userspace is widely fixed. */
+#ifndef __GNUC__
+#if defined __INTEL_COMPILER
+__extension__ typedef __signed__ long long __s64;
+__extension__ typedef unsigned long long __u64;
+#endif /* __INTEL_COMPILER */
+#endif /* ifndef __GNUC__ */
+
+/*----- Kernel versioning ------------------------------------*/
+#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
+
+/* ---- miscellaneous --------------------------------------- */
+
+#if defined(__GNU_LIBRARY__) && __GNU_LIBRARY__ < 5 && \
+ !defined(__dietlibc__) && \
+ !defined(_NEWLIB_VERSION) && \
+ !(defined __digital__ && defined __unix__)
+# error "Sorry, this libc version is not supported :("
+#endif
+
+// Don't perpetuate e2fsck crap into the headers. Clean up e2fsck instead.
+
+#if defined __GLIBC__ || defined __UCLIBC__ \
+ || defined __dietlibc__ || defined _NEWLIB_VERSION
+#include <features.h>
+#define HAVE_FEATURES_H
+#include <stdint.h>
+#define HAVE_STDINT_H
+#else
+/* Largest integral types. */
+#if __BIG_ENDIAN__
+typedef long int intmax_t;
+typedef unsigned long int uintmax_t;
+#else
+__extension__
+typedef long long int intmax_t;
+__extension__
+typedef unsigned long long int uintmax_t;
+#endif
+#endif
+
+/* Size-saving "small" ints (arch-dependent) */
+#if defined(i386) || defined(__x86_64__) || defined(__mips__) || defined(__cris__)
+/* add other arches which benefit from this... */
+typedef signed char smallint;
+typedef unsigned char smalluint;
+#else
+/* for arches where byte accesses generate larger code: */
+typedef int smallint;
+typedef unsigned smalluint;
+#endif
+
+/* ISO C Standard: 7.16 Boolean type and values <stdbool.h> */
+#if (defined __digital__ && defined __unix__)
+/* old system without (proper) C99 support */
+#define bool smalluint
+#else
+/* modern system, so use it */
+#include <stdbool.h>
+#endif
+
+
+/* uclibc does not implement daemon() for no-mmu systems.
+ * For 0.9.29 and svn, __ARCH_USE_MMU__ indicates no-mmu reliably.
+ * For earlier versions there is no reliable way to check if we are building
+ * for a mmu-less system; the user should pass EXTRA_CFLAGS="-DBB_NOMMU"
+ * on his own.
+ */
+#if defined __UCLIBC__ && __UCLIBC_MAJOR__ >= 0 && __UCLIBC_MINOR__ >= 9 && \
+ __UCLIBC_SUBLEVEL__ > 28 && !defined __ARCH_USE_MMU__
+#define BB_NOMMU
+#endif
+
+/* Platforms that haven't got dprintf need to implement fdprintf() in
+ * libbb. This would require a platform.c. It's not going to be cleaned
+ * out of the tree, so stop saying it should be. */
+#if !defined(__dietlibc__)
+/* Needed for: glibc */
+/* Not needed for: dietlibc */
+/* Others: ?? (add as needed) */
+#define fdprintf dprintf
+#endif
+
+#if defined(__dietlibc__)
+static ATTRIBUTE_ALWAYS_INLINE char* strchrnul(const char *s, char c) {
+ while (*s && *s != c) ++s;
+ return (char*)s;
+}
+#endif
+
+/* Don't use lchown with glibc older than 2.1.x ... uC-libc lacks it */
+#if (defined __GLIBC__ && __GLIBC__ <= 2 && __GLIBC_MINOR__ < 1) || \
+ defined __UC_LIBC__
+# define lchown chown
+#endif
+
+/* THIS SHOULD BE CLEANED OUT OF THE TREE ENTIRELY */
+/* FIXME: fix tar.c! */
+#ifndef FNM_LEADING_DIR
+#define FNM_LEADING_DIR 0
+#endif
+
+#if (defined __digital__ && defined __unix__)
+#include <standards.h>
+#define HAVE_STANDARDS_H
+#include <inttypes.h>
+#define HAVE_INTTYPES_H
+#define PRIu32 "u"
+
+/* use legacy setpgrp(pidt_,pid_t) for now. move to platform.c */
+#define bb_setpgrp do { pid_t __me = getpid(); setpgrp(__me,__me); } while (0)
+
+#if !defined ADJ_OFFSET_SINGLESHOT && defined MOD_CLKA && defined MOD_OFFSET
+#define ADJ_OFFSET_SINGLESHOT (MOD_CLKA | MOD_OFFSET)
+#endif
+#if !defined ADJ_FREQUENCY && defined MOD_FREQUENCY
+#define ADJ_FREQUENCY MOD_FREQUENCY
+#endif
+#if !defined ADJ_TIMECONST && defined MOD_TIMECONST
+#define ADJ_TIMECONST MOD_TIMECONST
+#endif
+#if !defined ADJ_TICK && defined MOD_CLKB
+#define ADJ_TICK MOD_CLKB
+#endif
+
+#else
+#define bb_setpgrp setpgrp()
+#endif
+
+#if defined(__linux__)
+#include <sys/mount.h>
+// Make sure we have all the new mount flags we actually try to use.
+#ifndef MS_BIND
+#define MS_BIND (1<<12)
+#endif
+#ifndef MS_MOVE
+#define MS_MOVE (1<<13)
+#endif
+#ifndef MS_RECURSIVE
+#define MS_RECURSIVE (1<<14)
+#endif
+#ifndef MS_SILENT
+#define MS_SILENT (1<<15)
+#endif
+
+// The shared subtree stuff, which went in around 2.6.15
+#ifndef MS_UNBINDABLE
+#define MS_UNBINDABLE (1<<17)
+#endif
+#ifndef MS_PRIVATE
+#define MS_PRIVATE (1<<18)
+#endif
+#ifndef MS_SLAVE
+#define MS_SLAVE (1<<19)
+#endif
+#ifndef MS_SHARED
+#define MS_SHARED (1<<20)
+#endif
+
+
+#if !defined(BLKSSZGET)
+#define BLKSSZGET _IO(0x12, 104)
+#endif
+#if !defined(BLKGETSIZE64)
+#define BLKGETSIZE64 _IOR(0x12,114,size_t)
+#endif
+#endif
+
+#endif /* platform.h */
diff --git a/i/pc104/initrd/conf/busybox/include/pwd_.h b/i/pc104/initrd/conf/busybox/include/pwd_.h
new file mode 100644
index 0000000..f47e4eb
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/include/pwd_.h
@@ -0,0 +1,122 @@
+/* vi: set sw=4 ts=4: */
+/* Copyright (C) 1991,92,95,96,97,98,99,2001 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+/*
+ * POSIX Standard: 9.2.2 User Database Access <pwd.h>
+ */
+
+#if !ENABLE_USE_BB_PWD_GRP
+
+#include <pwd.h>
+
+#else
+
+#ifndef _PWD_H
+#define _PWD_H 1
+
+/* The passwd structure. */
+struct passwd {
+ char *pw_name; /* Username. */
+ char *pw_passwd; /* Password. */
+ uid_t pw_uid; /* User ID. */
+ gid_t pw_gid; /* Group ID. */
+ char *pw_gecos; /* Real name. */
+ char *pw_dir; /* Home directory. */
+ char *pw_shell; /* Shell program. */
+};
+
+
+#define setpwent bb_internal_setpwent
+#define endpwent bb_internal_endpwent
+#define getpwent bb_internal_getpwent
+#define fgetpwent bb_internal_fgetpwent
+#define putpwent bb_internal_putpwent
+#define getpwuid bb_internal_getpwuid
+#define getpwnam bb_internal_getpwnam
+#define getpwent_r bb_internal_getpwent_r
+#define getpwuid_r bb_internal_getpwuid_r
+#define getpwnam_r bb_internal_getpwnam_r
+#define fgetpwent_r bb_internal_fgetpwent_r
+#define getpw bb_internal_getpw
+
+
+/* All function names below should be remapped by #defines above
+ * in order to not collide with libc names.
+ * In theory it isn't necessary, but I saw weird interactions at link time.
+ * Let's play safe */
+
+
+/* Rewind the password-file stream. */
+extern void setpwent(void);
+
+/* Close the password-file stream. */
+extern void endpwent(void);
+
+/* Read an entry from the password-file stream, opening it if necessary. */
+extern struct passwd *getpwent(void);
+
+/* Read an entry from STREAM. */
+extern struct passwd *fgetpwent(FILE *__stream);
+
+/* Write the given entry onto the given stream. */
+extern int putpwent(__const struct passwd *__restrict __p,
+ FILE *__restrict __f);
+
+/* Search for an entry with a matching user ID. */
+extern struct passwd *getpwuid(uid_t __uid);
+
+/* Search for an entry with a matching username. */
+extern struct passwd *getpwnam(__const char *__name);
+
+/* Reentrant versions of some of the functions above.
+
+ PLEASE NOTE: the `getpwent_r' function is not (yet) standardized.
+ The interface may change in later versions of this library. But
+ the interface is designed following the principals used for the
+ other reentrant functions so the chances are good this is what the
+ POSIX people would choose. */
+
+extern int getpwent_r(struct passwd *__restrict __resultbuf,
+ char *__restrict __buffer, size_t __buflen,
+ struct passwd **__restrict __result);
+
+extern int getpwuid_r(uid_t __uid,
+ struct passwd *__restrict __resultbuf,
+ char *__restrict __buffer, size_t __buflen,
+ struct passwd **__restrict __result);
+
+extern int getpwnam_r(__const char *__restrict __name,
+ struct passwd *__restrict __resultbuf,
+ char *__restrict __buffer, size_t __buflen,
+ struct passwd **__restrict __result);
+
+/* Read an entry from STREAM. This function is not standardized and
+ probably never will. */
+extern int fgetpwent_r(FILE *__restrict __stream,
+ struct passwd *__restrict __resultbuf,
+ char *__restrict __buffer, size_t __buflen,
+ struct passwd **__restrict __result);
+
+/* Re-construct the password-file line for the given uid
+ in the given buffer. This knows the format that the caller
+ will expect, but this need not be the format of the password file. */
+extern int getpw(uid_t __uid, char *__buffer);
+
+#endif /* pwd.h */
+#endif
diff --git a/i/pc104/initrd/conf/busybox/include/shadow_.h b/i/pc104/initrd/conf/busybox/include/shadow_.h
new file mode 100644
index 0000000..fb2153c
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/include/shadow_.h
@@ -0,0 +1,114 @@
+/* vi: set sw=4 ts=4: */
+/* Copyright (C) 1996, 1997, 1998, 1999 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+/* Declaration of types and functions for shadow password suite */
+
+#if !ENABLE_USE_BB_SHADOW
+
+#include <shadow.h>
+
+#else
+
+#ifndef _SHADOW_H
+#define _SHADOW_H 1
+
+/* Paths to the user database files */
+#ifndef _PATH_SHADOW
+#define _PATH_SHADOW "/etc/shadow"
+#endif
+
+/* Structure of the password file */
+struct spwd {
+ char *sp_namp; /* Login name */
+ char *sp_pwdp; /* Encrypted password */
+ long int sp_lstchg; /* Date of last change */
+ long int sp_min; /* Minimum number of days between changes */
+ long int sp_max; /* Maximum number of days between changes */
+ long int sp_warn; /* Number of days to warn user to change the password */
+ long int sp_inact; /* Number of days the account may be inactive */
+ long int sp_expire; /* Number of days since 1970-01-01 until account expires */
+ unsigned long int sp_flag; /* Reserved */
+};
+
+
+#define setspent bb_internal_setspent
+#define endspent bb_internal_endspent
+#define getspent bb_internal_getspent
+#define getspnam bb_internal_getspnam
+#define sgetspent bb_internal_sgetspent
+#define fgetspent bb_internal_fgetspent
+#define putspent bb_internal_putspent
+#define getspent_r bb_internal_getspent_r
+#define getspnam_r bb_internal_getspnam_r
+#define sgetspent_r bb_internal_sgetspent_r
+#define fgetspent_r bb_internal_fgetspent_r
+#define lckpwdf bb_internal_lckpwdf
+#define ulckpwdf bb_internal_ulckpwdf
+
+
+/* All function names below should be remapped by #defines above
+ * in order to not collide with libc names.
+ * In theory it isn't necessary, but I saw weird interactions at link time.
+ * Let's play safe */
+
+
+/* Open database for reading */
+extern void setspent(void);
+
+/* Close database */
+extern void endspent(void);
+
+/* Get next entry from database, perhaps after opening the file */
+extern struct spwd *getspent(void);
+
+/* Get shadow entry matching NAME */
+extern struct spwd *getspnam(__const char *__name);
+
+/* Read shadow entry from STRING */
+extern struct spwd *sgetspent(__const char *__string);
+
+/* Read next shadow entry from STREAM */
+extern struct spwd *fgetspent(FILE *__stream);
+
+/* Write line containing shadow password entry to stream */
+extern int putspent(__const struct spwd *__p, FILE *__stream);
+
+/* Reentrant versions of some of the functions above */
+extern int getspent_r(struct spwd *__result_buf, char *__buffer,
+ size_t __buflen, struct spwd **__result);
+
+extern int getspnam_r(__const char *__name, struct spwd *__result_buf,
+ char *__buffer, size_t __buflen,
+ struct spwd **__result);
+
+extern int sgetspent_r(__const char *__string, struct spwd *__result_buf,
+ char *__buffer, size_t __buflen,
+ struct spwd **__result);
+
+extern int fgetspent_r(FILE *__stream, struct spwd *__result_buf,
+ char *__buffer, size_t __buflen,
+ struct spwd **__result);
+/* Protect password file against multi writers */
+extern int lckpwdf(void);
+
+/* Unlock password file */
+extern int ulckpwdf(void);
+
+#endif /* shadow.h */
+#endif
diff --git a/i/pc104/initrd/conf/busybox/include/unarchive.h b/i/pc104/initrd/conf/busybox/include/unarchive.h
new file mode 100644
index 0000000..36b56a9
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/include/unarchive.h
@@ -0,0 +1,118 @@
+/* vi: set sw=4 ts=4: */
+#ifndef __UNARCHIVE_H__
+#define __UNARCHIVE_H__
+
+#define ARCHIVE_PRESERVE_DATE 1
+#define ARCHIVE_CREATE_LEADING_DIRS 2
+#define ARCHIVE_EXTRACT_UNCONDITIONAL 4
+#define ARCHIVE_EXTRACT_QUIET 8
+#define ARCHIVE_EXTRACT_NEWER 16
+#define ARCHIVE_NOPRESERVE_OWN 32
+#define ARCHIVE_NOPRESERVE_PERM 64
+
+#include "libbb.h"
+
+typedef struct file_headers_s {
+ char *name;
+ char *link_name;
+ off_t size;
+ uid_t uid;
+ gid_t gid;
+ mode_t mode;
+ time_t mtime;
+ dev_t device;
+} file_header_t;
+
+typedef struct archive_handle_s {
+ /* define if the header and data component should be processed */
+ char (*filter)(struct archive_handle_s *);
+ llist_t *accept;
+ /* List of files that have been rejected */
+ llist_t *reject;
+ /* List of files that have successfully been worked on */
+ llist_t *passed;
+
+ /* Contains the processed header entry */
+ file_header_t *file_header;
+
+ /* process the header component, e.g. tar -t */
+ void (*action_header)(const file_header_t *);
+
+ /* process the data component, e.g. extract to filesystem */
+ void (*action_data)(struct archive_handle_s *);
+
+ /* How to process any sub archive, e.g. get_header_tar_gz */
+ char (*action_data_subarchive)(struct archive_handle_s *);
+
+ /* Contains the handle to a sub archive */
+ struct archive_handle_s *sub_archive;
+
+ /* The raw stream as read from disk or stdin */
+ int src_fd;
+
+ /* Count the number of bytes processed */
+ off_t offset;
+
+ /* Function that skips data: read_by_char or read_by_skip */
+ void (*seek)(const struct archive_handle_s *archive_handle, const unsigned int amount);
+
+ /* Temporary storage */
+ char *buffer;
+
+ /* Flags and misc. stuff */
+ unsigned char flags;
+
+} archive_handle_t;
+
+
+extern archive_handle_t *init_handle(void);
+
+extern char filter_accept_all(archive_handle_t *archive_handle);
+extern char filter_accept_list(archive_handle_t *archive_handle);
+extern char filter_accept_list_reassign(archive_handle_t *archive_handle);
+extern char filter_accept_reject_list(archive_handle_t *archive_handle);
+
+extern void unpack_ar_archive(archive_handle_t *ar_archive);
+
+extern void data_skip(archive_handle_t *archive_handle);
+extern void data_extract_all(archive_handle_t *archive_handle);
+extern void data_extract_to_stdout(archive_handle_t *archive_handle);
+extern void data_extract_to_buffer(archive_handle_t *archive_handle);
+
+extern void header_skip(const file_header_t *file_header);
+extern void header_list(const file_header_t *file_header);
+extern void header_verbose_list(const file_header_t *file_header);
+
+extern void check_header_gzip_or_die(int src_fd);
+
+extern char get_header_ar(archive_handle_t *archive_handle);
+extern char get_header_cpio(archive_handle_t *archive_handle);
+extern char get_header_tar(archive_handle_t *archive_handle);
+extern char get_header_tar_bz2(archive_handle_t *archive_handle);
+extern char get_header_tar_lzma(archive_handle_t *archive_handle);
+extern char get_header_tar_gz(archive_handle_t *archive_handle);
+
+extern void seek_by_jump(const archive_handle_t *archive_handle, const unsigned int amount);
+extern void seek_by_read(const archive_handle_t *archive_handle, const unsigned int amount);
+
+extern ssize_t archive_xread_all_eof(archive_handle_t *archive_handle, unsigned char *buf, size_t count);
+
+extern void data_align(archive_handle_t *archive_handle, const unsigned short boundary);
+extern const llist_t *find_list_entry(const llist_t *list, const char *filename);
+extern const llist_t *find_list_entry2(const llist_t *list, const char *filename);
+
+extern USE_DESKTOP(long long) int uncompressStream(int src_fd, int dst_fd);
+
+typedef struct inflate_unzip_result {
+ off_t bytes_out;
+ uint32_t crc;
+} inflate_unzip_result;
+
+extern USE_DESKTOP(long long) int inflate_unzip(inflate_unzip_result *res, unsigned bufsize, int in, int out);
+extern USE_DESKTOP(long long) int inflate_gunzip(int in, int out);
+extern USE_DESKTOP(long long) int unlzma(int src_fd, int dst_fd);
+
+extern int open_transformer(int src_fd,
+ USE_DESKTOP(long long) int (*transformer)(int src_fd, int dst_fd));
+
+#endif
diff --git a/i/pc104/initrd/conf/busybox/include/usage.h b/i/pc104/initrd/conf/busybox/include/usage.h
new file mode 100644
index 0000000..66f8638
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/include/usage.h
@@ -0,0 +1,3688 @@
+/* vi: set sw=8 ts=8: */
+/*
+ * This file suffers from chronically incorrect tabification
+ * of messages. Before editing this file:
+ * 1. Switch you editor to 8-space tab mode.
+ * 2. Do not use \t in messages, use real tab character.
+ * 3. Start each source line with message as follows:
+ * |<7 spaces>"text with tabs"....
+ */
+
+#ifndef __BB_USAGE_H__
+#define __BB_USAGE_H__
+
+#define addgroup_trivial_usage \
+ "[-g GID] group_name [user_name]"
+#define addgroup_full_usage \
+ "Add a group to the system" \
+ "\n\nOptions:\n" \
+ " -g GID Specify gid"
+
+#define adduser_trivial_usage \
+ "[OPTIONS] user_name"
+#define adduser_full_usage \
+ "Add a user to the system" \
+ "\n\nOptions:\n" \
+ " -h DIR Assign home directory DIR\n" \
+ " -g GECOS Assign gecos field GECOS\n" \
+ " -s SHELL Assign login shell SHELL\n" \
+ " -G Add the user to existing group GROUP\n" \
+ " -S Create a system user (ignored)\n" \
+ " -D Do not assign a password (logins still possible via ssh)\n" \
+ " -H Do not create the home directory"
+
+#define adjtimex_trivial_usage \
+ "[-q] [-o offset] [-f frequency] [-p timeconstant] [-t tick]"
+#define adjtimex_full_usage \
+ "Read and optionally set system timebase parameters.\n" \
+ "See adjtimex(2)." \
+ "\n\nOptions:\n" \
+ " -q Quiet mode - do not print\n" \
+ " -o offset Time offset, microseconds\n" \
+ " -f frequency Frequency adjust, integer kernel units (65536 is 1ppm)\n" \
+ " (positive values make the system clock run fast)\n" \
+ " -t tick Microseconds per tick, usually 10000\n" \
+ " -p timeconstant"
+
+#define ar_trivial_usage \
+ "[-o] [-v] [-p] [-t] [-x] ARCHIVE FILES"
+#define ar_full_usage \
+ "Extract or list FILES from an ar archive" \
+ "\n\nOptions:\n" \
+ " -o Preserve original dates\n" \
+ " -p Extract to stdout\n" \
+ " -t List\n" \
+ " -x Extract\n" \
+ " -v Verbosely list files processed"
+
+#define arp_trivial_usage \
+ "\n" \
+ "[-vn] [-H type] [-i if] -a [hostname]\n" \
+ "[-v] [-i if] -d hostname [pub]\n" \
+ "[-v] [-H type] [-i if] -s hostname hw_addr [temp]\n" \
+ "[-v] [-H type] [-i if] -s hostname hw_addr [netmask nm] pub\n" \
+ "[-v] [-H type] [-i if] -Ds hostname ifa [netmask nm] pub\n"
+#define arp_full_usage \
+ "Manipulate the system ARP cache" \
+ "\n\nOptions:" \
+ "\n -a Display (all) hosts" \
+ "\n -s Set a new ARP entry" \
+ "\n -d Delete a specified entry" \
+ "\n -v Verbose" \
+ "\n -n Don't resolve names" \
+ "\n -i if Specify network interface (e.g. eth0)" \
+ "\n -D Read <hwaddr> from given device" \
+ "\n -A, -p Specify protocol family" \
+ "\n -H hwtype Specify hardware address type"
+
+#define arping_trivial_usage \
+ "[-fqbDUA] [-c count] [-w timeout] [-i device] [-s sender] target"
+#define arping_full_usage \
+ "Ping hosts by ARP requests/replies" \
+ "\n\nOptions:\n" \
+ " -f Quit on first ARP reply\n" \
+ " -q Be quiet\n" \
+ " -b Keep broadcasting, don't go unicast\n" \
+ " -D Duplicated address detection mode\n" \
+ " -U Unsolicited ARP mode, update your neighbours\n" \
+ " -A ARP answer mode, update your neighbours\n" \
+ " -c count Stop after sending count ARP request packets\n" \
+ " -w timeout Time to wait for ARP reply, in seconds\n" \
+ " -i device Outgoing interface name, default is eth0\n" \
+ " -s sender Set specific sender IP address\n" \
+ " target Target IP address of ARP request"
+
+#define ash_trivial_usage \
+ "[FILE]...\n" \
+ "or: ash -c command [args]..."
+#define ash_full_usage \
+ "The ash shell (command interpreter)"
+
+#define awk_trivial_usage \
+ "[OPTION]... [program-text] [FILE ...]"
+#define awk_full_usage \
+ "Options:\n" \
+ " -v var=val Assign value 'val' to variable 'var'\n" \
+ " -F sep Use 'sep' as field separator\n" \
+ " -f progname Read program source from file 'progname'"
+
+#define basename_trivial_usage \
+ "FILE [SUFFIX]"
+#define basename_full_usage \
+ "Strip directory path and suffixes from FILE.\n" \
+ "If specified, also remove any trailing SUFFIX."
+#define basename_example_usage \
+ "$ basename /usr/local/bin/foo\n" \
+ "foo\n" \
+ "$ basename /usr/local/bin/\n" \
+ "bin\n" \
+ "$ basename /foo/bar.txt .txt\n" \
+ "bar"
+
+#define bunzip2_trivial_usage \
+ "[OPTION]... [FILE]"
+#define bunzip2_full_usage \
+ "Uncompress FILE (or standard input if FILE is '-' or omitted)" \
+ "\n\nOptions:\n" \
+ " -c Write output to standard output\n" \
+ " -f Force"
+
+#define busybox_notes_usage \
+ "Hello world!\n"
+
+#define bzcat_trivial_usage \
+ "FILE"
+#define bzcat_full_usage \
+ "Uncompress to stdout"
+
+#define unlzma_trivial_usage \
+ "[OPTION]... [FILE]"
+#define unlzma_full_usage \
+ "Uncompress FILE (or standard input if FILE is '-' or omitted)" \
+ "\n\nOptions:\n" \
+ " -c Write output to standard output\n" \
+ " -f Force"
+
+#define lzmacat_trivial_usage \
+ "FILE"
+#define lzmacat_full_usage \
+ "Uncompress to stdout"
+
+#define cal_trivial_usage \
+ "[-jy] [[month] year]"
+#define cal_full_usage \
+ "Display a calendar" \
+ "\n\nOptions:" \
+ "\n -j Use julian dates" \
+ "\n -y Display the entire year"
+
+#define cat_trivial_usage \
+ "[-u] [FILE]..."
+#define cat_full_usage \
+ "Concatenate FILE(s) and print them to stdout" \
+ "\n\nOptions:" \
+ "\n -u Ignored since unbuffered i/o is always used"
+#define cat_example_usage \
+ "$ cat /proc/uptime\n" \
+ "110716.72 17.67"
+
+#define catv_trivial_usage \
+ "[-etv] [FILE]..."
+#define catv_full_usage \
+ "Display nonprinting characters as ^x or M-x" \
+ "\n\nOptions:\n" \
+ " -e End each line with $\n" \
+ " -t Show tabs as ^I\n" \
+ " -v Don't use ^x or M-x escapes"
+#define chattr_trivial_usage \
+ "[-R] [-+=AacDdijsStTu] [-v version] files..."
+#define chattr_full_usage \
+ "Change file attributes on an ext2 fs\n\n" \
+ "Modifiers:\n" \
+ " - Remove attributes\n" \
+ " + Add attributes\n" \
+ " = Set attributes\n" \
+ "Attributes:\n" \
+ " A Don't track atime\n" \
+ " a Append mode only\n" \
+ " c Enable compress\n" \
+ " D Write dir contents synchronously\n" \
+ " d Do not backup with dump\n" \
+ " i Cannot be modified (immutable)\n" \
+ " j Write all data to journal first\n" \
+ " s Zero disk storage when deleted\n" \
+ " S Write file contents synchronously\n" \
+ " t Disable tail-merging of partial blocks with other files\n" \
+ " u Allow file to be undeleted\n" \
+ "Options:\n" \
+ " -R Recursively list subdirectories\n" \
+ " -v Set the file's version/generation number"
+
+#define chcon_trivial_usage \
+ "[OPTIONS] CONTEXT FILE...\n" \
+ " chcon [OPTIONS] [-u USER] [-r ROLE] [-l RANGE] [-t TYPE] FILE...\n" \
+ " chcon [OPTIONS] --reference=RFILE FILE...\n"
+#define chcon_full_usage \
+ "Change the security context of each FILE to CONTEXT\n" \
+ "\n -v, --verbose Verbose" \
+ "\n -c, --changes Report changes made" \
+ "\n -h, --no-dereference Affect symlinks instead of their targets" \
+ "\n -f, --silent, --quiet Suppress most error messages" \
+ "\n --reference=RFILE Use RFILE's group instead of using a CONTEXT value" \
+ "\n -u, --user=USER Set user USER in the target security context" \
+ "\n -r, --role=ROLE Set role ROLE in the target security context" \
+ "\n -t, --type=TYPE Set type TYPE in the target security context" \
+ "\n -l, --range=RANGE Set range RANGE in the target security context" \
+ "\n -R, --recursive Recurse subdirectories" \
+
+#define chmod_trivial_usage \
+ "[-R"USE_DESKTOP("cvf")"] MODE[,MODE]... FILE..."
+#define chmod_full_usage \
+ "Each MODE is one or more of the letters ugoa, one of the\n" \
+ "symbols +-= and one or more of the letters rwxst" \
+ "\n\nOptions:" \
+ "\n -R Changes files and directories recursively" \
+ USE_DESKTOP( \
+ "\n -c List changed files" \
+ "\n -v List all files" \
+ "\n -f Hide errors" \
+ )
+#define chmod_example_usage \
+ "$ ls -l /tmp/foo\n" \
+ "-rw-rw-r-- 1 root root 0 Apr 12 18:25 /tmp/foo\n" \
+ "$ chmod u+x /tmp/foo\n" \
+ "$ ls -l /tmp/foo\n" \
+ "-rwxrw-r-- 1 root root 0 Apr 12 18:25 /tmp/foo*\n" \
+ "$ chmod 444 /tmp/foo\n" \
+ "$ ls -l /tmp/foo\n" \
+ "-r--r--r-- 1 root root 0 Apr 12 18:25 /tmp/foo\n"
+
+#define chgrp_trivial_usage \
+ "[-RhLHP"USE_DESKTOP("cvf")"]... GROUP FILE..."
+#define chgrp_full_usage \
+ "Change the group membership of each FILE to GROUP" \
+ "\n\nOptions:" \
+ "\n -R Recurse directories" \
+ "\n -h Affect symlinks instead of symlink targets" \
+ "\n -L Traverse all symlinks to directories" \
+ "\n -H Traverse symlinks on command line only" \
+ "\n -P Do not traverse symlinks (default)" \
+ USE_DESKTOP( \
+ "\n -c List changed files" \
+ "\n -v Verbose" \
+ "\n -f Hide errors" \
+ )
+#define chgrp_example_usage \
+ "$ ls -l /tmp/foo\n" \
+ "-r--r--r-- 1 andersen andersen 0 Apr 12 18:25 /tmp/foo\n" \
+ "$ chgrp root /tmp/foo\n" \
+ "$ ls -l /tmp/foo\n" \
+ "-r--r--r-- 1 andersen root 0 Apr 12 18:25 /tmp/foo\n"
+
+#define chown_trivial_usage \
+ "[-RhLHP"USE_DESKTOP("cvf")"]... OWNER[<.|:>[GROUP]] FILE..."
+#define chown_full_usage \
+ "Change the owner and/or group of each FILE to OWNER and/or GROUP" \
+ "\n\nOptions:" \
+ "\n -R Recurse directories" \
+ "\n -h Affect symlinks instead of symlink targets" \
+ "\n -L Traverse all symlinks to directories" \
+ "\n -H Traverse symlinks on command line only" \
+ "\n -P Do not traverse symlinks (default)" \
+ USE_DESKTOP( \
+ "\n -c List changed files" \
+ "\n -v List all files" \
+ "\n -f Hide errors" \
+ )
+#define chown_example_usage \
+ "$ ls -l /tmp/foo\n" \
+ "-r--r--r-- 1 andersen andersen 0 Apr 12 18:25 /tmp/foo\n" \
+ "$ chown root /tmp/foo\n" \
+ "$ ls -l /tmp/foo\n" \
+ "-r--r--r-- 1 root andersen 0 Apr 12 18:25 /tmp/foo\n" \
+ "$ chown root.root /tmp/foo\n" \
+ "ls -l /tmp/foo\n" \
+ "-r--r--r-- 1 root root 0 Apr 12 18:25 /tmp/foo\n"
+
+#define chpst_trivial_usage \
+ "[-vP012] [-u user[:group]] [-U user[:group]] [-e dir] " \
+ "[-/ dir] [-n nice] [-m bytes] [-d bytes] [-o files] " \
+ "[-p processes] [-f bytes] [-c bytes] prog args"
+#define chpst_full_usage \
+ "Change the process state and run specified program" \
+ "\n\nOptions:\n" \
+ " -u user[:grp] Set uid and gid\n" \
+ " -U user[:grp] Set environment variables UID and GID\n" \
+ " -e dir Set environment variables as specified by files\n" \
+ " in the directory: file=1st_line_of_file\n" \
+ " -/ dir Chroot to dir\n" \
+ " -n inc Add inc to nice value\n" \
+ " -m bytes Limit data segment, stack segment, locked physical pages,\n" \
+ " and total of all segment per process to bytes bytes each\n" \
+ " -d bytes Limit data segment\n" \
+ " -o n Limit the number of open file descriptors per process to n\n" \
+ " -p n Limit number of processes per uid to n\n" \
+ " -f bytes Limit output file size to bytes bytes\n" \
+ " -c bytes Limit core file size to bytes bytes\n" \
+ " -v Verbose\n" \
+ " -P Run prog in a new process group\n" \
+ " -0 Close standard input\n" \
+ " -1 Close standard output\n" \
+ " -2 Close standard error"
+#define setuidgid_trivial_usage \
+ "account prog args"
+#define setuidgid_full_usage \
+ "Set uid and gid to account's uid and gid, removing all supplementary\n" \
+ "groups, then run prog"
+#define envuidgid_trivial_usage \
+ "account prog args"
+#define envuidgid_full_usage \
+ "Set $UID to account's uid and $GID to account's gid, then run prog"
+#define envdir_trivial_usage \
+ "dir prog args"
+#define envdir_full_usage \
+ "Set various environment variables as specified by files\n" \
+ "in the directory dir, then run prog"
+#define softlimit_trivial_usage \
+ "[-a allbytes] [-c corebytes] [-d databytes] [-f filebytes] " \
+ "[-l lockbytes] [-m membytes] [-o openfiles] [-p processes] " \
+ "[-r residentbytes] [-s stackbytes] [-t cpusecs] prog args"
+#define softlimit_full_usage \
+ "Set soft resource limits, then run prog" \
+ "\n\nOptions:\n" \
+ " -m n Same as -d n -s n -l n -a n\n" \
+ " -d n Limit the data segment per process to n bytes\n" \
+ " -s n Limit the stack segment per process to n bytes\n" \
+ " -l n Limit the locked physical pages per process to n bytes\n" \
+ " -a n Limit the total of all segments per process to n bytes\n" \
+ " -o n Limit the number of open file descriptors per process to n\n" \
+ " -p n Limit the number of processes per uid to n\n" \
+ "Options controlling file sizes:\n" \
+ " -f n Limit output file sizes to n bytes\n" \
+ " -c n Limit core file sizes to n bytes\n" \
+ "Efficiency opts:\n" \
+ " -r n Limit the resident set size to n bytes. This limit is not\n" \
+ " enforced unless physical memory is full\n" \
+ " -t n Limit the CPU time to n seconds. This limit is not enforced\n" \
+ " except that the process receives a SIGXCPU signal after n seconds\n" \
+ "\n" \
+ "Some options may have no effect on some operating systems\n" \
+ "n may be =, indicating that soft limit should be set equal to hard limit"
+
+#define chroot_trivial_usage \
+ "NEWROOT [COMMAND...]"
+#define chroot_full_usage \
+ "Run COMMAND with root directory set to NEWROOT"
+#define chroot_example_usage \
+ "$ ls -l /bin/ls\n" \
+ "lrwxrwxrwx 1 root root 12 Apr 13 00:46 /bin/ls -> /BusyBox\n" \
+ "# mount /dev/hdc1 /mnt -t minix\n" \
+ "# chroot /mnt\n" \
+ "# ls -l /bin/ls\n" \
+ "-rwxr-xr-x 1 root root 40816 Feb 5 07:45 /bin/ls*\n"
+
+#define chvt_trivial_usage \
+ "N"
+#define chvt_full_usage \
+ "Change the foreground virtual terminal to /dev/ttyN"
+
+#define cksum_trivial_usage \
+ "FILES..."
+#define cksum_full_usage \
+ "Calculate the CRC32 checksums of FILES"
+
+#define clear_trivial_usage \
+ ""
+#define clear_full_usage \
+ "Clear screen"
+
+#define cmp_trivial_usage \
+ "[-l] [-s] FILE1 [FILE2" USE_DESKTOP(" [SKIP1 [SKIP2]") "]]"
+#define cmp_full_usage \
+ "Compares FILE1 vs stdin if FILE2 is not specified" \
+ "\n\nOptions:\n" \
+ " -l Write the byte numbers (decimal) and values (octal)\n" \
+ " for all differing bytes\n" \
+ " -s Quiet mode - do not print"
+
+#define comm_trivial_usage \
+ "[-123] FILE1 FILE2"
+#define comm_full_usage \
+ "Compare FILE1 to FILE2, or to stdin if - is specified" \
+ "\n\nOptions:\n" \
+ " -1 Suppress lines unique to FILE1\n" \
+ " -2 Suppress lines unique to FILE2\n" \
+ " -3 Suppress lines common to both files"
+
+#define bbconfig_trivial_usage \
+ ""
+#define bbconfig_full_usage \
+ "Print the config file which built busybox"
+
+#define bbsh_trivial_usage \
+ "[FILE]...\n" \
+ "or: bbsh -c command [args]..."
+#define bbsh_full_usage \
+ "The bbsh shell (command interpreter)"
+
+#define chrt_trivial_usage \
+ "[OPTION]... [prio] [pid | command [arg]...]"
+#define chrt_full_usage \
+ "manipulate real-time attributes of a process" \
+ "\n\nOptions:\n" \
+ " -p operate on pid\n" \
+ " -r set scheduling policy to SCHED_RR\n" \
+ " -f set scheduling policy to SCHED_FIFO\n" \
+ " -o set scheduling policy to SCHED_OTHER\n" \
+ " -m show min and max priorities"
+
+#define chrt_example_usage \
+ "$ chrt -r 4 sleep 900 ; x=$!\n" \
+ "$ chrt -f -p 3 $x\n" \
+ "You need CAP_SYS_NICE privileges to set scheduling attributes of a process"
+
+#define cp_trivial_usage \
+ "[OPTION]... SOURCE DEST"
+#define cp_full_usage \
+ "Copy SOURCE to DEST, or multiple SOURCE(s) to DIRECTORY" \
+ "\n\nOptions:" \
+ "\n -a Same as -dpR" \
+ USE_SELINUX( \
+ "\n -c Preserves security context" \
+ ) \
+ "\n -d,-P Preserve links" \
+ "\n -H,-L Dereference all symlinks (default)" \
+ "\n -p Preserve file attributes if possible" \
+ "\n -f Force overwrite" \
+ "\n -i Prompt before overwrite" \
+ "\n -R,-r Recurse directories" \
+ "\n -l,-s Create (sym)links"
+
+#define cpio_trivial_usage \
+ "-[dimtuv][F cpiofile]"
+#define cpio_full_usage \
+ "Extract or list files from a cpio archive\n" \
+ "Main operation mode:\n" \
+ " d Make leading directories\n" \
+ " i Extract\n" \
+ " m Preserve mtime\n" \
+ " t List\n" \
+ " v Verbose\n" \
+ " u Unconditional overwrite\n" \
+ " F Input from file"
+
+#define crond_trivial_usage \
+ "-d[#] -c <crondir> -f -b"
+#define crond_full_usage \
+ " -d [#] -l [#] -S -L logfile -f -b -c dir\n" \
+ " -d num Debug level\n" \
+ " -l num Log level (8 - default)\n" \
+ " -S Log to syslogd (default)\n" \
+ " -L file Log to file\n" \
+ " -f Run in foreground\n" \
+ " -b Run in background (default)\n" \
+ " -c dir Working dir"
+
+#define crontab_trivial_usage \
+ "[-c dir] {file|-}|[-u|-l|-e|-d user]"
+#define crontab_full_usage \
+ " File <opts> replace crontab from file\n" \
+ " - <opts> replace crontab from stdin\n" \
+ " -u user specify user\n" \
+ " -l [user] list crontab for user\n" \
+ " -e [user] edit crontab for user\n" \
+ " -d [user] delete crontab for user\n" \
+ " -c dir specify crontab directory"
+
+
+#define cut_trivial_usage \
+ "[OPTION]... [FILE]..."
+#define cut_full_usage \
+ "Print selected fields from each input FILE to standard output" \
+ "\n\nOptions:\n" \
+ " -b LIST Output only bytes from LIST\n" \
+ " -c LIST Output only characters from LIST\n" \
+ " -d CHAR Use CHAR instead of tab as the field delimiter\n" \
+ " -s Output only the lines containing delimiter\n" \
+ " -f N Print only these fields\n" \
+ " -n Ignored"
+#define cut_example_usage \
+ "$ echo \"Hello world\" | cut -f 1 -d ' '\n" \
+ "Hello\n" \
+ "$ echo \"Hello world\" | cut -f 2 -d ' '\n" \
+ "world\n"
+
+#define date_trivial_usage \
+ "[OPTION]... [MMDDhhmm[[CC]YY][.ss]] [+FORMAT]"
+#define date_full_usage \
+ "Display current time in the given FORMAT, or set system date" \
+ "\n\nOptions:\n" \
+ " -R Outputs RFC-822 compliant date string\n" \
+ " -d STRING Displays time described by STRING, not 'now'\n" \
+ USE_FEATURE_DATE_ISOFMT( \
+ " -I[TIMESPEC] Outputs an ISO-8601 compliant date/time string\n" \
+ " TIMESPEC='date' (or missing) for date only,\n" \
+ " 'hours', 'minutes', or 'seconds' for date and,\n" \
+ " time to the indicated precision\n" \
+ " -D hint Use 'hint' as date format, via strptime()\n" \
+ ) \
+ " -s Sets time described by STRING\n" \
+ " -r FILE Displays the last modification time of FILE\n" \
+ " -u Prints or sets Coordinated Universal Time"
+#define date_example_usage \
+ "$ date\n" \
+ "Wed Apr 12 18:52:41 MDT 2000\n"
+
+#define dc_trivial_usage \
+ "expression ..."
+#define dc_full_usage \
+ "This is a Tiny RPN calculator that understands the\n" \
+ "following operations: +, add, -, sub, *, mul, /, div, %, mod, " \
+ "**, exp, and, or, not, eor.\n" \
+ "For example: 'dc 2 2 add' -> 4, and 'dc 8 8 \\* 2 2 + /' -> 16." \
+ "\n\nOptions:\n" \
+ "p - Prints the value on the top of the stack, without altering the stack\n" \
+ "f - Prints the entire contents of the stack without altering anything\n" \
+ "o - Pops the value off the top of the stack and uses it to set the output radix\n" \
+ " Only 10 and 16 are supported"
+#define dc_example_usage \
+ "$ dc 2 2 + p\n" \
+ "4\n" \
+ "$ dc 8 8 \\* 2 2 + / p\n" \
+ "16\n" \
+ "$ dc 0 1 and p\n" \
+ "0\n" \
+ "$ dc 0 1 or p\n" \
+ "1\n" \
+ "$ echo 72 9 div 8 mul p | dc\n" \
+ "64\n"
+
+#define dd_trivial_usage \
+ "[if=FILE] [of=FILE] " USE_FEATURE_DD_IBS_OBS("[ibs=N] [obs=N] ") "[bs=N] [count=N] [skip=N]\n" \
+ " [seek=N]" USE_FEATURE_DD_IBS_OBS(" [conv=notrunc|noerror|sync]")
+#define dd_full_usage \
+ "Copy a file with converting and formatting" \
+ "\n\nOptions:\n" \
+ " if=FILE Read from FILE instead of stdin\n" \
+ " of=FILE Write to FILE instead of stdout\n" \
+ " bs=N Read and write N bytes at a time\n" \
+ USE_FEATURE_DD_IBS_OBS( \
+ " ibs=N Read N bytes at a time\n") \
+ USE_FEATURE_DD_IBS_OBS( \
+ " obs=N Write N bytes at a time\n") \
+ " count=N Copy only N input blocks\n" \
+ " skip=N Skip N input blocks\n" \
+ " seek=N Skip N output blocks\n" \
+ USE_FEATURE_DD_IBS_OBS( \
+ " conv=notrunc Don't truncate output file\n" \
+ " conv=noerror Continue after read errors\n" \
+ " conv=sync Pad blocks with zeros\n") \
+ "\n" \
+ "Numbers may be suffixed by c (x1), w (x2), b (x512), kD (x1000), k (x1024),\n" \
+ "MD (x1000000), M (x1048576), GD (x1000000000) or G (x1073741824)"
+#define dd_example_usage \
+ "$ dd if=/dev/zero of=/dev/ram1 bs=1M count=4\n" \
+ "4+0 records in\n" \
+ "4+0 records out\n"
+
+#define deallocvt_trivial_usage \
+ "[N]"
+#define deallocvt_full_usage \
+ "Deallocate unused virtual terminal /dev/ttyN"
+
+#define delgroup_trivial_usage \
+ "GROUP"
+#define delgroup_full_usage \
+ "Delete group GROUP from the system"
+
+#define deluser_trivial_usage \
+ "USER"
+#define deluser_full_usage \
+ "Delete user USER from the system"
+
+#define devfsd_trivial_usage \
+ "mntpnt [-v]" \
+ USE_DEVFSD_FG_NP("[-fg][-np]" )
+#define devfsd_full_usage \
+ "Manage devfs permissions and old device name symlinks" \
+ "\n\nOptions:" \
+ "\n mntpnt The mount point where devfs is mounted" \
+ "\n -v Print the protocol version numbers for devfsd" \
+ "\n and the kernel-side protocol version and exits" \
+ USE_DEVFSD_FG_NP( \
+ "\n -fg Run the daemon in the foreground" \
+ "\n -np Exit after parsing the configuration file" \
+ "\n and processing synthetic REGISTER events," \
+ "\n do not poll for events")
+
+#define df_trivial_usage \
+ "[-" USE_FEATURE_HUMAN_READABLE("hm") "k] [FILESYSTEM ...]"
+#define df_full_usage \
+ "Print the filesystem space used and space available" \
+ "\n\nOptions:\n" \
+ USE_FEATURE_HUMAN_READABLE( \
+ "\n -h Print sizes in human readable format (e.g., 1K 243M 2G )\n" \
+ " -m Print sizes in megabytes\n" \
+ " -k Print sizes in kilobytes(default)") \
+ SKIP_FEATURE_HUMAN_READABLE( \
+ "\n -k Ignored")
+#define df_example_usage \
+ "$ df\n" \
+ "Filesystem 1k-blocks Used Available Use% Mounted on\n" \
+ "/dev/sda3 8690864 8553540 137324 98% /\n" \
+ "/dev/sda1 64216 36364 27852 57% /boot\n" \
+ "$ df /dev/sda3\n" \
+ "Filesystem 1k-blocks Used Available Use% Mounted on\n" \
+ "/dev/sda3 8690864 8553540 137324 98% /\n"
+
+#define dhcprelay_trivial_usage \
+ "[client_device_list] [server_device]"
+#define dhcprelay_full_usage \
+ "Relay dhcp requests from client devices to server device"
+
+#define dhcprelay_trivial_usage \
+ "[client_device_list] [server_device]"
+#define dhcprelay_full_usage \
+ "Relay dhcp requests from client devices to server device"
+
+#define diff_trivial_usage \
+ "[-abdiNqrTstw] [-L LABEL] [-S FILE] [-U LINES] FILE1 FILE2"
+#define diff_full_usage \
+ "Compare files line by line and output the differences between them.\n" \
+ "This diff implementation only supports unified diffs." \
+ "\n\nOptions:\n" \
+ " -a Treat all files as text\n" \
+ " -b Ignore changes in the amount of whitespace\n" \
+ " -d Try hard to find a smaller set of changes\n" \
+ " -i Ignore case differences\n" \
+ " -L Use LABEL instead of the filename in the unified header\n" \
+ " -N Treat absent files as empty\n" \
+ " -q Output only whether files differ\n" \
+ " -r Recursively compare subdirectories\n" \
+ " -S Start with FILE when comparing directories\n" \
+ " -T Make tabs line up by prefixing a tab when necessary\n" \
+ " -s Report when two files are the same\n" \
+ " -t Expand tabs to spaces in output\n" \
+ " -U Output LINES lines of context\n" \
+ " -w Ignore all whitespace"
+
+#define dirname_trivial_usage \
+ "FILENAME"
+#define dirname_full_usage \
+ "Strip non-directory suffix from FILENAME"
+#define dirname_example_usage \
+ "$ dirname /tmp/foo\n" \
+ "/tmp\n" \
+ "$ dirname /tmp/foo/\n" \
+ "/tmp\n"
+
+#define dmesg_trivial_usage \
+ "[-c] [-n LEVEL] [-s SIZE]"
+#define dmesg_full_usage \
+ "Print or control the kernel ring buffer" \
+ "\n\nOptions:\n" \
+ " -c Clears the ring buffer's contents after printing\n" \
+ " -n LEVEL Sets console logging level\n" \
+ " -s SIZE Use a buffer of size SIZE"
+
+#define dnsd_trivial_usage \
+ "[-c config] [-t seconds] [-p port] [-i iface-ip] [-d]"
+#define dnsd_full_usage \
+ "Small and static DNS server daemon" \
+ "\n\nOptions:\n" \
+ " -c Config filename\n" \
+ " -t TTL in seconds\n" \
+ " -p Listening port\n" \
+ " -i Listening iface ip (default all)\n" \
+ " -d Daemonize"
+
+#define dos2unix_trivial_usage \
+ "[option] [FILE]"
+#define dos2unix_full_usage \
+ "Convert FILE from dos format to unix format. When no option\n" \
+ "is given, the input is converted to the opposite output format.\n" \
+ "When no file is given, use stdin for input and stdout for output." \
+ "\n\nOptions:\n" \
+ " -u Output will be in UNIX format\n" \
+ " -d Output will be in DOS format"
+
+#define dpkg_trivial_usage \
+ "[-ilCPru] [-F option] package_name"
+#define dpkg_full_usage \
+ "Install, remove and manage Debian packages" \
+ "\n\nOptions:\n" \
+ " -i Install the package\n" \
+ " -l List of installed packages\n" \
+ " -C Configure an unpackaged package\n" \
+ " -F depends Ignore dependency problems\n" \
+ " -P Purge all files of a package\n" \
+ " -r Remove all but the configuration files for a package\n" \
+ " -u Unpack a package, but don't configure it"
+
+#define dpkg_deb_trivial_usage \
+ "[-cefxX] FILE [argument]"
+#define dpkg_deb_full_usage \
+ "Perform actions on Debian packages (.debs)" \
+ "\n\nOptions:\n" \
+ " -c List contents of filesystem tree\n" \
+ " -e Extract control files to [argument] directory\n" \
+ " -f Display control field name starting with [argument]\n" \
+ " -x Extract packages filesystem tree to directory\n" \
+ " -X Verbose extract"
+#define dpkg_deb_example_usage \
+ "$ dpkg-deb -X ./busybox_0.48-1_i386.deb /tmp\n"
+
+#define du_trivial_usage \
+ "[-aHLdclsx" USE_FEATURE_HUMAN_READABLE("hm") "k] [FILE]..."
+#define du_full_usage \
+ "Summarize disk space used for each FILE and/or directory.\n" \
+ "Disk space is printed in units of " \
+ USE_FEATURE_DU_DEFAULT_BLOCKSIZE_1K("1024") \
+ SKIP_FEATURE_DU_DEFAULT_BLOCKSIZE_1K("512") \
+ " bytes." \
+ "\n\nOptions:\n" \
+ " -a Show sizes of files in addition to directories\n" \
+ " -H Follow symlinks that are FILE command line args\n" \
+ " -L Follow all symlinks encountered\n" \
+ " -d N Limit output to directories (and files with -a) of depth < N\n" \
+ " -c Output a grand total\n" \
+ " -l Count sizes many times if hard linked\n" \
+ " -s Display only a total for each argument\n" \
+ " -x Skip directories on different filesystems\n" \
+ USE_FEATURE_HUMAN_READABLE( \
+ " -h Print sizes in human readable format (e.g., 1K 243M 2G )\n" \
+ " -m Print sizes in megabytes\n" \
+ ) \
+ " -k Print sizes in kilobytes" \
+ USE_FEATURE_DU_DEFAULT_BLOCKSIZE_1K("(default)")
+#define du_example_usage \
+ "$ du\n" \
+ "16 ./CVS\n" \
+ "12 ./kernel-patches/CVS\n" \
+ "80 ./kernel-patches\n" \
+ "12 ./tests/CVS\n" \
+ "36 ./tests\n" \
+ "12 ./scripts/CVS\n" \
+ "16 ./scripts\n" \
+ "12 ./docs/CVS\n" \
+ "104 ./docs\n" \
+ "2417 .\n"
+
+#define dumpkmap_trivial_usage \
+ "> keymap"
+#define dumpkmap_full_usage \
+ "Print out a binary keyboard translation table to standard output"
+#define dumpkmap_example_usage \
+ "$ dumpkmap > keymap\n"
+
+#define dumpleases_trivial_usage \
+ "[-r|-a] [-f LEASEFILE]"
+#define dumpleases_full_usage \
+ "Display DHCP leases granted by udhcpd" \
+ "\n\nOptions:\n" \
+ " -f, --file=FILENAME Leases file to load\n" \
+ " -r, --remaining Interpret lease times as time remaining\n" \
+ " -a, --absolute Interpret lease times as expire time"
+
+#define e2fsck_trivial_usage \
+ "[-panyrcdfvstDFSV] [-b superblock] [-B blocksize] " \
+ "[-I inode_buffer_blocks] [-P process_inode_size] " \
+ "[-l|-L bad_blocks_file] [-C fd] [-j external_journal] " \
+ "[-E extended-options] device"
+#define e2fsck_full_usage \
+ "Check ext2/ext3 file system" \
+ "\n\nOptions:\n" \
+ " -p Automatic repair (no questions)\n" \
+ " -n Make no changes to the filesystem\n" \
+ " -y Assume 'yes' to all questions\n" \
+ " -c Check for bad blocks and add them to the badblock list\n" \
+ " -f Force checking even if filesystem is marked clean\n" \
+ " -v Be verbose\n" \
+ " -b superblock Use alternative superblock\n" \
+ " -B blocksize Force blocksize when looking for superblock\n" \
+ " -j journal Set location of the external journal\n" \
+ " -l file Add to badblocks list\n" \
+ " -L file Set badblocks list"
+
+#define echo_trivial_usage \
+ USE_FEATURE_FANCY_ECHO("[-neE] ") "[ARG ...]"
+#define echo_full_usage \
+ "Print the specified ARGs to stdout" \
+ USE_FEATURE_FANCY_ECHO( \
+ "\n\nOptions:\n" \
+ " -n Suppress trailing newline\n" \
+ " -e Interpret backslash-escaped characters (i.e., \\t=tab)\n" \
+ " -E Disable interpretation of backslash-escaped characters" \
+ )
+#define echo_example_usage \
+ "$ echo \"Erik is cool\"\n" \
+ "Erik is cool\n" \
+ USE_FEATURE_FANCY_ECHO("$ echo -e \"Erik\\nis\\ncool\"\n" \
+ "Erik\n" \
+ "is\n" \
+ "cool\n" \
+ "$ echo \"Erik\\nis\\ncool\"\n" \
+ "Erik\\nis\\ncool\n")
+
+#define eject_trivial_usage \
+ "[-t] [-T] [DEVICE]"
+#define eject_full_usage \
+ "Eject specified DEVICE (or default /dev/cdrom)" \
+ "\n\nOptions:\n" \
+ " -t Close tray\n" \
+ " -T Open/close tray (toggle)"
+
+#define ed_trivial_usage ""
+#define ed_full_usage ""
+
+#define env_trivial_usage \
+ "[-iu] [-] [name=value]... [command]"
+#define env_full_usage \
+ "Print the current environment or run a program after setting\n" \
+ "up the specified environment" \
+ "\n\nOptions:\n" \
+ " -, -i Start with an empty environment\n" \
+ " -u Remove variable from the environment"
+
+#define ether_wake_trivial_usage \
+ "[-b] [-i iface] [-p aa:bb:cc:dd[:ee:ff]] MAC"
+#define ether_wake_full_usage \
+ "Send a magic packet to wake up sleeping machines.\n" \
+ "MAC must be a station address (00:11:22:33:44:55) or\n" \
+ "a hostname with a known 'ethers' entry." \
+ "\n\nOptions:\n" \
+ " -b Send wake-up packet to the broadcast address\n" \
+ " -i iface Use interface ifname instead of the default \"eth0\"\n" \
+ " -p pass Append the four or six byte password PW to the packet"
+
+#define expr_trivial_usage \
+ "EXPRESSION"
+#define expr_full_usage \
+ "Print the value of EXPRESSION to standard output.\n\n" \
+ "EXPRESSION may be:\n" \
+ " ARG1 | ARG2 ARG1 if it is neither null nor 0, otherwise ARG2\n" \
+ " ARG1 & ARG2 ARG1 if neither argument is null or 0, otherwise 0\n" \
+ " ARG1 < ARG2 ARG1 is less than ARG2\n" \
+ " ARG1 <= ARG2 ARG1 is less than or equal to ARG2\n" \
+ " ARG1 = ARG2 ARG1 is equal to ARG2\n" \
+ " ARG1 != ARG2 ARG1 is unequal to ARG2\n" \
+ " ARG1 >= ARG2 ARG1 is greater than or equal to ARG2\n" \
+ " ARG1 > ARG2 ARG1 is greater than ARG2\n" \
+ " ARG1 + ARG2 Sum of ARG1 and ARG2\n" \
+ " ARG1 - ARG2 Difference of ARG1 and ARG2\n" \
+ " ARG1 * ARG2 Product of ARG1 and ARG2\n" \
+ " ARG1 / ARG2 Quotient of ARG1 divided by ARG2\n" \
+ " ARG1 % ARG2 Remainder of ARG1 divided by ARG2\n" \
+ " STRING : REGEXP Anchored pattern match of REGEXP in STRING\n" \
+ " match STRING REGEXP Same as STRING : REGEXP\n" \
+ " substr STRING POS LENGTH Substring of STRING, POS counted from 1\n" \
+ " index STRING CHARS Index in STRING where any CHARS is found, or 0\n" \
+ " length STRING Length of STRING\n" \
+ " quote TOKEN Interpret TOKEN as a string, even if\n" \
+ " it is a keyword like 'match' or an\n" \
+ " operator like '/'\n" \
+ " (EXPRESSION) Value of EXPRESSION\n\n" \
+ "Beware that many operators need to be escaped or quoted for shells.\n" \
+ "Comparisons are arithmetic if both ARGs are numbers, else\n" \
+ "lexicographical. Pattern matches return the string matched between\n" \
+ "\\( and \\) or null; if \\( and \\) are not used, they return the number\n" \
+ "of characters matched or 0."
+
+#define fakeidentd_trivial_usage \
+ "[-fiw] [-b ADDR] [STRING]"
+#define fakeidentd_full_usage \
+ "Provide fake ident (auth) service" \
+ "\n\nOptions:" \
+ "\n -f Run in foreground" \
+ "\n -i Inetd mode" \
+ "\n -w Inetd 'wait' mode" \
+ "\n -b ADDR Bind to specified address" \
+ "\n STRING Ident answer string (default is 'nobody')"
+
+#define false_trivial_usage \
+ ""
+#define false_full_usage \
+ "Return an exit code of FALSE (1)"
+#define false_example_usage \
+ "$ false\n" \
+ "$ echo $?\n" \
+ "1\n"
+
+#define fbset_trivial_usage \
+ "[options] [mode]"
+#define fbset_full_usage \
+ "Show and modify frame buffer settings"
+#define fbset_example_usage \
+ "$ fbset\n" \
+ "mode \"1024x768-76\"\n" \
+ " # D: 78.653 MHz, H: 59.949 kHz, V: 75.694 Hz\n" \
+ " geometry 1024 768 1024 768 16\n" \
+ " timings 12714 128 32 16 4 128 4\n" \
+ " accel false\n" \
+ " rgba 5/11,6/5,5/0,0/0\n" \
+ "endmode\n"
+
+#define fdflush_trivial_usage \
+ "DEVICE"
+#define fdflush_full_usage \
+ "Force floppy disk drive to detect disk change"
+
+#define fdformat_trivial_usage \
+ "[-n] DEVICE"
+#define fdformat_full_usage \
+ "Format floppy disk" \
+ "\n\nOptions:\n" \
+ " -n Don't verify after format"
+
+#define fdisk_trivial_usage \
+ "[-luv] [-C CYLINDERS] [-H HEADS] [-S SECTORS] [-b SSZ] DISK"
+#define fdisk_full_usage \
+ "Change partition table" \
+ "\n\nOptions:\n" \
+ " -l List partition table(s)\n" \
+ " -u Give Start and End in sector (instead of cylinder) units\n" \
+ " -s PARTITION Give partition size(s) in blocks\n" \
+ " -b 2048 (for certain MO disks) use 2048-byte sectors\n" \
+ " -C CYLINDERS Set the number of cylinders\n" \
+ " -H HEADS Set the number of heads\n" \
+ " -S SECTORS Set the number of sectors\n" \
+ " -v Give fdisk version"
+
+#define find_trivial_usage \
+ "[PATH...] [EXPRESSION]"
+#define find_full_usage \
+ "Search for files in a directory hierarchy. The default PATH is\n" \
+ "the current directory; default EXPRESSION is '-print'\n" \
+ "\nEXPRESSION may consist of:\n" \
+ " -follow Dereference symlinks\n" \
+ " -name PATTERN File name (leading directories removed) matches PATTERN\n" \
+ " -print Print (default and assumed)" \
+ USE_FEATURE_FIND_PRINT0( \
+ "\n -print0 Delimit output with null characters rather than" \
+ "\n newlines" \
+ ) USE_FEATURE_FIND_TYPE( \
+ "\n -type X Filetype matches X (where X is one of: f,d,l,b,c,...)" \
+ ) USE_FEATURE_FIND_PERM( \
+ "\n -perm PERMS Permissions match any of (+NNN); all of (-NNN);" \
+ "\n or exactly (NNN)" \
+ ) USE_FEATURE_FIND_MTIME( \
+ "\n -mtime DAYS Modified time is greater than (+N); less than (-N);" \
+ "\n Or exactly (N) days" \
+ ) USE_FEATURE_FIND_MMIN( \
+ "\n -mmin MINS Modified time is greater than (+N); less than (-N);" \
+ "\n or exactly (N) minutes" \
+ ) USE_FEATURE_FIND_NEWER( \
+ "\n -newer FILE Modified time is more recent than FILE's" \
+ ) USE_FEATURE_FIND_INUM( \
+ "\n -inum N File has inode number N" \
+ ) USE_FEATURE_FIND_EXEC( \
+ "\n -exec CMD Execute CMD with all instances of {} replaced by the" \
+ "\n files matching EXPRESSION" \
+ ) USE_DESKTOP( \
+ "\n -size N File size is N" \
+ "\n -prune Stop traversing current subtree" \
+ "\n (expr) Group" \
+ )
+
+#define find_example_usage \
+ "$ find / -name passwd\n" \
+ "/etc/passwd\n"
+
+#define fold_trivial_usage \
+ "[-bs] [-w WIDTH] [FILE]"
+#define fold_full_usage \
+ "Wrap input lines in each FILE (standard input by default), writing to\n" \
+ "standard output" \
+ "\n\nOptions:\n" \
+ " -b Count bytes rather than columns\n" \
+ " -s Break at spaces\n" \
+ " -w Use WIDTH columns instead of 80"
+
+#define free_trivial_usage \
+ ""
+#define free_full_usage \
+ "Display the amount of free and used system memory"
+#define free_example_usage \
+ "$ free\n" \
+ " total used free shared buffers\n" \
+ " Mem: 257628 248724 8904 59644 93124\n" \
+ " Swap: 128516 8404 120112\n" \
+ "Total: 386144 257128 129016\n" \
+
+#define freeramdisk_trivial_usage \
+ "DEVICE"
+#define freeramdisk_full_usage \
+ "Free all memory used by the specified ramdisk"
+#define freeramdisk_example_usage \
+ "$ freeramdisk /dev/ram2\n"
+
+#define fsck_trivial_usage \
+ "[-ANPRTV] [ -C fd ] [-t fstype] [fs-options] [filesys ...]"
+#define fsck_full_usage \
+ "Check and repair filesystems" \
+ "\n\nOptions:\n" \
+ " -A Walk /etc/fstab and check all filesystems\n" \
+ " -N Don't execute, just show what would be done\n" \
+ " -P When using -A, check filesystems in parallel\n" \
+ " -R When using -A, skip the root filesystem\n" \
+ " -T Don't show title on startup\n" \
+ " -V Verbose\n" \
+ " -C n Write status information to specified filedescriptor\n" \
+ " -t type List of filesystem types to check"
+
+#define fsck_minix_trivial_usage \
+ "[-larvsmf] /dev/name"
+#define fsck_minix_full_usage \
+ "Perform a consistency check for MINIX filesystems" \
+ "\n\nOptions:\n" \
+ " -l Lists all filenames\n" \
+ " -r Perform interactive repairs\n" \
+ " -a Perform automatic repairs\n" \
+ " -v Verbose\n" \
+ " -s Outputs super-block information\n" \
+ " -m Activates MINIX-like \"mode not cleared\" warnings\n" \
+ " -f Force file system check"
+
+#define ftpget_trivial_usage \
+ "[options] remote-host local-file remote-file"
+#define ftpget_full_usage \
+ "Retrieve a remote file via FTP" \
+ "\n\nOptions:\n" \
+ " -c, --continue Continue a previous transfer\n" \
+ " -v, --verbose Verbose\n" \
+ " -u, --username Username to be used\n" \
+ " -p, --password Password to be used\n" \
+ " -P, --port Port number to be used"
+
+#define ftpput_trivial_usage \
+ "[options] remote-host remote-file local-file"
+#define ftpput_full_usage \
+ "Store a local file on a remote machine via FTP" \
+ "\n\nOptions:\n" \
+ " -v, --verbose Verbose\n" \
+ " -u, --username Username to be used\n" \
+ " -p, --password Password to be used\n" \
+ " -P, --port Port number to be used"
+
+#define fuser_trivial_usage \
+ "[options] file OR port/proto"
+#define fuser_full_usage \
+ "Options:\n" \
+ " -m Show all processes on the same mounted fs\n" \
+ " -k Kill all processes that match\n" \
+ " -s Don't print or kill anything\n" \
+ " -4 When using port/proto only search IPv4 space\n" \
+ " -6 When using port/proto only search IPv6 space\n" \
+ " -SIGNAL When used with -k, this signal will be used to kill"
+
+#define getenforce_trivial_usage
+#define getenforce_full_usage
+
+#define getopt_trivial_usage \
+ "[OPTIONS]..."
+#define getopt_full_usage \
+ "Parse command options\n" \
+ " -a, --alternative Allow long options starting with single -\n" \
+ " -l, --longoptions=longopts Long options to be recognized\n" \
+ " -n, --name=progname The name under which errors are reported\n" \
+ " -o, --options=optstring Short options to be recognized\n" \
+ " -q, --quiet Disable error reporting by getopt(3)\n" \
+ " -Q, --quiet-output No normal output\n" \
+ " -s, --shell=shell Set shell quoting conventions\n" \
+ " -T, --test Test for getopt(1) version\n" \
+ " -u, --unquoted Do not quote the output"
+#define getopt_example_usage \
+ "$ cat getopt.test\n" \
+ "#!/bin/sh\n" \
+ "GETOPT=`getopt -o ab:c:: --long a-long,b-long:,c-long:: \\\n" \
+ " -n 'example.busybox' -- \"$@\"`\n" \
+ "if [ $? != 0 ] ; then exit 1 ; fi\n" \
+ "eval set -- \"$GETOPT\"\n" \
+ "while true ; do\n" \
+ " case $1 in\n" \
+ " -a|--a-long) echo \"Option a\" ; shift ;;\n" \
+ " -b|--b-long) echo \"Option b, argument '$2'\" ; shift 2 ;;\n" \
+ " -c|--c-long)\n" \
+ " case \"$2\" in\n" \
+ " \"\") echo \"Option c, no argument\"; shift 2 ;;\n" \
+ " *) echo \"Option c, argument '$2'\" ; shift 2 ;;\n" \
+ " esac ;;\n" \
+ " --) shift ; break ;;\n" \
+ " *) echo \"Internal error!\" ; exit 1 ;;\n" \
+ " esac\n" \
+ "done\n"
+
+#define getsebool_trivial_usage \
+ "-a or getsebool boolean..."
+#define getsebool_full_usage \
+ " -a Show all SELinux booleans"
+
+#define getty_trivial_usage \
+ "[OPTIONS]... baud_rate,... line [termtype]"
+#define getty_full_usage \
+ "Open a tty, prompt for a login name, then invoke /bin/login" \
+ "\n\nOptions:\n" \
+ " -h Enable hardware (RTS/CTS) flow control\n" \
+ " -i Do not display /etc/issue before running login\n" \
+ " -L Local line, so do not do carrier detect\n" \
+ " -m Get baud rate from modem's CONNECT status message\n" \
+ " -w Wait for a CR or LF before sending /etc/issue\n" \
+ " -n Do not prompt the user for a login name\n" \
+ " -f issue_file Display issue_file instead of /etc/issue\n" \
+ " -l login_app Invoke login_app instead of /bin/login\n" \
+ " -t timeout Terminate after timeout if no username is read\n" \
+ " -I initstring Sets the init string to send before anything else\n" \
+ " -H login_host Log login_host into the utmp file as the hostname"
+
+#define grep_trivial_usage \
+ "[-HhrilLnqvso" \
+ USE_DESKTOP("w") \
+ "eF" \
+ USE_FEATURE_GREP_EGREP_ALIAS("E") \
+ USE_FEATURE_GREP_CONTEXT("ABC") \
+ "] PATTERN [FILEs...]"
+#define grep_full_usage \
+ "Search for PATTERN in each FILE or standard input" \
+ "\n\nOptions:" \
+ "\n -H Prefix output lines with filename where match was found" \
+ "\n -h Suppress the prefixing filename on output" \
+ "\n -r Recurse subdirectories" \
+ "\n -i Ignore case distinctions" \
+ "\n -l List names of files that match" \
+ "\n -L List names of files that do not match" \
+ "\n -n Print line number with output lines" \
+ "\n -q Be quiet. Returns 0 if PATTERN was found, 1 otherwise" \
+ "\n -v Select non-matching lines" \
+ "\n -s Suppress file open/read error messages" \
+ "\n -c Only print count of matching lines" \
+ "\n -f Read PATTERN from file" \
+ "\n -o Show only the part of a line that matches PATTERN" \
+ USE_DESKTOP( \
+ "\n -w Match whole words only") \
+ "\n -e PATTERN is a regular expression" \
+ "\n -F PATTERN is a set of newline-separated strings" \
+ USE_FEATURE_GREP_EGREP_ALIAS( \
+ "\n -E PATTERN is an extended regular expression") \
+ USE_FEATURE_GREP_CONTEXT( \
+ "\n -A Print NUM lines of trailing context" \
+ "\n -B Print NUM lines of leading context" \
+ "\n -C Print NUM lines of output context") \
+
+#define grep_example_usage \
+ "$ grep root /etc/passwd\n" \
+ "root:x:0:0:root:/root:/bin/bash\n" \
+ "$ grep ^[rR]oo. /etc/passwd\n" \
+ "root:x:0:0:root:/root:/bin/bash\n"
+
+#define gunzip_trivial_usage \
+ "[OPTION]... FILE"
+#define gunzip_full_usage \
+ "Uncompress FILE (or standard input if FILE is '-')" \
+ "\n\nOptions:\n" \
+ " -c Write output to standard output\n" \
+ " -f Force read when source is a terminal\n" \
+ " -t Test compressed file integrity"
+#define gunzip_example_usage \
+ "$ ls -la /tmp/BusyBox*\n" \
+ "-rw-rw-r-- 1 andersen andersen 557009 Apr 11 10:55 /tmp/BusyBox-0.43.tar.gz\n" \
+ "$ gunzip /tmp/BusyBox-0.43.tar.gz\n" \
+ "$ ls -la /tmp/BusyBox*\n" \
+ "-rw-rw-r-- 1 andersen andersen 1761280 Apr 14 17:47 /tmp/BusyBox-0.43.tar\n"
+
+#define gzip_trivial_usage \
+ "[OPTION]... [FILE]..."
+#define gzip_full_usage \
+ "Compress FILE(s) with maximum compression.\n" \
+ "When FILE is '-' or unspecified, reads standard input. Implies -c." \
+ "\n\nOptions:\n" \
+ " -c Write output to standard output instead of FILE.gz\n" \
+ " -d Decompress\n" \
+ " -f Force write when destination is a terminal"
+#define gzip_example_usage \
+ "$ ls -la /tmp/busybox*\n" \
+ "-rw-rw-r-- 1 andersen andersen 1761280 Apr 14 17:47 /tmp/busybox.tar\n" \
+ "$ gzip /tmp/busybox.tar\n" \
+ "$ ls -la /tmp/busybox*\n" \
+ "-rw-rw-r-- 1 andersen andersen 554058 Apr 14 17:49 /tmp/busybox.tar.gz\n"
+
+#define halt_trivial_usage \
+ "[-d<delay>] [-n<nosync>] [-f<force>]"
+#define halt_full_usage \
+ "Halt the system" \
+ "\n\nOptions:\n" \
+ " -d Delay interval for halting\n" \
+ " -n No call to sync()\n" \
+ " -f Force halt (don't go through init)"
+
+#define hdparm_trivial_usage \
+ "[options] [device] .."
+#define hdparm_full_usage \
+ USE_FEATURE_HDPARM_GET_IDENTITY( \
+ "If no device name is specified try to read from stdin.\n\n") \
+ "Options:\n" \
+ " -a Get/set fs readahead\n" \
+ " -A Set drive read-lookahead flag (0/1)\n" \
+ " -b Get/set bus state (0 == off, 1 == on, 2 == tristate)\n" \
+ " -B Set Advanced Power Management setting (1-255)\n" \
+ " -c Get/set IDE 32-bit IO setting\n" \
+ " -C Check IDE power mode status\n" \
+ USE_FEATURE_HDPARM_HDIO_GETSET_DMA( \
+ " -d Get/set using_dma flag\n") \
+ " -D Enable/disable drive defect-mgmt\n" \
+ " -f Flush buffer cache for device on exit\n" \
+ " -g Display drive geometry\n" \
+ " -h Display terse usage information\n" \
+ USE_FEATURE_HDPARM_GET_IDENTITY( \
+ " -i Display drive identification\n") \
+ USE_FEATURE_HDPARM_GET_IDENTITY( \
+ " -I Detailed/current information directly from drive\n") \
+ " -k Get/set keep_settings_over_reset flag (0/1)\n" \
+ " -K Set drive keep_features_over_reset flag (0/1)\n" \
+ " -L Set drive doorlock (0/1) (removable harddisks only)\n" \
+ " -m Get/set multiple sector count\n" \
+ " -n Get/set ignore-write-errors flag (0/1)\n" \
+ " -p Set PIO mode on IDE interface chipset (0,1,2,3,4,...)\n" \
+ " -P Set drive prefetch count\n" \
+ " -q Change next setting quietly\n" \
+ " -Q Get/set DMA tagged-queuing depth (if supported)\n" \
+ " -r Get/set readonly flag (DANGEROUS to set)\n" \
+ USE_FEATURE_HDPARM_HDIO_SCAN_HWIF( \
+ " -R Register an IDE interface (DANGEROUS)\n") \
+ " -S Set standby (spindown) timeout\n" \
+ " -t Perform device read timings\n" \
+ " -T Perform cache read timings\n" \
+ " -u Get/set unmaskirq flag (0/1)\n" \
+ USE_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF( \
+ " -U Un-register an IDE interface (DANGEROUS)\n") \
+ " -v Defaults; same as -mcudkrag for IDE drives\n" \
+ " -V Display program version and exit immediately\n" \
+ USE_FEATURE_HDPARM_HDIO_DRIVE_RESET( \
+ " -w Perform device reset (DANGEROUS)\n") \
+ " -W Set drive write-caching flag (0/1) (DANGEROUS)\n" \
+ USE_FEATURE_HDPARM_HDIO_TRISTATE_HWIF( \
+ " -x Tristate device for hotswap (0/1) (DANGEROUS)\n") \
+ " -X Set IDE xfer mode (DANGEROUS)\n" \
+ " -y Put IDE drive in standby mode\n" \
+ " -Y Put IDE drive to sleep\n" \
+ " -Z Disable Seagate auto-powersaving mode\n" \
+ " -z Re-read partition table"
+
+#define head_trivial_usage \
+ "[OPTION]... [FILE]..."
+#define head_full_usage \
+ "Print first 10 lines of each FILE to standard output.\n" \
+ "With more than one FILE, precede each with a header giving the\n" \
+ "file name. With no FILE, or when FILE is -, read standard input." \
+ "\n\nOptions:" \
+ "\n -n NUM Print first NUM lines instead of first 10" \
+ USE_FEATURE_FANCY_HEAD( \
+ "\n -c NUM Output the first NUM bytes" \
+ "\n -q Never output headers giving file names" \
+ "\n -v Always output headers giving file names")
+#define head_example_usage \
+ "$ head -n 2 /etc/passwd\n" \
+ "root:x:0:0:root:/root:/bin/bash\n" \
+ "daemon:x:1:1:daemon:/usr/sbin:/bin/sh\n"
+
+#define hexdump_trivial_usage \
+ "[-[bcCdefnosvx]] [OPTION] FILE"
+#define hexdump_full_usage \
+ "Display file(s) or standard input in a user specified format" \
+ "\n\nOptions:\n" \
+ " -b One-byte octal display\n" \
+ " -c One-byte character display\n" \
+ " -C Canonical hex+ASCII, 16 bytes per line\n" \
+ " -d Two-byte decimal display\n" \
+ " -e FORMAT STRING\n" \
+ " -f FORMAT FILE\n" \
+ " -n LENGTH Interpret only length bytes of input\n" \
+ " -o Two-byte octal display\n" \
+ " -s OFFSET Skip offset bytes\n" \
+ " -v Display all input data\n" \
+ " -x Two-byte hexadecimal display"
+
+#define hostid_trivial_usage \
+ ""
+#define hostid_full_usage \
+ "Print out a unique 32-bit identifier for the machine"
+
+#define hostname_trivial_usage \
+ "[OPTION] {hostname | -F FILE}"
+#define hostname_full_usage \
+ "Get or set the hostname or DNS domain name. If a hostname is given\n" \
+ "(or FILE with the -F parameter), the host name will be set." \
+ "\n\nOptions:\n" \
+ " -s Short\n" \
+ " -i Addresses for the hostname\n" \
+ " -d DNS domain name\n" \
+ " -f Fully qualified domain name\n" \
+ " -F FILE Use the contents of FILE to specify the hostname"
+#define hostname_example_usage \
+ "$ hostname\n" \
+ "sage\n"
+
+#define httpd_trivial_usage \
+ "[-c <conf file>]" \
+ " [-p <port>]" \
+ " [-i] [-f]" \
+ USE_FEATURE_HTTPD_SETUID(" [-u user[:grp]]") \
+ USE_FEATURE_HTTPD_BASIC_AUTH(" [-r <realm>]") \
+ USE_FEATURE_HTTPD_AUTH_MD5(" [-m pass]") \
+ " [-h home]" \
+ " [-d/-e <string>]"
+#define httpd_full_usage \
+ "Listen for incoming http server requests" \
+ "\n\nOptions:\n" \
+ " -c FILE Specifies configuration file. (default httpd.conf)\n" \
+ " -p PORT Server port (default 80)\n" \
+ " -i Assume that we are started from inetd\n" \
+ " -f Do not daemonize\n" \
+ USE_FEATURE_HTTPD_SETUID( \
+ " -u USER[:GRP] Set uid/gid after binding to port\n") \
+ USE_FEATURE_HTTPD_BASIC_AUTH( \
+ " -r REALM Authentication Realm for Basic Authentication\n") \
+ USE_FEATURE_HTTPD_AUTH_MD5( \
+ " -m PASS Crypt PASS with md5 algorithm\n") \
+ " -h HOME Specifies http HOME directory (default ./)\n" \
+ " -e STRING HTML encode STRING\n" \
+ " -d STRING URL decode STRING"
+
+#define hwclock_trivial_usage \
+ "[-r|--show] [-s|--hctosys] [-w|--systohc]" \
+ " [-l|--localtime] [-u|--utc]" \
+ " [-f FILE]"
+#define hwclock_full_usage \
+ "Query and set a hardware clock (RTC)" \
+ "\n\nOptions:\n" \
+ " -r Read hardware clock and print result\n" \
+ " -s Set the system time from the hardware clock\n" \
+ " -w Set the hardware clock to the current system time\n" \
+ " -u The hardware clock is kept in coordinated universal time\n" \
+ " -l The hardware clock is kept in local time\n" \
+ " -f FILE Use the specified clock (e.g. /dev/rtc2)"
+
+#define id_trivial_usage \
+ "[OPTIONS]... [USERNAME]"
+#define id_full_usage \
+ "Print information for USERNAME or the current user" \
+ "\n\nOptions:\n" \
+ USE_SELINUX( \
+ " -Z prints only the security context\n" \
+ ) \
+ " -g Prints only the group ID\n" \
+ " -u Prints only the user ID\n" \
+ " -n Print a name instead of a number\n" \
+ " -r Prints the real user ID instead of the effective ID"
+#define id_example_usage \
+ "$ id\n" \
+ "uid=1000(andersen) gid=1000(andersen)\n"
+
+#define ifconfig_trivial_usage \
+ USE_FEATURE_IFCONFIG_STATUS("[-a]") " <interface> [<address>]"
+#define ifconfig_full_usage \
+ "Configure a network interface" \
+ "\n\nOptions:\n" \
+ USE_FEATURE_IPV6( \
+ " [add <address>[/<prefixlen>]]\n") \
+ USE_FEATURE_IPV6( \
+ " [del <address>[/<prefixlen>]]\n") \
+ " [[-]broadcast [<address>]] [[-]pointopoint [<address>]]\n" \
+ " [netmask <address>] [dstaddr <address>]\n" \
+ USE_FEATURE_IFCONFIG_SLIP( \
+ " [outfill <NN>] [keepalive <NN>]\n") \
+ " " USE_FEATURE_IFCONFIG_HW("[hw ether <address>] ") "[metric <NN>] [mtu <NN>]\n" \
+ " [[-]trailers] [[-]arp] [[-]allmulti]\n" \
+ " [multicast] [[-]promisc] [txqueuelen <NN>] [[-]dynamic]\n" \
+ USE_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ( \
+ " [mem_start <NN>] [io_addr <NN>] [irq <NN>]\n") \
+ " [up|down] ..."
+
+#define ifup_trivial_usage \
+ "<-ahinv> <ifaces...>"
+#define ifup_full_usage \
+ "Options:\n" \
+ " -a De/configure all interfaces automatically\n" \
+ " -i FILE Use FILE for interface definitions\n" \
+ " -n Print out what would happen, but don't do it\n" \
+ " (note that this option doesn't disable mappings)\n" \
+ " -v Print out what would happen before doing it\n" \
+ " -m Don't run any mappings\n" \
+ " -f Force de/configuration"
+
+#define ifdown_trivial_usage \
+ "<-ahinv> <ifaces...>"
+#define ifdown_full_usage \
+ "Options:\n" \
+ " -a De/configure all interfaces automatically\n" \
+ " -i FILE Use FILE for interface definitions\n" \
+ " -n Print out what would happen, but don't do it\n" \
+ " (note that this option doesn't disable mappings)\n" \
+ " -v Print out what would happen before doing it\n" \
+ " -m Don't run any mappings\n" \
+ " -f Force de/configuration"
+
+#define inetd_trivial_usage \
+ "[-f] [-q len] [conf]"
+#define inetd_full_usage \
+ "Listen for network connections and launch programs" \
+ "\n\nOptions:\n" \
+ " -f Run in foreground\n" \
+ " -q N Set the size of the socket listen queue to N\n" \
+ " (default: 128)"
+
+#define init_trivial_usage \
+ ""
+#define init_full_usage \
+ "Init is the parent of all processes"
+#define init_notes_usage \
+"This version of init is designed to be run only by the kernel.\n" \
+"\n" \
+"BusyBox init doesn't support multiple runlevels. The runlevels field of\n" \
+"the /etc/inittab file is completely ignored by BusyBox init. If you want\n" \
+"runlevels, use sysvinit.\n" \
+"\n" \
+"BusyBox init works just fine without an inittab. If no inittab is found,\n" \
+"it has the following default behavior:\n" \
+"\n" \
+" ::sysinit:/etc/init.d/rcS\n" \
+" ::askfirst:/bin/sh\n" \
+" ::ctrlaltdel:/sbin/reboot\n" \
+" ::shutdown:/sbin/swapoff -a\n" \
+" ::shutdown:/bin/umount -a -r\n" \
+" ::restart:/sbin/init\n" \
+"\n" \
+"if it detects that /dev/console is _not_ a serial console, it will also run:\n" \
+"\n" \
+" tty2::askfirst:/bin/sh\n" \
+" tty3::askfirst:/bin/sh\n" \
+" tty4::askfirst:/bin/sh\n" \
+"\n" \
+"If you choose to use an /etc/inittab file, the inittab entry format is as follows:\n" \
+"\n" \
+" <id>:<runlevels>:<action>:<process>\n" \
+"\n" \
+" <id>:\n" \
+"\n" \
+" WARNING: This field has a non-traditional meaning for BusyBox init!\n" \
+" The id field is used by BusyBox init to specify the controlling tty for\n" \
+" the specified process to run on. The contents of this field are\n" \
+" appended to \"/dev/\" and used as-is. There is no need for this field to\n" \
+" be unique, although if it isn't you may have strange results. If this\n" \
+" field is left blank, the controlling tty is set to the console. Also\n" \
+" note that if BusyBox detects that a serial console is in use, then only\n" \
+" entries whose controlling tty is either the serial console or /dev/null\n" \
+" will be run. BusyBox init does nothing with utmp. We don't need no\n" \
+" stinkin' utmp.\n" \
+"\n" \
+" <runlevels>:\n" \
+"\n" \
+" The runlevels field is completely ignored.\n" \
+"\n" \
+" <action>:\n" \
+"\n" \
+" Valid actions include: sysinit, respawn, askfirst, wait,\n" \
+" once, restart, ctrlaltdel, and shutdown.\n" \
+"\n" \
+" The available actions can be classified into two groups: actions\n" \
+" that are run only once, and actions that are re-run when the specified\n" \
+" process exits.\n" \
+"\n" \
+" Run only-once actions:\n" \
+"\n" \
+" 'sysinit' is the first item run on boot. init waits until all\n" \
+" sysinit actions are completed before continuing. Following the\n" \
+" completion of all sysinit actions, all 'wait' actions are run.\n" \
+" 'wait' actions, like 'sysinit' actions, cause init to wait until\n" \
+" the specified task completes. 'once' actions are asynchronous,\n" \
+" therefore, init does not wait for them to complete. 'restart' is\n" \
+" the action taken to restart the init process. By default this should\n" \
+" simply run /sbin/init, but can be a script which runs pivot_root or it\n" \
+" can do all sorts of other interesting things. The 'ctrlaltdel' init\n" \
+" actions are run when the system detects that someone on the system\n" \
+" console has pressed the CTRL-ALT-DEL key combination. Typically one\n" \
+" wants to run 'reboot' at this point to cause the system to reboot.\n" \
+" Finally the 'shutdown' action specifies the actions to taken when\n" \
+" init is told to reboot. Unmounting filesystems and disabling swap\n" \
+" is a very good here.\n" \
+"\n" \
+" Run repeatedly actions:\n" \
+"\n" \
+" 'respawn' actions are run after the 'once' actions. When a process\n" \
+" started with a 'respawn' action exits, init automatically restarts\n" \
+" it. Unlike sysvinit, BusyBox init does not stop processes from\n" \
+" respawning out of control. The 'askfirst' actions acts just like\n" \
+" respawn, except that before running the specified process it\n" \
+" displays the line \"Please press Enter to activate this console.\"\n" \
+" and then waits for the user to press enter before starting the\n" \
+" specified process.\n" \
+"\n" \
+" Unrecognized actions (like initdefault) will cause init to emit an\n" \
+" error message, and then go along with its business. All actions are\n" \
+" run in the order they appear in /etc/inittab.\n" \
+"\n" \
+" <process>:\n" \
+"\n" \
+" Specifies the process to be executed and its command line.\n" \
+"\n" \
+"Example /etc/inittab file:\n" \
+"\n" \
+" # This is run first except when booting in single-user mode\n" \
+" #\n" \
+" ::sysinit:/etc/init.d/rcS\n" \
+" \n" \
+" # /bin/sh invocations on selected ttys\n" \
+" #\n" \
+" # Start an \"askfirst\" shell on the console (whatever that may be)\n" \
+" ::askfirst:-/bin/sh\n" \
+" # Start an \"askfirst\" shell on /dev/tty2-4\n" \
+" tty2::askfirst:-/bin/sh\n" \
+" tty3::askfirst:-/bin/sh\n" \
+" tty4::askfirst:-/bin/sh\n" \
+" \n" \
+" # /sbin/getty invocations for selected ttys\n" \
+" #\n" \
+" tty4::respawn:/sbin/getty 38400 tty4\n" \
+" tty5::respawn:/sbin/getty 38400 tty5\n" \
+" \n" \
+" \n" \
+" # Example of how to put a getty on a serial line (for a terminal)\n" \
+" #\n" \
+" #::respawn:/sbin/getty -L ttyS0 9600 vt100\n" \
+" #::respawn:/sbin/getty -L ttyS1 9600 vt100\n" \
+" #\n" \
+" # Example how to put a getty on a modem line\n" \
+" #::respawn:/sbin/getty 57600 ttyS2\n" \
+" \n" \
+" # Stuff to do when restarting the init process\n" \
+" ::restart:/sbin/init\n" \
+" \n" \
+" # Stuff to do before rebooting\n" \
+" ::ctrlaltdel:/sbin/reboot\n" \
+" ::shutdown:/bin/umount -a -r\n" \
+" ::shutdown:/sbin/swapoff -a\n"
+
+#define insmod_trivial_usage \
+ "[OPTION]... MODULE [symbol=value]..."
+#define insmod_full_usage \
+ "Load the specified kernel modules into the kernel" \
+ "\n\nOptions:\n" \
+ " -f Force module to load into the wrong kernel version\n" \
+ " -k Make module autoclean-able\n" \
+ " -v Verbose output\n" \
+ " -q Quiet output\n" \
+ " -L Lock to prevent simultaneous loads of a module\n" \
+ USE_FEATURE_INSMOD_LOAD_MAP( \
+ " -m Output load map to stdout\n") \
+ " -o NAME Set internal module name to NAME\n" \
+ " -x Do not export externs"
+
+#define install_trivial_usage \
+ "[-cgmops] [sources] <dest|directory>"
+#define install_full_usage \
+ "Copy files and set attributes" \
+ "\n\nOptions:\n" \
+ " -c Copy the file, default\n" \
+ " -d Create directories\n" \
+ " -g Set group ownership\n" \
+ " -m Set permission modes\n" \
+ " -o Set ownership\n" \
+ " -p Preserve date\n" \
+ " -s Strip symbol tables" \
+ USE_SELINUX( \
+ "\n -Z Set security context of copy" \
+ )
+
+#define ip_trivial_usage \
+ "[OPTIONS] {address | link | route | tunnel | rule} {COMMAND}"
+#define ip_full_usage \
+ "ip [OPTIONS] OBJECT {COMMAND}\n" \
+ "where OBJECT := {link | addr | route | tunnel |rule}\n" \
+ "OPTIONS := { -f[amily] { inet | inet6 | link } | -o[neline] }"
+
+#define ipaddr_trivial_usage \
+ "{ {add|del} IFADDR dev STRING | {show|flush}\n" \
+ " [ dev STRING ] [ to PREFIX ] }"
+#define ipaddr_full_usage \
+ "ipaddr {add|delete} IFADDR dev STRING\n" \
+ "ipaddr {show|flush} [ dev STRING ] [ scope SCOPE-ID ]\n" \
+ " [ to PREFIX ] [ label PATTERN ]\n" \
+ " IFADDR := PREFIX | ADDR peer PREFIX\n" \
+ " [ broadcast ADDR ] [ anycast ADDR ]\n" \
+ " [ label STRING ] [ scope SCOPE-ID ]\n" \
+ " SCOPE-ID := [ host | link | global | NUMBER ]"
+
+#define ipcalc_trivial_usage \
+ "[OPTION]... <ADDRESS>[[/]<NETMASK>] [NETMASK]"
+#define ipcalc_full_usage \
+ "Calculate IP network settings from a IP address" \
+ "\n\nOptions:" \
+ "\n -b --broadcast Display calculated broadcast address" \
+ "\n -n --network Display calculated network address" \
+ "\n -m --netmask Display default netmask for IP" \
+ USE_FEATURE_IPCALC_FANCY( \
+ "\n -p --prefix Display the prefix for IP/NETMASK" \
+ "\n -h --hostname Display first resolved host name" \
+ "\n -s --silent Don't ever display error messages")
+
+#define ipcrm_trivial_usage \
+ "[-[MQS] key] [-[mqs] id]"
+#define ipcrm_full_usage \
+ "The upper-case options MQS are used to remove a shared memory segment by a\n" \
+ "segment by a shmkey value. The lower-case options mqs are used\n" \
+ "to remove a segment by shmid value.\n" \
+ "\n\nOptions:\n" \
+ " -[mM] Remove the memory segment after the last detach\n" \
+ " -[qQ] Remove the message queue\n" \
+ " -[sS] Remove the semaphore"
+
+#define ipcs_trivial_usage \
+ "[[-smq] -i shmid] | [[-asmq] [-tcplu]]"
+#define ipcs_full_usage \
+ " -i Specify a specific resource id\n" \
+ "Resource specification:\n" \
+ " -m Shared memory segments\n" \
+ " -q Message queues\n" \
+ " -s Semaphore arrays\n" \
+ " -a All (default)\n" \
+ "Output format:\n" \
+ " -t Time\n" \
+ " -c Creator\n" \
+ " -p Pid\n" \
+ " -l Limits\n" \
+ " -u Summary"
+
+#define iplink_trivial_usage \
+ "{ set DEVICE { up | down | arp { on | off } | show [ DEVICE ] }"
+#define iplink_full_usage \
+ "iplink set DEVICE { up | down | arp | multicast { on | off } |\n" \
+ " dynamic { on | off } |\n" \
+ " mtu MTU }\n" \
+ "iplink show [ DEVICE ]"
+
+#define iproute_trivial_usage \
+ "{ list | flush | { add | del | change | append |\n" \
+ " replace | monitor } ROUTE }"
+#define iproute_full_usage \
+ "iproute { list | flush } SELECTOR\n" \
+ "iproute get ADDRESS [ from ADDRESS iif STRING ]\n" \
+ " [ oif STRING ] [ tos TOS ]\n" \
+ "iproute { add | del | change | append | replace | monitor } ROUTE\n" \
+ " SELECTOR := [ root PREFIX ] [ match PREFIX ] [ proto RTPROTO ]\n" \
+ " ROUTE := [ TYPE ] PREFIX [ tos TOS ] [ proto RTPROTO ]"
+
+#define iprule_trivial_usage \
+ "{[ list | add | del ] RULE}"
+#define iprule_full_usage \
+ "iprule [ list | add | del ] SELECTOR ACTION\n" \
+ " SELECTOR := [ from PREFIX ] [ to PREFIX ] [ tos TOS ] [ fwmark FWMARK ]\n" \
+ " [ dev STRING ] [ pref NUMBER ]\n" \
+ " ACTION := [ table TABLE_ID ] [ nat ADDRESS ]\n" \
+ " [ prohibit | reject | unreachable ]\n" \
+ " [ realms [SRCREALM/]DSTREALM ]\n" \
+ " TABLE_ID := [ local | main | default | NUMBER ]"
+
+#define iptunnel_trivial_usage \
+ "{ add | change | del | show } [ NAME ]\n" \
+ " [ mode { ipip | gre | sit } ]\n" \
+ " [ remote ADDR ] [ local ADDR ] [ ttl TTL ]"
+#define iptunnel_full_usage \
+ "iptunnel { add | change | del | show } [ NAME ]\n" \
+ " [ mode { ipip | gre | sit } ] [ remote ADDR ] [ local ADDR ]\n" \
+ " [ [i|o]seq ] [ [i|o]key KEY ] [ [i|o]csum ]\n" \
+ " [ ttl TTL ] [ tos TOS ] [ [no]pmtudisc ] [ dev PHYS_DEV ]"
+
+#define kill_trivial_usage \
+ "[-l] [-signal] process-id [process-id ...]"
+#define kill_full_usage \
+ "Send a signal (default is TERM) to the specified process(es)" \
+ "\n\nOptions:\n" \
+ " -l List all signal names and numbers"
+#define kill_example_usage \
+ "$ ps | grep apache\n" \
+ "252 root root S [apache]\n" \
+ "263 www-data www-data S [apache]\n" \
+ "264 www-data www-data S [apache]\n" \
+ "265 www-data www-data S [apache]\n" \
+ "266 www-data www-data S [apache]\n" \
+ "267 www-data www-data S [apache]\n" \
+ "$ kill 252\n"
+
+#define killall_trivial_usage \
+ "[-l] [-q] [-signal] process-name [process-name ...]"
+#define killall_full_usage \
+ "Send a signal (default is TERM) to the specified process(es)" \
+ "\n\nOptions:\n" \
+ " -l List all signal names and numbers\n" \
+ " -q Do not complain if no processes were killed"
+#define killall_example_usage \
+ "$ killall apache\n"
+
+#define killall5_trivial_usage \
+ "[-l] [-signal]"
+#define killall5_full_usage \
+ "Send a signal (default is TERM) to all processes outside current session" \
+ "\n\nOptions:\n" \
+ " -l List all signal names and numbers\n" \
+
+#define klogd_trivial_usage \
+ "[-c n] [-n]"
+#define klogd_full_usage \
+ "Kernel logger" \
+ "\n\nOptions:\n" \
+ " -c n Sets the default log level of console messages to n\n" \
+ " -n Run as foreground process"
+
+#define length_trivial_usage \
+ "STRING"
+#define length_full_usage \
+ "Print out the length of the specified STRING"
+#define length_example_usage \
+ "$ length Hello\n" \
+ "5\n"
+
+#define less_trivial_usage \
+ "[-EMNmh~?] FILE1 FILE2..."
+#define less_full_usage \
+ "View a file or list of files. The position within files can be\n" \
+ "changed, and files can be manipulated in various ways." \
+ "\n\nOptions:\n" \
+ " -E Quit once the end of a file is reached\n" \
+ " -M Display a status line containing the current line numbers\n" \
+ " and the percentage through the file\n" \
+ " -N Prefix line numbers to each line\n" \
+ " -m Display a status line containing the percentage through the\n" \
+ " file\n" \
+ " -~ Suppress ~s displayed when input past the end of the file is\n" \
+ " reached"
+
+#define setarch_trivial_usage \
+ "<personality> <program> [args ...]"
+#define setarch_full_usage \
+ "Personality may be:\n" \
+ " linux32 Set 32bit uname emulation\n" \
+ " linux64 Set 64bit uname emulation"
+
+#define ln_trivial_usage \
+ "[OPTION] TARGET... LINK_NAME|DIRECTORY"
+#define ln_full_usage \
+ "Create a link named LINK_NAME or DIRECTORY to the specified TARGET.\n" \
+ "You may use '--' to indicate that all following arguments are non-options." \
+ "\n\nOptions:\n" \
+ " -s Make symlinks instead of hardlinks\n" \
+ " -f Remove existing destination files\n" \
+ " -n No dereference symlinks - treat like normal file\n" \
+ " -b Make a backup of the target (if exists) before link operation\n" \
+ " -S suf Use suffix instead of ~ when making backup files"
+#define ln_example_usage \
+ "$ ln -s BusyBox /tmp/ls\n" \
+ "$ ls -l /tmp/ls\n" \
+ "lrwxrwxrwx 1 root root 7 Apr 12 18:39 ls -> BusyBox*\n"
+
+#define loadfont_trivial_usage \
+ "< font"
+#define loadfont_full_usage \
+ "Load a console font from standard input"
+#define loadfont_example_usage \
+ "$ loadfont < /etc/i18n/fontname\n"
+
+#define loadkmap_trivial_usage \
+ "< keymap"
+#define loadkmap_full_usage \
+ "Load a binary keyboard translation table from standard input"
+#define loadkmap_example_usage \
+ "$ loadkmap < /etc/i18n/lang-keymap\n"
+
+#define logger_trivial_usage \
+ "[OPTION]... [MESSAGE]"
+#define logger_full_usage \
+ "Write MESSAGE to the system log. If MESSAGE is omitted, log stdin." \
+ "\n\nOptions:\n" \
+ " -s Log to stderr as well as the system log\n" \
+ " -t TAG Log using the specified tag (defaults to user name)\n" \
+ " -p PRIO Enter the message with the specified priority.\n" \
+ " This may be numerical or a 'facility.level' pair."
+#define logger_example_usage \
+ "$ logger \"hello\"\n"
+
+#define login_trivial_usage \
+ "[OPTION]... [username] [ENV=VAR ...]"
+#define login_full_usage \
+ "Begin a new session on the system" \
+ "\n\nOptions:\n" \
+ " -f Do not authenticate (user already authenticated)\n" \
+ " -h Name of the remote host for this login\n" \
+ " -p Preserve environment"
+
+#define logname_trivial_usage \
+ ""
+#define logname_full_usage \
+ "Print the name of the current user"
+#define logname_example_usage \
+ "$ logname\n" \
+ "root\n"
+
+#define logread_trivial_usage \
+ "[OPTION]..."
+#define logread_full_usage \
+ "Show the messages from syslogd (using circular buffer)" \
+ "\n\nOptions:\n" \
+ " -f Output data as the log grows"
+
+#define losetup_trivial_usage \
+ "[-o OFFSET] [-d] LOOPDEVICE [FILE]]"
+#define losetup_full_usage \
+ "(Dis)associate LOOPDEVICE with FILE, or display current associations" \
+ "\n\nOptions:\n" \
+ " -d Disassociate LOOPDEVICE\n" \
+ " -o OFFSET Start OFFSET bytes into FILE"
+#define losetup_notes_usage \
+ "No arguments will display all current associations.\n" \
+ "One argument (losetup /dev/loop1) will display the current association\n" \
+ "(if any), or disassociate it (with -d). The display shows the offset\n" \
+ "and filename of the file the loop device is currently bound to.\n\n" \
+ "Two arguments (losetup /dev/loop1 file.img) create a new association,\n" \
+ "with an optional offset (-o 12345). Encryption is not yet supported.\n\n"
+
+#define ls_trivial_usage \
+ "[-1Aa" USE_FEATURE_LS_TIMESTAMPS("c") "Cd" \
+ USE_FEATURE_LS_TIMESTAMPS("e") USE_FEATURE_LS_FILETYPES("F") "iln" \
+ USE_FEATURE_LS_FILETYPES("p") USE_FEATURE_LS_FOLLOWLINKS("L") \
+ USE_FEATURE_LS_RECURSIVE("R") USE_FEATURE_LS_SORTFILES("rS") "s" \
+ USE_FEATURE_AUTOWIDTH("T") USE_FEATURE_LS_TIMESTAMPS("tu") \
+ USE_FEATURE_LS_SORTFILES("v") USE_FEATURE_AUTOWIDTH("w") "x" \
+ USE_FEATURE_LS_SORTFILES("X") USE_FEATURE_HUMAN_READABLE("h") "k" \
+ USE_SELINUX("K") "] [filenames...]"
+#define ls_full_usage \
+ "List directory contents" \
+ "\n\nOptions:" \
+ "\n -1 List files in a single column" \
+ "\n -A Do not list implied . and .." \
+ "\n -a Do not hide entries starting with ." \
+ "\n -C List entries by columns" \
+ USE_FEATURE_LS_TIMESTAMPS( \
+ "\n -c With -l: show ctime") \
+ USE_FEATURE_LS_COLOR( \
+ "\n --color[={always,never,auto}] Control coloring") \
+ "\n -d List directory entries instead of contents" \
+ USE_FEATURE_LS_TIMESTAMPS( \
+ "\n -e List both full date and full time") \
+ USE_FEATURE_LS_FILETYPES( \
+ "\n -F Append indicator (one of */=@|) to entries") \
+ "\n -i List the i-node for each file" \
+ "\n -l Use a long listing format" \
+ "\n -n List numeric UIDs and GIDs instead of names" \
+ USE_FEATURE_LS_FILETYPES( \
+ "\n -p Append indicator (one of /=@|) to entries") \
+ USE_FEATURE_LS_FOLLOWLINKS( \
+ "\n -L List entries pointed to by symlinks") \
+ USE_FEATURE_LS_RECURSIVE( \
+ "\n -R List subdirectories recursively") \
+ USE_FEATURE_LS_SORTFILES( \
+ "\n -r Sort the listing in reverse order") \
+ USE_FEATURE_LS_SORTFILES( \
+ "\n -S Sort the listing by file size") \
+ "\n -s List the size of each file, in blocks" \
+ USE_FEATURE_AUTOWIDTH( \
+ "\n -T NUM Assume Tabstop every NUM columns") \
+ USE_FEATURE_LS_TIMESTAMPS( \
+ "\n -t With -l: show modification time") \
+ USE_FEATURE_LS_TIMESTAMPS( \
+ "\n -u With -l: show access time") \
+ USE_FEATURE_LS_SORTFILES( \
+ "\n -v Sort the listing by version") \
+ USE_FEATURE_AUTOWIDTH( \
+ "\n -w NUM Assume the terminal is NUM columns wide") \
+ "\n -x List entries by lines instead of by columns" \
+ USE_FEATURE_LS_SORTFILES( \
+ "\n -X Sort the listing by extension") \
+ USE_FEATURE_HUMAN_READABLE( \
+ "\n -h Print sizes in human readable format (e.g., 1K 243M 2G)") \
+ USE_SELINUX( \
+ "\n -k Print security context") \
+ USE_SELINUX( \
+ "\n -K Print security context in long format") \
+ USE_SELINUX( \
+ "\n -Z Print security context and permission")
+
+#define lsattr_trivial_usage \
+ "[-Radlv] [files...]"
+#define lsattr_full_usage \
+ "List file attributes on an ext2 fs" \
+ "\n\nOptions:\n" \
+ " -R Recursively list subdirectories\n" \
+ " -a Do not hide entries starting with .\n" \
+ " -d List directory entries instead of contents\n" \
+ " -l Print long flag names\n" \
+ " -v List the file's version/generation number"
+
+#define lsmod_trivial_usage \
+ ""
+#define lsmod_full_usage \
+ "List the currently loaded kernel modules"
+
+#ifdef CONFIG_FEATURE_MAKEDEVS_LEAF
+#define makedevs_trivial_usage \
+ "NAME TYPE MAJOR MINOR FIRST LAST [s]"
+#define makedevs_full_usage \
+ "Create a range of block or character special files\n\n" \
+ "TYPEs include:\n" \
+ " b: Make a block (buffered) device\n" \
+ " c or u: Make a character (un-buffered) device\n" \
+ " p: Make a named pipe. MAJOR and MINOR are ignored for named pipes\n" \
+ "\n" \
+ "FIRST specifies the number appended to NAME to create the first device.\n" \
+ "LAST specifies the number of the last item that should be created\n" \
+ "If 's' is the last argument, the base device is created as well.\n\n" \
+ "For example:\n" \
+ " makedevs /dev/ttyS c 4 66 2 63 -> ttyS2-ttyS63\n" \
+ " makedevs /dev/hda b 3 0 0 8 s -> hda,hda1-hda8"
+#define makedevs_example_usage \
+ "# makedevs /dev/ttyS c 4 66 2 63\n" \
+ "[creates ttyS2-ttyS63]\n" \
+ "# makedevs /dev/hda b 3 0 0 8 s\n" \
+ "[creates hda,hda1-hda8]\n"
+#endif
+
+#ifdef CONFIG_FEATURE_MAKEDEVS_TABLE
+#define makedevs_trivial_usage \
+ "[-d device_table] rootdir"
+#define makedevs_full_usage \
+ "Create a range of special files as specified in a device table.\n" \
+ "Device table entries take the form of:\n" \
+ "<type> <mode> <uid> <gid> <major> <minor> <start> <inc> <count>\n" \
+ "Where name is the file name, type can be one of:\n" \
+ " f A regular file\n" \
+ " d Directory\n" \
+ " c Character special device file\n" \
+ " b Block special device file\n" \
+ " p Fifo (named pipe)\n" \
+ "uid is the user id for the target file, gid is the group id for the\n" \
+ "target file. The rest of the entries (major, minor, etc) apply to\n" \
+ "to device special files. A '-' may be used for blank entries."
+#define makedevs_example_usage \
+ "For example:\n" \
+ "<name> <type> <mode><uid><gid><major><minor><start><inc><count>\n" \
+ "/dev d 755 0 0 - - - - -\n" \
+ "/dev/console c 666 0 0 5 1 - - -\n" \
+ "/dev/null c 666 0 0 1 3 0 0 -\n" \
+ "/dev/zero c 666 0 0 1 5 0 0 -\n" \
+ "/dev/hda b 640 0 0 3 0 0 0 -\n" \
+ "/dev/hda b 640 0 0 3 1 1 1 15\n\n" \
+ "Will Produce:\n" \
+ "/dev\n" \
+ "/dev/console\n" \
+ "/dev/null\n" \
+ "/dev/zero\n" \
+ "/dev/hda\n" \
+ "/dev/hda[0-15]\n"
+#endif
+
+#define matchpathcon_trivial_usage \
+ "[-n] [-N] [-f file_contexts_file] [-p prefix] [-V]"
+#define matchpathcon_full_usage \
+ " -n Do not display path" \
+ "\n -N Do not use translations" \
+ "\n -f Use alternate file_context file" \
+ "\n -p Use prefix to speed translations" \
+ "\n -V Verify file context on disk matches defaults"
+
+#define md5sum_trivial_usage \
+ "[OPTION] [FILEs...]" \
+ USE_FEATURE_MD5_SHA1_SUM_CHECK("\n or: md5sum [OPTION] -c [FILE]")
+#define md5sum_full_usage \
+ "Print" USE_FEATURE_MD5_SHA1_SUM_CHECK(" or check") " MD5 checksums" \
+ "\n\nOptions:\n" \
+ "With no FILE, or when FILE is -, read standard input." \
+ USE_FEATURE_MD5_SHA1_SUM_CHECK("\n\n" \
+ " -c Check MD5 sums against given list\n" \
+ "\nThe following two options are useful only when verifying checksums:\n" \
+ " -s Don't output anything, status code shows success\n" \
+ " -w Warn about improperly formatted MD5 checksum lines")
+#define md5sum_example_usage \
+ "$ md5sum < busybox\n" \
+ "6fd11e98b98a58f64ff3398d7b324003\n" \
+ "$ md5sum busybox\n" \
+ "6fd11e98b98a58f64ff3398d7b324003 busybox\n" \
+ "$ md5sum -c -\n" \
+ "6fd11e98b98a58f64ff3398d7b324003 busybox\n" \
+ "busybox: OK\n" \
+ "^D\n"
+
+#define mdev_trivial_usage \
+ "[-s]"
+#define mdev_full_usage \
+ " -s Scan /sys and populate /dev during system boot\n\n" \
+ "Called with no options (via hotplug) it uses environment variables\n" \
+ "to determine which device to add/remove."
+#define mdev_notes_usage "" \
+ USE_FEATURE_MDEV_CONFIG( \
+ "The mdev config file contains lines that look like:\n" \
+ " hd[a-z][0-9]* 0:3 660\n\n" \
+ "That's device name (with regex match), uid:gid, and permissions.\n\n" \
+ USE_FEATURE_MDEV_EXEC( \
+ "Optionally, that can be followed (on the same line) by a special character\n" \
+ "and a command line to run after creating/before deleting the corresponding\n" \
+ "device(s). The environment variable $MDEV indicates the active device node\n" \
+ "(which is useful if it's a regex match). For example:\n\n" \
+ " hdc root:cdrom 660 *ln -s $MDEV cdrom\n\n" \
+ "The special characters are @ (run after creating), $ (run before deleting),\n" \
+ "and * (run both after creating and before deleting). The commands run in\n" \
+ "the /dev directory, and use system() which calls /bin/sh.\n\n" \
+ ) \
+ "Config file parsing stops on the first matching line. If no config\n"\
+ "entry is matched, devices are created with default 0:0 660. (Make\n"\
+ "the last line match .* to override this.)\n\n" \
+ )
+
+#define mesg_trivial_usage \
+ "[y|n]"
+#define mesg_full_usage \
+ "Control write access to your terminal\n" \
+ " y Allow write access to your terminal\n" \
+ " n Disallow write access to your terminal"
+
+#define mkdir_trivial_usage \
+ "[OPTION] DIRECTORY..."
+#define mkdir_full_usage \
+ "Create the DIRECTORY(ies) if they do not already exist" \
+ "\n\nOptions:\n" \
+ " -m Set permission mode (as in chmod), not rwxrwxrwx - umask\n" \
+ " -p No error if existing, make parent directories as needed" \
+ USE_SELINUX( \
+ "\n -Z Set security context" \
+ )
+
+#define mkdir_example_usage \
+ "$ mkdir /tmp/foo\n" \
+ "$ mkdir /tmp/foo\n" \
+ "/tmp/foo: File exists\n" \
+ "$ mkdir /tmp/foo/bar/baz\n" \
+ "/tmp/foo/bar/baz: No such file or directory\n" \
+ "$ mkdir -p /tmp/foo/bar/baz\n"
+
+#define mke2fs_trivial_usage \
+ "[-c|-l filename] [-b block-size] [-f fragment-size] [-g blocks-per-group] " \
+ "[-i bytes-per-inode] [-j] [-J journal-options] [-N number-of-inodes] [-n] " \
+ "[-m reserved-blocks-percentage] [-o creator-os] [-O feature[,...]] [-q] " \
+ "[r fs-revision-level] [-E extended-options] [-v] [-F] [-L volume-label] " \
+ "[-M last-mounted-directory] [-S] [-T filesystem-type] " \
+ "device [blocks-count]"
+#define mke2fs_full_usage \
+ " -b size Block size in bytes\n" \
+ " -c Check for bad blocks before creating\n" \
+ " -E opts Set extended options\n" \
+ " -f size Fragment size in bytes\n" \
+ " -F Force (ignore sanity checks)\n" \
+ " -g num Number of blocks in a block group\n" \
+ " -i ratio The bytes/inode ratio\n" \
+ " -j Create a journal (ext3)\n" \
+ " -J opts Set journal options (size/device)\n" \
+ " -l file Read bad blocks list from file\n" \
+ " -L lbl Set the volume label\n" \
+ " -m percent Percent of fs blocks to reserve for admin\n" \
+ " -M dir Set last mounted directory\n" \
+ " -n Do not actually create anything\n" \
+ " -N num Number of inodes to create\n" \
+ " -o os Set the 'creator os' field\n" \
+ " -O features Dir_index/filetype/has_journal/journal_dev/sparse_super\n" \
+ " -q Quiet execution\n" \
+ " -r rev Set filesystem revision\n" \
+ " -S Write superblock and group descriptors only\n" \
+ " -T fs-type Set usage type (news/largefile/largefile4)\n" \
+ " -v Verbose execution"
+
+#define mkfifo_trivial_usage \
+ "[OPTIONS] name"
+#define mkfifo_full_usage \
+ "Create a named pipe (identical to 'mknod name p')" \
+ "\n\nOptions:\n" \
+ " -m Create the pipe using the specified mode (default a=rw)" \
+ USE_SELINUX( \
+ "\n -Z Set security context" \
+ )
+
+#define mkfs_minix_trivial_usage \
+ "[-c | -l filename] [-nXX] [-iXX] /dev/name [blocks]"
+#define mkfs_minix_full_usage \
+ "Make a MINIX filesystem" \
+ "\n\nOptions:\n" \
+ " -c Check the device for bad blocks\n" \
+ " -n [14|30] Specify the maximum length of filenames\n" \
+ " -i INODES Specify the number of inodes for the filesystem\n" \
+ " -l FILENAME Read the bad blocks list from FILENAME\n" \
+ " -v Make a Minix version 2 filesystem"
+
+#define mknod_trivial_usage \
+ "[OPTIONS] NAME TYPE MAJOR MINOR"
+#define mknod_full_usage \
+ "Create a special file (block, character, or pipe)" \
+ "\n\nOptions:\n" \
+ " -m Create the special file using the specified mode (default a=rw)" \
+ "\n\nTYPEs include:\n" \
+ " b: Make a block (buffered) device\n" \
+ " c or u: Make a character (un-buffered) device\n" \
+ " p: Make a named pipe. MAJOR and MINOR are ignored for named pipes" \
+ USE_SELINUX( \
+ "\n -Z Set security context" \
+ )
+
+#define mknod_example_usage \
+ "$ mknod /dev/fd0 b 2 0\n" \
+ "$ mknod -m 644 /tmp/pipe p\n"
+
+#define mkswap_trivial_usage \
+ "[-c] [-v0|-v1] device [block-count]"
+#define mkswap_full_usage \
+ "Prepare a disk partition to be used as swap partition" \
+ "\n\nOptions:\n" \
+ " -c Check for read-ability\n" \
+ " -v0 Make version 0 swap [max 128 Megs]\n" \
+ " -v1 Make version 1 swap [big!] (default for kernels > 2.1.117)\n" \
+ " block-count Number of block to use (default is entire partition)"
+
+#define mktemp_trivial_usage \
+ "[-dq] TEMPLATE"
+#define mktemp_full_usage \
+ "Create a temporary file with its name based on TEMPLATE.\n" \
+ "TEMPLATE is any name with six 'Xs' (i.e., /tmp/temp.XXXXXX)." \
+ "\n\nOptions:\n" \
+ " -d Make a directory instead of a file\n" \
+ " -q Fail silently if an error occurs"
+#define mktemp_example_usage \
+ "$ mktemp /tmp/temp.XXXXXX\n" \
+ "/tmp/temp.mWiLjM\n" \
+ "$ ls -la /tmp/temp.mWiLjM\n" \
+ "-rw------- 1 andersen andersen 0 Apr 25 17:10 /tmp/temp.mWiLjM\n"
+
+#define modprobe_trivial_usage \
+ "[-knqrsv] MODULE [symbol=value ...]"
+#define modprobe_full_usage \
+ "Options:\n" \
+ " -k Make module autoclean-able\n" \
+ " -n Just show what would be done\n" \
+ " -q Quiet output\n" \
+ " -r Remove module (stacks) or do autoclean\n" \
+ " -s Report via syslog instead of stderr\n" \
+ " -v Verbose output"
+#define modprobe_notes_usage \
+"modprobe can (un)load a stack of modules, passing each module options (when\n" \
+"loading). modprobe uses a configuration file to determine what option(s) to\n" \
+"pass each module it loads.\n" \
+"\n" \
+"The configuration file is searched (in order) amongst:\n" \
+"\n" \
+" /etc/modprobe.conf (2.6 only)\n" \
+" /etc/modules.conf\n" \
+" /etc/conf.modules (deprecated)\n" \
+"\n" \
+"They all have the same syntax (see below). If none is present, it is\n" \
+"_not_ an error; each loaded module is then expected to load without\n" \
+"options. Once a file is found, the others are tested for.\n" \
+"\n" \
+"/etc/modules.conf entry format:\n" \
+"\n" \
+" alias <alias_name> <mod_name>\n" \
+" Makes it possible to modprobe alias_name, when there is no such module.\n" \
+" It makes sense if your mod_name is long, or you want a more representative\n" \
+" name for that module (eg. 'scsi' in place of 'aha7xxx').\n" \
+" This makes it also possible to use a different set of options (below) for\n" \
+" the module and the alias.\n" \
+" A module can be aliased more than once.\n" \
+"\n" \
+" options <mod_name|alias_name> <symbol=value ...>\n" \
+" When loading module mod_name (or the module aliased by alias_name), pass\n" \
+" the \"symbol=value\" pairs as option to that module.\n" \
+"\n" \
+"Sample /etc/modules.conf file:\n" \
+"\n" \
+" options tulip irq=3\n" \
+" alias tulip tulip2\n" \
+" options tulip2 irq=4 io=0x308\n" \
+"\n" \
+"Other functionality offered by 'classic' modprobe is not available in\n" \
+"this implementation.\n" \
+"\n" \
+"If module options are present both in the config file, and on the command line,\n" \
+"then the options from the command line will be passed to the module _after_\n" \
+"the options from the config file. That way, you can have defaults in the config\n" \
+"file, and override them for a specific usage from the command line.\n"
+#define modprobe_example_usage \
+ "(with the above /etc/modules.conf):\n\n" \
+ "$ modprobe tulip\n" \
+ " will load the module 'tulip' with default option 'irq=3'\n\n" \
+ "$ modprobe tulip irq=5\n" \
+ " will load the module 'tulip' with option 'irq=5', thus overriding the default\n\n" \
+ "$ modprobe tulip2\n" \
+ " will load the module 'tulip' with default options 'irq=4 io=0x308',\n" \
+ " which are the default for alias 'tulip2'\n\n" \
+ "$ modprobe tulip2 irq=8\n" \
+ " will load the module 'tulip' with default options 'irq=4 io=0x308 irq=8',\n" \
+ " which are the default for alias 'tulip2' overridden by the option 'irq=8'\n\n" \
+ " from the command line\n\n" \
+ "$ modprobe tulip2 irq=2 io=0x210\n" \
+ " will load the module 'tulip' with default options 'irq=4 io=0x308 irq=4 io=0x210',\n" \
+ " which are the default for alias 'tulip2' overridden by the options 'irq=2 io=0x210'\n\n" \
+ " from the command line\n"
+
+#define more_trivial_usage \
+ "[FILE ...]"
+#define more_full_usage \
+ "View FILE or standard input one screenful at a time"
+#define more_example_usage \
+ "$ dmesg | more\n"
+
+#define mount_trivial_usage \
+ "[flags] DEVICE NODE [-o options,more-options]"
+#define mount_full_usage \
+ "Mount a filesystem. Filesystem autodetection requires /proc be mounted." \
+ "\n\nOptions:\n" \
+ " -a Mount all filesystems in fstab\n" \
+ USE_FEATURE_MTAB_SUPPORT( \
+ " -f \"Fake\" Add entry to mount table but don't mount it\n" \
+ " -n Don't write a mount table entry\n" \
+ ) \
+ " -o option One of many filesystem options, listed below\n" \
+ " -r Mount the filesystem read-only\n" \
+ " -t fs-type Specify the filesystem type\n" \
+ " -w Mount for reading and writing (default)\n" \
+ "\n" \
+ "Options for use with the \"-o\" flag:\n" \
+ USE_FEATURE_MOUNT_LOOP( \
+ " loop Ignored (loop devices are autodetected)\n" \
+ ) \
+ USE_FEATURE_MOUNT_FLAGS( \
+ " [a]sync Writes are asynchronous / synchronous\n" \
+ " [no]atime Disable / enable updates to inode access times\n" \
+ " [no]diratime Disable / enable atime updates to directories\n" \
+ " [no]dev Allow use of special device files / disallow them\n" \
+ " [no]exec Allow use of executable files / disallow them\n" \
+ " [no]suid Allow set-user-id-root programs / disallow them\n" \
+ " [r]shared Convert [recursively] to a shared subtree\n" \
+ " [r]slave Convert [recursively] to a slave subtree\n" \
+ " [r]private Convert [recursively] to a private subtree\n" \
+ " [un]bindable Make mount point [un]able to be bind mounted\n" \
+ " bind Bind a directory to an additional location\n" \
+ " move Relocate an existing mount point\n" \
+ ) \
+ " remount Remount a mounted filesystem, changing its flags\n" \
+ " ro/rw Mount for read-only / read-write\n" \
+ "\n" \
+ "There are EVEN MORE flags that are specific to each filesystem\n" \
+ "You'll have to see the written documentation for those filesystems"
+#define mount_example_usage \
+ "$ mount\n" \
+ "/dev/hda3 on / type minix (rw)\n" \
+ "proc on /proc type proc (rw)\n" \
+ "devpts on /dev/pts type devpts (rw)\n" \
+ "$ mount /dev/fd0 /mnt -t msdos -o ro\n" \
+ "$ mount /tmp/diskimage /opt -t ext2 -o loop\n" \
+ "$ mount cd_image.iso mydir\n"
+#define mount_notes_usage \
+ "Returns 0 for success, number of failed mounts for -a, or errno for one mount."
+
+#define mountpoint_trivial_usage \
+ "[-q] <[-d] DIR | -x DEVICE>"
+#define mountpoint_full_usage \
+ "mountpoint checks if the directory is a mountpoint" \
+ "\n\nOptions:\n" \
+ " -q Be more quiet\n" \
+ " -d Print major/minor device number of the filesystem\n" \
+ " -x Print major/minor device number of the blockdevice"
+#define mountpoint_example_usage \
+ "$ mountpoint /proc\n" \
+ "/proc is not a mountpoint\n" \
+ "$ mountpoint /sys\n" \
+ "/sys is a mountpoint\n"
+
+#define mt_trivial_usage \
+ "[-f device] opcode value"
+#define mt_full_usage \
+ "Control magnetic tape drive operation\n" \
+ "\nAvailable Opcodes:\n\n" \
+ "bsf bsfm bsr bss datacompression drvbuffer eof eom erase\n" \
+ "fsf fsfm fsr fss load lock mkpart nop offline ras1 ras2\n" \
+ "ras3 reset retension rewind rewoffline seek setblk setdensity\n" \
+ "setpart tell unload unlock weof wset"
+
+#define mv_trivial_usage \
+ "[OPTION]... SOURCE DEST\n" \
+ "or: mv [OPTION]... SOURCE... DIRECTORY"
+#define mv_full_usage \
+ "Rename SOURCE to DEST, or move SOURCE(s) to DIRECTORY" \
+ "\n\nOptions:\n" \
+ " -f Don't prompt before overwriting\n" \
+ " -i Interactive, prompt before overwrite"
+#define mv_example_usage \
+ "$ mv /tmp/foo /bin/bar\n"
+
+#define nameif_trivial_usage \
+ "[-s] [-c FILE] [{IFNAME MACADDR}]"
+#define nameif_full_usage \
+ "Rename network interface while it in the down state" \
+ "\n\nOptions:\n" \
+ " -c FILE Use configuration file (default is /etc/mactab)\n" \
+ " -s Use syslog (LOCAL0 facility)\n" \
+ " IFNAME MACADDR new_interface_name interface_mac_address"
+#define nameif_example_usage \
+ "$ nameif -s dmz0 00:A0:C9:8C:F6:3F\n" \
+ " or\n" \
+ "$ nameif -c /etc/my_mactab_file\n" \
+
+#if ENABLE_NC_SERVER || ENABLE_NC_EXTRA
+#define NC_OPTIONS_STR "\n\nOptions:"
+#else
+#define NC_OPTIONS_STR
+#endif
+
+#define nc_trivial_usage \
+ USE_NC_EXTRA("[-iN] [-wN] ")USE_NC_SERVER("[-l] [-p PORT] ") \
+ "["USE_NC_EXTRA("-f FILENAME|")"IPADDR PORTNUM]"USE_NC_EXTRA(" [-e COMMAND]")
+#define nc_full_usage \
+ "Open a pipe to IP:port" USE_NC_EXTRA(" or file") \
+ NC_OPTIONS_STR \
+ USE_NC_EXTRA( \
+ "\n -e Exec rest of command line after connect" \
+ "\n -i SECS Delay interval for lines sent" \
+ "\n -w SECS Timeout for connect" \
+ "\n -f FILE Use file (ala /dev/ttyS0) instead of network" \
+ ) \
+ USE_NC_SERVER( \
+ "\n -l Listen mode, for inbound connects" \
+ USE_NC_EXTRA( \
+ "\n (use -l twice with -e for persistent server)") \
+ "\n -p PORT Local port number" \
+ )
+
+#define nc_notes_usage "" \
+ USE_NC_EXTRA( \
+ "To use netcat as a terminal emulator on a serial port:\n\n" \
+ "$ stty 115200 -F /dev/ttyS0\n" \
+ "$ stty raw -echo -ctlecho && nc -f /dev/ttyS0\n" \
+ )
+
+#define nc_example_usage \
+ "$ nc foobar.somedomain.com 25\n" \
+ "220 foobar ESMTP Exim 3.12 #1 Sat, 15 Apr 2000 00:03:02 -0600\n" \
+ "help\n" \
+ "214-Commands supported:\n" \
+ "214- HELO EHLO MAIL RCPT DATA AUTH\n" \
+ "214 NOOP QUIT RSET HELP\n" \
+ "quit\n" \
+ "221 foobar closing connection\n"
+
+#define netstat_trivial_usage \
+ "[-laenrtuwx]"
+#define netstat_full_usage \
+ "Display networking information" \
+ "\n\nOptions:\n" \
+ " -l Display listening server sockets\n" \
+ " -a Display all sockets (default: connected)\n" \
+ " -e Display other/more information\n" \
+ " -n Don't resolve names\n" \
+ " -r Display routing table\n" \
+ " -t Tcp sockets\n" \
+ " -u Udp sockets\n" \
+ " -w Raw sockets\n" \
+ " -x Unix sockets"
+
+#define nice_trivial_usage \
+ "[-n ADJUST] [COMMAND [ARG] ...]"
+#define nice_full_usage \
+ "Run a program with modified scheduling priority" \
+ "\n\nOptions:\n" \
+ " -n ADJUST Adjust the scheduling priority by ADJUST"
+
+#define nmeter_trivial_usage \
+ "format_string"
+#define nmeter_full_usage \
+ "Monitor system in real time\n\n" \
+ "Format specifiers:\n" \
+ "%Nc or %[cN] Monitor CPU. N - bar size, default 10\n" \
+ " (displays: S:system U:user N:niced D:iowait I:irq i:softirq)\n" \
+ "%[niface] Monitor network interface 'iface'\n" \
+ "%m Monitor allocated memory\n" \
+ "%[mf] Monitor free memory\n" \
+ "%[mt] Monitor total memory\n" \
+ "%s Monitor allocated swap\n" \
+ "%f Monitor number of used file descriptors\n" \
+ "%Ni Monitor total/specific IRQ rate\n" \
+ "%x Monitor context switch rate\n" \
+ "%p Monitor forks\n" \
+ "%[pn] Monitor # of processes\n" \
+ "%b Monitor block io\n" \
+ "%Nt Show time (with N decimal points)\n" \
+ "%Nd Milliseconds between updates (default=1000)\n" \
+ "%r Print <cr> instead of <lf> at EOL"
+#define nmeter_example_usage \
+ "nmeter '%250d%t %20c int %i bio %b mem %m forks%p'"
+
+#define nohup_trivial_usage \
+ "COMMAND [ARGS]"
+#define nohup_full_usage \
+ "Run a command immune to hangups, with output to a non-tty"
+#define nohup_example_usage \
+ "$ nohup make &"
+
+#define nslookup_trivial_usage \
+ "[HOST] [SERVER]"
+#define nslookup_full_usage \
+ "Query the nameserver for the IP address of the given HOST\n" \
+ "optionally using a specified DNS server"
+#define nslookup_example_usage \
+ "$ nslookup localhost\n" \
+ "Server: default\n" \
+ "Address: default\n" \
+ "\n" \
+ "Name: debian\n" \
+ "Address: 127.0.0.1\n"
+
+#define od_trivial_usage \
+ "[-aBbcDdeFfHhIiLlOovXx] [FILE]"
+#define od_full_usage \
+ "Write an unambiguous representation, octal bytes by default, of FILE\n" \
+ "to standard output. With no FILE, or when FILE is -, read standard input."
+
+#define openvt_trivial_usage \
+ "<vtnum> <COMMAND> [ARGS...]"
+#define openvt_full_usage \
+ "Start a command on a new virtual terminal"
+#define openvt_example_usage \
+ "openvt 2 /bin/ash\n"
+
+#define passwd_trivial_usage \
+ "[OPTION] [name]"
+#define passwd_full_usage \
+ "Change a user password. If no name is specified,\n" \
+ "changes the password for the current user." \
+ "\n\nOptions:\n" \
+ " -a Define which algorithm shall be used for the password\n" \
+ " (choices: des, md5)\n" /* ", sha1)" */ \
+ " -d Delete the password for the specified user account\n" \
+ " -l Locks (disables) the specified user account\n" \
+ " -u Unlocks (re-enables) the specified user account"
+
+#define patch_trivial_usage \
+ "[-p<num>] [-i <diff>]"
+#define patch_full_usage \
+ " -p NUM Strip NUM leading components from file names\n" \
+ " -i DIFF Read DIFF instead of stdin"
+#define patch_example_usage \
+ "$ patch -p1 < example.diff\n" \
+ "$ patch -p0 -i example.diff"
+
+#if (ENABLE_FEATURE_PIDOF_SINGLE || ENABLE_FEATURE_PIDOF_OMIT)
+#define USAGE_PIDOF "Options:"
+#else
+#define USAGE_PIDOF "\nThis version of pidof accepts no options."
+#endif
+
+#define pidof_trivial_usage \
+ "process-name [OPTION] [process-name ...]"
+
+#define pidof_full_usage \
+ "List the PIDs of all processes with names that match the\n" \
+ "names on the command line\n" \
+ USAGE_PIDOF \
+ USE_FEATURE_PIDOF_SINGLE( \
+ "\n -s Display only a single PID") \
+ USE_FEATURE_PIDOF_OMIT( \
+ "\n -o Omit given pid") \
+ USE_FEATURE_PIDOF_OMIT( \
+ "\n Use %PPID to omit the parent pid of pidof itself")
+#define pidof_example_usage \
+ "$ pidof init\n" \
+ "1\n" \
+ USE_FEATURE_PIDOF_OMIT( \
+ "$ pidof /bin/sh\n20351 5973 5950\n") \
+ USE_FEATURE_PIDOF_OMIT( \
+ "$ pidof /bin/sh -o %PPID\n20351 5950")
+
+#ifndef CONFIG_FEATURE_FANCY_PING
+#define ping_trivial_usage \
+ "host"
+#define ping_full_usage \
+ "Send ICMP ECHO_REQUEST packets to network hosts"
+#define ping6_trivial_usage \
+ "host"
+#define ping6_full_usage \
+ "Send ICMP ECHO_REQUEST packets to network hosts"
+#else
+#define ping_trivial_usage \
+ "[OPTION]... host"
+#define ping_full_usage \
+ "Send ICMP ECHO_REQUEST packets to network hosts" \
+ "\n\nOptions:\n" \
+ " -4, -6 Force IPv4 or IPv6 hostname resolution\n" \
+ " -c CNT Send only CNT pings\n" \
+ " -s SIZE Send SIZE data bytes in packets (default=56)\n" \
+ " -I iface/IP Use interface or IP address as source\n" \
+ " -q Quiet mode, only displays output at start\n" \
+ " and when finished"
+#define ping6_trivial_usage \
+ "[OPTION]... host"
+#define ping6_full_usage \
+ "Send ICMP ECHO_REQUEST packets to network hosts" \
+ "\n\nOptions:\n" \
+ " -c CNT Send only CNT pings\n" \
+ " -s SIZE Send SIZE data bytes in packets (default=56)\n" \
+ " -I iface/IP Use interface or IP address as source\n" \
+ " -q Quiet mode, only displays output at start\n" \
+ " and when finished"
+#endif
+#define ping_example_usage \
+ "$ ping localhost\n" \
+ "PING slag (127.0.0.1): 56 data bytes\n" \
+ "64 bytes from 127.0.0.1: icmp_seq=0 ttl=255 time=20.1 ms\n" \
+ "\n" \
+ "--- debian ping statistics ---\n" \
+ "1 packets transmitted, 1 packets received, 0% packet loss\n" \
+ "round-trip min/avg/max = 20.1/20.1/20.1 ms\n"
+#define ping6_example_usage \
+ "$ ping6 ip6-localhost\n" \
+ "PING ip6-localhost (::1): 56 data bytes\n" \
+ "64 bytes from ::1: icmp6_seq=0 ttl=64 time=20.1 ms\n" \
+ "\n" \
+ "--- ip6-localhost ping statistics ---\n" \
+ "1 packets transmitted, 1 packets received, 0% packet loss\n" \
+ "round-trip min/avg/max = 20.1/20.1/20.1 ms\n"
+
+#define pivot_root_trivial_usage \
+ "NEW_ROOT PUT_OLD"
+#define pivot_root_full_usage \
+ "Move the current root file system to PUT_OLD and make NEW_ROOT\n" \
+ "the new root file system"
+
+#define poweroff_trivial_usage \
+ "[-d<delay>] [-n<nosync>] [-f<force>]"
+#define poweroff_full_usage \
+ "Halt and shut off power" \
+ "\n\nOptions:\n" \
+ " -d Delay interval for halting\n" \
+ " -n No call to sync()\n" \
+ " -f Force power off (don't go through init)"
+
+#define printenv_trivial_usage \
+ "[VARIABLES...]"
+#define printenv_full_usage \
+ "Print all or part of environment.\n" \
+ "If no environment VARIABLE specified, print them all."
+
+#define printf_trivial_usage \
+ "FORMAT [ARGUMENT...]"
+#define printf_full_usage \
+ "Format and print ARGUMENT(s) according to FORMAT,\n" \
+ "where FORMAT controls the output exactly as in C printf"
+#define printf_example_usage \
+ "$ printf \"Val=%d\\n\" 5\n" \
+ "Val=5\n"
+
+
+#if ENABLE_DESKTOP
+
+#define ps_trivial_usage \
+ ""
+#define ps_full_usage \
+ "Report process status" \
+ "\n\nOptions:" \
+ "\n -o col1,col2=header Select columns for display" \
+
+#else /* !ENABLE_DESKTOP */
+
+#if !defined CONFIG_SELINUX && !ENABLE_FEATURE_PS_WIDE
+#define USAGE_PS "\nThis version of ps accepts no options"
+#else
+#define USAGE_PS "\nOptions:"
+#endif
+
+#define ps_trivial_usage \
+ ""
+#define ps_full_usage \
+ "Report process status\n" \
+ USAGE_PS \
+ USE_SELINUX( \
+ "\n -Z Show SE Linux context") \
+ USE_FEATURE_PS_WIDE( \
+ "\n w Wide output")
+
+#endif /* ENABLE_DESKTOP */
+
+#define ps_example_usage \
+ "$ ps\n" \
+ " PID Uid Gid State Command\n" \
+ " 1 root root S init\n" \
+ " 2 root root S [kflushd]\n" \
+ " 3 root root S [kupdate]\n" \
+ " 4 root root S [kpiod]\n" \
+ " 5 root root S [kswapd]\n" \
+ " 742 andersen andersen S [bash]\n" \
+ " 743 andersen andersen S -bash\n" \
+ " 745 root root S [getty]\n" \
+ " 2990 andersen andersen R ps\n"
+
+
+#define pwd_trivial_usage \
+ ""
+#define pwd_full_usage \
+ "Print the full filename of the current working directory"
+#define pwd_example_usage \
+ "$ pwd\n" \
+ "/root\n"
+
+#define raidautorun_trivial_usage \
+ "DEVICE"
+#define raidautorun_full_usage \
+ "Tell the kernel to automatically search and start RAID arrays"
+#define raidautorun_example_usage \
+ "$ raidautorun /dev/md0"
+
+#define rdate_trivial_usage \
+ "[-sp] HOST"
+#define rdate_full_usage \
+ "Get and possibly set the system date and time from a remote HOST" \
+ "\n\nOptions:\n" \
+ " -s Set the system date and time (default)\n" \
+ " -p Print the date and time"
+
+#define readahead_trivial_usage \
+ "[FILE]..."
+#define readahead_full_usage \
+ "Preload FILE(s) in RAM cache so that subsequent reads for those" \
+ "files do not block on disk I/O"
+
+#define readlink_trivial_usage \
+ USE_FEATURE_READLINK_FOLLOW("[-f] ") "FILE"
+#define readlink_full_usage \
+ "Display the value of a symlink" \
+ USE_FEATURE_READLINK_FOLLOW( \
+ "\n\nOptions:\n" \
+ " -f Canonicalize by following all symlinks")
+
+#define readprofile_trivial_usage \
+ "[OPTIONS]..."
+#define readprofile_full_usage \
+ "Options:\n" \
+ " -m <mapfile> (Default: /boot/System.map)\n" \
+ " -p <profile> (Default: /proc/profile)\n" \
+ " -M <mult> Set the profiling multiplier to <mult>\n" \
+ " -i Print only info about the sampling step\n" \
+ " -v Print verbose data\n" \
+ " -a Print all symbols, even if count is 0\n" \
+ " -b Print individual histogram-bin counts\n" \
+ " -s Print individual counters within functions\n" \
+ " -r Reset all the counters (root only)\n" \
+ " -n Disable byte order auto-detection"
+
+#define realpath_trivial_usage \
+ "pathname ..."
+#define realpath_full_usage \
+ "Return the absolute pathnames of given argument"
+
+#define reboot_trivial_usage \
+ "[-d<delay>] [-n<nosync>] [-f<force>]"
+#define reboot_full_usage \
+ "Reboot the system" \
+ "\n\nOptions:\n" \
+ " -d Delay interval for rebooting\n" \
+ " -n No call to sync()\n" \
+ " -f Force reboot (don't go through init)"
+
+#define renice_trivial_usage \
+ "{{-n INCREMENT} | PRIORITY} [[ -p | -g | -u ] ID ...]"
+#define renice_full_usage \
+ "Change priority of running processes" \
+ "\n\nOptions:\n" \
+ " -n Adjusts current nice value (smaller is faster)\n" \
+ " -p Process id(s) (default)\n" \
+ " -g Process group id(s)\n" \
+ " -u Process user name(s) and/or id(s)"
+
+#define reset_trivial_usage \
+ ""
+#define reset_full_usage \
+ "Reset the screen"
+
+#define resize_trivial_usage \
+ ""
+#define resize_full_usage \
+ "Resize the screen"
+
+#define rm_trivial_usage \
+ "[OPTION]... FILE..."
+#define rm_full_usage \
+ "Remove (unlink) the FILE(s). You may use '--' to\n" \
+ "indicate that all following arguments are non-options." \
+ "\n\nOptions:\n" \
+ " -i Always prompt before removing each destination\n" \
+ " -f Remove existing destinations, never prompt\n" \
+ " -r,-R Remove the contents of directories recursively"
+#define rm_example_usage \
+ "$ rm -rf /tmp/foo\n"
+
+#define rmdir_trivial_usage \
+ "[OPTION]... DIRECTORY..."
+#define rmdir_full_usage \
+ "Remove the DIRECTORY, if it is empty"
+#define rmdir_example_usage \
+ "# rmdir /tmp/foo\n"
+
+#define rmmod_trivial_usage \
+ "[OPTION]... [MODULE]..."
+#define rmmod_full_usage \
+ "Unload the specified kernel modules from the kernel" \
+ "\n\nOptions:\n" \
+ " -a Remove all unused modules (recursively)"
+#define rmmod_example_usage \
+ "$ rmmod tulip\n"
+
+#define route_trivial_usage \
+ "[{add|del|delete}]"
+#define route_full_usage \
+ "Edit the kernel's routing tables" \
+ "\n\nOptions:\n" \
+ " -n Dont resolve names\n" \
+ " -e Display other/more information\n" \
+ " -A inet" USE_FEATURE_IPV6("{6}") " Select address family"
+
+#define rpm_trivial_usage \
+ "-i -q[ildc]p package.rpm"
+#define rpm_full_usage \
+ "Manipulate RPM packages" \
+ "\n\nOptions:" \
+ "\n -i Install package" \
+ "\n -q Query package" \
+ "\n -p Query uninstalled package" \
+ "\n -i Show information" \
+ "\n -l List contents" \
+ "\n -d List documents" \
+ "\n -c List config files"
+
+#define rpm2cpio_trivial_usage \
+ "package.rpm"
+#define rpm2cpio_full_usage \
+ "Output a cpio archive of the rpm file"
+
+#define runcon_trivial_usage \
+ "[-c] [-u USER] [-r ROLE] [-t TYPE] [-l RANGE] COMMAND [args]\n" \
+ " runcon CONTEXT COMMAND [args]"
+#define runcon_full_usage \
+ "runcon [-c] [-u USER] [-r ROLE] [-t TYPE] [-l RANGE] COMMAND [args]\n" \
+ "runcon CONTEXT COMMAND [args]\n" \
+ "Run a program in a different security context\n\n" \
+ " CONTEXT Complete security context\n" \
+ " -c, --compute Compute process transition context before modifying\n" \
+ " -t, --type=TYPE Type (for same role as parent)\n" \
+ " -u, --user=USER User identity\n" \
+ " -r, --role=ROLE Role\n" \
+ " -l, --range=RNG Levelrange" \
+
+#define run_parts_trivial_usage \
+ "[-t] [-a ARG] [-u MASK] DIRECTORY"
+#define run_parts_full_usage \
+ "Run a bunch of scripts in a directory" \
+ "\n\nOptions:\n" \
+ " -t Prints what would be run, but does not actually run anything\n" \
+ " -a ARG Pass ARG as an argument for every program invoked\n" \
+ " -u MASK Set the umask to MASK before executing every program"
+
+#define runlevel_trivial_usage \
+ "[utmp]"
+#define runlevel_full_usage \
+ "Find the current and previous system runlevel.\n\n" \
+ "If no utmp file exists or if no runlevel record can be found,\n" \
+ "print \"unknown\""
+#define runlevel_example_usage \
+ "$ runlevel /var/run/utmp\n" \
+ "N 2"
+
+#define runsv_trivial_usage \
+ "dir"
+#define runsv_full_usage \
+ "Start and monitor a service and optionally an appendant log service"
+
+#define runsvdir_trivial_usage \
+ "[-P] dir"
+#define runsvdir_full_usage \
+ "Start a runsv process for each subdirectory"
+
+#define rx_trivial_usage \
+ "FILE"
+#define rx_full_usage \
+ "Receive a file using the xmodem protocol"
+#define rx_example_usage \
+ "$ rx /tmp/foo\n"
+
+#define sed_trivial_usage \
+ "[-efinr] pattern [files...]"
+#define sed_full_usage \
+ "Options:\n" \
+ " -e script Add the script to the commands to be executed\n" \
+ " -f scriptfile Add script-file contents to the\n" \
+ " commands to be executed\n" \
+ " -i Edit files in-place\n" \
+ " -n Suppress automatic printing of pattern space\n" \
+ " -r Use extended regular expression syntax\n" \
+ "\n" \
+ "If no -e or -f is given, the first non-option argument is taken as the sed\n" \
+ "script to interpret. All remaining arguments are names of input files; if no\n" \
+ "input files are specified, then the standard input is read. Source files\n" \
+ "will not be modified unless -i option is given."
+
+#define sed_example_usage \
+ "$ echo \"foo\" | sed -e 's/f[a-zA-Z]o/bar/g'\n" \
+ "bar\n"
+
+#define selinuxenabled_trivial_usage
+#define selinuxenabled_full_usage
+
+#define seq_trivial_usage \
+ "[first [increment]] last"
+#define seq_full_usage \
+ "Print numbers from FIRST to LAST, in steps of INCREMENT.\n" \
+ "FIRST, INCREMENT default to 1" \
+ "\n\nArguments:\n" \
+ " LAST\n" \
+ " FIRST LAST\n" \
+ " FIRST INCREMENT LAST"
+
+#define setconsole_trivial_usage \
+ "[-r|--reset] [DEVICE]"
+#define setconsole_full_usage \
+ "Redirect system console output to DEVICE (default: /dev/tty)" \
+ "\n\nOptions:\n" \
+ " -r Reset output to /dev/console"
+
+#define setenforce_trivial_usage \
+ "[ Enforcing | Permissive | 1 | 0 ]"
+#define setenforce_full_usage
+
+#define setkeycodes_trivial_usage \
+ "SCANCODE KEYCODE ..."
+#define setkeycodes_full_usage \
+ "Set entries into the kernel's scancode-to-keycode map,\n" \
+ "allowing unusual keyboards to generate usable keycodes.\n\n" \
+ "SCANCODE may be either xx or e0xx (hexadecimal),\n" \
+ "and KEYCODE is given in decimal"
+#define setkeycodes_example_usage \
+ "$ setkeycodes e030 127\n"
+
+#define setlogcons_trivial_usage \
+ "N"
+#define setlogcons_full_usage \
+ "Redirect the kernel output to console N (0 for current)"
+
+#define setsid_trivial_usage \
+ "program [arg ...]"
+#define setsid_full_usage \
+ "Run any program in a new session by calling setsid() before\n" \
+ "exec'ing the rest of its arguments. See setsid(2) for details."
+
+#define lash_trivial_usage \
+ "[FILE]...\n" \
+ "or: sh -c command [args]..."
+#define lash_full_usage \
+ "The BusyBox LAme SHell (command interpreter)"
+#define lash_notes_usage \
+ "This command does not yet have proper documentation.\n\n" \
+ "Use lash just as you would use any other shell. It properly handles pipes,\n" \
+ "redirects, job control, can be used as the shell for scripts, and has a\n" \
+ "sufficient set of builtins to do what is needed. It does not (yet) support\n" \
+ "Bourne Shell syntax. If you need things like \"if-then-else\", \"while\", and such\n" \
+ "use ash or bash. If you just need a very simple and extremely small shell,\n" \
+ "this will do the job."
+
+#define last_trivial_usage \
+ ""
+#define last_full_usage \
+ "Show listing of the last users that logged into the system"
+
+#define sha1sum_trivial_usage \
+ "[OPTION] [FILEs...]" \
+ USE_FEATURE_MD5_SHA1_SUM_CHECK("\n or: sha1sum [OPTION] -c [FILE]")
+#define sha1sum_full_usage \
+ "Print" USE_FEATURE_MD5_SHA1_SUM_CHECK(" or check") " SHA1 checksums.\n" \
+ "With no FILE, or when FILE is -, read standard input." \
+ "\n\nOptions:\n" \
+ USE_FEATURE_MD5_SHA1_SUM_CHECK( \
+ " -c Check SHA1 sums against given list\n" \
+ "\nThe following two options are useful only when verifying checksums:\n" \
+ " -s Don't output anything, status code shows success\n" \
+ " -w Warn about improperly formatted SHA1 checksum lines")
+
+#define sleep_trivial_usage \
+ USE_FEATURE_FANCY_SLEEP("[") "N" USE_FEATURE_FANCY_SLEEP("]...")
+#define sleep_full_usage \
+ SKIP_FEATURE_FANCY_SLEEP("Pause for N seconds") \
+ USE_FEATURE_FANCY_SLEEP( \
+ "Pause for a time equal to the total of the args given, where each arg can\n" \
+ "have an optional suffix of (s)econds, (m)inutes, (h)ours, or (d)ays")
+#define sleep_example_usage \
+ "$ sleep 2\n" \
+ "[2 second delay results]\n" \
+ USE_FEATURE_FANCY_SLEEP( \
+ "$ sleep 1d 3h 22m 8s\n" \
+ "[98528 second delay results]\n")
+
+#define sort_trivial_usage \
+ "[-nru" \
+ USE_FEATURE_SORT_BIG("gMcszbdfimSTokt] [-o outfile] [-k start[.offset][opts][,end[.offset][opts]] [-t char") \
+ "] [FILE]..."
+#define sort_full_usage \
+ "Sort lines of text in the specified files" \
+ "\n\nOptions:\n" \
+ USE_FEATURE_SORT_BIG( \
+ " -b Ignore leading blanks\n" \
+ " -c Check whether input is sorted\n" \
+ " -d Dictionary order (blank or alphanumeric only)\n" \
+ " -f Ignore case\n" \
+ " -g General numerical sort\n" \
+ " -i Ignore unprintable characters\n" \
+ " -k Specify sort key\n" \
+ " -M Sort month\n") \
+ " -n Sort numbers\n" \
+ USE_FEATURE_SORT_BIG( \
+ " -o Output to file\n" \
+ " -k Sort by key\n" \
+ " -t Use key separator other than whitespace\n") \
+ " -r Reverse sort order\n" \
+ USE_FEATURE_SORT_BIG( \
+ " -s Stable (don't sort ties alphabetically)\n") \
+ " -u Suppress duplicate lines" \
+ USE_FEATURE_SORT_BIG( \
+ "\n -z Input terminated by nulls, not newlines\n") \
+ USE_FEATURE_SORT_BIG( \
+ " -mST Ignored for GNU compatibility") \
+ ""
+#define sort_example_usage \
+ "$ echo -e \"e\\nf\\nb\\nd\\nc\\na\" | sort\n" \
+ "a\n" \
+ "b\n" \
+ "c\n" \
+ "d\n" \
+ "e\n" \
+ "f\n" \
+ USE_FEATURE_SORT_BIG( \
+ "$ echo -e \"c 3\\nb 2\\nd 2\" | $SORT -k 2,2n -k 1,1r\n" \
+ "d 2\n" \
+ "b 2\n" \
+ "c 3\n" \
+ ) \
+ ""
+
+#define start_stop_daemon_trivial_usage \
+ "[OPTIONS] [--start|--stop] ... [-- arguments...]"
+#define start_stop_daemon_full_usage \
+ "Start and stop services" \
+ "\n\nOptions:" \
+ "\n -S|--start Start" \
+ "\n -K|--stop Stop" \
+ "\n -a|--startas <pathname> Starts process specified by pathname" \
+ "\n -b|--background Force process into background" \
+ "\n -u|--user <username>|<uid> Stop this user's processes" \
+ "\n -x|--exec <executable> Program to either start or check" \
+ "\n -m|--make-pidfile Create the -p file and enter pid in it" \
+ "\n -n|--name <process-name> Stop processes with this name" \
+ "\n -p|--pidfile <pid-file> Save or load pid using a pid-file" \
+ "\n -q|--quiet Be quiet" \
+ USE_FEATURE_START_STOP_DAEMON_FANCY( \
+ "\n -o|--oknodo Exit status 0 if nothing done" \
+ "\n -v|--verbose Be verbose" \
+ "\n -N|--nicelevel <N> Add N to process's nice level" \
+ ) \
+ "\n -s|--signal <signal> Signal to send (default TERM)" \
+ "\n -c|--chuid <user>[:[<group>]] Change to specified user/group"
+
+#define stat_trivial_usage \
+ "[OPTION] FILE..."
+#define stat_full_usage \
+ "Display file (default) or filesystem status" \
+ "\n\nOptions:\n" \
+ USE_FEATURE_STAT_FORMAT( \
+ " -c fmt Use the specified format\n") \
+ " -f Display filesystem status\n" \
+ " -L,-l Dereference links\n" \
+ " -t Display info in terse form" \
+ USE_SELINUX( \
+ "\n -Z Print security context" \
+ ) \
+ USE_FEATURE_STAT_FORMAT( \
+ "\n\nValid format sequences for files:\n" \
+ " %a Access rights in octal\n" \
+ " %A Access rights in human readable form\n" \
+ " %b Number of blocks allocated (see %B)\n" \
+ " %B The size in bytes of each block reported by %b\n" \
+ " %d Device number in decimal\n" \
+ " %D Device number in hex\n" \
+ " %f Raw mode in hex\n" \
+ " %F File type\n" \
+ " %g Group ID of owner\n" \
+ " %G Group name of owner\n" \
+ " %h Number of hard links\n" \
+ " %i Inode number\n" \
+ " %n File name\n" \
+ " %N Quoted file name with dereference if symlink\n" \
+ " %o I/O block size\n" \
+ " %s Total size, in bytes\n" \
+ " %t Major device type in hex\n" \
+ " %T Minor device type in hex\n" \
+ " %u User ID of owner\n" \
+ " %U User name of owner\n" \
+ " %x Time of last access\n" \
+ " %X Time of last access as seconds since Epoch\n" \
+ " %y Time of last modification\n" \
+ " %Y Time of last modification as seconds since Epoch\n" \
+ " %z Time of last change\n" \
+ " %Z Time of last change as seconds since Epoch\n" \
+ "\nValid format sequences for file systems:\n" \
+ " %a Free blocks available to non-superuser\n" \
+ " %b Total data blocks in file system\n" \
+ " %c Total file nodes in file system\n" \
+ " %d Free file nodes in file system\n" \
+ " %f Free blocks in file system\n" \
+ USE_SELINUX( \
+ " %C Security context in SELinux\n" \
+ ) \
+ " %i File System ID in hex\n" \
+ " %l Maximum length of filenames\n" \
+ " %n File name\n" \
+ " %s Block size (for faster transfers)\n" \
+ " %S Fundamental block size (for block counts)\n" \
+ " %t Type in hex\n" \
+ " %T Type in human readable form" \
+ )
+
+#define strings_trivial_usage \
+ "[-afo] [-n length] [file ... ]"
+#define strings_full_usage \
+ "Display printable strings in a binary file" \
+ "\n\nOptions:" \
+ "\n -a Scan the whole files (this is the default)" \
+ "\n -f Precede each string with the name of the file where it was found" \
+ "\n -n N Specifies that at least N characters forms a sequence (default 4)" \
+ "\n -o Each string is preceded by its decimal offset in the file"
+
+#define stty_trivial_usage \
+ "[-a|g] [-F DEVICE] [SETTING]..."
+#define stty_full_usage \
+ "Without arguments, prints baud rate, line discipline,\n" \
+ "and deviations from stty sane" \
+ "\n\nOptions:" \
+ "\n -F DEVICE Open device instead of stdin" \
+ "\n -a Print all current settings in human-readable form" \
+ "\n -g Print in stty-readable form" \
+ "\n [SETTING] See manpage"
+
+#define su_trivial_usage \
+ "[OPTION]... [-] [username]"
+#define su_full_usage \
+ "Change user id or become root" \
+ "\n\nOptions:" \
+ "\n -p, -m Preserve environment" \
+ "\n -c Command to pass to 'sh -c'" \
+ "\n -s Shell to use instead of default shell"
+
+#define sulogin_trivial_usage \
+ "[OPTION]... [tty-device]"
+#define sulogin_full_usage \
+ "Single user login" \
+ "\n\nOptions:" \
+ "\n -t Timeout"
+
+#define sum_trivial_usage \
+ "[rs] [files...]"
+#define sum_full_usage \
+ "Checksum and count the blocks in a file" \
+ "\n\nOptions:\n" \
+ " -r Use BSD sum algorithm (1K blocks)\n" \
+ " -s Use System V sum algorithm (512byte blocks)"
+
+#define sv_trivial_usage \
+ "[-v] [-w sec] command service..."
+#define sv_full_usage \
+ "Report the current status and control the state of services " \
+ "monitored by the runsv supervisor"
+
+#define svlogd_trivial_usage \
+ "[-ttv] [-r c] [-R abc] [-l len] [-b buflen] dir..."
+#define svlogd_full_usage \
+ "Continuously read log data from standard input, optionally " \
+ "filter log messages, and write the data to one or more automatically " \
+ "rotated logs"
+
+#define swapoff_trivial_usage \
+ "[-a] [DEVICE]"
+#define swapoff_full_usage \
+ "Stop swapping virtual memory pages on DEVICE" \
+ "\n\nOptions:\n" \
+ " -a Stop swapping on all swap devices"
+
+#define swapon_trivial_usage \
+ "[-a] [DEVICE]"
+#define swapon_full_usage \
+ "Start swapping virtual memory pages on DEVICE" \
+ "\n\nOptions:\n" \
+ " -a Start swapping on all swap devices"
+
+#define switch_root_trivial_usage \
+ "[-c /dev/console] NEW_ROOT NEW_INIT [ARGUMENTS_TO_INIT]"
+#define switch_root_full_usage \
+ "Use from PID 1 under initramfs to free initramfs, chroot to NEW_ROOT,\n" \
+ "and exec NEW_INIT" \
+ "\n\nOptions:\n" \
+ " -c Redirect console to device on new root"
+
+#define sync_trivial_usage \
+ ""
+#define sync_full_usage \
+ "Write all buffered filesystem blocks to disk"
+
+#define sysctl_trivial_usage \
+ "[OPTIONS]... [VALUE]..."
+#define sysctl_full_usage \
+ "Configure kernel parameters at runtime" \
+ "\n\nOptions:\n" \
+ " -n Use this option to disable printing of the key name when printing values\n" \
+ " -w Use this option when you want to change a sysctl setting\n" \
+ " -p Load in sysctl settings from the file specified or /etc/sysctl.conf if none given\n" \
+ " -a Display all values currently available\n" \
+ " -A Display all values currently available in table form"
+#define sysctl_example_usage \
+ "sysctl [-n] variable ...\n" \
+ "sysctl [-n] -w variable=value ...\n" \
+ "sysctl [-n] -a\n" \
+ "sysctl [-n] -p <file> (default /etc/sysctl.conf)\n" \
+ "sysctl [-n] -A\n"
+
+#define syslogd_trivial_usage \
+ "[OPTION]..."
+#define syslogd_full_usage \
+ "System logging utility.\n" \
+ "Note that this version of syslogd ignores /etc/syslog.conf." \
+ "\n\nOptions:" \
+ "\n -n Run as foreground process" \
+ "\n -O FILE Use an alternate log file (default=/var/log/messages)" \
+ "\n -l n Sets the local log level of messages to n" \
+ "\n -S Make logging output smaller" \
+ USE_FEATURE_ROTATE_LOGFILE( \
+ "\n -s SIZE Max size (KB) before rotate (default=200KB, 0=off)" \
+ "\n -b NUM Number of rotated logs to keep (default=1, max=99, 0=purge)") \
+ USE_FEATURE_REMOTE_LOG( \
+ "\n -R HOST[:PORT] Log to IP or hostname on PORT (default PORT=514/UDP)" \
+ "\n -L Log locally and via network logging (default is network only)") \
+ USE_FEATURE_IPC_SYSLOG( \
+ "\n -C[size(KiB)] Log to a shared mem buffer (read the buffer using logread)")
+ /* NB: -Csize shouldn't have space (because size is optional) */
+/* "\n -m MIN Minutes between MARK lines (default=20, 0=off)" */
+#define syslogd_example_usage \
+ "$ syslogd -R masterlog:514\n" \
+ "$ syslogd -R 192.168.1.1:601\n"
+
+#define tail_trivial_usage \
+ "[OPTION]... [FILE]..."
+#define tail_full_usage \
+ "Print last 10 lines of each FILE to standard output.\n" \
+ "With more than one FILE, precede each with a header giving the\n" \
+ "file name. With no FILE, or when FILE is -, read standard input." \
+ "\n\nOptions:" \
+ USE_FEATURE_FANCY_TAIL( \
+ "\n -c N[kbm] Output the last N bytes") \
+ "\n -n N[kbm] Print last N lines instead of last 10" \
+ "\n -f Output data as the file grows" \
+ USE_FEATURE_FANCY_TAIL( \
+ "\n -q Never output headers giving file names" \
+ "\n -s SEC Wait SEC seconds between reads with -f" \
+ "\n -v Always output headers giving file names" \
+ "\n\n" \
+ "If the first character of N (bytes or lines) is a '+', output begins with\n" \
+ "the Nth item from the start of each file, otherwise, print the last N items\n" \
+ "in the file. N bytes may be suffixed by k (x1024), b (x512), or m (1024^2)." )
+#define tail_example_usage \
+ "$ tail -n 1 /etc/resolv.conf\n" \
+ "nameserver 10.0.0.1\n"
+
+#define tar_trivial_usage \
+ "-[" USE_FEATURE_TAR_CREATE("c") USE_FEATURE_TAR_GZIP("z") \
+ USE_FEATURE_TAR_BZIP2("j") USE_FEATURE_TAR_LZMA("a") \
+ USE_FEATURE_TAR_COMPRESS("Z") "xtvO] " \
+ USE_FEATURE_TAR_FROM("[-X FILE] ") \
+ "[-f TARFILE] [-C DIR] [FILE(s)] ..."
+#define tar_full_usage \
+ "Create, extract, or list files from a tar file" \
+ "\n\nOptions:\n" \
+ USE_FEATURE_TAR_CREATE( \
+ " c Create\n") \
+ " x Extract\n" \
+ " t List\n" \
+ "\nArchive format selection:\n" \
+ USE_FEATURE_TAR_GZIP( \
+ " z Filter the archive through gzip\n") \
+ USE_FEATURE_TAR_BZIP2( \
+ " j Filter the archive through bzip2\n") \
+ USE_FEATURE_TAR_LZMA( \
+ " a Filter the archive through lzma\n") \
+ USE_FEATURE_TAR_COMPRESS( \
+ " Z Filter the archive through compress\n") \
+ "\nFile selection:\n" \
+ " f Name of TARFILE or \"-\" for stdin\n" \
+ " O Extract to stdout\n" \
+ USE_FEATURE_TAR_FROM( \
+ " exclude File to exclude\n" \
+ " X File with names to exclude\n") \
+ " C Change to directory DIR before operation\n" \
+ " v Verbosely list files processed"
+#define tar_example_usage \
+ "$ zcat /tmp/tarball.tar.gz | tar -xf -\n" \
+ "$ tar -cf /tmp/tarball.tar /usr/local\n"
+
+#define taskset_trivial_usage \
+ "[OPTIONS] [mask] [pid | command [arg]...]"
+#define taskset_full_usage \
+ "Set or get CPU affinity" \
+ "\n\nOptions:\n" \
+ " -p Operate on an existing PID"
+#define taskset_example_usage \
+ "$ taskset 0x7 ./dgemm_test&\n" \
+ "$ taskset -p 0x1 $!\n" \
+ "pid 4790's current affinity mask: 7\n" \
+ "pid 4790's new affinity mask: 1\n" \
+ "$ taskset 0x7 /bin/sh -c './taskset -p 0x1 $$'\n" \
+ "pid 6671's current affinity mask: 1\n" \
+ "pid 6671's new affinity mask: 1\n" \
+ "$ taskset -p 1\n" \
+ "pid 1's current affinity mask: 3\n"
+
+#define tee_trivial_usage \
+ "[OPTION]... [FILE]..."
+#define tee_full_usage \
+ "Copy standard input to each FILE, and also to standard output" \
+ "\n\nOptions:\n" \
+ " -a Append to the given FILEs, do not overwrite\n" \
+ " -i Ignore interrupt signals (SIGINT)"
+#define tee_example_usage \
+ "$ echo \"Hello\" | tee /tmp/foo\n" \
+ "$ cat /tmp/foo\n" \
+ "Hello\n"
+
+#ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN
+#define telnet_trivial_usage \
+ "[-a] [-l USER] HOST [PORT]"
+#define telnet_full_usage \
+ "Connect to remote telnet server" \
+ "\n\nOptions:\n" \
+ " -a Attempt an automatic login with the USER variable\n" \
+ " -l USER Attempt an automatic login with the USER argument\n" \
+ " HOST The official name, alias or the IP address of the\n" \
+ " remote host\n" \
+ " PORT The remote port number to connect to. If it is not\n" \
+ " specified, the default telnet (23) port is used."
+#else
+#define telnet_trivial_usage \
+ "HOST [PORT]"
+#define telnet_full_usage \
+ "Connect to remote telnet server"
+#endif
+
+#define telnetd_trivial_usage \
+ "[OPTION]"
+#define telnetd_full_usage \
+ "Handle incoming telnet connections" \
+ SKIP_FEATURE_TELNETD_STANDALONE(" via inetd") \
+ "\n\nOptions:" \
+ "\n -l LOGIN Exec LOGIN on connect" \
+ "\n -f issue_file Display issue_file instead of /etc/issue" \
+ USE_FEATURE_TELNETD_STANDALONE( \
+ "\n -p PORT Port to listen to" \
+ "\n -b ADDR Address to bind to" \
+ "\n -F Stay in foreground" \
+ "\n -i Run as inetd subservice" \
+ )
+
+#define test_trivial_usage \
+ "EXPRESSION\n or [ EXPRESSION ]"
+#define test_full_usage \
+ "Check file types and compares values returning an exit code\n" \
+ "determined by the value of EXPRESSION"
+#define test_example_usage \
+ "$ test 1 -eq 2\n" \
+ "$ echo $?\n" \
+ "1\n" \
+ "$ test 1 -eq 1\n" \
+ "$ echo $?\n" \
+ "0\n" \
+ "$ [ -d /etc ]\n" \
+ "$ echo $?\n" \
+ "0\n" \
+ "$ [ -d /junk ]\n" \
+ "$ echo $?\n" \
+ "1\n"
+
+#define tftp_trivial_usage \
+ "[OPTION]... HOST [PORT]"
+#define tftp_full_usage \
+ "Transfer a file from/to tftp server using \"octet\" mode" \
+ "\n\nOptions:" \
+ "\n -l FILE Local FILE" \
+ "\n -r FILE Remote FILE" \
+ USE_FEATURE_TFTP_GET( \
+ "\n -g Get file" \
+ ) \
+ USE_FEATURE_TFTP_PUT( \
+ "\n -p Put file" \
+ ) \
+ USE_FEATURE_TFTP_BLOCKSIZE( \
+ "\n -b SIZE Transfer blocks of SIZE octets" \
+ )
+#define time_trivial_usage \
+ "[OPTION]... COMMAND [ARGS...]"
+#define time_full_usage \
+ "Run the program COMMAND with arguments ARGS. When COMMAND finishes,\n" \
+ "COMMAND's resource usage information is displayed." \
+ "\n\nOptions:\n" \
+ " -v Display verbose resource usage information"
+
+#define top_trivial_usage \
+ "[-b] [-n count] [-d seconds]"
+#define top_full_usage \
+ "Provide a view of process activity in real time.\n" \
+ "Read the status of all processes from /proc each <seconds>\n" \
+ "and show the status for however many processes will fit on the screen."
+
+#define touch_trivial_usage \
+ "[-c] FILE [FILE ...]"
+#define touch_full_usage \
+ "Update the last-modified date on the given FILE[s]" \
+ "\n\nOptions:\n" \
+ " -c Do not create any files"
+#define touch_example_usage \
+ "$ ls -l /tmp/foo\n" \
+ "/bin/ls: /tmp/foo: No such file or directory\n" \
+ "$ touch /tmp/foo\n" \
+ "$ ls -l /tmp/foo\n" \
+ "-rw-rw-r-- 1 andersen andersen 0 Apr 15 01:11 /tmp/foo\n"
+
+#define tr_trivial_usage \
+ "[-cds] STRING1 [STRING2]"
+#define tr_full_usage \
+ "Translate, squeeze, and/or delete characters from\n" \
+ "standard input, writing to standard output" \
+ "\n\nOptions:\n" \
+ " -c Take complement of STRING1\n" \
+ " -d Delete input characters coded STRING1\n" \
+ " -s Squeeze multiple output characters of STRING2 into one character"
+#define tr_example_usage \
+ "$ echo \"gdkkn vnqkc\" | tr [a-y] [b-z]\n" \
+ "hello world\n"
+
+#define traceroute_trivial_usage \
+ "[-FIldnrv] [-f 1st_ttl] [-m max_ttl] [-p port#] [-q nqueries]\n" \
+ " [-s src_addr] [-t tos] [-w wait] [-g gateway] [-i iface]\n" \
+ " [-z pausemsecs] host [data size]"
+#define traceroute_full_usage \
+ "Trace the route ip packets follow going to \"host\"" \
+ "\n\nOptions:\n" \
+ " -F Set the don't fragment bit\n" \
+ " -I Use ICMP ECHO instead of UDP datagrams\n" \
+ " -l Display the ttl value of the returned packet\n" \
+ " -d Set SO_DEBUG options to socket\n" \
+ " -n Print hop addresses numerically rather than symbolically\n" \
+ " -r Bypass the normal routing tables and send directly to a host\n" \
+ " -v Verbose output\n" \
+ " -m max_ttl Set the max time-to-live (max number of hops)\n" \
+ " -p port# Set the base UDP port number used in probes\n" \
+ " (default is 33434)\n" \
+ " -q nqueries Set the number of probes per 'ttl' to nqueries\n" \
+ " (default is 3)\n" \
+ " -s src_addr Use the following IP address as the source address\n" \
+ " -t tos Set the type-of-service in probe packets to the following value\n" \
+ " (default 0)\n" \
+ " -w wait Set the time (in seconds) to wait for a response to a probe\n" \
+ " (default 3 sec)\n" \
+ " -g Specify a loose source route gateway (8 maximum)"
+
+
+#define true_trivial_usage \
+ ""
+#define true_full_usage \
+ "Return an exit code of TRUE (0)"
+#define true_example_usage \
+ "$ true\n" \
+ "$ echo $?\n" \
+ "0\n"
+
+#define tty_trivial_usage \
+ ""
+#define tty_full_usage \
+ "Print the file name of the terminal connected to standard input" \
+ USE_INCLUDE_SUSv2( \
+ "\n\nOptions:\n" \
+ " -s Print nothing, only return an exit status")
+#define tty_example_usage \
+ "$ tty\n" \
+ "/dev/tty2\n"
+
+#define tune2fs_trivial_usage \
+ "[-c max-mounts-count] [-e errors-behavior] [-g group] " \
+ "[-i interval[d|m|w]] [-j] [-J journal-options] [-l] [-s sparse-flag] " \
+ "[-m reserved-blocks-percent] [-o [^]mount-options[,...]] " \
+ "[-r reserved-blocks-count] [-u user] [-C mount-count] " \
+ "[-L volume-label] [-M last-mounted-dir] [-O [^]feature[,...]] " \
+ "[-T last-check-time] [-U UUID] device"
+#define tune2fs_full_usage \
+ "Adjust filesystem options on ext[23] filesystems"
+
+#define udhcpc_trivial_usage \
+ "[-Cfbnqtv] [-c CID] [-V VCLS] [-H HOSTNAME] [-i INTERFACE]\n[-p pidfile] [-r IP] [-s script]"
+#define udhcpc_full_usage \
+ " -V,--vendorclass=CLASSID Set vendor class identifier\n" \
+ " -i,--interface=INTERFACE Interface to use (default: eth0)\n" \
+ " -H,-h,--hostname=HOSTNAME Client hostname\n" \
+ " -c,--clientid=CLIENTID Set client identifier\n" \
+ " -C,--clientid-none Suppress default client identifier\n" \
+ " -p,--pidfile=file Store process ID of daemon in file\n" \
+ " -r,--request=IP IP address to request (default: none)\n" \
+ " -s,--script=file Run file at dhcp events (default: /usr/share/udhcpc/default.script)\n" \
+ " -t,--retries=NUM Send up to NUM request packets\n"\
+ " -f,--foreground Do not fork after getting lease\n" \
+ " -b,--background Fork to background if lease cannot be immediately negotiated\n" \
+ " -n,--now Exit with failure if lease cannot be immediately negotiated\n" \
+ " -q,--quit Quit after obtaining lease\n" \
+ " -R,--release Release IP on quit\n" \
+ " -v,--version Display version" \
+
+#define udhcpd_trivial_usage \
+ "[configfile]\n" \
+
+#define udhcpd_full_usage \
+ ""
+
+#define umount_trivial_usage \
+ "[flags] FILESYSTEM|DIRECTORY"
+#define umount_full_usage \
+ "Unmount file systems" \
+ "\n\nOptions:\n" \
+ USE_FEATURE_UMOUNT_ALL( \
+ "\n -a Unmount all file systems" USE_FEATURE_MTAB_SUPPORT(" in /etc/mtab")) \
+ USE_FEATURE_MTAB_SUPPORT( \
+ "\n -n Don't erase /etc/mtab entries") \
+ "\n -r Try to remount devices as read-only if mount is busy" \
+ "\n -l Lazy umount (detach filesystem)" \
+ "\n -f Force umount (i.e., unreachable NFS server)" \
+ USE_FEATURE_MOUNT_LOOP( \
+ "\n -D Do not free loop device (if a loop device has been used)")
+#define umount_example_usage \
+ "$ umount /dev/hdc1\n"
+
+#define uname_trivial_usage \
+ "[OPTION]..."
+#define uname_full_usage \
+ "Print certain system information. With no OPTION, same as -s." \
+ "\n\nOptions:\n" \
+ " -a Print all information\n" \
+ " -m The machine (hardware) type\n" \
+ " -n Print the machine's network node hostname\n" \
+ " -r Print the operating system release\n" \
+ " -s Print the operating system name\n" \
+ " -p Print the host processor type\n" \
+ " -v Print the operating system version"
+#define uname_example_usage \
+ "$ uname -a\n" \
+ "Linux debian 2.4.23 #2 Tue Dec 23 17:09:10 MST 2003 i686 GNU/Linux\n"
+
+#define uncompress_trivial_usage \
+ "[-c] [-f] [ name ... ]"
+#define uncompress_full_usage \
+ "Uncompress .Z file[s]" \
+ "\n\nOptions:\n" \
+ " -c Extract to stdout\n" \
+ " -f Force overwrite an existing file"
+
+#define uniq_trivial_usage \
+ "[-fscdu]... [INPUT [OUTPUT]]"
+#define uniq_full_usage \
+ "Discard all but one of successive identical lines from INPUT\n" \
+ "(or standard input), writing to OUTPUT (or standard output)" \
+ "\n\nOptions:\n" \
+ " -c Prefix lines by the number of occurrences\n" \
+ " -d Only print duplicate lines\n" \
+ " -u Only print unique lines\n" \
+ " -f N Skip the first N fields\n" \
+ " -s N Skip the first N chars (after any skipped fields)"
+#define uniq_example_usage \
+ "$ echo -e \"a\\na\\nb\\nc\\nc\\na\" | sort | uniq\n" \
+ "a\n" \
+ "b\n" \
+ "c\n"
+
+#define unix2dos_trivial_usage \
+ "[option] [FILE]"
+#define unix2dos_full_usage \
+ "Convert FILE from unix format to dos format. When no option\n" \
+ "is given, the input is converted to the opposite output format.\n" \
+ "When no file is given, use stdin for input and stdout for output." \
+ "\n\nOptions:\n" \
+ " -u Output will be in UNIX format\n" \
+ " -d Output will be in DOS format"
+
+#define unzip_trivial_usage \
+ "[-opts[modifiers]] file[.zip] [list] [-x xlist] [-d exdir]"
+#define unzip_full_usage \
+ "Extract files from ZIP archives" \
+ "\n\nOptions:\n" \
+ " -l List archive contents (short form)\n" \
+ " -n Never overwrite existing files (default)\n" \
+ " -o Overwrite files without prompting\n" \
+ " -p Send output to stdout\n" \
+ " -q Be quiet\n" \
+ " -x Exclude these files\n" \
+ " -d Extract files into this directory"
+
+#define uptime_trivial_usage \
+ ""
+#define uptime_full_usage \
+ "Display the time since the last boot"
+#define uptime_example_usage \
+ "$ uptime\n" \
+ " 1:55pm up 2:30, load average: 0.09, 0.04, 0.00\n"
+
+#define usleep_trivial_usage \
+ "N"
+#define usleep_full_usage \
+ "Pause for N microseconds"
+#define usleep_example_usage \
+ "$ usleep 1000000\n" \
+ "[pauses for 1 second]\n"
+
+#define uudecode_trivial_usage \
+ "[FILE]..."
+#define uudecode_full_usage \
+ "Uudecode a file" \
+ "\n\nOptions:\n" \
+ " -o FILE Direct output to FILE"
+#define uudecode_example_usage \
+ "$ uudecode -o busybox busybox.uu\n" \
+ "$ ls -l busybox\n" \
+ "-rwxr-xr-x 1 ams ams 245264 Jun 7 21:35 busybox\n"
+
+#define uuencode_trivial_usage \
+ "[OPTION] [INFILE] REMOTEFILE"
+#define uuencode_full_usage \
+ "Uuencode a file" \
+ "\n\nOptions:\n" \
+ " -m Use base64 encoding per RFC1521"
+#define uuencode_example_usage \
+ "$ uuencode busybox busybox\n" \
+ "begin 755 busybox\n" \
+ "<encoded file snipped>\n" \
+ "$ uudecode busybox busybox > busybox.uu\n" \
+ "$\n"
+
+#define vconfig_trivial_usage \
+ "COMMAND [OPTIONS] ..."
+#define vconfig_full_usage \
+ "Create and remove virtual ethernet devices" \
+ "\n\nOptions:\n" \
+ " add [interface-name] [vlan_id]\n" \
+ " rem [vlan-name]\n" \
+ " set_flag [interface-name] [flag-num] [0 | 1]\n" \
+ " set_egress_map [vlan-name] [skb_priority] [vlan_qos]\n" \
+ " set_ingress_map [vlan-name] [skb_priority] [vlan_qos]\n" \
+ " set_name_type [name-type]"
+
+#define vi_trivial_usage \
+ "[OPTION] [FILE]..."
+#define vi_full_usage \
+ "Edit FILE" \
+ "\n\nOptions:\n" \
+ " -R Read-only - do not write to the file"
+
+#define vlock_trivial_usage \
+ "[OPTIONS]"
+#define vlock_full_usage \
+ "Lock a virtual terminal. A password is required to unlock." \
+ "\n\nOptions:\n" \
+ " -a Lock all VTs"
+
+#define watch_trivial_usage \
+ "[-n <seconds>] [-t] COMMAND..."
+#define watch_full_usage \
+ "Execute a program periodically" \
+ "\n\nOptions:\n" \
+ " -n Loop period in seconds - default is 2\n" \
+ " -t Don't print header"
+#define watch_example_usage \
+ "$ watch date\n" \
+ "Mon Dec 17 10:31:40 GMT 2000\n" \
+ "Mon Dec 17 10:31:42 GMT 2000\n" \
+ "Mon Dec 17 10:31:44 GMT 2000"
+
+#define watchdog_trivial_usage \
+ "[-t <seconds>] [-F] DEV"
+#define watchdog_full_usage \
+ "Periodically write to watchdog device DEV" \
+ "\n\nOptions:\n" \
+ " -t Timer period in seconds - default is 30\n" \
+ " -F Stay in the foreground and don't fork"
+
+#define wc_trivial_usage \
+ "[OPTION]... [FILE]..."
+#define wc_full_usage \
+ "Print line, word, and byte counts for each FILE, and a total line if\n" \
+ "more than one FILE is specified. With no FILE, read standard input." \
+ "\n\nOptions:\n" \
+ " -c Print the byte counts\n" \
+ " -l Print the newline counts\n" \
+ " -L Print the length of the longest line\n" \
+ " -w Print the word counts"
+#define wc_example_usage \
+ "$ wc /etc/passwd\n" \
+ " 31 46 1365 /etc/passwd\n"
+
+#define wget_trivial_usage \
+ "[-c|--continue] [-q|--quiet] [-O|--output-document file]\n" \
+ " [--header 'header: value'] [-Y|--proxy on/off] [-P DIR]\n" \
+ " [-U|--user-agent agent] url"
+#define wget_full_usage \
+ "Retrieve files via HTTP or FTP" \
+ "\n\nOptions:\n" \
+ " -c Continue retrieval of aborted transfers\n" \
+ " -q Quiet mode - do not print\n" \
+ " -P Set directory prefix to DIR\n" \
+ " -O Save to filename ('-' for stdout)\n" \
+ " -U Adjust 'User-Agent' field\n" \
+ " -Y Use proxy ('on' or 'off')"
+
+#define which_trivial_usage \
+ "[COMMAND ...]"
+#define which_full_usage \
+ "Locate a COMMAND"
+#define which_example_usage \
+ "$ which login\n" \
+ "/bin/login\n"
+
+#define who_trivial_usage \
+ " "
+#define who_full_usage \
+ "Print the current user names and related information"
+
+#define whoami_trivial_usage \
+ ""
+#define whoami_full_usage \
+ "Print the user name associated with the current effective user id"
+
+#define xargs_trivial_usage \
+ "[OPTIONS] [COMMAND] [ARGS...]"
+#define xargs_full_usage \
+ "Execute COMMAND on every item given by standard input" \
+ "\n\nOptions:\n" \
+ USE_FEATURE_XARGS_SUPPORT_CONFIRMATION( \
+ " -p Prompt the user about whether to run each command\n") \
+ " -r Do not run command for empty read lines\n" \
+ USE_FEATURE_XARGS_SUPPORT_TERMOPT( \
+ " -x Exit if the size is exceeded\n") \
+ USE_FEATURE_XARGS_SUPPORT_ZERO_TERM( \
+ " -0 Input filenames are terminated by a null character\n") \
+ " -t Print the command line on stderr before executing it"
+#define xargs_example_usage \
+ "$ ls | xargs gzip\n" \
+ "$ find . -name '*.c' -print | xargs rm\n"
+
+#define yes_trivial_usage \
+ "[OPTION]... [STRING]..."
+#define yes_full_usage \
+ "Repeatedly output a line with all specified STRING(s), or 'y'"
+
+#define zcat_trivial_usage \
+ "FILE"
+#define zcat_full_usage \
+ "Uncompress to stdout"
+
+#define zcip_trivial_usage \
+ "[OPTIONS] ifname script"
+#define zcip_full_usage \
+ "Manage a ZeroConf IPv4 link-local address" \
+ "\n\nOptions:\n" \
+ " -f Foreground mode\n" \
+ " -q Quit after address (no daemon)\n" \
+ " -r 169.254.x.x Request this address first\n" \
+ " -v Verbose"
+
+#endif /* __BB_USAGE_H__ */
diff --git a/i/pc104/initrd/conf/busybox/include/xatonum.h b/i/pc104/initrd/conf/busybox/include/xatonum.h
new file mode 100644
index 0000000..cf088a4
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/include/xatonum.h
@@ -0,0 +1,157 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ascii-to-numbers implementations for busybox
+ *
+ * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
+ *
+ * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ */
+
+/* Provides extern declarations of functions */
+#define DECLARE_STR_CONV(type, T, UT) \
+\
+unsigned type xstrto##UT##_range_sfx(const char *str, int b, unsigned type l, unsigned type u, const struct suffix_mult *sfx); \
+unsigned type xstrto##UT##_range(const char *str, int b, unsigned type l, unsigned type u); \
+unsigned type xstrto##UT##_sfx(const char *str, int b, const struct suffix_mult *sfx); \
+unsigned type xstrto##UT(const char *str, int b); \
+unsigned type xato##UT##_range_sfx(const char *str, unsigned type l, unsigned type u, const struct suffix_mult *sfx); \
+unsigned type xato##UT##_range(const char *str, unsigned type l, unsigned type u); \
+unsigned type xato##UT##_sfx(const char *str, const struct suffix_mult *sfx); \
+unsigned type xato##UT(const char *str); \
+type xstrto##T##_range_sfx(const char *str, int b, type l, type u, const struct suffix_mult *sfx) ;\
+type xstrto##T##_range(const char *str, int b, type l, type u); \
+type xato##T##_range_sfx(const char *str, type l, type u, const struct suffix_mult *sfx); \
+type xato##T##_range(const char *str, type l, type u); \
+type xato##T##_sfx(const char *str, const struct suffix_mult *sfx); \
+type xato##T(const char *str); \
+
+/* Unsigned long long functions always exist */
+DECLARE_STR_CONV(long long, ll, ull)
+
+
+/* Provides inline definitions of functions */
+/* (useful for mapping them to the type of the same width) */
+#define DEFINE_EQUIV_STR_CONV(narrow, N, W, UN, UW) \
+\
+static ATTRIBUTE_ALWAYS_INLINE \
+unsigned narrow xstrto##UN##_range_sfx(const char *str, int b, unsigned narrow l, unsigned narrow u, const struct suffix_mult *sfx) \
+{ return xstrto##UW##_range_sfx(str, b, l, u, sfx); } \
+static ATTRIBUTE_ALWAYS_INLINE \
+unsigned narrow xstrto##UN##_range(const char *str, int b, unsigned narrow l, unsigned narrow u) \
+{ return xstrto##UW##_range(str, b, l, u); } \
+static ATTRIBUTE_ALWAYS_INLINE \
+unsigned narrow xstrto##UN##_sfx(const char *str, int b, const struct suffix_mult *sfx) \
+{ return xstrto##UW##_sfx(str, b, sfx); } \
+static ATTRIBUTE_ALWAYS_INLINE \
+unsigned narrow xstrto##UN(const char *str, int b) \
+{ return xstrto##UW(str, b); } \
+static ATTRIBUTE_ALWAYS_INLINE \
+unsigned narrow xato##UN##_range_sfx(const char *str, unsigned narrow l, unsigned narrow u, const struct suffix_mult *sfx) \
+{ return xato##UW##_range_sfx(str, l, u, sfx); } \
+static ATTRIBUTE_ALWAYS_INLINE \
+unsigned narrow xato##UN##_range(const char *str, unsigned narrow l, unsigned narrow u) \
+{ return xato##UW##_range(str, l, u); } \
+static ATTRIBUTE_ALWAYS_INLINE \
+unsigned narrow xato##UN##_sfx(const char *str, const struct suffix_mult *sfx) \
+{ return xato##UW##_sfx(str, sfx); } \
+static ATTRIBUTE_ALWAYS_INLINE \
+unsigned narrow xato##UN(const char *str) \
+{ return xato##UW(str); } \
+static ATTRIBUTE_ALWAYS_INLINE \
+narrow xstrto##N##_range_sfx(const char *str, int b, narrow l, narrow u, const struct suffix_mult *sfx) \
+{ return xstrto##W##_range_sfx(str, b, l, u, sfx); } \
+static ATTRIBUTE_ALWAYS_INLINE \
+narrow xstrto##N##_range(const char *str, int b, narrow l, narrow u) \
+{ return xstrto##W##_range(str, b, l, u); } \
+static ATTRIBUTE_ALWAYS_INLINE \
+narrow xato##N##_range_sfx(const char *str, narrow l, narrow u, const struct suffix_mult *sfx) \
+{ return xato##W##_range_sfx(str, l, u, sfx); } \
+static ATTRIBUTE_ALWAYS_INLINE \
+narrow xato##N##_range(const char *str, narrow l, narrow u) \
+{ return xato##W##_range(str, l, u); } \
+static ATTRIBUTE_ALWAYS_INLINE \
+narrow xato##N##_sfx(const char *str, const struct suffix_mult *sfx) \
+{ return xato##W##_sfx(str, sfx); } \
+static ATTRIBUTE_ALWAYS_INLINE \
+narrow xato##N(const char *str) \
+{ return xato##W(str); } \
+
+/* If long == long long, then just map them one-to-one */
+#if ULONG_MAX == ULLONG_MAX
+DEFINE_EQUIV_STR_CONV(long, l, ll, ul, ull)
+#else
+/* Else provide extern defs */
+DECLARE_STR_CONV(long, l, ul)
+#endif
+
+/* Same for int -> [long] long */
+#if UINT_MAX == ULLONG_MAX
+DEFINE_EQUIV_STR_CONV(int, i, ll, u, ull)
+#elif UINT_MAX == ULONG_MAX
+DEFINE_EQUIV_STR_CONV(int, i, l, u, ul)
+#else
+DECLARE_STR_CONV(int, i, u)
+#endif
+
+/* Specialized */
+
+int BUG_xatou32_unimplemented(void);
+static ATTRIBUTE_ALWAYS_INLINE uint32_t xatou32(const char *numstr)
+{
+ if (UINT_MAX == 0xffffffff)
+ return xatou(numstr);
+ if (ULONG_MAX == 0xffffffff)
+ return xatoul(numstr);
+ return BUG_xatou32_unimplemented();
+}
+
+/* Non-aborting kind of convertors */
+
+unsigned long long bb_strtoull(const char *arg, char **endp, int base);
+long long bb_strtoll(const char *arg, char **endp, int base);
+
+#if ULONG_MAX == ULLONG_MAX
+static ATTRIBUTE_ALWAYS_INLINE
+unsigned long bb_strtoul(const char *arg, char **endp, int base)
+{ return bb_strtoull(arg, endp, base); }
+static ATTRIBUTE_ALWAYS_INLINE
+long bb_strtol(const char *arg, char **endp, int base)
+{ return bb_strtoll(arg, endp, base); }
+#else
+unsigned long bb_strtoul(const char *arg, char **endp, int base);
+long bb_strtol(const char *arg, char **endp, int base);
+#endif
+
+#if UINT_MAX == ULLONG_MAX
+static ATTRIBUTE_ALWAYS_INLINE
+unsigned bb_strtou(const char *arg, char **endp, int base)
+{ return bb_strtoull(arg, endp, base); }
+static ATTRIBUTE_ALWAYS_INLINE
+int bb_strtoi(const char *arg, char **endp, int base)
+{ return bb_strtoll(arg, endp, base); }
+#elif UINT_MAX == ULONG_MAX
+static ATTRIBUTE_ALWAYS_INLINE
+unsigned bb_strtou(const char *arg, char **endp, int base)
+{ return bb_strtoul(arg, endp, base); }
+static ATTRIBUTE_ALWAYS_INLINE
+int bb_strtoi(const char *arg, char **endp, int base)
+{ return bb_strtol(arg, endp, base); }
+#else
+unsigned bb_strtou(const char *arg, char **endp, int base);
+int bb_strtoi(const char *arg, char **endp, int base);
+#endif
+
+int BUG_bb_strtou32_unimplemented(void);
+static ATTRIBUTE_ALWAYS_INLINE
+uint32_t bb_strtou32(const char *arg, char **endp, int base)
+{
+ if (sizeof(uint32_t) == sizeof(unsigned))
+ return bb_strtou(arg, endp, base);
+ if (sizeof(uint32_t) == sizeof(unsigned long))
+ return bb_strtoul(arg, endp, base);
+ return BUG_bb_strtou32_unimplemented();
+}
+
+/* Floating point */
+
+/* double bb_strtod(const char *arg, char **endp); */
diff --git a/i/pc104/initrd/conf/busybox/include/xregex.h b/i/pc104/initrd/conf/busybox/include/xregex.h
new file mode 100644
index 0000000..23cf19c
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/include/xregex.h
@@ -0,0 +1,18 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Busybox xregcomp utility routine. This isn't in libbb.h because the
+ * C library we're linking against may not support regex.h.
+ *
+ * Based in part on code from sash, Copyright (c) 1999 by David I. Bell
+ * Permission has been granted to redistribute this code under the GPL.
+ *
+ * Licensed under GPLv2 or later, see file License in this tarball for details.
+ */
+#ifndef __BB_REGEX__
+#define __BB_REGEX__
+
+#include <regex.h>
+char* regcomp_or_errmsg(regex_t *preg, const char *regex, int cflags);
+void xregcomp(regex_t *preg, const char *regex, int cflags);
+
+#endif
diff --git a/i/pc104/initrd/conf/busybox/init/Config.in b/i/pc104/initrd/conf/busybox/init/Config.in
new file mode 100644
index 0000000..3987d8f
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/init/Config.in
@@ -0,0 +1,89 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+menu "Init Utilities"
+
+config INIT
+ bool "init"
+ default n
+ select FEATURE_SYSLOG
+ help
+ init is the first program run when the system boots.
+
+config DEBUG_INIT
+ bool "debugging aid"
+ default n
+ depends on INIT
+ help
+ Turn this on to disable all the dangerous
+ rebooting stuff when debugging.
+
+config FEATURE_USE_INITTAB
+ bool "Support reading an inittab file"
+ default y
+ depends on INIT
+ help
+ Allow init to read an inittab file when the system boot.
+
+config FEATURE_INIT_SCTTY
+ bool "Support running commands with a controlling-tty"
+ default n
+ depends on INIT
+ help
+ If this option is enabled a command starting with hyphen (-)
+ is run in its own session (setsid(2)) and possibly with a
+ controlling tty (TIOCSCTTY). This is not the traditional init
+ behavour, but is often what you want in an embedded system where
+ the console is only accessed during development or for maintenance.
+
+config FEATURE_INIT_SYSLOG
+ bool "Enable init to write to syslog"
+ default n
+ depends on INIT
+
+config FEATURE_EXTRA_QUIET
+ bool "Be _extra_ quiet on boot"
+ default y
+ depends on INIT
+ help
+ Prevent init from logging some messages to the console during boot.
+
+config FEATURE_INIT_COREDUMPS
+ bool "Support dumping core for child processes (debugging only)"
+ default n
+ depends on INIT
+ help
+ If this option is enabled and the file /.init_enable_core
+ exists, then init will call setrlimit() to allow unlimited
+ core file sizes. If this option is disabled, processes
+ will not generate any core files.
+
+
+
+config FEATURE_INITRD
+ bool "Support running init from within an initrd (not initramfs)"
+ default y
+ depends on INIT
+ help
+ Legacy support for running init under the old-style initrd. Allows
+ the name linuxrc to act as init, and it doesn't assume init is PID 1.
+
+ This does not apply to initramfs, which runs /init as PID 1 and
+ requires no special support.
+
+config HALT
+ bool "poweroff, halt, and reboot"
+ default y
+ help
+ Stop all processes and either halt, reboot, or power off the system.
+
+config MESG
+ bool "mesg"
+ default y
+ help
+ Mesg controls access to your terminal by others. It is typically
+ used to allow or disallow other users to write to your terminal
+
+endmenu
diff --git a/i/pc104/initrd/conf/busybox/init/Kbuild b/i/pc104/initrd/conf/busybox/init/Kbuild
new file mode 100644
index 0000000..c060f3a
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/init/Kbuild
@@ -0,0 +1,10 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under the GPL v2, see the file LICENSE in this tarball.
+
+lib-y:=
+lib-$(CONFIG_HALT) += halt.o
+lib-$(CONFIG_INIT) += init.o
+lib-$(CONFIG_MESG) += mesg.o
diff --git a/i/pc104/initrd/conf/busybox/init/halt.c b/i/pc104/initrd/conf/busybox/init/halt.c
new file mode 100644
index 0000000..d5c22a8
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/init/halt.c
@@ -0,0 +1,59 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Poweroff reboot and halt, oh my.
+ *
+ * Copyright 2006 by Rob Landley <rob@landley.net>
+ *
+ * Licensed under GPL version 2, see file LICENSE in this tarball for details.
+ */
+
+#include "busybox.h"
+#include <sys/reboot.h>
+
+int halt_main(int argc, char *argv[]);
+int halt_main(int argc, char *argv[])
+{
+ static const int magic[] = {
+#ifdef RB_HALT_SYSTEM
+RB_HALT_SYSTEM,
+#elif defined RB_HALT
+RB_HALT,
+#endif
+#ifdef RB_POWER_OFF
+RB_POWER_OFF,
+#elif defined RB_POWERDOWN
+RB_POWERDOWN,
+#endif
+RB_AUTOBOOT
+ };
+ static const int signals[] = { SIGUSR1, SIGUSR2, SIGTERM };
+
+ char *delay;
+ int which, flags, rc = 1;
+
+ /* Figure out which applet we're running */
+ for (which = 0; "hpr"[which] != *applet_name; which++);
+
+ /* Parse and handle arguments */
+ flags = getopt32(argc, argv, "d:nf", &delay);
+ if (flags & 1) sleep(xatou(delay));
+ if (!(flags & 2)) sync();
+
+ /* Perform action. */
+ if (ENABLE_INIT && !(flags & 4)) {
+ if (ENABLE_FEATURE_INITRD) {
+ pid_t *pidlist = find_pid_by_name("linuxrc");
+ if (pidlist[0] > 0)
+ rc = kill(pidlist[0], signals[which]);
+ if (ENABLE_FEATURE_CLEAN_UP)
+ free(pidlist);
+ }
+ if (rc)
+ rc = kill(1, signals[which]);
+ } else
+ rc = reboot(magic[which]);
+
+ if (rc)
+ bb_error_msg("no");
+ return rc;
+}
diff --git a/i/pc104/initrd/conf/busybox/init/init.c b/i/pc104/initrd/conf/busybox/init/init.c
new file mode 100644
index 0000000..02a3327
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/init/init.c
@@ -0,0 +1,1043 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini init implementation for busybox
+ *
+ * Copyright (C) 1995, 1996 by Bruce Perens <bruce@pixar.com>.
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ * Adjusted by so many folks, it's impossible to keep track.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "busybox.h"
+#include <paths.h>
+#include <signal.h>
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+#include <sys/reboot.h>
+
+#if ENABLE_FEATURE_INIT_SYSLOG
+# include <sys/syslog.h>
+#endif
+
+#define INIT_BUFFS_SIZE 256
+#define CONSOLE_NAME_SIZE 32
+#define MAXENV 16 /* Number of env. vars */
+
+#ifndef _PATH_STDPATH
+#define _PATH_STDPATH "/usr/bin:/bin:/usr/sbin:/sbin"
+#endif
+
+#if ENABLE_FEATURE_INIT_COREDUMPS
+/*
+ * When a file named CORE_ENABLE_FLAG_FILE exists, setrlimit is called
+ * before processes are spawned to set core file size as unlimited.
+ * This is for debugging only. Don't use this is production, unless
+ * you want core dumps lying about....
+ */
+#define CORE_ENABLE_FLAG_FILE "/.init_enable_core"
+#include <sys/resource.h>
+#endif
+
+#define INITTAB "/etc/inittab" /* inittab file location */
+#ifndef INIT_SCRIPT
+#define INIT_SCRIPT "/etc/init.d/rcS" /* Default sysinit script. */
+#endif
+
+/* Allowed init action types */
+#define SYSINIT 0x001
+#define RESPAWN 0x002
+#define ASKFIRST 0x004
+#define WAIT 0x008
+#define ONCE 0x010
+#define CTRLALTDEL 0x020
+#define SHUTDOWN 0x040
+#define RESTART 0x080
+
+/* A mapping between "inittab" action name strings and action type codes. */
+struct init_action_type {
+ const char *name;
+ int action;
+};
+
+static const struct init_action_type actions[] = {
+ {"sysinit", SYSINIT},
+ {"respawn", RESPAWN},
+ {"askfirst", ASKFIRST},
+ {"wait", WAIT},
+ {"once", ONCE},
+ {"ctrlaltdel", CTRLALTDEL},
+ {"shutdown", SHUTDOWN},
+ {"restart", RESTART},
+ {0, 0}
+};
+
+/* Set up a linked list of init_actions, to be read from inittab */
+struct init_action {
+ struct init_action *next;
+ int action;
+ pid_t pid;
+ char command[INIT_BUFFS_SIZE];
+ char terminal[CONSOLE_NAME_SIZE];
+};
+
+/* Static variables */
+static struct init_action *init_action_list = NULL;
+
+#if !ENABLE_FEATURE_INIT_SYSLOG
+static const char *log_console = VC_5;
+#endif
+#if !ENABLE_DEBUG_INIT
+static sig_atomic_t got_cont = 0;
+#endif
+
+enum {
+ L_LOG = 0x1,
+ L_CONSOLE = 0x2,
+
+#if ENABLE_FEATURE_EXTRA_QUIET
+ MAYBE_CONSOLE = 0x0,
+#else
+ MAYBE_CONSOLE = L_CONSOLE,
+#endif
+
+#ifndef RB_HALT_SYSTEM
+ RB_HALT_SYSTEM = 0xcdef0123, /* FIXME: this overflows enum */
+ RB_ENABLE_CAD = 0x89abcdef,
+ RB_DISABLE_CAD = 0,
+ RB_POWER_OFF = 0x4321fedc,
+ RB_AUTOBOOT = 0x01234567,
+#endif
+};
+
+static const char * const environment[] = {
+ "HOME=/",
+ "PATH=" _PATH_STDPATH,
+ "SHELL=/bin/sh",
+ "USER=root",
+ NULL
+};
+
+/* Function prototypes */
+static void delete_init_action(struct init_action *a);
+static int waitfor(const struct init_action *a, pid_t pid);
+#if !ENABLE_DEBUG_INIT
+static void shutdown_signal(int sig);
+#endif
+
+#if !ENABLE_DEBUG_INIT
+static void loop_forever(void)
+{
+ while (1)
+ sleep(1);
+}
+#endif
+
+/* Print a message to the specified device.
+ * Device may be bitwise-or'd from L_LOG | L_CONSOLE */
+#if ENABLE_DEBUG_INIT
+#define messageD message
+#else
+#define messageD(...) do {} while (0)
+#endif
+static void message(int device, const char *fmt, ...)
+ __attribute__ ((format(printf, 2, 3)));
+static void message(int device, const char *fmt, ...)
+{
+#if !ENABLE_FEATURE_INIT_SYSLOG
+ static int log_fd = -1;
+#endif
+
+ va_list arguments;
+ int l;
+ char msg[128];
+
+ msg[0] = '\r';
+ va_start(arguments, fmt);
+ vsnprintf(msg + 1, sizeof(msg) - 2, fmt, arguments);
+ va_end(arguments);
+ msg[sizeof(msg) - 2] = '\0';
+ l = strlen(msg);
+
+#if ENABLE_FEATURE_INIT_SYSLOG
+ /* Log the message to syslogd */
+ if (device & L_LOG) {
+ /* don't out "\r" */
+ openlog(applet_name, 0, LOG_DAEMON);
+ syslog(LOG_INFO, "init: %s", msg + 1);
+ closelog();
+ }
+ msg[l++] = '\n';
+ msg[l] = '\0';
+#else
+ msg[l++] = '\n';
+ msg[l] = '\0';
+ /* Take full control of the log tty, and never close it.
+ * It's mine, all mine! Muhahahaha! */
+ if (log_fd < 0) {
+ if (!log_console) {
+ log_fd = 2;
+ } else {
+ log_fd = device_open(log_console, O_WRONLY | O_NONBLOCK | O_NOCTTY);
+ if (log_fd < 0) {
+ bb_error_msg("can't log to %s", log_console);
+ device = L_CONSOLE;
+ } else {
+ fcntl(log_fd, F_SETFD, FD_CLOEXEC);
+ }
+ }
+ }
+ if (device & L_LOG) {
+ full_write(log_fd, msg, l);
+ if (log_fd == 2)
+ return; /* don't print dup messages */
+ }
+#endif
+
+ if (device & L_CONSOLE) {
+ /* Send console messages to console so people will see them. */
+ full_write(2, msg, l);
+ }
+}
+
+/* Set terminal settings to reasonable defaults */
+static void set_sane_term(void)
+{
+ struct termios tty;
+
+ tcgetattr(STDIN_FILENO, &tty);
+
+ /* set control chars */
+ tty.c_cc[VINTR] = 3; /* C-c */
+ tty.c_cc[VQUIT] = 28; /* C-\ */
+ tty.c_cc[VERASE] = 127; /* C-? */
+ tty.c_cc[VKILL] = 21; /* C-u */
+ tty.c_cc[VEOF] = 4; /* C-d */
+ tty.c_cc[VSTART] = 17; /* C-q */
+ tty.c_cc[VSTOP] = 19; /* C-s */
+ tty.c_cc[VSUSP] = 26; /* C-z */
+
+ /* use line dicipline 0 */
+ tty.c_line = 0;
+
+ /* Make it be sane */
+ tty.c_cflag &= CBAUD | CBAUDEX | CSIZE | CSTOPB | PARENB | PARODD;
+ tty.c_cflag |= CREAD | HUPCL | CLOCAL;
+
+ /* input modes */
+ tty.c_iflag = ICRNL | IXON | IXOFF;
+
+ /* output modes */
+ tty.c_oflag = OPOST | ONLCR;
+
+ /* local modes */
+ tty.c_lflag =
+ ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOKE | IEXTEN;
+
+ tcsetattr(STDIN_FILENO, TCSANOW, &tty);
+}
+
+/* From <linux/serial.h> */
+struct serial_struct {
+ int type;
+ int line;
+ unsigned int port;
+ int irq;
+ int flags;
+ int xmit_fifo_size;
+ int custom_divisor;
+ int baud_base;
+ unsigned short close_delay;
+ char io_type;
+ char reserved_char[1];
+ int hub6;
+ unsigned short closing_wait; /* time to wait before closing */
+ unsigned short closing_wait2; /* no longer used... */
+ unsigned char *iomem_base;
+ unsigned short iomem_reg_shift;
+ unsigned int port_high;
+ unsigned long iomap_base; /* cookie passed into ioremap */
+ int reserved[1];
+ /* Paranoia (imagine 64bit kernel overwriting 32bit userspace stack) */
+ uint32_t bbox_reserved[16];
+};
+static void console_init(void)
+{
+ struct serial_struct sr;
+ char *s;
+
+ s = getenv("CONSOLE");
+ if (!s) s = getenv("console");
+ if (s) {
+ int fd = open(s, O_RDWR | O_NONBLOCK | O_NOCTTY);
+ if (fd >= 0) {
+ dup2(fd, 0);
+ dup2(fd, 1);
+ dup2(fd, 2);
+ while (fd > 2) close(fd--);
+ }
+ messageD(L_LOG, "console='%s'", s);
+ }
+
+ s = getenv("TERM");
+ if (ioctl(0, TIOCGSERIAL, &sr) == 0) {
+ /* Force the TERM setting to vt102 for serial console --
+ * if TERM is set to linux (the default) */
+ if (!s || strcmp(s, "linux") == 0)
+ putenv((char*)"TERM=vt102");
+#if !ENABLE_FEATURE_INIT_SYSLOG
+ log_console = NULL;
+#endif
+ } else if (!s)
+ putenv((char*)"TERM=linux");
+}
+
+static void fixup_argv(int argc, char **argv, const char *new_argv0)
+{
+ int len;
+
+ /* Fix up argv[0] to be certain we claim to be init */
+ len = strlen(argv[0]);
+ strncpy(argv[0], new_argv0, len);
+
+ /* Wipe argv[1]-argv[N] so they don't clutter the ps listing */
+ len = 1;
+ while (argc > len) {
+ memset(argv[len], 0, strlen(argv[len]));
+ len++;
+ }
+}
+
+/* Open the new terminal device */
+static void open_stdio_to_tty(const char* tty_name, int fail)
+{
+ /* empty tty_name means "use init's tty", else... */
+ if (tty_name[0]) {
+ int fd = device_open(tty_name, O_RDWR);
+ if (fd < 0) {
+ message(L_LOG | L_CONSOLE, "Can't open %s: %s",
+ tty_name, strerror(errno));
+ if (fail)
+ _exit(1);
+#if !ENABLE_DEBUG_INIT
+ shutdown_signal(SIGUSR1);
+#else
+ _exit(2);
+#endif
+ } else {
+ dup2(fd, 0);
+ dup2(fd, 1);
+ dup2(fd, 2);
+ if (fd > 2) close(fd);
+ }
+ }
+ set_sane_term();
+}
+
+static pid_t run(const struct init_action *a)
+{
+ int i;
+ pid_t pid;
+ char *s, *tmpCmd, *cmdpath;
+ char *cmd[INIT_BUFFS_SIZE];
+ char buf[INIT_BUFFS_SIZE + 6]; /* INIT_BUFFS_SIZE+strlen("exec ")+1 */
+ sigset_t nmask, omask;
+
+ /* Block sigchild while forking. */
+ sigemptyset(&nmask);
+ sigaddset(&nmask, SIGCHLD);
+ sigprocmask(SIG_BLOCK, &nmask, &omask);
+ pid = fork();
+ sigprocmask(SIG_SETMASK, &omask, NULL);
+
+ if (pid)
+ return pid;
+
+ /* Reset signal handlers that were set by the parent process */
+ signal(SIGUSR1, SIG_DFL);
+ signal(SIGUSR2, SIG_DFL);
+ signal(SIGINT, SIG_DFL);
+ signal(SIGTERM, SIG_DFL);
+ signal(SIGHUP, SIG_DFL);
+ signal(SIGQUIT, SIG_DFL);
+ signal(SIGCONT, SIG_DFL);
+ signal(SIGSTOP, SIG_DFL);
+ signal(SIGTSTP, SIG_DFL);
+
+ /* Create a new session and make ourself the process
+ * group leader */
+ setsid();
+
+ /* Open the new terminal device */
+ open_stdio_to_tty(a->terminal, 1);
+
+ /* If the init Action requires us to wait, then force the
+ * supplied terminal to be the controlling tty. */
+ if (a->action & (SYSINIT | WAIT | CTRLALTDEL | SHUTDOWN | RESTART)) {
+
+ /* Now fork off another process to just hang around */
+ if ((pid = fork()) < 0) {
+ message(L_LOG | L_CONSOLE, "Can't fork");
+ _exit(1);
+ }
+
+ if (pid > 0) {
+
+ /* We are the parent -- wait till the child is done */
+ signal(SIGINT, SIG_IGN);
+ signal(SIGTSTP, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ signal(SIGCHLD, SIG_DFL);
+
+ waitfor(NULL, pid);
+ /* See if stealing the controlling tty back is necessary */
+ if (tcgetpgrp(0) != getpid())
+ _exit(0);
+
+ /* Use a temporary process to steal the controlling tty. */
+ if ((pid = fork()) < 0) {
+ message(L_LOG | L_CONSOLE, "Can't fork");
+ _exit(1);
+ }
+ if (pid == 0) {
+ setsid();
+ ioctl(0, TIOCSCTTY, 1);
+ _exit(0);
+ }
+ waitfor(NULL, pid);
+ _exit(0);
+ }
+
+ /* Now fall though to actually execute things */
+ }
+
+ /* See if any special /bin/sh requiring characters are present */
+ if (strpbrk(a->command, "~`!$^&*()=|\\{}[];\"'<>?") != NULL) {
+ cmd[0] = (char*)DEFAULT_SHELL;
+ cmd[1] = (char*)"-c";
+ cmd[2] = strcat(strcpy(buf, "exec "), a->command);
+ cmd[3] = NULL;
+ } else {
+ /* Convert command (char*) into cmd (char**, one word per string) */
+ strcpy(buf, a->command);
+ s = buf;
+ for (tmpCmd = buf, i = 0; (tmpCmd = strsep(&s, " \t")) != NULL;) {
+ if (*tmpCmd != '\0') {
+ cmd[i] = tmpCmd;
+ i++;
+ }
+ }
+ cmd[i] = NULL;
+ }
+
+ cmdpath = cmd[0];
+
+ /*
+ * Interactive shells want to see a dash in argv[0]. This
+ * typically is handled by login, argv will be setup this
+ * way if a dash appears at the front of the command path
+ * (like "-/bin/sh").
+ */
+ if (*cmdpath == '-') {
+ /* skip over the dash */
+ ++cmdpath;
+
+ /* find the last component in the command pathname */
+ s = bb_get_last_path_component(cmdpath);
+
+ /* make a new argv[0] */
+ if ((cmd[0] = malloc(strlen(s) + 2)) == NULL) {
+ message(L_LOG | L_CONSOLE, bb_msg_memory_exhausted);
+ cmd[0] = cmdpath;
+ } else {
+ cmd[0][0] = '-';
+ strcpy(cmd[0] + 1, s);
+ }
+#if ENABLE_FEATURE_INIT_SCTTY
+ /* Establish this process as session leader and
+ * (attempt) to make the tty (if any) a controlling tty.
+ */
+ setsid();
+ ioctl(0, TIOCSCTTY, 0 /*don't steal it*/);
+#endif
+ }
+
+#if !defined(__UCLIBC__) || defined(__ARCH_HAS_MMU__)
+ if (a->action & ASKFIRST) {
+ static const char press_enter[] =
+#ifdef CUSTOMIZED_BANNER
+#include CUSTOMIZED_BANNER
+#endif
+ "\nPlease press Enter to activate this console. ";
+ char c;
+ /*
+ * Save memory by not exec-ing anything large (like a shell)
+ * before the user wants it. This is critical if swap is not
+ * enabled and the system has low memory. Generally this will
+ * be run on the second virtual console, and the first will
+ * be allowed to start a shell or whatever an init script
+ * specifies.
+ */
+ messageD(L_LOG, "waiting for enter to start '%s'"
+ "(pid %d, tty '%s')\n",
+ cmdpath, getpid(), a->terminal);
+ full_write(1, press_enter, sizeof(press_enter) - 1);
+ while (read(0, &c, 1) == 1 && c != '\n')
+ ;
+ }
+#endif
+ /* Log the process name and args */
+ message(L_LOG, "starting pid %d, tty '%s': '%s'",
+ getpid(), a->terminal, cmdpath);
+
+#if ENABLE_FEATURE_INIT_COREDUMPS
+ {
+ struct stat sb;
+ if (stat(CORE_ENABLE_FLAG_FILE, &sb) == 0) {
+ struct rlimit limit;
+
+ limit.rlim_cur = RLIM_INFINITY;
+ limit.rlim_max = RLIM_INFINITY;
+ setrlimit(RLIMIT_CORE, &limit);
+ }
+ }
+#endif
+ /* Now run it. The new program will take over this PID,
+ * so nothing further in init.c should be run. */
+ BB_EXECVP(cmdpath, cmd);
+
+ /* We're still here? Some error happened. */
+ message(L_LOG | L_CONSOLE, "Cannot run '%s': %s",
+ cmdpath, strerror(errno));
+ _exit(-1);
+}
+
+static int waitfor(const struct init_action *a, pid_t pid)
+{
+ int runpid;
+ int status, wpid;
+
+ runpid = (NULL == a)? pid : run(a);
+ while (1) {
+ wpid = waitpid(runpid, &status, 0);
+ if (wpid == runpid)
+ break;
+ if (wpid == -1 && errno == ECHILD) {
+ /* we missed its termination */
+ break;
+ }
+ /* FIXME other errors should maybe trigger an error, but allow
+ * the program to continue */
+ }
+ return wpid;
+}
+
+/* Run all commands of a particular type */
+static void run_actions(int action)
+{
+ struct init_action *a, *tmp;
+
+ for (a = init_action_list; a; a = tmp) {
+ tmp = a->next;
+ if (a->action == action) {
+ /* a->terminal of "" means "init's console" */
+ if (a->terminal[0] && access(a->terminal, R_OK | W_OK)) {
+ delete_init_action(a);
+ } else if (a->action & (SYSINIT | WAIT | CTRLALTDEL | SHUTDOWN | RESTART)) {
+ waitfor(a, 0);
+ delete_init_action(a);
+ } else if (a->action & ONCE) {
+ run(a);
+ delete_init_action(a);
+ } else if (a->action & (RESPAWN | ASKFIRST)) {
+ /* Only run stuff with pid==0. If they have
+ * a pid, that means it is still running */
+ if (a->pid == 0) {
+ a->pid = run(a);
+ }
+ }
+ }
+ }
+}
+
+#if !ENABLE_DEBUG_INIT
+static void init_reboot(unsigned long magic)
+{
+ pid_t pid;
+ /* We have to fork here, since the kernel calls do_exit(0) in
+ * linux/kernel/sys.c, which can cause the machine to panic when
+ * the init process is killed.... */
+ pid = vfork();
+ if (pid == 0) { /* child */
+ reboot(magic);
+ _exit(0);
+ }
+ waitpid(pid, NULL, 0);
+}
+
+static void shutdown_system(void)
+{
+ sigset_t block_signals;
+
+ /* run everything to be run at "shutdown". This is done _prior_
+ * to killing everything, in case people wish to use scripts to
+ * shut things down gracefully... */
+ run_actions(SHUTDOWN);
+
+ /* first disable all our signals */
+ sigemptyset(&block_signals);
+ sigaddset(&block_signals, SIGHUP);
+ sigaddset(&block_signals, SIGQUIT);
+ sigaddset(&block_signals, SIGCHLD);
+ sigaddset(&block_signals, SIGUSR1);
+ sigaddset(&block_signals, SIGUSR2);
+ sigaddset(&block_signals, SIGINT);
+ sigaddset(&block_signals, SIGTERM);
+ sigaddset(&block_signals, SIGCONT);
+ sigaddset(&block_signals, SIGSTOP);
+ sigaddset(&block_signals, SIGTSTP);
+ sigprocmask(SIG_BLOCK, &block_signals, NULL);
+
+ message(L_CONSOLE | L_LOG, "The system is going down NOW!");
+
+ /* Allow Ctrl-Alt-Del to reboot system. */
+ init_reboot(RB_ENABLE_CAD);
+
+ /* Send signals to every process _except_ pid 1 */
+ message(L_CONSOLE | L_LOG, "Sending SIG%s to all processes", "TERM");
+ kill(-1, SIGTERM);
+ sync();
+ sleep(1);
+
+ message(L_CONSOLE | L_LOG, "Sending SIG%s to all processes", "KILL");
+ kill(-1, SIGKILL);
+ sync();
+ sleep(1);
+}
+
+static void exec_signal(int sig ATTRIBUTE_UNUSED)
+{
+ struct init_action *a, *tmp;
+ sigset_t unblock_signals;
+
+ for (a = init_action_list; a; a = tmp) {
+ tmp = a->next;
+ if (a->action & RESTART) {
+ shutdown_system();
+
+ /* unblock all signals, blocked in shutdown_system() */
+ sigemptyset(&unblock_signals);
+ sigaddset(&unblock_signals, SIGHUP);
+ sigaddset(&unblock_signals, SIGQUIT);
+ sigaddset(&unblock_signals, SIGCHLD);
+ sigaddset(&unblock_signals, SIGUSR1);
+ sigaddset(&unblock_signals, SIGUSR2);
+ sigaddset(&unblock_signals, SIGINT);
+ sigaddset(&unblock_signals, SIGTERM);
+ sigaddset(&unblock_signals, SIGCONT);
+ sigaddset(&unblock_signals, SIGSTOP);
+ sigaddset(&unblock_signals, SIGTSTP);
+ sigprocmask(SIG_UNBLOCK, &unblock_signals, NULL);
+
+ /* Open the new terminal device */
+ open_stdio_to_tty(a->terminal, 0);
+
+ messageD(L_CONSOLE | L_LOG, "Trying to re-exec %s", a->command);
+ BB_EXECLP(a->command, a->command, NULL);
+
+ message(L_CONSOLE | L_LOG, "Cannot run '%s': %s",
+ a->command, strerror(errno));
+ sleep(2);
+ init_reboot(RB_HALT_SYSTEM);
+ loop_forever();
+ }
+ }
+}
+
+static void shutdown_signal(int sig)
+{
+ const char *m;
+ int rb;
+
+ shutdown_system();
+
+ m = "halt";
+ rb = RB_HALT_SYSTEM;
+ if (sig == SIGTERM) {
+ m = "reboot";
+ rb = RB_AUTOBOOT;
+ } else if (sig == SIGUSR2) {
+ m = "poweroff";
+ rb = RB_POWER_OFF;
+ }
+ message(L_CONSOLE | L_LOG, "Requesting system %s", m);
+ /* allow time for last message to reach serial console */
+ sleep(2);
+ init_reboot(rb);
+ loop_forever();
+}
+
+static void ctrlaltdel_signal(int sig ATTRIBUTE_UNUSED)
+{
+ run_actions(CTRLALTDEL);
+}
+
+/* The SIGSTOP & SIGTSTP handler */
+static void stop_handler(int sig ATTRIBUTE_UNUSED)
+{
+ int saved_errno = errno;
+
+ got_cont = 0;
+ while (!got_cont)
+ pause();
+ got_cont = 0;
+ errno = saved_errno;
+}
+
+/* The SIGCONT handler */
+static void cont_handler(int sig ATTRIBUTE_UNUSED)
+{
+ got_cont = 1;
+}
+
+#endif /* !ENABLE_DEBUG_INIT */
+
+static void new_init_action(int action, const char *command, const char *cons)
+{
+ struct init_action *new_action, *a, *last;
+
+ if (strcmp(cons, bb_dev_null) == 0 && (action & ASKFIRST))
+ return;
+
+ /* Append to the end of the list */
+ for (a = last = init_action_list; a; a = a->next) {
+ /* don't enter action if it's already in the list,
+ * but do overwrite existing actions */
+ if ((strcmp(a->command, command) == 0)
+ && (strcmp(a->terminal, cons) == 0)
+ ) {
+ a->action = action;
+ return;
+ }
+ last = a;
+ }
+
+ new_action = xzalloc(sizeof(struct init_action));
+ if (last) {
+ last->next = new_action;
+ } else {
+ init_action_list = new_action;
+ }
+ strcpy(new_action->command, command);
+ new_action->action = action;
+ strcpy(new_action->terminal, cons);
+ messageD(L_LOG | L_CONSOLE, "command='%s' action=%d tty='%s'\n",
+ new_action->command, new_action->action, new_action->terminal);
+}
+
+static void delete_init_action(struct init_action *action)
+{
+ struct init_action *a, *b = NULL;
+
+ for (a = init_action_list; a; b = a, a = a->next) {
+ if (a == action) {
+ if (b == NULL) {
+ init_action_list = a->next;
+ } else {
+ b->next = a->next;
+ }
+ free(a);
+ break;
+ }
+ }
+}
+
+/* NOTE that if CONFIG_FEATURE_USE_INITTAB is NOT defined,
+ * then parse_inittab() simply adds in some default
+ * actions(i.e., runs INIT_SCRIPT and then starts a pair
+ * of "askfirst" shells). If CONFIG_FEATURE_USE_INITTAB
+ * _is_ defined, but /etc/inittab is missing, this
+ * results in the same set of default behaviors.
+ */
+static void parse_inittab(void)
+{
+#if ENABLE_FEATURE_USE_INITTAB
+ FILE *file;
+ char buf[INIT_BUFFS_SIZE], lineAsRead[INIT_BUFFS_SIZE];
+ char tmpConsole[CONSOLE_NAME_SIZE];
+ char *id, *runlev, *action, *command, *eol;
+ const struct init_action_type *a = actions;
+
+ file = fopen(INITTAB, "r");
+ if (file == NULL) {
+ /* No inittab file -- set up some default behavior */
+#endif
+ /* Reboot on Ctrl-Alt-Del */
+ new_init_action(CTRLALTDEL, "reboot", "");
+ /* Umount all filesystems on halt/reboot */
+ new_init_action(SHUTDOWN, "umount -a -r", "");
+ /* Swapoff on halt/reboot */
+ if (ENABLE_SWAPONOFF) new_init_action(SHUTDOWN, "swapoff -a", "");
+ /* Prepare to restart init when a HUP is received */
+ new_init_action(RESTART, "init", "");
+ /* Askfirst shell on tty1-4 */
+ new_init_action(ASKFIRST, bb_default_login_shell, "");
+ new_init_action(ASKFIRST, bb_default_login_shell, VC_2);
+ new_init_action(ASKFIRST, bb_default_login_shell, VC_3);
+ new_init_action(ASKFIRST, bb_default_login_shell, VC_4);
+ /* sysinit */
+ new_init_action(SYSINIT, INIT_SCRIPT, "");
+
+ return;
+#if ENABLE_FEATURE_USE_INITTAB
+ }
+
+ while (fgets(buf, INIT_BUFFS_SIZE, file) != NULL) {
+ /* Skip leading spaces */
+ for (id = buf; *id == ' ' || *id == '\t'; id++);
+
+ /* Skip the line if it's a comment */
+ if (*id == '#' || *id == '\n')
+ continue;
+
+ /* Trim the trailing \n */
+ //XXX: chomp() ?
+ eol = strrchr(id, '\n');
+ if (eol != NULL)
+ *eol = '\0';
+
+ /* Keep a copy around for posterity's sake (and error msgs) */
+ strcpy(lineAsRead, buf);
+
+ /* Separate the ID field from the runlevels */
+ runlev = strchr(id, ':');
+ if (runlev == NULL || *(runlev + 1) == '\0') {
+ message(L_LOG | L_CONSOLE, "Bad inittab entry: %s", lineAsRead);
+ continue;
+ } else {
+ *runlev = '\0';
+ ++runlev;
+ }
+
+ /* Separate the runlevels from the action */
+ action = strchr(runlev, ':');
+ if (action == NULL || *(action + 1) == '\0') {
+ message(L_LOG | L_CONSOLE, "Bad inittab entry: %s", lineAsRead);
+ continue;
+ } else {
+ *action = '\0';
+ ++action;
+ }
+
+ /* Separate the action from the command */
+ command = strchr(action, ':');
+ if (command == NULL || *(command + 1) == '\0') {
+ message(L_LOG | L_CONSOLE, "Bad inittab entry: %s", lineAsRead);
+ continue;
+ } else {
+ *command = '\0';
+ ++command;
+ }
+
+ /* Ok, now process it */
+ for (a = actions; a->name != 0; a++) {
+ if (strcmp(a->name, action) == 0) {
+ if (*id != '\0') {
+ if(strncmp(id, "/dev/", 5) == 0)
+ id += 5;
+ strcpy(tmpConsole, "/dev/");
+ safe_strncpy(tmpConsole + 5, id,
+ sizeof(tmpConsole) - 5);
+ id = tmpConsole;
+ }
+ new_init_action(a->action, command, id);
+ break;
+ }
+ }
+ if (a->name == 0) {
+ /* Choke on an unknown action */
+ message(L_LOG | L_CONSOLE, "Bad inittab entry: %s", lineAsRead);
+ }
+ }
+ fclose(file);
+#endif /* FEATURE_USE_INITTAB */
+}
+
+#if ENABLE_FEATURE_USE_INITTAB
+static void reload_signal(int sig ATTRIBUTE_UNUSED)
+{
+ struct init_action *a, *tmp;
+
+ message(L_LOG, "reloading /etc/inittab");
+
+ /* disable old entrys */
+ for (a = init_action_list; a; a = a->next ) {
+ a->action = ONCE;
+ }
+
+ parse_inittab();
+
+ /* remove unused entrys */
+ for (a = init_action_list; a; a = tmp) {
+ tmp = a->next;
+ if (a->action & (ONCE | SYSINIT | WAIT ) &&
+ a->pid == 0 ) {
+ delete_init_action(a);
+ }
+ }
+ run_actions(RESPAWN);
+}
+#endif /* FEATURE_USE_INITTAB */
+
+int init_main(int argc, char **argv);
+int init_main(int argc, char **argv)
+{
+ struct init_action *a;
+ pid_t wpid;
+
+ die_sleep = 30 * 24*60*60; /* if xmalloc will ever die... */
+
+ if (argc > 1 && !strcmp(argv[1], "-q")) {
+ return kill(1, SIGHUP);
+ }
+#if !ENABLE_DEBUG_INIT
+ /* Expect to be invoked as init with PID=1 or be invoked as linuxrc */
+ if (getpid() != 1
+ && (!ENABLE_FEATURE_INITRD || !strstr(applet_name, "linuxrc"))
+ ) {
+ bb_show_usage();
+ }
+ /* Set up sig handlers -- be sure to
+ * clear all of these in run() */
+ signal(SIGHUP, exec_signal);
+ signal(SIGQUIT, exec_signal);
+ signal(SIGUSR1, shutdown_signal);
+ signal(SIGUSR2, shutdown_signal);
+ signal(SIGINT, ctrlaltdel_signal);
+ signal(SIGTERM, shutdown_signal);
+ signal(SIGCONT, cont_handler);
+ signal(SIGSTOP, stop_handler);
+ signal(SIGTSTP, stop_handler);
+
+ /* Turn off rebooting via CTL-ALT-DEL -- we get a
+ * SIGINT on CAD so we can shut things down gracefully... */
+ init_reboot(RB_DISABLE_CAD);
+#endif
+
+ /* Figure out where the default console should be */
+ console_init();
+ set_sane_term();
+ chdir("/");
+ setsid();
+ {
+ const char * const *e;
+ /* Make sure environs is set to something sane */
+ for (e = environment; *e; e++)
+ putenv((char *) *e);
+ }
+
+ if (argc > 1) setenv("RUNLEVEL", argv[1], 1);
+
+ /* Hello world */
+ message(MAYBE_CONSOLE | L_LOG, "init started: %s", bb_msg_full_version);
+
+ /* Make sure there is enough memory to do something useful. */
+ if (ENABLE_SWAPONOFF) {
+ struct sysinfo info;
+
+ if (!sysinfo(&info) &&
+ (info.mem_unit ? : 1) * (long long)info.totalram < 1024*1024)
+ {
+ message(L_CONSOLE, "Low memory, forcing swapon");
+ /* swapon -a requires /proc typically */
+ new_init_action(SYSINIT, "mount -t proc proc /proc", "");
+ /* Try to turn on swap */
+ new_init_action(SYSINIT, "swapon -a", "");
+ run_actions(SYSINIT); /* wait and removing */
+ }
+ }
+
+ /* Check if we are supposed to be in single user mode */
+ if (argc > 1
+ && (!strcmp(argv[1], "single") || !strcmp(argv[1], "-s") || LONE_CHAR(argv[1], '1'))
+ ) {
+ /* Start a shell on console */
+ new_init_action(RESPAWN, bb_default_login_shell, "");
+ } else {
+ /* Not in single user mode -- see what inittab says */
+
+ /* NOTE that if CONFIG_FEATURE_USE_INITTAB is NOT defined,
+ * then parse_inittab() simply adds in some default
+ * actions(i.e., runs INIT_SCRIPT and then starts a pair
+ * of "askfirst" shells */
+ parse_inittab();
+ }
+
+#if ENABLE_SELINUX
+ if (getenv("SELINUX_INIT") == NULL) {
+ int enforce = 0;
+
+ putenv("SELINUX_INIT=YES");
+ if (selinux_init_load_policy(&enforce) == 0) {
+ BB_EXECVP(argv[0], argv);
+ } else if (enforce > 0) {
+ /* SELinux in enforcing mode but load_policy failed */
+ /* At this point, we probably can't open /dev/console, so log() won't work */
+ message(L_CONSOLE, "Cannot load SELinux Policy. "
+ "Machine is in enforcing mode. Halting now.");
+ exit(1);
+ }
+ }
+#endif /* CONFIG_SELINUX */
+
+ /* Make the command line just say "init" -- thats all, nothing else */
+ fixup_argv(argc, argv, "init");
+
+ /* Now run everything that needs to be run */
+
+ /* First run the sysinit command */
+ run_actions(SYSINIT);
+
+ /* Next run anything that wants to block */
+ run_actions(WAIT);
+
+ /* Next run anything to be run only once */
+ run_actions(ONCE);
+
+#if ENABLE_FEATURE_USE_INITTAB
+ /* Redefine SIGHUP to reread /etc/inittab */
+ signal(SIGHUP, reload_signal);
+#else
+ signal(SIGHUP, SIG_IGN);
+#endif /* FEATURE_USE_INITTAB */
+
+ /* Now run the looping stuff for the rest of forever */
+ while (1) {
+ /* run the respawn stuff */
+ run_actions(RESPAWN);
+
+ /* run the askfirst stuff */
+ run_actions(ASKFIRST);
+
+ /* Don't consume all CPU time -- sleep a bit */
+ sleep(1);
+
+ /* Wait for a child process to exit */
+ wpid = wait(NULL);
+ while (wpid > 0) {
+ /* Find out who died and clean up their corpse */
+ for (a = init_action_list; a; a = a->next) {
+ if (a->pid == wpid) {
+ /* Set the pid to 0 so that the process gets
+ * restarted by run_actions() */
+ a->pid = 0;
+ message(L_LOG, "process '%s' (pid %d) exited. "
+ "Scheduling it for restart.",
+ a->command, wpid);
+ }
+ }
+ /* see if anyone else is waiting to be reaped */
+ wpid = waitpid(-1, NULL, WNOHANG);
+ }
+ }
+}
diff --git a/i/pc104/initrd/conf/busybox/init/mesg.c b/i/pc104/initrd/conf/busybox/init/mesg.c
new file mode 100644
index 0000000..40d8ae3
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/init/mesg.c
@@ -0,0 +1,46 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * mesg implementation for busybox
+ *
+ * Copyright (c) 2002 Manuel Novoa III <mjn3@codepoet.org>
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ */
+
+#include "busybox.h"
+
+#ifdef USE_TTY_GROUP
+#define S_IWGRP_OR_S_IWOTH S_IWGRP
+#else
+#define S_IWGRP_OR_S_IWOTH (S_IWGRP | S_IWOTH)
+#endif
+
+int mesg_main(int argc, char *argv[]);
+int mesg_main(int argc, char *argv[])
+{
+ struct stat sb;
+ const char *tty;
+ char c = 0;
+
+ if (--argc == 0
+ || (argc == 1 && ((c = **++argv) == 'y' || c == 'n'))
+ ) {
+ tty = ttyname(STDERR_FILENO);
+ if (tty == NULL) {
+ tty = "ttyname";
+ } else if (stat(tty, &sb) == 0) {
+ mode_t m;
+ if (argc == 0) {
+ puts((sb.st_mode & (S_IWGRP|S_IWOTH)) ? "is y" : "is n");
+ return EXIT_SUCCESS;
+ }
+ m = (c == 'y') ? sb.st_mode | S_IWGRP_OR_S_IWOTH
+ : sb.st_mode & ~(S_IWGRP|S_IWOTH);
+ if (chmod(tty, m) == 0) {
+ return EXIT_SUCCESS;
+ }
+ }
+ bb_perror_msg_and_die("%s", tty);
+ }
+ bb_show_usage();
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/Config.in b/i/pc104/initrd/conf/busybox/libbb/Config.in
new file mode 100644
index 0000000..112a3d6
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/Config.in
@@ -0,0 +1,88 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+menu "Busybox Library Tuning"
+
+config PASSWORD_MINLEN
+ int "Minimum password length"
+ default 6
+ range 5 32
+ help
+ Minimum allowable password length.
+
+config MD5_SIZE_VS_SPEED
+ int " MD5: Trade Bytes for Speed"
+ default 2
+ range 0 3
+ help
+ Trade binary size versus speed for the md5sum algorithm.
+ Approximate values running uClibc and hashing
+ linux-2.4.4.tar.bz2 were:
+ user times (sec) text size (386)
+ 0 (fastest) 1.1 6144
+ 1 1.4 5392
+ 2 3.0 5088
+ 3 (smallest) 5.1 4912
+
+config FEATURE_EDITING
+ bool "Command line editing"
+ default n
+ help
+ Enable command editing (mainly for shell).
+
+config FEATURE_EDITING_FANCY_KEYS
+ bool "Additional editing keys"
+ default n
+ depends on FEATURE_EDITING
+ help
+ Enable additonal editing keys (Ctrl-E, Ctrl-U etc).
+ Arrow keys, Home/End/Delete and Ctrl-W work even without this option.
+
+config FEATURE_EDITING_VI
+ bool "vi-style line editing commands"
+ default n
+ depends on FEATURE_EDITING
+ help
+ Enable vi-style line editing. In shells, this mode can be
+ turned on and off with "set -o vi" and "set +o vi".
+
+config FEATURE_EDITING_HISTORY
+ int "History size"
+ range 0 99999
+ default 15
+ depends on FEATURE_EDITING
+ help
+ Specify command history size.
+
+config FEATURE_EDITING_SAVEHISTORY
+ bool "History saving"
+ default n
+ depends on ASH && FEATURE_EDITING
+ help
+ Enable history saving in ash shell.
+
+config FEATURE_TAB_COMPLETION
+ bool "Tab completion"
+ default n
+ depends on FEATURE_EDITING
+ help
+ Enable tab completion.
+
+config FEATURE_USERNAME_COMPLETION
+ bool "Username completion"
+ default n
+ depends on FEATURE_TAB_COMPLETION
+ help
+ Enable username completion.
+
+config FEATURE_EDITING_FANCY_PROMPT
+ bool "Fancy shell prompts"
+ default n
+ depends on FEATURE_EDITING
+ help
+ Setting this option allows for prompts to use things like \w and
+ \$ and escape codes.
+
+endmenu
diff --git a/i/pc104/initrd/conf/busybox/libbb/Kbuild b/i/pc104/initrd/conf/busybox/libbb/Kbuild
new file mode 100644
index 0000000..ffded6a
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/Kbuild
@@ -0,0 +1,120 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under the GPL v2, see the file LICENSE in this tarball.
+
+lib-y:=
+
+lib-y += ask_confirmation.o
+lib-y += bb_askpass.o
+lib-y += bb_do_delay.o
+lib-y += bb_pwd.o
+lib-y += bb_strtonum.o
+lib-y += change_identity.o
+lib-y += chomp.o
+lib-y += compare_string_array.o
+lib-y += concat_path_file.o
+lib-y += concat_subpath_file.o
+lib-y += copy_file.o
+lib-y += copyfd.o
+lib-y += crc32.o
+lib-y += create_icmp6_socket.o
+lib-y += create_icmp_socket.o
+lib-y += default_error_retval.o
+lib-y += device_open.o
+lib-y += dump.o
+lib-y += error_msg.o
+lib-y += error_msg_and_die.o
+lib-y += execable.o
+lib-y += fclose_nonstdin.o
+lib-y += fflush_stdout_and_exit.o
+lib-y += fgets_str.o
+lib-y += find_pid_by_name.o
+lib-y += find_root_device.o
+lib-y += full_write.o
+lib-y += get_console.o
+lib-y += get_last_path_component.o
+lib-y += get_line_from_file.o
+lib-y += getopt32.o
+lib-y += herror_msg.o
+lib-y += herror_msg_and_die.o
+lib-y += human_readable.o
+lib-y += inet_common.o
+lib-y += info_msg.o
+lib-y += inode_hash.o
+lib-y += isdirectory.o
+lib-y += kernel_version.o
+lib-y += last_char_is.o
+lib-y += lineedit.o
+lib-y += llist.o
+lib-y += login.o
+lib-y += make_directory.o
+lib-y += makedev.o
+lib-y += md5.o
+lib-y += messages.o
+lib-y += mode_string.o
+lib-y += mtab_file.o
+lib-y += obscure.o
+lib-y += parse_mode.o
+lib-y += perror_msg.o
+lib-y += perror_msg_and_die.o
+lib-y += perror_nomsg.o
+lib-y += perror_nomsg_and_die.o
+lib-y += process_escape_sequence.o
+lib-y += procps.o
+lib-y += read.o
+lib-y += recursive_action.o
+lib-y += remove_file.o
+lib-y += restricted_shell.o
+lib-y += run_shell.o
+lib-y += safe_strncpy.o
+lib-y += safe_write.o
+lib-y += setup_environment.o
+lib-y += sha1.o
+lib-y += simplify_path.o
+lib-y += skip_whitespace.o
+lib-y += speed_table.o
+lib-y += trim.o
+lib-y += u_signal_names.o
+lib-y += uuencode.o
+lib-y += vdprintf.o
+lib-y += verror_msg.o
+lib-y += vfork_daemon_rexec.o
+lib-y += vherror_msg.o
+lib-y += vinfo_msg.o
+lib-y += vperror_msg.o
+lib-y += warn_ignoring_args.o
+lib-y += wfopen.o
+lib-y += wfopen_input.o
+lib-y += xatonum.o
+lib-y += xconnect.o
+lib-y += xfuncs.o
+lib-y += xgetcwd.o
+lib-y += xgethostbyname.o
+lib-y += xreadlink.o
+
+# conditionally compiled objects:
+lib-$(CONFIG_FEATURE_MOUNT_LOOP) += loop.o
+lib-$(CONFIG_LOSETUP) += loop.o
+lib-$(CONFIG_FEATURE_MTAB_SUPPORT) += mtab.o
+lib-$(CONFIG_PASSWD) += pw_encrypt.o
+lib-$(CONFIG_SULOGIN) += pw_encrypt.o
+lib-$(CONFIG_FEATURE_HTTPD_AUTH_MD5) += pw_encrypt.o
+lib-$(CONFIG_VLOCK) += correct_password.o
+lib-$(CONFIG_SU) += correct_password.o
+lib-$(CONFIG_LOGIN) += correct_password.o
+lib-$(CONFIG_DF) += find_mount_point.o
+lib-$(CONFIG_MKFS_MINIX) += find_mount_point.o
+lib-$(CONFIG_SELINUX) += selinux_common.o
+
+# We shouldn't build xregcomp.c if we don't need it - this ensures we don't
+# require regex.h to be in the include dir even if we don't need it thereby
+# allowing us to build busybox even if uclibc regex support is disabled.
+
+lib-$(CONFIG_AWK) += xregcomp.o
+lib-$(CONFIG_SED) += xregcomp.o
+lib-$(CONFIG_GREP) += xregcomp.o
+lib-$(CONFIG_MDEV) += xregcomp.o
+lib-$(CONFIG_LESS) += xregcomp.o
+lib-$(CONFIG_DEVFSD) += xregcomp.o
diff --git a/i/pc104/initrd/conf/busybox/libbb/README b/i/pc104/initrd/conf/busybox/libbb/README
new file mode 100644
index 0000000..4f28f7e
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/README
@@ -0,0 +1,11 @@
+Please see the LICENSE file for copyright information (GPLv2)
+
+libbb is BusyBox's utility library. All of this stuff used to be stuffed into
+a single file named utility.c. When I split utility.c to create libbb, some of
+the very oldest stuff ended up without their original copyright and licensing
+information (which is now lost in the mists of time). If you see something
+that you wrote that is mis-attributed, do let me know so we can fix that up.
+
+ Erik Andersen
+ <andersen@codepoet.org>
+
diff --git a/i/pc104/initrd/conf/busybox/libbb/ask_confirmation.c b/i/pc104/initrd/conf/busybox/libbb/ask_confirmation.c
new file mode 100644
index 0000000..4642fa0
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/ask_confirmation.c
@@ -0,0 +1,36 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * bb_ask_confirmation implementation for busybox
+ *
+ * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/* Read a line from stdin. If the first non-whitespace char is 'y' or 'Y',
+ * return 1. Otherwise return 0.
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include "libbb.h"
+
+int bb_ask_confirmation(void)
+{
+ int retval = 0;
+ int first = 1;
+ int c;
+
+ while (((c = getchar()) != EOF) && (c != '\n')) {
+ /* Make sure we get the actual function call for isspace,
+ * as speed is not critical here. */
+ if (first && !(isspace)(c)) {
+ --first;
+ if ((c == 'y') || (c == 'Y')) {
+ ++retval;
+ }
+ }
+ }
+
+ return retval;
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/bb_askpass.c b/i/pc104/initrd/conf/busybox/libbb/bb_askpass.c
new file mode 100644
index 0000000..097a0a2
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/bb_askpass.c
@@ -0,0 +1,75 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Ask for a password
+ * I use a static buffer in this function. Plan accordingly.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <termios.h>
+#include <sys/ioctl.h>
+
+#include "libbb.h"
+
+/* do nothing signal handler */
+static void askpass_timeout(int ATTRIBUTE_UNUSED ignore)
+{
+}
+
+char *bb_askpass(int timeout, const char * prompt)
+{
+ static char passwd[64];
+
+ char *ret;
+ int i;
+ struct sigaction sa;
+ struct termios old, new;
+
+ tcgetattr(STDIN_FILENO, &old);
+ tcflush(STDIN_FILENO, TCIFLUSH);
+
+ memset(passwd, 0, sizeof(passwd));
+
+ fputs(prompt, stdout);
+ fflush(stdout);
+
+ tcgetattr(STDIN_FILENO, &new);
+ new.c_iflag &= ~(IUCLC|IXON|IXOFF|IXANY);
+ new.c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL|TOSTOP);
+ tcsetattr(STDIN_FILENO, TCSANOW, &new);
+
+ if (timeout) {
+ sa.sa_flags = 0;
+ sa.sa_handler = askpass_timeout;
+ sigaction(SIGALRM, &sa, NULL);
+ alarm(timeout);
+ }
+
+ ret = NULL;
+ if (read(STDIN_FILENO, passwd, sizeof(passwd)-1) > 0) {
+ ret = passwd;
+ i = 0;
+ /* Last byte is guaranteed to be 0
+ (read did not overwrite it) */
+ do {
+ if (passwd[i] == '\r' || passwd[i] == '\n')
+ passwd[i] = '\0';
+ } while (passwd[i++]);
+ }
+
+ if (timeout) {
+ alarm(0);
+ }
+
+ tcsetattr(STDIN_FILENO, TCSANOW, &old);
+ puts("");
+ fflush(stdout);
+ return ret;
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/bb_do_delay.c b/i/pc104/initrd/conf/busybox/libbb/bb_do_delay.c
new file mode 100644
index 0000000..e14b67a
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/bb_do_delay.c
@@ -0,0 +1,24 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Busybox utility routines.
+ *
+ * Copyright (C) 2005 by Tito Ragusa <tito-wolit@tiscali.it>
+ *
+ * Licensed under the GPL v2, see the file LICENSE in this tarball.
+ */
+
+#include <time.h>
+#include <unistd.h>
+#include "libbb.h"
+
+void bb_do_delay(int seconds)
+{
+ time_t start, now;
+
+ time(&start);
+ now = start;
+ while (difftime(now, start) < seconds) {
+ sleep(seconds);
+ time(&now);
+ }
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/bb_pwd.c b/i/pc104/initrd/conf/busybox/libbb/bb_pwd.c
new file mode 100644
index 0000000..223a6b4
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/bb_pwd.c
@@ -0,0 +1,129 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * password utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ */
+
+#include "libbb.h"
+
+#define assert(x) ((void)0)
+
+/*
+ * if bufsize is > 0 char *buffer cannot be set to NULL.
+ * If idname is not NULL it is written on the static
+ * allocated buffer (and a pointer to it is returned).
+ * if idname is NULL, id as string is written to the static
+ * allocated buffer and NULL is returned.
+ * if bufsize is = 0 char *buffer can be set to NULL.
+ * If idname exists a pointer to it is returned,
+ * else NULL is returned.
+ * if bufsize is < 0 char *buffer can be set to NULL.
+ * If idname exists a pointer to it is returned,
+ * else an error message is printed and the program exits.
+ */
+
+/* internal function for bb_getpwuid and bb_getgrgid */
+static char* bb_getug(char *buffer, char *idname, long id, int bufsize, char prefix)
+{
+ if (bufsize > 0 ) {
+ assert(buffer != NULL);
+ if (idname) {
+ return safe_strncpy(buffer, idname, bufsize);
+ }
+ snprintf(buffer, bufsize, "%ld", id);
+ } else if (bufsize < 0 && !idname) {
+ bb_error_msg_and_die("unknown %cid %ld", prefix, id);
+ }
+ return idname;
+}
+
+/* Hacked by Tito Ragusa (c) 2004 <farmatito@tiscali.it> to make it more
+ * flexible :
+ *
+ * if bufsize is > 0 char *group cannot be set to NULL.
+ * On success groupname is written on static allocated buffer
+ * group (and a pointer to it is returned).
+ * On failure gid as string is written to static allocated
+ * buffer group and NULL is returned.
+ * if bufsize is = 0 char *group can be set to NULL.
+ * On success groupname is returned.
+ * On failure NULL is returned.
+ * if bufsize is < 0 char *group can be set to NULL.
+ * On success groupname is returned.
+ * On failure an error message is printed and
+ * the program exits.
+ */
+
+/* gets a groupname given a gid */
+char* bb_getgrgid(char *group, long gid, int bufsize)
+{
+ struct group *mygroup = getgrgid(gid);
+
+ return bb_getug(group,
+ mygroup ? mygroup->gr_name : (char *)mygroup,
+ gid, bufsize, 'g');
+}
+
+/* returns a gid given a group name */
+long xgroup2gid(const char *name)
+{
+ struct group *mygroup;
+
+ mygroup = getgrnam(name);
+ if (mygroup == NULL)
+ bb_error_msg_and_die("unknown group name: %s", name);
+
+ return mygroup->gr_gid;
+}
+
+/* returns a uid given a username */
+long xuname2uid(const char *name)
+{
+ struct passwd *myuser;
+
+ myuser = getpwnam(name);
+ if (myuser == NULL)
+ bb_error_msg_and_die("unknown user name: %s", name);
+
+ return myuser->pw_uid;
+}
+
+/* Hacked by Tito Ragusa (c) 2004 <farmatito@tiscali.it> to make it more
+ * flexible :
+ *
+ * if bufsize is > 0 char *name cannot be set to NULL.
+ * On success username is written on the static allocated
+ * buffer name (and a pointer to it is returned).
+ * On failure uid as string is written to the static
+ * allocated buffer name and NULL is returned.
+ * if bufsize is = 0 char *name can be set to NULL.
+ * On success username is returned.
+ * On failure NULL is returned.
+ * if bufsize is < 0 char *name can be set to NULL
+ * On success username is returned.
+ * On failure an error message is printed and
+ * the program exits.
+ */
+
+/* gets a username given a uid */
+char* bb_getpwuid(char *name, long uid, int bufsize)
+{
+ struct passwd *myuser = getpwuid(uid);
+
+ return bb_getug(name, myuser ? myuser->pw_name : (char *)myuser,
+ uid, bufsize, 'u');
+}
+
+unsigned long get_ug_id(const char *s,
+ long (*xname2id)(const char *))
+{
+ unsigned long r;
+
+ r = bb_strtoul(s, NULL, 10);
+ if (errno)
+ return xname2id(s);
+ return r;
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/bb_strtonum.c b/i/pc104/initrd/conf/busybox/libbb/bb_strtonum.c
new file mode 100644
index 0000000..50ef0ba
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/bb_strtonum.c
@@ -0,0 +1,156 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "libbb.h"
+
+/* On exit: errno = 0 only if there was non-empty, '\0' terminated value
+ * errno = EINVAL if value was not '\0' terminated, but othervise ok
+ * Return value is still valid, caller should just check whether end[0]
+ * is a valid terminating char for particular case. OTOH, if caller
+ * requires '\0' terminated input, [s]he can just check errno == 0.
+ * errno = ERANGE if value had alphanumeric terminating char ("1234abcg").
+ * errno = ERANGE if value is out of range, missing, etc.
+ * errno = ERANGE if value had minus sign for strtouXX (even "-0" is not ok )
+ * return value is all-ones in this case.
+ */
+
+static unsigned long long ret_ERANGE(void)
+{
+ errno = ERANGE; /* this ain't as small as it looks (on glibc) */
+ return ULLONG_MAX;
+}
+
+static unsigned long long handle_errors(unsigned long long v, char **endp, char *endptr)
+{
+ if (endp) *endp = endptr;
+
+ /* Check for the weird "feature":
+ * a "-" string is apparently a valid "number" for strto[u]l[l]!
+ * It returns zero and errno is 0! :( */
+ if (endptr[-1] == '-')
+ return ret_ERANGE();
+
+ /* errno is already set to ERANGE by strtoXXX if value overflowed */
+ if (endptr[0]) {
+ /* "1234abcg" or out-of-range? */
+ if (isalnum(endptr[0]) || errno)
+ return ret_ERANGE();
+ /* good number, just suspicious terminator */
+ errno = EINVAL;
+ }
+ return v;
+}
+
+
+unsigned long long bb_strtoull(const char *arg, char **endp, int base)
+{
+ unsigned long long v;
+ char *endptr;
+
+ /* strtoul(" -4200000000") returns 94967296, errno 0 (!) */
+ /* I don't think that this is right. Preventing this... */
+ if (!isalnum(arg[0])) return ret_ERANGE();
+
+ /* not 100% correct for lib func, but convenient for the caller */
+ errno = 0;
+ v = strtoull(arg, &endptr, base);
+ return handle_errors(v, endp, endptr);
+}
+
+long long bb_strtoll(const char *arg, char **endp, int base)
+{
+ unsigned long long v;
+ char *endptr;
+
+ if (arg[0] != '-' && !isalnum(arg[0])) return ret_ERANGE();
+ errno = 0;
+ v = strtoll(arg, &endptr, base);
+ return handle_errors(v, endp, endptr);
+}
+
+#if ULONG_MAX != ULLONG_MAX
+unsigned long bb_strtoul(const char *arg, char **endp, int base)
+{
+ unsigned long v;
+ char *endptr;
+
+ if (!isalnum(arg[0])) return ret_ERANGE();
+ errno = 0;
+ v = strtoul(arg, &endptr, base);
+ return handle_errors(v, endp, endptr);
+}
+
+long bb_strtol(const char *arg, char **endp, int base)
+{
+ long v;
+ char *endptr;
+
+ if (arg[0] != '-' && !isalnum(arg[0])) return ret_ERANGE();
+ errno = 0;
+ v = strtol(arg, &endptr, base);
+ return handle_errors(v, endp, endptr);
+}
+#endif
+
+#if UINT_MAX != ULONG_MAX
+unsigned bb_strtou(const char *arg, char **endp, int base)
+{
+ unsigned long v;
+ char *endptr;
+
+ if (!isalnum(arg[0])) return ret_ERANGE();
+ errno = 0;
+ v = strtoul(arg, &endptr, base);
+ if (v > UINT_MAX) return ret_ERANGE();
+ return handle_errors(v, endp, endptr);
+}
+
+int bb_strtoi(const char *arg, char **endp, int base)
+{
+ long v;
+ char *endptr;
+
+ if (arg[0] != '-' && !isalnum(arg[0])) return ret_ERANGE();
+ errno = 0;
+ v = strtol(arg, &endptr, base);
+ if (v > INT_MAX) return ret_ERANGE();
+ if (v < INT_MIN) return ret_ERANGE();
+ return handle_errors(v, endp, endptr);
+}
+#endif
+
+/* Floating point */
+
+#if 0
+
+#include <math.h> /* just for HUGE_VAL */
+#define NOT_DIGIT(a) (((unsigned char)(a-'0')) > 9)
+double bb_strtod(const char *arg, char **endp)
+{
+ double v;
+ char *endptr;
+
+ if (arg[0] != '-' && NOT_DIGIT(arg[0])) goto err;
+ errno = 0;
+ v = strtod(arg, &endptr);
+ if (endp) *endp = endptr;
+ if (endptr[0]) {
+ /* "1234abcg" or out-of-range? */
+ if (isalnum(endptr[0]) || errno) {
+ err:
+ errno = ERANGE;
+ return HUGE_VAL;
+ }
+ /* good number, just suspicious terminator */
+ errno = EINVAL;
+ }
+ return v;
+}
+
+#endif
diff --git a/i/pc104/initrd/conf/busybox/libbb/change_identity.c b/i/pc104/initrd/conf/busybox/libbb/change_identity.c
new file mode 100644
index 0000000..3f888f5
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/change_identity.c
@@ -0,0 +1,51 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Copyright 1989 - 1991, Julianne Frances Haugh <jockgrrl@austin.rr.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Julianne F. Haugh nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "libbb.h"
+
+
+/* Become the user and group(s) specified by PW. */
+const char *change_identity_e2str(const struct passwd *pw)
+{
+ if (initgroups(pw->pw_name, pw->pw_gid) == -1)
+ return "cannot set groups";
+ endgrent(); /* ?? */
+ xsetgid(pw->pw_gid);
+ xsetuid(pw->pw_uid);
+ return NULL;
+}
+
+void change_identity(const struct passwd *pw)
+{
+ const char *err_msg = change_identity_e2str(pw);
+
+ if (err_msg)
+ bb_perror_msg_and_die("%s", err_msg);
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/chomp.c b/i/pc104/initrd/conf/busybox/libbb/chomp.c
new file mode 100644
index 0000000..eab7707
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/chomp.c
@@ -0,0 +1,19 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) many different people.
+ * If you wrote this, please acknowledge your work.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "libbb.h"
+
+void chomp(char *s)
+{
+ char *lc = last_char_is(s, '\n');
+
+ if (lc)
+ *lc = 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/compare_string_array.c b/i/pc104/initrd/conf/busybox/libbb/compare_string_array.c
new file mode 100644
index 0000000..d15578c
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/compare_string_array.c
@@ -0,0 +1,37 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "libbb.h"
+
+/* returns the array index of the string */
+/* (index of first match is returned, or -1) */
+int index_in_str_array(const char * const string_array[], const char *key)
+{
+ int i;
+
+ for (i = 0; string_array[i] != 0; i++) {
+ if (strcmp(string_array[i], key) == 0) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+/* returns the array index of the string, even if it matches only a beginning */
+/* (index of first match is returned, or -1) */
+int index_in_substr_array(const char * const string_array[], const char *key)
+{
+ int i;
+ int len = strlen(key);
+ if (!len)
+ return -1;
+
+ for (i = 0; string_array[i] != 0; i++) {
+ if (strncmp(string_array[i], key, len) == 0) {
+ return i;
+ }
+ }
+ return -1;
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/concat_path_file.c b/i/pc104/initrd/conf/busybox/libbb/concat_path_file.c
new file mode 100644
index 0000000..9aae601
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/concat_path_file.c
@@ -0,0 +1,27 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) many different people.
+ * If you wrote this, please acknowledge your work.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/* concatenate path and file name to new allocation buffer,
+ * not adding '/' if path name already has '/'
+*/
+
+#include "libbb.h"
+
+char *concat_path_file(const char *path, const char *filename)
+{
+ char *lc;
+
+ if (!path)
+ path = "";
+ lc = last_char_is(path, '/');
+ while (*filename == '/')
+ filename++;
+ return xasprintf("%s%s%s", path, (lc==NULL ? "/" : ""), filename);
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/concat_subpath_file.c b/i/pc104/initrd/conf/busybox/libbb/concat_subpath_file.c
new file mode 100644
index 0000000..6bbe49a
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/concat_subpath_file.c
@@ -0,0 +1,23 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/*
+ This function make special for recursive actions with usage
+ concat_path_file(path, filename)
+ and skipping "." and ".." directory entries
+*/
+
+#include "libbb.h"
+
+char *concat_subpath_file(const char *path, const char *f)
+{
+ if (f && *f == '.' && (!f[1] || (f[1] == '.' && !f[2])))
+ return NULL;
+ return concat_path_file(path, f);
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/copy_file.c b/i/pc104/initrd/conf/busybox/libbb/copy_file.c
new file mode 100644
index 0000000..7d85920
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/copy_file.c
@@ -0,0 +1,343 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini copy_file implementation for busybox
+ *
+ * Copyright (C) 2001 by Matt Kraai <kraai@alumni.carnegiemellon.edu>
+ * SELinux support by Yuichi Nakamura <ynakam@hitachisoft.jp>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ *
+ */
+
+#include "libbb.h"
+
+// POSIX: if exists and -i, ask (w/o -i assume yes).
+// Then open w/o EXCL (yes, not unlink!).
+// If open still fails and -f, try unlink, then try open again.
+// Result: a mess:
+// If dest is a softlink, we overwrite softlink's destination!
+// (or fail, if it points to dir/nonexistent location/etc).
+// This is strange, but POSIX-correct.
+// coreutils cp has --remove-destination to override this...
+
+#define DO_POSIX_CP 0 /* 1 - POSIX behavior, 0 - safe behavior */
+
+
+static int ask_and_unlink(const char *dest, int flags)
+{
+ // If !DO_POSIX_CP, act as if -f is always in effect - we don't want
+ // "'file' exists" msg, we want unlink to be done (silently unless -i
+ // is also in effect).
+ // This prevents safe way from asking more questions than POSIX does.
+#if DO_POSIX_CP
+ if (!(flags & (FILEUTILS_FORCE|FILEUTILS_INTERACTIVE))) {
+ fprintf(stderr, "'%s' exists\n", dest);
+ return -1;
+ }
+#endif
+
+ // TODO: maybe we should do it only if ctty is present?
+ if (flags & FILEUTILS_INTERACTIVE) {
+ // We would not do POSIX insanity. -i asks,
+ // then _unlinks_ the offender. Presto.
+ // (No opening without O_EXCL, no unlinks only if -f)
+ // Or else we will end up having 3 open()s!
+ fprintf(stderr, "%s: overwrite '%s'? ", applet_name, dest);
+ if (!bb_ask_confirmation())
+ return 0; // not allowed to overwrite
+ }
+ if (unlink(dest) < 0) {
+ bb_perror_msg("cannot remove '%s'", dest);
+ return -1; // error
+ }
+ return 1; // ok (to try again)
+}
+
+/* Return:
+ * -1 error, copy not made
+ * 0 copy is made or user answered "no" in interactive mode
+ * (failures to preserve mode/owner/times are not reported in exit code)
+ */
+int copy_file(const char *source, const char *dest, int flags)
+{
+ struct stat source_stat;
+ struct stat dest_stat;
+ int status = 0;
+ signed char dest_exists = 0;
+ signed char ovr;
+
+#define FLAGS_DEREF (flags & FILEUTILS_DEREFERENCE)
+
+ if ((FLAGS_DEREF ? stat : lstat)(source, &source_stat) < 0) {
+ // This may be a dangling symlink.
+ // Making [sym]links to dangling symlinks works, so...
+ if (flags & (FILEUTILS_MAKE_SOFTLINK|FILEUTILS_MAKE_HARDLINK))
+ goto make_links;
+ bb_perror_msg("cannot stat '%s'", source);
+ return -1;
+ }
+
+ if (lstat(dest, &dest_stat) < 0) {
+ if (errno != ENOENT) {
+ bb_perror_msg("cannot stat '%s'", dest);
+ return -1;
+ }
+ } else {
+ if (source_stat.st_dev == dest_stat.st_dev
+ && source_stat.st_ino == dest_stat.st_ino
+ ) {
+ bb_error_msg("'%s' and '%s' are the same file", source, dest);
+ return -1;
+ }
+ dest_exists = 1;
+ }
+
+#if ENABLE_SELINUX
+ if ((flags & FILEUTILS_PRESERVE_SECURITY_CONTEXT) && is_selinux_enabled() > 0) {
+ security_context_t con;
+ if (lgetfilecon(source, &con) >= 0) {
+ if (setfscreatecon(con) < 0) {
+ bb_perror_msg("cannot set setfscreatecon %s", con);
+ freecon(con);
+ return -1;
+ }
+ } else if (errno == ENOTSUP || errno == ENODATA) {
+ setfscreatecon_or_die(NULL);
+ } else {
+ bb_perror_msg("cannot lgetfilecon %s", source);
+ return -1;
+ }
+ }
+#endif
+
+ if (S_ISDIR(source_stat.st_mode)) {
+ DIR *dp;
+ struct dirent *d;
+ mode_t saved_umask = 0;
+
+ if (!(flags & FILEUTILS_RECUR)) {
+ bb_error_msg("omitting directory '%s'", source);
+ return -1;
+ }
+
+ /* Create DEST. */
+ if (dest_exists) {
+ if (!S_ISDIR(dest_stat.st_mode)) {
+ bb_error_msg("target '%s' is not a directory", dest);
+ return -1;
+ }
+ } else {
+ mode_t mode;
+ saved_umask = umask(0);
+
+ mode = source_stat.st_mode;
+ if (!(flags & FILEUTILS_PRESERVE_STATUS))
+ mode = source_stat.st_mode & ~saved_umask;
+ mode |= S_IRWXU;
+
+ if (mkdir(dest, mode) < 0) {
+ umask(saved_umask);
+ bb_perror_msg("cannot create directory '%s'", dest);
+ return -1;
+ }
+
+ umask(saved_umask);
+ }
+
+ /* Recursively copy files in SOURCE. */
+ dp = opendir(source);
+ if (dp == NULL) {
+ status = -1;
+ goto preserve_status;
+ }
+
+ while ((d = readdir(dp)) != NULL) {
+ char *new_source, *new_dest;
+
+ new_source = concat_subpath_file(source, d->d_name);
+ if (new_source == NULL)
+ continue;
+ new_dest = concat_path_file(dest, d->d_name);
+ if (copy_file(new_source, new_dest, flags) < 0)
+ status = -1;
+ free(new_source);
+ free(new_dest);
+ }
+ closedir(dp);
+
+ if (!dest_exists
+ && chmod(dest, source_stat.st_mode & ~saved_umask) < 0
+ ) {
+ bb_perror_msg("cannot change permissions of '%s'", dest);
+ status = -1;
+ }
+
+ } else if (flags & (FILEUTILS_MAKE_SOFTLINK|FILEUTILS_MAKE_HARDLINK)) {
+ int (*lf)(const char *oldpath, const char *newpath);
+ make_links:
+ // Hmm... maybe
+ // if (DEREF && MAKE_SOFTLINK) source = realpath(source) ?
+ // (but realpath returns NULL on dangling symlinks...)
+ lf = (flags & FILEUTILS_MAKE_SOFTLINK) ? symlink : link;
+ if (lf(source, dest) < 0) {
+ ovr = ask_and_unlink(dest, flags);
+ if (ovr <= 0)
+ return ovr;
+ if (lf(source, dest) < 0) {
+ bb_perror_msg("cannot create link '%s'", dest);
+ return -1;
+ }
+ }
+ return 0;
+
+ } else if (S_ISREG(source_stat.st_mode)
+ /* Huh? DEREF uses stat, which never returns links! */
+ /* || (FLAGS_DEREF && S_ISLNK(source_stat.st_mode)) */
+ ) {
+ int src_fd;
+ int dst_fd;
+ if (ENABLE_FEATURE_PRESERVE_HARDLINKS) {
+ char *link_name;
+
+ if (!FLAGS_DEREF) {
+ link_name = is_in_ino_dev_hashtable(&source_stat);
+ if (link_name) {
+ if (link(link_name, dest) < 0) {
+ ovr = ask_and_unlink(dest, flags);
+ if (ovr <= 0)
+ return ovr;
+ if (link(link_name, dest) < 0) {
+ bb_perror_msg("cannot create link '%s'", dest);
+ return -1;
+ }
+ }
+ return 0;
+ }
+ }
+ add_to_ino_dev_hashtable(&source_stat, dest);
+ }
+
+ src_fd = open(source, O_RDONLY);
+ if (src_fd == -1) {
+ bb_perror_msg("cannot open '%s'", source);
+ return -1;
+ }
+
+#if DO_POSIX_CP /* POSIX way (a security problem versus symlink attacks!): */
+ dst_fd = open(dest, (flags & FILEUTILS_INTERACTIVE)
+ ? O_WRONLY|O_CREAT|O_EXCL
+ : O_WRONLY|O_CREAT|O_TRUNC, source_stat.st_mode);
+#else /* safe way: */
+ dst_fd = open(dest, O_WRONLY|O_CREAT|O_EXCL, source_stat.st_mode);
+#endif
+ if (dst_fd == -1) {
+ ovr = ask_and_unlink(dest, flags);
+ if (ovr <= 0) {
+ close(src_fd);
+ return ovr;
+ }
+ /* It shouldn't exist. If it exists, do not open (symlink attack?) */
+ dst_fd = open(dest, O_WRONLY|O_CREAT|O_EXCL, source_stat.st_mode);
+ if (dst_fd == -1) {
+ bb_perror_msg("cannot open '%s'", dest);
+ close(src_fd);
+ return -1;
+ }
+ }
+
+#if ENABLE_SELINUX
+ if (((flags & FILEUTILS_PRESERVE_SECURITY_CONTEXT)
+ || (flags & FILEUTILS_SET_SECURITY_CONTEXT))
+ && is_selinux_enabled() > 0
+ ) {
+ security_context_t con;
+ if (getfscreatecon(&con) == -1) {
+ bb_perror_msg("getfscreatecon");
+ return -1;
+ }
+ if (con) {
+ if(setfilecon(dest, con) == -1) {
+ bb_perror_msg("setfilecon:%s,%s", dest, con);
+ freecon(con);
+ return -1;
+ }
+ freecon(con);
+ }
+ }
+#endif
+ if (bb_copyfd_eof(src_fd, dst_fd) == -1)
+ status = -1;
+ if (close(dst_fd) < 0) {
+ bb_perror_msg("cannot close '%s'", dest);
+ status = -1;
+ }
+ if (close(src_fd) < 0) {
+ bb_perror_msg("cannot close '%s'", source);
+ status = -1;
+ }
+
+ } else if (S_ISBLK(source_stat.st_mode) || S_ISCHR(source_stat.st_mode)
+ || S_ISSOCK(source_stat.st_mode) || S_ISFIFO(source_stat.st_mode)
+ || S_ISLNK(source_stat.st_mode)
+ ) {
+ // We are lazy here, a bit lax with races...
+ if (dest_exists) {
+ ovr = ask_and_unlink(dest, flags);
+ if (ovr <= 0)
+ return ovr;
+ }
+ if (S_ISFIFO(source_stat.st_mode)) {
+ if (mkfifo(dest, source_stat.st_mode) < 0) {
+ bb_perror_msg("cannot create fifo '%s'", dest);
+ return -1;
+ }
+ } else if (S_ISLNK(source_stat.st_mode)) {
+ char *lpath;
+
+ lpath = xmalloc_readlink_or_warn(source);
+ if (lpath && symlink(lpath, dest) < 0) {
+ bb_perror_msg("cannot create symlink '%s'", dest);
+ free(lpath);
+ return -1;
+ }
+ free(lpath);
+
+ if (flags & FILEUTILS_PRESERVE_STATUS)
+ if (lchown(dest, source_stat.st_uid, source_stat.st_gid) < 0)
+ bb_perror_msg("cannot preserve %s of '%s'", "ownership", dest);
+
+ return 0;
+
+ } else {
+ if (mknod(dest, source_stat.st_mode, source_stat.st_rdev) < 0) {
+ bb_perror_msg("cannot create '%s'", dest);
+ return -1;
+ }
+ }
+ } else {
+ bb_error_msg("internal error: unrecognized file type");
+ return -1;
+ }
+
+ preserve_status:
+
+ if (flags & FILEUTILS_PRESERVE_STATUS
+ /* Cannot happen: */
+ /* && !(flags & (FILEUTILS_MAKE_SOFTLINK|FILEUTILS_MAKE_HARDLINK)) */
+ ) {
+ struct utimbuf times;
+
+ times.actime = source_stat.st_atime;
+ times.modtime = source_stat.st_mtime;
+ if (utime(dest, &times) < 0)
+ bb_perror_msg("cannot preserve %s of '%s'", "times", dest);
+ if (chown(dest, source_stat.st_uid, source_stat.st_gid) < 0) {
+ source_stat.st_mode &= ~(S_ISUID | S_ISGID);
+ bb_perror_msg("cannot preserve %s of '%s'", "ownership", dest);
+ }
+ if (chmod(dest, source_stat.st_mode) < 0)
+ bb_perror_msg("cannot preserve %s of '%s'", "permissions", dest);
+ }
+
+ return status;
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/copyfd.c b/i/pc104/initrd/conf/busybox/libbb/copyfd.c
new file mode 100644
index 0000000..0d0ec22
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/copyfd.c
@@ -0,0 +1,106 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "libbb.h"
+
+
+#if BUFSIZ < 4096
+#undef BUFSIZ
+#define BUFSIZ 4096
+#endif
+
+
+static off_t bb_full_fd_action(int src_fd, int dst_fd, off_t size)
+{
+ int status = -1;
+ off_t total = 0;
+ RESERVE_CONFIG_BUFFER(buffer, BUFSIZ);
+
+ if (src_fd < 0) goto out;
+
+ if (!size) {
+ size = BUFSIZ;
+ status = 1; /* copy until eof */
+ }
+
+ while (1) {
+ ssize_t rd;
+
+ rd = safe_read(src_fd, buffer, size > BUFSIZ ? BUFSIZ : size);
+
+ if (!rd) { /* eof - all done */
+ status = 0;
+ break;
+ }
+ if (rd < 0) {
+ bb_perror_msg(bb_msg_read_error);
+ break;
+ }
+ /* dst_fd == -1 is a fake, else... */
+ if (dst_fd >= 0) {
+ ssize_t wr = full_write(dst_fd, buffer, rd);
+ if (wr < rd) {
+ bb_perror_msg(bb_msg_write_error);
+ break;
+ }
+ }
+ total += rd;
+ if (status < 0) { /* if we aren't copying till EOF... */
+ size -= rd;
+ if (!size) {
+ /* 'size' bytes copied - all done */
+ status = 0;
+ break;
+ }
+ }
+ }
+ out:
+ RELEASE_CONFIG_BUFFER(buffer);
+ return status ? -1 : total;
+}
+
+
+#if 0
+void complain_copyfd_and_die(off_t sz)
+{
+ if (sz != -1)
+ bb_error_msg_and_die("short read");
+ /* if sz == -1, bb_copyfd_XX already complained */
+ exit(xfunc_error_retval);
+}
+#endif
+
+off_t bb_copyfd_size(int fd1, int fd2, off_t size)
+{
+ if (size) {
+ return bb_full_fd_action(fd1, fd2, size);
+ }
+ return 0;
+}
+
+void bb_copyfd_exact_size(int fd1, int fd2, off_t size)
+{
+ off_t sz = bb_copyfd_size(fd1, fd2, size);
+ if (sz == size)
+ return;
+ if (sz != -1)
+ bb_error_msg_and_die("short read");
+ /* if sz == -1, bb_copyfd_XX already complained */
+ exit(xfunc_error_retval);
+}
+
+off_t bb_copyfd_eof(int fd1, int fd2)
+{
+ return bb_full_fd_action(fd1, fd2, 0);
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/correct_password.c b/i/pc104/initrd/conf/busybox/libbb/correct_password.c
new file mode 100644
index 0000000..c515b26
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/correct_password.c
@@ -0,0 +1,69 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Copyright 1989 - 1991, Julianne Frances Haugh <jockgrrl@austin.rr.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Julianne F. Haugh nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "libbb.h"
+
+/* Ask the user for a password.
+ Return 1 if the user gives the correct password for entry PW,
+ 0 if not. Return 1 without asking for a password if run by UID 0
+ or if PW has an empty password. */
+
+int correct_password(const struct passwd *pw)
+{
+ char *unencrypted, *encrypted;
+ const char *correct;
+#if ENABLE_FEATURE_SHADOWPASSWDS
+ /* Using _r function to avoid pulling in static buffers */
+ struct spwd spw;
+ struct spwd *result;
+ char buffer[256];
+#endif
+
+ correct = pw->pw_passwd;
+#if ENABLE_FEATURE_SHADOWPASSWDS
+ if (LONE_CHAR(pw->pw_passwd, 'x') || LONE_CHAR(pw->pw_passwd, '*')) {
+ if (getspnam_r(pw->pw_name, &spw, buffer, sizeof(buffer), &result))
+ bb_error_msg("no valid shadow password, checking ordinary one");
+ else
+ correct = spw.sp_pwdp;
+ }
+#endif
+
+ if (!correct || correct[0] == '\0')
+ return 1;
+
+ unencrypted = bb_askpass(0, "Password: ");
+ if (!unencrypted) {
+ return 0;
+ }
+ encrypted = crypt(unencrypted, correct);
+ memset(unencrypted, 0, strlen(unencrypted));
+ return strcmp(encrypted, correct) == 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/crc32.c b/i/pc104/initrd/conf/busybox/libbb/crc32.c
new file mode 100644
index 0000000..1e4a57e
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/crc32.c
@@ -0,0 +1,39 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * CRC32 table fill function
+ * Copyright (C) 2006 by Rob Sullivan <cogito.ergo.cogito@gmail.com>
+ * (I can't really claim much credit however, as the algorithm is
+ * very well-known)
+ *
+ * The following function creates a CRC32 table depending on whether
+ * a big-endian (0x04c11db7) or little-endian (0xedb88320) CRC32 is
+ * required. Admittedly, there are other CRC32 polynomials floating
+ * around, but Busybox doesn't use them.
+ *
+ * endian = 1: big-endian
+ * endian = 0: little-endian
+ */
+
+#include "libbb.h"
+
+uint32_t *crc32_filltable(int endian)
+{
+
+ uint32_t *crc_table = xmalloc(256 * sizeof(uint32_t));
+ uint32_t polynomial = endian ? 0x04c11db7 : 0xedb88320;
+ uint32_t c;
+ int i, j;
+
+ for (i = 0; i < 256; i++) {
+ c = endian ? (i << 24) : i;
+ for (j = 8; j; j--) {
+ if (endian)
+ c = (c&0x80000000) ? ((c << 1) ^ polynomial) : (c << 1);
+ else
+ c = (c&1) ? ((c >> 1) ^ polynomial) : (c >> 1);
+ }
+ *crc_table++ = c;
+ }
+
+ return crc_table - 256;
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/create_icmp6_socket.c b/i/pc104/initrd/conf/busybox/libbb/create_icmp6_socket.c
new file mode 100644
index 0000000..b90f3e9
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/create_icmp6_socket.c
@@ -0,0 +1,37 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * create raw socket for icmp (IPv6 version) protocol test permission
+ * and drop root privileges if running setuid
+ *
+ */
+
+#include <sys/types.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include "libbb.h"
+
+#ifdef CONFIG_FEATURE_IPV6
+int create_icmp6_socket(void)
+{
+ struct protoent *proto;
+ int sock;
+
+ proto = getprotobyname("ipv6-icmp");
+ /* if getprotobyname failed, just silently force
+ * proto->p_proto to have the correct value for "ipv6-icmp" */
+ sock = socket(AF_INET6, SOCK_RAW,
+ (proto ? proto->p_proto : IPPROTO_ICMPV6));
+ if (sock < 0) {
+ if (errno == EPERM)
+ bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
+ bb_perror_msg_and_die(bb_msg_can_not_create_raw_socket);
+ }
+
+ /* drop root privs if running setuid */
+ xsetuid(getuid());
+
+ return sock;
+}
+#endif
diff --git a/i/pc104/initrd/conf/busybox/libbb/create_icmp_socket.c b/i/pc104/initrd/conf/busybox/libbb/create_icmp_socket.c
new file mode 100644
index 0000000..dbd4769
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/create_icmp_socket.c
@@ -0,0 +1,35 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * create raw socket for icmp protocol test permission
+ * and drop root privileges if running setuid
+ *
+ */
+
+#include <sys/types.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include "libbb.h"
+
+int create_icmp_socket(void)
+{
+ struct protoent *proto;
+ int sock;
+
+ proto = getprotobyname("icmp");
+ /* if getprotobyname failed, just silently force
+ * proto->p_proto to have the correct value for "icmp" */
+ sock = socket(AF_INET, SOCK_RAW,
+ (proto ? proto->p_proto : 1)); /* 1 == ICMP */
+ if (sock < 0) {
+ if (errno == EPERM)
+ bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
+ bb_perror_msg_and_die(bb_msg_can_not_create_raw_socket);
+ }
+
+ /* drop root privs if running setuid */
+ xsetuid(getuid());
+
+ return sock;
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/default_error_retval.c b/i/pc104/initrd/conf/busybox/libbb/default_error_retval.c
new file mode 100644
index 0000000..f4e46a4
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/default_error_retval.c
@@ -0,0 +1,19 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/* Seems silly to copyright a global variable. ;-) Oh well.
+ *
+ * At least one applet (cmp) returns a value different from the typical
+ * EXIT_FAILURE values (1) when an error occurs. So, make it configurable
+ * by the applet. I suppose we could use a wrapper function to set it, but
+ * that too seems silly.
+ */
+
+#include <stdlib.h>
+#include "libbb.h"
+
+int xfunc_error_retval = EXIT_FAILURE;
diff --git a/i/pc104/initrd/conf/busybox/libbb/device_open.c b/i/pc104/initrd/conf/busybox/libbb/device_open.c
new file mode 100644
index 0000000..2b35ad8
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/device_open.c
@@ -0,0 +1,30 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "libbb.h"
+
+/* try to open up the specified device */
+int device_open(const char *device, int mode)
+{
+ int m, f, fd = -1;
+
+ m = mode | O_NONBLOCK;
+
+ /* Retry up to 5 times */
+ /* TODO: explain why it can't be considered insane */
+ for (f = 0; f < 5; f++)
+ if ((fd = open(device, m, 0600)) >= 0)
+ break;
+ if (fd < 0)
+ return fd;
+ /* Reset original flags. */
+ if (m != mode)
+ fcntl(fd, F_SETFL, mode);
+ return fd;
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/dump.c b/i/pc104/initrd/conf/busybox/libbb/dump.c
new file mode 100644
index 0000000..f078afc
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/dump.c
@@ -0,0 +1,805 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Support code for the hexdump and od applets,
+ * based on code from util-linux v 2.11l
+ *
+ * Copyright (c) 1989
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ *
+ * Original copyright notice is retained at the end of this file.
+ */
+
+#include "libbb.h"
+#include "dump.h"
+
+enum _vflag bb_dump_vflag = FIRST;
+FS *bb_dump_fshead; /* head of format strings */
+static FU *endfu;
+static char **_argv;
+static off_t savaddress; /* saved address/offset in stream */
+static off_t eaddress; /* end address */
+static off_t address; /* address/offset in stream */
+off_t bb_dump_skip; /* bytes to skip */
+static int exitval; /* final exit value */
+int bb_dump_blocksize; /* data block size */
+int bb_dump_length = -1; /* max bytes to read */
+
+static const char index_str[] = ".#-+ 0123456789";
+
+static const char size_conv_str[] =
+"\x1\x4\x4\x4\x4\x4\x4\x8\x8\x8\x8\010cdiouxXeEfgG";
+
+static const char lcc[] = "diouxX";
+
+int bb_dump_size(FS * fs)
+{
+ FU *fu;
+ int bcnt, cur_size;
+ char *fmt;
+ const char *p;
+ int prec;
+
+ /* figure out the data block bb_dump_size needed for each format unit */
+ for (cur_size = 0, fu = fs->nextfu; fu; fu = fu->nextfu) {
+ if (fu->bcnt) {
+ cur_size += fu->bcnt * fu->reps;
+ continue;
+ }
+ for (bcnt = prec = 0, fmt = fu->fmt; *fmt; ++fmt) {
+ if (*fmt != '%')
+ continue;
+ /*
+ * bb_dump_skip any special chars -- save precision in
+ * case it's a %s format.
+ */
+ while (strchr(index_str + 1, *++fmt));
+ if (*fmt == '.' && isdigit(*++fmt)) {
+ prec = atoi(fmt);
+ while (isdigit(*++fmt));
+ }
+ if (!(p = strchr(size_conv_str + 12, *fmt))) {
+ if (*fmt == 's') {
+ bcnt += prec;
+ } else if (*fmt == '_') {
+ ++fmt;
+ if ((*fmt == 'c') || (*fmt == 'p') || (*fmt == 'u')) {
+ bcnt += 1;
+ }
+ }
+ } else {
+ bcnt += size_conv_str[p - (size_conv_str + 12)];
+ }
+ }
+ cur_size += bcnt * fu->reps;
+ }
+ return cur_size;
+}
+
+static void rewrite(FS * fs)
+{
+ enum { NOTOKAY, USEBCNT, USEPREC } sokay;
+ PR *pr, **nextpr = NULL;
+ FU *fu;
+ char *p1, *p2, *p3;
+ char savech, *fmtp;
+ const char *byte_count_str;
+ int nconv, prec = 0;
+
+ for (fu = fs->nextfu; fu; fu = fu->nextfu) {
+ /*
+ * break each format unit into print units; each
+ * conversion character gets its own.
+ */
+ for (nconv = 0, fmtp = fu->fmt; *fmtp; nextpr = &pr->nextpr) {
+ /* NOSTRICT */
+ /* DBU:[dvae@cray.com] calloc so that forward ptrs start out NULL*/
+ pr = xzalloc(sizeof(PR));
+ if (!fu->nextpr)
+ fu->nextpr = pr;
+ /* ignore nextpr -- its unused inside the loop and is
+ * uninitialized 1st time thru.
+ */
+
+ /* bb_dump_skip preceding text and up to the next % sign */
+ for (p1 = fmtp; *p1 && *p1 != '%'; ++p1);
+
+ /* only text in the string */
+ if (!*p1) {
+ pr->fmt = fmtp;
+ pr->flags = F_TEXT;
+ break;
+ }
+
+ /*
+ * get precision for %s -- if have a byte count, don't
+ * need it.
+ */
+ if (fu->bcnt) {
+ sokay = USEBCNT;
+ /* bb_dump_skip to conversion character */
+ for (++p1; strchr(index_str, *p1); ++p1);
+ } else {
+ /* bb_dump_skip any special chars, field width */
+ while (strchr(index_str + 1, *++p1));
+ if (*p1 == '.' && isdigit(*++p1)) {
+ sokay = USEPREC;
+ prec = atoi(p1);
+ while (isdigit(*++p1));
+ } else
+ sokay = NOTOKAY;
+ }
+
+ p2 = p1 + 1; /* set end pointer */
+
+ /*
+ * figure out the byte count for each conversion;
+ * rewrite the format as necessary, set up blank-
+ * pbb_dump_adding for end of data.
+ */
+
+ if (*p1 == 'c') {
+ pr->flags = F_CHAR;
+ DO_BYTE_COUNT_1:
+ byte_count_str = "\001";
+ DO_BYTE_COUNT:
+ if (fu->bcnt) {
+ do {
+ if (fu->bcnt == *byte_count_str) {
+ break;
+ }
+ } while (*++byte_count_str);
+ }
+ /* Unlike the original, output the remainder of the format string. */
+ if (!*byte_count_str) {
+ bb_error_msg_and_die("bad byte count for conversion character %s", p1);
+ }
+ pr->bcnt = *byte_count_str;
+ } else if (*p1 == 'l') {
+ ++p2;
+ ++p1;
+ DO_INT_CONV:
+ {
+ const char *e;
+ if (!(e = strchr(lcc, *p1))) {
+ goto DO_BAD_CONV_CHAR;
+ }
+ pr->flags = F_INT;
+ if (e > lcc + 1) {
+ pr->flags = F_UINT;
+ }
+ byte_count_str = "\004\002\001";
+ goto DO_BYTE_COUNT;
+ }
+ /* NOTREACHED */
+ } else if (strchr(lcc, *p1)) {
+ goto DO_INT_CONV;
+ } else if (strchr("eEfgG", *p1)) {
+ pr->flags = F_DBL;
+ byte_count_str = "\010\004";
+ goto DO_BYTE_COUNT;
+ } else if (*p1 == 's') {
+ pr->flags = F_STR;
+ if (sokay == USEBCNT) {
+ pr->bcnt = fu->bcnt;
+ } else if (sokay == USEPREC) {
+ pr->bcnt = prec;
+ } else { /* NOTOKAY */
+ bb_error_msg_and_die("%%s requires a precision or a byte count");
+ }
+ } else if (*p1 == '_') {
+ ++p2;
+ switch (p1[1]) {
+ case 'A':
+ endfu = fu;
+ fu->flags |= F_IGNORE;
+ /* FALLTHROUGH */
+ case 'a':
+ pr->flags = F_ADDRESS;
+ ++p2;
+ if ((p1[2] != 'd') && (p1[2] != 'o') && (p1[2] != 'x')) {
+ goto DO_BAD_CONV_CHAR;
+ }
+ *p1 = p1[2];
+ break;
+ case 'c':
+ pr->flags = F_C;
+ /* *p1 = 'c'; set in conv_c */
+ goto DO_BYTE_COUNT_1;
+ case 'p':
+ pr->flags = F_P;
+ *p1 = 'c';
+ goto DO_BYTE_COUNT_1;
+ case 'u':
+ pr->flags = F_U;
+ /* *p1 = 'c'; set in conv_u */
+ goto DO_BYTE_COUNT_1;
+ default:
+ goto DO_BAD_CONV_CHAR;
+ }
+ } else {
+ DO_BAD_CONV_CHAR:
+ bb_error_msg_and_die("bad conversion character %%%s", p1);
+ }
+
+ /*
+ * copy to PR format string, set conversion character
+ * pointer, update original.
+ */
+ savech = *p2;
+ p1[1] = '\0';
+ pr->fmt = xstrdup(fmtp);
+ *p2 = savech;
+ pr->cchar = pr->fmt + (p1 - fmtp);
+
+ /* DBU:[dave@cray.com] w/o this, trailing fmt text, space is lost.
+ * Skip subsequent text and up to the next % sign and tack the
+ * additional text onto fmt: eg. if fmt is "%x is a HEX number",
+ * we lose the " is a HEX number" part of fmt.
+ */
+ for (p3 = p2; *p3 && *p3 != '%'; p3++);
+ if (p3 > p2)
+ {
+ savech = *p3;
+ *p3 = '\0';
+ pr->fmt = xrealloc(pr->fmt, strlen(pr->fmt)+(p3-p2)+1);
+ strcat(pr->fmt, p2);
+ *p3 = savech;
+ p2 = p3;
+ }
+
+ fmtp = p2;
+
+ /* only one conversion character if byte count */
+ if (!(pr->flags & F_ADDRESS) && fu->bcnt && nconv++) {
+ bb_error_msg_and_die("byte count with multiple conversion characters");
+ }
+ }
+ /*
+ * if format unit byte count not specified, figure it out
+ * so can adjust rep count later.
+ */
+ if (!fu->bcnt)
+ for (pr = fu->nextpr; pr; pr = pr->nextpr)
+ fu->bcnt += pr->bcnt;
+ }
+ /*
+ * if the format string interprets any data at all, and it's
+ * not the same as the bb_dump_blocksize, and its last format unit
+ * interprets any data at all, and has no iteration count,
+ * repeat it as necessary.
+ *
+ * if, rep count is greater than 1, no trailing whitespace
+ * gets output from the last iteration of the format unit.
+ */
+ for (fu = fs->nextfu;; fu = fu->nextfu) {
+ if (!fu->nextfu && fs->bcnt < bb_dump_blocksize &&
+ !(fu->flags & F_SETREP) && fu->bcnt)
+ fu->reps += (bb_dump_blocksize - fs->bcnt) / fu->bcnt;
+ if (fu->reps > 1) {
+ for (pr = fu->nextpr;; pr = pr->nextpr)
+ if (!pr->nextpr)
+ break;
+ for (p1 = pr->fmt, p2 = NULL; *p1; ++p1)
+ p2 = isspace(*p1) ? p1 : NULL;
+ if (p2)
+ pr->nospace = p2;
+ }
+ if (!fu->nextfu)
+ break;
+ }
+}
+
+static void do_skip(const char *fname, int statok)
+{
+ struct stat sbuf;
+
+ if (statok) {
+ if (fstat(STDIN_FILENO, &sbuf)) {
+ bb_perror_msg_and_die("%s", fname);
+ }
+ if ((!(S_ISCHR(sbuf.st_mode) ||
+ S_ISBLK(sbuf.st_mode) ||
+ S_ISFIFO(sbuf.st_mode))) && bb_dump_skip >= sbuf.st_size) {
+ /* If bb_dump_size valid and bb_dump_skip >= size */
+ bb_dump_skip -= sbuf.st_size;
+ address += sbuf.st_size;
+ return;
+ }
+ }
+ if (fseek(stdin, bb_dump_skip, SEEK_SET)) {
+ bb_perror_msg_and_die("%s", fname);
+ }
+ savaddress = address += bb_dump_skip;
+ bb_dump_skip = 0;
+}
+
+static int next(char **argv)
+{
+ static int done;
+ int statok;
+
+ if (argv) {
+ _argv = argv;
+ return 1;
+ }
+ for (;;) {
+ if (*_argv) {
+ if (!(freopen(*_argv, "r", stdin))) {
+ bb_perror_msg("%s", *_argv);
+ exitval = 1;
+ ++_argv;
+ continue;
+ }
+ statok = done = 1;
+ } else {
+ if (done++)
+ return 0;
+ statok = 0;
+ }
+ if (bb_dump_skip)
+ do_skip(statok ? *_argv : "stdin", statok);
+ if (*_argv)
+ ++_argv;
+ if (!bb_dump_skip)
+ return 1;
+ }
+ /* NOTREACHED */
+}
+
+static unsigned char *get(void)
+{
+ static int ateof = 1;
+ static unsigned char *curp=NULL, *savp; /*DBU:[dave@cray.com]initialize curp */
+ int n;
+ int need, nread;
+ unsigned char *tmpp;
+
+ if (!curp) {
+ address = (off_t)0; /*DBU:[dave@cray.com] initialize,initialize..*/
+ curp = xmalloc(bb_dump_blocksize);
+ savp = xmalloc(bb_dump_blocksize);
+ } else {
+ tmpp = curp;
+ curp = savp;
+ savp = tmpp;
+ address = savaddress += bb_dump_blocksize;
+ }
+ for (need = bb_dump_blocksize, nread = 0;;) {
+ /*
+ * if read the right number of bytes, or at EOF for one file,
+ * and no other files are available, zero-pad the rest of the
+ * block and set the end flag.
+ */
+ if (!bb_dump_length || (ateof && !next((char **) NULL))) {
+ if (need == bb_dump_blocksize) {
+ return NULL;
+ }
+ if (bb_dump_vflag != ALL && !memcmp(curp, savp, nread)) {
+ if (bb_dump_vflag != DUP) {
+ puts("*");
+ }
+ return NULL;
+ }
+ memset((char *) curp + nread, 0, need);
+ eaddress = address + nread;
+ return curp;
+ }
+ n = fread((char *) curp + nread, sizeof(unsigned char),
+ bb_dump_length == -1 ? need : MIN(bb_dump_length, need), stdin);
+ if (!n) {
+ if (ferror(stdin)) {
+ bb_perror_msg("%s", _argv[-1]);
+ }
+ ateof = 1;
+ continue;
+ }
+ ateof = 0;
+ if (bb_dump_length != -1) {
+ bb_dump_length -= n;
+ }
+ if (!(need -= n)) {
+ if (bb_dump_vflag == ALL || bb_dump_vflag == FIRST
+ || memcmp(curp, savp, bb_dump_blocksize)) {
+ if (bb_dump_vflag == DUP || bb_dump_vflag == FIRST) {
+ bb_dump_vflag = WAIT;
+ }
+ return curp;
+ }
+ if (bb_dump_vflag == WAIT) {
+ puts("*");
+ }
+ bb_dump_vflag = DUP;
+ address = savaddress += bb_dump_blocksize;
+ need = bb_dump_blocksize;
+ nread = 0;
+ } else {
+ nread += n;
+ }
+ }
+}
+
+static void bpad(PR * pr)
+{
+ char *p1, *p2;
+
+ /*
+ * remove all conversion flags; '-' is the only one valid
+ * with %s, and it's not useful here.
+ */
+ pr->flags = F_BPAD;
+ *pr->cchar = 's';
+ for (p1 = pr->fmt; *p1 != '%'; ++p1);
+ for (p2 = ++p1; *p1 && strchr(" -0+#", *p1); ++p1)
+ if (pr->nospace) pr->nospace--;
+ while ((*p2++ = *p1++) != 0);
+}
+
+static const char conv_str[] =
+ "\0\\0\0"
+ "\007\\a\0" /* \a */
+ "\b\\b\0"
+ "\f\\b\0"
+ "\n\\n\0"
+ "\r\\r\0"
+ "\t\\t\0"
+ "\v\\v\0"
+ "\0";
+
+
+static void conv_c(PR * pr, unsigned char * p)
+{
+ const char *str = conv_str;
+ char buf[10];
+
+ do {
+ if (*p == *str) {
+ ++str;
+ goto strpr;
+ }
+ str += 4;
+ } while (*str);
+
+ if (isprint(*p)) {
+ *pr->cchar = 'c';
+ (void) printf(pr->fmt, *p);
+ } else {
+ sprintf(buf, "%03o", (int) *p);
+ str = buf;
+ strpr:
+ *pr->cchar = 's';
+ printf(pr->fmt, str);
+ }
+}
+
+static void conv_u(PR * pr, unsigned char * p)
+{
+ static const char list[] =
+ "nul\0soh\0stx\0etx\0eot\0enq\0ack\0bel\0"
+ "bs\0_ht\0_lf\0_vt\0_ff\0_cr\0_so\0_si\0_"
+ "dle\0dcl\0dc2\0dc3\0dc4\0nak\0syn\0etb\0"
+ "can\0em\0_sub\0esc\0fs\0_gs\0_rs\0_us";
+
+ /* od used nl, not lf */
+ if (*p <= 0x1f) {
+ *pr->cchar = 's';
+ printf(pr->fmt, list + (4 * (int)*p));
+ } else if (*p == 0x7f) {
+ *pr->cchar = 's';
+ printf(pr->fmt, "del");
+ } else if (isprint(*p)) {
+ *pr->cchar = 'c';
+ printf(pr->fmt, *p);
+ } else {
+ *pr->cchar = 'x';
+ printf(pr->fmt, (int) *p);
+ }
+}
+
+static void display(void)
+{
+/* extern FU *endfu; */
+ FS *fs;
+ FU *fu;
+ PR *pr;
+ int cnt;
+ unsigned char *bp;
+
+ off_t saveaddress;
+ unsigned char savech = 0, *savebp;
+
+ while ((bp = get()) != NULL) {
+ for (fs = bb_dump_fshead, savebp = bp, saveaddress = address; fs;
+ fs = fs->nextfs, bp = savebp, address = saveaddress) {
+ for (fu = fs->nextfu; fu; fu = fu->nextfu) {
+ if (fu->flags & F_IGNORE) {
+ break;
+ }
+ for (cnt = fu->reps; cnt; --cnt) {
+ for (pr = fu->nextpr; pr; address += pr->bcnt,
+ bp += pr->bcnt, pr = pr->nextpr) {
+ if (eaddress && address >= eaddress &&
+ !(pr->flags & (F_TEXT | F_BPAD))) {
+ bpad(pr);
+ }
+ if (cnt == 1 && pr->nospace) {
+ savech = *pr->nospace;
+ *pr->nospace = '\0';
+ }
+/* PRINT; */
+ switch (pr->flags) {
+ case F_ADDRESS:
+ printf(pr->fmt, (unsigned int) address);
+ break;
+ case F_BPAD:
+ printf(pr->fmt, "");
+ break;
+ case F_C:
+ conv_c(pr, bp);
+ break;
+ case F_CHAR:
+ printf(pr->fmt, *bp);
+ break;
+ case F_DBL:{
+ double dval;
+ float fval;
+
+ switch (pr->bcnt) {
+ case 4:
+ memmove((char *) &fval, (char *) bp,
+ sizeof(fval));
+ printf(pr->fmt, fval);
+ break;
+ case 8:
+ memmove((char *) &dval, (char *) bp,
+ sizeof(dval));
+ printf(pr->fmt, dval);
+ break;
+ }
+ break;
+ }
+ case F_INT:{
+ int ival;
+ short sval;
+
+ switch (pr->bcnt) {
+ case 1:
+ printf(pr->fmt, (int) *bp);
+ break;
+ case 2:
+ memmove((char *) &sval, (char *) bp,
+ sizeof(sval));
+ printf(pr->fmt, (int) sval);
+ break;
+ case 4:
+ memmove((char *) &ival, (char *) bp,
+ sizeof(ival));
+ printf(pr->fmt, ival);
+ break;
+ }
+ break;
+ }
+ case F_P:
+ printf(pr->fmt, isprint(*bp) ? *bp : '.');
+ break;
+ case F_STR:
+ printf(pr->fmt, (char *) bp);
+ break;
+ case F_TEXT:
+ printf(pr->fmt);
+ break;
+ case F_U:
+ conv_u(pr, bp);
+ break;
+ case F_UINT:{
+ unsigned int ival;
+ unsigned short sval;
+
+ switch (pr->bcnt) {
+ case 1:
+ printf(pr->fmt, (unsigned int) * bp);
+ break;
+ case 2:
+ memmove((char *) &sval, (char *) bp,
+ sizeof(sval));
+ printf(pr->fmt, (unsigned int) sval);
+ break;
+ case 4:
+ memmove((char *) &ival, (char *) bp,
+ sizeof(ival));
+ printf(pr->fmt, ival);
+ break;
+ }
+ break;
+ }
+ }
+ if (cnt == 1 && pr->nospace) {
+ *pr->nospace = savech;
+ }
+ }
+ }
+ }
+ }
+ }
+ if (endfu) {
+ /*
+ * if eaddress not set, error or file bb_dump_size was multiple of
+ * bb_dump_blocksize, and no partial block ever found.
+ */
+ if (!eaddress) {
+ if (!address) {
+ return;
+ }
+ eaddress = address;
+ }
+ for (pr = endfu->nextpr; pr; pr = pr->nextpr) {
+ switch (pr->flags) {
+ case F_ADDRESS:
+ (void) printf(pr->fmt, (unsigned int) eaddress);
+ break;
+ case F_TEXT:
+ (void) printf(pr->fmt);
+ break;
+ }
+ }
+ }
+}
+
+int bb_dump_dump(char **argv)
+{
+ FS *tfs;
+
+ /* figure out the data block bb_dump_size */
+ for (bb_dump_blocksize = 0, tfs = bb_dump_fshead; tfs; tfs = tfs->nextfs) {
+ tfs->bcnt = bb_dump_size(tfs);
+ if (bb_dump_blocksize < tfs->bcnt) {
+ bb_dump_blocksize = tfs->bcnt;
+ }
+ }
+ /* rewrite the rules, do syntax checking */
+ for (tfs = bb_dump_fshead; tfs; tfs = tfs->nextfs) {
+ rewrite(tfs);
+ }
+
+ next(argv);
+ display();
+
+ return exitval;
+}
+
+void bb_dump_add(const char *fmt)
+{
+ const char *p;
+ char *p1;
+ char *p2;
+ static FS **nextfs;
+ FS *tfs;
+ FU *tfu, **nextfu;
+ const char *savep;
+
+ /* start new linked list of format units */
+ tfs = xzalloc(sizeof(FS)); /*DBU:[dave@cray.com] start out NULL */
+ if (!bb_dump_fshead) {
+ bb_dump_fshead = tfs;
+ } else {
+ *nextfs = tfs;
+ }
+ nextfs = &tfs->nextfs;
+ nextfu = &tfs->nextfu;
+
+ /* take the format string and break it up into format units */
+ for (p = fmt;;) {
+ /* bb_dump_skip leading white space */
+ p = skip_whitespace(p);
+ if (!*p) {
+ break;
+ }
+
+ /* allocate a new format unit and link it in */
+ /* NOSTRICT */
+ /* DBU:[dave@cray.com] calloc so that forward pointers start out NULL */
+ tfu = xzalloc(sizeof(FU));
+ *nextfu = tfu;
+ nextfu = &tfu->nextfu;
+ tfu->reps = 1;
+
+ /* if leading digit, repetition count */
+ if (isdigit(*p)) {
+ for (savep = p; isdigit(*p); ++p);
+ if (!isspace(*p) && *p != '/') {
+ bb_error_msg_and_die("bad format {%s}", fmt);
+ }
+ /* may overwrite either white space or slash */
+ tfu->reps = atoi(savep);
+ tfu->flags = F_SETREP;
+ /* bb_dump_skip trailing white space */
+ p = skip_whitespace(++p);
+ }
+
+ /* bb_dump_skip slash and trailing white space */
+ if (*p == '/') {
+ p = skip_whitespace(++p);
+ }
+
+ /* byte count */
+ if (isdigit(*p)) {
+// TODO: use bb_strtou
+ savep = p;
+ do p++; while (isdigit(*p));
+ if (!isspace(*p)) {
+ bb_error_msg_and_die("bad format {%s}", fmt);
+ }
+ tfu->bcnt = atoi(savep);
+ /* bb_dump_skip trailing white space */
+ p = skip_whitespace(++p);
+ }
+
+ /* format */
+ if (*p != '"') {
+ bb_error_msg_and_die("bad format {%s}", fmt);
+ }
+ for (savep = ++p; *p != '"';) {
+ if (*p++ == 0) {
+ bb_error_msg_and_die("bad format {%s}", fmt);
+ }
+ }
+ tfu->fmt = xmalloc(p - savep + 1);
+ strncpy(tfu->fmt, savep, p - savep);
+ tfu->fmt[p - savep] = '\0';
+/* escape(tfu->fmt); */
+
+ p1 = tfu->fmt;
+
+ /* alphabetic escape sequences have to be done in place */
+ for (p2 = p1;; ++p1, ++p2) {
+ if (!*p1) {
+ *p2 = *p1;
+ break;
+ }
+ if (*p1 == '\\') {
+ const char *cs = conv_str + 4;
+ ++p1;
+ *p2 = *p1;
+ do {
+ if (*p1 == cs[2]) {
+ *p2 = cs[0];
+ break;
+ }
+ cs += 4;
+ } while (*cs);
+ }
+ }
+
+ p++;
+ }
+}
+
+/*
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
diff --git a/i/pc104/initrd/conf/busybox/libbb/error_msg.c b/i/pc104/initrd/conf/busybox/libbb/error_msg.c
new file mode 100644
index 0000000..b2141f3
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/error_msg.c
@@ -0,0 +1,23 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include "libbb.h"
+
+void bb_error_msg(const char *s, ...)
+{
+ va_list p;
+
+ va_start(p, s);
+ bb_verror_msg(s, p, NULL);
+ va_end(p);
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/error_msg_and_die.c b/i/pc104/initrd/conf/busybox/libbb/error_msg_and_die.c
new file mode 100644
index 0000000..7c5a4eb
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/error_msg_and_die.c
@@ -0,0 +1,33 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include "libbb.h"
+
+int die_sleep;
+
+void sleep_and_die(void)
+{
+ if (die_sleep)
+ sleep(die_sleep);
+ exit(xfunc_error_retval);
+}
+
+void bb_error_msg_and_die(const char *s, ...)
+{
+ va_list p;
+
+ va_start(p, s);
+ bb_verror_msg(s, p, NULL);
+ va_end(p);
+ sleep_and_die();
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/execable.c b/i/pc104/initrd/conf/busybox/libbb/execable.c
new file mode 100644
index 0000000..ee10c61
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/execable.c
@@ -0,0 +1,71 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 2006 Gabriel Somlo <somlo at cmu.edu>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "libbb.h"
+
+/* check if path points to an executable file;
+ * return 1 if found;
+ * return 0 otherwise;
+ */
+int execable_file(const char *name)
+{
+ struct stat s;
+ return (!access(name, X_OK) && !stat(name, &s) && S_ISREG(s.st_mode));
+}
+
+/* search $PATH for an executable file;
+ * return allocated string containing full path if found;
+ * return NULL otherwise;
+ */
+char *find_execable(const char *filename)
+{
+ char *path, *p, *n;
+
+ p = path = xstrdup(getenv("PATH"));
+ while (p) {
+ n = strchr(p, ':');
+ if (n)
+ *n++ = '\0';
+ if (*p != '\0') { /* it's not a PATH="foo::bar" situation */
+ p = concat_path_file(p, filename);
+ if (execable_file(p)) {
+ free(path);
+ return p;
+ }
+ free(p);
+ }
+ p = n;
+ }
+ free(path);
+ return NULL;
+}
+
+/* search $PATH for an executable file;
+ * return 1 if found;
+ * return 0 otherwise;
+ */
+int exists_execable(const char *filename)
+{
+ char *ret = find_execable(filename);
+ if (ret) {
+ free(ret);
+ return 1;
+ }
+ return 0;
+}
+
+#if ENABLE_FEATURE_EXEC_PREFER_APPLETS
+/* just like the real execvp, but try to launch an applet named 'file' first
+ */
+int bb_execvp(const char *file, char *const argv[])
+{
+ return execvp(find_applet_by_name(file) ? CONFIG_BUSYBOX_EXEC_PATH : file,
+ argv);
+}
+#endif
diff --git a/i/pc104/initrd/conf/busybox/libbb/fclose_nonstdin.c b/i/pc104/initrd/conf/busybox/libbb/fclose_nonstdin.c
new file mode 100644
index 0000000..88e8474
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/fclose_nonstdin.c
@@ -0,0 +1,23 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * fclose_nonstdin implementation for busybox
+ *
+ * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/* A number of standard utilities can accept multiple command line args
+ * of '-' for stdin, according to SUSv3. So we encapsulate the check
+ * here to save a little space.
+ */
+
+#include "libbb.h"
+
+int fclose_if_not_stdin(FILE *f)
+{
+ if (f != stdin) {
+ return fclose(f);
+ }
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/fflush_stdout_and_exit.c b/i/pc104/initrd/conf/busybox/libbb/fflush_stdout_and_exit.c
new file mode 100644
index 0000000..6f44770
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/fflush_stdout_and_exit.c
@@ -0,0 +1,21 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * fflush_stdout_and_exit implementation for busybox
+ *
+ * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/* Attempt to fflush(stdout), and exit with an error code if stdout is
+ * in an error state.
+ */
+
+#include "libbb.h"
+
+void fflush_stdout_and_exit(int retval)
+{
+ if (fflush(stdout))
+ sleep_and_die();
+ exit(retval);
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/fgets_str.c b/i/pc104/initrd/conf/busybox/libbb/fgets_str.c
new file mode 100644
index 0000000..1bc6c3b
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/fgets_str.c
@@ -0,0 +1,53 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) many different people.
+ * If you wrote this, please acknowledge your work.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "libbb.h"
+
+/* Read up to (and including) TERMINATING_STRING from FILE and return it.
+ * Return NULL on EOF. */
+
+char *xmalloc_fgets_str(FILE *file, const char *terminating_string)
+{
+ char *linebuf = NULL;
+ const int term_length = strlen(terminating_string);
+ int end_string_offset;
+ int linebufsz = 0;
+ int idx = 0;
+ int ch;
+
+ while (1) {
+ ch = fgetc(file);
+ if (ch == EOF) {
+ free(linebuf);
+ return NULL;
+ }
+
+ /* grow the line buffer as necessary */
+ while (idx > linebufsz - 2) {
+ linebufsz += 200;
+ linebuf = xrealloc(linebuf, linebufsz);
+ }
+
+ linebuf[idx] = ch;
+ idx++;
+
+ /* Check for terminating string */
+ end_string_offset = idx - term_length;
+ if (end_string_offset > 0
+ && memcmp(&linebuf[end_string_offset], terminating_string, term_length) == 0
+ ) {
+ idx -= term_length;
+ break;
+ }
+ }
+ linebuf = xrealloc(linebuf, idx + 1);
+ linebuf[idx] = '\0';
+ return linebuf;
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/find_mount_point.c b/i/pc104/initrd/conf/busybox/libbb/find_mount_point.c
new file mode 100644
index 0000000..cb00b98
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/find_mount_point.c
@@ -0,0 +1,53 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "libbb.h"
+#include <mntent.h>
+
+/*
+ * Given a block device, find the mount table entry if that block device
+ * is mounted.
+ *
+ * Given any other file (or directory), find the mount table entry for its
+ * filesystem.
+ */
+struct mntent *find_mount_point(const char *name, const char *table)
+{
+ struct stat s;
+ dev_t mountDevice;
+ FILE *mountTable;
+ struct mntent *mountEntry;
+
+ if (stat(name, &s) != 0)
+ return 0;
+
+ if ((s.st_mode & S_IFMT) == S_IFBLK)
+ mountDevice = s.st_rdev;
+ else
+ mountDevice = s.st_dev;
+
+
+ mountTable = setmntent(table ? table : bb_path_mtab_file, "r");
+ if (!mountTable)
+ return 0;
+
+ while ((mountEntry = getmntent(mountTable)) != 0) {
+ if (strcmp(name, mountEntry->mnt_dir) == 0
+ || strcmp(name, mountEntry->mnt_fsname) == 0
+ ) { /* String match. */
+ break;
+ }
+ if (stat(mountEntry->mnt_fsname, &s) == 0 && s.st_rdev == mountDevice) /* Match the device. */
+ break;
+ if (stat(mountEntry->mnt_dir, &s) == 0 && s.st_dev == mountDevice) /* Match the directory's mount point. */
+ break;
+ }
+ endmntent(mountTable);
+ return mountEntry;
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/find_pid_by_name.c b/i/pc104/initrd/conf/busybox/libbb/find_pid_by_name.c
new file mode 100644
index 0000000..e986169
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/find_pid_by_name.c
@@ -0,0 +1,55 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ */
+
+#include "libbb.h"
+
+/* find_pid_by_name()
+ *
+ * Modified by Vladimir Oleynik for use with libbb/procps.c
+ * This finds the pid of the specified process.
+ * Currently, it's implemented by rummaging through
+ * the proc filesystem.
+ *
+ * Returns a list of all matching PIDs
+ * It is the caller's duty to free the returned pidlist.
+ */
+pid_t* find_pid_by_name(const char* procName)
+{
+ pid_t* pidList;
+ int i = 0;
+ procps_status_t* p = NULL;
+
+ pidList = xmalloc(sizeof(*pidList));
+ while ((p = procps_scan(p, PSSCAN_PID|PSSCAN_COMM))) {
+ if (strncmp(p->comm, procName, sizeof(p->comm)-1) == 0) {
+ pidList = xrealloc(pidList, sizeof(*pidList) * (i+2));
+ pidList[i++] = p->pid;
+ }
+ }
+
+ pidList[i] = 0;
+ return pidList;
+}
+
+pid_t *pidlist_reverse(pid_t *pidList)
+{
+ int i = 0;
+ while (pidList[i])
+ i++;
+ if (--i >= 0) {
+ pid_t k;
+ int j;
+ for (j = 0; i > j; i--, j++) {
+ k = pidList[i];
+ pidList[i] = pidList[j];
+ pidList[j] = k;
+ }
+ }
+ return pidList;
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/find_root_device.c b/i/pc104/initrd/conf/busybox/libbb/find_root_device.c
new file mode 100644
index 0000000..ea360ea
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/find_root_device.c
@@ -0,0 +1,34 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "libbb.h"
+
+char *find_block_device(const char *path)
+{
+ DIR *dir;
+ struct dirent *entry;
+ struct stat st;
+ dev_t dev;
+ char *retpath=NULL;
+
+ if (stat(path, &st) || !(dir = opendir("/dev")))
+ return NULL;
+ dev = (st.st_mode & S_IFMT) == S_IFBLK ? st.st_rdev : st.st_dev;
+ while ((entry = readdir(dir)) != NULL) {
+ char devpath[PATH_MAX];
+ sprintf(devpath,"/dev/%s", entry->d_name);
+ if (!stat(devpath, &st) && S_ISBLK(st.st_mode) && st.st_rdev == dev) {
+ retpath = xstrdup(devpath);
+ break;
+ }
+ }
+ closedir(dir);
+
+ return retpath;
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/full_write.c b/i/pc104/initrd/conf/busybox/libbb/full_write.c
new file mode 100644
index 0000000..7bbacb8
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/full_write.c
@@ -0,0 +1,36 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "libbb.h"
+
+/*
+ * Write all of the supplied buffer out to a file.
+ * This does multiple writes as necessary.
+ * Returns the amount written, or -1 on an error.
+ */
+ssize_t full_write(int fd, const void *buf, size_t len)
+{
+ ssize_t cc;
+ ssize_t total;
+
+ total = 0;
+
+ while (len) {
+ cc = safe_write(fd, buf, len);
+
+ if (cc < 0)
+ return cc; /* write() returns -1 on failure. */
+
+ total += cc;
+ buf = ((const char *)buf) + cc;
+ len -= cc;
+ }
+
+ return total;
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/get_console.c b/i/pc104/initrd/conf/busybox/libbb/get_console.c
new file mode 100644
index 0000000..42ee137
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/get_console.c
@@ -0,0 +1,72 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) many different people. If you wrote this, please
+ * acknowledge your work.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include <sys/ioctl.h>
+#include "libbb.h"
+
+
+/* From <linux/kd.h> */
+enum { KDGKBTYPE = 0x4B33 }; /* get keyboard type */
+
+
+static int open_a_console(const char *fnam)
+{
+ int fd;
+
+ /* try read-write */
+ fd = open(fnam, O_RDWR);
+
+ /* if failed, try read-only */
+ if (fd < 0 && errno == EACCES)
+ fd = open(fnam, O_RDONLY);
+
+ /* if failed, try write-only */
+ if (fd < 0 && errno == EACCES)
+ fd = open(fnam, O_WRONLY);
+
+ return fd;
+}
+
+/*
+ * Get an fd for use with kbd/console ioctls.
+ * We try several things because opening /dev/console will fail
+ * if someone else used X (which does a chown on /dev/console).
+ */
+
+int get_console_fd(void)
+{
+ static const char *const console_names[] = {
+ DEV_CONSOLE, CURRENT_VC, CURRENT_TTY
+ };
+
+ int fd;
+
+ for (fd = 2; fd >= 0; fd--) {
+ int fd4name;
+ int choise_fd;
+ char arg;
+
+ fd4name = open_a_console(console_names[fd]);
+ chk_std:
+ choise_fd = (fd4name >= 0 ? fd4name : fd);
+
+ arg = 0;
+ if (ioctl(choise_fd, KDGKBTYPE, &arg) == 0)
+ return choise_fd;
+ if(fd4name >= 0) {
+ close(fd4name);
+ fd4name = -1;
+ goto chk_std;
+ }
+ }
+
+ bb_error_msg("cannot get file descriptor referring to console");
+ return fd; /* total failure */
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/get_last_path_component.c b/i/pc104/initrd/conf/busybox/libbb/get_last_path_component.c
new file mode 100644
index 0000000..b7bc0e6
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/get_last_path_component.c
@@ -0,0 +1,32 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * bb_get_last_path_component implementation for busybox
+ *
+ * Copyright (C) 2001 Manuel Novoa III <mjn3@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "libbb.h"
+
+char *bb_get_last_path_component(char *path)
+{
+ char *first = path;
+ char *last;
+
+ last = path - 1;
+
+ while (*path) {
+ if ((*path != '/') && (path > ++last)) {
+ last = first = path;
+ }
+ ++path;
+ }
+
+ if (*first == '/') {
+ last = first;
+ }
+ last[1] = '\0';
+
+ return first;
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/get_line_from_file.c b/i/pc104/initrd/conf/busybox/libbb/get_line_from_file.c
new file mode 100644
index 0000000..2c9608e
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/get_line_from_file.c
@@ -0,0 +1,69 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 2005, 2006 Rob Landley <rob@landley.net>
+ * Copyright (C) 2004 Erik Andersen <andersen@codepoet.org>
+ * Copyright (C) 2001 Matt Krai
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "libbb.h"
+
+/* This function reads an entire line from a text file, up to a newline
+ * or NUL byte, inclusive. It returns a malloc'ed char * which must be
+ * stored and free'ed by the caller. If end is NULL '\n' isn't considered
+ * end of line. If end isn't NULL, length of the chunk read is stored in it.
+ * Return NULL if EOF/error */
+
+char *bb_get_chunk_from_file(FILE * file, int *end)
+{
+ int ch;
+ int idx = 0;
+ char *linebuf = NULL;
+ int linebufsz = 0;
+
+ while ((ch = getc(file)) != EOF) {
+ /* grow the line buffer as necessary */
+ if (idx >= linebufsz) {
+ linebuf = xrealloc(linebuf, linebufsz += 80);
+ }
+ linebuf[idx++] = (char) ch;
+ if (!ch || (end && ch == '\n'))
+ break;
+ }
+ if (end)
+ *end = idx;
+ if (linebuf) {
+ // huh, does fgets discard prior data on error like this?
+ // I don't think so....
+ //if (ferror(file)) {
+ // free(linebuf);
+ // return NULL;
+ //}
+ linebuf = xrealloc(linebuf, idx+1);
+ linebuf[idx] = '\0';
+ }
+ return linebuf;
+}
+
+/* Get line, including trailing \n if any */
+char *xmalloc_fgets(FILE * file)
+{
+ int i;
+
+ return bb_get_chunk_from_file(file, &i);
+}
+
+/* Get line. Remove trailing \n */
+char *xmalloc_getline(FILE * file)
+{
+ int i;
+ char *c = bb_get_chunk_from_file(file, &i);
+
+ if (i && c[--i] == '\n')
+ c[i] = '\0';
+
+ return c;
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/getopt32.c b/i/pc104/initrd/conf/busybox/libbb/getopt32.c
new file mode 100644
index 0000000..6cdbfd3
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/getopt32.c
@@ -0,0 +1,531 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * universal getopt32 implementation for busybox
+ *
+ * Copyright (C) 2003-2005 Vladimir Oleynik <dzo@simtreas.ru>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "libbb.h"
+#include <getopt.h>
+
+/* Documentation
+
+uint32_t
+getopt32(int argc, char **argv, const char *applet_opts, ...)
+
+ The command line options must be declared in const char
+ *applet_opts as a string of chars, for example:
+
+ flags = getopt32(argc, argv, "rnug");
+
+ If one of the given options is found, a flag value is added to
+ the return value (an unsigned long).
+
+ The flag value is determined by the position of the char in
+ applet_opts string. For example, in the above case:
+
+ flags = getopt32(argc, argv, "rnug");
+
+ "r" will add 1 (bit 0)
+ "n" will add 2 (bit 1)
+ "u will add 4 (bit 2)
+ "g" will add 8 (bit 3)
+
+ and so on. You can also look at the return value as a bit
+ field and each option sets one bit.
+
+ On exit, global variable optind is set so that if you
+ will do argc -= optind; argv += optind; then
+ argc will be equal to number of remaining non-option
+ arguments, first one would be in argv[0], next in argv[1] and so on
+ (options and their parameters will be moved into argv[]
+ positions prior to argv[optind]).
+
+ ":" If one of the options requires an argument, then add a ":"
+ after the char in applet_opts and provide a pointer to store
+ the argument. For example:
+
+ char *pointer_to_arg_for_a;
+ char *pointer_to_arg_for_b;
+ char *pointer_to_arg_for_c;
+ char *pointer_to_arg_for_d;
+
+ flags = getopt32(argc, argv, "a:b:c:d:",
+ &pointer_to_arg_for_a, &pointer_to_arg_for_b,
+ &pointer_to_arg_for_c, &pointer_to_arg_for_d);
+
+ The type of the pointer (char* or llist_t*) may be controlled
+ by the "::" special separator that is set in the external string
+ opt_complementary (see below for more info).
+
+ "::" If option can have an *optional* argument, then add a "::"
+ after its char in applet_opts and provide a pointer to store
+ the argument. Note that optional arguments _must_
+ immediately follow the option: -oparam, not -o param.
+
+ "+" If the first character in the applet_opts string is a plus,
+ then option processing will stop as soon as a non-option is
+ encountered in the argv array. Useful for applets like env
+ which should not process arguments to subprograms:
+ env -i ls -d /
+ Here we want env to process just the '-i', not the '-d'.
+
+const struct option *applet_long_options
+
+ This struct allows you to define long options. The syntax for
+ declaring the array is just like that of getopt's longopts.
+ (see getopt(3))
+
+ static const struct option applet_long_options[] = {
+ //name,has_arg,flag,val
+ { "verbose", 0, 0, 'v' },
+ { 0, 0, 0, 0 }
+ };
+ applet_long_options = applet_long_options;
+
+ The last member of struct option (val) typically is set to
+ matching short option from applet_opts. If there is no matching
+ char in applet_opts, then:
+ - return bit have next position after short options
+ - if has_arg is not "no_argument", use ptr for arg also
+ - opt_complementary affects it too
+
+ Note: a good applet will make long options configurable via the
+ config process and not a required feature. The current standard
+ is to name the config option CONFIG_FEATURE_<applet>_LONG_OPTIONS.
+
+const char *opt_complementary
+
+ ":" The colon (":") is used to separate groups of two or more chars
+ and/or groups of chars and special characters (stating some
+ conditions to be checked).
+
+ "abc" If groups of two or more chars are specified, the first char
+ is the main option and the other chars are secondary options.
+ Their flags will be turned on if the main option is found even
+ if they are not specifed on the command line. For example:
+
+ opt_complementary = "abc";
+ flags = getopt32(argc, argv, "abcd")
+
+ If getopt() finds "-a" on the command line, then
+ getopt32's return value will be as if "-a -b -c" were
+ found.
+
+ "ww" Adjacent double options have a counter associated which indicates
+ the number of occurences of the option.
+ For example the ps applet needs:
+ if w is given once, GNU ps sets the width to 132,
+ if w is given more than once, it is "unlimited"
+
+ int w_counter = 0;
+ opt_complementary = "ww";
+ getopt32(argc, argv, "w", &w_counter);
+ if (w_counter)
+ width = (w_counter == 1) ? 132 : INT_MAX;
+ else
+ get_terminal_width(...&width...);
+
+ w_counter is a pointer to an integer. It has to be passed to
+ getopt32() after all other option argument sinks.
+
+ For example: accept multiple -v to indicate the level of verbosity
+ and for each -b optarg, add optarg to my_b. Finally, if b is given,
+ turn off c and vice versa:
+
+ llist_t *my_b = NULL;
+ int verbose_level = 0;
+ opt_complementary = "vv:b::b-c:c-b";
+ f = getopt32(argc, argv, "vb:c", &my_b, &verbose_level);
+ if (f & 2) // -c after -b unsets -b flag
+ while (my_b) { dosomething_with(my_b->data); my_b = my_b->link; }
+ if (my_b) // but llist is stored if -b is specified
+ free_llist(my_b);
+ if (verbose_level) printf("verbose level is %d\n", verbose_level);
+
+Special characters:
+
+ "-" A dash as the first char in a opt_complementary group forces
+ all arguments to be treated as options, even if they have
+ no leading dashes. Next char in this case can't be a digit (0-9),
+ use ':' or end of line. For example:
+
+ opt_complementary = "-:w-x:x-w";
+ getopt32(argc, argv, "wx");
+
+ Allows any arguments to be given without a dash (./program w x)
+ as well as with a dash (./program -x).
+
+ "--" A double dash at the beginning of opt_complementary means the
+ argv[1] string should always be treated as options, even if it isn't
+ prefixed with a "-". This is useful for special syntax in applets
+ such as "ar" and "tar":
+ tar xvf foo.tar
+
+ "-N" A dash as the first char in a opt_complementary group followed
+ by a single digit (0-9) means that at least N non-option
+ arguments must be present on the command line
+
+ "=N" An equal sign as the first char in a opt_complementary group followed
+ by a single digit (0-9) means that exactly N non-option
+ arguments must be present on the command line
+
+ "?N" A "?" as the first char in a opt_complementary group followed
+ by a single digit (0-9) means that at most N arguments must be present
+ on the command line.
+
+ "V-" An option with dash before colon or end-of-line results in
+ bb_show_usage being called if this option is encountered.
+ This is typically used to implement "print verbose usage message
+ and exit" option.
+
+ "-" A dash between two options causes the second of the two
+ to be unset (and ignored) if it is given on the command line.
+
+ [FIXME: what if they are the same? like "x-x"? Is it ever useful?]
+
+ For example:
+ The du applet has the options "-s" and "-d depth". If
+ getopt32 finds -s, then -d is unset or if it finds -d
+ then -s is unset. (Note: busybox implements the GNU
+ "--max-depth" option as "-d".) To obtain this behavior, you
+ set opt_complementary = "s-d:d-s". Only one flag value is
+ added to getopt32's return value depending on the
+ position of the options on the command line. If one of the
+ two options requires an argument pointer (":" in applet_opts
+ as in "d:") optarg is set accordingly.
+
+ char *smax_print_depth;
+
+ opt_complementary = "s-d:d-s:x-x";
+ opt = getopt32(argc, argv, "sd:x", &smax_print_depth);
+
+ if (opt & 2)
+ max_print_depth = atoi(smax_print_depth);
+ if (opt & 4)
+ printf("Detected odd -x usage\n");
+
+ "--" A double dash between two options, or between an option and a group
+ of options, means that they are mutually exclusive. Unlike
+ the "-" case above, an error will be forced if the options
+ are used together.
+
+ For example:
+ The cut applet must have only one type of list specified, so
+ -b, -c and -f are mutally exclusive and should raise an error
+ if specified together. In this case you must set
+ opt_complementary = "b--cf:c--bf:f--bc". If two of the
+ mutually exclusive options are found, getopt32's
+ return value will have the error flag set (BB_GETOPT_ERROR) so
+ that we can check for it:
+
+ if (flags & BB_GETOPT_ERROR)
+ bb_show_usage();
+
+ "x--x" Variation of the above, it means that -x option should occur
+ at most once.
+
+ "?" A "?" as the first char in a opt_complementary group means:
+ if BB_GETOPT_ERROR is detected, don't return, call bb_show_usage
+ and exit instead. Next char after '?' can't be a digit.
+
+ "::" A double colon after a char in opt_complementary means that the
+ option can occur multiple times. Each occurrence will be saved as
+ a llist_t element instead of char*.
+
+ For example:
+ The grep applet can have one or more "-e pattern" arguments.
+ In this case you should use getopt32() as follows:
+
+ llist_t *patterns = NULL;
+
+ (this pointer must be initializated to NULL if the list is empty
+ as required by *llist_add_to(llist_t *old_head, char *new_item).)
+
+ opt_complementary = "e::";
+
+ getopt32(argc, argv, "e:", &patterns);
+ $ grep -e user -e root /etc/passwd
+ root:x:0:0:root:/root:/bin/bash
+ user:x:500:500::/home/user:/bin/bash
+
+ "?" An "?" between an option and a group of options means that
+ at least one of them is required to occur if the first option
+ occurs in preceding command line arguments.
+
+ For example from "id" applet:
+
+ // Don't allow -n -r -rn -ug -rug -nug -rnug
+ opt_complementary = "r?ug:n?ug:?u--g:g--u";
+ flags = getopt32(argc, argv, "rnug");
+
+ This example allowed only:
+ $ id; id -u; id -g; id -ru; id -nu; id -rg; id -ng; id -rnu; id -rng
+
+ "X" A opt_complementary group with just a single letter means
+ that this option is required. If more than one such group exists,
+ at least one option is required to occur (not all of them).
+ For example from "start-stop-daemon" applet:
+
+ // Don't allow -KS -SK, but -S or -K is required
+ opt_complementary = "K:S:?K--S:S--K";
+ flags = getopt32(argc, argv, "KS...);
+
+
+ Don't forget to use ':'. For example, "?322-22-23X-x-a"
+ is interpreted as "?3:22:-2:2-2:2-3Xa:2--x" -
+ max 3 args; count uses of '-2'; min 2 args; if there is
+ a '-2' option then unset '-3', '-X' and '-a'; if there is
+ a '-2' and after it a '-x' then error out.
+*/
+
+/* Code here assumes that 'unsigned' is at least 32 bits wide */
+
+const char *opt_complementary;
+
+typedef struct {
+ int opt;
+ int list_flg;
+ unsigned switch_on;
+ unsigned switch_off;
+ unsigned incongruously;
+ unsigned requires;
+ void **optarg; /* char **optarg or llist_t **optarg */
+ int *counter;
+} t_complementary;
+
+/* You can set applet_long_options for parse called long options */
+#if ENABLE_GETOPT_LONG
+static const struct option bb_default_long_options[] = {
+/* { "help", 0, NULL, '?' }, */
+ { 0, 0, 0, 0 }
+};
+
+const struct option *applet_long_options = bb_default_long_options;
+#endif
+
+uint32_t option_mask32;
+
+uint32_t
+getopt32(int argc, char **argv, const char *applet_opts, ...)
+{
+ unsigned flags = 0;
+ unsigned requires = 0;
+ t_complementary complementary[33];
+ int c;
+ const unsigned char *s;
+ t_complementary *on_off;
+ va_list p;
+#if ENABLE_GETOPT_LONG
+ const struct option *l_o;
+#endif
+ unsigned trigger;
+ char **pargv = NULL;
+ int min_arg = 0;
+ int max_arg = -1;
+
+#define SHOW_USAGE_IF_ERROR 1
+#define ALL_ARGV_IS_OPTS 2
+#define FIRST_ARGV_IS_OPT 4
+#define FREE_FIRST_ARGV_IS_OPT 8
+ int spec_flgs = 0;
+
+ va_start(p, applet_opts);
+
+ c = 0;
+ on_off = complementary;
+ memset(on_off, 0, sizeof(complementary));
+
+ /* skip GNU extension */
+ s = (const unsigned char *)applet_opts;
+ if (*s == '+' || *s == '-')
+ s++;
+ while (*s) {
+ if (c >= 32) break;
+ on_off->opt = *s;
+ on_off->switch_on = (1 << c);
+ if (*++s == ':') {
+ on_off->optarg = va_arg(p, void **);
+ while (*++s == ':') /* skip */;
+ }
+ on_off++;
+ c++;
+ }
+
+#if ENABLE_GETOPT_LONG
+ for (l_o = applet_long_options; l_o->name; l_o++) {
+ if (l_o->flag)
+ continue;
+ for (on_off = complementary; on_off->opt != 0; on_off++)
+ if (on_off->opt == l_o->val)
+ goto next_long;
+ if (c >= 32) break;
+ on_off->opt = l_o->val;
+ on_off->switch_on = (1 << c);
+ if (l_o->has_arg != no_argument)
+ on_off->optarg = va_arg(p, void **);
+ c++;
+ next_long: ;
+ }
+#endif /* ENABLE_GETOPT_LONG */
+ for (s = (const unsigned char *)opt_complementary; s && *s; s++) {
+ t_complementary *pair;
+ unsigned *pair_switch;
+
+ if (*s == ':')
+ continue;
+ c = s[1];
+ if (*s == '?') {
+ if (c < '0' || c > '9') {
+ spec_flgs |= SHOW_USAGE_IF_ERROR;
+ } else {
+ max_arg = c - '0';
+ s++;
+ }
+ continue;
+ }
+ if (*s == '-') {
+ if (c < '0' || c > '9') {
+ if (c == '-') {
+ spec_flgs |= FIRST_ARGV_IS_OPT;
+ s++;
+ } else
+ spec_flgs |= ALL_ARGV_IS_OPTS;
+ } else {
+ min_arg = c - '0';
+ s++;
+ }
+ continue;
+ }
+ if (*s == '=') {
+ min_arg = max_arg = c - '0';
+ s++;
+ continue;
+ }
+ for (on_off = complementary; on_off->opt; on_off++)
+ if (on_off->opt == *s)
+ break;
+ if (c == ':' && s[2] == ':') {
+ on_off->list_flg++;
+ continue;
+ }
+ if (c == ':' || c == '\0') {
+ requires |= on_off->switch_on;
+ continue;
+ }
+ if (c == '-' && (s[2] == ':' || s[2] == '\0')) {
+ flags |= on_off->switch_on;
+ on_off->incongruously |= on_off->switch_on;
+ s++;
+ continue;
+ }
+ if (c == *s) {
+ on_off->counter = va_arg(p, int *);
+ s++;
+ }
+ pair = on_off;
+ pair_switch = &(pair->switch_on);
+ for (s++; *s && *s != ':'; s++) {
+ if (*s == '?') {
+ pair_switch = &(pair->requires);
+ } else if (*s == '-') {
+ if (pair_switch == &(pair->switch_off))
+ pair_switch = &(pair->incongruously);
+ else
+ pair_switch = &(pair->switch_off);
+ } else {
+ for (on_off = complementary; on_off->opt; on_off++)
+ if (on_off->opt == *s) {
+ *pair_switch |= on_off->switch_on;
+ break;
+ }
+ }
+ }
+ s--;
+ }
+ va_end (p);
+
+ if (spec_flgs & FIRST_ARGV_IS_OPT) {
+ if (argv[1] && argv[1][0] != '-' && argv[1][0] != '\0') {
+ argv[1] = xasprintf("-%s", argv[1]);
+ if (ENABLE_FEATURE_CLEAN_UP)
+ spec_flgs |= FREE_FIRST_ARGV_IS_OPT;
+ }
+ }
+
+ /* In case getopt32 was already called, reinit some state */
+ optind = 1;
+
+ /* Note: just "getopt() <= 0" will not work good for
+ * "fake" short options, like this one:
+ * wget $'-\203' "Test: test" http://kernel.org/
+ * (supposed to act as --header, but doesn't) */
+#if ENABLE_GETOPT_LONG
+ while ((c = getopt_long(argc, argv, applet_opts,
+ applet_long_options, NULL)) != -1) {
+#else
+ while ((c = getopt(argc, argv, applet_opts)) != -1) {
+#endif
+ c &= 0xff; /* fight libc's sign extends */
+ loop_arg_is_opt:
+ for (on_off = complementary; on_off->opt != c; on_off++) {
+ /* c==0 if long opt have non NULL flag */
+ if (on_off->opt == 0 && c != 0)
+ bb_show_usage();
+ }
+ if (flags & on_off->incongruously) {
+ if ((spec_flgs & SHOW_USAGE_IF_ERROR))
+ bb_show_usage();
+ flags |= BB_GETOPT_ERROR;
+ }
+ trigger = on_off->switch_on & on_off->switch_off;
+ flags &= ~(on_off->switch_off ^ trigger);
+ flags |= on_off->switch_on ^ trigger;
+ flags ^= trigger;
+ if (on_off->counter)
+ (*(on_off->counter))++;
+ if (on_off->list_flg) {
+ llist_add_to((llist_t **)(on_off->optarg), optarg);
+ } else if (on_off->optarg) {
+ *(char **)(on_off->optarg) = optarg;
+ }
+ if (pargv != NULL)
+ break;
+ }
+
+ if (spec_flgs & ALL_ARGV_IS_OPTS) {
+ /* process argv is option, for example "ps" applet */
+ if (pargv == NULL)
+ pargv = argv + optind;
+ while (*pargv) {
+ c = **pargv;
+ if (c == '\0') {
+ pargv++;
+ } else {
+ (*pargv)++;
+ goto loop_arg_is_opt;
+ }
+ }
+ }
+
+#if (ENABLE_AR || ENABLE_TAR) && ENABLE_FEATURE_CLEAN_UP
+ if (spec_flgs & FREE_FIRST_ARGV_IS_OPT)
+ free(argv[1]);
+#endif
+ /* check depending requires for given options */
+ for (on_off = complementary; on_off->opt; on_off++) {
+ if (on_off->requires && (flags & on_off->switch_on) &&
+ (flags & on_off->requires) == 0)
+ bb_show_usage();
+ }
+ if (requires && (flags & requires) == 0)
+ bb_show_usage();
+ argc -= optind;
+ if (argc < min_arg || (max_arg >= 0 && argc > max_arg))
+ bb_show_usage();
+
+ option_mask32 = flags;
+ return flags;
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/herror_msg.c b/i/pc104/initrd/conf/busybox/libbb/herror_msg.c
new file mode 100644
index 0000000..1e6908d
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/herror_msg.c
@@ -0,0 +1,22 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include <stdarg.h>
+#include <stdlib.h>
+
+#include "libbb.h"
+
+void bb_herror_msg(const char *s, ...)
+{
+ va_list p;
+
+ va_start(p, s);
+ bb_vherror_msg(s, p);
+ va_end(p);
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/herror_msg_and_die.c b/i/pc104/initrd/conf/busybox/libbb/herror_msg_and_die.c
new file mode 100644
index 0000000..a7a22ca
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/herror_msg_and_die.c
@@ -0,0 +1,23 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include <stdarg.h>
+#include <stdlib.h>
+
+#include "libbb.h"
+
+void bb_herror_msg_and_die(const char *s, ...)
+{
+ va_list p;
+
+ va_start(p, s);
+ bb_vherror_msg(s, p);
+ va_end(p);
+ sleep_and_die();
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/human_readable.c b/i/pc104/initrd/conf/busybox/libbb/human_readable.c
new file mode 100644
index 0000000..ff1b551
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/human_readable.c
@@ -0,0 +1,89 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * June 30, 2001 Manuel Novoa III
+ *
+ * All-integer version (hey, not everyone has floating point) of
+ * make_human_readable_str, modified from similar code I had written
+ * for busybox several months ago.
+ *
+ * Notes:
+ * 1) I'm using an unsigned long long to hold the product size * block_size,
+ * as df (which calls this routine) could request a representation of a
+ * partition size in bytes > max of unsigned long. If long longs aren't
+ * available, it would be possible to do what's needed using polynomial
+ * representations (say, powers of 1024) and manipulating coefficients.
+ * The base ten "bytes" output could be handled similarly.
+ *
+ * 2) This routine always outputs a decimal point and a tenths digit when
+ * display_unit != 0. Hence, it isn't uncommon for the returned string
+ * to have a length of 5 or 6.
+ *
+ * It might be nice to add a flag to indicate no decimal digits in
+ * that case. This could be either an additional parameter, or a
+ * special value of display_unit. Such a flag would also be nice for du.
+ *
+ * Some code to omit the decimal point and tenths digit is sketched out
+ * and "#if 0"'d below.
+ */
+
+#include <stdio.h>
+#include "libbb.h"
+
+const char *make_human_readable_str(unsigned long long size,
+ unsigned long block_size, unsigned long display_unit)
+{
+ /* The code will adjust for additional (appended) units. */
+ static const char zero_and_units[] = { '0', 0, 'k', 'M', 'G', 'T' };
+ static const char fmt[] = "%llu";
+ static const char fmt_tenths[] = "%llu.%d%c";
+
+ static char str[21]; /* Sufficient for 64 bit unsigned integers. */
+
+ unsigned long long val;
+ int frac;
+ const char *u;
+ const char *f;
+
+ u = zero_and_units;
+ f = fmt;
+ frac = 0;
+
+ val = size * block_size;
+ if (val == 0) {
+ return u;
+ }
+
+ if (display_unit) {
+ val += display_unit/2; /* Deal with rounding. */
+ val /= display_unit; /* Don't combine with the line above!!! */
+ } else {
+ ++u;
+ while ((val >= 1024)
+ && (u < zero_and_units + sizeof(zero_and_units) - 1)
+ ) {
+ f = fmt_tenths;
+ ++u;
+ frac = (((int)(val % 1024)) * 10 + 1024/2) / 1024;
+ val /= 1024;
+ }
+ if (frac >= 10) { /* We need to round up here. */
+ ++val;
+ frac = 0;
+ }
+#if 0
+ /* Sample code to omit decimal point and tenths digit. */
+ if ( /* no_tenths */ 1 ) {
+ if ( frac >= 5 ) {
+ ++val;
+ }
+ f = "%llu%*c" /* fmt_no_tenths */ ;
+ frac = 1;
+ }
+#endif
+ }
+
+ /* If f==fmt then 'frac' and 'u' are ignored. */
+ snprintf(str, sizeof(str), f, val, frac, *u);
+
+ return str;
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/inet_common.c b/i/pc104/initrd/conf/busybox/libbb/inet_common.c
new file mode 100644
index 0000000..0ef91dc
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/inet_common.c
@@ -0,0 +1,235 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * stolen from net-tools-1.59 and stripped down for busybox by
+ * Erik Andersen <andersen@codepoet.org>
+ *
+ * Heavily modified by Manuel Novoa III Mar 12, 2001
+ *
+ *
+ */
+
+#include "libbb.h"
+#include "inet_common.h"
+
+int INET_resolve(const char *name, struct sockaddr_in *s_in, int hostfirst)
+{
+ struct hostent *hp;
+ struct netent *np;
+
+ /* Grmpf. -FvK */
+ s_in->sin_family = AF_INET;
+ s_in->sin_port = 0;
+
+ /* Default is special, meaning 0.0.0.0. */
+ if (!strcmp(name, bb_str_default)) {
+ s_in->sin_addr.s_addr = INADDR_ANY;
+ return 1;
+ }
+ /* Look to see if it's a dotted quad. */
+ if (inet_aton(name, &s_in->sin_addr)) {
+ return 0;
+ }
+ /* If we expect this to be a hostname, try hostname database first */
+#ifdef DEBUG
+ if (hostfirst) {
+ bb_error_msg("gethostbyname(%s)", name);
+ }
+#endif
+ if (hostfirst) {
+ hp = gethostbyname(name);
+ if (hp != NULL) {
+ memcpy(&s_in->sin_addr, hp->h_addr_list[0],
+ sizeof(struct in_addr));
+ return 0;
+ }
+ }
+ /* Try the NETWORKS database to see if this is a known network. */
+#ifdef DEBUG
+ bb_error_msg("getnetbyname(%s)", name);
+#endif
+ np = getnetbyname(name);
+ if (np != NULL) {
+ s_in->sin_addr.s_addr = htonl(np->n_net);
+ return 1;
+ }
+ if (hostfirst) {
+ /* Don't try again */
+ return -1;
+ }
+#ifdef DEBUG
+ res_init();
+ _res.options |= RES_DEBUG;
+#endif
+
+#ifdef DEBUG
+ bb_error_msg("gethostbyname(%s)", name);
+#endif
+ hp = gethostbyname(name);
+ if (hp == NULL) {
+ return -1;
+ }
+ memcpy(&s_in->sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
+ return 0;
+}
+
+/* cache */
+struct addr {
+ struct sockaddr_in addr;
+ char *name;
+ int host;
+ struct addr *next;
+};
+
+static struct addr *INET_nn = NULL; /* addr-to-name cache */
+
+/* numeric: & 0x8000: default instead of *,
+ * & 0x4000: host instead of net,
+ * & 0x0fff: don't resolve
+ */
+int INET_rresolve(char *name, size_t len, struct sockaddr_in *s_in,
+ int numeric, unsigned int netmask)
+{
+ struct hostent *ent;
+ struct netent *np;
+ struct addr *pn;
+ uint32_t ad, host_ad;
+ int host = 0;
+
+ /* Grmpf. -FvK */
+ if (s_in->sin_family != AF_INET) {
+#ifdef DEBUG
+ bb_error_msg("rresolve: unsupport address family %d !",
+ s_in->sin_family);
+#endif
+ errno = EAFNOSUPPORT;
+ return -1;
+ }
+ ad = s_in->sin_addr.s_addr;
+#ifdef DEBUG
+ bb_error_msg("rresolve: %08x, mask %08x, num %08x", (unsigned)ad, netmask, numeric);
+#endif
+ if (ad == INADDR_ANY) {
+ if ((numeric & 0x0FFF) == 0) {
+ if (numeric & 0x8000)
+ safe_strncpy(name, bb_str_default, len);
+ else
+ safe_strncpy(name, "*", len);
+ return 0;
+ }
+ }
+ if (numeric & 0x0FFF) {
+ safe_strncpy(name, inet_ntoa(s_in->sin_addr), len);
+ return 0;
+ }
+
+ if ((ad & (~netmask)) != 0 || (numeric & 0x4000))
+ host = 1;
+ pn = INET_nn;
+ while (pn != NULL) {
+ if (pn->addr.sin_addr.s_addr == ad && pn->host == host) {
+ safe_strncpy(name, pn->name, len);
+#ifdef DEBUG
+ bb_error_msg("rresolve: found %s %08x in cache",
+ (host ? "host" : "net"), (unsigned)ad);
+#endif
+ return 0;
+ }
+ pn = pn->next;
+ }
+
+ host_ad = ntohl(ad);
+ np = NULL;
+ ent = NULL;
+ if (host) {
+#ifdef DEBUG
+ bb_error_msg("gethostbyaddr (%08x)", (unsigned)ad);
+#endif
+ ent = gethostbyaddr((char *) &ad, 4, AF_INET);
+ if (ent != NULL) {
+ safe_strncpy(name, ent->h_name, len);
+ }
+ } else {
+#ifdef DEBUG
+ bb_error_msg("getnetbyaddr (%08x)", (unsigned)host_ad);
+#endif
+ np = getnetbyaddr(host_ad, AF_INET);
+ if (np != NULL) {
+ safe_strncpy(name, np->n_name, len);
+ }
+ }
+ if (!ent && !np) {
+ safe_strncpy(name, inet_ntoa(s_in->sin_addr), len);
+ }
+ pn = xmalloc(sizeof(struct addr));
+ pn->addr = *s_in;
+ pn->next = INET_nn;
+ pn->host = host;
+ pn->name = xstrdup(name);
+ INET_nn = pn;
+ return 0;
+}
+
+#ifdef CONFIG_FEATURE_IPV6
+
+int INET6_resolve(const char *name, struct sockaddr_in6 *sin6)
+{
+ struct addrinfo req, *ai;
+ int s;
+
+ memset(&req, '\0', sizeof req);
+ req.ai_family = AF_INET6;
+ s = getaddrinfo(name, NULL, &req, &ai);
+ if (s) {
+ bb_error_msg("getaddrinfo: %s: %d", name, s);
+ return -1;
+ }
+ memcpy(sin6, ai->ai_addr, sizeof(struct sockaddr_in6));
+ freeaddrinfo(ai);
+ return 0;
+}
+
+#ifndef IN6_IS_ADDR_UNSPECIFIED
+# define IN6_IS_ADDR_UNSPECIFIED(a) \
+ (((uint32_t *) (a))[0] == 0 && ((uint32_t *) (a))[1] == 0 && \
+ ((uint32_t *) (a))[2] == 0 && ((uint32_t *) (a))[3] == 0)
+#endif
+
+
+int INET6_rresolve(char *name, size_t len, struct sockaddr_in6 *sin6,
+ int numeric)
+{
+ int s;
+
+ /* Grmpf. -FvK */
+ if (sin6->sin6_family != AF_INET6) {
+#ifdef DEBUG
+ bb_error_msg("rresolve: unsupport address family %d!",
+ sin6->sin6_family);
+#endif
+ errno = EAFNOSUPPORT;
+ return -1;
+ }
+ if (numeric & 0x7FFF) {
+ inet_ntop(AF_INET6, &sin6->sin6_addr, name, len);
+ return 0;
+ }
+ if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
+ if (numeric & 0x8000) {
+ strcpy(name, bb_str_default);
+ } else {
+ name[0] = '*';
+ name[1] = '\0';
+ }
+ return 0;
+ }
+
+ s = getnameinfo((struct sockaddr *) sin6, sizeof(struct sockaddr_in6),
+ name, len, NULL, 0, 0);
+ if (s) {
+ bb_error_msg("getnameinfo failed");
+ return -1;
+ }
+ return 0;
+}
+
+#endif /* CONFIG_FEATURE_IPV6 */
diff --git a/i/pc104/initrd/conf/busybox/libbb/info_msg.c b/i/pc104/initrd/conf/busybox/libbb/info_msg.c
new file mode 100644
index 0000000..78d5c8f
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/info_msg.c
@@ -0,0 +1,23 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include "libbb.h"
+
+void bb_info_msg(const char *s, ...)
+{
+ va_list p;
+
+ va_start(p, s);
+ bb_vinfo_msg(s, p);
+ va_end(p);
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/inode_hash.c b/i/pc104/initrd/conf/busybox/libbb/inode_hash.c
new file mode 100644
index 0000000..0705102
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/inode_hash.c
@@ -0,0 +1,90 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) many different people.
+ * If you wrote this, please acknowledge your work.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "libbb.h"
+
+typedef struct ino_dev_hash_bucket_struct {
+ struct ino_dev_hash_bucket_struct *next;
+ ino_t ino;
+ dev_t dev;
+ char name[1];
+} ino_dev_hashtable_bucket_t;
+
+#define HASH_SIZE 311 /* Should be prime */
+#define hash_inode(i) ((i) % HASH_SIZE)
+
+/* array of [HASH_SIZE] elements */
+static ino_dev_hashtable_bucket_t **ino_dev_hashtable;
+
+/*
+ * Return name if statbuf->st_ino && statbuf->st_dev are recorded in
+ * ino_dev_hashtable, else return NULL
+ */
+char *is_in_ino_dev_hashtable(const struct stat *statbuf)
+{
+ ino_dev_hashtable_bucket_t *bucket;
+
+ if (!ino_dev_hashtable)
+ return NULL;
+
+ bucket = ino_dev_hashtable[hash_inode(statbuf->st_ino)];
+ while (bucket != NULL) {
+ if ((bucket->ino == statbuf->st_ino)
+ && (bucket->dev == statbuf->st_dev)
+ ) {
+ return bucket->name;
+ }
+ bucket = bucket->next;
+ }
+ return NULL;
+}
+
+/* Add statbuf to statbuf hash table */
+void add_to_ino_dev_hashtable(const struct stat *statbuf, const char *name)
+{
+ int i;
+ ino_dev_hashtable_bucket_t *bucket;
+
+ i = hash_inode(statbuf->st_ino);
+ if (!name)
+ name = "";
+ bucket = xmalloc(sizeof(ino_dev_hashtable_bucket_t) + strlen(name));
+ bucket->ino = statbuf->st_ino;
+ bucket->dev = statbuf->st_dev;
+ strcpy(bucket->name, name);
+
+ if (!ino_dev_hashtable)
+ ino_dev_hashtable = xzalloc(HASH_SIZE * sizeof(*ino_dev_hashtable));
+
+ bucket->next = ino_dev_hashtable[i];
+ ino_dev_hashtable[i] = bucket;
+}
+
+#if ENABLE_FEATURE_CLEAN_UP
+/* Clear statbuf hash table */
+void reset_ino_dev_hashtable(void)
+{
+ int i;
+ ino_dev_hashtable_bucket_t *bucket;
+
+ for (i = 0; i < HASH_SIZE; i++) {
+ while (ino_dev_hashtable[i] != NULL) {
+ bucket = ino_dev_hashtable[i]->next;
+ free(ino_dev_hashtable[i]);
+ ino_dev_hashtable[i] = bucket;
+ }
+ }
+ free(ino_dev_hashtable);
+ ino_dev_hashtable = NULL;
+}
+#endif
diff --git a/i/pc104/initrd/conf/busybox/libbb/isdirectory.c b/i/pc104/initrd/conf/busybox/libbb/isdirectory.c
new file mode 100644
index 0000000..b359198
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/isdirectory.c
@@ -0,0 +1,39 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Based in part on code from sash, Copyright (c) 1999 by David I. Bell
+ * Permission has been granted to redistribute this code under the GPL.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include <sys/stat.h>
+#include "libbb.h"
+
+/*
+ * Return TRUE if a fileName is a directory.
+ * Nonexistent files return FALSE.
+ */
+int is_directory(const char *fileName, const int followLinks, struct stat *statBuf)
+{
+ int status;
+ struct stat astatBuf;
+
+ if (statBuf == NULL) {
+ /* set from auto stack buffer */
+ statBuf = &astatBuf;
+ }
+
+ if (followLinks)
+ status = stat(fileName, statBuf);
+ else
+ status = lstat(fileName, statBuf);
+
+ if (status < 0 || !(S_ISDIR(statBuf->st_mode))) {
+ status = FALSE;
+ }
+ else status = TRUE;
+
+ return status;
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/kernel_version.c b/i/pc104/initrd/conf/busybox/libbb/kernel_version.c
new file mode 100644
index 0000000..50b82ae
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/kernel_version.c
@@ -0,0 +1,37 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include <sys/utsname.h> /* for uname(2) */
+
+#include "libbb.h"
+
+/* Returns current kernel version encoded as major*65536 + minor*256 + patch,
+ * so, for example, to check if the kernel is greater than 2.2.11:
+ *
+ * if (get_linux_version_code() > KERNEL_VERSION(2,2,11)) { <stuff> }
+ */
+int get_linux_version_code(void)
+{
+ struct utsname name;
+ char *s;
+ int i, r;
+
+ if (uname(&name) == -1) {
+ bb_perror_msg("cannot get system information");
+ return 0;
+ }
+
+ s = name.release;
+ r = 0;
+ for (i = 0; i < 3; i++) {
+ r = r * 256 + atoi(strtok(s, "."));
+ s = NULL;
+ }
+ return r;
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/last_char_is.c b/i/pc104/initrd/conf/busybox/libbb/last_char_is.c
new file mode 100644
index 0000000..aaa85dd
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/last_char_is.c
@@ -0,0 +1,24 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * busybox library eXtended function
+ *
+ * Copyright (C) 2001 Larry Doolittle, <ldoolitt@recycle.lbl.gov>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "libbb.h"
+
+/* Find out if the last character of a string matches the one given.
+ * Don't underrun the buffer if the string length is 0.
+ */
+char* last_char_is(const char *s, int c)
+{
+ if (s && *s) {
+ size_t sz = strlen(s) - 1;
+ s += sz;
+ if ( (unsigned char)*s == c)
+ return (char*)s;
+ }
+ return NULL;
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/lineedit.c b/i/pc104/initrd/conf/busybox/libbb/lineedit.c
new file mode 100644
index 0000000..edc0e51
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/lineedit.c
@@ -0,0 +1,1801 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Termios command line History and Editing.
+ *
+ * Copyright (c) 1986-2003 may safely be consumed by a BSD or GPL license.
+ * Written by: Vladimir Oleynik <dzo@simtreas.ru>
+ *
+ * Used ideas:
+ * Adam Rogoyski <rogoyski@cs.utexas.edu>
+ * Dave Cinege <dcinege@psychosis.com>
+ * Jakub Jelinek (c) 1995
+ * Erik Andersen <andersen@codepoet.org> (Majorly adjusted for busybox)
+ *
+ * This code is 'as is' with no warranty.
+ */
+
+/*
+ Usage and known bugs:
+ Terminal key codes are not extensive, and more will probably
+ need to be added. This version was created on Debian GNU/Linux 2.x.
+ Delete, Backspace, Home, End, and the arrow keys were tested
+ to work in an Xterm and console. Ctrl-A also works as Home.
+ Ctrl-E also works as End.
+
+ Small bugs (simple effect):
+ - not true viewing if terminal size (x*y symbols) less
+ size (prompt + editor's line + 2 symbols)
+ - not true viewing if length prompt less terminal width
+ */
+
+#include <sys/ioctl.h>
+#include "busybox.h"
+
+
+/* FIXME: obsolete CONFIG item? */
+#define ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT 0
+
+
+#ifdef TEST
+
+#define ENABLE_FEATURE_EDITING 0
+#define ENABLE_FEATURE_TAB_COMPLETION 0
+#define ENABLE_FEATURE_USERNAME_COMPLETION 0
+#define ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT 0
+#define ENABLE_FEATURE_CLEAN_UP 0
+
+#endif /* TEST */
+
+
+/* Entire file (except TESTing part) sits inside this #if */
+#if ENABLE_FEATURE_EDITING
+
+#if ENABLE_LOCALE_SUPPORT
+#define Isprint(c) isprint(c)
+#else
+#define Isprint(c) ((c) >= ' ' && (c) != ((unsigned char)'\233'))
+#endif
+
+#define ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR \
+(ENABLE_FEATURE_USERNAME_COMPLETION || ENABLE_FEATURE_EDITING_FANCY_PROMPT)
+
+
+static line_input_t *state;
+
+static struct termios initial_settings, new_settings;
+
+static volatile unsigned cmdedit_termw = 80; /* actual terminal width */
+
+static int cmdedit_x; /* real x terminal position */
+static int cmdedit_y; /* pseudoreal y terminal position */
+static int cmdedit_prmt_len; /* length of prompt (without colors etc) */
+
+static unsigned cursor;
+static unsigned command_len;
+static char *command_ps;
+static const char *cmdedit_prompt;
+
+#if ENABLE_FEATURE_EDITING_FANCY_PROMPT
+static char *hostname_buf;
+static int num_ok_lines = 1;
+#endif
+
+#if ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR
+static char *user_buf = (char*)"";
+static char *home_pwd_buf = (char*)"";
+#endif
+
+#if ENABLE_FEATURE_TAB_COMPLETION
+static int my_uid;
+static int my_gid;
+#endif
+
+/* Put 'command_ps[cursor]', cursor++.
+ * Advance cursor on screen. If we reached right margin, scroll text up
+ * and remove terminal margin effect by printing 'next_char' */
+static void cmdedit_set_out_char(int next_char)
+{
+ int c = (unsigned char)command_ps[cursor];
+
+ if (c == '\0') {
+ /* erase character after end of input string */
+ c = ' ';
+ }
+#if ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT
+ /* Display non-printable characters in reverse */
+ if (!Isprint(c)) {
+ if (c >= 128)
+ c -= 128;
+ if (c < ' ')
+ c += '@';
+ if (c == 127)
+ c = '?';
+ printf("\033[7m%c\033[0m", c);
+ } else
+#endif
+ {
+ if (initial_settings.c_lflag & ECHO)
+ putchar(c);
+ }
+ if (++cmdedit_x >= cmdedit_termw) {
+ /* terminal is scrolled down */
+ cmdedit_y++;
+ cmdedit_x = 0;
+ /* destroy "(auto)margin" */
+ putchar(next_char);
+ putchar('\b');
+ }
+// Huh? What if command_ps[cursor] == '\0' (we are at the end already?)
+ cursor++;
+}
+
+/* Move to end of line (by printing all chars till the end) */
+static void input_end(void)
+{
+ while (cursor < command_len)
+ cmdedit_set_out_char(' ');
+}
+
+/* Go to the next line */
+static void goto_new_line(void)
+{
+ input_end();
+ if (cmdedit_x)
+ putchar('\n');
+}
+
+
+static void out1str(const char *s)
+{
+ if (s)
+ fputs(s, stdout);
+}
+
+static void beep(void)
+{
+ putchar('\007');
+}
+
+/* Move back one character */
+/* (optimized for slow terminals) */
+static void input_backward(unsigned num)
+{
+ int count_y;
+
+ if (num > cursor)
+ num = cursor;
+ if (!num)
+ return;
+ cursor -= num;
+
+ if (cmdedit_x >= num) {
+ cmdedit_x -= num;
+ if (num <= 4) {
+ printf("\b\b\b\b" + (4-num));
+ return;
+ }
+ printf("\033[%uD", num);
+ return;
+ }
+
+ /* Need to go one or more lines up */
+ num -= cmdedit_x;
+ count_y = 1 + (num / cmdedit_termw);
+ cmdedit_y -= count_y;
+ cmdedit_x = cmdedit_termw * count_y - num;
+ /* go to 1st column; go up; go to correct column */
+ printf("\r" "\033[%dA" "\033[%dC", count_y, cmdedit_x);
+}
+
+static void put_prompt(void)
+{
+ out1str(cmdedit_prompt);
+ cmdedit_x = cmdedit_prmt_len;
+ cursor = 0;
+// Huh? what if cmdedit_prmt_len >= width?
+ cmdedit_y = 0; /* new quasireal y */
+}
+
+/* draw prompt, editor line, and clear tail */
+static void redraw(int y, int back_cursor)
+{
+ if (y > 0) /* up to start y */
+ printf("\033[%dA", y);
+ putchar('\r');
+ put_prompt();
+ input_end(); /* rewrite */
+ printf("\033[J"); /* erase after cursor */
+ input_backward(back_cursor);
+}
+
+#if ENABLE_FEATURE_EDITING_VI
+#define DELBUFSIZ 128
+static char *delbuf; /* a (malloced) place to store deleted characters */
+static char *delp;
+static char newdelflag; /* whether delbuf should be reused yet */
+#endif
+
+/* Delete the char in front of the cursor, optionally saving it
+ * for later putback */
+static void input_delete(int save)
+{
+ int j = cursor;
+
+ if (j == command_len)
+ return;
+
+#if ENABLE_FEATURE_EDITING_VI
+ if (save) {
+ if (newdelflag) {
+ if (!delbuf)
+ delbuf = malloc(DELBUFSIZ);
+ /* safe if malloc fails */
+ delp = delbuf;
+ newdelflag = 0;
+ }
+ if (delbuf && (delp - delbuf < DELBUFSIZ))
+ *delp++ = command_ps[j];
+ }
+#endif
+
+ strcpy(command_ps + j, command_ps + j + 1);
+ command_len--;
+ input_end(); /* rewrite new line */
+ cmdedit_set_out_char(' '); /* erase char */
+ input_backward(cursor - j); /* back to old pos cursor */
+}
+
+#if ENABLE_FEATURE_EDITING_VI
+static void put(void)
+{
+ int ocursor;
+ int j = delp - delbuf;
+
+ if (j == 0)
+ return;
+ ocursor = cursor;
+ /* open hole and then fill it */
+ memmove(command_ps + cursor + j, command_ps + cursor, command_len - cursor + 1);
+ strncpy(command_ps + cursor, delbuf, j);
+ command_len += j;
+ input_end(); /* rewrite new line */
+ input_backward(cursor - ocursor - j + 1); /* at end of new text */
+}
+#endif
+
+/* Delete the char in back of the cursor */
+static void input_backspace(void)
+{
+ if (cursor > 0) {
+ input_backward(1);
+ input_delete(0);
+ }
+}
+
+/* Move forward one character */
+static void input_forward(void)
+{
+ if (cursor < command_len)
+ cmdedit_set_out_char(command_ps[cursor + 1]);
+}
+
+
+#if ENABLE_FEATURE_TAB_COMPLETION
+
+static char **matches;
+static unsigned num_matches;
+
+static void free_tab_completion_data(void)
+{
+ if (matches) {
+ while (num_matches)
+ free(matches[--num_matches]);
+ free(matches);
+ matches = NULL;
+ }
+}
+
+static void add_match(char *matched)
+{
+ int nm = num_matches;
+ int nm1 = nm + 1;
+
+ matches = xrealloc(matches, nm1 * sizeof(char *));
+ matches[nm] = matched;
+ num_matches++;
+}
+
+#if ENABLE_FEATURE_USERNAME_COMPLETION
+static void username_tab_completion(char *ud, char *with_shash_flg)
+{
+ struct passwd *entry;
+ int userlen;
+
+ ud++; /* ~user/... to user/... */
+ userlen = strlen(ud);
+
+ if (with_shash_flg) { /* "~/..." or "~user/..." */
+ char *sav_ud = ud - 1;
+ char *home = 0;
+ char *temp;
+
+ if (*ud == '/') { /* "~/..." */
+ home = home_pwd_buf;
+ } else {
+ /* "~user/..." */
+ temp = strchr(ud, '/');
+ *temp = 0; /* ~user\0 */
+ entry = getpwnam(ud);
+ *temp = '/'; /* restore ~user/... */
+ ud = temp;
+ if (entry)
+ home = entry->pw_dir;
+ }
+ if (home) {
+ if ((userlen + strlen(home) + 1) < BUFSIZ) {
+ char temp2[BUFSIZ]; /* argument size */
+
+ /* /home/user/... */
+ sprintf(temp2, "%s%s", home, ud);
+ strcpy(sav_ud, temp2);
+ }
+ }
+ } else {
+ /* "~[^/]*" */
+ /* Using _r function to avoid pulling in static buffers */
+ char line_buff[256];
+ struct passwd pwd;
+ struct passwd *result;
+
+ setpwent();
+ while (!getpwent_r(&pwd, line_buff, sizeof(line_buff), &result)) {
+ /* Null usernames should result in all users as possible completions. */
+ if (/*!userlen || */ strncmp(ud, pwd.pw_name, userlen) == 0) {
+ add_match(xasprintf("~%s/", pwd.pw_name));
+ }
+ }
+ endpwent();
+ }
+}
+#endif /* FEATURE_COMMAND_USERNAME_COMPLETION */
+
+enum {
+ FIND_EXE_ONLY = 0,
+ FIND_DIR_ONLY = 1,
+ FIND_FILE_ONLY = 2,
+};
+
+static int path_parse(char ***p, int flags)
+{
+ int npth;
+ const char *pth;
+ char *tmp;
+ char **res;
+
+ /* if not setenv PATH variable, to search cur dir "." */
+ if (flags != FIND_EXE_ONLY)
+ return 1;
+
+ if (state->flags & WITH_PATH_LOOKUP)
+ pth = state->path_lookup;
+ else
+ pth = getenv("PATH");
+ /* PATH=<empty> or PATH=:<empty> */
+ if (!pth || !pth[0] || LONE_CHAR(pth, ':'))
+ return 1;
+
+ tmp = (char*)pth;
+ npth = 1; /* path component count */
+ while (1) {
+ tmp = strchr(tmp, ':');
+ if (!tmp)
+ break;
+ if (*++tmp == '\0')
+ break; /* :<empty> */
+ npth++;
+ }
+
+ res = xmalloc(npth * sizeof(char*));
+ res[0] = tmp = xstrdup(pth);
+ npth = 1;
+ while (1) {
+ tmp = strchr(tmp, ':');
+ if (!tmp)
+ break;
+ *tmp++ = '\0'; /* ':' -> '\0' */
+ if (*tmp == '\0')
+ break; /* :<empty> */
+ res[npth++] = tmp;
+ }
+ *p = res;
+ return npth;
+}
+
+static void exe_n_cwd_tab_completion(char *command, int type)
+{
+ DIR *dir;
+ struct dirent *next;
+ char dirbuf[BUFSIZ];
+ struct stat st;
+ char *path1[1];
+ char **paths = path1;
+ int npaths;
+ int i;
+ char *found;
+ char *pfind = strrchr(command, '/');
+
+ npaths = 1;
+ path1[0] = (char*)".";
+
+ if (pfind == NULL) {
+ /* no dir, if flags==EXE_ONLY - get paths, else "." */
+ npaths = path_parse(&paths, type);
+ pfind = command;
+ } else {
+ /* dirbuf = ".../.../.../" */
+ safe_strncpy(dirbuf, command, (pfind - command) + 2);
+#if ENABLE_FEATURE_USERNAME_COMPLETION
+ if (dirbuf[0] == '~') /* ~/... or ~user/... */
+ username_tab_completion(dirbuf, dirbuf);
+#endif
+ paths[0] = dirbuf;
+ /* point to 'l' in "..../last_component" */
+ pfind++;
+ }
+
+ for (i = 0; i < npaths; i++) {
+ dir = opendir(paths[i]);
+ if (!dir) /* Don't print an error */
+ continue;
+
+ while ((next = readdir(dir)) != NULL) {
+ int len1;
+ const char *str_found = next->d_name;
+
+ /* matched? */
+ if (strncmp(str_found, pfind, strlen(pfind)))
+ continue;
+ /* not see .name without .match */
+ if (*str_found == '.' && *pfind == 0) {
+ if (NOT_LONE_CHAR(paths[i], '/') || str_found[1])
+ continue;
+ str_found = ""; /* only "/" */
+ }
+ found = concat_path_file(paths[i], str_found);
+ /* hmm, remover in progress? */
+ if (stat(found, &st) < 0)
+ goto cont;
+ /* find with dirs? */
+ if (paths[i] != dirbuf)
+ strcpy(found, next->d_name); /* only name */
+
+ len1 = strlen(found);
+ found = xrealloc(found, len1 + 2);
+ found[len1] = '\0';
+ found[len1+1] = '\0';
+
+ if (S_ISDIR(st.st_mode)) {
+ /* name is directory */
+ if (found[len1-1] != '/') {
+ found[len1] = '/';
+ }
+ } else {
+ /* not put found file if search only dirs for cd */
+ if (type == FIND_DIR_ONLY)
+ goto cont;
+ }
+ /* Add it to the list */
+ add_match(found);
+ continue;
+ cont:
+ free(found);
+ }
+ closedir(dir);
+ }
+ if (paths != path1) {
+ free(paths[0]); /* allocated memory only in first member */
+ free(paths);
+ }
+}
+
+#define QUOT (UCHAR_MAX+1)
+
+#define collapse_pos(is, in) { \
+ memmove(int_buf+(is), int_buf+(in), (BUFSIZ+1-(is)-(in))*sizeof(int)); \
+ memmove(pos_buf+(is), pos_buf+(in), (BUFSIZ+1-(is)-(in))*sizeof(int)); }
+
+static int find_match(char *matchBuf, int *len_with_quotes)
+{
+ int i, j;
+ int command_mode;
+ int c, c2;
+ int int_buf[BUFSIZ + 1];
+ int pos_buf[BUFSIZ + 1];
+
+ /* set to integer dimension characters and own positions */
+ for (i = 0;; i++) {
+ int_buf[i] = (unsigned char)matchBuf[i];
+ if (int_buf[i] == 0) {
+ pos_buf[i] = -1; /* indicator end line */
+ break;
+ }
+ pos_buf[i] = i;
+ }
+
+ /* mask \+symbol and convert '\t' to ' ' */
+ for (i = j = 0; matchBuf[i]; i++, j++)
+ if (matchBuf[i] == '\\') {
+ collapse_pos(j, j + 1);
+ int_buf[j] |= QUOT;
+ i++;
+#if ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT
+ if (matchBuf[i] == '\t') /* algorithm equivalent */
+ int_buf[j] = ' ' | QUOT;
+#endif
+ }
+#if ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT
+ else if (matchBuf[i] == '\t')
+ int_buf[j] = ' ';
+#endif
+
+ /* mask "symbols" or 'symbols' */
+ c2 = 0;
+ for (i = 0; int_buf[i]; i++) {
+ c = int_buf[i];
+ if (c == '\'' || c == '"') {
+ if (c2 == 0)
+ c2 = c;
+ else {
+ if (c == c2)
+ c2 = 0;
+ else
+ int_buf[i] |= QUOT;
+ }
+ } else if (c2 != 0 && c != '$')
+ int_buf[i] |= QUOT;
+ }
+
+ /* skip commands with arguments if line has commands delimiters */
+ /* ';' ';;' '&' '|' '&&' '||' but `>&' `<&' `>|' */
+ for (i = 0; int_buf[i]; i++) {
+ c = int_buf[i];
+ c2 = int_buf[i + 1];
+ j = i ? int_buf[i - 1] : -1;
+ command_mode = 0;
+ if (c == ';' || c == '&' || c == '|') {
+ command_mode = 1 + (c == c2);
+ if (c == '&') {
+ if (j == '>' || j == '<')
+ command_mode = 0;
+ } else if (c == '|' && j == '>')
+ command_mode = 0;
+ }
+ if (command_mode) {
+ collapse_pos(0, i + command_mode);
+ i = -1; /* hack incremet */
+ }
+ }
+ /* collapse `command...` */
+ for (i = 0; int_buf[i]; i++)
+ if (int_buf[i] == '`') {
+ for (j = i + 1; int_buf[j]; j++)
+ if (int_buf[j] == '`') {
+ collapse_pos(i, j + 1);
+ j = 0;
+ break;
+ }
+ if (j) {
+ /* not found close ` - command mode, collapse all previous */
+ collapse_pos(0, i + 1);
+ break;
+ } else
+ i--; /* hack incremet */
+ }
+
+ /* collapse (command...(command...)...) or {command...{command...}...} */
+ c = 0; /* "recursive" level */
+ c2 = 0;
+ for (i = 0; int_buf[i]; i++)
+ if (int_buf[i] == '(' || int_buf[i] == '{') {
+ if (int_buf[i] == '(')
+ c++;
+ else
+ c2++;
+ collapse_pos(0, i + 1);
+ i = -1; /* hack incremet */
+ }
+ for (i = 0; pos_buf[i] >= 0 && (c > 0 || c2 > 0); i++)
+ if ((int_buf[i] == ')' && c > 0) || (int_buf[i] == '}' && c2 > 0)) {
+ if (int_buf[i] == ')')
+ c--;
+ else
+ c2--;
+ collapse_pos(0, i + 1);
+ i = -1; /* hack incremet */
+ }
+
+ /* skip first not quote space */
+ for (i = 0; int_buf[i]; i++)
+ if (int_buf[i] != ' ')
+ break;
+ if (i)
+ collapse_pos(0, i);
+
+ /* set find mode for completion */
+ command_mode = FIND_EXE_ONLY;
+ for (i = 0; int_buf[i]; i++)
+ if (int_buf[i] == ' ' || int_buf[i] == '<' || int_buf[i] == '>') {
+ if (int_buf[i] == ' ' && command_mode == FIND_EXE_ONLY
+ && matchBuf[pos_buf[0]]=='c'
+ && matchBuf[pos_buf[1]]=='d'
+ ) {
+ command_mode = FIND_DIR_ONLY;
+ } else {
+ command_mode = FIND_FILE_ONLY;
+ break;
+ }
+ }
+ for (i = 0; int_buf[i]; i++)
+ /* "strlen" */;
+ /* find last word */
+ for (--i; i >= 0; i--) {
+ c = int_buf[i];
+ if (c == ' ' || c == '<' || c == '>' || c == '|' || c == '&') {
+ collapse_pos(0, i + 1);
+ break;
+ }
+ }
+ /* skip first not quoted '\'' or '"' */
+ for (i = 0; int_buf[i] == '\'' || int_buf[i] == '"'; i++)
+ /*skip*/;
+ /* collapse quote or unquote // or /~ */
+ while ((int_buf[i] & ~QUOT) == '/'
+ && ((int_buf[i+1] & ~QUOT) == '/' || (int_buf[i+1] & ~QUOT) == '~')
+ ) {
+ i++;
+ }
+
+ /* set only match and destroy quotes */
+ j = 0;
+ for (c = 0; pos_buf[i] >= 0; i++) {
+ matchBuf[c++] = matchBuf[pos_buf[i]];
+ j = pos_buf[i] + 1;
+ }
+ matchBuf[c] = 0;
+ /* old lenght matchBuf with quotes symbols */
+ *len_with_quotes = j ? j - pos_buf[0] : 0;
+
+ return command_mode;
+}
+
+/*
+ * display by column (original idea from ls applet,
+ * very optimized by me :)
+ */
+static void showfiles(void)
+{
+ int ncols, row;
+ int column_width = 0;
+ int nfiles = num_matches;
+ int nrows = nfiles;
+ int l;
+
+ /* find the longest file name- use that as the column width */
+ for (row = 0; row < nrows; row++) {
+ l = strlen(matches[row]);
+ if (column_width < l)
+ column_width = l;
+ }
+ column_width += 2; /* min space for columns */
+ ncols = cmdedit_termw / column_width;
+
+ if (ncols > 1) {
+ nrows /= ncols;
+ if (nfiles % ncols)
+ nrows++; /* round up fractionals */
+ } else {
+ ncols = 1;
+ }
+ for (row = 0; row < nrows; row++) {
+ int n = row;
+ int nc;
+
+ for (nc = 1; nc < ncols && n+nrows < nfiles; n += nrows, nc++) {
+ printf("%s%-*s", matches[n],
+ (int)(column_width - strlen(matches[n])), "");
+ }
+ printf("%s\n", matches[n]);
+ }
+}
+
+static char *add_quote_for_spec_chars(char *found)
+{
+ int l = 0;
+ char *s = xmalloc((strlen(found) + 1) * 2);
+
+ while (*found) {
+ if (strchr(" `\"#$%^&*()=+{}[]:;\'|\\<>", *found))
+ s[l++] = '\\';
+ s[l++] = *found++;
+ }
+ s[l] = 0;
+ return s;
+}
+
+static int match_compare(const void *a, const void *b)
+{
+ return strcmp(*(char**)a, *(char**)b);
+}
+
+/* Do TAB completion */
+static void input_tab(int *lastWasTab)
+{
+ if (!(state->flags & TAB_COMPLETION))
+ return;
+
+ if (!*lastWasTab) {
+ char *tmp, *tmp1;
+ int len_found;
+ char matchBuf[BUFSIZ];
+ int find_type;
+ int recalc_pos;
+
+ *lastWasTab = TRUE; /* flop trigger */
+
+ /* Make a local copy of the string -- up
+ * to the position of the cursor */
+ tmp = strncpy(matchBuf, command_ps, cursor);
+ tmp[cursor] = '\0';
+
+ find_type = find_match(matchBuf, &recalc_pos);
+
+ /* Free up any memory already allocated */
+ free_tab_completion_data();
+
+#if ENABLE_FEATURE_USERNAME_COMPLETION
+ /* If the word starts with `~' and there is no slash in the word,
+ * then try completing this word as a username. */
+ if (state->flags & USERNAME_COMPLETION)
+ if (matchBuf[0] == '~' && strchr(matchBuf, '/') == 0)
+ username_tab_completion(matchBuf, NULL);
+#endif
+ /* Try to match any executable in our path and everything
+ * in the current working directory */
+ if (!matches)
+ exe_n_cwd_tab_completion(matchBuf, find_type);
+ /* Sort, then remove any duplicates found */
+ if (matches) {
+ int i, n = 0;
+ qsort(matches, num_matches, sizeof(char*), match_compare);
+ for (i = 0; i < num_matches - 1; ++i) {
+ if (matches[i] && matches[i+1]) { /* paranoia */
+ if (strcmp(matches[i], matches[i+1]) == 0) {
+ free(matches[i]);
+ matches[i] = NULL; /* paranoia */
+ } else {
+ matches[n++] = matches[i];
+ }
+ }
+ }
+ matches[n] = matches[i];
+ num_matches = n + 1;
+ }
+ /* Did we find exactly one match? */
+ if (!matches || num_matches > 1) {
+ beep();
+ if (!matches)
+ return; /* not found */
+ /* find minimal match */
+ tmp1 = xstrdup(matches[0]);
+ for (tmp = tmp1; *tmp; tmp++)
+ for (len_found = 1; len_found < num_matches; len_found++)
+ if (matches[len_found][(tmp - tmp1)] != *tmp) {
+ *tmp = '\0';
+ break;
+ }
+ if (*tmp1 == '\0') { /* have unique */
+ free(tmp1);
+ return;
+ }
+ tmp = add_quote_for_spec_chars(tmp1);
+ free(tmp1);
+ } else { /* one match */
+ tmp = add_quote_for_spec_chars(matches[0]);
+ /* for next completion current found */
+ *lastWasTab = FALSE;
+
+ len_found = strlen(tmp);
+ if (tmp[len_found-1] != '/') {
+ tmp[len_found] = ' ';
+ tmp[len_found+1] = '\0';
+ }
+ }
+ len_found = strlen(tmp);
+ /* have space to placed match? */
+ if ((len_found - strlen(matchBuf) + command_len) < BUFSIZ) {
+ /* before word for match */
+ command_ps[cursor - recalc_pos] = 0;
+ /* save tail line */
+ strcpy(matchBuf, command_ps + cursor);
+ /* add match */
+ strcat(command_ps, tmp);
+ /* add tail */
+ strcat(command_ps, matchBuf);
+ /* back to begin word for match */
+ input_backward(recalc_pos);
+ /* new pos */
+ recalc_pos = cursor + len_found;
+ /* new len */
+ command_len = strlen(command_ps);
+ /* write out the matched command */
+ redraw(cmdedit_y, command_len - recalc_pos);
+ }
+ free(tmp);
+ } else {
+ /* Ok -- the last char was a TAB. Since they
+ * just hit TAB again, print a list of all the
+ * available choices... */
+ if (matches && num_matches > 0) {
+ int sav_cursor = cursor; /* change goto_new_line() */
+
+ /* Go to the next line */
+ goto_new_line();
+ showfiles();
+ redraw(0, command_len - sav_cursor);
+ }
+ }
+}
+
+#else
+#define input_tab(a) ((void)0)
+#endif /* FEATURE_COMMAND_TAB_COMPLETION */
+
+
+#if MAX_HISTORY > 0
+
+/* state->flags is already checked to be nonzero */
+static void get_previous_history(void)
+{
+ if (command_ps[0] != '\0' || state->history[state->cur_history] == NULL) {
+ free(state->history[state->cur_history]);
+ state->history[state->cur_history] = xstrdup(command_ps);
+ }
+ state->cur_history--;
+}
+
+static int get_next_history(void)
+{
+ if (state->flags & DO_HISTORY) {
+ int ch = state->cur_history;
+ if (ch < state->cnt_history) {
+ get_previous_history(); /* save the current history line */
+ state->cur_history = ch + 1;
+ return state->cur_history;
+ }
+ }
+ beep();
+ return 0;
+}
+
+#if ENABLE_FEATURE_EDITING_SAVEHISTORY
+/* state->flags is already checked to be nonzero */
+static void load_history(const char *fromfile)
+{
+ FILE *fp;
+ int hi;
+
+ /* cleanup old */
+ for (hi = state->cnt_history; hi > 0;) {
+ hi--;
+ free(state->history[hi]);
+ }
+
+ fp = fopen(fromfile, "r");
+ if (fp) {
+ for (hi = 0; hi < MAX_HISTORY;) {
+ char * hl = xmalloc_getline(fp);
+ int l;
+
+ if (!hl)
+ break;
+ l = strlen(hl);
+ if (l >= BUFSIZ)
+ hl[BUFSIZ-1] = 0;
+ if (l == 0 || hl[0] == ' ') {
+ free(hl);
+ continue;
+ }
+ state->history[hi++] = hl;
+ }
+ fclose(fp);
+ }
+ state->cur_history = state->cnt_history = hi;
+}
+
+/* state->flags is already checked to be nonzero */
+static void save_history(const char *tofile)
+{
+ FILE *fp;
+
+ fp = fopen(tofile, "w");
+ if (fp) {
+ int i;
+
+ for (i = 0; i < state->cnt_history; i++) {
+ fprintf(fp, "%s\n", state->history[i]);
+ }
+ fclose(fp);
+ }
+}
+#else
+#define load_history(a) ((void)0)
+#define save_history(a) ((void)0)
+#endif /* FEATURE_COMMAND_SAVEHISTORY */
+
+static void remember_in_history(const char *str)
+{
+ int i;
+
+ if (!(state->flags & DO_HISTORY))
+ return;
+
+ i = state->cnt_history;
+ free(state->history[MAX_HISTORY]);
+ state->history[MAX_HISTORY] = NULL;
+ /* After max history, remove the oldest command */
+ if (i >= MAX_HISTORY) {
+ free(state->history[0]);
+ for (i = 0; i < MAX_HISTORY-1; i++)
+ state->history[i] = state->history[i+1];
+ }
+// Maybe "if (!i || strcmp(history[i-1], command) != 0) ..."
+// (i.e. do not save dups?)
+ state->history[i++] = xstrdup(str);
+ state->cur_history = i;
+ state->cnt_history = i;
+ if (state->flags & SAVE_HISTORY)
+ save_history(state->hist_file);
+ USE_FEATURE_EDITING_FANCY_PROMPT(num_ok_lines++;)
+}
+
+#else /* MAX_HISTORY == 0 */
+#define remember_in_history(a) ((void)0)
+#endif /* MAX_HISTORY */
+
+
+/*
+ * This function is used to grab a character buffer
+ * from the input file descriptor and allows you to
+ * a string with full command editing (sort of like
+ * a mini readline).
+ *
+ * The following standard commands are not implemented:
+ * ESC-b -- Move back one word
+ * ESC-f -- Move forward one word
+ * ESC-d -- Delete back one word
+ * ESC-h -- Delete forward one word
+ * CTL-t -- Transpose two characters
+ *
+ * Minimalist vi-style command line editing available if configured.
+ * vi mode implemented 2005 by Paul Fox <pgf@foxharp.boston.ma.us>
+ */
+
+#if ENABLE_FEATURE_EDITING_VI
+static void
+vi_Word_motion(char *command, int eat)
+{
+ while (cursor < command_len && !isspace(command[cursor]))
+ input_forward();
+ if (eat) while (cursor < command_len && isspace(command[cursor]))
+ input_forward();
+}
+
+static void
+vi_word_motion(char *command, int eat)
+{
+ if (isalnum(command[cursor]) || command[cursor] == '_') {
+ while (cursor < command_len
+ && (isalnum(command[cursor+1]) || command[cursor+1] == '_'))
+ input_forward();
+ } else if (ispunct(command[cursor])) {
+ while (cursor < command_len && ispunct(command[cursor+1]))
+ input_forward();
+ }
+
+ if (cursor < command_len)
+ input_forward();
+
+ if (eat && cursor < command_len && isspace(command[cursor]))
+ while (cursor < command_len && isspace(command[cursor]))
+ input_forward();
+}
+
+static void
+vi_End_motion(char *command)
+{
+ input_forward();
+ while (cursor < command_len && isspace(command[cursor]))
+ input_forward();
+ while (cursor < command_len-1 && !isspace(command[cursor+1]))
+ input_forward();
+}
+
+static void
+vi_end_motion(char *command)
+{
+ if (cursor >= command_len-1)
+ return;
+ input_forward();
+ while (cursor < command_len-1 && isspace(command[cursor]))
+ input_forward();
+ if (cursor >= command_len-1)
+ return;
+ if (isalnum(command[cursor]) || command[cursor] == '_') {
+ while (cursor < command_len-1
+ && (isalnum(command[cursor+1]) || command[cursor+1] == '_')
+ ) {
+ input_forward();
+ }
+ } else if (ispunct(command[cursor])) {
+ while (cursor < command_len-1 && ispunct(command[cursor+1]))
+ input_forward();
+ }
+}
+
+static void
+vi_Back_motion(char *command)
+{
+ while (cursor > 0 && isspace(command[cursor-1]))
+ input_backward(1);
+ while (cursor > 0 && !isspace(command[cursor-1]))
+ input_backward(1);
+}
+
+static void
+vi_back_motion(char *command)
+{
+ if (cursor <= 0)
+ return;
+ input_backward(1);
+ while (cursor > 0 && isspace(command[cursor]))
+ input_backward(1);
+ if (cursor <= 0)
+ return;
+ if (isalnum(command[cursor]) || command[cursor] == '_') {
+ while (cursor > 0
+ && (isalnum(command[cursor-1]) || command[cursor-1] == '_')
+ ) {
+ input_backward(1);
+ }
+ } else if (ispunct(command[cursor])) {
+ while (cursor > 0 && ispunct(command[cursor-1]))
+ input_backward(1);
+ }
+}
+#endif
+
+
+/*
+ * read_line_input and its helpers
+ */
+
+#if !ENABLE_FEATURE_EDITING_FANCY_PROMPT
+static void parse_prompt(const char *prmt_ptr)
+{
+ cmdedit_prompt = prmt_ptr;
+ cmdedit_prmt_len = strlen(prmt_ptr);
+ put_prompt();
+}
+#else
+static void parse_prompt(const char *prmt_ptr)
+{
+ int prmt_len = 0;
+ size_t cur_prmt_len = 0;
+ char flg_not_length = '[';
+ char *prmt_mem_ptr = xzalloc(1);
+ char *pwd_buf = xrealloc_getcwd_or_warn(NULL);
+ char buf2[PATH_MAX + 1];
+ char buf[2];
+ char c;
+ char *pbuf;
+
+ cmdedit_prmt_len = 0;
+
+ if (!pwd_buf) {
+ pwd_buf = (char *)bb_msg_unknown;
+ }
+
+ while (*prmt_ptr) {
+ pbuf = buf;
+ pbuf[1] = 0;
+ c = *prmt_ptr++;
+ if (c == '\\') {
+ const char *cp = prmt_ptr;
+ int l;
+
+ c = bb_process_escape_sequence(&prmt_ptr);
+ if (prmt_ptr == cp) {
+ if (*cp == 0)
+ break;
+ c = *prmt_ptr++;
+ switch (c) {
+#if ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR
+ case 'u':
+ pbuf = user_buf;
+ break;
+#endif
+ case 'h':
+ pbuf = hostname_buf;
+ if (!pbuf) {
+ pbuf = xzalloc(256);
+ if (gethostname(pbuf, 255) < 0) {
+ strcpy(pbuf, "?");
+ } else {
+ char *s = strchr(pbuf, '.');
+ if (s)
+ *s = '\0';
+ }
+ hostname_buf = pbuf;
+ }
+ break;
+ case '$':
+ c = (geteuid() == 0 ? '#' : '$');
+ break;
+#if ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR
+ case 'w':
+ pbuf = pwd_buf;
+ l = strlen(home_pwd_buf);
+ if (home_pwd_buf[0] != 0
+ && strncmp(home_pwd_buf, pbuf, l) == 0
+ && (pbuf[l]=='/' || pbuf[l]=='\0')
+ && strlen(pwd_buf+l)<PATH_MAX
+ ) {
+ pbuf = buf2;
+ *pbuf = '~';
+ strcpy(pbuf+1, pwd_buf+l);
+ }
+ break;
+#endif
+ case 'W':
+ pbuf = pwd_buf;
+ cp = strrchr(pbuf,'/');
+ if (cp != NULL && cp != pbuf)
+ pbuf += (cp-pbuf) + 1;
+ break;
+ case '!':
+ pbuf = buf2;
+ snprintf(buf2, sizeof(buf2), "%d", num_ok_lines);
+ break;
+ case 'e': case 'E': /* \e \E = \033 */
+ c = '\033';
+ break;
+ case 'x': case 'X':
+ for (l = 0; l < 3;) {
+ int h;
+ buf2[l++] = *prmt_ptr;
+ buf2[l] = 0;
+ h = strtol(buf2, &pbuf, 16);
+ if (h > UCHAR_MAX || (pbuf - buf2) < l) {
+ l--;
+ break;
+ }
+ prmt_ptr++;
+ }
+ buf2[l] = 0;
+ c = (char)strtol(buf2, NULL, 16);
+ if (c == 0)
+ c = '?';
+ pbuf = buf;
+ break;
+ case '[': case ']':
+ if (c == flg_not_length) {
+ flg_not_length = flg_not_length == '[' ? ']' : '[';
+ continue;
+ }
+ break;
+ }
+ }
+ }
+ if (pbuf == buf)
+ *pbuf = c;
+ cur_prmt_len = strlen(pbuf);
+ prmt_len += cur_prmt_len;
+ if (flg_not_length != ']')
+ cmdedit_prmt_len += cur_prmt_len;
+ prmt_mem_ptr = strcat(xrealloc(prmt_mem_ptr, prmt_len+1), pbuf);
+ }
+ if (pwd_buf != (char *)bb_msg_unknown)
+ free(pwd_buf);
+ cmdedit_prompt = prmt_mem_ptr;
+ put_prompt();
+}
+#endif
+
+#define setTermSettings(fd, argp) tcsetattr(fd, TCSANOW, argp)
+#define getTermSettings(fd, argp) tcgetattr(fd, argp);
+
+static sighandler_t previous_SIGWINCH_handler;
+
+static void cmdedit_setwidth(unsigned w, int redraw_flg)
+{
+ cmdedit_termw = w;
+ if (redraw_flg) {
+ /* new y for current cursor */
+ int new_y = (cursor + cmdedit_prmt_len) / w;
+ /* redraw */
+ redraw((new_y >= cmdedit_y ? new_y : cmdedit_y), command_len - cursor);
+ fflush(stdout);
+ }
+}
+
+static void win_changed(int nsig)
+{
+ int width;
+ get_terminal_width_height(0, &width, NULL);
+ cmdedit_setwidth(width, nsig /* - just a yes/no flag */);
+ if (nsig == SIGWINCH)
+ signal(SIGWINCH, win_changed); /* rearm ourself */
+}
+
+/*
+ * The emacs and vi modes share much of the code in the big
+ * command loop. Commands entered when in vi's command mode (aka
+ * "escape mode") get an extra bit added to distinguish them --
+ * this keeps them from being self-inserted. This clutters the
+ * big switch a bit, but keeps all the code in one place.
+ */
+
+#define vbit 0x100
+
+/* leave out the "vi-mode"-only case labels if vi editing isn't
+ * configured. */
+#define vi_case(caselabel) USE_FEATURE_EDITING(case caselabel)
+
+/* convert uppercase ascii to equivalent control char, for readability */
+#undef CTRL
+#define CTRL(a) ((a) & ~0x40)
+
+int read_line_input(const char* prompt, char* command, int maxsize, line_input_t *st)
+{
+ int lastWasTab = FALSE;
+ unsigned int ic;
+ unsigned char c;
+ smallint break_out = 0;
+#if ENABLE_FEATURE_EDITING_VI
+ smallint vi_cmdmode = 0;
+ smalluint prevc;
+#endif
+
+// FIXME: audit & improve this
+ if (maxsize > BUFSIZ)
+ maxsize = BUFSIZ;
+
+ /* With null flags, no other fields are ever used */
+ state = st ? st : (line_input_t*) &const_int_0;
+#if ENABLE_FEATURE_EDITING_SAVEHISTORY
+ if (state->flags & SAVE_HISTORY)
+ load_history(state->hist_file);
+#endif
+
+ /* prepare before init handlers */
+ cmdedit_y = 0; /* quasireal y, not true if line > xt*yt */
+ command_len = 0;
+ command_ps = command;
+ command[0] = '\0';
+
+ getTermSettings(0, (void *) &initial_settings);
+ memcpy(&new_settings, &initial_settings, sizeof(new_settings));
+ new_settings.c_lflag &= ~ICANON; /* unbuffered input */
+ /* Turn off echoing and CTRL-C, so we can trap it */
+ new_settings.c_lflag &= ~(ECHO | ECHONL | ISIG);
+ /* Hmm, in linux c_cc[] is not parsed if ICANON is off */
+ new_settings.c_cc[VMIN] = 1;
+ new_settings.c_cc[VTIME] = 0;
+ /* Turn off CTRL-C, so we can trap it */
+#ifndef _POSIX_VDISABLE
+#define _POSIX_VDISABLE '\0'
+#endif
+ new_settings.c_cc[VINTR] = _POSIX_VDISABLE;
+ setTermSettings(0, (void *) &new_settings);
+
+ /* Now initialize things */
+ previous_SIGWINCH_handler = signal(SIGWINCH, win_changed);
+ win_changed(0); /* do initial resizing */
+#if ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR
+ {
+ struct passwd *entry;
+
+ entry = getpwuid(geteuid());
+ if (entry) {
+ user_buf = xstrdup(entry->pw_name);
+ home_pwd_buf = xstrdup(entry->pw_dir);
+ }
+ }
+#endif
+#if ENABLE_FEATURE_TAB_COMPLETION
+ my_uid = getuid();
+ my_gid = getgid();
+#endif
+ /* Print out the command prompt */
+ parse_prompt(prompt);
+
+ while (1) {
+ fflush(stdout);
+
+ if (safe_read(0, &c, 1) < 1) {
+ /* if we can't read input then exit */
+ goto prepare_to_die;
+ }
+
+ ic = c;
+
+#if ENABLE_FEATURE_EDITING_VI
+ newdelflag = 1;
+ if (vi_cmdmode)
+ ic |= vbit;
+#endif
+ switch (ic) {
+ case '\n':
+ case '\r':
+ vi_case('\n'|vbit:)
+ vi_case('\r'|vbit:)
+ /* Enter */
+ goto_new_line();
+ break_out = 1;
+ break;
+#if ENABLE_FEATURE_EDITING_FANCY_KEYS
+ case CTRL('A'):
+ vi_case('0'|vbit:)
+ /* Control-a -- Beginning of line */
+ input_backward(cursor);
+ break;
+ case CTRL('B'):
+ vi_case('h'|vbit:)
+ vi_case('\b'|vbit:)
+ vi_case('\x7f'|vbit:) /* DEL */
+ /* Control-b -- Move back one character */
+ input_backward(1);
+ break;
+#endif
+ case CTRL('C'):
+ vi_case(CTRL('C')|vbit:)
+ /* Control-c -- stop gathering input */
+ goto_new_line();
+ command_len = 0;
+ break_out = -1; /* "do not append '\n'" */
+ break;
+ case CTRL('D'):
+ /* Control-d -- Delete one character, or exit
+ * if the len=0 and no chars to delete */
+ if (command_len == 0) {
+ errno = 0;
+ prepare_to_die:
+ /* to control stopped jobs */
+ break_out = command_len = -1;
+ break;
+ }
+ input_delete(0);
+ break;
+
+#if ENABLE_FEATURE_EDITING_FANCY_KEYS
+ case CTRL('E'):
+ vi_case('$'|vbit:)
+ /* Control-e -- End of line */
+ input_end();
+ break;
+ case CTRL('F'):
+ vi_case('l'|vbit:)
+ vi_case(' '|vbit:)
+ /* Control-f -- Move forward one character */
+ input_forward();
+ break;
+#endif
+
+ case '\b':
+ case '\x7f': /* DEL */
+ /* Control-h and DEL */
+ input_backspace();
+ break;
+
+ case '\t':
+ input_tab(&lastWasTab);
+ break;
+
+#if ENABLE_FEATURE_EDITING_FANCY_KEYS
+ case CTRL('K'):
+ /* Control-k -- clear to end of line */
+ command[cursor] = 0;
+ command_len = cursor;
+ printf("\033[J");
+ break;
+ case CTRL('L'):
+ vi_case(CTRL('L')|vbit:)
+ /* Control-l -- clear screen */
+ printf("\033[H");
+ redraw(0, command_len - cursor);
+ break;
+#endif
+
+#if MAX_HISTORY > 0
+ case CTRL('N'):
+ vi_case(CTRL('N')|vbit:)
+ vi_case('j'|vbit:)
+ /* Control-n -- Get next command in history */
+ if (get_next_history())
+ goto rewrite_line;
+ break;
+ case CTRL('P'):
+ vi_case(CTRL('P')|vbit:)
+ vi_case('k'|vbit:)
+ /* Control-p -- Get previous command from history */
+ if ((state->flags & DO_HISTORY) && state->cur_history > 0) {
+ get_previous_history();
+ goto rewrite_line;
+ }
+ beep();
+ break;
+#endif
+
+#if ENABLE_FEATURE_EDITING_FANCY_KEYS
+ case CTRL('U'):
+ vi_case(CTRL('U')|vbit:)
+ /* Control-U -- Clear line before cursor */
+ if (cursor) {
+ strcpy(command, command + cursor);
+ command_len -= cursor;
+ redraw(cmdedit_y, command_len);
+ }
+ break;
+#endif
+ case CTRL('W'):
+ vi_case(CTRL('W')|vbit:)
+ /* Control-W -- Remove the last word */
+ while (cursor > 0 && isspace(command[cursor-1]))
+ input_backspace();
+ while (cursor > 0 && !isspace(command[cursor-1]))
+ input_backspace();
+ break;
+
+#if ENABLE_FEATURE_EDITING_VI
+ case 'i'|vbit:
+ vi_cmdmode = 0;
+ break;
+ case 'I'|vbit:
+ input_backward(cursor);
+ vi_cmdmode = 0;
+ break;
+ case 'a'|vbit:
+ input_forward();
+ vi_cmdmode = 0;
+ break;
+ case 'A'|vbit:
+ input_end();
+ vi_cmdmode = 0;
+ break;
+ case 'x'|vbit:
+ input_delete(1);
+ break;
+ case 'X'|vbit:
+ if (cursor > 0) {
+ input_backward(1);
+ input_delete(1);
+ }
+ break;
+ case 'W'|vbit:
+ vi_Word_motion(command, 1);
+ break;
+ case 'w'|vbit:
+ vi_word_motion(command, 1);
+ break;
+ case 'E'|vbit:
+ vi_End_motion(command);
+ break;
+ case 'e'|vbit:
+ vi_end_motion(command);
+ break;
+ case 'B'|vbit:
+ vi_Back_motion(command);
+ break;
+ case 'b'|vbit:
+ vi_back_motion(command);
+ break;
+ case 'C'|vbit:
+ vi_cmdmode = 0;
+ /* fall through */
+ case 'D'|vbit:
+ goto clear_to_eol;
+
+ case 'c'|vbit:
+ vi_cmdmode = 0;
+ /* fall through */
+ case 'd'|vbit: {
+ int nc, sc;
+ sc = cursor;
+ prevc = ic;
+ if (safe_read(0, &c, 1) < 1)
+ goto prepare_to_die;
+ if (c == (prevc & 0xff)) {
+ /* "cc", "dd" */
+ input_backward(cursor);
+ goto clear_to_eol;
+ break;
+ }
+ switch (c) {
+ case 'w':
+ case 'W':
+ case 'e':
+ case 'E':
+ switch (c) {
+ case 'w': /* "dw", "cw" */
+ vi_word_motion(command, vi_cmdmode);
+ break;
+ case 'W': /* 'dW', 'cW' */
+ vi_Word_motion(command, vi_cmdmode);
+ break;
+ case 'e': /* 'de', 'ce' */
+ vi_end_motion(command);
+ input_forward();
+ break;
+ case 'E': /* 'dE', 'cE' */
+ vi_End_motion(command);
+ input_forward();
+ break;
+ }
+ nc = cursor;
+ input_backward(cursor - sc);
+ while (nc-- > cursor)
+ input_delete(1);
+ break;
+ case 'b': /* "db", "cb" */
+ case 'B': /* implemented as B */
+ if (c == 'b')
+ vi_back_motion(command);
+ else
+ vi_Back_motion(command);
+ while (sc-- > cursor)
+ input_delete(1);
+ break;
+ case ' ': /* "d ", "c " */
+ input_delete(1);
+ break;
+ case '$': /* "d$", "c$" */
+ clear_to_eol:
+ while (cursor < command_len)
+ input_delete(1);
+ break;
+ }
+ break;
+ }
+ case 'p'|vbit:
+ input_forward();
+ /* fallthrough */
+ case 'P'|vbit:
+ put();
+ break;
+ case 'r'|vbit:
+ if (safe_read(0, &c, 1) < 1)
+ goto prepare_to_die;
+ if (c == 0)
+ beep();
+ else {
+ *(command + cursor) = c;
+ putchar(c);
+ putchar('\b');
+ }
+ break;
+#endif /* FEATURE_COMMAND_EDITING_VI */
+
+ case '\x1b': /* ESC */
+
+#if ENABLE_FEATURE_EDITING_VI
+ if (state->flags & VI_MODE) {
+ /* ESC: insert mode --> command mode */
+ vi_cmdmode = 1;
+ input_backward(1);
+ break;
+ }
+#endif
+ /* escape sequence follows */
+ if (safe_read(0, &c, 1) < 1)
+ goto prepare_to_die;
+ /* different vt100 emulations */
+ if (c == '[' || c == 'O') {
+ vi_case('['|vbit:)
+ vi_case('O'|vbit:)
+ if (safe_read(0, &c, 1) < 1)
+ goto prepare_to_die;
+ }
+ if (c >= '1' && c <= '9') {
+ unsigned char dummy;
+
+ if (safe_read(0, &dummy, 1) < 1)
+ goto prepare_to_die;
+ if (dummy != '~')
+ c = '\0';
+ }
+
+ switch (c) {
+#if ENABLE_FEATURE_TAB_COMPLETION
+ case '\t': /* Alt-Tab */
+ input_tab(&lastWasTab);
+ break;
+#endif
+#if MAX_HISTORY > 0
+ case 'A':
+ /* Up Arrow -- Get previous command from history */
+ if ((state->flags & DO_HISTORY) && state->cur_history > 0) {
+ get_previous_history();
+ goto rewrite_line;
+ }
+ beep();
+ break;
+ case 'B':
+ /* Down Arrow -- Get next command in history */
+ if (!get_next_history())
+ break;
+ rewrite_line:
+ /* Rewrite the line with the selected history item */
+ /* change command */
+ command_len = strlen(strcpy(command, state->history[state->cur_history]));
+ /* redraw and go to eol (bol, in vi */
+ redraw(cmdedit_y, (state->flags & VI_MODE) ? 9999 : 0);
+ break;
+#endif
+ case 'C':
+ /* Right Arrow -- Move forward one character */
+ input_forward();
+ break;
+ case 'D':
+ /* Left Arrow -- Move back one character */
+ input_backward(1);
+ break;
+ case '3':
+ /* Delete */
+ input_delete(0);
+ break;
+ case '1':
+ case 'H':
+ /* <Home> */
+ input_backward(cursor);
+ break;
+ case '4':
+ case 'F':
+ /* <End> */
+ input_end();
+ break;
+ default:
+ c = '\0';
+ beep();
+ }
+ break;
+
+ default: /* If it's regular input, do the normal thing */
+#if ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT
+ /* Control-V -- Add non-printable symbol */
+ if (c == CTRL('V')) {
+ if (safe_read(0, &c, 1) < 1)
+ goto prepare_to_die;
+ if (c == 0) {
+ beep();
+ break;
+ }
+ } else
+#endif
+
+#if ENABLE_FEATURE_EDITING_VI
+ if (vi_cmdmode) /* Don't self-insert */
+ break;
+#endif
+ if (!Isprint(c)) /* Skip non-printable characters */
+ break;
+
+ if (command_len >= (maxsize - 2)) /* Need to leave space for enter */
+ break;
+
+ command_len++;
+ if (cursor == (command_len - 1)) { /* Append if at the end of the line */
+ command[cursor] = c;
+ command[cursor+1] = '\0';
+ cmdedit_set_out_char(' ');
+ } else { /* Insert otherwise */
+ int sc = cursor;
+
+ memmove(command + sc + 1, command + sc, command_len - sc);
+ command[sc] = c;
+ sc++;
+ /* rewrite from cursor */
+ input_end();
+ /* to prev x pos + 1 */
+ input_backward(cursor - sc);
+ }
+ break;
+ }
+ if (break_out) /* Enter is the command terminator, no more input. */
+ break;
+
+ if (c != '\t')
+ lastWasTab = FALSE;
+ }
+
+ if (command_len > 0)
+ remember_in_history(command);
+
+ if (break_out > 0) {
+ command[command_len++] = '\n';
+ command[command_len] = '\0';
+ }
+
+#if ENABLE_FEATURE_CLEAN_UP && ENABLE_FEATURE_TAB_COMPLETION
+ free_tab_completion_data();
+#endif
+
+#if ENABLE_FEATURE_EDITING_FANCY_PROMPT
+ free((char*)cmdedit_prompt);
+#endif
+ /* restore initial_settings */
+ setTermSettings(STDIN_FILENO, (void *) &initial_settings);
+ /* restore SIGWINCH handler */
+ signal(SIGWINCH, previous_SIGWINCH_handler);
+ fflush(stdout);
+ return command_len;
+}
+
+line_input_t *new_line_input_t(int flags)
+{
+ line_input_t *n = xzalloc(sizeof(*n));
+ n->flags = flags;
+ return n;
+}
+
+#else
+
+#undef read_line_input
+int read_line_input(const char* prompt, char* command, int maxsize)
+{
+ fputs(prompt, stdout);
+ fflush(stdout);
+ fgets(command, maxsize, stdin);
+ return strlen(command);
+}
+
+#endif /* FEATURE_COMMAND_EDITING */
+
+
+/*
+ * Testing
+ */
+
+#ifdef TEST
+
+#include <locale.h>
+
+const char *applet_name = "debug stuff usage";
+
+int main(int argc, char **argv)
+{
+ char buff[BUFSIZ];
+ char *prompt =
+#if ENABLE_FEATURE_EDITING_FANCY_PROMPT
+ "\\[\\033[32;1m\\]\\u@\\[\\x1b[33;1m\\]\\h:"
+ "\\[\\033[34;1m\\]\\w\\[\\033[35;1m\\] "
+ "\\!\\[\\e[36;1m\\]\\$ \\[\\E[0m\\]";
+#else
+ "% ";
+#endif
+
+#if ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT
+ setlocale(LC_ALL, "");
+#endif
+ while (1) {
+ int l;
+ l = read_line_input(prompt, buff);
+ if (l <= 0 || buff[l-1] != '\n')
+ break;
+ buff[l-1] = 0;
+ printf("*** read_line_input() returned line =%s=\n", buff);
+ }
+ printf("*** read_line_input() detect ^D\n");
+ return 0;
+}
+
+#endif /* TEST */
diff --git a/i/pc104/initrd/conf/busybox/libbb/llist.c b/i/pc104/initrd/conf/busybox/libbb/llist.c
new file mode 100644
index 0000000..2b34f76
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/llist.c
@@ -0,0 +1,108 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * linked list helper functions.
+ *
+ * Copyright (C) 2003 Glenn McGrath
+ * Copyright (C) 2005 Vladimir Oleynik
+ * Copyright (C) 2005 Bernhard Fischer
+ * Copyright (C) 2006 Rob Landley <rob@landley.net>
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ */
+
+#include <stdlib.h>
+#include "libbb.h"
+
+/* Add data to the start of the linked list. */
+void llist_add_to(llist_t ** old_head, void *data)
+{
+ llist_t *new_head = xmalloc(sizeof(llist_t));
+
+ new_head->data = data;
+ new_head->link = *old_head;
+ *old_head = new_head;
+}
+
+/* Add data to the end of the linked list. */
+void llist_add_to_end(llist_t ** list_head, void *data)
+{
+ llist_t *new_item = xmalloc(sizeof(llist_t));
+
+ new_item->data = data;
+ new_item->link = NULL;
+
+ if (!*list_head)
+ *list_head = new_item;
+ else {
+ llist_t *tail = *list_head;
+
+ while (tail->link)
+ tail = tail->link;
+ tail->link = new_item;
+ }
+}
+
+/* Remove first element from the list and return it */
+void *llist_pop(llist_t ** head)
+{
+ void *data, *next;
+
+ if (!*head)
+ return NULL;
+
+ data = (*head)->data;
+ next = (*head)->link;
+ free(*head);
+ *head = next;
+
+ return data;
+}
+
+/* Unlink arbitrary given element from the list */
+void llist_unlink(llist_t **head, llist_t *elm)
+{
+ llist_t *crt;
+
+ if (!(elm && *head))
+ return;
+
+ if (elm == *head) {
+ *head = (*head)->link;
+ return;
+ }
+
+ for (crt = *head; crt; crt = crt->link) {
+ if (crt->link == elm) {
+ crt->link = elm->link;
+ return;
+ }
+ }
+}
+
+/* Recursively free all elements in the linked list. If freeit != NULL
+ * call it on each datum in the list */
+void llist_free(llist_t * elm, void (*freeit) (void *data))
+{
+ while (elm) {
+ void *data = llist_pop(&elm);
+
+ if (freeit)
+ freeit(data);
+ }
+}
+
+/* Reverse list order. Useful since getopt32 saves option params
+ * in reverse order */
+llist_t *llist_rev(llist_t * list)
+{
+ llist_t *new = NULL;
+
+ while (list) {
+ llist_t *next = list->link;
+
+ list->link = new;
+ new = list;
+ list = next;
+ }
+ return new;
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/login.c b/i/pc104/initrd/conf/busybox/libbb/login.c
new file mode 100644
index 0000000..6ebb9a6
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/login.c
@@ -0,0 +1,105 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * issue.c: issue printing code
+ *
+ * Copyright (C) 2003 Bastian Blank <waldi@tuxbox.org>
+ *
+ * Optimize and correcting OCRNL by Vladimir Oleynik <dzo@simtreas.ru>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include <sys/param.h> /* MAXHOSTNAMELEN */
+#include <stdio.h>
+#include <unistd.h>
+#include "libbb.h"
+
+#include <sys/utsname.h>
+#include <time.h>
+
+#define LOGIN " login: "
+
+static const char fmtstr_d[] = "%A, %d %B %Y";
+static const char fmtstr_t[] = "%H:%M:%S";
+
+void print_login_issue(const char *issue_file, const char *tty)
+{
+ FILE *fd;
+ int c;
+ char buf[256+1];
+ const char *outbuf;
+ time_t t;
+ struct utsname uts;
+
+ time(&t);
+ uname(&uts);
+
+ puts("\r"); /* start a new line */
+
+ fd = fopen(issue_file, "r");
+ if (!fd)
+ return;
+ while ((c = fgetc(fd)) != EOF) {
+ outbuf = buf;
+ buf[0] = c;
+ buf[1] = '\0';
+ if(c == '\n') {
+ buf[1] = '\r';
+ buf[2] = '\0';
+ }
+ if (c == '\\' || c == '%') {
+ c = fgetc(fd);
+ switch (c) {
+ case 's':
+ outbuf = uts.sysname;
+ break;
+ case 'n':
+ outbuf = uts.nodename;
+ break;
+ case 'r':
+ outbuf = uts.release;
+ break;
+ case 'v':
+ outbuf = uts.version;
+ break;
+ case 'm':
+ outbuf = uts.machine;
+ break;
+ case 'D':
+ case 'o':
+ c = getdomainname(buf, sizeof(buf) - 1);
+ buf[c >= 0 ? c : 0] = '\0';
+ break;
+ case 'd':
+ strftime(buf, sizeof(buf), fmtstr_d, localtime(&t));
+ break;
+ case 't':
+ strftime(buf, sizeof(buf), fmtstr_t, localtime(&t));
+ break;
+ case 'h':
+ gethostname(buf, sizeof(buf) - 1);
+ buf[sizeof(buf) - 1] = '\0';
+ break;
+ case 'l':
+ outbuf = tty;
+ break;
+ default:
+ buf[0] = c;
+ }
+ }
+ fputs(outbuf, stdout);
+ }
+ fclose(fd);
+ fflush(stdout);
+}
+
+void print_login_prompt(void)
+{
+ char buf[MAXHOSTNAMELEN+1];
+
+ if (gethostname(buf, MAXHOSTNAMELEN) == 0)
+ fputs(buf, stdout);
+
+ fputs(LOGIN, stdout);
+ fflush(stdout);
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/loop.c b/i/pc104/initrd/conf/busybox/libbb/loop.c
new file mode 100644
index 0000000..9818a30
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/loop.c
@@ -0,0 +1,149 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ * Copyright (C) 2005 by Rob Landley <rob@landley.net>
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ */
+
+#include "libbb.h"
+
+/* For 2.6, use the cleaned up header to get the 64 bit API. */
+#include <linux/version.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+#include <linux/loop.h>
+typedef struct loop_info64 bb_loop_info;
+#define BB_LOOP_SET_STATUS LOOP_SET_STATUS64
+#define BB_LOOP_GET_STATUS LOOP_GET_STATUS64
+
+/* For 2.4 and earlier, use the 32 bit API (and don't trust the headers) */
+#else
+/* Stuff stolen from linux/loop.h for 2.4 and earlier kernels*/
+#include <linux/posix_types.h>
+#define LO_NAME_SIZE 64
+#define LO_KEY_SIZE 32
+#define LOOP_SET_FD 0x4C00
+#define LOOP_CLR_FD 0x4C01
+#define BB_LOOP_SET_STATUS 0x4C02
+#define BB_LOOP_GET_STATUS 0x4C03
+typedef struct {
+ int lo_number;
+ __kernel_dev_t lo_device;
+ unsigned long lo_inode;
+ __kernel_dev_t lo_rdevice;
+ int lo_offset;
+ int lo_encrypt_type;
+ int lo_encrypt_key_size;
+ int lo_flags;
+ char lo_file_name[LO_NAME_SIZE];
+ unsigned char lo_encrypt_key[LO_KEY_SIZE];
+ unsigned long lo_init[2];
+ char reserved[4];
+} bb_loop_info;
+#endif
+
+char *query_loop(const char *device)
+{
+ int fd;
+ bb_loop_info loopinfo;
+ char *dev = 0;
+
+ fd = open(device, O_RDONLY);
+ if (fd < 0) return 0;
+ if (!ioctl(fd, BB_LOOP_GET_STATUS, &loopinfo))
+ dev = xasprintf("%ld %s", (long) loopinfo.lo_offset,
+ (char *)loopinfo.lo_file_name);
+ close(fd);
+
+ return dev;
+}
+
+
+int del_loop(const char *device)
+{
+ int fd, rc;
+
+ fd = open(device, O_RDONLY);
+ if (fd < 0) return 1;
+ rc = ioctl(fd, LOOP_CLR_FD, 0);
+ close(fd);
+
+ return rc;
+}
+
+/* Returns 0 if mounted RW, 1 if mounted read-only, <0 for error.
+ *device is loop device to use, or if *device==NULL finds a loop device to
+ mount it on and sets *device to a strdup of that loop device name. This
+ search will re-use an existing loop device already bound to that
+ file/offset if it finds one.
+ */
+int set_loop(char **device, const char *file, unsigned long long offset)
+{
+ char dev[20], *try;
+ bb_loop_info loopinfo;
+ struct stat statbuf;
+ int i, dfd, ffd, mode, rc=-1;
+
+ /* Open the file. Barf if this doesn't work. */
+ mode = O_RDWR;
+ ffd = open(file, mode);
+ if (ffd < 0) {
+ mode = O_RDONLY;
+ ffd = open(file, mode);
+ if (ffd < 0)
+ return -errno;
+ }
+
+ /* Find a loop device. */
+ try = *device ? : dev;
+ for (i=0;rc;i++) {
+ sprintf(dev, LOOP_FORMAT, i);
+
+ /* Ran out of block devices, return failure. */
+ if (stat(try, &statbuf) || !S_ISBLK(statbuf.st_mode)) {
+ rc=-ENOENT;
+ break;
+ }
+ /* Open the sucker and check its loopiness. */
+ dfd = open(try, mode);
+ if (dfd < 0 && errno == EROFS) {
+ mode = O_RDONLY;
+ dfd = open(try, mode);
+ }
+ if (dfd < 0) goto try_again;
+
+ rc = ioctl(dfd, BB_LOOP_GET_STATUS, &loopinfo);
+
+ /* If device free, claim it. */
+ if (rc && errno == ENXIO) {
+ memset(&loopinfo, 0, sizeof(loopinfo));
+ safe_strncpy((char *)loopinfo.lo_file_name, file, LO_NAME_SIZE);
+ loopinfo.lo_offset = offset;
+ /* Associate free loop device with file. */
+ if (!ioctl(dfd, LOOP_SET_FD, ffd)) {
+ if (!ioctl(dfd, BB_LOOP_SET_STATUS, &loopinfo)) rc = 0;
+ else ioctl(dfd, LOOP_CLR_FD, 0);
+ }
+
+ /* If this block device already set up right, re-use it.
+ (Yes this is racy, but associating two loop devices with the same
+ file isn't pretty either. In general, mounting the same file twice
+ without using losetup manually is problematic.)
+ */
+ } else if (strcmp(file,(char *)loopinfo.lo_file_name)
+ || offset != loopinfo.lo_offset) {
+ rc = -1;
+ }
+ close(dfd);
+try_again:
+ if (*device) break;
+ }
+ close(ffd);
+ if (!rc) {
+ if (!*device) *device = xstrdup(dev);
+ return mode==O_RDONLY ? 1 : 0;
+ }
+ return rc;
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/make_directory.c b/i/pc104/initrd/conf/busybox/libbb/make_directory.c
new file mode 100644
index 0000000..fbec4e2
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/make_directory.c
@@ -0,0 +1,104 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * parse_mode implementation for busybox
+ *
+ * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/* Mar 5, 2003 Manuel Novoa III
+ *
+ * This is the main work function for the 'mkdir' applet. As such, it
+ * strives to be SUSv3 compliant in it's behaviour when recursively
+ * making missing parent dirs, and in it's mode setting of the final
+ * directory 'path'.
+ *
+ * To recursively build all missing intermediate directories, make
+ * sure that (flags & FILEUTILS_RECUR) is non-zero. Newly created
+ * intermediate directories will have at least u+wx perms.
+ *
+ * To set specific permissions on 'path', pass the appropriate 'mode'
+ * val. Otherwise, pass -1 to get default permissions.
+ */
+
+#include <errno.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include "libbb.h"
+
+int bb_make_directory (char *path, long mode, int flags)
+{
+ mode_t mask;
+ const char *fail_msg;
+ char *s = path;
+ char c;
+ struct stat st;
+
+ mask = umask(0);
+ if (mode == -1) {
+ umask(mask);
+ mode = (S_IXUSR | S_IXGRP | S_IXOTH |
+ S_IWUSR | S_IWGRP | S_IWOTH |
+ S_IRUSR | S_IRGRP | S_IROTH) & ~mask;
+ } else {
+ umask(mask & ~0300);
+ }
+
+ do {
+ c = 0;
+
+ if (flags & FILEUTILS_RECUR) { /* Get the parent. */
+ /* Bypass leading non-'/'s and then subsequent '/'s. */
+ while (*s) {
+ if (*s == '/') {
+ do {
+ ++s;
+ } while (*s == '/');
+ c = *s; /* Save the current char */
+ *s = 0; /* and replace it with nul. */
+ break;
+ }
+ ++s;
+ }
+ }
+
+ if (mkdir(path, 0777) < 0) {
+ /* If we failed for any other reason than the directory
+ * already exists, output a diagnostic and return -1.*/
+ if (errno != EEXIST
+ || !(flags & FILEUTILS_RECUR)
+ || (stat(path, &st) < 0 || !S_ISDIR(st.st_mode))) {
+ fail_msg = "create";
+ umask(mask);
+ break;
+ }
+ /* Since the directory exists, don't attempt to change
+ * permissions if it was the full target. Note that
+ * this is not an error conditon. */
+ if (!c) {
+ umask(mask);
+ return 0;
+ }
+ }
+
+ if (!c) {
+ /* Done. If necessary, updated perms on the newly
+ * created directory. Failure to update here _is_
+ * an error.*/
+ umask(mask);
+ if ((mode != -1) && (chmod(path, mode) < 0)){
+ fail_msg = "set permissions of";
+ break;
+ }
+ return 0;
+ }
+
+ /* Remove any inserted nul from the path (recursive mode). */
+ *s = c;
+
+ } while (1);
+
+ bb_perror_msg ("cannot %s directory '%s'", fail_msg, path);
+ return -1;
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/makedev.c b/i/pc104/initrd/conf/busybox/libbb/makedev.c
new file mode 100644
index 0000000..81cd82b
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/makedev.c
@@ -0,0 +1,23 @@
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 2006 Denis Vlasenko
+ *
+ * Licensed under GPL version 2, see file LICENSE in this tarball for details.
+ */
+
+/* We do not include libbb.h - #define makedev() is there! */
+#include <features.h>
+#include <sys/sysmacros.h>
+
+#ifdef __GLIBC__
+/* At least glibc has horrendously large inline for this, so wrap it */
+/* uclibc people please check - do we need "&& !__UCLIBC__" above? */
+
+/* suppress gcc "no previous prototype" warning */
+unsigned long long bb_makedev(unsigned int major, unsigned int minor);
+unsigned long long bb_makedev(unsigned int major, unsigned int minor)
+{
+ return makedev(major, minor);
+}
+#endif
diff --git a/i/pc104/initrd/conf/busybox/libbb/md5.c b/i/pc104/initrd/conf/busybox/libbb/md5.c
new file mode 100644
index 0000000..e672559
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/md5.c
@@ -0,0 +1,450 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * md5.c - Compute MD5 checksum of strings according to the
+ * definition of MD5 in RFC 1321 from April 1992.
+ *
+ * Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995.
+ *
+ * Copyright (C) 1995-1999 Free Software Foundation, Inc.
+ * Copyright (C) 2001 Manuel Novoa III
+ * Copyright (C) 2003 Glenn L. McGrath
+ * Copyright (C) 2003 Erik Andersen
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ */
+
+#include "libbb.h"
+
+#if CONFIG_MD5_SIZE_VS_SPEED < 0 || CONFIG_MD5_SIZE_VS_SPEED > 3
+# define MD5_SIZE_VS_SPEED 2
+#else
+# define MD5_SIZE_VS_SPEED CONFIG_MD5_SIZE_VS_SPEED
+#endif
+
+/* Initialize structure containing state of computation.
+ * (RFC 1321, 3.3: Step 3)
+ */
+void md5_begin(md5_ctx_t *ctx)
+{
+ ctx->A = 0x67452301;
+ ctx->B = 0xefcdab89;
+ ctx->C = 0x98badcfe;
+ ctx->D = 0x10325476;
+
+ ctx->total = 0;
+ ctx->buflen = 0;
+}
+
+/* These are the four functions used in the four steps of the MD5 algorithm
+ * and defined in the RFC 1321. The first function is a little bit optimized
+ * (as found in Colin Plumbs public domain implementation).
+ * #define FF(b, c, d) ((b & c) | (~b & d))
+ */
+# define FF(b, c, d) (d ^ (b & (c ^ d)))
+# define FG(b, c, d) FF (d, b, c)
+# define FH(b, c, d) (b ^ c ^ d)
+# define FI(b, c, d) (c ^ (b | ~d))
+
+/* Hash a single block, 64 bytes long and 4-byte aligned. */
+static void md5_hash_block(const void *buffer, md5_ctx_t *ctx)
+{
+ uint32_t correct_words[16];
+ const uint32_t *words = buffer;
+
+# if MD5_SIZE_VS_SPEED > 0
+ static const uint32_t C_array[] = {
+ /* round 1 */
+ 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
+ 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
+ 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
+ 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
+ /* round 2 */
+ 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
+ 0xd62f105d, 0x2441453, 0xd8a1e681, 0xe7d3fbc8,
+ 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
+ 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
+ /* round 3 */
+ 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
+ 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
+ 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x4881d05,
+ 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
+ /* round 4 */
+ 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
+ 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
+ 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
+ 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391
+ };
+
+ static const char P_array[] = {
+# if MD5_SIZE_VS_SPEED > 1
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, /* 1 */
+# endif /* MD5_SIZE_VS_SPEED > 1 */
+ 1, 6, 11, 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, /* 2 */
+ 5, 8, 11, 14, 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, 2, /* 3 */
+ 0, 7, 14, 5, 12, 3, 10, 1, 8, 15, 6, 13, 4, 11, 2, 9 /* 4 */
+ };
+
+# if MD5_SIZE_VS_SPEED > 1
+ static const char S_array[] = {
+ 7, 12, 17, 22,
+ 5, 9, 14, 20,
+ 4, 11, 16, 23,
+ 6, 10, 15, 21
+ };
+# endif /* MD5_SIZE_VS_SPEED > 1 */
+# endif
+
+ uint32_t A = ctx->A;
+ uint32_t B = ctx->B;
+ uint32_t C = ctx->C;
+ uint32_t D = ctx->D;
+
+ /* Process all bytes in the buffer with 64 bytes in each round of
+ the loop. */
+ uint32_t *cwp = correct_words;
+ uint32_t A_save = A;
+ uint32_t B_save = B;
+ uint32_t C_save = C;
+ uint32_t D_save = D;
+
+# if MD5_SIZE_VS_SPEED > 1
+# define CYCLIC(w, s) (w = (w << s) | (w >> (32 - s)))
+
+ const uint32_t *pc;
+ const char *pp;
+ const char *ps;
+ int i;
+ uint32_t temp;
+
+ for (i = 0; i < 16; i++) {
+ cwp[i] = SWAP_LE32(words[i]);
+ }
+ words += 16;
+
+# if MD5_SIZE_VS_SPEED > 2
+ pc = C_array;
+ pp = P_array;
+ ps = S_array - 4;
+
+ for (i = 0; i < 64; i++) {
+ if ((i & 0x0f) == 0)
+ ps += 4;
+ temp = A;
+ switch (i >> 4) {
+ case 0:
+ temp += FF(B, C, D);
+ break;
+ case 1:
+ temp += FG(B, C, D);
+ break;
+ case 2:
+ temp += FH(B, C, D);
+ break;
+ case 3:
+ temp += FI(B, C, D);
+ }
+ temp += cwp[(int) (*pp++)] + *pc++;
+ CYCLIC(temp, ps[i & 3]);
+ temp += B;
+ A = D;
+ D = C;
+ C = B;
+ B = temp;
+ }
+# else
+ pc = C_array;
+ pp = P_array;
+ ps = S_array;
+
+ for (i = 0; i < 16; i++) {
+ temp = A + FF(B, C, D) + cwp[(int) (*pp++)] + *pc++;
+ CYCLIC(temp, ps[i & 3]);
+ temp += B;
+ A = D;
+ D = C;
+ C = B;
+ B = temp;
+ }
+
+ ps += 4;
+ for (i = 0; i < 16; i++) {
+ temp = A + FG(B, C, D) + cwp[(int) (*pp++)] + *pc++;
+ CYCLIC(temp, ps[i & 3]);
+ temp += B;
+ A = D;
+ D = C;
+ C = B;
+ B = temp;
+ }
+ ps += 4;
+ for (i = 0; i < 16; i++) {
+ temp = A + FH(B, C, D) + cwp[(int) (*pp++)] + *pc++;
+ CYCLIC(temp, ps[i & 3]);
+ temp += B;
+ A = D;
+ D = C;
+ C = B;
+ B = temp;
+ }
+ ps += 4;
+ for (i = 0; i < 16; i++) {
+ temp = A + FI(B, C, D) + cwp[(int) (*pp++)] + *pc++;
+ CYCLIC(temp, ps[i & 3]);
+ temp += B;
+ A = D;
+ D = C;
+ C = B;
+ B = temp;
+ }
+
+# endif /* MD5_SIZE_VS_SPEED > 2 */
+# else
+ /* First round: using the given function, the context and a constant
+ the next context is computed. Because the algorithms processing
+ unit is a 32-bit word and it is determined to work on words in
+ little endian byte order we perhaps have to change the byte order
+ before the computation. To reduce the work for the next steps
+ we store the swapped words in the array CORRECT_WORDS. */
+
+# define OP(a, b, c, d, s, T) \
+ do \
+ { \
+ a += FF (b, c, d) + (*cwp++ = SWAP_LE32(*words)) + T; \
+ ++words; \
+ CYCLIC (a, s); \
+ a += b; \
+ } \
+ while (0)
+
+ /* It is unfortunate that C does not provide an operator for
+ cyclic rotation. Hope the C compiler is smart enough. */
+ /* gcc 2.95.4 seems to be --aaronl */
+# define CYCLIC(w, s) (w = (w << s) | (w >> (32 - s)))
+
+ /* Before we start, one word to the strange constants.
+ They are defined in RFC 1321 as
+
+ T[i] = (int) (4294967296.0 * fabs (sin (i))), i=1..64
+ */
+
+# if MD5_SIZE_VS_SPEED == 1
+ const uint32_t *pc;
+ const char *pp;
+ int i;
+# endif /* MD5_SIZE_VS_SPEED */
+
+ /* Round 1. */
+# if MD5_SIZE_VS_SPEED == 1
+ pc = C_array;
+ for (i = 0; i < 4; i++) {
+ OP(A, B, C, D, 7, *pc++);
+ OP(D, A, B, C, 12, *pc++);
+ OP(C, D, A, B, 17, *pc++);
+ OP(B, C, D, A, 22, *pc++);
+ }
+# else
+ OP(A, B, C, D, 7, 0xd76aa478);
+ OP(D, A, B, C, 12, 0xe8c7b756);
+ OP(C, D, A, B, 17, 0x242070db);
+ OP(B, C, D, A, 22, 0xc1bdceee);
+ OP(A, B, C, D, 7, 0xf57c0faf);
+ OP(D, A, B, C, 12, 0x4787c62a);
+ OP(C, D, A, B, 17, 0xa8304613);
+ OP(B, C, D, A, 22, 0xfd469501);
+ OP(A, B, C, D, 7, 0x698098d8);
+ OP(D, A, B, C, 12, 0x8b44f7af);
+ OP(C, D, A, B, 17, 0xffff5bb1);
+ OP(B, C, D, A, 22, 0x895cd7be);
+ OP(A, B, C, D, 7, 0x6b901122);
+ OP(D, A, B, C, 12, 0xfd987193);
+ OP(C, D, A, B, 17, 0xa679438e);
+ OP(B, C, D, A, 22, 0x49b40821);
+# endif /* MD5_SIZE_VS_SPEED == 1 */
+
+ /* For the second to fourth round we have the possibly swapped words
+ in CORRECT_WORDS. Redefine the macro to take an additional first
+ argument specifying the function to use. */
+# undef OP
+# define OP(f, a, b, c, d, k, s, T) \
+ do \
+ { \
+ a += f (b, c, d) + correct_words[k] + T; \
+ CYCLIC (a, s); \
+ a += b; \
+ } \
+ while (0)
+
+ /* Round 2. */
+# if MD5_SIZE_VS_SPEED == 1
+ pp = P_array;
+ for (i = 0; i < 4; i++) {
+ OP(FG, A, B, C, D, (int) (*pp++), 5, *pc++);
+ OP(FG, D, A, B, C, (int) (*pp++), 9, *pc++);
+ OP(FG, C, D, A, B, (int) (*pp++), 14, *pc++);
+ OP(FG, B, C, D, A, (int) (*pp++), 20, *pc++);
+ }
+# else
+ OP(FG, A, B, C, D, 1, 5, 0xf61e2562);
+ OP(FG, D, A, B, C, 6, 9, 0xc040b340);
+ OP(FG, C, D, A, B, 11, 14, 0x265e5a51);
+ OP(FG, B, C, D, A, 0, 20, 0xe9b6c7aa);
+ OP(FG, A, B, C, D, 5, 5, 0xd62f105d);
+ OP(FG, D, A, B, C, 10, 9, 0x02441453);
+ OP(FG, C, D, A, B, 15, 14, 0xd8a1e681);
+ OP(FG, B, C, D, A, 4, 20, 0xe7d3fbc8);
+ OP(FG, A, B, C, D, 9, 5, 0x21e1cde6);
+ OP(FG, D, A, B, C, 14, 9, 0xc33707d6);
+ OP(FG, C, D, A, B, 3, 14, 0xf4d50d87);
+ OP(FG, B, C, D, A, 8, 20, 0x455a14ed);
+ OP(FG, A, B, C, D, 13, 5, 0xa9e3e905);
+ OP(FG, D, A, B, C, 2, 9, 0xfcefa3f8);
+ OP(FG, C, D, A, B, 7, 14, 0x676f02d9);
+ OP(FG, B, C, D, A, 12, 20, 0x8d2a4c8a);
+# endif /* MD5_SIZE_VS_SPEED == 1 */
+
+ /* Round 3. */
+# if MD5_SIZE_VS_SPEED == 1
+ for (i = 0; i < 4; i++) {
+ OP(FH, A, B, C, D, (int) (*pp++), 4, *pc++);
+ OP(FH, D, A, B, C, (int) (*pp++), 11, *pc++);
+ OP(FH, C, D, A, B, (int) (*pp++), 16, *pc++);
+ OP(FH, B, C, D, A, (int) (*pp++), 23, *pc++);
+ }
+# else
+ OP(FH, A, B, C, D, 5, 4, 0xfffa3942);
+ OP(FH, D, A, B, C, 8, 11, 0x8771f681);
+ OP(FH, C, D, A, B, 11, 16, 0x6d9d6122);
+ OP(FH, B, C, D, A, 14, 23, 0xfde5380c);
+ OP(FH, A, B, C, D, 1, 4, 0xa4beea44);
+ OP(FH, D, A, B, C, 4, 11, 0x4bdecfa9);
+ OP(FH, C, D, A, B, 7, 16, 0xf6bb4b60);
+ OP(FH, B, C, D, A, 10, 23, 0xbebfbc70);
+ OP(FH, A, B, C, D, 13, 4, 0x289b7ec6);
+ OP(FH, D, A, B, C, 0, 11, 0xeaa127fa);
+ OP(FH, C, D, A, B, 3, 16, 0xd4ef3085);
+ OP(FH, B, C, D, A, 6, 23, 0x04881d05);
+ OP(FH, A, B, C, D, 9, 4, 0xd9d4d039);
+ OP(FH, D, A, B, C, 12, 11, 0xe6db99e5);
+ OP(FH, C, D, A, B, 15, 16, 0x1fa27cf8);
+ OP(FH, B, C, D, A, 2, 23, 0xc4ac5665);
+# endif /* MD5_SIZE_VS_SPEED == 1 */
+
+ /* Round 4. */
+# if MD5_SIZE_VS_SPEED == 1
+ for (i = 0; i < 4; i++) {
+ OP(FI, A, B, C, D, (int) (*pp++), 6, *pc++);
+ OP(FI, D, A, B, C, (int) (*pp++), 10, *pc++);
+ OP(FI, C, D, A, B, (int) (*pp++), 15, *pc++);
+ OP(FI, B, C, D, A, (int) (*pp++), 21, *pc++);
+ }
+# else
+ OP(FI, A, B, C, D, 0, 6, 0xf4292244);
+ OP(FI, D, A, B, C, 7, 10, 0x432aff97);
+ OP(FI, C, D, A, B, 14, 15, 0xab9423a7);
+ OP(FI, B, C, D, A, 5, 21, 0xfc93a039);
+ OP(FI, A, B, C, D, 12, 6, 0x655b59c3);
+ OP(FI, D, A, B, C, 3, 10, 0x8f0ccc92);
+ OP(FI, C, D, A, B, 10, 15, 0xffeff47d);
+ OP(FI, B, C, D, A, 1, 21, 0x85845dd1);
+ OP(FI, A, B, C, D, 8, 6, 0x6fa87e4f);
+ OP(FI, D, A, B, C, 15, 10, 0xfe2ce6e0);
+ OP(FI, C, D, A, B, 6, 15, 0xa3014314);
+ OP(FI, B, C, D, A, 13, 21, 0x4e0811a1);
+ OP(FI, A, B, C, D, 4, 6, 0xf7537e82);
+ OP(FI, D, A, B, C, 11, 10, 0xbd3af235);
+ OP(FI, C, D, A, B, 2, 15, 0x2ad7d2bb);
+ OP(FI, B, C, D, A, 9, 21, 0xeb86d391);
+# endif /* MD5_SIZE_VS_SPEED == 1 */
+# endif /* MD5_SIZE_VS_SPEED > 1 */
+
+ /* Add the starting values of the context. */
+ A += A_save;
+ B += B_save;
+ C += C_save;
+ D += D_save;
+
+ /* Put checksum in context given as argument. */
+ ctx->A = A;
+ ctx->B = B;
+ ctx->C = C;
+ ctx->D = D;
+}
+
+/* Feed data through a temporary buffer to call md5_hash_aligned_block()
+ * with chunks of data that are 4-byte aligned and a multiple of 64 bytes.
+ * This function's internal buffer remembers previous data until it has 64
+ * bytes worth to pass on. Call md5_end() to flush this buffer. */
+
+void md5_hash(const void *buffer, size_t len, md5_ctx_t *ctx)
+{
+ char *buf=(char *)buffer;
+
+ /* RFC 1321 specifies the possible length of the file up to 2^64 bits,
+ * Here we only track the number of bytes. */
+
+ ctx->total += len;
+
+ // Process all input.
+
+ while (len) {
+ int i = 64 - ctx->buflen;
+
+ // Copy data into aligned buffer.
+
+ if (i > len) i = len;
+ memcpy(ctx->buffer + ctx->buflen, buf, i);
+ len -= i;
+ ctx->buflen += i;
+ buf += i;
+
+ // When buffer fills up, process it.
+
+ if (ctx->buflen == 64) {
+ md5_hash_block(ctx->buffer, ctx);
+ ctx->buflen = 0;
+ }
+ }
+}
+
+/* Process the remaining bytes in the buffer and put result from CTX
+ * in first 16 bytes following RESBUF. The result is always in little
+ * endian byte order, so that a byte-wise output yields to the wanted
+ * ASCII representation of the message digest.
+ *
+ * IMPORTANT: On some systems it is required that RESBUF is correctly
+ * aligned for a 32 bits value.
+ */
+void *md5_end(void *resbuf, md5_ctx_t *ctx)
+{
+ char *buf = ctx->buffer;
+ int i;
+
+ /* Pad data to block size. */
+
+ buf[ctx->buflen++] = 0x80;
+ memset(buf + ctx->buflen, 0, 128 - ctx->buflen);
+
+ /* Put the 64-bit file length in *bits* at the end of the buffer. */
+ ctx->total <<= 3;
+ if (ctx->buflen > 56) buf += 64;
+ for (i = 0; i < 8; i++) buf[56 + i] = ctx->total >> (i*8);
+
+ /* Process last bytes. */
+ if (buf != ctx->buffer) md5_hash_block(ctx->buffer, ctx);
+ md5_hash_block(buf, ctx);
+
+ /* Put result from CTX in first 16 bytes following RESBUF. The result is
+ * always in little endian byte order, so that a byte-wise output yields
+ * to the wanted ASCII representation of the message digest.
+ *
+ * IMPORTANT: On some systems it is required that RESBUF is correctly
+ * aligned for a 32 bits value.
+ */
+ ((uint32_t *) resbuf)[0] = SWAP_LE32(ctx->A);
+ ((uint32_t *) resbuf)[1] = SWAP_LE32(ctx->B);
+ ((uint32_t *) resbuf)[2] = SWAP_LE32(ctx->C);
+ ((uint32_t *) resbuf)[3] = SWAP_LE32(ctx->D);
+
+ return resbuf;
+}
+
diff --git a/i/pc104/initrd/conf/busybox/libbb/messages.c b/i/pc104/initrd/conf/busybox/libbb/messages.c
new file mode 100644
index 0000000..12a165a
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/messages.c
@@ -0,0 +1,61 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "libbb.h"
+
+#ifndef BB_EXTRA_VERSION
+#define BANNER "BusyBox v" BB_VER " (" BB_BT ")"
+#else
+#define BANNER "BusyBox v" BB_VER " (" BB_EXTRA_VERSION ")"
+#endif
+const char BB_BANNER[] = BANNER;
+const char bb_msg_full_version[] = BANNER " multi-call binary";
+
+const char bb_msg_memory_exhausted[] = "memory exhausted";
+const char bb_msg_invalid_date[] = "invalid date '%s'";
+const char bb_msg_write_error[] = "write error";
+const char bb_msg_read_error[] = "read error";
+const char bb_msg_unknown[] = "(unknown)";
+const char bb_msg_can_not_create_raw_socket[] = "can't create raw socket";
+const char bb_msg_perm_denied_are_you_root[] = "permission denied. (are you root?)";
+const char bb_msg_requires_arg[] = "%s requires an argument";
+const char bb_msg_invalid_arg[] = "invalid argument '%s' to '%s'";
+const char bb_msg_standard_input[] = "standard input";
+const char bb_msg_standard_output[] = "standard output";
+
+const char bb_str_default[] = "default";
+const char bb_hexdigits_upcase[] = "0123456789ABCDEF";
+
+const char bb_path_passwd_file[] = "/etc/passwd";
+const char bb_path_shadow_file[] = "/etc/shadow";
+const char bb_path_group_file[] = "/etc/group";
+const char bb_path_gshadow_file[] = "/etc/gshadow";
+const char bb_path_nologin_file[] = "/etc/nologin";
+const char bb_path_securetty_file[] = "/etc/securetty";
+const char bb_path_motd_file[] = "/etc/motd";
+const char bb_default_login_shell[] = LIBBB_DEFAULT_LOGIN_SHELL;
+const char bb_dev_null[] = "/dev/null";
+
+const int const_int_0;
+const int const_int_1 = 1;
+
+#include <utmp.h>
+/* This is usually something like "/var/adm/wtmp" or "/var/log/wtmp" */
+const char bb_path_wtmp_file[] =
+#if defined _PATH_WTMP
+_PATH_WTMP;
+#elif defined WTMP_FILE
+WTMP_FILE;
+#else
+# error unknown path to wtmp file
+#endif
+
+char bb_common_bufsiz1[BUFSIZ+1];
+
+struct globals;
+/* Make it reside in R/W memory: */
+struct globals *const ptr_to_globals __attribute__ ((section (".data")));
diff --git a/i/pc104/initrd/conf/busybox/libbb/mode_string.c b/i/pc104/initrd/conf/busybox/libbb/mode_string.c
new file mode 100644
index 0000000..d3ff183
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/mode_string.c
@@ -0,0 +1,128 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * mode_string implementation for busybox
+ *
+ * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/* Aug 13, 2003
+ * Fix a bug reported by junkio@cox.net involving the mode_chars index.
+ */
+
+
+#include <assert.h>
+#include <sys/stat.h>
+
+#include "libbb.h"
+
+#if ( S_ISUID != 04000 ) || ( S_ISGID != 02000 ) || ( S_ISVTX != 01000 ) \
+ || ( S_IRUSR != 00400 ) || ( S_IWUSR != 00200 ) || ( S_IXUSR != 00100 ) \
+ || ( S_IRGRP != 00040 ) || ( S_IWGRP != 00020 ) || ( S_IXGRP != 00010 ) \
+ || ( S_IROTH != 00004 ) || ( S_IWOTH != 00002 ) || ( S_IXOTH != 00001 )
+#error permission bitflag value assumption(s) violated!
+#endif
+
+#if ( S_IFSOCK!= 0140000 ) || ( S_IFLNK != 0120000 ) \
+ || ( S_IFREG != 0100000 ) || ( S_IFBLK != 0060000 ) \
+ || ( S_IFDIR != 0040000 ) || ( S_IFCHR != 0020000 ) \
+ || ( S_IFIFO != 0010000 )
+#warning mode type bitflag value assumption(s) violated! falling back to larger version
+
+#if (S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID | S_ISVTX) == 07777
+#undef mode_t
+#define mode_t unsigned short
+#endif
+
+static const mode_t mode_flags[] = {
+ S_IRUSR, S_IWUSR, S_IXUSR, S_ISUID,
+ S_IRGRP, S_IWGRP, S_IXGRP, S_ISGID,
+ S_IROTH, S_IWOTH, S_IXOTH, S_ISVTX
+};
+
+/* The static const char arrays below are duplicated for the two cases
+ * because moving them ahead of the mode_flags declaration cause a text
+ * size increase with the gcc version I'm using. */
+
+/* The previous version used "0pcCd?bB-?l?s???". However, the '0', 'C',
+ * and 'B' types don't appear to be available on linux. So I removed them. */
+static const char type_chars[16] = "?pc?d?b?-?l?s???";
+/* 0123456789abcdef */
+static const char mode_chars[7] = "rwxSTst";
+
+const char *bb_mode_string(mode_t mode)
+{
+ static char buf[12];
+ char *p = buf;
+
+ int i, j, k;
+
+ *p = type_chars[ (mode >> 12) & 0xf ];
+ i = 0;
+ do {
+ j = k = 0;
+ do {
+ *++p = '-';
+ if (mode & mode_flags[i+j]) {
+ *p = mode_chars[j];
+ k = j;
+ }
+ } while (++j < 3);
+ if (mode & mode_flags[i+j]) {
+ *p = mode_chars[3 + (k & 2) + ((i&8) >> 3)];
+ }
+ i += 4;
+ } while (i < 12);
+
+ /* Note: We don't bother with nul termination because bss initialization
+ * should have taken care of that for us. If the user scribbled in buf
+ * memory, they deserve whatever happens. But we'll at least assert. */
+ assert(buf[10] == 0);
+
+ return buf;
+}
+
+#else
+
+/* The previous version used "0pcCd?bB-?l?s???". However, the '0', 'C',
+ * and 'B' types don't appear to be available on linux. So I removed them. */
+static const char type_chars[16] = "?pc?d?b?-?l?s???";
+/* 0123456789abcdef */
+static const char mode_chars[7] = "rwxSTst";
+
+const char *bb_mode_string(mode_t mode)
+{
+ static char buf[12];
+ char *p = buf;
+
+ int i, j, k, m;
+
+ *p = type_chars[ (mode >> 12) & 0xf ];
+ i = 0;
+ m = 0400;
+ do {
+ j = k = 0;
+ do {
+ *++p = '-';
+ if (mode & m) {
+ *p = mode_chars[j];
+ k = j;
+ }
+ m >>= 1;
+ } while (++j < 3);
+ ++i;
+ if (mode & (010000 >> i)) {
+ *p = mode_chars[3 + (k & 2) + (i == 3)];
+ }
+ } while (i < 3);
+
+ /* Note: We don't bother with nul termination because bss initialization
+ * should have taken care of that for us. If the user scribbled in buf
+ * memory, they deserve whatever happens. But we'll at least assert. */
+ assert(buf[10] == 0);
+
+ return buf;
+}
+
+#endif
diff --git a/i/pc104/initrd/conf/busybox/libbb/mtab.c b/i/pc104/initrd/conf/busybox/libbb/mtab.c
new file mode 100644
index 0000000..18386ef
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/mtab.c
@@ -0,0 +1,52 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include <mntent.h>
+#include "libbb.h"
+
+#if ENABLE_FEATURE_MTAB_SUPPORT
+void erase_mtab(const char *name)
+{
+ struct mntent *entries = NULL;
+ int i, count = 0;
+ FILE *mountTable;
+ struct mntent *m;
+
+ mountTable = setmntent(bb_path_mtab_file, "r");
+ /* Bummer. Fall back on trying the /proc filesystem */
+ if (!mountTable) mountTable = setmntent("/proc/mounts", "r");
+ if (!mountTable) {
+ bb_perror_msg(bb_path_mtab_file);
+ return;
+ }
+
+ while ((m = getmntent(mountTable)) != 0) {
+ i = count++;
+ entries = xrealloc(entries, count * sizeof(entries[0]));
+ entries[i].mnt_fsname = xstrdup(m->mnt_fsname);
+ entries[i].mnt_dir = xstrdup(m->mnt_dir);
+ entries[i].mnt_type = xstrdup(m->mnt_type);
+ entries[i].mnt_opts = xstrdup(m->mnt_opts);
+ entries[i].mnt_freq = m->mnt_freq;
+ entries[i].mnt_passno = m->mnt_passno;
+ }
+ endmntent(mountTable);
+
+ mountTable = setmntent(bb_path_mtab_file, "w");
+ if (mountTable) {
+ for (i = 0; i < count; i++) {
+ if (strcmp(entries[i].mnt_fsname, name) != 0
+ && strcmp(entries[i].mnt_dir, name) != 0)
+ addmntent(mountTable, &entries[i]);
+ }
+ endmntent(mountTable);
+ } else if (errno != EROFS)
+ bb_perror_msg(bb_path_mtab_file);
+}
+#endif
diff --git a/i/pc104/initrd/conf/busybox/libbb/mtab_file.c b/i/pc104/initrd/conf/busybox/libbb/mtab_file.c
new file mode 100644
index 0000000..67367e3
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/mtab_file.c
@@ -0,0 +1,17 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include <stdio.h>
+#include "libbb.h"
+
+
+/* Busybox mount uses either /proc/mounts or /etc/mtab to
+ * get the list of currently mounted filesystems */
+const char bb_path_mtab_file[] =
+USE_FEATURE_MTAB_SUPPORT("/etc/mtab")SKIP_FEATURE_MTAB_SUPPORT("/proc/mounts");
diff --git a/i/pc104/initrd/conf/busybox/libbb/obscure.c b/i/pc104/initrd/conf/busybox/libbb/obscure.c
new file mode 100644
index 0000000..2599095
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/obscure.c
@@ -0,0 +1,170 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini weak password checker implementation for busybox
+ *
+ * Copyright (C) 2006 Tito Ragusa <farmatito@tiscali.it>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/* A good password:
+ 1) should contain at least six characters (man passwd);
+ 2) empty passwords are not permitted;
+ 3) should contain a mix of four different types of characters
+ upper case letters,
+ lower case letters,
+ numbers,
+ special characters such as !@#$%^&*,;".
+ This password types should not be permitted:
+ a) pure numbers: birthdates, social security number, license plate, phone numbers;
+ b) words and all letters only passwords (uppercase, lowercase or mixed)
+ as palindromes, consecutive or repetitive letters
+ or adjacent letters on your keyboard;
+ c) username, real name, company name or (e-mail?) address
+ in any form (as-is, reversed, capitalized, doubled, etc.).
+ (we can check only against username, gecos and hostname)
+ d) common and obvious letter-number replacements
+ (e.g. replace the letter O with number 0)
+ such as "M1cr0$0ft" or "P@ssw0rd" (CAVEAT: we cannot check for them
+ without the use of a dictionary).
+
+ For each missing type of characters an increase of password length is
+ requested.
+
+ If user is root we warn only.
+
+ CAVEAT: some older versions of crypt() truncates passwords to 8 chars,
+ so that aaaaaaaa1Q$ is equal to aaaaaaaa making it possible to fool
+ some of our checks. We don't test for this special case as newer versions
+ of crypt do not truncate passwords.
+*/
+
+#include "libbb.h"
+
+static int string_checker_helper(const char *p1, const char *p2) __attribute__ ((__pure__));
+
+static int string_checker_helper(const char *p1, const char *p2)
+{
+ /* as-is or capitalized */
+ if (strcasecmp(p1, p2) == 0
+ /* as sub-string */
+ || strcasestr(p2, p1) != NULL
+ /* invert in case haystack is shorter than needle */
+ || strcasestr(p1, p2) != NULL)
+ return 1;
+ return 0;
+}
+
+static int string_checker(const char *p1, const char *p2)
+{
+ int size;
+ /* check string */
+ int ret = string_checker_helper(p1, p2);
+ /* Make our own copy */
+ char *p = xstrdup(p1);
+ /* reverse string */
+ size = strlen(p);
+
+ while (size--) {
+ *p = p1[size];
+ p++;
+ }
+ /* restore pointer */
+ p -= strlen(p1);
+ /* check reversed string */
+ ret |= string_checker_helper(p, p2);
+ /* clean up */
+ memset(p, 0, strlen(p1));
+ free(p);
+ return ret;
+}
+
+#define LOWERCASE 1
+#define UPPERCASE 2
+#define NUMBERS 4
+#define SPECIAL 8
+
+static const char *obscure_msg(const char *old_p, const char *new_p, const struct passwd *pw)
+{
+ int i;
+ int c;
+ int length;
+ int mixed = 0;
+ /* Add 2 for each type of characters to the minlen of password */
+ int size = CONFIG_PASSWORD_MINLEN + 8;
+ const char *p;
+ char hostname[255];
+
+ /* size */
+ if (!new_p || (length = strlen(new_p)) < CONFIG_PASSWORD_MINLEN)
+ return "too short";
+
+ /* no username as-is, as sub-string, reversed, capitalized, doubled */
+ if (string_checker(new_p, pw->pw_name)) {
+ return "similar to username";
+ }
+ /* no gecos as-is, as sub-string, reversed, capitalized, doubled */
+ if (*pw->pw_gecos && string_checker(new_p, pw->pw_gecos)) {
+ return "similar to gecos";
+ }
+ /* hostname as-is, as sub-string, reversed, capitalized, doubled */
+ if (gethostname(hostname, 255) == 0) {
+ hostname[254] = '\0';
+ if (string_checker(new_p, hostname)) {
+ return "similar to hostname";
+ }
+ }
+
+ /* Should / Must contain a mix of: */
+ for (i = 0; i < length; i++) {
+ if (islower(new_p[i])) { /* a-z */
+ mixed |= LOWERCASE;
+ } else if (isupper(new_p[i])) { /* A-Z */
+ mixed |= UPPERCASE;
+ } else if (isdigit(new_p[i])) { /* 0-9 */
+ mixed |= NUMBERS;
+ } else { /* special characters */
+ mixed |= SPECIAL;
+ }
+ /* More than 50% similar characters ? */
+ c = 0;
+ p = new_p;
+ while (1) {
+ if ((p = strchr(p, new_p[i])) == NULL) {
+ break;
+ }
+ c++;
+ if (!++p) {
+ break; /* move past the matched char if possible */
+ }
+ }
+
+ if (c >= (length / 2)) {
+ return "too many similar characters";
+ }
+ }
+ for (i=0; i<4; i++)
+ if (mixed & (1<<i)) size -= 2;
+ if (length < size)
+ return "too weak";
+
+ if (old_p && old_p[0] != '\0') {
+ /* check vs. old password */
+ if (string_checker(new_p, old_p)) {
+ return "similar to old password";
+ }
+ }
+ return NULL;
+}
+
+int obscure(const char *old, const char *newval, const struct passwd *pw)
+{
+ const char *msg;
+
+ msg = obscure_msg(old, newval, pw);
+ if (msg) {
+ printf("Bad password: %s\n", msg);
+ return 1;
+ }
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/parse_mode.c b/i/pc104/initrd/conf/busybox/libbb/parse_mode.c
new file mode 100644
index 0000000..3ab4eb6
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/parse_mode.c
@@ -0,0 +1,148 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * parse_mode implementation for busybox
+ *
+ * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/chmod.html */
+
+#include "libbb.h"
+
+#define FILEMODEBITS (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)
+
+int bb_parse_mode(const char *s, mode_t *current_mode)
+{
+ static const mode_t who_mask[] = {
+ S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO, /* a */
+ S_ISUID | S_IRWXU, /* u */
+ S_ISGID | S_IRWXG, /* g */
+ S_IRWXO /* o */
+ };
+ static const mode_t perm_mask[] = {
+ S_IRUSR | S_IRGRP | S_IROTH, /* r */
+ S_IWUSR | S_IWGRP | S_IWOTH, /* w */
+ S_IXUSR | S_IXGRP | S_IXOTH, /* x */
+ S_IXUSR | S_IXGRP | S_IXOTH, /* X -- special -- see below */
+ S_ISUID | S_ISGID, /* s */
+ S_ISVTX /* t */
+ };
+ static const char who_chars[] = "augo";
+ static const char perm_chars[] = "rwxXst";
+
+ const char *p;
+ mode_t wholist;
+ mode_t permlist;
+ mode_t new_mode;
+ char op;
+
+ if (((unsigned int)(*s - '0')) < 8) {
+ unsigned long tmp;
+ char *e;
+
+ tmp = strtoul(s, &e, 8);
+ if (*e || (tmp > 07777U)) { /* Check range and trailing chars. */
+ return 0;
+ }
+ *current_mode = tmp;
+ return 1;
+ }
+
+ new_mode = *current_mode;
+
+ /* Note: we allow empty clauses, and hence empty modes.
+ * We treat an empty mode as no change to perms. */
+
+ while (*s) { /* Process clauses. */
+ if (*s == ',') { /* We allow empty clauses. */
+ ++s;
+ continue;
+ }
+
+ /* Get a wholist. */
+ wholist = 0;
+ WHO_LIST:
+ p = who_chars;
+ do {
+ if (*p == *s) {
+ wholist |= who_mask[(int)(p-who_chars)];
+ if (!*++s) {
+ return 0;
+ }
+ goto WHO_LIST;
+ }
+ } while (*++p);
+
+ do { /* Process action list. */
+ if ((*s != '+') && (*s != '-')) {
+ if (*s != '=') {
+ return 0;
+ }
+ /* Since op is '=', clear all bits corresponding to the
+ * wholist, or all file bits if wholist is empty. */
+ permlist = ~FILEMODEBITS;
+ if (wholist) {
+ permlist = ~wholist;
+ }
+ new_mode &= permlist;
+ }
+ op = *s++;
+
+ /* Check for permcopy. */
+ p = who_chars + 1; /* Skip 'a' entry. */
+ do {
+ if (*p == *s) {
+ int i = 0;
+ permlist = who_mask[(int)(p-who_chars)]
+ & (S_IRWXU | S_IRWXG | S_IRWXO)
+ & new_mode;
+ do {
+ if (permlist & perm_mask[i]) {
+ permlist |= perm_mask[i];
+ }
+ } while (++i < 3);
+ ++s;
+ goto GOT_ACTION;
+ }
+ } while (*++p);
+
+ /* It was not a permcopy, so get a permlist. */
+ permlist = 0;
+ PERM_LIST:
+ p = perm_chars;
+ do {
+ if (*p == *s) {
+ if ((*p != 'X')
+ || (new_mode & (S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH))
+ ) {
+ permlist |= perm_mask[(int)(p-perm_chars)];
+ }
+ if (!*++s) {
+ break;
+ }
+ goto PERM_LIST;
+ }
+ } while (*++p);
+ GOT_ACTION:
+ if (permlist) { /* The permlist was nonempty. */
+ mode_t tmp = wholist;
+ if (!wholist) {
+ mode_t u_mask = umask(0);
+ umask(u_mask);
+ tmp = ~u_mask;
+ }
+ permlist &= tmp;
+ if (op == '-') {
+ new_mode &= ~permlist;
+ } else {
+ new_mode |= permlist;
+ }
+ }
+ } while (*s && (*s != ','));
+ }
+
+ *current_mode = new_mode;
+ return 1;
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/perror_msg.c b/i/pc104/initrd/conf/busybox/libbb/perror_msg.c
new file mode 100644
index 0000000..7fb0830
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/perror_msg.c
@@ -0,0 +1,23 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include "libbb.h"
+
+void bb_perror_msg(const char *s, ...)
+{
+ va_list p;
+
+ va_start(p, s);
+ bb_vperror_msg(s, p);
+ va_end(p);
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/perror_msg_and_die.c b/i/pc104/initrd/conf/busybox/libbb/perror_msg_and_die.c
new file mode 100644
index 0000000..7521e71
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/perror_msg_and_die.c
@@ -0,0 +1,24 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include "libbb.h"
+
+void bb_perror_msg_and_die(const char *s, ...)
+{
+ va_list p;
+
+ va_start(p, s);
+ bb_vperror_msg(s, p);
+ va_end(p);
+ sleep_and_die();
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/perror_nomsg.c b/i/pc104/initrd/conf/busybox/libbb/perror_nomsg.c
new file mode 100644
index 0000000..62ce888
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/perror_nomsg.c
@@ -0,0 +1,21 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * bb_perror_nomsg implementation for busybox
+ *
+ * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/* gcc warns about a null format string, therefore we provide
+ * modified definition without "attribute (format)"
+ * instead of including libbb.h */
+//#include "libbb.h"
+extern void bb_perror_msg(const char *s, ...);
+
+/* suppress gcc "no previous prototype" warning */
+void bb_perror_nomsg(void);
+void bb_perror_nomsg(void)
+{
+ bb_perror_msg(0);
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/perror_nomsg_and_die.c b/i/pc104/initrd/conf/busybox/libbb/perror_nomsg_and_die.c
new file mode 100644
index 0000000..dab3df6
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/perror_nomsg_and_die.c
@@ -0,0 +1,21 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * bb_perror_nomsg_and_die implementation for busybox
+ *
+ * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/* gcc warns about a null format string, therefore we provide
+ * modified definition without "attribute (format)"
+ * instead of including libbb.h */
+//#include "libbb.h"
+extern void bb_perror_msg_and_die(const char *s, ...);
+
+/* suppress gcc "no previous prototype" warning */
+void bb_perror_nomsg_and_die(void);
+void bb_perror_nomsg_and_die(void)
+{
+ bb_perror_msg_and_die(0);
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/process_escape_sequence.c b/i/pc104/initrd/conf/busybox/libbb/process_escape_sequence.c
new file mode 100644
index 0000000..138e751
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/process_escape_sequence.c
@@ -0,0 +1,89 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) Manuel Novoa III <mjn3@codepoet.org>
+ * and Vladimir Oleynik <dzo@simtreas.ru>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include <stdio.h>
+#include <limits.h>
+#include <ctype.h>
+#include "libbb.h"
+
+#define WANT_HEX_ESCAPES 1
+
+/* Usual "this only works for ascii compatible encodings" disclaimer. */
+#undef _tolower
+#define _tolower(X) ((X)|((char) 0x20))
+
+char bb_process_escape_sequence(const char **ptr)
+{
+ static const char charmap[] = {
+ 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\', 0,
+ '\a', '\b', '\f', '\n', '\r', '\t', '\v', '\\', '\\' };
+
+ const char *p;
+ const char *q;
+ unsigned int num_digits;
+ unsigned int r;
+ unsigned int n;
+ unsigned int d;
+ unsigned int base;
+
+ num_digits = n = 0;
+ base = 8;
+ q = *ptr;
+
+#ifdef WANT_HEX_ESCAPES
+ if (*q == 'x') {
+ ++q;
+ base = 16;
+ ++num_digits;
+ }
+#endif
+
+ do {
+ d = (unsigned int)(*q - '0');
+#ifdef WANT_HEX_ESCAPES
+ if (d >= 10) {
+ d = ((unsigned int)(_tolower(*q) - 'a')) + 10;
+ }
+#endif
+
+ if (d >= base) {
+#ifdef WANT_HEX_ESCAPES
+ if ((base == 16) && (!--num_digits)) {
+/* return '\\'; */
+ --q;
+ }
+#endif
+ break;
+ }
+
+ r = n * base + d;
+ if (r > UCHAR_MAX) {
+ break;
+ }
+
+ n = r;
+ ++q;
+ } while (++num_digits < 3);
+
+ if (num_digits == 0) { /* mnemonic escape sequence? */
+ p = charmap;
+ do {
+ if (*p == *q) {
+ q++;
+ break;
+ }
+ } while (*++p);
+ n = *(p+(sizeof(charmap)/2));
+ }
+
+ *ptr = q;
+
+ return (char) n;
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/procps.c b/i/pc104/initrd/conf/busybox/libbb/procps.c
new file mode 100644
index 0000000..c9dcfde
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/procps.c
@@ -0,0 +1,249 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright 1998 by Albert Cahalan; all rights reserved.
+ * Copyright (C) 2002 by Vladimir Oleynik <dzo@simtreas.ru>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "libbb.h"
+
+
+typedef struct unsigned_to_name_map_t {
+ unsigned id;
+ char name[USERNAME_MAX_SIZE];
+} unsigned_to_name_map_t;
+
+typedef struct cache_t {
+ unsigned_to_name_map_t *cache;
+ int size;
+} cache_t;
+
+static cache_t username, groupname;
+
+static void clear_cache(cache_t *cp)
+{
+ free(cp->cache);
+ cp->cache = NULL;
+ cp->size = 0;
+}
+void clear_username_cache(void)
+{
+ clear_cache(&username);
+ clear_cache(&groupname);
+}
+
+#if 0 /* more generic, but we don't need that yet */
+/* Returns -N-1 if not found. */
+/* cp->cache[N] is allocated and must be filled in this case */
+static int get_cached(cache_t *cp, unsigned id)
+{
+ int i;
+ for (i = 0; i < cp->size; i++)
+ if (cp->cache[i].id == id)
+ return i;
+ i = cp->size++;
+ cp->cache = xrealloc(cp->cache, cp->size * sizeof(*cp->cache));
+ cp->cache[i++].id = id;
+ return -i;
+}
+#endif
+
+typedef char* ug_func(char *name, long uid, int bufsize);
+static char* get_cached(cache_t *cp, unsigned id, ug_func* fp)
+{
+ int i;
+ for (i = 0; i < cp->size; i++)
+ if (cp->cache[i].id == id)
+ return cp->cache[i].name;
+ i = cp->size++;
+ cp->cache = xrealloc(cp->cache, cp->size * sizeof(*cp->cache));
+ cp->cache[i].id = id;
+ fp(cp->cache[i].name, id, sizeof(cp->cache[i].name));
+ return cp->cache[i].name;
+}
+const char* get_cached_username(uid_t uid)
+{
+ return get_cached(&username, uid, bb_getpwuid);
+}
+const char* get_cached_groupname(gid_t gid)
+{
+ return get_cached(&groupname, gid, bb_getgrgid);
+}
+
+
+#define PROCPS_BUFSIZE 1024
+
+static int read_to_buf(const char *filename, void *buf)
+{
+ ssize_t ret;
+ ret = open_read_close(filename, buf, PROCPS_BUFSIZE-1);
+ ((char *)buf)[ret > 0 ? ret : 0] = '\0';
+ return ret;
+}
+
+procps_status_t* alloc_procps_scan(int flags)
+{
+ procps_status_t* sp = xzalloc(sizeof(procps_status_t));
+ sp->dir = xopendir("/proc");
+ return sp;
+}
+
+void free_procps_scan(procps_status_t* sp)
+{
+ closedir(sp->dir);
+ free(sp->cmd);
+ free(sp);
+}
+
+void BUG_comm_size(void);
+procps_status_t* procps_scan(procps_status_t* sp, int flags)
+{
+ struct dirent *entry;
+ char buf[PROCPS_BUFSIZE];
+ char filename[sizeof("/proc//cmdline") + sizeof(int)*3];
+ char *filename_tail;
+ long tasknice;
+ unsigned pid;
+ int n;
+ struct stat sb;
+
+ if (!sp)
+ sp = alloc_procps_scan(flags);
+
+ for (;;) {
+ entry = readdir(sp->dir);
+ if (entry == NULL) {
+ free_procps_scan(sp);
+ return NULL;
+ }
+ pid = bb_strtou(entry->d_name, NULL, 10);
+ if (errno)
+ continue;
+
+ /* After this point we have to break, not continue
+ * ("continue" would mean that current /proc/NNN
+ * is not a valid process info) */
+
+ memset(&sp->vsz, 0, sizeof(*sp) - offsetof(procps_status_t, vsz));
+
+ sp->pid = pid;
+ if (!(flags & ~PSSCAN_PID)) break;
+
+ filename_tail = filename + sprintf(filename, "/proc/%d", pid);
+
+ if (flags & PSSCAN_UIDGID) {
+ if (stat(filename, &sb))
+ break;
+ /* Need comment - is this effective or real UID/GID? */
+ sp->uid = sb.st_uid;
+ sp->gid = sb.st_gid;
+ }
+
+ if (flags & PSSCAN_STAT) {
+ char *cp;
+ /* see proc(5) for some details on this */
+ strcpy(filename_tail, "/stat");
+ n = read_to_buf(filename, buf);
+ if (n < 0)
+ break;
+ cp = strrchr(buf, ')'); /* split into "PID (cmd" and "<rest>" */
+ if (!cp || cp[1] != ' ')
+ break;
+ cp[0] = '\0';
+ if (sizeof(sp->comm) < 16)
+ BUG_comm_size();
+ sscanf(buf, "%*s (%15c", sp->comm);
+ n = sscanf(cp+2,
+ "%c %u " /* state, ppid */
+ "%u %u %*s %*s " /* pgid, sid, tty, tpgid */
+ "%*s %*s %*s %*s %*s " /* flags, min_flt, cmin_flt, maj_flt, cmaj_flt */
+ "%lu %lu " /* utime, stime */
+ "%*s %*s %*s " /* cutime, cstime, priority */
+ "%ld " /* nice */
+ "%*s %*s %*s " /* timeout, it_real_value, start_time */
+ "%lu ", /* vsize */
+ sp->state, &sp->ppid,
+ &sp->pgid, &sp->sid,
+ &sp->utime, &sp->stime,
+ &tasknice,
+ &sp->vsz);
+ if (n != 8)
+ break;
+
+ if (sp->vsz == 0 && sp->state[0] != 'Z')
+ sp->state[1] = 'W';
+ else
+ sp->state[1] = ' ';
+ if (tasknice < 0)
+ sp->state[2] = '<';
+ else if (tasknice > 0)
+ sp->state[2] = 'N';
+ else
+ sp->state[2] = ' ';
+
+ sp->vsz >>= 10; /* vsize is in bytes and we want kb */
+ }
+
+ if (flags & PSSCAN_CMD) {
+ free(sp->cmd);
+ sp->cmd = NULL;
+ strcpy(filename_tail, "/cmdline");
+ n = read_to_buf(filename, buf);
+ if (n <= 0)
+ break;
+ if (buf[n-1] == '\n') {
+ if (!--n)
+ break;
+ buf[n] = '\0';
+ }
+ do {
+ n--;
+ if ((unsigned char)(buf[n]) < ' ')
+ buf[n] = ' ';
+ } while (n);
+ sp->cmd = xstrdup(buf);
+ }
+ break;
+ }
+ return sp;
+}
+/* from kernel:
+ // pid comm S ppid pgid sid tty_nr tty_pgrp flg
+ sprintf(buffer,"%d (%s) %c %d %d %d %d %d %lu %lu \
+%lu %lu %lu %lu %lu %ld %ld %ld %ld %d 0 %llu %lu %ld %lu %lu %lu %lu %lu \
+%lu %lu %lu %lu %lu %lu %lu %lu %d %d %lu %lu %llu\n",
+ task->pid,
+ tcomm,
+ state,
+ ppid,
+ pgid,
+ sid,
+ tty_nr,
+ tty_pgrp,
+ task->flags,
+ min_flt,
+ cmin_flt,
+ maj_flt,
+ cmaj_flt,
+ cputime_to_clock_t(utime),
+ cputime_to_clock_t(stime),
+ cputime_to_clock_t(cutime),
+ cputime_to_clock_t(cstime),
+ priority,
+ nice,
+ num_threads,
+ // 0,
+ start_time,
+ vsize,
+ mm ? get_mm_rss(mm) : 0,
+ rsslim,
+ mm ? mm->start_code : 0,
+ mm ? mm->end_code : 0,
+ mm ? mm->start_stack : 0,
+ esp,
+ eip,
+the rest is some obsolete cruft
+*/
diff --git a/i/pc104/initrd/conf/busybox/libbb/pw_encrypt.c b/i/pc104/initrd/conf/busybox/libbb/pw_encrypt.c
new file mode 100644
index 0000000..d546bc8
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/pw_encrypt.c
@@ -0,0 +1,29 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routine.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "libbb.h"
+#include <crypt.h>
+
+char *pw_encrypt(const char *clear, const char *salt)
+{
+ static char cipher[128];
+ char *cp;
+
+#if 0 /* was CONFIG_FEATURE_SHA1_PASSWORDS, but there is no such thing??? */
+ if (strncmp(salt, "$2$", 3) == 0) {
+ return sha1_crypt(clear);
+ }
+#endif
+ cp = (char *) crypt(clear, salt);
+ /* if crypt (a nonstandard crypt) returns a string too large,
+ truncate it so we don't overrun buffers and hope there is
+ enough security in what's left */
+ safe_strncpy(cipher, cp, sizeof(cipher));
+ return cipher;
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/read.c b/i/pc104/initrd/conf/busybox/libbb/read.c
new file mode 100644
index 0000000..c05b26b
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/read.c
@@ -0,0 +1,137 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "libbb.h"
+
+ssize_t safe_read(int fd, void *buf, size_t count)
+{
+ ssize_t n;
+
+ do {
+ n = read(fd, buf, count);
+ } while (n < 0 && errno == EINTR);
+
+ return n;
+}
+
+/*
+ * Read all of the supplied buffer from a file.
+ * This does multiple reads as necessary.
+ * Returns the amount read, or -1 on an error.
+ * A short read is returned on an end of file.
+ */
+ssize_t full_read(int fd, void *buf, size_t len)
+{
+ ssize_t cc;
+ ssize_t total;
+
+ total = 0;
+
+ while (len) {
+ cc = safe_read(fd, buf, len);
+
+ if (cc < 0)
+ return cc; /* read() returns -1 on failure. */
+
+ if (cc == 0)
+ break;
+
+ buf = ((char *)buf) + cc;
+ total += cc;
+ len -= cc;
+ }
+
+ return total;
+}
+
+// Die with an error message if we can't read the entire buffer.
+void xread(int fd, void *buf, size_t count)
+{
+ if (count) {
+ ssize_t size = full_read(fd, buf, count);
+ if (size != count)
+ bb_error_msg_and_die("short read");
+ }
+}
+
+// Die with an error message if we can't read one character.
+unsigned char xread_char(int fd)
+{
+ char tmp;
+
+ xread(fd, &tmp, 1);
+
+ return tmp;
+}
+
+// Read one line a-la fgets. Works only on seekable streams
+char *reads(int fd, char *buffer, size_t size)
+{
+ char *p;
+
+ if (size < 2)
+ return NULL;
+ size = full_read(fd, buffer, size-1);
+ if ((ssize_t)size <= 0)
+ return NULL;
+
+ buffer[size] = '\0';
+ p = strchr(buffer, '\n');
+ if (p) {
+ off_t offset;
+ *p++ = '\0';
+ // avoid incorrect (unsigned) widening
+ offset = (off_t)(p-buffer) - (off_t)size;
+ // set fd position right after '\n'
+ if (offset && lseek(fd, offset, SEEK_CUR) == (off_t)-1)
+ return NULL;
+ }
+ return buffer;
+}
+
+ssize_t read_close(int fd, void *buf, size_t size)
+{
+ int e;
+ size = full_read(fd, buf, size);
+ e = errno;
+ close(fd);
+ errno = e;
+ return size;
+}
+
+ssize_t open_read_close(const char *filename, void *buf, size_t size)
+{
+ int fd = open(filename, O_RDONLY);
+ if (fd < 0)
+ return fd;
+ return read_close(fd, buf, size);
+}
+
+void *xmalloc_open_read_close(const char *filename, size_t *sizep)
+{
+ char *buf;
+ size_t size = sizep ? *sizep : INT_MAX;
+ int fd = xopen(filename, O_RDONLY);
+ /* /proc/N/stat files report len 0 here */
+ /* In order to make such files readable, we add small const */
+ off_t len = xlseek(fd, 0, SEEK_END) + 256;
+ xlseek(fd, 0, SEEK_SET);
+
+ if (len > size)
+ bb_error_msg_and_die("file '%s' is too big", filename);
+ size = len;
+ buf = xmalloc(size + 1);
+ size = read_close(fd, buf, size);
+ if ((ssize_t)size < 0)
+ bb_perror_msg_and_die("'%s'", filename);
+ xrealloc(buf, size + 1);
+ buf[size] = '\0';
+ if (sizep) *sizep = size;
+ return buf;
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/recursive_action.c b/i/pc104/initrd/conf/busybox/libbb/recursive_action.c
new file mode 100644
index 0000000..25a87b8
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/recursive_action.c
@@ -0,0 +1,126 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "libbb.h"
+
+#undef DEBUG_RECURS_ACTION
+
+/*
+ * Walk down all the directories under the specified
+ * location, and do something (something specified
+ * by the fileAction and dirAction function pointers).
+ *
+ * Unfortunately, while nftw(3) could replace this and reduce
+ * code size a bit, nftw() wasn't supported before GNU libc 2.1,
+ * and so isn't sufficiently portable to take over since glibc2.1
+ * is so stinking huge.
+ */
+
+static int true_action(const char *fileName, struct stat *statbuf, void* userData, int depth)
+{
+ return TRUE;
+}
+
+/* fileAction return value of 0 on any file in directory will make
+ * recursive_action() return 0, but it doesn't stop directory traversal
+ * (fileAction/dirAction will be called on each file).
+ *
+ * if !depthFirst, dirAction return value of 0 (FALSE) or 2 (SKIP)
+ * prevents recursion into that directory, instead
+ * recursive_action() returns 0 (if FALSE) or 1 (if SKIP).
+ *
+ * followLinks=0/1 differs mainly in handling of links to dirs.
+ * 0: lstat(statbuf). Calls fileAction on link name even if points to dir.
+ * 1: stat(statbuf). Calls dirAction and optionally recurse on link to dir.
+ */
+
+int recursive_action(const char *fileName,
+ int recurse, int followLinks, int depthFirst,
+ int (*fileAction)(const char *fileName, struct stat *statbuf, void* userData, int depth),
+ int (*dirAction)(const char *fileName, struct stat *statbuf, void* userData, int depth),
+ void* userData,
+ int depth)
+{
+ struct stat statbuf;
+ int status;
+ DIR *dir;
+ struct dirent *next;
+
+ if (!fileAction) fileAction = true_action;
+ if (!dirAction) dirAction = true_action;
+
+ status = (followLinks ? stat : lstat)(fileName, &statbuf);
+
+ if (status < 0) {
+#ifdef DEBUG_RECURS_ACTION
+ bb_error_msg("status=%d followLinks=%d TRUE=%d",
+ status, followLinks, TRUE);
+#endif
+ bb_perror_msg("%s", fileName);
+ return FALSE;
+ }
+
+ /* If S_ISLNK(m), then we know that !S_ISDIR(m).
+ * Then we can skip checking first part: if it is true, then
+ * (!dir) is also true! */
+ if ( /* (!followLinks && S_ISLNK(statbuf.st_mode)) || */
+ !S_ISDIR(statbuf.st_mode)
+ ) {
+ return fileAction(fileName, &statbuf, userData, depth);
+ }
+
+ /* It's a directory (or a link to one, and followLinks is set) */
+
+ if (!recurse) {
+ return dirAction(fileName, &statbuf, userData, depth);
+ }
+
+ if (!depthFirst) {
+ status = dirAction(fileName, &statbuf, userData, depth);
+ if (!status) {
+ bb_perror_msg("%s", fileName);
+ return FALSE;
+ }
+ if (status == SKIP)
+ return TRUE;
+ }
+
+ dir = opendir(fileName);
+ if (!dir) {
+ /* findutils-4.1.20 reports this */
+ /* (i.e. it doesn't silently return with exit code 1) */
+ /* To trigger: "find -exec rm -rf {} \;" */
+ bb_perror_msg("%s", fileName);
+ return FALSE;
+ }
+ status = TRUE;
+ while ((next = readdir(dir)) != NULL) {
+ char *nextFile;
+
+ nextFile = concat_subpath_file(fileName, next->d_name);
+ if (nextFile == NULL)
+ continue;
+ if (!recursive_action(nextFile, TRUE, followLinks, depthFirst,
+ fileAction, dirAction, userData, depth+1)) {
+ status = FALSE;
+ }
+ free(nextFile);
+ }
+ closedir(dir);
+ if (depthFirst) {
+ if (!dirAction(fileName, &statbuf, userData, depth)) {
+ bb_perror_msg("%s", fileName);
+ return FALSE;
+ }
+ }
+
+ if (!status)
+ return FALSE;
+ return TRUE;
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/remove_file.c b/i/pc104/initrd/conf/busybox/libbb/remove_file.c
new file mode 100644
index 0000000..ab159a4
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/remove_file.c
@@ -0,0 +1,111 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini remove_file implementation for busybox
+ *
+ * Copyright (C) 2001 Matt Kraai <kraai@alumni.carnegiemellon.edu>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include <stdio.h>
+#include <time.h>
+#include <utime.h>
+#include <dirent.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include "libbb.h"
+
+int remove_file(const char *path, int flags)
+{
+ struct stat path_stat;
+ int path_exists = 1;
+
+ if (lstat(path, &path_stat) < 0) {
+ if (errno != ENOENT) {
+ bb_perror_msg("cannot stat '%s'", path);
+ return -1;
+ }
+
+ path_exists = 0;
+ }
+
+ if (!path_exists) {
+ if (!(flags & FILEUTILS_FORCE)) {
+ bb_perror_msg("cannot remove '%s'", path);
+ return -1;
+ }
+ return 0;
+ }
+
+ if (S_ISDIR(path_stat.st_mode)) {
+ DIR *dp;
+ struct dirent *d;
+ int status = 0;
+
+ if (!(flags & FILEUTILS_RECUR)) {
+ bb_error_msg("%s: is a directory", path);
+ return -1;
+ }
+
+ if ((!(flags & FILEUTILS_FORCE) && access(path, W_OK) < 0 &&
+ isatty(0)) ||
+ (flags & FILEUTILS_INTERACTIVE)) {
+ fprintf(stderr, "%s: descend into directory '%s'? ", applet_name,
+ path);
+ if (!bb_ask_confirmation())
+ return 0;
+ }
+
+ if ((dp = opendir(path)) == NULL) {
+ return -1;
+ }
+
+ while ((d = readdir(dp)) != NULL) {
+ char *new_path;
+
+ new_path = concat_subpath_file(path, d->d_name);
+ if(new_path == NULL)
+ continue;
+ if (remove_file(new_path, flags) < 0)
+ status = -1;
+ free(new_path);
+ }
+
+ if (closedir(dp) < 0) {
+ bb_perror_msg("cannot close '%s'", path);
+ return -1;
+ }
+
+ if (flags & FILEUTILS_INTERACTIVE) {
+ fprintf(stderr, "%s: remove directory '%s'? ", applet_name, path);
+ if (!bb_ask_confirmation())
+ return status;
+ }
+
+ if (rmdir(path) < 0) {
+ bb_perror_msg("cannot remove '%s'", path);
+ return -1;
+ }
+
+ return status;
+ } else {
+ if ((!(flags & FILEUTILS_FORCE) && access(path, W_OK) < 0 &&
+ !S_ISLNK(path_stat.st_mode) &&
+ isatty(0)) ||
+ (flags & FILEUTILS_INTERACTIVE)) {
+ fprintf(stderr, "%s: remove '%s'? ", applet_name, path);
+ if (!bb_ask_confirmation())
+ return 0;
+ }
+
+ if (unlink(path) < 0) {
+ bb_perror_msg("cannot remove '%s'", path);
+ return -1;
+ }
+
+ return 0;
+ }
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/restricted_shell.c b/i/pc104/initrd/conf/busybox/libbb/restricted_shell.c
new file mode 100644
index 0000000..dc4cfb4
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/restricted_shell.c
@@ -0,0 +1,46 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Copyright 1989 - 1991, Julianne Frances Haugh <jockgrrl@austin.rr.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Julianne F. Haugh nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "libbb.h"
+
+/* Return 1 if SHELL is a restricted shell (one not returned by
+ getusershell), else 0, meaning it is a standard shell. */
+int restricted_shell(const char *shell)
+{
+ char *line;
+
+ setusershell();
+ while ((line = getusershell())) {
+ if (*line != '#' && strcmp(line, shell) == 0)
+ return 0;
+ }
+ endusershell();
+ return 1;
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/run_shell.c b/i/pc104/initrd/conf/busybox/libbb/run_shell.c
new file mode 100644
index 0000000..6be0908
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/run_shell.c
@@ -0,0 +1,101 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Copyright 1989 - 1991, Julianne Frances Haugh <jockgrrl@austin.rr.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Julianne F. Haugh nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <ctype.h>
+#include "libbb.h"
+#ifdef CONFIG_SELINUX
+#include <selinux/selinux.h> /* for setexeccon */
+#endif
+
+#ifdef CONFIG_SELINUX
+static security_context_t current_sid;
+
+void
+renew_current_security_context(void)
+{
+ if (current_sid)
+ freecon(current_sid); /* Release old context */
+ getcon(&current_sid); /* update */
+}
+void
+set_current_security_context(security_context_t sid)
+{
+ if (current_sid)
+ freecon(current_sid); /* Release old context */
+ current_sid = sid;
+}
+
+#endif
+
+/* Run SHELL, or DEFAULT_SHELL if SHELL is empty.
+ If COMMAND is nonzero, pass it to the shell with the -c option.
+ If ADDITIONAL_ARGS is nonzero, pass it to the shell as more
+ arguments. */
+
+void run_shell(const char *shell, int loginshell, const char *command, const char **additional_args)
+{
+ const char **args;
+ int argno = 1;
+ int additional_args_cnt = 0;
+
+ for (args = additional_args; args && *args; args++)
+ additional_args_cnt++;
+
+ args = xmalloc(sizeof(char*) * (4 + additional_args_cnt));
+
+ args[0] = bb_get_last_path_component(xstrdup(shell));
+
+ if (loginshell)
+ args[0] = xasprintf("-%s", args[0]);
+
+ if (command) {
+ args[argno++] = "-c";
+ args[argno++] = command;
+ }
+ if (additional_args) {
+ for (; *additional_args; ++additional_args)
+ args[argno++] = *additional_args;
+ }
+ args[argno] = NULL;
+#ifdef CONFIG_SELINUX
+ if (current_sid && !setexeccon(current_sid)) {
+ freecon(current_sid);
+ execve(shell, (char **) args, environ);
+ } else
+#endif
+ execv(shell, (char **) args);
+ bb_perror_msg_and_die("cannot run %s", shell);
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/safe_strncpy.c b/i/pc104/initrd/conf/busybox/libbb/safe_strncpy.c
new file mode 100644
index 0000000..42bc16e
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/safe_strncpy.c
@@ -0,0 +1,21 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include <string.h>
+#include "libbb.h"
+
+
+
+/* Like strncpy but make sure the resulting string is always 0 terminated. */
+char * safe_strncpy(char *dst, const char *src, size_t size)
+{
+ if (!size) return dst;
+ dst[--size] = '\0';
+ return strncpy(dst, src, size);
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/safe_write.c b/i/pc104/initrd/conf/busybox/libbb/safe_write.c
new file mode 100644
index 0000000..c81f124
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/safe_write.c
@@ -0,0 +1,26 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include "libbb.h"
+
+
+
+ssize_t safe_write(int fd, const void *buf, size_t count)
+{
+ ssize_t n;
+
+ do {
+ n = write(fd, buf, count);
+ } while (n < 0 && errno == EINTR);
+
+ return n;
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/selinux_common.c b/i/pc104/initrd/conf/busybox/libbb/selinux_common.c
new file mode 100644
index 0000000..4cb85f0
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/selinux_common.c
@@ -0,0 +1,40 @@
+/*
+ * libbb/selinux_common.c
+ * -- common SELinux utility functions
+ *
+ * Copyright 2007 KaiGai Kohei <kaigai@kaigai.gr.jp>
+ */
+#include "busybox.h"
+#include <selinux/context.h>
+
+context_t set_security_context_component(security_context_t cur_context,
+ char *user, char *role, char *type, char *range)
+{
+ context_t con = context_new(cur_context);
+ if (!con)
+ return NULL;
+
+ if (user && context_user_set(con, user))
+ goto error;
+ if (type && context_type_set(con, type))
+ goto error;
+ if (range && context_range_set(con, range))
+ goto error;
+ if (role && context_role_set(con, role))
+ goto error;
+ return con;
+
+error:
+ context_free(con);
+ return NULL;
+}
+
+void setfscreatecon_or_die(security_context_t scontext)
+{
+ if (setfscreatecon(scontext) < 0) {
+ /* Can be NULL. All known printf implementations
+ * display "(null)", "<null>" etc */
+ bb_perror_msg_and_die("cannot set default "
+ "file creation context to %s", scontext);
+ }
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/setup_environment.c b/i/pc104/initrd/conf/busybox/libbb/setup_environment.c
new file mode 100644
index 0000000..874a58e
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/setup_environment.c
@@ -0,0 +1,83 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Copyright 1989 - 1991, Julianne Frances Haugh <jockgrrl@austin.rr.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Julianne F. Haugh nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <ctype.h>
+#include "libbb.h"
+
+
+
+#define DEFAULT_LOGIN_PATH "/bin:/usr/bin"
+#define DEFAULT_ROOT_LOGIN_PATH "/usr/sbin:/bin:/usr/bin:/sbin"
+
+void setup_environment(const char *shell, int loginshell, int changeenv, const struct passwd *pw)
+{
+ if (loginshell) {
+ const char *term;
+
+ /* Change the current working directory to be the home directory
+ * of the user. It is a fatal error for this process to be unable
+ * to change to that directory. There is no "default" home
+ * directory.
+ * Some systems default to HOME=/
+ */
+ if (chdir(pw->pw_dir)) {
+ xchdir("/");
+ fputs("warning: cannot change to home directory\n", stderr);
+ }
+
+ /* Leave TERM unchanged. Set HOME, SHELL, USER, LOGNAME, PATH.
+ Unset all other environment variables. */
+ term = getenv("TERM");
+ clearenv();
+ if (term)
+ xsetenv("TERM", term);
+ xsetenv("HOME", pw->pw_dir);
+ xsetenv("SHELL", shell);
+ xsetenv("USER", pw->pw_name);
+ xsetenv("LOGNAME", pw->pw_name);
+ xsetenv("PATH", (pw->pw_uid ? DEFAULT_LOGIN_PATH : DEFAULT_ROOT_LOGIN_PATH));
+ }
+ else if (changeenv) {
+ /* Set HOME, SHELL, and if not becoming a super-user,
+ USER and LOGNAME. */
+ xsetenv("HOME", pw->pw_dir);
+ xsetenv("SHELL", shell);
+ if (pw->pw_uid) {
+ xsetenv("USER", pw->pw_name);
+ xsetenv("LOGNAME", pw->pw_name);
+ }
+ }
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/sha1.c b/i/pc104/initrd/conf/busybox/libbb/sha1.c
new file mode 100644
index 0000000..734fde4
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/sha1.c
@@ -0,0 +1,178 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Based on shasum from http://www.netsw.org/crypto/hash/
+ * Majorly hacked up to use Dr Brian Gladman's sha1 code
+ *
+ * Copyright (C) 2002 Dr Brian Gladman <brg@gladman.me.uk>, Worcester, UK.
+ * Copyright (C) 2003 Glenn L. McGrath
+ * Copyright (C) 2003 Erik Andersen
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ *
+ * ---------------------------------------------------------------------------
+ * Issue Date: 10/11/2002
+ *
+ * This is a byte oriented version of SHA1 that operates on arrays of bytes
+ * stored in memory. It runs at 22 cycles per byte on a Pentium P4 processor
+ */
+
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "libbb.h"
+
+#define SHA1_BLOCK_SIZE 64
+#define SHA1_DIGEST_SIZE 20
+#define SHA1_HASH_SIZE SHA1_DIGEST_SIZE
+#define SHA2_GOOD 0
+#define SHA2_BAD 1
+
+#define rotl32(x,n) (((x) << n) | ((x) >> (32 - n)))
+
+#define SHA1_MASK (SHA1_BLOCK_SIZE - 1)
+
+/* reverse byte order in 32-bit words */
+#define ch(x,y,z) ((z) ^ ((x) & ((y) ^ (z))))
+#define parity(x,y,z) ((x) ^ (y) ^ (z))
+#define maj(x,y,z) (((x) & (y)) | ((z) & ((x) | (y))))
+
+/* A normal version as set out in the FIPS. This version uses */
+/* partial loop unrolling and is optimised for the Pentium 4 */
+#define rnd(f,k) \
+ do { \
+ t = a; a = rotl32(a,5) + f(b,c,d) + e + k + w[i]; \
+ e = d; d = c; c = rotl32(b, 30); b = t; \
+ } while (0)
+
+static void sha1_compile(sha1_ctx_t *ctx)
+{
+ uint32_t w[80], i, a, b, c, d, e, t;
+
+ /* note that words are compiled from the buffer into 32-bit */
+ /* words in big-endian order so an order reversal is needed */
+ /* here on little endian machines */
+ for (i = 0; i < SHA1_BLOCK_SIZE / 4; ++i)
+ w[i] = htonl(ctx->wbuf[i]);
+
+ for (i = SHA1_BLOCK_SIZE / 4; i < 80; ++i)
+ w[i] = rotl32(w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16], 1);
+
+ a = ctx->hash[0];
+ b = ctx->hash[1];
+ c = ctx->hash[2];
+ d = ctx->hash[3];
+ e = ctx->hash[4];
+
+ for (i = 0; i < 20; ++i) {
+ rnd(ch, 0x5a827999);
+ }
+
+ for (i = 20; i < 40; ++i) {
+ rnd(parity, 0x6ed9eba1);
+ }
+
+ for (i = 40; i < 60; ++i) {
+ rnd(maj, 0x8f1bbcdc);
+ }
+
+ for (i = 60; i < 80; ++i) {
+ rnd(parity, 0xca62c1d6);
+ }
+
+ ctx->hash[0] += a;
+ ctx->hash[1] += b;
+ ctx->hash[2] += c;
+ ctx->hash[3] += d;
+ ctx->hash[4] += e;
+}
+
+void sha1_begin(sha1_ctx_t *ctx)
+{
+ ctx->count[0] = ctx->count[1] = 0;
+ ctx->hash[0] = 0x67452301;
+ ctx->hash[1] = 0xefcdab89;
+ ctx->hash[2] = 0x98badcfe;
+ ctx->hash[3] = 0x10325476;
+ ctx->hash[4] = 0xc3d2e1f0;
+}
+
+/* SHA1 hash data in an array of bytes into hash buffer and call the */
+/* hash_compile function as required. */
+void sha1_hash(const void *data, size_t length, sha1_ctx_t *ctx)
+{
+ uint32_t pos = (uint32_t) (ctx->count[0] & SHA1_MASK);
+ uint32_t freeb = SHA1_BLOCK_SIZE - pos;
+ const unsigned char *sp = data;
+
+ if ((ctx->count[0] += length) < length)
+ ++(ctx->count[1]);
+
+ while (length >= freeb) { /* tranfer whole blocks while possible */
+ memcpy(((unsigned char *) ctx->wbuf) + pos, sp, freeb);
+ sp += freeb;
+ length -= freeb;
+ freeb = SHA1_BLOCK_SIZE;
+ pos = 0;
+ sha1_compile(ctx);
+ }
+
+ memcpy(((unsigned char *) ctx->wbuf) + pos, sp, length);
+}
+
+void *sha1_end(void *resbuf, sha1_ctx_t *ctx)
+{
+ /* SHA1 Final padding and digest calculation */
+#if BB_BIG_ENDIAN
+ static uint32_t mask[4] = { 0x00000000, 0xff000000, 0xffff0000, 0xffffff00 };
+ static uint32_t bits[4] = { 0x80000000, 0x00800000, 0x00008000, 0x00000080 };
+#else
+ static uint32_t mask[4] = { 0x00000000, 0x000000ff, 0x0000ffff, 0x00ffffff };
+ static uint32_t bits[4] = { 0x00000080, 0x00008000, 0x00800000, 0x80000000 };
+#endif
+
+ uint8_t *hval = resbuf;
+ uint32_t i, cnt = (uint32_t) (ctx->count[0] & SHA1_MASK);
+
+ /* mask out the rest of any partial 32-bit word and then set */
+ /* the next byte to 0x80. On big-endian machines any bytes in */
+ /* the buffer will be at the top end of 32 bit words, on little */
+ /* endian machines they will be at the bottom. Hence the AND */
+ /* and OR masks above are reversed for little endian systems */
+ ctx->wbuf[cnt >> 2] =
+ (ctx->wbuf[cnt >> 2] & mask[cnt & 3]) | bits[cnt & 3];
+
+ /* we need 9 or more empty positions, one for the padding byte */
+ /* (above) and eight for the length count. If there is not */
+ /* enough space pad and empty the buffer */
+ if (cnt > SHA1_BLOCK_SIZE - 9) {
+ if (cnt < 60)
+ ctx->wbuf[15] = 0;
+ sha1_compile(ctx);
+ cnt = 0;
+ } else /* compute a word index for the empty buffer positions */
+ cnt = (cnt >> 2) + 1;
+
+ while (cnt < 14) /* and zero pad all but last two positions */
+ ctx->wbuf[cnt++] = 0;
+
+ /* assemble the eight byte counter in the buffer in big-endian */
+ /* format */
+
+ ctx->wbuf[14] = htonl((ctx->count[1] << 3) | (ctx->count[0] >> 29));
+ ctx->wbuf[15] = htonl(ctx->count[0] << 3);
+
+ sha1_compile(ctx);
+
+ /* extract the hash value as bytes in case the hash buffer is */
+ /* misaligned for 32-bit words */
+
+ for (i = 0; i < SHA1_DIGEST_SIZE; ++i)
+ hval[i] = (unsigned char) (ctx->hash[i >> 2] >> 8 * (~i & 3));
+
+ return resbuf;
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/simplify_path.c b/i/pc104/initrd/conf/busybox/libbb/simplify_path.c
new file mode 100644
index 0000000..7e68e39
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/simplify_path.c
@@ -0,0 +1,50 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * bb_simplify_path implementation for busybox
+ *
+ * Copyright (C) 2001 Manuel Novoa III <mjn3@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "libbb.h"
+
+char *bb_simplify_path(const char *path)
+{
+ char *s, *start, *p;
+
+ if (path[0] == '/')
+ start = xstrdup(path);
+ else {
+ s = xrealloc_getcwd_or_warn(NULL);
+ start = concat_path_file(s, path);
+ free(s);
+ }
+ p = s = start;
+
+ do {
+ if (*p == '/') {
+ if (*s == '/') { /* skip duplicate (or initial) slash */
+ continue;
+ } else if (*s == '.') {
+ if (s[1] == '/' || s[1] == 0) { /* remove extra '.' */
+ continue;
+ } else if ((s[1] == '.') && (s[2] == '/' || s[2] == 0)) {
+ ++s;
+ if (p > start) {
+ while (*--p != '/'); /* omit previous dir */
+ }
+ continue;
+ }
+ }
+ }
+ *++p = *s;
+ } while (*++s);
+
+ if ((p == start) || (*p != '/')) { /* not a trailing slash */
+ ++p; /* so keep last character */
+ }
+ *p = 0;
+
+ return start;
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/skip_whitespace.c b/i/pc104/initrd/conf/busybox/libbb/skip_whitespace.c
new file mode 100644
index 0000000..bdfb97d
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/skip_whitespace.c
@@ -0,0 +1,25 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * skip_whitespace implementation for busybox
+ *
+ * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "libbb.h"
+
+char *skip_whitespace(const char *s)
+{
+ /* NB: isspace('0') returns 0 */
+ while (isspace(*s)) ++s;
+
+ return (char *) s;
+}
+
+char *skip_non_whitespace(const char *s)
+{
+ while (*s && !isspace(*s)) ++s;
+
+ return (char *) s;
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/speed_table.c b/i/pc104/initrd/conf/busybox/libbb/speed_table.c
new file mode 100644
index 0000000..6137b77
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/speed_table.c
@@ -0,0 +1,117 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * compact speed_t <-> speed functions for busybox
+ *
+ * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include <termios.h>
+#include "libbb.h"
+
+struct speed_map {
+ unsigned short speed;
+ unsigned short value;
+};
+
+static const struct speed_map speeds[] = {
+ {B0, 0},
+ {B50, 50},
+ {B75, 75},
+ {B110, 110},
+ {B134, 134},
+ {B150, 150},
+ {B200, 200},
+ {B300, 300},
+ {B600, 600},
+ {B1200, 1200},
+ {B1800, 1800},
+ {B2400, 2400},
+ {B4800, 4800},
+ {B9600, 9600},
+#ifdef B19200
+ {B19200, 19200},
+#elif defined(EXTA)
+ {EXTA, 19200},
+#endif
+#ifdef B38400
+ {B38400, 38400/256 + 0x8000U},
+#elif defined(EXTB)
+ {EXTB, 38400/256 + 0x8000U},
+#endif
+#ifdef B57600
+ {B57600, 57600/256 + 0x8000U},
+#endif
+#ifdef B115200
+ {B115200, 115200/256 + 0x8000U},
+#endif
+#ifdef B230400
+ {B230400, 230400/256 + 0x8000U},
+#endif
+#ifdef B460800
+ {B460800, 460800/256 + 0x8000U},
+#endif
+};
+
+enum { NUM_SPEEDS = (sizeof(speeds) / sizeof(struct speed_map)) };
+
+unsigned int tty_baud_to_value(speed_t speed)
+{
+ int i = 0;
+
+ do {
+ if (speed == speeds[i].speed) {
+ if (speeds[i].value & 0x8000U) {
+ return ((unsigned long) (speeds[i].value) & 0x7fffU) * 256;
+ }
+ return speeds[i].value;
+ }
+ } while (++i < NUM_SPEEDS);
+
+ return 0;
+}
+
+speed_t tty_value_to_baud(unsigned int value)
+{
+ int i = 0;
+
+ do {
+ if (value == tty_baud_to_value(speeds[i].speed)) {
+ return speeds[i].speed;
+ }
+ } while (++i < NUM_SPEEDS);
+
+ return (speed_t) - 1;
+}
+
+#if 0
+/* testing code */
+#include <stdio.h>
+
+int main(void)
+{
+ unsigned long v;
+ speed_t s;
+
+ for (v = 0 ; v < 500000 ; v++) {
+ s = tty_value_to_baud(v);
+ if (s == (speed_t) -1) {
+ continue;
+ }
+ printf("v = %lu -- s = %0lo\n", v, (unsigned long) s);
+ }
+
+ printf("-------------------------------\n");
+
+ for (s = 0 ; s < 010017+1 ; s++) {
+ v = tty_baud_to_value(s);
+ if (!v) {
+ continue;
+ }
+ printf("v = %lu -- s = %0lo\n", v, (unsigned long) s);
+ }
+
+ return 0;
+}
+#endif
diff --git a/i/pc104/initrd/conf/busybox/libbb/trim.c b/i/pc104/initrd/conf/busybox/libbb/trim.c
new file mode 100644
index 0000000..d363915
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/trim.c
@@ -0,0 +1,31 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) many different people.
+ * If you wrote this, please acknowledge your work.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include "libbb.h"
+
+
+void trim(char *s)
+{
+ size_t len = strlen(s);
+ size_t lws;
+
+ /* trim trailing whitespace */
+ while (len && isspace(s[len-1])) --len;
+
+ /* trim leading whitespace */
+ if(len) {
+ lws = strspn(s, " \n\r\t\v");
+ memmove(s, s + lws, len -= lws);
+ }
+ s[len] = 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/u_signal_names.c b/i/pc104/initrd/conf/busybox/libbb/u_signal_names.c
new file mode 100644
index 0000000..52861d4
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/u_signal_names.c
@@ -0,0 +1,57 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Signal name/number conversion routines.
+ *
+ * Copyright 2006 Rob Landley <rob@landley.net>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "libbb.h"
+
+static const struct signal_name {
+ int number;
+ char name[5];
+} signals[] = {
+ // SUSv3 says kill must support these, and specifies the numerical values,
+ // http://www.opengroup.org/onlinepubs/009695399/utilities/kill.html
+ // TODO: "[SIG]EXIT" shouldn't work for kill, right?
+ {0, "EXIT"}, {1, "HUP"}, {2, "INT"}, {3, "QUIT"}, {6, "ABRT"}, {9, "KILL"},
+ {14, "ALRM"}, {15, "TERM"},
+ // And Posix adds the following:
+ {SIGILL, "ILL"}, {SIGTRAP, "TRAP"}, {SIGFPE, "FPE"}, {SIGUSR1, "USR1"},
+ {SIGSEGV, "SEGV"}, {SIGUSR2, "USR2"}, {SIGPIPE, "PIPE"}, {SIGCHLD, "CHLD"},
+ {SIGCONT, "CONT"}, {SIGSTOP, "STOP"}, {SIGTSTP, "TSTP"}, {SIGTTIN, "TTIN"},
+ {SIGTTOU, "TTOU"}
+};
+
+// Convert signal name to number.
+
+int get_signum(const char *name)
+{
+ int i;
+
+ i = bb_strtou(name, NULL, 10);
+ if (!errno) return i;
+ for (i = 0; i < sizeof(signals) / sizeof(struct signal_name); i++)
+ if (strcasecmp(name, signals[i].name) == 0
+ || (strncasecmp(name, "SIG", 3) == 0
+ && strcasecmp(&name[3], signals[i].name) == 0))
+ return signals[i].number;
+ return -1;
+}
+
+// Convert signal number to name
+
+const char *get_signame(int number)
+{
+ int i;
+
+ for (i=0; i < sizeof(signals) / sizeof(struct signal_name); i++) {
+ if (number == signals[i].number) {
+ return signals[i].name;
+ }
+ }
+
+ return itoa(number);
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/uuencode.c b/i/pc104/initrd/conf/busybox/libbb/uuencode.c
new file mode 100644
index 0000000..3840120
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/uuencode.c
@@ -0,0 +1,63 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Copyright 2006 Rob Landley <rob@landley.net>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "libbb.h"
+
+/* Conversion table. for base 64 */
+const char bb_uuenc_tbl_base64[65] = {
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
+ 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
+ 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+ 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
+ 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
+ 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
+ 'w', 'x', 'y', 'z', '0', '1', '2', '3',
+ '4', '5', '6', '7', '8', '9', '+', '/',
+ '=' /* termination character */
+};
+
+const char bb_uuenc_tbl_std[65] = {
+ '`', '!', '"', '#', '$', '%', '&', '\'',
+ '(', ')', '*', '+', ',', '-', '.', '/',
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', ':', ';', '<', '=', '>', '?',
+ '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
+ 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
+ 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
+ 'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
+ '`' /* termination character */
+};
+
+/*
+ * Encode the string S of length LENGTH to base64 format and place it
+ * to STORE. STORE will be 0-terminated, and must point to a writable
+ * buffer of at least 1+BASE64_LENGTH(length) bytes.
+ * where BASE64_LENGTH(len) = (4 * ((LENGTH + 2) / 3))
+ */
+void bb_uuencode(const unsigned char *s, char *store, const int length, const char *tbl)
+{
+ int i;
+ char *p = store;
+
+ /* Transform the 3x8 bits to 4x6 bits, as required by base64. */
+ for (i = 0; i < length; i += 3) {
+ *p++ = tbl[s[0] >> 2];
+ *p++ = tbl[((s[0] & 3) << 4) + (s[1] >> 4)];
+ *p++ = tbl[((s[1] & 0xf) << 2) + (s[2] >> 6)];
+ *p++ = tbl[s[2] & 0x3f];
+ s += 3;
+ }
+ /* Pad the result if necessary... */
+ if (i == length + 1) {
+ *(p - 1) = tbl[64];
+ }
+ else if (i == length + 2) {
+ *(p - 1) = *(p - 2) = tbl[64];
+ }
+ /* ...and zero-terminate it. */
+ *p = '\0';
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/vdprintf.c b/i/pc104/initrd/conf/busybox/libbb/vdprintf.c
new file mode 100644
index 0000000..d16c519
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/vdprintf.c
@@ -0,0 +1,21 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "libbb.h"
+
+#if defined(__GLIBC__) && __GLIBC__ < 2
+int vdprintf(int d, const char *format, va_list ap)
+{
+ char buf[BUF_SIZE];
+ int len;
+
+ len = vsprintf(buf, format, ap);
+ return write(d, buf, len);
+}
+#endif
diff --git a/i/pc104/initrd/conf/busybox/libbb/verror_msg.c b/i/pc104/initrd/conf/busybox/libbb/verror_msg.c
new file mode 100644
index 0000000..0f018c5
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/verror_msg.c
@@ -0,0 +1,46 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "libbb.h"
+#include <syslog.h>
+
+int logmode = LOGMODE_STDIO;
+const char *msg_eol = "\n";
+
+void bb_verror_msg(const char *s, va_list p, const char* strerr)
+{
+ /* va_copy is used because it is not portable
+ * to use va_list p twice */
+ va_list p2;
+ va_copy(p2, p);
+
+ if (logmode & LOGMODE_STDIO) {
+ fflush(stdout);
+ fprintf(stderr, "%s: ", applet_name);
+ vfprintf(stderr, s, p);
+ if (!strerr)
+ fputs(msg_eol, stderr);
+ else
+ fprintf(stderr, "%s%s%s",
+ s ? ": " : "",
+ strerr, msg_eol);
+ }
+ if (ENABLE_FEATURE_SYSLOG && (logmode & LOGMODE_SYSLOG)) {
+ if (!strerr)
+ vsyslog(LOG_ERR, s, p2);
+ else {
+ char *msg;
+ if (vasprintf(&msg, s, p2) < 0)
+ bb_error_msg_and_die(bb_msg_memory_exhausted);
+ syslog(LOG_ERR, "%s: %s", msg, strerr);
+ free(msg);
+ }
+ }
+ va_end(p2);
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/vfork_daemon_rexec.c b/i/pc104/initrd/conf/busybox/libbb/vfork_daemon_rexec.c
new file mode 100644
index 0000000..26d1826
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/vfork_daemon_rexec.c
@@ -0,0 +1,63 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Rexec program for system have fork() as vfork() with foreground option
+ *
+ * Copyright (C) Vladimir N. Oleynik <dzo@simtreas.ru>
+ * Copyright (C) 2003 Russ Dill <Russ.Dill@asu.edu>
+ *
+ * daemon() portion taken from uClibc:
+ *
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Modified for uClibc by Erik Andersen <andersee@debian.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include <paths.h>
+#include "libbb.h"
+
+#ifdef BB_NOMMU
+void vfork_daemon_rexec(int nochdir, int noclose,
+ int argc, char **argv, char *foreground_opt)
+{
+ int fd;
+ char **vfork_args;
+ int a = 0;
+
+ setsid();
+
+ if (!nochdir)
+ xchdir("/");
+
+ if (!noclose && (fd = open(bb_dev_null, O_RDWR, 0)) != -1) {
+ dup2(fd, STDIN_FILENO);
+ dup2(fd, STDOUT_FILENO);
+ dup2(fd, STDERR_FILENO);
+ while (fd > 2)
+ close(fd--);
+ }
+
+ vfork_args = xzalloc(sizeof(char *) * (argc + 3));
+ vfork_args[a++] = CONFIG_BUSYBOX_EXEC_PATH;
+ while (*argv) {
+ vfork_args[a++] = *argv;
+ argv++;
+ }
+ vfork_args[a] = foreground_opt;
+ switch (vfork()) {
+ case 0: /* child */
+ /* Make certain we are not a session leader, or else we
+ * might reacquire a controlling terminal */
+ if (vfork())
+ _exit(0);
+ execv(vfork_args[0], vfork_args);
+ bb_perror_msg_and_die("execv %s", vfork_args[0]);
+ case -1: /* error */
+ bb_perror_msg_and_die("vfork");
+ default: /* parent */
+ exit(0);
+ }
+}
+#endif /* BB_NOMMU */
diff --git a/i/pc104/initrd/conf/busybox/libbb/vherror_msg.c b/i/pc104/initrd/conf/busybox/libbb/vherror_msg.c
new file mode 100644
index 0000000..04446a0
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/vherror_msg.c
@@ -0,0 +1,15 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "libbb.h"
+
+void bb_vherror_msg(const char *s, va_list p)
+{
+ bb_verror_msg(s, p, hstrerror(h_errno));
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/vinfo_msg.c b/i/pc104/initrd/conf/busybox/libbb/vinfo_msg.c
new file mode 100644
index 0000000..fa27986
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/vinfo_msg.c
@@ -0,0 +1,26 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "libbb.h"
+#include <syslog.h>
+
+void bb_vinfo_msg(const char *s, va_list p)
+{
+ /* va_copy is used because it is not portable
+ * to use va_list p twice */
+ va_list p2;
+ va_copy(p2, p);
+ if (logmode & LOGMODE_STDIO) {
+ vprintf(s, p);
+ fputs(msg_eol, stdout);
+ }
+ if (ENABLE_FEATURE_SYSLOG && (logmode & LOGMODE_SYSLOG))
+ vsyslog(LOG_INFO, s, p2);
+ va_end(p2);
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/vperror_msg.c b/i/pc104/initrd/conf/busybox/libbb/vperror_msg.c
new file mode 100644
index 0000000..c3f79c2
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/vperror_msg.c
@@ -0,0 +1,15 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "libbb.h"
+
+void bb_vperror_msg(const char *s, va_list p)
+{
+ bb_verror_msg(s, p, strerror(errno));
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/warn_ignoring_args.c b/i/pc104/initrd/conf/busybox/libbb/warn_ignoring_args.c
new file mode 100644
index 0000000..be78a44
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/warn_ignoring_args.c
@@ -0,0 +1,17 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * warn_ignoring_args implementation for busybox
+ *
+ * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "libbb.h"
+
+void bb_warn_ignoring_args(int n)
+{
+ if (n) {
+ bb_error_msg("ignoring all arguments");
+ }
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/wfopen.c b/i/pc104/initrd/conf/busybox/libbb/wfopen.c
new file mode 100644
index 0000000..26e6a13
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/wfopen.c
@@ -0,0 +1,20 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "libbb.h"
+
+FILE *fopen_or_warn(const char *path, const char *mode)
+{
+ FILE *fp = fopen(path, mode);
+ if (!fp) {
+ bb_perror_msg("%s", path);
+ errno = 0;
+ }
+ return fp;
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/wfopen_input.c b/i/pc104/initrd/conf/busybox/libbb/wfopen_input.c
new file mode 100644
index 0000000..7a11dac
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/wfopen_input.c
@@ -0,0 +1,31 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * wfopen_input implementation for busybox
+ *
+ * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/* A number of applets need to open a file for reading, where the filename
+ * is a command line arg. Since often that arg is '-' (meaning stdin),
+ * we avoid testing everywhere by consolidating things in this routine.
+ *
+ * Note: We also consider "" to main stdin (for 'cmp' at least).
+ */
+
+#include "libbb.h"
+
+FILE *fopen_or_warn_stdin(const char *filename)
+{
+ FILE *fp = stdin;
+
+ if (filename != bb_msg_standard_input
+ && filename[0]
+ && NOT_LONE_DASH(filename)
+ ) {
+ fp = fopen_or_warn(filename, "r");
+ }
+
+ return fp;
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/xatonum.c b/i/pc104/initrd/conf/busybox/libbb/xatonum.c
new file mode 100644
index 0000000..dec3c2d
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/xatonum.c
@@ -0,0 +1,70 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ascii-to-numbers implementations for busybox
+ *
+ * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
+ *
+ * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ */
+
+#include "libbb.h"
+
+#define type long long
+#define xstrtou(rest) xstrtoull##rest
+#define xstrto(rest) xstrtoll##rest
+#define xatou(rest) xatoull##rest
+#define xato(rest) xatoll##rest
+#define XSTR_UTYPE_MAX ULLONG_MAX
+#define XSTR_TYPE_MAX LLONG_MAX
+#define XSTR_TYPE_MIN LLONG_MIN
+#define XSTR_STRTOU strtoull
+#include "xatonum_template.c"
+
+#if ULONG_MAX != ULLONG_MAX
+#define type long
+#define xstrtou(rest) xstrtoul##rest
+#define xstrto(rest) xstrtol##rest
+#define xatou(rest) xatoul##rest
+#define xato(rest) xatol##rest
+#define XSTR_UTYPE_MAX ULONG_MAX
+#define XSTR_TYPE_MAX LONG_MAX
+#define XSTR_TYPE_MIN LONG_MIN
+#define XSTR_STRTOU strtoul
+#include "xatonum_template.c"
+#endif
+
+#if UINT_MAX != ULONG_MAX
+static ATTRIBUTE_ALWAYS_INLINE
+unsigned bb_strtoui(const char *str, char **end, int b)
+{
+ unsigned long v = strtoul(str, end, b);
+ if (v > UINT_MAX) {
+ errno = ERANGE;
+ return UINT_MAX;
+ }
+ return v;
+}
+#define type int
+#define xstrtou(rest) xstrtou##rest
+#define xstrto(rest) xstrtoi##rest
+#define xatou(rest) xatou##rest
+#define xato(rest) xatoi##rest
+#define XSTR_UTYPE_MAX UINT_MAX
+#define XSTR_TYPE_MAX INT_MAX
+#define XSTR_TYPE_MIN INT_MIN
+/* libc has no strtoui, so we need to create/use our own */
+#define XSTR_STRTOU bb_strtoui
+#include "xatonum_template.c"
+#endif
+
+/* A few special cases */
+
+int xatoi_u(const char *numstr)
+{
+ return xatou_range(numstr, 0, INT_MAX);
+}
+
+uint16_t xatou16(const char *numstr)
+{
+ return xatou_range(numstr, 0, 0xffff);
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/xatonum_template.c b/i/pc104/initrd/conf/busybox/libbb/xatonum_template.c
new file mode 100644
index 0000000..ce01995
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/xatonum_template.c
@@ -0,0 +1,185 @@
+/*
+You need to define the following (example):
+
+#define type long
+#define xstrtou(rest) xstrtoul##rest
+#define xstrto(rest) xstrtol##rest
+#define xatou(rest) xatoul##rest
+#define xato(rest) xatol##rest
+#define XSTR_UTYPE_MAX ULONG_MAX
+#define XSTR_TYPE_MAX LONG_MAX
+#define XSTR_TYPE_MIN LONG_MIN
+#define XSTR_STRTOU strtoul
+*/
+
+unsigned type xstrtou(_range_sfx)(const char *numstr, int base,
+ unsigned type lower,
+ unsigned type upper,
+ const struct suffix_mult *suffixes)
+{
+ unsigned type r;
+ int old_errno;
+ char *e;
+
+ /* Disallow '-' and any leading whitespace. Speed isn't critical here
+ * since we're parsing commandline args. So make sure we get the
+ * actual isspace function rather than a lnumstrer macro implementaion. */
+ if (*numstr == '-' || *numstr == '+' || (isspace)(*numstr))
+ goto inval;
+
+ /* Since this is a lib function, we're not allowed to reset errno to 0.
+ * Doing so could break an app that is deferring checking of errno.
+ * So, save the old value so that we can restore it if successful. */
+ old_errno = errno;
+ errno = 0;
+ r = XSTR_STRTOU(numstr, &e, base);
+ /* Do the initial validity check. Note: The standards do not
+ * guarantee that errno is set if no digits were found. So we
+ * must test for this explicitly. */
+ if (errno || numstr == e)
+ goto inval; /* error / no digits / illegal trailing chars */
+
+ errno = old_errno; /* Ok. So restore errno. */
+
+ /* Do optional suffix parsing. Allow 'empty' suffix tables.
+ * Note that we also allow nul suffixes with associated multipliers,
+ * to allow for scaling of the numstr by some default multiplier. */
+ if (suffixes) {
+ while (suffixes->suffix) {
+ if (strcmp(suffixes->suffix, e) == 0) {
+ if (XSTR_UTYPE_MAX / suffixes->mult < r)
+ goto range; /* overflow! */
+ ++e;
+ r *= suffixes->mult;
+ break;
+ }
+ ++suffixes;
+ }
+ }
+
+ /* Note: trailing space is an error.
+ It would be easy enough to allow though if desired. */
+ if (*e)
+ goto inval;
+ /* Finally, check for range limits. */
+ if (r >= lower && r <= upper)
+ return r;
+ range:
+ bb_error_msg_and_die("number %s is not in %llu..%llu range",
+ numstr, (unsigned long long)lower,
+ (unsigned long long)upper);
+ inval:
+ bb_error_msg_and_die("invalid number '%s'", numstr);
+}
+
+unsigned type xstrtou(_range)(const char *numstr, int base,
+ unsigned type lower,
+ unsigned type upper)
+{
+ return xstrtou(_range_sfx)(numstr, base, lower, upper, NULL);
+}
+
+unsigned type xstrtou(_sfx)(const char *numstr, int base,
+ const struct suffix_mult *suffixes)
+{
+ return xstrtou(_range_sfx)(numstr, base, 0, XSTR_UTYPE_MAX, suffixes);
+}
+
+unsigned type xstrtou()(const char *numstr, int base)
+{
+ return xstrtou(_range_sfx)(numstr, base, 0, XSTR_UTYPE_MAX, NULL);
+}
+
+unsigned type xatou(_range_sfx)(const char *numstr,
+ unsigned type lower,
+ unsigned type upper,
+ const struct suffix_mult *suffixes)
+{
+ return xstrtou(_range_sfx)(numstr, 10, lower, upper, suffixes);
+}
+
+unsigned type xatou(_range)(const char *numstr,
+ unsigned type lower,
+ unsigned type upper)
+{
+ return xstrtou(_range_sfx)(numstr, 10, lower, upper, NULL);
+}
+
+unsigned type xatou(_sfx)(const char *numstr,
+ const struct suffix_mult *suffixes)
+{
+ return xstrtou(_range_sfx)(numstr, 10, 0, XSTR_UTYPE_MAX, suffixes);
+}
+
+unsigned type xatou()(const char *numstr)
+{
+ return xatou(_sfx)(numstr, NULL);
+}
+
+/* Signed ones */
+
+type xstrto(_range_sfx)(const char *numstr, int base,
+ type lower,
+ type upper,
+ const struct suffix_mult *suffixes)
+{
+ unsigned type u = XSTR_TYPE_MAX;
+ type r;
+ const char *p = numstr;
+
+ if (p[0] == '-') {
+ ++p;
+ ++u; /* two's complement */
+ }
+
+ r = xstrtou(_range_sfx)(p, base, 0, u, suffixes);
+
+ if (*numstr == '-') {
+ r = -r;
+ }
+
+ if (r < lower || r > upper) {
+ bb_error_msg_and_die("number %s is not in %lld..%lld range",
+ numstr, (long long)lower, (long long)upper);
+ }
+
+ return r;
+}
+
+type xstrto(_range)(const char *numstr, int base, type lower, type upper)
+{
+ return xstrto(_range_sfx)(numstr, base, lower, upper, NULL);
+}
+
+type xato(_range_sfx)(const char *numstr,
+ type lower,
+ type upper,
+ const struct suffix_mult *suffixes)
+{
+ return xstrto(_range_sfx)(numstr, 10, lower, upper, suffixes);
+}
+
+type xato(_range)(const char *numstr, type lower, type upper)
+{
+ return xstrto(_range_sfx)(numstr, 10, lower, upper, NULL);
+}
+
+type xato(_sfx)(const char *numstr, const struct suffix_mult *suffixes)
+{
+ return xstrto(_range_sfx)(numstr, 10, XSTR_TYPE_MIN, XSTR_TYPE_MAX, suffixes);
+}
+
+type xato()(const char *numstr)
+{
+ return xstrto(_range_sfx)(numstr, 10, XSTR_TYPE_MIN, XSTR_TYPE_MAX, NULL);
+}
+
+#undef type
+#undef xstrtou
+#undef xstrto
+#undef xatou
+#undef xato
+#undef XSTR_UTYPE_MAX
+#undef XSTR_TYPE_MAX
+#undef XSTR_TYPE_MIN
+#undef XSTR_STRTOU
diff --git a/i/pc104/initrd/conf/busybox/libbb/xconnect.c b/i/pc104/initrd/conf/busybox/libbb/xconnect.c
new file mode 100644
index 0000000..118fe3e
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/xconnect.c
@@ -0,0 +1,330 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Connect to host at port using address resolution from getaddrinfo
+ *
+ */
+
+#include <netinet/in.h>
+#include "libbb.h"
+
+int setsockopt_reuseaddr(int fd)
+{
+ return setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &const_int_1, sizeof(const_int_1));
+}
+int setsockopt_broadcast(int fd)
+{
+ return setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &const_int_1, sizeof(const_int_1));
+}
+
+void xconnect(int s, const struct sockaddr *s_addr, socklen_t addrlen)
+{
+ if (connect(s, s_addr, addrlen) < 0) {
+ if (ENABLE_FEATURE_CLEAN_UP)
+ close(s);
+ if (s_addr->sa_family == AF_INET)
+ bb_perror_msg_and_die("%s (%s)",
+ "cannot connect to remote host",
+ inet_ntoa(((struct sockaddr_in *)s_addr)->sin_addr));
+ bb_perror_msg_and_die("cannot connect to remote host");
+ }
+}
+
+/* Return port number for a service.
+ * If "port" is a number use it as the port.
+ * If "port" is a name it is looked up in /etc/services, if it isnt found return
+ * default_port */
+unsigned bb_lookup_port(const char *port, const char *protocol, unsigned default_port)
+{
+ unsigned port_nr = default_port;
+ if (port) {
+ int old_errno;
+
+ /* Since this is a lib function, we're not allowed to reset errno to 0.
+ * Doing so could break an app that is deferring checking of errno. */
+ old_errno = errno;
+ port_nr = bb_strtou(port, NULL, 10);
+ if (errno || port_nr > 65535) {
+ struct servent *tserv = getservbyname(port, protocol);
+ port_nr = default_port;
+ if (tserv)
+ port_nr = ntohs(tserv->s_port);
+ }
+ errno = old_errno;
+ }
+ return (uint16_t)port_nr;
+}
+
+
+/* "Old" networking API - only IPv4 */
+
+/*
+void bb_lookup_host(struct sockaddr_in *s_in, const char *host)
+{
+ struct hostent *he;
+
+ memset(s_in, 0, sizeof(struct sockaddr_in));
+ s_in->sin_family = AF_INET;
+ he = xgethostbyname(host);
+ memcpy(&(s_in->sin_addr), he->h_addr_list[0], he->h_length);
+}
+
+
+int xconnect_tcp_v4(struct sockaddr_in *s_addr)
+{
+ int s = xsocket(AF_INET, SOCK_STREAM, 0);
+ xconnect(s, (struct sockaddr*) s_addr, sizeof(*s_addr));
+ return s;
+}
+*/
+
+/* "New" networking API */
+
+
+int get_nport(const len_and_sockaddr *lsa)
+{
+#if ENABLE_FEATURE_IPV6
+ if (lsa->sa.sa_family == AF_INET6) {
+ return lsa->sin6.sin6_port;
+ }
+#endif
+ if (lsa->sa.sa_family == AF_INET) {
+ return lsa->sin.sin_port;
+ }
+ /* What? UNIX socket? IPX?? :) */
+ return -1;
+}
+
+void set_nport(len_and_sockaddr *lsa, unsigned port)
+{
+#if ENABLE_FEATURE_IPV6
+ if (lsa->sa.sa_family == AF_INET6) {
+ lsa->sin6.sin6_port = port;
+ return;
+ }
+#endif
+ if (lsa->sa.sa_family == AF_INET) {
+ lsa->sin.sin_port = port;
+ return;
+ }
+ /* What? UNIX socket? IPX?? :) */
+}
+
+/* We hijack this constant to mean something else */
+/* It doesn't hurt because we will remove this bit anyway */
+#define DIE_ON_ERROR AI_CANONNAME
+
+/* host: "1.2.3.4[:port]", "www.google.com[:port]"
+ * port: if neither of above specifies port # */
+static len_and_sockaddr* str2sockaddr(
+ const char *host, int port,
+USE_FEATURE_IPV6(sa_family_t af,)
+ int ai_flags)
+{
+ int rc;
+ len_and_sockaddr *r = NULL;
+ struct addrinfo *result = NULL;
+ const char *org_host = host; /* only for error msg */
+ const char *cp;
+ struct addrinfo hint;
+
+ /* Ugly parsing of host:addr */
+ if (ENABLE_FEATURE_IPV6 && host[0] == '[') {
+ host++;
+ cp = strchr(host, ']');
+ if (!cp || cp[1] != ':') /* Malformed: must have [xx]:nn */
+ bb_error_msg_and_die("bad address '%s'", org_host);
+ //return r; /* return NULL */
+ } else {
+ cp = strrchr(host, ':');
+ if (ENABLE_FEATURE_IPV6 && cp && strchr(host, ':') != cp) {
+ /* There is more than one ':' (e.g. "::1") */
+ cp = NULL; /* it's not a port spec */
+ }
+ }
+ if (cp) {
+ int sz = cp - host + 1;
+ host = safe_strncpy(alloca(sz), host, sz);
+ if (ENABLE_FEATURE_IPV6 && *cp != ':')
+ cp++; /* skip ']' */
+ cp++; /* skip ':' */
+ port = xatou16(cp);
+ }
+
+ memset(&hint, 0 , sizeof(hint));
+#if !ENABLE_FEATURE_IPV6
+ hint.ai_family = AF_INET; /* do not try to find IPv6 */
+#else
+ hint.ai_family = af;
+#endif
+ /* Needed. Or else we will get each address thrice (or more)
+ * for each possible socket type (tcp,udp,raw...): */
+ hint.ai_socktype = SOCK_STREAM;
+ hint.ai_flags = ai_flags & ~DIE_ON_ERROR;
+ rc = getaddrinfo(host, NULL, &hint, &result);
+ if (rc || !result) {
+ bb_error_msg("bad address '%s'", org_host);
+ if (ai_flags & DIE_ON_ERROR)
+ sleep_and_die();
+ goto ret;
+ }
+ r = xmalloc(offsetof(len_and_sockaddr, sa) + result->ai_addrlen);
+ r->len = result->ai_addrlen;
+ memcpy(&r->sa, result->ai_addr, result->ai_addrlen);
+ set_nport(r, htons(port));
+ ret:
+ freeaddrinfo(result);
+ return r;
+}
+#if !ENABLE_FEATURE_IPV6
+#define str2sockaddr(host, port, af, ai_flags) str2sockaddr(host, port, ai_flags)
+#endif
+
+#if ENABLE_FEATURE_IPV6
+len_and_sockaddr* host_and_af2sockaddr(const char *host, int port, sa_family_t af)
+{
+ return str2sockaddr(host, port, af, 0);
+}
+
+len_and_sockaddr* xhost_and_af2sockaddr(const char *host, int port, sa_family_t af)
+{
+ return str2sockaddr(host, port, af, DIE_ON_ERROR);
+}
+#endif
+
+len_and_sockaddr* host2sockaddr(const char *host, int port)
+{
+ return str2sockaddr(host, port, AF_UNSPEC, 0);
+}
+
+len_and_sockaddr* xhost2sockaddr(const char *host, int port)
+{
+ return str2sockaddr(host, port, AF_UNSPEC, DIE_ON_ERROR);
+}
+
+len_and_sockaddr* xdotted2sockaddr(const char *host, int port)
+{
+ return str2sockaddr(host, port, AF_UNSPEC, AI_NUMERICHOST | DIE_ON_ERROR);
+}
+
+int xsocket_stream(len_and_sockaddr **lsap)
+{
+ len_and_sockaddr *lsa;
+ int fd;
+ int len = sizeof(struct sockaddr_in);
+ int family = AF_INET;
+
+#if ENABLE_FEATURE_IPV6
+ fd = socket(AF_INET6, SOCK_STREAM, 0);
+ if (fd >= 0) {
+ len = sizeof(struct sockaddr_in6);
+ family = AF_INET6;
+ } else
+#endif
+ {
+ fd = xsocket(AF_INET, SOCK_STREAM, 0);
+ }
+ lsa = xzalloc(offsetof(len_and_sockaddr, sa) + len);
+ lsa->len = len;
+ lsa->sa.sa_family = family;
+ *lsap = lsa;
+ return fd;
+}
+
+int create_and_bind_stream_or_die(const char *bindaddr, int port)
+{
+ int fd;
+ len_and_sockaddr *lsa;
+
+ if (bindaddr && bindaddr[0]) {
+ lsa = xdotted2sockaddr(bindaddr, port);
+ /* user specified bind addr dictates family */
+ fd = xsocket(lsa->sa.sa_family, SOCK_STREAM, 0);
+ } else {
+ fd = xsocket_stream(&lsa);
+ set_nport(lsa, htons(port));
+ }
+ setsockopt_reuseaddr(fd);
+ xbind(fd, &lsa->sa, lsa->len);
+ free(lsa);
+ return fd;
+}
+
+int create_and_connect_stream_or_die(const char *peer, int port)
+{
+ int fd;
+ len_and_sockaddr *lsa;
+
+ lsa = xhost2sockaddr(peer, port);
+ fd = xsocket(lsa->sa.sa_family, SOCK_STREAM, 0);
+ setsockopt_reuseaddr(fd);
+ xconnect(fd, &lsa->sa, lsa->len);
+ free(lsa);
+ return fd;
+}
+
+int xconnect_stream(const len_and_sockaddr *lsa)
+{
+ int fd = xsocket(lsa->sa.sa_family, SOCK_STREAM, 0);
+ xconnect(fd, &lsa->sa, lsa->len);
+ return fd;
+}
+
+/* We hijack this constant to mean something else */
+/* It doesn't hurt because we will add this bit anyway */
+#define IGNORE_PORT NI_NUMERICSERV
+static char* sockaddr2str(const struct sockaddr *sa, socklen_t salen, int flags)
+{
+ char host[128];
+ char serv[16];
+ int rc = getnameinfo(sa, salen,
+ host, sizeof(host),
+ /* can do ((flags & IGNORE_PORT) ? NULL : serv) but why bother? */
+ serv, sizeof(serv),
+ /* do not resolve port# into service _name_ */
+ flags | NI_NUMERICSERV
+ );
+ if (rc)
+ return NULL;
+ if (flags & IGNORE_PORT)
+ return xstrdup(host);
+#if ENABLE_FEATURE_IPV6
+ if (sa->sa_family == AF_INET6) {
+ if (strchr(host, ':')) /* heh, it's not a resolved hostname */
+ return xasprintf("[%s]:%s", host, serv);
+ /*return xasprintf("%s:%s", host, serv);*/
+ /* - fall through instead */
+ }
+#endif
+ /* For now we don't support anything else, so it has to be INET */
+ /*if (sa->sa_family == AF_INET)*/
+ return xasprintf("%s:%s", host, serv);
+ /*return xstrdup(host);*/
+}
+
+char* xmalloc_sockaddr2host(const struct sockaddr *sa, socklen_t salen)
+{
+ return sockaddr2str(sa, salen, 0);
+}
+
+/* Unused
+char* xmalloc_sockaddr2host_noport(const struct sockaddr *sa, socklen_t salen)
+{
+ return sockaddr2str(sa, salen, IGNORE_PORT);
+}
+*/
+
+char* xmalloc_sockaddr2hostonly_noport(const struct sockaddr *sa, socklen_t salen)
+{
+ return sockaddr2str(sa, salen, NI_NAMEREQD | IGNORE_PORT);
+}
+char* xmalloc_sockaddr2dotted(const struct sockaddr *sa, socklen_t salen)
+{
+ return sockaddr2str(sa, salen, NI_NUMERICHOST);
+}
+
+char* xmalloc_sockaddr2dotted_noport(const struct sockaddr *sa, socklen_t salen)
+{
+ return sockaddr2str(sa, salen, NI_NUMERICHOST | IGNORE_PORT);
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/xfuncs.c b/i/pc104/initrd/conf/busybox/libbb/xfuncs.c
new file mode 100644
index 0000000..2cc6a82
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/xfuncs.c
@@ -0,0 +1,616 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ * Copyright (C) 2006 Rob Landley
+ * Copyright (C) 2006 Denis Vlasenko
+ *
+ * Licensed under GPL version 2, see file LICENSE in this tarball for details.
+ */
+
+#include "busybox.h"
+
+/* All the functions starting with "x" call bb_error_msg_and_die() if they
+ * fail, so callers never need to check for errors. If it returned, it
+ * succeeded. */
+
+#ifndef DMALLOC
+/* dmalloc provides variants of these that do abort() on failure.
+ * Since dmalloc's prototypes overwrite the impls here as they are
+ * included after these prototypes in libbb.h, all is well.
+ */
+// Die if we can't allocate size bytes of memory.
+void *xmalloc(size_t size)
+{
+ void *ptr = malloc(size);
+ if (ptr == NULL && size != 0)
+ bb_error_msg_and_die(bb_msg_memory_exhausted);
+ return ptr;
+}
+
+// Die if we can't resize previously allocated memory. (This returns a pointer
+// to the new memory, which may or may not be the same as the old memory.
+// It'll copy the contents to a new chunk and free the old one if necessary.)
+void *xrealloc(void *ptr, size_t size)
+{
+ ptr = realloc(ptr, size);
+ if (ptr == NULL && size != 0)
+ bb_error_msg_and_die(bb_msg_memory_exhausted);
+ return ptr;
+}
+#endif /* DMALLOC */
+
+// Die if we can't allocate and zero size bytes of memory.
+void *xzalloc(size_t size)
+{
+ void *ptr = xmalloc(size);
+ memset(ptr, 0, size);
+ return ptr;
+}
+
+// Die if we can't copy a string to freshly allocated memory.
+char * xstrdup(const char *s)
+{
+ char *t;
+
+ if (s == NULL)
+ return NULL;
+
+ t = strdup(s);
+
+ if (t == NULL)
+ bb_error_msg_and_die(bb_msg_memory_exhausted);
+
+ return t;
+}
+
+// Die if we can't allocate n+1 bytes (space for the null terminator) and copy
+// the (possibly truncated to length n) string into it.
+char * xstrndup(const char *s, int n)
+{
+ int m;
+ char *t;
+
+ if (ENABLE_DEBUG && s == NULL)
+ bb_error_msg_and_die("xstrndup bug");
+
+ /* We can just xmalloc(n+1) and strncpy into it, */
+ /* but think about xstrndup("abc", 10000) wastage! */
+ m = n;
+ t = (char*) s;
+ while (m) {
+ if (!*t) break;
+ m--;
+ t++;
+ }
+ n -= m;
+ t = xmalloc(n + 1);
+ t[n] = '\0';
+
+ return memcpy(t, s, n);
+}
+
+// Die if we can't open a file and return a FILE * to it.
+// Notice we haven't got xfread(), This is for use with fscanf() and friends.
+FILE *xfopen(const char *path, const char *mode)
+{
+ FILE *fp = fopen(path, mode);
+ if (fp == NULL)
+ bb_perror_msg_and_die("%s", path);
+ return fp;
+}
+
+// Die if we can't open an existing file and return an fd.
+int xopen(const char *pathname, int flags)
+{
+ //if (ENABLE_DEBUG && (flags & O_CREAT))
+ // bb_error_msg_and_die("xopen() with O_CREAT");
+
+ return xopen3(pathname, flags, 0666);
+}
+
+// Die if we can't open a new file and return an fd.
+int xopen3(const char *pathname, int flags, int mode)
+{
+ int ret;
+
+ ret = open(pathname, flags, mode);
+ if (ret < 0) {
+ bb_perror_msg_and_die("%s", pathname);
+ }
+ return ret;
+}
+
+// Turn on nonblocking I/O on a fd
+int ndelay_on(int fd)
+{
+ return fcntl(fd,F_SETFL,fcntl(fd,F_GETFL,0) | O_NONBLOCK);
+}
+
+int ndelay_off(int fd)
+{
+ return fcntl(fd,F_SETFL,fcntl(fd,F_GETFL,0) & ~O_NONBLOCK);
+}
+
+// Die with an error message if we can't write the entire buffer.
+void xwrite(int fd, const void *buf, size_t count)
+{
+ if (count) {
+ ssize_t size = full_write(fd, buf, count);
+ if (size != count)
+ bb_error_msg_and_die("short write");
+ }
+}
+
+// Die with an error message if we can't lseek to the right spot.
+off_t xlseek(int fd, off_t offset, int whence)
+{
+ off_t off = lseek(fd, offset, whence);
+ if (off == (off_t)-1) {
+ if (whence == SEEK_SET)
+ bb_perror_msg_and_die("lseek(%"OFF_FMT"u)", offset);
+ bb_perror_msg_and_die("lseek");
+ }
+ return off;
+}
+
+// Die with supplied filename if this FILE * has ferror set.
+void die_if_ferror(FILE *fp, const char *fn)
+{
+ if (ferror(fp)) {
+ bb_error_msg_and_die("%s: I/O error", fn);
+ }
+}
+
+// Die with an error message if stdout has ferror set.
+void die_if_ferror_stdout(void)
+{
+ die_if_ferror(stdout, bb_msg_standard_output);
+}
+
+// Die with an error message if we have trouble flushing stdout.
+void xfflush_stdout(void)
+{
+ if (fflush(stdout)) {
+ bb_perror_msg_and_die(bb_msg_standard_output);
+ }
+}
+
+// This does a fork/exec in one call, using vfork(). Return PID of new child,
+// -1 for failure. Runs argv[0], searching path if that has no / in it.
+pid_t spawn(char **argv)
+{
+ /* Why static? */
+ static int failed;
+ pid_t pid;
+
+ // Be nice to nommu machines.
+ failed = 0;
+ pid = vfork();
+ if (pid < 0) return pid;
+ if (!pid) {
+ BB_EXECVP(argv[0], argv);
+
+ // We're sharing a stack with blocked parent, let parent know we failed
+ // and then exit to unblock parent (but don't run atexit() stuff, which
+ // would screw up parent.)
+
+ failed = errno;
+ _exit(0);
+ }
+ if (failed) {
+ errno = failed;
+ return -1;
+ }
+ return pid;
+}
+
+// Die with an error message if we can't spawn a child process.
+pid_t xspawn(char **argv)
+{
+ pid_t pid = spawn(argv);
+ if (pid < 0) bb_perror_msg_and_die("%s", *argv);
+ return pid;
+}
+
+// Wait for the specified child PID to exit, returning child's error return.
+int wait4pid(int pid)
+{
+ int status;
+
+ if (pid == -1 || waitpid(pid, &status, 0) == -1) return -1;
+ if (WIFEXITED(status)) return WEXITSTATUS(status);
+ if (WIFSIGNALED(status)) return WTERMSIG(status);
+ return 0;
+}
+
+void xsetenv(const char *key, const char *value)
+{
+ if (setenv(key, value, 1))
+ bb_error_msg_and_die(bb_msg_memory_exhausted);
+}
+
+// Converts unsigned long long value into compact 4-char
+// representation. Examples: "1234", "1.2k", " 27M", "123T"
+// Fifth char is always '\0'
+void smart_ulltoa5(unsigned long long ul, char buf[5])
+{
+ const char *fmt;
+ char c;
+ unsigned v,idx = 0;
+ ul *= 10;
+ if (ul > 9999*10) { // do not scale if 9999 or less
+ while (ul >= 10000) {
+ ul /= 1024;
+ idx++;
+ }
+ }
+ v = ul; // ullong divisions are expensive, avoid them
+
+ fmt = " 123456789";
+ if (!idx) { // 9999 or less: use 1234 format
+ c = buf[0] = " 123456789"[v/10000];
+ if (c!=' ') fmt = "0123456789";
+ c = buf[1] = fmt[v/1000%10];
+ if (c!=' ') fmt = "0123456789";
+ buf[2] = fmt[v/100%10];
+ buf[3] = "0123456789"[v/10%10];
+ } else {
+ if (v>=10*10) { // scaled value is >=10: use 123M format
+ c = buf[0] = " 123456789"[v/1000];
+ if (c!=' ') fmt = "0123456789";
+ buf[1] = fmt[v/100%10];
+ buf[2] = "0123456789"[v/10%10];
+ } else { // scaled value is <10: use 1.2M format
+ buf[0] = "0123456789"[v/10];
+ buf[1] = '.';
+ buf[2] = "0123456789"[v%10];
+ }
+ // see http://en.wikipedia.org/wiki/Tera
+ buf[3] = " kMGTPEZY"[idx];
+ }
+ buf[4] = '\0';
+}
+
+// Convert unsigned integer to ascii, writing into supplied buffer. A
+// truncated result is always null terminated (unless buflen is 0), and
+// contains the first few digits of the result ala strncpy.
+void BUG_sizeof_unsigned_not_4(void);
+void utoa_to_buf(unsigned n, char *buf, unsigned buflen)
+{
+ unsigned i, out, res;
+ if (sizeof(unsigned) != 4)
+ BUG_sizeof_unsigned_not_4();
+ if (buflen) {
+ out = 0;
+ for (i = 1000000000; i; i /= 10) {
+ res = n / i;
+ if (res || out || i == 1) {
+ if (!--buflen) break;
+ out++;
+ n -= res*i;
+ *buf++ = '0' + res;
+ }
+ }
+ *buf = '\0';
+ }
+}
+
+// Convert signed integer to ascii, like utoa_to_buf()
+void itoa_to_buf(int n, char *buf, unsigned buflen)
+{
+ if (buflen && n<0) {
+ n = -n;
+ *buf++ = '-';
+ buflen--;
+ }
+ utoa_to_buf((unsigned)n, buf, buflen);
+}
+
+// The following two functions use a static buffer, so calling either one a
+// second time will overwrite previous results.
+//
+// The largest 32 bit integer is -2 billion plus null terminator, or 12 bytes.
+// Int should always be 32 bits on any remotely Unix-like system, see
+// http://www.unix.org/whitepapers/64bit.html for the reasons why.
+
+static char local_buf[12];
+
+// Convert unsigned integer to ascii using a static buffer (returned).
+char *utoa(unsigned n)
+{
+ utoa_to_buf(n, local_buf, sizeof(local_buf));
+
+ return local_buf;
+}
+
+// Convert signed integer to ascii using a static buffer (returned).
+char *itoa(int n)
+{
+ itoa_to_buf(n, local_buf, sizeof(local_buf));
+
+ return local_buf;
+}
+
+// Emit a string of hex representation of bytes
+char *bin2hex(char *p, const char *cp, int count)
+{
+ while (count) {
+ unsigned char c = *cp++;
+ /* put lowercase hex digits */
+ *p++ = 0x20 | bb_hexdigits_upcase[c >> 4];
+ *p++ = 0x20 | bb_hexdigits_upcase[c & 0xf];
+ count--;
+ }
+ return p;
+}
+
+// Die with an error message if we can't set gid. (Because resource limits may
+// limit this user to a given number of processes, and if that fills up the
+// setgid() will fail and we'll _still_be_root_, which is bad.)
+void xsetgid(gid_t gid)
+{
+ if (setgid(gid)) bb_error_msg_and_die("setgid");
+}
+
+// Die with an error message if we can't set uid. (See xsetgid() for why.)
+void xsetuid(uid_t uid)
+{
+ if (setuid(uid)) bb_error_msg_and_die("setuid");
+}
+
+// Return how long the file at fd is, if there's any way to determine it.
+off_t fdlength(int fd)
+{
+ off_t bottom = 0, top = 0, pos;
+ long size;
+
+ // If the ioctl works for this, return it.
+
+ if (ioctl(fd, BLKGETSIZE, &size) >= 0) return size*512;
+
+ // FIXME: explain why lseek(SEEK_END) is not used here!
+
+ // If not, do a binary search for the last location we can read. (Some
+ // block devices don't do BLKGETSIZE right.)
+
+ do {
+ char temp;
+
+ pos = bottom + (top - bottom) / 2;
+
+ // If we can read from the current location, it's bigger.
+
+ if (lseek(fd, pos, SEEK_SET)>=0 && safe_read(fd, &temp, 1)==1) {
+ if (bottom == top) bottom = top = (top+1) * 2;
+ else bottom = pos;
+
+ // If we can't, it's smaller.
+
+ } else {
+ if (bottom == top) {
+ if (!top) return 0;
+ bottom = top/2;
+ }
+ else top = pos;
+ }
+ } while (bottom + 1 != top);
+
+ return pos + 1;
+}
+
+// Die with an error message if we can't malloc() enough space and do an
+// sprintf() into that space.
+char *xasprintf(const char *format, ...)
+{
+ va_list p;
+ int r;
+ char *string_ptr;
+
+#if 1
+ // GNU extension
+ va_start(p, format);
+ r = vasprintf(&string_ptr, format, p);
+ va_end(p);
+#else
+ // Bloat for systems that haven't got the GNU extension.
+ va_start(p, format);
+ r = vsnprintf(NULL, 0, format, p);
+ va_end(p);
+ string_ptr = xmalloc(r+1);
+ va_start(p, format);
+ r = vsnprintf(string_ptr, r+1, format, p);
+ va_end(p);
+#endif
+
+ if (r < 0) bb_error_msg_and_die(bb_msg_memory_exhausted);
+ return string_ptr;
+}
+
+#if 0 /* If we will ever meet a libc which hasn't [f]dprintf... */
+int fdprintf(int fd, const char *format, ...)
+{
+ va_list p;
+ int r;
+ char *string_ptr;
+
+#if 1
+ // GNU extension
+ va_start(p, format);
+ r = vasprintf(&string_ptr, format, p);
+ va_end(p);
+#else
+ // Bloat for systems that haven't got the GNU extension.
+ va_start(p, format);
+ r = vsnprintf(NULL, 0, format, p);
+ va_end(p);
+ string_ptr = xmalloc(r+1);
+ va_start(p, format);
+ r = vsnprintf(string_ptr, r+1, format, p);
+ va_end(p);
+#endif
+
+ if (r >= 0) {
+ full_write(fd, string_ptr, r);
+ free(string_ptr);
+ }
+ return r;
+}
+#endif
+
+// Die with an error message if we can't copy an entire FILE * to stdout, then
+// close that file.
+void xprint_and_close_file(FILE *file)
+{
+ fflush(stdout);
+ // copyfd outputs error messages for us.
+ if (bb_copyfd_eof(fileno(file), 1) == -1)
+ exit(xfunc_error_retval);
+
+ fclose(file);
+}
+
+// Die if we can't chdir to a new path.
+void xchdir(const char *path)
+{
+ if (chdir(path))
+ bb_perror_msg_and_die("chdir(%s)", path);
+}
+
+// Print a warning message if opendir() fails, but don't die.
+DIR *warn_opendir(const char *path)
+{
+ DIR *dp;
+
+ if ((dp = opendir(path)) == NULL) {
+ bb_perror_msg("cannot open '%s'", path);
+ return NULL;
+ }
+ return dp;
+}
+
+// Die with an error message if opendir() fails.
+DIR *xopendir(const char *path)
+{
+ DIR *dp;
+
+ if ((dp = opendir(path)) == NULL)
+ bb_perror_msg_and_die("cannot open '%s'", path);
+ return dp;
+}
+
+#ifndef BB_NOMMU
+// Die with an error message if we can't daemonize.
+void xdaemon(int nochdir, int noclose)
+{
+ if (daemon(nochdir, noclose))
+ bb_perror_msg_and_die("daemon");
+}
+#endif
+
+void bb_sanitize_stdio_maybe_daemonize(int daemonize)
+{
+ int fd;
+ /* Mega-paranoid */
+ fd = xopen(bb_dev_null, O_RDWR);
+ while ((unsigned)fd < 2)
+ fd = dup(fd); /* have 0,1,2 open at least to /dev/null */
+ if (daemonize) {
+ pid_t pid = fork();
+ if (pid < 0) /* wtf? */
+ bb_perror_msg_and_die("fork");
+ if (pid) /* parent */
+ exit(0);
+ /* child */
+ /* if daemonizing, make sure we detach from stdio */
+ setsid();
+ dup2(fd, 0);
+ dup2(fd, 1);
+ dup2(fd, 2);
+ }
+ while (fd > 2)
+ close(fd--); /* close everything after fd#2 */
+}
+void bb_sanitize_stdio(void)
+{
+ bb_sanitize_stdio_maybe_daemonize(0);
+}
+void bb_daemonize(void)
+{
+ bb_sanitize_stdio_maybe_daemonize(1);
+}
+
+// Die with an error message if we can't open a new socket.
+int xsocket(int domain, int type, int protocol)
+{
+ int r = socket(domain, type, protocol);
+
+ if (r < 0) bb_perror_msg_and_die("socket");
+
+ return r;
+}
+
+// Die with an error message if we can't bind a socket to an address.
+void xbind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen)
+{
+ if (bind(sockfd, my_addr, addrlen)) bb_perror_msg_and_die("bind");
+}
+
+// Die with an error message if we can't listen for connections on a socket.
+void xlisten(int s, int backlog)
+{
+ if (listen(s, backlog)) bb_perror_msg_and_die("listen");
+}
+
+// xstat() - a stat() which dies on failure with meaningful error message
+void xstat(const char *name, struct stat *stat_buf)
+{
+ if (stat(name, stat_buf))
+ bb_perror_msg_and_die("can't stat '%s'", name);
+}
+
+// selinux_or_die() - die if SELinux is disabled.
+void selinux_or_die(void)
+{
+#if ENABLE_SELINUX
+ int rc = is_selinux_enabled();
+ if (rc == 0) {
+ bb_error_msg_and_die("SELinux is disabled");
+ } else if (rc < 0) {
+ bb_error_msg_and_die("is_selinux_enabled() failed");
+ }
+#else
+ bb_error_msg_and_die("SELinux support is disabled");
+#endif
+}
+
+/* It is perfectly ok to pass in a NULL for either width or for
+ * height, in which case that value will not be set. */
+int get_terminal_width_height(const int fd, int *width, int *height)
+{
+ struct winsize win = { 0, 0, 0, 0 };
+ int ret = ioctl(fd, TIOCGWINSZ, &win);
+
+ if (height) {
+ if (!win.ws_row) {
+ char *s = getenv("LINES");
+ if (s) win.ws_row = atoi(s);
+ }
+ if (win.ws_row <= 1 || win.ws_row >= 30000)
+ win.ws_row = 24;
+ *height = (int) win.ws_row;
+ }
+
+ if (width) {
+ if (!win.ws_col) {
+ char *s = getenv("COLUMNS");
+ if (s) win.ws_col = atoi(s);
+ }
+ if (win.ws_col <= 1 || win.ws_col >= 30000)
+ win.ws_col = 80;
+ *width = (int) win.ws_col;
+ }
+
+ return ret;
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/xgetcwd.c b/i/pc104/initrd/conf/busybox/libbb/xgetcwd.c
new file mode 100644
index 0000000..ec1d8f7
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/xgetcwd.c
@@ -0,0 +1,44 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * xgetcwd.c -- return current directory with unlimited length
+ * Copyright (C) 1992, 1996 Free Software Foundation, Inc.
+ * Written by David MacKenzie <djm@gnu.ai.mit.edu>.
+ *
+ * Special function for busybox written by Vladimir Oleynik <dzo@simtreas.ru>
+*/
+
+#include "libbb.h"
+
+/* Amount to increase buffer size by in each try. */
+#define PATH_INCR 32
+
+/* Return the current directory, newly allocated, arbitrarily long.
+ Return NULL and set errno on error.
+ If argument is not NULL (previous usage allocate memory), call free()
+*/
+
+char *
+xrealloc_getcwd_or_warn(char *cwd)
+{
+ char *ret;
+ unsigned path_max;
+
+ path_max = (unsigned) PATH_MAX;
+ path_max += 2; /* The getcwd docs say to do this. */
+
+ if (cwd == NULL)
+ cwd = xmalloc(path_max);
+
+ while ((ret = getcwd(cwd, path_max)) == NULL && errno == ERANGE) {
+ path_max += PATH_INCR;
+ cwd = xrealloc(cwd, path_max);
+ }
+
+ if (ret == NULL) {
+ free(cwd);
+ bb_perror_msg("getcwd");
+ return NULL;
+ }
+
+ return cwd;
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/xgethostbyname.c b/i/pc104/initrd/conf/busybox/libbb/xgethostbyname.c
new file mode 100644
index 0000000..c3158c3
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/xgethostbyname.c
@@ -0,0 +1,19 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini xgethostbyname implementation.
+ *
+ * Copyright (C) 2001 Matt Kraai <kraai@alumni.carnegiemellon.edu>.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include <netdb.h>
+#include "libbb.h"
+
+struct hostent *xgethostbyname(const char *name)
+{
+ struct hostent *retval = gethostbyname(name);
+ if (!retval)
+ bb_herror_msg_and_die("%s", name);
+ return retval;
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/xreadlink.c b/i/pc104/initrd/conf/busybox/libbb/xreadlink.c
new file mode 100644
index 0000000..18a8b94
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/xreadlink.c
@@ -0,0 +1,48 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * xreadlink.c - safe implementation of readlink.
+ * Returns a NULL on failure...
+ */
+
+#include "libbb.h"
+
+/*
+ * NOTE: This function returns a malloced char* that you will have to free
+ * yourself. You have been warned.
+ */
+
+char *xmalloc_readlink_or_warn(const char *path)
+{
+ enum { GROWBY = 80 }; /* how large we will grow strings by */
+
+ char *buf = NULL;
+ int bufsize = 0, readsize = 0;
+
+ do {
+ buf = xrealloc(buf, bufsize += GROWBY);
+ readsize = readlink(path, buf, bufsize); /* 1st try */
+ if (readsize == -1) {
+ bb_perror_msg("%s", path);
+ free(buf);
+ return NULL;
+ }
+ }
+ while (bufsize < readsize + 1);
+
+ buf[readsize] = '\0';
+
+ return buf;
+}
+
+char *xmalloc_realpath(const char *path)
+{
+#if defined(__GLIBC__) && !defined(__UCLIBC__)
+ /* glibc provides a non-standard extension */
+ return realpath(path, NULL);
+#else
+ char buf[PATH_MAX+1];
+
+ /* on error returns NULL (xstrdup(NULL) ==NULL) */
+ return xstrdup(realpath(path, buf));
+#endif
+}
diff --git a/i/pc104/initrd/conf/busybox/libbb/xregcomp.c b/i/pc104/initrd/conf/busybox/libbb/xregcomp.c
new file mode 100644
index 0000000..157132c
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libbb/xregcomp.c
@@ -0,0 +1,32 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) many different people.
+ * If you wrote this, please acknowledge your work.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "libbb.h"
+#include "xregex.h"
+
+char* regcomp_or_errmsg(regex_t *preg, const char *regex, int cflags)
+{
+ int ret = regcomp(preg, regex, cflags);
+ if (ret) {
+ int errmsgsz = regerror(ret, preg, NULL, 0);
+ char *errmsg = xmalloc(errmsgsz);
+ regerror(ret, preg, errmsg, errmsgsz);
+ return errmsg;
+ }
+ return NULL;
+}
+
+void xregcomp(regex_t *preg, const char *regex, int cflags)
+{
+ char *errmsg = regcomp_or_errmsg(preg, regex, cflags);
+ if (errmsg) {
+ bb_error_msg_and_die("xregcomp: %s", errmsg);
+ }
+}
diff --git a/i/pc104/initrd/conf/busybox/libpwdgrp/Kbuild b/i/pc104/initrd/conf/busybox/libpwdgrp/Kbuild
new file mode 100644
index 0000000..9e60ef1
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libpwdgrp/Kbuild
@@ -0,0 +1,7 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under the GPL v2, see the file LICENSE in this tarball.
+
+lib-y:=pwd_grp.o uidgid_get.o
diff --git a/i/pc104/initrd/conf/busybox/libpwdgrp/pwd_grp.c b/i/pc104/initrd/conf/busybox/libpwdgrp/pwd_grp.c
new file mode 100644
index 0000000..7a8a237
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libpwdgrp/pwd_grp.c
@@ -0,0 +1,997 @@
+/* vi: set sw=4 ts=4: */
+/* Copyright (C) 2003 Manuel Novoa III
+ *
+ * Licensed under GPL v2, or later. See file LICENSE in this tarball.
+ */
+
+/* Nov 6, 2003 Initial version.
+ *
+ * NOTE: This implementation is quite strict about requiring all
+ * field seperators. It also does not allow leading whitespace
+ * except when processing the numeric fields. glibc is more
+ * lenient. See the various glibc difference comments below.
+ *
+ * TODO:
+ * Move to dynamic allocation of (currently statically allocated)
+ * buffers; especially for the group-related functions since
+ * large group member lists will cause error returns.
+ *
+ */
+
+#include "libbb.h"
+#include <features.h>
+#include <assert.h>
+
+#ifndef _PATH_SHADOW
+#define _PATH_SHADOW "/etc/shadow"
+#endif
+#ifndef _PATH_PASSWD
+#define _PATH_PASSWD "/etc/passwd"
+#endif
+#ifndef _PATH_GROUP
+#define _PATH_GROUP "/etc/group"
+#endif
+
+/**********************************************************************/
+/* Sizes for statically allocated buffers. */
+
+/* If you change these values, also change _SC_GETPW_R_SIZE_MAX and
+ * _SC_GETGR_R_SIZE_MAX in libc/unistd/sysconf.c to match */
+#define PWD_BUFFER_SIZE 256
+#define GRP_BUFFER_SIZE 256
+
+/**********************************************************************/
+/* Prototypes for internal functions. */
+
+static int bb__pgsreader(int (*parserfunc)(void *d, char *line), void *data,
+ char *__restrict line_buff, size_t buflen, FILE *f);
+
+static int bb__parsepwent(void *pw, char *line);
+static int bb__parsegrent(void *gr, char *line);
+#if ENABLE_USE_BB_SHADOW
+static int bb__parsespent(void *sp, char *line);
+#endif
+
+/**********************************************************************/
+/* For the various fget??ent_r funcs, return
+ *
+ * 0: success
+ * ENOENT: end-of-file encountered
+ * ERANGE: buflen too small
+ * other error values possible. See bb__pgsreader.
+ *
+ * Also, *result == resultbuf on success and NULL on failure.
+ *
+ * NOTE: glibc difference - For the ENOENT case, glibc also sets errno.
+ * We do not, as it really isn't an error if we reach the end-of-file.
+ * Doing so is analogous to having fgetc() set errno on EOF.
+ */
+/**********************************************************************/
+
+int fgetpwent_r(FILE *__restrict stream, struct passwd *__restrict resultbuf,
+ char *__restrict buffer, size_t buflen,
+ struct passwd **__restrict result)
+{
+ int rv;
+
+ *result = NULL;
+
+ rv = bb__pgsreader(bb__parsepwent, resultbuf, buffer, buflen, stream);
+ if (!rv) {
+ *result = resultbuf;
+ }
+
+ return rv;
+}
+
+int fgetgrent_r(FILE *__restrict stream, struct group *__restrict resultbuf,
+ char *__restrict buffer, size_t buflen,
+ struct group **__restrict result)
+{
+ int rv;
+
+ *result = NULL;
+
+ rv = bb__pgsreader(bb__parsegrent, resultbuf, buffer, buflen, stream);
+ if (!rv) {
+ *result = resultbuf;
+ }
+
+ return rv;
+}
+
+#if ENABLE_USE_BB_SHADOW
+int fgetspent_r(FILE *__restrict stream, struct spwd *__restrict resultbuf,
+ char *__restrict buffer, size_t buflen,
+ struct spwd **__restrict result)
+{
+ int rv;
+
+ *result = NULL;
+
+ rv = bb__pgsreader(bb__parsespent, resultbuf, buffer, buflen, stream);
+ if (!rv) {
+ *result = resultbuf;
+ }
+
+ return rv;
+}
+#endif
+
+/**********************************************************************/
+/* For the various fget??ent funcs, return NULL on failure and a
+ * pointer to the appropriate struct (statically allocated) on success.
+ * TODO: audit & stop using these in bbox, they pull in static buffers */
+/**********************************************************************/
+
+#if 0
+struct passwd *fgetpwent(FILE *stream)
+{
+ static char buffer[PWD_BUFFER_SIZE];
+ static struct passwd resultbuf;
+ struct passwd *result;
+
+ fgetpwent_r(stream, &resultbuf, buffer, sizeof(buffer), &result);
+ return result;
+}
+
+struct group *fgetgrent(FILE *stream)
+{
+ static char buffer[GRP_BUFFER_SIZE];
+ static struct group resultbuf;
+ struct group *result;
+
+ fgetgrent_r(stream, &resultbuf, buffer, sizeof(buffer), &result);
+ return result;
+}
+#endif
+
+#if ENABLE_USE_BB_SHADOW
+#if 0
+struct spwd *fgetspent(FILE *stream)
+{
+ static char buffer[PWD_BUFFER_SIZE];
+ static struct spwd resultbuf;
+ struct spwd *result;
+
+ fgetspent_r(stream, &resultbuf, buffer, sizeof(buffer), &result);
+ return result;
+}
+#endif
+
+int sgetspent_r(const char *string, struct spwd *result_buf,
+ char *buffer, size_t buflen, struct spwd **result)
+{
+ int rv = ERANGE;
+
+ *result = NULL;
+
+ if (buflen < PWD_BUFFER_SIZE) {
+ DO_ERANGE:
+ errno=rv;
+ goto DONE;
+ }
+
+ if (string != buffer) {
+ if (strlen(string) >= buflen) {
+ goto DO_ERANGE;
+ }
+ strcpy(buffer, string);
+ }
+
+ rv = bb__parsespent(result_buf, buffer);
+ if (!rv) {
+ *result = result_buf;
+ }
+
+ DONE:
+ return rv;
+}
+#endif
+
+/**********************************************************************/
+
+#define GETXXKEY_R_FUNC getpwnam_r
+#define GETXXKEY_R_PARSER bb__parsepwent
+#define GETXXKEY_R_ENTTYPE struct passwd
+#define GETXXKEY_R_TEST(ENT) (!strcmp((ENT)->pw_name, key))
+#define GETXXKEY_R_KEYTYPE const char *__restrict
+#define GETXXKEY_R_PATHNAME _PATH_PASSWD
+#include "pwd_grp_internal.c"
+
+#define GETXXKEY_R_FUNC getgrnam_r
+#define GETXXKEY_R_PARSER bb__parsegrent
+#define GETXXKEY_R_ENTTYPE struct group
+#define GETXXKEY_R_TEST(ENT) (!strcmp((ENT)->gr_name, key))
+#define GETXXKEY_R_KEYTYPE const char *__restrict
+#define GETXXKEY_R_PATHNAME _PATH_GROUP
+#include "pwd_grp_internal.c"
+
+#if ENABLE_USE_BB_SHADOW
+#define GETXXKEY_R_FUNC getspnam_r
+#define GETXXKEY_R_PARSER bb__parsespent
+#define GETXXKEY_R_ENTTYPE struct spwd
+#define GETXXKEY_R_TEST(ENT) (!strcmp((ENT)->sp_namp, key))
+#define GETXXKEY_R_KEYTYPE const char *__restrict
+#define GETXXKEY_R_PATHNAME _PATH_SHADOW
+#include "pwd_grp_internal.c"
+#endif
+
+#define GETXXKEY_R_FUNC getpwuid_r
+#define GETXXKEY_R_PARSER bb__parsepwent
+#define GETXXKEY_R_ENTTYPE struct passwd
+#define GETXXKEY_R_TEST(ENT) ((ENT)->pw_uid == key)
+#define GETXXKEY_R_KEYTYPE uid_t
+#define GETXXKEY_R_PATHNAME _PATH_PASSWD
+#include "pwd_grp_internal.c"
+
+#define GETXXKEY_R_FUNC getgrgid_r
+#define GETXXKEY_R_PARSER bb__parsegrent
+#define GETXXKEY_R_ENTTYPE struct group
+#define GETXXKEY_R_TEST(ENT) ((ENT)->gr_gid == key)
+#define GETXXKEY_R_KEYTYPE gid_t
+#define GETXXKEY_R_PATHNAME _PATH_GROUP
+#include "pwd_grp_internal.c"
+
+/**********************************************************************/
+/* TODO: audit & stop using these in bbox, they pull in static buffers */
+
+/* This one has many users */
+struct passwd *getpwuid(uid_t uid)
+{
+ static char buffer[PWD_BUFFER_SIZE];
+ static struct passwd resultbuf;
+ struct passwd *result;
+
+ getpwuid_r(uid, &resultbuf, buffer, sizeof(buffer), &result);
+ return result;
+}
+
+/* This one has many users */
+struct group *getgrgid(gid_t gid)
+{
+ static char buffer[GRP_BUFFER_SIZE];
+ static struct group resultbuf;
+ struct group *result;
+
+ getgrgid_r(gid, &resultbuf, buffer, sizeof(buffer), &result);
+ return result;
+}
+
+#if 0 //ENABLE_USE_BB_SHADOW
+/* This function is non-standard and is currently not built. It seems
+ * to have been created as a reentrant version of the non-standard
+ * functions getspuid. Why getspuid was added, I do not know. */
+int getspuid_r(uid_t uid, struct spwd *__restrict resultbuf,
+ char *__restrict buffer, size_t buflen,
+ struct spwd **__restrict result)
+{
+ int rv;
+ struct passwd *pp;
+ struct passwd password;
+ char pwd_buff[PWD_BUFFER_SIZE];
+
+ *result = NULL;
+ rv = getpwuid_r(uid, &password, pwd_buff, sizeof(pwd_buff), &pp);
+ if (!rv) {
+ rv = getspnam_r(password.pw_name, resultbuf, buffer, buflen, result);
+ }
+
+ return rv;
+}
+
+/* This function is non-standard and is currently not built.
+ * Why it was added, I do not know. */
+struct spwd *getspuid(uid_t uid)
+{
+ static char buffer[PWD_BUFFER_SIZE];
+ static struct spwd resultbuf;
+ struct spwd *result;
+
+ getspuid_r(uid, &resultbuf, buffer, sizeof(buffer), &result);
+ return result;
+}
+#endif
+
+/* This one has many users */
+struct passwd *getpwnam(const char *name)
+{
+ static char buffer[PWD_BUFFER_SIZE];
+ static struct passwd resultbuf;
+ struct passwd *result;
+
+ getpwnam_r(name, &resultbuf, buffer, sizeof(buffer), &result);
+ return result;
+}
+
+/* This one has many users */
+struct group *getgrnam(const char *name)
+{
+ static char buffer[GRP_BUFFER_SIZE];
+ static struct group resultbuf;
+ struct group *result;
+
+ getgrnam_r(name, &resultbuf, buffer, sizeof(buffer), &result);
+ return result;
+}
+
+#if 0 //ENABLE_USE_BB_SHADOW
+struct spwd *getspnam(const char *name)
+{
+ static char buffer[PWD_BUFFER_SIZE];
+ static struct spwd resultbuf;
+ struct spwd *result;
+
+ getspnam_r(name, &resultbuf, buffer, sizeof(buffer), &result);
+ return result;
+}
+#endif
+
+/* This one doesn't use static buffers */
+int getpw(uid_t uid, char *buf)
+{
+ struct passwd resultbuf;
+ struct passwd *result;
+ char buffer[PWD_BUFFER_SIZE];
+
+ if (!buf) {
+ errno = EINVAL;
+ } else if (!getpwuid_r(uid, &resultbuf, buffer, sizeof(buffer), &result)) {
+ if (sprintf(buf, "%s:%s:%lu:%lu:%s:%s:%s\n",
+ resultbuf.pw_name, resultbuf.pw_passwd,
+ (unsigned long)(resultbuf.pw_uid),
+ (unsigned long)(resultbuf.pw_gid),
+ resultbuf.pw_gecos, resultbuf.pw_dir,
+ resultbuf.pw_shell) >= 0
+ ) {
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+/**********************************************************************/
+
+/* FIXME: we don't have such CONFIG_xx - ?! */
+
+#if defined CONFIG_USE_BB_THREADSAFE_SHADOW && defined PTHREAD_MUTEX_INITIALIZER
+static pthread_mutex_t mylock = PTHREAD_MUTEX_INITIALIZER;
+# define LOCK pthread_mutex_lock(&mylock)
+# define UNLOCK pthread_mutex_unlock(&mylock);
+#else
+# define LOCK ((void) 0)
+# define UNLOCK ((void) 0)
+#endif
+
+static FILE *pwf /*= NULL*/;
+void setpwent(void)
+{
+ LOCK;
+ if (pwf) {
+ rewind(pwf);
+ }
+ UNLOCK;
+}
+
+void endpwent(void)
+{
+ LOCK;
+ if (pwf) {
+ fclose(pwf);
+ pwf = NULL;
+ }
+ UNLOCK;
+}
+
+
+int getpwent_r(struct passwd *__restrict resultbuf,
+ char *__restrict buffer, size_t buflen,
+ struct passwd **__restrict result)
+{
+ int rv;
+
+ LOCK;
+ *result = NULL; /* In case of error... */
+
+ if (!pwf) {
+ pwf = fopen(_PATH_PASSWD, "r");
+ if (!pwf) {
+ rv = errno;
+ goto ERR;
+ }
+ }
+
+ rv = bb__pgsreader(bb__parsepwent, resultbuf, buffer, buflen, pwf);
+ if (!rv) {
+ *result = resultbuf;
+ }
+
+ ERR:
+ UNLOCK;
+ return rv;
+}
+
+static FILE *grf /*= NULL*/;
+void setgrent(void)
+{
+ LOCK;
+ if (grf) {
+ rewind(grf);
+ }
+ UNLOCK;
+}
+
+void endgrent(void)
+{
+ LOCK;
+ if (grf) {
+ fclose(grf);
+ grf = NULL;
+ }
+ UNLOCK;
+}
+
+int getgrent_r(struct group *__restrict resultbuf,
+ char *__restrict buffer, size_t buflen,
+ struct group **__restrict result)
+{
+ int rv;
+
+ LOCK;
+ *result = NULL; /* In case of error... */
+
+ if (!grf) {
+ grf = fopen(_PATH_GROUP, "r");
+ if (!grf) {
+ rv = errno;
+ goto ERR;
+ }
+ }
+
+ rv = bb__pgsreader(bb__parsegrent, resultbuf, buffer, buflen, grf);
+ if (!rv) {
+ *result = resultbuf;
+ }
+
+ ERR:
+ UNLOCK;
+ return rv;
+}
+
+#if ENABLE_USE_BB_SHADOW
+static FILE *spf /*= NULL*/;
+void setspent(void)
+{
+ LOCK;
+ if (spf) {
+ rewind(spf);
+ }
+ UNLOCK;
+}
+
+void endspent(void)
+{
+ LOCK;
+ if (spf) {
+ fclose(spf);
+ spf = NULL;
+ }
+ UNLOCK;
+}
+
+int getspent_r(struct spwd *resultbuf, char *buffer,
+ size_t buflen, struct spwd **result)
+{
+ int rv;
+
+ LOCK;
+ *result = NULL; /* In case of error... */
+
+ if (!spf) {
+ spf = fopen(_PATH_SHADOW, "r");
+ if (!spf) {
+ rv = errno;
+ goto ERR;
+ }
+ }
+
+ rv = bb__pgsreader(bb__parsespent, resultbuf, buffer, buflen, spf);
+ if (!rv) {
+ *result = resultbuf;
+ }
+
+ ERR:
+ UNLOCK;
+ return rv;
+}
+#endif
+
+#if 0
+struct passwd *getpwent(void)
+{
+ static char line_buff[PWD_BUFFER_SIZE];
+ static struct passwd pwd;
+ struct passwd *result;
+
+ getpwent_r(&pwd, line_buff, sizeof(line_buff), &result);
+ return result;
+}
+
+struct group *getgrent(void)
+{
+ static char line_buff[GRP_BUFFER_SIZE];
+ static struct group gr;
+ struct group *result;
+
+ getgrent_r(&gr, line_buff, sizeof(line_buff), &result);
+ return result;
+}
+#endif
+
+#if 0 //ENABLE_USE_BB_SHADOW
+struct spwd *getspent(void)
+{
+ static char line_buff[PWD_BUFFER_SIZE];
+ static struct spwd spwd;
+ struct spwd *result;
+
+ getspent_r(&spwd, line_buff, sizeof(line_buff), &result);
+ return result;
+}
+
+struct spwd *sgetspent(const char *string)
+{
+ static char line_buff[PWD_BUFFER_SIZE];
+ static struct spwd spwd;
+ struct spwd *result;
+
+ sgetspent_r(string, &spwd, line_buff, sizeof(line_buff), &result);
+ return result;
+}
+#endif
+
+int initgroups(const char *user, gid_t gid)
+{
+ FILE *grfile;
+ gid_t *group_list;
+ int num_groups, rv;
+ char **m;
+ struct group group;
+ char buff[PWD_BUFFER_SIZE];
+
+ rv = -1;
+
+ /* We alloc space for 8 gids at a time. */
+ group_list = (gid_t *) malloc(8*sizeof(gid_t *));
+ if (group_list
+ && ((grfile = fopen(_PATH_GROUP, "r")) != NULL)
+ ) {
+ *group_list = gid;
+ num_groups = 1;
+
+ while (!bb__pgsreader(bb__parsegrent, &group, buff, sizeof(buff), grfile)) {
+ assert(group.gr_mem); /* Must have at least a NULL terminator. */
+ if (group.gr_gid != gid) {
+ for (m=group.gr_mem ; *m ; m++) {
+ if (!strcmp(*m, user)) {
+ if (!(num_groups & 7)) {
+ gid_t *tmp = (gid_t *)
+ realloc(group_list,
+ (num_groups+8) * sizeof(gid_t *));
+ if (!tmp) {
+ rv = -1;
+ goto DO_CLOSE;
+ }
+ group_list = tmp;
+ }
+ group_list[num_groups++] = group.gr_gid;
+ break;
+ }
+ }
+ }
+ }
+
+ rv = setgroups(num_groups, group_list);
+ DO_CLOSE:
+ fclose(grfile);
+ }
+
+ /* group_list will be NULL if initial malloc failed, which may trigger
+ * warnings from various malloc debuggers. */
+ free(group_list);
+ return rv;
+}
+
+int putpwent(const struct passwd *__restrict p, FILE *__restrict f)
+{
+ int rv = -1;
+
+ if (!p || !f) {
+ errno=EINVAL;
+ } else {
+ /* No extra thread locking is needed above what fprintf does. */
+ if (fprintf(f, "%s:%s:%lu:%lu:%s:%s:%s\n",
+ p->pw_name, p->pw_passwd,
+ (unsigned long)(p->pw_uid),
+ (unsigned long)(p->pw_gid),
+ p->pw_gecos, p->pw_dir, p->pw_shell) >= 0
+ ) {
+ rv = 0;
+ }
+ }
+
+ return rv;
+}
+
+int putgrent(const struct group *__restrict p, FILE *__restrict f)
+{
+ static const char format[] = ",%s";
+ char **m;
+ const char *fmt;
+ int rv = -1;
+
+ if (!p || !f) { /* Sigh... glibc checks. */
+ errno=EINVAL;
+ } else {
+ if (fprintf(f, "%s:%s:%lu:",
+ p->gr_name, p->gr_passwd,
+ (unsigned long)(p->gr_gid)) >= 0
+ ) {
+
+ fmt = format + 1;
+
+ assert(p->gr_mem);
+ m = p->gr_mem;
+
+ do {
+ if (!*m) {
+ if (fputc('\n', f) >= 0) {
+ rv = 0;
+ }
+ break;
+ }
+ if (fprintf(f, fmt, *m) < 0) {
+ break;
+ }
+ ++m;
+ fmt = format;
+ } while (1);
+
+ }
+
+ }
+
+ return rv;
+}
+
+#if ENABLE_USE_BB_SHADOW
+static const unsigned char _sp_off[] = {
+ offsetof(struct spwd, sp_lstchg), /* 2 - not a char ptr */
+ offsetof(struct spwd, sp_min), /* 3 - not a char ptr */
+ offsetof(struct spwd, sp_max), /* 4 - not a char ptr */
+ offsetof(struct spwd, sp_warn), /* 5 - not a char ptr */
+ offsetof(struct spwd, sp_inact), /* 6 - not a char ptr */
+ offsetof(struct spwd, sp_expire), /* 7 - not a char ptr */
+};
+
+int putspent(const struct spwd *p, FILE *stream)
+{
+ static const char ld_format[] = "%ld:";
+ const char *f;
+ long int x;
+ int i;
+ int rv = -1;
+
+ /* Unlike putpwent and putgrent, glibc does not check the args. */
+ if (fprintf(stream, "%s:%s:", p->sp_namp,
+ (p->sp_pwdp ? p->sp_pwdp : "")) < 0
+ ) {
+ goto DO_UNLOCK;
+ }
+
+ for (i=0 ; i < sizeof(_sp_off) ; i++) {
+ f = ld_format;
+ x = *(const long int *)(((const char *) p) + _sp_off[i]);
+ if (x == -1) {
+ f += 3;
+ }
+ if (fprintf(stream, f, x) < 0) {
+ goto DO_UNLOCK;
+ }
+ }
+
+ if ((p->sp_flag != ~0UL) && (fprintf(stream, "%lu", p->sp_flag) < 0)) {
+ goto DO_UNLOCK;
+ }
+
+ if (fputc('\n', stream) > 0) {
+ rv = 0;
+ }
+
+DO_UNLOCK:
+ return rv;
+}
+#endif
+
+/**********************************************************************/
+/* Internal uClibc functions. */
+/**********************************************************************/
+
+static const unsigned char pw_off[] = {
+ offsetof(struct passwd, pw_name), /* 0 */
+ offsetof(struct passwd, pw_passwd), /* 1 */
+ offsetof(struct passwd, pw_uid), /* 2 - not a char ptr */
+ offsetof(struct passwd, pw_gid), /* 3 - not a char ptr */
+ offsetof(struct passwd, pw_gecos), /* 4 */
+ offsetof(struct passwd, pw_dir), /* 5 */
+ offsetof(struct passwd, pw_shell) /* 6 */
+};
+
+static int bb__parsepwent(void *data, char *line)
+{
+ char *endptr;
+ char *p;
+ int i;
+
+ i = 0;
+ do {
+ p = ((char *) ((struct passwd *) data)) + pw_off[i];
+
+ if ((i & 6) ^ 2) { /* i!=2 and i!=3 */
+ *((char **) p) = line;
+ if (i==6) {
+ return 0;
+ }
+ /* NOTE: glibc difference - glibc allows omission of
+ * ':' seperators after the gid field if all remaining
+ * entries are empty. We require all separators. */
+ line = strchr(line, ':');
+ if (!line) {
+ break;
+ }
+ } else {
+ unsigned long t = strtoul(line, &endptr, 10);
+ /* Make sure we had at least one digit, and that the
+ * failing char is the next field seperator ':'. See
+ * glibc difference note above. */
+ /* TODO: Also check for leading whitespace? */
+ if ((endptr == line) || (*endptr != ':')) {
+ break;
+ }
+ line = endptr;
+ if (i & 1) { /* i == 3 -- gid */
+ *((gid_t *) p) = t;
+ } else { /* i == 2 -- uid */
+ *((uid_t *) p) = t;
+ }
+ }
+
+ *line++ = 0;
+ ++i;
+ } while (1);
+
+ return -1;
+}
+
+/**********************************************************************/
+
+static const unsigned char gr_off[] = {
+ offsetof(struct group, gr_name), /* 0 */
+ offsetof(struct group, gr_passwd), /* 1 */
+ offsetof(struct group, gr_gid) /* 2 - not a char ptr */
+};
+
+static int bb__parsegrent(void *data, char *line)
+{
+ char *endptr;
+ char *p;
+ int i;
+ char **members;
+ char *end_of_buf;
+
+ end_of_buf = ((struct group *) data)->gr_name; /* Evil hack! */
+ i = 0;
+ do {
+ p = ((char *) ((struct group *) data)) + gr_off[i];
+
+ if (i < 2) {
+ *((char **) p) = line;
+ line = strchr(line, ':');
+ if (!line) {
+ break;
+ }
+ *line++ = 0;
+ ++i;
+ } else {
+ *((gid_t *) p) = strtoul(line, &endptr, 10);
+
+ /* NOTE: glibc difference - glibc allows omission of the
+ * trailing colon when there is no member list. We treat
+ * this as an error. */
+
+ /* Make sure we had at least one digit, and that the
+ * failing char is the next field seperator ':'. See
+ * glibc difference note above. */
+ if ((endptr == line) || (*endptr != ':')) {
+ break;
+ }
+
+ i = 1; /* Count terminating NULL ptr. */
+ p = endptr;
+
+ if (p[1]) { /* We have a member list to process. */
+ /* Overwrite the last ':' with a ',' before counting.
+ * This allows us to test for initial ',' and adds
+ * one ',' so that the ',' count equals the member
+ * count. */
+ *p = ',';
+ do {
+ /* NOTE: glibc difference - glibc allows and trims leading
+ * (but not trailing) space. We treat this as an error. */
+ /* NOTE: glibc difference - glibc allows consecutive and
+ * trailing commas, and ignores "empty string" users. We
+ * treat this as an error. */
+ if (*p == ',') {
+ ++i;
+ *p = 0; /* nul-terminate each member string. */
+ if (!*++p || (*p == ',') || isspace(*p)) {
+ goto ERR;
+ }
+ }
+ } while (*++p);
+ }
+
+ /* Now align (p+1), rounding up. */
+ /* Assumes sizeof(char **) is a power of 2. */
+ members = (char **)( (((intptr_t) p) + sizeof(char **))
+ & ~((intptr_t)(sizeof(char **) - 1)) );
+
+ if (((char *)(members + i)) > end_of_buf) { /* No space. */
+ break;
+ }
+
+ ((struct group *) data)->gr_mem = members;
+
+ if (--i) {
+ p = endptr; /* Pointing to char prior to first member. */
+ do {
+ *members++ = ++p;
+ if (!--i) break;
+ while (*++p) {}
+ } while (1);
+ }
+ *members = NULL;
+
+ return 0;
+ }
+ } while (1);
+
+ ERR:
+ return -1;
+}
+
+/**********************************************************************/
+
+#if ENABLE_USE_BB_SHADOW
+static const unsigned char sp_off[] = {
+ offsetof(struct spwd, sp_namp), /* 0 */
+ offsetof(struct spwd, sp_pwdp), /* 1 */
+ offsetof(struct spwd, sp_lstchg), /* 2 - not a char ptr */
+ offsetof(struct spwd, sp_min), /* 3 - not a char ptr */
+ offsetof(struct spwd, sp_max), /* 4 - not a char ptr */
+ offsetof(struct spwd, sp_warn), /* 5 - not a char ptr */
+ offsetof(struct spwd, sp_inact), /* 6 - not a char ptr */
+ offsetof(struct spwd, sp_expire), /* 7 - not a char ptr */
+ offsetof(struct spwd, sp_flag) /* 8 - not a char ptr */
+};
+
+static int bb__parsespent(void *data, char * line)
+{
+ char *endptr;
+ char *p;
+ int i;
+
+ i = 0;
+ do {
+ p = ((char *) ((struct spwd *) data)) + sp_off[i];
+ if (i < 2) {
+ *((char **) p) = line;
+ line = strchr(line, ':');
+ if (!line) {
+ break;
+ }
+ } else {
+ *((long *) p) = (long) strtoul(line, &endptr, 10);
+
+ if (endptr == line) {
+ *((long *) p) = ((i != 8) ? -1L : ((long)(~0UL)));
+ }
+
+ line = endptr;
+
+ if (i == 8) {
+ if (!*endptr) {
+ return 0;
+ }
+ break;
+ }
+
+ if (*endptr != ':') {
+ break;
+ }
+
+ }
+
+ *line++ = 0;
+ ++i;
+ } while (1);
+
+ return EINVAL;
+}
+#endif
+
+/**********************************************************************/
+
+/* Reads until if EOF, or until if finds a line which fits in the buffer
+ * and for which the parser function succeeds.
+ *
+ * Returns 0 on success and ENOENT for end-of-file (glibc concession).
+ */
+
+static int bb__pgsreader(int (*parserfunc)(void *d, char *line), void *data,
+ char *__restrict line_buff, size_t buflen, FILE *f)
+{
+ int line_len;
+ int skip;
+ int rv = ERANGE;
+
+ if (buflen < PWD_BUFFER_SIZE) {
+ errno = rv;
+ } else {
+ skip = 0;
+ do {
+ if (!fgets(line_buff, buflen, f)) {
+ if (feof(f)) {
+ rv = ENOENT;
+ }
+ break;
+ }
+
+ line_len = strlen(line_buff) - 1; /* strlen() must be > 0. */
+ if (line_buff[line_len] == '\n') {
+ line_buff[line_len] = 0;
+ } else if (line_len + 2 == buflen) { /* line too long */
+ ++skip;
+ continue;
+ }
+
+ if (skip) {
+ --skip;
+ continue;
+ }
+
+ /* NOTE: glibc difference - glibc strips leading whitespace from
+ * records. We do not allow leading whitespace. */
+
+ /* Skip empty lines, comment lines, and lines with leading
+ * whitespace. */
+ if (*line_buff && (*line_buff != '#') && !isspace(*line_buff)) {
+ if (parserfunc == bb__parsegrent) { /* Do evil group hack. */
+ /* The group entry parsing function needs to know where
+ * the end of the buffer is so that it can construct the
+ * group member ptr table. */
+ ((struct group *) data)->gr_name = line_buff + buflen;
+ }
+
+ if (!parserfunc(data, line_buff)) {
+ rv = 0;
+ break;
+ }
+ }
+ } while (1);
+
+ }
+
+ return rv;
+}
diff --git a/i/pc104/initrd/conf/busybox/libpwdgrp/pwd_grp_internal.c b/i/pc104/initrd/conf/busybox/libpwdgrp/pwd_grp_internal.c
new file mode 100644
index 0000000..d55edc3
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libpwdgrp/pwd_grp_internal.c
@@ -0,0 +1,62 @@
+/* vi: set sw=4 ts=4: */
+/* Copyright (C) 2003 Manuel Novoa III
+ *
+ * Licensed under GPL v2, or later. See file LICENSE in this tarball.
+ */
+
+/* Nov 6, 2003 Initial version.
+ *
+ * NOTE: This implementation is quite strict about requiring all
+ * field seperators. It also does not allow leading whitespace
+ * except when processing the numeric fields. glibc is more
+ * lenient. See the various glibc difference comments below.
+ *
+ * TODO:
+ * Move to dynamic allocation of (currently statically allocated)
+ * buffers; especially for the group-related functions since
+ * large group member lists will cause error returns.
+ *
+ */
+
+#ifndef GETXXKEY_R_FUNC
+#error GETXXKEY_R_FUNC is not defined!
+#endif
+
+int GETXXKEY_R_FUNC(GETXXKEY_R_KEYTYPE key,
+ GETXXKEY_R_ENTTYPE *__restrict resultbuf,
+ char *__restrict buffer, size_t buflen,
+ GETXXKEY_R_ENTTYPE **__restrict result)
+{
+ FILE *stream;
+ int rv;
+
+ *result = NULL;
+
+ stream = fopen(GETXXKEY_R_PATHNAME, "r");
+ if (!stream)
+ return errno;
+ while (1) {
+ rv = bb__pgsreader(GETXXKEY_R_PARSER, resultbuf, buffer, buflen, stream);
+ if (!rv) {
+ if (GETXXKEY_R_TEST(resultbuf)) { /* Found key? */
+ *result = resultbuf;
+ break;
+ }
+ } else {
+ if (rv == ENOENT) { /* end-of-file encountered. */
+ rv = 0;
+ }
+ break;
+ }
+ }
+ fclose(stream);
+
+ return rv;
+}
+
+#undef GETXXKEY_R_FUNC
+#undef GETXXKEY_R_PARSER
+#undef GETXXKEY_R_ENTTYPE
+#undef GETXXKEY_R_TEST
+#undef GETXXKEY_R_KEYTYPE
+#undef GETXXKEY_R_PATHNAME
diff --git a/i/pc104/initrd/conf/busybox/libpwdgrp/uidgid_get.c b/i/pc104/initrd/conf/busybox/libpwdgrp/uidgid_get.c
new file mode 100644
index 0000000..f10b406
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/libpwdgrp/uidgid_get.c
@@ -0,0 +1,133 @@
+/*
+Copyright (c) 2001-2006, Gerrit Pape
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. The name of the author may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "busybox.h"
+
+/* Always sets uid and gid */
+int get_uidgid(struct bb_uidgid_t *u, const char *ug, int numeric_ok)
+{
+ struct passwd *pwd;
+ struct group *gr;
+ char *user, *group;
+ unsigned n;
+
+ user = (char*)ug;
+ group = strchr(ug, ':');
+ if (group) {
+ int sz = (++group) - ug;
+ user = alloca(sz);
+ /* copies sz-1 bytes, stores terminating '\0' */
+ safe_strncpy(user, ug, sz);
+ }
+ if (numeric_ok) {
+ n = bb_strtou(user, NULL, 10);
+ if (!errno) {
+ u->uid = n;
+ pwd = getpwuid(n);
+ /* If we have e.g. "500" string without user */
+ /* with uid 500 in /etc/passwd, we set gid == uid */
+ u->gid = pwd ? pwd->pw_gid : n;
+ goto skip;
+ }
+ }
+ /* Either it is not numeric, or caller disallows numeric username */
+ pwd = getpwnam(user);
+ if (!pwd)
+ return 0;
+ u->uid = pwd->pw_uid;
+ u->gid = pwd->pw_gid;
+
+ skip:
+ if (group) {
+ if (numeric_ok) {
+ n = bb_strtou(group, NULL, 10);
+ if (!errno) {
+ u->gid = n;
+ return 1;
+ }
+ }
+ gr = getgrnam(group);
+ if (!gr) return 0;
+ u->gid = gr->gr_gid;
+ }
+ return 1;
+}
+
+/* chown-like:
+ * "user" sets uid only,
+ * ":group" sets gid only
+ * "user:" sets uid and gid (to user's primary group id)
+ * "user:group" sets uid and gid
+ * ('unset' uid or gid is actually set to -1)
+ */
+void parse_chown_usergroup_or_die(struct bb_uidgid_t *u, char *user_group)
+{
+ char *group;
+
+ u->uid = -1;
+ u->gid = -1;
+
+ /* Check if there is a group name */
+ group = strchr(user_group, '.'); /* deprecated? */
+ if (!group)
+ group = strchr(user_group, ':');
+ else
+ *group = ':'; /* replace '.' with ':' */
+
+ /* Parse "user[:[group]]" */
+ if (!group) { /* "user" */
+ u->uid = get_ug_id(user_group, xuname2uid);
+ } else if (group == user_group) { /* ":group" */
+ u->gid = get_ug_id(group + 1, xgroup2gid);
+ } else {
+ if (!group[1]) /* "user:" */
+ *group = '\0';
+ if (!get_uidgid(u, user_group, 1))
+ bb_error_msg_and_die("unknown user/group %s", user_group);
+ }
+}
+
+#if 0
+#include <stdio.h>
+int main()
+{
+ unsigned u;
+ struct bb_uidgid_t ug;
+ u = get_uidgid(&ug, "apache", 0);
+ printf("%u = %u:%u\n", u, ug.uid, ug.gid);
+ ug.uid = ug.gid = 1111;
+ u = get_uidgid(&ug, "apache", 0);
+ printf("%u = %u:%u\n", u, ug.uid, ug.gid);
+ ug.uid = ug.gid = 1111;
+ u = get_uidgid(&ug, "apache:users", 0);
+ printf("%u = %u:%u\n", u, ug.uid, ug.gid);
+ ug.uid = ug.gid = 1111;
+ u = get_uidgid(&ug, "apache:users", 0);
+ printf("%u = %u:%u\n", u, ug.uid, ug.gid);
+ return 0;
+}
+#endif
diff --git a/i/pc104/initrd/conf/busybox/loginutils/Config.in b/i/pc104/initrd/conf/busybox/loginutils/Config.in
new file mode 100644
index 0000000..03a638c
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/loginutils/Config.in
@@ -0,0 +1,193 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+menu "Login/Password Management Utilities"
+
+config FEATURE_SHADOWPASSWDS
+ bool "Support for shadow passwords"
+ default n
+ help
+ Build support for shadow password in /etc/shadow. This file is only
+ readable by root and thus the encrypted passwords are no longer
+ publicly readable.
+
+config USE_BB_SHADOW
+ bool " Use busybox shadow password functions"
+ default y
+ depends on USE_BB_PWD_GRP && FEATURE_SHADOWPASSWDS
+ help
+ If you leave this disabled, busybox will use the system's shadow
+ password handling functions. And if you are using the GNU C library
+ (glibc), you will then need to install the /etc/nsswitch.conf
+ configuration file and the required /lib/libnss_* libraries in
+ order for the shadow password functions to work. This generally
+ makes your embedded system quite a bit larger.
+
+ Enabling this option will cause busybox to directly access the
+ system's /etc/shadow file when handling shadow passwords. This
+ makes your system smaller and I will get fewer emails asking about
+ how glibc NSS works). When this option is enabled, you will not be
+ able to use PAM to access shadow passwords from remote LDAP
+ password servers and whatnot.
+
+config USE_BB_PWD_GRP
+ bool "Use internal password and group functions rather than system functions"
+ default n
+ help
+ If you leave this disabled, busybox will use the system's password
+ and group functions. And if you are using the GNU C library
+ (glibc), you will then need to install the /etc/nsswitch.conf
+ configuration file and the required /lib/libnss_* libraries in
+ order for the password and group functions to work. This generally
+ makes your embedded system quite a bit larger.
+
+ Enabling this option will cause busybox to directly access the
+ system's /etc/password, /etc/group files (and your system will be
+ smaller, and I will get fewer emails asking about how glibc NSS
+ works). When this option is enabled, you will not be able to use
+ PAM to access remote LDAP password servers and whatnot. And if you
+ want hostname resolution to work with glibc, you still need the
+ /lib/libnss_* libraries.
+
+ If you enable this option, it will add about 1.5k to busybox.
+
+config ADDGROUP
+ bool "addgroup"
+ default n
+ help
+ Utility for creating a new group account.
+
+config DELGROUP
+ bool "delgroup"
+ default n
+ help
+ Utility for deleting a group account.
+
+config ADDUSER
+ bool "adduser"
+ default n
+ help
+ Utility for creating a new user account.
+
+config DELUSER
+ bool "deluser"
+ default n
+ help
+ Utility for deleting a user account.
+
+config GETTY
+ bool "getty"
+ default n
+ select FEATURE_SYSLOG
+ help
+ getty lets you log in on a tty, it is normally invoked by init.
+
+config FEATURE_UTMP
+ bool "Support utmp file"
+ depends on GETTY || LOGIN || SU || WHO
+ default n
+ help
+ The file /var/run/utmp is used to track who is currently logged in.
+
+config FEATURE_WTMP
+ bool "Support wtmp file"
+ depends on GETTY || LOGIN || SU || LAST
+ default n
+ select FEATURE_UTMP
+ help
+ The file /var/run/wtmp is used to track when user's have logged into
+ and logged out of the system.
+
+config LOGIN
+ bool "login"
+ default n
+ select FEATURE_SUID
+ select FEATURE_SYSLOG
+ help
+ login is used when signing onto a system.
+
+ Note that Busybox binary must be setuid root for this applet to
+ work properly.
+
+config LOGIN_SCRIPTS
+ bool "Support for login scripts"
+ depends on LOGIN
+ default n
+ help
+ Enable this if you want login to execute $LOGIN_PRE_SUID_SCRIPT
+ just prior to switching from root to logged-in user.
+
+config FEATURE_SECURETTY
+ bool "Support for /etc/securetty"
+ default y
+ depends on LOGIN
+ help
+ The file /etc/securetty is used by (some versions of) login(1).
+ The file contains the device names of tty lines (one per line,
+ without leading /dev/) on which root is allowed to login.
+
+config PASSWD
+ bool "passwd"
+ default n
+ select FEATURE_SUID
+ select FEATURE_SYSLOG
+ help
+ passwd changes passwords for user and group accounts. A normal user
+ may only change the password for his/her own account, the super user
+ may change the password for any account. The administrator of a group
+ may change the password for the group.
+
+ Note that Busybox binary must be setuid root for this applet to
+ work properly.
+
+config FEATURE_PASSWD_WEAK_CHECK
+ bool "Check new passwords for weakness"
+ default y
+ depends on PASSWD
+ help
+ With this option passwd will refuse new passwords which are "weak".
+
+config SU
+ bool "su"
+ default n
+ select FEATURE_SUID
+ select FEATURE_SYSLOG
+ help
+ su is used to become another user during a login session.
+ Invoked without a username, su defaults to becoming the super user.
+
+ Note that Busybox binary must be setuid root for this applet to
+ work properly.
+
+config FEATURE_SU_SYSLOG
+ bool "Enable su to write to syslog"
+ default y
+ depends on SU
+
+config FEATURE_SU_CHECKS_SHELLS
+ bool "Enable su to check user's shell to be listed in /etc/shells"
+ depends on SU
+ default y
+
+config SULOGIN
+ bool "sulogin"
+ default n
+ select FEATURE_SYSLOG
+ help
+ sulogin is invoked when the system goes into single user
+ mode (this is done through an entry in inittab).
+
+config VLOCK
+ bool "vlock"
+ default n
+ select FEATURE_SUID
+ help
+ Build the "vlock" applet which allows you to lock (virtual) terminals.
+
+ Note that Busybox binary must be setuid root for this applet to
+ work properly.
+
+endmenu
+
diff --git a/i/pc104/initrd/conf/busybox/loginutils/Kbuild b/i/pc104/initrd/conf/busybox/loginutils/Kbuild
new file mode 100644
index 0000000..6c9d193
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/loginutils/Kbuild
@@ -0,0 +1,17 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under the GPL v2, see the file LICENSE in this tarball.
+
+lib-y:=
+lib-$(CONFIG_ADDGROUP) += addgroup.o
+lib-$(CONFIG_ADDUSER) += adduser.o
+lib-$(CONFIG_GETTY) += getty.o
+lib-$(CONFIG_LOGIN) += login.o
+lib-$(CONFIG_PASSWD) += passwd.o
+lib-$(CONFIG_SU) += su.o
+lib-$(CONFIG_SULOGIN) += sulogin.o
+lib-$(CONFIG_VLOCK) += vlock.o
+lib-$(CONFIG_DELUSER) += deluser.o
+lib-$(CONFIG_DELGROUP) += deluser.o
diff --git a/i/pc104/initrd/conf/busybox/loginutils/addgroup.c b/i/pc104/initrd/conf/busybox/loginutils/addgroup.c
new file mode 100644
index 0000000..78250a4
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/loginutils/addgroup.c
@@ -0,0 +1,111 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * addgroup - add users to /etc/passwd and /etc/shadow
+ *
+ * Copyright (C) 1999 by Lineo, inc. and John Beppu
+ * Copyright (C) 1999,2000,2001 by John Beppu <beppu@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ *
+ */
+
+#include "busybox.h"
+
+/* make sure gr_name isn't taken, make sure gid is kosher
+ * return 1 on failure */
+static int group_study(struct group *g)
+{
+ enum { max = 65000 };
+ FILE *etc_group;
+ gid_t desired;
+ /* Using _r function to avoid static buffers pulled in */
+ char buffer[256];
+ struct group grp;
+ struct group *result;
+
+ etc_group = xfopen(bb_path_group_file, "r");
+
+ /* make sure gr_name isn't taken, make sure gid is kosher */
+ desired = g->gr_gid;
+ while (!fgetgrent_r(etc_group, &grp, buffer, sizeof(buffer), &result)) {
+ if ((strcmp(grp.gr_name, g->gr_name)) == 0) {
+ bb_error_msg_and_die("%s: group already in use", g->gr_name);
+ }
+ if ((desired) && grp.gr_gid == desired) {
+ bb_error_msg_and_die("%d: gid already in use",
+ desired);
+ }
+ if ((grp.gr_gid > g->gr_gid) && (grp.gr_gid < max)) {
+ g->gr_gid = grp.gr_gid;
+ }
+ }
+ if (ENABLE_FEATURE_CLEAN_UP)
+ fclose(etc_group);
+
+ /* gid */
+ g->gr_gid++;
+ if (desired) {
+ g->gr_gid = desired;
+ }
+ /* return 1; */
+ return 0;
+}
+
+/* append a new user to the passwd file */
+static int addgroup(char *group, gid_t gid, const char *user)
+{
+ FILE *file;
+ struct group gr;
+
+ /* make sure gid and group haven't already been allocated */
+ gr.gr_gid = gid;
+ gr.gr_name = group;
+ if (group_study(&gr))
+ return 1;
+
+ /* add entry to group */
+ file = xfopen(bb_path_group_file, "a");
+ /* group:passwd:gid:userlist */
+ fprintf(file, "%s:%s:%d:%s\n", group, "x", gr.gr_gid, user);
+ if (ENABLE_FEATURE_CLEAN_UP)
+ fclose(file);
+
+#if ENABLE_FEATURE_SHADOWPASSWDS
+ file = fopen_or_warn(bb_path_gshadow_file, "a");
+ if (file) {
+ fprintf(file, "%s:!::\n", group);
+ if (ENABLE_FEATURE_CLEAN_UP)
+ fclose(file);
+ }
+#endif
+
+ /* return 1; */
+ return 0;
+}
+
+/*
+ * addgroup will take a login_name as its first parameter.
+ *
+ * gid can be customized via command-line parameters.
+ */
+int addgroup_main(int argc, char **argv);
+int addgroup_main(int argc, char **argv)
+{
+ char *group;
+ gid_t gid = 0;
+
+ /* check for min, max and missing args and exit on error */
+ opt_complementary = "-1:?2:?";
+ if (getopt32(argc, argv, "g:", &group)) {
+ gid = xatoul_range(group, 0, (gid_t)ULONG_MAX);
+ }
+ /* move past the commandline options */
+ argv += optind;
+
+ /* need to be root */
+ if (geteuid()) {
+ bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
+ }
+
+ return addgroup(argv[0], gid, argv[1] ? argv[1] : "");
+}
diff --git a/i/pc104/initrd/conf/busybox/loginutils/adduser.c b/i/pc104/initrd/conf/busybox/loginutils/adduser.c
new file mode 100644
index 0000000..4c03790
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/loginutils/adduser.c
@@ -0,0 +1,196 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * adduser - add users to /etc/passwd and /etc/shadow
+ *
+ * Copyright (C) 1999 by Lineo, inc. and John Beppu
+ * Copyright (C) 1999,2000,2001 by John Beppu <beppu@codepoet.org>
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ */
+
+#include "busybox.h"
+
+#define OPT_DONT_SET_PASS (1 << 4)
+#define OPT_DONT_MAKE_HOME (1 << 6)
+
+
+/* remix */
+/* EDR recoded such that the uid may be passed in *p */
+static int passwd_study(const char *filename, struct passwd *p)
+{
+ enum { min = 500, max = 65000 };
+ FILE *passwd;
+ /* We are using reentrant fgetpwent_r() in order to avoid
+ * pulling in static buffers from libc (think static build here) */
+ char buffer[256];
+ struct passwd pw;
+ struct passwd *result;
+
+ passwd = xfopen(filename, "r");
+
+ /* EDR if uid is out of bounds, set to min */
+ if ((p->pw_uid > max) || (p->pw_uid < min))
+ p->pw_uid = min;
+
+ /* stuff to do:
+ * make sure login isn't taken;
+ * find free uid and gid;
+ */
+ while (!fgetpwent_r(passwd, &pw, buffer, sizeof(buffer), &result)) {
+ if (strcmp(pw.pw_name, p->pw_name) == 0) {
+ /* return 0; */
+ return 1;
+ }
+ if ((pw.pw_uid >= p->pw_uid) && (pw.pw_uid < max)
+ && (pw.pw_uid >= min)) {
+ p->pw_uid = pw.pw_uid + 1;
+ }
+ }
+
+ if (p->pw_gid == 0) {
+ /* EDR check for an already existing gid */
+ while (getgrgid(p->pw_uid) != NULL)
+ p->pw_uid++;
+
+ /* EDR also check for an existing group definition */
+ if (getgrnam(p->pw_name) != NULL)
+ return 3;
+
+ /* EDR create new gid always = uid */
+ p->pw_gid = p->pw_uid;
+ }
+
+ /* EDR bounds check */
+ if ((p->pw_uid > max) || (p->pw_uid < min))
+ return 2;
+
+ /* return 1; */
+ return 0;
+}
+
+static void addgroup_wrapper(struct passwd *p)
+{
+ char *cmd;
+
+ cmd = xasprintf("addgroup -g %d \"%s\"", p->pw_gid, p->pw_name);
+ system(cmd);
+ free(cmd);
+}
+
+static void passwd_wrapper(const char *login) ATTRIBUTE_NORETURN;
+
+static void passwd_wrapper(const char *login)
+{
+ static const char prog[] = "passwd";
+ BB_EXECLP(prog, prog, login, NULL);
+ bb_error_msg_and_die("failed to execute '%s', you must set the password for '%s' manually", prog, login);
+}
+
+/* putpwent(3) remix */
+static int adduser(struct passwd *p)
+{
+ FILE *file;
+ int addgroup = !p->pw_gid;
+
+ /* make sure everything is kosher and setup uid && gid */
+ file = xfopen(bb_path_passwd_file, "a");
+ fseek(file, 0, SEEK_END);
+
+ switch (passwd_study(bb_path_passwd_file, p)) {
+ case 1:
+ bb_error_msg_and_die("%s: login already in use", p->pw_name);
+ case 2:
+ bb_error_msg_and_die("illegal uid or no uids left");
+ case 3:
+ bb_error_msg_and_die("%s: group name already in use", p->pw_name);
+ }
+
+ /* add to passwd */
+ if (putpwent(p, file) == -1) {
+ bb_perror_nomsg_and_die();
+ }
+ fclose(file);
+
+#if ENABLE_FEATURE_SHADOWPASSWDS
+ /* add to shadow if necessary */
+ file = xfopen(bb_path_shadow_file, "a");
+ fseek(file, 0, SEEK_END);
+ fprintf(file, "%s:!:%ld:%d:%d:%d:::\n",
+ p->pw_name, /* username */
+ time(NULL) / 86400, /* sp->sp_lstchg */
+ 0, /* sp->sp_min */
+ 99999, /* sp->sp_max */
+ 7); /* sp->sp_warn */
+ fclose(file);
+#endif
+
+ /* add to group */
+ /* addgroup should be responsible for dealing w/ gshadow */
+ /* if using a pre-existing group, don't create one */
+ if (addgroup) addgroup_wrapper(p);
+
+ /* Clear the umask for this process so it doesn't
+ * * screw up the permissions on the mkdir and chown. */
+ umask(0);
+ if (!(option_mask32 & OPT_DONT_MAKE_HOME)) {
+ /* Set the owner and group so it is owned by the new user,
+ then fix up the permissions to 2755. Can't do it before
+ since chown will clear the setgid bit */
+ if (mkdir(p->pw_dir, 0755)
+ || chown(p->pw_dir, p->pw_uid, p->pw_gid)
+ || chmod(p->pw_dir, 02755)) {
+ bb_perror_msg("%s", p->pw_dir);
+ }
+ }
+
+ if (!(option_mask32 & OPT_DONT_SET_PASS)) {
+ /* interactively set passwd */
+ passwd_wrapper(p->pw_name);
+ }
+
+ return 0;
+}
+
+/*
+ * adduser will take a login_name as its first parameter.
+ *
+ * home
+ * shell
+ * gecos
+ *
+ * can be customized via command-line parameters.
+ */
+int adduser_main(int argc, char **argv);
+int adduser_main(int argc, char **argv)
+{
+ struct passwd pw;
+ const char *usegroup = NULL;
+
+ /* got root? */
+ if (geteuid()) {
+ bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
+ }
+
+ pw.pw_gecos = (char *)"Linux User,,,";
+ pw.pw_shell = (char *)DEFAULT_SHELL;
+ pw.pw_dir = NULL;
+
+ /* check for min, max and missing args and exit on error */
+ opt_complementary = "-1:?1:?";
+ getopt32(argc, argv, "h:g:s:G:DSH", &pw.pw_dir, &pw.pw_gecos, &pw.pw_shell, &usegroup);
+
+ /* create string for $HOME if not specified already */
+ if (!pw.pw_dir) {
+ snprintf(bb_common_bufsiz1, BUFSIZ, "/home/%s", argv[optind]);
+ pw.pw_dir = bb_common_bufsiz1;
+ }
+
+ /* create a passwd struct */
+ pw.pw_name = argv[optind];
+ pw.pw_passwd = (char *)"x";
+ pw.pw_uid = 0;
+ pw.pw_gid = usegroup ? xgroup2gid(usegroup) : 0; /* exits on failure */
+
+ /* grand finale */
+ return adduser(&pw);
+}
diff --git a/i/pc104/initrd/conf/busybox/loginutils/deluser.c b/i/pc104/initrd/conf/busybox/loginutils/deluser.c
new file mode 100644
index 0000000..e9bde00
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/loginutils/deluser.c
@@ -0,0 +1,83 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * deluser (remove lusers from the system ;) for TinyLogin
+ *
+ * Copyright (C) 1999 by Lineo, inc. and John Beppu
+ * Copyright (C) 1999,2000,2001 by John Beppu <beppu@codepoet.org>
+ * Unified with delgroup by Tito Ragusa <farmatito@tiscali.it>
+ *
+ * Licensed under GPL version 2, see file LICENSE in this tarball for details.
+ *
+ */
+
+#include "busybox.h"
+
+static void del_line_matching(const char *login, const char *filename)
+{
+ char *line;
+ FILE *passwd;
+ int len = strlen(login);
+ int found = 0;
+ llist_t *plist = NULL;
+
+ passwd = fopen_or_warn(filename, "r");
+ if (!passwd) return;
+
+ while ((line = xmalloc_fgets(passwd))) {
+ if (!strncmp(line, login, len)
+ && line[len] == ':'
+ ) {
+ found++;
+ free(line);
+ } else {
+ llist_add_to_end(&plist, line);
+ }
+ }
+
+ if (!ENABLE_FEATURE_CLEAN_UP) {
+ if (!found) {
+ bb_error_msg("can't find '%s' in '%s'", login, filename);
+ return;
+ }
+ passwd = fopen_or_warn(filename, "w");
+ if (passwd)
+ while ((line = llist_pop(&plist)))
+ fputs(line, passwd);
+ } else {
+ if (!found) {
+ bb_error_msg("can't find '%s' in '%s'", login, filename);
+ goto clean_up;
+ }
+ fclose(passwd);
+ passwd = fopen_or_warn(filename, "w");
+ if (passwd) {
+ clean_up:
+ while ((line = llist_pop(&plist))) {
+ if (found) fputs(line, passwd);
+ free(line);
+ }
+ fclose(passwd);
+ }
+ }
+}
+
+int deluser_main(int argc, char **argv);
+int deluser_main(int argc, char **argv)
+{
+ if (argc != 2)
+ bb_show_usage();
+
+ if (ENABLE_DELUSER
+ && (!ENABLE_DELGROUP || applet_name[3] == 'u')
+ ) {
+ del_line_matching(argv[1], bb_path_passwd_file);
+ if (ENABLE_FEATURE_SHADOWPASSWDS)
+ del_line_matching(argv[1], bb_path_shadow_file);
+ }
+ del_line_matching(argv[1], bb_path_group_file);
+ if (ENABLE_FEATURE_SHADOWPASSWDS)
+ del_line_matching(argv[1], bb_path_gshadow_file);
+
+ return EXIT_SUCCESS;
+}
+
diff --git a/i/pc104/initrd/conf/busybox/loginutils/getty.c b/i/pc104/initrd/conf/busybox/loginutils/getty.c
new file mode 100644
index 0000000..64d2d08
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/loginutils/getty.c
@@ -0,0 +1,856 @@
+/* vi: set sw=4 ts=4: */
+/* agetty.c - another getty program for Linux. By W. Z. Venema 1989
+ * Ported to Linux by Peter Orbaek <poe@daimi.aau.dk>
+ * This program is freely distributable. The entire man-page used to
+ * be here. Now read the real man-page agetty.8 instead.
+ *
+ * option added by Eric Rasmussen <ear@usfirst.org> - 12/28/95
+ *
+ * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@misiek.eu.org>
+ * - added Native Language Support
+
+ * 1999-05-05 Thorsten Kranzkowski <dl8bcu@gmx.net>
+ * - enable hardware flow control before displaying /etc/issue
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ *
+ */
+
+#include "busybox.h"
+#include <syslog.h>
+
+#if ENABLE_FEATURE_UTMP
+#include <utmp.h>
+#endif
+
+/*
+ * Some heuristics to find out what environment we are in: if it is not
+ * System V, assume it is SunOS 4.
+ */
+#ifdef LOGIN_PROCESS /* defined in System V utmp.h */
+#define SYSV_STYLE /* select System V style getty */
+#include <sys/utsname.h>
+#include <time.h>
+#if ENABLE_FEATURE_WTMP
+extern void updwtmp(const char *filename, const struct utmp *ut);
+static void update_utmp(const char *line);
+#endif
+#endif /* LOGIN_PROCESS */
+
+/*
+ * Things you may want to modify.
+ *
+ * You may disagree with the default line-editing etc. characters defined
+ * below. Note, however, that DEL cannot be used for interrupt generation
+ * and for line editing at the same time.
+ */
+
+/* I doubt there are systems which still need this */
+#undef HANDLE_ALLCAPS
+
+#define _PATH_LOGIN "/bin/login"
+
+/* If ISSUE is not defined, getty will never display the contents of the
+ * /etc/issue file. You will not want to spit out large "issue" files at the
+ * wrong baud rate.
+ */
+#define ISSUE "/etc/issue" /* displayed before the login prompt */
+
+/* Some shorthands for control characters. */
+#define CTL(x) (x ^ 0100) /* Assumes ASCII dialect */
+#define CR CTL('M') /* carriage return */
+#define NL CTL('J') /* line feed */
+#define BS CTL('H') /* back space */
+#define DEL CTL('?') /* delete */
+
+/* Defaults for line-editing etc. characters; you may want to change this. */
+#define DEF_ERASE DEL /* default erase character */
+#define DEF_INTR CTL('C') /* default interrupt character */
+#define DEF_QUIT CTL('\\') /* default quit char */
+#define DEF_KILL CTL('U') /* default kill char */
+#define DEF_EOF CTL('D') /* default EOF char */
+#define DEF_EOL '\n'
+#define DEF_SWITCH 0 /* default switch char */
+
+/*
+ * When multiple baud rates are specified on the command line, the first one
+ * we will try is the first one specified.
+ */
+#define FIRST_SPEED 0
+
+/* Storage for command-line options. */
+
+#define MAX_SPEED 10 /* max. nr. of baud rates */
+
+struct options {
+ int flags; /* toggle switches, see below */
+ unsigned timeout; /* time-out period */
+ const char *login; /* login program */
+ const char *tty; /* name of tty */
+ const char *initstring; /* modem init string */
+ const char *issue; /* alternative issue file */
+ int numspeed; /* number of baud rates to try */
+ int speeds[MAX_SPEED]; /* baud rates to be tried */
+};
+
+static const char opt_string[] = "I:LH:f:hil:mt:wn";
+#define F_INITSTRING (1<<0) /* initstring is set */
+#define F_LOCAL (1<<1) /* force local */
+#define F_FAKEHOST (1<<2) /* force fakehost */
+#define F_CUSTISSUE (1<<3) /* give alternative issue file */
+#define F_RTSCTS (1<<4) /* enable RTS/CTS flow control */
+#define F_ISSUE (1<<5) /* display /etc/issue */
+#define F_LOGIN (1<<6) /* non-default login program */
+#define F_PARSE (1<<7) /* process modem status messages */
+#define F_TIMEOUT (1<<8) /* time out */
+#define F_WAITCRLF (1<<9) /* wait for CR or LF */
+#define F_NOPROMPT (1<<10) /* don't ask for login name! */
+
+/* Storage for things detected while the login name was read. */
+struct chardata {
+ unsigned char erase; /* erase character */
+ unsigned char kill; /* kill character */
+ unsigned char eol; /* end-of-line character */
+ unsigned char parity; /* what parity did we see */
+#ifdef HANDLE_ALLCAPS
+ unsigned char capslock; /* upper case without lower case */
+#endif
+};
+
+/* Initial values for the above. */
+static const struct chardata init_chardata = {
+ DEF_ERASE, /* default erase character */
+ DEF_KILL, /* default kill character */
+ 13, /* default eol char */
+ 0, /* space parity */
+#ifdef HANDLE_ALLCAPS
+ 0, /* no capslock */
+#endif
+};
+
+/* The following is used for understandable diagnostics. */
+
+/* Fake hostname for ut_host specified on command line. */
+static char *fakehost = NULL;
+
+/* ... */
+#ifdef DEBUGGING
+#define debug(s) fprintf(dbf,s); fflush(dbf)
+#define DEBUGTERM "/dev/ttyp0"
+static FILE *dbf;
+#else
+#define debug(s) /* nothing */
+#endif
+
+
+/* bcode - convert speed string to speed code; return 0 on failure */
+static int bcode(const char *s)
+{
+ int r;
+ unsigned value = bb_strtou(s, NULL, 10);
+ if (errno) {
+ return -1;
+ }
+ r = tty_value_to_baud(value);
+ if (r > 0) {
+ return r;
+ }
+ return 0;
+}
+
+
+/* parse_speeds - parse alternate baud rates */
+static void parse_speeds(struct options *op, char *arg)
+{
+ char *cp;
+
+ debug("entered parse_speeds\n");
+ for (cp = strtok(arg, ","); cp != 0; cp = strtok((char *) 0, ",")) {
+ if ((op->speeds[op->numspeed++] = bcode(cp)) <= 0)
+ bb_error_msg_and_die("bad speed: %s", cp);
+ if (op->numspeed > MAX_SPEED)
+ bb_error_msg_and_die("too many alternate speeds");
+ }
+ debug("exiting parsespeeds\n");
+}
+
+
+/* parse_args - parse command-line arguments */
+static void parse_args(int argc, char **argv, struct options *op)
+{
+ char *ts;
+
+ op->flags = getopt32(argc, argv, opt_string,
+ &(op->initstring), &fakehost, &(op->issue),
+ &(op->login), &ts);
+ if (op->flags & F_INITSTRING) {
+ const char *p = op->initstring;
+ char *q;
+
+ op->initstring = q = xstrdup(op->initstring);
+ /* copy optarg into op->initstring decoding \ddd
+ octal codes into chars */
+ while (*p) {
+ if (*p == '\\') {
+ p++;
+ *q++ = bb_process_escape_sequence(&p);
+ } else {
+ *q++ = *p++;
+ }
+ }
+ *q = '\0';
+ }
+ op->flags ^= F_ISSUE; /* revert flag show /etc/issue */
+ if (op->flags & F_TIMEOUT) {
+ op->timeout = xatoul_range(ts, 1, INT_MAX);
+ }
+ argv += optind;
+ argc -= optind;
+ debug("after getopt loop\n");
+ if (argc < 2) /* check parameter count */
+ bb_show_usage();
+
+ /* we loosen up a bit and accept both "baudrate tty" and "tty baudrate" */
+ if (isdigit(argv[0][0])) {
+ /* a number first, assume it's a speed (BSD style) */
+ parse_speeds(op, argv[0]); /* baud rate(s) */
+ op->tty = argv[1]; /* tty name */
+ } else {
+ op->tty = argv[0]; /* tty name */
+ parse_speeds(op, argv[1]); /* baud rate(s) */
+ }
+
+ if (argv[2])
+ setenv("TERM", argv[2], 1);
+
+ debug("exiting parseargs\n");
+}
+
+static void xdup2(int srcfd, int dstfd, const char *tty)
+{
+ if (dup2(srcfd, dstfd) == -1)
+ bb_perror_msg_and_die("%s: dup", tty);
+}
+
+/* open_tty - set up tty as standard { input, output, error } */
+static void open_tty(const char *tty, struct termios *tp, int local)
+{
+ int chdir_to_root = 0;
+
+ /* Set up new standard input, unless we are given an already opened port. */
+
+ if (NOT_LONE_DASH(tty)) {
+ struct stat st;
+ int fd;
+
+ /* Sanity checks... */
+
+ xchdir("/dev");
+ chdir_to_root = 1;
+ xstat(tty, &st);
+ if ((st.st_mode & S_IFMT) != S_IFCHR)
+ bb_error_msg_and_die("%s: not a character device", tty);
+
+ /* Open the tty as standard input. */
+
+ debug("open(2)\n");
+ fd = xopen(tty, O_RDWR | O_NONBLOCK);
+ xdup2(fd, 0, tty);
+ while (fd > 2) close(fd--);
+ } else {
+ /*
+ * Standard input should already be connected to an open port. Make
+ * sure it is open for read/write.
+ */
+
+ if ((fcntl(0, F_GETFL, 0) & O_RDWR) != O_RDWR)
+ bb_error_msg_and_die("stdin is not open for read/write");
+ }
+
+ /* Replace current standard output/error fd's with new ones */
+ debug("duping\n");
+ xdup2(0, 1, tty);
+ xdup2(0, 2, tty);
+
+ /*
+ * The following ioctl will fail if stdin is not a tty, but also when
+ * there is noise on the modem control lines. In the latter case, the
+ * common course of action is (1) fix your cables (2) give the modem more
+ * time to properly reset after hanging up. SunOS users can achieve (2)
+ * by patching the SunOS kernel variable "zsadtrlow" to a larger value;
+ * 5 seconds seems to be a good value.
+ */
+
+ if (ioctl(0, TCGETS, tp) < 0)
+ bb_perror_msg_and_die("%s: ioctl(TCGETS)", tty);
+
+ /*
+ * It seems to be a terminal. Set proper protections and ownership. Mode
+ * 0622 is suitable for SYSV <4 because /bin/login does not change
+ * protections. SunOS 4 login will change the protections to 0620 (write
+ * access for group tty) after the login has succeeded.
+ */
+
+#ifdef DEBIAN
+#warning Debian /dev/vcs[a]NN hack is deprecated and will be removed
+ {
+ /* tty to root.dialout 660 */
+ struct group *gr;
+ int id;
+
+ gr = getgrnam("dialout");
+ id = gr ? gr->gr_gid : 0;
+ chown(tty, 0, id);
+ chmod(tty, 0660);
+
+ /* vcs,vcsa to root.sys 600 */
+ if (!strncmp(tty, "tty", 3) && isdigit(tty[3])) {
+ char *vcs, *vcsa;
+
+ vcs = xstrdup(tty);
+ vcsa = xmalloc(strlen(tty) + 2);
+ strcpy(vcs, "vcs");
+ strcpy(vcs + 3, tty + 3);
+ strcpy(vcsa, "vcsa");
+ strcpy(vcsa + 4, tty + 3);
+
+ gr = getgrnam("sys");
+ id = gr ? gr->gr_gid : 0;
+ chown(vcs, 0, id);
+ chmod(vcs, 0600);
+ chown(vcsa, 0, id);
+ chmod(vcs, 0600);
+
+ free(vcs);
+ free(vcsa);
+ }
+ }
+#else
+ if (NOT_LONE_DASH(tty)) {
+ chown(tty, 0, 0); /* 0:0 */
+ chmod(tty, 0622); /* crw--w--w- */
+ }
+#endif
+ if (chdir_to_root)
+ xchdir("/");
+}
+
+/* termios_init - initialize termios settings */
+static void termios_init(struct termios *tp, int speed, struct options *op)
+{
+ /*
+ * Initial termios settings: 8-bit characters, raw-mode, blocking i/o.
+ * Special characters are set after we have read the login name; all
+ * reads will be done in raw mode anyway. Errors will be dealt with
+ * later on.
+ */
+#ifdef __linux__
+ /* flush input and output queues, important for modems! */
+ ioctl(0, TCFLSH, TCIOFLUSH);
+#endif
+
+ tp->c_cflag = CS8 | HUPCL | CREAD | speed;
+ if (op->flags & F_LOCAL) {
+ tp->c_cflag |= CLOCAL;
+ }
+
+ tp->c_iflag = tp->c_lflag = tp->c_line = 0;
+ tp->c_oflag = OPOST | ONLCR;
+ tp->c_cc[VMIN] = 1;
+ tp->c_cc[VTIME] = 0;
+
+ /* Optionally enable hardware flow control */
+
+#ifdef CRTSCTS
+ if (op->flags & F_RTSCTS)
+ tp->c_cflag |= CRTSCTS;
+#endif
+
+ ioctl(0, TCSETS, tp);
+
+ /* go to blocking input even in local mode */
+ fcntl(0, F_SETFL, fcntl(0, F_GETFL, 0) & ~O_NONBLOCK);
+
+ debug("term_io 2\n");
+}
+
+/* auto_baud - extract baud rate from modem status message */
+static void auto_baud(char *buf, unsigned size_buf, struct termios *tp)
+{
+ int speed;
+ int vmin;
+ unsigned iflag;
+ char *bp;
+ int nread;
+
+ /*
+ * This works only if the modem produces its status code AFTER raising
+ * the DCD line, and if the computer is fast enough to set the proper
+ * baud rate before the message has gone by. We expect a message of the
+ * following format:
+ *
+ * <junk><number><junk>
+ *
+ * The number is interpreted as the baud rate of the incoming call. If the
+ * modem does not tell us the baud rate within one second, we will keep
+ * using the current baud rate. It is advisable to enable BREAK
+ * processing (comma-separated list of baud rates) if the processing of
+ * modem status messages is enabled.
+ */
+
+ /*
+ * Use 7-bit characters, don't block if input queue is empty. Errors will
+ * be dealt with later on.
+ */
+
+ iflag = tp->c_iflag;
+ tp->c_iflag |= ISTRIP; /* enable 8th-bit stripping */
+ vmin = tp->c_cc[VMIN];
+ tp->c_cc[VMIN] = 0; /* don't block if queue empty */
+ ioctl(0, TCSETS, tp);
+
+ /*
+ * Wait for a while, then read everything the modem has said so far and
+ * try to extract the speed of the dial-in call.
+ */
+
+ sleep(1);
+ nread = read(0, buf, size_buf - 1);
+ if (nread > 0) {
+ buf[nread] = '\0';
+ for (bp = buf; bp < buf + nread; bp++) {
+ if (isascii(*bp) && isdigit(*bp)) {
+ speed = bcode(bp);
+ if (speed) {
+ tp->c_cflag &= ~CBAUD;
+ tp->c_cflag |= speed;
+ }
+ break;
+ }
+ }
+ }
+ /* Restore terminal settings. Errors will be dealt with later on. */
+
+ tp->c_iflag = iflag;
+ tp->c_cc[VMIN] = vmin;
+ ioctl(0, TCSETS, tp);
+}
+
+/* next_speed - select next baud rate */
+static void next_speed(struct termios *tp, struct options *op)
+{
+ static int baud_index = FIRST_SPEED; /* current speed index */
+
+ baud_index = (baud_index + 1) % op->numspeed;
+ tp->c_cflag &= ~CBAUD;
+ tp->c_cflag |= op->speeds[baud_index];
+ ioctl(0, TCSETS, tp);
+}
+
+
+/* do_prompt - show login prompt, optionally preceded by /etc/issue contents */
+static void do_prompt(struct options *op, struct termios *tp)
+{
+#ifdef ISSUE
+ print_login_issue(op->issue, op->tty);
+#endif
+ print_login_prompt();
+}
+
+#ifdef HANDLE_ALLCAPS
+/* caps_lock - string contains upper case without lower case */
+/* returns 1 if true, 0 if false */
+static int caps_lock(const char *s)
+{
+ while (*s)
+ if (islower(*s++))
+ return 0;
+ return 1;
+}
+#endif
+
+/* get_logname - get user name, establish parity, speed, erase, kill, eol */
+/* return NULL on failure, logname on success */
+static char *get_logname(char *logname, unsigned size_logname,
+ struct options *op, struct chardata *cp, struct termios *tp)
+{
+ char *bp;
+ char c; /* input character, full eight bits */
+ char ascval; /* low 7 bits of input character */
+ int bits; /* # of "1" bits per character */
+ int mask; /* mask with 1 bit up */
+ static const char erase[][3] = { /* backspace-space-backspace */
+ "\010\040\010", /* space parity */
+ "\010\040\010", /* odd parity */
+ "\210\240\210", /* even parity */
+ "\210\240\210", /* no parity */
+ };
+
+ /* Initialize kill, erase, parity etc. (also after switching speeds). */
+
+ *cp = init_chardata;
+
+ /* Flush pending input (esp. after parsing or switching the baud rate). */
+
+ sleep(1);
+ ioctl(0, TCFLSH, TCIFLUSH);
+
+ /* Prompt for and read a login name. */
+
+ logname[0] = '\0';
+ while (!logname[0]) {
+
+ /* Write issue file and prompt, with "parity" bit == 0. */
+
+ do_prompt(op, tp);
+
+ /* Read name, watch for break, parity, erase, kill, end-of-line. */
+
+ bp = logname;
+ cp->eol = '\0';
+ while (cp->eol == '\0') {
+
+ /* Do not report trivial EINTR/EIO errors. */
+ if (read(0, &c, 1) < 1) {
+ if (errno == EINTR || errno == EIO)
+ exit(0);
+ bb_perror_msg_and_die("%s: read", op->tty);
+ }
+
+ /* Do BREAK handling elsewhere. */
+ if (c == '\0' && op->numspeed > 1)
+ return NULL;
+
+ /* Do parity bit handling. */
+ ascval = c & 0177;
+ if (c != ascval) { /* "parity" bit on ? */
+ bits = 1;
+ mask = 1;
+ while (mask & 0177) {
+ if (mask & ascval)
+ bits++; /* count "1" bits */
+ mask <<= 1;
+ }
+ /* ... |= 2 - even, 1 - odd */
+ cp->parity |= 2 - (bits & 1);
+ }
+
+ /* Do erase, kill and end-of-line processing. */
+ switch (ascval) {
+ case CR:
+ case NL:
+ *bp = '\0'; /* terminate logname */
+ cp->eol = ascval; /* set end-of-line char */
+ break;
+ case BS:
+ case DEL:
+ case '#':
+ cp->erase = ascval; /* set erase character */
+ if (bp > logname) {
+ write(1, erase[cp->parity], 3);
+ bp--;
+ }
+ break;
+ case CTL('U'):
+ case '@':
+ cp->kill = ascval; /* set kill character */
+ while (bp > logname) {
+ write(1, erase[cp->parity], 3);
+ bp--;
+ }
+ break;
+ case CTL('D'):
+ exit(0);
+ default:
+ if (!isascii(ascval) || !isprint(ascval)) {
+ /* ignore garbage characters */
+ } else if (bp - logname >= size_logname - 1) {
+ bb_error_msg_and_die("%s: input overrun", op->tty);
+ } else {
+ write(1, &c, 1); /* echo the character */
+ *bp++ = ascval; /* and store it */
+ }
+ break;
+ }
+ }
+ }
+ /* Handle names with upper case and no lower case. */
+
+#ifdef HANDLE_ALLCAPS
+ cp->capslock = caps_lock(logname);
+ if (cp->capslock) {
+ for (bp = logname; *bp; bp++)
+ if (isupper(*bp))
+ *bp = tolower(*bp); /* map name to lower case */
+ }
+#endif
+ return logname;
+}
+
+/* termios_final - set the final tty mode bits */
+static void termios_final(struct options *op, struct termios *tp, struct chardata *cp)
+{
+ /* General terminal-independent stuff. */
+
+ tp->c_iflag |= IXON | IXOFF; /* 2-way flow control */
+ tp->c_lflag |= ICANON | ISIG | ECHO | ECHOE | ECHOK | ECHOKE;
+ /* no longer| ECHOCTL | ECHOPRT */
+ tp->c_oflag |= OPOST;
+ /* tp->c_cflag = 0; */
+ tp->c_cc[VINTR] = DEF_INTR; /* default interrupt */
+ tp->c_cc[VQUIT] = DEF_QUIT; /* default quit */
+ tp->c_cc[VEOF] = DEF_EOF; /* default EOF character */
+ tp->c_cc[VEOL] = DEF_EOL;
+ tp->c_cc[VSWTC] = DEF_SWITCH; /* default switch character */
+
+ /* Account for special characters seen in input. */
+
+ if (cp->eol == CR) {
+ tp->c_iflag |= ICRNL; /* map CR in input to NL */
+ tp->c_oflag |= ONLCR; /* map NL in output to CR-NL */
+ }
+ tp->c_cc[VERASE] = cp->erase; /* set erase character */
+ tp->c_cc[VKILL] = cp->kill; /* set kill character */
+
+ /* Account for the presence or absence of parity bits in input. */
+
+ switch (cp->parity) {
+ case 0: /* space (always 0) parity */
+ break;
+ case 1: /* odd parity */
+ tp->c_cflag |= PARODD;
+ /* FALLTHROUGH */
+ case 2: /* even parity */
+ tp->c_cflag |= PARENB;
+ tp->c_iflag |= INPCK | ISTRIP;
+ /* FALLTHROUGH */
+ case (1 | 2): /* no parity bit */
+ tp->c_cflag &= ~CSIZE;
+ tp->c_cflag |= CS7;
+ break;
+ }
+
+ /* Account for upper case without lower case. */
+#ifdef HANDLE_ALLCAPS
+ if (cp->capslock) {
+ tp->c_iflag |= IUCLC;
+ tp->c_lflag |= XCASE;
+ tp->c_oflag |= OLCUC;
+ }
+#endif
+ /* Optionally enable hardware flow control */
+
+#ifdef CRTSCTS
+ if (op->flags & F_RTSCTS)
+ tp->c_cflag |= CRTSCTS;
+#endif
+
+ /* Finally, make the new settings effective */
+
+ if (ioctl(0, TCSETS, tp) < 0)
+ bb_perror_msg_and_die("%s: ioctl(TCSETS)", op->tty);
+}
+
+
+#ifdef SYSV_STYLE
+#if ENABLE_FEATURE_UTMP
+/* update_utmp - update our utmp entry */
+static void update_utmp(const char *line)
+{
+ struct utmp ut;
+ struct utmp *utp;
+ time_t t;
+ int mypid = getpid();
+
+ /*
+ * The utmp file holds miscellaneous information about things started by
+ * /sbin/init and other system-related events. Our purpose is to update
+ * the utmp entry for the current process, in particular the process type
+ * and the tty line we are listening to. Return successfully only if the
+ * utmp file can be opened for update, and if we are able to find our
+ * entry in the utmp file.
+ */
+ if (access(_PATH_UTMP, R_OK|W_OK) == -1) {
+ close(creat(_PATH_UTMP, 0664));
+ }
+ utmpname(_PATH_UTMP);
+ setutent();
+ while ((utp = getutent())
+ && !(utp->ut_type == INIT_PROCESS && utp->ut_pid == mypid))
+ /* nothing */;
+
+ if (utp) {
+ memcpy(&ut, utp, sizeof(ut));
+ } else {
+ /* some inits don't initialize utmp... */
+ memset(&ut, 0, sizeof(ut));
+ safe_strncpy(ut.ut_id, line + 3, sizeof(ut.ut_id));
+ }
+ /* endutent(); */
+
+ strcpy(ut.ut_user, "LOGIN");
+ safe_strncpy(ut.ut_line, line, sizeof(ut.ut_line));
+ if (fakehost)
+ safe_strncpy(ut.ut_host, fakehost, sizeof(ut.ut_host));
+ time(&t);
+ ut.ut_time = t;
+ ut.ut_type = LOGIN_PROCESS;
+ ut.ut_pid = mypid;
+
+ pututline(&ut);
+ endutent();
+
+#if ENABLE_FEATURE_WTMP
+ if (access(bb_path_wtmp_file, R_OK|W_OK) == -1)
+ close(creat(bb_path_wtmp_file, 0664));
+ updwtmp(bb_path_wtmp_file, &ut);
+#endif
+}
+
+#endif /* CONFIG_FEATURE_UTMP */
+#endif /* SYSV_STYLE */
+
+
+int getty_main(int argc, char **argv);
+int getty_main(int argc, char **argv)
+{
+ int nullfd;
+ char *logname = NULL; /* login name, given to /bin/login */
+ /* Merging these into "struct local" may _seem_ to reduce
+ * parameter passing, but today's gcc will inline
+ * statics which are called once anyway, so don't do that */
+ struct chardata chardata; /* set by get_logname() */
+ struct termios termios; /* terminal mode bits */
+ struct options options = {
+ 0, /* show /etc/issue (SYSV_STYLE) */
+ 0, /* no timeout */
+ _PATH_LOGIN, /* default login program */
+ "tty1", /* default tty line */
+ "", /* modem init string */
+#ifdef ISSUE
+ ISSUE, /* default issue file */
+#else
+ NULL,
+#endif
+ 0, /* no baud rates known yet */
+ };
+
+ /* Already too late because of theoretical
+ * possibility of getty --help somehow triggered
+ * inadvertently before we reach this. Oh well. */
+ logmode = LOGMODE_NONE;
+ setsid();
+ nullfd = xopen(bb_dev_null, O_RDWR);
+ /* dup2(nullfd, 0); - no, because of possible "getty - 9600" */
+ /* open_tty() will take care of fd# 0 anyway */
+ dup2(nullfd, 1);
+ dup2(nullfd, 2);
+ while (nullfd > 2) close(nullfd--);
+ /* We want special flavor of error_msg_and_die */
+ die_sleep = 10;
+ msg_eol = "\r\n";
+ openlog(applet_name, LOG_PID, LOG_AUTH);
+ logmode = LOGMODE_BOTH;
+
+#ifdef DEBUGGING
+ dbf = xfopen(DEBUGTERM, "w");
+
+ {
+ int i;
+
+ for (i = 1; i < argc; i++) {
+ debug(argv[i]);
+ debug("\n");
+ }
+ }
+#endif
+
+ /* Parse command-line arguments. */
+ parse_args(argc, argv, &options);
+
+#ifdef SYSV_STYLE
+#if ENABLE_FEATURE_UTMP
+ /* Update the utmp file. */
+ update_utmp(options.tty);
+#endif
+#endif
+
+ debug("calling open_tty\n");
+ /* Open the tty as standard { input, output, error }. */
+ open_tty(options.tty, &termios, options.flags & F_LOCAL);
+
+#ifdef __linux__
+ {
+ int iv;
+
+ iv = getpid();
+ ioctl(0, TIOCSPGRP, &iv);
+ }
+#endif
+ /* Initialize the termios settings (raw mode, eight-bit, blocking i/o). */
+ debug("calling termios_init\n");
+ termios_init(&termios, options.speeds[FIRST_SPEED], &options);
+
+ /* write the modem init string and DON'T flush the buffers */
+ if (options.flags & F_INITSTRING) {
+ debug("writing init string\n");
+ write(1, options.initstring, strlen(options.initstring));
+ }
+
+ if (!(options.flags & F_LOCAL)) {
+ /* go to blocking write mode unless -L is specified */
+ fcntl(1, F_SETFL, fcntl(1, F_GETFL, 0) & ~O_NONBLOCK);
+ }
+
+ /* Optionally detect the baud rate from the modem status message. */
+ debug("before autobaud\n");
+ if (options.flags & F_PARSE)
+ auto_baud(bb_common_bufsiz1, sizeof(bb_common_bufsiz1), &termios);
+
+ /* Set the optional timer. */
+ if (options.timeout)
+ alarm(options.timeout);
+
+ /* optionally wait for CR or LF before writing /etc/issue */
+ if (options.flags & F_WAITCRLF) {
+ char ch;
+
+ debug("waiting for cr-lf\n");
+ while (read(0, &ch, 1) == 1) {
+ ch &= 0x7f; /* strip "parity bit" */
+#ifdef DEBUGGING
+ fprintf(dbf, "read %c\n", ch);
+#endif
+ if (ch == '\n' || ch == '\r')
+ break;
+ }
+ }
+
+ chardata = init_chardata;
+ if (!(options.flags & F_NOPROMPT)) {
+ /* Read the login name. */
+ debug("reading login name\n");
+ logname = get_logname(bb_common_bufsiz1, sizeof(bb_common_bufsiz1),
+ &options, &chardata, &termios);
+ while (logname == NULL)
+ next_speed(&termios, &options);
+ }
+
+ /* Disable timer. */
+
+ if (options.timeout)
+ alarm(0);
+
+ /* Finalize the termios settings. */
+
+ termios_final(&options, &termios, &chardata);
+
+ /* Now the newline character should be properly written. */
+
+ write(1, "\n", 1);
+
+ /* Let the login program take care of password validation. */
+
+ execl(options.login, options.login, "--", logname, (char *) 0);
+ bb_error_msg_and_die("%s: can't exec %s", options.tty, options.login);
+}
diff --git a/i/pc104/initrd/conf/busybox/loginutils/login.c b/i/pc104/initrd/conf/busybox/loginutils/login.c
new file mode 100644
index 0000000..830df0a
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/loginutils/login.c
@@ -0,0 +1,403 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "busybox.h"
+#include <utmp.h>
+#include <sys/resource.h>
+#include <syslog.h>
+
+#ifdef CONFIG_SELINUX
+#include <selinux/selinux.h> /* for is_selinux_enabled() */
+#include <selinux/get_context_list.h> /* for get_default_context() */
+#include <selinux/flask.h> /* for security class definitions */
+#include <errno.h>
+#endif
+
+enum {
+ TIMEOUT = 60,
+ EMPTY_USERNAME_COUNT = 10,
+ USERNAME_SIZE = 32,
+ TTYNAME_SIZE = 32,
+};
+
+static char full_tty[TTYNAME_SIZE];
+static char* short_tty = full_tty;
+
+#if ENABLE_FEATURE_UTMP
+/* vv Taken from tinylogin utmp.c vv */
+/*
+ * read_or_build_utent - see if utmp file is correct for this process
+ *
+ * System V is very picky about the contents of the utmp file
+ * and requires that a slot for the current process exist.
+ * The utmp file is scanned for an entry with the same process
+ * ID. If no entry exists the process exits with a message.
+ *
+ * The "picky" flag is for network and other logins that may
+ * use special flags. It allows the pid checks to be overridden.
+ * This means that getty should never invoke login with any
+ * command line flags.
+ */
+
+static struct utmp utent;
+
+static void read_or_build_utent(int picky)
+{
+ struct utmp *ut;
+ pid_t pid = getpid();
+
+ setutent();
+
+ /* First, try to find a valid utmp entry for this process. */
+ while ((ut = getutent()))
+ if (ut->ut_pid == pid && ut->ut_line[0] && ut->ut_id[0] &&
+ (ut->ut_type == LOGIN_PROCESS || ut->ut_type == USER_PROCESS))
+ break;
+
+ /* If there is one, just use it, otherwise create a new one. */
+ if (ut) {
+ utent = *ut;
+ } else {
+ if (picky)
+ bb_error_msg_and_die("no utmp entry found");
+
+ memset(&utent, 0, sizeof(utent));
+ utent.ut_type = LOGIN_PROCESS;
+ utent.ut_pid = pid;
+ strncpy(utent.ut_line, short_tty, sizeof(utent.ut_line));
+ /* This one is only 4 chars wide. Try to fit something
+ * remotely meaningful by skipping "tty"... */
+ strncpy(utent.ut_id, short_tty + 3, sizeof(utent.ut_id));
+ strncpy(utent.ut_user, "LOGIN", sizeof(utent.ut_user));
+ utent.ut_time = time(NULL);
+ }
+ if (!picky) /* root login */
+ memset(utent.ut_host, 0, sizeof(utent.ut_host));
+}
+
+/*
+ * write_utent - put a USER_PROCESS entry in the utmp file
+ *
+ * write_utent changes the type of the current utmp entry to
+ * USER_PROCESS. the wtmp file will be updated as well.
+ */
+static void write_utent(const char *username)
+{
+ utent.ut_type = USER_PROCESS;
+ strncpy(utent.ut_user, username, sizeof(utent.ut_user));
+ utent.ut_time = time(NULL);
+ /* other fields already filled in by read_or_build_utent above */
+ setutent();
+ pututline(&utent);
+ endutent();
+#if ENABLE_FEATURE_WTMP
+ if (access(bb_path_wtmp_file, R_OK|W_OK) == -1) {
+ close(creat(bb_path_wtmp_file, 0664));
+ }
+ updwtmp(bb_path_wtmp_file, &utent);
+#endif
+}
+#else /* !ENABLE_FEATURE_UTMP */
+static inline void read_or_build_utent(int ATTRIBUTE_UNUSED picky) {}
+static inline void write_utent(const char ATTRIBUTE_UNUSED *username) {}
+#endif /* !ENABLE_FEATURE_UTMP */
+
+static void die_if_nologin_and_non_root(int amroot)
+{
+ FILE *fp;
+ int c;
+
+ if (access(bb_path_nologin_file, F_OK))
+ return;
+
+ fp = fopen(bb_path_nologin_file, "r");
+ if (fp) {
+ while ((c = getc(fp)) != EOF)
+ putchar((c=='\n') ? '\r' : c);
+ fflush(stdout);
+ fclose(fp);
+ } else
+ puts("\r\nSystem closed for routine maintenance\r");
+ if (!amroot)
+ exit(1);
+ puts("\r\n[Disconnect bypassed -- root login allowed.]\r");
+}
+
+#if ENABLE_FEATURE_SECURETTY
+static int check_securetty(void)
+{
+ FILE *fp;
+ int i;
+ char buf[BUFSIZ];
+
+ fp = fopen(bb_path_securetty_file, "r");
+ if (!fp) {
+ /* A missing securetty file is not an error. */
+ return 1;
+ }
+ while (fgets(buf, sizeof(buf)-1, fp)) {
+ for (i = strlen(buf)-1; i>=0; --i) {
+ if (!isspace(buf[i]))
+ break;
+ }
+ buf[++i] = '\0';
+ if ((buf[0]=='\0') || (buf[0]=='#'))
+ continue;
+ if (strcmp(buf, short_tty) == 0) {
+ fclose(fp);
+ return 1;
+ }
+ }
+ fclose(fp);
+ return 0;
+}
+#else
+static inline int check_securetty(void) { return 1; }
+#endif
+
+static void get_username_or_die(char *buf, int size_buf)
+{
+ int c, cntdown;
+ cntdown = EMPTY_USERNAME_COUNT;
+prompt:
+ /* skip whitespace */
+ print_login_prompt();
+ do {
+ c = getchar();
+ if (c == EOF) exit(1);
+ if (c == '\n') {
+ if (!--cntdown) exit(1);
+ goto prompt;
+ }
+ } while (isspace(c));
+
+ *buf++ = c;
+ if (!fgets(buf, size_buf-2, stdin))
+ exit(1);
+ if (!strchr(buf, '\n'))
+ exit(1);
+ while (isgraph(*buf)) buf++;
+ *buf = '\0';
+}
+
+static void motd(void)
+{
+ FILE *fp;
+ int c;
+
+ fp = fopen(bb_path_motd_file, "r");
+ if (fp) {
+ while ((c = getc(fp)) != EOF)
+ putchar(c);
+ fclose(fp);
+ }
+}
+
+static void nonblock(int fd)
+{
+ fcntl(fd, F_SETFL, O_NONBLOCK | fcntl(fd, F_GETFL));
+}
+
+static void alarm_handler(int sig ATTRIBUTE_UNUSED)
+{
+ /* This is the escape hatch! Poor serial line users and the like
+ * arrive here when their connection is broken.
+ * We don't want to block here */
+ nonblock(1);
+ nonblock(2);
+ bb_info_msg("\r\nLogin timed out after %d seconds\r", TIMEOUT);
+ exit(EXIT_SUCCESS);
+}
+
+int login_main(int argc, char **argv);
+int login_main(int argc, char **argv)
+{
+ enum {
+ LOGIN_OPT_f = (1<<0),
+ LOGIN_OPT_h = (1<<1),
+ LOGIN_OPT_p = (1<<2),
+ };
+ char fromhost[512];
+ char username[USERNAME_SIZE];
+ const char *tmp;
+ int amroot;
+ unsigned opt;
+ int count = 0;
+ struct passwd *pw;
+ char *opt_host = NULL;
+ char *opt_user = NULL;
+ USE_SELINUX(security_context_t user_sid = NULL;)
+
+ username[0] = '\0';
+ amroot = (getuid() == 0);
+ signal(SIGALRM, alarm_handler);
+ alarm(TIMEOUT);
+
+ opt = getopt32(argc, argv, "f:h:p", &opt_user, &opt_host);
+ if (opt & LOGIN_OPT_f) {
+ if (!amroot)
+ bb_error_msg_and_die("-f is for root only");
+ safe_strncpy(username, opt_user, sizeof(username));
+ }
+ if (optind < argc) /* user from command line (getty) */
+ safe_strncpy(username, argv[optind], sizeof(username));
+
+ /* Let's find out and memorize our tty */
+ if (!isatty(0) || !isatty(1) || !isatty(2))
+ return EXIT_FAILURE; /* Must be a terminal */
+ safe_strncpy(full_tty, "UNKNOWN", sizeof(full_tty));
+ tmp = ttyname(0);
+ if (tmp) {
+ safe_strncpy(full_tty, tmp, sizeof(full_tty));
+ if (strncmp(full_tty, "/dev/", 5) == 0)
+ short_tty = full_tty + 5;
+ }
+
+ read_or_build_utent(!amroot);
+
+ if (opt_host) {
+ USE_FEATURE_UTMP(
+ safe_strncpy(utent.ut_host, opt_host, sizeof(utent.ut_host));
+ )
+ snprintf(fromhost, sizeof(fromhost)-1, " on '%.100s' from "
+ "'%.200s'", short_tty, opt_host);
+ }
+ else
+ snprintf(fromhost, sizeof(fromhost)-1, " on '%.100s'", short_tty);
+
+ bb_setpgrp;
+
+ openlog(applet_name, LOG_PID | LOG_CONS | LOG_NOWAIT, LOG_AUTH);
+
+ while (1) {
+ if (!username[0])
+ get_username_or_die(username, sizeof(username));
+
+ pw = getpwnam(username);
+ if (!pw) {
+ safe_strncpy(username, "UNKNOWN", sizeof(username));
+ goto auth_failed;
+ }
+
+ if (pw->pw_passwd[0] == '!' || pw->pw_passwd[0] == '*')
+ goto auth_failed;
+
+ if (opt & LOGIN_OPT_f)
+ break; /* -f USER: success without asking passwd */
+
+ if (pw->pw_uid == 0 && !check_securetty())
+ goto auth_failed;
+
+ /* Don't check the password if password entry is empty (!) */
+ if (!pw->pw_passwd[0])
+ break;
+
+ /* authorization takes place here */
+ if (correct_password(pw))
+ break;
+
+auth_failed:
+ opt &= ~LOGIN_OPT_f;
+ bb_do_delay(FAIL_DELAY);
+ puts("Login incorrect");
+ if (++count == 3) {
+ syslog(LOG_WARNING, "invalid password for '%s'%s",
+ username, fromhost);
+ return EXIT_FAILURE;
+ }
+ username[0] = '\0';
+ }
+
+ alarm(0);
+ die_if_nologin_and_non_root(pw->pw_uid == 0);
+
+ write_utent(username);
+
+#ifdef CONFIG_SELINUX
+ if (is_selinux_enabled()) {
+ security_context_t old_tty_sid, new_tty_sid;
+
+ if (get_default_context(username, NULL, &user_sid)) {
+ bb_error_msg_and_die("cannot get SID for %s",
+ username);
+ }
+ if (getfilecon(full_tty, &old_tty_sid) < 0) {
+ bb_perror_msg_and_die("getfilecon(%s) failed",
+ full_tty);
+ }
+ if (security_compute_relabel(user_sid, old_tty_sid,
+ SECCLASS_CHR_FILE, &new_tty_sid) != 0) {
+ bb_perror_msg_and_die("security_change_sid(%s) failed",
+ full_tty);
+ }
+ if (setfilecon(full_tty, new_tty_sid) != 0) {
+ bb_perror_msg_and_die("chsid(%s, %s) failed",
+ full_tty, new_tty_sid);
+ }
+ }
+#endif
+ /* Try these, but don't complain if they fail.
+ * _f_chown is safe wrt race t=ttyname(0);...;chown(t); */
+ fchown(0, pw->pw_uid, pw->pw_gid);
+ fchmod(0, 0600);
+
+ /* TODO: be nommu-friendly, use spawn? */
+ if (ENABLE_LOGIN_SCRIPTS) {
+ char *script = getenv("LOGIN_PRE_SUID_SCRIPT");
+ if (script) {
+ char *t_argv[2] = { script, NULL };
+ switch (fork()) {
+ case -1: break;
+ case 0: /* child */
+ xchdir("/");
+ setenv("LOGIN_TTY", full_tty, 1);
+ setenv("LOGIN_USER", pw->pw_name, 1);
+ setenv("LOGIN_UID", utoa(pw->pw_uid), 1);
+ setenv("LOGIN_GID", utoa(pw->pw_gid), 1);
+ setenv("LOGIN_SHELL", pw->pw_shell, 1);
+ BB_EXECVP(script, t_argv);
+ exit(1);
+ default: /* parent */
+ wait(NULL);
+ }
+ }
+ }
+
+ change_identity(pw);
+ tmp = pw->pw_shell;
+ if (!tmp || !*tmp)
+ tmp = DEFAULT_SHELL;
+ setup_environment(tmp, 1, !(opt & LOGIN_OPT_p), pw);
+
+ motd();
+
+ if (pw->pw_uid == 0)
+ syslog(LOG_INFO, "root login%s", fromhost);
+#ifdef CONFIG_SELINUX
+ /* well, a simple setexeccon() here would do the job as well,
+ * but let's play the game for now */
+ set_current_security_context(user_sid);
+#endif
+
+ // util-linux login also does:
+ // /* start new session */
+ // setsid();
+ // /* TIOCSCTTY: steal tty from other process group */
+ // if (ioctl(0, TIOCSCTTY, 1)) error_msg...
+
+ /* set signals to defaults */
+ signal(SIGALRM, SIG_DFL);
+ /* Is this correct? This way user can ctrl-c out of /etc/profile,
+ * potentially creating security breach (tested with bash 3.0).
+ * But without this, bash 3.0 will not enable ctrl-c either.
+ * Maybe bash is buggy?
+ * Need to find out what standards say about /bin/login -
+ * should it leave SIGINT etc enabled or disabled? */
+ signal(SIGINT, SIG_DFL);
+
+ run_shell(tmp, 1, 0, 0); /* exec the shell finally */
+
+ return EXIT_FAILURE;
+}
diff --git a/i/pc104/initrd/conf/busybox/loginutils/passwd.c b/i/pc104/initrd/conf/busybox/loginutils/passwd.c
new file mode 100644
index 0000000..b937ce4
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/loginutils/passwd.c
@@ -0,0 +1,347 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "busybox.h"
+#include <syslog.h>
+
+
+static void nuke_str(char *str)
+{
+ if (str) memset(str, 0, strlen(str));
+}
+
+
+static int i64c(int i)
+{
+ i &= 0x3f;
+ if (i == 0)
+ return '.';
+ if (i == 1)
+ return '/';
+ if (i < 12)
+ return ('0' - 2 + i);
+ if (i < 38)
+ return ('A' - 12 + i);
+ return ('a' - 38 + i);
+}
+
+
+static void crypt_make_salt(char *p, int cnt)
+{
+ unsigned x = x; /* it's pointless to initialize it anyway :) */
+
+ x += getpid() + time(NULL) + clock();
+ do {
+ /* x = (x*1664525 + 1013904223) % 2^32 generator is lame
+ * (low-order bit is not "random", etc...),
+ * but for our purposes it is good enough */
+ x = x*1664525 + 1013904223;
+ /* BTW, Park and Miller's "minimal standard generator" is
+ * x = x*16807 % ((2^31)-1)
+ * It has no problem with visibly alternating lowest bit
+ * but is also weak in cryptographic sense + needs div,
+ * which needs more code (and slower) on many CPUs */
+ *p++ = i64c(x >> 16);
+ *p++ = i64c(x >> 22);
+ } while (--cnt);
+ *p = '\0';
+}
+
+
+static char* new_password(const struct passwd *pw, uid_t myuid, int algo)
+{
+ char salt[sizeof("$N$XXXXXXXX")]; /* "$N$XXXXXXXX" or "XX" */
+ char *orig = (char*)"";
+ char *newp = NULL;
+ char *cipher = NULL;
+ char *cp = NULL;
+ char *ret = NULL; /* failure so far */
+
+ if (myuid && pw->pw_passwd[0]) {
+ orig = bb_askpass(0, "Old password:"); /* returns ptr to static */
+ if (!orig)
+ goto err_ret;
+ cipher = pw_encrypt(orig, pw->pw_passwd); /* returns ptr to static */
+ if (strcmp(cipher, pw->pw_passwd) != 0) {
+ syslog(LOG_WARNING, "incorrect password for '%s'",
+ pw->pw_name);
+ bb_do_delay(FAIL_DELAY);
+ puts("Incorrect password");
+ goto err_ret;
+ }
+ }
+ orig = xstrdup(orig); /* or else bb_askpass() will destroy it */
+ newp = bb_askpass(0, "New password:"); /* returns ptr to static */
+ if (!newp)
+ goto err_ret;
+ newp = xstrdup(newp); /* we are going to bb_askpass() again, so save it */
+ if (ENABLE_FEATURE_PASSWD_WEAK_CHECK
+ && obscure(orig, newp, pw) && myuid)
+ goto err_ret; /* non-root is not allowed to have weak passwd */
+
+ cp = bb_askpass(0, "Retype password:");
+ if (!cp)
+ goto err_ret;
+ if (strcmp(cp, newp)) {
+ puts("Passwords don't match");
+ goto err_ret;
+ }
+
+ /*memset(salt, 0, sizeof(salt)); - why?*/
+ crypt_make_salt(salt, 1); /* des */
+ if (algo) { /* MD5 */
+ strcpy(salt, "$1$");
+ crypt_make_salt(salt + 3, 4);
+ }
+ ret = xstrdup(pw_encrypt(newp, salt)); /* returns ptr to static */
+ /* whee, success! */
+
+ err_ret:
+ nuke_str(orig);
+ if (ENABLE_FEATURE_CLEAN_UP) free(orig);
+ nuke_str(newp);
+ if (ENABLE_FEATURE_CLEAN_UP) free(newp);
+ nuke_str(cipher);
+ nuke_str(cp);
+ return ret;
+}
+
+
+#if 0
+static int get_algo(char *a)
+{
+ /* standard: MD5 */
+ int x = 1;
+ if (strcasecmp(a, "des") == 0)
+ x = 0;
+ return x;
+}
+#endif
+
+
+static int update_passwd(const char *filename, const char *username,
+ const char *new_pw)
+{
+ struct stat sb;
+ struct flock lock;
+ FILE *old_fp;
+ FILE *new_fp;
+ char *new_name;
+ char *last_char;
+ unsigned user_len;
+ int old_fd;
+ int new_fd;
+ int i;
+ int ret = 1; /* failure */
+
+ logmode = LOGMODE_STDIO;
+ /* New passwd file, "/etc/passwd+" for now */
+ new_name = xasprintf("%s+", filename);
+ last_char = &new_name[strlen(new_name)-1];
+ username = xasprintf("%s:", username);
+ user_len = strlen(username);
+
+ old_fp = fopen(filename, "r+");
+ if (!old_fp)
+ goto free_mem;
+ old_fd = fileno(old_fp);
+
+ /* Try to create "/etc/passwd+". Wait if it exists. */
+ i = 30;
+ do {
+ // FIXME: on last iteration try w/o O_EXCL but with O_TRUNC?
+ new_fd = open(new_name, O_WRONLY|O_CREAT|O_EXCL,0600);
+ if (new_fd >= 0) goto created;
+ if (errno != EEXIST) break;
+ usleep(100000); /* 0.1 sec */
+ } while (--i);
+ bb_perror_msg("cannot create '%s'", new_name);
+ goto close_old_fp;
+ created:
+ if (!fstat(old_fd, &sb)) {
+ fchmod(new_fd, sb.st_mode & 0777); /* ignore errors */
+ fchown(new_fd, sb.st_uid, sb.st_gid);
+ }
+ new_fp = fdopen(new_fd, "w");
+ if (!new_fp) {
+ close(new_fd);
+ goto unlink_new;
+ }
+
+ /* Backup file is "/etc/passwd-" */
+ last_char[0] = '-';
+ /* Delete old one, create new as a hardlink to current */
+ i = (unlink(new_name) && errno != ENOENT);
+ if (i || link(filename, new_name))
+ bb_perror_msg("warning: cannot create backup copy '%s'", new_name);
+ last_char[0] = '+';
+
+ /* Lock the password file before updating */
+ lock.l_type = F_WRLCK;
+ lock.l_whence = SEEK_SET;
+ lock.l_start = 0;
+ lock.l_len = 0;
+ if (fcntl(old_fd, F_SETLK, &lock) < 0)
+ bb_perror_msg("warning: cannot lock '%s'", filename);
+ lock.l_type = F_UNLCK;
+
+ /* Read current password file, write updated one */
+ while (1) {
+ char *line = xmalloc_fgets(old_fp);
+ if (!line) break; /* EOF/error */
+ if (strncmp(username, line, user_len) == 0) {
+ /* we have a match with "username:"... */
+ const char *cp = line + user_len;
+ /* now cp -> old passwd, skip it: */
+ cp = strchr(cp, ':');
+ if (!cp) cp = "";
+ /* now cp -> ':' after old passwd or -> "" */
+ fprintf(new_fp, "%s%s%s", username, new_pw, cp);
+ /* Erase password in memory */
+ } else
+ fputs(line, new_fp);
+ free(line);
+ }
+ fcntl(old_fd, F_SETLK, &lock);
+
+ /* We do want all of them to execute, thus | instead of || */
+ if ((ferror(old_fp) | fflush(new_fp) | fsync(new_fd) | fclose(new_fp))
+ || rename(new_name, filename)
+ ) {
+ /* At least one of those failed */
+ goto unlink_new;
+ }
+ ret = 0; /* whee, success! */
+
+ unlink_new:
+ if (ret) unlink(new_name);
+
+ close_old_fp:
+ fclose(old_fp);
+
+ free_mem:
+ if (ENABLE_FEATURE_CLEAN_UP) free(new_name);
+ if (ENABLE_FEATURE_CLEAN_UP) free((char*)username);
+ logmode = LOGMODE_BOTH;
+ return ret;
+}
+
+
+int passwd_main(int argc, char **argv);
+int passwd_main(int argc, char **argv)
+{
+ enum {
+ OPT_algo = 0x1, /* -a - password algorithm */
+ OPT_lock = 0x2, /* -l - lock account */
+ OPT_unlock = 0x4, /* -u - unlock account */
+ OPT_delete = 0x8, /* -d - delete password */
+ OPT_lud = 0xe,
+ STATE_ALGO_md5 = 0x10,
+ /*STATE_ALGO_des = 0x20, not needed yet */
+ };
+ unsigned opt;
+ const char *opt_a = "";
+ const char *filename;
+ char *myname;
+ char *name;
+ char *newp;
+ struct passwd *pw;
+ uid_t myuid;
+ struct rlimit rlimit_fsize;
+ char c;
+
+#if ENABLE_FEATURE_SHADOWPASSWDS
+ /* Using _r function to avoid pulling in static buffers */
+ struct spwd spw;
+ struct spwd *result;
+ char buffer[256];
+#endif
+
+ logmode = LOGMODE_BOTH;
+ openlog(applet_name, LOG_NOWAIT, LOG_AUTH);
+ opt = getopt32(argc, argv, "a:lud", &opt_a);
+ argc -= optind;
+ argv += optind;
+
+ if (strcasecmp(opt_a, "des") != 0) /* -a */
+ opt |= STATE_ALGO_md5;
+ //else
+ // opt |= STATE_ALGO_des;
+ myuid = getuid();
+ if ((opt & OPT_lud) && (!argc || myuid))
+ bb_show_usage();
+
+ myname = xstrdup(bb_getpwuid(NULL, myuid, -1));
+ name = argc ? argv[0] : myname;
+
+ pw = getpwnam(name);
+ if (!pw) bb_error_msg_and_die("unknown user %s", name);
+ if (myuid && pw->pw_uid != myuid) {
+ /* LOGMODE_BOTH */
+ bb_error_msg_and_die("%s can't change password for %s", myname, name);
+ }
+
+ filename = bb_path_passwd_file;
+#if ENABLE_FEATURE_SHADOWPASSWDS
+ if (getspnam_r(pw->pw_name, &spw, buffer, sizeof(buffer), &result)) {
+ /* LOGMODE_BOTH */
+ bb_error_msg("no record of %s in %s, using %s",
+ name, bb_path_shadow_file,
+ bb_path_passwd_file);
+ } else {
+ filename = bb_path_shadow_file;
+ pw->pw_passwd = spw.sp_pwdp;
+ }
+#endif
+
+ /* Decide what the new password will be */
+ newp = NULL;
+ c = pw->pw_passwd[0] - '!';
+ if (!(opt & OPT_lud)) {
+ if (myuid && !c) { /* passwd starts with '!' */
+ /* LOGMODE_BOTH */
+ bb_error_msg_and_die("cannot change "
+ "locked password for %s", name);
+ }
+ printf("Changing password for %s\n", name);
+ newp = new_password(pw, myuid, opt & STATE_ALGO_md5);
+ if (!newp) {
+ logmode = LOGMODE_STDIO;
+ bb_error_msg_and_die("password for %s is unchanged", name);
+ }
+ } else if (opt & OPT_lock) {
+ if (!c) goto skip; /* passwd starts with '!' */
+ newp = xasprintf("!%s", pw->pw_passwd);
+ } else if (opt & OPT_unlock) {
+ if (c) goto skip; /* not '!' */
+ newp = xstrdup(&pw->pw_passwd[1]);
+ } else if (opt & OPT_delete) {
+ newp = xstrdup("");
+ }
+
+ rlimit_fsize.rlim_cur = rlimit_fsize.rlim_max = 512L * 30000;
+ setrlimit(RLIMIT_FSIZE, &rlimit_fsize);
+ signal(SIGHUP, SIG_IGN);
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ umask(077);
+ xsetuid(0);
+ if (update_passwd(filename, name, newp) != 0) {
+ /* LOGMODE_BOTH */
+ bb_error_msg_and_die("cannot update password file %s",
+ filename);
+ }
+ /* LOGMODE_BOTH */
+ bb_info_msg("Password for %s changed by %s", name, myname);
+
+ if (ENABLE_FEATURE_CLEAN_UP) free(newp);
+skip:
+ if (!newp) {
+ bb_error_msg_and_die("password for %s is already %slocked",
+ name, (opt & OPT_unlock) ? "un" : "");
+ }
+ if (ENABLE_FEATURE_CLEAN_UP) free(myname);
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/loginutils/su.c b/i/pc104/initrd/conf/busybox/loginutils/su.c
new file mode 100644
index 0000000..0a786cb
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/loginutils/su.c
@@ -0,0 +1,103 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini su implementation for busybox
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ */
+
+#include "busybox.h"
+#include <syslog.h>
+
+#define SU_OPT_mp (3)
+#define SU_OPT_l (4)
+
+int su_main(int argc, char **argv);
+int su_main(int argc, char **argv)
+{
+ unsigned flags;
+ char *opt_shell = NULL;
+ char *opt_command = NULL;
+ const char *opt_username = "root";
+ struct passwd *pw;
+ uid_t cur_uid = getuid();
+ const char *tty;
+ char *old_user;
+
+ flags = getopt32(argc, argv, "mplc:s:", &opt_command, &opt_shell);
+ argc -= optind;
+ argv += optind;
+
+ if (argc && LONE_DASH(argv[0])) {
+ flags |= SU_OPT_l;
+ argc--;
+ argv++;
+ }
+
+ /* get user if specified */
+ if (argc) {
+ opt_username = argv[0];
+// argc--;
+ argv++;
+ }
+
+ if (ENABLE_FEATURE_SU_SYSLOG) {
+ /* The utmp entry (via getlogin) is probably the best way to identify
+ the user, especially if someone su's from a su-shell.
+ But getlogin can fail -- usually due to lack of utmp entry.
+ in this case resort to getpwuid. */
+ old_user = xstrdup(USE_FEATURE_UTMP(getlogin() ? : ) (pw = getpwuid(cur_uid)) ? pw->pw_name : "");
+ tty = ttyname(2) ? : "none";
+ openlog(applet_name, 0, LOG_AUTH);
+ }
+
+ pw = getpwnam(opt_username);
+ if (!pw)
+ bb_error_msg_and_die("unknown id: %s", opt_username);
+
+ /* Make sure pw->pw_shell is non-NULL. It may be NULL when NEW_USER
+ is a username that is retrieved via NIS (YP), but that doesn't have
+ a default shell listed. */
+ if (!pw->pw_shell || !pw->pw_shell[0])
+ pw->pw_shell = (char *)DEFAULT_SHELL;
+
+ if ((cur_uid == 0) || correct_password(pw)) {
+ if (ENABLE_FEATURE_SU_SYSLOG)
+ syslog(LOG_NOTICE, "%c %s %s:%s",
+ '+', tty, old_user, opt_username);
+ } else {
+ if (ENABLE_FEATURE_SU_SYSLOG)
+ syslog(LOG_NOTICE, "%c %s %s:%s",
+ '-', tty, old_user, opt_username);
+ bb_error_msg_and_die("incorrect password");
+ }
+
+ if (ENABLE_FEATURE_CLEAN_UP && ENABLE_FEATURE_SU_SYSLOG) {
+ closelog();
+ free(old_user);
+ }
+
+ if (!opt_shell && (flags & SU_OPT_mp))
+ opt_shell = getenv("SHELL");
+
+#if ENABLE_FEATURE_SU_CHECKS_SHELLS
+ if (opt_shell && cur_uid && restricted_shell(pw->pw_shell)) {
+ /* The user being su'd to has a nonstandard shell, and so is
+ probably a uucp account or has restricted access. Don't
+ compromise the account by allowing access with a standard
+ shell. */
+ bb_error_msg("using restricted shell");
+ opt_shell = 0;
+ }
+#endif
+ if (!opt_shell)
+ opt_shell = pw->pw_shell;
+
+ change_identity(pw);
+ setup_environment(opt_shell, flags & SU_OPT_l, !(flags & SU_OPT_mp), pw);
+ USE_SELINUX(set_current_security_context(NULL);)
+
+ /* Never returns */
+ run_shell(opt_shell, flags & SU_OPT_l, opt_command, (const char**)argv);
+
+ return EXIT_FAILURE;
+}
diff --git a/i/pc104/initrd/conf/busybox/loginutils/sulogin.c b/i/pc104/initrd/conf/busybox/loginutils/sulogin.c
new file mode 100644
index 0000000..38503f7
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/loginutils/sulogin.c
@@ -0,0 +1,126 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini sulogin implementation for busybox
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include <syslog.h>
+
+#include "busybox.h"
+
+static const char * const forbid[] = {
+ "ENV",
+ "BASH_ENV",
+ "HOME",
+ "IFS",
+ "PATH",
+ "SHELL",
+ "LD_LIBRARY_PATH",
+ "LD_PRELOAD",
+ "LD_TRACE_LOADED_OBJECTS",
+ "LD_BIND_NOW",
+ "LD_AOUT_LIBRARY_PATH",
+ "LD_AOUT_PRELOAD",
+ "LD_NOWARN",
+ "LD_KEEPDIR",
+ (char *) 0
+};
+
+
+static void catchalarm(int ATTRIBUTE_UNUSED junk)
+{
+ exit(EXIT_FAILURE);
+}
+
+
+int sulogin_main(int argc, char **argv);
+int sulogin_main(int argc, char **argv)
+{
+ char *cp;
+ int timeout = 0;
+ char *timeout_arg;
+ const char * const *p;
+ struct passwd *pwd;
+ const char *shell;
+#if ENABLE_FEATURE_SHADOWPASSWDS
+ /* Using _r function to avoid pulling in static buffers */
+ char buffer[256];
+ struct spwd spw;
+ struct spwd *result;
+#endif
+
+ logmode = LOGMODE_BOTH;
+ openlog(applet_name, 0, LOG_AUTH);
+
+ if (getopt32(argc, argv, "t:", &timeout_arg)) {
+ timeout = xatoi_u(timeout_arg);
+ }
+
+ if (argv[optind]) {
+ close(0);
+ close(1);
+ dup(xopen(argv[optind], O_RDWR));
+ close(2);
+ dup(0);
+ }
+
+ if (!isatty(0) || !isatty(1) || !isatty(2)) {
+ logmode = LOGMODE_SYSLOG;
+ bb_error_msg_and_die("not a tty");
+ }
+
+ /* Clear out anything dangerous from the environment */
+ for (p = forbid; *p; p++)
+ unsetenv(*p);
+
+ signal(SIGALRM, catchalarm);
+
+ pwd = getpwuid(0);
+ if (!pwd) {
+ goto auth_error;
+ }
+
+#if ENABLE_FEATURE_SHADOWPASSWDS
+ if (getspnam_r(pwd->pw_name, &spw, buffer, sizeof(buffer), &result)) {
+ goto auth_error;
+ }
+ pwd->pw_passwd = spw.sp_pwdp;
+#endif
+
+ while (1) {
+ /* cp points to a static buffer that is zeroed every time */
+ cp = bb_askpass(timeout,
+ "Give root password for system maintenance\n"
+ "(or type Control-D for normal startup):");
+
+ if (!cp || !*cp) {
+ bb_info_msg("Normal startup");
+ return 0;
+ }
+ if (strcmp(pw_encrypt(cp, pwd->pw_passwd), pwd->pw_passwd) == 0) {
+ break;
+ }
+ bb_do_delay(FAIL_DELAY);
+ bb_error_msg("login incorrect");
+ }
+ memset(cp, 0, strlen(cp));
+ signal(SIGALRM, SIG_DFL);
+
+ bb_info_msg("System Maintenance Mode");
+
+ USE_SELINUX(renew_current_security_context());
+
+ shell = getenv("SUSHELL");
+ if (!shell) shell = getenv("sushell");
+ if (!shell) {
+ shell = "/bin/sh";
+ if (pwd->pw_shell[0])
+ shell = pwd->pw_shell;
+ }
+ run_shell(shell, 1, 0, 0);
+ /* never returns */
+
+auth_error:
+ bb_error_msg_and_die("no password entry for 'root'");
+}
diff --git a/i/pc104/initrd/conf/busybox/loginutils/vlock.c b/i/pc104/initrd/conf/busybox/loginutils/vlock.c
new file mode 100644
index 0000000..06a7169
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/loginutils/vlock.c
@@ -0,0 +1,119 @@
+/* vi: set sw=4 ts=4: */
+
+/*
+ * vlock implementation for busybox
+ *
+ * Copyright (C) 2000 by spoon <spoon@ix.netcom.com>
+ * Written by spoon <spon@ix.netcom.com>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/* Shoutz to Michael K. Johnson <johnsonm@redhat.com>, author of the
+ * original vlock. I snagged a bunch of his code to write this
+ * minimalistic vlock.
+ */
+/* Fixed by Erik Andersen to do passwords the tinylogin way...
+ * It now works with md5, sha1, etc passwords. */
+
+#include "busybox.h"
+#include <sys/vt.h>
+
+static struct passwd *pw;
+static struct vt_mode ovtm;
+static struct termios oterm;
+static int vfd;
+static unsigned long o_lock_all;
+
+static void release_vt(int signo)
+{
+ ioctl(vfd, VT_RELDISP, !o_lock_all);
+}
+
+static void acquire_vt(int signo)
+{
+ ioctl(vfd, VT_RELDISP, VT_ACKACQ);
+}
+
+static void restore_terminal(void)
+{
+ ioctl(vfd, VT_SETMODE, &ovtm);
+ tcsetattr(STDIN_FILENO, TCSANOW, &oterm);
+}
+
+int vlock_main(int argc, char **argv);
+int vlock_main(int argc, char **argv)
+{
+ sigset_t sig;
+ struct sigaction sa;
+ struct vt_mode vtm;
+ struct termios term;
+ uid_t uid = getuid();
+
+ pw = getpwuid(uid);
+ if (pw == NULL)
+ bb_error_msg_and_die("unknown uid %d", uid);
+
+ if (argc > 2) {
+ bb_show_usage();
+ }
+
+ o_lock_all = getopt32(argc, argv, "a");
+
+ vfd = xopen(CURRENT_TTY, O_RDWR);
+
+ if (ioctl(vfd, VT_GETMODE, &vtm) < 0) {
+ bb_perror_msg_and_die("VT_GETMODE");
+ }
+
+ /* mask a bunch of signals */
+ sigprocmask(SIG_SETMASK, NULL, &sig);
+ sigdelset(&sig, SIGUSR1);
+ sigdelset(&sig, SIGUSR2);
+ sigaddset(&sig, SIGTSTP);
+ sigaddset(&sig, SIGTTIN);
+ sigaddset(&sig, SIGTTOU);
+ sigaddset(&sig, SIGHUP);
+ sigaddset(&sig, SIGCHLD);
+ sigaddset(&sig, SIGQUIT);
+ sigaddset(&sig, SIGINT);
+
+ sigemptyset(&(sa.sa_mask));
+ sa.sa_flags = SA_RESTART;
+ sa.sa_handler = release_vt;
+ sigaction(SIGUSR1, &sa, NULL);
+ sa.sa_handler = acquire_vt;
+ sigaction(SIGUSR2, &sa, NULL);
+
+ /* need to handle some signals so that we don't get killed by them */
+ sa.sa_handler = SIG_IGN;
+ sigaction(SIGHUP, &sa, NULL);
+ sigaction(SIGQUIT, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL);
+ sigaction(SIGTSTP, &sa, NULL);
+
+ ovtm = vtm;
+ vtm.mode = VT_PROCESS;
+ vtm.relsig = SIGUSR1;
+ vtm.acqsig = SIGUSR2;
+ ioctl(vfd, VT_SETMODE, &vtm);
+
+ tcgetattr(STDIN_FILENO, &oterm);
+ term = oterm;
+ term.c_iflag &= ~BRKINT;
+ term.c_iflag |= IGNBRK;
+ term.c_lflag &= ~ISIG;
+ term.c_lflag &= ~(ECHO | ECHOCTL);
+ tcsetattr(STDIN_FILENO, TCSANOW, &term);
+
+ do {
+ printf("Virtual Console%s locked by %s.\n", (o_lock_all) ? "s" : "", pw->pw_name);
+ if (correct_password(pw)) {
+ break;
+ }
+ bb_do_delay(FAIL_DELAY);
+ puts("Password incorrect");
+ } while (1);
+ restore_terminal();
+ fflush_stdout_and_exit(0);
+}
diff --git a/i/pc104/initrd/conf/busybox/miscutils/Config.in b/i/pc104/initrd/conf/busybox/miscutils/Config.in
new file mode 100644
index 0000000..a1ed243
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/miscutils/Config.in
@@ -0,0 +1,387 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+menu "Miscellaneous Utilities"
+
+config ADJTIMEX
+ bool "adjtimex"
+ default n
+ help
+ Adjtimex reads and optionally sets adjustment parameters for
+ the Linux clock adjustment algorithm.
+
+config BBCONFIG
+ bool "bbconfig"
+ default n
+ help
+ The bbconfig applet will print the config file with which
+ busybox was built.
+
+config CHRT
+ bool "chrt"
+ default n
+ help
+ manipulate real-time attributes of a process.
+ This requires sched_{g,s}etparam support in your libc.
+
+config CROND
+ bool "crond"
+ default n
+ select FEATURE_SUID
+ select FEATURE_SYSLOG
+ help
+ Crond is a background daemon that parses individual crontab
+ files and executes commands on behalf of the users in question.
+ This is a port of dcron from slackware. It uses files of the
+ format /var/spool/cron/crontabs/<username> files, for example:
+ $ cat /var/spool/cron/crontabs/root
+ # Run daily cron jobs at 4:40 every day:
+ 40 4 * * * /etc/cron/daily > /dev/null 2>&1
+ Note that Busybox binary must be setuid root for this applet to
+ work properly.
+
+config DEBUG_CROND_OPTION
+ bool "Support debug option -d"
+ depends on CROND
+ default n
+ help
+ Support option -d to enter debug mode.
+
+config FEATURE_CROND_CALL_SENDMAIL
+ bool "Using /usr/sbin/sendmail?"
+ default n
+ depends on CROND
+ help
+ Support calling /usr/sbin/sendmail for send cmd outputs.
+
+config CRONTAB
+ bool "crontab"
+ default n
+ select FEATURE_SUID
+ help
+ Crontab manipulates the crontab for a particular user. Only
+ the superuser may specify a different user and/or crontab directory.
+
+config DC
+ bool "dc"
+ default n
+ help
+ Dc is a reverse-polish desk calculator which supports unlimited
+ precision arithmetic.
+
+config DEVFSD
+ bool "devfsd (obsolete)"
+ default n
+ select FEATURE_SYSLOG
+ help
+ This is deprecated, and will be removed at the end of 2008.
+
+ Provides compatibility with old device names on a devfs systems.
+ You should set it to true if you have devfs enabled.
+ The following keywords in devsfd.conf are supported:
+ "CLEAR_CONFIG", "INCLUDE", "OPTIONAL_INCLUDE", "RESTORE",
+ "PERMISSIONS", "EXECUTE", "COPY", "IGNORE",
+ "MKOLDCOMPAT", "MKNEWCOMPAT","RMOLDCOMPAT", "RMNEWCOMPAT".
+
+ But only if they are written UPPERCASE!!!!!!!!
+
+config DEVFSD_MODLOAD
+ bool "Adds support for MODLOAD keyword in devsfd.conf"
+ default n
+ depends on DEVFSD
+ help
+ This actually doesn't work with busybox modutils but needs
+ the external modutils.
+
+config DEVFSD_FG_NP
+ bool "Enables the -fg and -np options"
+ default n
+ depends on DEVFSD
+ help
+ -fg Run the daemon in the foreground.
+ -np Exit after parsing the configuration file. Do not poll for events.
+
+config DEVFSD_VERBOSE
+ bool "Increases logging (and size)"
+ default n
+ depends on DEVFSD
+ help
+ Increases logging to stderr or syslog.
+
+config FEATURE_DEVFS
+ bool " Use devfs names for all devices (obsolete)"
+ default n
+ help
+ This is obsolete and will be going away at the end of 2008..
+
+ This tells busybox to look for names like /dev/loop/0 instead of
+ /dev/loop0. If your /dev directory has normal names instead of
+ devfs names, you don't want this.
+
+config EJECT
+ bool "eject"
+ default n
+ help
+ Used to eject cdroms. (defaults to /dev/cdrom)
+
+config LAST
+ bool "last"
+ default n
+ select FEATURE_WTMP
+ help
+ 'last' displays a list of the last users that logged into the system.
+
+config LESS
+ bool "less"
+ default n
+ help
+ 'less' is a pager, meaning that it displays text files. It possesses
+ a wide array of features, and is an improvement over 'more'.
+
+config FEATURE_LESS_MAXLINES
+ int "Max number of input lines less will try to eat"
+ default 9999999
+ depends on LESS
+
+config FEATURE_LESS_BRACKETS
+ bool "Enable bracket searching"
+ default y
+ depends on LESS
+ help
+ This option adds the capability to search for matching left and right
+ brackets, facilitating programming.
+
+config FEATURE_LESS_FLAGS
+ bool "Enable extra flags"
+ default y
+ depends on LESS
+ help
+ The extra flags provided do the following:
+
+ The -M flag enables a more sophisticated status line.
+ The -m flag enables a simpler status line with a percentage.
+
+config FEATURE_LESS_FLAGCS
+ bool "Enable flag changes"
+ default n
+ depends on LESS
+ help
+ This enables the ability to change command-line flags within
+ less itself.
+
+config FEATURE_LESS_MARKS
+ bool "Enable marks"
+ default n
+ depends on LESS
+ help
+ Marks enable positions in a file to be stored for easy reference.
+
+config FEATURE_LESS_REGEXP
+ bool "Enable regular expressions"
+ default n
+ depends on LESS
+ help
+ Enable regular expressions, allowing complex file searches.
+
+config HDPARM
+ bool "hdparm"
+ default n
+ help
+ Get/Set hard drive parameters. Primarily intended for ATA
+ drives. Adds about 13k (or around 30k if you enable the
+ FEATURE_HDPARM_GET_IDENTITY option)....
+
+config FEATURE_HDPARM_GET_IDENTITY
+ bool "Support obtaining detailed information directly from drives"
+ default y
+ depends on HDPARM
+ help
+ Enables the -I and -i options to obtain detailed information
+ directly from drives about their capabilities and supported ATA
+ feature set. If no device name is specified, hdparm will read
+ identify data from stdin. Enabling this option will add about 16k...
+
+config FEATURE_HDPARM_HDIO_SCAN_HWIF
+ bool "Register an IDE interface (DANGEROUS)"
+ default n
+ depends on HDPARM
+ help
+ Enables the 'hdparm -R' option to register an IDE interface.
+ This is dangerous stuff, so you should probably say N.
+
+config FEATURE_HDPARM_HDIO_UNREGISTER_HWIF
+ bool "Un-register an IDE interface (DANGEROUS)"
+ default n
+ depends on HDPARM
+ help
+ Enables the 'hdparm -U' option to un-register an IDE interface.
+ This is dangerous stuff, so you should probably say N.
+
+config FEATURE_HDPARM_HDIO_DRIVE_RESET
+ bool "perform device reset (DANGEROUS)"
+ default n
+ depends on HDPARM
+ help
+ Enables the 'hdparm -w' option to perform a device reset.
+ This is dangerous stuff, so you should probably say N.
+
+config FEATURE_HDPARM_HDIO_TRISTATE_HWIF
+ bool "tristate device for hotswap (DANGEROUS)"
+ default n
+ depends on HDPARM
+ help
+ Enables the 'hdparm -x' option to tristate device for hotswap,
+ and the '-b' option to get/set bus state. This is dangerous
+ stuff, so you should probably say N.
+
+config FEATURE_HDPARM_HDIO_GETSET_DMA
+ bool "get/set using_dma flag (DANGEROUS)"
+ default n
+ depends on HDPARM
+ help
+ Enables the 'hdparm -d' option to get/set using_dma flag.
+ This is dangerous stuff, so you should probably say N.
+
+config MAKEDEVS
+ bool "makedevs"
+ default n
+ help
+ 'makedevs' is a utility used to create a batch of devices with
+ one command.
+ .
+ There are two choices for command line behaviour, the interface
+ as used by LEAF/Linux Router Project, or a device table file.
+ .
+ 'leaf' is traditionally what busybox follows, it allows multiple
+ devices of a particluar type to be created per command.
+ e.g. /dev/hda[0-9]
+ Device properties are passed as command line arguments.
+ .
+ 'table' reads device properties from a file or stdin, allowing
+ a batch of unrelated devices to be made with one command.
+ User/group names are allowed as an alternative to uid/gid.
+
+choice
+ prompt "Choose makedevs behaviour"
+ depends on MAKEDEVS
+ default FEATURE_MAKEDEVS_TABLE
+
+config FEATURE_MAKEDEVS_LEAF
+ bool "leaf"
+
+config FEATURE_MAKEDEVS_TABLE
+ bool "table"
+
+endchoice
+
+config MOUNTPOINT
+ bool "mountpoint"
+ default n
+ help
+ mountpoint checks if the directory is a mountpoint.
+
+config MT
+ bool "mt"
+ default n
+ help
+ mt is used to control tape devices. You can use the mt utility
+ to advance or rewind a tape past a specified number of archive
+ files on the tape.
+
+config NMETER
+ bool "nmeter"
+ default n
+ help
+ nmeter prints various system parameters continuously.
+
+config RAIDAUTORUN
+ bool "raidautorun"
+ default n
+ help
+ raidautorun tells the kernel md driver to
+ search and start RAID arrays.
+
+config READAHEAD
+ bool "readahead"
+ default n
+ depends on LFS
+ help
+ Preload the files listed on the command line into RAM cache so that
+ subsequent reads on these files will not block on disk I/O.
+
+ This applet just calls the readahead(2) system call on each file.
+ It is mainly useful in system startup scripts to preload files
+ or executables before they are used. When used at the right time
+ (in particular when a CPU boundprocess is running) it can
+ significantly speed up system startup.
+
+ As readahead(2) blocks until each file has been read, it is best to
+ run this applet as a background job.
+
+config RUNLEVEL
+ bool "runlevel"
+ default n
+ help
+ find the current and previous system runlevel.
+
+ This applet uses utmp but does not rely on busybox supporing
+ utmp on purpose. It is used by e.g. emdebian via /etc/init.d/rc.
+
+config RX
+ bool "rx"
+ default n
+ help
+ Receive files using the Xmodem protocol.
+
+config STRINGS
+ bool "strings"
+ default n
+ help
+ strings prints the printable character sequences for each file
+ specified.
+
+config SETSID
+ bool "setsid"
+ default n
+ help
+ setsid runs a program in a new session
+
+config TASKSET
+ bool "taskset"
+ default n
+ help
+ Retrieve or set a processes's CPU affinity.
+ This requires sched_{g,s}etaffinity support in your libc.
+
+config FEATURE_TASKSET_FANCY
+ bool "fancy output"
+ default y
+ depends on TASKSET
+ help
+ Add code for fancy output. This merely silences a compiler-warning
+ and adds about 135 Bytes. May be needed for machines with alot
+ of CPUs.
+
+config TIME
+ bool "time"
+ default n
+ help
+ The time command runs the specified program with the given arguments.
+ When the command finishes, time writes a message to standard output
+ giving timing statistics about this program run.
+
+config WATCHDOG
+ bool "watchdog"
+ default n
+ help
+ The watchdog utility is used with hardware or software watchdog
+ device drivers. It opens the specified watchdog device special file
+ and periodically writes a magic character to the device. If the
+ watchdog applet ever fails to write the magic character within a
+ certain amount of time, the watchdog device assumes the system has
+ hung, and will cause the hardware to reboot.
+
+endmenu
+
diff --git a/i/pc104/initrd/conf/busybox/miscutils/Kbuild b/i/pc104/initrd/conf/busybox/miscutils/Kbuild
new file mode 100644
index 0000000..4551137
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/miscutils/Kbuild
@@ -0,0 +1,31 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under the GPL v2, see the file LICENSE in this tarball.
+
+lib-y:=
+lib-$(CONFIG_ADJTIMEX) += adjtimex.o
+lib-$(CONFIG_BBCONFIG) += bbconfig.o
+lib-$(CONFIG_CHRT) += chrt.o
+lib-$(CONFIG_CROND) += crond.o
+lib-$(CONFIG_CRONTAB) += crontab.o
+lib-$(CONFIG_DC) += dc.o
+lib-$(CONFIG_DEVFSD) += devfsd.o
+lib-$(CONFIG_EJECT) += eject.o
+lib-$(CONFIG_HDPARM) += hdparm.o
+lib-$(CONFIG_LAST) += last.o
+lib-$(CONFIG_LESS) += less.o
+lib-$(CONFIG_MAKEDEVS) += makedevs.o
+lib-$(CONFIG_MOUNTPOINT) += mountpoint.o
+lib-$(CONFIG_MT) += mt.o
+lib-$(CONFIG_NMETER) += nmeter.o
+lib-$(CONFIG_RAIDAUTORUN) += raidautorun.o
+lib-$(CONFIG_READAHEAD) += readahead.o
+lib-$(CONFIG_RUNLEVEL) += runlevel.o
+lib-$(CONFIG_RX) += rx.o
+lib-$(CONFIG_SETSID) += setsid.o
+lib-$(CONFIG_STRINGS) += strings.o
+lib-$(CONFIG_TASKSET) += taskset.o
+lib-$(CONFIG_TIME) += time.o
+lib-$(CONFIG_WATCHDOG) += watchdog.o
diff --git a/i/pc104/initrd/conf/busybox/miscutils/adjtimex.c b/i/pc104/initrd/conf/busybox/miscutils/adjtimex.c
new file mode 100644
index 0000000..22fd39e
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/miscutils/adjtimex.c
@@ -0,0 +1,118 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * adjtimex.c - read, and possibly modify, the Linux kernel `timex' variables.
+ *
+ * Originally written: October 1997
+ * Last hack: March 2001
+ * Copyright 1997, 2000, 2001 Larry Doolittle <LRDoolittle@lbl.gov>
+ *
+ * busyboxed 20 March 2001, Larry Doolittle <ldoolitt@recycle.lbl.gov>
+ *
+ * Licensed under GPLv2 or later, see file License in this tarball for details.
+ */
+
+#include "busybox.h"
+#include <sys/timex.h>
+
+static const struct {int bit; const char *name;} statlist[] = {
+ { STA_PLL, "PLL" },
+ { STA_PPSFREQ, "PPSFREQ" },
+ { STA_PPSTIME, "PPSTIME" },
+ { STA_FLL, "FFL" },
+ { STA_INS, "INS" },
+ { STA_DEL, "DEL" },
+ { STA_UNSYNC, "UNSYNC" },
+ { STA_FREQHOLD, "FREQHOLD" },
+ { STA_PPSSIGNAL, "PPSSIGNAL" },
+ { STA_PPSJITTER, "PPSJITTER" },
+ { STA_PPSWANDER, "PPSWANDER" },
+ { STA_PPSERROR, "PPSERROR" },
+ { STA_CLOCKERR, "CLOCKERR" },
+ { 0, NULL } };
+
+static const char * const ret_code_descript[] = {
+ "clock synchronized",
+ "insert leap second",
+ "delete leap second",
+ "leap second in progress",
+ "leap second has occurred",
+ "clock not synchronized" };
+
+int adjtimex_main(int argc, char **argv);
+int adjtimex_main(int argc, char **argv)
+{
+ enum {
+ OPT_quiet = 0x1
+ };
+ unsigned opt;
+ char *opt_o, *opt_f, *opt_p, *opt_t;
+ struct timex txc;
+ int i, ret, sep;
+ const char *descript;
+ txc.modes=0;
+
+ opt = getopt32(argc, argv, "qo:f:p:t:",
+ &opt_o, &opt_f, &opt_p, &opt_t);
+ //if (opt & 0x1) // -q
+ if (opt & 0x2) { // -o
+ txc.offset = xatol(opt_o);
+ txc.modes |= ADJ_OFFSET_SINGLESHOT;
+ }
+ if (opt & 0x4) { // -f
+ txc.freq = xatol(opt_f);
+ txc.modes |= ADJ_FREQUENCY;
+ }
+ if (opt & 0x8) { // -p
+ txc.constant = xatol(opt_p);
+ txc.modes |= ADJ_TIMECONST;
+ }
+ if (opt & 0x10) { // -t
+ txc.tick = xatol(opt_t);
+ txc.modes |= ADJ_TICK;
+ }
+ if (argc != optind) { /* no valid non-option parameters */
+ bb_show_usage();
+ }
+
+ ret = adjtimex(&txc);
+
+ if (ret < 0) perror("adjtimex");
+
+ if (!(opt & OPT_quiet) && ret>=0) {
+ printf(
+ " mode: %d\n"
+ "-o offset: %ld\n"
+ "-f frequency: %ld\n"
+ " maxerror: %ld\n"
+ " esterror: %ld\n"
+ " status: %d ( ",
+ txc.modes, txc.offset, txc.freq, txc.maxerror,
+ txc.esterror, txc.status);
+
+ /* representative output of next code fragment:
+ "PLL | PPSTIME" */
+ sep=0;
+ for (i=0; statlist[i].name; i++) {
+ if (txc.status & statlist[i].bit) {
+ if (sep) fputs(" | ",stdout);
+ fputs(statlist[i].name,stdout);
+ sep=1;
+ }
+ }
+
+ descript = "error";
+ if (ret >= 0 && ret <= 5) descript = ret_code_descript[ret];
+ printf(" )\n"
+ "-p timeconstant: %ld\n"
+ " precision: %ld\n"
+ " tolerance: %ld\n"
+ "-t tick: %ld\n"
+ " time.tv_sec: %ld\n"
+ " time.tv_usec: %ld\n"
+ " return value: %d (%s)\n",
+ txc.constant,
+ txc.precision, txc.tolerance, txc.tick,
+ (long)txc.time.tv_sec, (long)txc.time.tv_usec, ret, descript);
+ }
+ return (ret<0);
+}
diff --git a/i/pc104/initrd/conf/busybox/miscutils/bbconfig.c b/i/pc104/initrd/conf/busybox/miscutils/bbconfig.c
new file mode 100644
index 0000000..6896506
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/miscutils/bbconfig.c
@@ -0,0 +1,12 @@
+/* vi: set sw=4 ts=4: */
+/* This file was released into the public domain by Paul Fox.
+ */
+#include "busybox.h"
+#include "bbconfigopts.h"
+
+int bbconfig_main(int argc, char **argv);
+int bbconfig_main(int argc, char **argv)
+{
+ printf(bbconfig_config);
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/miscutils/chrt.c b/i/pc104/initrd/conf/busybox/miscutils/chrt.c
new file mode 100644
index 0000000..75d77d5
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/miscutils/chrt.c
@@ -0,0 +1,124 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * chrt - manipulate real-time attributes of a process
+ * Copyright (c) 2006-2007 Bernhard Fischer
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "busybox.h"
+#include <unistd.h>
+#include <sched.h>
+#include <getopt.h> /* optind */
+#ifndef _POSIX_PRIORITY_SCHEDULING
+#warning your system may be foobared
+#endif
+static const struct {
+ const int policy;
+ const char const name[12];
+} policies[] = {
+ {SCHED_OTHER, "SCHED_OTHER"},
+ {SCHED_FIFO, "SCHED_FIFO"},
+ {SCHED_RR, "SCHED_RR"}
+};
+
+static void show_min_max(int pol) {
+ const char *fmt = "%s min/max priority\t: %d/%d\n\0%s not supported?\n";
+ int max, min;
+ max = sched_get_priority_max(pol);
+ min = sched_get_priority_min(pol);
+ if (max >= 0 && min >= 0)
+ printf(fmt, policies[pol].name, min, max);
+ else {
+ fmt += 29;
+ printf(fmt, policies[pol].name);
+ }
+}
+
+#define OPT_m (1<<0)
+#define OPT_p (1<<1)
+#define OPT_r (1<<2)
+#define OPT_f (1<<3)
+#define OPT_o (1<<4)
+
+int chrt_main(int argc, char** argv);
+int chrt_main(int argc, char** argv)
+{
+ pid_t pid = 0;
+ unsigned opt;
+ struct sched_param sp;
+ char *p_opt = NULL, *priority = NULL;
+ const char *state = "current\0new";
+ int prio = 0, policy = SCHED_RR;
+
+ opt_complementary = "r--fo:f--ro:r--fo"; /* only one policy accepted */
+ opt = getopt32(argc, argv, "+mp:rfo", &p_opt);
+ if (opt & OPT_r)
+ policy = SCHED_RR;
+ if (opt & OPT_f)
+ policy = SCHED_FIFO;
+ if (opt & OPT_o)
+ policy = SCHED_OTHER;
+
+ if (opt & OPT_m) { /* print min/max */
+ show_min_max(SCHED_FIFO);
+ show_min_max(SCHED_RR);
+ show_min_max(SCHED_OTHER);
+ fflush_stdout_and_exit(EXIT_SUCCESS);
+ }
+ if (opt & OPT_p) {
+ if (argc == optind+1) { /* -p <priority> <pid> */
+ priority = p_opt;
+ p_opt = argv[optind];
+ }
+ argv += optind; /* me -p <arg> */
+ pid = xatoul_range(p_opt, 1, ULONG_MAX); /* -p <pid> */
+ } else {
+ argv += optind; /* me -p <arg> */
+ priority = *argv;
+ }
+ if (priority) {
+ /* from the manpage of sched_getscheduler:
+ [...] sched_priority can have a value
+ in the range 0 to 99.
+ [...] SCHED_OTHER or SCHED_BATCH must be assigned
+ the static priority 0. [...] SCHED_FIFO or
+ SCHED_RR can have a static priority in the range 1 to 99.
+ */
+ prio = xstrtol_range(priority, 0, policy == SCHED_OTHER
+ ? 0 : 1, 99);
+ }
+
+ if (opt & OPT_p) {
+ int pol = 0;
+print_rt_info:
+ pol = sched_getscheduler(pid);
+ if (pol < 0)
+ bb_perror_msg_and_die("failed to %cet pid %d's policy", 'g', pid);
+ printf("pid %d's %s scheduling policy: %s\n",
+ pid, state, policies[pol].name);
+ if (sched_getparam(pid, &sp))
+ bb_perror_msg_and_die("failed to get pid %d's attributes", pid);
+ printf("pid %d's %s scheduling priority: %d\n",
+ pid, state, sp.sched_priority);
+ if (!*argv) /* no new prio given or we did print already, done. */
+ return EXIT_SUCCESS;
+ }
+
+ sp.sched_priority = prio;
+ if (sched_setscheduler(pid, policy, &sp) < 0)
+ bb_perror_msg_and_die("failed to %cet pid %d's policy", 's', pid);
+ if (opt & OPT_p) {
+ state += 8;
+ ++argv;
+ goto print_rt_info;
+ }
+ ++argv;
+ BB_EXECVP(*argv, argv);
+ bb_perror_msg_and_die("%s", *argv);
+}
+#undef OPT_p
+#undef OPT_r
+#undef OPT_f
+#undef OPT_o
+#undef OPT_m
diff --git a/i/pc104/initrd/conf/busybox/miscutils/crond.c b/i/pc104/initrd/conf/busybox/miscutils/crond.c
new file mode 100644
index 0000000..d237a67
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/miscutils/crond.c
@@ -0,0 +1,1017 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * crond -d[#] -c <crondir> -f -b
+ *
+ * run as root, but NOT setuid root
+ *
+ * Copyright 1994 Matthew Dillon (dillon@apollo.west.oic.com)
+ * Vladimir Oleynik <dzo@simtreas.ru> (C) 2002
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ */
+
+#define VERSION "2.3.2"
+
+#include "busybox.h"
+#include <sys/syslog.h>
+
+#define arysize(ary) (sizeof(ary)/sizeof((ary)[0]))
+
+#ifndef CRONTABS
+#define CRONTABS "/var/spool/cron/crontabs"
+#endif
+#ifndef TMPDIR
+#define TMPDIR "/var/spool/cron"
+#endif
+#ifndef SENDMAIL
+#define SENDMAIL "/usr/sbin/sendmail"
+#endif
+#ifndef SENDMAIL_ARGS
+#define SENDMAIL_ARGS "-ti", "oem"
+#endif
+#ifndef CRONUPDATE
+#define CRONUPDATE "cron.update"
+#endif
+#ifndef MAXLINES
+#define MAXLINES 256 /* max lines in non-root crontabs */
+#endif
+
+typedef struct CronFile {
+ struct CronFile *cf_Next;
+ struct CronLine *cf_LineBase;
+ char *cf_User; /* username */
+ int cf_Ready; /* bool: one or more jobs ready */
+ int cf_Running; /* bool: one or more jobs running */
+ int cf_Deleted; /* marked for deletion, ignore */
+} CronFile;
+
+typedef struct CronLine {
+ struct CronLine *cl_Next;
+ char *cl_Shell; /* shell command */
+ pid_t cl_Pid; /* running pid, 0, or armed (-1) */
+ int cl_MailFlag; /* running pid is for mail */
+ int cl_MailPos; /* 'empty file' size */
+ char cl_Mins[60]; /* 0-59 */
+ char cl_Hrs[24]; /* 0-23 */
+ char cl_Days[32]; /* 1-31 */
+ char cl_Mons[12]; /* 0-11 */
+ char cl_Dow[7]; /* 0-6, beginning sunday */
+} CronLine;
+
+#define RUN_RANOUT 1
+#define RUN_RUNNING 2
+#define RUN_FAILED 3
+
+#define DaemonUid 0
+
+#if ENABLE_DEBUG_CROND_OPTION
+static unsigned DebugOpt;
+#endif
+
+static unsigned LogLevel = 8;
+static const char *LogFile;
+static const char *CDir = CRONTABS;
+
+static void startlogger(void);
+
+static void CheckUpdates(void);
+static void SynchronizeDir(void);
+static int TestJobs(time_t t1, time_t t2);
+static void RunJobs(void);
+static int CheckJobs(void);
+
+static void RunJob(const char *user, CronLine * line);
+
+#if ENABLE_FEATURE_CROND_CALL_SENDMAIL
+static void EndJob(const char *user, CronLine * line);
+#else
+#define EndJob(user, line) line->cl_Pid = 0
+#endif
+
+static void DeleteFile(const char *userName);
+
+static CronFile *FileBase;
+
+
+static void crondlog(const char *ctl, ...)
+{
+ va_list va;
+ const char *fmt;
+ int level = (int) (ctl[0] & 0xf);
+ int type = level == 20 ?
+ LOG_ERR : ((ctl[0] & 0100) ? LOG_WARNING : LOG_NOTICE);
+
+
+ va_start(va, ctl);
+ fmt = ctl + 1;
+ if (level >= LogLevel) {
+
+#if ENABLE_DEBUG_CROND_OPTION
+ if (DebugOpt) {
+ vfprintf(stderr, fmt, va);
+ } else
+#endif
+ if (LogFile == 0) {
+ vsyslog(type, fmt, va);
+ } else {
+ int logfd = open(LogFile, O_WRONLY | O_CREAT | O_APPEND, 0600);
+ if (logfd >= 0) {
+ vdprintf(logfd, fmt, va);
+ close(logfd);
+#if ENABLE_DEBUG_CROND_OPTION
+ } else {
+ bb_perror_msg("can't open log file");
+#endif
+ }
+ }
+ }
+ va_end(va);
+ if (ctl[0] & 0200) {
+ exit(20);
+ }
+}
+
+int crond_main(int ac, char **av);
+int crond_main(int ac, char **av)
+{
+ unsigned opt;
+ char *lopt, *Lopt, *copt;
+
+#if ENABLE_DEBUG_CROND_OPTION
+ char *dopt;
+
+ opt_complementary = "f-b:b-f:S-L:L-S:d-l";
+#else
+ opt_complementary = "f-b:b-f:S-L:L-S";
+#endif
+
+ opterr = 0; /* disable getopt 'errors' message. */
+ opt = getopt32(ac, av, "l:L:fbSc:"
+#if ENABLE_DEBUG_CROND_OPTION
+ "d:"
+#endif
+ , &lopt, &Lopt, &copt
+#if ENABLE_DEBUG_CROND_OPTION
+ , &dopt
+#endif
+ );
+ if (opt & 1) {
+ LogLevel = xatou(lopt);
+ }
+ if (opt & 2) {
+ if (*Lopt != 0) {
+ LogFile = Lopt;
+ }
+ }
+ if (opt & 32) {
+ if (*copt != 0) {
+ CDir = copt;
+ }
+ }
+#if ENABLE_DEBUG_CROND_OPTION
+ if (opt & 64) {
+ DebugOpt = xatou(dopt);
+ LogLevel = 0;
+ }
+#endif
+
+ /*
+ * change directory
+ */
+
+ xchdir(CDir);
+ signal(SIGHUP, SIG_IGN); /* hmm.. but, if kill -HUP original
+ * version - his died. ;(
+ */
+ /*
+ * close stdin and stdout, stderr.
+ * close unused descriptors - don't need.
+ * optional detach from controlling terminal
+ */
+
+ if (!(opt & 4)) {
+#ifdef BB_NOMMU
+ /* reexec for vfork() do continue parent */
+ vfork_daemon_rexec(1, 0, ac, av, "-f");
+#else
+ xdaemon(1, 0);
+#endif
+ }
+
+ (void) startlogger(); /* need if syslog mode selected */
+
+ /*
+ * main loop - synchronize to 1 second after the minute, minimum sleep
+ * of 1 second.
+ */
+
+ crondlog("\011%s " VERSION " dillon, started, log level %d\n",
+ applet_name, LogLevel);
+
+ SynchronizeDir();
+
+ {
+ time_t t1 = time(NULL);
+ time_t t2;
+ long dt;
+ int rescan = 60;
+ short sleep_time = 60;
+
+ for (;;) {
+ sleep((sleep_time + 1) - (short) (time(NULL) % sleep_time));
+
+ t2 = time(NULL);
+ dt = t2 - t1;
+
+ /*
+ * The file 'cron.update' is checked to determine new cron
+ * jobs. The directory is rescanned once an hour to deal
+ * with any screwups.
+ *
+ * check for disparity. Disparities over an hour either way
+ * result in resynchronization. A reverse-indexed disparity
+ * less then an hour causes us to effectively sleep until we
+ * match the original time (i.e. no re-execution of jobs that
+ * have just been run). A forward-indexed disparity less then
+ * an hour causes intermediate jobs to be run, but only once
+ * in the worst case.
+ *
+ * when running jobs, the inequality used is greater but not
+ * equal to t1, and less then or equal to t2.
+ */
+
+ if (--rescan == 0) {
+ rescan = 60;
+ SynchronizeDir();
+ }
+ CheckUpdates();
+#if ENABLE_DEBUG_CROND_OPTION
+ if (DebugOpt)
+ crondlog("\005Wakeup dt=%d\n", dt);
+#endif
+ if (dt < -60 * 60 || dt > 60 * 60) {
+ t1 = t2;
+ crondlog("\111time disparity of %d minutes detected\n", dt / 60);
+ } else if (dt > 0) {
+ TestJobs(t1, t2);
+ RunJobs();
+ sleep(5);
+ if (CheckJobs() > 0) {
+ sleep_time = 10;
+ } else {
+ sleep_time = 60;
+ }
+ t1 = t2;
+ }
+ }
+ }
+ return 0; /* not reached */
+}
+
+static int ChangeUser(const char *user)
+{
+ struct passwd *pas;
+ const char *err_msg;
+
+ /*
+ * Obtain password entry and change privileges
+ */
+ pas = getpwnam(user);
+ if (pas == 0) {
+ crondlog("\011failed to get uid for %s", user);
+ return -1;
+ }
+ setenv("USER", pas->pw_name, 1);
+ setenv("HOME", pas->pw_dir, 1);
+ setenv("SHELL", DEFAULT_SHELL, 1);
+
+ /*
+ * Change running state to the user in question
+ */
+ err_msg = change_identity_e2str(pas);
+ if (err_msg) {
+ crondlog("\011%s for user %s", err_msg, user);
+ return -1;
+ }
+ if (chdir(pas->pw_dir) < 0) {
+ crondlog("\011chdir failed: %s: %m", pas->pw_dir);
+ if (chdir(TMPDIR) < 0) {
+ crondlog("\011chdir failed: %s: %m", TMPDIR);
+ return -1;
+ }
+ }
+ return pas->pw_uid;
+}
+
+static void startlogger(void)
+{
+ if (LogFile == 0) {
+ openlog(applet_name, LOG_CONS | LOG_PID, LOG_CRON);
+ }
+#if ENABLE_DEBUG_CROND_OPTION
+ else { /* test logfile */
+ int logfd;
+
+ if ((logfd = open(LogFile, O_WRONLY | O_CREAT | O_APPEND, 0600)) >= 0) {
+ close(logfd);
+ } else {
+ bb_perror_msg("failed to open log file '%s': ", LogFile);
+ }
+ }
+#endif
+}
+
+
+static const char *const DowAry[] = {
+ "sun",
+ "mon",
+ "tue",
+ "wed",
+ "thu",
+ "fri",
+ "sat",
+
+ "Sun",
+ "Mon",
+ "Tue",
+ "Wed",
+ "Thu",
+ "Fri",
+ "Sat",
+ NULL
+};
+
+static const char *const MonAry[] = {
+ "jan",
+ "feb",
+ "mar",
+ "apr",
+ "may",
+ "jun",
+ "jul",
+ "aug",
+ "sep",
+ "oct",
+ "nov",
+ "dec",
+
+ "Jan",
+ "Feb",
+ "Mar",
+ "Apr",
+ "May",
+ "Jun",
+ "Jul",
+ "Aug",
+ "Sep",
+ "Oct",
+ "Nov",
+ "Dec",
+ NULL
+};
+
+static char *ParseField(char *user, char *ary, int modvalue, int off,
+ const char *const *names, char *ptr)
+{
+ char *base = ptr;
+ int n1 = -1;
+ int n2 = -1;
+
+ if (base == NULL) {
+ return NULL;
+ }
+
+ while (*ptr != ' ' && *ptr != '\t' && *ptr != '\n') {
+ int skip = 0;
+
+ /* Handle numeric digit or symbol or '*' */
+
+ if (*ptr == '*') {
+ n1 = 0; /* everything will be filled */
+ n2 = modvalue - 1;
+ skip = 1;
+ ++ptr;
+ } else if (*ptr >= '0' && *ptr <= '9') {
+ if (n1 < 0) {
+ n1 = strtol(ptr, &ptr, 10) + off;
+ } else {
+ n2 = strtol(ptr, &ptr, 10) + off;
+ }
+ skip = 1;
+ } else if (names) {
+ int i;
+
+ for (i = 0; names[i]; ++i) {
+ if (strncmp(ptr, names[i], strlen(names[i])) == 0) {
+ break;
+ }
+ }
+ if (names[i]) {
+ ptr += strlen(names[i]);
+ if (n1 < 0) {
+ n1 = i;
+ } else {
+ n2 = i;
+ }
+ skip = 1;
+ }
+ }
+
+ /* handle optional range '-' */
+
+ if (skip == 0) {
+ crondlog("\111failed user %s parsing %s\n", user, base);
+ return NULL;
+ }
+ if (*ptr == '-' && n2 < 0) {
+ ++ptr;
+ continue;
+ }
+
+ /*
+ * collapse single-value ranges, handle skipmark, and fill
+ * in the character array appropriately.
+ */
+
+ if (n2 < 0) {
+ n2 = n1;
+ }
+ if (*ptr == '/') {
+ skip = strtol(ptr + 1, &ptr, 10);
+ }
+ /*
+ * fill array, using a failsafe is the easiest way to prevent
+ * an endless loop
+ */
+
+ {
+ int s0 = 1;
+ int failsafe = 1024;
+
+ --n1;
+ do {
+ n1 = (n1 + 1) % modvalue;
+
+ if (--s0 == 0) {
+ ary[n1 % modvalue] = 1;
+ s0 = skip;
+ }
+ }
+ while (n1 != n2 && --failsafe);
+
+ if (failsafe == 0) {
+ crondlog("\111failed user %s parsing %s\n", user, base);
+ return NULL;
+ }
+ }
+ if (*ptr != ',') {
+ break;
+ }
+ ++ptr;
+ n1 = -1;
+ n2 = -1;
+ }
+
+ if (*ptr != ' ' && *ptr != '\t' && *ptr != '\n') {
+ crondlog("\111failed user %s parsing %s\n", user, base);
+ return NULL;
+ }
+
+ while (*ptr == ' ' || *ptr == '\t' || *ptr == '\n') {
+ ++ptr;
+ }
+#if ENABLE_DEBUG_CROND_OPTION
+ if (DebugOpt) {
+ int i;
+
+ for (i = 0; i < modvalue; ++i) {
+ crondlog("\005%d", ary[i]);
+ }
+ crondlog("\005\n");
+ }
+#endif
+
+ return ptr;
+}
+
+static void FixDayDow(CronLine * line)
+{
+ int i;
+ int weekUsed = 0;
+ int daysUsed = 0;
+
+ for (i = 0; i < (int)(arysize(line->cl_Dow)); ++i) {
+ if (line->cl_Dow[i] == 0) {
+ weekUsed = 1;
+ break;
+ }
+ }
+ for (i = 0; i < (int)(arysize(line->cl_Days)); ++i) {
+ if (line->cl_Days[i] == 0) {
+ daysUsed = 1;
+ break;
+ }
+ }
+ if (weekUsed && !daysUsed) {
+ memset(line->cl_Days, 0, sizeof(line->cl_Days));
+ }
+ if (daysUsed && !weekUsed) {
+ memset(line->cl_Dow, 0, sizeof(line->cl_Dow));
+ }
+}
+
+
+
+static void SynchronizeFile(const char *fileName)
+{
+ int maxEntries = MAXLINES;
+ int maxLines;
+ char buf[1024];
+
+ if (strcmp(fileName, "root") == 0) {
+ maxEntries = 65535;
+ }
+ maxLines = maxEntries * 10;
+
+ if (fileName) {
+ FILE *fi;
+
+ DeleteFile(fileName);
+
+ fi = fopen(fileName, "r");
+ if (fi != NULL) {
+ struct stat sbuf;
+
+ if (fstat(fileno(fi), &sbuf) == 0 && sbuf.st_uid == DaemonUid) {
+ CronFile *file = xzalloc(sizeof(CronFile));
+ CronLine **pline;
+
+ file->cf_User = strdup(fileName);
+ pline = &file->cf_LineBase;
+
+ while (fgets(buf, sizeof(buf), fi) != NULL && --maxLines) {
+ CronLine line;
+ char *ptr;
+
+ trim(buf);
+ if (buf[0] == 0 || buf[0] == '#') {
+ continue;
+ }
+ if (--maxEntries == 0) {
+ break;
+ }
+ memset(&line, 0, sizeof(line));
+
+#if ENABLE_DEBUG_CROND_OPTION
+ if (DebugOpt) {
+ crondlog("\111User %s Entry %s\n", fileName, buf);
+ }
+#endif
+
+ /* parse date ranges */
+ ptr = ParseField(file->cf_User, line.cl_Mins, 60, 0, NULL, buf);
+ ptr = ParseField(file->cf_User, line.cl_Hrs, 24, 0, NULL, ptr);
+ ptr = ParseField(file->cf_User, line.cl_Days, 32, 0, NULL, ptr);
+ ptr = ParseField(file->cf_User, line.cl_Mons, 12, -1, MonAry, ptr);
+ ptr = ParseField(file->cf_User, line.cl_Dow, 7, 0, DowAry, ptr);
+
+ /* check failure */
+ if (ptr == NULL) {
+ continue;
+ }
+
+ /*
+ * fix days and dow - if one is not * and the other
+ * is *, the other is set to 0, and vise-versa
+ */
+
+ FixDayDow(&line);
+
+ *pline = xzalloc(sizeof(CronLine));
+ **pline = line;
+
+ /* copy command */
+ (*pline)->cl_Shell = strdup(ptr);
+
+#if ENABLE_DEBUG_CROND_OPTION
+ if (DebugOpt) {
+ crondlog("\111 Command %s\n", ptr);
+ }
+#endif
+
+ pline = &((*pline)->cl_Next);
+ }
+ *pline = NULL;
+
+ file->cf_Next = FileBase;
+ FileBase = file;
+
+ if (maxLines == 0 || maxEntries == 0) {
+ crondlog("\111Maximum number of lines reached for user %s\n", fileName);
+ }
+ }
+ fclose(fi);
+ }
+ }
+}
+
+static void CheckUpdates(void)
+{
+ FILE *fi;
+ char buf[256];
+
+ fi = fopen(CRONUPDATE, "r");
+ if (fi != NULL) {
+ remove(CRONUPDATE);
+ while (fgets(buf, sizeof(buf), fi) != NULL) {
+ SynchronizeFile(strtok(buf, " \t\r\n"));
+ }
+ fclose(fi);
+ }
+}
+
+static void SynchronizeDir(void)
+{
+ /* Attempt to delete the database. */
+
+ for (;;) {
+ CronFile *file;
+
+ for (file = FileBase; file && file->cf_Deleted; file = file->cf_Next);
+ if (file == NULL) {
+ break;
+ }
+ DeleteFile(file->cf_User);
+ }
+
+ /*
+ * Remove cron update file
+ *
+ * Re-chdir, in case directory was renamed & deleted, or otherwise
+ * screwed up.
+ *
+ * scan directory and add associated users
+ */
+
+ remove(CRONUPDATE);
+ if (chdir(CDir) < 0) {
+ crondlog("\311cannot find %s\n", CDir);
+ }
+ {
+ DIR *dir = opendir(".");
+ struct dirent *den;
+
+ if (dir) {
+ while ((den = readdir(dir))) {
+ if (strchr(den->d_name, '.') != NULL) {
+ continue;
+ }
+ if (getpwnam(den->d_name)) {
+ SynchronizeFile(den->d_name);
+ } else {
+ crondlog("\007ignoring %s\n", den->d_name);
+ }
+ }
+ closedir(dir);
+ } else {
+ crondlog("\311cannot open current dir!\n");
+ }
+ }
+}
+
+
+/*
+ * DeleteFile() - delete user database
+ *
+ * Note: multiple entries for same user may exist if we were unable to
+ * completely delete a database due to running processes.
+ */
+
+static void DeleteFile(const char *userName)
+{
+ CronFile **pfile = &FileBase;
+ CronFile *file;
+
+ while ((file = *pfile) != NULL) {
+ if (strcmp(userName, file->cf_User) == 0) {
+ CronLine **pline = &file->cf_LineBase;
+ CronLine *line;
+
+ file->cf_Running = 0;
+ file->cf_Deleted = 1;
+
+ while ((line = *pline) != NULL) {
+ if (line->cl_Pid > 0) {
+ file->cf_Running = 1;
+ pline = &line->cl_Next;
+ } else {
+ *pline = line->cl_Next;
+ free(line->cl_Shell);
+ free(line);
+ }
+ }
+ if (file->cf_Running == 0) {
+ *pfile = file->cf_Next;
+ free(file->cf_User);
+ free(file);
+ } else {
+ pfile = &file->cf_Next;
+ }
+ } else {
+ pfile = &file->cf_Next;
+ }
+ }
+}
+
+/*
+ * TestJobs()
+ *
+ * determine which jobs need to be run. Under normal conditions, the
+ * period is about a minute (one scan). Worst case it will be one
+ * hour (60 scans).
+ */
+
+static int TestJobs(time_t t1, time_t t2)
+{
+ int nJobs = 0;
+ time_t t;
+
+ /* Find jobs > t1 and <= t2 */
+
+ for (t = t1 - t1 % 60; t <= t2; t += 60) {
+ if (t > t1) {
+ struct tm *tp = localtime(&t);
+ CronFile *file;
+ CronLine *line;
+
+ for (file = FileBase; file; file = file->cf_Next) {
+#if ENABLE_DEBUG_CROND_OPTION
+ if (DebugOpt)
+ crondlog("\005FILE %s:\n", file->cf_User);
+#endif
+ if (file->cf_Deleted)
+ continue;
+ for (line = file->cf_LineBase; line; line = line->cl_Next) {
+#if ENABLE_DEBUG_CROND_OPTION
+ if (DebugOpt)
+ crondlog("\005 LINE %s\n", line->cl_Shell);
+#endif
+ if (line->cl_Mins[tp->tm_min] && line->cl_Hrs[tp->tm_hour] &&
+ (line->cl_Days[tp->tm_mday] || line->cl_Dow[tp->tm_wday])
+ && line->cl_Mons[tp->tm_mon]) {
+#if ENABLE_DEBUG_CROND_OPTION
+ if (DebugOpt) {
+ crondlog("\005 JobToDo: %d %s\n",
+ line->cl_Pid, line->cl_Shell);
+ }
+#endif
+ if (line->cl_Pid > 0) {
+ crondlog("\010 process already running: %s %s\n",
+ file->cf_User, line->cl_Shell);
+ } else if (line->cl_Pid == 0) {
+ line->cl_Pid = -1;
+ file->cf_Ready = 1;
+ ++nJobs;
+ }
+ }
+ }
+ }
+ }
+ }
+ return nJobs;
+}
+
+static void RunJobs(void)
+{
+ CronFile *file;
+ CronLine *line;
+
+ for (file = FileBase; file; file = file->cf_Next) {
+ if (file->cf_Ready) {
+ file->cf_Ready = 0;
+
+ for (line = file->cf_LineBase; line; line = line->cl_Next) {
+ if (line->cl_Pid < 0) {
+
+ RunJob(file->cf_User, line);
+
+ crondlog("\010USER %s pid %3d cmd %s\n",
+ file->cf_User, line->cl_Pid, line->cl_Shell);
+ if (line->cl_Pid < 0) {
+ file->cf_Ready = 1;
+ }
+ else if (line->cl_Pid > 0) {
+ file->cf_Running = 1;
+ }
+ }
+ }
+ }
+ }
+}
+
+/*
+ * CheckJobs() - check for job completion
+ *
+ * Check for job completion, return number of jobs still running after
+ * all done.
+ */
+
+static int CheckJobs(void)
+{
+ CronFile *file;
+ CronLine *line;
+ int nStillRunning = 0;
+
+ for (file = FileBase; file; file = file->cf_Next) {
+ if (file->cf_Running) {
+ file->cf_Running = 0;
+
+ for (line = file->cf_LineBase; line; line = line->cl_Next) {
+ if (line->cl_Pid > 0) {
+ int status;
+ int r = wait4(line->cl_Pid, &status, WNOHANG, NULL);
+
+ if (r < 0 || r == line->cl_Pid) {
+ EndJob(file->cf_User, line);
+ if (line->cl_Pid) {
+ file->cf_Running = 1;
+ }
+ } else if (r == 0) {
+ file->cf_Running = 1;
+ }
+ }
+ }
+ }
+ nStillRunning += file->cf_Running;
+ }
+ return nStillRunning;
+}
+
+
+#if ENABLE_FEATURE_CROND_CALL_SENDMAIL
+static void
+ForkJob(const char *user, CronLine * line, int mailFd,
+ const char *prog, const char *cmd, const char *arg, const char *mailf)
+{
+ /* Fork as the user in question and run program */
+ pid_t pid = fork();
+
+ line->cl_Pid = pid;
+ if (pid == 0) {
+ /* CHILD */
+
+ /* Change running state to the user in question */
+
+ if (ChangeUser(user) < 0) {
+ exit(0);
+ }
+#if ENABLE_DEBUG_CROND_OPTION
+ if (DebugOpt) {
+ crondlog("\005Child Running %s\n", prog);
+ }
+#endif
+
+ if (mailFd >= 0) {
+ dup2(mailFd, mailf != NULL);
+ dup2((mailf ? mailFd : 1), 2);
+ close(mailFd);
+ }
+ execl(prog, prog, cmd, arg, NULL);
+ crondlog("\024cannot exec, user %s cmd %s %s %s\n", user, prog, cmd, arg);
+ if (mailf) {
+ fdprintf(1, "Exec failed: %s -c %s\n", prog, arg);
+ }
+ exit(0);
+ } else if (pid < 0) {
+ /* FORK FAILED */
+ crondlog("\024cannot fork, user %s\n", user);
+ line->cl_Pid = 0;
+ if (mailf) {
+ remove(mailf);
+ }
+ } else if (mailf) {
+ /* PARENT, FORK SUCCESS
+ * rename mail-file based on pid of process
+ */
+ char mailFile2[128];
+
+ snprintf(mailFile2, sizeof(mailFile2), TMPDIR "/cron.%s.%d", user, pid);
+ rename(mailf, mailFile2);
+ }
+ /*
+ * Close the mail file descriptor.. we can't just leave it open in
+ * a structure, closing it later, because we might run out of descriptors
+ */
+
+ if (mailFd >= 0) {
+ close(mailFd);
+ }
+}
+
+static void RunJob(const char *user, CronLine * line)
+{
+ char mailFile[128];
+ int mailFd;
+
+ line->cl_Pid = 0;
+ line->cl_MailFlag = 0;
+
+ /* open mail file - owner root so nobody can screw with it. */
+
+ snprintf(mailFile, sizeof(mailFile), TMPDIR "/cron.%s.%d", user, getpid());
+ mailFd = open(mailFile, O_CREAT | O_TRUNC | O_WRONLY | O_EXCL | O_APPEND, 0600);
+
+ if (mailFd >= 0) {
+ line->cl_MailFlag = 1;
+ fdprintf(mailFd, "To: %s\nSubject: cron: %s\n\n", user,
+ line->cl_Shell);
+ line->cl_MailPos = lseek(mailFd, 0, SEEK_CUR);
+ } else {
+ crondlog("\024cannot create mail file user %s file %s, output to /dev/null\n", user, mailFile);
+ }
+
+ ForkJob(user, line, mailFd, DEFAULT_SHELL, "-c", line->cl_Shell, mailFile);
+}
+
+/*
+ * EndJob - called when job terminates and when mail terminates
+ */
+
+static void EndJob(const char *user, CronLine * line)
+{
+ int mailFd;
+ char mailFile[128];
+ struct stat sbuf;
+
+ /* No job */
+
+ if (line->cl_Pid <= 0) {
+ line->cl_Pid = 0;
+ return;
+ }
+
+ /*
+ * End of job and no mail file
+ * End of sendmail job
+ */
+
+ snprintf(mailFile, sizeof(mailFile), TMPDIR "/cron.%s.%d", user, line->cl_Pid);
+ line->cl_Pid = 0;
+
+ if (line->cl_MailFlag != 1) {
+ return;
+ }
+ line->cl_MailFlag = 0;
+
+ /*
+ * End of primary job - check for mail file. If size has increased and
+ * the file is still valid, we sendmail it.
+ */
+
+ mailFd = open(mailFile, O_RDONLY);
+ remove(mailFile);
+ if (mailFd < 0) {
+ return;
+ }
+
+ if (fstat(mailFd, &sbuf) < 0 || sbuf.st_uid != DaemonUid || sbuf.st_nlink != 0 ||
+ sbuf.st_size == line->cl_MailPos || !S_ISREG(sbuf.st_mode)) {
+ close(mailFd);
+ return;
+ }
+ ForkJob(user, line, mailFd, SENDMAIL, SENDMAIL_ARGS, NULL);
+}
+#else
+/* crond without sendmail */
+
+static void RunJob(const char *user, CronLine * line)
+{
+ /* Fork as the user in question and run program */
+ pid_t pid = fork();
+
+ if (pid == 0) {
+ /* CHILD */
+
+ /* Change running state to the user in question */
+
+ if (ChangeUser(user) < 0) {
+ exit(0);
+ }
+#if ENABLE_DEBUG_CROND_OPTION
+ if (DebugOpt) {
+ crondlog("\005Child Running %s\n", DEFAULT_SHELL);
+ }
+#endif
+
+ execl(DEFAULT_SHELL, DEFAULT_SHELL, "-c", line->cl_Shell, NULL);
+ crondlog("\024cannot exec, user %s cmd %s -c %s\n", user,
+ DEFAULT_SHELL, line->cl_Shell);
+ exit(0);
+ } else if (pid < 0) {
+ /* FORK FAILED */
+ crondlog("\024cannot, user %s\n", user);
+ pid = 0;
+ }
+ line->cl_Pid = pid;
+}
+#endif /* ENABLE_FEATURE_CROND_CALL_SENDMAIL */
diff --git a/i/pc104/initrd/conf/busybox/miscutils/crontab.c b/i/pc104/initrd/conf/busybox/miscutils/crontab.c
new file mode 100644
index 0000000..02df85b
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/miscutils/crontab.c
@@ -0,0 +1,343 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * CRONTAB
+ *
+ * usually setuid root, -c option only works if getuid() == geteuid()
+ *
+ * Copyright 1994 Matthew Dillon (dillon@apollo.west.oic.com)
+ * Vladimir Oleynik <dzo@simtreas.ru> (C) 2002
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ */
+
+#include "busybox.h"
+
+#ifndef CRONTABS
+#define CRONTABS "/var/spool/cron/crontabs"
+#endif
+#ifndef TMPDIR
+#define TMPDIR "/var/spool/cron"
+#endif
+#ifndef CRONUPDATE
+#define CRONUPDATE "cron.update"
+#endif
+#ifndef PATH_VI
+#define PATH_VI "/bin/vi" /* location of vi */
+#endif
+
+static const char *CDir = CRONTABS;
+
+static void EditFile(const char *user, const char *file);
+static int GetReplaceStream(const char *user, const char *file);
+static int ChangeUser(const char *user, short dochdir);
+
+int crontab_main(int ac, char **av);
+int crontab_main(int ac, char **av)
+{
+ enum { NONE, EDIT, LIST, REPLACE, DELETE } option = NONE;
+ const struct passwd *pas;
+ const char *repFile = NULL;
+ int repFd = 0;
+ int i;
+ char caller[256]; /* user that ran program */
+ char buf[1024];
+ int UserId;
+
+ UserId = getuid();
+ pas = getpwuid(UserId);
+ if (pas == NULL)
+ bb_perror_msg_and_die("getpwuid");
+
+ safe_strncpy(caller, pas->pw_name, sizeof(caller));
+
+ i = 1;
+ if (ac > 1) {
+ if (LONE_DASH(av[1])) {
+ option = REPLACE;
+ ++i;
+ } else if (av[1][0] != '-') {
+ option = REPLACE;
+ ++i;
+ repFile = av[1];
+ }
+ }
+
+ for (; i < ac; ++i) {
+ char *ptr = av[i];
+
+ if (*ptr != '-')
+ break;
+ ptr += 2;
+
+ switch (ptr[-1]) {
+ case 'l':
+ if (ptr[-1] == 'l')
+ option = LIST;
+ /* fall through */
+ case 'e':
+ if (ptr[-1] == 'e')
+ option = EDIT;
+ /* fall through */
+ case 'd':
+ if (ptr[-1] == 'd')
+ option = DELETE;
+ /* fall through */
+ case 'u':
+ if (i + 1 < ac && av[i+1][0] != '-') {
+ ++i;
+ if (getuid() == geteuid()) {
+ pas = getpwnam(av[i]);
+ if (pas) {
+ UserId = pas->pw_uid;
+ } else {
+ bb_error_msg_and_die("user %s unknown", av[i]);
+ }
+ } else {
+ bb_error_msg_and_die("only the superuser may specify a user");
+ }
+ }
+ break;
+ case 'c':
+ if (getuid() == geteuid()) {
+ CDir = (*ptr) ? ptr : av[++i];
+ } else {
+ bb_error_msg_and_die("-c option: superuser only");
+ }
+ break;
+ default:
+ i = ac;
+ break;
+ }
+ }
+ if (i != ac || option == NONE)
+ bb_show_usage();
+
+ /*
+ * Get password entry
+ */
+
+ pas = getpwuid(UserId);
+ if (pas == NULL)
+ bb_perror_msg_and_die("getpwuid");
+
+ /*
+ * If there is a replacement file, obtain a secure descriptor to it.
+ */
+
+ if (repFile) {
+ repFd = GetReplaceStream(caller, repFile);
+ if (repFd < 0)
+ bb_error_msg_and_die("cannot read replacement file");
+ }
+
+ /*
+ * Change directory to our crontab directory
+ */
+
+ xchdir(CDir);
+
+ /*
+ * Handle options as appropriate
+ */
+
+ switch (option) {
+ case LIST:
+ {
+ FILE *fi;
+
+ fi = fopen(pas->pw_name, "r");
+ if (fi) {
+ while (fgets(buf, sizeof(buf), fi) != NULL)
+ fputs(buf, stdout);
+ fclose(fi);
+ } else {
+ bb_error_msg("no crontab for %s", pas->pw_name);
+ }
+ }
+ break;
+ case EDIT:
+ {
+/* FIXME: messy code here! we have file copying helpers for this! */
+ FILE *fi;
+ int fd;
+ int n;
+ char tmp[128];
+
+ snprintf(tmp, sizeof(tmp), TMPDIR "/crontab.%d", getpid());
+ fd = xopen3(tmp, O_RDWR|O_CREAT|O_TRUNC|O_EXCL, 0600);
+/* race, use fchown */
+ chown(tmp, getuid(), getgid());
+ fi = fopen(pas->pw_name, "r");
+ if (fi) {
+ while ((n = fread(buf, 1, sizeof(buf), fi)) > 0)
+ full_write(fd, buf, n);
+ }
+ EditFile(caller, tmp);
+ remove(tmp);
+ lseek(fd, 0L, SEEK_SET);
+ repFd = fd;
+ }
+ option = REPLACE;
+ /* fall through */
+ case REPLACE:
+ {
+/* same here */
+ char path[1024];
+ int fd;
+ int n;
+
+ snprintf(path, sizeof(path), "%s.new", pas->pw_name);
+ fd = open(path, O_CREAT|O_TRUNC|O_APPEND|O_WRONLY, 0600);
+ if (fd >= 0) {
+ while ((n = read(repFd, buf, sizeof(buf))) > 0) {
+ full_write(fd, buf, n);
+ }
+ close(fd);
+ rename(path, pas->pw_name);
+ } else {
+ bb_error_msg("cannot create %s/%s", CDir, path);
+ }
+ close(repFd);
+ }
+ break;
+ case DELETE:
+ remove(pas->pw_name);
+ break;
+ case NONE:
+ default:
+ break;
+ }
+
+ /*
+ * Bump notification file. Handle window where crond picks file up
+ * before we can write our entry out.
+ */
+
+ if (option == REPLACE || option == DELETE) {
+ FILE *fo;
+ struct stat st;
+
+ while ((fo = fopen(CRONUPDATE, "a"))) {
+ fprintf(fo, "%s\n", pas->pw_name);
+ fflush(fo);
+ if (fstat(fileno(fo), &st) != 0 || st.st_nlink != 0) {
+ fclose(fo);
+ break;
+ }
+ fclose(fo);
+ /* loop */
+ }
+ if (fo == NULL) {
+ bb_error_msg("cannot append to %s/%s", CDir, CRONUPDATE);
+ }
+ }
+ return 0;
+}
+
+static int GetReplaceStream(const char *user, const char *file)
+{
+ int filedes[2];
+ int pid;
+ int fd;
+ int n;
+ char buf[1024];
+
+ if (pipe(filedes) < 0) {
+ perror("pipe");
+ return -1;
+ }
+ pid = fork();
+ if (pid < 0) {
+ perror("fork");
+ return -1;
+ }
+ if (pid > 0) {
+ /*
+ * PARENT
+ */
+
+ close(filedes[1]);
+ if (read(filedes[0], buf, 1) != 1) {
+ close(filedes[0]);
+ filedes[0] = -1;
+ }
+ return filedes[0];
+ }
+
+ /*
+ * CHILD
+ */
+
+ close(filedes[0]);
+
+ if (ChangeUser(user, 0) < 0)
+ exit(0);
+
+ xfunc_error_retval = 0;
+ fd = xopen(file, O_RDONLY);
+ buf[0] = 0;
+ write(filedes[1], buf, 1);
+ while ((n = read(fd, buf, sizeof(buf))) > 0) {
+ write(filedes[1], buf, n);
+ }
+ exit(0);
+}
+
+static void EditFile(const char *user, const char *file)
+{
+ int pid = fork();
+
+ if (pid == 0) {
+ /*
+ * CHILD - change user and run editor
+ */
+ const char *ptr;
+
+ if (ChangeUser(user, 1) < 0)
+ exit(0);
+ ptr = getenv("VISUAL");
+ if (ptr == NULL || strlen(ptr) > 256)
+ ptr = PATH_VI;
+
+ ptr = xasprintf("%s %s", ptr, file);
+ execl(DEFAULT_SHELL, DEFAULT_SHELL, "-c", ptr, NULL);
+ bb_perror_msg_and_die("exec");
+ }
+ if (pid < 0) {
+ /*
+ * PARENT - failure
+ */
+ bb_perror_msg_and_die("fork");
+ }
+ wait4(pid, NULL, 0, NULL);
+}
+
+static int ChangeUser(const char *user, short dochdir)
+{
+ struct passwd *pas;
+
+ /*
+ * Obtain password entry and change privileges
+ */
+
+ pas = getpwnam(user);
+ if (pas == NULL) {
+ bb_perror_msg_and_die("failed to get uid for %s", user);
+ }
+ setenv("USER", pas->pw_name, 1);
+ setenv("HOME", pas->pw_dir, 1);
+ setenv("SHELL", DEFAULT_SHELL, 1);
+
+ /*
+ * Change running state to the user in question
+ */
+ change_identity(pas);
+
+ if (dochdir) {
+ if (chdir(pas->pw_dir) < 0) {
+ bb_perror_msg("chdir(%s) by %s failed", pas->pw_dir, user);
+ xchdir(TMPDIR);
+ }
+ }
+ return pas->pw_uid;
+}
diff --git a/i/pc104/initrd/conf/busybox/miscutils/dc.c b/i/pc104/initrd/conf/busybox/miscutils/dc.c
new file mode 100644
index 0000000..2121f76
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/miscutils/dc.c
@@ -0,0 +1,232 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "busybox.h"
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <math.h>
+
+/* Tiny RPN calculator, because "expr" didn't give me bitwise operations. */
+
+static double stack[100];
+static unsigned int pointer;
+static unsigned char base;
+
+static void push(double a)
+{
+ if (pointer >= (sizeof(stack) / sizeof(*stack)))
+ bb_error_msg_and_die("stack overflow");
+ stack[pointer++] = a;
+}
+
+static double pop(void)
+{
+ if (pointer == 0)
+ bb_error_msg_and_die("stack underflow");
+ return stack[--pointer];
+}
+
+static void add(void)
+{
+ push(pop() + pop());
+}
+
+static void sub(void)
+{
+ double subtrahend = pop();
+
+ push(pop() - subtrahend);
+}
+
+static void mul(void)
+{
+ push(pop() * pop());
+}
+
+static void power(void)
+{
+ double topower = pop();
+
+ push(pow(pop(), topower));
+}
+
+static void divide(void)
+{
+ double divisor = pop();
+
+ push(pop() / divisor);
+}
+
+static void mod(void)
+{
+ unsigned int d = pop();
+
+ push((unsigned int) pop() % d);
+}
+
+static void and(void)
+{
+ push((unsigned int) pop() & (unsigned int) pop());
+}
+
+static void or(void)
+{
+ push((unsigned int) pop() | (unsigned int) pop());
+}
+
+static void eor(void)
+{
+ push((unsigned int) pop() ^ (unsigned int) pop());
+}
+
+static void not(void)
+{
+ push(~(unsigned int) pop());
+}
+
+static void set_output_base(void)
+{
+ base=(unsigned char)pop();
+ if ((base != 10) && (base != 16)) {
+ fprintf(stderr, "Error: base = %d is not supported.\n", base);
+ base=10;
+ }
+}
+
+static void print_base(double print)
+{
+ if (base == 16)
+ printf("%x\n", (unsigned int)print);
+ else
+ printf("%g\n", print);
+}
+
+static void print_stack_no_pop(void)
+{
+ unsigned int i=pointer;
+ while (i)
+ print_base(stack[--i]);
+}
+
+static void print_no_pop(void)
+{
+ print_base(stack[pointer-1]);
+}
+
+struct op {
+ const char *name;
+ void (*function) (void);
+};
+
+static const struct op operators[] = {
+ {"+", add},
+ {"add", add},
+ {"-", sub},
+ {"sub", sub},
+ {"*", mul},
+ {"mul", mul},
+ {"/", divide},
+ {"div", divide},
+ {"**", power},
+ {"exp", power},
+ {"pow", power},
+ {"%", mod},
+ {"mod", mod},
+ {"and", and},
+ {"or", or},
+ {"not", not},
+ {"eor", eor},
+ {"xor", eor},
+ {"p", print_no_pop},
+ {"f", print_stack_no_pop},
+ {"o", set_output_base},
+ {0, 0}
+};
+
+static void stack_machine(const char *argument)
+{
+ char *endPointer = 0;
+ double d;
+ const struct op *o = operators;
+
+ if (argument == 0)
+ return;
+
+ d = strtod(argument, &endPointer);
+
+ if (endPointer != argument) {
+ push(d);
+ return;
+ }
+
+ while (o->name != 0) {
+ if (strcmp(o->name, argument) == 0) {
+ (*(o->function)) ();
+ return;
+ }
+ o++;
+ }
+ bb_error_msg_and_die("%s: syntax error", argument);
+}
+
+/* return pointer to next token in buffer and set *buffer to one char
+ * past the end of the above mentioned token
+ */
+static char *get_token(char **buffer)
+{
+ char *start = NULL;
+ char *current;
+
+ current = skip_whitespace(*buffer);
+ if (*current != 0) {
+ start = current;
+ while (!isspace(*current) && *current != 0) { current++; }
+ *buffer = current;
+ }
+ return start;
+}
+
+/* In Perl one might say, scalar m|\s*(\S+)\s*|g */
+static int number_of_tokens(char *buffer)
+{
+ int i = 0;
+ char *b = buffer;
+ while (get_token(&b)) { i++; }
+ return i;
+}
+
+int dc_main(int argc, char **argv);
+int dc_main(int argc, char **argv)
+{
+ /* take stuff from stdin if no args are given */
+ if (argc <= 1) {
+ int i, len;
+ char *line = NULL;
+ char *cursor = NULL;
+ char *token = NULL;
+ while ((line = xmalloc_getline(stdin))) {
+ cursor = line;
+ len = number_of_tokens(line);
+ for (i = 0; i < len; i++) {
+ token = get_token(&cursor);
+ *cursor++ = 0;
+ stack_machine(token);
+ }
+ free(line);
+ }
+ } else {
+ if (*argv[1]=='-')
+ bb_show_usage();
+ while (argc >= 2) {
+ stack_machine(argv[1]);
+ argv++;
+ argc--;
+ }
+ }
+ return EXIT_SUCCESS;
+}
diff --git a/i/pc104/initrd/conf/busybox/miscutils/devfsd.c b/i/pc104/initrd/conf/busybox/miscutils/devfsd.c
new file mode 100644
index 0000000..0069b28
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/miscutils/devfsd.c
@@ -0,0 +1,2030 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/*
+ devfsd implementation for busybox
+
+ Copyright (C) 2003 by Tito Ragusa <farmatito@tiscali.it>
+
+ Busybox version is based on some previous work and ideas
+ Copyright (C) [2003] by [Matteo Croce] <3297627799@wind.it>
+
+ devfsd.c
+
+ Main file for devfsd (devfs daemon for Linux).
+
+ Copyright (C) 1998-2002 Richard Gooch
+
+ devfsd.h
+
+ Header file for devfsd (devfs daemon for Linux).
+
+ Copyright (C) 1998-2000 Richard Gooch
+
+ compat_name.c
+
+ Compatibility name file for devfsd (build compatibility names).
+
+ Copyright (C) 1998-2002 Richard Gooch
+
+ expression.c
+
+ This code provides Borne Shell-like expression expansion.
+
+ Copyright (C) 1997-1999 Richard Gooch
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ Richard Gooch may be reached by email at rgooch@atnf.csiro.au
+ The postal address is:
+ Richard Gooch, c/o ATNF, P. O. Box 76, Epping, N.S.W., 2121, Australia.
+*/
+
+#include "busybox.h"
+#include "xregex.h"
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/sysmacros.h>
+
+
+/* Various defines taken from linux/major.h */
+#define IDE0_MAJOR 3
+#define IDE1_MAJOR 22
+#define IDE2_MAJOR 33
+#define IDE3_MAJOR 34
+#define IDE4_MAJOR 56
+#define IDE5_MAJOR 57
+#define IDE6_MAJOR 88
+#define IDE7_MAJOR 89
+#define IDE8_MAJOR 90
+#define IDE9_MAJOR 91
+
+
+/* Various defines taken from linux/devfs_fs.h */
+#define DEVFSD_PROTOCOL_REVISION_KERNEL 5
+#define DEVFSD_IOCTL_BASE 'd'
+/* These are the various ioctls */
+#define DEVFSDIOC_GET_PROTO_REV _IOR(DEVFSD_IOCTL_BASE, 0, int)
+#define DEVFSDIOC_SET_EVENT_MASK _IOW(DEVFSD_IOCTL_BASE, 2, int)
+#define DEVFSDIOC_RELEASE_EVENT_QUEUE _IOW(DEVFSD_IOCTL_BASE, 3, int)
+#define DEVFSDIOC_SET_CONFIG_DEBUG_MASK _IOW(DEVFSD_IOCTL_BASE, 4, int)
+#define DEVFSD_NOTIFY_REGISTERED 0
+#define DEVFSD_NOTIFY_UNREGISTERED 1
+#define DEVFSD_NOTIFY_ASYNC_OPEN 2
+#define DEVFSD_NOTIFY_CLOSE 3
+#define DEVFSD_NOTIFY_LOOKUP 4
+#define DEVFSD_NOTIFY_CHANGE 5
+#define DEVFSD_NOTIFY_CREATE 6
+#define DEVFSD_NOTIFY_DELETE 7
+#define DEVFS_PATHLEN 1024
+/* Never change this otherwise the binary interface will change */
+
+struct devfsd_notify_struct
+{ /* Use native C types to ensure same types in kernel and user space */
+ unsigned int type; /* DEVFSD_NOTIFY_* value */
+ unsigned int mode; /* Mode of the inode or device entry */
+ unsigned int major; /* Major number of device entry */
+ unsigned int minor; /* Minor number of device entry */
+ unsigned int uid; /* Uid of process, inode or device entry */
+ unsigned int gid; /* Gid of process, inode or device entry */
+ unsigned int overrun_count; /* Number of lost events */
+ unsigned int namelen; /* Number of characters not including '\0' */
+ /* The device name MUST come last */
+ char devname[DEVFS_PATHLEN]; /* This will be '\0' terminated */
+};
+
+#define BUFFER_SIZE 16384
+#define DEVFSD_VERSION "1.3.25"
+#define CONFIG_FILE "/etc/devfsd.conf"
+#define MODPROBE "/sbin/modprobe"
+#define MODPROBE_SWITCH_1 "-k"
+#define MODPROBE_SWITCH_2 "-C"
+#define CONFIG_MODULES_DEVFS "/etc/modules.devfs"
+#define MAX_ARGS (6 + 1)
+#define MAX_SUBEXPR 10
+#define STRING_LENGTH 255
+
+/* for get_uid_gid() */
+#define UID 0
+#define GID 1
+
+/* fork_and_execute() */
+# define DIE 1
+# define NO_DIE 0
+
+/* for dir_operation() */
+#define RESTORE 0
+#define SERVICE 1
+#define READ_CONFIG 2
+
+/* Update only after changing code to reflect new protocol */
+#define DEVFSD_PROTOCOL_REVISION_DAEMON 5
+
+/* Compile-time check */
+#if DEVFSD_PROTOCOL_REVISION_KERNEL != DEVFSD_PROTOCOL_REVISION_DAEMON
+#error protocol version mismatch. Update your kernel headers
+#endif
+
+#define AC_PERMISSIONS 0
+#define AC_MODLOAD 1
+#define AC_EXECUTE 2
+#define AC_MFUNCTION 3 /* not supported by busybox */
+#define AC_CFUNCTION 4 /* not supported by busybox */
+#define AC_COPY 5
+#define AC_IGNORE 6
+#define AC_MKOLDCOMPAT 7
+#define AC_MKNEWCOMPAT 8
+#define AC_RMOLDCOMPAT 9
+#define AC_RMNEWCOMPAT 10
+#define AC_RESTORE 11
+
+struct permissions_type
+{
+ mode_t mode;
+ uid_t uid;
+ gid_t gid;
+};
+
+struct execute_type
+{
+ char *argv[MAX_ARGS + 1]; /* argv[0] must always be the programme */
+};
+
+struct copy_type
+{
+ const char *source;
+ const char *destination;
+};
+
+struct action_type
+{
+ unsigned int what;
+ unsigned int when;
+};
+
+struct config_entry_struct
+{
+ struct action_type action;
+ regex_t preg;
+ union
+ {
+ struct permissions_type permissions;
+ struct execute_type execute;
+ struct copy_type copy;
+ }
+ u;
+ struct config_entry_struct *next;
+};
+
+struct get_variable_info
+{
+ const struct devfsd_notify_struct *info;
+ const char *devname;
+ char devpath[STRING_LENGTH];
+};
+
+static void dir_operation(int , const char * , int, unsigned long* );
+static void service(struct stat statbuf, char *path);
+static int st_expr_expand(char *, unsigned, const char *, const char *(*) (const char *, void *), void *);
+static const char *get_old_name(const char *, unsigned, char *, unsigned, unsigned);
+static int mksymlink (const char *oldpath, const char *newpath);
+static void read_config_file (char *path, int optional, unsigned long *event_mask);
+static void process_config_line (const char *, unsigned long *);
+static int do_servicing (int, unsigned long);
+static void service_name (const struct devfsd_notify_struct *);
+static void action_permissions (const struct devfsd_notify_struct *, const struct config_entry_struct *);
+static void action_execute (const struct devfsd_notify_struct *, const struct config_entry_struct *,
+ const regmatch_t *, unsigned);
+static void action_modload (const struct devfsd_notify_struct *info, const struct config_entry_struct *entry);
+static void action_copy (const struct devfsd_notify_struct *, const struct config_entry_struct *,
+ const regmatch_t *, unsigned);
+static void action_compat (const struct devfsd_notify_struct *, unsigned);
+static void free_config (void);
+static void restore(char *spath, struct stat source_stat, int rootlen);
+static int copy_inode (const char *, const struct stat *, mode_t, const char *, const struct stat *);
+static mode_t get_mode (const char *);
+static void signal_handler (int);
+static const char *get_variable (const char *, void *);
+static int make_dir_tree (const char *);
+static int expand_expression(char *, unsigned, const char *, const char *(*)(const char *, void *), void *,
+ const char *, const regmatch_t *, unsigned );
+static void expand_regexp (char *, size_t, const char *, const char *, const regmatch_t *, unsigned );
+static const char *expand_variable( char *, unsigned, unsigned *, const char *,
+ const char *(*) (const char *, void *), void * );
+static const char *get_variable_v2(const char *, const char *(*) (const char *, void *), void *);
+static char get_old_ide_name (unsigned , unsigned);
+static char *write_old_sd_name (char *, unsigned, unsigned, const char *);
+
+/* busybox functions */
+static void msg_logger(int pri, const char * fmt, ... )__attribute__ ((format (printf, 2, 3)));
+static void msg_logger_and_die(int pri, const char * fmt, ... )__attribute__ ((noreturn, format (printf, 2, 3)));
+static void do_ioctl_and_die(int fd, int request, unsigned long event_mask_flag);
+static void fork_and_execute(int die, char *arg0, char **arg );
+static int get_uid_gid ( int, const char *);
+static void safe_memcpy( char * dest, const char * src, int len);
+static unsigned int scan_dev_name_common(const char *d, unsigned int n, int addendum, char *ptr);
+static unsigned int scan_dev_name(const char *d, unsigned int n, char *ptr);
+
+/* Structs and vars */
+static struct config_entry_struct *first_config = NULL;
+static struct config_entry_struct *last_config = NULL;
+static const char *mount_point = NULL;
+static volatile int caught_signal = FALSE;
+static volatile int caught_sighup = FALSE;
+static struct initial_symlink_struct
+{
+ const char *dest;
+ const char *name;
+} initial_symlinks[] =
+{
+ {"/proc/self/fd", "fd"},
+ {"fd/0", "stdin"},
+ {"fd/1", "stdout"},
+ {"fd/2", "stderr"},
+ {NULL, NULL},
+};
+
+static struct event_type
+{
+ unsigned int type; /* The DEVFSD_NOTIFY_* value */
+ const char *config_name; /* The name used in the config file */
+} event_types[] =
+{
+ {DEVFSD_NOTIFY_REGISTERED, "REGISTER"},
+ {DEVFSD_NOTIFY_UNREGISTERED, "UNREGISTER"},
+ {DEVFSD_NOTIFY_ASYNC_OPEN, "ASYNC_OPEN"},
+ {DEVFSD_NOTIFY_CLOSE, "CLOSE"},
+ {DEVFSD_NOTIFY_LOOKUP, "LOOKUP"},
+ {DEVFSD_NOTIFY_CHANGE, "CHANGE"},
+ {DEVFSD_NOTIFY_CREATE, "CREATE"},
+ {DEVFSD_NOTIFY_DELETE, "DELETE"},
+ {0xffffffff, NULL}
+};
+
+/* Busybox messages */
+
+static const char * const bb_msg_proto_rev = "protocol revision";
+static const char * const bb_msg_bad_config = "bad %s config file: %s";
+static const char * const bb_msg_small_buffer = "buffer too small";
+static const char * const bb_msg_variable_not_found = "variable: %s not found";
+
+/* Busybox functions */
+static void msg_logger(int pri, const char * fmt, ... )
+{
+ va_list ap;
+ int ret;
+
+ va_start(ap, fmt);
+ ret = access ("/dev/log", F_OK);
+ if (ret == 0) {
+ openlog(applet_name, 0, LOG_DAEMON);
+ vsyslog( pri , fmt, ap);
+ /* Man: A trailing newline is added when needed. */
+ closelog();
+ }
+ /* ENABLE_DEVFSD_VERBOSE is always enabled if msg_logger is used */
+ if ((ENABLE_DEVFSD_VERBOSE && ret) || ENABLE_DEBUG) {
+ bb_error_msg(fmt, ap);
+ }
+ va_end(ap);
+}
+
+static void msg_logger_and_die(int pri, const char* fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ msg_logger(pri, fmt, ap);
+ va_end(ap);
+ exit(EXIT_FAILURE);
+}
+
+/* Busybox stuff */
+#if defined(CONFIG_DEVFSD_VERBOSE) || defined(CONFIG_DEBUG)
+#define devfsd_error_msg(fmt, args...) bb_error_msg(fmt, ## args)
+#define devfsd_perror_msg_and_die(fmt, args...) bb_perror_msg_and_die(fmt, ## args)
+#define devfsd_error_msg_and_die(fmt, args...) bb_error_msg_and_die(fmt, ## args)
+#if defined(CONFIG_DEBUG)
+#define debug_msg_logger(x, fmt, args...) msg_logger(x, fmt, ## args)
+#else
+#define debug_msg_logger(x, fmt, args...)
+#endif
+#else
+#define debug_msg_logger(x, fmt, args...)
+#define msg_logger(p, fmt, args...)
+#define msg_logger_and_die(p, fmt, args...) exit(1)
+#define devfsd_perror_msg_and_die(fmt, args...) exit(1)
+#define devfsd_error_msg_and_die(fmt, args...) exit(1)
+#define devfsd_error_msg(fmt, args...)
+#endif
+
+static void do_ioctl_and_die(int fd, int request, unsigned long event_mask_flag)
+{
+ if (ioctl (fd, request, event_mask_flag) == -1)
+ msg_logger_and_die(LOG_ERR, "ioctl");
+}
+
+static void fork_and_execute(int die, char *arg0, char **arg )
+{
+ switch ( fork () )
+ {
+ case 0:
+ /* Child */
+ break;
+ case -1:
+ /* Parent: Error : die or return */
+ msg_logger(LOG_ERR,(char *) bb_msg_memory_exhausted);
+ if(die)
+ exit(EXIT_FAILURE);
+ return;
+ default:
+ /* Parent : ok : return or exit */
+ if(arg0 != NULL)
+ {
+ wait (NULL);
+ return;
+ }
+ exit (EXIT_SUCCESS);
+ }
+ /* Child : if arg0 != NULL do execvp */
+ if(arg0 != NULL )
+ {
+ BB_EXECVP(arg0, arg);
+ msg_logger_and_die(LOG_ERR, "execvp");
+ }
+}
+
+static void safe_memcpy( char *dest, const char *src, int len)
+{
+ memcpy (dest , src , len );
+ dest[len] = '\0';
+}
+
+static unsigned int scan_dev_name_common(const char *d, unsigned int n, int addendum, char *ptr)
+{
+ if(d[n - 4]=='d' && d[n - 3]=='i' && d[n - 2]=='s' && d[n - 1]=='c')
+ return 2 + addendum;
+ if(d[n - 2]=='c' && d[n - 1]=='d')
+ return 3 + addendum;
+ if(ptr[0]=='p' && ptr[1]=='a' && ptr[2]=='r' && ptr[3]=='t')
+ return 4 + addendum;
+ if(ptr[n - 2]=='m' && ptr[n - 1]=='t')
+ return 5 + addendum;
+ return 0;
+}
+
+static unsigned int scan_dev_name(const char *d, unsigned int n, char *ptr)
+{
+ if(d[0]=='s' && d[1]=='c' && d[2]=='s' && d[3]=='i' && d[4]=='/') {
+ if( d[n - 7]=='g' && d[n - 6]=='e' && d[n - 5]=='n' &&
+ d[n - 4]=='e' && d[n - 3]=='r' && d[n - 2]=='i' &&
+ d[n - 1]=='c' )
+ return 1;
+ return scan_dev_name_common(d, n, 0, ptr);
+ }
+ if(d[0]=='i' && d[1]=='d' && d[2]=='e' && d[3]=='/' &&
+ d[4]=='h' && d[5]=='o' && d[6]=='s' && d[7]=='t')
+ return scan_dev_name_common(d, n, 4, ptr);
+ if(d[0]=='s' && d[1]=='b' && d[2]=='p' && d[3]=='/')
+ return 10;
+ if(d[0]=='v' && d[1]=='c' && d[2]=='c' && d[3]=='/')
+ return 11;
+ if(d[0]=='p' && d[1]=='t' && d[2]=='y' && d[3]=='/')
+ return 12;
+ return 0;
+}
+
+/* Public functions follow */
+
+int devfsd_main (int argc, char **argv);
+int devfsd_main (int argc, char **argv)
+{
+ int print_version = FALSE;
+ int do_daemon = TRUE;
+ int no_polling = FALSE;
+ int do_scan;
+ int fd, proto_rev, count;
+ unsigned long event_mask = 0;
+ struct sigaction new_action;
+ struct initial_symlink_struct *curr;
+
+ if (argc < 2)
+ bb_show_usage();
+
+ for (count = 2; count < argc; ++count)
+ {
+ if(argv[count][0] == '-')
+ {
+ if(argv[count][1]=='v' && !argv[count][2]) /* -v */
+ print_version = TRUE;
+ else if(ENABLE_DEVFSD_FG_NP && argv[count][1]=='f'
+ && argv[count][2]=='g' && !argv[count][3]) /* -fg */
+ do_daemon = FALSE;
+ else if(ENABLE_DEVFSD_FG_NP && argv[count][1]=='n'
+ && argv[count][2]=='p' && !argv[count][3]) /* -np */
+ no_polling = TRUE;
+ else
+ bb_show_usage();
+ }
+ }
+
+ /* strip last / from mount point, so we don't need to check for it later */
+ while (argv[1][1]!='\0' && argv[1][strlen(argv[1])-1] == '/' )
+ argv[1][strlen(argv[1]) -1] = '\0';
+
+ mount_point = argv[1];
+
+ if (chdir (mount_point) != 0)
+ devfsd_perror_msg_and_die(mount_point);
+
+ fd = xopen (".devfsd", O_RDONLY);
+
+ if (fcntl (fd, F_SETFD, FD_CLOEXEC) != 0)
+ devfsd_perror_msg_and_die("FD_CLOEXEC");
+
+ if (ioctl (fd, DEVFSDIOC_GET_PROTO_REV, &proto_rev) == -1)
+ msg_logger_and_die(LOG_ERR, "ioctl");
+
+ /*setup initial entries */
+ for (curr = initial_symlinks; curr->dest != NULL; ++curr)
+ symlink (curr->dest, curr->name);
+
+ /* NB: The check for CONFIG_FILE is done in read_config_file() */
+
+ if (print_version || (DEVFSD_PROTOCOL_REVISION_DAEMON != proto_rev)) {
+ printf("%s v%s\nDaemon %s:\t%d\nKernel-side %s:\t%d\n",
+ applet_name,DEVFSD_VERSION,bb_msg_proto_rev,
+ DEVFSD_PROTOCOL_REVISION_DAEMON,bb_msg_proto_rev, proto_rev);
+ if (DEVFSD_PROTOCOL_REVISION_DAEMON != proto_rev)
+ bb_error_msg_and_die( "%s mismatch!",bb_msg_proto_rev);
+ exit(EXIT_SUCCESS); /* -v */
+ }
+ /* Tell kernel we are special (i.e. we get to see hidden entries) */
+ do_ioctl_and_die(fd, DEVFSDIOC_SET_EVENT_MASK, 0);
+
+ sigemptyset(&new_action.sa_mask);
+ new_action.sa_flags = 0;
+
+ /* Set up SIGHUP and SIGUSR1 handlers */
+ new_action.sa_handler = signal_handler;
+ if (sigaction(SIGHUP, &new_action, NULL) != 0 || sigaction (SIGUSR1, &new_action, NULL) != 0 )
+ devfsd_error_msg_and_die( "sigaction");
+
+ printf("%s v%s started for %s\n",applet_name, DEVFSD_VERSION, mount_point);
+
+ /* Set umask so that mknod(2), open(2) and mkdir(2) have complete control over permissions */
+ umask(0);
+ read_config_file((char*)CONFIG_FILE, FALSE, &event_mask);
+ /* Do the scan before forking, so that boot scripts see the finished product */
+ dir_operation(SERVICE,mount_point,0,NULL);
+
+ if (ENABLE_DEVFSD_FG_NP && no_polling)
+ exit (0);
+ if (do_daemon)
+ {
+ /* Release so that the child can grab it */
+ do_ioctl_and_die(fd, DEVFSDIOC_RELEASE_EVENT_QUEUE, 0);
+ fork_and_execute(DIE, NULL, NULL);
+ setsid (); /* Prevent hangups and become pgrp leader */
+ } else if(ENABLE_DEVFSD_FG_NP) {
+ setpgid (0, 0); /* Become process group leader */
+ }
+
+ while (TRUE)
+ {
+ do_scan = do_servicing (fd, event_mask);
+
+ free_config ();
+ read_config_file ((char*)CONFIG_FILE, FALSE, &event_mask);
+ if (do_scan)
+ dir_operation(SERVICE,mount_point,0,NULL);
+ }
+} /* End Function main */
+
+
+/* Private functions follow */
+
+static void read_config_file (char *path, int optional, unsigned long *event_mask)
+/* [SUMMARY] Read a configuration database.
+ <path> The path to read the database from. If this is a directory, all
+ entries in that directory will be read (except hidden entries).
+ <optional> If TRUE, the routine will silently ignore a missing config file.
+ <event_mask> The event mask is written here. This is not initialised.
+ [RETURNS] Nothing.
+*/
+{
+ struct stat statbuf;
+ FILE *fp;
+ char buf[STRING_LENGTH];
+ char *line = NULL;
+
+ debug_msg_logger(LOG_INFO, "%s: %s", __FUNCTION__, path);
+
+ if (stat (path, &statbuf) == 0 )
+ {
+ /* Don't read 0 length files: ignored */
+ /*if( statbuf.st_size == 0 )
+ return;*/
+ if ( S_ISDIR (statbuf.st_mode) )
+ {
+ /* strip last / from dirname so we don't need to check for it later */
+ while (path && path[1]!='\0' && path[strlen(path)-1] == '/')
+ path[strlen(path) -1] = '\0';
+
+ dir_operation(READ_CONFIG, path, 0, event_mask);
+ return;
+ }
+ if ( ( fp = fopen (path, "r") ) != NULL )
+ {
+ while (fgets (buf, STRING_LENGTH, fp) != NULL)
+ {
+ /* Skip whitespace */
+ for (line = buf; isspace (*line); ++line)
+ /*VOID*/;
+ if (line[0] == '\0' || line[0] == '#' )
+ continue;
+ process_config_line (line, event_mask);
+ }
+ fclose (fp);
+ } else {
+ goto read_config_file_err;
+ }
+ } else {
+read_config_file_err:
+ if(optional == 0 && errno == ENOENT)
+ msg_logger_and_die(LOG_ERR, "read config file: %s: %m", path);
+ }
+} /* End Function read_config_file */
+
+static void process_config_line (const char *line, unsigned long *event_mask)
+/* [SUMMARY] Process a line from a configuration file.
+ <line> The configuration line.
+ <event_mask> The event mask is written here. This is not initialised.
+ [RETURNS] Nothing.
+*/
+{
+ int num_args, count;
+ struct config_entry_struct *new;
+ char p[MAX_ARGS][STRING_LENGTH];
+ char when[STRING_LENGTH], what[STRING_LENGTH];
+ char name[STRING_LENGTH];
+ const char *msg = "";
+ char *ptr;
+ int i;
+
+ /* !!!! Only Uppercase Keywords in devsfd.conf */
+ static const char *const options[] = {
+ "CLEAR_CONFIG", "INCLUDE", "OPTIONAL_INCLUDE",
+ "RESTORE", "PERMISSIONS", "MODLOAD", "EXECUTE",
+ "COPY", "IGNORE", "MKOLDCOMPAT", "MKNEWCOMPAT",
+ "RMOLDCOMPAT", "RMNEWCOMPAT", 0
+ };
+
+ debug_msg_logger(LOG_INFO, __FUNCTION__);
+
+ for (count = 0; count < MAX_ARGS; ++count) p[count][0] = '\0';
+ num_args = sscanf (line, "%s %s %s %s %s %s %s %s %s %s",
+ when, name, what,
+ p[0], p[1], p[2], p[3], p[4], p[5], p[6]);
+
+ i = index_in_str_array(options, when );
+
+ /*"CLEAR_CONFIG"*/
+ if( i == 0)
+ {
+ free_config ();
+ *event_mask = 0;
+ return;
+ }
+
+ if ( num_args < 2)
+ goto process_config_line_err;
+
+ /* "INCLUDE" & "OPTIONAL_INCLUDE" */
+ if( i == 1 || i == 2 )
+ {
+ st_expr_expand (name, STRING_LENGTH, name, get_variable, NULL );
+ msg_logger(LOG_INFO, "%sinclude: %s",(toupper (when[0]) == 'I') ? "": "optional_", name);
+ read_config_file (name, (toupper (when[0]) == 'I') ? FALSE : TRUE, event_mask);
+ return;
+ }
+ /* "RESTORE" */
+ if( i == 3)
+ {
+ dir_operation(RESTORE,name, strlen (name),NULL);
+ return;
+ }
+ if (num_args < 3)
+ goto process_config_line_err;
+
+ new = xmalloc (sizeof *new);
+ memset (new, 0, sizeof *new);
+
+ for (count = 0; event_types[count].config_name != NULL; ++count)
+ {
+ if (strcasecmp (when, event_types[count].config_name) != 0)
+ continue;
+ new->action.when = event_types[count].type;
+ break;
+ }
+ if (event_types[count].config_name == NULL)
+ {
+ msg="WHEN in";
+ goto process_config_line_err;
+ }
+
+ i = index_in_str_array(options, what );
+
+ switch (i)
+ {
+ case 4: /* "PERMISSIONS" */
+ new->action.what = AC_PERMISSIONS;
+ /* Get user and group */
+ if ( ( ptr = strchr (p[0], '.') ) == NULL )
+ {
+ msg="UID.GID";
+ goto process_config_line_err; /*"missing '.' in UID.GID"*/
+ }
+
+ *ptr++ = '\0';
+ new->u.permissions.uid = get_uid_gid (UID, p[0]);
+ new->u.permissions.gid = get_uid_gid (GID, ptr);
+ /* Get mode */
+ new->u.permissions.mode = get_mode (p[1]);
+ break;
+ case 5: /* MODLOAD */
+ /*This action will pass "/dev/$devname" (i.e. "/dev/" prefixed to
+ the device name) to the module loading facility. In addition,
+ the /etc/modules.devfs configuration file is used.*/
+ if (ENABLE_DEVFSD_MODLOAD)
+ new->action.what = AC_MODLOAD;
+ break;
+ case 6: /* EXECUTE */
+ new->action.what = AC_EXECUTE;
+ num_args -= 3;
+
+ for (count = 0; count < num_args; ++count)
+ new->u.execute.argv[count] = xstrdup (p[count]);
+
+ new->u.execute.argv[num_args] = NULL;
+ break;
+ case 7: /* COPY */
+ new->action.what = AC_COPY;
+ num_args -= 3;
+ if (num_args != 2)
+ goto process_config_line_err; /* missing path and function in line */
+
+ new->u.copy.source = xstrdup (p[0]);
+ new->u.copy.destination = xstrdup (p[1]);
+ break;
+ case 8: /* IGNORE */
+ /* FALLTROUGH */
+ case 9: /* MKOLDCOMPAT */
+ /* FALLTROUGH */
+ case 10: /* MKNEWCOMPAT */
+ /* FALLTROUGH */
+ case 11:/* RMOLDCOMPAT */
+ /* FALLTROUGH */
+ case 12: /* RMNEWCOMPAT */
+ /* AC_IGNORE 6
+ AC_MKOLDCOMPAT 7
+ AC_MKNEWCOMPAT 8
+ AC_RMOLDCOMPAT 9
+ AC_RMNEWCOMPAT 10*/
+ new->action.what = i - 2;
+ break;
+ default:
+ msg ="WHAT in";
+ goto process_config_line_err;
+ /*esac*/
+ } /* switch (i) */
+
+ xregcomp( &new->preg, name, REG_EXTENDED);
+
+ *event_mask |= 1 << new->action.when;
+ new->next = NULL;
+ if (first_config == NULL)
+ first_config = new;
+ else
+ last_config->next = new;
+ last_config = new;
+ return;
+process_config_line_err:
+ msg_logger_and_die(LOG_ERR, bb_msg_bad_config, msg , line);
+} /* End Function process_config_line */
+
+static int do_servicing (int fd, unsigned long event_mask)
+/* [SUMMARY] Service devfs changes until a signal is received.
+ <fd> The open control file.
+ <event_mask> The event mask.
+ [RETURNS] TRUE if SIGHUP was caught, else FALSE.
+*/
+{
+ ssize_t bytes;
+ struct devfsd_notify_struct info;
+ unsigned long tmp_event_mask;
+
+ debug_msg_logger(LOG_INFO, __FUNCTION__);
+
+ /* Tell devfs what events we care about */
+ tmp_event_mask = event_mask;
+ do_ioctl_and_die(fd, DEVFSDIOC_SET_EVENT_MASK, tmp_event_mask);
+ while (!caught_signal)
+ {
+ errno = 0;
+ bytes = read (fd, (char *) &info, sizeof info);
+ if (caught_signal)
+ break; /* Must test for this first */
+ if (errno == EINTR)
+ continue; /* Yes, the order is important */
+ if (bytes < 1)
+ break;
+ service_name (&info);
+ }
+ if (caught_signal)
+ {
+ int c_sighup = caught_sighup;
+
+ caught_signal = FALSE;
+ caught_sighup = FALSE;
+ return c_sighup;
+ }
+ msg_logger_and_die(LOG_ERR, "read error on control file");
+} /* End Function do_servicing */
+
+static void service_name (const struct devfsd_notify_struct *info)
+/* [SUMMARY] Service a single devfs change.
+ <info> The devfs change.
+ [RETURNS] Nothing.
+*/
+{
+ unsigned int n;
+ regmatch_t mbuf[MAX_SUBEXPR];
+ struct config_entry_struct *entry;
+
+ debug_msg_logger(LOG_INFO, __FUNCTION__);
+ if (ENABLE_DEBUG && info->overrun_count > 0)
+ debug_msg_logger(LOG_ERR, "lost %u events", info->overrun_count);
+
+ /* Discard lookups on "/dev/log" and "/dev/initctl" */
+ if( info->type == DEVFSD_NOTIFY_LOOKUP &&
+ ((info->devname[0]=='l' && info->devname[1]=='o' &&
+ info->devname[2]=='g' && !info->devname[3]) ||
+ ( info->devname[0]=='i' && info->devname[1]=='n' &&
+ info->devname[2]=='i' && info->devname[3]=='t' &&
+ info->devname[4]=='c' && info->devname[5]=='t' &&
+ info->devname[6]=='l' && !info->devname[7])))
+ return;
+ for (entry = first_config; entry != NULL; entry = entry->next)
+ {
+ /* First check if action matches the type, then check if name matches */
+ if (info->type != entry->action.when || regexec (&entry->preg, info->devname, MAX_SUBEXPR, mbuf, 0) != 0 )
+ continue;
+ for (n = 0; (n < MAX_SUBEXPR) && (mbuf[n].rm_so != -1); ++n)
+ /* VOID */;
+
+ debug_msg_logger(LOG_INFO, "%s: action.what %d", __FUNCTION__, entry->action.what);
+
+ switch (entry->action.what)
+ {
+ case AC_PERMISSIONS:
+ action_permissions (info, entry);
+ break;
+ case AC_MODLOAD:
+ if(ENABLE_DEVFSD_MODLOAD)
+ action_modload (info, entry);
+ break;
+ case AC_EXECUTE:
+ action_execute (info, entry, mbuf, n);
+ break;
+ case AC_COPY:
+ action_copy (info, entry, mbuf, n);
+ break;
+ case AC_IGNORE:
+ return;
+ /*break;*/
+ case AC_MKOLDCOMPAT:
+ case AC_MKNEWCOMPAT:
+ case AC_RMOLDCOMPAT:
+ case AC_RMNEWCOMPAT:
+ action_compat (info, entry->action.what);
+ break;
+ default:
+ msg_logger_and_die(LOG_ERR, "Unknown action");
+ }
+ }
+} /* End Function service_name */
+
+static void action_permissions (const struct devfsd_notify_struct *info,
+ const struct config_entry_struct *entry)
+/* [SUMMARY] Update permissions for a device entry.
+ <info> The devfs change.
+ <entry> The config file entry.
+ [RETURNS] Nothing.
+*/
+{
+ struct stat statbuf;
+
+ debug_msg_logger(LOG_INFO, __FUNCTION__);
+
+ if ( stat (info->devname, &statbuf) != 0 ||
+ chmod (info->devname,(statbuf.st_mode & S_IFMT) | (entry->u.permissions.mode & ~S_IFMT)) != 0 ||
+ chown (info->devname, entry->u.permissions.uid, entry->u.permissions.gid) != 0)
+ {
+ msg_logger(LOG_ERR, "Can't chmod or chown: %s: %m",info->devname);
+ }
+} /* End Function action_permissions */
+
+static void action_modload (const struct devfsd_notify_struct *info,
+ const struct config_entry_struct *entry ATTRIBUTE_UNUSED)
+/* [SUMMARY] Load a module.
+ <info> The devfs change.
+ <entry> The config file entry.
+ [RETURNS] Nothing.
+*/
+{
+ char *argv[6];
+ char device[STRING_LENGTH];
+
+ argv[0] = (char*)MODPROBE;
+ argv[1] = (char*)MODPROBE_SWITCH_1; /* "-k" */
+ argv[2] = (char*)MODPROBE_SWITCH_2; /* "-C" */
+ argv[3] = (char*)CONFIG_MODULES_DEVFS;
+ argv[4] = device;
+ argv[5] = NULL;
+
+ snprintf (device, sizeof (device), "/dev/%s", info->devname);
+ debug_msg_logger(LOG_INFO, "%s: %s %s %s %s %s",__FUNCTION__, argv[0],argv[1],argv[2],argv[3],argv[4]);
+ fork_and_execute(DIE, argv[0], argv);
+} /* End Function action_modload */
+
+static void action_execute (const struct devfsd_notify_struct *info,
+ const struct config_entry_struct *entry,
+ const regmatch_t *regexpr, unsigned int numexpr)
+/* [SUMMARY] Execute a programme.
+ <info> The devfs change.
+ <entry> The config file entry.
+ <regexpr> The number of subexpression (start, end) offsets within the
+ device name.
+ <numexpr> The number of elements within <<regexpr>>.
+ [RETURNS] Nothing.
+*/
+{
+ unsigned int count;
+ struct get_variable_info gv_info;
+ char *argv[MAX_ARGS + 1];
+ char largv[MAX_ARGS + 1][STRING_LENGTH];
+
+ debug_msg_logger(LOG_INFO ,__FUNCTION__);
+ gv_info.info = info;
+ gv_info.devname = info->devname;
+ snprintf (gv_info.devpath, sizeof (gv_info.devpath), "%s/%s", mount_point, info->devname);
+ for (count = 0; entry->u.execute.argv[count] != NULL; ++count)
+ {
+ expand_expression (largv[count], STRING_LENGTH,
+ entry->u.execute.argv[count],
+ get_variable, &gv_info,
+ gv_info.devname, regexpr, numexpr );
+ argv[count] = largv[count];
+ }
+ argv[count] = NULL;
+ fork_and_execute(NO_DIE, argv[0], argv);
+} /* End Function action_execute */
+
+
+static void action_copy (const struct devfsd_notify_struct *info,
+ const struct config_entry_struct *entry,
+ const regmatch_t *regexpr, unsigned int numexpr)
+/* [SUMMARY] Copy permissions.
+ <info> The devfs change.
+ <entry> The config file entry.
+ <regexpr> This list of subexpression (start, end) offsets within the
+ device name.
+ <numexpr> The number of elements in <<regexpr>>.
+ [RETURNS] Nothing.
+*/
+{
+ mode_t new_mode;
+ struct get_variable_info gv_info;
+ struct stat source_stat, dest_stat;
+ char source[STRING_LENGTH], destination[STRING_LENGTH];
+ int ret = 0;
+
+ debug_msg_logger(LOG_INFO, __FUNCTION__);
+
+ dest_stat.st_mode = 0;
+
+ if ( (info->type == DEVFSD_NOTIFY_CHANGE) && S_ISLNK (info->mode) )
+ return;
+ gv_info.info = info;
+ gv_info.devname = info->devname;
+
+ snprintf (gv_info.devpath, sizeof (gv_info.devpath), "%s/%s", mount_point, info->devname);
+ expand_expression (source, STRING_LENGTH, entry->u.copy.source,
+ get_variable, &gv_info, gv_info.devname,
+ regexpr, numexpr);
+
+ expand_expression (destination, STRING_LENGTH, entry->u.copy.destination,
+ get_variable, &gv_info, gv_info.devname,
+ regexpr, numexpr);
+
+ if ( !make_dir_tree (destination) || lstat (source, &source_stat) != 0)
+ return;
+ lstat (destination, &dest_stat);
+ new_mode = source_stat.st_mode & ~S_ISVTX;
+ if (info->type == DEVFSD_NOTIFY_CREATE)
+ new_mode |= S_ISVTX;
+ else if ( (info->type == DEVFSD_NOTIFY_CHANGE) && (dest_stat.st_mode & S_ISVTX) )
+ new_mode |= S_ISVTX;
+ ret = copy_inode (destination, &dest_stat, new_mode, source, &source_stat);
+ if (ENABLE_DEBUG && ret && (errno != EEXIST))
+ debug_msg_logger(LOG_ERR, "copy_inode: %s to %s: %m", source, destination);
+} /* End Function action_copy */
+
+static void action_compat (const struct devfsd_notify_struct *info, unsigned int action)
+/* [SUMMARY] Process a compatibility request.
+ <info> The devfs change.
+ <action> The action to take.
+ [RETURNS] Nothing.
+*/
+{
+ int ret;
+ const char *compat_name = NULL;
+ const char *dest_name = info->devname;
+ char *ptr=NULL;
+ char compat_buf[STRING_LENGTH], dest_buf[STRING_LENGTH];
+ int mode, host, bus, target, lun;
+ unsigned int i;
+ char rewind_;
+ /* 1 to 5 "scsi/" , 6 to 9 "ide/host" */
+ static const char *const fmt[] = {
+ NULL ,
+ "sg/c%db%dt%du%d", /* scsi/generic */
+ "sd/c%db%dt%du%d", /* scsi/disc */
+ "sr/c%db%dt%du%d", /* scsi/cd */
+ "sd/c%db%dt%du%dp%d", /* scsi/part */
+ "st/c%db%dt%du%dm%d%c", /* scsi/mt */
+ "ide/hd/c%db%dt%du%d", /* ide/host/disc */
+ "ide/cd/c%db%dt%du%d", /* ide/host/cd */
+ "ide/hd/c%db%dt%du%dp%d", /* ide/host/part */
+ "ide/mt/c%db%dt%du%d%s", /* ide/host/mt */
+ NULL
+ };
+
+ /* First construct compatibility name */
+ switch (action)
+ {
+ case AC_MKOLDCOMPAT:
+ case AC_RMOLDCOMPAT:
+ compat_name = get_old_name (info->devname, info->namelen, compat_buf, info->major, info->minor);
+ break;
+ case AC_MKNEWCOMPAT:
+ case AC_RMNEWCOMPAT:
+ ptr = strrchr (info->devname, '/') + 1;
+ i=scan_dev_name(info->devname, info->namelen, ptr);
+
+ debug_msg_logger(LOG_INFO, "%s: scan_dev_name = %d", __FUNCTION__, i);
+
+ /* nothing found */
+ if(i==0 || i > 9)
+ return;
+
+ sscanf (info->devname +((i<6)?5:4), "host%d/bus%d/target%d/lun%d/", &host, &bus, &target, &lun);
+ snprintf (dest_buf, sizeof (dest_buf), "../%s", info->devname + ((i>5)?4:0));
+ dest_name = dest_buf;
+ compat_name = compat_buf;
+
+
+ /* 1 == scsi/generic 2 == scsi/disc 3 == scsi/cd 6 == ide/host/disc 7 == ide/host/cd */
+ if( i == 1 || i == 2 || i == 3 || i == 6 || i ==7 )
+ sprintf ( compat_buf, fmt[i], host, bus, target, lun);
+
+ /* 4 == scsi/part 8 == ide/host/part */
+ if( i == 4 || i == 8)
+ sprintf ( compat_buf, fmt[i], host, bus, target, lun, atoi (ptr + 4) );
+
+ /* 5 == scsi/mt */
+ if( i == 5)
+ {
+ rewind_ = info->devname[info->namelen - 1];
+ if (rewind_ != 'n')
+ rewind_ = '\0';
+ mode=0;
+ if(ptr[2] == 'l' /*108*/ || ptr[2] == 'm'/*109*/)
+ mode = ptr[2] - 107; /* 1 or 2 */
+ if(ptr[2] == 'a')
+ mode = 3;
+ sprintf (compat_buf, fmt [i], host, bus, target, lun, mode, rewind_);
+ }
+
+ /* 9 == ide/host/mt */
+ if( i == 9 )
+ snprintf (compat_buf, sizeof (compat_buf), fmt[i], host, bus, target, lun, ptr + 2);
+ /* esac */
+ } /* switch (action) */
+
+ if(compat_name == NULL )
+ return;
+
+ debug_msg_logger( LOG_INFO, "%s: %s", __FUNCTION__, compat_name);
+
+ /* Now decide what to do with it */
+ switch (action)
+ {
+ case AC_MKOLDCOMPAT:
+ case AC_MKNEWCOMPAT:
+ mksymlink (dest_name, compat_name);
+ break;
+ case AC_RMOLDCOMPAT:
+ case AC_RMNEWCOMPAT:
+ ret = unlink (compat_name);
+ if (ENABLE_DEBUG && ret)
+ debug_msg_logger(LOG_ERR, "unlink: %s: %m", compat_name);
+ break;
+ /*esac*/
+ } /* switch (action) */
+} /* End Function action_compat */
+
+static void restore(char *spath, struct stat source_stat, int rootlen)
+{
+ char dpath[STRING_LENGTH];
+ struct stat dest_stat;
+
+ debug_msg_logger(LOG_INFO, __FUNCTION__);
+
+ dest_stat.st_mode = 0;
+ snprintf (dpath, sizeof dpath, "%s%s", mount_point, spath + rootlen);
+ lstat (dpath, &dest_stat);
+
+ if ( S_ISLNK (source_stat.st_mode) || (source_stat.st_mode & S_ISVTX) )
+ copy_inode (dpath, &dest_stat, (source_stat.st_mode & ~S_ISVTX) , spath, &source_stat);
+
+ if ( S_ISDIR (source_stat.st_mode) )
+ dir_operation(RESTORE, spath, rootlen,NULL);
+}
+
+
+static int copy_inode (const char *destpath, const struct stat *dest_stat,
+ mode_t new_mode,
+ const char *sourcepath, const struct stat *source_stat)
+/* [SUMMARY] Copy an inode.
+ <destpath> The destination path. An existing inode may be deleted.
+ <dest_stat> The destination stat(2) information.
+ <new_mode> The desired new mode for the destination.
+ <sourcepath> The source path.
+ <source_stat> The source stat(2) information.
+ [RETURNS] TRUE on success, else FALSE.
+*/
+{
+ int source_len, dest_len;
+ char source_link[STRING_LENGTH], dest_link[STRING_LENGTH];
+ int fd, val;
+ struct sockaddr_un un_addr;
+ char symlink_val[STRING_LENGTH];
+
+ debug_msg_logger(LOG_INFO, __FUNCTION__);
+
+ if ( (source_stat->st_mode & S_IFMT) == (dest_stat->st_mode & S_IFMT) )
+ {
+ /* Same type */
+ if ( S_ISLNK (source_stat->st_mode) )
+ {
+ if (( source_len = readlink (sourcepath, source_link, STRING_LENGTH - 1) ) < 0 ||
+ ( dest_len = readlink (destpath , dest_link , STRING_LENGTH - 1) ) < 0 )
+ return FALSE;
+ source_link[source_len] = '\0';
+ dest_link[dest_len] = '\0';
+ if ( (source_len != dest_len) || (strcmp (source_link, dest_link) != 0) )
+ {
+ unlink (destpath);
+ symlink (source_link, destpath);
+ }
+ return TRUE;
+ } /* Else not a symlink */
+ chmod (destpath, new_mode & ~S_IFMT);
+ chown (destpath, source_stat->st_uid, source_stat->st_gid);
+ return TRUE;
+ }
+ /* Different types: unlink and create */
+ unlink (destpath);
+ switch (source_stat->st_mode & S_IFMT)
+ {
+ case S_IFSOCK:
+ if ( ( fd = socket (AF_UNIX, SOCK_STREAM, 0) ) < 0 )
+ break;
+ un_addr.sun_family = AF_UNIX;
+ snprintf (un_addr.sun_path, sizeof (un_addr.sun_path), "%s", destpath);
+ val = bind (fd, (struct sockaddr *) &un_addr, (int) sizeof un_addr);
+ close (fd);
+ if (val != 0 || chmod (destpath, new_mode & ~S_IFMT) != 0)
+ break;
+ goto do_chown;
+ case S_IFLNK:
+ if ( ( val = readlink (sourcepath, symlink_val, STRING_LENGTH - 1) ) < 0 )
+ break;
+ symlink_val[val] = '\0';
+ if (symlink (symlink_val, destpath) == 0)
+ return TRUE;
+ break;
+ case S_IFREG:
+ if ( ( fd = open (destpath, O_RDONLY | O_CREAT, new_mode & ~S_IFMT) ) < 0 )
+ break;
+ close (fd);
+ if (chmod (destpath, new_mode & ~S_IFMT) != 0)
+ break;
+ goto do_chown;
+ case S_IFBLK:
+ case S_IFCHR:
+ case S_IFIFO:
+ if (mknod (destpath, new_mode, source_stat->st_rdev) != 0)
+ break;
+ goto do_chown;
+ case S_IFDIR:
+ if (mkdir (destpath, new_mode & ~S_IFMT) != 0)
+ break;
+do_chown:
+ if (chown (destpath, source_stat->st_uid, source_stat->st_gid) == 0)
+ return TRUE;
+ /*break;*/
+ }
+ return FALSE;
+} /* End Function copy_inode */
+
+static void free_config (void)
+/* [SUMMARY] Free the configuration information.
+ [RETURNS] Nothing.
+*/
+{
+ struct config_entry_struct *c_entry;
+ void *next;
+
+ debug_msg_logger(LOG_INFO, __FUNCTION__);
+
+ for (c_entry = first_config; c_entry != NULL; c_entry = next)
+ {
+ unsigned int count;
+
+ next = c_entry->next;
+ regfree (&c_entry->preg);
+ if (c_entry->action.what == AC_EXECUTE)
+ {
+ for (count = 0; count < MAX_ARGS; ++count)
+ {
+ if (c_entry->u.execute.argv[count] == NULL)
+ break;
+ free (c_entry->u.execute.argv[count]);
+ }
+ }
+ free (c_entry);
+ }
+ first_config = NULL;
+ last_config = NULL;
+} /* End Function free_config */
+
+static int get_uid_gid (int flag, const char *string)
+/* [SUMMARY] Convert a string to a UID or GID value.
+ <flag> "UID" or "GID".
+ <string> The string.
+ [RETURNS] The UID or GID value.
+*/
+{
+ struct passwd *pw_ent;
+ struct group *grp_ent;
+ static const char *msg;
+
+ if (ENABLE_DEVFSD_VERBOSE)
+ msg="user";
+
+ debug_msg_logger(LOG_INFO, __FUNCTION__);
+
+ if(ENABLE_DEBUG && flag != UID && flag != GID)
+ msg_logger_and_die(LOG_ERR,"%s: flag != UID && flag != GID", __FUNCTION__);
+
+ if ( isdigit (string[0]) || ( (string[0] == '-') && isdigit (string[1]) ) )
+ return atoi(string);
+
+ if ( flag == UID && ( pw_ent = getpwnam (string) ) != NULL )
+ return pw_ent->pw_uid;
+
+ if ( flag == GID && ( grp_ent = getgrnam (string) ) != NULL )
+ return grp_ent->gr_gid;
+ else if(ENABLE_DEVFSD_VERBOSE)
+ msg="group";
+
+ if(ENABLE_DEVFSD_VERBOSE)
+ msg_logger(LOG_ERR,"unknown %s: %s, defaulting to %cid=0", msg, string, msg[0]);
+ return 0;
+}/* End Function get_uid_gid */
+
+static mode_t get_mode (const char *string)
+/* [SUMMARY] Convert a string to a mode value.
+ <string> The string.
+ [RETURNS] The mode value.
+*/
+{
+ mode_t mode;
+ int i;
+
+ debug_msg_logger(LOG_INFO, __FUNCTION__);
+
+ if ( isdigit (string[0]) )
+ return strtoul(string, NULL, 8);
+ if (strlen (string) != 9)
+ msg_logger_and_die(LOG_ERR, "bad mode: %s", string);
+
+ mode = 0;
+ i= S_IRUSR;
+ while (i>0)
+ {
+ if(string[0]=='r'||string[0]=='w'||string[0]=='x')
+ mode+=i;
+ i=i/2;
+ string++;
+ }
+ return mode;
+} /* End Function get_mode */
+
+static void signal_handler (int sig)
+{
+ debug_msg_logger(LOG_INFO, __FUNCTION__);
+
+ caught_signal = TRUE;
+ if (sig == SIGHUP)
+ caught_sighup = TRUE;
+
+ msg_logger(LOG_INFO, "Caught signal %d", sig);
+} /* End Function signal_handler */
+
+static const char *get_variable (const char *variable, void *info)
+{
+ struct get_variable_info *gv_info = info;
+ static char hostname[STRING_LENGTH], sbuf[STRING_LENGTH];
+ const char *field_names[] = { "hostname", "mntpt", "devpath", "devname",
+ "uid", "gid", "mode", hostname, mount_point,
+ gv_info->devpath, gv_info->devname, 0 };
+ int i;
+
+ debug_msg_logger(LOG_INFO, __FUNCTION__);
+
+ if (gethostname (hostname, STRING_LENGTH - 1) != 0)
+ msg_logger_and_die(LOG_ERR, "gethostname: %m");
+
+ /* Here on error we should do exit(RV_SYS_ERROR), instead we do exit(EXIT_FAILURE) */
+ hostname[STRING_LENGTH - 1] = '\0';
+
+ /* index_in_str_array returns i>=0 */
+ i=index_in_str_array(field_names, variable);
+
+ if ( i > 6 || i < 0 || (i > 1 && gv_info == NULL))
+ return NULL;
+ if( i >= 0 && i <= 3)
+ {
+ debug_msg_logger(LOG_INFO, "%s: i=%d %s", __FUNCTION__, i ,field_names[i+7]);
+ return field_names[i+7];
+ }
+
+ if(i == 4 )
+ sprintf (sbuf, "%u", gv_info->info->uid);
+ else if(i == 5)
+ sprintf (sbuf, "%u", gv_info->info->gid);
+ else if(i == 6)
+ sprintf (sbuf, "%o", gv_info->info->mode);
+
+ debug_msg_logger(LOG_INFO, "%s: %s", __FUNCTION__, sbuf);
+
+ return sbuf;
+} /* End Function get_variable */
+
+static void service(struct stat statbuf, char *path)
+{
+ struct devfsd_notify_struct info;
+
+ debug_msg_logger(LOG_INFO, __FUNCTION__);
+
+ memset (&info, 0, sizeof info);
+ info.type = DEVFSD_NOTIFY_REGISTERED;
+ info.mode = statbuf.st_mode;
+ info.major = major (statbuf.st_rdev);
+ info.minor = minor (statbuf.st_rdev);
+ info.uid = statbuf.st_uid;
+ info.gid = statbuf.st_gid;
+ snprintf (info.devname, sizeof (info.devname), "%s", path + strlen (mount_point) + 1);
+ info.namelen = strlen (info.devname);
+ service_name (&info);
+ if ( S_ISDIR (statbuf.st_mode) )
+ dir_operation(SERVICE,path,0,NULL);
+}
+
+static void dir_operation(int type, const char * dir_name, int var, unsigned long *event_mask)
+/* [SUMMARY] Scan a directory tree and generate register events on leaf nodes.
+ <flag> To choose which function to perform
+ <dp> The directory pointer. This is closed upon completion.
+ <dir_name> The name of the directory.
+ <rootlen> string length parameter.
+ [RETURNS] Nothing.
+*/
+{
+ struct stat statbuf;
+ DIR *dp;
+ struct dirent *de;
+ char path[STRING_LENGTH];
+
+ debug_msg_logger(LOG_INFO, __FUNCTION__);
+
+ if((dp = opendir( dir_name))==NULL)
+ {
+ debug_msg_logger(LOG_ERR, "opendir: %s: %m", dir_name);
+ return;
+ }
+
+ while ( (de = readdir (dp) ) != NULL )
+ {
+
+ if(de->d_name && *de->d_name == '.' && (!de->d_name[1] || (de->d_name[1] == '.' && !de->d_name[2])))
+ continue;
+ snprintf (path, sizeof (path), "%s/%s", dir_name, de->d_name);
+ debug_msg_logger(LOG_ERR, "%s: %s", __FUNCTION__, path);
+
+ if (lstat (path, &statbuf) != 0)
+ {
+ debug_msg_logger(LOG_ERR, "%s: %s: %m", __FUNCTION__, path);
+ continue;
+ }
+ switch (type)
+ {
+ case SERVICE:
+ service(statbuf,path);
+ break;
+ case RESTORE:
+ restore(path, statbuf, var);
+ break;
+ case READ_CONFIG:
+ read_config_file (path, var, event_mask);
+ break;
+ }
+ }
+ closedir (dp);
+} /* End Function do_scan_and_service */
+
+static int mksymlink (const char *oldpath, const char *newpath)
+/* [SUMMARY] Create a symlink, creating intervening directories as required.
+ <oldpath> The string contained in the symlink.
+ <newpath> The name of the new symlink.
+ [RETURNS] 0 on success, else -1.
+*/
+{
+ debug_msg_logger(LOG_INFO, __FUNCTION__);
+
+ if ( !make_dir_tree (newpath) )
+ return -1;
+
+ if (symlink (oldpath, newpath) != 0) {
+ if (errno != EEXIST) {
+ debug_msg_logger(LOG_ERR, "%s: %s to %s: %m", __FUNCTION__, oldpath, newpath);
+ return -1;
+ }
+ }
+ return 0;
+} /* End Function mksymlink */
+
+
+static int make_dir_tree (const char *path)
+/* [SUMMARY] Creating intervening directories for a path as required.
+ <path> The full pathname (including the leaf node).
+ [RETURNS] TRUE on success, else FALSE.
+*/
+{
+ debug_msg_logger(LOG_INFO, __FUNCTION__);
+
+ if (bb_make_directory( dirname((char *)path), -1, FILEUTILS_RECUR )==-1)
+ {
+ debug_msg_logger(LOG_ERR, "%s: %s: %m",__FUNCTION__, path);
+ return FALSE;
+ }
+ return TRUE;
+} /* End Function make_dir_tree */
+
+static int expand_expression(char *output, unsigned int outsize,
+ const char *input,
+ const char *(*get_variable_func)(const char *variable, void *info),
+ void *info,
+ const char *devname,
+ const regmatch_t *ex, unsigned int numexp)
+/* [SUMMARY] Expand environment variables and regular subexpressions in string.
+ <output> The output expanded expression is written here.
+ <length> The size of the output buffer.
+ <input> The input expression. This may equal <<output>>.
+ <get_variable> A function which will be used to get variable values. If
+ this returns NULL, the environment is searched instead. If this is NULL,
+ only the environment is searched.
+ <info> An arbitrary pointer passed to <<get_variable>>.
+ <devname> Device name; specifically, this is the string that contains all
+ of the regular subexpressions.
+ <ex> Array of start / end offsets into info->devname for each subexpression
+ <numexp> Number of regular subexpressions found in <<devname>>.
+ [RETURNS] TRUE on success, else FALSE.
+*/
+{
+ char temp[STRING_LENGTH];
+
+ debug_msg_logger(LOG_INFO, __FUNCTION__);
+
+ if ( !st_expr_expand (temp, STRING_LENGTH, input, get_variable_func, info) )
+ return FALSE;
+ expand_regexp (output, outsize, temp, devname, ex, numexp);
+ return TRUE;
+} /* End Function expand_expression */
+
+static void expand_regexp (char *output, size_t outsize, const char *input,
+ const char *devname,
+ const regmatch_t *ex, unsigned int numex )
+/* [SUMMARY] Expand all occurrences of the regular subexpressions \0 to \9.
+ <output> The output expanded expression is written here.
+ <outsize> The size of the output buffer.
+ <input> The input expression. This may NOT equal <<output>>, because
+ supporting that would require yet another string-copy. However, it's not
+ hard to write a simple wrapper function to add this functionality for those
+ few cases that need it.
+ <devname> Device name; specifically, this is the string that contains all
+ of the regular subexpressions.
+ <ex> An array of start and end offsets into <<devname>>, one for each
+ subexpression
+ <numex> Number of subexpressions in the offset-array <<ex>>.
+ [RETURNS] Nothing.
+*/
+{
+ const char last_exp = '0' - 1 + numex;
+ int c = -1;
+
+ debug_msg_logger(LOG_INFO, __FUNCTION__);
+
+ /* Guarantee NULL termination by writing an explicit '\0' character into
+ the very last byte */
+ if (outsize)
+ output[--outsize] = '\0';
+ /* Copy the input string into the output buffer, replacing '\\' with '\'
+ and '\0' .. '\9' with subexpressions 0 .. 9, if they exist. Other \x
+ codes are deleted */
+ while ( (c != '\0') && (outsize != 0) )
+ {
+ c = *input;
+ ++input;
+ if (c == '\\')
+ {
+ c = *input;
+ ++input;
+ if (c != '\\')
+ {
+ if ((c >= '0') && (c <= last_exp))
+ {
+ const regmatch_t *subexp = ex + (c - '0');
+ unsigned int sublen = subexp->rm_eo - subexp->rm_so;
+
+ /* Range checking */
+ if (sublen > outsize)
+ sublen = outsize;
+ strncpy (output, devname + subexp->rm_so, sublen);
+ output += sublen;
+ outsize -= sublen;
+ }
+ continue;
+ }
+ }
+ *output = c;
+ ++output;
+ --outsize;
+ } /* while */
+} /* End Function expand_regexp */
+
+
+/* from compat_name.c */
+
+struct translate_struct
+{
+ const char *match; /* The string to match to (up to length) */
+ const char *format; /* Format of output, "%s" takes data past match string,
+ NULL is effectively "%s" (just more efficient) */
+};
+
+static struct translate_struct translate_table[] =
+{
+ {"sound/", NULL},
+ {"printers/", "lp%s"},
+ {"v4l/", NULL},
+ {"parports/", "parport%s"},
+ {"fb/", "fb%s"},
+ {"netlink/", NULL},
+ {"loop/", "loop%s"},
+ {"floppy/", "fd%s"},
+ {"rd/", "ram%s"},
+ {"md/", "md%s"}, /* Meta-devices */
+ {"vc/", "tty%s"},
+ {"misc/", NULL},
+ {"isdn/", NULL},
+ {"pg/", "pg%s"}, /* Parallel port generic ATAPI interface*/
+ {"i2c/", "i2c-%s"},
+ {"staliomem/", "staliomem%s"}, /* Stallion serial driver control */
+ {"tts/E", "ttyE%s"}, /* Stallion serial driver */
+ {"cua/E", "cue%s"}, /* Stallion serial driver callout */
+ {"tts/R", "ttyR%s"}, /* Rocketport serial driver */
+ {"cua/R", "cur%s"}, /* Rocketport serial driver callout */
+ {"ip2/", "ip2%s"}, /* Computone serial driver control */
+ {"tts/F", "ttyF%s"}, /* Computone serial driver */
+ {"cua/F", "cuf%s"}, /* Computone serial driver callout */
+ {"tts/C", "ttyC%s"}, /* Cyclades serial driver */
+ {"cua/C", "cub%s"}, /* Cyclades serial driver callout */
+ {"tts/", "ttyS%s"}, /* Generic serial: must be after others */
+ {"cua/", "cua%s"}, /* Generic serial: must be after others */
+ {"input/js", "js%s"}, /* Joystick driver */
+ {NULL, NULL}
+};
+
+const char *get_old_name (const char *devname, unsigned int namelen,
+ char *buffer, unsigned int major, unsigned int minor)
+/* [SUMMARY] Translate a kernel-supplied name into an old name.
+ <devname> The device name provided by the kernel.
+ <namelen> The length of the name.
+ <buffer> A buffer that may be used. This should be at least 128 bytes long.
+ <major> The major number for the device.
+ <minor> The minor number for the device.
+ [RETURNS] A pointer to the old name if known, else NULL.
+*/
+{
+ const char *compat_name = NULL;
+ char *ptr;
+ struct translate_struct *trans;
+ unsigned int i;
+ char mode;
+ int indexx;
+ const char *pty1;
+ const char *pty2;
+ size_t len;
+ /* 1 to 5 "scsi/" , 6 to 9 "ide/host", 10 sbp/, 11 vcc/, 12 pty/ */
+ static const char *const fmt[] = {
+ NULL ,
+ "sg%u", /* scsi/generic */
+ NULL, /* scsi/disc */
+ "sr%u", /* scsi/cd */
+ NULL, /* scsi/part */
+ "nst%u%c", /* scsi/mt */
+ "hd%c" , /* ide/host/disc */
+ "hd%c" , /* ide/host/cd */
+ "hd%c%s", /* ide/host/part */
+ "%sht%d", /* ide/host/mt */
+ "sbpcd%u", /* sbp/ */
+ "vcs%s", /* vcc/ */
+ "%cty%c%c", /* pty/ */
+ NULL
+ };
+
+ debug_msg_logger(LOG_INFO, __FUNCTION__);
+
+ for (trans = translate_table; trans->match != NULL; ++trans)
+ {
+ len = strlen (trans->match);
+
+ if (strncmp (devname, trans->match, len) == 0)
+ {
+ if (trans->format == NULL)
+ return devname + len;
+ sprintf (buffer, trans->format, devname + len);
+ return buffer;
+ }
+ }
+
+ ptr = (strrchr (devname, '/') + 1);
+ i = scan_dev_name(devname, namelen, ptr);
+
+ if( i > 0 && i < 13)
+ compat_name = buffer;
+ else
+ return NULL;
+
+ debug_msg_logger(LOG_INFO, "%s: scan_dev_name = %d", __FUNCTION__, i);
+
+ /* 1 == scsi/generic, 3 == scsi/cd, 10 == sbp/ */
+ if( i == 1 || i == 3 || i == 10 )
+ sprintf (buffer, fmt[i], minor);
+
+ /* 2 ==scsi/disc, 4 == scsi/part */
+ if( i == 2 || i == 4)
+ compat_name = write_old_sd_name (buffer, major, minor,((i == 2)?"":(ptr + 4)));
+
+ /* 5 == scsi/mt */
+ if( i == 5)
+ {
+ mode = ptr[2];
+ if (mode == 'n')
+ mode = '\0';
+ sprintf (buffer, fmt[i], minor & 0x1f, mode);
+ if (devname[namelen - 1] != 'n')
+ ++compat_name;
+ }
+ /* 6 == ide/host/disc, 7 == ide/host/cd, 8 == ide/host/part */
+ if( i == 6 || i == 7 || i == 8 )
+ /* last arg should be ignored for i == 6 or i== 7 */
+ sprintf (buffer, fmt[i] , get_old_ide_name (major, minor), ptr + 4);
+
+ /* 9 == ide/host/mt */
+ if( i == 9 )
+ sprintf (buffer, fmt[i], ptr + 2, minor & 0x7f);
+
+ /* 11 == vcc/ */
+ if( i == 11 )
+ {
+ sprintf (buffer, fmt[i], devname + 4);
+ if (buffer[3] == '0')
+ buffer[3] = '\0';
+ }
+ /* 12 == pty/ */
+ if( i == 12 )
+ {
+ pty1 = "pqrstuvwxyzabcde";
+ pty2 = "0123456789abcdef";
+ indexx = atoi (devname + 5);
+ sprintf (buffer, fmt[i], (devname[4] == 'm') ? 'p' : 't', pty1[indexx >> 4], pty2[indexx & 0x0f]);
+ }
+
+ if(ENABLE_DEBUG && compat_name!=NULL)
+ msg_logger(LOG_INFO, "%s: compat_name %s", __FUNCTION__, compat_name);
+
+ return compat_name;
+} /* End Function get_old_name */
+
+static char get_old_ide_name (unsigned int major, unsigned int minor)
+/* [SUMMARY] Get the old IDE name for a device.
+ <major> The major number for the device.
+ <minor> The minor number for the device.
+ [RETURNS] The drive letter.
+*/
+{
+ char letter='y'; /* 121 */
+ char c='a'; /* 97 */
+ int i=IDE0_MAJOR;
+
+ debug_msg_logger(LOG_INFO, __FUNCTION__);
+
+ /* I hope it works like the previous code as it saves a few bytes. Tito ;P */
+ do {
+ if( i==IDE0_MAJOR || i==IDE1_MAJOR || i==IDE2_MAJOR ||
+ i==IDE3_MAJOR || i==IDE4_MAJOR || i==IDE5_MAJOR ||
+ i==IDE6_MAJOR || i==IDE7_MAJOR || i==IDE8_MAJOR ||
+ i==IDE9_MAJOR )
+ {
+ if((unsigned int)i==major)
+ {
+ letter=c;
+ break;
+ }
+ c+=2;
+ }
+ i++;
+ } while (i<=IDE9_MAJOR);
+
+ if (minor > 63)
+ ++letter;
+ return letter;
+} /* End Function get_old_ide_name */
+
+static char *write_old_sd_name (char *buffer,
+ unsigned int major, unsigned int minor,
+ const char *part)
+/* [SUMMARY] Write the old SCSI disc name to a buffer.
+ <buffer> The buffer to write to.
+ <major> The major number for the device.
+ <minor> The minor number for the device.
+ <part> The partition string. Must be "" for a whole-disc entry.
+ [RETURNS] A pointer to the buffer on success, else NULL.
+*/
+{
+ unsigned int disc_index;
+
+ debug_msg_logger(LOG_INFO, __FUNCTION__);
+
+ if (major == 8)
+ {
+ sprintf (buffer, "sd%c%s", 'a' + (minor >> 4), part);
+ return buffer;
+ }
+ if ( (major > 64) && (major < 72) )
+ {
+ disc_index = ( (major - 64) << 4 ) + (minor >> 4);
+ if (disc_index < 26)
+ sprintf (buffer, "sd%c%s", 'a' + disc_index, part);
+ else
+ sprintf (buffer, "sd%c%c%s", 'a' + (disc_index / 26) - 1, 'a' + disc_index % 26,part);
+ return buffer;
+ }
+ return NULL;
+} /* End Function write_old_sd_name */
+
+
+/* expression.c */
+
+/*EXPERIMENTAL_FUNCTION*/
+
+int st_expr_expand (char *output, unsigned int length, const char *input,
+ const char *(*get_variable_func) (const char *variable,
+ void *info),
+ void *info)
+/* [SUMMARY] Expand an expression using Borne Shell-like unquoted rules.
+ <output> The output expanded expression is written here.
+ <length> The size of the output buffer.
+ <input> The input expression. This may equal <<output>>.
+ <get_variable> A function which will be used to get variable values. If
+ this returns NULL, the environment is searched instead. If this is NULL,
+ only the environment is searched.
+ <info> An arbitrary pointer passed to <<get_variable>>.
+ [RETURNS] TRUE on success, else FALSE.
+*/
+{
+ char ch;
+ unsigned int len;
+ unsigned int out_pos = 0;
+ const char *env;
+ const char *ptr;
+ struct passwd *pwent;
+ char buffer[BUFFER_SIZE], tmp[STRING_LENGTH];
+
+ debug_msg_logger(LOG_INFO, __FUNCTION__);
+
+ if (length > BUFFER_SIZE)
+ length = BUFFER_SIZE;
+ for (; TRUE; ++input)
+ {
+ switch (ch = *input)
+ {
+ case '$':
+ /* Variable expansion */
+ input = expand_variable (buffer, length, &out_pos, ++input, get_variable_func, info);
+ if (input == NULL)
+ return FALSE;
+ break;
+ case '~':
+ /* Home directory expansion */
+ ch = input[1];
+ if ( isspace (ch) || (ch == '/') || (ch == '\0') )
+ {
+ /* User's own home directory: leave separator for next time */
+ if ( ( env = getenv ("HOME") ) == NULL )
+ {
+ msg_logger(LOG_INFO, bb_msg_variable_not_found, "HOME");
+ return FALSE;
+ }
+ len = strlen (env);
+ if (len + out_pos >= length)
+ goto st_expr_expand_out;
+ memcpy (buffer + out_pos, env, len + 1);
+ out_pos += len;
+ continue;
+ }
+ /* Someone else's home directory */
+ for (ptr = ++input; !isspace (ch) && (ch != '/') && (ch != '\0'); ch = *++ptr)
+ /* VOID */ ;
+ len = ptr - input;
+ if (len >= sizeof tmp)
+ goto st_expr_expand_out;
+ safe_memcpy (tmp, input, len);
+ input = ptr - 1;
+ if ( ( pwent = getpwnam (tmp) ) == NULL )
+ {
+ msg_logger(LOG_INFO, "no pwent for: %s", tmp);
+ return FALSE;
+ }
+ len = strlen (pwent->pw_dir);
+ if (len + out_pos >= length)
+ goto st_expr_expand_out;
+ memcpy (buffer + out_pos, pwent->pw_dir, len + 1);
+ out_pos += len;
+ break;
+ case '\0':
+ /* Falltrough */
+ default:
+ if (out_pos >= length)
+ goto st_expr_expand_out;
+ buffer[out_pos++] = ch;
+ if (ch == '\0')
+ {
+ memcpy (output, buffer, out_pos);
+ return TRUE;
+ }
+ break;
+ /* esac */
+ }
+ }
+ return FALSE;
+st_expr_expand_out:
+ msg_logger(LOG_INFO, bb_msg_small_buffer);
+ return FALSE;
+} /* End Function st_expr_expand */
+
+
+/* Private functions follow */
+
+static const char *expand_variable (char *buffer, unsigned int length,
+ unsigned int *out_pos, const char *input,
+ const char *(*func) (const char *variable,
+ void *info),
+ void *info)
+/* [SUMMARY] Expand a variable.
+ <buffer> The buffer to write to.
+ <length> The length of the output buffer.
+ <out_pos> The current output position. This is updated.
+ <input> A pointer to the input character pointer.
+ <func> A function which will be used to get variable values. If this
+ returns NULL, the environment is searched instead. If this is NULL, only
+ the environment is searched.
+ <info> An arbitrary pointer passed to <<func>>.
+ <errfp> Diagnostic messages are written here.
+ [RETURNS] A pointer to the end of this subexpression on success, else NULL.
+*/
+{
+ char ch;
+ int len;
+ unsigned int open_braces;
+ const char *env, *ptr;
+ char tmp[STRING_LENGTH];
+
+ debug_msg_logger(LOG_INFO, __FUNCTION__);
+
+ ch = input[0];
+ if (ch == '$')
+ {
+ /* Special case for "$$": PID */
+ sprintf ( tmp, "%d", (int) getpid () );
+ len = strlen (tmp);
+ if (len + *out_pos >= length)
+ goto expand_variable_out;
+
+ memcpy (buffer + *out_pos, tmp, len + 1);
+ out_pos += len;
+ return input;
+ }
+ /* Ordinary variable expansion, possibly in braces */
+ if (ch != '{')
+ {
+ /* Simple variable expansion */
+ for (ptr = input; isalnum (ch) || (ch == '_') || (ch == ':');ch = *++ptr)
+ /* VOID */ ;
+ len = ptr - input;
+ if ((size_t)len >= sizeof tmp)
+ goto expand_variable_out;
+
+ safe_memcpy (tmp, input, len);
+ input = ptr - 1;
+ if ( ( env = get_variable_v2 (tmp, func, info) ) == NULL )
+ {
+ msg_logger(LOG_INFO, bb_msg_variable_not_found, tmp);
+ return NULL;
+ }
+ len = strlen (env);
+ if (len + *out_pos >= length)
+ goto expand_variable_out;
+
+ memcpy (buffer + *out_pos, env, len + 1);
+ *out_pos += len;
+ return input;
+ }
+ /* Variable in braces: check for ':' tricks */
+ ch = *++input;
+ for (ptr = input; isalnum (ch) || (ch == '_'); ch = *++ptr)
+ /* VOID */;
+ if (ch == '}')
+ {
+ /* Must be simple variable expansion with "${var}" */
+ len = ptr - input;
+ if ((size_t)len >= sizeof tmp)
+ goto expand_variable_out;
+
+ safe_memcpy (tmp, input, len);
+ ptr = expand_variable (buffer, length, out_pos, tmp, func, info );
+ if (ptr == NULL)
+ return NULL;
+ return input + len;
+ }
+ if (ch != ':' || ptr[1] != '-' )
+ {
+ msg_logger(LOG_INFO, "illegal char in var name");
+ return NULL;
+ }
+ /* It's that handy "${var:-word}" expression. Check if var is defined */
+ len = ptr - input;
+ if ((size_t)len >= sizeof tmp)
+ goto expand_variable_out;
+
+ safe_memcpy (tmp, input, len);
+ /* Move input pointer to ':' */
+ input = ptr;
+ /* First skip to closing brace, taking note of nested expressions */
+ ptr += 2;
+ ch = ptr[0];
+ for (open_braces = 1; open_braces > 0; ch = *++ptr)
+ {
+ switch (ch)
+ {
+ case '{':
+ ++open_braces;
+ break;
+ case '}':
+ --open_braces;
+ break;
+ case '\0':
+ msg_logger(LOG_INFO,"\"}\" not found in: %s", input);
+ return NULL;
+ default:
+ break;
+ }
+ }
+ --ptr;
+ /* At this point ptr should point to closing brace of "${var:-word}" */
+ if ( ( env = get_variable_v2 (tmp, func, info) ) != NULL )
+ {
+ /* Found environment variable, so skip the input to the closing brace
+ and return the variable */
+ input = ptr;
+ len = strlen (env);
+ if (len + *out_pos >= length)
+ goto expand_variable_out;
+
+ memcpy (buffer + *out_pos, env, len + 1);
+ *out_pos += len;
+ return input;
+ }
+ /* Environment variable was not found, so process word. Advance input
+ pointer to start of word in "${var:-word}" */
+ input += 2;
+ len = ptr - input;
+ if ((size_t)len >= sizeof tmp)
+ goto expand_variable_out;
+
+ safe_memcpy (tmp, input, len);
+ input = ptr;
+ if ( !st_expr_expand (tmp, STRING_LENGTH, tmp, func, info ) )
+ return NULL;
+ len = strlen (tmp);
+ if (len + *out_pos >= length)
+ goto expand_variable_out;
+
+ memcpy (buffer + *out_pos, tmp, len + 1);
+ *out_pos += len;
+ return input;
+expand_variable_out:
+ msg_logger(LOG_INFO, bb_msg_small_buffer);
+ return NULL;
+} /* End Function expand_variable */
+
+
+static const char *get_variable_v2 (const char *variable,
+ const char *(*func) (const char *variable, void *info),
+ void *info)
+/* [SUMMARY] Get a variable from the environment or .
+ <variable> The variable name.
+ <func> A function which will be used to get the variable. If this returns
+ NULL, the environment is searched instead. If this is NULL, only the
+ environment is searched.
+ [RETURNS] The value of the variable on success, else NULL.
+*/
+{
+ const char *value;
+
+ debug_msg_logger(LOG_INFO, __FUNCTION__);
+
+ if (func != NULL)
+ {
+ value = (*func) (variable, info);
+ if (value != NULL)
+ return value;
+ }
+ return getenv(variable);
+} /* End Function get_variable */
+
+/* END OF CODE */
diff --git a/i/pc104/initrd/conf/busybox/miscutils/eject.c b/i/pc104/initrd/conf/busybox/miscutils/eject.c
new file mode 100644
index 0000000..5d7ef1c
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/miscutils/eject.c
@@ -0,0 +1,61 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * eject implementation for busybox
+ *
+ * Copyright (C) 2004 Peter Willis <psyphreak@phreaker.net>
+ * Copyright (C) 2005 Tito Ragusa <farmatito@tiscali.it>
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ */
+
+/*
+ * This is a simple hack of eject based on something Erik posted in #uclibc.
+ * Most of the dirty work blatantly ripped off from cat.c =)
+ */
+
+#include "busybox.h"
+
+/* various defines swiped from linux/cdrom.h */
+#define CDROMCLOSETRAY 0x5319 /* pendant of CDROMEJECT */
+#define CDROMEJECT 0x5309 /* Ejects the cdrom media */
+#define CDROM_DRIVE_STATUS 0x5326 /* Get tray position, etc. */
+/* drive status possibilities returned by CDROM_DRIVE_STATUS ioctl */
+#define CDS_TRAY_OPEN 2
+
+#define FLAG_CLOSE 1
+#define FLAG_SMART 2
+
+int eject_main(int argc, char **argv);
+int eject_main(int argc, char **argv)
+{
+ unsigned long flags;
+ const char *device;
+ int dev, cmd;
+
+ opt_complementary = "?:?1:t--T:T--t";
+ flags = getopt32(argc, argv, "tT");
+ device = argv[optind] ? : "/dev/cdrom";
+
+ // We used to do "umount <device>" here, but it was buggy
+ // if something was mounted OVER cdrom and
+ // if cdrom is mounted many times.
+ //
+ // This works equally well (or better):
+ // #!/bin/sh
+ // umount /dev/cdrom
+ // eject
+
+ dev = xopen(device, O_RDONLY|O_NONBLOCK);
+ cmd = CDROMEJECT;
+ if (flags & FLAG_CLOSE
+ || (flags & FLAG_SMART && ioctl(dev, CDROM_DRIVE_STATUS) == CDS_TRAY_OPEN))
+ cmd = CDROMCLOSETRAY;
+ if (ioctl(dev, cmd)) {
+ bb_perror_msg_and_die("%s", device);
+ }
+
+ if (ENABLE_FEATURE_CLEAN_UP)
+ close(dev);
+
+ return EXIT_SUCCESS;
+}
diff --git a/i/pc104/initrd/conf/busybox/miscutils/hdparm.c b/i/pc104/initrd/conf/busybox/miscutils/hdparm.c
new file mode 100644
index 0000000..0138eed
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/miscutils/hdparm.c
@@ -0,0 +1,2048 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * hdparm implementation for busybox
+ *
+ * Copyright (C) [2003] by [Matteo Croce] <3297627799@wind.it>
+ * Hacked by Tito <farmatito@tiscali.it> for size optimization.
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ *
+ * This program is based on the source code of hdparm: see below...
+ * hdparm.c - Command line interface to get/set hard disk parameters
+ * - by Mark Lord (C) 1994-2002 -- freely distributable
+ */
+
+#include "busybox.h"
+#include <linux/hdreg.h>
+
+/* device types */
+/* ------------ */
+#define NO_DEV 0xffff
+#define ATA_DEV 0x0000
+#define ATAPI_DEV 0x0001
+
+/* word definitions */
+/* ---------------- */
+#define GEN_CONFIG 0 /* general configuration */
+#define LCYLS 1 /* number of logical cylinders */
+#define CONFIG 2 /* specific configuration */
+#define LHEADS 3 /* number of logical heads */
+#define TRACK_BYTES 4 /* number of bytes/track (ATA-1) */
+#define SECT_BYTES 5 /* number of bytes/sector (ATA-1) */
+#define LSECTS 6 /* number of logical sectors/track */
+#define START_SERIAL 10 /* ASCII serial number */
+#define LENGTH_SERIAL 10 /* 10 words (20 bytes or characters) */
+#define BUF_TYPE 20 /* buffer type (ATA-1) */
+#define BUFFER__SIZE 21 /* buffer size (ATA-1) */
+#define RW_LONG 22 /* extra bytes in R/W LONG cmd ( < ATA-4)*/
+#define START_FW_REV 23 /* ASCII firmware revision */
+#define LENGTH_FW_REV 4 /* 4 words (8 bytes or characters) */
+#define START_MODEL 27 /* ASCII model number */
+#define LENGTH_MODEL 20 /* 20 words (40 bytes or characters) */
+#define SECTOR_XFER_MAX 47 /* r/w multiple: max sectors xfered */
+#define DWORD_IO 48 /* can do double-word IO (ATA-1 only) */
+#define CAPAB_0 49 /* capabilities */
+#define CAPAB_1 50
+#define PIO_MODE 51 /* max PIO mode supported (obsolete)*/
+#define DMA_MODE 52 /* max Singleword DMA mode supported (obs)*/
+#define WHATS_VALID 53 /* what fields are valid */
+#define LCYLS_CUR 54 /* current logical cylinders */
+#define LHEADS_CUR 55 /* current logical heads */
+#define LSECTS_CUR 56 /* current logical sectors/track */
+#define CAPACITY_LSB 57 /* current capacity in sectors */
+#define CAPACITY_MSB 58
+#define SECTOR_XFER_CUR 59 /* r/w multiple: current sectors xfered */
+#define LBA_SECTS_LSB 60 /* LBA: total number of user */
+#define LBA_SECTS_MSB 61 /* addressable sectors */
+#define SINGLE_DMA 62 /* singleword DMA modes */
+#define MULTI_DMA 63 /* multiword DMA modes */
+#define ADV_PIO_MODES 64 /* advanced PIO modes supported */
+ /* multiword DMA xfer cycle time: */
+#define DMA_TIME_MIN 65 /* - minimum */
+#define DMA_TIME_NORM 66 /* - manufacturer's recommended */
+ /* minimum PIO xfer cycle time: */
+#define PIO_NO_FLOW 67 /* - without flow control */
+#define PIO_FLOW 68 /* - with IORDY flow control */
+#define PKT_REL 71 /* typical #ns from PKT cmd to bus rel */
+#define SVC_NBSY 72 /* typical #ns from SERVICE cmd to !BSY */
+#define CDR_MAJOR 73 /* CD ROM: major version number */
+#define CDR_MINOR 74 /* CD ROM: minor version number */
+#define QUEUE_DEPTH 75 /* queue depth */
+#define MAJOR 80 /* major version number */
+#define MINOR 81 /* minor version number */
+#define CMDS_SUPP_0 82 /* command/feature set(s) supported */
+#define CMDS_SUPP_1 83
+#define CMDS_SUPP_2 84
+#define CMDS_EN_0 85 /* command/feature set(s) enabled */
+#define CMDS_EN_1 86
+#define CMDS_EN_2 87
+#define ULTRA_DMA 88 /* ultra DMA modes */
+ /* time to complete security erase */
+#define ERASE_TIME 89 /* - ordinary */
+#define ENH_ERASE_TIME 90 /* - enhanced */
+#define ADV_PWR 91 /* current advanced power management level
+ in low byte, 0x40 in high byte. */
+#define PSWD_CODE 92 /* master password revision code */
+#define HWRST_RSLT 93 /* hardware reset result */
+#define ACOUSTIC 94 /* acoustic mgmt values ( >= ATA-6) */
+#define LBA_LSB 100 /* LBA: maximum. Currently only 48 */
+#define LBA_MID 101 /* bits are used, but addr 103 */
+#define LBA_48_MSB 102 /* has been reserved for LBA in */
+#define LBA_64_MSB 103 /* the future. */
+#define RM_STAT 127 /* removable media status notification feature set support */
+#define SECU_STATUS 128 /* security status */
+#define CFA_PWR_MODE 160 /* CFA power mode 1 */
+#define START_MEDIA 176 /* media serial number */
+#define LENGTH_MEDIA 20 /* 20 words (40 bytes or characters)*/
+#define START_MANUF 196 /* media manufacturer I.D. */
+#define LENGTH_MANUF 10 /* 10 words (20 bytes or characters) */
+#define INTEGRITY 255 /* integrity word */
+
+/* bit definitions within the words */
+/* -------------------------------- */
+
+/* many words are considered valid if bit 15 is 0 and bit 14 is 1 */
+#define VALID 0xc000
+#define VALID_VAL 0x4000
+/* many words are considered invalid if they are either all-0 or all-1 */
+#define NOVAL_0 0x0000
+#define NOVAL_1 0xffff
+
+/* word 0: gen_config */
+#define NOT_ATA 0x8000
+#define NOT_ATAPI 0x4000 /* (check only if bit 15 == 1) */
+#define MEDIA_REMOVABLE 0x0080
+#define DRIVE_NOT_REMOVABLE 0x0040 /* bit obsoleted in ATA 6 */
+#define INCOMPLETE 0x0004
+#define CFA_SUPPORT_VAL 0x848a /* 848a=CFA feature set support */
+#define DRQ_RESPONSE_TIME 0x0060
+#define DRQ_3MS_VAL 0x0000
+#define DRQ_INTR_VAL 0x0020
+#define DRQ_50US_VAL 0x0040
+#define PKT_SIZE_SUPPORTED 0x0003
+#define PKT_SIZE_12_VAL 0x0000
+#define PKT_SIZE_16_VAL 0x0001
+#define EQPT_TYPE 0x1f00
+#define SHIFT_EQPT 8
+
+#define CDROM 0x0005
+
+#if ENABLE_FEATURE_HDPARM_GET_IDENTITY
+static const char * const pkt_str[] = {
+ "Direct-access device", /* word 0, bits 12-8 = 00 */
+ "Sequential-access device", /* word 0, bits 12-8 = 01 */
+ "Printer", /* word 0, bits 12-8 = 02 */
+ "Processor", /* word 0, bits 12-8 = 03 */
+ "Write-once device", /* word 0, bits 12-8 = 04 */
+ "CD-ROM", /* word 0, bits 12-8 = 05 */
+ "Scanner", /* word 0, bits 12-8 = 06 */
+ "Optical memory", /* word 0, bits 12-8 = 07 */
+ "Medium changer", /* word 0, bits 12-8 = 08 */
+ "Communications device", /* word 0, bits 12-8 = 09 */
+ "ACS-IT8 device", /* word 0, bits 12-8 = 0a */
+ "ACS-IT8 device", /* word 0, bits 12-8 = 0b */
+ "Array controller", /* word 0, bits 12-8 = 0c */
+ "Enclosure services", /* word 0, bits 12-8 = 0d */
+ "Reduced block command device", /* word 0, bits 12-8 = 0e */
+ "Optical card reader/writer", /* word 0, bits 12-8 = 0f */
+ "", /* word 0, bits 12-8 = 10 */
+ "", /* word 0, bits 12-8 = 11 */
+ "", /* word 0, bits 12-8 = 12 */
+ "", /* word 0, bits 12-8 = 13 */
+ "", /* word 0, bits 12-8 = 14 */
+ "", /* word 0, bits 12-8 = 15 */
+ "", /* word 0, bits 12-8 = 16 */
+ "", /* word 0, bits 12-8 = 17 */
+ "", /* word 0, bits 12-8 = 18 */
+ "", /* word 0, bits 12-8 = 19 */
+ "", /* word 0, bits 12-8 = 1a */
+ "", /* word 0, bits 12-8 = 1b */
+ "", /* word 0, bits 12-8 = 1c */
+ "", /* word 0, bits 12-8 = 1d */
+ "", /* word 0, bits 12-8 = 1e */
+ "Unknown", /* word 0, bits 12-8 = 1f */
+};
+
+static const char * const ata1_cfg_str[] = { /* word 0 in ATA-1 mode */
+ "Reserved", /* bit 0 */
+ "hard sectored", /* bit 1 */
+ "soft sectored", /* bit 2 */
+ "not MFM encoded ", /* bit 3 */
+ "head switch time > 15us", /* bit 4 */
+ "spindle motor control option", /* bit 5 */
+ "fixed drive", /* bit 6 */
+ "removable drive", /* bit 7 */
+ "disk xfer rate <= 5Mbs", /* bit 8 */
+ "disk xfer rate > 5Mbs, <= 10Mbs", /* bit 9 */
+ "disk xfer rate > 5Mbs", /* bit 10 */
+ "rotational speed tol.", /* bit 11 */
+ "data strobe offset option", /* bit 12 */
+ "track offset option", /* bit 13 */
+ "format speed tolerance gap reqd", /* bit 14 */
+ "ATAPI" /* bit 14 */
+};
+#endif
+
+/* word 1: number of logical cylinders */
+#define LCYLS_MAX 0x3fff /* maximum allowable value */
+
+/* word 2: specific configuration
+ * (a) require SET FEATURES to spin-up
+ * (b) require spin-up to fully reply to IDENTIFY DEVICE
+ */
+#define STBY_NID_VAL 0x37c8 /* (a) and (b) */
+#define STBY_ID_VAL 0x738c /* (a) and not (b) */
+#define PWRD_NID_VAL 0x8c73 /* not (a) and (b) */
+#define PWRD_ID_VAL 0xc837 /* not (a) and not (b) */
+
+/* words 47 & 59: sector_xfer_max & sector_xfer_cur */
+#define SECTOR_XFER 0x00ff /* sectors xfered on r/w multiple cmds*/
+#define MULTIPLE_SETTING_VALID 0x0100 /* 1=multiple sector setting is valid */
+
+/* word 49: capabilities 0 */
+#define STD_STBY 0x2000 /* 1=standard values supported (ATA);
+ 0=vendor specific values */
+#define IORDY_SUP 0x0800 /* 1=support; 0=may be supported */
+#define IORDY_OFF 0x0400 /* 1=may be disabled */
+#define LBA_SUP 0x0200 /* 1=Logical Block Address support */
+#define DMA_SUP 0x0100 /* 1=Direct Memory Access support */
+#define DMA_IL_SUP 0x8000 /* 1=interleaved DMA support (ATAPI) */
+#define CMD_Q_SUP 0x4000 /* 1=command queuing support (ATAPI) */
+#define OVLP_SUP 0x2000 /* 1=overlap operation support (ATAPI) */
+#define SWRST_REQ 0x1000 /* 1=ATA SW reset required (ATAPI, obsolete */
+
+/* word 50: capabilities 1 */
+#define MIN_STANDBY_TIMER 0x0001 /* 1=device specific standby timer value minimum */
+
+/* words 51 & 52: PIO & DMA cycle times */
+#define MODE 0xff00 /* the mode is in the MSBs */
+
+/* word 53: whats_valid */
+#define OK_W88 0x0004 /* the ultra_dma info is valid */
+#define OK_W64_70 0x0002 /* see above for word descriptions */
+#define OK_W54_58 0x0001 /* current cyl, head, sector, cap. info valid */
+
+/*word 63,88: dma_mode, ultra_dma_mode*/
+#define MODE_MAX 7 /* bit definitions force udma <=7 (when
+ * udma >=8 comes out it'll have to be
+ * defined in a new dma_mode word!) */
+
+/* word 64: PIO transfer modes */
+#define PIO_SUP 0x00ff /* only bits 0 & 1 are used so far, */
+#define PIO_MODE_MAX 8 /* but all 8 bits are defined */
+
+/* word 75: queue_depth */
+#define DEPTH_BITS 0x001f /* bits used for queue depth */
+
+/* words 80-81: version numbers */
+/* NOVAL_0 or NOVAL_1 means device does not report version */
+
+/* word 81: minor version number */
+#define MINOR_MAX 0x22
+#if ENABLE_FEATURE_HDPARM_GET_IDENTITY
+static const char *minor_str[MINOR_MAX+2] = { /* word 81 value: */
+ "Unspecified", /* 0x0000 */
+ "ATA-1 X3T9.2 781D prior to rev.4", /* 0x0001 */
+ "ATA-1 published, ANSI X3.221-1994", /* 0x0002 */
+ "ATA-1 X3T9.2 781D rev.4", /* 0x0003 */
+ "ATA-2 published, ANSI X3.279-1996", /* 0x0004 */
+ "ATA-2 X3T10 948D prior to rev.2k", /* 0x0005 */
+ "ATA-3 X3T10 2008D rev.1", /* 0x0006 */
+ "ATA-2 X3T10 948D rev.2k", /* 0x0007 */
+ "ATA-3 X3T10 2008D rev.0", /* 0x0008 */
+ "ATA-2 X3T10 948D rev.3", /* 0x0009 */
+ "ATA-3 published, ANSI X3.298-199x", /* 0x000a */
+ "ATA-3 X3T10 2008D rev.6", /* 0x000b */
+ "ATA-3 X3T13 2008D rev.7 and 7a", /* 0x000c */
+ "ATA/ATAPI-4 X3T13 1153D rev.6", /* 0x000d */
+ "ATA/ATAPI-4 T13 1153D rev.13", /* 0x000e */
+ "ATA/ATAPI-4 X3T13 1153D rev.7", /* 0x000f */
+ "ATA/ATAPI-4 T13 1153D rev.18", /* 0x0010 */
+ "ATA/ATAPI-4 T13 1153D rev.15", /* 0x0011 */
+ "ATA/ATAPI-4 published, ANSI INCITS 317-1998", /* 0x0012 */
+ "ATA/ATAPI-5 T13 1321D rev.3",
+ "ATA/ATAPI-4 T13 1153D rev.14", /* 0x0014 */
+ "ATA/ATAPI-5 T13 1321D rev.1", /* 0x0015 */
+ "ATA/ATAPI-5 published, ANSI INCITS 340-2000", /* 0x0016 */
+ "ATA/ATAPI-4 T13 1153D rev.17", /* 0x0017 */
+ "ATA/ATAPI-6 T13 1410D rev.0", /* 0x0018 */
+ "ATA/ATAPI-6 T13 1410D rev.3a", /* 0x0019 */
+ "ATA/ATAPI-7 T13 1532D rev.1", /* 0x001a */
+ "ATA/ATAPI-6 T13 1410D rev.2", /* 0x001b */
+ "ATA/ATAPI-6 T13 1410D rev.1", /* 0x001c */
+ "ATA/ATAPI-7 published, ANSI INCITS 397-2005", /* 0x001d */
+ "ATA/ATAPI-7 T13 1532D rev.0", /* 0x001e */
+ "Reserved" /* 0x001f */
+ "Reserved" /* 0x0020 */
+ "ATA/ATAPI-7 T13 1532D rev.4a", /* 0x0021 */
+ "ATA/ATAPI-6 published, ANSI INCITS 361-2002", /* 0x0022 */
+ "Reserved" /* 0x0023-0xfffe*/
+};
+#endif
+static const char actual_ver[MINOR_MAX+2] = {
+ /* word 81 value: */
+ 0, /* 0x0000 WARNING: */
+ 1, /* 0x0001 WARNING: */
+ 1, /* 0x0002 WARNING: */
+ 1, /* 0x0003 WARNING: */
+ 2, /* 0x0004 WARNING: This array */
+ 2, /* 0x0005 WARNING: corresponds */
+ 3, /* 0x0006 WARNING: *exactly* */
+ 2, /* 0x0007 WARNING: to the ATA/ */
+ 3, /* 0x0008 WARNING: ATAPI version */
+ 2, /* 0x0009 WARNING: listed in */
+ 3, /* 0x000a WARNING: the */
+ 3, /* 0x000b WARNING: minor_str */
+ 3, /* 0x000c WARNING: array */
+ 4, /* 0x000d WARNING: above. */
+ 4, /* 0x000e WARNING: */
+ 4, /* 0x000f WARNING: if you change */
+ 4, /* 0x0010 WARNING: that one, */
+ 4, /* 0x0011 WARNING: change this one */
+ 4, /* 0x0012 WARNING: too!!! */
+ 5, /* 0x0013 WARNING: */
+ 4, /* 0x0014 WARNING: */
+ 5, /* 0x0015 WARNING: */
+ 5, /* 0x0016 WARNING: */
+ 4, /* 0x0017 WARNING: */
+ 6, /* 0x0018 WARNING: */
+ 6, /* 0x0019 WARNING: */
+ 7, /* 0x001a WARNING: */
+ 6, /* 0x001b WARNING: */
+ 6, /* 0x001c WARNING: */
+ 7, /* 0x001d WARNING: */
+ 7, /* 0x001e WARNING: */
+ 0, /* 0x001f WARNING: */
+ 0, /* 0x0020 WARNING: */
+ 7, /* 0x0021 WARNING: */
+ 6, /* 0x0022 WARNING: */
+ 0 /* 0x0023-0xfffe */
+};
+
+/* words 82-84: cmds/feats supported */
+#define CMDS_W82 0x77ff /* word 82: defined command locations*/
+#define CMDS_W83 0x3fff /* word 83: defined command locations*/
+#define CMDS_W84 0x002f /* word 83: defined command locations*/
+#define SUPPORT_48_BIT 0x0400
+#define NUM_CMD_FEAT_STR 48
+
+#if ENABLE_FEATURE_HDPARM_GET_IDENTITY
+static const char * const cmd_feat_str[] = {
+ "", /* word 82 bit 15: obsolete */
+ "NOP cmd", /* word 82 bit 14 */
+ "READ BUFFER cmd", /* word 82 bit 13 */
+ "WRITE BUFFER cmd", /* word 82 bit 12 */
+ "", /* word 82 bit 11: obsolete */
+ "Host Protected Area feature set", /* word 82 bit 10 */
+ "DEVICE RESET cmd", /* word 82 bit 9 */
+ "SERVICE interrupt", /* word 82 bit 8 */
+ "Release interrupt", /* word 82 bit 7 */
+ "Look-ahead", /* word 82 bit 6 */
+ "Write cache", /* word 82 bit 5 */
+ "PACKET command feature set", /* word 82 bit 4 */
+ "Power Management feature set", /* word 82 bit 3 */
+ "Removable Media feature set", /* word 82 bit 2 */
+ "Security Mode feature set", /* word 82 bit 1 */
+ "SMART feature set", /* word 82 bit 0 */
+ /* --------------*/
+ "", /* word 83 bit 15: !valid bit */
+ "", /* word 83 bit 14: valid bit */
+ "FLUSH CACHE EXT cmd", /* word 83 bit 13 */
+ "Mandatory FLUSH CACHE cmd ", /* word 83 bit 12 */
+ "Device Configuration Overlay feature set ",
+ "48-bit Address feature set ", /* word 83 bit 10 */
+ "",
+ "SET MAX security extension", /* word 83 bit 8 */
+ "Address Offset Reserved Area Boot", /* word 83 bit 7 */
+ "SET FEATURES subcommand required to spinup after power up",
+ "Power-Up In Standby feature set", /* word 83 bit 5 */
+ "Removable Media Status Notification feature set",
+ "Adv. Power Management feature set",/* word 83 bit 3 */
+ "CFA feature set", /* word 83 bit 2 */
+ "READ/WRITE DMA QUEUED", /* word 83 bit 1 */
+ "DOWNLOAD MICROCODE cmd", /* word 83 bit 0 */
+ /* --------------*/
+ "", /* word 84 bit 15: !valid bit */
+ "", /* word 84 bit 14: valid bit */
+ "", /* word 84 bit 13: reserved */
+ "", /* word 84 bit 12: reserved */
+ "", /* word 84 bit 11: reserved */
+ "", /* word 84 bit 10: reserved */
+ "", /* word 84 bit 9: reserved */
+ "", /* word 84 bit 8: reserved */
+ "", /* word 84 bit 7: reserved */
+ "", /* word 84 bit 6: reserved */
+ "General Purpose Logging feature set", /* word 84 bit 5 */
+ "", /* word 84 bit 4: reserved */
+ "Media Card Pass Through Command feature set ",
+ "Media serial number ", /* word 84 bit 2 */
+ "SMART self-test ", /* word 84 bit 1 */
+ "SMART error logging " /* word 84 bit 0 */
+};
+
+static void identify(uint16_t *id_supplied) ATTRIBUTE_NORETURN;
+static void identify_from_stdin(void) ATTRIBUTE_NORETURN;
+#else
+void identify_from_stdin(void);
+#endif
+
+
+/* words 85-87: cmds/feats enabled */
+/* use cmd_feat_str[] to display what commands and features have
+ * been enabled with words 85-87
+ */
+
+/* words 89, 90, SECU ERASE TIME */
+#define ERASE_BITS 0x00ff
+
+/* word 92: master password revision */
+/* NOVAL_0 or NOVAL_1 means no support for master password revision */
+
+/* word 93: hw reset result */
+#define CBLID 0x2000 /* CBLID status */
+#define RST0 0x0001 /* 1=reset to device #0 */
+#define DEV_DET 0x0006 /* how device num determined */
+#define JUMPER_VAL 0x0002 /* device num determined by jumper */
+#define CSEL_VAL 0x0004 /* device num determined by CSEL_VAL */
+
+/* word 127: removable media status notification feature set support */
+#define RM_STAT_BITS 0x0003
+#define RM_STAT_SUP 0x0001
+
+/* word 128: security */
+#define SECU_ENABLED 0x0002
+#define SECU_LEVEL 0x0010
+#define NUM_SECU_STR 6
+#if ENABLE_FEATURE_HDPARM_GET_IDENTITY
+static const char * const secu_str[] = {
+ "supported", /* word 128, bit 0 */
+ "enabled", /* word 128, bit 1 */
+ "locked", /* word 128, bit 2 */
+ "frozen", /* word 128, bit 3 */
+ "expired: security count", /* word 128, bit 4 */
+ "supported: enhanced erase" /* word 128, bit 5 */
+};
+#endif
+
+/* word 160: CFA power mode */
+#define VALID_W160 0x8000 /* 1=word valid */
+#define PWR_MODE_REQ 0x2000 /* 1=CFA power mode req'd by some cmds*/
+#define PWR_MODE_OFF 0x1000 /* 1=CFA power moded disabled */
+#define MAX_AMPS 0x0fff /* value = max current in ma */
+
+/* word 255: integrity */
+#define SIG 0x00ff /* signature location */
+#define SIG_VAL 0x00A5 /* signature value */
+
+#define TIMING_MB 64
+#define TIMING_BUF_MB 1
+#define TIMING_BUF_BYTES (TIMING_BUF_MB * 1024 * 1024)
+#define TIMING_BUF_COUNT (timing_MB / TIMING_BUF_MB)
+#define BUFCACHE_FACTOR 2
+
+#undef DO_FLUSHCACHE /* under construction: force cache flush on -W0 */
+
+/* Busybox messages and functions */
+static int bb_ioctl(int fd, int request, void *argp, const char *string)
+{
+ int e = ioctl(fd, request, argp);
+ if (e && string)
+ bb_perror_msg(" %s", string);
+ return e;
+}
+
+static int bb_ioctl_alt(int fd, int cmd, unsigned char *args, int alt, const char *string)
+{
+ if (!ioctl(fd, cmd, args))
+ return 0;
+ args[0] = alt;
+ return bb_ioctl(fd, cmd, args, string);
+}
+
+static void on_off(unsigned int value);
+
+static void print_flag_on_off(unsigned long get_arg, const char *s, unsigned long arg)
+{
+ if (get_arg) {
+ printf(" setting %s to %ld", s, arg);
+ on_off(arg);
+ }
+}
+
+static void bb_ioctl_on_off(int fd, int request, void *argp, const char *string,
+ const char * str)
+{
+ if (ioctl(fd, request, &argp) != 0)
+ bb_perror_msg(" %s", string);
+ else {
+ printf(" %s\t= %2ld", str, (unsigned long) argp);
+ on_off((unsigned long) argp);
+ }
+}
+
+#if ENABLE_FEATURE_HDPARM_GET_IDENTITY
+static void print_ascii(uint16_t *p, uint8_t length);
+
+static void xprint_ascii(uint16_t *val ,int i, const char *string, int n)
+{
+ if (val[i]) {
+ printf("\t%-20s", string);
+ print_ascii(&val[i], n);
+ }
+}
+#endif
+/* end of busybox specific stuff */
+
+#if ENABLE_FEATURE_HDPARM_GET_IDENTITY
+static uint8_t mode_loop(uint16_t mode_sup, uint16_t mode_sel, int cc, uint8_t *have_mode)
+{
+ uint16_t ii;
+ uint8_t err_dma = 0;
+
+ for (ii = 0; ii <= MODE_MAX; ii++) {
+ if (mode_sel & 0x0001) {
+ printf("*%cdma%u ",cc,ii);
+ if (*have_mode)
+ err_dma = 1;
+ *have_mode = 1;
+ } else if (mode_sup & 0x0001)
+ printf("%cdma%u ",cc,ii);
+
+ mode_sup >>= 1;
+ mode_sel >>= 1;
+ }
+ return err_dma;
+}
+
+static void print_ascii(uint16_t *p, uint8_t length) {
+ uint8_t ii;
+ char cl;
+
+ /* find first non-space & print it */
+ for (ii = 0; ii < length; ii++) {
+ if ((char)((*p)>>8) != ' ')
+ break;
+ cl = (char)(*p);
+ if (cl != ' ') {
+ if (cl != '\0')
+ printf("%c", cl);
+ p++;
+ ii++;
+ break;
+ }
+ p++;
+ }
+ /* print the rest */
+ for (; ii< length; ii++) {
+ if (!(*p))
+ break; /* some older devices have NULLs */
+ printf("%c%c", (char)((*p)>>8), (char)(*p));
+ p++;
+ }
+ puts("");
+}
+
+// Parse 512 byte disk identification block and print much crap.
+
+static void identify(uint16_t *id_supplied)
+{
+ uint16_t buf[256];
+ uint16_t *val, ii, jj, kk;
+ uint16_t like_std = 1, std = 0, min_std = 0xffff;
+ uint16_t dev = NO_DEV, eqpt = NO_DEV;
+ uint8_t have_mode = 0, err_dma = 0;
+ uint8_t chksum = 0;
+ uint32_t ll, mm, nn, oo;
+ uint64_t bbbig; /* (:) */
+ const char *strng;
+
+ // Adjust for endianness if necessary.
+
+ if (BB_BIG_ENDIAN) {
+ swab(id_supplied, buf, sizeof(buf));
+ val = buf;
+ } else
+ val = id_supplied;
+
+ chksum &= 0xff;
+
+ /* check if we recognise the device type */
+ puts("");
+ if (!(val[GEN_CONFIG] & NOT_ATA)) {
+ dev = ATA_DEV;
+ printf("ATA device, with ");
+ } else if (val[GEN_CONFIG]==CFA_SUPPORT_VAL) {
+ dev = ATA_DEV;
+ like_std = 4;
+ printf("CompactFlash ATA device, with ");
+ } else if (!(val[GEN_CONFIG] & NOT_ATAPI)) {
+ dev = ATAPI_DEV;
+ eqpt = (val[GEN_CONFIG] & EQPT_TYPE) >> SHIFT_EQPT;
+ printf("ATAPI %s, with ", pkt_str[eqpt]);
+ like_std = 3;
+ } else
+ /*"Unknown device type:\n\tbits 15&14 of general configuration word 0 both set to 1.\n"*/
+ bb_error_msg_and_die("unknown device type");
+
+ printf("%sremovable media\n", !(val[GEN_CONFIG] & MEDIA_REMOVABLE) ? "non-" : "");
+ /* Info from the specific configuration word says whether or not the
+ * ID command completed correctly. It is only defined, however in
+ * ATA/ATAPI-5 & 6; it is reserved (value theoretically 0) in prior
+ * standards. Since the values allowed for this word are extremely
+ * specific, it should be safe to check it now, even though we don't
+ * know yet what standard this device is using.
+ */
+ if ((val[CONFIG]==STBY_NID_VAL) || (val[CONFIG]==STBY_ID_VAL)
+ || (val[CONFIG]==PWRD_NID_VAL) || (val[CONFIG]==PWRD_ID_VAL)
+ ) {
+ like_std = 5;
+ if ((val[CONFIG]==STBY_NID_VAL) || (val[CONFIG]==STBY_ID_VAL))
+ printf("powers-up in standby; SET FEATURES subcmd spins-up.\n");
+ if (((val[CONFIG]==STBY_NID_VAL) || (val[CONFIG]==PWRD_NID_VAL)) && (val[GEN_CONFIG] & INCOMPLETE))
+ printf("\n\tWARNING: ID response incomplete.\n\tFollowing data may be incorrect.\n\n");
+ }
+
+ /* output the model and serial numbers and the fw revision */
+ xprint_ascii(val, START_MODEL, "Model Number:", LENGTH_MODEL);
+ xprint_ascii(val, START_SERIAL, "Serial Number:", LENGTH_SERIAL);
+ xprint_ascii(val, START_FW_REV, "Firmware Revision:", LENGTH_FW_REV);
+ xprint_ascii(val, START_MEDIA, "Media Serial Num:", LENGTH_MEDIA);
+ xprint_ascii(val, START_MANUF, "Media Manufacturer:", LENGTH_MANUF);
+
+ /* major & minor standards version number (Note: these words were not
+ * defined until ATA-3 & the CDROM std uses different words.) */
+ printf("Standards:");
+ if (eqpt != CDROM) {
+ if (val[MINOR] && (val[MINOR] <= MINOR_MAX)) {
+ if (like_std < 3) like_std = 3;
+ std = actual_ver[val[MINOR]];
+ if (std) printf("\n\tUsed: %s ",minor_str[val[MINOR]]);
+
+ }
+ /* looks like when they up-issue the std, they obsolete one;
+ * thus, only the newest 4 issues need be supported. (That's
+ * what "kk" and "min_std" are all about.) */
+ if (val[MAJOR] && (val[MAJOR] != NOVAL_1)) {
+ printf("\n\tSupported: ");
+ jj = val[MAJOR] << 1;
+ kk = like_std >4 ? like_std-4: 0;
+ for (ii = 14; (ii >0)&&(ii>kk); ii--) {
+ if (jj & 0x8000) {
+ printf("%u ", ii);
+ if (like_std < ii) {
+ like_std = ii;
+ kk = like_std >4 ? like_std-4: 0;
+ }
+ if (min_std > ii) min_std = ii;
+ }
+ jj <<= 1;
+ }
+ if (like_std < 3) like_std = 3;
+ }
+ /* Figure out what standard the device is using if it hasn't told
+ * us. If we know the std, check if the device is using any of
+ * the words from the next level up. It happens.
+ */
+ if (like_std < std) like_std = std;
+
+ if (((std == 5) || (!std && (like_std < 6))) &&
+ ((((val[CMDS_SUPP_1] & VALID) == VALID_VAL) &&
+ (( val[CMDS_SUPP_1] & CMDS_W83) > 0x00ff)) ||
+ ((( val[CMDS_SUPP_2] & VALID) == VALID_VAL) &&
+ ( val[CMDS_SUPP_2] & CMDS_W84) ) )
+ ) {
+ like_std = 6;
+ } else if (((std == 4) || (!std && (like_std < 5))) &&
+ ((((val[INTEGRITY] & SIG) == SIG_VAL) && !chksum) ||
+ (( val[HWRST_RSLT] & VALID) == VALID_VAL) ||
+ ((( val[CMDS_SUPP_1] & VALID) == VALID_VAL) &&
+ (( val[CMDS_SUPP_1] & CMDS_W83) > 0x001f)) ) )
+ {
+ like_std = 5;
+ } else if (((std == 3) || (!std && (like_std < 4))) &&
+ ((((val[CMDS_SUPP_1] & VALID) == VALID_VAL) &&
+ ((( val[CMDS_SUPP_1] & CMDS_W83) > 0x0000) ||
+ (( val[CMDS_SUPP_0] & CMDS_W82) > 0x000f))) ||
+ (( val[CAPAB_1] & VALID) == VALID_VAL) ||
+ (( val[WHATS_VALID] & OK_W88) && val[ULTRA_DMA]) ||
+ (( val[RM_STAT] & RM_STAT_BITS) == RM_STAT_SUP) )
+ ) {
+ like_std = 4;
+ } else if (((std == 2) || (!std && (like_std < 3)))
+ && ((val[CMDS_SUPP_1] & VALID) == VALID_VAL)
+ ) {
+ like_std = 3;
+ } else if (((std == 1) || (!std && (like_std < 2))) &&
+ ((val[CAPAB_0] & (IORDY_SUP | IORDY_OFF)) ||
+ (val[WHATS_VALID] & OK_W64_70)) )
+ {
+ like_std = 2;
+ }
+
+ if (!std)
+ printf("\n\tLikely used: %u\n", like_std);
+ else if (like_std > std)
+ printf("& some of %u\n", like_std);
+ else
+ puts("");
+ } else {
+ /* TBD: do CDROM stuff more thoroughly. For now... */
+ kk = 0;
+ if (val[CDR_MINOR] == 9) {
+ kk = 1;
+ printf("\n\tUsed: ATAPI for CD-ROMs, SFF-8020i, r2.5");
+ }
+ if (val[CDR_MAJOR] && (val[CDR_MAJOR] !=NOVAL_1)) {
+ kk = 1;
+ printf("\n\tSupported: CD-ROM ATAPI");
+ jj = val[CDR_MAJOR] >> 1;
+ for (ii = 1; ii < 15; ii++) {
+ if (jj & 0x0001) printf("-%u ", ii);
+ jj >>= 1;
+ }
+ }
+ printf("%s\n", (!kk) ? "\n\tLikely used CD-ROM ATAPI-1" : "" );
+ /* the cdrom stuff is more like ATA-2 than anything else, so: */
+ like_std = 2;
+ }
+
+ if (min_std == 0xffff)
+ min_std = like_std > 4 ? like_std - 3 : 1;
+
+ printf("Configuration:\n");
+ /* more info from the general configuration word */
+ if ((eqpt != CDROM) && (like_std == 1)) {
+ jj = val[GEN_CONFIG] >> 1;
+ for (ii = 1; ii < 15; ii++) {
+ if (jj & 0x0001) printf("\t%s\n",ata1_cfg_str[ii]);
+ jj >>=1;
+ }
+ }
+ if (dev == ATAPI_DEV) {
+ if ((val[GEN_CONFIG] & DRQ_RESPONSE_TIME) == DRQ_3MS_VAL)
+ strng = "3ms";
+ else if ((val[GEN_CONFIG] & DRQ_RESPONSE_TIME) == DRQ_INTR_VAL)
+ strng = "<=10ms with INTRQ";
+ else if ((val[GEN_CONFIG] & DRQ_RESPONSE_TIME) == DRQ_50US_VAL)
+ strng ="50us";
+ else
+ strng = "Unknown";
+ printf("\tDRQ response: %s\n\tPacket size: ", strng); /* Data Request (DRQ) */
+
+ if ((val[GEN_CONFIG] & PKT_SIZE_SUPPORTED) == PKT_SIZE_12_VAL)
+ strng = "12 bytes";
+ else if ((val[GEN_CONFIG] & PKT_SIZE_SUPPORTED) == PKT_SIZE_16_VAL)
+ strng = "16 bytes";
+ else
+ strng = "Unknown";
+ puts(strng);
+ } else {
+ /* addressing...CHS? See section 6.2 of ATA specs 4 or 5 */
+ ll = (uint32_t)val[LBA_SECTS_MSB] << 16 | val[LBA_SECTS_LSB];
+ mm = 0; bbbig = 0;
+ if ( (ll > 0x00FBFC10) && (!val[LCYLS]))
+ printf("\tCHS addressing not supported\n");
+ else {
+ jj = val[WHATS_VALID] & OK_W54_58;
+ printf("\tLogical\t\tmax\tcurrent\n\tcylinders\t%u\t%u\n\theads\t\t%u\t%u\n\tsectors/track\t%u\t%u\n\t--\n",
+ val[LCYLS],jj?val[LCYLS_CUR]:0, val[LHEADS],jj?val[LHEADS_CUR]:0, val[LSECTS],jj?val[LSECTS_CUR]:0);
+
+ if ((min_std == 1) && (val[TRACK_BYTES] || val[SECT_BYTES]))
+ printf("\tbytes/track: %u\tbytes/sector: %u\n",val[TRACK_BYTES], val[SECT_BYTES]);
+
+ if (jj) {
+ mm = (uint32_t)val[CAPACITY_MSB] << 16 | val[CAPACITY_LSB];
+ if (like_std < 3) {
+ /* check Endian of capacity bytes */
+ nn = val[LCYLS_CUR] * val[LHEADS_CUR] * val[LSECTS_CUR];
+ oo = (uint32_t)val[CAPACITY_LSB] << 16 | val[CAPACITY_MSB];
+ if (abs(mm - nn) > abs(oo - nn))
+ mm = oo;
+ }
+ printf("\tCHS current addressable sectors:%11u\n",mm);
+ }
+ }
+ /* LBA addressing */
+ printf("\tLBA user addressable sectors:%11u\n",ll);
+ if (((val[CMDS_SUPP_1] & VALID) == VALID_VAL)
+ && (val[CMDS_SUPP_1] & SUPPORT_48_BIT)
+ ) {
+ bbbig = (uint64_t)val[LBA_64_MSB] << 48 |
+ (uint64_t)val[LBA_48_MSB] << 32 |
+ (uint64_t)val[LBA_MID] << 16 |
+ val[LBA_LSB] ;
+ printf("\tLBA48 user addressable sectors:%11"PRIu64"\n",bbbig);
+ }
+
+ if (!bbbig)
+ bbbig = (uint64_t)(ll>mm ? ll : mm); /* # 512 byte blocks */
+ printf("\tdevice size with M = 1024*1024: %11"PRIu64" MBytes\n",bbbig>>11);
+ bbbig = (bbbig<<9)/1000000;
+ printf("\tdevice size with M = 1000*1000: %11"PRIu64" MBytes ",bbbig);
+
+ if (bbbig > 1000)
+ printf("(%"PRIu64" GB)\n", bbbig/1000);
+ else
+ puts("");
+ }
+
+ /* hw support of commands (capabilities) */
+ printf("Capabilities:\n\t");
+
+ if (dev == ATAPI_DEV) {
+ if (eqpt != CDROM && (val[CAPAB_0] & CMD_Q_SUP)) printf("Cmd queuing, ");
+ if (val[CAPAB_0] & OVLP_SUP) printf("Cmd overlap, ");
+ }
+ if (val[CAPAB_0] & LBA_SUP) printf("LBA, ");
+
+ if (like_std != 1) {
+ printf("IORDY%s(can%s be disabled)\n",
+ !(val[CAPAB_0] & IORDY_SUP) ? "(may be)" : "",
+ (val[CAPAB_0] & IORDY_OFF) ? "" :"not");
+ } else
+ printf("no IORDY\n");
+
+ if ((like_std == 1) && val[BUF_TYPE]) {
+ printf("\tBuffer type: %04x: %s%s\n", val[BUF_TYPE],
+ (val[BUF_TYPE] < 2) ? "single port, single-sector" : "dual port, multi-sector",
+ (val[BUF_TYPE] > 2) ? " with read caching ability" : "");
+ }
+
+ if ((min_std == 1) && (val[BUFFER__SIZE] && (val[BUFFER__SIZE] != NOVAL_1))) {
+ printf("\tBuffer size: %.1fkB\n",(float)val[BUFFER__SIZE]/2);
+ }
+ if ((min_std < 4) && (val[RW_LONG])) {
+ printf("\tbytes avail on r/w long: %u\n",val[RW_LONG]);
+ }
+ if ((eqpt != CDROM) && (like_std > 3)) {
+ printf("\tQueue depth: %u\n",(val[QUEUE_DEPTH] & DEPTH_BITS)+1);
+ }
+
+ if (dev == ATA_DEV) {
+ if (like_std == 1)
+ printf("\tCan%s perform double-word IO\n",(!val[DWORD_IO]) ?"not":"");
+ else {
+ printf("\tStandby timer values: spec'd by %s", (val[CAPAB_0] & STD_STBY) ? "Standard" : "Vendor");
+ if ((like_std > 3) && ((val[CAPAB_1] & VALID) == VALID_VAL))
+ printf(", %s device specific minimum\n",(val[CAPAB_1] & MIN_STANDBY_TIMER)?"with":"no");
+ else
+ puts("");
+ }
+ printf("\tR/W multiple sector transfer: ");
+ if ((like_std < 3) && !(val[SECTOR_XFER_MAX] & SECTOR_XFER))
+ printf("not supported\n");
+ else {
+ printf("Max = %u\tCurrent = ",val[SECTOR_XFER_MAX] & SECTOR_XFER);
+ if (val[SECTOR_XFER_CUR] & MULTIPLE_SETTING_VALID)
+ printf("%u\n", val[SECTOR_XFER_CUR] & SECTOR_XFER);
+ else
+ printf("?\n");
+ }
+ if ((like_std > 3) && (val[CMDS_SUPP_1] & 0x0008)) {
+ /* We print out elsewhere whether the APM feature is enabled or
+ not. If it's not enabled, let's not repeat the info; just print
+ nothing here. */
+ printf("\tAdvancedPM level: ");
+ if ((val[ADV_PWR] & 0xFF00) == 0x4000) {
+ uint8_t apm_level = val[ADV_PWR] & 0x00FF;
+ printf("%u (0x%x)\n", apm_level, apm_level);
+ }
+ else
+ printf("unknown setting (0x%04x)\n", val[ADV_PWR]);
+ }
+ if (like_std > 5 && val[ACOUSTIC]) {
+ printf("\tRecommended acoustic management value: %u, current value: %u\n",
+ (val[ACOUSTIC] >> 8) & 0x00ff, val[ACOUSTIC] & 0x00ff);
+ }
+ } else {
+ /* ATAPI */
+ if (eqpt != CDROM && (val[CAPAB_0] & SWRST_REQ))
+ printf("\tATA sw reset required\n");
+
+ if (val[PKT_REL] || val[SVC_NBSY]) {
+ printf("\tOverlap support:");
+ if (val[PKT_REL]) printf(" %uus to release bus.",val[PKT_REL]);
+ if (val[SVC_NBSY]) printf(" %uus to clear BSY after SERVICE cmd.",val[SVC_NBSY]);
+ puts("");
+ }
+ }
+
+ /* DMA stuff. Check that only one DMA mode is selected. */
+ printf("\tDMA: ");
+ if (!(val[CAPAB_0] & DMA_SUP))
+ printf("not supported\n");
+ else {
+ if (val[DMA_MODE] && !val[SINGLE_DMA] && !val[MULTI_DMA])
+ printf(" sdma%u\n",(val[DMA_MODE] & MODE) >> 8);
+ if (val[SINGLE_DMA]) {
+ jj = val[SINGLE_DMA];
+ kk = val[SINGLE_DMA] >> 8;
+ err_dma += mode_loop(jj,kk,'s',&have_mode);
+ }
+ if (val[MULTI_DMA]) {
+ jj = val[MULTI_DMA];
+ kk = val[MULTI_DMA] >> 8;
+ err_dma += mode_loop(jj,kk,'m',&have_mode);
+ }
+ if ((val[WHATS_VALID] & OK_W88) && val[ULTRA_DMA]) {
+ jj = val[ULTRA_DMA];
+ kk = val[ULTRA_DMA] >> 8;
+ err_dma += mode_loop(jj,kk,'u',&have_mode);
+ }
+ if (err_dma || !have_mode) printf("(?)");
+ puts("");
+
+ if ((dev == ATAPI_DEV) && (eqpt != CDROM) && (val[CAPAB_0] & DMA_IL_SUP))
+ printf("\t\tInterleaved DMA support\n");
+
+ if ((val[WHATS_VALID] & OK_W64_70)
+ && (val[DMA_TIME_MIN] || val[DMA_TIME_NORM])
+ ) {
+ printf("\t\tCycle time:");
+ if (val[DMA_TIME_MIN]) printf(" min=%uns",val[DMA_TIME_MIN]);
+ if (val[DMA_TIME_NORM]) printf(" recommended=%uns",val[DMA_TIME_NORM]);
+ puts("");
+ }
+ }
+
+ /* Programmed IO stuff */
+ printf("\tPIO: ");
+ /* If a drive supports mode n (e.g. 3), it also supports all modes less
+ * than n (e.g. 3, 2, 1 and 0). Print all the modes. */
+ if ((val[WHATS_VALID] & OK_W64_70) && (val[ADV_PIO_MODES] & PIO_SUP)) {
+ jj = ((val[ADV_PIO_MODES] & PIO_SUP) << 3) | 0x0007;
+ for (ii = 0; ii <= PIO_MODE_MAX ; ii++) {
+ if (jj & 0x0001) printf("pio%d ",ii);
+ jj >>=1;
+ }
+ puts("");
+ } else if (((min_std < 5) || (eqpt == CDROM)) && (val[PIO_MODE] & MODE)) {
+ for (ii = 0; ii <= val[PIO_MODE]>>8; ii++)
+ printf("pio%d ",ii);
+ puts("");
+ } else
+ printf("unknown\n");
+
+ if (val[WHATS_VALID] & OK_W64_70) {
+ if (val[PIO_NO_FLOW] || val[PIO_FLOW]) {
+ printf("\t\tCycle time:");
+ if (val[PIO_NO_FLOW]) printf(" no flow control=%uns", val[PIO_NO_FLOW]);
+ if (val[PIO_FLOW]) printf(" IORDY flow control=%uns", val[PIO_FLOW]);
+ puts("");
+ }
+ }
+
+ if ((val[CMDS_SUPP_1] & VALID) == VALID_VAL) {
+ printf("Commands/features:\n\tEnabled\tSupported:\n");
+ jj = val[CMDS_SUPP_0];
+ kk = val[CMDS_EN_0];
+ for (ii = 0; ii < NUM_CMD_FEAT_STR; ii++) {
+ if ((jj & 0x8000) && (*cmd_feat_str[ii] != '\0')) {
+ printf("\t%s\t%s\n", (kk & 0x8000) ? " *" : "", cmd_feat_str[ii]);
+ }
+ jj <<= 1;
+ kk <<= 1;
+ if (ii % 16 == 15) {
+ jj = val[CMDS_SUPP_0+1+(ii/16)];
+ kk = val[CMDS_EN_0+1+(ii/16)];
+ }
+ if (ii == 31) {
+ if ((val[CMDS_SUPP_2] & VALID) != VALID_VAL)
+ ii +=16;
+ }
+ }
+ }
+ /* Removable Media Status Notification feature set */
+ if ((val[RM_STAT] & RM_STAT_BITS) == RM_STAT_SUP)
+ printf("\t%s supported\n", cmd_feat_str[27]);
+
+ /* security */
+ if ((eqpt != CDROM) && (like_std > 3)
+ && (val[SECU_STATUS] || val[ERASE_TIME] || val[ENH_ERASE_TIME])
+ ) {
+ printf("Security:\n");
+ if (val[PSWD_CODE] && (val[PSWD_CODE] != NOVAL_1))
+ printf("\tMaster password revision code = %u\n",val[PSWD_CODE]);
+ jj = val[SECU_STATUS];
+ if (jj) {
+ for (ii = 0; ii < NUM_SECU_STR; ii++) {
+ printf("\t%s\t%s\n", (!(jj & 0x0001)) ? "not" : "", secu_str[ii]);
+ jj >>=1;
+ }
+ if (val[SECU_STATUS] & SECU_ENABLED) {
+ printf("\tSecurity level %s\n", (val[SECU_STATUS] & SECU_LEVEL) ? "maximum" : "high");
+ }
+ }
+ jj = val[ERASE_TIME] & ERASE_BITS;
+ kk = val[ENH_ERASE_TIME] & ERASE_BITS;
+ if (jj || kk) {
+ printf("\t");
+ if (jj) printf("%umin for %sSECURITY ERASE UNIT. ", jj==ERASE_BITS ? 508 : jj<<1, "");
+ if (kk) printf("%umin for %sSECURITY ERASE UNIT. ", kk==ERASE_BITS ? 508 : kk<<1, "ENHANCED ");
+ puts("");
+ }
+ }
+
+ /* reset result */
+ jj = val[HWRST_RSLT];
+ if ((jj & VALID) == VALID_VAL) {
+ if (!(oo = (jj & RST0)))
+ jj >>= 8;
+ if ((jj & DEV_DET) == JUMPER_VAL)
+ strng = " determined by the jumper";
+ else if ((jj & DEV_DET) == CSEL_VAL)
+ strng = " determined by CSEL";
+ else
+ strng = "";
+ printf("HW reset results:\n\tCBLID- %s Vih\n\tDevice num = %i%s\n",
+ (val[HWRST_RSLT] & CBLID) ? "above" : "below", !(oo), strng);
+ }
+
+ /* more stuff from std 5 */
+ if ((like_std > 4) && (eqpt != CDROM)) {
+ if (val[CFA_PWR_MODE] & VALID_W160) {
+ printf("CFA power mode 1:\n\t%s%s\n", (val[CFA_PWR_MODE] & PWR_MODE_OFF) ? "disabled" : "enabled",
+ (val[CFA_PWR_MODE] & PWR_MODE_REQ) ? " and required by some commands" : "");
+
+ if (val[CFA_PWR_MODE] & MAX_AMPS)
+ printf("\tMaximum current = %uma\n",val[CFA_PWR_MODE] & MAX_AMPS);
+ }
+ if ((val[INTEGRITY] & SIG) == SIG_VAL) {
+ printf("Checksum: %scorrect\n", chksum ? "in" : "");
+ }
+ }
+
+ exit(EXIT_SUCCESS);
+}
+#endif
+
+static int get_identity, get_geom;
+static int do_flush;
+static int do_ctimings, do_timings;
+static unsigned long set_readahead, get_readahead, Xreadahead;
+static unsigned long set_readonly, get_readonly, readonly;
+static unsigned long set_unmask, get_unmask, unmask;
+static unsigned long set_mult, get_mult, mult;
+#if ENABLE_FEATURE_HDPARM_HDIO_GETSET_DMA
+static unsigned long set_dma, get_dma, dma;
+#endif
+static unsigned long set_dma_q, get_dma_q, dma_q;
+static unsigned long set_nowerr, get_nowerr, nowerr;
+static unsigned long set_keep, get_keep, keep;
+static unsigned long set_io32bit, get_io32bit, io32bit;
+static unsigned long set_piomode, noisy_piomode;
+static int piomode;
+#ifdef HDIO_DRIVE_CMD
+static unsigned long set_dkeep, get_dkeep, dkeep;
+static unsigned long set_standby, get_standby, standby_requested;
+static unsigned long set_xfermode, get_xfermode;
+static int xfermode_requested;
+static unsigned long set_lookahead, get_lookahead, lookahead;
+static unsigned long set_prefetch, get_prefetch, prefetch;
+static unsigned long set_defects, get_defects, defects;
+static unsigned long set_wcache, get_wcache, wcache;
+static unsigned long set_doorlock, get_doorlock, doorlock;
+static unsigned long set_seagate, get_seagate;
+static unsigned long set_standbynow, get_standbynow;
+static unsigned long set_sleepnow, get_sleepnow;
+static unsigned long get_powermode;
+static unsigned long set_apmmode, get_apmmode, apmmode;
+#endif
+#if ENABLE_FEATURE_HDPARM_GET_IDENTITY
+static int get_IDentity;
+#endif
+#if ENABLE_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF
+static unsigned long unregister_hwif;
+static unsigned long hwif;
+#endif
+#if ENABLE_FEATURE_HDPARM_HDIO_SCAN_HWIF
+static unsigned long scan_hwif;
+static unsigned long hwif_data;
+static unsigned long hwif_ctrl;
+static unsigned long hwif_irq;
+#endif
+#if ENABLE_FEATURE_HDPARM_HDIO_TRISTATE_HWIF
+static unsigned long set_busstate, get_busstate, busstate;
+#endif
+static int reread_partn;
+
+#if ENABLE_FEATURE_HDPARM_HDIO_DRIVE_RESET
+static int perform_reset;
+#endif /* FEATURE_HDPARM_HDIO_DRIVE_RESET */
+#if ENABLE_FEATURE_HDPARM_HDIO_TRISTATE_HWIF
+static unsigned long perform_tristate, tristate;
+#endif /* FEATURE_HDPARM_HDIO_TRISTATE_HWIF */
+
+// Historically, if there was no HDIO_OBSOLETE_IDENTITY, then
+// then the HDIO_GET_IDENTITY only returned 142 bytes.
+// Otherwise, HDIO_OBSOLETE_IDENTITY returns 142 bytes,
+// and HDIO_GET_IDENTITY returns 512 bytes. But the latest
+// 2.5.xx kernels no longer define HDIO_OBSOLETE_IDENTITY
+// (which they should, but they should just return -EINVAL).
+//
+// So.. we must now assume that HDIO_GET_IDENTITY returns 512 bytes.
+// On a really old system, it will not, and we will be confused.
+// Too bad, really.
+
+#if ENABLE_FEATURE_HDPARM_GET_IDENTITY
+static const char * const cfg_str[] =
+{ "", "HardSect", "SoftSect", "NotMFM",
+ "HdSw>15uSec", "SpinMotCtl", "Fixed", "Removeable",
+ "DTR<=5Mbs", "DTR>5Mbs", "DTR>10Mbs", "RotSpdTol>.5%",
+ "dStbOff", "TrkOff", "FmtGapReq", "nonMagnetic"
+};
+
+static const char * const BuffType[] = {"Unknown", "1Sect", "DualPort", "DualPortCache"};
+
+static void dump_identity(const struct hd_driveid *id)
+{
+ int i;
+ const unsigned short int *id_regs = (const void*) id;
+
+ printf("\n Model=%.40s, FwRev=%.8s, SerialNo=%.20s\n Config={",
+ id->model, id->fw_rev, id->serial_no);
+ for (i=0; i<=15; i++) {
+ if (id->config & (1<<i))
+ printf(" %s", cfg_str[i]);
+ }
+ printf( " }\n RawCHS=%u/%u/%u, TrkSize=%u, SectSize=%u, ECCbytes=%u\n"
+ " BuffType=(%u) %s, BuffSize=%ukB, MaxMultSect=%u",
+ id->cyls, id->heads, id->sectors, id->track_bytes,
+ id->sector_bytes, id->ecc_bytes,
+ id->buf_type, BuffType[(id->buf_type > 3) ? 0 : id->buf_type],
+ id->buf_size/2, id->max_multsect);
+ if (id->max_multsect) {
+ printf(", MultSect=");
+ if (!(id->multsect_valid & 1))
+ printf("?%u?", id->multsect);
+ else if (id->multsect)
+ printf("%u", id->multsect);
+ else
+ printf("off");
+ }
+ puts("");
+
+ if (!(id->field_valid & 1))
+ printf(" (maybe):");
+
+ printf(" CurCHS=%u/%u/%u, CurSects=%lu, LBA=%s",id->cur_cyls, id->cur_heads,
+ id->cur_sectors,
+ (BB_BIG_ENDIAN) ?
+ (long unsigned int)(id->cur_capacity0 << 16) | id->cur_capacity1 :
+ (long unsigned int)(id->cur_capacity1 << 16) | id->cur_capacity0,
+ ((id->capability&2) == 0) ? "no" : "yes");
+
+ if (id->capability & 2)
+ printf(", LBAsects=%u", id->lba_capacity);
+
+ printf("\n IORDY=%s", (id->capability & 8) ? (id->capability & 4) ? "on/off" : "yes" : "no");
+
+ if (((id->capability & 8) || (id->field_valid & 2)) && (id->field_valid & 2))
+ printf(", tPIO={min:%u,w/IORDY:%u}", id->eide_pio, id->eide_pio_iordy);
+
+ if ((id->capability & 1) && (id->field_valid & 2))
+ printf(", tDMA={min:%u,rec:%u}", id->eide_dma_min, id->eide_dma_time);
+
+ printf("\n PIO modes: ");
+ if (id->tPIO <= 5) {
+ printf("pio0 ");
+ if (id->tPIO >= 1) printf("pio1 ");
+ if (id->tPIO >= 2) printf("pio2 ");
+ }
+ if (id->field_valid & 2) {
+ if (id->eide_pio_modes & 1) printf("pio3 ");
+ if (id->eide_pio_modes & 2) printf("pio4 ");
+ if (id->eide_pio_modes &~3) printf("pio? ");
+ }
+ if (id->capability & 1) {
+ if (id->dma_1word | id->dma_mword) {
+ printf("\n DMA modes: ");
+ if (id->dma_1word & 0x100) printf("*");
+ if (id->dma_1word & 1) printf("sdma0 ");
+ if (id->dma_1word & 0x200) printf("*");
+ if (id->dma_1word & 2) printf("sdma1 ");
+ if (id->dma_1word & 0x400) printf("*");
+ if (id->dma_1word & 4) printf("sdma2 ");
+ if (id->dma_1word & 0xf800) printf("*");
+ if (id->dma_1word & 0xf8) printf("sdma? ");
+ if (id->dma_mword & 0x100) printf("*");
+ if (id->dma_mword & 1) printf("mdma0 ");
+ if (id->dma_mword & 0x200) printf("*");
+ if (id->dma_mword & 2) printf("mdma1 ");
+ if (id->dma_mword & 0x400) printf("*");
+ if (id->dma_mword & 4) printf("mdma2 ");
+ if (id->dma_mword & 0xf800) printf("*");
+ if (id->dma_mword & 0xf8) printf("mdma? ");
+ }
+ }
+ if (((id->capability & 8) || (id->field_valid & 2)) && id->field_valid & 4) {
+ printf("\n UDMA modes: ");
+ if (id->dma_ultra & 0x100) printf("*");
+ if (id->dma_ultra & 0x001) printf("udma0 ");
+ if (id->dma_ultra & 0x200) printf("*");
+ if (id->dma_ultra & 0x002) printf("udma1 ");
+ if (id->dma_ultra & 0x400) printf("*");
+ if (id->dma_ultra & 0x004) printf("udma2 ");
+#ifdef __NEW_HD_DRIVE_ID
+ if (id->hw_config & 0x2000) {
+#else /* !__NEW_HD_DRIVE_ID */
+ if (id->word93 & 0x2000) {
+#endif /* __NEW_HD_DRIVE_ID */
+ if (id->dma_ultra & 0x0800) printf("*");
+ if (id->dma_ultra & 0x0008) printf("udma3 ");
+ if (id->dma_ultra & 0x1000) printf("*");
+ if (id->dma_ultra & 0x0010) printf("udma4 ");
+ if (id->dma_ultra & 0x2000) printf("*");
+ if (id->dma_ultra & 0x0020) printf("udma5 ");
+ if (id->dma_ultra & 0x4000) printf("*");
+ if (id->dma_ultra & 0x0040) printf("udma6 ");
+ if (id->dma_ultra & 0x8000) printf("*");
+ if (id->dma_ultra & 0x0080) printf("udma7 ");
+ }
+ }
+ printf("\n AdvancedPM=%s",((id_regs[83]&8)==0)?"no":"yes");
+ if (id_regs[83] & 8) {
+ if (!(id_regs[86] & 8))
+ printf(": disabled (255)");
+ else if ((id_regs[91] & 0xFF00) != 0x4000)
+ printf(": unknown setting");
+ else
+ printf(": mode=0x%02X (%u)",id_regs[91]&0xFF,id_regs[91]&0xFF);
+ }
+ if (id_regs[82] & 0x20)
+ printf(" WriteCache=%s",(id_regs[85]&0x20) ? "enabled" : "disabled");
+#ifdef __NEW_HD_DRIVE_ID
+ if ((id->minor_rev_num && id->minor_rev_num <= 31)
+ || (id->major_rev_num && id->minor_rev_num <= 31)
+ ) {
+ printf("\n Drive conforms to: %s: ", (id->minor_rev_num <= 31) ? minor_str[id->minor_rev_num] : "Unknown");
+ if (id->major_rev_num != 0x0000 && /* NOVAL_0 */
+ id->major_rev_num != 0xFFFF) { /* NOVAL_1 */
+ for (i=0; i <= 15; i++) {
+ if (id->major_rev_num & (1<<i))
+ printf(" ATA/ATAPI-%u", i);
+ }
+ }
+ }
+#endif /* __NEW_HD_DRIVE_ID */
+ printf("\n\n * current active mode\n\n");
+}
+#endif
+
+static void flush_buffer_cache(int fd)
+{
+ fsync(fd); /* flush buffers */
+ bb_ioctl(fd, BLKFLSBUF, NULL,"BLKFLSBUF" ) ;/* do it again, big time */
+#ifdef HDIO_DRIVE_CMD
+ sleep(1);
+ if (ioctl(fd, HDIO_DRIVE_CMD, NULL) && errno != EINVAL) /* await completion */
+ bb_perror_msg("HDIO_DRIVE_CMD");
+#endif
+}
+
+static int seek_to_zero(int fd)
+{
+ if (lseek(fd, (off_t) 0, SEEK_SET))
+ return 1;
+ return 0;
+}
+
+static int read_big_block(int fd, char *buf)
+{
+ int i;
+
+ i = read(fd, buf, TIMING_BUF_BYTES);
+ if (i != TIMING_BUF_BYTES) {
+ bb_error_msg("read(%d bytes) failed (rc=%d)", TIMING_BUF_BYTES, i);
+ return 1;
+ }
+ /* access all sectors of buf to ensure the read fully completed */
+ for (i = 0; i < TIMING_BUF_BYTES; i += 512)
+ buf[i] &= 1;
+ return 0;
+}
+
+static void print_timing(int t, double e)
+{
+ if (t >= e) /* more than 1MB/s */
+ printf("%2d MB in %5.2f seconds =%6.2f %cB/sec\n", t, e, t / e, 'M');
+ else
+ printf("%2d MB in %5.2f seconds =%6.2f %cB/sec\n", t, e, t / e * 1024, 'k');
+}
+
+static int do_blkgetsize (int fd, unsigned long long *blksize64)
+{
+ int rc;
+ unsigned blksize32 = 0;
+
+ if (0 == ioctl(fd, BLKGETSIZE64, blksize64)) { // returns bytes
+ *blksize64 /= 512;
+ return 0;
+ }
+ rc = ioctl(fd, BLKGETSIZE, &blksize32); // returns sectors
+ if (rc)
+ bb_perror_msg("BLKGETSIZE");
+ *blksize64 = blksize32;
+ return rc;
+}
+
+static void do_time(int flag, int fd)
+/*
+ flag = 0 time_cache
+ flag = 1 time_device
+*/
+{
+ struct itimerval e1, e2;
+ double elapsed, elapsed2;
+ unsigned int max_iterations = 1024, total_MB, iterations;
+ unsigned long long blksize;
+ RESERVE_CONFIG_BUFFER(buf, TIMING_BUF_BYTES);
+
+ if (mlock(buf, TIMING_BUF_BYTES)) {
+ bb_perror_msg("mlock");
+ goto quit2;
+ }
+
+ if (0 == do_blkgetsize(fd, &blksize)) {
+ max_iterations = blksize / (2 * 1024) / TIMING_BUF_MB;
+ }
+
+ /* Clear out the device request queues & give them time to complete */
+ sync();
+ sleep(3);
+
+ setitimer(ITIMER_REAL, &(struct itimerval){{1000,0},{1000,0}}, NULL);
+
+ if (flag == 0) {
+ /* Time cache */
+
+ if (seek_to_zero (fd)) return;
+ if (read_big_block (fd, buf)) return;
+ printf(" Timing cached reads: ");
+ fflush(stdout);
+
+ /* Now do the timing */
+ iterations = 0;
+ getitimer(ITIMER_REAL, &e1);
+ do {
+ ++iterations;
+ if (seek_to_zero (fd) || read_big_block (fd, buf))
+ goto quit;
+ getitimer(ITIMER_REAL, &e2);
+ elapsed = (e1.it_value.tv_sec - e2.it_value.tv_sec)
+ + ((e1.it_value.tv_usec - e2.it_value.tv_usec) / 1000000.0);
+ } while (elapsed < 2.0);
+ total_MB = iterations * TIMING_BUF_MB;
+
+ /* Now remove the lseek() and getitimer() overheads from the elapsed time */
+ getitimer(ITIMER_REAL, &e1);
+ do {
+ if (seek_to_zero (fd))
+ goto quit;
+ getitimer(ITIMER_REAL, &e2);
+ elapsed2 = (e1.it_value.tv_sec - e2.it_value.tv_sec)
+ + ((e1.it_value.tv_usec - e2.it_value.tv_usec) / 1000000.0);
+ } while (--iterations);
+
+ elapsed -= elapsed2;
+ print_timing(BUFCACHE_FACTOR * total_MB, elapsed);
+ flush_buffer_cache(fd);
+ sleep(1);
+ } else {
+ /* Time device */
+
+ printf(" Timing buffered disk reads: ");
+ fflush(stdout);
+ /*
+ * getitimer() is used rather than gettimeofday() because
+ * it is much more consistent (on my machine, at least).
+ */
+ /* Now do the timings for real */
+ iterations = 0;
+ getitimer(ITIMER_REAL, &e1);
+ do {
+ ++iterations;
+ if (read_big_block (fd, buf))
+ goto quit;
+ getitimer(ITIMER_REAL, &e2);
+ elapsed = (e1.it_value.tv_sec - e2.it_value.tv_sec)
+ + ((e1.it_value.tv_usec - e2.it_value.tv_usec) / 1000000.0);
+ } while (elapsed < 3.0 && iterations < max_iterations);
+
+ total_MB = iterations * TIMING_BUF_MB;
+ print_timing(total_MB, elapsed);
+ }
+quit:
+ munlock(buf, TIMING_BUF_BYTES);
+quit2:
+ RELEASE_CONFIG_BUFFER(buf);
+}
+
+static void on_off (unsigned int value)
+{
+ printf(value ? " (on)\n" : " (off)\n");
+}
+
+#if ENABLE_FEATURE_HDPARM_HDIO_TRISTATE_HWIF
+static void bus_state_value(unsigned int value)
+{
+ if (value == BUSSTATE_ON)
+ on_off(1);
+ else if (value == BUSSTATE_OFF)
+ on_off(0);
+ else if (value == BUSSTATE_TRISTATE)
+ printf(" (tristate)\n");
+ else
+ printf(" (unknown: %d)\n", value);
+}
+#endif
+
+#ifdef HDIO_DRIVE_CMD
+static void interpret_standby(unsigned int standby)
+{
+ unsigned int t;
+
+ printf(" (");
+ if (standby == 0)
+ printf("off");
+ else if (standby == 252)
+ printf("21 minutes");
+ else if (standby == 253)
+ printf("vendor-specific");
+ else if (standby == 254)
+ printf("Reserved");
+ else if (standby == 255)
+ printf("21 minutes + 15 seconds");
+ else {
+ if (standby <= 240) {
+ t = standby * 5;
+ printf("%u minutes + %u seconds", t / 60, t % 60);
+ } else if (standby <= 251) {
+ t = (standby - 240) * 30;
+ printf("%u hours + %u minutes", t / 60, t % 60);
+ } else
+ printf("illegal value");
+ }
+ printf(")\n");
+}
+
+struct xfermode_entry {
+ int val;
+ const char *name;
+};
+
+static const struct xfermode_entry xfermode_table[] = {
+ { 8, "pio0" },
+ { 9, "pio1" },
+ { 10, "pio2" },
+ { 11, "pio3" },
+ { 12, "pio4" },
+ { 13, "pio5" },
+ { 14, "pio6" },
+ { 15, "pio7" },
+ { 16, "sdma0" },
+ { 17, "sdma1" },
+ { 18, "sdma2" },
+ { 19, "sdma3" },
+ { 20, "sdma4" },
+ { 21, "sdma5" },
+ { 22, "sdma6" },
+ { 23, "sdma7" },
+ { 32, "mdma0" },
+ { 33, "mdma1" },
+ { 34, "mdma2" },
+ { 35, "mdma3" },
+ { 36, "mdma4" },
+ { 37, "mdma5" },
+ { 38, "mdma6" },
+ { 39, "mdma7" },
+ { 64, "udma0" },
+ { 65, "udma1" },
+ { 66, "udma2" },
+ { 67, "udma3" },
+ { 68, "udma4" },
+ { 69, "udma5" },
+ { 70, "udma6" },
+ { 71, "udma7" },
+ { 0, NULL }
+};
+
+static int translate_xfermode(char * name)
+{
+ const struct xfermode_entry *tmp;
+ char *endptr;
+ int val = -1;
+
+ for (tmp = xfermode_table; tmp->name != NULL; ++tmp) {
+ if (!strcmp(name, tmp->name))
+ return tmp->val;
+ }
+
+ val = strtol(name, &endptr, 10);
+ if (*endptr == '\0')
+ return val;
+
+ return -1;
+}
+
+static void interpret_xfermode(unsigned int xfermode)
+{
+ printf(" (");
+ if (xfermode == 0)
+ printf("default PIO mode");
+ else if (xfermode == 1)
+ printf("default PIO mode, disable IORDY");
+ else if (xfermode >= 8 && xfermode <= 15)
+ printf("PIO flow control mode%u", xfermode-8);
+ else if (xfermode >= 16 && xfermode <= 23)
+ printf("singleword DMA mode%u", xfermode-16);
+ else if (xfermode >= 32 && xfermode <= 39)
+ printf("multiword DMA mode%u", xfermode-32);
+ else if (xfermode >= 64 && xfermode <= 71)
+ printf("UltraDMA mode%u", xfermode-64);
+ else
+ printf("Unknown");
+ printf(")\n");
+}
+#endif /* HDIO_DRIVE_CMD */
+
+static void print_flag(unsigned long flag, const char *s, unsigned long value)
+{
+ if (flag)
+ printf(" setting %s to %ld\n", s, value);
+}
+
+static void process_dev(char *devname)
+{
+ int fd;
+ static long parm, multcount;
+#ifndef HDIO_DRIVE_CMD
+ int force_operation = 0;
+#endif
+ /* Please restore args[n] to these values after each ioctl
+ except for args[2] */
+ unsigned char args[4] = {WIN_SETFEATURES,0,0,0};
+ const char *fmt = " %s\t= %2ld";
+
+ fd = xopen(devname, O_RDONLY|O_NONBLOCK);
+ printf("\n%s:\n", devname);
+
+ if (set_readahead) {
+ print_flag(get_readahead,"fs readahead", Xreadahead);
+ bb_ioctl(fd, BLKRASET,(int *)Xreadahead,"BLKRASET");
+ }
+#if ENABLE_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF
+ if (unregister_hwif) {
+ printf(" attempting to unregister hwif#%lu\n", hwif);
+ bb_ioctl(fd, HDIO_UNREGISTER_HWIF,(int *)(unsigned long)hwif,"HDIO_UNREGISTER_HWIF");
+ }
+#endif
+#if ENABLE_FEATURE_HDPARM_HDIO_SCAN_HWIF
+ if (scan_hwif) {
+ printf(" attempting to scan hwif (0x%lx, 0x%lx, %lu)\n", hwif_data, hwif_ctrl, hwif_irq);
+ args[0] = hwif_data;
+ args[1] = hwif_ctrl;
+ args[2] = hwif_irq;
+ bb_ioctl(fd, HDIO_SCAN_HWIF, args, "HDIO_SCAN_HWIF");
+ args[0] = WIN_SETFEATURES;
+ args[1] = 0;
+ }
+#endif
+ if (set_piomode) {
+ if (noisy_piomode) {
+ printf(" attempting to ");
+ if (piomode == 255)
+ printf("auto-tune PIO mode\n");
+ else if (piomode < 100)
+ printf("set PIO mode to %d\n", piomode);
+ else if (piomode < 200)
+ printf("set MDMA mode to %d\n", (piomode-100));
+ else
+ printf("set UDMA mode to %d\n", (piomode-200));
+ }
+ bb_ioctl(fd, HDIO_SET_PIO_MODE, (int *)(unsigned long)piomode, "HDIO_SET_PIO_MODE");
+ }
+ if (set_io32bit) {
+ print_flag(get_io32bit,"32-bit IO_support flag", io32bit);
+ bb_ioctl(fd, HDIO_SET_32BIT, (int *)io32bit, "HDIO_SET_32BIT");
+ }
+ if (set_mult) {
+ print_flag(get_mult, "multcount", mult);
+#ifdef HDIO_DRIVE_CMD
+ bb_ioctl(fd, HDIO_SET_MULTCOUNT, &mult, "HDIO_SET_MULTCOUNT");
+#else
+ force_operation |= (!bb_ioctl(fd, HDIO_SET_MULTCOUNT, &mult, "HDIO_SET_MULTCOUNT"));
+#endif
+ }
+ if (set_readonly) {
+ print_flag_on_off(get_readonly,"readonly", readonly);
+ bb_ioctl(fd, BLKROSET, &readonly, "BLKROSET");
+ }
+ if (set_unmask) {
+ print_flag_on_off(get_unmask,"unmaskirq", unmask);
+ bb_ioctl(fd, HDIO_SET_UNMASKINTR, (int *)unmask, "HDIO_SET_UNMASKINTR");
+ }
+#if ENABLE_FEATURE_HDPARM_HDIO_GETSET_DMA
+ if (set_dma) {
+ print_flag_on_off(get_dma, "using_dma", dma);
+ bb_ioctl(fd, HDIO_SET_DMA, (int *)dma, "HDIO_SET_DMA");
+ }
+#endif /* FEATURE_HDPARM_HDIO_GETSET_DMA */
+ if (set_dma_q) {
+ print_flag_on_off(get_dma_q,"DMA queue_depth", dma_q);
+ bb_ioctl(fd, HDIO_SET_QDMA, (int *)dma_q, "HDIO_SET_QDMA");
+ }
+ if (set_nowerr) {
+ print_flag_on_off(get_nowerr,"nowerr", nowerr);
+ bb_ioctl(fd, HDIO_SET_NOWERR, (int *)nowerr,"HDIO_SET_NOWERR");
+ }
+ if (set_keep) {
+ print_flag_on_off(get_keep,"keep_settings", keep);
+ bb_ioctl(fd, HDIO_SET_KEEPSETTINGS, (int *)keep,"HDIO_SET_KEEPSETTINGS");
+ }
+#ifdef HDIO_DRIVE_CMD
+ if (set_doorlock) {
+ args[0] = doorlock ? WIN_DOORLOCK : WIN_DOORUNLOCK;
+ args[2] = 0;
+ print_flag_on_off(get_doorlock,"drive doorlock", doorlock);
+ bb_ioctl(fd, HDIO_DRIVE_CMD, &args,"HDIO_DRIVE_CMD(doorlock)");
+ args[0] = WIN_SETFEATURES;
+ }
+ if (set_dkeep) {
+ /* lock/unlock the drive's "feature" settings */
+ print_flag_on_off(get_dkeep,"drive keep features", dkeep);
+ args[2] = dkeep ? 0x66 : 0xcc;
+ bb_ioctl(fd, HDIO_DRIVE_CMD, &args,"HDIO_DRIVE_CMD(keepsettings)");
+ }
+ if (set_defects) {
+ args[2] = defects ? 0x04 : 0x84;
+ print_flag(get_defects,"drive defect-mgmt", defects);
+ bb_ioctl(fd, HDIO_DRIVE_CMD, &args,"HDIO_DRIVE_CMD(defectmgmt)");
+ }
+ if (set_prefetch) {
+ args[1] = prefetch;
+ args[2] = 0xab;
+ print_flag(get_prefetch,"drive prefetch", prefetch);
+ bb_ioctl(fd, HDIO_DRIVE_CMD, &args, "HDIO_DRIVE_CMD(setprefetch)");
+ args[1] = 0;
+ }
+ if (set_xfermode) {
+ args[1] = xfermode_requested;
+ args[2] = 3;
+ if (get_xfermode)
+ {
+ print_flag(1,"xfermode", xfermode_requested);
+ interpret_xfermode(xfermode_requested);
+ }
+ bb_ioctl(fd, HDIO_DRIVE_CMD, &args,"HDIO_DRIVE_CMD(setxfermode)");
+ args[1] = 0;
+ }
+ if (set_lookahead) {
+ args[2] = lookahead ? 0xaa : 0x55;
+ print_flag_on_off(get_lookahead,"drive read-lookahead", lookahead);
+ bb_ioctl(fd, HDIO_DRIVE_CMD, &args, "HDIO_DRIVE_CMD(setreadahead)");
+ }
+ if (set_apmmode) {
+ args[2] = (apmmode == 255) ? 0x85 /* disable */ : 0x05 /* set */; /* feature register */
+ args[1] = apmmode; /* sector count register 1-255 */
+ if (get_apmmode)
+ printf(" setting APM level to %s 0x%02lX (%ld)\n", (apmmode == 255) ? "disabled" : "", apmmode, apmmode);
+ bb_ioctl(fd, HDIO_DRIVE_CMD, &args,"HDIO_DRIVE_CMD");
+ args[1] = 0;
+ }
+ if (set_wcache) {
+#ifdef DO_FLUSHCACHE
+#ifndef WIN_FLUSHCACHE
+#define WIN_FLUSHCACHE 0xe7
+#endif
+ static unsigned char flushcache[4] = {WIN_FLUSHCACHE,0,0,0};
+#endif /* DO_FLUSHCACHE */
+ args[2] = wcache ? 0x02 : 0x82;
+ print_flag_on_off(get_wcache,"drive write-caching", wcache);
+#ifdef DO_FLUSHCACHE
+ if (!wcache)
+ bb_ioctl(fd, HDIO_DRIVE_CMD, &flushcache, "HDIO_DRIVE_CMD(flushcache)");
+#endif /* DO_FLUSHCACHE */
+ bb_ioctl(fd, HDIO_DRIVE_CMD, &args, "HDIO_DRIVE_CMD(setcache)");
+#ifdef DO_FLUSHCACHE
+ if (!wcache)
+ bb_ioctl(fd, HDIO_DRIVE_CMD, &flushcache, "HDIO_DRIVE_CMD(flushcache)");
+#endif /* DO_FLUSHCACHE */
+ }
+
+ /* In code below, we do not preserve args[0], but the rest
+ is preserved, including args[2] */
+ args[2] = 0;
+
+ if (set_standbynow) {
+#ifndef WIN_STANDBYNOW1
+#define WIN_STANDBYNOW1 0xE0
+#endif
+#ifndef WIN_STANDBYNOW2
+#define WIN_STANDBYNOW2 0x94
+#endif
+ if (get_standbynow) printf(" issuing standby command\n");
+ args[0] = WIN_STANDBYNOW1;
+ bb_ioctl_alt(fd, HDIO_DRIVE_CMD, args, WIN_STANDBYNOW2, "HDIO_DRIVE_CMD(standby)");
+ }
+ if (set_sleepnow) {
+#ifndef WIN_SLEEPNOW1
+#define WIN_SLEEPNOW1 0xE6
+#endif
+#ifndef WIN_SLEEPNOW2
+#define WIN_SLEEPNOW2 0x99
+#endif
+ if (get_sleepnow) printf(" issuing sleep command\n");
+ args[0] = WIN_SLEEPNOW1;
+ bb_ioctl_alt(fd, HDIO_DRIVE_CMD, args, WIN_SLEEPNOW2, "HDIO_DRIVE_CMD(sleep)");
+ }
+ if (set_seagate) {
+ args[0] = 0xfb;
+ if (get_seagate) printf(" disabling Seagate auto powersaving mode\n");
+ bb_ioctl(fd, HDIO_DRIVE_CMD, &args, "HDIO_DRIVE_CMD(seagatepwrsave)");
+ }
+ if (set_standby) {
+ args[0] = WIN_SETIDLE1;
+ args[1] = standby_requested;
+ if (get_standby) {
+ print_flag(1,"standby", standby_requested);
+ interpret_standby(standby_requested);
+ }
+ bb_ioctl(fd, HDIO_DRIVE_CMD, &args, "HDIO_DRIVE_CMD(setidle1)");
+ args[1] = 0;
+ }
+#else /* HDIO_DRIVE_CMD */
+ if (force_operation) {
+ char buf[512];
+ flush_buffer_cache(fd);
+ if (-1 == read(fd, buf, sizeof(buf)))
+ bb_perror_msg("read(%d bytes) failed (rc=%d)", sizeof(buf), -1);
+ }
+#endif /* HDIO_DRIVE_CMD */
+
+ if (get_mult || get_identity) {
+ multcount = -1;
+ if (ioctl(fd, HDIO_GET_MULTCOUNT, &multcount)) {
+ if (get_mult)
+ bb_perror_msg("HDIO_GET_MULTCOUNT");
+ } else if (get_mult) {
+ printf(fmt, "multcount", multcount);
+ on_off(multcount);
+ }
+ }
+ if (get_io32bit) {
+ if (!bb_ioctl(fd, HDIO_GET_32BIT, &parm, "HDIO_GET_32BIT")) {
+ printf(" IO_support\t=%3ld (", parm);
+ if (parm == 0)
+ printf("default 16-bit)\n");
+ else if (parm == 2)
+ printf("16-bit)\n");
+ else if (parm == 1)
+ printf("32-bit)\n");
+ else if (parm == 3)
+ printf("32-bit w/sync)\n");
+ else if (parm == 8)
+ printf("Request-Queue-Bypass)\n");
+ else
+ printf("\?\?\?)\n");
+ }
+ }
+ if (get_unmask) {
+ bb_ioctl_on_off(fd, HDIO_GET_UNMASKINTR,(unsigned long *)parm,
+ "HDIO_GET_UNMASKINTR","unmaskirq");
+ }
+
+
+#if ENABLE_FEATURE_HDPARM_HDIO_GETSET_DMA
+ if (get_dma) {
+ if (!bb_ioctl(fd, HDIO_GET_DMA, &parm, "HDIO_GET_DMA"))
+ {
+ printf(fmt, "using_dma", parm);
+ if (parm == 8)
+ printf(" (DMA-Assisted-PIO)\n");
+ else
+ on_off(parm);
+ }
+ }
+#endif
+ if (get_dma_q) {
+ bb_ioctl_on_off (fd, HDIO_GET_QDMA,(unsigned long *)parm,
+ "HDIO_GET_QDMA","queue_depth");
+ }
+ if (get_keep) {
+ bb_ioctl_on_off (fd, HDIO_GET_KEEPSETTINGS,(unsigned long *)parm,
+ "HDIO_GET_KEEPSETTINGS","keepsettings");
+ }
+
+ if (get_nowerr) {
+ bb_ioctl_on_off (fd, HDIO_GET_NOWERR,(unsigned long *)&parm,
+ "HDIO_GET_NOWERR","nowerr");
+ }
+ if (get_readonly) {
+ bb_ioctl_on_off(fd, BLKROGET,(unsigned long *)parm,
+ "BLKROGET","readonly");
+ }
+ if (get_readahead) {
+ bb_ioctl_on_off (fd, BLKRAGET, (unsigned long *) parm,
+ "BLKRAGET","readahead");
+ }
+ if (get_geom) {
+ if (!bb_ioctl(fd, BLKGETSIZE, &parm, "BLKGETSIZE")) {
+ struct hd_geometry g;
+
+ if (!bb_ioctl(fd, HDIO_GETGEO, &g, "HDIO_GETGEO"))
+ printf(" geometry\t= %u/%u/%u, sectors = %ld, start = %ld\n",
+ g.cylinders, g.heads, g.sectors, parm, g.start);
+ }
+ }
+#ifdef HDIO_DRIVE_CMD
+ if (get_powermode) {
+#ifndef WIN_CHECKPOWERMODE1
+#define WIN_CHECKPOWERMODE1 0xE5
+#endif
+#ifndef WIN_CHECKPOWERMODE2
+#define WIN_CHECKPOWERMODE2 0x98
+#endif
+ const char *state;
+
+ args[0] = WIN_CHECKPOWERMODE1;
+ if (bb_ioctl_alt(fd, HDIO_DRIVE_CMD, args, WIN_CHECKPOWERMODE2, 0)) {
+ if (errno != EIO || args[0] != 0 || args[1] != 0)
+ state = "Unknown";
+ else
+ state = "sleeping";
+ } else
+ state = (args[2] == 255) ? "active/idle" : "standby";
+ args[1] = args[2] = 0;
+
+ printf(" drive state is: %s\n", state);
+ }
+#endif
+#if ENABLE_FEATURE_HDPARM_HDIO_DRIVE_RESET
+ if (perform_reset) {
+ bb_ioctl(fd, HDIO_DRIVE_RESET, NULL, "HDIO_DRIVE_RESET");
+ }
+#endif /* FEATURE_HDPARM_HDIO_DRIVE_RESET */
+#if ENABLE_FEATURE_HDPARM_HDIO_TRISTATE_HWIF
+ if (perform_tristate) {
+ args[0] = 0;
+ args[1] = tristate;
+ bb_ioctl(fd, HDIO_TRISTATE_HWIF, &args, "HDIO_TRISTATE_HWIF");
+ }
+#endif /* FEATURE_HDPARM_HDIO_TRISTATE_HWIF */
+#if ENABLE_FEATURE_HDPARM_GET_IDENTITY
+ if (get_identity) {
+ static struct hd_driveid id;
+
+ if (!ioctl(fd, HDIO_GET_IDENTITY, &id)) {
+ if (multcount != -1) {
+ id.multsect = multcount;
+ id.multsect_valid |= 1;
+ } else
+ id.multsect_valid &= ~1;
+ dump_identity(&id);
+ } else if (errno == -ENOMSG)
+ printf(" no identification info available\n");
+ else
+ bb_perror_msg("HDIO_GET_IDENTITY");
+ }
+
+ if (get_IDentity) {
+ unsigned char args1[4+512]; /* = { ... } will eat 0.5k of rodata! */
+
+ memset(args1, 0, sizeof(args1));
+ args1[0] = WIN_IDENTIFY;
+ args1[3] = 1;
+ if (!bb_ioctl_alt(fd, HDIO_DRIVE_CMD, args1, WIN_PIDENTIFY, "HDIO_DRIVE_CMD(identify)"))
+ identify((void *)(args1 + 4));
+ }
+#endif
+#if ENABLE_FEATURE_HDPARM_HDIO_TRISTATE_HWIF
+ if (set_busstate) {
+ if (get_busstate) {
+ print_flag(1, "bus state", busstate);
+ bus_state_value(busstate);
+ }
+ bb_ioctl(fd, HDIO_SET_BUSSTATE, (int *)(unsigned long)busstate, "HDIO_SET_BUSSTATE");
+ }
+ if (get_busstate) {
+ if (!bb_ioctl(fd, HDIO_GET_BUSSTATE, &parm, "HDIO_GET_BUSSTATE")) {
+ printf(fmt, "bus state", parm);
+ bus_state_value(parm);
+ }
+ }
+#endif
+ if (reread_partn)
+ bb_ioctl(fd, BLKRRPART, NULL, "BLKRRPART");
+
+ if (do_ctimings)
+ do_time(0, fd); /* time cache */
+ if (do_timings)
+ do_time(1, fd); /* time device */
+ if (do_flush)
+ flush_buffer_cache(fd);
+ close(fd);
+}
+
+#if ENABLE_FEATURE_HDPARM_GET_IDENTITY
+static int fromhex(unsigned char c)
+{
+ if (isdigit(c))
+ return (c - '0');
+ if (c >= 'a' && c <= 'f')
+ return (c - ('a' - 10));
+ bb_error_msg_and_die("bad char: '%c' 0x%02x", c, c);
+}
+
+static void identify_from_stdin(void)
+{
+ uint16_t sbuf[256];
+ unsigned char buf[1280];
+ unsigned char *b = (unsigned char *)buf;
+ int i;
+
+ xread(0, buf, 1280);
+
+ // Convert the newline-separated hex data into an identify block.
+
+ for (i = 0; i < 256; i++) {
+ int j;
+ for (j = 0; j < 4; j++)
+ sbuf[i] = (sbuf[i] << 4) + fromhex(*(b++));
+ }
+
+ // Parse the data.
+
+ identify(sbuf);
+}
+#endif
+
+/* busybox specific stuff */
+static void parse_opts(unsigned long *get, unsigned long *set, unsigned long *value, int min, int max)
+{
+ if (get) {
+ *get = 1;
+ }
+ if (optarg) {
+ *set = 1;
+ *value = xatol_range(optarg, min, max);
+ }
+}
+
+static void parse_xfermode(int flag, unsigned long *get, unsigned long *set, int *value)
+{
+ if (flag) {
+ *get = 1;
+ if (optarg) {
+ *set = ((*value = translate_xfermode(optarg)) > -1);
+ }
+ }
+}
+
+/*------- getopt short options --------*/
+static const char hdparm_options[] = "gfu::n::p:r::m::c::k::a::B:tTh"
+ USE_FEATURE_HDPARM_GET_IDENTITY("iI")
+ USE_FEATURE_HDPARM_HDIO_GETSET_DMA("d::")
+#ifdef HDIO_DRIVE_CMD
+ "S:D:P:X:K:A:L:W:CyYzZ"
+#endif
+ USE_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF("U:")
+#ifdef HDIO_GET_QDMA
+#ifdef HDIO_SET_QDMA
+ "Q:"
+#else
+ "Q"
+#endif
+#endif
+ USE_FEATURE_HDPARM_HDIO_DRIVE_RESET("w")
+ USE_FEATURE_HDPARM_HDIO_TRISTATE_HWIF("x::b:")
+ USE_FEATURE_HDPARM_HDIO_SCAN_HWIF("R:");
+/*-------------------------------------*/
+
+/* our main() routine: */
+int hdparm_main(int argc, char **argv) ATTRIBUTE_NORETURN;;
+int hdparm_main(int argc, char **argv) ATTRIBUTE_NORETURN;
+int hdparm_main(int argc, char **argv);
+int hdparm_main(int argc, char **argv)
+{
+ int c;
+ int flagcount = 0;
+
+ while ((c = getopt(argc, argv, hdparm_options)) >= 0) {
+ flagcount++;
+ if (c == 'h') bb_show_usage(); /* EXIT */
+ USE_FEATURE_HDPARM_GET_IDENTITY(get_IDentity |= (c == 'I'));
+ USE_FEATURE_HDPARM_GET_IDENTITY(get_identity |= (c == 'i'));
+ get_geom |= (c == 'g');
+ do_flush |= (c == 'f');
+ if (c == 'u') parse_opts(&get_unmask, &set_unmask, &unmask, 0, 1);
+ USE_FEATURE_HDPARM_HDIO_GETSET_DMA(if (c == 'd') parse_opts(&get_dma, &set_dma, &dma, 0, 9));
+ if (c == 'n') parse_opts(&get_nowerr, &set_nowerr, &nowerr, 0, 1);
+ parse_xfermode((c == 'p'),&noisy_piomode, &set_piomode, &piomode);
+ if (c == 'r') parse_opts(&get_readonly, &set_readonly, &readonly, 0, 1);
+ if (c == 'm') parse_opts(&get_mult, &set_mult, &mult, 0, INT_MAX /*32*/);
+ if (c == 'c') parse_opts(&get_io32bit, &set_io32bit, &io32bit, 0, INT_MAX /*8*/);
+ if (c == 'k') parse_opts(&get_keep, &set_keep, &keep, 0, 1);
+ if (c == 'a') parse_opts(&get_readahead, &set_readahead, &Xreadahead, 0, INT_MAX);
+ if (c == 'B') parse_opts(&get_apmmode, &set_apmmode, &apmmode, 1, 255);
+ do_flush |= do_timings |= (c == 't');
+ do_flush |= do_ctimings |= (c == 'T');
+#ifdef HDIO_DRIVE_CMD
+ if (c == 'S') parse_opts(&get_standby, &set_standby, &standby_requested, 0, INT_MAX);
+ if (c == 'D') parse_opts(&get_defects, &set_defects, &defects, 0, INT_MAX);
+ if (c == 'P') parse_opts(&get_prefetch, &set_prefetch, &prefetch, 0, INT_MAX);
+ parse_xfermode((c == 'X'), &get_xfermode, &set_xfermode, &xfermode_requested);
+ if (c == 'K') parse_opts(&get_dkeep, &set_dkeep, &prefetch, 0, 1);
+ if (c == 'A') parse_opts(&get_lookahead, &set_lookahead, &lookahead, 0, 1);
+ if (c == 'L') parse_opts(&get_doorlock, &set_doorlock, &doorlock, 0, 1);
+ if (c == 'W') parse_opts(&get_wcache, &set_wcache, &wcache, 0, 1);
+ get_powermode |= (c == 'C');
+ get_standbynow = set_standbynow |= (c == 'y');
+ get_sleepnow = set_sleepnow |= (c == 'Y');
+ reread_partn |= (c == 'z');
+ get_seagate = set_seagate |= (c == 'Z');
+#endif
+ USE_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF(if (c == 'U') parse_opts(NULL, &unregister_hwif, &hwif, 0, INT_MAX));
+#ifdef HDIO_GET_QDMA
+ if (c == 'Q') {
+#ifdef HDIO_SET_QDMA
+ parse_opts(&get_dma_q, &set_dma_q, &dma_q, 0, INT_MAX);
+#else
+ parse_opts(&get_dma_q, NULL, NULL, 0, 0);
+#endif
+ }
+#endif
+ USE_FEATURE_HDPARM_HDIO_DRIVE_RESET(perform_reset = (c == 'r'));
+ USE_FEATURE_HDPARM_HDIO_TRISTATE_HWIF(if (c == 'x') parse_opts(NULL, &perform_tristate, &tristate, 0, 1));
+ USE_FEATURE_HDPARM_HDIO_TRISTATE_HWIF(if (c == 'b') parse_opts(&get_busstate, &set_busstate, &busstate, 0, 2));
+#if ENABLE_FEATURE_HDPARM_HDIO_SCAN_HWIF
+ if (c == 'R') {
+ parse_opts(NULL, &scan_hwif, &hwif_data, 0, INT_MAX);
+ hwif_ctrl = xatoi_u((argv[optind]) ? argv[optind] : "");
+ hwif_irq = xatoi_u((argv[optind+1]) ? argv[optind+1] : "");
+ /* Move past the 2 additional arguments */
+ argv += 2;
+ argc -= 2;
+ }
+#endif
+ }
+ /* When no flags are given (flagcount = 0), -acdgkmnru is assumed. */
+ if (!flagcount) {
+ get_mult = get_io32bit = get_unmask = get_keep = get_readonly = get_readahead = get_geom = 1;
+ USE_FEATURE_HDPARM_HDIO_GETSET_DMA(get_dma = 1);
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ if (ENABLE_FEATURE_HDPARM_GET_IDENTITY && !isatty(STDIN_FILENO))
+ identify_from_stdin(); /* EXIT */
+ else bb_show_usage();
+ }
+
+ while (argc--) {
+ process_dev(*argv);
+ argv++;
+ }
+ exit(EXIT_SUCCESS);
+}
diff --git a/i/pc104/initrd/conf/busybox/miscutils/last.c b/i/pc104/initrd/conf/busybox/miscutils/last.c
new file mode 100644
index 0000000..d354807
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/miscutils/last.c
@@ -0,0 +1,92 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * last implementation for busybox
+ *
+ * Copyright (C) 2003-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under the GPL version 2, see the file LICENSE in this tarball.
+ */
+
+#include "busybox.h"
+#include <utmp.h>
+
+#ifndef SHUTDOWN_TIME
+# define SHUTDOWN_TIME 254
+#endif
+
+/* Grr... utmp char[] members do not have to be nul-terminated.
+ * Do what we can while still keeping this reasonably small.
+ * Note: We are assuming the ut_id[] size is fixed at 4. */
+
+#if defined UT_LINESIZE \
+ && ((UT_LINESIZE != 32) || (UT_NAMESIZE != 32) || (UT_HOSTSIZE != 256))
+#error struct utmp member char[] size(s) have changed!
+#elif defined __UT_LINESIZE \
+ && ((__UT_LINESIZE != 32) || (__UT_NAMESIZE != 64) || (__UT_HOSTSIZE != 256))
+#error struct utmp member char[] size(s) have changed!
+#endif
+
+int last_main(int argc, char **argv);
+int last_main(int argc, char **argv)
+{
+ struct utmp ut;
+ int n, file = STDIN_FILENO;
+ time_t t_tmp;
+
+ if (argc > 1) {
+ bb_show_usage();
+ }
+ file = xopen(bb_path_wtmp_file, O_RDONLY);
+
+ printf("%-10s %-14s %-18s %-12.12s %s\n", "USER", "TTY", "HOST", "LOGIN", "TIME");
+ while ((n = safe_read(file, (void*)&ut, sizeof(struct utmp))) != 0) {
+
+ if (n != sizeof(struct utmp)) {
+ bb_perror_msg_and_die("short read");
+ }
+
+ if (ut.ut_line[0] == '~') {
+ if (strncmp(ut.ut_user, "shutdown", 8) == 0)
+ ut.ut_type = SHUTDOWN_TIME;
+ else if (strncmp(ut.ut_user, "reboot", 6) == 0)
+ ut.ut_type = BOOT_TIME;
+ else if (strncmp(ut.ut_user, "runlevel", 7) == 0)
+ ut.ut_type = RUN_LVL;
+ } else {
+ if (!ut.ut_name[0] || strcmp(ut.ut_name, "LOGIN") == 0 ||
+ ut.ut_name[0] == 0)
+ {
+ /* Don't bother. This means we can't find how long
+ * someone was logged in for. Oh well. */
+ continue;
+ }
+ if (ut.ut_type != DEAD_PROCESS &&
+ ut.ut_name[0] && ut.ut_line[0])
+ {
+ ut.ut_type = USER_PROCESS;
+ }
+ if (strcmp(ut.ut_name, "date") == 0) {
+ if (ut.ut_line[0] == '|') ut.ut_type = OLD_TIME;
+ if (ut.ut_line[0] == '{') ut.ut_type = NEW_TIME;
+ }
+ }
+
+ if (ut.ut_type!=USER_PROCESS) {
+ switch (ut.ut_type) {
+ case OLD_TIME:
+ case NEW_TIME:
+ case RUN_LVL:
+ case SHUTDOWN_TIME:
+ continue;
+ case BOOT_TIME:
+ strcpy(ut.ut_line, "system boot");
+ break;
+ }
+ }
+ t_tmp = (time_t)ut.ut_tv.tv_sec;
+ printf("%-10s %-14s %-18s %-12.12s\n", ut.ut_user, ut.ut_line, ut.ut_host,
+ ctime(&t_tmp) + 4);
+ }
+
+ fflush_stdout_and_exit(EXIT_SUCCESS);
+}
diff --git a/i/pc104/initrd/conf/busybox/miscutils/less.c b/i/pc104/initrd/conf/busybox/miscutils/less.c
new file mode 100644
index 0000000..b81430d
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/miscutils/less.c
@@ -0,0 +1,1296 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini less implementation for busybox
+ *
+ * Copyright (C) 2005 by Rob Sullivan <cogito.ergo.cogito@gmail.com>
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ */
+
+/*
+ * TODO:
+ * - Add more regular expression support - search modifiers, certain matches, etc.
+ * - Add more complex bracket searching - currently, nested brackets are
+ * not considered.
+ * - Add support for "F" as an input. This causes less to act in
+ * a similar way to tail -f.
+ * - Allow horizontal scrolling.
+ *
+ * Notes:
+ * - the inp file pointer is used so that keyboard input works after
+ * redirected input has been read from stdin
+ */
+
+#include <sched.h> /* sched_yield() */
+
+#include "busybox.h"
+#if ENABLE_FEATURE_LESS_REGEXP
+#include "xregex.h"
+#endif
+
+/* FIXME: currently doesn't work right */
+#undef ENABLE_FEATURE_LESS_FLAGCS
+#define ENABLE_FEATURE_LESS_FLAGCS 0
+
+/* The escape codes for highlighted and normal text */
+#define HIGHLIGHT "\033[7m"
+#define NORMAL "\033[0m"
+/* The escape code to clear the screen */
+#define CLEAR "\033[H\033[J"
+/* The escape code to clear to end of line */
+#define CLEAR_2_EOL "\033[K"
+
+/* These are the escape sequences corresponding to special keys */
+enum {
+ REAL_KEY_UP = 'A',
+ REAL_KEY_DOWN = 'B',
+ REAL_KEY_RIGHT = 'C',
+ REAL_KEY_LEFT = 'D',
+ REAL_PAGE_UP = '5',
+ REAL_PAGE_DOWN = '6',
+ REAL_KEY_HOME = '7', // vt100? linux vt? or what?
+ REAL_KEY_END = '8',
+ REAL_KEY_HOME_ALT = '1', // ESC [1~ (vt100? linux vt? or what?)
+ REAL_KEY_END_ALT = '4', // ESC [4~
+ REAL_KEY_HOME_XTERM = 'H',
+ REAL_KEY_END_XTERM = 'F',
+
+/* These are the special codes assigned by this program to the special keys */
+ KEY_UP = 20,
+ KEY_DOWN = 21,
+ KEY_RIGHT = 22,
+ KEY_LEFT = 23,
+ PAGE_UP = 24,
+ PAGE_DOWN = 25,
+ KEY_HOME = 26,
+ KEY_END = 27,
+
+/* Absolute max of lines eaten */
+ MAXLINES = CONFIG_FEATURE_LESS_MAXLINES,
+
+/* This many "after the end" lines we will show (at max) */
+ TILDES = 1,
+};
+
+static unsigned max_displayed_line;
+static unsigned width;
+static const char *empty_line_marker = "~";
+
+static char *filename;
+static char **files;
+static unsigned num_files = 1;
+static unsigned current_file = 1;
+static const char **buffer;
+static const char **flines;
+static int cur_fline; /* signed */
+static unsigned max_fline;
+static unsigned max_lineno; /* this one tracks linewrap */
+
+static ssize_t eof_error = 1; /* eof if 0, error if < 0 */
+static char terminated = 1;
+static size_t readpos;
+static size_t readeof;
+/* last position in last line, taking into account tabs */
+static size_t linepos;
+
+/* Command line options */
+enum {
+ FLAG_E = 1,
+ FLAG_M = 1 << 1,
+ FLAG_m = 1 << 2,
+ FLAG_N = 1 << 3,
+ FLAG_TILDE = 1 << 4,
+/* hijack command line options variable for internal state vars */
+ LESS_STATE_MATCH_BACKWARDS = 1 << 15,
+};
+
+#if ENABLE_FEATURE_LESS_MARKS
+static unsigned mark_lines[15][2];
+static unsigned num_marks;
+#endif
+
+#if ENABLE_FEATURE_LESS_REGEXP
+static unsigned *match_lines;
+static int match_pos; /* signed! */
+static unsigned num_matches;
+static regex_t pattern;
+static unsigned pattern_valid;
+#else
+enum { pattern_valid = 0 };
+#endif
+
+static struct termios term_orig, term_vi;
+
+/* File pointer to get input from */
+static int kbd_fd;
+
+/* Reset terminal input to normal */
+static void set_tty_cooked(void)
+{
+ fflush(stdout);
+ tcsetattr(kbd_fd, TCSANOW, &term_orig);
+}
+
+/* Exit the program gracefully */
+static void less_exit(int code)
+{
+ /* TODO: We really should save the terminal state when we start,
+ * and restore it when we exit. Less does this with the
+ * "ti" and "te" termcap commands; can this be done with
+ * only termios.h? */
+ putchar('\n');
+ fflush_stdout_and_exit(code);
+}
+
+/* Move the cursor to a position (x,y), where (0,0) is the
+ top-left corner of the console */
+static void move_cursor(int line, int row)
+{
+ printf("\033[%u;%uH", line, row);
+}
+
+static void clear_line(void)
+{
+ printf("\033[%u;0H" CLEAR_2_EOL, max_displayed_line + 2);
+}
+
+static void print_hilite(const char *str)
+{
+ printf(HIGHLIGHT"%s"NORMAL, str);
+}
+
+static void print_statusline(const char *str)
+{
+ clear_line();
+ printf(HIGHLIGHT"%.*s"NORMAL, width - 1, str);
+}
+
+#if ENABLE_FEATURE_LESS_REGEXP
+static void fill_match_lines(unsigned pos);
+#else
+#define fill_match_lines(pos) ((void)0)
+#endif
+
+
+static void read_lines(void)
+{
+#define readbuf bb_common_bufsiz1
+ char *current_line, *p;
+ USE_FEATURE_LESS_REGEXP(unsigned old_max_fline = max_fline;)
+ int w = width;
+ char last_terminated = terminated;
+
+ if (option_mask32 & FLAG_N)
+ w -= 8;
+
+ current_line = xmalloc(w);
+ p = current_line;
+ max_fline += last_terminated;
+ if (!last_terminated) {
+ const char *cp = flines[max_fline];
+ if (option_mask32 & FLAG_N)
+ cp += 8;
+ strcpy(current_line, cp);
+ p += strlen(current_line);
+ } else {
+ linepos = 0;
+ }
+
+ while (1) {
+ again:
+ *p = '\0';
+ terminated = 0;
+ while (1) {
+ char c;
+ /* if no unprocessed chars left, eat more */
+ if (readpos >= readeof) {
+ smallint yielded = 0;
+
+ ndelay_on(0);
+ read_again:
+ eof_error = safe_read(0, readbuf, sizeof(readbuf));
+ readpos = 0;
+ readeof = eof_error;
+ if (eof_error < 0) {
+ if (errno == EAGAIN && !yielded) {
+ /* We can hit EAGAIN while searching for regexp match.
+ * Yield is not 100% reliable solution in general,
+ * but for less it should be good enough -
+ * we give stdin supplier some CPU time to produce
+ * more input. We do it just once.
+ * Currently, we do not stop when we found the Nth
+ * occurrence we were looking for. We read till end
+ * (or double EAGAIN). TODO? */
+ sched_yield();
+ yielded = 1;
+ goto read_again;
+ }
+ readeof = 0;
+ if (errno != EAGAIN)
+ print_statusline("read error");
+ }
+ ndelay_off(0);
+
+ if (eof_error <= 0) {
+ goto reached_eof;
+ }
+ }
+ c = readbuf[readpos];
+ /* backspace? [needed for manpages] */
+ /* <tab><bs> is (a) insane and */
+ /* (b) harder to do correctly, so we refuse to do it */
+ if (c == '\x8' && linepos && p[-1] != '\t') {
+ readpos++; /* eat it */
+ linepos--;
+ *--p = '\0';
+ continue;
+ }
+ if (c == '\t')
+ linepos += (linepos^7) & 7;
+ linepos++;
+ if (linepos >= w)
+ break;
+ /* ok, we will eat this char */
+ readpos++;
+ if (c == '\n') { terminated = 1; break; }
+ /* NUL is substituted by '\n'! */
+ if (c == '\0') c = '\n';
+ *p++ = c;
+ *p = '\0';
+ }
+ /* Corner case: linewrap with only "" wrapping to next line */
+ /* Looks ugly on screen, so we do not store this empty line */
+ if (!last_terminated && !current_line[0]) {
+ last_terminated = 1;
+ max_lineno++;
+ goto again;
+ }
+ reached_eof:
+ last_terminated = terminated;
+ flines = xrealloc(flines, (max_fline+1) * sizeof(char *));
+ if (option_mask32 & FLAG_N) {
+ /* Width of 7 preserves tab spacing in the text */
+ flines[max_fline] = xasprintf(
+ (max_lineno <= 9999999) ? "%7u %s" : "%07u %s",
+ max_lineno % 10000000, current_line);
+ free(current_line);
+ if (terminated)
+ max_lineno++;
+ } else {
+ flines[max_fline] = xrealloc(current_line, strlen(current_line)+1);
+ }
+ if (max_fline >= MAXLINES)
+ break;
+ if (max_fline > cur_fline + max_displayed_line)
+ break;
+ if (eof_error <= 0) {
+ if (eof_error < 0 && errno == EAGAIN) {
+ /* not yet eof or error, reset flag (or else
+ * we will hog CPU - select() will return
+ * immediately */
+ eof_error = 1;
+ }
+ break;
+ }
+ max_fline++;
+ current_line = xmalloc(w);
+ p = current_line;
+ linepos = 0;
+ }
+ fill_match_lines(old_max_fline);
+#undef readbuf
+}
+
+#if ENABLE_FEATURE_LESS_FLAGS
+/* Interestingly, writing calc_percent as a function saves around 32 bytes
+ * on my build. */
+static int calc_percent(void)
+{
+ unsigned p = (100 * (cur_fline+max_displayed_line+1) + max_fline/2) / (max_fline+1);
+ return p <= 100 ? p : 100;
+}
+
+/* Print a status line if -M was specified */
+static void m_status_print(void)
+{
+ int percentage;
+
+ clear_line();
+ printf(HIGHLIGHT"%s", filename);
+ if (num_files > 1)
+ printf(" (file %i of %i)", current_file, num_files);
+ printf(" lines %i-%i/%i ",
+ cur_fline + 1, cur_fline + max_displayed_line + 1,
+ max_fline + 1);
+ if (cur_fline >= max_fline - max_displayed_line) {
+ printf("(END)"NORMAL);
+ if (num_files > 1 && current_file != num_files)
+ printf(HIGHLIGHT" - next: %s"NORMAL, files[current_file]);
+ return;
+ }
+ percentage = calc_percent();
+ printf("%i%%"NORMAL, percentage);
+}
+#endif
+
+/* Print the status line */
+static void status_print(void)
+{
+ const char *p;
+
+ /* Change the status if flags have been set */
+#if ENABLE_FEATURE_LESS_FLAGS
+ if (option_mask32 & (FLAG_M|FLAG_m)) {
+ m_status_print();
+ return;
+ }
+ /* No flags set */
+#endif
+
+ clear_line();
+ if (cur_fline && cur_fline < max_fline - max_displayed_line) {
+ putchar(':');
+ return;
+ }
+ p = "(END)";
+ if (!cur_fline)
+ p = filename;
+ if (num_files > 1) {
+ printf(HIGHLIGHT"%s (file %i of %i)"NORMAL,
+ p, current_file, num_files);
+ return;
+ }
+ print_hilite(p);
+}
+
+static void cap_cur_fline(int nlines)
+{
+ int diff;
+ if (cur_fline < 0)
+ cur_fline = 0;
+ if (cur_fline + max_displayed_line > max_fline + TILDES) {
+ cur_fline -= nlines;
+ if (cur_fline < 0)
+ cur_fline = 0;
+ diff = max_fline - (cur_fline + max_displayed_line) + TILDES;
+ /* As the number of lines requested was too large, we just move
+ to the end of the file */
+ if (diff > 0)
+ cur_fline += diff;
+ }
+}
+
+static char controls[] =
+ /* NUL: never encountered; TAB: not converted */
+ /**/"\x01\x02\x03\x04\x05\x06\x07\x08" "\x0a\x0b\x0c\x0d\x0e\x0f"
+ "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"
+ "\x7f\x9b"; /* DEL and infamous Meta-ESC :( */
+static char ctrlconv[] =
+ /* '\n': it's a former NUL - subst with '@', not 'J' */
+ "\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x40\x4b\x4c\x4d\x4e\x4f"
+ "\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f";
+
+#if ENABLE_FEATURE_LESS_REGEXP
+static void print_found(const char *line)
+{
+ int match_status;
+ int eflags;
+ char *growline;
+ regmatch_t match_structs;
+
+ char buf[width];
+ const char *str = line;
+ char *p = buf;
+ size_t n;
+
+ while (*str) {
+ n = strcspn(str, controls);
+ if (n) {
+ if (!str[n]) break;
+ memcpy(p, str, n);
+ p += n;
+ str += n;
+ }
+ n = strspn(str, controls);
+ memset(p, '.', n);
+ p += n;
+ str += n;
+ }
+ strcpy(p, str);
+
+ /* buf[] holds quarantined version of str */
+
+ /* Each part of the line that matches has the HIGHLIGHT
+ and NORMAL escape sequences placed around it.
+ NB: we regex against line, but insert text
+ from quarantined copy (buf[]) */
+ str = buf;
+ growline = NULL;
+ eflags = 0;
+ goto start;
+
+ while (match_status == 0) {
+ char *new = xasprintf("%s%.*s"HIGHLIGHT"%.*s"NORMAL,
+ growline ? : "",
+ match_structs.rm_so, str,
+ match_structs.rm_eo - match_structs.rm_so,
+ str + match_structs.rm_so);
+ free(growline); growline = new;
+ str += match_structs.rm_eo;
+ line += match_structs.rm_eo;
+ eflags = REG_NOTBOL;
+ start:
+ /* Most of the time doesn't find the regex, optimize for that */
+ match_status = regexec(&pattern, line, 1, &match_structs, eflags);
+ }
+
+ if (!growline) {
+ printf(CLEAR_2_EOL"%s\n", str);
+ return;
+ }
+ printf(CLEAR_2_EOL"%s%s\n", growline, str);
+ free(growline);
+}
+#else
+void print_found(const char *line);
+#endif
+
+static void print_ascii(const char *str)
+{
+ char buf[width];
+ char *p;
+ size_t n;
+
+ printf(CLEAR_2_EOL);
+ while (*str) {
+ n = strcspn(str, controls);
+ if (n) {
+ if (!str[n]) break;
+ printf("%.*s", (int) n, str);
+ str += n;
+ }
+ n = strspn(str, controls);
+ p = buf;
+ do {
+ if (*str == 0x7f)
+ *p++ = '?';
+ else if (*str == (char)0x9b)
+ /* VT100's CSI, aka Meta-ESC. Who's inventor? */
+ /* I want to know who committed this sin */
+ *p++ = '{';
+ else
+ *p++ = ctrlconv[(unsigned char)*str];
+ str++;
+ } while (--n);
+ *p = '\0';
+ print_hilite(buf);
+ }
+ puts(str);
+}
+
+/* Print the buffer */
+static void buffer_print(void)
+{
+ int i;
+
+ move_cursor(0, 0);
+ for (i = 0; i <= max_displayed_line; i++)
+ if (pattern_valid)
+ print_found(buffer[i]);
+ else
+ print_ascii(buffer[i]);
+ status_print();
+}
+
+static void buffer_fill_and_print(void)
+{
+ int i;
+ for (i = 0; i <= max_displayed_line && cur_fline + i <= max_fline; i++) {
+ buffer[i] = flines[cur_fline + i];
+ }
+ for (; i <= max_displayed_line; i++) {
+ buffer[i] = empty_line_marker;
+ }
+ buffer_print();
+}
+
+/* Move the buffer up and down in the file in order to scroll */
+static void buffer_down(int nlines)
+{
+ cur_fline += nlines;
+ read_lines();
+ cap_cur_fline(nlines);
+ buffer_fill_and_print();
+}
+
+static void buffer_up(int nlines)
+{
+ cur_fline -= nlines;
+ if (cur_fline < 0) cur_fline = 0;
+ read_lines();
+ buffer_fill_and_print();
+}
+
+static void buffer_line(int linenum)
+{
+ if (linenum < 0)
+ linenum = 0;
+ cur_fline = linenum;
+ read_lines();
+ if (linenum + max_displayed_line > max_fline)
+ linenum = max_fline - max_displayed_line + TILDES;
+ if (linenum < 0)
+ linenum = 0;
+ cur_fline = linenum;
+ buffer_fill_and_print();
+}
+
+static void open_file_and_read_lines(void)
+{
+ if (filename) {
+ int fd = xopen(filename, O_RDONLY);
+ dup2(fd, 0);
+ if (fd) close(fd);
+ } else {
+ /* "less" with no arguments in argv[] */
+ /* For status line only */
+ filename = xstrdup(bb_msg_standard_input);
+ }
+ readpos = 0;
+ readeof = 0;
+ linepos = 0;
+ terminated = 1;
+ read_lines();
+}
+
+/* Reinitialize everything for a new file - free the memory and start over */
+static void reinitialize(void)
+{
+ int i;
+
+ if (flines) {
+ for (i = 0; i <= max_fline; i++)
+ free((void*)(flines[i]));
+ free(flines);
+ flines = NULL;
+ }
+
+ max_fline = -1;
+ cur_fline = 0;
+ max_lineno = 0;
+ open_file_and_read_lines();
+ buffer_fill_and_print();
+}
+
+static void getch_nowait(char* input, int sz)
+{
+ ssize_t rd;
+ fd_set readfds;
+ again:
+ fflush(stdout);
+
+ /* NB: select returns whenever read will not block. Therefore:
+ * (a) with O_NONBLOCK'ed fds select will return immediately
+ * (b) if eof is reached, select will also return
+ * because read will immediately return 0 bytes.
+ * Even if select says that input is available, read CAN block
+ * (switch fd into O_NONBLOCK'ed mode to avoid it)
+ */
+ FD_ZERO(&readfds);
+ if (max_fline <= cur_fline + max_displayed_line
+ && eof_error > 0 /* did NOT reach eof yet */
+ ) {
+ /* We are interested in stdin */
+ FD_SET(0, &readfds);
+ }
+ FD_SET(kbd_fd, &readfds);
+ tcsetattr(kbd_fd, TCSANOW, &term_vi);
+ select(kbd_fd + 1, &readfds, NULL, NULL, NULL);
+
+ input[0] = '\0';
+ ndelay_on(kbd_fd);
+ rd = read(kbd_fd, input, sz);
+ ndelay_off(kbd_fd);
+ if (rd < 0) {
+ /* No keyboard input, but we have input on stdin! */
+ if (errno != EAGAIN) /* Huh?? */
+ return;
+ read_lines();
+ buffer_fill_and_print();
+ goto again;
+ }
+}
+
+/* Grab a character from input without requiring the return key. If the
+ * character is ASCII \033, get more characters and assign certain sequences
+ * special return codes. Note that this function works best with raw input. */
+static int less_getch(void)
+{
+ char input[16];
+ unsigned i;
+ again:
+ getch_nowait(input, sizeof(input));
+ /* Detect escape sequences (i.e. arrow keys) and handle
+ * them accordingly */
+
+ if (input[0] == '\033' && input[1] == '[') {
+ set_tty_cooked();
+ i = input[2] - REAL_KEY_UP;
+ if (i < 4)
+ return 20 + i;
+ i = input[2] - REAL_PAGE_UP;
+ if (i < 4)
+ return 24 + i;
+ if (input[2] == REAL_KEY_HOME_XTERM)
+ return KEY_HOME;
+ if (input[2] == REAL_KEY_HOME_ALT)
+ return KEY_HOME;
+ if (input[2] == REAL_KEY_END_XTERM)
+ return KEY_END;
+ if (input[2] == REAL_KEY_END_ALT)
+ return KEY_END;
+ return 0;
+ }
+ /* Reject almost all control chars */
+ i = input[0];
+ if (i < ' ' && i != 0x0d && i != 8) goto again;
+ set_tty_cooked();
+ return i;
+}
+
+static char* less_gets(int sz)
+{
+ char c;
+ int i = 0;
+ char *result = xzalloc(1);
+ while (1) {
+ fflush(stdout);
+
+ /* I be damned if I know why is it needed *repeatedly*,
+ * but it is needed. Is it because of stdio? */
+ tcsetattr(kbd_fd, TCSANOW, &term_vi);
+
+ read(kbd_fd, &c, 1);
+ if (c == 0x0d)
+ return result;
+ if (c == 0x7f)
+ c = 8;
+ if (c == 8 && i) {
+ printf("\x8 \x8");
+ i--;
+ }
+ if (c < ' ')
+ continue;
+ if (i >= width - sz - 1)
+ continue; /* len limit */
+ putchar(c);
+ result[i++] = c;
+ result = xrealloc(result, i+1);
+ result[i] = '\0';
+ }
+}
+
+static void examine_file(void)
+{
+ print_statusline("Examine: ");
+ free(filename);
+ filename = less_gets(sizeof("Examine: ")-1);
+ /* files start by = argv. why we assume that argv is infinitely long??
+ files[num_files] = filename;
+ current_file = num_files + 1;
+ num_files++; */
+ files[0] = filename;
+ num_files = current_file = 1;
+ reinitialize();
+}
+
+/* This function changes the file currently being paged. direction can be one of the following:
+ * -1: go back one file
+ * 0: go to the first file
+ * 1: go forward one file */
+static void change_file(int direction)
+{
+ if (current_file != ((direction > 0) ? num_files : 1)) {
+ current_file = direction ? current_file + direction : 1;
+ free(filename);
+ filename = xstrdup(files[current_file - 1]);
+ reinitialize();
+ } else {
+ print_statusline(direction > 0 ? "No next file" : "No previous file");
+ }
+}
+
+static void remove_current_file(void)
+{
+ int i;
+
+ if (num_files < 2)
+ return;
+
+ if (current_file != 1) {
+ change_file(-1);
+ for (i = 3; i <= num_files; i++)
+ files[i - 2] = files[i - 1];
+ num_files--;
+ } else {
+ change_file(1);
+ for (i = 2; i <= num_files; i++)
+ files[i - 2] = files[i - 1];
+ num_files--;
+ current_file--;
+ }
+}
+
+static void colon_process(void)
+{
+ int keypress;
+
+ /* Clear the current line and print a prompt */
+ print_statusline(" :");
+
+ keypress = less_getch();
+ switch (keypress) {
+ case 'd':
+ remove_current_file();
+ break;
+ case 'e':
+ examine_file();
+ break;
+#if ENABLE_FEATURE_LESS_FLAGS
+ case 'f':
+ m_status_print();
+ break;
+#endif
+ case 'n':
+ change_file(1);
+ break;
+ case 'p':
+ change_file(-1);
+ break;
+ case 'q':
+ less_exit(0);
+ break;
+ case 'x':
+ change_file(0);
+ break;
+ }
+}
+
+#if ENABLE_FEATURE_LESS_REGEXP
+static void normalize_match_pos(int match)
+{
+ if (match >= num_matches)
+ match = num_matches - 1;
+ if (match < 0)
+ match = 0;
+ match_pos = match;
+}
+
+static void goto_match(int match)
+{
+ if (!pattern_valid)
+ return;
+ if (match < 0)
+ match = 0;
+ /* Try to find next match if eof isn't reached yet */
+ if (match >= num_matches && eof_error > 0) {
+ cur_fline = MAXLINES; /* look as far as needed */
+ read_lines();
+ cap_cur_fline(cur_fline);
+ }
+ if (num_matches) {
+ normalize_match_pos(match);
+ buffer_line(match_lines[match_pos]);
+ }
+}
+
+static void fill_match_lines(unsigned pos)
+{
+ if (!pattern_valid)
+ return;
+ /* Run the regex on each line of the current file */
+ while (pos <= max_fline) {
+ /* If this line matches */
+ if (regexec(&pattern, flines[pos], 0, NULL, 0) == 0
+ /* and we didn't match it last time */
+ && !(num_matches && match_lines[num_matches-1] == pos)
+ ) {
+ match_lines = xrealloc(match_lines, (num_matches+1) * sizeof(int));
+ match_lines[num_matches++] = pos;
+ }
+ pos++;
+ }
+}
+
+static void regex_process(void)
+{
+ char *uncomp_regex, *err;
+
+ /* Reset variables */
+ free(match_lines);
+ match_lines = NULL;
+ match_pos = 0;
+ num_matches = 0;
+ if (pattern_valid) {
+ regfree(&pattern);
+ pattern_valid = 0;
+ }
+
+ /* Get the uncompiled regular expression from the user */
+ clear_line();
+ putchar((option_mask32 & LESS_STATE_MATCH_BACKWARDS) ? '?' : '/');
+ uncomp_regex = less_gets(1);
+ if (!uncomp_regex[0]) {
+ free(uncomp_regex);
+ buffer_print();
+ return;
+ }
+
+ /* Compile the regex and check for errors */
+ err = regcomp_or_errmsg(&pattern, uncomp_regex, 0);
+ free(uncomp_regex);
+ if (err) {
+ print_statusline(err);
+ free(err);
+ return;
+ }
+
+ pattern_valid = 1;
+ match_pos = 0;
+ fill_match_lines(0);
+ while (match_pos < num_matches) {
+ if (match_lines[match_pos] > cur_fline)
+ break;
+ match_pos++;
+ }
+ if (option_mask32 & LESS_STATE_MATCH_BACKWARDS)
+ match_pos--;
+
+ /* It's possible that no matches are found yet.
+ * goto_match() will read input looking for match,
+ * if needed */
+ goto_match(match_pos);
+}
+#endif
+
+static void number_process(int first_digit)
+{
+ int i = 1;
+ int num;
+ char num_input[sizeof(int)*4]; /* more than enough */
+ char keypress;
+
+ num_input[0] = first_digit;
+
+ /* Clear the current line, print a prompt, and then print the digit */
+ clear_line();
+ printf(":%c", first_digit);
+
+ /* Receive input until a letter is given */
+ while (i < sizeof(num_input)-1) {
+ num_input[i] = less_getch();
+ if (!num_input[i] || !isdigit(num_input[i]))
+ break;
+ putchar(num_input[i]);
+ i++;
+ }
+
+ /* Take the final letter out of the digits string */
+ keypress = num_input[i];
+ num_input[i] = '\0';
+ num = bb_strtou(num_input, NULL, 10);
+ /* on format error, num == -1 */
+ if (num < 1 || num > MAXLINES) {
+ buffer_print();
+ return;
+ }
+
+ /* We now know the number and the letter entered, so we process them */
+ switch (keypress) {
+ case KEY_DOWN: case 'z': case 'd': case 'e': case ' ': case '\015':
+ buffer_down(num);
+ break;
+ case KEY_UP: case 'b': case 'w': case 'y': case 'u':
+ buffer_up(num);
+ break;
+ case 'g': case '<': case 'G': case '>':
+ cur_fline = num + max_displayed_line;
+ read_lines();
+ buffer_line(num - 1);
+ break;
+ case 'p': case '%':
+ num = num * (max_fline / 100); /* + max_fline / 2; */
+ cur_fline = num + max_displayed_line;
+ read_lines();
+ buffer_line(num);
+ break;
+#if ENABLE_FEATURE_LESS_REGEXP
+ case 'n':
+ goto_match(match_pos + num);
+ break;
+ case '/':
+ option_mask32 &= ~LESS_STATE_MATCH_BACKWARDS;
+ regex_process();
+ break;
+ case '?':
+ option_mask32 |= LESS_STATE_MATCH_BACKWARDS;
+ regex_process();
+ break;
+#endif
+ }
+}
+
+#if ENABLE_FEATURE_LESS_FLAGCS
+static void flag_change(void)
+{
+ int keypress;
+
+ clear_line();
+ putchar('-');
+ keypress = less_getch();
+
+ switch (keypress) {
+ case 'M':
+ option_mask32 ^= FLAG_M;
+ break;
+ case 'm':
+ option_mask32 ^= FLAG_m;
+ break;
+ case 'E':
+ option_mask32 ^= FLAG_E;
+ break;
+ case '~':
+ option_mask32 ^= FLAG_TILDE;
+ break;
+ }
+}
+
+static void show_flag_status(void)
+{
+ int keypress;
+ int flag_val;
+
+ clear_line();
+ putchar('_');
+ keypress = less_getch();
+
+ switch (keypress) {
+ case 'M':
+ flag_val = option_mask32 & FLAG_M;
+ break;
+ case 'm':
+ flag_val = option_mask32 & FLAG_m;
+ break;
+ case '~':
+ flag_val = option_mask32 & FLAG_TILDE;
+ break;
+ case 'N':
+ flag_val = option_mask32 & FLAG_N;
+ break;
+ case 'E':
+ flag_val = option_mask32 & FLAG_E;
+ break;
+ default:
+ flag_val = 0;
+ break;
+ }
+
+ clear_line();
+ printf(HIGHLIGHT"The status of the flag is: %u"NORMAL, flag_val != 0);
+}
+#endif
+
+static void save_input_to_file(void)
+{
+ const char *msg = "";
+ char *current_line;
+ int i;
+ FILE *fp;
+
+ print_statusline("Log file: ");
+ current_line = less_gets(sizeof("Log file: ")-1);
+ if (strlen(current_line) > 0) {
+ fp = fopen(current_line, "w");
+ if (!fp) {
+ msg = "Error opening log file";
+ goto ret;
+ }
+ for (i = 0; i <= max_fline; i++)
+ fprintf(fp, "%s\n", flines[i]);
+ fclose(fp);
+ msg = "Done";
+ }
+ ret:
+ print_statusline(msg);
+ free(current_line);
+}
+
+#if ENABLE_FEATURE_LESS_MARKS
+static void add_mark(void)
+{
+ int letter;
+
+ print_statusline("Mark: ");
+ letter = less_getch();
+
+ if (isalpha(letter)) {
+ /* If we exceed 15 marks, start overwriting previous ones */
+ if (num_marks == 14)
+ num_marks = 0;
+
+ mark_lines[num_marks][0] = letter;
+ mark_lines[num_marks][1] = cur_fline;
+ num_marks++;
+ } else {
+ print_statusline("Invalid mark letter");
+ }
+}
+
+static void goto_mark(void)
+{
+ int letter;
+ int i;
+
+ print_statusline("Go to mark: ");
+ letter = less_getch();
+ clear_line();
+
+ if (isalpha(letter)) {
+ for (i = 0; i <= num_marks; i++)
+ if (letter == mark_lines[i][0]) {
+ buffer_line(mark_lines[i][1]);
+ break;
+ }
+ if (num_marks == 14 && letter != mark_lines[14][0])
+ print_statusline("Mark not set");
+ } else
+ print_statusline("Invalid mark letter");
+}
+#endif
+
+#if ENABLE_FEATURE_LESS_BRACKETS
+static char opp_bracket(char bracket)
+{
+ switch (bracket) {
+ case '{': case '[':
+ return bracket + 2;
+ case '(':
+ return ')';
+ case '}': case ']':
+ return bracket - 2;
+ case ')':
+ return '(';
+ }
+ return 0;
+}
+
+static void match_right_bracket(char bracket)
+{
+ int bracket_line = -1;
+ int i;
+
+ if (strchr(flines[cur_fline], bracket) == NULL) {
+ print_statusline("No bracket in top line");
+ return;
+ }
+ for (i = cur_fline + 1; i < max_fline; i++) {
+ if (strchr(flines[i], opp_bracket(bracket)) != NULL) {
+ bracket_line = i;
+ break;
+ }
+ }
+ if (bracket_line == -1)
+ print_statusline("No matching bracket found");
+ buffer_line(bracket_line - max_displayed_line);
+}
+
+static void match_left_bracket(char bracket)
+{
+ int bracket_line = -1;
+ int i;
+
+ if (strchr(flines[cur_fline + max_displayed_line], bracket) == NULL) {
+ print_statusline("No bracket in bottom line");
+ return;
+ }
+
+ for (i = cur_fline + max_displayed_line; i >= 0; i--) {
+ if (strchr(flines[i], opp_bracket(bracket)) != NULL) {
+ bracket_line = i;
+ break;
+ }
+ }
+ if (bracket_line == -1)
+ print_statusline("No matching bracket found");
+ buffer_line(bracket_line);
+}
+#endif /* FEATURE_LESS_BRACKETS */
+
+static void keypress_process(int keypress)
+{
+ switch (keypress) {
+ case KEY_DOWN: case 'e': case 'j': case 0x0d:
+ buffer_down(1);
+ break;
+ case KEY_UP: case 'y': case 'k':
+ buffer_up(1);
+ break;
+ case PAGE_DOWN: case ' ': case 'z':
+ buffer_down(max_displayed_line + 1);
+ break;
+ case PAGE_UP: case 'w': case 'b':
+ buffer_up(max_displayed_line + 1);
+ break;
+ case 'd':
+ buffer_down((max_displayed_line + 1) / 2);
+ break;
+ case 'u':
+ buffer_up((max_displayed_line + 1) / 2);
+ break;
+ case KEY_HOME: case 'g': case 'p': case '<': case '%':
+ buffer_line(0);
+ break;
+ case KEY_END: case 'G': case '>':
+ cur_fline = MAXLINES;
+ read_lines();
+ buffer_line(cur_fline);
+ break;
+ case 'q': case 'Q':
+ less_exit(0);
+ break;
+#if ENABLE_FEATURE_LESS_MARKS
+ case 'm':
+ add_mark();
+ buffer_print();
+ break;
+ case '\'':
+ goto_mark();
+ buffer_print();
+ break;
+#endif
+ case 'r': case 'R':
+ buffer_print();
+ break;
+ /*case 'R':
+ full_repaint();
+ break;*/
+ case 's':
+ save_input_to_file();
+ break;
+ case 'E':
+ examine_file();
+ break;
+#if ENABLE_FEATURE_LESS_FLAGS
+ case '=':
+ m_status_print();
+ break;
+#endif
+#if ENABLE_FEATURE_LESS_REGEXP
+ case '/':
+ option_mask32 &= ~LESS_STATE_MATCH_BACKWARDS;
+ regex_process();
+ break;
+ case 'n':
+ goto_match(match_pos + 1);
+ break;
+ case 'N':
+ goto_match(match_pos - 1);
+ break;
+ case '?':
+ option_mask32 |= LESS_STATE_MATCH_BACKWARDS;
+ regex_process();
+ break;
+#endif
+#if ENABLE_FEATURE_LESS_FLAGCS
+ case '-':
+ flag_change();
+ buffer_print();
+ break;
+ case '_':
+ show_flag_status();
+ break;
+#endif
+#if ENABLE_FEATURE_LESS_BRACKETS
+ case '{': case '(': case '[':
+ match_right_bracket(keypress);
+ break;
+ case '}': case ')': case ']':
+ match_left_bracket(keypress);
+ break;
+#endif
+ case ':':
+ colon_process();
+ break;
+ }
+
+ if (isdigit(keypress))
+ number_process(keypress);
+}
+
+static void sig_catcher(int sig ATTRIBUTE_UNUSED)
+{
+ set_tty_cooked();
+ exit(1);
+}
+
+int less_main(int argc, char **argv);
+int less_main(int argc, char **argv)
+{
+ int keypress;
+
+ /* TODO: -x: do not interpret backspace, -xx: tab also */
+ /* -xxx: newline also */
+ /* -w N: assume width N (-xxx -w 32: hex viewer of sorts) */
+ getopt32(argc, argv, "EMmN~");
+ argc -= optind;
+ argv += optind;
+ num_files = argc;
+ files = argv;
+
+ /* Another popular pager, most, detects when stdout
+ * is not a tty and turns into cat. This makes sense. */
+ if (!isatty(STDOUT_FILENO))
+ return bb_cat(argv);
+ kbd_fd = open(CURRENT_TTY, O_RDONLY);
+ if (kbd_fd < 0)
+ return bb_cat(argv);
+
+ if (!num_files) {
+ if (isatty(STDIN_FILENO)) {
+ /* Just "less"? No args and no redirection? */
+ bb_error_msg("missing filename");
+ bb_show_usage();
+ }
+ } else
+ filename = xstrdup(files[0]);
+
+ get_terminal_width_height(kbd_fd, &width, &max_displayed_line);
+ /* 20: two tabstops + 4 */
+ if (width < 20 || max_displayed_line < 3)
+ bb_error_msg_and_die("too narrow here");
+ max_displayed_line -= 2;
+
+ buffer = xmalloc((max_displayed_line+1) * sizeof(char *));
+ if (option_mask32 & FLAG_TILDE)
+ empty_line_marker = "";
+
+ tcgetattr(kbd_fd, &term_orig);
+ signal(SIGTERM, sig_catcher);
+ signal(SIGINT, sig_catcher);
+ term_vi = term_orig;
+ term_vi.c_lflag &= ~(ICANON | ECHO);
+ term_vi.c_iflag &= ~(IXON | ICRNL);
+ /*term_vi.c_oflag &= ~ONLCR;*/
+ term_vi.c_cc[VMIN] = 1;
+ term_vi.c_cc[VTIME] = 0;
+
+ /* Want to do it just once, but it doesn't work, */
+ /* so we are redoing it (see code above). Mystery... */
+ /*tcsetattr(kbd_fd, TCSANOW, &term_vi);*/
+
+ reinitialize();
+ while (1) {
+ keypress = less_getch();
+ keypress_process(keypress);
+ }
+}
diff --git a/i/pc104/initrd/conf/busybox/miscutils/makedevs.c b/i/pc104/initrd/conf/busybox/miscutils/makedevs.c
new file mode 100644
index 0000000..aa000a7
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/miscutils/makedevs.c
@@ -0,0 +1,232 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * public domain -- Dave 'Kill a Cop' Cinege <dcinege@psychosis.com>
+ *
+ * makedevs
+ * Make ranges of device files quickly.
+ * known bugs: can't deal with alpha ranges
+ */
+
+#include "busybox.h"
+
+#ifdef CONFIG_FEATURE_MAKEDEVS_LEAF
+int makedevs_main(int argc, char **argv);
+int makedevs_main(int argc, char **argv)
+{
+ mode_t mode;
+ char *basedev, *type, *nodname, buf[255];
+ int Smajor, Sminor, S, E;
+
+ if (argc < 7 || *argv[1]=='-')
+ bb_show_usage();
+
+ basedev = argv[1];
+ type = argv[2];
+ Smajor = xatoi_u(argv[3]);
+ Sminor = xatoi_u(argv[4]);
+ S = xatoi_u(argv[5]);
+ E = xatoi_u(argv[6]);
+ nodname = argc == 8 ? basedev : buf;
+
+ mode = 0660;
+
+ switch (type[0]) {
+ case 'c':
+ mode |= S_IFCHR;
+ break;
+ case 'b':
+ mode |= S_IFBLK;
+ break;
+ case 'f':
+ mode |= S_IFIFO;
+ break;
+ default:
+ bb_show_usage();
+ }
+
+ while (S <= E) {
+ int sz;
+
+ sz = snprintf(buf, sizeof(buf), "%s%d", basedev, S);
+ if(sz<0 || sz>=sizeof(buf)) /* libc different */
+ bb_error_msg_and_die("%s too large", basedev);
+
+ /* if mode != S_IFCHR and != S_IFBLK third param in mknod() ignored */
+
+ if (mknod(nodname, mode, makedev(Smajor, Sminor)))
+ bb_error_msg("failed to create: %s", nodname);
+
+ if (nodname == basedev) /* ex. /dev/hda - to /dev/hda1 ... */
+ nodname = buf;
+ S++;
+ Sminor++;
+ }
+
+ return 0;
+}
+
+#elif defined CONFIG_FEATURE_MAKEDEVS_TABLE
+
+/* Licensed under the GPL v2 or later, see the file LICENSE in this tarball. */
+
+int makedevs_main(int argc, char **argv);
+int makedevs_main(int argc, char **argv)
+{
+ FILE *table = stdin;
+ char *rootdir = NULL;
+ char *line = NULL;
+ int linenum = 0;
+ int ret = EXIT_SUCCESS;
+
+ getopt32(argc, argv, "d:", &line);
+ if (line)
+ table = xfopen(line, "r");
+
+ if (optind >= argc || (rootdir=argv[optind])==NULL) {
+ bb_error_msg_and_die("root directory not specified");
+ }
+
+ xchdir(rootdir);
+
+ umask(0);
+
+ printf("rootdir=%s\n", rootdir);
+ if (line) {
+ printf("table='%s'\n", line);
+ } else {
+ printf("table=<stdin>\n");
+ }
+
+ while ((line = xmalloc_getline(table))) {
+ char type;
+ unsigned int mode = 0755;
+ unsigned int major = 0;
+ unsigned int minor = 0;
+ unsigned int count = 0;
+ unsigned int increment = 0;
+ unsigned int start = 0;
+ char name[41];
+ char user[41];
+ char group[41];
+ char *full_name;
+ uid_t uid;
+ gid_t gid;
+
+ linenum++;
+
+ if ((2 > sscanf(line, "%40s %c %o %40s %40s %u %u %u %u %u", name,
+ &type, &mode, user, group, &major,
+ &minor, &start, &increment, &count)) ||
+ ((major | minor | start | count | increment) > 255))
+ {
+ if (*line=='\0' || *line=='#' || isspace(*line))
+ continue;
+ bb_error_msg("line %d invalid: '%s'", linenum, line);
+ ret = EXIT_FAILURE;
+ continue;
+ }
+ if (name[0] == '#') {
+ continue;
+ }
+
+ gid = (*group) ? get_ug_id(group, xgroup2gid) : getgid();
+ uid = (*user) ? get_ug_id(user, xuname2uid) : getuid();
+ full_name = concat_path_file(rootdir, name);
+
+ if (type == 'd') {
+ bb_make_directory(full_name, mode | S_IFDIR, FILEUTILS_RECUR);
+ if (chown(full_name, uid, gid) == -1) {
+ bb_perror_msg("line %d: chown failed for %s", linenum, full_name);
+ ret = EXIT_FAILURE;
+ goto loop;
+ }
+ if ((mode != -1) && (chmod(full_name, mode) < 0)){
+ bb_perror_msg("line %d: chmod failed for %s", linenum, full_name);
+ ret = EXIT_FAILURE;
+ goto loop;
+ }
+ } else if (type == 'f') {
+ struct stat st;
+ if ((stat(full_name, &st) < 0 || !S_ISREG(st.st_mode))) {
+ bb_perror_msg("line %d: regular file '%s' does not exist", linenum, full_name);
+ ret = EXIT_FAILURE;
+ goto loop;
+ }
+ if (chown(full_name, uid, gid) == -1) {
+ bb_perror_msg("line %d: chown failed for %s", linenum, full_name);
+ ret = EXIT_FAILURE;
+ goto loop;
+ }
+ if ((mode != -1) && (chmod(full_name, mode) < 0)){
+ bb_perror_msg("line %d: chmod failed for %s", linenum, full_name);
+ ret = EXIT_FAILURE;
+ goto loop;
+ }
+ } else
+ {
+ dev_t rdev;
+
+ if (type == 'p') {
+ mode |= S_IFIFO;
+ }
+ else if (type == 'c') {
+ mode |= S_IFCHR;
+ }
+ else if (type == 'b') {
+ mode |= S_IFBLK;
+ } else {
+ bb_error_msg("line %d: unsupported file type %c", linenum, type);
+ ret = EXIT_FAILURE;
+ goto loop;
+ }
+
+ if (count > 0) {
+ int i;
+ char *full_name_inc;
+
+ full_name_inc = xmalloc(strlen(full_name) + 4);
+ for (i = start; i < count; i++) {
+ sprintf(full_name_inc, "%s%d", full_name, i);
+ rdev = makedev(major, minor + (i * increment - start));
+ if (mknod(full_name_inc, mode, rdev) == -1) {
+ bb_perror_msg("line %d: cannot create node %s", linenum, full_name_inc);
+ ret = EXIT_FAILURE;
+ }
+ else if (chown(full_name_inc, uid, gid) == -1) {
+ bb_perror_msg("line %d: chown failed for %s", linenum, full_name_inc);
+ ret = EXIT_FAILURE;
+ }
+ if ((mode != -1) && (chmod(full_name_inc, mode) < 0)){
+ bb_perror_msg("line %d: chmod failed for %s", linenum, full_name_inc);
+ ret = EXIT_FAILURE;
+ }
+ }
+ free(full_name_inc);
+ } else {
+ rdev = makedev(major, minor);
+ if (mknod(full_name, mode, rdev) == -1) {
+ bb_perror_msg("line %d: cannot create node %s", linenum, full_name);
+ ret = EXIT_FAILURE;
+ }
+ else if (chown(full_name, uid, gid) == -1) {
+ bb_perror_msg("line %d: chown failed for %s", linenum, full_name);
+ ret = EXIT_FAILURE;
+ }
+ if ((mode != -1) && (chmod(full_name, mode) < 0)){
+ bb_perror_msg("line %d: chmod failed for %s", linenum, full_name);
+ ret = EXIT_FAILURE;
+ }
+ }
+ }
+loop:
+ free(line);
+ free(full_name);
+ }
+ fclose(table);
+
+ return ret;
+}
+
+#else
+# error makedevs configuration error, either leaf or table must be selected
+#endif
diff --git a/i/pc104/initrd/conf/busybox/miscutils/mountpoint.c b/i/pc104/initrd/conf/busybox/miscutils/mountpoint.c
new file mode 100644
index 0000000..e380154
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/miscutils/mountpoint.c
@@ -0,0 +1,66 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * mountpoint implementation for busybox
+ *
+ * Copyright (C) 2005 Bernhard Fischer
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ *
+ * Based on sysvinit's mountpoint
+ */
+
+#include "busybox.h"
+
+int mountpoint_main(int argc, char **argv);
+int mountpoint_main(int argc, char **argv)
+{
+ struct stat st;
+ char *arg;
+ int opt = getopt32(argc, argv, "qdx");
+#define OPT_q (1)
+#define OPT_d (2)
+#define OPT_x (4)
+
+ if (optind != argc - 1)
+ bb_show_usage();
+
+ arg = argv[optind];
+
+ if ( (opt & OPT_x && stat(arg, &st) == 0) || (lstat(arg, &st) == 0) ) {
+ if (opt & OPT_x) {
+ if (S_ISBLK(st.st_mode)) {
+ printf("%u:%u\n", major(st.st_rdev),
+ minor(st.st_rdev));
+ return EXIT_SUCCESS;
+ } else {
+ if (opt & OPT_q)
+ putchar('\n');
+ else
+ bb_error_msg("%s: not a block device", arg);
+ }
+ return EXIT_FAILURE;
+ } else
+ if (S_ISDIR(st.st_mode)) {
+ dev_t st_dev = st.st_dev;
+ ino_t st_ino = st.st_ino;
+ char *p = xasprintf("%s/..", arg);
+
+ if (stat(p, &st) == 0) {
+ int ret = (st_dev != st.st_dev) ||
+ (st_dev == st.st_dev && st_ino == st.st_ino);
+ if (opt & OPT_d)
+ printf("%u:%u\n", major(st_dev), minor(st_dev));
+ else if (!(opt & OPT_q))
+ printf("%s is %sa mountpoint\n", arg, ret?"":"not ");
+ return !ret;
+ }
+ } else {
+ if (!(opt & OPT_q))
+ bb_error_msg("%s: not a directory", arg);
+ return EXIT_FAILURE;
+ }
+ }
+ if (!(opt & OPT_q))
+ bb_perror_msg("%s", arg);
+ return EXIT_FAILURE;
+}
diff --git a/i/pc104/initrd/conf/busybox/miscutils/mt.c b/i/pc104/initrd/conf/busybox/miscutils/mt.c
new file mode 100644
index 0000000..bed2969
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/miscutils/mt.c
@@ -0,0 +1,121 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "busybox.h"
+#include <sys/mtio.h>
+
+struct mt_opcodes {
+ const char *name;
+ short value;
+};
+
+/* missing: eod/seod, stoptions, stwrthreshold, densities */
+static const struct mt_opcodes opcodes[] = {
+ {"bsf", MTBSF},
+ {"bsfm", MTBSFM},
+ {"bsr", MTBSR},
+ {"bss", MTBSS},
+ {"datacompression", MTCOMPRESSION},
+ {"eom", MTEOM},
+ {"erase", MTERASE},
+ {"fsf", MTFSF},
+ {"fsfm", MTFSFM},
+ {"fsr", MTFSR},
+ {"fss", MTFSS},
+ {"load", MTLOAD},
+ {"lock", MTLOCK},
+ {"mkpart", MTMKPART},
+ {"nop", MTNOP},
+ {"offline", MTOFFL},
+ {"rewoffline", MTOFFL},
+ {"ras1", MTRAS1},
+ {"ras2", MTRAS2},
+ {"ras3", MTRAS3},
+ {"reset", MTRESET},
+ {"retension", MTRETEN},
+ {"rewind", MTREW},
+ {"seek", MTSEEK},
+ {"setblk", MTSETBLK},
+ {"setdensity", MTSETDENSITY},
+ {"drvbuffer", MTSETDRVBUFFER},
+ {"setpart", MTSETPART},
+ {"tell", MTTELL},
+ {"wset", MTWSM},
+ {"unload", MTUNLOAD},
+ {"unlock", MTUNLOCK},
+ {"eof", MTWEOF},
+ {"weof", MTWEOF},
+ {0, 0}
+};
+
+int mt_main(int argc, char **argv);
+int mt_main(int argc, char **argv)
+{
+ const char *file = "/dev/tape";
+ const struct mt_opcodes *code = opcodes;
+ struct mtop op;
+ struct mtpos position;
+ int fd, mode;
+
+ if (argc < 2) {
+ bb_show_usage();
+ }
+
+ if (strcmp(argv[1], "-f") == 0) {
+ if (argc < 4) {
+ bb_show_usage();
+ }
+ file = argv[2];
+ argv += 2;
+ argc -= 2;
+ }
+
+ while (code->name != 0) {
+ if (strcmp(code->name, argv[1]) == 0)
+ break;
+ code++;
+ }
+
+ if (code->name == 0) {
+ bb_error_msg("unrecognized opcode %s", argv[1]);
+ return EXIT_FAILURE;
+ }
+
+ op.mt_op = code->value;
+ if (argc >= 3)
+ op.mt_count = xatoi_u(argv[2]);
+ else
+ op.mt_count = 1; /* One, not zero, right? */
+
+ switch (code->value) {
+ case MTWEOF:
+ case MTERASE:
+ case MTWSM:
+ case MTSETDRVBUFFER:
+ mode = O_WRONLY;
+ break;
+
+ default:
+ mode = O_RDONLY;
+ break;
+ }
+
+ fd = xopen(file, mode);
+
+ switch (code->value) {
+ case MTTELL:
+ if (ioctl(fd, MTIOCPOS, &position) < 0)
+ bb_perror_msg_and_die("%s", file);
+ printf("At block %d.\n", (int) position.mt_blkno);
+ break;
+
+ default:
+ if (ioctl(fd, MTIOCTOP, &op) != 0)
+ bb_perror_msg_and_die("%s", file);
+ break;
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/i/pc104/initrd/conf/busybox/miscutils/nmeter.c b/i/pc104/initrd/conf/busybox/miscutils/nmeter.c
new file mode 100644
index 0000000..efc78d7
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/miscutils/nmeter.c
@@ -0,0 +1,849 @@
+/*
+** Licensed under the GPL v2, see the file LICENSE in this tarball
+**
+** Based on nanotop.c from floppyfw project
+**
+** Contact me: vda.linux@googlemail.com */
+
+//TODO:
+// simplify code
+// /proc/locks
+// /proc/stat:
+// disk_io: (3,0):(22272,17897,410702,4375,54750)
+// btime 1059401962
+
+#include "busybox.h"
+#include <time.h>
+
+typedef unsigned long long ullong;
+
+enum { proc_file_size = 4096 };
+
+typedef struct proc_file {
+ const char *name;
+ int gen;
+ char *file;
+} proc_file;
+
+static proc_file proc_stat = { "/proc/stat", -1 };
+static proc_file proc_loadavg = { "/proc/loadavg", -1 };
+static proc_file proc_net_dev = { "/proc/net/dev", -1 };
+static proc_file proc_meminfo = { "/proc/meminfo", -1 };
+static proc_file proc_diskstats = { "/proc/diskstats", -1 };
+// Sample #
+static int gen = -1;
+// Linux 2.6? (otherwise assumes 2.4)
+static int is26 = 0;
+static struct timeval tv;
+static int delta = 1000000;
+static int deltanz = 1000000;
+static int need_seconds = 0;
+static const char *final_str = "\n";
+
+// We depend on this being a char[], not char* - we take sizeof() of it
+#define outbuf bb_common_bufsiz1
+static char *cur_outbuf = outbuf;
+
+
+static inline void reset_outbuf(void)
+{
+ cur_outbuf = outbuf;
+}
+
+static inline int outbuf_count(void)
+{
+ return cur_outbuf - outbuf;
+}
+
+static void print_outbuf(void)
+{
+ int sz = cur_outbuf - outbuf;
+ if (sz > 0) {
+ write(1, outbuf, sz);
+ cur_outbuf = outbuf;
+ }
+}
+
+static void put(const char *s)
+{
+ int sz = strlen(s);
+ if (sz > outbuf + sizeof(outbuf) - cur_outbuf)
+ sz = outbuf + sizeof(outbuf) - cur_outbuf;
+ memcpy(cur_outbuf, s, sz);
+ cur_outbuf += sz;
+}
+
+static void put_c(char c)
+{
+ if (cur_outbuf < outbuf + sizeof(outbuf))
+ *cur_outbuf++ = c;
+}
+
+static void put_question_marks(int count)
+{
+ while (count--)
+ put_c('?');
+}
+
+static int readfile_z(char *buf, int sz, const char* fname)
+{
+ sz = open_read_close(fname, buf, sz-1);
+ if (sz < 0) {
+ buf[0] = '\0';
+ return 1;
+ }
+ buf[sz] = '\0';
+ return 0;
+}
+
+static const char* get_file(proc_file *pf)
+{
+ if (pf->gen != gen) {
+ pf->gen = gen;
+ // We allocate proc_file_size bytes. This wastes memory,
+ // but allows us to allocate only once (at first sample)
+ // per proc file, and reuse buffer for each sample
+ if (!pf->file)
+ pf->file = xmalloc(proc_file_size);
+ readfile_z(pf->file, proc_file_size, pf->name);
+ }
+ return pf->file;
+}
+
+static inline ullong read_after_slash(const char *p)
+{
+ p = strchr(p, '/');
+ if (!p) return 0;
+ return strtoull(p+1, NULL, 10);
+}
+
+enum conv_type { conv_decimal, conv_slash };
+
+// Reads decimal values from line. Values start after key, for example:
+// "cpu 649369 0 341297 4336769..." - key is "cpu" here.
+// Values are stored in vec[]. arg_ptr has list of positions
+// we are interested in: for example: 1,2,5 - we want 1st, 2nd and 5th value.
+static int vrdval(const char* p, const char* key,
+ enum conv_type conv, ullong *vec, va_list arg_ptr)
+{
+ int indexline;
+ int indexnext;
+
+ p = strstr(p, key);
+ if (!p) return 1;
+
+ p += strlen(key);
+ indexline = 1;
+ indexnext = va_arg(arg_ptr, int);
+ while (1) {
+ while (*p == ' ' || *p == '\t') p++;
+ if (*p == '\n' || *p == '\0') break;
+
+ if (indexline == indexnext) { // read this value
+ *vec++ = conv==conv_decimal ?
+ strtoull(p, NULL, 10) :
+ read_after_slash(p);
+ indexnext = va_arg(arg_ptr, int);
+ }
+ while (*p > ' ') p++; // skip over value
+ indexline++;
+ }
+ return 0;
+}
+
+// Parses files with lines like "cpu0 21727 0 15718 1813856 9461 10485 0 0":
+// rdval(file_contents, "string_to_find", result_vector, value#, value#...)
+// value# start with 1
+static int rdval(const char* p, const char* key, ullong *vec, ...)
+{
+ va_list arg_ptr;
+ int result;
+
+ va_start(arg_ptr, vec);
+ result = vrdval(p, key, conv_decimal, vec, arg_ptr);
+ va_end(arg_ptr);
+
+ return result;
+}
+
+// Parses files with lines like "... ... ... 3/148 ...."
+static int rdval_loadavg(const char* p, ullong *vec, ...)
+{
+ va_list arg_ptr;
+ int result;
+
+ va_start(arg_ptr, vec);
+ result = vrdval(p, "", conv_slash, vec, arg_ptr);
+ va_end(arg_ptr);
+
+ return result;
+}
+
+// Parses /proc/diskstats
+// 1 2 3 4 5 6(rd) 7 8 9 10(wr) 11 12 13 14
+// 3 0 hda 51292 14441 841783 926052 25717 79650 843256 3029804 0 148459 3956933
+// 3 1 hda1 0 0 0 0 <- ignore if only 4 fields
+static int rdval_diskstats(const char* p, ullong *vec)
+{
+ ullong rd = 0; // to avoid "warning: 'rd' might be used uninitialized"
+ int indexline = 0;
+ vec[0] = 0;
+ vec[1] = 0;
+ while (1) {
+ indexline++;
+ while (*p == ' ' || *p == '\t') p++;
+ if (*p == '\0') break;
+ if (*p == '\n') {
+ indexline = 0;
+ p++;
+ continue;
+ }
+ if (indexline == 6) {
+ rd = strtoull(p, NULL, 10);
+ } else if (indexline == 10) {
+ vec[0] += rd; // TODO: *sectorsize (don't know how to find out sectorsize)
+ vec[1] += strtoull(p, NULL, 10);
+ while (*p != '\n' && *p != '\0') p++;
+ continue;
+ }
+ while (*p > ' ') p++; // skip over value
+ }
+ return 0;
+}
+
+static void scale(ullong ul)
+{
+ char buf[5];
+ smart_ulltoa5(ul, buf);
+ put(buf);
+}
+
+
+#define S_STAT(a) \
+typedef struct a { \
+ struct s_stat *next; \
+ void (*collect)(struct a *s); \
+ const char *label;
+#define S_STAT_END(a) } a;
+
+S_STAT(s_stat)
+S_STAT_END(s_stat)
+
+static void collect_literal(s_stat *s)
+{
+}
+
+static s_stat* init_literal(void)
+{
+ s_stat *s = xmalloc(sizeof(s_stat));
+ s->collect = collect_literal;
+ return (s_stat*)s;
+}
+
+static s_stat* init_delay(const char *param)
+{
+ delta = strtol(param, NULL, 0)*1000;
+ deltanz = delta > 0 ? delta : 1;
+ need_seconds = (1000000%deltanz) != 0;
+ return (s_stat*)0;
+}
+
+static s_stat* init_cr(const char *param)
+{
+ final_str = "\r";
+ return (s_stat*)0;
+}
+
+
+// user nice system idle iowait irq softirq (last 3 only in 2.6)
+//cpu 649369 0 341297 4336769 11640 7122 1183
+//cpuN 649369 0 341297 4336769 11640 7122 1183
+enum { CPU_FIELDCNT = 7 };
+S_STAT(cpu_stat)
+ ullong old[CPU_FIELDCNT];
+ int bar_sz;
+ char *bar;
+S_STAT_END(cpu_stat)
+
+
+static void collect_cpu(cpu_stat *s)
+{
+ ullong data[CPU_FIELDCNT] = { 0, 0, 0, 0, 0, 0, 0 };
+ unsigned frac[CPU_FIELDCNT] = { 0, 0, 0, 0, 0, 0, 0 };
+ ullong all = 0;
+ int norm_all = 0;
+ int bar_sz = s->bar_sz;
+ char *bar = s->bar;
+ int i;
+
+ if (rdval(get_file(&proc_stat), "cpu ", data, 1, 2, 3, 4, 5, 6, 7)) {
+ put_question_marks(bar_sz);
+ return;
+ }
+
+ for (i=0; i<CPU_FIELDCNT; i++) {
+ ullong old = s->old[i];
+ if (data[i] < old) old = data[i]; //sanitize
+ s->old[i] = data[i];
+ all += (data[i] -= old);
+ }
+
+ if (all) {
+ for (i=0; i<CPU_FIELDCNT; i++) {
+ ullong t = bar_sz * data[i];
+ norm_all += data[i] = t / all;
+ frac[i] = t % all;
+ }
+
+ while (norm_all < bar_sz) {
+ unsigned max = frac[0];
+ int pos = 0;
+ for (i=1; i<CPU_FIELDCNT; i++) {
+ if (frac[i] > max) max = frac[i], pos = i;
+ }
+ frac[pos] = 0; //avoid bumping up same value twice
+ data[pos]++;
+ norm_all++;
+ }
+
+ memset(bar, '.', bar_sz);
+ memset(bar, 'S', data[2]); bar += data[2]; //sys
+ memset(bar, 'U', data[0]); bar += data[0]; //usr
+ memset(bar, 'N', data[1]); bar += data[1]; //nice
+ memset(bar, 'D', data[4]); bar += data[4]; //iowait
+ memset(bar, 'I', data[5]); bar += data[5]; //irq
+ memset(bar, 'i', data[6]); bar += data[6]; //softirq
+ } else {
+ memset(bar, '?', bar_sz);
+ }
+ put(s->bar);
+}
+
+
+static s_stat* init_cpu(const char *param)
+{
+ int sz;
+ cpu_stat *s = xmalloc(sizeof(cpu_stat));
+ s->collect = collect_cpu;
+ sz = strtol(param, NULL, 0);
+ if (sz < 10) sz = 10;
+ if (sz > 1000) sz = 1000;
+ s->bar = xmalloc(sz+1);
+ s->bar[sz] = '\0';
+ s->bar_sz = sz;
+ return (s_stat*)s;
+}
+
+
+S_STAT(int_stat)
+ ullong old;
+ int no;
+S_STAT_END(int_stat)
+
+static void collect_int(int_stat *s)
+{
+ ullong data[1];
+ ullong old;
+
+ if (rdval(get_file(&proc_stat), "intr", data, s->no)) {
+ put_question_marks(4);
+ return;
+ }
+
+ old = s->old;
+ if (data[0] < old) old = data[0]; //sanitize
+ s->old = data[0];
+ scale(data[0] - old);
+}
+
+static s_stat* init_int(const char *param)
+{
+ int_stat *s = xmalloc(sizeof(int_stat));
+ s->collect = collect_int;
+ if (param[0]=='\0') {
+ s->no = 1;
+ } else {
+ int n = strtoul(param, NULL, 0);
+ s->no = n+2;
+ }
+ return (s_stat*)s;
+}
+
+
+S_STAT(ctx_stat)
+ ullong old;
+S_STAT_END(ctx_stat)
+
+static void collect_ctx(ctx_stat *s)
+{
+ ullong data[1];
+ ullong old;
+
+ if (rdval(get_file(&proc_stat), "ctxt", data, 1)) {
+ put_question_marks(4);
+ return;
+ }
+
+ old = s->old;
+ if (data[0] < old) old = data[0]; //sanitize
+ s->old = data[0];
+ scale(data[0] - old);
+}
+
+static s_stat* init_ctx(const char *param)
+{
+ ctx_stat *s = xmalloc(sizeof(ctx_stat));
+ s->collect = collect_ctx;
+ return (s_stat*)s;
+}
+
+
+S_STAT(blk_stat)
+ const char* lookfor;
+ ullong old[2];
+S_STAT_END(blk_stat)
+
+static void collect_blk(blk_stat *s)
+{
+ ullong data[2];
+ int i;
+
+ if (is26) {
+ i = rdval_diskstats(get_file(&proc_diskstats), data);
+ } else {
+ i = rdval(get_file(&proc_stat), s->lookfor, data, 1, 2);
+ // Linux 2.4 reports bio in Kbytes, convert to sectors:
+ data[0] *= 2;
+ data[1] *= 2;
+ }
+ if (i) {
+ put_question_marks(9);
+ return;
+ }
+
+ for (i=0; i<2; i++) {
+ ullong old = s->old[i];
+ if (data[i] < old) old = data[i]; //sanitize
+ s->old[i] = data[i];
+ data[i] -= old;
+ }
+ scale(data[0]*512); // TODO: *sectorsize
+ put_c(' ');
+ scale(data[1]*512);
+}
+
+static s_stat* init_blk(const char *param)
+{
+ blk_stat *s = xmalloc(sizeof(blk_stat));
+ s->collect = collect_blk;
+ s->lookfor = "page";
+ return (s_stat*)s;
+}
+
+
+S_STAT(fork_stat)
+ ullong old;
+S_STAT_END(fork_stat)
+
+static void collect_thread_nr(fork_stat *s)
+{
+ ullong data[1];
+
+ if (rdval_loadavg(get_file(&proc_loadavg), data, 4)) {
+ put_question_marks(4);
+ return;
+ }
+ scale(data[0]);
+}
+
+static void collect_fork(fork_stat *s)
+{
+ ullong data[1];
+ ullong old;
+
+ if (rdval(get_file(&proc_stat), "processes", data, 1)) {
+ put_question_marks(4);
+ return;
+ }
+
+ old = s->old;
+ if (data[0] < old) old = data[0]; //sanitize
+ s->old = data[0];
+ scale(data[0] - old);
+}
+
+static s_stat* init_fork(const char *param)
+{
+ fork_stat *s = xmalloc(sizeof(fork_stat));
+ if (*param == 'n') {
+ s->collect = collect_thread_nr;
+ } else {
+ s->collect = collect_fork;
+ }
+ return (s_stat*)s;
+}
+
+
+S_STAT(if_stat)
+ ullong old[4];
+ const char *device;
+ char *device_colon;
+S_STAT_END(if_stat)
+
+static void collect_if(if_stat *s)
+{
+ ullong data[4];
+ int i;
+
+ if (rdval(get_file(&proc_net_dev), s->device_colon, data, 1, 3, 9, 11)) {
+ put_question_marks(10);
+ return;
+ }
+
+ for (i=0; i<4; i++) {
+ ullong old = s->old[i];
+ if (data[i] < old) old = data[i]; //sanitize
+ s->old[i] = data[i];
+ data[i] -= old;
+ }
+ put_c(data[1] ? '*' : ' ');
+ scale(data[0]);
+ put_c(data[3] ? '*' : ' ');
+ scale(data[2]);
+}
+
+static s_stat* init_if(const char *device)
+{
+ if_stat *s = xmalloc(sizeof(if_stat));
+
+ if (!device || !device[0])
+ bb_show_usage();
+ s->collect = collect_if;
+
+ s->device = device;
+ s->device_colon = xmalloc(strlen(device)+2);
+ strcpy(s->device_colon, device);
+ strcat(s->device_colon, ":");
+ return (s_stat*)s;
+}
+
+
+S_STAT(mem_stat)
+ char opt;
+S_STAT_END(mem_stat)
+
+// "Memory" value should not include any caches.
+// IOW: neither "ls -laR /" nor heavy read/write activity
+// should affect it. We'd like to also include any
+// long-term allocated kernel-side mem, but it is hard
+// to figure out. For now, bufs, cached & slab are
+// counted as "free" memory
+//2.6.16:
+//MemTotal: 773280 kB
+//MemFree: 25912 kB - genuinely free
+//Buffers: 320672 kB - cache
+//Cached: 146396 kB - cache
+//SwapCached: 0 kB
+//Active: 183064 kB
+//Inactive: 356892 kB
+//HighTotal: 0 kB
+//HighFree: 0 kB
+//LowTotal: 773280 kB
+//LowFree: 25912 kB
+//SwapTotal: 131064 kB
+//SwapFree: 131064 kB
+//Dirty: 48 kB
+//Writeback: 0 kB
+//Mapped: 96620 kB
+//Slab: 200668 kB - takes 7 Mb on my box fresh after boot,
+// but includes dentries and inodes
+// (== can take arbitrary amount of mem)
+//CommitLimit: 517704 kB
+//Committed_AS: 236776 kB
+//PageTables: 1248 kB
+//VmallocTotal: 516052 kB
+//VmallocUsed: 3852 kB
+//VmallocChunk: 512096 kB
+//HugePages_Total: 0
+//HugePages_Free: 0
+//Hugepagesize: 4096 kB
+static void collect_mem(mem_stat *s)
+{
+ ullong m_total = 0;
+ ullong m_free = 0;
+ ullong m_bufs = 0;
+ ullong m_cached = 0;
+ ullong m_slab = 0;
+
+ if (rdval(get_file(&proc_meminfo), "MemTotal:", &m_total, 1)) {
+ put_question_marks(4);
+ return;
+ }
+ if (s->opt == 'f') {
+ scale(m_total << 10);
+ return;
+ }
+
+ if (rdval(proc_meminfo.file, "MemFree:", &m_free , 1)
+ || rdval(proc_meminfo.file, "Buffers:", &m_bufs , 1)
+ || rdval(proc_meminfo.file, "Cached:", &m_cached, 1)
+ || rdval(proc_meminfo.file, "Slab:", &m_slab , 1)
+ ) {
+ put_question_marks(4);
+ return;
+ }
+
+ m_free += m_bufs + m_cached + m_slab;
+ switch (s->opt) {
+ case 'f':
+ scale(m_free << 10); break;
+ default:
+ scale((m_total - m_free) << 10); break;
+ }
+}
+
+static s_stat* init_mem(const char *param)
+{
+ mem_stat *s = xmalloc(sizeof(mem_stat));
+ s->collect = collect_mem;
+ s->opt = param[0];
+ return (s_stat*)s;
+}
+
+
+S_STAT(swp_stat)
+S_STAT_END(swp_stat)
+
+static void collect_swp(swp_stat *s)
+{
+ ullong s_total[1];
+ ullong s_free[1];
+ if (rdval(get_file(&proc_meminfo), "SwapTotal:", s_total, 1)
+ || rdval(proc_meminfo.file, "SwapFree:" , s_free, 1)
+ ) {
+ put_question_marks(4);
+ return;
+ }
+ scale((s_total[0]-s_free[0]) << 10);
+}
+
+static s_stat* init_swp(const char *param)
+{
+ swp_stat *s = xmalloc(sizeof(swp_stat));
+ s->collect = collect_swp;
+ return (s_stat*)s;
+}
+
+
+S_STAT(fd_stat)
+S_STAT_END(fd_stat)
+
+static void collect_fd(fd_stat *s)
+{
+ char file[4096];
+ ullong data[2];
+
+ readfile_z(file, sizeof(file), "/proc/sys/fs/file-nr");
+ if (rdval(file, "", data, 1, 2)) {
+ put_question_marks(4);
+ return;
+ }
+
+ scale(data[0] - data[1]);
+}
+
+static s_stat* init_fd(const char *param)
+{
+ fd_stat *s = xmalloc(sizeof(fd_stat));
+ s->collect = collect_fd;
+ return (s_stat*)s;
+}
+
+
+S_STAT(time_stat)
+ int prec;
+ int scale;
+S_STAT_END(time_stat)
+
+static void collect_time(time_stat *s)
+{
+ char buf[sizeof("12:34:56.123456")];
+ struct tm* tm;
+ int us = tv.tv_usec + s->scale/2;
+ time_t t = tv.tv_sec;
+
+ if (us >= 1000000) {
+ t++;
+ us -= 1000000;
+ }
+ tm = localtime(&t);
+
+ sprintf(buf, "%02d:%02d:%02d", tm->tm_hour, tm->tm_min, tm->tm_sec);
+ if (s->prec)
+ sprintf(buf+8, ".%0*d", s->prec, us / s->scale);
+ put(buf);
+}
+
+static s_stat* init_time(const char *param)
+{
+ int prec;
+ time_stat *s = xmalloc(sizeof(time_stat));
+
+ s->collect = collect_time;
+ prec = param[0]-'0';
+ if (prec < 0) prec = 0;
+ else if (prec > 6) prec = 6;
+ s->prec = prec;
+ s->scale = 1;
+ while (prec++ < 6)
+ s->scale *= 10;
+ return (s_stat*)s;
+}
+
+static void collect_info(s_stat *s)
+{
+ gen++;
+ while (s) {
+ put(s->label);
+ s->collect(s);
+ s = s->next;
+ }
+}
+
+
+typedef s_stat* init_func(const char *param);
+
+static const char options[] = "ncmsfixptbdr";
+static init_func* init_functions[] = {
+ init_if,
+ init_cpu,
+ init_mem,
+ init_swp,
+ init_fd,
+ init_int,
+ init_ctx,
+ init_fork,
+ init_time,
+ init_blk,
+ init_delay,
+ init_cr,
+};
+
+int nmeter_main(int argc, char* argv[]);
+int nmeter_main(int argc, char* argv[])
+{
+ char buf[32];
+ s_stat *first = NULL;
+ s_stat *last = NULL;
+ s_stat *s;
+ char *cur, *prev;
+
+ if (argc != 2)
+ bb_show_usage();
+
+ if (open_read_close("/proc/version", buf, sizeof(buf)) > 0)
+ is26 = (strstr(buf, " 2.4.")==NULL);
+
+ // Can use argv[1] directly, but this will mess up
+ // parameters as seen by e.g. ps. Making a copy...
+ cur = xstrdup(argv[1]);
+ while (1) {
+ char *param, *p;
+ prev = cur;
+again:
+ cur = strchr(cur, '%');
+ if (!cur)
+ break;
+ if (cur[1]=='%') { // %%
+ strcpy(cur, cur+1);
+ cur++;
+ goto again;
+ }
+ *cur++ = '\0'; // overwrite %
+ if (cur[0] == '[') {
+ // format: %[foptstring]
+ cur++;
+ p = strchr(options, cur[0]);
+ param = cur+1;
+ while (cur[0] != ']') {
+ if (!cur[0])
+ bb_show_usage();
+ cur++;
+ }
+ *cur++ = '\0'; // overwrite [
+ } else {
+ // format: %NNNNNNf
+ param = cur;
+ while (cur[0] >= '0' && cur[0] <= '9')
+ cur++;
+ if (!cur[0])
+ bb_show_usage();
+ p = strchr(options, cur[0]);
+ *cur++ = '\0'; // overwrite format char
+ }
+ if (!p)
+ bb_show_usage();
+ s = init_functions[p-options](param);
+ if (s) {
+ s->label = prev;
+ s->next = 0;
+ if (!first)
+ first = s;
+ else
+ last->next = s;
+ last = s;
+ } else {
+ // %NNNNd or %r option. remove it from string
+ strcpy(prev + strlen(prev), cur);
+ cur = prev;
+ }
+ }
+ if (prev[0]) {
+ s = init_literal();
+ s->label = prev;
+ s->next = 0;
+ if (!first)
+ first = s;
+ else
+ last->next = s;
+ last = s;
+ }
+
+ // Generate first samples but do not print them, they're bogus
+ collect_info(first);
+ reset_outbuf();
+ if (delta >= 0) {
+ gettimeofday(&tv, 0);
+ usleep(delta > 1000000 ? 1000000 : delta - tv.tv_usec%deltanz);
+ }
+
+ while (1) {
+ gettimeofday(&tv, 0);
+ collect_info(first);
+ put(final_str);
+ print_outbuf();
+
+ // Negative delta -> no usleep at all
+ // This will hog the CPU but you can have REALLY GOOD
+ // time resolution ;)
+ // TODO: detect and avoid useless updates
+ // (like: nothing happens except time)
+ if (delta >= 0) {
+ int rem;
+ // can be commented out, will sacrifice sleep time precision a bit
+ gettimeofday(&tv, 0);
+ if (need_seconds)
+ rem = delta - ((ullong)tv.tv_sec*1000000+tv.tv_usec)%deltanz;
+ else
+ rem = delta - tv.tv_usec%deltanz;
+ // Sometimes kernel wakes us up just a tiny bit earlier than asked
+ // Do not go to very short sleep in this case
+ if (rem < delta/128) {
+ rem += delta;
+ }
+ usleep(rem);
+ }
+ }
+
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/miscutils/raidautorun.c b/i/pc104/initrd/conf/busybox/miscutils/raidautorun.c
new file mode 100644
index 0000000..c95b00a
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/miscutils/raidautorun.c
@@ -0,0 +1,27 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * raidautorun implementation for busybox
+ *
+ * Copyright (C) 2006 Bernhard Fischer
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ *
+ */
+
+#include "busybox.h"
+
+#include <linux/major.h>
+#include <linux/raid/md_u.h>
+
+int raidautorun_main(int argc, char **argv);
+int raidautorun_main(int argc, char **argv)
+{
+ if (argc != 2)
+ bb_show_usage();
+
+ if (ioctl(xopen(argv[1], O_RDONLY), RAID_AUTORUN, NULL) != 0) {
+ bb_perror_msg_and_die("ioctl");
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/i/pc104/initrd/conf/busybox/miscutils/readahead.c b/i/pc104/initrd/conf/busybox/miscutils/readahead.c
new file mode 100644
index 0000000..601e845
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/miscutils/readahead.c
@@ -0,0 +1,35 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * readahead implementation for busybox
+ *
+ * Preloads the given files in RAM, to reduce access time.
+ * Does this by calling the readahead(2) system call.
+ *
+ * Copyright (C) 2006 Michael Opdenacker <michael@free-electrons.com>
+ *
+ * Licensed under GPLv2 or later, see file License in this tarball for details.
+ */
+
+#include "busybox.h"
+
+int readahead_main(int argc, char **argv);
+int readahead_main(int argc, char **argv)
+{
+ FILE *f;
+ int retval = EXIT_SUCCESS;
+
+ if (argc == 1) bb_show_usage();
+
+ while (*++argv) {
+ if ((f = fopen_or_warn(*argv, "r")) != NULL) {
+ int r, fd=fileno(f);
+
+ r = readahead(fd, 0, fdlength(fd));
+ fclose(f);
+ if (r >= 0) continue;
+ }
+ retval = EXIT_FAILURE;
+ }
+
+ return retval;
+}
diff --git a/i/pc104/initrd/conf/busybox/miscutils/runlevel.c b/i/pc104/initrd/conf/busybox/miscutils/runlevel.c
new file mode 100644
index 0000000..23714f7
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/miscutils/runlevel.c
@@ -0,0 +1,43 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * runlevel Prints out the previous and the current runlevel.
+ *
+ * Version: @(#)runlevel 1.20 16-Apr-1997 MvS
+ *
+ * This file is part of the sysvinit suite,
+ * Copyright 1991-1997 Miquel van Smoorenburg.
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ *
+ * initially busyboxified by Bernhard Fischer
+ */
+
+#include "busybox.h"
+#include <stdio.h>
+#include <utmp.h>
+#include <time.h>
+#include <stdlib.h>
+
+int runlevel_main(int argc, char *argv[]);
+int runlevel_main(int argc, char *argv[])
+{
+ struct utmp *ut;
+ char prev;
+
+ if (argc > 1) utmpname(argv[1]);
+
+ setutent();
+ while ((ut = getutent()) != NULL) {
+ if (ut->ut_type == RUN_LVL) {
+ prev = ut->ut_pid / 256;
+ if (prev == 0) prev = 'N';
+ printf("%c %c\n", prev, ut->ut_pid % 256);
+ endutent();
+ return 0;
+ }
+ }
+
+ puts("unknown");
+ endutent();
+ return 1;
+}
diff --git a/i/pc104/initrd/conf/busybox/miscutils/rx.c b/i/pc104/initrd/conf/busybox/miscutils/rx.c
new file mode 100644
index 0000000..29848b1
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/miscutils/rx.c
@@ -0,0 +1,290 @@
+/* vi: set sw=4 ts=4: */
+/*-------------------------------------------------------------------------
+ * Filename: xmodem.c
+ * Copyright: Copyright (C) 2001, Hewlett-Packard Company
+ * Author: Christopher Hoover <ch@hpl.hp.com>
+ * Description: xmodem functionality for uploading of kernels
+ * and the like
+ * Created at: Thu Dec 20 01:58:08 PST 2001
+ *-----------------------------------------------------------------------*/
+/*
+ * xmodem.c: xmodem functionality for uploading of kernels and
+ * the like
+ *
+ * Copyright (C) 2001 Hewlett-Packard Laboratories
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ *
+ * This was originally written for blob and then adapted for busybox.
+ *
+ */
+
+#include "busybox.h"
+
+#define SOH 0x01
+#define STX 0x02
+#define EOT 0x04
+#define ACK 0x06
+#define NAK 0x15
+#define BS 0x08
+
+/*
+
+Cf:
+
+ http://www.textfiles.com/apple/xmodem
+ http://www.phys.washington.edu/~belonis/xmodem/docxmodem.txt
+ http://www.phys.washington.edu/~belonis/xmodem/docymodem.txt
+ http://www.phys.washington.edu/~belonis/xmodem/modmprot.col
+
+*/
+
+#define TIMEOUT 1
+#define TIMEOUT_LONG 10
+#define MAXERRORS 10
+
+static int read_byte(int fd, unsigned int timeout) {
+ char buf[1];
+ int n;
+
+ alarm(timeout);
+
+ n = read(fd, &buf, 1);
+
+ alarm(0);
+
+ if (n == 1)
+ return buf[0] & 0xff;
+ else
+ return -1;
+}
+
+static int receive(char *error_buf, size_t error_buf_size,
+ int ttyfd, int filefd)
+{
+ char blockBuf[1024];
+ unsigned int errors = 0;
+ unsigned int wantBlockNo = 1;
+ unsigned int length = 0;
+ int docrc = 1;
+ char nak = 'C';
+ unsigned int timeout = TIMEOUT_LONG;
+
+#define note_error(fmt,args...) \
+ snprintf(error_buf, error_buf_size, fmt,##args)
+
+ /* Flush pending input */
+ tcflush(ttyfd, TCIFLUSH);
+
+ /* Ask for CRC; if we get errors, we will go with checksum */
+ write(ttyfd, &nak, 1);
+
+ for (;;) {
+ int blockBegin;
+ int blockNo, blockNoOnesCompl;
+ int blockLength;
+ int cksum = 0;
+ int crcHi = 0;
+ int crcLo = 0;
+
+ blockBegin = read_byte(ttyfd, timeout);
+ if (blockBegin < 0)
+ goto timeout;
+
+ timeout = TIMEOUT;
+ nak = NAK;
+
+ switch (blockBegin) {
+ case SOH:
+ case STX:
+ break;
+
+ case EOT:
+ nak = ACK;
+ write(ttyfd, &nak, 1);
+ goto done;
+
+ default:
+ goto error;
+ }
+
+ /* block no */
+ blockNo = read_byte(ttyfd, TIMEOUT);
+ if (blockNo < 0)
+ goto timeout;
+
+ /* block no one's compliment */
+ blockNoOnesCompl = read_byte(ttyfd, TIMEOUT);
+ if (blockNoOnesCompl < 0)
+ goto timeout;
+
+ if (blockNo != (255 - blockNoOnesCompl)) {
+ note_error("bad block ones compl");
+ goto error;
+ }
+
+ blockLength = (blockBegin == SOH) ? 128 : 1024;
+
+ {
+ int i;
+
+ for (i = 0; i < blockLength; i++) {
+ int cc = read_byte(ttyfd, TIMEOUT);
+ if (cc < 0)
+ goto timeout;
+ blockBuf[i] = cc;
+ }
+ }
+
+ if (docrc) {
+ crcHi = read_byte(ttyfd, TIMEOUT);
+ if (crcHi < 0)
+ goto timeout;
+
+ crcLo = read_byte(ttyfd, TIMEOUT);
+ if (crcLo < 0)
+ goto timeout;
+ } else {
+ cksum = read_byte(ttyfd, TIMEOUT);
+ if (cksum < 0)
+ goto timeout;
+ }
+
+ if (blockNo == ((wantBlockNo - 1) & 0xff)) {
+ /* a repeat of the last block is ok, just ignore it. */
+ /* this also ignores the initial block 0 which is */
+ /* meta data. */
+ goto next;
+ } else if (blockNo != (wantBlockNo & 0xff)) {
+ note_error("unexpected block no, 0x%08x, expecting 0x%08x", blockNo, wantBlockNo);
+ goto error;
+ }
+
+ if (docrc) {
+ int crc = 0;
+ int i, j;
+ int expectedCrcHi;
+ int expectedCrcLo;
+
+ for (i = 0; i < blockLength; i++) {
+ crc = crc ^ (int) blockBuf[i] << 8;
+ for (j = 0; j < 8; j++)
+ if (crc & 0x8000)
+ crc = crc << 1 ^ 0x1021;
+ else
+ crc = crc << 1;
+ }
+
+ expectedCrcHi = (crc >> 8) & 0xff;
+ expectedCrcLo = crc & 0xff;
+
+ if ((crcHi != expectedCrcHi) ||
+ (crcLo != expectedCrcLo)) {
+ note_error("crc error, expected 0x%02x 0x%02x, got 0x%02x 0x%02x", expectedCrcHi, expectedCrcLo, crcHi, crcLo);
+ goto error;
+ }
+ } else {
+ unsigned char expectedCksum = 0;
+ int i;
+
+ for (i = 0; i < blockLength; i++)
+ expectedCksum += blockBuf[i];
+
+ if (cksum != expectedCksum) {
+ note_error("checksum error, expected 0x%02x, got 0x%02x", expectedCksum, cksum);
+ goto error;
+ }
+ }
+
+ wantBlockNo++;
+ length += blockLength;
+
+ if (full_write(filefd, blockBuf, blockLength) < 0) {
+ note_error("write to file failed: %m");
+ goto fatal;
+ }
+
+ next:
+ errors = 0;
+ nak = ACK;
+ write(ttyfd, &nak, 1);
+ continue;
+
+ error:
+ timeout:
+ errors++;
+ if (errors == MAXERRORS) {
+ /* Abort */
+
+ // if using crc, try again w/o crc
+ if (nak == 'C') {
+ nak = NAK;
+ errors = 0;
+ docrc = 0;
+ goto timeout;
+ }
+
+ note_error("too many errors; giving up");
+
+ fatal:
+ /* 5 CAN followed by 5 BS */
+ write(ttyfd, "\030\030\030\030\030\010\010\010\010\010", 10);
+ return -1;
+ }
+
+ /* Flush pending input */
+ tcflush(ttyfd, TCIFLUSH);
+
+ write(ttyfd, &nak, 1);
+ }
+
+ done:
+ return length;
+
+#undef note_error
+}
+
+static void sigalrm_handler(int ATTRIBUTE_UNUSED signum)
+{
+}
+
+int rx_main(int argc, char **argv);
+int rx_main(int argc, char **argv)
+{
+ char *fn;
+ int ttyfd, filefd;
+ struct termios tty, orig_tty;
+ struct sigaction act;
+ int n;
+ char error_buf[256];
+
+ if (argc != 2)
+ bb_show_usage();
+
+ fn = argv[1];
+ ttyfd = xopen(CURRENT_TTY, O_RDWR);
+ filefd = xopen(fn, O_RDWR|O_CREAT|O_TRUNC);
+
+ if (tcgetattr(ttyfd, &tty) < 0)
+ bb_perror_msg_and_die("tcgetattr");
+
+ orig_tty = tty;
+
+ cfmakeraw(&tty);
+ tcsetattr(ttyfd, TCSAFLUSH, &tty);
+
+ memset(&act, 0, sizeof(act));
+ act.sa_handler = sigalrm_handler;
+ sigaction(SIGALRM, &act, 0);
+
+ n = receive(error_buf, sizeof(error_buf), ttyfd, filefd);
+
+ close(filefd);
+
+ tcsetattr(ttyfd, TCSAFLUSH, &orig_tty);
+
+ if (n < 0)
+ bb_error_msg_and_die("\nreceive failed:\n %s", error_buf);
+
+ fflush_stdout_and_exit(EXIT_SUCCESS);
+}
diff --git a/i/pc104/initrd/conf/busybox/miscutils/setsid.c b/i/pc104/initrd/conf/busybox/miscutils/setsid.c
new file mode 100644
index 0000000..6db07be
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/miscutils/setsid.c
@@ -0,0 +1,42 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * setsid.c -- execute a command in a new session
+ * Rick Sladkey <jrs@world.std.com>
+ * In the public domain.
+ *
+ * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
+ * - added Native Language Support
+ *
+ * 2001-01-18 John Fremlin <vii@penguinpowered.com>
+ * - fork in case we are process group leader
+ *
+ * 2004-11-12 Paul Fox
+ * - busyboxed
+ */
+
+#include "busybox.h"
+
+int setsid_main(int argc, char *argv[]);
+int setsid_main(int argc, char *argv[])
+{
+ if (argc < 2)
+ bb_show_usage();
+
+ if (getpgrp() == getpid()) {
+ switch (fork()) {
+ case -1:
+ bb_perror_msg_and_die("fork");
+ case 0:
+ break;
+ default: /* parent */
+ exit(0);
+ }
+ }
+ /* child */
+
+ setsid(); /* no error possible */
+
+ BB_EXECVP(argv[1], argv + 1);
+
+ bb_perror_msg_and_die("%s", argv[1]);
+}
diff --git a/i/pc104/initrd/conf/busybox/miscutils/strings.c b/i/pc104/initrd/conf/busybox/miscutils/strings.c
new file mode 100644
index 0000000..49f2cf3
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/miscutils/strings.c
@@ -0,0 +1,89 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * strings implementation for busybox
+ *
+ * Copyright Tito Ragusa <farmatito@tiscali.it>
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ */
+
+#include "busybox.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <ctype.h>
+
+#define WHOLE_FILE 1
+#define PRINT_NAME 2
+#define PRINT_OFFSET 4
+#define SIZE 8
+
+int strings_main(int argc, char **argv);
+int strings_main(int argc, char **argv)
+{
+ int n, c, i = 0, status = EXIT_SUCCESS;
+ unsigned opt;
+ unsigned long count;
+ FILE *file = stdin;
+ char *string;
+ const char *fmt = "%s: ";
+ const char *n_arg = "4";
+
+ opt = getopt32(argc, argv, "afon:", &n_arg);
+ /* -a is our default behaviour */
+
+ argc -= optind;
+ argv += optind;
+
+ n = xatoul_range(n_arg, 1, INT_MAX);
+ string = xzalloc(n + 1);
+ n--;
+
+ if (argc == 0) {
+ fmt = "{%s}: ";
+ *argv = (char *)bb_msg_standard_input;
+ goto PIPE;
+ }
+
+ do {
+ file = fopen_or_warn(*argv, "r");
+ if (file) {
+PIPE:
+ count = 0;
+ do {
+ c = fgetc(file);
+ if (isprint(c) || c == '\t') {
+ if (i <= n) {
+ string[i] = c;
+ } else {
+ putchar(c);
+ }
+ if (i == n) {
+ if (opt & PRINT_NAME) {
+ printf(fmt, *argv);
+ }
+ if (opt & PRINT_OFFSET) {
+ printf("%7lo ", count - n);
+ }
+ printf("%s", string);
+ }
+ i++;
+ } else {
+ if (i > n) {
+ putchar('\n');
+ }
+ i = 0;
+ }
+ count++;
+ } while (c != EOF);
+ fclose_if_not_stdin(file);
+ } else {
+ status = EXIT_FAILURE;
+ }
+ } while (--argc > 0);
+
+ if (ENABLE_FEATURE_CLEAN_UP)
+ free(string);
+
+ fflush_stdout_and_exit(status);
+}
diff --git a/i/pc104/initrd/conf/busybox/miscutils/taskset.c b/i/pc104/initrd/conf/busybox/miscutils/taskset.c
new file mode 100644
index 0000000..b3bf490
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/miscutils/taskset.c
@@ -0,0 +1,99 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * taskset - retrieve or set a processes' CPU affinity
+ * Copyright (c) 2006 Bernhard Fischer
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "busybox.h"
+#include <sched.h>
+#include <getopt.h> /* optind */
+
+#if ENABLE_FEATURE_TASKSET_FANCY
+#define TASKSET_PRINTF_MASK "%s"
+#define from_cpuset(x) __from_cpuset(&x)
+/* craft a string from the mask */
+static char *__from_cpuset(cpu_set_t *mask)
+{
+ int i;
+ char *ret = 0, *str = xzalloc(9);
+
+ for (i = CPU_SETSIZE - 4; i >= 0; i -= 4) {
+ char val = 0;
+ int off;
+ for (off = 0; off <= 3; ++off)
+ if (CPU_ISSET(i+off, mask))
+ val |= 1<<off;
+
+ if (!ret && val)
+ ret = str;
+ *str++ = (val-'0'<=9) ? (val+48) : (val+87);
+ }
+ return ret;
+}
+#else
+#define TASKSET_PRINTF_MASK "%x"
+/* (void*) cast is for battling gcc: */
+/* "dereferencing type-punned pointer will break strict-aliasing rules" */
+#define from_cpuset(mask) (*(unsigned*)(void*)&(mask))
+#endif
+
+#define OPT_p 1
+
+int taskset_main(int argc, char** argv);
+int taskset_main(int argc, char** argv)
+{
+ cpu_set_t mask, new_mask;
+ pid_t pid = 0;
+ unsigned opt;
+ const char *state = "current\0new";
+ char *p_opt = NULL, *aff = NULL;
+
+ opt = getopt32(argc, argv, "+p:", &p_opt);
+
+ if (opt & OPT_p) {
+ if (argc == optind+1) { /* -p <aff> <pid> */
+ aff = p_opt;
+ p_opt = argv[optind];
+ }
+ argv += optind; /* me -p <arg> */
+ pid = xatoul_range(p_opt, 1, ULONG_MAX); /* -p <pid> */
+ } else
+ aff = *++argv; /* <aff> <cmd...> */
+ if (aff) {
+ unsigned i = 0;
+ unsigned long l = xstrtol_range(aff, 0, 1, LONG_MAX);
+
+ CPU_ZERO(&new_mask);
+ while (i < CPU_SETSIZE && l >= (1<<i)) {
+ if ((1<<i) & l)
+ CPU_SET(i, &new_mask);
+ ++i;
+ }
+ }
+
+ if (opt & OPT_p) {
+ print_aff:
+ if (sched_getaffinity(pid, sizeof(mask), &mask) < 0)
+ bb_perror_msg_and_die("failed to %cet pid %d's affinity", 'g', pid);
+ printf("pid %d's %s affinity mask: "TASKSET_PRINTF_MASK"\n",
+ pid, state, from_cpuset(mask));
+ if (!*argv) /* no new affinity given or we did print already, done. */
+ return EXIT_SUCCESS;
+ }
+
+ if (sched_setaffinity(pid, sizeof(new_mask), &new_mask))
+ bb_perror_msg_and_die("failed to %cet pid %d's affinity", 's', pid);
+ if (opt & OPT_p) {
+ state += 8;
+ ++argv;
+ goto print_aff;
+ }
+ ++argv;
+ BB_EXECVP(*argv, argv);
+ bb_perror_msg_and_die("%s", *argv);
+}
+#undef OPT_p
+#undef TASKSET_PRINTF_MASK
+#undef from_cpuset
diff --git a/i/pc104/initrd/conf/busybox/miscutils/time.c b/i/pc104/initrd/conf/busybox/miscutils/time.c
new file mode 100644
index 0000000..a459195
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/miscutils/time.c
@@ -0,0 +1,474 @@
+/* vi: set sw=4 ts=4: */
+/* `time' utility to display resource usage of processes.
+ Copyright (C) 1990, 91, 92, 93, 96 Free Software Foundation, Inc.
+
+ Licensed under GPL version 2, see file LICENSE in this tarball for details.
+*/
+/* Originally written by David Keppel <pardo@cs.washington.edu>.
+ Heavily modified by David MacKenzie <djm@gnu.ai.mit.edu>.
+ Heavily modified for busybox by Erik Andersen <andersen@codepoet.org>
+*/
+
+#include "busybox.h"
+
+#define TV_MSEC tv_usec / 1000
+
+/* Information on the resources used by a child process. */
+typedef struct {
+ int waitstatus;
+ struct rusage ru;
+ struct timeval start, elapsed; /* Wallclock time of process. */
+} resource_t;
+
+/* msec = milliseconds = 1/1,000 (1*10e-3) second.
+ usec = microseconds = 1/1,000,000 (1*10e-6) second. */
+
+#ifndef TICKS_PER_SEC
+#define TICKS_PER_SEC 100
+#endif
+
+/* The number of milliseconds in one `tick' used by the `rusage' structure. */
+#define MSEC_PER_TICK (1000 / TICKS_PER_SEC)
+
+/* Return the number of clock ticks that occur in M milliseconds. */
+#define MSEC_TO_TICKS(m) ((m) / MSEC_PER_TICK)
+
+#define UL unsigned long
+
+static const char *const default_format = "real\t%E\nuser\t%u\nsys\t%T";
+
+/* The output format for the -p option .*/
+static const char *const posix_format = "real %e\nuser %U\nsys %S";
+
+
+/* Format string for printing all statistics verbosely.
+ Keep this output to 24 lines so users on terminals can see it all.*/
+static const char *const long_format =
+ "\tCommand being timed: \"%C\"\n"
+ "\tUser time (seconds): %U\n"
+ "\tSystem time (seconds): %S\n"
+ "\tPercent of CPU this job got: %P\n"
+ "\tElapsed (wall clock) time (h:mm:ss or m:ss): %E\n"
+ "\tAverage shared text size (kbytes): %X\n"
+ "\tAverage unshared data size (kbytes): %D\n"
+ "\tAverage stack size (kbytes): %p\n"
+ "\tAverage total size (kbytes): %K\n"
+ "\tMaximum resident set size (kbytes): %M\n"
+ "\tAverage resident set size (kbytes): %t\n"
+ "\tMajor (requiring I/O) page faults: %F\n"
+ "\tMinor (reclaiming a frame) page faults: %R\n"
+ "\tVoluntary context switches: %w\n"
+ "\tInvoluntary context switches: %c\n"
+ "\tSwaps: %W\n"
+ "\tFile system inputs: %I\n"
+ "\tFile system outputs: %O\n"
+ "\tSocket messages sent: %s\n"
+ "\tSocket messages received: %r\n"
+ "\tSignals delivered: %k\n"
+ "\tPage size (bytes): %Z\n"
+ "\tExit status: %x";
+
+
+/* Wait for and fill in data on child process PID.
+ Return 0 on error, 1 if ok. */
+
+/* pid_t is short on BSDI, so don't try to promote it. */
+static int resuse_end(pid_t pid, resource_t * resp)
+{
+ int status;
+
+ pid_t caught;
+
+ /* Ignore signals, but don't ignore the children. When wait3
+ returns the child process, set the time the command finished. */
+ while ((caught = wait3(&status, 0, &resp->ru)) != pid) {
+ if (caught == -1)
+ return 0;
+ }
+
+ gettimeofday(&resp->elapsed, (struct timezone *) 0);
+ resp->elapsed.tv_sec -= resp->start.tv_sec;
+ if (resp->elapsed.tv_usec < resp->start.tv_usec) {
+ /* Manually carry a one from the seconds field. */
+ resp->elapsed.tv_usec += 1000000;
+ --resp->elapsed.tv_sec;
+ }
+ resp->elapsed.tv_usec -= resp->start.tv_usec;
+
+ resp->waitstatus = status;
+
+ return 1;
+}
+
+/* Print ARGV, with each entry in ARGV separated by FILLER. */
+static void printargv(char *const *argv, const char *filler)
+{
+ fputs(*argv, stdout);
+ while (*++argv) {
+ fputs(filler, stdout);
+ fputs(*argv, stdout);
+ }
+}
+
+/* Return the number of kilobytes corresponding to a number of pages PAGES.
+ (Actually, we use it to convert pages*ticks into kilobytes*ticks.)
+
+ Try to do arithmetic so that the risk of overflow errors is minimized.
+ This is funky since the pagesize could be less than 1K.
+ Note: Some machines express getrusage statistics in terms of K,
+ others in terms of pages. */
+
+static unsigned long ptok(unsigned long pages)
+{
+ static unsigned long ps;
+ unsigned long tmp;
+
+ /* Initialization. */
+ if (ps == 0)
+ ps = getpagesize();
+
+ /* Conversion. */
+ if (pages > (LONG_MAX / ps)) { /* Could overflow. */
+ tmp = pages / 1024; /* Smaller first, */
+ return tmp * ps; /* then larger. */
+ }
+ /* Could underflow. */
+ tmp = pages * ps; /* Larger first, */
+ return tmp / 1024; /* then smaller. */
+}
+
+/* summarize: Report on the system use of a command.
+
+ Print the FMT argument except that `%' sequences
+ have special meaning, and `\n' and `\t' are translated into
+ newline and tab, respectively, and `\\' is translated into `\'.
+
+ The character following a `%' can be:
+ (* means the tcsh time builtin also recognizes it)
+ % == a literal `%'
+ C == command name and arguments
+* D == average unshared data size in K (ru_idrss+ru_isrss)
+* E == elapsed real (wall clock) time in [hour:]min:sec
+* F == major page faults (required physical I/O) (ru_majflt)
+* I == file system inputs (ru_inblock)
+* K == average total mem usage (ru_idrss+ru_isrss+ru_ixrss)
+* M == maximum resident set size in K (ru_maxrss)
+* O == file system outputs (ru_oublock)
+* P == percent of CPU this job got (total cpu time / elapsed time)
+* R == minor page faults (reclaims; no physical I/O involved) (ru_minflt)
+* S == system (kernel) time (seconds) (ru_stime)
+* T == system time in [hour:]min:sec
+* U == user time (seconds) (ru_utime)
+* u == user time in [hour:]min:sec
+* W == times swapped out (ru_nswap)
+* X == average amount of shared text in K (ru_ixrss)
+ Z == page size
+* c == involuntary context switches (ru_nivcsw)
+ e == elapsed real time in seconds
+* k == signals delivered (ru_nsignals)
+ p == average unshared stack size in K (ru_isrss)
+* r == socket messages received (ru_msgrcv)
+* s == socket messages sent (ru_msgsnd)
+ t == average resident set size in K (ru_idrss)
+* w == voluntary context switches (ru_nvcsw)
+ x == exit status of command
+
+ Various memory usages are found by converting from page-seconds
+ to kbytes by multiplying by the page size, dividing by 1024,
+ and dividing by elapsed real time.
+
+ FMT is the format string, interpreted as described above.
+ COMMAND is the command and args that are being summarized.
+ RESP is resource information on the command. */
+
+static void summarize(const char *fmt, char **command, resource_t * resp)
+{
+ unsigned long r; /* Elapsed real milliseconds. */
+ unsigned long v; /* Elapsed virtual (CPU) milliseconds. */
+
+ if (WIFSTOPPED(resp->waitstatus))
+ printf("Command stopped by signal %d\n",
+ WSTOPSIG(resp->waitstatus));
+ else if (WIFSIGNALED(resp->waitstatus))
+ printf("Command terminated by signal %d\n",
+ WTERMSIG(resp->waitstatus));
+ else if (WIFEXITED(resp->waitstatus) && WEXITSTATUS(resp->waitstatus))
+ printf("Command exited with non-zero status %d\n",
+ WEXITSTATUS(resp->waitstatus));
+
+ /* Convert all times to milliseconds. Occasionally, one of these values
+ comes out as zero. Dividing by zero causes problems, so we first
+ check the time value. If it is zero, then we take `evasive action'
+ instead of calculating a value. */
+
+ r = resp->elapsed.tv_sec * 1000 + resp->elapsed.tv_usec / 1000;
+
+ v = resp->ru.ru_utime.tv_sec * 1000 + resp->ru.ru_utime.TV_MSEC +
+ resp->ru.ru_stime.tv_sec * 1000 + resp->ru.ru_stime.TV_MSEC;
+
+ /* putchar() != putc(stdout) in glibc! */
+
+ while (*fmt) {
+ /* Handle leading literal part */
+ int n = strcspn(fmt, "%\\");
+ if (n) {
+ printf("%.*s", n, fmt);
+ fmt += n;
+ continue;
+ }
+
+ switch (*fmt) {
+#ifdef NOT_NEEDED
+ /* Handle literal char */
+ /* Usually we optimize for size, but there is a limit
+ * for everything. With this we do a lot of 1-byte writes */
+ default:
+ putc(*fmt, stdout);
+ break;
+#endif
+
+ case '%':
+ switch (*++fmt) {
+#ifdef NOT_NEEDED_YET
+ /* Our format strings do not have these */
+ /* and we do not take format str from user */
+ default:
+ putc('%', stdout);
+ /*FALLTHROUGH*/
+ case '%':
+ if (!*fmt) goto ret;
+ putc(*fmt, stdout);
+ break;
+#endif
+ case 'C': /* The command that got timed. */
+ printargv(command, " ");
+ break;
+ case 'D': /* Average unshared data size. */
+ printf("%lu",
+ MSEC_TO_TICKS(v) == 0 ? 0 :
+ ptok((UL) resp->ru.ru_idrss) / MSEC_TO_TICKS(v) +
+ ptok((UL) resp->ru.ru_isrss) / MSEC_TO_TICKS(v));
+ break;
+ case 'E': /* Elapsed real (wall clock) time. */
+ if (resp->elapsed.tv_sec >= 3600) /* One hour -> h:m:s. */
+ printf("%ldh %ldm %02lds",
+ resp->elapsed.tv_sec / 3600,
+ (resp->elapsed.tv_sec % 3600) / 60,
+ resp->elapsed.tv_sec % 60);
+ else
+ printf("%ldm %ld.%02lds", /* -> m:s. */
+ resp->elapsed.tv_sec / 60,
+ resp->elapsed.tv_sec % 60,
+ resp->elapsed.tv_usec / 10000);
+ break;
+ case 'F': /* Major page faults. */
+ printf("%ld", resp->ru.ru_majflt);
+ break;
+ case 'I': /* Inputs. */
+ printf("%ld", resp->ru.ru_inblock);
+ break;
+ case 'K': /* Average mem usage == data+stack+text. */
+ printf("%lu",
+ MSEC_TO_TICKS(v) == 0 ? 0 :
+ ptok((UL) resp->ru.ru_idrss) / MSEC_TO_TICKS(v) +
+ ptok((UL) resp->ru.ru_isrss) / MSEC_TO_TICKS(v) +
+ ptok((UL) resp->ru.ru_ixrss) / MSEC_TO_TICKS(v));
+ break;
+ case 'M': /* Maximum resident set size. */
+ printf("%lu", ptok((UL) resp->ru.ru_maxrss));
+ break;
+ case 'O': /* Outputs. */
+ printf("%ld", resp->ru.ru_oublock);
+ break;
+ case 'P': /* Percent of CPU this job got. */
+ /* % cpu is (total cpu time)/(elapsed time). */
+ if (r > 0)
+ printf("%lu%%", (v * 100 / r));
+ else
+ printf("?%%");
+ break;
+ case 'R': /* Minor page faults (reclaims). */
+ printf("%ld", resp->ru.ru_minflt);
+ break;
+ case 'S': /* System time. */
+ printf("%ld.%02ld",
+ resp->ru.ru_stime.tv_sec,
+ resp->ru.ru_stime.TV_MSEC / 10);
+ break;
+ case 'T': /* System time. */
+ if (resp->ru.ru_stime.tv_sec >= 3600) /* One hour -> h:m:s. */
+ printf("%ldh %ldm %02lds",
+ resp->ru.ru_stime.tv_sec / 3600,
+ (resp->ru.ru_stime.tv_sec % 3600) / 60,
+ resp->ru.ru_stime.tv_sec % 60);
+ else
+ printf("%ldm %ld.%02lds", /* -> m:s. */
+ resp->ru.ru_stime.tv_sec / 60,
+ resp->ru.ru_stime.tv_sec % 60,
+ resp->ru.ru_stime.tv_usec / 10000);
+ break;
+ case 'U': /* User time. */
+ printf("%ld.%02ld",
+ resp->ru.ru_utime.tv_sec,
+ resp->ru.ru_utime.TV_MSEC / 10);
+ break;
+ case 'u': /* User time. */
+ if (resp->ru.ru_utime.tv_sec >= 3600) /* One hour -> h:m:s. */
+ printf("%ldh %ldm %02lds",
+ resp->ru.ru_utime.tv_sec / 3600,
+ (resp->ru.ru_utime.tv_sec % 3600) / 60,
+ resp->ru.ru_utime.tv_sec % 60);
+ else
+ printf("%ldm %ld.%02lds", /* -> m:s. */
+ resp->ru.ru_utime.tv_sec / 60,
+ resp->ru.ru_utime.tv_sec % 60,
+ resp->ru.ru_utime.tv_usec / 10000);
+ break;
+ case 'W': /* Times swapped out. */
+ printf("%ld", resp->ru.ru_nswap);
+ break;
+ case 'X': /* Average shared text size. */
+ printf("%lu",
+ MSEC_TO_TICKS(v) == 0 ? 0 :
+ ptok((UL) resp->ru.ru_ixrss) / MSEC_TO_TICKS(v));
+ break;
+ case 'Z': /* Page size. */
+ printf("%d", getpagesize());
+ break;
+ case 'c': /* Involuntary context switches. */
+ printf("%ld", resp->ru.ru_nivcsw);
+ break;
+ case 'e': /* Elapsed real time in seconds. */
+ printf("%ld.%02ld",
+ resp->elapsed.tv_sec, resp->elapsed.tv_usec / 10000);
+ break;
+ case 'k': /* Signals delivered. */
+ printf("%ld", resp->ru.ru_nsignals);
+ break;
+ case 'p': /* Average stack segment. */
+ printf("%lu",
+ MSEC_TO_TICKS(v) == 0 ? 0 :
+ ptok((UL) resp->ru.ru_isrss) / MSEC_TO_TICKS(v));
+ break;
+ case 'r': /* Incoming socket messages received. */
+ printf("%ld", resp->ru.ru_msgrcv);
+ break;
+ case 's': /* Outgoing socket messages sent. */
+ printf("%ld", resp->ru.ru_msgsnd);
+ break;
+ case 't': /* Average resident set size. */
+ printf("%lu",
+ MSEC_TO_TICKS(v) == 0 ? 0 :
+ ptok((UL) resp->ru.ru_idrss) / MSEC_TO_TICKS(v));
+ break;
+ case 'w': /* Voluntary context switches. */
+ printf("%ld", resp->ru.ru_nvcsw);
+ break;
+ case 'x': /* Exit status. */
+ printf("%d", WEXITSTATUS(resp->waitstatus));
+ break;
+ }
+ break;
+
+#ifdef NOT_NEEDED_YET
+ case '\\': /* Format escape. */
+ switch (*++fmt) {
+ default:
+ putc('\\', stdout);
+ /*FALLTHROUGH*/
+ case '\\':
+ if (!*fmt) goto ret;
+ putc(*fmt, stdout);
+ break;
+ case 't':
+ putc('\t', stdout);
+ break;
+ case 'n':
+ putc('\n', stdout);
+ break;
+ }
+ break;
+#endif
+ }
+ ++fmt;
+ }
+ /* ret: */
+ putc('\n', stdout);
+}
+
+/* Run command CMD and return statistics on it.
+ Put the statistics in *RESP. */
+static void run_command(char *const *cmd, resource_t * resp)
+{
+ pid_t pid; /* Pid of child. */
+ __sighandler_t interrupt_signal, quit_signal;
+
+ gettimeofday(&resp->start, (struct timezone *) 0);
+ pid = vfork(); /* Run CMD as child process. */
+ if (pid < 0)
+ bb_error_msg_and_die("cannot fork");
+ else if (pid == 0) { /* If child. */
+ /* Don't cast execvp arguments; that causes errors on some systems,
+ versus merely warnings if the cast is left off. */
+ BB_EXECVP(cmd[0], cmd);
+ bb_error_msg("cannot run %s", cmd[0]);
+ _exit(errno == ENOENT ? 127 : 126);
+ }
+
+ /* Have signals kill the child but not self (if possible). */
+ interrupt_signal = signal(SIGINT, SIG_IGN);
+ quit_signal = signal(SIGQUIT, SIG_IGN);
+
+ if (resuse_end(pid, resp) == 0)
+ bb_error_msg("error waiting for child process");
+
+ /* Re-enable signals. */
+ signal(SIGINT, interrupt_signal);
+ signal(SIGQUIT, quit_signal);
+}
+
+int time_main(int argc, char **argv);
+int time_main(int argc, char **argv)
+{
+ resource_t res;
+ const char *output_format = default_format;
+ char c;
+
+ goto next;
+ /* Parse any options -- don't use getopt() here so we don't
+ * consume the args of our client application... */
+ while (argc > 0 && argv[0][0] == '-') {
+ while ((c = *++*argv)) {
+ switch (c) {
+ case 'v':
+ output_format = long_format;
+ break;
+ case 'p':
+ output_format = posix_format;
+ break;
+ default:
+ bb_show_usage();
+ }
+ }
+ next:
+ argv++;
+ argc--;
+ if (!argc)
+ bb_show_usage();
+ }
+
+ run_command(argv, &res);
+
+ /* Cheat. printf's are shorter :) */
+ stdout = stderr;
+ dup2(2, 1); /* just in case libc does something silly :( */
+ summarize(output_format, argv, &res);
+
+ if (WIFSTOPPED(res.waitstatus))
+ return WSTOPSIG(res.waitstatus);
+ if (WIFSIGNALED(res.waitstatus))
+ return WTERMSIG(res.waitstatus);
+ if (WIFEXITED(res.waitstatus))
+ return WEXITSTATUS(res.waitstatus);
+ fflush_stdout_and_exit(0);
+}
diff --git a/i/pc104/initrd/conf/busybox/miscutils/watchdog.c b/i/pc104/initrd/conf/busybox/miscutils/watchdog.c
new file mode 100644
index 0000000..ed9026d
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/miscutils/watchdog.c
@@ -0,0 +1,66 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini watchdog implementation for busybox
+ *
+ * Copyright (C) 2003 Paul Mundt <lethal@linux-sh.org>
+ * Copyright (C) 2006 Bernhard Fischer <busybox@busybox.net>
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ */
+
+#include "busybox.h"
+
+#define OPT_FOREGROUND 0x01
+#define OPT_TIMER 0x02
+
+static void watchdog_shutdown(int ATTRIBUTE_UNUSED sig) ATTRIBUTE_NORETURN;
+static void watchdog_shutdown(int ATTRIBUTE_UNUSED sig)
+{
+ write(3, "V", 1); /* Magic, see watchdog-api.txt in kernel */
+ if (ENABLE_FEATURE_CLEAN_UP)
+ close(3);
+ exit(0);
+}
+
+int watchdog_main(int argc, char **argv);
+int watchdog_main(int argc, char **argv)
+{
+ unsigned opts;
+ unsigned timer_duration = 30; /* Userspace timer duration, in seconds */
+ char *t_arg;
+
+ opts = getopt32(argc, argv, "Ft:", &t_arg);
+
+ if (opts & OPT_TIMER)
+ timer_duration = xatou(t_arg);
+
+ /* We're only interested in the watchdog device .. */
+ if (optind < argc - 1 || argc == 1)
+ bb_show_usage();
+
+#ifdef BB_NOMMU
+ if (!(opts & OPT_FOREGROUND))
+ vfork_daemon_rexec(0, 1, argc, argv, "-F");
+#else
+ xdaemon(0, 1);
+#endif
+
+ signal(SIGHUP, watchdog_shutdown);
+ signal(SIGINT, watchdog_shutdown);
+
+ /* Use known fd # - avoid needing global 'int fd' */
+ dup2(xopen(argv[argc - 1], O_WRONLY), 3);
+
+ while (1) {
+ /*
+ * Make sure we clear the counter before sleeping, as the counter value
+ * is undefined at this point -- PFM
+ */
+ write(3, "", 1);
+ sleep(timer_duration);
+ }
+
+ watchdog_shutdown(0);
+
+ /* return EXIT_SUCCESS; */
+}
diff --git a/i/pc104/initrd/conf/busybox/modutils/Config.in b/i/pc104/initrd/conf/busybox/modutils/Config.in
new file mode 100644
index 0000000..6a69567
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/modutils/Config.in
@@ -0,0 +1,158 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+menu "Linux Module Utilities"
+
+config INSMOD
+ bool "insmod"
+ default n
+ help
+ insmod is used to load specified modules in the running kernel.
+
+config FEATURE_INSMOD_VERSION_CHECKING
+ bool "Module version checking"
+ default n
+ depends on INSMOD && FEATURE_2_4_MODULES
+ help
+ Support checking of versions for modules. This is used to
+ ensure that the kernel and module are made for each other.
+
+config FEATURE_INSMOD_KSYMOOPS_SYMBOLS
+ bool "Add module symbols to kernel symbol table"
+ default n
+ depends on INSMOD && FEATURE_2_4_MODULES
+ help
+ By adding module symbols to the kernel symbol table, Oops messages
+ occuring within kernel modules can be properly debugged. By enabling
+ this feature, module symbols will always be added to the kernel symbol
+ table for properly debugging support. If you are not interested in
+ Oops messages from kernel modules, say N.
+
+config FEATURE_INSMOD_LOADINKMEM
+ bool "In kernel memory optimization (uClinux only)"
+ default n
+ depends on INSMOD && FEATURE_2_4_MODULES
+ help
+ This is a special uClinux only memory optimization that lets insmod
+ load the specified kernel module directly into kernel space, reducing
+ memory usage by preventing the need for two copies of the module
+ being loaded into memory.
+
+config FEATURE_INSMOD_LOAD_MAP
+ bool "Enable load map (-m) option"
+ default n
+ depends on INSMOD && ( FEATURE_2_4_MODULES || FEATURE_2_6_MODULES )
+ help
+ Enabling this, one would be able to get a load map
+ output on stdout. This makes kernel module debugging
+ easier.
+ If you don't plan to debug kernel modules, you
+ don't need this option.
+
+config FEATURE_INSMOD_LOAD_MAP_FULL
+ bool "Symbols in load map"
+ default y
+ depends on FEATURE_INSMOD_LOAD_MAP
+ help
+ Without this option, -m will only output section
+ load map. With this option, -m will also output
+ symbols load map.
+
+config RMMOD
+ bool "rmmod"
+ default n
+ help
+ rmmod is used to unload specified modules from the kernel.
+
+config LSMOD
+ bool "lsmod"
+ default n
+ help
+ lsmod is used to display a list of loaded modules.
+
+config FEATURE_LSMOD_PRETTY_2_6_OUTPUT
+ bool "lsmod pretty output for 2.6.x Linux kernels "
+ default n
+ depends on LSMOD
+ help
+ This option makes output format of lsmod adjusted to
+ the format of module-init-tools for Linux kernel 2.6.
+
+config MODPROBE
+ bool "modprobe"
+ default n
+ help
+ Handle the loading of modules, and their dependencies on a high
+ level.
+
+ Note that in the state, modprobe does not understand multiple
+ module options from the configuration file. See option below.
+
+config FEATURE_MODPROBE_MULTIPLE_OPTIONS
+ bool
+ prompt "Multiple options parsing" if NITPICK
+ default y
+ depends on MODPROBE
+ help
+ Allow modprobe to understand more than one option to pass to
+ modules.
+
+ This is a WIP, while waiting for a common argument parsing
+ common amongst all BB applets (shell, modprobe, etc...) and
+ adds around 600 bytes on x86, 700 bytes on ARM. The code is
+ biggish and uggly, but just works.
+
+ Saying Y here is not a bad idea if you're not that short
+ on storage capacity.
+
+config FEATURE_MODPROBE_FANCY_ALIAS
+ bool
+ prompt "Fancy alias parsing" if NITPICK
+ default y
+ depends on MODPROBE && FEATURE_2_6_MODULES
+ help
+ Say 'y' here to enable parsing of aliases with underscore/dash
+ mismatch between module name and file name, along with bus-specific
+ aliases (such as pci:... or usb:... aliases).
+
+comment "Options common to multiple modutils"
+ depends on INSMOD || RMMOD || MODPROBE || LSMOD
+
+config FEATURE_CHECK_TAINTED_MODULE
+ # Simulate indentation
+ bool "Support tainted module checking with new kernels"
+ default y
+ depends on INSMOD || LSMOD
+ help
+ Support checking for tainted modules. These are usually binary
+ only modules that will make the linux-kernel list ignore your
+ support request.
+ This option is required to support GPLONLY modules.
+
+config FEATURE_2_4_MODULES
+ # Simulate indentation
+ bool "Support version 2.2.x to 2.4.x Linux kernels"
+ default y
+ depends on INSMOD || RMMOD || MODPROBE
+ help
+ Support module loading for 2.2.x and 2.4.x Linux kernels.
+
+config FEATURE_2_6_MODULES
+ # Simulate indentation
+ bool "Support version 2.6.x Linux kernels"
+ default y
+ depends on INSMOD || RMMOD || MODPROBE
+ help
+ Support module loading for newer 2.6.x Linux kernels.
+
+
+config FEATURE_QUERY_MODULE_INTERFACE
+ bool
+ default y
+ depends on FEATURE_2_4_MODULES && !FEATURE_2_6_MODULES
+
+
+endmenu
+
diff --git a/i/pc104/initrd/conf/busybox/modutils/Kbuild b/i/pc104/initrd/conf/busybox/modutils/Kbuild
new file mode 100644
index 0000000..cff02b4
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/modutils/Kbuild
@@ -0,0 +1,11 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under the GPL v2, see the file LICENSE in this tarball.
+
+lib-y:=
+lib-$(CONFIG_INSMOD) += insmod.o
+lib-$(CONFIG_LSMOD) += lsmod.o
+lib-$(CONFIG_MODPROBE) += modprobe.o
+lib-$(CONFIG_RMMOD) += rmmod.o
diff --git a/i/pc104/initrd/conf/busybox/modutils/insmod.c b/i/pc104/initrd/conf/busybox/modutils/insmod.c
new file mode 100644
index 0000000..57092f7
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/modutils/insmod.c
@@ -0,0 +1,4315 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini insmod implementation for busybox
+ *
+ * This version of insmod supports ARM, CRIS, H8/300, x86, ia64, x86_64,
+ * m68k, MIPS, PowerPC, S390, SH3/4/5, Sparc, v850e, and x86_64.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ * and Ron Alder <alder@lineo.com>
+ *
+ * Rodney Radford <rradford@mindspring.com> 17-Aug-2004.
+ * Added x86_64 support.
+ *
+ * Miles Bader <miles@gnu.org> added NEC V850E support.
+ *
+ * Modified by Bryan Rittmeyer <bryan@ixiacom.com> to support SH4
+ * and (theoretically) SH3. I have only tested SH4 in little endian mode.
+ *
+ * Modified by Alcove, Julien Gaulmin <julien.gaulmin@alcove.fr> and
+ * Nicolas Ferre <nicolas.ferre@alcove.fr> to support ARM7TDMI. Only
+ * very minor changes required to also work with StrongArm and presumably
+ * all ARM based systems.
+ *
+ * Yoshinori Sato <ysato@users.sourceforge.jp> 19-May-2004.
+ * added Renesas H8/300 support.
+ *
+ * Paul Mundt <lethal@linux-sh.org> 08-Aug-2003.
+ * Integrated support for sh64 (SH-5), from preliminary modutils
+ * patches from Benedict Gaster <benedict.gaster@superh.com>.
+ * Currently limited to support for 32bit ABI.
+ *
+ * Magnus Damm <damm@opensource.se> 22-May-2002.
+ * The plt and got code are now using the same structs.
+ * Added generic linked list code to fully support PowerPC.
+ * Replaced the mess in arch_apply_relocation() with architecture blocks.
+ * The arch_create_got() function got cleaned up with architecture blocks.
+ * These blocks should be easy maintain and sync with obj_xxx.c in modutils.
+ *
+ * Magnus Damm <damm@opensource.se> added PowerPC support 20-Feb-2001.
+ * PowerPC specific code stolen from modutils-2.3.16,
+ * written by Paul Mackerras, Copyright 1996, 1997 Linux International.
+ * I've only tested the code on mpc8xx platforms in big-endian mode.
+ * Did some cleanup and added USE_xxx_ENTRIES...
+ *
+ * Quinn Jensen <jensenq@lineo.com> added MIPS support 23-Feb-2001.
+ * based on modutils-2.4.2
+ * MIPS specific support for Elf loading and relocation.
+ * Copyright 1996, 1997 Linux International.
+ * Contributed by Ralf Baechle <ralf@gnu.ai.mit.edu>
+ *
+ * Based almost entirely on the Linux modutils-2.3.11 implementation.
+ * Copyright 1996, 1997 Linux International.
+ * New implementation contributed by Richard Henderson <rth@tamu.edu>
+ * Based on original work by Bjorn Ekwall <bj0rn@blox.se>
+ * Restructured (and partly rewritten) by:
+ * Björn Ekwall <bj0rn@blox.se> February 1999
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "busybox.h"
+#include <libgen.h>
+#include <sys/utsname.h>
+
+#if !ENABLE_FEATURE_2_4_MODULES && !ENABLE_FEATURE_2_6_MODULES
+#undef ENABLE_FEATURE_2_4_MODULES
+#define ENABLE_FEATURE_2_4_MODULES 1
+#endif
+
+#if !ENABLE_FEATURE_2_4_MODULES
+#define insmod_ng_main insmod_main
+#endif
+
+#if ENABLE_FEATURE_2_6_MODULES
+extern int insmod_ng_main( int argc, char **argv);
+#endif
+
+
+#if ENABLE_FEATURE_2_4_MODULES
+
+
+#if ENABLE_FEATURE_INSMOD_LOADINKMEM
+#define LOADBITS 0
+#else
+#define LOADBITS 1
+#endif
+
+
+/* Alpha */
+#if defined(__alpha__)
+#define MATCH_MACHINE(x) (x == EM_ALPHA)
+#define SHT_RELM SHT_RELA
+#define Elf64_RelM Elf64_Rela
+#define ELFCLASSM ELFCLASS64
+#endif
+
+/* ARM support */
+#if defined(__arm__)
+#define MATCH_MACHINE(x) (x == EM_ARM)
+#define SHT_RELM SHT_REL
+#define Elf32_RelM Elf32_Rel
+#define ELFCLASSM ELFCLASS32
+#define USE_PLT_ENTRIES
+#define PLT_ENTRY_SIZE 8
+#define USE_GOT_ENTRIES
+#define GOT_ENTRY_SIZE 8
+#define USE_SINGLE
+#endif
+
+/* blackfin */
+#if defined(BFIN)
+#define MATCH_MACHINE(x) (x == EM_BLACKFIN)
+#define SHT_RELM SHT_RELA
+#define Elf32_RelM Elf32_Rela
+#define ELFCLASSM ELFCLASS32
+#endif
+
+/* CRIS */
+#if defined(__cris__)
+#define MATCH_MACHINE(x) (x == EM_CRIS)
+#define SHT_RELM SHT_RELA
+#define Elf32_RelM Elf32_Rela
+#define ELFCLASSM ELFCLASS32
+#ifndef EM_CRIS
+#define EM_CRIS 76
+#define R_CRIS_NONE 0
+#define R_CRIS_32 3
+#endif
+#endif
+
+/* H8/300 */
+#if defined(__H8300H__) || defined(__H8300S__)
+#define MATCH_MACHINE(x) (x == EM_H8_300)
+#define SHT_RELM SHT_RELA
+#define Elf32_RelM Elf32_Rela
+#define ELFCLASSM ELFCLASS32
+#define USE_SINGLE
+#define SYMBOL_PREFIX "_"
+#endif
+
+/* PA-RISC / HP-PA */
+#if defined(__hppa__)
+#define MATCH_MACHINE(x) (x == EM_PARISC)
+#define SHT_RELM SHT_RELA
+#if defined(__LP64__)
+#define Elf64_RelM Elf64_Rela
+#define ELFCLASSM ELFCLASS64
+#else
+#define Elf32_RelM Elf32_Rela
+#define ELFCLASSM ELFCLASS32
+#endif
+#endif
+
+/* x86 */
+#if defined(__i386__)
+#ifndef EM_486
+#define MATCH_MACHINE(x) (x == EM_386)
+#else
+#define MATCH_MACHINE(x) (x == EM_386 || x == EM_486)
+#endif
+#define SHT_RELM SHT_REL
+#define Elf32_RelM Elf32_Rel
+#define ELFCLASSM ELFCLASS32
+#define USE_GOT_ENTRIES
+#define GOT_ENTRY_SIZE 4
+#define USE_SINGLE
+#endif
+
+/* IA64, aka Itanium */
+#if defined(__ia64__)
+#define MATCH_MACHINE(x) (x == EM_IA_64)
+#define SHT_RELM SHT_RELA
+#define Elf64_RelM Elf64_Rela
+#define ELFCLASSM ELFCLASS64
+#endif
+
+/* m68k */
+#if defined(__mc68000__)
+#define MATCH_MACHINE(x) (x == EM_68K)
+#define SHT_RELM SHT_RELA
+#define Elf32_RelM Elf32_Rela
+#define ELFCLASSM ELFCLASS32
+#define USE_GOT_ENTRIES
+#define GOT_ENTRY_SIZE 4
+#define USE_SINGLE
+#endif
+
+/* Microblaze */
+#if defined(__microblaze__)
+#define USE_SINGLE
+#define MATCH_MACHINE(x) (x == EM_XILINX_MICROBLAZE)
+#define SHT_RELM SHT_RELA
+#define Elf32_RelM Elf32_Rela
+#define ELFCLASSM ELFCLASS32
+#endif
+
+/* MIPS */
+#if defined(__mips__)
+#define MATCH_MACHINE(x) (x == EM_MIPS || x == EM_MIPS_RS3_LE)
+#define SHT_RELM SHT_REL
+#define Elf32_RelM Elf32_Rel
+#define ELFCLASSM ELFCLASS32
+/* Account for ELF spec changes. */
+#ifndef EM_MIPS_RS3_LE
+#ifdef EM_MIPS_RS4_BE
+#define EM_MIPS_RS3_LE EM_MIPS_RS4_BE
+#else
+#define EM_MIPS_RS3_LE 10
+#endif
+#endif /* !EM_MIPS_RS3_LE */
+#define ARCHDATAM "__dbe_table"
+#endif
+
+/* Nios II */
+#if defined(__nios2__)
+#define MATCH_MACHINE(x) (x == EM_ALTERA_NIOS2)
+#define SHT_RELM SHT_RELA
+#define Elf32_RelM Elf32_Rela
+#define ELFCLASSM ELFCLASS32
+#endif
+
+/* PowerPC */
+#if defined(__powerpc64__)
+#define MATCH_MACHINE(x) (x == EM_PPC64)
+#define SHT_RELM SHT_RELA
+#define Elf64_RelM Elf64_Rela
+#define ELFCLASSM ELFCLASS64
+#elif defined(__powerpc__)
+#define MATCH_MACHINE(x) (x == EM_PPC)
+#define SHT_RELM SHT_RELA
+#define Elf32_RelM Elf32_Rela
+#define ELFCLASSM ELFCLASS32
+#define USE_PLT_ENTRIES
+#define PLT_ENTRY_SIZE 16
+#define USE_PLT_LIST
+#define LIST_ARCHTYPE ElfW(Addr)
+#define USE_LIST
+#define ARCHDATAM "__ftr_fixup"
+#endif
+
+/* S390 */
+#if defined(__s390__)
+#define MATCH_MACHINE(x) (x == EM_S390)
+#define SHT_RELM SHT_RELA
+#define Elf32_RelM Elf32_Rela
+#define ELFCLASSM ELFCLASS32
+#define USE_PLT_ENTRIES
+#define PLT_ENTRY_SIZE 8
+#define USE_GOT_ENTRIES
+#define GOT_ENTRY_SIZE 8
+#define USE_SINGLE
+#endif
+
+/* SuperH */
+#if defined(__sh__)
+#define MATCH_MACHINE(x) (x == EM_SH)
+#define SHT_RELM SHT_RELA
+#define Elf32_RelM Elf32_Rela
+#define ELFCLASSM ELFCLASS32
+#define USE_GOT_ENTRIES
+#define GOT_ENTRY_SIZE 4
+#define USE_SINGLE
+/* the SH changes have only been tested in =little endian= mode */
+/* I'm not sure about big endian, so let's warn: */
+#if defined(__sh__) && BB_BIG_ENDIAN
+# error insmod.c may require changes for use on big endian SH
+#endif
+/* it may or may not work on the SH1/SH2... Error on those also */
+#if ((!(defined(__SH3__) || defined(__SH4__) || defined(__SH5__)))) && (defined(__sh__))
+#error insmod.c may require changes for SH1 or SH2 use
+#endif
+#endif
+
+/* Sparc */
+#if defined(__sparc__)
+#define MATCH_MACHINE(x) (x == EM_SPARC)
+#define SHT_RELM SHT_RELA
+#define Elf32_RelM Elf32_Rela
+#define ELFCLASSM ELFCLASS32
+#endif
+
+/* v850e */
+#if defined (__v850e__)
+#define MATCH_MACHINE(x) ((x) == EM_V850 || (x) == EM_CYGNUS_V850)
+#define SHT_RELM SHT_RELA
+#define Elf32_RelM Elf32_Rela
+#define ELFCLASSM ELFCLASS32
+#define USE_PLT_ENTRIES
+#define PLT_ENTRY_SIZE 8
+#define USE_SINGLE
+#ifndef EM_CYGNUS_V850 /* grumble */
+#define EM_CYGNUS_V850 0x9080
+#endif
+#define SYMBOL_PREFIX "_"
+#endif
+
+/* X86_64 */
+#if defined(__x86_64__)
+#define MATCH_MACHINE(x) (x == EM_X86_64)
+#define SHT_RELM SHT_RELA
+#define USE_GOT_ENTRIES
+#define GOT_ENTRY_SIZE 8
+#define USE_SINGLE
+#define Elf64_RelM Elf64_Rela
+#define ELFCLASSM ELFCLASS64
+#endif
+
+#ifndef SHT_RELM
+#error Sorry, but insmod.c does not yet support this architecture...
+#endif
+
+
+//----------------------------------------------------------------------------
+//--------modutils module.h, lines 45-242
+//----------------------------------------------------------------------------
+
+/* Definitions for the Linux module syscall interface.
+ Copyright 1996, 1997 Linux International.
+
+ Contributed by Richard Henderson <rth@tamu.edu>
+
+ This file is part of the Linux modutils.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+
+#ifndef MODUTILS_MODULE_H
+
+/*======================================================================*/
+/* For sizeof() which are related to the module platform and not to the
+ environment isnmod is running in, use sizeof_xx instead of sizeof(xx). */
+
+#define tgt_sizeof_char sizeof(char)
+#define tgt_sizeof_short sizeof(short)
+#define tgt_sizeof_int sizeof(int)
+#define tgt_sizeof_long sizeof(long)
+#define tgt_sizeof_char_p sizeof(char *)
+#define tgt_sizeof_void_p sizeof(void *)
+#define tgt_long long
+
+#if defined(__sparc__) && !defined(__sparc_v9__) && defined(ARCH_sparc64)
+#undef tgt_sizeof_long
+#undef tgt_sizeof_char_p
+#undef tgt_sizeof_void_p
+#undef tgt_long
+enum {
+ tgt_sizeof_long = 8,
+ tgt_sizeof_char_p = 8,
+ tgt_sizeof_void_p = 8
+};
+#define tgt_long long long
+#endif
+
+/*======================================================================*/
+/* The structures used in Linux 2.1. */
+
+/* Note: new_module_symbol does not use tgt_long intentionally */
+struct new_module_symbol {
+ unsigned long value;
+ unsigned long name;
+};
+
+struct new_module_persist;
+
+struct new_module_ref {
+ unsigned tgt_long dep; /* kernel addresses */
+ unsigned tgt_long ref;
+ unsigned tgt_long next_ref;
+};
+
+struct new_module {
+ unsigned tgt_long size_of_struct; /* == sizeof(module) */
+ unsigned tgt_long next;
+ unsigned tgt_long name;
+ unsigned tgt_long size;
+
+ tgt_long usecount;
+ unsigned tgt_long flags; /* AUTOCLEAN et al */
+
+ unsigned nsyms;
+ unsigned ndeps;
+
+ unsigned tgt_long syms;
+ unsigned tgt_long deps;
+ unsigned tgt_long refs;
+ unsigned tgt_long init;
+ unsigned tgt_long cleanup;
+ unsigned tgt_long ex_table_start;
+ unsigned tgt_long ex_table_end;
+#ifdef __alpha__
+ unsigned tgt_long gp;
+#endif
+ /* Everything after here is extension. */
+ unsigned tgt_long persist_start;
+ unsigned tgt_long persist_end;
+ unsigned tgt_long can_unload;
+ unsigned tgt_long runsize;
+ const char *kallsyms_start; /* All symbols for kernel debugging */
+ const char *kallsyms_end;
+ const char *archdata_start; /* arch specific data for module */
+ const char *archdata_end;
+ const char *kernel_data; /* Reserved for kernel internal use */
+};
+
+#ifdef ARCHDATAM
+#define ARCHDATA_SEC_NAME ARCHDATAM
+#else
+#define ARCHDATA_SEC_NAME "__archdata"
+#endif
+#define KALLSYMS_SEC_NAME "__kallsyms"
+
+
+struct new_module_info {
+ unsigned long addr;
+ unsigned long size;
+ unsigned long flags;
+ long usecount;
+};
+
+/* Bits of module.flags. */
+enum {
+ NEW_MOD_RUNNING = 1,
+ NEW_MOD_DELETED = 2,
+ NEW_MOD_AUTOCLEAN = 4,
+ NEW_MOD_VISITED = 8,
+ NEW_MOD_USED_ONCE = 16
+};
+
+int init_module(const char *name, const struct new_module *);
+int query_module(const char *name, int which, void *buf,
+ size_t bufsize, size_t *ret);
+
+/* Values for query_module's which. */
+enum {
+ QM_MODULES = 1,
+ QM_DEPS = 2,
+ QM_REFS = 3,
+ QM_SYMBOLS = 4,
+ QM_INFO = 5
+};
+
+/*======================================================================*/
+/* The system calls unchanged between 2.0 and 2.1. */
+
+unsigned long create_module(const char *, size_t);
+int delete_module(const char *);
+
+
+#endif /* module.h */
+
+//----------------------------------------------------------------------------
+//--------end of modutils module.h
+//----------------------------------------------------------------------------
+
+
+
+//----------------------------------------------------------------------------
+//--------modutils obj.h, lines 253-462
+//----------------------------------------------------------------------------
+
+/* Elf object file loading and relocation routines.
+ Copyright 1996, 1997 Linux International.
+
+ Contributed by Richard Henderson <rth@tamu.edu>
+
+ This file is part of the Linux modutils.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+
+#ifndef MODUTILS_OBJ_H
+
+/* The relocatable object is manipulated using elfin types. */
+
+#include <stdio.h>
+#include <elf.h>
+#include <endian.h>
+
+#ifndef ElfW
+# if ELFCLASSM == ELFCLASS32
+# define ElfW(x) Elf32_ ## x
+# define ELFW(x) ELF32_ ## x
+# else
+# define ElfW(x) Elf64_ ## x
+# define ELFW(x) ELF64_ ## x
+# endif
+#endif
+
+/* For some reason this is missing from some ancient C libraries.... */
+#ifndef ELF32_ST_INFO
+# define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf))
+#endif
+
+#ifndef ELF64_ST_INFO
+# define ELF64_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf))
+#endif
+
+#define ELF_ST_BIND(info) ELFW(ST_BIND)(info)
+#define ELF_ST_TYPE(info) ELFW(ST_TYPE)(info)
+#define ELF_ST_INFO(bind, type) ELFW(ST_INFO)(bind, type)
+#define ELF_R_TYPE(val) ELFW(R_TYPE)(val)
+#define ELF_R_SYM(val) ELFW(R_SYM)(val)
+
+struct obj_string_patch;
+struct obj_symbol_patch;
+
+struct obj_section
+{
+ ElfW(Shdr) header;
+ const char *name;
+ char *contents;
+ struct obj_section *load_next;
+ int idx;
+};
+
+struct obj_symbol
+{
+ struct obj_symbol *next; /* hash table link */
+ const char *name;
+ unsigned long value;
+ unsigned long size;
+ int secidx; /* the defining section index/module */
+ int info;
+ int ksymidx; /* for export to the kernel symtab */
+ int referenced; /* actually used in the link */
+};
+
+/* Hardcode the hash table size. We shouldn't be needing so many
+ symbols that we begin to degrade performance, and we get a big win
+ by giving the compiler a constant divisor. */
+
+#define HASH_BUCKETS 521
+
+struct obj_file {
+ ElfW(Ehdr) header;
+ ElfW(Addr) baseaddr;
+ struct obj_section **sections;
+ struct obj_section *load_order;
+ struct obj_section **load_order_search_start;
+ struct obj_string_patch *string_patches;
+ struct obj_symbol_patch *symbol_patches;
+ int (*symbol_cmp)(const char *, const char *);
+ unsigned long (*symbol_hash)(const char *);
+ unsigned long local_symtab_size;
+ struct obj_symbol **local_symtab;
+ struct obj_symbol *symtab[HASH_BUCKETS];
+};
+
+enum obj_reloc {
+ obj_reloc_ok,
+ obj_reloc_overflow,
+ obj_reloc_dangerous,
+ obj_reloc_unhandled
+};
+
+struct obj_string_patch {
+ struct obj_string_patch *next;
+ int reloc_secidx;
+ ElfW(Addr) reloc_offset;
+ ElfW(Addr) string_offset;
+};
+
+struct obj_symbol_patch {
+ struct obj_symbol_patch *next;
+ int reloc_secidx;
+ ElfW(Addr) reloc_offset;
+ struct obj_symbol *sym;
+};
+
+
+/* Generic object manipulation routines. */
+
+static unsigned long obj_elf_hash(const char *);
+
+static unsigned long obj_elf_hash_n(const char *, unsigned long len);
+
+static struct obj_symbol *obj_find_symbol (struct obj_file *f,
+ const char *name);
+
+static ElfW(Addr) obj_symbol_final_value(struct obj_file *f,
+ struct obj_symbol *sym);
+
+#if ENABLE_FEATURE_INSMOD_VERSION_CHECKING
+static void obj_set_symbol_compare(struct obj_file *f,
+ int (*cmp)(const char *, const char *),
+ unsigned long (*hash)(const char *));
+#endif
+
+static struct obj_section *obj_find_section (struct obj_file *f,
+ const char *name);
+
+static void obj_insert_section_load_order (struct obj_file *f,
+ struct obj_section *sec);
+
+static struct obj_section *obj_create_alloced_section (struct obj_file *f,
+ const char *name,
+ unsigned long align,
+ unsigned long size);
+
+static struct obj_section *obj_create_alloced_section_first (struct obj_file *f,
+ const char *name,
+ unsigned long align,
+ unsigned long size);
+
+static void *obj_extend_section (struct obj_section *sec, unsigned long more);
+
+static int obj_string_patch(struct obj_file *f, int secidx, ElfW(Addr) offset,
+ const char *string);
+
+static int obj_symbol_patch(struct obj_file *f, int secidx, ElfW(Addr) offset,
+ struct obj_symbol *sym);
+
+static int obj_check_undefineds(struct obj_file *f);
+
+static void obj_allocate_commons(struct obj_file *f);
+
+static unsigned long obj_load_size (struct obj_file *f);
+
+static int obj_relocate (struct obj_file *f, ElfW(Addr) base);
+
+static struct obj_file *obj_load(FILE *f, int loadprogbits);
+
+static int obj_create_image (struct obj_file *f, char *image);
+
+/* Architecture specific manipulation routines. */
+
+static struct obj_file *arch_new_file (void);
+
+static struct obj_section *arch_new_section (void);
+
+static struct obj_symbol *arch_new_symbol (void);
+
+static enum obj_reloc arch_apply_relocation (struct obj_file *f,
+ struct obj_section *targsec,
+ struct obj_section *symsec,
+ struct obj_symbol *sym,
+ ElfW(RelM) *rel, ElfW(Addr) value);
+
+static void arch_create_got (struct obj_file *f);
+#if ENABLE_FEATURE_CHECK_TAINTED_MODULE
+static int obj_gpl_license(struct obj_file *f, const char **license);
+#endif /* FEATURE_CHECK_TAINTED_MODULE */
+#endif /* obj.h */
+//----------------------------------------------------------------------------
+//--------end of modutils obj.h
+//----------------------------------------------------------------------------
+
+
+/* SPFX is always a string, so it can be concatenated to string constants. */
+#ifdef SYMBOL_PREFIX
+#define SPFX SYMBOL_PREFIX
+#else
+#define SPFX ""
+#endif
+
+
+#define _PATH_MODULES "/lib/modules"
+enum { STRVERSIONLEN = 64 };
+
+/*======================================================================*/
+
+#define OPTION_STR "sLo:fkvqx" USE_FEATURE_INSMOD_LOAD_MAP("m")
+enum {
+ OPT_s = 0x1, // -s /* log to syslog */
+ /* Not supported but kernel needs this for request_module(),
+ as this calls: modprobe -k -s -- <module>
+ so silently ignore this flag */
+ OPT_L = 0x2, // -L /* Stub warning */
+ /* Compatibility with modprobe.
+ In theory, this does locking, but we don't do
+ that. So be careful and plan your life around not
+ loading the same module 50 times concurrently. */
+ OPT_o = 0x4, // -o /* name the output module */
+ OPT_f = 0x8, // -f /* force loading */
+ OPT_k = 0x10, // -k /* module loaded by kerneld, auto-cleanable */
+ OPT_v = 0x20, // -v /* verbose output */
+ OPT_q = 0x40, // -q /* silent */
+ OPT_x = 0x80, // -x /* do not export externs */
+ OPT_m = 0x100, // -m /* print module load map */
+};
+#define flag_force_load (option_mask32 & OPT_f)
+#define flag_autoclean (option_mask32 & OPT_k)
+#define flag_verbose (option_mask32 & OPT_v)
+#define flag_quiet (option_mask32 & OPT_q)
+#define flag_noexport (option_mask32 & OPT_x)
+#if ENABLE_FEATURE_INSMOD_LOAD_MAP
+#define flag_print_load_map (option_mask32 & OPT_m)
+#else
+#define flag_print_load_map 0
+#endif
+
+/*======================================================================*/
+
+#if defined(USE_LIST)
+
+struct arch_list_entry
+{
+ struct arch_list_entry *next;
+ LIST_ARCHTYPE addend;
+ int offset;
+ int inited : 1;
+};
+
+#endif
+
+#if defined(USE_SINGLE)
+
+struct arch_single_entry
+{
+ int offset;
+ int inited : 1;
+ int allocated : 1;
+};
+
+#endif
+
+#if defined(__mips__)
+struct mips_hi16
+{
+ struct mips_hi16 *next;
+ ElfW(Addr) *addr;
+ ElfW(Addr) value;
+};
+#endif
+
+struct arch_file {
+ struct obj_file root;
+#if defined(USE_PLT_ENTRIES)
+ struct obj_section *plt;
+#endif
+#if defined(USE_GOT_ENTRIES)
+ struct obj_section *got;
+#endif
+#if defined(__mips__)
+ struct mips_hi16 *mips_hi16_list;
+#endif
+};
+
+struct arch_symbol {
+ struct obj_symbol root;
+#if defined(USE_PLT_ENTRIES)
+#if defined(USE_PLT_LIST)
+ struct arch_list_entry *pltent;
+#else
+ struct arch_single_entry pltent;
+#endif
+#endif
+#if defined(USE_GOT_ENTRIES)
+ struct arch_single_entry gotent;
+#endif
+};
+
+
+struct external_module {
+ const char *name;
+ ElfW(Addr) addr;
+ int used;
+ size_t nsyms;
+ struct new_module_symbol *syms;
+};
+
+static struct new_module_symbol *ksyms;
+static size_t nksyms;
+
+static struct external_module *ext_modules;
+static int n_ext_modules;
+static int n_ext_modules_used;
+extern int delete_module(const char *);
+
+static char *m_filename;
+static char *m_fullName;
+
+
+/*======================================================================*/
+
+
+static int check_module_name_match(const char *filename, struct stat *statbuf,
+ void *userdata, int depth)
+{
+ char *fullname = (char *) userdata;
+
+ if (fullname[0] == '\0')
+ return FALSE;
+ else {
+ char *tmp, *tmp1 = xstrdup(filename);
+ tmp = bb_get_last_path_component(tmp1);
+ if (strcmp(tmp, fullname) == 0) {
+ free(tmp1);
+ /* Stop searching if we find a match */
+ m_filename = xstrdup(filename);
+ return FALSE;
+ }
+ free(tmp1);
+ }
+ return TRUE;
+}
+
+
+/*======================================================================*/
+
+static struct obj_file *arch_new_file(void)
+{
+ struct arch_file *f;
+ f = xmalloc(sizeof(*f));
+
+ memset(f, 0, sizeof(*f));
+
+ return &f->root;
+}
+
+static struct obj_section *arch_new_section(void)
+{
+ return xmalloc(sizeof(struct obj_section));
+}
+
+static struct obj_symbol *arch_new_symbol(void)
+{
+ struct arch_symbol *sym;
+ sym = xmalloc(sizeof(*sym));
+
+ memset(sym, 0, sizeof(*sym));
+
+ return &sym->root;
+}
+
+static enum obj_reloc
+arch_apply_relocation(struct obj_file *f,
+ struct obj_section *targsec,
+ struct obj_section *symsec,
+ struct obj_symbol *sym,
+ ElfW(RelM) *rel, ElfW(Addr) v)
+{
+ struct arch_file *ifile = (struct arch_file *) f;
+ enum obj_reloc ret = obj_reloc_ok;
+ ElfW(Addr) *loc = (ElfW(Addr) *) (targsec->contents + rel->r_offset);
+ ElfW(Addr) dot = targsec->header.sh_addr + rel->r_offset;
+#if defined(USE_GOT_ENTRIES) || defined(USE_PLT_ENTRIES)
+ struct arch_symbol *isym = (struct arch_symbol *) sym;
+#endif
+#if defined(__arm__) || defined(__i386__) || defined(__mc68000__) || defined(__sh__) || defined(__s390__)
+#if defined(USE_GOT_ENTRIES)
+ ElfW(Addr) got = ifile->got ? ifile->got->header.sh_addr : 0;
+#endif
+#endif
+#if defined(USE_PLT_ENTRIES)
+ ElfW(Addr) plt = ifile->plt ? ifile->plt->header.sh_addr : 0;
+ unsigned long *ip;
+# if defined(USE_PLT_LIST)
+ struct arch_list_entry *pe;
+# else
+ struct arch_single_entry *pe;
+# endif
+#endif
+
+ switch (ELF_R_TYPE(rel->r_info)) {
+
+#if defined(__arm__)
+
+ case R_ARM_NONE:
+ break;
+
+ case R_ARM_ABS32:
+ *loc += v;
+ break;
+
+ case R_ARM_GOT32:
+ goto bb_use_got;
+
+ case R_ARM_GOTPC:
+ /* relative reloc, always to _GLOBAL_OFFSET_TABLE_
+ * (which is .got) similar to branch,
+ * but is full 32 bits relative */
+
+ *loc += got - dot;
+ break;
+
+ case R_ARM_PC24:
+ case R_ARM_PLT32:
+ goto bb_use_plt;
+
+ case R_ARM_GOTOFF: /* address relative to the got */
+ *loc += v - got;
+ break;
+
+#elif defined(__cris__)
+
+ case R_CRIS_NONE:
+ break;
+
+ case R_CRIS_32:
+ /* CRIS keeps the relocation value in the r_addend field and
+ * should not use whats in *loc at all
+ */
+ *loc = v;
+ break;
+
+#elif defined(__H8300H__) || defined(__H8300S__)
+
+ case R_H8_DIR24R8:
+ loc = (ElfW(Addr) *)((ElfW(Addr))loc - 1);
+ *loc = (*loc & 0xff000000) | ((*loc & 0xffffff) + v);
+ break;
+ case R_H8_DIR24A8:
+ *loc += v;
+ break;
+ case R_H8_DIR32:
+ case R_H8_DIR32A16:
+ *loc += v;
+ break;
+ case R_H8_PCREL16:
+ v -= dot + 2;
+ if ((ElfW(Sword))v > 0x7fff ||
+ (ElfW(Sword))v < -(ElfW(Sword))0x8000)
+ ret = obj_reloc_overflow;
+ else
+ *(unsigned short *)loc = v;
+ break;
+ case R_H8_PCREL8:
+ v -= dot + 1;
+ if ((ElfW(Sword))v > 0x7f ||
+ (ElfW(Sword))v < -(ElfW(Sword))0x80)
+ ret = obj_reloc_overflow;
+ else
+ *(unsigned char *)loc = v;
+ break;
+
+#elif defined(__i386__)
+
+ case R_386_NONE:
+ break;
+
+ case R_386_32:
+ *loc += v;
+ break;
+
+ case R_386_PLT32:
+ case R_386_PC32:
+ *loc += v - dot;
+ break;
+
+ case R_386_GLOB_DAT:
+ case R_386_JMP_SLOT:
+ *loc = v;
+ break;
+
+ case R_386_RELATIVE:
+ *loc += f->baseaddr;
+ break;
+
+ case R_386_GOTPC:
+ *loc += got - dot;
+ break;
+
+ case R_386_GOT32:
+ goto bb_use_got;
+
+ case R_386_GOTOFF:
+ *loc += v - got;
+ break;
+
+#elif defined (__microblaze__)
+ case R_MICROBLAZE_NONE:
+ case R_MICROBLAZE_64_NONE:
+ case R_MICROBLAZE_32_SYM_OP_SYM:
+ case R_MICROBLAZE_32_PCREL:
+ break;
+
+ case R_MICROBLAZE_64_PCREL: {
+ /* dot is the address of the current instruction.
+ * v is the target symbol address.
+ * So we need to extract the offset in the code,
+ * adding v, then subtrating the current address
+ * of this instruction.
+ * Ex: "IMM 0xFFFE bralid 0x0000" = "bralid 0xFFFE0000"
+ */
+
+ /* Get split offset stored in code */
+ unsigned int temp = (loc[0] & 0xFFFF) << 16 |
+ (loc[1] & 0xFFFF);
+
+ /* Adjust relative offset. -4 adjustment required
+ * because dot points to the IMM insn, but branch
+ * is computed relative to the branch instruction itself.
+ */
+ temp += v - dot - 4;
+
+ /* Store back into code */
+ loc[0] = (loc[0] & 0xFFFF0000) | temp >> 16;
+ loc[1] = (loc[1] & 0xFFFF0000) | (temp & 0xFFFF);
+
+ break;
+ }
+
+ case R_MICROBLAZE_32:
+ *loc += v;
+ break;
+
+ case R_MICROBLAZE_64: {
+ /* Get split pointer stored in code */
+ unsigned int temp1 = (loc[0] & 0xFFFF) << 16 |
+ (loc[1] & 0xFFFF);
+
+ /* Add reloc offset */
+ temp1+=v;
+
+ /* Store back into code */
+ loc[0] = (loc[0] & 0xFFFF0000) | temp1 >> 16;
+ loc[1] = (loc[1] & 0xFFFF0000) | (temp1 & 0xFFFF);
+
+ break;
+ }
+
+ case R_MICROBLAZE_32_PCREL_LO:
+ case R_MICROBLAZE_32_LO:
+ case R_MICROBLAZE_SRO32:
+ case R_MICROBLAZE_SRW32:
+ ret = obj_reloc_unhandled;
+ break;
+
+#elif defined(__mc68000__)
+
+ case R_68K_NONE:
+ break;
+
+ case R_68K_32:
+ *loc += v;
+ break;
+
+ case R_68K_8:
+ if (v > 0xff) {
+ ret = obj_reloc_overflow;
+ }
+ *(char *)loc = v;
+ break;
+
+ case R_68K_16:
+ if (v > 0xffff) {
+ ret = obj_reloc_overflow;
+ }
+ *(short *)loc = v;
+ break;
+
+ case R_68K_PC8:
+ v -= dot;
+ if ((ElfW(Sword))v > 0x7f ||
+ (ElfW(Sword))v < -(ElfW(Sword))0x80) {
+ ret = obj_reloc_overflow;
+ }
+ *(char *)loc = v;
+ break;
+
+ case R_68K_PC16:
+ v -= dot;
+ if ((ElfW(Sword))v > 0x7fff ||
+ (ElfW(Sword))v < -(ElfW(Sword))0x8000) {
+ ret = obj_reloc_overflow;
+ }
+ *(short *)loc = v;
+ break;
+
+ case R_68K_PC32:
+ *(int *)loc = v - dot;
+ break;
+
+ case R_68K_GLOB_DAT:
+ case R_68K_JMP_SLOT:
+ *loc = v;
+ break;
+
+ case R_68K_RELATIVE:
+ *(int *)loc += f->baseaddr;
+ break;
+
+ case R_68K_GOT32:
+ goto bb_use_got;
+
+# ifdef R_68K_GOTOFF
+ case R_68K_GOTOFF:
+ *loc += v - got;
+ break;
+# endif
+
+#elif defined(__mips__)
+
+ case R_MIPS_NONE:
+ break;
+
+ case R_MIPS_32:
+ *loc += v;
+ break;
+
+ case R_MIPS_26:
+ if (v % 4)
+ ret = obj_reloc_dangerous;
+ if ((v & 0xf0000000) != ((dot + 4) & 0xf0000000))
+ ret = obj_reloc_overflow;
+ *loc =
+ (*loc & ~0x03ffffff) | ((*loc + (v >> 2)) &
+ 0x03ffffff);
+ break;
+
+ case R_MIPS_HI16:
+ {
+ struct mips_hi16 *n;
+
+ /* We cannot relocate this one now because we don't know the value
+ of the carry we need to add. Save the information, and let LO16
+ do the actual relocation. */
+ n = xmalloc(sizeof *n);
+ n->addr = loc;
+ n->value = v;
+ n->next = ifile->mips_hi16_list;
+ ifile->mips_hi16_list = n;
+ break;
+ }
+
+ case R_MIPS_LO16:
+ {
+ unsigned long insnlo = *loc;
+ ElfW(Addr) val, vallo;
+
+ /* Sign extend the addend we extract from the lo insn. */
+ vallo = ((insnlo & 0xffff) ^ 0x8000) - 0x8000;
+
+ if (ifile->mips_hi16_list != NULL) {
+ struct mips_hi16 *l;
+
+ l = ifile->mips_hi16_list;
+ while (l != NULL) {
+ struct mips_hi16 *next;
+ unsigned long insn;
+
+ /* Do the HI16 relocation. Note that we actually don't
+ need to know anything about the LO16 itself, except where
+ to find the low 16 bits of the addend needed by the LO16. */
+ insn = *l->addr;
+ val =
+ ((insn & 0xffff) << 16) +
+ vallo;
+ val += v;
+
+ /* Account for the sign extension that will happen in the
+ low bits. */
+ val =
+ ((val >> 16) +
+ ((val & 0x8000) !=
+ 0)) & 0xffff;
+
+ insn = (insn & ~0xffff) | val;
+ *l->addr = insn;
+
+ next = l->next;
+ free(l);
+ l = next;
+ }
+
+ ifile->mips_hi16_list = NULL;
+ }
+
+ /* Ok, we're done with the HI16 relocs. Now deal with the LO16. */
+ val = v + vallo;
+ insnlo = (insnlo & ~0xffff) | (val & 0xffff);
+ *loc = insnlo;
+ break;
+ }
+
+#elif defined(__nios2__)
+
+ case R_NIOS2_NONE:
+ break;
+
+ case R_NIOS2_BFD_RELOC_32:
+ *loc += v;
+ break;
+
+ case R_NIOS2_BFD_RELOC_16:
+ if (v > 0xffff) {
+ ret = obj_reloc_overflow;
+ }
+ *(short *)loc = v;
+ break;
+
+ case R_NIOS2_BFD_RELOC_8:
+ if (v > 0xff) {
+ ret = obj_reloc_overflow;
+ }
+ *(char *)loc = v;
+ break;
+
+ case R_NIOS2_S16:
+ {
+ Elf32_Addr word;
+
+ if ((Elf32_Sword)v > 0x7fff ||
+ (Elf32_Sword)v < -(Elf32_Sword)0x8000) {
+ ret = obj_reloc_overflow;
+ }
+
+ word = *loc;
+ *loc = ((((word >> 22) << 16) | (v & 0xffff)) << 6) |
+ (word & 0x3f);
+ }
+ break;
+
+ case R_NIOS2_U16:
+ {
+ Elf32_Addr word;
+
+ if (v > 0xffff) {
+ ret = obj_reloc_overflow;
+ }
+
+ word = *loc;
+ *loc = ((((word >> 22) << 16) | (v & 0xffff)) << 6) |
+ (word & 0x3f);
+ }
+ break;
+
+ case R_NIOS2_PCREL16:
+ {
+ Elf32_Addr word;
+
+ v -= dot + 4;
+ if ((Elf32_Sword)v > 0x7fff ||
+ (Elf32_Sword)v < -(Elf32_Sword)0x8000) {
+ ret = obj_reloc_overflow;
+ }
+
+ word = *loc;
+ *loc = ((((word >> 22) << 16) | (v & 0xffff)) << 6) | (word & 0x3f);
+ }
+ break;
+
+ case R_NIOS2_GPREL:
+ {
+ Elf32_Addr word, gp;
+ /* get _gp */
+ gp = obj_symbol_final_value(f, obj_find_symbol(f, SPFX "_gp"));
+ v-=gp;
+ if ((Elf32_Sword)v > 0x7fff ||
+ (Elf32_Sword)v < -(Elf32_Sword)0x8000) {
+ ret = obj_reloc_overflow;
+ }
+
+ word = *loc;
+ *loc = ((((word >> 22) << 16) | (v & 0xffff)) << 6) | (word & 0x3f);
+ }
+ break;
+
+ case R_NIOS2_CALL26:
+ if (v & 3)
+ ret = obj_reloc_dangerous;
+ if ((v >> 28) != (dot >> 28))
+ ret = obj_reloc_overflow;
+ *loc = (*loc & 0x3f) | ((v >> 2) << 6);
+ break;
+
+ case R_NIOS2_IMM5:
+ {
+ Elf32_Addr word;
+
+ if (v > 0x1f) {
+ ret = obj_reloc_overflow;
+ }
+
+ word = *loc & ~0x7c0;
+ *loc = word | ((v & 0x1f) << 6);
+ }
+ break;
+
+ case R_NIOS2_IMM6:
+ {
+ Elf32_Addr word;
+
+ if (v > 0x3f) {
+ ret = obj_reloc_overflow;
+ }
+
+ word = *loc & ~0xfc0;
+ *loc = word | ((v & 0x3f) << 6);
+ }
+ break;
+
+ case R_NIOS2_IMM8:
+ {
+ Elf32_Addr word;
+
+ if (v > 0xff) {
+ ret = obj_reloc_overflow;
+ }
+
+ word = *loc & ~0x3fc0;
+ *loc = word | ((v & 0xff) << 6);
+ }
+ break;
+
+ case R_NIOS2_HI16:
+ {
+ Elf32_Addr word;
+
+ word = *loc;
+ *loc = ((((word >> 22) << 16) | ((v >>16) & 0xffff)) << 6) |
+ (word & 0x3f);
+ }
+ break;
+
+ case R_NIOS2_LO16:
+ {
+ Elf32_Addr word;
+
+ word = *loc;
+ *loc = ((((word >> 22) << 16) | (v & 0xffff)) << 6) |
+ (word & 0x3f);
+ }
+ break;
+
+ case R_NIOS2_HIADJ16:
+ {
+ Elf32_Addr word1, word2;
+
+ word1 = *loc;
+ word2 = ((v >> 16) + ((v >> 15) & 1)) & 0xffff;
+ *loc = ((((word1 >> 22) << 16) | word2) << 6) |
+ (word1 & 0x3f);
+ }
+ break;
+
+#elif defined(__powerpc64__)
+ /* PPC64 needs a 2.6 kernel, 2.4 module relocation irrelevant */
+
+#elif defined(__powerpc__)
+
+ case R_PPC_ADDR16_HA:
+ *(unsigned short *)loc = (v + 0x8000) >> 16;
+ break;
+
+ case R_PPC_ADDR16_HI:
+ *(unsigned short *)loc = v >> 16;
+ break;
+
+ case R_PPC_ADDR16_LO:
+ *(unsigned short *)loc = v;
+ break;
+
+ case R_PPC_REL24:
+ goto bb_use_plt;
+
+ case R_PPC_REL32:
+ *loc = v - dot;
+ break;
+
+ case R_PPC_ADDR32:
+ *loc = v;
+ break;
+
+#elif defined(__s390__)
+
+ case R_390_32:
+ *(unsigned int *) loc += v;
+ break;
+ case R_390_16:
+ *(unsigned short *) loc += v;
+ break;
+ case R_390_8:
+ *(unsigned char *) loc += v;
+ break;
+
+ case R_390_PC32:
+ *(unsigned int *) loc += v - dot;
+ break;
+ case R_390_PC16DBL:
+ *(unsigned short *) loc += (v - dot) >> 1;
+ break;
+ case R_390_PC16:
+ *(unsigned short *) loc += v - dot;
+ break;
+
+ case R_390_PLT32:
+ case R_390_PLT16DBL:
+ /* find the plt entry and initialize it. */
+ pe = (struct arch_single_entry *) &isym->pltent;
+ if (pe->inited == 0) {
+ ip = (unsigned long *)(ifile->plt->contents + pe->offset);
+ ip[0] = 0x0d105810; /* basr 1,0; lg 1,10(1); br 1 */
+ ip[1] = 0x100607f1;
+ if (ELF_R_TYPE(rel->r_info) == R_390_PLT16DBL)
+ ip[2] = v - 2;
+ else
+ ip[2] = v;
+ pe->inited = 1;
+ }
+
+ /* Insert relative distance to target. */
+ v = plt + pe->offset - dot;
+ if (ELF_R_TYPE(rel->r_info) == R_390_PLT32)
+ *(unsigned int *) loc = (unsigned int) v;
+ else if (ELF_R_TYPE(rel->r_info) == R_390_PLT16DBL)
+ *(unsigned short *) loc = (unsigned short) ((v + 2) >> 1);
+ break;
+
+ case R_390_GLOB_DAT:
+ case R_390_JMP_SLOT:
+ *loc = v;
+ break;
+
+ case R_390_RELATIVE:
+ *loc += f->baseaddr;
+ break;
+
+ case R_390_GOTPC:
+ *(unsigned long *) loc += got - dot;
+ break;
+
+ case R_390_GOT12:
+ case R_390_GOT16:
+ case R_390_GOT32:
+ if (!isym->gotent.inited)
+ {
+ isym->gotent.inited = 1;
+ *(ElfW(Addr) *)(ifile->got->contents + isym->gotent.offset) = v;
+ }
+ if (ELF_R_TYPE(rel->r_info) == R_390_GOT12)
+ *(unsigned short *) loc |= (*(unsigned short *) loc + isym->gotent.offset) & 0xfff;
+ else if (ELF_R_TYPE(rel->r_info) == R_390_GOT16)
+ *(unsigned short *) loc += isym->gotent.offset;
+ else if (ELF_R_TYPE(rel->r_info) == R_390_GOT32)
+ *(unsigned int *) loc += isym->gotent.offset;
+ break;
+
+# ifndef R_390_GOTOFF32
+# define R_390_GOTOFF32 R_390_GOTOFF
+# endif
+ case R_390_GOTOFF32:
+ *loc += v - got;
+ break;
+
+#elif defined(__sh__)
+
+ case R_SH_NONE:
+ break;
+
+ case R_SH_DIR32:
+ *loc += v;
+ break;
+
+ case R_SH_REL32:
+ *loc += v - dot;
+ break;
+
+ case R_SH_PLT32:
+ *loc = v - dot;
+ break;
+
+ case R_SH_GLOB_DAT:
+ case R_SH_JMP_SLOT:
+ *loc = v;
+ break;
+
+ case R_SH_RELATIVE:
+ *loc = f->baseaddr + rel->r_addend;
+ break;
+
+ case R_SH_GOTPC:
+ *loc = got - dot + rel->r_addend;
+ break;
+
+ case R_SH_GOT32:
+ goto bb_use_got;
+
+ case R_SH_GOTOFF:
+ *loc = v - got;
+ break;
+
+# if defined(__SH5__)
+ case R_SH_IMM_MEDLOW16:
+ case R_SH_IMM_LOW16:
+ {
+ ElfW(Addr) word;
+
+ if (ELF_R_TYPE(rel->r_info) == R_SH_IMM_MEDLOW16)
+ v >>= 16;
+
+ /*
+ * movi and shori have the format:
+ *
+ * | op | imm | reg | reserved |
+ * 31..26 25..10 9.. 4 3 .. 0
+ *
+ * so we simply mask and or in imm.
+ */
+ word = *loc & ~0x3fffc00;
+ word |= (v & 0xffff) << 10;
+
+ *loc = word;
+
+ break;
+ }
+
+ case R_SH_IMM_MEDLOW16_PCREL:
+ case R_SH_IMM_LOW16_PCREL:
+ {
+ ElfW(Addr) word;
+
+ word = *loc & ~0x3fffc00;
+
+ v -= dot;
+
+ if (ELF_R_TYPE(rel->r_info) == R_SH_IMM_MEDLOW16_PCREL)
+ v >>= 16;
+
+ word |= (v & 0xffff) << 10;
+
+ *loc = word;
+
+ break;
+ }
+# endif /* __SH5__ */
+
+#elif defined (__v850e__)
+
+ case R_V850_NONE:
+ break;
+
+ case R_V850_32:
+ /* We write two shorts instead of a long because even
+ 32-bit insns only need half-word alignment, but
+ 32-bit data needs to be long-word aligned. */
+ v += ((unsigned short *)loc)[0];
+ v += ((unsigned short *)loc)[1] << 16;
+ ((unsigned short *)loc)[0] = v & 0xffff;
+ ((unsigned short *)loc)[1] = (v >> 16) & 0xffff;
+ break;
+
+ case R_V850_22_PCREL:
+ goto bb_use_plt;
+
+#elif defined(__x86_64__)
+
+ case R_X86_64_NONE:
+ break;
+
+ case R_X86_64_64:
+ *loc += v;
+ break;
+
+ case R_X86_64_32:
+ *(unsigned int *) loc += v;
+ if (v > 0xffffffff)
+ {
+ ret = obj_reloc_overflow; /* Kernel module compiled without -mcmodel=kernel. */
+ /* error("Possibly is module compiled without -mcmodel=kernel!"); */
+ }
+ break;
+
+ case R_X86_64_32S:
+ *(signed int *) loc += v;
+ break;
+
+ case R_X86_64_16:
+ *(unsigned short *) loc += v;
+ break;
+
+ case R_X86_64_8:
+ *(unsigned char *) loc += v;
+ break;
+
+ case R_X86_64_PC32:
+ *(unsigned int *) loc += v - dot;
+ break;
+
+ case R_X86_64_PC16:
+ *(unsigned short *) loc += v - dot;
+ break;
+
+ case R_X86_64_PC8:
+ *(unsigned char *) loc += v - dot;
+ break;
+
+ case R_X86_64_GLOB_DAT:
+ case R_X86_64_JUMP_SLOT:
+ *loc = v;
+ break;
+
+ case R_X86_64_RELATIVE:
+ *loc += f->baseaddr;
+ break;
+
+ case R_X86_64_GOT32:
+ case R_X86_64_GOTPCREL:
+ goto bb_use_got;
+# if 0
+ if (!isym->gotent.reloc_done)
+ {
+ isym->gotent.reloc_done = 1;
+ *(Elf64_Addr *)(ifile->got->contents + isym->gotent.offset) = v;
+ }
+ /* XXX are these really correct? */
+ if (ELF64_R_TYPE(rel->r_info) == R_X86_64_GOTPCREL)
+ *(unsigned int *) loc += v + isym->gotent.offset;
+ else
+ *loc += isym->gotent.offset;
+ break;
+# endif
+
+#else
+# warning "no idea how to handle relocations on your arch"
+#endif
+
+ default:
+ printf("Warning: unhandled reloc %d\n",(int)ELF_R_TYPE(rel->r_info));
+ ret = obj_reloc_unhandled;
+ break;
+
+#if defined(USE_PLT_ENTRIES)
+
+bb_use_plt:
+
+ /* find the plt entry and initialize it if necessary */
+
+#if defined(USE_PLT_LIST)
+ for (pe = isym->pltent; pe != NULL && pe->addend != rel->r_addend;)
+ pe = pe->next;
+#else
+ pe = &isym->pltent;
+#endif
+
+ if (! pe->inited) {
+ ip = (unsigned long *) (ifile->plt->contents + pe->offset);
+
+ /* generate some machine code */
+
+#if defined(__arm__)
+ ip[0] = 0xe51ff004; /* ldr pc,[pc,#-4] */
+ ip[1] = v; /* sym@ */
+#endif
+#if defined(__powerpc__)
+ ip[0] = 0x3d600000 + ((v + 0x8000) >> 16); /* lis r11,sym@ha */
+ ip[1] = 0x396b0000 + (v & 0xffff); /* addi r11,r11,sym@l */
+ ip[2] = 0x7d6903a6; /* mtctr r11 */
+ ip[3] = 0x4e800420; /* bctr */
+#endif
+#if defined (__v850e__)
+ /* We have to trash a register, so we assume that any control
+ transfer more than 21-bits away must be a function call
+ (so we can use a call-clobbered register). */
+ ip[0] = 0x0621 + ((v & 0xffff) << 16); /* mov sym, r1 ... */
+ ip[1] = ((v >> 16) & 0xffff) + 0x610000; /* ...; jmp r1 */
+#endif
+ pe->inited = 1;
+ }
+
+ /* relative distance to target */
+ v -= dot;
+ /* if the target is too far away.... */
+#if defined (__arm__) || defined (__powerpc__)
+ if ((int)v < -0x02000000 || (int)v >= 0x02000000)
+#elif defined (__v850e__)
+ if ((ElfW(Sword))v > 0x1fffff || (ElfW(Sword))v < (ElfW(Sword))-0x200000)
+#endif
+ /* go via the plt */
+ v = plt + pe->offset - dot;
+
+#if defined (__v850e__)
+ if (v & 1)
+#else
+ if (v & 3)
+#endif
+ ret = obj_reloc_dangerous;
+
+ /* merge the offset into the instruction. */
+#if defined(__arm__)
+ /* Convert to words. */
+ v >>= 2;
+
+ *loc = (*loc & ~0x00ffffff) | ((v + *loc) & 0x00ffffff);
+#endif
+#if defined(__powerpc__)
+ *loc = (*loc & ~0x03fffffc) | (v & 0x03fffffc);
+#endif
+#if defined (__v850e__)
+ /* We write two shorts instead of a long because even 32-bit insns
+ only need half-word alignment, but the 32-bit data write needs
+ to be long-word aligned. */
+ ((unsigned short *)loc)[0] =
+ (*(unsigned short *)loc & 0xffc0) /* opcode + reg */
+ | ((v >> 16) & 0x3f); /* offs high part */
+ ((unsigned short *)loc)[1] =
+ (v & 0xffff); /* offs low part */
+#endif
+ break;
+#endif /* USE_PLT_ENTRIES */
+
+#if defined(USE_GOT_ENTRIES)
+bb_use_got:
+
+ /* needs an entry in the .got: set it, once */
+ if (!isym->gotent.inited) {
+ isym->gotent.inited = 1;
+ *(ElfW(Addr) *) (ifile->got->contents + isym->gotent.offset) = v;
+ }
+ /* make the reloc with_respect_to_.got */
+#if defined(__sh__)
+ *loc += isym->gotent.offset + rel->r_addend;
+#elif defined(__i386__) || defined(__arm__) || defined(__mc68000__)
+ *loc += isym->gotent.offset;
+#endif
+ break;
+
+#endif /* USE_GOT_ENTRIES */
+ }
+
+ return ret;
+}
+
+
+#if defined(USE_LIST)
+
+static int arch_list_add(ElfW(RelM) *rel, struct arch_list_entry **list,
+ int offset, int size)
+{
+ struct arch_list_entry *pe;
+
+ for (pe = *list; pe != NULL; pe = pe->next) {
+ if (pe->addend == rel->r_addend) {
+ break;
+ }
+ }
+
+ if (pe == NULL) {
+ pe = xmalloc(sizeof(struct arch_list_entry));
+ pe->next = *list;
+ pe->addend = rel->r_addend;
+ pe->offset = offset;
+ pe->inited = 0;
+ *list = pe;
+ return size;
+ }
+ return 0;
+}
+
+#endif
+
+#if defined(USE_SINGLE)
+
+static int arch_single_init(ElfW(RelM) *rel, struct arch_single_entry *single,
+ int offset, int size)
+{
+ if (single->allocated == 0) {
+ single->allocated = 1;
+ single->offset = offset;
+ single->inited = 0;
+ return size;
+ }
+ return 0;
+}
+
+#endif
+
+#if defined(USE_GOT_ENTRIES) || defined(USE_PLT_ENTRIES)
+
+static struct obj_section *arch_xsect_init(struct obj_file *f, const char *name,
+ int offset, int size)
+{
+ struct obj_section *myrelsec = obj_find_section(f, name);
+
+ if (offset == 0) {
+ offset += size;
+ }
+
+ if (myrelsec) {
+ obj_extend_section(myrelsec, offset);
+ } else {
+ myrelsec = obj_create_alloced_section(f, name,
+ size, offset);
+ }
+
+ return myrelsec;
+}
+
+#endif
+
+static void arch_create_got(struct obj_file *f)
+{
+#if defined(USE_GOT_ENTRIES) || defined(USE_PLT_ENTRIES)
+ struct arch_file *ifile = (struct arch_file *) f;
+ int i;
+#if defined(USE_GOT_ENTRIES)
+ int got_offset = 0, got_needed = 0, got_allocate;
+#endif
+#if defined(USE_PLT_ENTRIES)
+ int plt_offset = 0, plt_needed = 0, plt_allocate;
+#endif
+ struct obj_section *relsec, *symsec, *strsec;
+ ElfW(RelM) *rel, *relend;
+ ElfW(Sym) *symtab, *extsym;
+ const char *strtab, *name;
+ struct arch_symbol *intsym;
+
+ for (i = 0; i < f->header.e_shnum; ++i) {
+ relsec = f->sections[i];
+ if (relsec->header.sh_type != SHT_RELM)
+ continue;
+
+ symsec = f->sections[relsec->header.sh_link];
+ strsec = f->sections[symsec->header.sh_link];
+
+ rel = (ElfW(RelM) *) relsec->contents;
+ relend = rel + (relsec->header.sh_size / sizeof(ElfW(RelM)));
+ symtab = (ElfW(Sym) *) symsec->contents;
+ strtab = (const char *) strsec->contents;
+
+ for (; rel < relend; ++rel) {
+ extsym = &symtab[ELF_R_SYM(rel->r_info)];
+
+#if defined(USE_GOT_ENTRIES)
+ got_allocate = 0;
+#endif
+#if defined(USE_PLT_ENTRIES)
+ plt_allocate = 0;
+#endif
+
+ switch (ELF_R_TYPE(rel->r_info)) {
+#if defined(__arm__)
+ case R_ARM_PC24:
+ case R_ARM_PLT32:
+ plt_allocate = 1;
+ break;
+
+ case R_ARM_GOTOFF:
+ case R_ARM_GOTPC:
+ got_needed = 1;
+ continue;
+
+ case R_ARM_GOT32:
+ got_allocate = 1;
+ break;
+
+#elif defined(__i386__)
+ case R_386_GOTPC:
+ case R_386_GOTOFF:
+ got_needed = 1;
+ continue;
+
+ case R_386_GOT32:
+ got_allocate = 1;
+ break;
+
+#elif defined(__powerpc__)
+ case R_PPC_REL24:
+ plt_allocate = 1;
+ break;
+
+#elif defined(__mc68000__)
+ case R_68K_GOT32:
+ got_allocate = 1;
+ break;
+
+#ifdef R_68K_GOTOFF
+ case R_68K_GOTOFF:
+ got_needed = 1;
+ continue;
+#endif
+
+#elif defined(__sh__)
+ case R_SH_GOT32:
+ got_allocate = 1;
+ break;
+
+ case R_SH_GOTPC:
+ case R_SH_GOTOFF:
+ got_needed = 1;
+ continue;
+
+#elif defined (__v850e__)
+ case R_V850_22_PCREL:
+ plt_needed = 1;
+ break;
+
+#endif
+ default:
+ continue;
+ }
+
+ if (extsym->st_name != 0) {
+ name = strtab + extsym->st_name;
+ } else {
+ name = f->sections[extsym->st_shndx]->name;
+ }
+ intsym = (struct arch_symbol *) obj_find_symbol(f, name);
+#if defined(USE_GOT_ENTRIES)
+ if (got_allocate) {
+ got_offset += arch_single_init(
+ rel, &intsym->gotent,
+ got_offset, GOT_ENTRY_SIZE);
+
+ got_needed = 1;
+ }
+#endif
+#if defined(USE_PLT_ENTRIES)
+ if (plt_allocate) {
+#if defined(USE_PLT_LIST)
+ plt_offset += arch_list_add(
+ rel, &intsym->pltent,
+ plt_offset, PLT_ENTRY_SIZE);
+#else
+ plt_offset += arch_single_init(
+ rel, &intsym->pltent,
+ plt_offset, PLT_ENTRY_SIZE);
+#endif
+ plt_needed = 1;
+ }
+#endif
+ }
+ }
+
+#if defined(USE_GOT_ENTRIES)
+ if (got_needed) {
+ ifile->got = arch_xsect_init(f, ".got", got_offset,
+ GOT_ENTRY_SIZE);
+ }
+#endif
+
+#if defined(USE_PLT_ENTRIES)
+ if (plt_needed) {
+ ifile->plt = arch_xsect_init(f, ".plt", plt_offset,
+ PLT_ENTRY_SIZE);
+ }
+#endif
+
+#endif /* defined(USE_GOT_ENTRIES) || defined(USE_PLT_ENTRIES) */
+}
+
+/*======================================================================*/
+
+/* Standard ELF hash function. */
+static unsigned long obj_elf_hash_n(const char *name, unsigned long n)
+{
+ unsigned long h = 0;
+ unsigned long g;
+ unsigned char ch;
+
+ while (n > 0) {
+ ch = *name++;
+ h = (h << 4) + ch;
+ if ((g = (h & 0xf0000000)) != 0) {
+ h ^= g >> 24;
+ h &= ~g;
+ }
+ n--;
+ }
+ return h;
+}
+
+static unsigned long obj_elf_hash(const char *name)
+{
+ return obj_elf_hash_n(name, strlen(name));
+}
+
+#if ENABLE_FEATURE_INSMOD_VERSION_CHECKING
+/* String comparison for non-co-versioned kernel and module. */
+
+static int ncv_strcmp(const char *a, const char *b)
+{
+ size_t alen = strlen(a), blen = strlen(b);
+
+ if (blen == alen + 10 && b[alen] == '_' && b[alen + 1] == 'R')
+ return strncmp(a, b, alen);
+ else if (alen == blen + 10 && a[blen] == '_' && a[blen + 1] == 'R')
+ return strncmp(a, b, blen);
+ else
+ return strcmp(a, b);
+}
+
+/* String hashing for non-co-versioned kernel and module. Here
+ we are simply forced to drop the crc from the hash. */
+
+static unsigned long ncv_symbol_hash(const char *str)
+{
+ size_t len = strlen(str);
+ if (len > 10 && str[len - 10] == '_' && str[len - 9] == 'R')
+ len -= 10;
+ return obj_elf_hash_n(str, len);
+}
+
+static void
+obj_set_symbol_compare(struct obj_file *f,
+ int (*cmp) (const char *, const char *),
+ unsigned long (*hash) (const char *))
+{
+ if (cmp)
+ f->symbol_cmp = cmp;
+ if (hash) {
+ struct obj_symbol *tmptab[HASH_BUCKETS], *sym, *next;
+ int i;
+
+ f->symbol_hash = hash;
+
+ memcpy(tmptab, f->symtab, sizeof(tmptab));
+ memset(f->symtab, 0, sizeof(f->symtab));
+
+ for (i = 0; i < HASH_BUCKETS; ++i)
+ for (sym = tmptab[i]; sym; sym = next) {
+ unsigned long h = hash(sym->name) % HASH_BUCKETS;
+ next = sym->next;
+ sym->next = f->symtab[h];
+ f->symtab[h] = sym;
+ }
+ }
+}
+
+#endif /* FEATURE_INSMOD_VERSION_CHECKING */
+
+static struct obj_symbol *
+obj_add_symbol(struct obj_file *f, const char *name,
+ unsigned long symidx, int info,
+ int secidx, ElfW(Addr) value,
+ unsigned long size)
+{
+ struct obj_symbol *sym;
+ unsigned long hash = f->symbol_hash(name) % HASH_BUCKETS;
+ int n_type = ELF_ST_TYPE(info);
+ int n_binding = ELF_ST_BIND(info);
+
+ for (sym = f->symtab[hash]; sym; sym = sym->next)
+ if (f->symbol_cmp(sym->name, name) == 0) {
+ int o_secidx = sym->secidx;
+ int o_info = sym->info;
+ int o_type = ELF_ST_TYPE(o_info);
+ int o_binding = ELF_ST_BIND(o_info);
+
+ /* A redefinition! Is it legal? */
+
+ if (secidx == SHN_UNDEF)
+ return sym;
+ else if (o_secidx == SHN_UNDEF)
+ goto found;
+ else if (n_binding == STB_GLOBAL && o_binding == STB_LOCAL) {
+ /* Cope with local and global symbols of the same name
+ in the same object file, as might have been created
+ by ld -r. The only reason locals are now seen at this
+ level at all is so that we can do semi-sensible things
+ with parameters. */
+
+ struct obj_symbol *nsym, **p;
+
+ nsym = arch_new_symbol();
+ nsym->next = sym->next;
+ nsym->ksymidx = -1;
+
+ /* Excise the old (local) symbol from the hash chain. */
+ for (p = &f->symtab[hash]; *p != sym; p = &(*p)->next)
+ continue;
+ *p = sym = nsym;
+ goto found;
+ } else if (n_binding == STB_LOCAL) {
+ /* Another symbol of the same name has already been defined.
+ Just add this to the local table. */
+ sym = arch_new_symbol();
+ sym->next = NULL;
+ sym->ksymidx = -1;
+ f->local_symtab[symidx] = sym;
+ goto found;
+ } else if (n_binding == STB_WEAK)
+ return sym;
+ else if (o_binding == STB_WEAK)
+ goto found;
+ /* Don't unify COMMON symbols with object types the programmer
+ doesn't expect. */
+ else if (secidx == SHN_COMMON
+ && (o_type == STT_NOTYPE || o_type == STT_OBJECT))
+ return sym;
+ else if (o_secidx == SHN_COMMON
+ && (n_type == STT_NOTYPE || n_type == STT_OBJECT))
+ goto found;
+ else {
+ /* Don't report an error if the symbol is coming from
+ the kernel or some external module. */
+ if (secidx <= SHN_HIRESERVE)
+ bb_error_msg("%s multiply defined", name);
+ return sym;
+ }
+ }
+
+ /* Completely new symbol. */
+ sym = arch_new_symbol();
+ sym->next = f->symtab[hash];
+ f->symtab[hash] = sym;
+ sym->ksymidx = -1;
+
+ if (ELF_ST_BIND(info) == STB_LOCAL && symidx != -1) {
+ if (symidx >= f->local_symtab_size)
+ bb_error_msg("local symbol %s with index %ld exceeds local_symtab_size %ld",
+ name, (long) symidx, (long) f->local_symtab_size);
+ else
+ f->local_symtab[symidx] = sym;
+ }
+
+found:
+ sym->name = name;
+ sym->value = value;
+ sym->size = size;
+ sym->secidx = secidx;
+ sym->info = info;
+
+ return sym;
+}
+
+static struct obj_symbol *
+obj_find_symbol(struct obj_file *f, const char *name)
+{
+ struct obj_symbol *sym;
+ unsigned long hash = f->symbol_hash(name) % HASH_BUCKETS;
+
+ for (sym = f->symtab[hash]; sym; sym = sym->next)
+ if (f->symbol_cmp(sym->name, name) == 0)
+ return sym;
+
+ return NULL;
+}
+
+static ElfW(Addr) obj_symbol_final_value(struct obj_file * f, struct obj_symbol * sym)
+{
+ if (sym) {
+ if (sym->secidx >= SHN_LORESERVE)
+ return sym->value;
+
+ return sym->value + f->sections[sym->secidx]->header.sh_addr;
+ } else {
+ /* As a special case, a NULL sym has value zero. */
+ return 0;
+ }
+}
+
+static struct obj_section *obj_find_section(struct obj_file *f, const char *name)
+{
+ int i, n = f->header.e_shnum;
+
+ for (i = 0; i < n; ++i)
+ if (strcmp(f->sections[i]->name, name) == 0)
+ return f->sections[i];
+
+ return NULL;
+}
+
+static int obj_load_order_prio(struct obj_section *a)
+{
+ unsigned long af, ac;
+
+ af = a->header.sh_flags;
+
+ ac = 0;
+ if (a->name[0] != '.' || strlen(a->name) != 10 ||
+ strcmp(a->name + 5, ".init"))
+ ac |= 32;
+ if (af & SHF_ALLOC)
+ ac |= 16;
+ if (!(af & SHF_WRITE))
+ ac |= 8;
+ if (af & SHF_EXECINSTR)
+ ac |= 4;
+ if (a->header.sh_type != SHT_NOBITS)
+ ac |= 2;
+
+ return ac;
+}
+
+static void
+obj_insert_section_load_order(struct obj_file *f, struct obj_section *sec)
+{
+ struct obj_section **p;
+ int prio = obj_load_order_prio(sec);
+ for (p = f->load_order_search_start; *p; p = &(*p)->load_next)
+ if (obj_load_order_prio(*p) < prio)
+ break;
+ sec->load_next = *p;
+ *p = sec;
+}
+
+static struct obj_section *obj_create_alloced_section(struct obj_file *f,
+ const char *name,
+ unsigned long align,
+ unsigned long size)
+{
+ int newidx = f->header.e_shnum++;
+ struct obj_section *sec;
+
+ f->sections = xrealloc(f->sections, (newidx + 1) * sizeof(sec));
+ f->sections[newidx] = sec = arch_new_section();
+
+ memset(sec, 0, sizeof(*sec));
+ sec->header.sh_type = SHT_PROGBITS;
+ sec->header.sh_flags = SHF_WRITE | SHF_ALLOC;
+ sec->header.sh_size = size;
+ sec->header.sh_addralign = align;
+ sec->name = name;
+ sec->idx = newidx;
+ if (size)
+ sec->contents = xmalloc(size);
+
+ obj_insert_section_load_order(f, sec);
+
+ return sec;
+}
+
+static struct obj_section *obj_create_alloced_section_first(struct obj_file *f,
+ const char *name,
+ unsigned long align,
+ unsigned long size)
+{
+ int newidx = f->header.e_shnum++;
+ struct obj_section *sec;
+
+ f->sections = xrealloc(f->sections, (newidx + 1) * sizeof(sec));
+ f->sections[newidx] = sec = arch_new_section();
+
+ memset(sec, 0, sizeof(*sec));
+ sec->header.sh_type = SHT_PROGBITS;
+ sec->header.sh_flags = SHF_WRITE | SHF_ALLOC;
+ sec->header.sh_size = size;
+ sec->header.sh_addralign = align;
+ sec->name = name;
+ sec->idx = newidx;
+ if (size)
+ sec->contents = xmalloc(size);
+
+ sec->load_next = f->load_order;
+ f->load_order = sec;
+ if (f->load_order_search_start == &f->load_order)
+ f->load_order_search_start = &sec->load_next;
+
+ return sec;
+}
+
+static void *obj_extend_section(struct obj_section *sec, unsigned long more)
+{
+ unsigned long oldsize = sec->header.sh_size;
+ if (more) {
+ sec->contents = xrealloc(sec->contents, sec->header.sh_size += more);
+ }
+ return sec->contents + oldsize;
+}
+
+
+/* Conditionally add the symbols from the given symbol set to the
+ new module. */
+
+static int
+add_symbols_from( struct obj_file *f,
+ int idx, struct new_module_symbol *syms, size_t nsyms)
+{
+ struct new_module_symbol *s;
+ size_t i;
+ int used = 0;
+#ifdef SYMBOL_PREFIX
+ char *name_buf = 0;
+ size_t name_alloced_size = 0;
+#endif
+#if ENABLE_FEATURE_CHECK_TAINTED_MODULE
+ int gpl;
+
+ gpl = obj_gpl_license(f, NULL) == 0;
+#endif
+ for (i = 0, s = syms; i < nsyms; ++i, ++s) {
+ /* Only add symbols that are already marked external.
+ If we override locals we may cause problems for
+ argument initialization. We will also create a false
+ dependency on the module. */
+ struct obj_symbol *sym;
+ char *name;
+
+ /* GPL licensed modules can use symbols exported with
+ * EXPORT_SYMBOL_GPL, so ignore any GPLONLY_ prefix on the
+ * exported names. Non-GPL modules never see any GPLONLY_
+ * symbols so they cannot fudge it by adding the prefix on
+ * their references.
+ */
+ if (strncmp((char *)s->name, "GPLONLY_", 8) == 0) {
+#if ENABLE_FEATURE_CHECK_TAINTED_MODULE
+ if (gpl)
+ s->name += 8;
+ else
+#endif
+ continue;
+ }
+ name = (char *)s->name;
+
+#ifdef SYMBOL_PREFIX
+ /* Prepend SYMBOL_PREFIX to the symbol's name (the
+ kernel exports `C names', but module object files
+ reference `linker names'). */
+ size_t extra = sizeof SYMBOL_PREFIX;
+ size_t name_size = strlen (name) + extra;
+ if (name_size > name_alloced_size) {
+ name_alloced_size = name_size * 2;
+ name_buf = alloca (name_alloced_size);
+ }
+ strcpy (name_buf, SYMBOL_PREFIX);
+ strcpy (name_buf + extra - 1, name);
+ name = name_buf;
+#endif /* SYMBOL_PREFIX */
+
+ sym = obj_find_symbol(f, name);
+ if (sym && !(ELF_ST_BIND(sym->info) == STB_LOCAL)) {
+#ifdef SYMBOL_PREFIX
+ /* Put NAME_BUF into more permanent storage. */
+ name = xmalloc (name_size);
+ strcpy (name, name_buf);
+#endif
+ sym = obj_add_symbol(f, name, -1,
+ ELF_ST_INFO(STB_GLOBAL,
+ STT_NOTYPE),
+ idx, s->value, 0);
+ /* Did our symbol just get installed? If so, mark the
+ module as "used". */
+ if (sym->secidx == idx)
+ used = 1;
+ }
+ }
+
+ return used;
+}
+
+static void add_kernel_symbols(struct obj_file *f)
+{
+ struct external_module *m;
+ int i, nused = 0;
+
+ /* Add module symbols first. */
+
+ for (i = 0, m = ext_modules; i < n_ext_modules; ++i, ++m)
+ if (m->nsyms
+ && add_symbols_from(f, SHN_HIRESERVE + 2 + i, m->syms,
+ m->nsyms)) m->used = 1, ++nused;
+
+ n_ext_modules_used = nused;
+
+ /* And finally the symbols from the kernel proper. */
+
+ if (nksyms)
+ add_symbols_from(f, SHN_HIRESERVE + 1, ksyms, nksyms);
+}
+
+static char *get_modinfo_value(struct obj_file *f, const char *key)
+{
+ struct obj_section *sec;
+ char *p, *v, *n, *ep;
+ size_t klen = strlen(key);
+
+ sec = obj_find_section(f, ".modinfo");
+ if (sec == NULL)
+ return NULL;
+ p = sec->contents;
+ ep = p + sec->header.sh_size;
+ while (p < ep) {
+ v = strchr(p, '=');
+ n = strchr(p, '\0');
+ if (v) {
+ if (p + klen == v && strncmp(p, key, klen) == 0)
+ return v + 1;
+ } else {
+ if (p + klen == n && strcmp(p, key) == 0)
+ return n;
+ }
+ p = n + 1;
+ }
+
+ return NULL;
+}
+
+
+/*======================================================================*/
+/* Functions relating to module loading after 2.1.18. */
+
+static int
+new_process_module_arguments(struct obj_file *f, int argc, char **argv)
+{
+ while (argc > 0) {
+ char *p, *q, *key, *sym_name;
+ struct obj_symbol *sym;
+ char *contents, *loc;
+ int min, max, n;
+
+ p = *argv;
+ if ((q = strchr(p, '=')) == NULL) {
+ argc--;
+ continue;
+ }
+
+ key = alloca(q - p + 6);
+ memcpy(key, "parm_", 5);
+ memcpy(key + 5, p, q - p);
+ key[q - p + 5] = 0;
+
+ p = get_modinfo_value(f, key);
+ key += 5;
+ if (p == NULL) {
+ bb_error_msg("invalid parameter %s", key);
+ return 0;
+ }
+
+#ifdef SYMBOL_PREFIX
+ sym_name = alloca (strlen (key) + sizeof SYMBOL_PREFIX);
+ strcpy (sym_name, SYMBOL_PREFIX);
+ strcat (sym_name, key);
+#else
+ sym_name = key;
+#endif
+ sym = obj_find_symbol(f, sym_name);
+
+ /* Also check that the parameter was not resolved from the kernel. */
+ if (sym == NULL || sym->secidx > SHN_HIRESERVE) {
+ bb_error_msg("symbol for parameter %s not found", key);
+ return 0;
+ }
+
+ if (isdigit(*p)) {
+ min = strtoul(p, &p, 10);
+ if (*p == '-')
+ max = strtoul(p + 1, &p, 10);
+ else
+ max = min;
+ } else
+ min = max = 1;
+
+ contents = f->sections[sym->secidx]->contents;
+ loc = contents + sym->value;
+ n = (*++q != '\0');
+
+ while (1) {
+ if ((*p == 's') || (*p == 'c')) {
+ char *str;
+
+ /* Do C quoting if we begin with a ", else slurp the lot. */
+ if (*q == '"') {
+ char *r;
+
+ str = alloca(strlen(q));
+ for (r = str, q++; *q != '"'; ++q, ++r) {
+ if (*q == '\0') {
+ bb_error_msg("improperly terminated string argument for %s",
+ key);
+ return 0;
+ } else if (*q == '\\')
+ switch (*++q) {
+ case 'a':
+ *r = '\a';
+ break;
+ case 'b':
+ *r = '\b';
+ break;
+ case 'e':
+ *r = '\033';
+ break;
+ case 'f':
+ *r = '\f';
+ break;
+ case 'n':
+ *r = '\n';
+ break;
+ case 'r':
+ *r = '\r';
+ break;
+ case 't':
+ *r = '\t';
+ break;
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ {
+ int c = *q - '0';
+ if (q[1] >= '0' && q[1] <= '7') {
+ c = (c * 8) + *++q - '0';
+ if (q[1] >= '0' && q[1] <= '7')
+ c = (c * 8) + *++q - '0';
+ }
+ *r = c;
+ }
+ break;
+
+ default:
+ *r = *q;
+ break;
+ } else
+ *r = *q;
+ }
+ *r = '\0';
+ ++q;
+ } else {
+ char *r;
+
+ /* In this case, the string is not quoted. We will break
+ it using the coma (like for ints). If the user wants to
+ include comas in a string, he just has to quote it */
+
+ /* Search the next coma */
+ r = strchr(q, ',');
+
+ /* Found ? */
+ if (r != (char *) NULL) {
+ /* Recopy the current field */
+ str = alloca(r - q + 1);
+ memcpy(str, q, r - q);
+
+ /* I don't know if it is useful, as the previous case
+ doesn't nul terminate the string ??? */
+ str[r - q] = '\0';
+
+ /* Keep next fields */
+ q = r;
+ } else {
+ /* last string */
+ str = q;
+ q = (char*)"";
+ }
+ }
+
+ if (*p == 's') {
+ /* Normal string */
+ obj_string_patch(f, sym->secidx, loc - contents, str);
+ loc += tgt_sizeof_char_p;
+ } else {
+ /* Array of chars (in fact, matrix !) */
+ unsigned long charssize; /* size of each member */
+
+ /* Get the size of each member */
+ /* Probably we should do that outside the loop ? */
+ if (!isdigit(*(p + 1))) {
+ bb_error_msg("parameter type 'c' for %s must be followed by"
+ " the maximum size", key);
+ return 0;
+ }
+ charssize = strtoul(p + 1, (char **) NULL, 10);
+
+ /* Check length */
+ if (strlen(str) >= charssize) {
+ bb_error_msg("string too long for %s (max %ld)", key,
+ charssize - 1);
+ return 0;
+ }
+
+ /* Copy to location */
+ strcpy((char *) loc, str);
+ loc += charssize;
+ }
+ } else {
+ long v = strtoul(q, &q, 0);
+ switch (*p) {
+ case 'b':
+ *loc++ = v;
+ break;
+ case 'h':
+ *(short *) loc = v;
+ loc += tgt_sizeof_short;
+ break;
+ case 'i':
+ *(int *) loc = v;
+ loc += tgt_sizeof_int;
+ break;
+ case 'l':
+ *(long *) loc = v;
+ loc += tgt_sizeof_long;
+ break;
+
+ default:
+ bb_error_msg("unknown parameter type '%c' for %s", *p, key);
+ return 0;
+ }
+ }
+
+retry_end_of_value:
+ switch (*q) {
+ case '\0':
+ goto end_of_arg;
+
+ case ' ':
+ case '\t':
+ case '\n':
+ case '\r':
+ ++q;
+ goto retry_end_of_value;
+
+ case ',':
+ if (++n > max) {
+ bb_error_msg("too many values for %s (max %d)", key, max);
+ return 0;
+ }
+ ++q;
+ break;
+
+ default:
+ bb_error_msg("invalid argument syntax for %s", key);
+ return 0;
+ }
+ }
+
+end_of_arg:
+ if (n < min) {
+ bb_error_msg("too few values for %s (min %d)", key, min);
+ return 0;
+ }
+
+ argc--, argv++;
+ }
+
+ return 1;
+}
+
+#if ENABLE_FEATURE_INSMOD_VERSION_CHECKING
+static int new_is_module_checksummed(struct obj_file *f)
+{
+ const char *p = get_modinfo_value(f, "using_checksums");
+ if (p)
+ return xatoi(p);
+ else
+ return 0;
+}
+
+/* Get the module's kernel version in the canonical integer form. */
+
+static int
+new_get_module_version(struct obj_file *f, char str[STRVERSIONLEN])
+{
+ char *p, *q;
+ int a, b, c;
+
+ p = get_modinfo_value(f, "kernel_version");
+ if (p == NULL)
+ return -1;
+ safe_strncpy(str, p, STRVERSIONLEN);
+
+ a = strtoul(p, &p, 10);
+ if (*p != '.')
+ return -1;
+ b = strtoul(p + 1, &p, 10);
+ if (*p != '.')
+ return -1;
+ c = strtoul(p + 1, &q, 10);
+ if (p + 1 == q)
+ return -1;
+
+ return a << 16 | b << 8 | c;
+}
+
+#endif /* FEATURE_INSMOD_VERSION_CHECKING */
+
+
+/* Fetch the loaded modules, and all currently exported symbols. */
+
+static int new_get_kernel_symbols(void)
+{
+ char *module_names, *mn;
+ struct external_module *modules, *m;
+ struct new_module_symbol *syms, *s;
+ size_t ret, bufsize, nmod, nsyms, i, j;
+
+ /* Collect the loaded modules. */
+
+ module_names = xmalloc(bufsize = 256);
+retry_modules_load:
+ if (query_module(NULL, QM_MODULES, module_names, bufsize, &ret)) {
+ if (errno == ENOSPC && bufsize < ret) {
+ module_names = xrealloc(module_names, bufsize = ret);
+ goto retry_modules_load;
+ }
+ bb_perror_msg("QM_MODULES");
+ return 0;
+ }
+
+ n_ext_modules = nmod = ret;
+
+ /* Collect the modules' symbols. */
+
+ if (nmod) {
+ ext_modules = modules = xmalloc(nmod * sizeof(*modules));
+ memset(modules, 0, nmod * sizeof(*modules));
+ for (i = 0, mn = module_names, m = modules;
+ i < nmod; ++i, ++m, mn += strlen(mn) + 1) {
+ struct new_module_info info;
+
+ if (query_module(mn, QM_INFO, &info, sizeof(info), &ret)) {
+ if (errno == ENOENT) {
+ /* The module was removed out from underneath us. */
+ continue;
+ }
+ bb_perror_msg("query_module: QM_INFO: %s", mn);
+ return 0;
+ }
+
+ syms = xmalloc(bufsize = 1024);
+retry_mod_sym_load:
+ if (query_module(mn, QM_SYMBOLS, syms, bufsize, &ret)) {
+ switch (errno) {
+ case ENOSPC:
+ syms = xrealloc(syms, bufsize = ret);
+ goto retry_mod_sym_load;
+ case ENOENT:
+ /* The module was removed out from underneath us. */
+ continue;
+ default:
+ bb_perror_msg("query_module: QM_SYMBOLS: %s", mn);
+ return 0;
+ }
+ }
+ nsyms = ret;
+
+ m->name = mn;
+ m->addr = info.addr;
+ m->nsyms = nsyms;
+ m->syms = syms;
+
+ for (j = 0, s = syms; j < nsyms; ++j, ++s) {
+ s->name += (unsigned long) syms;
+ }
+ }
+ }
+
+ /* Collect the kernel's symbols. */
+
+ syms = xmalloc(bufsize = 16 * 1024);
+retry_kern_sym_load:
+ if (query_module(NULL, QM_SYMBOLS, syms, bufsize, &ret)) {
+ if (errno == ENOSPC && bufsize < ret) {
+ syms = xrealloc(syms, bufsize = ret);
+ goto retry_kern_sym_load;
+ }
+ bb_perror_msg("kernel: QM_SYMBOLS");
+ return 0;
+ }
+ nksyms = nsyms = ret;
+ ksyms = syms;
+
+ for (j = 0, s = syms; j < nsyms; ++j, ++s) {
+ s->name += (unsigned long) syms;
+ }
+ return 1;
+}
+
+
+/* Return the kernel symbol checksum version, or zero if not used. */
+
+static int new_is_kernel_checksummed(void)
+{
+ struct new_module_symbol *s;
+ size_t i;
+
+ /* Using_Versions is not the first symbol, but it should be in there. */
+
+ for (i = 0, s = ksyms; i < nksyms; ++i, ++s)
+ if (strcmp((char *) s->name, "Using_Versions") == 0)
+ return s->value;
+
+ return 0;
+}
+
+
+static int new_create_this_module(struct obj_file *f, const char *m_name)
+{
+ struct obj_section *sec;
+
+ sec = obj_create_alloced_section_first(f, ".this", tgt_sizeof_long,
+ sizeof(struct new_module));
+ memset(sec->contents, 0, sizeof(struct new_module));
+
+ obj_add_symbol(f, SPFX "__this_module", -1,
+ ELF_ST_INFO(STB_LOCAL, STT_OBJECT), sec->idx, 0,
+ sizeof(struct new_module));
+
+ obj_string_patch(f, sec->idx, offsetof(struct new_module, name),
+ m_name);
+
+ return 1;
+}
+
+#if ENABLE_FEATURE_INSMOD_KSYMOOPS_SYMBOLS
+/* add an entry to the __ksymtab section, creating it if necessary */
+static void new_add_ksymtab(struct obj_file *f, struct obj_symbol *sym)
+{
+ struct obj_section *sec;
+ ElfW(Addr) ofs;
+
+ /* ensure __ksymtab is allocated, EXPORT_NOSYMBOLS creates a non-alloc section.
+ * If __ksymtab is defined but not marked alloc, x out the first character
+ * (no obj_delete routine) and create a new __ksymtab with the correct
+ * characteristics.
+ */
+ sec = obj_find_section(f, "__ksymtab");
+ if (sec && !(sec->header.sh_flags & SHF_ALLOC)) {
+ *((char *)(sec->name)) = 'x'; /* override const */
+ sec = NULL;
+ }
+ if (!sec)
+ sec = obj_create_alloced_section(f, "__ksymtab",
+ tgt_sizeof_void_p, 0);
+ if (!sec)
+ return;
+ sec->header.sh_flags |= SHF_ALLOC;
+ /* Empty section might be byte-aligned */
+ sec->header.sh_addralign = tgt_sizeof_void_p;
+ ofs = sec->header.sh_size;
+ obj_symbol_patch(f, sec->idx, ofs, sym);
+ obj_string_patch(f, sec->idx, ofs + tgt_sizeof_void_p, sym->name);
+ obj_extend_section(sec, 2 * tgt_sizeof_char_p);
+}
+#endif /* FEATURE_INSMOD_KSYMOOPS_SYMBOLS */
+
+static int new_create_module_ksymtab(struct obj_file *f)
+{
+ struct obj_section *sec;
+ int i;
+
+ /* We must always add the module references. */
+
+ if (n_ext_modules_used) {
+ struct new_module_ref *dep;
+ struct obj_symbol *tm;
+
+ sec = obj_create_alloced_section(f, ".kmodtab", tgt_sizeof_void_p,
+ (sizeof(struct new_module_ref)
+ * n_ext_modules_used));
+ if (!sec)
+ return 0;
+
+ tm = obj_find_symbol(f, SPFX "__this_module");
+ dep = (struct new_module_ref *) sec->contents;
+ for (i = 0; i < n_ext_modules; ++i)
+ if (ext_modules[i].used) {
+ dep->dep = ext_modules[i].addr;
+ obj_symbol_patch(f, sec->idx,
+ (char *) &dep->ref - sec->contents, tm);
+ dep->next_ref = 0;
+ ++dep;
+ }
+ }
+
+ if (!flag_noexport && !obj_find_section(f, "__ksymtab")) {
+ size_t nsyms;
+ int *loaded;
+
+ sec = obj_create_alloced_section(f, "__ksymtab", tgt_sizeof_void_p, 0);
+
+ /* We don't want to export symbols residing in sections that
+ aren't loaded. There are a number of these created so that
+ we make sure certain module options don't appear twice. */
+
+ loaded = alloca(sizeof(int) * (i = f->header.e_shnum));
+ while (--i >= 0)
+ loaded[i] = (f->sections[i]->header.sh_flags & SHF_ALLOC) != 0;
+
+ for (nsyms = i = 0; i < HASH_BUCKETS; ++i) {
+ struct obj_symbol *sym;
+ for (sym = f->symtab[i]; sym; sym = sym->next)
+ if (ELF_ST_BIND(sym->info) != STB_LOCAL
+ && sym->secidx <= SHN_HIRESERVE
+ && (sym->secidx >= SHN_LORESERVE
+ || loaded[sym->secidx])) {
+ ElfW(Addr) ofs = nsyms * 2 * tgt_sizeof_void_p;
+
+ obj_symbol_patch(f, sec->idx, ofs, sym);
+ obj_string_patch(f, sec->idx, ofs + tgt_sizeof_void_p,
+ sym->name);
+
+ nsyms++;
+ }
+ }
+
+ obj_extend_section(sec, nsyms * 2 * tgt_sizeof_char_p);
+ }
+
+ return 1;
+}
+
+
+static int
+new_init_module(const char *m_name, struct obj_file *f, unsigned long m_size)
+{
+ struct new_module *module;
+ struct obj_section *sec;
+ void *image;
+ int ret;
+ tgt_long m_addr;
+
+ sec = obj_find_section(f, ".this");
+ if (!sec || !sec->contents) {
+ bb_perror_msg_and_die("corrupt module %s?",m_name);
+ }
+ module = (struct new_module *) sec->contents;
+ m_addr = sec->header.sh_addr;
+
+ module->size_of_struct = sizeof(*module);
+ module->size = m_size;
+ module->flags = flag_autoclean ? NEW_MOD_AUTOCLEAN : 0;
+
+ sec = obj_find_section(f, "__ksymtab");
+ if (sec && sec->header.sh_size) {
+ module->syms = sec->header.sh_addr;
+ module->nsyms = sec->header.sh_size / (2 * tgt_sizeof_char_p);
+ }
+
+ if (n_ext_modules_used) {
+ sec = obj_find_section(f, ".kmodtab");
+ module->deps = sec->header.sh_addr;
+ module->ndeps = n_ext_modules_used;
+ }
+
+ module->init =
+ obj_symbol_final_value(f, obj_find_symbol(f, SPFX "init_module"));
+ module->cleanup =
+ obj_symbol_final_value(f, obj_find_symbol(f, SPFX "cleanup_module"));
+
+ sec = obj_find_section(f, "__ex_table");
+ if (sec) {
+ module->ex_table_start = sec->header.sh_addr;
+ module->ex_table_end = sec->header.sh_addr + sec->header.sh_size;
+ }
+
+ sec = obj_find_section(f, ".text.init");
+ if (sec) {
+ module->runsize = sec->header.sh_addr - m_addr;
+ }
+ sec = obj_find_section(f, ".data.init");
+ if (sec) {
+ if (!module->runsize ||
+ module->runsize > sec->header.sh_addr - m_addr)
+ module->runsize = sec->header.sh_addr - m_addr;
+ }
+ sec = obj_find_section(f, ARCHDATA_SEC_NAME);
+ if (sec && sec->header.sh_size) {
+ module->archdata_start = (void*)sec->header.sh_addr;
+ module->archdata_end = module->archdata_start + sec->header.sh_size;
+ }
+ sec = obj_find_section(f, KALLSYMS_SEC_NAME);
+ if (sec && sec->header.sh_size) {
+ module->kallsyms_start = (void*)sec->header.sh_addr;
+ module->kallsyms_end = module->kallsyms_start + sec->header.sh_size;
+ }
+
+ /* Whew! All of the initialization is complete. Collect the final
+ module image and give it to the kernel. */
+
+ image = xmalloc(m_size);
+ obj_create_image(f, image);
+
+ ret = init_module(m_name, (struct new_module *) image);
+ if (ret)
+ bb_perror_msg("init_module: %s", m_name);
+
+ free(image);
+
+ return ret == 0;
+}
+
+
+/*======================================================================*/
+
+static int
+obj_string_patch(struct obj_file *f, int secidx, ElfW(Addr) offset,
+ const char *string)
+{
+ struct obj_string_patch *p;
+ struct obj_section *strsec;
+ size_t len = strlen(string) + 1;
+ char *loc;
+
+ p = xmalloc(sizeof(*p));
+ p->next = f->string_patches;
+ p->reloc_secidx = secidx;
+ p->reloc_offset = offset;
+ f->string_patches = p;
+
+ strsec = obj_find_section(f, ".kstrtab");
+ if (strsec == NULL) {
+ strsec = obj_create_alloced_section(f, ".kstrtab", 1, len);
+ p->string_offset = 0;
+ loc = strsec->contents;
+ } else {
+ p->string_offset = strsec->header.sh_size;
+ loc = obj_extend_section(strsec, len);
+ }
+ memcpy(loc, string, len);
+
+ return 1;
+}
+
+static int
+obj_symbol_patch(struct obj_file *f, int secidx, ElfW(Addr) offset,
+ struct obj_symbol *sym)
+{
+ struct obj_symbol_patch *p;
+
+ p = xmalloc(sizeof(*p));
+ p->next = f->symbol_patches;
+ p->reloc_secidx = secidx;
+ p->reloc_offset = offset;
+ p->sym = sym;
+ f->symbol_patches = p;
+
+ return 1;
+}
+
+static int obj_check_undefineds(struct obj_file *f)
+{
+ unsigned long i;
+ int ret = 1;
+
+ for (i = 0; i < HASH_BUCKETS; ++i) {
+ struct obj_symbol *sym;
+ for (sym = f->symtab[i]; sym; sym = sym->next)
+ if (sym->secidx == SHN_UNDEF) {
+ if (ELF_ST_BIND(sym->info) == STB_WEAK) {
+ sym->secidx = SHN_ABS;
+ sym->value = 0;
+ } else {
+ if (!flag_quiet) {
+ bb_error_msg("unresolved symbol %s", sym->name);
+ }
+ ret = 0;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static void obj_allocate_commons(struct obj_file *f)
+{
+ struct common_entry {
+ struct common_entry *next;
+ struct obj_symbol *sym;
+ } *common_head = NULL;
+
+ unsigned long i;
+
+ for (i = 0; i < HASH_BUCKETS; ++i) {
+ struct obj_symbol *sym;
+ for (sym = f->symtab[i]; sym; sym = sym->next)
+ if (sym->secidx == SHN_COMMON) {
+ /* Collect all COMMON symbols and sort them by size so as to
+ minimize space wasted by alignment requirements. */
+ {
+ struct common_entry **p, *n;
+ for (p = &common_head; *p; p = &(*p)->next)
+ if (sym->size <= (*p)->sym->size)
+ break;
+
+ n = alloca(sizeof(*n));
+ n->next = *p;
+ n->sym = sym;
+ *p = n;
+ }
+ }
+ }
+
+ for (i = 1; i < f->local_symtab_size; ++i) {
+ struct obj_symbol *sym = f->local_symtab[i];
+ if (sym && sym->secidx == SHN_COMMON) {
+ struct common_entry **p, *n;
+ for (p = &common_head; *p; p = &(*p)->next)
+ if (sym == (*p)->sym)
+ break;
+ else if (sym->size < (*p)->sym->size) {
+ n = alloca(sizeof(*n));
+ n->next = *p;
+ n->sym = sym;
+ *p = n;
+ break;
+ }
+ }
+ }
+
+ if (common_head) {
+ /* Find the bss section. */
+ for (i = 0; i < f->header.e_shnum; ++i)
+ if (f->sections[i]->header.sh_type == SHT_NOBITS)
+ break;
+
+ /* If for some reason there hadn't been one, create one. */
+ if (i == f->header.e_shnum) {
+ struct obj_section *sec;
+
+ f->sections = xrealloc(f->sections, (i + 1) * sizeof(sec));
+ f->sections[i] = sec = arch_new_section();
+ f->header.e_shnum = i + 1;
+
+ memset(sec, 0, sizeof(*sec));
+ sec->header.sh_type = SHT_PROGBITS;
+ sec->header.sh_flags = SHF_WRITE | SHF_ALLOC;
+ sec->name = ".bss";
+ sec->idx = i;
+ }
+
+ /* Allocate the COMMONS. */
+ {
+ ElfW(Addr) bss_size = f->sections[i]->header.sh_size;
+ ElfW(Addr) max_align = f->sections[i]->header.sh_addralign;
+ struct common_entry *c;
+
+ for (c = common_head; c; c = c->next) {
+ ElfW(Addr) align = c->sym->value;
+
+ if (align > max_align)
+ max_align = align;
+ if (bss_size & (align - 1))
+ bss_size = (bss_size | (align - 1)) + 1;
+
+ c->sym->secidx = i;
+ c->sym->value = bss_size;
+
+ bss_size += c->sym->size;
+ }
+
+ f->sections[i]->header.sh_size = bss_size;
+ f->sections[i]->header.sh_addralign = max_align;
+ }
+ }
+
+ /* For the sake of patch relocation and parameter initialization,
+ allocate zeroed data for NOBITS sections now. Note that after
+ this we cannot assume NOBITS are really empty. */
+ for (i = 0; i < f->header.e_shnum; ++i) {
+ struct obj_section *s = f->sections[i];
+ if (s->header.sh_type == SHT_NOBITS) {
+ if (s->header.sh_size != 0)
+ s->contents = memset(xmalloc(s->header.sh_size),
+ 0, s->header.sh_size);
+ else
+ s->contents = NULL;
+
+ s->header.sh_type = SHT_PROGBITS;
+ }
+ }
+}
+
+static unsigned long obj_load_size(struct obj_file *f)
+{
+ unsigned long dot = 0;
+ struct obj_section *sec;
+
+ /* Finalize the positions of the sections relative to one another. */
+
+ for (sec = f->load_order; sec; sec = sec->load_next) {
+ ElfW(Addr) align;
+
+ align = sec->header.sh_addralign;
+ if (align && (dot & (align - 1)))
+ dot = (dot | (align - 1)) + 1;
+
+ sec->header.sh_addr = dot;
+ dot += sec->header.sh_size;
+ }
+
+ return dot;
+}
+
+static int obj_relocate(struct obj_file *f, ElfW(Addr) base)
+{
+ int i, n = f->header.e_shnum;
+ int ret = 1;
+
+ /* Finalize the addresses of the sections. */
+
+ f->baseaddr = base;
+ for (i = 0; i < n; ++i)
+ f->sections[i]->header.sh_addr += base;
+
+ /* And iterate over all of the relocations. */
+
+ for (i = 0; i < n; ++i) {
+ struct obj_section *relsec, *symsec, *targsec, *strsec;
+ ElfW(RelM) * rel, *relend;
+ ElfW(Sym) * symtab;
+ const char *strtab;
+
+ relsec = f->sections[i];
+ if (relsec->header.sh_type != SHT_RELM)
+ continue;
+
+ symsec = f->sections[relsec->header.sh_link];
+ targsec = f->sections[relsec->header.sh_info];
+ strsec = f->sections[symsec->header.sh_link];
+
+ rel = (ElfW(RelM) *) relsec->contents;
+ relend = rel + (relsec->header.sh_size / sizeof(ElfW(RelM)));
+ symtab = (ElfW(Sym) *) symsec->contents;
+ strtab = (const char *) strsec->contents;
+
+ for (; rel < relend; ++rel) {
+ ElfW(Addr) value = 0;
+ struct obj_symbol *intsym = NULL;
+ unsigned long symndx;
+ ElfW(Sym) * extsym = 0;
+ const char *errmsg;
+
+ /* Attempt to find a value to use for this relocation. */
+
+ symndx = ELF_R_SYM(rel->r_info);
+ if (symndx) {
+ /* Note we've already checked for undefined symbols. */
+
+ extsym = &symtab[symndx];
+ if (ELF_ST_BIND(extsym->st_info) == STB_LOCAL) {
+ /* Local symbols we look up in the local table to be sure
+ we get the one that is really intended. */
+ intsym = f->local_symtab[symndx];
+ } else {
+ /* Others we look up in the hash table. */
+ const char *name;
+ if (extsym->st_name)
+ name = strtab + extsym->st_name;
+ else
+ name = f->sections[extsym->st_shndx]->name;
+ intsym = obj_find_symbol(f, name);
+ }
+
+ value = obj_symbol_final_value(f, intsym);
+ intsym->referenced = 1;
+ }
+#if SHT_RELM == SHT_RELA
+#if defined(__alpha__) && defined(AXP_BROKEN_GAS)
+ /* Work around a nasty GAS bug, that is fixed as of 2.7.0.9. */
+ if (!extsym || !extsym->st_name ||
+ ELF_ST_BIND(extsym->st_info) != STB_LOCAL)
+#endif
+ value += rel->r_addend;
+#endif
+
+ /* Do it! */
+ switch (arch_apply_relocation
+ (f, targsec, symsec, intsym, rel, value)
+ ) {
+ case obj_reloc_ok:
+ break;
+
+ case obj_reloc_overflow:
+ errmsg = "Relocation overflow";
+ goto bad_reloc;
+ case obj_reloc_dangerous:
+ errmsg = "Dangerous relocation";
+ goto bad_reloc;
+ case obj_reloc_unhandled:
+ errmsg = "Unhandled relocation";
+bad_reloc:
+ if (extsym) {
+ bb_error_msg("%s of type %ld for %s", errmsg,
+ (long) ELF_R_TYPE(rel->r_info),
+ strtab + extsym->st_name);
+ } else {
+ bb_error_msg("%s of type %ld", errmsg,
+ (long) ELF_R_TYPE(rel->r_info));
+ }
+ ret = 0;
+ break;
+ }
+ }
+ }
+
+ /* Finally, take care of the patches. */
+
+ if (f->string_patches) {
+ struct obj_string_patch *p;
+ struct obj_section *strsec;
+ ElfW(Addr) strsec_base;
+ strsec = obj_find_section(f, ".kstrtab");
+ strsec_base = strsec->header.sh_addr;
+
+ for (p = f->string_patches; p; p = p->next) {
+ struct obj_section *targsec = f->sections[p->reloc_secidx];
+ *(ElfW(Addr) *) (targsec->contents + p->reloc_offset)
+ = strsec_base + p->string_offset;
+ }
+ }
+
+ if (f->symbol_patches) {
+ struct obj_symbol_patch *p;
+
+ for (p = f->symbol_patches; p; p = p->next) {
+ struct obj_section *targsec = f->sections[p->reloc_secidx];
+ *(ElfW(Addr) *) (targsec->contents + p->reloc_offset)
+ = obj_symbol_final_value(f, p->sym);
+ }
+ }
+
+ return ret;
+}
+
+static int obj_create_image(struct obj_file *f, char *image)
+{
+ struct obj_section *sec;
+ ElfW(Addr) base = f->baseaddr;
+
+ for (sec = f->load_order; sec; sec = sec->load_next) {
+ char *secimg;
+
+ if (sec->contents == 0 || sec->header.sh_size == 0)
+ continue;
+
+ secimg = image + (sec->header.sh_addr - base);
+
+ /* Note that we allocated data for NOBITS sections earlier. */
+ memcpy(secimg, sec->contents, sec->header.sh_size);
+ }
+
+ return 1;
+}
+
+/*======================================================================*/
+
+static struct obj_file *obj_load(FILE * fp, int loadprogbits)
+{
+ struct obj_file *f;
+ ElfW(Shdr) * section_headers;
+ int shnum, i;
+ char *shstrtab;
+
+ /* Read the file header. */
+
+ f = arch_new_file();
+ memset(f, 0, sizeof(*f));
+ f->symbol_cmp = strcmp;
+ f->symbol_hash = obj_elf_hash;
+ f->load_order_search_start = &f->load_order;
+
+ fseek(fp, 0, SEEK_SET);
+ if (fread(&f->header, sizeof(f->header), 1, fp) != 1) {
+ bb_perror_msg("error reading ELF header");
+ return NULL;
+ }
+
+ if (f->header.e_ident[EI_MAG0] != ELFMAG0
+ || f->header.e_ident[EI_MAG1] != ELFMAG1
+ || f->header.e_ident[EI_MAG2] != ELFMAG2
+ || f->header.e_ident[EI_MAG3] != ELFMAG3) {
+ bb_error_msg("not an ELF file");
+ return NULL;
+ }
+ if (f->header.e_ident[EI_CLASS] != ELFCLASSM
+ || f->header.e_ident[EI_DATA] != (BB_BIG_ENDIAN
+ ? ELFDATA2MSB : ELFDATA2LSB)
+ || f->header.e_ident[EI_VERSION] != EV_CURRENT
+ || !MATCH_MACHINE(f->header.e_machine)) {
+ bb_error_msg("ELF file not for this architecture");
+ return NULL;
+ }
+ if (f->header.e_type != ET_REL) {
+ bb_error_msg("ELF file not a relocatable object");
+ return NULL;
+ }
+
+ /* Read the section headers. */
+
+ if (f->header.e_shentsize != sizeof(ElfW(Shdr))) {
+ bb_error_msg("section header size mismatch: %lu != %lu",
+ (unsigned long) f->header.e_shentsize,
+ (unsigned long) sizeof(ElfW(Shdr)));
+ return NULL;
+ }
+
+ shnum = f->header.e_shnum;
+ f->sections = xmalloc(sizeof(struct obj_section *) * shnum);
+ memset(f->sections, 0, sizeof(struct obj_section *) * shnum);
+
+ section_headers = alloca(sizeof(ElfW(Shdr)) * shnum);
+ fseek(fp, f->header.e_shoff, SEEK_SET);
+ if (fread(section_headers, sizeof(ElfW(Shdr)), shnum, fp) != shnum) {
+ bb_perror_msg("error reading ELF section headers");
+ return NULL;
+ }
+
+ /* Read the section data. */
+
+ for (i = 0; i < shnum; ++i) {
+ struct obj_section *sec;
+
+ f->sections[i] = sec = arch_new_section();
+ memset(sec, 0, sizeof(*sec));
+
+ sec->header = section_headers[i];
+ sec->idx = i;
+
+ if(sec->header.sh_size) {
+ switch (sec->header.sh_type) {
+ case SHT_NULL:
+ case SHT_NOTE:
+ case SHT_NOBITS:
+ /* ignore */
+ break;
+
+ case SHT_PROGBITS:
+#if LOADBITS
+ if (!loadprogbits) {
+ sec->contents = NULL;
+ break;
+ }
+#endif
+ case SHT_SYMTAB:
+ case SHT_STRTAB:
+ case SHT_RELM:
+ if (sec->header.sh_size > 0) {
+ sec->contents = xmalloc(sec->header.sh_size);
+ fseek(fp, sec->header.sh_offset, SEEK_SET);
+ if (fread(sec->contents, sec->header.sh_size, 1, fp) != 1) {
+ bb_perror_msg("error reading ELF section data");
+ return NULL;
+ }
+ } else {
+ sec->contents = NULL;
+ }
+ break;
+
+#if SHT_RELM == SHT_REL
+ case SHT_RELA:
+ bb_error_msg("RELA relocations not supported on this architecture");
+ return NULL;
+#else
+ case SHT_REL:
+ bb_error_msg("REL relocations not supported on this architecture");
+ return NULL;
+#endif
+
+ default:
+ if (sec->header.sh_type >= SHT_LOPROC) {
+ /* Assume processor specific section types are debug
+ info and can safely be ignored. If this is ever not
+ the case (Hello MIPS?), don't put ifdefs here but
+ create an arch_load_proc_section(). */
+ break;
+ }
+
+ bb_error_msg("can't handle sections of type %ld",
+ (long) sec->header.sh_type);
+ return NULL;
+ }
+ }
+ }
+
+ /* Do what sort of interpretation as needed by each section. */
+
+ shstrtab = f->sections[f->header.e_shstrndx]->contents;
+
+ for (i = 0; i < shnum; ++i) {
+ struct obj_section *sec = f->sections[i];
+ sec->name = shstrtab + sec->header.sh_name;
+ }
+
+ for (i = 0; i < shnum; ++i) {
+ struct obj_section *sec = f->sections[i];
+
+ /* .modinfo should be contents only but gcc has no attribute for that.
+ * The kernel may have marked .modinfo as ALLOC, ignore this bit.
+ */
+ if (strcmp(sec->name, ".modinfo") == 0)
+ sec->header.sh_flags &= ~SHF_ALLOC;
+
+ if (sec->header.sh_flags & SHF_ALLOC)
+ obj_insert_section_load_order(f, sec);
+
+ switch (sec->header.sh_type) {
+ case SHT_SYMTAB:
+ {
+ unsigned long nsym, j;
+ char *strtab;
+ ElfW(Sym) * sym;
+
+ if (sec->header.sh_entsize != sizeof(ElfW(Sym))) {
+ bb_error_msg("symbol size mismatch: %lu != %lu",
+ (unsigned long) sec->header.sh_entsize,
+ (unsigned long) sizeof(ElfW(Sym)));
+ return NULL;
+ }
+
+ nsym = sec->header.sh_size / sizeof(ElfW(Sym));
+ strtab = f->sections[sec->header.sh_link]->contents;
+ sym = (ElfW(Sym) *) sec->contents;
+
+ /* Allocate space for a table of local symbols. */
+ j = f->local_symtab_size = sec->header.sh_info;
+ f->local_symtab = xzalloc(j * sizeof(struct obj_symbol *));
+
+ /* Insert all symbols into the hash table. */
+ for (j = 1, ++sym; j < nsym; ++j, ++sym) {
+ ElfW(Addr) val = sym->st_value;
+ const char *name;
+ if (sym->st_name)
+ name = strtab + sym->st_name;
+ else if (sym->st_shndx < shnum)
+ name = f->sections[sym->st_shndx]->name;
+ else
+ continue;
+#if defined(__SH5__)
+ /*
+ * For sh64 it is possible that the target of a branch
+ * requires a mode switch (32 to 16 and back again).
+ *
+ * This is implied by the lsb being set in the target
+ * address for SHmedia mode and clear for SHcompact.
+ */
+ val |= sym->st_other & 4;
+#endif
+
+ obj_add_symbol(f, name, j, sym->st_info, sym->st_shndx,
+ val, sym->st_size);
+ }
+ }
+ break;
+
+ case SHT_RELM:
+ if (sec->header.sh_entsize != sizeof(ElfW(RelM))) {
+ bb_error_msg("relocation entry size mismatch: %lu != %lu",
+ (unsigned long) sec->header.sh_entsize,
+ (unsigned long) sizeof(ElfW(RelM)));
+ return NULL;
+ }
+ break;
+ /* XXX Relocation code from modutils-2.3.19 is not here.
+ * Why? That's about 20 lines of code from obj/obj_load.c,
+ * which gets done in a second pass through the sections.
+ * This BusyBox insmod does similar work in obj_relocate(). */
+ }
+ }
+
+ return f;
+}
+
+#if ENABLE_FEATURE_INSMOD_LOADINKMEM
+/*
+ * load the unloaded sections directly into the memory allocated by
+ * kernel for the module
+ */
+
+static int obj_load_progbits(FILE * fp, struct obj_file* f, char* imagebase)
+{
+ ElfW(Addr) base = f->baseaddr;
+ struct obj_section* sec;
+
+ for (sec = f->load_order; sec; sec = sec->load_next) {
+
+ /* section already loaded? */
+ if (sec->contents != NULL)
+ continue;
+
+ if (sec->header.sh_size == 0)
+ continue;
+
+ sec->contents = imagebase + (sec->header.sh_addr - base);
+ fseek(fp, sec->header.sh_offset, SEEK_SET);
+ if (fread(sec->contents, sec->header.sh_size, 1, fp) != 1) {
+ bb_perror_msg("error reading ELF section data");
+ return 0;
+ }
+
+ }
+ return 1;
+}
+#endif
+
+static void hide_special_symbols(struct obj_file *f)
+{
+ static const char *const specials[] = {
+ SPFX "cleanup_module",
+ SPFX "init_module",
+ SPFX "kernel_version",
+ NULL
+ };
+
+ struct obj_symbol *sym;
+ const char *const *p;
+
+ for (p = specials; *p; ++p) {
+ sym = obj_find_symbol(f, *p);
+ if (sym != NULL)
+ sym->info = ELF_ST_INFO(STB_LOCAL, ELF_ST_TYPE(sym->info));
+ }
+}
+
+
+#if ENABLE_FEATURE_CHECK_TAINTED_MODULE
+static int obj_gpl_license(struct obj_file *f, const char **license)
+{
+ struct obj_section *sec;
+ /* This list must match *exactly* the list of allowable licenses in
+ * linux/include/linux/module.h. Checking for leading "GPL" will not
+ * work, somebody will use "GPL sucks, this is proprietary".
+ */
+ static const char * const gpl_licenses[] = {
+ "GPL",
+ "GPL v2",
+ "GPL and additional rights",
+ "Dual BSD/GPL",
+ "Dual MPL/GPL",
+ };
+
+ sec = obj_find_section(f, ".modinfo");
+ if (sec) {
+ const char *value, *ptr, *endptr;
+ ptr = sec->contents;
+ endptr = ptr + sec->header.sh_size;
+ while (ptr < endptr) {
+ value = strchr(ptr, '=');
+ if (value && strncmp(ptr, "license", value-ptr) == 0) {
+ int i;
+ if (license)
+ *license = value+1;
+ for (i = 0; i < sizeof(gpl_licenses)/sizeof(gpl_licenses[0]); ++i) {
+ if (strcmp(value+1, gpl_licenses[i]) == 0)
+ return 0;
+ }
+ return 2;
+ }
+ if (strchr(ptr, '\0'))
+ ptr = strchr(ptr, '\0') + 1;
+ else
+ ptr = endptr;
+ }
+ }
+ return 1;
+}
+
+#define TAINT_FILENAME "/proc/sys/kernel/tainted"
+#define TAINT_PROPRIETORY_MODULE (1<<0)
+#define TAINT_FORCED_MODULE (1<<1)
+#define TAINT_UNSAFE_SMP (1<<2)
+#define TAINT_URL "http://www.tux.org/lkml/#export-tainted"
+
+static void set_tainted(struct obj_file *f, int fd, char *m_name,
+ int kernel_has_tainted, int taint, const char *text1, const char *text2)
+{
+ char buf[80];
+ int oldval;
+ static int first = 1;
+ if (fd < 0 && !kernel_has_tainted)
+ return; /* New modutils on old kernel */
+ printf("Warning: loading %s will taint the kernel: %s%s\n",
+ m_name, text1, text2);
+ if (first) {
+ printf(" See %s for information about tainted modules\n", TAINT_URL);
+ first = 0;
+ }
+ if (fd >= 0) {
+ read(fd, buf, sizeof(buf)-1);
+ buf[sizeof(buf)-1] = '\0';
+ oldval = strtoul(buf, NULL, 10);
+ sprintf(buf, "%d\n", oldval | taint);
+ write(fd, buf, strlen(buf));
+ }
+}
+
+/* Check if loading this module will taint the kernel. */
+static void check_tainted_module(struct obj_file *f, char *m_name)
+{
+ static const char tainted_file[] = TAINT_FILENAME;
+ int fd, kernel_has_tainted;
+ const char *ptr;
+
+ kernel_has_tainted = 1;
+ fd = open(tainted_file, O_RDWR);
+ if (fd < 0) {
+ if (errno == ENOENT)
+ kernel_has_tainted = 0;
+ else if (errno == EACCES)
+ kernel_has_tainted = 1;
+ else {
+ perror(tainted_file);
+ kernel_has_tainted = 0;
+ }
+ }
+
+ switch (obj_gpl_license(f, &ptr)) {
+ case 0:
+ break;
+ case 1:
+ set_tainted(f, fd, m_name, kernel_has_tainted, TAINT_PROPRIETORY_MODULE, "no license", "");
+ break;
+ case 2:
+ /* The module has a non-GPL license so we pretend that the
+ * kernel always has a taint flag to get a warning even on
+ * kernels without the proc flag.
+ */
+ set_tainted(f, fd, m_name, 1, TAINT_PROPRIETORY_MODULE, "non-GPL license - ", ptr);
+ break;
+ default:
+ set_tainted(f, fd, m_name, 1, TAINT_PROPRIETORY_MODULE, "Unexpected return from obj_gpl_license", "");
+ break;
+ }
+
+ if (flag_force_load)
+ set_tainted(f, fd, m_name, 1, TAINT_FORCED_MODULE, "forced load", "");
+
+ if (fd >= 0)
+ close(fd);
+}
+#else /* FEATURE_CHECK_TAINTED_MODULE */
+#define check_tainted_module(x, y) do { } while (0);
+#endif /* FEATURE_CHECK_TAINTED_MODULE */
+
+#if ENABLE_FEATURE_INSMOD_KSYMOOPS_SYMBOLS
+/* add module source, timestamp, kernel version and a symbol for the
+ * start of some sections. this info is used by ksymoops to do better
+ * debugging.
+ */
+static int
+get_module_version(struct obj_file *f, char str[STRVERSIONLEN])
+{
+#if ENABLE_FEATURE_INSMOD_VERSION_CHECKING
+ return new_get_module_version(f, str);
+#else /* FEATURE_INSMOD_VERSION_CHECKING */
+ strncpy(str, "???", sizeof(str));
+ return -1;
+#endif /* FEATURE_INSMOD_VERSION_CHECKING */
+}
+
+/* add module source, timestamp, kernel version and a symbol for the
+ * start of some sections. this info is used by ksymoops to do better
+ * debugging.
+ */
+static void
+add_ksymoops_symbols(struct obj_file *f, const char *filename,
+ const char *m_name)
+{
+ static const char symprefix[] = "__insmod_";
+ struct obj_section *sec;
+ struct obj_symbol *sym;
+ char *name, *absolute_filename;
+ char str[STRVERSIONLEN], real[PATH_MAX];
+ int i, l, lm_name, lfilename, use_ksymtab, version;
+ struct stat statbuf;
+
+ static const char *section_names[] = {
+ ".text",
+ ".rodata",
+ ".data",
+ ".bss",
+ ".sbss"
+ };
+
+ if (realpath(filename, real)) {
+ absolute_filename = xstrdup(real);
+ } else {
+ bb_perror_msg("cannot get realpath for %s", filename);
+ absolute_filename = xstrdup(filename);
+ }
+
+ lm_name = strlen(m_name);
+ lfilename = strlen(absolute_filename);
+
+ /* add to ksymtab if it already exists or there is no ksymtab and other symbols
+ * are not to be exported. otherwise leave ksymtab alone for now, the
+ * "export all symbols" compatibility code will export these symbols later.
+ */
+ use_ksymtab = obj_find_section(f, "__ksymtab") || flag_noexport;
+
+ sec = obj_find_section(f, ".this");
+ if (sec) {
+ /* tag the module header with the object name, last modified
+ * timestamp and module version. worst case for module version
+ * is 0xffffff, decimal 16777215. putting all three fields in
+ * one symbol is less readable but saves kernel space.
+ */
+ l = sizeof(symprefix)+ /* "__insmod_" */
+ lm_name+ /* module name */
+ 2+ /* "_O" */
+ lfilename+ /* object filename */
+ 2+ /* "_M" */
+ 2*sizeof(statbuf.st_mtime)+ /* mtime in hex */
+ 2+ /* "_V" */
+ 8+ /* version in dec */
+ 1; /* nul */
+ name = xmalloc(l);
+ if (stat(absolute_filename, &statbuf) != 0)
+ statbuf.st_mtime = 0;
+ version = get_module_version(f, str); /* -1 if not found */
+ snprintf(name, l, "%s%s_O%s_M%0*lX_V%d",
+ symprefix, m_name, absolute_filename,
+ (int)(2*sizeof(statbuf.st_mtime)), statbuf.st_mtime,
+ version);
+ sym = obj_add_symbol(f, name, -1,
+ ELF_ST_INFO(STB_GLOBAL, STT_NOTYPE),
+ sec->idx, sec->header.sh_addr, 0);
+ if (use_ksymtab)
+ new_add_ksymtab(f, sym);
+ }
+ free(absolute_filename);
+#ifdef _NOT_SUPPORTED_
+ /* record where the persistent data is going, same address as previous symbol */
+
+ if (f->persist) {
+ l = sizeof(symprefix)+ /* "__insmod_" */
+ lm_name+ /* module name */
+ 2+ /* "_P" */
+ strlen(f->persist)+ /* data store */
+ 1; /* nul */
+ name = xmalloc(l);
+ snprintf(name, l, "%s%s_P%s",
+ symprefix, m_name, f->persist);
+ sym = obj_add_symbol(f, name, -1, ELF_ST_INFO(STB_GLOBAL, STT_NOTYPE),
+ sec->idx, sec->header.sh_addr, 0);
+ if (use_ksymtab)
+ new_add_ksymtab(f, sym);
+ }
+#endif /* _NOT_SUPPORTED_ */
+ /* tag the desired sections if size is non-zero */
+
+ for (i = 0; i < sizeof(section_names)/sizeof(section_names[0]); ++i) {
+ sec = obj_find_section(f, section_names[i]);
+ if (sec && sec->header.sh_size) {
+ l = sizeof(symprefix)+ /* "__insmod_" */
+ lm_name+ /* module name */
+ 2+ /* "_S" */
+ strlen(sec->name)+ /* section name */
+ 2+ /* "_L" */
+ 8+ /* length in dec */
+ 1; /* nul */
+ name = xmalloc(l);
+ snprintf(name, l, "%s%s_S%s_L%ld",
+ symprefix, m_name, sec->name,
+ (long)sec->header.sh_size);
+ sym = obj_add_symbol(f, name, -1, ELF_ST_INFO(STB_GLOBAL, STT_NOTYPE),
+ sec->idx, sec->header.sh_addr, 0);
+ if (use_ksymtab)
+ new_add_ksymtab(f, sym);
+ }
+ }
+}
+#endif /* FEATURE_INSMOD_KSYMOOPS_SYMBOLS */
+
+#if ENABLE_FEATURE_INSMOD_LOAD_MAP
+static void print_load_map(struct obj_file *f)
+{
+ struct obj_section *sec;
+#if ENABLE_FEATURE_INSMOD_LOAD_MAP_FULL
+ struct obj_symbol **all, **p;
+ int i, nsyms, *loaded;
+ struct obj_symbol *sym;
+#endif
+ /* Report on the section layout. */
+
+ printf("Sections: Size %-*s Align\n",
+ (int) (2 * sizeof(void *)), "Address");
+
+ for (sec = f->load_order; sec; sec = sec->load_next) {
+ int a;
+ unsigned long tmp;
+
+ for (a = -1, tmp = sec->header.sh_addralign; tmp; ++a)
+ tmp >>= 1;
+ if (a == -1)
+ a = 0;
+
+ printf("%-15s %08lx %0*lx 2**%d\n",
+ sec->name,
+ (long)sec->header.sh_size,
+ (int) (2 * sizeof(void *)),
+ (long)sec->header.sh_addr,
+ a);
+ }
+#if ENABLE_FEATURE_INSMOD_LOAD_MAP_FULL
+ /* Quick reference which section indicies are loaded. */
+
+ loaded = alloca(sizeof(int) * (i = f->header.e_shnum));
+ while (--i >= 0)
+ loaded[i] = (f->sections[i]->header.sh_flags & SHF_ALLOC) != 0;
+
+ /* Collect the symbols we'll be listing. */
+
+ for (nsyms = i = 0; i < HASH_BUCKETS; ++i)
+ for (sym = f->symtab[i]; sym; sym = sym->next)
+ if (sym->secidx <= SHN_HIRESERVE
+ && (sym->secidx >= SHN_LORESERVE || loaded[sym->secidx]))
+ ++nsyms;
+
+ all = alloca(nsyms * sizeof(struct obj_symbol *));
+
+ for (i = 0, p = all; i < HASH_BUCKETS; ++i)
+ for (sym = f->symtab[i]; sym; sym = sym->next)
+ if (sym->secidx <= SHN_HIRESERVE
+ && (sym->secidx >= SHN_LORESERVE || loaded[sym->secidx]))
+ *p++ = sym;
+
+ /* And list them. */
+ printf("\nSymbols:\n");
+ for (p = all; p < all + nsyms; ++p) {
+ char type = '?';
+ unsigned long value;
+
+ sym = *p;
+ if (sym->secidx == SHN_ABS) {
+ type = 'A';
+ value = sym->value;
+ } else if (sym->secidx == SHN_UNDEF) {
+ type = 'U';
+ value = 0;
+ } else {
+ sec = f->sections[sym->secidx];
+
+ if (sec->header.sh_type == SHT_NOBITS)
+ type = 'B';
+ else if (sec->header.sh_flags & SHF_ALLOC) {
+ if (sec->header.sh_flags & SHF_EXECINSTR)
+ type = 'T';
+ else if (sec->header.sh_flags & SHF_WRITE)
+ type = 'D';
+ else
+ type = 'R';
+ }
+ value = sym->value + sec->header.sh_addr;
+ }
+
+ if (ELF_ST_BIND(sym->info) == STB_LOCAL)
+ type = tolower(type);
+
+ printf("%0*lx %c %s\n", (int) (2 * sizeof(void *)), value,
+ type, sym->name);
+ }
+#endif
+}
+#else /* !FEATURE_INSMOD_LOAD_MAP */
+void print_load_map(struct obj_file *f);
+#endif
+
+int insmod_main( int argc, char **argv);
+int insmod_main( int argc, char **argv)
+{
+ char *opt_o, *arg1;
+ int len;
+ int k_crcs;
+ char *tmp, *tmp1;
+ unsigned long m_size;
+ ElfW(Addr) m_addr;
+ struct obj_file *f;
+ struct stat st;
+ char *m_name = 0;
+ int exit_status = EXIT_FAILURE;
+ int m_has_modinfo;
+#if ENABLE_FEATURE_INSMOD_VERSION_CHECKING
+ struct utsname uts_info;
+ char m_strversion[STRVERSIONLEN];
+ int m_version, m_crcs;
+#endif
+#if ENABLE_FEATURE_CLEAN_UP
+ FILE *fp = 0;
+#else
+ FILE *fp;
+#endif
+ int k_version = 0;
+ struct utsname myuname;
+
+ /* Parse any options */
+ getopt32(argc, argv, OPTION_STR, &opt_o);
+ arg1 = argv[optind];
+ if (option_mask32 & OPT_o) { // -o /* name the output module */
+ free(m_name);
+ m_name = xstrdup(opt_o);
+ }
+
+ if (arg1 == NULL) {
+ bb_show_usage();
+ }
+
+ /* Grab the module name */
+ tmp1 = xstrdup(arg1);
+ tmp = basename(tmp1);
+ len = strlen(tmp);
+
+ if (uname(&myuname) == 0) {
+ if (myuname.release[0] == '2') {
+ k_version = myuname.release[2] - '0';
+ }
+ }
+
+#if ENABLE_FEATURE_2_6_MODULES
+ if (k_version > 4 && len > 3 && tmp[len - 3] == '.'
+ && tmp[len - 2] == 'k' && tmp[len - 1] == 'o'
+ ) {
+ len -= 3;
+ tmp[len] = '\0';
+ } else
+#endif
+ if (len > 2 && tmp[len - 2] == '.' && tmp[len - 1] == 'o') {
+ len -= 2;
+ tmp[len] = '\0';
+ }
+
+
+#if ENABLE_FEATURE_2_6_MODULES
+ if (k_version > 4)
+ m_fullName = xasprintf("%s.ko", tmp);
+ else
+#endif
+ m_fullName = xasprintf("%s.o", tmp);
+
+ if (!m_name) {
+ m_name = tmp;
+ } else {
+ free(tmp1);
+ tmp1 = 0; /* flag for free(m_name) before exit() */
+ }
+
+ /* Get a filedesc for the module. Check we we have a complete path */
+ if (stat(arg1, &st) < 0 || !S_ISREG(st.st_mode)
+ || (fp = fopen(arg1, "r")) == NULL
+ ) {
+ /* Hmm. Could not open it. First search under /lib/modules/`uname -r`,
+ * but do not error out yet if we fail to find it... */
+ if (k_version) { /* uname succeedd */
+ char *module_dir;
+ char *tmdn;
+ char real_module_dir[FILENAME_MAX];
+
+ tmdn = concat_path_file(_PATH_MODULES, myuname.release);
+ /* Jump through hoops in case /lib/modules/`uname -r`
+ * is a symlink. We do not want recursive_action to
+ * follow symlinks, but we do want to follow the
+ * /lib/modules/`uname -r` dir, So resolve it ourselves
+ * if it is a link... */
+ if (realpath(tmdn, real_module_dir) == NULL)
+ module_dir = tmdn;
+ else
+ module_dir = real_module_dir;
+ recursive_action(module_dir, TRUE, FALSE, FALSE,
+ check_module_name_match, 0, m_fullName, 0);
+ free(tmdn);
+ }
+
+ /* Check if we have found anything yet */
+ if (m_filename == 0 || ((fp = fopen(m_filename, "r")) == NULL)) {
+ char module_dir[FILENAME_MAX];
+
+ free(m_filename);
+ m_filename = 0;
+ if (realpath (_PATH_MODULES, module_dir) == NULL)
+ strcpy(module_dir, _PATH_MODULES);
+ /* No module found under /lib/modules/`uname -r`, this
+ * time cast the net a bit wider. Search /lib/modules/ */
+ if (!recursive_action(module_dir, TRUE, FALSE, FALSE,
+ check_module_name_match, 0, m_fullName, 0)
+ ) {
+ if (m_filename == 0
+ || ((fp = fopen(m_filename, "r")) == NULL)
+ ) {
+ bb_error_msg("%s: no module by that name found", m_fullName);
+ goto out;
+ }
+ } else
+ bb_error_msg_and_die("%s: no module by that name found", m_fullName);
+ }
+ } else
+ m_filename = xstrdup(arg1);
+
+ if (flag_verbose)
+ printf("Using %s\n", m_filename);
+
+#if ENABLE_FEATURE_2_6_MODULES
+ if (k_version > 4) {
+ argv[optind] = m_filename;
+ optind--;
+ return insmod_ng_main(argc - optind, argv + optind);
+ }
+#endif
+
+ f = obj_load(fp, LOADBITS);
+ if (f == NULL)
+ bb_perror_msg_and_die("cannot load the module");
+
+ if (get_modinfo_value(f, "kernel_version") == NULL)
+ m_has_modinfo = 0;
+ else
+ m_has_modinfo = 1;
+
+#if ENABLE_FEATURE_INSMOD_VERSION_CHECKING
+ /* Version correspondence? */
+ if (!flag_quiet) {
+ if (uname(&uts_info) < 0)
+ uts_info.release[0] = '\0';
+ if (m_has_modinfo) {
+ m_version = new_get_module_version(f, m_strversion);
+ if (m_version == -1) {
+ bb_error_msg("cannot find the kernel version the module was "
+ "compiled for");
+ goto out;
+ }
+ }
+
+ if (strncmp(uts_info.release, m_strversion, STRVERSIONLEN) != 0) {
+ if (flag_force_load) {
+ bb_error_msg("warning: kernel-module version mismatch\n"
+ "\t%s was compiled for kernel version %s\n"
+ "\twhile this kernel is version %s",
+ m_filename, m_strversion, uts_info.release);
+ } else {
+ bb_error_msg("kernel-module version mismatch\n"
+ "\t%s was compiled for kernel version %s\n"
+ "\twhile this kernel is version %s.",
+ m_filename, m_strversion, uts_info.release);
+ goto out;
+ }
+ }
+ }
+ k_crcs = 0;
+#endif /* FEATURE_INSMOD_VERSION_CHECKING */
+
+ if (!query_module(NULL, 0, NULL, 0, NULL)) {
+ if (!new_get_kernel_symbols())
+ goto out;
+ k_crcs = new_is_kernel_checksummed();
+ } else {
+ bb_error_msg("not configured to support old kernels");
+ goto out;
+ }
+
+#if ENABLE_FEATURE_INSMOD_VERSION_CHECKING
+ m_crcs = 0;
+ if (m_has_modinfo)
+ m_crcs = new_is_module_checksummed(f);
+
+ if (m_crcs != k_crcs)
+ obj_set_symbol_compare(f, ncv_strcmp, ncv_symbol_hash);
+#endif /* FEATURE_INSMOD_VERSION_CHECKING */
+
+ /* Let the module know about the kernel symbols. */
+ add_kernel_symbols(f);
+
+ /* Allocate common symbols, symbol tables, and string tables. */
+
+ if (!new_create_this_module(f, m_name)) {
+ goto out;
+ }
+
+ if (!obj_check_undefineds(f)) {
+ goto out;
+ }
+ obj_allocate_commons(f);
+ check_tainted_module(f, m_name);
+
+ /* done with the module name, on to the optional var=value arguments */
+ ++optind;
+ if (optind < argc) {
+ if (!new_process_module_arguments(f, argc - optind, argv + optind)) {
+ goto out;
+ }
+ }
+
+ arch_create_got(f);
+ hide_special_symbols(f);
+
+#if ENABLE_FEATURE_INSMOD_KSYMOOPS_SYMBOLS
+ add_ksymoops_symbols(f, m_filename, m_name);
+#endif /* FEATURE_INSMOD_KSYMOOPS_SYMBOLS */
+
+ new_create_module_ksymtab(f);
+
+ /* Find current size of the module */
+ m_size = obj_load_size(f);
+
+
+ m_addr = create_module(m_name, m_size);
+ if (m_addr == -1) switch (errno) {
+ case EEXIST:
+ bb_error_msg("a module named %s already exists", m_name);
+ goto out;
+ case ENOMEM:
+ bb_error_msg("can't allocate kernel memory for module; needed %lu bytes",
+ m_size);
+ goto out;
+ default:
+ bb_perror_msg("create_module: %s", m_name);
+ goto out;
+ }
+
+#if !LOADBITS
+ /*
+ * the PROGBITS section was not loaded by the obj_load
+ * now we can load them directly into the kernel memory
+ */
+ if (!obj_load_progbits(fp, f, (char*)m_addr)) {
+ delete_module(m_name);
+ goto out;
+ }
+#endif
+
+ if (!obj_relocate(f, m_addr)) {
+ delete_module(m_name);
+ goto out;
+ }
+
+ if (!new_init_module(m_name, f, m_size)) {
+ delete_module(m_name);
+ goto out;
+ }
+
+ if(flag_print_load_map)
+ print_load_map(f);
+
+ exit_status = EXIT_SUCCESS;
+
+out:
+#if ENABLE_FEATURE_CLEAN_UP
+ if(fp)
+ fclose(fp);
+ free(tmp1);
+ if(!tmp1)
+ free(m_name);
+ free(m_filename);
+#endif
+ return exit_status;
+}
+
+
+#endif
+
+
+#if ENABLE_FEATURE_2_6_MODULES
+
+#include <sys/mman.h>
+#include <asm/unistd.h>
+#include <sys/syscall.h>
+
+/* We use error numbers in a loose translation... */
+static const char *moderror(int err)
+{
+ switch (err) {
+ case ENOEXEC:
+ return "Invalid module format";
+ case ENOENT:
+ return "Unknown symbol in module";
+ case ESRCH:
+ return "Module has wrong symbol version";
+ case EINVAL:
+ return "Invalid parameters";
+ default:
+ return strerror(err);
+ }
+}
+
+int insmod_ng_main(int argc, char **argv);
+int insmod_ng_main(int argc, char **argv)
+{
+ long ret;
+ size_t len;
+ void *map;
+ char *filename, *options;
+
+ filename = *++argv;
+ if (!filename)
+ bb_show_usage();
+
+ /* Rest is options */
+ options = xstrdup("");
+ while (*++argv) {
+ int optlen = strlen(options);
+ options = xrealloc(options, optlen + 2 + strlen(*argv) + 2);
+ /* Spaces handled by "" pairs, but no way of escaping quotes */
+ sprintf(options + optlen, (strchr(*argv,' ') ? "\"%s\" " : "%s "), *argv);
+ }
+
+#if 0
+ /* Any special reason why mmap? It isn't performace critical... */
+ int fd;
+ struct stat st;
+ unsigned long len;
+ fd = xopen(filename, O_RDONLY);
+ fstat(fd, &st);
+ len = st.st_size;
+ map = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (map == MAP_FAILED) {
+ bb_perror_msg_and_die("cannot mmap '%s'", filename);
+ }
+
+ /* map == NULL on Blackfin, probably on other MMU-less systems too. Workaround. */
+ if (map == NULL) {
+ map = xmalloc(len);
+ xread(fd, map, len);
+ }
+#else
+ len = MAXINT(ssize_t);
+ map = xmalloc_open_read_close(filename, &len);
+#endif
+
+ ret = syscall(__NR_init_module, map, len, options);
+ if (ret != 0) {
+ bb_perror_msg_and_die("cannot insert '%s': %s (%li)",
+ filename, moderror(errno), ret);
+ }
+
+ return 0;
+}
+
+#endif
diff --git a/i/pc104/initrd/conf/busybox/modutils/lsmod.c b/i/pc104/initrd/conf/busybox/modutils/lsmod.c
new file mode 100644
index 0000000..31549b9
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/modutils/lsmod.c
@@ -0,0 +1,192 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini lsmod implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Modified by Alcove, Julien Gaulmin <julien.gaulmin@alcove.fr> and
+ * Nicolas Ferre <nicolas.ferre@alcove.fr> to support pre 2.1 kernels
+ * (which lack the query_module() interface).
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "busybox.h"
+
+
+#ifndef CONFIG_FEATURE_CHECK_TAINTED_MODULE
+static void check_tainted(void) { puts(""); }
+#else
+#define TAINT_FILENAME "/proc/sys/kernel/tainted"
+#define TAINT_PROPRIETORY_MODULE (1<<0)
+#define TAINT_FORCED_MODULE (1<<1)
+#define TAINT_UNSAFE_SMP (1<<2)
+
+static void check_tainted(void)
+{
+ int tainted;
+ FILE *f;
+
+ tainted = 0;
+ if ((f = fopen(TAINT_FILENAME, "r"))) {
+ fscanf(f, "%d", &tainted);
+ fclose(f);
+ }
+ if (f && tainted) {
+ printf(" Tainted: %c%c%c\n",
+ tainted & TAINT_PROPRIETORY_MODULE ? 'P' : 'G',
+ tainted & TAINT_FORCED_MODULE ? 'F' : ' ',
+ tainted & TAINT_UNSAFE_SMP ? 'S' : ' ');
+ }
+ else {
+ printf(" Not tainted\n");
+ }
+}
+#endif
+
+#ifdef CONFIG_FEATURE_QUERY_MODULE_INTERFACE
+
+struct module_info
+{
+ unsigned long addr;
+ unsigned long size;
+ unsigned long flags;
+ long usecount;
+};
+
+
+int query_module(const char *name, int which, void *buf, size_t bufsize, size_t *ret);
+
+enum {
+/* Values for query_module's which. */
+ QM_MODULES = 1,
+ QM_DEPS = 2,
+ QM_REFS = 3,
+ QM_SYMBOLS = 4,
+ QM_INFO = 5,
+
+/* Bits of module.flags. */
+ NEW_MOD_RUNNING = 1,
+ NEW_MOD_DELETED = 2,
+ NEW_MOD_AUTOCLEAN = 4,
+ NEW_MOD_VISITED = 8,
+ NEW_MOD_USED_ONCE = 16,
+ NEW_MOD_INITIALIZING = 64
+};
+
+int lsmod_main(int argc, char **argv);
+int lsmod_main(int argc, char **argv)
+{
+ struct module_info info;
+ char *module_names, *mn, *deps, *dn;
+ size_t bufsize, depsize, nmod, count, i, j;
+
+ module_names = deps = NULL;
+ bufsize = depsize = 0;
+ while (query_module(NULL, QM_MODULES, module_names, bufsize, &nmod)) {
+ if (errno != ENOSPC) bb_perror_msg_and_die("QM_MODULES");
+ module_names = xmalloc(bufsize = nmod);
+ }
+
+ deps = xmalloc(depsize = 256);
+ printf("Module\t\t\tSize Used by");
+ check_tainted();
+
+ for (i = 0, mn = module_names; i < nmod; mn += strlen(mn) + 1, i++) {
+ if (query_module(mn, QM_INFO, &info, sizeof(info), &count)) {
+ if (errno == ENOENT) {
+ /* The module was removed out from underneath us. */
+ continue;
+ }
+ /* else choke */
+ bb_perror_msg_and_die("module %s: QM_INFO", mn);
+ }
+ while (query_module(mn, QM_REFS, deps, depsize, &count)) {
+ if (errno == ENOENT) {
+ /* The module was removed out from underneath us. */
+ continue;
+ } else if (errno != ENOSPC)
+ bb_perror_msg_and_die("module %s: QM_REFS", mn);
+ deps = xrealloc(deps, count);
+ }
+ printf("%-20s%8lu%4ld", mn, info.size, info.usecount);
+ if (info.flags & NEW_MOD_DELETED)
+ printf(" (deleted)");
+ else if (info.flags & NEW_MOD_INITIALIZING)
+ printf(" (initializing)");
+ else if (!(info.flags & NEW_MOD_RUNNING))
+ printf(" (uninitialized)");
+ else {
+ if (info.flags & NEW_MOD_AUTOCLEAN)
+ printf(" (autoclean) ");
+ if (!(info.flags & NEW_MOD_USED_ONCE))
+ printf(" (unused)");
+ }
+ if (count) printf(" [");
+ for (j = 0, dn = deps; j < count; dn += strlen(dn) + 1, j++) {
+ printf("%s%s", dn, (j==count-1)? "":" ");
+ }
+ if (count) printf("]");
+
+ puts("");
+ }
+
+#ifdef CONFIG_FEATURE_CLEAN_UP
+ free(module_names);
+#endif
+
+ return 0;
+}
+
+#else /* CONFIG_FEATURE_QUERY_MODULE_INTERFACE */
+
+int lsmod_main(int argc, char **argv);
+int lsmod_main(int argc, char **argv)
+{
+ FILE *file = xfopen("/proc/modules", "r");
+
+ printf("Module Size Used by");
+ check_tainted();
+#if defined(CONFIG_FEATURE_LSMOD_PRETTY_2_6_OUTPUT)
+ {
+ char *line;
+ while ((line = xmalloc_fgets(file)) != NULL) {
+ char *tok;
+
+ tok = strtok(line, " \t");
+ printf("%-19s", tok);
+ tok = strtok(NULL, " \t\n");
+ printf(" %8s", tok);
+ tok = strtok(NULL, " \t\n");
+ /* Null if no module unloading support. */
+ if (tok) {
+ printf(" %s", tok);
+ tok = strtok(NULL, "\n");
+ if (!tok)
+ tok = (char*)"";
+ /* New-style has commas, or -. If so,
+ truncate (other fields might follow). */
+ else if (strchr(tok, ',')) {
+ tok = strtok(tok, "\t ");
+ /* Strip trailing comma. */
+ if (tok[strlen(tok)-1] == ',')
+ tok[strlen(tok)-1] = '\0';
+ } else if (tok[0] == '-'
+ && (tok[1] == '\0' || isspace(tok[1]))
+ ) {
+ tok = (char*)"";
+ }
+ printf(" %s", tok);
+ }
+ puts("");
+ free(line);
+ }
+ fclose(file);
+ }
+#else
+ xprint_and_close_file(file);
+#endif /* CONFIG_FEATURE_2_6_MODULES */
+ return EXIT_SUCCESS;
+}
+
+#endif /* CONFIG_FEATURE_QUERY_MODULE_INTERFACE */
diff --git a/i/pc104/initrd/conf/busybox/modutils/modprobe.c b/i/pc104/initrd/conf/busybox/modutils/modprobe.c
new file mode 100644
index 0000000..57e19b4
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/modutils/modprobe.c
@@ -0,0 +1,896 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Modprobe written from scratch for BusyBox
+ *
+ * Copyright (c) 2002 by Robert Griebl, griebl@gmx.de
+ * Copyright (c) 2003 by Andrew Dennison, andrew.dennison@motec.com.au
+ * Copyright (c) 2005 by Jim Bauer, jfbauer@nfr.com
+ *
+ * Portions Copyright (c) 2005 by Yann E. MORIN, yann.morin.1998@anciens.enib.fr
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+*/
+
+#include "busybox.h"
+#include <sys/utsname.h>
+#include <fnmatch.h>
+
+struct mod_opt_t { /* one-way list of options to pass to a module */
+ char * m_opt_val;
+ struct mod_opt_t * m_next;
+};
+
+struct dep_t { /* one-way list of dependency rules */
+ /* a dependency rule */
+ char * m_name; /* the module name*/
+ char * m_path; /* the module file path */
+ struct mod_opt_t * m_options; /* the module options */
+
+ int m_isalias : 1; /* the module is an alias */
+ int m_reserved : 15; /* stuffin' */
+
+ int m_depcnt : 16; /* the number of dependable module(s) */
+ char ** m_deparr; /* the list of dependable module(s) */
+
+ struct dep_t * m_next; /* the next dependency rule */
+};
+
+struct mod_list_t { /* two-way list of modules to process */
+ /* a module description */
+ const char * m_name;
+ char * m_path;
+ struct mod_opt_t * m_options;
+
+ struct mod_list_t * m_prev;
+ struct mod_list_t * m_next;
+};
+
+
+static struct dep_t *depend;
+
+#define main_options "acdklnqrst:vVC:"
+#define INSERT_ALL 1 /* a */
+#define DUMP_CONF_EXIT 2 /* c */
+#define D_OPT_IGNORED 4 /* d */
+#define AUTOCLEAN_FLG 8 /* k */
+#define LIST_ALL 16 /* l */
+#define SHOW_ONLY 32 /* n */
+#define QUIET 64 /* q */
+#define REMOVE_OPT 128 /* r */
+#define DO_SYSLOG 256 /* s */
+#define RESTRICT_DIR 512 /* t */
+#define VERBOSE 1024 /* v */
+#define VERSION_ONLY 2048 /* V */
+#define CONFIG_FILE 4096 /* C */
+
+#define autoclean (main_opts & AUTOCLEAN_FLG)
+#define show_only (main_opts & SHOW_ONLY)
+#define quiet (main_opts & QUIET)
+#define remove_opt (main_opts & REMOVE_OPT)
+#define do_syslog (main_opts & DO_SYSLOG)
+#define verbose (main_opts & VERBOSE)
+
+static int main_opts;
+
+static int parse_tag_value(char *buffer, char **ptag, char **pvalue)
+{
+ char *tag, *value;
+
+ buffer = skip_whitespace(buffer);
+ tag = value = buffer;
+ while (!isspace(*value))
+ if (!*value) return 0;
+ else value++;
+ *value++ = 0;
+ value = skip_whitespace(value);
+ if (!*value) return 0;
+
+ *ptag = tag;
+ *pvalue = value;
+
+ return 1;
+}
+
+/*
+ * This function appends an option to a list
+ */
+static struct mod_opt_t *append_option(struct mod_opt_t *opt_list, char *opt)
+{
+ struct mod_opt_t *ol = opt_list;
+
+ if (ol) {
+ while (ol->m_next) {
+ ol = ol->m_next;
+ }
+ ol->m_next = xmalloc(sizeof(struct mod_opt_t));
+ ol = ol->m_next;
+ } else {
+ ol = opt_list = xmalloc(sizeof(struct mod_opt_t));
+ }
+
+ ol->m_opt_val = xstrdup(opt);
+ ol->m_next = NULL;
+
+ return opt_list;
+}
+
+#if ENABLE_FEATURE_MODPROBE_MULTIPLE_OPTIONS
+/* static char* parse_command_string(char* src, char **dst);
+ * src: pointer to string containing argument
+ * dst: pointer to where to store the parsed argument
+ * return value: the pointer to the first char after the parsed argument,
+ * NULL if there was no argument parsed (only trailing spaces).
+ * Note that memory is allocated with xstrdup when a new argument was
+ * parsed. Don't forget to free it!
+ */
+#define ARG_EMPTY 0x00
+#define ARG_IN_DQUOTES 0x01
+#define ARG_IN_SQUOTES 0x02
+static char *parse_command_string(char *src, char **dst)
+{
+ int opt_status = ARG_EMPTY;
+ char* tmp_str;
+
+ /* Dumb you, I have nothing to do... */
+ if (src == NULL) return src;
+
+ /* Skip leading spaces */
+ while (*src == ' ') {
+ src++;
+ }
+ /* Is the end of string reached? */
+ if (*src == '\0') {
+ return NULL;
+ }
+ /* Reached the start of an argument
+ * By the way, we duplicate a little too much
+ * here but what is too much is freed later. */
+ *dst = tmp_str = xstrdup(src);
+ /* Get to the end of that argument */
+ while (*tmp_str != '\0'
+ && (*tmp_str != ' ' || (opt_status & (ARG_IN_DQUOTES | ARG_IN_SQUOTES)))
+ ) {
+ switch (*tmp_str) {
+ case '\'':
+ if (opt_status & ARG_IN_DQUOTES) {
+ /* Already in double quotes, keep current char as is */
+ } else {
+ /* shift left 1 char, until end of string: get rid of the opening/closing quotes */
+ memmove(tmp_str, tmp_str + 1, strlen(tmp_str));
+ /* mark me: we enter or leave single quotes */
+ opt_status ^= ARG_IN_SQUOTES;
+ /* Back one char, as we need to re-scan the new char there. */
+ tmp_str--;
+ }
+ break;
+ case '"':
+ if (opt_status & ARG_IN_SQUOTES) {
+ /* Already in single quotes, keep current char as is */
+ } else {
+ /* shift left 1 char, until end of string: get rid of the opening/closing quotes */
+ memmove(tmp_str, tmp_str + 1, strlen(tmp_str));
+ /* mark me: we enter or leave double quotes */
+ opt_status ^= ARG_IN_DQUOTES;
+ /* Back one char, as we need to re-scan the new char there. */
+ tmp_str--;
+ }
+ break;
+ case '\\':
+ if (opt_status & ARG_IN_SQUOTES) {
+ /* Between single quotes: keep as is. */
+ } else {
+ switch (*(tmp_str+1)) {
+ case 'a':
+ case 'b':
+ case 't':
+ case 'n':
+ case 'v':
+ case 'f':
+ case 'r':
+ case '0':
+ /* We escaped a special character. For now, keep
+ * both the back-slash and the following char. */
+ tmp_str++; src++;
+ break;
+ default:
+ /* We escaped a space or a single or double quote,
+ * or a back-slash, or a non-escapable char. Remove
+ * the '\' and keep the new current char as is. */
+ memmove(tmp_str, tmp_str + 1, strlen(tmp_str));
+ break;
+ }
+ }
+ break;
+ /* Any other char that is special shall appear here.
+ * Example: $ starts a variable
+ case '$':
+ do_variable_expansion();
+ break;
+ * */
+ default:
+ /* any other char is kept as is. */
+ break;
+ }
+ tmp_str++; /* Go to next char */
+ src++; /* Go to next char to find the end of the argument. */
+ }
+ /* End of string, but still no ending quote */
+ if (opt_status & (ARG_IN_DQUOTES | ARG_IN_SQUOTES)) {
+ bb_error_msg_and_die("unterminated (single or double) quote in options list: %s", src);
+ }
+ *tmp_str++ = '\0';
+ *dst = xrealloc(*dst, (tmp_str - *dst));
+ return src;
+}
+#else
+#define parse_command_string(src, dst) (0)
+#endif /* ENABLE_FEATURE_MODPROBE_MULTIPLE_OPTIONS */
+
+/*
+ * This function reads aliases and default module options from a configuration file
+ * (/etc/modprobe.conf syntax). It supports includes (only files, no directories).
+ */
+static void include_conf(struct dep_t **first, struct dep_t **current, char *buffer, int buflen, int fd)
+{
+ int continuation_line = 0;
+
+ // alias parsing is not 100% correct (no correct handling of continuation lines within an alias) !
+
+ while (reads(fd, buffer, buflen)) {
+ int l;
+ char *p;
+
+ p = strchr(buffer, '#');
+ if (p)
+ *p = 0;
+
+ l = strlen(buffer);
+
+ while (l && isspace(buffer[l-1])) {
+ buffer[l-1] = 0;
+ l--;
+ }
+
+ if (l == 0) {
+ continuation_line = 0;
+ continue;
+ }
+
+ if (!continuation_line) {
+ if ((strncmp(buffer, "alias", 5) == 0) && isspace(buffer[5])) {
+ char *alias, *mod;
+
+ if (parse_tag_value(buffer + 6, &alias, &mod)) {
+ /* handle alias as a module dependent on the aliased module */
+ if (!*current) {
+ (*first) = (*current) = xzalloc(sizeof(struct dep_t));
+ } else {
+ (*current)->m_next = xzalloc(sizeof(struct dep_t));
+ (*current) = (*current)->m_next;
+ }
+ (*current)->m_name = xstrdup(alias);
+ (*current)->m_isalias = 1;
+
+ if ((strcmp(mod, "off") == 0) || (strcmp(mod, "null") == 0)) {
+ (*current)->m_depcnt = 0;
+ (*current)->m_deparr = 0;
+ } else {
+ (*current)->m_depcnt = 1;
+ (*current)->m_deparr = xmalloc(1 * sizeof(char *));
+ (*current)->m_deparr[0] = xstrdup(mod);
+ }
+ (*current)->m_next = 0;
+ }
+ } else if ((strncmp(buffer, "options", 7) == 0) && isspace(buffer[7])) {
+ char *mod, *opt;
+
+ /* split the line in the module/alias name, and options */
+ if (parse_tag_value(buffer + 8, &mod, &opt)) {
+ struct dep_t *dt;
+
+ /* find the corresponding module */
+ for (dt = *first; dt; dt = dt->m_next) {
+ if (strcmp(dt->m_name, mod) == 0)
+ break;
+ }
+ if (dt) {
+ if (ENABLE_FEATURE_MODPROBE_MULTIPLE_OPTIONS) {
+ char* new_opt = NULL;
+ while ((opt = parse_command_string(opt, &new_opt))) {
+ dt->m_options = append_option(dt->m_options, new_opt);
+ }
+ } else {
+ dt->m_options = append_option(dt->m_options, opt);
+ }
+ }
+ }
+ } else if ((strncmp(buffer, "include", 7) == 0) && isspace(buffer[7])) {
+ int fdi; char *filename;
+
+ filename = skip_whitespace(buffer + 8);
+
+ if ((fdi = open(filename, O_RDONLY)) >= 0) {
+ include_conf(first, current, buffer, buflen, fdi);
+ close(fdi);
+ }
+ }
+ }
+ }
+}
+
+/*
+ * This function builds a list of dependency rules from /lib/modules/`uname -r`/modules.dep.
+ * It then fills every modules and aliases with their default options, found by parsing
+ * modprobe.conf (or modules.conf, or conf.modules).
+ */
+static struct dep_t *build_dep(void)
+{
+ int fd;
+ struct utsname un;
+ struct dep_t *first = 0;
+ struct dep_t *current = 0;
+ char buffer[2048];
+ char *filename;
+ int continuation_line = 0;
+ int k_version;
+
+ if (uname(&un))
+ bb_error_msg_and_die("can't determine kernel version");
+
+ k_version = 0;
+ if (un.release[0] == '2') {
+ k_version = un.release[2] - '0';
+ }
+
+ filename = xasprintf("/lib/modules/%s/modules.dep", un.release);
+ fd = open(filename, O_RDONLY);
+ if (ENABLE_FEATURE_CLEAN_UP)
+ free(filename);
+ if (fd < 0) {
+ /* Ok, that didn't work. Fall back to looking in /lib/modules */
+ fd = open("/lib/modules/modules.dep", O_RDONLY);
+ if (fd < 0) {
+ return 0;
+ }
+ }
+
+ while (reads(fd, buffer, sizeof(buffer))) {
+ int l = strlen(buffer);
+ char *p = 0;
+
+ while (l > 0 && isspace(buffer[l-1])) {
+ buffer[l-1] = 0;
+ l--;
+ }
+
+ if (l == 0) {
+ continuation_line = 0;
+ continue;
+ }
+
+ /* Is this a new module dep description? */
+ if (!continuation_line) {
+ /* find the dep beginning */
+ char *col = strchr(buffer, ':');
+ char *dot = col;
+
+ if (col) {
+ /* This line is a dep description */
+ char *mods;
+ char *modpath;
+ char *mod;
+
+ /* Find the beginning of the module file name */
+ *col = 0;
+ mods = strrchr(buffer, '/');
+
+ if (!mods)
+ mods = buffer; /* no path for this module */
+ else
+ mods++; /* there was a path for this module... */
+
+ /* find the path of the module */
+ modpath = strchr(buffer, '/'); /* ... and this is the path */
+ if (!modpath)
+ modpath = buffer; /* module with no path */
+ /* find the end of the module name in the file name */
+ if (ENABLE_FEATURE_2_6_MODULES &&
+ (k_version > 4) && (*(col-3) == '.') &&
+ (*(col-2) == 'k') && (*(col-1) == 'o'))
+ dot = col - 3;
+ else
+ if ((*(col-2) == '.') && (*(col-1) == 'o'))
+ dot = col - 2;
+
+ mod = xstrndup(mods, dot - mods);
+
+ /* enqueue new module */
+ if (!current) {
+ first = current = xmalloc(sizeof(struct dep_t));
+ } else {
+ current->m_next = xmalloc(sizeof(struct dep_t));
+ current = current->m_next;
+ }
+ current->m_name = mod;
+ current->m_path = xstrdup(modpath);
+ current->m_options = NULL;
+ current->m_isalias = 0;
+ current->m_depcnt = 0;
+ current->m_deparr = 0;
+ current->m_next = 0;
+
+ p = col + 1;
+ } else
+ /* this line is not a dep description */
+ p = 0;
+ } else
+ /* It's a dep description continuation */
+ p = buffer;
+
+ while (p && *p && isblank(*p))
+ p++;
+
+ /* p points to the first dependable module; if NULL, no dependable module */
+ if (p && *p) {
+ char *end = &buffer[l-1];
+ char *deps;
+ char *dep;
+ char *next;
+ int ext = 0;
+
+ while (isblank(*end) || (*end == '\\'))
+ end--;
+
+ do {
+ /* search the end of the dependency */
+ next = strchr(p, ' ');
+ if (next) {
+ *next = 0;
+ next--;
+ } else
+ next = end;
+
+ /* find the beginning of the module file name */
+ deps = strrchr(p, '/');
+
+ if (!deps || (deps < p)) {
+ deps = p;
+
+ while (isblank(*deps))
+ deps++;
+ } else
+ deps++;
+
+ /* find the end of the module name in the file name */
+ if (ENABLE_FEATURE_2_6_MODULES
+ && (k_version > 4) && (*(next-2) == '.')
+ && (*(next-1) == 'k') && (*next == 'o'))
+ ext = 3;
+ else
+ if ((*(next-1) == '.') && (*next == 'o'))
+ ext = 2;
+
+ /* Cope with blank lines */
+ if ((next-deps-ext+1) <= 0)
+ continue;
+ dep = xstrndup(deps, next - deps - ext + 1);
+
+ /* Add the new dependable module name */
+ current->m_depcnt++;
+ current->m_deparr = xrealloc(current->m_deparr,
+ sizeof(char *) * current->m_depcnt);
+ current->m_deparr[current->m_depcnt - 1] = dep;
+
+ p = next + 2;
+ } while (next < end);
+ }
+
+ /* is there other dependable module(s) ? */
+ if (buffer[l-1] == '\\')
+ continuation_line = 1;
+ else
+ continuation_line = 0;
+ }
+ close(fd);
+
+ /*
+ * First parse system-specific options and aliases
+ * as they take precedence over the kernel ones.
+ * >=2.6: we only care about modprobe.conf
+ * <=2.4: we care about modules.conf and conf.modules
+ */
+ if (ENABLE_FEATURE_2_6_MODULES
+ && (fd = open("/etc/modprobe.conf", O_RDONLY)) < 0)
+ if (ENABLE_FEATURE_2_4_MODULES
+ && (fd = open("/etc/modules.conf", O_RDONLY)) < 0)
+ if (ENABLE_FEATURE_2_4_MODULES)
+ fd = open("/etc/conf.modules", O_RDONLY);
+
+ if (fd >= 0) {
+ include_conf(&first, &current, buffer, sizeof(buffer), fd);
+ close(fd);
+ }
+
+ /* Only 2.6 has a modules.alias file */
+ if (ENABLE_FEATURE_2_6_MODULES) {
+ /* Parse kernel-declared aliases */
+ filename = xasprintf("/lib/modules/%s/modules.alias", un.release);
+ fd = open(filename, O_RDONLY);
+ if (fd < 0) {
+ /* Ok, that didn't work. Fall back to looking in /lib/modules */
+ fd = open("/lib/modules/modules.alias", O_RDONLY);
+ }
+ if (ENABLE_FEATURE_CLEAN_UP)
+ free(filename);
+
+ if (fd >= 0) {
+ include_conf(&first, &current, buffer, sizeof(buffer), fd);
+ close(fd);
+ }
+ }
+
+ return first;
+}
+
+/* return 1 = loaded, 0 = not loaded, -1 = can't tell */
+static int already_loaded(const char *name)
+{
+ int fd, ret = 0;
+ char buffer[4096];
+
+ fd = open("/proc/modules", O_RDONLY);
+ if (fd < 0)
+ return -1;
+
+ while (reads(fd, buffer, sizeof(buffer))) {
+ char *p;
+
+ p = strchr (buffer, ' ');
+ if (p) {
+ const char *n;
+
+ // Truncate buffer at first space and check for matches, with
+ // the idiosyncrasy that _ and - are interchangeable because the
+ // 2.6 kernel does weird things.
+
+ *p = 0;
+ for (p = buffer, n = name; ; p++, n++) {
+ if (*p != *n) {
+ if ((*p == '_' || *p == '-') && (*n == '_' || *n == '-'))
+ continue;
+ break;
+ }
+ // If we made it to the end, that's a match.
+ if (!*p) {
+ ret = 1;
+ goto done;
+ }
+ }
+ }
+ }
+done:
+ close (fd);
+ return ret;
+}
+
+static int mod_process(const struct mod_list_t *list, int do_insert)
+{
+ int rc = 0;
+ char **argv = NULL;
+ struct mod_opt_t *opts;
+ int argc_malloc; /* never used when CONFIG_FEATURE_CLEAN_UP not defined */
+ int argc;
+
+ while (list) {
+ argc = 0;
+ if (ENABLE_FEATURE_CLEAN_UP)
+ argc_malloc = 0;
+ /* If CONFIG_FEATURE_CLEAN_UP is not defined, then we leak memory
+ * each time we allocate memory for argv.
+ * But it is (quite) small amounts of memory that leak each
+ * time a module is loaded, and it is reclaimed when modprobe
+ * exits anyway (even when standalone shell?).
+ * This could become a problem when loading a module with LOTS of
+ * dependencies, with LOTS of options for each dependencies, with
+ * very little memory on the target... But in that case, the module
+ * would not load because there is no more memory, so there's no
+ * problem. */
+ /* enough for minimal insmod (5 args + NULL) or rmmod (3 args + NULL) */
+ argv = xmalloc(6 * sizeof(char*));
+ if (do_insert) {
+ if (already_loaded(list->m_name) != 1) {
+ argv[argc++] = (char*)"insmod";
+ if (ENABLE_FEATURE_2_4_MODULES) {
+ if (do_syslog)
+ argv[argc++] = (char*)"-s";
+ if (autoclean)
+ argv[argc++] = (char*)"-k";
+ if (quiet)
+ argv[argc++] = (char*)"-q";
+ else if (verbose) /* verbose and quiet are mutually exclusive */
+ argv[argc++] = (char*)"-v";
+ }
+ argv[argc++] = list->m_path;
+ if (ENABLE_FEATURE_CLEAN_UP)
+ argc_malloc = argc;
+ opts = list->m_options;
+ while (opts) {
+ /* Add one more option */
+ argc++;
+ argv = xrealloc(argv,(argc + 1)* sizeof(char*));
+ argv[argc-1] = opts->m_opt_val;
+ opts = opts->m_next;
+ }
+ }
+ } else {
+ /* modutils uses short name for removal */
+ if (already_loaded(list->m_name) != 0) {
+ argv[argc++] = (char*)"rmmod";
+ if (do_syslog)
+ argv[argc++] = (char*)"-s";
+ argv[argc++] = (char*)list->m_name;
+ if (ENABLE_FEATURE_CLEAN_UP)
+ argc_malloc = argc;
+ }
+ }
+ argv[argc] = NULL;
+
+ if (argc) {
+ if (verbose) {
+ printf("%s module %s\n", do_insert?"Loading":"Unloading", list->m_name);
+ }
+ if (!show_only) {
+ int rc2 = wait4pid(spawn(argv));
+
+ if (do_insert) {
+ rc = rc2; /* only last module matters */
+ } else if (!rc2) {
+ rc = 0; /* success if remove any mod */
+ }
+ }
+ if (ENABLE_FEATURE_CLEAN_UP) {
+ /* the last value in the array has index == argc, but
+ * it is the terminating NULL, so we must not free it. */
+ while (argc_malloc < argc) {
+ free(argv[argc_malloc++]);
+ }
+ }
+ }
+ if (ENABLE_FEATURE_CLEAN_UP) {
+ free(argv);
+ argv = NULL;
+ }
+ list = do_insert ? list->m_prev : list->m_next;
+ }
+ return (show_only) ? 0 : rc;
+}
+
+/*
+ * Check the matching between a pattern and a module name.
+ * We need this as *_* is equivalent to *-*, even in pattern matching.
+ */
+static int check_pattern(const char* pat_src, const char* mod_src) {
+ int ret;
+
+ if (ENABLE_FEATURE_MODPROBE_FANCY_ALIAS) {
+ char* pat;
+ char* mod;
+ char* p;
+
+ pat = xstrdup (pat_src);
+ mod = xstrdup (mod_src);
+
+ for (p = pat; (p = strchr(p, '-')); *p++ = '_');
+ for (p = mod; (p = strchr(p, '-')); *p++ = '_');
+
+ ret = fnmatch(pat, mod, 0);
+
+ if (ENABLE_FEATURE_CLEAN_UP) {
+ free (pat);
+ free (mod);
+ }
+
+ return ret;
+ } else {
+ return fnmatch(pat_src, mod_src, 0);
+ }
+}
+
+/*
+ * Builds the dependency list (aka stack) of a module.
+ * head: the highest module in the stack (last to insmod, first to rmmod)
+ * tail: the lowest module in the stack (first to insmod, last to rmmod)
+ */
+static void check_dep(char *mod, struct mod_list_t **head, struct mod_list_t **tail)
+{
+ struct mod_list_t *find;
+ struct dep_t *dt;
+ struct mod_opt_t *opt = 0;
+ char *path = 0;
+
+ /* Search for the given module name amongst all dependency rules.
+ * The module name in a dependency rule can be a shell pattern,
+ * so try to match the given module name against such a pattern.
+ * Of course if the name in the dependency rule is a plain string,
+ * then we consider it a pattern, and matching will still work. */
+ for (dt = depend; dt; dt = dt->m_next) {
+ if (check_pattern(dt->m_name, mod) == 0) {
+ break;
+ }
+ }
+
+ if (!dt) {
+ bb_error_msg("module %s not found", mod);
+ return;
+ }
+
+ // resolve alias names
+ while (dt->m_isalias) {
+ if (dt->m_depcnt == 1) {
+ struct dep_t *adt;
+
+ for (adt = depend; adt; adt = adt->m_next) {
+ if (check_pattern(adt->m_name, dt->m_deparr[0]) == 0)
+ break;
+ }
+ if (adt) {
+ /* This is the module we are aliased to */
+ struct mod_opt_t *opts = dt->m_options;
+ /* Option of the alias are appended to the options of the module */
+ while (opts) {
+ adt->m_options = append_option(adt->m_options, opts->m_opt_val);
+ opts = opts->m_next;
+ }
+ dt = adt;
+ } else {
+ bb_error_msg("module %s not found", mod);
+ return;
+ }
+ } else {
+ bb_error_msg("bad alias %s", dt->m_name);
+ return;
+ }
+ }
+
+ mod = dt->m_name;
+ path = dt->m_path;
+ opt = dt->m_options;
+
+ // search for duplicates
+ for (find = *head; find; find = find->m_next) {
+ if (!strcmp(mod, find->m_name)) {
+ // found ->dequeue it
+
+ if (find->m_prev)
+ find->m_prev->m_next = find->m_next;
+ else
+ *head = find->m_next;
+
+ if (find->m_next)
+ find->m_next->m_prev = find->m_prev;
+ else
+ *tail = find->m_prev;
+
+ break; // there can be only one duplicate
+ }
+ }
+
+ if (!find) { // did not find a duplicate
+ find = xmalloc(sizeof(struct mod_list_t));
+ find->m_name = mod;
+ find->m_path = path;
+ find->m_options = opt;
+ }
+
+ // enqueue at tail
+ if (*tail)
+ (*tail)->m_next = find;
+ find->m_prev = *tail;
+ find->m_next = 0;
+
+ if (!*head)
+ *head = find;
+ *tail = find;
+
+ if (dt) {
+ int i;
+
+ /* Add all dependable module for that new module */
+ for (i = 0; i < dt->m_depcnt; i++)
+ check_dep(dt->m_deparr[i], head, tail);
+ }
+}
+
+static int mod_insert(char *mod, int argc, char **argv)
+{
+ struct mod_list_t *tail = NULL;
+ struct mod_list_t *head = NULL;
+ int rc;
+
+ // get dep list for module mod
+ check_dep(mod, &head, &tail);
+
+ rc = 1;
+ if (head && tail) {
+ if (argc) {
+ int i;
+ // append module args
+ for (i = 0; i < argc; i++)
+ head->m_options = append_option(head->m_options, argv[i]);
+ }
+
+ // process tail ---> head
+ rc = mod_process(tail, 1);
+ if (rc) {
+ /*
+ * In case of using udev, multiple instances of modprobe can be
+ * spawned to load the same module (think of two same usb devices,
+ * for example; or cold-plugging at boot time). Thus we shouldn't
+ * fail if the module was loaded, and not by us.
+ */
+ if (already_loaded(mod))
+ rc = 0;
+ }
+ }
+ return rc;
+}
+
+static int mod_remove(char *mod)
+{
+ int rc;
+ static const struct mod_list_t rm_a_dummy = { "-a", NULL, NULL, NULL, NULL };
+
+ struct mod_list_t *head = NULL;
+ struct mod_list_t *tail = NULL;
+
+ if (mod)
+ check_dep(mod, &head, &tail);
+ else // autoclean
+ head = tail = (struct mod_list_t*) &rm_a_dummy;
+
+ rc = 1;
+ if (head && tail)
+ rc = mod_process(head, 0); // process head ---> tail
+ return rc;
+}
+
+int modprobe_main(int argc, char** argv);
+int modprobe_main(int argc, char** argv)
+{
+ int rc = EXIT_SUCCESS;
+ char *unused;
+
+ opt_complementary = "?V-:q-v:v-q";
+ main_opts = getopt32(argc, argv, "acdklnqrst:vVC:",
+ &unused, &unused);
+ if (main_opts & (DUMP_CONF_EXIT | LIST_ALL))
+ return EXIT_SUCCESS;
+ if (main_opts & (RESTRICT_DIR | CONFIG_FILE))
+ bb_error_msg_and_die("-t and -C not supported");
+
+ depend = build_dep();
+
+ if (!depend)
+ bb_error_msg_and_die("cannot parse modules.dep");
+
+ if (remove_opt) {
+ do {
+ if (mod_remove(optind < argc ?
+ argv[optind] : NULL)) {
+ bb_error_msg("failed to remove module %s",
+ argv[optind]);
+ rc = EXIT_FAILURE;
+ }
+ } while (++optind < argc);
+ } else {
+ if (optind >= argc)
+ bb_error_msg_and_die("no module or pattern provided");
+
+ if (mod_insert(argv[optind], argc - optind - 1, argv + optind + 1))
+ bb_error_msg_and_die("failed to load module %s", argv[optind]);
+ }
+
+ /* Here would be a good place to free up memory allocated during the dependencies build. */
+
+ return rc;
+}
diff --git a/i/pc104/initrd/conf/busybox/modutils/rmmod.c b/i/pc104/initrd/conf/busybox/modutils/rmmod.c
new file mode 100644
index 0000000..0a67b89
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/modutils/rmmod.c
@@ -0,0 +1,97 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini rmmod implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "busybox.h"
+#include <sys/syscall.h>
+
+#ifdef CONFIG_FEATURE_2_6_MODULES
+static inline void filename2modname(char *modname, const char *afterslash)
+{
+ unsigned int i;
+ int kr_chk = 1;
+
+ if (ENABLE_FEATURE_2_4_MODULES
+ && get_linux_version_code() <= KERNEL_VERSION(2,6,0))
+ kr_chk = 0;
+
+ /* Convert to underscores, stop at first . */
+ for (i = 0; afterslash[i] && afterslash[i] != '.'; i++) {
+ if (kr_chk && (afterslash[i] == '-'))
+ modname[i] = '_';
+ else
+ modname[i] = afterslash[i];
+ }
+ modname[i] = '\0';
+}
+#else
+void filename2modname(char *modname, const char *afterslash);
+#endif
+
+// There really should be a header file for this...
+
+int query_module(const char *name, int which, void *buf,
+ size_t bufsize, size_t *ret);
+
+int rmmod_main(int argc, char **argv);
+int rmmod_main(int argc, char **argv)
+{
+ int n, ret = EXIT_SUCCESS;
+ unsigned int flags = O_NONBLOCK|O_EXCL;
+
+ /* Parse command line. */
+ n = getopt32(argc, argv, "wfa");
+ if((n & 1)) // --wait
+ flags &= ~O_NONBLOCK;
+ if((n & 2)) // --force
+ flags |= O_TRUNC;
+ if((n & 4)) {
+ /* Unload _all_ unused modules via NULL delete_module() call */
+ /* until the number of modules does not change */
+ size_t nmod = 0; /* number of modules */
+ size_t pnmod = -1; /* previous number of modules */
+
+ while (nmod != pnmod) {
+ if (syscall(__NR_delete_module, NULL, flags) != 0) {
+ if (errno == EFAULT)
+ return ret;
+ bb_perror_msg_and_die("rmmod");
+ }
+ pnmod = nmod;
+ // the 1 here is QM_MODULES.
+ if (ENABLE_FEATURE_QUERY_MODULE_INTERFACE && query_module(NULL,
+ 1, bb_common_bufsiz1, sizeof(bb_common_bufsiz1),
+ &nmod))
+ {
+ bb_perror_msg_and_die("QM_MODULES");
+ }
+ }
+ return EXIT_SUCCESS;
+ }
+
+ if (optind == argc)
+ bb_show_usage();
+
+ for (n = optind; n < argc; n++) {
+ if (ENABLE_FEATURE_2_6_MODULES) {
+ const char *afterslash;
+
+ afterslash = strrchr(argv[n], '/');
+ if (!afterslash) afterslash = argv[n];
+ else afterslash++;
+ filename2modname(bb_common_bufsiz1, afterslash);
+ }
+
+ if (syscall(__NR_delete_module, ENABLE_FEATURE_2_6_MODULES ? bb_common_bufsiz1 : argv[n], flags)) {
+ bb_perror_msg("%s", argv[n]);
+ ret = EXIT_FAILURE;
+ }
+ }
+
+ return ret;
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/Config.in b/i/pc104/initrd/conf/busybox/networking/Config.in
new file mode 100644
index 0000000..11e7812
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/Config.in
@@ -0,0 +1,719 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+menu "Networking Utilities"
+
+config FEATURE_IPV6
+ bool "Enable IPv6 support"
+ default n
+ help
+ Enable IPv6 support in busybox.
+ This adds IPv6 support in the networking applets.
+
+config VERBOSE_RESOLUTION_ERRORS
+ bool "Verbose resolution errors"
+ default n
+ help
+ Enable if you are not satisfied with simplistic
+ "can't resolve 'hostname.com'" and want to know more.
+ This may increase size of your executable a bit.
+
+config ARP
+ bool "arp"
+ default n
+ help
+ Manipulate the system ARP cache
+
+config ARPING
+ bool "arping"
+ default n
+ help
+ Ping hosts by ARP packets
+
+config DNSD
+ bool "dnsd"
+ default n
+ help
+ Small and static DNS server daemon.
+
+config ETHER_WAKE
+ bool "ether-wake"
+ default n
+ help
+ Send a magic packet to wake up sleeping machines.
+
+config FAKEIDENTD
+ bool "fakeidentd"
+ default n
+ select FEATURE_SYSLOG
+ help
+ fakeidentd listens on the ident port and returns a predefined
+ fake value on any query.
+
+config FTPGET
+ bool "ftpget"
+ default n
+ help
+ Retrieve a remote file via FTP.
+
+config FTPPUT
+ bool "ftpput"
+ default n
+ help
+ Store a remote file via FTP.
+
+config FEATURE_FTPGETPUT_LONG_OPTIONS
+ bool "Enable long options in ftpget/ftpput"
+ default n
+ depends on GETOPT_LONG && (FTPGET || FTPPUT)
+ help
+ Support long options for the ftpget/ftpput applet.
+
+config HOSTNAME
+ bool "hostname"
+ default n
+ help
+ Show or set the system's host name
+
+config HTTPD
+ bool "httpd"
+ default n
+ help
+ Serve web pages via an HTTP server.
+
+config FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
+ bool "Support reloading the global config file using hup signal"
+ default n
+ depends on HTTPD
+ help
+ This option enables processing of SIGHUP to reload cached
+ configuration settings.
+
+config FEATURE_HTTPD_SETUID
+ bool "Enable -u <user> option"
+ default n
+ depends on HTTPD
+ help
+ This option allows the server to run as a specific user
+ rather than defaulting to the user that starts the server.
+ Use of this option requires special privileges to change to a
+ different user.
+
+config FEATURE_HTTPD_BASIC_AUTH
+ bool "Enable Basic http Authentication"
+ default y
+ depends on HTTPD
+ help
+ Utilizes password settings from /etc/httpd.conf for basic
+ authentication on a per url basis.
+
+config FEATURE_HTTPD_AUTH_MD5
+ bool "Support MD5 crypted passwords for http Authentication"
+ default n
+ depends on FEATURE_HTTPD_BASIC_AUTH
+ help
+ Enables basic per URL authentication from /etc/httpd.conf
+ using md5 passwords.
+
+config FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
+ bool "Support loading additional MIME types at run-time"
+ default n
+ depends on HTTPD
+ help
+ This option enables support for additional MIME types at
+ run-time to be specified in the configuration file.
+
+config FEATURE_HTTPD_CGI
+ bool "Support Common Gateway Interface (CGI)"
+ default y
+ depends on HTTPD
+ help
+ This option allows scripts and executables to be invoked
+ when specific URLs are requested.
+
+config FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
+ bool "Enable support for running scripts through an interpreter"
+ default n
+ depends on FEATURE_HTTPD_CGI
+ help
+ This option enables support for running scripts through an
+ interpreter. Turn this on if you want PHP scripts to work
+ properly. You need to supply an addition line in your httpd
+ config file:
+ *.php:/path/to/your/php
+
+config FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV
+ bool "Support the REMOTE_PORT environment variable for CGI"
+ default n
+ depends on FEATURE_HTTPD_CGI
+ help
+ Use of this option can assist scripts in generating
+ references that contain a unique port number.
+
+config FEATURE_HTTPD_ENCODE_URL_STR
+ bool "Enable the -e option for shell script CGI simplification."
+ default y
+ depends on HTTPD
+ help
+ This option allows html encoding arbitrary
+ strings for display of the browser. Output goes to stdout.
+ For example, httpd -e "<Hello World>" as
+ "&#60Hello&#32World&#62".
+
+config IFCONFIG
+ bool "ifconfig"
+ default n
+ help
+ Ifconfig is used to configure the kernel-resident network interfaces.
+
+config FEATURE_IFCONFIG_STATUS
+ bool "Enable status reporting output (+7k)"
+ default y
+ depends on IFCONFIG
+ help
+ If ifconfig is called with no arguments it will display the status
+ of the currently active interfaces.
+
+config FEATURE_IFCONFIG_SLIP
+ bool "Enable slip-specific options \"keepalive\" and \"outfill\""
+ default n
+ depends on IFCONFIG
+ help
+ Allow "keepalive" and "outfill" support for SLIP. If you're not
+ planning on using serial lines, leave this unchecked.
+
+config FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ
+ bool "Enable options \"mem_start\", \"io_addr\", and \"irq\""
+ default n
+ depends on IFCONFIG
+ help
+ Allow the start address for shared memory, start address for I/O,
+ and/or the interrupt line used by the specified device.
+
+config FEATURE_IFCONFIG_HW
+ bool "Enable option \"hw\" (ether only)"
+ default y
+ depends on IFCONFIG
+ help
+ Set the hardware address of this interface, if the device driver
+ supports this operation. Currently, we only support the 'ether'
+ class.
+
+config FEATURE_IFCONFIG_BROADCAST_PLUS
+ bool "Set the broadcast automatically"
+ default n
+ depends on IFCONFIG
+ help
+ Setting this will make ifconfig attempt to find the broadcast
+ automatically if the value '+' is used.
+
+config IFUPDOWN
+ bool "ifupdown"
+ default n
+ help
+ Activate or deactivate the specified interfaces. This applet makes
+ use of either "ifconfig" and "route" or the "ip" command to actually
+ configure network interfaces. Therefore, you will probably also want
+ to enable either IFCONFIG and ROUTE, or enable
+ FEATURE_IFUPDOWN_IP and the various IP options. Of
+ course you could use non-busybox versions of these programs, so
+ against my better judgement (since this will surely result in plenty
+ of support questions on the mailing list), I do not force you to
+ enable these additional options. It is up to you to supply either
+ "ifconfig", "route" and "run-parts" or the "ip" command, either
+ via busybox or via
+ standalone utilities.
+
+config FEATURE_IFUPDOWN_IP
+ bool "Use ip applet"
+ default n
+ depends on IFUPDOWN
+ help
+ Use the iproute "ip" command to implement "ifup" and "ifdown", rather
+ than the default of using the older 'ifconfig' and 'route' utilities.
+
+config FEATURE_IFUPDOWN_IP_BUILTIN
+ bool "Use busybox ip applet"
+ default y
+ depends on FEATURE_IFUPDOWN_IP
+ select IP
+ select FEATURE_IP_ADDRESS
+ select FEATURE_IP_LINK
+ select FEATURE_IP_ROUTE
+ help
+ Use the busybox iproute "ip" applet to implement "ifupdown".
+
+ If left disabled, you must install the full-blown iproute2
+ utility or the "ifup" and "ifdown" applets will not work.
+
+config FEATURE_IFUPDOWN_IFCONFIG_BUILTIN
+ bool "Use busybox ifconfig and route applets"
+ default y
+ depends on IFUPDOWN && !FEATURE_IFUPDOWN_IP
+ select IFCONFIG
+ select ROUTE
+ help
+ Use the busybox iproute "ifconfig" and "route" applets to
+ implement the "ifup" and "ifdown" utilities.
+
+ If left disabled, you must install the full-blown ifconfig
+ and route utilities, or the "ifup" and "ifdown" applets will not
+ work.
+
+config FEATURE_IFUPDOWN_IPV4
+ bool "Enable support for IPv4"
+ default y
+ depends on IFUPDOWN
+ help
+ If you want busybox to talk IPv4, leave this on.
+
+config FEATURE_IFUPDOWN_IPV6
+ bool "Enable support for IPv6"
+ default n
+ depends on IFUPDOWN && FEATURE_IPV6
+ help
+ If you need support for IPv6, turn this option on.
+
+config FEATURE_IFUPDOWN_IPX
+ bool "Enable support for IPX"
+ default n
+ depends on IFUPDOWN
+ help
+ If this option is selected you can use busybox to work with IPX
+ networks.
+
+config FEATURE_IFUPDOWN_MAPPING
+ bool "Enable mapping support"
+ default n
+ depends on IFUPDOWN
+ help
+ This enables support for the "mapping" stanza, unless you have
+ a weird network setup you don't need it.
+
+config INETD
+ bool "inetd"
+ default n
+ select FEATURE_SYSLOG
+ help
+ Internet superserver daemon
+
+config FEATURE_INETD_SUPPORT_BUILTIN_ECHO
+ bool "Support echo service"
+ default y
+ depends on INETD
+ help
+ Echo received data internal inetd service
+
+config FEATURE_INETD_SUPPORT_BUILTIN_DISCARD
+ bool "Support discard service"
+ default y
+ depends on INETD
+ help
+ Internet /dev/null internal inetd service
+
+config FEATURE_INETD_SUPPORT_BUILTIN_TIME
+ bool "Support time service"
+ default y
+ depends on INETD
+ help
+ Return 32 bit time since 1900 internal inetd service
+
+config FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME
+ bool "Support daytime service"
+ default y
+ depends on INETD
+ help
+ Return human-readable time internal inetd service
+
+config FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN
+ bool "Support chargen service"
+ default y
+ depends on INETD
+ help
+ Familiar character generator internal inetd service
+
+config FEATURE_INETD_RPC
+ bool "Support RPC services"
+ default n
+ depends on INETD && FEATURE_HAVE_RPC
+ help
+ Support Sun-RPC based services
+
+config IP
+ bool "ip"
+ default n
+ help
+ The "ip" applet is a TCP/IP interface configuration and routing
+ utility. You generally don't need "ip" to use busybox with
+ TCP/IP.
+
+config FEATURE_IP_ADDRESS
+ bool "ip address"
+ default y
+ depends on IP
+ help
+ Address manipulation support for the "ip" applet.
+
+config FEATURE_IP_LINK
+ bool "ip link"
+ default y
+ depends on IP
+ help
+ Configure network devices with "ip".
+
+config FEATURE_IP_ROUTE
+ bool "ip route"
+ default y
+ depends on IP
+ help
+ Add support for routing table management to "ip".
+
+config FEATURE_IP_TUNNEL
+ bool "ip tunnel"
+ default n
+ depends on IP
+ help
+ Add support for tunneling commands to "ip".
+
+config FEATURE_IP_RULE
+ bool "ip rule"
+ default n
+ depends on IP
+ help
+ Add support for rule commands to "ip".
+
+config FEATURE_IP_SHORT_FORMS
+ bool "Support short forms of ip commands."
+ default n
+ depends on IP
+ help
+ Also support short-form of ip <OBJECT> commands:
+ ip addr -> ipaddr
+ ip link -> iplink
+ ip route -> iproute
+ ip tunnel -> iptunnel
+ ip rule -> iprule
+
+ Say N unless you desparately need the short form of the ip
+ object commands.
+
+config IPADDR
+ bool
+ default y
+ depends on FEATURE_IP_SHORT_FORMS && FEATURE_IP_ADDRESS
+
+config IPLINK
+ bool
+ default y
+ depends on FEATURE_IP_SHORT_FORMS && FEATURE_IP_LINK
+
+config IPROUTE
+ bool
+ default y
+ depends on FEATURE_IP_SHORT_FORMS && FEATURE_IP_ROUTE
+
+config IPTUNNEL
+ bool
+ default y
+ depends on FEATURE_IP_SHORT_FORMS && FEATURE_IP_TUNNEL
+
+config IPRULE
+ bool
+ default y
+ depends on FEATURE_IP_SHORT_FORMS && FEATURE_IP_RULE
+
+config IPCALC
+ bool "ipcalc"
+ default n
+ help
+ ipcalc takes an IP address and netmask and calculates the
+ resulting broadcast, network, and host range.
+
+config FEATURE_IPCALC_FANCY
+ bool "Fancy IPCALC, more options, adds 1 kbyte"
+ default y
+ depends on IPCALC
+ help
+ Adds the options hostname, prefix and silent to the output of "ipcalc".
+
+config FEATURE_IPCALC_LONG_OPTIONS
+ bool "Enable long options"
+ default n
+ depends on IPCALC && GETOPT_LONG
+ help
+ Support long options for the ipcalc applet.
+
+config NAMEIF
+ bool "nameif"
+ default n
+ select FEATURE_SYSLOG
+ help
+ nameif is used to rename network interface by its MAC address.
+ Renamed interfaces MUST be in the down state.
+ It is possible to use a file (default: /etc/mactab)
+ with list of new interface names and MACs.
+ Maximum interface name length: IF_NAMESIZE = 16
+ File fields are separated by space or tab.
+ File format:
+ # Comment
+ new_interface_name XX:XX:XX:XX:XX:XX
+
+config NC
+ bool "nc"
+ default n
+ help
+ A simple Unix utility which reads and writes data across network
+ connections.
+
+config NC_SERVER
+ bool "Netcat server options (-lp)"
+ default n
+ depends on NC
+ help
+ Allow netcat to act as a server.
+
+config NC_EXTRA
+ bool "Netcat extensions (-eiw and filename)"
+ default n
+ depends on NC
+ help
+ Add -e (support for executing the rest of the command line after
+ making or receiving a successful connection), -i (delay interval for
+ lines sent), -w (timeout for initial connection).
+
+config NETSTAT
+ bool "netstat"
+ default n
+ help
+ netstat prints information about the Linux networking subsystem.
+
+config NSLOOKUP
+ bool "nslookup"
+ default n
+ help
+ nslookup is a tool to query Internet name servers.
+
+config PING
+ bool "ping"
+ default n
+ help
+ ping uses the ICMP protocol's mandatory ECHO_REQUEST datagram to
+ elicit an ICMP ECHO_RESPONSE from a host or gateway.
+
+config PING6
+ bool "ping6"
+ default n
+ depends on FEATURE_IPV6 && PING
+ help
+ This will give you a ping that can talk IPv6.
+
+config FEATURE_FANCY_PING
+ bool "Enable fancy ping output"
+ default y
+ depends on PING
+ help
+ Make the output from the ping applet include statistics, and at the
+ same time provide full support for ICMP packets.
+
+config ROUTE
+ bool "route"
+ default n
+ help
+ Route displays or manipulates the kernel's IP routing tables.
+
+config TELNET
+ bool "telnet"
+ default n
+ help
+ Telnet is an interface to the TELNET protocol, but is also commonly
+ used to test other simple protocols.
+
+config FEATURE_TELNET_TTYPE
+ bool "Pass TERM type to remote host"
+ default y
+ depends on TELNET
+ help
+ Setting this option will forward the TERM environment variable to the
+ remote host you are connecting to. This is useful to make sure that
+ things like ANSI colors and other control sequences behave.
+
+config FEATURE_TELNET_AUTOLOGIN
+ bool "Pass USER type to remote host"
+ default y
+ depends on TELNET
+ help
+ Setting this option will forward the USER environment variable to the
+ remote host you are connecting to. This is useful when you need to
+ log into a machine without telling the username (autologin). This
+ option enables `-a' and `-l USER' arguments.
+
+config TELNETD
+ bool "telnetd"
+ default n
+ select FEATURE_SYSLOG
+ help
+ A daemon for the TELNET protocol, allowing you to log onto the host
+ running the daemon. Please keep in mind that the TELNET protocol
+ sends passwords in plain text. If you can't afford the space for an
+ SSH daemon and you trust your network, you may say 'y' here. As a
+ more secure alternative, you should seriously consider installing the
+ very small Dropbear SSH daemon instead:
+ http://matt.ucc.asn.au/dropbear/dropbear.html
+
+ Note that for busybox telnetd to work you need several things:
+ First of all, your kernel needs:
+ UNIX98_PTYS=y
+ DEVPTS_FS=y
+
+ Next, you need a /dev/pts directory on your root filesystem:
+
+ $ ls -ld /dev/pts
+ drwxr-xr-x 2 root root 0 Sep 23 13:21 /dev/pts/
+
+ Next you need the pseudo terminal master multiplexer /dev/ptmx:
+
+ $ ls -la /dev/ptmx
+ crw-rw-rw- 1 root tty 5, 2 Sep 23 13:55 /dev/ptmx
+
+ Any /dev/ttyp[0-9]* files you may have can be removed.
+ Next, you need to mount the devpts filesystem on /dev/pts using:
+
+ mount -t devpts devpts /dev/pts
+
+ You need to be sure that Busybox has LOGIN and
+ FEATURE_SUID enabled. And finally, you should make
+ certain that Busybox has been installed setuid root:
+
+ chown root.root /bin/busybox
+ chmod 4755 /bin/busybox
+
+ with all that done, telnetd _should_ work....
+
+
+config FEATURE_TELNETD_STANDALONE
+ bool "Support standalone telnetd (not inetd only)"
+ default n
+ depends on TELNETD
+ help
+ Selecting this will make telnetd able to run standalone.
+
+config TFTP
+ bool "tftp"
+ default n
+ help
+ This enables the Trivial File Transfer Protocol client program. TFTP
+ is usually used for simple, small transfers such as a root image
+ for a network-enabled bootloader.
+
+config FEATURE_TFTP_GET
+ bool "Enable \"get\" command"
+ default y
+ depends on TFTP
+ help
+ Add support for the GET command within the TFTP client. This allows
+ a client to retrieve a file from a TFTP server.
+
+config FEATURE_TFTP_PUT
+ bool "Enable \"put\" command"
+ default y
+ depends on TFTP
+ help
+ Add support for the PUT command within the TFTP client. This allows
+ a client to transfer a file to a TFTP server.
+
+config FEATURE_TFTP_BLOCKSIZE
+ bool "Enable \"blocksize\" command"
+ default n
+ depends on TFTP
+ help
+ Allow the client to specify the desired block size for transfers.
+
+config DEBUG_TFTP
+ bool "Enable debug"
+ default n
+ depends on TFTP
+ help
+ Enable debug settings for tftp. This is useful if you're running
+ into problems with tftp as the protocol doesn't help you much when
+ you run into problems.
+
+config TRACEROUTE
+ bool "traceroute"
+ default n
+ help
+ Utility to trace the route of IP packets
+
+config FEATURE_TRACEROUTE_VERBOSE
+ bool "Enable verbose output"
+ default n
+ depends on TRACEROUTE
+ help
+ Add some verbosity to traceroute. This includes amongst other things
+ hostnames and ICMP response types.
+
+config FEATURE_TRACEROUTE_SOURCE_ROUTE
+ bool "Enable loose source route"
+ default n
+ depends on TRACEROUTE
+ help
+ Add option to specify a loose source route gateway
+ (8 maximum).
+
+config FEATURE_TRACEROUTE_USE_ICMP
+ bool "Use ICMP instead of UDP"
+ default n
+ depends on TRACEROUTE
+ help
+ Add feature to allow for ICMP ECHO instead of UDP datagrams.
+
+source networking/udhcp/Config.in
+
+config VCONFIG
+ bool "vconfig"
+ default n
+ help
+ Creates, removes, and configures VLAN interfaces
+
+config WGET
+ bool "wget"
+ default n
+ help
+ wget is a utility for non-interactive download of files from HTTP,
+ HTTPS, and FTP servers.
+
+config FEATURE_WGET_STATUSBAR
+ bool "Enable a nifty process meter (+2k)"
+ default y
+ depends on WGET
+ help
+ Enable the transfer progress bar for wget transfers.
+
+config FEATURE_WGET_AUTHENTICATION
+ bool "Enable HTTP authentication"
+ default y
+ depends on WGET
+ help
+ Support authenticated HTTP transfers.
+
+config FEATURE_WGET_LONG_OPTIONS
+ bool "Enable long options"
+ default n
+ depends on WGET && GETOPT_LONG
+ help
+ Support long options for the wget applet.
+
+config ZCIP
+ bool "zcip"
+ default n
+ select FEATURE_SYSLOG
+ help
+ ZCIP provides ZeroConf IPv4 address selection, according to RFC 3927.
+ It's a daemon that allocates and defends a dynamically assigned
+ address on the 169.254/16 network, requiring no system administrator.
+
+ See http://www.zeroconf.org for further details, and "zcip.script"
+ in the busybox examples.
+
+endmenu
diff --git a/i/pc104/initrd/conf/busybox/networking/Kbuild b/i/pc104/initrd/conf/busybox/networking/Kbuild
new file mode 100644
index 0000000..68d3613
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/Kbuild
@@ -0,0 +1,40 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under the GPL v2, see the file LICENSE in this tarball.
+
+lib-y:=
+lib-$(CONFIG_ARP) += arp.o interface.o
+lib-$(CONFIG_ARPING) += arping.o
+lib-$(CONFIG_DNSD) += dnsd.o
+lib-$(CONFIG_ETHER_WAKE) += ether-wake.o
+lib-$(CONFIG_FAKEIDENTD) += isrv_identd.o isrv.o
+lib-$(CONFIG_FTPGET) += ftpgetput.o
+lib-$(CONFIG_FTPPUT) += ftpgetput.o
+lib-$(CONFIG_HOSTNAME) += hostname.o
+lib-$(CONFIG_HTTPD) += httpd.o
+lib-$(CONFIG_IFCONFIG) += ifconfig.o interface.o
+lib-$(CONFIG_IFUPDOWN) += ifupdown.o
+lib-$(CONFIG_INETD) += inetd.o
+lib-$(CONFIG_IP) += ip.o
+lib-$(CONFIG_IPCALC) += ipcalc.o
+lib-$(CONFIG_IPADDR) += ipaddr.o
+lib-$(CONFIG_IPLINK) += iplink.o
+lib-$(CONFIG_IPROUTE) += iproute.o
+lib-$(CONFIG_IPRULE) += iprule.o
+lib-$(CONFIG_IPTUNNEL) += iptunnel.o
+lib-$(CONFIG_NAMEIF) += nameif.o
+lib-$(CONFIG_NC) += nc.o
+lib-$(CONFIG_NETSTAT) += netstat.o
+lib-$(CONFIG_NSLOOKUP) += nslookup.o
+lib-$(CONFIG_PING) += ping.o
+lib-$(CONFIG_PING6) += ping.o
+lib-$(CONFIG_ROUTE) += route.o
+lib-$(CONFIG_TELNET) += telnet.o
+lib-$(CONFIG_TELNETD) += telnetd.o
+lib-$(CONFIG_TFTP) += tftp.o
+lib-$(CONFIG_TRACEROUTE) += traceroute.o
+lib-$(CONFIG_VCONFIG) += vconfig.o
+lib-$(CONFIG_WGET) += wget.o
+lib-$(CONFIG_ZCIP) += zcip.o
diff --git a/i/pc104/initrd/conf/busybox/networking/arp.c b/i/pc104/initrd/conf/busybox/networking/arp.c
new file mode 100644
index 0000000..7a36f44
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/arp.c
@@ -0,0 +1,496 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * arp.c - Manipulate the system ARP cache
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Author: Fred N. van Kempen, <waltje at uwalt.nl.mugnet.org>
+ * Busybox port: Paul van Gool <pvangool at mimotech.com>
+ *
+ * modified for getopt32 by Arne Bernin <arne [at] alamut.de>
+ */
+
+#include "busybox.h"
+#include "inet_common.h"
+
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <netinet/ether.h>
+#include <netpacket/packet.h>
+
+#define DEBUG 0
+
+#define DFLT_AF "inet"
+#define DFLT_HW "ether"
+
+#define ARP_OPT_A (0x1)
+#define ARP_OPT_p (0x2)
+#define ARP_OPT_H (0x4)
+#define ARP_OPT_t (0x8)
+#define ARP_OPT_i (0x10)
+#define ARP_OPT_a (0x20)
+#define ARP_OPT_d (0x40)
+#define ARP_OPT_n (0x80) /* do not resolve addresses */
+#define ARP_OPT_D (0x100) /* HW-address is devicename */
+#define ARP_OPT_s (0x200)
+#define ARP_OPT_v (0x400 * DEBUG) /* debugging output flag */
+
+
+static const struct aftype *ap; /* current address family */
+static const struct hwtype *hw; /* current hardware type */
+static int sockfd; /* active socket descriptor */
+static smallint hw_set; /* flag if hw-type was set (-H) */
+static const char *device = ""; /* current device */
+
+static const char *const options[] = {
+ "pub",
+ "priv",
+ "temp",
+ "trail",
+ "dontpub",
+ "auto",
+ "dev",
+ "netmask",
+ NULL
+};
+
+/* Delete an entry from the ARP cache. */
+/* Called only from main, once */
+static int arp_del(char **args)
+{
+ char *host;
+ struct arpreq req;
+ struct sockaddr sa;
+ int flags = 0;
+ int err;
+
+ memset(&req, 0, sizeof(req));
+
+ /* Resolve the host name. */
+ host = *args;
+ if (ap->input(host, &sa) < 0) {
+ bb_herror_msg_and_die("%s", host);
+ }
+
+ /* If a host has more than one address, use the correct one! */
+ memcpy(&req.arp_pa, &sa, sizeof(struct sockaddr));
+
+ if (hw_set)
+ req.arp_ha.sa_family = hw->type;
+
+ req.arp_flags = ATF_PERM;
+ args++;
+ while (*args != NULL) {
+ switch (index_in_str_array(options, *args)) {
+ case 0: /* "pub" */
+ flags |= 1;
+ args++;
+ break;
+ case 1: /* "priv" */
+ flags |= 2;
+ args++;
+ break;
+ case 2: /* "temp" */
+ req.arp_flags &= ~ATF_PERM;
+ args++;
+ break;
+ case 3: /* "trail" */
+ req.arp_flags |= ATF_USETRAILERS;
+ args++;
+ break;
+ case 4: /* "dontpub" */
+#ifdef HAVE_ATF_DONTPUB
+ req.arp_flags |= ATF_DONTPUB;
+#else
+ bb_error_msg("feature ATF_DONTPUB is not supported");
+#endif
+ args++;
+ break;
+ case 5: /* "auto" */
+#ifdef HAVE_ATF_MAGIC
+ req.arp_flags |= ATF_MAGIC;
+#else
+ bb_error_msg("feature ATF_MAGIC is not supported");
+#endif
+ args++;
+ break;
+ case 6: /* "dev" */
+ if (*++args == NULL)
+ bb_show_usage();
+ device = *args;
+ args++;
+ break;
+ case 7: /* "netmask" */
+ if (*++args == NULL)
+ bb_show_usage();
+ if (strcmp(*args, "255.255.255.255") != 0) {
+ host = *args;
+ if (ap->input(host, &sa) < 0) {
+ bb_herror_msg_and_die("%s", host);
+ }
+ memcpy(&req.arp_netmask, &sa, sizeof(struct sockaddr));
+ req.arp_flags |= ATF_NETMASK;
+ }
+ args++;
+ break;
+ default:
+ bb_show_usage();
+ break;
+ }
+ }
+ if (flags == 0)
+ flags = 3;
+
+ strncpy(req.arp_dev, device, sizeof(req.arp_dev));
+
+ err = -1;
+
+ /* Call the kernel. */
+ if (flags & 2) {
+ if (option_mask32 & ARP_OPT_v)
+ bb_error_msg("SIOCDARP(nopub)");
+ err = ioctl(sockfd, SIOCDARP, &req);
+ if (err < 0) {
+ if (errno == ENXIO) {
+ if (flags & 1)
+ goto nopub;
+ printf("No ARP entry for %s\n", host);
+ return -1;
+ }
+ bb_perror_msg_and_die("SIOCDARP(priv)");
+ }
+ }
+ if ((flags & 1) && err) {
+ nopub:
+ req.arp_flags |= ATF_PUBL;
+ if (option_mask32 & ARP_OPT_v)
+ bb_error_msg("SIOCDARP(pub)");
+ if (ioctl(sockfd, SIOCDARP, &req) < 0) {
+ if (errno == ENXIO) {
+ printf("No ARP entry for %s\n", host);
+ return -1;
+ }
+ bb_perror_msg_and_die("SIOCDARP(pub)");
+ }
+ }
+ return 0;
+}
+
+/* Get the hardware address to a specified interface name */
+static void arp_getdevhw(char *ifname, struct sockaddr *sa,
+ const struct hwtype *hwt)
+{
+ struct ifreq ifr;
+ const struct hwtype *xhw;
+
+ strcpy(ifr.ifr_name, ifname);
+ if (ioctl(sockfd, SIOCGIFHWADDR, &ifr) < 0) {
+ bb_perror_msg_and_die("cant get HW-Address for '%s'", ifname);
+ }
+ if (hwt && (ifr.ifr_hwaddr.sa_family != hw->type)) {
+ bb_error_msg_and_die("protocol type mismatch");
+ }
+ memcpy(sa, &(ifr.ifr_hwaddr), sizeof(struct sockaddr));
+
+ if (option_mask32 & ARP_OPT_v) {
+ xhw = get_hwntype(ifr.ifr_hwaddr.sa_family);
+ if (!xhw || !xhw->print) {
+ xhw = get_hwntype(-1);
+ }
+ bb_error_msg("device '%s' has HW address %s '%s'",
+ ifname, xhw->name,
+ xhw->print((char *) &ifr.ifr_hwaddr.sa_data));
+ }
+}
+
+/* Set an entry in the ARP cache. */
+/* Called only from main, once */
+static int arp_set(char **args)
+{
+ char *host;
+ struct arpreq req;
+ struct sockaddr sa;
+ int flags;
+
+ memset(&req, 0, sizeof(req));
+
+ host = *args++;
+ if (ap->input(host, &sa) < 0) {
+ bb_herror_msg_and_die("%s", host);
+ }
+ /* If a host has more than one address, use the correct one! */
+ memcpy(&req.arp_pa, &sa, sizeof(struct sockaddr));
+
+ /* Fetch the hardware address. */
+ if (*args == NULL) {
+ bb_error_msg_and_die("need hardware address");
+ }
+ if (option_mask32 & ARP_OPT_D) {
+ arp_getdevhw(*args++, &req.arp_ha, hw_set ? hw : NULL);
+ } else {
+ if (hw->input(*args++, &req.arp_ha) < 0) {
+ bb_error_msg_and_die("invalid hardware address");
+ }
+ }
+
+ /* Check out any modifiers. */
+ flags = ATF_PERM | ATF_COM;
+ while (*args != NULL) {
+ switch (index_in_str_array(options, *args)) {
+ case 0: /* "pub" */
+ flags |= ATF_PUBL;
+ args++;
+ break;
+ case 1: /* "priv" */
+ flags &= ~ATF_PUBL;
+ args++;
+ break;
+ case 2: /* "temp" */
+ flags &= ~ATF_PERM;
+ args++;
+ break;
+ case 3: /* "trail" */
+ flags |= ATF_USETRAILERS;
+ args++;
+ break;
+ case 4: /* "dontpub" */
+#ifdef HAVE_ATF_DONTPUB
+ flags |= ATF_DONTPUB;
+#else
+ bb_error_msg("feature ATF_DONTPUB is not supported");
+#endif
+ args++;
+ break;
+ case 5: /* "auto" */
+#ifdef HAVE_ATF_MAGIC
+ flags |= ATF_MAGIC;
+#else
+ bb_error_msg("feature ATF_MAGIC is not supported");
+#endif
+ args++;
+ break;
+ case 6: /* "dev" */
+ if (*++args == NULL)
+ bb_show_usage();
+ device = *args;
+ args++;
+ break;
+ case 7: /* "netmask" */
+ if (*++args == NULL)
+ bb_show_usage();
+ if (strcmp(*args, "255.255.255.255") != 0) {
+ host = *args;
+ if (ap->input(host, &sa) < 0) {
+ bb_herror_msg_and_die("%s", host);
+ }
+ memcpy(&req.arp_netmask, &sa, sizeof(struct sockaddr));
+ flags |= ATF_NETMASK;
+ }
+ args++;
+ break;
+ default:
+ bb_show_usage();
+ break;
+ }
+ }
+
+ /* Fill in the remainder of the request. */
+ req.arp_flags = flags;
+
+ strncpy(req.arp_dev, device, sizeof(req.arp_dev));
+
+ /* Call the kernel. */
+ if (option_mask32 & ARP_OPT_v)
+ bb_error_msg("SIOCSARP()");
+ if (ioctl(sockfd, SIOCSARP, &req) < 0) {
+ bb_perror_msg_and_die("SIOCSARP");
+ }
+ return 0;
+}
+
+
+/* Print the contents of an ARP request block. */
+static void
+arp_disp(const char *name, char *ip, int type, int arp_flags,
+ char *hwa, char *mask, char *dev)
+{
+ const struct hwtype *xhw;
+
+ xhw = get_hwntype(type);
+ if (xhw == NULL)
+ xhw = get_hwtype(DFLT_HW);
+
+ printf("%s (%s) at ", name, ip);
+
+ if (!(arp_flags & ATF_COM)) {
+ if (arp_flags & ATF_PUBL)
+ printf("* ");
+ else
+ printf("<incomplete> ");
+ } else {
+ printf("%s [%s] ", hwa, xhw->name);
+ }
+
+ if (arp_flags & ATF_NETMASK)
+ printf("netmask %s ", mask);
+
+ if (arp_flags & ATF_PERM)
+ printf("PERM ");
+ if (arp_flags & ATF_PUBL)
+ printf("PUP ");
+#ifdef HAVE_ATF_MAGIC
+ if (arp_flags & ATF_MAGIC)
+ printf("AUTO ");
+#endif
+#ifdef HAVE_ATF_DONTPUB
+ if (arp_flags & ATF_DONTPUB)
+ printf("DONTPUB ");
+#endif
+ if (arp_flags & ATF_USETRAILERS)
+ printf("TRAIL ");
+
+ printf("on %s\n", dev);
+}
+
+/* Display the contents of the ARP cache in the kernel. */
+/* Called only from main, once */
+static int arp_show(char *name)
+{
+ const char *host;
+ const char *hostname;
+ FILE *fp;
+ struct sockaddr sa;
+ int type, flags;
+ int num;
+ unsigned entries = 0, shown = 0;
+ char ip[128];
+ char hwa[128];
+ char mask[128];
+ char line[128];
+ char dev[128];
+
+ host = NULL;
+ if (name != NULL) {
+ /* Resolve the host name. */
+ if (ap->input(name, &sa) < 0) {
+ bb_herror_msg_and_die("%s", name);
+ }
+ host = xstrdup(ap->sprint(&sa, 1));
+ }
+ fp = xfopen("/proc/net/arp", "r");
+ /* Bypass header -- read one line */
+ fgets(line, sizeof(line), fp);
+
+ /* Read the ARP cache entries. */
+ while (fgets(line, sizeof(line), fp)) {
+
+ mask[0] = '-'; mask[1] = '\0';
+ dev[0] = '-'; dev[1] = '\0';
+ /* All these strings can't overflow
+ * because fgets above reads limited amount of data */
+ num = sscanf(line, "%s 0x%x 0x%x %s %s %s\n",
+ ip, &type, &flags, hwa, mask, dev);
+ if (num < 4)
+ break;
+
+ entries++;
+ /* if the user specified hw-type differs, skip it */
+ if (hw_set && (type != hw->type))
+ continue;
+
+ /* if the user specified address differs, skip it */
+ if (host && strcmp(ip, host) != 0)
+ continue;
+
+ /* if the user specified device differs, skip it */
+ if (device[0] && strcmp(dev, device) != 0)
+ continue;
+
+ shown++;
+ /* This IS ugly but it works -be */
+ hostname = "?";
+ if (!(option_mask32 & ARP_OPT_n)) {
+ if (ap->input(ip, &sa) < 0)
+ hostname = ip;
+ else
+ hostname = ap->sprint(&sa, (option_mask32 & ARP_OPT_n) | 0x8000);
+ if (strcmp(hostname, ip) == 0)
+ hostname = "?";
+ }
+
+ arp_disp(hostname, ip, type, flags, hwa, mask, dev);
+ }
+ if (option_mask32 & ARP_OPT_v)
+ printf("Entries: %d\tSkipped: %d\tFound: %d\n",
+ entries, entries - shown, shown);
+
+ if (!shown) {
+ if (hw_set || host || device[0])
+ printf("No match found in %d entries\n", entries);
+ }
+ if (ENABLE_FEATURE_CLEAN_UP) {
+ free((char*)host);
+ fclose(fp);
+ }
+ return 0;
+}
+
+int arp_main(int argc, char **argv);
+int arp_main(int argc, char **argv)
+{
+ char *hw_type;
+ char *protocol;
+
+ /* Initialize variables... */
+ ap = get_aftype(DFLT_AF);
+ if (!ap)
+ bb_error_msg_and_die("%s: %s not supported", DFLT_AF, "address family");
+
+ getopt32(argc, argv, "A:p:H:t:i:adnDsv", &protocol, &protocol,
+ &hw_type, &hw_type, &device);
+ argv += optind;
+ if (option_mask32 & ARP_OPT_A || option_mask32 & ARP_OPT_p) {
+ ap = get_aftype(protocol);
+ if (ap == NULL)
+ bb_error_msg_and_die("%s: unknown %s", protocol, "address family");
+ }
+ if (option_mask32 & ARP_OPT_A || option_mask32 & ARP_OPT_p) {
+ hw = get_hwtype(hw_type);
+ if (hw == NULL)
+ bb_error_msg_and_die("%s: unknown %s", hw_type, "hardware type");
+ hw_set = 1;
+ }
+ //if (option_mask32 & ARP_OPT_i)... -i
+
+ if (ap->af != AF_INET) {
+ bb_error_msg_and_die("%s: kernel only supports 'inet'", ap->name);
+ }
+
+ /* If no hw type specified get default */
+ if (!hw) {
+ hw = get_hwtype(DFLT_HW);
+ if (!hw)
+ bb_error_msg_and_die("%s: %s not supported", DFLT_HW, "hardware type");
+ }
+
+ if (hw->alen <= 0) {
+ bb_error_msg_and_die("%s: %s without ARP support",
+ hw->name, "hardware type");
+ }
+ sockfd = xsocket(AF_INET, SOCK_DGRAM, 0);
+
+ /* Now see what we have to do here... */
+ if (option_mask32 & (ARP_OPT_d|ARP_OPT_s)) {
+ if (argv[0] == NULL)
+ bb_error_msg_and_die("need host name");
+ if (option_mask32 & ARP_OPT_s)
+ return arp_set(argv);
+ return arp_del(argv);
+ }
+ //if (option_mask32 & ARP_OPT_a) - default
+ return arp_show(argv[0]);
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/arping.c b/i/pc104/initrd/conf/busybox/networking/arping.c
new file mode 100644
index 0000000..d71ac49
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/arping.c
@@ -0,0 +1,417 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * arping.c - Ping hosts by ARP requests/replies
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ *
+ * Author: Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
+ * Busybox port: Nick Fedchik <nick@fedchik.org.ua>
+ */
+
+#include <sys/ioctl.h>
+#include <signal.h>
+
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <netinet/ether.h>
+#include <netpacket/packet.h>
+
+#include "busybox.h"
+
+static struct in_addr src;
+static struct in_addr dst;
+static struct sockaddr_ll me;
+static struct sockaddr_ll he;
+static struct timeval last;
+
+enum cfg_e {
+ dad = 1,
+ unsolicited = 2,
+ advert = 4,
+ quiet = 8,
+ quit_on_reply = 16,
+ broadcast_only = 32,
+ unicasting = 64
+};
+static int cfg;
+
+static int s;
+static unsigned count = UINT_MAX;
+static unsigned timeout;
+static int sent;
+static int brd_sent;
+static int received;
+static int brd_recv;
+static int req_recv;
+
+#define MS_TDIFF(tv1,tv2) ( ((tv1).tv_sec-(tv2).tv_sec)*1000 + \
+ ((tv1).tv_usec-(tv2).tv_usec)/1000 )
+
+static int send_pack(int sock, struct in_addr *src_addr,
+ struct in_addr *dst_addr, struct sockaddr_ll *ME,
+ struct sockaddr_ll *HE)
+{
+ int err;
+ struct timeval now;
+ RESERVE_CONFIG_UBUFFER(buf, 256);
+ struct arphdr *ah = (struct arphdr *) buf;
+ unsigned char *p = (unsigned char *) (ah + 1);
+
+ ah->ar_hrd = htons(ME->sll_hatype);
+ ah->ar_hrd = htons(ARPHRD_ETHER);
+ ah->ar_pro = htons(ETH_P_IP);
+ ah->ar_hln = ME->sll_halen;
+ ah->ar_pln = 4;
+ ah->ar_op = cfg & advert ? htons(ARPOP_REPLY) : htons(ARPOP_REQUEST);
+
+ memcpy(p, &ME->sll_addr, ah->ar_hln);
+ p += ME->sll_halen;
+
+ memcpy(p, src_addr, 4);
+ p += 4;
+
+ if (cfg & advert)
+ memcpy(p, &ME->sll_addr, ah->ar_hln);
+ else
+ memcpy(p, &HE->sll_addr, ah->ar_hln);
+ p += ah->ar_hln;
+
+ memcpy(p, dst_addr, 4);
+ p += 4;
+
+ gettimeofday(&now, NULL);
+ err = sendto(sock, buf, p - buf, 0, (struct sockaddr *) HE, sizeof(*HE));
+ if (err == p - buf) {
+ last = now;
+ sent++;
+ if (!(cfg & unicasting))
+ brd_sent++;
+ }
+ RELEASE_CONFIG_BUFFER(buf);
+ return err;
+}
+
+static void finish(void)
+{
+ if (!(cfg & quiet)) {
+ printf("Sent %d probe(s) (%d broadcast(s))\n"
+ "Received %d repl%s"
+ " (%d request(s), %d broadcast(s))\n",
+ sent, brd_sent,
+ received, (received == 1) ? "ies" : "y",
+ req_recv, brd_recv);
+ }
+ if (cfg & dad)
+ exit(!!received);
+ if (cfg & unsolicited)
+ exit(0);
+ exit(!received);
+}
+
+static void catcher(void)
+{
+ struct timeval tv;
+ static struct timeval start;
+
+ gettimeofday(&tv, NULL);
+
+ if (start.tv_sec == 0)
+ start = tv;
+
+ if (count-- == 0
+ || (timeout && MS_TDIFF(tv, start) > timeout * 1000 + 500))
+ finish();
+
+ if (last.tv_sec == 0 || MS_TDIFF(tv, last) > 500) {
+ send_pack(s, &src, &dst, &me, &he);
+ if (count == 0 && (cfg & unsolicited))
+ finish();
+ }
+ alarm(1);
+}
+
+static int recv_pack(unsigned char *buf, int len, struct sockaddr_ll *FROM)
+{
+ struct arphdr *ah = (struct arphdr *) buf;
+ unsigned char *p = (unsigned char *) (ah + 1);
+ struct in_addr src_ip, dst_ip;
+
+ /* Filter out wild packets */
+ if (FROM->sll_pkttype != PACKET_HOST
+ && FROM->sll_pkttype != PACKET_BROADCAST
+ && FROM->sll_pkttype != PACKET_MULTICAST)
+ return 0;
+
+ /* Only these types are recognised */
+ if (ah->ar_op != htons(ARPOP_REQUEST) && ah->ar_op != htons(ARPOP_REPLY))
+ return 0;
+
+ /* ARPHRD check and this darned FDDI hack here :-( */
+ if (ah->ar_hrd != htons(FROM->sll_hatype)
+ && (FROM->sll_hatype != ARPHRD_FDDI || ah->ar_hrd != htons(ARPHRD_ETHER)))
+ return 0;
+
+ /* Protocol must be IP. */
+ if (ah->ar_pro != htons(ETH_P_IP))
+ return 0;
+ if (ah->ar_pln != 4)
+ return 0;
+ if (ah->ar_hln != me.sll_halen)
+ return 0;
+ if (len < sizeof(*ah) + 2 * (4 + ah->ar_hln))
+ return 0;
+ memcpy(&src_ip, p + ah->ar_hln, 4);
+ memcpy(&dst_ip, p + ah->ar_hln + 4 + ah->ar_hln, 4);
+ if (!(cfg & dad)) {
+ if (src_ip.s_addr != dst.s_addr)
+ return 0;
+ if (src.s_addr != dst_ip.s_addr)
+ return 0;
+ if (memcmp(p + ah->ar_hln + 4, &me.sll_addr, ah->ar_hln))
+ return 0;
+ } else {
+ /* DAD packet was:
+ src_ip = 0 (or some src)
+ src_hw = ME
+ dst_ip = tested address
+ dst_hw = <unspec>
+
+ We fail, if receive request/reply with:
+ src_ip = tested_address
+ src_hw != ME
+ if src_ip in request was not zero, check
+ also that it matches to dst_ip, otherwise
+ dst_ip/dst_hw do not matter.
+ */
+ if (src_ip.s_addr != dst.s_addr)
+ return 0;
+ if (memcmp(p, &me.sll_addr, me.sll_halen) == 0)
+ return 0;
+ if (src.s_addr && src.s_addr != dst_ip.s_addr)
+ return 0;
+ }
+ if (!(cfg & quiet)) {
+ int s_printed = 0;
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
+
+ printf("%scast re%s from %s [%s]",
+ FROM->sll_pkttype == PACKET_HOST ? "Uni" : "Broad",
+ ah->ar_op == htons(ARPOP_REPLY) ? "ply" : "quest",
+ inet_ntoa(src_ip),
+ ether_ntoa((struct ether_addr *) p));
+ if (dst_ip.s_addr != src.s_addr) {
+ printf("for %s ", inet_ntoa(dst_ip));
+ s_printed = 1;
+ }
+ if (memcmp(p + ah->ar_hln + 4, me.sll_addr, ah->ar_hln)) {
+ if (!s_printed)
+ printf("for ");
+ printf("[%s]",
+ ether_ntoa((struct ether_addr *) p + ah->ar_hln + 4));
+ }
+
+ if (last.tv_sec) {
+ long usecs = (tv.tv_sec - last.tv_sec) * 1000000 +
+ tv.tv_usec - last.tv_usec;
+ long msecs = (usecs + 500) / 1000;
+
+ usecs -= msecs * 1000 - 500;
+ printf(" %ld.%03ldms\n", msecs, usecs);
+ } else {
+ printf(" UNSOLICITED?\n");
+ }
+ fflush(stdout);
+ }
+ received++;
+ if (FROM->sll_pkttype != PACKET_HOST)
+ brd_recv++;
+ if (ah->ar_op == htons(ARPOP_REQUEST))
+ req_recv++;
+ if (cfg & quit_on_reply)
+ finish();
+ if (!(cfg & broadcast_only)) {
+ memcpy(he.sll_addr, p, me.sll_halen);
+ cfg |= unicasting;
+ }
+ return 1;
+}
+
+int arping_main(int argc, char **argv);
+int arping_main(int argc, char **argv)
+{
+ const char *device = "eth0";
+ int ifindex;
+ char *source = NULL;
+ char *target;
+
+ s = xsocket(PF_PACKET, SOCK_DGRAM, 0);
+
+ // Drop suid root privileges
+ xsetuid(getuid());
+
+ {
+ unsigned opt;
+ char *_count, *_timeout;
+
+ /* Dad also sets quit_on_reply.
+ * Advert also sets unsolicited.
+ */
+ opt_complementary = "Df:AU";
+ opt = getopt32(argc, argv, "DUAqfbc:w:i:s:",
+ &_count, &_timeout, &device, &source);
+ cfg |= opt & 0x3f; /* set respective flags */
+ if (opt & 0x40) /* -c: count */
+ count = xatou(_count);
+ if (opt & 0x80) /* -w: timeout */
+ timeout = xatoul_range(_timeout, 0, INT_MAX/2000);
+ //if (opt & 0x100) /* -i: interface */
+ if (strlen(device) > IF_NAMESIZE) {
+ bb_error_msg_and_die("interface name '%s' is too long",
+ device);
+ }
+ //if (opt & 0x200) /* -s: source */
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 1)
+ bb_show_usage();
+
+ target = *argv;
+
+ xfunc_error_retval = 2;
+
+ {
+ struct ifreq ifr;
+
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, device, IFNAMSIZ - 1);
+ if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) {
+ bb_error_msg_and_die("interface %s not found", device);
+ }
+ ifindex = ifr.ifr_ifindex;
+
+ if (ioctl(s, SIOCGIFFLAGS, (char *) &ifr)) {
+ bb_error_msg_and_die("SIOCGIFFLAGS");
+ }
+ if (!(ifr.ifr_flags & IFF_UP)) {
+ bb_error_msg_and_die("interface %s is down", device);
+ }
+ if (ifr.ifr_flags & (IFF_NOARP | IFF_LOOPBACK)) {
+ bb_error_msg("interface %s is not ARPable", device);
+ return (cfg & dad ? 0 : 2);
+ }
+ }
+
+ if (!inet_aton(target, &dst)) {
+ len_and_sockaddr *lsa;
+ lsa = xhost_and_af2sockaddr(target, 0, AF_INET);
+ memcpy(&dst, &lsa->sin.sin_addr.s_addr, 4);
+ if (ENABLE_FEATURE_CLEAN_UP)
+ free(lsa);
+ }
+
+ if (source && !inet_aton(source, &src)) {
+ bb_error_msg_and_die("invalid source address %s", source);
+ }
+
+ if (!(cfg & dad) && (cfg & unsolicited) && src.s_addr == 0)
+ src = dst;
+
+ if (!(cfg & dad) || src.s_addr) {
+ struct sockaddr_in saddr;
+ int probe_fd = xsocket(AF_INET, SOCK_DGRAM, 0);
+
+ if (device) {
+ if (setsockopt(probe_fd, SOL_SOCKET, SO_BINDTODEVICE, device, strlen(device) + 1) == -1)
+ bb_error_msg("warning: interface %s is ignored", device);
+ }
+ memset(&saddr, 0, sizeof(saddr));
+ saddr.sin_family = AF_INET;
+ if (src.s_addr) {
+ saddr.sin_addr = src;
+ xbind(probe_fd, (struct sockaddr *) &saddr, sizeof(saddr));
+ } else if (!(cfg & dad)) {
+ socklen_t alen = sizeof(saddr);
+
+ saddr.sin_port = htons(1025);
+ saddr.sin_addr = dst;
+
+ if (setsockopt(probe_fd, SOL_SOCKET, SO_DONTROUTE, &const_int_1, sizeof(const_int_1)) == -1)
+ bb_perror_msg("warning: setsockopt(SO_DONTROUTE)");
+ xconnect(probe_fd, (struct sockaddr *) &saddr, sizeof(saddr));
+ if (getsockname(probe_fd, (struct sockaddr *) &saddr, &alen) == -1) {
+ bb_error_msg_and_die("getsockname");
+ }
+ src = saddr.sin_addr;
+ }
+ close(probe_fd);
+ }
+
+ me.sll_family = AF_PACKET;
+ me.sll_ifindex = ifindex;
+ me.sll_protocol = htons(ETH_P_ARP);
+ xbind(s, (struct sockaddr *) &me, sizeof(me));
+
+ {
+ socklen_t alen = sizeof(me);
+
+ if (getsockname(s, (struct sockaddr *) &me, &alen) == -1) {
+ bb_error_msg_and_die("getsockname");
+ }
+ }
+ if (me.sll_halen == 0) {
+ bb_error_msg("interface \"%s\" is not ARPable (no ll address)", device);
+ return (cfg & dad ? 0 : 2);
+ }
+ he = me;
+ memset(he.sll_addr, -1, he.sll_halen);
+
+ if (!(cfg & quiet)) {
+ printf("ARPING to %s from %s via %s\n",
+ inet_ntoa(dst), inet_ntoa(src),
+ device ? device : "unknown");
+ }
+
+ if (!src.s_addr && !(cfg & dad)) {
+ bb_error_msg_and_die("no src address in the non-DAD mode");
+ }
+
+ {
+ struct sigaction sa;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_flags = SA_RESTART;
+
+ sa.sa_handler = (void (*)(int)) finish;
+ sigaction(SIGINT, &sa, NULL);
+
+ sa.sa_handler = (void (*)(int)) catcher;
+ sigaction(SIGALRM, &sa, NULL);
+ }
+
+ catcher();
+
+ while (1) {
+ sigset_t sset, osset;
+ RESERVE_CONFIG_UBUFFER(packet, 4096);
+ struct sockaddr_ll from;
+ socklen_t alen = sizeof(from);
+ int cc;
+
+ cc = recvfrom(s, packet, 4096, 0, (struct sockaddr *) &from, &alen);
+ if (cc < 0) {
+ bb_perror_msg("recvfrom");
+ continue;
+ }
+ sigemptyset(&sset);
+ sigaddset(&sset, SIGALRM);
+ sigaddset(&sset, SIGINT);
+ sigprocmask(SIG_BLOCK, &sset, &osset);
+ recv_pack(packet, cc, &from);
+ sigprocmask(SIG_SETMASK, &osset, NULL);
+ RELEASE_CONFIG_BUFFER(packet);
+ }
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/dnsd.c b/i/pc104/initrd/conf/busybox/networking/dnsd.c
new file mode 100644
index 0000000..78722d6
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/dnsd.c
@@ -0,0 +1,426 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini DNS server implementation for busybox
+ *
+ * Copyright (C) 2005 Roberto A. Foglietta (me@roberto.foglietta.name)
+ * Copyright (C) 2005 Odd Arild Olsen (oao at fibula dot no)
+ * Copyright (C) 2003 Paul Sheer
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ *
+ * Odd Arild Olsen started out with the sheerdns [1] of Paul Sheer and rewrote
+ * it into a shape which I believe is both easier to understand and maintain.
+ * I also reused the input buffer for output and removed services he did not
+ * need. [1] http://threading.2038bug.com/sheerdns/
+ *
+ * Some bugfix and minor changes was applied by Roberto A. Foglietta who made
+ * the first porting of oao' scdns to busybox also.
+ */
+
+#include "busybox.h"
+
+static const char *fileconf = "/etc/dnsd.conf";
+#define LOCK_FILE "/var/run/dnsd.lock"
+
+// Must match getopt32 call
+#define OPT_daemon (option_mask32 & 0x10)
+#define OPT_verbose (option_mask32 & 0x20)
+
+//#define DEBUG 1
+#define DEBUG 0
+
+enum {
+ MAX_HOST_LEN = 16, // longest host name allowed is 15
+ IP_STRING_LEN = 18, // .xxx.xxx.xxx.xxx\0
+
+//must be strlen('.in-addr.arpa') larger than IP_STRING_LEN
+ MAX_NAME_LEN = (IP_STRING_LEN + 13),
+
+/* Cannot get bigger packets than 512 per RFC1035
+ In practice this can be set considerably smaller:
+ Length of response packet is header (12B) + 2*type(4B) + 2*class(4B) +
+ ttl(4B) + rlen(2B) + r (MAX_NAME_LEN =21B) +
+ 2*querystring (2 MAX_NAME_LEN= 42B), all together 90 Byte
+*/
+ MAX_PACK_LEN = 512 + 1,
+
+ DEFAULT_TTL = 30, // increase this when not testing?
+
+ REQ_A = 1,
+ REQ_PTR = 12
+};
+
+struct dns_repl { // resource record, add 0 or 1 to accepted dns_msg in resp
+ uint16_t rlen;
+ uint8_t *r; // resource
+ uint16_t flags;
+};
+
+struct dns_head { // the message from client and first part of response mag
+ uint16_t id;
+ uint16_t flags;
+ uint16_t nquer; // accepts 0
+ uint16_t nansw; // 1 in response
+ uint16_t nauth; // 0
+ uint16_t nadd; // 0
+};
+struct dns_prop {
+ uint16_t type;
+ uint16_t class;
+};
+struct dns_entry { // element of known name, ip address and reversed ip address
+ struct dns_entry *next;
+ char ip[IP_STRING_LEN]; // dotted decimal IP
+ char rip[IP_STRING_LEN]; // length decimal reversed IP
+ char name[MAX_HOST_LEN];
+};
+
+static struct dns_entry *dnsentry = NULL;
+static uint32_t ttl = DEFAULT_TTL;
+
+/*
+ * Convert host name from C-string to dns length/string.
+ */
+static void convname(char *a, uint8_t *q)
+{
+ int i = (q[0] == '.') ? 0 : 1;
+ for (; i < MAX_HOST_LEN-1 && *q; i++, q++)
+ a[i] = tolower(*q);
+ a[0] = i - 1;
+ a[i] = 0;
+}
+
+/*
+ * Insert length of substrings instead of dots
+ */
+static void undot(uint8_t * rip)
+{
+ int i = 0, s = 0;
+ while (rip[i])
+ i++;
+ for (--i; i >= 0; i--) {
+ if (rip[i] == '.') {
+ rip[i] = s;
+ s = 0;
+ } else s++;
+ }
+}
+
+/*
+ * Read one line of hostname/IP from file
+ * Returns 0 for each valid entry read, -1 at EOF
+ * Assumes all host names are lower case only
+ * Hostnames with more than one label are not handled correctly.
+ * Presently the dot is copied into name without
+ * converting to a length/string substring for that label.
+ */
+
+static int getfileentry(FILE * fp, struct dns_entry *s)
+{
+ unsigned int a,b,c,d;
+ char *line, *r, *name;
+
+ restart:
+ line = r = xmalloc_fgets(fp);
+ if (!r)
+ return -1;
+ while (*r == ' ' || *r == '\t') {
+ r++;
+ if (!*r || *r == '#' || *r == '\n') {
+ free(line);
+ goto restart; /* skipping empty/blank and commented lines */
+ }
+ }
+ name = r;
+ while (*r != ' ' && *r != '\t')
+ r++;
+ *r++ = '\0';
+ if (sscanf(r, "%u.%u.%u.%u", &a, &b, &c, &d) != 4) {
+ free(line);
+ goto restart; /* skipping wrong lines */
+ }
+
+ sprintf(s->ip, "%u.%u.%u.%u", a, b, c, d);
+ sprintf(s->rip, ".%u.%u.%u.%u", d, c, b, a);
+ undot((uint8_t*)s->rip);
+ convname(s->name, (uint8_t*)name);
+
+ if (OPT_verbose)
+ fprintf(stderr, "\tname:%s, ip:%s\n", &(s->name[1]),s->ip);
+
+ free(line);
+ return 0;
+}
+
+/*
+ * Read hostname/IP records from file
+ */
+static void dnsentryinit(void)
+{
+ FILE *fp;
+ struct dns_entry *m, *prev;
+
+ prev = dnsentry = NULL;
+ fp = xfopen(fileconf, "r");
+
+ while (1) {
+ m = xzalloc(sizeof(*m));
+ /*m->next = NULL;*/
+ if (getfileentry(fp, m))
+ break;
+
+ if (prev == NULL)
+ dnsentry = m;
+ else
+ prev->next = m;
+ prev = m;
+ }
+ fclose(fp);
+}
+
+/*
+ * Look query up in dns records and return answer if found
+ * qs is the query string, first byte the string length
+ */
+static int table_lookup(uint16_t type, uint8_t * as, uint8_t * qs)
+{
+ int i;
+ struct dns_entry *d=dnsentry;
+
+ do {
+#if DEBUG
+ char *p,*q;
+ q = (char *)&(qs[1]);
+ p = &(d->name[1]);
+ fprintf(stderr, "\n%s: %d/%d p:%s q:%s %d",
+ __FUNCTION__, (int)strlen(p), (int)(d->name[0]),
+ p, q, (int)strlen(q));
+#endif
+ if (type == REQ_A) { /* search by host name */
+ for (i = 1; i <= (int)(d->name[0]); i++)
+ if (tolower(qs[i]) != d->name[i])
+ break;
+ if (i > (int)(d->name[0])) {
+#if DEBUG
+ fprintf(stderr, " OK");
+#endif
+ strcpy((char *)as, d->ip);
+#if DEBUG
+ fprintf(stderr, " as:%s\n", as);
+#endif
+ return 0;
+ }
+ } else
+ if (type == REQ_PTR) { /* search by IP-address */
+ if (!strncmp((char*)&d->rip[1], (char*)&qs[1], strlen(d->rip)-1)) {
+ strcpy((char *)as, d->name);
+ return 0;
+ }
+ }
+ d = d->next;
+ } while (d);
+ return -1;
+}
+
+
+/*
+ * Decode message and generate answer
+ */
+#define eret(s) do { fputs(s, stderr); return -1; } while (0)
+static int process_packet(uint8_t * buf)
+{
+ struct dns_head *head;
+ struct dns_prop *qprop;
+ struct dns_repl outr;
+ void *next, *from, *answb;
+
+ uint8_t answstr[MAX_NAME_LEN + 1];
+ int lookup_result, type, len, packet_len;
+ uint16_t flags;
+
+ answstr[0] = '\0';
+
+ head = (struct dns_head *)buf;
+ if (head->nquer == 0)
+ eret("no queries\n");
+
+ if (head->flags & 0x8000)
+ eret("ignoring response packet\n");
+
+ from = (void *)&head[1]; // start of query string
+ next = answb = from + strlen((char *)from) + 1 + sizeof(struct dns_prop); // where to append answer block
+
+ outr.rlen = 0; // may change later
+ outr.r = NULL;
+ outr.flags = 0;
+
+ qprop = (struct dns_prop *)(answb - 4);
+ type = ntohs(qprop->type);
+
+ // only let REQ_A and REQ_PTR pass
+ if (!(type == REQ_A || type == REQ_PTR)) {
+ goto empty_packet; /* we can't handle the query type */
+ }
+
+ if (ntohs(qprop->class) != 1 /* class INET */ ) {
+ outr.flags = 4; /* not supported */
+ goto empty_packet;
+ }
+ /* we only support standard queries */
+
+ if ((ntohs(head->flags) & 0x7800) != 0)
+ goto empty_packet;
+
+ // We have a standard query
+ bb_info_msg("%s", (char *)from);
+ lookup_result = table_lookup(type, answstr, (uint8_t*)from);
+ if (lookup_result != 0) {
+ outr.flags = 3 | 0x0400; //name do not exist and auth
+ goto empty_packet;
+ }
+ if (type == REQ_A) { // return an address
+ struct in_addr a;
+ if (!inet_aton((char*)answstr, &a)) {//dotted dec to long conv
+ outr.flags = 1; /* Frmt err */
+ goto empty_packet;
+ }
+ memcpy(answstr, &a.s_addr, 4); // save before a disappears
+ outr.rlen = 4; // uint32_t IP
+ }
+ else
+ outr.rlen = strlen((char *)answstr) + 1; // a host name
+ outr.r = answstr; // 32 bit ip or a host name
+ outr.flags |= 0x0400; /* authority-bit */
+ // we have an answer
+ head->nansw = htons(1);
+
+ // copy query block to answer block
+ len = answb - from;
+ memcpy(answb, from, len);
+ next += len;
+
+ // and append answer rr
+ *(uint32_t *) next = htonl(ttl);
+ next += 4;
+ *(uint16_t *) next = htons(outr.rlen);
+ next += 2;
+ memcpy(next, (void *)answstr, outr.rlen);
+ next += outr.rlen;
+
+ empty_packet:
+
+ flags = ntohs(head->flags);
+ // clear rcode and RA, set responsebit and our new flags
+ flags |= (outr.flags & 0xff80) | 0x8000;
+ head->flags = htons(flags);
+ head->nauth = head->nadd = htons(0);
+ head->nquer = htons(1);
+
+ packet_len = next - (void *)buf;
+ return packet_len;
+}
+
+/*
+ * Exit on signal
+ */
+static void interrupt(int x)
+{
+ unlink(LOCK_FILE);
+ bb_error_msg("interrupt, exiting\n");
+ exit(2);
+}
+
+int dnsd_main(int argc, char **argv);
+int dnsd_main(int argc, char **argv)
+{
+ char *listen_interface = NULL;
+ char *sttl, *sport;
+ len_and_sockaddr *lsa;
+ int udps;
+ uint16_t port = 53;
+ uint8_t buf[MAX_PACK_LEN];
+
+ getopt32(argc, argv, "i:c:t:p:dv", &listen_interface, &fileconf, &sttl, &sport);
+ //if (option_mask32 & 0x1) // -i
+ //if (option_mask32 & 0x2) // -c
+ if (option_mask32 & 0x4) // -t
+ ttl = xatou_range(sttl, 1, 0xffffffff);
+ if (option_mask32 & 0x8) // -p
+ port = xatou_range(sttl, 1, 0xffff);
+
+ if (OPT_verbose) {
+ bb_info_msg("listen_interface: %s", listen_interface);
+ bb_info_msg("ttl: %d, port: %d", ttl, port);
+ bb_info_msg("fileconf: %s", fileconf);
+ }
+
+ if (OPT_daemon) {
+//FIXME: NOMMU will NOT set LOGMODE_SYSLOG!
+#ifdef BB_NOMMU
+ /* reexec for vfork() do continue parent */
+ vfork_daemon_rexec(1, 0, argc, argv, "-d");
+#else
+ xdaemon(1, 0);
+#endif
+ logmode = LOGMODE_SYSLOG;
+ }
+
+ dnsentryinit();
+
+ signal(SIGINT, interrupt);
+ signal(SIGPIPE, SIG_IGN);
+ signal(SIGHUP, SIG_IGN);
+#ifdef SIGTSTP
+ signal(SIGTSTP, SIG_IGN);
+#endif
+#ifdef SIGURG
+ signal(SIGURG, SIG_IGN);
+#endif
+
+ lsa = xhost2sockaddr(listen_interface, port);
+ udps = xsocket(lsa->sa.sa_family, SOCK_DGRAM, 0);
+ xbind(udps, &lsa->sa, lsa->len);
+ // xlisten(udps, 50); - ?!! DGRAM sockets are never listened on I think?
+ bb_info_msg("Accepting UDP packets on %s",
+ xmalloc_sockaddr2dotted(&lsa->sa, lsa->len));
+
+ while (1) {
+ fd_set fdset;
+ int r;
+
+ FD_ZERO(&fdset);
+ FD_SET(udps, &fdset);
+ // Block until a message arrives
+// FIXME: Fantastic. select'ing on just one fd??
+// Why no just block on it doing recvfrom() ?
+ r = select(udps + 1, &fdset, NULL, NULL, NULL);
+ if (r < 0)
+ bb_perror_msg_and_die("select error");
+ if (r == 0)
+ bb_perror_msg_and_die("select spurious return");
+
+ /* Can this test ever be false? - yes */
+ if (FD_ISSET(udps, &fdset)) {
+ socklen_t fromlen = lsa->len;
+// FIXME: need to get *DEST* address (to which of our addresses
+// this query was directed), and reply from the same address.
+// Or else we can exhibit usual UDP ugliness:
+// [ip1.multihomed.ip2] <= query to ip1 <= peer
+// [ip1.multihomed.ip2] => reply from ip2 => peer (confused)
+ r = recvfrom(udps, buf, sizeof(buf), 0, &lsa->sa, &fromlen);
+ if (OPT_verbose)
+ bb_info_msg("Got UDP packet");
+
+ if (r < 12 || r > 512) {
+ bb_error_msg("invalid packet size");
+ continue;
+ }
+ if (r <= 0)
+ continue;
+ r = process_packet(buf);
+ if (r <= 0)
+ continue;
+ sendto(udps, buf, r, 0, &lsa->sa, fromlen);
+ }
+ }
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/ether-wake.c b/i/pc104/initrd/conf/busybox/networking/ether-wake.c
new file mode 100644
index 0000000..73f693e
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/ether-wake.c
@@ -0,0 +1,284 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ether-wake.c - Send a magic packet to wake up sleeping machines.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ *
+ * Author: Donald Becker, http://www.scyld.com/"; http://www.scyld.com/wakeonlan.html
+ * Busybox port: Christian Volkmann <haveaniceday@online.de>
+ * Used version of ether-wake.c: v1.09 11/12/2003 Donald Becker, http://www.scyld.com/";
+ */
+
+/* full usage according Donald Becker
+ * usage: ether-wake [-i <ifname>] [-p aa:bb:cc:dd[:ee:ff]] 00:11:22:33:44:55\n"
+ *
+ * This program generates and transmits a Wake-On-LAN (WOL)\n"
+ * \"Magic Packet\", used for restarting machines that have been\n"
+ * soft-powered-down (ACPI D3-warm state).\n"
+ * It currently generates the standard AMD Magic Packet format, with\n"
+ * an optional password appended.\n"
+ *
+ * The single required parameter is the Ethernet MAC (station) address\n"
+ * of the machine to wake or a host ID with known NSS 'ethers' entry.\n"
+ * The MAC address may be found with the 'arp' program while the target\n"
+ * machine is awake.\n"
+ *
+ * Options:\n"
+ * -b Send wake-up packet to the broadcast address.\n"
+ * -D Increase the debug level.\n"
+ * -i ifname Use interface IFNAME instead of the default 'eth0'.\n"
+ * -p <pw> Append the four or six byte password PW to the packet.\n"
+ * A password is only required for a few adapter types.\n"
+ * The password may be specified in ethernet hex format\n"
+ * or dotted decimal (Internet address)\n"
+ * -p 00:22:44:66:88:aa\n"
+ * -p 192.168.1.1\n";
+ *
+ *
+ * This program generates and transmits a Wake-On-LAN (WOL) "Magic Packet",
+ * used for restarting machines that have been soft-powered-down
+ * (ACPI D3-warm state). It currently generates the standard AMD Magic Packet
+ * format, with an optional password appended.
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU Public License, incorporated herein by reference.
+ * Contact the author for use under other terms.
+ *
+ * This source file was originally part of the network tricks package, and
+ * is now distributed to support the Scyld Beowulf system.
+ * Copyright 1999-2003 Donald Becker and Scyld Computing Corporation.
+ *
+ * The author may be reached as becker@scyld, or C/O
+ * Scyld Computing Corporation
+ * 914 Bay Ridge Road, Suite 220
+ * Annapolis MD 21403
+ *
+ * Notes:
+ * On some systems dropping root capability allows the process to be
+ * dumped, traced or debugged.
+ * If someone traces this program, they get control of a raw socket.
+ * Linux handles this safely, but beware when porting this program.
+ *
+ * An alternative to needing 'root' is using a UDP broadcast socket, however
+ * doing so only works with adapters configured for unicast+broadcast Rx
+ * filter. That configuration consumes more power.
+*/
+
+
+#include <netpacket/packet.h>
+#include <net/ethernet.h>
+#include <netinet/ether.h>
+#include <linux/if.h>
+
+#include "busybox.h"
+
+/* Note: PF_INET, SOCK_DGRAM, IPPROTO_UDP would allow SIOCGIFHWADDR to
+ * work as non-root, but we need SOCK_PACKET to specify the Ethernet
+ * destination address.
+ */
+#ifdef PF_PACKET
+# define whereto_t sockaddr_ll
+# define make_socket() xsocket(PF_PACKET, SOCK_RAW, 0)
+#else
+# define whereto_t sockaddr
+# define make_socket() xsocket(AF_INET, SOCK_PACKET, SOCK_PACKET)
+#endif
+
+#ifdef DEBUG
+# define bb_debug_msg(fmt, args...) fprintf(stderr, fmt, ## args)
+void bb_debug_dump_packet(unsigned char *outpack, int pktsize)
+{
+ int i;
+ printf("packet dump:\n");
+ for (i = 0; i < pktsize; ++i) {
+ printf("%2.2x ", outpack[i]);
+ if (i % 20 == 19) puts("");
+ }
+ printf("\n\n");
+}
+#else
+# define bb_debug_msg(fmt, args...)
+# define bb_debug_dump_packet(outpack, pktsize)
+#endif
+
+static inline void get_dest_addr(const char *arg, struct ether_addr *eaddr);
+static inline int get_fill(unsigned char *pkt, struct ether_addr *eaddr, int broadcast);
+static inline int get_wol_pw(const char *ethoptarg, unsigned char *wol_passwd);
+
+int ether_wake_main(int argc, char *argv[]);
+int ether_wake_main(int argc, char *argv[])
+{
+ const char *ifname = "eth0";
+ char *pass = NULL;
+ unsigned long flags;
+ unsigned char wol_passwd[6];
+ int wol_passwd_sz = 0;
+
+ int s; /* Raw socket */
+ int pktsize;
+ unsigned char outpack[1000];
+
+ struct ether_addr eaddr;
+ struct whereto_t whereto; /* who to wake up */
+
+ /* handle misc user options */
+ flags = getopt32(argc, argv, "bi:p:", &ifname, &pass);
+ if (optind == argc)
+ bb_show_usage();
+ if (pass)
+ wol_passwd_sz = get_wol_pw(pass, wol_passwd);
+
+ /* create the raw socket */
+ s = make_socket();
+
+ /* now that we have a raw socket we can drop root */
+ xsetuid(getuid());
+
+ /* look up the dest mac address */
+ get_dest_addr(argv[optind], &eaddr);
+
+ /* fill out the header of the packet */
+ pktsize = get_fill(outpack, &eaddr, flags /*& 1 [OPT_BROADCAST]*/);
+
+ bb_debug_dump_packet(outpack, pktsize);
+
+ /* Fill in the source address, if possible. */
+#ifdef __linux__
+ {
+ struct ifreq if_hwaddr;
+
+ strncpy(if_hwaddr.ifr_name, ifname, sizeof(if_hwaddr.ifr_name));
+ if (ioctl(s, SIOCGIFHWADDR, &if_hwaddr) < 0)
+ bb_perror_msg_and_die("SIOCGIFHWADDR on %s failed", ifname);
+
+ memcpy(outpack+6, if_hwaddr.ifr_hwaddr.sa_data, 6);
+
+# ifdef DEBUG
+ {
+ unsigned char *hwaddr = if_hwaddr.ifr_hwaddr.sa_data;
+ printf("The hardware address (SIOCGIFHWADDR) of %s is type %d "
+ "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x\n\n", ifname,
+ if_hwaddr.ifr_hwaddr.sa_family, hwaddr[0], hwaddr[1],
+ hwaddr[2], hwaddr[3], hwaddr[4], hwaddr[5]);
+ }
+# endif
+ }
+#endif /* __linux__ */
+
+ bb_debug_dump_packet(outpack, pktsize);
+
+ /* append the password if specified */
+ if (wol_passwd_sz > 0) {
+ memcpy(outpack+pktsize, wol_passwd, wol_passwd_sz);
+ pktsize += wol_passwd_sz;
+ }
+
+ bb_debug_dump_packet(outpack, pktsize);
+
+ /* This is necessary for broadcasts to work */
+ if (flags /*& 1 [OPT_BROADCAST]*/) {
+ if (setsockopt_broadcast(s) < 0)
+ bb_perror_msg("SO_BROADCAST");
+ }
+
+#if defined(PF_PACKET)
+ {
+ struct ifreq ifr;
+ strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ if (ioctl(s, SIOCGIFINDEX, &ifr) == -1)
+ bb_perror_msg_and_die("SIOCGIFINDEX");
+ memset(&whereto, 0, sizeof(whereto));
+ whereto.sll_family = AF_PACKET;
+ whereto.sll_ifindex = ifr.ifr_ifindex;
+ /* The manual page incorrectly claims the address must be filled.
+ We do so because the code may change to match the docs. */
+ whereto.sll_halen = ETH_ALEN;
+ memcpy(whereto.sll_addr, outpack, ETH_ALEN);
+ }
+#else
+ whereto.sa_family = 0;
+ strcpy(whereto.sa_data, ifname);
+#endif
+
+ if (sendto(s, outpack, pktsize, 0, (struct sockaddr *)&whereto, sizeof(whereto)) < 0)
+ bb_perror_msg(bb_msg_write_error);
+
+ close(s);
+
+ return EXIT_SUCCESS;
+}
+
+/* Convert the host ID string to a MAC address.
+ * The string may be a:
+ * Host name
+ * IP address string
+ * MAC address string
+*/
+static inline void get_dest_addr(const char *hostid, struct ether_addr *eaddr)
+{
+ struct ether_addr *eap;
+
+ eap = ether_aton(hostid);
+ if (eap) {
+ *eaddr = *eap;
+ bb_debug_msg("The target station address is %s\n\n", ether_ntoa(eaddr));
+#if !defined(__UCLIBC__)
+ } else if (ether_hostton(hostid, eaddr) == 0) {
+ bb_debug_msg("Station address for hostname %s is %s\n\n", hostid, ether_ntoa(eaddr));
+#endif
+ } else
+ bb_show_usage();
+}
+
+static inline int get_fill(unsigned char *pkt, struct ether_addr *eaddr, int broadcast)
+{
+ int offset, i;
+ unsigned char *station_addr = eaddr->ether_addr_octet;
+
+ if (broadcast)
+ memset(pkt+0, 0xff, 6);
+ else
+ memcpy(pkt, station_addr, 6);
+ memcpy(pkt+6, station_addr, 6);
+ pkt[12] = 0x08; /* Or 0x0806 for ARP, 0x8035 for RARP */
+ pkt[13] = 0x42;
+ offset = 14;
+
+ memset(pkt+offset, 0xff, 6);
+ offset += 6;
+
+ for (i = 0; i < 16; ++i) {
+ memcpy(pkt+offset, station_addr, 6);
+ offset += 6;
+ }
+
+ return offset;
+}
+
+static inline int get_wol_pw(const char *ethoptarg, unsigned char *wol_passwd)
+{
+ int passwd[6];
+ int byte_cnt, i;
+
+ /* handle MAC format */
+ byte_cnt = sscanf(ethoptarg, "%2x:%2x:%2x:%2x:%2x:%2x",
+ &passwd[0], &passwd[1], &passwd[2],
+ &passwd[3], &passwd[4], &passwd[5]);
+ /* handle IP format */
+ if (byte_cnt < 4)
+ byte_cnt = sscanf(ethoptarg, "%d.%d.%d.%d",
+ &passwd[0], &passwd[1], &passwd[2], &passwd[3]);
+ if (byte_cnt < 4) {
+ bb_error_msg("cannot read Wake-On-LAN pass");
+ return 0;
+ }
+
+ for (i = 0; i < byte_cnt; ++i)
+ wol_passwd[i] = passwd[i];
+
+ bb_debug_msg("password: %2.2x %2.2x %2.2x %2.2x (%d)\n\n",
+ wol_passwd[0], wol_passwd[1], wol_passwd[2], wol_passwd[3],
+ byte_cnt);
+
+ return byte_cnt;
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/ftpgetput.c b/i/pc104/initrd/conf/busybox/networking/ftpgetput.c
new file mode 100644
index 0000000..9b0510d
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/ftpgetput.c
@@ -0,0 +1,359 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ftpget
+ *
+ * Mini implementation of FTP to retrieve a remote file.
+ *
+ * Copyright (C) 2002 Jeff Angielski, The PTR Group <jeff@theptrgroup.com>
+ * Copyright (C) 2002 Glenn McGrath <bug1@iinet.net.au>
+ *
+ * Based on wget.c by Chip Rosenthal Covad Communications
+ * <chip@laserlink.net>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "busybox.h"
+#include <getopt.h>
+
+typedef struct ftp_host_info_s {
+ const char *user;
+ const char *password;
+ struct len_and_sockaddr *lsa;
+} ftp_host_info_t;
+
+static smallint verbose_flag;
+static smallint do_continue;
+
+static void ftp_die(const char *msg, const char *remote) ATTRIBUTE_NORETURN;
+static void ftp_die(const char *msg, const char *remote)
+{
+ /* Guard against garbage from remote server */
+ const char *cp = remote;
+ while (*cp >= ' ' && *cp < '\x7f') cp++;
+ bb_error_msg_and_die("unexpected server response%s%s: %.*s",
+ msg ? " to " : "", msg ? msg : "",
+ (int)(cp - remote), remote);
+}
+
+
+static int ftpcmd(const char *s1, const char *s2, FILE *stream, char *buf)
+{
+ unsigned n;
+ if (verbose_flag) {
+ bb_error_msg("cmd %s %s", s1, s2);
+ }
+
+ if (s1) {
+ if (s2) {
+ fprintf(stream, "%s %s\r\n", s1, s2);
+ } else {
+ fprintf(stream, "%s\r\n", s1);
+ }
+ }
+ do {
+ char *buf_ptr;
+
+ if (fgets(buf, 510, stream) == NULL) {
+ bb_perror_msg_and_die("fgets");
+ }
+ buf_ptr = strstr(buf, "\r\n");
+ if (buf_ptr) {
+ *buf_ptr = '\0';
+ }
+ } while (!isdigit(buf[0]) || buf[3] != ' ');
+
+ buf[3] = '\0';
+ n = xatou(buf);
+ buf[3] = ' ';
+ return n;
+}
+
+static int xconnect_ftpdata(ftp_host_info_t *server, char *buf)
+{
+ char *buf_ptr;
+ unsigned short port_num;
+
+ /* Response is "NNN garbageN1,N2,N3,N4,P1,P2[)garbage]
+ * Server's IP is N1.N2.N3.N4 (we ignore it)
+ * Server's port for data connection is P1*256+P2 */
+ buf_ptr = strrchr(buf, ')');
+ if (buf_ptr) *buf_ptr = '\0';
+
+ buf_ptr = strrchr(buf, ',');
+ *buf_ptr = '\0';
+ port_num = xatoul_range(buf_ptr + 1, 0, 255);
+
+ buf_ptr = strrchr(buf, ',');
+ *buf_ptr = '\0';
+ port_num += xatoul_range(buf_ptr + 1, 0, 255) * 256;
+
+ set_nport(server->lsa, htons(port_num));
+ return xconnect_stream(server->lsa);
+}
+
+static FILE *ftp_login(ftp_host_info_t *server)
+{
+ FILE *control_stream;
+ char buf[512];
+
+ /* Connect to the command socket */
+ control_stream = fdopen(xconnect_stream(server->lsa), "r+");
+ if (control_stream == NULL) {
+ /* fdopen failed - extremely unlikely */
+ bb_perror_nomsg_and_die();
+ }
+
+ if (ftpcmd(NULL, NULL, control_stream, buf) != 220) {
+ ftp_die(NULL, buf);
+ }
+
+ /* Login to the server */
+ switch (ftpcmd("USER", server->user, control_stream, buf)) {
+ case 230:
+ break;
+ case 331:
+ if (ftpcmd("PASS", server->password, control_stream, buf) != 230) {
+ ftp_die("PASS", buf);
+ }
+ break;
+ default:
+ ftp_die("USER", buf);
+ }
+
+ ftpcmd("TYPE I", NULL, control_stream, buf);
+
+ return control_stream;
+}
+
+#if !ENABLE_FTPGET
+int ftp_receive(ftp_host_info_t *server, FILE *control_stream,
+ const char *local_path, char *server_path);
+#else
+static
+int ftp_receive(ftp_host_info_t *server, FILE *control_stream,
+ const char *local_path, char *server_path)
+{
+ char buf[512];
+/* I think 'filesize' usage here is bogus. Let's see... */
+ //off_t filesize = -1;
+#define filesize ((off_t)-1)
+ int fd_data;
+ int fd_local = -1;
+ off_t beg_range = 0;
+
+ /* Connect to the data socket */
+ if (ftpcmd("PASV", NULL, control_stream, buf) != 227) {
+ ftp_die("PASV", buf);
+ }
+ fd_data = xconnect_ftpdata(server, buf);
+
+ if (ftpcmd("SIZE", server_path, control_stream, buf) == 213) {
+ //filesize = BB_STRTOOFF(buf + 4, NULL, 10);
+ //if (errno || filesize < 0)
+ // ftp_die("SIZE", buf);
+ } else {
+ do_continue = 0;
+ }
+
+ if (LONE_DASH(local_path)) {
+ fd_local = STDOUT_FILENO;
+ do_continue = 0;
+ }
+
+ if (do_continue) {
+ struct stat sbuf;
+ if (lstat(local_path, &sbuf) < 0) {
+ bb_perror_msg_and_die("lstat");
+ }
+ if (sbuf.st_size > 0) {
+ beg_range = sbuf.st_size;
+ } else {
+ do_continue = 0;
+ }
+ }
+
+ if (do_continue) {
+ sprintf(buf, "REST %"OFF_FMT"d", beg_range);
+ if (ftpcmd(buf, NULL, control_stream, buf) != 350) {
+ do_continue = 0;
+ } else {
+ //if (filesize != -1)
+ // filesize -= beg_range;
+ }
+ }
+
+ if (ftpcmd("RETR", server_path, control_stream, buf) > 150) {
+ ftp_die("RETR", buf);
+ }
+
+ /* only make a local file if we know that one exists on the remote server */
+ if (fd_local == -1) {
+ if (do_continue) {
+ fd_local = xopen(local_path, O_APPEND | O_WRONLY);
+ } else {
+ fd_local = xopen(local_path, O_CREAT | O_TRUNC | O_WRONLY);
+ }
+ }
+
+ /* Copy the file */
+ if (filesize != -1) {
+ if (bb_copyfd_size(fd_data, fd_local, filesize) == -1)
+ return EXIT_FAILURE;
+ } else {
+ if (bb_copyfd_eof(fd_data, fd_local) == -1)
+ return EXIT_FAILURE;
+ }
+
+ /* close it all down */
+ close(fd_data);
+ if (ftpcmd(NULL, NULL, control_stream, buf) != 226) {
+ ftp_die(NULL, buf);
+ }
+ ftpcmd("QUIT", NULL, control_stream, buf);
+
+ return EXIT_SUCCESS;
+}
+#endif
+
+#if !ENABLE_FTPPUT
+int ftp_send(ftp_host_info_t *server, FILE *control_stream,
+ const char *server_path, char *local_path);
+#else
+static
+int ftp_send(ftp_host_info_t *server, FILE *control_stream,
+ const char *server_path, char *local_path)
+{
+ struct stat sbuf;
+ char buf[512];
+ int fd_data;
+ int fd_local;
+ int response;
+
+ /* Connect to the data socket */
+ if (ftpcmd("PASV", NULL, control_stream, buf) != 227) {
+ ftp_die("PASV", buf);
+ }
+ fd_data = xconnect_ftpdata(server, buf);
+
+ /* get the local file */
+ fd_local = STDIN_FILENO;
+ if (NOT_LONE_DASH(local_path)) {
+ fd_local = xopen(local_path, O_RDONLY);
+ fstat(fd_local, &sbuf);
+
+ sprintf(buf, "ALLO %"OFF_FMT"u", sbuf.st_size);
+ response = ftpcmd(buf, NULL, control_stream, buf);
+ switch (response) {
+ case 200:
+ case 202:
+ break;
+ default:
+ close(fd_local);
+ ftp_die("ALLO", buf);
+ break;
+ }
+ }
+ response = ftpcmd("STOR", server_path, control_stream, buf);
+ switch (response) {
+ case 125:
+ case 150:
+ break;
+ default:
+ close(fd_local);
+ ftp_die("STOR", buf);
+ }
+
+ /* transfer the file */
+ if (bb_copyfd_eof(fd_local, fd_data) == -1) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* close it all down */
+ close(fd_data);
+ if (ftpcmd(NULL, NULL, control_stream, buf) != 226) {
+ ftp_die("close", buf);
+ }
+ ftpcmd("QUIT", NULL, control_stream, buf);
+
+ return EXIT_SUCCESS;
+}
+#endif
+
+#define FTPGETPUT_OPT_CONTINUE 1
+#define FTPGETPUT_OPT_VERBOSE 2
+#define FTPGETPUT_OPT_USER 4
+#define FTPGETPUT_OPT_PASSWORD 8
+#define FTPGETPUT_OPT_PORT 16
+
+#if ENABLE_FEATURE_FTPGETPUT_LONG_OPTIONS
+static const struct option ftpgetput_long_options[] = {
+ { "continue", 1, NULL, 'c' },
+ { "verbose", 0, NULL, 'v' },
+ { "username", 1, NULL, 'u' },
+ { "password", 1, NULL, 'p' },
+ { "port", 1, NULL, 'P' },
+ { 0, 0, 0, 0 }
+};
+#endif
+
+int ftpgetput_main(int argc, char **argv);
+int ftpgetput_main(int argc, char **argv)
+{
+ /* content-length of the file */
+ unsigned opt;
+ const char *port = "ftp";
+ /* socket to ftp server */
+ FILE *control_stream;
+ /* continue previous transfer (-c) */
+ ftp_host_info_t *server;
+
+#if ENABLE_FTPPUT && !ENABLE_FTPGET
+# define ftp_action ftp_send
+#elif ENABLE_FTPGET && !ENABLE_FTPPUT
+# define ftp_action ftp_receive
+#else
+ int (*ftp_action)(ftp_host_info_t *, FILE *, const char *, char *) = ftp_send;
+ /* Check to see if the command is ftpget or ftput */
+ if (applet_name[3] == 'g') {
+ ftp_action = ftp_receive;
+ }
+#endif
+
+ /* Set default values */
+ server = xmalloc(sizeof(*server));
+ server->user = "anonymous";
+ server->password = "busybox@";
+
+ /*
+ * Decipher the command line
+ */
+#if ENABLE_FEATURE_FTPGETPUT_LONG_OPTIONS
+ applet_long_options = ftpgetput_long_options;
+#endif
+ opt_complementary = "=3"; /* must have 3 params */
+ opt = getopt32(argc, argv, "cvu:p:P:", &server->user, &server->password, &port);
+ argv += optind;
+
+ /* Process the non-option command line arguments */
+ if (opt & FTPGETPUT_OPT_CONTINUE) {
+ do_continue = 1;
+ }
+ if (opt & FTPGETPUT_OPT_VERBOSE) {
+ verbose_flag = 1;
+ }
+
+ /* We want to do exactly _one_ DNS lookup, since some
+ * sites (i.e. ftp.us.debian.org) use round-robin DNS
+ * and we want to connect to only one IP... */
+ server->lsa = xhost2sockaddr(argv[0], bb_lookup_port(port, "tcp", 21));
+ if (verbose_flag) {
+ printf("Connecting to %s (%s)\n", argv[0],
+ xmalloc_sockaddr2dotted(&server->lsa->sa, server->lsa->len));
+ }
+
+ /* Connect/Setup/Configure the FTP session */
+ control_stream = ftp_login(server);
+
+ return ftp_action(server, control_stream, argv[1], argv[2]);
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/hostname.c b/i/pc104/initrd/conf/busybox/networking/hostname.c
new file mode 100644
index 0000000..3f6a658
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/hostname.c
@@ -0,0 +1,101 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini hostname implementation for busybox
+ *
+ * Copyright (C) 1999 by Randolph Chung <tausq@debian.org>
+ *
+ * adjusted by Erik Andersen <andersen@codepoet.org> to remove
+ * use of long options and GNU getopt. Improved the usage info.
+ *
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "busybox.h"
+
+static void do_sethostname(char *s, int isfile)
+{
+ FILE *f;
+
+ if (!s)
+ return;
+ if (!isfile) {
+ if (sethostname(s, strlen(s)) < 0) {
+ if (errno == EPERM)
+ bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
+ else
+ bb_perror_msg_and_die("sethostname");
+ }
+ } else {
+ f = xfopen(s, "r");
+ while (fgets(bb_common_bufsiz1, sizeof(bb_common_bufsiz1), f) != NULL) {
+ if (bb_common_bufsiz1[0] == '#') {
+ continue;
+ }
+ chomp(bb_common_bufsiz1);
+ do_sethostname(bb_common_bufsiz1, 0);
+ }
+ if (ENABLE_FEATURE_CLEAN_UP)
+ fclose(f);
+ }
+}
+
+int hostname_main(int argc, char **argv);
+int hostname_main(int argc, char **argv)
+{
+ enum {
+ OPT_d = 0x1,
+ OPT_f = 0x2,
+ OPT_i = 0x4,
+ OPT_s = 0x8,
+ OPT_F = 0x10,
+ OPT_dfis = 0xf,
+ };
+
+ char buf[256];
+ char *hostname_str;
+
+ if (argc < 1)
+ bb_show_usage();
+
+ getopt32(argc, argv, "dfisF:", &hostname_str);
+
+ /* Output in desired format */
+ if (option_mask32 & OPT_dfis) {
+ struct hostent *hp;
+ char *p;
+ gethostname(buf, sizeof(buf));
+ hp = xgethostbyname(buf);
+ p = strchr(hp->h_name, '.');
+ if (option_mask32 & OPT_f) {
+ puts(hp->h_name);
+ } else if (option_mask32 & OPT_s) {
+ if (p != NULL) {
+ *p = '\0';
+ }
+ puts(hp->h_name);
+ } else if (option_mask32 & OPT_d) {
+ if (p)
+ puts(p + 1);
+ } else if (option_mask32 & OPT_i) {
+ while (hp->h_addr_list[0]) {
+ printf("%s ", inet_ntoa(*(struct in_addr *) (*hp->h_addr_list++)));
+ }
+ puts("");
+ }
+ }
+ /* Set the hostname */
+ else if (option_mask32 & OPT_F) {
+ do_sethostname(hostname_str, 1);
+ } else if (optind < argc) {
+ do_sethostname(argv[optind], 0);
+ }
+ /* Or if all else fails,
+ * just print the current hostname */
+ else {
+ gethostname(buf, sizeof(buf));
+ puts(buf);
+ }
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/httpd.c b/i/pc104/initrd/conf/busybox/networking/httpd.c
new file mode 100644
index 0000000..72b03de
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/httpd.c
@@ -0,0 +1,2049 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * httpd implementation for busybox
+ *
+ * Copyright (C) 2002,2003 Glenn Engel <glenne@engel.org>
+ * Copyright (C) 2003-2006 Vladimir Oleynik <dzo@simtreas.ru>
+ *
+ * simplify patch stolen from libbb without using strdup
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ *
+ *****************************************************************************
+ *
+ * Typical usage:
+ * for non root user
+ * httpd -p 8080 -h $HOME/public_html
+ * or for daemon start from rc script with uid=0:
+ * httpd -u www
+ * This is equivalent if www user have uid=80 to
+ * httpd -p 80 -u 80 -h /www -c /etc/httpd.conf -r "Web Server Authentication"
+ *
+ *
+ * When a url contains "cgi-bin" it is assumed to be a cgi script. The
+ * server changes directory to the location of the script and executes it
+ * after setting QUERY_STRING and other environment variables.
+ *
+ * Doc:
+ * "CGI Environment Variables": http://hoohoo.ncsa.uiuc.edu/cgi/env.html
+ *
+ * The server can also be invoked as a url arg decoder and html text encoder
+ * as follows:
+ * foo=`httpd -d $foo` # decode "Hello%20World" as "Hello World"
+ * bar=`httpd -e "<Hello World>"` # encode as "&#60Hello&#32World&#62"
+ * Note that url encoding for arguments is not the same as html encoding for
+ * presentation. -d decodes a url-encoded argument while -e encodes in html
+ * for page display.
+ *
+ * httpd.conf has the following format:
+ *
+ * A:172.20. # Allow address from 172.20.0.0/16
+ * A:10.0.0.0/25 # Allow any address from 10.0.0.0-10.0.0.127
+ * A:10.0.0.0/255.255.255.128 # Allow any address that previous set
+ * A:127.0.0.1 # Allow local loopback connections
+ * D:* # Deny from other IP connections
+ * /cgi-bin:foo:bar # Require user foo, pwd bar on urls starting with /cgi-bin/
+ * /adm:admin:setup # Require user admin, pwd setup on urls starting with /adm/
+ * /adm:toor:PaSsWd # or user toor, pwd PaSsWd on urls starting with /adm/
+ * .au:audio/basic # additional mime type for audio.au files
+ * *.php:/path/php # running cgi.php scripts through an interpreter
+ *
+ * A/D may be as a/d or allow/deny - first char case insensitive
+ * Deny IP rules take precedence over allow rules.
+ *
+ *
+ * The Deny/Allow IP logic:
+ *
+ * - Default is to allow all. No addresses are denied unless
+ * denied with a D: rule.
+ * - Order of Deny/Allow rules is significant
+ * - Deny rules take precedence over allow rules.
+ * - If a deny all rule (D:*) is used it acts as a catch-all for unmatched
+ * addresses.
+ * - Specification of Allow all (A:*) is a no-op
+ *
+ * Example:
+ * 1. Allow only specified addresses
+ * A:172.20 # Allow any address that begins with 172.20.
+ * A:10.10. # Allow any address that begins with 10.10.
+ * A:127.0.0.1 # Allow local loopback connections
+ * D:* # Deny from other IP connections
+ *
+ * 2. Only deny specified addresses
+ * D:1.2.3. # deny from 1.2.3.0 - 1.2.3.255
+ * D:2.3.4. # deny from 2.3.4.0 - 2.3.4.255
+ * A:* # (optional line added for clarity)
+ *
+ * If a sub directory contains a config file it is parsed and merged with
+ * any existing settings as if it was appended to the original configuration.
+ *
+ * subdir paths are relative to the containing subdir and thus cannot
+ * affect the parent rules.
+ *
+ * Note that since the sub dir is parsed in the forked thread servicing the
+ * subdir http request, any merge is discarded when the process exits. As a
+ * result, the subdir settings only have a lifetime of a single request.
+ *
+ *
+ * If -c is not set, an attempt will be made to open the default
+ * root configuration file. If -c is set and the file is not found, the
+ * server exits with an error.
+ *
+*/
+
+#include "busybox.h"
+
+/* amount of buffering in a pipe */
+#ifndef PIPE_BUF
+# define PIPE_BUF 4096
+#endif
+
+static const char httpdVersion[] = "busybox httpd/1.35 6-Oct-2004";
+static const char default_path_httpd_conf[] = "/etc";
+static const char httpd_conf[] = "httpd.conf";
+static const char home[] = "./";
+
+#define TIMEOUT 60
+
+// Note: busybox xfuncs are not used because we want the server to keep running
+// if something bad happens due to a malformed user request.
+// As a result, all memory allocation after daemonize
+// is checked rigorously
+
+//#define DEBUG 1
+#define DEBUG 0
+
+#define MAX_MEMORY_BUFF 8192 /* IO buffer */
+
+typedef struct HT_ACCESS {
+ char *after_colon;
+ struct HT_ACCESS *next;
+ char before_colon[1]; /* really bigger, must last */
+} Htaccess;
+
+typedef struct HT_ACCESS_IP {
+ unsigned int ip;
+ unsigned int mask;
+ int allow_deny;
+ struct HT_ACCESS_IP *next;
+} Htaccess_IP;
+
+typedef struct {
+ char buf[MAX_MEMORY_BUFF];
+
+ USE_FEATURE_HTTPD_BASIC_AUTH(const char *realm;)
+ USE_FEATURE_HTTPD_BASIC_AUTH(char *remoteuser;)
+
+ const char *query;
+
+ USE_FEATURE_HTTPD_CGI(char *referer;)
+
+ const char *configFile;
+
+ unsigned int rmt_ip;
+#if ENABLE_FEATURE_HTTPD_CGI || DEBUG
+ char *rmt_ip_str; /* for set env REMOTE_ADDR */
+#endif
+ unsigned port; /* server initial port and for
+ set env REMOTE_PORT */
+ const char *found_mime_type;
+ const char *found_moved_temporarily;
+
+ off_t ContentLength; /* -1 - unknown */
+ time_t last_mod;
+
+ Htaccess_IP *ip_a_d; /* config allow/deny lines */
+ int flg_deny_all;
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
+ Htaccess *auth; /* config user:password lines */
+#endif
+#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
+ Htaccess *mime_a; /* config mime types */
+#endif
+
+ int server_socket;
+ int accepted_socket;
+ volatile int alarm_signaled;
+
+#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
+ Htaccess *script_i; /* config script interpreters */
+#endif
+} HttpdConfig;
+
+static HttpdConfig *config;
+
+static const char request_GET[] = "GET"; /* size algorithmic optimize */
+
+static const char* const suffixTable [] = {
+/* Warning: shorted equivalent suffix in one line must be first */
+ ".htm.html", "text/html",
+ ".jpg.jpeg", "image/jpeg",
+ ".gif", "image/gif",
+ ".png", "image/png",
+ ".txt.h.c.cc.cpp", "text/plain",
+ ".css", "text/css",
+ ".wav", "audio/wav",
+ ".avi", "video/x-msvideo",
+ ".qt.mov", "video/quicktime",
+ ".mpe.mpeg", "video/mpeg",
+ ".mid.midi", "audio/midi",
+ ".mp3", "audio/mpeg",
+#if 0 /* unpopular */
+ ".au", "audio/basic",
+ ".pac", "application/x-ns-proxy-autoconfig",
+ ".vrml.wrl", "model/vrml",
+#endif
+ 0, "application/octet-stream" /* default */
+};
+
+typedef enum {
+ HTTP_OK = 200,
+ HTTP_MOVED_TEMPORARILY = 302,
+ HTTP_BAD_REQUEST = 400, /* malformed syntax */
+ HTTP_UNAUTHORIZED = 401, /* authentication needed, respond with auth hdr */
+ HTTP_NOT_FOUND = 404,
+ HTTP_FORBIDDEN = 403,
+ HTTP_REQUEST_TIMEOUT = 408,
+ HTTP_NOT_IMPLEMENTED = 501, /* used for unrecognized requests */
+ HTTP_INTERNAL_SERVER_ERROR = 500,
+#if 0 /* future use */
+ HTTP_CONTINUE = 100,
+ HTTP_SWITCHING_PROTOCOLS = 101,
+ HTTP_CREATED = 201,
+ HTTP_ACCEPTED = 202,
+ HTTP_NON_AUTHORITATIVE_INFO = 203,
+ HTTP_NO_CONTENT = 204,
+ HTTP_MULTIPLE_CHOICES = 300,
+ HTTP_MOVED_PERMANENTLY = 301,
+ HTTP_NOT_MODIFIED = 304,
+ HTTP_PAYMENT_REQUIRED = 402,
+ HTTP_BAD_GATEWAY = 502,
+ HTTP_SERVICE_UNAVAILABLE = 503, /* overload, maintenance */
+ HTTP_RESPONSE_SETSIZE = 0xffffffff
+#endif
+} HttpResponseNum;
+
+typedef struct {
+ HttpResponseNum type;
+ const char *name;
+ const char *info;
+} HttpEnumString;
+
+static const HttpEnumString httpResponseNames[] = {
+ { HTTP_OK, "OK", NULL },
+ { HTTP_MOVED_TEMPORARILY, "Found", "Directories must end with a slash." },
+ { HTTP_REQUEST_TIMEOUT, "Request Timeout",
+ "No request appeared within a reasonable time period." },
+ { HTTP_NOT_IMPLEMENTED, "Not Implemented",
+ "The requested method is not recognized by this server." },
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
+ { HTTP_UNAUTHORIZED, "Unauthorized", "" },
+#endif
+ { HTTP_NOT_FOUND, "Not Found",
+ "The requested URL was not found on this server." },
+ { HTTP_BAD_REQUEST, "Bad Request", "Unsupported method." },
+ { HTTP_FORBIDDEN, "Forbidden", "" },
+ { HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error",
+ "Internal Server Error" },
+#if 0 /* not implemented */
+ { HTTP_CREATED, "Created" },
+ { HTTP_ACCEPTED, "Accepted" },
+ { HTTP_NO_CONTENT, "No Content" },
+ { HTTP_MULTIPLE_CHOICES, "Multiple Choices" },
+ { HTTP_MOVED_PERMANENTLY, "Moved Permanently" },
+ { HTTP_NOT_MODIFIED, "Not Modified" },
+ { HTTP_BAD_GATEWAY, "Bad Gateway", "" },
+ { HTTP_SERVICE_UNAVAILABLE, "Service Unavailable", "" },
+#endif
+};
+
+
+static const char RFC1123FMT[] = "%a, %d %b %Y %H:%M:%S GMT";
+
+
+#define STRNCASECMP(a, str) strncasecmp((a), (str), sizeof(str)-1)
+
+
+static int scan_ip(const char **ep, unsigned int *ip, unsigned char endc)
+{
+ const char *p = *ep;
+ int auto_mask = 8;
+ int j;
+
+ *ip = 0;
+ for (j = 0; j < 4; j++) {
+ unsigned int octet;
+
+ if ((*p < '0' || *p > '9') && (*p != '/' || j == 0) && *p != 0)
+ return -auto_mask;
+ octet = 0;
+ while (*p >= '0' && *p <= '9') {
+ octet *= 10;
+ octet += *p - '0';
+ if (octet > 255)
+ return -auto_mask;
+ p++;
+ }
+ if (*p == '.')
+ p++;
+ if (*p != '/' && *p != 0)
+ auto_mask += 8;
+ *ip = ((*ip) << 8) | octet;
+ }
+ if (*p != 0) {
+ if (*p != endc)
+ return -auto_mask;
+ p++;
+ if (*p == 0)
+ return -auto_mask;
+ }
+ *ep = p;
+ return auto_mask;
+}
+
+static int scan_ip_mask(const char *ipm, unsigned int *ip, unsigned int *mask)
+{
+ int i;
+ unsigned int msk;
+
+ i = scan_ip(&ipm, ip, '/');
+ if (i < 0)
+ return i;
+ if (*ipm) {
+ const char *p = ipm;
+
+ i = 0;
+ while (*p) {
+ if (*p < '0' || *p > '9') {
+ if (*p == '.') {
+ i = scan_ip(&ipm, mask, 0);
+ return i != 32;
+ }
+ return -1;
+ }
+ i *= 10;
+ i += *p - '0';
+ p++;
+ }
+ }
+ if (i > 32 || i < 0)
+ return -1;
+ msk = 0x80000000;
+ *mask = 0;
+ while (i > 0) {
+ *mask |= msk;
+ msk >>= 1;
+ i--;
+ }
+ return 0;
+}
+
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH \
+ || ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES \
+ || ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
+static void free_config_lines(Htaccess **pprev)
+{
+ Htaccess *prev = *pprev;
+
+ while (prev) {
+ Htaccess *cur = prev;
+
+ prev = cur->next;
+ free(cur);
+ }
+ *pprev = NULL;
+}
+#endif
+
+/* flag */
+#define FIRST_PARSE 0
+#define SUBDIR_PARSE 1
+#define SIGNALED_PARSE 2
+#define FIND_FROM_HTTPD_ROOT 3
+/****************************************************************************
+ *
+ > $Function: parse_conf()
+ *
+ * $Description: parse configuration file into in-memory linked list.
+ *
+ * The first non-white character is examined to determine if the config line
+ * is one of the following:
+ * .ext:mime/type # new mime type not compiled into httpd
+ * [adAD]:from # ip address allow/deny, * for wildcard
+ * /path:user:pass # username/password
+ *
+ * Any previous IP rules are discarded.
+ * If the flag argument is not SUBDIR_PARSE then all /path and mime rules
+ * are also discarded. That is, previous settings are retained if flag is
+ * SUBDIR_PARSE.
+ *
+ * $Parameters:
+ * (const char *) path . . null for ip address checks, path for password
+ * checks.
+ * (int) flag . . . . . . the source of the parse request.
+ *
+ * $Return: (None)
+ *
+ ****************************************************************************/
+static void parse_conf(const char *path, int flag)
+{
+ FILE *f;
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
+ Htaccess *prev;
+#endif
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH \
+ || ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES \
+ || ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
+ Htaccess *cur;
+#endif
+
+ const char *cf = config->configFile;
+ char buf[160];
+ char *p0 = NULL;
+ char *c, *p;
+
+ /* free previous ip setup if present */
+ Htaccess_IP *pip = config->ip_a_d;
+
+ while (pip) {
+ Htaccess_IP *cur_ipl = pip;
+
+ pip = cur_ipl->next;
+ free(cur_ipl);
+ }
+ config->ip_a_d = NULL;
+
+ config->flg_deny_all = 0;
+
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH \
+ || ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES \
+ || ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
+ /* retain previous auth and mime config only for subdir parse */
+ if (flag != SUBDIR_PARSE) {
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
+ free_config_lines(&config->auth);
+#endif
+#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
+ free_config_lines(&config->mime_a);
+#endif
+#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
+ free_config_lines(&config->script_i);
+#endif
+ }
+#endif
+
+ if (flag == SUBDIR_PARSE || cf == NULL) {
+ cf = alloca(strlen(path) + sizeof(httpd_conf) + 2);
+ if (cf == NULL) {
+ if (flag == FIRST_PARSE)
+ bb_error_msg_and_die(bb_msg_memory_exhausted);
+ return;
+ }
+ sprintf((char *)cf, "%s/%s", path, httpd_conf);
+ }
+
+ while ((f = fopen(cf, "r")) == NULL) {
+ if (flag == SUBDIR_PARSE || flag == FIND_FROM_HTTPD_ROOT) {
+ /* config file not found, no changes to config */
+ return;
+ }
+ if (config->configFile && flag == FIRST_PARSE) /* if -c option given */
+ bb_perror_msg_and_die("%s", cf);
+ flag = FIND_FROM_HTTPD_ROOT;
+ cf = httpd_conf;
+ }
+
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
+ prev = config->auth;
+#endif
+ /* This could stand some work */
+ while ((p0 = fgets(buf, sizeof(buf), f)) != NULL) {
+ c = NULL;
+ for (p = p0; *p0 != 0 && *p0 != '#'; p0++) {
+ if (!isspace(*p0)) {
+ *p++ = *p0;
+ if (*p0 == ':' && c == NULL)
+ c = p;
+ }
+ }
+ *p = 0;
+
+ /* test for empty or strange line */
+ if (c == NULL || *c == 0)
+ continue;
+ p0 = buf;
+ if (*p0 == 'd')
+ *p0 = 'D';
+ if (*c == '*') {
+ if (*p0 == 'D') {
+ /* memorize deny all */
+ config->flg_deny_all++;
+ }
+ /* skip default other "word:*" config lines */
+ continue;
+ }
+
+ if (*p0 == 'a')
+ *p0 = 'A';
+ else if (*p0 != 'D' && *p0 != 'A'
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
+ && *p0 != '/'
+#endif
+#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
+ && *p0 != '.'
+#endif
+#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
+ && *p0 != '*'
+#endif
+ )
+ continue;
+ if (*p0 == 'A' || *p0 == 'D') {
+ /* storing current config IP line */
+ pip = xzalloc(sizeof(Htaccess_IP));
+ if (pip) {
+ if (scan_ip_mask(c, &(pip->ip), &(pip->mask))) {
+ /* syntax IP{/mask} error detected, protect all */
+ *p0 = 'D';
+ pip->mask = 0;
+ }
+ pip->allow_deny = *p0;
+ if (*p0 == 'D') {
+ /* Deny:form_IP move top */
+ pip->next = config->ip_a_d;
+ config->ip_a_d = pip;
+ } else {
+ /* add to bottom A:form_IP config line */
+ Htaccess_IP *prev_IP = config->ip_a_d;
+
+ if (prev_IP == NULL) {
+ config->ip_a_d = pip;
+ } else {
+ while (prev_IP->next)
+ prev_IP = prev_IP->next;
+ prev_IP->next = pip;
+ }
+ }
+ }
+ continue;
+ }
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
+ if (*p0 == '/') {
+ /* make full path from httpd root / curent_path / config_line_path */
+ cf = flag == SUBDIR_PARSE ? path : "";
+ p0 = malloc(strlen(cf) + (c - buf) + 2 + strlen(c));
+ if (p0 == NULL)
+ continue;
+ c[-1] = 0;
+ sprintf(p0, "/%s%s", cf, buf);
+
+ /* another call bb_simplify_path */
+ cf = p = p0;
+
+ do {
+ if (*p == '/') {
+ if (*cf == '/') { /* skip duplicate (or initial) slash */
+ continue;
+ } else if (*cf == '.') {
+ if (cf[1] == '/' || cf[1] == 0) { /* remove extra '.' */
+ continue;
+ } else if ((cf[1] == '.') && (cf[2] == '/' || cf[2] == 0)) {
+ ++cf;
+ if (p > p0) {
+ while (*--p != '/') /* omit previous dir */;
+ }
+ continue;
+ }
+ }
+ }
+ *++p = *cf;
+ } while (*++cf);
+
+ if ((p == p0) || (*p != '/')) { /* not a trailing slash */
+ ++p; /* so keep last character */
+ }
+ *p = 0;
+ sprintf(p0, "%s:%s", p0, c);
+ }
+#endif
+
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH \
+ || ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES \
+ || ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
+ /* storing current config line */
+ cur = xzalloc(sizeof(Htaccess) + strlen(p0));
+ if (cur) {
+ cf = strcpy(cur->before_colon, p0);
+ c = strchr(cf, ':');
+ *c++ = 0;
+ cur->after_colon = c;
+#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
+ if (*cf == '.') {
+ /* config .mime line move top for overwrite previous */
+ cur->next = config->mime_a;
+ config->mime_a = cur;
+ continue;
+ }
+#endif
+#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
+ if (*cf == '*' && cf[1] == '.') {
+ /* config script interpreter line move top for overwrite previous */
+ cur->next = config->script_i;
+ config->script_i = cur;
+ continue;
+ }
+#endif
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
+ free(p0);
+ if (prev == NULL) {
+ /* first line */
+ config->auth = prev = cur;
+ } else {
+ /* sort path, if current lenght eq or bigger then move up */
+ Htaccess *prev_hti = config->auth;
+ size_t l = strlen(cf);
+ Htaccess *hti;
+
+ for (hti = prev_hti; hti; hti = hti->next) {
+ if (l >= strlen(hti->before_colon)) {
+ /* insert before hti */
+ cur->next = hti;
+ if (prev_hti != hti) {
+ prev_hti->next = cur;
+ } else {
+ /* insert as top */
+ config->auth = cur;
+ }
+ break;
+ }
+ if (prev_hti != hti)
+ prev_hti = prev_hti->next;
+ }
+ if (!hti) { /* not inserted, add to bottom */
+ prev->next = cur;
+ prev = cur;
+ }
+ }
+#endif
+ }
+#endif
+ }
+ fclose(f);
+}
+
+#if ENABLE_FEATURE_HTTPD_ENCODE_URL_STR
+/****************************************************************************
+ *
+ > $Function: encodeString()
+ *
+ * $Description: Given a string, html encode special characters.
+ * This is used for the -e command line option to provide an easy way
+ * for scripts to encode result data without confusing browsers. The
+ * returned string pointer is memory allocated by malloc().
+ *
+ * $Parameters:
+ * (const char *) string . . The first string to encode.
+ *
+ * $Return: (char *) . . . .. . . A pointer to the encoded string.
+ *
+ * $Errors: Returns a null string ("") if memory is not available.
+ *
+ ****************************************************************************/
+static char *encodeString(const char *string)
+{
+ /* take the simple route and encode everything */
+ /* could possibly scan once to get length. */
+ int len = strlen(string);
+ char *out = xmalloc(len * 6 + 1);
+ char *p = out;
+ char ch;
+
+ while ((ch = *string++)) {
+ // very simple check for what to encode
+ if (isalnum(ch)) *p++ = ch;
+ else p += sprintf(p, "&#%d;", (unsigned char) ch);
+ }
+ *p = '\0';
+ return out;
+}
+#endif /* FEATURE_HTTPD_ENCODE_URL_STR */
+
+/****************************************************************************
+ *
+ > $Function: decodeString()
+ *
+ * $Description: Given a URL encoded string, convert it to plain ascii.
+ * Since decoding always makes strings smaller, the decode is done in-place.
+ * Thus, callers should strdup() the argument if they do not want the
+ * argument modified. The return is the original pointer, allowing this
+ * function to be easily used as arguments to other functions.
+ *
+ * $Parameters:
+ * (char *) string . . . The first string to decode.
+ * (int) option_d . . 1 if called for httpd -d
+ *
+ * $Return: (char *) . . . . A pointer to the decoded string (same as input).
+ *
+ * $Errors: None
+ *
+ ****************************************************************************/
+static char *decodeString(char *orig, int option_d)
+{
+ /* note that decoded string is always shorter than original */
+ char *string = orig;
+ char *ptr = string;
+ char c;
+
+ while ((c = *ptr++) != '\0') {
+ unsigned value1, value2;
+
+ if (option_d && c == '+') {
+ *string++ = ' ';
+ continue;
+ }
+ if (c != '%') {
+ *string++ = c;
+ continue;
+ }
+ if (sscanf(ptr, "%1X", &value1) != 1
+ || sscanf(ptr+1, "%1X", &value2) != 1
+ ) {
+ if (!option_d)
+ return NULL;
+ *string++ = '%';
+ continue;
+ }
+ value1 = value1 * 16 + value2;
+ if (!option_d && (value1 == '/' || value1 == '\0')) {
+ /* caller takes it as indication of invalid
+ * (dangerous wrt exploits) chars */
+ return orig + 1;
+ }
+ *string++ = value1;
+ ptr += 2;
+ }
+ *string = '\0';
+ return orig;
+}
+
+
+#if ENABLE_FEATURE_HTTPD_CGI
+/****************************************************************************
+ * setenv helpers
+ ****************************************************************************/
+static void setenv1(const char *name, const char *value)
+{
+ if (!value)
+ value = "";
+ setenv(name, value, 1);
+}
+static void setenv_long(const char *name, long value)
+{
+ char buf[sizeof(value)*3 + 1];
+ sprintf(buf, "%ld", value);
+ setenv(name, buf, 1);
+}
+#endif
+
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
+/****************************************************************************
+ *
+ > $Function: decodeBase64()
+ *
+ > $Description: Decode a base 64 data stream as per rfc1521.
+ * Note that the rfc states that none base64 chars are to be ignored.
+ * Since the decode always results in a shorter size than the input, it is
+ * OK to pass the input arg as an output arg.
+ *
+ * $Parameter:
+ * (char *) Data . . . . A pointer to a base64 encoded string.
+ * Where to place the decoded data.
+ *
+ * $Return: void
+ *
+ * $Errors: None
+ *
+ ****************************************************************************/
+static void decodeBase64(char *Data)
+{
+
+ const unsigned char *in = (const unsigned char *)Data;
+ // The decoded size will be at most 3/4 the size of the encoded
+ unsigned long ch = 0;
+ int i = 0;
+
+ while (*in) {
+ int t = *in++;
+
+ if (t >= '0' && t <= '9')
+ t = t - '0' + 52;
+ else if (t >= 'A' && t <= 'Z')
+ t = t - 'A';
+ else if (t >= 'a' && t <= 'z')
+ t = t - 'a' + 26;
+ else if (t == '+')
+ t = 62;
+ else if (t == '/')
+ t = 63;
+ else if (t == '=')
+ t = 0;
+ else
+ continue;
+
+ ch = (ch << 6) | t;
+ i++;
+ if (i == 4) {
+ *Data++ = (char) (ch >> 16);
+ *Data++ = (char) (ch >> 8);
+ *Data++ = (char) ch;
+ i = 0;
+ }
+ }
+ *Data = 0;
+}
+#endif
+
+
+/****************************************************************************
+ *
+ > $Function: openServer()
+ *
+ * $Description: create a listen server socket on the designated port.
+ *
+ * $Return: (int) . . . A connection socket. -1 for errors.
+ *
+ * $Errors: None
+ *
+ ****************************************************************************/
+static int openServer(void)
+{
+ int fd;
+
+ /* create the socket right now */
+ fd = create_and_bind_stream_or_die(NULL, config->port);
+ xlisten(fd, 9);
+ return fd;
+}
+
+/****************************************************************************
+ *
+ > $Function: sendHeaders()
+ *
+ * $Description: Create and send HTTP response headers.
+ * The arguments are combined and sent as one write operation. Note that
+ * IE will puke big-time if the headers are not sent in one packet and the
+ * second packet is delayed for any reason.
+ *
+ * $Parameter:
+ * (HttpResponseNum) responseNum . . . The result code to send.
+ *
+ * $Return: (int) . . . . writing errors
+ *
+ ****************************************************************************/
+static int sendHeaders(HttpResponseNum responseNum)
+{
+ char *buf = config->buf;
+ const char *responseString = "";
+ const char *infoString = 0;
+ const char *mime_type;
+ unsigned i;
+ time_t timer = time(0);
+ char timeStr[80];
+ int len;
+ enum {
+ numNames = sizeof(httpResponseNames) / sizeof(httpResponseNames[0])
+ };
+
+ for (i = 0; i < numNames; i++) {
+ if (httpResponseNames[i].type == responseNum) {
+ responseString = httpResponseNames[i].name;
+ infoString = httpResponseNames[i].info;
+ break;
+ }
+ }
+ /* error message is HTML */
+ mime_type = responseNum == HTTP_OK ?
+ config->found_mime_type : "text/html";
+
+ /* emit the current date */
+ strftime(timeStr, sizeof(timeStr), RFC1123FMT, gmtime(&timer));
+ len = sprintf(buf,
+ "HTTP/1.0 %d %s\r\nContent-type: %s\r\n"
+ "Date: %s\r\nConnection: close\r\n",
+ responseNum, responseString, mime_type, timeStr);
+
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
+ if (responseNum == HTTP_UNAUTHORIZED) {
+ len += sprintf(buf+len,
+ "WWW-Authenticate: Basic realm=\"%s\"\r\n",
+ config->realm);
+ }
+#endif
+ if (responseNum == HTTP_MOVED_TEMPORARILY) {
+ len += sprintf(buf+len, "Location: %s/%s%s\r\n",
+ config->found_moved_temporarily,
+ (config->query ? "?" : ""),
+ (config->query ? config->query : ""));
+ }
+
+ if (config->ContentLength != -1) { /* file */
+ strftime(timeStr, sizeof(timeStr), RFC1123FMT, gmtime(&config->last_mod));
+ len += sprintf(buf+len, "Last-Modified: %s\r\n%s %"OFF_FMT"d\r\n",
+ timeStr, "Content-length:", config->ContentLength);
+ }
+ strcat(buf, "\r\n");
+ len += 2;
+ if (infoString) {
+ len += sprintf(buf+len,
+ "<HEAD><TITLE>%d %s</TITLE></HEAD>\n"
+ "<BODY><H1>%d %s</H1>\n%s\n</BODY>\n",
+ responseNum, responseString,
+ responseNum, responseString, infoString);
+ }
+ if (DEBUG)
+ fprintf(stderr, "headers: '%s'\n", buf);
+ i = config->accepted_socket;
+ if (i == 0) i++; /* write to fd# 1 in inetd mode */
+ return full_write(i, buf, len);
+}
+
+/****************************************************************************
+ *
+ > $Function: getLine()
+ *
+ * $Description: Read from the socket until an end of line char found.
+ *
+ * Characters are read one at a time until an eol sequence is found.
+ *
+ * $Return: (int) . . . . number of characters read. -1 if error.
+ *
+ ****************************************************************************/
+static int getLine(void)
+{
+ int count = 0;
+ char *buf = config->buf;
+
+ while (read(config->accepted_socket, buf + count, 1) == 1) {
+ if (buf[count] == '\r') continue;
+ if (buf[count] == '\n') {
+ buf[count] = 0;
+ return count;
+ }
+ if (count < (MAX_MEMORY_BUFF-1)) /* check overflow */
+ count++;
+ }
+ if (count) return count;
+ else return -1;
+}
+
+#if ENABLE_FEATURE_HTTPD_CGI
+/****************************************************************************
+ *
+ > $Function: sendCgi()
+ *
+ * $Description: Execute a CGI script and send it's stdout back
+ *
+ * Environment variables are set up and the script is invoked with pipes
+ * for stdin/stdout. If a post is being done the script is fed the POST
+ * data in addition to setting the QUERY_STRING variable (for GETs or POSTs).
+ *
+ * $Parameters:
+ * (const char *) url . . . . . . The requested URL (with leading /).
+ * (int bodyLen) . . . . . . . . Length of the post body.
+ * (const char *cookie) . . . . . For set HTTP_COOKIE.
+ * (const char *content_type) . . For set CONTENT_TYPE.
+ *
+ * $Return: (char *) . . . . A pointer to the decoded string (same as input).
+ *
+ * $Errors: None
+ *
+ ****************************************************************************/
+static int sendCgi(const char *url,
+ const char *request, int bodyLen, const char *cookie,
+ const char *content_type)
+{
+ int fromCgi[2]; /* pipe for reading data from CGI */
+ int toCgi[2]; /* pipe for sending data to CGI */
+
+ static char * argp[] = { 0, 0 };
+ int pid = 0;
+ int inFd;
+ int outFd;
+ int buf_count;
+ int status;
+ size_t post_read_size, post_read_idx;
+
+ if (pipe(fromCgi) != 0)
+ return 0;
+ if (pipe(toCgi) != 0)
+ return 0;
+
+/*
+ * Note: We can use vfork() here in the no-mmu case, although
+ * the child modifies the parent's variables, due to:
+ * 1) The parent does not use the child-modified variables.
+ * 2) The allocated memory (in the child) is freed when the process
+ * exits. This happens instantly after the child finishes,
+ * since httpd is run from inetd (and it can't run standalone
+ * in uClinux).
+ */
+#ifdef BB_NOMMU
+ pid = vfork();
+#else
+ pid = fork();
+#endif
+ if (pid < 0)
+ return 0;
+
+ if (!pid) {
+ /* child process */
+ char *script;
+ char *purl;
+ char realpath_buff[MAXPATHLEN];
+
+ if (config->accepted_socket > 1)
+ close(config->accepted_socket);
+ if (config->server_socket > 1)
+ close(config->server_socket);
+
+ dup2(toCgi[0], 0); // replace stdin with the pipe
+ dup2(fromCgi[1], 1); // replace stdout with the pipe
+ /* Huh? User seeing stderr can be a security problem...
+ * and if CGI really wants that, it can always dup2(1,2)...
+ if (!DEBUG)
+ dup2(fromCgi[1], 2); // replace stderr with the pipe
+ */
+ /* I think we cannot inadvertently close 0, 1 here... */
+ close(toCgi[0]);
+ close(toCgi[1]);
+ close(fromCgi[0]);
+ close(fromCgi[1]);
+
+ /*
+ * Find PATH_INFO.
+ */
+ xfunc_error_retval = 242;
+ purl = xstrdup(url);
+ script = purl;
+ while ((script = strchr(script + 1, '/')) != NULL) {
+ /* have script.cgi/PATH_INFO or dirs/script.cgi[/PATH_INFO] */
+ struct stat sb;
+
+ *script = '\0';
+ if (is_directory(purl + 1, 1, &sb) == 0) {
+ /* not directory, found script.cgi/PATH_INFO */
+ *script = '/';
+ break;
+ }
+ *script = '/'; /* is directory, find next '/' */
+ }
+ setenv1("PATH_INFO", script); /* set /PATH_INFO or "" */
+ /* setenv1("PATH", getenv("PATH")); redundant */
+ setenv1("REQUEST_METHOD", request);
+ if (config->query) {
+ char *uri = alloca(strlen(purl) + 2 + strlen(config->query));
+ if (uri)
+ sprintf(uri, "%s?%s", purl, config->query);
+ setenv1("REQUEST_URI", uri);
+ } else {
+ setenv1("REQUEST_URI", purl);
+ }
+ if (script != NULL)
+ *script = '\0'; /* cut off /PATH_INFO */
+ /* SCRIPT_FILENAME required by PHP in CGI mode */
+ if (!realpath(purl + 1, realpath_buff))
+ goto error_execing_cgi;
+ setenv1("SCRIPT_FILENAME", realpath_buff);
+ /* set SCRIPT_NAME as full path: /cgi-bin/dirs/script.cgi */
+ setenv1("SCRIPT_NAME", purl);
+ /* http://hoohoo.ncsa.uiuc.edu/cgi/env.html:
+ * QUERY_STRING: The information which follows the ? in the URL
+ * which referenced this script. This is the query information.
+ * It should not be decoded in any fashion. This variable
+ * should always be set when there is query information,
+ * regardless of command line decoding. */
+ /* (Older versions of bbox seem to do some decoding) */
+ setenv1("QUERY_STRING", config->query);
+ setenv1("SERVER_SOFTWARE", httpdVersion);
+ putenv((char*)"SERVER_PROTOCOL=HTTP/1.0");
+ putenv((char*)"GATEWAY_INTERFACE=CGI/1.1");
+ /* Having _separate_ variables for IP and port defeats
+ * the purpose of having socket abstraction. Which "port"
+ * are you using on Unix domain socket?
+ * IOW - REMOTE_PEER="1.2.3.4:56" makes much more sense.
+ * Oh well... */
+ {
+ char *p = config->rmt_ip_str ? : (char*)"";
+ char *cp = strrchr(p, ':');
+ if (ENABLE_FEATURE_IPV6 && cp && strchr(cp, ']'))
+ cp = NULL;
+ if (cp) *cp = '\0'; /* delete :PORT */
+ setenv1("REMOTE_ADDR", p);
+ }
+#if ENABLE_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV
+ setenv_long("REMOTE_PORT", config->port);
+#endif
+ if (bodyLen)
+ setenv_long("CONTENT_LENGTH", bodyLen);
+ if (cookie)
+ setenv1("HTTP_COOKIE", cookie);
+ if (content_type)
+ setenv1("CONTENT_TYPE", content_type);
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
+ if (config->remoteuser) {
+ setenv1("REMOTE_USER", config->remoteuser);
+ putenv((char*)"AUTH_TYPE=Basic");
+ }
+#endif
+ if (config->referer)
+ setenv1("HTTP_REFERER", config->referer);
+
+ /* set execve argp[0] without path */
+ argp[0] = strrchr(purl, '/') + 1;
+ /* but script argp[0] must have absolute path and chdiring to this */
+ script = strrchr(realpath_buff, '/');
+ if (!script)
+ goto error_execing_cgi;
+ *script = '\0';
+ if (chdir(realpath_buff) == 0) {
+ // Now run the program. If it fails,
+ // use _exit() so no destructors
+ // get called and make a mess.
+#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
+ char *interpr = NULL;
+ char *suffix = strrchr(purl, '.');
+
+ if (suffix) {
+ Htaccess *cur;
+ for (cur = config->script_i; cur; cur = cur->next) {
+ if (strcmp(cur->before_colon + 1, suffix) == 0) {
+ interpr = cur->after_colon;
+ break;
+ }
+ }
+ }
+#endif
+ *script = '/';
+#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
+ if (interpr)
+ execv(interpr, argp);
+ else
+#endif
+ execv(realpath_buff, argp);
+ }
+ error_execing_cgi:
+ /* send to stdout (even if we are not from inetd) */
+ config->accepted_socket = 1;
+ sendHeaders(HTTP_NOT_FOUND);
+ _exit(242);
+ } /* end child */
+
+ /* parent process */
+
+ buf_count = 0;
+ post_read_size = 0;
+ post_read_idx = 0; /* for gcc */
+ inFd = fromCgi[0];
+ outFd = toCgi[1];
+ close(fromCgi[1]);
+ close(toCgi[0]);
+ signal(SIGPIPE, SIG_IGN);
+
+ while (1) {
+ fd_set readSet;
+ fd_set writeSet;
+ char wbuf[128];
+ int nfound;
+ int count;
+
+ FD_ZERO(&readSet);
+ FD_ZERO(&writeSet);
+ FD_SET(inFd, &readSet);
+ if (bodyLen > 0 || post_read_size > 0) {
+ FD_SET(outFd, &writeSet);
+ nfound = outFd > inFd ? outFd : inFd;
+ if (post_read_size == 0) {
+ FD_SET(config->accepted_socket, &readSet);
+ if (nfound < config->accepted_socket)
+ nfound = config->accepted_socket;
+ }
+ /* Now wait on the set of sockets! */
+ nfound = select(nfound + 1, &readSet, &writeSet, NULL, NULL);
+ } else {
+ if (!bodyLen) {
+ close(outFd); /* no more POST data to CGI */
+ bodyLen = -1;
+ }
+ nfound = select(inFd + 1, &readSet, NULL, NULL, NULL);
+ }
+
+ if (nfound <= 0) {
+ if (waitpid(pid, &status, WNOHANG) <= 0) {
+ /* Weird. CGI didn't exit and no fd's
+ * are ready, yet select returned?! */
+ continue;
+ }
+ close(inFd);
+ if (DEBUG && WIFEXITED(status))
+ bb_error_msg("piped has exited with status=%d", WEXITSTATUS(status));
+ if (DEBUG && WIFSIGNALED(status))
+ bb_error_msg("piped has exited with signal=%d", WTERMSIG(status));
+ break;
+ }
+
+ if (post_read_size > 0 && FD_ISSET(outFd, &writeSet)) {
+ /* Have data from peer and can write to CGI */
+ // huh? why full_write? what if we will block?
+ // (imagine that CGI does not read its stdin...)
+ count = full_write(outFd, wbuf + post_read_idx, post_read_size);
+ if (count > 0) {
+ post_read_idx += count;
+ post_read_size -= count;
+ } else {
+ post_read_size = bodyLen = 0; /* broken pipe to CGI */
+ }
+ } else if (bodyLen > 0 && post_read_size == 0
+ && FD_ISSET(config->accepted_socket, &readSet)
+ ) {
+ /* We expect data, prev data portion is eaten by CGI
+ * and there *is* data to read from the peer
+ * (POSTDATA?) */
+ count = bodyLen > (int)sizeof(wbuf) ? (int)sizeof(wbuf) : bodyLen;
+ count = safe_read(config->accepted_socket, wbuf, count);
+ if (count > 0) {
+ post_read_size = count;
+ post_read_idx = 0;
+ bodyLen -= count;
+ } else {
+ bodyLen = 0; /* closed */
+ }
+ }
+
+#define PIPESIZE PIPE_BUF
+#if PIPESIZE >= MAX_MEMORY_BUFF
+# error "PIPESIZE >= MAX_MEMORY_BUFF"
+#endif
+ if (FD_ISSET(inFd, &readSet)) {
+ /* There is something to read from CGI */
+ int s = config->accepted_socket;
+ char *rbuf = config->buf;
+
+ /* Are we still buffering CGI output? */
+ if (buf_count >= 0) {
+ static const char HTTP_200[] = "HTTP/1.0 200 OK\r\n";
+ /* Must use safe_read, not full_read, because
+ * CGI may output a few first bytes and then wait
+ * for POSTDATA without closing stdout.
+ * With full_read we may wait here forever. */
+ count = safe_read(inFd, rbuf + buf_count, PIPESIZE - 4);
+ if (count <= 0) {
+ /* eof (or error) and there was no "HTTP",
+ * so add one and write out the received data */
+ if (buf_count) {
+ full_write(s, HTTP_200, sizeof(HTTP_200)-1);
+ full_write(s, rbuf, buf_count);
+ }
+ break; /* closed */
+ }
+ buf_count += count;
+ count = 0;
+ if (buf_count >= 4) {
+ /* check to see if CGI added "HTTP" */
+ if (memcmp(rbuf, HTTP_200, 4) != 0) {
+ /* there is no "HTTP", do it ourself */
+ if (full_write(s, HTTP_200, sizeof(HTTP_200)-1) != sizeof(HTTP_200)-1)
+ break;
+ }
+ /* example of valid CGI without "Content-type:"
+ * echo -en "HTTP/1.0 302 Found\r\n"
+ * echo -en "Location: http://www.busybox.net\r\n"
+ * echo -en "\r\n"
+ if (!strstr(rbuf, "ontent-")) {
+ full_write(s, "Content-type: text/plain\r\n\r\n", 28);
+ }
+ */
+ count = buf_count;
+ buf_count = -1; /* buffering off */
+ }
+ } else {
+ count = safe_read(inFd, rbuf, PIPESIZE);
+ if (count <= 0)
+ break; /* eof (or error) */
+ }
+ if (full_write(s, rbuf, count) != count)
+ break;
+ if (DEBUG)
+ fprintf(stderr, "cgi read %d bytes: '%.*s'\n", count, count, rbuf);
+ } /* if (FD_ISSET(inFd)) */
+ } /* while (1) */
+ return 0;
+}
+#endif /* FEATURE_HTTPD_CGI */
+
+/****************************************************************************
+ *
+ > $Function: sendFile()
+ *
+ * $Description: Send a file response to a HTTP request
+ *
+ * $Parameter:
+ * (const char *) url . . The URL requested.
+ *
+ * $Return: (int) . . . . . . Always 0.
+ *
+ ****************************************************************************/
+static int sendFile(const char *url)
+{
+ char * suffix;
+ int f;
+ const char * const * table;
+ const char * try_suffix;
+
+ suffix = strrchr(url, '.');
+
+ for (table = suffixTable; *table; table += 2)
+ if (suffix != NULL && (try_suffix = strstr(*table, suffix)) != 0) {
+ try_suffix += strlen(suffix);
+ if (*try_suffix == 0 || *try_suffix == '.')
+ break;
+ }
+ /* also, if not found, set default as "application/octet-stream"; */
+ config->found_mime_type = table[1];
+#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
+ if (suffix) {
+ Htaccess * cur;
+
+ for (cur = config->mime_a; cur; cur = cur->next) {
+ if (strcmp(cur->before_colon, suffix) == 0) {
+ config->found_mime_type = cur->after_colon;
+ break;
+ }
+ }
+ }
+#endif /* FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES */
+
+ if (DEBUG)
+ fprintf(stderr, "sending file '%s' content-type: %s\n",
+ url, config->found_mime_type);
+
+ f = open(url, O_RDONLY);
+ if (f >= 0) {
+ int count;
+ char *buf = config->buf;
+
+ sendHeaders(HTTP_OK);
+ /* TODO: sendfile() */
+ while ((count = full_read(f, buf, MAX_MEMORY_BUFF)) > 0) {
+ int fd = config->accepted_socket;
+ if (fd == 0) fd++; /* write to fd# 1 in inetd mode */
+ if (full_write(fd, buf, count) != count)
+ break;
+ }
+ close(f);
+ } else {
+ if (DEBUG)
+ bb_perror_msg("cannot open '%s'", url);
+ sendHeaders(HTTP_NOT_FOUND);
+ }
+
+ return 0;
+}
+
+static int checkPermIP(void)
+{
+ Htaccess_IP * cur;
+
+ /* This could stand some work */
+ for (cur = config->ip_a_d; cur; cur = cur->next) {
+#if ENABLE_FEATURE_HTTPD_CGI && DEBUG
+ fprintf(stderr, "checkPermIP: '%s' ? ", config->rmt_ip_str);
+#endif
+#if DEBUG
+ fprintf(stderr, "'%u.%u.%u.%u/%u.%u.%u.%u'\n",
+ (unsigned char)(cur->ip >> 24),
+ (unsigned char)(cur->ip >> 16),
+ (unsigned char)(cur->ip >> 8),
+ (unsigned char)(cur->ip),
+ (unsigned char)(cur->mask >> 24),
+ (unsigned char)(cur->mask >> 16),
+ (unsigned char)(cur->mask >> 8),
+ (unsigned char)(cur->mask)
+ );
+#endif
+ if ((config->rmt_ip & cur->mask) == cur->ip)
+ return cur->allow_deny == 'A'; /* Allow/Deny */
+ }
+
+ /* if unconfigured, return 1 - access from all */
+ return !config->flg_deny_all;
+}
+
+/****************************************************************************
+ *
+ > $Function: checkPerm()
+ *
+ * $Description: Check the permission file for access password protected.
+ *
+ * If config file isn't present, everything is allowed.
+ * Entries are of the form you can see example from header source
+ *
+ * $Parameters:
+ * (const char *) path . . . . The file path.
+ * (const char *) request . . . User information to validate.
+ *
+ * $Return: (int) . . . . . . . . . 1 if request OK, 0 otherwise.
+ *
+ ****************************************************************************/
+
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
+static int checkPerm(const char *path, const char *request)
+{
+ Htaccess * cur;
+ const char *p;
+ const char *p0;
+
+ const char *prev = NULL;
+
+ /* This could stand some work */
+ for (cur = config->auth; cur; cur = cur->next) {
+ size_t l;
+
+ p0 = cur->before_colon;
+ if (prev != NULL && strcmp(prev, p0) != 0)
+ continue; /* find next identical */
+ p = cur->after_colon;
+ if (DEBUG)
+ fprintf(stderr, "checkPerm: '%s' ? '%s'\n", p0, request);
+
+ l = strlen(p0);
+ if (strncmp(p0, path, l) == 0
+ && (l == 1 || path[l] == '/' || path[l] == '\0')
+ ) {
+ char *u;
+ /* path match found. Check request */
+ /* for check next /path:user:password */
+ prev = p0;
+ u = strchr(request, ':');
+ if (u == NULL) {
+ /* bad request, ':' required */
+ break;
+ }
+
+ if (ENABLE_FEATURE_HTTPD_AUTH_MD5) {
+ char *cipher;
+ char *pp;
+
+ if (strncmp(p, request, u-request) != 0) {
+ /* user uncompared */
+ continue;
+ }
+ pp = strchr(p, ':');
+ if (pp && pp[1] == '$' && pp[2] == '1' &&
+ pp[3] == '$' && pp[4]) {
+ pp++;
+ cipher = pw_encrypt(u+1, pp);
+ if (strcmp(cipher, pp) == 0)
+ goto set_remoteuser_var; /* Ok */
+ /* unauthorized */
+ continue;
+ }
+ }
+
+ if (strcmp(p, request) == 0) {
+set_remoteuser_var:
+ config->remoteuser = strdup(request);
+ if (config->remoteuser)
+ config->remoteuser[(u - request)] = 0;
+ return 1; /* Ok */
+ }
+ /* unauthorized */
+ }
+ } /* for */
+
+ return prev == NULL;
+}
+
+#endif /* FEATURE_HTTPD_BASIC_AUTH */
+
+/****************************************************************************
+ *
+ > $Function: handle_sigalrm()
+ *
+ * $Description: Handle timeouts
+ *
+ ****************************************************************************/
+
+static void handle_sigalrm(int sig)
+{
+ sendHeaders(HTTP_REQUEST_TIMEOUT);
+ config->alarm_signaled = sig;
+}
+
+/****************************************************************************
+ *
+ > $Function: handleIncoming()
+ *
+ * $Description: Handle an incoming http request.
+ *
+ ****************************************************************************/
+static void handleIncoming(void)
+{
+ char *buf = config->buf;
+ char *url;
+ char *purl;
+ int blank = -1;
+ char *test;
+ struct stat sb;
+ int ip_allowed;
+#if ENABLE_FEATURE_HTTPD_CGI
+ const char *prequest = request_GET;
+ unsigned long length = 0;
+ char *cookie = 0;
+ char *content_type = 0;
+#endif
+ fd_set s_fd;
+ struct timeval tv;
+ int retval;
+ struct sigaction sa;
+
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
+ int credentials = -1; /* if not required this is Ok */
+#endif
+
+ sa.sa_handler = handle_sigalrm;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0; /* no SA_RESTART */
+ sigaction(SIGALRM, &sa, NULL);
+
+ do {
+ int count;
+
+ (void) alarm(TIMEOUT);
+ if (getLine() <= 0)
+ break; /* closed */
+
+ purl = strpbrk(buf, " \t");
+ if (purl == NULL) {
+ BAD_REQUEST:
+ sendHeaders(HTTP_BAD_REQUEST);
+ break;
+ }
+ *purl = '\0';
+#if ENABLE_FEATURE_HTTPD_CGI
+ if (strcasecmp(buf, prequest) != 0) {
+ prequest = "POST";
+ if (strcasecmp(buf, prequest) != 0) {
+ sendHeaders(HTTP_NOT_IMPLEMENTED);
+ break;
+ }
+ }
+#else
+ if (strcasecmp(buf, request_GET) != 0) {
+ sendHeaders(HTTP_NOT_IMPLEMENTED);
+ break;
+ }
+#endif
+ *purl = ' ';
+ count = sscanf(purl, " %[^ ] HTTP/%d.%*d", buf, &blank);
+
+ if (count < 1 || buf[0] != '/') {
+ /* Garbled request/URL */
+ goto BAD_REQUEST;
+ }
+ url = alloca(strlen(buf) + sizeof("/index.html"));
+ if (url == NULL) {
+ sendHeaders(HTTP_INTERNAL_SERVER_ERROR);
+ break;
+ }
+ strcpy(url, buf);
+ /* extract url args if present */
+ test = strchr(url, '?');
+ config->query = NULL;
+ if (test) {
+ *test++ = '\0';
+ config->query = test;
+ }
+
+ test = decodeString(url, 0);
+ if (test == NULL)
+ goto BAD_REQUEST;
+ if (test == url+1) {
+ /* '/' or NUL is encoded */
+ sendHeaders(HTTP_NOT_FOUND);
+ break;
+ }
+
+ /* algorithm stolen from libbb bb_simplify_path(),
+ but don't strdup and reducing trailing slash and protect out root */
+ purl = test = url;
+ do {
+ if (*purl == '/') {
+ /* skip duplicate (or initial) slash */
+ if (*test == '/') {
+ continue;
+ }
+ if (*test == '.') {
+ /* skip extra '.' */
+ if (test[1] == '/' || test[1] == 0) {
+ continue;
+ } else
+ /* '..': be careful */
+ if (test[1] == '.' && (test[2] == '/' || test[2] == 0)) {
+ ++test;
+ if (purl == url) {
+ /* protect out root */
+ goto BAD_REQUEST;
+ }
+ while (*--purl != '/') /* omit previous dir */;
+ continue;
+ }
+ }
+ }
+ *++purl = *test;
+ } while (*++test);
+ *++purl = '\0'; /* so keep last character */
+ test = purl; /* end ptr */
+
+ /* If URL is directory, adding '/' */
+ if (test[-1] != '/') {
+ if (is_directory(url + 1, 1, &sb)) {
+ config->found_moved_temporarily = url;
+ }
+ }
+ if (DEBUG)
+ fprintf(stderr, "url='%s', args=%s\n", url, config->query);
+
+ test = url;
+ ip_allowed = checkPermIP();
+ while (ip_allowed && (test = strchr(test + 1, '/')) != NULL) {
+ /* have path1/path2 */
+ *test = '\0';
+ if (is_directory(url + 1, 1, &sb)) {
+ /* may be having subdir config */
+ parse_conf(url + 1, SUBDIR_PARSE);
+ ip_allowed = checkPermIP();
+ }
+ *test = '/';
+ }
+ if (blank >= 0) {
+ /* read until blank line for HTTP version specified, else parse immediate */
+ while (1) {
+ alarm(TIMEOUT);
+ count = getLine();
+ if (count <= 0)
+ break;
+
+ if (DEBUG)
+ fprintf(stderr, "header: '%s'\n", buf);
+
+#if ENABLE_FEATURE_HTTPD_CGI
+ /* try and do our best to parse more lines */
+ if ((STRNCASECMP(buf, "Content-length:") == 0)) {
+ /* extra read only for POST */
+ if (prequest != request_GET) {
+ test = buf + sizeof("Content-length:")-1;
+ if (!test[0])
+ goto bail_out;
+ errno = 0;
+ /* not using strtoul: it ignores leading munis! */
+ length = strtol(test, &test, 10);
+ /* length is "ulong", but we need to pass it to int later */
+ /* so we check for negative or too large values in one go: */
+ /* (long -> ulong conv caused negatives to be seen as > INT_MAX) */
+ if (test[0] || errno || length > INT_MAX)
+ goto bail_out;
+ }
+ } else if ((STRNCASECMP(buf, "Cookie:") == 0)) {
+ cookie = strdup(skip_whitespace(buf + sizeof("Cookie:")-1));
+ } else if ((STRNCASECMP(buf, "Content-Type:") == 0)) {
+ content_type = strdup(skip_whitespace(buf + sizeof("Content-Type:")-1));
+ } else if ((STRNCASECMP(buf, "Referer:") == 0)) {
+ config->referer = strdup(skip_whitespace(buf + sizeof("Referer:")-1));
+ }
+#endif
+
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
+ if (STRNCASECMP(buf, "Authorization:") == 0) {
+ /* We only allow Basic credentials.
+ * It shows up as "Authorization: Basic <userid:password>" where
+ * the userid:password is base64 encoded.
+ */
+ test = skip_whitespace(buf + sizeof("Authorization:")-1);
+ if (STRNCASECMP(test, "Basic") != 0)
+ continue;
+ test += sizeof("Basic")-1;
+ /* decodeBase64() skips whitespace itself */
+ decodeBase64(test);
+ credentials = checkPerm(url, test);
+ }
+#endif /* FEATURE_HTTPD_BASIC_AUTH */
+
+ } /* while extra header reading */
+ }
+ alarm(0);
+ if (config->alarm_signaled)
+ break;
+
+ if (strcmp(strrchr(url, '/') + 1, httpd_conf) == 0 || ip_allowed == 0) {
+ /* protect listing [/path]/httpd_conf or IP deny */
+#if ENABLE_FEATURE_HTTPD_CGI
+ FORBIDDEN: /* protect listing /cgi-bin */
+#endif
+ sendHeaders(HTTP_FORBIDDEN);
+ break;
+ }
+
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
+ if (credentials <= 0 && checkPerm(url, ":") == 0) {
+ sendHeaders(HTTP_UNAUTHORIZED);
+ break;
+ }
+#endif
+
+ if (config->found_moved_temporarily) {
+ sendHeaders(HTTP_MOVED_TEMPORARILY);
+ /* clear unforked memory flag */
+ config->found_moved_temporarily = NULL;
+ break;
+ }
+
+ test = url + 1; /* skip first '/' */
+
+#if ENABLE_FEATURE_HTTPD_CGI
+ if (strncmp(test, "cgi-bin", 7) == 0) {
+ if (test[7] == '/' && test[8] == 0)
+ goto FORBIDDEN; /* protect listing cgi-bin/ */
+ sendCgi(url, prequest, length, cookie, content_type);
+ break;
+ }
+#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
+ {
+ char *suffix = strrchr(test, '.');
+ if (suffix) {
+ Htaccess *cur;
+ for (cur = config->script_i; cur; cur = cur->next) {
+ if (strcmp(cur->before_colon + 1, suffix) == 0) {
+ sendCgi(url, prequest, length, cookie, content_type);
+ goto bail_out;
+ }
+ }
+ }
+ }
+#endif
+ if (prequest != request_GET) {
+ sendHeaders(HTTP_NOT_IMPLEMENTED);
+ break;
+ }
+#endif /* FEATURE_HTTPD_CGI */
+ if (purl[-1] == '/')
+ strcpy(purl, "index.html");
+ if (stat(test, &sb) == 0) {
+ /* It's a dir URL and there is index.html */
+ config->ContentLength = sb.st_size;
+ config->last_mod = sb.st_mtime;
+ }
+#if ENABLE_FEATURE_HTTPD_CGI
+ else if (purl[-1] == '/') {
+ /* It's a dir URL and there is no index.html
+ * Try cgi-bin/index.cgi */
+ if (access("/cgi-bin/index.cgi"+1, X_OK) == 0) {
+ purl[0] = '\0';
+ config->query = url;
+ sendCgi("/cgi-bin/index.cgi", prequest, length, cookie, content_type);
+ break;
+ }
+ }
+#endif /* FEATURE_HTTPD_CGI */
+ sendFile(test);
+ config->ContentLength = -1;
+ } while (0);
+
+#if ENABLE_FEATURE_HTTPD_CGI
+ bail_out:
+#endif
+
+ if (DEBUG)
+ fprintf(stderr, "closing socket\n\n");
+#if ENABLE_FEATURE_HTTPD_CGI
+ free(cookie);
+ free(content_type);
+ free(config->referer);
+ config->referer = NULL;
+# if ENABLE_FEATURE_HTTPD_BASIC_AUTH
+ free(config->remoteuser);
+ config->remoteuser = NULL;
+# endif
+#endif
+ shutdown(config->accepted_socket, SHUT_WR);
+
+ /* Properly wait for remote to closed */
+ FD_ZERO(&s_fd);
+ FD_SET(config->accepted_socket, &s_fd);
+
+ do {
+ tv.tv_sec = 2;
+ tv.tv_usec = 0;
+ retval = select(config->accepted_socket + 1, &s_fd, NULL, NULL, &tv);
+ } while (retval > 0 && read(config->accepted_socket, buf, sizeof(config->buf) > 0));
+
+ shutdown(config->accepted_socket, SHUT_RD);
+ /* In inetd case, we close fd 1 (stdout) here. We will exit soon anyway */
+ close(config->accepted_socket);
+}
+
+/****************************************************************************
+ *
+ > $Function: miniHttpd()
+ *
+ * $Description: The main http server function.
+ *
+ * Given an open socket fildes, listen for new connections and farm out
+ * the processing as a forked process.
+ *
+ * $Parameters:
+ * (int) server. . . The server socket fildes.
+ *
+ * $Return: (int) . . . . Always 0.
+ *
+ ****************************************************************************/
+static int miniHttpd(int server)
+{
+ fd_set readfd, portfd;
+
+ FD_ZERO(&portfd);
+ FD_SET(server, &portfd);
+
+ /* copy the ports we are watching to the readfd set */
+ while (1) {
+ int s;
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in sin;
+ USE_FEATURE_IPV6(struct sockaddr_in6 sin6;)
+ } fromAddr;
+ socklen_t fromAddrLen = sizeof(fromAddr);
+
+ /* Now wait INDEFINITELY on the set of sockets! */
+ readfd = portfd;
+ if (select(server + 1, &readfd, 0, 0, 0) <= 0)
+ continue;
+ if (!FD_ISSET(server, &readfd))
+ continue;
+ s = accept(server, &fromAddr.sa, &fromAddrLen);
+ if (s < 0)
+ continue;
+ config->accepted_socket = s;
+ config->rmt_ip = 0;
+ config->port = 0;
+#if ENABLE_FEATURE_HTTPD_CGI || DEBUG
+ free(config->rmt_ip_str);
+ config->rmt_ip_str = xmalloc_sockaddr2dotted(&fromAddr.sa, fromAddrLen);
+#if DEBUG
+ bb_error_msg("connection from '%s'", config->rmt_ip_str);
+#endif
+#endif /* FEATURE_HTTPD_CGI */
+ if (fromAddr.sa.sa_family == AF_INET) {
+ config->rmt_ip = ntohl(fromAddr.sin.sin_addr.s_addr);
+ config->port = ntohs(fromAddr.sin.sin_port);
+ }
+#if ENABLE_FEATURE_IPV6
+ if (fromAddr.sa.sa_family == AF_INET6) {
+ //config->rmt_ip = ntohl(fromAddr.sin.sin_addr.s_addr);
+ config->port = ntohs(fromAddr.sin6.sin6_port);
+ }
+#endif
+
+ /* set the KEEPALIVE option to cull dead connections */
+ setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1));
+
+ if (DEBUG || fork() == 0) {
+ /* child */
+#if ENABLE_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
+ /* protect reload config, may be confuse checking */
+ signal(SIGHUP, SIG_IGN);
+#endif
+ handleIncoming();
+ if (!DEBUG)
+ exit(0);
+ }
+ close(s);
+ } /* while (1) */
+ return 0;
+}
+
+/* from inetd */
+static int miniHttpd_inetd(void)
+{
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in sin;
+ USE_FEATURE_IPV6(struct sockaddr_in6 sin6;)
+ } fromAddr;
+ socklen_t fromAddrLen = sizeof(fromAddr);
+
+ getpeername(0, &fromAddr.sa, &fromAddrLen);
+ config->rmt_ip = 0;
+ config->port = 0;
+#if ENABLE_FEATURE_HTTPD_CGI || DEBUG
+ free(config->rmt_ip_str);
+ config->rmt_ip_str = xmalloc_sockaddr2dotted(&fromAddr.sa, fromAddrLen);
+#endif
+ if (fromAddr.sa.sa_family == AF_INET) {
+ config->rmt_ip = ntohl(fromAddr.sin.sin_addr.s_addr);
+ config->port = ntohs(fromAddr.sin.sin_port);
+ }
+#if ENABLE_FEATURE_IPV6
+ if (fromAddr.sa.sa_family == AF_INET6) {
+ //config->rmt_ip = ntohl(fromAddr.sin.sin_addr.s_addr);
+ config->port = ntohs(fromAddr.sin6.sin6_port);
+ }
+#endif
+ handleIncoming();
+ return 0;
+}
+
+#if ENABLE_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
+static void sighup_handler(int sig)
+{
+ /* set and reset */
+ struct sigaction sa;
+
+ parse_conf(default_path_httpd_conf, sig == SIGHUP ? SIGNALED_PARSE : FIRST_PARSE);
+ sa.sa_handler = sighup_handler;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_RESTART;
+ sigaction(SIGHUP, &sa, NULL);
+}
+#endif
+
+enum {
+ c_opt_config_file = 0,
+ d_opt_decode_url,
+ h_opt_home_httpd,
+ USE_FEATURE_HTTPD_ENCODE_URL_STR(e_opt_encode_url,)
+ USE_FEATURE_HTTPD_BASIC_AUTH( r_opt_realm ,)
+ USE_FEATURE_HTTPD_AUTH_MD5( m_opt_md5 ,)
+ USE_FEATURE_HTTPD_SETUID( u_opt_setuid ,)
+ p_opt_port ,
+ p_opt_inetd ,
+ p_opt_foreground,
+ OPT_CONFIG_FILE = 1 << c_opt_config_file,
+ OPT_DECODE_URL = 1 << d_opt_decode_url,
+ OPT_HOME_HTTPD = 1 << h_opt_home_httpd,
+ OPT_ENCODE_URL = USE_FEATURE_HTTPD_ENCODE_URL_STR((1 << e_opt_encode_url)) + 0,
+ OPT_REALM = USE_FEATURE_HTTPD_BASIC_AUTH( (1 << r_opt_realm )) + 0,
+ OPT_MD5 = USE_FEATURE_HTTPD_AUTH_MD5( (1 << m_opt_md5 )) + 0,
+ OPT_SETUID = USE_FEATURE_HTTPD_SETUID( (1 << u_opt_setuid )) + 0,
+ OPT_PORT = 1 << p_opt_port,
+ OPT_INETD = 1 << p_opt_inetd,
+ OPT_FOREGROUND = 1 << p_opt_foreground,
+};
+
+static const char httpd_opts[] = "c:d:h:"
+ USE_FEATURE_HTTPD_ENCODE_URL_STR("e:")
+ USE_FEATURE_HTTPD_BASIC_AUTH("r:")
+ USE_FEATURE_HTTPD_AUTH_MD5("m:")
+ USE_FEATURE_HTTPD_SETUID("u:")
+ "p:if";
+
+
+int httpd_main(int argc, char *argv[]);
+int httpd_main(int argc, char *argv[])
+{
+ unsigned opt;
+ const char *home_httpd = home;
+ char *url_for_decode;
+ USE_FEATURE_HTTPD_ENCODE_URL_STR(const char *url_for_encode;)
+ const char *s_port;
+ USE_FEATURE_HTTPD_SETUID(const char *s_ugid = NULL;)
+ USE_FEATURE_HTTPD_SETUID(struct bb_uidgid_t ugid;)
+ USE_FEATURE_HTTPD_AUTH_MD5(const char *pass;)
+
+#if ENABLE_LOCALE_SUPPORT
+ /* Undo busybox.c: we want to speak English in http (dates etc) */
+ setlocale(LC_TIME, "C");
+#endif
+
+ config = xzalloc(sizeof(*config));
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
+ config->realm = "Web Server Authentication";
+#endif
+ config->port = 80;
+ config->ContentLength = -1;
+
+ opt = getopt32(argc, argv, httpd_opts,
+ &(config->configFile), &url_for_decode, &home_httpd
+ USE_FEATURE_HTTPD_ENCODE_URL_STR(, &url_for_encode)
+ USE_FEATURE_HTTPD_BASIC_AUTH(, &(config->realm))
+ USE_FEATURE_HTTPD_AUTH_MD5(, &pass)
+ USE_FEATURE_HTTPD_SETUID(, &s_ugid)
+ , &s_port
+ );
+ if (opt & OPT_DECODE_URL) {
+ printf("%s", decodeString(url_for_decode, 1));
+ return 0;
+ }
+#if ENABLE_FEATURE_HTTPD_ENCODE_URL_STR
+ if (opt & OPT_ENCODE_URL) {
+ printf("%s", encodeString(url_for_encode));
+ return 0;
+ }
+#endif
+#if ENABLE_FEATURE_HTTPD_AUTH_MD5
+ if (opt & OPT_MD5) {
+ puts(pw_encrypt(pass, "$1$"));
+ return 0;
+ }
+#endif
+ if (opt & OPT_PORT)
+ config->port = xatou16(s_port);
+
+#if ENABLE_FEATURE_HTTPD_SETUID
+ if (opt & OPT_SETUID) {
+ if (!get_uidgid(&ugid, s_ugid, 1))
+ bb_error_msg_and_die("unrecognized user[:group] "
+ "name '%s'", s_ugid);
+ }
+#endif
+
+ xchdir(home_httpd);
+ if (!(opt & OPT_INETD)) {
+ signal(SIGCHLD, SIG_IGN);
+ config->server_socket = openServer();
+#if ENABLE_FEATURE_HTTPD_SETUID
+ /* drop privileges */
+ if (opt & OPT_SETUID) {
+ if (ugid.gid != (gid_t)-1) {
+ if (setgroups(1, &ugid.gid) == -1)
+ bb_perror_msg_and_die("setgroups");
+ xsetgid(ugid.gid);
+ }
+ xsetuid(ugid.uid);
+ }
+#endif
+ }
+
+#if ENABLE_FEATURE_HTTPD_CGI
+ {
+ char *p = getenv("PATH");
+ p = xstrdup(p); /* if gets NULL, returns NULL */
+ clearenv();
+ if (p)
+ setenv1("PATH", p);
+ if (!(opt & OPT_INETD))
+ setenv_long("SERVER_PORT", config->port);
+ }
+#endif
+
+#if ENABLE_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
+ sighup_handler(0);
+#else
+ parse_conf(default_path_httpd_conf, FIRST_PARSE);
+#endif
+
+ if (opt & OPT_INETD)
+ return miniHttpd_inetd();
+
+ if (!(opt & OPT_FOREGROUND))
+ xdaemon(1, 0); /* don't change current directory */
+ return miniHttpd(config->server_socket);
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/httpd_index_cgi_example b/i/pc104/initrd/conf/busybox/networking/httpd_index_cgi_example
new file mode 100644
index 0000000..9c8e022
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/httpd_index_cgi_example
@@ -0,0 +1,50 @@
+#!/bin/sh
+# This CGI creates directory index.
+# Put it into cgi-bin/index.cgi and chmod 0755.
+#
+# Problems:
+# * Unsafe wrt weird filenames with <>"'& etc...
+# * Not efficient: calls stat (program, not syscall) for each file
+# * Probably requires bash
+#
+# If you want speed and safety, you need to code it in C
+
+# Must start with '/'
+test "${QUERY_STRING:0:1}" = "/" || exit 1
+# /../ is not allowed
+test "${QUERY_STRING%/../*}" = "$QUERY_STRING" || exit 1
+test "${QUERY_STRING%/..}" = "$QUERY_STRING" || exit 1
+
+# Outta cgi-bin...
+cd .. 2>/dev/null || exit 1
+# Strip leading '/', go to target dir
+cd "${QUERY_STRING:1}" 2>/dev/null || exit 1
+
+f=`dirname "$QUERY_STRING"`
+test "$f" = "/" && f=""
+
+printf "%s" \
+$'HTTP/1.0 200 OK\r\n'\
+$'Content-type: text/html\r\n\r\n'\
+"<html><head><title>Index of $QUERY_STRING</title></head>"$'\r\n'\
+"<body><h1>Index of $QUERY_STRING</h1><pre>"$'\r\n'\
+$'<table width=100%>\r\n'\
+$'<col><col><col width=0*>\r\n'\
+$'<tr><th>Name<th align=right>Last modified<th align=right>Size\r\n'\
+\
+"<tr><td><a href='$f/'>..</a><td><td>"$'\r\n'
+
+IFS='#'
+for f in *; do
+ # Guard against empty dirs...
+ test -e "$f" && \
+ stat -c "%F#%s#%z" "$f" | {
+ read type size cdt junk
+ dir=''
+ test "$type" = "directory" && dir='/'
+ cdt="${cdt//.*}" # no fractional seconds
+ cdt="${cdt// /&nbsp;}" # prevent wrapping around space
+ printf "%s" "<tr><td><a href='$f$dir'>$f</a><td align=right>$cdt<td align=right>$size"$'\r\n'
+ }
+done
+printf "</table></pre><hr></body></html>"$'\r\n'
diff --git a/i/pc104/initrd/conf/busybox/networking/ifconfig.c b/i/pc104/initrd/conf/busybox/networking/ifconfig.c
new file mode 100644
index 0000000..3dcc902
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/ifconfig.c
@@ -0,0 +1,546 @@
+/* vi: set sw=4 ts=4: */
+/* ifconfig
+ *
+ * Similar to the standard Unix ifconfig, but with only the necessary
+ * parts for AF_INET, and without any printing of if info (for now).
+ *
+ * Bjorn Wesen, Axis Communications AB
+ *
+ *
+ * Authors of the original ifconfig was:
+ * Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/*
+ * Heavily modified by Manuel Novoa III Mar 6, 2001
+ *
+ * From initial port to busybox, removed most of the redundancy by
+ * converting to a table-driven approach. Added several (optional)
+ * args missing from initial port.
+ *
+ * Still missing: media, tunnel.
+ *
+ * 2002-04-20
+ * IPV6 support added by Bart Visscher <magick@linux-fan.com>
+ */
+
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <netinet/in.h>
+#if defined(__GLIBC__) && __GLIBC__ >=2 && __GLIBC_MINOR__ >= 1
+#include <netpacket/packet.h>
+#include <net/ethernet.h>
+#else
+#include <sys/types.h>
+#include <netinet/if_ether.h>
+#endif
+#include "inet_common.h"
+#include "busybox.h"
+
+#if ENABLE_FEATURE_IFCONFIG_SLIP
+# include <net/if_slip.h>
+#endif
+
+/* I don't know if this is needed for busybox or not. Anyone? */
+#define QUESTIONABLE_ALIAS_CASE
+
+
+/* Defines for glibc2.0 users. */
+#ifndef SIOCSIFTXQLEN
+# define SIOCSIFTXQLEN 0x8943
+# define SIOCGIFTXQLEN 0x8942
+#endif
+
+/* ifr_qlen is ifru_ivalue, but it isn't present in 2.0 kernel headers */
+#ifndef ifr_qlen
+# define ifr_qlen ifr_ifru.ifru_mtu
+#endif
+
+#ifndef IFF_DYNAMIC
+# define IFF_DYNAMIC 0x8000 /* dialup device with changing addresses */
+#endif
+
+#if ENABLE_FEATURE_IPV6
+struct in6_ifreq {
+ struct in6_addr ifr6_addr;
+ uint32_t ifr6_prefixlen;
+ int ifr6_ifindex;
+};
+#endif
+
+/*
+ * Here are the bit masks for the "flags" member of struct options below.
+ * N_ signifies no arg prefix; M_ signifies arg prefixed by '-'.
+ * CLR clears the flag; SET sets the flag; ARG signifies (optional) arg.
+ */
+#define N_CLR 0x01
+#define M_CLR 0x02
+#define N_SET 0x04
+#define M_SET 0x08
+#define N_ARG 0x10
+#define M_ARG 0x20
+
+#define M_MASK (M_CLR | M_SET | M_ARG)
+#define N_MASK (N_CLR | N_SET | N_ARG)
+#define SET_MASK (N_SET | M_SET)
+#define CLR_MASK (N_CLR | M_CLR)
+#define SET_CLR_MASK (SET_MASK | CLR_MASK)
+#define ARG_MASK (M_ARG | N_ARG)
+
+/*
+ * Here are the bit masks for the "arg_flags" member of struct options below.
+ */
+
+/*
+ * cast type:
+ * 00 int
+ * 01 char *
+ * 02 HOST_COPY in_ether
+ * 03 HOST_COPY INET_resolve
+ */
+#define A_CAST_TYPE 0x03
+/*
+ * map type:
+ * 00 not a map type (mem_start, io_addr, irq)
+ * 04 memstart (unsigned long)
+ * 08 io_addr (unsigned short)
+ * 0C irq (unsigned char)
+ */
+#define A_MAP_TYPE 0x0C
+#define A_ARG_REQ 0x10 /* Set if an arg is required. */
+#define A_NETMASK 0x20 /* Set if netmask (check for multiple sets). */
+#define A_SET_AFTER 0x40 /* Set a flag at the end. */
+#define A_COLON_CHK 0x80 /* Is this needed? See below. */
+#if ENABLE_FEATURE_IFCONFIG_BROADCAST_PLUS
+#define A_HOSTNAME 0x100 /* Set if it is ip addr. */
+#define A_BROADCAST 0x200 /* Set if it is broadcast addr. */
+#else
+#define A_HOSTNAME 0
+#define A_BROADCAST 0
+#endif
+
+/*
+ * These defines are for dealing with the A_CAST_TYPE field.
+ */
+#define A_CAST_CHAR_PTR 0x01
+#define A_CAST_RESOLVE 0x01
+#define A_CAST_HOST_COPY 0x02
+#define A_CAST_HOST_COPY_IN_ETHER A_CAST_HOST_COPY
+#define A_CAST_HOST_COPY_RESOLVE (A_CAST_HOST_COPY | A_CAST_RESOLVE)
+
+/*
+ * These defines are for dealing with the A_MAP_TYPE field.
+ */
+#define A_MAP_ULONG 0x04 /* memstart */
+#define A_MAP_USHORT 0x08 /* io_addr */
+#define A_MAP_UCHAR 0x0C /* irq */
+
+/*
+ * Define the bit masks signifying which operations to perform for each arg.
+ */
+
+#define ARG_METRIC (A_ARG_REQ /*| A_CAST_INT*/)
+#define ARG_MTU (A_ARG_REQ /*| A_CAST_INT*/)
+#define ARG_TXQUEUELEN (A_ARG_REQ /*| A_CAST_INT*/)
+#define ARG_MEM_START (A_ARG_REQ | A_MAP_ULONG)
+#define ARG_IO_ADDR (A_ARG_REQ | A_MAP_ULONG)
+#define ARG_IRQ (A_ARG_REQ | A_MAP_UCHAR)
+#define ARG_DSTADDR (A_ARG_REQ | A_CAST_HOST_COPY_RESOLVE)
+#define ARG_NETMASK (A_ARG_REQ | A_CAST_HOST_COPY_RESOLVE | A_NETMASK)
+#define ARG_BROADCAST (A_ARG_REQ | A_CAST_HOST_COPY_RESOLVE | A_SET_AFTER | A_BROADCAST)
+#define ARG_HW (A_ARG_REQ | A_CAST_HOST_COPY_IN_ETHER)
+#define ARG_POINTOPOINT (A_ARG_REQ | A_CAST_HOST_COPY_RESOLVE | A_SET_AFTER)
+#define ARG_KEEPALIVE (A_ARG_REQ | A_CAST_CHAR_PTR)
+#define ARG_OUTFILL (A_ARG_REQ | A_CAST_CHAR_PTR)
+#define ARG_HOSTNAME (A_CAST_HOST_COPY_RESOLVE | A_SET_AFTER | A_COLON_CHK | A_HOSTNAME)
+#define ARG_ADD_DEL (A_CAST_HOST_COPY_RESOLVE | A_SET_AFTER)
+
+
+/*
+ * Set up the tables. Warning! They must have corresponding order!
+ */
+
+struct arg1opt {
+ const char *name;
+ int selector;
+ unsigned short ifr_offset;
+};
+
+struct options {
+ const char *name;
+#if ENABLE_FEATURE_IFCONFIG_BROADCAST_PLUS
+ const unsigned int flags:6;
+ const unsigned int arg_flags:10;
+#else
+ const unsigned char flags;
+ const unsigned char arg_flags;
+#endif
+ const unsigned short selector;
+};
+
+#define ifreq_offsetof(x) offsetof(struct ifreq, x)
+
+static const struct arg1opt Arg1Opt[] = {
+ {"SIOCSIFMETRIC", SIOCSIFMETRIC, ifreq_offsetof(ifr_metric)},
+ {"SIOCSIFMTU", SIOCSIFMTU, ifreq_offsetof(ifr_mtu)},
+ {"SIOCSIFTXQLEN", SIOCSIFTXQLEN, ifreq_offsetof(ifr_qlen)},
+ {"SIOCSIFDSTADDR", SIOCSIFDSTADDR, ifreq_offsetof(ifr_dstaddr)},
+ {"SIOCSIFNETMASK", SIOCSIFNETMASK, ifreq_offsetof(ifr_netmask)},
+ {"SIOCSIFBRDADDR", SIOCSIFBRDADDR, ifreq_offsetof(ifr_broadaddr)},
+#if ENABLE_FEATURE_IFCONFIG_HW
+ {"SIOCSIFHWADDR", SIOCSIFHWADDR, ifreq_offsetof(ifr_hwaddr)},
+#endif
+ {"SIOCSIFDSTADDR", SIOCSIFDSTADDR, ifreq_offsetof(ifr_dstaddr)},
+#ifdef SIOCSKEEPALIVE
+ {"SIOCSKEEPALIVE", SIOCSKEEPALIVE, ifreq_offsetof(ifr_data)},
+#endif
+#ifdef SIOCSOUTFILL
+ {"SIOCSOUTFILL", SIOCSOUTFILL, ifreq_offsetof(ifr_data)},
+#endif
+#if ENABLE_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ
+ {"SIOCSIFMAP", SIOCSIFMAP, ifreq_offsetof(ifr_map.mem_start)},
+ {"SIOCSIFMAP", SIOCSIFMAP, ifreq_offsetof(ifr_map.base_addr)},
+ {"SIOCSIFMAP", SIOCSIFMAP, ifreq_offsetof(ifr_map.irq)},
+#endif
+ /* Last entry if for unmatched (possibly hostname) arg. */
+#if ENABLE_FEATURE_IPV6
+ {"SIOCSIFADDR", SIOCSIFADDR, ifreq_offsetof(ifr_addr)}, /* IPv6 version ignores the offset */
+ {"SIOCDIFADDR", SIOCDIFADDR, ifreq_offsetof(ifr_addr)}, /* IPv6 version ignores the offset */
+#endif
+ {"SIOCSIFADDR", SIOCSIFADDR, ifreq_offsetof(ifr_addr)},
+};
+
+static const struct options OptArray[] = {
+ {"metric", N_ARG, ARG_METRIC, 0},
+ {"mtu", N_ARG, ARG_MTU, 0},
+ {"txqueuelen", N_ARG, ARG_TXQUEUELEN, 0},
+ {"dstaddr", N_ARG, ARG_DSTADDR, 0},
+ {"netmask", N_ARG, ARG_NETMASK, 0},
+ {"broadcast", N_ARG | M_CLR, ARG_BROADCAST, IFF_BROADCAST},
+#if ENABLE_FEATURE_IFCONFIG_HW
+ {"hw", N_ARG, ARG_HW, 0},
+#endif
+ {"pointopoint", N_ARG | M_CLR, ARG_POINTOPOINT, IFF_POINTOPOINT},
+#ifdef SIOCSKEEPALIVE
+ {"keepalive", N_ARG, ARG_KEEPALIVE, 0},
+#endif
+#ifdef SIOCSOUTFILL
+ {"outfill", N_ARG, ARG_OUTFILL, 0},
+#endif
+#if ENABLE_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ
+ {"mem_start", N_ARG, ARG_MEM_START, 0},
+ {"io_addr", N_ARG, ARG_IO_ADDR, 0},
+ {"irq", N_ARG, ARG_IRQ, 0},
+#endif
+#if ENABLE_FEATURE_IPV6
+ {"add", N_ARG, ARG_ADD_DEL, 0},
+ {"del", N_ARG, ARG_ADD_DEL, 0},
+#endif
+ {"arp", N_CLR | M_SET, 0, IFF_NOARP},
+ {"trailers", N_CLR | M_SET, 0, IFF_NOTRAILERS},
+ {"promisc", N_SET | M_CLR, 0, IFF_PROMISC},
+ {"multicast", N_SET | M_CLR, 0, IFF_MULTICAST},
+ {"allmulti", N_SET | M_CLR, 0, IFF_ALLMULTI},
+ {"dynamic", N_SET | M_CLR, 0, IFF_DYNAMIC},
+ {"up", N_SET, 0, (IFF_UP | IFF_RUNNING)},
+ {"down", N_CLR, 0, IFF_UP},
+ {NULL, 0, ARG_HOSTNAME, (IFF_UP | IFF_RUNNING)}
+};
+
+/*
+ * A couple of prototypes.
+ */
+
+#if ENABLE_FEATURE_IFCONFIG_HW
+static int in_ether(const char *bufp, struct sockaddr *sap);
+#endif
+
+/*
+ * Our main function.
+ */
+
+int ifconfig_main(int argc, char **argv);
+int ifconfig_main(int argc, char **argv)
+{
+ struct ifreq ifr;
+ struct sockaddr_in sai;
+#if ENABLE_FEATURE_IFCONFIG_HW
+ struct sockaddr sa;
+#endif
+ const struct arg1opt *a1op;
+ const struct options *op;
+ int sockfd; /* socket fd we use to manipulate stuff with */
+ int selector;
+#if ENABLE_FEATURE_IFCONFIG_BROADCAST_PLUS
+ unsigned int mask;
+ unsigned int did_flags;
+ unsigned int sai_hostname, sai_netmask;
+#else
+ unsigned char mask;
+ unsigned char did_flags;
+#endif
+ char *p;
+ /*char host[128];*/
+ const char *host = NULL; /* make gcc happy */
+
+ did_flags = 0;
+#if ENABLE_FEATURE_IFCONFIG_BROADCAST_PLUS
+ sai_hostname = 0;
+ sai_netmask = 0;
+#endif
+
+ /* skip argv[0] */
+ ++argv;
+ --argc;
+
+#if ENABLE_FEATURE_IFCONFIG_STATUS
+ if (argc > 0 && (argv[0][0] == '-' && argv[0][1] == 'a' && !argv[0][2])) {
+ interface_opt_a = 1;
+ --argc;
+ ++argv;
+ }
+#endif
+
+ if (argc <= 1) {
+#if ENABLE_FEATURE_IFCONFIG_STATUS
+ return display_interfaces(argc ? *argv : NULL);
+#else
+ bb_error_msg_and_die("no support for status display");
+#endif
+ }
+
+ /* Create a channel to the NET kernel. */
+ sockfd = xsocket(AF_INET, SOCK_DGRAM, 0);
+
+ /* get interface name */
+ safe_strncpy(ifr.ifr_name, *argv, IFNAMSIZ);
+
+ /* Process the remaining arguments. */
+ while (*++argv != (char *) NULL) {
+ p = *argv;
+ mask = N_MASK;
+ if (*p == '-') { /* If the arg starts with '-'... */
+ ++p; /* advance past it and */
+ mask = M_MASK; /* set the appropriate mask. */
+ }
+ for (op = OptArray; op->name; op++) { /* Find table entry. */
+ if (strcmp(p, op->name) == 0) { /* If name matches... */
+ mask &= op->flags;
+ if (mask) /* set the mask and go. */
+ goto FOUND_ARG;
+ /* If we get here, there was a valid arg with an */
+ /* invalid '-' prefix. */
+ bb_error_msg_and_die("bad: '%s'", p-1);
+ }
+ }
+
+ /* We fell through, so treat as possible hostname. */
+ a1op = Arg1Opt + (sizeof(Arg1Opt) / sizeof(Arg1Opt[0])) - 1;
+ mask = op->arg_flags;
+ goto HOSTNAME;
+
+ FOUND_ARG:
+ if (mask & ARG_MASK) {
+ mask = op->arg_flags;
+ a1op = Arg1Opt + (op - OptArray);
+ if (mask & A_NETMASK & did_flags)
+ bb_show_usage();
+ if (*++argv == NULL) {
+ if (mask & A_ARG_REQ)
+ bb_show_usage();
+ --argv;
+ mask &= A_SET_AFTER; /* just for broadcast */
+ } else { /* got an arg so process it */
+ HOSTNAME:
+ did_flags |= (mask & (A_NETMASK|A_HOSTNAME));
+ if (mask & A_CAST_HOST_COPY) {
+#if ENABLE_FEATURE_IFCONFIG_HW
+ if (mask & A_CAST_RESOLVE) {
+#endif
+#if ENABLE_FEATURE_IPV6
+ char *prefix;
+ int prefix_len = 0;
+#endif
+ /*safe_strncpy(host, *argv, (sizeof host));*/
+ host = *argv;
+#if ENABLE_FEATURE_IPV6
+ prefix = strchr(host, '/');
+ if (prefix) {
+ prefix_len = xatou_range(prefix + 1, 0, 128);
+ *prefix = '\0';
+ }
+#endif
+ sai.sin_family = AF_INET;
+ sai.sin_port = 0;
+ if (!strcmp(host, bb_str_default)) {
+ /* Default is special, meaning 0.0.0.0. */
+ sai.sin_addr.s_addr = INADDR_ANY;
+ }
+#if ENABLE_FEATURE_IFCONFIG_BROADCAST_PLUS
+ else if ((host[0] == '+' && !host[1]) && (mask & A_BROADCAST)
+ && (did_flags & (A_NETMASK|A_HOSTNAME)) == (A_NETMASK|A_HOSTNAME)
+ ) {
+ /* + is special, meaning broadcast is derived. */
+ sai.sin_addr.s_addr = (~sai_netmask) | (sai_hostname & sai_netmask);
+ }
+#endif
+ else {
+ len_and_sockaddr *lsa;
+ if (strcmp(host, "inet") == 0)
+ continue; /* compat stuff */
+ lsa = xhost2sockaddr(host, 0);
+#if ENABLE_FEATURE_IPV6
+ if (lsa->sa.sa_family == AF_INET6) {
+ int sockfd6;
+ struct in6_ifreq ifr6;
+
+ memcpy((char *) &ifr6.ifr6_addr,
+ (char *) &(lsa->sin6.sin6_addr),
+ sizeof(struct in6_addr));
+
+ /* Create a channel to the NET kernel. */
+ sockfd6 = xsocket(AF_INET6, SOCK_DGRAM, 0);
+ if (ioctl(sockfd6, SIOGIFINDEX, &ifr) < 0)
+ bb_perror_msg_and_die("SIOGIFINDEX");
+ ifr6.ifr6_ifindex = ifr.ifr_ifindex;
+ ifr6.ifr6_prefixlen = prefix_len;
+ if (ioctl(sockfd6, a1op->selector, &ifr6) < 0)
+ bb_perror_msg_and_die(a1op->name);
+ if (ENABLE_FEATURE_CLEAN_UP)
+ free(lsa);
+ continue;
+ }
+#endif
+ sai.sin_addr = lsa->sin.sin_addr;
+ if (ENABLE_FEATURE_CLEAN_UP)
+ free(lsa);
+ }
+#if ENABLE_FEATURE_IFCONFIG_BROADCAST_PLUS
+ if (mask & A_HOSTNAME)
+ sai_hostname = sai.sin_addr.s_addr;
+ if (mask & A_NETMASK)
+ sai_netmask = sai.sin_addr.s_addr;
+#endif
+ p = (char *) &sai;
+#if ENABLE_FEATURE_IFCONFIG_HW
+ } else { /* A_CAST_HOST_COPY_IN_ETHER */
+ /* This is the "hw" arg case. */
+ if (strcmp("ether", *argv) || !*++argv)
+ bb_show_usage();
+ /*safe_strncpy(host, *argv, sizeof(host));*/
+ host = *argv;
+ if (in_ether(host, &sa))
+ bb_error_msg_and_die("invalid hw-addr %s", host);
+ p = (char *) &sa;
+ }
+#endif
+ memcpy( (((char *)&ifr) + a1op->ifr_offset),
+ p, sizeof(struct sockaddr));
+ } else {
+ /* FIXME: error check?? */
+ unsigned long i = strtoul(*argv, NULL, 0);
+ p = ((char *)&ifr) + a1op->ifr_offset;
+#if ENABLE_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ
+ if (mask & A_MAP_TYPE) {
+ if (ioctl(sockfd, SIOCGIFMAP, &ifr) < 0)
+ bb_perror_msg_and_die("SIOCGIFMAP");
+ if ((mask & A_MAP_UCHAR) == A_MAP_UCHAR)
+ *((unsigned char *) p) = i;
+ else if (mask & A_MAP_USHORT)
+ *((unsigned short *) p) = i;
+ else
+ *((unsigned long *) p) = i;
+ } else
+#endif
+ if (mask & A_CAST_CHAR_PTR)
+ *((caddr_t *) p) = (caddr_t) i;
+ else /* A_CAST_INT */
+ *((int *) p) = i;
+ }
+
+ if (ioctl(sockfd, a1op->selector, &ifr) < 0)
+ bb_perror_msg_and_die(a1op->name);
+#ifdef QUESTIONABLE_ALIAS_CASE
+ if (mask & A_COLON_CHK) {
+ /*
+ * Don't do the set_flag() if the address is an alias with
+ * a '-' at the end, since it's deleted already! - Roman
+ *
+ * Should really use regex.h here, not sure though how well
+ * it'll go with the cross-platform support etc.
+ */
+ char *ptr;
+ short int found_colon = 0;
+ for (ptr = ifr.ifr_name; *ptr; ptr++)
+ if (*ptr == ':')
+ found_colon++;
+ if (found_colon && ptr[-1] == '-')
+ continue;
+ }
+#endif
+ }
+ if (!(mask & A_SET_AFTER))
+ continue;
+ mask = N_SET;
+ }
+
+ if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0)
+ bb_perror_msg_and_die("SIOCGIFFLAGS");
+ selector = op->selector;
+ if (mask & SET_MASK)
+ ifr.ifr_flags |= selector;
+ else
+ ifr.ifr_flags &= ~selector;
+ if (ioctl(sockfd, SIOCSIFFLAGS, &ifr) < 0)
+ bb_perror_msg_and_die("SIOCSIFFLAGS");
+ } /* while () */
+
+ if (ENABLE_FEATURE_CLEAN_UP)
+ close(sockfd);
+ return 0;
+}
+
+#if ENABLE_FEATURE_IFCONFIG_HW
+/* Input an Ethernet address and convert to binary. */
+static int in_ether(const char *bufp, struct sockaddr *sap)
+{
+ char *ptr;
+ int i, j;
+ unsigned char val;
+ unsigned char c;
+
+ sap->sa_family = ARPHRD_ETHER;
+ ptr = sap->sa_data;
+
+ i = 0;
+ do {
+ j = val = 0;
+
+ /* We might get a semicolon here - not required. */
+ if (i && (*bufp == ':')) {
+ bufp++;
+ }
+
+ do {
+ c = *bufp;
+ if (((unsigned char)(c - '0')) <= 9) {
+ c -= '0';
+ } else if (((unsigned char)((c|0x20) - 'a')) <= 5) {
+ c = (c|0x20) - ('a'-10);
+ } else if (j && (c == ':' || c == 0)) {
+ break;
+ } else {
+ return -1;
+ }
+ ++bufp;
+ val <<= 4;
+ val += c;
+ } while (++j < 2);
+ *ptr++ = val;
+ } while (++i < ETH_ALEN);
+
+ return *bufp; /* Error if we don't end at end of string. */
+}
+#endif
diff --git a/i/pc104/initrd/conf/busybox/networking/ifupdown.c b/i/pc104/initrd/conf/busybox/networking/ifupdown.c
new file mode 100644
index 0000000..887c2ee
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/ifupdown.c
@@ -0,0 +1,1273 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ifupdown for busybox
+ * Copyright (c) 2002 Glenn McGrath <bug1@iinet.net.au>
+ * Copyright (c) 2003-2004 Erik Andersen <andersen@codepoet.org>
+ *
+ * Based on ifupdown v 0.6.4 by Anthony Towns
+ * Copyright (c) 1999 Anthony Towns <aj@azure.humbug.org.au>
+ *
+ * Changes to upstream version
+ * Remove checks for kernel version, assume kernel version 2.2.0 or better.
+ * Lines in the interfaces file cannot wrap.
+ * To adhere to the FHS, the default state file is /var/run/ifstate.
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ */
+
+#include "busybox.h"
+#include <sys/utsname.h>
+#include <fnmatch.h>
+#include <getopt.h>
+
+#define MAX_OPT_DEPTH 10
+#define EUNBALBRACK 10001
+#define EUNDEFVAR 10002
+#define EUNBALPER 10000
+
+#if ENABLE_FEATURE_IFUPDOWN_MAPPING
+#define MAX_INTERFACE_LENGTH 10
+#endif
+
+#define debug_noise(args...) /*fprintf(stderr, args)*/
+
+/* Forward declaration */
+struct interface_defn_t;
+
+typedef int execfn(char *command);
+
+struct method_t {
+ const char *name;
+ int (*up)(struct interface_defn_t *ifd, execfn *e);
+ int (*down)(struct interface_defn_t *ifd, execfn *e);
+};
+
+struct address_family_t {
+ const char *name;
+ int n_methods;
+ const struct method_t *method;
+};
+
+struct mapping_defn_t {
+ struct mapping_defn_t *next;
+
+ int max_matches;
+ int n_matches;
+ char **match;
+
+ char *script;
+
+ int max_mappings;
+ int n_mappings;
+ char **mapping;
+};
+
+struct variable_t {
+ char *name;
+ char *value;
+};
+
+struct interface_defn_t {
+ const struct address_family_t *address_family;
+ const struct method_t *method;
+
+ char *iface;
+ int max_options;
+ int n_options;
+ struct variable_t *option;
+};
+
+struct interfaces_file_t {
+ llist_t *autointerfaces;
+ llist_t *ifaces;
+ struct mapping_defn_t *mappings;
+};
+
+#define OPTION_STR "anvf" USE_FEATURE_IFUPDOWN_MAPPING("m") "i:"
+enum {
+ OPT_do_all = 0x1,
+ OPT_no_act = 0x2,
+ OPT_verbose = 0x4,
+ OPT_force = 0x8,
+ OPT_no_mappings = 0x10,
+};
+#define DO_ALL (option_mask32 & OPT_do_all)
+#define NO_ACT (option_mask32 & OPT_no_act)
+#define VERBOSE (option_mask32 & OPT_verbose)
+#define FORCE (option_mask32 & OPT_force)
+#define NO_MAPPINGS (option_mask32 & OPT_no_mappings)
+
+static char **my_environ;
+
+static const char *startup_PATH;
+
+#if ENABLE_FEATURE_IFUPDOWN_IPV4 || ENABLE_FEATURE_IFUPDOWN_IPV6
+
+#if ENABLE_FEATURE_IFUPDOWN_IP
+
+static unsigned count_bits(unsigned a)
+{
+ unsigned result;
+ result = (a & 0x55) + ((a >> 1) & 0x55);
+ result = (result & 0x33) + ((result >> 2) & 0x33);
+ return (result & 0x0F) + ((result >> 4) & 0x0F);
+}
+
+static int count_netmask_bits(char *dotted_quad)
+{
+ unsigned result, a, b, c, d;
+ /* Found a netmask... Check if it is dotted quad */
+ if (sscanf(dotted_quad, "%u.%u.%u.%u", &a, &b, &c, &d) != 4)
+ return -1;
+ // FIXME: will be confused by e.g. 255.0.255.0
+ result = count_bits(a);
+ result += count_bits(b);
+ result += count_bits(c);
+ result += count_bits(d);
+ return (int)result;
+}
+#endif
+
+static void addstr(char **bufp, const char *str, size_t str_length)
+{
+ /* xasprintf trick will be smaller, but we are often
+ * called with str_length == 1 - don't want to have
+ * THAT much of malloc/freeing! */
+ char *buf = *bufp;
+ int len = (buf ? strlen(buf) : 0);
+ str_length++;
+ buf = xrealloc(buf, len + str_length);
+ /* copies at most str_length-1 chars! */
+ safe_strncpy(buf + len, str, str_length);
+ *bufp = buf;
+}
+
+static int strncmpz(const char *l, const char *r, size_t llen)
+{
+ int i = strncmp(l, r, llen);
+
+ if (i == 0)
+ return -r[llen];
+ return i;
+}
+
+static char *get_var(const char *id, size_t idlen, struct interface_defn_t *ifd)
+{
+ int i;
+
+ if (strncmpz(id, "iface", idlen) == 0) {
+ char *result;
+ static char label_buf[20];
+ safe_strncpy(label_buf, ifd->iface, sizeof(label_buf));
+ result = strchr(label_buf, ':');
+ if (result) {
+ *result = '\0';
+ }
+ return label_buf;
+ }
+ if (strncmpz(id, "label", idlen) == 0) {
+ return ifd->iface;
+ }
+ for (i = 0; i < ifd->n_options; i++) {
+ if (strncmpz(id, ifd->option[i].name, idlen) == 0) {
+ return ifd->option[i].value;
+ }
+ }
+ return NULL;
+}
+
+static char *parse(const char *command, struct interface_defn_t *ifd)
+{
+ size_t old_pos[MAX_OPT_DEPTH] = { 0 };
+ int okay[MAX_OPT_DEPTH] = { 1 };
+ int opt_depth = 1;
+ char *result = NULL;
+
+ while (*command) {
+ switch (*command) {
+ default:
+ addstr(&result, command, 1);
+ command++;
+ break;
+ case '\\':
+ if (command[1]) {
+ addstr(&result, command + 1, 1);
+ command += 2;
+ } else {
+ addstr(&result, command, 1);
+ command++;
+ }
+ break;
+ case '[':
+ if (command[1] == '[' && opt_depth < MAX_OPT_DEPTH) {
+ old_pos[opt_depth] = result ? strlen(result) : 0;
+ okay[opt_depth] = 1;
+ opt_depth++;
+ command += 2;
+ } else {
+ addstr(&result, "[", 1);
+ command++;
+ }
+ break;
+ case ']':
+ if (command[1] == ']' && opt_depth > 1) {
+ opt_depth--;
+ if (!okay[opt_depth]) {
+ result[old_pos[opt_depth]] = '\0';
+ }
+ command += 2;
+ } else {
+ addstr(&result, "]", 1);
+ command++;
+ }
+ break;
+ case '%':
+ {
+ char *nextpercent;
+ char *varvalue;
+
+ command++;
+ nextpercent = strchr(command, '%');
+ if (!nextpercent) {
+ errno = EUNBALPER;
+ free(result);
+ return NULL;
+ }
+
+ varvalue = get_var(command, nextpercent - command, ifd);
+
+ if (varvalue) {
+ addstr(&result, varvalue, strlen(varvalue));
+ } else {
+#if ENABLE_FEATURE_IFUPDOWN_IP
+ /* Sigh... Add a special case for 'ip' to convert from
+ * dotted quad to bit count style netmasks. */
+ if (strncmp(command, "bnmask", 6) == 0) {
+ unsigned res;
+ varvalue = get_var("netmask", 7, ifd);
+ if (varvalue && (res = count_netmask_bits(varvalue)) > 0) {
+ const char *argument = utoa(res);
+ addstr(&result, argument, strlen(argument));
+ command = nextpercent + 1;
+ break;
+ }
+ }
+#endif
+ okay[opt_depth - 1] = 0;
+ }
+
+ command = nextpercent + 1;
+ }
+ break;
+ }
+ }
+
+ if (opt_depth > 1) {
+ errno = EUNBALBRACK;
+ free(result);
+ return NULL;
+ }
+
+ if (!okay[0]) {
+ errno = EUNDEFVAR;
+ free(result);
+ return NULL;
+ }
+
+ return result;
+}
+
+/* execute() returns 1 for success and 0 for failure */
+static int execute(const char *command, struct interface_defn_t *ifd, execfn *exec)
+{
+ char *out;
+ int ret;
+
+ out = parse(command, ifd);
+ if (!out) {
+ /* parse error? */
+ return 0;
+ }
+ /* out == "": parsed ok but not all needed variables known, skip */
+ ret = out[0] ? (*exec)(out) : 1;
+
+ free(out);
+ if (ret != 1) {
+ return 0;
+ }
+ return 1;
+}
+#endif
+
+#if ENABLE_FEATURE_IFUPDOWN_IPV6
+static int loopback_up6(struct interface_defn_t *ifd, execfn *exec)
+{
+#if ENABLE_FEATURE_IFUPDOWN_IP
+ int result;
+ result = execute("ip addr add ::1 dev %iface%", ifd, exec);
+ result += execute("ip link set %iface% up", ifd, exec);
+ return ((result == 2) ? 2 : 0);
+#else
+ return execute("ifconfig %iface% add ::1", ifd, exec);
+#endif
+}
+
+static int loopback_down6(struct interface_defn_t *ifd, execfn *exec)
+{
+#if ENABLE_FEATURE_IFUPDOWN_IP
+ return execute("ip link set %iface% down", ifd, exec);
+#else
+ return execute("ifconfig %iface% del ::1", ifd, exec);
+#endif
+}
+
+static int static_up6(struct interface_defn_t *ifd, execfn *exec)
+{
+ int result;
+#if ENABLE_FEATURE_IFUPDOWN_IP
+ result = execute("ip addr add %address%/%netmask% dev %iface%[[ label %label%]]", ifd, exec);
+ result += execute("ip link set[[ mtu %mtu%]][[ address %hwaddress%]] %iface% up", ifd, exec);
+ /* Was: "[[ ip ....%gateway% ]]". Removed extra spaces w/o checking */
+ result += execute("[[ip route add ::/0 via %gateway%]]", ifd, exec);
+#else
+ result = execute("ifconfig %iface%[[ media %media%]][[ hw %hwaddress%]][[ mtu %mtu%]] up", ifd, exec);
+ result += execute("ifconfig %iface% add %address%/%netmask%", ifd, exec);
+ result += execute("[[route -A inet6 add ::/0 gw %gateway%]]", ifd, exec);
+#endif
+ return ((result == 3) ? 3 : 0);
+}
+
+static int static_down6(struct interface_defn_t *ifd, execfn *exec)
+{
+#if ENABLE_FEATURE_IFUPDOWN_IP
+ return execute("ip link set %iface% down", ifd, exec);
+#else
+ return execute("ifconfig %iface% down", ifd, exec);
+#endif
+}
+
+#if ENABLE_FEATURE_IFUPDOWN_IP
+static int v4tunnel_up(struct interface_defn_t *ifd, execfn *exec)
+{
+ int result;
+ result = execute("ip tunnel add %iface% mode sit remote "
+ "%endpoint%[[ local %local%]][[ ttl %ttl%]]", ifd, exec);
+ result += execute("ip link set %iface% up", ifd, exec);
+ result += execute("ip addr add %address%/%netmask% dev %iface%", ifd, exec);
+ result += execute("[[ip route add ::/0 via %gateway%]]", ifd, exec);
+ return ((result == 4) ? 4 : 0);
+}
+
+static int v4tunnel_down(struct interface_defn_t * ifd, execfn * exec)
+{
+ return execute("ip tunnel del %iface%", ifd, exec);
+}
+#endif
+
+static const struct method_t methods6[] = {
+#if ENABLE_FEATURE_IFUPDOWN_IP
+ { "v4tunnel", v4tunnel_up, v4tunnel_down, },
+#endif
+ { "static", static_up6, static_down6, },
+ { "loopback", loopback_up6, loopback_down6, },
+};
+
+static const struct address_family_t addr_inet6 = {
+ "inet6",
+ sizeof(methods6) / sizeof(struct method_t),
+ methods6
+};
+#endif /* FEATURE_IFUPDOWN_IPV6 */
+
+#if ENABLE_FEATURE_IFUPDOWN_IPV4
+static int loopback_up(struct interface_defn_t *ifd, execfn *exec)
+{
+#if ENABLE_FEATURE_IFUPDOWN_IP
+ int result;
+ result = execute("ip addr add 127.0.0.1/8 dev %iface%", ifd, exec);
+ result += execute("ip link set %iface% up", ifd, exec);
+ return ((result == 2) ? 2 : 0);
+#else
+ return execute("ifconfig %iface% 127.0.0.1 up", ifd, exec);
+#endif
+}
+
+static int loopback_down(struct interface_defn_t *ifd, execfn *exec)
+{
+#if ENABLE_FEATURE_IFUPDOWN_IP
+ int result;
+ result = execute("ip addr flush dev %iface%", ifd, exec);
+ result += execute("ip link set %iface% down", ifd, exec);
+ return ((result == 2) ? 2 : 0);
+#else
+ return execute("ifconfig %iface% 127.0.0.1 down", ifd, exec);
+#endif
+}
+
+static int static_up(struct interface_defn_t *ifd, execfn *exec)
+{
+ int result;
+#if ENABLE_FEATURE_IFUPDOWN_IP
+ result = execute("ip addr add %address%/%bnmask%[[ broadcast %broadcast%]] "
+ "dev %iface%[[ peer %pointopoint%]][[ label %label%]]", ifd, exec);
+ result += execute("ip link set[[ mtu %mtu%]][[ address %hwaddress%]] %iface% up", ifd, exec);
+ result += execute("[[ip route add default via %gateway% dev %iface%]]", ifd, exec);
+ return ((result == 3) ? 3 : 0);
+#else
+ /* ifconfig said to set iface up before it processes hw %hwaddress%,
+ * which then of course fails. Thus we run two separate ifconfig */
+ result = execute("ifconfig %iface%[[ hw %hwaddress%]][[ media %media%]][[ mtu %mtu%]] up",
+ ifd, exec);
+ result += execute("ifconfig %iface% %address% netmask %netmask%"
+ "[[ broadcast %broadcast%]][[ pointopoint %pointopoint%]] ",
+ ifd, exec);
+ result += execute("[[route add default gw %gateway% %iface%]]", ifd, exec);
+ return ((result == 3) ? 3 : 0);
+#endif
+}
+
+static int static_down(struct interface_defn_t *ifd, execfn *exec)
+{
+ int result;
+#if ENABLE_FEATURE_IFUPDOWN_IP
+ result = execute("ip addr flush dev %iface%", ifd, exec);
+ result += execute("ip link set %iface% down", ifd, exec);
+#else
+ result = execute("[[route del default gw %gateway% %iface%]]", ifd, exec);
+ result += execute("ifconfig %iface% down", ifd, exec);
+#endif
+ return ((result == 2) ? 2 : 0);
+}
+
+#if !ENABLE_APP_UDHCPC
+struct dhcp_client_t
+{
+ const char *name;
+ const char *startcmd;
+ const char *stopcmd;
+};
+
+static const struct dhcp_client_t ext_dhcp_clients[] = {
+ { "udhcpc",
+ "udhcpc -R -n -p /var/run/udhcpc.%iface%.pid -i %iface%[[ -H %hostname%]][[ -c %clientid%]][[ -s %script%]]",
+ "kill -TERM `cat /var/run/udhcpc.%iface%.pid` 2>/dev/null",
+ },
+ { "pump",
+ "pump -i %iface%[[ -h %hostname%]][[ -l %leasehours%]]",
+ "pump -i %iface% -k",
+ },
+ { "dhclient",
+ "dhclient -pf /var/run/dhclient.%iface%.pid %iface%",
+ "kill -9 `cat /var/run/dhclient.%iface%.pid` 2>/dev/null",
+ },
+ { "dhcpcd",
+ "dhcpcd[[ -h %hostname%]][[ -i %vendor%]][[ -I %clientid%]][[ -l %leasetime%]] %iface%",
+ "dhcpcd -k %iface%",
+ },
+};
+#endif
+
+static int dhcp_up(struct interface_defn_t *ifd, execfn *exec)
+{
+#if ENABLE_APP_UDHCPC
+ return execute("udhcpc -R -n -p /var/run/udhcpc.%iface%.pid "
+ "-i %iface%[[ -H %hostname%]][[ -c %clientid%]][[ -s %script%]]",
+ ifd, exec);
+#else
+ int i, nclients = sizeof(ext_dhcp_clients) / sizeof(ext_dhcp_clients[0]);
+ for (i = 0; i < nclients; i++) {
+ if (exists_execable(ext_dhcp_clients[i].name))
+ return execute(ext_dhcp_clients[i].startcmd, ifd, exec);
+ }
+ bb_error_msg("no dhcp clients found");
+ return 0;
+#endif
+}
+
+static int dhcp_down(struct interface_defn_t *ifd, execfn *exec)
+{
+#if ENABLE_APP_UDHCPC
+ return execute("kill -TERM "
+ "`cat /var/run/udhcpc.%iface%.pid` 2>/dev/null", ifd, exec);
+#else
+ int i, nclients = sizeof(ext_dhcp_clients) / sizeof(ext_dhcp_clients[0]);
+ for (i = 0; i < nclients; i++) {
+ if (exists_execable(ext_dhcp_clients[i].name))
+ return execute(ext_dhcp_clients[i].stopcmd, ifd, exec);
+ }
+ bb_error_msg("no dhcp clients found, using static interface shutdown");
+ return static_down(ifd, exec);
+#endif
+}
+
+static int manual_up_down(struct interface_defn_t *ifd, execfn *exec)
+{
+ return 1;
+}
+
+static int bootp_up(struct interface_defn_t *ifd, execfn *exec)
+{
+ return execute("bootpc[[ --bootfile %bootfile%]] --dev %iface%"
+ "[[ --server %server%]][[ --hwaddr %hwaddr%]] "
+ "--returniffail --serverbcast", ifd, exec);
+}
+
+static int ppp_up(struct interface_defn_t *ifd, execfn *exec)
+{
+ return execute("pon[[ %provider%]]", ifd, exec);
+}
+
+static int ppp_down(struct interface_defn_t *ifd, execfn *exec)
+{
+ return execute("poff[[ %provider%]]", ifd, exec);
+}
+
+static int wvdial_up(struct interface_defn_t *ifd, execfn *exec)
+{
+ return execute("start-stop-daemon --start -x wvdial "
+ "-p /var/run/wvdial.%iface% -b -m --[[ %provider%]]", ifd, exec);
+}
+
+static int wvdial_down(struct interface_defn_t *ifd, execfn *exec)
+{
+ return execute("start-stop-daemon --stop -x wvdial "
+ "-p /var/run/wvdial.%iface% -s 2", ifd, exec);
+}
+
+static const struct method_t methods[] = {
+ { "manual", manual_up_down, manual_up_down, },
+ { "wvdial", wvdial_up, wvdial_down, },
+ { "ppp", ppp_up, ppp_down, },
+ { "static", static_up, static_down, },
+ { "bootp", bootp_up, static_down, },
+ { "dhcp", dhcp_up, dhcp_down, },
+ { "loopback", loopback_up, loopback_down, },
+};
+
+static const struct address_family_t addr_inet = {
+ "inet",
+ sizeof(methods) / sizeof(struct method_t),
+ methods
+};
+
+#endif /* if ENABLE_FEATURE_IFUPDOWN_IPV4 */
+
+static char *next_word(char **buf)
+{
+ unsigned short length;
+ char *word;
+
+ if (!buf || !*buf || !**buf) {
+ return NULL;
+ }
+
+ /* Skip over leading whitespace */
+ word = skip_whitespace(*buf);
+
+ /* Skip over comments */
+ if (*word == '#') {
+ return NULL;
+ }
+
+ /* Find the length of this word */
+ length = strcspn(word, " \t\n");
+ if (length == 0) {
+ return NULL;
+ }
+ *buf = word + length;
+ /*DBU:[dave@cray.com] if we are already at EOL dont't increment beyond it */
+ if (**buf) {
+ **buf = '\0';
+ (*buf)++;
+ }
+
+ return word;
+}
+
+static const struct address_family_t *get_address_family(const struct address_family_t *const af[], char *name)
+{
+ int i;
+
+ if (!name)
+ return NULL;
+
+ for (i = 0; af[i]; i++) {
+ if (strcmp(af[i]->name, name) == 0) {
+ return af[i];
+ }
+ }
+ return NULL;
+}
+
+static const struct method_t *get_method(const struct address_family_t *af, char *name)
+{
+ int i;
+
+ if (!name)
+ return NULL;
+
+ for (i = 0; i < af->n_methods; i++) {
+ if (strcmp(af->method[i].name, name) == 0) {
+ return &af->method[i];
+ }
+ }
+ return NULL;
+}
+
+static const llist_t *find_list_string(const llist_t *list, const char *string)
+{
+ if (string == NULL)
+ return NULL;
+
+ while (list) {
+ if (strcmp(list->data, string) == 0) {
+ return list;
+ }
+ list = list->link;
+ }
+ return NULL;
+}
+
+static struct interfaces_file_t *read_interfaces(const char *filename)
+{
+#if ENABLE_FEATURE_IFUPDOWN_MAPPING
+ struct mapping_defn_t *currmap = NULL;
+#endif
+ struct interface_defn_t *currif = NULL;
+ struct interfaces_file_t *defn;
+ FILE *f;
+ char *firstword;
+ char *buf;
+
+ enum { NONE, IFACE, MAPPING } currently_processing = NONE;
+
+ defn = xzalloc(sizeof(struct interfaces_file_t));
+
+ f = xfopen(filename, "r");
+
+ while ((buf = xmalloc_getline(f)) != NULL) {
+ char *buf_ptr = buf;
+
+ firstword = next_word(&buf_ptr);
+ if (firstword == NULL) {
+ free(buf);
+ continue; /* blank line */
+ }
+
+ if (strcmp(firstword, "mapping") == 0) {
+#if ENABLE_FEATURE_IFUPDOWN_MAPPING
+ currmap = xzalloc(sizeof(struct mapping_defn_t));
+
+ while ((firstword = next_word(&buf_ptr)) != NULL) {
+ if (currmap->max_matches == currmap->n_matches) {
+ currmap->max_matches = currmap->max_matches * 2 + 1;
+ currmap->match = xrealloc(currmap->match, sizeof(currmap->match) * currmap->max_matches);
+ }
+
+ currmap->match[currmap->n_matches++] = xstrdup(firstword);
+ }
+ currmap->max_mappings = 0;
+ currmap->n_mappings = 0;
+ currmap->mapping = NULL;
+ currmap->script = NULL;
+ {
+ struct mapping_defn_t **where = &defn->mappings;
+ while (*where != NULL) {
+ where = &(*where)->next;
+ }
+ *where = currmap;
+ currmap->next = NULL;
+ }
+ debug_noise("Added mapping\n");
+#endif
+ currently_processing = MAPPING;
+ } else if (strcmp(firstword, "iface") == 0) {
+ static const struct address_family_t *const addr_fams[] = {
+#if ENABLE_FEATURE_IFUPDOWN_IPV4
+ &addr_inet,
+#endif
+#if ENABLE_FEATURE_IFUPDOWN_IPV6
+ &addr_inet6,
+#endif
+ NULL
+ };
+
+ char *iface_name;
+ char *address_family_name;
+ char *method_name;
+ llist_t *iface_list;
+
+ currif = xzalloc(sizeof(struct interface_defn_t));
+ iface_name = next_word(&buf_ptr);
+ address_family_name = next_word(&buf_ptr);
+ method_name = next_word(&buf_ptr);
+
+ if (buf_ptr == NULL) {
+ bb_error_msg("too few parameters for line \"%s\"", buf);
+ return NULL;
+ }
+
+ /* ship any trailing whitespace */
+ buf_ptr = skip_whitespace(buf_ptr);
+
+ if (buf_ptr[0] != '\0') {
+ bb_error_msg("too many parameters \"%s\"", buf);
+ return NULL;
+ }
+
+ currif->iface = xstrdup(iface_name);
+
+ currif->address_family = get_address_family(addr_fams, address_family_name);
+ if (!currif->address_family) {
+ bb_error_msg("unknown address type \"%s\"", address_family_name);
+ return NULL;
+ }
+
+ currif->method = get_method(currif->address_family, method_name);
+ if (!currif->method) {
+ bb_error_msg("unknown method \"%s\"", method_name);
+ return NULL;
+ }
+
+ for (iface_list = defn->ifaces; iface_list; iface_list = iface_list->link) {
+ struct interface_defn_t *tmp = (struct interface_defn_t *) iface_list->data;
+ if ((strcmp(tmp->iface, currif->iface) == 0) &&
+ (tmp->address_family == currif->address_family)) {
+ bb_error_msg("duplicate interface \"%s\"", tmp->iface);
+ return NULL;
+ }
+ }
+ llist_add_to_end(&(defn->ifaces), (char*)currif);
+
+ debug_noise("iface %s %s %s\n", currif->iface, address_family_name, method_name);
+ currently_processing = IFACE;
+ } else if (strcmp(firstword, "auto") == 0) {
+ while ((firstword = next_word(&buf_ptr)) != NULL) {
+
+ /* Check the interface isnt already listed */
+ if (find_list_string(defn->autointerfaces, firstword)) {
+ bb_perror_msg_and_die("interface declared auto twice \"%s\"", buf);
+ }
+
+ /* Add the interface to the list */
+ llist_add_to_end(&(defn->autointerfaces), xstrdup(firstword));
+ debug_noise("\nauto %s\n", firstword);
+ }
+ currently_processing = NONE;
+ } else {
+ switch (currently_processing) {
+ case IFACE:
+ {
+ int i;
+
+ if (strlen(buf_ptr) == 0) {
+ bb_error_msg("option with empty value \"%s\"", buf);
+ return NULL;
+ }
+
+ if (strcmp(firstword, "up") != 0
+ && strcmp(firstword, "down") != 0
+ && strcmp(firstword, "pre-up") != 0
+ && strcmp(firstword, "post-down") != 0) {
+ for (i = 0; i < currif->n_options; i++) {
+ if (strcmp(currif->option[i].name, firstword) == 0) {
+ bb_error_msg("duplicate option \"%s\"", buf);
+ return NULL;
+ }
+ }
+ }
+ }
+ if (currif->n_options >= currif->max_options) {
+ struct variable_t *opt;
+
+ currif->max_options = currif->max_options + 10;
+ opt = xrealloc(currif->option, sizeof(*opt) * currif->max_options);
+ currif->option = opt;
+ }
+ currif->option[currif->n_options].name = xstrdup(firstword);
+ currif->option[currif->n_options].value = xstrdup(buf_ptr);
+ if (!currif->option[currif->n_options].name) {
+ perror(filename);
+ return NULL;
+ }
+ if (!currif->option[currif->n_options].value) {
+ perror(filename);
+ return NULL;
+ }
+ debug_noise("\t%s=%s\n", currif->option[currif->n_options].name,
+ currif->option[currif->n_options].value);
+ currif->n_options++;
+ break;
+ case MAPPING:
+#if ENABLE_FEATURE_IFUPDOWN_MAPPING
+ if (strcmp(firstword, "script") == 0) {
+ if (currmap->script != NULL) {
+ bb_error_msg("duplicate script in mapping \"%s\"", buf);
+ return NULL;
+ } else {
+ currmap->script = xstrdup(next_word(&buf_ptr));
+ }
+ } else if (strcmp(firstword, "map") == 0) {
+ if (currmap->max_mappings == currmap->n_mappings) {
+ currmap->max_mappings = currmap->max_mappings * 2 + 1;
+ currmap->mapping = xrealloc(currmap->mapping, sizeof(char *) * currmap->max_mappings);
+ }
+ currmap->mapping[currmap->n_mappings] = xstrdup(next_word(&buf_ptr));
+ currmap->n_mappings++;
+ } else {
+ bb_error_msg("misplaced option \"%s\"", buf);
+ return NULL;
+ }
+#endif
+ break;
+ case NONE:
+ default:
+ bb_error_msg("misplaced option \"%s\"", buf);
+ return NULL;
+ }
+ }
+ free(buf);
+ }
+ if (ferror(f) != 0) {
+ bb_perror_msg_and_die("%s", filename);
+ }
+ fclose(f);
+
+ return defn;
+}
+
+static char *setlocalenv(const char *format, const char *name, const char *value)
+{
+ char *result;
+ char *here;
+ char *there;
+
+ result = xasprintf(format, name, value);
+
+ for (here = there = result; *there != '=' && *there; there++) {
+ if (*there == '-')
+ *there = '_';
+ if (isalpha(*there))
+ *there = toupper(*there);
+
+ if (isalnum(*there) || *there == '_') {
+ *here = *there;
+ here++;
+ }
+ }
+ memmove(here, there, strlen(there) + 1);
+
+ return result;
+}
+
+static void set_environ(struct interface_defn_t *iface, const char *mode)
+{
+ char **environend;
+ int i;
+ const int n_env_entries = iface->n_options + 5;
+ char **ppch;
+
+ if (my_environ != NULL) {
+ for (ppch = my_environ; *ppch; ppch++) {
+ free(*ppch);
+ *ppch = NULL;
+ }
+ free(my_environ);
+ }
+ my_environ = xzalloc(sizeof(char *) * (n_env_entries + 1 /* for final NULL */ ));
+ environend = my_environ;
+
+ for (i = 0; i < iface->n_options; i++) {
+ if (strcmp(iface->option[i].name, "up") == 0
+ || strcmp(iface->option[i].name, "down") == 0
+ || strcmp(iface->option[i].name, "pre-up") == 0
+ || strcmp(iface->option[i].name, "post-down") == 0) {
+ continue;
+ }
+ *(environend++) = setlocalenv("IF_%s=%s", iface->option[i].name, iface->option[i].value);
+ }
+
+ *(environend++) = setlocalenv("%s=%s", "IFACE", iface->iface);
+ *(environend++) = setlocalenv("%s=%s", "ADDRFAM", iface->address_family->name);
+ *(environend++) = setlocalenv("%s=%s", "METHOD", iface->method->name);
+ *(environend++) = setlocalenv("%s=%s", "MODE", mode);
+ *(environend++) = setlocalenv("%s=%s", "PATH", startup_PATH);
+}
+
+static int doit(char *str)
+{
+ if (option_mask32 & (OPT_no_act|OPT_verbose)) {
+ puts(str);
+ }
+ if (!(option_mask32 & OPT_no_act)) {
+ pid_t child;
+ int status;
+
+ fflush(NULL);
+ child = fork();
+ switch (child) {
+ case -1: /* failure */
+ return 0;
+ case 0: /* child */
+ execle(DEFAULT_SHELL, DEFAULT_SHELL, "-c", str, NULL, my_environ);
+ exit(127);
+ }
+ waitpid(child, &status, 0);
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static int execute_all(struct interface_defn_t *ifd, const char *opt)
+{
+ int i;
+ char *buf;
+ for (i = 0; i < ifd->n_options; i++) {
+ if (strcmp(ifd->option[i].name, opt) == 0) {
+ if (!doit(ifd->option[i].value)) {
+ return 0;
+ }
+ }
+ }
+
+ buf = xasprintf("run-parts /etc/network/if-%s.d", opt);
+ /* heh, we don't bother free'ing it */
+ return doit(buf);
+}
+
+static int check(char *str)
+{
+ return str != NULL;
+}
+
+static int iface_up(struct interface_defn_t *iface)
+{
+ if (!iface->method->up(iface, check)) return -1;
+ set_environ(iface, "start");
+ if (!execute_all(iface, "pre-up")) return 0;
+ if (!iface->method->up(iface, doit)) return 0;
+ if (!execute_all(iface, "up")) return 0;
+ return 1;
+}
+
+static int iface_down(struct interface_defn_t *iface)
+{
+ if (!iface->method->down(iface,check)) return -1;
+ set_environ(iface, "stop");
+ if (!execute_all(iface, "down")) return 0;
+ if (!iface->method->down(iface, doit)) return 0;
+ if (!execute_all(iface, "post-down")) return 0;
+ return 1;
+}
+
+#if ENABLE_FEATURE_IFUPDOWN_MAPPING
+static int popen2(FILE **in, FILE **out, char *command, ...)
+{
+ va_list ap;
+ char *argv[11] = { command };
+ int argc;
+ int infd[2], outfd[2];
+ pid_t pid;
+
+ argc = 1;
+ va_start(ap, command);
+ while ((argc < 10) && (argv[argc] = va_arg(ap, char *))) {
+ argc++;
+ }
+ argv[argc] = NULL; /* make sure */
+ va_end(ap);
+
+ if (pipe(infd) != 0) {
+ return 0;
+ }
+
+ if (pipe(outfd) != 0) {
+ close(infd[0]);
+ close(infd[1]);
+ return 0;
+ }
+
+ fflush(NULL);
+ switch (pid = fork()) {
+ case -1: /* failure */
+ close(infd[0]);
+ close(infd[1]);
+ close(outfd[0]);
+ close(outfd[1]);
+ return 0;
+ case 0: /* child */
+ dup2(infd[0], 0);
+ dup2(outfd[1], 1);
+ close(infd[0]);
+ close(infd[1]);
+ close(outfd[0]);
+ close(outfd[1]);
+ BB_EXECVP(command, argv);
+ exit(127);
+ default: /* parent */
+ *in = fdopen(infd[1], "w");
+ *out = fdopen(outfd[0], "r");
+ close(infd[0]);
+ close(outfd[1]);
+ return pid;
+ }
+ /* unreached */
+}
+
+static char *run_mapping(char *physical, struct mapping_defn_t * map)
+{
+ FILE *in, *out;
+ int i, status;
+ pid_t pid;
+
+ char *logical = xstrdup(physical);
+
+ /* Run the mapping script. */
+ pid = popen2(&in, &out, map->script, physical, NULL);
+
+ /* popen2() returns 0 on failure. */
+ if (pid == 0)
+ return logical;
+
+ /* Write mappings to stdin of mapping script. */
+ for (i = 0; i < map->n_mappings; i++) {
+ fprintf(in, "%s\n", map->mapping[i]);
+ }
+ fclose(in);
+ waitpid(pid, &status, 0);
+
+ if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
+ /* If the mapping script exited successfully, try to
+ * grab a line of output and use that as the name of the
+ * logical interface. */
+ char *new_logical = xmalloc(MAX_INTERFACE_LENGTH);
+
+ if (fgets(new_logical, MAX_INTERFACE_LENGTH, out)) {
+ /* If we are able to read a line of output from the script,
+ * remove any trailing whitespace and use this value
+ * as the name of the logical interface. */
+ char *pch = new_logical + strlen(new_logical) - 1;
+
+ while (pch >= new_logical && isspace(*pch))
+ *(pch--) = '\0';
+
+ free(logical);
+ logical = new_logical;
+ } else {
+ /* If we are UNABLE to read a line of output, discard our
+ * freshly allocated memory. */
+ free(new_logical);
+ }
+ }
+
+ fclose(out);
+
+ return logical;
+}
+#endif /* FEATURE_IFUPDOWN_MAPPING */
+
+static llist_t *find_iface_state(llist_t *state_list, const char *iface)
+{
+ unsigned short iface_len = strlen(iface);
+ llist_t *search = state_list;
+
+ while (search) {
+ if ((strncmp(search->data, iface, iface_len) == 0) &&
+ (search->data[iface_len] == '=')) {
+ return search;
+ }
+ search = search->link;
+ }
+ return NULL;
+}
+
+int ifupdown_main(int argc, char **argv);
+int ifupdown_main(int argc, char **argv)
+{
+ int (*cmds)(struct interface_defn_t *) = NULL;
+ struct interfaces_file_t *defn;
+ llist_t *state_list = NULL;
+ llist_t *target_list = NULL;
+ const char *interfaces = "/etc/network/interfaces";
+ FILE *state_fp;
+ bool any_failures = 0;
+
+ cmds = iface_down;
+ if (applet_name[2] == 'u') {
+ /* ifup command */
+ cmds = iface_up;
+ }
+
+ getopt32(argc, argv, OPTION_STR, &interfaces);
+ if (argc - optind > 0) {
+ if (DO_ALL) bb_show_usage();
+ } else {
+ if (!DO_ALL) bb_show_usage();
+ }
+
+ debug_noise("reading %s file:\n", interfaces);
+ defn = read_interfaces(interfaces);
+ debug_noise("\ndone reading %s\n\n", interfaces);
+
+ if (!defn) {
+ return EXIT_FAILURE;
+ }
+
+ startup_PATH = getenv("PATH");
+ if (!startup_PATH) startup_PATH = "";
+
+ /* Read the previous state from the state file */
+ state_fp = fopen("/var/run/ifstate", "r");
+ if (state_fp) {
+ char *start, *end_ptr;
+ while ((start = xmalloc_fgets(state_fp)) != NULL) {
+ /* We should only need to check for a single character */
+ end_ptr = start + strcspn(start, " \t\n");
+ *end_ptr = '\0';
+ llist_add_to(&state_list, start);
+ }
+ fclose(state_fp);
+ }
+
+ /* Create a list of interfaces to work on */
+ if (DO_ALL) {
+ if (cmds == iface_up) {
+ target_list = defn->autointerfaces;
+ } else {
+ /* iface_down */
+ const llist_t *list = state_list;
+ while (list) {
+ llist_add_to_end(&target_list, xstrdup(list->data));
+ list = list->link;
+ }
+ target_list = defn->autointerfaces;
+ }
+ } else {
+ llist_add_to_end(&target_list, argv[optind]);
+ }
+
+ /* Update the interfaces */
+ while (target_list) {
+ llist_t *iface_list;
+ struct interface_defn_t *currif;
+ char *iface;
+ char *liface;
+ char *pch;
+ bool okay = 0;
+ unsigned cmds_ret;
+
+ iface = xstrdup(target_list->data);
+ target_list = target_list->link;
+
+ pch = strchr(iface, '=');
+ if (pch) {
+ *pch = '\0';
+ liface = xstrdup(pch + 1);
+ } else {
+ liface = xstrdup(iface);
+ }
+
+ if (!FORCE) {
+ const llist_t *iface_state = find_iface_state(state_list, iface);
+
+ if (cmds == iface_up) {
+ /* ifup */
+ if (iface_state) {
+ bb_error_msg("interface %s already configured", iface);
+ continue;
+ }
+ } else {
+ /* ifdown */
+ if (!iface_state) {
+ bb_error_msg("interface %s not configured", iface);
+ continue;
+ }
+ }
+ }
+
+#if ENABLE_FEATURE_IFUPDOWN_MAPPING
+ if ((cmds == iface_up) && !NO_MAPPINGS) {
+ struct mapping_defn_t *currmap;
+
+ for (currmap = defn->mappings; currmap; currmap = currmap->next) {
+ int i;
+ for (i = 0; i < currmap->n_matches; i++) {
+ if (fnmatch(currmap->match[i], liface, 0) != 0)
+ continue;
+ if (VERBOSE) {
+ printf("Running mapping script %s on %s\n", currmap->script, liface);
+ }
+ liface = run_mapping(iface, currmap);
+ break;
+ }
+ }
+ }
+#endif
+
+ iface_list = defn->ifaces;
+ while (iface_list) {
+ currif = (struct interface_defn_t *) iface_list->data;
+ if (strcmp(liface, currif->iface) == 0) {
+ char *oldiface = currif->iface;
+
+ okay = 1;
+ currif->iface = iface;
+
+ debug_noise("\nConfiguring interface %s (%s)\n", liface, currif->address_family->name);
+
+ /* Call the cmds function pointer, does either iface_up() or iface_down() */
+ cmds_ret = cmds(currif);
+ if (cmds_ret == -1) {
+ bb_error_msg("don't seem to have all the variables for %s/%s",
+ liface, currif->address_family->name);
+ any_failures = 1;
+ } else if (cmds_ret == 0) {
+ any_failures = 1;
+ }
+
+ currif->iface = oldiface;
+ }
+ iface_list = iface_list->link;
+ }
+ if (VERBOSE) {
+ puts("");
+ }
+
+ if (!okay && !FORCE) {
+ bb_error_msg("ignoring unknown interface %s", liface);
+ any_failures = 1;
+ } else {
+ llist_t *iface_state = find_iface_state(state_list, iface);
+
+ if (cmds == iface_up) {
+ char * const newiface = xasprintf("%s=%s", iface, liface);
+ if (iface_state == NULL) {
+ llist_add_to_end(&state_list, newiface);
+ } else {
+ free(iface_state->data);
+ iface_state->data = newiface;
+ }
+ } else {
+ /* Remove an interface from state_list */
+ llist_unlink(&state_list, iface_state);
+ free(llist_pop(&iface_state));
+ }
+ }
+ }
+
+ /* Actually write the new state */
+ if (!NO_ACT) {
+ state_fp = xfopen("/var/run/ifstate", "w");
+ while (state_list) {
+ if (state_list->data) {
+ fprintf(state_fp, "%s\n", state_list->data);
+ }
+ state_list = state_list->link;
+ }
+ fclose(state_fp);
+ }
+
+ return any_failures;
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/inetd.c b/i/pc104/initrd/conf/busybox/networking/inetd.c
new file mode 100644
index 0000000..c037d11
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/inetd.c
@@ -0,0 +1,1791 @@
+/* vi: set sw=4 ts=4: */
+/* $Slackware: inetd.c 1.79s 2001/02/06 13:18:00 volkerdi Exp $ */
+/* $OpenBSD: inetd.c,v 1.79 2001/01/30 08:30:57 deraadt Exp $ */
+/* $NetBSD: inetd.c,v 1.11 1996/02/22 11:14:41 mycroft Exp $ */
+/* Busybox port by Vladimir Oleynik (C) 2001-2005 <dzo@simtreas.ru> */
+/*
+ * Copyright (c) 1983,1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* Inetd - Internet super-server
+ *
+ * This program invokes all internet services as needed.
+ * connection-oriented services are invoked each time a
+ * connection is made, by creating a process. This process
+ * is passed the connection as file descriptor 0 and is
+ * expected to do a getpeername to find out the source host
+ * and port.
+ *
+ * Datagram oriented services are invoked when a datagram
+ * arrives; a process is created and passed a pending message
+ * on file descriptor 0. Datagram servers may either connect
+ * to their peer, freeing up the original socket for inetd
+ * to receive further messages on, or "take over the socket",
+ * processing all arriving datagrams and, eventually, timing
+ * out. The first type of server is said to be "multi-threaded";
+ * the second type of server "single-threaded".
+ *
+ * Inetd uses a configuration file which is read at startup
+ * and, possibly, at some later time in response to a hangup signal.
+ * The configuration file is "free format" with fields given in the
+ * order shown below. Continuation lines for an entry must begin with
+ * a space or tab. All fields must be present in each entry.
+ *
+ * service name must be in /etc/services
+ * socket type stream/dgram/raw/rdm/seqpacket
+ * protocol must be in /etc/protocols
+ * wait/nowait[.max] single-threaded/multi-threaded, max #
+ * user[.group] or user[:group] user/group to run daemon as
+ * server program full path name
+ * server program arguments maximum of MAXARGS (20)
+ *
+ * For RPC services
+ * service name/version must be in /etc/rpc
+ * socket type stream/dgram/raw/rdm/seqpacket
+ * protocol must be in /etc/protocols
+ * wait/nowait[.max] single-threaded/multi-threaded
+ * user[.group] or user[:group] user to run daemon as
+ * server program full path name
+ * server program arguments maximum of MAXARGS (20)
+ *
+ * For non-RPC services, the "service name" can be of the form
+ * hostaddress:servicename, in which case the hostaddress is used
+ * as the host portion of the address to listen on. If hostaddress
+ * consists of a single `*' character, INADDR_ANY is used.
+ *
+ * A line can also consist of just
+ * hostaddress:
+ * where hostaddress is as in the preceding paragraph. Such a line must
+ * have no further fields; the specified hostaddress is remembered and
+ * used for all further lines that have no hostaddress specified,
+ * until the next such line (or EOF). (This is why * is provided to
+ * allow explicit specification of INADDR_ANY.) A line
+ * *:
+ * is implicitly in effect at the beginning of the file.
+ *
+ * The hostaddress specifier may (and often will) contain dots;
+ * the service name must not.
+ *
+ * For RPC services, host-address specifiers are accepted and will
+ * work to some extent; however, because of limitations in the
+ * portmapper interface, it will not work to try to give more than
+ * one line for any given RPC service, even if the host-address
+ * specifiers are different.
+ *
+ * Comment lines are indicated by a `#' in column 1.
+ */
+
+/* inetd rules for passing file descriptors to children
+ * (http://www.freebsd.org/cgi/man.cgi?query=inetd):
+ *
+ * The wait/nowait entry specifies whether the server that is invoked by
+ * inetd will take over the socket associated with the service access point,
+ * and thus whether inetd should wait for the server to exit before listen-
+ * ing for new service requests. Datagram servers must use "wait", as
+ * they are always invoked with the original datagram socket bound to the
+ * specified service address. These servers must read at least one datagram
+ * from the socket before exiting. If a datagram server connects to its
+ * peer, freeing the socket so inetd can receive further messages on the
+ * socket, it is said to be a "multi-threaded" server; it should read one
+ * datagram from the socket and create a new socket connected to the peer.
+ * It should fork, and the parent should then exit to allow inetd to check
+ * for new service requests to spawn new servers. Datagram servers which
+ * process all incoming datagrams on a socket and eventually time out are
+ * said to be "single-threaded". The comsat(8), (biff(1)) and talkd(8)
+ * utilities are both examples of the latter type of datagram server. The
+ * tftpd(8) utility is an example of a multi-threaded datagram server.
+ *
+ * Servers using stream sockets generally are multi-threaded and use the
+ * "nowait" entry. Connection requests for these services are accepted by
+ * inetd, and the server is given only the newly-accepted socket connected
+ * to a client of the service. Most stream-based services operate in this
+ * manner. Stream-based servers that use "wait" are started with the lis-
+ * tening service socket, and must accept at least one connection request
+ * before exiting. Such a server would normally accept and process incoming
+ * connection requests until a timeout.
+ */
+
+/* Here's the scoop concerning the user[.:]group feature:
+ *
+ * 1) set-group-option off.
+ *
+ * a) user = root: NO setuid() or setgid() is done
+ *
+ * b) other: setgid(primary group as found in passwd)
+ * initgroups(name, primary group)
+ * setuid()
+ *
+ * 2) set-group-option on.
+ *
+ * a) user = root: setgid(specified group)
+ * NO initgroups()
+ * NO setuid()
+ *
+ * b) other: setgid(specified group)
+ * initgroups(name, specified group)
+ * setuid()
+ */
+
+#include "busybox.h"
+#include <syslog.h>
+#include <sys/un.h>
+
+//#define ENABLE_FEATURE_INETD_RPC 1
+//#define ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_ECHO 1
+//#define ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD 1
+//#define ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_TIME 1
+//#define ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME 1
+//#define ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN 1
+//#define ENABLE_FEATURE_IPV6 1
+
+#if ENABLE_FEATURE_INETD_RPC
+#include <rpc/rpc.h>
+#include <rpc/pmap_clnt.h>
+#endif
+
+#define _PATH_INETDCONF "/etc/inetd.conf"
+#define _PATH_INETDPID "/var/run/inetd.pid"
+
+
+#define CNT_INTVL 60 /* servers in CNT_INTVL sec. */
+#define RETRYTIME (60*10) /* retry after bind or server fail */
+
+#ifndef RLIMIT_NOFILE
+#define RLIMIT_NOFILE RLIMIT_OFILE
+#endif
+
+#ifndef OPEN_MAX
+#define OPEN_MAX 64
+#endif
+
+/* Reserve some descriptors, 3 stdio + at least: 1 log, 1 conf. file */
+#define FD_MARGIN 8
+static rlim_t rlim_ofile_cur = OPEN_MAX;
+static struct rlimit rlim_ofile;
+
+
+/* Check unsupporting builtin */
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_ECHO || \
+ ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD || \
+ ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_TIME || \
+ ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME || \
+ ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN
+# define INETD_FEATURE_ENABLED
+#endif
+
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_ECHO || \
+ ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD || \
+ ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN
+# define INETD_SETPROCTITLE
+#endif
+
+typedef struct servtab {
+ char *se_hostaddr; /* host address to listen on */
+ char *se_service; /* name of service */
+ int se_socktype; /* type of socket to use */
+ int se_family; /* address family */
+ char *se_proto; /* protocol used */
+#if ENABLE_FEATURE_INETD_RPC
+ int se_rpcprog; /* rpc program number */
+ int se_rpcversl; /* rpc program lowest version */
+ int se_rpcversh; /* rpc program highest version */
+#define isrpcservice(sep) ((sep)->se_rpcversl != 0)
+#else
+#define isrpcservice(sep) 0
+#endif
+ pid_t se_wait; /* single threaded server */
+ short se_checked; /* looked at during merge */
+ char *se_user; /* user name to run as */
+ char *se_group; /* group name to run as */
+#ifdef INETD_FEATURE_ENABLED
+ const struct builtin *se_bi; /* if built-in, description */
+#endif
+ char *se_server; /* server program */
+#define MAXARGV 20
+ char *se_argv[MAXARGV + 1]; /* program arguments */
+ int se_fd; /* open descriptor */
+ union {
+ struct sockaddr se_un_ctrladdr;
+ struct sockaddr_in se_un_ctrladdr_in;
+#if ENABLE_FEATURE_IPV6
+ struct sockaddr_in6 se_un_ctrladdr_in6;
+#endif
+ struct sockaddr_un se_un_ctrladdr_un;
+ } se_un; /* bound address */
+#define se_ctrladdr se_un.se_un_ctrladdr
+#define se_ctrladdr_in se_un.se_un_ctrladdr_in
+#define se_ctrladdr_in6 se_un.se_un_ctrladdr_in6
+#define se_ctrladdr_un se_un.se_un_ctrladdr_un
+ int se_ctrladdr_size;
+ int se_max; /* max # of instances of this service */
+ int se_count; /* number started since se_time */
+ struct timeval se_time; /* start of se_count */
+ struct servtab *se_next;
+} servtab_t;
+
+static servtab_t *servtab;
+
+#ifdef INETD_FEATURE_ENABLED
+struct builtin {
+ const char *bi_service; /* internally provided service name */
+ int bi_socktype; /* type of socket supported */
+ short bi_fork; /* 1 if should fork before call */
+ short bi_wait; /* 1 if should wait for child */
+ void (*bi_fn) (int, servtab_t *);
+};
+
+ /* Echo received data */
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_ECHO
+static void echo_stream(int, servtab_t *);
+static void echo_dg(int, servtab_t *);
+#endif
+ /* Internet /dev/null */
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD
+static void discard_stream(int, servtab_t *);
+static void discard_dg(int, servtab_t *);
+#endif
+ /* Return 32 bit time since 1900 */
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_TIME
+static void machtime_stream(int, servtab_t *);
+static void machtime_dg(int, servtab_t *);
+#endif
+ /* Return human-readable time */
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME
+static void daytime_stream(int, servtab_t *);
+static void daytime_dg(int, servtab_t *);
+#endif
+ /* Familiar character generator */
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN
+static void chargen_stream(int, servtab_t *);
+static void chargen_dg(int, servtab_t *);
+#endif
+
+static const struct builtin builtins[] = {
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_ECHO
+ /* Echo received data */
+ {"echo", SOCK_STREAM, 1, 0, echo_stream,},
+ {"echo", SOCK_DGRAM, 0, 0, echo_dg,},
+#endif
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD
+ /* Internet /dev/null */
+ {"discard", SOCK_STREAM, 1, 0, discard_stream,},
+ {"discard", SOCK_DGRAM, 0, 0, discard_dg,},
+#endif
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_TIME
+ /* Return 32 bit time since 1900 */
+ {"time", SOCK_STREAM, 0, 0, machtime_stream,},
+ {"time", SOCK_DGRAM, 0, 0, machtime_dg,},
+#endif
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME
+ /* Return human-readable time */
+ {"daytime", SOCK_STREAM, 0, 0, daytime_stream,},
+ {"daytime", SOCK_DGRAM, 0, 0, daytime_dg,},
+#endif
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN
+ /* Familiar character generator */
+ {"chargen", SOCK_STREAM, 1, 0, chargen_stream,},
+ {"chargen", SOCK_DGRAM, 0, 0, chargen_dg,},
+#endif
+ {NULL, 0, 0, 0, NULL}
+};
+#endif /* INETD_FEATURE_ENABLED */
+
+static int global_queuelen = 128;
+static int nsock, maxsock;
+static fd_set allsock;
+static int toomany;
+static int timingout;
+static struct servent *sp;
+static uid_t uid;
+
+static const char *CONFIG = _PATH_INETDCONF;
+
+static FILE *fconfig;
+static char line[1024];
+static char *defhost;
+
+/* xstrdup(NULL) returns NULL, but this one
+ * will return newly-allocated "" if called with NULL arg
+ * TODO: audit whether this makes any real difference
+ */
+static char *xxstrdup(char *cp)
+{
+ return xstrdup(cp ? cp : "");
+}
+
+static int setconfig(void)
+{
+ free(defhost);
+ defhost = xstrdup("*");
+ if (fconfig != NULL) {
+ fseek(fconfig, 0L, SEEK_SET);
+ return 1;
+ }
+ fconfig = fopen(CONFIG, "r");
+ return (fconfig != NULL);
+}
+
+static void endconfig(void)
+{
+ if (fconfig) {
+ (void) fclose(fconfig);
+ fconfig = NULL;
+ }
+ free(defhost);
+ defhost = 0;
+}
+
+#if ENABLE_FEATURE_INETD_RPC
+static void register_rpc(servtab_t *sep)
+{
+ int n;
+ struct sockaddr_in ir_sin;
+ struct protoent *pp;
+ socklen_t size;
+
+ if ((pp = getprotobyname(sep->se_proto + 4)) == NULL) {
+ bb_perror_msg("%s: getproto", sep->se_proto);
+ return;
+ }
+ size = sizeof ir_sin;
+ if (getsockname(sep->se_fd, (struct sockaddr *) &ir_sin, &size) < 0) {
+ bb_perror_msg("%s/%s: getsockname",
+ sep->se_service, sep->se_proto);
+ return;
+ }
+
+ for (n = sep->se_rpcversl; n <= sep->se_rpcversh; n++) {
+ (void) pmap_unset(sep->se_rpcprog, n);
+ if (!pmap_set(sep->se_rpcprog, n, pp->p_proto, ntohs(ir_sin.sin_port)))
+ bb_perror_msg("%s %s: pmap_set: %u %u %u %u",
+ sep->se_service, sep->se_proto,
+ sep->se_rpcprog, n, pp->p_proto, ntohs(ir_sin.sin_port));
+ }
+}
+
+static void unregister_rpc(servtab_t *sep)
+{
+ int n;
+
+ for (n = sep->se_rpcversl; n <= sep->se_rpcversh; n++) {
+ if (!pmap_unset(sep->se_rpcprog, n))
+ bb_error_msg("pmap_unset(%u, %u)", sep->se_rpcprog, n);
+ }
+}
+#endif /* FEATURE_INETD_RPC */
+
+static void freeconfig(servtab_t *cp)
+{
+ int i;
+
+ free(cp->se_hostaddr);
+ free(cp->se_service);
+ free(cp->se_proto);
+ free(cp->se_user);
+ free(cp->se_group);
+ free(cp->se_server);
+ for (i = 0; i < MAXARGV; i++)
+ free(cp->se_argv[i]);
+}
+
+static int bump_nofile(void)
+{
+#define FD_CHUNK 32
+
+ struct rlimit rl;
+
+ if (getrlimit(RLIMIT_NOFILE, &rl) < 0) {
+ bb_perror_msg("getrlimit");
+ return -1;
+ }
+ rl.rlim_cur = MIN(rl.rlim_max, rl.rlim_cur + FD_CHUNK);
+ rl.rlim_cur = MIN(FD_SETSIZE, rl.rlim_cur + FD_CHUNK);
+ if (rl.rlim_cur <= rlim_ofile_cur) {
+ bb_error_msg("bump_nofile: cannot extend file limit, max = %d",
+ (int) rl.rlim_cur);
+ return -1;
+ }
+
+ if (setrlimit(RLIMIT_NOFILE, &rl) < 0) {
+ bb_perror_msg("setrlimit");
+ return -1;
+ }
+
+ rlim_ofile_cur = rl.rlim_cur;
+ return 0;
+}
+
+static void setup(servtab_t *sep)
+{
+ int r;
+
+ sep->se_fd = socket(sep->se_family, sep->se_socktype, 0);
+ if (sep->se_fd < 0) {
+ bb_perror_msg("%s/%s: socket", sep->se_service, sep->se_proto);
+ return;
+ }
+ if (setsockopt_reuseaddr(sep->se_fd) < 0)
+ bb_perror_msg("setsockopt(SO_REUSEADDR)");
+
+#if ENABLE_FEATURE_INETD_RPC
+ if (isrpcservice(sep)) {
+ struct passwd *pwd;
+
+ /*
+ * for RPC services, attempt to use a reserved port
+ * if they are going to be running as root.
+ *
+ * Also, zero out the port for all RPC services; let bind()
+ * find one.
+ */
+ sep->se_ctrladdr_in.sin_port = 0;
+ if (sep->se_user && (pwd = getpwnam(sep->se_user)) &&
+ pwd->pw_uid == 0 && uid == 0)
+ r = bindresvport(sep->se_fd, &sep->se_ctrladdr_in);
+ else {
+ r = bind(sep->se_fd, &sep->se_ctrladdr, sep->se_ctrladdr_size);
+ if (r == 0) {
+ socklen_t len = sep->se_ctrladdr_size;
+ int saveerrno = errno;
+
+ /* update se_ctrladdr_in.sin_port */
+ r = getsockname(sep->se_fd, &sep->se_ctrladdr, &len);
+ if (r <= 0)
+ errno = saveerrno;
+ }
+ }
+ } else
+#endif
+ r = bind(sep->se_fd, &sep->se_ctrladdr, sep->se_ctrladdr_size);
+ if (r < 0) {
+ bb_perror_msg("%s/%s (%d): bind",
+ sep->se_service, sep->se_proto, sep->se_ctrladdr.sa_family);
+ close(sep->se_fd);
+ sep->se_fd = -1;
+ if (!timingout) {
+ timingout = 1;
+ alarm(RETRYTIME);
+ }
+ return;
+ }
+ if (sep->se_socktype == SOCK_STREAM)
+ listen(sep->se_fd, global_queuelen);
+
+ FD_SET(sep->se_fd, &allsock);
+ nsock++;
+ if (sep->se_fd > maxsock) {
+ maxsock = sep->se_fd;
+ if ((rlim_t)maxsock > rlim_ofile_cur - FD_MARGIN)
+ bump_nofile();
+ }
+}
+
+static char *nextline(void)
+{
+ char *cp;
+ FILE *fd = fconfig;
+
+ if (fgets(line, sizeof(line), fd) == NULL)
+ return NULL;
+ cp = strchr(line, '\n');
+ if (cp)
+ *cp = '\0';
+ return line;
+}
+
+static char *skip(char **cpp) /* int report; */
+{
+ char *cp = *cpp;
+ char *start;
+
+/* erp: */
+ if (*cpp == NULL) {
+ /* if (report) */
+ /* bb_error_msg("syntax error in inetd config file"); */
+ return NULL;
+ }
+
+again:
+ while (*cp == ' ' || *cp == '\t')
+ cp++;
+ if (*cp == '\0') {
+ int c;
+
+ c = getc(fconfig);
+ (void) ungetc(c, fconfig);
+ if (c == ' ' || c == '\t')
+ if ((cp = nextline()))
+ goto again;
+ *cpp = NULL;
+ /* goto erp; */
+ return NULL;
+ }
+ start = cp;
+ while (*cp && *cp != ' ' && *cp != '\t')
+ cp++;
+ if (*cp != '\0')
+ *cp++ = '\0';
+ /* if ((*cpp = cp) == NULL) */
+ /* goto erp; */
+
+ *cpp = cp;
+ return start;
+}
+
+static servtab_t *new_servtab(void)
+{
+ return xmalloc(sizeof(servtab_t));
+}
+
+static servtab_t *dupconfig(servtab_t *sep)
+{
+ servtab_t *newtab;
+ int argc;
+
+ newtab = new_servtab();
+ memset(newtab, 0, sizeof(servtab_t));
+ newtab->se_service = xstrdup(sep->se_service);
+ newtab->se_socktype = sep->se_socktype;
+ newtab->se_family = sep->se_family;
+ newtab->se_proto = xstrdup(sep->se_proto);
+#if ENABLE_FEATURE_INETD_RPC
+ newtab->se_rpcprog = sep->se_rpcprog;
+ newtab->se_rpcversl = sep->se_rpcversl;
+ newtab->se_rpcversh = sep->se_rpcversh;
+#endif
+ newtab->se_wait = sep->se_wait;
+ newtab->se_user = xstrdup(sep->se_user);
+ newtab->se_group = xstrdup(sep->se_group);
+#ifdef INETD_FEATURE_ENABLED
+ newtab->se_bi = sep->se_bi;
+#endif
+ newtab->se_server = xstrdup(sep->se_server);
+
+ for (argc = 0; argc <= MAXARGV; argc++)
+ newtab->se_argv[argc] = xstrdup(sep->se_argv[argc]);
+ newtab->se_max = sep->se_max;
+
+ return newtab;
+}
+
+static servtab_t *getconfigent(void)
+{
+ servtab_t *sep;
+ int argc;
+ char *cp, *arg;
+ char *hostdelim;
+ servtab_t *nsep;
+ servtab_t *psep;
+
+ sep = new_servtab();
+
+ /* memset(sep, 0, sizeof *sep); */
+ more:
+ /* freeconfig(sep); */
+
+ while ((cp = nextline()) && *cp == '#') /* skip comment line */;
+ if (cp == NULL) {
+ /* free(sep); */
+ return NULL;
+ }
+
+ memset((char *) sep, 0, sizeof *sep);
+ arg = skip(&cp);
+ if (arg == NULL) {
+ /* A blank line. */
+ goto more;
+ }
+
+ /* Check for a host name. */
+ hostdelim = strrchr(arg, ':');
+ if (hostdelim) {
+ *hostdelim = '\0';
+ sep->se_hostaddr = xstrdup(arg);
+ arg = hostdelim + 1;
+ /*
+ * If the line is of the form `host:', then just change the
+ * default host for the following lines.
+ */
+ if (*arg == '\0') {
+ arg = skip(&cp);
+ if (cp == NULL) {
+ free(defhost);
+ defhost = sep->se_hostaddr;
+ goto more;
+ }
+ }
+ } else
+ sep->se_hostaddr = xxstrdup(defhost);
+
+ sep->se_service = xxstrdup(arg);
+ arg = skip(&cp);
+
+ if (strcmp(arg, "stream") == 0)
+ sep->se_socktype = SOCK_STREAM;
+ else if (strcmp(arg, "dgram") == 0)
+ sep->se_socktype = SOCK_DGRAM;
+ else if (strcmp(arg, "rdm") == 0)
+ sep->se_socktype = SOCK_RDM;
+ else if (strcmp(arg, "seqpacket") == 0)
+ sep->se_socktype = SOCK_SEQPACKET;
+ else if (strcmp(arg, "raw") == 0)
+ sep->se_socktype = SOCK_RAW;
+ else
+ sep->se_socktype = -1;
+
+ sep->se_proto = xxstrdup(skip(&cp));
+
+ if (strcmp(sep->se_proto, "unix") == 0) {
+ sep->se_family = AF_UNIX;
+ } else {
+ sep->se_family = AF_INET;
+ if (sep->se_proto[strlen(sep->se_proto) - 1] == '6')
+#if ENABLE_FEATURE_IPV6
+ sep->se_family = AF_INET6;
+#else
+ bb_error_msg("%s: IPV6 not supported", sep->se_proto);
+#endif
+ if (strncmp(sep->se_proto, "rpc/", 4) == 0) {
+#if ENABLE_FEATURE_INETD_RPC
+ char *p, *ccp;
+ long l;
+
+ p = strchr(sep->se_service, '/');
+ if (p == 0) {
+ bb_error_msg("%s: no rpc version", sep->se_service);
+ goto more;
+ }
+ *p++ = '\0';
+ l = strtol(p, &ccp, 0);
+ if (ccp == p || l < 0 || l > INT_MAX) {
+ badafterall:
+ bb_error_msg("%s/%s: bad rpc version", sep->se_service, p);
+ goto more;
+ }
+ sep->se_rpcversl = sep->se_rpcversh = l;
+ if (*ccp == '-') {
+ p = ccp + 1;
+ l = strtol(p, &ccp, 0);
+ if (ccp == p || l < 0 || l > INT_MAX || l < sep->se_rpcversl || *ccp)
+ goto badafterall;
+ sep->se_rpcversh = l;
+ } else if (*ccp != '\0')
+ goto badafterall;
+#else
+ bb_error_msg("%s: rpc services not supported", sep->se_service);
+#endif
+ }
+ }
+ arg = skip(&cp);
+ if (arg == NULL)
+ goto more;
+
+ {
+ char *s = strchr(arg, '.');
+ if (s) {
+ *s++ = '\0';
+ sep->se_max = xatoi(s);
+ } else
+ sep->se_max = toomany;
+ }
+ sep->se_wait = strcmp(arg, "wait") == 0;
+ /* if ((arg = skip(&cp, 1)) == NULL) */
+ /* goto more; */
+ sep->se_user = xxstrdup(skip(&cp));
+ arg = strchr(sep->se_user, '.');
+ if (arg == NULL)
+ arg = strchr(sep->se_user, ':');
+ if (arg) {
+ *arg++ = '\0';
+ sep->se_group = xstrdup(arg);
+ }
+ /* if ((arg = skip(&cp, 1)) == NULL) */
+ /* goto more; */
+
+ sep->se_server = xxstrdup(skip(&cp));
+ if (strcmp(sep->se_server, "internal") == 0) {
+#ifdef INETD_FEATURE_ENABLED
+ const struct builtin *bi;
+
+ for (bi = builtins; bi->bi_service; bi++)
+ if (bi->bi_socktype == sep->se_socktype &&
+ strcmp(bi->bi_service, sep->se_service) == 0)
+ break;
+ if (bi->bi_service == 0) {
+ bb_error_msg("internal service %s unknown", sep->se_service);
+ goto more;
+ }
+ sep->se_bi = bi;
+ sep->se_wait = bi->bi_wait;
+#else
+ bb_perror_msg("internal service %s unknown", sep->se_service);
+ goto more;
+#endif
+ }
+#ifdef INETD_FEATURE_ENABLED
+ else
+ sep->se_bi = NULL;
+#endif
+ argc = 0;
+ for (arg = skip(&cp); cp; arg = skip(&cp)) {
+ if (argc < MAXARGV)
+ sep->se_argv[argc++] = xxstrdup(arg);
+ }
+ while (argc <= MAXARGV)
+ sep->se_argv[argc++] = NULL;
+
+ /*
+ * Now that we've processed the entire line, check if the hostname
+ * specifier was a comma separated list of hostnames. If so
+ * we'll make new entries for each address.
+ */
+ while ((hostdelim = strrchr(sep->se_hostaddr, ',')) != NULL) {
+ nsep = dupconfig(sep);
+
+ /*
+ * NULL terminate the hostname field of the existing entry,
+ * and make a dup for the new entry.
+ */
+ *hostdelim++ = '\0';
+ nsep->se_hostaddr = xstrdup(hostdelim);
+
+ nsep->se_next = sep->se_next;
+ sep->se_next = nsep;
+ }
+
+ nsep = sep;
+ while (nsep != NULL) {
+ nsep->se_checked = 1;
+ if (nsep->se_family == AF_INET) {
+ if (LONE_CHAR(nsep->se_hostaddr, '*'))
+ nsep->se_ctrladdr_in.sin_addr.s_addr = INADDR_ANY;
+ else if (!inet_aton(nsep->se_hostaddr, &nsep->se_ctrladdr_in.sin_addr)) {
+ struct hostent *hp;
+
+ hp = gethostbyname(nsep->se_hostaddr);
+ if (hp == 0) {
+ bb_error_msg("%s: unknown host", nsep->se_hostaddr);
+ nsep->se_checked = 0;
+ goto skip;
+ } else if (hp->h_addrtype != AF_INET) {
+ bb_error_msg("%s: address isn't an Internet "
+ "address", nsep->se_hostaddr);
+ nsep->se_checked = 0;
+ goto skip;
+ } else {
+ int i = 1;
+
+ memmove(&nsep->se_ctrladdr_in.sin_addr,
+ hp->h_addr_list[0], sizeof(struct in_addr));
+ while (hp->h_addr_list[i] != NULL) {
+ psep = dupconfig(nsep);
+ psep->se_hostaddr = xxstrdup(nsep->se_hostaddr);
+ psep->se_checked = 1;
+ memmove(&psep->se_ctrladdr_in.sin_addr,
+ hp->h_addr_list[i], sizeof(struct in_addr));
+ psep->se_ctrladdr_size = sizeof(psep->se_ctrladdr_in);
+ i++;
+ /* Prepend to list, don't want to look up */
+ /* its hostname again. */
+ psep->se_next = sep;
+ sep = psep;
+ }
+ }
+ }
+ }
+/* XXX BUG?: is this skip: label supposed to remain? */
+ skip:
+ nsep = nsep->se_next;
+ }
+
+ /*
+ * Finally, free any entries which failed the gethostbyname
+ * check.
+ */
+ psep = NULL;
+ nsep = sep;
+ while (nsep != NULL) {
+ servtab_t *tsep;
+
+ if (nsep->se_checked == 0) {
+ tsep = nsep;
+ if (psep == NULL) {
+ sep = nsep->se_next;
+ nsep = sep;
+ } else {
+ nsep = nsep->se_next;
+ psep->se_next = nsep;
+ }
+ freeconfig(tsep);
+ } else {
+ nsep->se_checked = 0;
+ psep = nsep;
+ nsep = nsep->se_next;
+ }
+ }
+
+ return sep;
+}
+
+#define Block_Using_Signals(m) do { \
+ sigemptyset(&m); \
+ sigaddset(&m, SIGCHLD); \
+ sigaddset(&m, SIGHUP); \
+ sigaddset(&m, SIGALRM); \
+ sigprocmask(SIG_BLOCK, &m, NULL); \
+} while (0)
+
+static servtab_t *enter(servtab_t *cp)
+{
+ servtab_t *sep;
+ sigset_t omask;
+
+ sep = new_servtab();
+ *sep = *cp;
+ sep->se_fd = -1;
+#if ENABLE_FEATURE_INETD_RPC
+ sep->se_rpcprog = -1;
+#endif
+ Block_Using_Signals(omask);
+ sep->se_next = servtab;
+ servtab = sep;
+ sigprocmask(SIG_UNBLOCK, &omask, NULL);
+ return sep;
+}
+
+static int matchconf(servtab_t *old, servtab_t *new)
+{
+ if (strcmp(old->se_service, new->se_service) != 0)
+ return 0;
+
+ if (strcmp(old->se_hostaddr, new->se_hostaddr) != 0)
+ return 0;
+
+ if (strcmp(old->se_proto, new->se_proto) != 0)
+ return 0;
+
+ /*
+ * If the new servtab is bound to a specific address, check that the
+ * old servtab is bound to the same entry. If the new service is not
+ * bound to a specific address then the check of se_hostaddr above
+ * is sufficient.
+ */
+
+ if (old->se_family == AF_INET && new->se_family == AF_INET &&
+ memcmp(&old->se_ctrladdr_in.sin_addr,
+ &new->se_ctrladdr_in.sin_addr,
+ sizeof(new->se_ctrladdr_in.sin_addr)) != 0)
+ return 0;
+
+#if ENABLE_FEATURE_IPV6
+ if (old->se_family == AF_INET6 && new->se_family == AF_INET6 &&
+ memcmp(&old->se_ctrladdr_in6.sin6_addr,
+ &new->se_ctrladdr_in6.sin6_addr,
+ sizeof(new->se_ctrladdr_in6.sin6_addr)) != 0)
+ return 0;
+#endif
+ return 1;
+}
+
+static void config(int sig ATTRIBUTE_UNUSED)
+{
+ servtab_t *sep, *cp, **sepp;
+ sigset_t omask;
+ size_t n;
+ char protoname[10];
+
+ if (!setconfig()) {
+ bb_perror_msg("%s", CONFIG);
+ return;
+ }
+ for (sep = servtab; sep; sep = sep->se_next)
+ sep->se_checked = 0;
+ cp = getconfigent();
+ while (cp != NULL) {
+ for (sep = servtab; sep; sep = sep->se_next)
+ if (matchconf(sep, cp))
+ break;
+
+ if (sep != 0) {
+ int i;
+
+#define SWAP(type, a, b) do {type c=(type)a; a=(type)b; b=(type)c;} while (0)
+
+ Block_Using_Signals(omask);
+ /*
+ * sep->se_wait may be holding the pid of a daemon
+ * that we're waiting for. If so, don't overwrite
+ * it unless the config file explicitly says don't
+ * wait.
+ */
+ if (
+#ifdef INETD_FEATURE_ENABLED
+ cp->se_bi == 0 &&
+#endif
+ (sep->se_wait == 1 || cp->se_wait == 0))
+ sep->se_wait = cp->se_wait;
+ SWAP(int, cp->se_max, sep->se_max);
+ SWAP(char *, sep->se_user, cp->se_user);
+ SWAP(char *, sep->se_group, cp->se_group);
+ SWAP(char *, sep->se_server, cp->se_server);
+ for (i = 0; i < MAXARGV; i++)
+ SWAP(char *, sep->se_argv[i], cp->se_argv[i]);
+#undef SWAP
+
+#if ENABLE_FEATURE_INETD_RPC
+ if (isrpcservice(sep))
+ unregister_rpc(sep);
+ sep->se_rpcversl = cp->se_rpcversl;
+ sep->se_rpcversh = cp->se_rpcversh;
+#endif
+ sigprocmask(SIG_UNBLOCK, &omask, NULL);
+ freeconfig(cp);
+ } else {
+ sep = enter(cp);
+ }
+ sep->se_checked = 1;
+
+ switch (sep->se_family) {
+ case AF_UNIX:
+ if (sep->se_fd != -1)
+ break;
+ (void) unlink(sep->se_service);
+ n = strlen(sep->se_service);
+ if (n > sizeof sep->se_ctrladdr_un.sun_path - 1)
+ n = sizeof sep->se_ctrladdr_un.sun_path - 1;
+ safe_strncpy(sep->se_ctrladdr_un.sun_path, sep->se_service, n + 1);
+ sep->se_ctrladdr_un.sun_family = AF_UNIX;
+ sep->se_ctrladdr_size = n + sizeof sep->se_ctrladdr_un.sun_family;
+ setup(sep);
+ break;
+ case AF_INET:
+ sep->se_ctrladdr_in.sin_family = AF_INET;
+ /* se_ctrladdr_in was set in getconfigent */
+ sep->se_ctrladdr_size = sizeof sep->se_ctrladdr_in;
+
+#if ENABLE_FEATURE_INETD_RPC
+ if (isrpcservice(sep)) {
+ struct rpcent *rp;
+ // FIXME: atoi_or_else(str, 0) would be handy here
+ sep->se_rpcprog = atoi(sep->se_service);
+ if (sep->se_rpcprog == 0) {
+ rp = getrpcbyname(sep->se_service);
+ if (rp == 0) {
+ bb_error_msg("%s: unknown rpc service", sep->se_service);
+ goto serv_unknown;
+ }
+ sep->se_rpcprog = rp->r_number;
+ }
+ if (sep->se_fd == -1)
+ setup(sep);
+ if (sep->se_fd != -1)
+ register_rpc(sep);
+ } else
+#endif
+ {
+ uint16_t port = htons(atoi(sep->se_service));
+ // FIXME: atoi_or_else(str, 0) would be handy here
+ if (!port) {
+ /*XXX*/ strncpy(protoname, sep->se_proto, sizeof(protoname));
+ if (isdigit(protoname[strlen(protoname) - 1]))
+ protoname[strlen(protoname) - 1] = '\0';
+ sp = getservbyname(sep->se_service, protoname);
+ if (sp == 0) {
+ bb_error_msg("%s/%s: unknown service",
+ sep->se_service, sep->se_proto);
+ goto serv_unknown;
+ }
+ port = sp->s_port;
+ }
+ if (port != sep->se_ctrladdr_in.sin_port) {
+ sep->se_ctrladdr_in.sin_port = port;
+ if (sep->se_fd != -1) {
+ FD_CLR(sep->se_fd, &allsock);
+ nsock--;
+ (void) close(sep->se_fd);
+ }
+ sep->se_fd = -1;
+ }
+ if (sep->se_fd == -1)
+ setup(sep);
+ }
+ break;
+#if ENABLE_FEATURE_IPV6
+ case AF_INET6:
+ sep->se_ctrladdr_in6.sin6_family = AF_INET6;
+ /* se_ctrladdr_in was set in getconfigent */
+ sep->se_ctrladdr_size = sizeof sep->se_ctrladdr_in6;
+
+#if ENABLE_FEATURE_INETD_RPC
+ if (isrpcservice(sep)) {
+ struct rpcent *rp;
+
+ sep->se_rpcprog = atoi(sep->se_service);
+ if (sep->se_rpcprog == 0) {
+ rp = getrpcbyname(sep->se_service);
+ if (rp == 0) {
+ bb_error_msg("%s: unknown rpc service", sep->se_service);
+ goto serv_unknown;
+ }
+ sep->se_rpcprog = rp->r_number;
+ }
+ if (sep->se_fd == -1)
+ setup(sep);
+ if (sep->se_fd != -1)
+ register_rpc(sep);
+ } else
+#endif
+ {
+ uint16_t port = htons(atoi(sep->se_service));
+
+ if (!port) {
+ /*XXX*/ strncpy(protoname, sep->se_proto, sizeof(protoname));
+ if (isdigit(protoname[strlen(protoname) - 1]))
+ protoname[strlen(protoname) - 1] = '\0';
+ sp = getservbyname(sep->se_service, protoname);
+ if (sp == 0) {
+ bb_error_msg("%s/%s: unknown service",
+ sep->se_service, sep->se_proto);
+ goto serv_unknown;
+ }
+ port = sp->s_port;
+ }
+ if (port != sep->se_ctrladdr_in6.sin6_port) {
+ sep->se_ctrladdr_in6.sin6_port = port;
+ if (sep->se_fd != -1) {
+ FD_CLR(sep->se_fd, &allsock);
+ nsock--;
+ (void) close(sep->se_fd);
+ }
+ sep->se_fd = -1;
+ }
+ if (sep->se_fd == -1)
+ setup(sep);
+ }
+ break;
+#endif /* FEATURE_IPV6 */
+ }
+ serv_unknown:
+ if (cp->se_next != NULL) {
+ servtab_t *tmp = cp;
+
+ cp = cp->se_next;
+ free(tmp);
+ } else {
+ free(cp);
+ cp = getconfigent();
+ }
+ }
+ endconfig();
+ /*
+ * Purge anything not looked at above.
+ */
+ Block_Using_Signals(omask);
+ sepp = &servtab;
+ while ((sep = *sepp)) {
+ if (sep->se_checked) {
+ sepp = &sep->se_next;
+ continue;
+ }
+ *sepp = sep->se_next;
+ if (sep->se_fd != -1) {
+ FD_CLR(sep->se_fd, &allsock);
+ nsock--;
+ (void) close(sep->se_fd);
+ }
+#if ENABLE_FEATURE_INETD_RPC
+ if (isrpcservice(sep))
+ unregister_rpc(sep);
+#endif
+ if (sep->se_family == AF_UNIX)
+ (void) unlink(sep->se_service);
+ freeconfig(sep);
+ free(sep);
+ }
+ sigprocmask(SIG_UNBLOCK, &omask, NULL);
+}
+
+
+static void reapchild(int sig ATTRIBUTE_UNUSED)
+{
+ pid_t pid;
+ int save_errno = errno, status;
+ servtab_t *sep;
+
+ for (;;) {
+ pid = wait3(&status, WNOHANG, NULL);
+ if (pid <= 0)
+ break;
+ for (sep = servtab; sep; sep = sep->se_next)
+ if (sep->se_wait == pid) {
+ if (WIFEXITED(status) && WEXITSTATUS(status))
+ bb_error_msg("%s: exit status 0x%x",
+ sep->se_server, WEXITSTATUS(status));
+ else if (WIFSIGNALED(status))
+ bb_error_msg("%s: exit signal 0x%x",
+ sep->se_server, WTERMSIG(status));
+ sep->se_wait = 1;
+ FD_SET(sep->se_fd, &allsock);
+ nsock++;
+ }
+ }
+ errno = save_errno;
+}
+
+static void retry(int sig ATTRIBUTE_UNUSED)
+{
+ servtab_t *sep;
+
+ timingout = 0;
+ for (sep = servtab; sep; sep = sep->se_next) {
+ if (sep->se_fd == -1) {
+ switch (sep->se_family) {
+ case AF_UNIX:
+ case AF_INET:
+#if ENABLE_FEATURE_IPV6
+ case AF_INET6:
+#endif
+ setup(sep);
+#if ENABLE_FEATURE_INETD_RPC
+ if (sep->se_fd != -1 && isrpcservice(sep))
+ register_rpc(sep);
+#endif
+ break;
+ }
+ }
+ }
+}
+
+static void goaway(int sig ATTRIBUTE_UNUSED)
+{
+ servtab_t *sep;
+
+ /* XXX signal race walking sep list */
+ for (sep = servtab; sep; sep = sep->se_next) {
+ if (sep->se_fd == -1)
+ continue;
+
+ switch (sep->se_family) {
+ case AF_UNIX:
+ (void) unlink(sep->se_service);
+ break;
+ case AF_INET:
+#if ENABLE_FEATURE_IPV6
+ case AF_INET6:
+#endif
+#if ENABLE_FEATURE_INETD_RPC
+ if (sep->se_wait == 1 && isrpcservice(sep))
+ unregister_rpc(sep); /* XXX signal race */
+#endif
+ break;
+ }
+ (void) close(sep->se_fd);
+ }
+ (void) unlink(_PATH_INETDPID);
+ exit(0);
+}
+
+
+#ifdef INETD_SETPROCTITLE
+static char **Argv;
+static char *LastArg;
+
+static void
+inetd_setproctitle(char *a, int s)
+{
+ socklen_t size;
+ char *cp;
+ struct sockaddr_in prt_sin;
+ char buf[80];
+
+ cp = Argv[0];
+ size = sizeof(prt_sin);
+ (void) snprintf(buf, sizeof buf, "-%s", a);
+ if (getpeername(s, (struct sockaddr *) &prt_sin, &size) == 0) {
+ char *sa = inet_ntoa(prt_sin.sin_addr);
+
+ buf[sizeof(buf) - 1 - strlen(sa) - 3] = '\0';
+ strcat(buf, " [");
+ strcat(buf, sa);
+ strcat(buf, "]");
+ }
+ strncpy(cp, buf, LastArg - cp);
+ cp += strlen(cp);
+ while (cp < LastArg)
+ *cp++ = ' ';
+}
+#endif
+
+
+int inetd_main(int argc, char *argv[]);
+int inetd_main(int argc, char *argv[])
+{
+ servtab_t *sep;
+ struct passwd *pwd;
+ struct group *grp = NULL;
+ int tmpint;
+ struct sigaction sa, sapipe;
+ int opt;
+ pid_t pid;
+ char buf[50];
+ char *stoomany;
+ sigset_t omask, wait_mask;
+
+#ifdef INETD_SETPROCTITLE
+ extern char **environ;
+ char **envp = environ;
+
+ Argv = argv;
+ if (envp == 0 || *envp == 0)
+ envp = argv;
+ while (*envp)
+ envp++;
+ LastArg = envp[-1] + strlen(envp[-1]);
+#endif
+
+ opt = getopt32(argc, argv, "R:f", &stoomany);
+ if(opt & 1) {
+ toomany = xatoi_u(stoomany);
+ }
+ argc -= optind;
+ argv += optind;
+
+ uid = getuid();
+ if (uid != 0)
+ CONFIG = NULL;
+ if (argc > 0)
+ CONFIG = argv[0];
+ if (CONFIG == NULL)
+ bb_error_msg_and_die("non-root must specify a config file");
+
+#ifdef BB_NOMMU
+ if (!(opt & 2)) {
+ /* reexec for vfork() do continue parent */
+ vfork_daemon_rexec(0, 0, argc, argv, "-f");
+ }
+ bb_sanitize_stdio();
+#else
+ bb_sanitize_stdio_maybe_daemonize(!(opt & 2));
+#endif
+ openlog(applet_name, LOG_PID | LOG_NOWAIT, LOG_DAEMON);
+ logmode = LOGMODE_SYSLOG;
+
+ if (uid == 0) {
+ /* If run by hand, ensure groups vector gets trashed */
+ gid_t gid = getgid();
+ setgroups(1, &gid);
+ }
+
+ {
+ FILE *fp = fopen(_PATH_INETDPID, "w");
+ if (fp != NULL) {
+ fprintf(fp, "%u\n", getpid());
+ fclose(fp);
+ }
+ }
+
+ if (getrlimit(RLIMIT_NOFILE, &rlim_ofile) < 0) {
+ bb_perror_msg("getrlimit");
+ } else {
+ rlim_ofile_cur = rlim_ofile.rlim_cur;
+ if (rlim_ofile_cur == RLIM_INFINITY) /* ! */
+ rlim_ofile_cur = OPEN_MAX;
+ }
+
+ memset((char *) &sa, 0, sizeof(sa));
+ sigemptyset(&sa.sa_mask);
+ sigaddset(&sa.sa_mask, SIGALRM);
+ sigaddset(&sa.sa_mask, SIGCHLD);
+ sigaddset(&sa.sa_mask, SIGHUP);
+ sa.sa_handler = retry;
+ sigaction(SIGALRM, &sa, NULL);
+ config(SIGHUP);
+ sa.sa_handler = config;
+ sigaction(SIGHUP, &sa, NULL);
+ sa.sa_handler = reapchild;
+ sigaction(SIGCHLD, &sa, NULL);
+ sa.sa_handler = goaway;
+ sigaction(SIGTERM, &sa, NULL);
+ sa.sa_handler = goaway;
+ sigaction(SIGINT, &sa, NULL);
+ sa.sa_handler = SIG_IGN;
+ sigaction(SIGPIPE, &sa, &sapipe);
+ memset(&wait_mask, 0, sizeof(wait_mask));
+ {
+ /* space for daemons to overwrite environment for ps */
+#define DUMMYSIZE 100
+ char dummy[DUMMYSIZE];
+
+ (void) memset(dummy, 'x', DUMMYSIZE - 1);
+ dummy[DUMMYSIZE - 1] = '\0';
+
+ (void) setenv("inetd_dummy", dummy, 1);
+ }
+
+ for (;;) {
+ int n, ctrl = -1;
+ fd_set readable;
+
+ if (nsock == 0) {
+ Block_Using_Signals(omask);
+ while (nsock == 0)
+ sigsuspend(&wait_mask);
+ sigprocmask(SIG_UNBLOCK, &omask, NULL);
+ }
+
+ readable = allsock;
+ n = select(maxsock + 1, &readable, NULL, NULL, NULL);
+ if (n <= 0) {
+ if (n < 0 && errno != EINTR) {
+ bb_perror_msg("select");
+ sleep(1);
+ }
+ continue;
+ }
+
+ for (sep = servtab; n && sep; sep = sep->se_next) {
+ if (sep->se_fd == -1 || !FD_ISSET(sep->se_fd, &readable))
+ continue;
+
+ n--;
+ if (!sep->se_wait && sep->se_socktype == SOCK_STREAM) {
+ ctrl = accept(sep->se_fd, NULL, NULL);
+ if (ctrl < 0) {
+ if (errno == EINTR)
+ continue;
+ bb_perror_msg("accept (for %s)", sep->se_service);
+ continue;
+ }
+ if (sep->se_family == AF_INET && sep->se_socktype == SOCK_STREAM) {
+ struct sockaddr_in peer;
+ socklen_t plen = sizeof(peer);
+
+ if (getpeername(ctrl, (struct sockaddr *) &peer, &plen) < 0) {
+ bb_error_msg("cannot getpeername");
+ close(ctrl);
+ continue;
+ }
+ if (ntohs(peer.sin_port) == 20) {
+ /* XXX ftp bounce */
+ close(ctrl);
+ continue;
+ }
+ }
+ } else
+ ctrl = sep->se_fd;
+
+ Block_Using_Signals(omask);
+ pid = 0;
+#ifdef INETD_FEATURE_ENABLED
+ if (sep->se_bi == 0 || sep->se_bi->bi_fork)
+#endif
+ {
+ if (sep->se_count++ == 0)
+ (void) gettimeofday(&sep->se_time, NULL);
+ else if (toomany > 0 && sep->se_count >= sep->se_max) {
+ struct timeval now;
+
+ (void) gettimeofday(&now, NULL);
+ if (now.tv_sec - sep->se_time.tv_sec > CNT_INTVL) {
+ sep->se_time = now;
+ sep->se_count = 1;
+ } else {
+ if (!sep->se_wait && sep->se_socktype == SOCK_STREAM)
+ close(ctrl);
+ if (sep->se_family == AF_INET &&
+ ntohs(sep->se_ctrladdr_in.sin_port) >= IPPORT_RESERVED) {
+ /*
+ * Cannot close it -- there are
+ * thieves on the system.
+ * Simply ignore the connection.
+ */
+ --sep->se_count;
+ continue;
+ }
+ bb_error_msg("%s/%s server failing (looping), service terminated",
+ sep->se_service, sep->se_proto);
+ if (!sep->se_wait && sep->se_socktype == SOCK_STREAM)
+ close(ctrl);
+ FD_CLR(sep->se_fd, &allsock);
+ (void) close(sep->se_fd);
+ sep->se_fd = -1;
+ sep->se_count = 0;
+ nsock--;
+ sigprocmask(SIG_UNBLOCK, &omask, NULL);
+ if (!timingout) {
+ timingout = 1;
+ alarm(RETRYTIME);
+ }
+ continue;
+ }
+ }
+ pid = fork();
+ }
+ if (pid < 0) {
+ bb_perror_msg("fork");
+ if (!sep->se_wait && sep->se_socktype == SOCK_STREAM)
+ close(ctrl);
+ sigprocmask(SIG_UNBLOCK, &omask, NULL);
+ sleep(1);
+ continue;
+ }
+ if (pid && sep->se_wait) {
+ sep->se_wait = pid;
+ FD_CLR(sep->se_fd, &allsock);
+ nsock--;
+ }
+ sigprocmask(SIG_UNBLOCK, &omask, NULL);
+ if (pid == 0) {
+#ifdef INETD_FEATURE_ENABLED
+ if (sep->se_bi) {
+ (*sep->se_bi->bi_fn)(ctrl, sep);
+ } else
+#endif
+ {
+ pwd = getpwnam(sep->se_user);
+ if (pwd == NULL) {
+ bb_error_msg("getpwnam: %s: no such user", sep->se_user);
+ goto do_exit1;
+ }
+ if (setsid() < 0)
+ bb_perror_msg("%s: setsid", sep->se_service);
+ if (sep->se_group && (grp = getgrnam(sep->se_group)) == NULL) {
+ bb_error_msg("getgrnam: %s: no such group", sep->se_group);
+ goto do_exit1;
+ }
+ if (uid != 0) {
+ /* a user running private inetd */
+ if (uid != pwd->pw_uid)
+ _exit(1);
+ } else if (pwd->pw_uid) {
+ if (sep->se_group)
+ pwd->pw_gid = grp->gr_gid;
+ xsetgid((gid_t) pwd->pw_gid);
+ initgroups(pwd->pw_name, pwd->pw_gid);
+ xsetuid((uid_t) pwd->pw_uid);
+ } else if (sep->se_group) {
+ xsetgid(grp->gr_gid);
+ setgroups(1, &grp->gr_gid);
+ }
+ dup2(ctrl, 0);
+ if (ctrl) close(ctrl);
+ dup2(0, 1);
+ dup2(0, 2);
+ if (rlim_ofile.rlim_cur != rlim_ofile_cur)
+ if (setrlimit(RLIMIT_NOFILE, &rlim_ofile) < 0)
+ bb_perror_msg("setrlimit");
+ closelog();
+ for (tmpint = rlim_ofile_cur - 1; --tmpint > 2;)
+ (void) close(tmpint);
+ sigaction(SIGPIPE, &sapipe, NULL);
+ execv(sep->se_server, sep->se_argv);
+ bb_perror_msg("execv %s", sep->se_server);
+do_exit1:
+ if (sep->se_socktype != SOCK_STREAM)
+ recv(0, buf, sizeof(buf), 0);
+ _exit(1);
+ }
+ }
+ if (!sep->se_wait && sep->se_socktype == SOCK_STREAM)
+ close(ctrl);
+ } /* for (sep = servtab...) */
+ } /* for (;;) */
+}
+
+/*
+ * Internet services provided internally by inetd:
+ */
+#define BUFSIZE 4096
+
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_ECHO || \
+ ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN || \
+ ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME
+static int dg_badinput(struct sockaddr_in *dg_sin)
+{
+ if (ntohs(dg_sin->sin_port) < IPPORT_RESERVED)
+ return 1;
+ if (dg_sin->sin_addr.s_addr == htonl(INADDR_BROADCAST))
+ return 1;
+ /* XXX compare against broadcast addresses in SIOCGIFCONF list? */
+ return 0;
+}
+#endif
+
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_ECHO
+/* Echo service -- echo data back */
+/* ARGSUSED */
+static void
+echo_stream(int s, servtab_t *sep)
+{
+ char buffer[BUFSIZE];
+ int i;
+
+ inetd_setproctitle(sep->se_service, s);
+ while (1) {
+ i = read(s, buffer, sizeof(buffer));
+ if (i <= 0) break;
+ /* FIXME: this isnt correct - safe_write()? */
+ if (write(s, buffer, i) <= 0) break;
+ }
+ exit(0);
+}
+
+/* Echo service -- echo data back */
+/* ARGSUSED */
+static void
+echo_dg(int s, servtab_t *sep ATTRIBUTE_UNUSED)
+{
+ char buffer[BUFSIZE];
+ int i;
+ socklen_t size;
+ /* struct sockaddr_storage ss; */
+ struct sockaddr sa;
+
+ size = sizeof(sa);
+ i = recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size);
+ if (i < 0)
+ return;
+ if (dg_badinput((struct sockaddr_in *) &sa))
+ return;
+ (void) sendto(s, buffer, i, 0, &sa, sizeof(sa));
+}
+#endif /* FEATURE_INETD_SUPPORT_BUILTIN_ECHO */
+
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD
+/* Discard service -- ignore data */
+/* ARGSUSED */
+static void
+discard_stream(int s, servtab_t *sep)
+{
+ char buffer[BUFSIZE];
+
+ inetd_setproctitle(sep->se_service, s);
+ while (1) {
+ errno = 0;
+ if (read(s, buffer, sizeof(buffer)) <= 0 && errno != EINTR)
+ exit(0);
+ }
+}
+
+/* Discard service -- ignore data */
+/* ARGSUSED */
+static void
+discard_dg(int s, servtab_t *sep ATTRIBUTE_UNUSED)
+{
+ char buffer[BUFSIZE];
+
+ (void) read(s, buffer, sizeof(buffer));
+}
+#endif /* FEATURE_INETD_SUPPORT_BUILTIN_DISCARD */
+
+
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN
+#define LINESIZ 72
+static char ring[128];
+static char *endring;
+
+static void
+initring(void)
+{
+ int i;
+
+ endring = ring;
+
+ for (i = 0; i <= 128; ++i)
+ if (isprint(i))
+ *endring++ = i;
+}
+
+/* Character generator */
+/* ARGSUSED */
+static void
+chargen_stream(int s, servtab_t *sep)
+{
+ char *rs;
+ int len;
+ char text[LINESIZ + 2];
+
+ inetd_setproctitle(sep->se_service, s);
+
+ if (!endring) {
+ initring();
+ rs = ring;
+ }
+
+ text[LINESIZ] = '\r';
+ text[LINESIZ + 1] = '\n';
+ rs = ring;
+ for (;;) {
+ len = endring - rs;
+ if (len >= LINESIZ)
+ memmove(text, rs, LINESIZ);
+ else {
+ memmove(text, rs, len);
+ memmove(text + len, ring, LINESIZ - len);
+ }
+ if (++rs == endring)
+ rs = ring;
+ if (write(s, text, sizeof(text)) != sizeof(text))
+ break;
+ }
+ exit(0);
+}
+
+/* Character generator */
+/* ARGSUSED */
+static void
+chargen_dg(int s, servtab_t *sep ATTRIBUTE_UNUSED)
+{
+ /* struct sockaddr_storage ss; */
+ struct sockaddr sa;
+ static char *rs;
+ int len;
+ char text[LINESIZ + 2];
+ socklen_t size;
+
+ if (endring == 0) {
+ initring();
+ rs = ring;
+ }
+
+ size = sizeof(sa);
+ if (recvfrom(s, text, sizeof(text), 0, &sa, &size) < 0)
+ return;
+ if (dg_badinput((struct sockaddr_in *) &sa))
+ return;
+
+ if ((len = endring - rs) >= LINESIZ)
+ memmove(text, rs, LINESIZ);
+ else {
+ memmove(text, rs, len);
+ memmove(text + len, ring, LINESIZ - len);
+ }
+ if (++rs == endring)
+ rs = ring;
+ text[LINESIZ] = '\r';
+ text[LINESIZ + 1] = '\n';
+ (void) sendto(s, text, sizeof(text), 0, &sa, sizeof(sa));
+}
+#endif /* FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN */
+
+
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_TIME
+/*
+ * Return a machine readable date and time, in the form of the
+ * number of seconds since midnight, Jan 1, 1900. Since gettimeofday
+ * returns the number of seconds since midnight, Jan 1, 1970,
+ * we must add 2208988800 seconds to this figure to make up for
+ * some seventy years Bell Labs was asleep.
+ */
+
+static unsigned machtime(void)
+{
+ struct timeval tv;
+
+ if (gettimeofday(&tv, NULL) < 0) {
+ fprintf(stderr, "Unable to get time of day\n");
+ return 0L;
+ }
+ return htonl((unsigned) tv.tv_sec + 2208988800UL);
+}
+
+/* ARGSUSED */
+static void
+machtime_stream(int s, servtab_t *sep ATTRIBUTE_UNUSED)
+{
+ unsigned result;
+
+ result = machtime();
+ (void) write(s, (char *) &result, sizeof(result));
+}
+
+/* ARGSUSED */
+static void
+machtime_dg(int s, servtab_t *sep ATTRIBUTE_UNUSED)
+{
+ unsigned result;
+ /* struct sockaddr_storage ss; */
+ struct sockaddr sa;
+ struct sockaddr_in *dg_sin;
+ socklen_t size;
+
+ size = sizeof(sa);
+ if (recvfrom(s, (char *) &result, sizeof(result), 0, &sa, &size) < 0)
+ return;
+ /* if (dg_badinput((struct sockaddr *)&ss)) */
+ dg_sin = (struct sockaddr_in *) &sa;
+ if (dg_sin->sin_addr.s_addr == htonl(INADDR_BROADCAST) ||
+ ntohs(dg_sin->sin_port) < IPPORT_RESERVED / 2)
+ return;
+ result = machtime();
+ (void) sendto(s, (char *) &result, sizeof(result), 0, &sa, sizeof(sa));
+}
+#endif /* FEATURE_INETD_SUPPORT_BUILTIN_TIME */
+
+
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME
+/* Return human-readable time of day */
+/* ARGSUSED */
+static void daytime_stream(int s, servtab_t *sep ATTRIBUTE_UNUSED)
+{
+ char buffer[32];
+ time_t t;
+
+ t = time(NULL);
+
+// fdprintf instead?
+ (void) sprintf(buffer, "%.24s\r\n", ctime(&t));
+ (void) write(s, buffer, strlen(buffer));
+}
+
+/* Return human-readable time of day */
+/* ARGSUSED */
+void
+daytime_dg(int s, servtab_t *sep ATTRIBUTE_UNUSED)
+{
+ char buffer[256];
+ time_t t;
+ /* struct sockaddr_storage ss; */
+ struct sockaddr sa;
+ socklen_t size;
+
+ t = time(NULL);
+
+ size = sizeof(sa);
+ if (recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size) < 0)
+ return;
+ if (dg_badinput((struct sockaddr_in *) &sa))
+ return;
+ (void) sprintf(buffer, "%.24s\r\n", ctime(&t));
+ (void) sendto(s, buffer, strlen(buffer), 0, &sa, sizeof(sa));
+}
+#endif /* FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME */
diff --git a/i/pc104/initrd/conf/busybox/networking/interface.c b/i/pc104/initrd/conf/busybox/networking/interface.c
new file mode 100644
index 0000000..b1b935e
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/interface.c
@@ -0,0 +1,1195 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * stolen from net-tools-1.59 and stripped down for busybox by
+ * Erik Andersen <andersen@codepoet.org>
+ *
+ * Heavily modified by Manuel Novoa III Mar 12, 2001
+ *
+ * Added print_bytes_scaled function to reduce code size.
+ * Added some (potentially) missing defines.
+ * Improved display support for -a and for a named interface.
+ *
+ * -----------------------------------------------------------
+ *
+ * ifconfig This file contains an implementation of the command
+ * that either displays or sets the characteristics of
+ * one or more of the system's networking interfaces.
+ *
+ *
+ * Author: Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+ * and others. Copyright 1993 MicroWalt Corporation
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ *
+ * Patched to support 'add' and 'del' keywords for INET(4) addresses
+ * by Mrs. Brisby <mrs.brisby@nimh.org>
+ *
+ * {1.34} - 19980630 - Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ * - gettext instead of catgets for i18n
+ * 10/1998 - Andi Kleen. Use interface list primitives.
+ * 20001008 - Bernd Eckenfels, Patch from RH for setting mtu
+ * (default AF was wrong)
+ */
+
+#include <net/if.h>
+#include <net/if_arp.h>
+#include "inet_common.h"
+#include "busybox.h"
+
+#if ENABLE_FEATURE_IPV6
+# define HAVE_AFINET6 1
+#else
+# undef HAVE_AFINET6
+#endif
+
+#define _PATH_PROCNET_DEV "/proc/net/dev"
+#define _PATH_PROCNET_IFINET6 "/proc/net/if_inet6"
+
+#ifdef HAVE_AFINET6
+
+#ifndef _LINUX_IN6_H
+/*
+ * This is in linux/include/net/ipv6.h.
+ */
+
+struct in6_ifreq {
+ struct in6_addr ifr6_addr;
+ uint32_t ifr6_prefixlen;
+ unsigned int ifr6_ifindex;
+};
+
+#endif
+
+#endif /* HAVE_AFINET6 */
+
+/* Defines for glibc2.0 users. */
+#ifndef SIOCSIFTXQLEN
+#define SIOCSIFTXQLEN 0x8943
+#define SIOCGIFTXQLEN 0x8942
+#endif
+
+/* ifr_qlen is ifru_ivalue, but it isn't present in 2.0 kernel headers */
+#ifndef ifr_qlen
+#define ifr_qlen ifr_ifru.ifru_mtu
+#endif
+
+#ifndef HAVE_TXQUEUELEN
+#define HAVE_TXQUEUELEN 1
+#endif
+
+#ifndef IFF_DYNAMIC
+#define IFF_DYNAMIC 0x8000 /* dialup device with changing addresses */
+#endif
+
+/* Display an Internet socket address. */
+static const char *INET_sprint(struct sockaddr *sap, int numeric)
+{
+ static char buff[128];
+
+ if (sap->sa_family == 0xFFFF || sap->sa_family == 0)
+ return "[NONE SET]";
+
+ if (INET_rresolve(buff, sizeof(buff), (struct sockaddr_in *) sap,
+ numeric, 0xffffff00) != 0)
+ return NULL;
+
+ return buff;
+}
+
+#ifdef UNUSED_AND_BUGGY
+static int INET_getsock(char *bufp, struct sockaddr *sap)
+{
+ char *sp = bufp, *bp;
+ unsigned int i;
+ unsigned val;
+ struct sockaddr_in *sock_in;
+
+ sock_in = (struct sockaddr_in *) sap;
+ sock_in->sin_family = AF_INET;
+ sock_in->sin_port = 0;
+
+ val = 0;
+ bp = (char *) &val;
+ for (i = 0; i < sizeof(sock_in->sin_addr.s_addr); i++) {
+ *sp = toupper(*sp);
+
+ if ((unsigned)(*sp - 'A') <= 5)
+ bp[i] |= (int) (*sp - ('A' - 10));
+ else if (isdigit(*sp))
+ bp[i] |= (int) (*sp - '0');
+ else
+ return -1;
+
+ bp[i] <<= 4;
+ sp++;
+ *sp = toupper(*sp);
+
+ if ((unsigned)(*sp - 'A') <= 5)
+ bp[i] |= (int) (*sp - ('A' - 10));
+ else if (isdigit(*sp))
+ bp[i] |= (int) (*sp - '0');
+ else
+ return -1;
+
+ sp++;
+ }
+ sock_in->sin_addr.s_addr = htonl(val);
+
+ return (sp - bufp);
+}
+#endif
+
+static int INET_input(/*int type,*/ const char *bufp, struct sockaddr *sap)
+{
+ return INET_resolve(bufp, (struct sockaddr_in *) sap, 0);
+/*
+ switch (type) {
+ case 1:
+ return (INET_getsock(bufp, sap));
+ case 256:
+ return (INET_resolve(bufp, (struct sockaddr_in *) sap, 1));
+ default:
+ return (INET_resolve(bufp, (struct sockaddr_in *) sap, 0));
+ }
+*/
+}
+
+static const struct aftype inet_aftype = {
+ .name = "inet",
+ .title = "DARPA Internet",
+ .af = AF_INET,
+ .alen = 4,
+ .sprint = INET_sprint,
+ .input = INET_input,
+};
+
+#ifdef HAVE_AFINET6
+
+/* Display an Internet socket address. */
+/* dirty! struct sockaddr usually doesn't suffer for inet6 addresses, fst. */
+static const char *INET6_sprint(struct sockaddr *sap, int numeric)
+{
+ static char buff[128];
+
+ if (sap->sa_family == 0xFFFF || sap->sa_family == 0)
+ return "[NONE SET]";
+ if (INET6_rresolve(buff, sizeof(buff), (struct sockaddr_in6 *) sap, numeric))
+ return "[UNKNOWN]";
+ return buff;
+}
+
+#ifdef UNUSED
+static int INET6_getsock(char *bufp, struct sockaddr *sap)
+{
+ struct sockaddr_in6 *sin6;
+
+ sin6 = (struct sockaddr_in6 *) sap;
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = 0;
+
+ if (inet_pton(AF_INET6, bufp, sin6->sin6_addr.s6_addr) <= 0)
+ return -1;
+
+ return 16; /* ?;) */
+}
+#endif
+
+static int INET6_input(/*int type,*/ const char *bufp, struct sockaddr *sap)
+{
+ return INET6_resolve(bufp, (struct sockaddr_in6 *) sap);
+/*
+ switch (type) {
+ case 1:
+ return (INET6_getsock(bufp, sap));
+ default:
+ return (INET6_resolve(bufp, (struct sockaddr_in6 *) sap));
+ }
+*/
+}
+
+static const struct aftype inet6_aftype = {
+ .name = "inet6",
+ .title = "IPv6",
+ .af = AF_INET6,
+ .alen = sizeof(struct in6_addr),
+ .sprint = INET6_sprint,
+ .input = INET6_input,
+};
+
+#endif /* HAVE_AFINET6 */
+
+/* Display an UNSPEC address. */
+static char *UNSPEC_print(unsigned char *ptr)
+{
+ static char buff[sizeof(struct sockaddr) * 3 + 1];
+ char *pos;
+ unsigned int i;
+
+ pos = buff;
+ for (i = 0; i < sizeof(struct sockaddr); i++) {
+ /* careful -- not every libc's sprintf returns # bytes written */
+ sprintf(pos, "%02X-", (*ptr++ & 0377));
+ pos += 3;
+ }
+ /* Erase trailing "-". Works as long as sizeof(struct sockaddr) != 0 */
+ *--pos = '\0';
+ return buff;
+}
+
+/* Display an UNSPEC socket address. */
+static const char *UNSPEC_sprint(struct sockaddr *sap, int numeric)
+{
+ if (sap->sa_family == 0xFFFF || sap->sa_family == 0)
+ return "[NONE SET]";
+ return UNSPEC_print((unsigned char *)sap->sa_data);
+}
+
+static const struct aftype unspec_aftype = {
+ .name = "unspec",
+ .title = "UNSPEC",
+ .af = AF_UNSPEC,
+ .alen = 0,
+ .print = UNSPEC_print,
+ .sprint = UNSPEC_sprint,
+};
+
+static const struct aftype *const aftypes[] = {
+ &inet_aftype,
+#ifdef HAVE_AFINET6
+ &inet6_aftype,
+#endif
+ &unspec_aftype,
+ NULL
+};
+
+/* Check our protocol family table for this family. */
+const struct aftype *get_aftype(const char *name)
+{
+ const struct aftype *const *afp;
+
+ afp = aftypes;
+ while (*afp != NULL) {
+ if (!strcmp((*afp)->name, name))
+ return (*afp);
+ afp++;
+ }
+ return NULL;
+}
+
+/* Check our protocol family table for this family. */
+static const struct aftype *get_afntype(int af)
+{
+ const struct aftype *const *afp;
+
+ afp = aftypes;
+ while (*afp != NULL) {
+ if ((*afp)->af == af)
+ return *afp;
+ afp++;
+ }
+ return NULL;
+}
+
+struct user_net_device_stats {
+ unsigned long long rx_packets; /* total packets received */
+ unsigned long long tx_packets; /* total packets transmitted */
+ unsigned long long rx_bytes; /* total bytes received */
+ unsigned long long tx_bytes; /* total bytes transmitted */
+ unsigned long rx_errors; /* bad packets received */
+ unsigned long tx_errors; /* packet transmit problems */
+ unsigned long rx_dropped; /* no space in linux buffers */
+ unsigned long tx_dropped; /* no space available in linux */
+ unsigned long rx_multicast; /* multicast packets received */
+ unsigned long rx_compressed;
+ unsigned long tx_compressed;
+ unsigned long collisions;
+
+ /* detailed rx_errors: */
+ unsigned long rx_length_errors;
+ unsigned long rx_over_errors; /* receiver ring buff overflow */
+ unsigned long rx_crc_errors; /* recved pkt with crc error */
+ unsigned long rx_frame_errors; /* recv'd frame alignment error */
+ unsigned long rx_fifo_errors; /* recv'r fifo overrun */
+ unsigned long rx_missed_errors; /* receiver missed packet */
+ /* detailed tx_errors */
+ unsigned long tx_aborted_errors;
+ unsigned long tx_carrier_errors;
+ unsigned long tx_fifo_errors;
+ unsigned long tx_heartbeat_errors;
+ unsigned long tx_window_errors;
+};
+
+struct interface {
+ struct interface *next, *prev;
+ char name[IFNAMSIZ]; /* interface name */
+ short type; /* if type */
+ short flags; /* various flags */
+ int metric; /* routing metric */
+ int mtu; /* MTU value */
+ int tx_queue_len; /* transmit queue length */
+ struct ifmap map; /* hardware setup */
+ struct sockaddr addr; /* IP address */
+ struct sockaddr dstaddr; /* P-P IP address */
+ struct sockaddr broadaddr; /* IP broadcast address */
+ struct sockaddr netmask; /* IP network mask */
+ int has_ip;
+ char hwaddr[32]; /* HW address */
+ int statistics_valid;
+ struct user_net_device_stats stats; /* statistics */
+ int keepalive; /* keepalive value for SLIP */
+ int outfill; /* outfill value for SLIP */
+};
+
+
+int interface_opt_a; /* show all interfaces */
+
+static struct interface *int_list, *int_last;
+
+
+#if 0
+/* like strcmp(), but knows about numbers */
+except that the freshly added calls to xatoul() brf on ethernet aliases with
+uClibc with e.g.: ife->name='lo' name='eth0:1'
+static int nstrcmp(const char *a, const char *b)
+{
+ const char *a_ptr = a;
+ const char *b_ptr = b;
+
+ while (*a == *b) {
+ if (*a == '\0') {
+ return 0;
+ }
+ if (!isdigit(*a) && isdigit(*(a+1))) {
+ a_ptr = a+1;
+ b_ptr = b+1;
+ }
+ a++;
+ b++;
+ }
+
+ if (isdigit(*a) && isdigit(*b)) {
+ return xatoul(a_ptr) > xatoul(b_ptr) ? 1 : -1;
+ }
+ return *a - *b;
+}
+#endif
+
+static struct interface *add_interface(char *name)
+{
+ struct interface *ife, **nextp, *new;
+
+ for (ife = int_last; ife; ife = ife->prev) {
+ int n = /*n*/strcmp(ife->name, name);
+
+ if (n == 0)
+ return ife;
+ if (n < 0)
+ break;
+ }
+
+ new = xzalloc(sizeof(*new));
+ safe_strncpy(new->name, name, IFNAMSIZ);
+ nextp = ife ? &ife->next : &int_list;
+ new->prev = ife;
+ new->next = *nextp;
+ if (new->next)
+ new->next->prev = new;
+ else
+ int_last = new;
+ *nextp = new;
+ return new;
+}
+
+static char *get_name(char *name, char *p)
+{
+ /* Extract <name> from nul-terminated p where p matches
+ <name>: after leading whitespace.
+ If match is not made, set name empty and return unchanged p */
+ int namestart=0, nameend=0;
+ while (isspace(p[namestart]))
+ namestart++;
+ nameend=namestart;
+ while (p[nameend] && p[nameend]!=':' && !isspace(p[nameend]))
+ nameend++;
+ if (p[nameend]==':') {
+ if ((nameend-namestart)<IFNAMSIZ) {
+ memcpy(name,&p[namestart],nameend-namestart);
+ name[nameend-namestart]='\0';
+ p=&p[nameend];
+ } else {
+ /* Interface name too large */
+ name[0]='\0';
+ }
+ } else {
+ /* trailing ':' not found - return empty */
+ name[0]='\0';
+ }
+ return p + 1;
+}
+
+/* If scanf supports size qualifiers for %n conversions, then we can
+ * use a modified fmt that simply stores the position in the fields
+ * having no associated fields in the proc string. Of course, we need
+ * to zero them again when we're done. But that is smaller than the
+ * old approach of multiple scanf occurrences with large numbers of
+ * args. */
+
+/* static const char *const ss_fmt[] = { */
+/* "%lln%llu%lu%lu%lu%lu%ln%ln%lln%llu%lu%lu%lu%lu%lu", */
+/* "%llu%llu%lu%lu%lu%lu%ln%ln%llu%llu%lu%lu%lu%lu%lu", */
+/* "%llu%llu%lu%lu%lu%lu%lu%lu%llu%llu%lu%lu%lu%lu%lu%lu" */
+/* }; */
+
+ /* Lie about the size of the int pointed to for %n. */
+#if INT_MAX == LONG_MAX
+static const char *const ss_fmt[] = {
+ "%n%llu%u%u%u%u%n%n%n%llu%u%u%u%u%u",
+ "%llu%llu%u%u%u%u%n%n%llu%llu%u%u%u%u%u",
+ "%llu%llu%u%u%u%u%u%u%llu%llu%u%u%u%u%u%u"
+};
+#else
+static const char *const ss_fmt[] = {
+ "%n%llu%lu%lu%lu%lu%n%n%n%llu%lu%lu%lu%lu%lu",
+ "%llu%llu%lu%lu%lu%lu%n%n%llu%llu%lu%lu%lu%lu%lu",
+ "%llu%llu%lu%lu%lu%lu%lu%lu%llu%llu%lu%lu%lu%lu%lu%lu"
+};
+
+#endif
+
+static void get_dev_fields(char *bp, struct interface *ife, int procnetdev_vsn)
+{
+ memset(&ife->stats, 0, sizeof(struct user_net_device_stats));
+
+ sscanf(bp, ss_fmt[procnetdev_vsn],
+ &ife->stats.rx_bytes, /* missing for 0 */
+ &ife->stats.rx_packets,
+ &ife->stats.rx_errors,
+ &ife->stats.rx_dropped,
+ &ife->stats.rx_fifo_errors,
+ &ife->stats.rx_frame_errors,
+ &ife->stats.rx_compressed, /* missing for <= 1 */
+ &ife->stats.rx_multicast, /* missing for <= 1 */
+ &ife->stats.tx_bytes, /* missing for 0 */
+ &ife->stats.tx_packets,
+ &ife->stats.tx_errors,
+ &ife->stats.tx_dropped,
+ &ife->stats.tx_fifo_errors,
+ &ife->stats.collisions,
+ &ife->stats.tx_carrier_errors,
+ &ife->stats.tx_compressed /* missing for <= 1 */
+ );
+
+ if (procnetdev_vsn <= 1) {
+ if (procnetdev_vsn == 0) {
+ ife->stats.rx_bytes = 0;
+ ife->stats.tx_bytes = 0;
+ }
+ ife->stats.rx_multicast = 0;
+ ife->stats.rx_compressed = 0;
+ ife->stats.tx_compressed = 0;
+ }
+}
+
+static inline int procnetdev_version(char *buf)
+{
+ if (strstr(buf, "compressed"))
+ return 2;
+ if (strstr(buf, "bytes"))
+ return 1;
+ return 0;
+}
+
+static int if_readconf(void)
+{
+ int numreqs = 30;
+ struct ifconf ifc;
+ struct ifreq *ifr;
+ int n, err = -1;
+ int skfd;
+
+ ifc.ifc_buf = NULL;
+
+ /* SIOCGIFCONF currently seems to only work properly on AF_INET sockets
+ (as of 2.1.128) */
+ skfd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (skfd < 0) {
+ bb_perror_msg("error: no inet socket available");
+ return -1;
+ }
+
+ for (;;) {
+ ifc.ifc_len = sizeof(struct ifreq) * numreqs;
+ ifc.ifc_buf = xrealloc(ifc.ifc_buf, ifc.ifc_len);
+
+ if (ioctl(skfd, SIOCGIFCONF, &ifc) < 0) {
+ perror("SIOCGIFCONF");
+ goto out;
+ }
+ if (ifc.ifc_len == sizeof(struct ifreq) * numreqs) {
+ /* assume it overflowed and try again */
+ numreqs += 10;
+ continue;
+ }
+ break;
+ }
+
+ ifr = ifc.ifc_req;
+ for (n = 0; n < ifc.ifc_len; n += sizeof(struct ifreq)) {
+ add_interface(ifr->ifr_name);
+ ifr++;
+ }
+ err = 0;
+
+ out:
+ close(skfd);
+ free(ifc.ifc_buf);
+ return err;
+}
+
+static int if_readlist_proc(char *target)
+{
+ static int proc_read;
+ FILE *fh;
+ char buf[512];
+ struct interface *ife;
+ int err, procnetdev_vsn;
+
+ if (proc_read)
+ return 0;
+ if (!target)
+ proc_read = 1;
+
+ fh = fopen(_PATH_PROCNET_DEV, "r");
+ if (!fh) {
+ bb_perror_msg("warning: cannot open %s, limiting output", _PATH_PROCNET_DEV);
+ return if_readconf();
+ }
+ fgets(buf, sizeof buf, fh); /* eat line */
+ fgets(buf, sizeof buf, fh);
+
+ procnetdev_vsn = procnetdev_version(buf);
+
+ err = 0;
+ while (fgets(buf, sizeof buf, fh)) {
+ char *s, name[128];
+
+ s = get_name(name, buf);
+ ife = add_interface(name);
+ get_dev_fields(s, ife, procnetdev_vsn);
+ ife->statistics_valid = 1;
+ if (target && !strcmp(target, name))
+ break;
+ }
+ if (ferror(fh)) {
+ perror(_PATH_PROCNET_DEV);
+ err = -1;
+ proc_read = 0;
+ }
+ fclose(fh);
+ return err;
+}
+
+static int if_readlist(void)
+{
+ int err = if_readlist_proc(NULL);
+ /* Needed in order to get ethN:M aliases */
+ if (!err)
+ err = if_readconf();
+ return err;
+}
+
+static int for_all_interfaces(int (*doit) (struct interface *, void *),
+ void *cookie)
+{
+ struct interface *ife;
+
+ if (!int_list && (if_readlist() < 0))
+ return -1;
+ for (ife = int_list; ife; ife = ife->next) {
+ int err = doit(ife, cookie);
+
+ if (err)
+ return err;
+ }
+ return 0;
+}
+
+/* Fetch the interface configuration from the kernel. */
+static int if_fetch(struct interface *ife)
+{
+ struct ifreq ifr;
+ char *ifname = ife->name;
+ int skfd;
+
+ skfd = xsocket(AF_INET, SOCK_DGRAM, 0);
+
+ strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ if (ioctl(skfd, SIOCGIFFLAGS, &ifr) < 0) {
+ close(skfd);
+ return -1;
+ }
+ ife->flags = ifr.ifr_flags;
+
+ strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ memset(ife->hwaddr, 0, 32);
+ if (ioctl(skfd, SIOCGIFHWADDR, &ifr) >= 0)
+ memcpy(ife->hwaddr, ifr.ifr_hwaddr.sa_data, 8);
+
+ ife->type = ifr.ifr_hwaddr.sa_family;
+
+ strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ ife->metric = 0;
+ if (ioctl(skfd, SIOCGIFMETRIC, &ifr) >= 0)
+ ife->metric = ifr.ifr_metric;
+
+ strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ ife->mtu = 0;
+ if (ioctl(skfd, SIOCGIFMTU, &ifr) >= 0)
+ ife->mtu = ifr.ifr_mtu;
+
+ memset(&ife->map, 0, sizeof(struct ifmap));
+#ifdef SIOCGIFMAP
+ strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ if (ioctl(skfd, SIOCGIFMAP, &ifr) == 0)
+ ife->map = ifr.ifr_map;
+#endif
+
+#ifdef HAVE_TXQUEUELEN
+ strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ ife->tx_queue_len = -1; /* unknown value */
+ if (ioctl(skfd, SIOCGIFTXQLEN, &ifr) >= 0)
+ ife->tx_queue_len = ifr.ifr_qlen;
+#else
+ ife->tx_queue_len = -1; /* unknown value */
+#endif
+
+ strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ ifr.ifr_addr.sa_family = AF_INET;
+ memset(&ife->addr, 0, sizeof(struct sockaddr));
+ if (ioctl(skfd, SIOCGIFADDR, &ifr) == 0) {
+ ife->has_ip = 1;
+ ife->addr = ifr.ifr_addr;
+ strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ memset(&ife->dstaddr, 0, sizeof(struct sockaddr));
+ if (ioctl(skfd, SIOCGIFDSTADDR, &ifr) >= 0)
+ ife->dstaddr = ifr.ifr_dstaddr;
+
+ strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ memset(&ife->broadaddr, 0, sizeof(struct sockaddr));
+ if (ioctl(skfd, SIOCGIFBRDADDR, &ifr) >= 0)
+ ife->broadaddr = ifr.ifr_broadaddr;
+
+ strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ memset(&ife->netmask, 0, sizeof(struct sockaddr));
+ if (ioctl(skfd, SIOCGIFNETMASK, &ifr) >= 0)
+ ife->netmask = ifr.ifr_netmask;
+ }
+
+ close(skfd);
+ return 0;
+}
+
+
+static int do_if_fetch(struct interface *ife)
+{
+ if (if_fetch(ife) < 0) {
+ const char *errmsg;
+
+ if (errno == ENODEV) {
+ /* Give better error message for this case. */
+ errmsg = "Device not found";
+ } else {
+ errmsg = strerror(errno);
+ }
+ bb_error_msg("%s: error fetching interface information: %s",
+ ife->name, errmsg);
+ return -1;
+ }
+ return 0;
+}
+
+static const struct hwtype unspec_hwtype = {
+ .name = "unspec",
+ .title = "UNSPEC",
+ .type = -1,
+ .print = UNSPEC_print
+};
+
+static const struct hwtype loop_hwtype = {
+ .name = "loop",
+ .title = "Local Loopback",
+ .type = ARPHRD_LOOPBACK
+};
+
+#include <net/if_arp.h>
+
+#if (defined(__GLIBC__) && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1) || defined(_NEWLIB_VERSION)
+#include <net/ethernet.h>
+#else
+#include <linux/if_ether.h>
+#endif
+
+/* Display an Ethernet address in readable format. */
+static char *pr_ether(unsigned char *ptr)
+{
+ static char buff[64];
+
+ snprintf(buff, sizeof(buff), "%02X:%02X:%02X:%02X:%02X:%02X",
+ (ptr[0] & 0377), (ptr[1] & 0377), (ptr[2] & 0377),
+ (ptr[3] & 0377), (ptr[4] & 0377), (ptr[5] & 0377)
+ );
+ return buff;
+}
+
+static int in_ether(const char *bufp, struct sockaddr *sap);
+
+static struct hwtype ether_hwtype = {
+ .name = "ether",
+ .title = "Ethernet",
+ .type = ARPHRD_ETHER,
+ .alen = ETH_ALEN,
+ .print = pr_ether,
+ .input = in_ether
+};
+
+static unsigned hexchar2int(char c)
+{
+ if (isdigit(c))
+ return c - '0';
+ c &= ~0x20; /* a -> A */
+ if ((unsigned)(c - 'A') <= 5)
+ return c - ('A' - 10);
+ return ~0U;
+}
+
+/* Input an Ethernet address and convert to binary. */
+static int in_ether(const char *bufp, struct sockaddr *sap)
+{
+ unsigned char *ptr;
+ char c;
+ int i;
+ unsigned val;
+
+ sap->sa_family = ether_hwtype.type;
+ ptr = (unsigned char*) sap->sa_data;
+
+ i = 0;
+ while ((*bufp != '\0') && (i < ETH_ALEN)) {
+ val = hexchar2int(*bufp++) * 0x10;
+ if (val > 0xff) {
+ errno = EINVAL;
+ return -1;
+ }
+ c = *bufp;
+ if (c == ':' || c == 0)
+ val >>= 4;
+ else {
+ val |= hexchar2int(c);
+ if (val > 0xff) {
+ errno = EINVAL;
+ return -1;
+ }
+ }
+ if (c != 0)
+ bufp++;
+ *ptr++ = (unsigned char) val;
+ i++;
+
+ /* We might get a semicolon here - not required. */
+ if (*bufp == ':') {
+ bufp++;
+ }
+ }
+ return 0;
+}
+
+#include <net/if_arp.h>
+
+static const struct hwtype ppp_hwtype = {
+ .name = "ppp",
+ .title = "Point-to-Point Protocol",
+ .type = ARPHRD_PPP
+};
+
+#if ENABLE_FEATURE_IPV6
+static const struct hwtype sit_hwtype = {
+ .name = "sit",
+ .title = "IPv6-in-IPv4",
+ .type = ARPHRD_SIT,
+ .print = UNSPEC_print,
+ .suppress_null_addr = 1
+} ;
+#endif
+
+static const struct hwtype *const hwtypes[] = {
+ &loop_hwtype,
+ &ether_hwtype,
+ &ppp_hwtype,
+ &unspec_hwtype,
+#if ENABLE_FEATURE_IPV6
+ &sit_hwtype,
+#endif
+ NULL
+};
+
+#ifdef IFF_PORTSEL
+static const char *const if_port_text[] = {
+ /* Keep in step with <linux/netdevice.h> */
+ "unknown",
+ "10base2",
+ "10baseT",
+ "AUI",
+ "100baseT",
+ "100baseTX",
+ "100baseFX",
+ NULL
+};
+#endif
+
+/* Check our hardware type table for this type. */
+const struct hwtype *get_hwtype(const char *name)
+{
+ const struct hwtype *const *hwp;
+
+ hwp = hwtypes;
+ while (*hwp != NULL) {
+ if (!strcmp((*hwp)->name, name))
+ return (*hwp);
+ hwp++;
+ }
+ return NULL;
+}
+
+/* Check our hardware type table for this type. */
+const struct hwtype *get_hwntype(int type)
+{
+ const struct hwtype *const *hwp;
+
+ hwp = hwtypes;
+ while (*hwp != NULL) {
+ if ((*hwp)->type == type)
+ return *hwp;
+ hwp++;
+ }
+ return NULL;
+}
+
+/* return 1 if address is all zeros */
+static int hw_null_address(const struct hwtype *hw, void *ap)
+{
+ unsigned int i;
+ unsigned char *address = (unsigned char *) ap;
+
+ for (i = 0; i < hw->alen; i++)
+ if (address[i])
+ return 0;
+ return 1;
+}
+
+static const char TRext[] = "\0\0\0Ki\0Mi\0Gi\0Ti";
+
+static void print_bytes_scaled(unsigned long long ull, const char *end)
+{
+ unsigned long long int_part;
+ const char *ext;
+ unsigned int frac_part;
+ int i;
+
+ frac_part = 0;
+ ext = TRext;
+ int_part = ull;
+ i = 4;
+ do {
+ if (int_part >= 1024) {
+ frac_part = ((((unsigned int) int_part) & (1024-1)) * 10) / 1024;
+ int_part /= 1024;
+ ext += 3; /* KiB, MiB, GiB, TiB */
+ }
+ --i;
+ } while (i);
+
+ printf("X bytes:%llu (%llu.%u %sB)%s", ull, int_part, frac_part, ext, end);
+}
+
+static const char *const ife_print_flags_strs[] = {
+ "UP ",
+ "BROADCAST ",
+ "DEBUG ",
+ "LOOPBACK ",
+ "POINTOPOINT ",
+ "NOTRAILERS ",
+ "RUNNING ",
+ "NOARP ",
+ "PROMISC ",
+ "ALLMULTI ",
+ "SLAVE ",
+ "MASTER ",
+ "MULTICAST ",
+#ifdef HAVE_DYNAMIC
+ "DYNAMIC "
+#endif
+};
+
+static const unsigned short ife_print_flags_mask[] = {
+ IFF_UP,
+ IFF_BROADCAST,
+ IFF_DEBUG,
+ IFF_LOOPBACK,
+ IFF_POINTOPOINT,
+ IFF_NOTRAILERS,
+ IFF_RUNNING,
+ IFF_NOARP,
+ IFF_PROMISC,
+ IFF_ALLMULTI,
+ IFF_SLAVE,
+ IFF_MASTER,
+ IFF_MULTICAST,
+#ifdef HAVE_DYNAMIC
+ IFF_DYNAMIC
+#endif
+ 0
+};
+
+static void ife_print(struct interface *ptr)
+{
+ const struct aftype *ap;
+ const struct hwtype *hw;
+ int hf;
+ int can_compress = 0;
+
+#ifdef HAVE_AFINET6
+ FILE *f;
+ char addr6[40], devname[20];
+ struct sockaddr_in6 sap;
+ int plen, scope, dad_status, if_idx;
+ char addr6p[8][5];
+#endif
+
+ ap = get_afntype(ptr->addr.sa_family);
+ if (ap == NULL)
+ ap = get_afntype(0);
+
+ hf = ptr->type;
+
+ if (hf == ARPHRD_CSLIP || hf == ARPHRD_CSLIP6)
+ can_compress = 1;
+
+ hw = get_hwntype(hf);
+ if (hw == NULL)
+ hw = get_hwntype(-1);
+
+ printf("%-9.9s Link encap:%s ", ptr->name, hw->title);
+ /* For some hardware types (eg Ash, ATM) we don't print the
+ hardware address if it's null. */
+ if (hw->print != NULL && (!(hw_null_address(hw, ptr->hwaddr) &&
+ hw->suppress_null_addr)))
+ printf("HWaddr %s ", hw->print((unsigned char *)ptr->hwaddr));
+#ifdef IFF_PORTSEL
+ if (ptr->flags & IFF_PORTSEL) {
+ printf("Media:%s", if_port_text[ptr->map.port] /* [0] */);
+ if (ptr->flags & IFF_AUTOMEDIA)
+ printf("(auto)");
+ }
+#endif
+ puts("");
+
+ if (ptr->has_ip) {
+ printf(" %s addr:%s ", ap->name,
+ ap->sprint(&ptr->addr, 1));
+ if (ptr->flags & IFF_POINTOPOINT) {
+ printf(" P-t-P:%s ", ap->sprint(&ptr->dstaddr, 1));
+ }
+ if (ptr->flags & IFF_BROADCAST) {
+ printf(" Bcast:%s ", ap->sprint(&ptr->broadaddr, 1));
+ }
+ printf(" Mask:%s\n", ap->sprint(&ptr->netmask, 1));
+ }
+
+#ifdef HAVE_AFINET6
+
+#define IPV6_ADDR_ANY 0x0000U
+
+#define IPV6_ADDR_UNICAST 0x0001U
+#define IPV6_ADDR_MULTICAST 0x0002U
+#define IPV6_ADDR_ANYCAST 0x0004U
+
+#define IPV6_ADDR_LOOPBACK 0x0010U
+#define IPV6_ADDR_LINKLOCAL 0x0020U
+#define IPV6_ADDR_SITELOCAL 0x0040U
+
+#define IPV6_ADDR_COMPATv4 0x0080U
+
+#define IPV6_ADDR_SCOPE_MASK 0x00f0U
+
+#define IPV6_ADDR_MAPPED 0x1000U
+#define IPV6_ADDR_RESERVED 0x2000U /* reserved address space */
+
+ f = fopen(_PATH_PROCNET_IFINET6, "r");
+ if (f != NULL) {
+ while (fscanf
+ (f, "%4s%4s%4s%4s%4s%4s%4s%4s %02x %02x %02x %02x %20s\n",
+ addr6p[0], addr6p[1], addr6p[2], addr6p[3], addr6p[4],
+ addr6p[5], addr6p[6], addr6p[7], &if_idx, &plen, &scope,
+ &dad_status, devname) != EOF
+ ) {
+ if (!strcmp(devname, ptr->name)) {
+ sprintf(addr6, "%s:%s:%s:%s:%s:%s:%s:%s",
+ addr6p[0], addr6p[1], addr6p[2], addr6p[3],
+ addr6p[4], addr6p[5], addr6p[6], addr6p[7]);
+ inet_pton(AF_INET6, addr6,
+ (struct sockaddr *) &sap.sin6_addr);
+ sap.sin6_family = AF_INET6;
+ printf(" inet6 addr: %s/%d",
+ inet6_aftype.sprint((struct sockaddr *) &sap, 1),
+ plen);
+ printf(" Scope:");
+ switch (scope & IPV6_ADDR_SCOPE_MASK) {
+ case 0:
+ printf("Global");
+ break;
+ case IPV6_ADDR_LINKLOCAL:
+ printf("Link");
+ break;
+ case IPV6_ADDR_SITELOCAL:
+ printf("Site");
+ break;
+ case IPV6_ADDR_COMPATv4:
+ printf("Compat");
+ break;
+ case IPV6_ADDR_LOOPBACK:
+ printf("Host");
+ break;
+ default:
+ printf("Unknown");
+ }
+ puts("");
+ }
+ }
+ fclose(f);
+ }
+#endif
+
+ printf(" ");
+ /* DONT FORGET TO ADD THE FLAGS IN ife_print_short, too */
+
+ if (ptr->flags == 0) {
+ printf("[NO FLAGS] ");
+ } else {
+ int i = 0;
+ do {
+ if (ptr->flags & ife_print_flags_mask[i]) {
+ printf(ife_print_flags_strs[i]);
+ }
+ } while (ife_print_flags_mask[++i]);
+ }
+
+ /* DONT FORGET TO ADD THE FLAGS IN ife_print_short */
+ printf(" MTU:%d Metric:%d", ptr->mtu, ptr->metric ? ptr->metric : 1);
+#ifdef SIOCSKEEPALIVE
+ if (ptr->outfill || ptr->keepalive)
+ printf(" Outfill:%d Keepalive:%d", ptr->outfill, ptr->keepalive);
+#endif
+ puts("");
+
+ /* If needed, display the interface statistics. */
+
+ if (ptr->statistics_valid) {
+ /* XXX: statistics are currently only printed for the primary address,
+ * not for the aliases, although strictly speaking they're shared
+ * by all addresses.
+ */
+ printf(" ");
+
+ printf("RX packets:%llu errors:%lu dropped:%lu overruns:%lu frame:%lu\n",
+ ptr->stats.rx_packets, ptr->stats.rx_errors,
+ ptr->stats.rx_dropped, ptr->stats.rx_fifo_errors,
+ ptr->stats.rx_frame_errors);
+ if (can_compress)
+ printf(" compressed:%lu\n",
+ ptr->stats.rx_compressed);
+ printf(" ");
+ printf("TX packets:%llu errors:%lu dropped:%lu overruns:%lu carrier:%lu\n",
+ ptr->stats.tx_packets, ptr->stats.tx_errors,
+ ptr->stats.tx_dropped, ptr->stats.tx_fifo_errors,
+ ptr->stats.tx_carrier_errors);
+ printf(" collisions:%lu ", ptr->stats.collisions);
+ if (can_compress)
+ printf("compressed:%lu ", ptr->stats.tx_compressed);
+ if (ptr->tx_queue_len != -1)
+ printf("txqueuelen:%d ", ptr->tx_queue_len);
+ printf("\n R");
+ print_bytes_scaled(ptr->stats.rx_bytes, " T");
+ print_bytes_scaled(ptr->stats.tx_bytes, "\n");
+
+ }
+
+ if ((ptr->map.irq || ptr->map.mem_start || ptr->map.dma ||
+ ptr->map.base_addr)) {
+ printf(" ");
+ if (ptr->map.irq)
+ printf("Interrupt:%d ", ptr->map.irq);
+ if (ptr->map.base_addr >= 0x100) /* Only print devices using it for
+ I/O maps */
+ printf("Base address:0x%lx ",
+ (unsigned long) ptr->map.base_addr);
+ if (ptr->map.mem_start) {
+ printf("Memory:%lx-%lx ", ptr->map.mem_start,
+ ptr->map.mem_end);
+ }
+ if (ptr->map.dma)
+ printf("DMA chan:%x ", ptr->map.dma);
+ puts("");
+ }
+ puts("");
+}
+
+
+static int do_if_print(struct interface *ife, void *cookie)
+{
+ int *opt_a = (int *) cookie;
+ int res;
+
+ res = do_if_fetch(ife);
+ if (res >= 0) {
+ if ((ife->flags & IFF_UP) || *opt_a)
+ ife_print(ife);
+ }
+ return res;
+}
+
+static struct interface *lookup_interface(char *name)
+{
+ struct interface *ife = NULL;
+
+ if (if_readlist_proc(name) < 0)
+ return NULL;
+ ife = add_interface(name);
+ return ife;
+}
+
+/* for ipv4 add/del modes */
+static int if_print(char *ifname)
+{
+ int res;
+
+ if (!ifname) {
+ res = for_all_interfaces(do_if_print, &interface_opt_a);
+ } else {
+ struct interface *ife;
+
+ ife = lookup_interface(ifname);
+ res = do_if_fetch(ife);
+ if (res >= 0)
+ ife_print(ife);
+ }
+ return res;
+}
+
+int display_interfaces(char *ifname)
+{
+ int status;
+
+ status = if_print(ifname);
+
+ return (status < 0); /* status < 0 == 1 -- error */
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/ip.c b/i/pc104/initrd/conf/busybox/networking/ip.c
new file mode 100644
index 0000000..fdd8480
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/ip.c
@@ -0,0 +1,50 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ip.c "ip" utility frontend.
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ *
+ * Changes:
+ *
+ * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
+ */
+
+#include "busybox.h"
+
+#include "libiproute/utils.h"
+#include "libiproute/ip_common.h"
+
+int ip_main(int argc, char **argv);
+int ip_main(int argc, char **argv)
+{
+ int ret = EXIT_FAILURE;
+
+ ip_parse_common_args(&argc, &argv);
+
+ if (argc > 1) {
+ if (ENABLE_FEATURE_IP_ADDRESS && matches(argv[1], "address") == 0) {
+ ret = do_ipaddr(argc-2, argv+2);
+ }
+ if (ENABLE_FEATURE_IP_ROUTE && matches(argv[1], "route") == 0) {
+ ret = do_iproute(argc-2, argv+2);
+ }
+ if (ENABLE_FEATURE_IP_LINK && matches(argv[1], "link") == 0) {
+ ret = do_iplink(argc-2, argv+2);
+ }
+ if (ENABLE_FEATURE_IP_TUNNEL
+ && (matches(argv[1], "tunnel") == 0 || strcmp(argv[1], "tunl") == 0)
+ ) {
+ ret = do_iptunnel(argc-2, argv+2);
+ }
+ if (ENABLE_FEATURE_IP_RULE && matches(argv[1], "rule") == 0) {
+ ret = do_iprule(argc-2, argv+2);
+ }
+ }
+ if (ret) {
+ bb_show_usage();
+ }
+ return EXIT_SUCCESS;
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/ipaddr.c b/i/pc104/initrd/conf/busybox/networking/ipaddr.c
new file mode 100644
index 0000000..fb02137
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/ipaddr.c
@@ -0,0 +1,26 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ip.c "ip" utility frontend.
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ *
+ * Changes:
+ *
+ * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
+ */
+
+#include "libiproute/utils.h"
+#include "libiproute/ip_common.h"
+
+#include "busybox.h"
+
+int ipaddr_main(int argc, char **argv);
+int ipaddr_main(int argc, char **argv)
+{
+ ip_parse_common_args(&argc, &argv);
+
+ return do_ipaddr(argc-1, argv+1);
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/ipcalc.c b/i/pc104/initrd/conf/busybox/networking/ipcalc.c
new file mode 100644
index 0000000..63ce99f
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/ipcalc.c
@@ -0,0 +1,195 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini ipcalc implementation for busybox
+ *
+ * By Jordan Crouse <jordan@cosmicpenguin.net>
+ * Stephan Linz <linz@li-pro.net>
+ *
+ * This is a complete reimplementation of the ipcalc program
+ * from Red Hat. I didn't look at their source code, but there
+ * is no denying that this is a loving reimplementation
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "busybox.h"
+#include <ctype.h>
+#include <getopt.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+#define CLASS_A_NETMASK ntohl(0xFF000000)
+#define CLASS_B_NETMASK ntohl(0xFFFF0000)
+#define CLASS_C_NETMASK ntohl(0xFFFFFF00)
+
+static unsigned long get_netmask(unsigned long ipaddr)
+{
+ ipaddr = htonl(ipaddr);
+
+ if ((ipaddr & 0xC0000000) == 0xC0000000)
+ return CLASS_C_NETMASK;
+ else if ((ipaddr & 0x80000000) == 0x80000000)
+ return CLASS_B_NETMASK;
+ else if ((ipaddr & 0x80000000) == 0)
+ return CLASS_A_NETMASK;
+ else
+ return 0;
+}
+
+#ifdef CONFIG_FEATURE_IPCALC_FANCY
+static int get_prefix(unsigned long netmask)
+{
+ unsigned long msk = 0x80000000;
+ int ret = 0;
+
+ netmask = htonl(netmask);
+ while (msk) {
+ if (netmask & msk)
+ ret++;
+ msk >>= 1;
+ }
+ return ret;
+}
+#else
+int get_prefix(unsigned long netmask);
+#endif
+
+
+#define NETMASK 0x01
+#define BROADCAST 0x02
+#define NETWORK 0x04
+#define NETPREFIX 0x08
+#define HOSTNAME 0x10
+#define SILENT 0x20
+
+#if ENABLE_FEATURE_IPCALC_LONG_OPTIONS
+ static const struct option long_options[] = {
+ { "netmask", no_argument, NULL, 'm' },
+ { "broadcast", no_argument, NULL, 'b' },
+ { "network", no_argument, NULL, 'n' },
+# if ENABLE_FEATURE_IPCALC_FANCY
+ { "prefix", no_argument, NULL, 'p' },
+ { "hostname", no_argument, NULL, 'h' },
+ { "silent", no_argument, NULL, 's' },
+# endif
+ { NULL, 0, NULL, 0 }
+ };
+#endif
+
+int ipcalc_main(int argc, char **argv);
+int ipcalc_main(int argc, char **argv)
+{
+ unsigned opt;
+ int have_netmask = 0;
+ in_addr_t netmask, broadcast, network, ipaddr;
+ struct in_addr a;
+ char *ipstr;
+
+#if ENABLE_FEATURE_IPCALC_LONG_OPTIONS
+ applet_long_options = long_options;
+#endif
+ opt = getopt32(argc, argv, "mbn" USE_FEATURE_IPCALC_FANCY("phs"));
+ argc -= optind;
+ argv += optind;
+ if (opt & (BROADCAST | NETWORK | NETPREFIX)) {
+ if (argc > 2 || argc <= 0)
+ bb_show_usage();
+ } else {
+ if (argc != 1)
+ bb_show_usage();
+ }
+ if (opt & SILENT)
+ logmode = LOGMODE_NONE; /* Suppress error_msg() output */
+
+ ipstr = argv[0];
+ if (ENABLE_FEATURE_IPCALC_FANCY) {
+ unsigned long netprefix = 0;
+ char *prefixstr;
+
+ prefixstr = ipstr;
+
+ while (*prefixstr) {
+ if (*prefixstr == '/') {
+ *prefixstr = (char)0;
+ prefixstr++;
+ if (*prefixstr) {
+ unsigned msk;
+ netprefix = xatoul_range(prefixstr, 0, 32);
+ netmask = 0;
+ msk = 0x80000000;
+ while (netprefix > 0) {
+ netmask |= msk;
+ msk >>= 1;
+ netprefix--;
+ }
+ netmask = htonl(netmask);
+ /* Even if it was 0, we will signify that we have a netmask. This allows */
+ /* for specification of default routes, etc which have a 0 netmask/prefix */
+ have_netmask = 1;
+ }
+ break;
+ }
+ prefixstr++;
+ }
+ }
+ ipaddr = inet_aton(ipstr, &a);
+
+ if (ipaddr == 0) {
+ bb_error_msg_and_die("bad IP address: %s", argv[0]);
+ }
+ ipaddr = a.s_addr;
+
+ if (argc == 2) {
+ if (ENABLE_FEATURE_IPCALC_FANCY && have_netmask) {
+ bb_error_msg_and_die("use prefix or netmask, not both");
+ }
+
+ netmask = inet_aton(argv[1], &a);
+ if (netmask == 0) {
+ bb_error_msg_and_die("bad netmask: %s", argv[1]);
+ }
+ netmask = a.s_addr;
+ } else {
+
+ /* JHC - If the netmask wasn't provided then calculate it */
+ if (!ENABLE_FEATURE_IPCALC_FANCY || !have_netmask)
+ netmask = get_netmask(ipaddr);
+ }
+
+ if (opt & NETMASK) {
+ printf("NETMASK=%s\n", inet_ntoa((*(struct in_addr *) &netmask)));
+ }
+
+ if (opt & BROADCAST) {
+ broadcast = (ipaddr & netmask) | ~netmask;
+ printf("BROADCAST=%s\n", inet_ntoa((*(struct in_addr *) &broadcast)));
+ }
+
+ if (opt & NETWORK) {
+ network = ipaddr & netmask;
+ printf("NETWORK=%s\n", inet_ntoa((*(struct in_addr *) &network)));
+ }
+
+ if (ENABLE_FEATURE_IPCALC_FANCY) {
+ if (opt & NETPREFIX) {
+ printf("PREFIX=%i\n", get_prefix(netmask));
+ }
+
+ if (opt & HOSTNAME) {
+ struct hostent *hostinfo;
+ int x;
+
+ hostinfo = gethostbyaddr((char *) &ipaddr, sizeof(ipaddr), AF_INET);
+ if (!hostinfo) {
+ bb_herror_msg_and_die("cannot find hostname for %s", argv[0]);
+ }
+ for (x = 0; hostinfo->h_name[x]; x++) {
+ hostinfo->h_name[x] = tolower(hostinfo->h_name[x]);
+ }
+
+ printf("HOSTNAME=%s\n", hostinfo->h_name);
+ }
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/iplink.c b/i/pc104/initrd/conf/busybox/networking/iplink.c
new file mode 100644
index 0000000..54087e9
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/iplink.c
@@ -0,0 +1,26 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ip.c "ip" utility frontend.
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ *
+ * Changes:
+ *
+ * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
+ */
+
+#include "libiproute/utils.h"
+#include "libiproute/ip_common.h"
+
+#include "busybox.h"
+
+int iplink_main(int argc, char **argv);
+int iplink_main(int argc, char **argv)
+{
+ ip_parse_common_args(&argc, &argv);
+
+ return do_iplink(argc-1, argv+1);
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/iproute.c b/i/pc104/initrd/conf/busybox/networking/iproute.c
new file mode 100644
index 0000000..3d540b2
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/iproute.c
@@ -0,0 +1,26 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ip.c "ip" utility frontend.
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ *
+ * Changes:
+ *
+ * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
+ */
+
+#include "libiproute/utils.h"
+#include "libiproute/ip_common.h"
+
+#include "busybox.h"
+
+int iproute_main(int argc, char **argv);
+int iproute_main(int argc, char **argv)
+{
+ ip_parse_common_args(&argc, &argv);
+
+ return do_iproute(argc-1, argv+1);
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/iprule.c b/i/pc104/initrd/conf/busybox/networking/iprule.c
new file mode 100644
index 0000000..9c1fb50
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/iprule.c
@@ -0,0 +1,26 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ip.c "ip" utility frontend.
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ *
+ * Changes:
+ *
+ * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
+ */
+
+#include "libiproute/utils.h"
+#include "libiproute/ip_common.h"
+
+#include "busybox.h"
+
+int iprule_main(int argc, char **argv);
+int iprule_main(int argc, char **argv)
+{
+ ip_parse_common_args(&argc, &argv);
+
+ return do_iprule(argc-1, argv+1);
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/iptunnel.c b/i/pc104/initrd/conf/busybox/networking/iptunnel.c
new file mode 100644
index 0000000..8a65413
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/iptunnel.c
@@ -0,0 +1,26 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ip.c "ip" utility frontend.
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ *
+ * Changes:
+ *
+ * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
+ */
+
+#include "libiproute/utils.h"
+#include "libiproute/ip_common.h"
+
+#include "busybox.h"
+
+int iptunnel_main(int argc, char **argv);
+int iptunnel_main(int argc, char **argv)
+{
+ ip_parse_common_args(&argc, &argv);
+
+ return do_iptunnel(argc-1, argv+1);
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/isrv.c b/i/pc104/initrd/conf/busybox/networking/isrv.c
new file mode 100644
index 0000000..f0df222
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/isrv.c
@@ -0,0 +1,352 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Generic non-forking server infrastructure.
+ * Intended to make writing telnetd-type servers easier.
+ *
+ * Copyright (C) 2007 Denis Vlasenko
+ *
+ * Licensed under GPL version 2, see file LICENSE in this tarball for details.
+ */
+
+#include "busybox.h"
+#include "isrv.h"
+
+#define DEBUG 0
+
+#if DEBUG
+#define DPRINTF(args...) bb_error_msg(args)
+#else
+#define DPRINTF(args...) ((void)0)
+#endif
+
+/* Helpers */
+
+/* Even if _POSIX_MONOTONIC_CLOCK is defined, this
+ * may require librt */
+#if 0 /*def _POSIX_MONOTONIC_CLOCK*/
+static time_t monotonic_time(void)
+{
+ struct timespec ts;
+ if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0)
+ time(&ts.tv_sec);
+ return ts.tv_sec;
+}
+#else
+#define monotonic_time() (time(NULL))
+#endif
+
+/* Opaque structure */
+
+struct isrv_state_t {
+ short *fd2peer; /* one per registered fd */
+ void **param_tbl; /* one per registered peer */
+ /* one per registered peer; doesn't exist if !timeout */
+ time_t *timeo_tbl;
+ int (*new_peer)(isrv_state_t *state, int fd);
+ time_t curtime;
+ int timeout;
+ int fd_count;
+ int peer_count;
+ int wr_count;
+ fd_set rd;
+ fd_set wr;
+};
+#define FD2PEER (state->fd2peer)
+#define PARAM_TBL (state->param_tbl)
+#define TIMEO_TBL (state->timeo_tbl)
+#define CURTIME (state->curtime)
+#define TIMEOUT (state->timeout)
+#define FD_COUNT (state->fd_count)
+#define PEER_COUNT (state->peer_count)
+#define WR_COUNT (state->wr_count)
+
+/* callback */
+void isrv_want_rd(isrv_state_t *state, int fd)
+{
+ FD_SET(fd, &state->rd);
+}
+
+/* callback */
+void isrv_want_wr(isrv_state_t *state, int fd)
+{
+ if (!FD_ISSET(fd, &state->wr)) {
+ WR_COUNT++;
+ FD_SET(fd, &state->wr);
+ }
+}
+
+/* callback */
+void isrv_dont_want_rd(isrv_state_t *state, int fd)
+{
+ FD_CLR(fd, &state->rd);
+}
+
+/* callback */
+void isrv_dont_want_wr(isrv_state_t *state, int fd)
+{
+ if (FD_ISSET(fd, &state->wr)) {
+ WR_COUNT--;
+ FD_CLR(fd, &state->wr);
+ }
+}
+
+/* callback */
+int isrv_register_fd(isrv_state_t *state, int peer, int fd)
+{
+ int n;
+
+ DPRINTF("register_fd(peer:%d,fd:%d)", peer, fd);
+
+ if (FD_COUNT >= FD_SETSIZE) return -1;
+ if (FD_COUNT <= fd) {
+ n = FD_COUNT;
+ FD_COUNT = fd + 1;
+
+ DPRINTF("register_fd: FD_COUNT %d", FD_COUNT);
+
+ FD2PEER = xrealloc(FD2PEER, FD_COUNT * sizeof(FD2PEER[0]));
+ while (n < fd) FD2PEER[n++] = -1;
+ }
+
+ DPRINTF("register_fd: FD2PEER[%d] = %d", fd, peer);
+
+ FD2PEER[fd] = peer;
+ return 0;
+}
+
+/* callback */
+void isrv_close_fd(isrv_state_t *state, int fd)
+{
+ DPRINTF("close_fd(%d)", fd);
+
+ close(fd);
+ isrv_dont_want_rd(state, fd);
+ if (WR_COUNT) isrv_dont_want_wr(state, fd);
+
+ FD2PEER[fd] = -1;
+ if (fd == FD_COUNT-1) {
+ do fd--; while (fd >= 0 && FD2PEER[fd] == -1);
+ FD_COUNT = fd + 1;
+
+ DPRINTF("close_fd: FD_COUNT %d", FD_COUNT);
+
+ FD2PEER = xrealloc(FD2PEER, FD_COUNT * sizeof(FD2PEER[0]));
+ }
+}
+
+/* callback */
+int isrv_register_peer(isrv_state_t *state, void *param)
+{
+ int n;
+
+ if (PEER_COUNT >= FD_SETSIZE) return -1;
+ n = PEER_COUNT++;
+
+ DPRINTF("register_peer: PEER_COUNT %d", PEER_COUNT);
+
+ PARAM_TBL = xrealloc(PARAM_TBL, PEER_COUNT * sizeof(PARAM_TBL[0]));
+ PARAM_TBL[n] = param;
+ if (TIMEOUT) {
+ TIMEO_TBL = xrealloc(TIMEO_TBL, PEER_COUNT * sizeof(TIMEO_TBL[0]));
+ TIMEO_TBL[n] = CURTIME;
+ }
+ return n;
+}
+
+static void remove_peer(isrv_state_t *state, int peer)
+{
+ int movesize;
+ int fd;
+
+ DPRINTF("remove_peer(%d)", peer);
+
+ fd = FD_COUNT - 1;
+ while (fd >= 0) {
+ if (FD2PEER[fd] == peer) {
+ isrv_close_fd(state, fd);
+ fd--;
+ continue;
+ }
+ if (FD2PEER[fd] > peer)
+ FD2PEER[fd]--;
+ fd--;
+ }
+
+ PEER_COUNT--;
+ DPRINTF("remove_peer: PEER_COUNT %d", PEER_COUNT);
+
+ movesize = (PEER_COUNT - peer) * sizeof(void*);
+ if (movesize > 0) {
+ memcpy(&PARAM_TBL[peer], &PARAM_TBL[peer+1], movesize);
+ if (TIMEOUT)
+ memcpy(&TIMEO_TBL[peer], &TIMEO_TBL[peer+1], movesize);
+ }
+ PARAM_TBL = xrealloc(PARAM_TBL, PEER_COUNT * sizeof(PARAM_TBL[0]));
+ if (TIMEOUT)
+ TIMEO_TBL = xrealloc(TIMEO_TBL, PEER_COUNT * sizeof(TIMEO_TBL[0]));
+}
+
+static void handle_accept(isrv_state_t *state, int fd)
+{
+ int n, newfd;
+
+ /* suppress gcc warning "cast from ptr to int of different size" */
+ fcntl(fd, F_SETFL, (int)(ptrdiff_t)(PARAM_TBL[0]) | O_NONBLOCK);
+ newfd = accept(fd, NULL, 0);
+ fcntl(fd, F_SETFL, (int)(ptrdiff_t)(PARAM_TBL[0]));
+ if (newfd < 0) {
+ if (errno == EAGAIN) return;
+ /* Most probably someone gave us wrong fd type
+ * (for example, non-socket). Don't want
+ * to loop forever. */
+ bb_perror_msg_and_die("accept");
+ }
+
+ DPRINTF("new_peer(%d)", newfd);
+ n = state->new_peer(state, newfd);
+ if (n)
+ remove_peer(state, n); /* unsuccesful peer start */
+}
+
+void BUG_sizeof_fd_set_is_strange(void);
+static void handle_fd_set(isrv_state_t *state, fd_set *fds, int (*h)(int, void **))
+{
+ enum { LONG_CNT = sizeof(fd_set) / sizeof(long) };
+ int fds_pos;
+ int fd, peer;
+ /* need to know value at _the beginning_ of this routine */
+ int fd_cnt = FD_COUNT;
+
+ if (LONG_CNT * sizeof(long) != sizeof(fd_set))
+ BUG_sizeof_fd_set_is_strange();
+
+ fds_pos = 0;
+ while (1) {
+ /* Find next nonzero bit */
+ while (fds_pos < LONG_CNT) {
+ if (((long*)fds)[fds_pos] == 0) {
+ fds_pos++;
+ continue;
+ }
+ /* Found non-zero word */
+ fd = fds_pos * sizeof(long)*8; /* word# -> bit# */
+ while (1) {
+ if (FD_ISSET(fd, fds)) {
+ FD_CLR(fd, fds);
+ goto found_fd;
+ }
+ fd++;
+ }
+ }
+ break; /* all words are zero */
+ found_fd:
+ if (fd >= fd_cnt) { /* paranoia */
+ DPRINTF("handle_fd_set: fd > fd_cnt?? (%d > %d)",
+ fd, fd_cnt);
+ break;
+ }
+ DPRINTF("handle_fd_set: fd %d is active", fd);
+ peer = FD2PEER[fd];
+ if (peer < 0)
+ continue; /* peer is already gone */
+ if (peer == 0) {
+ handle_accept(state, fd);
+ continue;
+ }
+ DPRINTF("h(fd:%d)", fd);
+ if (h(fd, &PARAM_TBL[peer])) {
+ /* this peer is gone */
+ remove_peer(state, peer);
+ } else if (TIMEOUT) {
+ TIMEO_TBL[peer] = monotonic_time();
+ }
+ }
+}
+
+static void handle_timeout(isrv_state_t *state, int (*do_timeout)(void **))
+{
+ int n, peer;
+ peer = PEER_COUNT-1;
+ /* peer 0 is not checked */
+ while (peer > 0) {
+ DPRINTF("peer %d: time diff %d", peer,
+ (int)(CURTIME - TIMEO_TBL[peer]));
+ if ((CURTIME - TIMEO_TBL[peer]) >= TIMEOUT) {
+ DPRINTF("peer %d: do_timeout()", peer);
+ n = do_timeout(&PARAM_TBL[peer]);
+ if (n)
+ remove_peer(state, peer);
+ }
+ peer--;
+ }
+}
+
+/* Driver */
+void isrv_run(
+ int listen_fd,
+ int (*new_peer)(isrv_state_t *state, int fd),
+ int (*do_rd)(int fd, void **),
+ int (*do_wr)(int fd, void **),
+ int (*do_timeout)(void **),
+ int timeout,
+ int linger_timeout)
+{
+ isrv_state_t *state = xzalloc(sizeof(*state));
+ state->new_peer = new_peer;
+ state->timeout = timeout;
+
+ /* register "peer" #0 - it will accept new connections */
+ isrv_register_peer(state, NULL);
+ isrv_register_fd(state, /*peer:*/ 0, listen_fd);
+ isrv_want_rd(state, listen_fd);
+ /* remember flags to make blocking<->nonblocking switch faster */
+ /* (suppress gcc warning "cast from ptr to int of different size") */
+ PARAM_TBL[0] = (void*)(ptrdiff_t)(fcntl(listen_fd, F_GETFL, 0));
+
+ while (1) {
+ struct timeval tv;
+ fd_set rd;
+ fd_set wr;
+ fd_set *wrp = NULL;
+ int n;
+
+ tv.tv_sec = timeout;
+ if (PEER_COUNT <= 1)
+ tv.tv_sec = linger_timeout;
+ tv.tv_usec = 0;
+ rd = state->rd;
+ if (WR_COUNT) {
+ wr = state->wr;
+ wrp = &wr;
+ }
+
+ DPRINTF("run: select(FD_COUNT:%d,timeout:%d)...",
+ FD_COUNT, (int)tv.tv_sec);
+ n = select(FD_COUNT, &rd, wrp, NULL, tv.tv_sec ? &tv : NULL);
+ DPRINTF("run: ...select:%d", n);
+
+ if (n < 0) {
+ if (errno != EINTR)
+ bb_perror_msg("select");
+ continue;
+ }
+
+ if (n == 0 && linger_timeout && PEER_COUNT <= 1)
+ break;
+
+ if (timeout) {
+ time_t t = monotonic_time();
+ if (t != CURTIME) {
+ CURTIME = t;
+ handle_timeout(state, do_timeout);
+ }
+ }
+ if (n > 0) {
+ handle_fd_set(state, &rd, do_rd);
+ if (wrp)
+ handle_fd_set(state, wrp, do_wr);
+ }
+ }
+ DPRINTF("run: bailout");
+ /* NB: accept socket is not closed. Caller is to decide what to do */
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/isrv.h b/i/pc104/initrd/conf/busybox/networking/isrv.h
new file mode 100644
index 0000000..c786770
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/isrv.h
@@ -0,0 +1,33 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Generic non-forking server infrastructure.
+ * Intended to make writing telnetd-type servers easier.
+ *
+ * Copyright (C) 2007 Denis Vlasenko
+ *
+ * Licensed under GPL version 2, see file LICENSE in this tarball for details.
+ */
+
+/* opaque structure */
+struct isrv_state_t;
+typedef struct isrv_state_t isrv_state_t;
+
+/* callbacks */
+void isrv_want_rd(isrv_state_t *state, int fd);
+void isrv_want_wr(isrv_state_t *state, int fd);
+void isrv_dont_want_rd(isrv_state_t *state, int fd);
+void isrv_dont_want_wr(isrv_state_t *state, int fd);
+int isrv_register_fd(isrv_state_t *state, int peer, int fd);
+void isrv_close_fd(isrv_state_t *state, int fd);
+int isrv_register_peer(isrv_state_t *state, void *param);
+
+/* driver */
+void isrv_run(
+ int listen_fd,
+ int (*new_peer)(isrv_state_t *state, int fd),
+ int (*do_rd)(int fd, void **),
+ int (*do_wr)(int fd, void **),
+ int (*do_timeout)(void **),
+ int timeout,
+ int linger_timeout
+);
diff --git a/i/pc104/initrd/conf/busybox/networking/isrv_identd.c b/i/pc104/initrd/conf/busybox/networking/isrv_identd.c
new file mode 100644
index 0000000..f9ab0b2
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/isrv_identd.c
@@ -0,0 +1,144 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Fake identd server.
+ *
+ * Copyright (C) 2007 Denis Vlasenko
+ *
+ * Licensed under GPL version 2, see file LICENSE in this tarball for details.
+ */
+
+#include <syslog.h>
+#include "busybox.h"
+#include "isrv.h"
+
+enum { TIMEOUT = 20 };
+
+typedef struct identd_buf_t {
+ int pos;
+ int fd_flag;
+ char buf[64 - 2*sizeof(int)];
+} identd_buf_t;
+
+static const char *bogouser = "nobody";
+
+static int new_peer(isrv_state_t *state, int fd)
+{
+ int peer;
+ identd_buf_t *buf = xzalloc(sizeof(*buf));
+
+ peer = isrv_register_peer(state, buf);
+ if (peer < 0)
+ return 0; /* failure */
+ if (isrv_register_fd(state, peer, fd) < 0)
+ return peer; /* failure, unregister peer */
+
+ buf->fd_flag = fcntl(fd, F_GETFL, 0) | O_NONBLOCK;
+ isrv_want_rd(state, fd);
+ return 0;
+}
+
+static int do_rd(int fd, void **paramp)
+{
+ identd_buf_t *buf = *paramp;
+ char *cur, *p;
+ int retval = 0; /* session is ok (so far) */
+ int sz;
+
+ cur = buf->buf + buf->pos;
+
+ if (buf->fd_flag & O_NONBLOCK)
+ fcntl(fd, F_SETFL, buf->fd_flag);
+ sz = safe_read(fd, cur, sizeof(buf->buf) - buf->pos);
+
+ if (sz < 0) {
+ if (errno != EAGAIN)
+ goto term; /* terminate this session if !EAGAIN */
+ goto ok;
+ }
+
+ buf->pos += sz;
+ buf->buf[buf->pos] = '\0';
+ p = strpbrk(cur, "\r\n");
+ if (p)
+ *p = '\0';
+ if (!p && sz && buf->pos <= sizeof(buf->buf))
+ goto ok;
+ /* Terminate session. If we are in server mode, then
+ * fd is still in nonblocking mode - we never block here */
+ if (fd == 0) fd++; /* inetd mode? then write to fd 1 */
+ fdprintf(fd, "%s : USERID : UNIX : %s\r\n", buf->buf, bogouser);
+ term:
+ free(buf);
+ retval = 1; /* terminate */
+ ok:
+ if (buf->fd_flag & O_NONBLOCK)
+ fcntl(fd, F_SETFL, buf->fd_flag & ~O_NONBLOCK);
+ return retval;
+}
+
+static int do_timeout(void **paramp)
+{
+ return 1; /* terminate session */
+}
+
+static void inetd_mode(void)
+{
+ identd_buf_t *buf = xzalloc(sizeof(*buf));
+ /* buf->pos = 0; - xzalloc did it */
+ /* We do NOT want nonblocking I/O here! */
+ /* buf->fd_flag = 0; - xzalloc did it */
+ do
+ alarm(TIMEOUT);
+ while (do_rd(0, (void*)&buf) == 0);
+}
+
+int fakeidentd_main(int argc, char **argv);
+int fakeidentd_main(int argc, char **argv)
+{
+ enum {
+ OPT_foreground = 0x1,
+ OPT_inetd = 0x2,
+ OPT_inetdwait = 0x4,
+ OPT_fiw = 0x7,
+ OPT_bindaddr = 0x8,
+ };
+
+ const char *bind_address = NULL;
+ unsigned opt;
+ int fd;
+
+ opt = getopt32(argc, argv, "fiwb:", &bind_address);
+ if (optind < argc)
+ bogouser = argv[optind];
+
+ /* Daemonize if no -f and no -i and no -w */
+ bb_sanitize_stdio_maybe_daemonize(!(opt & OPT_fiw));
+ /* Where to log in inetd modes? "Classic" inetd
+ * probably has its stderr /dev/null'ed (we need log to syslog?),
+ * but daemontools-like utilities usually expect that children
+ * log to stderr. I like daemontools more. Go their way.
+ * (Or maybe we need yet another option "log to syslog") */
+ if (!(opt & OPT_fiw) /* || (opt & OPT_syslog) */) {
+ openlog(applet_name, 0, LOG_DAEMON);
+ logmode = LOGMODE_SYSLOG;
+ }
+
+ if (opt & OPT_inetd) {
+ inetd_mode();
+ return 0;
+ }
+
+ /* Ignore closed connections when writing */
+ signal(SIGPIPE, SIG_IGN);
+
+ fd = 0;
+ if (!(opt & OPT_inetdwait)) {
+ fd = create_and_bind_stream_or_die(bind_address,
+ bb_lookup_port("identd", "tcp", 113));
+ xlisten(fd, 5);
+ }
+
+ isrv_run(fd, new_peer, do_rd, /*do_wr:*/ NULL, do_timeout,
+ TIMEOUT, (opt & OPT_inetdwait) ? TIMEOUT : 0);
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/libiproute/Kbuild b/i/pc104/initrd/conf/busybox/networking/libiproute/Kbuild
new file mode 100644
index 0000000..8383630
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/libiproute/Kbuild
@@ -0,0 +1,66 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+#
+
+lib-y:=
+lib-$(CONFIG_IP) += \
+ ip_parse_common_args.o \
+ ipaddress.o \
+ iplink.o \
+ iproute.o \
+ iptunnel.o \
+ iprule.o \
+ libnetlink.o \
+ ll_addr.o \
+ ll_map.o \
+ ll_proto.o \
+ ll_types.o \
+ rt_names.o \
+ rtm_map.o \
+ utils.o
+
+lib-$(CONFIG_IPADDR) += \
+ ip_parse_common_args.o \
+ ipaddress.o \
+ libnetlink.o \
+ ll_addr.o \
+ ll_map.o \
+ ll_types.o \
+ rt_names.o \
+ utils.o
+
+lib-$(CONFIG_IPLINK) += \
+ ip_parse_common_args.o \
+ ipaddress.o \
+ iplink.o \
+ libnetlink.o \
+ ll_addr.o \
+ ll_map.o \
+ ll_types.o \
+ rt_names.o \
+ utils.o
+
+lib-$(CONFIG_IPROUTE) += \
+ ip_parse_common_args.o \
+ iproute.o \
+ libnetlink.o \
+ ll_map.o \
+ rt_names.o \
+ rtm_map.o \
+ utils.o
+
+lib-$(CONFIG_IPTUNNEL) += \
+ ip_parse_common_args.o \
+ iptunnel.o \
+ rt_names.o \
+ utils.o
+
+lib-$(CONFIG_IPRULE) += \
+ ip_parse_common_args.o \
+ iprule.o \
+ rt_names.o \
+ utils.o
+
diff --git a/i/pc104/initrd/conf/busybox/networking/libiproute/ip_common.h b/i/pc104/initrd/conf/busybox/networking/libiproute/ip_common.h
new file mode 100644
index 0000000..05a6a95
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/libiproute/ip_common.h
@@ -0,0 +1,35 @@
+/* vi: set sw=4 ts=4: */
+#ifndef _IP_COMMON_H
+#define _IP_COMMON_H 1
+
+#include "busybox.h"
+#include <asm/types.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#if !defined IFA_RTA
+#include <linux/if_addr.h>
+#endif
+#if !defined IFLA_RTA
+#include <linux/if_link.h>
+#endif
+
+extern int preferred_family;
+//FIXME! Appears in two .h files!
+extern const char * _SL_;
+
+extern void ip_parse_common_args(int *argcp, char ***argvp);
+extern int print_neigh(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg);
+extern int ipaddr_list_or_flush(int argc, char **argv, int flush);
+extern int iproute_monitor(int argc, char **argv);
+extern void iplink_usage(void) ATTRIBUTE_NORETURN;
+extern void ipneigh_reset_filter(void);
+extern int do_ipaddr(int argc, char **argv);
+extern int do_iproute(int argc, char **argv);
+extern int do_iprule(int argc, char **argv);
+extern int do_ipneigh(int argc, char **argv);
+extern int do_iptunnel(int argc, char **argv);
+extern int do_iplink(int argc, char **argv);
+extern int do_ipmonitor(int argc, char **argv);
+extern int do_multiaddr(int argc, char **argv);
+extern int do_multiroute(int argc, char **argv);
+#endif /* ip_common.h */
diff --git a/i/pc104/initrd/conf/busybox/networking/libiproute/ip_parse_common_args.c b/i/pc104/initrd/conf/busybox/networking/libiproute/ip_parse_common_args.c
new file mode 100644
index 0000000..00a8912
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/libiproute/ip_parse_common_args.c
@@ -0,0 +1,79 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ip.c "ip" utility frontend.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ *
+ * Changes:
+ *
+ * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
+ */
+
+#include <string.h>
+
+#include "libbb.h"
+#include "utils.h"
+#include "ip_common.h"
+
+
+int preferred_family = AF_UNSPEC;
+int oneline = 0;
+const char * _SL_ = NULL;
+
+void ip_parse_common_args(int *argcp, char ***argvp)
+{
+ int argc = *argcp;
+ char **argv = *argvp;
+
+ while (argc > 1) {
+ char *opt = argv[1];
+
+ if (strcmp(opt,"--") == 0) {
+ argc--;
+ argv++;
+ break;
+ }
+
+ if (opt[0] != '-')
+ break;
+
+ if (opt[1] == '-')
+ opt++;
+
+ if (matches(opt, "-family") == 0) {
+ argc--;
+ argv++;
+ if (!argv[1])
+ bb_show_usage();
+ if (strcmp(argv[1], "inet") == 0)
+ preferred_family = AF_INET;
+ else if (strcmp(argv[1], "inet6") == 0)
+ preferred_family = AF_INET6;
+ else if (strcmp(argv[1], "link") == 0)
+ preferred_family = AF_PACKET;
+ else
+ invarg(argv[1], "protocol family");
+ } else if (strcmp(opt, "-4") == 0) {
+ preferred_family = AF_INET;
+ } else if (strcmp(opt, "-6") == 0) {
+ preferred_family = AF_INET6;
+ } else if (strcmp(opt, "-0") == 0) {
+ preferred_family = AF_PACKET;
+ } else if (matches(opt, "-oneline") == 0) {
+ ++oneline;
+ } else {
+ bb_show_usage();
+ }
+ argc--;
+ argv++;
+ }
+ _SL_ = oneline ? "\\" : "\n" ;
+ *argcp = argc;
+ *argvp = argv;
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/libiproute/ipaddress.c b/i/pc104/initrd/conf/busybox/networking/libiproute/ipaddress.c
new file mode 100644
index 0000000..bc9963f
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/libiproute/ipaddress.c
@@ -0,0 +1,824 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ipaddress.c "ip address".
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ * Changes:
+ * Laszlo Valko <valko@linux.karinthy.hu> 990223: address label must be zero terminated
+ */
+
+#include "libbb.h"
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+
+#include <fnmatch.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <net/if.h>
+#include <net/if_arp.h>
+
+#include "rt_names.h"
+#include "utils.h"
+#include "ip_common.h"
+
+
+static struct
+{
+ int ifindex;
+ int family;
+ int oneline;
+ int showqueue;
+ inet_prefix pfx;
+ int scope, scopemask;
+ int flags, flagmask;
+ int up;
+ char *label;
+ int flushed;
+ char *flushb;
+ int flushp;
+ int flushe;
+ struct rtnl_handle *rth;
+} filter;
+
+static void print_link_flags(FILE *fp, unsigned flags, unsigned mdown)
+{
+ fprintf(fp, "<");
+ flags &= ~IFF_RUNNING;
+#define _PF(f) if (flags&IFF_##f) { \
+ flags &= ~IFF_##f ; \
+ fprintf(fp, #f "%s", flags ? "," : ""); }
+ _PF(LOOPBACK);
+ _PF(BROADCAST);
+ _PF(POINTOPOINT);
+ _PF(MULTICAST);
+ _PF(NOARP);
+#if 0
+ _PF(ALLMULTI);
+ _PF(PROMISC);
+ _PF(MASTER);
+ _PF(SLAVE);
+ _PF(DEBUG);
+ _PF(DYNAMIC);
+ _PF(AUTOMEDIA);
+ _PF(PORTSEL);
+ _PF(NOTRAILERS);
+#endif
+ _PF(UP);
+#undef _PF
+ if (flags)
+ fprintf(fp, "%x", flags);
+ if (mdown)
+ fprintf(fp, ",M-DOWN");
+ fprintf(fp, "> ");
+}
+
+static void print_queuelen(char *name)
+{
+ struct ifreq ifr;
+ int s;
+
+ s = socket(AF_INET, SOCK_STREAM, 0);
+ if (s < 0)
+ return;
+
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
+ if (ioctl(s, SIOCGIFTXQLEN, &ifr) < 0) {
+ perror("SIOCGIFXQLEN");
+ close(s);
+ return;
+ }
+ close(s);
+
+ if (ifr.ifr_qlen)
+ printf("qlen %d", ifr.ifr_qlen);
+}
+
+static int print_linkinfo(struct sockaddr_nl ATTRIBUTE_UNUSED *who,
+ const struct nlmsghdr *n, void ATTRIBUTE_UNUSED *arg)
+{
+ FILE *fp = (FILE*)arg;
+ struct ifinfomsg *ifi = NLMSG_DATA(n);
+ struct rtattr * tb[IFLA_MAX+1];
+ int len = n->nlmsg_len;
+ unsigned m_flag = 0;
+
+ if (n->nlmsg_type != RTM_NEWLINK && n->nlmsg_type != RTM_DELLINK)
+ return 0;
+
+ len -= NLMSG_LENGTH(sizeof(*ifi));
+ if (len < 0)
+ return -1;
+
+ if (filter.ifindex && ifi->ifi_index != filter.ifindex)
+ return 0;
+ if (filter.up && !(ifi->ifi_flags&IFF_UP))
+ return 0;
+
+ memset(tb, 0, sizeof(tb));
+ parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len);
+ if (tb[IFLA_IFNAME] == NULL) {
+ bb_error_msg("nil ifname");
+ return -1;
+ }
+ if (filter.label &&
+ (!filter.family || filter.family == AF_PACKET) &&
+ fnmatch(filter.label, RTA_DATA(tb[IFLA_IFNAME]), 0))
+ return 0;
+
+ if (n->nlmsg_type == RTM_DELLINK)
+ fprintf(fp, "Deleted ");
+
+ fprintf(fp, "%d: %s", ifi->ifi_index,
+ tb[IFLA_IFNAME] ? (char*)RTA_DATA(tb[IFLA_IFNAME]) : "<nil>");
+
+ if (tb[IFLA_LINK]) {
+ SPRINT_BUF(b1);
+ int iflink = *(int*)RTA_DATA(tb[IFLA_LINK]);
+ if (iflink == 0)
+ fprintf(fp, "@NONE: ");
+ else {
+ fprintf(fp, "@%s: ", ll_idx_n2a(iflink, b1));
+ m_flag = ll_index_to_flags(iflink);
+ m_flag = !(m_flag & IFF_UP);
+ }
+ } else {
+ fprintf(fp, ": ");
+ }
+ print_link_flags(fp, ifi->ifi_flags, m_flag);
+
+ if (tb[IFLA_MTU])
+ fprintf(fp, "mtu %u ", *(int*)RTA_DATA(tb[IFLA_MTU]));
+ if (tb[IFLA_QDISC])
+ fprintf(fp, "qdisc %s ", (char*)RTA_DATA(tb[IFLA_QDISC]));
+#ifdef IFLA_MASTER
+ if (tb[IFLA_MASTER]) {
+ SPRINT_BUF(b1);
+ fprintf(fp, "master %s ", ll_idx_n2a(*(int*)RTA_DATA(tb[IFLA_MASTER]), b1));
+ }
+#endif
+ if (filter.showqueue)
+ print_queuelen((char*)RTA_DATA(tb[IFLA_IFNAME]));
+
+ if (!filter.family || filter.family == AF_PACKET) {
+ SPRINT_BUF(b1);
+ fprintf(fp, "%s", _SL_);
+ fprintf(fp, " link/%s ", ll_type_n2a(ifi->ifi_type, b1, sizeof(b1)));
+
+ if (tb[IFLA_ADDRESS]) {
+ fprintf(fp, "%s", ll_addr_n2a(RTA_DATA(tb[IFLA_ADDRESS]),
+ RTA_PAYLOAD(tb[IFLA_ADDRESS]),
+ ifi->ifi_type,
+ b1, sizeof(b1)));
+ }
+ if (tb[IFLA_BROADCAST]) {
+ if (ifi->ifi_flags&IFF_POINTOPOINT)
+ fprintf(fp, " peer ");
+ else
+ fprintf(fp, " brd ");
+ fprintf(fp, "%s", ll_addr_n2a(RTA_DATA(tb[IFLA_BROADCAST]),
+ RTA_PAYLOAD(tb[IFLA_BROADCAST]),
+ ifi->ifi_type,
+ b1, sizeof(b1)));
+ }
+ }
+ fprintf(fp, "\n");
+ fflush(fp);
+ return 0;
+}
+
+static int flush_update(void)
+{
+ if (rtnl_send(filter.rth, filter.flushb, filter.flushp) < 0) {
+ perror("Failed to send flush request\n");
+ return -1;
+ }
+ filter.flushp = 0;
+ return 0;
+}
+
+static int print_addrinfo(struct sockaddr_nl ATTRIBUTE_UNUSED *who,
+ struct nlmsghdr *n, void ATTRIBUTE_UNUSED *arg)
+{
+ FILE *fp = (FILE*)arg;
+ struct ifaddrmsg *ifa = NLMSG_DATA(n);
+ int len = n->nlmsg_len;
+ struct rtattr * rta_tb[IFA_MAX+1];
+ char abuf[256];
+ SPRINT_BUF(b1);
+
+ if (n->nlmsg_type != RTM_NEWADDR && n->nlmsg_type != RTM_DELADDR)
+ return 0;
+ len -= NLMSG_LENGTH(sizeof(*ifa));
+ if (len < 0) {
+ bb_error_msg("wrong nlmsg len %d", len);
+ return -1;
+ }
+
+ if (filter.flushb && n->nlmsg_type != RTM_NEWADDR)
+ return 0;
+
+ memset(rta_tb, 0, sizeof(rta_tb));
+ parse_rtattr(rta_tb, IFA_MAX, IFA_RTA(ifa), n->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa)));
+
+ if (!rta_tb[IFA_LOCAL])
+ rta_tb[IFA_LOCAL] = rta_tb[IFA_ADDRESS];
+ if (!rta_tb[IFA_ADDRESS])
+ rta_tb[IFA_ADDRESS] = rta_tb[IFA_LOCAL];
+
+ if (filter.ifindex && filter.ifindex != ifa->ifa_index)
+ return 0;
+ if ((filter.scope^ifa->ifa_scope)&filter.scopemask)
+ return 0;
+ if ((filter.flags^ifa->ifa_flags)&filter.flagmask)
+ return 0;
+ if (filter.label) {
+ const char *label;
+ if (rta_tb[IFA_LABEL])
+ label = RTA_DATA(rta_tb[IFA_LABEL]);
+ else
+ label = ll_idx_n2a(ifa->ifa_index, b1);
+ if (fnmatch(filter.label, label, 0) != 0)
+ return 0;
+ }
+ if (filter.pfx.family) {
+ if (rta_tb[IFA_LOCAL]) {
+ inet_prefix dst;
+ memset(&dst, 0, sizeof(dst));
+ dst.family = ifa->ifa_family;
+ memcpy(&dst.data, RTA_DATA(rta_tb[IFA_LOCAL]), RTA_PAYLOAD(rta_tb[IFA_LOCAL]));
+ if (inet_addr_match(&dst, &filter.pfx, filter.pfx.bitlen))
+ return 0;
+ }
+ }
+
+ if (filter.flushb) {
+ struct nlmsghdr *fn;
+ if (NLMSG_ALIGN(filter.flushp) + n->nlmsg_len > filter.flushe) {
+ if (flush_update())
+ return -1;
+ }
+ fn = (struct nlmsghdr*)(filter.flushb + NLMSG_ALIGN(filter.flushp));
+ memcpy(fn, n, n->nlmsg_len);
+ fn->nlmsg_type = RTM_DELADDR;
+ fn->nlmsg_flags = NLM_F_REQUEST;
+ fn->nlmsg_seq = ++filter.rth->seq;
+ filter.flushp = (((char*)fn) + n->nlmsg_len) - filter.flushb;
+ filter.flushed++;
+ return 0;
+ }
+
+ if (n->nlmsg_type == RTM_DELADDR)
+ fprintf(fp, "Deleted ");
+
+ if (filter.oneline)
+ fprintf(fp, "%u: %s", ifa->ifa_index, ll_index_to_name(ifa->ifa_index));
+ if (ifa->ifa_family == AF_INET)
+ fprintf(fp, " inet ");
+ else if (ifa->ifa_family == AF_INET6)
+ fprintf(fp, " inet6 ");
+ else
+ fprintf(fp, " family %d ", ifa->ifa_family);
+
+ if (rta_tb[IFA_LOCAL]) {
+ fprintf(fp, "%s", rt_addr_n2a(ifa->ifa_family,
+ RTA_PAYLOAD(rta_tb[IFA_LOCAL]),
+ RTA_DATA(rta_tb[IFA_LOCAL]),
+ abuf, sizeof(abuf)));
+
+ if (rta_tb[IFA_ADDRESS] == NULL ||
+ memcmp(RTA_DATA(rta_tb[IFA_ADDRESS]), RTA_DATA(rta_tb[IFA_LOCAL]), 4) == 0) {
+ fprintf(fp, "/%d ", ifa->ifa_prefixlen);
+ } else {
+ fprintf(fp, " peer %s/%d ",
+ rt_addr_n2a(ifa->ifa_family,
+ RTA_PAYLOAD(rta_tb[IFA_ADDRESS]),
+ RTA_DATA(rta_tb[IFA_ADDRESS]),
+ abuf, sizeof(abuf)),
+ ifa->ifa_prefixlen);
+ }
+ }
+
+ if (rta_tb[IFA_BROADCAST]) {
+ fprintf(fp, "brd %s ",
+ rt_addr_n2a(ifa->ifa_family,
+ RTA_PAYLOAD(rta_tb[IFA_BROADCAST]),
+ RTA_DATA(rta_tb[IFA_BROADCAST]),
+ abuf, sizeof(abuf)));
+ }
+ if (rta_tb[IFA_ANYCAST]) {
+ fprintf(fp, "any %s ",
+ rt_addr_n2a(ifa->ifa_family,
+ RTA_PAYLOAD(rta_tb[IFA_ANYCAST]),
+ RTA_DATA(rta_tb[IFA_ANYCAST]),
+ abuf, sizeof(abuf)));
+ }
+ fprintf(fp, "scope %s ", rtnl_rtscope_n2a(ifa->ifa_scope, b1, sizeof(b1)));
+ if (ifa->ifa_flags&IFA_F_SECONDARY) {
+ ifa->ifa_flags &= ~IFA_F_SECONDARY;
+ fprintf(fp, "secondary ");
+ }
+ if (ifa->ifa_flags&IFA_F_TENTATIVE) {
+ ifa->ifa_flags &= ~IFA_F_TENTATIVE;
+ fprintf(fp, "tentative ");
+ }
+ if (ifa->ifa_flags&IFA_F_DEPRECATED) {
+ ifa->ifa_flags &= ~IFA_F_DEPRECATED;
+ fprintf(fp, "deprecated ");
+ }
+ if (!(ifa->ifa_flags&IFA_F_PERMANENT)) {
+ fprintf(fp, "dynamic ");
+ } else
+ ifa->ifa_flags &= ~IFA_F_PERMANENT;
+ if (ifa->ifa_flags)
+ fprintf(fp, "flags %02x ", ifa->ifa_flags);
+ if (rta_tb[IFA_LABEL])
+ fprintf(fp, "%s", (char*)RTA_DATA(rta_tb[IFA_LABEL]));
+ if (rta_tb[IFA_CACHEINFO]) {
+ struct ifa_cacheinfo *ci = RTA_DATA(rta_tb[IFA_CACHEINFO]);
+ char buf[128];
+ fprintf(fp, "%s", _SL_);
+ if (ci->ifa_valid == 0xFFFFFFFFU)
+ sprintf(buf, "valid_lft forever");
+ else
+ sprintf(buf, "valid_lft %dsec", ci->ifa_valid);
+ if (ci->ifa_prefered == 0xFFFFFFFFU)
+ sprintf(buf+strlen(buf), " preferred_lft forever");
+ else
+ sprintf(buf+strlen(buf), " preferred_lft %dsec", ci->ifa_prefered);
+ fprintf(fp, " %s", buf);
+ }
+ fprintf(fp, "\n");
+ fflush(fp);
+ return 0;
+}
+
+
+struct nlmsg_list
+{
+ struct nlmsg_list *next;
+ struct nlmsghdr h;
+};
+
+static int print_selected_addrinfo(int ifindex, struct nlmsg_list *ainfo, FILE *fp)
+{
+ for ( ;ainfo ; ainfo = ainfo->next) {
+ struct nlmsghdr *n = &ainfo->h;
+ struct ifaddrmsg *ifa = NLMSG_DATA(n);
+
+ if (n->nlmsg_type != RTM_NEWADDR)
+ continue;
+
+ if (n->nlmsg_len < NLMSG_LENGTH(sizeof(ifa)))
+ return -1;
+
+ if (ifa->ifa_index != ifindex ||
+ (filter.family && filter.family != ifa->ifa_family))
+ continue;
+
+ print_addrinfo(NULL, n, fp);
+ }
+ return 0;
+}
+
+
+static int store_nlmsg(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+{
+ struct nlmsg_list **linfo = (struct nlmsg_list**)arg;
+ struct nlmsg_list *h;
+ struct nlmsg_list **lp;
+
+ h = malloc(n->nlmsg_len+sizeof(void*));
+ if (h == NULL)
+ return -1;
+
+ memcpy(&h->h, n, n->nlmsg_len);
+ h->next = NULL;
+
+ for (lp = linfo; *lp; lp = &(*lp)->next) /* NOTHING */;
+ *lp = h;
+
+ ll_remember_index(who, n, NULL);
+ return 0;
+}
+
+static void ipaddr_reset_filter(int _oneline)
+{
+ memset(&filter, 0, sizeof(filter));
+ filter.oneline = _oneline;
+}
+
+int ipaddr_list_or_flush(int argc, char **argv, int flush)
+{
+ static const char *const option[] = { "to", "scope", "up", "label", "dev", 0 };
+
+ struct nlmsg_list *linfo = NULL;
+ struct nlmsg_list *ainfo = NULL;
+ struct nlmsg_list *l;
+ struct rtnl_handle rth;
+ char *filter_dev = NULL;
+ int no_link = 0;
+
+ ipaddr_reset_filter(oneline);
+ filter.showqueue = 1;
+
+ if (filter.family == AF_UNSPEC)
+ filter.family = preferred_family;
+
+ if (flush) {
+ if (argc <= 0) {
+ bb_error_msg(bb_msg_requires_arg, "flush");
+ return -1;
+ }
+ if (filter.family == AF_PACKET) {
+ bb_error_msg("cannot flush link addresses");
+ return -1;
+ }
+ }
+
+ while (argc > 0) {
+ const int option_num = index_in_str_array(option, *argv);
+ switch (option_num) {
+ case 0: /* to */
+ NEXT_ARG();
+ get_prefix(&filter.pfx, *argv, filter.family);
+ if (filter.family == AF_UNSPEC) {
+ filter.family = filter.pfx.family;
+ }
+ break;
+ case 1: /* scope */
+ {
+ uint32_t scope = 0;
+ NEXT_ARG();
+ filter.scopemask = -1;
+ if (rtnl_rtscope_a2n(&scope, *argv)) {
+ if (strcmp(*argv, "all") != 0) {
+ invarg(*argv, "scope");
+ }
+ scope = RT_SCOPE_NOWHERE;
+ filter.scopemask = 0;
+ }
+ filter.scope = scope;
+ break;
+ }
+ case 2: /* up */
+ filter.up = 1;
+ break;
+ case 3: /* label */
+ NEXT_ARG();
+ filter.label = *argv;
+ break;
+ case 4: /* dev */
+ NEXT_ARG();
+ default:
+ if (filter_dev) {
+ duparg2("dev", *argv);
+ }
+ filter_dev = *argv;
+ }
+ argv++;
+ argc--;
+ }
+
+ if (rtnl_open(&rth, 0) < 0)
+ exit(1);
+
+ if (rtnl_wilddump_request(&rth, preferred_family, RTM_GETLINK) < 0) {
+ bb_perror_msg_and_die("cannot send dump request");
+ }
+
+ if (rtnl_dump_filter(&rth, store_nlmsg, &linfo, NULL, NULL) < 0) {
+ bb_error_msg_and_die("dump terminated");
+ }
+
+ if (filter_dev) {
+ filter.ifindex = ll_name_to_index(filter_dev);
+ if (filter.ifindex <= 0) {
+ bb_error_msg("device \"%s\" does not exist", filter_dev);
+ return -1;
+ }
+ }
+
+ if (flush) {
+ char flushb[4096-512];
+
+ filter.flushb = flushb;
+ filter.flushp = 0;
+ filter.flushe = sizeof(flushb);
+ filter.rth = &rth;
+
+ for (;;) {
+ if (rtnl_wilddump_request(&rth, filter.family, RTM_GETADDR) < 0) {
+ perror("Cannot send dump request");
+ exit(1);
+ }
+ filter.flushed = 0;
+ if (rtnl_dump_filter(&rth, print_addrinfo, stdout, NULL, NULL) < 0) {
+ fprintf(stderr, "Flush terminated\n");
+ exit(1);
+ }
+ if (filter.flushed == 0) {
+ fflush(stdout);
+ return 0;
+ }
+ if (flush_update() < 0)
+ exit(1);
+ }
+ }
+
+ if (filter.family != AF_PACKET) {
+ if (rtnl_wilddump_request(&rth, filter.family, RTM_GETADDR) < 0) {
+ bb_perror_msg_and_die("cannot send dump request");
+ }
+
+ if (rtnl_dump_filter(&rth, store_nlmsg, &ainfo, NULL, NULL) < 0) {
+ bb_error_msg_and_die("dump terminated");
+ }
+ }
+
+
+ if (filter.family && filter.family != AF_PACKET) {
+ struct nlmsg_list **lp;
+ lp=&linfo;
+
+ if (filter.oneline)
+ no_link = 1;
+
+ while ((l=*lp)!=NULL) {
+ int ok = 0;
+ struct ifinfomsg *ifi = NLMSG_DATA(&l->h);
+ struct nlmsg_list *a;
+
+ for (a=ainfo; a; a=a->next) {
+ struct nlmsghdr *n = &a->h;
+ struct ifaddrmsg *ifa = NLMSG_DATA(n);
+
+ if (ifa->ifa_index != ifi->ifi_index ||
+ (filter.family && filter.family != ifa->ifa_family))
+ continue;
+ if ((filter.scope^ifa->ifa_scope)&filter.scopemask)
+ continue;
+ if ((filter.flags^ifa->ifa_flags)&filter.flagmask)
+ continue;
+ if (filter.pfx.family || filter.label) {
+ struct rtattr *tb[IFA_MAX+1];
+ memset(tb, 0, sizeof(tb));
+ parse_rtattr(tb, IFA_MAX, IFA_RTA(ifa), IFA_PAYLOAD(n));
+ if (!tb[IFA_LOCAL])
+ tb[IFA_LOCAL] = tb[IFA_ADDRESS];
+
+ if (filter.pfx.family && tb[IFA_LOCAL]) {
+ inet_prefix dst;
+ memset(&dst, 0, sizeof(dst));
+ dst.family = ifa->ifa_family;
+ memcpy(&dst.data, RTA_DATA(tb[IFA_LOCAL]), RTA_PAYLOAD(tb[IFA_LOCAL]));
+ if (inet_addr_match(&dst, &filter.pfx, filter.pfx.bitlen))
+ continue;
+ }
+ if (filter.label) {
+ SPRINT_BUF(b1);
+ const char *label;
+ if (tb[IFA_LABEL])
+ label = RTA_DATA(tb[IFA_LABEL]);
+ else
+ label = ll_idx_n2a(ifa->ifa_index, b1);
+ if (fnmatch(filter.label, label, 0) != 0)
+ continue;
+ }
+ }
+
+ ok = 1;
+ break;
+ }
+ if (!ok)
+ *lp = l->next;
+ else
+ lp = &l->next;
+ }
+ }
+
+ for (l=linfo; l; l = l->next) {
+ if (no_link || print_linkinfo(NULL, &l->h, stdout) == 0) {
+ struct ifinfomsg *ifi = NLMSG_DATA(&l->h);
+ if (filter.family != AF_PACKET)
+ print_selected_addrinfo(ifi->ifi_index, ainfo, stdout);
+ }
+ fflush(stdout);
+ }
+
+ exit(0);
+}
+
+static int default_scope(inet_prefix *lcl)
+{
+ if (lcl->family == AF_INET) {
+ if (lcl->bytelen >= 1 && *(uint8_t*)&lcl->data == 127)
+ return RT_SCOPE_HOST;
+ }
+ return 0;
+}
+
+static int ipaddr_modify(int cmd, int argc, char **argv)
+{
+ static const char *const option[] = {
+ "peer", "remote", "broadcast", "brd",
+ "anycast", "scope", "dev", "label", "local", 0
+ };
+
+ struct rtnl_handle rth;
+ struct {
+ struct nlmsghdr n;
+ struct ifaddrmsg ifa;
+ char buf[256];
+ } req;
+ char *d = NULL;
+ char *l = NULL;
+ inet_prefix lcl;
+ inet_prefix peer;
+ int local_len = 0;
+ int peer_len = 0;
+ int brd_len = 0;
+ int any_len = 0;
+ int scoped = 0;
+
+ memset(&req, 0, sizeof(req));
+
+ req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
+ req.n.nlmsg_flags = NLM_F_REQUEST;
+ req.n.nlmsg_type = cmd;
+ req.ifa.ifa_family = preferred_family;
+
+ while (argc > 0) {
+ const int option_num = index_in_str_array(option, *argv);
+ switch (option_num) {
+ case 0: /* peer */
+ case 1: /* remote */
+ NEXT_ARG();
+
+ if (peer_len) {
+ duparg("peer", *argv);
+ }
+ get_prefix(&peer, *argv, req.ifa.ifa_family);
+ peer_len = peer.bytelen;
+ if (req.ifa.ifa_family == AF_UNSPEC) {
+ req.ifa.ifa_family = peer.family;
+ }
+ addattr_l(&req.n, sizeof(req), IFA_ADDRESS, &peer.data, peer.bytelen);
+ req.ifa.ifa_prefixlen = peer.bitlen;
+ break;
+ case 2: /* broadcast */
+ case 3: /* brd */
+ {
+ inet_prefix addr;
+ NEXT_ARG();
+ if (brd_len) {
+ duparg("broadcast", *argv);
+ }
+ if (LONE_CHAR(*argv, '+')) {
+ brd_len = -1;
+ }
+ else if (LONE_DASH(*argv)) {
+ brd_len = -2;
+ } else {
+ get_addr(&addr, *argv, req.ifa.ifa_family);
+ if (req.ifa.ifa_family == AF_UNSPEC)
+ req.ifa.ifa_family = addr.family;
+ addattr_l(&req.n, sizeof(req), IFA_BROADCAST, &addr.data, addr.bytelen);
+ brd_len = addr.bytelen;
+ }
+ break;
+ }
+ case 4: /* anycast */
+ {
+ inet_prefix addr;
+ NEXT_ARG();
+ if (any_len) {
+ duparg("anycast", *argv);
+ }
+ get_addr(&addr, *argv, req.ifa.ifa_family);
+ if (req.ifa.ifa_family == AF_UNSPEC) {
+ req.ifa.ifa_family = addr.family;
+ }
+ addattr_l(&req.n, sizeof(req), IFA_ANYCAST, &addr.data, addr.bytelen);
+ any_len = addr.bytelen;
+ break;
+ }
+ case 5: /* scope */
+ {
+ uint32_t scope = 0;
+ NEXT_ARG();
+ if (rtnl_rtscope_a2n(&scope, *argv)) {
+ invarg(*argv, "scope");
+ }
+ req.ifa.ifa_scope = scope;
+ scoped = 1;
+ break;
+ }
+ case 6: /* dev */
+ NEXT_ARG();
+ d = *argv;
+ break;
+ case 7: /* label */
+ NEXT_ARG();
+ l = *argv;
+ addattr_l(&req.n, sizeof(req), IFA_LABEL, l, strlen(l)+1);
+ break;
+ case 8: /* local */
+ NEXT_ARG();
+ default:
+ if (local_len) {
+ duparg2("local", *argv);
+ }
+ get_prefix(&lcl, *argv, req.ifa.ifa_family);
+ if (req.ifa.ifa_family == AF_UNSPEC) {
+ req.ifa.ifa_family = lcl.family;
+ }
+ addattr_l(&req.n, sizeof(req), IFA_LOCAL, &lcl.data, lcl.bytelen);
+ local_len = lcl.bytelen;
+ }
+ argc--;
+ argv++;
+ }
+
+ if (d == NULL) {
+ bb_error_msg(bb_msg_requires_arg,"\"dev\"");
+ return -1;
+ }
+ if (l && matches(d, l) != 0) {
+ bb_error_msg_and_die("\"dev\" (%s) must match \"label\" (%s)", d, l);
+ }
+
+ if (peer_len == 0 && local_len && cmd != RTM_DELADDR) {
+ peer = lcl;
+ addattr_l(&req.n, sizeof(req), IFA_ADDRESS, &lcl.data, lcl.bytelen);
+ }
+ if (req.ifa.ifa_prefixlen == 0)
+ req.ifa.ifa_prefixlen = lcl.bitlen;
+
+ if (brd_len < 0 && cmd != RTM_DELADDR) {
+ inet_prefix brd;
+ int i;
+ if (req.ifa.ifa_family != AF_INET) {
+ bb_error_msg("broadcast can be set only for IPv4 addresses");
+ return -1;
+ }
+ brd = peer;
+ if (brd.bitlen <= 30) {
+ for (i=31; i>=brd.bitlen; i--) {
+ if (brd_len == -1)
+ brd.data[0] |= htonl(1<<(31-i));
+ else
+ brd.data[0] &= ~htonl(1<<(31-i));
+ }
+ addattr_l(&req.n, sizeof(req), IFA_BROADCAST, &brd.data, brd.bytelen);
+ brd_len = brd.bytelen;
+ }
+ }
+ if (!scoped && cmd != RTM_DELADDR)
+ req.ifa.ifa_scope = default_scope(&lcl);
+
+ if (rtnl_open(&rth, 0) < 0)
+ exit(1);
+
+ ll_init_map(&rth);
+
+ if ((req.ifa.ifa_index = ll_name_to_index(d)) == 0) {
+ bb_error_msg("cannot find device \"%s\"", d);
+ return -1;
+ }
+
+ if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0)
+ exit(2);
+
+ exit(0);
+}
+
+int do_ipaddr(int argc, char **argv)
+{
+ static const char *const commands[] = {
+ "add", "delete", "list", "show", "lst", "flush", 0
+ };
+
+ int command_num = 2;
+
+ if (*argv) {
+ command_num = index_in_substr_array(commands, *argv);
+ }
+ switch (command_num) {
+ case 0: /* add */
+ return ipaddr_modify(RTM_NEWADDR, argc-1, argv+1);
+ case 1: /* delete */
+ return ipaddr_modify(RTM_DELADDR, argc-1, argv+1);
+ case 2: /* list */
+ case 3: /* show */
+ case 4: /* lst */
+ return ipaddr_list_or_flush(argc-1, argv+1, 0);
+ case 5: /* flush */
+ return ipaddr_list_or_flush(argc-1, argv+1, 1);
+ }
+ bb_error_msg_and_die("unknown command %s", *argv);
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/libiproute/iplink.c b/i/pc104/initrd/conf/busybox/networking/libiproute/iplink.c
new file mode 100644
index 0000000..cb6ee96
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/libiproute/iplink.c
@@ -0,0 +1,350 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * iplink.c "ip link".
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "libbb.h"
+
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/if_packet.h>
+#include <netpacket/packet.h>
+
+#include <net/ethernet.h>
+
+#include "rt_names.h"
+#include "utils.h"
+#include "ip_common.h"
+
+/* take from linux/sockios.h */
+#define SIOCSIFNAME 0x8923 /* set interface name */
+
+static int on_off(const char *msg)
+{
+ bb_error_msg("error: argument of \"%s\" must be \"on\" or \"off\"", msg);
+ return -1;
+}
+
+static int get_ctl_fd(void)
+{
+ int s_errno;
+ int fd;
+
+ fd = socket(PF_INET, SOCK_DGRAM, 0);
+ if (fd >= 0)
+ return fd;
+ s_errno = errno;
+ fd = socket(PF_PACKET, SOCK_DGRAM, 0);
+ if (fd >= 0)
+ return fd;
+ fd = socket(PF_INET6, SOCK_DGRAM, 0);
+ if (fd >= 0)
+ return fd;
+ errno = s_errno;
+ bb_perror_msg("cannot create control socket");
+ return -1;
+}
+
+static int do_chflags(char *dev, uint32_t flags, uint32_t mask)
+{
+ struct ifreq ifr;
+ int fd;
+ int err;
+
+ strncpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name));
+ fd = get_ctl_fd();
+ if (fd < 0)
+ return -1;
+ err = ioctl(fd, SIOCGIFFLAGS, &ifr);
+ if (err) {
+ bb_perror_msg("SIOCGIFFLAGS");
+ close(fd);
+ return -1;
+ }
+ if ((ifr.ifr_flags^flags)&mask) {
+ ifr.ifr_flags &= ~mask;
+ ifr.ifr_flags |= mask&flags;
+ err = ioctl(fd, SIOCSIFFLAGS, &ifr);
+ if (err)
+ bb_perror_msg("SIOCSIFFLAGS");
+ }
+ close(fd);
+ return err;
+}
+
+static int do_changename(char *dev, char *newdev)
+{
+ struct ifreq ifr;
+ int fd;
+ int err;
+
+ strncpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name));
+ strncpy(ifr.ifr_newname, newdev, sizeof(ifr.ifr_newname));
+ fd = get_ctl_fd();
+ if (fd < 0)
+ return -1;
+ err = ioctl(fd, SIOCSIFNAME, &ifr);
+ if (err) {
+ bb_perror_msg("SIOCSIFNAME");
+ close(fd);
+ return -1;
+ }
+ close(fd);
+ return err;
+}
+
+static int set_qlen(char *dev, int qlen)
+{
+ struct ifreq ifr;
+ int s;
+
+ s = get_ctl_fd();
+ if (s < 0)
+ return -1;
+
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name));
+ ifr.ifr_qlen = qlen;
+ if (ioctl(s, SIOCSIFTXQLEN, &ifr) < 0) {
+ bb_perror_msg("SIOCSIFXQLEN");
+ close(s);
+ return -1;
+ }
+ close(s);
+
+ return 0;
+}
+
+static int set_mtu(char *dev, int mtu)
+{
+ struct ifreq ifr;
+ int s;
+
+ s = get_ctl_fd();
+ if (s < 0)
+ return -1;
+
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name));
+ ifr.ifr_mtu = mtu;
+ if (ioctl(s, SIOCSIFMTU, &ifr) < 0) {
+ bb_perror_msg("SIOCSIFMTU");
+ close(s);
+ return -1;
+ }
+ close(s);
+
+ return 0;
+}
+
+static int get_address(char *dev, int *htype)
+{
+ struct ifreq ifr;
+ struct sockaddr_ll me;
+ socklen_t alen;
+ int s;
+
+ s = socket(PF_PACKET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ bb_perror_msg("socket(PF_PACKET)");
+ return -1;
+ }
+
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name));
+ if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) {
+ bb_perror_msg("SIOCGIFINDEX");
+ close(s);
+ return -1;
+ }
+
+ memset(&me, 0, sizeof(me));
+ me.sll_family = AF_PACKET;
+ me.sll_ifindex = ifr.ifr_ifindex;
+ me.sll_protocol = htons(ETH_P_LOOP);
+ if (bind(s, (struct sockaddr*)&me, sizeof(me)) == -1) {
+ bb_perror_msg("bind");
+ close(s);
+ return -1;
+ }
+
+ alen = sizeof(me);
+ if (getsockname(s, (struct sockaddr*)&me, &alen) == -1) {
+ bb_perror_msg("getsockname");
+ close(s);
+ return -1;
+ }
+ close(s);
+ *htype = me.sll_hatype;
+ return me.sll_halen;
+}
+
+static int parse_address(char *dev, int hatype, int halen, char *lla, struct ifreq *ifr)
+{
+ int alen;
+
+ memset(ifr, 0, sizeof(*ifr));
+ strncpy(ifr->ifr_name, dev, sizeof(ifr->ifr_name));
+ ifr->ifr_hwaddr.sa_family = hatype;
+ alen = ll_addr_a2n((unsigned char *)(ifr->ifr_hwaddr.sa_data), 14, lla);
+ if (alen < 0)
+ return -1;
+ if (alen != halen) {
+ bb_error_msg("wrong address (%s) length: expected %d bytes", lla, halen);
+ return -1;
+ }
+ return 0;
+}
+
+static int set_address(struct ifreq *ifr, int brd)
+{
+ int s;
+
+ s = get_ctl_fd();
+ if (s < 0)
+ return -1;
+ if (ioctl(s, brd?SIOCSIFHWBROADCAST:SIOCSIFHWADDR, ifr) < 0) {
+ bb_perror_msg(brd ? "SIOCSIFHWBROADCAST" : "SIOCSIFHWADDR");
+ close(s);
+ return -1;
+ }
+ close(s);
+ return 0;
+}
+
+
+static int do_set(int argc, char **argv)
+{
+ char *dev = NULL;
+ uint32_t mask = 0;
+ uint32_t flags = 0;
+ int qlen = -1;
+ int mtu = -1;
+ char *newaddr = NULL;
+ char *newbrd = NULL;
+ struct ifreq ifr0, ifr1;
+ char *newname = NULL;
+ int htype, halen;
+
+ while (argc > 0) {
+ if (strcmp(*argv, "up") == 0) {
+ mask |= IFF_UP;
+ flags |= IFF_UP;
+ } else if (strcmp(*argv, "down") == 0) {
+ mask |= IFF_UP;
+ flags &= ~IFF_UP;
+ } else if (strcmp(*argv, "name") == 0) {
+ NEXT_ARG();
+ newname = *argv;
+ } else if (strcmp(*argv, "mtu") == 0) {
+ NEXT_ARG();
+ if (mtu != -1)
+ duparg("mtu", *argv);
+ if (get_integer(&mtu, *argv, 0))
+ invarg(*argv, "mtu");
+ } else if (strcmp(*argv, "multicast") == 0) {
+ NEXT_ARG();
+ mask |= IFF_MULTICAST;
+ if (strcmp(*argv, "on") == 0) {
+ flags |= IFF_MULTICAST;
+ } else if (strcmp(*argv, "off") == 0) {
+ flags &= ~IFF_MULTICAST;
+ } else
+ return on_off("multicast");
+ } else if (strcmp(*argv, "arp") == 0) {
+ NEXT_ARG();
+ mask |= IFF_NOARP;
+ if (strcmp(*argv, "on") == 0) {
+ flags &= ~IFF_NOARP;
+ } else if (strcmp(*argv, "off") == 0) {
+ flags |= IFF_NOARP;
+ } else
+ return on_off("noarp");
+ } else if (strcmp(*argv, "addr") == 0) {
+ NEXT_ARG();
+ newaddr = *argv;
+ } else {
+ if (strcmp(*argv, "dev") == 0) {
+ NEXT_ARG();
+ }
+ if (dev)
+ duparg2("dev", *argv);
+ dev = *argv;
+ }
+ argc--; argv++;
+ }
+
+ if (!dev) {
+ bb_error_msg(bb_msg_requires_arg, "\"dev\"");
+ exit(-1);
+ }
+
+ if (newaddr || newbrd) {
+ halen = get_address(dev, &htype);
+ if (halen < 0)
+ return -1;
+ if (newaddr) {
+ if (parse_address(dev, htype, halen, newaddr, &ifr0) < 0)
+ return -1;
+ }
+ if (newbrd) {
+ if (parse_address(dev, htype, halen, newbrd, &ifr1) < 0)
+ return -1;
+ }
+ }
+
+ if (newname && strcmp(dev, newname)) {
+ if (do_changename(dev, newname) < 0)
+ return -1;
+ dev = newname;
+ }
+ if (qlen != -1) {
+ if (set_qlen(dev, qlen) < 0)
+ return -1;
+ }
+ if (mtu != -1) {
+ if (set_mtu(dev, mtu) < 0)
+ return -1;
+ }
+ if (newaddr || newbrd) {
+ if (newbrd) {
+ if (set_address(&ifr1, 1) < 0)
+ return -1;
+ }
+ if (newaddr) {
+ if (set_address(&ifr0, 0) < 0)
+ return -1;
+ }
+ }
+ if (mask)
+ return do_chflags(dev, flags, mask);
+ return 0;
+}
+
+static int ipaddr_list_link(int argc, char **argv)
+{
+ preferred_family = AF_PACKET;
+ return ipaddr_list_or_flush(argc, argv, 0);
+}
+
+int do_iplink(int argc, char **argv)
+{
+ if (argc > 0) {
+ if (matches(*argv, "set") == 0)
+ return do_set(argc-1, argv+1);
+ if (matches(*argv, "show") == 0 ||
+ matches(*argv, "lst") == 0 ||
+ matches(*argv, "list") == 0)
+ return ipaddr_list_link(argc-1, argv+1);
+ } else
+ return ipaddr_list_link(0, NULL);
+
+ bb_error_msg("command \"%s\" is unknown", *argv);
+ exit(-1);
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/libiproute/iproute.c b/i/pc104/initrd/conf/busybox/networking/libiproute/iproute.c
new file mode 100644
index 0000000..27d37ac
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/libiproute/iproute.c
@@ -0,0 +1,878 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * iproute.c "ip route".
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ *
+ * Changes:
+ *
+ * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
+ * Kunihiro Ishiguro <kunihiro@zebra.org> 001102: rtnh_ifindex was not initialized
+ */
+
+#include "libbb.h"
+
+#include "rt_names.h"
+#include "utils.h"
+#include "ip_common.h"
+
+#ifndef RTAX_RTTVAR
+#define RTAX_RTTVAR RTAX_HOPS
+#endif
+
+
+static struct
+{
+ int tb;
+ int flushed;
+ char *flushb;
+ int flushp;
+ int flushe;
+ struct rtnl_handle *rth;
+ int protocol, protocolmask;
+ int scope, scopemask;
+ int type, typemask;
+ int tos, tosmask;
+ int iif, iifmask;
+ int oif, oifmask;
+ int realm, realmmask;
+ inet_prefix rprefsrc;
+ inet_prefix rvia;
+ inet_prefix rdst;
+ inet_prefix mdst;
+ inet_prefix rsrc;
+ inet_prefix msrc;
+} filter;
+
+static int flush_update(void)
+{
+ if (rtnl_send(filter.rth, filter.flushb, filter.flushp) < 0) {
+ perror("Failed to send flush request\n");
+ return -1;
+ }
+ filter.flushp = 0;
+ return 0;
+}
+
+static unsigned get_hz(void)
+{
+ static unsigned hz_internal;
+ FILE *fp;
+
+ if (hz_internal)
+ return hz_internal;
+
+ fp = fopen("/proc/net/psched", "r");
+ if (fp) {
+ unsigned nom, denom;
+
+ if (fscanf(fp, "%*08x%*08x%08x%08x", &nom, &denom) == 2)
+ if (nom == 1000000)
+ hz_internal = denom;
+ fclose(fp);
+ }
+ if (!hz_internal)
+ hz_internal = sysconf(_SC_CLK_TCK);
+ return hz_internal;
+}
+
+static int print_route(struct sockaddr_nl *who ATTRIBUTE_UNUSED,
+ struct nlmsghdr *n, void *arg)
+{
+ FILE *fp = (FILE*)arg;
+ struct rtmsg *r = NLMSG_DATA(n);
+ int len = n->nlmsg_len;
+ struct rtattr * tb[RTA_MAX+1];
+ char abuf[256];
+ inet_prefix dst;
+ inet_prefix src;
+ int host_len = -1;
+ SPRINT_BUF(b1);
+
+
+ if (n->nlmsg_type != RTM_NEWROUTE && n->nlmsg_type != RTM_DELROUTE) {
+ fprintf(stderr, "Not a route: %08x %08x %08x\n",
+ n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
+ return 0;
+ }
+ if (filter.flushb && n->nlmsg_type != RTM_NEWROUTE)
+ return 0;
+ len -= NLMSG_LENGTH(sizeof(*r));
+ if (len < 0) {
+ bb_error_msg("wrong nlmsg len %d", len);
+ return -1;
+ }
+
+ if (r->rtm_family == AF_INET6)
+ host_len = 128;
+ else if (r->rtm_family == AF_INET)
+ host_len = 32;
+
+ if (r->rtm_family == AF_INET6) {
+ if (filter.tb) {
+ if (filter.tb < 0) {
+ if (!(r->rtm_flags&RTM_F_CLONED)) {
+ return 0;
+ }
+ } else {
+ if (r->rtm_flags&RTM_F_CLONED) {
+ return 0;
+ }
+ if (filter.tb == RT_TABLE_LOCAL) {
+ if (r->rtm_type != RTN_LOCAL) {
+ return 0;
+ }
+ } else if (filter.tb == RT_TABLE_MAIN) {
+ if (r->rtm_type == RTN_LOCAL) {
+ return 0;
+ }
+ } else {
+ return 0;
+ }
+ }
+ }
+ } else {
+ if (filter.tb > 0 && filter.tb != r->rtm_table) {
+ return 0;
+ }
+ }
+ if (filter.rdst.family &&
+ (r->rtm_family != filter.rdst.family || filter.rdst.bitlen > r->rtm_dst_len)) {
+ return 0;
+ }
+ if (filter.mdst.family &&
+ (r->rtm_family != filter.mdst.family ||
+ (filter.mdst.bitlen >= 0 && filter.mdst.bitlen < r->rtm_dst_len))) {
+ return 0;
+ }
+ if (filter.rsrc.family &&
+ (r->rtm_family != filter.rsrc.family || filter.rsrc.bitlen > r->rtm_src_len)) {
+ return 0;
+ }
+ if (filter.msrc.family &&
+ (r->rtm_family != filter.msrc.family ||
+ (filter.msrc.bitlen >= 0 && filter.msrc.bitlen < r->rtm_src_len))) {
+ return 0;
+ }
+
+ memset(tb, 0, sizeof(tb));
+ parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
+
+ if (filter.rdst.family && inet_addr_match(&dst, &filter.rdst, filter.rdst.bitlen))
+ return 0;
+ if (filter.mdst.family && filter.mdst.bitlen >= 0 &&
+ inet_addr_match(&dst, &filter.mdst, r->rtm_dst_len))
+ return 0;
+
+ if (filter.rsrc.family && inet_addr_match(&src, &filter.rsrc, filter.rsrc.bitlen))
+ return 0;
+ if (filter.msrc.family && filter.msrc.bitlen >= 0 &&
+ inet_addr_match(&src, &filter.msrc, r->rtm_src_len))
+ return 0;
+
+ if (filter.flushb &&
+ r->rtm_family == AF_INET6 &&
+ r->rtm_dst_len == 0 &&
+ r->rtm_type == RTN_UNREACHABLE &&
+ tb[RTA_PRIORITY] &&
+ *(int*)RTA_DATA(tb[RTA_PRIORITY]) == -1)
+ return 0;
+
+ if (filter.flushb) {
+ struct nlmsghdr *fn;
+ if (NLMSG_ALIGN(filter.flushp) + n->nlmsg_len > filter.flushe) {
+ if (flush_update())
+ return -1;
+ }
+ fn = (struct nlmsghdr*)(filter.flushb + NLMSG_ALIGN(filter.flushp));
+ memcpy(fn, n, n->nlmsg_len);
+ fn->nlmsg_type = RTM_DELROUTE;
+ fn->nlmsg_flags = NLM_F_REQUEST;
+ fn->nlmsg_seq = ++filter.rth->seq;
+ filter.flushp = (((char*)fn) + n->nlmsg_len) - filter.flushb;
+ filter.flushed++;
+ return 0;
+ }
+
+ if (n->nlmsg_type == RTM_DELROUTE) {
+ fprintf(fp, "Deleted ");
+ }
+ if (r->rtm_type != RTN_UNICAST && !filter.type) {
+ fprintf(fp, "%s ", rtnl_rtntype_n2a(r->rtm_type, b1, sizeof(b1)));
+ }
+
+ if (tb[RTA_DST]) {
+ if (r->rtm_dst_len != host_len) {
+ fprintf(fp, "%s/%u ", rt_addr_n2a(r->rtm_family,
+ RTA_PAYLOAD(tb[RTA_DST]),
+ RTA_DATA(tb[RTA_DST]),
+ abuf, sizeof(abuf)),
+ r->rtm_dst_len
+ );
+ } else {
+ fprintf(fp, "%s ", format_host(r->rtm_family,
+ RTA_PAYLOAD(tb[RTA_DST]),
+ RTA_DATA(tb[RTA_DST]),
+ abuf, sizeof(abuf))
+ );
+ }
+ } else if (r->rtm_dst_len) {
+ fprintf(fp, "0/%d ", r->rtm_dst_len);
+ } else {
+ fprintf(fp, "default ");
+ }
+ if (tb[RTA_SRC]) {
+ if (r->rtm_src_len != host_len) {
+ fprintf(fp, "from %s/%u ", rt_addr_n2a(r->rtm_family,
+ RTA_PAYLOAD(tb[RTA_SRC]),
+ RTA_DATA(tb[RTA_SRC]),
+ abuf, sizeof(abuf)),
+ r->rtm_src_len
+ );
+ } else {
+ fprintf(fp, "from %s ", format_host(r->rtm_family,
+ RTA_PAYLOAD(tb[RTA_SRC]),
+ RTA_DATA(tb[RTA_SRC]),
+ abuf, sizeof(abuf))
+ );
+ }
+ } else if (r->rtm_src_len) {
+ fprintf(fp, "from 0/%u ", r->rtm_src_len);
+ }
+ if (tb[RTA_GATEWAY] && filter.rvia.bitlen != host_len) {
+ fprintf(fp, "via %s ",
+ format_host(r->rtm_family,
+ RTA_PAYLOAD(tb[RTA_GATEWAY]),
+ RTA_DATA(tb[RTA_GATEWAY]),
+ abuf, sizeof(abuf)));
+ }
+ if (tb[RTA_OIF] && filter.oifmask != -1) {
+ fprintf(fp, "dev %s ", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_OIF])));
+ }
+
+ if (tb[RTA_PREFSRC] && filter.rprefsrc.bitlen != host_len) {
+ /* Do not use format_host(). It is our local addr
+ and symbolic name will not be useful.
+ */
+ fprintf(fp, " src %s ",
+ rt_addr_n2a(r->rtm_family,
+ RTA_PAYLOAD(tb[RTA_PREFSRC]),
+ RTA_DATA(tb[RTA_PREFSRC]),
+ abuf, sizeof(abuf)));
+ }
+ if (tb[RTA_PRIORITY]) {
+ fprintf(fp, " metric %d ", *(uint32_t*)RTA_DATA(tb[RTA_PRIORITY]));
+ }
+ if (r->rtm_family == AF_INET6) {
+ struct rta_cacheinfo *ci = NULL;
+ if (tb[RTA_CACHEINFO]) {
+ ci = RTA_DATA(tb[RTA_CACHEINFO]);
+ }
+ if ((r->rtm_flags & RTM_F_CLONED) || (ci && ci->rta_expires)) {
+ if (r->rtm_flags & RTM_F_CLONED) {
+ fprintf(fp, "%s cache ", _SL_);
+ }
+ if (ci->rta_expires) {
+ fprintf(fp, " expires %dsec", ci->rta_expires / get_hz());
+ }
+ if (ci->rta_error != 0) {
+ fprintf(fp, " error %d", ci->rta_error);
+ }
+ } else if (ci) {
+ if (ci->rta_error != 0)
+ fprintf(fp, " error %d", ci->rta_error);
+ }
+ }
+ if (tb[RTA_IIF] && filter.iifmask != -1) {
+ fprintf(fp, " iif %s", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_IIF])));
+ }
+ fprintf(fp, "\n");
+ fflush(fp);
+ return 0;
+}
+
+static int iproute_modify(int cmd, unsigned flags, int argc, char **argv)
+{
+ struct rtnl_handle rth;
+ struct {
+ struct nlmsghdr n;
+ struct rtmsg r;
+ char buf[1024];
+ } req;
+ char mxbuf[256];
+ struct rtattr * mxrta = (void*)mxbuf;
+ unsigned mxlock = 0;
+ char *d = NULL;
+ int gw_ok = 0;
+ int dst_ok = 0;
+ int proto_ok = 0;
+ int type_ok = 0;
+
+ memset(&req, 0, sizeof(req));
+
+ req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
+ req.n.nlmsg_flags = NLM_F_REQUEST|flags;
+ req.n.nlmsg_type = cmd;
+ req.r.rtm_family = preferred_family;
+ req.r.rtm_table = RT_TABLE_MAIN;
+ req.r.rtm_scope = RT_SCOPE_NOWHERE;
+
+ if (cmd != RTM_DELROUTE) {
+ req.r.rtm_protocol = RTPROT_BOOT;
+ req.r.rtm_scope = RT_SCOPE_UNIVERSE;
+ req.r.rtm_type = RTN_UNICAST;
+ }
+
+ mxrta->rta_type = RTA_METRICS;
+ mxrta->rta_len = RTA_LENGTH(0);
+
+ while (argc > 0) {
+ if (strcmp(*argv, "src") == 0) {
+ inet_prefix addr;
+ NEXT_ARG();
+ get_addr(&addr, *argv, req.r.rtm_family);
+ if (req.r.rtm_family == AF_UNSPEC) {
+ req.r.rtm_family = addr.family;
+ }
+ addattr_l(&req.n, sizeof(req), RTA_PREFSRC, &addr.data, addr.bytelen);
+ } else if (strcmp(*argv, "via") == 0) {
+ inet_prefix addr;
+ gw_ok = 1;
+ NEXT_ARG();
+ get_addr(&addr, *argv, req.r.rtm_family);
+ if (req.r.rtm_family == AF_UNSPEC) {
+ req.r.rtm_family = addr.family;
+ }
+ addattr_l(&req.n, sizeof(req), RTA_GATEWAY, &addr.data, addr.bytelen);
+ } else if (strcmp(*argv, "mtu") == 0) {
+ unsigned mtu;
+ NEXT_ARG();
+ if (strcmp(*argv, "lock") == 0) {
+ mxlock |= (1<<RTAX_MTU);
+ NEXT_ARG();
+ }
+ if (get_unsigned(&mtu, *argv, 0)) {
+ invarg(*argv, "mtu");
+ }
+ rta_addattr32(mxrta, sizeof(mxbuf), RTAX_MTU, mtu);
+ } else if (matches(*argv, "protocol") == 0) {
+ uint32_t prot;
+ NEXT_ARG();
+ if (rtnl_rtprot_a2n(&prot, *argv))
+ invarg(*argv, "protocol");
+ req.r.rtm_protocol = prot;
+ proto_ok =1;
+#if ENABLE_FEATURE_IP_RULE
+ } else if (matches(*argv, "table") == 0) {
+ uint32_t tid;
+ NEXT_ARG();
+ if (rtnl_rttable_a2n(&tid, *argv))
+ invarg(*argv, "table");
+ req.r.rtm_table = tid;
+#endif
+ } else if (strcmp(*argv, "dev") == 0 ||
+ strcmp(*argv, "oif") == 0) {
+ NEXT_ARG();
+ d = *argv;
+ } else {
+ int type;
+ inet_prefix dst;
+
+ if (strcmp(*argv, "to") == 0) {
+ NEXT_ARG();
+ }
+ if ((**argv < '0' || **argv > '9') &&
+ rtnl_rtntype_a2n(&type, *argv) == 0) {
+ NEXT_ARG();
+ req.r.rtm_type = type;
+ type_ok = 1;
+ }
+
+ if (dst_ok) {
+ duparg2("to", *argv);
+ }
+ get_prefix(&dst, *argv, req.r.rtm_family);
+ if (req.r.rtm_family == AF_UNSPEC) {
+ req.r.rtm_family = dst.family;
+ }
+ req.r.rtm_dst_len = dst.bitlen;
+ dst_ok = 1;
+ if (dst.bytelen) {
+ addattr_l(&req.n, sizeof(req), RTA_DST, &dst.data, dst.bytelen);
+ }
+ }
+ argc--; argv++;
+ }
+
+ if (rtnl_open(&rth, 0) < 0) {
+ exit(1);
+ }
+
+ if (d) {
+ int idx;
+
+ ll_init_map(&rth);
+
+ if (d) {
+ idx = ll_name_to_index(d);
+ if (idx == 0) {
+ bb_error_msg("cannot find device \"%s\"", d);
+ return -1;
+ }
+ addattr32(&req.n, sizeof(req), RTA_OIF, idx);
+ }
+ }
+
+ if (mxrta->rta_len > RTA_LENGTH(0)) {
+ if (mxlock) {
+ rta_addattr32(mxrta, sizeof(mxbuf), RTAX_LOCK, mxlock);
+ }
+ addattr_l(&req.n, sizeof(req), RTA_METRICS, RTA_DATA(mxrta), RTA_PAYLOAD(mxrta));
+ }
+
+ if (req.r.rtm_family == AF_UNSPEC) {
+ req.r.rtm_family = AF_INET;
+ }
+
+ if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) {
+ exit(2);
+ }
+
+ return 0;
+}
+
+static int rtnl_rtcache_request(struct rtnl_handle *rth, int family)
+{
+ struct {
+ struct nlmsghdr nlh;
+ struct rtmsg rtm;
+ } req;
+ struct sockaddr_nl nladdr;
+
+ memset(&nladdr, 0, sizeof(nladdr));
+ memset(&req, 0, sizeof(req));
+ nladdr.nl_family = AF_NETLINK;
+
+ req.nlh.nlmsg_len = sizeof(req);
+ req.nlh.nlmsg_type = RTM_GETROUTE;
+ req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_REQUEST;
+ req.nlh.nlmsg_pid = 0;
+ req.nlh.nlmsg_seq = rth->dump = ++rth->seq;
+ req.rtm.rtm_family = family;
+ req.rtm.rtm_flags |= RTM_F_CLONED;
+
+ return sendto(rth->fd, (void*)&req, sizeof(req), 0, (struct sockaddr*)&nladdr, sizeof(nladdr));
+}
+
+static int iproute_flush_cache(void)
+{
+ static const char fn[] = "/proc/sys/net/ipv4/route/flush";
+ int flush_fd = open(fn, O_WRONLY);
+ if (flush_fd < 0) {
+ bb_perror_msg("cannot open '%s'", fn);
+ return -1;
+ }
+
+ if (write(flush_fd, "-1", 2) < 2) {
+ bb_perror_msg("cannot flush routing cache");
+ return -1;
+ }
+ close(flush_fd);
+ return 0;
+}
+
+static void iproute_reset_filter(void)
+{
+ memset(&filter, 0, sizeof(filter));
+ filter.mdst.bitlen = -1;
+ filter.msrc.bitlen = -1;
+}
+
+static int iproute_list_or_flush(int argc, char **argv, int flush)
+{
+ int do_ipv6 = preferred_family;
+ struct rtnl_handle rth;
+ char *id = NULL;
+ char *od = NULL;
+
+ iproute_reset_filter();
+ filter.tb = RT_TABLE_MAIN;
+
+ if (flush && argc <= 0) {
+ bb_error_msg(bb_msg_requires_arg, "\"ip route flush\"");
+ return -1;
+ }
+
+ while (argc > 0) {
+ if (matches(*argv, "protocol") == 0) {
+ uint32_t prot = 0;
+ NEXT_ARG();
+ filter.protocolmask = -1;
+ if (rtnl_rtprot_a2n(&prot, *argv)) {
+ if (strcmp(*argv, "all") != 0) {
+ invarg(*argv, "protocol");
+ }
+ prot = 0;
+ filter.protocolmask = 0;
+ }
+ filter.protocol = prot;
+ } else if (strcmp(*argv, "dev") == 0 ||
+ strcmp(*argv, "oif") == 0) {
+ NEXT_ARG();
+ od = *argv;
+ } else if (strcmp(*argv, "iif") == 0) {
+ NEXT_ARG();
+ id = *argv;
+ } else if (matches(*argv, "from") == 0) {
+ NEXT_ARG();
+ if (matches(*argv, "root") == 0) {
+ NEXT_ARG();
+ get_prefix(&filter.rsrc, *argv, do_ipv6);
+ } else if (matches(*argv, "match") == 0) {
+ NEXT_ARG();
+ get_prefix(&filter.msrc, *argv, do_ipv6);
+ } else {
+ if (matches(*argv, "exact") == 0) {
+ NEXT_ARG();
+ }
+ get_prefix(&filter.msrc, *argv, do_ipv6);
+ filter.rsrc = filter.msrc;
+ }
+ } else {
+ if (matches(*argv, "to") == 0) {
+ NEXT_ARG();
+ }
+ if (matches(*argv, "root") == 0) {
+ NEXT_ARG();
+ get_prefix(&filter.rdst, *argv, do_ipv6);
+ } else if (matches(*argv, "match") == 0) {
+ NEXT_ARG();
+ get_prefix(&filter.mdst, *argv, do_ipv6);
+ } else if (matches(*argv, "table") == 0) {
+ NEXT_ARG();
+ if (matches(*argv, "cache") == 0) {
+ filter.tb = -1;
+#if 0 && ENABLE_FEATURE_IP_RULE
+
+#else
+ } else if (matches(*argv, "main") != 0) {
+ invarg(*argv, "table");
+ }
+#endif
+ } else if (matches(*argv, "cache") == 0) {
+ filter.tb = -1;
+ } else {
+ if (matches(*argv, "exact") == 0) {
+ NEXT_ARG();
+ }
+ get_prefix(&filter.mdst, *argv, do_ipv6);
+ filter.rdst = filter.mdst;
+ }
+ }
+ argc--; argv++;
+ }
+
+ if (do_ipv6 == AF_UNSPEC && filter.tb) {
+ do_ipv6 = AF_INET;
+ }
+
+ if (rtnl_open(&rth, 0) < 0) {
+ exit(1);
+ }
+
+ ll_init_map(&rth);
+
+ if (id || od) {
+ int idx;
+
+ if (id) {
+ if ((idx = ll_name_to_index(id)) == 0) {
+ bb_error_msg("cannot find device \"%s\"", id);
+ return -1;
+ }
+ filter.iif = idx;
+ filter.iifmask = -1;
+ }
+ if (od) {
+ if ((idx = ll_name_to_index(od)) == 0) {
+ bb_error_msg("cannot find device \"%s\"", od);
+ }
+ filter.oif = idx;
+ filter.oifmask = -1;
+ }
+ }
+
+ if (flush) {
+ char flushb[4096-512];
+
+ if (filter.tb == -1) {
+ if (do_ipv6 != AF_INET6)
+ iproute_flush_cache();
+ if (do_ipv6 == AF_INET)
+ return 0;
+ }
+
+ filter.flushb = flushb;
+ filter.flushp = 0;
+ filter.flushe = sizeof(flushb);
+ filter.rth = &rth;
+
+ for (;;) {
+ if (rtnl_wilddump_request(&rth, do_ipv6, RTM_GETROUTE) < 0) {
+ perror("Cannot send dump request");
+ return -1;
+ }
+ filter.flushed = 0;
+ if (rtnl_dump_filter(&rth, print_route, stdout, NULL, NULL) < 0) {
+ bb_error_msg("flush terminated");
+ return -1;
+ }
+ if (filter.flushed == 0) {
+ fflush(stdout);
+ return 0;
+ }
+ if (flush_update() < 0)
+ exit(1);
+ }
+ }
+
+ if (filter.tb != -1) {
+ if (rtnl_wilddump_request(&rth, do_ipv6, RTM_GETROUTE) < 0) {
+ bb_perror_msg_and_die("cannot send dump request");
+ }
+ } else {
+ if (rtnl_rtcache_request(&rth, do_ipv6) < 0) {
+ bb_perror_msg_and_die("cannot send dump request");
+ }
+ }
+
+ if (rtnl_dump_filter(&rth, print_route, stdout, NULL, NULL) < 0) {
+ bb_error_msg_and_die("dump terminated");
+ }
+
+ exit(0);
+}
+
+
+static int iproute_get(int argc, char **argv)
+{
+ struct rtnl_handle rth;
+ struct {
+ struct nlmsghdr n;
+ struct rtmsg r;
+ char buf[1024];
+ } req;
+ char *idev = NULL;
+ char *odev = NULL;
+ int connected = 0;
+ int from_ok = 0;
+ static const char * const options[] =
+ { "from", "iif", "oif", "dev", "notify", "connected", "to", 0 };
+
+ memset(&req, 0, sizeof(req));
+
+ iproute_reset_filter();
+
+ req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
+ req.n.nlmsg_flags = NLM_F_REQUEST;
+ req.n.nlmsg_type = RTM_GETROUTE;
+ req.r.rtm_family = preferred_family;
+ req.r.rtm_table = 0;
+ req.r.rtm_protocol = 0;
+ req.r.rtm_scope = 0;
+ req.r.rtm_type = 0;
+ req.r.rtm_src_len = 0;
+ req.r.rtm_dst_len = 0;
+ req.r.rtm_tos = 0;
+
+ while (argc > 0) {
+ switch (index_in_str_array(options, *argv)) {
+ case 0: /* from */
+ {
+ inet_prefix addr;
+ NEXT_ARG();
+ from_ok = 1;
+ get_prefix(&addr, *argv, req.r.rtm_family);
+ if (req.r.rtm_family == AF_UNSPEC) {
+ req.r.rtm_family = addr.family;
+ }
+ if (addr.bytelen) {
+ addattr_l(&req.n, sizeof(req), RTA_SRC, &addr.data, addr.bytelen);
+ }
+ req.r.rtm_src_len = addr.bitlen;
+ break;
+ }
+ case 1: /* iif */
+ NEXT_ARG();
+ idev = *argv;
+ break;
+ case 2: /* oif */
+ case 3: /* dev */
+ NEXT_ARG();
+ odev = *argv;
+ break;
+ case 4: /* notify */
+ req.r.rtm_flags |= RTM_F_NOTIFY;
+ break;
+ case 5: /* connected */
+ connected = 1;
+ break;
+ case 6: /* to */
+ NEXT_ARG();
+ default:
+ {
+ inet_prefix addr;
+ get_prefix(&addr, *argv, req.r.rtm_family);
+ if (req.r.rtm_family == AF_UNSPEC) {
+ req.r.rtm_family = addr.family;
+ }
+ if (addr.bytelen) {
+ addattr_l(&req.n, sizeof(req), RTA_DST, &addr.data, addr.bytelen);
+ }
+ req.r.rtm_dst_len = addr.bitlen;
+ }
+ argc--; argv++;
+ }
+ }
+
+ if (req.r.rtm_dst_len == 0) {
+ bb_error_msg_and_die("need at least destination address");
+ }
+
+ if (rtnl_open(&rth, 0) < 0)
+ exit(1);
+
+ ll_init_map(&rth);
+
+ if (idev || odev) {
+ int idx;
+
+ if (idev) {
+ if ((idx = ll_name_to_index(idev)) == 0) {
+ bb_error_msg("cannot find device \"%s\"", idev);
+ return -1;
+ }
+ addattr32(&req.n, sizeof(req), RTA_IIF, idx);
+ }
+ if (odev) {
+ if ((idx = ll_name_to_index(odev)) == 0) {
+ bb_error_msg("cannot find device \"%s\"", odev);
+ return -1;
+ }
+ addattr32(&req.n, sizeof(req), RTA_OIF, idx);
+ }
+ }
+
+ if (req.r.rtm_family == AF_UNSPEC) {
+ req.r.rtm_family = AF_INET;
+ }
+
+ if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0) {
+ exit(2);
+ }
+
+ if (connected && !from_ok) {
+ struct rtmsg *r = NLMSG_DATA(&req.n);
+ int len = req.n.nlmsg_len;
+ struct rtattr * tb[RTA_MAX+1];
+
+ if (print_route(NULL, &req.n, (void*)stdout) < 0) {
+ bb_error_msg_and_die("an error :-)");
+ }
+
+ if (req.n.nlmsg_type != RTM_NEWROUTE) {
+ bb_error_msg("not a route?");
+ return -1;
+ }
+ len -= NLMSG_LENGTH(sizeof(*r));
+ if (len < 0) {
+ bb_error_msg("wrong len %d", len);
+ return -1;
+ }
+
+ memset(tb, 0, sizeof(tb));
+ parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
+
+ if (tb[RTA_PREFSRC]) {
+ tb[RTA_PREFSRC]->rta_type = RTA_SRC;
+ r->rtm_src_len = 8*RTA_PAYLOAD(tb[RTA_PREFSRC]);
+ } else if (!tb[RTA_SRC]) {
+ bb_error_msg("failed to connect the route");
+ return -1;
+ }
+ if (!odev && tb[RTA_OIF]) {
+ tb[RTA_OIF]->rta_type = 0;
+ }
+ if (tb[RTA_GATEWAY]) {
+ tb[RTA_GATEWAY]->rta_type = 0;
+ }
+ if (!idev && tb[RTA_IIF]) {
+ tb[RTA_IIF]->rta_type = 0;
+ }
+ req.n.nlmsg_flags = NLM_F_REQUEST;
+ req.n.nlmsg_type = RTM_GETROUTE;
+
+ if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0) {
+ exit(2);
+ }
+ }
+
+ if (print_route(NULL, &req.n, (void*)stdout) < 0) {
+ bb_error_msg_and_die("an error :-)");
+ }
+
+ exit(0);
+}
+
+int do_iproute(int argc, char **argv)
+{
+ static const char * const ip_route_commands[] = {
+ /*0-3*/ "add", "append", "change", "chg",
+ /*4-7*/ "delete", "get", "list", "show",
+ /*8..*/ "prepend", "replace", "test", "flush", 0
+ };
+ int command_num = 6;
+ unsigned int flags = 0;
+ int cmd = RTM_NEWROUTE;
+
+ /* "Standard" 'ip r a' treats 'a' as 'add', not 'append' */
+ /* It probably means that it is using "first match" rule */
+ if (*argv) {
+ command_num = index_in_substr_array(ip_route_commands, *argv);
+ }
+ switch (command_num) {
+ case 0: /* add */
+ flags = NLM_F_CREATE|NLM_F_EXCL;
+ break;
+ case 1: /* append */
+ flags = NLM_F_CREATE|NLM_F_APPEND;
+ break;
+ case 2: /* change */
+ case 3: /* chg */
+ flags = NLM_F_REPLACE;
+ break;
+ case 4: /* delete */
+ cmd = RTM_DELROUTE;
+ break;
+ case 5: /* get */
+ return iproute_get(argc-1, argv+1);
+ case 6: /* list */
+ case 7: /* show */
+ return iproute_list_or_flush(argc-1, argv+1, 0);
+ case 8: /* prepend */
+ flags = NLM_F_CREATE;
+ case 9: /* replace */
+ flags = NLM_F_CREATE|NLM_F_REPLACE;
+ case 10: /* test */
+ flags = NLM_F_EXCL;
+ case 11: /* flush */
+ return iproute_list_or_flush(argc-1, argv+1, 1);
+ default:
+ bb_error_msg_and_die("unknown command %s", *argv);
+ }
+
+ return iproute_modify(cmd, flags, argc-1, argv+1);
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/libiproute/iprule.c b/i/pc104/initrd/conf/busybox/networking/libiproute/iprule.c
new file mode 100644
index 0000000..4b31303
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/libiproute/iprule.c
@@ -0,0 +1,334 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * iprule.c "ip rule".
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ *
+ * Changes:
+ *
+ * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
+ * initially integrated into busybox by Bernhard Fischer
+ */
+
+#include "libbb.h"
+#include <syslog.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+
+#include "rt_names.h"
+#include "utils.h"
+#include "ip_common.h"
+/*
+static void usage(void) __attribute__((noreturn));
+
+static void usage(void)
+{
+ fprintf(stderr, "Usage: ip rule [ list | add | del ] SELECTOR ACTION\n");
+ fprintf(stderr, "SELECTOR := [ from PREFIX ] [ to PREFIX ] [ tos TOS ] [ fwmark FWMARK ]\n");
+ fprintf(stderr, " [ dev STRING ] [ pref NUMBER ]\n");
+ fprintf(stderr, "ACTION := [ table TABLE_ID ] [ nat ADDRESS ]\n");
+ fprintf(stderr, " [ prohibit | reject | unreachable ]\n");
+ fprintf(stderr, " [ realms [SRCREALM/]DSTREALM ]\n");
+ fprintf(stderr, "TABLE_ID := [ local | main | default | NUMBER ]\n");
+ exit(-1);
+}
+*/
+static int print_rule(struct sockaddr_nl *who ATTRIBUTE_UNUSED,
+ struct nlmsghdr *n, void *arg)
+{
+ FILE *fp = (FILE*)arg;
+ struct rtmsg *r = NLMSG_DATA(n);
+ int len = n->nlmsg_len;
+ int host_len = -1;
+ struct rtattr * tb[RTA_MAX+1];
+ char abuf[256];
+ SPRINT_BUF(b1);
+
+ if (n->nlmsg_type != RTM_NEWRULE)
+ return 0;
+
+ len -= NLMSG_LENGTH(sizeof(*r));
+ if (len < 0)
+ return -1;
+
+ memset(tb, 0, sizeof(tb));
+ parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
+
+ if (r->rtm_family == AF_INET)
+ host_len = 32;
+ else if (r->rtm_family == AF_INET6)
+ host_len = 128;
+/* else if (r->rtm_family == AF_DECnet)
+ host_len = 16;
+ else if (r->rtm_family == AF_IPX)
+ host_len = 80;
+*/
+ if (tb[RTA_PRIORITY])
+ fprintf(fp, "%u:\t", *(unsigned*)RTA_DATA(tb[RTA_PRIORITY]));
+ else
+ fprintf(fp, "0:\t");
+
+ fprintf(fp, "from ");
+ if (tb[RTA_SRC]) {
+ if (r->rtm_src_len != host_len) {
+ fprintf(fp, "%s/%u", rt_addr_n2a(r->rtm_family,
+ RTA_PAYLOAD(tb[RTA_SRC]),
+ RTA_DATA(tb[RTA_SRC]),
+ abuf, sizeof(abuf)),
+ r->rtm_src_len
+ );
+ } else {
+ fprintf(fp, "%s", format_host(r->rtm_family,
+ RTA_PAYLOAD(tb[RTA_SRC]),
+ RTA_DATA(tb[RTA_SRC]),
+ abuf, sizeof(abuf))
+ );
+ }
+ } else if (r->rtm_src_len) {
+ fprintf(fp, "0/%d", r->rtm_src_len);
+ } else {
+ fprintf(fp, "all");
+ }
+ fprintf(fp, " ");
+
+ if (tb[RTA_DST]) {
+ if (r->rtm_dst_len != host_len) {
+ fprintf(fp, "to %s/%u ", rt_addr_n2a(r->rtm_family,
+ RTA_PAYLOAD(tb[RTA_DST]),
+ RTA_DATA(tb[RTA_DST]),
+ abuf, sizeof(abuf)),
+ r->rtm_dst_len
+ );
+ } else {
+ fprintf(fp, "to %s ", format_host(r->rtm_family,
+ RTA_PAYLOAD(tb[RTA_DST]),
+ RTA_DATA(tb[RTA_DST]),
+ abuf, sizeof(abuf)));
+ }
+ } else if (r->rtm_dst_len) {
+ fprintf(fp, "to 0/%d ", r->rtm_dst_len);
+ }
+
+ if (r->rtm_tos) {
+ fprintf(fp, "tos %s ", rtnl_dsfield_n2a(r->rtm_tos, b1, sizeof(b1)));
+ }
+ if (tb[RTA_PROTOINFO]) {
+ fprintf(fp, "fwmark %#x ", *(uint32_t*)RTA_DATA(tb[RTA_PROTOINFO]));
+ }
+
+ if (tb[RTA_IIF]) {
+ fprintf(fp, "iif %s ", (char*)RTA_DATA(tb[RTA_IIF]));
+ }
+
+ if (r->rtm_table)
+ fprintf(fp, "lookup %s ", rtnl_rttable_n2a(r->rtm_table, b1, sizeof(b1)));
+
+ if (tb[RTA_FLOW]) {
+ uint32_t to = *(uint32_t*)RTA_DATA(tb[RTA_FLOW]);
+ uint32_t from = to>>16;
+ to &= 0xFFFF;
+ if (from) {
+ fprintf(fp, "realms %s/",
+ rtnl_rtrealm_n2a(from, b1, sizeof(b1)));
+ }
+ fprintf(fp, "%s ",
+ rtnl_rtrealm_n2a(to, b1, sizeof(b1)));
+ }
+
+ if (r->rtm_type == RTN_NAT) {
+ if (tb[RTA_GATEWAY]) {
+ fprintf(fp, "map-to %s ",
+ format_host(r->rtm_family,
+ RTA_PAYLOAD(tb[RTA_GATEWAY]),
+ RTA_DATA(tb[RTA_GATEWAY]),
+ abuf, sizeof(abuf)));
+ } else
+ fprintf(fp, "masquerade");
+ } else if (r->rtm_type != RTN_UNICAST)
+ fprintf(fp, "%s", rtnl_rtntype_n2a(r->rtm_type, b1, sizeof(b1)));
+
+ fprintf(fp, "\n");
+ fflush(fp);
+ return 0;
+}
+
+static int iprule_list(int argc, char **argv)
+{
+ struct rtnl_handle rth;
+ int af = preferred_family;
+
+ if (af == AF_UNSPEC)
+ af = AF_INET;
+
+ if (argc > 0) {
+ //bb_error_msg("\"rule show\" needs no arguments");
+ bb_warn_ignoring_args(argc);
+ return -1;
+ }
+
+ if (rtnl_open(&rth, 0) < 0)
+ return 1;
+
+ if (rtnl_wilddump_request(&rth, af, RTM_GETRULE) < 0) {
+ bb_perror_msg("Cannot send dump request");
+ return 1;
+ }
+
+ if (rtnl_dump_filter(&rth, print_rule, stdout, NULL, NULL) < 0) {
+ bb_error_msg("Dump terminated");
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static int iprule_modify(int cmd, int argc, char **argv)
+{
+ int table_ok = 0;
+ struct rtnl_handle rth;
+ struct {
+ struct nlmsghdr n;
+ struct rtmsg r;
+ char buf[1024];
+ } req;
+
+ memset(&req, 0, sizeof(req));
+
+ req.n.nlmsg_type = cmd;
+ req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
+ req.n.nlmsg_flags = NLM_F_REQUEST;
+ req.r.rtm_family = preferred_family;
+ req.r.rtm_protocol = RTPROT_BOOT;
+ req.r.rtm_scope = RT_SCOPE_UNIVERSE;
+ req.r.rtm_table = 0;
+ req.r.rtm_type = RTN_UNSPEC;
+
+ if (cmd == RTM_NEWRULE) {
+ req.n.nlmsg_flags |= NLM_F_CREATE|NLM_F_EXCL;
+ req.r.rtm_type = RTN_UNICAST;
+ }
+
+ while (argc > 0) {
+ if (strcmp(*argv, "from") == 0) {
+ inet_prefix dst;
+ NEXT_ARG();
+ get_prefix(&dst, *argv, req.r.rtm_family);
+ req.r.rtm_src_len = dst.bitlen;
+ addattr_l(&req.n, sizeof(req), RTA_SRC, &dst.data, dst.bytelen);
+ } else if (strcmp(*argv, "to") == 0) {
+ inet_prefix dst;
+ NEXT_ARG();
+ get_prefix(&dst, *argv, req.r.rtm_family);
+ req.r.rtm_dst_len = dst.bitlen;
+ addattr_l(&req.n, sizeof(req), RTA_DST, &dst.data, dst.bytelen);
+ } else if (matches(*argv, "preference") == 0 ||
+ matches(*argv, "order") == 0 ||
+ matches(*argv, "priority") == 0) {
+ uint32_t pref;
+ NEXT_ARG();
+ if (get_u32(&pref, *argv, 0))
+ invarg("preference value", *argv);
+ addattr32(&req.n, sizeof(req), RTA_PRIORITY, pref);
+ } else if (strcmp(*argv, "tos") == 0) {
+ uint32_t tos;
+ NEXT_ARG();
+ if (rtnl_dsfield_a2n(&tos, *argv))
+ invarg("TOS value", *argv);
+ req.r.rtm_tos = tos;
+ } else if (strcmp(*argv, "fwmark") == 0) {
+ uint32_t fwmark;
+ NEXT_ARG();
+ if (get_u32(&fwmark, *argv, 0))
+ invarg("fwmark value", *argv);
+ addattr32(&req.n, sizeof(req), RTA_PROTOINFO, fwmark);
+ } else if (matches(*argv, "realms") == 0) {
+ uint32_t realm;
+ NEXT_ARG();
+ if (get_rt_realms(&realm, *argv))
+ invarg("realms", *argv);
+ addattr32(&req.n, sizeof(req), RTA_FLOW, realm);
+ } else if (matches(*argv, "table") == 0 ||
+ strcmp(*argv, "lookup") == 0) {
+ uint32_t tid;
+ NEXT_ARG();
+ if (rtnl_rttable_a2n(&tid, *argv))
+ invarg("table ID", *argv);
+ req.r.rtm_table = tid;
+ table_ok = 1;
+ } else if (strcmp(*argv, "dev") == 0 ||
+ strcmp(*argv, "iif") == 0) {
+ NEXT_ARG();
+ addattr_l(&req.n, sizeof(req), RTA_IIF, *argv, strlen(*argv)+1);
+ } else if (strcmp(*argv, "nat") == 0 ||
+ matches(*argv, "map-to") == 0) {
+ NEXT_ARG();
+ addattr32(&req.n, sizeof(req), RTA_GATEWAY, get_addr32(*argv));
+ req.r.rtm_type = RTN_NAT;
+ } else {
+ int type;
+
+ if (strcmp(*argv, "type") == 0) {
+ NEXT_ARG();
+ }
+ if (matches(*argv, "help") == 0)
+ bb_show_usage();
+ if (rtnl_rtntype_a2n(&type, *argv))
+ invarg("Failed to parse rule type", *argv);
+ req.r.rtm_type = type;
+ }
+ argc--;
+ argv++;
+ }
+
+ if (req.r.rtm_family == AF_UNSPEC)
+ req.r.rtm_family = AF_INET;
+
+ if (!table_ok && cmd == RTM_NEWRULE)
+ req.r.rtm_table = RT_TABLE_MAIN;
+
+ if (rtnl_open(&rth, 0) < 0)
+ return 1;
+
+ if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0)
+ return 2;
+
+ return 0;
+}
+
+int do_iprule(int argc, char **argv)
+{
+ static const char * const ip_rule_commands[] =
+ {"add", "delete", "list", "show", 0};
+ int cmd = 2; /* list */
+
+ if (argc < 1)
+ return iprule_list(0, NULL);
+ if (*argv)
+ cmd = index_in_substr_array(ip_rule_commands, *argv);
+
+ switch (cmd) {
+ case 0: /* add */
+ cmd = RTM_NEWRULE;
+ break;
+ case 1: /* delete */
+ cmd = RTM_DELRULE;
+ break;
+ case 2: /* list */
+ case 3: /* show */
+ return iprule_list(argc-1, argv+1);
+ break;
+ default:
+ bb_error_msg_and_die("unknown command %s", *argv);
+ }
+ return iprule_modify(cmd, argc-1, argv+1);
+}
+
diff --git a/i/pc104/initrd/conf/busybox/networking/libiproute/iptunnel.c b/i/pc104/initrd/conf/busybox/networking/libiproute/iptunnel.c
new file mode 100644
index 0000000..e2e75fc
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/libiproute/iptunnel.c
@@ -0,0 +1,547 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * iptunnel.c "ip tunnel"
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ *
+ * Changes:
+ *
+ * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
+ * Rani Assaf <rani@magic.metawire.com> 980930: do not allow key for ipip/sit
+ * Phil Karn <karn@ka9q.ampr.org> 990408: "pmtudisc" flag
+ */
+
+#include "libbb.h"
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+
+#include <string.h>
+#include <unistd.h>
+
+#include <netinet/ip.h>
+
+#include <net/if.h>
+#include <net/if_arp.h>
+
+#include <asm/types.h>
+#ifndef __constant_htons
+#define __constant_htons htons
+#endif
+#include <linux/if_tunnel.h>
+
+#include "rt_names.h"
+#include "utils.h"
+#include "ip_common.h"
+
+
+static int do_ioctl_get_ifindex(char *dev)
+{
+ struct ifreq ifr;
+ int fd;
+
+ strncpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name));
+ fd = xsocket(AF_INET, SOCK_DGRAM, 0);
+ if (ioctl(fd, SIOCGIFINDEX, &ifr)) {
+ bb_perror_msg("ioctl");
+ return 0;
+ }
+ close(fd);
+ return ifr.ifr_ifindex;
+}
+
+static int do_ioctl_get_iftype(char *dev)
+{
+ struct ifreq ifr;
+ int fd;
+
+ strncpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name));
+ fd = xsocket(AF_INET, SOCK_DGRAM, 0);
+ if (ioctl(fd, SIOCGIFHWADDR, &ifr)) {
+ bb_perror_msg("ioctl");
+ return -1;
+ }
+ close(fd);
+ return ifr.ifr_addr.sa_family;
+}
+
+
+static char *do_ioctl_get_ifname(int idx)
+{
+ static struct ifreq ifr;
+ int fd;
+
+ ifr.ifr_ifindex = idx;
+ fd = xsocket(AF_INET, SOCK_DGRAM, 0);
+ if (ioctl(fd, SIOCGIFNAME, &ifr)) {
+ bb_perror_msg("ioctl");
+ return NULL;
+ }
+ close(fd);
+ return ifr.ifr_name;
+}
+
+
+
+static int do_get_ioctl(const char *basedev, struct ip_tunnel_parm *p)
+{
+ struct ifreq ifr;
+ int fd;
+ int err;
+
+ strncpy(ifr.ifr_name, basedev, sizeof(ifr.ifr_name));
+ ifr.ifr_ifru.ifru_data = (void*)p;
+ fd = xsocket(AF_INET, SOCK_DGRAM, 0);
+ err = ioctl(fd, SIOCGETTUNNEL, &ifr);
+ if (err) {
+ bb_perror_msg("ioctl");
+ }
+ close(fd);
+ return err;
+}
+
+static int do_add_ioctl(int cmd, const char *basedev, struct ip_tunnel_parm *p)
+{
+ struct ifreq ifr;
+ int fd;
+ int err;
+
+ if (cmd == SIOCCHGTUNNEL && p->name[0]) {
+ strncpy(ifr.ifr_name, p->name, sizeof(ifr.ifr_name));
+ } else {
+ strncpy(ifr.ifr_name, basedev, sizeof(ifr.ifr_name));
+ }
+ ifr.ifr_ifru.ifru_data = (void*)p;
+ fd = xsocket(AF_INET, SOCK_DGRAM, 0);
+ err = ioctl(fd, cmd, &ifr);
+ if (err) {
+ bb_perror_msg("ioctl");
+ }
+ close(fd);
+ return err;
+}
+
+static int do_del_ioctl(const char *basedev, struct ip_tunnel_parm *p)
+{
+ struct ifreq ifr;
+ int fd;
+ int err;
+
+ if (p->name[0]) {
+ strncpy(ifr.ifr_name, p->name, sizeof(ifr.ifr_name));
+ } else {
+ strncpy(ifr.ifr_name, basedev, sizeof(ifr.ifr_name));
+ }
+ ifr.ifr_ifru.ifru_data = (void*)p;
+ fd = xsocket(AF_INET, SOCK_DGRAM, 0);
+ err = ioctl(fd, SIOCDELTUNNEL, &ifr);
+ if (err) {
+ bb_perror_msg("ioctl");
+ }
+ close(fd);
+ return err;
+}
+
+static int parse_args(int argc, char **argv, int cmd, struct ip_tunnel_parm *p)
+{
+ int count = 0;
+ char medium[IFNAMSIZ];
+ memset(p, 0, sizeof(*p));
+ memset(&medium, 0, sizeof(medium));
+
+ p->iph.version = 4;
+ p->iph.ihl = 5;
+#ifndef IP_DF
+#define IP_DF 0x4000 /* Flag: "Don't Fragment" */
+#endif
+ p->iph.frag_off = htons(IP_DF);
+
+ while (argc > 0) {
+ if (strcmp(*argv, "mode") == 0) {
+ NEXT_ARG();
+ if (strcmp(*argv, "ipip") == 0 ||
+ strcmp(*argv, "ip/ip") == 0) {
+ if (p->iph.protocol && p->iph.protocol != IPPROTO_IPIP) {
+ bb_error_msg("you managed to ask for more than one tunnel mode");
+ exit(-1);
+ }
+ p->iph.protocol = IPPROTO_IPIP;
+ } else if (strcmp(*argv, "gre") == 0 ||
+ strcmp(*argv, "gre/ip") == 0) {
+ if (p->iph.protocol && p->iph.protocol != IPPROTO_GRE) {
+ bb_error_msg("you managed to ask for more than one tunnel mode");
+ exit(-1);
+ }
+ p->iph.protocol = IPPROTO_GRE;
+ } else if (strcmp(*argv, "sit") == 0 ||
+ strcmp(*argv, "ipv6/ip") == 0) {
+ if (p->iph.protocol && p->iph.protocol != IPPROTO_IPV6) {
+ bb_error_msg("you managed to ask for more than one tunnel mode");
+ exit(-1);
+ }
+ p->iph.protocol = IPPROTO_IPV6;
+ } else {
+ bb_error_msg("cannot guess tunnel mode");
+ exit(-1);
+ }
+ } else if (strcmp(*argv, "key") == 0) {
+ unsigned uval;
+ NEXT_ARG();
+ p->i_flags |= GRE_KEY;
+ p->o_flags |= GRE_KEY;
+ if (strchr(*argv, '.'))
+ p->i_key = p->o_key = get_addr32(*argv);
+ else {
+ if (get_unsigned(&uval, *argv, 0)<0) {
+ bb_error_msg("invalid value of \"key\"");
+ exit(-1);
+ }
+ p->i_key = p->o_key = htonl(uval);
+ }
+ } else if (strcmp(*argv, "ikey") == 0) {
+ unsigned uval;
+ NEXT_ARG();
+ p->i_flags |= GRE_KEY;
+ if (strchr(*argv, '.'))
+ p->o_key = get_addr32(*argv);
+ else {
+ if (get_unsigned(&uval, *argv, 0)<0) {
+ bb_error_msg("invalid value of \"ikey\"");
+ exit(-1);
+ }
+ p->i_key = htonl(uval);
+ }
+ } else if (strcmp(*argv, "okey") == 0) {
+ unsigned uval;
+ NEXT_ARG();
+ p->o_flags |= GRE_KEY;
+ if (strchr(*argv, '.'))
+ p->o_key = get_addr32(*argv);
+ else {
+ if (get_unsigned(&uval, *argv, 0)<0) {
+ bb_error_msg("invalid value of \"okey\"");
+ exit(-1);
+ }
+ p->o_key = htonl(uval);
+ }
+ } else if (strcmp(*argv, "seq") == 0) {
+ p->i_flags |= GRE_SEQ;
+ p->o_flags |= GRE_SEQ;
+ } else if (strcmp(*argv, "iseq") == 0) {
+ p->i_flags |= GRE_SEQ;
+ } else if (strcmp(*argv, "oseq") == 0) {
+ p->o_flags |= GRE_SEQ;
+ } else if (strcmp(*argv, "csum") == 0) {
+ p->i_flags |= GRE_CSUM;
+ p->o_flags |= GRE_CSUM;
+ } else if (strcmp(*argv, "icsum") == 0) {
+ p->i_flags |= GRE_CSUM;
+ } else if (strcmp(*argv, "ocsum") == 0) {
+ p->o_flags |= GRE_CSUM;
+ } else if (strcmp(*argv, "nopmtudisc") == 0) {
+ p->iph.frag_off = 0;
+ } else if (strcmp(*argv, "pmtudisc") == 0) {
+ p->iph.frag_off = htons(IP_DF);
+ } else if (strcmp(*argv, "remote") == 0) {
+ NEXT_ARG();
+ if (strcmp(*argv, "any"))
+ p->iph.daddr = get_addr32(*argv);
+ } else if (strcmp(*argv, "local") == 0) {
+ NEXT_ARG();
+ if (strcmp(*argv, "any"))
+ p->iph.saddr = get_addr32(*argv);
+ } else if (strcmp(*argv, "dev") == 0) {
+ NEXT_ARG();
+ strncpy(medium, *argv, IFNAMSIZ-1);
+ } else if (strcmp(*argv, "ttl") == 0) {
+ unsigned uval;
+ NEXT_ARG();
+ if (strcmp(*argv, "inherit") != 0) {
+ if (get_unsigned(&uval, *argv, 0))
+ invarg(*argv, "TTL");
+ if (uval > 255)
+ invarg(*argv, "TTL must be <=255");
+ p->iph.ttl = uval;
+ }
+ } else if (strcmp(*argv, "tos") == 0 ||
+ matches(*argv, "dsfield") == 0) {
+ uint32_t uval;
+ NEXT_ARG();
+ if (strcmp(*argv, "inherit") != 0) {
+ if (rtnl_dsfield_a2n(&uval, *argv))
+ invarg(*argv, "TOS");
+ p->iph.tos = uval;
+ } else
+ p->iph.tos = 1;
+ } else {
+ if (strcmp(*argv, "name") == 0) {
+ NEXT_ARG();
+ }
+ if (p->name[0])
+ duparg2("name", *argv);
+ strncpy(p->name, *argv, IFNAMSIZ);
+ if (cmd == SIOCCHGTUNNEL && count == 0) {
+ struct ip_tunnel_parm old_p;
+ memset(&old_p, 0, sizeof(old_p));
+ if (do_get_ioctl(*argv, &old_p))
+ return -1;
+ *p = old_p;
+ }
+ }
+ count++;
+ argc--; argv++;
+ }
+
+
+ if (p->iph.protocol == 0) {
+ if (memcmp(p->name, "gre", 3) == 0)
+ p->iph.protocol = IPPROTO_GRE;
+ else if (memcmp(p->name, "ipip", 4) == 0)
+ p->iph.protocol = IPPROTO_IPIP;
+ else if (memcmp(p->name, "sit", 3) == 0)
+ p->iph.protocol = IPPROTO_IPV6;
+ }
+
+ if (p->iph.protocol == IPPROTO_IPIP || p->iph.protocol == IPPROTO_IPV6) {
+ if ((p->i_flags & GRE_KEY) || (p->o_flags & GRE_KEY)) {
+ bb_error_msg("keys are not allowed with ipip and sit");
+ return -1;
+ }
+ }
+
+ if (medium[0]) {
+ p->link = do_ioctl_get_ifindex(medium);
+ if (p->link == 0)
+ return -1;
+ }
+
+ if (p->i_key == 0 && IN_MULTICAST(ntohl(p->iph.daddr))) {
+ p->i_key = p->iph.daddr;
+ p->i_flags |= GRE_KEY;
+ }
+ if (p->o_key == 0 && IN_MULTICAST(ntohl(p->iph.daddr))) {
+ p->o_key = p->iph.daddr;
+ p->o_flags |= GRE_KEY;
+ }
+ if (IN_MULTICAST(ntohl(p->iph.daddr)) && !p->iph.saddr) {
+ bb_error_msg("broadcast tunnel requires a source address");
+ return -1;
+ }
+ return 0;
+}
+
+
+static int do_add(int cmd, int argc, char **argv)
+{
+ struct ip_tunnel_parm p;
+
+ if (parse_args(argc, argv, cmd, &p) < 0)
+ return -1;
+
+ if (p.iph.ttl && p.iph.frag_off == 0) {
+ bb_error_msg("ttl != 0 and noptmudisc are incompatible");
+ return -1;
+ }
+
+ switch (p.iph.protocol) {
+ case IPPROTO_IPIP:
+ return do_add_ioctl(cmd, "tunl0", &p);
+ case IPPROTO_GRE:
+ return do_add_ioctl(cmd, "gre0", &p);
+ case IPPROTO_IPV6:
+ return do_add_ioctl(cmd, "sit0", &p);
+ default:
+ bb_error_msg("cannot determine tunnel mode (ipip, gre or sit)");
+ return -1;
+ }
+ return -1;
+}
+
+static int do_del(int argc, char **argv)
+{
+ struct ip_tunnel_parm p;
+
+ if (parse_args(argc, argv, SIOCDELTUNNEL, &p) < 0)
+ return -1;
+
+ switch (p.iph.protocol) {
+ case IPPROTO_IPIP:
+ return do_del_ioctl("tunl0", &p);
+ case IPPROTO_GRE:
+ return do_del_ioctl("gre0", &p);
+ case IPPROTO_IPV6:
+ return do_del_ioctl("sit0", &p);
+ default:
+ return do_del_ioctl(p.name, &p);
+ }
+ return -1;
+}
+
+static void print_tunnel(struct ip_tunnel_parm *p)
+{
+ char s1[256];
+ char s2[256];
+ char s3[64];
+ char s4[64];
+
+ format_host(AF_INET, 4, &p->iph.daddr, s1, sizeof(s1));
+ format_host(AF_INET, 4, &p->iph.saddr, s2, sizeof(s2));
+ inet_ntop(AF_INET, &p->i_key, s3, sizeof(s3));
+ inet_ntop(AF_INET, &p->o_key, s4, sizeof(s4));
+
+ printf("%s: %s/ip remote %s local %s ",
+ p->name,
+ p->iph.protocol == IPPROTO_IPIP ? "ip" :
+ (p->iph.protocol == IPPROTO_GRE ? "gre" :
+ (p->iph.protocol == IPPROTO_IPV6 ? "ipv6" : "unknown")),
+ p->iph.daddr ? s1 : "any", p->iph.saddr ? s2 : "any");
+ if (p->link) {
+ char *n = do_ioctl_get_ifname(p->link);
+ if (n)
+ printf(" dev %s ", n);
+ }
+ if (p->iph.ttl)
+ printf(" ttl %d ", p->iph.ttl);
+ else
+ printf(" ttl inherit ");
+ if (p->iph.tos) {
+ SPRINT_BUF(b1);
+ printf(" tos");
+ if (p->iph.tos & 1)
+ printf(" inherit");
+ if (p->iph.tos & ~1)
+ printf("%c%s ", p->iph.tos & 1 ? '/' : ' ',
+ rtnl_dsfield_n2a(p->iph.tos & ~1, b1, sizeof(b1)));
+ }
+ if (!(p->iph.frag_off & htons(IP_DF)))
+ printf(" nopmtudisc");
+
+ if ((p->i_flags & GRE_KEY) && (p->o_flags & GRE_KEY) && p->o_key == p->i_key)
+ printf(" key %s", s3);
+ else if ((p->i_flags | p->o_flags) & GRE_KEY) {
+ if (p->i_flags & GRE_KEY)
+ printf(" ikey %s ", s3);
+ if (p->o_flags & GRE_KEY)
+ printf(" okey %s ", s4);
+ }
+
+ if (p->i_flags & GRE_SEQ)
+ printf("%s Drop packets out of sequence.\n", _SL_);
+ if (p->i_flags & GRE_CSUM)
+ printf("%s Checksum in received packet is required.", _SL_);
+ if (p->o_flags & GRE_SEQ)
+ printf("%s Sequence packets on output.", _SL_);
+ if (p->o_flags & GRE_CSUM)
+ printf("%s Checksum output packets.", _SL_);
+}
+
+static int do_tunnels_list(struct ip_tunnel_parm *p)
+{
+ char name[IFNAMSIZ];
+ unsigned long rx_bytes, rx_packets, rx_errs, rx_drops,
+ rx_fifo, rx_frame,
+ tx_bytes, tx_packets, tx_errs, tx_drops,
+ tx_fifo, tx_colls, tx_carrier, rx_multi;
+ int type;
+ struct ip_tunnel_parm p1;
+ char buf[512];
+ FILE *fp = fopen("/proc/net/dev", "r");
+
+ if (fp == NULL) {
+ perror("fopen");
+ return -1;
+ }
+
+ fgets(buf, sizeof(buf), fp);
+ fgets(buf, sizeof(buf), fp);
+
+ while (fgets(buf, sizeof(buf), fp) != NULL) {
+ char *ptr;
+
+ /*buf[sizeof(buf) - 1] = 0; - fgets is safe anyway */
+ ptr = strchr(buf, ':');
+ if (ptr == NULL ||
+ (*ptr++ = 0, sscanf(buf, "%s", name) != 1)) {
+ bb_error_msg("wrong format of /proc/net/dev. Sorry");
+ return -1;
+ }
+ if (sscanf(ptr, "%lu%lu%lu%lu%lu%lu%lu%*d%lu%lu%lu%lu%lu%lu%lu",
+ &rx_bytes, &rx_packets, &rx_errs, &rx_drops,
+ &rx_fifo, &rx_frame, &rx_multi,
+ &tx_bytes, &tx_packets, &tx_errs, &tx_drops,
+ &tx_fifo, &tx_colls, &tx_carrier) != 14)
+ continue;
+ if (p->name[0] && strcmp(p->name, name))
+ continue;
+ type = do_ioctl_get_iftype(name);
+ if (type == -1) {
+ bb_error_msg("failed to get type of [%s]", name);
+ continue;
+ }
+ if (type != ARPHRD_TUNNEL && type != ARPHRD_IPGRE && type != ARPHRD_SIT)
+ continue;
+ memset(&p1, 0, sizeof(p1));
+ if (do_get_ioctl(name, &p1))
+ continue;
+ if ((p->link && p1.link != p->link) ||
+ (p->name[0] && strcmp(p1.name, p->name)) ||
+ (p->iph.daddr && p1.iph.daddr != p->iph.daddr) ||
+ (p->iph.saddr && p1.iph.saddr != p->iph.saddr) ||
+ (p->i_key && p1.i_key != p->i_key))
+ continue;
+ print_tunnel(&p1);
+ puts("");
+ }
+ return 0;
+}
+
+static int do_show(int argc, char **argv)
+{
+ int err;
+ struct ip_tunnel_parm p;
+
+ if (parse_args(argc, argv, SIOCGETTUNNEL, &p) < 0)
+ return -1;
+
+ switch (p.iph.protocol) {
+ case IPPROTO_IPIP:
+ err = do_get_ioctl(p.name[0] ? p.name : "tunl0", &p);
+ break;
+ case IPPROTO_GRE:
+ err = do_get_ioctl(p.name[0] ? p.name : "gre0", &p);
+ break;
+ case IPPROTO_IPV6:
+ err = do_get_ioctl(p.name[0] ? p.name : "sit0", &p);
+ break;
+ default:
+ do_tunnels_list(&p);
+ return 0;
+ }
+ if (err)
+ return -1;
+
+ print_tunnel(&p);
+ puts("");
+ return 0;
+}
+
+int do_iptunnel(int argc, char **argv)
+{
+ if (argc > 0) {
+ if (matches(*argv, "add") == 0)
+ return do_add(SIOCADDTUNNEL, argc-1, argv+1);
+ if (matches(*argv, "change") == 0)
+ return do_add(SIOCCHGTUNNEL, argc-1, argv+1);
+ if (matches(*argv, "del") == 0)
+ return do_del(argc-1, argv+1);
+ if (matches(*argv, "show") == 0 ||
+ matches(*argv, "lst") == 0 ||
+ matches(*argv, "list") == 0)
+ return do_show(argc-1, argv+1);
+ } else
+ return do_show(0, NULL);
+
+ bb_error_msg_and_die("command \"%s\" is unknown", *argv);
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/libiproute/libnetlink.c b/i/pc104/initrd/conf/busybox/networking/libiproute/libnetlink.c
new file mode 100644
index 0000000..9696480
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/libiproute/libnetlink.c
@@ -0,0 +1,396 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * libnetlink.c RTnetlink service routines.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ */
+
+#include "libbb.h"
+#include <sys/socket.h>
+
+#include <errno.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <sys/uio.h>
+
+#include "libnetlink.h"
+
+void rtnl_close(struct rtnl_handle *rth)
+{
+ close(rth->fd);
+}
+
+int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions)
+{
+ socklen_t addr_len;
+
+ memset(rth, 0, sizeof(rth));
+
+ rth->fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+ if (rth->fd < 0) {
+ bb_perror_msg("cannot open netlink socket");
+ return -1;
+ }
+
+ memset(&rth->local, 0, sizeof(rth->local));
+ rth->local.nl_family = AF_NETLINK;
+ rth->local.nl_groups = subscriptions;
+
+ if (bind(rth->fd, (struct sockaddr*)&rth->local, sizeof(rth->local)) < 0) {
+ bb_perror_msg("cannot bind netlink socket");
+ return -1;
+ }
+ addr_len = sizeof(rth->local);
+ if (getsockname(rth->fd, (struct sockaddr*)&rth->local, &addr_len) < 0) {
+ bb_perror_msg("cannot getsockname");
+ return -1;
+ }
+ if (addr_len != sizeof(rth->local)) {
+ bb_error_msg("wrong address length %d", addr_len);
+ return -1;
+ }
+ if (rth->local.nl_family != AF_NETLINK) {
+ bb_error_msg("wrong address family %d", rth->local.nl_family);
+ return -1;
+ }
+ rth->seq = time(NULL);
+ return 0;
+}
+
+int rtnl_wilddump_request(struct rtnl_handle *rth, int family, int type)
+{
+ struct {
+ struct nlmsghdr nlh;
+ struct rtgenmsg g;
+ } req;
+ struct sockaddr_nl nladdr;
+
+ memset(&nladdr, 0, sizeof(nladdr));
+ nladdr.nl_family = AF_NETLINK;
+
+ req.nlh.nlmsg_len = sizeof(req);
+ req.nlh.nlmsg_type = type;
+ req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
+ req.nlh.nlmsg_pid = 0;
+ req.nlh.nlmsg_seq = rth->dump = ++rth->seq;
+ req.g.rtgen_family = family;
+
+ return sendto(rth->fd, (void*)&req, sizeof(req), 0, (struct sockaddr*)&nladdr, sizeof(nladdr));
+}
+
+int rtnl_send(struct rtnl_handle *rth, char *buf, int len)
+{
+ struct sockaddr_nl nladdr;
+
+ memset(&nladdr, 0, sizeof(nladdr));
+ nladdr.nl_family = AF_NETLINK;
+
+ return sendto(rth->fd, buf, len, 0, (struct sockaddr*)&nladdr, sizeof(nladdr));
+}
+
+int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len)
+{
+ struct nlmsghdr nlh;
+ struct sockaddr_nl nladdr;
+ struct iovec iov[2] = { { &nlh, sizeof(nlh) }, { req, len } };
+ struct msghdr msg = {
+ (void*)&nladdr, sizeof(nladdr),
+ iov, 2,
+ NULL, 0,
+ 0
+ };
+
+ memset(&nladdr, 0, sizeof(nladdr));
+ nladdr.nl_family = AF_NETLINK;
+
+ nlh.nlmsg_len = NLMSG_LENGTH(len);
+ nlh.nlmsg_type = type;
+ nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
+ nlh.nlmsg_pid = 0;
+ nlh.nlmsg_seq = rth->dump = ++rth->seq;
+
+ return sendmsg(rth->fd, &msg, 0);
+}
+
+int rtnl_dump_filter(struct rtnl_handle *rth,
+ int (*filter)(struct sockaddr_nl *, struct nlmsghdr *n, void *),
+ void *arg1,
+ int (*junk)(struct sockaddr_nl *, struct nlmsghdr *n, void *),
+ void *arg2)
+{
+ char buf[8192];
+ struct sockaddr_nl nladdr;
+ struct iovec iov = { buf, sizeof(buf) };
+
+ while (1) {
+ int status;
+ struct nlmsghdr *h;
+
+ struct msghdr msg = {
+ (void*)&nladdr, sizeof(nladdr),
+ &iov, 1,
+ NULL, 0,
+ 0
+ };
+
+ status = recvmsg(rth->fd, &msg, 0);
+
+ if (status < 0) {
+ if (errno == EINTR)
+ continue;
+ bb_perror_msg("OVERRUN");
+ continue;
+ }
+ if (status == 0) {
+ bb_error_msg("EOF on netlink");
+ return -1;
+ }
+ if (msg.msg_namelen != sizeof(nladdr)) {
+ bb_error_msg_and_die("sender address length == %d", msg.msg_namelen);
+ }
+
+ h = (struct nlmsghdr*)buf;
+ while (NLMSG_OK(h, status)) {
+ int err;
+
+ if (nladdr.nl_pid != 0 ||
+ h->nlmsg_pid != rth->local.nl_pid ||
+ h->nlmsg_seq != rth->dump) {
+ if (junk) {
+ err = junk(&nladdr, h, arg2);
+ if (err < 0) {
+ return err;
+ }
+ }
+ goto skip_it;
+ }
+
+ if (h->nlmsg_type == NLMSG_DONE) {
+ return 0;
+ }
+ if (h->nlmsg_type == NLMSG_ERROR) {
+ struct nlmsgerr *l_err = (struct nlmsgerr*)NLMSG_DATA(h);
+ if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) {
+ bb_error_msg("ERROR truncated");
+ } else {
+ errno = -l_err->error;
+ bb_perror_msg("RTNETLINK answers");
+ }
+ return -1;
+ }
+ err = filter(&nladdr, h, arg1);
+ if (err < 0) {
+ return err;
+ }
+
+skip_it:
+ h = NLMSG_NEXT(h, status);
+ }
+ if (msg.msg_flags & MSG_TRUNC) {
+ bb_error_msg("message truncated");
+ continue;
+ }
+ if (status) {
+ bb_error_msg_and_die("remnant of size %d!", status);
+ }
+ }
+}
+
+int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer,
+ unsigned groups, struct nlmsghdr *answer,
+ int (*junk)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
+ void *jarg)
+{
+ int status;
+ unsigned seq;
+ struct nlmsghdr *h;
+ struct sockaddr_nl nladdr;
+ struct iovec iov = { (void*)n, n->nlmsg_len };
+ char buf[8192];
+ struct msghdr msg = {
+ (void*)&nladdr, sizeof(nladdr),
+ &iov, 1,
+ NULL, 0,
+ 0
+ };
+
+ memset(&nladdr, 0, sizeof(nladdr));
+ nladdr.nl_family = AF_NETLINK;
+ nladdr.nl_pid = peer;
+ nladdr.nl_groups = groups;
+
+ n->nlmsg_seq = seq = ++rtnl->seq;
+ if (answer == NULL) {
+ n->nlmsg_flags |= NLM_F_ACK;
+ }
+ status = sendmsg(rtnl->fd, &msg, 0);
+
+ if (status < 0) {
+ bb_perror_msg("cannot talk to rtnetlink");
+ return -1;
+ }
+
+ iov.iov_base = buf;
+
+ while (1) {
+ iov.iov_len = sizeof(buf);
+ status = recvmsg(rtnl->fd, &msg, 0);
+
+ if (status < 0) {
+ if (errno == EINTR) {
+ continue;
+ }
+ bb_perror_msg("OVERRUN");
+ continue;
+ }
+ if (status == 0) {
+ bb_error_msg("EOF on netlink");
+ return -1;
+ }
+ if (msg.msg_namelen != sizeof(nladdr)) {
+ bb_error_msg_and_die("sender address length == %d", msg.msg_namelen);
+ }
+ for (h = (struct nlmsghdr*)buf; status >= sizeof(*h); ) {
+ int l_err;
+ int len = h->nlmsg_len;
+ int l = len - sizeof(*h);
+
+ if (l<0 || len>status) {
+ if (msg.msg_flags & MSG_TRUNC) {
+ bb_error_msg("truncated message");
+ return -1;
+ }
+ bb_error_msg_and_die("malformed message: len=%d!", len);
+ }
+
+ if (nladdr.nl_pid != peer ||
+ h->nlmsg_pid != rtnl->local.nl_pid ||
+ h->nlmsg_seq != seq) {
+ if (junk) {
+ l_err = junk(&nladdr, h, jarg);
+ if (l_err < 0) {
+ return l_err;
+ }
+ }
+ continue;
+ }
+
+ if (h->nlmsg_type == NLMSG_ERROR) {
+ struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
+ if (l < sizeof(struct nlmsgerr)) {
+ bb_error_msg("ERROR truncated");
+ } else {
+ errno = -err->error;
+ if (errno == 0) {
+ if (answer) {
+ memcpy(answer, h, h->nlmsg_len);
+ }
+ return 0;
+ }
+ bb_perror_msg("RTNETLINK answers");
+ }
+ return -1;
+ }
+ if (answer) {
+ memcpy(answer, h, h->nlmsg_len);
+ return 0;
+ }
+
+ bb_error_msg("unexpected reply!");
+
+ status -= NLMSG_ALIGN(len);
+ h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
+ }
+ if (msg.msg_flags & MSG_TRUNC) {
+ bb_error_msg("message truncated");
+ continue;
+ }
+ if (status) {
+ bb_error_msg_and_die("remnant of size %d!", status);
+ }
+ }
+}
+
+int addattr32(struct nlmsghdr *n, int maxlen, int type, uint32_t data)
+{
+ int len = RTA_LENGTH(4);
+ struct rtattr *rta;
+ if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen)
+ return -1;
+ rta = (struct rtattr*)(((char*)n) + NLMSG_ALIGN(n->nlmsg_len));
+ rta->rta_type = type;
+ rta->rta_len = len;
+ memcpy(RTA_DATA(rta), &data, 4);
+ n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len;
+ return 0;
+}
+
+int addattr_l(struct nlmsghdr *n, int maxlen, int type, void *data, int alen)
+{
+ int len = RTA_LENGTH(alen);
+ struct rtattr *rta;
+
+ if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen)
+ return -1;
+ rta = (struct rtattr*)(((char*)n) + NLMSG_ALIGN(n->nlmsg_len));
+ rta->rta_type = type;
+ rta->rta_len = len;
+ memcpy(RTA_DATA(rta), data, alen);
+ n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len;
+ return 0;
+}
+
+int rta_addattr32(struct rtattr *rta, int maxlen, int type, uint32_t data)
+{
+ int len = RTA_LENGTH(4);
+ struct rtattr *subrta;
+
+ if (RTA_ALIGN(rta->rta_len) + len > maxlen) {
+ return -1;
+ }
+ subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len));
+ subrta->rta_type = type;
+ subrta->rta_len = len;
+ memcpy(RTA_DATA(subrta), &data, 4);
+ rta->rta_len = NLMSG_ALIGN(rta->rta_len) + len;
+ return 0;
+}
+
+int rta_addattr_l(struct rtattr *rta, int maxlen, int type, void *data, int alen)
+{
+ struct rtattr *subrta;
+ int len = RTA_LENGTH(alen);
+
+ if (RTA_ALIGN(rta->rta_len) + len > maxlen) {
+ return -1;
+ }
+ subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len));
+ subrta->rta_type = type;
+ subrta->rta_len = len;
+ memcpy(RTA_DATA(subrta), data, alen);
+ rta->rta_len = NLMSG_ALIGN(rta->rta_len) + len;
+ return 0;
+}
+
+
+int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len)
+{
+ while (RTA_OK(rta, len)) {
+ if (rta->rta_type <= max) {
+ tb[rta->rta_type] = rta;
+ }
+ rta = RTA_NEXT(rta,len);
+ }
+ if (len) {
+ bb_error_msg("deficit %d, rta_len=%d!", len, rta->rta_len);
+ }
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/libiproute/libnetlink.h b/i/pc104/initrd/conf/busybox/networking/libiproute/libnetlink.h
new file mode 100644
index 0000000..9a5a9d3
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/libiproute/libnetlink.h
@@ -0,0 +1,43 @@
+/* vi: set sw=4 ts=4: */
+#ifndef __LIBNETLINK_H__
+#define __LIBNETLINK_H__ 1
+
+#include <linux/types.h>
+/* We need linux/types.h because older kernels use __u32 etc
+ * in linux/[rt]netlink.h. 2.6.19 seems to be ok, though */
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+
+struct rtnl_handle
+{
+ int fd;
+ struct sockaddr_nl local;
+ struct sockaddr_nl peer;
+ uint32_t seq;
+ uint32_t dump;
+};
+
+extern int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions);
+extern void rtnl_close(struct rtnl_handle *rth);
+extern int rtnl_wilddump_request(struct rtnl_handle *rth, int fam, int type);
+extern int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len);
+extern int rtnl_dump_filter(struct rtnl_handle *rth,
+ int (*filter)(struct sockaddr_nl*, struct nlmsghdr *n, void*),
+ void *arg1,
+ int (*junk)(struct sockaddr_nl *, struct nlmsghdr *n, void *),
+ void *arg2);
+extern int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer,
+ unsigned groups, struct nlmsghdr *answer,
+ int (*junk)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
+ void *jarg);
+extern int rtnl_send(struct rtnl_handle *rth, char *buf, int);
+
+
+extern int addattr32(struct nlmsghdr *n, int maxlen, int type, uint32_t data);
+extern int addattr_l(struct nlmsghdr *n, int maxlen, int type, void *data, int alen);
+extern int rta_addattr32(struct rtattr *rta, int maxlen, int type, uint32_t data);
+extern int rta_addattr_l(struct rtattr *rta, int maxlen, int type, void *data, int alen);
+
+extern int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len);
+
+#endif /* __LIBNETLINK_H__ */
diff --git a/i/pc104/initrd/conf/busybox/networking/libiproute/ll_addr.c b/i/pc104/initrd/conf/busybox/networking/libiproute/ll_addr.c
new file mode 100644
index 0000000..b4a2187
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/libiproute/ll_addr.c
@@ -0,0 +1,85 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ll_addr.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ */
+
+#include "libbb.h"
+
+#include <string.h>
+#include <net/if_arp.h>
+
+#include "rt_names.h"
+#include "utils.h"
+
+
+const char *ll_addr_n2a(unsigned char *addr, int alen, int type, char *buf, int blen)
+{
+ int i;
+ int l;
+
+ if (alen == 4 &&
+ (type == ARPHRD_TUNNEL || type == ARPHRD_SIT || type == ARPHRD_IPGRE)) {
+ return inet_ntop(AF_INET, addr, buf, blen);
+ }
+ l = 0;
+ for (i=0; i<alen; i++) {
+ if (i==0) {
+ snprintf(buf+l, blen, ":%02x"+1, addr[i]);
+ blen -= 2;
+ l += 2;
+ } else {
+ snprintf(buf+l, blen, ":%02x", addr[i]);
+ blen -= 3;
+ l += 3;
+ }
+ }
+ return buf;
+}
+
+int ll_addr_a2n(unsigned char *lladdr, int len, char *arg)
+{
+ if (strchr(arg, '.')) {
+ inet_prefix pfx;
+ if (get_addr_1(&pfx, arg, AF_INET)) {
+ bb_error_msg("\"%s\" is invalid lladdr", arg);
+ return -1;
+ }
+ if (len < 4) {
+ return -1;
+ }
+ memcpy(lladdr, pfx.data, 4);
+ return 4;
+ } else {
+ int i;
+
+ for (i=0; i<len; i++) {
+ int temp;
+ char *cp = strchr(arg, ':');
+ if (cp) {
+ *cp = 0;
+ cp++;
+ }
+ if (sscanf(arg, "%x", &temp) != 1) {
+ bb_error_msg("\"%s\" is invalid lladdr", arg);
+ return -1;
+ }
+ if (temp < 0 || temp > 255) {
+ bb_error_msg("\"%s\" is invalid lladdr", arg);
+ return -1;
+ }
+ lladdr[i] = temp;
+ if (!cp) {
+ break;
+ }
+ arg = cp;
+ }
+ return i+1;
+ }
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/libiproute/ll_map.c b/i/pc104/initrd/conf/busybox/networking/libiproute/ll_map.c
new file mode 100644
index 0000000..f429763
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/libiproute/ll_map.c
@@ -0,0 +1,186 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ll_map.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ */
+
+#include "libbb.h"
+#include <string.h>
+
+#include "libnetlink.h"
+#include "ll_map.h"
+
+#include <sys/socket.h> /* socket() */
+#include <net/if.h> /* struct ifreq and co. */
+#include <sys/ioctl.h> /* ioctl() & SIOCGIFINDEX */
+
+struct idxmap {
+ struct idxmap * next;
+ int index;
+ int type;
+ int alen;
+ unsigned flags;
+ unsigned char addr[8];
+ char name[16];
+};
+
+static struct idxmap *idxmap[16];
+
+int ll_remember_index(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+{
+ int h;
+ struct ifinfomsg *ifi = NLMSG_DATA(n);
+ struct idxmap *im, **imp;
+ struct rtattr *tb[IFLA_MAX+1];
+
+ if (n->nlmsg_type != RTM_NEWLINK)
+ return 0;
+
+ if (n->nlmsg_len < NLMSG_LENGTH(sizeof(ifi)))
+ return -1;
+
+
+ memset(tb, 0, sizeof(tb));
+ parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), IFLA_PAYLOAD(n));
+ if (tb[IFLA_IFNAME] == NULL)
+ return 0;
+
+ h = ifi->ifi_index&0xF;
+
+ for (imp=&idxmap[h]; (im=*imp)!=NULL; imp = &im->next)
+ if (im->index == ifi->ifi_index)
+ break;
+
+ if (im == NULL) {
+ im = xmalloc(sizeof(*im));
+ im->next = *imp;
+ im->index = ifi->ifi_index;
+ *imp = im;
+ }
+
+ im->type = ifi->ifi_type;
+ im->flags = ifi->ifi_flags;
+ if (tb[IFLA_ADDRESS]) {
+ int alen;
+ im->alen = alen = RTA_PAYLOAD(tb[IFLA_ADDRESS]);
+ if (alen > sizeof(im->addr))
+ alen = sizeof(im->addr);
+ memcpy(im->addr, RTA_DATA(tb[IFLA_ADDRESS]), alen);
+ } else {
+ im->alen = 0;
+ memset(im->addr, 0, sizeof(im->addr));
+ }
+ strcpy(im->name, RTA_DATA(tb[IFLA_IFNAME]));
+ return 0;
+}
+
+const char *ll_idx_n2a(int idx, char *buf)
+{
+ struct idxmap *im;
+
+ if (idx == 0)
+ return "*";
+ for (im = idxmap[idx&0xF]; im; im = im->next)
+ if (im->index == idx)
+ return im->name;
+ snprintf(buf, 16, "if%d", idx);
+ return buf;
+}
+
+
+const char *ll_index_to_name(int idx)
+{
+ static char nbuf[16];
+
+ return ll_idx_n2a(idx, nbuf);
+}
+
+int ll_index_to_type(int idx)
+{
+ struct idxmap *im;
+
+ if (idx == 0)
+ return -1;
+ for (im = idxmap[idx&0xF]; im; im = im->next)
+ if (im->index == idx)
+ return im->type;
+ return -1;
+}
+
+unsigned ll_index_to_flags(int idx)
+{
+ struct idxmap *im;
+
+ if (idx == 0)
+ return 0;
+
+ for (im = idxmap[idx&0xF]; im; im = im->next)
+ if (im->index == idx)
+ return im->flags;
+ return 0;
+}
+
+int ll_name_to_index(char *name)
+{
+ static char ncache[16];
+ static int icache;
+ struct idxmap *im;
+ int sock_fd;
+ int i;
+
+ if (name == NULL)
+ return 0;
+ if (icache && strcmp(name, ncache) == 0)
+ return icache;
+ for (i=0; i<16; i++) {
+ for (im = idxmap[i]; im; im = im->next) {
+ if (strcmp(im->name, name) == 0) {
+ icache = im->index;
+ strcpy(ncache, name);
+ return im->index;
+ }
+ }
+ }
+ /* We have not found the interface in our cache, but the kernel
+ * may still know about it. One reason is that we may be using
+ * module on-demand loading, which means that the kernel will
+ * load the module and make the interface exist only when
+ * we explicitely request it (check for dev_load() in net/core/dev.c).
+ * I can think of other similar scenario, but they are less common...
+ * Jean II */
+ sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock_fd) {
+ struct ifreq ifr;
+ int ret;
+ strncpy(ifr.ifr_name, name, IFNAMSIZ);
+ ifr.ifr_ifindex = -1;
+ ret = ioctl(sock_fd, SIOCGIFINDEX, &ifr);
+ close(sock_fd);
+ if (ret >= 0)
+ /* In theory, we should redump the interface list
+ * to update our cache, this is left as an exercise
+ * to the reader... Jean II */
+ return ifr.ifr_ifindex;
+ }
+
+ return 0;
+}
+
+int ll_init_map(struct rtnl_handle *rth)
+{
+ if (rtnl_wilddump_request(rth, AF_UNSPEC, RTM_GETLINK) < 0) {
+ bb_perror_msg_and_die("cannot send dump request");
+ }
+
+ if (rtnl_dump_filter(rth, ll_remember_index, &idxmap, NULL, NULL) < 0) {
+ bb_error_msg_and_die("dump terminated");
+ }
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/libiproute/ll_map.h b/i/pc104/initrd/conf/busybox/networking/libiproute/ll_map.h
new file mode 100644
index 0000000..226d48f
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/libiproute/ll_map.h
@@ -0,0 +1,13 @@
+/* vi: set sw=4 ts=4: */
+#ifndef __LL_MAP_H__
+#define __LL_MAP_H__ 1
+
+extern int ll_remember_index(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg);
+extern int ll_init_map(struct rtnl_handle *rth);
+extern int ll_name_to_index(char *name);
+extern const char *ll_index_to_name(int idx);
+extern const char *ll_idx_n2a(int idx, char *buf);
+extern int ll_index_to_type(int idx);
+extern unsigned ll_index_to_flags(int idx);
+
+#endif /* __LL_MAP_H__ */
diff --git a/i/pc104/initrd/conf/busybox/networking/libiproute/ll_proto.c b/i/pc104/initrd/conf/busybox/networking/libiproute/ll_proto.c
new file mode 100644
index 0000000..aad460b
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/libiproute/ll_proto.c
@@ -0,0 +1,121 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ll_proto.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ */
+
+#include "libbb.h"
+
+#include "rt_names.h"
+#include "utils.h"
+
+#if defined(__GLIBC__) && __GLIBC__ >=2 && __GLIBC_MINOR__ >= 1
+#include <net/ethernet.h>
+#else
+#include <linux/if_ether.h>
+#endif
+
+#define __PF(f,n) { ETH_P_##f, #n },
+static struct {
+ int id;
+ const char *name;
+} llproto_names[] = {
+__PF(LOOP,loop)
+__PF(PUP,pup)
+#ifdef ETH_P_PUPAT
+__PF(PUPAT,pupat)
+#endif
+__PF(IP,ip)
+__PF(X25,x25)
+__PF(ARP,arp)
+__PF(BPQ,bpq)
+#ifdef ETH_P_IEEEPUP
+__PF(IEEEPUP,ieeepup)
+#endif
+#ifdef ETH_P_IEEEPUPAT
+__PF(IEEEPUPAT,ieeepupat)
+#endif
+__PF(DEC,dec)
+__PF(DNA_DL,dna_dl)
+__PF(DNA_RC,dna_rc)
+__PF(DNA_RT,dna_rt)
+__PF(LAT,lat)
+__PF(DIAG,diag)
+__PF(CUST,cust)
+__PF(SCA,sca)
+__PF(RARP,rarp)
+__PF(ATALK,atalk)
+__PF(AARP,aarp)
+__PF(IPX,ipx)
+__PF(IPV6,ipv6)
+#ifdef ETH_P_PPP_DISC
+__PF(PPP_DISC,ppp_disc)
+#endif
+#ifdef ETH_P_PPP_SES
+__PF(PPP_SES,ppp_ses)
+#endif
+#ifdef ETH_P_ATMMPOA
+__PF(ATMMPOA,atmmpoa)
+#endif
+#ifdef ETH_P_ATMFATE
+__PF(ATMFATE,atmfate)
+#endif
+
+__PF(802_3,802_3)
+__PF(AX25,ax25)
+__PF(ALL,all)
+__PF(802_2,802_2)
+__PF(SNAP,snap)
+__PF(DDCMP,ddcmp)
+__PF(WAN_PPP,wan_ppp)
+__PF(PPP_MP,ppp_mp)
+__PF(LOCALTALK,localtalk)
+__PF(PPPTALK,ppptalk)
+__PF(TR_802_2,tr_802_2)
+__PF(MOBITEX,mobitex)
+__PF(CONTROL,control)
+__PF(IRDA,irda)
+#ifdef ETH_P_ECONET
+__PF(ECONET,econet)
+#endif
+
+{ 0x8100, "802.1Q" },
+{ ETH_P_IP, "ipv4" },
+};
+#undef __PF
+
+
+const char * ll_proto_n2a(unsigned short id, char *buf, int len)
+{
+ int i;
+
+ id = ntohs(id);
+
+ for (i=0; i<sizeof(llproto_names)/sizeof(llproto_names[0]); i++) {
+ if (llproto_names[i].id == id)
+ return llproto_names[i].name;
+ }
+ snprintf(buf, len, "[%d]", id);
+ return buf;
+}
+
+int ll_proto_a2n(unsigned short *id, char *buf)
+{
+ int i;
+ for (i=0; i<sizeof(llproto_names)/sizeof(llproto_names[0]); i++) {
+ if (strcasecmp(llproto_names[i].name, buf) == 0) {
+ *id = htons(llproto_names[i].id);
+ return 0;
+ }
+ }
+ if (get_u16(id, buf, 0))
+ return -1;
+ *id = htons(*id);
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/libiproute/ll_types.c b/i/pc104/initrd/conf/busybox/networking/libiproute/ll_types.c
new file mode 100644
index 0000000..416ea6b
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/libiproute/ll_types.c
@@ -0,0 +1,117 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ll_types.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ */
+#include <stdio.h>
+#include <arpa/inet.h>
+#include <linux/if_arp.h>
+
+#include "rt_names.h"
+
+const char* ll_type_n2a(int type, char *buf, int len)
+{
+#define __PF(f,n) { ARPHRD_##f, #n },
+static const struct {
+ int type;
+ const char *name;
+} arphrd_names[] = {
+{ 0, "generic" },
+__PF(ETHER,ether)
+__PF(EETHER,eether)
+__PF(AX25,ax25)
+__PF(PRONET,pronet)
+__PF(CHAOS,chaos)
+#ifdef ARPHRD_IEEE802_TR
+__PF(IEEE802,ieee802)
+#else
+__PF(IEEE802,tr)
+#endif
+__PF(ARCNET,arcnet)
+__PF(APPLETLK,atalk)
+__PF(DLCI,dlci)
+#ifdef ARPHRD_ATM
+__PF(ATM,atm)
+#endif
+__PF(METRICOM,metricom)
+#ifdef ARPHRD_IEEE1394
+__PF(IEEE1394,ieee1394)
+#endif
+
+__PF(SLIP,slip)
+__PF(CSLIP,cslip)
+__PF(SLIP6,slip6)
+__PF(CSLIP6,cslip6)
+__PF(RSRVD,rsrvd)
+__PF(ADAPT,adapt)
+__PF(ROSE,rose)
+__PF(X25,x25)
+#ifdef ARPHRD_HWX25
+__PF(HWX25,hwx25)
+#endif
+__PF(PPP,ppp)
+__PF(HDLC,hdlc)
+__PF(LAPB,lapb)
+#ifdef ARPHRD_DDCMP
+__PF(DDCMP,ddcmp)
+__PF(RAWHDLC,rawhdlc)
+#endif
+
+__PF(TUNNEL,ipip)
+__PF(TUNNEL6,tunnel6)
+__PF(FRAD,frad)
+__PF(SKIP,skip)
+__PF(LOOPBACK,loopback)
+__PF(LOCALTLK,ltalk)
+__PF(FDDI,fddi)
+__PF(BIF,bif)
+__PF(SIT,sit)
+__PF(IPDDP,ip/ddp)
+__PF(IPGRE,gre)
+__PF(PIMREG,pimreg)
+__PF(HIPPI,hippi)
+__PF(ASH,ash)
+__PF(ECONET,econet)
+__PF(IRDA,irda)
+__PF(FCPP,fcpp)
+__PF(FCAL,fcal)
+__PF(FCPL,fcpl)
+__PF(FCFABRIC,fcfb0)
+__PF(FCFABRIC+1,fcfb1)
+__PF(FCFABRIC+2,fcfb2)
+__PF(FCFABRIC+3,fcfb3)
+__PF(FCFABRIC+4,fcfb4)
+__PF(FCFABRIC+5,fcfb5)
+__PF(FCFABRIC+6,fcfb6)
+__PF(FCFABRIC+7,fcfb7)
+__PF(FCFABRIC+8,fcfb8)
+__PF(FCFABRIC+9,fcfb9)
+__PF(FCFABRIC+10,fcfb10)
+__PF(FCFABRIC+11,fcfb11)
+__PF(FCFABRIC+12,fcfb12)
+#ifdef ARPHRD_IEEE802_TR
+__PF(IEEE802_TR,tr)
+#endif
+#ifdef ARPHRD_IEEE80211
+__PF(IEEE80211,ieee802.11)
+#endif
+#ifdef ARPHRD_VOID
+__PF(VOID,void)
+#endif
+};
+#undef __PF
+
+ int i;
+ for (i = 0; i < sizeof(arphrd_names)/sizeof(arphrd_names[0]); i++) {
+ if (arphrd_names[i].type == type)
+ return arphrd_names[i].name;
+ }
+ snprintf(buf, len, "[%d]", type);
+ return buf;
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/libiproute/rt_names.c b/i/pc104/initrd/conf/busybox/networking/libiproute/rt_names.c
new file mode 100644
index 0000000..797c83b
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/libiproute/rt_names.c
@@ -0,0 +1,365 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * rt_names.c rtnetlink names DB.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ */
+
+#include "libbb.h"
+#include "rt_names.h"
+
+static void rtnl_tab_initialize(const char *file, const char **tab, int size)
+{
+ char buf[512];
+ FILE *fp;
+
+ fp = fopen(file, "r");
+ if (!fp)
+ return;
+ while (fgets(buf, sizeof(buf), fp)) {
+ char *p = buf;
+ int id;
+ char namebuf[512];
+
+ while (*p == ' ' || *p == '\t')
+ p++;
+ if (*p == '#' || *p == '\n' || *p == 0)
+ continue;
+ if (sscanf(p, "0x%x %s\n", &id, namebuf) != 2
+ && sscanf(p, "0x%x %s #", &id, namebuf) != 2
+ && sscanf(p, "%d %s\n", &id, namebuf) != 2
+ && sscanf(p, "%d %s #", &id, namebuf) != 2
+ ) {
+ bb_error_msg("database %s is corrupted at %s",
+ file, p);
+ return;
+ }
+
+ if (id < 0 || id > size)
+ continue;
+
+ tab[id] = xstrdup(namebuf);
+ }
+ fclose(fp);
+}
+
+
+static const char **rtnl_rtprot_tab; /* [256] */
+
+static void rtnl_rtprot_initialize(void)
+{
+ static const char *const init_tab[] = {
+ "none",
+ "redirect",
+ "kernel",
+ "boot",
+ "static",
+ NULL,
+ NULL,
+ NULL,
+ "gated",
+ "ra",
+ "mrt",
+ "zebra",
+ "bird",
+ };
+ if (rtnl_rtprot_tab) return;
+ rtnl_rtprot_tab = xzalloc(256 * sizeof(rtnl_rtprot_tab[0]));
+ memcpy(rtnl_rtprot_tab, init_tab, sizeof(init_tab));
+ rtnl_tab_initialize("/etc/iproute2/rt_protos",
+ rtnl_rtprot_tab, 256);
+}
+
+
+const char* rtnl_rtprot_n2a(int id, char *buf, int len)
+{
+ if (id < 0 || id >= 256) {
+ snprintf(buf, len, "%d", id);
+ return buf;
+ }
+
+ rtnl_rtprot_initialize();
+
+ if (rtnl_rtprot_tab[id])
+ return rtnl_rtprot_tab[id];
+ snprintf(buf, len, "%d", id);
+ return buf;
+}
+
+int rtnl_rtprot_a2n(uint32_t *id, char *arg)
+{
+ static const char *cache = NULL;
+ static unsigned long res;
+ int i;
+
+ if (cache && strcmp(cache, arg) == 0) {
+ *id = res;
+ return 0;
+ }
+
+ rtnl_rtprot_initialize();
+
+ for (i = 0; i < 256; i++) {
+ if (rtnl_rtprot_tab[i] &&
+ strcmp(rtnl_rtprot_tab[i], arg) == 0) {
+ cache = rtnl_rtprot_tab[i];
+ res = i;
+ *id = res;
+ return 0;
+ }
+ }
+
+ res = bb_strtoul(arg, NULL, 0);
+ if (errno || res > 255)
+ return -1;
+ *id = res;
+ return 0;
+}
+
+
+static const char **rtnl_rtscope_tab; /* [256] */
+
+static void rtnl_rtscope_initialize(void)
+{
+ if (rtnl_rtscope_tab) return;
+ rtnl_rtscope_tab = xzalloc(256 * sizeof(rtnl_rtscope_tab[0]));
+ rtnl_rtscope_tab[0] = "global";
+ rtnl_rtscope_tab[255] = "nowhere";
+ rtnl_rtscope_tab[254] = "host";
+ rtnl_rtscope_tab[253] = "link";
+ rtnl_rtscope_tab[200] = "site";
+ rtnl_tab_initialize("/etc/iproute2/rt_scopes",
+ rtnl_rtscope_tab, 256);
+}
+
+
+const char* rtnl_rtscope_n2a(int id, char *buf, int len)
+{
+ if (id < 0 || id >= 256) {
+ snprintf(buf, len, "%d", id);
+ return buf;
+ }
+
+ rtnl_rtscope_initialize();
+
+ if (rtnl_rtscope_tab[id])
+ return rtnl_rtscope_tab[id];
+ snprintf(buf, len, "%d", id);
+ return buf;
+}
+
+int rtnl_rtscope_a2n(uint32_t *id, char *arg)
+{
+ static const char *cache = NULL;
+ static unsigned long res;
+ int i;
+
+ if (cache && strcmp(cache, arg) == 0) {
+ *id = res;
+ return 0;
+ }
+
+ rtnl_rtscope_initialize();
+
+ for (i = 0; i < 256; i++) {
+ if (rtnl_rtscope_tab[i] &&
+ strcmp(rtnl_rtscope_tab[i], arg) == 0) {
+ cache = rtnl_rtscope_tab[i];
+ res = i;
+ *id = res;
+ return 0;
+ }
+ }
+
+ res = bb_strtoul(arg, NULL, 0);
+ if (errno || res > 255)
+ return -1;
+ *id = res;
+ return 0;
+}
+
+
+static const char **rtnl_rtrealm_tab; /* [256] */
+
+static void rtnl_rtrealm_initialize(void)
+{
+ if (rtnl_rtrealm_tab) return;
+ rtnl_rtrealm_tab = xzalloc(256 * sizeof(rtnl_rtrealm_tab[0]));
+ rtnl_rtrealm_tab[0] = "unknown";
+ rtnl_tab_initialize("/etc/iproute2/rt_realms",
+ rtnl_rtrealm_tab, 256);
+}
+
+
+int rtnl_rtrealm_a2n(uint32_t *id, char *arg)
+{
+ static const char *cache = NULL;
+ static unsigned long res;
+ int i;
+
+ if (cache && strcmp(cache, arg) == 0) {
+ *id = res;
+ return 0;
+ }
+
+ rtnl_rtrealm_initialize();
+
+ for (i = 0; i < 256; i++) {
+ if (rtnl_rtrealm_tab[i] &&
+ strcmp(rtnl_rtrealm_tab[i], arg) == 0) {
+ cache = rtnl_rtrealm_tab[i];
+ res = i;
+ *id = res;
+ return 0;
+ }
+ }
+
+ res = bb_strtoul(arg, NULL, 0);
+ if (errno || res > 255)
+ return -1;
+ *id = res;
+ return 0;
+}
+
+#if ENABLE_FEATURE_IP_RULE
+const char* rtnl_rtrealm_n2a(int id, char *buf, int len)
+{
+ if (id < 0 || id >= 256) {
+ snprintf(buf, len, "%d", id);
+ return buf;
+ }
+
+ rtnl_rtrealm_initialize();
+
+ if (rtnl_rtrealm_tab[id])
+ return rtnl_rtrealm_tab[id];
+ snprintf(buf, len, "%d", id);
+ return buf;
+}
+#endif
+
+
+static const char **rtnl_rtdsfield_tab; /* [256] */
+
+static void rtnl_rtdsfield_initialize(void)
+{
+ if (rtnl_rtdsfield_tab) return;
+ rtnl_rtdsfield_tab = xzalloc(256 * sizeof(rtnl_rtdsfield_tab[0]));
+ rtnl_rtdsfield_tab[0] = "0";
+ rtnl_tab_initialize("/etc/iproute2/rt_dsfield",
+ rtnl_rtdsfield_tab, 256);
+}
+
+
+const char * rtnl_dsfield_n2a(int id, char *buf, int len)
+{
+ if (id < 0 || id >= 256) {
+ snprintf(buf, len, "%d", id);
+ return buf;
+ }
+
+ rtnl_rtdsfield_initialize();
+
+ if (rtnl_rtdsfield_tab[id])
+ return rtnl_rtdsfield_tab[id];
+ snprintf(buf, len, "0x%02x", id);
+ return buf;
+}
+
+
+int rtnl_dsfield_a2n(uint32_t *id, char *arg)
+{
+ static const char *cache = NULL;
+ static unsigned long res;
+ int i;
+
+ if (cache && strcmp(cache, arg) == 0) {
+ *id = res;
+ return 0;
+ }
+
+ rtnl_rtdsfield_initialize();
+
+ for (i = 0; i < 256; i++) {
+ if (rtnl_rtdsfield_tab[i] &&
+ strcmp(rtnl_rtdsfield_tab[i], arg) == 0) {
+ cache = rtnl_rtdsfield_tab[i];
+ res = i;
+ *id = res;
+ return 0;
+ }
+ }
+
+ res = bb_strtoul(arg, NULL, 16);
+ if (errno || res > 255)
+ return -1;
+ *id = res;
+ return 0;
+}
+
+
+#if ENABLE_FEATURE_IP_RULE
+static const char **rtnl_rttable_tab; /* [256] */
+
+static void rtnl_rttable_initialize(void)
+{
+ if (rtnl_rtdsfield_tab) return;
+ rtnl_rttable_tab = xzalloc(256 * sizeof(rtnl_rttable_tab[0]));
+ rtnl_rttable_tab[0] = "unspec";
+ rtnl_rttable_tab[255] = "local";
+ rtnl_rttable_tab[254] = "main";
+ rtnl_rttable_tab[253] = "default";
+ rtnl_tab_initialize("/etc/iproute2/rt_tables", rtnl_rttable_tab, 256);
+}
+
+
+const char *rtnl_rttable_n2a(int id, char *buf, int len)
+{
+ if (id < 0 || id >= 256) {
+ snprintf(buf, len, "%d", id);
+ return buf;
+ }
+
+ rtnl_rttable_initialize();
+
+ if (rtnl_rttable_tab[id])
+ return rtnl_rttable_tab[id];
+ snprintf(buf, len, "%d", id);
+ return buf;
+}
+
+int rtnl_rttable_a2n(uint32_t * id, char *arg)
+{
+ static char *cache = NULL;
+ static unsigned long res;
+ int i;
+
+ if (cache && strcmp(cache, arg) == 0) {
+ *id = res;
+ return 0;
+ }
+
+ rtnl_rttable_initialize();
+
+ for (i = 0; i < 256; i++) {
+ if (rtnl_rttable_tab[i] && strcmp(rtnl_rttable_tab[i], arg) == 0) {
+ cache = (char*)rtnl_rttable_tab[i];
+ res = i;
+ *id = res;
+ return 0;
+ }
+ }
+
+ i = bb_strtoul(arg, NULL, 0);
+ if (errno || i > 255)
+ return -1;
+ *id = i;
+ return 0;
+}
+
+#endif
diff --git a/i/pc104/initrd/conf/busybox/networking/libiproute/rt_names.h b/i/pc104/initrd/conf/busybox/networking/libiproute/rt_names.h
new file mode 100644
index 0000000..98c22b0
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/libiproute/rt_names.h
@@ -0,0 +1,28 @@
+/* vi: set sw=4 ts=4: */
+#ifndef RT_NAMES_H_
+#define RT_NAMES_H_ 1
+
+#include <stdint.h>
+
+extern const char* rtnl_rtprot_n2a(int id, char *buf, int len);
+extern const char* rtnl_rtscope_n2a(int id, char *buf, int len);
+extern const char* rtnl_rtrealm_n2a(int id, char *buf, int len);
+extern const char* rtnl_dsfield_n2a(int id, char *buf, int len);
+extern const char* rtnl_rttable_n2a(int id, char *buf, int len);
+extern int rtnl_rtprot_a2n(uint32_t *id, char *arg);
+extern int rtnl_rtscope_a2n(uint32_t *id, char *arg);
+extern int rtnl_rtrealm_a2n(uint32_t *id, char *arg);
+extern int rtnl_dsfield_a2n(uint32_t *id, char *arg);
+extern int rtnl_rttable_a2n(uint32_t *id, char *arg);
+
+
+extern const char* ll_type_n2a(int type, char *buf, int len);
+
+extern const char* ll_addr_n2a(unsigned char *addr, int alen, int type,
+ char *buf, int blen);
+extern int ll_addr_a2n(unsigned char *lladdr, int len, char *arg);
+
+extern const char* ll_proto_n2a(unsigned short id, char *buf, int len);
+extern int ll_proto_a2n(unsigned short *id, char *buf);
+
+#endif
diff --git a/i/pc104/initrd/conf/busybox/networking/libiproute/rtm_map.c b/i/pc104/initrd/conf/busybox/networking/libiproute/rtm_map.c
new file mode 100644
index 0000000..c16406b
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/libiproute/rtm_map.c
@@ -0,0 +1,110 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * rtm_map.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "rt_names.h"
+#include "utils.h"
+
+const char *rtnl_rtntype_n2a(int id, char *buf, int len)
+{
+ switch (id) {
+ case RTN_UNSPEC:
+ return "none";
+ case RTN_UNICAST:
+ return "unicast";
+ case RTN_LOCAL:
+ return "local";
+ case RTN_BROADCAST:
+ return "broadcast";
+ case RTN_ANYCAST:
+ return "anycast";
+ case RTN_MULTICAST:
+ return "multicast";
+ case RTN_BLACKHOLE:
+ return "blackhole";
+ case RTN_UNREACHABLE:
+ return "unreachable";
+ case RTN_PROHIBIT:
+ return "prohibit";
+ case RTN_THROW:
+ return "throw";
+ case RTN_NAT:
+ return "nat";
+ case RTN_XRESOLVE:
+ return "xresolve";
+ default:
+ snprintf(buf, len, "%d", id);
+ return buf;
+ }
+}
+
+
+int rtnl_rtntype_a2n(int *id, char *arg)
+{
+ char *end;
+ unsigned long res;
+
+ if (strcmp(arg, "local") == 0)
+ res = RTN_LOCAL;
+ else if (strcmp(arg, "nat") == 0)
+ res = RTN_NAT;
+ else if (matches(arg, "broadcast") == 0 ||
+ strcmp(arg, "brd") == 0)
+ res = RTN_BROADCAST;
+ else if (matches(arg, "anycast") == 0)
+ res = RTN_ANYCAST;
+ else if (matches(arg, "multicast") == 0)
+ res = RTN_MULTICAST;
+ else if (matches(arg, "prohibit") == 0)
+ res = RTN_PROHIBIT;
+ else if (matches(arg, "unreachable") == 0)
+ res = RTN_UNREACHABLE;
+ else if (matches(arg, "blackhole") == 0)
+ res = RTN_BLACKHOLE;
+ else if (matches(arg, "xresolve") == 0)
+ res = RTN_XRESOLVE;
+ else if (matches(arg, "unicast") == 0)
+ res = RTN_UNICAST;
+ else if (strcmp(arg, "throw") == 0)
+ res = RTN_THROW;
+ else {
+ res = strtoul(arg, &end, 0);
+ if (!end || end == arg || *end || res > 255)
+ return -1;
+ }
+ *id = res;
+ return 0;
+}
+
+int get_rt_realms(uint32_t *realms, char *arg)
+{
+ uint32_t realm = 0;
+ char *p = strchr(arg, '/');
+
+ *realms = 0;
+ if (p) {
+ *p = 0;
+ if (rtnl_rtrealm_a2n(realms, arg)) {
+ *p = '/';
+ return -1;
+ }
+ *realms <<= 16;
+ *p = '/';
+ arg = p+1;
+ }
+ if (*arg && rtnl_rtrealm_a2n(&realm, arg))
+ return -1;
+ *realms |= realm;
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/libiproute/rtm_map.h b/i/pc104/initrd/conf/busybox/networking/libiproute/rtm_map.h
new file mode 100644
index 0000000..cbbcc21
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/libiproute/rtm_map.h
@@ -0,0 +1,11 @@
+/* vi: set sw=4 ts=4: */
+#ifndef __RTM_MAP_H__
+#define __RTM_MAP_H__ 1
+
+const char *rtnl_rtntype_n2a(int id, char *buf, int len);
+int rtnl_rtntype_a2n(int *id, char *arg);
+
+int get_rt_realms(uint32_t *realms, char *arg);
+
+
+#endif /* __RTM_MAP_H__ */
diff --git a/i/pc104/initrd/conf/busybox/networking/libiproute/utils.c b/i/pc104/initrd/conf/busybox/networking/libiproute/utils.c
new file mode 100644
index 0000000..591c893
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/libiproute/utils.c
@@ -0,0 +1,330 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * utils.c
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ * Changes:
+ *
+ * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
+ */
+
+#include "libbb.h"
+
+#include <string.h>
+#include <unistd.h>
+
+#include "utils.h"
+#include "inet_common.h"
+
+int get_integer(int *val, char *arg, int base)
+{
+ long res;
+ char *ptr;
+
+ if (!arg || !*arg)
+ return -1;
+ res = strtol(arg, &ptr, base);
+ if (!ptr || ptr == arg || *ptr || res > INT_MAX || res < INT_MIN)
+ return -1;
+ *val = res;
+ return 0;
+}
+
+int get_unsigned(unsigned *val, char *arg, int base)
+{
+ unsigned long res;
+ char *ptr;
+
+ if (!arg || !*arg)
+ return -1;
+ res = strtoul(arg, &ptr, base);
+ if (!ptr || ptr == arg || *ptr || res > UINT_MAX)
+ return -1;
+ *val = res;
+ return 0;
+}
+
+int get_u32(uint32_t * val, char *arg, int base)
+{
+ unsigned long res;
+ char *ptr;
+
+ if (!arg || !*arg)
+ return -1;
+ res = strtoul(arg, &ptr, base);
+ if (!ptr || ptr == arg || *ptr || res > 0xFFFFFFFFUL)
+ return -1;
+ *val = res;
+ return 0;
+}
+
+int get_u16(uint16_t * val, char *arg, int base)
+{
+ unsigned long res;
+ char *ptr;
+
+ if (!arg || !*arg)
+ return -1;
+ res = strtoul(arg, &ptr, base);
+ if (!ptr || ptr == arg || *ptr || res > 0xFFFF)
+ return -1;
+ *val = res;
+ return 0;
+}
+
+int get_u8(uint8_t * val, char *arg, int base)
+{
+ unsigned long res;
+ char *ptr;
+
+ if (!arg || !*arg)
+ return -1;
+ res = strtoul(arg, &ptr, base);
+ if (!ptr || ptr == arg || *ptr || res > 0xFF)
+ return -1;
+ *val = res;
+ return 0;
+}
+
+int get_s16(int16_t * val, char *arg, int base)
+{
+ long res;
+ char *ptr;
+
+ if (!arg || !*arg)
+ return -1;
+ res = strtol(arg, &ptr, base);
+ if (!ptr || ptr == arg || *ptr || res > 0x7FFF || res < -0x8000)
+ return -1;
+ *val = res;
+ return 0;
+}
+
+int get_s8(int8_t * val, char *arg, int base)
+{
+ long res;
+ char *ptr;
+
+ if (!arg || !*arg)
+ return -1;
+ res = strtol(arg, &ptr, base);
+ if (!ptr || ptr == arg || *ptr || res > 0x7F || res < -0x80)
+ return -1;
+ *val = res;
+ return 0;
+}
+
+int get_addr_1(inet_prefix * addr, char *name, int family)
+{
+ char *cp;
+ unsigned char *ap = (unsigned char *) addr->data;
+ int i;
+
+ memset(addr, 0, sizeof(*addr));
+
+ if (strcmp(name, bb_str_default) == 0 ||
+ strcmp(name, "all") == 0 || strcmp(name, "any") == 0) {
+ addr->family = family;
+ addr->bytelen = (family == AF_INET6 ? 16 : 4);
+ addr->bitlen = -1;
+ return 0;
+ }
+
+ if (strchr(name, ':')) {
+ addr->family = AF_INET6;
+ if (family != AF_UNSPEC && family != AF_INET6)
+ return -1;
+ if (inet_pton(AF_INET6, name, addr->data) <= 0)
+ return -1;
+ addr->bytelen = 16;
+ addr->bitlen = -1;
+ return 0;
+ }
+
+ addr->family = AF_INET;
+ if (family != AF_UNSPEC && family != AF_INET)
+ return -1;
+ addr->bytelen = 4;
+ addr->bitlen = -1;
+ for (cp = name, i = 0; *cp; cp++) {
+ if (*cp <= '9' && *cp >= '0') {
+ ap[i] = 10 * ap[i] + (*cp - '0');
+ continue;
+ }
+ if (*cp == '.' && ++i <= 3)
+ continue;
+ return -1;
+ }
+ return 0;
+}
+
+int get_prefix_1(inet_prefix * dst, char *arg, int family)
+{
+ int err;
+ int plen;
+ char *slash;
+
+ memset(dst, 0, sizeof(*dst));
+
+ if (strcmp(arg, bb_str_default) == 0 || strcmp(arg, "any") == 0) {
+ dst->family = family;
+ dst->bytelen = 0;
+ dst->bitlen = 0;
+ return 0;
+ }
+
+ slash = strchr(arg, '/');
+ if (slash)
+ *slash = '\0';
+ err = get_addr_1(dst, arg, family);
+ if (err == 0) {
+ switch (dst->family) {
+ case AF_INET6:
+ dst->bitlen = 128;
+ break;
+ default:
+ case AF_INET:
+ dst->bitlen = 32;
+ }
+ if (slash) {
+ if (get_integer(&plen, slash + 1, 0) || plen > dst->bitlen) {
+ err = -1;
+ goto done;
+ }
+ dst->bitlen = plen;
+ }
+ }
+ done:
+ if (slash)
+ *slash = '/';
+ return err;
+}
+
+int get_addr(inet_prefix * dst, char *arg, int family)
+{
+ if (family == AF_PACKET) {
+ bb_error_msg_and_die("\"%s\" may be inet address, but it is not allowed in this context", arg);
+ }
+ if (get_addr_1(dst, arg, family)) {
+ bb_error_msg_and_die("an inet address is expected rather than \"%s\"", arg);
+ }
+ return 0;
+}
+
+int get_prefix(inet_prefix * dst, char *arg, int family)
+{
+ if (family == AF_PACKET) {
+ bb_error_msg_and_die("\"%s\" may be inet address, but it is not allowed in this context", arg);
+ }
+ if (get_prefix_1(dst, arg, family)) {
+ bb_error_msg_and_die("an inet address is expected rather than \"%s\"", arg);
+ }
+ return 0;
+}
+
+uint32_t get_addr32(char *name)
+{
+ inet_prefix addr;
+
+ if (get_addr_1(&addr, name, AF_INET)) {
+ bb_error_msg_and_die("an IP address is expected rather than \"%s\"", name);
+ }
+ return addr.data[0];
+}
+
+void incomplete_command(void)
+{
+ bb_error_msg_and_die("command line is not complete, try option \"help\"");
+}
+
+void invarg(const char *arg, const char *opt)
+{
+ bb_error_msg_and_die(bb_msg_invalid_arg, arg, opt);
+}
+
+void duparg(const char *key, const char *arg)
+{
+ bb_error_msg_and_die("duplicate \"%s\": \"%s\" is the second value", key, arg);
+}
+
+void duparg2(const char *key, const char *arg)
+{
+ bb_error_msg_and_die("either \"%s\" is duplicate, or \"%s\" is garbage", key, arg);
+}
+
+int matches(const char *cmd, const char *pattern)
+{
+ int len = strlen(cmd);
+
+ return strncmp(pattern, cmd, len);
+}
+
+int inet_addr_match(inet_prefix * a, inet_prefix * b, int bits)
+{
+ uint32_t *a1 = a->data;
+ uint32_t *a2 = b->data;
+ int words = bits >> 0x05;
+
+ bits &= 0x1f;
+
+ if (words)
+ if (memcmp(a1, a2, words << 2))
+ return -1;
+
+ if (bits) {
+ uint32_t w1, w2;
+ uint32_t mask;
+
+ w1 = a1[words];
+ w2 = a2[words];
+
+ mask = htonl((0xffffffff) << (0x20 - bits));
+
+ if ((w1 ^ w2) & mask)
+ return 1;
+ }
+
+ return 0;
+}
+
+const char *rt_addr_n2a(int af, int ATTRIBUTE_UNUSED len,
+ void *addr, char *buf, int buflen)
+{
+ switch (af) {
+ case AF_INET:
+ case AF_INET6:
+ return inet_ntop(af, addr, buf, buflen);
+ default:
+ return "???";
+ }
+}
+
+
+const char *format_host(int af, int len, void *addr, char *buf, int buflen)
+{
+#ifdef RESOLVE_HOSTNAMES
+ if (resolve_hosts) {
+ struct hostent *h_ent;
+
+ if (len <= 0) {
+ switch (af) {
+ case AF_INET:
+ len = 4;
+ break;
+ case AF_INET6:
+ len = 16;
+ break;
+ default:;
+ }
+ }
+ if (len > 0 && (h_ent = gethostbyaddr(addr, len, af)) != NULL) {
+ snprintf(buf, buflen - 1, "%s", h_ent->h_name);
+ return buf;
+ }
+ }
+#endif
+ return rt_addr_n2a(af, len, addr, buf, buflen);
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/libiproute/utils.h b/i/pc104/initrd/conf/busybox/networking/libiproute/utils.h
new file mode 100644
index 0000000..556541f
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/libiproute/utils.h
@@ -0,0 +1,91 @@
+/* vi: set sw=4 ts=4: */
+#ifndef __UTILS_H__
+#define __UTILS_H__ 1
+
+#include "libbb.h"
+
+#include "libnetlink.h"
+#include "ll_map.h"
+#include "rtm_map.h"
+
+extern int preferred_family;
+extern int show_stats;
+extern int show_details;
+extern int show_raw;
+extern int resolve_hosts;
+extern int oneline;
+//FIXME! Appears in two .h files!
+extern const char * _SL_;
+
+#ifndef IPPROTO_ESP
+#define IPPROTO_ESP 50
+#endif
+#ifndef IPPROTO_AH
+#define IPPROTO_AH 51
+#endif
+
+#define SPRINT_BSIZE 64
+#define SPRINT_BUF(x) char x[SPRINT_BSIZE]
+
+extern void incomplete_command(void) ATTRIBUTE_NORETURN;
+
+#define NEXT_ARG() do { argv++; if (--argc <= 0) incomplete_command(); } while (0)
+
+typedef struct
+{
+ uint8_t family;
+ uint8_t bytelen;
+ int16_t bitlen;
+ uint32_t data[4];
+} inet_prefix;
+
+#define DN_MAXADDL 20
+#ifndef AF_DECnet
+#define AF_DECnet 12
+#endif
+
+struct dn_naddr {
+ unsigned short a_len;
+ unsigned char a_addr[DN_MAXADDL];
+};
+
+#define IPX_NODE_LEN 6
+
+struct ipx_addr {
+ uint32_t ipx_net;
+ uint8_t ipx_node[IPX_NODE_LEN];
+};
+
+extern uint32_t get_addr32(char *name);
+extern int get_addr_1(inet_prefix *dst, char *arg, int family);
+extern int get_prefix_1(inet_prefix *dst, char *arg, int family);
+extern int get_addr(inet_prefix *dst, char *arg, int family);
+extern int get_prefix(inet_prefix *dst, char *arg, int family);
+
+extern int get_integer(int *val, char *arg, int base);
+extern int get_unsigned(unsigned *val, char *arg, int base);
+#define get_byte get_u8
+#define get_ushort get_u16
+#define get_short get_s16
+extern int get_u32(uint32_t *val, char *arg, int base);
+extern int get_u16(uint16_t *val, char *arg, int base);
+extern int get_s16(int16_t *val, char *arg, int base);
+extern int get_u8(uint8_t *val, char *arg, int base);
+extern int get_s8(int8_t *val, char *arg, int base);
+
+extern const char *format_host(int af, int len, void *addr, char *buf, int buflen);
+extern const char *rt_addr_n2a(int af, int len, void *addr, char *buf, int buflen);
+
+void invarg(const char *, const char *) ATTRIBUTE_NORETURN;
+void duparg(const char *, const char *) ATTRIBUTE_NORETURN;
+void duparg2(const char *, const char *) ATTRIBUTE_NORETURN;
+int matches(const char *arg, const char *pattern);
+extern int inet_addr_match(inet_prefix *a, inet_prefix *b, int bits);
+
+const char *dnet_ntop(int af, const void *addr, char *str, size_t len);
+int dnet_pton(int af, const char *src, void *addr);
+
+const char *ipx_ntop(int af, const void *addr, char *str, size_t len);
+int ipx_pton(int af, const char *src, void *addr);
+
+#endif /* __UTILS_H__ */
diff --git a/i/pc104/initrd/conf/busybox/networking/nameif.c b/i/pc104/initrd/conf/busybox/networking/nameif.c
new file mode 100644
index 0000000..ec77512
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/nameif.c
@@ -0,0 +1,171 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * nameif.c - Naming Interfaces based on MAC address for busybox.
+ *
+ * Written 2000 by Andi Kleen.
+ * Busybox port 2002 by Nick Fedchik <nick@fedchik.org.ua>
+ * Glenn McGrath <bug1@iinet.net.au>
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ */
+
+#include "busybox.h"
+#include <syslog.h>
+#include <net/if.h>
+#include <netinet/ether.h>
+
+
+/* Older versions of net/if.h do not appear to define IF_NAMESIZE. */
+#ifndef IF_NAMESIZE
+# ifdef IFNAMSIZ
+# define IF_NAMESIZE IFNAMSIZ
+# else
+# define IF_NAMESIZE 16
+# endif
+#endif
+
+/* take from linux/sockios.h */
+#define SIOCSIFNAME 0x8923 /* set interface name */
+
+/* Octets in one Ethernet addr, from <linux/if_ether.h> */
+#define ETH_ALEN 6
+
+#ifndef ifr_newname
+#define ifr_newname ifr_ifru.ifru_slave
+#endif
+
+typedef struct mactable_s {
+ struct mactable_s *next;
+ struct mactable_s *prev;
+ char *ifname;
+ struct ether_addr *mac;
+} mactable_t;
+
+/* Check ascii str_macaddr, convert and copy to *mac */
+static struct ether_addr *cc_macaddr(const char *str_macaddr)
+{
+ struct ether_addr *lmac, *mac;
+
+ lmac = ether_aton(str_macaddr);
+ if (lmac == NULL)
+ bb_error_msg_and_die("cannot parse MAC %s", str_macaddr);
+ mac = xmalloc(ETH_ALEN);
+ memcpy(mac, lmac, ETH_ALEN);
+
+ return mac;
+}
+
+int nameif_main(int argc, char **argv);
+int nameif_main(int argc, char **argv)
+{
+ mactable_t *clist = NULL;
+ FILE *ifh;
+ const char *fname = "/etc/mactab";
+ char *line;
+ int ctl_sk;
+ int if_index = 1;
+ mactable_t *ch;
+
+ if (1 & getopt32(argc, argv, "sc:", &fname)) {
+ openlog(applet_name, 0, LOG_LOCAL0);
+ logmode = LOGMODE_SYSLOG;
+ }
+
+ if ((argc - optind) & 1)
+ bb_show_usage();
+
+ if (optind < argc) {
+ char **a = argv + optind;
+
+ while (*a) {
+ if (strlen(*a) > IF_NAMESIZE)
+ bb_error_msg_and_die("interface name '%s' "
+ "too long", *a);
+ ch = xzalloc(sizeof(mactable_t));
+ ch->ifname = xstrdup(*a++);
+ ch->mac = cc_macaddr(*a++);
+ if (clist)
+ clist->prev = ch;
+ ch->next = clist;
+ clist = ch;
+ }
+ } else {
+ ifh = xfopen(fname, "r");
+
+ while ((line = xmalloc_fgets(ifh)) != NULL) {
+ char *line_ptr;
+ size_t name_length;
+
+ line_ptr = line + strspn(line, " \t");
+ if ((line_ptr[0] == '#') || (line_ptr[0] == '\n')) {
+ free(line);
+ continue;
+ }
+ name_length = strcspn(line_ptr, " \t");
+ ch = xzalloc(sizeof(mactable_t));
+ ch->ifname = xstrndup(line_ptr, name_length);
+ if (name_length > IF_NAMESIZE)
+ bb_error_msg_and_die("interface name '%s' "
+ "too long", ch->ifname);
+ line_ptr += name_length;
+ line_ptr += strspn(line_ptr, " \t");
+ name_length = strspn(line_ptr, "0123456789ABCDEFabcdef:");
+ line_ptr[name_length] = '\0';
+ ch->mac = cc_macaddr(line_ptr);
+ if (clist)
+ clist->prev = ch;
+ ch->next = clist;
+ clist = ch;
+ free(line);
+ }
+ fclose(ifh);
+ }
+
+ ctl_sk = xsocket(PF_INET, SOCK_DGRAM, 0);
+
+ while (clist) {
+ struct ifreq ifr;
+
+ memset(&ifr, 0, sizeof(struct ifreq));
+ if_index++;
+ ifr.ifr_ifindex = if_index;
+
+ /* Get ifname by index or die */
+ if (ioctl(ctl_sk, SIOCGIFNAME, &ifr))
+ break;
+
+ /* Has this device hwaddr? */
+ if (ioctl(ctl_sk, SIOCGIFHWADDR, &ifr))
+ continue;
+
+ /* Search for mac like in ifr.ifr_hwaddr.sa_data */
+ for (ch = clist; ch; ch = ch->next)
+ if (!memcmp(ch->mac, ifr.ifr_hwaddr.sa_data, ETH_ALEN))
+ break;
+
+ /* Nothing found for current ifr.ifr_hwaddr.sa_data */
+ if (ch == NULL)
+ continue;
+
+ strcpy(ifr.ifr_newname, ch->ifname);
+ if (ioctl(ctl_sk, SIOCSIFNAME, &ifr) < 0)
+ bb_perror_msg_and_die("cannot change ifname %s to %s",
+ ifr.ifr_name, ch->ifname);
+
+ /* Remove list entry of renamed interface */
+ if (ch->prev != NULL) {
+ (ch->prev)->next = ch->next;
+ } else {
+ clist = ch->next;
+ }
+ if (ch->next != NULL)
+ (ch->next)->prev = ch->prev;
+ if (ENABLE_FEATURE_CLEAN_UP) {
+ free(ch->ifname);
+ free(ch->mac);
+ free(ch);
+ }
+ }
+
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/nc.c b/i/pc104/initrd/conf/busybox/networking/nc.c
new file mode 100644
index 0000000..1bdecaf
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/nc.c
@@ -0,0 +1,197 @@
+/* vi: set sw=4 ts=4: */
+/* nc: mini-netcat - built from the ground up for LRP
+ *
+ * Copyright (C) 1998, 1999 Charles P. Wright
+ * Copyright (C) 1998 Dave Cinege
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "busybox.h"
+
+/* Lots of small differences in features
+ * when compared to "standard" nc
+ */
+
+static void timeout(int signum)
+{
+ bb_error_msg_and_die("timed out");
+}
+
+int nc_main(int argc, char **argv);
+int nc_main(int argc, char **argv)
+{
+ /* sfd sits _here_ only because of "repeat" option (-l -l). */
+ int sfd = sfd; /* for gcc */
+ int cfd = 0;
+ unsigned lport = 0;
+ SKIP_NC_SERVER(const) unsigned do_listen = 0;
+ SKIP_NC_EXTRA (const) unsigned wsecs = 0;
+ SKIP_NC_EXTRA (const) unsigned delay = 0;
+ SKIP_NC_EXTRA (const int execparam = 0;)
+ USE_NC_EXTRA (char **execparam = NULL;)
+ len_and_sockaddr *lsa;
+ fd_set readfds, testfds;
+ int opt; /* must be signed (getopt returns -1) */
+
+ if (ENABLE_NC_SERVER || ENABLE_NC_EXTRA) {
+ /* getopt32 is _almost_ usable:
+ ** it cannot handle "... -e prog -prog-opt" */
+ while ((opt = getopt(argc, argv,
+ "" USE_NC_SERVER("lp:") USE_NC_EXTRA("w:i:f:e:") )) > 0
+ ) {
+ if (ENABLE_NC_SERVER && opt=='l') USE_NC_SERVER(do_listen++);
+ else if (ENABLE_NC_SERVER && opt=='p') {
+ USE_NC_SERVER(lport = bb_lookup_port(optarg, "tcp", 0));
+ }
+ else if (ENABLE_NC_EXTRA && opt=='w') USE_NC_EXTRA( wsecs = xatou(optarg));
+ else if (ENABLE_NC_EXTRA && opt=='i') USE_NC_EXTRA( delay = xatou(optarg));
+ else if (ENABLE_NC_EXTRA && opt=='f') USE_NC_EXTRA( cfd = xopen(optarg, O_RDWR));
+ else if (ENABLE_NC_EXTRA && opt=='e' && optind<=argc) {
+ /* We cannot just 'break'. We should let getopt finish.
+ ** Or else we won't be able to find where
+ ** 'host' and 'port' params are
+ ** (think "nc -w 60 host port -e prog"). */
+ USE_NC_EXTRA(
+ char **p;
+ // +2: one for progname (optarg) and one for NULL
+ execparam = xzalloc(sizeof(char*) * (argc - optind + 2));
+ p = execparam;
+ *p++ = optarg;
+ while (optind < argc) {
+ *p++ = argv[optind++];
+ }
+ )
+ /* optind points to argv[arvc] (NULL) now.
+ ** FIXME: we assume that getopt will not count options
+ ** possibly present on "-e prog args" and will not
+ ** include them into final value of optind
+ ** which is to be used ... */
+ } else bb_show_usage();
+ }
+ argv += optind; /* ... here! */
+ argc -= optind;
+ // -l and -f don't mix
+ if (do_listen && cfd) bb_show_usage();
+ // Listen or file modes need zero arguments, client mode needs 2
+ if (do_listen || cfd) {
+ if (argc) bb_show_usage();
+ } else {
+ if (!argc || argc > 2) bb_show_usage();
+ }
+ } else {
+ if (argc != 3) bb_show_usage();
+ argc--;
+ argv++;
+ }
+
+ if (wsecs) {
+ signal(SIGALRM, timeout);
+ alarm(wsecs);
+ }
+
+ if (!cfd) {
+ if (do_listen) {
+ /* create_and_bind_stream_or_die(NULL, lport)
+ * would've work wonderfully, but we need
+ * to know lsa */
+ sfd = xsocket_stream(&lsa);
+ if (lport)
+ set_nport(lsa, htons(lport));
+ setsockopt_reuseaddr(sfd);
+ xbind(sfd, &lsa->sa, lsa->len);
+ xlisten(sfd, do_listen); /* can be > 1 */
+ /* If we didn't specify a port number,
+ * query and print it after listen() */
+ if (!lport) {
+ socklen_t addrlen = lsa->len;
+ getsockname(sfd, &lsa->sa, &addrlen);
+ lport = get_nport(lsa);
+ fdprintf(2, "%d\n", ntohs(lport));
+ }
+ fcntl(sfd, F_SETFD, FD_CLOEXEC);
+ accept_again:
+ cfd = accept(sfd, NULL, 0);
+ if (cfd < 0)
+ bb_perror_msg_and_die("accept");
+ if (!execparam)
+ close(sfd);
+ } else {
+ cfd = create_and_connect_stream_or_die(argv[0],
+ argv[1] ? bb_lookup_port(argv[1], "tcp", 0) : 0);
+ }
+ }
+
+ if (wsecs) {
+ alarm(0);
+ signal(SIGALRM, SIG_DFL);
+ }
+
+ /* -e given? */
+ if (execparam) {
+ signal(SIGCHLD, SIG_IGN);
+ // With more than one -l, repeatedly act as server.
+ if (do_listen > 1 && vfork()) {
+ /* parent */
+ // This is a bit weird as cleanup goes, since we wind up with no
+ // stdin/stdout/stderr. But it's small and shouldn't hurt anything.
+ // We check for cfd == 0 above.
+ logmode = LOGMODE_NONE;
+ close(0);
+ close(1);
+ close(2);
+ goto accept_again;
+ }
+ /* child (or main thread if no multiple -l) */
+ if (cfd) {
+ dup2(cfd, 0);
+ close(cfd);
+ }
+ dup2(0, 1);
+ dup2(0, 2);
+ USE_NC_EXTRA(BB_EXECVP(execparam[0], execparam);)
+ /* Don't print stuff or it will go over the wire.... */
+ _exit(127);
+ }
+
+ // Select loop copying stdin to cfd, and cfd to stdout.
+
+ FD_ZERO(&readfds);
+ FD_SET(cfd, &readfds);
+ FD_SET(STDIN_FILENO, &readfds);
+
+ for (;;) {
+ int fd;
+ int ofd;
+ int nread;
+
+ testfds = readfds;
+
+ if (select(FD_SETSIZE, &testfds, NULL, NULL, NULL) < 0)
+ bb_perror_msg_and_die("select");
+
+ for (fd = 0; fd < FD_SETSIZE; fd++) {
+ if (FD_ISSET(fd, &testfds)) {
+ nread = safe_read(fd, bb_common_bufsiz1,
+ sizeof(bb_common_bufsiz1));
+
+ if (fd == cfd) {
+ if (nread < 1)
+ exit(0);
+ ofd = STDOUT_FILENO;
+ } else {
+ if (nread<1) {
+ // Close outgoing half-connection so they get EOF, but
+ // leave incoming alone so we can see response.
+ shutdown(cfd, 1);
+ FD_CLR(STDIN_FILENO, &readfds);
+ }
+ ofd = cfd;
+ }
+
+ xwrite(ofd, bb_common_bufsiz1, nread);
+ if (delay > 0) sleep(delay);
+ }
+ }
+ }
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/netstat.c b/i/pc104/initrd/conf/busybox/networking/netstat.c
new file mode 100644
index 0000000..d89d3aa
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/netstat.c
@@ -0,0 +1,596 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini netstat implementation(s) for busybox
+ * based in part on the netstat implementation from net-tools.
+ *
+ * Copyright (C) 2002 by Bart Visscher <magick@linux-fan.com>
+ *
+ * 2002-04-20
+ * IPV6 support added by Bart Visscher <magick@linux-fan.com>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "busybox.h"
+#include "inet_common.h"
+
+#define NETSTAT_CONNECTED 0x01
+#define NETSTAT_LISTENING 0x02
+#define NETSTAT_NUMERIC 0x04
+/* Must match getopt32 option string */
+#define NETSTAT_TCP 0x10
+#define NETSTAT_UDP 0x20
+#define NETSTAT_RAW 0x40
+#define NETSTAT_UNIX 0x80
+#define NETSTAT_ALLPROTO (NETSTAT_TCP|NETSTAT_UDP|NETSTAT_RAW|NETSTAT_UNIX)
+
+static int flags = NETSTAT_CONNECTED | NETSTAT_ALLPROTO;
+
+enum {
+ TCP_ESTABLISHED = 1,
+ TCP_SYN_SENT,
+ TCP_SYN_RECV,
+ TCP_FIN_WAIT1,
+ TCP_FIN_WAIT2,
+ TCP_TIME_WAIT,
+ TCP_CLOSE,
+ TCP_CLOSE_WAIT,
+ TCP_LAST_ACK,
+ TCP_LISTEN,
+ TCP_CLOSING /* now a valid state */
+};
+
+static const char * const tcp_state[] =
+{
+ "",
+ "ESTABLISHED",
+ "SYN_SENT",
+ "SYN_RECV",
+ "FIN_WAIT1",
+ "FIN_WAIT2",
+ "TIME_WAIT",
+ "CLOSE",
+ "CLOSE_WAIT",
+ "LAST_ACK",
+ "LISTEN",
+ "CLOSING"
+};
+
+typedef enum {
+ SS_FREE = 0, /* not allocated */
+ SS_UNCONNECTED, /* unconnected to any socket */
+ SS_CONNECTING, /* in process of connecting */
+ SS_CONNECTED, /* connected to socket */
+ SS_DISCONNECTING /* in process of disconnecting */
+} socket_state;
+
+#define SO_ACCEPTCON (1<<16) /* performed a listen */
+#define SO_WAITDATA (1<<17) /* wait data to read */
+#define SO_NOSPACE (1<<18) /* no space to write */
+
+static const char *get_sname(int port, const char *proto, int num)
+{
+ /* hummm, we return static buffer here!! */
+ const char *str = itoa(ntohs(port));
+ if (!num) {
+ struct servent *se = getservbyport(port, proto);
+ if (se)
+ str = se->s_name;
+ }
+ if (!port) {
+ str = "*";
+ }
+ return str;
+}
+
+static void snprint_ip_port(char *ip_port, int size, struct sockaddr *addr, int port, const char *proto, int numeric)
+{
+ const char *port_name;
+
+#if ENABLE_FEATURE_IPV6
+ if (addr->sa_family == AF_INET6) {
+ INET6_rresolve(ip_port, size, (struct sockaddr_in6 *)addr,
+ (numeric & NETSTAT_NUMERIC) ? 0x0fff : 0);
+ } else
+#endif
+ {
+ INET_rresolve(ip_port, size, (struct sockaddr_in *)addr,
+ 0x4000 | ((numeric & NETSTAT_NUMERIC) ? 0x0fff : 0),
+ 0xffffffff);
+ }
+ port_name = get_sname(htons(port), proto, numeric);
+ if ((strlen(ip_port) + strlen(port_name)) > 22)
+ ip_port[22 - strlen(port_name)] = '\0';
+ ip_port += strlen(ip_port);
+ strcat(ip_port, ":");
+ strcat(ip_port, port_name);
+}
+
+static void tcp_do_one(int lnr, const char *line)
+{
+ char local_addr[64], rem_addr[64];
+ const char *state_str;
+ char more[512];
+ int num, local_port, rem_port, d, state, timer_run, uid, timeout;
+#if ENABLE_FEATURE_IPV6
+ struct sockaddr_in6 localaddr, remaddr;
+ char addr6[INET6_ADDRSTRLEN];
+ struct in6_addr in6;
+#else
+ struct sockaddr_in localaddr, remaddr;
+#endif
+ unsigned long rxq, txq, time_len, retr, inode;
+
+ if (lnr == 0)
+ return;
+
+ more[0] = '\0';
+ num = sscanf(line,
+ "%d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %X %lX:%lX %X:%lX %lX %d %d %ld %512s\n",
+ &d, local_addr, &local_port,
+ rem_addr, &rem_port, &state,
+ &txq, &rxq, &timer_run, &time_len, &retr, &uid, &timeout, &inode, more);
+
+ if (strlen(local_addr) > 8) {
+#if ENABLE_FEATURE_IPV6
+ sscanf(local_addr, "%08X%08X%08X%08X",
+ &in6.s6_addr32[0], &in6.s6_addr32[1],
+ &in6.s6_addr32[2], &in6.s6_addr32[3]);
+ inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6));
+ inet_pton(AF_INET6, addr6, (struct sockaddr *) &localaddr.sin6_addr);
+ sscanf(rem_addr, "%08X%08X%08X%08X",
+ &in6.s6_addr32[0], &in6.s6_addr32[1],
+ &in6.s6_addr32[2], &in6.s6_addr32[3]);
+ inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6));
+ inet_pton(AF_INET6, addr6, (struct sockaddr *) &remaddr.sin6_addr);
+ localaddr.sin6_family = AF_INET6;
+ remaddr.sin6_family = AF_INET6;
+#endif
+ } else {
+ sscanf(local_addr, "%X",
+ &((struct sockaddr_in *) &localaddr)->sin_addr.s_addr);
+ sscanf(rem_addr, "%X",
+ &((struct sockaddr_in *) &remaddr)->sin_addr.s_addr);
+ ((struct sockaddr *) &localaddr)->sa_family = AF_INET;
+ ((struct sockaddr *) &remaddr)->sa_family = AF_INET;
+ }
+
+ if (num < 10) {
+ bb_error_msg("warning, got bogus tcp line");
+ return;
+ }
+ state_str = tcp_state[state];
+ if ((rem_port && (flags & NETSTAT_CONNECTED))
+ || (!rem_port && (flags & NETSTAT_LISTENING))
+ ) {
+ snprint_ip_port(local_addr, sizeof(local_addr),
+ (struct sockaddr *) &localaddr, local_port,
+ "tcp", flags & NETSTAT_NUMERIC);
+
+ snprint_ip_port(rem_addr, sizeof(rem_addr),
+ (struct sockaddr *) &remaddr, rem_port,
+ "tcp", flags & NETSTAT_NUMERIC);
+
+ printf("tcp %6ld %6ld %-23s %-23s %-12s\n",
+ rxq, txq, local_addr, rem_addr, state_str);
+ }
+}
+
+static void udp_do_one(int lnr, const char *line)
+{
+ char local_addr[64], rem_addr[64];
+ const char *state_str;
+ char more[512];
+ int num, local_port, rem_port, d, state, timer_run, uid, timeout;
+#if ENABLE_FEATURE_IPV6
+ struct sockaddr_in6 localaddr, remaddr;
+ char addr6[INET6_ADDRSTRLEN];
+ struct in6_addr in6;
+#else
+ struct sockaddr_in localaddr, remaddr;
+#endif
+ unsigned long rxq, txq, time_len, retr, inode;
+
+ if (lnr == 0)
+ return;
+
+ more[0] = '\0';
+ num = sscanf(line,
+ "%d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %X %lX:%lX %X:%lX %lX %d %d %ld %512s\n",
+ &d, local_addr, &local_port,
+ rem_addr, &rem_port, &state,
+ &txq, &rxq, &timer_run, &time_len, &retr, &uid, &timeout, &inode, more);
+
+ if (strlen(local_addr) > 8) {
+#if ENABLE_FEATURE_IPV6
+ /* Demangle what the kernel gives us */
+ sscanf(local_addr, "%08X%08X%08X%08X",
+ &in6.s6_addr32[0], &in6.s6_addr32[1],
+ &in6.s6_addr32[2], &in6.s6_addr32[3]);
+ inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6));
+ inet_pton(AF_INET6, addr6, (struct sockaddr *) &localaddr.sin6_addr);
+ sscanf(rem_addr, "%08X%08X%08X%08X",
+ &in6.s6_addr32[0], &in6.s6_addr32[1],
+ &in6.s6_addr32[2], &in6.s6_addr32[3]);
+ inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6));
+ inet_pton(AF_INET6, addr6, (struct sockaddr *) &remaddr.sin6_addr);
+ localaddr.sin6_family = AF_INET6;
+ remaddr.sin6_family = AF_INET6;
+#endif
+ } else {
+ sscanf(local_addr, "%X",
+ &((struct sockaddr_in *) &localaddr)->sin_addr.s_addr);
+ sscanf(rem_addr, "%X",
+ &((struct sockaddr_in *) &remaddr)->sin_addr.s_addr);
+ ((struct sockaddr *) &localaddr)->sa_family = AF_INET;
+ ((struct sockaddr *) &remaddr)->sa_family = AF_INET;
+ }
+
+ if (num < 10) {
+ bb_error_msg("warning, got bogus udp line");
+ return;
+ }
+ switch (state) {
+ case TCP_ESTABLISHED:
+ state_str = "ESTABLISHED";
+ break;
+
+ case TCP_CLOSE:
+ state_str = "";
+ break;
+
+ default:
+ state_str = "UNKNOWN";
+ break;
+ }
+
+#if ENABLE_FEATURE_IPV6
+# define notnull(A) (((A.sin6_family == AF_INET6) && \
+ ((A.sin6_addr.s6_addr32[0]) || \
+ (A.sin6_addr.s6_addr32[1]) || \
+ (A.sin6_addr.s6_addr32[2]) || \
+ (A.sin6_addr.s6_addr32[3]))) || \
+ ((A.sin6_family == AF_INET) && \
+ ((struct sockaddr_in *) &A)->sin_addr.s_addr))
+#else
+# define notnull(A) (A.sin_addr.s_addr)
+#endif
+ if ((notnull(remaddr) && (flags & NETSTAT_CONNECTED))
+ || (!notnull(remaddr) && (flags & NETSTAT_LISTENING))
+ ) {
+ snprint_ip_port(local_addr, sizeof(local_addr),
+ (struct sockaddr *) &localaddr, local_port,
+ "udp", flags & NETSTAT_NUMERIC);
+
+ snprint_ip_port(rem_addr, sizeof(rem_addr),
+ (struct sockaddr *) &remaddr, rem_port,
+ "udp", flags & NETSTAT_NUMERIC);
+
+ printf("udp %6ld %6ld %-23s %-23s %-12s\n",
+ rxq, txq, local_addr, rem_addr, state_str);
+ }
+}
+
+static void raw_do_one(int lnr, const char *line)
+{
+ char local_addr[64], rem_addr[64];
+ char *state_str, more[512];
+ int num, local_port, rem_port, d, state, timer_run, uid, timeout;
+#if ENABLE_FEATURE_IPV6
+ struct sockaddr_in6 localaddr, remaddr;
+ char addr6[INET6_ADDRSTRLEN];
+ struct in6_addr in6;
+#else
+ struct sockaddr_in localaddr, remaddr;
+#endif
+ unsigned long rxq, txq, time_len, retr, inode;
+
+ if (lnr == 0)
+ return;
+
+ more[0] = '\0';
+ num = sscanf(line,
+ "%d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %X %lX:%lX %X:%lX %lX %d %d %ld %512s\n",
+ &d, local_addr, &local_port,
+ rem_addr, &rem_port, &state,
+ &txq, &rxq, &timer_run, &time_len, &retr, &uid, &timeout, &inode, more);
+
+ if (strlen(local_addr) > 8) {
+#if ENABLE_FEATURE_IPV6
+ sscanf(local_addr, "%08X%08X%08X%08X",
+ &in6.s6_addr32[0], &in6.s6_addr32[1],
+ &in6.s6_addr32[2], &in6.s6_addr32[3]);
+ inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6));
+ inet_pton(AF_INET6, addr6, (struct sockaddr *) &localaddr.sin6_addr);
+ sscanf(rem_addr, "%08X%08X%08X%08X",
+ &in6.s6_addr32[0], &in6.s6_addr32[1],
+ &in6.s6_addr32[2], &in6.s6_addr32[3]);
+ inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6));
+ inet_pton(AF_INET6, addr6, (struct sockaddr *) &remaddr.sin6_addr);
+ localaddr.sin6_family = AF_INET6;
+ remaddr.sin6_family = AF_INET6;
+#endif
+ } else {
+ sscanf(local_addr, "%X",
+ &((struct sockaddr_in *) &localaddr)->sin_addr.s_addr);
+ sscanf(rem_addr, "%X",
+ &((struct sockaddr_in *) &remaddr)->sin_addr.s_addr);
+ ((struct sockaddr *) &localaddr)->sa_family = AF_INET;
+ ((struct sockaddr *) &remaddr)->sa_family = AF_INET;
+ }
+
+ if (num < 10) {
+ bb_error_msg("warning, got bogus raw line");
+ return;
+ }
+ state_str = itoa(state);
+
+#if ENABLE_FEATURE_IPV6
+# define notnull(A) (((A.sin6_family == AF_INET6) && \
+ ((A.sin6_addr.s6_addr32[0]) || \
+ (A.sin6_addr.s6_addr32[1]) || \
+ (A.sin6_addr.s6_addr32[2]) || \
+ (A.sin6_addr.s6_addr32[3]))) || \
+ ((A.sin6_family == AF_INET) && \
+ ((struct sockaddr_in *) &A)->sin_addr.s_addr))
+#else
+# define notnull(A) (A.sin_addr.s_addr)
+#endif
+ if ((notnull(remaddr) && (flags & NETSTAT_CONNECTED))
+ || (!notnull(remaddr) && (flags & NETSTAT_LISTENING))
+ ) {
+ snprint_ip_port(local_addr, sizeof(local_addr),
+ (struct sockaddr *) &localaddr, local_port,
+ "raw", flags & NETSTAT_NUMERIC);
+
+ snprint_ip_port(rem_addr, sizeof(rem_addr),
+ (struct sockaddr *) &remaddr, rem_port,
+ "raw", flags & NETSTAT_NUMERIC);
+
+ printf("raw %6ld %6ld %-23s %-23s %-12s\n",
+ rxq, txq, local_addr, rem_addr, state_str);
+ }
+}
+
+#define HAS_INODE 1
+
+static void unix_do_one(int nr, const char *line)
+{
+ static int has = 0;
+ char path[PATH_MAX], ss_flags[32];
+ const char *ss_proto, *ss_state, *ss_type;
+ int num, state, type, inode;
+ void *d;
+ unsigned long refcnt, proto, unix_flags;
+
+ if (nr == 0) {
+ if (strstr(line, "Inode"))
+ has |= HAS_INODE;
+ return;
+ }
+ path[0] = '\0';
+ num = sscanf(line, "%p: %lX %lX %lX %X %X %d %s",
+ &d, &refcnt, &proto, &unix_flags, &type, &state, &inode, path);
+ if (num < 6) {
+ bb_error_msg("warning, got bogus unix line");
+ return;
+ }
+ if (!(has & HAS_INODE))
+ snprintf(path,sizeof(path),"%d",inode);
+
+ if ((flags & (NETSTAT_LISTENING|NETSTAT_CONNECTED)) != (NETSTAT_LISTENING|NETSTAT_CONNECTED)) {
+ if ((state == SS_UNCONNECTED) && (unix_flags & SO_ACCEPTCON)) {
+ if (!(flags & NETSTAT_LISTENING))
+ return;
+ } else {
+ if (!(flags & NETSTAT_CONNECTED))
+ return;
+ }
+ }
+
+ switch (proto) {
+ case 0:
+ ss_proto = "unix";
+ break;
+
+ default:
+ ss_proto = "??";
+ }
+
+ switch (type) {
+ case SOCK_STREAM:
+ ss_type = "STREAM";
+ break;
+
+ case SOCK_DGRAM:
+ ss_type = "DGRAM";
+ break;
+
+ case SOCK_RAW:
+ ss_type = "RAW";
+ break;
+
+ case SOCK_RDM:
+ ss_type = "RDM";
+ break;
+
+ case SOCK_SEQPACKET:
+ ss_type = "SEQPACKET";
+ break;
+
+ default:
+ ss_type = "UNKNOWN";
+ }
+
+ switch (state) {
+ case SS_FREE:
+ ss_state = "FREE";
+ break;
+
+ case SS_UNCONNECTED:
+ /*
+ * Unconnected sockets may be listening
+ * for something.
+ */
+ if (unix_flags & SO_ACCEPTCON) {
+ ss_state = "LISTENING";
+ } else {
+ ss_state = "";
+ }
+ break;
+
+ case SS_CONNECTING:
+ ss_state = "CONNECTING";
+ break;
+
+ case SS_CONNECTED:
+ ss_state = "CONNECTED";
+ break;
+
+ case SS_DISCONNECTING:
+ ss_state = "DISCONNECTING";
+ break;
+
+ default:
+ ss_state = "UNKNOWN";
+ }
+
+ strcpy(ss_flags, "[ ");
+ if (unix_flags & SO_ACCEPTCON)
+ strcat(ss_flags, "ACC ");
+ if (unix_flags & SO_WAITDATA)
+ strcat(ss_flags, "W ");
+ if (unix_flags & SO_NOSPACE)
+ strcat(ss_flags, "N ");
+
+ strcat(ss_flags, "]");
+
+ printf("%-5s %-6ld %-11s %-10s %-13s ",
+ ss_proto, refcnt, ss_flags, ss_type, ss_state);
+ if (has & HAS_INODE)
+ printf("%-6d ",inode);
+ else
+ printf("- ");
+ puts(path);
+}
+
+#define _PATH_PROCNET_UDP "/proc/net/udp"
+#define _PATH_PROCNET_UDP6 "/proc/net/udp6"
+#define _PATH_PROCNET_TCP "/proc/net/tcp"
+#define _PATH_PROCNET_TCP6 "/proc/net/tcp6"
+#define _PATH_PROCNET_RAW "/proc/net/raw"
+#define _PATH_PROCNET_RAW6 "/proc/net/raw6"
+#define _PATH_PROCNET_UNIX "/proc/net/unix"
+
+static void do_info(const char *file, const char *name, void (*proc)(int, const char *))
+{
+ int lnr = 0;
+ FILE *procinfo;
+
+ procinfo = fopen(file, "r");
+ if (procinfo == NULL) {
+ if (errno != ENOENT) {
+ bb_perror_msg("%s", file);
+ } else {
+ bb_error_msg("no support for '%s' on this system", name);
+ }
+ return;
+ }
+ do {
+ char *buffer = xmalloc_fgets(procinfo);
+ if (buffer) {
+ (proc)(lnr++, buffer);
+ free(buffer);
+ }
+ } while (!feof(procinfo));
+ fclose(procinfo);
+}
+
+/*
+ * Our main function.
+ */
+
+int netstat_main(int argc, char **argv);
+int netstat_main(int argc, char **argv)
+{
+ enum {
+ OPT_extended = 0x4,
+ OPT_showroute = 0x100,
+ };
+ unsigned opt;
+#if ENABLE_FEATURE_IPV6
+ int inet = 1;
+ int inet6 = 1;
+#else
+ enum { inet = 1, inet6 = 0 };
+#endif
+
+ /* Option string must match NETSTAT_xxx constants */
+ opt = getopt32(argc, argv, "laentuwxr");
+ if (opt & 0x1) { // -l
+ flags &= ~NETSTAT_CONNECTED;
+ flags |= NETSTAT_LISTENING;
+ }
+ if (opt & 0x2) flags |= NETSTAT_LISTENING | NETSTAT_CONNECTED; // -a
+ //if (opt & 0x4) // -e
+ if (opt & 0x8) flags |= NETSTAT_NUMERIC; // -n
+ //if (opt & 0x10) // -t: NETSTAT_TCP
+ //if (opt & 0x20) // -u: NETSTAT_UDP
+ //if (opt & 0x40) // -w: NETSTAT_RAW
+ //if (opt & 0x80) // -x: NETSTAT_UNIX
+ if (opt & OPT_showroute) { // -r
+#if ENABLE_ROUTE
+ bb_displayroutes(flags & NETSTAT_NUMERIC, !(opt & OPT_extended));
+ return 0;
+#else
+ bb_error_msg_and_die("-r (display routing table) is not compiled in");
+#endif
+ }
+
+ opt &= NETSTAT_ALLPROTO;
+ if (opt) {
+ flags &= ~NETSTAT_ALLPROTO;
+ flags |= opt;
+ }
+ if (flags & (NETSTAT_TCP|NETSTAT_UDP|NETSTAT_RAW)) {
+ printf("Active Internet connections "); /* xxx */
+
+ if ((flags & (NETSTAT_LISTENING|NETSTAT_CONNECTED)) == (NETSTAT_LISTENING|NETSTAT_CONNECTED))
+ printf("(servers and established)");
+ else if (flags & NETSTAT_LISTENING)
+ printf("(only servers)");
+ else
+ printf("(w/o servers)");
+ printf("\nProto Recv-Q Send-Q Local Address Foreign Address State\n");
+ }
+ if (inet && flags & NETSTAT_TCP)
+ do_info(_PATH_PROCNET_TCP, "AF INET (tcp)", tcp_do_one);
+#if ENABLE_FEATURE_IPV6
+ if (inet6 && flags & NETSTAT_TCP)
+ do_info(_PATH_PROCNET_TCP6, "AF INET6 (tcp)", tcp_do_one);
+#endif
+ if (inet && flags & NETSTAT_UDP)
+ do_info(_PATH_PROCNET_UDP, "AF INET (udp)", udp_do_one);
+#if ENABLE_FEATURE_IPV6
+ if (inet6 && flags & NETSTAT_UDP)
+ do_info(_PATH_PROCNET_UDP6, "AF INET6 (udp)", udp_do_one);
+#endif
+ if (inet && flags & NETSTAT_RAW)
+ do_info(_PATH_PROCNET_RAW, "AF INET (raw)", raw_do_one);
+#if ENABLE_FEATURE_IPV6
+ if (inet6 && flags & NETSTAT_RAW)
+ do_info(_PATH_PROCNET_RAW6, "AF INET6 (raw)", raw_do_one);
+#endif
+ if (flags & NETSTAT_UNIX) {
+ printf("Active UNIX domain sockets ");
+ if ((flags & (NETSTAT_LISTENING|NETSTAT_CONNECTED)) == (NETSTAT_LISTENING|NETSTAT_CONNECTED))
+ printf("(servers and established)");
+ else if (flags & NETSTAT_LISTENING)
+ printf("(only servers)");
+ else
+ printf("(w/o servers)");
+ printf("\nProto RefCnt Flags Type State I-Node Path\n");
+ do_info(_PATH_PROCNET_UNIX, "AF UNIX", unix_do_one);
+ }
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/nslookup.c b/i/pc104/initrd/conf/busybox/networking/nslookup.c
new file mode 100644
index 0000000..8076aff
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/nslookup.c
@@ -0,0 +1,155 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini nslookup implementation for busybox
+ *
+ * Copyright (C) 1999,2000 by Lineo, inc. and John Beppu
+ * Copyright (C) 1999,2000,2001 by John Beppu <beppu@codepoet.org>
+ *
+ * Correct default name server display and explicit name server option
+ * added by Ben Zeckel <bzeckel@hmc.edu> June 2001
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include <resolv.h>
+#include "busybox.h"
+
+/*
+ * I'm only implementing non-interactive mode;
+ * I totally forgot nslookup even had an interactive mode.
+ */
+
+/* Examples of 'standard' nslookup output
+ * $ nslookup yahoo.com
+ * Server: 128.193.0.10
+ * Address: 128.193.0.10#53
+ *
+ * Non-authoritative answer:
+ * Name: yahoo.com
+ * Address: 216.109.112.135
+ * Name: yahoo.com
+ * Address: 66.94.234.13
+ *
+ * $ nslookup 204.152.191.37
+ * Server: 128.193.4.20
+ * Address: 128.193.4.20#53
+ *
+ * Non-authoritative answer:
+ * 37.191.152.204.in-addr.arpa canonical name = 37.32-27.191.152.204.in-addr.arpa.
+ * 37.32-27.191.152.204.in-addr.arpa name = zeus-pub2.kernel.org.
+ *
+ * Authoritative answers can be found from:
+ * 32-27.191.152.204.in-addr.arpa nameserver = ns1.kernel.org.
+ * 32-27.191.152.204.in-addr.arpa nameserver = ns2.kernel.org.
+ * 32-27.191.152.204.in-addr.arpa nameserver = ns3.kernel.org.
+ * ns1.kernel.org internet address = 140.211.167.34
+ * ns2.kernel.org internet address = 204.152.191.4
+ * ns3.kernel.org internet address = 204.152.191.36
+ */
+
+static int print_host(const char *hostname, const char *header)
+{
+ /* We can't use xhost2sockaddr() - we want to get ALL addresses,
+ * not just one */
+
+ struct addrinfo *result = NULL;
+ int rc;
+ struct addrinfo hint;
+
+ memset(&hint, 0 , sizeof(hint));
+ /* hint.ai_family = AF_UNSPEC; - zero anyway */
+ /* Needed. Or else we will get each address thrice (or more)
+ * for each possible socket type (tcp,udp,raw...): */
+ hint.ai_socktype = SOCK_STREAM;
+ // hint.ai_flags = AI_CANONNAME;
+ rc = getaddrinfo(hostname, NULL /*service*/, &hint, &result);
+
+ if (!rc) {
+ struct addrinfo *cur = result;
+ unsigned cnt = 0;
+
+ printf("%-10s %s\n", header, hostname);
+ // printf("%s\n", cur->ai_canonname); ?
+ while (cur) {
+ char *dotted, *revhost;
+ dotted = xmalloc_sockaddr2dotted_noport(cur->ai_addr, cur->ai_addrlen);
+ revhost = xmalloc_sockaddr2hostonly_noport(cur->ai_addr, cur->ai_addrlen);
+
+ printf("Address %u: %s%c", ++cnt, dotted, revhost ? ' ' : '\n');
+ if (revhost) {
+ puts(revhost);
+ if (ENABLE_FEATURE_CLEAN_UP)
+ free(revhost);
+ }
+ if (ENABLE_FEATURE_CLEAN_UP)
+ free(dotted);
+ cur = cur->ai_next;
+ }
+ } else {
+#if ENABLE_VERBOSE_RESOLUTION_ERRORS
+ bb_error_msg("can't resolve '%s': %s", hostname, gai_strerror(rc));
+#else
+ bb_error_msg("can't resolve '%s'", hostname);
+#endif
+ }
+ if (ENABLE_FEATURE_CLEAN_UP)
+ freeaddrinfo(result);
+ return (rc != 0);
+}
+
+/* lookup the default nameserver and display it */
+static void server_print(void)
+{
+ char *server;
+
+ server = xmalloc_sockaddr2dotted_noport((struct sockaddr*)&_res.nsaddr_list[0],
+ sizeof(struct sockaddr_in));
+ /* I honestly don't know what to do if DNS server has _IPv6 address_.
+ * Probably it is listed in
+ * _res._u._ext_.nsaddrs[MAXNS] (of type "struct sockaddr_in6*" each)
+ * but how to find out whether resolver uses
+ * _res.nsaddr_list[] or _res._u._ext_.nsaddrs[], or both?
+ * Looks like classic design from hell, BIND-grade. Hard to surpass. */
+ print_host(server, "Server:");
+ if (ENABLE_FEATURE_CLEAN_UP)
+ free(server);
+ puts("");
+}
+
+/* alter the global _res nameserver structure to use
+ an explicit dns server instead of what is in /etc/resolv.h */
+static void set_default_dns(char *server)
+{
+ struct in_addr server_in_addr;
+
+ if (inet_pton(AF_INET, server, &server_in_addr) > 0) {
+ _res.nscount = 1;
+ _res.nsaddr_list[0].sin_addr = server_in_addr;
+ }
+}
+
+int nslookup_main(int argc, char **argv);
+int nslookup_main(int argc, char **argv)
+{
+ /* We allow 1 or 2 arguments.
+ * The first is the name to be looked up and the second is an
+ * optional DNS server with which to do the lookup.
+ * More than 3 arguments is an error to follow the pattern of the
+ * standard nslookup */
+
+ if (argc < 2 || *argv[1] == '-' || argc > 3)
+ bb_show_usage();
+
+ /* initialize DNS structure _res used in printing the default
+ * name server and in the explicit name server option feature. */
+ res_init();
+ /* rfc2133 says this enables IPv6 lookups */
+ /* (but it also says "may be enabled in /etc/resolv.conf|) */
+ /*_res.options |= RES_USE_INET6;*/
+
+ if(argc == 3)
+ set_default_dns(argv[2]);
+
+ server_print();
+ return print_host(argv[1], "Name:");
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/ping.c b/i/pc104/initrd/conf/busybox/networking/ping.c
new file mode 100644
index 0000000..bbe2c9f
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/ping.c
@@ -0,0 +1,752 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini ping implementation for busybox
+ *
+ * Copyright (C) 1999 by Randolph Chung <tausq@debian.org>
+ *
+ * Adapted from the ping in netkit-base 0.10:
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mike Muuss.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+/* from ping6.c:
+ * Copyright (C) 1999 by Randolph Chung <tausq@debian.org>
+ *
+ * This version of ping is adapted from the ping in netkit-base 0.10,
+ * which is:
+ *
+ * Original copyright notice is retained at the end of this file.
+ *
+ * This version is an adaptation of ping.c from busybox.
+ * The code was modified by Bart Visscher <magick@linux-fan.com>
+ */
+
+#include <net/if.h>
+#include <netinet/ip_icmp.h>
+#include "busybox.h"
+
+#if ENABLE_PING6
+#include <netinet/icmp6.h>
+/* I see RENUMBERED constants in bits/in.h - !!?
+ * What a fuck is going on with libc? Is it a glibc joke? */
+#ifdef IPV6_2292HOPLIMIT
+#undef IPV6_HOPLIMIT
+#define IPV6_HOPLIMIT IPV6_2292HOPLIMIT
+#endif
+#endif
+
+enum {
+ DEFDATALEN = 56,
+ MAXIPLEN = 60,
+ MAXICMPLEN = 76,
+ MAXPACKET = 65468,
+ MAX_DUP_CHK = (8 * 128),
+ MAXWAIT = 10,
+ PINGINTERVAL = 1, /* 1 second */
+};
+
+/* common routines */
+
+static int in_cksum(unsigned short *buf, int sz)
+{
+ int nleft = sz;
+ int sum = 0;
+ unsigned short *w = buf;
+ unsigned short ans = 0;
+
+ while (nleft > 1) {
+ sum += *w++;
+ nleft -= 2;
+ }
+
+ if (nleft == 1) {
+ *(unsigned char *) (&ans) = *(unsigned char *) w;
+ sum += ans;
+ }
+
+ sum = (sum >> 16) + (sum & 0xFFFF);
+ sum += (sum >> 16);
+ ans = ~sum;
+ return ans;
+}
+
+#if !ENABLE_FEATURE_FANCY_PING
+
+/* simple version */
+
+static char *hostname;
+
+static void noresp(int ign ATTRIBUTE_UNUSED)
+{
+ printf("No response from %s\n", hostname);
+ exit(EXIT_FAILURE);
+}
+
+static void ping4(len_and_sockaddr *lsa)
+{
+ struct sockaddr_in pingaddr;
+ struct icmp *pkt;
+ int pingsock, c;
+ char packet[DEFDATALEN + MAXIPLEN + MAXICMPLEN];
+
+ pingsock = create_icmp_socket();
+ pingaddr = lsa->sin;
+
+ pkt = (struct icmp *) packet;
+ memset(pkt, 0, sizeof(packet));
+ pkt->icmp_type = ICMP_ECHO;
+ pkt->icmp_cksum = in_cksum((unsigned short *) pkt, sizeof(packet));
+
+ c = sendto(pingsock, packet, DEFDATALEN + ICMP_MINLEN, 0,
+ (struct sockaddr *) &pingaddr, sizeof(pingaddr));
+
+ if (c < 0) {
+ if (ENABLE_FEATURE_CLEAN_UP)
+ close(pingsock);
+ bb_perror_msg_and_die("sendto");
+ }
+
+ /* listen for replies */
+ while (1) {
+ struct sockaddr_in from;
+ socklen_t fromlen = sizeof(from);
+
+ c = recvfrom(pingsock, packet, sizeof(packet), 0,
+ (struct sockaddr *) &from, &fromlen);
+ if (c < 0) {
+ if (errno != EINTR)
+ bb_perror_msg("recvfrom");
+ continue;
+ }
+ if (c >= 76) { /* ip + icmp */
+ struct iphdr *iphdr = (struct iphdr *) packet;
+
+ pkt = (struct icmp *) (packet + (iphdr->ihl << 2)); /* skip ip hdr */
+ if (pkt->icmp_type == ICMP_ECHOREPLY)
+ break;
+ }
+ }
+ if (ENABLE_FEATURE_CLEAN_UP)
+ close(pingsock);
+}
+
+#if ENABLE_PING6
+static void ping6(len_and_sockaddr *lsa)
+{
+ struct sockaddr_in6 pingaddr;
+ struct icmp6_hdr *pkt;
+ int pingsock, c;
+ int sockopt;
+ char packet[DEFDATALEN + MAXIPLEN + MAXICMPLEN];
+
+ pingsock = create_icmp6_socket();
+ pingaddr = lsa->sin6;
+
+ pkt = (struct icmp6_hdr *) packet;
+ memset(pkt, 0, sizeof(packet));
+ pkt->icmp6_type = ICMP6_ECHO_REQUEST;
+
+ sockopt = offsetof(struct icmp6_hdr, icmp6_cksum);
+ setsockopt(pingsock, SOL_RAW, IPV6_CHECKSUM, &sockopt, sizeof(sockopt));
+
+ c = sendto(pingsock, packet, DEFDATALEN + sizeof (struct icmp6_hdr), 0,
+ (struct sockaddr *) &pingaddr, sizeof(pingaddr));
+
+ if (c < 0) {
+ if (ENABLE_FEATURE_CLEAN_UP)
+ close(pingsock);
+ bb_perror_msg_and_die("sendto");
+ }
+
+ /* listen for replies */
+ while (1) {
+ struct sockaddr_in6 from;
+ socklen_t fromlen = sizeof(from);
+
+ c = recvfrom(pingsock, packet, sizeof(packet), 0,
+ (struct sockaddr *) &from, &fromlen);
+ if (c < 0) {
+ if (errno != EINTR)
+ bb_perror_msg("recvfrom");
+ continue;
+ }
+ if (c >= 8) { /* icmp6_hdr */
+ pkt = (struct icmp6_hdr *) packet;
+ if (pkt->icmp6_type == ICMP6_ECHO_REPLY)
+ break;
+ }
+ }
+ if (ENABLE_FEATURE_CLEAN_UP)
+ close(pingsock);
+}
+#endif
+
+int ping_main(int argc, char **argv);
+int ping_main(int argc, char **argv)
+{
+ len_and_sockaddr *lsa;
+#if ENABLE_PING6
+ sa_family_t af = AF_UNSPEC;
+ while (++argv[0][0] == '-') {
+ if (argv[0][1] == '4') {
+ af = AF_INET;
+ continue;
+ }
+ if (argv[0][1] == '6') {
+ af = AF_INET6;
+ continue;
+ }
+ bb_show_usage();
+ }
+#else
+ argv++;
+#endif
+
+ hostname = *argv;
+ if (!hostname)
+ bb_show_usage();
+
+#if ENABLE_PING6
+ lsa = xhost_and_af2sockaddr(hostname, 0, af);
+#else
+ lsa = xhost_and_af2sockaddr(hostname, 0, AF_INET);
+#endif
+ /* Set timer _after_ DNS resolution */
+ signal(SIGALRM, noresp);
+ alarm(5); /* give the host 5000ms to respond */
+
+#if ENABLE_PING6
+ if (lsa->sa.sa_family == AF_INET6)
+ ping6(lsa);
+ else
+#endif
+ ping4(lsa);
+ printf("%s is alive!\n", hostname);
+ return EXIT_SUCCESS;
+}
+
+
+#else /* FEATURE_FANCY_PING */
+
+
+/* full(er) version */
+
+#define OPT_STRING ("qvc:s:I:4" USE_PING6("6"))
+enum {
+ OPT_QUIET = 1 << 0,
+ OPT_VERBOSE = 1 << 1,
+ OPT_c = 1 << 2,
+ OPT_s = 1 << 3,
+ OPT_I = 1 << 4,
+ OPT_IPV4 = 1 << 5,
+ OPT_IPV6 = (1 << 6) * ENABLE_PING6,
+};
+
+
+static union {
+ struct sockaddr sa;
+ struct sockaddr_in sin;
+#if ENABLE_PING6
+ struct sockaddr_in6 sin6;
+#endif
+} pingaddr;
+static len_and_sockaddr *source_lsa;
+static int pingsock = -1;
+static unsigned datalen; /* intentionally uninitialized to work around gcc bug */
+
+static int if_index;
+
+static unsigned long ntransmitted, nreceived, nrepeats, pingcount;
+static int myid;
+static unsigned tmin = UINT_MAX, tmax;
+static unsigned long tsum;
+static char rcvd_tbl[MAX_DUP_CHK / 8];
+
+static const char *hostname;
+static const char *dotted;
+
+#define A(bit) rcvd_tbl[(bit)>>3] /* identify byte in array */
+#define B(bit) (1 << ((bit) & 0x07)) /* identify bit in byte */
+#define SET(bit) (A(bit) |= B(bit))
+#define CLR(bit) (A(bit) &= (~B(bit)))
+#define TST(bit) (A(bit) & B(bit))
+
+/**************************************************************************/
+
+static void pingstats(int junk ATTRIBUTE_UNUSED)
+{
+ signal(SIGINT, SIG_IGN);
+
+ printf("\n--- %s ping statistics ---\n", hostname);
+ printf("%lu packets transmitted, ", ntransmitted);
+ printf("%lu packets received, ", nreceived);
+ if (nrepeats)
+ printf("%lu duplicates, ", nrepeats);
+ if (ntransmitted)
+ ntransmitted = (ntransmitted - nreceived) * 100 / ntransmitted;
+ printf("%lu%% packet loss\n", ntransmitted);
+ if (tmin != UINT_MAX)
+ printf("round-trip min/avg/max = %u.%u/%lu.%lu/%u.%u ms\n",
+ tmin / 10, tmin % 10,
+ (tsum / (nreceived + nrepeats)) / 10,
+ (tsum / (nreceived + nrepeats)) % 10, tmax / 10, tmax % 10);
+ exit(nreceived == 0); /* (nreceived == 0) is true (1) -- 'failure' */
+}
+
+static void sendping_tail(void (*sp)(int), const void *pkt, int size_pkt)
+{
+ int sz;
+
+ CLR((uint16_t)ntransmitted % MAX_DUP_CHK);
+ ntransmitted++;
+
+ /* sizeof(pingaddr) can be larger than real sa size, but I think
+ * it doesn't matter */
+ sz = sendto(pingsock, pkt, size_pkt, 0, &pingaddr.sa, sizeof(pingaddr));
+ if (sz < 0)
+ bb_perror_msg_and_die("sendto");
+ if (sz != size_pkt)
+ bb_error_msg_and_die("ping wrote %d chars; %d expected", sz,
+ size_pkt);
+
+ signal(SIGALRM, sp);
+ if (pingcount == 0 || ntransmitted < pingcount) { /* schedule next in 1s */
+ alarm(PINGINTERVAL);
+ } else { /* done, wait for the last ping to come back */
+ /* todo, don't necessarily need to wait so long... */
+ signal(SIGALRM, pingstats);
+ alarm(MAXWAIT);
+ }
+}
+
+static void sendping4(int junk ATTRIBUTE_UNUSED)
+{
+ struct icmp *pkt = alloca(datalen + ICMP_MINLEN);
+
+ pkt->icmp_type = ICMP_ECHO;
+ pkt->icmp_code = 0;
+ pkt->icmp_cksum = 0;
+ pkt->icmp_seq = htons(ntransmitted); /* don't ++ here, it can be a macro */
+ pkt->icmp_id = myid;
+ gettimeofday((struct timeval *) &pkt->icmp_dun, NULL);
+ pkt->icmp_cksum = in_cksum((unsigned short *) pkt, datalen + ICMP_MINLEN);
+
+ sendping_tail(sendping4, pkt, datalen + ICMP_MINLEN);
+}
+#if ENABLE_PING6
+static void sendping6(int junk ATTRIBUTE_UNUSED)
+{
+ struct icmp6_hdr *pkt = alloca(datalen + sizeof(struct icmp6_hdr));
+
+ pkt->icmp6_type = ICMP6_ECHO_REQUEST;
+ pkt->icmp6_code = 0;
+ pkt->icmp6_cksum = 0;
+ pkt->icmp6_seq = htons(ntransmitted); /* don't ++ here, it can be a macro */
+ pkt->icmp6_id = myid;
+ gettimeofday((struct timeval *) &pkt->icmp6_data8[4], NULL);
+
+ sendping_tail(sendping6, pkt, datalen + sizeof(struct icmp6_hdr));
+}
+#endif
+
+static const char *icmp_type_name(int id)
+{
+ switch (id) {
+ case ICMP_ECHOREPLY: return "Echo Reply";
+ case ICMP_DEST_UNREACH: return "Destination Unreachable";
+ case ICMP_SOURCE_QUENCH: return "Source Quench";
+ case ICMP_REDIRECT: return "Redirect (change route)";
+ case ICMP_ECHO: return "Echo Request";
+ case ICMP_TIME_EXCEEDED: return "Time Exceeded";
+ case ICMP_PARAMETERPROB: return "Parameter Problem";
+ case ICMP_TIMESTAMP: return "Timestamp Request";
+ case ICMP_TIMESTAMPREPLY: return "Timestamp Reply";
+ case ICMP_INFO_REQUEST: return "Information Request";
+ case ICMP_INFO_REPLY: return "Information Reply";
+ case ICMP_ADDRESS: return "Address Mask Request";
+ case ICMP_ADDRESSREPLY: return "Address Mask Reply";
+ default: return "unknown ICMP type";
+ }
+}
+#if ENABLE_PING6
+/* RFC3542 changed some definitions from RFC2292 for no good reason, whee!
+ * the newer 3542 uses a MLD_ prefix where as 2292 uses ICMP6_ prefix */
+#ifndef MLD_LISTENER_QUERY
+# define MLD_LISTENER_QUERY ICMP6_MEMBERSHIP_QUERY
+#endif
+#ifndef MLD_LISTENER_REPORT
+# define MLD_LISTENER_REPORT ICMP6_MEMBERSHIP_REPORT
+#endif
+#ifndef MLD_LISTENER_REDUCTION
+# define MLD_LISTENER_REDUCTION ICMP6_MEMBERSHIP_REDUCTION
+#endif
+static const char *icmp6_type_name(int id)
+{
+ switch (id) {
+ case ICMP6_DST_UNREACH: return "Destination Unreachable";
+ case ICMP6_PACKET_TOO_BIG: return "Packet too big";
+ case ICMP6_TIME_EXCEEDED: return "Time Exceeded";
+ case ICMP6_PARAM_PROB: return "Parameter Problem";
+ case ICMP6_ECHO_REPLY: return "Echo Reply";
+ case ICMP6_ECHO_REQUEST: return "Echo Request";
+ case MLD_LISTENER_QUERY: return "Listener Query";
+ case MLD_LISTENER_REPORT: return "Listener Report";
+ case MLD_LISTENER_REDUCTION: return "Listener Reduction";
+ default: return "unknown ICMP type";
+ }
+}
+#endif
+
+static void unpack_tail(int sz, struct timeval *tp,
+ const char *from_str,
+ uint16_t recv_seq, int ttl)
+{
+ const char *dupmsg = " (DUP!)";
+ unsigned triptime = triptime; /* for gcc */
+
+ ++nreceived;
+
+ if (tp) {
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
+ tv.tv_usec -= tp->tv_usec;
+ if (tv.tv_usec < 0) {
+ --tv.tv_sec;
+ tv.tv_usec += 1000000;
+ }
+ tv.tv_sec -= tp->tv_sec;
+
+ triptime = tv.tv_sec * 10000 + (tv.tv_usec / 100);
+ tsum += triptime;
+ if (triptime < tmin)
+ tmin = triptime;
+ if (triptime > tmax)
+ tmax = triptime;
+ }
+
+ if (TST(recv_seq % MAX_DUP_CHK)) {
+ ++nrepeats;
+ --nreceived;
+ } else {
+ SET(recv_seq % MAX_DUP_CHK);
+ dupmsg += 7;
+ }
+
+ if (option_mask32 & OPT_QUIET)
+ return;
+
+ printf("%d bytes from %s: seq=%u ttl=%d", sz,
+ from_str, recv_seq, ttl);
+ if (tp)
+ printf(" time=%u.%u ms", triptime / 10, triptime % 10);
+ puts(dupmsg);
+ fflush(stdout);
+}
+static void unpack4(char *buf, int sz, struct sockaddr_in *from)
+{
+ struct icmp *icmppkt;
+ struct iphdr *iphdr;
+ int hlen;
+
+ /* discard if too short */
+ if (sz < (datalen + ICMP_MINLEN))
+ return;
+
+ /* check IP header */
+ iphdr = (struct iphdr *) buf;
+ hlen = iphdr->ihl << 2;
+ sz -= hlen;
+ icmppkt = (struct icmp *) (buf + hlen);
+ if (icmppkt->icmp_id != myid)
+ return; /* not our ping */
+
+ if (icmppkt->icmp_type == ICMP_ECHOREPLY) {
+ uint16_t recv_seq = ntohs(icmppkt->icmp_seq);
+ struct timeval *tp = NULL;
+
+ if (sz >= ICMP_MINLEN + sizeof(struct timeval))
+ tp = (struct timeval *) icmppkt->icmp_data;
+ unpack_tail(sz, tp,
+ inet_ntoa(*(struct in_addr *) &from->sin_addr.s_addr),
+ recv_seq, iphdr->ttl);
+ } else if (icmppkt->icmp_type != ICMP_ECHO) {
+ bb_error_msg("warning: got ICMP %d (%s)",
+ icmppkt->icmp_type,
+ icmp_type_name(icmppkt->icmp_type));
+ }
+}
+#if ENABLE_PING6
+static void unpack6(char *packet, int sz, struct sockaddr_in6 *from, int hoplimit)
+{
+ struct icmp6_hdr *icmppkt;
+ char buf[INET6_ADDRSTRLEN];
+
+ /* discard if too short */
+ if (sz < (datalen + sizeof(struct icmp6_hdr)))
+ return;
+
+ icmppkt = (struct icmp6_hdr *) packet;
+ if (icmppkt->icmp6_id != myid)
+ return; /* not our ping */
+
+ if (icmppkt->icmp6_type == ICMP6_ECHO_REPLY) {
+ uint16_t recv_seq = ntohs(icmppkt->icmp6_seq);
+ struct timeval *tp = NULL;
+
+ if (sz >= sizeof(struct icmp6_hdr) + sizeof(struct timeval))
+ tp = (struct timeval *) &icmppkt->icmp6_data8[4];
+ unpack_tail(sz, tp,
+ inet_ntop(AF_INET6, &pingaddr.sin6.sin6_addr,
+ buf, sizeof(buf)),
+ recv_seq, hoplimit);
+ } else if (icmppkt->icmp6_type != ICMP6_ECHO_REQUEST) {
+ bb_error_msg("warning: got ICMP %d (%s)",
+ icmppkt->icmp6_type,
+ icmp6_type_name(icmppkt->icmp6_type));
+ }
+}
+#endif
+
+static void ping4(len_and_sockaddr *lsa)
+{
+ char packet[datalen + MAXIPLEN + MAXICMPLEN];
+ int sockopt;
+
+ pingsock = create_icmp_socket();
+ pingaddr.sin = lsa->sin;
+ if (source_lsa)
+ xbind(pingsock, &lsa->sa, lsa->len);
+
+ /* enable broadcast pings */
+ setsockopt_broadcast(pingsock);
+
+ /* set recv buf for broadcast pings */
+ sockopt = 48 * 1024; /* explain why 48k? */
+ setsockopt(pingsock, SOL_SOCKET, SO_RCVBUF, &sockopt, sizeof(sockopt));
+
+ signal(SIGINT, pingstats);
+
+ /* start the ping's going ... */
+ sendping4(0);
+
+ /* listen for replies */
+ while (1) {
+ struct sockaddr_in from;
+ socklen_t fromlen = (socklen_t) sizeof(from);
+ int c;
+
+ c = recvfrom(pingsock, packet, sizeof(packet), 0,
+ (struct sockaddr *) &from, &fromlen);
+ if (c < 0) {
+ if (errno != EINTR)
+ bb_perror_msg("recvfrom");
+ continue;
+ }
+ unpack4(packet, c, &from);
+ if (pingcount > 0 && nreceived >= pingcount)
+ break;
+ }
+}
+#if ENABLE_PING6
+extern int BUG_bad_offsetof_icmp6_cksum(void);
+static void ping6(len_and_sockaddr *lsa)
+{
+ char packet[datalen + MAXIPLEN + MAXICMPLEN];
+ int sockopt;
+ struct msghdr msg;
+ struct sockaddr_in6 from;
+ struct iovec iov;
+ char control_buf[CMSG_SPACE(36)];
+
+ pingsock = create_icmp6_socket();
+ pingaddr.sin6 = lsa->sin6;
+ /* untested whether "-I addr" really works for IPv6: */
+ if (source_lsa)
+ xbind(pingsock, &lsa->sa, lsa->len);
+
+#ifdef ICMP6_FILTER
+ {
+ struct icmp6_filter filt;
+ if (!(option_mask32 & OPT_VERBOSE)) {
+ ICMP6_FILTER_SETBLOCKALL(&filt);
+ ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &filt);
+ } else {
+ ICMP6_FILTER_SETPASSALL(&filt);
+ }
+ if (setsockopt(pingsock, IPPROTO_ICMPV6, ICMP6_FILTER, &filt,
+ sizeof(filt)) < 0)
+ bb_error_msg_and_die("setsockopt(ICMP6_FILTER)");
+ }
+#endif /*ICMP6_FILTER*/
+
+ /* enable broadcast pings */
+ setsockopt_broadcast(pingsock);
+
+ /* set recv buf for broadcast pings */
+ sockopt = 48 * 1024; /* explain why 48k? */
+ setsockopt(pingsock, SOL_SOCKET, SO_RCVBUF, &sockopt, sizeof(sockopt));
+
+ sockopt = offsetof(struct icmp6_hdr, icmp6_cksum);
+ if (offsetof(struct icmp6_hdr, icmp6_cksum) != 2)
+ BUG_bad_offsetof_icmp6_cksum();
+ setsockopt(pingsock, SOL_RAW, IPV6_CHECKSUM, &sockopt, sizeof(sockopt));
+
+ /* request ttl info to be returned in ancillary data */
+ setsockopt(pingsock, SOL_IPV6, IPV6_HOPLIMIT, &const_int_1, sizeof(const_int_1));
+
+ if (if_index)
+ pingaddr.sin6.sin6_scope_id = if_index;
+
+ signal(SIGINT, pingstats);
+
+ /* start the ping's going ... */
+ sendping6(0);
+
+ /* listen for replies */
+ msg.msg_name = &from;
+ msg.msg_namelen = sizeof(from);
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = control_buf;
+ iov.iov_base = packet;
+ iov.iov_len = sizeof(packet);
+ while (1) {
+ int c;
+ struct cmsghdr *mp;
+ int hoplimit = -1;
+ msg.msg_controllen = sizeof(control_buf);
+
+ c = recvmsg(pingsock, &msg, 0);
+ if (c < 0) {
+ if (errno != EINTR)
+ bb_perror_msg("recvfrom");
+ continue;
+ }
+ for (mp = CMSG_FIRSTHDR(&msg); mp; mp = CMSG_NXTHDR(&msg, mp)) {
+ if (mp->cmsg_level == SOL_IPV6
+ && mp->cmsg_type == IPV6_HOPLIMIT
+ /* don't check len - we trust the kernel: */
+ /* && mp->cmsg_len >= CMSG_LEN(sizeof(int)) */
+ ) {
+ hoplimit = *(int*)CMSG_DATA(mp);
+ }
+ }
+ unpack6(packet, c, &from, hoplimit);
+ if (pingcount > 0 && nreceived >= pingcount)
+ break;
+ }
+}
+#endif
+
+static void ping(len_and_sockaddr *lsa)
+{
+ printf("PING %s (%s)", hostname, dotted);
+ if (source_lsa) {
+ printf(" from %s",
+ xmalloc_sockaddr2dotted_noport(&lsa->sa, lsa->len));
+ }
+ printf(": %d data bytes\n", datalen);
+
+#if ENABLE_PING6
+ if (lsa->sa.sa_family == AF_INET6)
+ ping6(lsa);
+ else
+#endif
+ ping4(lsa);
+}
+
+int ping_main(int argc, char **argv);
+int ping_main(int argc, char **argv)
+{
+ len_and_sockaddr *lsa;
+ char *opt_c, *opt_s, *opt_I;
+ USE_PING6(sa_family_t af = AF_UNSPEC;)
+
+ datalen = DEFDATALEN; /* initialized here rather than in global scope to work around gcc bug */
+
+ /* exactly one argument needed, -v and -q don't mix */
+ opt_complementary = "=1:q--v:v--q";
+ getopt32(argc, argv, OPT_STRING, &opt_c, &opt_s, &opt_I);
+ if (option_mask32 & OPT_c) pingcount = xatoul(opt_c); // -c
+ if (option_mask32 & OPT_s) datalen = xatou16(opt_s); // -s
+ if (option_mask32 & OPT_I) { // -I
+ if_index = if_nametoindex(opt_I);
+ if (!if_index) {
+ /* TODO: I'm not sure it takes IPv6 unless in [XX:XX..] format */
+ /* (ping doesn't support source IPv6 addresses yet anyway) */
+ source_lsa = xdotted2sockaddr(opt_I, 0);
+ }
+ }
+ myid = (int16_t) getpid();
+ hostname = argv[optind];
+#if ENABLE_PING6
+ if (option_mask32 & OPT_IPV4)
+ af = AF_INET;
+ if (option_mask32 & OPT_IPV6)
+ af = AF_INET6;
+ lsa = xhost_and_af2sockaddr(hostname, 0, af);
+#else
+ lsa = xhost_and_af2sockaddr(hostname, 0, AF_INET);
+#endif
+
+ if (source_lsa && source_lsa->sa.sa_family != lsa->sa.sa_family)
+ /* leaking it here... */
+ source_lsa = NULL;
+
+ dotted = xmalloc_sockaddr2dotted_noport(&lsa->sa, lsa->len);
+ ping(lsa);
+ pingstats(0);
+ return EXIT_SUCCESS;
+}
+#endif /* FEATURE_FANCY_PING */
+
+
+#if ENABLE_PING6
+int ping6_main(int argc, char **argv);
+int ping6_main(int argc, char **argv)
+{
+ argv[0] = (char*)"-6";
+ return ping_main(argc + 1, argv - 1);
+}
+#endif
+
+/* from ping6.c:
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mike Muuss.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change
+ * ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>
+ *
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
diff --git a/i/pc104/initrd/conf/busybox/networking/route.c b/i/pc104/initrd/conf/busybox/networking/route.c
new file mode 100644
index 0000000..0697390
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/route.c
@@ -0,0 +1,697 @@
+/* vi: set sw=4 ts=4: */
+/* route
+ *
+ * Similar to the standard Unix route, but with only the necessary
+ * parts for AF_INET and AF_INET6
+ *
+ * Bjorn Wesen, Axis Communications AB
+ *
+ * Author of the original route:
+ * Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+ * (derived from FvK's 'route.c 1.70 01/04/94')
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ *
+ *
+ * displayroute() code added by Vladimir N. Oleynik <dzo@simtreas.ru>
+ * adjustments by Larry Doolittle <LRDoolittle@lbl.gov>
+ *
+ * IPV6 support added by Bart Visscher <magick@linux-fan.com>
+ */
+
+/* 2004/03/09 Manuel Novoa III <mjn3@codepoet.org>
+ *
+ * Rewritten to fix several bugs, add additional error checking, and
+ * remove ridiculous amounts of bloat.
+ */
+
+#include "busybox.h"
+#include "inet_common.h"
+#include <getopt.h>
+#include <net/route.h>
+#include <net/if.h>
+
+
+#ifndef RTF_UP
+/* Keep this in sync with /usr/src/linux/include/linux/route.h */
+#define RTF_UP 0x0001 /* route usable */
+#define RTF_GATEWAY 0x0002 /* destination is a gateway */
+#define RTF_HOST 0x0004 /* host entry (net otherwise) */
+#define RTF_REINSTATE 0x0008 /* reinstate route after tmout */
+#define RTF_DYNAMIC 0x0010 /* created dyn. (by redirect) */
+#define RTF_MODIFIED 0x0020 /* modified dyn. (by redirect) */
+#define RTF_MTU 0x0040 /* specific MTU for this route */
+#ifndef RTF_MSS
+#define RTF_MSS RTF_MTU /* Compatibility :-( */
+#endif
+#define RTF_WINDOW 0x0080 /* per route window clamping */
+#define RTF_IRTT 0x0100 /* Initial round trip time */
+#define RTF_REJECT 0x0200 /* Reject route */
+#endif
+
+#if defined (SIOCADDRTOLD) || defined (RTF_IRTT) /* route */
+#define HAVE_NEW_ADDRT 1
+#endif
+
+#if HAVE_NEW_ADDRT
+#define mask_in_addr(x) (((struct sockaddr_in *)&((x).rt_genmask))->sin_addr.s_addr)
+#define full_mask(x) (x)
+#else
+#define mask_in_addr(x) ((x).rt_genmask)
+#define full_mask(x) (((struct sockaddr_in *)&(x))->sin_addr.s_addr)
+#endif
+
+/* The RTACTION entries must agree with tbl_verb[] below! */
+#define RTACTION_ADD 1
+#define RTACTION_DEL 2
+
+/* For the various tbl_*[] arrays, the 1st byte is the offset to
+ * the next entry and the 2nd byte is return value. */
+
+#define NET_FLAG 1
+#define HOST_FLAG 2
+
+/* We remap '-' to '#' to avoid problems with getopt. */
+static const char tbl_hash_net_host[] =
+ "\007\001#net\0"
+/* "\010\002#host\0" */
+ "\007\002#host" /* Since last, we can save a byte. */
+;
+
+#define KW_TAKES_ARG 020
+#define KW_SETS_FLAG 040
+
+#define KW_IPVx_METRIC 020
+#define KW_IPVx_NETMASK 021
+#define KW_IPVx_GATEWAY 022
+#define KW_IPVx_MSS 023
+#define KW_IPVx_WINDOW 024
+#define KW_IPVx_IRTT 025
+#define KW_IPVx_DEVICE 026
+
+#define KW_IPVx_FLAG_ONLY 040
+#define KW_IPVx_REJECT 040
+#define KW_IPVx_MOD 041
+#define KW_IPVx_DYN 042
+#define KW_IPVx_REINSTATE 043
+
+static const char tbl_ipvx[] =
+ /* 020 is the "takes an arg" bit */
+#if HAVE_NEW_ADDRT
+ "\011\020metric\0"
+#endif
+ "\012\021netmask\0"
+ "\005\022gw\0"
+ "\012\022gateway\0"
+ "\006\023mss\0"
+ "\011\024window\0"
+#ifdef RTF_IRTT
+ "\007\025irtt\0"
+#endif
+ "\006\026dev\0"
+ "\011\026device\0"
+ /* 040 is the "sets a flag" bit - MUST match flags_ipvx[] values below. */
+#ifdef RTF_REJECT
+ "\011\040reject\0"
+#endif
+ "\006\041mod\0"
+ "\006\042dyn\0"
+/* "\014\043reinstate\0" */
+ "\013\043reinstate" /* Since last, we can save a byte. */
+;
+
+static const int flags_ipvx[] = { /* MUST match tbl_ipvx[] values above. */
+#ifdef RTF_REJECT
+ RTF_REJECT,
+#endif
+ RTF_MODIFIED,
+ RTF_DYNAMIC,
+ RTF_REINSTATE
+};
+
+static int kw_lookup(const char *kwtbl, char ***pargs)
+{
+ if (**pargs) {
+ do {
+ if (strcmp(kwtbl+2, **pargs) == 0) { /* Found a match. */
+ *pargs += 1;
+ if (kwtbl[1] & KW_TAKES_ARG) {
+ if (!**pargs) { /* No more args! */
+ bb_show_usage();
+ }
+ *pargs += 1; /* Calling routine will use args[-1]. */
+ }
+ return kwtbl[1];
+ }
+ kwtbl += *kwtbl;
+ } while (*kwtbl);
+ }
+ return 0;
+}
+
+/* Add or delete a route, depending on action. */
+
+static void INET_setroute(int action, char **args)
+{
+ struct rtentry rt;
+ const char *netmask = NULL;
+ int skfd, isnet, xflag;
+
+ /* Grab the -net or -host options. Remember they were transformed. */
+ xflag = kw_lookup(tbl_hash_net_host, &args);
+
+ /* If we did grab -net or -host, make sure we still have an arg left. */
+ if (*args == NULL) {
+ bb_show_usage();
+ }
+
+ /* Clean out the RTREQ structure. */
+ memset(&rt, 0, sizeof(rt));
+
+ {
+ const char *target = *args++;
+ char *prefix;
+
+ /* recognize x.x.x.x/mask format. */
+ prefix = strchr(target, '/');
+ if(prefix) {
+ int prefix_len;
+
+ prefix_len = xatoul_range(prefix+1, 0, 32);
+ mask_in_addr(rt) = htonl( ~ (0xffffffffUL >> prefix_len));
+ *prefix = '\0';
+#if HAVE_NEW_ADDRT
+ rt.rt_genmask.sa_family = AF_INET;
+#endif
+ } else {
+ /* Default netmask. */
+ netmask = bb_str_default;
+ }
+ /* Prefer hostname lookup is -host flag (xflag==1) was given. */
+ isnet = INET_resolve(target, (struct sockaddr_in *) &rt.rt_dst,
+ (xflag & HOST_FLAG));
+ if (isnet < 0) {
+ bb_error_msg_and_die("resolving %s", target);
+ }
+ if(prefix) {
+ /* do not destroy prefix for process args */
+ *prefix = '/';
+ }
+ }
+
+ if (xflag) { /* Reinit isnet if -net or -host was specified. */
+ isnet = (xflag & NET_FLAG);
+ }
+
+ /* Fill in the other fields. */
+ rt.rt_flags = ((isnet) ? RTF_UP : (RTF_UP | RTF_HOST));
+
+ while (*args) {
+ int k = kw_lookup(tbl_ipvx, &args);
+ const char *args_m1 = args[-1];
+
+ if (k & KW_IPVx_FLAG_ONLY) {
+ rt.rt_flags |= flags_ipvx[k & 3];
+ continue;
+ }
+
+#if HAVE_NEW_ADDRT
+ if (k == KW_IPVx_METRIC) {
+ rt.rt_metric = xatoul(args_m1) + 1;
+ continue;
+ }
+#endif
+
+ if (k == KW_IPVx_NETMASK) {
+ struct sockaddr mask;
+
+ if (mask_in_addr(rt)) {
+ bb_show_usage();
+ }
+
+ netmask = args_m1;
+ isnet = INET_resolve(netmask, (struct sockaddr_in *) &mask, 0);
+ if (isnet < 0) {
+ bb_error_msg_and_die("resolving %s", netmask);
+ }
+ rt.rt_genmask = full_mask(mask);
+ continue;
+ }
+
+ if (k == KW_IPVx_GATEWAY) {
+ if (rt.rt_flags & RTF_GATEWAY) {
+ bb_show_usage();
+ }
+
+ isnet = INET_resolve(args_m1,
+ (struct sockaddr_in *) &rt.rt_gateway, 1);
+ rt.rt_flags |= RTF_GATEWAY;
+
+ if (isnet) {
+ if (isnet < 0) {
+ bb_error_msg_and_die("resolving %s", args_m1);
+ }
+ bb_error_msg_and_die("gateway %s is a NETWORK", args_m1);
+ }
+ continue;
+ }
+
+ if (k == KW_IPVx_MSS) { /* Check valid MSS bounds. */
+ rt.rt_flags |= RTF_MSS;
+ rt.rt_mss = xatoul_range(args_m1, 64, 32768);
+ continue;
+ }
+
+ if (k == KW_IPVx_WINDOW) { /* Check valid window bounds. */
+ rt.rt_flags |= RTF_WINDOW;
+ rt.rt_window = xatoul_range(args_m1, 128, INT_MAX);
+ continue;
+ }
+
+#ifdef RTF_IRTT
+ if (k == KW_IPVx_IRTT) {
+ rt.rt_flags |= RTF_IRTT;
+ rt.rt_irtt = xatoul(args_m1);
+ rt.rt_irtt *= (sysconf(_SC_CLK_TCK) / 100); /* FIXME */
+#if 0 /* FIXME: do we need to check anything of this? */
+ if (rt.rt_irtt < 1 || rt.rt_irtt > (120 * HZ)) {
+ bb_error_msg_and_die("bad irtt");
+ }
+#endif
+ continue;
+ }
+#endif
+
+ /* Device is special in that it can be the last arg specified
+ * and doesn't requre the dev/device keyword in that case. */
+ if (!rt.rt_dev && ((k == KW_IPVx_DEVICE) || (!k && !*++args))) {
+ /* Don't use args_m1 here since args may have changed! */
+ rt.rt_dev = args[-1];
+ continue;
+ }
+
+ /* Nothing matched. */
+ bb_show_usage();
+ }
+
+#ifdef RTF_REJECT
+ if ((rt.rt_flags & RTF_REJECT) && !rt.rt_dev) {
+ rt.rt_dev = (char*)"lo";
+ }
+#endif
+
+ /* sanity checks.. */
+ if (mask_in_addr(rt)) {
+ unsigned long mask = mask_in_addr(rt);
+
+ mask = ~ntohl(mask);
+ if ((rt.rt_flags & RTF_HOST) && mask != 0xffffffff) {
+ bb_error_msg_and_die("netmask %.8x and host route conflict",
+ (unsigned int) mask);
+ }
+ if (mask & (mask + 1)) {
+ bb_error_msg_and_die("bogus netmask %s", netmask);
+ }
+ mask = ((struct sockaddr_in *) &rt.rt_dst)->sin_addr.s_addr;
+ if (mask & ~mask_in_addr(rt)) {
+ bb_error_msg_and_die("netmask and route address conflict");
+ }
+ }
+
+ /* Fill out netmask if still unset */
+ if ((action == RTACTION_ADD) && (rt.rt_flags & RTF_HOST)) {
+ mask_in_addr(rt) = 0xffffffff;
+ }
+
+ /* Create a socket to the INET kernel. */
+ skfd = xsocket(AF_INET, SOCK_DGRAM, 0);
+
+ if (ioctl(skfd, ((action==RTACTION_ADD) ? SIOCADDRT : SIOCDELRT), &rt)<0) {
+ bb_perror_msg_and_die("SIOC[ADD|DEL]RT");
+ }
+
+ if (ENABLE_FEATURE_CLEAN_UP) close(skfd);
+}
+
+#if ENABLE_FEATURE_IPV6
+
+static void INET6_setroute(int action, char **args)
+{
+ struct sockaddr_in6 sa6;
+ struct in6_rtmsg rt;
+ int prefix_len, skfd;
+ const char *devname;
+
+ /* We know args isn't NULL from the check in route_main. */
+ const char *target = *args++;
+
+ if (strcmp(target, bb_str_default) == 0) {
+ prefix_len = 0;
+ memset(&sa6, 0, sizeof(sa6));
+ } else {
+ char *cp;
+ if ((cp = strchr(target, '/'))) { /* Yes... const to non is ok. */
+ *cp = 0;
+ prefix_len = xatoul_range(cp+1, 0, 128);
+ } else {
+ prefix_len = 128;
+ }
+ if (INET6_resolve(target, (struct sockaddr_in6 *) &sa6) < 0) {
+ bb_error_msg_and_die("resolving %s", target);
+ }
+ }
+
+ /* Clean out the RTREQ structure. */
+ memset(&rt, 0, sizeof(rt));
+
+ memcpy(&rt.rtmsg_dst, sa6.sin6_addr.s6_addr, sizeof(struct in6_addr));
+
+ /* Fill in the other fields. */
+ rt.rtmsg_dst_len = prefix_len;
+ rt.rtmsg_flags = ((prefix_len == 128) ? (RTF_UP|RTF_HOST) : RTF_UP);
+ rt.rtmsg_metric = 1;
+
+ devname = NULL;
+
+ while (*args) {
+ int k = kw_lookup(tbl_ipvx, &args);
+ const char *args_m1 = args[-1];
+
+ if ((k == KW_IPVx_MOD) || (k == KW_IPVx_DYN)) {
+ rt.rtmsg_flags |= flags_ipvx[k & 3];
+ continue;
+ }
+
+ if (k == KW_IPVx_METRIC) {
+ rt.rtmsg_metric = xatoul(args_m1);
+ continue;
+ }
+
+ if (k == KW_IPVx_GATEWAY) {
+ if (rt.rtmsg_flags & RTF_GATEWAY) {
+ bb_show_usage();
+ }
+
+ if (INET6_resolve(args_m1, (struct sockaddr_in6 *) &sa6) < 0) {
+ bb_error_msg_and_die("resolving %s", args_m1);
+ }
+ memcpy(&rt.rtmsg_gateway, sa6.sin6_addr.s6_addr,
+ sizeof(struct in6_addr));
+ rt.rtmsg_flags |= RTF_GATEWAY;
+ continue;
+ }
+
+ /* Device is special in that it can be the last arg specified
+ * and doesn't requre the dev/device keyword in that case. */
+ if (!devname && ((k == KW_IPVx_DEVICE) || (!k && !*++args))) {
+ /* Don't use args_m1 here since args may have changed! */
+ devname = args[-1];
+ continue;
+ }
+
+ /* Nothing matched. */
+ bb_show_usage();
+ }
+
+ /* Create a socket to the INET6 kernel. */
+ skfd = xsocket(AF_INET6, SOCK_DGRAM, 0);
+
+ rt.rtmsg_ifindex = 0;
+
+ if (devname) {
+ struct ifreq ifr;
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, devname, sizeof(ifr.ifr_name));
+
+ if (ioctl(skfd, SIOGIFINDEX, &ifr) < 0) {
+ bb_perror_msg_and_die("SIOGIFINDEX");
+ }
+ rt.rtmsg_ifindex = ifr.ifr_ifindex;
+ }
+
+ /* Tell the kernel to accept this route. */
+ if (ioctl(skfd, ((action==RTACTION_ADD) ? SIOCADDRT : SIOCDELRT), &rt)<0) {
+ bb_perror_msg_and_die("SIOC[ADD|DEL]RT");
+ }
+
+ if (ENABLE_FEATURE_CLEAN_UP) close(skfd);
+}
+#endif
+
+static const unsigned int flagvals[] = { /* Must agree with flagchars[]. */
+ RTF_GATEWAY,
+ RTF_HOST,
+ RTF_REINSTATE,
+ RTF_DYNAMIC,
+ RTF_MODIFIED,
+#if ENABLE_FEATURE_IPV6
+ RTF_DEFAULT,
+ RTF_ADDRCONF,
+ RTF_CACHE
+#endif
+};
+
+#define IPV4_MASK (RTF_GATEWAY|RTF_HOST|RTF_REINSTATE|RTF_DYNAMIC|RTF_MODIFIED)
+#define IPV6_MASK (RTF_GATEWAY|RTF_HOST|RTF_DEFAULT|RTF_ADDRCONF|RTF_CACHE)
+
+static const char flagchars[] = /* Must agree with flagvals[]. */
+ "GHRDM"
+#if ENABLE_FEATURE_IPV6
+ "DAC"
+#endif
+;
+
+static void set_flags(char *flagstr, int flags)
+{
+ int i;
+
+ *flagstr++ = 'U';
+
+ for (i = 0; (*flagstr = flagchars[i]) != 0; i++) {
+ if (flags & flagvals[i]) {
+ ++flagstr;
+ }
+ }
+}
+
+/* also used in netstat */
+void bb_displayroutes(int noresolve, int netstatfmt)
+{
+ char devname[64], flags[16], sdest[16], sgw[16];
+ unsigned long int d, g, m;
+ int flgs, ref, use, metric, mtu, win, ir;
+ struct sockaddr_in s_addr;
+ struct in_addr mask;
+
+ FILE *fp = xfopen("/proc/net/route", "r");
+
+ printf("Kernel IP routing table\n"
+ "Destination Gateway Genmask Flags %s Iface\n",
+ netstatfmt ? " MSS Window irtt" : "Metric Ref Use");
+
+ if (fscanf(fp, "%*[^\n]\n") < 0) { /* Skip the first line. */
+ goto ERROR; /* Empty or missing line, or read error. */
+ }
+ while (1) {
+ int r;
+ r = fscanf(fp, "%63s%lx%lx%X%d%d%d%lx%d%d%d\n",
+ devname, &d, &g, &flgs, &ref, &use, &metric, &m,
+ &mtu, &win, &ir);
+ if (r != 11) {
+ if ((r < 0) && feof(fp)) { /* EOF with no (nonspace) chars read. */
+ break;
+ }
+ ERROR:
+ bb_error_msg_and_die("fscanf");
+ }
+
+ if (!(flgs & RTF_UP)) { /* Skip interfaces that are down. */
+ continue;
+ }
+
+ set_flags(flags, (flgs & IPV4_MASK));
+#ifdef RTF_REJECT
+ if (flgs & RTF_REJECT) {
+ flags[0] = '!';
+ }
+#endif
+
+ memset(&s_addr, 0, sizeof(struct sockaddr_in));
+ s_addr.sin_family = AF_INET;
+ s_addr.sin_addr.s_addr = d;
+ INET_rresolve(sdest, sizeof(sdest), &s_addr,
+ (noresolve | 0x8000), m); /* Default instead of *. */
+
+ s_addr.sin_addr.s_addr = g;
+ INET_rresolve(sgw, sizeof(sgw), &s_addr,
+ (noresolve | 0x4000), m); /* Host instead of net. */
+
+ mask.s_addr = m;
+ printf("%-16s%-16s%-16s%-6s", sdest, sgw, inet_ntoa(mask), flags);
+ if (netstatfmt) {
+ printf("%5d %-5d %6d %s\n", mtu, win, ir, devname);
+ } else {
+ printf("%-6d %-2d %7d %s\n", metric, ref, use, devname);
+ }
+ }
+}
+
+#if ENABLE_FEATURE_IPV6
+
+static void INET6_displayroutes(int noresolve)
+{
+ char addr6[128], naddr6[128];
+ /* In addr6x, we store both 40-byte ':'-delimited ipv6 addresses.
+ * We read the non-delimited strings into the tail of the buffer
+ * using fscanf and then modify the buffer by shifting forward
+ * while inserting ':'s and the nul terminator for the first string.
+ * Hence the strings are at addr6x and addr6x+40. This generates
+ * _much_ less code than the previous (upstream) approach. */
+ char addr6x[80];
+ char iface[16], flags[16];
+ int iflags, metric, refcnt, use, prefix_len, slen;
+ struct sockaddr_in6 snaddr6;
+
+ FILE *fp = xfopen("/proc/net/ipv6_route", "r");
+
+ printf("Kernel IPv6 routing table\n%-44s%-40s"
+ "Flags Metric Ref Use Iface\n",
+ "Destination", "Next Hop");
+
+ while (1) {
+ int r;
+ r = fscanf(fp, "%32s%x%*s%x%32s%x%x%x%x%s\n",
+ addr6x+14, &prefix_len, &slen, addr6x+40+7,
+ &metric, &use, &refcnt, &iflags, iface);
+ if (r != 9) {
+ if ((r < 0) && feof(fp)) { /* EOF with no (nonspace) chars read. */
+ break;
+ }
+ ERROR:
+ bb_error_msg_and_die("fscanf");
+ }
+
+ /* Do the addr6x shift-and-insert changes to ':'-delimit addresses.
+ * For now, always do this to validate the proc route format, even
+ * if the interface is down. */
+ {
+ int i = 0;
+ char *p = addr6x+14;
+
+ do {
+ if (!*p) {
+ if (i==40) { /* nul terminator for 1st address? */
+ addr6x[39] = 0; /* Fixup... need 0 instead of ':'. */
+ ++p; /* Skip and continue. */
+ continue;
+ }
+ goto ERROR;
+ }
+ addr6x[i++] = *p++;
+ if (!((i+1) % 5)) {
+ addr6x[i++] = ':';
+ }
+ } while (i < 40+28+7);
+ }
+
+ if (!(iflags & RTF_UP)) { /* Skip interfaces that are down. */
+ continue;
+ }
+
+ set_flags(flags, (iflags & IPV6_MASK));
+
+ r = 0;
+ do {
+ inet_pton(AF_INET6, addr6x + r,
+ (struct sockaddr *) &snaddr6.sin6_addr);
+ snaddr6.sin6_family = AF_INET6;
+ INET6_rresolve(naddr6, sizeof(naddr6),
+ (struct sockaddr_in6 *) &snaddr6,
+ 0x0fff /* Apparently, upstream never resolves. */
+ );
+
+ if (!r) { /* 1st pass */
+ snprintf(addr6, sizeof(addr6), "%s/%d", naddr6, prefix_len);
+ r += 40;
+ } else { /* 2nd pass */
+ /* Print the info. */
+ printf("%-43s %-39s %-5s %-6d %-2d %7d %-8s\n",
+ addr6, naddr6, flags, metric, refcnt, use, iface);
+ break;
+ }
+ } while (1);
+ }
+}
+
+#endif
+
+#define ROUTE_OPT_A 0x01
+#define ROUTE_OPT_n 0x02
+#define ROUTE_OPT_e 0x04
+#define ROUTE_OPT_INET6 0x08 /* Not an actual option. See below. */
+
+/* 1st byte is offset to next entry offset. 2nd byte is return value. */
+static const char tbl_verb[] = /* 2nd byte matches RTACTION_* code */
+ "\006\001add\0"
+ "\006\002del\0"
+/* "\011\002delete\0" */
+ "\010\002delete" /* Since last, we can save a byte. */
+;
+
+int route_main(int argc, char **argv);
+int route_main(int argc, char **argv)
+{
+ unsigned opt;
+ int what;
+ char *family;
+ char **p;
+
+ /* First, remap '-net' and '-host' to avoid getopt problems. */
+ p = argv;
+ while (*++p) {
+ if (strcmp(*p, "-net") == 0 || strcmp(*p, "-host") == 0) {
+ p[0][0] = '#';
+ }
+ }
+
+ opt = getopt32(argc, argv, "A:ne", &family);
+
+ if ((opt & ROUTE_OPT_A) && strcmp(family, "inet") != 0) {
+#if ENABLE_FEATURE_IPV6
+ if (strcmp(family, "inet6") == 0) {
+ opt |= ROUTE_OPT_INET6; /* Set flag for ipv6. */
+ } else
+#endif
+ bb_show_usage();
+ }
+
+ argv += optind;
+
+ /* No more args means display the routing table. */
+ if (!*argv) {
+ int noresolve = (opt & ROUTE_OPT_n) ? 0x0fff : 0;
+#if ENABLE_FEATURE_IPV6
+ if (opt & ROUTE_OPT_INET6)
+ INET6_displayroutes(noresolve);
+ else
+#endif
+ bb_displayroutes(noresolve, opt & ROUTE_OPT_e);
+
+ fflush_stdout_and_exit(EXIT_SUCCESS);
+ }
+
+ /* Check verb. At the moment, must be add, del, or delete. */
+ what = kw_lookup(tbl_verb, &argv);
+ if (!what || !*argv) { /* Unknown verb or no more args. */
+ bb_show_usage();
+ }
+
+#if ENABLE_FEATURE_IPV6
+ if (opt & ROUTE_OPT_INET6)
+ INET6_setroute(what, argv);
+ else
+#endif
+ INET_setroute(what, argv);
+
+ return EXIT_SUCCESS;
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/telnet.c b/i/pc104/initrd/conf/busybox/networking/telnet.c
new file mode 100644
index 0000000..cd88a9b
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/telnet.c
@@ -0,0 +1,665 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * telnet implementation for busybox
+ *
+ * Author: Tomi Ollila <too@iki.fi>
+ * Copyright (C) 1994-2000 by Tomi Ollila
+ *
+ * Created: Thu Apr 7 13:29:41 1994 too
+ * Last modified: Fri Jun 9 14:34:24 2000 too
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ *
+ * HISTORY
+ * Revision 3.1 1994/04/17 11:31:54 too
+ * initial revision
+ * Modified 2000/06/13 for inclusion into BusyBox by Erik Andersen <andersen@codepoet.org>
+ * Modified 2001/05/07 to add ability to pass TTYPE to remote host by Jim McQuillan
+ * <jam@ltsp.org>
+ * Modified 2004/02/11 to add ability to pass the USER variable to remote host
+ * by Fernando Silveira <swrh@gmx.net>
+ *
+ */
+
+#include <termios.h>
+#include <arpa/telnet.h>
+#include <netinet/in.h>
+#include "busybox.h"
+
+#ifdef DOTRACE
+#define TRACE(x, y) do { if (x) printf y; } while (0)
+#else
+#define TRACE(x, y)
+#endif
+
+enum {
+ DATABUFSIZE = 128,
+ IACBUFSIZE = 128,
+
+ CHM_TRY = 0,
+ CHM_ON = 1,
+ CHM_OFF = 2,
+
+ UF_ECHO = 0x01,
+ UF_SGA = 0x02,
+
+ TS_0 = 1,
+ TS_IAC = 2,
+ TS_OPT = 3,
+ TS_SUB1 = 4,
+ TS_SUB2 = 5,
+};
+
+typedef unsigned char byte;
+
+
+struct globals {
+ int netfd; /* console fd:s are 0 and 1 (and 2) */
+ short iaclen; /* could even use byte */
+ byte telstate; /* telnet negotiation state from network input */
+ byte telwish; /* DO, DONT, WILL, WONT */
+ byte charmode;
+ byte telflags;
+ byte gotsig;
+ byte do_termios;
+#if ENABLE_FEATURE_TELNET_TTYPE
+ char *ttype;
+#endif
+#if ENABLE_FEATURE_TELNET_AUTOLOGIN
+ const char *autologin;
+#endif
+#if ENABLE_FEATURE_AUTOWIDTH
+ int win_width, win_height;
+#endif
+ /* same buffer used both for network and console read/write */
+ char buf[DATABUFSIZE];
+ /* buffer to handle telnet negotiations */
+ char iacbuf[IACBUFSIZE];
+ struct termios termios_def;
+ struct termios termios_raw;
+};
+
+#define G (*(struct globals*)bb_common_bufsiz1)
+
+
+/* Function prototypes */
+static void rawmode(void);
+static void cookmode(void);
+static void do_linemode(void);
+static void will_charmode(void);
+static void telopt(byte c);
+static int subneg(byte c);
+
+static void iacflush(void)
+{
+ write(G.netfd, G.iacbuf, G.iaclen);
+ G.iaclen = 0;
+}
+
+#define write_str(fd, str) write(fd, str, sizeof(str) - 1)
+
+static void doexit(int ev)
+{
+ cookmode();
+ exit(ev);
+}
+
+static void conescape(void)
+{
+ char b;
+
+ if (G.gotsig) /* came from line mode... go raw */
+ rawmode();
+
+ write_str(1, "\r\nConsole escape. Commands are:\r\n\n"
+ " l go to line mode\r\n"
+ " c go to character mode\r\n"
+ " z suspend telnet\r\n"
+ " e exit telnet\r\n");
+
+ if (read(0, &b, 1) <= 0)
+ doexit(1);
+
+ switch (b) {
+ case 'l':
+ if (!G.gotsig) {
+ do_linemode();
+ goto rrturn;
+ }
+ break;
+ case 'c':
+ if (G.gotsig) {
+ will_charmode();
+ goto rrturn;
+ }
+ break;
+ case 'z':
+ cookmode();
+ kill(0, SIGTSTP);
+ rawmode();
+ break;
+ case 'e':
+ doexit(0);
+ }
+
+ write_str(1, "continuing...\r\n");
+
+ if (G.gotsig)
+ cookmode();
+
+ rrturn:
+ G.gotsig = 0;
+
+}
+
+static void handlenetoutput(int len)
+{
+ /* here we could do smart tricks how to handle 0xFF:s in output
+ * stream like writing twice every sequence of FF:s (thus doing
+ * many write()s. But I think interactive telnet application does
+ * not need to be 100% 8-bit clean, so changing every 0xff:s to
+ * 0x7f:s
+ *
+ * 2002-mar-21, Przemyslaw Czerpak (druzus@polbox.com)
+ * I don't agree.
+ * first - I cannot use programs like sz/rz
+ * second - the 0x0D is sent as one character and if the next
+ * char is 0x0A then it's eaten by a server side.
+ * third - whay doy you have to make 'many write()s'?
+ * I don't understand.
+ * So I implemented it. It's realy useful for me. I hope that
+ * others people will find it interesting to.
+ */
+
+ int i, j;
+ byte * p = (byte*)G.buf;
+ byte outbuf[4*DATABUFSIZE];
+
+ for (i = len, j = 0; i > 0; i--, p++)
+ {
+ if (*p == 0x1d)
+ {
+ conescape();
+ return;
+ }
+ outbuf[j++] = *p;
+ if (*p == 0xff)
+ outbuf[j++] = 0xff;
+ else if (*p == 0x0d)
+ outbuf[j++] = 0x00;
+ }
+ if (j > 0 )
+ write(G.netfd, outbuf, j);
+}
+
+static void handlenetinput(int len)
+{
+ int i;
+ int cstart = 0;
+
+ for (i = 0; i < len; i++)
+ {
+ byte c = G.buf[i];
+
+ if (G.telstate == 0) /* most of the time state == 0 */
+ {
+ if (c == IAC)
+ {
+ cstart = i;
+ G.telstate = TS_IAC;
+ }
+ }
+ else
+ switch (G.telstate)
+ {
+ case TS_0:
+ if (c == IAC)
+ G.telstate = TS_IAC;
+ else
+ G.buf[cstart++] = c;
+ break;
+
+ case TS_IAC:
+ if (c == IAC) /* IAC IAC -> 0xFF */
+ {
+ G.buf[cstart++] = c;
+ G.telstate = TS_0;
+ break;
+ }
+ /* else */
+ switch (c)
+ {
+ case SB:
+ G.telstate = TS_SUB1;
+ break;
+ case DO:
+ case DONT:
+ case WILL:
+ case WONT:
+ G.telwish = c;
+ G.telstate = TS_OPT;
+ break;
+ default:
+ G.telstate = TS_0; /* DATA MARK must be added later */
+ }
+ break;
+ case TS_OPT: /* WILL, WONT, DO, DONT */
+ telopt(c);
+ G.telstate = TS_0;
+ break;
+ case TS_SUB1: /* Subnegotiation */
+ case TS_SUB2: /* Subnegotiation */
+ if (subneg(c))
+ G.telstate = TS_0;
+ break;
+ }
+ }
+ if (G.telstate)
+ {
+ if (G.iaclen) iacflush();
+ if (G.telstate == TS_0) G.telstate = 0;
+
+ len = cstart;
+ }
+
+ if (len)
+ write(1, G.buf, len);
+}
+
+static void putiac(int c)
+{
+ G.iacbuf[G.iaclen++] = c;
+}
+
+static void putiac2(byte wwdd, byte c)
+{
+ if (G.iaclen + 3 > IACBUFSIZE)
+ iacflush();
+
+ putiac(IAC);
+ putiac(wwdd);
+ putiac(c);
+}
+
+#if ENABLE_FEATURE_TELNET_TTYPE
+static void putiac_subopt(byte c, char *str)
+{
+ int len = strlen(str) + 6; // ( 2 + 1 + 1 + strlen + 2 )
+
+ if (G.iaclen + len > IACBUFSIZE)
+ iacflush();
+
+ putiac(IAC);
+ putiac(SB);
+ putiac(c);
+ putiac(0);
+
+ while (*str)
+ putiac(*str++);
+
+ putiac(IAC);
+ putiac(SE);
+}
+#endif
+
+#if ENABLE_FEATURE_TELNET_AUTOLOGIN
+static void putiac_subopt_autologin(void)
+{
+ int len = strlen(G.autologin) + 6; // (2 + 1 + 1 + strlen + 2)
+ const char *user = "USER";
+
+ if (G.iaclen + len > IACBUFSIZE)
+ iacflush();
+
+ putiac(IAC);
+ putiac(SB);
+ putiac(TELOPT_NEW_ENVIRON);
+ putiac(TELQUAL_IS);
+ putiac(NEW_ENV_VAR);
+
+ while (*user)
+ putiac(*user++);
+
+ putiac(NEW_ENV_VALUE);
+
+ while (*G.autologin)
+ putiac(*G.autologin++);
+
+ putiac(IAC);
+ putiac(SE);
+}
+#endif
+
+#if ENABLE_FEATURE_AUTOWIDTH
+static void putiac_naws(byte c, int x, int y)
+{
+ if (G.iaclen + 9 > IACBUFSIZE)
+ iacflush();
+
+ putiac(IAC);
+ putiac(SB);
+ putiac(c);
+
+ putiac((x >> 8) & 0xff);
+ putiac(x & 0xff);
+ putiac((y >> 8) & 0xff);
+ putiac(y & 0xff);
+
+ putiac(IAC);
+ putiac(SE);
+}
+#endif
+
+static char const escapecharis[] = "\r\nEscape character is ";
+
+static void setConMode(void)
+{
+ if (G.telflags & UF_ECHO) {
+ if (G.charmode == CHM_TRY) {
+ G.charmode = CHM_ON;
+ printf("\r\nEntering character mode%s'^]'.\r\n", escapecharis);
+ rawmode();
+ }
+ } else {
+ if (G.charmode != CHM_OFF) {
+ G.charmode = CHM_OFF;
+ printf("\r\nEntering line mode%s'^C'.\r\n", escapecharis);
+ cookmode();
+ }
+ }
+}
+
+static void will_charmode(void)
+{
+ G.charmode = CHM_TRY;
+ G.telflags |= (UF_ECHO | UF_SGA);
+ setConMode();
+
+ putiac2(DO, TELOPT_ECHO);
+ putiac2(DO, TELOPT_SGA);
+ iacflush();
+}
+
+static void do_linemode(void)
+{
+ G.charmode = CHM_TRY;
+ G.telflags &= ~(UF_ECHO | UF_SGA);
+ setConMode();
+
+ putiac2(DONT, TELOPT_ECHO);
+ putiac2(DONT, TELOPT_SGA);
+ iacflush();
+}
+
+static void to_notsup(char c)
+{
+ if (G.telwish == WILL)
+ putiac2(DONT, c);
+ else if (G.telwish == DO)
+ putiac2(WONT, c);
+}
+
+static void to_echo(void)
+{
+ /* if server requests ECHO, don't agree */
+ if (G.telwish == DO) {
+ putiac2(WONT, TELOPT_ECHO);
+ return;
+ }
+ if (G.telwish == DONT)
+ return;
+
+ if (G.telflags & UF_ECHO) {
+ if (G.telwish == WILL)
+ return;
+ } else if (G.telwish == WONT)
+ return;
+
+ if (G.charmode != CHM_OFF)
+ G.telflags ^= UF_ECHO;
+
+ if (G.telflags & UF_ECHO)
+ putiac2(DO, TELOPT_ECHO);
+ else
+ putiac2(DONT, TELOPT_ECHO);
+
+ setConMode();
+ write_str(1, "\r\n"); /* sudden modec */
+}
+
+static void to_sga(void)
+{
+ /* daemon always sends will/wont, client do/dont */
+
+ if (G.telflags & UF_SGA) {
+ if (G.telwish == WILL)
+ return;
+ } else if (G.telwish == WONT)
+ return;
+
+ if ((G.telflags ^= UF_SGA) & UF_SGA) /* toggle */
+ putiac2(DO, TELOPT_SGA);
+ else
+ putiac2(DONT, TELOPT_SGA);
+}
+
+#if ENABLE_FEATURE_TELNET_TTYPE
+static void to_ttype(void)
+{
+ /* Tell server we will (or won't) do TTYPE */
+
+ if (G.ttype)
+ putiac2(WILL, TELOPT_TTYPE);
+ else
+ putiac2(WONT, TELOPT_TTYPE);
+}
+#endif
+
+#if ENABLE_FEATURE_TELNET_AUTOLOGIN
+static void to_new_environ(void)
+{
+ /* Tell server we will (or will not) do AUTOLOGIN */
+
+ if (G.autologin)
+ putiac2(WILL, TELOPT_NEW_ENVIRON);
+ else
+ putiac2(WONT, TELOPT_NEW_ENVIRON);
+}
+#endif
+
+#if ENABLE_FEATURE_AUTOWIDTH
+static void to_naws(void)
+{
+ /* Tell server we will do NAWS */
+ putiac2(WILL, TELOPT_NAWS);
+}
+#endif
+
+static void telopt(byte c)
+{
+ switch (c) {
+ case TELOPT_ECHO:
+ to_echo(); break;
+ case TELOPT_SGA:
+ to_sga(); break;
+#if ENABLE_FEATURE_TELNET_TTYPE
+ case TELOPT_TTYPE:
+ to_ttype(); break;
+#endif
+#if ENABLE_FEATURE_TELNET_AUTOLOGIN
+ case TELOPT_NEW_ENVIRON:
+ to_new_environ(); break;
+#endif
+#if ENABLE_FEATURE_AUTOWIDTH
+ case TELOPT_NAWS:
+ to_naws();
+ putiac_naws(c, G.win_width, G.win_height);
+ break;
+#endif
+ default:
+ to_notsup(c);
+ break;
+ }
+}
+
+/* subnegotiation -- ignore all (except TTYPE,NAWS) */
+static int subneg(byte c)
+{
+ switch (G.telstate) {
+ case TS_SUB1:
+ if (c == IAC)
+ G.telstate = TS_SUB2;
+#if ENABLE_FEATURE_TELNET_TTYPE
+ else
+ if (c == TELOPT_TTYPE)
+ putiac_subopt(TELOPT_TTYPE, G.ttype);
+#endif
+#if ENABLE_FEATURE_TELNET_AUTOLOGIN
+ else
+ if (c == TELOPT_NEW_ENVIRON)
+ putiac_subopt_autologin();
+#endif
+ break;
+ case TS_SUB2:
+ if (c == SE)
+ return TRUE;
+ G.telstate = TS_SUB1;
+ /* break; */
+ }
+ return FALSE;
+}
+
+static void fgotsig(int sig)
+{
+ G.gotsig = sig;
+}
+
+
+static void rawmode(void)
+{
+ if (G.do_termios)
+ tcsetattr(0, TCSADRAIN, &G.termios_raw);
+}
+
+static void cookmode(void)
+{
+ if (G.do_termios)
+ tcsetattr(0, TCSADRAIN, &G.termios_def);
+}
+
+void BUG_telnet_globals_too_big(void);
+
+int telnet_main(int argc, char** argv);
+int telnet_main(int argc, char** argv)
+{
+ char *host;
+ int port;
+ int len;
+#ifdef USE_POLL
+ struct pollfd ufds[2];
+#else
+ fd_set readfds;
+ int maxfd;
+#endif
+
+ if (sizeof(G) > sizeof(bb_common_bufsiz1))
+ BUG_telnet_globals_too_big();
+ /* memset(&G, 0, sizeof G); - already is */
+
+#if ENABLE_FEATURE_AUTOWIDTH
+ get_terminal_width_height(0, &G.win_width, &G.win_height);
+#endif
+
+#if ENABLE_FEATURE_TELNET_TTYPE
+ G.ttype = getenv("TERM");
+#endif
+
+ if (tcgetattr(0, &G.termios_def) >= 0) {
+ G.do_termios = 1;
+ G.termios_raw = G.termios_def;
+ cfmakeraw(&G.termios_raw);
+ }
+
+ if (argc < 2)
+ bb_show_usage();
+
+#if ENABLE_FEATURE_TELNET_AUTOLOGIN
+ if (1 & getopt32(argc, argv, "al:", &G.autologin))
+ G.autologin = getenv("USER");
+ argv += optind;
+#else
+ argv++;
+#endif
+ if (!*argv)
+ bb_show_usage();
+ host = *argv++;
+ port = bb_lookup_port(*argv ? *argv++ : "telnet", "tcp", 23);
+ if (*argv) /* extra params?? */
+ bb_show_usage();
+
+ G.netfd = create_and_connect_stream_or_die(host, port);
+
+ setsockopt(G.netfd, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1));
+
+ signal(SIGINT, fgotsig);
+
+#ifdef USE_POLL
+ ufds[0].fd = 0; ufds[1].fd = G.netfd;
+ ufds[0].events = ufds[1].events = POLLIN;
+#else
+ FD_ZERO(&readfds);
+ FD_SET(0, &readfds);
+ FD_SET(G.netfd, &readfds);
+ maxfd = G.netfd + 1;
+#endif
+
+ while (1) {
+#ifndef USE_POLL
+ fd_set rfds = readfds;
+
+ switch (select(maxfd, &rfds, NULL, NULL, NULL))
+#else
+ switch (poll(ufds, 2, -1))
+#endif
+ {
+ case 0:
+ /* timeout */
+ case -1:
+ /* error, ignore and/or log something, bay go to loop */
+ if (G.gotsig)
+ conescape();
+ else
+ sleep(1);
+ break;
+ default:
+
+#ifdef USE_POLL
+ if (ufds[0].revents) /* well, should check POLLIN, but ... */
+#else
+ if (FD_ISSET(0, &rfds))
+#endif
+ {
+ len = read(0, G.buf, DATABUFSIZE);
+ if (len <= 0)
+ doexit(0);
+ TRACE(0, ("Read con: %d\n", len));
+ handlenetoutput(len);
+ }
+
+#ifdef USE_POLL
+ if (ufds[1].revents) /* well, should check POLLIN, but ... */
+#else
+ if (FD_ISSET(G.netfd, &rfds))
+#endif
+ {
+ len = read(G.netfd, G.buf, DATABUFSIZE);
+ if (len <= 0) {
+ write_str(1, "Connection closed by foreign host\r\n");
+ doexit(1);
+ }
+ TRACE(0, ("Read netfd (%d): %d\n", G.netfd, len));
+ handlenetinput(len);
+ }
+ }
+ }
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/telnetd.c b/i/pc104/initrd/conf/busybox/networking/telnetd.c
new file mode 100644
index 0000000..ef9b1ac
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/telnetd.c
@@ -0,0 +1,573 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Simple telnet server
+ * Bjorn Wesen, Axis Communications AB (bjornw@axis.com)
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ *
+ * ---------------------------------------------------------------------------
+ * (C) Copyright 2000, Axis Communications AB, LUND, SWEDEN
+ ****************************************************************************
+ *
+ * The telnetd manpage says it all:
+ *
+ * Telnetd operates by allocating a pseudo-terminal device (see pty(4)) for
+ * a client, then creating a login process which has the slave side of the
+ * pseudo-terminal as stdin, stdout, and stderr. Telnetd manipulates the
+ * master side of the pseudo-terminal, implementing the telnet protocol and
+ * passing characters between the remote client and the login process.
+ *
+ * Vladimir Oleynik <dzo@simtreas.ru> 2001
+ * Set process group corrections, initial busybox port
+ */
+
+/*#define DEBUG 1 */
+#define DEBUG 0
+
+#include "busybox.h"
+
+#if DEBUG
+#define TELCMDS
+#define TELOPTS
+#endif
+#include <arpa/telnet.h>
+#include <sys/syslog.h>
+
+
+#define BUFSIZE 4000
+
+#if ENABLE_LOGIN
+static const char *loginpath = "/bin/login";
+#else
+static const char *loginpath = DEFAULT_SHELL;
+#endif
+
+static const char *issuefile = "/etc/issue.net";
+
+/* shell name and arguments */
+
+static const char *argv_init[2];
+
+/* structure that describes a session */
+
+struct tsession {
+ struct tsession *next;
+ int sockfd_read, sockfd_write, ptyfd;
+ int shell_pid;
+ /* two circular buffers */
+ char *buf1, *buf2;
+ int rdidx1, wridx1, size1;
+ int rdidx2, wridx2, size2;
+};
+
+/*
+ This is how the buffers are used. The arrows indicate the movement
+ of data.
+
+ +-------+ wridx1++ +------+ rdidx1++ +----------+
+ | | <-------------- | buf1 | <-------------- | |
+ | | size1-- +------+ size1++ | |
+ | pty | | socket |
+ | | rdidx2++ +------+ wridx2++ | |
+ | | --------------> | buf2 | --------------> | |
+ +-------+ size2++ +------+ size2-- +----------+
+
+ Each session has got two buffers.
+*/
+
+static int maxfd;
+
+static struct tsession *sessions;
+
+
+/*
+ Remove all IAC's from the buffer pointed to by bf (received IACs are ignored
+ and must be removed so as to not be interpreted by the terminal). Make an
+ uninterrupted string of characters fit for the terminal. Do this by packing
+ all characters meant for the terminal sequentially towards the end of bf.
+
+ Return a pointer to the beginning of the characters meant for the terminal.
+ and make *num_totty the number of characters that should be sent to
+ the terminal.
+
+ Note - If an IAC (3 byte quantity) starts before (bf + len) but extends
+ past (bf + len) then that IAC will be left unprocessed and *processed will be
+ less than len.
+
+ FIXME - if we mean to send 0xFF to the terminal then it will be escaped,
+ what is the escape character? We aren't handling that situation here.
+
+ CR-LF ->'s CR mapping is also done here, for convenience
+ */
+static char *
+remove_iacs(struct tsession *ts, int *pnum_totty)
+{
+ unsigned char *ptr0 = (unsigned char *)ts->buf1 + ts->wridx1;
+ unsigned char *ptr = ptr0;
+ unsigned char *totty = ptr;
+ unsigned char *end = ptr + MIN(BUFSIZE - ts->wridx1, ts->size1);
+ int processed;
+ int num_totty;
+
+ while (ptr < end) {
+ if (*ptr != IAC) {
+ int c = *ptr;
+ *totty++ = *ptr++;
+ /* We now map \r\n ==> \r for pragmatic reasons.
+ * Many client implementations send \r\n when
+ * the user hits the CarriageReturn key.
+ */
+ if (c == '\r' && (*ptr == '\n' || *ptr == 0) && ptr < end)
+ ptr++;
+ } else {
+ /*
+ * TELOPT_NAWS support!
+ */
+ if ((ptr+2) >= end) {
+ /* only the beginning of the IAC is in the
+ buffer we were asked to process, we can't
+ process this char. */
+ break;
+ }
+
+ /*
+ * IAC -> SB -> TELOPT_NAWS -> 4-byte -> IAC -> SE
+ */
+ else if (ptr[1] == SB && ptr[2] == TELOPT_NAWS) {
+ struct winsize ws;
+ if ((ptr+8) >= end)
+ break; /* incomplete, can't process */
+ ws.ws_col = (ptr[3] << 8) | ptr[4];
+ ws.ws_row = (ptr[5] << 8) | ptr[6];
+ ioctl(ts->ptyfd, TIOCSWINSZ, (char *)&ws);
+ ptr += 9;
+ } else {
+ /* skip 3-byte IAC non-SB cmd */
+#if DEBUG
+ fprintf(stderr, "Ignoring IAC %s,%s\n",
+ TELCMD(ptr[1]), TELOPT(ptr[2]));
+#endif
+ ptr += 3;
+ }
+ }
+ }
+
+ processed = ptr - ptr0;
+ num_totty = totty - ptr0;
+ /* the difference between processed and num_to tty
+ is all the iacs we removed from the stream.
+ Adjust buf1 accordingly. */
+ ts->wridx1 += processed - num_totty;
+ ts->size1 -= processed - num_totty;
+ *pnum_totty = num_totty;
+ /* move the chars meant for the terminal towards the end of the
+ buffer. */
+ return memmove(ptr - num_totty, ptr0, num_totty);
+}
+
+
+static int
+getpty(char *line, int size)
+{
+ int p;
+#if ENABLE_FEATURE_DEVPTS
+ p = open("/dev/ptmx", O_RDWR);
+ if (p > 0) {
+ const char *name;
+ grantpt(p);
+ unlockpt(p);
+ name = ptsname(p);
+ if (!name) {
+ bb_perror_msg("ptsname error (is /dev/pts mounted?)");
+ return -1;
+ }
+ safe_strncpy(line, name, size);
+ return p;
+ }
+#else
+ struct stat stb;
+ int i;
+ int j;
+
+ strcpy(line, "/dev/ptyXX");
+
+ for (i = 0; i < 16; i++) {
+ line[8] = "pqrstuvwxyzabcde"[i];
+ line[9] = '0';
+ if (stat(line, &stb) < 0) {
+ continue;
+ }
+ for (j = 0; j < 16; j++) {
+ line[9] = j < 10 ? j + '0' : j - 10 + 'a';
+ if (DEBUG)
+ fprintf(stderr, "Trying to open device: %s\n", line);
+ p = open(line, O_RDWR | O_NOCTTY);
+ if (p >= 0) {
+ line[5] = 't';
+ return p;
+ }
+ }
+ }
+#endif /* FEATURE_DEVPTS */
+ return -1;
+}
+
+
+static void
+send_iac(struct tsession *ts, unsigned char command, int option)
+{
+ /* We rely on that there is space in the buffer for now. */
+ char *b = ts->buf2 + ts->rdidx2;
+ *b++ = IAC;
+ *b++ = command;
+ *b++ = option;
+ ts->rdidx2 += 3;
+ ts->size2 += 3;
+}
+
+
+static struct tsession *
+make_new_session(
+ USE_FEATURE_TELNETD_STANDALONE(int sock_r, int sock_w)
+ SKIP_FEATURE_TELNETD_STANDALONE(void)
+) {
+ struct termios termbuf;
+ int fd, pid;
+ char tty_name[32];
+ struct tsession *ts = xzalloc(sizeof(struct tsession) + BUFSIZE * 2);
+
+ ts->buf1 = (char *)(&ts[1]);
+ ts->buf2 = ts->buf1 + BUFSIZE;
+
+ /* Got a new connection, set up a tty. */
+ fd = getpty(tty_name, 32);
+ if (fd < 0) {
+ bb_error_msg("all terminals in use");
+ return NULL;
+ }
+ if (fd > maxfd) maxfd = fd;
+ ndelay_on(ts->ptyfd = fd);
+#if ENABLE_FEATURE_TELNETD_STANDALONE
+ if (sock_w > maxfd) maxfd = sock_w;
+ if (sock_r > maxfd) maxfd = sock_r;
+ ndelay_on(ts->sockfd_write = sock_w);
+ ndelay_on(ts->sockfd_read = sock_r);
+#else
+ ts->sockfd_write = 1;
+ /* xzalloc: ts->sockfd_read = 0; */
+ ndelay_on(0);
+ ndelay_on(1);
+#endif
+ /* Make the telnet client understand we will echo characters so it
+ * should not do it locally. We don't tell the client to run linemode,
+ * because we want to handle line editing and tab completion and other
+ * stuff that requires char-by-char support. */
+ send_iac(ts, DO, TELOPT_ECHO);
+ send_iac(ts, DO, TELOPT_NAWS);
+ send_iac(ts, DO, TELOPT_LFLOW);
+ send_iac(ts, WILL, TELOPT_ECHO);
+ send_iac(ts, WILL, TELOPT_SGA);
+
+ pid = fork();
+ if (pid < 0) {
+ free(ts);
+ close(fd);
+ bb_perror_msg("fork");
+ return NULL;
+ }
+ if (pid > 0) {
+ /* parent */
+ ts->shell_pid = pid;
+ return ts;
+ }
+
+ /* child */
+
+ /* make new process group */
+ setsid();
+ tcsetpgrp(0, getpid());
+ /* ^^^ strace says: "ioctl(0, TIOCSPGRP, [pid]) = -1 ENOTTY" -- ??! */
+
+ /* open the child's side of the tty. */
+ /* NB: setsid() disconnects from any previous ctty's. Therefore
+ * we must open child's side of the tty AFTER setsid! */
+ fd = xopen(tty_name, O_RDWR); /* becomes our ctty */
+ dup2(fd, 0);
+ dup2(fd, 1);
+ dup2(fd, 2);
+ while (fd > 2) close(fd--);
+
+ /* The pseudo-terminal allocated to the client is configured to operate in
+ * cooked mode, and with XTABS CRMOD enabled (see tty(4)). */
+ tcgetattr(0, &termbuf);
+ termbuf.c_lflag |= ECHO; /* if we use readline we dont want this */
+ termbuf.c_oflag |= ONLCR|XTABS;
+ termbuf.c_iflag |= ICRNL;
+ termbuf.c_iflag &= ~IXOFF;
+ /*termbuf.c_lflag &= ~ICANON;*/
+ tcsetattr(0, TCSANOW, &termbuf);
+
+ print_login_issue(issuefile, NULL);
+
+ /* exec shell, with correct argv and env */
+ execv(loginpath, (char *const *)argv_init);
+ bb_perror_msg_and_die("execv");
+}
+
+#if ENABLE_FEATURE_TELNETD_STANDALONE
+
+static void
+free_session(struct tsession *ts)
+{
+ struct tsession *t = sessions;
+
+ /* unlink this telnet session from the session list */
+ if (t == ts)
+ sessions = ts->next;
+ else {
+ while (t->next != ts)
+ t = t->next;
+ t->next = ts->next;
+ }
+
+ kill(ts->shell_pid, SIGKILL);
+ wait4(ts->shell_pid, NULL, 0, NULL);
+ close(ts->ptyfd);
+ close(ts->sockfd_read);
+ /* error if ts->sockfd_read == ts->sockfd_write. So what? ;) */
+ close(ts->sockfd_write);
+ free(ts);
+
+ /* scan all sessions and find new maxfd */
+ ts = sessions;
+ maxfd = 0;
+ while (ts) {
+ if (maxfd < ts->ptyfd)
+ maxfd = ts->ptyfd;
+ if (maxfd < ts->sockfd_read)
+ maxfd = ts->sockfd_read;
+ if (maxfd < ts->sockfd_write)
+ maxfd = ts->sockfd_write;
+ ts = ts->next;
+ }
+}
+
+#else /* !FEATURE_TELNETD_STANDALONE */
+
+/* Never actually called */
+void free_session(struct tsession *ts);
+
+#endif
+
+
+int telnetd_main(int argc, char **argv);
+int telnetd_main(int argc, char **argv)
+{
+ fd_set rdfdset, wrfdset;
+ unsigned opt;
+ int selret, maxlen, w, r;
+ struct tsession *ts;
+#if ENABLE_FEATURE_TELNETD_STANDALONE
+#define IS_INETD (opt & OPT_INETD)
+ int master_fd = -1; /* be happy, gcc */
+ unsigned portnbr = 23;
+ char *opt_bindaddr = NULL;
+ char *opt_portnbr;
+#else
+ enum {
+ IS_INETD = 1,
+ master_fd = -1,
+ portnbr = 23,
+ };
+#endif
+ enum {
+ OPT_PORT = 4 * ENABLE_FEATURE_TELNETD_STANDALONE,
+ OPT_FOREGROUND = 0x10 * ENABLE_FEATURE_TELNETD_STANDALONE,
+ OPT_INETD = 0x20 * ENABLE_FEATURE_TELNETD_STANDALONE,
+ };
+
+ opt = getopt32(argc, argv, "f:l:" USE_FEATURE_TELNETD_STANDALONE("p:b:Fi"),
+ &issuefile, &loginpath
+ USE_FEATURE_TELNETD_STANDALONE(, &opt_portnbr, &opt_bindaddr));
+ /* Redirect log to syslog early, if needed */
+ if (IS_INETD || !(opt & OPT_FOREGROUND)) {
+ openlog(applet_name, 0, LOG_USER);
+ logmode = LOGMODE_SYSLOG;
+ }
+ //if (opt & 1) // -f
+ //if (opt & 2) // -l
+ USE_FEATURE_TELNETD_STANDALONE(
+ if (opt & OPT_PORT) // -p
+ portnbr = xatou16(opt_portnbr);
+ //if (opt & 8) // -b
+ //if (opt & 0x10) // -F
+ //if (opt & 0x20) // -i
+ );
+
+ /* Used to check access(loginpath, X_OK) here. Pointless.
+ * exec will do this for us for free later. */
+ argv_init[0] = loginpath;
+
+#if ENABLE_FEATURE_TELNETD_STANDALONE
+ if (IS_INETD) {
+ sessions = make_new_session(0, 1);
+ } else {
+ master_fd = create_and_bind_stream_or_die(opt_bindaddr, portnbr);
+ xlisten(master_fd, 1);
+ if (!(opt & OPT_FOREGROUND))
+ xdaemon(0, 0);
+ }
+#else
+ sessions = make_new_session();
+#endif
+
+ /* We don't want to die if just one session is broken */
+ signal(SIGPIPE, SIG_IGN);
+
+ again:
+ FD_ZERO(&rdfdset);
+ FD_ZERO(&wrfdset);
+ if (!IS_INETD) {
+ FD_SET(master_fd, &rdfdset);
+ /* This is needed because free_session() does not
+ * take into account master_fd when it finds new
+ * maxfd among remaining fd's: */
+ if (master_fd > maxfd)
+ maxfd = master_fd;
+ }
+
+ /* select on the master socket, all telnet sockets and their
+ * ptys if there is room in their session buffers. */
+ ts = sessions;
+ while (ts) {
+ /* buf1 is used from socket to pty
+ * buf2 is used from pty to socket */
+ if (ts->size1 > 0) /* can write to pty */
+ FD_SET(ts->ptyfd, &wrfdset);
+ if (ts->size1 < BUFSIZE) /* can read from socket */
+ FD_SET(ts->sockfd_read, &rdfdset);
+ if (ts->size2 > 0) /* can write to socket */
+ FD_SET(ts->sockfd_write, &wrfdset);
+ if (ts->size2 < BUFSIZE) /* can read from pty */
+ FD_SET(ts->ptyfd, &rdfdset);
+ ts = ts->next;
+ }
+
+ selret = select(maxfd + 1, &rdfdset, &wrfdset, 0, 0);
+ if (!selret)
+ return 0;
+
+#if ENABLE_FEATURE_TELNETD_STANDALONE
+ /* First check for and accept new sessions. */
+ if (!IS_INETD && FD_ISSET(master_fd, &rdfdset)) {
+ int fd;
+ struct tsession *new_ts;
+
+ fd = accept(master_fd, NULL, 0);
+ if (fd < 0)
+ goto again;
+ /* Create a new session and link it into our active list */
+ new_ts = make_new_session(fd, fd);
+ if (new_ts) {
+ new_ts->next = sessions;
+ sessions = new_ts;
+ } else {
+ close(fd);
+ }
+ }
+#endif
+
+ /* Then check for data tunneling. */
+ ts = sessions;
+ while (ts) { /* For all sessions... */
+ struct tsession *next = ts->next; /* in case we free ts. */
+
+ if (ts->size1 && FD_ISSET(ts->ptyfd, &wrfdset)) {
+ int num_totty;
+ char *ptr;
+ /* Write to pty from buffer 1. */
+ ptr = remove_iacs(ts, &num_totty);
+ w = safe_write(ts->ptyfd, ptr, num_totty);
+ /* needed? if (w < 0 && errno == EAGAIN) continue; */
+ if (w < 0) {
+ if (IS_INETD)
+ return 0;
+ free_session(ts);
+ ts = next;
+ continue;
+ }
+ ts->wridx1 += w;
+ ts->size1 -= w;
+ if (ts->wridx1 == BUFSIZE)
+ ts->wridx1 = 0;
+ }
+
+ if (ts->size2 && FD_ISSET(ts->sockfd_write, &wrfdset)) {
+ /* Write to socket from buffer 2. */
+ maxlen = MIN(BUFSIZE - ts->wridx2, ts->size2);
+ w = safe_write(ts->sockfd_write, ts->buf2 + ts->wridx2, maxlen);
+ /* needed? if (w < 0 && errno == EAGAIN) continue; */
+ if (w < 0) {
+ if (IS_INETD)
+ return 0;
+ free_session(ts);
+ ts = next;
+ continue;
+ }
+ ts->wridx2 += w;
+ ts->size2 -= w;
+ if (ts->wridx2 == BUFSIZE)
+ ts->wridx2 = 0;
+ }
+
+ if (ts->size1 < BUFSIZE && FD_ISSET(ts->sockfd_read, &rdfdset)) {
+ /* Read from socket to buffer 1. */
+ maxlen = MIN(BUFSIZE - ts->rdidx1, BUFSIZE - ts->size1);
+ r = safe_read(ts->sockfd_read, ts->buf1 + ts->rdidx1, maxlen);
+ if (r < 0 && errno == EAGAIN) continue;
+ if (r <= 0) {
+ if (IS_INETD)
+ return 0;
+ free_session(ts);
+ ts = next;
+ continue;
+ }
+ if (!ts->buf1[ts->rdidx1 + r - 1])
+ if (!--r)
+ continue;
+ ts->rdidx1 += r;
+ ts->size1 += r;
+ if (ts->rdidx1 == BUFSIZE)
+ ts->rdidx1 = 0;
+ }
+
+ if (ts->size2 < BUFSIZE && FD_ISSET(ts->ptyfd, &rdfdset)) {
+ /* Read from pty to buffer 2. */
+ maxlen = MIN(BUFSIZE - ts->rdidx2, BUFSIZE - ts->size2);
+ r = safe_read(ts->ptyfd, ts->buf2 + ts->rdidx2, maxlen);
+ if (r < 0 && errno == EAGAIN) continue;
+ if (r <= 0) {
+ if (IS_INETD)
+ return 0;
+ free_session(ts);
+ ts = next;
+ continue;
+ }
+ ts->rdidx2 += r;
+ ts->size2 += r;
+ if (ts->rdidx2 == BUFSIZE)
+ ts->rdidx2 = 0;
+ }
+
+ if (ts->size1 == 0) {
+ ts->rdidx1 = 0;
+ ts->wridx1 = 0;
+ }
+ if (ts->size2 == 0) {
+ ts->rdidx2 = 0;
+ ts->wridx2 = 0;
+ }
+ ts = next;
+ }
+ goto again;
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/tftp.c b/i/pc104/initrd/conf/busybox/networking/tftp.c
new file mode 100644
index 0000000..bbed9ac
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/tftp.c
@@ -0,0 +1,518 @@
+/* vi: set sw=4 ts=4: */
+/* -------------------------------------------------------------------------
+ * tftp.c
+ *
+ * A simple tftp client for busybox.
+ * Tries to follow RFC1350.
+ * Only "octet" mode supported.
+ * Optional blocksize negotiation (RFC2347 + RFC2348)
+ *
+ * Copyright (C) 2001 Magnus Damm <damm@opensource.se>
+ *
+ * Parts of the code based on:
+ *
+ * atftp: Copyright (C) 2000 Jean-Pierre Lefebvre <helix@step.polymtl.ca>
+ * and Remi Lefebvre <remi@debian.org>
+ *
+ * utftp: Copyright (C) 1999 Uwe Ohse <uwe@ohse.de>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * ------------------------------------------------------------------------- */
+
+#include "busybox.h"
+
+
+#if ENABLE_FEATURE_TFTP_GET || ENABLE_FEATURE_TFTP_PUT
+
+#define TFTP_BLOCKSIZE_DEFAULT 512 /* according to RFC 1350, don't change */
+#define TFTP_TIMEOUT 5 /* seconds */
+#define TFTP_NUM_RETRIES 5 /* number of retries */
+
+/* opcodes we support */
+#define TFTP_RRQ 1
+#define TFTP_WRQ 2
+#define TFTP_DATA 3
+#define TFTP_ACK 4
+#define TFTP_ERROR 5
+#define TFTP_OACK 6
+
+static const char *const tftp_bb_error_msg[] = {
+ "Undefined error",
+ "File not found",
+ "Access violation",
+ "Disk full or allocation error",
+ "Illegal TFTP operation",
+ "Unknown transfer ID",
+ "File already exists",
+ "No such user"
+};
+
+#if ENABLE_FEATURE_TFTP_GET && !ENABLE_FEATURE_TFTP_PUT
+#define USE_GETPUT(a)
+#define CMD_GET(cmd) 1
+#define CMD_PUT(cmd) 0
+#elif !ENABLE_FEATURE_TFTP_GET && ENABLE_FEATURE_TFTP_PUT
+#define USE_GETPUT(a)
+#define CMD_GET(cmd) 0
+#define CMD_PUT(cmd) 1
+#else
+#define USE_GETPUT(a) a
+/* masks coming from getpot32 */
+#define CMD_GET(cmd) ((cmd) & 1)
+#define CMD_PUT(cmd) ((cmd) & 2)
+#endif
+/* NB: in the code below
+ * CMD_GET(cmd) and CMD_GET(cmd) are mutually exclusive
+ */
+
+
+#if ENABLE_FEATURE_TFTP_BLOCKSIZE
+
+static int tftp_blocksize_check(int blocksize, int bufsize)
+{
+ /* Check if the blocksize is valid:
+ * RFC2348 says between 8 and 65464,
+ * but our implementation makes it impossible
+ * to use blocksizes smaller than 22 octets.
+ */
+
+ if ((bufsize && (blocksize > bufsize))
+ || (blocksize < 8) || (blocksize > 65564)
+ ) {
+ bb_error_msg("bad blocksize");
+ return 0;
+ }
+
+ return blocksize;
+}
+
+static char *tftp_option_get(char *buf, int len, const char * const option)
+{
+ int opt_val = 0;
+ int opt_found = 0;
+ int k;
+
+ while (len > 0) {
+ /* Make sure the options are terminated correctly */
+
+ for (k = 0; k < len; k++) {
+ if (buf[k] == '\0') {
+ break;
+ }
+ }
+
+ if (k >= len) {
+ break;
+ }
+
+ if (opt_val == 0) {
+ if (strcasecmp(buf, option) == 0) {
+ opt_found = 1;
+ }
+ } else {
+ if (opt_found) {
+ return buf;
+ }
+ }
+
+ k++;
+
+ buf += k;
+ len -= k;
+
+ opt_val ^= 1;
+ }
+
+ return NULL;
+}
+
+#endif
+
+static int tftp(
+#if ENABLE_FEATURE_TFTP_GET && ENABLE_FEATURE_TFTP_PUT
+ const int cmd,
+#endif
+ len_and_sockaddr *peer_lsa,
+ const char *remotefile, const int localfd,
+ unsigned port, int tftp_bufsize)
+{
+ struct timeval tv;
+ fd_set rfds;
+ int socketfd;
+ int len;
+ int opcode = 0;
+ int finished = 0;
+ int timeout = TFTP_NUM_RETRIES;
+ uint16_t block_nr = 1;
+ uint16_t tmp;
+ char *cp;
+
+ USE_FEATURE_TFTP_BLOCKSIZE(int want_option_ack = 0;)
+
+ unsigned org_port;
+ len_and_sockaddr *const from = alloca(offsetof(len_and_sockaddr, sa) + peer_lsa->len);
+
+ /* Can't use RESERVE_CONFIG_BUFFER here since the allocation
+ * size varies meaning BUFFERS_GO_ON_STACK would fail */
+ /* We must keep the transmit and receive buffers seperate */
+ /* In case we rcv a garbage pkt and we need to rexmit the last pkt */
+ char *xbuf = xmalloc(tftp_bufsize += 4);
+ char *rbuf = xmalloc(tftp_bufsize);
+
+ port = org_port = htons(port);
+
+ socketfd = xsocket(peer_lsa->sa.sa_family, SOCK_DGRAM, 0);
+
+ /* build opcode */
+ opcode = TFTP_WRQ;
+ if (CMD_GET(cmd)) {
+ opcode = TFTP_RRQ;
+ }
+
+ while (1) {
+ cp = xbuf;
+
+ /* first create the opcode part */
+ /* (this 16bit store is aligned) */
+ *((uint16_t*)cp) = htons(opcode);
+ cp += 2;
+
+ /* add filename and mode */
+ if (CMD_GET(cmd) ? (opcode == TFTP_RRQ) : (opcode == TFTP_WRQ)) {
+ int too_long = 0;
+
+ /* see if the filename fits into xbuf
+ * and fill in packet. */
+ len = strlen(remotefile) + 1;
+
+ if ((cp + len) >= &xbuf[tftp_bufsize - 1]) {
+ too_long = 1;
+ } else {
+ safe_strncpy(cp, remotefile, len);
+ cp += len;
+ }
+
+ if (too_long || (&xbuf[tftp_bufsize - 1] - cp) < sizeof("octet")) {
+ bb_error_msg("remote filename too long");
+ break;
+ }
+
+ /* add "mode" part of the package */
+ memcpy(cp, "octet", sizeof("octet"));
+ cp += sizeof("octet");
+
+#if ENABLE_FEATURE_TFTP_BLOCKSIZE
+
+ len = tftp_bufsize - 4; /* data block size */
+
+ if (len != TFTP_BLOCKSIZE_DEFAULT) {
+
+ if ((&xbuf[tftp_bufsize - 1] - cp) < 15) {
+ bb_error_msg("remote filename too long");
+ break;
+ }
+
+ /* add "blksize" + number of blocks */
+ memcpy(cp, "blksize", sizeof("blksize"));
+ cp += sizeof("blksize");
+ cp += snprintf(cp, 6, "%d", len) + 1;
+
+ want_option_ack = 1;
+ }
+#endif
+ }
+
+ /* add ack and data */
+
+ if (CMD_GET(cmd) ? (opcode == TFTP_ACK) : (opcode == TFTP_DATA)) {
+ /* TODO: unaligned access! */
+ *((uint16_t*)cp) = htons(block_nr);
+ cp += 2;
+ block_nr++;
+
+ if (CMD_PUT(cmd) && (opcode == TFTP_DATA)) {
+ len = full_read(localfd, cp, tftp_bufsize - 4);
+
+ if (len < 0) {
+ bb_perror_msg(bb_msg_read_error);
+ break;
+ }
+
+ if (len != (tftp_bufsize - 4)) {
+ finished++;
+ }
+
+ cp += len;
+ }
+ }
+
+ /* send packet */
+
+ timeout = TFTP_NUM_RETRIES; /* re-initialize */
+ do {
+ len = cp - xbuf;
+#if ENABLE_DEBUG_TFTP
+ fprintf(stderr, "sending %u bytes\n", len);
+ for (cp = xbuf; cp < &xbuf[len]; cp++)
+ fprintf(stderr, "%02x ", (unsigned char) *cp);
+ fprintf(stderr, "\n");
+#endif
+ if (sendto(socketfd, xbuf, len, 0,
+ &peer_lsa->sa, peer_lsa->len) < 0) {
+ bb_perror_msg("send");
+ len = -1;
+ break;
+ }
+
+ if (finished && (opcode == TFTP_ACK)) {
+ break;
+ }
+
+ /* receive packet */
+ recv_again:
+ tv.tv_sec = TFTP_TIMEOUT;
+ tv.tv_usec = 0;
+
+ FD_ZERO(&rfds);
+ FD_SET(socketfd, &rfds);
+
+ switch (select(socketfd + 1, &rfds, NULL, NULL, &tv)) {
+ unsigned from_port;
+ case 1:
+ from->len = peer_lsa->len;
+ memset(&from->sa, 0, peer_lsa->len);
+ len = recvfrom(socketfd, rbuf, tftp_bufsize, 0,
+ &from->sa, &from->len);
+ if (len < 0) {
+ bb_perror_msg("recvfrom");
+ break;
+ }
+ from_port = get_nport(from);
+ if (port == org_port) {
+ /* Our first query went to port 69
+ * but reply will come from different one.
+ * Remember and use this new port */
+ port = from_port;
+ set_nport(peer_lsa, from_port);
+ }
+ if (port != from_port)
+ goto recv_again;
+ timeout = 0;
+ break;
+ case 0:
+ bb_error_msg("timeout");
+ timeout--;
+ if (timeout == 0) {
+ len = -1;
+ bb_error_msg("last timeout");
+ }
+ break;
+ default:
+ bb_perror_msg("select");
+ len = -1;
+ }
+
+ } while (timeout && (len >= 0));
+
+ if (finished || (len < 0)) {
+ break;
+ }
+
+ /* process received packet */
+ /* (both accesses seems to be aligned) */
+
+ opcode = ntohs( ((uint16_t*)rbuf)[0] );
+ tmp = ntohs( ((uint16_t*)rbuf)[1] );
+
+#if ENABLE_DEBUG_TFTP
+ fprintf(stderr, "received %d bytes: %04x %04x\n", len, opcode, tmp);
+#endif
+
+ if (opcode == TFTP_ERROR) {
+ const char *msg = NULL;
+
+ if (rbuf[4] != '\0') {
+ msg = &rbuf[4];
+ rbuf[tftp_bufsize - 1] = '\0';
+ } else if (tmp < (sizeof(tftp_bb_error_msg)
+ / sizeof(char *))) {
+ msg = tftp_bb_error_msg[tmp];
+ }
+
+ if (msg) {
+ bb_error_msg("server says: %s", msg);
+ }
+
+ break;
+ }
+#if ENABLE_FEATURE_TFTP_BLOCKSIZE
+ if (want_option_ack) {
+
+ want_option_ack = 0;
+
+ if (opcode == TFTP_OACK) {
+ /* server seems to support options */
+ char *res;
+
+ res = tftp_option_get(&rbuf[2], len - 2, "blksize");
+
+ if (res) {
+ int blksize = xatoi_u(res);
+
+ if (tftp_blocksize_check(blksize, tftp_bufsize - 4)) {
+ if (CMD_PUT(cmd)) {
+ opcode = TFTP_DATA;
+ } else {
+ opcode = TFTP_ACK;
+ }
+#if ENABLE_DEBUG_TFTP
+ fprintf(stderr, "using blksize %u\n",
+ blksize);
+#endif
+ tftp_bufsize = blksize + 4;
+ block_nr = 0;
+ continue;
+ }
+ }
+ /* FIXME:
+ * we should send ERROR 8 */
+ bb_error_msg("bad server option");
+ break;
+ }
+
+ bb_error_msg("warning: blksize not supported by server"
+ " - reverting to 512");
+
+ tftp_bufsize = TFTP_BLOCKSIZE_DEFAULT + 4;
+ }
+#endif
+
+ if (CMD_GET(cmd) && (opcode == TFTP_DATA)) {
+ if (tmp == block_nr) {
+ len = full_write(localfd, &rbuf[4], len - 4);
+
+ if (len < 0) {
+ bb_perror_msg(bb_msg_write_error);
+ break;
+ }
+
+ if (len != (tftp_bufsize - 4)) {
+ finished++;
+ }
+
+ opcode = TFTP_ACK;
+ continue;
+ }
+ /* in case the last ack disappeared into the ether */
+ if (tmp == (block_nr - 1)) {
+ --block_nr;
+ opcode = TFTP_ACK;
+ continue;
+// tmp==(block_nr-1) and (tmp+1)==block_nr is always same, I think. wtf?
+ } else if (tmp + 1 == block_nr) {
+ /* Server lost our TFTP_ACK. Resend it */
+ block_nr = tmp;
+ opcode = TFTP_ACK;
+ continue;
+ }
+ }
+
+ if (CMD_PUT(cmd) && (opcode == TFTP_ACK)) {
+ if (tmp == (uint16_t) (block_nr - 1)) {
+ if (finished) {
+ break;
+ }
+
+ opcode = TFTP_DATA;
+ continue;
+ }
+ }
+ }
+
+ if (ENABLE_FEATURE_CLEAN_UP) {
+ close(socketfd);
+ free(xbuf);
+ free(rbuf);
+ }
+
+ return finished ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
+int tftp_main(int argc, char **argv);
+int tftp_main(int argc, char **argv)
+{
+ len_and_sockaddr *peer_lsa;
+ const char *localfile = NULL;
+ const char *remotefile = NULL;
+#if ENABLE_FEATURE_TFTP_BLOCKSIZE
+ const char *sblocksize = NULL;
+#endif
+ int port;
+ USE_GETPUT(int cmd;)
+ int fd = -1;
+ int flags = 0;
+ int result;
+ int blocksize = TFTP_BLOCKSIZE_DEFAULT;
+
+ /* -p or -g is mandatory, and they are mutually exclusive */
+ opt_complementary = "" USE_FEATURE_TFTP_GET("g:") USE_FEATURE_TFTP_PUT("p:")
+ USE_GETPUT("?g--p:p--g");
+
+ USE_GETPUT(cmd =) getopt32(argc, argv,
+ USE_FEATURE_TFTP_GET("g") USE_FEATURE_TFTP_PUT("p")
+ "l:r:" USE_FEATURE_TFTP_BLOCKSIZE("b:"),
+ &localfile, &remotefile
+ USE_FEATURE_TFTP_BLOCKSIZE(, &sblocksize));
+
+ flags = O_RDONLY;
+ if (CMD_GET(cmd))
+ flags = O_WRONLY | O_CREAT | O_TRUNC;
+
+#if ENABLE_FEATURE_TFTP_BLOCKSIZE
+ if (sblocksize) {
+ blocksize = xatoi_u(sblocksize);
+ if (!tftp_blocksize_check(blocksize, 0)) {
+ return EXIT_FAILURE;
+ }
+ }
+#endif
+
+ if (localfile == NULL)
+ localfile = remotefile;
+ if (remotefile == NULL)
+ remotefile = localfile;
+ if ((localfile == NULL && remotefile == NULL) || (argv[optind] == NULL))
+ bb_show_usage();
+
+ if (localfile == NULL || LONE_DASH(localfile)) {
+ fd = CMD_GET(cmd) ? STDOUT_FILENO : STDIN_FILENO;
+ } else {
+ fd = xopen3(localfile, flags, 0644);
+ }
+
+ port = bb_lookup_port(argv[optind + 1], "udp", 69);
+ peer_lsa = xhost2sockaddr(argv[optind], port);
+
+#if ENABLE_DEBUG_TFTP
+ fprintf(stderr, "using server \"%s\", "
+ "remotefile \"%s\", localfile \"%s\".\n",
+ xmalloc_sockaddr2dotted(&peer_lsa->sa, peer_lsa->len),
+ remotefile, localfile);
+#endif
+
+ result = tftp(
+#if ENABLE_FEATURE_TFTP_GET && ENABLE_FEATURE_TFTP_PUT
+ cmd,
+#endif
+ peer_lsa, remotefile, fd, port, blocksize);
+
+ if (fd > 1) {
+ if (ENABLE_FEATURE_CLEAN_UP)
+ close(fd);
+ if (CMD_GET(cmd) && result != EXIT_SUCCESS)
+ unlink(localfile);
+ }
+ return result;
+}
+
+#endif /* ENABLE_FEATURE_TFTP_GET || ENABLE_FEATURE_TFTP_PUT */
diff --git a/i/pc104/initrd/conf/busybox/networking/traceroute.c b/i/pc104/initrd/conf/busybox/networking/traceroute.c
new file mode 100644
index 0000000..fd4c20e
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/traceroute.c
@@ -0,0 +1,1342 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Copyright (c) 1988, 1989, 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Busybox port by Vladimir Oleynik (C) 2005 <dzo@simtreas.ru>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+//#define version "1.4a12"
+
+
+/*
+ * traceroute host - trace the route ip packets follow going to "host".
+ *
+ * Attempt to trace the route an ip packet would follow to some
+ * internet host. We find out intermediate hops by launching probe
+ * packets with a small ttl (time to live) then listening for an
+ * icmp "time exceeded" reply from a gateway. We start our probes
+ * with a ttl of one and increase by one until we get an icmp "port
+ * unreachable" (which means we got to "host") or hit a max (which
+ * defaults to 30 hops & can be changed with the -m flag). Three
+ * probes (change with -q flag) are sent at each ttl setting and a
+ * line is printed showing the ttl, address of the gateway and
+ * round trip time of each probe. If the probe answers come from
+ * different gateways, the address of each responding system will
+ * be printed. If there is no response within a 5 sec. timeout
+ * interval (changed with the -w flag), a "*" is printed for that
+ * probe.
+ *
+ * Probe packets are UDP format. We don't want the destination
+ * host to process them so the destination port is set to an
+ * unlikely value (if some clod on the destination is using that
+ * value, it can be changed with the -p flag).
+ *
+ * A sample use might be:
+ *
+ * [yak 71]% traceroute nis.nsf.net.
+ * traceroute to nis.nsf.net (35.1.1.48), 30 hops max, 56 byte packet
+ * 1 helios.ee.lbl.gov (128.3.112.1) 19 ms 19 ms 0 ms
+ * 2 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 39 ms 19 ms
+ * 3 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 39 ms 19 ms
+ * 4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 39 ms 40 ms 39 ms
+ * 5 ccn-nerif22.Berkeley.EDU (128.32.168.22) 39 ms 39 ms 39 ms
+ * 6 128.32.197.4 (128.32.197.4) 40 ms 59 ms 59 ms
+ * 7 131.119.2.5 (131.119.2.5) 59 ms 59 ms 59 ms
+ * 8 129.140.70.13 (129.140.70.13) 99 ms 99 ms 80 ms
+ * 9 129.140.71.6 (129.140.71.6) 139 ms 239 ms 319 ms
+ * 10 129.140.81.7 (129.140.81.7) 220 ms 199 ms 199 ms
+ * 11 nic.merit.edu (35.1.1.48) 239 ms 239 ms 239 ms
+ *
+ * Note that lines 2 & 3 are the same. This is due to a buggy
+ * kernel on the 2nd hop system -- lbl-csam.arpa -- that forwards
+ * packets with a zero ttl.
+ *
+ * A more interesting example is:
+ *
+ * [yak 72]% traceroute allspice.lcs.mit.edu.
+ * traceroute to allspice.lcs.mit.edu (18.26.0.115), 30 hops max
+ * 1 helios.ee.lbl.gov (128.3.112.1) 0 ms 0 ms 0 ms
+ * 2 lilac-dmc.Berkeley.EDU (128.32.216.1) 19 ms 19 ms 19 ms
+ * 3 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 19 ms 19 ms
+ * 4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 19 ms 39 ms 39 ms
+ * 5 ccn-nerif22.Berkeley.EDU (128.32.168.22) 20 ms 39 ms 39 ms
+ * 6 128.32.197.4 (128.32.197.4) 59 ms 119 ms 39 ms
+ * 7 131.119.2.5 (131.119.2.5) 59 ms 59 ms 39 ms
+ * 8 129.140.70.13 (129.140.70.13) 80 ms 79 ms 99 ms
+ * 9 129.140.71.6 (129.140.71.6) 139 ms 139 ms 159 ms
+ * 10 129.140.81.7 (129.140.81.7) 199 ms 180 ms 300 ms
+ * 11 129.140.72.17 (129.140.72.17) 300 ms 239 ms 239 ms
+ * 12 * * *
+ * 13 128.121.54.72 (128.121.54.72) 259 ms 499 ms 279 ms
+ * 14 * * *
+ * 15 * * *
+ * 16 * * *
+ * 17 * * *
+ * 18 ALLSPICE.LCS.MIT.EDU (18.26.0.115) 339 ms 279 ms 279 ms
+ *
+ * (I start to see why I'm having so much trouble with mail to
+ * MIT.) Note that the gateways 12, 14, 15, 16 & 17 hops away
+ * either don't send ICMP "time exceeded" messages or send them
+ * with a ttl too small to reach us. 14 - 17 are running the
+ * MIT C Gateway code that doesn't send "time exceeded"s. God
+ * only knows what's going on with 12.
+ *
+ * The silent gateway 12 in the above may be the result of a bug in
+ * the 4.[23]BSD network code (and its derivatives): 4.x (x <= 3)
+ * sends an unreachable message using whatever ttl remains in the
+ * original datagram. Since, for gateways, the remaining ttl is
+ * zero, the icmp "time exceeded" is guaranteed to not make it back
+ * to us. The behavior of this bug is slightly more interesting
+ * when it appears on the destination system:
+ *
+ * 1 helios.ee.lbl.gov (128.3.112.1) 0 ms 0 ms 0 ms
+ * 2 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 19 ms 39 ms
+ * 3 lilac-dmc.Berkeley.EDU (128.32.216.1) 19 ms 39 ms 19 ms
+ * 4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 39 ms 40 ms 19 ms
+ * 5 ccn-nerif35.Berkeley.EDU (128.32.168.35) 39 ms 39 ms 39 ms
+ * 6 csgw.Berkeley.EDU (128.32.133.254) 39 ms 59 ms 39 ms
+ * 7 * * *
+ * 8 * * *
+ * 9 * * *
+ * 10 * * *
+ * 11 * * *
+ * 12 * * *
+ * 13 rip.Berkeley.EDU (128.32.131.22) 59 ms ! 39 ms ! 39 ms !
+ *
+ * Notice that there are 12 "gateways" (13 is the final
+ * destination) and exactly the last half of them are "missing".
+ * What's really happening is that rip (a Sun-3 running Sun OS3.5)
+ * is using the ttl from our arriving datagram as the ttl in its
+ * icmp reply. So, the reply will time out on the return path
+ * (with no notice sent to anyone since icmp's aren't sent for
+ * icmp's) until we probe with a ttl that's at least twice the path
+ * length. I.e., rip is really only 7 hops away. A reply that
+ * returns with a ttl of 1 is a clue this problem exists.
+ * Traceroute prints a "!" after the time if the ttl is <= 1.
+ * Since vendors ship a lot of obsolete (DEC's Ultrix, Sun 3.x) or
+ * non-standard (HPUX) software, expect to see this problem
+ * frequently and/or take care picking the target host of your
+ * probes.
+ *
+ * Other possible annotations after the time are !H, !N, !P (got a host,
+ * network or protocol unreachable, respectively), !S or !F (source
+ * route failed or fragmentation needed -- neither of these should
+ * ever occur and the associated gateway is busted if you see one). If
+ * almost all the probes result in some kind of unreachable, traceroute
+ * will give up and exit.
+ *
+ * Notes
+ * -----
+ * This program must be run by root or be setuid. (I suggest that
+ * you *don't* make it setuid -- casual use could result in a lot
+ * of unnecessary traffic on our poor, congested nets.)
+ *
+ * This program requires a kernel mod that does not appear in any
+ * system available from Berkeley: A raw ip socket using proto
+ * IPPROTO_RAW must interpret the data sent as an ip datagram (as
+ * opposed to data to be wrapped in a ip datagram). See the README
+ * file that came with the source to this program for a description
+ * of the mods I made to /sys/netinet/raw_ip.c. Your mileage may
+ * vary. But, again, ANY 4.x (x < 4) BSD KERNEL WILL HAVE TO BE
+ * MODIFIED TO RUN THIS PROGRAM.
+ *
+ * The udp port usage may appear bizarre (well, ok, it is bizarre).
+ * The problem is that an icmp message only contains 8 bytes of
+ * data from the original datagram. 8 bytes is the size of a udp
+ * header so, if we want to associate replies with the original
+ * datagram, the necessary information must be encoded into the
+ * udp header (the ip id could be used but there's no way to
+ * interlock with the kernel's assignment of ip id's and, anyway,
+ * it would have taken a lot more kernel hacking to allow this
+ * code to set the ip id). So, to allow two or more users to
+ * use traceroute simultaneously, we use this task's pid as the
+ * source port (the high bit is set to move the port number out
+ * of the "likely" range). To keep track of which probe is being
+ * replied to (so times and/or hop counts don't get confused by a
+ * reply that was delayed in transit), we increment the destination
+ * port number before each probe.
+ *
+ * Don't use this as a coding example. I was trying to find a
+ * routing problem and this code sort-of popped out after 48 hours
+ * without sleep. I was amazed it ever compiled, much less ran.
+ *
+ * I stole the idea for this program from Steve Deering. Since
+ * the first release, I've learned that had I attended the right
+ * IETF working group meetings, I also could have stolen it from Guy
+ * Almes or Matt Mathis. I don't know (or care) who came up with
+ * the idea first. I envy the originators' perspicacity and I'm
+ * glad they didn't keep the idea a secret.
+ *
+ * Tim Seaver, Ken Adelman and C. Philip Wood provided bug fixes and/or
+ * enhancements to the original distribution.
+ *
+ * I've hacked up a round-trip-route version of this that works by
+ * sending a loose-source-routed udp datagram through the destination
+ * back to yourself. Unfortunately, SO many gateways botch source
+ * routing, the thing is almost worthless. Maybe one day...
+ *
+ * -- Van Jacobson (van@ee.lbl.gov)
+ * Tue Dec 20 03:50:13 PST 1988
+ */
+
+#define TRACEROUTE_SO_DEBUG 0
+
+/* TODO: undefs were uncommented - ??! we have config system for that! */
+/* probably ok to remove altogether */
+//#undef CONFIG_FEATURE_TRACEROUTE_VERBOSE
+//#define CONFIG_FEATURE_TRACEROUTE_VERBOSE
+//#undef CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE
+//#define CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE
+//#undef CONFIG_FEATURE_TRACEROUTE_USE_ICMP
+//#define CONFIG_FEATURE_TRACEROUTE_USE_ICMP
+
+
+#include <net/if.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <netinet/udp.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+
+#include "busybox.h"
+#include "inet_common.h"
+
+
+/*
+ * Definitions for internet protocol version 4.
+ * Per RFC 791, September 1981.
+ */
+#define IPVERSION 4
+
+#ifndef IPPROTO_ICMP
+/* Grrrr.... */
+#define IPPROTO_ICMP 1
+#endif
+#ifndef IPPROTO_IP
+#define IPPROTO_IP 0
+#endif
+
+/*
+ * Overlay for ip header used by other protocols (tcp, udp).
+ */
+struct ipovly {
+ unsigned char ih_x1[9]; /* (unused) */
+ unsigned char ih_pr; /* protocol */
+ short ih_len; /* protocol length */
+ struct in_addr ih_src; /* source internet address */
+ struct in_addr ih_dst; /* destination internet address */
+};
+
+/*
+ * UDP kernel structures and variables.
+ */
+struct udpiphdr {
+ struct ipovly ui_i; /* overlaid ip structure */
+ struct udphdr ui_u; /* udp header */
+};
+#define ui_next ui_i.ih_next
+#define ui_prev ui_i.ih_prev
+#define ui_x1 ui_i.ih_x1
+#define ui_pr ui_i.ih_pr
+#define ui_len ui_i.ih_len
+#define ui_src ui_i.ih_src
+#define ui_dst ui_i.ih_dst
+#define ui_sport ui_u.uh_sport
+#define ui_dport ui_u.uh_dport
+#define ui_ulen ui_u.uh_ulen
+#define ui_sum ui_u.uh_sum
+
+
+/* Host name and address list */
+struct hostinfo {
+ char *name;
+ int n;
+ uint32_t *addrs;
+};
+
+/* Data section of the probe packet */
+struct outdata {
+ unsigned char seq; /* sequence number of this packet */
+ unsigned char ttl; /* ttl packet left with */
+ struct timeval tv ATTRIBUTE_PACKED; /* time packet left */
+};
+
+struct IFADDRLIST {
+ uint32_t addr;
+ char device[sizeof(struct ifreq)];
+};
+
+
+static const char route[] = "/proc/net/route";
+
+/* last inbound (icmp) packet */
+static unsigned char packet[512] ATTRIBUTE_ALIGNED(32);
+
+static struct ip *outip; /* last output (udp) packet */
+static struct udphdr *outudp; /* last output (udp) packet */
+static struct outdata *outdata; /* last output (udp) packet */
+
+#if ENABLE_FEATURE_TRACEROUTE_USE_ICMP
+static struct icmp *outicmp; /* last output (icmp) packet */
+#endif
+
+#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
+/* Maximum number of gateways (include room for one noop) */
+#define NGATEWAYS ((int)((MAX_IPOPTLEN - IPOPT_MINOFF - 1) / sizeof(uint32_t)))
+/* loose source route gateway list (including room for final destination) */
+static uint32_t gwlist[NGATEWAYS + 1];
+#endif
+
+static int s; /* receive (icmp) socket file descriptor */
+static int sndsock; /* send (udp/icmp) socket file descriptor */
+
+static struct sockaddr_storage whereto; /* Who to try to reach */
+static struct sockaddr_storage wherefrom; /* Who we are */
+static int packlen; /* total length of packet */
+static int minpacket; /* min ip packet size */
+static int maxpacket = 32 * 1024; /* max ip packet size */
+static int pmtu; /* Path MTU Discovery (RFC1191) */
+
+static char *hostname;
+
+static uint16_t ident;
+static uint16_t port = 32768 + 666; /* start udp dest port # for probe packets */
+
+static int waittime = 5; /* time to wait for response (in seconds) */
+static int nflag; /* print addresses numerically */
+static int doipcksum = 1; /* calculate ip checksums by default */
+
+#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
+static int optlen; /* length of ip options */
+#else
+#define optlen 0
+#endif
+
+#if ENABLE_FEATURE_TRACEROUTE_USE_ICMP
+static int useicmp; /* use icmp echo instead of udp packets */
+#endif
+#if ENABLE_FEATURE_TRACEROUTE_VERBOSE
+static int verbose;
+#endif
+
+/*
+ * Return the interface list
+ */
+static int
+ifaddrlist(struct IFADDRLIST **ipaddrp)
+{
+ int fd, nipaddr;
+#ifdef HAVE_SOCKADDR_SA_LEN
+ int n;
+#endif
+ struct ifreq *ifrp, *ifend, *ifnext;
+ struct sockaddr_in *addr_sin;
+ struct IFADDRLIST *al;
+ struct ifconf ifc;
+ struct ifreq ibuf[(32 * 1024) / sizeof(struct ifreq)], ifr;
+ struct IFADDRLIST *st_ifaddrlist;
+
+ fd = xsocket(AF_INET, SOCK_DGRAM, 0);
+
+ ifc.ifc_len = sizeof(ibuf);
+ ifc.ifc_buf = (caddr_t)ibuf;
+
+ if (ioctl(fd, SIOCGIFCONF, (char *)&ifc) < 0 ||
+ ifc.ifc_len < sizeof(struct ifreq)) {
+ if (errno == EINVAL)
+ bb_error_msg_and_die(
+ "SIOCGIFCONF: ifreq struct too small (%d bytes)",
+ (int)sizeof(ibuf));
+ else
+ bb_perror_msg_and_die("SIOCGIFCONF");
+ }
+ ifrp = ibuf;
+ ifend = (struct ifreq *)((char *)ibuf + ifc.ifc_len);
+
+ nipaddr = 1 + (ifc.ifc_len / sizeof(struct ifreq));
+ st_ifaddrlist = xzalloc(nipaddr * sizeof(struct IFADDRLIST));
+ al = st_ifaddrlist;
+ nipaddr = 0;
+
+ for (; ifrp < ifend; ifrp = ifnext) {
+#ifdef HAVE_SOCKADDR_SA_LEN
+ n = ifrp->ifr_addr.sa_len + sizeof(ifrp->ifr_name);
+ if (n < sizeof(*ifrp))
+ ifnext = ifrp + 1;
+ else
+ ifnext = (struct ifreq *)((char *)ifrp + n);
+ if (ifrp->ifr_addr.sa_family != AF_INET)
+ continue;
+#else
+ ifnext = ifrp + 1;
+#endif
+ /*
+ * Need a template to preserve address info that is
+ * used below to locate the next entry. (Otherwise,
+ * SIOCGIFFLAGS stomps over it because the requests
+ * are returned in a union.)
+ */
+ strncpy(ifr.ifr_name, ifrp->ifr_name, sizeof(ifr.ifr_name));
+ if (ioctl(fd, SIOCGIFFLAGS, (char *)&ifr) < 0) {
+ if (errno == ENXIO)
+ continue;
+ bb_perror_msg_and_die("SIOCGIFFLAGS: %.*s",
+ (int)sizeof(ifr.ifr_name), ifr.ifr_name);
+ }
+
+ /* Must be up */
+ if ((ifr.ifr_flags & IFF_UP) == 0)
+ continue;
+
+ safe_strncpy(al->device, ifr.ifr_name, sizeof(ifr.ifr_name) + 1);
+#ifdef sun
+ /* Ignore sun virtual interfaces */
+ if (strchr(al->device, ':') != NULL)
+ continue;
+#endif
+ if (ioctl(fd, SIOCGIFADDR, (char *)&ifr) < 0)
+ bb_perror_msg_and_die("SIOCGIFADDR: %s", al->device);
+
+ addr_sin = (struct sockaddr_in *)&ifr.ifr_addr;
+ al->addr = addr_sin->sin_addr.s_addr;
+ ++al;
+ ++nipaddr;
+ }
+ if (nipaddr == 0)
+ bb_error_msg_and_die ("can't find any network interfaces");
+ (void)close(fd);
+
+ *ipaddrp = st_ifaddrlist;
+ return nipaddr;
+}
+
+
+static void
+setsin(struct sockaddr_in *addr_sin, uint32_t addr)
+{
+ memset(addr_sin, 0, sizeof(*addr_sin));
+#ifdef HAVE_SOCKADDR_SA_LEN
+ addr_sin->sin_len = sizeof(*addr_sin);
+#endif
+ addr_sin->sin_family = AF_INET;
+ addr_sin->sin_addr.s_addr = addr;
+}
+
+
+/*
+ * Return the source address for the given destination address
+ */
+static void
+findsaddr(const struct sockaddr_in *to, struct sockaddr_in *from)
+{
+ int i, n;
+ FILE *f;
+ uint32_t mask;
+ uint32_t dest, tmask;
+ struct IFADDRLIST *al;
+ char buf[256], tdevice[256], device[256];
+
+ f = xfopen(route, "r");
+
+ /* Find the appropriate interface */
+ n = 0;
+ mask = 0;
+ device[0] = '\0';
+ while (fgets(buf, sizeof(buf), f) != NULL) {
+ ++n;
+ if (n == 1 && strncmp(buf, "Iface", 5) == 0)
+ continue;
+ if ((i = sscanf(buf, "%255s %x %*s %*s %*s %*s %*s %x",
+ tdevice, &dest, &tmask)) != 3)
+ bb_error_msg_and_die ("junk in buffer");
+ if ((to->sin_addr.s_addr & tmask) == dest &&
+ (tmask > mask || mask == 0)) {
+ mask = tmask;
+ strcpy(device, tdevice);
+ }
+ }
+ fclose(f);
+
+ if (device[0] == '\0')
+ bb_error_msg_and_die ("can't find interface");
+
+ /* Get the interface address list */
+ n = ifaddrlist(&al);
+
+ /* Find our appropriate source address */
+ for (i = n; i > 0; --i, ++al)
+ if (strcmp(device, al->device) == 0)
+ break;
+ if (i <= 0)
+ bb_error_msg_and_die("can't find interface %s", device);
+
+ setsin(from, al->addr);
+}
+
+/*
+"Usage: %s [-dFIlnrvx] [-g gateway] [-i iface] [-f first_ttl]\n"
+"\t[-m max_ttl] [ -p port] [-q nqueries] [-s src_addr] [-t tos]\n"
+"\t[-w waittime] [-z pausemsecs] host [packetlen]"
+
+*/
+
+/*
+ * Subtract 2 timeval structs: out = out - in.
+ * Out is assumed to be >= in.
+ */
+static inline void
+tvsub(struct timeval *out, struct timeval *in)
+{
+
+ if ((out->tv_usec -= in->tv_usec) < 0) {
+ --out->tv_sec;
+ out->tv_usec += 1000000;
+ }
+ out->tv_sec -= in->tv_sec;
+}
+
+static int
+wait_for_reply(int sock, struct sockaddr_in *fromp, const struct timeval *tp)
+{
+ fd_set fds;
+ struct timeval now, tvwait;
+ struct timezone tz;
+ int cc = 0;
+ socklen_t fromlen = sizeof(*fromp);
+
+ FD_ZERO(&fds);
+ FD_SET(sock, &fds);
+
+ tvwait.tv_sec = tp->tv_sec + waittime;
+ tvwait.tv_usec = tp->tv_usec;
+ (void)gettimeofday(&now, &tz);
+ tvsub(&tvwait, &now);
+
+ if (select(sock + 1, &fds, NULL, NULL, &tvwait) > 0)
+ cc = recvfrom(sock, (char *)packet, sizeof(packet), 0,
+ (struct sockaddr *)fromp, &fromlen);
+
+ return cc;
+}
+
+/*
+ * Checksum routine for Internet Protocol family headers (C Version)
+ */
+static uint16_t
+in_cksum(uint16_t *addr, int len)
+{
+ int nleft = len;
+ uint16_t *w = addr;
+ uint16_t answer;
+ int sum = 0;
+
+ /*
+ * Our algorithm is simple, using a 32 bit accumulator (sum),
+ * we add sequential 16 bit words to it, and at the end, fold
+ * back all the carry bits from the top 16 bits into the lower
+ * 16 bits.
+ */
+ while (nleft > 1) {
+ sum += *w++;
+ nleft -= 2;
+ }
+
+ /* mop up an odd byte, if necessary */
+ if (nleft == 1)
+ sum += *(unsigned char *)w;
+
+ /*
+ * add back carry outs from top 16 bits to low 16 bits
+ */
+ sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
+ sum += (sum >> 16); /* add carry */
+ answer = ~sum; /* truncate to 16 bits */
+ return answer;
+}
+
+
+static void
+send_probe(int seq, int ttl, struct timeval *tp)
+{
+ int cc;
+ struct udpiphdr *ui, *oui;
+ struct ip tip;
+
+ outip->ip_ttl = ttl;
+ outip->ip_id = htons(ident + seq);
+
+ /*
+ * In most cases, the kernel will recalculate the ip checksum.
+ * But we must do it anyway so that the udp checksum comes out
+ * right.
+ */
+ if (doipcksum) {
+ outip->ip_sum =
+ in_cksum((uint16_t *)outip, sizeof(*outip) + optlen);
+ if (outip->ip_sum == 0)
+ outip->ip_sum = 0xffff;
+ }
+
+ /* Payload */
+ outdata->seq = seq;
+ outdata->ttl = ttl;
+ memcpy(&outdata->tv, tp, sizeof(outdata->tv));
+
+#if ENABLE_FEATURE_TRACEROUTE_USE_ICMP
+ if (useicmp)
+ outicmp->icmp_seq = htons(seq);
+ else
+#endif
+ outudp->dest = htons(port + seq);
+
+#if ENABLE_FEATURE_TRACEROUTE_USE_ICMP
+ if (useicmp) {
+ /* Always calculate checksum for icmp packets */
+ outicmp->icmp_cksum = 0;
+ outicmp->icmp_cksum = in_cksum((uint16_t *)outicmp,
+ packlen - (sizeof(*outip) + optlen));
+ if (outicmp->icmp_cksum == 0)
+ outicmp->icmp_cksum = 0xffff;
+ } else
+#endif
+ if (doipcksum) {
+ /* Checksum (we must save and restore ip header) */
+ tip = *outip;
+ ui = (struct udpiphdr *)outip;
+ oui = (struct udpiphdr *)&tip;
+ /* Easier to zero and put back things that are ok */
+ memset((char *)ui, 0, sizeof(ui->ui_i));
+ ui->ui_src = oui->ui_src;
+ ui->ui_dst = oui->ui_dst;
+ ui->ui_pr = oui->ui_pr;
+ ui->ui_len = outudp->len;
+ outudp->check = 0;
+ outudp->check = in_cksum((uint16_t *)ui, packlen);
+ if (outudp->check == 0)
+ outudp->check = 0xffff;
+ *outip = tip;
+ }
+
+#if ENABLE_FEATURE_TRACEROUTE_VERBOSE
+ /* XXX undocumented debugging hack */
+ if (verbose > 1) {
+ const uint16_t *sp;
+ int nshorts, i;
+
+ sp = (uint16_t *)outip;
+ nshorts = (unsigned)packlen / sizeof(uint16_t);
+ i = 0;
+ printf("[ %d bytes", packlen);
+ while (--nshorts >= 0) {
+ if ((i++ % 8) == 0)
+ printf("\n\t");
+ printf(" %04x", ntohs(*sp));
+ sp++;
+ }
+ if (packlen & 1) {
+ if ((i % 8) == 0)
+ printf("\n\t");
+ printf(" %02x", *(unsigned char *)sp);
+ }
+ printf("]\n");
+ }
+#endif
+
+#if !defined(IP_HDRINCL) && defined(IP_TTL)
+ if (setsockopt(sndsock, IPPROTO_IP, IP_TTL,
+ (char *)&ttl, sizeof(ttl)) < 0) {
+ bb_perror_msg_and_die("setsockopt ttl %d", ttl);
+ }
+#endif
+
+ cc = sendto(sndsock, (char *)outip,
+ packlen, 0, (struct sockaddr *)&whereto, sizeof(whereto));
+ if (cc < 0 || cc != packlen) {
+ if (cc < 0)
+ bb_perror_msg_and_die("sendto");
+ printf("%s: wrote %s %d chars, ret=%d\n",
+ applet_name, hostname, packlen, cc);
+ (void)fflush(stdout);
+ }
+}
+
+static inline double
+deltaT(struct timeval *t1p, struct timeval *t2p)
+{
+ double dt;
+
+ dt = (double)(t2p->tv_sec - t1p->tv_sec) * 1000.0 +
+ (double)(t2p->tv_usec - t1p->tv_usec) / 1000.0;
+ return dt;
+}
+
+#if ENABLE_FEATURE_TRACEROUTE_VERBOSE
+/*
+ * Convert an ICMP "type" field to a printable string.
+ */
+static inline const char *
+pr_type(unsigned char t)
+{
+ static const char * const ttab[] = {
+ "Echo Reply", "ICMP 1", "ICMP 2", "Dest Unreachable",
+ "Source Quench", "Redirect", "ICMP 6", "ICMP 7",
+ "Echo", "Router Advert", "Router Solicit", "Time Exceeded",
+ "Param Problem", "Timestamp", "Timestamp Reply", "Info Request",
+ "Info Reply", "Mask Request", "Mask Reply"
+ };
+
+ if (t > 18)
+ return "OUT-OF-RANGE";
+
+ return ttab[t];
+}
+#endif
+
+static int
+packet_ok(unsigned char *buf, int cc, struct sockaddr_in *from, int seq)
+{
+ struct icmp *icp;
+ unsigned char type, code;
+ int hlen;
+ struct ip *ip;
+
+ ip = (struct ip *) buf;
+ hlen = ip->ip_hl << 2;
+ if (cc < hlen + ICMP_MINLEN) {
+#if ENABLE_FEATURE_TRACEROUTE_VERBOSE
+ if (verbose)
+ printf("packet too short (%d bytes) from %s\n", cc,
+ inet_ntoa(from->sin_addr));
+#endif
+ return 0;
+ }
+ cc -= hlen;
+ icp = (struct icmp *)(buf + hlen);
+ type = icp->icmp_type;
+ code = icp->icmp_code;
+ /* Path MTU Discovery (RFC1191) */
+ if (code != ICMP_UNREACH_NEEDFRAG)
+ pmtu = 0;
+ else {
+ pmtu = ntohs(icp->icmp_nextmtu);
+ }
+ if ((type == ICMP_TIMXCEED && code == ICMP_TIMXCEED_INTRANS) ||
+ type == ICMP_UNREACH || type == ICMP_ECHOREPLY) {
+ struct ip *hip;
+ struct udphdr *up;
+
+ hip = &icp->icmp_ip;
+ hlen = hip->ip_hl << 2;
+#if ENABLE_FEATURE_TRACEROUTE_USE_ICMP
+ if (useicmp) {
+ struct icmp *hicmp;
+
+ /* XXX */
+ if (type == ICMP_ECHOREPLY &&
+ icp->icmp_id == htons(ident) &&
+ icp->icmp_seq == htons(seq))
+ return -2;
+
+ hicmp = (struct icmp *)((unsigned char *)hip + hlen);
+ /* XXX 8 is a magic number */
+ if (hlen + 8 <= cc &&
+ hip->ip_p == IPPROTO_ICMP &&
+ hicmp->icmp_id == htons(ident) &&
+ hicmp->icmp_seq == htons(seq))
+ return (type == ICMP_TIMXCEED ? -1 : code + 1);
+ } else
+#endif
+ {
+ up = (struct udphdr *)((unsigned char *)hip + hlen);
+ /* XXX 8 is a magic number */
+ if (hlen + 12 <= cc &&
+ hip->ip_p == IPPROTO_UDP &&
+ up->source == htons(ident) &&
+ up->dest == htons(port + seq))
+ return (type == ICMP_TIMXCEED ? -1 : code + 1);
+ }
+ }
+#if ENABLE_FEATURE_TRACEROUTE_VERBOSE
+ if (verbose) {
+ int i;
+ uint32_t *lp = (uint32_t *)&icp->icmp_ip;
+
+ printf("\n%d bytes from %s to "
+ "%s: icmp type %d (%s) code %d\n",
+ cc, inet_ntoa(from->sin_addr),
+ inet_ntoa(ip->ip_dst), type, pr_type(type), icp->icmp_code);
+ for (i = 4; i < cc ; i += sizeof(*lp))
+ printf("%2d: x%8.8x\n", i, *lp++);
+ }
+#endif
+ return 0;
+}
+
+
+/*
+ * Construct an Internet address representation.
+ * If the nflag has been supplied, give
+ * numeric value, otherwise try for symbolic name.
+ */
+static inline void
+inetname(struct sockaddr_in *from)
+{
+ const char *n = NULL;
+ const char *ina;
+ char name[257];
+
+ if (!nflag && from->sin_addr.s_addr != INADDR_ANY) {
+ if (INET_rresolve(name, sizeof(name), from, 0x4000,
+ 0xffffffff) >= 0)
+ n = name;
+ }
+ ina = inet_ntoa(from->sin_addr);
+ if (nflag)
+ printf(" %s", ina);
+ else
+ printf(" %s (%s)", (n ? n : ina), ina);
+}
+
+static inline void
+print(unsigned char *buf, int cc, struct sockaddr_in *from)
+{
+ struct ip *ip;
+ int hlen;
+
+ ip = (struct ip *) buf;
+ hlen = ip->ip_hl << 2;
+ cc -= hlen;
+
+ inetname(from);
+#if ENABLE_FEATURE_TRACEROUTE_VERBOSE
+ if (verbose)
+ printf(" %d bytes to %s", cc, inet_ntoa (ip->ip_dst));
+#endif
+}
+
+
+static struct hostinfo *
+gethostinfo(const char *host)
+{
+ int n;
+ struct hostent *hp;
+ struct hostinfo *hi;
+ char **p;
+ uint32_t addr, *ap;
+
+ hi = xzalloc(sizeof(*hi));
+ addr = inet_addr(host);
+ if (addr != 0xffffffff) {
+ hi->name = xstrdup(host);
+ hi->n = 1;
+ hi->addrs = xzalloc(sizeof(hi->addrs[0]));
+ hi->addrs[0] = addr;
+ return hi;
+ }
+
+ hp = xgethostbyname(host);
+ if (hp->h_addrtype != AF_INET || hp->h_length != 4)
+ bb_perror_msg_and_die("bad host %s", host);
+ hi->name = xstrdup(hp->h_name);
+ for (n = 0, p = hp->h_addr_list; *p != NULL; ++n, ++p)
+ continue;
+ hi->n = n;
+ hi->addrs = xzalloc(n * sizeof(hi->addrs[0]));
+ for (ap = hi->addrs, p = hp->h_addr_list; *p != NULL; ++ap, ++p)
+ memcpy(ap, *p, sizeof(*ap));
+ return hi;
+}
+
+static void
+freehostinfo(struct hostinfo *hi)
+{
+ free(hi->name);
+ hi->name = NULL;
+ free((char *)hi->addrs);
+ free((char *)hi);
+}
+
+#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
+static void
+getaddr(uint32_t *ap, const char *host)
+{
+ struct hostinfo *hi;
+
+ hi = gethostinfo(host);
+ *ap = hi->addrs[0];
+ freehostinfo(hi);
+}
+#endif
+
+
+int traceroute_main(int argc, char *argv[]);
+int traceroute_main(int argc, char *argv[])
+{
+ int code, n;
+ unsigned char *outp;
+ uint32_t *ap;
+ struct sockaddr_in *from = (struct sockaddr_in *)&wherefrom;
+ struct sockaddr_in *to = (struct sockaddr_in *)&whereto;
+ struct hostinfo *hi;
+ int ttl, probe, i;
+ int seq = 0;
+ int tos = 0;
+ char *tos_str = NULL;
+ char *source = NULL;
+ unsigned long op;
+
+#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
+ int lsrr = 0;
+#endif
+ uint16_t off = 0;
+ struct IFADDRLIST *al;
+ char *device = NULL;
+ int max_ttl = 30;
+ char *max_ttl_str = NULL;
+ char *port_str = NULL;
+ int nprobes = 3;
+ char *nprobes_str = NULL;
+ char *waittime_str = NULL;
+ unsigned pausemsecs = 0;
+ char *pausemsecs_str = NULL;
+ int first_ttl = 1;
+ char *first_ttl_str = NULL;
+#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
+ llist_t *sourse_route_list = NULL;
+#endif
+
+ opterr = 0;
+#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
+ opt_complementary = "x-x:g::";
+#else
+ opt_complementary = "x-x";
+#endif
+
+ op = getopt32(argc, argv, "FIlnrdvxt:i:m:p:q:s:w:z:f:"
+#define USAGE_OP_DONT_FRAGMNT (1<<0) /* F */
+#define USAGE_OP_USE_ICMP (1<<1) /* I */
+#define USAGE_OP_TTL_FLAG (1<<2) /* l */
+#define USAGE_OP_ADDR_NUM (1<<3) /* n */
+#define USAGE_OP_BYPASS_ROUTE (1<<4) /* r */
+#define USAGE_OP_DEBUG (1<<5) /* d */
+#define USAGE_OP_VERBOSE (1<<6) /* v */
+#define USAGE_OP_IP_CHKSUM (1<<7) /* x */
+
+#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
+ "g:"
+#endif
+ , &tos_str, &device, &max_ttl_str, &port_str, &nprobes_str,
+ &source, &waittime_str, &pausemsecs_str, &first_ttl_str
+#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
+ , &sourse_route_list
+#endif
+ );
+
+ if (op & USAGE_OP_DONT_FRAGMNT)
+ off = IP_DF;
+#if ENABLE_FEATURE_TRACEROUTE_USE_ICMP
+ useicmp = op & USAGE_OP_USE_ICMP;
+#endif
+ nflag = op & USAGE_OP_ADDR_NUM;
+#if ENABLE_FEATURE_TRACEROUTE_VERBOSE
+ verbose = op & USAGE_OP_VERBOSE;
+#endif
+ if (op & USAGE_OP_IP_CHKSUM) {
+ doipcksum = 0;
+ bb_error_msg("warning: ip checksums disabled");
+ }
+ if (tos_str)
+ tos = xatoul_range(tos_str, 0, 255);
+ if (max_ttl_str)
+ max_ttl = xatoul_range(max_ttl_str, 1, 255);
+ if (port_str)
+ port = xatou16(port_str);
+ if (nprobes_str)
+ nprobes = xatoul_range(nprobes_str, 1, INT_MAX);
+ if (source) {
+ /*
+ * set the ip source address of the outbound
+ * probe (e.g., on a multi-homed host).
+ */
+ if (getuid()) bb_error_msg_and_die("-s %s: permission denied", source);
+ }
+ if (waittime_str)
+ waittime = xatoul_range(waittime_str, 2, 24 * 60 * 60);
+ if (pausemsecs_str)
+ pausemsecs = xatoul_range(pausemsecs_str, 0, 60 * 60 * 1000);
+ if (first_ttl_str)
+ first_ttl = xatoul_range(first_ttl_str, 1, 255);
+
+#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
+ if (sourse_route_list) {
+ llist_t *l_sr;
+
+ for (l_sr = sourse_route_list; l_sr; ) {
+ if (lsrr >= NGATEWAYS)
+ bb_error_msg_and_die("no more than %d gateways", NGATEWAYS);
+ getaddr(gwlist + lsrr, l_sr->data);
+ ++lsrr;
+ l_sr = l_sr->link;
+ free(sourse_route_list);
+ sourse_route_list = l_sr;
+ }
+ optlen = (lsrr + 1) * sizeof(gwlist[0]);
+ }
+#endif
+
+ if (first_ttl > max_ttl) {
+ bb_error_msg_and_die(
+ "first ttl (%d) may not be greater than max ttl (%d)",
+ first_ttl, max_ttl);
+ }
+
+ minpacket = sizeof(*outip) + sizeof(*outdata) + optlen;
+
+#if ENABLE_FEATURE_TRACEROUTE_USE_ICMP
+ if (useicmp)
+ minpacket += 8; /* XXX magic number */
+ else
+#endif
+ minpacket += sizeof(*outudp);
+ packlen = minpacket; /* minimum sized packet */
+
+ /* Process destination and optional packet size */
+ switch (argc - optind) {
+
+ case 2:
+ packlen = xatoul_range(argv[optind + 1], minpacket, maxpacket);
+ /* Fall through */
+
+ case 1:
+ hostname = argv[optind];
+ hi = gethostinfo(hostname);
+ setsin(to, hi->addrs[0]);
+ if (hi->n > 1)
+ bb_error_msg("warning: %s has multiple addresses; using %s",
+ hostname, inet_ntoa(to->sin_addr));
+ hostname = hi->name;
+ hi->name = NULL;
+ freehostinfo(hi);
+ break;
+
+ default:
+ bb_show_usage();
+ }
+
+ /* Ensure the socket fds won't be 0, 1 or 2 */
+ bb_sanitize_stdio();
+
+ s = xsocket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
+
+#if TRACEROUTE_SO_DEBUG
+ if (op & USAGE_OP_DEBUG)
+ setsockopt(s, SOL_SOCKET, SO_DEBUG,
+ &const_int_1, sizeof(const_int_1));
+#endif
+ if (op & USAGE_OP_BYPASS_ROUTE)
+ setsockopt(s, SOL_SOCKET, SO_DONTROUTE,
+ &const_int_1, sizeof(const_int_1));
+
+ sndsock = xsocket(AF_INET, SOCK_RAW, IPPROTO_RAW);
+
+#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
+#if defined(IP_OPTIONS)
+ if (lsrr > 0) {
+ unsigned char optlist[MAX_IPOPTLEN];
+
+ /* final hop */
+ gwlist[lsrr] = to->sin_addr.s_addr;
+ ++lsrr;
+
+ /* force 4 byte alignment */
+ optlist[0] = IPOPT_NOP;
+ /* loose source route option */
+ optlist[1] = IPOPT_LSRR;
+ i = lsrr * sizeof(gwlist[0]);
+ optlist[2] = i + 3;
+ /* Pointer to LSRR addresses */
+ optlist[3] = IPOPT_MINOFF;
+ memcpy(optlist + 4, gwlist, i);
+
+ if ((setsockopt(sndsock, IPPROTO_IP, IP_OPTIONS,
+ (char *)optlist, i + sizeof(gwlist[0]))) < 0) {
+ bb_perror_msg_and_die("IP_OPTIONS");
+ }
+ }
+#endif /* IP_OPTIONS */
+#endif /* CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE */
+
+#ifdef SO_SNDBUF
+ if (setsockopt(sndsock, SOL_SOCKET, SO_SNDBUF, &packlen, sizeof(packlen)) < 0) {
+ bb_perror_msg_and_die("SO_SNDBUF");
+ }
+#endif
+#ifdef IP_HDRINCL
+ if (setsockopt(sndsock, IPPROTO_IP, IP_HDRINCL, &const_int_1, sizeof(const_int_1)) < 0
+ && errno != ENOPROTOOPT
+ ) {
+ bb_perror_msg_and_die("IP_HDRINCL");
+ }
+#else
+#ifdef IP_TOS
+ if (tos_str && setsockopt(sndsock, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) < 0) {
+ bb_perror_msg_and_die("setsockopt tos %d", tos);
+ }
+#endif
+#endif
+#if TRACEROUTE_SO_DEBUG
+ if (op & USAGE_OP_DEBUG)
+ setsockopt(sndsock, SOL_SOCKET, SO_DEBUG,
+ &const_int_1, sizeof(const_int_1));
+#endif
+ if (op & USAGE_OP_BYPASS_ROUTE)
+ setsockopt(sndsock, SOL_SOCKET, SO_DONTROUTE,
+ &const_int_1, sizeof(const_int_1));
+
+ /* Revert to non-privileged user after opening sockets */
+ xsetgid(getgid());
+ xsetuid(getuid());
+
+ outip = xzalloc(packlen);
+
+ outip->ip_v = IPVERSION;
+ if (tos_str)
+ outip->ip_tos = tos;
+ outip->ip_len = htons(packlen);
+ outip->ip_off = htons(off);
+ outp = (unsigned char *)(outip + 1);
+ outip->ip_dst = to->sin_addr;
+
+ outip->ip_hl = (outp - (unsigned char *)outip) >> 2;
+ ident = (getpid() & 0xffff) | 0x8000;
+#if ENABLE_FEATURE_TRACEROUTE_USE_ICMP
+ if (useicmp) {
+ outip->ip_p = IPPROTO_ICMP;
+ outicmp = (struct icmp *)outp;
+ outicmp->icmp_type = ICMP_ECHO;
+ outicmp->icmp_id = htons(ident);
+ outdata = (struct outdata *)(outp + 8); /* XXX magic number */
+ } else
+#endif
+ {
+ outip->ip_p = IPPROTO_UDP;
+ outudp = (struct udphdr *)outp;
+ outudp->source = htons(ident);
+ outudp->len = htons((uint16_t)(packlen - (sizeof(*outip) + optlen)));
+ outdata = (struct outdata *)(outudp + 1);
+ }
+
+ /* Get the interface address list */
+ n = ifaddrlist(&al);
+
+ /* Look for a specific device */
+ if (device != NULL) {
+ for (i = n; i > 0; --i, ++al)
+ if (strcmp(device, al->device) == 0)
+ break;
+ if (i <= 0) {
+ bb_error_msg_and_die("can't find interface %s", device);
+ }
+ }
+
+ /* Determine our source address */
+ if (source == NULL) {
+ /*
+ * If a device was specified, use the interface address.
+ * Otherwise, try to determine our source address.
+ */
+ if (device != NULL)
+ setsin(from, al->addr);
+ findsaddr(to, from);
+ } else {
+ hi = gethostinfo(source);
+ source = hi->name;
+ hi->name = NULL;
+ /*
+ * If the device was specified make sure it
+ * corresponds to the source address specified.
+ * Otherwise, use the first address (and warn if
+ * there are more than one).
+ */
+ if (device != NULL) {
+ for (i = hi->n, ap = hi->addrs; i > 0; --i, ++ap)
+ if (*ap == al->addr)
+ break;
+ if (i <= 0) {
+ bb_error_msg_and_die(
+ "%s is not on interface %s",
+ source, device);
+ }
+ setsin(from, *ap);
+ } else {
+ setsin(from, hi->addrs[0]);
+ if (hi->n > 1)
+ bb_error_msg(
+ "Warning: %s has multiple addresses; using %s",
+ source, inet_ntoa(from->sin_addr));
+ }
+ freehostinfo(hi);
+ }
+
+ outip->ip_src = from->sin_addr;
+#ifndef IP_HDRINCL
+ xbind(sndsock, (struct sockaddr *)from, sizeof(*from));
+#endif
+
+ fprintf(stderr, "traceroute to %s (%s)", hostname, inet_ntoa(to->sin_addr));
+ if (source)
+ fprintf(stderr, " from %s", source);
+ fprintf(stderr, ", %d hops max, %d byte packets\n", max_ttl, packlen);
+ (void)fflush(stderr);
+
+ for (ttl = first_ttl; ttl <= max_ttl; ++ttl) {
+ uint32_t lastaddr = 0;
+ int gotlastaddr = 0;
+ int got_there = 0;
+ int unreachable = 0;
+ int sentfirst = 0;
+
+ printf("%2d ", ttl);
+ for (probe = 0; probe < nprobes; ++probe) {
+ int cc;
+ struct timeval t1, t2;
+ struct timezone tz;
+ struct ip *ip;
+
+ if (sentfirst && pausemsecs > 0)
+ usleep(pausemsecs * 1000);
+ (void)gettimeofday(&t1, &tz);
+ send_probe(++seq, ttl, &t1);
+ ++sentfirst;
+ while ((cc = wait_for_reply(s, from, &t1)) != 0) {
+ (void)gettimeofday(&t2, &tz);
+ i = packet_ok(packet, cc, from, seq);
+ /* Skip short packet */
+ if (i == 0)
+ continue;
+ if (!gotlastaddr ||
+ from->sin_addr.s_addr != lastaddr) {
+ print(packet, cc, from);
+ lastaddr = from->sin_addr.s_addr;
+ ++gotlastaddr;
+ }
+ printf(" %.3f ms", deltaT(&t1, &t2));
+ ip = (struct ip *)packet;
+ if (op & USAGE_OP_TTL_FLAG)
+ printf(" (%d)", ip->ip_ttl);
+ if (i == -2) {
+ if (ip->ip_ttl <= 1)
+ printf(" !");
+ ++got_there;
+ break;
+ }
+ /* time exceeded in transit */
+ if (i == -1)
+ break;
+ code = i - 1;
+ switch (code) {
+
+ case ICMP_UNREACH_PORT:
+ if (ip->ip_ttl <= 1)
+ printf(" !");
+ ++got_there;
+ break;
+
+ case ICMP_UNREACH_NET:
+ ++unreachable;
+ printf(" !N");
+ break;
+
+ case ICMP_UNREACH_HOST:
+ ++unreachable;
+ printf(" !H");
+ break;
+
+ case ICMP_UNREACH_PROTOCOL:
+ ++got_there;
+ printf(" !P");
+ break;
+
+ case ICMP_UNREACH_NEEDFRAG:
+ ++unreachable;
+ printf(" !F-%d", pmtu);
+ break;
+
+ case ICMP_UNREACH_SRCFAIL:
+ ++unreachable;
+ printf(" !S");
+ break;
+
+ case ICMP_UNREACH_FILTER_PROHIB:
+ case ICMP_UNREACH_NET_PROHIB: /* misuse */
+ ++unreachable;
+ printf(" !A");
+ break;
+
+ case ICMP_UNREACH_HOST_PROHIB:
+ ++unreachable;
+ printf(" !C");
+ break;
+
+ case ICMP_UNREACH_HOST_PRECEDENCE:
+ ++unreachable;
+ printf(" !V");
+ break;
+
+ case ICMP_UNREACH_PRECEDENCE_CUTOFF:
+ ++unreachable;
+ printf(" !C");
+ break;
+
+ case ICMP_UNREACH_NET_UNKNOWN:
+ case ICMP_UNREACH_HOST_UNKNOWN:
+ ++unreachable;
+ printf(" !U");
+ break;
+
+ case ICMP_UNREACH_ISOLATED:
+ ++unreachable;
+ printf(" !I");
+ break;
+
+ case ICMP_UNREACH_TOSNET:
+ case ICMP_UNREACH_TOSHOST:
+ ++unreachable;
+ printf(" !T");
+ break;
+
+ default:
+ ++unreachable;
+ printf(" !<%d>", code);
+ break;
+ }
+ break;
+ }
+ if (cc == 0)
+ printf(" *");
+ (void)fflush(stdout);
+ }
+ putchar('\n');
+ if (got_there ||
+ (unreachable > 0 && unreachable >= nprobes - 1))
+ break;
+ }
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/udhcp/Config.in b/i/pc104/initrd/conf/busybox/networking/udhcp/Config.in
new file mode 100644
index 0000000..9dd02c4
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/udhcp/Config.in
@@ -0,0 +1,75 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+config APP_UDHCPD
+ bool "udhcp Server (udhcpd)"
+ default n
+ help
+ uDHCPd is a DHCP server geared primarily toward embedded systems,
+ while striving to be fully functional and RFC compliant.
+
+ See http://udhcp.busybox.net for further details.
+
+config APP_DHCPRELAY
+ bool "dhcprelay"
+ default n
+ depends on APP_UDHCPD
+ help
+ dhcprelay listens for dhcp requests on one or more interfaces
+ and forwards these requests to a different interface or dhcp
+ server.
+
+config APP_DUMPLEASES
+ bool "Lease display utility (dumpleases)"
+ default n
+ depends on APP_UDHCPD
+ help
+ dumpleases displays the leases written out by the udhcpd server.
+ Lease times are stored in the file by time remaining in lease, or
+ by the absolute time that it expires in seconds from epoch.
+
+ See http://udhcp.busybox.net for further details.
+
+config APP_UDHCPC
+ bool "udhcp Client (udhcpc)"
+ default n
+ help
+ uDHCPc is a DHCP client geared primarily toward embedded systems,
+ while striving to be fully functional and RFC compliant.
+
+ The udhcp client negotiates a lease with the DHCP server and
+ notifies a set of scripts when a lease is obtained or lost.
+
+ See http://udhcp.busybox.net for further details.
+
+config FEATURE_UDHCP_SYSLOG
+ bool "Log udhcp messages to syslog"
+ default n
+ depends on APP_UDHCPD || APP_UDHCPC
+ select FEATURE_SYSLOG
+ help
+ If not daemonized, udhcpd prints its messages to stdout/stderr.
+ If this option is selected, it will also log them to syslog.
+
+ See http://udhcp.busybox.net for further details.
+
+config FEATURE_UDHCP_DEBUG
+ bool "Compile udhcp with noisy debugging messages"
+ default n
+ depends on APP_UDHCPD || APP_UDHCPC
+ help
+ If selected, udhcpd will output extra debugging output. If using
+ this option, compile uDHCP with "-g", and do not fork the daemon to
+ the background.
+
+ See http://udhcp.busybox.net for further details.
+
+config FEATURE_RFC3397
+ bool "Support for RFC3397 domain search (experimental)"
+ default n
+ depends on APP_UDHCPD || APP_UDHCPC
+ help
+ If selected, both client and server will support passing of domain
+ search lists via option 119, specified in RFC3397.
diff --git a/i/pc104/initrd/conf/busybox/networking/udhcp/Kbuild b/i/pc104/initrd/conf/busybox/networking/udhcp/Kbuild
new file mode 100644
index 0000000..57d2511
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/udhcp/Kbuild
@@ -0,0 +1,19 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+#
+
+lib-y:=
+lib-$(CONFIG_APP_UDHCPC) += common.o options.o packet.o pidfile.o \
+ signalpipe.o socket.o
+lib-$(CONFIG_APP_UDHCPD) += common.o options.o packet.o pidfile.o \
+ signalpipe.o socket.o
+lib-$(CONFIG_APP_UDHCPC) += dhcpc.o clientpacket.o clientsocket.o \
+ script.o
+lib-$(CONFIG_APP_UDHCPD) += dhcpd.o arpping.o files.o leases.o \
+ serverpacket.o static_leases.o
+lib-$(CONFIG_APP_DUMPLEASES) += dumpleases.o
+lib-$(CONFIG_APP_DHCPRELAY) += dhcprelay.o
+lib-$(CONFIG_FEATURE_RFC3397) += domain_codec.o
diff --git a/i/pc104/initrd/conf/busybox/networking/udhcp/arpping.c b/i/pc104/initrd/conf/busybox/networking/udhcp/arpping.c
new file mode 100644
index 0000000..f78fd3f
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/udhcp/arpping.c
@@ -0,0 +1,114 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * arpping.c
+ *
+ * Mostly stolen from: dhcpcd - DHCP client daemon
+ * by Yoichi Hariguchi <yoichi@fore.com>
+ */
+
+#include <netinet/if_ether.h>
+#include <net/if_arp.h>
+
+#include "common.h"
+#include "dhcpd.h"
+
+
+struct arpMsg {
+ /* Ethernet header */
+ uint8_t h_dest[6]; /* destination ether addr */
+ uint8_t h_source[6]; /* source ether addr */
+ uint16_t h_proto; /* packet type ID field */
+
+ /* ARP packet */
+ uint16_t htype; /* hardware type (must be ARPHRD_ETHER) */
+ uint16_t ptype; /* protocol type (must be ETH_P_IP) */
+ uint8_t hlen; /* hardware address length (must be 6) */
+ uint8_t plen; /* protocol address length (must be 4) */
+ uint16_t operation; /* ARP opcode */
+ uint8_t sHaddr[6]; /* sender's hardware address */
+ uint8_t sInaddr[4]; /* sender's IP address */
+ uint8_t tHaddr[6]; /* target's hardware address */
+ uint8_t tInaddr[4]; /* target's IP address */
+ uint8_t pad[18]; /* pad for min. Ethernet payload (60 bytes) */
+} ATTRIBUTE_PACKED;
+
+/* args: yiaddr - what IP to ping
+ * ip - our ip
+ * mac - our arp address
+ * interface - interface to use
+ * retn: 1 addr free
+ * 0 addr used
+ * -1 error
+ */
+
+/* FIXME: match response against chaddr */
+int arpping(uint32_t yiaddr, uint32_t ip, uint8_t *mac, char *interface)
+{
+ int timeout = 2;
+ int s; /* socket */
+ int rv = 1; /* return value */
+ struct sockaddr addr; /* for interface name */
+ struct arpMsg arp;
+ fd_set fdset;
+ struct timeval tm;
+ time_t prevTime;
+
+
+ s = socket(PF_PACKET, SOCK_PACKET, htons(ETH_P_ARP));
+ if (s == -1) {
+ bb_perror_msg(bb_msg_can_not_create_raw_socket);
+ return -1;
+ }
+
+ if (setsockopt_broadcast(s) == -1) {
+ bb_perror_msg("cannot setsocketopt on raw socket");
+ close(s);
+ return -1;
+ }
+
+ /* send arp request */
+ memset(&arp, 0, sizeof(arp));
+ memcpy(arp.h_dest, MAC_BCAST_ADDR, 6); /* MAC DA */
+ memcpy(arp.h_source, mac, 6); /* MAC SA */
+ arp.h_proto = htons(ETH_P_ARP); /* protocol type (Ethernet) */
+ arp.htype = htons(ARPHRD_ETHER); /* hardware type */
+ arp.ptype = htons(ETH_P_IP); /* protocol type (ARP message) */
+ arp.hlen = 6; /* hardware address length */
+ arp.plen = 4; /* protocol address length */
+ arp.operation = htons(ARPOP_REQUEST); /* ARP op code */
+ memcpy(arp.sInaddr, &ip, sizeof(ip)); /* source IP address */
+ memcpy(arp.sHaddr, mac, 6); /* source hardware address */
+ memcpy(arp.tInaddr, &yiaddr, sizeof(yiaddr)); /* target IP address */
+
+ memset(&addr, 0, sizeof(addr));
+ strcpy(addr.sa_data, interface);
+ if (sendto(s, &arp, sizeof(arp), 0, &addr, sizeof(addr)) < 0)
+ rv = 0;
+
+ /* wait arp reply, and check it */
+ tm.tv_usec = 0;
+ prevTime = uptime();
+ while (timeout > 0) {
+ FD_ZERO(&fdset);
+ FD_SET(s, &fdset);
+ tm.tv_sec = timeout;
+ if (select(s + 1, &fdset, (fd_set *) NULL, (fd_set *) NULL, &tm) < 0) {
+ bb_perror_msg("error on ARPING request");
+ if (errno != EINTR) rv = 0;
+ } else if (FD_ISSET(s, &fdset)) {
+ if (recv(s, &arp, sizeof(arp), 0) < 0 ) rv = 0;
+ if (arp.operation == htons(ARPOP_REPLY) &&
+ memcmp(arp.tHaddr, mac, 6) == 0 &&
+ *((uint32_t *) arp.sInaddr) == yiaddr) {
+ DEBUG("Valid arp reply received for this address");
+ rv = 0;
+ break;
+ }
+ }
+ timeout -= uptime() - prevTime;
+ prevTime = uptime();
+ }
+ close(s);
+ DEBUG("%salid arp replies for this address", rv ? "No v" : "V");
+ return rv;
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/udhcp/clientpacket.c b/i/pc104/initrd/conf/busybox/networking/udhcp/clientpacket.c
new file mode 100644
index 0000000..69d7f2d
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/udhcp/clientpacket.c
@@ -0,0 +1,224 @@
+/* vi: set sw=4 ts=4: */
+/* clientpacket.c
+ *
+ * Packet generation and dispatching functions for the DHCP client.
+ *
+ * Russ Dill <Russ.Dill@asu.edu> July 2001
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include <features.h>
+#if (defined(__GLIBC__) && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1) || defined _NEWLIB_VERSION
+#include <netpacket/packet.h>
+#include <net/ethernet.h>
+#else
+#include <asm/types.h>
+#include <linux/if_packet.h>
+#include <linux/if_ether.h>
+#endif
+
+#include "common.h"
+#include "dhcpd.h"
+#include "dhcpc.h"
+#include "options.h"
+
+
+/* Create a random xid */
+unsigned long random_xid(void)
+{
+ static int initialized;
+ if (!initialized) {
+ unsigned long seed;
+
+ if (open_read_close("/dev/urandom", &seed, sizeof(seed)) < 0) {
+ bb_info_msg("Cannot load seed "
+ "from /dev/urandom: %s", strerror(errno));
+ seed = time(0);
+ }
+ srand(seed);
+ initialized++;
+ }
+ return rand();
+}
+
+
+/* initialize a packet with the proper defaults */
+static void init_packet(struct dhcpMessage *packet, char type)
+{
+ udhcp_init_header(packet, type);
+ memcpy(packet->chaddr, client_config.arp, 6);
+ if (client_config.clientid)
+ add_option_string(packet->options, client_config.clientid);
+ if (client_config.hostname) add_option_string(packet->options, client_config.hostname);
+ if (client_config.fqdn) add_option_string(packet->options, client_config.fqdn);
+ add_option_string(packet->options, client_config.vendorclass);
+}
+
+
+/* Add a parameter request list for stubborn DHCP servers. Pull the data
+ * from the struct in options.c. Don't do bounds checking here because it
+ * goes towards the head of the packet. */
+static void add_requests(struct dhcpMessage *packet)
+{
+ int end = end_option(packet->options);
+ int i, len = 0;
+
+ packet->options[end + OPT_CODE] = DHCP_PARAM_REQ;
+ for (i = 0; dhcp_options[i].code; i++)
+ if (dhcp_options[i].flags & OPTION_REQ)
+ packet->options[end + OPT_DATA + len++] = dhcp_options[i].code;
+ packet->options[end + OPT_LEN] = len;
+ packet->options[end + OPT_DATA + len] = DHCP_END;
+
+}
+
+
+/* Broadcast a DHCP discover packet to the network, with an optionally requested IP */
+int send_discover(unsigned long xid, unsigned long requested)
+{
+ struct dhcpMessage packet;
+
+ init_packet(&packet, DHCPDISCOVER);
+ packet.xid = xid;
+ if (requested)
+ add_simple_option(packet.options, DHCP_REQUESTED_IP, requested);
+
+ add_requests(&packet);
+ bb_info_msg("Sending discover...");
+ return udhcp_raw_packet(&packet, INADDR_ANY, CLIENT_PORT, INADDR_BROADCAST,
+ SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex);
+}
+
+
+/* Broadcasts a DHCP request message */
+int send_selecting(unsigned long xid, unsigned long server, unsigned long requested)
+{
+ struct dhcpMessage packet;
+ struct in_addr addr;
+
+ init_packet(&packet, DHCPREQUEST);
+ packet.xid = xid;
+
+ add_simple_option(packet.options, DHCP_REQUESTED_IP, requested);
+ add_simple_option(packet.options, DHCP_SERVER_ID, server);
+
+ add_requests(&packet);
+ addr.s_addr = requested;
+ bb_info_msg("Sending select for %s...", inet_ntoa(addr));
+ return udhcp_raw_packet(&packet, INADDR_ANY, CLIENT_PORT, INADDR_BROADCAST,
+ SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex);
+}
+
+
+/* Unicasts or broadcasts a DHCP renew message */
+int send_renew(unsigned long xid, unsigned long server, unsigned long ciaddr)
+{
+ struct dhcpMessage packet;
+ int ret = 0;
+
+ init_packet(&packet, DHCPREQUEST);
+ packet.xid = xid;
+ packet.ciaddr = ciaddr;
+
+ add_requests(&packet);
+ bb_info_msg("Sending renew...");
+ if (server)
+ ret = udhcp_kernel_packet(&packet, ciaddr, CLIENT_PORT, server, SERVER_PORT);
+ else ret = udhcp_raw_packet(&packet, INADDR_ANY, CLIENT_PORT, INADDR_BROADCAST,
+ SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex);
+ return ret;
+}
+
+
+/* Unicasts a DHCP release message */
+int send_release(unsigned long server, unsigned long ciaddr)
+{
+ struct dhcpMessage packet;
+
+ init_packet(&packet, DHCPRELEASE);
+ packet.xid = random_xid();
+ packet.ciaddr = ciaddr;
+
+ add_simple_option(packet.options, DHCP_REQUESTED_IP, ciaddr);
+ add_simple_option(packet.options, DHCP_SERVER_ID, server);
+
+ bb_info_msg("Sending release...");
+ return udhcp_kernel_packet(&packet, ciaddr, CLIENT_PORT, server, SERVER_PORT);
+}
+
+
+/* return -1 on errors that are fatal for the socket, -2 for those that aren't */
+int get_raw_packet(struct dhcpMessage *payload, int fd)
+{
+ int bytes;
+ struct udp_dhcp_packet packet;
+ uint32_t source, dest;
+ uint16_t check;
+
+ memset(&packet, 0, sizeof(struct udp_dhcp_packet));
+ bytes = read(fd, &packet, sizeof(struct udp_dhcp_packet));
+ if (bytes < 0) {
+ DEBUG("Cannot read on raw listening socket - ignoring");
+ usleep(500000); /* possible down interface, looping condition */
+ return -1;
+ }
+
+ if (bytes < (int) (sizeof(struct iphdr) + sizeof(struct udphdr))) {
+ DEBUG("Message too short, ignoring");
+ return -2;
+ }
+
+ if (bytes < ntohs(packet.ip.tot_len)) {
+ DEBUG("Truncated packet");
+ return -2;
+ }
+
+ /* ignore any extra garbage bytes */
+ bytes = ntohs(packet.ip.tot_len);
+
+ /* Make sure its the right packet for us, and that it passes sanity checks */
+ if (packet.ip.protocol != IPPROTO_UDP || packet.ip.version != IPVERSION
+ || packet.ip.ihl != sizeof(packet.ip) >> 2
+ || packet.udp.dest != htons(CLIENT_PORT)
+ || bytes > (int) sizeof(struct udp_dhcp_packet)
+ || ntohs(packet.udp.len) != (uint16_t)(bytes - sizeof(packet.ip))
+ ) {
+ DEBUG("Unrelated/bogus packet");
+ return -2;
+ }
+
+ /* check IP checksum */
+ check = packet.ip.check;
+ packet.ip.check = 0;
+ if (check != udhcp_checksum(&(packet.ip), sizeof(packet.ip))) {
+ DEBUG("bad IP header checksum, ignoring");
+ return -1;
+ }
+
+ /* verify the UDP checksum by replacing the header with a psuedo header */
+ source = packet.ip.saddr;
+ dest = packet.ip.daddr;
+ check = packet.udp.check;
+ packet.udp.check = 0;
+ memset(&packet.ip, 0, sizeof(packet.ip));
+
+ packet.ip.protocol = IPPROTO_UDP;
+ packet.ip.saddr = source;
+ packet.ip.daddr = dest;
+ packet.ip.tot_len = packet.udp.len; /* cheat on the psuedo-header */
+ if (check && check != udhcp_checksum(&packet, bytes)) {
+ bb_error_msg("packet with bad UDP checksum received, ignoring");
+ return -2;
+ }
+
+ memcpy(payload, &(packet.data), bytes - (sizeof(packet.ip) + sizeof(packet.udp)));
+
+ if (ntohl(payload->cookie) != DHCP_MAGIC) {
+ bb_error_msg("received bogus message (bad magic) - ignoring");
+ return -2;
+ }
+ DEBUG("oooooh!!! got some!");
+ return bytes - (sizeof(packet.ip) + sizeof(packet.udp));
+
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/udhcp/clientsocket.c b/i/pc104/initrd/conf/busybox/networking/udhcp/clientsocket.c
new file mode 100644
index 0000000..541f883
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/udhcp/clientsocket.c
@@ -0,0 +1,51 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * clientsocket.c -- DHCP client socket creation
+ *
+ * udhcp client
+ *
+ * Russ Dill <Russ.Dill@asu.edu> July 2001
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <features.h>
+#if (defined(__GLIBC__) && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1) || defined(_NEWLIB_VERSION)
+#include <netpacket/packet.h>
+#include <net/ethernet.h>
+#else
+#include <asm/types.h>
+#include <linux/if_packet.h>
+#include <linux/if_ether.h>
+#endif
+
+#include "common.h"
+
+
+int raw_socket(int ifindex)
+{
+ int fd;
+ struct sockaddr_ll sock;
+
+ DEBUG("Opening raw socket on ifindex %d", ifindex);
+ fd = xsocket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP));
+
+ sock.sll_family = AF_PACKET;
+ sock.sll_protocol = htons(ETH_P_IP);
+ sock.sll_ifindex = ifindex;
+ xbind(fd, (struct sockaddr *) &sock, sizeof(sock));
+
+ return fd;
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/udhcp/common.c b/i/pc104/initrd/conf/busybox/networking/udhcp/common.c
new file mode 100644
index 0000000..3704ba7
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/udhcp/common.c
@@ -0,0 +1,61 @@
+/* vi: set sw=4 ts=4: */
+/* common.c
+ *
+ * Functions for debugging and logging as well as some other
+ * simple helper functions.
+ *
+ * Russ Dill <Russ.Dill@asu.edu> 2001-2003
+ * Rewritten by Vladimir Oleynik <dzo@simtreas.ru> (C) 2003
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include <syslog.h>
+
+#include "common.h"
+
+
+long uptime(void)
+{
+ struct sysinfo info;
+ sysinfo(&info);
+ return info.uptime;
+}
+
+void udhcp_background(const char *pidfile)
+{
+#ifdef __uClinux__
+ bb_error_msg("cannot background in uclinux (yet)");
+#else /* __uClinux__ */
+ int pid_fd;
+
+ /* hold lock during fork. */
+ pid_fd = pidfile_acquire(pidfile);
+ setsid();
+ xdaemon(0, 0);
+ logmode &= ~LOGMODE_STDIO;
+ pidfile_write_release(pid_fd);
+#endif /* __uClinux__ */
+}
+
+void udhcp_start_log_and_pid(const char *pidfile)
+{
+ int pid_fd;
+
+ /* Make sure our syslog fd isn't overwritten */
+ bb_sanitize_stdio();
+
+ /* do some other misc startup stuff while we are here to save bytes */
+ pid_fd = pidfile_acquire(pidfile);
+ pidfile_write_release(pid_fd);
+
+ /* equivelent of doing a fflush after every \n */
+ setlinebuf(stdout);
+
+ if (ENABLE_FEATURE_UDHCP_SYSLOG) {
+ openlog(applet_name, LOG_PID, LOG_LOCAL0);
+ logmode |= LOGMODE_SYSLOG;
+ }
+
+ bb_info_msg("%s (v%s) started", applet_name, BB_VER);
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/udhcp/common.h b/i/pc104/initrd/conf/busybox/networking/udhcp/common.h
new file mode 100644
index 0000000..a80691b
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/udhcp/common.h
@@ -0,0 +1,104 @@
+/* vi: set sw=4 ts=4: */
+/* common.h
+ *
+ * Russ Dill <Russ.Dill@asu.edu> September 2001
+ * Rewritten by Vladimir Oleynik <dzo@simtreas.ru> (C) 2003
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#ifndef _COMMON_H
+#define _COMMON_H
+
+#include "busybox.h"
+
+#define DEFAULT_SCRIPT "/usr/share/udhcpc/default.script"
+
+#define COMBINED_BINARY
+
+
+/*** packet.h ***/
+
+#include <netinet/udp.h>
+#include <netinet/ip.h>
+
+struct dhcpMessage {
+ uint8_t op;
+ uint8_t htype;
+ uint8_t hlen;
+ uint8_t hops;
+ uint32_t xid;
+ uint16_t secs;
+ uint16_t flags;
+ uint32_t ciaddr;
+ uint32_t yiaddr;
+ uint32_t siaddr;
+ uint32_t giaddr;
+ uint8_t chaddr[16];
+ uint8_t sname[64];
+ uint8_t file[128];
+ uint32_t cookie;
+ uint8_t options[308]; /* 312 - cookie */
+};
+
+struct udp_dhcp_packet {
+ struct iphdr ip;
+ struct udphdr udp;
+ struct dhcpMessage data;
+};
+
+void udhcp_init_header(struct dhcpMessage *packet, char type);
+int udhcp_get_packet(struct dhcpMessage *packet, int fd);
+uint16_t udhcp_checksum(void *addr, int count);
+int udhcp_raw_packet(struct dhcpMessage *payload,
+ uint32_t source_ip, int source_port,
+ uint32_t dest_ip, int dest_port, uint8_t *dest_arp, int ifindex);
+int udhcp_kernel_packet(struct dhcpMessage *payload,
+ uint32_t source_ip, int source_port,
+ uint32_t dest_ip, int dest_port);
+
+
+/**/
+
+void udhcp_background(const char *pidfile);
+void udhcp_start_log_and_pid(const char *pidfile);
+
+void udhcp_run_script(struct dhcpMessage *packet, const char *name);
+
+// Still need to clean these up...
+
+/* from pidfile.h */
+#define pidfile_acquire udhcp_pidfile_acquire
+#define pidfile_write_release udhcp_pidfile_write_release
+/* from options.h */
+#define get_option udhcp_get_option
+#define end_option udhcp_end_option
+#define add_option_string udhcp_add_option_string
+#define add_simple_option udhcp_add_simple_option
+#define option_lengths udhcp_option_lengths
+/* from socket.h */
+#define listen_socket udhcp_listen_socket
+#define read_interface udhcp_read_interface
+/* from dhcpc.h */
+#define client_config udhcp_client_config
+/* from dhcpd.h */
+#define server_config udhcp_server_config
+
+long uptime(void);
+void udhcp_sp_setup(void);
+int udhcp_sp_fd_set(fd_set *rfds, int extra_fd);
+int udhcp_sp_read(fd_set *rfds);
+int raw_socket(int ifindex);
+int read_interface(const char *interface, int *ifindex, uint32_t *addr, uint8_t *arp);
+int listen_socket(uint32_t ip, int port, const char *inf);
+int pidfile_acquire(const char *pidfile);
+void pidfile_write_release(int pid_fd);
+int arpping(uint32_t yiaddr, uint32_t ip, uint8_t *arp, char *interface);
+
+#if ENABLE_FEATURE_UDHCP_DEBUG
+# define DEBUG(str, args...) bb_info_msg(str, ## args)
+#else
+# define DEBUG(str, args...) do {;} while (0)
+#endif
+
+#endif
diff --git a/i/pc104/initrd/conf/busybox/networking/udhcp/dhcpc.c b/i/pc104/initrd/conf/busybox/networking/udhcp/dhcpc.c
new file mode 100644
index 0000000..a59c5db
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/udhcp/dhcpc.c
@@ -0,0 +1,509 @@
+/* vi: set sw=4 ts=4: */
+/* dhcpc.c
+ *
+ * udhcp DHCP client
+ *
+ * Russ Dill <Russ.Dill@asu.edu> July 2001
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ */
+
+#include <getopt.h>
+
+#include "common.h"
+#include "dhcpd.h"
+#include "dhcpc.h"
+#include "options.h"
+
+
+static int state;
+/* Something is definitely wrong here. IPv4 addresses
+ * in variables of type long?? BTW, we use inet_ntoa()
+ * in the code. Manpage says that struct in_addr has a member of type long (!)
+ * which holds IPv4 address, and the struct is passed by value (!!)
+ */
+static unsigned long requested_ip; /* = 0 */
+static uint32_t server_addr;
+static unsigned long timeout;
+static int packet_num; /* = 0 */
+static int fd = -1;
+
+#define LISTEN_NONE 0
+#define LISTEN_KERNEL 1
+#define LISTEN_RAW 2
+static int listen_mode;
+
+struct client_config_t client_config;
+
+
+/* just a little helper */
+static void change_mode(int new_mode)
+{
+ DEBUG("entering %s listen mode",
+ new_mode ? (new_mode == 1 ? "kernel" : "raw") : "none");
+ if (fd >= 0) close(fd);
+ fd = -1;
+ listen_mode = new_mode;
+}
+
+
+/* perform a renew */
+static void perform_renew(void)
+{
+ bb_info_msg("Performing a DHCP renew");
+ switch (state) {
+ case BOUND:
+ change_mode(LISTEN_KERNEL);
+ case RENEWING:
+ case REBINDING:
+ state = RENEW_REQUESTED;
+ break;
+ case RENEW_REQUESTED: /* impatient are we? fine, square 1 */
+ udhcp_run_script(NULL, "deconfig");
+ case REQUESTING:
+ case RELEASED:
+ change_mode(LISTEN_RAW);
+ state = INIT_SELECTING;
+ break;
+ case INIT_SELECTING:
+ break;
+ }
+
+ /* start things over */
+ packet_num = 0;
+
+ /* Kill any timeouts because the user wants this to hurry along */
+ timeout = 0;
+}
+
+
+/* perform a release */
+static void perform_release(void)
+{
+ char buffer[16];
+ struct in_addr temp_addr;
+
+ /* send release packet */
+ if (state == BOUND || state == RENEWING || state == REBINDING) {
+ temp_addr.s_addr = server_addr;
+ sprintf(buffer, "%s", inet_ntoa(temp_addr));
+ temp_addr.s_addr = requested_ip;
+ bb_info_msg("Unicasting a release of %s to %s",
+ inet_ntoa(temp_addr), buffer);
+ send_release(server_addr, requested_ip); /* unicast */
+ udhcp_run_script(NULL, "deconfig");
+ }
+ bb_info_msg("Entering released state");
+
+ change_mode(LISTEN_NONE);
+ state = RELEASED;
+ timeout = 0x7fffffff;
+}
+
+
+static void client_background(void)
+{
+ udhcp_background(client_config.pidfile);
+ client_config.foreground = 1; /* Do not fork again. */
+ client_config.background_if_no_lease = 0;
+}
+
+
+static uint8_t* alloc_dhcp_option(int code, const char *str, int extra)
+{
+ uint8_t *storage;
+ int len = strlen(str);
+ if (len > 255) len = 255;
+ storage = xzalloc(len + extra + OPT_DATA);
+ storage[OPT_CODE] = code;
+ storage[OPT_LEN] = len + extra;
+ memcpy(storage + extra + OPT_DATA, str, len);
+ return storage;
+}
+
+
+int udhcpc_main(int argc, char *argv[]);
+int udhcpc_main(int argc, char *argv[])
+{
+ uint8_t *temp, *message;
+ char *str_c, *str_V, *str_h, *str_F, *str_r, *str_T, *str_t;
+ unsigned long t1 = 0, t2 = 0, xid = 0;
+ unsigned long start = 0, lease = 0;
+ long now;
+ unsigned opt;
+ int max_fd;
+ int sig;
+ int retval;
+ int len;
+ int no_clientid = 0;
+ fd_set rfds;
+ struct timeval tv;
+ struct dhcpMessage packet;
+ struct in_addr temp_addr;
+
+ enum {
+ OPT_c = 1 << 0,
+ OPT_C = 1 << 1,
+ OPT_V = 1 << 2,
+ OPT_f = 1 << 3,
+ OPT_b = 1 << 4,
+ OPT_H = 1 << 5,
+ OPT_h = 1 << 6,
+ OPT_F = 1 << 7,
+ OPT_i = 1 << 8,
+ OPT_n = 1 << 9,
+ OPT_p = 1 << 10,
+ OPT_q = 1 << 11,
+ OPT_R = 1 << 12,
+ OPT_r = 1 << 13,
+ OPT_s = 1 << 14,
+ OPT_T = 1 << 15,
+ OPT_t = 1 << 16,
+ OPT_v = 1 << 17,
+ };
+#if ENABLE_GETOPT_LONG
+ static const struct option arg_options[] = {
+ { "clientid", required_argument, 0, 'c' },
+ { "clientid-none", no_argument, 0, 'C' },
+ { "vendorclass", required_argument, 0, 'V' },
+ { "foreground", no_argument, 0, 'f' },
+ { "background", no_argument, 0, 'b' },
+ { "hostname", required_argument, 0, 'H' },
+ { "hostname", required_argument, 0, 'h' },
+ { "fqdn", required_argument, 0, 'F' },
+ { "interface", required_argument, 0, 'i' },
+ { "now", no_argument, 0, 'n' },
+ { "pidfile", required_argument, 0, 'p' },
+ { "quit", no_argument, 0, 'q' },
+ { "release", no_argument, 0, 'R' },
+ { "request", required_argument, 0, 'r' },
+ { "script", required_argument, 0, 's' },
+ { "timeout", required_argument, 0, 'T' },
+ { "version", no_argument, 0, 'v' },
+ { "retries", required_argument, 0, 't' },
+ { 0, 0, 0, 0 }
+ };
+#endif
+ /* Default options. */
+ client_config.interface = "eth0";
+ client_config.script = DEFAULT_SCRIPT;
+ client_config.retries = 3;
+ client_config.timeout = 3;
+
+ /* Parse command line */
+ opt_complementary = "?:c--C:C--c" // mutually exclusive
+ ":hH:Hh"; // -h and -H are the same
+#if ENABLE_GETOPT_LONG
+ applet_long_options = arg_options;
+#endif
+ opt = getopt32(argc, argv, "c:CV:fbH:h:F:i:np:qRr:s:T:t:v",
+ &str_c, &str_V, &str_h, &str_h, &str_F,
+ &client_config.interface, &client_config.pidfile, &str_r,
+ &client_config.script, &str_T, &str_t
+ );
+
+ if (opt & OPT_c)
+ client_config.clientid = alloc_dhcp_option(DHCP_CLIENT_ID, str_c, 0);
+ if (opt & OPT_C)
+ no_clientid = 1;
+ if (opt & OPT_V)
+ client_config.vendorclass = alloc_dhcp_option(DHCP_VENDOR, str_V, 0);
+ if (opt & OPT_f)
+ client_config.foreground = 1;
+ if (opt & OPT_b)
+ client_config.background_if_no_lease = 1;
+ if (opt & OPT_h)
+ client_config.hostname = alloc_dhcp_option(DHCP_HOST_NAME, str_h, 0);
+ if (opt & OPT_F) {
+ client_config.fqdn = alloc_dhcp_option(DHCP_FQDN, str_F, 3);
+ /* Flags: 0000NEOS
+ S: 1 => Client requests Server to update A RR in DNS as well as PTR
+ O: 1 => Server indicates to client that DNS has been updated regardless
+ E: 1 => Name data is DNS format, i.e. <4>host<6>domain<4>com<0> not "host.domain.com"
+ N: 1 => Client requests Server to not update DNS
+ */
+ client_config.fqdn[OPT_DATA + 0] = 0x1;
+ /* client_config.fqdn[OPT_DATA + 1] = 0; - redundant */
+ /* client_config.fqdn[OPT_DATA + 2] = 0; - redundant */
+ }
+ // if (opt & OPT_i) client_config.interface = ...
+ if (opt & OPT_n)
+ client_config.abort_if_no_lease = 1;
+ // if (opt & OPT_p) client_config.pidfile = ...
+ if (opt & OPT_q)
+ client_config.quit_after_lease = 1;
+ if (opt & OPT_R)
+ client_config.release_on_quit = 1;
+ if (opt & OPT_r)
+ requested_ip = inet_addr(str_r);
+ // if (opt & OPT_s) client_config.script = ...
+ if (opt & OPT_T)
+ client_config.timeout = xatoi_u(str_T);
+ if (opt & OPT_t)
+ client_config.retries = xatoi_u(str_t);
+ if (opt & OPT_v) {
+ printf("version %s\n\n", BB_VER);
+ return 0;
+ }
+
+ /* Start the log, sanitize fd's, and write a pid file */
+ udhcp_start_log_and_pid(client_config.pidfile);
+
+ if (read_interface(client_config.interface, &client_config.ifindex,
+ NULL, client_config.arp) < 0)
+ return 1;
+
+ /* if not set, and not suppressed, setup the default client ID */
+ if (!client_config.clientid && !no_clientid) {
+ client_config.clientid = alloc_dhcp_option(DHCP_CLIENT_ID, "", 7);
+ client_config.clientid[OPT_DATA] = 1;
+ memcpy(client_config.clientid + OPT_DATA+1, client_config.arp, 6);
+ }
+
+ if (!client_config.vendorclass)
+ client_config.vendorclass = alloc_dhcp_option(DHCP_VENDOR, "udhcp "BB_VER, 0);
+
+ /* setup the signal pipe */
+ udhcp_sp_setup();
+
+ state = INIT_SELECTING;
+ udhcp_run_script(NULL, "deconfig");
+ change_mode(LISTEN_RAW);
+
+ for (;;) {
+ tv.tv_sec = timeout - uptime();
+ tv.tv_usec = 0;
+
+ if (listen_mode != LISTEN_NONE && fd < 0) {
+ if (listen_mode == LISTEN_KERNEL)
+ fd = listen_socket(INADDR_ANY, CLIENT_PORT, client_config.interface);
+ else
+ fd = raw_socket(client_config.ifindex);
+ }
+ max_fd = udhcp_sp_fd_set(&rfds, fd);
+
+ if (tv.tv_sec > 0) {
+ DEBUG("Waiting on select...");
+ retval = select(max_fd + 1, &rfds, NULL, NULL, &tv);
+ } else retval = 0; /* If we already timed out, fall through */
+
+ now = uptime();
+ if (retval == 0) {
+ /* timeout dropped to zero */
+ switch (state) {
+ case INIT_SELECTING:
+ if (packet_num < client_config.retries) {
+ if (packet_num == 0)
+ xid = random_xid();
+
+ /* send discover packet */
+ send_discover(xid, requested_ip); /* broadcast */
+
+ timeout = now + client_config.timeout;
+ packet_num++;
+ } else {
+ udhcp_run_script(NULL, "leasefail");
+ if (client_config.background_if_no_lease) {
+ bb_info_msg("No lease, forking to background");
+ client_background();
+ } else if (client_config.abort_if_no_lease) {
+ bb_info_msg("No lease, failing");
+ return 1;
+ }
+ /* wait to try again */
+ packet_num = 0;
+ timeout = now + 60;
+ }
+ break;
+ case RENEW_REQUESTED:
+ case REQUESTING:
+ if (packet_num < client_config.retries) {
+ /* send request packet */
+ if (state == RENEW_REQUESTED)
+ send_renew(xid, server_addr, requested_ip); /* unicast */
+ else send_selecting(xid, server_addr, requested_ip); /* broadcast */
+
+ timeout = now + ((packet_num == 2) ? 10 : 2);
+ packet_num++;
+ } else {
+ /* timed out, go back to init state */
+ if (state == RENEW_REQUESTED) udhcp_run_script(NULL, "deconfig");
+ state = INIT_SELECTING;
+ timeout = now;
+ packet_num = 0;
+ change_mode(LISTEN_RAW);
+ }
+ break;
+ case BOUND:
+ /* Lease is starting to run out, time to enter renewing state */
+ state = RENEWING;
+ change_mode(LISTEN_KERNEL);
+ DEBUG("Entering renew state");
+ /* fall right through */
+ case RENEWING:
+ /* Either set a new T1, or enter REBINDING state */
+ if ((t2 - t1) <= (lease / 14400 + 1)) {
+ /* timed out, enter rebinding state */
+ state = REBINDING;
+ timeout = now + (t2 - t1);
+ DEBUG("Entering rebinding state");
+ } else {
+ /* send a request packet */
+ send_renew(xid, server_addr, requested_ip); /* unicast */
+
+ t1 = (t2 - t1) / 2 + t1;
+ timeout = t1 + start;
+ }
+ break;
+ case REBINDING:
+ /* Either set a new T2, or enter INIT state */
+ if ((lease - t2) <= (lease / 14400 + 1)) {
+ /* timed out, enter init state */
+ state = INIT_SELECTING;
+ bb_info_msg("Lease lost, entering init state");
+ udhcp_run_script(NULL, "deconfig");
+ timeout = now;
+ packet_num = 0;
+ change_mode(LISTEN_RAW);
+ } else {
+ /* send a request packet */
+ send_renew(xid, 0, requested_ip); /* broadcast */
+
+ t2 = (lease - t2) / 2 + t2;
+ timeout = t2 + start;
+ }
+ break;
+ case RELEASED:
+ /* yah, I know, *you* say it would never happen */
+ timeout = 0x7fffffff;
+ break;
+ }
+ } else if (retval > 0 && listen_mode != LISTEN_NONE && FD_ISSET(fd, &rfds)) {
+ /* a packet is ready, read it */
+
+ if (listen_mode == LISTEN_KERNEL)
+ len = udhcp_get_packet(&packet, fd);
+ else len = get_raw_packet(&packet, fd);
+
+ if (len == -1 && errno != EINTR) {
+ DEBUG("error on read, %s, reopening socket", strerror(errno));
+ change_mode(listen_mode); /* just close and reopen */
+ }
+ if (len < 0) continue;
+
+ if (packet.xid != xid) {
+ DEBUG("Ignoring XID %lx (our xid is %lx)",
+ (unsigned long) packet.xid, xid);
+ continue;
+ }
+
+ /* Ignore packets that aren't for us */
+ if (memcmp(packet.chaddr, client_config.arp, 6)) {
+ DEBUG("Packet does not have our chaddr - ignoring");
+ continue;
+ }
+
+ if ((message = get_option(&packet, DHCP_MESSAGE_TYPE)) == NULL) {
+ bb_error_msg("cannot get option from packet - ignoring");
+ continue;
+ }
+
+ switch (state) {
+ case INIT_SELECTING:
+ /* Must be a DHCPOFFER to one of our xid's */
+ if (*message == DHCPOFFER) {
+ temp = get_option(&packet, DHCP_SERVER_ID);
+ if (temp) {
+ /* can be misaligned, thus memcpy */
+ memcpy(&server_addr, temp, 4);
+ xid = packet.xid;
+ requested_ip = packet.yiaddr;
+
+ /* enter requesting state */
+ state = REQUESTING;
+ timeout = now;
+ packet_num = 0;
+ } else {
+ bb_error_msg("no server ID in message");
+ }
+ }
+ break;
+ case RENEW_REQUESTED:
+ case REQUESTING:
+ case RENEWING:
+ case REBINDING:
+ if (*message == DHCPACK) {
+ temp = get_option(&packet, DHCP_LEASE_TIME);
+ if (!temp) {
+ bb_error_msg("no lease time with ACK, using 1 hour lease");
+ lease = 60 * 60;
+ } else {
+ /* can be misaligned, thus memcpy */
+ memcpy(&lease, temp, 4);
+ lease = ntohl(lease);
+ }
+
+ /* enter bound state */
+ t1 = lease / 2;
+
+ /* little fixed point for n * .875 */
+ t2 = (lease * 0x7) >> 3;
+ temp_addr.s_addr = packet.yiaddr;
+ bb_info_msg("Lease of %s obtained, lease time %ld",
+ inet_ntoa(temp_addr), lease);
+ start = now;
+ timeout = t1 + start;
+ requested_ip = packet.yiaddr;
+ udhcp_run_script(&packet,
+ ((state == RENEWING || state == REBINDING) ? "renew" : "bound"));
+
+ state = BOUND;
+ change_mode(LISTEN_NONE);
+ if (client_config.quit_after_lease) {
+ if (client_config.release_on_quit)
+ perform_release();
+ return 0;
+ }
+ if (!client_config.foreground)
+ client_background();
+
+ } else if (*message == DHCPNAK) {
+ /* return to init state */
+ bb_info_msg("Received DHCP NAK");
+ udhcp_run_script(&packet, "nak");
+ if (state != REQUESTING)
+ udhcp_run_script(NULL, "deconfig");
+ state = INIT_SELECTING;
+ timeout = now;
+ requested_ip = 0;
+ packet_num = 0;
+ change_mode(LISTEN_RAW);
+ sleep(3); /* avoid excessive network traffic */
+ }
+ break;
+ /* case BOUND, RELEASED: - ignore all packets */
+ }
+ } else if (retval > 0 && (sig = udhcp_sp_read(&rfds))) {
+ switch (sig) {
+ case SIGUSR1:
+ perform_renew();
+ break;
+ case SIGUSR2:
+ perform_release();
+ break;
+ case SIGTERM:
+ bb_info_msg("Received SIGTERM");
+ if (client_config.release_on_quit)
+ perform_release();
+ return 0;
+ }
+ } else if (retval == -1 && errno == EINTR) {
+ /* a signal was caught */
+ } else {
+ /* An error occured */
+ bb_perror_msg("select");
+ }
+
+ }
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/udhcp/dhcpc.h b/i/pc104/initrd/conf/busybox/networking/udhcp/dhcpc.h
new file mode 100644
index 0000000..4ddd121
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/udhcp/dhcpc.h
@@ -0,0 +1,50 @@
+/* vi: set sw=4 ts=4: */
+/* dhcpc.h */
+#ifndef _DHCPC_H
+#define _DHCPC_H
+
+#define INIT_SELECTING 0
+#define REQUESTING 1
+#define BOUND 2
+#define RENEWING 3
+#define REBINDING 4
+#define INIT_REBOOT 5
+#define RENEW_REQUESTED 6
+#define RELEASED 7
+
+struct client_config_t {
+ /* TODO: combine flag fields into single "unsigned opt" */
+ /* (can be set directly to the result of getopt32) */
+ char foreground; /* Do not fork */
+ char quit_after_lease; /* Quit after obtaining lease */
+ char release_on_quit; /* perform release on quit */
+ char abort_if_no_lease; /* Abort if no lease */
+ char background_if_no_lease; /* Fork to background if no lease */
+ const char *interface; /* The name of the interface to use */
+ char *pidfile; /* Optionally store the process ID */
+ const char *script; /* User script to run at dhcp events */
+ uint8_t *clientid; /* Optional client id to use */
+ uint8_t *vendorclass; /* Optional vendor class-id to use */
+ uint8_t *hostname; /* Optional hostname to use */
+ uint8_t *fqdn; /* Optional fully qualified domain name to use */
+ int ifindex; /* Index number of the interface to use */
+ int retries; /* Max number of request packets */
+ int timeout; /* Number of seconds to try to get a lease */
+ uint8_t arp[6]; /* Our arp address */
+};
+
+extern struct client_config_t client_config;
+
+
+/*** clientpacket.h ***/
+
+unsigned long random_xid(void);
+int send_discover(unsigned long xid, unsigned long requested);
+int send_selecting(unsigned long xid, unsigned long server, unsigned long requested);
+int send_renew(unsigned long xid, unsigned long server, unsigned long ciaddr);
+int send_renew(unsigned long xid, unsigned long server, unsigned long ciaddr);
+int send_release(unsigned long server, unsigned long ciaddr);
+int get_raw_packet(struct dhcpMessage *payload, int fd);
+
+
+#endif
diff --git a/i/pc104/initrd/conf/busybox/networking/udhcp/dhcpd.c b/i/pc104/initrd/conf/busybox/networking/udhcp/dhcpd.c
new file mode 100644
index 0000000..90d8f0d
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/udhcp/dhcpd.c
@@ -0,0 +1,223 @@
+/* vi: set sw=4 ts=4: */
+/* dhcpd.c
+ *
+ * udhcp Server
+ * Copyright (C) 1999 Matthew Ramsay <matthewr@moreton.com.au>
+ * Chris Trew <ctrew@moreton.com.au>
+ *
+ * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "common.h"
+#include "dhcpd.h"
+#include "options.h"
+
+
+/* globals */
+struct dhcpOfferedAddr *leases;
+struct server_config_t server_config;
+
+
+int udhcpd_main(int argc, char *argv[]);
+int udhcpd_main(int argc, char *argv[])
+{
+ fd_set rfds;
+ struct timeval tv;
+ int server_socket = -1, bytes, retval, max_sock;
+ struct dhcpMessage packet;
+ uint8_t *state, *server_id, *requested;
+ uint32_t server_id_align, requested_align, static_lease_ip;
+ unsigned long timeout_end, num_ips;
+ struct option_set *option;
+ struct dhcpOfferedAddr *lease, static_lease;
+
+ read_config(argc < 2 ? DHCPD_CONF_FILE : argv[1]);
+
+ /* Start the log, sanitize fd's, and write a pid file */
+ udhcp_start_log_and_pid(server_config.pidfile);
+
+ if ((option = find_option(server_config.options, DHCP_LEASE_TIME))) {
+ memcpy(&server_config.lease, option->data + 2, 4);
+ server_config.lease = ntohl(server_config.lease);
+ }
+ else server_config.lease = LEASE_TIME;
+
+ /* Sanity check */
+ num_ips = ntohl(server_config.end) - ntohl(server_config.start) + 1;
+ if (server_config.max_leases > num_ips) {
+ bb_error_msg("max_leases value (%lu) not sane, "
+ "setting to %lu instead",
+ server_config.max_leases, num_ips);
+ server_config.max_leases = num_ips;
+ }
+
+ leases = xzalloc(server_config.max_leases * sizeof(struct dhcpOfferedAddr));
+ read_leases(server_config.lease_file);
+
+ if (read_interface(server_config.interface, &server_config.ifindex,
+ &server_config.server, server_config.arp) < 0)
+ return 1;
+
+ if (!ENABLE_FEATURE_UDHCP_DEBUG)
+ udhcp_background(server_config.pidfile); /* hold lock during fork. */
+
+ /* Setup the signal pipe */
+ udhcp_sp_setup();
+
+ timeout_end = time(0) + server_config.auto_time;
+ while (1) { /* loop until universe collapses */
+
+ if (server_socket < 0) {
+ server_socket = listen_socket(INADDR_ANY, SERVER_PORT, server_config.interface);
+ }
+
+ max_sock = udhcp_sp_fd_set(&rfds, server_socket);
+ if (server_config.auto_time) {
+ tv.tv_sec = timeout_end - time(0);
+ tv.tv_usec = 0;
+ }
+ if (!server_config.auto_time || tv.tv_sec > 0) {
+ retval = select(max_sock + 1, &rfds, NULL, NULL,
+ server_config.auto_time ? &tv : NULL);
+ } else retval = 0; /* If we already timed out, fall through */
+
+ if (retval == 0) {
+ write_leases();
+ timeout_end = time(0) + server_config.auto_time;
+ continue;
+ } else if (retval < 0 && errno != EINTR) {
+ DEBUG("error on select");
+ continue;
+ }
+
+ switch (udhcp_sp_read(&rfds)) {
+ case SIGUSR1:
+ bb_info_msg("Received a SIGUSR1");
+ write_leases();
+ /* why not just reset the timeout, eh */
+ timeout_end = time(0) + server_config.auto_time;
+ continue;
+ case SIGTERM:
+ bb_info_msg("Received a SIGTERM");
+ return 0;
+ case 0: break; /* no signal */
+ default: continue; /* signal or error (probably EINTR) */
+ }
+
+ if ((bytes = udhcp_get_packet(&packet, server_socket)) < 0) { /* this waits for a packet - idle */
+ if (bytes == -1 && errno != EINTR) {
+ DEBUG("error on read, %s, reopening socket", strerror(errno));
+ close(server_socket);
+ server_socket = -1;
+ }
+ continue;
+ }
+
+ if ((state = get_option(&packet, DHCP_MESSAGE_TYPE)) == NULL) {
+ bb_error_msg("cannot get option from packet, ignoring");
+ continue;
+ }
+
+ /* Look for a static lease */
+ static_lease_ip = getIpByMac(server_config.static_leases, &packet.chaddr);
+
+ if (static_lease_ip) {
+ bb_info_msg("Found static lease: %x", static_lease_ip);
+
+ memcpy(&static_lease.chaddr, &packet.chaddr, 16);
+ static_lease.yiaddr = static_lease_ip;
+ static_lease.expires = 0;
+
+ lease = &static_lease;
+
+ } else {
+ lease = find_lease_by_chaddr(packet.chaddr);
+ }
+
+ switch (state[0]) {
+ case DHCPDISCOVER:
+ DEBUG("Received DISCOVER");
+
+ if (sendOffer(&packet) < 0) {
+ bb_error_msg("send OFFER failed");
+ }
+ break;
+ case DHCPREQUEST:
+ DEBUG("received REQUEST");
+
+ requested = get_option(&packet, DHCP_REQUESTED_IP);
+ server_id = get_option(&packet, DHCP_SERVER_ID);
+
+ if (requested) memcpy(&requested_align, requested, 4);
+ if (server_id) memcpy(&server_id_align, server_id, 4);
+
+ if (lease) {
+ if (server_id) {
+ /* SELECTING State */
+ DEBUG("server_id = %08x", ntohl(server_id_align));
+ if (server_id_align == server_config.server && requested &&
+ requested_align == lease->yiaddr) {
+ sendACK(&packet, lease->yiaddr);
+ }
+ } else {
+ if (requested) {
+ /* INIT-REBOOT State */
+ if (lease->yiaddr == requested_align)
+ sendACK(&packet, lease->yiaddr);
+ else sendNAK(&packet);
+ } else {
+ /* RENEWING or REBINDING State */
+ if (lease->yiaddr == packet.ciaddr)
+ sendACK(&packet, lease->yiaddr);
+ else {
+ /* don't know what to do!!!! */
+ sendNAK(&packet);
+ }
+ }
+ }
+
+ /* what to do if we have no record of the client */
+ } else if (server_id) {
+ /* SELECTING State */
+
+ } else if (requested) {
+ /* INIT-REBOOT State */
+ if ((lease = find_lease_by_yiaddr(requested_align))) {
+ if (lease_expired(lease)) {
+ /* probably best if we drop this lease */
+ memset(lease->chaddr, 0, 16);
+ /* make some contention for this address */
+ } else sendNAK(&packet);
+ } else if (requested_align < server_config.start ||
+ requested_align > server_config.end) {
+ sendNAK(&packet);
+ } /* else remain silent */
+
+ } else {
+ /* RENEWING or REBINDING State */
+ }
+ break;
+ case DHCPDECLINE:
+ DEBUG("Received DECLINE");
+ if (lease) {
+ memset(lease->chaddr, 0, 16);
+ lease->expires = time(0) + server_config.decline_time;
+ }
+ break;
+ case DHCPRELEASE:
+ DEBUG("Received RELEASE");
+ if (lease) lease->expires = time(0);
+ break;
+ case DHCPINFORM:
+ DEBUG("Received INFORM");
+ send_inform(&packet);
+ break;
+ default:
+ bb_info_msg("Unsupported DHCP message (%02x) - ignoring", state[0]);
+ }
+ }
+
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/udhcp/dhcpd.h b/i/pc104/initrd/conf/busybox/networking/udhcp/dhcpd.h
new file mode 100644
index 0000000..40959e4
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/udhcp/dhcpd.h
@@ -0,0 +1,190 @@
+/* vi: set sw=4 ts=4: */
+/* dhcpd.h */
+#ifndef _DHCPD_H
+#define _DHCPD_H
+
+/************************************/
+/* Defaults _you_ may want to tweak */
+/************************************/
+
+/* the period of time the client is allowed to use that address */
+#define LEASE_TIME (60*60*24*10) /* 10 days of seconds */
+#define LEASES_FILE "/var/lib/misc/udhcpd.leases"
+
+/* where to find the DHCP server configuration file */
+#define DHCPD_CONF_FILE "/etc/udhcpd.conf"
+
+/*****************************************************************/
+/* Do not modify below here unless you know what you are doing!! */
+/*****************************************************************/
+
+/* DHCP protocol -- see RFC 2131 */
+#define SERVER_PORT 67
+#define CLIENT_PORT 68
+
+#define DHCP_MAGIC 0x63825363
+
+/* DHCP option codes (partial list) */
+#define DHCP_PADDING 0x00
+#define DHCP_SUBNET 0x01
+#define DHCP_TIME_OFFSET 0x02
+#define DHCP_ROUTER 0x03
+#define DHCP_TIME_SERVER 0x04
+#define DHCP_NAME_SERVER 0x05
+#define DHCP_DNS_SERVER 0x06
+#define DHCP_LOG_SERVER 0x07
+#define DHCP_COOKIE_SERVER 0x08
+#define DHCP_LPR_SERVER 0x09
+#define DHCP_HOST_NAME 0x0c
+#define DHCP_BOOT_SIZE 0x0d
+#define DHCP_DOMAIN_NAME 0x0f
+#define DHCP_SWAP_SERVER 0x10
+#define DHCP_ROOT_PATH 0x11
+#define DHCP_IP_TTL 0x17
+#define DHCP_MTU 0x1a
+#define DHCP_BROADCAST 0x1c
+#define DHCP_NTP_SERVER 0x2a
+#define DHCP_WINS_SERVER 0x2c
+#define DHCP_REQUESTED_IP 0x32
+#define DHCP_LEASE_TIME 0x33
+#define DHCP_OPTION_OVER 0x34
+#define DHCP_MESSAGE_TYPE 0x35
+#define DHCP_SERVER_ID 0x36
+#define DHCP_PARAM_REQ 0x37
+#define DHCP_MESSAGE 0x38
+#define DHCP_MAX_SIZE 0x39
+#define DHCP_T1 0x3a
+#define DHCP_T2 0x3b
+#define DHCP_VENDOR 0x3c
+#define DHCP_CLIENT_ID 0x3d
+#define DHCP_FQDN 0x51
+
+#define DHCP_END 0xFF
+
+
+#define BOOTREQUEST 1
+#define BOOTREPLY 2
+
+#define ETH_10MB 1
+#define ETH_10MB_LEN 6
+
+#define DHCPDISCOVER 1
+#define DHCPOFFER 2
+#define DHCPREQUEST 3
+#define DHCPDECLINE 4
+#define DHCPACK 5
+#define DHCPNAK 6
+#define DHCPRELEASE 7
+#define DHCPINFORM 8
+
+#define BROADCAST_FLAG 0x8000
+
+#define OPTION_FIELD 0
+#define FILE_FIELD 1
+#define SNAME_FIELD 2
+
+/* miscellaneous defines */
+#define MAC_BCAST_ADDR (uint8_t *) "\xff\xff\xff\xff\xff\xff"
+#define OPT_CODE 0
+#define OPT_LEN 1
+#define OPT_DATA 2
+
+struct option_set {
+ uint8_t *data;
+ struct option_set *next;
+};
+
+struct static_lease {
+ uint8_t *mac;
+ uint32_t *ip;
+ struct static_lease *next;
+};
+
+struct server_config_t {
+ uint32_t server; /* Our IP, in network order */
+ uint32_t start; /* Start address of leases, network order */
+ uint32_t end; /* End of leases, network order */
+ struct option_set *options; /* List of DHCP options loaded from the config file */
+ char *interface; /* The name of the interface to use */
+ int ifindex; /* Index number of the interface to use */
+ uint8_t arp[6]; /* Our arp address */
+ unsigned long lease; /* lease time in seconds (host order) */
+ unsigned long max_leases; /* maximum number of leases (including reserved address) */
+ char remaining; /* should the lease file be interpreted as lease time remaining, or
+ * as the time the lease expires */
+ unsigned long auto_time; /* how long should udhcpd wait before writing a config file.
+ * if this is zero, it will only write one on SIGUSR1 */
+ unsigned long decline_time; /* how long an address is reserved if a client returns a
+ * decline message */
+ unsigned long conflict_time; /* how long an arp conflict offender is leased for */
+ unsigned long offer_time; /* how long an offered address is reserved */
+ unsigned long min_lease; /* minimum lease a client can request*/
+ char *lease_file;
+ char *pidfile;
+ char *notify_file; /* What to run whenever leases are written */
+ uint32_t siaddr; /* next server bootp option */
+ char *sname; /* bootp server name */
+ char *boot_file; /* bootp boot file option */
+ struct static_lease *static_leases; /* List of ip/mac pairs to assign static leases */
+};
+
+extern struct server_config_t server_config;
+extern struct dhcpOfferedAddr *leases;
+
+
+/*** leases.h ***/
+
+struct dhcpOfferedAddr {
+ uint8_t chaddr[16];
+ uint32_t yiaddr; /* network order */
+ uint32_t expires; /* host order */
+};
+
+extern uint8_t blank_chaddr[];
+
+void clear_lease(uint8_t *chaddr, uint32_t yiaddr);
+struct dhcpOfferedAddr *add_lease(uint8_t *chaddr, uint32_t yiaddr, unsigned long lease);
+int lease_expired(struct dhcpOfferedAddr *lease);
+struct dhcpOfferedAddr *oldest_expired_lease(void);
+struct dhcpOfferedAddr *find_lease_by_chaddr(uint8_t *chaddr);
+struct dhcpOfferedAddr *find_lease_by_yiaddr(uint32_t yiaddr);
+uint32_t find_address(int check_expired);
+
+
+/*** static_leases.h ***/
+
+/* Config file will pass static lease info to this function which will add it
+ * to a data structure that can be searched later */
+int addStaticLease(struct static_lease **lease_struct, uint8_t *mac, uint32_t *ip);
+/* Check to see if a mac has an associated static lease */
+uint32_t getIpByMac(struct static_lease *lease_struct, void *arg);
+/* Check to see if an ip is reserved as a static ip */
+uint32_t reservedIp(struct static_lease *lease_struct, uint32_t ip);
+/* Print out static leases just to check what's going on (debug code) */
+void printStaticLeases(struct static_lease **lease_struct);
+
+
+/*** serverpacket.h ***/
+
+int sendOffer(struct dhcpMessage *oldpacket);
+int sendNAK(struct dhcpMessage *oldpacket);
+int sendACK(struct dhcpMessage *oldpacket, uint32_t yiaddr);
+int send_inform(struct dhcpMessage *oldpacket);
+
+
+/*** files.h ***/
+
+struct config_keyword {
+ const char *keyword;
+ int (* const handler)(const char *line, void *var);
+ void *var;
+ const char *def;
+};
+
+int read_config(const char *file);
+void write_leases(void);
+void read_leases(const char *file);
+struct option_set *find_option(struct option_set *opt_list, char code);
+
+
+#endif
diff --git a/i/pc104/initrd/conf/busybox/networking/udhcp/dhcprelay.c b/i/pc104/initrd/conf/busybox/networking/udhcp/dhcprelay.c
new file mode 100644
index 0000000..9bb7aea
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/udhcp/dhcprelay.c
@@ -0,0 +1,338 @@
+/* vi: set sw=4 ts=4: */
+/* Port to Busybox Copyright (C) 2006 Jesse Dutton <jessedutton@gmail.com>
+ *
+ * Licensed under GPL v2, see file LICENSE in this tarball for details.
+ *
+ * DHCP Relay for 'DHCPv4 Configuration of IPSec Tunnel Mode' support
+ * Copyright (C) 2002 Mario Strasser <mast@gmx.net>,
+ * Zuercher Hochschule Winterthur,
+ * Netbeat AG
+ * Upstream has GPL v2 or later
+ */
+
+#include "common.h"
+#include "dhcpd.h"
+#include "options.h"
+
+/* constants */
+#define SELECT_TIMEOUT 5 /* select timeout in sec. */
+#define MAX_LIFETIME 2*60 /* lifetime of an xid entry in sec. */
+#define MAX_INTERFACES 9
+
+
+/* This list holds information about clients. The xid_* functions manipulate this list. */
+static struct xid_item {
+ uint32_t xid;
+ struct sockaddr_in ip;
+ int client;
+ time_t timestamp;
+ struct xid_item *next;
+} dhcprelay_xid_list = {0, {0}, 0, 0, NULL};
+
+
+static struct xid_item * xid_add(uint32_t xid, struct sockaddr_in *ip, int client)
+{
+ struct xid_item *item;
+
+ /* create new xid entry */
+ item = xmalloc(sizeof(struct xid_item));
+
+ /* add xid entry */
+ item->ip = *ip;
+ item->xid = xid;
+ item->client = client;
+ item->timestamp = time(NULL);
+ item->next = dhcprelay_xid_list.next;
+ dhcprelay_xid_list.next = item;
+
+ return item;
+}
+
+
+static void xid_expire(void)
+{
+ struct xid_item *item = dhcprelay_xid_list.next;
+ struct xid_item *last = &dhcprelay_xid_list;
+ time_t current_time = time(NULL);
+
+ while (item != NULL) {
+ if ((current_time-item->timestamp) > MAX_LIFETIME) {
+ last->next = item->next;
+ free(item);
+ item = last->next;
+ } else {
+ last = item;
+ item = item->next;
+ }
+ }
+}
+
+static struct xid_item * xid_find(uint32_t xid)
+{
+ struct xid_item *item = dhcprelay_xid_list.next;
+ while (item != NULL) {
+ if (item->xid == xid) {
+ return item;
+ }
+ item = item->next;
+ }
+ return NULL;
+}
+
+static void xid_del(uint32_t xid)
+{
+ struct xid_item *item = dhcprelay_xid_list.next;
+ struct xid_item *last = &dhcprelay_xid_list;
+ while (item != NULL) {
+ if (item->xid == xid) {
+ last->next = item->next;
+ free(item);
+ item = last->next;
+ } else {
+ last = item;
+ item = item->next;
+ }
+ }
+}
+
+
+/**
+ * get_dhcp_packet_type - gets the message type of a dhcp packet
+ * p - pointer to the dhcp packet
+ * returns the message type on success, -1 otherwise
+ */
+static int get_dhcp_packet_type(struct dhcpMessage *p)
+{
+ uint8_t *op;
+
+ /* it must be either a BOOTREQUEST or a BOOTREPLY */
+ if (p->op != BOOTREQUEST && p->op != BOOTREPLY)
+ return -1;
+ /* get message type option */
+ op = get_option(p, DHCP_MESSAGE_TYPE);
+ if (op != NULL)
+ return op[0];
+ return -1;
+}
+
+/**
+ * signal_handler - handles signals ;-)
+ * sig - sent signal
+ */
+static int dhcprelay_stopflag;
+static void dhcprelay_signal_handler(int sig)
+{
+ dhcprelay_stopflag = 1;
+}
+
+/**
+ * get_client_devices - parses the devices list
+ * dev_list - comma separated list of devices
+ * returns array
+ */
+static char ** get_client_devices(char *dev_list, int *client_number)
+{
+ char *s, *list, **client_dev;
+ int i, cn;
+
+ /* copy list */
+ list = xstrdup(dev_list);
+ if (list == NULL) return NULL;
+
+ /* get number of items */
+ for (s = dev_list, cn = 1; *s; s++)
+ if (*s == ',')
+ cn++;
+
+ client_dev = xzalloc(cn * sizeof(*client_dev));
+
+ /* parse list */
+ s = strtok(list, ",");
+ i = 0;
+ while (s != NULL) {
+ client_dev[i++] = xstrdup(s);
+ s = strtok(NULL, ",");
+ }
+
+ /* free copy and exit */
+ free(list);
+ *client_number = cn;
+ return client_dev;
+}
+
+
+/* Creates listen sockets (in fds) and returns the number allocated. */
+static int init_sockets(char **client, int num_clients,
+ char *server, int *fds, int *max_socket)
+{
+ int i;
+
+ /* talk to real server on bootps */
+ fds[0] = listen_socket(htonl(INADDR_ANY), 67, server);
+ *max_socket = fds[0];
+
+ /* array starts at 1 since server is 0 */
+ num_clients++;
+
+ for (i=1; i < num_clients; i++) {
+ /* listen for clients on bootps */
+ fds[i] = listen_socket(htonl(INADDR_ANY), 67, client[i-1]);
+ if (fds[i] > *max_socket) *max_socket = fds[i];
+ }
+
+ return i;
+}
+
+
+/**
+ * pass_on() - forwards dhcp packets from client to server
+ * p - packet to send
+ * client - number of the client
+ */
+static void pass_on(struct dhcpMessage *p, int packet_len, int client, int *fds,
+ struct sockaddr_in *client_addr, struct sockaddr_in *server_addr)
+{
+ int res, type;
+ struct xid_item *item;
+
+ /* check packet_type */
+ type = get_dhcp_packet_type(p);
+ if (type != DHCPDISCOVER && type != DHCPREQUEST
+ && type != DHCPDECLINE && type != DHCPRELEASE
+ && type != DHCPINFORM
+ ) {
+ return;
+ }
+
+ /* create new xid entry */
+ item = xid_add(p->xid, client_addr, client);
+
+ /* forward request to LAN (server) */
+ res = sendto(fds[0], p, packet_len, 0, (struct sockaddr*)server_addr,
+ sizeof(struct sockaddr_in));
+ if (res != packet_len) {
+ bb_perror_msg("pass_on");
+ return;
+ }
+}
+
+/**
+ * pass_back() - forwards dhcp packets from server to client
+ * p - packet to send
+ */
+static void pass_back(struct dhcpMessage *p, int packet_len, int *fds)
+{
+ int res, type;
+ struct xid_item *item;
+
+ /* check xid */
+ item = xid_find(p->xid);
+ if (!item) {
+ return;
+ }
+
+ /* check packet type */
+ type = get_dhcp_packet_type(p);
+ if (type != DHCPOFFER && type != DHCPACK && type != DHCPNAK) {
+ return;
+ }
+
+ if (item->ip.sin_addr.s_addr == htonl(INADDR_ANY))
+ item->ip.sin_addr.s_addr = htonl(INADDR_BROADCAST);
+ if (item->client > MAX_INTERFACES)
+ return;
+ res = sendto(fds[item->client], p, packet_len, 0, (struct sockaddr*)(&item->ip),
+ sizeof(item->ip));
+ if (res != packet_len) {
+ bb_perror_msg("pass_back");
+ return;
+ }
+
+ /* remove xid entry */
+ xid_del(p->xid);
+}
+
+static void dhcprelay_loop(int *fds, int num_sockets, int max_socket, char **clients,
+ struct sockaddr_in *server_addr, uint32_t gw_ip)
+{
+ struct dhcpMessage dhcp_msg;
+ fd_set rfds;
+ size_t packlen;
+ socklen_t addr_size;
+ struct sockaddr_in client_addr;
+ struct timeval tv;
+ int i;
+
+ while (!dhcprelay_stopflag) {
+ FD_ZERO(&rfds);
+ for (i = 0; i < num_sockets; i++)
+ FD_SET(fds[i], &rfds);
+ tv.tv_sec = SELECT_TIMEOUT;
+ tv.tv_usec = 0;
+ if (select(max_socket + 1, &rfds, NULL, NULL, &tv) > 0) {
+ /* server */
+ if (FD_ISSET(fds[0], &rfds)) {
+ packlen = udhcp_get_packet(&dhcp_msg, fds[0]);
+ if (packlen > 0) {
+ pass_back(&dhcp_msg, packlen, fds);
+ }
+ }
+ for (i = 1; i < num_sockets; i++) {
+ /* clients */
+ if (!FD_ISSET(fds[i], &rfds))
+ continue;
+ addr_size = sizeof(struct sockaddr_in);
+ packlen = recvfrom(fds[i], &dhcp_msg, sizeof(dhcp_msg), 0,
+ (struct sockaddr *)(&client_addr), &addr_size);
+ if (packlen <= 0)
+ continue;
+ if (read_interface(clients[i-1], NULL, &dhcp_msg.giaddr, NULL) < 0)
+ dhcp_msg.giaddr = gw_ip;
+ pass_on(&dhcp_msg, packlen, i, fds, &client_addr, server_addr);
+ }
+ }
+ xid_expire();
+ }
+}
+
+int dhcprelay_main(int argc, char **argv);
+int dhcprelay_main(int argc, char **argv)
+{
+ int i, num_sockets, max_socket, fds[MAX_INTERFACES];
+ uint32_t gw_ip;
+ char **clients;
+ struct sockaddr_in server_addr;
+
+ server_addr.sin_family = AF_INET;
+ server_addr.sin_port = htons(67);
+ if (argc == 4) {
+ if (!inet_aton(argv[3], &server_addr.sin_addr))
+ bb_perror_msg_and_die("didn't grok server");
+ } else if (argc == 3) {
+ server_addr.sin_addr.s_addr = htonl(INADDR_BROADCAST);
+ } else {
+ bb_show_usage();
+ }
+ clients = get_client_devices(argv[1], &num_sockets);
+ if (!clients) return 0;
+
+ signal(SIGTERM, dhcprelay_signal_handler);
+ signal(SIGQUIT, dhcprelay_signal_handler);
+ signal(SIGINT, dhcprelay_signal_handler);
+
+ num_sockets = init_sockets(clients, num_sockets, argv[2], fds, &max_socket);
+
+ if (read_interface(argv[2], NULL, &gw_ip, NULL) == -1)
+ return 1;
+
+ dhcprelay_loop(fds, num_sockets, max_socket, clients, &server_addr, gw_ip);
+
+ if (ENABLE_FEATURE_CLEAN_UP) {
+ for (i = 0; i < num_sockets; i++) {
+ close(fds[i]);
+ free(clients[i]);
+ }
+ }
+
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/udhcp/domain_codec.c b/i/pc104/initrd/conf/busybox/networking/udhcp/domain_codec.c
new file mode 100644
index 0000000..239ae5b
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/udhcp/domain_codec.c
@@ -0,0 +1,205 @@
+/* vi: set sw=4 ts=4: */
+
+/* RFC1035 domain compression routines (C) 2007 Gabriel Somlo <somlo at cmu.edu>
+ *
+ * Loosely based on the isc-dhcpd implementation by dhankins@isc.org
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#if ENABLE_FEATURE_RFC3397
+
+#include "common.h"
+#include "options.h"
+
+#define NS_MAXDNAME 1025 /* max domain name length */
+#define NS_MAXCDNAME 255 /* max compressed domain name length */
+#define NS_MAXLABEL 63 /* max label length */
+#define NS_MAXDNSRCH 6 /* max domains in search path */
+#define NS_CMPRSFLGS 0xc0 /* name compression pointer flag */
+
+
+/* expand a RFC1035-compressed list of domain names "cstr", of length "clen";
+ * returns a newly allocated string containing the space-separated domains,
+ * prefixed with the contents of string pre, or NULL if an error occurs.
+ */
+char *dname_dec(const uint8_t *cstr, int clen, const char *pre)
+{
+ const uint8_t *c;
+ int crtpos, retpos, depth, plen = 0, len = 0;
+ char *dst = NULL;
+
+ if (!cstr)
+ return NULL;
+
+ if (pre)
+ plen = strlen(pre);
+
+ /* We make two passes over the cstr string. First, we compute
+ * how long the resulting string would be. Then we allocate a
+ * new buffer of the required length, and fill it in with the
+ * expanded content. The advantage of this approach is not
+ * having to deal with requiring callers to supply their own
+ * buffer, then having to check if it's sufficiently large, etc.
+ */
+
+ while (!dst) {
+
+ if (len > 0) { /* second pass? allocate dst buffer and copy pre */
+ dst = xmalloc(len + plen);
+ memcpy(dst, pre, plen);
+ }
+
+ crtpos = retpos = depth = len = 0;
+
+ while (crtpos < clen) {
+ c = cstr + crtpos;
+
+ if ((*c & NS_CMPRSFLGS) != 0) { /* pointer */
+ if (crtpos + 2 > clen) /* no offset to jump to? abort */
+ return NULL;
+ if (retpos == 0) /* toplevel? save return spot */
+ retpos = crtpos + 2;
+ depth++;
+ crtpos = ((*c & 0x3f) << 8) | (*(c + 1) & 0xff); /* jump */
+ } else if (*c) { /* label */
+ if (crtpos + *c + 1 > clen) /* label too long? abort */
+ return NULL;
+ if (dst)
+ memcpy(dst + plen + len, c + 1, *c);
+ len += *c + 1;
+ crtpos += *c + 1;
+ if (dst)
+ *(dst + plen + len - 1) = '.';
+ } else { /* null: end of current domain name */
+ if (retpos == 0) { /* toplevel? keep going */
+ crtpos++;
+ } else { /* return to toplevel saved spot */
+ crtpos = retpos;
+ retpos = depth = 0;
+ }
+ if (dst)
+ *(dst + plen + len - 1) = ' ';
+ }
+
+ if (depth > NS_MAXDNSRCH || /* too many jumps? abort, it's a loop */
+ len > NS_MAXDNAME * NS_MAXDNSRCH) /* result too long? abort */
+ return NULL;
+ }
+
+ if (!len) /* expanded string has 0 length? abort */
+ return NULL;
+
+ if (dst)
+ *(dst + plen + len - 1) = '\0';
+ }
+
+ return dst;
+}
+
+/* Convert a domain name (src) from human-readable "foo.blah.com" format into
+ * RFC1035 encoding "\003foo\004blah\003com\000". Return allocated string, or
+ * NULL if an error occurs.
+ */
+static uint8_t *convert_dname(const char *src)
+{
+ uint8_t c, *res, *lp, *rp;
+ int len;
+
+ res = xmalloc(strlen(src) + 2);
+ rp = lp = res;
+ rp++;
+
+ for (;;) {
+ c = (uint8_t)*src++;
+ if (c == '.' || c == '\0') { /* end of label */
+ len = rp - lp - 1;
+ /* label too long, too short, or two '.'s in a row? abort */
+ if (len > NS_MAXLABEL || len == 0 || (c == '.' && *src == '.')) {
+ free(res);
+ return NULL;
+ }
+ *lp = len;
+ lp = rp++;
+ if (c == '\0' || *src == '\0') /* end of dname */
+ break;
+ } else {
+ if (c >= 0x41 && c <= 0x5A) /* uppercase? convert to lower */
+ c += 0x20;
+ *rp++ = c;
+ }
+ }
+
+ *lp = 0;
+ if (rp - res > NS_MAXCDNAME) { /* dname too long? abort */
+ free(res);
+ return NULL;
+ }
+ return res;
+}
+
+/* returns the offset within cstr at which dname can be found, or -1
+ */
+static int find_offset(const uint8_t *cstr, int clen, const uint8_t *dname)
+{
+ const uint8_t *c, *d;
+ int off, inc;
+
+ /* find all labels in cstr */
+ off = 0;
+ while (off < clen) {
+ c = cstr + off;
+
+ if ((*c & NS_CMPRSFLGS) != 0) { /* pointer, skip */
+ off += 2;
+ } else if (*c) { /* label, try matching dname */
+ inc = *c + 1;
+ d = dname;
+ while (*c == *d && memcmp(c + 1, d + 1, *c) == 0) {
+ if (*c == 0) /* match, return offset */
+ return off;
+ d += *c + 1;
+ c += *c + 1;
+ if ((*c & NS_CMPRSFLGS) != 0) /* pointer, jump */
+ c = cstr + (((*c & 0x3f) << 8) | (*(c + 1) & 0xff));
+ }
+ off += inc;
+ } else { /* null, skip */
+ off++;
+ }
+ }
+
+ return -1;
+}
+
+/* computes string to be appended to cstr so that src would be added to
+ * the compression (best case, it's a 2-byte pointer to some offset within
+ * cstr; worst case, it's all of src, converted to rfc3011 format).
+ * The computed string is returned directly; its length is returned via retlen;
+ * NULL and 0, respectively, are returned if an error occurs.
+ */
+uint8_t *dname_enc(const uint8_t *cstr, int clen, const char *src, int *retlen)
+{
+ uint8_t *d, *dname;
+ int off;
+
+ dname = convert_dname(src);
+ if (dname == NULL) {
+ *retlen = 0;
+ return NULL;
+ }
+
+ for (d = dname; *d != 0; d += *d + 1) {
+ off = find_offset(cstr, clen, d);
+ if (off >= 0) { /* found a match, add pointer and terminate string */
+ *d++ = NS_CMPRSFLGS;
+ *d = off;
+ break;
+ }
+ }
+
+ *retlen = d - dname + 1;
+ return dname;
+}
+
+#endif /* ENABLE_FEATURE_RFC3397 */
diff --git a/i/pc104/initrd/conf/busybox/networking/udhcp/dumpleases.c b/i/pc104/initrd/conf/busybox/networking/udhcp/dumpleases.c
new file mode 100644
index 0000000..ce73c47
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/udhcp/dumpleases.c
@@ -0,0 +1,75 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ */
+#include <getopt.h>
+
+#include "common.h"
+#include "dhcpd.h"
+
+
+#define REMAINING 0
+#define ABSOLUTE 1
+
+int dumpleases_main(int argc, char *argv[]);
+int dumpleases_main(int argc, char *argv[])
+{
+ int fp;
+ int i, c, mode = REMAINING;
+ unsigned long expires;
+ const char *file = LEASES_FILE;
+ struct dhcpOfferedAddr lease;
+ struct in_addr addr;
+
+ static const struct option options[] = {
+ {"absolute", 0, 0, 'a'},
+ {"remaining", 0, 0, 'r'},
+ {"file", 1, 0, 'f'},
+ {0, 0, 0, 0}
+ };
+
+ while (1) {
+ int option_index = 0;
+ c = getopt_long(argc, argv, "arf:", options, &option_index);
+ if (c == -1) break;
+
+ switch (c) {
+ case 'a': mode = ABSOLUTE; break;
+ case 'r': mode = REMAINING; break;
+ case 'f':
+ file = optarg;
+ break;
+ default:
+ bb_show_usage();
+ }
+ }
+
+ fp = xopen(file, O_RDONLY);
+
+ printf("Mac Address IP-Address Expires %s\n", mode == REMAINING ? "in" : "at");
+ /* "00:00:00:00:00:00 255.255.255.255 Wed Jun 30 21:49:08 1993" */
+ while (full_read(fp, &lease, sizeof(lease)) == sizeof(lease)) {
+ printf(":%02x"+1, lease.chaddr[0]);
+ for (i = 1; i < 6; i++) {
+ printf(":%02x", lease.chaddr[i]);
+ }
+ addr.s_addr = lease.yiaddr;
+ printf(" %-15s ", inet_ntoa(addr));
+ expires = ntohl(lease.expires);
+ if (mode == REMAINING) {
+ if (!expires)
+ printf("expired\n");
+ else {
+ unsigned d, h, m;
+ d = expires / (24*60*60); expires %= (24*60*60);
+ h = expires / (60*60); expires %= (60*60);
+ m = expires / 60; expires %= 60;
+ if (d) printf("%u days ", d);
+ printf("%02u:%02u:%02u\n", h, m, (unsigned)expires);
+ }
+ } else fputs(ctime(&expires), stdout);
+ }
+ /* close(fp); */
+
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/udhcp/files.c b/i/pc104/initrd/conf/busybox/networking/udhcp/files.c
new file mode 100644
index 0000000..ab6f4a3
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/udhcp/files.c
@@ -0,0 +1,426 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * files.c -- DHCP server file manipulation *
+ * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001
+ */
+
+#include <netinet/ether.h>
+
+#include "common.h"
+#include "dhcpd.h"
+#include "options.h"
+
+
+/*
+ * Domain names may have 254 chars, and string options can be 254
+ * chars long. However, 80 bytes will be enough for most, and won't
+ * hog up memory. If you have a special application, change it
+ */
+#define READ_CONFIG_BUF_SIZE 80
+
+/* on these functions, make sure you datatype matches */
+static int read_ip(const char *line, void *arg)
+{
+ len_and_sockaddr *lsa;
+ int retval = 0;
+
+ lsa = host_and_af2sockaddr(line, 0, AF_INET);
+ if (lsa) {
+ *(struct in_addr*)arg = lsa->sin.sin_addr;
+ free(lsa);
+ retval = 1;
+ }
+ return retval;
+}
+
+static int read_mac(const char *line, void *arg)
+{
+ uint8_t *mac_bytes = arg;
+ struct ether_addr *temp_ether_addr;
+ int retval = 1;
+
+ temp_ether_addr = ether_aton(line);
+
+ if (temp_ether_addr == NULL)
+ retval = 0;
+ else
+ memcpy(mac_bytes, temp_ether_addr, 6);
+
+ return retval;
+}
+
+
+static int read_str(const char *line, void *arg)
+{
+ char **dest = arg;
+
+ free(*dest);
+ *dest = xstrdup(line);
+
+ return 1;
+}
+
+
+static int read_u32(const char *line, void *arg)
+{
+ *((uint32_t*)arg) = bb_strtou32(line, NULL, 10);
+ return errno == 0;
+}
+
+
+static int read_yn(const char *line, void *arg)
+{
+ char *dest = arg;
+ int retval = 1;
+
+ if (!strcasecmp("yes", line))
+ *dest = 1;
+ else if (!strcasecmp("no", line))
+ *dest = 0;
+ else retval = 0;
+
+ return retval;
+}
+
+
+/* find option 'code' in opt_list */
+struct option_set *find_option(struct option_set *opt_list, char code)
+{
+ while (opt_list && opt_list->data[OPT_CODE] < code)
+ opt_list = opt_list->next;
+
+ if (opt_list && opt_list->data[OPT_CODE] == code) return opt_list;
+ else return NULL;
+}
+
+
+/* add an option to the opt_list */
+static void attach_option(struct option_set **opt_list,
+ const struct dhcp_option *option, char *buffer, int length)
+{
+ struct option_set *existing, *new, **curr;
+
+ existing = find_option(*opt_list, option->code);
+ if (!existing) {
+ DEBUG("Attaching option %s to list", option->name);
+
+#if ENABLE_FEATURE_RFC3397
+ if ((option->flags & TYPE_MASK) == OPTION_STR1035)
+ /* reuse buffer and length for RFC1035-formatted string */
+ buffer = dname_enc(NULL, 0, buffer, &length);
+#endif
+
+ /* make a new option */
+ new = xmalloc(sizeof(struct option_set));
+ new->data = xmalloc(length + 2);
+ new->data[OPT_CODE] = option->code;
+ new->data[OPT_LEN] = length;
+ memcpy(new->data + 2, buffer, length);
+
+ curr = opt_list;
+ while (*curr && (*curr)->data[OPT_CODE] < option->code)
+ curr = &(*curr)->next;
+
+ new->next = *curr;
+ *curr = new;
+#if ENABLE_FEATURE_RFC3397
+ if ((option->flags & TYPE_MASK) == OPTION_STR1035 && buffer != NULL)
+ free(buffer);
+#endif
+ return;
+ }
+
+ /* add it to an existing option */
+ DEBUG("Attaching option %s to existing member of list", option->name);
+ if (option->flags & OPTION_LIST) {
+#if ENABLE_FEATURE_RFC3397
+ if ((option->flags & TYPE_MASK) == OPTION_STR1035)
+ /* reuse buffer and length for RFC1035-formatted string */
+ buffer = dname_enc(existing->data + 2,
+ existing->data[OPT_LEN], buffer, &length);
+#endif
+ if (existing->data[OPT_LEN] + length <= 255) {
+ existing->data = xrealloc(existing->data,
+ existing->data[OPT_LEN] + length + 3);
+ if ((option->flags & TYPE_MASK) == OPTION_STRING) {
+ /* ' ' can bring us to 256 - bad */
+ if (existing->data[OPT_LEN] + length >= 255)
+ return;
+ /* add space separator between STRING options in a list */
+ existing->data[existing->data[OPT_LEN] + 2] = ' ';
+ existing->data[OPT_LEN]++;
+ }
+ memcpy(existing->data + existing->data[OPT_LEN] + 2, buffer, length);
+ existing->data[OPT_LEN] += length;
+ } /* else, ignore the data, we could put this in a second option in the future */
+#if ENABLE_FEATURE_RFC3397
+ if ((option->flags & TYPE_MASK) == OPTION_STR1035 && buffer != NULL)
+ free(buffer);
+#endif
+ } /* else, ignore the new data */
+}
+
+
+/* read a dhcp option and add it to opt_list */
+static int read_opt(const char *const_line, void *arg)
+{
+ struct option_set **opt_list = arg;
+ char *opt, *val, *endptr;
+ const struct dhcp_option *option;
+ int retval = 0, length;
+ char buffer[8];
+ char *line;
+ uint16_t *result_u16 = (uint16_t *) buffer;
+ uint32_t *result_u32 = (uint32_t *) buffer;
+
+ /* Cheat, the only const line we'll actually get is "" */
+ line = (char *) const_line;
+ opt = strtok(line, " \t=");
+ if (!opt) return 0;
+
+ option = dhcp_options;
+ while (1) {
+ if (!option->code)
+ return 0;
+ if (!strcasecmp(option->name, opt))
+ break;
+ option++;
+ }
+
+ do {
+ val = strtok(NULL, ", \t");
+ if (!val) break;
+ length = option_lengths[option->flags & TYPE_MASK];
+ retval = 0;
+ opt = buffer; /* new meaning for variable opt */
+ switch (option->flags & TYPE_MASK) {
+ case OPTION_IP:
+ retval = read_ip(val, buffer);
+ break;
+ case OPTION_IP_PAIR:
+ retval = read_ip(val, buffer);
+ if (!(val = strtok(NULL, ", \t/-"))) retval = 0;
+ if (retval) retval = read_ip(val, buffer + 4);
+ break;
+ case OPTION_STRING:
+#if ENABLE_FEATURE_RFC3397
+ case OPTION_STR1035:
+#endif
+ length = strlen(val);
+ if (length > 0) {
+ if (length > 254) length = 254;
+ opt = val;
+ retval = 1;
+ }
+ break;
+ case OPTION_BOOLEAN:
+ retval = read_yn(val, buffer);
+ break;
+ case OPTION_U8:
+ buffer[0] = strtoul(val, &endptr, 0);
+ retval = (endptr[0] == '\0');
+ break;
+ case OPTION_U16:
+ *result_u16 = htons(strtoul(val, &endptr, 0));
+ retval = (endptr[0] == '\0');
+ break;
+ case OPTION_S16:
+ *result_u16 = htons(strtol(val, &endptr, 0));
+ retval = (endptr[0] == '\0');
+ break;
+ case OPTION_U32:
+ *result_u32 = htonl(strtoul(val, &endptr, 0));
+ retval = (endptr[0] == '\0');
+ break;
+ case OPTION_S32:
+ *result_u32 = htonl(strtol(val, &endptr, 0));
+ retval = (endptr[0] == '\0');
+ break;
+ default:
+ break;
+ }
+ if (retval)
+ attach_option(opt_list, option, opt, length);
+ } while (retval && option->flags & OPTION_LIST);
+ return retval;
+}
+
+static int read_staticlease(const char *const_line, void *arg)
+{
+ char *line;
+ char *mac_string;
+ char *ip_string;
+ uint8_t *mac_bytes;
+ uint32_t *ip;
+
+
+ /* Allocate memory for addresses */
+ mac_bytes = xmalloc(sizeof(unsigned char) * 8);
+ ip = xmalloc(sizeof(uint32_t));
+
+ /* Read mac */
+ line = (char *) const_line;
+ mac_string = strtok(line, " \t");
+ read_mac(mac_string, mac_bytes);
+
+ /* Read ip */
+ ip_string = strtok(NULL, " \t");
+ read_ip(ip_string, ip);
+
+ addStaticLease(arg, mac_bytes, ip);
+
+ if (ENABLE_FEATURE_UDHCP_DEBUG) printStaticLeases(arg);
+
+ return 1;
+}
+
+
+static const struct config_keyword keywords[] = {
+ /* keyword handler variable address default */
+ {"start", read_ip, &(server_config.start), "192.168.0.20"},
+ {"end", read_ip, &(server_config.end), "192.168.0.254"},
+ {"interface", read_str, &(server_config.interface), "eth0"},
+ {"option", read_opt, &(server_config.options), ""},
+ {"opt", read_opt, &(server_config.options), ""},
+ {"max_leases", read_u32, &(server_config.max_leases), "254"},
+ {"remaining", read_yn, &(server_config.remaining), "yes"},
+ {"auto_time", read_u32, &(server_config.auto_time), "7200"},
+ {"decline_time",read_u32, &(server_config.decline_time),"3600"},
+ {"conflict_time",read_u32,&(server_config.conflict_time),"3600"},
+ {"offer_time", read_u32, &(server_config.offer_time), "60"},
+ {"min_lease", read_u32, &(server_config.min_lease), "60"},
+ {"lease_file", read_str, &(server_config.lease_file), LEASES_FILE},
+ {"pidfile", read_str, &(server_config.pidfile), "/var/run/udhcpd.pid"},
+ {"notify_file", read_str, &(server_config.notify_file), ""},
+ {"siaddr", read_ip, &(server_config.siaddr), "0.0.0.0"},
+ {"sname", read_str, &(server_config.sname), ""},
+ {"boot_file", read_str, &(server_config.boot_file), ""},
+ {"static_lease",read_staticlease, &(server_config.static_leases), ""},
+ /*ADDME: static lease */
+ {"", NULL, NULL, ""}
+};
+
+
+int read_config(const char *file)
+{
+ FILE *in;
+ char buffer[READ_CONFIG_BUF_SIZE], *token, *line;
+ int i, lm = 0;
+
+ for (i = 0; keywords[i].keyword[0]; i++)
+ if (keywords[i].def[0])
+ keywords[i].handler(keywords[i].def, keywords[i].var);
+
+ in = fopen(file, "r");
+ if (!in) {
+ bb_error_msg("cannot open config file: %s", file);
+ return 0;
+ }
+
+ while (fgets(buffer, READ_CONFIG_BUF_SIZE, in)) {
+ char debug_orig[READ_CONFIG_BUF_SIZE];
+ char *p;
+
+ lm++;
+ p = strchr(buffer, '\n');
+ if (p) *p = '\0';
+ if (ENABLE_FEATURE_UDHCP_DEBUG) strcpy(debug_orig, buffer);
+ p = strchr(buffer, '#');
+ if (p) *p = '\0';
+
+ if (!(token = strtok(buffer, " \t"))) continue;
+ if (!(line = strtok(NULL, ""))) continue;
+
+ /* eat leading whitespace */
+ line = skip_whitespace(line);
+ /* eat trailing whitespace */
+ i = strlen(line) - 1;
+ while (i >= 0 && isspace(line[i]))
+ line[i--] = '\0';
+
+ for (i = 0; keywords[i].keyword[0]; i++)
+ if (!strcasecmp(token, keywords[i].keyword))
+ if (!keywords[i].handler(line, keywords[i].var)) {
+ bb_error_msg("cannot parse line %d of %s", lm, file);
+ if (ENABLE_FEATURE_UDHCP_DEBUG)
+ bb_error_msg("cannot parse '%s'", debug_orig);
+ /* reset back to the default value */
+ keywords[i].handler(keywords[i].def, keywords[i].var);
+ }
+ }
+ fclose(in);
+ return 1;
+}
+
+
+void write_leases(void)
+{
+ int fp;
+ unsigned i;
+ time_t curr = time(0);
+ unsigned long tmp_time;
+
+ fp = open(server_config.lease_file, O_WRONLY|O_CREAT|O_TRUNC, 0666);
+ if (fp < 0) {
+ bb_error_msg("cannot open %s for writing", server_config.lease_file);
+ return;
+ }
+
+ for (i = 0; i < server_config.max_leases; i++) {
+ if (leases[i].yiaddr != 0) {
+
+ /* screw with the time in the struct, for easier writing */
+ tmp_time = leases[i].expires;
+
+ if (server_config.remaining) {
+ if (lease_expired(&(leases[i])))
+ leases[i].expires = 0;
+ else leases[i].expires -= curr;
+ } /* else stick with the time we got */
+ leases[i].expires = htonl(leases[i].expires);
+ // FIXME: error check??
+ full_write(fp, &leases[i], sizeof(leases[i]));
+
+ /* then restore it when done */
+ leases[i].expires = tmp_time;
+ }
+ }
+ close(fp);
+
+ if (server_config.notify_file) {
+ char *cmd = xasprintf("%s %s", server_config.notify_file, server_config.lease_file);
+ system(cmd);
+ free(cmd);
+ }
+}
+
+
+void read_leases(const char *file)
+{
+ int fp;
+ unsigned int i = 0;
+ struct dhcpOfferedAddr lease;
+
+ fp = open(file, O_RDONLY);
+ if (fp < 0) {
+ bb_error_msg("cannot open %s for reading", file);
+ return;
+ }
+
+ while (i < server_config.max_leases
+ && full_read(fp, &lease, sizeof(lease)) == sizeof(lease)
+ ) {
+ /* ADDME: is it a static lease */
+ if (lease.yiaddr >= server_config.start && lease.yiaddr <= server_config.end) {
+ lease.expires = ntohl(lease.expires);
+ if (!server_config.remaining) lease.expires -= time(0);
+ if (!(add_lease(lease.chaddr, lease.yiaddr, lease.expires))) {
+ bb_error_msg("too many leases while loading %s", file);
+ break;
+ }
+ i++;
+ }
+ }
+ DEBUG("Read %d leases", i);
+ close(fp);
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/udhcp/leases.c b/i/pc104/initrd/conf/busybox/networking/udhcp/leases.c
new file mode 100644
index 0000000..2f7847d
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/udhcp/leases.c
@@ -0,0 +1,145 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * leases.c -- tools to manage DHCP leases
+ * Russ Dill <Russ.Dill@asu.edu> July 2001
+ */
+
+#include "common.h"
+#include "dhcpd.h"
+
+
+uint8_t blank_chaddr[] = {[0 ... 15] = 0};
+
+/* clear every lease out that chaddr OR yiaddr matches and is nonzero */
+void clear_lease(uint8_t *chaddr, uint32_t yiaddr)
+{
+ unsigned int i, j;
+
+ for (j = 0; j < 16 && !chaddr[j]; j++);
+
+ for (i = 0; i < server_config.max_leases; i++)
+ if ((j != 16 && !memcmp(leases[i].chaddr, chaddr, 16)) ||
+ (yiaddr && leases[i].yiaddr == yiaddr)) {
+ memset(&(leases[i]), 0, sizeof(struct dhcpOfferedAddr));
+ }
+}
+
+
+/* add a lease into the table, clearing out any old ones */
+struct dhcpOfferedAddr *add_lease(uint8_t *chaddr, uint32_t yiaddr, unsigned long lease)
+{
+ struct dhcpOfferedAddr *oldest;
+
+ /* clean out any old ones */
+ clear_lease(chaddr, yiaddr);
+
+ oldest = oldest_expired_lease();
+
+ if (oldest) {
+ memcpy(oldest->chaddr, chaddr, 16);
+ oldest->yiaddr = yiaddr;
+ oldest->expires = time(0) + lease;
+ }
+
+ return oldest;
+}
+
+
+/* true if a lease has expired */
+int lease_expired(struct dhcpOfferedAddr *lease)
+{
+ return (lease->expires < (unsigned long) time(0));
+}
+
+
+/* Find the oldest expired lease, NULL if there are no expired leases */
+struct dhcpOfferedAddr *oldest_expired_lease(void)
+{
+ struct dhcpOfferedAddr *oldest = NULL;
+ unsigned long oldest_lease = time(0);
+ unsigned int i;
+
+
+ for (i = 0; i < server_config.max_leases; i++)
+ if (oldest_lease > leases[i].expires) {
+ oldest_lease = leases[i].expires;
+ oldest = &(leases[i]);
+ }
+ return oldest;
+
+}
+
+
+/* Find the first lease that matches chaddr, NULL if no match */
+struct dhcpOfferedAddr *find_lease_by_chaddr(uint8_t *chaddr)
+{
+ unsigned int i;
+
+ for (i = 0; i < server_config.max_leases; i++)
+ if (!memcmp(leases[i].chaddr, chaddr, 16)) return &(leases[i]);
+
+ return NULL;
+}
+
+
+/* Find the first lease that matches yiaddr, NULL is no match */
+struct dhcpOfferedAddr *find_lease_by_yiaddr(uint32_t yiaddr)
+{
+ unsigned int i;
+
+ for (i = 0; i < server_config.max_leases; i++)
+ if (leases[i].yiaddr == yiaddr) return &(leases[i]);
+
+ return NULL;
+}
+
+
+/* check is an IP is taken, if it is, add it to the lease table */
+static int check_ip(uint32_t addr)
+{
+ struct in_addr temp;
+
+ if (arpping(addr, server_config.server, server_config.arp, server_config.interface) == 0) {
+ temp.s_addr = addr;
+ bb_info_msg("%s belongs to someone, reserving it for %ld seconds",
+ inet_ntoa(temp), server_config.conflict_time);
+ add_lease(blank_chaddr, addr, server_config.conflict_time);
+ return 1;
+ } else return 0;
+}
+
+
+/* find an assignable address, it check_expired is true, we check all the expired leases as well.
+ * Maybe this should try expired leases by age... */
+uint32_t find_address(int check_expired)
+{
+ uint32_t addr, ret;
+ struct dhcpOfferedAddr *lease = NULL;
+
+ addr = ntohl(server_config.start); /* addr is in host order here */
+ for (;addr <= ntohl(server_config.end); addr++) {
+
+ /* ie, 192.168.55.0 */
+ if (!(addr & 0xFF)) continue;
+
+ /* ie, 192.168.55.255 */
+ if ((addr & 0xFF) == 0xFF) continue;
+
+ /* Only do if it isn't an assigned as a static lease */
+ if (!reservedIp(server_config.static_leases, htonl(addr))) {
+
+ /* lease is not taken */
+ ret = htonl(addr);
+ lease = find_lease_by_yiaddr(ret);
+
+ /* no lease or it expired and we are checking for expired leases */
+ if ( (!lease || (check_expired && lease_expired(lease)))
+ && /* and it isn't on the network */ !check_ip(ret)
+ ) {
+ return ret;
+ break;
+ }
+ }
+ }
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/udhcp/options.c b/i/pc104/initrd/conf/busybox/networking/udhcp/options.c
new file mode 100644
index 0000000..a58adb9
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/udhcp/options.c
@@ -0,0 +1,180 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * options.c -- DHCP server option packet tools
+ * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001
+ */
+
+#include "common.h"
+#include "dhcpd.h"
+#include "options.h"
+
+
+/* supported options are easily added here */
+const struct dhcp_option dhcp_options[] = {
+ /* name[12] flags code */
+ {"subnet", OPTION_IP | OPTION_REQ, 0x01},
+ {"timezone", OPTION_S32, 0x02},
+ {"router", OPTION_IP | OPTION_LIST | OPTION_REQ, 0x03},
+ {"timesvr", OPTION_IP | OPTION_LIST, 0x04},
+ {"namesvr", OPTION_IP | OPTION_LIST, 0x05},
+ {"dns", OPTION_IP | OPTION_LIST | OPTION_REQ, 0x06},
+ {"logsvr", OPTION_IP | OPTION_LIST, 0x07},
+ {"cookiesvr", OPTION_IP | OPTION_LIST, 0x08},
+ {"lprsvr", OPTION_IP | OPTION_LIST, 0x09},
+ {"hostname", OPTION_STRING | OPTION_REQ, 0x0c},
+ {"bootsize", OPTION_U16, 0x0d},
+ {"domain", OPTION_STRING | OPTION_LIST | OPTION_REQ, 0x0f},
+ {"swapsvr", OPTION_IP, 0x10},
+ {"rootpath", OPTION_STRING, 0x11},
+ {"ipttl", OPTION_U8, 0x17},
+ {"mtu", OPTION_U16, 0x1a},
+ {"broadcast", OPTION_IP | OPTION_REQ, 0x1c},
+ {"nisdomain", OPTION_STRING | OPTION_REQ, 0x28},
+ {"nissrv", OPTION_IP | OPTION_LIST | OPTION_REQ, 0x29},
+ {"ntpsrv", OPTION_IP | OPTION_LIST | OPTION_REQ, 0x2a},
+ {"wins", OPTION_IP | OPTION_LIST, 0x2c},
+ {"requestip", OPTION_IP, 0x32},
+ {"lease", OPTION_U32, 0x33},
+ {"dhcptype", OPTION_U8, 0x35},
+ {"serverid", OPTION_IP, 0x36},
+ {"message", OPTION_STRING, 0x38},
+ {"vendorclass", OPTION_STRING, 0x3C},
+ {"clientid", OPTION_STRING, 0x3D},
+ {"tftp", OPTION_STRING, 0x42},
+ {"bootfile", OPTION_STRING, 0x43},
+ {"userclass", OPTION_STRING, 0x4D},
+#if ENABLE_FEATURE_RFC3397
+ {"search", OPTION_STR1035 | OPTION_LIST | OPTION_REQ, 0x77},
+#endif
+ /* MSIE's "Web Proxy Autodiscovery Protocol" support */
+ {"wpad", OPTION_STRING, 0xfc},
+ {"", 0x00, 0x00}
+};
+
+/* Lengths of the different option types */
+const unsigned char option_lengths[] = {
+ [OPTION_IP] = 4,
+ [OPTION_IP_PAIR] = 8,
+ [OPTION_BOOLEAN] = 1,
+ [OPTION_STRING] = 1,
+#if ENABLE_FEATURE_RFC3397
+ [OPTION_STR1035] = 1,
+#endif
+ [OPTION_U8] = 1,
+ [OPTION_U16] = 2,
+ [OPTION_S16] = 2,
+ [OPTION_U32] = 4,
+ [OPTION_S32] = 4
+};
+
+
+/* get an option with bounds checking (warning, not aligned). */
+uint8_t *get_option(struct dhcpMessage *packet, int code)
+{
+ int i, length;
+ uint8_t *optionptr;
+ int over = 0, done = 0, curr = OPTION_FIELD;
+
+ optionptr = packet->options;
+ i = 0;
+ length = 308;
+ while (!done) {
+ if (i >= length) {
+ bb_error_msg("bogus packet, option fields too long");
+ return NULL;
+ }
+ if (optionptr[i + OPT_CODE] == code) {
+ if (i + 1 + optionptr[i + OPT_LEN] >= length) {
+ bb_error_msg("bogus packet, option fields too long");
+ return NULL;
+ }
+ return optionptr + i + 2;
+ }
+ switch (optionptr[i + OPT_CODE]) {
+ case DHCP_PADDING:
+ i++;
+ break;
+ case DHCP_OPTION_OVER:
+ if (i + 1 + optionptr[i + OPT_LEN] >= length) {
+ bb_error_msg("bogus packet, option fields too long");
+ return NULL;
+ }
+ over = optionptr[i + 3];
+ i += optionptr[OPT_LEN] + 2;
+ break;
+ case DHCP_END:
+ if (curr == OPTION_FIELD && over & FILE_FIELD) {
+ optionptr = packet->file;
+ i = 0;
+ length = 128;
+ curr = FILE_FIELD;
+ } else if (curr == FILE_FIELD && over & SNAME_FIELD) {
+ optionptr = packet->sname;
+ i = 0;
+ length = 64;
+ curr = SNAME_FIELD;
+ } else done = 1;
+ break;
+ default:
+ i += optionptr[OPT_LEN + i] + 2;
+ }
+ }
+ return NULL;
+}
+
+
+/* return the position of the 'end' option (no bounds checking) */
+int end_option(uint8_t *optionptr)
+{
+ int i = 0;
+
+ while (optionptr[i] != DHCP_END) {
+ if (optionptr[i] == DHCP_PADDING) i++;
+ else i += optionptr[i + OPT_LEN] + 2;
+ }
+ return i;
+}
+
+
+/* add an option string to the options (an option string contains an option code,
+ * length, then data) */
+int add_option_string(uint8_t *optionptr, uint8_t *string)
+{
+ int end = end_option(optionptr);
+
+ /* end position + string length + option code/length + end option */
+ if (end + string[OPT_LEN] + 2 + 1 >= 308) {
+ bb_error_msg("option 0x%02x did not fit into the packet",
+ string[OPT_CODE]);
+ return 0;
+ }
+ DEBUG("adding option 0x%02x", string[OPT_CODE]);
+ memcpy(optionptr + end, string, string[OPT_LEN] + 2);
+ optionptr[end + string[OPT_LEN] + 2] = DHCP_END;
+ return string[OPT_LEN] + 2;
+}
+
+
+/* add a one to four byte option to a packet */
+int add_simple_option(uint8_t *optionptr, uint8_t code, uint32_t data)
+{
+ const struct dhcp_option *dh;
+
+ for (dh = dhcp_options; dh->code; dh++) {
+ if (dh->code == code) {
+ uint8_t option[6], len;
+
+ option[OPT_CODE] = code;
+ len = option_lengths[dh->flags & TYPE_MASK];
+ option[OPT_LEN] = len;
+ if (BB_BIG_ENDIAN) data <<= 8 * (4 - len);
+ /* This memcpy is for broken processors which can't
+ * handle a simple unaligned 32-bit assignment */
+ memcpy(&option[OPT_DATA], &data, 4);
+ return add_option_string(optionptr, option);
+ }
+ }
+
+ bb_error_msg("cannot add option 0x%02x", code);
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/udhcp/options.h b/i/pc104/initrd/conf/busybox/networking/udhcp/options.h
new file mode 100644
index 0000000..11f013f
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/udhcp/options.h
@@ -0,0 +1,44 @@
+/* vi: set sw=4 ts=4: */
+/* options.h */
+#ifndef _OPTIONS_H
+#define _OPTIONS_H
+
+#define TYPE_MASK 0x0F
+
+enum {
+ OPTION_IP=1,
+ OPTION_IP_PAIR,
+ OPTION_STRING,
+#if ENABLE_FEATURE_RFC3397
+ OPTION_STR1035, /* RFC1035 compressed domain name list */
+#endif
+ OPTION_BOOLEAN,
+ OPTION_U8,
+ OPTION_U16,
+ OPTION_S16,
+ OPTION_U32,
+ OPTION_S32
+};
+
+#define OPTION_REQ 0x10 /* have the client request this option */
+#define OPTION_LIST 0x20 /* There can be a list of 1 or more of these */
+
+struct dhcp_option {
+ char name[12];
+ char flags;
+ uint8_t code;
+};
+
+extern const struct dhcp_option dhcp_options[];
+extern const unsigned char option_lengths[];
+
+uint8_t *get_option(struct dhcpMessage *packet, int code);
+int end_option(uint8_t *optionptr);
+int add_option_string(uint8_t *optionptr, uint8_t *string);
+int add_simple_option(uint8_t *optionptr, uint8_t code, uint32_t data);
+#if ENABLE_FEATURE_RFC3397
+char *dname_dec(const uint8_t *cstr, int clen, const char *pre);
+uint8_t *dname_enc(const uint8_t *cstr, int clen, const char *src, int *retlen);
+#endif
+
+#endif
diff --git a/i/pc104/initrd/conf/busybox/networking/udhcp/packet.c b/i/pc104/initrd/conf/busybox/networking/udhcp/packet.c
new file mode 100644
index 0000000..25c55fa
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/udhcp/packet.c
@@ -0,0 +1,211 @@
+/* vi: set sw=4 ts=4: */
+
+#include <netinet/in.h>
+#if (defined(__GLIBC__) && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1) || defined _NEWLIB_VERSION
+#include <netpacket/packet.h>
+#include <net/ethernet.h>
+#else
+#include <asm/types.h>
+#include <linux/if_packet.h>
+#include <linux/if_ether.h>
+#endif
+
+#include "common.h"
+#include "dhcpd.h"
+#include "options.h"
+
+
+void udhcp_init_header(struct dhcpMessage *packet, char type)
+{
+ memset(packet, 0, sizeof(struct dhcpMessage));
+ switch (type) {
+ case DHCPDISCOVER:
+ case DHCPREQUEST:
+ case DHCPRELEASE:
+ case DHCPINFORM:
+ packet->op = BOOTREQUEST;
+ break;
+ case DHCPOFFER:
+ case DHCPACK:
+ case DHCPNAK:
+ packet->op = BOOTREPLY;
+ }
+ packet->htype = ETH_10MB;
+ packet->hlen = ETH_10MB_LEN;
+ packet->cookie = htonl(DHCP_MAGIC);
+ packet->options[0] = DHCP_END;
+ add_simple_option(packet->options, DHCP_MESSAGE_TYPE, type);
+}
+
+
+/* read a packet from socket fd, return -1 on read error, -2 on packet error */
+int udhcp_get_packet(struct dhcpMessage *packet, int fd)
+{
+ static const char broken_vendors[][8] = {
+ "MSFT 98",
+ ""
+ };
+ int bytes;
+ int i;
+ char unsigned *vendor;
+
+ memset(packet, 0, sizeof(struct dhcpMessage));
+ bytes = read(fd, packet, sizeof(struct dhcpMessage));
+ if (bytes < 0) {
+ DEBUG("cannot read on listening socket, ignoring");
+ return -1;
+ }
+
+ if (ntohl(packet->cookie) != DHCP_MAGIC) {
+ bb_error_msg("received bogus message, ignoring");
+ return -2;
+ }
+ DEBUG("Received a packet");
+
+ if (packet->op == BOOTREQUEST && (vendor = get_option(packet, DHCP_VENDOR))) {
+ for (i = 0; broken_vendors[i][0]; i++) {
+ if (vendor[OPT_LEN - 2] == (uint8_t)strlen(broken_vendors[i])
+ && !strncmp((char*)vendor, broken_vendors[i], vendor[OPT_LEN - 2])
+ ) {
+ DEBUG("broken client (%s), forcing broadcast",
+ broken_vendors[i]);
+ packet->flags |= htons(BROADCAST_FLAG);
+ }
+ }
+ }
+
+ return bytes;
+}
+
+
+uint16_t udhcp_checksum(void *addr, int count)
+{
+ /* Compute Internet Checksum for "count" bytes
+ * beginning at location "addr".
+ */
+ int32_t sum = 0;
+ uint16_t *source = (uint16_t *) addr;
+
+ while (count > 1) {
+ /* This is the inner loop */
+ sum += *source++;
+ count -= 2;
+ }
+
+ /* Add left-over byte, if any */
+ if (count > 0) {
+ /* Make sure that the left-over byte is added correctly both
+ * with little and big endian hosts */
+ uint16_t tmp = 0;
+ *(uint8_t *) (&tmp) = * (uint8_t *) source;
+ sum += tmp;
+ }
+ /* Fold 32-bit sum to 16 bits */
+ while (sum >> 16)
+ sum = (sum & 0xffff) + (sum >> 16);
+
+ return ~sum;
+}
+
+
+/* Construct a ip/udp header for a packet, and specify the source and dest hardware address */
+void BUG_sizeof_struct_udp_dhcp_packet_must_be_576(void);
+int udhcp_raw_packet(struct dhcpMessage *payload,
+ uint32_t source_ip, int source_port,
+ uint32_t dest_ip, int dest_port, uint8_t *dest_arp, int ifindex)
+{
+ int fd;
+ int result;
+ struct sockaddr_ll dest;
+ struct udp_dhcp_packet packet;
+
+ fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP));
+ if (fd < 0) {
+ bb_perror_msg("socket");
+ return -1;
+ }
+
+ memset(&dest, 0, sizeof(dest));
+ memset(&packet, 0, sizeof(packet));
+
+ dest.sll_family = AF_PACKET;
+ dest.sll_protocol = htons(ETH_P_IP);
+ dest.sll_ifindex = ifindex;
+ dest.sll_halen = 6;
+ memcpy(dest.sll_addr, dest_arp, 6);
+ if (bind(fd, (struct sockaddr *)&dest, sizeof(struct sockaddr_ll)) < 0) {
+ bb_perror_msg("bind");
+ close(fd);
+ return -1;
+ }
+
+ packet.ip.protocol = IPPROTO_UDP;
+ packet.ip.saddr = source_ip;
+ packet.ip.daddr = dest_ip;
+ packet.udp.source = htons(source_port);
+ packet.udp.dest = htons(dest_port);
+ packet.udp.len = htons(sizeof(packet.udp) + sizeof(struct dhcpMessage)); /* cheat on the psuedo-header */
+ packet.ip.tot_len = packet.udp.len;
+ memcpy(&(packet.data), payload, sizeof(struct dhcpMessage));
+ packet.udp.check = udhcp_checksum(&packet, sizeof(struct udp_dhcp_packet));
+
+ packet.ip.tot_len = htons(sizeof(struct udp_dhcp_packet));
+ packet.ip.ihl = sizeof(packet.ip) >> 2;
+ packet.ip.version = IPVERSION;
+ packet.ip.ttl = IPDEFTTL;
+ packet.ip.check = udhcp_checksum(&(packet.ip), sizeof(packet.ip));
+
+ if (sizeof(struct udp_dhcp_packet) != 576)
+ BUG_sizeof_struct_udp_dhcp_packet_must_be_576();
+
+ result = sendto(fd, &packet, sizeof(struct udp_dhcp_packet), 0,
+ (struct sockaddr *) &dest, sizeof(dest));
+ if (result <= 0) {
+ bb_perror_msg("sendto");
+ }
+ close(fd);
+ return result;
+}
+
+
+/* Let the kernel do all the work for packet generation */
+int udhcp_kernel_packet(struct dhcpMessage *payload,
+ uint32_t source_ip, int source_port,
+ uint32_t dest_ip, int dest_port)
+{
+ int fd, result;
+ struct sockaddr_in client;
+
+ fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (fd < 0)
+ return -1;
+
+ if (setsockopt_reuseaddr(fd) == -1) {
+ close(fd);
+ return -1;
+ }
+
+ memset(&client, 0, sizeof(client));
+ client.sin_family = AF_INET;
+ client.sin_port = htons(source_port);
+ client.sin_addr.s_addr = source_ip;
+
+ if (bind(fd, (struct sockaddr *)&client, sizeof(struct sockaddr)) == -1) {
+ close(fd);
+ return -1;
+ }
+
+ memset(&client, 0, sizeof(client));
+ client.sin_family = AF_INET;
+ client.sin_port = htons(dest_port);
+ client.sin_addr.s_addr = dest_ip;
+
+ if (connect(fd, (struct sockaddr *)&client, sizeof(struct sockaddr)) == -1) {
+ close(fd);
+ return -1;
+ }
+
+ result = write(fd, payload, sizeof(struct dhcpMessage));
+ close(fd);
+ return result;
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/udhcp/pidfile.c b/i/pc104/initrd/conf/busybox/networking/udhcp/pidfile.c
new file mode 100644
index 0000000..be65d5f
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/udhcp/pidfile.c
@@ -0,0 +1,60 @@
+/* vi: set sw=4 ts=4: */
+/* pidfile.c
+ *
+ * Functions to assist in the writing and removing of pidfiles.
+ *
+ * Russ Dill <Russ.Dill@asu.edu> September 2001
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "common.h"
+
+
+static const char *saved_pidfile;
+
+static void pidfile_delete(void)
+{
+ if (saved_pidfile) unlink(saved_pidfile);
+}
+
+
+int pidfile_acquire(const char *pidfile)
+{
+ int pid_fd;
+ if (!pidfile) return -1;
+
+ pid_fd = open(pidfile, O_CREAT|O_WRONLY|O_TRUNC, 0644);
+ if (pid_fd < 0) {
+ bb_perror_msg("cannot open pidfile %s", pidfile);
+ } else {
+ lockf(pid_fd, F_LOCK, 0);
+ if (!saved_pidfile)
+ atexit(pidfile_delete);
+ saved_pidfile = pidfile;
+ }
+
+ return pid_fd;
+}
+
+
+void pidfile_write_release(int pid_fd)
+{
+ if (pid_fd < 0) return;
+
+ fdprintf(pid_fd, "%d\n", getpid());
+ lockf(pid_fd, F_UNLCK, 0);
+ close(pid_fd);
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/udhcp/script.c b/i/pc104/initrd/conf/busybox/networking/udhcp/script.c
new file mode 100644
index 0000000..dc8ff7a
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/udhcp/script.c
@@ -0,0 +1,224 @@
+/* vi: set sw=4 ts=4: */
+/* script.c
+ *
+ * Functions to call the DHCP client notification scripts
+ *
+ * Russ Dill <Russ.Dill@asu.edu> July 2001
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "common.h"
+#include "dhcpd.h"
+#include "dhcpc.h"
+#include "options.h"
+
+
+/* get a rough idea of how long an option will be (rounding up...) */
+static const int max_option_length[] = {
+ [OPTION_IP] = sizeof("255.255.255.255 "),
+ [OPTION_IP_PAIR] = sizeof("255.255.255.255 ") * 2,
+ [OPTION_STRING] = 1,
+#if ENABLE_FEATURE_RFC3397
+ [OPTION_STR1035] = 1,
+#endif
+ [OPTION_BOOLEAN] = sizeof("yes "),
+ [OPTION_U8] = sizeof("255 "),
+ [OPTION_U16] = sizeof("65535 "),
+ [OPTION_S16] = sizeof("-32768 "),
+ [OPTION_U32] = sizeof("4294967295 "),
+ [OPTION_S32] = sizeof("-2147483684 "),
+};
+
+
+static inline int upper_length(int length, int opt_index)
+{
+ return max_option_length[opt_index] *
+ (length / option_lengths[opt_index]);
+}
+
+
+static int sprintip(char *dest, const char *pre, const uint8_t *ip)
+{
+ return sprintf(dest, "%s%d.%d.%d.%d", pre, ip[0], ip[1], ip[2], ip[3]);
+}
+
+
+/* really simple implementation, just count the bits */
+static int mton(struct in_addr *mask)
+{
+ int i;
+ unsigned long bits = ntohl(mask->s_addr);
+ /* too bad one can't check the carry bit, etc in c bit
+ * shifting */
+ for (i = 0; i < 32 && !((bits >> i) & 1); i++);
+ return 32 - i;
+}
+
+
+/* Allocate and fill with the text of option 'option'. */
+static char *alloc_fill_opts(uint8_t *option, const struct dhcp_option *type_p)
+{
+ int len, type, optlen;
+ uint16_t val_u16;
+ int16_t val_s16;
+ uint32_t val_u32;
+ int32_t val_s32;
+ char *dest, *ret;
+
+ len = option[OPT_LEN - 2];
+ type = type_p->flags & TYPE_MASK;
+ optlen = option_lengths[type];
+
+ dest = ret = xmalloc(upper_length(len, type) + strlen(type_p->name) + 2);
+ dest += sprintf(ret, "%s=", type_p->name);
+
+ for (;;) {
+ switch (type) {
+ case OPTION_IP_PAIR:
+ dest += sprintip(dest, "", option);
+ *(dest++) = '/';
+ option += 4;
+ optlen = 4;
+ case OPTION_IP: /* Works regardless of host byte order. */
+ dest += sprintip(dest, "", option);
+ break;
+ case OPTION_BOOLEAN:
+ dest += sprintf(dest, *option ? "yes" : "no");
+ break;
+ case OPTION_U8:
+ dest += sprintf(dest, "%u", *option);
+ break;
+ case OPTION_U16:
+ memcpy(&val_u16, option, 2);
+ dest += sprintf(dest, "%u", ntohs(val_u16));
+ break;
+ case OPTION_S16:
+ memcpy(&val_s16, option, 2);
+ dest += sprintf(dest, "%d", ntohs(val_s16));
+ break;
+ case OPTION_U32:
+ memcpy(&val_u32, option, 4);
+ dest += sprintf(dest, "%lu", (unsigned long) ntohl(val_u32));
+ break;
+ case OPTION_S32:
+ memcpy(&val_s32, option, 4);
+ dest += sprintf(dest, "%ld", (long) ntohl(val_s32));
+ break;
+ case OPTION_STRING:
+ memcpy(dest, option, len);
+ dest[len] = '\0';
+ return ret; /* Short circuit this case */
+#if ENABLE_FEATURE_RFC3397
+ case OPTION_STR1035:
+ /* unpack option into dest; use ret for prefix (i.e., "optname=") */
+ dest = dname_dec(option, len, ret);
+ free(ret);
+ return dest;
+#endif
+ }
+ option += optlen;
+ len -= optlen;
+ if (len <= 0) break;
+ dest += sprintf(dest, " ");
+ }
+ return ret;
+}
+
+
+/* put all the parameters into an environment */
+static char **fill_envp(struct dhcpMessage *packet)
+{
+ int num_options = 0;
+ int i, j;
+ char **envp;
+ uint8_t *temp;
+ struct in_addr subnet;
+ char over = 0;
+
+ if (packet == NULL)
+ num_options = 0;
+ else {
+ for (i = 0; dhcp_options[i].code; i++)
+ if (get_option(packet, dhcp_options[i].code)) {
+ num_options++;
+ if (dhcp_options[i].code == DHCP_SUBNET)
+ num_options++; /* for mton */
+ }
+ if (packet->siaddr) num_options++;
+ if ((temp = get_option(packet, DHCP_OPTION_OVER)))
+ over = *temp;
+ if (!(over & FILE_FIELD) && packet->file[0]) num_options++;
+ if (!(over & SNAME_FIELD) && packet->sname[0]) num_options++;
+ }
+
+ envp = xzalloc(sizeof(char *) * (num_options + 5));
+ j = 0;
+ envp[j++] = xasprintf("interface=%s", client_config.interface);
+ envp[j++] = xasprintf("PATH=%s",
+ getenv("PATH") ? : "/bin:/usr/bin:/sbin:/usr/sbin");
+ envp[j++] = xasprintf("HOME=%s", getenv("HOME") ? : "/");
+
+ if (packet == NULL) return envp;
+
+ envp[j] = xmalloc(sizeof("ip=255.255.255.255"));
+ sprintip(envp[j++], "ip=", (uint8_t *) &packet->yiaddr);
+
+ for (i = 0; dhcp_options[i].code; i++) {
+ temp = get_option(packet, dhcp_options[i].code);
+ if (!temp)
+ continue;
+ envp[j++] = alloc_fill_opts(temp, &dhcp_options[i]);
+
+ /* Fill in a subnet bits option for things like /24 */
+ if (dhcp_options[i].code == DHCP_SUBNET) {
+ memcpy(&subnet, temp, 4);
+ envp[j++] = xasprintf("mask=%d", mton(&subnet));
+ }
+ }
+ if (packet->siaddr) {
+ envp[j] = xmalloc(sizeof("siaddr=255.255.255.255"));
+ sprintip(envp[j++], "siaddr=", (uint8_t *) &packet->siaddr);
+ }
+ if (!(over & FILE_FIELD) && packet->file[0]) {
+ /* watch out for invalid packets */
+ packet->file[sizeof(packet->file) - 1] = '\0';
+ envp[j++] = xasprintf("boot_file=%s", packet->file);
+ }
+ if (!(over & SNAME_FIELD) && packet->sname[0]) {
+ /* watch out for invalid packets */
+ packet->sname[sizeof(packet->sname) - 1] = '\0';
+ envp[j++] = xasprintf("sname=%s", packet->sname);
+ }
+ return envp;
+}
+
+
+/* Call a script with a par file and env vars */
+void udhcp_run_script(struct dhcpMessage *packet, const char *name)
+{
+ int pid;
+ char **envp, **curr;
+
+ if (client_config.script == NULL)
+ return;
+
+ DEBUG("vfork'ing and execle'ing %s", client_config.script);
+
+ envp = fill_envp(packet);
+ /* call script */
+ pid = vfork();
+ if (pid) {
+ waitpid(pid, NULL, 0);
+ for (curr = envp; *curr; curr++) free(*curr);
+ free(envp);
+ return;
+ } else if (pid == 0) {
+ /* close fd's? */
+ /* exec script */
+ execle(client_config.script, client_config.script,
+ name, NULL, envp);
+ bb_perror_msg("script %s failed", client_config.script);
+ exit(1);
+ }
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/udhcp/serverpacket.c b/i/pc104/initrd/conf/busybox/networking/udhcp/serverpacket.c
new file mode 100644
index 0000000..8889fda
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/udhcp/serverpacket.c
@@ -0,0 +1,261 @@
+/* vi: set sw=4 ts=4: */
+/* serverpacket.c
+ *
+ * Construct and send DHCP server packets
+ *
+ * Russ Dill <Russ.Dill@asu.edu> July 2001
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "common.h"
+#include "dhcpd.h"
+#include "options.h"
+
+
+/* send a packet to giaddr using the kernel ip stack */
+static int send_packet_to_relay(struct dhcpMessage *payload)
+{
+ DEBUG("Forwarding packet to relay");
+
+ return udhcp_kernel_packet(payload, server_config.server, SERVER_PORT,
+ payload->giaddr, SERVER_PORT);
+}
+
+
+/* send a packet to a specific arp address and ip address by creating our own ip packet */
+static int send_packet_to_client(struct dhcpMessage *payload, int force_broadcast)
+{
+ uint8_t *chaddr;
+ uint32_t ciaddr;
+
+ if (force_broadcast) {
+ DEBUG("broadcasting packet to client (NAK)");
+ ciaddr = INADDR_BROADCAST;
+ chaddr = MAC_BCAST_ADDR;
+ } else if (payload->ciaddr) {
+ DEBUG("unicasting packet to client ciaddr");
+ ciaddr = payload->ciaddr;
+ chaddr = payload->chaddr;
+ } else if (ntohs(payload->flags) & BROADCAST_FLAG) {
+ DEBUG("broadcasting packet to client (requested)");
+ ciaddr = INADDR_BROADCAST;
+ chaddr = MAC_BCAST_ADDR;
+ } else {
+ DEBUG("unicasting packet to client yiaddr");
+ ciaddr = payload->yiaddr;
+ chaddr = payload->chaddr;
+ }
+ return udhcp_raw_packet(payload, server_config.server, SERVER_PORT,
+ ciaddr, CLIENT_PORT, chaddr, server_config.ifindex);
+}
+
+
+/* send a dhcp packet, if force broadcast is set, the packet will be broadcast to the client */
+static int send_packet(struct dhcpMessage *payload, int force_broadcast)
+{
+ int ret;
+
+ if (payload->giaddr)
+ ret = send_packet_to_relay(payload);
+ else ret = send_packet_to_client(payload, force_broadcast);
+ return ret;
+}
+
+
+static void init_packet(struct dhcpMessage *packet, struct dhcpMessage *oldpacket, char type)
+{
+ udhcp_init_header(packet, type);
+ packet->xid = oldpacket->xid;
+ memcpy(packet->chaddr, oldpacket->chaddr, 16);
+ packet->flags = oldpacket->flags;
+ packet->giaddr = oldpacket->giaddr;
+ packet->ciaddr = oldpacket->ciaddr;
+ add_simple_option(packet->options, DHCP_SERVER_ID, server_config.server);
+}
+
+
+/* add in the bootp options */
+static void add_bootp_options(struct dhcpMessage *packet)
+{
+ packet->siaddr = server_config.siaddr;
+ if (server_config.sname)
+ strncpy((char*)packet->sname, server_config.sname, sizeof(packet->sname) - 1);
+ if (server_config.boot_file)
+ strncpy((char*)packet->file, server_config.boot_file, sizeof(packet->file) - 1);
+}
+
+
+/* send a DHCP OFFER to a DHCP DISCOVER */
+int sendOffer(struct dhcpMessage *oldpacket)
+{
+ struct dhcpMessage packet;
+ struct dhcpOfferedAddr *lease = NULL;
+ uint32_t req_align, lease_time_align = server_config.lease;
+ uint8_t *req, *lease_time;
+ struct option_set *curr;
+ struct in_addr addr;
+
+ uint32_t static_lease_ip;
+
+ init_packet(&packet, oldpacket, DHCPOFFER);
+
+ static_lease_ip = getIpByMac(server_config.static_leases, oldpacket->chaddr);
+
+ /* ADDME: if static, short circuit */
+ if (!static_lease_ip) {
+ /* the client is in our lease/offered table */
+ lease = find_lease_by_chaddr(oldpacket->chaddr);
+ if (lease) {
+ if (!lease_expired(lease))
+ lease_time_align = lease->expires - time(0);
+ packet.yiaddr = lease->yiaddr;
+
+ /* Or the client has a requested ip */
+ } else if ((req = get_option(oldpacket, DHCP_REQUESTED_IP))
+ /* Don't look here (ugly hackish thing to do) */
+ && memcpy(&req_align, req, 4)
+ /* and the ip is in the lease range */
+ && ntohl(req_align) >= ntohl(server_config.start)
+ && ntohl(req_align) <= ntohl(server_config.end)
+ && !static_lease_ip /* Check that its not a static lease */
+ /* and is not already taken/offered */
+ && (!(lease = find_lease_by_yiaddr(req_align))
+ /* or its taken, but expired */ /* ADDME: or maybe in here */
+ || lease_expired(lease))
+ ) {
+ packet.yiaddr = req_align; /* FIXME: oh my, is there a host using this IP? */
+ /* otherwise, find a free IP */
+ } else {
+ /* Is it a static lease? (No, because find_address skips static lease) */
+ packet.yiaddr = find_address(0);
+ /* try for an expired lease */
+ if (!packet.yiaddr) packet.yiaddr = find_address(1);
+ }
+
+ if (!packet.yiaddr) {
+ bb_error_msg("no IP addresses to give - OFFER abandoned");
+ return -1;
+ }
+ if (!add_lease(packet.chaddr, packet.yiaddr, server_config.offer_time)) {
+ bb_error_msg("lease pool is full - OFFER abandoned");
+ return -1;
+ }
+ lease_time = get_option(oldpacket, DHCP_LEASE_TIME);
+ if (lease_time) {
+ memcpy(&lease_time_align, lease_time, 4);
+ lease_time_align = ntohl(lease_time_align);
+ if (lease_time_align > server_config.lease)
+ lease_time_align = server_config.lease;
+ }
+
+ /* Make sure we aren't just using the lease time from the previous offer */
+ if (lease_time_align < server_config.min_lease)
+ lease_time_align = server_config.lease;
+ /* ADDME: end of short circuit */
+ } else {
+ /* It is a static lease... use it */
+ packet.yiaddr = static_lease_ip;
+ }
+
+ add_simple_option(packet.options, DHCP_LEASE_TIME, htonl(lease_time_align));
+
+ curr = server_config.options;
+ while (curr) {
+ if (curr->data[OPT_CODE] != DHCP_LEASE_TIME)
+ add_option_string(packet.options, curr->data);
+ curr = curr->next;
+ }
+
+ add_bootp_options(&packet);
+
+ addr.s_addr = packet.yiaddr;
+ bb_info_msg("Sending OFFER of %s", inet_ntoa(addr));
+ return send_packet(&packet, 0);
+}
+
+
+int sendNAK(struct dhcpMessage *oldpacket)
+{
+ struct dhcpMessage packet;
+
+ init_packet(&packet, oldpacket, DHCPNAK);
+
+ DEBUG("Sending NAK");
+ return send_packet(&packet, 1);
+}
+
+
+int sendACK(struct dhcpMessage *oldpacket, uint32_t yiaddr)
+{
+ struct dhcpMessage packet;
+ struct option_set *curr;
+ uint8_t *lease_time;
+ uint32_t lease_time_align = server_config.lease;
+ struct in_addr addr;
+
+ init_packet(&packet, oldpacket, DHCPACK);
+ packet.yiaddr = yiaddr;
+
+ if ((lease_time = get_option(oldpacket, DHCP_LEASE_TIME))) {
+ memcpy(&lease_time_align, lease_time, 4);
+ lease_time_align = ntohl(lease_time_align);
+ if (lease_time_align > server_config.lease)
+ lease_time_align = server_config.lease;
+ else if (lease_time_align < server_config.min_lease)
+ lease_time_align = server_config.lease;
+ }
+
+ add_simple_option(packet.options, DHCP_LEASE_TIME, htonl(lease_time_align));
+
+ curr = server_config.options;
+ while (curr) {
+ if (curr->data[OPT_CODE] != DHCP_LEASE_TIME)
+ add_option_string(packet.options, curr->data);
+ curr = curr->next;
+ }
+
+ add_bootp_options(&packet);
+
+ addr.s_addr = packet.yiaddr;
+ bb_info_msg("Sending ACK to %s", inet_ntoa(addr));
+
+ if (send_packet(&packet, 0) < 0)
+ return -1;
+
+ add_lease(packet.chaddr, packet.yiaddr, lease_time_align);
+
+ return 0;
+}
+
+
+int send_inform(struct dhcpMessage *oldpacket)
+{
+ struct dhcpMessage packet;
+ struct option_set *curr;
+
+ init_packet(&packet, oldpacket, DHCPACK);
+
+ curr = server_config.options;
+ while (curr) {
+ if (curr->data[OPT_CODE] != DHCP_LEASE_TIME)
+ add_option_string(packet.options, curr->data);
+ curr = curr->next;
+ }
+
+ add_bootp_options(&packet);
+
+ return send_packet(&packet, 0);
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/udhcp/signalpipe.c b/i/pc104/initrd/conf/busybox/networking/udhcp/signalpipe.c
new file mode 100644
index 0000000..3615965
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/udhcp/signalpipe.c
@@ -0,0 +1,77 @@
+/* vi: set sw=4 ts=4: */
+/* signalpipe.c
+ *
+ * Signal pipe infrastructure. A reliable way of delivering signals.
+ *
+ * Russ Dill <Russ.Dill@asu.edu> December 2003
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "common.h"
+
+
+static int signal_pipe[2];
+
+static void signal_handler(int sig)
+{
+ if (send(signal_pipe[1], &sig, sizeof(sig), MSG_DONTWAIT) < 0)
+ bb_perror_msg("cannot send signal");
+}
+
+
+/* Call this before doing anything else. Sets up the socket pair
+ * and installs the signal handler */
+void udhcp_sp_setup(void)
+{
+ socketpair(AF_UNIX, SOCK_STREAM, 0, signal_pipe);
+ fcntl(signal_pipe[0], F_SETFD, FD_CLOEXEC);
+ fcntl(signal_pipe[1], F_SETFD, FD_CLOEXEC);
+ signal(SIGUSR1, signal_handler);
+ signal(SIGUSR2, signal_handler);
+ signal(SIGTERM, signal_handler);
+}
+
+
+/* Quick little function to setup the rfds. Will return the
+ * max_fd for use with select. Limited in that you can only pass
+ * one extra fd */
+int udhcp_sp_fd_set(fd_set *rfds, int extra_fd)
+{
+ FD_ZERO(rfds);
+ FD_SET(signal_pipe[0], rfds);
+ if (extra_fd >= 0) {
+ fcntl(extra_fd, F_SETFD, FD_CLOEXEC);
+ FD_SET(extra_fd, rfds);
+ }
+ return signal_pipe[0] > extra_fd ? signal_pipe[0] : extra_fd;
+}
+
+
+/* Read a signal from the signal pipe. Returns 0 if there is
+ * no signal, -1 on error (and sets errno appropriately), and
+ * your signal on success */
+int udhcp_sp_read(fd_set *rfds)
+{
+ int sig;
+
+ if (!FD_ISSET(signal_pipe[0], rfds))
+ return 0;
+
+ if (read(signal_pipe[0], &sig, sizeof(sig)) < 0)
+ return -1;
+
+ return sig;
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/udhcp/socket.c b/i/pc104/initrd/conf/busybox/networking/udhcp/socket.c
new file mode 100644
index 0000000..d294fb2
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/udhcp/socket.c
@@ -0,0 +1,126 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * socket.c -- DHCP server client/server socket creation
+ *
+ * udhcp client/server
+ * Copyright (C) 1999 Matthew Ramsay <matthewr@moreton.com.au>
+ * Chris Trew <ctrew@moreton.com.au>
+ *
+ * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <net/if.h>
+#include <features.h>
+#if (defined(__GLIBC__) && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1) || defined _NEWLIB_VERSION
+#include <netpacket/packet.h>
+#include <net/ethernet.h>
+#else
+#include <asm/types.h>
+#include <linux/if_packet.h>
+#include <linux/if_ether.h>
+#endif
+
+#include "common.h"
+
+
+int read_interface(const char *interface, int *ifindex, uint32_t *addr, uint8_t *arp)
+{
+ int fd;
+ struct ifreq ifr;
+ struct sockaddr_in *our_ip;
+
+ memset(&ifr, 0, sizeof(struct ifreq));
+ fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
+ if (fd < 0) {
+ bb_perror_msg("socket failed");
+ return -1;
+ }
+
+ ifr.ifr_addr.sa_family = AF_INET;
+ strncpy(ifr.ifr_name, interface, sizeof(ifr.ifr_name));
+ if (addr) {
+ if (ioctl(fd, SIOCGIFADDR, &ifr) != 0) {
+ bb_perror_msg("SIOCGIFADDR failed, is the interface up and configured?");
+ close(fd);
+ return -1;
+ }
+ our_ip = (struct sockaddr_in *) &ifr.ifr_addr;
+ *addr = our_ip->sin_addr.s_addr;
+ DEBUG("%s (our ip) = %s", ifr.ifr_name, inet_ntoa(our_ip->sin_addr));
+ }
+
+ if (ifindex) {
+ if (ioctl(fd, SIOCGIFINDEX, &ifr) != 0) {
+ bb_perror_msg("SIOCGIFINDEX failed");
+ close(fd);
+ return -1;
+ }
+ DEBUG("adapter index %d", ifr.ifr_ifindex);
+ *ifindex = ifr.ifr_ifindex;
+ }
+
+ if (arp) {
+ if (ioctl(fd, SIOCGIFHWADDR, &ifr) != 0) {
+ bb_perror_msg("SIOCGIFHWADDR failed");
+ close(fd);
+ return -1;
+ }
+ memcpy(arp, ifr.ifr_hwaddr.sa_data, 6);
+ DEBUG("adapter hardware address %02x:%02x:%02x:%02x:%02x:%02x",
+ arp[0], arp[1], arp[2], arp[3], arp[4], arp[5]);
+ }
+
+ return 0;
+}
+
+
+int listen_socket(uint32_t ip, int port, const char *inf)
+{
+ struct ifreq interface;
+ int fd;
+ struct sockaddr_in addr;
+
+ DEBUG("Opening listen socket on 0x%08x:%d %s", ip, port, inf);
+ fd = xsocket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ addr.sin_addr.s_addr = ip;
+
+ if (setsockopt_reuseaddr(fd) == -1) {
+ close(fd);
+ return -1;
+ }
+ if (setsockopt_broadcast(fd) == -1) {
+ close(fd);
+ return -1;
+ }
+
+ strncpy(interface.ifr_name, inf, IFNAMSIZ);
+ if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, &interface, sizeof(interface)) < 0) {
+ close(fd);
+ return -1;
+ }
+
+ if (bind(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr)) == -1) {
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/udhcp/static_leases.c b/i/pc104/initrd/conf/busybox/networking/udhcp/static_leases.c
new file mode 100644
index 0000000..aabfb81
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/udhcp/static_leases.c
@@ -0,0 +1,99 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * static_leases.c -- Couple of functions to assist with storing and
+ * retrieving data for static leases
+ *
+ * Wade Berrier <wberrier@myrealbox.com> September 2004
+ *
+ */
+
+#include "common.h"
+#include "dhcpd.h"
+
+
+/* Takes the address of the pointer to the static_leases linked list,
+ * Address to a 6 byte mac address
+ * Address to a 4 byte ip address */
+int addStaticLease(struct static_lease **lease_struct, uint8_t *mac, uint32_t *ip)
+{
+ struct static_lease *cur;
+ struct static_lease *new_static_lease;
+
+ /* Build new node */
+ new_static_lease = xmalloc(sizeof(struct static_lease));
+ new_static_lease->mac = mac;
+ new_static_lease->ip = ip;
+ new_static_lease->next = NULL;
+
+ /* If it's the first node to be added... */
+ if (*lease_struct == NULL) {
+ *lease_struct = new_static_lease;
+ } else {
+ cur = *lease_struct;
+ while (cur->next) {
+ cur = cur->next;
+ }
+
+ cur->next = new_static_lease;
+ }
+
+ return 1;
+}
+
+/* Check to see if a mac has an associated static lease */
+uint32_t getIpByMac(struct static_lease *lease_struct, void *arg)
+{
+ uint32_t return_ip;
+ struct static_lease *cur = lease_struct;
+ uint8_t *mac = arg;
+
+ return_ip = 0;
+
+ while (cur) {
+ /* If the client has the correct mac */
+ if (memcmp(cur->mac, mac, 6) == 0) {
+ return_ip = *(cur->ip);
+ }
+
+ cur = cur->next;
+ }
+
+ return return_ip;
+}
+
+/* Check to see if an ip is reserved as a static ip */
+uint32_t reservedIp(struct static_lease *lease_struct, uint32_t ip)
+{
+ struct static_lease *cur = lease_struct;
+
+ uint32_t return_val = 0;
+
+ while (cur) {
+ /* If the client has the correct ip */
+ if (*cur->ip == ip)
+ return_val = 1;
+
+ cur = cur->next;
+ }
+
+ return return_val;
+}
+
+#if ENABLE_FEATURE_UDHCP_DEBUG
+/* Print out static leases just to check what's going on */
+/* Takes the address of the pointer to the static_leases linked list */
+void printStaticLeases(struct static_lease **arg)
+{
+ /* Get a pointer to the linked list */
+ struct static_lease *cur = *arg;
+
+ while (cur) {
+ /* printf("PrintStaticLeases: Lease mac Address: %x\n", cur->mac); */
+ printf("PrintStaticLeases: Lease mac Value: %x\n", *(cur->mac));
+ /* printf("PrintStaticLeases: Lease ip Address: %x\n", cur->ip); */
+ printf("PrintStaticLeases: Lease ip Value: %x\n", *(cur->ip));
+
+ cur = cur->next;
+ }
+}
+#endif
diff --git a/i/pc104/initrd/conf/busybox/networking/vconfig.c b/i/pc104/initrd/conf/busybox/networking/vconfig.c
new file mode 100644
index 0000000..06c06cb
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/vconfig.c
@@ -0,0 +1,165 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * vconfig implementation for busybox
+ *
+ * Copyright (C) 2001 Manuel Novoa III <mjn3@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/* BB_AUDIT SUSv3 N/A */
+
+#include "busybox.h"
+#include <net/if.h>
+
+/* Stuff from linux/if_vlan.h, kernel version 2.4.23 */
+enum vlan_ioctl_cmds {
+ ADD_VLAN_CMD,
+ DEL_VLAN_CMD,
+ SET_VLAN_INGRESS_PRIORITY_CMD,
+ SET_VLAN_EGRESS_PRIORITY_CMD,
+ GET_VLAN_INGRESS_PRIORITY_CMD,
+ GET_VLAN_EGRESS_PRIORITY_CMD,
+ SET_VLAN_NAME_TYPE_CMD,
+ SET_VLAN_FLAG_CMD
+};
+enum vlan_name_types {
+ VLAN_NAME_TYPE_PLUS_VID, /* Name will look like: vlan0005 */
+ VLAN_NAME_TYPE_RAW_PLUS_VID, /* name will look like: eth1.0005 */
+ VLAN_NAME_TYPE_PLUS_VID_NO_PAD, /* Name will look like: vlan5 */
+ VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD, /* Name will look like: eth0.5 */
+ VLAN_NAME_TYPE_HIGHEST
+};
+
+struct vlan_ioctl_args {
+ int cmd; /* Should be one of the vlan_ioctl_cmds enum above. */
+ char device1[24];
+
+ union {
+ char device2[24];
+ int VID;
+ unsigned int skb_priority;
+ unsigned int name_type;
+ unsigned int bind_type;
+ unsigned int flag; /* Matches vlan_dev_info flags */
+ } u;
+
+ short vlan_qos;
+};
+
+#define VLAN_GROUP_ARRAY_LEN 4096
+#define SIOCSIFVLAN 0x8983 /* Set 802.1Q VLAN options */
+
+/* On entry, table points to the length of the current string plus
+ * nul terminator plus data length for the subsequent entry. The
+ * return value is the last data entry for the matching string. */
+static const char *xfind_str(const char *table, const char *str)
+{
+ while (strcasecmp(str, table+1) != 0) {
+ if (!*(table += table[0])) {
+ bb_show_usage();
+ }
+ }
+ return table - 1;
+}
+
+static const char cmds[] = {
+ 4, ADD_VLAN_CMD, 7,
+ 'a', 'd', 'd', 0,
+ 3, DEL_VLAN_CMD, 7,
+ 'r', 'e', 'm', 0,
+ 3, SET_VLAN_NAME_TYPE_CMD, 17,
+ 's', 'e', 't', '_',
+ 'n', 'a', 'm', 'e', '_',
+ 't', 'y', 'p', 'e', 0,
+ 5, SET_VLAN_FLAG_CMD, 12,
+ 's', 'e', 't', '_',
+ 'f', 'l', 'a', 'g', 0,
+ 5, SET_VLAN_EGRESS_PRIORITY_CMD, 18,
+ 's', 'e', 't', '_',
+ 'e', 'g', 'r', 'e', 's', 's', '_',
+ 'm', 'a', 'p', 0,
+ 5, SET_VLAN_INGRESS_PRIORITY_CMD, 16,
+ 's', 'e', 't', '_',
+ 'i', 'n', 'g', 'r', 'e', 's', 's', '_',
+ 'm', 'a', 'p', 0,
+};
+
+static const char name_types[] = {
+ VLAN_NAME_TYPE_PLUS_VID, 16,
+ 'V', 'L', 'A', 'N',
+ '_', 'P', 'L', 'U', 'S', '_', 'V', 'I', 'D',
+ 0,
+ VLAN_NAME_TYPE_PLUS_VID_NO_PAD, 22,
+ 'V', 'L', 'A', 'N',
+ '_', 'P', 'L', 'U', 'S', '_', 'V', 'I', 'D',
+ '_', 'N', 'O', '_', 'P', 'A', 'D', 0,
+ VLAN_NAME_TYPE_RAW_PLUS_VID, 15,
+ 'D', 'E', 'V',
+ '_', 'P', 'L', 'U', 'S', '_', 'V', 'I', 'D',
+ 0,
+ VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD, 20,
+ 'D', 'E', 'V',
+ '_', 'P', 'L', 'U', 'S', '_', 'V', 'I', 'D',
+ '_', 'N', 'O', '_', 'P', 'A', 'D', 0,
+};
+
+static const char conf_file_name[] = "/proc/net/vlan/config";
+
+int vconfig_main(int argc, char **argv);
+int vconfig_main(int argc, char **argv)
+{
+ struct vlan_ioctl_args ifr;
+ const char *p;
+ int fd;
+
+ if (argc < 3) {
+ bb_show_usage();
+ }
+
+ /* Don't bother closing the filedes. It will be closed on cleanup. */
+ /* Will die if 802.1q is not present */
+ xopen(conf_file_name, O_RDONLY);
+
+ memset(&ifr, 0, sizeof(struct vlan_ioctl_args));
+
+ ++argv;
+ p = xfind_str(cmds+2, *argv);
+ ifr.cmd = *p;
+ if (argc != p[-1]) {
+ bb_show_usage();
+ }
+
+ if (ifr.cmd == SET_VLAN_NAME_TYPE_CMD) { /* set_name_type */
+ ifr.u.name_type = *xfind_str(name_types+1, argv[1]);
+ } else {
+ if (strlen(argv[1]) >= IF_NAMESIZE) {
+ bb_error_msg_and_die("if_name >= %d chars", IF_NAMESIZE);
+ }
+ strcpy(ifr.device1, argv[1]);
+ p = argv[2];
+
+ /* I suppose one could try to combine some of the function calls below,
+ * since ifr.u.flag, ifr.u.VID, and ifr.u.skb_priority are all same-sized
+ * (unsigned) int members of a unions. But because of the range checking,
+ * doing so wouldn't save that much space and would also make maintainence
+ * more of a pain. */
+ if (ifr.cmd == SET_VLAN_FLAG_CMD) { /* set_flag */
+ ifr.u.flag = xatoul_range(p, 0, 1);
+ /* DM: in order to set reorder header, qos must be set */
+ ifr.vlan_qos = xatoul_range(argv[3], 0, 7);
+ } else if (ifr.cmd == ADD_VLAN_CMD) { /* add */
+ ifr.u.VID = xatoul_range(p, 0, VLAN_GROUP_ARRAY_LEN-1);
+ } else if (ifr.cmd != DEL_VLAN_CMD) { /* set_{egress|ingress}_map */
+ ifr.u.skb_priority = xatou(p);
+ ifr.vlan_qos = xatoul_range(argv[3], 0, 7);
+ }
+ }
+
+ fd = xsocket(AF_INET, SOCK_STREAM, 0);
+ if (ioctl(fd, SIOCSIFVLAN, &ifr) < 0) {
+ bb_perror_msg_and_die("ioctl error for %s", *argv);
+ }
+
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/networking/wget.c b/i/pc104/initrd/conf/busybox/networking/wget.c
new file mode 100644
index 0000000..db22215
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/wget.c
@@ -0,0 +1,808 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * wget - retrieve a file using HTTP or FTP
+ *
+ * Chip Rosenthal Covad Communications <chip@laserlink.net>
+ *
+ */
+
+/* We want libc to give us xxx64 functions also */
+/* http://www.unix.org/version2/whatsnew/lfs20mar.html */
+#define _LARGEFILE64_SOURCE 1
+
+#include "busybox.h"
+#include <getopt.h> /* for struct option */
+
+struct host_info {
+ // May be used if we ever will want to free() all xstrdup()s...
+ /* char *allocated; */
+ char *host;
+ int port;
+ char *path;
+ int is_ftp;
+ char *user;
+};
+
+static void parse_url(char *url, struct host_info *h);
+static FILE *open_socket(len_and_sockaddr *lsa);
+static char *gethdr(char *buf, size_t bufsiz, FILE *fp, int *istrunc);
+static int ftpcmd(const char *s1, const char *s2, FILE *fp, char *buf);
+
+/* Globals (can be accessed from signal handlers */
+static off_t content_len; /* Content-length of the file */
+static off_t beg_range; /* Range at which continue begins */
+#if ENABLE_FEATURE_WGET_STATUSBAR
+static off_t transferred; /* Number of bytes transferred so far */
+#endif
+static int chunked; /* chunked transfer encoding */
+#if ENABLE_FEATURE_WGET_STATUSBAR
+static void progressmeter(int flag);
+static const char *curfile; /* Name of current file being transferred */
+static struct timeval start; /* Time a transfer started */
+enum {
+ STALLTIME = 5 /* Seconds when xfer considered "stalled" */
+};
+#else
+static void progressmeter(int flag) {}
+#endif
+
+/* Read NMEMB elements of SIZE bytes into PTR from STREAM. Returns the
+ * number of elements read, and a short count if an eof or non-interrupt
+ * error is encountered. */
+static size_t safe_fread(void *ptr, size_t size, size_t nmemb, FILE *stream)
+{
+ size_t ret = 0;
+
+ do {
+ clearerr(stream);
+ ret += fread((char *)ptr + (ret * size), size, nmemb - ret, stream);
+ } while (ret < nmemb && ferror(stream) && errno == EINTR);
+
+ return ret;
+}
+
+/* Read a line or SIZE - 1 bytes into S, whichever is less, from STREAM.
+ * Returns S, or NULL if an eof or non-interrupt error is encountered. */
+static char *safe_fgets(char *s, int size, FILE *stream)
+{
+ char *ret;
+
+ do {
+ clearerr(stream);
+ ret = fgets(s, size, stream);
+ } while (ret == NULL && ferror(stream) && errno == EINTR);
+
+ return ret;
+}
+
+#if ENABLE_FEATURE_WGET_AUTHENTICATION
+/*
+ * Base64-encode character string and return the string.
+ */
+static char *base64enc(unsigned char *p, char *buf, int len)
+{
+ bb_uuencode(p, buf, len, bb_uuenc_tbl_base64);
+ return buf;
+}
+#endif
+
+int wget_main(int argc, char **argv);
+int wget_main(int argc, char **argv)
+{
+ char buf[512];
+ struct host_info server, target;
+ len_and_sockaddr *lsa;
+ int n, status;
+ int port;
+ int try = 5;
+ unsigned opt;
+ char *s;
+ char *proxy = 0;
+ char *dir_prefix = NULL;
+#if ENABLE_FEATURE_WGET_LONG_OPTIONS
+ char *extra_headers = NULL;
+ llist_t *headers_llist = NULL;
+#endif
+
+ /* server.allocated = target.allocated = NULL; */
+
+ FILE *sfp = NULL; /* socket to web/ftp server */
+ FILE *dfp = NULL; /* socket to ftp server (data) */
+ char *fname_out = NULL; /* where to direct output (-O) */
+ int got_clen = 0; /* got content-length: from server */
+ int output_fd = -1;
+ int use_proxy = 1; /* Use proxies if env vars are set */
+ const char *proxy_flag = "on"; /* Use proxies if env vars are set */
+ const char *user_agent = "Wget";/* Content of the "User-Agent" header field */
+
+ /*
+ * Crack command line.
+ */
+ enum {
+ WGET_OPT_CONTINUE = 0x1,
+ WGET_OPT_QUIET = 0x2,
+ WGET_OPT_OUTNAME = 0x4,
+ WGET_OPT_PREFIX = 0x8,
+ WGET_OPT_PROXY = 0x10,
+ WGET_OPT_USER_AGENT = 0x20,
+ WGET_OPT_PASSIVE = 0x40,
+ WGET_OPT_HEADER = 0x80,
+ };
+#if ENABLE_FEATURE_WGET_LONG_OPTIONS
+ static const struct option wget_long_options[] = {
+ // name, has_arg, flag, val
+ { "continue", no_argument, NULL, 'c' },
+ { "quiet", no_argument, NULL, 'q' },
+ { "output-document", required_argument, NULL, 'O' },
+ { "directory-prefix", required_argument, NULL, 'P' },
+ { "proxy", required_argument, NULL, 'Y' },
+ { "user-agent", required_argument, NULL, 'U' },
+ { "passive-ftp", no_argument, NULL, 0xff },
+ { "header", required_argument, NULL, 0xfe },
+ { 0, 0, 0, 0 }
+ };
+ applet_long_options = wget_long_options;
+#endif
+ opt_complementary = "-1" USE_FEATURE_WGET_LONG_OPTIONS(":\xfe::");
+ opt = getopt32(argc, argv, "cqO:P:Y:U:",
+ &fname_out, &dir_prefix,
+ &proxy_flag, &user_agent
+ USE_FEATURE_WGET_LONG_OPTIONS(, &headers_llist)
+ );
+ if (strcmp(proxy_flag, "off") == 0) {
+ /* Use the proxy if necessary. */
+ use_proxy = 0;
+ }
+#if ENABLE_FEATURE_WGET_LONG_OPTIONS
+ if (headers_llist) {
+ int size = 1;
+ char *cp;
+ llist_t *ll = headers_llist = llist_rev(headers_llist);
+ while (ll) {
+ size += strlen(ll->data) + 2;
+ ll = ll->link;
+ }
+ extra_headers = cp = xmalloc(size);
+ while (headers_llist) {
+ cp += sprintf(cp, "%s\r\n", headers_llist->data);
+ headers_llist = headers_llist->link;
+ }
+ }
+#endif
+
+ parse_url(argv[optind], &target);
+ server.host = target.host;
+ server.port = target.port;
+
+ /*
+ * Use the proxy if necessary.
+ */
+ if (use_proxy) {
+ proxy = getenv(target.is_ftp ? "ftp_proxy" : "http_proxy");
+ if (proxy && *proxy) {
+ parse_url(proxy, &server);
+ } else {
+ use_proxy = 0;
+ }
+ }
+
+ /* Guess an output filename */
+ if (!fname_out) {
+ // Dirty hack. Needed because bb_get_last_path_component
+ // will destroy trailing / by storing '\0' in last byte!
+ if (!last_char_is(target.path, '/')) {
+ fname_out = bb_get_last_path_component(target.path);
+#if ENABLE_FEATURE_WGET_STATUSBAR
+ curfile = fname_out;
+#endif
+ }
+ if (!fname_out || !fname_out[0]) {
+ /* bb_get_last_path_component writes
+ * to last '/' only. We don't have one here... */
+ fname_out = (char*)"index.html";
+#if ENABLE_FEATURE_WGET_STATUSBAR
+ curfile = fname_out;
+#endif
+ }
+ if (dir_prefix != NULL)
+ fname_out = concat_path_file(dir_prefix, fname_out);
+#if ENABLE_FEATURE_WGET_STATUSBAR
+ } else {
+ curfile = bb_get_last_path_component(fname_out);
+#endif
+ }
+ /* Impossible?
+ if ((opt & WGET_OPT_CONTINUE) && !fname_out)
+ bb_error_msg_and_die("cannot specify continue (-c) without a filename (-O)"); */
+
+ /*
+ * Determine where to start transfer.
+ */
+ if (LONE_DASH(fname_out)) {
+ output_fd = 1;
+ opt &= ~WGET_OPT_CONTINUE;
+ }
+ if (opt & WGET_OPT_CONTINUE) {
+ output_fd = open(fname_out, O_WRONLY);
+ if (output_fd >= 0) {
+ beg_range = xlseek(output_fd, 0, SEEK_END);
+ }
+ /* File doesn't exist. We do not create file here yet.
+ We are not sure it exists on remove side */
+ }
+
+ /* We want to do exactly _one_ DNS lookup, since some
+ * sites (i.e. ftp.us.debian.org) use round-robin DNS
+ * and we want to connect to only one IP... */
+ lsa = xhost2sockaddr(server.host, server.port);
+ if (!(opt & WGET_OPT_QUIET)) {
+ fprintf(stderr, "Connecting to %s (%s)\n", server.host,
+ xmalloc_sockaddr2dotted(&lsa->sa, lsa->len));
+ /* We leak result of xmalloc_sockaddr2dotted */
+ }
+
+ if (use_proxy || !target.is_ftp) {
+ /*
+ * HTTP session
+ */
+ do {
+ got_clen = chunked = 0;
+
+ if (!--try)
+ bb_error_msg_and_die("too many redirections");
+
+ /*
+ * Open socket to http server
+ */
+ if (sfp) fclose(sfp);
+ sfp = open_socket(lsa);
+
+ /*
+ * Send HTTP request.
+ */
+ if (use_proxy) {
+ fprintf(sfp, "GET %stp://%s/%s HTTP/1.1\r\n",
+ target.is_ftp ? "f" : "ht", target.host,
+ target.path);
+ } else {
+ fprintf(sfp, "GET /%s HTTP/1.1\r\n", target.path);
+ }
+
+ fprintf(sfp, "Host: %s\r\nUser-Agent: %s\r\n",
+ target.host, user_agent);
+
+#if ENABLE_FEATURE_WGET_AUTHENTICATION
+ if (target.user) {
+ fprintf(sfp, "Authorization: Basic %s\r\n",
+ base64enc((unsigned char*)target.user, buf, sizeof(buf)));
+ }
+ if (use_proxy && server.user) {
+ fprintf(sfp, "Proxy-Authorization: Basic %s\r\n",
+ base64enc((unsigned char*)server.user, buf, sizeof(buf)));
+ }
+#endif
+
+ if (beg_range)
+ fprintf(sfp, "Range: bytes=%"OFF_FMT"d-\r\n", beg_range);
+#if ENABLE_FEATURE_WGET_LONG_OPTIONS
+ if (extra_headers)
+ fputs(extra_headers, sfp);
+#endif
+ fprintf(sfp, "Connection: close\r\n\r\n");
+
+ /*
+ * Retrieve HTTP response line and check for "200" status code.
+ */
+ read_response:
+ if (fgets(buf, sizeof(buf), sfp) == NULL)
+ bb_error_msg_and_die("no response from server");
+
+ s = buf;
+ while (*s != '\0' && !isspace(*s)) ++s;
+ s = skip_whitespace(s);
+ // FIXME: no error check
+ // xatou wouldn't work: "200 OK"
+ status = atoi(s);
+ switch (status) {
+ case 0:
+ case 100:
+ while (gethdr(buf, sizeof(buf), sfp, &n) != NULL)
+ /* eat all remaining headers */;
+ goto read_response;
+ case 200:
+ break;
+ case 300: /* redirection */
+ case 301:
+ case 302:
+ case 303:
+ break;
+ case 206:
+ if (beg_range)
+ break;
+ /*FALLTHRU*/
+ default:
+ /* Show first line only and kill any ESC tricks */
+ buf[strcspn(buf, "\n\r\x1b")] = '\0';
+ bb_error_msg_and_die("server returned error: %s", buf);
+ }
+
+ /*
+ * Retrieve HTTP headers.
+ */
+ while ((s = gethdr(buf, sizeof(buf), sfp, &n)) != NULL) {
+ if (strcasecmp(buf, "content-length") == 0) {
+ content_len = BB_STRTOOFF(s, NULL, 10);
+ if (errno || content_len < 0) {
+ bb_error_msg_and_die("content-length %s is garbage", s);
+ }
+ got_clen = 1;
+ continue;
+ }
+ if (strcasecmp(buf, "transfer-encoding") == 0) {
+ if (strcasecmp(s, "chunked") != 0)
+ bb_error_msg_and_die("server wants to do %s transfer encoding", s);
+ chunked = got_clen = 1;
+ }
+ if (strcasecmp(buf, "location") == 0) {
+ if (s[0] == '/')
+ /* free(target.allocated); */
+ target.path = /* target.allocated = */ xstrdup(s+1);
+ else {
+ parse_url(s, &target);
+ if (use_proxy == 0) {
+ server.host = target.host;
+ server.port = target.port;
+ }
+ free(lsa);
+ lsa = xhost2sockaddr(server.host, server.port);
+ break;
+ }
+ }
+ }
+ } while (status >= 300);
+
+ dfp = sfp;
+
+ } else {
+
+ /*
+ * FTP session
+ */
+ if (!target.user)
+ target.user = xstrdup("anonymous:busybox@");
+
+ sfp = open_socket(lsa);
+ if (ftpcmd(NULL, NULL, sfp, buf) != 220)
+ bb_error_msg_and_die("%s", buf+4);
+
+ /*
+ * Splitting username:password pair,
+ * trying to log in
+ */
+ s = strchr(target.user, ':');
+ if (s)
+ *(s++) = '\0';
+ switch (ftpcmd("USER ", target.user, sfp, buf)) {
+ case 230:
+ break;
+ case 331:
+ if (ftpcmd("PASS ", s, sfp, buf) == 230)
+ break;
+ /* FALLTHRU (failed login) */
+ default:
+ bb_error_msg_and_die("ftp login: %s", buf+4);
+ }
+
+ ftpcmd("TYPE I", NULL, sfp, buf);
+
+ /*
+ * Querying file size
+ */
+ if (ftpcmd("SIZE ", target.path, sfp, buf) == 213) {
+ content_len = BB_STRTOOFF(buf+4, NULL, 10);
+ if (errno || content_len < 0) {
+ bb_error_msg_and_die("SIZE value is garbage");
+ }
+ got_clen = 1;
+ }
+
+ /*
+ * Entering passive mode
+ */
+ if (ftpcmd("PASV", NULL, sfp, buf) != 227) {
+ pasv_error:
+ bb_error_msg_and_die("bad response to %s: %s", "PASV", buf);
+ }
+ // Response is "227 garbageN1,N2,N3,N4,P1,P2[)garbage]
+ // Server's IP is N1.N2.N3.N4 (we ignore it)
+ // Server's port for data connection is P1*256+P2
+ s = strrchr(buf, ')');
+ if (s) s[0] = '\0';
+ s = strrchr(buf, ',');
+ if (!s) goto pasv_error;
+ port = xatou_range(s+1, 0, 255);
+ *s = '\0';
+ s = strrchr(buf, ',');
+ if (!s) goto pasv_error;
+ port += xatou_range(s+1, 0, 255) * 256;
+ set_nport(lsa, htons(port));
+ dfp = open_socket(lsa);
+
+ if (beg_range) {
+ sprintf(buf, "REST %"OFF_FMT"d", beg_range);
+ if (ftpcmd(buf, NULL, sfp, buf) == 350)
+ content_len -= beg_range;
+ }
+
+ if (ftpcmd("RETR ", target.path, sfp, buf) > 150)
+ bb_error_msg_and_die("bad response to RETR: %s", buf);
+ }
+
+
+ /*
+ * Retrieve file
+ */
+ if (chunked) {
+ fgets(buf, sizeof(buf), dfp);
+ content_len = STRTOOFF(buf, NULL, 16);
+ /* FIXME: error check?? */
+ }
+
+ /* Do it before progressmeter (want to have nice error message) */
+ if (output_fd < 0)
+ output_fd = xopen(fname_out,
+ O_WRONLY|O_CREAT|O_EXCL|O_TRUNC);
+
+ if (!(opt & WGET_OPT_QUIET))
+ progressmeter(-1);
+
+ do {
+ while (content_len > 0 || !got_clen) {
+ unsigned rdsz = sizeof(buf);
+ if (content_len < sizeof(buf) && (chunked || got_clen))
+ rdsz = (unsigned)content_len;
+ n = safe_fread(buf, 1, rdsz, dfp);
+ if (n <= 0)
+ break;
+ if (full_write(output_fd, buf, n) != n) {
+ bb_perror_msg_and_die(bb_msg_write_error);
+ }
+#if ENABLE_FEATURE_WGET_STATUSBAR
+ transferred += n;
+#endif
+ if (got_clen) {
+ content_len -= n;
+ }
+ }
+
+ if (chunked) {
+ safe_fgets(buf, sizeof(buf), dfp); /* This is a newline */
+ safe_fgets(buf, sizeof(buf), dfp);
+ content_len = STRTOOFF(buf, NULL, 16);
+ /* FIXME: error check? */
+ if (content_len == 0) {
+ chunked = 0; /* all done! */
+ }
+ }
+
+ if (n == 0 && ferror(dfp)) {
+ bb_perror_msg_and_die(bb_msg_read_error);
+ }
+ } while (chunked);
+
+ if (!(opt & WGET_OPT_QUIET))
+ progressmeter(1);
+
+ if ((use_proxy == 0) && target.is_ftp) {
+ fclose(dfp);
+ if (ftpcmd(NULL, NULL, sfp, buf) != 226)
+ bb_error_msg_and_die("ftp error: %s", buf+4);
+ ftpcmd("QUIT", NULL, sfp, buf);
+ }
+ exit(EXIT_SUCCESS);
+}
+
+
+static void parse_url(char *src_url, struct host_info *h)
+{
+ char *url, *p, *sp;
+
+ /* h->allocated = */ url = xstrdup(src_url);
+
+ if (strncmp(url, "http://", 7) == 0) {
+ h->port = bb_lookup_port("http", "tcp", 80);
+ h->host = url + 7;
+ h->is_ftp = 0;
+ } else if (strncmp(url, "ftp://", 6) == 0) {
+ h->port = bb_lookup_port("ftp", "tcp", 21);
+ h->host = url + 6;
+ h->is_ftp = 1;
+ } else
+ bb_error_msg_and_die("not an http or ftp url: %s", url);
+
+ // FYI:
+ // "Real" wget 'http://busybox.net?var=a/b' sends this request:
+ // 'GET /?var=a/b HTTP 1.0'
+ // and saves 'index.html?var=a%2Fb' (we save 'b')
+ // wget 'http://busybox.net?login=john@doe':
+ // request: 'GET /?login=john@doe HTTP/1.0'
+ // saves: 'index.html?login=john@doe' (we save '?login=john@doe')
+ // wget 'http://busybox.net#test/test':
+ // request: 'GET / HTTP/1.0'
+ // saves: 'index.html' (we save 'test')
+ //
+ // We also don't add unique .N suffix if file exists...
+ sp = strchr(h->host, '/');
+ p = strchr(h->host, '?'); if (!sp || (p && sp > p)) sp = p;
+ p = strchr(h->host, '#'); if (!sp || (p && sp > p)) sp = p;
+ if (!sp) {
+ /* must be writable because of bb_get_last_path_component() */
+ static char nullstr[] = "";
+ h->path = nullstr;
+ } else if (*sp == '/') {
+ *sp = '\0';
+ h->path = sp + 1;
+ } else { // '#' or '?'
+ // http://busybox.net?login=john@doe is a valid URL
+ // memmove converts to:
+ // http:/busybox.nett?login=john@doe...
+ memmove(h->host-1, h->host, sp - h->host);
+ h->host--;
+ sp[-1] = '\0';
+ h->path = sp;
+ }
+
+ sp = strrchr(h->host, '@');
+ h->user = NULL;
+ if (sp != NULL) {
+ h->user = h->host;
+ *sp = '\0';
+ h->host = sp + 1;
+ }
+
+ sp = h->host;
+}
+
+
+static FILE *open_socket(len_and_sockaddr *lsa)
+{
+ FILE *fp;
+
+ /* glibc 2.4 seems to try seeking on it - ??! */
+ /* hopefully it understands what ESPIPE means... */
+ fp = fdopen(xconnect_stream(lsa), "r+");
+ if (fp == NULL)
+ bb_perror_msg_and_die("fdopen");
+
+ return fp;
+}
+
+
+static char *gethdr(char *buf, size_t bufsiz, FILE *fp, int *istrunc)
+{
+ char *s, *hdrval;
+ int c;
+
+ *istrunc = 0;
+
+ /* retrieve header line */
+ if (fgets(buf, bufsiz, fp) == NULL)
+ return NULL;
+
+ /* see if we are at the end of the headers */
+ for (s = buf; *s == '\r'; ++s)
+ ;
+ if (s[0] == '\n')
+ return NULL;
+
+ /* convert the header name to lower case */
+ for (s = buf; isalnum(*s) || *s == '-'; ++s)
+ *s = tolower(*s);
+
+ /* verify we are at the end of the header name */
+ if (*s != ':')
+ bb_error_msg_and_die("bad header line: %s", buf);
+
+ /* locate the start of the header value */
+ for (*s++ = '\0'; *s == ' ' || *s == '\t'; ++s)
+ ;
+ hdrval = s;
+
+ /* locate the end of header */
+ while (*s != '\0' && *s != '\r' && *s != '\n')
+ ++s;
+
+ /* end of header found */
+ if (*s != '\0') {
+ *s = '\0';
+ return hdrval;
+ }
+
+ /* Rats! The buffer isn't big enough to hold the entire header value. */
+ while (c = getc(fp), c != EOF && c != '\n')
+ ;
+ *istrunc = 1;
+ return hdrval;
+}
+
+static int ftpcmd(const char *s1, const char *s2, FILE *fp, char *buf)
+{
+ int result;
+ if (s1) {
+ if (!s2) s2 = "";
+ fprintf(fp, "%s%s\r\n", s1, s2);
+ fflush(fp);
+ }
+
+ do {
+ char *buf_ptr;
+
+ if (fgets(buf, 510, fp) == NULL) {
+ bb_perror_msg_and_die("error getting response");
+ }
+ buf_ptr = strstr(buf, "\r\n");
+ if (buf_ptr) {
+ *buf_ptr = '\0';
+ }
+ } while (!isdigit(buf[0]) || buf[3] != ' ');
+
+ buf[3] = '\0';
+ result = xatoi_u(buf);
+ buf[3] = ' ';
+ return result;
+}
+
+#if ENABLE_FEATURE_WGET_STATUSBAR
+/* Stuff below is from BSD rcp util.c, as added to openshh.
+ * Original copyright notice is retained at the end of this file.
+ */
+static int
+getttywidth(void)
+{
+ int width;
+ get_terminal_width_height(0, &width, NULL);
+ return width;
+}
+
+static void
+updateprogressmeter(int ignore)
+{
+ int save_errno = errno;
+
+ progressmeter(0);
+ errno = save_errno;
+}
+
+static void alarmtimer(int iwait)
+{
+ struct itimerval itv;
+
+ itv.it_value.tv_sec = iwait;
+ itv.it_value.tv_usec = 0;
+ itv.it_interval = itv.it_value;
+ setitimer(ITIMER_REAL, &itv, NULL);
+}
+
+
+static void
+progressmeter(int flag)
+{
+ static struct timeval lastupdate;
+ static off_t lastsize, totalsize;
+
+ struct timeval now, td, tvwait;
+ off_t abbrevsize;
+ int elapsed, ratio, barlength, i;
+ char buf[256];
+
+ if (flag == -1) { /* first call to progressmeter */
+ gettimeofday(&start, (struct timezone *) 0);
+ lastupdate = start;
+ lastsize = 0;
+ totalsize = content_len + beg_range; /* as content_len changes.. */
+ }
+
+ gettimeofday(&now, (struct timezone *) 0);
+ ratio = 100;
+ if (totalsize != 0 && !chunked) {
+ /* long long helps to have working ETA even if !LFS */
+ ratio = (int) (100 * (unsigned long long)(transferred+beg_range) / totalsize);
+ ratio = MIN(ratio, 100);
+ }
+
+ fprintf(stderr, "\r%-20.20s%4d%% ", curfile, ratio);
+
+ barlength = getttywidth() - 51;
+ if (barlength > 0 && barlength < sizeof(buf)) {
+ i = barlength * ratio / 100;
+ memset(buf, '*', i);
+ memset(buf + i, ' ', barlength - i);
+ buf[barlength] = '\0';
+ fprintf(stderr, "|%s|", buf);
+ }
+ i = 0;
+ abbrevsize = transferred + beg_range;
+ while (abbrevsize >= 100000) {
+ i++;
+ abbrevsize >>= 10;
+ }
+ /* see http://en.wikipedia.org/wiki/Tera */
+ fprintf(stderr, "%6d %c%c ", (int)abbrevsize, " KMGTPEZY"[i], i?'B':' ');
+
+ timersub(&now, &lastupdate, &tvwait);
+ if (transferred > lastsize) {
+ lastupdate = now;
+ lastsize = transferred;
+ if (tvwait.tv_sec >= STALLTIME)
+ timeradd(&start, &tvwait, &start);
+ tvwait.tv_sec = 0;
+ }
+ timersub(&now, &start, &td);
+ elapsed = td.tv_sec;
+
+ if (tvwait.tv_sec >= STALLTIME) {
+ fprintf(stderr, " - stalled -");
+ } else {
+ off_t to_download = totalsize - beg_range;
+ if (transferred <= 0 || elapsed <= 0 || transferred > to_download || chunked) {
+ fprintf(stderr, "--:--:-- ETA");
+ } else {
+ /* to_download / (transferred/elapsed) - elapsed: */
+ int eta = (int) ((unsigned long long)to_download*elapsed/transferred - elapsed);
+ /* (long long helps to have working ETA even if !LFS) */
+ i = eta % 3600;
+ fprintf(stderr, "%02d:%02d:%02d ETA", eta / 3600, i / 60, i % 60);
+ }
+ }
+
+ if (flag == -1) { /* first call to progressmeter */
+ struct sigaction sa;
+ sa.sa_handler = updateprogressmeter;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_RESTART;
+ sigaction(SIGALRM, &sa, NULL);
+ alarmtimer(1);
+ } else if (flag == 1) { /* last call to progressmeter */
+ alarmtimer(0);
+ transferred = 0;
+ putc('\n', stderr);
+ }
+}
+#endif
+
+/* Original copyright notice which applies to the CONFIG_FEATURE_WGET_STATUSBAR stuff,
+ * much of which was blatantly stolen from openssh. */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change
+ * ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>
+ *
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
diff --git a/i/pc104/initrd/conf/busybox/networking/zcip.c b/i/pc104/initrd/conf/busybox/networking/zcip.c
new file mode 100644
index 0000000..a8bfee6
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/networking/zcip.c
@@ -0,0 +1,548 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * RFC3927 ZeroConf IPv4 Link-Local addressing
+ * (see <http://www.zeroconf.org/>)
+ *
+ * Copyright (C) 2003 by Arthur van Hoff (avh@strangeberry.com)
+ * Copyright (C) 2004 by David Brownell
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ */
+
+/*
+ * ZCIP just manages the 169.254.*.* addresses. That network is not
+ * routed at the IP level, though various proxies or bridges can
+ * certainly be used. Its naming is built over multicast DNS.
+ */
+
+//#define DEBUG
+
+// TODO:
+// - more real-world usage/testing, especially daemon mode
+// - kernel packet filters to reduce scheduling noise
+// - avoid silent script failures, especially under load...
+// - link status monitoring (restart on link-up; stop on link-down)
+
+#include "busybox.h"
+#include <syslog.h>
+#include <poll.h>
+#include <sys/wait.h>
+#include <netinet/ether.h>
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+
+#include <linux/if_packet.h>
+#include <linux/sockios.h>
+
+
+struct arp_packet {
+ struct ether_header hdr;
+ struct ether_arp arp;
+} ATTRIBUTE_PACKED;
+
+enum {
+/* 169.254.0.0 */
+ LINKLOCAL_ADDR = 0xa9fe0000,
+
+/* protocol timeout parameters, specified in seconds */
+ PROBE_WAIT = 1,
+ PROBE_MIN = 1,
+ PROBE_MAX = 2,
+ PROBE_NUM = 3,
+ MAX_CONFLICTS = 10,
+ RATE_LIMIT_INTERVAL = 60,
+ ANNOUNCE_WAIT = 2,
+ ANNOUNCE_NUM = 2,
+ ANNOUNCE_INTERVAL = 2,
+ DEFEND_INTERVAL = 10
+};
+
+/* States during the configuration process. */
+enum {
+ PROBE = 0,
+ RATE_LIMIT_PROBE,
+ ANNOUNCE,
+ MONITOR,
+ DEFEND
+};
+
+#define VDBG(fmt,args...) \
+ do { } while (0)
+
+static unsigned opts;
+#define FOREGROUND (opts & 1)
+#define QUIT (opts & 2)
+
+/**
+ * Pick a random link local IP address on 169.254/16, except that
+ * the first and last 256 addresses are reserved.
+ */
+static void pick(struct in_addr *ip)
+{
+ unsigned tmp;
+
+ /* use cheaper math than lrand48() mod N */
+ do {
+ tmp = (lrand48() >> 16) & IN_CLASSB_HOST;
+ } while (tmp > (IN_CLASSB_HOST - 0x0200));
+ ip->s_addr = htonl((LINKLOCAL_ADDR + 0x0100) + tmp);
+}
+
+/* TODO: we need a flag to direct bb_[p]error_msg output to stderr. */
+
+/**
+ * Broadcast an ARP packet.
+ */
+static void arp(int fd, struct sockaddr *saddr, int op,
+ const struct ether_addr *source_addr, struct in_addr source_ip,
+ const struct ether_addr *target_addr, struct in_addr target_ip)
+{
+ struct arp_packet p;
+ memset(&p, 0, sizeof(p));
+
+ // ether header
+ p.hdr.ether_type = htons(ETHERTYPE_ARP);
+ memcpy(p.hdr.ether_shost, source_addr, ETH_ALEN);
+ memset(p.hdr.ether_dhost, 0xff, ETH_ALEN);
+
+ // arp request
+ p.arp.arp_hrd = htons(ARPHRD_ETHER);
+ p.arp.arp_pro = htons(ETHERTYPE_IP);
+ p.arp.arp_hln = ETH_ALEN;
+ p.arp.arp_pln = 4;
+ p.arp.arp_op = htons(op);
+ memcpy(&p.arp.arp_sha, source_addr, ETH_ALEN);
+ memcpy(&p.arp.arp_spa, &source_ip, sizeof(p.arp.arp_spa));
+ memcpy(&p.arp.arp_tha, target_addr, ETH_ALEN);
+ memcpy(&p.arp.arp_tpa, &target_ip, sizeof(p.arp.arp_tpa));
+
+ // send it
+ if (sendto(fd, &p, sizeof(p), 0, saddr, sizeof(*saddr)) < 0) {
+ bb_perror_msg("sendto");
+ //return -errno;
+ }
+ // Currently all callers ignore errors, that's why returns are
+ // commented out...
+ //return 0;
+}
+
+/**
+ * Run a script.
+ */
+static int run(const char *script, const char *arg, const char *intf, struct in_addr *ip)
+{
+ int pid, status;
+ const char *why;
+
+ if(1) { //always true: if (script != NULL)
+ VDBG("%s run %s %s\n", intf, script, arg);
+ if (ip != NULL) {
+ char *addr = inet_ntoa(*ip);
+ setenv("ip", addr, 1);
+ bb_info_msg("%s %s %s", arg, intf, addr);
+ }
+
+ pid = vfork();
+ if (pid < 0) { // error
+ why = "vfork";
+ goto bad;
+ } else if (pid == 0) { // child
+ execl(script, script, arg, NULL);
+ bb_perror_msg("execl");
+ _exit(EXIT_FAILURE);
+ }
+
+ if (waitpid(pid, &status, 0) <= 0) {
+ why = "waitpid";
+ goto bad;
+ }
+ if (WEXITSTATUS(status) != 0) {
+ bb_error_msg("script %s failed, exit=%d",
+ script, WEXITSTATUS(status));
+ return -errno;
+ }
+ }
+ return 0;
+bad:
+ status = -errno;
+ bb_perror_msg("%s %s, %s", arg, intf, why);
+ return status;
+}
+
+
+/**
+ * Return milliseconds of random delay, up to "secs" seconds.
+ */
+static unsigned ATTRIBUTE_ALWAYS_INLINE ms_rdelay(unsigned secs)
+{
+ return lrand48() % (secs * 1000);
+}
+
+/**
+ * main program
+ */
+
+/* Used to be auto variables on main() stack, but
+ * most of them were zero-inited. Moving them to bss
+ * is more space-efficient.
+ */
+static const struct in_addr null_ip; // = { 0 };
+static const struct ether_addr null_addr; // = { {0, 0, 0, 0, 0, 0} };
+
+static struct sockaddr saddr; // memset(0);
+static struct in_addr ip; // = { 0 };
+static struct ifreq ifr; //memset(0);
+
+static char *intf; // = NULL;
+static char *script; // = NULL;
+static suseconds_t timeout; // = 0; // milliseconds
+static unsigned conflicts; // = 0;
+static unsigned nprobes; // = 0;
+static unsigned nclaims; // = 0;
+static int ready; // = 0;
+static int verbose; // = 0;
+static int state = PROBE;
+
+int zcip_main(int argc, char *argv[]);
+int zcip_main(int argc, char *argv[])
+{
+ struct ether_addr eth_addr;
+ const char *why;
+ int fd;
+
+ // parse commandline: prog [options] ifname script
+ char *r_opt;
+ opt_complementary = "vv:vf"; // -v accumulates and implies -f
+ opts = getopt32(argc, argv, "fqr:v", &r_opt, &verbose);
+ if (!FOREGROUND) {
+ /* Do it early, before all bb_xx_msg calls */
+ logmode = LOGMODE_SYSLOG;
+ openlog(applet_name, 0, LOG_DAEMON);
+ }
+ if (opts & 4) { // -r n.n.n.n
+ if (inet_aton(r_opt, &ip) == 0
+ || (ntohl(ip.s_addr) & IN_CLASSB_NET) != LINKLOCAL_ADDR
+ ) {
+ bb_error_msg_and_die("invalid link address");
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc != 2)
+ bb_show_usage();
+ intf = argv[0];
+ script = argv[1];
+ setenv("interface", intf, 1);
+
+ // initialize the interface (modprobe, ifup, etc)
+ if (run(script, "init", intf, NULL) < 0)
+ return EXIT_FAILURE;
+
+ // initialize saddr
+ //memset(&saddr, 0, sizeof(saddr));
+ safe_strncpy(saddr.sa_data, intf, sizeof(saddr.sa_data));
+
+ // open an ARP socket
+ fd = xsocket(PF_PACKET, SOCK_PACKET, htons(ETH_P_ARP));
+ // bind to the interface's ARP socket
+ xbind(fd, &saddr, sizeof(saddr));
+
+ // get the interface's ethernet address
+ //memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, intf, sizeof(ifr.ifr_name));
+ if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) {
+ bb_perror_msg_and_die("get ethernet address");
+ }
+ memcpy(&eth_addr, &ifr.ifr_hwaddr.sa_data, ETH_ALEN);
+
+ // start with some stable ip address, either a function of
+ // the hardware address or else the last address we used.
+ // NOTE: the sequence of addresses we try changes only
+ // depending on when we detect conflicts.
+ // (SVID 3 bogon: who says that "short" is always 16 bits?)
+ seed48( (unsigned short*)&ifr.ifr_hwaddr.sa_data );
+ if (ip.s_addr == 0)
+ pick(&ip);
+
+ // FIXME cases to handle:
+ // - zcip already running!
+ // - link already has local address... just defend/update
+
+ // daemonize now; don't delay system startup
+ if (!FOREGROUND) {
+ /* bb_daemonize(); - bad, will close fd! */
+ xdaemon(0, 0);
+ bb_info_msg("start, interface %s", intf);
+ }
+
+ // run the dynamic address negotiation protocol,
+ // restarting after address conflicts:
+ // - start with some address we want to try
+ // - short random delay
+ // - arp probes to see if another host else uses it
+ // - arp announcements that we're claiming it
+ // - use it
+ // - defend it, within limits
+ while (1) {
+ struct pollfd fds[1];
+ struct timeval tv1;
+ struct arp_packet p;
+
+ int source_ip_conflict = 0;
+ int target_ip_conflict = 0;
+
+ fds[0].fd = fd;
+ fds[0].events = POLLIN;
+ fds[0].revents = 0;
+
+ // poll, being ready to adjust current timeout
+ if (!timeout) {
+ timeout = ms_rdelay(PROBE_WAIT);
+ // FIXME setsockopt(fd, SO_ATTACH_FILTER, ...) to
+ // make the kernel filter out all packets except
+ // ones we'd care about.
+ }
+ // set tv1 to the point in time when we timeout
+ gettimeofday(&tv1, NULL);
+ tv1.tv_usec += (timeout % 1000) * 1000;
+ while (tv1.tv_usec > 1000000) {
+ tv1.tv_usec -= 1000000;
+ tv1.tv_sec++;
+ }
+ tv1.tv_sec += timeout / 1000;
+
+ VDBG("...wait %ld %s nprobes=%d, nclaims=%d\n",
+ timeout, intf, nprobes, nclaims);
+ switch (poll(fds, 1, timeout)) {
+
+ // timeout
+ case 0:
+ VDBG("state = %d\n", state);
+ switch (state) {
+ case PROBE:
+ // timeouts in the PROBE state mean no conflicting ARP packets
+ // have been received, so we can progress through the states
+ if (nprobes < PROBE_NUM) {
+ nprobes++;
+ VDBG("probe/%d %s@%s\n",
+ nprobes, intf, inet_ntoa(ip));
+ arp(fd, &saddr, ARPOP_REQUEST,
+ &eth_addr, null_ip,
+ &null_addr, ip);
+ timeout = PROBE_MIN * 1000;
+ timeout += ms_rdelay(PROBE_MAX
+ - PROBE_MIN);
+ }
+ else {
+ // Switch to announce state.
+ state = ANNOUNCE;
+ nclaims = 0;
+ VDBG("announce/%d %s@%s\n",
+ nclaims, intf, inet_ntoa(ip));
+ arp(fd, &saddr, ARPOP_REQUEST,
+ &eth_addr, ip,
+ &eth_addr, ip);
+ timeout = ANNOUNCE_INTERVAL * 1000;
+ }
+ break;
+ case RATE_LIMIT_PROBE:
+ // timeouts in the RATE_LIMIT_PROBE state mean no conflicting ARP packets
+ // have been received, so we can move immediately to the announce state
+ state = ANNOUNCE;
+ nclaims = 0;
+ VDBG("announce/%d %s@%s\n",
+ nclaims, intf, inet_ntoa(ip));
+ arp(fd, &saddr, ARPOP_REQUEST,
+ &eth_addr, ip,
+ &eth_addr, ip);
+ timeout = ANNOUNCE_INTERVAL * 1000;
+ break;
+ case ANNOUNCE:
+ // timeouts in the ANNOUNCE state mean no conflicting ARP packets
+ // have been received, so we can progress through the states
+ if (nclaims < ANNOUNCE_NUM) {
+ nclaims++;
+ VDBG("announce/%d %s@%s\n",
+ nclaims, intf, inet_ntoa(ip));
+ arp(fd, &saddr, ARPOP_REQUEST,
+ &eth_addr, ip,
+ &eth_addr, ip);
+ timeout = ANNOUNCE_INTERVAL * 1000;
+ }
+ else {
+ // Switch to monitor state.
+ state = MONITOR;
+ // link is ok to use earlier
+ // FIXME update filters
+ run(script, "config", intf, &ip);
+ ready = 1;
+ conflicts = 0;
+ timeout = -1; // Never timeout in the monitor state.
+
+ // NOTE: all other exit paths
+ // should deconfig ...
+ if (QUIT)
+ return EXIT_SUCCESS;
+ }
+ break;
+ case DEFEND:
+ // We won! No ARP replies, so just go back to monitor.
+ state = MONITOR;
+ timeout = -1;
+ conflicts = 0;
+ break;
+ default:
+ // Invalid, should never happen. Restart the whole protocol.
+ state = PROBE;
+ pick(&ip);
+ timeout = 0;
+ nprobes = 0;
+ nclaims = 0;
+ break;
+ } // switch (state)
+ break; // case 0 (timeout)
+ // packets arriving
+ case 1:
+ // We need to adjust the timeout in case we didn't receive
+ // a conflicting packet.
+ if (timeout > 0) {
+ struct timeval tv2;
+
+ gettimeofday(&tv2, NULL);
+ if (timercmp(&tv1, &tv2, <)) {
+ // Current time is greater than the expected timeout time.
+ // Should never happen.
+ VDBG("missed an expected timeout\n");
+ timeout = 0;
+ } else {
+ VDBG("adjusting timeout\n");
+ timersub(&tv1, &tv2, &tv1);
+ timeout = 1000 * tv1.tv_sec
+ + tv1.tv_usec / 1000;
+ }
+ }
+
+ if ((fds[0].revents & POLLIN) == 0) {
+ if (fds[0].revents & POLLERR) {
+ // FIXME: links routinely go down;
+ // this shouldn't necessarily exit.
+ bb_error_msg("%s: poll error", intf);
+ if (ready) {
+ run(script, "deconfig",
+ intf, &ip);
+ }
+ return EXIT_FAILURE;
+ }
+ continue;
+ }
+
+ // read ARP packet
+ if (recv(fd, &p, sizeof(p), 0) < 0) {
+ why = "recv";
+ goto bad;
+ }
+ if (p.hdr.ether_type != htons(ETHERTYPE_ARP))
+ continue;
+
+#ifdef DEBUG
+ {
+ struct ether_addr * sha = (struct ether_addr *) p.arp.arp_sha;
+ struct ether_addr * tha = (struct ether_addr *) p.arp.arp_tha;
+ struct in_addr * spa = (struct in_addr *) p.arp.arp_spa;
+ struct in_addr * tpa = (struct in_addr *) p.arp.arp_tpa;
+ VDBG("%s recv arp type=%d, op=%d,\n",
+ intf, ntohs(p.hdr.ether_type),
+ ntohs(p.arp.arp_op));
+ VDBG("\tsource=%s %s\n",
+ ether_ntoa(sha),
+ inet_ntoa(*spa));
+ VDBG("\ttarget=%s %s\n",
+ ether_ntoa(tha),
+ inet_ntoa(*tpa));
+ }
+#endif
+ if (p.arp.arp_op != htons(ARPOP_REQUEST)
+ && p.arp.arp_op != htons(ARPOP_REPLY))
+ continue;
+
+ if (memcmp(p.arp.arp_spa, &ip.s_addr, sizeof(struct in_addr)) == 0 &&
+ memcmp(&eth_addr, &p.arp.arp_sha, ETH_ALEN) != 0) {
+ source_ip_conflict = 1;
+ }
+ if (memcmp(p.arp.arp_tpa, &ip.s_addr, sizeof(struct in_addr)) == 0 &&
+ p.arp.arp_op == htons(ARPOP_REQUEST) &&
+ memcmp(&eth_addr, &p.arp.arp_tha, ETH_ALEN) != 0) {
+ target_ip_conflict = 1;
+ }
+
+ VDBG("state = %d, source ip conflict = %d, target ip conflict = %d\n",
+ state, source_ip_conflict, target_ip_conflict);
+ switch (state) {
+ case PROBE:
+ case ANNOUNCE:
+ // When probing or announcing, check for source IP conflicts
+ // and other hosts doing ARP probes (target IP conflicts).
+ if (source_ip_conflict || target_ip_conflict) {
+ conflicts++;
+ if (conflicts >= MAX_CONFLICTS) {
+ VDBG("%s ratelimit\n", intf);
+ timeout = RATE_LIMIT_INTERVAL * 1000;
+ state = RATE_LIMIT_PROBE;
+ }
+
+ // restart the whole protocol
+ pick(&ip);
+ timeout = 0;
+ nprobes = 0;
+ nclaims = 0;
+ }
+ break;
+ case MONITOR:
+ // If a conflict, we try to defend with a single ARP probe.
+ if (source_ip_conflict) {
+ VDBG("monitor conflict -- defending\n");
+ state = DEFEND;
+ timeout = DEFEND_INTERVAL * 1000;
+ arp(fd, &saddr,
+ ARPOP_REQUEST,
+ &eth_addr, ip,
+ &eth_addr, ip);
+ }
+ break;
+ case DEFEND:
+ // Well, we tried. Start over (on conflict).
+ if (source_ip_conflict) {
+ state = PROBE;
+ VDBG("defend conflict -- starting over\n");
+ ready = 0;
+ run(script, "deconfig", intf, &ip);
+
+ // restart the whole protocol
+ pick(&ip);
+ timeout = 0;
+ nprobes = 0;
+ nclaims = 0;
+ }
+ break;
+ default:
+ // Invalid, should never happen. Restart the whole protocol.
+ VDBG("invalid state -- starting over\n");
+ state = PROBE;
+ pick(&ip);
+ timeout = 0;
+ nprobes = 0;
+ nclaims = 0;
+ break;
+ } // switch state
+
+ break; // case 1 (packets arriving)
+ default:
+ why = "poll";
+ goto bad;
+ } // switch poll
+ }
+bad:
+ bb_perror_msg("%s, %s", intf, why);
+ return EXIT_FAILURE;
+}
diff --git a/i/pc104/initrd/conf/busybox/procps/Config.in b/i/pc104/initrd/conf/busybox/procps/Config.in
new file mode 100644
index 0000000..e58b89f
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/procps/Config.in
@@ -0,0 +1,121 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+menu "Process Utilities"
+
+config FREE
+ bool "free"
+ default n
+ help
+ free displays the total amount of free and used physical and swap
+ memory in the system, as well as the buffers used by the kernel.
+ The shared memory column should be ignored; it is obsolete.
+
+config FUSER
+ bool "fuser"
+ default n
+ help
+ fuser lists all PIDs (Process IDs) that currently have a given
+ file open. fuser can also list all PIDs that have a given network
+ (TCP or UDP) port open.
+
+config KILL
+ bool "kill"
+ default n
+ help
+ The command kill sends the specified signal to the specified
+ process or process group. If no signal is specified, the TERM
+ signal is sent.
+
+config KILLALL
+ bool "killall"
+ default n
+ depends on KILL
+ help
+ killall sends a signal to all processes running any of the
+ specified commands. If no signal name is specified, SIGTERM is
+ sent.
+
+config KILLALL5
+ bool "killall5"
+ default n
+ depends on KILL
+
+config PIDOF
+ bool "pidof"
+ default n
+ help
+ Pidof finds the process id's (pids) of the named programs. It prints
+ those id's on the standard output.
+
+config FEATURE_PIDOF_SINGLE
+ bool "Enable argument for single shot (-s)"
+ default n
+ depends on PIDOF
+ help
+ Support argument '-s' for returning only the first pid found.
+
+config FEATURE_PIDOF_OMIT
+ bool "Enable argument for omitting pids (-o)"
+ default n
+ depends on PIDOF
+ help
+ Support argument '-o' for omitting the given pids in output.
+ The special pid %PPID can be used to name the parent process
+ of the pidof, in other words the calling shell or shell script.
+
+config PS
+ bool "ps"
+ default n
+ help
+ ps gives a snapshot of the current processes.
+
+config FEATURE_PS_WIDE
+ bool "Enable argument for wide output (-w)"
+ default n
+ depends on PS
+ help
+ Support argument 'w' for wide output.
+ If given once, 132 chars are printed and given more than
+ one, the length is unlimited.
+
+config RENICE
+ bool "renice"
+ default n
+ help
+ Renice alters the scheduling priority of one or more running
+ processes.
+
+config BB_SYSCTL
+ bool "sysctl"
+ default n
+ help
+ Configure kernel parameters at runtime.
+
+config TOP
+ bool "top"
+ default n
+ help
+ The top program provides a dynamic real-time view of a running
+ system.
+
+config FEATURE_TOP_CPU_USAGE_PERCENTAGE
+ bool "Support showing CPU usage percentage (add 2k bytes)"
+ default y
+ depends on TOP
+ help
+ Make top display CPU usage.
+
+config UPTIME
+ bool "uptime"
+ default n
+ help
+ uptime gives a one line display of the current time, how long
+ the system has been running, how many users are currently logged
+ on, and the system load averages for the past 1, 5, and 15 minutes.
+
+
+endmenu
+
diff --git a/i/pc104/initrd/conf/busybox/procps/Kbuild b/i/pc104/initrd/conf/busybox/procps/Kbuild
new file mode 100644
index 0000000..6a9a866
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/procps/Kbuild
@@ -0,0 +1,16 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under the GPL v2, see the file LICENSE in this tarball.
+
+lib-y:=
+lib-$(CONFIG_FREE) += free.o
+lib-$(CONFIG_KILL) += kill.o
+lib-$(CONFIG_PIDOF) += pidof.o
+lib-$(CONFIG_PS) += ps.o
+lib-$(CONFIG_RENICE) += renice.o
+lib-$(CONFIG_BB_SYSCTL) += sysctl.o
+lib-$(CONFIG_TOP) += top.o
+lib-$(CONFIG_UPTIME) += uptime.o
+lib-$(CONFIG_FUSER) += fuser.o
diff --git a/i/pc104/initrd/conf/busybox/procps/free.c b/i/pc104/initrd/conf/busybox/procps/free.c
new file mode 100644
index 0000000..860f573
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/procps/free.c
@@ -0,0 +1,69 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini free implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under the GPL version 2, see the file LICENSE in this tarball.
+ */
+
+/* getopt not needed */
+
+#include "busybox.h"
+
+int free_main(int argc, char **argv);
+int free_main(int argc, char **argv)
+{
+ struct sysinfo info;
+ sysinfo(&info);
+
+ /* Kernels prior to 2.4.x will return info.mem_unit==0, so cope... */
+ if (info.mem_unit==0) {
+ info.mem_unit=1;
+ }
+ if ( info.mem_unit == 1 ) {
+ info.mem_unit=1024;
+
+ /* TODO: Make all this stuff not overflow when mem >= 4 Gib */
+ info.totalram/=info.mem_unit;
+ info.freeram/=info.mem_unit;
+#ifndef __uClinux__
+ info.totalswap/=info.mem_unit;
+ info.freeswap/=info.mem_unit;
+#endif
+ info.sharedram/=info.mem_unit;
+ info.bufferram/=info.mem_unit;
+ } else {
+ info.mem_unit/=1024;
+ /* TODO: Make all this stuff not overflow when mem >= 4 Gib */
+ info.totalram*=info.mem_unit;
+ info.freeram*=info.mem_unit;
+#ifndef __uClinux__
+ info.totalswap*=info.mem_unit;
+ info.freeswap*=info.mem_unit;
+#endif
+ info.sharedram*=info.mem_unit;
+ info.bufferram*=info.mem_unit;
+ }
+
+ if (argc > 1 && **(argv + 1) == '-')
+ bb_show_usage();
+
+ printf("%6s%13s%13s%13s%13s%13s\n", "", "total", "used", "free",
+ "shared", "buffers");
+
+ printf("%6s%13ld%13ld%13ld%13ld%13ld\n", "Mem:", info.totalram,
+ info.totalram-info.freeram, info.freeram,
+ info.sharedram, info.bufferram);
+
+#ifndef __uClinux__
+ printf("%6s%13ld%13ld%13ld\n", "Swap:", info.totalswap,
+ info.totalswap-info.freeswap, info.freeswap);
+
+ printf("%6s%13ld%13ld%13ld\n", "Total:", info.totalram+info.totalswap,
+ (info.totalram-info.freeram)+(info.totalswap-info.freeswap),
+ info.freeram+info.freeswap);
+#endif
+ return EXIT_SUCCESS;
+}
+
diff --git a/i/pc104/initrd/conf/busybox/procps/fuser.c b/i/pc104/initrd/conf/busybox/procps/fuser.c
new file mode 100644
index 0000000..c91ae21
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/procps/fuser.c
@@ -0,0 +1,368 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * tiny fuser implementation
+ *
+ * Copyright 2004 Tony J. White
+ *
+ * May be distributed under the conditions of the
+ * GNU Library General Public License
+ */
+
+#include "busybox.h"
+
+#define FUSER_PROC_DIR "/proc"
+#define FUSER_MAX_LINE 255
+
+#define FUSER_OPT_MOUNT 1
+#define FUSER_OPT_KILL 2
+#define FUSER_OPT_SILENT 4
+#define FUSER_OPT_IP6 8
+#define FUSER_OPT_IP4 16
+
+typedef struct inode_list {
+ ino_t inode;
+ dev_t dev;
+ struct inode_list *next;
+} inode_list;
+
+typedef struct pid_list {
+ pid_t pid;
+ struct pid_list *next;
+} pid_list;
+
+static int fuser_option(char *option)
+{
+ int opt = 0;
+
+ if(!(strlen(option))) return 0;
+ if(option[0] != '-') return 0;
+ ++option;
+ while(*option != '\0') {
+ if(*option == 'm') opt |= FUSER_OPT_MOUNT;
+ else if(*option == 'k') opt |= FUSER_OPT_KILL;
+ else if(*option == 's') opt |= FUSER_OPT_SILENT;
+ else if(*option == '6') opt |= FUSER_OPT_IP6;
+ else if(*option == '4') opt |= FUSER_OPT_IP4;
+ else {
+ bb_error_msg_and_die(
+ "Unsupported option '%c'", *option);
+ }
+ ++option;
+ }
+ return opt;
+}
+
+static int fuser_file_to_dev_inode(const char *filename,
+ dev_t *dev, ino_t *inode)
+{
+ struct stat f_stat;
+ if((stat(filename, &f_stat)) < 0) return 0;
+ *inode = f_stat.st_ino;
+ *dev = f_stat.st_dev;
+ return 1;
+}
+
+static int fuser_find_socket_dev(dev_t *dev)
+{
+ int fd = socket(PF_INET, SOCK_DGRAM,0);
+ struct stat buf;
+
+ if (fd >= 0 && (fstat(fd, &buf)) == 0) {
+ *dev = buf.st_dev;
+ close(fd);
+ return 1;
+ }
+ return 0;
+}
+
+static int fuser_parse_net_arg(const char *filename,
+ const char **proto, int *port)
+{
+ char path[sizeof(FUSER_PROC_DIR)+12], tproto[5];
+
+ if((sscanf(filename, "%d/%4s", port, tproto)) != 2) return 0;
+ sprintf(path, "%s/net/%s", FUSER_PROC_DIR, tproto);
+ if((access(path, R_OK)) != 0) return 0;
+ *proto = xstrdup(tproto);
+ return 1;
+}
+
+static int fuser_add_pid(pid_list *plist, pid_t pid)
+{
+ pid_list *curr = NULL, *last = NULL;
+
+ if(plist->pid == 0) plist->pid = pid;
+ curr = plist;
+ while(curr != NULL) {
+ if(curr->pid == pid) return 1;
+ last = curr;
+ curr = curr->next;
+ }
+ curr = xmalloc(sizeof(pid_list));
+ last->next = curr;
+ curr->pid = pid;
+ curr->next = NULL;
+ return 1;
+}
+
+static int fuser_add_inode(inode_list *ilist, dev_t dev, ino_t inode)
+{
+ inode_list *curr = NULL, *last = NULL;
+
+ if(!ilist->inode && !ilist->dev) {
+ ilist->dev = dev;
+ ilist->inode = inode;
+ }
+ curr = ilist;
+ while(curr != NULL) {
+ if(curr->inode == inode && curr->dev == dev) return 1;
+ last = curr;
+ curr = curr->next;
+ }
+ curr = xmalloc(sizeof(inode_list));
+ last->next = curr;
+ curr->dev = dev;
+ curr->inode = inode;
+ curr->next = NULL;
+ return 1;
+}
+
+static int fuser_scan_proc_net(int opts, const char *proto,
+ int port, inode_list *ilist)
+{
+ char path[sizeof(FUSER_PROC_DIR)+12], line[FUSER_MAX_LINE+1];
+ char addr[128];
+ ino_t tmp_inode;
+ dev_t tmp_dev;
+ long long uint64_inode;
+ int tmp_port;
+ FILE *f;
+
+ if(!fuser_find_socket_dev(&tmp_dev)) tmp_dev = 0;
+ sprintf(path, "%s/net/%s", FUSER_PROC_DIR, proto);
+
+ if (!(f = fopen(path, "r"))) return 0;
+ while(fgets(line, FUSER_MAX_LINE, f)) {
+ if(sscanf(line,
+ "%*d: %64[0-9A-Fa-f]:%x %*x:%*x %*x %*x:%*x "
+ "%*x:%*x %*x %*d %*d %llu",
+ addr, &tmp_port, &uint64_inode) == 3) {
+ if((strlen(addr) == 8) &&
+ (opts & FUSER_OPT_IP6)) continue;
+ else if((strlen(addr) > 8) &&
+ (opts & FUSER_OPT_IP4)) continue;
+ if(tmp_port == port) {
+ tmp_inode = uint64_inode;
+ fuser_add_inode(ilist, tmp_dev, tmp_inode);
+ }
+ }
+
+ }
+ fclose(f);
+ return 1;
+}
+
+static int fuser_search_dev_inode(int opts, inode_list *ilist,
+ dev_t dev, ino_t inode)
+{
+ inode_list *curr;
+ curr = ilist;
+
+ while(curr) {
+ if((opts & FUSER_OPT_MOUNT) && curr->dev == dev)
+ return 1;
+ if(curr->inode == inode && curr->dev == dev)
+ return 1;
+ curr = curr->next;
+ }
+ return 0;
+}
+
+static int fuser_scan_pid_maps(int opts, const char *fname, pid_t pid,
+ inode_list *ilist, pid_list *plist)
+{
+ FILE *file;
+ char line[FUSER_MAX_LINE + 1];
+ int major, minor;
+ ino_t inode;
+ long long uint64_inode;
+ dev_t dev;
+
+ if (!(file = fopen(fname, "r"))) return 0;
+ while (fgets(line, FUSER_MAX_LINE, file)) {
+ if(sscanf(line, "%*s %*s %*s %x:%x %llu",
+ &major, &minor, &uint64_inode) != 3) continue;
+ inode = uint64_inode;
+ if(major == 0 && minor == 0 && inode == 0) continue;
+ dev = makedev(major, minor);
+ if(fuser_search_dev_inode(opts, ilist, dev, inode)) {
+ fuser_add_pid(plist, pid);
+ }
+
+ }
+ fclose(file);
+ return 1;
+}
+
+static int fuser_scan_link(int opts, const char *lname, pid_t pid,
+ inode_list *ilist, pid_list *plist)
+{
+ ino_t inode;
+ dev_t dev;
+
+ if(!fuser_file_to_dev_inode(lname, &dev, &inode)) return 0;
+ if(fuser_search_dev_inode(opts, ilist, dev, inode))
+ fuser_add_pid(plist, pid);
+ return 1;
+}
+
+static int fuser_scan_dir_links(int opts, const char *dname, pid_t pid,
+ inode_list *ilist, pid_list *plist)
+{
+ DIR *d;
+ struct dirent *de;
+ char *lname;
+
+ if((d = opendir(dname))) {
+ while((de = readdir(d)) != NULL) {
+ lname = concat_subpath_file(dname, de->d_name);
+ if(lname == NULL)
+ continue;
+ fuser_scan_link(opts, lname, pid, ilist, plist);
+ free(lname);
+ }
+ closedir(d);
+ }
+ else return 0;
+ return 1;
+
+}
+
+static int fuser_scan_proc_pids(int opts, inode_list *ilist, pid_list *plist)
+{
+ DIR *d;
+ struct dirent *de;
+ pid_t pid;
+ char *dname;
+
+ if(!(d = opendir(FUSER_PROC_DIR))) return 0;
+ while((de = readdir(d)) != NULL) {
+ pid = (pid_t)atoi(de->d_name);
+ if(!pid) continue;
+ dname = concat_subpath_file(FUSER_PROC_DIR, de->d_name);
+ if(chdir(dname) < 0) {
+ free(dname);
+ continue;
+ }
+ free(dname);
+ fuser_scan_link(opts, "cwd", pid, ilist, plist);
+ fuser_scan_link(opts, "exe", pid, ilist, plist);
+ fuser_scan_link(opts, "root", pid, ilist, plist);
+ fuser_scan_dir_links(opts, "fd", pid, ilist, plist);
+ fuser_scan_dir_links(opts, "lib", pid, ilist, plist);
+ fuser_scan_dir_links(opts, "mmap", pid, ilist, plist);
+ fuser_scan_pid_maps(opts, "maps", pid, ilist, plist);
+ chdir("..");
+ }
+ closedir(d);
+ return 1;
+}
+
+static int fuser_print_pid_list(pid_list *plist)
+{
+ pid_list *curr = plist;
+
+ if(plist == NULL) return 0;
+ while(curr != NULL) {
+ if(curr->pid > 0) printf("%d ", curr->pid);
+ curr = curr->next;
+ }
+ puts("");
+ return 1;
+}
+
+static int fuser_kill_pid_list(pid_list *plist, int sig)
+{
+ pid_list *curr = plist;
+ pid_t mypid = getpid();
+ int success = 1;
+
+ if(plist == NULL) return 0;
+ while(curr != NULL) {
+ if(curr->pid > 0 && curr->pid != mypid) {
+ if (kill(curr->pid, sig) != 0) {
+ bb_perror_msg(
+ "cannot kill pid '%d'", curr->pid);
+ success = 0;
+ }
+ }
+ curr = curr->next;
+ }
+ return success;
+}
+
+int fuser_main(int argc, char **argv);
+int fuser_main(int argc, char **argv)
+{
+ int port, i, optn;
+ int* fni; /* file name indexes of argv */
+ int fnic = 0; /* file name index count */
+ const char *proto;
+ static int opt = 0; /* FUSER_OPT_ */
+ dev_t dev;
+ ino_t inode;
+ pid_list *pids;
+ inode_list *inodes;
+ int killsig = SIGTERM;
+ int success = 1;
+
+ if (argc < 2)
+ bb_show_usage();
+
+ fni = xmalloc(sizeof(int));
+ for (i=1;i<argc;i++) {
+ optn = fuser_option(argv[i]);
+ if(optn) opt |= optn;
+ else if(argv[i][0] == '-') {
+ killsig = get_signum(argv[i]+1);
+ if(0 > killsig)
+ killsig = SIGTERM;
+ }
+ else {
+ fni = xrealloc(fni, sizeof(int) * (fnic+2));
+ fni[fnic++] = i;
+ }
+ }
+ if(!fnic) return 1;
+
+ inodes = xmalloc(sizeof(inode_list));
+ for (i=0;i<fnic;i++) {
+ if(fuser_parse_net_arg(argv[fni[i]], &proto, &port)) {
+ fuser_scan_proc_net(opt, proto, port, inodes);
+ }
+ else {
+ if(!fuser_file_to_dev_inode(
+ argv[fni[i]], &dev, &inode)) {
+ if (ENABLE_FEATURE_CLEAN_UP) free(inodes);
+ bb_perror_msg_and_die("cannot open '%s'", argv[fni[i]]);
+ }
+ fuser_add_inode(inodes, dev, inode);
+ }
+ }
+ pids = xmalloc(sizeof(pid_list));
+ success = fuser_scan_proc_pids(opt, inodes, pids);
+ /* if the first pid in the list is 0, none have been found */
+ if(pids->pid == 0) success = 0;
+ if(success) {
+ if(opt & FUSER_OPT_KILL) {
+ success = fuser_kill_pid_list(pids, killsig);
+ }
+ else if(!(opt & FUSER_OPT_SILENT)) {
+ success = fuser_print_pid_list(pids);
+ }
+ }
+ free(pids);
+ free(inodes);
+ /* return 0 on (success == 1) 1 otherwise */
+ return (success != 1);
+}
diff --git a/i/pc104/initrd/conf/busybox/procps/kill.c b/i/pc104/initrd/conf/busybox/procps/kill.c
new file mode 100644
index 0000000..9a6e936
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/procps/kill.c
@@ -0,0 +1,156 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini kill/killall implementation for busybox
+ *
+ * Copyright (C) 1995, 1996 by Bruce Perens <bruce@pixar.com>.
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ */
+
+#include "busybox.h"
+
+int kill_main(int argc, char **argv);
+int kill_main(int argc, char **argv)
+{
+ char *arg;
+ pid_t pid;
+ int signo = SIGTERM, errors = 0, quiet = 0;
+ const int killall = (ENABLE_KILLALL && applet_name[4] == 'a'
+ && (!ENABLE_KILLALL5 || applet_name[7] != '5'));
+ const int killall5 = (ENABLE_KILLALL5 && applet_name[4] == 'a'
+ && (!ENABLE_KILLALL || applet_name[7] == '5'));
+
+ /* Parse any options */
+ argc--;
+ arg = *++argv;
+
+ if (argc < 1 || arg[0] != '-') {
+ goto do_it_now;
+ }
+
+ /* The -l option, which prints out signal names. */
+ if (arg[1] == 'l' && arg[2] == '\0') {
+ const char *name;
+ if (argc == 1) {
+ /* Print the whole signal list */
+ int col = 0;
+ for (signo = 1; signo < 32; signo++) {
+ name = get_signame(signo);
+ if (isdigit(name[0])) continue;
+ if (col > 66) {
+ puts("");
+ col = 0;
+ }
+ col += printf("%2d) %-6s", signo, name);
+ }
+ puts("");
+ } else { /* -l <sig list> */
+ while ((arg = *++argv)) {
+ if (isdigit(arg[0])) {
+ signo = xatoi_u(arg);
+ name = get_signame(signo);
+ } else {
+ signo = get_signum(arg);
+ if (signo < 0)
+ bb_error_msg_and_die("unknown signal '%s'", arg);
+ name = get_signame(signo);
+ }
+ printf("%2d) %s\n", signo, name);
+ }
+ }
+ /* If they specified -l, we are all done */
+ return EXIT_SUCCESS;
+ }
+
+ /* The -q quiet option */
+ if (killall && arg[1] == 'q' && arg[2] == '\0') {
+ quiet = 1;
+ arg = *++argv;
+ argc--;
+ if (argc < 1) bb_show_usage();
+ if (arg[0] != '-') goto do_it_now;
+ }
+
+ /* -SIG */
+ signo = get_signum(&arg[1]);
+ if (signo < 0)
+ bb_error_msg_and_die("bad signal name '%s'", &arg[1]);
+ arg = *++argv;
+ argc--;
+
+do_it_now:
+
+ if (killall5) {
+ pid_t sid;
+ procps_status_t* p = NULL;
+
+// Cannot happen anyway? We don't TERM ourself, we STOP
+// /* kill(-1, sig) on Linux (at least 2.1.x)
+// * might send signal to the calling process too */
+// signal(SIGTERM, SIG_IGN);
+ /* Now stop all processes */
+ kill(-1, SIGSTOP);
+ /* Find out our own session id */
+ pid = getpid();
+ sid = getsid(pid);
+ /* Now kill all processes except our session */
+ while ((p = procps_scan(p, PSSCAN_PID|PSSCAN_SID))) {
+ if (p->sid != sid && p->pid != pid && p->pid != 1)
+ kill(p->pid, signo);
+ }
+ /* And let them continue */
+ kill(-1, SIGCONT);
+ return 0;
+ }
+
+ /* Pid or name required for kill/killall */
+ if (argc < 1)
+ bb_show_usage();
+
+ if (killall) {
+ /* Looks like they want to do a killall. Do that */
+ pid = getpid();
+ while (arg) {
+ pid_t* pidList;
+
+ pidList = find_pid_by_name(arg);
+ if (*pidList == 0) {
+ errors++;
+ if (!quiet)
+ bb_error_msg("%s: no process killed", arg);
+ } else {
+ pid_t *pl;
+
+ for (pl = pidList; *pl; pl++) {
+ if (*pl == pid)
+ continue;
+ if (kill(*pl, signo) == 0)
+ continue;
+ errors++;
+ if (!quiet)
+ bb_perror_msg("cannot kill pid %u", (unsigned)*pl);
+ }
+ }
+ free(pidList);
+ arg = *++argv;
+ }
+ return errors;
+ }
+
+ /* Looks like they want to do a kill. Do that */
+ while (arg) {
+ /* Huh?
+ if (!isdigit(arg[0]) && arg[0] != '-')
+ bb_error_msg_and_die("bad pid '%s'", arg);
+ */
+ pid = xatou(arg);
+ /* FIXME: better overflow check? */
+ if (kill(pid, signo) != 0) {
+ bb_perror_msg("cannot kill pid %u", (unsigned)pid);
+ errors++;
+ }
+ arg = *++argv;
+ }
+ return errors;
+}
diff --git a/i/pc104/initrd/conf/busybox/procps/pidof.c b/i/pc104/initrd/conf/busybox/procps/pidof.c
new file mode 100644
index 0000000..ea2520a
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/procps/pidof.c
@@ -0,0 +1,91 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * pidof implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under the GPL version 2, see the file LICENSE in this tarball.
+ */
+
+#include "busybox.h"
+
+enum {
+ USE_FEATURE_PIDOF_SINGLE(OPTBIT_SINGLE,)
+ USE_FEATURE_PIDOF_OMIT( OPTBIT_OMIT ,)
+ OPT_SINGLE = USE_FEATURE_PIDOF_SINGLE((1<<OPTBIT_SINGLE)) + 0,
+ OPT_OMIT = USE_FEATURE_PIDOF_OMIT( (1<<OPTBIT_OMIT )) + 0,
+};
+
+int pidof_main(int argc, char **argv);
+int pidof_main(int argc, char **argv)
+{
+ unsigned first = 1;
+ unsigned fail = 1;
+ unsigned opt;
+#if ENABLE_FEATURE_PIDOF_OMIT
+ llist_t *omits = NULL; /* list of pids to omit */
+ opt_complementary = "o::";
+#endif
+
+ /* do unconditional option parsing */
+ opt = getopt32(argc, argv, ""
+ USE_FEATURE_PIDOF_SINGLE ("s")
+ USE_FEATURE_PIDOF_OMIT("o:", &omits));
+
+#if ENABLE_FEATURE_PIDOF_OMIT
+ /* fill omit list. */
+ {
+ char getppid_str[sizeof(int)*3 + 1];
+ llist_t * omits_p = omits;
+ while (omits_p) {
+ /* are we asked to exclude the parent's process ID? */
+ if (!strncmp(omits_p->data, "%PPID", 5)) {
+ llist_pop(&omits_p);
+ snprintf(getppid_str, sizeof(getppid_str), "%u", (unsigned)getppid());
+ llist_add_to(&omits_p, getppid_str);
+ }
+ omits_p = omits_p->link;
+ }
+ }
+#endif
+ /* Looks like everything is set to go. */
+ while (optind < argc) {
+ pid_t *pidList;
+ pid_t *pl;
+
+ /* reverse the pidlist like GNU pidof does. */
+ pidList = pidlist_reverse(find_pid_by_name(argv[optind]));
+ for (pl = pidList; *pl; pl++) {
+ SKIP_FEATURE_PIDOF_OMIT(const) unsigned omitted = 0;
+#if ENABLE_FEATURE_PIDOF_OMIT
+ if (opt & OPT_OMIT) {
+ llist_t *omits_p = omits;
+ while (omits_p) {
+ if (xatoul(omits_p->data) == *pl) {
+ omitted = 1;
+ break;
+ } else
+ omits_p = omits_p->link;
+ }
+ }
+#endif
+ if (!omitted) {
+ printf(" %u" + first, (unsigned)*pl);
+ first = 0;
+ }
+ fail = (!ENABLE_FEATURE_PIDOF_OMIT && omitted);
+
+ if (ENABLE_FEATURE_PIDOF_SINGLE && (opt & OPT_SINGLE))
+ break;
+ }
+ free(pidList);
+ optind++;
+ }
+ putchar('\n');
+
+#if ENABLE_FEATURE_PIDOF_OMIT
+ if (ENABLE_FEATURE_CLEAN_UP)
+ llist_free(omits, NULL);
+#endif
+ return fail ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/i/pc104/initrd/conf/busybox/procps/ps.c b/i/pc104/initrd/conf/busybox/procps/ps.c
new file mode 100644
index 0000000..c1cb643
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/procps/ps.c
@@ -0,0 +1,388 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini ps implementation(s) for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under the GPL version 2, see the file LICENSE in this tarball.
+ */
+
+#include "busybox.h"
+
+#if ENABLE_DESKTOP
+
+/* Print value to buf, max size+1 chars (including trailing '\0') */
+
+static void func_user(char *buf, int size, const procps_status_t *ps)
+{
+ safe_strncpy(buf, get_cached_username(ps->uid), size+1);
+}
+
+static void func_comm(char *buf, int size, const procps_status_t *ps)
+{
+ safe_strncpy(buf, ps->comm, size+1);
+}
+
+static void func_args(char *buf, int size, const procps_status_t *ps)
+{
+ buf[0] = '\0';
+ if (ps->cmd)
+ safe_strncpy(buf, ps->cmd, size+1);
+ else if (size >= 2)
+ snprintf(buf, size+1, "[%.*s]", size-2, ps->comm);
+}
+
+static void func_pid(char *buf, int size, const procps_status_t *ps)
+{
+ snprintf(buf, size+1, "%*u", size, ps->pid);
+}
+
+static void func_ppid(char *buf, int size, const procps_status_t *ps)
+{
+ snprintf(buf, size+1, "%*u", size, ps->ppid);
+}
+
+static void func_pgid(char *buf, int size, const procps_status_t *ps)
+{
+ snprintf(buf, size+1, "%*u", size, ps->pgid);
+}
+
+static void func_vsz(char *buf, int size, const procps_status_t *ps)
+{
+ char buf5[5];
+ smart_ulltoa5( ((unsigned long long)ps->vsz) << 10, buf5);
+ snprintf(buf, size+1, "%.*s", size, buf5);
+}
+
+/*
+void func_nice(char *buf, int size, const procps_status_t *ps)
+{
+ ps->???
+}
+
+void func_etime(char *buf, int size, const procps_status_t *ps)
+{
+ elapled time [[dd-]hh:]mm:ss
+}
+
+void func_time(char *buf, int size, const procps_status_t *ps)
+{
+ cumulative time [[dd-]hh:]mm:ss
+}
+
+void func_pcpu(char *buf, int size, const procps_status_t *ps)
+{
+}
+
+void func_tty(char *buf, int size, const procps_status_t *ps)
+{
+}
+*/
+
+typedef struct {
+ char name[8];
+ const char *header;
+ void (*f)(char *buf, int size, const procps_status_t *ps);
+ int ps_flags;
+ int width;
+} ps_out_t;
+
+static const ps_out_t out_spec[] = {
+// Mandated by POSIX:
+ { "user" ,"USER" ,func_user ,PSSCAN_UIDGID,8 },
+ { "comm" ,"COMMAND",func_comm ,PSSCAN_COMM ,16 },
+ { "args" ,"COMMAND",func_args ,PSSCAN_CMD|PSSCAN_COMM,256 },
+ { "pid" ,"PID" ,func_pid ,PSSCAN_PID ,5 },
+ { "ppid" ,"PPID" ,func_ppid ,PSSCAN_PPID ,5 },
+ { "pgid" ,"PGID" ,func_pgid ,PSSCAN_PGID ,5 },
+// { "etime" ,"ELAPSED",func_etime ,PSSCAN_ ,sizeof("ELAPSED")-1 },
+// { "group" ,"GROUP" ,func_group ,PSSCAN_UIDGID,sizeof("GROUP" )-1 },
+// { "nice" ,"NI" ,func_nice ,PSSCAN_ ,sizeof("NI" )-1 },
+// { "pcpu" ,"%CPU" ,func_pcpu ,PSSCAN_ ,sizeof("%CPU" )-1 },
+// { "rgroup","RGROUP" ,func_rgroup,PSSCAN_UIDGID,sizeof("RGROUP" )-1 },
+// { "ruser" ,"RUSER" ,func_ruser ,PSSCAN_UIDGID,sizeof("RUSER" )-1 },
+// { "time" ,"TIME" ,func_time ,PSSCAN_ ,sizeof("TIME" )-1 },
+// { "tty" ,"TT" ,func_tty ,PSSCAN_ ,sizeof("TT" )-1 },
+ { "vsz" ,"VSZ" ,func_vsz ,PSSCAN_VSZ ,4 },
+// Not mandated by POSIX:
+// { "rss" ,"RSS" ,func_rss ,PSSCAN_RSS ,4 },
+};
+
+#define VEC_SIZE(v) ( sizeof(v) / sizeof((v)[0]) )
+
+static ps_out_t* out;
+static int out_cnt;
+static int print_header;
+static int ps_flags;
+static char *buffer;
+static unsigned terminal_width;
+
+
+static ps_out_t* new_out_t(void)
+{
+ int i = out_cnt++;
+ out = xrealloc(out, out_cnt * sizeof(*out));
+ return &out[i];
+}
+
+static const ps_out_t* find_out_spec(const char *name)
+{
+ int i;
+ for (i = 0; i < VEC_SIZE(out_spec); i++) {
+ if (!strcmp(name, out_spec[i].name))
+ return &out_spec[i];
+ }
+ bb_error_msg_and_die("bad -o argument '%s'", name);
+}
+
+static void parse_o(char* opt)
+{
+ ps_out_t* new;
+ // POSIX: "-o is blank- or comma-separated list" (FIXME)
+ char *comma, *equal;
+ while (1) {
+ comma = strchr(opt, ',');
+ equal = strchr(opt, '=');
+ if (comma && (!equal || equal > comma)) {
+ *comma = '\0';
+ *new_out_t() = *find_out_spec(opt);
+ *comma = ',';
+ opt = comma + 1;
+ continue;
+ }
+ break;
+ }
+ new = new_out_t();
+ if (equal)
+ *equal = '\0';
+ *new = *find_out_spec(opt);
+ if (equal) {
+ *equal = '=';
+ new->header = equal + 1;
+ // POSIX: the field widths shall be ... at least as wide as
+ // the header text (default or overridden value).
+ // If the header text is null, such as -o user=,
+ // the field width shall be at least as wide as the
+ // default header text
+ if (new->header[0]) {
+ new->width = strlen(new->header);
+ print_header = 1;
+ }
+ } else
+ print_header = 1;
+}
+
+static void post_process(void)
+{
+ int i;
+ int width = 0;
+ for (i = 0; i < out_cnt; i++) {
+ ps_flags |= out[i].ps_flags;
+ if (out[i].header[0]) {
+ print_header = 1;
+ }
+ width += out[i].width + 1; /* "FIELD " */
+ }
+ buffer = xmalloc(width + 1); /* for trailing \0 */
+}
+
+static void format_header(void)
+{
+ int i;
+ ps_out_t* op;
+ char *p = buffer;
+ if (!print_header)
+ return;
+ i = 0;
+ if (out_cnt) {
+ while (1) {
+ op = &out[i];
+ if (++i == out_cnt) /* do not pad last field */
+ break;
+ p += sprintf(p, "%-*s ", op->width, op->header);
+ }
+ strcpy(p, op->header);
+ }
+ printf("%.*s\n", terminal_width, buffer);
+}
+
+static void format_process(const procps_status_t *ps)
+{
+ int i, len;
+ char *p = buffer;
+ i = 0;
+ if (out_cnt) while (1) {
+ out[i].f(p, out[i].width, ps);
+ // POSIX: Any field need not be meaningful in all
+ // implementations. In such a case a hyphen ( '-' )
+ // should be output in place of the field value.
+ if (!p[0]) {
+ p[0] = '-';
+ p[1] = '\0';
+ }
+ len = strlen(p);
+ p += len;
+ len = out[i].width - len + 1;
+ if (++i == out_cnt) /* do not pad last field */
+ break;
+ p += sprintf(p, "%*s", len, "");
+ }
+ printf("%.*s\n", terminal_width, buffer);
+}
+
+/* Cannot be const: parse_o() will choke */
+static char default_o[] = "pid,user" /* TODO: ,vsz,stat */ ",args";
+
+int ps_main(int argc, char **argv);
+int ps_main(int argc, char **argv)
+{
+ procps_status_t *p;
+ llist_t* opt_o = NULL;
+
+ // POSIX:
+ // -a Write information for all processes associated with terminals
+ // Implementations may omit session leaders from this list
+ // -A Write information for all processes
+ // -d Write information for all processes, except session leaders
+ // -e Write information for all processes (equivalent to -A.)
+ // -f Generate a full listing
+ // -l Generate a long listing
+ // -o col1,col2,col3=header
+ // Select which columns to distplay
+ /* We allow (and ignore) most of the above. FIXME */
+ opt_complementary = "o::";
+ getopt32(argc, argv, "o:aAdefl", &opt_o);
+ if (opt_o) {
+ opt_o = llist_rev(opt_o);
+ do {
+ parse_o(opt_o->data);
+ opt_o = opt_o->link;
+ } while (opt_o);
+ } else
+ parse_o(default_o);
+ post_process();
+
+ terminal_width = INT_MAX;
+ if (isatty(1)) {
+ get_terminal_width_height(1, &terminal_width, NULL);
+ terminal_width--;
+ }
+ format_header();
+
+ p = NULL;
+ while ((p = procps_scan(p, ps_flags))) {
+ format_process(p);
+ }
+
+ return EXIT_SUCCESS;
+}
+
+
+#else /* !ENABLE_DESKTOP */
+
+
+int ps_main(int argc, char **argv);
+int ps_main(int argc, char **argv)
+{
+ procps_status_t *p = NULL;
+ int i, len;
+ SKIP_SELINUX(const) int use_selinux = 0;
+ USE_SELINUX(security_context_t sid = NULL;)
+#if !ENABLE_FEATURE_PS_WIDE
+ enum { terminal_width = 79 };
+#else
+ int terminal_width;
+ int w_count = 0;
+#endif
+
+#if ENABLE_FEATURE_PS_WIDE || ENABLE_SELINUX
+#if ENABLE_FEATURE_PS_WIDE
+ opt_complementary = "-:ww";
+ USE_SELINUX(i =) getopt32(argc, argv, USE_SELINUX("Z") "w", &w_count);
+ /* if w is given once, GNU ps sets the width to 132,
+ * if w is given more than once, it is "unlimited"
+ */
+ if (w_count) {
+ terminal_width = (w_count==1) ? 132 : INT_MAX;
+ } else {
+ get_terminal_width_height(1, &terminal_width, NULL);
+ /* Go one less... */
+ terminal_width--;
+ }
+#else /* only ENABLE_SELINUX */
+ i = getopt32(argc, argv, "Z");
+#endif
+#if ENABLE_SELINUX
+ if ((i & 1) && is_selinux_enabled())
+ use_selinux = 1;
+#endif
+#endif /* ENABLE_FEATURE_PS_WIDE || ENABLE_SELINUX */
+
+ if (use_selinux)
+ puts(" PID Context Stat Command");
+ else
+ puts(" PID Uid VSZ Stat Command");
+
+ while ((p = procps_scan(p, 0
+ | PSSCAN_PID
+ | PSSCAN_UIDGID
+ | PSSCAN_STATE
+ | PSSCAN_VSZ
+ | PSSCAN_CMD
+ ))) {
+ char *namecmd = p->cmd;
+#if ENABLE_SELINUX
+ if (use_selinux) {
+ char sbuf[128];
+ len = sizeof(sbuf);
+
+ if (is_selinux_enabled()) {
+ if (getpidcon(p->pid, &sid) < 0)
+ sid = NULL;
+ }
+
+ if (sid) {
+ /* I assume sid initialized with NULL */
+ len = strlen(sid) + 1;
+ safe_strncpy(sbuf, sid, len);
+ freecon(sid);
+ sid = NULL;
+ } else {
+ safe_strncpy(sbuf, "unknown", 7);
+ }
+ len = printf("%5u %-32s %s ", p->pid, sbuf, p->state);
+ } else
+#endif
+ {
+ const char *user = get_cached_username(p->uid);
+ if (p->vsz == 0)
+ len = printf("%5u %-8s %s ",
+ p->pid, user, p->state);
+ else
+ len = printf("%5u %-8s %6ld %s ",
+ p->pid, user, p->vsz, p->state);
+ }
+
+ i = terminal_width-len;
+
+ if (namecmd && namecmd[0]) {
+ if (i < 0)
+ i = 0;
+ if (strlen(namecmd) > (size_t)i)
+ namecmd[i] = 0;
+ puts(namecmd);
+ } else {
+ namecmd = p->comm;
+ if (i < 2)
+ i = 2;
+ if (strlen(namecmd) > ((size_t)i-2))
+ namecmd[i-2] = 0;
+ printf("[%s]\n", namecmd);
+ }
+ }
+ if (ENABLE_FEATURE_CLEAN_UP)
+ clear_username_cache();
+ return EXIT_SUCCESS;
+}
+
+#endif /* ENABLE_DESKTOP */
diff --git a/i/pc104/initrd/conf/busybox/procps/ps.posix b/i/pc104/initrd/conf/busybox/procps/ps.posix
new file mode 100644
index 0000000..57f4fa8
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/procps/ps.posix
@@ -0,0 +1,175 @@
+This is what POSIX 2003 says about ps:
+
+By default, ps shall select all processes with the same effective user
+ID as the current user and the same controlling terminal as the invoker
+
+ps [-aA][-defl][-G grouplist][-o format]...[-p proclist][-t termlist]
+[-U userlist][-g grouplist][-n namelist][-u userlist]
+
+-a Write information for all processes associated with terminals.
+ Implementations may omit session leaders from this list.
+
+-A Write information for all processes.
+
+-d Write information for all processes, except session leaders.
+
+-e Write information for all processes. (Equivalent to -A.)
+
+-f Generate a full listing. (See the STDOUT section for the con-
+ tents of a full listing.)
+
+-g grouplist
+ Write information for processes whose session leaders are given
+ in grouplist. The application shall ensure that the grouplist is
+ a single argument in the form of a <blank> or comma-separated
+ list.
+
+-G grouplist
+ Write information for processes whose real group ID numbers are
+ given in grouplist. The application shall ensure that the grou-
+ plist is a single argument in the form of a <blank> or comma-
+ separated list.
+
+-l Generate a long listing. (See STDOUT for the contents of a long
+ listing.)
+
+-n namelist
+ Specify the name of an alternative system namelist file in place
+ of the default. The name of the default file and the format of a
+ namelist file are unspecified.
+
+-o format
+ Write information according to the format specification given in
+ format. Multiple -o options can be specified; the format speci-
+ fication shall be interpreted as the <space>-separated concate-
+ nation of all the format option-arguments.
+
+-p proclist
+ Write information for processes whose process ID numbers are
+ given in proclist. The application shall ensure that the pro-
+ clist is a single argument in the form of a <blank> or comma-
+ separated list.
+
+-t termlist
+ Write information for processes associated with terminals given
+ in termlist. The application shall ensure that the termlist is a
+ single argument in the form of a <blank> or comma-separated
+ list. Terminal identifiers shall be given in an implementation-
+ defined format. On XSI-conformant systems, they shall be
+ given in one of two forms: the device's filename (for example,
+ tty04) or, if the device's filename starts with tty, just the
+ identifier following the characters tty (for example, "04" ).
+
+-u userlist
+ Write information for processes whose user ID numbers or login
+ names are given in userlist. The application shall ensure that
+ the userlist is a single argument in the form of a <blank> or
+ comma-separated list. In the listing, the numerical user ID
+ shall be written unless the -f option is used, in which case the
+ login name shall be written.
+
+-U userlist
+ Write information for processes whose real user ID numbers or
+ login names are given in userlist. The application shall ensure
+ that the userlist is a single argument in the form of a <blank>
+ or comma-separated list.
+
+With the exception of -o format, all of the options shown are used to
+select processes. If any are specified, the default list shall be
+ignored and ps shall select the processes represented by the inclusive
+OR of all the selection-criteria options.
+
+The -o option allows the output format to be specified under user con-
+trol.
+
+The application shall ensure that the format specification is a list of
+names presented as a single argument, <blank> or comma-separated. Each
+variable has a default header. The default header can be overridden by
+appending an equals sign and the new text of the header. The rest of
+the characters in the argument shall be used as the header text. The
+fields specified shall be written in the order specified on the command
+line, and should be arranged in columns in the output. The field widths
+shall be selected by the system to be at least as wide as the header
+text (default or overridden value). If the header text is null, such as
+-o user=, the field width shall be at least as wide as the default
+header text. If all header text fields are null, no header line shall
+be written.
+
+ruser The real user ID of the process. This shall be the textual user
+ ID, if it can be obtained and the field width permits, or a dec-
+ imal representation otherwise.
+
+user The effective user ID of the process. This shall be the textual
+ user ID, if it can be obtained and the field width permits, or a
+ decimal representation otherwise.
+
+rgroup The real group ID of the process. This shall be the textual
+ group ID, if it can be obtained and the field width permits, or
+ a decimal representation otherwise.
+
+group The effective group ID of the process. This shall be the textual
+ group ID, if it can be obtained and the field width permits, or
+ a decimal representation otherwise.
+
+pid The decimal value of the process ID.
+
+ppid The decimal value of the parent process ID.
+
+pgid The decimal value of the process group ID.
+
+pcpu The ratio of CPU time used recently to CPU time available in the
+ same period, expressed as a percentage. The meaning of
+ "recently" in this context is unspecified. The CPU time avail-
+ able is determined in an unspecified manner.
+
+vsz The size of the process in (virtual) memory in 1024 byte units
+ as a decimal integer.
+
+nice The decimal value of the nice value of the process; see nice() .
+
+etime In the POSIX locale, the elapsed time since the process was
+ started, in the form: [[dd-]hh:]mm:ss
+
+time In the POSIX locale, the cumulative CPU time of the process in
+ the form: [dd-]hh:mm:ss
+
+tty The name of the controlling terminal of the process (if any) in
+ the same format used by the who utility.
+
+comm The name of the command being executed ( argv[0] value) as a
+ string.
+
+args The command with all its arguments as a string. The implementa-
+ tion may truncate this value to the field width; it is implemen-
+ tation-defined whether any further truncation occurs. It is
+ unspecified whether the string represented is a version of the
+ argument list as it was passed to the command when it started,
+ or is a version of the arguments as they may have been modified
+ by the application. Applications cannot depend on being able to
+ modify their argument list and having that modification be
+ reflected in the output of ps.
+
+Any field need not be meaningful in all implementations. In such a case
+a hyphen ( '-' ) should be output in place of the field value.
+
+Only comm and args shall be allowed to contain <blank>s; all others
+shall not.
+
+The following table specifies the default header to be used in the
+POSIX locale corresponding to each format specifier.
+
+ Format Specifier Default Header Format Specifier Default Header
+ args COMMAND ppid PPID
+ comm COMMAND rgroup RGROUP
+ etime ELAPSED ruser RUSER
+ group GROUP time TIME
+ nice NI tty TT
+ pcpu %CPU user USER
+ pgid PGID vsz VSZ
+ pid PID
+
+There is no special quoting mechanism for header text. The header text
+is the rest of the argument. If multiple header changes are needed,
+multiple -o options can be used, such as:
+
+ ps -o "user=User Name" -o pid=Process\ ID
diff --git a/i/pc104/initrd/conf/busybox/procps/renice.c b/i/pc104/initrd/conf/busybox/procps/renice.c
new file mode 100644
index 0000000..2964e20
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/procps/renice.c
@@ -0,0 +1,127 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * renice implementation for busybox
+ *
+ * Copyright (C) 2005 Manuel Novoa III <mjn3@codepoet.org>
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ */
+
+/* Notes:
+ * Setting an absolute priority was obsoleted in SUSv2 and removed
+ * in SUSv3. However, the common linux version of renice does
+ * absolute and not relative. So we'll continue supporting absolute,
+ * although the stdout logging has been removed since both SUSv2 and
+ * SUSv3 specify that stdout isn't used.
+ *
+ * This version is lenient in that it doesn't require any IDs. The
+ * options -p, -g, and -u are treated as mode switches for the
+ * following IDs (if any). Multiple switches are allowed.
+ */
+
+#include "busybox.h"
+#include <sys/resource.h>
+
+void BUG_bad_PRIO_PROCESS(void);
+void BUG_bad_PRIO_PGRP(void);
+void BUG_bad_PRIO_USER(void);
+
+int renice_main(int argc, char **argv);
+int renice_main(int argc, char **argv)
+{
+ static const char Xetpriority_msg[] = "%cetpriority";
+
+ int retval = EXIT_SUCCESS;
+ int which = PRIO_PROCESS; /* Default 'which' value. */
+ int use_relative = 0;
+ int adjustment, new_priority;
+ unsigned who;
+ char *arg;
+
+ /* Yes, they are not #defines in glibc 2.4! #if won't work */
+ if (PRIO_PROCESS < CHAR_MIN || PRIO_PROCESS > CHAR_MAX)
+ BUG_bad_PRIO_PROCESS();
+ if (PRIO_PGRP < CHAR_MIN || PRIO_PGRP > CHAR_MAX)
+ BUG_bad_PRIO_PGRP();
+ if (PRIO_USER < CHAR_MIN || PRIO_USER > CHAR_MAX)
+ BUG_bad_PRIO_USER();
+
+ arg = *++argv;
+
+ /* Check if we are using a relative adjustment. */
+ if (arg && arg[0] == '-' && arg[1] == 'n') {
+ use_relative = 1;
+ if (!arg[2])
+ arg = *++argv;
+ else
+ arg += 2;
+ }
+
+ if (!arg) { /* No args? Then show usage. */
+ bb_show_usage();
+ }
+
+ /* Get the priority adjustment (absolute or relative). */
+ adjustment = xatoi_range(arg, INT_MIN/2, INT_MAX/2);
+
+ while ((arg = *++argv) != NULL) {
+ /* Check for a mode switch. */
+ if (arg[0] == '-' && arg[1]) {
+ static const char opts[]
+ = { 'p', 'g', 'u', 0, PRIO_PROCESS, PRIO_PGRP, PRIO_USER };
+ const char *p = strchr(opts, arg[1]);
+ if (p) {
+ which = p[4];
+ if (!arg[2])
+ continue;
+ arg += 2;
+ }
+ }
+
+ /* Process an ID arg. */
+ if (which == PRIO_USER) {
+ struct passwd *p;
+ p = getpwnam(arg);
+ if (!p) {
+ bb_error_msg("unknown user: %s", arg);
+ goto HAD_ERROR;
+ }
+ who = p->pw_uid;
+ } else {
+ who = bb_strtou(arg, NULL, 10);
+ if (errno) {
+ bb_error_msg("bad value: %s", arg);
+ goto HAD_ERROR;
+ }
+ }
+
+ /* Get priority to use, and set it. */
+ if (use_relative) {
+ int old_priority;
+
+ errno = 0; /* Needed for getpriority error detection. */
+ old_priority = getpriority(which, who);
+ if (errno) {
+ bb_perror_msg(Xetpriority_msg, 'g');
+ goto HAD_ERROR;
+ }
+
+ new_priority = old_priority + adjustment;
+ } else {
+ new_priority = adjustment;
+ }
+
+ if (setpriority(which, who, new_priority) == 0) {
+ continue;
+ }
+
+ bb_perror_msg(Xetpriority_msg, 's');
+ HAD_ERROR:
+ retval = EXIT_FAILURE;
+ }
+
+ /* No need to check for errors outputing to stderr since, if it
+ * was used, the HAD_ERROR label was reached and retval was set. */
+
+ return retval;
+}
diff --git a/i/pc104/initrd/conf/busybox/procps/sysctl.c b/i/pc104/initrd/conf/busybox/procps/sysctl.c
new file mode 100644
index 0000000..b5a0189
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/procps/sysctl.c
@@ -0,0 +1,327 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Sysctl 1.01 - A utility to read and manipulate the sysctl parameters
+ *
+ * Copyright 1999 George Staikos
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ *
+ * Changelog:
+ * v1.01:
+ * - added -p <preload> to preload values from a file
+ * v1.01.1
+ * - busybox applet aware by <solar@gentoo.org>
+ *
+ */
+
+#include "busybox.h"
+
+/*
+ * Function Prototypes
+ */
+static int sysctl_read_setting(const char *setting, int output);
+static int sysctl_write_setting(const char *setting, int output);
+static int sysctl_preload_file(const char *filename, int output);
+static int sysctl_display_all(const char *path, int output, int show_table);
+
+/*
+ * Globals...
+ */
+static const char PROC_PATH[] = "/proc/sys/";
+static const char DEFAULT_PRELOAD[] = "/etc/sysctl.conf";
+
+/* error messages */
+static const char ERR_UNKNOWN_PARAMETER[] = "error: Unknown parameter '%s'\n";
+static const char ERR_MALFORMED_SETTING[] = "error: Malformed setting '%s'\n";
+static const char ERR_NO_EQUALS[] =
+ "error: '%s' must be of the form name=value\n";
+static const char ERR_INVALID_KEY[] = "error: '%s' is an unknown key\n";
+static const char ERR_UNKNOWN_WRITING[] =
+ "error: unknown error %d setting key '%s'\n";
+static const char ERR_UNKNOWN_READING[] =
+ "error: unknown error %d reading key '%s'\n";
+static const char ERR_PERMISSION_DENIED[] =
+ "error: permission denied on key '%s'\n";
+static const char ERR_PRELOAD_FILE[] =
+ "error: cannot open preload file '%s'\n";
+static const char WARN_BAD_LINE[] =
+ "warning: %s(%d): invalid syntax, continuing...\n";
+
+
+static void dwrite_str(int fd, const char *buf)
+{
+ write(fd, buf, strlen(buf));
+}
+
+/*
+ * sysctl_main()...
+ */
+int sysctl_main(int argc, char **argv);
+int sysctl_main(int argc, char **argv)
+{
+ int retval = 0;
+ int output = 1;
+ int write_mode = 0;
+ int switches_allowed = 1;
+
+ if (argc < 2)
+ bb_show_usage();
+
+ argv++;
+
+ for (; argv && *argv && **argv; argv++) {
+ if (switches_allowed && **argv == '-') { /* we have a switch */
+ switch ((*argv)[1]) {
+ case 'n':
+ output = 0;
+ break;
+ case 'w':
+ write_mode = 1;
+ switches_allowed = 0;
+ break;
+ case 'p':
+ argv++;
+ return
+ sysctl_preload_file(((argv && *argv
+ && **argv) ? *argv :
+ DEFAULT_PRELOAD), output);
+ case 'a':
+ case 'A':
+ switches_allowed = 0;
+ return sysctl_display_all(PROC_PATH, output,
+ ((*argv)[1] == 'a') ? 0 : 1);
+ case 'h':
+ case '?':
+ bb_show_usage();
+ default:
+ bb_error_msg(ERR_UNKNOWN_PARAMETER, *argv);
+ bb_show_usage();
+ }
+ } else {
+ switches_allowed = 0;
+ if (write_mode)
+ retval = sysctl_write_setting(*argv, output);
+ else
+ sysctl_read_setting(*argv, output);
+ }
+ }
+ return retval;
+} /* end sysctl_main() */
+
+
+
+/*
+ * sysctl_preload_file
+ * preload the sysctl's from a conf file
+ * - we parse the file and then reform it (strip out whitespace)
+ */
+#define PRELOAD_BUF 256
+
+int sysctl_preload_file(const char *filename, int output)
+{
+ int lineno = 0;
+ char oneline[PRELOAD_BUF];
+ char buffer[PRELOAD_BUF];
+ char *name, *value, *ptr;
+ FILE *fp = NULL;
+
+ if (!filename || ((fp = fopen(filename, "r")) == NULL)) {
+ bb_error_msg_and_die(ERR_PRELOAD_FILE, filename);
+ }
+
+ while (fgets(oneline, sizeof(oneline) - 1, fp)) {
+ oneline[sizeof(oneline) - 1] = '\0';
+ lineno++;
+ trim(oneline);
+ ptr = (char *) oneline;
+
+ if (*ptr == '#' || *ptr == ';')
+ continue;
+
+ if (strlen(ptr) < 2)
+ continue;
+
+ name = strtok(ptr, "=");
+ if (!name || !*name) {
+ bb_error_msg(WARN_BAD_LINE, filename, lineno);
+ continue;
+ }
+
+ trim(name);
+
+ value = strtok(NULL, "\n\r");
+ if (!value || !*value) {
+ bb_error_msg(WARN_BAD_LINE, filename, lineno);
+ continue;
+ }
+
+ while ((*value == ' ' || *value == '\t') && *value != 0)
+ value++;
+ /* safe because sizeof(oneline) == sizeof(buffer) */
+ sprintf(buffer, "%s=%s", name, value);
+ sysctl_write_setting(buffer, output);
+ }
+ fclose(fp);
+ return 0;
+} /* end sysctl_preload_file() */
+
+
+/*
+ * Write a single sysctl setting
+ */
+int sysctl_write_setting(const char *setting, int output)
+{
+ int retval = 0;
+ const char *name = setting;
+ const char *value;
+ const char *equals;
+ char *tmpname, *outname, *cptr;
+ int fd = -1;
+
+ if (!name) /* probably dont' want to display this err */
+ return 0;
+
+ if (!(equals = strchr(setting, '='))) {
+ bb_error_msg(ERR_NO_EQUALS, setting);
+ return -1;
+ }
+
+ value = equals + sizeof(char); /* point to the value in name=value */
+
+ if (!*name || !*value || name == equals) {
+ bb_error_msg(ERR_MALFORMED_SETTING, setting);
+ return -2;
+ }
+
+ tmpname = xasprintf("%s%.*s", PROC_PATH, (int)(equals - name), name);
+ outname = xstrdup(tmpname + strlen(PROC_PATH));
+
+ while ((cptr = strchr(tmpname, '.')) != NULL)
+ *cptr = '/';
+
+ while ((cptr = strchr(outname, '/')) != NULL)
+ *cptr = '.';
+
+ if ((fd = open(tmpname, O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0) {
+ switch (errno) {
+ case ENOENT:
+ bb_error_msg(ERR_INVALID_KEY, outname);
+ break;
+ case EACCES:
+ bb_perror_msg(ERR_PERMISSION_DENIED, outname);
+ break;
+ default:
+ bb_error_msg(ERR_UNKNOWN_WRITING, errno, outname);
+ break;
+ }
+ retval = -1;
+ } else {
+ dwrite_str(fd, value);
+ close(fd);
+ if (output) {
+ dwrite_str(STDOUT_FILENO, outname);
+ dwrite_str(STDOUT_FILENO, " = ");
+ }
+ dwrite_str(STDOUT_FILENO, value);
+ dwrite_str(STDOUT_FILENO, "\n");
+ }
+
+ /* cleanup */
+ free(tmpname);
+ free(outname);
+ return retval;
+} /* end sysctl_write_setting() */
+
+
+/*
+ * Read a sysctl setting
+ *
+ */
+int sysctl_read_setting(const char *setting, int output)
+{
+ int retval = 0;
+ char *tmpname, *outname, *cptr;
+ char inbuf[1025];
+ const char *name = setting;
+ FILE *fp;
+
+ if (!setting || !*setting)
+ bb_error_msg(ERR_INVALID_KEY, setting);
+
+ tmpname = concat_path_file(PROC_PATH, name);
+ outname = xstrdup(tmpname + strlen(PROC_PATH));
+
+ while ((cptr = strchr(tmpname, '.')) != NULL)
+ *cptr = '/';
+ while ((cptr = strchr(outname, '/')) != NULL)
+ *cptr = '.';
+
+ if ((fp = fopen(tmpname, "r")) == NULL) {
+ switch (errno) {
+ case ENOENT:
+ bb_error_msg(ERR_INVALID_KEY, outname);
+ break;
+ case EACCES:
+ bb_error_msg(ERR_PERMISSION_DENIED, outname);
+ break;
+ default:
+ bb_error_msg(ERR_UNKNOWN_READING, errno, outname);
+ break;
+ }
+ retval = -1;
+ } else {
+ while (fgets(inbuf, sizeof(inbuf) - 1, fp)) {
+ if (output) {
+ dwrite_str(STDOUT_FILENO, outname);
+ dwrite_str(STDOUT_FILENO, " = ");
+ }
+ dwrite_str(STDOUT_FILENO, inbuf);
+ }
+ fclose(fp);
+ }
+
+ free(tmpname);
+ free(outname);
+ return retval;
+} /* end sysctl_read_setting() */
+
+
+
+/*
+ * Display all the sysctl settings
+ *
+ */
+int sysctl_display_all(const char *path, int output, int show_table)
+{
+ int retval = 0;
+ int retval2;
+ DIR *dp;
+ struct dirent *de;
+ char *tmpdir;
+ struct stat ts;
+
+ if (!(dp = opendir(path))) {
+ retval = -1;
+ } else {
+ while ((de = readdir(dp)) != NULL) {
+ tmpdir = concat_subpath_file(path, de->d_name);
+ if(tmpdir == NULL)
+ continue;
+ if ((retval2 = stat(tmpdir, &ts)) != 0)
+ bb_perror_msg(tmpdir);
+ else {
+ if (S_ISDIR(ts.st_mode)) {
+ sysctl_display_all(tmpdir, output, show_table);
+ } else
+ retval |=
+ sysctl_read_setting(tmpdir + strlen(PROC_PATH),
+ output);
+
+ }
+ free(tmpdir);
+ } /* end while */
+ closedir(dp);
+ }
+
+ return retval;
+} /* end sysctl_display_all() */
diff --git a/i/pc104/initrd/conf/busybox/procps/top.c b/i/pc104/initrd/conf/busybox/procps/top.c
new file mode 100644
index 0000000..ee6ee5d
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/procps/top.c
@@ -0,0 +1,570 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * A tiny 'top' utility.
+ *
+ * This is written specifically for the linux /proc/<PID>/stat(m)
+ * files format.
+
+ * This reads the PIDs of all processes and their status and shows
+ * the status of processes (first ones that fit to screen) at given
+ * intervals.
+ *
+ * NOTES:
+ * - At startup this changes to /proc, all the reads are then
+ * relative to that.
+ *
+ * (C) Eero Tamminen <oak at welho dot com>
+ *
+ * Rewritten by Vladimir Oleynik (C) 2002 <dzo@simtreas.ru>
+ */
+
+/* Original code Copyrights */
+/*
+ * Copyright (c) 1992 Branko Lankester
+ * Copyright (c) 1992 Roger Binns
+ * Copyright (C) 1994-1996 Charles L. Blake.
+ * Copyright (C) 1992-1998 Michael K. Johnson
+ * May be distributed under the conditions of the
+ * GNU Library General Public License
+ */
+
+#include "busybox.h"
+
+
+typedef struct {
+ unsigned long vsz;
+#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
+ unsigned long ticks;
+ unsigned pcpu; /* delta of ticks */
+#endif
+ unsigned pid, ppid;
+ unsigned uid;
+ char state[4];
+ char comm[COMM_LEN];
+} top_status_t;
+static top_status_t *top;
+static int ntop;
+/* This structure stores some critical information from one frame to
+ the next. Used for finding deltas. */
+struct save_hist {
+ unsigned long ticks;
+ unsigned pid;
+};
+#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
+static struct save_hist *prev_hist;
+static int prev_hist_count;
+/* static int hist_iterations; */
+static unsigned total_pcpu;
+/* static unsigned long total_vsz; */
+#endif
+
+#define OPT_BATCH_MODE (option_mask32 & 0x4)
+
+#if ENABLE_FEATURE_USE_TERMIOS
+static int pid_sort(top_status_t *P, top_status_t *Q)
+{
+ /* Buggy wrt pids with high bit set */
+ /* (linux pids are in [1..2^15-1]) */
+ return (Q->pid - P->pid);
+}
+#endif
+
+static int mem_sort(top_status_t *P, top_status_t *Q)
+{
+ /* We want to avoid unsigned->signed and truncation errors */
+ if (Q->vsz < P->vsz) return -1;
+ return Q->vsz != P->vsz; /* 0 if ==, 1 if > */
+}
+
+
+typedef int (*cmp_funcp)(top_status_t *P, top_status_t *Q);
+
+#if !ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
+
+static cmp_funcp sort_function;
+
+#else
+
+enum { SORT_DEPTH = 3 };
+
+static cmp_funcp sort_function[SORT_DEPTH];
+
+static int pcpu_sort(top_status_t *P, top_status_t *Q)
+{
+ /* Buggy wrt ticks with high bit set */
+ /* Affects only processes for which ticks overflow */
+ return (int)Q->pcpu - (int)P->pcpu;
+}
+
+static int time_sort(top_status_t *P, top_status_t *Q)
+{
+ /* We want to avoid unsigned->signed and truncation errors */
+ if (Q->ticks < P->ticks) return -1;
+ return Q->ticks != P->ticks; /* 0 if ==, 1 if > */
+}
+
+static int mult_lvl_cmp(void* a, void* b) {
+ int i, cmp_val;
+
+ for (i = 0; i < SORT_DEPTH; i++) {
+ cmp_val = (*sort_function[i])(a, b);
+ if (cmp_val != 0)
+ return cmp_val;
+ }
+ return 0;
+}
+
+
+typedef struct {
+ unsigned long long usr,nic,sys,idle,iowait,irq,softirq,steal;
+ unsigned long long total;
+ unsigned long long busy;
+} jiffy_counts_t;
+static jiffy_counts_t jif, prev_jif;
+static void get_jiffy_counts(void)
+{
+ FILE* fp = xfopen("stat", "r");
+ prev_jif = jif;
+ if (fscanf(fp, "cpu %lld %lld %lld %lld %lld %lld %lld %lld",
+ &jif.usr,&jif.nic,&jif.sys,&jif.idle,
+ &jif.iowait,&jif.irq,&jif.softirq,&jif.steal) < 4) {
+ bb_error_msg_and_die("failed to read /proc/stat");
+ }
+ fclose(fp);
+ jif.total = jif.usr + jif.nic + jif.sys + jif.idle
+ + jif.iowait + jif.irq + jif.softirq + jif.steal;
+ /* procps 2.x does not count iowait as busy time */
+ jif.busy = jif.total - jif.idle - jif.iowait;
+}
+
+
+static void do_stats(void)
+{
+ top_status_t *cur;
+ pid_t pid;
+ int i, last_i, n;
+ struct save_hist *new_hist;
+
+ get_jiffy_counts();
+ total_pcpu = 0;
+ /* total_vsz = 0; */
+ new_hist = xmalloc(sizeof(struct save_hist)*ntop);
+ /*
+ * Make a pass through the data to get stats.
+ */
+ /* hist_iterations = 0; */
+ i = 0;
+ for (n = 0; n < ntop; n++) {
+ cur = top + n;
+
+ /*
+ * Calculate time in cur process. Time is sum of user time
+ * and system time
+ */
+ pid = cur->pid;
+ new_hist[n].ticks = cur->ticks;
+ new_hist[n].pid = pid;
+
+ /* find matching entry from previous pass */
+ cur->pcpu = 0;
+ /* do not start at index 0, continue at last used one
+ * (brought hist_iterations from ~14000 down to 172) */
+ last_i = i;
+ if (prev_hist_count) do {
+ if (prev_hist[i].pid == pid) {
+ cur->pcpu = cur->ticks - prev_hist[i].ticks;
+ total_pcpu += cur->pcpu;
+ break;
+ }
+ i = (i+1) % prev_hist_count;
+ /* hist_iterations++; */
+ } while (i != last_i);
+ /* total_vsz += cur->vsz; */
+ }
+
+ /*
+ * Save cur frame's information.
+ */
+ free(prev_hist);
+ prev_hist = new_hist;
+ prev_hist_count = ntop;
+}
+#endif /* FEATURE_TOP_CPU_USAGE_PERCENTAGE */
+
+
+/* display generic info (meminfo / loadavg) */
+static unsigned long display_generic(int scr_width)
+{
+ FILE *fp;
+ char buf[80];
+ char scrbuf[80];
+ char *end;
+ unsigned long total, used, mfree, shared, buffers, cached;
+ unsigned int needs_conversion = 1;
+
+ /* read memory info */
+ fp = xfopen("meminfo", "r");
+
+ /*
+ * Old kernels (such as 2.4.x) had a nice summary of memory info that
+ * we could parse, however this is gone entirely in 2.6. Try parsing
+ * the old way first, and if that fails, parse each field manually.
+ *
+ * First, we read in the first line. Old kernels will have bogus
+ * strings we don't care about, whereas new kernels will start right
+ * out with MemTotal:
+ * -- PFM.
+ */
+ if (fscanf(fp, "MemTotal: %lu %s\n", &total, buf) != 2) {
+ fgets(buf, sizeof(buf), fp); /* skip first line */
+
+ fscanf(fp, "Mem: %lu %lu %lu %lu %lu %lu",
+ &total, &used, &mfree, &shared, &buffers, &cached);
+ } else {
+ /*
+ * Revert to manual parsing, which incidentally already has the
+ * sizes in kilobytes. This should be safe for both 2.4 and
+ * 2.6.
+ */
+ needs_conversion = 0;
+
+ fscanf(fp, "MemFree: %lu %s\n", &mfree, buf);
+
+ /*
+ * MemShared: is no longer present in 2.6. Report this as 0,
+ * to maintain consistent behavior with normal procps.
+ */
+ if (fscanf(fp, "MemShared: %lu %s\n", &shared, buf) != 2)
+ shared = 0;
+
+ fscanf(fp, "Buffers: %lu %s\n", &buffers, buf);
+ fscanf(fp, "Cached: %lu %s\n", &cached, buf);
+
+ used = total - mfree;
+ }
+ fclose(fp);
+
+ /* read load average as a string */
+ buf[0] = '\0';
+ open_read_close("loadavg", buf, sizeof(buf));
+ end = strchr(buf, ' ');
+ if (end) end = strchr(end+1, ' ');
+ if (end) end = strchr(end+1, ' ');
+ if (end) *end = '\0';
+
+ if (needs_conversion) {
+ /* convert to kilobytes */
+ used /= 1024;
+ mfree /= 1024;
+ shared /= 1024;
+ buffers /= 1024;
+ cached /= 1024;
+ total /= 1024;
+ }
+
+ /* output memory info and load average */
+ /* clear screen & go to top */
+ if (scr_width > sizeof(scrbuf))
+ scr_width = sizeof(scrbuf);
+ snprintf(scrbuf, scr_width,
+ "Mem: %ldK used, %ldK free, %ldK shrd, %ldK buff, %ldK cached",
+ used, mfree, shared, buffers, cached);
+
+ printf(OPT_BATCH_MODE ? "%s\n" : "\e[H\e[J%s\n", scrbuf);
+
+ snprintf(scrbuf, scr_width, "Load average: %s", buf);
+ printf("%s\n", scrbuf);
+
+ return total;
+}
+
+
+/* display process statuses */
+static void display_status(int count, int scr_width)
+{
+ enum {
+ bits_per_int = sizeof(int)*8
+ };
+
+ top_status_t *s = top;
+ char vsz_str_buf[8];
+ unsigned long total_memory = display_generic(scr_width); /* or use total_vsz? */
+ unsigned pmem_shift, pmem_scale;
+
+#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
+ unsigned pcpu_shift, pcpu_scale;
+ unsigned busy_jifs;
+
+ /* what info of the processes is shown */
+ printf(OPT_BATCH_MODE ? "%.*s" : "\e[7m%.*s\e[0m", scr_width,
+ " PID USER STATUS VSZ PPID %CPU %MEM COMMAND");
+#define MIN_WIDTH \
+ sizeof( " PID USER STATUS VSZ PPID %CPU %MEM C")
+#else
+ printf(OPT_BATCH_MODE ? "%.*s" : "\e[7m%.*s\e[0m", scr_width,
+ " PID USER STATUS VSZ PPID %MEM COMMAND");
+#define MIN_WIDTH \
+ sizeof( " PID USER STATUS VSZ PPID %MEM C")
+#endif
+
+ /*
+ * MEM% = s->vsz/MemTotal
+ */
+ pmem_shift = bits_per_int-11;
+ pmem_scale = 1000*(1U<<(bits_per_int-11)) / total_memory;
+ /* s->vsz is in kb. we want (s->vsz * pmem_scale) to never overflow */
+ while (pmem_scale >= 512) {
+ pmem_scale /= 4;
+ pmem_shift -= 2;
+ }
+#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
+ busy_jifs = jif.busy - prev_jif.busy;
+ /* This happens if there were lots of short-lived processes
+ * between two top updates (e.g. compilation) */
+ if (total_pcpu < busy_jifs) total_pcpu = busy_jifs;
+
+ /*
+ * CPU% = s->pcpu/sum(s->pcpu) * busy_cpu_ticks/total_cpu_ticks
+ * (pcpu is delta of sys+user time between samples)
+ */
+ /* (jif.xxx - prev_jif.xxx) and s->pcpu are
+ * in 0..~64000 range (HZ*update_interval).
+ * we assume that unsigned is at least 32-bit.
+ */
+ pcpu_shift = 6;
+ pcpu_scale = (1000*64*(uint16_t)busy_jifs ? : 1);
+ while (pcpu_scale < (1U<<(bits_per_int-2))) {
+ pcpu_scale *= 4;
+ pcpu_shift += 2;
+ }
+ pcpu_scale /= ( (uint16_t)(jif.total-prev_jif.total)*total_pcpu ? : 1);
+ /* we want (s->pcpu * pcpu_scale) to never overflow */
+ while (pcpu_scale >= 1024) {
+ pcpu_scale /= 4;
+ pcpu_shift -= 2;
+ }
+ /* printf(" pmem_scale=%u pcpu_scale=%u ", pmem_scale, pcpu_scale); */
+#endif
+ while (count-- > 0) {
+ div_t pmem = div((s->vsz*pmem_scale) >> pmem_shift, 10);
+ int col = scr_width+1;
+ USE_FEATURE_TOP_CPU_USAGE_PERCENTAGE(div_t pcpu;)
+
+ if (s->vsz >= 100*1024)
+ sprintf(vsz_str_buf, "%6ldM", s->vsz/1024);
+ else
+ sprintf(vsz_str_buf, "%7ld", s->vsz);
+ USE_FEATURE_TOP_CPU_USAGE_PERCENTAGE(
+ pcpu = div((s->pcpu*pcpu_scale) >> pcpu_shift, 10);
+ )
+ col -= printf("\n%5u %-8s %s "
+ "%s%6u"
+ USE_FEATURE_TOP_CPU_USAGE_PERCENTAGE("%3u.%c")
+ "%3u.%c ",
+ s->pid, get_cached_username(s->uid), s->state,
+ vsz_str_buf, s->ppid,
+ USE_FEATURE_TOP_CPU_USAGE_PERCENTAGE(pcpu.quot, '0'+pcpu.rem,)
+ pmem.quot, '0'+pmem.rem);
+ if (col > 0)
+ printf("%.*s", col, s->comm);
+ /* printf(" %d/%d %lld/%lld", s->pcpu, total_pcpu,
+ jif.busy - prev_jif.busy, jif.total - prev_jif.total); */
+ s++;
+ }
+ /* printf(" %d", hist_iterations); */
+ putchar(OPT_BATCH_MODE ? '\n' : '\r');
+ fflush(stdout);
+}
+
+
+static void clearmems(void)
+{
+ clear_username_cache();
+ free(top);
+ top = 0;
+ ntop = 0;
+}
+
+
+#if ENABLE_FEATURE_USE_TERMIOS
+#include <termios.h>
+#include <signal.h>
+
+static struct termios initial_settings;
+
+static void reset_term(void)
+{
+ tcsetattr(0, TCSANOW, (void *) &initial_settings);
+#if ENABLE_FEATURE_CLEAN_UP
+ clearmems();
+#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
+ free(prev_hist);
+#endif
+#endif /* FEATURE_CLEAN_UP */
+}
+
+static void sig_catcher(int sig ATTRIBUTE_UNUSED)
+{
+ reset_term();
+ exit(1);
+}
+#endif /* FEATURE_USE_TERMIOS */
+
+
+int top_main(int argc, char **argv);
+int top_main(int argc, char **argv)
+{
+ int count, lines, col;
+ unsigned interval = 5; /* default update rate is 5 seconds */
+ unsigned iterations = UINT_MAX; /* 2^32 iterations by default :) */
+ char *sinterval, *siterations;
+#if ENABLE_FEATURE_USE_TERMIOS
+ struct termios new_settings;
+ struct timeval tv;
+ fd_set readfds;
+ unsigned char c;
+#endif /* FEATURE_USE_TERMIOS */
+
+ /* do normal option parsing */
+ interval = 5;
+ opt_complementary = "-";
+ getopt32(argc, argv, "d:n:b", &sinterval, &siterations);
+ if (option_mask32 & 0x1) interval = xatou(sinterval); // -d
+ if (option_mask32 & 0x2) iterations = xatou(siterations); // -n
+ //if (option_mask32 & 0x4) // -b
+
+ /* change to /proc */
+ xchdir("/proc");
+#if ENABLE_FEATURE_USE_TERMIOS
+ tcgetattr(0, (void *) &initial_settings);
+ memcpy(&new_settings, &initial_settings, sizeof(struct termios));
+ /* unbuffered input, turn off echo */
+ new_settings.c_lflag &= ~(ISIG | ICANON | ECHO | ECHONL);
+
+ signal(SIGTERM, sig_catcher);
+ signal(SIGINT, sig_catcher);
+ tcsetattr(0, TCSANOW, (void *) &new_settings);
+ atexit(reset_term);
+#endif /* FEATURE_USE_TERMIOS */
+
+#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
+ sort_function[0] = pcpu_sort;
+ sort_function[1] = mem_sort;
+ sort_function[2] = time_sort;
+#else
+ sort_function = mem_sort;
+#endif /* FEATURE_TOP_CPU_USAGE_PERCENTAGE */
+
+ while (1) {
+ procps_status_t *p = NULL;
+
+ /* Default to 25 lines - 5 lines for status */
+ lines = 24 - 3;
+ col = 79;
+#if ENABLE_FEATURE_USE_TERMIOS
+ get_terminal_width_height(0, &col, &lines);
+ if (lines < 5 || col < MIN_WIDTH) {
+ sleep(interval);
+ continue;
+ }
+ lines -= 3;
+#endif /* FEATURE_USE_TERMIOS */
+
+ /* read process IDs & status for all the processes */
+ while ((p = procps_scan(p, 0
+ | PSSCAN_PID
+ | PSSCAN_PPID
+ | PSSCAN_VSZ
+ | PSSCAN_STIME
+ | PSSCAN_UTIME
+ | PSSCAN_STATE
+ | PSSCAN_COMM
+ | PSSCAN_SID
+ | PSSCAN_UIDGID
+ ))) {
+ int n = ntop;
+ top = xrealloc(top, (++ntop)*sizeof(top_status_t));
+ top[n].pid = p->pid;
+ top[n].ppid = p->ppid;
+ top[n].vsz = p->vsz;
+#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
+ top[n].ticks = p->stime + p->utime;
+#endif
+ top[n].uid = p->uid;
+ strcpy(top[n].state, p->state);
+ strcpy(top[n].comm, p->comm);
+ }
+ if (ntop == 0) {
+ bb_error_msg_and_die("can't find process info in /proc");
+ }
+#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
+ if (!prev_hist_count) {
+ do_stats();
+ sleep(1);
+ clearmems();
+ continue;
+ }
+ do_stats();
+ qsort(top, ntop, sizeof(top_status_t), (void*)mult_lvl_cmp);
+#else
+ qsort(top, ntop, sizeof(top_status_t), (void*)sort_function);
+#endif /* FEATURE_TOP_CPU_USAGE_PERCENTAGE */
+ count = lines;
+ if (OPT_BATCH_MODE || count > ntop) {
+ count = ntop;
+ }
+ /* show status for each of the processes */
+ display_status(count, col);
+#if ENABLE_FEATURE_USE_TERMIOS
+ tv.tv_sec = interval;
+ tv.tv_usec = 0;
+ FD_ZERO(&readfds);
+ FD_SET(0, &readfds);
+ select(1, &readfds, NULL, NULL, &tv);
+ if (FD_ISSET(0, &readfds)) {
+ if (read(0, &c, 1) <= 0) { /* signal */
+ return EXIT_FAILURE;
+ }
+ if (c == 'q' || c == initial_settings.c_cc[VINTR])
+ break;
+ if (c == 'M') {
+#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
+ sort_function[0] = mem_sort;
+ sort_function[1] = pcpu_sort;
+ sort_function[2] = time_sort;
+#else
+ sort_function = mem_sort;
+#endif
+ }
+#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
+ if (c == 'P') {
+ sort_function[0] = pcpu_sort;
+ sort_function[1] = mem_sort;
+ sort_function[2] = time_sort;
+ }
+ if (c == 'T') {
+ sort_function[0] = time_sort;
+ sort_function[1] = mem_sort;
+ sort_function[2] = pcpu_sort;
+ }
+#endif
+ if (c == 'N') {
+#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
+ sort_function[0] = pid_sort;
+#else
+ sort_function = pid_sort;
+#endif
+ }
+ }
+ if (!--iterations)
+ break;
+#else
+ sleep(interval);
+#endif /* FEATURE_USE_TERMIOS */
+ clearmems();
+ }
+ if (ENABLE_FEATURE_CLEAN_UP)
+ clearmems();
+ putchar('\n');
+ return EXIT_SUCCESS;
+}
diff --git a/i/pc104/initrd/conf/busybox/procps/uptime.c b/i/pc104/initrd/conf/busybox/procps/uptime.c
new file mode 100644
index 0000000..3573ffe
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/procps/uptime.c
@@ -0,0 +1,60 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini uptime implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under the GPL version 2, see the file LICENSE in this tarball.
+ */
+
+/* This version of uptime doesn't display the number of users on the system,
+ * since busybox init doesn't mess with utmp. For folks using utmp that are
+ * just dying to have # of users reported, feel free to write it as some type
+ * of CONFIG_FEATURE_UTMP_SUPPORT #define
+ */
+
+/* getopt not needed */
+
+#include "busybox.h"
+
+#ifndef FSHIFT
+# define FSHIFT 16 /* nr of bits of precision */
+#endif
+#define FIXED_1 (1<<FSHIFT) /* 1.0 as fixed-point */
+#define LOAD_INT(x) ((x) >> FSHIFT)
+#define LOAD_FRAC(x) LOAD_INT(((x) & (FIXED_1-1)) * 100)
+
+
+int uptime_main(int argc, char **argv);
+int uptime_main(int argc, char **argv)
+{
+ int updays, uphours, upminutes;
+ struct sysinfo info;
+ struct tm *current_time;
+ time_t current_secs;
+
+ time(&current_secs);
+ current_time = localtime(&current_secs);
+
+ sysinfo(&info);
+
+ printf(" %02d:%02d:%02d up ",
+ current_time->tm_hour, current_time->tm_min, current_time->tm_sec);
+ updays = (int) info.uptime / (60*60*24);
+ if (updays)
+ printf("%d day%s, ", updays, (updays != 1) ? "s" : "");
+ upminutes = (int) info.uptime / 60;
+ uphours = (upminutes / 60) % 24;
+ upminutes %= 60;
+ if(uphours)
+ printf("%2d:%02d, ", uphours, upminutes);
+ else
+ printf("%d min, ", upminutes);
+
+ printf("load average: %ld.%02ld, %ld.%02ld, %ld.%02ld\n",
+ LOAD_INT(info.loads[0]), LOAD_FRAC(info.loads[0]),
+ LOAD_INT(info.loads[1]), LOAD_FRAC(info.loads[1]),
+ LOAD_INT(info.loads[2]), LOAD_FRAC(info.loads[2]));
+
+ return EXIT_SUCCESS;
+}
diff --git a/i/pc104/initrd/conf/busybox/runit/Config.in b/i/pc104/initrd/conf/busybox/runit/Config.in
new file mode 100644
index 0000000..8a7deea
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/runit/Config.in
@@ -0,0 +1,66 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+menu "Runit Utilities"
+
+config RUNSV
+ bool "runsv"
+ default n
+ help
+ runsv starts and monitors a service and optionally an appendant log
+ service.
+
+config RUNSVDIR
+ bool "runsvdir"
+ default n
+ help
+ runsvdir starts a runsv process for each subdirectory, or symlink to
+ a directory, in the services directory dir, up to a limit of 1000
+ subdirectories, and restarts a runsv process if it terminates.
+
+config SV
+ bool "sv"
+ default n
+ help
+ sv reports the current status and controls the state of services
+ monitored by the runsv supervisor.
+
+config SVLOGD
+ bool "svlogd"
+ default n
+ help
+ svlogd continuously reads log data from its standard input, optionally
+ filters log messages, and writes the data to one or more automatically
+ rotated logs.
+
+config CHPST
+ bool "chpst"
+ default n
+ help
+ chpst changes the process state according to the given options, and
+ execs specified program.
+
+config SETUIDGID
+ bool "setuidgid"
+ help
+ Sets soft resource limits as specified by options
+
+config ENVUIDGID
+ bool "envuidgid"
+ help
+ Sets $UID to account's uid and $GID to account's gid
+
+config ENVDIR
+ bool "envdir"
+ help
+ Sets various environment variables as specified by files
+ in the given directory
+
+config SOFTLIMIT
+ bool "softlimit"
+ help
+ Sets soft resource limits as specified by options
+
+endmenu
diff --git a/i/pc104/initrd/conf/busybox/runit/Kbuild b/i/pc104/initrd/conf/busybox/runit/Kbuild
new file mode 100644
index 0000000..ab9eef6
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/runit/Kbuild
@@ -0,0 +1,17 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under the GPL v2, see the file LICENSE in this tarball.
+
+lib-y:=
+lib-$(CONFIG_RUNSV) += runsv.o runit_lib.o
+lib-$(CONFIG_RUNSVDIR) += runsvdir.o runit_lib.o
+lib-$(CONFIG_SV) += sv.o runit_lib.o
+lib-$(CONFIG_SVLOGD) += svlogd.o runit_lib.o
+lib-$(CONFIG_CHPST) += chpst.o
+
+lib-$(CONFIG_ENVDIR) += chpst.o
+lib-$(CONFIG_ENVUIDGID) += chpst.o
+lib-$(CONFIG_SETUIDGID) += chpst.o
+lib-$(CONFIG_SOFTLIMIT) += chpst.o
diff --git a/i/pc104/initrd/conf/busybox/runit/chpst.c b/i/pc104/initrd/conf/busybox/runit/chpst.c
new file mode 100644
index 0000000..7b90784
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/runit/chpst.c
@@ -0,0 +1,374 @@
+/*
+Copyright (c) 2001-2006, Gerrit Pape
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. The name of the author may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/* Busyboxed by Denis Vlasenko <vda.linux@googlemail.com> */
+/* Dependencies on runit_lib.c removed */
+
+#include "busybox.h"
+
+#include <dirent.h>
+
+// Must match constants in chpst_main!
+#define OPT_verbose (option_mask32 & 0x2000)
+#define OPT_pgrp (option_mask32 & 0x4000)
+#define OPT_nostdin (option_mask32 & 0x8000)
+#define OPT_nostdout (option_mask32 & 0x10000)
+#define OPT_nostderr (option_mask32 & 0x20000)
+
+static char *set_user;
+static char *env_user;
+static const char *env_dir;
+static long limitd = -2;
+static long limits = -2;
+static long limitl = -2;
+static long limita = -2;
+static long limito = -2;
+static long limitp = -2;
+static long limitf = -2;
+static long limitc = -2;
+static long limitr = -2;
+static long limitt = -2;
+static int nicelvl;
+static const char *root;
+
+static void suidgid(char *user)
+{
+ struct bb_uidgid_t ugid;
+
+ if (!get_uidgid(&ugid, user, 1)) {
+ bb_error_msg_and_die("unknown user/group: %s", user);
+ }
+ if (setgroups(1, &ugid.gid) == -1)
+ bb_perror_msg_and_die("setgroups");
+ xsetgid(ugid.gid);
+ xsetuid(ugid.uid);
+}
+
+static void euidgid(char *user)
+{
+ struct bb_uidgid_t ugid;
+
+ if (!get_uidgid(&ugid, user, 1)) {
+ bb_error_msg_and_die("unknown user/group: %s", user);
+ }
+ xsetenv("GID", utoa(ugid.gid));
+ xsetenv("UID", utoa(ugid.uid));
+}
+
+static void edir(const char *directory_name)
+{
+ int wdir;
+ DIR *dir;
+ struct dirent *d;
+ int fd;
+
+ wdir = xopen(".", O_RDONLY | O_NDELAY);
+ xchdir(directory_name);
+ dir = opendir(".");
+ if (!dir)
+ bb_perror_msg_and_die("opendir %s", directory_name);
+ for (;;) {
+ errno = 0;
+ d = readdir(dir);
+ if (!d) {
+ if (errno)
+ bb_perror_msg_and_die("readdir %s",
+ directory_name);
+ break;
+ }
+ if (d->d_name[0] == '.') continue;
+ fd = open(d->d_name, O_RDONLY | O_NDELAY);
+ if (fd < 0) {
+ if ((errno == EISDIR) && env_dir) {
+ if (OPT_verbose)
+ bb_perror_msg("warning: %s/%s is a directory",
+ directory_name, d->d_name);
+ continue;
+ } else
+ bb_perror_msg_and_die("open %s/%s",
+ directory_name, d->d_name);
+ }
+ if (fd >= 0) {
+ char buf[256];
+ char *tail;
+ int size;
+
+ size = safe_read(fd, buf, sizeof(buf)-1);
+ if (size < 0)
+ bb_perror_msg_and_die("read %s/%s",
+ directory_name, d->d_name);
+ if (size == 0) {
+ unsetenv(d->d_name);
+ continue;
+ }
+ buf[size] = '\n';
+ tail = memchr(buf, '\n', sizeof(buf));
+ /* skip trailing whitespace */;
+ while (1) {
+ if (tail[0]==' ') tail[0] = '\0';
+ if (tail[0]=='\t') tail[0] = '\0';
+ if (tail[0]=='\n') tail[0] = '\0';
+ if (tail == buf) break;
+ tail--;
+ }
+ xsetenv(d->d_name, buf);
+ }
+ }
+ closedir(dir);
+ if (fchdir(wdir) == -1) bb_perror_msg_and_die("fchdir");
+ close(wdir);
+}
+
+static void limit(int what, long l)
+{
+ struct rlimit r;
+
+ if (getrlimit(what, &r) == -1) bb_perror_msg_and_die("getrlimit");
+ if ((l < 0) || (l > r.rlim_max))
+ r.rlim_cur = r.rlim_max;
+ else
+ r.rlim_cur = l;
+ if (setrlimit(what, &r) == -1) bb_perror_msg_and_die("setrlimit");
+}
+
+static void slimit(void)
+{
+ if (limitd >= -1) {
+#ifdef RLIMIT_DATA
+ limit(RLIMIT_DATA, limitd);
+#else
+ if (OPT_verbose) bb_error_msg("system does not support %s",
+ "RLIMIT_DATA");
+#endif
+ }
+ if (limits >= -1) {
+#ifdef RLIMIT_STACK
+ limit(RLIMIT_STACK, limits);
+#else
+ if (OPT_verbose) bb_error_msg("system does not support %s",
+ "RLIMIT_STACK");
+#endif
+ }
+ if (limitl >= -1) {
+#ifdef RLIMIT_MEMLOCK
+ limit(RLIMIT_MEMLOCK, limitl);
+#else
+ if (OPT_verbose) bb_error_msg("system does not support %s",
+ "RLIMIT_MEMLOCK");
+#endif
+ }
+ if (limita >= -1) {
+#ifdef RLIMIT_VMEM
+ limit(RLIMIT_VMEM, limita);
+#else
+#ifdef RLIMIT_AS
+ limit(RLIMIT_AS, limita);
+#else
+ if (OPT_verbose)
+ bb_error_msg("system does not support %s",
+ "RLIMIT_VMEM");
+#endif
+#endif
+ }
+ if (limito >= -1) {
+#ifdef RLIMIT_NOFILE
+ limit(RLIMIT_NOFILE, limito);
+#else
+#ifdef RLIMIT_OFILE
+ limit(RLIMIT_OFILE, limito);
+#else
+ if (OPT_verbose)
+ bb_error_msg("system does not support %s",
+ "RLIMIT_NOFILE");
+#endif
+#endif
+ }
+ if (limitp >= -1) {
+#ifdef RLIMIT_NPROC
+ limit(RLIMIT_NPROC, limitp);
+#else
+ if (OPT_verbose) bb_error_msg("system does not support %s",
+ "RLIMIT_NPROC");
+#endif
+ }
+ if (limitf >= -1) {
+#ifdef RLIMIT_FSIZE
+ limit(RLIMIT_FSIZE, limitf);
+#else
+ if (OPT_verbose) bb_error_msg("system does not support %s",
+ "RLIMIT_FSIZE");
+#endif
+ }
+ if (limitc >= -1) {
+#ifdef RLIMIT_CORE
+ limit(RLIMIT_CORE, limitc);
+#else
+ if (OPT_verbose) bb_error_msg("system does not support %s",
+ "RLIMIT_CORE");
+#endif
+ }
+ if (limitr >= -1) {
+#ifdef RLIMIT_RSS
+ limit(RLIMIT_RSS, limitr);
+#else
+ if (OPT_verbose) bb_error_msg("system does not support %s",
+ "RLIMIT_RSS");
+#endif
+ }
+ if (limitt >= -1) {
+#ifdef RLIMIT_CPU
+ limit(RLIMIT_CPU, limitt);
+#else
+ if (OPT_verbose) bb_error_msg("system does not support %s",
+ "RLIMIT_CPU");
+#endif
+ }
+}
+
+/* argv[0] */
+static void setuidgid(int, char **);
+static void envuidgid(int, char **);
+static void envdir(int, char **);
+static void softlimit(int, char **);
+
+int chpst_main(int argc, char **argv);
+int chpst_main(int argc, char **argv)
+{
+ if (applet_name[3] == 'd') envdir(argc, argv);
+ if (applet_name[1] == 'o') softlimit(argc, argv);
+ if (applet_name[0] == 's') setuidgid(argc, argv);
+ if (applet_name[0] == 'e') envuidgid(argc, argv);
+ // otherwise we are chpst
+
+ {
+ char *m,*d,*o,*p,*f,*c,*r,*t,*n;
+ getopt32(argc, argv, "+u:U:e:m:d:o:p:f:c:r:t:/:n:vP012",
+ &set_user,&env_user,&env_dir,
+ &m,&d,&o,&p,&f,&c,&r,&t,&root,&n);
+ // if (option_mask32 & 0x1) // -u
+ // if (option_mask32 & 0x2) // -U
+ // if (option_mask32 & 0x4) // -e
+ if (option_mask32 & 0x8) limits = limitl = limita = limitd = xatoul(m); // -m
+ if (option_mask32 & 0x10) limitd = xatoul(d); // -d
+ if (option_mask32 & 0x20) limito = xatoul(o); // -o
+ if (option_mask32 & 0x40) limitp = xatoul(p); // -p
+ if (option_mask32 & 0x80) limitf = xatoul(f); // -f
+ if (option_mask32 & 0x100) limitc = xatoul(c); // -c
+ if (option_mask32 & 0x200) limitr = xatoul(r); // -r
+ if (option_mask32 & 0x400) limitt = xatoul(t); // -t
+ // if (option_mask32 & 0x800) // -/
+ if (option_mask32 & 0x1000) nicelvl = xatoi(n); // -n
+ // The below consts should match #defines at top!
+ //if (option_mask32 & 0x2000) OPT_verbose = 1; // -v
+ //if (option_mask32 & 0x4000) OPT_pgrp = 1; // -P
+ //if (option_mask32 & 0x8000) OPT_nostdin = 1; // -0
+ //if (option_mask32 & 0x10000) OPT_nostdout = 1; // -1
+ //if (option_mask32 & 0x20000) OPT_nostderr = 1; // -2
+ }
+ argv += optind;
+ if (!argv || !*argv) bb_show_usage();
+
+ if (OPT_pgrp) setsid();
+ if (env_dir) edir(env_dir);
+ if (root) {
+ xchdir(root);
+ if (chroot(".") == -1)
+ bb_perror_msg_and_die("chroot");
+ }
+ slimit();
+ if (nicelvl) {
+ errno = 0;
+ if (nice(nicelvl) == -1)
+ bb_perror_msg_and_die("nice");
+ }
+ if (env_user) euidgid(env_user);
+ if (set_user) suidgid(set_user);
+ if (OPT_nostdin) close(0);
+ if (OPT_nostdout) close(1);
+ if (OPT_nostderr) close(2);
+ BB_EXECVP(argv[0], argv);
+ bb_perror_msg_and_die("exec %s", argv[0]);
+}
+
+static void setuidgid(int argc, char **argv)
+{
+ const char *account;
+
+ account = *++argv;
+ if (!account) bb_show_usage();
+ if (!*++argv) bb_show_usage();
+ suidgid((char*)account);
+ BB_EXECVP(argv[0], argv);
+ bb_perror_msg_and_die("exec %s", argv[0]);
+}
+
+static void envuidgid(int argc, char **argv)
+{
+ const char *account;
+
+ account = *++argv;
+ if (!account) bb_show_usage();
+ if (!*++argv) bb_show_usage();
+ euidgid((char*)account);
+ BB_EXECVP(argv[0], argv);
+ bb_perror_msg_and_die("exec %s", argv[0]);
+}
+
+static void envdir(int argc, char **argv)
+{
+ const char *dir;
+
+ dir = *++argv;
+ if (!dir) bb_show_usage();
+ if (!*++argv) bb_show_usage();
+ edir(dir);
+ BB_EXECVP(argv[0], argv);
+ bb_perror_msg_and_die("exec %s", argv[0]);
+}
+
+static void softlimit(int argc, char **argv)
+{
+ char *a,*c,*d,*f,*l,*m,*o,*p,*r,*s,*t;
+ getopt32(argc, argv, "+a:c:d:f:l:m:o:p:r:s:t:",
+ &a,&c,&d,&f,&l,&m,&o,&p,&r,&s,&t);
+ if (option_mask32 & 0x001) limita = xatoul(a); // -a
+ if (option_mask32 & 0x002) limitc = xatoul(c); // -c
+ if (option_mask32 & 0x004) limitd = xatoul(d); // -d
+ if (option_mask32 & 0x008) limitf = xatoul(f); // -f
+ if (option_mask32 & 0x010) limitl = xatoul(l); // -l
+ if (option_mask32 & 0x020) limits = limitl = limita = limitd = xatoul(m); // -m
+ if (option_mask32 & 0x040) limito = xatoul(o); // -o
+ if (option_mask32 & 0x080) limitp = xatoul(p); // -p
+ if (option_mask32 & 0x100) limitr = xatoul(r); // -r
+ if (option_mask32 & 0x200) limits = xatoul(s); // -s
+ if (option_mask32 & 0x400) limitt = xatoul(t); // -t
+ argv += optind;
+ if (!argv[0]) bb_show_usage();
+ slimit();
+ BB_EXECVP(argv[0], argv);
+ bb_perror_msg_and_die("exec %s", argv[0]);
+}
diff --git a/i/pc104/initrd/conf/busybox/runit/runit_lib.c b/i/pc104/initrd/conf/busybox/runit/runit_lib.c
new file mode 100644
index 0000000..0830314
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/runit/runit_lib.c
@@ -0,0 +1,500 @@
+/*
+Copyright (c) 2001-2006, Gerrit Pape
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. The name of the author may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/* Busyboxed by Denis Vlasenko <vda.linux@googlemail.com> */
+/* Collected into one file from runit's many tiny files */
+/* TODO: review, eliminate unneeded stuff, move good stuff to libbb */
+
+#include <sys/poll.h>
+#include <sys/file.h>
+#include "libbb.h"
+#include "runit_lib.h"
+
+/*** byte_chr.c ***/
+
+unsigned byte_chr(char *s,unsigned n,int c)
+{
+ char ch;
+ char *t;
+
+ ch = c;
+ t = s;
+ for (;;) {
+ if (!n) break;
+ if (*t == ch) break;
+ ++t;
+ --n;
+ }
+ return t - s;
+}
+
+
+/*** coe.c ***/
+
+int coe(int fd)
+{
+ return fcntl(fd,F_SETFD,FD_CLOEXEC);
+}
+
+
+/*** fd_copy.c ***/
+
+int fd_copy(int to,int from)
+{
+ if (to == from)
+ return 0;
+ if (fcntl(from,F_GETFL,0) == -1)
+ return -1;
+ close(to);
+ if (fcntl(from,F_DUPFD,to) == -1)
+ return -1;
+ return 0;
+}
+
+
+/*** fd_move.c ***/
+
+int fd_move(int to,int from)
+{
+ if (to == from)
+ return 0;
+ if (fd_copy(to,from) == -1)
+ return -1;
+ close(from);
+ return 0;
+}
+
+
+/*** fmt_ptime.c ***/
+
+void fmt_ptime30nul(char *s, struct taia *ta) {
+ struct tm *t;
+ unsigned long u;
+
+ if (ta->sec.x < 4611686018427387914ULL)
+ return; /* impossible? */
+ u = ta->sec.x -4611686018427387914ULL;
+ t = gmtime((time_t*)&u);
+ if (!t)
+ return; /* huh? */
+ //fmt_ulong(s, 1900 + t->tm_year);
+ //s[4] = '-'; fmt_uint0(&s[5], t->tm_mon+1, 2);
+ //s[7] = '-'; fmt_uint0(&s[8], t->tm_mday, 2);
+ //s[10] = '_'; fmt_uint0(&s[11], t->tm_hour, 2);
+ //s[13] = ':'; fmt_uint0(&s[14], t->tm_min, 2);
+ //s[16] = ':'; fmt_uint0(&s[17], t->tm_sec, 2);
+ //s[19] = '.'; fmt_uint0(&s[20], ta->nano, 9);
+ sprintf(s, "%04u-%02u-%02u_%02u:%02u:%02u.%09u",
+ (unsigned)(1900 + t->tm_year),
+ (unsigned)(t->tm_mon+1),
+ (unsigned)(t->tm_mday),
+ (unsigned)(t->tm_hour),
+ (unsigned)(t->tm_min),
+ (unsigned)(t->tm_sec),
+ (unsigned)(ta->nano)
+ );
+ /* 4+1 + 2+1 + 2+1 + 2+1 + 2+1 + 2+1 + 9 = */
+ /* 5 + 3 + 3 + 3 + 3 + 3 + 9 = */
+ /* 20 (up to '.' inclusive) + 9 (not including '\0') */
+}
+
+unsigned fmt_taia25(char *s, struct taia *t) {
+ static char pack[TAIA_PACK];
+
+ taia_pack(pack, t);
+ *s++ = '@';
+ bin2hex(s, pack, 12);
+ return 25;
+}
+
+
+/*** tai_pack.c ***/
+
+static /* as it isn't used anywhere else */
+void tai_pack(char *s,const struct tai *t)
+{
+ uint64_t x;
+
+ x = t->x;
+ s[7] = x & 255; x >>= 8;
+ s[6] = x & 255; x >>= 8;
+ s[5] = x & 255; x >>= 8;
+ s[4] = x & 255; x >>= 8;
+ s[3] = x & 255; x >>= 8;
+ s[2] = x & 255; x >>= 8;
+ s[1] = x & 255; x >>= 8;
+ s[0] = x;
+}
+
+
+#ifdef UNUSED
+/*** tai_sub.c ***/
+
+void tai_sub(struct tai *t, const struct tai *u, const struct tai *v)
+{
+ t->x = u->x - v->x;
+}
+#endif
+
+
+/*** tai_unpack.c ***/
+
+void tai_unpack(const char *s,struct tai *t)
+{
+ uint64_t x;
+
+ x = (unsigned char) s[0];
+ x <<= 8; x += (unsigned char) s[1];
+ x <<= 8; x += (unsigned char) s[2];
+ x <<= 8; x += (unsigned char) s[3];
+ x <<= 8; x += (unsigned char) s[4];
+ x <<= 8; x += (unsigned char) s[5];
+ x <<= 8; x += (unsigned char) s[6];
+ x <<= 8; x += (unsigned char) s[7];
+ t->x = x;
+}
+
+
+/*** taia_add.c ***/
+
+void taia_add(struct taia *t,const struct taia *u,const struct taia *v)
+{
+ t->sec.x = u->sec.x + v->sec.x;
+ t->nano = u->nano + v->nano;
+ t->atto = u->atto + v->atto;
+ if (t->atto > 999999999UL) {
+ t->atto -= 1000000000UL;
+ ++t->nano;
+ }
+ if (t->nano > 999999999UL) {
+ t->nano -= 1000000000UL;
+ ++t->sec.x;
+ }
+}
+
+
+/*** taia_less.c ***/
+
+int taia_less(const struct taia *t, const struct taia *u)
+{
+ if (t->sec.x < u->sec.x) return 1;
+ if (t->sec.x > u->sec.x) return 0;
+ if (t->nano < u->nano) return 1;
+ if (t->nano > u->nano) return 0;
+ return t->atto < u->atto;
+}
+
+
+/*** taia_now.c ***/
+
+void taia_now(struct taia *t)
+{
+ struct timeval now;
+ gettimeofday(&now, NULL);
+ tai_unix(&t->sec, now.tv_sec);
+ t->nano = 1000 * now.tv_usec + 500;
+ t->atto = 0;
+}
+
+
+/*** taia_pack.c ***/
+
+void taia_pack(char *s, const struct taia *t)
+{
+ unsigned long x;
+
+ tai_pack(s, &t->sec);
+ s += 8;
+
+ x = t->atto;
+ s[7] = x & 255; x >>= 8;
+ s[6] = x & 255; x >>= 8;
+ s[5] = x & 255; x >>= 8;
+ s[4] = x;
+ x = t->nano;
+ s[3] = x & 255; x >>= 8;
+ s[2] = x & 255; x >>= 8;
+ s[1] = x & 255; x >>= 8;
+ s[0] = x;
+}
+
+
+/*** taia_sub.c ***/
+
+void taia_sub(struct taia *t, const struct taia *u, const struct taia *v)
+{
+ unsigned long unano = u->nano;
+ unsigned long uatto = u->atto;
+
+ t->sec.x = u->sec.x - v->sec.x;
+ t->nano = unano - v->nano;
+ t->atto = uatto - v->atto;
+ if (t->atto > uatto) {
+ t->atto += 1000000000UL;
+ --t->nano;
+ }
+ if (t->nano > unano) {
+ t->nano += 1000000000UL;
+ --t->sec.x;
+ }
+}
+
+
+/*** taia_uint.c ***/
+
+/* XXX: breaks tai encapsulation */
+
+void taia_uint(struct taia *t, unsigned s)
+{
+ t->sec.x = s;
+ t->nano = 0;
+ t->atto = 0;
+}
+
+
+/*** iopause.c ***/
+
+static
+uint64_t taia2millisec(const struct taia *t)
+{
+ return (t->sec.x * 1000) + (t->nano / 1000000);
+}
+
+
+void iopause(iopause_fd *x, unsigned len, struct taia *deadline, struct taia *stamp)
+{
+ int millisecs;
+ int i;
+
+ if (taia_less(deadline, stamp))
+ millisecs = 0;
+ else {
+ uint64_t m;
+ struct taia t;
+ t = *stamp;
+ taia_sub(&t, deadline, &t);
+ millisecs = m = taia2millisec(&t);
+ if (m > 1000) millisecs = 1000;
+ millisecs += 20;
+ }
+
+ for (i = 0; i < len; ++i)
+ x[i].revents = 0;
+
+ poll(x, len, millisecs);
+ /* XXX: some kernels apparently need x[0] even if len is 0 */
+ /* XXX: how to handle EAGAIN? are kernels really this dumb? */
+ /* XXX: how to handle EINVAL? when exactly can this happen? */
+}
+
+
+/*** lock_ex.c ***/
+
+int lock_ex(int fd)
+{
+ return flock(fd,LOCK_EX);
+}
+
+
+/*** lock_exnb.c ***/
+
+int lock_exnb(int fd)
+{
+ return flock(fd,LOCK_EX | LOCK_NB);
+}
+
+
+/*** open_append.c ***/
+
+int open_append(const char *fn)
+{
+ return open(fn, O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
+}
+
+
+/*** open_read.c ***/
+
+int open_read(const char *fn)
+{
+ return open(fn, O_RDONLY|O_NDELAY);
+}
+
+
+/*** open_trunc.c ***/
+
+int open_trunc(const char *fn)
+{
+ return open(fn,O_WRONLY | O_NDELAY | O_TRUNC | O_CREAT,0644);
+}
+
+
+/*** open_write.c ***/
+
+int open_write(const char *fn)
+{
+ return open(fn, O_WRONLY|O_NDELAY);
+}
+
+
+/*** pmatch.c ***/
+
+unsigned pmatch(const char *p, const char *s, unsigned len) {
+ for (;;) {
+ char c = *p++;
+ if (!c) return !len;
+ switch (c) {
+ case '*':
+ if (!(c = *p)) return 1;
+ for (;;) {
+ if (!len) return 0;
+ if (*s == c) break;
+ ++s; --len;
+ }
+ continue;
+ case '+':
+ if ((c = *p++) != *s) return 0;
+ for (;;) {
+ if (!len) return 1;
+ if (*s != c) break;
+ ++s; --len;
+ }
+ continue;
+ /*
+ case '?':
+ if (*p == '?') {
+ if (*s != '?') return 0;
+ ++p;
+ }
+ ++s; --len;
+ continue;
+ */
+ default:
+ if (!len) return 0;
+ if (*s != c) return 0;
+ ++s; --len;
+ continue;
+ }
+ }
+ return 0;
+}
+
+
+#ifdef UNUSED
+/*** seek_set.c ***/
+
+int seek_set(int fd,seek_pos pos)
+{
+ if (lseek(fd,(off_t) pos,SEEK_SET) == -1) return -1; return 0;
+}
+#endif
+
+
+/*** sig_block.c ***/
+
+void sig_block(int sig)
+{
+ sigset_t ss;
+ sigemptyset(&ss);
+ sigaddset(&ss, sig);
+ sigprocmask(SIG_BLOCK, &ss, NULL);
+}
+
+void sig_unblock(int sig)
+{
+ sigset_t ss;
+ sigemptyset(&ss);
+ sigaddset(&ss, sig);
+ sigprocmask(SIG_UNBLOCK, &ss, NULL);
+}
+
+void sig_blocknone(void)
+{
+ sigset_t ss;
+ sigemptyset(&ss);
+ sigprocmask(SIG_SETMASK, &ss, NULL);
+}
+
+
+/*** sig_catch.c ***/
+
+void sig_catch(int sig,void (*f)(int))
+{
+ struct sigaction sa;
+ sa.sa_handler = f;
+ sa.sa_flags = 0;
+ sigemptyset(&sa.sa_mask);
+ sigaction(sig,&sa, NULL);
+}
+
+
+/*** sig_pause.c ***/
+
+void sig_pause(void)
+{
+ sigset_t ss;
+ sigemptyset(&ss);
+ sigsuspend(&ss);
+}
+
+
+/*** str_chr.c ***/
+
+unsigned str_chr(const char *s,int c)
+{
+ char ch;
+ const char *t;
+
+ ch = c;
+ t = s;
+ for (;;) {
+ if (!*t) break;
+ if (*t == ch) break;
+ ++t;
+ }
+ return t - s;
+}
+
+
+/*** wait_nohang.c ***/
+
+int wait_nohang(int *wstat)
+{
+ return waitpid(-1, wstat, WNOHANG);
+}
+
+
+/*** wait_pid.c ***/
+
+int wait_pid(int *wstat, int pid)
+{
+ int r;
+
+ do
+ r = waitpid(pid, wstat, 0);
+ while ((r == -1) && (errno == EINTR));
+ return r;
+}
diff --git a/i/pc104/initrd/conf/busybox/runit/runit_lib.h b/i/pc104/initrd/conf/busybox/runit/runit_lib.h
new file mode 100644
index 0000000..7b268e2
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/runit/runit_lib.h
@@ -0,0 +1,160 @@
+/*
+Copyright (c) 2001-2006, Gerrit Pape
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. The name of the author may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/*** byte.h ***/
+
+extern unsigned byte_chr(char *s,unsigned n,int c);
+
+
+/*** coe.h ***/
+
+extern int coe(int);
+
+
+/*** direntry.h ***/
+
+#define direntry struct dirent
+
+
+/*** fd.h ***/
+
+extern int fd_copy(int,int);
+extern int fd_move(int,int);
+
+
+/*** tai.h ***/
+
+struct tai {
+ uint64_t x;
+};
+
+#define tai_unix(t,u) ((void) ((t)->x = 4611686018427387914ULL + (uint64_t) (u)))
+
+#define TAI_PACK 8
+//extern void tai_pack(char *,const struct tai *);
+extern void tai_unpack(const char *,struct tai *);
+
+extern void tai_uint(struct tai *,unsigned);
+
+
+/*** taia.h ***/
+
+struct taia {
+ struct tai sec;
+ unsigned long nano; /* 0...999999999 */
+ unsigned long atto; /* 0...999999999 */
+};
+
+//extern void taia_tai(const struct taia *,struct tai *);
+
+extern void taia_now(struct taia *);
+
+extern void taia_add(struct taia *,const struct taia *,const struct taia *);
+extern void taia_addsec(struct taia *,const struct taia *,int);
+extern void taia_sub(struct taia *,const struct taia *,const struct taia *);
+extern void taia_half(struct taia *,const struct taia *);
+extern int taia_less(const struct taia *,const struct taia *);
+
+#define TAIA_PACK 16
+extern void taia_pack(char *,const struct taia *);
+//extern void taia_unpack(const char *,struct taia *);
+
+//#define TAIA_FMTFRAC 19
+//extern unsigned taia_fmtfrac(char *,const struct taia *);
+
+extern void taia_uint(struct taia *,unsigned);
+
+
+/*** fmt_ptime.h ***/
+
+#define FMT_PTIME 30
+
+/* NUL terminated */
+extern void fmt_ptime30nul(char *, struct taia *);
+/* NOT terminated! */
+extern unsigned fmt_taia25(char *, struct taia *);
+
+
+/*** iopause.h ***/
+
+typedef struct pollfd iopause_fd;
+#define IOPAUSE_READ POLLIN
+#define IOPAUSE_WRITE POLLOUT
+
+extern void iopause(iopause_fd *,unsigned,struct taia *,struct taia *);
+
+
+/*** lock.h ***/
+
+extern int lock_ex(int);
+extern int lock_un(int);
+extern int lock_exnb(int);
+
+
+/*** open.h ***/
+
+extern int open_read(const char *);
+extern int open_excl(const char *);
+extern int open_append(const char *);
+extern int open_trunc(const char *);
+extern int open_write(const char *);
+
+
+/*** pmatch.h ***/
+
+extern unsigned pmatch(const char *, const char *, unsigned);
+
+
+/*** sig.h ***/
+
+extern void sig_catch(int,void (*)(int));
+#define sig_ignore(s) (sig_catch((s), SIG_IGN))
+#define sig_uncatch(s) (sig_catch((s), SIG_DFL))
+
+extern void sig_block(int);
+extern void sig_unblock(int);
+extern void sig_blocknone(void);
+extern void sig_pause(void);
+
+
+/*** str.h ***/
+
+extern unsigned str_chr(const char *,int); /* never returns NULL */
+
+#define str_diff(s,t) strcmp((s), (t))
+#define str_equal(s,t) (!strcmp((s), (t)))
+
+
+/*** wait.h ***/
+
+extern int wait_pid(int *wstat, int pid);
+extern int wait_nohang(int *wstat);
+
+#define wait_crashed(w) ((w) & 127)
+#define wait_exitcode(w) ((w) >> 8)
+#define wait_stopsig(w) ((w) >> 8)
+#define wait_stopped(w) (((w) & 127) == 127)
diff --git a/i/pc104/initrd/conf/busybox/runit/runsv.c b/i/pc104/initrd/conf/busybox/runit/runsv.c
new file mode 100644
index 0000000..cd80685
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/runit/runsv.c
@@ -0,0 +1,633 @@
+/*
+Copyright (c) 2001-2006, Gerrit Pape
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. The name of the author may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/* Busyboxed by Denis Vlasenko <vda.linux@googlemail.com> */
+/* TODO: depends on runit_lib.c - review and reduce/eliminate */
+
+#include <sys/poll.h>
+#include <sys/file.h>
+#include "busybox.h"
+#include "runit_lib.h"
+
+static int selfpipe[2];
+
+/* state */
+#define S_DOWN 0
+#define S_RUN 1
+#define S_FINISH 2
+/* ctrl */
+#define C_NOOP 0
+#define C_TERM 1
+#define C_PAUSE 2
+/* want */
+#define W_UP 0
+#define W_DOWN 1
+#define W_EXIT 2
+
+struct svdir {
+ int pid;
+ smallint state;
+ smallint ctrl;
+ smallint want;
+ smallint islog;
+ struct taia start;
+ int fdlock;
+ int fdcontrol;
+ int fdcontrolwrite;
+};
+static struct svdir svd[2];
+
+static smallint sigterm;
+static smallint haslog;
+static smallint pidchanged = 1;
+static int logpipe[2];
+static char *dir;
+
+#define usage() bb_show_usage()
+
+static void fatal2_cannot(const char *m1, const char *m2)
+{
+ bb_perror_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2);
+ /* was exiting 111 */
+}
+static void fatal_cannot(const char *m)
+{
+ fatal2_cannot(m, "");
+ /* was exiting 111 */
+}
+static void fatal2x_cannot(const char *m1, const char *m2)
+{
+ bb_error_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2);
+ /* was exiting 111 */
+}
+static void warn_cannot(const char *m)
+{
+ bb_perror_msg("%s: warning: cannot %s", dir, m);
+}
+static void warnx_cannot(const char *m)
+{
+ bb_error_msg("%s: warning: cannot %s", dir, m);
+}
+
+static void s_child(int sig_no)
+{
+ write(selfpipe[1], "", 1);
+}
+
+static void s_term(int sig_no)
+{
+ sigterm = 1;
+ write(selfpipe[1], "", 1); /* XXX */
+}
+
+static char *add_str(char *p, const char *to_add)
+{
+ while ((*p = *to_add) != '\0') {
+ p++;
+ to_add++;
+ }
+ return p;
+}
+
+static int open_trunc_or_warn(const char *name)
+{
+ int fd = open_trunc(name);
+ if (fd < 0)
+ bb_perror_msg("%s: warning: cannot open %s",
+ dir, name);
+ return fd;
+}
+
+static int rename_or_warn(const char *old, const char *new)
+{
+ if (rename(old, new) == -1) {
+ bb_perror_msg("%s: warning: cannot rename %s to %s",
+ dir, old, new);
+ return -1;
+ }
+ return 0;
+}
+
+static void update_status(struct svdir *s)
+{
+ unsigned long l;
+ int fd;
+ char status[20];
+
+ /* pid */
+ if (pidchanged) {
+ fd = open_trunc_or_warn("supervise/pid.new");
+ if (fd < 0)
+ return;
+ if (s->pid) {
+ char spid[sizeof(int)*3 + 2];
+ int size = sprintf(spid, "%u\n", (unsigned)s->pid);
+ write(fd, spid, size);
+ }
+ close(fd);
+ if (rename_or_warn("supervise/pid.new",
+ s->islog ? "log/supervise/pid" : "log/supervise/pid"+4))
+ return;
+ pidchanged = 0;
+ }
+
+ /* stat */
+ fd = open_trunc_or_warn("supervise/stat.new");
+ if (fd < -1)
+ return;
+
+ {
+ char stat_buf[sizeof("finish, paused, got TERM, want down\n")];
+ char *p = stat_buf;
+ switch (s->state) {
+ case S_DOWN:
+ p = add_str(p, "down");
+ break;
+ case S_RUN:
+ p = add_str(p, "run");
+ break;
+ case S_FINISH:
+ p = add_str(p, "finish");
+ break;
+ }
+ if (s->ctrl & C_PAUSE) p = add_str(p, ", paused");
+ if (s->ctrl & C_TERM) p = add_str(p, ", got TERM");
+ if (s->state != S_DOWN)
+ switch (s->want) {
+ case W_DOWN:
+ p = add_str(p, ", want down");
+ break;
+ case W_EXIT:
+ p = add_str(p, ", want exit");
+ break;
+ }
+ *p++ = '\n';
+ write(fd, stat_buf, p - stat_buf);
+ close(fd);
+ }
+
+ rename_or_warn("supervise/stat.new",
+ s->islog ? "log/supervise/stat" : "log/supervise/stat"+4);
+
+ /* supervise compatibility */
+ taia_pack(status, &s->start);
+ l = (unsigned long)s->pid;
+ status[12] = l; l >>=8;
+ status[13] = l; l >>=8;
+ status[14] = l; l >>=8;
+ status[15] = l;
+ if (s->ctrl & C_PAUSE)
+ status[16] = 1;
+ else
+ status[16] = 0;
+ if (s->want == W_UP)
+ status[17] = 'u';
+ else
+ status[17] = 'd';
+ if (s->ctrl & C_TERM)
+ status[18] = 1;
+ else
+ status[18] = 0;
+ status[19] = s->state;
+ fd = open_trunc_or_warn("supervise/status.new");
+ if (fd < 0)
+ return;
+ l = write(fd, status, sizeof(status));
+ if (l < 0) {
+ warn_cannot("write supervise/status.new");
+ close(fd);
+ unlink("supervise/status.new");
+ return;
+ }
+ close(fd);
+ if (l < sizeof(status)) {
+ warnx_cannot("write supervise/status.new: partial write");
+ return;
+ }
+ rename_or_warn("supervise/status.new",
+ s->islog ? "log/supervise/status" : "log/supervise/status"+4);
+}
+
+static unsigned custom(struct svdir *s, char c)
+{
+ int pid;
+ int w;
+ char a[10];
+ struct stat st;
+ char *prog[2];
+
+ if (s->islog) return 0;
+ strcpy(a, "control/?");
+ a[8] = c;
+ if (stat(a, &st) == 0) {
+ if (st.st_mode & S_IXUSR) {
+ pid = fork();
+ if (pid == -1) {
+ warn_cannot("fork for control/?");
+ return 0;
+ }
+ if (!pid) {
+ if (haslog && fd_copy(1, logpipe[1]) == -1)
+ warn_cannot("setup stdout for control/?");
+ prog[0] = a;
+ prog[1] = NULL;
+ execve(a, prog, environ);
+ fatal_cannot("run control/?");
+ }
+ while (wait_pid(&w, pid) == -1) {
+ if (errno == EINTR) continue;
+ warn_cannot("wait for child control/?");
+ return 0;
+ }
+ return !wait_exitcode(w);
+ }
+ } else {
+ if (errno != ENOENT)
+ warn_cannot("stat control/?");
+ }
+ return 0;
+}
+
+static void stopservice(struct svdir *s)
+{
+ if (s->pid && !custom(s, 't')) {
+ kill(s->pid, SIGTERM);
+ s->ctrl |= C_TERM;
+ update_status(s);
+ }
+ if (s->want == W_DOWN) {
+ kill(s->pid, SIGCONT);
+ custom(s, 'd');
+ return;
+ }
+ if (s->want == W_EXIT) {
+ kill(s->pid, SIGCONT);
+ custom(s, 'x');
+ }
+}
+
+static void startservice(struct svdir *s)
+{
+ int p;
+ char *run[2];
+
+ if (s->state == S_FINISH)
+ run[0] = (char*)"./finish";
+ else {
+ run[0] = (char*)"./run";
+ custom(s, 'u');
+ }
+ run[1] = NULL;
+
+ if (s->pid != 0) stopservice(s); /* should never happen */
+ while ((p = fork()) == -1) {
+ warn_cannot("fork, sleeping");
+ sleep(5);
+ }
+ if (p == 0) {
+ /* child */
+ if (haslog) {
+ if (s->islog) {
+ if (fd_copy(0, logpipe[0]) == -1)
+ fatal_cannot("setup filedescriptor for ./log/run");
+ close(logpipe[1]);
+ if (chdir("./log") == -1)
+ fatal_cannot("change directory to ./log");
+ } else {
+ if (fd_copy(1, logpipe[1]) == -1)
+ fatal_cannot("setup filedescriptor for ./run");
+ close(logpipe[0]);
+ }
+ }
+ sig_uncatch(SIGCHLD);
+ sig_unblock(SIGCHLD);
+ sig_uncatch(SIGTERM);
+ sig_unblock(SIGTERM);
+ execve(*run, run, environ);
+ fatal2_cannot(s->islog ? "start log/" : "start ", *run);
+ }
+ if (s->state != S_FINISH) {
+ taia_now(&s->start);
+ s->state = S_RUN;
+ }
+ s->pid = p;
+ pidchanged = 1;
+ s->ctrl = C_NOOP;
+ update_status(s);
+}
+
+static int ctrl(struct svdir *s, char c)
+{
+ int sig;
+
+ switch (c) {
+ case 'd': /* down */
+ s->want = W_DOWN;
+ update_status(s);
+ if (s->pid && s->state != S_FINISH) stopservice(s);
+ break;
+ case 'u': /* up */
+ s->want = W_UP;
+ update_status(s);
+ if (s->pid == 0) startservice(s);
+ break;
+ case 'x': /* exit */
+ if (s->islog) break;
+ s->want = W_EXIT;
+ update_status(s);
+ /* FALLTHROUGH */
+ case 't': /* sig term */
+ if (s->pid && s->state != S_FINISH) stopservice(s);
+ break;
+ case 'k': /* sig kill */
+ if (s->pid && !custom(s, c)) kill(s->pid, SIGKILL);
+ s->state = S_DOWN;
+ break;
+ case 'p': /* sig pause */
+ if (s->pid && !custom(s, c)) kill(s->pid, SIGSTOP);
+ s->ctrl |= C_PAUSE;
+ update_status(s);
+ break;
+ case 'c': /* sig cont */
+ if (s->pid && !custom(s, c)) kill(s->pid, SIGCONT);
+ if (s->ctrl & C_PAUSE) s->ctrl &= ~C_PAUSE;
+ update_status(s);
+ break;
+ case 'o': /* once */
+ s->want = W_DOWN;
+ update_status(s);
+ if (!s->pid) startservice(s);
+ break;
+ case 'a': /* sig alarm */
+ sig = SIGALRM;
+ goto sendsig;
+ case 'h': /* sig hup */
+ sig = SIGHUP;
+ goto sendsig;
+ case 'i': /* sig int */
+ sig = SIGINT;
+ goto sendsig;
+ case 'q': /* sig quit */
+ sig = SIGQUIT;
+ goto sendsig;
+ case '1': /* sig usr1 */
+ sig = SIGUSR1;
+ goto sendsig;
+ case '2': /* sig usr2 */
+ sig = SIGUSR2;
+ goto sendsig;
+ }
+ return 1;
+ sendsig:
+ if (s->pid && !custom(s, c))
+ kill(s->pid, sig);
+ return 1;
+}
+
+int runsv_main(int argc, char **argv);
+int runsv_main(int argc, char **argv)
+{
+ struct stat s;
+ int fd;
+ int r;
+ char buf[256];
+
+ if (!argv[1] || argv[2]) usage();
+ dir = argv[1];
+
+ if (pipe(selfpipe) == -1) fatal_cannot("create selfpipe");
+ coe(selfpipe[0]);
+ coe(selfpipe[1]);
+ ndelay_on(selfpipe[0]);
+ ndelay_on(selfpipe[1]);
+
+ sig_block(SIGCHLD);
+ sig_catch(SIGCHLD, s_child);
+ sig_block(SIGTERM);
+ sig_catch(SIGTERM, s_term);
+
+ xchdir(dir);
+ /* bss: svd[0].pid = 0; */
+ if (S_DOWN) svd[0].state = S_DOWN; /* otherwise already 0 (bss) */
+ if (C_NOOP) svd[0].ctrl = C_NOOP;
+ if (W_UP) svd[0].want = W_UP;
+ /* bss: svd[0].islog = 0; */
+ /* bss: svd[1].pid = 0; */
+ taia_now(&svd[0].start);
+ if (stat("down", &s) != -1) svd[0].want = W_DOWN;
+
+ if (stat("log", &s) == -1) {
+ if (errno != ENOENT)
+ warn_cannot("stat ./log");
+ } else {
+ if (!S_ISDIR(s.st_mode))
+ warnx_cannot("stat log/down: log is not a directory");
+ else {
+ haslog = 1;
+ svd[1].state = S_DOWN;
+ svd[1].ctrl = C_NOOP;
+ svd[1].want = W_UP;
+ svd[1].islog = 1;
+ taia_now(&svd[1].start);
+ if (stat("log/down", &s) != -1)
+ svd[1].want = W_DOWN;
+ if (pipe(logpipe) == -1)
+ fatal_cannot("create log pipe");
+ coe(logpipe[0]);
+ coe(logpipe[1]);
+ }
+ }
+
+ if (mkdir("supervise", 0700) == -1) {
+ r = readlink("supervise", buf, sizeof(buf));
+ if (r != -1) {
+ if (r == sizeof(buf))
+ fatal2x_cannot("readlink ./supervise", ": name too long");
+ buf[r] = 0;
+ mkdir(buf, 0700);
+ } else {
+ if ((errno != ENOENT) && (errno != EINVAL))
+ fatal_cannot("readlink ./supervise");
+ }
+ }
+ svd[0].fdlock = xopen3("log/supervise/lock"+4,
+ O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
+ if (lock_exnb(svd[0].fdlock) == -1)
+ fatal_cannot("lock supervise/lock");
+ coe(svd[0].fdlock);
+ if (haslog) {
+ if (mkdir("log/supervise", 0700) == -1) {
+ r = readlink("log/supervise", buf, 256);
+ if (r != -1) {
+ if (r == 256)
+ fatal2x_cannot("readlink ./log/supervise", ": name too long");
+ buf[r] = 0;
+ fd = xopen(".", O_RDONLY|O_NDELAY);
+ xchdir("./log");
+ mkdir(buf, 0700);
+ if (fchdir(fd) == -1)
+ fatal_cannot("change back to service directory");
+ close(fd);
+ }
+ else {
+ if ((errno != ENOENT) && (errno != EINVAL))
+ fatal_cannot("readlink ./log/supervise");
+ }
+ }
+ svd[1].fdlock = xopen3("log/supervise/lock",
+ O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
+ if (lock_ex(svd[1].fdlock) == -1)
+ fatal_cannot("lock log/supervise/lock");
+ coe(svd[1].fdlock);
+ }
+
+ mkfifo("log/supervise/control"+4, 0600);
+ svd[0].fdcontrol = xopen("log/supervise/control"+4, O_RDONLY|O_NDELAY);
+ coe(svd[0].fdcontrol);
+ svd[0].fdcontrolwrite = xopen("log/supervise/control"+4, O_WRONLY|O_NDELAY);
+ coe(svd[0].fdcontrolwrite);
+ update_status(&svd[0]);
+ if (haslog) {
+ mkfifo("log/supervise/control", 0600);
+ svd[1].fdcontrol = xopen("log/supervise/control", O_RDONLY|O_NDELAY);
+ coe(svd[1].fdcontrol);
+ svd[1].fdcontrolwrite = xopen("log/supervise/control", O_WRONLY|O_NDELAY);
+ coe(svd[1].fdcontrolwrite);
+ update_status(&svd[1]);
+ }
+ mkfifo("log/supervise/ok"+4, 0600);
+ fd = xopen("log/supervise/ok"+4, O_RDONLY|O_NDELAY);
+ coe(fd);
+ if (haslog) {
+ mkfifo("log/supervise/ok", 0600);
+ fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY);
+ coe(fd);
+ }
+ for (;;) {
+ iopause_fd x[3];
+ struct taia deadline;
+ struct taia now;
+ char ch;
+
+ if (haslog)
+ if (!svd[1].pid && svd[1].want == W_UP)
+ startservice(&svd[1]);
+ if (!svd[0].pid)
+ if (svd[0].want == W_UP || svd[0].state == S_FINISH)
+ startservice(&svd[0]);
+
+ x[0].fd = selfpipe[0];
+ x[0].events = IOPAUSE_READ;
+ x[1].fd = svd[0].fdcontrol;
+ x[1].events = IOPAUSE_READ;
+ if (haslog) {
+ x[2].fd = svd[1].fdcontrol;
+ x[2].events = IOPAUSE_READ;
+ }
+ taia_now(&now);
+ taia_uint(&deadline, 3600);
+ taia_add(&deadline, &now, &deadline);
+
+ sig_unblock(SIGTERM);
+ sig_unblock(SIGCHLD);
+ iopause(x, 2+haslog, &deadline, &now);
+ sig_block(SIGTERM);
+ sig_block(SIGCHLD);
+
+ while (read(selfpipe[0], &ch, 1) == 1)
+ ;
+ for (;;) {
+ int child;
+ int wstat;
+
+ child = wait_nohang(&wstat);
+ if (!child) break;
+ if ((child == -1) && (errno != EINTR)) break;
+ if (child == svd[0].pid) {
+ svd[0].pid = 0;
+ pidchanged = 1;
+ svd[0].ctrl &=~ C_TERM;
+ if (svd[0].state != S_FINISH) {
+ fd = open_read("finish");
+ if (fd != -1) {
+ close(fd);
+ svd[0].state = S_FINISH;
+ update_status(&svd[0]);
+ continue;
+ }
+ }
+ svd[0].state = S_DOWN;
+ taia_uint(&deadline, 1);
+ taia_add(&deadline, &svd[0].start, &deadline);
+ taia_now(&svd[0].start);
+ update_status(&svd[0]);
+ if (taia_less(&svd[0].start, &deadline)) sleep(1);
+ }
+ if (haslog) {
+ if (child == svd[1].pid) {
+ svd[1].pid = 0;
+ pidchanged = 1;
+ svd[1].state = S_DOWN;
+ svd[1].ctrl &= ~C_TERM;
+ taia_uint(&deadline, 1);
+ taia_add(&deadline, &svd[1].start, &deadline);
+ taia_now(&svd[1].start);
+ update_status(&svd[1]);
+ if (taia_less(&svd[1].start, &deadline)) sleep(1);
+ }
+ }
+ }
+ if (read(svd[0].fdcontrol, &ch, 1) == 1)
+ ctrl(&svd[0], ch);
+ if (haslog)
+ if (read(svd[1].fdcontrol, &ch, 1) == 1)
+ ctrl(&svd[1], ch);
+
+ if (sigterm) {
+ ctrl(&svd[0], 'x');
+ sigterm = 0;
+ }
+
+ if (svd[0].want == W_EXIT && svd[0].state == S_DOWN) {
+ if (svd[1].pid == 0)
+ _exit(0);
+ if (svd[1].want != W_EXIT) {
+ svd[1].want = W_EXIT;
+ /* stopservice(&svd[1]); */
+ update_status(&svd[1]);
+ close(logpipe[1]);
+ close(logpipe[0]);
+ //if (close(logpipe[1]) == -1)
+ // warn_cannot("close logpipe[1]");
+ //if (close(logpipe[0]) == -1)
+ // warn_cannot("close logpipe[0]");
+ }
+ }
+ }
+ /* not reached */
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/runit/runsvdir.c b/i/pc104/initrd/conf/busybox/runit/runsvdir.c
new file mode 100644
index 0000000..2f54cfe
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/runit/runsvdir.c
@@ -0,0 +1,334 @@
+/*
+Copyright (c) 2001-2006, Gerrit Pape
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. The name of the author may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/* Busyboxed by Denis Vlasenko <vda.linux@googlemail.com> */
+/* TODO: depends on runit_lib.c - review and reduce/eliminate */
+
+#include <sys/poll.h>
+#include <sys/file.h>
+#include "busybox.h"
+#include "runit_lib.h"
+
+#define MAXSERVICES 1000
+
+static char *svdir;
+static unsigned long dev;
+static unsigned long ino;
+static struct service {
+ unsigned long dev;
+ unsigned long ino;
+ int pid;
+ int isgone;
+} *sv;
+static int svnum;
+static int check = 1;
+static char *rplog;
+static int rploglen;
+static int logpipe[2];
+static iopause_fd io[1];
+static struct taia stamplog;
+static int exitsoon;
+static int pgrp;
+
+#define usage() bb_show_usage()
+static void fatal2_cannot(const char *m1, const char *m2)
+{
+ bb_perror_msg_and_die("%s: fatal: cannot %s%s", svdir, m1, m2);
+ /* was exiting 100 */
+}
+static void warn3x(const char *m1, const char *m2, const char *m3)
+{
+ bb_error_msg("%s: warning: %s%s%s", svdir, m1, m2, m3);
+}
+static void warn2_cannot(const char *m1, const char *m2)
+{
+ warn3x("cannot ", m1, m2);
+}
+static void warnx(const char *m1)
+{
+ warn3x(m1, "", "");
+}
+
+static void s_term(int sig_no)
+{
+ exitsoon = 1;
+}
+static void s_hangup(int sig_no)
+{
+ exitsoon = 2;
+}
+
+static void runsv(int no, const char *name)
+{
+ int pid = fork();
+
+ if (pid == -1) {
+ warn2_cannot("fork for ", name);
+ return;
+ }
+ if (pid == 0) {
+ /* child */
+ char *prog[3];
+
+ prog[0] = (char*)"runsv";
+ prog[1] = (char*)name;
+ prog[2] = NULL;
+ sig_uncatch(SIGHUP);
+ sig_uncatch(SIGTERM);
+ if (pgrp) setsid();
+ BB_EXECVP(prog[0], prog);
+ //pathexec_run(*prog, prog, (char* const*)environ);
+ fatal2_cannot("start runsv ", name);
+ }
+ sv[no].pid = pid;
+}
+
+static void runsvdir(void)
+{
+ DIR *dir;
+ direntry *d;
+ int i;
+ struct stat s;
+
+ dir = opendir(".");
+ if (!dir) {
+ warn2_cannot("open directory ", svdir);
+ return;
+ }
+ for (i = 0; i < svnum; i++)
+ sv[i].isgone = 1;
+ errno = 0;
+ while ((d = readdir(dir))) {
+ if (d->d_name[0] == '.') continue;
+ if (stat(d->d_name, &s) == -1) {
+ warn2_cannot("stat ", d->d_name);
+ errno = 0;
+ continue;
+ }
+ if (!S_ISDIR(s.st_mode)) continue;
+ for (i = 0; i < svnum; i++) {
+ if ((sv[i].ino == s.st_ino) && (sv[i].dev == s.st_dev)) {
+ sv[i].isgone = 0;
+ if (!sv[i].pid)
+ runsv(i, d->d_name);
+ break;
+ }
+ }
+ if (i == svnum) {
+ /* new service */
+ struct service *svnew = realloc(sv, (i+1) * sizeof(*sv));
+ if (!svnew) {
+ warn3x("cannot start runsv ", d->d_name,
+ " too many services");
+ continue;
+ }
+ sv = svnew;
+ svnum++;
+ memset(&sv[i], 0, sizeof(sv[i]));
+ sv[i].ino = s.st_ino;
+ sv[i].dev = s.st_dev;
+ //sv[i].pid = 0;
+ //sv[i].isgone = 0;
+ runsv(i, d->d_name);
+ check = 1;
+ }
+ }
+ if (errno) {
+ warn2_cannot("read directory ", svdir);
+ closedir(dir);
+ check = 1;
+ return;
+ }
+ closedir(dir);
+
+ /* SIGTERM removed runsv's */
+ for (i = 0; i < svnum; i++) {
+ if (!sv[i].isgone)
+ continue;
+ if (sv[i].pid)
+ kill(sv[i].pid, SIGTERM);
+ sv[i] = sv[--svnum];
+ check = 1;
+ }
+}
+
+static int setup_log(void)
+{
+ rploglen = strlen(rplog);
+ if (rploglen < 7) {
+ warnx("log must have at least seven characters");
+ return 0;
+ }
+ if (pipe(logpipe) == -1) {
+ warnx("cannot create pipe for log");
+ return -1;
+ }
+ coe(logpipe[1]);
+ coe(logpipe[0]);
+ ndelay_on(logpipe[0]);
+ ndelay_on(logpipe[1]);
+ if (fd_copy(2, logpipe[1]) == -1) {
+ warnx("cannot set filedescriptor for log");
+ return -1;
+ }
+ io[0].fd = logpipe[0];
+ io[0].events = IOPAUSE_READ;
+ taia_now(&stamplog);
+ return 1;
+}
+
+int runsvdir_main(int argc, char **argv);
+int runsvdir_main(int argc, char **argv)
+{
+ struct stat s;
+ time_t mtime = 0;
+ int wstat;
+ int curdir;
+ int pid;
+ struct taia deadline;
+ struct taia now;
+ struct taia stampcheck;
+ char ch;
+ int i;
+
+ argv++;
+ if (!argv || !*argv) usage();
+ if (**argv == '-') {
+ switch (*(*argv + 1)) {
+ case 'P': pgrp = 1;
+ case '-': ++argv;
+ }
+ if (!argv || !*argv) usage();
+ }
+
+ sig_catch(SIGTERM, s_term);
+ sig_catch(SIGHUP, s_hangup);
+ svdir = *argv++;
+ if (argv && *argv) {
+ rplog = *argv;
+ if (setup_log() != 1) {
+ rplog = 0;
+ warnx("log service disabled");
+ }
+ }
+ curdir = open_read(".");
+ if (curdir == -1)
+ fatal2_cannot("open current directory", "");
+ coe(curdir);
+
+ taia_now(&stampcheck);
+
+ for (;;) {
+ /* collect children */
+ for (;;) {
+ pid = wait_nohang(&wstat);
+ if (pid <= 0) break;
+ for (i = 0; i < svnum; i++) {
+ if (pid == sv[i].pid) {
+ /* runsv has gone */
+ sv[i].pid = 0;
+ check = 1;
+ break;
+ }
+ }
+ }
+
+ taia_now(&now);
+ if (now.sec.x < (stampcheck.sec.x - 3)) {
+ /* time warp */
+ warnx("time warp: resetting time stamp");
+ taia_now(&stampcheck);
+ taia_now(&now);
+ if (rplog) taia_now(&stamplog);
+ }
+ if (taia_less(&now, &stampcheck) == 0) {
+ /* wait at least a second */
+ taia_uint(&deadline, 1);
+ taia_add(&stampcheck, &now, &deadline);
+
+ if (stat(svdir, &s) != -1) {
+ if (check || s.st_mtime != mtime
+ || s.st_ino != ino || s.st_dev != dev
+ ) {
+ /* svdir modified */
+ if (chdir(svdir) != -1) {
+ mtime = s.st_mtime;
+ dev = s.st_dev;
+ ino = s.st_ino;
+ check = 0;
+ if (now.sec.x <= (4611686018427387914ULL + (uint64_t)mtime))
+ sleep(1);
+ runsvdir();
+ while (fchdir(curdir) == -1) {
+ warn2_cannot("change directory, pausing", "");
+ sleep(5);
+ }
+ } else
+ warn2_cannot("change directory to ", svdir);
+ }
+ } else
+ warn2_cannot("stat ", svdir);
+ }
+
+ if (rplog) {
+ if (taia_less(&now, &stamplog) == 0) {
+ write(logpipe[1], ".", 1);
+ taia_uint(&deadline, 900);
+ taia_add(&stamplog, &now, &deadline);
+ }
+ }
+ taia_uint(&deadline, check ? 1 : 5);
+ taia_add(&deadline, &now, &deadline);
+
+ sig_block(SIGCHLD);
+ if (rplog)
+ iopause(io, 1, &deadline, &now);
+ else
+ iopause(0, 0, &deadline, &now);
+ sig_unblock(SIGCHLD);
+
+ if (rplog && (io[0].revents | IOPAUSE_READ))
+ while (read(logpipe[0], &ch, 1) > 0)
+ if (ch) {
+ for (i = 6; i < rploglen; i++)
+ rplog[i-1] = rplog[i];
+ rplog[rploglen-1] = ch;
+ }
+
+ switch (exitsoon) {
+ case 1:
+ _exit(0);
+ case 2:
+ for (i = 0; i < svnum; i++)
+ if (sv[i].pid)
+ kill(sv[i].pid, SIGTERM);
+ _exit(111);
+ }
+ }
+ /* not reached */
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/runit/sv.c b/i/pc104/initrd/conf/busybox/runit/sv.c
new file mode 100644
index 0000000..051e64e
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/runit/sv.c
@@ -0,0 +1,590 @@
+/*
+Copyright (c) 2001-2006, Gerrit Pape
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. The name of the author may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/* Taken from http://smarden.sunsite.dk/runit/sv.8.html:
+
+sv - control and manage services monitored by runsv
+
+sv [-v] [-w sec] command services
+/etc/init.d/service [-w sec] command
+
+The sv program reports the current status and controls the state of services
+monitored by the runsv(8) supervisor.
+
+services consists of one or more arguments, each argument naming a directory
+service used by runsv(8). If service doesn?t start with a dot or slash,
+it is searched in the default services directory /var/service/, otherwise
+relative to the current directory.
+
+command is one of up, down, status, once, pause, cont, hup, alarm, interrupt,
+1, 2, term, kill, or exit, or start, stop, restart, shutdown, force-stop,
+force-reload, force-restart, force-shutdown.
+
+The sv program can be sym-linked to /etc/init.d/ to provide an LSB init
+script interface. The service to be controlled then is specified by the
+base name of the "init script".
+
+status
+ Report the current status of the service, and the appendant log service
+ if available, to standard output.
+up
+ If the service is not running, start it. If the service stops, restart it.
+down
+ If the service is running, send it the TERM signal, and the CONT signal.
+ If ./run exits, start ./finish if it exists. After it stops, do not
+ restart service.
+once
+ If the service is not running, start it. Do not restart it if it stops.
+pause cont hup alarm interrupt quit 1 2 term kill
+ If the service is running, send it the STOP, CONT, HUP, ALRM, INT, QUIT,
+ USR1, USR2, TERM, or KILL signal respectively.
+exit
+ If the service is running, send it the TERM signal, and the CONT signal.
+ Do not restart the service. If the service is down, and no log service
+ exists, runsv(8) exits. If the service is down and a log service exists,
+ send the TERM signal to the log service. If the log service is down,
+ runsv(8) exits. This command is ignored if it is given to an appendant
+ log service.
+
+sv actually looks only at the first character of above commands.
+
+status
+ Same as status.
+start
+ Same as up, but wait up to 7 seconds for the command to take effect.
+ Then report the status or timeout. If the script ./check exists in
+ the service directory, sv runs this script to check whether the service
+ is up and available; it's considered to be available if ./check exits
+ with 0.
+stop
+ Same as down, but wait up to 7 seconds for the service to become down.
+ Then report the status or timeout.
+restart
+ Send the commands term, cont, and up to the service, and wait up to
+ 7 seconds for the service to restart. Then report the status or timeout.
+ If the script ./check exists in the service directory, sv runs this script
+ to check whether the service is up and available again; it's considered
+ to be available if ./check exits with 0.
+shutdown
+ Same as exit, but wait up to 7 seconds for the runsv(8) process
+ to terminate. Then report the status or timeout.
+force-stop
+ Same as down, but wait up to 7 seconds for the service to become down.
+ Then report the status, and on timeout send the service the kill command.
+force-reload
+ Send the service the term and cont commands, and wait up to
+ 7 seconds for the service to restart. Then report the status,
+ and on timeout send the service the kill command.
+force-restart
+ Send the service the term, cont and up commands, and wait up to
+ 7 seconds for the service to restart. Then report the status, and
+ on timeout send the service the kill command. If the script ./check
+ exists in the service directory, sv runs this script to check whether
+ the service is up and available again; it?s considered to be available
+ if ./check exits with 0.
+force-shutdown
+ Same as exit, but wait up to 7 seconds for the runsv(8) process to
+ terminate. Then report the status, and on timeout send the service
+ the kill command.
+
+Additional Commands
+
+check
+ Check for the service to be in the state that's been requested. Wait up to
+ 7 seconds for the service to reach the requested state, then report
+ the status or timeout. If the requested state of the service is up,
+ and the script ./check exists in the service directory, sv runs
+ this script to check whether the service is up and running;
+ it's considered to be up if ./check exits with 0.
+
+Options
+
+-v
+ wait up to 7 seconds for the command to take effect.
+ Then report the status or timeout.
+-w sec
+ Override the default timeout of 7 seconds with sec seconds. Implies -v.
+
+Environment
+
+SVDIR
+ The environment variable $SVDIR overrides the default services directory
+ /var/service.
+SVWAIT
+ The environment variable $SVWAIT overrides the default 7 seconds to wait
+ for a command to take effect. It is overridden by the -w option.
+
+Exit Codes
+ sv exits 0, if the command was successfully sent to all services, and,
+ if it was told to wait, the command has taken effect to all services.
+
+ For each service that caused an error (e.g. the directory is not
+ controlled by a runsv(8) process, or sv timed out while waiting),
+ sv increases the exit code by one and exits non zero. The maximum
+ is 99. sv exits 100 on error.
+*/
+
+/* Busyboxed by Denis Vlasenko <vda.linux@googlemail.com> */
+/* TODO: depends on runit_lib.c - review and reduce/eliminate */
+
+#include <sys/poll.h>
+#include <sys/file.h>
+#include "busybox.h"
+#include "runit_lib.h"
+
+static const char *acts;
+static char **service;
+static unsigned rc;
+static struct taia tstart, tnow;
+static char svstatus[20];
+
+#define usage() bb_show_usage()
+
+static void fatal_cannot(const char *m1) ATTRIBUTE_NORETURN;
+static void fatal_cannot(const char *m1)
+{
+ bb_perror_msg("fatal: cannot %s", m1);
+ _exit(151);
+}
+
+static void out(const char *p, const char *m1)
+{
+ printf("%s%s: %s", p, *service, m1);
+ if (errno) {
+ printf(": %s", strerror(errno));
+ }
+ puts(""); /* will also flush the output */
+}
+
+#define WARN "warning: "
+#define OK "ok: "
+
+static void fail(const char *m1) {
+ ++rc;
+ out("fail: ", m1);
+}
+static void failx(const char *m1) {
+ errno = 0;
+ fail(m1);
+}
+static void warn_cannot(const char *m1) {
+ ++rc;
+ out("warning: cannot ", m1);
+}
+static void warnx_cannot(const char *m1) {
+ errno = 0;
+ warn_cannot(m1);
+}
+static void ok(const char *m1) {
+ errno = 0;
+ out(OK, m1);
+}
+
+static int svstatus_get(void)
+{
+ int fd, r;
+
+ fd = open_write("supervise/ok");
+ if (fd == -1) {
+ if (errno == ENODEV) {
+ *acts == 'x' ? ok("runsv not running")
+ : failx("runsv not running");
+ return 0;
+ }
+ warn_cannot("open supervise/ok");
+ return -1;
+ }
+ close(fd);
+ fd = open_read("supervise/status");
+ if (fd == -1) {
+ warn_cannot("open supervise/status");
+ return -1;
+ }
+ r = read(fd, svstatus, 20);
+ close(fd);
+ switch (r) {
+ case 20: break;
+ case -1: warn_cannot("read supervise/status"); return -1;
+ default: warnx_cannot("read supervise/status: bad format"); return -1;
+ }
+ return 1;
+}
+
+static unsigned svstatus_print(const char *m)
+{
+ long diff;
+ int pid;
+ int normallyup = 0;
+ struct stat s;
+ struct tai tstatus;
+
+ if (stat("down", &s) == -1) {
+ if (errno != ENOENT) {
+ bb_perror_msg(WARN"cannot stat %s/down", *service);
+ return 0;
+ }
+ normallyup = 1;
+ }
+ pid = (unsigned char) svstatus[15];
+ pid <<= 8; pid += (unsigned char)svstatus[14];
+ pid <<= 8; pid += (unsigned char)svstatus[13];
+ pid <<= 8; pid += (unsigned char)svstatus[12];
+ tai_unpack(svstatus, &tstatus);
+ if (pid) {
+ switch (svstatus[19]) {
+ case 1: printf("run: "); break;
+ case 2: printf("finish: "); break;
+ }
+ printf("%s: (pid %d) ", m, pid);
+ } else {
+ printf("down: %s: ", m);
+ }
+ diff = tnow.sec.x - tstatus.x;
+ printf("%lds", (diff < 0 ? 0L : diff));
+ if (pid) {
+ if (!normallyup) printf(", normally down");
+ if (svstatus[16]) printf(", paused");
+ if (svstatus[17] == 'd') printf(", want down");
+ if (svstatus[18]) printf(", got TERM");
+ } else {
+ if (normallyup) printf(", normally up");
+ if (svstatus[17] == 'u') printf(", want up");
+ }
+ return pid ? 1 : 2;
+}
+
+static int status(const char *unused)
+{
+ int r;
+
+ r = svstatus_get();
+ switch (r) { case -1: case 0: return 0; }
+
+ r = svstatus_print(*service);
+ if (chdir("log") == -1) {
+ if (errno != ENOENT) {
+ printf("; log: "WARN"cannot change to log service directory: %s",
+ strerror(errno));
+ }
+ } else if (svstatus_get()) {
+ printf("; ");
+ svstatus_print("log");
+ }
+ puts(""); /* will also flush the output */
+ return r;
+}
+
+static int checkscript(void)
+{
+ char *prog[2];
+ struct stat s;
+ int pid, w;
+
+ if (stat("check", &s) == -1) {
+ if (errno == ENOENT) return 1;
+ bb_perror_msg(WARN"cannot stat %s/check", *service);
+ return 0;
+ }
+ /* if (!(s.st_mode & S_IXUSR)) return 1; */
+ if ((pid = fork()) == -1) {
+ bb_perror_msg(WARN"cannot fork for %s/check", *service);
+ return 0;
+ }
+ if (!pid) {
+ prog[0] = (char*)"./check";
+ prog[1] = NULL;
+ close(1);
+ execve("check", prog, environ);
+ bb_perror_msg(WARN"cannot run %s/check", *service);
+ _exit(0);
+ }
+ while (wait_pid(&w, pid) == -1) {
+ if (errno == EINTR) continue;
+ bb_perror_msg(WARN"cannot wait for child %s/check", *service);
+ return 0;
+ }
+ return !wait_exitcode(w);
+}
+
+static int check(const char *a)
+{
+ int r;
+ unsigned pid;
+ struct tai tstatus;
+
+ r = svstatus_get();
+ if (r == -1)
+ return -1;
+ if (r == 0) {
+ if (*a == 'x')
+ return 1;
+ return -1;
+ }
+ pid = (unsigned char)svstatus[15];
+ pid <<= 8; pid += (unsigned char)svstatus[14];
+ pid <<= 8; pid += (unsigned char)svstatus[13];
+ pid <<= 8; pid += (unsigned char)svstatus[12];
+ switch (*a) {
+ case 'x':
+ return 0;
+ case 'u':
+ if (!pid || svstatus[19] != 1) return 0;
+ if (!checkscript()) return 0;
+ break;
+ case 'd':
+ if (pid) return 0;
+ break;
+ case 'c':
+ if (pid && !checkscript()) return 0;
+ break;
+ case 't':
+ if (!pid && svstatus[17] == 'd') break;
+ tai_unpack(svstatus, &tstatus);
+ if ((tstart.sec.x > tstatus.x) || !pid || svstatus[18] || !checkscript())
+ return 0;
+ break;
+ case 'o':
+ tai_unpack(svstatus, &tstatus);
+ if ((!pid && tstart.sec.x > tstatus.x) || (pid && svstatus[17] != 'd'))
+ return 0;
+ }
+ printf(OK);
+ svstatus_print(*service);
+ puts(""); /* will also flush the output */
+ return 1;
+}
+
+static int control(const char *a)
+{
+ int fd, r;
+
+ if (svstatus_get() <= 0) return -1;
+ if (svstatus[17] == *a) return 0;
+ fd = open_write("supervise/control");
+ if (fd == -1) {
+ if (errno != ENODEV)
+ warn_cannot("open supervise/control");
+ else
+ *a == 'x' ? ok("runsv not running") : failx("runsv not running");
+ return -1;
+ }
+ r = write(fd, a, strlen(a));
+ close(fd);
+ if (r != strlen(a)) {
+ warn_cannot("write to supervise/control");
+ return -1;
+ }
+ return 1;
+}
+
+int sv_main(int argc, char **argv);
+int sv_main(int argc, char **argv)
+{
+ unsigned opt;
+ unsigned i, want_exit;
+ char *x;
+ char *action;
+ const char *varservice = "/var/service/";
+ unsigned services;
+ char **servicex;
+ unsigned long waitsec = 7;
+ smallint kll = 0;
+ smallint verbose = 0;
+ int (*act)(const char*);
+ int (*cbk)(const char*);
+ int curdir;
+
+ xfunc_error_retval = 100;
+
+ x = getenv("SVDIR");
+ if (x) varservice = x;
+ x = getenv("SVWAIT");
+ if (x) waitsec = xatoul(x);
+
+ opt = getopt32(argc, argv, "w:v", &x);
+ if (opt & 1) waitsec = xatoul(x); // -w
+ if (opt & 2) verbose = 1; // -v
+ argc -= optind;
+ argv += optind;
+ action = *argv++;
+ if (!action || !*argv) usage();
+ service = argv;
+ services = argc - 1;
+
+ taia_now(&tnow);
+ tstart = tnow;
+ curdir = open_read(".");
+ if (curdir == -1)
+ fatal_cannot("open current directory");
+
+ act = &control;
+ acts = "s";
+ cbk = &check;
+
+ switch (*action) {
+ case 'x':
+ case 'e':
+ acts = "x";
+ if (!verbose) cbk = NULL;
+ break;
+ case 'X':
+ case 'E':
+ acts = "x";
+ kll = 1;
+ break;
+ case 'D':
+ acts = "d";
+ kll = 1;
+ break;
+ case 'T':
+ acts = "tc";
+ kll = 1;
+ break;
+ case 'c':
+ if (!str_diff(action, "check")) {
+ act = NULL;
+ acts = "c";
+ break;
+ }
+ case 'u': case 'd': case 'o': case 't': case 'p': case 'h':
+ case 'a': case 'i': case 'k': case 'q': case '1': case '2':
+ action[1] = '\0';
+ acts = action;
+ if (!verbose) cbk = NULL;
+ break;
+ case 's':
+ if (!str_diff(action, "shutdown")) {
+ acts = "x";
+ break;
+ }
+ if (!str_diff(action, "start")) {
+ acts = "u";
+ break;
+ }
+ if (!str_diff(action, "stop")) {
+ acts = "d";
+ break;
+ }
+ /* "status" */
+ act = &status;
+ cbk = NULL;
+ break;
+ case 'r':
+ if (!str_diff(action, "restart")) {
+ acts = "tcu";
+ break;
+ }
+ usage();
+ case 'f':
+ if (!str_diff(action, "force-reload")) {
+ acts = "tc";
+ kll = 1;
+ break;
+ }
+ if (!str_diff(action, "force-restart")) {
+ acts = "tcu";
+ kll = 1;
+ break;
+ }
+ if (!str_diff(action, "force-shutdown")) {
+ acts = "x";
+ kll = 1;
+ break;
+ }
+ if (!str_diff(action, "force-stop")) {
+ acts = "d";
+ kll = 1;
+ break;
+ }
+ default:
+ usage();
+ }
+
+ servicex = service;
+ for (i = 0; i < services; ++i) {
+ if ((**service != '/') && (**service != '.')) {
+ if (chdir(varservice) == -1)
+ goto chdir_failed_0;
+ }
+ if (chdir(*service) == -1) {
+ chdir_failed_0:
+ fail("cannot change to service directory");
+ goto nullify_service_0;
+ }
+ if (act && (act(acts) == -1)) {
+ nullify_service_0:
+ *service = NULL;
+ }
+ if (fchdir(curdir) == -1)
+ fatal_cannot("change to original directory");
+ service++;
+ }
+
+ if (cbk) while (1) {
+ //struct taia tdiff;
+ long diff;
+
+ //taia_sub(&tdiff, &tnow, &tstart);
+ diff = tnow.sec.x - tstart.sec.x;
+ service = servicex;
+ want_exit = 1;
+ for (i = 0; i < services; ++i, ++service) {
+ if (!*service)
+ continue;
+ if ((**service != '/') && (**service != '.')) {
+ if (chdir(varservice) == -1)
+ goto chdir_failed;
+ }
+ if (chdir(*service) == -1) {
+ chdir_failed:
+ fail("cannot change to service directory");
+ goto nullify_service;
+ }
+ if (cbk(acts) != 0)
+ goto nullify_service;
+ want_exit = 0;
+ if (diff >= waitsec) {
+ printf(kll ? "kill: " : "timeout: ");
+ if (svstatus_get() > 0) {
+ svstatus_print(*service);
+ ++rc;
+ }
+ puts(""); /* will also flush the output */
+ if (kll)
+ control("k");
+ nullify_service:
+ *service = NULL;
+ }
+ if (fchdir(curdir) == -1)
+ fatal_cannot("change to original directory");
+ }
+ if (want_exit) break;
+ usleep(420000);
+ taia_now(&tnow);
+ }
+ return rc > 99 ? 99 : rc;
+}
diff --git a/i/pc104/initrd/conf/busybox/runit/svlogd.c b/i/pc104/initrd/conf/busybox/runit/svlogd.c
new file mode 100644
index 0000000..e454bac
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/runit/svlogd.c
@@ -0,0 +1,960 @@
+/*
+Copyright (c) 2001-2006, Gerrit Pape
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. The name of the author may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/* Busyboxed by Denis Vlasenko <vda.linux@googlemail.com> */
+/* TODO: depends on runit_lib.c - review and reduce/eliminate */
+
+#include <sys/poll.h>
+#include <sys/file.h>
+#include "busybox.h"
+#include "runit_lib.h"
+
+static unsigned verbose;
+static int linemax = 1000;
+////static int buflen = 1024;
+static int linelen;
+
+static char **fndir;
+static int fdwdir;
+static int wstat;
+static struct taia trotate;
+
+static char *line;
+static smallint exitasap;
+static smallint rotateasap;
+static smallint reopenasap;
+static smallint linecomplete = 1;
+
+static smallint tmaxflag;
+
+static char repl;
+static const char *replace = "";
+
+static sigset_t *blocked_sigset;
+static iopause_fd input;
+static int fl_flag_0;
+
+static struct logdir {
+ ////char *btmp;
+ /* pattern list to match, in "aa\0bb\0\cc\0\0" form */
+ char *inst;
+ char *processor;
+ char *name;
+ unsigned size;
+ unsigned sizemax;
+ unsigned nmax;
+ unsigned nmin;
+ /* int (not long) because of taia_uint() usage: */
+ unsigned tmax;
+ int ppid;
+ int fddir;
+ int fdcur;
+ FILE* filecur; ////
+ int fdlock;
+ struct taia trotate;
+ char fnsave[FMT_PTIME];
+ char match;
+ char matcherr;
+} *dir;
+static unsigned dirn;
+
+#define FATAL "fatal: "
+#define WARNING "warning: "
+#define PAUSE "pausing: "
+#define INFO "info: "
+
+#define usage() bb_show_usage()
+static void fatalx(const char *m0)
+{
+ bb_error_msg_and_die(FATAL"%s", m0);
+}
+static void warn(const char *m0) {
+ bb_perror_msg(WARNING"%s", m0);
+}
+static void warn2(const char *m0, const char *m1)
+{
+ bb_perror_msg(WARNING"%s: %s", m0, m1);
+}
+static void warnx(const char *m0, const char *m1)
+{
+ bb_error_msg(WARNING"%s: %s", m0, m1);
+}
+static void pause_nomem(void)
+{
+ bb_error_msg(PAUSE"out of memory");
+ sleep(3);
+}
+static void pause1cannot(const char *m0)
+{
+ bb_perror_msg(PAUSE"cannot %s", m0);
+ sleep(3);
+}
+static void pause2cannot(const char *m0, const char *m1)
+{
+ bb_perror_msg(PAUSE"cannot %s %s", m0, m1);
+ sleep(3);
+}
+
+static char* wstrdup(const char *str)
+{
+ char *s;
+ while (!(s = strdup(str)))
+ pause_nomem();
+ return s;
+}
+
+static unsigned processorstart(struct logdir *ld)
+{
+ int pid;
+
+ if (!ld->processor) return 0;
+ if (ld->ppid) {
+ warnx("processor already running", ld->name);
+ return 0;
+ }
+ while ((pid = fork()) == -1)
+ pause2cannot("fork for processor", ld->name);
+ if (!pid) {
+ char *prog[4];
+ int fd;
+
+ /* child */
+ sig_uncatch(SIGTERM);
+ sig_uncatch(SIGALRM);
+ sig_uncatch(SIGHUP);
+ sig_unblock(SIGTERM);
+ sig_unblock(SIGALRM);
+ sig_unblock(SIGHUP);
+
+ if (verbose)
+ bb_error_msg(INFO"processing: %s/%s", ld->name, ld->fnsave);
+ fd = xopen(ld->fnsave, O_RDONLY|O_NDELAY);
+ if (fd_move(0, fd) == -1)
+ bb_perror_msg_and_die(FATAL"cannot %s processor %s", "move filedescriptor for", ld->name);
+ ld->fnsave[26] = 't';
+ fd = xopen(ld->fnsave, O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
+ if (fd_move(1, fd) == -1)
+ bb_perror_msg_and_die(FATAL"cannot %s processor %s", "move filedescriptor for", ld->name);
+ fd = open_read("state");
+ if (fd == -1) {
+ if (errno != ENOENT)
+ bb_perror_msg_and_die(FATAL"cannot %s processor %s", "open state for", ld->name);
+ close(xopen("state", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT));
+ fd = xopen("state", O_RDONLY|O_NDELAY);
+ }
+ if (fd_move(4, fd) == -1)
+ bb_perror_msg_and_die(FATAL"cannot %s processor %s", "move filedescriptor for", ld->name);
+ fd = xopen("newstate", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
+ if (fd_move(5, fd) == -1)
+ bb_perror_msg_and_die(FATAL"cannot %s processor %s", "move filedescriptor for", ld->name);
+
+// getenv("SHELL")?
+ prog[0] = (char*)"sh";
+ prog[1] = (char*)"-c";
+ prog[2] = ld->processor;
+ prog[3] = '\0';
+ execve("/bin/sh", prog, environ);
+ bb_perror_msg_and_die(FATAL"cannot %s processor %s", "run", ld->name);
+ }
+ ld->ppid = pid;
+ return 1;
+}
+
+static unsigned processorstop(struct logdir *ld)
+{
+ char f[28];
+
+ if (ld->ppid) {
+ sig_unblock(SIGHUP);
+ while (wait_pid(&wstat, ld->ppid) == -1)
+ pause2cannot("wait for processor", ld->name);
+ sig_block(SIGHUP);
+ ld->ppid = 0;
+ }
+ if (ld->fddir == -1) return 1;
+ while (fchdir(ld->fddir) == -1)
+ pause2cannot("change directory, want processor", ld->name);
+ if (wait_exitcode(wstat) != 0) {
+ warnx("processor failed, restart", ld->name);
+ ld->fnsave[26] = 't';
+ unlink(ld->fnsave);
+ ld->fnsave[26] = 'u';
+ processorstart(ld);
+ while (fchdir(fdwdir) == -1)
+ pause1cannot("change to initial working directory");
+ return ld->processor ? 0 : 1;
+ }
+ ld->fnsave[26] = 't';
+ memcpy(f, ld->fnsave, 26);
+ f[26] = 's';
+ f[27] = '\0';
+ while (rename(ld->fnsave, f) == -1)
+ pause2cannot("rename processed", ld->name);
+ while (chmod(f, 0744) == -1)
+ pause2cannot("set mode of processed", ld->name);
+ ld->fnsave[26] = 'u';
+ if (unlink(ld->fnsave) == -1)
+ bb_error_msg(WARNING"cannot unlink: %s/%s", ld->name, ld->fnsave);
+ while (rename("newstate", "state") == -1)
+ pause2cannot("rename state", ld->name);
+ if (verbose)
+ bb_error_msg(INFO"processed: %s/%s", ld->name, f);
+ while (fchdir(fdwdir) == -1)
+ pause1cannot("change to initial working directory");
+ return 1;
+}
+
+static void rmoldest(struct logdir *ld)
+{
+ DIR *d;
+ struct dirent *f;
+ char oldest[FMT_PTIME];
+ int n = 0;
+
+ oldest[0] = 'A'; oldest[1] = oldest[27] = 0;
+ while (!(d = opendir(".")))
+ pause2cannot("open directory, want rotate", ld->name);
+ errno = 0;
+ while ((f = readdir(d))) {
+ if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) {
+ if (f->d_name[26] == 't') {
+ if (unlink(f->d_name) == -1)
+ warn2("cannot unlink processor leftover", f->d_name);
+ } else {
+ ++n;
+ if (strcmp(f->d_name, oldest) < 0)
+ memcpy(oldest, f->d_name, 27);
+ }
+ errno = 0;
+ }
+ }
+ if (errno)
+ warn2("cannot read directory", ld->name);
+ closedir(d);
+
+ if (ld->nmax && (n > ld->nmax)) {
+ if (verbose)
+ bb_error_msg(INFO"delete: %s/%s", ld->name, oldest);
+ if ((*oldest == '@') && (unlink(oldest) == -1))
+ warn2("cannot unlink oldest logfile", ld->name);
+ }
+}
+
+static unsigned rotate(struct logdir *ld)
+{
+ struct stat st;
+ struct taia now;
+
+ if (ld->fddir == -1) {
+ ld->tmax = 0;
+ return 0;
+ }
+ if (ld->ppid)
+ while (!processorstop(ld))
+ /* wait */;
+
+ while (fchdir(ld->fddir) == -1)
+ pause2cannot("change directory, want rotate", ld->name);
+
+ /* create new filename */
+ ld->fnsave[25] = '.';
+ ld->fnsave[26] = 's';
+ if (ld->processor)
+ ld->fnsave[26] = 'u';
+ ld->fnsave[27] = '\0';
+ do {
+ taia_now(&now);
+ fmt_taia25(ld->fnsave, &now);
+ errno = 0;
+ stat(ld->fnsave, &st);
+ } while (errno != ENOENT);
+
+ if (ld->tmax && taia_less(&ld->trotate, &now)) {
+ taia_uint(&ld->trotate, ld->tmax);
+ taia_add(&ld->trotate, &now, &ld->trotate);
+ if (taia_less(&ld->trotate, &trotate))
+ trotate = ld->trotate;
+ }
+
+ if (ld->size > 0) {
+ while (fflush(ld->filecur) || fsync(ld->fdcur) == -1)
+ pause2cannot("fsync current logfile", ld->name);
+ while (fchmod(ld->fdcur, 0744) == -1)
+ pause2cannot("set mode of current", ld->name);
+ ////close(ld->fdcur);
+ fclose(ld->filecur);
+
+ if (verbose) {
+ bb_error_msg(INFO"rename: %s/current %s %u", ld->name,
+ ld->fnsave, ld->size);
+ }
+ while (rename("current", ld->fnsave) == -1)
+ pause2cannot("rename current", ld->name);
+ while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1)
+ pause2cannot("create new current", ld->name);
+ /* we presume this cannot fail */
+ ld->filecur = fdopen(ld->fdcur, "a"); ////
+ setvbuf(ld->filecur, NULL, _IOFBF, linelen); ////
+ coe(ld->fdcur);
+ ld->size = 0;
+ while (fchmod(ld->fdcur, 0644) == -1)
+ pause2cannot("set mode of current", ld->name);
+ rmoldest(ld);
+ processorstart(ld);
+ }
+
+ while (fchdir(fdwdir) == -1)
+ pause1cannot("change to initial working directory");
+ return 1;
+}
+
+static int buffer_pwrite(int n, char *s, unsigned len)
+{
+ int i;
+ struct logdir *ld = &dir[n];
+
+ if (ld->sizemax) {
+ if (ld->size >= ld->sizemax)
+ rotate(ld);
+ if (len > (ld->sizemax - ld->size))
+ len = ld->sizemax - ld->size;
+ }
+ while (1) {
+ ////i = full_write(ld->fdcur, s, len);
+ ////if (i != -1) break;
+ i = fwrite(s, 1, len, ld->filecur);
+ if (i == len) break;
+
+ if ((errno == ENOSPC) && (ld->nmin < ld->nmax)) {
+ DIR *d;
+ struct dirent *f;
+ char oldest[FMT_PTIME];
+ int j = 0;
+
+ while (fchdir(ld->fddir) == -1)
+ pause2cannot("change directory, want remove old logfile",
+ ld->name);
+ oldest[0] = 'A';
+ oldest[1] = oldest[27] = '\0';
+ while (!(d = opendir(".")))
+ pause2cannot("open directory, want remove old logfile",
+ ld->name);
+ errno = 0;
+ while ((f = readdir(d)))
+ if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) {
+ ++j;
+ if (strcmp(f->d_name, oldest) < 0)
+ memcpy(oldest, f->d_name, 27);
+ }
+ if (errno) warn2("cannot read directory, want remove old logfile",
+ ld->name);
+ closedir(d);
+ errno = ENOSPC;
+ if (j > ld->nmin) {
+ if (*oldest == '@') {
+ bb_error_msg(WARNING"out of disk space, delete: %s/%s",
+ ld->name, oldest);
+ errno = 0;
+ if (unlink(oldest) == -1) {
+ warn2("cannot unlink oldest logfile", ld->name);
+ errno = ENOSPC;
+ }
+ while (fchdir(fdwdir) == -1)
+ pause1cannot("change to initial working directory");
+ }
+ }
+ }
+ if (errno)
+ pause2cannot("write to current", ld->name);
+ }
+
+ ld->size += i;
+ if (ld->sizemax)
+ if (s[i-1] == '\n')
+ if (ld->size >= (ld->sizemax - linemax))
+ rotate(ld);
+ return i;
+}
+
+static void logdir_close(struct logdir *ld)
+{
+ if (ld->fddir == -1)
+ return;
+ if (verbose)
+ bb_error_msg(INFO"close: %s", ld->name);
+ close(ld->fddir);
+ ld->fddir = -1;
+ if (ld->fdcur == -1)
+ return; /* impossible */
+ while (fflush(ld->filecur) || fsync(ld->fdcur) == -1)
+ pause2cannot("fsync current logfile", ld->name);
+ while (fchmod(ld->fdcur, 0744) == -1)
+ pause2cannot("set mode of current", ld->name);
+ ////close(ld->fdcur);
+ fclose(ld->filecur);
+ ld->fdcur = -1;
+ if (ld->fdlock == -1)
+ return; /* impossible */
+ close(ld->fdlock);
+ ld->fdlock = -1;
+ free(ld->processor);
+ ld->processor = NULL;
+}
+
+static unsigned logdir_open(struct logdir *ld, const char *fn)
+{
+ char buf[128];
+ struct taia now;
+ char *new, *s, *np;
+ int i;
+ struct stat st;
+
+ ld->fddir = open(fn, O_RDONLY|O_NDELAY);
+ if (ld->fddir == -1) {
+ warn2("cannot open log directory", (char*)fn);
+ return 0;
+ }
+ coe(ld->fddir);
+ if (fchdir(ld->fddir) == -1) {
+ logdir_close(ld);
+ warn2("cannot change directory", (char*)fn);
+ return 0;
+ }
+ ld->fdlock = open("lock", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
+ if ((ld->fdlock == -1)
+ || (lock_exnb(ld->fdlock) == -1)
+ ) {
+ logdir_close(ld);
+ warn2("cannot lock directory", (char*)fn);
+ while (fchdir(fdwdir) == -1)
+ pause1cannot("change to initial working directory");
+ return 0;
+ }
+ coe(ld->fdlock);
+
+ ld->size = 0;
+ ld->sizemax = 1000000;
+ ld->nmax = ld->nmin = 10;
+ ld->tmax = 0;
+ ld->name = (char*)fn;
+ ld->ppid = 0;
+ ld->match = '+';
+ free(ld->inst); ld->inst = NULL;
+ free(ld->processor); ld->processor = NULL;
+
+ /* read config */
+ i = open_read_close("config", buf, sizeof(buf));
+ if (i < 0)
+ warn2("cannot read config", ld->name);
+ if (i > 0) {
+ if (verbose) bb_error_msg(INFO"read: %s/config", ld->name);
+ s = buf;
+ while (s) {
+ np = strchr(s, '\n');
+ if (np) *np++ = '\0';
+ switch (s[0]) {
+ case '+':
+ case '-':
+ case 'e':
+ case 'E':
+ while (1) {
+ int l = asprintf(&new, "%s%s\n", ld->inst?:"", s);
+ if (l >= 0 && new) break;
+ pause_nomem();
+ }
+ free(ld->inst);
+ ld->inst = new;
+ break;
+ case 's': {
+ static const struct suffix_mult km_suffixes[] = {
+ { "k", 1024 },
+ { "m", 1024*1024 },
+ { NULL, 0 }
+ };
+ ld->sizemax = xatou_sfx(&s[1], km_suffixes);
+ break;
+ }
+ case 'n':
+ ld->nmax = xatoi_u(&s[1]);
+ break;
+ case 'N':
+ ld->nmin = xatoi_u(&s[1]);
+ break;
+ case 't': {
+ static const struct suffix_mult mh_suffixes[] = {
+ { "m", 60 },
+ { "h", 60*60 },
+ /*{ "d", 24*60*60 },*/
+ { NULL, 0 }
+ };
+ ld->tmax = xatou_sfx(&s[1], mh_suffixes);
+ if (ld->tmax) {
+ taia_uint(&ld->trotate, ld->tmax);
+ taia_add(&ld->trotate, &now, &ld->trotate);
+ if (!tmaxflag || taia_less(&ld->trotate, &trotate))
+ trotate = ld->trotate;
+ tmaxflag = 1;
+ }
+ break;
+ }
+ case '!':
+ if (s[1]) {
+ free(ld->processor);
+ ld->processor = wstrdup(s);
+ }
+ break;
+ }
+ s = np;
+ }
+ /* Convert "aa\nbb\ncc\n\0" to "aa\0bb\0cc\0\0" */
+ s = ld->inst;
+ while (s) {
+ np = strchr(s, '\n');
+ if (np) *np++ = '\0';
+ s = np;
+ }
+ }
+
+ /* open current */
+ i = stat("current", &st);
+ if (i != -1) {
+ if (st.st_size && ! (st.st_mode & S_IXUSR)) {
+ ld->fnsave[25] = '.';
+ ld->fnsave[26] = 'u';
+ ld->fnsave[27] = '\0';
+ do {
+ taia_now(&now);
+ fmt_taia25(ld->fnsave, &now);
+ errno = 0;
+ stat(ld->fnsave, &st);
+ } while (errno != ENOENT);
+ while (rename("current", ld->fnsave) == -1)
+ pause2cannot("rename current", ld->name);
+ rmoldest(ld);
+ i = -1;
+ } else {
+ /* Be paranoid: st.st_size can be not just bigger, but WIDER! */
+ /* (bug in original svlogd. remove this comment when fixed there) */
+ ld->size = (st.st_size > ld->sizemax) ? ld->sizemax : st.st_size;
+ }
+ } else {
+ if (errno != ENOENT) {
+ logdir_close(ld);
+ warn2("cannot stat current", ld->name);
+ while (fchdir(fdwdir) == -1)
+ pause1cannot("change to initial working directory");
+ return 0;
+ }
+ }
+ while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1)
+ pause2cannot("open current", ld->name);
+ /* we presume this cannot fail */
+ ld->filecur = fdopen(ld->fdcur, "a"); ////
+ setvbuf(ld->filecur, NULL, _IOFBF, linelen); ////
+
+ coe(ld->fdcur);
+ while (fchmod(ld->fdcur, 0644) == -1)
+ pause2cannot("set mode of current", ld->name);
+
+ if (verbose) {
+ if (i == 0) bb_error_msg(INFO"append: %s/current", ld->name);
+ else bb_error_msg(INFO"new: %s/current", ld->name);
+ }
+
+ while (fchdir(fdwdir) == -1)
+ pause1cannot("change to initial working directory");
+ return 1;
+}
+
+static void logdirs_reopen(void)
+{
+ struct taia now;
+ int l;
+ int ok = 0;
+
+ tmaxflag = 0;
+ taia_now(&now);
+ for (l = 0; l < dirn; ++l) {
+ logdir_close(&dir[l]);
+ if (logdir_open(&dir[l], fndir[l])) ok = 1;
+ }
+ if (!ok) fatalx("no functional log directories");
+}
+
+/* Will look good in libbb one day */
+static ssize_t ndelay_read(int fd, void *buf, size_t count)
+{
+ if (!(fl_flag_0 & O_NONBLOCK))
+ fcntl(fd, F_SETFL, fl_flag_0 | O_NONBLOCK);
+ count = safe_read(fd, buf, count);
+ if (!(fl_flag_0 & O_NONBLOCK))
+ fcntl(fd, F_SETFL, fl_flag_0);
+ return count;
+}
+
+/* Used for reading stdin */
+static int buffer_pread(int fd, char *s, unsigned len, struct taia *now)
+{
+ int i;
+
+ if (rotateasap) {
+ for (i = 0; i < dirn; ++i)
+ rotate(dir+i);
+ rotateasap = 0;
+ }
+ if (exitasap) {
+ if (linecomplete)
+ return 0;
+ len = 1;
+ }
+ if (reopenasap) {
+ logdirs_reopen();
+ reopenasap = 0;
+ }
+ taia_uint(&trotate, 2744);
+ taia_add(&trotate, now, &trotate);
+ for (i = 0; i < dirn; ++i)
+ if (dir[i].tmax) {
+ if (taia_less(&dir[i].trotate, now))
+ rotate(dir+i);
+ if (taia_less(&dir[i].trotate, &trotate))
+ trotate = dir[i].trotate;
+ }
+
+ while (1) {
+ sigprocmask(SIG_UNBLOCK, blocked_sigset, NULL);
+ iopause(&input, 1, &trotate, now);
+ sigprocmask(SIG_BLOCK, blocked_sigset, NULL);
+ i = ndelay_read(fd, s, len);
+ if (i >= 0) break;
+ if (errno != EAGAIN) {
+ warn("cannot read standard input");
+ break;
+ }
+ /* else: EAGAIN - normal, repeat silently */
+ }
+
+ if (i > 0) {
+ int cnt;
+ linecomplete = (s[i-1] == '\n');
+ if (!repl) return i;
+
+ cnt = i;
+ while (--cnt >= 0) {
+ char ch = *s;
+ if (ch != '\n') {
+ if (ch < 32 || ch > 126)
+ *s = repl;
+ else {
+ int j;
+ for (j = 0; replace[j]; ++j) {
+ if (ch == replace[j]) {
+ *s = repl;
+ break;
+ }
+ }
+ }
+ }
+ s++;
+ }
+ }
+ return i;
+}
+
+
+static void sig_term_handler(int sig_no)
+{
+ if (verbose)
+ bb_error_msg(INFO"sig%s received", "term");
+ exitasap = 1;
+}
+
+static void sig_child_handler(int sig_no)
+{
+ int pid, l;
+
+ if (verbose)
+ bb_error_msg(INFO"sig%s received", "child");
+ while ((pid = wait_nohang(&wstat)) > 0)
+ for (l = 0; l < dirn; ++l)
+ if (dir[l].ppid == pid) {
+ dir[l].ppid = 0;
+ processorstop(&dir[l]);
+ break;
+ }
+}
+
+static void sig_alarm_handler(int sig_no)
+{
+ if (verbose)
+ bb_error_msg(INFO"sig%s received", "alarm");
+ rotateasap = 1;
+}
+
+static void sig_hangup_handler(int sig_no)
+{
+ if (verbose)
+ bb_error_msg(INFO"sig%s received", "hangup");
+ reopenasap = 1;
+}
+
+static void logmatch(struct logdir *ld)
+{
+ char *s;
+
+ ld->match = '+';
+ ld->matcherr = 'E';
+ s = ld->inst;
+ while (s && s[0]) {
+ switch (s[0]) {
+ case '+':
+ case '-':
+ if (pmatch(s+1, line, linelen))
+ ld->match = s[0];
+ break;
+ case 'e':
+ case 'E':
+ if (pmatch(s+1, line, linelen))
+ ld->matcherr = s[0];
+ break;
+ }
+ s += strlen(s) + 1;
+ }
+}
+
+int svlogd_main(int argc, char **argv);
+int svlogd_main(int argc, char **argv)
+{
+ sigset_t ss;
+ char *r,*l,*b;
+ ssize_t stdin_cnt = 0;
+ int i;
+ unsigned opt;
+ unsigned timestamp = 0;
+ void* (*memRchr)(const void *, int, size_t) = memchr;
+
+#define line bb_common_bufsiz1
+
+ opt_complementary = "tt:vv";
+ opt = getopt32(argc, argv, "r:R:l:b:tv",
+ &r, &replace, &l, &b, &timestamp, &verbose);
+ if (opt & 1) { // -r
+ repl = r[0];
+ if (!repl || r[1]) usage();
+ }
+ if (opt & 2) if (!repl) repl = '_'; // -R
+ if (opt & 4) { // -l
+ linemax = xatou_range(l, 0, BUFSIZ-26);
+ if (linemax == 0) linemax = BUFSIZ-26;
+ if (linemax < 256) linemax = 256;
+ }
+ ////if (opt & 8) { // -b
+ //// buflen = xatoi_u(b);
+ //// if (buflen == 0) buflen = 1024;
+ ////}
+ //if (opt & 0x10) timestamp++; // -t
+ //if (opt & 0x20) verbose++; // -v
+ //if (timestamp > 2) timestamp = 2;
+ argv += optind;
+ argc -= optind;
+
+ dirn = argc;
+ if (dirn <= 0) usage();
+ ////if (buflen <= linemax) usage();
+ fdwdir = xopen(".", O_RDONLY|O_NDELAY);
+ coe(fdwdir);
+ dir = xmalloc(dirn * sizeof(struct logdir));
+ for (i = 0; i < dirn; ++i) {
+ dir[i].fddir = -1;
+ dir[i].fdcur = -1;
+ ////dir[i].btmp = xmalloc(buflen);
+ dir[i].ppid = 0;
+ }
+ /* line = xmalloc(linemax + (timestamp ? 26 : 0)); */
+ fndir = argv;
+ input.fd = 0;
+ input.events = IOPAUSE_READ;
+ /* We cannot set NONBLOCK on fd #0 permanently - this setting
+ * _isn't_ per-process! It is shared among all other processes
+ * with the same stdin */
+ fl_flag_0 = fcntl(0, F_GETFL, 0);
+
+ blocked_sigset = &ss;
+ sigemptyset(&ss);
+ sigaddset(&ss, SIGTERM);
+ sigaddset(&ss, SIGCHLD);
+ sigaddset(&ss, SIGALRM);
+ sigaddset(&ss, SIGHUP);
+ sigprocmask(SIG_BLOCK, &ss, NULL);
+ sig_catch(SIGTERM, sig_term_handler);
+ sig_catch(SIGCHLD, sig_child_handler);
+ sig_catch(SIGALRM, sig_alarm_handler);
+ sig_catch(SIGHUP, sig_hangup_handler);
+
+ logdirs_reopen();
+
+ /* Without timestamps, we don't have to print each line
+ * separately, so we can look for _last_ newline, not first,
+ * thus batching writes */
+ if (!timestamp)
+ memRchr = memrchr;
+
+ setvbuf(stderr, NULL, _IOFBF, linelen);
+
+ /* Each iteration processes one or more lines */
+ while (1) {
+ struct taia now;
+ char stamp[FMT_PTIME];
+ char *lineptr;
+ char *printptr;
+ char *np;
+ int printlen;
+ char ch;
+
+ lineptr = line;
+ taia_now(&now);
+ /* Prepare timestamp if needed */
+ if (timestamp) {
+ switch (timestamp) {
+ case 1:
+ fmt_taia25(stamp, &now);
+ break;
+ default: /* case 2: */
+ fmt_ptime30nul(stamp, &now);
+ break;
+ }
+ lineptr += 26;
+ }
+
+ /* lineptr[0..linemax-1] - buffer for stdin */
+ /* (possibly has some unprocessed data from prev loop) */
+
+ /* Refill the buffer if needed */
+ np = memRchr(lineptr, '\n', stdin_cnt);
+ if (!np && !exitasap) {
+ i = linemax - stdin_cnt; /* avail. bytes at tail */
+ if (i >= 128) {
+ i = buffer_pread(0, lineptr + stdin_cnt, i, &now);
+ if (i <= 0) /* EOF or error on stdin */
+ exitasap = 1;
+ else {
+ np = memRchr(lineptr + stdin_cnt, '\n', i);
+ stdin_cnt += i;
+ }
+ }
+ }
+ if (stdin_cnt <= 0 && exitasap)
+ break;
+
+ /* Search for '\n' (in fact, np already holds the result) */
+ linelen = stdin_cnt;
+ if (np) {
+ print_to_nl: /* NB: starting from here lineptr may point
+ * farther out into line[] */
+ linelen = np - lineptr + 1;
+ }
+ /* linelen == no of chars incl. '\n' (or == stdin_cnt) */
+ ch = lineptr[linelen-1];
+
+ /* Biggest performance hit was coming from the fact
+ * that we did not buffer writes. We were reading many lines
+ * in one read() above, but wrote one line per write().
+ * We are using stdio to fix that */
+
+ /* write out lineptr[0..linelen-1] to each log destination
+ * (or lineptr[-26..linelen-1] if timestamping) */
+ printlen = linelen;
+ printptr = lineptr;
+ if (timestamp) {
+ printlen += 26;
+ printptr -= 26;
+ memcpy(printptr, stamp, 25);
+ printptr[25] = ' ';
+ }
+ for (i = 0; i < dirn; ++i) {
+ struct logdir *ld = &dir[i];
+ if (ld->fddir == -1) continue;
+ if (ld->inst)
+ logmatch(ld);
+ if (ld->matcherr == 'e')
+ ////full_write(2, printptr, printlen);
+ fwrite(lineptr, 1, linelen, stderr);
+ if (ld->match != '+') continue;
+ buffer_pwrite(i, printptr, printlen);
+ }
+
+ /* If we didn't see '\n' (long input line), */
+ /* read/write repeatedly until we see it */
+ while (ch != '\n') {
+ /* lineptr is emptied now, safe to use as buffer */
+ taia_now(&now);
+ stdin_cnt = exitasap ? -1 : buffer_pread(0, lineptr, linemax, &now);
+ if (stdin_cnt <= 0) { /* EOF or error on stdin */
+ exitasap = 1;
+ lineptr[0] = ch = '\n';
+ linelen = 1;
+ stdin_cnt = 1;
+ } else {
+ linelen = stdin_cnt;
+ np = memRchr(lineptr, '\n', stdin_cnt);
+ if (np)
+ linelen = np - lineptr + 1;
+ ch = lineptr[linelen-1];
+ }
+ /* linelen == no of chars incl. '\n' (or == stdin_cnt) */
+ for (i = 0; i < dirn; ++i) {
+ if (dir[i].fddir == -1) continue;
+ if (dir[i].matcherr == 'e')
+ ////full_write(2, lineptr, linelen);
+ fwrite(lineptr, 1, linelen, stderr);
+ if (dir[i].match != '+') continue;
+ buffer_pwrite(i, lineptr, linelen);
+ }
+ }
+
+ stdin_cnt -= linelen;
+ if (stdin_cnt > 0) {
+ lineptr += linelen;
+ /* If we see another '\n', we don't need to read
+ * next piece of input: can print what we have */
+ np = memRchr(lineptr, '\n', stdin_cnt);
+ if (np)
+ goto print_to_nl;
+ /* Move unprocessed data to the front of line */
+ memmove((timestamp ? line+26 : line), lineptr, stdin_cnt);
+ }
+ fflush(NULL);////
+ }
+
+ for (i = 0; i < dirn; ++i) {
+ if (dir[i].ppid)
+ while (!processorstop(&dir[i]))
+ /* repeat */;
+ logdir_close(&dir[i]);
+ }
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/scripts/Kbuild b/i/pc104/initrd/conf/busybox/scripts/Kbuild
new file mode 100644
index 0000000..83b4232
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/scripts/Kbuild
@@ -0,0 +1,7 @@
+###
+# scripts contains sources for various helper programs used throughout
+# the kernel for the build process.
+# ---------------------------------------------------------------------------
+
+# Let clean descend into subdirs
+subdir- += basic kconfig
diff --git a/i/pc104/initrd/conf/busybox/scripts/Kbuild.include b/i/pc104/initrd/conf/busybox/scripts/Kbuild.include
new file mode 100644
index 0000000..c6599c8
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/scripts/Kbuild.include
@@ -0,0 +1,154 @@
+####
+# kbuild: Generic definitions
+
+# Convinient variables
+comma := ,
+squote := '
+empty :=
+space := $(empty) $(empty)
+
+###
+# The temporary file to save gcc -MD generated dependencies must not
+# contain a comma
+depfile = $(subst $(comma),_,$(@D)/.$(@F).d)
+
+###
+# Escape single quote for use in echo statements
+escsq = $(subst $(squote),'\$(squote)',$1)
+
+###
+# filechk is used to check if the content of a generated file is updated.
+# Sample usage:
+# define filechk_sample
+# echo $KERNELRELEASE
+# endef
+# version.h : Makefile
+# $(call filechk,sample)
+# The rule defined shall write to stdout the content of the new file.
+# The existing file will be compared with the new one.
+# - If no file exist it is created
+# - If the content differ the new file is used
+# - If they are equal no change, and no timestamp update
+# - stdin is piped in from the first prerequisite ($<) so one has
+# to specify a valid file as first prerequisite (often the kbuild file)
+define filechk
+ $(Q)set -e; \
+ echo ' CHK $@'; \
+ mkdir -p $(dir $@); \
+ $(filechk_$(1)) < $< > $@.tmp; \
+ if [ -r $@ ] && cmp -s $@ $@.tmp; then \
+ rm -f $@.tmp; \
+ else \
+ echo ' UPD $@'; \
+ mv -f $@.tmp $@; \
+ fi
+endef
+
+######
+# gcc support functions
+# See documentation in Documentation/kbuild/makefiles.txt
+
+# as-option
+# Usage: cflags-y += $(call as-option, -Wa$(comma)-isa=foo,)
+
+as-option = $(shell if $(CC) $(CFLAGS) $(1) -Wa,-Z -c -o /dev/null \
+ -xassembler /dev/null > /dev/null 2>&1; then echo "$(1)"; \
+ else echo "$(2)"; fi ;)
+
+# cc-option
+# Usage: cflags-y += $(call cc-option, -march=winchip-c6, -march=i586)
+
+cc-option = $(shell if $(CC) $(CFLAGS) $(1) -S -o /dev/null -xc /dev/null \
+ > /dev/null 2>&1; then echo "$(1)"; else echo "$(2)"; fi ;)
+
+# hostcc-option
+# Usage: hostcflags-y += $(call hostcc-option, -march=winchip-c6, -march=i586)
+
+hostcc-option = $(shell if $(HOSTCC) $(HOSTCFLAGS) $(1) -S -o /dev/null -xc /dev/null \
+ > /dev/null 2>&1; then echo "$(1)"; else echo "$(2)"; fi ;)
+
+# cc-option-yn
+# Usage: flag := $(call cc-option-yn, -march=winchip-c6)
+cc-option-yn = $(shell if $(CC) $(CFLAGS) $(1) -S -o /dev/null -xc /dev/null \
+ > /dev/null 2>&1; then echo "y"; else echo "n"; fi;)
+
+# cc-option-align
+# Prefix align with either -falign or -malign
+cc-option-align = $(subst -functions=0,,\
+ $(call cc-option,-falign-functions=0,-malign-functions=0))
+
+# cc-version
+# Usage gcc-ver := $(call cc-version, $(CC))
+cc-version = $(shell $(CONFIG_SHELL) $(srctree)/scripts/gcc-version.sh \
+ $(if $(1), $(1), $(CC)))
+
+# cc-ifversion
+# Usage: EXTRA_CFLAGS += $(call cc-ifversion, -lt, 0402, -O1)
+cc-ifversion = $(shell if [ $(call cc-version, $(CC)) $(1) $(2) ]; then \
+ echo $(3); fi;)
+
+###
+# Shorthand for $(Q)$(MAKE) -f scripts/Makefile.build obj=
+# Usage:
+# $(Q)$(MAKE) $(build)=dir
+build := -f $(if $(KBUILD_SRC),$(srctree)/)scripts/Makefile.build obj
+
+# Prefix -I with $(srctree) if it is not an absolute path
+addtree = $(if $(filter-out -I/%,$(1)),$(patsubst -I%,-I$(srctree)/%,$(1))) $(1)
+# Find all -I options and call addtree
+flags = $(foreach o,$($(1)),$(if $(filter -I%,$(o)),$(call addtree,$(o)),$(o)))
+
+# If quiet is set, only print short version of command
+cmd = @$(echo-cmd) $(cmd_$(1))
+
+# Add $(obj)/ for paths that is not absolute
+objectify = $(foreach o,$(1),$(if $(filter /%,$(o)),$(o),$(obj)/$(o)))
+
+###
+# if_changed - execute command if any prerequisite is newer than
+# target, or command line has changed
+# if_changed_dep - as if_changed, but uses fixdep to reveal dependencies
+# including used config symbols
+# if_changed_rule - as if_changed but execute rule instead
+# See Documentation/kbuild/makefiles.txt for more info
+
+ifneq ($(KBUILD_NOCMDDEP),1)
+# Check if both arguments has same arguments. Result in empty string if equal
+# User may override this check using make KBUILD_NOCMDDEP=1
+arg-check = $(strip $(filter-out $(1), $(2)) $(filter-out $(2), $(1)) )
+endif
+
+# echo command. Short version is $(quiet) equals quiet, otherwise full command
+echo-cmd = $(if $($(quiet)cmd_$(1)), \
+ echo ' $(call escsq,$($(quiet)cmd_$(1)))';)
+
+make-cmd = $(subst \#,\\\#,$(subst $$,$$$$,$(call escsq,$(cmd_$(1)))))
+
+# function to only execute the passed command if necessary
+# >'< substitution is for echo to work, >$< substitution to preserve $ when reloading .cmd file
+# note: when using inline perl scripts [perl -e '...$$t=1;...'] in $(cmd_xxx) double $$ your perl vars
+#
+if_changed = $(if $(strip $(filter-out $(PHONY),$?) \
+ $(call arg-check, $(cmd_$(1)), $(cmd_$@)) ), \
+ @set -e; \
+ $(echo-cmd) $(cmd_$(1)); \
+ echo 'cmd_$@ := $(make-cmd)' > $(@D)/.$(@F).cmd)
+
+# execute the command and also postprocess generated .d dependencies
+# file
+if_changed_dep = $(if $(strip $(filter-out $(PHONY),$?) \
+ $(filter-out FORCE $(wildcard $^),$^) \
+ $(call arg-check, $(cmd_$(1)), $(cmd_$@)) ), \
+ @set -e; \
+ $(echo-cmd) $(cmd_$(1)); \
+ scripts/basic/fixdep $(depfile) $@ '$(make-cmd)' > $(@D)/.$(@F).tmp; \
+ rm -f $(depfile); \
+ mv -f $(@D)/.$(@F).tmp $(@D)/.$(@F).cmd)
+
+# Usage: $(call if_changed_rule,foo)
+# will check if $(cmd_foo) changed, or any of the prequisites changed,
+# and if so will execute $(rule_foo)
+if_changed_rule = $(if $(strip $(filter-out $(PHONY),$?) \
+ $(call arg-check, $(cmd_$(1)), $(cmd_$@)) ),\
+ @set -e; \
+ $(rule_$(1)))
diff --git a/i/pc104/initrd/conf/busybox/scripts/Makefile.IMA b/i/pc104/initrd/conf/busybox/scripts/Makefile.IMA
new file mode 100644
index 0000000..a923731
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/scripts/Makefile.IMA
@@ -0,0 +1,134 @@
+# This is completely unsupported.
+# Fix COMBINED_COMPILE upstream (in the Kbuild) and propagate
+# the changes back
+srctree := $(if $(KBUILD_SRC),$(KBUILD_SRC),$(CURDIR))
+objtree := $(CURDIR)
+src := $(srctree)
+obj := $(objtree)
+
+default: busybox
+include .config
+ifdef CONFIG_FEATURE_COMPRESS_USAGE
+usage_stuff = include/usage_compressed.h
+endif
+
+# pull in the config stuff
+lib-all-y := applets/applets.o applets/busybox.o
+lib-y:=
+include procps/Kbuild
+lib-all-y += $(patsubst %,procps/%,$(sort $(lib-y)))
+lib-y:=
+include networking/Kbuild
+lib-all-y += $(patsubst %,networking/%,$(sort $(lib-y)))
+lib-y:=
+include networking/udhcp/Kbuild
+lib-all-y += $(patsubst %,networking/udhcp/%,$(sort $(lib-y)))
+lib-y:=
+include networking/libiproute/Kbuild
+lib-all-y += $(patsubst %,networking/libiproute/%,$(sort $(lib-y)))
+lib-y:=
+include loginutils/Kbuild
+lib-all-y += $(patsubst %,loginutils/%,$(sort $(lib-y)))
+lib-y:=
+include archival/Kbuild
+lib-all-y += $(patsubst %,archival/%,$(sort $(lib-y)))
+lib-y:=
+include archival/libunarchive/Kbuild
+lib-all-y += $(patsubst %,archival/libunarchive/%,$(sort $(lib-y)))
+lib-y:=
+include applets/Kbuild
+lib-all-y += $(patsubst %,applets/%,$(sort $(lib-y)))
+lib-y:=
+include e2fsprogs/Kbuild
+lib-all-y += $(patsubst %,e2fsprogs/%,$(sort $(lib-y)))
+lib-y:=
+#include e2fsprogs/old_e2fsprogs/Kbuild
+#lib-all-y += $(patsubst %,e2fsprogs/old_e2fsprogs/%,$(sort $(lib-y)))
+#lib-y:=
+#include e2fsprogs/old_e2fsprogs/ext2fs/Kbuild
+#lib-all-y += $(patsubst %,e2fsprogs/old_e2fsprogs/ext2fs/%,$(sort $(lib-y)))
+#lib-y:=
+#include e2fsprogs/old_e2fsprogs/blkid/Kbuild
+#lib-all-y += $(patsubst %,e2fsprogs/old_e2fsprogs/blkid/%,$(sort $(lib-y)))
+#lib-y:=
+#include e2fsprogs/old_e2fsprogs/uuid/Kbuild
+#lib-all-y += $(patsubst %,e2fsprogs/old_e2fsprogs/uuid/%,$(sort $(lib-y)))
+#lib-y:=
+#include e2fsprogs/old_e2fsprogs/e2p/Kbuild
+#lib-all-y += $(patsubst %,e2fsprogs/old_e2fsprogs/e2p/%,$(sort $(lib-y)))
+#lib-y:=
+include debianutils/Kbuild
+lib-all-y += $(patsubst %,debianutils/%,$(sort $(lib-y)))
+lib-y:=
+include runit/Kbuild
+lib-all-y += $(patsubst %,runit/%,$(sort $(lib-y)))
+lib-y:=
+include modutils/Kbuild
+lib-all-y += $(patsubst %,modutils/%,$(sort $(lib-y)))
+lib-y:=
+include miscutils/Kbuild
+lib-all-y += $(patsubst %,miscutils/%,$(sort $(lib-y)))
+lib-y:=
+include coreutils/libcoreutils/Kbuild
+lib-all-y += $(patsubst %,coreutils/libcoreutils/%,$(sort $(lib-y)))
+lib-y:=
+include coreutils/Kbuild
+lib-all-y += $(patsubst %,coreutils/%,$(sort $(lib-y)))
+lib-y:=
+include sysklogd/Kbuild
+lib-all-y += $(patsubst %,sysklogd/%,$(sort $(lib-y)))
+lib-y:=
+include shell/Kbuild
+lib-all-y += $(patsubst %,shell/%,$(sort $(lib-y)))
+lib-y:=
+include console-tools/Kbuild
+lib-all-y += $(patsubst %,console-tools/%,$(sort $(lib-y)))
+lib-y:=
+include findutils/Kbuild
+lib-all-y += $(patsubst %,findutils/%,$(sort $(lib-y)))
+lib-y:=
+include util-linux/Kbuild
+lib-all-y += $(patsubst %,util-linux/%,$(sort $(lib-y)))
+lib-y:=
+include init/Kbuild
+lib-all-y += $(patsubst %,init/%,$(sort $(lib-y)))
+lib-y:=
+include libpwdgrp/Kbuild
+lib-all-y += $(patsubst %,libpwdgrp/%,$(sort $(lib-y)))
+lib-y:=
+include editors/Kbuild
+lib-all-y += $(patsubst %,editors/%,$(sort $(lib-y)))
+lib-y:=
+include selinux/Kbuild
+lib-all-y += $(patsubst %,selinux/%,$(sort $(lib-y)))
+lib-y:=
+include scripts/Kbuild
+lib-all-y += $(patsubst %,scripts/%,$(sort $(lib-y)))
+lib-y:=
+include libbb/Kbuild
+lib-all-y += $(patsubst %,libbb/%,$(sort $(lib-y)))
+lib-y:=
+
+include Makefile.flags
+ifndef BB_VER
+BB_VER:=""
+endif
+
+CPPFLAGS+= -D"KBUILD_STR(s)=\#s" #-Q
+
+HOSTCC = gcc
+CC = gcc
+WHOLE_PROGRAM:=$(call cc-option,-fwhole-program,)
+busybox: $(usage_stuff)
+ $(CC) $(CPPFLAGS) $(CFLAGS) $(EXTRA_CFLAGS) --combine $(WHOLE_PROGRAM) \
+ -funit-at-a-time -Wno-error -std=gnu99 \
+ -o $(@)_unstripped $(lib-all-y:.o=.c) \
+ -Wl,--start-group -lcrypt -lm -Wl,--end-group
+ cp $(@)_unstripped $@
+ -strip -s -R .note -R .comment -R .version $@
+
+applets/usage:
+ $(HOSTCC) -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -I$(srctree)/include -o applets/usage applets/usage.c
+include/usage_compressed.h: $(srctree)/include/usage.h applets/usage
+ $(srctree)/applets/usage_compressed include/usage_compressed.h applets
+
diff --git a/i/pc104/initrd/conf/busybox/scripts/Makefile.build b/i/pc104/initrd/conf/busybox/scripts/Makefile.build
new file mode 100644
index 0000000..ddefea5
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/scripts/Makefile.build
@@ -0,0 +1,338 @@
+# ==========================================================================
+# Building
+# ==========================================================================
+
+src := $(obj)
+
+PHONY := __build
+__build:
+
+# Read .config if it exist, otherwise ignore
+-include .config
+
+include scripts/Kbuild.include
+
+# The filename Kbuild has precedence over Makefile
+kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src))
+include $(if $(wildcard $(kbuild-dir)/Kbuild), $(kbuild-dir)/Kbuild, $(kbuild-dir)/Makefile)
+
+include scripts/Makefile.lib
+
+ifdef host-progs
+ifneq ($(hostprogs-y),$(host-progs))
+$(warning kbuild: $(obj)/Makefile - Usage of host-progs is deprecated. Please replace with hostprogs-y!)
+hostprogs-y += $(host-progs)
+endif
+endif
+
+# Do not include host rules unles needed
+ifneq ($(hostprogs-y)$(hostprogs-m),)
+include scripts/Makefile.host
+endif
+
+ifneq ($(KBUILD_SRC),)
+# Create output directory if not already present
+_dummy := $(shell [ -d $(obj) ] || mkdir -p $(obj))
+
+# Create directories for object files if directory does not exist
+# Needed when obj-y := dir/file.o syntax is used
+_dummy := $(foreach d,$(obj-dirs), $(shell [ -d $(d) ] || mkdir -p $(d)))
+endif
+
+
+ifdef EXTRA_TARGETS
+$(warning kbuild: $(obj)/Makefile - Usage of EXTRA_TARGETS is obsolete in 2.6. Please fix!)
+endif
+
+ifdef build-targets
+$(warning kbuild: $(obj)/Makefile - Usage of build-targets is obsolete in 2.6. Please fix!)
+endif
+
+ifdef export-objs
+$(warning kbuild: $(obj)/Makefile - Usage of export-objs is obsolete in 2.6. Please fix!)
+endif
+
+ifdef O_TARGET
+$(warning kbuild: $(obj)/Makefile - Usage of O_TARGET := $(O_TARGET) is obsolete in 2.6. Please fix!)
+endif
+
+ifdef L_TARGET
+$(error kbuild: $(obj)/Makefile - Use of L_TARGET is replaced by lib-y in 2.6. Please fix!)
+endif
+
+ifdef list-multi
+$(warning kbuild: $(obj)/Makefile - list-multi := $(list-multi) is obsolete in 2.6. Please fix!)
+endif
+
+ifndef obj
+$(warning kbuild: Makefile.build is included improperly)
+endif
+
+# ===========================================================================
+
+ifneq ($(strip $(lib-y) $(lib-m) $(lib-n) $(lib-)),)
+lib-target := $(obj)/lib.a
+endif
+
+ifneq ($(strip $(obj-y) $(obj-m) $(obj-n) $(obj-) $(lib-target)),)
+builtin-target := $(obj)/built-in.o
+endif
+
+# We keep a list of all modules in $(MODVERDIR)
+
+__build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y)) \
+ $(if $(KBUILD_MODULES),$(obj-m)) \
+ $(subdir-ym) $(always)
+ @:
+
+# Linus' kernel sanity checking tool
+ifneq ($(KBUILD_CHECKSRC),0)
+ ifeq ($(KBUILD_CHECKSRC),2)
+ quiet_cmd_force_checksrc = CHECK $<
+ cmd_force_checksrc = $(CHECK) $(CHECKFLAGS) $(c_flags) $< ;
+ else
+ quiet_cmd_checksrc = CHECK $<
+ cmd_checksrc = $(CHECK) $(CHECKFLAGS) $(c_flags) $< ;
+ endif
+endif
+
+
+# Compile C sources (.c)
+# ---------------------------------------------------------------------------
+
+# Default is built-in, unless we know otherwise
+modkern_cflags := $(CFLAGS_KERNEL)
+quiet_modtag := $(empty) $(empty)
+
+$(real-objs-m) : modkern_cflags := $(CFLAGS_MODULE)
+$(real-objs-m:.o=.i) : modkern_cflags := $(CFLAGS_MODULE)
+$(real-objs-m:.o=.s) : modkern_cflags := $(CFLAGS_MODULE)
+$(real-objs-m:.o=.lst): modkern_cflags := $(CFLAGS_MODULE)
+
+$(real-objs-m) : quiet_modtag := [M]
+$(real-objs-m:.o=.i) : quiet_modtag := [M]
+$(real-objs-m:.o=.s) : quiet_modtag := [M]
+$(real-objs-m:.o=.lst): quiet_modtag := [M]
+
+$(obj-m) : quiet_modtag := [M]
+
+# Default for not multi-part modules
+modname = $(*F)
+
+$(multi-objs-m) : modname = $(modname-multi)
+$(multi-objs-m:.o=.i) : modname = $(modname-multi)
+$(multi-objs-m:.o=.s) : modname = $(modname-multi)
+$(multi-objs-m:.o=.lst) : modname = $(modname-multi)
+$(multi-objs-y) : modname = $(modname-multi)
+$(multi-objs-y:.o=.i) : modname = $(modname-multi)
+$(multi-objs-y:.o=.s) : modname = $(modname-multi)
+$(multi-objs-y:.o=.lst) : modname = $(modname-multi)
+
+quiet_cmd_cc_s_c = CC $(quiet_modtag) $@
+cmd_cc_s_c = $(CC) $(c_flags) -fverbose-asm -S -o $@ $<
+
+%.s: %.c FORCE
+ $(call if_changed_dep,cc_s_c)
+
+quiet_cmd_cc_i_c = CPP $(quiet_modtag) $@
+cmd_cc_i_c = $(CPP) $(c_flags) -o $@ $<
+
+%.i: %.c FORCE
+ $(call if_changed_dep,cc_i_c)
+
+# C (.c) files
+# The C file is compiled and updated dependency information is generated.
+# (See cmd_cc_o_c + relevant part of rule_cc_o_c)
+
+quiet_cmd_cc_o_c = CC $(quiet_modtag) $@
+
+ifndef CONFIG_MODVERSIONS
+cmd_cc_o_c = $(CC) $(c_flags) -c -o $@ $<
+
+else
+# When module versioning is enabled the following steps are executed:
+# o compile a .tmp_<file>.o from <file>.c
+# o if .tmp_<file>.o doesn't contain a __ksymtab version, i.e. does
+# not export symbols, we just rename .tmp_<file>.o to <file>.o and
+# are done.
+# o otherwise, we calculate symbol versions using the good old
+# genksyms on the preprocessed source and postprocess them in a way
+# that they are usable as a linker script
+# o generate <file>.o from .tmp_<file>.o using the linker to
+# replace the unresolved symbols __crc_exported_symbol with
+# the actual value of the checksum generated by genksyms
+
+cmd_cc_o_c = $(CC) $(c_flags) -c -o $(@D)/.tmp_$(@F) $<
+cmd_modversions = \
+ if $(OBJDUMP) -h $(@D)/.tmp_$(@F) | grep -q __ksymtab; then \
+ $(CPP) -D__GENKSYMS__ $(c_flags) $< \
+ | $(GENKSYMS) -a $(ARCH) \
+ > $(@D)/.tmp_$(@F:.o=.ver); \
+ \
+ $(LD) $(LDFLAGS) -r -o $@ $(@D)/.tmp_$(@F) \
+ -T $(@D)/.tmp_$(@F:.o=.ver); \
+ rm -f $(@D)/.tmp_$(@F) $(@D)/.tmp_$(@F:.o=.ver); \
+ else \
+ mv -f $(@D)/.tmp_$(@F) $@; \
+ fi;
+endif
+
+define rule_cc_o_c
+ $(call echo-cmd,checksrc) $(cmd_checksrc) \
+ $(call echo-cmd,cc_o_c) $(cmd_cc_o_c); \
+ $(cmd_modversions) \
+ scripts/basic/fixdep $(depfile) $@ '$(call make-cmd,cc_o_c)' > $(@D)/.$(@F).tmp; \
+ rm -f $(depfile); \
+ mv -f $(@D)/.$(@F).tmp $(@D)/.$(@F).cmd
+endef
+
+# Built-in and composite module parts
+
+%.o: %.c FORCE
+ $(call cmd,force_checksrc)
+ $(call if_changed_rule,cc_o_c)
+
+# Single-part modules are special since we need to mark them in $(MODVERDIR)
+
+$(single-used-m): %.o: %.c FORCE
+ $(call cmd,force_checksrc)
+ $(call if_changed_rule,cc_o_c)
+ @{ echo $(@:.o=.ko); echo $@; } > $(MODVERDIR)/$(@F:.o=.mod)
+
+quiet_cmd_cc_lst_c = MKLST $@
+ cmd_cc_lst_c = $(CC) $(c_flags) -g -c -o $*.o $< && \
+ $(CONFIG_SHELL) $(srctree)/scripts/makelst $*.o \
+ System.map $(OBJDUMP) > $@
+
+%.lst: %.c FORCE
+ $(call if_changed_dep,cc_lst_c)
+
+# Compile assembler sources (.S)
+# ---------------------------------------------------------------------------
+
+modkern_aflags := $(AFLAGS_KERNEL)
+
+$(real-objs-m) : modkern_aflags := $(AFLAGS_MODULE)
+$(real-objs-m:.o=.s): modkern_aflags := $(AFLAGS_MODULE)
+
+quiet_cmd_as_s_S = CPP $(quiet_modtag) $@
+cmd_as_s_S = $(CPP) $(a_flags) -o $@ $<
+
+%.s: %.S FORCE
+ $(call if_changed_dep,as_s_S)
+
+quiet_cmd_as_o_S = AS $(quiet_modtag) $@
+cmd_as_o_S = $(CC) $(a_flags) -c -o $@ $<
+
+%.o: %.S FORCE
+ $(call if_changed_dep,as_o_S)
+
+targets += $(real-objs-y) $(real-objs-m) $(lib-y)
+targets += $(extra-y) $(MAKECMDGOALS) $(always)
+
+# Linker scripts preprocessor (.lds.S -> .lds)
+# ---------------------------------------------------------------------------
+quiet_cmd_cpp_lds_S = LDS $@
+ cmd_cpp_lds_S = $(CPP) $(cpp_flags) -D__ASSEMBLY__ -o $@ $<
+
+%.lds: %.lds.S FORCE
+ $(call if_changed_dep,cpp_lds_S)
+
+# Build the compiled-in targets
+# ---------------------------------------------------------------------------
+
+# To build objects in subdirs, we need to descend into the directories
+$(sort $(subdir-obj-y)): $(subdir-ym) ;
+
+#
+# Rule to compile a set of .o files into one .o file
+#
+ifdef builtin-target
+quiet_cmd_link_o_target = LD $@
+# If the list of objects to link is empty, just create an empty built-in.o
+cmd_link_o_target = $(if $(strip $(obj-y)),\
+ $(LD) $(ld_flags) -r -o $@ $(filter $(obj-y), $^),\
+ rm -f $@; $(AR) rcs $@)
+
+$(builtin-target): $(obj-y) FORCE
+ $(call if_changed,link_o_target)
+
+targets += $(builtin-target)
+endif # builtin-target
+
+#
+# Rule to compile a set of .o files into one .a file
+#
+ifdef lib-target
+quiet_cmd_link_l_target = AR $@
+cmd_link_l_target = rm -f $@; $(AR) $(EXTRA_ARFLAGS) rcs $@ $(lib-y)
+
+$(lib-target): $(lib-y) FORCE
+ $(call if_changed,link_l_target)
+
+targets += $(lib-target)
+endif
+
+#
+# Rule to link composite objects
+#
+# Composite objects are specified in kbuild makefile as follows:
+# <composite-object>-objs := <list of .o files>
+# or
+# <composite-object>-y := <list of .o files>
+link_multi_deps = \
+$(filter $(addprefix $(obj)/, \
+$($(subst $(obj)/,,$(@:.o=-objs))) \
+$($(subst $(obj)/,,$(@:.o=-y)))), $^)
+
+quiet_cmd_link_multi-y = LD $@
+cmd_link_multi-y = $(LD) $(ld_flags) -r -o $@ $(link_multi_deps)
+
+quiet_cmd_link_multi-m = LD [M] $@
+cmd_link_multi-m = $(LD) $(ld_flags) $(LDFLAGS_MODULE) -o $@ $(link_multi_deps)
+
+# We would rather have a list of rules like
+# foo.o: $(foo-objs)
+# but that's not so easy, so we rather make all composite objects depend
+# on the set of all their parts
+$(multi-used-y) : %.o: $(multi-objs-y) FORCE
+ $(call if_changed,link_multi-y)
+
+$(multi-used-m) : %.o: $(multi-objs-m) FORCE
+ $(call if_changed,link_multi-m)
+ @{ echo $(@:.o=.ko); echo $(link_multi_deps); } > $(MODVERDIR)/$(@F:.o=.mod)
+
+targets += $(multi-used-y) $(multi-used-m)
+
+
+# Descending
+# ---------------------------------------------------------------------------
+
+PHONY += $(subdir-ym)
+$(subdir-ym):
+ $(Q)$(MAKE) $(build)=$@
+
+# Add FORCE to the prequisites of a target to force it to be always rebuilt.
+# ---------------------------------------------------------------------------
+
+PHONY += FORCE
+
+FORCE:
+
+# Read all saved command lines and dependencies for the $(targets) we
+# may be building above, using $(if_changed{,_dep}). As an
+# optimization, we don't need to read them if the target does not
+# exist, we will rebuild anyway in that case.
+
+targets := $(wildcard $(sort $(targets)))
+cmd_files := $(wildcard $(foreach f,$(targets),$(dir $(f)).$(notdir $(f)).cmd))
+
+ifneq ($(cmd_files),)
+ include $(cmd_files)
+endif
+
+
+# Declare the contents of the .PHONY variable as phony. We keep that
+# information in a variable se we can use it in if_changed and friends.
+
+.PHONY: $(PHONY)
diff --git a/i/pc104/initrd/conf/busybox/scripts/Makefile.clean b/i/pc104/initrd/conf/busybox/scripts/Makefile.clean
new file mode 100644
index 0000000..cff3349
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/scripts/Makefile.clean
@@ -0,0 +1,102 @@
+# ==========================================================================
+# Cleaning up
+# ==========================================================================
+
+src := $(obj)
+
+PHONY := __clean
+__clean:
+
+# Shorthand for $(Q)$(MAKE) scripts/Makefile.clean obj=dir
+# Usage:
+# $(Q)$(MAKE) $(clean)=dir
+clean := -f $(if $(KBUILD_SRC),$(srctree)/)scripts/Makefile.clean obj
+
+# The filename Kbuild has precedence over Makefile
+kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src))
+include $(if $(wildcard $(kbuild-dir)/Kbuild), $(kbuild-dir)/Kbuild, $(kbuild-dir)/Makefile)
+
+# Figure out what we need to build from the various variables
+# ==========================================================================
+
+__subdir-y := $(patsubst %/,%,$(filter %/, $(obj-y)))
+subdir-y += $(__subdir-y)
+__subdir-m := $(patsubst %/,%,$(filter %/, $(obj-m)))
+subdir-m += $(__subdir-m)
+__subdir-n := $(patsubst %/,%,$(filter %/, $(obj-n)))
+subdir-n += $(__subdir-n)
+__subdir- := $(patsubst %/,%,$(filter %/, $(obj-)))
+subdir- += $(__subdir-)
+
+# Subdirectories we need to descend into
+
+subdir-ym := $(sort $(subdir-y) $(subdir-m))
+subdir-ymn := $(sort $(subdir-ym) $(subdir-n) $(subdir-))
+
+# Add subdir path
+
+subdir-ymn := $(addprefix $(obj)/,$(subdir-ymn))
+
+# build a list of files to remove, usually releative to the current
+# directory
+
+__clean-files := $(extra-y) $(EXTRA_TARGETS) $(always) \
+ $(targets) $(clean-files) \
+ $(host-progs) \
+ $(hostprogs-y) $(hostprogs-m) $(hostprogs-)
+
+# as clean-files is given relative to the current directory, this adds
+# a $(obj) prefix, except for absolute paths
+
+__clean-files := $(wildcard \
+ $(addprefix $(obj)/, $(filter-out /%, $(__clean-files))) \
+ $(filter /%, $(__clean-files)))
+
+# as clean-dirs is given relative to the current directory, this adds
+# a $(obj) prefix, except for absolute paths
+
+__clean-dirs := $(wildcard \
+ $(addprefix $(obj)/, $(filter-out /%, $(clean-dirs))) \
+ $(filter /%, $(clean-dirs)))
+
+# ==========================================================================
+
+quiet_cmd_clean = CLEAN $(obj)
+ cmd_clean = rm -f $(__clean-files)
+quiet_cmd_cleandir = CLEAN $(__clean-dirs)
+ cmd_cleandir = rm -rf $(__clean-dirs)
+
+
+__clean: $(subdir-ymn)
+ifneq ($(strip $(__clean-files)),)
+ +$(call cmd,clean)
+endif
+ifneq ($(strip $(__clean-dirs)),)
+ +$(call cmd,cleandir)
+endif
+ifneq ($(strip $(clean-rule)),)
+ +$(clean-rule)
+endif
+ @:
+
+
+# ===========================================================================
+# Generic stuff
+# ===========================================================================
+
+# Descending
+# ---------------------------------------------------------------------------
+
+PHONY += $(subdir-ymn)
+$(subdir-ymn):
+ $(Q)$(MAKE) $(clean)=$@
+
+# If quiet is set, only print short version of command
+
+cmd = @$(if $($(quiet)cmd_$(1)),echo ' $($(quiet)cmd_$(1))' &&) $(cmd_$(1))
+
+
+# Declare the contents of the .PHONY variable as phony. We keep that
+# information in a variable se we can use it in if_changed and friends.
+
+.PHONY: $(PHONY)
diff --git a/i/pc104/initrd/conf/busybox/scripts/Makefile.host b/i/pc104/initrd/conf/busybox/scripts/Makefile.host
new file mode 100644
index 0000000..763e2f2
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/scripts/Makefile.host
@@ -0,0 +1,156 @@
+# ==========================================================================
+# Building binaries on the host system
+# Binaries are used during the compilation of the kernel, for example
+# to preprocess a data file.
+#
+# Both C and C++ is supported, but preferred language is C for such utilities.
+#
+# Samle syntax (see Documentation/kbuild/makefile.txt for reference)
+# hostprogs-y := bin2hex
+# Will compile bin2hex.c and create an executable named bin2hex
+#
+# hostprogs-y := lxdialog
+# lxdialog-objs := checklist.o lxdialog.o
+# Will compile lxdialog.c and checklist.c, and then link the executable
+# lxdialog, based on checklist.o and lxdialog.o
+#
+# hostprogs-y := qconf
+# qconf-cxxobjs := qconf.o
+# qconf-objs := menu.o
+# Will compile qconf as a C++ program, and menu as a C program.
+# They are linked as C++ code to the executable qconf
+
+# hostprogs-y := conf
+# conf-objs := conf.o libkconfig.so
+# libkconfig-objs := expr.o type.o
+# Will create a shared library named libkconfig.so that consist of
+# expr.o and type.o (they are both compiled as C code and the object file
+# are made as position independent code).
+# conf.c is compiled as a c program, and conf.o is linked together with
+# libkconfig.so as the executable conf.
+# Note: Shared libraries consisting of C++ files are not supported
+
+__hostprogs := $(sort $(hostprogs-y)$(hostprogs-m))
+
+# hostprogs-y := tools/build may have been specified. Retreive directory
+obj-dirs += $(foreach f,$(__hostprogs), $(if $(dir $(f)),$(dir $(f))))
+obj-dirs := $(strip $(sort $(filter-out ./,$(obj-dirs))))
+
+
+# C code
+# Executables compiled from a single .c file
+host-csingle := $(foreach m,$(__hostprogs),$(if $($(m)-objs),,$(m)))
+
+# C executables linked based on several .o files
+host-cmulti := $(foreach m,$(__hostprogs),\
+ $(if $($(m)-cxxobjs),,$(if $($(m)-objs),$(m))))
+
+# Object (.o) files compiled from .c files
+host-cobjs := $(sort $(foreach m,$(__hostprogs),$($(m)-objs)))
+
+# C++ code
+# C++ executables compiled from at least on .cc file
+# and zero or more .c files
+host-cxxmulti := $(foreach m,$(__hostprogs),$(if $($(m)-cxxobjs),$(m)))
+
+# C++ Object (.o) files compiled from .cc files
+host-cxxobjs := $(sort $(foreach m,$(host-cxxmulti),$($(m)-cxxobjs)))
+
+# Shared libaries (only .c supported)
+# Shared libraries (.so) - all .so files referenced in "xxx-objs"
+host-cshlib := $(sort $(filter %.so, $(host-cobjs)))
+# Remove .so files from "xxx-objs"
+host-cobjs := $(filter-out %.so,$(host-cobjs))
+
+#Object (.o) files used by the shared libaries
+host-cshobjs := $(sort $(foreach m,$(host-cshlib),$($(m:.so=-objs))))
+
+__hostprogs := $(addprefix $(obj)/,$(__hostprogs))
+host-csingle := $(addprefix $(obj)/,$(host-csingle))
+host-cmulti := $(addprefix $(obj)/,$(host-cmulti))
+host-cobjs := $(addprefix $(obj)/,$(host-cobjs))
+host-cxxmulti := $(addprefix $(obj)/,$(host-cxxmulti))
+host-cxxobjs := $(addprefix $(obj)/,$(host-cxxobjs))
+host-cshlib := $(addprefix $(obj)/,$(host-cshlib))
+host-cshobjs := $(addprefix $(obj)/,$(host-cshobjs))
+obj-dirs := $(addprefix $(obj)/,$(obj-dirs))
+
+#####
+# Handle options to gcc. Support building with separate output directory
+
+_hostc_flags = $(HOSTCFLAGS) $(HOST_EXTRACFLAGS) $(HOSTCFLAGS_$(*F).o)
+_hostcxx_flags = $(HOSTCXXFLAGS) $(HOST_EXTRACXXFLAGS) $(HOSTCXXFLAGS_$(*F).o)
+
+ifeq ($(KBUILD_SRC),)
+__hostc_flags = $(_hostc_flags)
+__hostcxx_flags = $(_hostcxx_flags)
+else
+__hostc_flags = -I$(obj) $(call flags,_hostc_flags)
+__hostcxx_flags = -I$(obj) $(call flags,_hostcxx_flags)
+endif
+
+hostc_flags = -Wp,-MD,$(depfile) $(__hostc_flags)
+hostcxx_flags = -Wp,-MD,$(depfile) $(__hostcxx_flags)
+
+#####
+# Compile programs on the host
+
+# Create executable from a single .c file
+# host-csingle -> Executable
+quiet_cmd_host-csingle = HOSTCC $@
+ cmd_host-csingle = $(HOSTCC) $(hostc_flags) -o $@ $< \
+ $(HOST_LOADLIBES) $(HOSTLOADLIBES_$(@F))
+$(host-csingle): %: %.c FORCE
+ $(call if_changed_dep,host-csingle)
+
+# Link an executable based on list of .o files, all plain c
+# host-cmulti -> executable
+quiet_cmd_host-cmulti = HOSTLD $@
+ cmd_host-cmulti = $(HOSTCC) $(HOSTLDFLAGS) -o $@ \
+ $(addprefix $(obj)/,$($(@F)-objs)) \
+ $(HOST_LOADLIBES) $(HOSTLOADLIBES_$(@F))
+$(host-cmulti): %: $(host-cobjs) $(host-cshlib) FORCE
+ $(call if_changed,host-cmulti)
+
+# Create .o file from a single .c file
+# host-cobjs -> .o
+quiet_cmd_host-cobjs = HOSTCC $@
+ cmd_host-cobjs = $(HOSTCC) $(hostc_flags) -c -o $@ $<
+$(host-cobjs): %.o: %.c FORCE
+ $(call if_changed_dep,host-cobjs)
+
+# Link an executable based on list of .o files, a mixture of .c and .cc
+# host-cxxmulti -> executable
+quiet_cmd_host-cxxmulti = HOSTLD $@
+ cmd_host-cxxmulti = $(HOSTCXX) $(HOSTLDFLAGS) -o $@ \
+ $(foreach o,objs cxxobjs,\
+ $(addprefix $(obj)/,$($(@F)-$(o)))) \
+ $(HOST_LOADLIBES) $(HOSTLOADLIBES_$(@F))
+$(host-cxxmulti): %: $(host-cobjs) $(host-cxxobjs) $(host-cshlib) FORCE
+ $(call if_changed,host-cxxmulti)
+
+# Create .o file from a single .cc (C++) file
+quiet_cmd_host-cxxobjs = HOSTCXX $@
+ cmd_host-cxxobjs = $(HOSTCXX) $(hostcxx_flags) -c -o $@ $<
+$(host-cxxobjs): %.o: %.cc FORCE
+ $(call if_changed_dep,host-cxxobjs)
+
+# Compile .c file, create position independent .o file
+# host-cshobjs -> .o
+quiet_cmd_host-cshobjs = HOSTCC -fPIC $@
+ cmd_host-cshobjs = $(HOSTCC) $(hostc_flags) -fPIC -c -o $@ $<
+$(host-cshobjs): %.o: %.c FORCE
+ $(call if_changed_dep,host-cshobjs)
+
+# Link a shared library, based on position independent .o files
+# *.o -> .so shared library (host-cshlib)
+quiet_cmd_host-cshlib = HOSTLLD -shared $@
+ cmd_host-cshlib = $(HOSTCC) $(HOSTLDFLAGS) -shared -o $@ \
+ $(addprefix $(obj)/,$($(@F:.so=-objs))) \
+ $(HOST_LOADLIBES) $(HOSTLOADLIBES_$(@F))
+$(host-cshlib): %: $(host-cshobjs) FORCE
+ $(call if_changed,host-cshlib)
+
+targets += $(host-csingle) $(host-cmulti) $(host-cobjs)\
+ $(host-cxxmulti) $(host-cxxobjs) $(host-cshlib) $(host-cshobjs)
+
diff --git a/i/pc104/initrd/conf/busybox/scripts/Makefile.lib b/i/pc104/initrd/conf/busybox/scripts/Makefile.lib
new file mode 100644
index 0000000..e1e0ba4
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/scripts/Makefile.lib
@@ -0,0 +1,165 @@
+# Backward compatibility - to be removed...
+extra-y += $(EXTRA_TARGETS)
+# Figure out what we need to build from the various variables
+# ===========================================================================
+
+# When an object is listed to be built compiled-in and modular,
+# only build the compiled-in version
+
+obj-m := $(filter-out $(obj-y),$(obj-m))
+
+# Libraries are always collected in one lib file.
+# Filter out objects already built-in
+
+lib-y := $(filter-out $(obj-y), $(sort $(lib-y) $(lib-m)))
+
+
+# Handle objects in subdirs
+# ---------------------------------------------------------------------------
+# o if we encounter foo/ in $(obj-y), replace it by foo/built-in.o
+# and add the directory to the list of dirs to descend into: $(subdir-y)
+# o if we encounter foo/ in $(obj-m), remove it from $(obj-m)
+# and add the directory to the list of dirs to descend into: $(subdir-m)
+
+__subdir-y := $(patsubst %/,%,$(filter %/, $(obj-y)))
+subdir-y += $(__subdir-y)
+__subdir-m := $(patsubst %/,%,$(filter %/, $(obj-m)))
+subdir-m += $(__subdir-m)
+obj-y := $(patsubst %/, %/built-in.o, $(obj-y))
+obj-m := $(filter-out %/, $(obj-m))
+
+# Subdirectories we need to descend into
+
+subdir-ym := $(sort $(subdir-y) $(subdir-m))
+
+# if $(foo-objs) exists, foo.o is a composite object
+multi-used-y := $(sort $(foreach m,$(obj-y), $(if $(strip $($(m:.o=-objs)) $($(m:.o=-y))), $(m))))
+multi-used-m := $(sort $(foreach m,$(obj-m), $(if $(strip $($(m:.o=-objs)) $($(m:.o=-y))), $(m))))
+multi-used := $(multi-used-y) $(multi-used-m)
+single-used-m := $(sort $(filter-out $(multi-used-m),$(obj-m)))
+
+# Build list of the parts of our composite objects, our composite
+# objects depend on those (obviously)
+multi-objs-y := $(foreach m, $(multi-used-y), $($(m:.o=-objs)) $($(m:.o=-y)))
+multi-objs-m := $(foreach m, $(multi-used-m), $($(m:.o=-objs)) $($(m:.o=-y)))
+multi-objs := $(multi-objs-y) $(multi-objs-m)
+
+# $(subdir-obj-y) is the list of objects in $(obj-y) which do not live
+# in the local directory
+subdir-obj-y := $(foreach o,$(obj-y),$(if $(filter-out $(o),$(notdir $(o))),$(o)))
+
+# $(obj-dirs) is a list of directories that contain object files
+obj-dirs := $(dir $(multi-objs) $(subdir-obj-y))
+
+# Replace multi-part objects by their individual parts, look at local dir only
+real-objs-y := $(foreach m, $(filter-out $(subdir-obj-y), $(obj-y)), $(if $(strip $($(m:.o=-objs)) $($(m:.o=-y))),$($(m:.o=-objs)) $($(m:.o=-y)),$(m))) $(extra-y)
+real-objs-m := $(foreach m, $(obj-m), $(if $(strip $($(m:.o=-objs)) $($(m:.o=-y))),$($(m:.o=-objs)) $($(m:.o=-y)),$(m)))
+
+# Add subdir path
+
+extra-y := $(addprefix $(obj)/,$(extra-y))
+always := $(addprefix $(obj)/,$(always))
+targets := $(addprefix $(obj)/,$(targets))
+obj-y := $(addprefix $(obj)/,$(obj-y))
+obj-m := $(addprefix $(obj)/,$(obj-m))
+lib-y := $(addprefix $(obj)/,$(lib-y))
+subdir-obj-y := $(addprefix $(obj)/,$(subdir-obj-y))
+real-objs-y := $(addprefix $(obj)/,$(real-objs-y))
+real-objs-m := $(addprefix $(obj)/,$(real-objs-m))
+single-used-m := $(addprefix $(obj)/,$(single-used-m))
+multi-used-y := $(addprefix $(obj)/,$(multi-used-y))
+multi-used-m := $(addprefix $(obj)/,$(multi-used-m))
+multi-objs-y := $(addprefix $(obj)/,$(multi-objs-y))
+multi-objs-m := $(addprefix $(obj)/,$(multi-objs-m))
+subdir-ym := $(addprefix $(obj)/,$(subdir-ym))
+obj-dirs := $(addprefix $(obj)/,$(obj-dirs))
+
+# These flags are needed for modversions and compiling, so we define them here
+# already
+# $(modname_flags) #defines KBUILD_MODNAME as the name of the module it will
+# end up in (or would, if it gets compiled in)
+# Note: It's possible that one object gets potentially linked into more
+# than one module. In that case KBUILD_MODNAME will be set to foo_bar,
+# where foo and bar are the name of the modules.
+name-fix = $(subst $(comma),_,$(subst -,_,$1))
+basename_flags = -D"KBUILD_BASENAME=KBUILD_STR($(call name-fix,$(*F)))"
+modname_flags = $(if $(filter 1,$(words $(modname))),\
+ -D"KBUILD_MODNAME=KBUILD_STR($(call name-fix,$(modname)))")
+
+_c_flags = $(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$(*F).o)
+_a_flags = $(AFLAGS) $(EXTRA_AFLAGS) $(AFLAGS_$(*F).o)
+_cpp_flags = $(CPPFLAGS) $(EXTRA_CPPFLAGS) $(CPPFLAGS_$(@F))
+
+# If building the kernel in a separate objtree expand all occurrences
+# of -Idir to -I$(srctree)/dir except for absolute paths (starting with '/').
+
+ifeq ($(KBUILD_SRC),)
+__c_flags = $(_c_flags)
+__a_flags = $(_a_flags)
+__cpp_flags = $(_cpp_flags)
+else
+
+# -I$(obj) locates generated .h files
+# $(call addtree,-I$(obj)) locates .h files in srctree, from generated .c files
+# and locates generated .h files
+# FIXME: Replace both with specific CFLAGS* statements in the makefiles
+__c_flags = $(call addtree,-I$(obj)) $(call flags,_c_flags)
+__a_flags = $(call flags,_a_flags)
+__cpp_flags = $(call flags,_cpp_flags)
+endif
+
+c_flags = -Wp,-MD,$(depfile) $(NOSTDINC_FLAGS) $(CPPFLAGS) \
+ $(__c_flags) $(modkern_cflags) \
+ -D"KBUILD_STR(s)=\#s" $(basename_flags) $(modname_flags)
+
+a_flags = -Wp,-MD,$(depfile) $(NOSTDINC_FLAGS) $(CPPFLAGS) \
+ $(__a_flags) $(modkern_aflags)
+
+cpp_flags = -Wp,-MD,$(depfile) $(NOSTDINC_FLAGS) $(__cpp_flags)
+
+ld_flags = $(LDFLAGS) $(EXTRA_LDFLAGS)
+
+# Finds the multi-part object the current object will be linked into
+modname-multi = $(sort $(foreach m,$(multi-used),\
+ $(if $(filter $(subst $(obj)/,,$*.o), $($(m:.o=-objs)) $($(m:.o=-y))),$(m:.o=))))
+
+# Shipped files
+# ===========================================================================
+
+quiet_cmd_shipped = SHIPPED $@
+cmd_shipped = cat $< > $@
+
+$(obj)/%:: $(src)/%_shipped
+ $(call cmd,shipped)
+
+# Commands useful for building a boot image
+# ===========================================================================
+#
+# Use as following:
+#
+# target: source(s) FORCE
+# $(if_changed,ld/objcopy/gzip)
+#
+# and add target to EXTRA_TARGETS so that we know we have to
+# read in the saved command line
+
+# Linking
+# ---------------------------------------------------------------------------
+
+quiet_cmd_ld = LD $@
+cmd_ld = $(LD) $(LDFLAGS) $(EXTRA_LDFLAGS) $(LDFLAGS_$(@F)) \
+ $(filter-out FORCE,$^) -o $@
+
+# Objcopy
+# ---------------------------------------------------------------------------
+
+quiet_cmd_objcopy = OBJCOPY $@
+cmd_objcopy = $(OBJCOPY) $(OBJCOPYFLAGS) $(OBJCOPYFLAGS_$(@F)) $< $@
+
+# Gzip
+# ---------------------------------------------------------------------------
+
+quiet_cmd_gzip = GZIP $@
+cmd_gzip = gzip -f -9 < $< > $@
+
+
diff --git a/i/pc104/initrd/conf/busybox/scripts/basic/Makefile b/i/pc104/initrd/conf/busybox/scripts/basic/Makefile
new file mode 100644
index 0000000..119f079
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/scripts/basic/Makefile
@@ -0,0 +1,18 @@
+###
+# Makefile.basic list the most basic programs used during the build process.
+# The programs listed herein is what is needed to do the basic stuff,
+# such as splitting .config and fix dependency file.
+# This initial step is needed to avoid files to be recompiled
+# when busybox configuration changes (which is what happens when
+# .config is included by main Makefile.
+# ---------------------------------------------------------------------------
+# fixdep: Used to generate dependency information during build process
+# split-include: Divide all config symbols up in a number of files in
+# include/config/...
+# docproc: Used in Documentation/docbook
+
+hostprogs-y := fixdep split-include docproc
+always := $(hostprogs-y)
+
+# fixdep is needed to compile other host programs
+$(addprefix $(obj)/,$(filter-out fixdep,$(always))): $(obj)/fixdep
diff --git a/i/pc104/initrd/conf/busybox/scripts/basic/docproc.c b/i/pc104/initrd/conf/busybox/scripts/basic/docproc.c
new file mode 100644
index 0000000..cb02baa
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/scripts/basic/docproc.c
@@ -0,0 +1,398 @@
+/*
+ * docproc is a simple preprocessor for the template files
+ * used as placeholders for the kernel internal documentation.
+ * docproc is used for documentation-frontend and
+ * dependency-generator.
+ * The two usages have in common that they require
+ * some knowledge of the .tmpl syntax, therefore they
+ * are kept together.
+ *
+ * documentation-frontend
+ * Scans the template file and call kernel-doc for
+ * all occurrences of ![EIF]file
+ * Beforehand each referenced file are scanned for
+ * any exported sympols "EXPORT_SYMBOL()" statements.
+ * This is used to create proper -function and
+ * -nofunction arguments in calls to kernel-doc.
+ * Usage: docproc doc file.tmpl
+ *
+ * dependency-generator:
+ * Scans the template file and list all files
+ * referenced in a format recognized by make.
+ * Usage: docproc depend file.tmpl
+ * Writes dependency information to stdout
+ * in the following format:
+ * file.tmpl src.c src2.c
+ * The filenames are obtained from the following constructs:
+ * !Efilename
+ * !Ifilename
+ * !Dfilename
+ * !Ffilename
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+/* exitstatus is used to keep track of any failing calls to kernel-doc,
+ * but execution continues. */
+int exitstatus = 0;
+
+typedef void DFL(char *);
+DFL *defaultline;
+
+typedef void FILEONLY(char * file);
+FILEONLY *internalfunctions;
+FILEONLY *externalfunctions;
+FILEONLY *symbolsonly;
+
+typedef void FILELINE(char * file, char * line);
+FILELINE * singlefunctions;
+FILELINE * entity_system;
+
+#define MAXLINESZ 2048
+#define MAXFILES 250
+#define KERNELDOCPATH "scripts/"
+#define KERNELDOC "kernel-doc"
+#define DOCBOOK "-docbook"
+#define FUNCTION "-function"
+#define NOFUNCTION "-nofunction"
+
+void usage (void)
+{
+ fprintf(stderr, "Usage: docproc {doc|depend} file\n");
+ fprintf(stderr, "Input is read from file.tmpl. Output is sent to stdout\n");
+ fprintf(stderr, "doc: frontend when generating kernel documentation\n");
+ fprintf(stderr, "depend: generate list of files referenced within file\n");
+}
+
+/*
+ * Execute kernel-doc with parameters givin in svec
+ */
+void exec_kernel_doc(char **svec)
+{
+ pid_t pid;
+ int ret;
+ char real_filename[PATH_MAX + 1];
+ /* Make sure output generated so far are flushed */
+ fflush(stdout);
+ switch(pid=fork()) {
+ case -1:
+ perror("fork");
+ exit(1);
+ case 0:
+ memset(real_filename, 0, sizeof(real_filename));
+ strncat(real_filename, getenv("SRCTREE"), PATH_MAX);
+ strncat(real_filename, KERNELDOCPATH KERNELDOC,
+ PATH_MAX - strlen(real_filename));
+ execvp(real_filename, svec);
+ fprintf(stderr, "exec ");
+ perror(real_filename);
+ exit(1);
+ default:
+ waitpid(pid, &ret ,0);
+ }
+ if (WIFEXITED(ret))
+ exitstatus |= WEXITSTATUS(ret);
+ else
+ exitstatus = 0xff;
+}
+
+/* Types used to create list of all exported symbols in a number of files */
+struct symbols
+{
+ char *name;
+};
+
+struct symfile
+{
+ char *filename;
+ struct symbols *symbollist;
+ int symbolcnt;
+};
+
+struct symfile symfilelist[MAXFILES];
+int symfilecnt = 0;
+
+void add_new_symbol(struct symfile *sym, char * symname)
+{
+ sym->symbollist =
+ realloc(sym->symbollist, (sym->symbolcnt + 1) * sizeof(char *));
+ sym->symbollist[sym->symbolcnt++].name = strdup(symname);
+}
+
+/* Add a filename to the list */
+struct symfile * add_new_file(char * filename)
+{
+ symfilelist[symfilecnt++].filename = strdup(filename);
+ return &symfilelist[symfilecnt - 1];
+}
+/* Check if file already are present in the list */
+struct symfile * filename_exist(char * filename)
+{
+ int i;
+ for (i=0; i < symfilecnt; i++)
+ if (strcmp(symfilelist[i].filename, filename) == 0)
+ return &symfilelist[i];
+ return NULL;
+}
+
+/*
+ * List all files referenced within the template file.
+ * Files are separated by tabs.
+ */
+void adddep(char * file) { printf("\t%s", file); }
+void adddep2(char * file, char * line) { line = line; adddep(file); }
+void noaction(char * line) { line = line; }
+void noaction2(char * file, char * line) { file = file; line = line; }
+
+/* Echo the line without further action */
+void printline(char * line) { printf("%s", line); }
+
+/*
+ * Find all symbols exported with EXPORT_SYMBOL and EXPORT_SYMBOL_GPL
+ * in filename.
+ * All symbols located are stored in symfilelist.
+ */
+void find_export_symbols(char * filename)
+{
+ FILE * fp;
+ struct symfile *sym;
+ char line[MAXLINESZ];
+ if (filename_exist(filename) == NULL) {
+ char real_filename[PATH_MAX + 1];
+ memset(real_filename, 0, sizeof(real_filename));
+ strncat(real_filename, getenv("SRCTREE"), PATH_MAX);
+ strncat(real_filename, filename,
+ PATH_MAX - strlen(real_filename));
+ sym = add_new_file(filename);
+ fp = fopen(real_filename, "r");
+ if (fp == NULL)
+ {
+ fprintf(stderr, "docproc: ");
+ perror(real_filename);
+ }
+ while(fgets(line, MAXLINESZ, fp)) {
+ char *p;
+ char *e;
+ if (((p = strstr(line, "EXPORT_SYMBOL_GPL")) != 0) ||
+ ((p = strstr(line, "EXPORT_SYMBOL")) != 0)) {
+ /* Skip EXPORT_SYMBOL{_GPL} */
+ while (isalnum(*p) || *p == '_')
+ p++;
+ /* Remove paranteses and additional ws */
+ while (isspace(*p))
+ p++;
+ if (*p != '(')
+ continue; /* Syntax error? */
+ else
+ p++;
+ while (isspace(*p))
+ p++;
+ e = p;
+ while (isalnum(*e) || *e == '_')
+ e++;
+ *e = '\0';
+ add_new_symbol(sym, p);
+ }
+ }
+ fclose(fp);
+ }
+}
+
+/*
+ * Document all external or internal functions in a file.
+ * Call kernel-doc with following parameters:
+ * kernel-doc -docbook -nofunction function_name1 filename
+ * function names are obtained from all the the src files
+ * by find_export_symbols.
+ * intfunc uses -nofunction
+ * extfunc uses -function
+ */
+void docfunctions(char * filename, char * type)
+{
+ int i,j;
+ int symcnt = 0;
+ int idx = 0;
+ char **vec;
+
+ for (i=0; i <= symfilecnt; i++)
+ symcnt += symfilelist[i].symbolcnt;
+ vec = malloc((2 + 2 * symcnt + 2) * sizeof(char*));
+ if (vec == NULL) {
+ perror("docproc: ");
+ exit(1);
+ }
+ vec[idx++] = KERNELDOC;
+ vec[idx++] = DOCBOOK;
+ for (i=0; i < symfilecnt; i++) {
+ struct symfile * sym = &symfilelist[i];
+ for (j=0; j < sym->symbolcnt; j++) {
+ vec[idx++] = type;
+ vec[idx++] = sym->symbollist[j].name;
+ }
+ }
+ vec[idx++] = filename;
+ vec[idx] = NULL;
+ printf("<!-- %s -->\n", filename);
+ exec_kernel_doc(vec);
+ fflush(stdout);
+ free(vec);
+}
+void intfunc(char * filename) { docfunctions(filename, NOFUNCTION); }
+void extfunc(char * filename) { docfunctions(filename, FUNCTION); }
+
+/*
+ * Document spåecific function(s) in a file.
+ * Call kernel-doc with the following parameters:
+ * kernel-doc -docbook -function function1 [-function function2]
+ */
+void singfunc(char * filename, char * line)
+{
+ char *vec[200]; /* Enough for specific functions */
+ int i, idx = 0;
+ int startofsym = 1;
+ vec[idx++] = KERNELDOC;
+ vec[idx++] = DOCBOOK;
+
+ /* Split line up in individual parameters preceeded by FUNCTION */
+ for (i=0; line[i]; i++) {
+ if (isspace(line[i])) {
+ line[i] = '\0';
+ startofsym = 1;
+ continue;
+ }
+ if (startofsym) {
+ startofsym = 0;
+ vec[idx++] = FUNCTION;
+ vec[idx++] = &line[i];
+ }
+ }
+ vec[idx++] = filename;
+ vec[idx] = NULL;
+ exec_kernel_doc(vec);
+}
+
+/*
+ * Parse file, calling action specific functions for:
+ * 1) Lines containing !E
+ * 2) Lines containing !I
+ * 3) Lines containing !D
+ * 4) Lines containing !F
+ * 5) Default lines - lines not matching the above
+ */
+void parse_file(FILE *infile)
+{
+ char line[MAXLINESZ];
+ char * s;
+ while(fgets(line, MAXLINESZ, infile)) {
+ if (line[0] == '!') {
+ s = line + 2;
+ switch (line[1]) {
+ case 'E':
+ while (*s && !isspace(*s)) s++;
+ *s = '\0';
+ externalfunctions(line+2);
+ break;
+ case 'I':
+ while (*s && !isspace(*s)) s++;
+ *s = '\0';
+ internalfunctions(line+2);
+ break;
+ case 'D':
+ while (*s && !isspace(*s)) s++;
+ *s = '\0';
+ symbolsonly(line+2);
+ break;
+ case 'F':
+ /* filename */
+ while (*s && !isspace(*s)) s++;
+ *s++ = '\0';
+ /* function names */
+ while (isspace(*s))
+ s++;
+ singlefunctions(line +2, s);
+ break;
+ default:
+ defaultline(line);
+ }
+ }
+ else {
+ defaultline(line);
+ }
+ }
+ fflush(stdout);
+}
+
+
+int main(int argc, char *argv[])
+{
+ FILE * infile;
+ if (argc != 3) {
+ usage();
+ exit(1);
+ }
+ /* Open file, exit on error */
+ infile = fopen(argv[2], "r");
+ if (infile == NULL) {
+ fprintf(stderr, "docproc: ");
+ perror(argv[2]);
+ exit(2);
+ }
+
+ if (strcmp("doc", argv[1]) == 0)
+ {
+ /* Need to do this in two passes.
+ * First pass is used to collect all symbols exported
+ * in the various files.
+ * Second pass generate the documentation.
+ * This is required because function are declared
+ * and exported in different files :-((
+ */
+ /* Collect symbols */
+ defaultline = noaction;
+ internalfunctions = find_export_symbols;
+ externalfunctions = find_export_symbols;
+ symbolsonly = find_export_symbols;
+ singlefunctions = noaction2;
+ parse_file(infile);
+
+ /* Rewind to start from beginning of file again */
+ fseek(infile, 0, SEEK_SET);
+ defaultline = printline;
+ internalfunctions = intfunc;
+ externalfunctions = extfunc;
+ symbolsonly = printline;
+ singlefunctions = singfunc;
+
+ parse_file(infile);
+ }
+ else if (strcmp("depend", argv[1]) == 0)
+ {
+ /* Create first part of dependency chain
+ * file.tmpl */
+ printf("%s\t", argv[2]);
+ defaultline = noaction;
+ internalfunctions = adddep;
+ externalfunctions = adddep;
+ symbolsonly = adddep;
+ singlefunctions = adddep2;
+ parse_file(infile);
+ printf("\n");
+ }
+ else
+ {
+ fprintf(stderr, "Unknown option: %s\n", argv[1]);
+ exit(1);
+ }
+ fclose(infile);
+ fflush(stdout);
+ return exitstatus;
+}
+
diff --git a/i/pc104/initrd/conf/busybox/scripts/basic/fixdep.c b/i/pc104/initrd/conf/busybox/scripts/basic/fixdep.c
new file mode 100644
index 0000000..a96239d
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/scripts/basic/fixdep.c
@@ -0,0 +1,404 @@
+/*
+ * "Optimize" a list of dependencies as spit out by gcc -MD
+ * for the kernel build
+ * ===========================================================================
+ *
+ * Author Kai Germaschewski
+ * Copyright 2002 by Kai Germaschewski <kai.germaschewski@gmx.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ *
+ * Introduction:
+ *
+ * gcc produces a very nice and correct list of dependencies which
+ * tells make when to remake a file.
+ *
+ * To use this list as-is however has the drawback that virtually
+ * every file in the kernel includes <linux/config.h> which then again
+ * includes <linux/autoconf.h>
+ *
+ * If the user re-runs make *config, linux/autoconf.h will be
+ * regenerated. make notices that and will rebuild every file which
+ * includes autoconf.h, i.e. basically all files. This is extremely
+ * annoying if the user just changed CONFIG_HIS_DRIVER from n to m.
+ *
+ * So we play the same trick that "mkdep" played before. We replace
+ * the dependency on linux/autoconf.h by a dependency on every config
+ * option which is mentioned in any of the listed prequisites.
+ *
+ * To be exact, split-include populates a tree in include/config/,
+ * e.g. include/config/his/driver.h, which contains the #define/#undef
+ * for the CONFIG_HIS_DRIVER option.
+ *
+ * So if the user changes his CONFIG_HIS_DRIVER option, only the objects
+ * which depend on "include/linux/config/his/driver.h" will be rebuilt,
+ * so most likely only his driver ;-)
+ *
+ * The idea above dates, by the way, back to Michael E Chastain, AFAIK.
+ *
+ * So to get dependencies right, there are two issues:
+ * o if any of the files the compiler read changed, we need to rebuild
+ * o if the command line given to the compile the file changed, we
+ * better rebuild as well.
+ *
+ * The former is handled by using the -MD output, the later by saving
+ * the command line used to compile the old object and comparing it
+ * to the one we would now use.
+ *
+ * Again, also this idea is pretty old and has been discussed on
+ * kbuild-devel a long time ago. I don't have a sensibly working
+ * internet connection right now, so I rather don't mention names
+ * without double checking.
+ *
+ * This code here has been based partially based on mkdep.c, which
+ * says the following about its history:
+ *
+ * Copyright abandoned, Michael Chastain, <mailto:mec@shout.net>.
+ * This is a C version of syncdep.pl by Werner Almesberger.
+ *
+ *
+ * It is invoked as
+ *
+ * fixdep <depfile> <target> <cmdline>
+ *
+ * and will read the dependency file <depfile>
+ *
+ * The transformed dependency snipped is written to stdout.
+ *
+ * It first generates a line
+ *
+ * cmd_<target> = <cmdline>
+ *
+ * and then basically copies the .<target>.d file to stdout, in the
+ * process filtering out the dependency on linux/autoconf.h and adding
+ * dependencies on include/config/my/option.h for every
+ * CONFIG_MY_OPTION encountered in any of the prequisites.
+ *
+ * It will also filter out all the dependencies on *.ver. We need
+ * to make sure that the generated version checksum are globally up
+ * to date before even starting the recursive build, so it's too late
+ * at this point anyway.
+ *
+ * The algorithm to grep for "CONFIG_..." is bit unusual, but should
+ * be fast ;-) We don't even try to really parse the header files, but
+ * merely grep, i.e. if CONFIG_FOO is mentioned in a comment, it will
+ * be picked up as well. It's not a problem with respect to
+ * correctness, since that can only give too many dependencies, thus
+ * we cannot miss a rebuild. Since people tend to not mention totally
+ * unrelated CONFIG_ options all over the place, it's not an
+ * efficiency problem either.
+ *
+ * (Note: it'd be easy to port over the complete mkdep state machine,
+ * but I don't think the added complexity is worth it)
+ */
+/*
+ * Note 2: if somebody writes HELLO_CONFIG_BOOM in a file, it will depend onto
+ * CONFIG_BOOM. This could seem a bug (not too hard to fix), but please do not
+ * fix it! Some UserModeLinux files (look at arch/um/) call CONFIG_BOOM as
+ * UML_CONFIG_BOOM, to avoid conflicts with /usr/include/linux/autoconf.h,
+ * through arch/um/include/uml-config.h; this fixdep "bug" makes sure that
+ * those files will have correct dependencies.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <limits.h>
+#include <ctype.h>
+#include <arpa/inet.h>
+
+/* bbox: not needed
+#define INT_CONF ntohl(0x434f4e46)
+#define INT_ONFI ntohl(0x4f4e4649)
+#define INT_NFIG ntohl(0x4e464947)
+#define INT_FIG_ ntohl(0x4649475f)
+*/
+
+char *target;
+char *depfile;
+char *cmdline;
+
+void usage(void)
+
+{
+ fprintf(stderr, "Usage: fixdep <depfile> <target> <cmdline>\n");
+ exit(1);
+}
+
+/*
+ * Print out the commandline prefixed with cmd_<target filename> :=
+ */
+void print_cmdline(void)
+{
+ printf("cmd_%s := %s\n\n", target, cmdline);
+}
+
+char * str_config = NULL;
+int size_config = 0;
+int len_config = 0;
+
+/*
+ * Grow the configuration string to a desired length.
+ * Usually the first growth is plenty.
+ */
+void grow_config(int len)
+{
+ while (len_config + len > size_config) {
+ if (size_config == 0)
+ size_config = 2048;
+ str_config = realloc(str_config, size_config *= 2);
+ if (str_config == NULL)
+ { perror("fixdep:malloc"); exit(1); }
+ }
+}
+
+
+
+/*
+ * Lookup a value in the configuration string.
+ */
+int is_defined_config(const char * name, int len)
+{
+ const char * pconfig;
+ const char * plast = str_config + len_config - len;
+ for ( pconfig = str_config + 1; pconfig < plast; pconfig++ ) {
+ if (pconfig[ -1] == '\n'
+ && pconfig[len] == '\n'
+ && !memcmp(pconfig, name, len))
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Add a new value to the configuration string.
+ */
+void define_config(const char * name, int len)
+{
+ grow_config(len + 1);
+
+ memcpy(str_config+len_config, name, len);
+ len_config += len;
+ str_config[len_config++] = '\n';
+}
+
+/*
+ * Clear the set of configuration strings.
+ */
+void clear_config(void)
+{
+ len_config = 0;
+ define_config("", 0);
+}
+
+/*
+ * Record the use of a CONFIG_* word.
+ */
+void use_config(char *m, int slen)
+{
+ char s[PATH_MAX];
+ char *p;
+
+ if (is_defined_config(m, slen))
+ return;
+
+ define_config(m, slen);
+
+ memcpy(s, m, slen); s[slen] = 0;
+
+ for (p = s; p < s + slen; p++) {
+ if (*p == '_')
+ *p = '/';
+ else
+ *p = tolower((int)*p);
+ }
+ printf(" $(wildcard include/config/%s.h) \\\n", s);
+}
+
+void parse_config_file(char *map, size_t len)
+{
+ /* modified for bbox */
+ char *end_4 = map + len - 4; /* 4 == length of "USE_" */
+ char *end_7 = map + len - 7;
+ char *p = map;
+ char *q;
+ int off;
+
+ for (; p < end_4; p++) {
+ if (p < end_7 && p[6] == '_') {
+ if (!memcmp(p, "CONFIG", 6)) goto conf7;
+ if (!memcmp(p, "ENABLE", 6)) goto conf7;
+ }
+ /* We have at least 5 chars: for() has
+ * "p < end-4", not "p <= end-4"
+ * therefore we don't need to check p <= end-5 here */
+ if (p[4] == '_')
+ if (!memcmp(p, "SKIP", 4)) goto conf5;
+ /* Ehhh, gcc is too stupid to just compare it as 32bit int */
+ if (p[0] == 'U')
+ if (!memcmp(p, "USE_", 4)) goto conf4;
+ continue;
+
+ conf4: off = 4;
+ conf5: off = 5;
+ conf7: off = 7;
+ p += off;
+ for (q = p; q < end_4+4; q++) {
+ if (!(isalnum(*q) || *q == '_'))
+ break;
+ }
+ use_config(p, q-p);
+ }
+}
+
+/* test is s ends in sub */
+int strrcmp(char *s, char *sub)
+{
+ int slen = strlen(s);
+ int sublen = strlen(sub);
+
+ if (sublen > slen)
+ return 1;
+
+ return memcmp(s + slen - sublen, sub, sublen);
+}
+
+void do_config_file(char *filename)
+{
+ struct stat st;
+ int fd;
+ void *map;
+
+ fd = open(filename, O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "fixdep: ");
+ perror(filename);
+ exit(2);
+ }
+ fstat(fd, &st);
+ if (st.st_size == 0) {
+ close(fd);
+ return;
+ }
+ map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if ((long) map == -1) {
+ perror("fixdep: mmap");
+ close(fd);
+ return;
+ }
+
+ parse_config_file(map, st.st_size);
+
+ munmap(map, st.st_size);
+
+ close(fd);
+}
+
+void parse_dep_file(void *map, size_t len)
+{
+ char *m = map;
+ char *end = m + len;
+ char *p;
+ char s[PATH_MAX];
+
+ p = memchr(m, ':', len);
+ if (!p) {
+ fprintf(stderr, "fixdep: parse error\n");
+ exit(1);
+ }
+ memcpy(s, m, p-m); s[p-m] = 0;
+ printf("deps_%s := \\\n", target);
+ m = p+1;
+
+ clear_config();
+
+ while (m < end) {
+ while (m < end && (*m == ' ' || *m == '\\' || *m == '\n'))
+ m++;
+ p = m;
+ while (p < end && *p != ' ') p++;
+ if (p == end) {
+ do p--; while (!isalnum(*p));
+ p++;
+ }
+ memcpy(s, m, p-m); s[p-m] = 0;
+ if (strrcmp(s, "include/autoconf.h") &&
+ strrcmp(s, "arch/um/include/uml-config.h") &&
+ strrcmp(s, ".ver")) {
+ printf(" %s \\\n", s);
+ do_config_file(s);
+ }
+ m = p + 1;
+ }
+ printf("\n%s: $(deps_%s)\n\n", target, target);
+ printf("$(deps_%s):\n", target);
+}
+
+void print_deps(void)
+{
+ struct stat st;
+ int fd;
+ void *map;
+
+ fd = open(depfile, O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "fixdep: ");
+ perror(depfile);
+ exit(2);
+ }
+ fstat(fd, &st);
+ if (st.st_size == 0) {
+ fprintf(stderr,"fixdep: %s is empty\n",depfile);
+ close(fd);
+ return;
+ }
+ map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if ((long) map == -1) {
+ perror("fixdep: mmap");
+ close(fd);
+ return;
+ }
+
+ parse_dep_file(map, st.st_size);
+
+ munmap(map, st.st_size);
+
+ close(fd);
+}
+
+void traps(void)
+{
+/* bbox: not needed
+ static char test[] __attribute__((aligned(sizeof(int)))) = "CONF";
+
+ if (*(int *)test != INT_CONF) {
+ fprintf(stderr, "fixdep: sizeof(int) != 4 or wrong endianess? %#x\n",
+ *(int *)test);
+ exit(2);
+ }
+*/
+}
+
+int main(int argc, char *argv[])
+{
+ traps();
+
+ if (argc != 4)
+ usage();
+
+ depfile = argv[1];
+ target = argv[2];
+ cmdline = argv[3];
+
+ print_cmdline();
+ print_deps();
+
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/scripts/basic/split-include.c b/i/pc104/initrd/conf/busybox/scripts/basic/split-include.c
new file mode 100644
index 0000000..459c452
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/scripts/basic/split-include.c
@@ -0,0 +1,226 @@
+/*
+ * split-include.c
+ *
+ * Copyright abandoned, Michael Chastain, <mailto:mec@shout.net>.
+ * This is a C version of syncdep.pl by Werner Almesberger.
+ *
+ * This program takes autoconf.h as input and outputs a directory full
+ * of one-line include files, merging onto the old values.
+ *
+ * Think of the configuration options as key-value pairs. Then there
+ * are five cases:
+ *
+ * key old value new value action
+ *
+ * KEY-1 VALUE-1 VALUE-1 leave file alone
+ * KEY-2 VALUE-2A VALUE-2B write VALUE-2B into file
+ * KEY-3 - VALUE-3 write VALUE-3 into file
+ * KEY-4 VALUE-4 - write an empty file
+ * KEY-5 (empty) - leave old empty file alone
+ */
+
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define ERROR_EXIT(strExit) \
+ { \
+ const int errnoSave = errno; \
+ fprintf(stderr, "%s: ", str_my_name); \
+ errno = errnoSave; \
+ perror((strExit)); \
+ exit(1); \
+ }
+
+
+
+int main(int argc, const char * argv [])
+{
+ const char * str_my_name;
+ const char * str_file_autoconf;
+ const char * str_dir_config;
+
+ FILE * fp_config;
+ FILE * fp_target;
+ FILE * fp_find;
+
+ int buffer_size;
+
+ char * line;
+ char * old_line;
+ char * list_target;
+ char * ptarget;
+
+ struct stat stat_buf;
+
+ /* Check arg count. */
+ if (argc != 3)
+ {
+ fprintf(stderr, "%s: wrong number of arguments.\n", argv[0]);
+ exit(1);
+ }
+
+ str_my_name = argv[0];
+ str_file_autoconf = argv[1];
+ str_dir_config = argv[2];
+
+ /* Find a buffer size. */
+ if (stat(str_file_autoconf, &stat_buf) != 0)
+ ERROR_EXIT(str_file_autoconf);
+ buffer_size = 2 * stat_buf.st_size + 4096;
+
+ /* Allocate buffers. */
+ if ( (line = malloc(buffer_size)) == NULL
+ || (old_line = malloc(buffer_size)) == NULL
+ || (list_target = malloc(buffer_size)) == NULL )
+ ERROR_EXIT(str_file_autoconf);
+
+ /* Open autoconfig file. */
+ if ((fp_config = fopen(str_file_autoconf, "r")) == NULL)
+ ERROR_EXIT(str_file_autoconf);
+
+ /* Make output directory if needed. */
+ if (stat(str_dir_config, &stat_buf) != 0)
+ {
+ if (mkdir(str_dir_config, 0755) != 0)
+ ERROR_EXIT(str_dir_config);
+ }
+
+ /* Change to output directory. */
+ if (chdir(str_dir_config) != 0)
+ ERROR_EXIT(str_dir_config);
+
+ /* Put initial separator into target list. */
+ ptarget = list_target;
+ *ptarget++ = '\n';
+
+ /* Read config lines. */
+ while (fgets(line, buffer_size, fp_config))
+ {
+ const char * str_config;
+ int is_same;
+ int itarget;
+
+ if (line[0] != '#')
+ continue;
+ if ((str_config = strstr(line, "CONFIG_")) == NULL)
+ continue;
+
+ /* Make the output file name. */
+ str_config += sizeof("CONFIG_") - 1;
+ for (itarget = 0; !isspace(str_config[itarget]); itarget++)
+ {
+ int c = (unsigned char) str_config[itarget];
+ if (isupper(c)) c = tolower(c);
+ if (c == '_') c = '/';
+ ptarget[itarget] = c;
+ }
+ ptarget[itarget++] = '.';
+ ptarget[itarget++] = 'h';
+ ptarget[itarget++] = '\0';
+
+ /* Check for existing file. */
+ is_same = 0;
+ if ((fp_target = fopen(ptarget, "r")) != NULL)
+ {
+ fgets(old_line, buffer_size, fp_target);
+ if (fclose(fp_target) != 0)
+ ERROR_EXIT(ptarget);
+ if (!strcmp(line, old_line))
+ is_same = 1;
+ }
+
+ if (!is_same)
+ {
+ /* Auto-create directories. */
+ int islash;
+ for (islash = 0; islash < itarget; islash++)
+ {
+ if (ptarget[islash] == '/')
+ {
+ ptarget[islash] = '\0';
+ if (stat(ptarget, &stat_buf) != 0
+ && mkdir(ptarget, 0755) != 0)
+ ERROR_EXIT( ptarget );
+ ptarget[islash] = '/';
+ }
+ }
+
+ /* Write the file. */
+ if ((fp_target = fopen(ptarget, "w" )) == NULL)
+ ERROR_EXIT(ptarget);
+ fputs(line, fp_target);
+ if (ferror(fp_target) || fclose(fp_target) != 0)
+ ERROR_EXIT(ptarget);
+ }
+
+ /* Update target list */
+ ptarget += itarget;
+ *(ptarget-1) = '\n';
+ }
+
+ /*
+ * Close autoconfig file.
+ * Terminate the target list.
+ */
+ if (fclose(fp_config) != 0)
+ ERROR_EXIT(str_file_autoconf);
+ *ptarget = '\0';
+
+ /*
+ * Fix up existing files which have no new value.
+ * This is Case 4 and Case 5.
+ *
+ * I re-read the tree and filter it against list_target.
+ * This is crude. But it avoids data copies. Also, list_target
+ * is compact and contiguous, so it easily fits into cache.
+ *
+ * Notice that list_target contains strings separated by \n,
+ * with a \n before the first string and after the last.
+ * fgets gives the incoming names a terminating \n.
+ * So by having an initial \n, strstr will find exact matches.
+ */
+
+ fp_find = popen("find * -type f -name \"*.h\" -print", "r");
+ if (fp_find == 0)
+ ERROR_EXIT( "find" );
+
+ line[0] = '\n';
+ while (fgets(line+1, buffer_size, fp_find))
+ {
+ if (strstr(list_target, line) == NULL)
+ {
+ /*
+ * This is an old file with no CONFIG_* flag in autoconf.h.
+ */
+
+ /* First strip the \n. */
+ line[strlen(line)-1] = '\0';
+
+ /* Grab size. */
+ if (stat(line+1, &stat_buf) != 0)
+ ERROR_EXIT(line);
+
+ /* If file is not empty, make it empty and give it a fresh date. */
+ if (stat_buf.st_size != 0)
+ {
+ if ((fp_target = fopen(line+1, "w")) == NULL)
+ ERROR_EXIT(line);
+ if (fclose(fp_target) != 0)
+ ERROR_EXIT(line);
+ }
+ }
+ }
+
+ if (pclose(fp_find) != 0)
+ ERROR_EXIT("find");
+
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/scripts/bloat-o-meter b/i/pc104/initrd/conf/busybox/scripts/bloat-o-meter
new file mode 100755
index 0000000..67b3d29
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/scripts/bloat-o-meter
@@ -0,0 +1,65 @@
+#!/usr/bin/python
+#
+# Copyright 2004 Matt Mackall <mpm@selenic.com>
+#
+# inspired by perl Bloat-O-Meter (c) 1997 by Andi Kleen
+#
+# This software may be used and distributed according to the terms
+# of the GNU General Public License, incorporated herein by reference.
+
+import sys, os, re
+
+if len(sys.argv) != 3:
+ sys.stderr.write("usage: %s file1 file2\n" % sys.argv[0])
+ sys.exit(-1)
+
+def getsizes(file):
+ sym = {}
+ for l in os.popen("nm --size-sort " + file).readlines():
+ size, type, name = l[:-1].split()
+ if type in "tTdDbBrR":
+ if "." in name: name = "static." + name.split(".")[0]
+ sym[name] = sym.get(name, 0) + int(size, 16)
+ for l in os.popen("readelf -S " + file).readlines():
+ x = l.split()
+ if len(x)<6 or x[1] != ".rodata": continue
+ sym[".rodata"] = int(x[5], 16)
+ return sym
+
+old = getsizes(sys.argv[1])
+new = getsizes(sys.argv[2])
+grow, shrink, add, remove, up, down = 0, 0, 0, 0, 0, 0
+delta, common = [], {}
+
+for a in old:
+ if a in new:
+ common[a] = 1
+
+for name in old:
+ if name not in common:
+ remove += 1
+ down += old[name]
+ delta.append((-old[name], name))
+
+for name in new:
+ if name not in common:
+ add += 1
+ up += new[name]
+ delta.append((new[name], name))
+
+for name in common:
+ d = new.get(name, 0) - old.get(name, 0)
+ if d>0: grow, up = grow+1, up+d
+ if d<0: shrink, down = shrink+1, down-d
+ delta.append((d, name))
+
+delta.sort()
+delta.reverse()
+
+print "%-48s %7s %7s %+7s" % ("function", "old", "new", "delta")
+for d, n in delta:
+ if d: print "%-48s %7s %7s %+7d" % (n, old.get(n,"-"), new.get(n,"-"), d)
+print "-"*78
+total="(add/remove: %s/%s grow/shrink: %s/%s up/down: %s/%s)%%sTotal: %s bytes"\
+ % (add, remove, grow, shrink, up, -down, up-down)
+print total % (" "*(80-len(total)))
diff --git a/i/pc104/initrd/conf/busybox/scripts/checkhelp.awk b/i/pc104/initrd/conf/busybox/scripts/checkhelp.awk
new file mode 100755
index 0000000..4bb4996
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/scripts/checkhelp.awk
@@ -0,0 +1,40 @@
+#!/usr/bin/awk -f
+# AWK script to check for missing help entries for config options
+#
+# Copyright (C) 2006 Bernhard Fischer
+#
+# This file is distributed under the terms and conditions of the
+# MIT/X public licenses. See http://opensource.org/licenses/mit-license.html
+# and notice http://www.gnu.org/licenses/license-list.html#X11License
+
+
+/^choice/ { is_choice = 1; }
+/^endchoice/ { is_choice = 0; }
+/^config/ {
+ pos++;
+ conf[pos] = $2;
+ file[pos] = FILENAME;
+ if (is_choice) {
+ help[pos] = 1; # do not warn about 'choice' config entries.
+ } else {
+ help[pos] = 0;
+ }
+}
+/^[ \t]*help[ \t]*$/ {
+ help[pos] = 1;
+}
+/^[ \t]*bool[ \t]*$/ {
+ help[pos] = 1; # ignore options which are not selectable
+}
+BEGIN {
+ pos = -1;
+ is_choice = 0;
+}
+END {
+ for (i = 0; i <= pos; i++) {
+# printf("%s: help for #%i '%s' == %i\n", file[i], i, conf[i], help[i]);
+ if (help[i] == 0) {
+ printf("%s: No helptext for '%s'\n", file[i], conf[i]);
+ }
+ }
+}
diff --git a/i/pc104/initrd/conf/busybox/scripts/defconfig b/i/pc104/initrd/conf/busybox/scripts/defconfig
new file mode 100644
index 0000000..c6c2002
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/scripts/defconfig
@@ -0,0 +1,691 @@
+#
+#
+CONFIG_HAVE_DOT_CONFIG=y
+
+#
+# Busybox Settings
+#
+
+#
+# General Configuration
+#
+# CONFIG_NITPICK is not set
+# CONFIG_DESKTOP is not set
+# CONFIG_FEATURE_BUFFERS_USE_MALLOC is not set
+# CONFIG_FEATURE_BUFFERS_GO_ON_STACK is not set
+# CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set
+CONFIG_SHOW_USAGE=y
+CONFIG_FEATURE_VERBOSE_USAGE=y
+CONFIG_FEATURE_COMPRESS_USAGE=y
+CONFIG_FEATURE_INSTALLER=y
+CONFIG_LOCALE_SUPPORT=y
+CONFIG_GETOPT_LONG=y
+CONFIG_FEATURE_DEVPTS=y
+# CONFIG_FEATURE_CLEAN_UP is not set
+CONFIG_FEATURE_SUID=y
+CONFIG_FEATURE_SYSLOG=y
+CONFIG_FEATURE_SUID_CONFIG=y
+CONFIG_FEATURE_SUID_CONFIG_QUIET=y
+# CONFIG_FEATURE_HAVE_RPC is not set
+# CONFIG_SELINUX is not set
+# CONFIG_FEATURE_EXEC_PREFER_APPLETS is not set
+CONFIG_BUSYBOX_EXEC_PATH="/proc/self/exe"
+
+#
+# Build Options
+#
+# CONFIG_STATIC is not set
+# CONFIG_BUILD_LIBBUSYBOX is not set
+# CONFIG_FEATURE_FULL_LIBBUSYBOX is not set
+# CONFIG_FEATURE_SHARED_BUSYBOX is not set
+CONFIG_LFS=y
+# CONFIG_BUILD_AT_ONCE is not set
+
+#
+# Debugging Options
+#
+# CONFIG_DEBUG is not set
+# CONFIG_WERROR is not set
+# CONFIG_NO_DEBUG_LIB is not set
+# CONFIG_DMALLOC is not set
+# CONFIG_EFENCE is not set
+CONFIG_INCLUDE_SUSv2=y
+
+#
+# Installation Options
+#
+# CONFIG_INSTALL_NO_USR is not set
+CONFIG_INSTALL_APPLET_SYMLINKS=y
+# CONFIG_INSTALL_APPLET_HARDLINKS is not set
+# CONFIG_INSTALL_APPLET_DONT is not set
+CONFIG_PREFIX="./_install"
+
+#
+# Busybox Library Tuning
+#
+CONFIG_PASSWORD_MINLEN=6
+CONFIG_MD5_SIZE_VS_SPEED=2
+CONFIG_FEATURE_EDITING=y
+# CONFIG_FEATURE_EDITING_FANCY_KEYS is not set
+# CONFIG_FEATURE_EDITING_VI is not set
+CONFIG_FEATURE_EDITING_HISTORY=15
+# CONFIG_FEATURE_EDITING_SAVEHISTORY is not set
+# CONFIG_FEATURE_TAB_COMPLETION is not set
+# CONFIG_FEATURE_USERNAME_COMPLETION is not set
+# CONFIG_FEATURE_EDITING_FANCY_PROMPT is not set
+
+#
+# Applets
+#
+
+#
+# Archival Utilities
+#
+CONFIG_AR=y
+CONFIG_FEATURE_AR_LONG_FILENAMES=y
+CONFIG_BUNZIP2=y
+CONFIG_CPIO=y
+# CONFIG_DPKG is not set
+# CONFIG_DPKG_DEB is not set
+# CONFIG_FEATURE_DPKG_DEB_EXTRACT_ONLY is not set
+CONFIG_GUNZIP=y
+CONFIG_FEATURE_GUNZIP_UNCOMPRESS=y
+CONFIG_GZIP=y
+CONFIG_RPM2CPIO=y
+CONFIG_RPM=y
+CONFIG_TAR=y
+CONFIG_FEATURE_TAR_CREATE=y
+CONFIG_FEATURE_TAR_BZIP2=y
+CONFIG_FEATURE_TAR_LZMA=y
+CONFIG_FEATURE_TAR_FROM=y
+CONFIG_FEATURE_TAR_GZIP=y
+CONFIG_FEATURE_TAR_COMPRESS=y
+CONFIG_FEATURE_TAR_OLDGNU_COMPATIBILITY=y
+CONFIG_FEATURE_TAR_GNU_EXTENSIONS=y
+CONFIG_FEATURE_TAR_LONG_OPTIONS=y
+CONFIG_UNCOMPRESS=y
+CONFIG_UNLZMA=y
+CONFIG_FEATURE_LZMA_FAST=y
+CONFIG_UNZIP=y
+
+#
+# Common options for cpio and tar
+#
+CONFIG_FEATURE_UNARCHIVE_TAPE=y
+# CONFIG_FEATURE_DEB_TAR_GZ is not set
+# CONFIG_FEATURE_DEB_TAR_BZ2 is not set
+# CONFIG_FEATURE_DEB_TAR_LZMA is not set
+
+#
+# Coreutils
+#
+CONFIG_BASENAME=y
+CONFIG_CAL=y
+CONFIG_CAT=y
+CONFIG_CATV=y
+CONFIG_CHGRP=y
+CONFIG_CHMOD=y
+CONFIG_CHOWN=y
+CONFIG_CHROOT=y
+CONFIG_CKSUM=y
+CONFIG_CMP=y
+CONFIG_COMM=y
+CONFIG_CP=y
+CONFIG_CUT=y
+CONFIG_DATE=y
+CONFIG_FEATURE_DATE_ISOFMT=y
+CONFIG_DD=y
+CONFIG_FEATURE_DD_SIGNAL_HANDLING=y
+CONFIG_FEATURE_DD_IBS_OBS=y
+CONFIG_DF=y
+CONFIG_DIFF=y
+CONFIG_FEATURE_DIFF_BINARY=y
+CONFIG_FEATURE_DIFF_DIR=y
+CONFIG_FEATURE_DIFF_MINIMAL=y
+CONFIG_DIRNAME=y
+CONFIG_DOS2UNIX=y
+CONFIG_UNIX2DOS=y
+CONFIG_DU=y
+CONFIG_FEATURE_DU_DEFAULT_BLOCKSIZE_1K=y
+CONFIG_ECHO=y
+CONFIG_FEATURE_FANCY_ECHO=y
+CONFIG_ENV=y
+CONFIG_FEATURE_ENV_LONG_OPTIONS=y
+CONFIG_EXPR=y
+CONFIG_EXPR_MATH_SUPPORT_64=y
+CONFIG_FALSE=y
+CONFIG_FOLD=y
+CONFIG_HEAD=y
+CONFIG_FEATURE_FANCY_HEAD=y
+CONFIG_HOSTID=y
+CONFIG_ID=y
+CONFIG_INSTALL=y
+CONFIG_FEATURE_INSTALL_LONG_OPTIONS=y
+CONFIG_LENGTH=y
+CONFIG_LN=y
+CONFIG_LOGNAME=y
+CONFIG_LS=y
+CONFIG_FEATURE_LS_FILETYPES=y
+CONFIG_FEATURE_LS_FOLLOWLINKS=y
+CONFIG_FEATURE_LS_RECURSIVE=y
+CONFIG_FEATURE_LS_SORTFILES=y
+CONFIG_FEATURE_LS_TIMESTAMPS=y
+CONFIG_FEATURE_LS_USERNAME=y
+CONFIG_FEATURE_LS_COLOR=y
+CONFIG_FEATURE_LS_COLOR_IS_DEFAULT=y
+CONFIG_MD5SUM=y
+CONFIG_MKDIR=y
+CONFIG_FEATURE_MKDIR_LONG_OPTIONS=y
+CONFIG_MKFIFO=y
+CONFIG_MKNOD=y
+CONFIG_MV=y
+CONFIG_FEATURE_MV_LONG_OPTIONS=y
+CONFIG_NICE=y
+CONFIG_NOHUP=y
+CONFIG_OD=y
+CONFIG_PRINTENV=y
+CONFIG_PRINTF=y
+CONFIG_PWD=y
+CONFIG_REALPATH=y
+CONFIG_RM=y
+CONFIG_RMDIR=y
+CONFIG_SEQ=y
+CONFIG_SHA1SUM=y
+CONFIG_SLEEP=y
+CONFIG_FEATURE_FANCY_SLEEP=y
+CONFIG_SORT=y
+CONFIG_FEATURE_SORT_BIG=y
+CONFIG_STAT=y
+CONFIG_FEATURE_STAT_FORMAT=y
+CONFIG_STTY=y
+CONFIG_SUM=y
+CONFIG_SYNC=y
+CONFIG_TAIL=y
+CONFIG_FEATURE_FANCY_TAIL=y
+CONFIG_TEE=y
+CONFIG_FEATURE_TEE_USE_BLOCK_IO=y
+CONFIG_TEST=y
+CONFIG_FEATURE_TEST_64=y
+CONFIG_TOUCH=y
+CONFIG_TR=y
+CONFIG_FEATURE_TR_CLASSES=y
+CONFIG_FEATURE_TR_EQUIV=y
+CONFIG_TRUE=y
+CONFIG_TTY=y
+CONFIG_UNAME=y
+CONFIG_UNIQ=y
+CONFIG_USLEEP=y
+CONFIG_UUDECODE=y
+CONFIG_UUENCODE=y
+CONFIG_WATCH=y
+CONFIG_WC=y
+CONFIG_FEATURE_WC_LARGE=y
+CONFIG_WHO=y
+CONFIG_WHOAMI=y
+CONFIG_YES=y
+
+#
+# Common options for cp and mv
+#
+CONFIG_FEATURE_PRESERVE_HARDLINKS=y
+
+#
+# Common options for ls, more and telnet
+#
+CONFIG_FEATURE_AUTOWIDTH=y
+
+#
+# Common options for df, du, ls
+#
+CONFIG_FEATURE_HUMAN_READABLE=y
+
+#
+# Common options for md5sum, sha1sum
+#
+CONFIG_FEATURE_MD5_SHA1_SUM_CHECK=y
+
+#
+# Console Utilities
+#
+CONFIG_CHVT=y
+CONFIG_CLEAR=y
+CONFIG_DEALLOCVT=y
+CONFIG_DUMPKMAP=y
+CONFIG_LOADFONT=y
+CONFIG_LOADKMAP=y
+CONFIG_OPENVT=y
+CONFIG_RESET=y
+CONFIG_RESIZE=y
+CONFIG_FEATURE_RESIZE_PRINT=y
+CONFIG_SETCONSOLE=y
+# CONFIG_FEATURE_SETCONSOLE_LONG_OPTIONS is not set
+CONFIG_SETKEYCODES=y
+CONFIG_SETLOGCONS=y
+
+#
+# Debian Utilities
+#
+CONFIG_MKTEMP=y
+CONFIG_PIPE_PROGRESS=y
+CONFIG_READLINK=y
+CONFIG_FEATURE_READLINK_FOLLOW=y
+CONFIG_RUN_PARTS=y
+CONFIG_FEATURE_RUN_PARTS_LONG_OPTIONS=y
+CONFIG_START_STOP_DAEMON=y
+CONFIG_FEATURE_START_STOP_DAEMON_FANCY=y
+CONFIG_FEATURE_START_STOP_DAEMON_LONG_OPTIONS=y
+CONFIG_WHICH=y
+
+#
+# Editors
+#
+CONFIG_AWK=y
+CONFIG_FEATURE_AWK_MATH=y
+CONFIG_ED=y
+CONFIG_PATCH=y
+CONFIG_SED=y
+CONFIG_VI=y
+CONFIG_FEATURE_VI_COLON=y
+CONFIG_FEATURE_VI_YANKMARK=y
+CONFIG_FEATURE_VI_SEARCH=y
+CONFIG_FEATURE_VI_USE_SIGNALS=y
+CONFIG_FEATURE_VI_DOT_CMD=y
+CONFIG_FEATURE_VI_READONLY=y
+CONFIG_FEATURE_VI_SETOPTS=y
+CONFIG_FEATURE_VI_SET=y
+CONFIG_FEATURE_VI_WIN_RESIZE=y
+CONFIG_FEATURE_VI_OPTIMIZE_CURSOR=y
+CONFIG_FEATURE_ALLOW_EXEC=y
+
+#
+# Finding Utilities
+#
+CONFIG_FIND=y
+CONFIG_FEATURE_FIND_PRINT0=y
+CONFIG_FEATURE_FIND_MTIME=y
+CONFIG_FEATURE_FIND_MMIN=y
+CONFIG_FEATURE_FIND_PERM=y
+CONFIG_FEATURE_FIND_TYPE=y
+CONFIG_FEATURE_FIND_XDEV=y
+CONFIG_FEATURE_FIND_NEWER=y
+CONFIG_FEATURE_FIND_INUM=y
+CONFIG_FEATURE_FIND_EXEC=y
+CONFIG_FEATURE_FIND_USER=y
+CONFIG_FEATURE_FIND_NOT=y
+CONFIG_GREP=y
+CONFIG_FEATURE_GREP_EGREP_ALIAS=y
+CONFIG_FEATURE_GREP_FGREP_ALIAS=y
+CONFIG_FEATURE_GREP_CONTEXT=y
+CONFIG_XARGS=y
+CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION=y
+CONFIG_FEATURE_XARGS_SUPPORT_QUOTES=y
+CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT=y
+CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM=y
+
+#
+# Init Utilities
+#
+CONFIG_INIT=y
+# CONFIG_DEBUG_INIT is not set
+CONFIG_FEATURE_USE_INITTAB=y
+CONFIG_FEATURE_INIT_SCTTY=y
+# CONFIG_FEATURE_INIT_SYSLOG is not set
+CONFIG_FEATURE_EXTRA_QUIET=y
+CONFIG_FEATURE_INIT_COREDUMPS=y
+CONFIG_FEATURE_INITRD=y
+CONFIG_HALT=y
+CONFIG_MESG=y
+
+#
+# Login/Password Management Utilities
+#
+CONFIG_FEATURE_SHADOWPASSWDS=y
+CONFIG_USE_BB_SHADOW=y
+CONFIG_USE_BB_PWD_GRP=y
+CONFIG_ADDGROUP=y
+CONFIG_DELGROUP=y
+CONFIG_ADDUSER=y
+CONFIG_DELUSER=y
+CONFIG_GETTY=y
+CONFIG_FEATURE_UTMP=y
+CONFIG_FEATURE_WTMP=y
+CONFIG_LOGIN=y
+CONFIG_LOGIN_SCRIPTS=y
+CONFIG_FEATURE_SECURETTY=y
+CONFIG_PASSWD=y
+CONFIG_FEATURE_PASSWD_WEAK_CHECK=y
+CONFIG_SU=y
+CONFIG_FEATURE_SU_SYSLOG=y
+CONFIG_FEATURE_SU_CHECKS_SHELLS=y
+CONFIG_SULOGIN=y
+CONFIG_VLOCK=y
+
+#
+# Linux Ext2 FS Progs
+#
+CONFIG_CHATTR=y
+CONFIG_FSCK=y
+CONFIG_LSATTR=y
+
+#
+# Linux Module Utilities
+#
+CONFIG_INSMOD=y
+CONFIG_FEATURE_INSMOD_VERSION_CHECKING=y
+CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS=y
+CONFIG_FEATURE_INSMOD_LOADINKMEM=y
+CONFIG_FEATURE_INSMOD_LOAD_MAP=y
+CONFIG_FEATURE_INSMOD_LOAD_MAP_FULL=y
+CONFIG_RMMOD=y
+CONFIG_LSMOD=y
+CONFIG_FEATURE_LSMOD_PRETTY_2_6_OUTPUT=y
+CONFIG_MODPROBE=y
+CONFIG_FEATURE_MODPROBE_MULTIPLE_OPTIONS=y
+CONFIG_FEATURE_MODPROBE_FANCY_ALIAS=y
+
+#
+# Options common to multiple modutils
+#
+CONFIG_FEATURE_CHECK_TAINTED_MODULE=y
+CONFIG_FEATURE_2_4_MODULES=y
+CONFIG_FEATURE_2_6_MODULES=y
+# CONFIG_FEATURE_QUERY_MODULE_INTERFACE is not set
+
+#
+# Linux System Utilities
+#
+CONFIG_DMESG=y
+CONFIG_FEATURE_DMESG_PRETTY=y
+CONFIG_FBSET=y
+CONFIG_FEATURE_FBSET_FANCY=y
+CONFIG_FEATURE_FBSET_READMODE=y
+CONFIG_FDFLUSH=y
+CONFIG_FDFORMAT=y
+CONFIG_FDISK=y
+CONFIG_FDISK_SUPPORT_LARGE_DISKS=y
+CONFIG_FEATURE_FDISK_WRITABLE=y
+# CONFIG_FEATURE_AIX_LABEL is not set
+# CONFIG_FEATURE_SGI_LABEL is not set
+# CONFIG_FEATURE_SUN_LABEL is not set
+# CONFIG_FEATURE_OSF_LABEL is not set
+CONFIG_FEATURE_FDISK_ADVANCED=y
+CONFIG_FREERAMDISK=y
+CONFIG_FSCK_MINIX=y
+CONFIG_MKFS_MINIX=y
+
+#
+# Minix filesystem support
+#
+CONFIG_FEATURE_MINIX2=y
+CONFIG_GETOPT=y
+CONFIG_HEXDUMP=y
+CONFIG_HWCLOCK=y
+CONFIG_FEATURE_HWCLOCK_LONG_OPTIONS=y
+CONFIG_FEATURE_HWCLOCK_ADJTIME_FHS=y
+CONFIG_IPCRM=y
+CONFIG_IPCS=y
+CONFIG_LOSETUP=y
+CONFIG_MDEV=y
+CONFIG_FEATURE_MDEV_CONF=y
+CONFIG_FEATURE_MDEV_EXEC=y
+CONFIG_MKSWAP=y
+CONFIG_FEATURE_MKSWAP_V0=y
+CONFIG_MORE=y
+CONFIG_FEATURE_USE_TERMIOS=y
+CONFIG_MOUNT=y
+# CONFIG_FEATURE_MOUNT_NFS is not set
+CONFIG_FEATURE_MOUNT_CIFS=y
+CONFIG_FEATURE_MOUNT_FLAGS=y
+CONFIG_FEATURE_MOUNT_FSTAB=y
+CONFIG_PIVOT_ROOT=y
+CONFIG_RDATE=y
+CONFIG_READPROFILE=y
+CONFIG_SETARCH=y
+CONFIG_SWAPONOFF=y
+CONFIG_SWITCH_ROOT=y
+CONFIG_UMOUNT=y
+CONFIG_FEATURE_UMOUNT_ALL=y
+
+#
+# Common options for mount/umount
+#
+CONFIG_FEATURE_MOUNT_LOOP=y
+# CONFIG_FEATURE_MTAB_SUPPORT is not set
+
+#
+# Miscellaneous Utilities
+#
+CONFIG_ADJTIMEX=y
+# CONFIG_BBCONFIG is not set
+CONFIG_CHRT=y
+CONFIG_CROND=y
+# CONFIG_DEBUG_CROND_OPTION is not set
+CONFIG_FEATURE_CROND_CALL_SENDMAIL=y
+CONFIG_CRONTAB=y
+CONFIG_DC=y
+# CONFIG_DEVFSD is not set
+# CONFIG_DEVFSD_MODLOAD is not set
+# CONFIG_DEVFSD_FG_NP is not set
+# CONFIG_DEVFSD_VERBOSE is not set
+# CONFIG_FEATURE_DEVFS is not set
+CONFIG_EJECT=y
+CONFIG_LAST=y
+CONFIG_LESS=y
+CONFIG_FEATURE_LESS_MAXLINES=9999999
+CONFIG_FEATURE_LESS_BRACKETS=y
+CONFIG_FEATURE_LESS_FLAGS=y
+CONFIG_FEATURE_LESS_FLAGCS=y
+CONFIG_FEATURE_LESS_MARKS=y
+CONFIG_FEATURE_LESS_REGEXP=y
+CONFIG_HDPARM=y
+CONFIG_FEATURE_HDPARM_GET_IDENTITY=y
+CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF=y
+CONFIG_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF=y
+CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET=y
+CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF=y
+CONFIG_FEATURE_HDPARM_HDIO_GETSET_DMA=y
+CONFIG_MAKEDEVS=y
+# CONFIG_FEATURE_MAKEDEVS_LEAF is not set
+CONFIG_FEATURE_MAKEDEVS_TABLE=y
+CONFIG_MOUNTPOINT=y
+CONFIG_MT=y
+CONFIG_NMETER=y
+CONFIG_RAIDAUTORUN=y
+CONFIG_READAHEAD=y
+CONFIG_RUNLEVEL=y
+CONFIG_RX=y
+CONFIG_STRINGS=y
+CONFIG_SETSID=y
+CONFIG_TASKSET=y
+CONFIG_FEATURE_TASKSET_FANCY=y
+CONFIG_TIME=y
+CONFIG_WATCHDOG=y
+
+#
+# Networking Utilities
+#
+CONFIG_FEATURE_IPV6=y
+# CONFIG_VERBOSE_RESOLUTION_ERRORS is not set
+CONFIG_ARP=y
+CONFIG_ARPING=y
+CONFIG_DNSD=y
+CONFIG_ETHER_WAKE=y
+CONFIG_FAKEIDENTD=y
+CONFIG_FTPGET=y
+CONFIG_FTPPUT=y
+CONFIG_FEATURE_FTPGETPUT_LONG_OPTIONS=y
+CONFIG_HOSTNAME=y
+CONFIG_HTTPD=y
+# CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP is not set
+# CONFIG_FEATURE_HTTPD_SETUID is not set
+CONFIG_FEATURE_HTTPD_BASIC_AUTH=y
+CONFIG_FEATURE_HTTPD_AUTH_MD5=y
+CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES=y
+CONFIG_FEATURE_HTTPD_CGI=y
+CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR=y
+CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV=y
+CONFIG_FEATURE_HTTPD_ENCODE_URL_STR=y
+CONFIG_IFCONFIG=y
+CONFIG_FEATURE_IFCONFIG_STATUS=y
+CONFIG_FEATURE_IFCONFIG_SLIP=y
+CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ=y
+CONFIG_FEATURE_IFCONFIG_HW=y
+CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS=y
+CONFIG_IFUPDOWN=y
+CONFIG_FEATURE_IFUPDOWN_IP=y
+CONFIG_FEATURE_IFUPDOWN_IP_BUILTIN=y
+# CONFIG_FEATURE_IFUPDOWN_IFCONFIG_BUILTIN is not set
+CONFIG_FEATURE_IFUPDOWN_IPV4=y
+CONFIG_FEATURE_IFUPDOWN_IPV6=y
+CONFIG_FEATURE_IFUPDOWN_IPX=y
+CONFIG_FEATURE_IFUPDOWN_MAPPING=y
+CONFIG_INETD=y
+CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_ECHO=y
+CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD=y
+CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_TIME=y
+CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME=y
+CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN=y
+# CONFIG_FEATURE_INETD_RPC is not set
+CONFIG_IP=y
+CONFIG_FEATURE_IP_ADDRESS=y
+CONFIG_FEATURE_IP_LINK=y
+CONFIG_FEATURE_IP_ROUTE=y
+CONFIG_FEATURE_IP_TUNNEL=y
+CONFIG_FEATURE_IP_RULE=y
+CONFIG_FEATURE_IP_SHORT_FORMS=y
+CONFIG_IPADDR=y
+CONFIG_IPLINK=y
+CONFIG_IPROUTE=y
+CONFIG_IPTUNNEL=y
+CONFIG_IPRULE=y
+CONFIG_IPCALC=y
+CONFIG_FEATURE_IPCALC_FANCY=y
+CONFIG_FEATURE_IPCALC_LONG_OPTIONS=y
+CONFIG_NAMEIF=y
+CONFIG_NC=y
+CONFIG_NC_SERVER=y
+CONFIG_NC_EXTRA=y
+CONFIG_NETSTAT=y
+CONFIG_NSLOOKUP=y
+CONFIG_PING=y
+CONFIG_PING6=y
+CONFIG_FEATURE_FANCY_PING=y
+CONFIG_ROUTE=y
+CONFIG_TELNET=y
+CONFIG_FEATURE_TELNET_TTYPE=y
+CONFIG_FEATURE_TELNET_AUTOLOGIN=y
+CONFIG_TELNETD=y
+CONFIG_FEATURE_TELNETD_STANDALONE=y
+CONFIG_TFTP=y
+CONFIG_FEATURE_TFTP_GET=y
+CONFIG_FEATURE_TFTP_PUT=y
+CONFIG_FEATURE_TFTP_BLOCKSIZE=y
+# CONFIG_DEBUG_TFTP is not set
+CONFIG_TRACEROUTE=y
+# CONFIG_FEATURE_TRACEROUTE_VERBOSE is not set
+# CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE is not set
+# CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set
+CONFIG_APP_UDHCPD=y
+CONFIG_APP_DHCPRELAY=y
+CONFIG_APP_DUMPLEASES=y
+CONFIG_APP_UDHCPC=y
+CONFIG_FEATURE_UDHCP_SYSLOG=y
+# CONFIG_FEATURE_UDHCP_DEBUG is not set
+CONFIG_FEATURE_RFC3397=y
+CONFIG_VCONFIG=y
+CONFIG_WGET=y
+CONFIG_FEATURE_WGET_STATUSBAR=y
+CONFIG_FEATURE_WGET_AUTHENTICATION=y
+CONFIG_FEATURE_WGET_LONG_OPTIONS=y
+CONFIG_ZCIP=y
+
+#
+# Process Utilities
+#
+CONFIG_FREE=y
+CONFIG_FUSER=y
+CONFIG_KILL=y
+CONFIG_KILLALL=y
+CONFIG_KILLALL5=y
+CONFIG_PIDOF=y
+CONFIG_FEATURE_PIDOF_SINGLE=y
+CONFIG_FEATURE_PIDOF_OMIT=y
+CONFIG_PS=y
+CONFIG_FEATURE_PS_WIDE=y
+CONFIG_RENICE=y
+CONFIG_BB_SYSCTL=y
+CONFIG_TOP=y
+CONFIG_FEATURE_TOP_CPU_USAGE_PERCENTAGE=y
+CONFIG_UPTIME=y
+
+#
+# Shells
+#
+CONFIG_FEATURE_SH_IS_ASH=y
+# CONFIG_FEATURE_SH_IS_HUSH is not set
+# CONFIG_FEATURE_SH_IS_LASH is not set
+# CONFIG_FEATURE_SH_IS_MSH is not set
+# CONFIG_FEATURE_SH_IS_NONE is not set
+CONFIG_ASH=y
+
+#
+# Ash Shell Options
+#
+CONFIG_ASH_JOB_CONTROL=y
+CONFIG_ASH_READ_NCHARS=y
+CONFIG_ASH_READ_TIMEOUT=y
+# CONFIG_ASH_ALIAS is not set
+CONFIG_ASH_MATH_SUPPORT=y
+CONFIG_ASH_MATH_SUPPORT_64=y
+# CONFIG_ASH_GETOPTS is not set
+CONFIG_ASH_BUILTIN_ECHO=y
+CONFIG_ASH_BUILTIN_TEST=y
+CONFIG_ASH_CMDCMD=y
+# CONFIG_ASH_MAIL is not set
+CONFIG_ASH_OPTIMIZE_FOR_SIZE=y
+CONFIG_ASH_RANDOM_SUPPORT=y
+# CONFIG_ASH_EXPAND_PRMT is not set
+# CONFIG_HUSH is not set
+# CONFIG_LASH is not set
+# CONFIG_MSH is not set
+
+#
+# Bourne Shell Options
+#
+CONFIG_FEATURE_SH_EXTRA_QUIET=y
+# CONFIG_FEATURE_SH_STANDALONE_SHELL is not set
+
+#
+# System Logging Utilities
+#
+CONFIG_SYSLOGD=y
+CONFIG_FEATURE_ROTATE_LOGFILE=y
+CONFIG_FEATURE_REMOTE_LOG=y
+CONFIG_FEATURE_IPC_SYSLOG=y
+CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE=16
+CONFIG_LOGREAD=y
+CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING=y
+CONFIG_KLOGD=y
+CONFIG_LOGGER=y
+
+#
+# Runit Utilities
+#
+CONFIG_RUNSV=y
+CONFIG_RUNSVDIR=y
+CONFIG_SV=y
+CONFIG_SVLOGD=y
+CONFIG_CHPST=y
+CONFIG_SETUIDGID=y
+CONFIG_ENVUIDGID=y
+CONFIG_ENVDIR=y
+CONFIG_SOFTLIMIT=y
+# CONFIG_CHCON is not set
+# CONFIG_FEATURE_CHCON_LONG_OPTIONS is not set
+# CONFIG_GETENFORCE is not set
+# CONFIG_GETSEBOOL is not set
+# CONFIG_MATCHPATHCON is not set
+# CONFIG_RUNCON is not set
+# CONFIG_FEATURE_RUNCON_LONG_OPTIONS is not set
+# CONFIG_SELINUXENABLED is not set
+# CONFIG_SETENFORCE is not set
diff --git a/i/pc104/initrd/conf/busybox/scripts/gcc-version.sh b/i/pc104/initrd/conf/busybox/scripts/gcc-version.sh
new file mode 100755
index 0000000..3451080
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/scripts/gcc-version.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+#
+# gcc-version gcc-command
+#
+# Prints the gcc version of `gcc-command' in a canonical 4-digit form
+# such as `0295' for gcc-2.95, `0303' for gcc-3.3, etc.
+#
+
+compiler="$*"
+
+MAJ_MIN=$(echo __GNUC__ __GNUC_MINOR__ | $compiler -E -xc - | tail -n 1)
+printf '%02d%02d\n' $MAJ_MIN
diff --git a/i/pc104/initrd/conf/busybox/scripts/individual b/i/pc104/initrd/conf/busybox/scripts/individual
new file mode 100755
index 0000000..a09a5dc
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/scripts/individual
@@ -0,0 +1,129 @@
+#!/bin/sh
+
+# Compile individual versions of each busybox applet.
+
+if [ $# -eq 0 ]
+then
+
+# Clear out the build directory. (Make clean should do this instead of here.)
+
+rm -rf build
+mkdir build
+
+# Make our prerequisites.
+
+make busybox.links include/bb_config.h $(pwd)/{libbb/libbb.a,archival/libunarchive/libunarchive.a,coreutils/libcoreutils/libcoreutils.a,networking/libiproute/libiproute.a}
+
+else
+# Could very well be that we want to build an individual applet but have no
+# 'build' dir yet..
+
+test -d ./build || mkdir build
+
+fi
+
+# About 3/5 of the applets build from one .c file (with the same name as the
+# corresponding applet), and all it needs to link against. However, to build
+# them all we need more than that.
+
+# Figure out which applets need extra libraries added to their command line.
+
+function substithing()
+{
+ if [ "${1/ $3 //}" != "$1" ]
+ then
+ echo $2
+ fi
+}
+
+function extra_libraries()
+{
+ # gzip needs gunzip.c (when gunzip is enabled, anyway).
+ substithing " gzip " "archival/gunzip.c archival/uncompress.c" "$1"
+
+ # init needs init_shared.c
+ substithing " init " "init/init_shared.c" "$1"
+
+ # ifconfig needs interface.c
+ substithing " ifconfig " "networking/interface.c" "$1"
+
+ # Applets that need libunarchive.a
+ substithing " ar bunzip2 unlzma cpio dpkg gunzip rpm2cpio rpm tar uncompress unzip dpkg_deb gzip " "archival/libunarchive/libunarchive.a" "$1"
+
+ # Applets that need libcoreutils.a
+ substithing " cp mv " "coreutils/libcoreutils/libcoreutils.a" "$1"
+
+ # Applets that need libiproute.a
+ substithing " ip " "networking/libiproute/libiproute.a" "$1"
+
+ # What needs -libm?
+ substithing " awk dc " "-lm" "$1"
+
+ # What needs -lcrypt?
+ substithing " httpd vlock " "-lcrypt" "$1"
+}
+
+# Query applets.h to figure out which applets need special treatment
+
+strange_names=`sed -rn -e 's/\#.*//' -e 's/.*APPLET_NOUSAGE\(([^,]*),([^,]*),.*/\1 \2/p' -e 's/.*APPLET_ODDNAME\(([^,]*),([^,]*),.*, *([^)]*).*/\1 \2@\3/p' include/applets.h`
+
+function bonkname()
+{
+ while [ $# -gt 0 ]
+ do
+ if [ "$APPLET" == "$1" ]
+ then
+ APPFILT="${2/@*/}"
+ if [ "${APPFILT}" == "$2" ]
+ then
+ HELPNAME='"nousage\n"' # These should be _fixed_.
+ else
+ HELPNAME="${2/*@/}"_full_usage
+ fi
+ break
+ fi
+ shift 2
+ done
+#echo APPLET=${APPLET} APPFILT=${APPFILT} HELPNAME=${HELPNAME} 2=${2}
+}
+
+# Iterate through every name in busybox.links
+
+function buildit ()
+{
+ export APPLET="$1"
+ export APPFILT=${APPLET}
+ export HELPNAME=${APPLET}_full_usage
+
+ bonkname $strange_names
+
+ j=`find archival console-tools coreutils debianutils editors findutils init loginutils miscutils modutils networking procps shell sysklogd util-linux -name "${APPFILT}.c"`
+ if [ -z "$j" ]
+ then
+ echo no file for $APPLET
+ else
+ echo "Building $APPLET"
+ gcc -Os -o build/$APPLET applets/individual.c $j \
+ `extra_libraries $APPFILT` libbb/libbb.a -Iinclude \
+ -DBUILD_INDIVIDUAL \
+ '-Drun_applet_by_name(...)' '-Dfind_applet_by_name(...)=0' \
+ -DAPPLET_main=${APPFILT}_main -DAPPLET_full_usage=${HELPNAME}
+ if [ $? -ne 0 ];
+ then
+ echo "Failed $APPLET"
+ fi
+ fi
+}
+
+if [ $# -eq 0 ]
+then
+ for APPLET in `sed 's .*/ ' busybox.links`
+ do
+ buildit "$APPLET"
+ done
+else
+ buildit "$1"
+fi
+
+
+strip build/*
diff --git a/i/pc104/initrd/conf/busybox/scripts/kconfig/Makefile b/i/pc104/initrd/conf/busybox/scripts/kconfig/Makefile
new file mode 100644
index 0000000..a28414d
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/scripts/kconfig/Makefile
@@ -0,0 +1,254 @@
+# ===========================================================================
+# Kernel configuration targets
+# These targets are used from top-level makefile
+
+PHONY += oldconfig xconfig gconfig menuconfig config silentoldconfig update-po-config
+
+xconfig: $(obj)/qconf
+ $< Config.in
+
+gconfig: $(obj)/gconf
+ $< Config.in
+
+menuconfig: $(obj)/mconf
+ $(Q)$(MAKE) $(build)=scripts/kconfig/lxdialog
+ $< Config.in
+
+config: $(obj)/conf
+ $< Config.in
+
+oldconfig: $(obj)/conf
+ $< -o Config.in
+
+silentoldconfig: $(obj)/conf
+ $< -s Config.in
+
+update-po-config: $(obj)/kxgettext
+ xgettext --default-domain=linux \
+ --add-comments --keyword=_ --keyword=N_ \
+ --files-from=scripts/kconfig/POTFILES.in \
+ --output scripts/kconfig/config.pot
+ $(Q)ln -fs Kconfig_i386 arch/um/Kconfig_arch
+ $(Q)for i in `ls arch/`; \
+ do \
+ scripts/kconfig/kxgettext arch/$$i/Kconfig \
+ | msguniq -o scripts/kconfig/linux_$${i}.pot; \
+ done
+ $(Q)msgcat scripts/kconfig/config.pot \
+ `find scripts/kconfig/ -type f -name linux_*.pot` \
+ --output scripts/kconfig/linux_raw.pot
+ $(Q)msguniq --sort-by-file scripts/kconfig/linux_raw.pot \
+ --output scripts/kconfig/linux.pot
+ $(Q)rm -f arch/um/Kconfig_arch
+ $(Q)rm -f scripts/kconfig/linux_*.pot scripts/kconfig/config.pot
+
+PHONY += randconfig allyesconfig allnoconfig allmodconfig defconfig
+
+randconfig: $(obj)/conf
+ $< -r Config.in
+
+allyesconfig: $(obj)/conf
+ $< -y Config.in
+
+allnoconfig: $(obj)/conf
+ $< -n Config.in
+
+allmodconfig: $(obj)/conf
+ $< -m Config.in
+
+defconfig: $(obj)/conf
+ifeq ($(KBUILD_DEFCONFIG),)
+ $< -d Config.in
+else
+ @echo *** Default configuration is based on '$(KBUILD_DEFCONFIG)'
+ $(Q)$< -D $(KBUILD_DEFCONFIG) Config.in
+endif
+
+%_defconfig: $(obj)/conf
+ $(Q)$< -D $@ Config.in
+
+# Help text used by make help
+help:
+ @echo ' config - Update current config utilising a line-oriented program'
+ @echo ' menuconfig - Update current config utilising a menu based program'
+ @echo ' xconfig - Update current config utilising a QT based front-end'
+ @echo ' gconfig - Update current config utilising a GTK based front-end'
+ @echo ' oldconfig - Update current config utilising a provided .config as base'
+ @echo ' randconfig - New config with random answer to all options'
+ @echo ' defconfig - New config with default answer to all options'
+ @echo ' allmodconfig - New config selecting modules when possible'
+ @echo ' allyesconfig - New config where all options are accepted with yes'
+ @echo ' allnoconfig - New config where all options are answered with no'
+
+# ===========================================================================
+# Shared Makefile for the various kconfig executables:
+# conf: Used for defconfig, oldconfig and related targets
+# mconf: Used for the mconfig target.
+# Utilizes the lxdialog package
+# qconf: Used for the xconfig target
+# Based on QT which needs to be installed to compile it
+# gconf: Used for the gconfig target
+# Based on GTK which needs to be installed to compile it
+# object files used by all kconfig flavours
+
+hostprogs-y := conf mconf qconf gconf kxgettext
+conf-objs := conf.o zconf.tab.o
+mconf-objs := mconf.o zconf.tab.o
+kxgettext-objs := kxgettext.o zconf.tab.o
+
+ifeq ($(MAKECMDGOALS),xconfig)
+ qconf-target := 1
+endif
+ifeq ($(MAKECMDGOALS),gconfig)
+ gconf-target := 1
+endif
+
+
+ifeq ($(qconf-target),1)
+qconf-cxxobjs := qconf.o
+qconf-objs := kconfig_load.o zconf.tab.o
+endif
+
+ifeq ($(gconf-target),1)
+gconf-objs := gconf.o kconfig_load.o zconf.tab.o
+endif
+
+clean-files := lkc_defs.h qconf.moc .tmp_qtcheck \
+ .tmp_gtkcheck zconf.tab.c lex.zconf.c zconf.hash.c
+subdir- += lxdialog
+
+# Needed for systems without gettext
+KBUILD_HAVE_NLS := $(shell \
+ if echo "\#include <libintl.h>" | $(HOSTCC) $(HOSTCFLAGS) -E - > /dev/null 2>&1 ; \
+ then echo yes ; \
+ else echo no ; fi)
+ifeq ($(KBUILD_HAVE_NLS),no)
+HOSTCFLAGS += -DKBUILD_NO_NLS
+endif
+
+# generated files seem to need this to find local include files
+HOSTCFLAGS_lex.zconf.o := -I$(src)
+HOSTCFLAGS_zconf.tab.o := -I$(src)
+
+HOSTLOADLIBES_qconf = $(KC_QT_LIBS) -ldl
+HOSTCXXFLAGS_qconf.o = $(KC_QT_CFLAGS) -D LKC_DIRECT_LINK
+
+HOSTLOADLIBES_gconf = `pkg-config --libs gtk+-2.0 gmodule-2.0 libglade-2.0`
+HOSTCFLAGS_gconf.o = `pkg-config --cflags gtk+-2.0 gmodule-2.0 libglade-2.0` \
+ -D LKC_DIRECT_LINK
+
+$(obj)/qconf.o: $(obj)/.tmp_qtcheck
+
+ifeq ($(qconf-target),1)
+$(obj)/.tmp_qtcheck: $(src)/Makefile
+-include $(obj)/.tmp_qtcheck
+
+# QT needs some extra effort...
+$(obj)/.tmp_qtcheck:
+ @set -e; echo " CHECK qt"; dir=""; pkg=""; \
+ pkg-config --exists qt 2> /dev/null && pkg=qt; \
+ pkg-config --exists qt-mt 2> /dev/null && pkg=qt-mt; \
+ if [ -n "$$pkg" ]; then \
+ cflags="\$$(shell pkg-config $$pkg --cflags)"; \
+ libs="\$$(shell pkg-config $$pkg --libs)"; \
+ moc="\$$(shell pkg-config $$pkg --variable=prefix)/bin/moc"; \
+ dir="$$(pkg-config $$pkg --variable=prefix)"; \
+ else \
+ for d in $$QTDIR /usr/share/qt* /usr/lib/qt*; do \
+ if [ -f $$d/include/qconfig.h ]; then dir=$$d; break; fi; \
+ done; \
+ if [ -z "$$dir" ]; then \
+ echo "*"; \
+ echo "* Unable to find the QT installation. Please make sure that"; \
+ echo "* the QT development package is correctly installed and"; \
+ echo "* either install pkg-config or set the QTDIR environment"; \
+ echo "* variable to the correct location."; \
+ echo "*"; \
+ false; \
+ fi; \
+ libpath=$$dir/lib; lib=qt; osdir=""; \
+ $(HOSTCXX) -print-multi-os-directory > /dev/null 2>&1 && \
+ osdir=x$$($(HOSTCXX) -print-multi-os-directory); \
+ test -d $$libpath/$$osdir && libpath=$$libpath/$$osdir; \
+ test -f $$libpath/libqt-mt.so && lib=qt-mt; \
+ cflags="-I$$dir/include"; \
+ libs="-L$$libpath -Wl,-rpath,$$libpath -l$$lib"; \
+ moc="$$dir/bin/moc"; \
+ fi; \
+ if [ ! -x $$dir/bin/moc -a -x /usr/bin/moc ]; then \
+ echo "*"; \
+ echo "* Unable to find $$dir/bin/moc, using /usr/bin/moc instead."; \
+ echo "*"; \
+ moc="/usr/bin/moc"; \
+ fi; \
+ echo "KC_QT_CFLAGS=$$cflags" > $@; \
+ echo "KC_QT_LIBS=$$libs" >> $@; \
+ echo "KC_QT_MOC=$$moc" >> $@
+endif
+
+$(obj)/gconf.o: $(obj)/.tmp_gtkcheck
+
+ifeq ($(gconf-target),1)
+-include $(obj)/.tmp_gtkcheck
+
+# GTK needs some extra effort, too...
+$(obj)/.tmp_gtkcheck:
+ @if `pkg-config --exists gtk+-2.0 gmodule-2.0 libglade-2.0`; then \
+ if `pkg-config --atleast-version=2.0.0 gtk+-2.0`; then \
+ touch $@; \
+ else \
+ echo "*"; \
+ echo "* GTK+ is present but version >= 2.0.0 is required."; \
+ echo "*"; \
+ false; \
+ fi \
+ else \
+ echo "*"; \
+ echo "* Unable to find the GTK+ installation. Please make sure that"; \
+ echo "* the GTK+ 2.0 development package is correctly installed..."; \
+ echo "* You need gtk+-2.0, glib-2.0 and libglade-2.0."; \
+ echo "*"; \
+ false; \
+ fi
+endif
+
+$(obj)/zconf.tab.o: $(obj)/lex.zconf.c $(obj)/zconf.hash.c
+
+$(obj)/kconfig_load.o: $(obj)/lkc_defs.h
+
+$(obj)/qconf.o: $(obj)/qconf.moc $(obj)/lkc_defs.h
+
+$(obj)/gconf.o: $(obj)/lkc_defs.h
+
+$(obj)/%.moc: $(src)/%.h
+ $(KC_QT_MOC) -i $< -o $@
+
+$(obj)/lkc_defs.h: $(src)/lkc_proto.h
+ sed < $< > $@ 's/P(\([^,]*\),.*/#define \1 (\*\1_p)/'
+
+
+###
+# The following requires flex/bison/gperf
+# By default we use the _shipped versions, uncomment the following line if
+# you are modifying the flex/bison src.
+# LKC_GENPARSER := 1
+
+ifdef LKC_GENPARSER
+
+$(obj)/zconf.tab.c: $(src)/zconf.y
+$(obj)/lex.zconf.c: $(src)/zconf.l
+$(obj)/zconf.hash.c: $(src)/zconf.gperf
+
+%.tab.c: %.y
+ bison -l -b $* -p $(notdir $*) $<
+ cp $@ $@_shipped
+
+lex.%.c: %.l
+ flex -L -P$(notdir $*) -o$@ $<
+ cp $@ $@_shipped
+
+%.hash.c: %.gperf
+ gperf < $< > $@
+ cp $@ $@_shipped
+
+endif
diff --git a/i/pc104/initrd/conf/busybox/scripts/kconfig/POTFILES.in b/i/pc104/initrd/conf/busybox/scripts/kconfig/POTFILES.in
new file mode 100644
index 0000000..cc94e46
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/scripts/kconfig/POTFILES.in
@@ -0,0 +1,5 @@
+scripts/kconfig/mconf.c
+scripts/kconfig/conf.c
+scripts/kconfig/confdata.c
+scripts/kconfig/gconf.c
+scripts/kconfig/qconf.cc
diff --git a/i/pc104/initrd/conf/busybox/scripts/kconfig/conf.c b/i/pc104/initrd/conf/busybox/scripts/kconfig/conf.c
new file mode 100644
index 0000000..02693b7
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/scripts/kconfig/conf.c
@@ -0,0 +1,612 @@
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/stat.h>
+
+#define LKC_DIRECT_LINK
+#include "lkc.h"
+
+static void conf(struct menu *menu);
+static void check_conf(struct menu *menu);
+
+enum {
+ ask_all,
+ ask_new,
+ ask_silent,
+ set_default,
+ set_yes,
+ set_mod,
+ set_no,
+ set_random
+} input_mode = ask_all;
+char *defconfig_file;
+
+static int indent = 1;
+static int valid_stdin = 1;
+static int conf_cnt;
+static char line[128];
+static struct menu *rootEntry;
+
+static char nohelp_text[] = N_("Sorry, no help available for this option yet.\n");
+
+static void strip(char *str)
+{
+ char *p = str;
+ int l;
+
+ while ((isspace(*p)))
+ p++;
+ l = strlen(p);
+ if (p != str)
+ memmove(str, p, l + 1);
+ if (!l)
+ return;
+ p = str + l - 1;
+ while ((isspace(*p)))
+ *p-- = 0;
+}
+
+static void check_stdin(void)
+{
+ if (!valid_stdin && input_mode == ask_silent) {
+ printf(_("aborted!\n\n"));
+ printf(_("Console input/output is redirected. "));
+ printf(_("Run 'make oldconfig' to update configuration.\n\n"));
+ exit(1);
+ }
+}
+
+static void conf_askvalue(struct symbol *sym, const char *def)
+{
+ enum symbol_type type = sym_get_type(sym);
+ tristate val;
+
+ if (!sym_has_value(sym))
+ printf("(NEW) ");
+
+ line[0] = '\n';
+ line[1] = 0;
+
+ if (!sym_is_changable(sym)) {
+ printf("%s\n", def);
+ line[0] = '\n';
+ line[1] = 0;
+ return;
+ }
+
+ switch (input_mode) {
+ case set_no:
+ case set_mod:
+ case set_yes:
+ case set_random:
+ if (sym_has_value(sym)) {
+ printf("%s\n", def);
+ return;
+ }
+ break;
+ case ask_new:
+ case ask_silent:
+ if (sym_has_value(sym)) {
+ printf("%s\n", def);
+ return;
+ }
+ check_stdin();
+ case ask_all:
+ fflush(stdout);
+ fgets(line, 128, stdin);
+ return;
+ case set_default:
+ printf("%s\n", def);
+ return;
+ default:
+ break;
+ }
+
+ switch (type) {
+ case S_INT:
+ case S_HEX:
+ case S_STRING:
+ printf("%s\n", def);
+ return;
+ default:
+ ;
+ }
+ switch (input_mode) {
+ case set_yes:
+ if (sym_tristate_within_range(sym, yes)) {
+ line[0] = 'y';
+ line[1] = '\n';
+ line[2] = 0;
+ break;
+ }
+ case set_mod:
+ if (type == S_TRISTATE) {
+ if (sym_tristate_within_range(sym, mod)) {
+ line[0] = 'm';
+ line[1] = '\n';
+ line[2] = 0;
+ break;
+ }
+ } else {
+ if (sym_tristate_within_range(sym, yes)) {
+ line[0] = 'y';
+ line[1] = '\n';
+ line[2] = 0;
+ break;
+ }
+ }
+ case set_no:
+ if (sym_tristate_within_range(sym, no)) {
+ line[0] = 'n';
+ line[1] = '\n';
+ line[2] = 0;
+ break;
+ }
+ case set_random:
+ do {
+ val = (tristate)(random() % 3);
+ } while (!sym_tristate_within_range(sym, val));
+ switch (val) {
+ case no: line[0] = 'n'; break;
+ case mod: line[0] = 'm'; break;
+ case yes: line[0] = 'y'; break;
+ }
+ line[1] = '\n';
+ line[2] = 0;
+ break;
+ default:
+ break;
+ }
+ printf("%s", line);
+}
+
+int conf_string(struct menu *menu)
+{
+ struct symbol *sym = menu->sym;
+ const char *def, *help;
+
+ while (1) {
+ printf("%*s%s ", indent - 1, "", menu->prompt->text);
+ printf("(%s) ", sym->name);
+ def = sym_get_string_value(sym);
+ if (sym_get_string_value(sym))
+ printf("[%s] ", def);
+ conf_askvalue(sym, def);
+ switch (line[0]) {
+ case '\n':
+ break;
+ case '?':
+ /* print help */
+ if (line[1] == '\n') {
+ help = nohelp_text;
+ if (menu->sym->help)
+ help = menu->sym->help;
+ printf("\n%s\n", menu->sym->help);
+ def = NULL;
+ break;
+ }
+ default:
+ line[strlen(line)-1] = 0;
+ def = line;
+ }
+ if (def && sym_set_string_value(sym, def))
+ return 0;
+ }
+}
+
+static int conf_sym(struct menu *menu)
+{
+ struct symbol *sym = menu->sym;
+ int type;
+ tristate oldval, newval;
+ const char *help;
+
+ while (1) {
+ printf("%*s%s ", indent - 1, "", menu->prompt->text);
+ if (sym->name)
+ printf("(%s) ", sym->name);
+ type = sym_get_type(sym);
+ putchar('[');
+ oldval = sym_get_tristate_value(sym);
+ switch (oldval) {
+ case no:
+ putchar('N');
+ break;
+ case mod:
+ putchar('M');
+ break;
+ case yes:
+ putchar('Y');
+ break;
+ }
+ if (oldval != no && sym_tristate_within_range(sym, no))
+ printf("/n");
+ if (oldval != mod && sym_tristate_within_range(sym, mod))
+ printf("/m");
+ if (oldval != yes && sym_tristate_within_range(sym, yes))
+ printf("/y");
+ if (sym->help)
+ printf("/?");
+ printf("] ");
+ conf_askvalue(sym, sym_get_string_value(sym));
+ strip(line);
+
+ switch (line[0]) {
+ case 'n':
+ case 'N':
+ newval = no;
+ if (!line[1] || !strcmp(&line[1], "o"))
+ break;
+ continue;
+ case 'm':
+ case 'M':
+ newval = mod;
+ if (!line[1])
+ break;
+ continue;
+ case 'y':
+ case 'Y':
+ newval = yes;
+ if (!line[1] || !strcmp(&line[1], "es"))
+ break;
+ continue;
+ case 0:
+ newval = oldval;
+ break;
+ case '?':
+ goto help;
+ default:
+ continue;
+ }
+ if (sym_set_tristate_value(sym, newval))
+ return 0;
+help:
+ help = nohelp_text;
+ if (sym->help)
+ help = sym->help;
+ printf("\n%s\n", help);
+ }
+}
+
+static int conf_choice(struct menu *menu)
+{
+ struct symbol *sym, *def_sym;
+ struct menu *child;
+ int type;
+ bool is_new;
+
+ sym = menu->sym;
+ type = sym_get_type(sym);
+ is_new = !sym_has_value(sym);
+ if (sym_is_changable(sym)) {
+ conf_sym(menu);
+ sym_calc_value(sym);
+ switch (sym_get_tristate_value(sym)) {
+ case no:
+ return 1;
+ case mod:
+ return 0;
+ case yes:
+ break;
+ }
+ } else {
+ switch (sym_get_tristate_value(sym)) {
+ case no:
+ return 1;
+ case mod:
+ printf("%*s%s\n", indent - 1, "", menu_get_prompt(menu));
+ return 0;
+ case yes:
+ break;
+ }
+ }
+
+ while (1) {
+ int cnt, def;
+
+ printf("%*s%s\n", indent - 1, "", menu_get_prompt(menu));
+ def_sym = sym_get_choice_value(sym);
+ cnt = def = 0;
+ line[0] = 0;
+ for (child = menu->list; child; child = child->next) {
+ if (!menu_is_visible(child))
+ continue;
+ if (!child->sym) {
+ printf("%*c %s\n", indent, '*', menu_get_prompt(child));
+ continue;
+ }
+ cnt++;
+ if (child->sym == def_sym) {
+ def = cnt;
+ printf("%*c", indent, '>');
+ } else
+ printf("%*c", indent, ' ');
+ printf(" %d. %s", cnt, menu_get_prompt(child));
+ if (child->sym->name)
+ printf(" (%s)", child->sym->name);
+ if (!sym_has_value(child->sym))
+ printf(" (NEW)");
+ printf("\n");
+ }
+ printf("%*schoice", indent - 1, "");
+ if (cnt == 1) {
+ printf("[1]: 1\n");
+ goto conf_childs;
+ }
+ printf("[1-%d", cnt);
+ if (sym->help)
+ printf("?");
+ printf("]: ");
+ switch (input_mode) {
+ case ask_new:
+ case ask_silent:
+ if (!is_new) {
+ cnt = def;
+ printf("%d\n", cnt);
+ break;
+ }
+ check_stdin();
+ case ask_all:
+ fflush(stdout);
+ fgets(line, 128, stdin);
+ strip(line);
+ if (line[0] == '?') {
+ printf("\n%s\n", menu->sym->help ?
+ menu->sym->help : nohelp_text);
+ continue;
+ }
+ if (!line[0])
+ cnt = def;
+ else if (isdigit(line[0]))
+ cnt = atoi(line);
+ else
+ continue;
+ break;
+ case set_random:
+ def = (random() % cnt) + 1;
+ case set_default:
+ case set_yes:
+ case set_mod:
+ case set_no:
+ cnt = def;
+ printf("%d\n", cnt);
+ break;
+ }
+
+ conf_childs:
+ for (child = menu->list; child; child = child->next) {
+ if (!child->sym || !menu_is_visible(child))
+ continue;
+ if (!--cnt)
+ break;
+ }
+ if (!child)
+ continue;
+ if (strlen(line) > 0 && line[strlen(line) - 1] == '?') {
+ printf("\n%s\n", child->sym->help ?
+ child->sym->help : nohelp_text);
+ continue;
+ }
+ sym_set_choice_value(sym, child->sym);
+ if (child->list) {
+ indent += 2;
+ conf(child->list);
+ indent -= 2;
+ }
+ return 1;
+ }
+}
+
+static void conf(struct menu *menu)
+{
+ struct symbol *sym;
+ struct property *prop;
+ struct menu *child;
+
+ if (!menu_is_visible(menu))
+ return;
+
+ sym = menu->sym;
+ prop = menu->prompt;
+ if (prop) {
+ const char *prompt;
+
+ switch (prop->type) {
+ case P_MENU:
+ if (input_mode == ask_silent && rootEntry != menu) {
+ check_conf(menu);
+ return;
+ }
+ case P_COMMENT:
+ prompt = menu_get_prompt(menu);
+ if (prompt)
+ printf("%*c\n%*c %s\n%*c\n",
+ indent, '*',
+ indent, '*', prompt,
+ indent, '*');
+ default:
+ ;
+ }
+ }
+
+ if (!sym)
+ goto conf_childs;
+
+ if (sym_is_choice(sym)) {
+ conf_choice(menu);
+ if (sym->curr.tri != mod)
+ return;
+ goto conf_childs;
+ }
+
+ switch (sym->type) {
+ case S_INT:
+ case S_HEX:
+ case S_STRING:
+ conf_string(menu);
+ break;
+ default:
+ conf_sym(menu);
+ break;
+ }
+
+conf_childs:
+ if (sym)
+ indent += 2;
+ for (child = menu->list; child; child = child->next)
+ conf(child);
+ if (sym)
+ indent -= 2;
+}
+
+static void check_conf(struct menu *menu)
+{
+ struct symbol *sym;
+ struct menu *child;
+
+ if (!menu_is_visible(menu))
+ return;
+
+ sym = menu->sym;
+ if (sym && !sym_has_value(sym)) {
+ if (sym_is_changable(sym) ||
+ (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes)) {
+ if (!conf_cnt++)
+ printf(_("*\n* Restart config...\n*\n"));
+ rootEntry = menu_get_parent_menu(menu);
+ conf(rootEntry);
+ }
+ }
+
+ for (child = menu->list; child; child = child->next)
+ check_conf(child);
+}
+
+int main(int ac, char **av)
+{
+ int i = 1;
+ const char *name;
+ struct stat tmpstat;
+
+ if (ac > i && av[i][0] == '-') {
+ switch (av[i++][1]) {
+ case 'o':
+ input_mode = ask_new;
+ break;
+ case 's':
+ input_mode = ask_silent;
+ valid_stdin = isatty(0) && isatty(1) && isatty(2);
+ break;
+ case 'd':
+ input_mode = set_default;
+ break;
+ case 'D':
+ input_mode = set_default;
+ defconfig_file = av[i++];
+ if (!defconfig_file) {
+ printf(_("%s: No default config file specified\n"),
+ av[0]);
+ exit(1);
+ }
+ break;
+ case 'n':
+ input_mode = set_no;
+ break;
+ case 'm':
+ input_mode = set_mod;
+ break;
+ case 'y':
+ input_mode = set_yes;
+ break;
+ case 'r':
+ input_mode = set_random;
+ srandom(time(NULL));
+ break;
+ case 'h':
+ case '?':
+ fprintf(stderr, "See README for usage info\n");
+ exit(0);
+ }
+ }
+ name = av[i];
+ if (!name) {
+ printf(_("%s: Kconfig file missing\n"), av[0]);
+ }
+ conf_parse(name);
+ //zconfdump(stdout);
+ switch (input_mode) {
+ case set_default:
+ if (!defconfig_file)
+ defconfig_file = conf_get_default_confname();
+ if (conf_read(defconfig_file)) {
+ printf("***\n"
+ "*** Can't find default configuration \"%s\"!\n"
+ "***\n", defconfig_file);
+ exit(1);
+ }
+ break;
+ case ask_silent:
+ if (stat(".config", &tmpstat)) {
+ printf(_("***\n"
+ "*** You have not yet configured busybox!\n"
+ "***\n"
+ "*** Please run some configurator (e.g. \"make oldconfig\" or\n"
+ "*** \"make menuconfig\" or \"make defconfig\").\n"
+ "***\n"));
+ exit(1);
+ }
+ case ask_all:
+ case ask_new:
+ conf_read(NULL);
+ break;
+ case set_no:
+ case set_mod:
+ case set_yes:
+ case set_random:
+ name = getenv("KCONFIG_ALLCONFIG");
+ if (name && !stat(name, &tmpstat)) {
+ conf_read_simple(name);
+ break;
+ }
+ switch (input_mode) {
+ case set_no: name = "allno.config"; break;
+ case set_mod: name = "allmod.config"; break;
+ case set_yes: name = "allyes.config"; break;
+ case set_random: name = "allrandom.config"; break;
+ default: break;
+ }
+ if (!stat(name, &tmpstat))
+ conf_read_simple(name);
+ else if (!stat("all.config", &tmpstat))
+ conf_read_simple("all.config");
+ break;
+ default:
+ break;
+ }
+
+ if (input_mode != ask_silent) {
+ rootEntry = &rootmenu;
+ conf(&rootmenu);
+ if (input_mode == ask_all) {
+ input_mode = ask_silent;
+ valid_stdin = 1;
+ }
+ }
+ do {
+ conf_cnt = 0;
+ check_conf(&rootmenu);
+ } while (conf_cnt);
+ if (conf_write(NULL)) {
+ fprintf(stderr, _("\n*** Error during writing of the busybox configuration.\n\n"));
+ return 1;
+ }
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/scripts/kconfig/confdata.c b/i/pc104/initrd/conf/busybox/scripts/kconfig/confdata.c
new file mode 100644
index 0000000..f3f76dd
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/scripts/kconfig/confdata.c
@@ -0,0 +1,571 @@
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#include <sys/stat.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#define LKC_DIRECT_LINK
+#include "lkc.h"
+
+static void conf_warning(const char *fmt, ...)
+ __attribute__ ((format (printf, 1, 2)));
+
+static const char *conf_filename;
+static int conf_lineno, conf_warnings, conf_unsaved;
+
+const char conf_def_filename[] = ".config";
+
+const char conf_defname[] = "scripts/defconfig";
+
+const char *conf_confnames[] = {
+ ".config",
+ "/lib/modules/$UNAME_RELEASE/.config",
+ "/etc/busybox-config",
+ "/boot/config-$UNAME_RELEASE",
+ conf_defname,
+ NULL,
+};
+
+static void conf_warning(const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ fprintf(stderr, "%s:%d:warning: ", conf_filename, conf_lineno);
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ va_end(ap);
+ conf_warnings++;
+}
+
+static char *conf_expand_value(const char *in)
+{
+ struct symbol *sym;
+ const char *src;
+ static char res_value[SYMBOL_MAXLENGTH];
+ char *dst, name[SYMBOL_MAXLENGTH];
+
+ res_value[0] = 0;
+ dst = name;
+ while ((src = strchr(in, '$'))) {
+ strncat(res_value, in, src - in);
+ src++;
+ dst = name;
+ while (isalnum(*src) || *src == '_')
+ *dst++ = *src++;
+ *dst = 0;
+ sym = sym_lookup(name, 0);
+ sym_calc_value(sym);
+ strcat(res_value, sym_get_string_value(sym));
+ in = src;
+ }
+ strcat(res_value, in);
+
+ return res_value;
+}
+
+char *conf_get_default_confname(void)
+{
+ struct stat buf;
+ static char fullname[PATH_MAX+1];
+ char *env, *name;
+
+ name = conf_expand_value(conf_defname);
+ env = getenv(SRCTREE);
+ if (env) {
+ sprintf(fullname, "%s/%s", env, name);
+ if (!stat(fullname, &buf))
+ return fullname;
+ }
+ return name;
+}
+
+int conf_read_simple(const char *name)
+{
+ FILE *in = NULL;
+ char line[1024];
+ char *p, *p2;
+ struct symbol *sym;
+ int i;
+
+ if (name) {
+ in = zconf_fopen(name);
+ } else {
+ const char **names = conf_confnames;
+ while ((name = *names++)) {
+ name = conf_expand_value(name);
+ in = zconf_fopen(name);
+ if (in) {
+ printf(_("#\n"
+ "# using defaults found in %s\n"
+ "#\n"), name);
+ break;
+ }
+ }
+ }
+ if (!in)
+ return 1;
+
+ conf_filename = name;
+ conf_lineno = 0;
+ conf_warnings = 0;
+ conf_unsaved = 0;
+
+ for_all_symbols(i, sym) {
+ sym->flags |= SYMBOL_NEW | SYMBOL_CHANGED;
+ if (sym_is_choice(sym))
+ sym->flags &= ~SYMBOL_NEW;
+ sym->flags &= ~SYMBOL_VALID;
+ switch (sym->type) {
+ case S_INT:
+ case S_HEX:
+ case S_STRING:
+ if (sym->user.val)
+ free(sym->user.val);
+ default:
+ sym->user.val = NULL;
+ sym->user.tri = no;
+ }
+ }
+
+ while (fgets(line, sizeof(line), in)) {
+ conf_lineno++;
+ sym = NULL;
+ switch (line[0]) {
+ case '#':
+ if (memcmp(line + 2, "CONFIG_", 7))
+ continue;
+ p = strchr(line + 9, ' ');
+ if (!p)
+ continue;
+ *p++ = 0;
+ if (strncmp(p, "is not set", 10))
+ continue;
+ sym = sym_find(line + 9);
+ if (!sym) {
+ conf_warning("trying to assign nonexistent symbol %s", line + 9);
+ break;
+ } else if (!(sym->flags & SYMBOL_NEW)) {
+ conf_warning("trying to reassign symbol %s", sym->name);
+ break;
+ }
+ switch (sym->type) {
+ case S_BOOLEAN:
+ case S_TRISTATE:
+ sym->user.tri = no;
+ sym->flags &= ~SYMBOL_NEW;
+ break;
+ default:
+ ;
+ }
+ break;
+ case 'C':
+ if (memcmp(line, "CONFIG_", 7)) {
+ conf_warning("unexpected data");
+ continue;
+ }
+ p = strchr(line + 7, '=');
+ if (!p)
+ continue;
+ *p++ = 0;
+ p2 = strchr(p, '\n');
+ if (p2)
+ *p2 = 0;
+ sym = sym_find(line + 7);
+ if (!sym) {
+ conf_warning("trying to assign nonexistent symbol %s", line + 7);
+ break;
+ } else if (!(sym->flags & SYMBOL_NEW)) {
+ conf_warning("trying to reassign symbol %s", sym->name);
+ break;
+ }
+ switch (sym->type) {
+ case S_TRISTATE:
+ if (p[0] == 'm') {
+ sym->user.tri = mod;
+ sym->flags &= ~SYMBOL_NEW;
+ break;
+ }
+ case S_BOOLEAN:
+ if (p[0] == 'y') {
+ sym->user.tri = yes;
+ sym->flags &= ~SYMBOL_NEW;
+ break;
+ }
+ if (p[0] == 'n') {
+ sym->user.tri = no;
+ sym->flags &= ~SYMBOL_NEW;
+ break;
+ }
+ conf_warning("symbol value '%s' invalid for %s", p, sym->name);
+ break;
+ case S_STRING:
+ if (*p++ != '"')
+ break;
+ for (p2 = p; (p2 = strpbrk(p2, "\"\\")); p2++) {
+ if (*p2 == '"') {
+ *p2 = 0;
+ break;
+ }
+ memmove(p2, p2 + 1, strlen(p2));
+ }
+ if (!p2) {
+ conf_warning("invalid string found");
+ continue;
+ }
+ case S_INT:
+ case S_HEX:
+ if (sym_string_valid(sym, p)) {
+ sym->user.val = strdup(p);
+ sym->flags &= ~SYMBOL_NEW;
+ } else {
+ conf_warning("symbol value '%s' invalid for %s", p, sym->name);
+ continue;
+ }
+ break;
+ default:
+ ;
+ }
+ break;
+ case '\n':
+ break;
+ default:
+ conf_warning("unexpected data");
+ continue;
+ }
+ if (sym && sym_is_choice_value(sym)) {
+ struct symbol *cs = prop_get_symbol(sym_get_choice_prop(sym));
+ switch (sym->user.tri) {
+ case no:
+ break;
+ case mod:
+ if (cs->user.tri == yes) {
+ conf_warning("%s creates inconsistent choice state", sym->name);
+ cs->flags |= SYMBOL_NEW;
+ }
+ break;
+ case yes:
+ if (cs->user.tri != no) {
+ conf_warning("%s creates inconsistent choice state", sym->name);
+ cs->flags |= SYMBOL_NEW;
+ } else
+ cs->user.val = sym;
+ break;
+ }
+ cs->user.tri = E_OR(cs->user.tri, sym->user.tri);
+ }
+ }
+ fclose(in);
+
+ if (modules_sym)
+ sym_calc_value(modules_sym);
+ return 0;
+}
+
+int conf_read(const char *name)
+{
+ struct symbol *sym;
+ struct property *prop;
+ struct expr *e;
+ int i;
+
+ if (conf_read_simple(name))
+ return 1;
+
+ for_all_symbols(i, sym) {
+ sym_calc_value(sym);
+ if (sym_is_choice(sym) || (sym->flags & SYMBOL_AUTO))
+ goto sym_ok;
+ if (sym_has_value(sym) && (sym->flags & SYMBOL_WRITE)) {
+ /* check that calculated value agrees with saved value */
+ switch (sym->type) {
+ case S_BOOLEAN:
+ case S_TRISTATE:
+ if (sym->user.tri != sym_get_tristate_value(sym))
+ break;
+ if (!sym_is_choice(sym))
+ goto sym_ok;
+ default:
+ if (!strcmp(sym->curr.val, sym->user.val))
+ goto sym_ok;
+ break;
+ }
+ } else if (!sym_has_value(sym) && !(sym->flags & SYMBOL_WRITE))
+ /* no previous value and not saved */
+ goto sym_ok;
+ conf_unsaved++;
+ /* maybe print value in verbose mode... */
+ sym_ok:
+ if (sym_has_value(sym) && !sym_is_choice_value(sym)) {
+ if (sym->visible == no)
+ sym->flags |= SYMBOL_NEW;
+ switch (sym->type) {
+ case S_STRING:
+ case S_INT:
+ case S_HEX:
+ if (!sym_string_within_range(sym, sym->user.val)) {
+ sym->flags |= SYMBOL_NEW;
+ sym->flags &= ~SYMBOL_VALID;
+ }
+ default:
+ break;
+ }
+ }
+ if (!sym_is_choice(sym))
+ continue;
+ prop = sym_get_choice_prop(sym);
+ for (e = prop->expr; e; e = e->left.expr)
+ if (e->right.sym->visible != no)
+ sym->flags |= e->right.sym->flags & SYMBOL_NEW;
+ }
+
+ sym_change_count = conf_warnings || conf_unsaved;
+
+ return 0;
+}
+
+int conf_write(const char *name)
+{
+ FILE *out, *out_h;
+ struct symbol *sym;
+ struct menu *menu;
+ const char *basename;
+ char dirname[128], tmpname[128], newname[128];
+ int type, l;
+ const char *str;
+ time_t now;
+ int use_timestamp = 1;
+ char *env;
+
+ dirname[0] = 0;
+ if (name && name[0]) {
+ struct stat st;
+ char *slash;
+
+ if (!stat(name, &st) && S_ISDIR(st.st_mode)) {
+ strcpy(dirname, name);
+ strcat(dirname, "/");
+ basename = conf_def_filename;
+ } else if ((slash = strrchr(name, '/'))) {
+ int size = slash - name + 1;
+ memcpy(dirname, name, size);
+ dirname[size] = 0;
+ if (slash[1])
+ basename = slash + 1;
+ else
+ basename = conf_def_filename;
+ } else
+ basename = name;
+ } else
+ basename = conf_def_filename;
+
+ sprintf(newname, "%s.tmpconfig.%d", dirname, (int)getpid());
+ out = fopen(newname, "w");
+ if (!out)
+ return 1;
+ out_h = NULL;
+ if (!name) {
+ out_h = fopen(".tmpconfig.h", "w");
+ if (!out_h)
+ return 1;
+ file_write_dep(NULL);
+ }
+ sym = sym_lookup("KERNELVERSION", 0);
+ sym_calc_value(sym);
+ time(&now);
+ env = getenv("KCONFIG_NOTIMESTAMP");
+ if (env && *env)
+ use_timestamp = 0;
+
+ fprintf(out, _("#\n"
+ "# Automatically generated make config: don't edit\n"
+ "# Busybox version: %s\n"
+ "%s%s"
+ "#\n"),
+ sym_get_string_value(sym),
+ use_timestamp ? "# " : "",
+ use_timestamp ? ctime(&now) : "");
+ if (out_h) {
+ char buf[sizeof("#define AUTOCONF_TIMESTAMP "
+ "\"YYYY-MM-DD HH:MM:SS some_timezone\"\n")];
+ buf[0] = '\0';
+ if (use_timestamp) {
+ size_t ret = \
+ strftime(buf, sizeof(buf), "#define AUTOCONF_TIMESTAMP "
+ "\"%Y-%m-%d %H:%M:%S %Z\"\n", localtime(&now));
+ /* if user has Factory timezone or some other odd install, the
+ * %Z above will overflow the string leaving us with undefined
+ * results ... so let's try again without the timezone.
+ */
+ if (ret == 0)
+ strftime(buf, sizeof(buf), "#define AUTOCONF_TIMESTAMP "
+ "\"%Y-%m-%d %H:%M:%S\"\n", localtime(&now));
+ }
+ fprintf(out_h, "/*\n"
+ " * Automatically generated C config: don't edit\n"
+ " * Busybox version: %s\n"
+ " */\n"
+ "%s"
+ "\n",
+ sym_get_string_value(sym),
+ buf);
+ }
+ if (!sym_change_count)
+ sym_clear_all_valid();
+
+ menu = rootmenu.list;
+ while (menu) {
+ sym = menu->sym;
+ if (!sym) {
+ if (!menu_is_visible(menu))
+ goto next;
+ str = menu_get_prompt(menu);
+ fprintf(out, "\n"
+ "#\n"
+ "# %s\n"
+ "#\n", str);
+ if (out_h)
+ fprintf(out_h, "\n"
+ "/*\n"
+ " * %s\n"
+ " */\n", str);
+ } else if (!(sym->flags & SYMBOL_CHOICE)) {
+ sym_calc_value(sym);
+/* bbox: we want to all syms
+ if (!(sym->flags & SYMBOL_WRITE))
+ goto next;
+*/
+ sym->flags &= ~SYMBOL_WRITE;
+ type = sym->type;
+ if (type == S_TRISTATE) {
+ sym_calc_value(modules_sym);
+ if (modules_sym->curr.tri == no)
+ type = S_BOOLEAN;
+ }
+ switch (type) {
+ case S_BOOLEAN:
+ case S_TRISTATE:
+ switch (sym_get_tristate_value(sym)) {
+ case no:
+ fprintf(out, "# CONFIG_%s is not set\n", sym->name);
+ if (out_h) {
+ fprintf(out_h, "#undef CONFIG_%s\n", sym->name);
+ /* bbox */
+ fprintf(out_h, "#define ENABLE_%s 0\n", sym->name);
+ fprintf(out_h, "#define USE_%s(...)\n", sym->name);
+ fprintf(out_h, "#define SKIP_%s(...) __VA_ARGS__\n", sym->name);
+ }
+ break;
+ case mod:
+ fprintf(out, "CONFIG_%s=m\n", sym->name);
+ if (out_h)
+ fprintf(out_h, "#define CONFIG_%s_MODULE 1\n", sym->name);
+ break;
+ case yes:
+ fprintf(out, "CONFIG_%s=y\n", sym->name);
+ if (out_h) {
+ fprintf(out_h, "#define CONFIG_%s 1\n", sym->name);
+ /* bbox */
+ fprintf(out_h, "#define ENABLE_%s 1\n", sym->name);
+ fprintf(out_h, "#define USE_%s(...) __VA_ARGS__\n", sym->name);
+ fprintf(out_h, "#define SKIP_%s(...)\n", sym->name);
+ }
+ break;
+ }
+ break;
+ case S_STRING:
+ // fix me
+ str = sym_get_string_value(sym);
+ fprintf(out, "CONFIG_%s=\"", sym->name);
+ if (out_h)
+ fprintf(out_h, "#define CONFIG_%s \"", sym->name);
+ do {
+ l = strcspn(str, "\"\\");
+ if (l) {
+ fwrite(str, l, 1, out);
+ if (out_h)
+ fwrite(str, l, 1, out_h);
+ }
+ str += l;
+ while (*str == '\\' || *str == '"') {
+ fprintf(out, "\\%c", *str);
+ if (out_h)
+ fprintf(out_h, "\\%c", *str);
+ str++;
+ }
+ } while (*str);
+ fputs("\"\n", out);
+ if (out_h) {
+ fputs("\"\n", out_h);
+ /* bbox */
+ fprintf(out_h, "#define ENABLE_%s 1\n", sym->name);
+ fprintf(out_h, "#define USE_%s(...) __VA_ARGS__\n", sym->name);
+ fprintf(out_h, "#define SKIP_%s(...)\n", sym->name);
+ }
+ break;
+ case S_HEX:
+ str = sym_get_string_value(sym);
+ if (str[0] != '0' || (str[1] != 'x' && str[1] != 'X')) {
+ fprintf(out, "CONFIG_%s=%s\n", sym->name, str);
+ if (out_h) {
+ fprintf(out_h, "#define CONFIG_%s 0x%s\n", sym->name, str);
+ /* bbox */
+ fprintf(out_h, "#define ENABLE_%s 1\n", sym->name);
+ fprintf(out_h, "#define USE_%s(...) __VA_ARGS__\n", sym->name);
+ fprintf(out_h, "#define SKIP_%s(...)\n", sym->name);
+ }
+ break;
+ }
+ case S_INT:
+ str = sym_get_string_value(sym);
+ fprintf(out, "CONFIG_%s=%s\n", sym->name, str);
+ if (out_h) {
+ fprintf(out_h, "#define CONFIG_%s %s\n", sym->name, str);
+ /* bbox */
+ fprintf(out_h, "#define ENABLE_%s 1\n", sym->name);
+ fprintf(out_h, "#define USE_%s(...) __VA_ARGS__\n", sym->name);
+ fprintf(out_h, "#define SKIP_%s(...)\n", sym->name);
+ }
+ break;
+ }
+ }
+
+ next:
+ if (menu->list) {
+ menu = menu->list;
+ continue;
+ }
+ if (menu->next)
+ menu = menu->next;
+ else while ((menu = menu->parent)) {
+ if (menu->next) {
+ menu = menu->next;
+ break;
+ }
+ }
+ }
+ fclose(out);
+ if (out_h) {
+ fclose(out_h);
+ rename(".tmpconfig.h", "include/autoconf.h");
+ }
+ if (!name || basename != conf_def_filename) {
+ if (!name)
+ name = conf_def_filename;
+ sprintf(tmpname, "%s.old", name);
+ rename(name, tmpname);
+ }
+ sprintf(tmpname, "%s%s", dirname, basename);
+ if (rename(newname, tmpname))
+ return 1;
+
+ sym_change_count = 0;
+
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/scripts/kconfig/expr.c b/i/pc104/initrd/conf/busybox/scripts/kconfig/expr.c
new file mode 100644
index 0000000..30e4f9d
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/scripts/kconfig/expr.c
@@ -0,0 +1,1099 @@
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define LKC_DIRECT_LINK
+#include "lkc.h"
+
+#define DEBUG_EXPR 0
+
+struct expr *expr_alloc_symbol(struct symbol *sym)
+{
+ struct expr *e = malloc(sizeof(*e));
+ memset(e, 0, sizeof(*e));
+ e->type = E_SYMBOL;
+ e->left.sym = sym;
+ return e;
+}
+
+struct expr *expr_alloc_one(enum expr_type type, struct expr *ce)
+{
+ struct expr *e = malloc(sizeof(*e));
+ memset(e, 0, sizeof(*e));
+ e->type = type;
+ e->left.expr = ce;
+ return e;
+}
+
+struct expr *expr_alloc_two(enum expr_type type, struct expr *e1, struct expr *e2)
+{
+ struct expr *e = malloc(sizeof(*e));
+ memset(e, 0, sizeof(*e));
+ e->type = type;
+ e->left.expr = e1;
+ e->right.expr = e2;
+ return e;
+}
+
+struct expr *expr_alloc_comp(enum expr_type type, struct symbol *s1, struct symbol *s2)
+{
+ struct expr *e = malloc(sizeof(*e));
+ memset(e, 0, sizeof(*e));
+ e->type = type;
+ e->left.sym = s1;
+ e->right.sym = s2;
+ return e;
+}
+
+struct expr *expr_alloc_and(struct expr *e1, struct expr *e2)
+{
+ if (!e1)
+ return e2;
+ return e2 ? expr_alloc_two(E_AND, e1, e2) : e1;
+}
+
+struct expr *expr_alloc_or(struct expr *e1, struct expr *e2)
+{
+ if (!e1)
+ return e2;
+ return e2 ? expr_alloc_two(E_OR, e1, e2) : e1;
+}
+
+struct expr *expr_copy(struct expr *org)
+{
+ struct expr *e;
+
+ if (!org)
+ return NULL;
+
+ e = malloc(sizeof(*org));
+ memcpy(e, org, sizeof(*org));
+ switch (org->type) {
+ case E_SYMBOL:
+ e->left = org->left;
+ break;
+ case E_NOT:
+ e->left.expr = expr_copy(org->left.expr);
+ break;
+ case E_EQUAL:
+ case E_UNEQUAL:
+ e->left.sym = org->left.sym;
+ e->right.sym = org->right.sym;
+ break;
+ case E_AND:
+ case E_OR:
+ case E_CHOICE:
+ e->left.expr = expr_copy(org->left.expr);
+ e->right.expr = expr_copy(org->right.expr);
+ break;
+ default:
+ printf("can't copy type %d\n", e->type);
+ free(e);
+ e = NULL;
+ break;
+ }
+
+ return e;
+}
+
+void expr_free(struct expr *e)
+{
+ if (!e)
+ return;
+
+ switch (e->type) {
+ case E_SYMBOL:
+ break;
+ case E_NOT:
+ expr_free(e->left.expr);
+ return;
+ case E_EQUAL:
+ case E_UNEQUAL:
+ break;
+ case E_OR:
+ case E_AND:
+ expr_free(e->left.expr);
+ expr_free(e->right.expr);
+ break;
+ default:
+ printf("how to free type %d?\n", e->type);
+ break;
+ }
+ free(e);
+}
+
+static int trans_count;
+
+#define e1 (*ep1)
+#define e2 (*ep2)
+
+static void __expr_eliminate_eq(enum expr_type type, struct expr **ep1, struct expr **ep2)
+{
+ if (e1->type == type) {
+ __expr_eliminate_eq(type, &e1->left.expr, &e2);
+ __expr_eliminate_eq(type, &e1->right.expr, &e2);
+ return;
+ }
+ if (e2->type == type) {
+ __expr_eliminate_eq(type, &e1, &e2->left.expr);
+ __expr_eliminate_eq(type, &e1, &e2->right.expr);
+ return;
+ }
+ if (e1->type == E_SYMBOL && e2->type == E_SYMBOL &&
+ e1->left.sym == e2->left.sym && (e1->left.sym->flags & (SYMBOL_YES|SYMBOL_NO)))
+ return;
+ if (!expr_eq(e1, e2))
+ return;
+ trans_count++;
+ expr_free(e1); expr_free(e2);
+ switch (type) {
+ case E_OR:
+ e1 = expr_alloc_symbol(&symbol_no);
+ e2 = expr_alloc_symbol(&symbol_no);
+ break;
+ case E_AND:
+ e1 = expr_alloc_symbol(&symbol_yes);
+ e2 = expr_alloc_symbol(&symbol_yes);
+ break;
+ default:
+ ;
+ }
+}
+
+void expr_eliminate_eq(struct expr **ep1, struct expr **ep2)
+{
+ if (!e1 || !e2)
+ return;
+ switch (e1->type) {
+ case E_OR:
+ case E_AND:
+ __expr_eliminate_eq(e1->type, ep1, ep2);
+ default:
+ ;
+ }
+ if (e1->type != e2->type) switch (e2->type) {
+ case E_OR:
+ case E_AND:
+ __expr_eliminate_eq(e2->type, ep1, ep2);
+ default:
+ ;
+ }
+ e1 = expr_eliminate_yn(e1);
+ e2 = expr_eliminate_yn(e2);
+}
+
+#undef e1
+#undef e2
+
+int expr_eq(struct expr *e1, struct expr *e2)
+{
+ int res, old_count;
+
+ if (e1->type != e2->type)
+ return 0;
+ switch (e1->type) {
+ case E_EQUAL:
+ case E_UNEQUAL:
+ return e1->left.sym == e2->left.sym && e1->right.sym == e2->right.sym;
+ case E_SYMBOL:
+ return e1->left.sym == e2->left.sym;
+ case E_NOT:
+ return expr_eq(e1->left.expr, e2->left.expr);
+ case E_AND:
+ case E_OR:
+ e1 = expr_copy(e1);
+ e2 = expr_copy(e2);
+ old_count = trans_count;
+ expr_eliminate_eq(&e1, &e2);
+ res = (e1->type == E_SYMBOL && e2->type == E_SYMBOL &&
+ e1->left.sym == e2->left.sym);
+ expr_free(e1);
+ expr_free(e2);
+ trans_count = old_count;
+ return res;
+ case E_CHOICE:
+ case E_RANGE:
+ case E_NONE:
+ /* panic */;
+ }
+
+ if (DEBUG_EXPR) {
+ expr_fprint(e1, stdout);
+ printf(" = ");
+ expr_fprint(e2, stdout);
+ printf(" ?\n");
+ }
+
+ return 0;
+}
+
+struct expr *expr_eliminate_yn(struct expr *e)
+{
+ struct expr *tmp;
+
+ if (e) switch (e->type) {
+ case E_AND:
+ e->left.expr = expr_eliminate_yn(e->left.expr);
+ e->right.expr = expr_eliminate_yn(e->right.expr);
+ if (e->left.expr->type == E_SYMBOL) {
+ if (e->left.expr->left.sym == &symbol_no) {
+ expr_free(e->left.expr);
+ expr_free(e->right.expr);
+ e->type = E_SYMBOL;
+ e->left.sym = &symbol_no;
+ e->right.expr = NULL;
+ return e;
+ } else if (e->left.expr->left.sym == &symbol_yes) {
+ free(e->left.expr);
+ tmp = e->right.expr;
+ *e = *(e->right.expr);
+ free(tmp);
+ return e;
+ }
+ }
+ if (e->right.expr->type == E_SYMBOL) {
+ if (e->right.expr->left.sym == &symbol_no) {
+ expr_free(e->left.expr);
+ expr_free(e->right.expr);
+ e->type = E_SYMBOL;
+ e->left.sym = &symbol_no;
+ e->right.expr = NULL;
+ return e;
+ } else if (e->right.expr->left.sym == &symbol_yes) {
+ free(e->right.expr);
+ tmp = e->left.expr;
+ *e = *(e->left.expr);
+ free(tmp);
+ return e;
+ }
+ }
+ break;
+ case E_OR:
+ e->left.expr = expr_eliminate_yn(e->left.expr);
+ e->right.expr = expr_eliminate_yn(e->right.expr);
+ if (e->left.expr->type == E_SYMBOL) {
+ if (e->left.expr->left.sym == &symbol_no) {
+ free(e->left.expr);
+ tmp = e->right.expr;
+ *e = *(e->right.expr);
+ free(tmp);
+ return e;
+ } else if (e->left.expr->left.sym == &symbol_yes) {
+ expr_free(e->left.expr);
+ expr_free(e->right.expr);
+ e->type = E_SYMBOL;
+ e->left.sym = &symbol_yes;
+ e->right.expr = NULL;
+ return e;
+ }
+ }
+ if (e->right.expr->type == E_SYMBOL) {
+ if (e->right.expr->left.sym == &symbol_no) {
+ free(e->right.expr);
+ tmp = e->left.expr;
+ *e = *(e->left.expr);
+ free(tmp);
+ return e;
+ } else if (e->right.expr->left.sym == &symbol_yes) {
+ expr_free(e->left.expr);
+ expr_free(e->right.expr);
+ e->type = E_SYMBOL;
+ e->left.sym = &symbol_yes;
+ e->right.expr = NULL;
+ return e;
+ }
+ }
+ break;
+ default:
+ ;
+ }
+ return e;
+}
+
+/*
+ * bool FOO!=n => FOO
+ */
+struct expr *expr_trans_bool(struct expr *e)
+{
+ if (!e)
+ return NULL;
+ switch (e->type) {
+ case E_AND:
+ case E_OR:
+ case E_NOT:
+ e->left.expr = expr_trans_bool(e->left.expr);
+ e->right.expr = expr_trans_bool(e->right.expr);
+ break;
+ case E_UNEQUAL:
+ // FOO!=n -> FOO
+ if (e->left.sym->type == S_TRISTATE) {
+ if (e->right.sym == &symbol_no) {
+ e->type = E_SYMBOL;
+ e->right.sym = NULL;
+ }
+ }
+ break;
+ default:
+ ;
+ }
+ return e;
+}
+
+/*
+ * e1 || e2 -> ?
+ */
+struct expr *expr_join_or(struct expr *e1, struct expr *e2)
+{
+ struct expr *tmp;
+ struct symbol *sym1, *sym2;
+
+ if (expr_eq(e1, e2))
+ return expr_copy(e1);
+ if (e1->type != E_EQUAL && e1->type != E_UNEQUAL && e1->type != E_SYMBOL && e1->type != E_NOT)
+ return NULL;
+ if (e2->type != E_EQUAL && e2->type != E_UNEQUAL && e2->type != E_SYMBOL && e2->type != E_NOT)
+ return NULL;
+ if (e1->type == E_NOT) {
+ tmp = e1->left.expr;
+ if (tmp->type != E_EQUAL && tmp->type != E_UNEQUAL && tmp->type != E_SYMBOL)
+ return NULL;
+ sym1 = tmp->left.sym;
+ } else
+ sym1 = e1->left.sym;
+ if (e2->type == E_NOT) {
+ if (e2->left.expr->type != E_SYMBOL)
+ return NULL;
+ sym2 = e2->left.expr->left.sym;
+ } else
+ sym2 = e2->left.sym;
+ if (sym1 != sym2)
+ return NULL;
+ if (sym1->type != S_BOOLEAN && sym1->type != S_TRISTATE)
+ return NULL;
+ if (sym1->type == S_TRISTATE) {
+ if (e1->type == E_EQUAL && e2->type == E_EQUAL &&
+ ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_mod) ||
+ (e1->right.sym == &symbol_mod && e2->right.sym == &symbol_yes))) {
+ // (a='y') || (a='m') -> (a!='n')
+ return expr_alloc_comp(E_UNEQUAL, sym1, &symbol_no);
+ }
+ if (e1->type == E_EQUAL && e2->type == E_EQUAL &&
+ ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_no) ||
+ (e1->right.sym == &symbol_no && e2->right.sym == &symbol_yes))) {
+ // (a='y') || (a='n') -> (a!='m')
+ return expr_alloc_comp(E_UNEQUAL, sym1, &symbol_mod);
+ }
+ if (e1->type == E_EQUAL && e2->type == E_EQUAL &&
+ ((e1->right.sym == &symbol_mod && e2->right.sym == &symbol_no) ||
+ (e1->right.sym == &symbol_no && e2->right.sym == &symbol_mod))) {
+ // (a='m') || (a='n') -> (a!='y')
+ return expr_alloc_comp(E_UNEQUAL, sym1, &symbol_yes);
+ }
+ }
+ if (sym1->type == S_BOOLEAN && sym1 == sym2) {
+ if ((e1->type == E_NOT && e1->left.expr->type == E_SYMBOL && e2->type == E_SYMBOL) ||
+ (e2->type == E_NOT && e2->left.expr->type == E_SYMBOL && e1->type == E_SYMBOL))
+ return expr_alloc_symbol(&symbol_yes);
+ }
+
+ if (DEBUG_EXPR) {
+ printf("optimize (");
+ expr_fprint(e1, stdout);
+ printf(") || (");
+ expr_fprint(e2, stdout);
+ printf(")?\n");
+ }
+ return NULL;
+}
+
+struct expr *expr_join_and(struct expr *e1, struct expr *e2)
+{
+ struct expr *tmp;
+ struct symbol *sym1, *sym2;
+
+ if (expr_eq(e1, e2))
+ return expr_copy(e1);
+ if (e1->type != E_EQUAL && e1->type != E_UNEQUAL && e1->type != E_SYMBOL && e1->type != E_NOT)
+ return NULL;
+ if (e2->type != E_EQUAL && e2->type != E_UNEQUAL && e2->type != E_SYMBOL && e2->type != E_NOT)
+ return NULL;
+ if (e1->type == E_NOT) {
+ tmp = e1->left.expr;
+ if (tmp->type != E_EQUAL && tmp->type != E_UNEQUAL && tmp->type != E_SYMBOL)
+ return NULL;
+ sym1 = tmp->left.sym;
+ } else
+ sym1 = e1->left.sym;
+ if (e2->type == E_NOT) {
+ if (e2->left.expr->type != E_SYMBOL)
+ return NULL;
+ sym2 = e2->left.expr->left.sym;
+ } else
+ sym2 = e2->left.sym;
+ if (sym1 != sym2)
+ return NULL;
+ if (sym1->type != S_BOOLEAN && sym1->type != S_TRISTATE)
+ return NULL;
+
+ if ((e1->type == E_SYMBOL && e2->type == E_EQUAL && e2->right.sym == &symbol_yes) ||
+ (e2->type == E_SYMBOL && e1->type == E_EQUAL && e1->right.sym == &symbol_yes))
+ // (a) && (a='y') -> (a='y')
+ return expr_alloc_comp(E_EQUAL, sym1, &symbol_yes);
+
+ if ((e1->type == E_SYMBOL && e2->type == E_UNEQUAL && e2->right.sym == &symbol_no) ||
+ (e2->type == E_SYMBOL && e1->type == E_UNEQUAL && e1->right.sym == &symbol_no))
+ // (a) && (a!='n') -> (a)
+ return expr_alloc_symbol(sym1);
+
+ if ((e1->type == E_SYMBOL && e2->type == E_UNEQUAL && e2->right.sym == &symbol_mod) ||
+ (e2->type == E_SYMBOL && e1->type == E_UNEQUAL && e1->right.sym == &symbol_mod))
+ // (a) && (a!='m') -> (a='y')
+ return expr_alloc_comp(E_EQUAL, sym1, &symbol_yes);
+
+ if (sym1->type == S_TRISTATE) {
+ if (e1->type == E_EQUAL && e2->type == E_UNEQUAL) {
+ // (a='b') && (a!='c') -> 'b'='c' ? 'n' : a='b'
+ sym2 = e1->right.sym;
+ if ((e2->right.sym->flags & SYMBOL_CONST) && (sym2->flags & SYMBOL_CONST))
+ return sym2 != e2->right.sym ? expr_alloc_comp(E_EQUAL, sym1, sym2)
+ : expr_alloc_symbol(&symbol_no);
+ }
+ if (e1->type == E_UNEQUAL && e2->type == E_EQUAL) {
+ // (a='b') && (a!='c') -> 'b'='c' ? 'n' : a='b'
+ sym2 = e2->right.sym;
+ if ((e1->right.sym->flags & SYMBOL_CONST) && (sym2->flags & SYMBOL_CONST))
+ return sym2 != e1->right.sym ? expr_alloc_comp(E_EQUAL, sym1, sym2)
+ : expr_alloc_symbol(&symbol_no);
+ }
+ if (e1->type == E_UNEQUAL && e2->type == E_UNEQUAL &&
+ ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_no) ||
+ (e1->right.sym == &symbol_no && e2->right.sym == &symbol_yes)))
+ // (a!='y') && (a!='n') -> (a='m')
+ return expr_alloc_comp(E_EQUAL, sym1, &symbol_mod);
+
+ if (e1->type == E_UNEQUAL && e2->type == E_UNEQUAL &&
+ ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_mod) ||
+ (e1->right.sym == &symbol_mod && e2->right.sym == &symbol_yes)))
+ // (a!='y') && (a!='m') -> (a='n')
+ return expr_alloc_comp(E_EQUAL, sym1, &symbol_no);
+
+ if (e1->type == E_UNEQUAL && e2->type == E_UNEQUAL &&
+ ((e1->right.sym == &symbol_mod && e2->right.sym == &symbol_no) ||
+ (e1->right.sym == &symbol_no && e2->right.sym == &symbol_mod)))
+ // (a!='m') && (a!='n') -> (a='m')
+ return expr_alloc_comp(E_EQUAL, sym1, &symbol_yes);
+
+ if ((e1->type == E_SYMBOL && e2->type == E_EQUAL && e2->right.sym == &symbol_mod) ||
+ (e2->type == E_SYMBOL && e1->type == E_EQUAL && e1->right.sym == &symbol_mod) ||
+ (e1->type == E_SYMBOL && e2->type == E_UNEQUAL && e2->right.sym == &symbol_yes) ||
+ (e2->type == E_SYMBOL && e1->type == E_UNEQUAL && e1->right.sym == &symbol_yes))
+ return NULL;
+ }
+
+ if (DEBUG_EXPR) {
+ printf("optimize (");
+ expr_fprint(e1, stdout);
+ printf(") && (");
+ expr_fprint(e2, stdout);
+ printf(")?\n");
+ }
+ return NULL;
+}
+
+static void expr_eliminate_dups1(enum expr_type type, struct expr **ep1, struct expr **ep2)
+{
+#define e1 (*ep1)
+#define e2 (*ep2)
+ struct expr *tmp;
+
+ if (e1->type == type) {
+ expr_eliminate_dups1(type, &e1->left.expr, &e2);
+ expr_eliminate_dups1(type, &e1->right.expr, &e2);
+ return;
+ }
+ if (e2->type == type) {
+ expr_eliminate_dups1(type, &e1, &e2->left.expr);
+ expr_eliminate_dups1(type, &e1, &e2->right.expr);
+ return;
+ }
+ if (e1 == e2)
+ return;
+
+ switch (e1->type) {
+ case E_OR: case E_AND:
+ expr_eliminate_dups1(e1->type, &e1, &e1);
+ default:
+ ;
+ }
+
+ switch (type) {
+ case E_OR:
+ tmp = expr_join_or(e1, e2);
+ if (tmp) {
+ expr_free(e1); expr_free(e2);
+ e1 = expr_alloc_symbol(&symbol_no);
+ e2 = tmp;
+ trans_count++;
+ }
+ break;
+ case E_AND:
+ tmp = expr_join_and(e1, e2);
+ if (tmp) {
+ expr_free(e1); expr_free(e2);
+ e1 = expr_alloc_symbol(&symbol_yes);
+ e2 = tmp;
+ trans_count++;
+ }
+ break;
+ default:
+ ;
+ }
+#undef e1
+#undef e2
+}
+
+static void expr_eliminate_dups2(enum expr_type type, struct expr **ep1, struct expr **ep2)
+{
+#define e1 (*ep1)
+#define e2 (*ep2)
+ struct expr *tmp, *tmp1, *tmp2;
+
+ if (e1->type == type) {
+ expr_eliminate_dups2(type, &e1->left.expr, &e2);
+ expr_eliminate_dups2(type, &e1->right.expr, &e2);
+ return;
+ }
+ if (e2->type == type) {
+ expr_eliminate_dups2(type, &e1, &e2->left.expr);
+ expr_eliminate_dups2(type, &e1, &e2->right.expr);
+ }
+ if (e1 == e2)
+ return;
+
+ switch (e1->type) {
+ case E_OR:
+ expr_eliminate_dups2(e1->type, &e1, &e1);
+ // (FOO || BAR) && (!FOO && !BAR) -> n
+ tmp1 = expr_transform(expr_alloc_one(E_NOT, expr_copy(e1)));
+ tmp2 = expr_copy(e2);
+ tmp = expr_extract_eq_and(&tmp1, &tmp2);
+ if (expr_is_yes(tmp1)) {
+ expr_free(e1);
+ e1 = expr_alloc_symbol(&symbol_no);
+ trans_count++;
+ }
+ expr_free(tmp2);
+ expr_free(tmp1);
+ expr_free(tmp);
+ break;
+ case E_AND:
+ expr_eliminate_dups2(e1->type, &e1, &e1);
+ // (FOO && BAR) || (!FOO || !BAR) -> y
+ tmp1 = expr_transform(expr_alloc_one(E_NOT, expr_copy(e1)));
+ tmp2 = expr_copy(e2);
+ tmp = expr_extract_eq_or(&tmp1, &tmp2);
+ if (expr_is_no(tmp1)) {
+ expr_free(e1);
+ e1 = expr_alloc_symbol(&symbol_yes);
+ trans_count++;
+ }
+ expr_free(tmp2);
+ expr_free(tmp1);
+ expr_free(tmp);
+ break;
+ default:
+ ;
+ }
+#undef e1
+#undef e2
+}
+
+struct expr *expr_eliminate_dups(struct expr *e)
+{
+ int oldcount;
+ if (!e)
+ return e;
+
+ oldcount = trans_count;
+ while (1) {
+ trans_count = 0;
+ switch (e->type) {
+ case E_OR: case E_AND:
+ expr_eliminate_dups1(e->type, &e, &e);
+ expr_eliminate_dups2(e->type, &e, &e);
+ default:
+ ;
+ }
+ if (!trans_count)
+ break;
+ e = expr_eliminate_yn(e);
+ }
+ trans_count = oldcount;
+ return e;
+}
+
+struct expr *expr_transform(struct expr *e)
+{
+ struct expr *tmp;
+
+ if (!e)
+ return NULL;
+ switch (e->type) {
+ case E_EQUAL:
+ case E_UNEQUAL:
+ case E_SYMBOL:
+ case E_CHOICE:
+ break;
+ default:
+ e->left.expr = expr_transform(e->left.expr);
+ e->right.expr = expr_transform(e->right.expr);
+ }
+
+ switch (e->type) {
+ case E_EQUAL:
+ if (e->left.sym->type != S_BOOLEAN)
+ break;
+ if (e->right.sym == &symbol_no) {
+ e->type = E_NOT;
+ e->left.expr = expr_alloc_symbol(e->left.sym);
+ e->right.sym = NULL;
+ break;
+ }
+ if (e->right.sym == &symbol_mod) {
+ printf("boolean symbol %s tested for 'm'? test forced to 'n'\n", e->left.sym->name);
+ e->type = E_SYMBOL;
+ e->left.sym = &symbol_no;
+ e->right.sym = NULL;
+ break;
+ }
+ if (e->right.sym == &symbol_yes) {
+ e->type = E_SYMBOL;
+ e->right.sym = NULL;
+ break;
+ }
+ break;
+ case E_UNEQUAL:
+ if (e->left.sym->type != S_BOOLEAN)
+ break;
+ if (e->right.sym == &symbol_no) {
+ e->type = E_SYMBOL;
+ e->right.sym = NULL;
+ break;
+ }
+ if (e->right.sym == &symbol_mod) {
+ printf("boolean symbol %s tested for 'm'? test forced to 'y'\n", e->left.sym->name);
+ e->type = E_SYMBOL;
+ e->left.sym = &symbol_yes;
+ e->right.sym = NULL;
+ break;
+ }
+ if (e->right.sym == &symbol_yes) {
+ e->type = E_NOT;
+ e->left.expr = expr_alloc_symbol(e->left.sym);
+ e->right.sym = NULL;
+ break;
+ }
+ break;
+ case E_NOT:
+ switch (e->left.expr->type) {
+ case E_NOT:
+ // !!a -> a
+ tmp = e->left.expr->left.expr;
+ free(e->left.expr);
+ free(e);
+ e = tmp;
+ e = expr_transform(e);
+ break;
+ case E_EQUAL:
+ case E_UNEQUAL:
+ // !a='x' -> a!='x'
+ tmp = e->left.expr;
+ free(e);
+ e = tmp;
+ e->type = e->type == E_EQUAL ? E_UNEQUAL : E_EQUAL;
+ break;
+ case E_OR:
+ // !(a || b) -> !a && !b
+ tmp = e->left.expr;
+ e->type = E_AND;
+ e->right.expr = expr_alloc_one(E_NOT, tmp->right.expr);
+ tmp->type = E_NOT;
+ tmp->right.expr = NULL;
+ e = expr_transform(e);
+ break;
+ case E_AND:
+ // !(a && b) -> !a || !b
+ tmp = e->left.expr;
+ e->type = E_OR;
+ e->right.expr = expr_alloc_one(E_NOT, tmp->right.expr);
+ tmp->type = E_NOT;
+ tmp->right.expr = NULL;
+ e = expr_transform(e);
+ break;
+ case E_SYMBOL:
+ if (e->left.expr->left.sym == &symbol_yes) {
+ // !'y' -> 'n'
+ tmp = e->left.expr;
+ free(e);
+ e = tmp;
+ e->type = E_SYMBOL;
+ e->left.sym = &symbol_no;
+ break;
+ }
+ if (e->left.expr->left.sym == &symbol_mod) {
+ // !'m' -> 'm'
+ tmp = e->left.expr;
+ free(e);
+ e = tmp;
+ e->type = E_SYMBOL;
+ e->left.sym = &symbol_mod;
+ break;
+ }
+ if (e->left.expr->left.sym == &symbol_no) {
+ // !'n' -> 'y'
+ tmp = e->left.expr;
+ free(e);
+ e = tmp;
+ e->type = E_SYMBOL;
+ e->left.sym = &symbol_yes;
+ break;
+ }
+ break;
+ default:
+ ;
+ }
+ break;
+ default:
+ ;
+ }
+ return e;
+}
+
+int expr_contains_symbol(struct expr *dep, struct symbol *sym)
+{
+ if (!dep)
+ return 0;
+
+ switch (dep->type) {
+ case E_AND:
+ case E_OR:
+ return expr_contains_symbol(dep->left.expr, sym) ||
+ expr_contains_symbol(dep->right.expr, sym);
+ case E_SYMBOL:
+ return dep->left.sym == sym;
+ case E_EQUAL:
+ case E_UNEQUAL:
+ return dep->left.sym == sym ||
+ dep->right.sym == sym;
+ case E_NOT:
+ return expr_contains_symbol(dep->left.expr, sym);
+ default:
+ ;
+ }
+ return 0;
+}
+
+bool expr_depends_symbol(struct expr *dep, struct symbol *sym)
+{
+ if (!dep)
+ return false;
+
+ switch (dep->type) {
+ case E_AND:
+ return expr_depends_symbol(dep->left.expr, sym) ||
+ expr_depends_symbol(dep->right.expr, sym);
+ case E_SYMBOL:
+ return dep->left.sym == sym;
+ case E_EQUAL:
+ if (dep->left.sym == sym) {
+ if (dep->right.sym == &symbol_yes || dep->right.sym == &symbol_mod)
+ return true;
+ }
+ break;
+ case E_UNEQUAL:
+ if (dep->left.sym == sym) {
+ if (dep->right.sym == &symbol_no)
+ return true;
+ }
+ break;
+ default:
+ ;
+ }
+ return false;
+}
+
+struct expr *expr_extract_eq_and(struct expr **ep1, struct expr **ep2)
+{
+ struct expr *tmp = NULL;
+ expr_extract_eq(E_AND, &tmp, ep1, ep2);
+ if (tmp) {
+ *ep1 = expr_eliminate_yn(*ep1);
+ *ep2 = expr_eliminate_yn(*ep2);
+ }
+ return tmp;
+}
+
+struct expr *expr_extract_eq_or(struct expr **ep1, struct expr **ep2)
+{
+ struct expr *tmp = NULL;
+ expr_extract_eq(E_OR, &tmp, ep1, ep2);
+ if (tmp) {
+ *ep1 = expr_eliminate_yn(*ep1);
+ *ep2 = expr_eliminate_yn(*ep2);
+ }
+ return tmp;
+}
+
+void expr_extract_eq(enum expr_type type, struct expr **ep, struct expr **ep1, struct expr **ep2)
+{
+#define e1 (*ep1)
+#define e2 (*ep2)
+ if (e1->type == type) {
+ expr_extract_eq(type, ep, &e1->left.expr, &e2);
+ expr_extract_eq(type, ep, &e1->right.expr, &e2);
+ return;
+ }
+ if (e2->type == type) {
+ expr_extract_eq(type, ep, ep1, &e2->left.expr);
+ expr_extract_eq(type, ep, ep1, &e2->right.expr);
+ return;
+ }
+ if (expr_eq(e1, e2)) {
+ *ep = *ep ? expr_alloc_two(type, *ep, e1) : e1;
+ expr_free(e2);
+ if (type == E_AND) {
+ e1 = expr_alloc_symbol(&symbol_yes);
+ e2 = expr_alloc_symbol(&symbol_yes);
+ } else if (type == E_OR) {
+ e1 = expr_alloc_symbol(&symbol_no);
+ e2 = expr_alloc_symbol(&symbol_no);
+ }
+ }
+#undef e1
+#undef e2
+}
+
+struct expr *expr_trans_compare(struct expr *e, enum expr_type type, struct symbol *sym)
+{
+ struct expr *e1, *e2;
+
+ if (!e) {
+ e = expr_alloc_symbol(sym);
+ if (type == E_UNEQUAL)
+ e = expr_alloc_one(E_NOT, e);
+ return e;
+ }
+ switch (e->type) {
+ case E_AND:
+ e1 = expr_trans_compare(e->left.expr, E_EQUAL, sym);
+ e2 = expr_trans_compare(e->right.expr, E_EQUAL, sym);
+ if (sym == &symbol_yes)
+ e = expr_alloc_two(E_AND, e1, e2);
+ if (sym == &symbol_no)
+ e = expr_alloc_two(E_OR, e1, e2);
+ if (type == E_UNEQUAL)
+ e = expr_alloc_one(E_NOT, e);
+ return e;
+ case E_OR:
+ e1 = expr_trans_compare(e->left.expr, E_EQUAL, sym);
+ e2 = expr_trans_compare(e->right.expr, E_EQUAL, sym);
+ if (sym == &symbol_yes)
+ e = expr_alloc_two(E_OR, e1, e2);
+ if (sym == &symbol_no)
+ e = expr_alloc_two(E_AND, e1, e2);
+ if (type == E_UNEQUAL)
+ e = expr_alloc_one(E_NOT, e);
+ return e;
+ case E_NOT:
+ return expr_trans_compare(e->left.expr, type == E_EQUAL ? E_UNEQUAL : E_EQUAL, sym);
+ case E_UNEQUAL:
+ case E_EQUAL:
+ if (type == E_EQUAL) {
+ if (sym == &symbol_yes)
+ return expr_copy(e);
+ if (sym == &symbol_mod)
+ return expr_alloc_symbol(&symbol_no);
+ if (sym == &symbol_no)
+ return expr_alloc_one(E_NOT, expr_copy(e));
+ } else {
+ if (sym == &symbol_yes)
+ return expr_alloc_one(E_NOT, expr_copy(e));
+ if (sym == &symbol_mod)
+ return expr_alloc_symbol(&symbol_yes);
+ if (sym == &symbol_no)
+ return expr_copy(e);
+ }
+ break;
+ case E_SYMBOL:
+ return expr_alloc_comp(type, e->left.sym, sym);
+ case E_CHOICE:
+ case E_RANGE:
+ case E_NONE:
+ /* panic */;
+ }
+ return NULL;
+}
+
+tristate expr_calc_value(struct expr *e)
+{
+ tristate val1, val2;
+ const char *str1, *str2;
+
+ if (!e)
+ return yes;
+
+ switch (e->type) {
+ case E_SYMBOL:
+ sym_calc_value(e->left.sym);
+ return e->left.sym->curr.tri;
+ case E_AND:
+ val1 = expr_calc_value(e->left.expr);
+ val2 = expr_calc_value(e->right.expr);
+ return E_AND(val1, val2);
+ case E_OR:
+ val1 = expr_calc_value(e->left.expr);
+ val2 = expr_calc_value(e->right.expr);
+ return E_OR(val1, val2);
+ case E_NOT:
+ val1 = expr_calc_value(e->left.expr);
+ return E_NOT(val1);
+ case E_EQUAL:
+ sym_calc_value(e->left.sym);
+ sym_calc_value(e->right.sym);
+ str1 = sym_get_string_value(e->left.sym);
+ str2 = sym_get_string_value(e->right.sym);
+ return !strcmp(str1, str2) ? yes : no;
+ case E_UNEQUAL:
+ sym_calc_value(e->left.sym);
+ sym_calc_value(e->right.sym);
+ str1 = sym_get_string_value(e->left.sym);
+ str2 = sym_get_string_value(e->right.sym);
+ return !strcmp(str1, str2) ? no : yes;
+ default:
+ printf("expr_calc_value: %d?\n", e->type);
+ return no;
+ }
+}
+
+int expr_compare_type(enum expr_type t1, enum expr_type t2)
+{
+#if 0
+ return 1;
+#else
+ if (t1 == t2)
+ return 0;
+ switch (t1) {
+ case E_EQUAL:
+ case E_UNEQUAL:
+ if (t2 == E_NOT)
+ return 1;
+ case E_NOT:
+ if (t2 == E_AND)
+ return 1;
+ case E_AND:
+ if (t2 == E_OR)
+ return 1;
+ case E_OR:
+ if (t2 == E_CHOICE)
+ return 1;
+ case E_CHOICE:
+ if (t2 == 0)
+ return 1;
+ default:
+ return -1;
+ }
+ printf("[%dgt%d?]", t1, t2);
+ return 0;
+#endif
+}
+
+void expr_print(struct expr *e, void (*fn)(void *, const char *), void *data, int prevtoken)
+{
+ if (!e) {
+ fn(data, "y");
+ return;
+ }
+
+ if (expr_compare_type(prevtoken, e->type) > 0)
+ fn(data, "(");
+ switch (e->type) {
+ case E_SYMBOL:
+ if (e->left.sym->name)
+ fn(data, e->left.sym->name);
+ else
+ fn(data, "<choice>");
+ break;
+ case E_NOT:
+ fn(data, "!");
+ expr_print(e->left.expr, fn, data, E_NOT);
+ break;
+ case E_EQUAL:
+ fn(data, e->left.sym->name);
+ fn(data, "=");
+ fn(data, e->right.sym->name);
+ break;
+ case E_UNEQUAL:
+ fn(data, e->left.sym->name);
+ fn(data, "!=");
+ fn(data, e->right.sym->name);
+ break;
+ case E_OR:
+ expr_print(e->left.expr, fn, data, E_OR);
+ fn(data, " || ");
+ expr_print(e->right.expr, fn, data, E_OR);
+ break;
+ case E_AND:
+ expr_print(e->left.expr, fn, data, E_AND);
+ fn(data, " && ");
+ expr_print(e->right.expr, fn, data, E_AND);
+ break;
+ case E_CHOICE:
+ fn(data, e->right.sym->name);
+ if (e->left.expr) {
+ fn(data, " ^ ");
+ expr_print(e->left.expr, fn, data, E_CHOICE);
+ }
+ break;
+ case E_RANGE:
+ fn(data, "[");
+ fn(data, e->left.sym->name);
+ fn(data, " ");
+ fn(data, e->right.sym->name);
+ fn(data, "]");
+ break;
+ default:
+ {
+ char buf[32];
+ sprintf(buf, "<unknown type %d>", e->type);
+ fn(data, buf);
+ break;
+ }
+ }
+ if (expr_compare_type(prevtoken, e->type) > 0)
+ fn(data, ")");
+}
+
+static void expr_print_file_helper(void *data, const char *str)
+{
+ fwrite(str, strlen(str), 1, data);
+}
+
+void expr_fprint(struct expr *e, FILE *out)
+{
+ expr_print(e, expr_print_file_helper, out, E_NONE);
+}
+
+static void expr_print_gstr_helper(void *data, const char *str)
+{
+ str_append((struct gstr*)data, str);
+}
+
+void expr_gstr_print(struct expr *e, struct gstr *gs)
+{
+ expr_print(e, expr_print_gstr_helper, gs, E_NONE);
+}
diff --git a/i/pc104/initrd/conf/busybox/scripts/kconfig/expr.h b/i/pc104/initrd/conf/busybox/scripts/kconfig/expr.h
new file mode 100644
index 0000000..1b36ef1
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/scripts/kconfig/expr.h
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#ifndef EXPR_H
+#define EXPR_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdio.h>
+#ifndef __cplusplus
+#include <stdbool.h>
+#endif
+
+struct file {
+ struct file *next;
+ struct file *parent;
+ char *name;
+ int lineno;
+ int flags;
+};
+
+#define FILE_BUSY 0x0001
+#define FILE_SCANNED 0x0002
+#define FILE_PRINTED 0x0004
+
+typedef enum tristate {
+ no, mod, yes
+} tristate;
+
+enum expr_type {
+ E_NONE, E_OR, E_AND, E_NOT, E_EQUAL, E_UNEQUAL, E_CHOICE, E_SYMBOL, E_RANGE
+};
+
+union expr_data {
+ struct expr *expr;
+ struct symbol *sym;
+};
+
+struct expr {
+ enum expr_type type;
+ union expr_data left, right;
+};
+
+#define E_OR(dep1, dep2) (((dep1)>(dep2))?(dep1):(dep2))
+#define E_AND(dep1, dep2) (((dep1)<(dep2))?(dep1):(dep2))
+#define E_NOT(dep) (2-(dep))
+
+struct expr_value {
+ struct expr *expr;
+ tristate tri;
+};
+
+struct symbol_value {
+ void *val;
+ tristate tri;
+};
+
+enum symbol_type {
+ S_UNKNOWN, S_BOOLEAN, S_TRISTATE, S_INT, S_HEX, S_STRING, S_OTHER
+};
+
+struct symbol {
+ struct symbol *next;
+ char *name;
+ char *help;
+ enum symbol_type type;
+ struct symbol_value curr, user;
+ tristate visible;
+ int flags;
+ struct property *prop;
+ struct expr *dep, *dep2;
+ struct expr_value rev_dep;
+};
+
+#define for_all_symbols(i, sym) for (i = 0; i < 257; i++) for (sym = symbol_hash[i]; sym; sym = sym->next) if (sym->type != S_OTHER)
+
+#define SYMBOL_YES 0x0001
+#define SYMBOL_MOD 0x0002
+#define SYMBOL_NO 0x0004
+#define SYMBOL_CONST 0x0007
+#define SYMBOL_CHECK 0x0008
+#define SYMBOL_CHOICE 0x0010
+#define SYMBOL_CHOICEVAL 0x0020
+#define SYMBOL_PRINTED 0x0040
+#define SYMBOL_VALID 0x0080
+#define SYMBOL_OPTIONAL 0x0100
+#define SYMBOL_WRITE 0x0200
+#define SYMBOL_CHANGED 0x0400
+#define SYMBOL_NEW 0x0800
+#define SYMBOL_AUTO 0x1000
+#define SYMBOL_CHECKED 0x2000
+#define SYMBOL_WARNED 0x8000
+
+#define SYMBOL_MAXLENGTH 256
+#define SYMBOL_HASHSIZE 257
+#define SYMBOL_HASHMASK 0xff
+
+enum prop_type {
+ P_UNKNOWN, P_PROMPT, P_COMMENT, P_MENU, P_DEFAULT, P_CHOICE, P_SELECT, P_RANGE
+};
+
+struct property {
+ struct property *next;
+ struct symbol *sym;
+ enum prop_type type;
+ const char *text;
+ struct expr_value visible;
+ struct expr *expr;
+ struct menu *menu;
+ struct file *file;
+ int lineno;
+};
+
+#define for_all_properties(sym, st, tok) \
+ for (st = sym->prop; st; st = st->next) \
+ if (st->type == (tok))
+#define for_all_defaults(sym, st) for_all_properties(sym, st, P_DEFAULT)
+#define for_all_choices(sym, st) for_all_properties(sym, st, P_CHOICE)
+#define for_all_prompts(sym, st) \
+ for (st = sym->prop; st; st = st->next) \
+ if (st->text)
+
+struct menu {
+ struct menu *next;
+ struct menu *parent;
+ struct menu *list;
+ struct symbol *sym;
+ struct property *prompt;
+ struct expr *dep;
+ unsigned int flags;
+ //char *help;
+ struct file *file;
+ int lineno;
+ void *data;
+};
+
+#define MENU_CHANGED 0x0001
+#define MENU_ROOT 0x0002
+
+#ifndef SWIG
+
+extern struct file *file_list;
+extern struct file *current_file;
+struct file *lookup_file(const char *name);
+
+extern struct symbol symbol_yes, symbol_no, symbol_mod;
+extern struct symbol *modules_sym;
+extern int cdebug;
+struct expr *expr_alloc_symbol(struct symbol *sym);
+struct expr *expr_alloc_one(enum expr_type type, struct expr *ce);
+struct expr *expr_alloc_two(enum expr_type type, struct expr *e1, struct expr *e2);
+struct expr *expr_alloc_comp(enum expr_type type, struct symbol *s1, struct symbol *s2);
+struct expr *expr_alloc_and(struct expr *e1, struct expr *e2);
+struct expr *expr_alloc_or(struct expr *e1, struct expr *e2);
+struct expr *expr_copy(struct expr *org);
+void expr_free(struct expr *e);
+int expr_eq(struct expr *e1, struct expr *e2);
+void expr_eliminate_eq(struct expr **ep1, struct expr **ep2);
+tristate expr_calc_value(struct expr *e);
+struct expr *expr_eliminate_yn(struct expr *e);
+struct expr *expr_trans_bool(struct expr *e);
+struct expr *expr_eliminate_dups(struct expr *e);
+struct expr *expr_transform(struct expr *e);
+int expr_contains_symbol(struct expr *dep, struct symbol *sym);
+bool expr_depends_symbol(struct expr *dep, struct symbol *sym);
+struct expr *expr_extract_eq_and(struct expr **ep1, struct expr **ep2);
+struct expr *expr_extract_eq_or(struct expr **ep1, struct expr **ep2);
+void expr_extract_eq(enum expr_type type, struct expr **ep, struct expr **ep1, struct expr **ep2);
+struct expr *expr_trans_compare(struct expr *e, enum expr_type type, struct symbol *sym);
+
+void expr_fprint(struct expr *e, FILE *out);
+struct gstr; /* forward */
+void expr_gstr_print(struct expr *e, struct gstr *gs);
+
+static inline int expr_is_yes(struct expr *e)
+{
+ return !e || (e->type == E_SYMBOL && e->left.sym == &symbol_yes);
+}
+
+static inline int expr_is_no(struct expr *e)
+{
+ return e && (e->type == E_SYMBOL && e->left.sym == &symbol_no);
+}
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* EXPR_H */
diff --git a/i/pc104/initrd/conf/busybox/scripts/kconfig/gconf.c b/i/pc104/initrd/conf/busybox/scripts/kconfig/gconf.c
new file mode 100644
index 0000000..6d37299
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/scripts/kconfig/gconf.c
@@ -0,0 +1,1645 @@
+/* Hey EMACS -*- linux-c -*- */
+/*
+ *
+ * Copyright (C) 2002-2003 Romain Lievin <roms@tilp.info>
+ * Released under the terms of the GNU GPL v2.0.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "lkc.h"
+#include "images.c"
+
+#include <glade/glade.h>
+#include <gtk/gtk.h>
+#include <glib.h>
+#include <gdk/gdkkeysyms.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+#include <stdlib.h>
+
+//#define DEBUG
+
+enum {
+ SINGLE_VIEW, SPLIT_VIEW, FULL_VIEW
+};
+
+static gint view_mode = FULL_VIEW;
+static gboolean show_name = TRUE;
+static gboolean show_range = TRUE;
+static gboolean show_value = TRUE;
+static gboolean show_all = FALSE;
+static gboolean show_debug = FALSE;
+static gboolean resizeable = FALSE;
+
+static gboolean config_changed = FALSE;
+
+static char nohelp_text[] =
+ N_("Sorry, no help available for this option yet.\n");
+
+GtkWidget *main_wnd = NULL;
+GtkWidget *tree1_w = NULL; // left frame
+GtkWidget *tree2_w = NULL; // right frame
+GtkWidget *text_w = NULL;
+GtkWidget *hpaned = NULL;
+GtkWidget *vpaned = NULL;
+GtkWidget *back_btn = NULL;
+
+GtkTextTag *tag1, *tag2;
+GdkColor color;
+
+GtkTreeStore *tree1, *tree2, *tree;
+GtkTreeModel *model1, *model2;
+static GtkTreeIter *parents[256];
+static gint indent;
+
+static struct menu *current; // current node for SINGLE view
+static struct menu *browsed; // browsed node for SPLIT view
+
+enum {
+ COL_OPTION, COL_NAME, COL_NO, COL_MOD, COL_YES, COL_VALUE,
+ COL_MENU, COL_COLOR, COL_EDIT, COL_PIXBUF,
+ COL_PIXVIS, COL_BTNVIS, COL_BTNACT, COL_BTNINC, COL_BTNRAD,
+ COL_NUMBER
+};
+
+static void display_list(void);
+static void display_tree(struct menu *menu);
+static void display_tree_part(void);
+static void update_tree(struct menu *src, GtkTreeIter * dst);
+static void set_node(GtkTreeIter * node, struct menu *menu, gchar ** row);
+static gchar **fill_row(struct menu *menu);
+
+
+/* Helping/Debugging Functions */
+
+
+const char *dbg_print_stype(int val)
+{
+ static char buf[256];
+
+ memset(buf, 0, 256);
+
+ if (val == S_UNKNOWN)
+ strcpy(buf, "unknown");
+ if (val == S_BOOLEAN)
+ strcpy(buf, "boolean");
+ if (val == S_TRISTATE)
+ strcpy(buf, "tristate");
+ if (val == S_INT)
+ strcpy(buf, "int");
+ if (val == S_HEX)
+ strcpy(buf, "hex");
+ if (val == S_STRING)
+ strcpy(buf, "string");
+ if (val == S_OTHER)
+ strcpy(buf, "other");
+
+#ifdef DEBUG
+ printf("%s", buf);
+#endif
+
+ return buf;
+}
+
+const char *dbg_print_flags(int val)
+{
+ static char buf[256];
+
+ memset(buf, 0, 256);
+
+ if (val & SYMBOL_YES)
+ strcat(buf, "yes/");
+ if (val & SYMBOL_MOD)
+ strcat(buf, "mod/");
+ if (val & SYMBOL_NO)
+ strcat(buf, "no/");
+ if (val & SYMBOL_CONST)
+ strcat(buf, "const/");
+ if (val & SYMBOL_CHECK)
+ strcat(buf, "check/");
+ if (val & SYMBOL_CHOICE)
+ strcat(buf, "choice/");
+ if (val & SYMBOL_CHOICEVAL)
+ strcat(buf, "choiceval/");
+ if (val & SYMBOL_PRINTED)
+ strcat(buf, "printed/");
+ if (val & SYMBOL_VALID)
+ strcat(buf, "valid/");
+ if (val & SYMBOL_OPTIONAL)
+ strcat(buf, "optional/");
+ if (val & SYMBOL_WRITE)
+ strcat(buf, "write/");
+ if (val & SYMBOL_CHANGED)
+ strcat(buf, "changed/");
+ if (val & SYMBOL_NEW)
+ strcat(buf, "new/");
+ if (val & SYMBOL_AUTO)
+ strcat(buf, "auto/");
+
+ buf[strlen(buf) - 1] = '\0';
+#ifdef DEBUG
+ printf("%s", buf);
+#endif
+
+ return buf;
+}
+
+const char *dbg_print_ptype(int val)
+{
+ static char buf[256];
+
+ memset(buf, 0, 256);
+
+ if (val == P_UNKNOWN)
+ strcpy(buf, "unknown");
+ if (val == P_PROMPT)
+ strcpy(buf, "prompt");
+ if (val == P_COMMENT)
+ strcpy(buf, "comment");
+ if (val == P_MENU)
+ strcpy(buf, "menu");
+ if (val == P_DEFAULT)
+ strcpy(buf, "default");
+ if (val == P_CHOICE)
+ strcpy(buf, "choice");
+
+#ifdef DEBUG
+ printf("%s", buf);
+#endif
+
+ return buf;
+}
+
+
+void replace_button_icon(GladeXML * xml, GdkDrawable * window,
+ GtkStyle * style, gchar * btn_name, gchar ** xpm)
+{
+ GdkPixmap *pixmap;
+ GdkBitmap *mask;
+ GtkToolButton *button;
+ GtkWidget *image;
+
+ pixmap = gdk_pixmap_create_from_xpm_d(window, &mask,
+ &style->bg[GTK_STATE_NORMAL],
+ xpm);
+
+ button = GTK_TOOL_BUTTON(glade_xml_get_widget(xml, btn_name));
+ image = gtk_image_new_from_pixmap(pixmap, mask);
+ gtk_widget_show(image);
+ gtk_tool_button_set_icon_widget(button, image);
+}
+
+/* Main Window Initialization */
+void init_main_window(const gchar * glade_file)
+{
+ GladeXML *xml;
+ GtkWidget *widget;
+ GtkTextBuffer *txtbuf;
+ char title[256];
+ GtkStyle *style;
+
+ xml = glade_xml_new(glade_file, "window1", NULL);
+ if (!xml)
+ g_error(_("GUI loading failed !\n"));
+ glade_xml_signal_autoconnect(xml);
+
+ main_wnd = glade_xml_get_widget(xml, "window1");
+ hpaned = glade_xml_get_widget(xml, "hpaned1");
+ vpaned = glade_xml_get_widget(xml, "vpaned1");
+ tree1_w = glade_xml_get_widget(xml, "treeview1");
+ tree2_w = glade_xml_get_widget(xml, "treeview2");
+ text_w = glade_xml_get_widget(xml, "textview3");
+
+ back_btn = glade_xml_get_widget(xml, "button1");
+ gtk_widget_set_sensitive(back_btn, FALSE);
+
+ widget = glade_xml_get_widget(xml, "show_name1");
+ gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget,
+ show_name);
+
+ widget = glade_xml_get_widget(xml, "show_range1");
+ gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget,
+ show_range);
+
+ widget = glade_xml_get_widget(xml, "show_data1");
+ gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget,
+ show_value);
+
+ style = gtk_widget_get_style(main_wnd);
+ widget = glade_xml_get_widget(xml, "toolbar1");
+
+#if 0 /* Use stock Gtk icons instead */
+ replace_button_icon(xml, main_wnd->window, style,
+ "button1", (gchar **) xpm_back);
+ replace_button_icon(xml, main_wnd->window, style,
+ "button2", (gchar **) xpm_load);
+ replace_button_icon(xml, main_wnd->window, style,
+ "button3", (gchar **) xpm_save);
+#endif
+ replace_button_icon(xml, main_wnd->window, style,
+ "button4", (gchar **) xpm_single_view);
+ replace_button_icon(xml, main_wnd->window, style,
+ "button5", (gchar **) xpm_split_view);
+ replace_button_icon(xml, main_wnd->window, style,
+ "button6", (gchar **) xpm_tree_view);
+
+#if 0
+ switch (view_mode) {
+ case SINGLE_VIEW:
+ widget = glade_xml_get_widget(xml, "button4");
+ g_signal_emit_by_name(widget, "clicked");
+ break;
+ case SPLIT_VIEW:
+ widget = glade_xml_get_widget(xml, "button5");
+ g_signal_emit_by_name(widget, "clicked");
+ break;
+ case FULL_VIEW:
+ widget = glade_xml_get_widget(xml, "button6");
+ g_signal_emit_by_name(widget, "clicked");
+ break;
+ }
+#endif
+ txtbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_w));
+ tag1 = gtk_text_buffer_create_tag(txtbuf, "mytag1",
+ "foreground", "red",
+ "weight", PANGO_WEIGHT_BOLD,
+ NULL);
+ tag2 = gtk_text_buffer_create_tag(txtbuf, "mytag2",
+ /*"style", PANGO_STYLE_OBLIQUE, */
+ NULL);
+
+ sprintf(title, _("BusyBox %s Configuration"),
+ getenv("KERNELVERSION"));
+ gtk_window_set_title(GTK_WINDOW(main_wnd), title);
+
+ gtk_widget_show(main_wnd);
+}
+
+void init_tree_model(void)
+{
+ gint i;
+
+ tree = tree2 = gtk_tree_store_new(COL_NUMBER,
+ G_TYPE_STRING, G_TYPE_STRING,
+ G_TYPE_STRING, G_TYPE_STRING,
+ G_TYPE_STRING, G_TYPE_STRING,
+ G_TYPE_POINTER, GDK_TYPE_COLOR,
+ G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF,
+ G_TYPE_BOOLEAN, G_TYPE_BOOLEAN,
+ G_TYPE_BOOLEAN, G_TYPE_BOOLEAN,
+ G_TYPE_BOOLEAN);
+ model2 = GTK_TREE_MODEL(tree2);
+
+ for (parents[0] = NULL, i = 1; i < 256; i++)
+ parents[i] = (GtkTreeIter *) g_malloc(sizeof(GtkTreeIter));
+
+ tree1 = gtk_tree_store_new(COL_NUMBER,
+ G_TYPE_STRING, G_TYPE_STRING,
+ G_TYPE_STRING, G_TYPE_STRING,
+ G_TYPE_STRING, G_TYPE_STRING,
+ G_TYPE_POINTER, GDK_TYPE_COLOR,
+ G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF,
+ G_TYPE_BOOLEAN, G_TYPE_BOOLEAN,
+ G_TYPE_BOOLEAN, G_TYPE_BOOLEAN,
+ G_TYPE_BOOLEAN);
+ model1 = GTK_TREE_MODEL(tree1);
+}
+
+void init_left_tree(void)
+{
+ GtkTreeView *view = GTK_TREE_VIEW(tree1_w);
+ GtkCellRenderer *renderer;
+ GtkTreeSelection *sel;
+ GtkTreeViewColumn *column;
+
+ gtk_tree_view_set_model(view, model1);
+ gtk_tree_view_set_headers_visible(view, TRUE);
+ gtk_tree_view_set_rules_hint(view, FALSE);
+
+ column = gtk_tree_view_column_new();
+ gtk_tree_view_append_column(view, column);
+ gtk_tree_view_column_set_title(column, _("Options"));
+
+ renderer = gtk_cell_renderer_toggle_new();
+ gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column),
+ renderer, FALSE);
+ gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column),
+ renderer,
+ "active", COL_BTNACT,
+ "inconsistent", COL_BTNINC,
+ "visible", COL_BTNVIS,
+ "radio", COL_BTNRAD, NULL);
+ renderer = gtk_cell_renderer_text_new();
+ gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column),
+ renderer, FALSE);
+ gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column),
+ renderer,
+ "text", COL_OPTION,
+ "foreground-gdk",
+ COL_COLOR, NULL);
+
+ sel = gtk_tree_view_get_selection(view);
+ gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE);
+ gtk_widget_realize(tree1_w);
+}
+
+static void renderer_edited(GtkCellRendererText * cell,
+ const gchar * path_string,
+ const gchar * new_text, gpointer user_data);
+static void renderer_toggled(GtkCellRendererToggle * cellrenderertoggle,
+ gchar * arg1, gpointer user_data);
+
+void init_right_tree(void)
+{
+ GtkTreeView *view = GTK_TREE_VIEW(tree2_w);
+ GtkCellRenderer *renderer;
+ GtkTreeSelection *sel;
+ GtkTreeViewColumn *column;
+ gint i;
+
+ gtk_tree_view_set_model(view, model2);
+ gtk_tree_view_set_headers_visible(view, TRUE);
+ gtk_tree_view_set_rules_hint(view, FALSE);
+
+ column = gtk_tree_view_column_new();
+ gtk_tree_view_append_column(view, column);
+ gtk_tree_view_column_set_title(column, _("Options"));
+
+ renderer = gtk_cell_renderer_pixbuf_new();
+ gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column),
+ renderer, FALSE);
+ gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column),
+ renderer,
+ "pixbuf", COL_PIXBUF,
+ "visible", COL_PIXVIS, NULL);
+ renderer = gtk_cell_renderer_toggle_new();
+ gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column),
+ renderer, FALSE);
+ gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column),
+ renderer,
+ "active", COL_BTNACT,
+ "inconsistent", COL_BTNINC,
+ "visible", COL_BTNVIS,
+ "radio", COL_BTNRAD, NULL);
+ /*g_signal_connect(G_OBJECT(renderer), "toggled",
+ G_CALLBACK(renderer_toggled), NULL); */
+ renderer = gtk_cell_renderer_text_new();
+ gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column),
+ renderer, FALSE);
+ gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column),
+ renderer,
+ "text", COL_OPTION,
+ "foreground-gdk",
+ COL_COLOR, NULL);
+
+ renderer = gtk_cell_renderer_text_new();
+ gtk_tree_view_insert_column_with_attributes(view, -1,
+ _("Name"), renderer,
+ "text", COL_NAME,
+ "foreground-gdk",
+ COL_COLOR, NULL);
+ renderer = gtk_cell_renderer_text_new();
+ gtk_tree_view_insert_column_with_attributes(view, -1,
+ "N", renderer,
+ "text", COL_NO,
+ "foreground-gdk",
+ COL_COLOR, NULL);
+ renderer = gtk_cell_renderer_text_new();
+ gtk_tree_view_insert_column_with_attributes(view, -1,
+ "M", renderer,
+ "text", COL_MOD,
+ "foreground-gdk",
+ COL_COLOR, NULL);
+ renderer = gtk_cell_renderer_text_new();
+ gtk_tree_view_insert_column_with_attributes(view, -1,
+ "Y", renderer,
+ "text", COL_YES,
+ "foreground-gdk",
+ COL_COLOR, NULL);
+ renderer = gtk_cell_renderer_text_new();
+ gtk_tree_view_insert_column_with_attributes(view, -1,
+ _("Value"), renderer,
+ "text", COL_VALUE,
+ "editable",
+ COL_EDIT,
+ "foreground-gdk",
+ COL_COLOR, NULL);
+ g_signal_connect(G_OBJECT(renderer), "edited",
+ G_CALLBACK(renderer_edited), NULL);
+
+ column = gtk_tree_view_get_column(view, COL_NAME);
+ gtk_tree_view_column_set_visible(column, show_name);
+ column = gtk_tree_view_get_column(view, COL_NO);
+ gtk_tree_view_column_set_visible(column, show_range);
+ column = gtk_tree_view_get_column(view, COL_MOD);
+ gtk_tree_view_column_set_visible(column, show_range);
+ column = gtk_tree_view_get_column(view, COL_YES);
+ gtk_tree_view_column_set_visible(column, show_range);
+ column = gtk_tree_view_get_column(view, COL_VALUE);
+ gtk_tree_view_column_set_visible(column, show_value);
+
+ if (resizeable) {
+ for (i = 0; i < COL_VALUE; i++) {
+ column = gtk_tree_view_get_column(view, i);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ }
+ }
+
+ sel = gtk_tree_view_get_selection(view);
+ gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE);
+}
+
+
+/* Utility Functions */
+
+
+static void text_insert_help(struct menu *menu)
+{
+ GtkTextBuffer *buffer;
+ GtkTextIter start, end;
+ const char *prompt = menu_get_prompt(menu);
+ gchar *name;
+ const char *help = _(nohelp_text);
+
+ if (!menu->sym)
+ help = "";
+ else if (menu->sym->help)
+ help = _(menu->sym->help);
+
+ if (menu->sym && menu->sym->name)
+ name = g_strdup_printf(_(menu->sym->name));
+ else
+ name = g_strdup("");
+
+ buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_w));
+ gtk_text_buffer_get_bounds(buffer, &start, &end);
+ gtk_text_buffer_delete(buffer, &start, &end);
+ gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text_w), 15);
+
+ gtk_text_buffer_get_end_iter(buffer, &end);
+ gtk_text_buffer_insert_with_tags(buffer, &end, prompt, -1, tag1,
+ NULL);
+ gtk_text_buffer_insert_at_cursor(buffer, " ", 1);
+ gtk_text_buffer_get_end_iter(buffer, &end);
+ gtk_text_buffer_insert_with_tags(buffer, &end, name, -1, tag1,
+ NULL);
+ gtk_text_buffer_insert_at_cursor(buffer, "\n\n", 2);
+ gtk_text_buffer_get_end_iter(buffer, &end);
+ gtk_text_buffer_insert_with_tags(buffer, &end, help, -1, tag2,
+ NULL);
+}
+
+
+static void text_insert_msg(const char *title, const char *message)
+{
+ GtkTextBuffer *buffer;
+ GtkTextIter start, end;
+ const char *msg = message;
+
+ buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_w));
+ gtk_text_buffer_get_bounds(buffer, &start, &end);
+ gtk_text_buffer_delete(buffer, &start, &end);
+ gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text_w), 15);
+
+ gtk_text_buffer_get_end_iter(buffer, &end);
+ gtk_text_buffer_insert_with_tags(buffer, &end, title, -1, tag1,
+ NULL);
+ gtk_text_buffer_insert_at_cursor(buffer, "\n\n", 2);
+ gtk_text_buffer_get_end_iter(buffer, &end);
+ gtk_text_buffer_insert_with_tags(buffer, &end, msg, -1, tag2,
+ NULL);
+}
+
+
+/* Main Windows Callbacks */
+
+void on_save1_activate(GtkMenuItem * menuitem, gpointer user_data);
+gboolean on_window1_delete_event(GtkWidget * widget, GdkEvent * event,
+ gpointer user_data)
+{
+ GtkWidget *dialog, *label;
+ gint result;
+
+ if (config_changed == FALSE)
+ return FALSE;
+
+ dialog = gtk_dialog_new_with_buttons(_("Warning !"),
+ GTK_WINDOW(main_wnd),
+ (GtkDialogFlags)
+ (GTK_DIALOG_MODAL |
+ GTK_DIALOG_DESTROY_WITH_PARENT),
+ GTK_STOCK_OK,
+ GTK_RESPONSE_YES,
+ GTK_STOCK_NO,
+ GTK_RESPONSE_NO,
+ GTK_STOCK_CANCEL,
+ GTK_RESPONSE_CANCEL, NULL);
+ gtk_dialog_set_default_response(GTK_DIALOG(dialog),
+ GTK_RESPONSE_CANCEL);
+
+ label = gtk_label_new(_("\nSave configuration ?\n"));
+ gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), label);
+ gtk_widget_show(label);
+
+ result = gtk_dialog_run(GTK_DIALOG(dialog));
+ switch (result) {
+ case GTK_RESPONSE_YES:
+ on_save1_activate(NULL, NULL);
+ return FALSE;
+ case GTK_RESPONSE_NO:
+ return FALSE;
+ case GTK_RESPONSE_CANCEL:
+ case GTK_RESPONSE_DELETE_EVENT:
+ default:
+ gtk_widget_destroy(dialog);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+void on_window1_destroy(GtkObject * object, gpointer user_data)
+{
+ gtk_main_quit();
+}
+
+
+void
+on_window1_size_request(GtkWidget * widget,
+ GtkRequisition * requisition, gpointer user_data)
+{
+ static gint old_h;
+ gint w, h;
+
+ if (widget->window == NULL)
+ gtk_window_get_default_size(GTK_WINDOW(main_wnd), &w, &h);
+ else
+ gdk_window_get_size(widget->window, &w, &h);
+
+ if (h == old_h)
+ return;
+ old_h = h;
+
+ gtk_paned_set_position(GTK_PANED(vpaned), 2 * h / 3);
+}
+
+
+/* Menu & Toolbar Callbacks */
+
+
+static void
+load_filename(GtkFileSelection * file_selector, gpointer user_data)
+{
+ const gchar *fn;
+
+ fn = gtk_file_selection_get_filename(GTK_FILE_SELECTION
+ (user_data));
+
+ if (conf_read(fn))
+ text_insert_msg(_("Error"), _("Unable to load configuration !"));
+ else
+ display_tree(&rootmenu);
+}
+
+void on_load1_activate(GtkMenuItem * menuitem, gpointer user_data)
+{
+ GtkWidget *fs;
+
+ fs = gtk_file_selection_new(_("Load file..."));
+ g_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(fs)->ok_button),
+ "clicked",
+ G_CALLBACK(load_filename), (gpointer) fs);
+ g_signal_connect_swapped(GTK_OBJECT
+ (GTK_FILE_SELECTION(fs)->ok_button),
+ "clicked", G_CALLBACK(gtk_widget_destroy),
+ (gpointer) fs);
+ g_signal_connect_swapped(GTK_OBJECT
+ (GTK_FILE_SELECTION(fs)->cancel_button),
+ "clicked", G_CALLBACK(gtk_widget_destroy),
+ (gpointer) fs);
+ gtk_widget_show(fs);
+}
+
+
+void on_save1_activate(GtkMenuItem * menuitem, gpointer user_data)
+{
+ if (conf_write(NULL))
+ text_insert_msg(_("Error"), _("Unable to save configuration !"));
+
+ config_changed = FALSE;
+}
+
+
+static void
+store_filename(GtkFileSelection * file_selector, gpointer user_data)
+{
+ const gchar *fn;
+
+ fn = gtk_file_selection_get_filename(GTK_FILE_SELECTION
+ (user_data));
+
+ if (conf_write(fn))
+ text_insert_msg(_("Error"), _("Unable to save configuration !"));
+
+ gtk_widget_destroy(GTK_WIDGET(user_data));
+}
+
+void on_save_as1_activate(GtkMenuItem * menuitem, gpointer user_data)
+{
+ GtkWidget *fs;
+
+ fs = gtk_file_selection_new(_("Save file as..."));
+ g_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(fs)->ok_button),
+ "clicked",
+ G_CALLBACK(store_filename), (gpointer) fs);
+ g_signal_connect_swapped(GTK_OBJECT
+ (GTK_FILE_SELECTION(fs)->ok_button),
+ "clicked", G_CALLBACK(gtk_widget_destroy),
+ (gpointer) fs);
+ g_signal_connect_swapped(GTK_OBJECT
+ (GTK_FILE_SELECTION(fs)->cancel_button),
+ "clicked", G_CALLBACK(gtk_widget_destroy),
+ (gpointer) fs);
+ gtk_widget_show(fs);
+}
+
+
+void on_quit1_activate(GtkMenuItem * menuitem, gpointer user_data)
+{
+ if (!on_window1_delete_event(NULL, NULL, NULL))
+ gtk_widget_destroy(GTK_WIDGET(main_wnd));
+}
+
+
+void on_show_name1_activate(GtkMenuItem * menuitem, gpointer user_data)
+{
+ GtkTreeViewColumn *col;
+
+ show_name = GTK_CHECK_MENU_ITEM(menuitem)->active;
+ col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_NAME);
+ if (col)
+ gtk_tree_view_column_set_visible(col, show_name);
+}
+
+
+void on_show_range1_activate(GtkMenuItem * menuitem, gpointer user_data)
+{
+ GtkTreeViewColumn *col;
+
+ show_range = GTK_CHECK_MENU_ITEM(menuitem)->active;
+ col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_NO);
+ if (col)
+ gtk_tree_view_column_set_visible(col, show_range);
+ col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_MOD);
+ if (col)
+ gtk_tree_view_column_set_visible(col, show_range);
+ col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_YES);
+ if (col)
+ gtk_tree_view_column_set_visible(col, show_range);
+
+}
+
+
+void on_show_data1_activate(GtkMenuItem * menuitem, gpointer user_data)
+{
+ GtkTreeViewColumn *col;
+
+ show_value = GTK_CHECK_MENU_ITEM(menuitem)->active;
+ col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_VALUE);
+ if (col)
+ gtk_tree_view_column_set_visible(col, show_value);
+}
+
+
+void
+on_show_all_options1_activate(GtkMenuItem * menuitem, gpointer user_data)
+{
+ show_all = GTK_CHECK_MENU_ITEM(menuitem)->active;
+
+ gtk_tree_store_clear(tree2);
+ display_tree(&rootmenu); // instead of update_tree to speed-up
+}
+
+
+void
+on_show_debug_info1_activate(GtkMenuItem * menuitem, gpointer user_data)
+{
+ show_debug = GTK_CHECK_MENU_ITEM(menuitem)->active;
+ update_tree(&rootmenu, NULL);
+}
+
+
+void on_introduction1_activate(GtkMenuItem * menuitem, gpointer user_data)
+{
+ GtkWidget *dialog;
+ const gchar *intro_text = _(
+ "Welcome to gkc, the GTK+ graphical busybox configuration tool\n"
+ "for Linux.\n"
+ "For each option, a blank box indicates the feature is disabled, a\n"
+ "check indicates it is enabled, and a dot indicates that it is to\n"
+ "be compiled as a module. Clicking on the box will cycle through the three states.\n"
+ "\n"
+ "If you do not see an option (e.g., a device driver) that you\n"
+ "believe should be present, try turning on Show All Options\n"
+ "under the Options menu.\n"
+ "Although there is no cross reference yet to help you figure out\n"
+ "what other options must be enabled to support the option you\n"
+ "are interested in, you can still view the help of a grayed-out\n"
+ "option.\n"
+ "\n"
+ "Toggling Show Debug Info under the Options menu will show\n"
+ "the dependencies, which you can then match by examining other options.");
+
+ dialog = gtk_message_dialog_new(GTK_WINDOW(main_wnd),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_INFO,
+ GTK_BUTTONS_CLOSE, intro_text);
+ g_signal_connect_swapped(GTK_OBJECT(dialog), "response",
+ G_CALLBACK(gtk_widget_destroy),
+ GTK_OBJECT(dialog));
+ gtk_widget_show_all(dialog);
+}
+
+
+void on_about1_activate(GtkMenuItem * menuitem, gpointer user_data)
+{
+ GtkWidget *dialog;
+ const gchar *about_text =
+ _("gkc is copyright (c) 2002 Romain Lievin <roms@lpg.ticalc.org>.\n"
+ "Based on the source code from Roman Zippel.\n");
+
+ dialog = gtk_message_dialog_new(GTK_WINDOW(main_wnd),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_INFO,
+ GTK_BUTTONS_CLOSE, about_text);
+ g_signal_connect_swapped(GTK_OBJECT(dialog), "response",
+ G_CALLBACK(gtk_widget_destroy),
+ GTK_OBJECT(dialog));
+ gtk_widget_show_all(dialog);
+}
+
+
+void on_license1_activate(GtkMenuItem * menuitem, gpointer user_data)
+{
+ GtkWidget *dialog;
+ const gchar *license_text =
+ _("gkc is released under the terms of the GNU GPL v2.\n"
+ "For more information, please see the source code or\n"
+ "visit http://www.fsf.org/licenses/licenses.html\n");
+
+ dialog = gtk_message_dialog_new(GTK_WINDOW(main_wnd),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_INFO,
+ GTK_BUTTONS_CLOSE, license_text);
+ g_signal_connect_swapped(GTK_OBJECT(dialog), "response",
+ G_CALLBACK(gtk_widget_destroy),
+ GTK_OBJECT(dialog));
+ gtk_widget_show_all(dialog);
+}
+
+
+void on_back_clicked(GtkButton * button, gpointer user_data)
+{
+ enum prop_type ptype;
+
+ current = current->parent;
+ ptype = current->prompt ? current->prompt->type : P_UNKNOWN;
+ if (ptype != P_MENU)
+ current = current->parent;
+ display_tree_part();
+
+ if (current == &rootmenu)
+ gtk_widget_set_sensitive(back_btn, FALSE);
+}
+
+
+void on_load_clicked(GtkButton * button, gpointer user_data)
+{
+ on_load1_activate(NULL, user_data);
+}
+
+
+void on_save_clicked(GtkButton * button, gpointer user_data)
+{
+ on_save1_activate(NULL, user_data);
+}
+
+
+void on_single_clicked(GtkButton * button, gpointer user_data)
+{
+ view_mode = SINGLE_VIEW;
+ gtk_paned_set_position(GTK_PANED(hpaned), 0);
+ gtk_widget_hide(tree1_w);
+ current = &rootmenu;
+ display_tree_part();
+}
+
+
+void on_split_clicked(GtkButton * button, gpointer user_data)
+{
+ gint w, h;
+ view_mode = SPLIT_VIEW;
+ gtk_widget_show(tree1_w);
+ gtk_window_get_default_size(GTK_WINDOW(main_wnd), &w, &h);
+ gtk_paned_set_position(GTK_PANED(hpaned), w / 2);
+ if (tree2)
+ gtk_tree_store_clear(tree2);
+ display_list();
+
+ /* Disable back btn, like in full mode. */
+ gtk_widget_set_sensitive(back_btn, FALSE);
+}
+
+
+void on_full_clicked(GtkButton * button, gpointer user_data)
+{
+ view_mode = FULL_VIEW;
+ gtk_paned_set_position(GTK_PANED(hpaned), 0);
+ gtk_widget_hide(tree1_w);
+ if (tree2)
+ gtk_tree_store_clear(tree2);
+ display_tree(&rootmenu);
+ gtk_widget_set_sensitive(back_btn, FALSE);
+}
+
+
+void on_collapse_clicked(GtkButton * button, gpointer user_data)
+{
+ gtk_tree_view_collapse_all(GTK_TREE_VIEW(tree2_w));
+}
+
+
+void on_expand_clicked(GtkButton * button, gpointer user_data)
+{
+ gtk_tree_view_expand_all(GTK_TREE_VIEW(tree2_w));
+}
+
+
+/* CTree Callbacks */
+
+/* Change hex/int/string value in the cell */
+static void renderer_edited(GtkCellRendererText * cell,
+ const gchar * path_string,
+ const gchar * new_text, gpointer user_data)
+{
+ GtkTreePath *path = gtk_tree_path_new_from_string(path_string);
+ GtkTreeIter iter;
+ const char *old_def, *new_def;
+ struct menu *menu;
+ struct symbol *sym;
+
+ if (!gtk_tree_model_get_iter(model2, &iter, path))
+ return;
+
+ gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1);
+ sym = menu->sym;
+
+ gtk_tree_model_get(model2, &iter, COL_VALUE, &old_def, -1);
+ new_def = new_text;
+
+ sym_set_string_value(sym, new_def);
+
+ config_changed = TRUE;
+ update_tree(&rootmenu, NULL);
+
+ gtk_tree_path_free(path);
+}
+
+/* Change the value of a symbol and update the tree */
+static void change_sym_value(struct menu *menu, gint col)
+{
+ struct symbol *sym = menu->sym;
+ tristate oldval, newval;
+
+ if (!sym)
+ return;
+
+ if (col == COL_NO)
+ newval = no;
+ else if (col == COL_MOD)
+ newval = mod;
+ else if (col == COL_YES)
+ newval = yes;
+ else
+ return;
+
+ switch (sym_get_type(sym)) {
+ case S_BOOLEAN:
+ case S_TRISTATE:
+ oldval = sym_get_tristate_value(sym);
+ if (!sym_tristate_within_range(sym, newval))
+ newval = yes;
+ sym_set_tristate_value(sym, newval);
+ config_changed = TRUE;
+ if (view_mode == FULL_VIEW)
+ update_tree(&rootmenu, NULL);
+ else if (view_mode == SPLIT_VIEW) {
+ update_tree(browsed, NULL);
+ display_list();
+ }
+ else if (view_mode == SINGLE_VIEW)
+ display_tree_part(); //fixme: keep exp/coll
+ break;
+ case S_INT:
+ case S_HEX:
+ case S_STRING:
+ default:
+ break;
+ }
+}
+
+static void toggle_sym_value(struct menu *menu)
+{
+ if (!menu->sym)
+ return;
+
+ sym_toggle_tristate_value(menu->sym);
+ if (view_mode == FULL_VIEW)
+ update_tree(&rootmenu, NULL);
+ else if (view_mode == SPLIT_VIEW) {
+ update_tree(browsed, NULL);
+ display_list();
+ }
+ else if (view_mode == SINGLE_VIEW)
+ display_tree_part(); //fixme: keep exp/coll
+}
+
+static void renderer_toggled(GtkCellRendererToggle * cell,
+ gchar * path_string, gpointer user_data)
+{
+ GtkTreePath *path, *sel_path = NULL;
+ GtkTreeIter iter, sel_iter;
+ GtkTreeSelection *sel;
+ struct menu *menu;
+
+ path = gtk_tree_path_new_from_string(path_string);
+ if (!gtk_tree_model_get_iter(model2, &iter, path))
+ return;
+
+ sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree2_w));
+ if (gtk_tree_selection_get_selected(sel, NULL, &sel_iter))
+ sel_path = gtk_tree_model_get_path(model2, &sel_iter);
+ if (!sel_path)
+ goto out1;
+ if (gtk_tree_path_compare(path, sel_path))
+ goto out2;
+
+ gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1);
+ toggle_sym_value(menu);
+
+ out2:
+ gtk_tree_path_free(sel_path);
+ out1:
+ gtk_tree_path_free(path);
+}
+
+static gint column2index(GtkTreeViewColumn * column)
+{
+ gint i;
+
+ for (i = 0; i < COL_NUMBER; i++) {
+ GtkTreeViewColumn *col;
+
+ col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), i);
+ if (col == column)
+ return i;
+ }
+
+ return -1;
+}
+
+
+/* User click: update choice (full) or goes down (single) */
+gboolean
+on_treeview2_button_press_event(GtkWidget * widget,
+ GdkEventButton * event, gpointer user_data)
+{
+ GtkTreeView *view = GTK_TREE_VIEW(widget);
+ GtkTreePath *path;
+ GtkTreeViewColumn *column;
+ GtkTreeIter iter;
+ struct menu *menu;
+ gint col;
+
+#if GTK_CHECK_VERSION(2,1,4) // bug in ctree with earlier version of GTK
+ gint tx = (gint) event->x;
+ gint ty = (gint) event->y;
+ gint cx, cy;
+
+ gtk_tree_view_get_path_at_pos(view, tx, ty, &path, &column, &cx,
+ &cy);
+#else
+ gtk_tree_view_get_cursor(view, &path, &column);
+#endif
+ if (path == NULL)
+ return FALSE;
+
+ if (!gtk_tree_model_get_iter(model2, &iter, path))
+ return FALSE;
+ gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1);
+
+ col = column2index(column);
+ if (event->type == GDK_2BUTTON_PRESS) {
+ enum prop_type ptype;
+ ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN;
+
+ if (ptype == P_MENU && view_mode != FULL_VIEW && col == COL_OPTION) {
+ // goes down into menu
+ current = menu;
+ display_tree_part();
+ gtk_widget_set_sensitive(back_btn, TRUE);
+ } else if ((col == COL_OPTION)) {
+ toggle_sym_value(menu);
+ gtk_tree_view_expand_row(view, path, TRUE);
+ }
+ } else {
+ if (col == COL_VALUE) {
+ toggle_sym_value(menu);
+ gtk_tree_view_expand_row(view, path, TRUE);
+ } else if (col == COL_NO || col == COL_MOD
+ || col == COL_YES) {
+ change_sym_value(menu, col);
+ gtk_tree_view_expand_row(view, path, TRUE);
+ }
+ }
+
+ return FALSE;
+}
+
+/* Key pressed: update choice */
+gboolean
+on_treeview2_key_press_event(GtkWidget * widget,
+ GdkEventKey * event, gpointer user_data)
+{
+ GtkTreeView *view = GTK_TREE_VIEW(widget);
+ GtkTreePath *path;
+ GtkTreeViewColumn *column;
+ GtkTreeIter iter;
+ struct menu *menu;
+ gint col;
+
+ gtk_tree_view_get_cursor(view, &path, &column);
+ if (path == NULL)
+ return FALSE;
+
+ if (event->keyval == GDK_space) {
+ if (gtk_tree_view_row_expanded(view, path))
+ gtk_tree_view_collapse_row(view, path);
+ else
+ gtk_tree_view_expand_row(view, path, FALSE);
+ return TRUE;
+ }
+ if (event->keyval == GDK_KP_Enter) {
+ }
+ if (widget == tree1_w)
+ return FALSE;
+
+ gtk_tree_model_get_iter(model2, &iter, path);
+ gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1);
+
+ if (!strcasecmp(event->string, "n"))
+ col = COL_NO;
+ else if (!strcasecmp(event->string, "m"))
+ col = COL_MOD;
+ else if (!strcasecmp(event->string, "y"))
+ col = COL_YES;
+ else
+ col = -1;
+ change_sym_value(menu, col);
+
+ return FALSE;
+}
+
+
+/* Row selection changed: update help */
+void
+on_treeview2_cursor_changed(GtkTreeView * treeview, gpointer user_data)
+{
+ GtkTreeSelection *selection;
+ GtkTreeIter iter;
+ struct menu *menu;
+
+ selection = gtk_tree_view_get_selection(treeview);
+ if (gtk_tree_selection_get_selected(selection, &model2, &iter)) {
+ gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1);
+ text_insert_help(menu);
+ }
+}
+
+
+/* User click: display sub-tree in the right frame. */
+gboolean
+on_treeview1_button_press_event(GtkWidget * widget,
+ GdkEventButton * event, gpointer user_data)
+{
+ GtkTreeView *view = GTK_TREE_VIEW(widget);
+ GtkTreePath *path;
+ GtkTreeViewColumn *column;
+ GtkTreeIter iter;
+ struct menu *menu;
+
+ gint tx = (gint) event->x;
+ gint ty = (gint) event->y;
+ gint cx, cy;
+
+ gtk_tree_view_get_path_at_pos(view, tx, ty, &path, &column, &cx,
+ &cy);
+ if (path == NULL)
+ return FALSE;
+
+ gtk_tree_model_get_iter(model1, &iter, path);
+ gtk_tree_model_get(model1, &iter, COL_MENU, &menu, -1);
+
+ if (event->type == GDK_2BUTTON_PRESS) {
+ toggle_sym_value(menu);
+ current = menu;
+ display_tree_part();
+ } else {
+ browsed = menu;
+ display_tree_part();
+ }
+
+ gtk_widget_realize(tree2_w);
+ gtk_tree_view_set_cursor(view, path, NULL, FALSE);
+ gtk_widget_grab_focus(tree2_w);
+
+ return FALSE;
+}
+
+
+/* Fill a row of strings */
+static gchar **fill_row(struct menu *menu)
+{
+ static gchar *row[COL_NUMBER];
+ struct symbol *sym = menu->sym;
+ const char *def;
+ int stype;
+ tristate val;
+ enum prop_type ptype;
+ int i;
+
+ for (i = COL_OPTION; i <= COL_COLOR; i++)
+ g_free(row[i]);
+ memset(row, 0, sizeof(row));
+
+ row[COL_OPTION] =
+ g_strdup_printf("%s %s", menu_get_prompt(menu),
+ sym ? (sym->
+ flags & SYMBOL_NEW ? "(NEW)" : "") :
+ "");
+
+ if (show_all && !menu_is_visible(menu))
+ row[COL_COLOR] = g_strdup("DarkGray");
+ else
+ row[COL_COLOR] = g_strdup("Black");
+
+ ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN;
+ switch (ptype) {
+ case P_MENU:
+ row[COL_PIXBUF] = (gchar *) xpm_menu;
+ if (view_mode == SINGLE_VIEW)
+ row[COL_PIXVIS] = GINT_TO_POINTER(TRUE);
+ row[COL_BTNVIS] = GINT_TO_POINTER(FALSE);
+ break;
+ case P_COMMENT:
+ row[COL_PIXBUF] = (gchar *) xpm_void;
+ row[COL_PIXVIS] = GINT_TO_POINTER(FALSE);
+ row[COL_BTNVIS] = GINT_TO_POINTER(FALSE);
+ break;
+ default:
+ row[COL_PIXBUF] = (gchar *) xpm_void;
+ row[COL_PIXVIS] = GINT_TO_POINTER(FALSE);
+ row[COL_BTNVIS] = GINT_TO_POINTER(TRUE);
+ break;
+ }
+
+ if (!sym)
+ return row;
+ row[COL_NAME] = g_strdup(sym->name);
+
+ sym_calc_value(sym);
+ sym->flags &= ~SYMBOL_CHANGED;
+
+ if (sym_is_choice(sym)) { // parse childs for getting final value
+ struct menu *child;
+ struct symbol *def_sym = sym_get_choice_value(sym);
+ struct menu *def_menu = NULL;
+
+ row[COL_BTNVIS] = GINT_TO_POINTER(FALSE);
+
+ for (child = menu->list; child; child = child->next) {
+ if (menu_is_visible(child)
+ && child->sym == def_sym)
+ def_menu = child;
+ }
+
+ if (def_menu)
+ row[COL_VALUE] =
+ g_strdup(menu_get_prompt(def_menu));
+ }
+ if (sym->flags & SYMBOL_CHOICEVAL)
+ row[COL_BTNRAD] = GINT_TO_POINTER(TRUE);
+
+ stype = sym_get_type(sym);
+ switch (stype) {
+ case S_BOOLEAN:
+ if (GPOINTER_TO_INT(row[COL_PIXVIS]) == FALSE)
+ row[COL_BTNVIS] = GINT_TO_POINTER(TRUE);
+ if (sym_is_choice(sym))
+ break;
+ case S_TRISTATE:
+ val = sym_get_tristate_value(sym);
+ switch (val) {
+ case no:
+ row[COL_NO] = g_strdup("N");
+ row[COL_VALUE] = g_strdup("N");
+ row[COL_BTNACT] = GINT_TO_POINTER(FALSE);
+ row[COL_BTNINC] = GINT_TO_POINTER(FALSE);
+ break;
+ case mod:
+ row[COL_MOD] = g_strdup("M");
+ row[COL_VALUE] = g_strdup("M");
+ row[COL_BTNINC] = GINT_TO_POINTER(TRUE);
+ break;
+ case yes:
+ row[COL_YES] = g_strdup("Y");
+ row[COL_VALUE] = g_strdup("Y");
+ row[COL_BTNACT] = GINT_TO_POINTER(TRUE);
+ row[COL_BTNINC] = GINT_TO_POINTER(FALSE);
+ break;
+ }
+
+ if (val != no && sym_tristate_within_range(sym, no))
+ row[COL_NO] = g_strdup("_");
+ if (val != mod && sym_tristate_within_range(sym, mod))
+ row[COL_MOD] = g_strdup("_");
+ if (val != yes && sym_tristate_within_range(sym, yes))
+ row[COL_YES] = g_strdup("_");
+ break;
+ case S_INT:
+ case S_HEX:
+ case S_STRING:
+ def = sym_get_string_value(sym);
+ row[COL_VALUE] = g_strdup(def);
+ row[COL_EDIT] = GINT_TO_POINTER(TRUE);
+ row[COL_BTNVIS] = GINT_TO_POINTER(FALSE);
+ break;
+ }
+
+ return row;
+}
+
+
+/* Set the node content with a row of strings */
+static void set_node(GtkTreeIter * node, struct menu *menu, gchar ** row)
+{
+ GdkColor color;
+ gboolean success;
+ GdkPixbuf *pix;
+
+ pix = gdk_pixbuf_new_from_xpm_data((const char **)
+ row[COL_PIXBUF]);
+
+ gdk_color_parse(row[COL_COLOR], &color);
+ gdk_colormap_alloc_colors(gdk_colormap_get_system(), &color, 1,
+ FALSE, FALSE, &success);
+
+ gtk_tree_store_set(tree, node,
+ COL_OPTION, row[COL_OPTION],
+ COL_NAME, row[COL_NAME],
+ COL_NO, row[COL_NO],
+ COL_MOD, row[COL_MOD],
+ COL_YES, row[COL_YES],
+ COL_VALUE, row[COL_VALUE],
+ COL_MENU, (gpointer) menu,
+ COL_COLOR, &color,
+ COL_EDIT, GPOINTER_TO_INT(row[COL_EDIT]),
+ COL_PIXBUF, pix,
+ COL_PIXVIS, GPOINTER_TO_INT(row[COL_PIXVIS]),
+ COL_BTNVIS, GPOINTER_TO_INT(row[COL_BTNVIS]),
+ COL_BTNACT, GPOINTER_TO_INT(row[COL_BTNACT]),
+ COL_BTNINC, GPOINTER_TO_INT(row[COL_BTNINC]),
+ COL_BTNRAD, GPOINTER_TO_INT(row[COL_BTNRAD]),
+ -1);
+
+ g_object_unref(pix);
+}
+
+
+/* Add a node to the tree */
+static void place_node(struct menu *menu, char **row)
+{
+ GtkTreeIter *parent = parents[indent - 1];
+ GtkTreeIter *node = parents[indent];
+
+ gtk_tree_store_append(tree, node, parent);
+ set_node(node, menu, row);
+}
+
+
+/* Find a node in the GTK+ tree */
+static GtkTreeIter found;
+
+/*
+ * Find a menu in the GtkTree starting at parent.
+ */
+GtkTreeIter *gtktree_iter_find_node(GtkTreeIter * parent,
+ struct menu *tofind)
+{
+ GtkTreeIter iter;
+ GtkTreeIter *child = &iter;
+ gboolean valid;
+ GtkTreeIter *ret;
+
+ valid = gtk_tree_model_iter_children(model2, child, parent);
+ while (valid) {
+ struct menu *menu;
+
+ gtk_tree_model_get(model2, child, 6, &menu, -1);
+
+ if (menu == tofind) {
+ memcpy(&found, child, sizeof(GtkTreeIter));
+ return &found;
+ }
+
+ ret = gtktree_iter_find_node(child, tofind);
+ if (ret)
+ return ret;
+
+ valid = gtk_tree_model_iter_next(model2, child);
+ }
+
+ return NULL;
+}
+
+
+/*
+ * Update the tree by adding/removing entries
+ * Does not change other nodes
+ */
+static void update_tree(struct menu *src, GtkTreeIter * dst)
+{
+ struct menu *child1;
+ GtkTreeIter iter, tmp;
+ GtkTreeIter *child2 = &iter;
+ gboolean valid;
+ GtkTreeIter *sibling;
+ struct symbol *sym;
+ struct property *prop;
+ struct menu *menu1, *menu2;
+
+ if (src == &rootmenu)
+ indent = 1;
+
+ valid = gtk_tree_model_iter_children(model2, child2, dst);
+ for (child1 = src->list; child1; child1 = child1->next) {
+
+ prop = child1->prompt;
+ sym = child1->sym;
+
+ reparse:
+ menu1 = child1;
+ if (valid)
+ gtk_tree_model_get(model2, child2, COL_MENU,
+ &menu2, -1);
+ else
+ menu2 = NULL; // force adding of a first child
+
+#ifdef DEBUG
+ printf("%*c%s | %s\n", indent, ' ',
+ menu1 ? menu_get_prompt(menu1) : "nil",
+ menu2 ? menu_get_prompt(menu2) : "nil");
+#endif
+
+ if (!menu_is_visible(child1) && !show_all) { // remove node
+ if (gtktree_iter_find_node(dst, menu1) != NULL) {
+ memcpy(&tmp, child2, sizeof(GtkTreeIter));
+ valid = gtk_tree_model_iter_next(model2,
+ child2);
+ gtk_tree_store_remove(tree2, &tmp);
+ if (!valid)
+ return; // next parent
+ else
+ goto reparse; // next child
+ } else
+ continue;
+ }
+
+ if (menu1 != menu2) {
+ if (gtktree_iter_find_node(dst, menu1) == NULL) { // add node
+ if (!valid && !menu2)
+ sibling = NULL;
+ else
+ sibling = child2;
+ gtk_tree_store_insert_before(tree2,
+ child2,
+ dst, sibling);
+ set_node(child2, menu1, fill_row(menu1));
+ if (menu2 == NULL)
+ valid = TRUE;
+ } else { // remove node
+ memcpy(&tmp, child2, sizeof(GtkTreeIter));
+ valid = gtk_tree_model_iter_next(model2,
+ child2);
+ gtk_tree_store_remove(tree2, &tmp);
+ if (!valid)
+ return; // next parent
+ else
+ goto reparse; // next child
+ }
+ } else if (sym && (sym->flags & SYMBOL_CHANGED)) {
+ set_node(child2, menu1, fill_row(menu1));
+ }
+
+ indent++;
+ update_tree(child1, child2);
+ indent--;
+
+ valid = gtk_tree_model_iter_next(model2, child2);
+ }
+}
+
+
+/* Display the whole tree (single/split/full view) */
+static void display_tree(struct menu *menu)
+{
+ struct symbol *sym;
+ struct property *prop;
+ struct menu *child;
+ enum prop_type ptype;
+
+ if (menu == &rootmenu) {
+ indent = 1;
+ current = &rootmenu;
+ }
+
+ for (child = menu->list; child; child = child->next) {
+ prop = child->prompt;
+ sym = child->sym;
+ ptype = prop ? prop->type : P_UNKNOWN;
+
+ if (sym)
+ sym->flags &= ~SYMBOL_CHANGED;
+
+ if ((view_mode == SPLIT_VIEW)
+ && !(child->flags & MENU_ROOT) && (tree == tree1))
+ continue;
+
+ if ((view_mode == SPLIT_VIEW) && (child->flags & MENU_ROOT)
+ && (tree == tree2))
+ continue;
+
+ if (menu_is_visible(child) || show_all)
+ place_node(child, fill_row(child));
+#ifdef DEBUG
+ printf("%*c%s: ", indent, ' ', menu_get_prompt(child));
+ printf("%s", child->flags & MENU_ROOT ? "rootmenu | " : "");
+ dbg_print_ptype(ptype);
+ printf(" | ");
+ if (sym) {
+ dbg_print_stype(sym->type);
+ printf(" | ");
+ dbg_print_flags(sym->flags);
+ printf("\n");
+ } else
+ printf("\n");
+#endif
+ if ((view_mode != FULL_VIEW) && (ptype == P_MENU)
+ && (tree == tree2))
+ continue;
+/*
+ if (((menu != &rootmenu) && !(menu->flags & MENU_ROOT))
+ || (view_mode == FULL_VIEW)
+ || (view_mode == SPLIT_VIEW))*/
+ if (((view_mode == SINGLE_VIEW) && (menu->flags & MENU_ROOT))
+ || (view_mode == FULL_VIEW)
+ || (view_mode == SPLIT_VIEW)) {
+ indent++;
+ display_tree(child);
+ indent--;
+ }
+ }
+}
+
+/* Display a part of the tree starting at current node (single/split view) */
+static void display_tree_part(void)
+{
+ if (tree2)
+ gtk_tree_store_clear(tree2);
+ if (view_mode == SINGLE_VIEW)
+ display_tree(current);
+ else if (view_mode == SPLIT_VIEW)
+ display_tree(browsed);
+ gtk_tree_view_expand_all(GTK_TREE_VIEW(tree2_w));
+}
+
+/* Display the list in the left frame (split view) */
+static void display_list(void)
+{
+ if (tree1)
+ gtk_tree_store_clear(tree1);
+
+ tree = tree1;
+ display_tree(&rootmenu);
+ gtk_tree_view_expand_all(GTK_TREE_VIEW(tree1_w));
+ tree = tree2;
+}
+
+void fixup_rootmenu(struct menu *menu)
+{
+ struct menu *child;
+ static int menu_cnt = 0;
+
+ menu->flags |= MENU_ROOT;
+ for (child = menu->list; child; child = child->next) {
+ if (child->prompt && child->prompt->type == P_MENU) {
+ menu_cnt++;
+ fixup_rootmenu(child);
+ menu_cnt--;
+ } else if (!menu_cnt)
+ fixup_rootmenu(child);
+ }
+}
+
+
+/* Main */
+int main(int ac, char *av[])
+{
+ const char *name;
+ char *env;
+ gchar *glade_file;
+
+#ifndef LKC_DIRECT_LINK
+ kconfig_load();
+#endif
+
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ bind_textdomain_codeset(PACKAGE, "UTF-8");
+ textdomain(PACKAGE);
+
+ /* GTK stuffs */
+ gtk_set_locale();
+ gtk_init(&ac, &av);
+ glade_init();
+
+ //add_pixmap_directory (PACKAGE_DATA_DIR "/" PACKAGE "/pixmaps");
+ //add_pixmap_directory (PACKAGE_SOURCE_DIR "/pixmaps");
+
+ /* Determine GUI path */
+ env = getenv(SRCTREE);
+ if (env)
+ glade_file = g_strconcat(env, "/scripts/kconfig/gconf.glade", NULL);
+ else if (av[0][0] == '/')
+ glade_file = g_strconcat(av[0], ".glade", NULL);
+ else
+ glade_file = g_strconcat(g_get_current_dir(), "/", av[0], ".glade", NULL);
+
+ /* Load the interface and connect signals */
+ init_main_window(glade_file);
+ init_tree_model();
+ init_left_tree();
+ init_right_tree();
+
+ /* Conf stuffs */
+ if (ac > 1 && av[1][0] == '-') {
+ switch (av[1][1]) {
+ case 'a':
+ //showAll = 1;
+ break;
+ case 'h':
+ case '?':
+ printf("%s <config>\n", av[0]);
+ exit(0);
+ }
+ name = av[2];
+ } else
+ name = av[1];
+
+ conf_parse(name);
+ fixup_rootmenu(&rootmenu);
+ conf_read(NULL);
+
+ switch (view_mode) {
+ case SINGLE_VIEW:
+ display_tree_part();
+ break;
+ case SPLIT_VIEW:
+ display_list();
+ break;
+ case FULL_VIEW:
+ display_tree(&rootmenu);
+ break;
+ }
+
+ gtk_main();
+
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/scripts/kconfig/gconf.glade b/i/pc104/initrd/conf/busybox/scripts/kconfig/gconf.glade
new file mode 100644
index 0000000..f8744ed
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/scripts/kconfig/gconf.glade
@@ -0,0 +1,648 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
+
+<glade-interface>
+
+<widget class="GtkWindow" id="window1">
+ <property name="visible">True</property>
+ <property name="title" translatable="yes">Gtk Kernel Configurator</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_NONE</property>
+ <property name="modal">False</property>
+ <property name="default_width">640</property>
+ <property name="default_height">480</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <signal name="destroy" handler="on_window1_destroy" object="window1"/>
+ <signal name="size_request" handler="on_window1_size_request" object="vpaned1" last_modification_time="Fri, 11 Jan 2002 16:17:11 GMT"/>
+ <signal name="delete_event" handler="on_window1_delete_event" object="window1" last_modification_time="Sun, 09 Mar 2003 19:42:46 GMT"/>
+
+ <child>
+ <widget class="GtkVBox" id="vbox1">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkMenuBar" id="menubar1">
+ <property name="visible">True</property>
+
+ <child>
+ <widget class="GtkMenuItem" id="file1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_File</property>
+ <property name="use_underline">True</property>
+
+ <child>
+ <widget class="GtkMenu" id="file1_menu">
+
+ <child>
+ <widget class="GtkImageMenuItem" id="load1">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Load a config file</property>
+ <property name="label" translatable="yes">_Load</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_load1_activate"/>
+ <accelerator key="L" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image39">
+ <property name="visible">True</property>
+ <property name="stock">gtk-open</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="save1">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Save the config in .config</property>
+ <property name="label" translatable="yes">_Save</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_save1_activate"/>
+ <accelerator key="S" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image40">
+ <property name="visible">True</property>
+ <property name="stock">gtk-save</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="save_as1">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Save the config in a file</property>
+ <property name="label" translatable="yes">Save _as</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_save_as1_activate"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image41">
+ <property name="visible">True</property>
+ <property name="stock">gtk-save-as</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkSeparatorMenuItem" id="separator1">
+ <property name="visible">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="quit1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Quit</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_quit1_activate"/>
+ <accelerator key="Q" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image42">
+ <property name="visible">True</property>
+ <property name="stock">gtk-quit</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenuItem" id="options1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Options</property>
+ <property name="use_underline">True</property>
+
+ <child>
+ <widget class="GtkMenu" id="options1_menu">
+
+ <child>
+ <widget class="GtkCheckMenuItem" id="show_name1">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Show name</property>
+ <property name="label" translatable="yes">Show _name</property>
+ <property name="use_underline">True</property>
+ <property name="active">False</property>
+ <signal name="activate" handler="on_show_name1_activate"/>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkCheckMenuItem" id="show_range1">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Show range (Y/M/N)</property>
+ <property name="label" translatable="yes">Show _range</property>
+ <property name="use_underline">True</property>
+ <property name="active">False</property>
+ <signal name="activate" handler="on_show_range1_activate"/>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkCheckMenuItem" id="show_data1">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Show value of the option</property>
+ <property name="label" translatable="yes">Show _data</property>
+ <property name="use_underline">True</property>
+ <property name="active">False</property>
+ <signal name="activate" handler="on_show_data1_activate"/>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkSeparatorMenuItem" id="separator2">
+ <property name="visible">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkCheckMenuItem" id="show_all_options1">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Show all options</property>
+ <property name="label" translatable="yes">Show all _options</property>
+ <property name="use_underline">True</property>
+ <property name="active">False</property>
+ <signal name="activate" handler="on_show_all_options1_activate"/>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkCheckMenuItem" id="show_debug_info1">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Show masked options</property>
+ <property name="label" translatable="yes">Show _debug info</property>
+ <property name="use_underline">True</property>
+ <property name="active">False</property>
+ <signal name="activate" handler="on_show_debug_info1_activate"/>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenuItem" id="help1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Help</property>
+ <property name="use_underline">True</property>
+
+ <child>
+ <widget class="GtkMenu" id="help1_menu">
+
+ <child>
+ <widget class="GtkImageMenuItem" id="introduction1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Introduction</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_introduction1_activate" last_modification_time="Fri, 15 Nov 2002 20:26:30 GMT"/>
+ <accelerator key="I" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image43">
+ <property name="visible">True</property>
+ <property name="stock">gtk-dialog-question</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="about1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_About</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_about1_activate" last_modification_time="Fri, 15 Nov 2002 20:26:30 GMT"/>
+ <accelerator key="A" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image44">
+ <property name="visible">True</property>
+ <property name="stock">gtk-properties</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="license1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_License</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_license1_activate" last_modification_time="Fri, 15 Nov 2002 20:26:30 GMT"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image45">
+ <property name="visible">True</property>
+ <property name="stock">gtk-justify-fill</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHandleBox" id="handlebox1">
+ <property name="visible">True</property>
+ <property name="shadow_type">GTK_SHADOW_OUT</property>
+ <property name="handle_position">GTK_POS_LEFT</property>
+ <property name="snap_edge">GTK_POS_TOP</property>
+
+ <child>
+ <widget class="GtkToolbar" id="toolbar1">
+ <property name="visible">True</property>
+ <property name="orientation">GTK_ORIENTATION_HORIZONTAL</property>
+ <property name="toolbar_style">GTK_TOOLBAR_BOTH</property>
+ <property name="tooltips">True</property>
+ <property name="show_arrow">True</property>
+
+ <child>
+ <widget class="GtkToolButton" id="button1">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Goes up of one level (single view)</property>
+ <property name="label" translatable="yes">Back</property>
+ <property name="use_underline">True</property>
+ <property name="stock_id">gtk-undo</property>
+ <property name="visible_horizontal">True</property>
+ <property name="visible_vertical">True</property>
+ <property name="is_important">False</property>
+ <signal name="clicked" handler="on_back_clicked"/>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkToolItem" id="toolitem1">
+ <property name="visible">True</property>
+ <property name="visible_horizontal">True</property>
+ <property name="visible_vertical">True</property>
+ <property name="is_important">False</property>
+
+ <child>
+ <widget class="GtkVSeparator" id="vseparator1">
+ <property name="visible">True</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkToolButton" id="button2">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Load a config file</property>
+ <property name="label" translatable="yes">Load</property>
+ <property name="use_underline">True</property>
+ <property name="stock_id">gtk-open</property>
+ <property name="visible_horizontal">True</property>
+ <property name="visible_vertical">True</property>
+ <property name="is_important">False</property>
+ <signal name="clicked" handler="on_load_clicked"/>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkToolButton" id="button3">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Save a config file</property>
+ <property name="label" translatable="yes">Save</property>
+ <property name="use_underline">True</property>
+ <property name="stock_id">gtk-save</property>
+ <property name="visible_horizontal">True</property>
+ <property name="visible_vertical">True</property>
+ <property name="is_important">False</property>
+ <signal name="clicked" handler="on_save_clicked"/>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkToolItem" id="toolitem2">
+ <property name="visible">True</property>
+ <property name="visible_horizontal">True</property>
+ <property name="visible_vertical">True</property>
+ <property name="is_important">False</property>
+
+ <child>
+ <widget class="GtkVSeparator" id="vseparator2">
+ <property name="visible">True</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkToolButton" id="button4">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Single view</property>
+ <property name="label" translatable="yes">Single</property>
+ <property name="use_underline">True</property>
+ <property name="stock_id">gtk-missing-image</property>
+ <property name="visible_horizontal">True</property>
+ <property name="visible_vertical">True</property>
+ <property name="is_important">False</property>
+ <signal name="clicked" handler="on_single_clicked" last_modification_time="Sun, 12 Jan 2003 14:28:39 GMT"/>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkToolButton" id="button5">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Split view</property>
+ <property name="label" translatable="yes">Split</property>
+ <property name="use_underline">True</property>
+ <property name="stock_id">gtk-missing-image</property>
+ <property name="visible_horizontal">True</property>
+ <property name="visible_vertical">True</property>
+ <property name="is_important">False</property>
+ <signal name="clicked" handler="on_split_clicked" last_modification_time="Sun, 12 Jan 2003 14:28:45 GMT"/>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkToolButton" id="button6">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Full view</property>
+ <property name="label" translatable="yes">Full</property>
+ <property name="use_underline">True</property>
+ <property name="stock_id">gtk-missing-image</property>
+ <property name="visible_horizontal">True</property>
+ <property name="visible_vertical">True</property>
+ <property name="is_important">False</property>
+ <signal name="clicked" handler="on_full_clicked" last_modification_time="Sun, 12 Jan 2003 14:28:50 GMT"/>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkToolItem" id="toolitem3">
+ <property name="visible">True</property>
+ <property name="visible_horizontal">True</property>
+ <property name="visible_vertical">True</property>
+ <property name="is_important">False</property>
+
+ <child>
+ <widget class="GtkVSeparator" id="vseparator3">
+ <property name="visible">True</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkToolButton" id="button7">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Collapse the whole tree in the right frame</property>
+ <property name="label" translatable="yes">Collapse</property>
+ <property name="use_underline">True</property>
+ <property name="stock_id">gtk-remove</property>
+ <property name="visible_horizontal">True</property>
+ <property name="visible_vertical">True</property>
+ <property name="is_important">False</property>
+ <signal name="clicked" handler="on_collapse_clicked"/>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkToolButton" id="button8">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Expand the whole tree in the right frame</property>
+ <property name="label" translatable="yes">Expand</property>
+ <property name="use_underline">True</property>
+ <property name="stock_id">gtk-add</property>
+ <property name="visible_horizontal">True</property>
+ <property name="visible_vertical">True</property>
+ <property name="is_important">False</property>
+ <signal name="clicked" handler="on_expand_clicked"/>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHPaned" id="hpaned1">
+ <property name="width_request">1</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="position">0</property>
+
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwindow1">
+ <property name="visible">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="shadow_type">GTK_SHADOW_IN</property>
+ <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+ <child>
+ <widget class="GtkTreeView" id="treeview1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="headers_visible">True</property>
+ <property name="rules_hint">False</property>
+ <property name="reorderable">False</property>
+ <property name="enable_search">True</property>
+ <signal name="cursor_changed" handler="on_treeview2_cursor_changed" last_modification_time="Sun, 12 Jan 2003 15:58:22 GMT"/>
+ <signal name="button_press_event" handler="on_treeview1_button_press_event" last_modification_time="Sun, 12 Jan 2003 16:03:52 GMT"/>
+ <signal name="key_press_event" handler="on_treeview2_key_press_event" last_modification_time="Sun, 12 Jan 2003 16:11:44 GMT"/>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="shrink">True</property>
+ <property name="resize">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVPaned" id="vpaned1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="position">0</property>
+
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwindow2">
+ <property name="visible">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="shadow_type">GTK_SHADOW_IN</property>
+ <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+ <child>
+ <widget class="GtkTreeView" id="treeview2">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_focus">True</property>
+ <property name="headers_visible">True</property>
+ <property name="rules_hint">False</property>
+ <property name="reorderable">False</property>
+ <property name="enable_search">True</property>
+ <signal name="cursor_changed" handler="on_treeview2_cursor_changed" last_modification_time="Sun, 12 Jan 2003 15:57:55 GMT"/>
+ <signal name="button_press_event" handler="on_treeview2_button_press_event" last_modification_time="Sun, 12 Jan 2003 15:57:58 GMT"/>
+ <signal name="key_press_event" handler="on_treeview2_key_press_event" last_modification_time="Sun, 12 Jan 2003 15:58:01 GMT"/>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="shrink">True</property>
+ <property name="resize">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwindow3">
+ <property name="visible">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_NEVER</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="shadow_type">GTK_SHADOW_IN</property>
+ <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+ <child>
+ <widget class="GtkTextView" id="textview3">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">False</property>
+ <property name="overwrite">False</property>
+ <property name="accepts_tab">True</property>
+ <property name="justification">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap_mode">GTK_WRAP_WORD</property>
+ <property name="cursor_visible">True</property>
+ <property name="pixels_above_lines">0</property>
+ <property name="pixels_below_lines">0</property>
+ <property name="pixels_inside_wrap">0</property>
+ <property name="left_margin">0</property>
+ <property name="right_margin">0</property>
+ <property name="indent">0</property>
+ <property name="text" translatable="yes">Sorry, no help available for this option yet.</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="shrink">True</property>
+ <property name="resize">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="shrink">True</property>
+ <property name="resize">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+</glade-interface>
diff --git a/i/pc104/initrd/conf/busybox/scripts/kconfig/images.c b/i/pc104/initrd/conf/busybox/scripts/kconfig/images.c
new file mode 100644
index 0000000..d4f84bd
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/scripts/kconfig/images.c
@@ -0,0 +1,326 @@
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+static const char *xpm_load[] = {
+"22 22 5 1",
+". c None",
+"# c #000000",
+"c c #838100",
+"a c #ffff00",
+"b c #ffffff",
+"......................",
+"......................",
+"......................",
+"............####....#.",
+"...........#....##.##.",
+"..................###.",
+".................####.",
+".####...........#####.",
+"#abab##########.......",
+"#babababababab#.......",
+"#ababababababa#.......",
+"#babababababab#.......",
+"#ababab###############",
+"#babab##cccccccccccc##",
+"#abab##cccccccccccc##.",
+"#bab##cccccccccccc##..",
+"#ab##cccccccccccc##...",
+"#b##cccccccccccc##....",
+"###cccccccccccc##.....",
+"##cccccccccccc##......",
+"###############.......",
+"......................"};
+
+static const char *xpm_save[] = {
+"22 22 5 1",
+". c None",
+"# c #000000",
+"a c #838100",
+"b c #c5c2c5",
+"c c #cdb6d5",
+"......................",
+".####################.",
+".#aa#bbbbbbbbbbbb#bb#.",
+".#aa#bbbbbbbbbbbb#bb#.",
+".#aa#bbbbbbbbbcbb####.",
+".#aa#bbbccbbbbbbb#aa#.",
+".#aa#bbbccbbbbbbb#aa#.",
+".#aa#bbbbbbbbbbbb#aa#.",
+".#aa#bbbbbbbbbbbb#aa#.",
+".#aa#bbbbbbbbbbbb#aa#.",
+".#aa#bbbbbbbbbbbb#aa#.",
+".#aaa############aaa#.",
+".#aaaaaaaaaaaaaaaaaa#.",
+".#aaaaaaaaaaaaaaaaaa#.",
+".#aaa#############aa#.",
+".#aaa#########bbb#aa#.",
+".#aaa#########bbb#aa#.",
+".#aaa#########bbb#aa#.",
+".#aaa#########bbb#aa#.",
+".#aaa#########bbb#aa#.",
+"..##################..",
+"......................"};
+
+static const char *xpm_back[] = {
+"22 22 3 1",
+". c None",
+"# c #000083",
+"a c #838183",
+"......................",
+"......................",
+"......................",
+"......................",
+"......................",
+"...........######a....",
+"..#......##########...",
+"..##...####......##a..",
+"..###.###.........##..",
+"..######..........##..",
+"..#####...........##..",
+"..######..........##..",
+"..#######.........##..",
+"..########.......##a..",
+"...............a###...",
+"...............###....",
+"......................",
+"......................",
+"......................",
+"......................",
+"......................",
+"......................"};
+
+static const char *xpm_tree_view[] = {
+"22 22 2 1",
+". c None",
+"# c #000000",
+"......................",
+"......................",
+"......#...............",
+"......#...............",
+"......#...............",
+"......#...............",
+"......#...............",
+"......########........",
+"......#...............",
+"......#...............",
+"......#...............",
+"......#...............",
+"......#...............",
+"......########........",
+"......#...............",
+"......#...............",
+"......#...............",
+"......#...............",
+"......#...............",
+"......########........",
+"......................",
+"......................"};
+
+static const char *xpm_single_view[] = {
+"22 22 2 1",
+". c None",
+"# c #000000",
+"......................",
+"......................",
+"..........#...........",
+"..........#...........",
+"..........#...........",
+"..........#...........",
+"..........#...........",
+"..........#...........",
+"..........#...........",
+"..........#...........",
+"..........#...........",
+"..........#...........",
+"..........#...........",
+"..........#...........",
+"..........#...........",
+"..........#...........",
+"..........#...........",
+"..........#...........",
+"..........#...........",
+"..........#...........",
+"......................",
+"......................"};
+
+static const char *xpm_split_view[] = {
+"22 22 2 1",
+". c None",
+"# c #000000",
+"......................",
+"......................",
+"......#......#........",
+"......#......#........",
+"......#......#........",
+"......#......#........",
+"......#......#........",
+"......#......#........",
+"......#......#........",
+"......#......#........",
+"......#......#........",
+"......#......#........",
+"......#......#........",
+"......#......#........",
+"......#......#........",
+"......#......#........",
+"......#......#........",
+"......#......#........",
+"......#......#........",
+"......#......#........",
+"......................",
+"......................"};
+
+static const char *xpm_symbol_no[] = {
+"12 12 2 1",
+" c white",
+". c black",
+" ",
+" .......... ",
+" . . ",
+" . . ",
+" . . ",
+" . . ",
+" . . ",
+" . . ",
+" . . ",
+" . . ",
+" .......... ",
+" "};
+
+static const char *xpm_symbol_mod[] = {
+"12 12 2 1",
+" c white",
+". c black",
+" ",
+" .......... ",
+" . . ",
+" . . ",
+" . .. . ",
+" . .... . ",
+" . .... . ",
+" . .. . ",
+" . . ",
+" . . ",
+" .......... ",
+" "};
+
+static const char *xpm_symbol_yes[] = {
+"12 12 2 1",
+" c white",
+". c black",
+" ",
+" .......... ",
+" . . ",
+" . . ",
+" . . . ",
+" . .. . ",
+" . . .. . ",
+" . .... . ",
+" . .. . ",
+" . . ",
+" .......... ",
+" "};
+
+static const char *xpm_choice_no[] = {
+"12 12 2 1",
+" c white",
+". c black",
+" ",
+" .... ",
+" .. .. ",
+" . . ",
+" . . ",
+" . . ",
+" . . ",
+" . . ",
+" . . ",
+" .. .. ",
+" .... ",
+" "};
+
+static const char *xpm_choice_yes[] = {
+"12 12 2 1",
+" c white",
+". c black",
+" ",
+" .... ",
+" .. .. ",
+" . . ",
+" . .. . ",
+" . .... . ",
+" . .... . ",
+" . .. . ",
+" . . ",
+" .. .. ",
+" .... ",
+" "};
+
+static const char *xpm_menu[] = {
+"12 12 2 1",
+" c white",
+". c black",
+" ",
+" .......... ",
+" . . ",
+" . .. . ",
+" . .... . ",
+" . ...... . ",
+" . ...... . ",
+" . .... . ",
+" . .. . ",
+" . . ",
+" .......... ",
+" "};
+
+static const char *xpm_menu_inv[] = {
+"12 12 2 1",
+" c white",
+". c black",
+" ",
+" .......... ",
+" .......... ",
+" .. ...... ",
+" .. .... ",
+" .. .. ",
+" .. .. ",
+" .. .... ",
+" .. ...... ",
+" .......... ",
+" .......... ",
+" "};
+
+static const char *xpm_menuback[] = {
+"12 12 2 1",
+" c white",
+". c black",
+" ",
+" .......... ",
+" . . ",
+" . .. . ",
+" . .... . ",
+" . ...... . ",
+" . ...... . ",
+" . .... . ",
+" . .. . ",
+" . . ",
+" .......... ",
+" "};
+
+static const char *xpm_void[] = {
+"12 12 2 1",
+" c white",
+". c black",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" "};
diff --git a/i/pc104/initrd/conf/busybox/scripts/kconfig/kconfig_load.c b/i/pc104/initrd/conf/busybox/scripts/kconfig/kconfig_load.c
new file mode 100644
index 0000000..dbdcaad
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/scripts/kconfig/kconfig_load.c
@@ -0,0 +1,35 @@
+#include <dlfcn.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "lkc.h"
+
+#define P(name,type,arg) type (*name ## _p) arg
+#include "lkc_proto.h"
+#undef P
+
+void kconfig_load(void)
+{
+ void *handle;
+ char *error;
+
+ handle = dlopen("./libkconfig.so", RTLD_LAZY);
+ if (!handle) {
+ handle = dlopen("./scripts/kconfig/libkconfig.so", RTLD_LAZY);
+ if (!handle) {
+ fprintf(stderr, "%s\n", dlerror());
+ exit(1);
+ }
+ }
+
+#define P(name,type,arg) \
+{ \
+ name ## _p = dlsym(handle, #name); \
+ if ((error = dlerror())) { \
+ fprintf(stderr, "%s\n", error); \
+ exit(1); \
+ } \
+}
+#include "lkc_proto.h"
+#undef P
+}
diff --git a/i/pc104/initrd/conf/busybox/scripts/kconfig/kxgettext.c b/i/pc104/initrd/conf/busybox/scripts/kconfig/kxgettext.c
new file mode 100644
index 0000000..abee55c
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/scripts/kconfig/kxgettext.c
@@ -0,0 +1,227 @@
+/*
+ * Arnaldo Carvalho de Melo <acme@conectiva.com.br>, 2005
+ *
+ * Released under the terms of the GNU GPL v2.0
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#define LKC_DIRECT_LINK
+#include "lkc.h"
+
+static char *escape(const char* text, char *bf, int len)
+{
+ char *bfp = bf;
+ int multiline = strchr(text, '\n') != NULL;
+ int eol = 0;
+ int textlen = strlen(text);
+
+ if ((textlen > 0) && (text[textlen-1] == '\n'))
+ eol = 1;
+
+ *bfp++ = '"';
+ --len;
+
+ if (multiline) {
+ *bfp++ = '"';
+ *bfp++ = '\n';
+ *bfp++ = '"';
+ len -= 3;
+ }
+
+ while (*text != '\0' && len > 1) {
+ if (*text == '"')
+ *bfp++ = '\\';
+ else if (*text == '\n') {
+ *bfp++ = '\\';
+ *bfp++ = 'n';
+ *bfp++ = '"';
+ *bfp++ = '\n';
+ *bfp++ = '"';
+ len -= 5;
+ ++text;
+ goto next;
+ }
+ *bfp++ = *text++;
+next:
+ --len;
+ }
+
+ if (multiline && eol)
+ bfp -= 3;
+
+ *bfp++ = '"';
+ *bfp = '\0';
+
+ return bf;
+}
+
+struct file_line {
+ struct file_line *next;
+ char* file;
+ int lineno;
+};
+
+static struct file_line *file_line__new(char *file, int lineno)
+{
+ struct file_line *self = malloc(sizeof(*self));
+
+ if (self == NULL)
+ goto out;
+
+ self->file = file;
+ self->lineno = lineno;
+ self->next = NULL;
+out:
+ return self;
+}
+
+struct message {
+ const char *msg;
+ const char *option;
+ struct message *next;
+ struct file_line *files;
+};
+
+static struct message *message__list;
+
+static struct message *message__new(const char *msg, char *option, char *file, int lineno)
+{
+ struct message *self = malloc(sizeof(*self));
+
+ if (self == NULL)
+ goto out;
+
+ self->files = file_line__new(file, lineno);
+ if (self->files == NULL)
+ goto out_fail;
+
+ self->msg = strdup(msg);
+ if (self->msg == NULL)
+ goto out_fail_msg;
+
+ self->option = option;
+ self->next = NULL;
+out:
+ return self;
+out_fail_msg:
+ free(self->files);
+out_fail:
+ free(self);
+ self = NULL;
+ goto out;
+}
+
+static struct message *mesage__find(const char *msg)
+{
+ struct message *m = message__list;
+
+ while (m != NULL) {
+ if (strcmp(m->msg, msg) == 0)
+ break;
+ m = m->next;
+ }
+
+ return m;
+}
+
+static int message__add_file_line(struct message *self, char *file, int lineno)
+{
+ int rc = -1;
+ struct file_line *fl = file_line__new(file, lineno);
+
+ if (fl == NULL)
+ goto out;
+
+ fl->next = self->files;
+ self->files = fl;
+ rc = 0;
+out:
+ return rc;
+}
+
+static int message__add(const char *msg, char *option, char *file, int lineno)
+{
+ int rc = 0;
+ char bf[16384];
+ char *escaped = escape(msg, bf, sizeof(bf));
+ struct message *m = mesage__find(escaped);
+
+ if (m != NULL)
+ rc = message__add_file_line(m, file, lineno);
+ else {
+ m = message__new(escaped, option, file, lineno);
+
+ if (m != NULL) {
+ m->next = message__list;
+ message__list = m;
+ } else
+ rc = -1;
+ }
+ return rc;
+}
+
+void menu_build_message_list(struct menu *menu)
+{
+ struct menu *child;
+
+ message__add(menu_get_prompt(menu), NULL,
+ menu->file == NULL ? "Root Menu" : menu->file->name,
+ menu->lineno);
+
+ if (menu->sym != NULL && menu->sym->help != NULL)
+ message__add(menu->sym->help, menu->sym->name,
+ menu->file == NULL ? "Root Menu" : menu->file->name,
+ menu->lineno);
+
+ for (child = menu->list; child != NULL; child = child->next)
+ if (child->prompt != NULL)
+ menu_build_message_list(child);
+}
+
+static void message__print_file_lineno(struct message *self)
+{
+ struct file_line *fl = self->files;
+
+ putchar('\n');
+ if (self->option != NULL)
+ printf("# %s:00000\n", self->option);
+
+ printf("#: %s:%d", fl->file, fl->lineno);
+ fl = fl->next;
+
+ while (fl != NULL) {
+ printf(", %s:%d", fl->file, fl->lineno);
+ fl = fl->next;
+ }
+
+ putchar('\n');
+}
+
+static void message__print_gettext_msgid_msgstr(struct message *self)
+{
+ message__print_file_lineno(self);
+
+ printf("msgid %s\n"
+ "msgstr \"\"\n", self->msg);
+}
+
+void menu__xgettext(void)
+{
+ struct message *m = message__list;
+
+ while (m != NULL) {
+ message__print_gettext_msgid_msgstr(m);
+ m = m->next;
+ }
+}
+
+int main(int ac, char **av)
+{
+ conf_parse(av[1]);
+
+ menu_build_message_list(menu_get_root_menu(NULL));
+ menu__xgettext();
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/scripts/kconfig/lex.zconf.c_shipped b/i/pc104/initrd/conf/busybox/scripts/kconfig/lex.zconf.c_shipped
new file mode 100644
index 0000000..7a4ca2c
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/scripts/kconfig/lex.zconf.c_shipped
@@ -0,0 +1,2317 @@
+
+#line 3 "scripts/kconfig/lex.zconf.c"
+
+#define YY_INT_ALIGNED short int
+
+/* A lexical scanner generated by flex */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+#define YY_FLEX_SUBMINOR_VERSION 31
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+/* First, we deal with platform-specific or compiler-specific issues. */
+
+/* begin standard C headers. */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/* end standard C headers. */
+
+/* flex integer type definitions */
+
+#ifndef FLEXINT_H
+#define FLEXINT_H
+
+/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
+
+#if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L
+#include <inttypes.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+#else
+typedef signed char flex_int8_t;
+typedef short int flex_int16_t;
+typedef int flex_int32_t;
+typedef unsigned char flex_uint8_t;
+typedef unsigned short int flex_uint16_t;
+typedef unsigned int flex_uint32_t;
+#endif /* ! C99 */
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX (4294967295U)
+#endif
+
+#endif /* ! FLEXINT_H */
+
+#ifdef __cplusplus
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else /* ! __cplusplus */
+
+#if __STDC__
+
+#define YY_USE_CONST
+
+#endif /* __STDC__ */
+#endif /* ! __cplusplus */
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+
+/* Promotes a possibly negative, possibly signed char to an unsigned
+ * integer for use as an array index. If the signed char is negative,
+ * we want to instead treat it as an 8-bit unsigned char, hence the
+ * double cast.
+ */
+#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
+
+/* Enter a start condition. This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN (yy_start) = 1 + 2 *
+
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state. The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START (((yy_start) - 1) / 2)
+#define YYSTATE YY_START
+
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE zconfrestart(zconfin )
+
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#define YY_BUF_SIZE 16384
+#endif
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+extern int zconfleng;
+
+extern FILE *zconfin, *zconfout;
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+ #define YY_LESS_LINENO(n)
+
+/* Return all but the first "n" matched characters back to the input stream. */
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up zconftext. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ *yy_cp = (yy_hold_char); \
+ YY_RESTORE_YY_MORE_OFFSET \
+ (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \
+ YY_DO_BEFORE_ACTION; /* set up zconftext again */ \
+ } \
+ while ( 0 )
+
+#define unput(c) yyunput( c, (yytext_ptr) )
+
+/* The following is because we cannot portably get our hands on size_t
+ * (without autoconf's help, which isn't available because we want
+ * flex-generated scanners to compile on their own).
+ */
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef unsigned int yy_size_t;
+#endif
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+ {
+ FILE *yy_input_file;
+
+ char *yy_ch_buf; /* input buffer */
+ char *yy_buf_pos; /* current position in input buffer */
+
+ /* Size of input buffer in bytes, not including room for EOB
+ * characters.
+ */
+ yy_size_t yy_buf_size;
+
+ /* Number of characters read into yy_ch_buf, not including EOB
+ * characters.
+ */
+ int yy_n_chars;
+
+ /* Whether we "own" the buffer - i.e., we know we created it,
+ * and can realloc() it to grow it, and should free() it to
+ * delete it.
+ */
+ int yy_is_our_buffer;
+
+ /* Whether this is an "interactive" input source; if so, and
+ * if we're using stdio for input, then we want to use getc()
+ * instead of fread(), to make sure we stop fetching input after
+ * each newline.
+ */
+ int yy_is_interactive;
+
+ /* Whether we're considered to be at the beginning of a line.
+ * If so, '^' rules will be active on the next match, otherwise
+ * not.
+ */
+ int yy_at_bol;
+
+ int yy_bs_lineno; /**< The line count. */
+ int yy_bs_column; /**< The column count. */
+
+ /* Whether to try to fill the input buffer when we reach the
+ * end of it.
+ */
+ int yy_fill_buffer;
+
+ int yy_buffer_status;
+
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+ /* When an EOF's been seen but there's still some text to process
+ * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+ * shouldn't try reading from the input source any more. We might
+ * still have a bunch of tokens to match, though, because of
+ * possible backing-up.
+ *
+ * When we actually see the EOF, we change the status to "new"
+ * (via zconfrestart()), so that the user can continue scanning by
+ * just pointing zconfin at a new input file.
+ */
+#define YY_BUFFER_EOF_PENDING 2
+
+ };
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+/* Stack of input buffers. */
+static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */
+static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */
+static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ *
+ * Returns the top of the stack, or NULL.
+ */
+#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \
+ ? (yy_buffer_stack)[(yy_buffer_stack_top)] \
+ : NULL)
+
+/* Same as previous macro, but useful when we know that the buffer stack is not
+ * NULL or when we need an lvalue. For internal use only.
+ */
+#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)]
+
+/* yy_hold_char holds the character lost when zconftext is formed. */
+static char yy_hold_char;
+static int yy_n_chars; /* number of characters read into yy_ch_buf */
+int zconfleng;
+
+/* Points to current character in buffer. */
+static char *yy_c_buf_p = (char *) 0;
+static int yy_init = 1; /* whether we need to initialize */
+static int yy_start = 0; /* start state number */
+
+/* Flag which is used to allow zconfwrap()'s to do buffer switches
+ * instead of setting up a fresh zconfin. A bit of a hack ...
+ */
+static int yy_did_buffer_switch_on_eof;
+
+void zconfrestart (FILE *input_file );
+void zconf_switch_to_buffer (YY_BUFFER_STATE new_buffer );
+YY_BUFFER_STATE zconf_create_buffer (FILE *file,int size );
+void zconf_delete_buffer (YY_BUFFER_STATE b );
+void zconf_flush_buffer (YY_BUFFER_STATE b );
+void zconfpush_buffer_state (YY_BUFFER_STATE new_buffer );
+void zconfpop_buffer_state (void );
+
+static void zconfensure_buffer_stack (void );
+static void zconf_load_buffer_state (void );
+static void zconf_init_buffer (YY_BUFFER_STATE b,FILE *file );
+
+#define YY_FLUSH_BUFFER zconf_flush_buffer(YY_CURRENT_BUFFER )
+
+YY_BUFFER_STATE zconf_scan_buffer (char *base,yy_size_t size );
+YY_BUFFER_STATE zconf_scan_string (yyconst char *yy_str );
+YY_BUFFER_STATE zconf_scan_bytes (yyconst char *bytes,int len );
+
+void *zconfalloc (yy_size_t );
+void *zconfrealloc (void *,yy_size_t );
+void zconffree (void * );
+
+#define yy_new_buffer zconf_create_buffer
+
+#define yy_set_interactive(is_interactive) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){ \
+ zconfensure_buffer_stack (); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ zconf_create_buffer(zconfin,YY_BUF_SIZE ); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \
+ }
+
+#define yy_set_bol(at_bol) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){\
+ zconfensure_buffer_stack (); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ zconf_create_buffer(zconfin,YY_BUF_SIZE ); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \
+ }
+
+#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol)
+
+/* Begin user sect3 */
+
+#define zconfwrap() 1
+#define YY_SKIP_YYWRAP
+
+typedef unsigned char YY_CHAR;
+
+FILE *zconfin = (FILE *) 0, *zconfout = (FILE *) 0;
+
+typedef int yy_state_type;
+
+extern int zconflineno;
+
+int zconflineno = 1;
+
+extern char *zconftext;
+#define yytext_ptr zconftext
+static yyconst flex_int16_t yy_nxt[][17] =
+ {
+ {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0
+ },
+
+ {
+ 11, 12, 13, 14, 12, 12, 15, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12
+ },
+
+ {
+ 11, 12, 13, 14, 12, 12, 15, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12
+ },
+
+ {
+ 11, 16, 16, 17, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 18, 16, 16, 16
+ },
+
+ {
+ 11, 16, 16, 17, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 18, 16, 16, 16
+
+ },
+
+ {
+ 11, 19, 20, 21, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19
+ },
+
+ {
+ 11, 19, 20, 21, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19
+ },
+
+ {
+ 11, 22, 22, 23, 22, 24, 22, 22, 24, 22,
+ 22, 22, 22, 22, 22, 25, 22
+ },
+
+ {
+ 11, 22, 22, 23, 22, 24, 22, 22, 24, 22,
+ 22, 22, 22, 22, 22, 25, 22
+ },
+
+ {
+ 11, 26, 26, 27, 28, 29, 30, 31, 29, 32,
+ 33, 34, 35, 35, 36, 37, 38
+
+ },
+
+ {
+ 11, 26, 26, 27, 28, 29, 30, 31, 29, 32,
+ 33, 34, 35, 35, 36, 37, 38
+ },
+
+ {
+ -11, -11, -11, -11, -11, -11, -11, -11, -11, -11,
+ -11, -11, -11, -11, -11, -11, -11
+ },
+
+ {
+ 11, -12, -12, -12, -12, -12, -12, -12, -12, -12,
+ -12, -12, -12, -12, -12, -12, -12
+ },
+
+ {
+ 11, -13, 39, 40, -13, -13, 41, -13, -13, -13,
+ -13, -13, -13, -13, -13, -13, -13
+ },
+
+ {
+ 11, -14, -14, -14, -14, -14, -14, -14, -14, -14,
+ -14, -14, -14, -14, -14, -14, -14
+
+ },
+
+ {
+ 11, 42, 42, 43, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42
+ },
+
+ {
+ 11, -16, -16, -16, -16, -16, -16, -16, -16, -16,
+ -16, -16, -16, -16, -16, -16, -16
+ },
+
+ {
+ 11, -17, -17, -17, -17, -17, -17, -17, -17, -17,
+ -17, -17, -17, -17, -17, -17, -17
+ },
+
+ {
+ 11, -18, -18, -18, -18, -18, -18, -18, -18, -18,
+ -18, -18, -18, 44, -18, -18, -18
+ },
+
+ {
+ 11, 45, 45, -19, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45
+
+ },
+
+ {
+ 11, -20, 46, 47, -20, -20, -20, -20, -20, -20,
+ -20, -20, -20, -20, -20, -20, -20
+ },
+
+ {
+ 11, 48, -21, -21, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48
+ },
+
+ {
+ 11, 49, 49, 50, 49, -22, 49, 49, -22, 49,
+ 49, 49, 49, 49, 49, -22, 49
+ },
+
+ {
+ 11, -23, -23, -23, -23, -23, -23, -23, -23, -23,
+ -23, -23, -23, -23, -23, -23, -23
+ },
+
+ {
+ 11, -24, -24, -24, -24, -24, -24, -24, -24, -24,
+ -24, -24, -24, -24, -24, -24, -24
+
+ },
+
+ {
+ 11, 51, 51, 52, 51, 51, 51, 51, 51, 51,
+ 51, 51, 51, 51, 51, 51, 51
+ },
+
+ {
+ 11, -26, -26, -26, -26, -26, -26, -26, -26, -26,
+ -26, -26, -26, -26, -26, -26, -26
+ },
+
+ {
+ 11, -27, -27, -27, -27, -27, -27, -27, -27, -27,
+ -27, -27, -27, -27, -27, -27, -27
+ },
+
+ {
+ 11, -28, -28, -28, -28, -28, -28, -28, -28, -28,
+ -28, -28, -28, -28, 53, -28, -28
+ },
+
+ {
+ 11, -29, -29, -29, -29, -29, -29, -29, -29, -29,
+ -29, -29, -29, -29, -29, -29, -29
+
+ },
+
+ {
+ 11, 54, 54, -30, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54
+ },
+
+ {
+ 11, -31, -31, -31, -31, -31, -31, 55, -31, -31,
+ -31, -31, -31, -31, -31, -31, -31
+ },
+
+ {
+ 11, -32, -32, -32, -32, -32, -32, -32, -32, -32,
+ -32, -32, -32, -32, -32, -32, -32
+ },
+
+ {
+ 11, -33, -33, -33, -33, -33, -33, -33, -33, -33,
+ -33, -33, -33, -33, -33, -33, -33
+ },
+
+ {
+ 11, -34, -34, -34, -34, -34, -34, -34, -34, -34,
+ -34, 56, 57, 57, -34, -34, -34
+
+ },
+
+ {
+ 11, -35, -35, -35, -35, -35, -35, -35, -35, -35,
+ -35, 57, 57, 57, -35, -35, -35
+ },
+
+ {
+ 11, -36, -36, -36, -36, -36, -36, -36, -36, -36,
+ -36, -36, -36, -36, -36, -36, -36
+ },
+
+ {
+ 11, -37, -37, 58, -37, -37, -37, -37, -37, -37,
+ -37, -37, -37, -37, -37, -37, -37
+ },
+
+ {
+ 11, -38, -38, -38, -38, -38, -38, -38, -38, -38,
+ -38, -38, -38, -38, -38, -38, 59
+ },
+
+ {
+ 11, -39, 39, 40, -39, -39, 41, -39, -39, -39,
+ -39, -39, -39, -39, -39, -39, -39
+
+ },
+
+ {
+ 11, -40, -40, -40, -40, -40, -40, -40, -40, -40,
+ -40, -40, -40, -40, -40, -40, -40
+ },
+
+ {
+ 11, 42, 42, 43, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42
+ },
+
+ {
+ 11, 42, 42, 43, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42
+ },
+
+ {
+ 11, -43, -43, -43, -43, -43, -43, -43, -43, -43,
+ -43, -43, -43, -43, -43, -43, -43
+ },
+
+ {
+ 11, -44, -44, -44, -44, -44, -44, -44, -44, -44,
+ -44, -44, -44, 44, -44, -44, -44
+
+ },
+
+ {
+ 11, 45, 45, -45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45
+ },
+
+ {
+ 11, -46, 46, 47, -46, -46, -46, -46, -46, -46,
+ -46, -46, -46, -46, -46, -46, -46
+ },
+
+ {
+ 11, 48, -47, -47, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48
+ },
+
+ {
+ 11, -48, -48, -48, -48, -48, -48, -48, -48, -48,
+ -48, -48, -48, -48, -48, -48, -48
+ },
+
+ {
+ 11, 49, 49, 50, 49, -49, 49, 49, -49, 49,
+ 49, 49, 49, 49, 49, -49, 49
+
+ },
+
+ {
+ 11, -50, -50, -50, -50, -50, -50, -50, -50, -50,
+ -50, -50, -50, -50, -50, -50, -50
+ },
+
+ {
+ 11, -51, -51, 52, -51, -51, -51, -51, -51, -51,
+ -51, -51, -51, -51, -51, -51, -51
+ },
+
+ {
+ 11, -52, -52, -52, -52, -52, -52, -52, -52, -52,
+ -52, -52, -52, -52, -52, -52, -52
+ },
+
+ {
+ 11, -53, -53, -53, -53, -53, -53, -53, -53, -53,
+ -53, -53, -53, -53, -53, -53, -53
+ },
+
+ {
+ 11, 54, 54, -54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54
+
+ },
+
+ {
+ 11, -55, -55, -55, -55, -55, -55, -55, -55, -55,
+ -55, -55, -55, -55, -55, -55, -55
+ },
+
+ {
+ 11, -56, -56, -56, -56, -56, -56, -56, -56, -56,
+ -56, 60, 57, 57, -56, -56, -56
+ },
+
+ {
+ 11, -57, -57, -57, -57, -57, -57, -57, -57, -57,
+ -57, 57, 57, 57, -57, -57, -57
+ },
+
+ {
+ 11, -58, -58, -58, -58, -58, -58, -58, -58, -58,
+ -58, -58, -58, -58, -58, -58, -58
+ },
+
+ {
+ 11, -59, -59, -59, -59, -59, -59, -59, -59, -59,
+ -59, -59, -59, -59, -59, -59, -59
+
+ },
+
+ {
+ 11, -60, -60, -60, -60, -60, -60, -60, -60, -60,
+ -60, 57, 57, 57, -60, -60, -60
+ },
+
+ } ;
+
+static yy_state_type yy_get_previous_state (void );
+static yy_state_type yy_try_NUL_trans (yy_state_type current_state );
+static int yy_get_next_buffer (void );
+static void yy_fatal_error (yyconst char msg[] );
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up zconftext.
+ */
+#define YY_DO_BEFORE_ACTION \
+ (yytext_ptr) = yy_bp; \
+ zconfleng = (size_t) (yy_cp - yy_bp); \
+ (yy_hold_char) = *yy_cp; \
+ *yy_cp = '\0'; \
+ (yy_c_buf_p) = yy_cp;
+
+#define YY_NUM_RULES 33
+#define YY_END_OF_BUFFER 34
+/* This struct is not used in this scanner,
+ but its presence is necessary. */
+struct yy_trans_info
+ {
+ flex_int32_t yy_verify;
+ flex_int32_t yy_nxt;
+ };
+static yyconst flex_int16_t yy_accept[61] =
+ { 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 34, 5, 4, 2, 3, 7, 8, 6, 32, 29,
+ 31, 24, 28, 27, 26, 22, 17, 13, 16, 20,
+ 22, 11, 12, 19, 19, 14, 22, 22, 4, 2,
+ 3, 3, 1, 6, 32, 29, 31, 30, 24, 23,
+ 26, 25, 15, 20, 9, 19, 19, 21, 10, 18
+ } ;
+
+static yyconst flex_int32_t yy_ec[256] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 2, 3,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 2, 4, 5, 6, 1, 1, 7, 8, 9,
+ 10, 1, 1, 1, 11, 12, 12, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 1, 1, 1,
+ 14, 1, 1, 1, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 1, 15, 1, 1, 13, 1, 13, 13, 13, 13,
+
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 1, 16, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1
+ } ;
+
+extern int zconf_flex_debug;
+int zconf_flex_debug = 0;
+
+/* The intent behind this definition is that it'll catch
+ * any uses of REJECT which flex missed.
+ */
+#define REJECT reject_used_but_not_detected
+#define yymore() yymore_used_but_not_detected
+#define YY_MORE_ADJ 0
+#define YY_RESTORE_YY_MORE_OFFSET
+char *zconftext;
+
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define LKC_DIRECT_LINK
+#include "lkc.h"
+
+#define START_STRSIZE 16
+
+static struct {
+ struct file *file;
+ int lineno;
+} current_pos;
+
+static char *text;
+static int text_size, text_asize;
+
+struct buffer {
+ struct buffer *parent;
+ YY_BUFFER_STATE state;
+};
+
+struct buffer *current_buf;
+
+static int last_ts, first_ts;
+
+static void zconf_endhelp(void);
+static void zconf_endfile(void);
+
+void new_string(void)
+{
+ text = malloc(START_STRSIZE);
+ text_asize = START_STRSIZE;
+ text_size = 0;
+ *text = 0;
+}
+
+void append_string(const char *str, int size)
+{
+ int new_size = text_size + size + 1;
+ if (new_size > text_asize) {
+ new_size += START_STRSIZE - 1;
+ new_size &= -START_STRSIZE;
+ text = realloc(text, new_size);
+ text_asize = new_size;
+ }
+ memcpy(text + text_size, str, size);
+ text_size += size;
+ text[text_size] = 0;
+}
+
+void alloc_string(const char *str, int size)
+{
+ text = malloc(size + 1);
+ memcpy(text, str, size);
+ text[size] = 0;
+}
+
+#define INITIAL 0
+#define COMMAND 1
+#define HELP 2
+#define STRING 3
+#define PARAM 4
+
+#ifndef YY_NO_UNISTD_H
+/* Special case for "unistd.h", since it is non-ANSI. We include it way
+ * down here because we want the user's section 1 to have been scanned first.
+ * The user has a chance to override it with an option.
+ */
+#include <unistd.h>
+#endif
+
+#ifndef YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#endif
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int zconfwrap (void );
+#else
+extern int zconfwrap (void );
+#endif
+#endif
+
+ static void yyunput (int c,char *buf_ptr );
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char *,yyconst char *,int );
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * );
+#endif
+
+#ifndef YY_NO_INPUT
+
+#ifdef __cplusplus
+static int yyinput (void );
+#else
+static int input (void );
+#endif
+
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#define YY_READ_BUF_SIZE 8192
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+#ifndef ECHO
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO (void) fwrite( zconftext, zconfleng, 1, zconfout )
+#endif
+
+/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+ errno=0; \
+ while ( (result = read( fileno(zconfin), (char *) buf, max_size )) < 0 ) \
+ { \
+ if( errno != EINTR) \
+ { \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ break; \
+ } \
+ errno=0; \
+ clearerr(zconfin); \
+ }\
+\
+
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg )
+#endif
+
+/* end tables serialization structures and prototypes */
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+
+extern int zconflex (void);
+
+#define YY_DECL int zconflex (void)
+#endif /* !YY_DECL */
+
+/* Code executed at the beginning of each rule, after zconftext and zconfleng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK break;
+#endif
+
+#define YY_RULE_SETUP \
+ YY_USER_ACTION
+
+/** The main scanner function which does all the work.
+ */
+YY_DECL
+{
+ register yy_state_type yy_current_state;
+ register char *yy_cp, *yy_bp;
+ register int yy_act;
+
+ int str = 0;
+ int ts, i;
+
+ if ( (yy_init) )
+ {
+ (yy_init) = 0;
+
+#ifdef YY_USER_INIT
+ YY_USER_INIT;
+#endif
+
+ if ( ! (yy_start) )
+ (yy_start) = 1; /* first start state */
+
+ if ( ! zconfin )
+ zconfin = stdin;
+
+ if ( ! zconfout )
+ zconfout = stdout;
+
+ if ( ! YY_CURRENT_BUFFER ) {
+ zconfensure_buffer_stack ();
+ YY_CURRENT_BUFFER_LVALUE =
+ zconf_create_buffer(zconfin,YY_BUF_SIZE );
+ }
+
+ zconf_load_buffer_state( );
+ }
+
+ while ( 1 ) /* loops until end-of-file is reached */
+ {
+ yy_cp = (yy_c_buf_p);
+
+ /* Support of zconftext. */
+ *yy_cp = (yy_hold_char);
+
+ /* yy_bp points to the position in yy_ch_buf of the start of
+ * the current run.
+ */
+ yy_bp = yy_cp;
+
+ yy_current_state = (yy_start);
+yy_match:
+ while ( (yy_current_state = yy_nxt[yy_current_state][ yy_ec[YY_SC_TO_UI(*yy_cp)] ]) > 0 )
+ ++yy_cp;
+
+ yy_current_state = -yy_current_state;
+
+yy_find_action:
+ yy_act = yy_accept[yy_current_state];
+
+ YY_DO_BEFORE_ACTION;
+
+do_action: /* This label is used only to access EOF actions. */
+
+ switch ( yy_act )
+ { /* beginning of action switch */
+case 1:
+/* rule 1 can match eol */
+case 2:
+/* rule 2 can match eol */
+YY_RULE_SETUP
+{
+ current_file->lineno++;
+ return T_EOL;
+}
+ YY_BREAK
+case 3:
+YY_RULE_SETUP
+
+ YY_BREAK
+case 4:
+YY_RULE_SETUP
+{
+ BEGIN(COMMAND);
+}
+ YY_BREAK
+case 5:
+YY_RULE_SETUP
+{
+ unput(zconftext[0]);
+ BEGIN(COMMAND);
+}
+ YY_BREAK
+
+case 6:
+YY_RULE_SETUP
+{
+ struct kconf_id *id = kconf_id_lookup(zconftext, zconfleng);
+ BEGIN(PARAM);
+ current_pos.file = current_file;
+ current_pos.lineno = current_file->lineno;
+ if (id && id->flags & TF_COMMAND) {
+ zconflval.id = id;
+ return id->token;
+ }
+ alloc_string(zconftext, zconfleng);
+ zconflval.string = text;
+ return T_WORD;
+ }
+ YY_BREAK
+case 7:
+YY_RULE_SETUP
+
+ YY_BREAK
+case 8:
+/* rule 8 can match eol */
+YY_RULE_SETUP
+{
+ BEGIN(INITIAL);
+ current_file->lineno++;
+ return T_EOL;
+ }
+ YY_BREAK
+
+case 9:
+YY_RULE_SETUP
+return T_AND;
+ YY_BREAK
+case 10:
+YY_RULE_SETUP
+return T_OR;
+ YY_BREAK
+case 11:
+YY_RULE_SETUP
+return T_OPEN_PAREN;
+ YY_BREAK
+case 12:
+YY_RULE_SETUP
+return T_CLOSE_PAREN;
+ YY_BREAK
+case 13:
+YY_RULE_SETUP
+return T_NOT;
+ YY_BREAK
+case 14:
+YY_RULE_SETUP
+return T_EQUAL;
+ YY_BREAK
+case 15:
+YY_RULE_SETUP
+return T_UNEQUAL;
+ YY_BREAK
+case 16:
+YY_RULE_SETUP
+{
+ str = zconftext[0];
+ new_string();
+ BEGIN(STRING);
+ }
+ YY_BREAK
+case 17:
+/* rule 17 can match eol */
+YY_RULE_SETUP
+BEGIN(INITIAL); current_file->lineno++; return T_EOL;
+ YY_BREAK
+case 18:
+YY_RULE_SETUP
+/* ignore */
+ YY_BREAK
+case 19:
+YY_RULE_SETUP
+{
+ struct kconf_id *id = kconf_id_lookup(zconftext, zconfleng);
+ if (id && id->flags & TF_PARAM) {
+ zconflval.id = id;
+ return id->token;
+ }
+ alloc_string(zconftext, zconfleng);
+ zconflval.string = text;
+ return T_WORD;
+ }
+ YY_BREAK
+case 20:
+YY_RULE_SETUP
+/* comment */
+ YY_BREAK
+case 21:
+/* rule 21 can match eol */
+YY_RULE_SETUP
+current_file->lineno++;
+ YY_BREAK
+case 22:
+YY_RULE_SETUP
+
+ YY_BREAK
+case YY_STATE_EOF(PARAM):
+{
+ BEGIN(INITIAL);
+ }
+ YY_BREAK
+
+case 23:
+/* rule 23 can match eol */
+*yy_cp = (yy_hold_char); /* undo effects of setting up zconftext */
+(yy_c_buf_p) = yy_cp -= 1;
+YY_DO_BEFORE_ACTION; /* set up zconftext again */
+YY_RULE_SETUP
+{
+ append_string(zconftext, zconfleng);
+ zconflval.string = text;
+ return T_WORD_QUOTE;
+ }
+ YY_BREAK
+case 24:
+YY_RULE_SETUP
+{
+ append_string(zconftext, zconfleng);
+ }
+ YY_BREAK
+case 25:
+/* rule 25 can match eol */
+*yy_cp = (yy_hold_char); /* undo effects of setting up zconftext */
+(yy_c_buf_p) = yy_cp -= 1;
+YY_DO_BEFORE_ACTION; /* set up zconftext again */
+YY_RULE_SETUP
+{
+ append_string(zconftext + 1, zconfleng - 1);
+ zconflval.string = text;
+ return T_WORD_QUOTE;
+ }
+ YY_BREAK
+case 26:
+YY_RULE_SETUP
+{
+ append_string(zconftext + 1, zconfleng - 1);
+ }
+ YY_BREAK
+case 27:
+YY_RULE_SETUP
+{
+ if (str == zconftext[0]) {
+ BEGIN(PARAM);
+ zconflval.string = text;
+ return T_WORD_QUOTE;
+ } else
+ append_string(zconftext, 1);
+ }
+ YY_BREAK
+case 28:
+/* rule 28 can match eol */
+YY_RULE_SETUP
+{
+ printf("%s:%d:warning: multi-line strings not supported\n", zconf_curname(), zconf_lineno());
+ current_file->lineno++;
+ BEGIN(INITIAL);
+ return T_EOL;
+ }
+ YY_BREAK
+case YY_STATE_EOF(STRING):
+{
+ BEGIN(INITIAL);
+ }
+ YY_BREAK
+
+case 29:
+YY_RULE_SETUP
+{
+ ts = 0;
+ for (i = 0; i < zconfleng; i++) {
+ if (zconftext[i] == '\t')
+ ts = (ts & ~7) + 8;
+ else
+ ts++;
+ }
+ last_ts = ts;
+ if (first_ts) {
+ if (ts < first_ts) {
+ zconf_endhelp();
+ return T_HELPTEXT;
+ }
+ ts -= first_ts;
+ while (ts > 8) {
+ append_string(" ", 8);
+ ts -= 8;
+ }
+ append_string(" ", ts);
+ }
+ }
+ YY_BREAK
+case 30:
+/* rule 30 can match eol */
+*yy_cp = (yy_hold_char); /* undo effects of setting up zconftext */
+(yy_c_buf_p) = yy_cp -= 1;
+YY_DO_BEFORE_ACTION; /* set up zconftext again */
+YY_RULE_SETUP
+{
+ current_file->lineno++;
+ zconf_endhelp();
+ return T_HELPTEXT;
+ }
+ YY_BREAK
+case 31:
+/* rule 31 can match eol */
+YY_RULE_SETUP
+{
+ current_file->lineno++;
+ append_string("\n", 1);
+ }
+ YY_BREAK
+case 32:
+YY_RULE_SETUP
+{
+ append_string(zconftext, zconfleng);
+ if (!first_ts)
+ first_ts = last_ts;
+ }
+ YY_BREAK
+case YY_STATE_EOF(HELP):
+{
+ zconf_endhelp();
+ return T_HELPTEXT;
+ }
+ YY_BREAK
+
+case YY_STATE_EOF(INITIAL):
+case YY_STATE_EOF(COMMAND):
+{
+ if (current_file) {
+ zconf_endfile();
+ return T_EOL;
+ }
+ fclose(zconfin);
+ yyterminate();
+}
+ YY_BREAK
+case 33:
+YY_RULE_SETUP
+YY_FATAL_ERROR( "flex scanner jammed" );
+ YY_BREAK
+
+ case YY_END_OF_BUFFER:
+ {
+ /* Amount of text matched not including the EOB char. */
+ int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1;
+
+ /* Undo the effects of YY_DO_BEFORE_ACTION. */
+ *yy_cp = (yy_hold_char);
+ YY_RESTORE_YY_MORE_OFFSET
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW )
+ {
+ /* We're scanning a new file or input source. It's
+ * possible that this happened because the user
+ * just pointed zconfin at a new source and called
+ * zconflex(). If so, then we have to assure
+ * consistency between YY_CURRENT_BUFFER and our
+ * globals. Here is the right place to do so, because
+ * this is the first action (other than possibly a
+ * back-up) that will match for the new input source.
+ */
+ (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ YY_CURRENT_BUFFER_LVALUE->yy_input_file = zconfin;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL;
+ }
+
+ /* Note that here we test for yy_c_buf_p "<=" to the position
+ * of the first EOB in the buffer, since yy_c_buf_p will
+ * already have been incremented past the NUL character
+ * (since all states make transitions on EOB to the
+ * end-of-buffer state). Contrast this with the test
+ * in input().
+ */
+ if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
+ { /* This was really a NUL. */
+ yy_state_type yy_next_state;
+
+ (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( );
+
+ /* Okay, we're now positioned to make the NUL
+ * transition. We couldn't have
+ * yy_get_previous_state() go ahead and do it
+ * for us because it doesn't know how to deal
+ * with the possibility of jamming (and we don't
+ * want to build jamming into it because then it
+ * will run more slowly).
+ */
+
+ yy_next_state = yy_try_NUL_trans( yy_current_state );
+
+ yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+
+ if ( yy_next_state )
+ {
+ /* Consume the NUL. */
+ yy_cp = ++(yy_c_buf_p);
+ yy_current_state = yy_next_state;
+ goto yy_match;
+ }
+
+ else
+ {
+ yy_cp = (yy_c_buf_p);
+ goto yy_find_action;
+ }
+ }
+
+ else switch ( yy_get_next_buffer( ) )
+ {
+ case EOB_ACT_END_OF_FILE:
+ {
+ (yy_did_buffer_switch_on_eof) = 0;
+
+ if ( zconfwrap( ) )
+ {
+ /* Note: because we've taken care in
+ * yy_get_next_buffer() to have set up
+ * zconftext, we can now set up
+ * yy_c_buf_p so that if some total
+ * hoser (like flex itself) wants to
+ * call the scanner after we return the
+ * YY_NULL, it'll still work - another
+ * YY_NULL will get returned.
+ */
+ (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ;
+
+ yy_act = YY_STATE_EOF(YY_START);
+ goto do_action;
+ }
+
+ else
+ {
+ if ( ! (yy_did_buffer_switch_on_eof) )
+ YY_NEW_FILE;
+ }
+ break;
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ (yy_c_buf_p) =
+ (yytext_ptr) + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( );
+
+ yy_cp = (yy_c_buf_p);
+ yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+ goto yy_match;
+
+ case EOB_ACT_LAST_MATCH:
+ (yy_c_buf_p) =
+ &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)];
+
+ yy_current_state = yy_get_previous_state( );
+
+ yy_cp = (yy_c_buf_p);
+ yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+ goto yy_find_action;
+ }
+ break;
+ }
+
+ default:
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--no action found" );
+ } /* end of action switch */
+ } /* end of scanning one token */
+} /* end of zconflex */
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ * EOB_ACT_LAST_MATCH -
+ * EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ * EOB_ACT_END_OF_FILE - end of file
+ */
+static int yy_get_next_buffer (void)
+{
+ register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
+ register char *source = (yytext_ptr);
+ register int number_to_move, i;
+ int ret_val;
+
+ if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] )
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--end of buffer missed" );
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 )
+ { /* Don't try to fill the buffer, so this is an EOF. */
+ if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 )
+ {
+ /* We matched a single character, the EOB, so
+ * treat this as a final EOF.
+ */
+ return EOB_ACT_END_OF_FILE;
+ }
+
+ else
+ {
+ /* We matched some text prior to the EOB, first
+ * process it.
+ */
+ return EOB_ACT_LAST_MATCH;
+ }
+ }
+
+ /* Try to read more data. */
+
+ /* First move last chars to start of buffer. */
+ number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1;
+
+ for ( i = 0; i < number_to_move; ++i )
+ *(dest++) = *(source++);
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+ /* don't do the read, it's not guaranteed to return an EOF,
+ * just force an EOF
+ */
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0;
+
+ else
+ {
+ size_t num_to_read =
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
+
+ while ( num_to_read <= 0 )
+ { /* Not enough room in the buffer - grow it. */
+
+ /* just a shorter name for the current buffer */
+ YY_BUFFER_STATE b = YY_CURRENT_BUFFER;
+
+ int yy_c_buf_p_offset =
+ (int) ((yy_c_buf_p) - b->yy_ch_buf);
+
+ if ( b->yy_is_our_buffer )
+ {
+ int new_size = b->yy_buf_size * 2;
+
+ if ( new_size <= 0 )
+ b->yy_buf_size += b->yy_buf_size / 8;
+ else
+ b->yy_buf_size *= 2;
+
+ b->yy_ch_buf = (char *)
+ /* Include room in for 2 EOB chars. */
+ zconfrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 );
+ }
+ else
+ /* Can't grow it, we don't own it. */
+ b->yy_ch_buf = 0;
+
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR(
+ "fatal error - scanner input buffer overflow" );
+
+ (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+ num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size -
+ number_to_move - 1;
+
+ }
+
+ if ( num_to_read > YY_READ_BUF_SIZE )
+ num_to_read = YY_READ_BUF_SIZE;
+
+ /* Read in more data. */
+ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
+ (yy_n_chars), num_to_read );
+
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+ }
+
+ if ( (yy_n_chars) == 0 )
+ {
+ if ( number_to_move == YY_MORE_ADJ )
+ {
+ ret_val = EOB_ACT_END_OF_FILE;
+ zconfrestart(zconfin );
+ }
+
+ else
+ {
+ ret_val = EOB_ACT_LAST_MATCH;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status =
+ YY_BUFFER_EOF_PENDING;
+ }
+ }
+
+ else
+ ret_val = EOB_ACT_CONTINUE_SCAN;
+
+ (yy_n_chars) += number_to_move;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR;
+
+ (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0];
+
+ return ret_val;
+}
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+ static yy_state_type yy_get_previous_state (void)
+{
+ register yy_state_type yy_current_state;
+ register char *yy_cp;
+
+ yy_current_state = (yy_start);
+
+ for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp )
+ {
+ yy_current_state = yy_nxt[yy_current_state][(*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1)];
+ }
+
+ return yy_current_state;
+}
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ * next_state = yy_try_NUL_trans( current_state );
+ */
+ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state )
+{
+ register int yy_is_jam;
+
+ yy_current_state = yy_nxt[yy_current_state][1];
+ yy_is_jam = (yy_current_state <= 0);
+
+ return yy_is_jam ? 0 : yy_current_state;
+}
+
+ static void yyunput (int c, register char * yy_bp )
+{
+ register char *yy_cp;
+
+ yy_cp = (yy_c_buf_p);
+
+ /* undo effects of setting up zconftext */
+ *yy_cp = (yy_hold_char);
+
+ if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+ { /* need to shift things up to make room */
+ /* +2 for EOB chars. */
+ register int number_to_move = (yy_n_chars) + 2;
+ register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2];
+ register char *source =
+ &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move];
+
+ while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+ *--dest = *--source;
+
+ yy_cp += (int) (dest - source);
+ yy_bp += (int) (dest - source);
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars =
+ (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_buf_size;
+
+ if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+ YY_FATAL_ERROR( "flex scanner push-back overflow" );
+ }
+
+ *--yy_cp = (char) c;
+
+ (yytext_ptr) = yy_bp;
+ (yy_hold_char) = *yy_cp;
+ (yy_c_buf_p) = yy_cp;
+}
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+ static int yyinput (void)
+#else
+ static int input (void)
+#endif
+
+{
+ int c;
+
+ *(yy_c_buf_p) = (yy_hold_char);
+
+ if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR )
+ {
+ /* yy_c_buf_p now points to the character we want to return.
+ * If this occurs *before* the EOB characters, then it's a
+ * valid NUL; if not, then we've hit the end of the buffer.
+ */
+ if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
+ /* This was really a NUL. */
+ *(yy_c_buf_p) = '\0';
+
+ else
+ { /* need more input */
+ int offset = (yy_c_buf_p) - (yytext_ptr);
+ ++(yy_c_buf_p);
+
+ switch ( yy_get_next_buffer( ) )
+ {
+ case EOB_ACT_LAST_MATCH:
+ /* This happens because yy_g_n_b()
+ * sees that we've accumulated a
+ * token and flags that we need to
+ * try matching the token before
+ * proceeding. But for input(),
+ * there's no matching to consider.
+ * So convert the EOB_ACT_LAST_MATCH
+ * to EOB_ACT_END_OF_FILE.
+ */
+
+ /* Reset buffer status. */
+ zconfrestart(zconfin );
+
+ /*FALLTHROUGH*/
+
+ case EOB_ACT_END_OF_FILE:
+ {
+ if ( zconfwrap( ) )
+ return EOF;
+
+ if ( ! (yy_did_buffer_switch_on_eof) )
+ YY_NEW_FILE;
+#ifdef __cplusplus
+ return yyinput();
+#else
+ return input();
+#endif
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ (yy_c_buf_p) = (yytext_ptr) + offset;
+ break;
+ }
+ }
+ }
+
+ c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */
+ *(yy_c_buf_p) = '\0'; /* preserve zconftext */
+ (yy_hold_char) = *++(yy_c_buf_p);
+
+ return c;
+}
+#endif /* ifndef YY_NO_INPUT */
+
+/** Immediately switch to a different input stream.
+ * @param input_file A readable stream.
+ *
+ * @note This function does not reset the start condition to @c INITIAL .
+ */
+ void zconfrestart (FILE * input_file )
+{
+
+ if ( ! YY_CURRENT_BUFFER ){
+ zconfensure_buffer_stack ();
+ YY_CURRENT_BUFFER_LVALUE =
+ zconf_create_buffer(zconfin,YY_BUF_SIZE );
+ }
+
+ zconf_init_buffer(YY_CURRENT_BUFFER,input_file );
+ zconf_load_buffer_state( );
+}
+
+/** Switch to a different input buffer.
+ * @param new_buffer The new input buffer.
+ *
+ */
+ void zconf_switch_to_buffer (YY_BUFFER_STATE new_buffer )
+{
+
+ /* TODO. We should be able to replace this entire function body
+ * with
+ * zconfpop_buffer_state();
+ * zconfpush_buffer_state(new_buffer);
+ */
+ zconfensure_buffer_stack ();
+ if ( YY_CURRENT_BUFFER == new_buffer )
+ return;
+
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *(yy_c_buf_p) = (yy_hold_char);
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+ }
+
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+ zconf_load_buffer_state( );
+
+ /* We don't actually know whether we did this switch during
+ * EOF (zconfwrap()) processing, but the only time this flag
+ * is looked at is after zconfwrap() is called, so it's safe
+ * to go ahead and always set it.
+ */
+ (yy_did_buffer_switch_on_eof) = 1;
+}
+
+static void zconf_load_buffer_state (void)
+{
+ (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos;
+ zconfin = YY_CURRENT_BUFFER_LVALUE->yy_input_file;
+ (yy_hold_char) = *(yy_c_buf_p);
+}
+
+/** Allocate and initialize an input buffer state.
+ * @param file A readable stream.
+ * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE.
+ *
+ * @return the allocated buffer state.
+ */
+ YY_BUFFER_STATE zconf_create_buffer (FILE * file, int size )
+{
+ YY_BUFFER_STATE b;
+
+ b = (YY_BUFFER_STATE) zconfalloc(sizeof( struct yy_buffer_state ) );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in zconf_create_buffer()" );
+
+ b->yy_buf_size = size;
+
+ /* yy_ch_buf has to be 2 characters longer than the size given because
+ * we need to put in 2 end-of-buffer characters.
+ */
+ b->yy_ch_buf = (char *) zconfalloc(b->yy_buf_size + 2 );
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in zconf_create_buffer()" );
+
+ b->yy_is_our_buffer = 1;
+
+ zconf_init_buffer(b,file );
+
+ return b;
+}
+
+/** Destroy the buffer.
+ * @param b a buffer created with zconf_create_buffer()
+ *
+ */
+ void zconf_delete_buffer (YY_BUFFER_STATE b )
+{
+
+ if ( ! b )
+ return;
+
+ if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */
+ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0;
+
+ if ( b->yy_is_our_buffer )
+ zconffree((void *) b->yy_ch_buf );
+
+ zconffree((void *) b );
+}
+
+/* Initializes or reinitializes a buffer.
+ * This function is sometimes called more than once on the same buffer,
+ * such as during a zconfrestart() or at EOF.
+ */
+ static void zconf_init_buffer (YY_BUFFER_STATE b, FILE * file )
+
+{
+ int oerrno = errno;
+
+ zconf_flush_buffer(b );
+
+ b->yy_input_file = file;
+ b->yy_fill_buffer = 1;
+
+ /* If b is the current buffer, then zconf_init_buffer was _probably_
+ * called from zconfrestart() or through yy_get_next_buffer.
+ * In that case, we don't want to reset the lineno or column.
+ */
+ if (b != YY_CURRENT_BUFFER){
+ b->yy_bs_lineno = 1;
+ b->yy_bs_column = 0;
+ }
+
+ b->yy_is_interactive = 0;
+
+ errno = oerrno;
+}
+
+/** Discard all buffered characters. On the next scan, YY_INPUT will be called.
+ * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER.
+ *
+ */
+ void zconf_flush_buffer (YY_BUFFER_STATE b )
+{
+ if ( ! b )
+ return;
+
+ b->yy_n_chars = 0;
+
+ /* We always need two end-of-buffer characters. The first causes
+ * a transition to the end-of-buffer state. The second causes
+ * a jam in that state.
+ */
+ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+ b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+ b->yy_buf_pos = &b->yy_ch_buf[0];
+
+ b->yy_at_bol = 1;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ if ( b == YY_CURRENT_BUFFER )
+ zconf_load_buffer_state( );
+}
+
+/** Pushes the new state onto the stack. The new state becomes
+ * the current state. This function will allocate the stack
+ * if necessary.
+ * @param new_buffer The new state.
+ *
+ */
+void zconfpush_buffer_state (YY_BUFFER_STATE new_buffer )
+{
+ if (new_buffer == NULL)
+ return;
+
+ zconfensure_buffer_stack();
+
+ /* This block is copied from zconf_switch_to_buffer. */
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *(yy_c_buf_p) = (yy_hold_char);
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+ }
+
+ /* Only push if top exists. Otherwise, replace top. */
+ if (YY_CURRENT_BUFFER)
+ (yy_buffer_stack_top)++;
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+
+ /* copied from zconf_switch_to_buffer. */
+ zconf_load_buffer_state( );
+ (yy_did_buffer_switch_on_eof) = 1;
+}
+
+/** Removes and deletes the top of the stack, if present.
+ * The next element becomes the new top.
+ *
+ */
+void zconfpop_buffer_state (void)
+{
+ if (!YY_CURRENT_BUFFER)
+ return;
+
+ zconf_delete_buffer(YY_CURRENT_BUFFER );
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ if ((yy_buffer_stack_top) > 0)
+ --(yy_buffer_stack_top);
+
+ if (YY_CURRENT_BUFFER) {
+ zconf_load_buffer_state( );
+ (yy_did_buffer_switch_on_eof) = 1;
+ }
+}
+
+/* Allocates the stack if it does not exist.
+ * Guarantees space for at least one push.
+ */
+static void zconfensure_buffer_stack (void)
+{
+ int num_to_alloc;
+
+ if (!(yy_buffer_stack)) {
+
+ /* First allocation is just for 2 elements, since we don't know if this
+ * scanner will even need a stack. We use 2 instead of 1 to avoid an
+ * immediate realloc on the next call.
+ */
+ num_to_alloc = 1;
+ (yy_buffer_stack) = (struct yy_buffer_state**)zconfalloc
+ (num_to_alloc * sizeof(struct yy_buffer_state*)
+ );
+
+ memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*));
+
+ (yy_buffer_stack_max) = num_to_alloc;
+ (yy_buffer_stack_top) = 0;
+ return;
+ }
+
+ if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){
+
+ /* Increase the buffer to prepare for a possible push. */
+ int grow_size = 8 /* arbitrary grow size */;
+
+ num_to_alloc = (yy_buffer_stack_max) + grow_size;
+ (yy_buffer_stack) = (struct yy_buffer_state**)zconfrealloc
+ ((yy_buffer_stack),
+ num_to_alloc * sizeof(struct yy_buffer_state*)
+ );
+
+ /* zero only the new slots.*/
+ memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*));
+ (yy_buffer_stack_max) = num_to_alloc;
+ }
+}
+
+/** Setup the input buffer state to scan directly from a user-specified character buffer.
+ * @param base the character buffer
+ * @param size the size in bytes of the character buffer
+ *
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE zconf_scan_buffer (char * base, yy_size_t size )
+{
+ YY_BUFFER_STATE b;
+
+ if ( size < 2 ||
+ base[size-2] != YY_END_OF_BUFFER_CHAR ||
+ base[size-1] != YY_END_OF_BUFFER_CHAR )
+ /* They forgot to leave room for the EOB's. */
+ return 0;
+
+ b = (YY_BUFFER_STATE) zconfalloc(sizeof( struct yy_buffer_state ) );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in zconf_scan_buffer()" );
+
+ b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */
+ b->yy_buf_pos = b->yy_ch_buf = base;
+ b->yy_is_our_buffer = 0;
+ b->yy_input_file = 0;
+ b->yy_n_chars = b->yy_buf_size;
+ b->yy_is_interactive = 0;
+ b->yy_at_bol = 1;
+ b->yy_fill_buffer = 0;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ zconf_switch_to_buffer(b );
+
+ return b;
+}
+
+/** Setup the input buffer state to scan a string. The next call to zconflex() will
+ * scan from a @e copy of @a str.
+ * @param yy_str a NUL-terminated string to scan
+ *
+ * @return the newly allocated buffer state object.
+ * @note If you want to scan bytes that may contain NUL values, then use
+ * zconf_scan_bytes() instead.
+ */
+YY_BUFFER_STATE zconf_scan_string (yyconst char * yy_str )
+{
+
+ return zconf_scan_bytes(yy_str,strlen(yy_str) );
+}
+
+/** Setup the input buffer state to scan the given bytes. The next call to zconflex() will
+ * scan from a @e copy of @a bytes.
+ * @param bytes the byte buffer to scan
+ * @param len the number of bytes in the buffer pointed to by @a bytes.
+ *
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE zconf_scan_bytes (yyconst char * bytes, int len )
+{
+ YY_BUFFER_STATE b;
+ char *buf;
+ yy_size_t n;
+ int i;
+
+ /* Get memory for full buffer, including space for trailing EOB's. */
+ n = len + 2;
+ buf = (char *) zconfalloc(n );
+ if ( ! buf )
+ YY_FATAL_ERROR( "out of dynamic memory in zconf_scan_bytes()" );
+
+ for ( i = 0; i < len; ++i )
+ buf[i] = bytes[i];
+
+ buf[len] = buf[len+1] = YY_END_OF_BUFFER_CHAR;
+
+ b = zconf_scan_buffer(buf,n );
+ if ( ! b )
+ YY_FATAL_ERROR( "bad buffer in zconf_scan_bytes()" );
+
+ /* It's okay to grow etc. this buffer, and we should throw it
+ * away when we're done.
+ */
+ b->yy_is_our_buffer = 1;
+
+ return b;
+}
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+static void yy_fatal_error (yyconst char* msg )
+{
+ (void) fprintf( stderr, "%s\n", msg );
+ exit( YY_EXIT_FAILURE );
+}
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up zconftext. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ zconftext[zconfleng] = (yy_hold_char); \
+ (yy_c_buf_p) = zconftext + yyless_macro_arg; \
+ (yy_hold_char) = *(yy_c_buf_p); \
+ *(yy_c_buf_p) = '\0'; \
+ zconfleng = yyless_macro_arg; \
+ } \
+ while ( 0 )
+
+/* Accessor methods (get/set functions) to struct members. */
+
+/** Get the current line number.
+ *
+ */
+int zconfget_lineno (void)
+{
+
+ return zconflineno;
+}
+
+/** Get the input stream.
+ *
+ */
+FILE *zconfget_in (void)
+{
+ return zconfin;
+}
+
+/** Get the output stream.
+ *
+ */
+FILE *zconfget_out (void)
+{
+ return zconfout;
+}
+
+/** Get the length of the current token.
+ *
+ */
+int zconfget_leng (void)
+{
+ return zconfleng;
+}
+
+/** Get the current token.
+ *
+ */
+
+char *zconfget_text (void)
+{
+ return zconftext;
+}
+
+/** Set the current line number.
+ * @param line_number
+ *
+ */
+void zconfset_lineno (int line_number )
+{
+
+ zconflineno = line_number;
+}
+
+/** Set the input stream. This does not discard the current
+ * input buffer.
+ * @param in_str A readable stream.
+ *
+ * @see zconf_switch_to_buffer
+ */
+void zconfset_in (FILE * in_str )
+{
+ zconfin = in_str ;
+}
+
+void zconfset_out (FILE * out_str )
+{
+ zconfout = out_str ;
+}
+
+int zconfget_debug (void)
+{
+ return zconf_flex_debug;
+}
+
+void zconfset_debug (int bdebug )
+{
+ zconf_flex_debug = bdebug ;
+}
+
+/* zconflex_destroy is for both reentrant and non-reentrant scanners. */
+int zconflex_destroy (void)
+{
+
+ /* Pop the buffer stack, destroying each element. */
+ while(YY_CURRENT_BUFFER){
+ zconf_delete_buffer(YY_CURRENT_BUFFER );
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ zconfpop_buffer_state();
+ }
+
+ /* Destroy the stack itself. */
+ zconffree((yy_buffer_stack) );
+ (yy_buffer_stack) = NULL;
+
+ return 0;
+}
+
+/*
+ * Internal utility routines.
+ */
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char* s1, yyconst char * s2, int n )
+{
+ register int i;
+ for ( i = 0; i < n; ++i )
+ s1[i] = s2[i];
+}
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * s )
+{
+ register int n;
+ for ( n = 0; s[n]; ++n )
+ ;
+
+ return n;
+}
+#endif
+
+void *zconfalloc (yy_size_t size )
+{
+ return (void *) malloc( size );
+}
+
+void *zconfrealloc (void * ptr, yy_size_t size )
+{
+ /* The cast to (char *) in the following accommodates both
+ * implementations that use char* generic pointers, and those
+ * that use void* generic pointers. It works with the latter
+ * because both ANSI C and C++ allow castless assignment from
+ * any pointer type to void*, and deal with argument conversions
+ * as though doing an assignment.
+ */
+ return (void *) realloc( (char *) ptr, size );
+}
+
+void zconffree (void * ptr )
+{
+ free( (char *) ptr ); /* see zconfrealloc() for (char *) cast */
+}
+
+#define YYTABLES_NAME "yytables"
+
+#undef YY_NEW_FILE
+#undef YY_FLUSH_BUFFER
+#undef yy_set_bol
+#undef yy_new_buffer
+#undef yy_set_interactive
+#undef yytext_ptr
+#undef YY_DO_BEFORE_ACTION
+
+#ifdef YY_DECL_IS_OURS
+#undef YY_DECL_IS_OURS
+#undef YY_DECL
+#endif
+
+void zconf_starthelp(void)
+{
+ new_string();
+ last_ts = first_ts = 0;
+ BEGIN(HELP);
+}
+
+static void zconf_endhelp(void)
+{
+ zconflval.string = text;
+ BEGIN(INITIAL);
+}
+
+/*
+ * Try to open specified file with following names:
+ * ./name
+ * $(srctree)/name
+ * The latter is used when srctree is separate from objtree
+ * when compiling the kernel.
+ * Return NULL if file is not found.
+ */
+FILE *zconf_fopen(const char *name)
+{
+ char *env, fullname[PATH_MAX+1];
+ FILE *f;
+
+ f = fopen(name, "r");
+ if (!f && name[0] != '/') {
+ env = getenv(SRCTREE);
+ if (env) {
+ sprintf(fullname, "%s/%s", env, name);
+ f = fopen(fullname, "r");
+ }
+ }
+ return f;
+}
+
+void zconf_initscan(const char *name)
+{
+ zconfin = zconf_fopen(name);
+ if (!zconfin) {
+ printf("can't find file %s\n", name);
+ exit(1);
+ }
+
+ current_buf = malloc(sizeof(*current_buf));
+ memset(current_buf, 0, sizeof(*current_buf));
+
+ current_file = file_lookup(name);
+ current_file->lineno = 1;
+ current_file->flags = FILE_BUSY;
+}
+
+void zconf_nextfile(const char *name)
+{
+ struct file *file = file_lookup(name);
+ struct buffer *buf = malloc(sizeof(*buf));
+ memset(buf, 0, sizeof(*buf));
+
+ current_buf->state = YY_CURRENT_BUFFER;
+ zconfin = zconf_fopen(name);
+ if (!zconfin) {
+ printf("%s:%d: can't open file \"%s\"\n", zconf_curname(), zconf_lineno(), name);
+ exit(1);
+ }
+ zconf_switch_to_buffer(zconf_create_buffer(zconfin,YY_BUF_SIZE));
+ buf->parent = current_buf;
+ current_buf = buf;
+
+ if (file->flags & FILE_BUSY) {
+ printf("recursive scan (%s)?\n", name);
+ exit(1);
+ }
+ if (file->flags & FILE_SCANNED) {
+ printf("file %s already scanned?\n", name);
+ exit(1);
+ }
+ file->flags |= FILE_BUSY;
+ file->lineno = 1;
+ file->parent = current_file;
+ current_file = file;
+}
+
+static void zconf_endfile(void)
+{
+ struct buffer *parent;
+
+ current_file->flags |= FILE_SCANNED;
+ current_file->flags &= ~FILE_BUSY;
+ current_file = current_file->parent;
+
+ parent = current_buf->parent;
+ if (parent) {
+ fclose(zconfin);
+ zconf_delete_buffer(YY_CURRENT_BUFFER);
+ zconf_switch_to_buffer(parent->state);
+ }
+ free(current_buf);
+ current_buf = parent;
+}
+
+int zconf_lineno(void)
+{
+ return current_pos.lineno;
+}
+
+char *zconf_curname(void)
+{
+ return current_pos.file ? current_pos.file->name : "<none>";
+}
+
diff --git a/i/pc104/initrd/conf/busybox/scripts/kconfig/lkc.h b/i/pc104/initrd/conf/busybox/scripts/kconfig/lkc.h
new file mode 100644
index 0000000..527f60c
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/scripts/kconfig/lkc.h
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#ifndef LKC_H
+#define LKC_H
+
+#include "expr.h"
+
+#ifndef KBUILD_NO_NLS
+# include <libintl.h>
+#else
+# define gettext(Msgid) ((const char *) (Msgid))
+# define textdomain(Domainname) ((const char *) (Domainname))
+# define bindtextdomain(Domainname, Dirname) ((const char *) (Dirname))
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef LKC_DIRECT_LINK
+#define P(name,type,arg) extern type name arg
+#else
+#include "lkc_defs.h"
+#define P(name,type,arg) extern type (*name ## _p) arg
+#endif
+#include "lkc_proto.h"
+#undef P
+
+#define SRCTREE "srctree"
+
+#define PACKAGE "linux"
+#define LOCALEDIR "/usr/share/locale"
+
+#define _(text) gettext(text)
+#define N_(text) (text)
+
+
+#define TF_COMMAND 0x0001
+#define TF_PARAM 0x0002
+
+struct kconf_id {
+ int name;
+ int token;
+ unsigned int flags;
+ enum symbol_type stype;
+};
+
+int zconfparse(void);
+void zconfdump(FILE *out);
+
+extern int zconfdebug;
+void zconf_starthelp(void);
+FILE *zconf_fopen(const char *name);
+void zconf_initscan(const char *name);
+void zconf_nextfile(const char *name);
+int zconf_lineno(void);
+char *zconf_curname(void);
+
+/* confdata.c */
+extern const char conf_def_filename[];
+
+char *conf_get_default_confname(void);
+
+/* kconfig_load.c */
+void kconfig_load(void);
+
+/* menu.c */
+void menu_init(void);
+struct menu *menu_add_menu(void);
+void menu_end_menu(void);
+void menu_add_entry(struct symbol *sym);
+void menu_end_entry(void);
+void menu_add_dep(struct expr *dep);
+struct property *menu_add_prop(enum prop_type type, char *prompt, struct expr *expr, struct expr *dep);
+struct property *menu_add_prompt(enum prop_type type, char *prompt, struct expr *dep);
+void menu_add_expr(enum prop_type type, struct expr *expr, struct expr *dep);
+void menu_add_symbol(enum prop_type type, struct symbol *sym, struct expr *dep);
+void menu_finalize(struct menu *parent);
+void menu_set_type(int type);
+
+/* util.c */
+struct file *file_lookup(const char *name);
+int file_write_dep(const char *name);
+
+struct gstr {
+ size_t len;
+ char *s;
+};
+struct gstr str_new(void);
+struct gstr str_assign(const char *s);
+void str_free(struct gstr *gs);
+void str_append(struct gstr *gs, const char *s);
+void str_printf(struct gstr *gs, const char *fmt, ...);
+const char *str_get(struct gstr *gs);
+
+/* symbol.c */
+void sym_init(void);
+void sym_clear_all_valid(void);
+void sym_set_changed(struct symbol *sym);
+struct symbol *sym_check_deps(struct symbol *sym);
+struct property *prop_alloc(enum prop_type type, struct symbol *sym);
+struct symbol *prop_get_symbol(struct property *prop);
+
+static inline tristate sym_get_tristate_value(struct symbol *sym)
+{
+ return sym->curr.tri;
+}
+
+
+static inline struct symbol *sym_get_choice_value(struct symbol *sym)
+{
+ return (struct symbol *)sym->curr.val;
+}
+
+static inline bool sym_set_choice_value(struct symbol *ch, struct symbol *chval)
+{
+ return sym_set_tristate_value(chval, yes);
+}
+
+static inline bool sym_is_choice(struct symbol *sym)
+{
+ return sym->flags & SYMBOL_CHOICE ? true : false;
+}
+
+static inline bool sym_is_choice_value(struct symbol *sym)
+{
+ return sym->flags & SYMBOL_CHOICEVAL ? true : false;
+}
+
+static inline bool sym_is_optional(struct symbol *sym)
+{
+ return sym->flags & SYMBOL_OPTIONAL ? true : false;
+}
+
+static inline bool sym_has_value(struct symbol *sym)
+{
+ return sym->flags & SYMBOL_NEW ? false : true;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LKC_H */
diff --git a/i/pc104/initrd/conf/busybox/scripts/kconfig/lkc_proto.h b/i/pc104/initrd/conf/busybox/scripts/kconfig/lkc_proto.h
new file mode 100644
index 0000000..b6a389c
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/scripts/kconfig/lkc_proto.h
@@ -0,0 +1,41 @@
+
+/* confdata.c */
+P(conf_parse,void,(const char *name));
+P(conf_read,int,(const char *name));
+P(conf_read_simple,int,(const char *name));
+P(conf_write,int,(const char *name));
+
+/* menu.c */
+P(rootmenu,struct menu,);
+
+P(menu_is_visible,bool,(struct menu *menu));
+P(menu_get_prompt,const char *,(struct menu *menu));
+P(menu_get_root_menu,struct menu *,(struct menu *menu));
+P(menu_get_parent_menu,struct menu *,(struct menu *menu));
+
+/* symbol.c */
+P(symbol_hash,struct symbol *,[SYMBOL_HASHSIZE]);
+P(sym_change_count,int,);
+
+P(sym_lookup,struct symbol *,(const char *name, int isconst));
+P(sym_find,struct symbol *,(const char *name));
+P(sym_re_search,struct symbol **,(const char *pattern));
+P(sym_type_name,const char *,(enum symbol_type type));
+P(sym_calc_value,void,(struct symbol *sym));
+P(sym_get_type,enum symbol_type,(struct symbol *sym));
+P(sym_tristate_within_range,bool,(struct symbol *sym,tristate tri));
+P(sym_set_tristate_value,bool,(struct symbol *sym,tristate tri));
+P(sym_toggle_tristate_value,tristate,(struct symbol *sym));
+P(sym_string_valid,bool,(struct symbol *sym, const char *newval));
+P(sym_string_within_range,bool,(struct symbol *sym, const char *str));
+P(sym_set_string_value,bool,(struct symbol *sym, const char *newval));
+P(sym_is_changable,bool,(struct symbol *sym));
+P(sym_get_choice_prop,struct property *,(struct symbol *sym));
+P(sym_get_default_prop,struct property *,(struct symbol *sym));
+P(sym_get_string_value,const char *,(struct symbol *sym));
+
+P(prop_get_type_name,const char *,(enum prop_type type));
+
+/* expr.c */
+P(expr_compare_type,int,(enum expr_type t1, enum expr_type t2));
+P(expr_print,void,(struct expr *e, void (*fn)(void *, const char *), void *data, int prevtoken));
diff --git a/i/pc104/initrd/conf/busybox/scripts/kconfig/lxdialog/BIG.FAT.WARNING b/i/pc104/initrd/conf/busybox/scripts/kconfig/lxdialog/BIG.FAT.WARNING
new file mode 100644
index 0000000..b5d3b10
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/scripts/kconfig/lxdialog/BIG.FAT.WARNING
@@ -0,0 +1,4 @@
+This is NOT the official version of dialog. This version has been
+significantly modified from the original. It is for use by the Linux
+busybox configuration script. Please do not bother Savio Lam with
+questions about this program.
diff --git a/i/pc104/initrd/conf/busybox/scripts/kconfig/lxdialog/Makefile b/i/pc104/initrd/conf/busybox/scripts/kconfig/lxdialog/Makefile
new file mode 100644
index 0000000..a8b0263
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/scripts/kconfig/lxdialog/Makefile
@@ -0,0 +1,21 @@
+# Makefile to build lxdialog package
+#
+
+check-lxdialog := $(srctree)/$(src)/check-lxdialog.sh
+
+# Use reursively expanded variables so we do not call gcc unless
+# we really need to do so. (Do not call gcc as part of make mrproper)
+HOST_EXTRACFLAGS = $(shell $(CONFIG_SHELL) $(check-lxdialog) -ccflags)
+HOST_LOADLIBES = $(shell $(CONFIG_SHELL) $(check-lxdialog) -ldflags $(HOSTCC))
+
+HOST_EXTRACFLAGS += -DLOCALE
+
+PHONY += dochecklxdialog
+$(obj)/dochecklxdialog:
+ $(Q)$(CONFIG_SHELL) $(check-lxdialog) -check $(HOSTCC) $(HOST_LOADLIBES)
+
+hostprogs-y := lxdialog
+always := $(hostprogs-y) dochecklxdialog
+
+lxdialog-objs := checklist.o menubox.o textbox.o yesno.o inputbox.o \
+ util.o lxdialog.o msgbox.o
diff --git a/i/pc104/initrd/conf/busybox/scripts/kconfig/lxdialog/check-lxdialog.sh b/i/pc104/initrd/conf/busybox/scripts/kconfig/lxdialog/check-lxdialog.sh
new file mode 100644
index 0000000..120d624
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/scripts/kconfig/lxdialog/check-lxdialog.sh
@@ -0,0 +1,84 @@
+#!/bin/sh
+# Check ncurses compatibility
+
+# What library to link
+ldflags()
+{
+ $cc -print-file-name=libncursesw.so | grep -q /
+ if [ $? -eq 0 ]; then
+ echo '-lncursesw'
+ exit
+ fi
+ $cc -print-file-name=libncurses.so | grep -q /
+ if [ $? -eq 0 ]; then
+ echo '-lncurses'
+ exit
+ fi
+ $cc -print-file-name=libcurses.so | grep -q /
+ if [ $? -eq 0 ]; then
+ echo '-lcurses'
+ exit
+ fi
+ exit 1
+}
+
+# Where is ncurses.h?
+ccflags()
+{
+ if [ -f /usr/include/ncurses/ncurses.h ]; then
+ echo '-I/usr/include/ncurses -DCURSES_LOC="<ncurses.h>"'
+ elif [ -f /usr/include/ncurses/curses.h ]; then
+ echo '-I/usr/include/ncurses -DCURSES_LOC="<ncurses/curses.h>"'
+ elif [ -f /usr/include/ncurses.h ]; then
+ echo '-DCURSES_LOC="<ncurses.h>"'
+ else
+ echo '-DCURSES_LOC="<curses.h>"'
+ fi
+}
+
+# Temp file, try to clean up after us
+tmp=.lxdialog.tmp
+trap "rm -f $tmp" 0 1 2 3 15
+
+# Check if we can link to ncurses
+check() {
+ echo "main() {}" | $cc -xc - -o $tmp 2> /dev/null
+ if [ $? != 0 ]; then
+ echo " *** Unable to find the ncurses libraries." 1>&2
+ echo " *** make menuconfig require the ncurses libraries" 1>&2
+ echo " *** " 1>&2
+ echo " *** Install ncurses (ncurses-devel) and try again" 1>&2
+ echo " *** " 1>&2
+ exit 1
+ fi
+}
+
+usage() {
+ printf "Usage: $0 [-check compiler options|-header|-library]\n"
+}
+
+if [ $# == 0 ]; then
+ usage
+ exit 1
+fi
+
+cc=""
+case "$1" in
+ "-check")
+ shift
+ cc="$@"
+ check
+ ;;
+ "-ccflags")
+ ccflags
+ ;;
+ "-ldflags")
+ shift
+ cc="$@"
+ ldflags
+ ;;
+ "*")
+ usage
+ exit 1
+ ;;
+esac
diff --git a/i/pc104/initrd/conf/busybox/scripts/kconfig/lxdialog/checklist.c b/i/pc104/initrd/conf/busybox/scripts/kconfig/lxdialog/checklist.c
new file mode 100644
index 0000000..be0200e
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/scripts/kconfig/lxdialog/checklist.c
@@ -0,0 +1,333 @@
+/*
+ * checklist.c -- implements the checklist box
+ *
+ * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
+ * Stuart Herbert - S.Herbert@sheffield.ac.uk: radiolist extension
+ * Alessandro Rubini - rubini@ipvvis.unipv.it: merged the two
+ * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "dialog.h"
+
+static int list_width, check_x, item_x;
+
+/*
+ * Print list item
+ */
+static void print_item(WINDOW * win, const char *item, int status, int choice,
+ int selected)
+{
+ int i;
+
+ /* Clear 'residue' of last item */
+ wattrset(win, menubox_attr);
+ wmove(win, choice, 0);
+ for (i = 0; i < list_width; i++)
+ waddch(win, ' ');
+
+ wmove(win, choice, check_x);
+ wattrset(win, selected ? check_selected_attr : check_attr);
+ wprintw(win, "(%c)", status ? 'X' : ' ');
+
+ wattrset(win, selected ? tag_selected_attr : tag_attr);
+ mvwaddch(win, choice, item_x, item[0]);
+ wattrset(win, selected ? item_selected_attr : item_attr);
+ waddstr(win, (char *)item + 1);
+ if (selected) {
+ wmove(win, choice, check_x + 1);
+ wrefresh(win);
+ }
+}
+
+/*
+ * Print the scroll indicators.
+ */
+static void print_arrows(WINDOW * win, int choice, int item_no, int scroll,
+ int y, int x, int height)
+{
+ wmove(win, y, x);
+
+ if (scroll > 0) {
+ wattrset(win, uarrow_attr);
+ waddch(win, ACS_UARROW);
+ waddstr(win, "(-)");
+ } else {
+ wattrset(win, menubox_attr);
+ waddch(win, ACS_HLINE);
+ waddch(win, ACS_HLINE);
+ waddch(win, ACS_HLINE);
+ waddch(win, ACS_HLINE);
+ }
+
+ y = y + height + 1;
+ wmove(win, y, x);
+
+ if ((height < item_no) && (scroll + choice < item_no - 1)) {
+ wattrset(win, darrow_attr);
+ waddch(win, ACS_DARROW);
+ waddstr(win, "(+)");
+ } else {
+ wattrset(win, menubox_border_attr);
+ waddch(win, ACS_HLINE);
+ waddch(win, ACS_HLINE);
+ waddch(win, ACS_HLINE);
+ waddch(win, ACS_HLINE);
+ }
+}
+
+/*
+ * Display the termination buttons
+ */
+static void print_buttons(WINDOW * dialog, int height, int width, int selected)
+{
+ int x = width / 2 - 11;
+ int y = height - 2;
+
+ print_button(dialog, "Select", y, x, selected == 0);
+ print_button(dialog, " Help ", y, x + 14, selected == 1);
+
+ wmove(dialog, y, x + 1 + 14 * selected);
+ wrefresh(dialog);
+}
+
+/*
+ * Display a dialog box with a list of options that can be turned on or off
+ * in the style of radiolist (only one option turned on at a time).
+ */
+int dialog_checklist(const char *title, const char *prompt, int height,
+ int width, int list_height, int item_no,
+ const char *const *items)
+{
+ int i, x, y, box_x, box_y;
+ int key = 0, button = 0, choice = 0, scroll = 0, max_choice, *status;
+ WINDOW *dialog, *list;
+
+ /* Allocate space for storing item on/off status */
+ if ((status = malloc(sizeof(int) * item_no)) == NULL) {
+ endwin();
+ fprintf(stderr,
+ "\nCan't allocate memory in dialog_checklist().\n");
+ exit(-1);
+ }
+
+ /* Initializes status */
+ for (i = 0; i < item_no; i++) {
+ status[i] = !strcasecmp(items[i * 3 + 2], "on");
+ if ((!choice && status[i])
+ || !strcasecmp(items[i * 3 + 2], "selected"))
+ choice = i + 1;
+ }
+ if (choice)
+ choice--;
+
+ max_choice = MIN(list_height, item_no);
+
+ /* center dialog box on screen */
+ x = (COLS - width) / 2;
+ y = (LINES - height) / 2;
+
+ draw_shadow(stdscr, y, x, height, width);
+
+ dialog = newwin(height, width, y, x);
+ keypad(dialog, TRUE);
+
+ draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr);
+ wattrset(dialog, border_attr);
+ mvwaddch(dialog, height - 3, 0, ACS_LTEE);
+ for (i = 0; i < width - 2; i++)
+ waddch(dialog, ACS_HLINE);
+ wattrset(dialog, dialog_attr);
+ waddch(dialog, ACS_RTEE);
+
+ print_title(dialog, title, width);
+
+ wattrset(dialog, dialog_attr);
+ print_autowrap(dialog, prompt, width - 2, 1, 3);
+
+ list_width = width - 6;
+ box_y = height - list_height - 5;
+ box_x = (width - list_width) / 2 - 1;
+
+ /* create new window for the list */
+ list = subwin(dialog, list_height, list_width, y + box_y + 1,
+ x + box_x + 1);
+
+ keypad(list, TRUE);
+
+ /* draw a box around the list items */
+ draw_box(dialog, box_y, box_x, list_height + 2, list_width + 2,
+ menubox_border_attr, menubox_attr);
+
+ /* Find length of longest item in order to center checklist */
+ check_x = 0;
+ for (i = 0; i < item_no; i++)
+ check_x = MAX(check_x, +strlen(items[i * 3 + 1]) + 4);
+
+ check_x = (list_width - check_x) / 2;
+ item_x = check_x + 4;
+
+ if (choice >= list_height) {
+ scroll = choice - list_height + 1;
+ choice -= scroll;
+ }
+
+ /* Print the list */
+ for (i = 0; i < max_choice; i++) {
+ print_item(list, items[(scroll + i) * 3 + 1],
+ status[i + scroll], i, i == choice);
+ }
+
+ print_arrows(dialog, choice, item_no, scroll,
+ box_y, box_x + check_x + 5, list_height);
+
+ print_buttons(dialog, height, width, 0);
+
+ wnoutrefresh(dialog);
+ wnoutrefresh(list);
+ doupdate();
+
+ while (key != ESC) {
+ key = wgetch(dialog);
+
+ for (i = 0; i < max_choice; i++)
+ if (toupper(key) ==
+ toupper(items[(scroll + i) * 3 + 1][0]))
+ break;
+
+ if (i < max_choice || key == KEY_UP || key == KEY_DOWN ||
+ key == '+' || key == '-') {
+ if (key == KEY_UP || key == '-') {
+ if (!choice) {
+ if (!scroll)
+ continue;
+ /* Scroll list down */
+ if (list_height > 1) {
+ /* De-highlight current first item */
+ print_item(list, items[scroll * 3 + 1],
+ status[scroll], 0, FALSE);
+ scrollok(list, TRUE);
+ wscrl(list, -1);
+ scrollok(list, FALSE);
+ }
+ scroll--;
+ print_item(list, items[scroll * 3 + 1], status[scroll], 0, TRUE);
+ print_arrows(dialog, choice, item_no,
+ scroll, box_y, box_x + check_x + 5, list_height);
+
+ wnoutrefresh(dialog);
+ wrefresh(list);
+
+ continue; /* wait for another key press */
+ } else
+ i = choice - 1;
+ } else if (key == KEY_DOWN || key == '+') {
+ if (choice == max_choice - 1) {
+ if (scroll + choice >= item_no - 1)
+ continue;
+ /* Scroll list up */
+ if (list_height > 1) {
+ /* De-highlight current last item before scrolling up */
+ print_item(list, items[(scroll + max_choice - 1) * 3 + 1],
+ status[scroll + max_choice - 1],
+ max_choice - 1, FALSE);
+ scrollok(list, TRUE);
+ wscrl(list, 1);
+ scrollok(list, FALSE);
+ }
+ scroll++;
+ print_item(list, items[(scroll + max_choice - 1) * 3 + 1],
+ status[scroll + max_choice - 1], max_choice - 1, TRUE);
+
+ print_arrows(dialog, choice, item_no,
+ scroll, box_y, box_x + check_x + 5, list_height);
+
+ wnoutrefresh(dialog);
+ wrefresh(list);
+
+ continue; /* wait for another key press */
+ } else
+ i = choice + 1;
+ }
+ if (i != choice) {
+ /* De-highlight current item */
+ print_item(list, items[(scroll + choice) * 3 + 1],
+ status[scroll + choice], choice, FALSE);
+ /* Highlight new item */
+ choice = i;
+ print_item(list, items[(scroll + choice) * 3 + 1],
+ status[scroll + choice], choice, TRUE);
+ wnoutrefresh(dialog);
+ wrefresh(list);
+ }
+ continue; /* wait for another key press */
+ }
+ switch (key) {
+ case 'H':
+ case 'h':
+ case '?':
+ fprintf(stderr, "%s", items[(scroll + choice) * 3]);
+ delwin(dialog);
+ free(status);
+ return 1;
+ case TAB:
+ case KEY_LEFT:
+ case KEY_RIGHT:
+ button = ((key == KEY_LEFT ? --button : ++button) < 0)
+ ? 1 : (button > 1 ? 0 : button);
+
+ print_buttons(dialog, height, width, button);
+ wrefresh(dialog);
+ break;
+ case 'S':
+ case 's':
+ case ' ':
+ case '\n':
+ if (!button) {
+ if (!status[scroll + choice]) {
+ for (i = 0; i < item_no; i++)
+ status[i] = 0;
+ status[scroll + choice] = 1;
+ for (i = 0; i < max_choice; i++)
+ print_item(list, items[(scroll + i) * 3 + 1],
+ status[scroll + i], i, i == choice);
+ }
+ wnoutrefresh(dialog);
+ wrefresh(list);
+
+ for (i = 0; i < item_no; i++)
+ if (status[i])
+ fprintf(stderr, "%s", items[i * 3]);
+ } else
+ fprintf(stderr, "%s", items[(scroll + choice) * 3]);
+ delwin(dialog);
+ free(status);
+ return button;
+ case 'X':
+ case 'x':
+ key = ESC;
+ case ESC:
+ break;
+ }
+
+ /* Now, update everything... */
+ doupdate();
+ }
+
+ delwin(dialog);
+ free(status);
+ return -1; /* ESC pressed */
+}
diff --git a/i/pc104/initrd/conf/busybox/scripts/kconfig/lxdialog/colors.h b/i/pc104/initrd/conf/busybox/scripts/kconfig/lxdialog/colors.h
new file mode 100644
index 0000000..db071df
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/scripts/kconfig/lxdialog/colors.h
@@ -0,0 +1,154 @@
+/*
+ * colors.h -- color attribute definitions
+ *
+ * AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * Default color definitions
+ *
+ * *_FG = foreground
+ * *_BG = background
+ * *_HL = highlight?
+ */
+#define SCREEN_FG COLOR_CYAN
+#define SCREEN_BG COLOR_BLUE
+#define SCREEN_HL TRUE
+
+#define SHADOW_FG COLOR_BLACK
+#define SHADOW_BG COLOR_BLACK
+#define SHADOW_HL TRUE
+
+#define DIALOG_FG COLOR_BLACK
+#define DIALOG_BG COLOR_WHITE
+#define DIALOG_HL FALSE
+
+#define TITLE_FG COLOR_YELLOW
+#define TITLE_BG COLOR_WHITE
+#define TITLE_HL TRUE
+
+#define BORDER_FG COLOR_WHITE
+#define BORDER_BG COLOR_WHITE
+#define BORDER_HL TRUE
+
+#define BUTTON_ACTIVE_FG COLOR_WHITE
+#define BUTTON_ACTIVE_BG COLOR_BLUE
+#define BUTTON_ACTIVE_HL TRUE
+
+#define BUTTON_INACTIVE_FG COLOR_BLACK
+#define BUTTON_INACTIVE_BG COLOR_WHITE
+#define BUTTON_INACTIVE_HL FALSE
+
+#define BUTTON_KEY_ACTIVE_FG COLOR_WHITE
+#define BUTTON_KEY_ACTIVE_BG COLOR_BLUE
+#define BUTTON_KEY_ACTIVE_HL TRUE
+
+#define BUTTON_KEY_INACTIVE_FG COLOR_RED
+#define BUTTON_KEY_INACTIVE_BG COLOR_WHITE
+#define BUTTON_KEY_INACTIVE_HL FALSE
+
+#define BUTTON_LABEL_ACTIVE_FG COLOR_YELLOW
+#define BUTTON_LABEL_ACTIVE_BG COLOR_BLUE
+#define BUTTON_LABEL_ACTIVE_HL TRUE
+
+#define BUTTON_LABEL_INACTIVE_FG COLOR_BLACK
+#define BUTTON_LABEL_INACTIVE_BG COLOR_WHITE
+#define BUTTON_LABEL_INACTIVE_HL TRUE
+
+#define INPUTBOX_FG COLOR_BLACK
+#define INPUTBOX_BG COLOR_WHITE
+#define INPUTBOX_HL FALSE
+
+#define INPUTBOX_BORDER_FG COLOR_BLACK
+#define INPUTBOX_BORDER_BG COLOR_WHITE
+#define INPUTBOX_BORDER_HL FALSE
+
+#define SEARCHBOX_FG COLOR_BLACK
+#define SEARCHBOX_BG COLOR_WHITE
+#define SEARCHBOX_HL FALSE
+
+#define SEARCHBOX_TITLE_FG COLOR_YELLOW
+#define SEARCHBOX_TITLE_BG COLOR_WHITE
+#define SEARCHBOX_TITLE_HL TRUE
+
+#define SEARCHBOX_BORDER_FG COLOR_WHITE
+#define SEARCHBOX_BORDER_BG COLOR_WHITE
+#define SEARCHBOX_BORDER_HL TRUE
+
+#define POSITION_INDICATOR_FG COLOR_YELLOW
+#define POSITION_INDICATOR_BG COLOR_WHITE
+#define POSITION_INDICATOR_HL TRUE
+
+#define MENUBOX_FG COLOR_BLACK
+#define MENUBOX_BG COLOR_WHITE
+#define MENUBOX_HL FALSE
+
+#define MENUBOX_BORDER_FG COLOR_WHITE
+#define MENUBOX_BORDER_BG COLOR_WHITE
+#define MENUBOX_BORDER_HL TRUE
+
+#define ITEM_FG COLOR_BLACK
+#define ITEM_BG COLOR_WHITE
+#define ITEM_HL FALSE
+
+#define ITEM_SELECTED_FG COLOR_WHITE
+#define ITEM_SELECTED_BG COLOR_BLUE
+#define ITEM_SELECTED_HL TRUE
+
+#define TAG_FG COLOR_YELLOW
+#define TAG_BG COLOR_WHITE
+#define TAG_HL TRUE
+
+#define TAG_SELECTED_FG COLOR_YELLOW
+#define TAG_SELECTED_BG COLOR_BLUE
+#define TAG_SELECTED_HL TRUE
+
+#define TAG_KEY_FG COLOR_YELLOW
+#define TAG_KEY_BG COLOR_WHITE
+#define TAG_KEY_HL TRUE
+
+#define TAG_KEY_SELECTED_FG COLOR_YELLOW
+#define TAG_KEY_SELECTED_BG COLOR_BLUE
+#define TAG_KEY_SELECTED_HL TRUE
+
+#define CHECK_FG COLOR_BLACK
+#define CHECK_BG COLOR_WHITE
+#define CHECK_HL FALSE
+
+#define CHECK_SELECTED_FG COLOR_WHITE
+#define CHECK_SELECTED_BG COLOR_BLUE
+#define CHECK_SELECTED_HL TRUE
+
+#define UARROW_FG COLOR_GREEN
+#define UARROW_BG COLOR_WHITE
+#define UARROW_HL TRUE
+
+#define DARROW_FG COLOR_GREEN
+#define DARROW_BG COLOR_WHITE
+#define DARROW_HL TRUE
+
+/* End of default color definitions */
+
+#define C_ATTR(x,y) ((x ? A_BOLD : 0) | COLOR_PAIR((y)))
+#define COLOR_NAME_LEN 10
+#define COLOR_COUNT 8
+
+/*
+ * Global variables
+ */
+
+extern int color_table[][3];
diff --git a/i/pc104/initrd/conf/busybox/scripts/kconfig/lxdialog/dialog.h b/i/pc104/initrd/conf/busybox/scripts/kconfig/lxdialog/dialog.h
new file mode 100644
index 0000000..af3cf71
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/scripts/kconfig/lxdialog/dialog.h
@@ -0,0 +1,177 @@
+/*
+ * dialog.h -- common declarations for all dialog modules
+ *
+ * AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef __sun__
+#define CURS_MACROS
+#endif
+#include CURSES_LOC
+
+/*
+ * Colors in ncurses 1.9.9e do not work properly since foreground and
+ * background colors are OR'd rather than separately masked. This version
+ * of dialog was hacked to work with ncurses 1.9.9e, making it incompatible
+ * with standard curses. The simplest fix (to make this work with standard
+ * curses) uses the wbkgdset() function, not used in the original hack.
+ * Turn it off if we're building with 1.9.9e, since it just confuses things.
+ */
+#if defined(NCURSES_VERSION) && defined(_NEED_WRAP) && !defined(GCC_PRINTFLIKE)
+#define OLD_NCURSES 1
+#undef wbkgdset
+#define wbkgdset(w,p) /*nothing */
+#else
+#define OLD_NCURSES 0
+#endif
+
+#define TR(params) _tracef params
+
+#define ESC 27
+#define TAB 9
+#define MAX_LEN 2048
+#define BUF_SIZE (10*1024)
+#define MIN(x,y) (x < y ? x : y)
+#define MAX(x,y) (x > y ? x : y)
+
+#ifndef ACS_ULCORNER
+#define ACS_ULCORNER '+'
+#endif
+#ifndef ACS_LLCORNER
+#define ACS_LLCORNER '+'
+#endif
+#ifndef ACS_URCORNER
+#define ACS_URCORNER '+'
+#endif
+#ifndef ACS_LRCORNER
+#define ACS_LRCORNER '+'
+#endif
+#ifndef ACS_HLINE
+#define ACS_HLINE '-'
+#endif
+#ifndef ACS_VLINE
+#define ACS_VLINE '|'
+#endif
+#ifndef ACS_LTEE
+#define ACS_LTEE '+'
+#endif
+#ifndef ACS_RTEE
+#define ACS_RTEE '+'
+#endif
+#ifndef ACS_UARROW
+#define ACS_UARROW '^'
+#endif
+#ifndef ACS_DARROW
+#define ACS_DARROW 'v'
+#endif
+
+/*
+ * Attribute names
+ */
+#define screen_attr attributes[0]
+#define shadow_attr attributes[1]
+#define dialog_attr attributes[2]
+#define title_attr attributes[3]
+#define border_attr attributes[4]
+#define button_active_attr attributes[5]
+#define button_inactive_attr attributes[6]
+#define button_key_active_attr attributes[7]
+#define button_key_inactive_attr attributes[8]
+#define button_label_active_attr attributes[9]
+#define button_label_inactive_attr attributes[10]
+#define inputbox_attr attributes[11]
+#define inputbox_border_attr attributes[12]
+#define searchbox_attr attributes[13]
+#define searchbox_title_attr attributes[14]
+#define searchbox_border_attr attributes[15]
+#define position_indicator_attr attributes[16]
+#define menubox_attr attributes[17]
+#define menubox_border_attr attributes[18]
+#define item_attr attributes[19]
+#define item_selected_attr attributes[20]
+#define tag_attr attributes[21]
+#define tag_selected_attr attributes[22]
+#define tag_key_attr attributes[23]
+#define tag_key_selected_attr attributes[24]
+#define check_attr attributes[25]
+#define check_selected_attr attributes[26]
+#define uarrow_attr attributes[27]
+#define darrow_attr attributes[28]
+
+/* number of attributes */
+#define ATTRIBUTE_COUNT 29
+
+/*
+ * Global variables
+ */
+extern bool use_colors;
+extern bool use_shadow;
+
+extern chtype attributes[];
+
+extern const char *backtitle;
+
+/*
+ * Function prototypes
+ */
+extern void create_rc(const char *filename);
+extern int parse_rc(void);
+
+void init_dialog(void);
+void end_dialog(void);
+void attr_clear(WINDOW * win, int height, int width, chtype attr);
+void dialog_clear(void);
+void color_setup(void);
+void print_autowrap(WINDOW * win, const char *prompt, int width, int y, int x);
+void print_button(WINDOW * win, const char *label, int y, int x, int selected);
+void print_title(WINDOW *dialog, const char *title, int width);
+void draw_box(WINDOW * win, int y, int x, int height, int width, chtype box,
+ chtype border);
+void draw_shadow(WINDOW * win, int y, int x, int height, int width);
+
+int first_alpha(const char *string, const char *exempt);
+int dialog_yesno(const char *title, const char *prompt, int height, int width);
+int dialog_msgbox(const char *title, const char *prompt, int height,
+ int width, int pause);
+int dialog_textbox(const char *title, const char *file, int height, int width);
+int dialog_menu(const char *title, const char *prompt, int height, int width,
+ int menu_height, const char *choice, int item_no,
+ const char *const *items);
+int dialog_checklist(const char *title, const char *prompt, int height,
+ int width, int list_height, int item_no,
+ const char *const *items);
+extern char dialog_input_result[];
+int dialog_inputbox(const char *title, const char *prompt, int height,
+ int width, const char *init);
+
+/*
+ * This is the base for fictitious keys, which activate
+ * the buttons.
+ *
+ * Mouse-generated keys are the following:
+ * -- the first 32 are used as numbers, in addition to '0'-'9'
+ * -- the lowercase are used to signal mouse-enter events (M_EVENT + 'o')
+ * -- uppercase chars are used to invoke the button (M_EVENT + 'O')
+ */
+#define M_EVENT (KEY_MAX+1)
diff --git a/i/pc104/initrd/conf/busybox/scripts/kconfig/lxdialog/inputbox.c b/i/pc104/initrd/conf/busybox/scripts/kconfig/lxdialog/inputbox.c
new file mode 100644
index 0000000..7795037
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/scripts/kconfig/lxdialog/inputbox.c
@@ -0,0 +1,224 @@
+/*
+ * inputbox.c -- implements the input box
+ *
+ * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
+ * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "dialog.h"
+
+char dialog_input_result[MAX_LEN + 1];
+
+/*
+ * Print the termination buttons
+ */
+static void print_buttons(WINDOW * dialog, int height, int width, int selected)
+{
+ int x = width / 2 - 11;
+ int y = height - 2;
+
+ print_button(dialog, " Ok ", y, x, selected == 0);
+ print_button(dialog, " Help ", y, x + 14, selected == 1);
+
+ wmove(dialog, y, x + 1 + 14 * selected);
+ wrefresh(dialog);
+}
+
+/*
+ * Display a dialog box for inputing a string
+ */
+int dialog_inputbox(const char *title, const char *prompt, int height, int width,
+ const char *init)
+{
+ int i, x, y, box_y, box_x, box_width;
+ int input_x = 0, scroll = 0, key = 0, button = -1;
+ char *instr = dialog_input_result;
+ WINDOW *dialog;
+
+ /* center dialog box on screen */
+ x = (COLS - width) / 2;
+ y = (LINES - height) / 2;
+
+ draw_shadow(stdscr, y, x, height, width);
+
+ dialog = newwin(height, width, y, x);
+ keypad(dialog, TRUE);
+
+ draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr);
+ wattrset(dialog, border_attr);
+ mvwaddch(dialog, height - 3, 0, ACS_LTEE);
+ for (i = 0; i < width - 2; i++)
+ waddch(dialog, ACS_HLINE);
+ wattrset(dialog, dialog_attr);
+ waddch(dialog, ACS_RTEE);
+
+ print_title(dialog, title, width);
+
+ wattrset(dialog, dialog_attr);
+ print_autowrap(dialog, prompt, width - 2, 1, 3);
+
+ /* Draw the input field box */
+ box_width = width - 6;
+ getyx(dialog, y, x);
+ box_y = y + 2;
+ box_x = (width - box_width) / 2;
+ draw_box(dialog, y + 1, box_x - 1, 3, box_width + 2, border_attr, dialog_attr);
+
+ print_buttons(dialog, height, width, 0);
+
+ /* Set up the initial value */
+ wmove(dialog, box_y, box_x);
+ wattrset(dialog, inputbox_attr);
+
+ if (!init)
+ instr[0] = '\0';
+ else
+ strcpy(instr, init);
+
+ input_x = strlen(instr);
+
+ if (input_x >= box_width) {
+ scroll = input_x - box_width + 1;
+ input_x = box_width - 1;
+ for (i = 0; i < box_width - 1; i++)
+ waddch(dialog, instr[scroll + i]);
+ } else {
+ waddstr(dialog, instr);
+ }
+
+ wmove(dialog, box_y, box_x + input_x);
+
+ wrefresh(dialog);
+
+ while (key != ESC) {
+ key = wgetch(dialog);
+
+ if (button == -1) { /* Input box selected */
+ switch (key) {
+ case TAB:
+ case KEY_UP:
+ case KEY_DOWN:
+ break;
+ case KEY_LEFT:
+ continue;
+ case KEY_RIGHT:
+ continue;
+ case KEY_BACKSPACE:
+ case 127:
+ if (input_x || scroll) {
+ wattrset(dialog, inputbox_attr);
+ if (!input_x) {
+ scroll = scroll < box_width - 1 ? 0 : scroll - (box_width - 1);
+ wmove(dialog, box_y, box_x);
+ for (i = 0; i < box_width; i++)
+ waddch(dialog,
+ instr[scroll + input_x + i] ?
+ instr[scroll + input_x + i] : ' ');
+ input_x = strlen(instr) - scroll;
+ } else
+ input_x--;
+ instr[scroll + input_x] = '\0';
+ mvwaddch(dialog, box_y, input_x + box_x, ' ');
+ wmove(dialog, box_y, input_x + box_x);
+ wrefresh(dialog);
+ }
+ continue;
+ default:
+ if (key < 0x100 && isprint(key)) {
+ if (scroll + input_x < MAX_LEN) {
+ wattrset(dialog, inputbox_attr);
+ instr[scroll + input_x] = key;
+ instr[scroll + input_x + 1] = '\0';
+ if (input_x == box_width - 1) {
+ scroll++;
+ wmove(dialog, box_y, box_x);
+ for (i = 0; i < box_width - 1; i++)
+ waddch(dialog, instr [scroll + i]);
+ } else {
+ wmove(dialog, box_y, input_x++ + box_x);
+ waddch(dialog, key);
+ }
+ wrefresh(dialog);
+ } else
+ flash(); /* Alarm user about overflow */
+ continue;
+ }
+ }
+ }
+ switch (key) {
+ case 'O':
+ case 'o':
+ delwin(dialog);
+ return 0;
+ case 'H':
+ case 'h':
+ delwin(dialog);
+ return 1;
+ case KEY_UP:
+ case KEY_LEFT:
+ switch (button) {
+ case -1:
+ button = 1; /* Indicates "Cancel" button is selected */
+ print_buttons(dialog, height, width, 1);
+ break;
+ case 0:
+ button = -1; /* Indicates input box is selected */
+ print_buttons(dialog, height, width, 0);
+ wmove(dialog, box_y, box_x + input_x);
+ wrefresh(dialog);
+ break;
+ case 1:
+ button = 0; /* Indicates "OK" button is selected */
+ print_buttons(dialog, height, width, 0);
+ break;
+ }
+ break;
+ case TAB:
+ case KEY_DOWN:
+ case KEY_RIGHT:
+ switch (button) {
+ case -1:
+ button = 0; /* Indicates "OK" button is selected */
+ print_buttons(dialog, height, width, 0);
+ break;
+ case 0:
+ button = 1; /* Indicates "Cancel" button is selected */
+ print_buttons(dialog, height, width, 1);
+ break;
+ case 1:
+ button = -1; /* Indicates input box is selected */
+ print_buttons(dialog, height, width, 0);
+ wmove(dialog, box_y, box_x + input_x);
+ wrefresh(dialog);
+ break;
+ }
+ break;
+ case ' ':
+ case '\n':
+ delwin(dialog);
+ return (button == -1 ? 0 : button);
+ case 'X':
+ case 'x':
+ key = ESC;
+ case ESC:
+ break;
+ }
+ }
+
+ delwin(dialog);
+ return -1; /* ESC pressed */
+}
diff --git a/i/pc104/initrd/conf/busybox/scripts/kconfig/lxdialog/lxdialog.c b/i/pc104/initrd/conf/busybox/scripts/kconfig/lxdialog/lxdialog.c
new file mode 100644
index 0000000..79f6c5f
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/scripts/kconfig/lxdialog/lxdialog.c
@@ -0,0 +1,204 @@
+/*
+ * dialog - Display simple dialog boxes from shell scripts
+ *
+ * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
+ * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "dialog.h"
+
+static void Usage(const char *name);
+
+typedef int (jumperFn) (const char *title, int argc, const char *const *argv);
+
+struct Mode {
+ char *name;
+ int argmin, argmax, argmod;
+ jumperFn *jumper;
+};
+
+jumperFn j_menu, j_radiolist, j_yesno, j_textbox, j_inputbox;
+jumperFn j_msgbox, j_infobox;
+
+static struct Mode modes[] = {
+ {"--menu", 9, 0, 3, j_menu},
+ {"--radiolist", 9, 0, 3, j_radiolist},
+ {"--yesno", 5, 5, 1, j_yesno},
+ {"--textbox", 5, 5, 1, j_textbox},
+ {"--inputbox", 5, 6, 1, j_inputbox},
+ {"--msgbox", 5, 5, 1, j_msgbox},
+ {"--infobox", 5, 5, 1, j_infobox},
+ {NULL, 0, 0, 0, NULL}
+};
+
+static struct Mode *modePtr;
+
+#ifdef LOCALE
+#include <locale.h>
+#endif
+
+int main(int argc, const char *const *argv)
+{
+ int offset = 0, opt_clear = 0, end_common_opts = 0, retval;
+ const char *title = NULL;
+
+#ifdef LOCALE
+ (void)setlocale(LC_ALL, "");
+#endif
+
+#ifdef TRACE
+ trace(TRACE_CALLS | TRACE_UPDATE);
+#endif
+ if (argc < 2) {
+ Usage(argv[0]);
+ exit(-1);
+ }
+
+ while (offset < argc - 1 && !end_common_opts) { /* Common options */
+ if (!strcmp(argv[offset + 1], "--title")) {
+ if (argc - offset < 3 || title != NULL) {
+ Usage(argv[0]);
+ exit(-1);
+ } else {
+ title = argv[offset + 2];
+ offset += 2;
+ }
+ } else if (!strcmp(argv[offset + 1], "--backtitle")) {
+ if (backtitle != NULL) {
+ Usage(argv[0]);
+ exit(-1);
+ } else {
+ backtitle = argv[offset + 2];
+ offset += 2;
+ }
+ } else if (!strcmp(argv[offset + 1], "--clear")) {
+ if (opt_clear) { /* Hey, "--clear" can't appear twice! */
+ Usage(argv[0]);
+ exit(-1);
+ } else if (argc == 2) { /* we only want to clear the screen */
+ init_dialog();
+ refresh(); /* init_dialog() will clear the screen for us */
+ end_dialog();
+ return 0;
+ } else {
+ opt_clear = 1;
+ offset++;
+ }
+ } else /* no more common options */
+ end_common_opts = 1;
+ }
+
+ if (argc - 1 == offset) { /* no more options */
+ Usage(argv[0]);
+ exit(-1);
+ }
+ /* use a table to look for the requested mode, to avoid code duplication */
+
+ for (modePtr = modes; modePtr->name; modePtr++) /* look for the mode */
+ if (!strcmp(argv[offset + 1], modePtr->name))
+ break;
+
+ if (!modePtr->name)
+ Usage(argv[0]);
+ if (argc - offset < modePtr->argmin)
+ Usage(argv[0]);
+ if (modePtr->argmax && argc - offset > modePtr->argmax)
+ Usage(argv[0]);
+
+ init_dialog();
+ retval = (*(modePtr->jumper)) (title, argc - offset, argv + offset);
+
+ if (opt_clear) { /* clear screen before exit */
+ attr_clear(stdscr, LINES, COLS, screen_attr);
+ refresh();
+ }
+ end_dialog();
+
+ exit(retval);
+}
+
+/*
+ * Print program usage
+ */
+static void Usage(const char *name)
+{
+ fprintf(stderr, "\
+\ndialog, by Savio Lam (lam836@cs.cuhk.hk).\
+\n patched by Stuart Herbert (S.Herbert@shef.ac.uk)\
+\n modified/gutted for use as a Linux kernel config tool by \
+\n William Roadcap (roadcapw@cfw.com)\
+\n\
+\n* Display dialog boxes from shell scripts *\
+\n\
+\nUsage: %s --clear\
+\n %s [--title <title>] [--backtitle <backtitle>] --clear <Box options>\
+\n\
+\nBox options:\
+\n\
+\n --menu <text> <height> <width> <menu height> <tag1> <item1>...\
+\n --radiolist <text> <height> <width> <list height> <tag1> <item1> <status1>...\
+\n --textbox <file> <height> <width>\
+\n --inputbox <text> <height> <width> [<init>]\
+\n --yesno <text> <height> <width>\
+\n", name, name);
+ exit(-1);
+}
+
+/*
+ * These are the program jumpers
+ */
+
+int j_menu(const char *t, int ac, const char *const *av)
+{
+ return dialog_menu(t, av[2], atoi(av[3]), atoi(av[4]),
+ atoi(av[5]), av[6], (ac - 6) / 2, av + 7);
+}
+
+int j_radiolist(const char *t, int ac, const char *const *av)
+{
+ return dialog_checklist(t, av[2], atoi(av[3]), atoi(av[4]),
+ atoi(av[5]), (ac - 6) / 3, av + 6);
+}
+
+int j_textbox(const char *t, int ac, const char *const *av)
+{
+ return dialog_textbox(t, av[2], atoi(av[3]), atoi(av[4]));
+}
+
+int j_yesno(const char *t, int ac, const char *const *av)
+{
+ return dialog_yesno(t, av[2], atoi(av[3]), atoi(av[4]));
+}
+
+int j_inputbox(const char *t, int ac, const char *const *av)
+{
+ int ret = dialog_inputbox(t, av[2], atoi(av[3]), atoi(av[4]),
+ ac == 6 ? av[5] : (char *)NULL);
+ if (ret == 0)
+ fprintf(stderr, dialog_input_result);
+ return ret;
+}
+
+int j_msgbox(const char *t, int ac, const char *const *av)
+{
+ return dialog_msgbox(t, av[2], atoi(av[3]), atoi(av[4]), 1);
+}
+
+int j_infobox(const char *t, int ac, const char *const *av)
+{
+ return dialog_msgbox(t, av[2], atoi(av[3]), atoi(av[4]), 0);
+}
diff --git a/i/pc104/initrd/conf/busybox/scripts/kconfig/lxdialog/menubox.c b/i/pc104/initrd/conf/busybox/scripts/kconfig/lxdialog/menubox.c
new file mode 100644
index 0000000..1fd501b
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/scripts/kconfig/lxdialog/menubox.c
@@ -0,0 +1,426 @@
+/*
+ * menubox.c -- implements the menu box
+ *
+ * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
+ * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcapw@cfw.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * Changes by Clifford Wolf (god@clifford.at)
+ *
+ * [ 1998-06-13 ]
+ *
+ * *) A bugfix for the Page-Down problem
+ *
+ * *) Formerly when I used Page Down and Page Up, the cursor would be set
+ * to the first position in the menu box. Now lxdialog is a bit
+ * smarter and works more like other menu systems (just have a look at
+ * it).
+ *
+ * *) Formerly if I selected something my scrolling would be broken because
+ * lxdialog is re-invoked by the Menuconfig shell script, can't
+ * remember the last scrolling position, and just sets it so that the
+ * cursor is at the bottom of the box. Now it writes the temporary file
+ * lxdialog.scrltmp which contains this information. The file is
+ * deleted by lxdialog if the user leaves a submenu or enters a new
+ * one, but it would be nice if Menuconfig could make another "rm -f"
+ * just to be sure. Just try it out - you will recognise a difference!
+ *
+ * [ 1998-06-14 ]
+ *
+ * *) Now lxdialog is crash-safe against broken "lxdialog.scrltmp" files
+ * and menus change their size on the fly.
+ *
+ * *) If for some reason the last scrolling position is not saved by
+ * lxdialog, it sets the scrolling so that the selected item is in the
+ * middle of the menu box, not at the bottom.
+ *
+ * 02 January 1999, Michael Elizabeth Chastain (mec@shout.net)
+ * Reset 'scroll' to 0 if the value from lxdialog.scrltmp is bogus.
+ * This fixes a bug in Menuconfig where using ' ' to descend into menus
+ * would leave mis-synchronized lxdialog.scrltmp files lying around,
+ * fscanf would read in 'scroll', and eventually that value would get used.
+ */
+
+#include "dialog.h"
+
+static int menu_width, item_x;
+
+/*
+ * Print menu item
+ */
+static void do_print_item(WINDOW * win, const char *item, int choice,
+ int selected, int hotkey)
+{
+ int j;
+ char *menu_item = malloc(menu_width + 1);
+
+ strncpy(menu_item, item, menu_width - item_x);
+ menu_item[menu_width] = 0;
+ j = first_alpha(menu_item, "YyNnMmHh");
+
+ /* Clear 'residue' of last item */
+ wattrset(win, menubox_attr);
+ wmove(win, choice, 0);
+#if OLD_NCURSES
+ {
+ int i;
+ for (i = 0; i < menu_width; i++)
+ waddch(win, ' ');
+ }
+#else
+ wclrtoeol(win);
+#endif
+ wattrset(win, selected ? item_selected_attr : item_attr);
+ mvwaddstr(win, choice, item_x, menu_item);
+ if (hotkey) {
+ wattrset(win, selected ? tag_key_selected_attr : tag_key_attr);
+ mvwaddch(win, choice, item_x + j, menu_item[j]);
+ }
+ if (selected) {
+ wmove(win, choice, item_x + 1);
+ }
+ free(menu_item);
+ wrefresh(win);
+}
+
+#define print_item(index, choice, selected) \
+do {\
+ int hotkey = (items[(index) * 2][0] != ':'); \
+ do_print_item(menu, items[(index) * 2 + 1], choice, selected, hotkey); \
+} while (0)
+
+/*
+ * Print the scroll indicators.
+ */
+static void print_arrows(WINDOW * win, int item_no, int scroll, int y, int x,
+ int height)
+{
+ int cur_y, cur_x;
+
+ getyx(win, cur_y, cur_x);
+
+ wmove(win, y, x);
+
+ if (scroll > 0) {
+ wattrset(win, uarrow_attr);
+ waddch(win, ACS_UARROW);
+ waddstr(win, "(-)");
+ } else {
+ wattrset(win, menubox_attr);
+ waddch(win, ACS_HLINE);
+ waddch(win, ACS_HLINE);
+ waddch(win, ACS_HLINE);
+ waddch(win, ACS_HLINE);
+ }
+
+ y = y + height + 1;
+ wmove(win, y, x);
+ wrefresh(win);
+
+ if ((height < item_no) && (scroll + height < item_no)) {
+ wattrset(win, darrow_attr);
+ waddch(win, ACS_DARROW);
+ waddstr(win, "(+)");
+ } else {
+ wattrset(win, menubox_border_attr);
+ waddch(win, ACS_HLINE);
+ waddch(win, ACS_HLINE);
+ waddch(win, ACS_HLINE);
+ waddch(win, ACS_HLINE);
+ }
+
+ wmove(win, cur_y, cur_x);
+ wrefresh(win);
+}
+
+/*
+ * Display the termination buttons.
+ */
+static void print_buttons(WINDOW * win, int height, int width, int selected)
+{
+ int x = width / 2 - 16;
+ int y = height - 2;
+
+ print_button(win, "Select", y, x, selected == 0);
+ print_button(win, " Exit ", y, x + 12, selected == 1);
+ print_button(win, " Help ", y, x + 24, selected == 2);
+
+ wmove(win, y, x + 1 + 12 * selected);
+ wrefresh(win);
+}
+
+/* scroll up n lines (n may be negative) */
+static void do_scroll(WINDOW *win, int *scroll, int n)
+{
+ /* Scroll menu up */
+ scrollok(win, TRUE);
+ wscrl(win, n);
+ scrollok(win, FALSE);
+ *scroll = *scroll + n;
+ wrefresh(win);
+}
+
+/*
+ * Display a menu for choosing among a number of options
+ */
+int dialog_menu(const char *title, const char *prompt, int height, int width,
+ int menu_height, const char *current, int item_no,
+ const char *const *items)
+{
+ int i, j, x, y, box_x, box_y;
+ int key = 0, button = 0, scroll = 0, choice = 0;
+ int first_item = 0, max_choice;
+ WINDOW *dialog, *menu;
+ FILE *f;
+
+ max_choice = MIN(menu_height, item_no);
+
+ /* center dialog box on screen */
+ x = (COLS - width) / 2;
+ y = (LINES - height) / 2;
+
+ draw_shadow(stdscr, y, x, height, width);
+
+ dialog = newwin(height, width, y, x);
+ keypad(dialog, TRUE);
+
+ draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr);
+ wattrset(dialog, border_attr);
+ mvwaddch(dialog, height - 3, 0, ACS_LTEE);
+ for (i = 0; i < width - 2; i++)
+ waddch(dialog, ACS_HLINE);
+ wattrset(dialog, dialog_attr);
+ wbkgdset(dialog, dialog_attr & A_COLOR);
+ waddch(dialog, ACS_RTEE);
+
+ print_title(dialog, title, width);
+
+ wattrset(dialog, dialog_attr);
+ print_autowrap(dialog, prompt, width - 2, 1, 3);
+
+ menu_width = width - 6;
+ box_y = height - menu_height - 5;
+ box_x = (width - menu_width) / 2 - 1;
+
+ /* create new window for the menu */
+ menu = subwin(dialog, menu_height, menu_width,
+ y + box_y + 1, x + box_x + 1);
+ keypad(menu, TRUE);
+
+ /* draw a box around the menu items */
+ draw_box(dialog, box_y, box_x, menu_height + 2, menu_width + 2,
+ menubox_border_attr, menubox_attr);
+
+ item_x = (menu_width - 70) / 2;
+
+ /* Set choice to default item */
+ for (i = 0; i < item_no; i++)
+ if (strcmp(current, items[i * 2]) == 0)
+ choice = i;
+
+ /* get the scroll info from the temp file */
+ if ((f = fopen("lxdialog.scrltmp", "r")) != NULL) {
+ if ((fscanf(f, "%d\n", &scroll) == 1) && (scroll <= choice) &&
+ (scroll + max_choice > choice) && (scroll >= 0) &&
+ (scroll + max_choice <= item_no)) {
+ first_item = scroll;
+ choice = choice - scroll;
+ fclose(f);
+ } else {
+ scroll = 0;
+ remove("lxdialog.scrltmp");
+ fclose(f);
+ f = NULL;
+ }
+ }
+ if ((choice >= max_choice) || (f == NULL && choice >= max_choice / 2)) {
+ if (choice >= item_no - max_choice / 2)
+ scroll = first_item = item_no - max_choice;
+ else
+ scroll = first_item = choice - max_choice / 2;
+ choice = choice - scroll;
+ }
+
+ /* Print the menu */
+ for (i = 0; i < max_choice; i++) {
+ print_item(first_item + i, i, i == choice);
+ }
+
+ wnoutrefresh(menu);
+
+ print_arrows(dialog, item_no, scroll,
+ box_y, box_x + item_x + 1, menu_height);
+
+ print_buttons(dialog, height, width, 0);
+ wmove(menu, choice, item_x + 1);
+ wrefresh(menu);
+
+ while (key != ESC) {
+ key = wgetch(menu);
+
+ if (key < 256 && isalpha(key))
+ key = tolower(key);
+
+ if (strchr("ynmh", key))
+ i = max_choice;
+ else {
+ for (i = choice + 1; i < max_choice; i++) {
+ j = first_alpha(items[(scroll + i) * 2 + 1], "YyNnMmHh");
+ if (key == tolower(items[(scroll + i) * 2 + 1][j]))
+ break;
+ }
+ if (i == max_choice)
+ for (i = 0; i < max_choice; i++) {
+ j = first_alpha(items [(scroll + i) * 2 + 1], "YyNnMmHh");
+ if (key == tolower(items[(scroll + i) * 2 + 1][j]))
+ break;
+ }
+ }
+
+ if (i < max_choice ||
+ key == KEY_UP || key == KEY_DOWN ||
+ key == '-' || key == '+' ||
+ key == KEY_PPAGE || key == KEY_NPAGE) {
+ /* Remove highligt of current item */
+ print_item(scroll + choice, choice, FALSE);
+
+ if (key == KEY_UP || key == '-') {
+ if (choice < 2 && scroll) {
+ /* Scroll menu down */
+ do_scroll(menu, &scroll, -1);
+
+ print_item(scroll, 0, FALSE);
+ } else
+ choice = MAX(choice - 1, 0);
+
+ } else if (key == KEY_DOWN || key == '+') {
+ print_item(scroll+choice, choice, FALSE);
+
+ if ((choice > max_choice - 3) &&
+ (scroll + max_choice < item_no)) {
+ /* Scroll menu up */
+ do_scroll(menu, &scroll, 1);
+
+ print_item(scroll+max_choice - 1,
+ max_choice - 1, FALSE);
+ } else
+ choice = MIN(choice + 1, max_choice - 1);
+
+ } else if (key == KEY_PPAGE) {
+ scrollok(menu, TRUE);
+ for (i = 0; (i < max_choice); i++) {
+ if (scroll > 0) {
+ do_scroll(menu, &scroll, -1);
+ print_item(scroll, 0, FALSE);
+ } else {
+ if (choice > 0)
+ choice--;
+ }
+ }
+
+ } else if (key == KEY_NPAGE) {
+ for (i = 0; (i < max_choice); i++) {
+ if (scroll + max_choice < item_no) {
+ do_scroll(menu, &scroll, 1);
+ print_item(scroll+max_choice-1,
+ max_choice - 1, FALSE);
+ } else {
+ if (choice + 1 < max_choice)
+ choice++;
+ }
+ }
+ } else
+ choice = i;
+
+ print_item(scroll + choice, choice, TRUE);
+
+ print_arrows(dialog, item_no, scroll,
+ box_y, box_x + item_x + 1, menu_height);
+
+ wnoutrefresh(dialog);
+ wrefresh(menu);
+
+ continue; /* wait for another key press */
+ }
+
+ switch (key) {
+ case KEY_LEFT:
+ case TAB:
+ case KEY_RIGHT:
+ button = ((key == KEY_LEFT ? --button : ++button) < 0)
+ ? 2 : (button > 2 ? 0 : button);
+
+ print_buttons(dialog, height, width, button);
+ wrefresh(menu);
+ break;
+ case ' ':
+ case 's':
+ case 'y':
+ case 'n':
+ case 'm':
+ case '/':
+ /* save scroll info */
+ if ((f = fopen("lxdialog.scrltmp", "w")) != NULL) {
+ fprintf(f, "%d\n", scroll);
+ fclose(f);
+ }
+ delwin(dialog);
+ fprintf(stderr, "%s\n", items[(scroll + choice) * 2]);
+ switch (key) {
+ case 's':
+ return 3;
+ case 'y':
+ return 3;
+ case 'n':
+ return 4;
+ case 'm':
+ return 5;
+ case ' ':
+ return 6;
+ case '/':
+ return 7;
+ }
+ return 0;
+ case 'h':
+ case '?':
+ button = 2;
+ case '\n':
+ delwin(dialog);
+ if (button == 2)
+ fprintf(stderr, "%s \"%s\"\n",
+ items[(scroll + choice) * 2],
+ items[(scroll + choice) * 2 + 1] +
+ first_alpha(items [(scroll + choice) * 2 + 1], ""));
+ else
+ fprintf(stderr, "%s\n",
+ items[(scroll + choice) * 2]);
+
+ remove("lxdialog.scrltmp");
+ return button;
+ case 'e':
+ case 'x':
+ key = ESC;
+ case ESC:
+ break;
+ }
+ }
+
+ delwin(dialog);
+ remove("lxdialog.scrltmp");
+ return -1; /* ESC pressed */
+}
diff --git a/i/pc104/initrd/conf/busybox/scripts/kconfig/lxdialog/msgbox.c b/i/pc104/initrd/conf/busybox/scripts/kconfig/lxdialog/msgbox.c
new file mode 100644
index 0000000..7323f54
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/scripts/kconfig/lxdialog/msgbox.c
@@ -0,0 +1,71 @@
+/*
+ * msgbox.c -- implements the message box and info box
+ *
+ * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
+ * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcapw@cfw.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "dialog.h"
+
+/*
+ * Display a message box. Program will pause and display an "OK" button
+ * if the parameter 'pause' is non-zero.
+ */
+int dialog_msgbox(const char *title, const char *prompt, int height, int width,
+ int pause)
+{
+ int i, x, y, key = 0;
+ WINDOW *dialog;
+
+ /* center dialog box on screen */
+ x = (COLS - width) / 2;
+ y = (LINES - height) / 2;
+
+ draw_shadow(stdscr, y, x, height, width);
+
+ dialog = newwin(height, width, y, x);
+ keypad(dialog, TRUE);
+
+ draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr);
+
+ print_title(dialog, title, width);
+
+ wattrset(dialog, dialog_attr);
+ print_autowrap(dialog, prompt, width - 2, 1, 2);
+
+ if (pause) {
+ wattrset(dialog, border_attr);
+ mvwaddch(dialog, height - 3, 0, ACS_LTEE);
+ for (i = 0; i < width - 2; i++)
+ waddch(dialog, ACS_HLINE);
+ wattrset(dialog, dialog_attr);
+ waddch(dialog, ACS_RTEE);
+
+ print_button(dialog, " Ok ", height - 2, width / 2 - 4, TRUE);
+
+ wrefresh(dialog);
+ while (key != ESC && key != '\n' && key != ' ' &&
+ key != 'O' && key != 'o' && key != 'X' && key != 'x')
+ key = wgetch(dialog);
+ } else {
+ key = '\n';
+ wrefresh(dialog);
+ }
+
+ delwin(dialog);
+ return key == ESC ? -1 : 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/scripts/kconfig/lxdialog/textbox.c b/i/pc104/initrd/conf/busybox/scripts/kconfig/lxdialog/textbox.c
new file mode 100644
index 0000000..77848bb
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/scripts/kconfig/lxdialog/textbox.c
@@ -0,0 +1,533 @@
+/*
+ * textbox.c -- implements the text box
+ *
+ * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
+ * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "dialog.h"
+
+static void back_lines(int n);
+static void print_page(WINDOW * win, int height, int width);
+static void print_line(WINDOW * win, int row, int width);
+static char *get_line(void);
+static void print_position(WINDOW * win, int height, int width);
+
+static int hscroll, fd, file_size, bytes_read;
+static int begin_reached = 1, end_reached, page_length;
+static char *buf, *page;
+
+/*
+ * Display text from a file in a dialog box.
+ */
+int dialog_textbox(const char *title, const char *file, int height, int width)
+{
+ int i, x, y, cur_x, cur_y, fpos, key = 0;
+ int passed_end;
+ char search_term[MAX_LEN + 1];
+ WINDOW *dialog, *text;
+
+ search_term[0] = '\0'; /* no search term entered yet */
+
+ /* Open input file for reading */
+ if ((fd = open(file, O_RDONLY)) == -1) {
+ endwin();
+ fprintf(stderr, "\nCan't open input file in dialog_textbox().\n");
+ exit(-1);
+ }
+ /* Get file size. Actually, 'file_size' is the real file size - 1,
+ since it's only the last byte offset from the beginning */
+ if ((file_size = lseek(fd, 0, SEEK_END)) == -1) {
+ endwin();
+ fprintf(stderr, "\nError getting file size in dialog_textbox().\n");
+ exit(-1);
+ }
+ /* Restore file pointer to beginning of file after getting file size */
+ if (lseek(fd, 0, SEEK_SET) == -1) {
+ endwin();
+ fprintf(stderr, "\nError moving file pointer in dialog_textbox().\n");
+ exit(-1);
+ }
+ /* Allocate space for read buffer */
+ if ((buf = malloc(BUF_SIZE + 1)) == NULL) {
+ endwin();
+ fprintf(stderr, "\nCan't allocate memory in dialog_textbox().\n");
+ exit(-1);
+ }
+ if ((bytes_read = read(fd, buf, BUF_SIZE)) == -1) {
+ endwin();
+ fprintf(stderr, "\nError reading file in dialog_textbox().\n");
+ exit(-1);
+ }
+ buf[bytes_read] = '\0'; /* mark end of valid data */
+ page = buf; /* page is pointer to start of page to be displayed */
+
+ /* center dialog box on screen */
+ x = (COLS - width) / 2;
+ y = (LINES - height) / 2;
+
+ draw_shadow(stdscr, y, x, height, width);
+
+ dialog = newwin(height, width, y, x);
+ keypad(dialog, TRUE);
+
+ /* Create window for text region, used for scrolling text */
+ text = subwin(dialog, height - 4, width - 2, y + 1, x + 1);
+ wattrset(text, dialog_attr);
+ wbkgdset(text, dialog_attr & A_COLOR);
+
+ keypad(text, TRUE);
+
+ /* register the new window, along with its borders */
+ draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr);
+
+ wattrset(dialog, border_attr);
+ mvwaddch(dialog, height - 3, 0, ACS_LTEE);
+ for (i = 0; i < width - 2; i++)
+ waddch(dialog, ACS_HLINE);
+ wattrset(dialog, dialog_attr);
+ wbkgdset(dialog, dialog_attr & A_COLOR);
+ waddch(dialog, ACS_RTEE);
+
+ print_title(dialog, title, width);
+
+ print_button(dialog, " Exit ", height - 2, width / 2 - 4, TRUE);
+ wnoutrefresh(dialog);
+ getyx(dialog, cur_y, cur_x); /* Save cursor position */
+
+ /* Print first page of text */
+ attr_clear(text, height - 4, width - 2, dialog_attr);
+ print_page(text, height - 4, width - 2);
+ print_position(dialog, height, width);
+ wmove(dialog, cur_y, cur_x); /* Restore cursor position */
+ wrefresh(dialog);
+
+ while ((key != ESC) && (key != '\n')) {
+ key = wgetch(dialog);
+ switch (key) {
+ case 'E': /* Exit */
+ case 'e':
+ case 'X':
+ case 'x':
+ delwin(dialog);
+ free(buf);
+ close(fd);
+ return 0;
+ case 'g': /* First page */
+ case KEY_HOME:
+ if (!begin_reached) {
+ begin_reached = 1;
+ /* First page not in buffer? */
+ if ((fpos = lseek(fd, 0, SEEK_CUR)) == -1) {
+ endwin();
+ fprintf(stderr, "\nError moving file pointer in dialog_textbox().\n");
+ exit(-1);
+ }
+ if (fpos > bytes_read) { /* Yes, we have to read it in */
+ if (lseek(fd, 0, SEEK_SET) == -1) {
+ endwin();
+ fprintf(stderr, "\nError moving file pointer in "
+ "dialog_textbox().\n");
+ exit(-1);
+ }
+ if ((bytes_read =
+ read(fd, buf, BUF_SIZE)) == -1) {
+ endwin();
+ fprintf(stderr, "\nError reading file in dialog_textbox().\n");
+ exit(-1);
+ }
+ buf[bytes_read] = '\0';
+ }
+ page = buf;
+ print_page(text, height - 4, width - 2);
+ print_position(dialog, height, width);
+ wmove(dialog, cur_y, cur_x); /* Restore cursor position */
+ wrefresh(dialog);
+ }
+ break;
+ case 'G': /* Last page */
+ case KEY_END:
+
+ end_reached = 1;
+ /* Last page not in buffer? */
+ if ((fpos = lseek(fd, 0, SEEK_CUR)) == -1) {
+ endwin();
+ fprintf(stderr, "\nError moving file pointer in dialog_textbox().\n");
+ exit(-1);
+ }
+ if (fpos < file_size) { /* Yes, we have to read it in */
+ if (lseek(fd, -BUF_SIZE, SEEK_END) == -1) {
+ endwin();
+ fprintf(stderr, "\nError moving file pointer in dialog_textbox().\n");
+ exit(-1);
+ }
+ if ((bytes_read =
+ read(fd, buf, BUF_SIZE)) == -1) {
+ endwin();
+ fprintf(stderr, "\nError reading file in dialog_textbox().\n");
+ exit(-1);
+ }
+ buf[bytes_read] = '\0';
+ }
+ page = buf + bytes_read;
+ back_lines(height - 4);
+ print_page(text, height - 4, width - 2);
+ print_position(dialog, height, width);
+ wmove(dialog, cur_y, cur_x); /* Restore cursor position */
+ wrefresh(dialog);
+ break;
+ case 'K': /* Previous line */
+ case 'k':
+ case KEY_UP:
+ if (!begin_reached) {
+ back_lines(page_length + 1);
+
+ /* We don't call print_page() here but use scrolling to ensure
+ faster screen update. However, 'end_reached' and
+ 'page_length' should still be updated, and 'page' should
+ point to start of next page. This is done by calling
+ get_line() in the following 'for' loop. */
+ scrollok(text, TRUE);
+ wscrl(text, -1); /* Scroll text region down one line */
+ scrollok(text, FALSE);
+ page_length = 0;
+ passed_end = 0;
+ for (i = 0; i < height - 4; i++) {
+ if (!i) {
+ /* print first line of page */
+ print_line(text, 0, width - 2);
+ wnoutrefresh(text);
+ } else
+ /* Called to update 'end_reached' and 'page' */
+ get_line();
+ if (!passed_end)
+ page_length++;
+ if (end_reached && !passed_end)
+ passed_end = 1;
+ }
+
+ print_position(dialog, height, width);
+ wmove(dialog, cur_y, cur_x); /* Restore cursor position */
+ wrefresh(dialog);
+ }
+ break;
+ case 'B': /* Previous page */
+ case 'b':
+ case KEY_PPAGE:
+ if (begin_reached)
+ break;
+ back_lines(page_length + height - 4);
+ print_page(text, height - 4, width - 2);
+ print_position(dialog, height, width);
+ wmove(dialog, cur_y, cur_x);
+ wrefresh(dialog);
+ break;
+ case 'J': /* Next line */
+ case 'j':
+ case KEY_DOWN:
+ if (!end_reached) {
+ begin_reached = 0;
+ scrollok(text, TRUE);
+ scroll(text); /* Scroll text region up one line */
+ scrollok(text, FALSE);
+ print_line(text, height - 5, width - 2);
+ wnoutrefresh(text);
+ print_position(dialog, height, width);
+ wmove(dialog, cur_y, cur_x); /* Restore cursor position */
+ wrefresh(dialog);
+ }
+ break;
+ case KEY_NPAGE: /* Next page */
+ case ' ':
+ if (end_reached)
+ break;
+
+ begin_reached = 0;
+ print_page(text, height - 4, width - 2);
+ print_position(dialog, height, width);
+ wmove(dialog, cur_y, cur_x);
+ wrefresh(dialog);
+ break;
+ case '0': /* Beginning of line */
+ case 'H': /* Scroll left */
+ case 'h':
+ case KEY_LEFT:
+ if (hscroll <= 0)
+ break;
+
+ if (key == '0')
+ hscroll = 0;
+ else
+ hscroll--;
+ /* Reprint current page to scroll horizontally */
+ back_lines(page_length);
+ print_page(text, height - 4, width - 2);
+ wmove(dialog, cur_y, cur_x);
+ wrefresh(dialog);
+ break;
+ case 'L': /* Scroll right */
+ case 'l':
+ case KEY_RIGHT:
+ if (hscroll >= MAX_LEN)
+ break;
+ hscroll++;
+ /* Reprint current page to scroll horizontally */
+ back_lines(page_length);
+ print_page(text, height - 4, width - 2);
+ wmove(dialog, cur_y, cur_x);
+ wrefresh(dialog);
+ break;
+ case ESC:
+ break;
+ }
+ }
+
+ delwin(dialog);
+ free(buf);
+ close(fd);
+ return -1; /* ESC pressed */
+}
+
+/*
+ * Go back 'n' lines in text file. Called by dialog_textbox().
+ * 'page' will be updated to point to the desired line in 'buf'.
+ */
+static void back_lines(int n)
+{
+ int i, fpos;
+
+ begin_reached = 0;
+ /* We have to distinguish between end_reached and !end_reached
+ since at end of file, the line is not ended by a '\n'.
+ The code inside 'if' basically does a '--page' to move one
+ character backward so as to skip '\n' of the previous line */
+ if (!end_reached) {
+ /* Either beginning of buffer or beginning of file reached? */
+ if (page == buf) {
+ if ((fpos = lseek(fd, 0, SEEK_CUR)) == -1) {
+ endwin();
+ fprintf(stderr, "\nError moving file pointer in "
+ "back_lines().\n");
+ exit(-1);
+ }
+ if (fpos > bytes_read) { /* Not beginning of file yet */
+ /* We've reached beginning of buffer, but not beginning of
+ file yet, so read previous part of file into buffer.
+ Note that we only move backward for BUF_SIZE/2 bytes,
+ but not BUF_SIZE bytes to avoid re-reading again in
+ print_page() later */
+ /* Really possible to move backward BUF_SIZE/2 bytes? */
+ if (fpos < BUF_SIZE / 2 + bytes_read) {
+ /* No, move less then */
+ if (lseek(fd, 0, SEEK_SET) == -1) {
+ endwin();
+ fprintf(stderr, "\nError moving file pointer in "
+ "back_lines().\n");
+ exit(-1);
+ }
+ page = buf + fpos - bytes_read;
+ } else { /* Move backward BUF_SIZE/2 bytes */
+ if (lseek (fd, -(BUF_SIZE / 2 + bytes_read), SEEK_CUR) == -1) {
+ endwin();
+ fprintf(stderr, "\nError moving file pointer "
+ "in back_lines().\n");
+ exit(-1);
+ }
+ page = buf + BUF_SIZE / 2;
+ }
+ if ((bytes_read =
+ read(fd, buf, BUF_SIZE)) == -1) {
+ endwin();
+ fprintf(stderr, "\nError reading file in back_lines().\n");
+ exit(-1);
+ }
+ buf[bytes_read] = '\0';
+ } else { /* Beginning of file reached */
+ begin_reached = 1;
+ return;
+ }
+ }
+ if (*(--page) != '\n') { /* '--page' here */
+ /* Something's wrong... */
+ endwin();
+ fprintf(stderr, "\nInternal error in back_lines().\n");
+ exit(-1);
+ }
+ }
+ /* Go back 'n' lines */
+ for (i = 0; i < n; i++)
+ do {
+ if (page == buf) {
+ if ((fpos = lseek(fd, 0, SEEK_CUR)) == -1) {
+ endwin();
+ fprintf(stderr, "\nError moving file pointer in back_lines().\n");
+ exit(-1);
+ }
+ if (fpos > bytes_read) {
+ /* Really possible to move backward BUF_SIZE/2 bytes? */
+ if (fpos < BUF_SIZE / 2 + bytes_read) {
+ /* No, move less then */
+ if (lseek(fd, 0, SEEK_SET) == -1) {
+ endwin();
+ fprintf(stderr, "\nError moving file pointer "
+ "in back_lines().\n");
+ exit(-1);
+ }
+ page = buf + fpos - bytes_read;
+ } else { /* Move backward BUF_SIZE/2 bytes */
+ if (lseek (fd, -(BUF_SIZE / 2 + bytes_read), SEEK_CUR) == -1) {
+ endwin();
+ fprintf(stderr, "\nError moving file pointer"
+ " in back_lines().\n");
+ exit(-1);
+ }
+ page = buf + BUF_SIZE / 2;
+ }
+ if ((bytes_read =
+ read(fd, buf, BUF_SIZE)) == -1) {
+ endwin();
+ fprintf(stderr, "\nError reading file in "
+ "back_lines().\n");
+ exit(-1);
+ }
+ buf[bytes_read] = '\0';
+ } else { /* Beginning of file reached */
+ begin_reached = 1;
+ return;
+ }
+ }
+ } while (*(--page) != '\n');
+ page++;
+}
+
+/*
+ * Print a new page of text. Called by dialog_textbox().
+ */
+static void print_page(WINDOW * win, int height, int width)
+{
+ int i, passed_end = 0;
+
+ page_length = 0;
+ for (i = 0; i < height; i++) {
+ print_line(win, i, width);
+ if (!passed_end)
+ page_length++;
+ if (end_reached && !passed_end)
+ passed_end = 1;
+ }
+ wnoutrefresh(win);
+}
+
+/*
+ * Print a new line of text. Called by dialog_textbox() and print_page().
+ */
+static void print_line(WINDOW * win, int row, int width)
+{
+ int y, x;
+ char *line;
+
+ line = get_line();
+ line += MIN(strlen(line), hscroll); /* Scroll horizontally */
+ wmove(win, row, 0); /* move cursor to correct line */
+ waddch(win, ' ');
+ waddnstr(win, line, MIN(strlen(line), width - 2));
+
+ getyx(win, y, x);
+ /* Clear 'residue' of previous line */
+#if OLD_NCURSES
+ {
+ int i;
+ for (i = 0; i < width - x; i++)
+ waddch(win, ' ');
+ }
+#else
+ wclrtoeol(win);
+#endif
+}
+
+/*
+ * Return current line of text. Called by dialog_textbox() and print_line().
+ * 'page' should point to start of current line before calling, and will be
+ * updated to point to start of next line.
+ */
+static char *get_line(void)
+{
+ int i = 0, fpos;
+ static char line[MAX_LEN + 1];
+
+ end_reached = 0;
+ while (*page != '\n') {
+ if (*page == '\0') {
+ /* Either end of file or end of buffer reached */
+ if ((fpos = lseek(fd, 0, SEEK_CUR)) == -1) {
+ endwin();
+ fprintf(stderr, "\nError moving file pointer in "
+ "get_line().\n");
+ exit(-1);
+ }
+ if (fpos < file_size) { /* Not end of file yet */
+ /* We've reached end of buffer, but not end of file yet,
+ so read next part of file into buffer */
+ if ((bytes_read =
+ read(fd, buf, BUF_SIZE)) == -1) {
+ endwin();
+ fprintf(stderr, "\nError reading file in get_line().\n");
+ exit(-1);
+ }
+ buf[bytes_read] = '\0';
+ page = buf;
+ } else {
+ if (!end_reached)
+ end_reached = 1;
+ break;
+ }
+ } else if (i < MAX_LEN)
+ line[i++] = *(page++);
+ else {
+ /* Truncate lines longer than MAX_LEN characters */
+ if (i == MAX_LEN)
+ line[i++] = '\0';
+ page++;
+ }
+ }
+ if (i <= MAX_LEN)
+ line[i] = '\0';
+ if (!end_reached)
+ page++; /* move pass '\n' */
+
+ return line;
+}
+
+/*
+ * Print current position
+ */
+static void print_position(WINDOW * win, int height, int width)
+{
+ int fpos, percent;
+
+ if ((fpos = lseek(fd, 0, SEEK_CUR)) == -1) {
+ endwin();
+ fprintf(stderr, "\nError moving file pointer in print_position().\n");
+ exit(-1);
+ }
+ wattrset(win, position_indicator_attr);
+ wbkgdset(win, position_indicator_attr & A_COLOR);
+ percent = !file_size ?
+ 100 : ((fpos - bytes_read + page - buf) * 100) / file_size;
+ wmove(win, height - 3, width - 9);
+ wprintw(win, "(%3d%%)", percent);
+}
diff --git a/i/pc104/initrd/conf/busybox/scripts/kconfig/lxdialog/util.c b/i/pc104/initrd/conf/busybox/scripts/kconfig/lxdialog/util.c
new file mode 100644
index 0000000..072d3ee
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/scripts/kconfig/lxdialog/util.c
@@ -0,0 +1,362 @@
+/*
+ * util.c
+ *
+ * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
+ * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "dialog.h"
+
+/* use colors by default? */
+bool use_colors = 1;
+
+const char *backtitle = NULL;
+
+/*
+ * Attribute values, default is for mono display
+ */
+chtype attributes[] = {
+ A_NORMAL, /* screen_attr */
+ A_NORMAL, /* shadow_attr */
+ A_NORMAL, /* dialog_attr */
+ A_BOLD, /* title_attr */
+ A_NORMAL, /* border_attr */
+ A_REVERSE, /* button_active_attr */
+ A_DIM, /* button_inactive_attr */
+ A_REVERSE, /* button_key_active_attr */
+ A_BOLD, /* button_key_inactive_attr */
+ A_REVERSE, /* button_label_active_attr */
+ A_NORMAL, /* button_label_inactive_attr */
+ A_NORMAL, /* inputbox_attr */
+ A_NORMAL, /* inputbox_border_attr */
+ A_NORMAL, /* searchbox_attr */
+ A_BOLD, /* searchbox_title_attr */
+ A_NORMAL, /* searchbox_border_attr */
+ A_BOLD, /* position_indicator_attr */
+ A_NORMAL, /* menubox_attr */
+ A_NORMAL, /* menubox_border_attr */
+ A_NORMAL, /* item_attr */
+ A_REVERSE, /* item_selected_attr */
+ A_BOLD, /* tag_attr */
+ A_REVERSE, /* tag_selected_attr */
+ A_BOLD, /* tag_key_attr */
+ A_REVERSE, /* tag_key_selected_attr */
+ A_BOLD, /* check_attr */
+ A_REVERSE, /* check_selected_attr */
+ A_BOLD, /* uarrow_attr */
+ A_BOLD /* darrow_attr */
+};
+
+#include "colors.h"
+
+/*
+ * Table of color values
+ */
+int color_table[][3] = {
+ {SCREEN_FG, SCREEN_BG, SCREEN_HL},
+ {SHADOW_FG, SHADOW_BG, SHADOW_HL},
+ {DIALOG_FG, DIALOG_BG, DIALOG_HL},
+ {TITLE_FG, TITLE_BG, TITLE_HL},
+ {BORDER_FG, BORDER_BG, BORDER_HL},
+ {BUTTON_ACTIVE_FG, BUTTON_ACTIVE_BG, BUTTON_ACTIVE_HL},
+ {BUTTON_INACTIVE_FG, BUTTON_INACTIVE_BG, BUTTON_INACTIVE_HL},
+ {BUTTON_KEY_ACTIVE_FG, BUTTON_KEY_ACTIVE_BG, BUTTON_KEY_ACTIVE_HL},
+ {BUTTON_KEY_INACTIVE_FG, BUTTON_KEY_INACTIVE_BG,
+ BUTTON_KEY_INACTIVE_HL},
+ {BUTTON_LABEL_ACTIVE_FG, BUTTON_LABEL_ACTIVE_BG,
+ BUTTON_LABEL_ACTIVE_HL},
+ {BUTTON_LABEL_INACTIVE_FG, BUTTON_LABEL_INACTIVE_BG,
+ BUTTON_LABEL_INACTIVE_HL},
+ {INPUTBOX_FG, INPUTBOX_BG, INPUTBOX_HL},
+ {INPUTBOX_BORDER_FG, INPUTBOX_BORDER_BG, INPUTBOX_BORDER_HL},
+ {SEARCHBOX_FG, SEARCHBOX_BG, SEARCHBOX_HL},
+ {SEARCHBOX_TITLE_FG, SEARCHBOX_TITLE_BG, SEARCHBOX_TITLE_HL},
+ {SEARCHBOX_BORDER_FG, SEARCHBOX_BORDER_BG, SEARCHBOX_BORDER_HL},
+ {POSITION_INDICATOR_FG, POSITION_INDICATOR_BG, POSITION_INDICATOR_HL},
+ {MENUBOX_FG, MENUBOX_BG, MENUBOX_HL},
+ {MENUBOX_BORDER_FG, MENUBOX_BORDER_BG, MENUBOX_BORDER_HL},
+ {ITEM_FG, ITEM_BG, ITEM_HL},
+ {ITEM_SELECTED_FG, ITEM_SELECTED_BG, ITEM_SELECTED_HL},
+ {TAG_FG, TAG_BG, TAG_HL},
+ {TAG_SELECTED_FG, TAG_SELECTED_BG, TAG_SELECTED_HL},
+ {TAG_KEY_FG, TAG_KEY_BG, TAG_KEY_HL},
+ {TAG_KEY_SELECTED_FG, TAG_KEY_SELECTED_BG, TAG_KEY_SELECTED_HL},
+ {CHECK_FG, CHECK_BG, CHECK_HL},
+ {CHECK_SELECTED_FG, CHECK_SELECTED_BG, CHECK_SELECTED_HL},
+ {UARROW_FG, UARROW_BG, UARROW_HL},
+ {DARROW_FG, DARROW_BG, DARROW_HL},
+}; /* color_table */
+
+/*
+ * Set window to attribute 'attr'
+ */
+void attr_clear(WINDOW * win, int height, int width, chtype attr)
+{
+ int i, j;
+
+ wattrset(win, attr);
+ for (i = 0; i < height; i++) {
+ wmove(win, i, 0);
+ for (j = 0; j < width; j++)
+ waddch(win, ' ');
+ }
+ touchwin(win);
+}
+
+void dialog_clear(void)
+{
+ attr_clear(stdscr, LINES, COLS, screen_attr);
+ /* Display background title if it exists ... - SLH */
+ if (backtitle != NULL) {
+ int i;
+
+ wattrset(stdscr, screen_attr);
+ mvwaddstr(stdscr, 0, 1, (char *)backtitle);
+ wmove(stdscr, 1, 1);
+ for (i = 1; i < COLS - 1; i++)
+ waddch(stdscr, ACS_HLINE);
+ }
+ wnoutrefresh(stdscr);
+}
+
+/*
+ * Do some initialization for dialog
+ */
+void init_dialog(void)
+{
+ initscr(); /* Init curses */
+ keypad(stdscr, TRUE);
+ cbreak();
+ noecho();
+
+ if (use_colors) /* Set up colors */
+ color_setup();
+
+ dialog_clear();
+}
+
+/*
+ * Setup for color display
+ */
+void color_setup(void)
+{
+ int i;
+
+ if (has_colors()) { /* Terminal supports color? */
+ start_color();
+
+ /* Initialize color pairs */
+ for (i = 0; i < ATTRIBUTE_COUNT; i++)
+ init_pair(i + 1, color_table[i][0], color_table[i][1]);
+
+ /* Setup color attributes */
+ for (i = 0; i < ATTRIBUTE_COUNT; i++)
+ attributes[i] = C_ATTR(color_table[i][2], i + 1);
+ }
+}
+
+/*
+ * End using dialog functions.
+ */
+void end_dialog(void)
+{
+ endwin();
+}
+
+/* Print the title of the dialog. Center the title and truncate
+ * tile if wider than dialog (- 2 chars).
+ **/
+void print_title(WINDOW *dialog, const char *title, int width)
+{
+ if (title) {
+ int tlen = MIN(width - 2, strlen(title));
+ wattrset(dialog, title_attr);
+ mvwaddch(dialog, 0, (width - tlen) / 2 - 1, ' ');
+ mvwaddnstr(dialog, 0, (width - tlen)/2, title, tlen);
+ waddch(dialog, ' ');
+ }
+}
+
+/*
+ * Print a string of text in a window, automatically wrap around to the
+ * next line if the string is too long to fit on one line. Newline
+ * characters '\n' are replaced by spaces. We start on a new line
+ * if there is no room for at least 4 nonblanks following a double-space.
+ */
+void print_autowrap(WINDOW * win, const char *prompt, int width, int y, int x)
+{
+ int newl, cur_x, cur_y;
+ int i, prompt_len, room, wlen;
+ char tempstr[MAX_LEN + 1], *word, *sp, *sp2;
+
+ strcpy(tempstr, prompt);
+
+ prompt_len = strlen(tempstr);
+
+ /*
+ * Remove newlines
+ */
+ for (i = 0; i < prompt_len; i++) {
+ if (tempstr[i] == '\n')
+ tempstr[i] = ' ';
+ }
+
+ if (prompt_len <= width - x * 2) { /* If prompt is short */
+ wmove(win, y, (width - prompt_len) / 2);
+ waddstr(win, tempstr);
+ } else {
+ cur_x = x;
+ cur_y = y;
+ newl = 1;
+ word = tempstr;
+ while (word && *word) {
+ sp = strchr(word, ' ');
+ if (sp)
+ *sp++ = 0;
+
+ /* Wrap to next line if either the word does not fit,
+ or it is the first word of a new sentence, and it is
+ short, and the next word does not fit. */
+ room = width - cur_x;
+ wlen = strlen(word);
+ if (wlen > room ||
+ (newl && wlen < 4 && sp
+ && wlen + 1 + strlen(sp) > room
+ && (!(sp2 = strchr(sp, ' '))
+ || wlen + 1 + (sp2 - sp) > room))) {
+ cur_y++;
+ cur_x = x;
+ }
+ wmove(win, cur_y, cur_x);
+ waddstr(win, word);
+ getyx(win, cur_y, cur_x);
+ cur_x++;
+ if (sp && *sp == ' ') {
+ cur_x++; /* double space */
+ while (*++sp == ' ') ;
+ newl = 1;
+ } else
+ newl = 0;
+ word = sp;
+ }
+ }
+}
+
+/*
+ * Print a button
+ */
+void print_button(WINDOW * win, const char *label, int y, int x, int selected)
+{
+ int i, temp;
+
+ wmove(win, y, x);
+ wattrset(win, selected ? button_active_attr : button_inactive_attr);
+ waddstr(win, "<");
+ temp = strspn(label, " ");
+ label += temp;
+ wattrset(win, selected ? button_label_active_attr
+ : button_label_inactive_attr);
+ for (i = 0; i < temp; i++)
+ waddch(win, ' ');
+ wattrset(win, selected ? button_key_active_attr
+ : button_key_inactive_attr);
+ waddch(win, label[0]);
+ wattrset(win, selected ? button_label_active_attr
+ : button_label_inactive_attr);
+ waddstr(win, (char *)label + 1);
+ wattrset(win, selected ? button_active_attr : button_inactive_attr);
+ waddstr(win, ">");
+ wmove(win, y, x + temp + 1);
+}
+
+/*
+ * Draw a rectangular box with line drawing characters
+ */
+void
+draw_box(WINDOW * win, int y, int x, int height, int width,
+ chtype box, chtype border)
+{
+ int i, j;
+
+ wattrset(win, 0);
+ for (i = 0; i < height; i++) {
+ wmove(win, y + i, x);
+ for (j = 0; j < width; j++)
+ if (!i && !j)
+ waddch(win, border | ACS_ULCORNER);
+ else if (i == height - 1 && !j)
+ waddch(win, border | ACS_LLCORNER);
+ else if (!i && j == width - 1)
+ waddch(win, box | ACS_URCORNER);
+ else if (i == height - 1 && j == width - 1)
+ waddch(win, box | ACS_LRCORNER);
+ else if (!i)
+ waddch(win, border | ACS_HLINE);
+ else if (i == height - 1)
+ waddch(win, box | ACS_HLINE);
+ else if (!j)
+ waddch(win, border | ACS_VLINE);
+ else if (j == width - 1)
+ waddch(win, box | ACS_VLINE);
+ else
+ waddch(win, box | ' ');
+ }
+}
+
+/*
+ * Draw shadows along the right and bottom edge to give a more 3D look
+ * to the boxes
+ */
+void draw_shadow(WINDOW * win, int y, int x, int height, int width)
+{
+ int i;
+
+ if (has_colors()) { /* Whether terminal supports color? */
+ wattrset(win, shadow_attr);
+ wmove(win, y + height, x + 2);
+ for (i = 0; i < width; i++)
+ waddch(win, winch(win) & A_CHARTEXT);
+ for (i = y + 1; i < y + height + 1; i++) {
+ wmove(win, i, x + width);
+ waddch(win, winch(win) & A_CHARTEXT);
+ waddch(win, winch(win) & A_CHARTEXT);
+ }
+ wnoutrefresh(win);
+ }
+}
+
+/*
+ * Return the position of the first alphabetic character in a string.
+ */
+int first_alpha(const char *string, const char *exempt)
+{
+ int i, in_paren = 0, c;
+
+ for (i = 0; i < strlen(string); i++) {
+ c = tolower(string[i]);
+
+ if (strchr("<[(", c))
+ ++in_paren;
+ if (strchr(">])", c) && in_paren > 0)
+ --in_paren;
+
+ if ((!in_paren) && isalpha(c) && strchr(exempt, c) == 0)
+ return i;
+ }
+
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/scripts/kconfig/lxdialog/yesno.c b/i/pc104/initrd/conf/busybox/scripts/kconfig/lxdialog/yesno.c
new file mode 100644
index 0000000..cb2568a
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/scripts/kconfig/lxdialog/yesno.c
@@ -0,0 +1,102 @@
+/*
+ * yesno.c -- implements the yes/no box
+ *
+ * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
+ * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "dialog.h"
+
+/*
+ * Display termination buttons
+ */
+static void print_buttons(WINDOW * dialog, int height, int width, int selected)
+{
+ int x = width / 2 - 10;
+ int y = height - 2;
+
+ print_button(dialog, " Yes ", y, x, selected == 0);
+ print_button(dialog, " No ", y, x + 13, selected == 1);
+
+ wmove(dialog, y, x + 1 + 13 * selected);
+ wrefresh(dialog);
+}
+
+/*
+ * Display a dialog box with two buttons - Yes and No
+ */
+int dialog_yesno(const char *title, const char *prompt, int height, int width)
+{
+ int i, x, y, key = 0, button = 0;
+ WINDOW *dialog;
+
+ /* center dialog box on screen */
+ x = (COLS - width) / 2;
+ y = (LINES - height) / 2;
+
+ draw_shadow(stdscr, y, x, height, width);
+
+ dialog = newwin(height, width, y, x);
+ keypad(dialog, TRUE);
+
+ draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr);
+ wattrset(dialog, border_attr);
+ mvwaddch(dialog, height - 3, 0, ACS_LTEE);
+ for (i = 0; i < width - 2; i++)
+ waddch(dialog, ACS_HLINE);
+ wattrset(dialog, dialog_attr);
+ waddch(dialog, ACS_RTEE);
+
+ print_title(dialog, title, width);
+
+ wattrset(dialog, dialog_attr);
+ print_autowrap(dialog, prompt, width - 2, 1, 3);
+
+ print_buttons(dialog, height, width, 0);
+
+ while (key != ESC) {
+ key = wgetch(dialog);
+ switch (key) {
+ case 'Y':
+ case 'y':
+ delwin(dialog);
+ return 0;
+ case 'N':
+ case 'n':
+ delwin(dialog);
+ return 1;
+
+ case TAB:
+ case KEY_LEFT:
+ case KEY_RIGHT:
+ button = ((key == KEY_LEFT ? --button : ++button) < 0) ? 1 : (button > 1 ? 0 : button);
+
+ print_buttons(dialog, height, width, button);
+ wrefresh(dialog);
+ break;
+ case ' ':
+ case '\n':
+ delwin(dialog);
+ return button;
+ case ESC:
+ break;
+ }
+ }
+
+ delwin(dialog);
+ return -1; /* ESC pressed */
+}
diff --git a/i/pc104/initrd/conf/busybox/scripts/kconfig/mconf.c b/i/pc104/initrd/conf/busybox/scripts/kconfig/mconf.c
new file mode 100644
index 0000000..810b018
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/scripts/kconfig/mconf.c
@@ -0,0 +1,1098 @@
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ * Released under the terms of the GNU GPL v2.0.
+ *
+ * Introduced single menu mode (show all sub-menus in one large tree).
+ * 2002-11-06 Petr Baudis <pasky@ucw.cz>
+ *
+ * i18n, 2005, Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ */
+
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+#include <locale.h>
+
+#define LKC_DIRECT_LINK
+#include "lkc.h"
+
+static char menu_backtitle[128];
+static const char mconf_readme[] = N_(
+"Overview\n"
+"--------\n"
+"Some features may be built directly into busybox.\n"
+"Some may be made into standalone applets. Some features\n"
+"may be completely removed altogether. There are also certain\n"
+"parameters which are not really features, but must be\n"
+"entered in as decimal or hexadecimal numbers or possibly text.\n"
+"\n"
+"Menu items beginning with [*], <M> or [ ] represent features\n"
+"configured to be built in, modularized or removed respectively.\n"
+"Pointed brackets <> represent module capable features.\n"
+"\n"
+"To change any of these features, highlight it with the cursor\n"
+"keys and press <Y> to build it in, <M> to make it a module or\n"
+"<N> to removed it. You may also press the <Space Bar> to cycle\n"
+"through the available options (ie. Y->N->M->Y).\n"
+"\n"
+"Some additional keyboard hints:\n"
+"\n"
+"Menus\n"
+"----------\n"
+"o Use the Up/Down arrow keys (cursor keys) to highlight the item\n"
+" you wish to change or submenu wish to select and press <Enter>.\n"
+" Submenus are designated by \"--->\".\n"
+"\n"
+" Shortcut: Press the option's highlighted letter (hotkey).\n"
+" Pressing a hotkey more than once will sequence\n"
+" through all visible items which use that hotkey.\n"
+"\n"
+" You may also use the <PAGE UP> and <PAGE DOWN> keys to scroll\n"
+" unseen options into view.\n"
+"\n"
+"o To exit a menu use the cursor keys to highlight the <Exit> button\n"
+" and press <ENTER>.\n"
+"\n"
+" Shortcut: Press <ESC><ESC> or <E> or <X> if there is no hotkey\n"
+" using those letters. You may press a single <ESC>, but\n"
+" there is a delayed response which you may find annoying.\n"
+"\n"
+" Also, the <TAB> and cursor keys will cycle between <Select>,\n"
+" <Exit> and <Help>\n"
+"\n"
+"o To get help with an item, use the cursor keys to highlight <Help>\n"
+" and Press <ENTER>.\n"
+"\n"
+" Shortcut: Press <H> or <?>.\n"
+"\n"
+"\n"
+"Radiolists (Choice lists)\n"
+"-----------\n"
+"o Use the cursor keys to select the option you wish to set and press\n"
+" <S> or the <SPACE BAR>.\n"
+"\n"
+" Shortcut: Press the first letter of the option you wish to set then\n"
+" press <S> or <SPACE BAR>.\n"
+"\n"
+"o To see available help for the item, use the cursor keys to highlight\n"
+" <Help> and Press <ENTER>.\n"
+"\n"
+" Shortcut: Press <H> or <?>.\n"
+"\n"
+" Also, the <TAB> and cursor keys will cycle between <Select> and\n"
+" <Help>\n"
+"\n"
+"\n"
+"Data Entry\n"
+"-----------\n"
+"o Enter the requested information and press <ENTER>\n"
+" If you are entering hexadecimal values, it is not necessary to\n"
+" add the '0x' prefix to the entry.\n"
+"\n"
+"o For help, use the <TAB> or cursor keys to highlight the help option\n"
+" and press <ENTER>. You can try <TAB><H> as well.\n"
+"\n"
+"\n"
+"Text Box (Help Window)\n"
+"--------\n"
+"o Use the cursor keys to scroll up/down/left/right. The VI editor\n"
+" keys h,j,k,l function here as do <SPACE BAR> and <B> for those\n"
+" who are familiar with less and lynx.\n"
+"\n"
+"o Press <E>, <X>, <Enter> or <Esc><Esc> to exit.\n"
+"\n"
+"\n"
+"Alternate Configuration Files\n"
+"-----------------------------\n"
+"Menuconfig supports the use of alternate configuration files for\n"
+"those who, for various reasons, find it necessary to switch\n"
+"between different busybox configurations.\n"
+"\n"
+"At the end of the main menu you will find two options. One is\n"
+"for saving the current configuration to a file of your choosing.\n"
+"The other option is for loading a previously saved alternate\n"
+"configuration.\n"
+"\n"
+"Even if you don't use alternate configuration files, but you\n"
+"find during a Menuconfig session that you have completely messed\n"
+"up your settings, you may use the \"Load Alternate...\" option to\n"
+"restore your previously saved settings from \".config\" without\n"
+"restarting Menuconfig.\n"
+"\n"
+"Other information\n"
+"-----------------\n"
+"If you use Menuconfig in an XTERM window make sure you have your\n"
+"$TERM variable set to point to a xterm definition which supports color.\n"
+"Otherwise, Menuconfig will look rather bad. Menuconfig will not\n"
+"display correctly in a RXVT window because rxvt displays only one\n"
+"intensity of color, bright.\n"
+"\n"
+"Menuconfig will display larger menus on screens or xterms which are\n"
+"set to display more than the standard 25 row by 80 column geometry.\n"
+"In order for this to work, the \"stty size\" command must be able to\n"
+"display the screen's current row and column geometry. I STRONGLY\n"
+"RECOMMEND that you make sure you do NOT have the shell variables\n"
+"LINES and COLUMNS exported into your environment. Some distributions\n"
+"export those variables via /etc/profile. Some ncurses programs can\n"
+"become confused when those variables (LINES & COLUMNS) don't reflect\n"
+"the true screen size.\n"
+"\n"
+"Optional personality available\n"
+"------------------------------\n"
+"If you prefer to have all of the busybox options listed in a single\n"
+"menu, rather than the default multimenu hierarchy, run the menuconfig\n"
+"with MENUCONFIG_MODE environment variable set to single_menu. Example:\n"
+"\n"
+"make MENUCONFIG_MODE=single_menu menuconfig\n"
+"\n"
+"<Enter> will then unroll the appropriate category, or enfold it if it\n"
+"is already unrolled.\n"
+"\n"
+"Note that this mode can eventually be a little more CPU expensive\n"
+"(especially with a larger number of unrolled categories) than the\n"
+"default mode.\n"),
+menu_instructions[] = N_(
+ "Arrow keys navigate the menu. "
+ "<Enter> selects submenus --->. "
+ "Highlighted letters are hotkeys. "
+ "Pressing <Y> includes, <N> excludes, <M> modularizes features. "
+ "Press <Esc><Esc> to exit, <?> for Help, </> for Search. "
+ "Legend: [*] built-in [ ] excluded <M> module < > module capable"),
+radiolist_instructions[] = N_(
+ "Use the arrow keys to navigate this window or "
+ "press the hotkey of the item you wish to select "
+ "followed by the <SPACE BAR>. "
+ "Press <?> for additional information about this option."),
+inputbox_instructions_int[] = N_(
+ "Please enter a decimal value. "
+ "Fractions will not be accepted. "
+ "Use the <TAB> key to move from the input field to the buttons below it."),
+inputbox_instructions_hex[] = N_(
+ "Please enter a hexadecimal value. "
+ "Use the <TAB> key to move from the input field to the buttons below it."),
+inputbox_instructions_string[] = N_(
+ "Please enter a string value. "
+ "Use the <TAB> key to move from the input field to the buttons below it."),
+setmod_text[] = N_(
+ "This feature depends on another which has been configured as a module.\n"
+ "As a result, this feature will be built as a module."),
+nohelp_text[] = N_(
+ "There is no help available for this option.\n"),
+load_config_text[] = N_(
+ "Enter the name of the configuration file you wish to load. "
+ "Accept the name shown to restore the configuration you "
+ "last retrieved. Leave blank to abort."),
+load_config_help[] = N_(
+ "\n"
+ "For various reasons, one may wish to keep several different busybox\n"
+ "configurations available on a single machine.\n"
+ "\n"
+ "If you have saved a previous configuration in a file other than \n"
+ "busybox's default, entering the name of the file here will allow you\n"
+ "to modify that configuration.\n"
+ "\n"
+ "If you are uncertain, then you have probably never used alternate\n"
+ "configuration files. You should therefor leave this blank to abort.\n"),
+save_config_text[] = N_(
+ "Enter a filename to which this configuration should be saved "
+ "as an alternate. Leave blank to abort."),
+save_config_help[] = N_(
+ "\n"
+ "For various reasons, one may wish to keep different busybox\n"
+ "configurations available on a single machine.\n"
+ "\n"
+ "Entering a file name here will allow you to later retrieve, modify\n"
+ "and use the current configuration as an alternate to whatever\n"
+ "configuration options you have selected at that time.\n"
+ "\n"
+ "If you are uncertain what all this means then you should probably\n"
+ "leave this blank.\n"),
+search_help[] = N_(
+ "\n"
+ "Search for CONFIG_ symbols and display their relations.\n"
+ "Regular expressions are allowed.\n"
+ "Example: search for \"^FOO\"\n"
+ "Result:\n"
+ "-----------------------------------------------------------------\n"
+ "Symbol: FOO [=m]\n"
+ "Prompt: Foo bus is used to drive the bar HW\n"
+ "Defined at drivers/pci/Kconfig:47\n"
+ "Depends on: X86_LOCAL_APIC && X86_IO_APIC || IA64\n"
+ "Location:\n"
+ " -> Bus options (PCI, PCMCIA, EISA, MCA, ISA)\n"
+ " -> PCI support (PCI [=y])\n"
+ " -> PCI access mode (<choice> [=y])\n"
+ "Selects: LIBCRC32\n"
+ "Selected by: BAR\n"
+ "-----------------------------------------------------------------\n"
+ "o The line 'Prompt:' shows the text used in the menu structure for\n"
+ " this CONFIG_ symbol\n"
+ "o The 'Defined at' line tell at what file / line number the symbol\n"
+ " is defined\n"
+ "o The 'Depends on:' line tell what symbols needs to be defined for\n"
+ " this symbol to be visible in the menu (selectable)\n"
+ "o The 'Location:' lines tell where in the menu structure this symbol\n"
+ " is located\n"
+ " A location followed by a [=y] indicate that this is a selectable\n"
+ " menu item - and current value is displayed inside brackets.\n"
+ "o The 'Selects:' line tell what symbol will be automatically\n"
+ " selected if this symbol is selected (y or m)\n"
+ "o The 'Selected by' line tell what symbol has selected this symbol\n"
+ "\n"
+ "Only relevant lines are shown.\n"
+ "\n\n"
+ "Search examples:\n"
+ "Examples: USB => find all CONFIG_ symbols containing USB\n"
+ " ^USB => find all CONFIG_ symbols starting with USB\n"
+ " USB$ => find all CONFIG_ symbols ending with USB\n"
+ "\n");
+
+static char buf[4096], *bufptr = buf;
+static char input_buf[4096];
+static char filename[PATH_MAX+1] = ".config";
+static char *args[1024], **argptr = args;
+static int indent;
+static struct termios ios_org;
+static int rows = 0, cols = 0;
+static struct menu *current_menu;
+static int child_count;
+static int do_resize;
+static int single_menu_mode;
+
+static void conf(struct menu *menu);
+static void conf_choice(struct menu *menu);
+static void conf_string(struct menu *menu);
+static void conf_load(void);
+static void conf_save(void);
+static void show_textbox(const char *title, const char *text, int r, int c);
+static void show_helptext(const char *title, const char *text);
+static void show_help(struct menu *menu);
+static void show_file(const char *filename, const char *title, int r, int c);
+
+static void cprint_init(void);
+static int cprint1(const char *fmt, ...);
+static void cprint_done(void);
+static int cprint(const char *fmt, ...);
+
+static void init_wsize(void)
+{
+ struct winsize ws;
+ char *env;
+
+ if (!ioctl(STDIN_FILENO, TIOCGWINSZ, &ws)) {
+ rows = ws.ws_row;
+ cols = ws.ws_col;
+ }
+
+ if (!rows) {
+ env = getenv("LINES");
+ if (env)
+ rows = atoi(env);
+ if (!rows)
+ rows = 24;
+ }
+ if (!cols) {
+ env = getenv("COLUMNS");
+ if (env)
+ cols = atoi(env);
+ if (!cols)
+ cols = 80;
+ }
+
+ if (rows < 19 || cols < 80) {
+ fprintf(stderr, N_("Your display is too small to run Menuconfig!\n"));
+ fprintf(stderr, N_("It must be at least 19 lines by 80 columns.\n"));
+ exit(1);
+ }
+
+ rows -= 4;
+ cols -= 5;
+}
+
+static void cprint_init(void)
+{
+ bufptr = buf;
+ argptr = args;
+ memset(args, 0, sizeof(args));
+ indent = 0;
+ child_count = 0;
+ cprint("./scripts/kconfig/lxdialog/lxdialog");
+ cprint("--backtitle");
+ cprint(menu_backtitle);
+}
+
+static int cprint1(const char *fmt, ...)
+{
+ va_list ap;
+ int res;
+
+ if (!*argptr)
+ *argptr = bufptr;
+ va_start(ap, fmt);
+ res = vsprintf(bufptr, fmt, ap);
+ va_end(ap);
+ bufptr += res;
+
+ return res;
+}
+
+static void cprint_done(void)
+{
+ *bufptr++ = 0;
+ argptr++;
+}
+
+static int cprint(const char *fmt, ...)
+{
+ va_list ap;
+ int res;
+
+ *argptr++ = bufptr;
+ va_start(ap, fmt);
+ res = vsprintf(bufptr, fmt, ap);
+ va_end(ap);
+ bufptr += res;
+ *bufptr++ = 0;
+
+ return res;
+}
+
+static void get_prompt_str(struct gstr *r, struct property *prop)
+{
+ int i, j;
+ struct menu *submenu[8], *menu;
+
+ str_printf(r, "Prompt: %s\n", prop->text);
+ str_printf(r, " Defined at %s:%d\n", prop->menu->file->name,
+ prop->menu->lineno);
+ if (!expr_is_yes(prop->visible.expr)) {
+ str_append(r, " Depends on: ");
+ expr_gstr_print(prop->visible.expr, r);
+ str_append(r, "\n");
+ }
+ menu = prop->menu->parent;
+ for (i = 0; menu != &rootmenu && i < 8; menu = menu->parent)
+ submenu[i++] = menu;
+ if (i > 0) {
+ str_printf(r, " Location:\n");
+ for (j = 4; --i >= 0; j += 2) {
+ menu = submenu[i];
+ str_printf(r, "%*c-> %s", j, ' ', menu_get_prompt(menu));
+ if (menu->sym) {
+ str_printf(r, " (%s [=%s])", menu->sym->name ?
+ menu->sym->name : "<choice>",
+ sym_get_string_value(menu->sym));
+ }
+ str_append(r, "\n");
+ }
+ }
+}
+
+static void get_symbol_str(struct gstr *r, struct symbol *sym)
+{
+ bool hit;
+ struct property *prop;
+
+ str_printf(r, "Symbol: %s [=%s]\n", sym->name,
+ sym_get_string_value(sym));
+ for_all_prompts(sym, prop)
+ get_prompt_str(r, prop);
+ hit = false;
+ for_all_properties(sym, prop, P_SELECT) {
+ if (!hit) {
+ str_append(r, " Selects: ");
+ hit = true;
+ } else
+ str_printf(r, " && ");
+ expr_gstr_print(prop->expr, r);
+ }
+ if (hit)
+ str_append(r, "\n");
+ if (sym->rev_dep.expr) {
+ str_append(r, " Selected by: ");
+ expr_gstr_print(sym->rev_dep.expr, r);
+ str_append(r, "\n");
+ }
+ str_append(r, "\n\n");
+}
+
+static struct gstr get_relations_str(struct symbol **sym_arr)
+{
+ struct symbol *sym;
+ struct gstr res = str_new();
+ int i;
+
+ for (i = 0; sym_arr && (sym = sym_arr[i]); i++)
+ get_symbol_str(&res, sym);
+ if (!i)
+ str_append(&res, "No matches found.\n");
+ return res;
+}
+
+pid_t pid;
+
+static void winch_handler(int sig)
+{
+ if (!do_resize) {
+ kill(pid, SIGINT);
+ do_resize = 1;
+ }
+}
+
+static int exec_conf(void)
+{
+ int pipefd[2], stat, size;
+ struct sigaction sa;
+ sigset_t sset, osset;
+
+ sigemptyset(&sset);
+ sigaddset(&sset, SIGINT);
+ sigprocmask(SIG_BLOCK, &sset, &osset);
+
+ signal(SIGINT, SIG_DFL);
+
+ sa.sa_handler = winch_handler;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_RESTART;
+ sigaction(SIGWINCH, &sa, NULL);
+
+ *argptr++ = NULL;
+
+ pipe(pipefd);
+ pid = fork();
+ if (pid == 0) {
+ sigprocmask(SIG_SETMASK, &osset, NULL);
+ dup2(pipefd[1], 2);
+ close(pipefd[0]);
+ close(pipefd[1]);
+ execv(args[0], args);
+ _exit(EXIT_FAILURE);
+ }
+
+ close(pipefd[1]);
+ bufptr = input_buf;
+ while (1) {
+ size = input_buf + sizeof(input_buf) - bufptr;
+ size = read(pipefd[0], bufptr, size);
+ if (size <= 0) {
+ if (size < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ perror("read");
+ }
+ break;
+ }
+ bufptr += size;
+ }
+ *bufptr++ = 0;
+ close(pipefd[0]);
+ waitpid(pid, &stat, 0);
+
+ if (do_resize) {
+ init_wsize();
+ do_resize = 0;
+ sigprocmask(SIG_SETMASK, &osset, NULL);
+ return -1;
+ }
+ if (WIFSIGNALED(stat)) {
+ printf("\finterrupted(%d)\n", WTERMSIG(stat));
+ exit(1);
+ }
+#if 0
+ printf("\fexit state: %d\nexit data: '%s'\n", WEXITSTATUS(stat), input_buf);
+ sleep(1);
+#endif
+ sigpending(&sset);
+ if (sigismember(&sset, SIGINT)) {
+ printf("\finterrupted\n");
+ exit(1);
+ }
+ sigprocmask(SIG_SETMASK, &osset, NULL);
+
+ return WEXITSTATUS(stat);
+}
+
+static void search_conf(void)
+{
+ struct symbol **sym_arr;
+ int stat;
+ struct gstr res;
+
+again:
+ cprint_init();
+ cprint("--title");
+ cprint(_("Search Configuration Parameter"));
+ cprint("--inputbox");
+ cprint(_("Enter CONFIG_ (sub)string to search for (omit CONFIG_)"));
+ cprint("10");
+ cprint("75");
+ cprint("");
+ stat = exec_conf();
+ if (stat < 0)
+ goto again;
+ switch (stat) {
+ case 0:
+ break;
+ case 1:
+ show_helptext(_("Search Configuration"), search_help);
+ goto again;
+ default:
+ return;
+ }
+
+ sym_arr = sym_re_search(input_buf);
+ res = get_relations_str(sym_arr);
+ free(sym_arr);
+ show_textbox(_("Search Results"), str_get(&res), 0, 0);
+ str_free(&res);
+}
+
+static void build_conf(struct menu *menu)
+{
+ struct symbol *sym;
+ struct property *prop;
+ struct menu *child;
+ int type, tmp, doint = 2;
+ tristate val;
+ char ch;
+
+ if (!menu_is_visible(menu))
+ return;
+
+ sym = menu->sym;
+ prop = menu->prompt;
+ if (!sym) {
+ if (prop && menu != current_menu) {
+ const char *prompt = menu_get_prompt(menu);
+ switch (prop->type) {
+ case P_MENU:
+ child_count++;
+ cprint("m%p", menu);
+
+ if (single_menu_mode) {
+ cprint1("%s%*c%s",
+ menu->data ? "-->" : "++>",
+ indent + 1, ' ', prompt);
+ } else
+ cprint1(" %*c%s --->", indent + 1, ' ', prompt);
+
+ cprint_done();
+ if (single_menu_mode && menu->data)
+ goto conf_childs;
+ return;
+ default:
+ if (prompt) {
+ child_count++;
+ cprint(":%p", menu);
+ cprint("---%*c%s", indent + 1, ' ', prompt);
+ }
+ }
+ } else
+ doint = 0;
+ goto conf_childs;
+ }
+
+ type = sym_get_type(sym);
+ if (sym_is_choice(sym)) {
+ struct symbol *def_sym = sym_get_choice_value(sym);
+ struct menu *def_menu = NULL;
+
+ child_count++;
+ for (child = menu->list; child; child = child->next) {
+ if (menu_is_visible(child) && child->sym == def_sym)
+ def_menu = child;
+ }
+
+ val = sym_get_tristate_value(sym);
+ if (sym_is_changable(sym)) {
+ cprint("t%p", menu);
+ switch (type) {
+ case S_BOOLEAN:
+ cprint1("[%c]", val == no ? ' ' : '*');
+ break;
+ case S_TRISTATE:
+ switch (val) {
+ case yes: ch = '*'; break;
+ case mod: ch = 'M'; break;
+ default: ch = ' '; break;
+ }
+ cprint1("<%c>", ch);
+ break;
+ }
+ } else {
+ cprint("%c%p", def_menu ? 't' : ':', menu);
+ cprint1(" ");
+ }
+
+ cprint1("%*c%s", indent + 1, ' ', menu_get_prompt(menu));
+ if (val == yes) {
+ if (def_menu) {
+ cprint1(" (%s)", menu_get_prompt(def_menu));
+ cprint1(" --->");
+ cprint_done();
+ if (def_menu->list) {
+ indent += 2;
+ build_conf(def_menu);
+ indent -= 2;
+ }
+ } else
+ cprint_done();
+ return;
+ }
+ cprint_done();
+ } else {
+ if (menu == current_menu) {
+ cprint(":%p", menu);
+ cprint("---%*c%s", indent + 1, ' ', menu_get_prompt(menu));
+ goto conf_childs;
+ }
+ child_count++;
+ val = sym_get_tristate_value(sym);
+ if (sym_is_choice_value(sym) && val == yes) {
+ cprint(":%p", menu);
+ cprint1(" ");
+ } else {
+ switch (type) {
+ case S_BOOLEAN:
+ cprint("t%p", menu);
+ if (sym_is_changable(sym))
+ cprint1("[%c]", val == no ? ' ' : '*');
+ else
+ cprint1("---");
+ break;
+ case S_TRISTATE:
+ cprint("t%p", menu);
+ switch (val) {
+ case yes: ch = '*'; break;
+ case mod: ch = 'M'; break;
+ default: ch = ' '; break;
+ }
+ if (sym_is_changable(sym))
+ cprint1("<%c>", ch);
+ else
+ cprint1("---");
+ break;
+ default:
+ cprint("s%p", menu);
+ tmp = cprint1("(%s)", sym_get_string_value(sym));
+ tmp = indent - tmp + 4;
+ if (tmp < 0)
+ tmp = 0;
+ cprint1("%*c%s%s", tmp, ' ', menu_get_prompt(menu),
+ (sym_has_value(sym) || !sym_is_changable(sym)) ?
+ "" : " (NEW)");
+ cprint_done();
+ goto conf_childs;
+ }
+ }
+ cprint1("%*c%s%s", indent + 1, ' ', menu_get_prompt(menu),
+ (sym_has_value(sym) || !sym_is_changable(sym)) ?
+ "" : " (NEW)");
+ if (menu->prompt->type == P_MENU) {
+ cprint1(" --->");
+ cprint_done();
+ return;
+ }
+ cprint_done();
+ }
+
+conf_childs:
+ indent += doint;
+ for (child = menu->list; child; child = child->next)
+ build_conf(child);
+ indent -= doint;
+}
+
+static void conf(struct menu *menu)
+{
+ struct menu *submenu;
+ const char *prompt = menu_get_prompt(menu);
+ struct symbol *sym;
+ char active_entry[40];
+ int stat, type, i;
+
+ unlink("lxdialog.scrltmp");
+ active_entry[0] = 0;
+ while (1) {
+ cprint_init();
+ cprint("--title");
+ cprint("%s", prompt ? prompt : _("Main Menu"));
+ cprint("--menu");
+ cprint(_(menu_instructions));
+ cprint("%d", rows);
+ cprint("%d", cols);
+ cprint("%d", rows - 10);
+ cprint("%s", active_entry);
+ current_menu = menu;
+ build_conf(menu);
+ if (!child_count)
+ break;
+ if (menu == &rootmenu) {
+ cprint(":");
+ cprint("--- ");
+ cprint("L");
+ cprint(_(" Load an Alternate Configuration File"));
+ cprint("S");
+ cprint(_(" Save Configuration to an Alternate File"));
+ }
+ stat = exec_conf();
+ if (stat < 0)
+ continue;
+
+ if (stat == 1 || stat == 255)
+ break;
+
+ type = input_buf[0];
+ if (!type)
+ continue;
+
+ for (i = 0; input_buf[i] && !isspace(input_buf[i]); i++)
+ ;
+ if (i >= sizeof(active_entry))
+ i = sizeof(active_entry) - 1;
+ input_buf[i] = 0;
+ strcpy(active_entry, input_buf);
+
+ sym = NULL;
+ submenu = NULL;
+ if (sscanf(input_buf + 1, "%p", &submenu) == 1)
+ sym = submenu->sym;
+
+ switch (stat) {
+ case 0:
+ switch (type) {
+ case 'm':
+ if (single_menu_mode)
+ submenu->data = (void *) (long) !submenu->data;
+ else
+ conf(submenu);
+ break;
+ case 't':
+ if (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes)
+ conf_choice(submenu);
+ else if (submenu->prompt->type == P_MENU)
+ conf(submenu);
+ break;
+ case 's':
+ conf_string(submenu);
+ break;
+ case 'L':
+ conf_load();
+ break;
+ case 'S':
+ conf_save();
+ break;
+ }
+ break;
+ case 2:
+ if (sym)
+ show_help(submenu);
+ else
+ show_helptext("README", _(mconf_readme));
+ break;
+ case 3:
+ if (type == 't') {
+ if (sym_set_tristate_value(sym, yes))
+ break;
+ if (sym_set_tristate_value(sym, mod))
+ show_textbox(NULL, setmod_text, 6, 74);
+ }
+ break;
+ case 4:
+ if (type == 't')
+ sym_set_tristate_value(sym, no);
+ break;
+ case 5:
+ if (type == 't')
+ sym_set_tristate_value(sym, mod);
+ break;
+ case 6:
+ if (type == 't')
+ sym_toggle_tristate_value(sym);
+ else if (type == 'm')
+ conf(submenu);
+ break;
+ case 7:
+ search_conf();
+ break;
+ }
+ }
+}
+
+static void show_textbox(const char *title, const char *text, int r, int c)
+{
+ int fd;
+
+ fd = creat(".help.tmp", 0777);
+ write(fd, text, strlen(text));
+ close(fd);
+ show_file(".help.tmp", title, r, c);
+ unlink(".help.tmp");
+}
+
+static void show_helptext(const char *title, const char *text)
+{
+ show_textbox(title, text, 0, 0);
+}
+
+static void show_help(struct menu *menu)
+{
+ struct gstr help = str_new();
+ struct symbol *sym = menu->sym;
+
+ if (sym->help)
+ {
+ if (sym->name) {
+ str_printf(&help, "CONFIG_%s:\n\n", sym->name);
+ str_append(&help, _(sym->help));
+ str_append(&help, "\n");
+ }
+ } else {
+ str_append(&help, nohelp_text);
+ }
+ get_symbol_str(&help, sym);
+ show_helptext(menu_get_prompt(menu), str_get(&help));
+ str_free(&help);
+}
+
+static void show_file(const char *filename, const char *title, int r, int c)
+{
+ do {
+ cprint_init();
+ if (title) {
+ cprint("--title");
+ cprint("%s", title);
+ }
+ cprint("--textbox");
+ cprint("%s", filename);
+ cprint("%d", r ? r : rows);
+ cprint("%d", c ? c : cols);
+ } while (exec_conf() < 0);
+}
+
+static void conf_choice(struct menu *menu)
+{
+ const char *prompt = menu_get_prompt(menu);
+ struct menu *child;
+ struct symbol *active;
+ int stat;
+
+ active = sym_get_choice_value(menu->sym);
+ while (1) {
+ cprint_init();
+ cprint("--title");
+ cprint("%s", prompt ? prompt : _("Main Menu"));
+ cprint("--radiolist");
+ cprint(_(radiolist_instructions));
+ cprint("15");
+ cprint("70");
+ cprint("6");
+
+ current_menu = menu;
+ for (child = menu->list; child; child = child->next) {
+ if (!menu_is_visible(child))
+ continue;
+ cprint("%p", child);
+ cprint("%s", menu_get_prompt(child));
+ if (child->sym == sym_get_choice_value(menu->sym))
+ cprint("ON");
+ else if (child->sym == active)
+ cprint("SELECTED");
+ else
+ cprint("OFF");
+ }
+
+ stat = exec_conf();
+ switch (stat) {
+ case 0:
+ if (sscanf(input_buf, "%p", &child) != 1)
+ break;
+ sym_set_tristate_value(child->sym, yes);
+ return;
+ case 1:
+ if (sscanf(input_buf, "%p", &child) == 1) {
+ show_help(child);
+ active = child->sym;
+ } else
+ show_help(menu);
+ break;
+ case 255:
+ return;
+ }
+ }
+}
+
+static void conf_string(struct menu *menu)
+{
+ const char *prompt = menu_get_prompt(menu);
+ int stat;
+
+ while (1) {
+ cprint_init();
+ cprint("--title");
+ cprint("%s", prompt ? prompt : _("Main Menu"));
+ cprint("--inputbox");
+ switch (sym_get_type(menu->sym)) {
+ case S_INT:
+ cprint(_(inputbox_instructions_int));
+ break;
+ case S_HEX:
+ cprint(_(inputbox_instructions_hex));
+ break;
+ case S_STRING:
+ cprint(_(inputbox_instructions_string));
+ break;
+ default:
+ /* panic? */;
+ }
+ cprint("10");
+ cprint("75");
+ cprint("%s", sym_get_string_value(menu->sym));
+ stat = exec_conf();
+ switch (stat) {
+ case 0:
+ if (sym_set_string_value(menu->sym, input_buf))
+ return;
+ show_textbox(NULL, _("You have made an invalid entry."), 5, 43);
+ break;
+ case 1:
+ show_help(menu);
+ break;
+ case 255:
+ return;
+ }
+ }
+}
+
+static void conf_load(void)
+{
+ int stat;
+
+ while (1) {
+ cprint_init();
+ cprint("--inputbox");
+ cprint(load_config_text);
+ cprint("11");
+ cprint("55");
+ cprint("%s", filename);
+ stat = exec_conf();
+ switch(stat) {
+ case 0:
+ if (!input_buf[0])
+ return;
+ if (!conf_read(input_buf))
+ return;
+ show_textbox(NULL, _("File does not exist!"), 5, 38);
+ break;
+ case 1:
+ show_helptext(_("Load Alternate Configuration"), load_config_help);
+ break;
+ case 255:
+ return;
+ }
+ }
+}
+
+static void conf_save(void)
+{
+ int stat;
+
+ while (1) {
+ cprint_init();
+ cprint("--inputbox");
+ cprint(save_config_text);
+ cprint("11");
+ cprint("55");
+ cprint("%s", filename);
+ stat = exec_conf();
+ switch(stat) {
+ case 0:
+ if (!input_buf[0])
+ return;
+ if (!conf_write(input_buf))
+ return;
+ show_textbox(NULL, _("Can't create file! Probably a nonexistent directory."), 5, 60);
+ break;
+ case 1:
+ show_helptext(_("Save Alternate Configuration"), save_config_help);
+ break;
+ case 255:
+ return;
+ }
+ }
+}
+
+static void conf_cleanup(void)
+{
+ tcsetattr(1, TCSAFLUSH, &ios_org);
+ unlink(".help.tmp");
+ unlink("lxdialog.scrltmp");
+}
+
+int main(int ac, char **av)
+{
+ struct symbol *sym;
+ char *mode;
+ int stat;
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+
+ conf_parse(av[1]);
+ conf_read(NULL);
+
+ sym = sym_lookup("KERNELVERSION", 0);
+ sym_calc_value(sym);
+ sprintf(menu_backtitle, _("BusyBox %s Configuration"),
+ sym_get_string_value(sym));
+
+ mode = getenv("MENUCONFIG_MODE");
+ if (mode) {
+ if (!strcasecmp(mode, "single_menu"))
+ single_menu_mode = 1;
+ }
+
+ tcgetattr(1, &ios_org);
+ atexit(conf_cleanup);
+ init_wsize();
+ conf(&rootmenu);
+
+ do {
+ cprint_init();
+ cprint("--yesno");
+ cprint(_("Do you wish to save your new busybox configuration?"));
+ cprint("5");
+ cprint("60");
+ stat = exec_conf();
+ } while (stat < 0);
+
+ if (stat == 0) {
+ if (conf_write(NULL)) {
+ fprintf(stderr, _("\n\n"
+ "Error during writing of the busybox configuration.\n"
+ "Your busybox configuration changes were NOT saved."
+ "\n\n"));
+ return 1;
+ }
+ printf(_("\n\n"
+ "*** End of busybox configuration.\n"
+ "*** Execute 'make' to build busybox or try 'make help'."
+ "\n\n"));
+ } else {
+ fprintf(stderr, _("\n\n"
+ "Your busybox configuration changes were NOT saved."
+ "\n\n"));
+ }
+
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/scripts/kconfig/menu.c b/i/pc104/initrd/conf/busybox/scripts/kconfig/menu.c
new file mode 100644
index 0000000..0fce20c
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/scripts/kconfig/menu.c
@@ -0,0 +1,397 @@
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#define LKC_DIRECT_LINK
+#include "lkc.h"
+
+struct menu rootmenu;
+static struct menu **last_entry_ptr;
+
+struct file *file_list;
+struct file *current_file;
+
+static void menu_warn(struct menu *menu, const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ fprintf(stderr, "%s:%d:warning: ", menu->file->name, menu->lineno);
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ va_end(ap);
+}
+
+static void prop_warn(struct property *prop, const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ fprintf(stderr, "%s:%d:warning: ", prop->file->name, prop->lineno);
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ va_end(ap);
+}
+
+void menu_init(void)
+{
+ current_entry = current_menu = &rootmenu;
+ last_entry_ptr = &rootmenu.list;
+}
+
+void menu_add_entry(struct symbol *sym)
+{
+ struct menu *menu;
+
+ menu = malloc(sizeof(*menu));
+ memset(menu, 0, sizeof(*menu));
+ menu->sym = sym;
+ menu->parent = current_menu;
+ menu->file = current_file;
+ menu->lineno = zconf_lineno();
+
+ *last_entry_ptr = menu;
+ last_entry_ptr = &menu->next;
+ current_entry = menu;
+}
+
+void menu_end_entry(void)
+{
+}
+
+struct menu *menu_add_menu(void)
+{
+ menu_end_entry();
+ last_entry_ptr = &current_entry->list;
+ return current_menu = current_entry;
+}
+
+void menu_end_menu(void)
+{
+ last_entry_ptr = &current_menu->next;
+ current_menu = current_menu->parent;
+}
+
+struct expr *menu_check_dep(struct expr *e)
+{
+ if (!e)
+ return e;
+
+ switch (e->type) {
+ case E_NOT:
+ e->left.expr = menu_check_dep(e->left.expr);
+ break;
+ case E_OR:
+ case E_AND:
+ e->left.expr = menu_check_dep(e->left.expr);
+ e->right.expr = menu_check_dep(e->right.expr);
+ break;
+ case E_SYMBOL:
+ /* change 'm' into 'm' && MODULES */
+ if (e->left.sym == &symbol_mod)
+ return expr_alloc_and(e, expr_alloc_symbol(modules_sym));
+ break;
+ default:
+ break;
+ }
+ return e;
+}
+
+void menu_add_dep(struct expr *dep)
+{
+ current_entry->dep = expr_alloc_and(current_entry->dep, menu_check_dep(dep));
+}
+
+void menu_set_type(int type)
+{
+ struct symbol *sym = current_entry->sym;
+
+ if (sym->type == type)
+ return;
+ if (sym->type == S_UNKNOWN) {
+ sym->type = type;
+ return;
+ }
+ menu_warn(current_entry, "type of '%s' redefined from '%s' to '%s'\n",
+ sym->name ? sym->name : "<choice>",
+ sym_type_name(sym->type), sym_type_name(type));
+}
+
+struct property *menu_add_prop(enum prop_type type, char *prompt, struct expr *expr, struct expr *dep)
+{
+ struct property *prop = prop_alloc(type, current_entry->sym);
+
+ prop->menu = current_entry;
+ prop->text = prompt;
+ prop->expr = expr;
+ prop->visible.expr = menu_check_dep(dep);
+
+ if (prompt) {
+ if (current_entry->prompt)
+ menu_warn(current_entry, "prompt redefined\n");
+ current_entry->prompt = prop;
+ }
+
+ return prop;
+}
+
+struct property *menu_add_prompt(enum prop_type type, char *prompt, struct expr *dep)
+{
+ return menu_add_prop(type, prompt, NULL, dep);
+}
+
+void menu_add_expr(enum prop_type type, struct expr *expr, struct expr *dep)
+{
+ menu_add_prop(type, NULL, expr, dep);
+}
+
+void menu_add_symbol(enum prop_type type, struct symbol *sym, struct expr *dep)
+{
+ menu_add_prop(type, NULL, expr_alloc_symbol(sym), dep);
+}
+
+static int menu_range_valid_sym(struct symbol *sym, struct symbol *sym2)
+{
+ return sym2->type == S_INT || sym2->type == S_HEX ||
+ (sym2->type == S_UNKNOWN && sym_string_valid(sym, sym2->name));
+}
+
+void sym_check_prop(struct symbol *sym)
+{
+ struct property *prop;
+ struct symbol *sym2;
+ for (prop = sym->prop; prop; prop = prop->next) {
+ switch (prop->type) {
+ case P_DEFAULT:
+ if ((sym->type == S_STRING || sym->type == S_INT || sym->type == S_HEX) &&
+ prop->expr->type != E_SYMBOL)
+ prop_warn(prop,
+ "default for config symbol '%'"
+ " must be a single symbol", sym->name);
+ break;
+ case P_SELECT:
+ sym2 = prop_get_symbol(prop);
+ if (sym->type != S_BOOLEAN && sym->type != S_TRISTATE)
+ prop_warn(prop,
+ "config symbol '%s' uses select, but is "
+ "not boolean or tristate", sym->name);
+ else if (sym2->type == S_UNKNOWN)
+ prop_warn(prop,
+ "'select' used by config symbol '%s' "
+ "refer to undefined symbol '%s'",
+ sym->name, sym2->name);
+ else if (sym2->type != S_BOOLEAN && sym2->type != S_TRISTATE)
+ prop_warn(prop,
+ "'%s' has wrong type. 'select' only "
+ "accept arguments of boolean and "
+ "tristate type", sym2->name);
+ break;
+ case P_RANGE:
+ if (sym->type != S_INT && sym->type != S_HEX)
+ prop_warn(prop, "range is only allowed "
+ "for int or hex symbols");
+ if (!menu_range_valid_sym(sym, prop->expr->left.sym) ||
+ !menu_range_valid_sym(sym, prop->expr->right.sym))
+ prop_warn(prop, "range is invalid");
+ break;
+ default:
+ ;
+ }
+ }
+}
+
+void menu_finalize(struct menu *parent)
+{
+ struct menu *menu, *last_menu;
+ struct symbol *sym;
+ struct property *prop;
+ struct expr *parentdep, *basedep, *dep, *dep2, **ep;
+
+ sym = parent->sym;
+ if (parent->list) {
+ if (sym && sym_is_choice(sym)) {
+ /* find the first choice value and find out choice type */
+ for (menu = parent->list; menu; menu = menu->next) {
+ if (menu->sym) {
+ current_entry = parent;
+ menu_set_type(menu->sym->type);
+ current_entry = menu;
+ menu_set_type(sym->type);
+ break;
+ }
+ }
+ parentdep = expr_alloc_symbol(sym);
+ } else if (parent->prompt)
+ parentdep = parent->prompt->visible.expr;
+ else
+ parentdep = parent->dep;
+
+ for (menu = parent->list; menu; menu = menu->next) {
+ basedep = expr_transform(menu->dep);
+ basedep = expr_alloc_and(expr_copy(parentdep), basedep);
+ basedep = expr_eliminate_dups(basedep);
+ menu->dep = basedep;
+ if (menu->sym)
+ prop = menu->sym->prop;
+ else
+ prop = menu->prompt;
+ for (; prop; prop = prop->next) {
+ if (prop->menu != menu)
+ continue;
+ dep = expr_transform(prop->visible.expr);
+ dep = expr_alloc_and(expr_copy(basedep), dep);
+ dep = expr_eliminate_dups(dep);
+ if (menu->sym && menu->sym->type != S_TRISTATE)
+ dep = expr_trans_bool(dep);
+ prop->visible.expr = dep;
+ if (prop->type == P_SELECT) {
+ struct symbol *es = prop_get_symbol(prop);
+ es->rev_dep.expr = expr_alloc_or(es->rev_dep.expr,
+ expr_alloc_and(expr_alloc_symbol(menu->sym), expr_copy(dep)));
+ }
+ }
+ }
+ for (menu = parent->list; menu; menu = menu->next)
+ menu_finalize(menu);
+ } else if (sym) {
+ basedep = parent->prompt ? parent->prompt->visible.expr : NULL;
+ basedep = expr_trans_compare(basedep, E_UNEQUAL, &symbol_no);
+ basedep = expr_eliminate_dups(expr_transform(basedep));
+ last_menu = NULL;
+ for (menu = parent->next; menu; menu = menu->next) {
+ dep = menu->prompt ? menu->prompt->visible.expr : menu->dep;
+ if (!expr_contains_symbol(dep, sym))
+ break;
+ if (expr_depends_symbol(dep, sym))
+ goto next;
+ dep = expr_trans_compare(dep, E_UNEQUAL, &symbol_no);
+ dep = expr_eliminate_dups(expr_transform(dep));
+ dep2 = expr_copy(basedep);
+ expr_eliminate_eq(&dep, &dep2);
+ expr_free(dep);
+ if (!expr_is_yes(dep2)) {
+ expr_free(dep2);
+ break;
+ }
+ expr_free(dep2);
+ next:
+ menu_finalize(menu);
+ menu->parent = parent;
+ last_menu = menu;
+ }
+ if (last_menu) {
+ parent->list = parent->next;
+ parent->next = last_menu->next;
+ last_menu->next = NULL;
+ }
+ }
+ for (menu = parent->list; menu; menu = menu->next) {
+ if (sym && sym_is_choice(sym) && menu->sym) {
+ menu->sym->flags |= SYMBOL_CHOICEVAL;
+ if (!menu->prompt)
+ menu_warn(menu, "choice value must have a prompt");
+ for (prop = menu->sym->prop; prop; prop = prop->next) {
+ if (prop->type == P_PROMPT && prop->menu != menu) {
+ prop_warn(prop, "choice values "
+ "currently only support a "
+ "single prompt");
+ }
+ if (prop->type == P_DEFAULT)
+ prop_warn(prop, "defaults for choice "
+ "values not supported");
+ }
+ current_entry = menu;
+ menu_set_type(sym->type);
+ menu_add_symbol(P_CHOICE, sym, NULL);
+ prop = sym_get_choice_prop(sym);
+ for (ep = &prop->expr; *ep; ep = &(*ep)->left.expr)
+ ;
+ *ep = expr_alloc_one(E_CHOICE, NULL);
+ (*ep)->right.sym = menu->sym;
+ }
+ if (menu->list && (!menu->prompt || !menu->prompt->text)) {
+ for (last_menu = menu->list; ; last_menu = last_menu->next) {
+ last_menu->parent = parent;
+ if (!last_menu->next)
+ break;
+ }
+ last_menu->next = menu->next;
+ menu->next = menu->list;
+ menu->list = NULL;
+ }
+ }
+
+ if (sym && !(sym->flags & SYMBOL_WARNED)) {
+ if (sym->type == S_UNKNOWN)
+ menu_warn(parent, "config symbol defined "
+ "without type\n");
+
+ if (sym_is_choice(sym) && !parent->prompt)
+ menu_warn(parent, "choice must have a prompt\n");
+
+ /* Check properties connected to this symbol */
+ sym_check_prop(sym);
+ sym->flags |= SYMBOL_WARNED;
+ }
+
+ if (sym && !sym_is_optional(sym) && parent->prompt) {
+ sym->rev_dep.expr = expr_alloc_or(sym->rev_dep.expr,
+ expr_alloc_and(parent->prompt->visible.expr,
+ expr_alloc_symbol(&symbol_mod)));
+ }
+}
+
+bool menu_is_visible(struct menu *menu)
+{
+ struct menu *child;
+ struct symbol *sym;
+ tristate visible;
+
+ if (!menu->prompt)
+ return false;
+ sym = menu->sym;
+ if (sym) {
+ sym_calc_value(sym);
+ visible = menu->prompt->visible.tri;
+ } else
+ visible = menu->prompt->visible.tri = expr_calc_value(menu->prompt->visible.expr);
+
+ if (visible != no)
+ return true;
+ if (!sym || sym_get_tristate_value(menu->sym) == no)
+ return false;
+
+ for (child = menu->list; child; child = child->next)
+ if (menu_is_visible(child))
+ return true;
+ return false;
+}
+
+const char *menu_get_prompt(struct menu *menu)
+{
+ if (menu->prompt)
+ return _(menu->prompt->text);
+ else if (menu->sym)
+ return _(menu->sym->name);
+ return NULL;
+}
+
+struct menu *menu_get_root_menu(struct menu *menu)
+{
+ return &rootmenu;
+}
+
+struct menu *menu_get_parent_menu(struct menu *menu)
+{
+ enum prop_type type;
+
+ for (; menu != &rootmenu; menu = menu->parent) {
+ type = menu->prompt ? menu->prompt->type : 0;
+ if (type == P_MENU)
+ break;
+ }
+ return menu;
+}
+
diff --git a/i/pc104/initrd/conf/busybox/scripts/kconfig/qconf.cc b/i/pc104/initrd/conf/busybox/scripts/kconfig/qconf.cc
new file mode 100644
index 0000000..605b025
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/scripts/kconfig/qconf.cc
@@ -0,0 +1,1426 @@
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#include <qapplication.h>
+#include <qmainwindow.h>
+#include <qtoolbar.h>
+#include <qvbox.h>
+#include <qsplitter.h>
+#include <qlistview.h>
+#include <qtextview.h>
+#include <qlineedit.h>
+#include <qmenubar.h>
+#include <qmessagebox.h>
+#include <qaction.h>
+#include <qheader.h>
+#include <qfiledialog.h>
+#include <qregexp.h>
+
+#include <stdlib.h>
+
+#include "lkc.h"
+#include "qconf.h"
+
+#include "qconf.moc"
+#include "images.c"
+
+#ifdef _
+# undef _
+# define _ qgettext
+#endif
+
+static QApplication *configApp;
+
+static inline QString qgettext(const char* str)
+{
+ return QString::fromLocal8Bit(gettext(str));
+}
+
+static inline QString qgettext(const QString& str)
+{
+ return QString::fromLocal8Bit(gettext(str.latin1()));
+}
+
+ConfigSettings::ConfigSettings()
+ : showAll(false), showName(false), showRange(false), showData(false)
+{
+}
+
+#if QT_VERSION >= 300
+/**
+ * Reads the list column settings from the application settings.
+ */
+void ConfigSettings::readListSettings()
+{
+ showAll = readBoolEntry("/kconfig/qconf/showAll", false);
+ showName = readBoolEntry("/kconfig/qconf/showName", false);
+ showRange = readBoolEntry("/kconfig/qconf/showRange", false);
+ showData = readBoolEntry("/kconfig/qconf/showData", false);
+}
+
+/**
+ * Reads a list of integer values from the application settings.
+ */
+QValueList<int> ConfigSettings::readSizes(const QString& key, bool *ok)
+{
+ QValueList<int> result;
+ QStringList entryList = readListEntry(key, ok);
+ if (ok) {
+ QStringList::Iterator it;
+ for (it = entryList.begin(); it != entryList.end(); ++it)
+ result.push_back((*it).toInt());
+ }
+
+ return result;
+}
+
+/**
+ * Writes a list of integer values to the application settings.
+ */
+bool ConfigSettings::writeSizes(const QString& key, const QValueList<int>& value)
+{
+ QStringList stringList;
+ QValueList<int>::ConstIterator it;
+
+ for (it = value.begin(); it != value.end(); ++it)
+ stringList.push_back(QString::number(*it));
+ return writeEntry(key, stringList);
+}
+#endif
+
+
+/*
+ * update all the children of a menu entry
+ * removes/adds the entries from the parent widget as necessary
+ *
+ * parent: either the menu list widget or a menu entry widget
+ * menu: entry to be updated
+ */
+template <class P>
+void ConfigList::updateMenuList(P* parent, struct menu* menu)
+{
+ struct menu* child;
+ ConfigItem* item;
+ ConfigItem* last;
+ bool visible;
+ enum prop_type type;
+
+ if (!menu) {
+ while ((item = parent->firstChild()))
+ delete item;
+ return;
+ }
+
+ last = parent->firstChild();
+ if (last && !last->goParent)
+ last = 0;
+ for (child = menu->list; child; child = child->next) {
+ item = last ? last->nextSibling() : parent->firstChild();
+ type = child->prompt ? child->prompt->type : P_UNKNOWN;
+
+ switch (mode) {
+ case menuMode:
+ if (!(child->flags & MENU_ROOT))
+ goto hide;
+ break;
+ case symbolMode:
+ if (child->flags & MENU_ROOT)
+ goto hide;
+ break;
+ default:
+ break;
+ }
+
+ visible = menu_is_visible(child);
+ if (showAll || visible) {
+ if (!item || item->menu != child)
+ item = new ConfigItem(parent, last, child, visible);
+ else
+ item->testUpdateMenu(visible);
+
+ if (mode == fullMode || mode == menuMode || type != P_MENU)
+ updateMenuList(item, child);
+ else
+ updateMenuList(item, 0);
+ last = item;
+ continue;
+ }
+ hide:
+ if (item && item->menu == child) {
+ last = parent->firstChild();
+ if (last == item)
+ last = 0;
+ else while (last->nextSibling() != item)
+ last = last->nextSibling();
+ delete item;
+ }
+ }
+}
+
+#if QT_VERSION >= 300
+/*
+ * set the new data
+ * TODO check the value
+ */
+void ConfigItem::okRename(int col)
+{
+ Parent::okRename(col);
+ sym_set_string_value(menu->sym, text(dataColIdx).latin1());
+}
+#endif
+
+/*
+ * update the displayed of a menu entry
+ */
+void ConfigItem::updateMenu(void)
+{
+ ConfigList* list;
+ struct symbol* sym;
+ struct property *prop;
+ QString prompt;
+ int type;
+ tristate expr;
+
+ list = listView();
+ if (goParent) {
+ setPixmap(promptColIdx, list->menuBackPix);
+ prompt = "..";
+ goto set_prompt;
+ }
+
+ sym = menu->sym;
+ prop = menu->prompt;
+ prompt = QString::fromLocal8Bit(menu_get_prompt(menu));
+
+ if (prop) switch (prop->type) {
+ case P_MENU:
+ if (list->mode == singleMode || list->mode == symbolMode) {
+ /* a menuconfig entry is displayed differently
+ * depending whether it's at the view root or a child.
+ */
+ if (sym && list->rootEntry == menu)
+ break;
+ setPixmap(promptColIdx, list->menuPix);
+ } else {
+ if (sym)
+ break;
+ setPixmap(promptColIdx, 0);
+ }
+ goto set_prompt;
+ case P_COMMENT:
+ setPixmap(promptColIdx, 0);
+ goto set_prompt;
+ default:
+ ;
+ }
+ if (!sym)
+ goto set_prompt;
+
+ setText(nameColIdx, QString::fromLocal8Bit(sym->name));
+
+ type = sym_get_type(sym);
+ switch (type) {
+ case S_BOOLEAN:
+ case S_TRISTATE:
+ char ch;
+
+ if (!sym_is_changable(sym) && !list->showAll) {
+ setPixmap(promptColIdx, 0);
+ setText(noColIdx, QString::null);
+ setText(modColIdx, QString::null);
+ setText(yesColIdx, QString::null);
+ break;
+ }
+ expr = sym_get_tristate_value(sym);
+ switch (expr) {
+ case yes:
+ if (sym_is_choice_value(sym) && type == S_BOOLEAN)
+ setPixmap(promptColIdx, list->choiceYesPix);
+ else
+ setPixmap(promptColIdx, list->symbolYesPix);
+ setText(yesColIdx, "Y");
+ ch = 'Y';
+ break;
+ case mod:
+ setPixmap(promptColIdx, list->symbolModPix);
+ setText(modColIdx, "M");
+ ch = 'M';
+ break;
+ default:
+ if (sym_is_choice_value(sym) && type == S_BOOLEAN)
+ setPixmap(promptColIdx, list->choiceNoPix);
+ else
+ setPixmap(promptColIdx, list->symbolNoPix);
+ setText(noColIdx, "N");
+ ch = 'N';
+ break;
+ }
+ if (expr != no)
+ setText(noColIdx, sym_tristate_within_range(sym, no) ? "_" : 0);
+ if (expr != mod)
+ setText(modColIdx, sym_tristate_within_range(sym, mod) ? "_" : 0);
+ if (expr != yes)
+ setText(yesColIdx, sym_tristate_within_range(sym, yes) ? "_" : 0);
+
+ setText(dataColIdx, QChar(ch));
+ break;
+ case S_INT:
+ case S_HEX:
+ case S_STRING:
+ const char* data;
+
+ data = sym_get_string_value(sym);
+
+#if QT_VERSION >= 300
+ int i = list->mapIdx(dataColIdx);
+ if (i >= 0)
+ setRenameEnabled(i, TRUE);
+#endif
+ setText(dataColIdx, data);
+ if (type == S_STRING)
+ prompt = QString("%1: %2").arg(prompt).arg(data);
+ else
+ prompt = QString("(%2) %1").arg(prompt).arg(data);
+ break;
+ }
+ if (!sym_has_value(sym) && visible)
+ prompt += " (NEW)";
+set_prompt:
+ setText(promptColIdx, prompt);
+}
+
+void ConfigItem::testUpdateMenu(bool v)
+{
+ ConfigItem* i;
+
+ visible = v;
+ if (!menu)
+ return;
+
+ sym_calc_value(menu->sym);
+ if (menu->flags & MENU_CHANGED) {
+ /* the menu entry changed, so update all list items */
+ menu->flags &= ~MENU_CHANGED;
+ for (i = (ConfigItem*)menu->data; i; i = i->nextItem)
+ i->updateMenu();
+ } else if (listView()->updateAll)
+ updateMenu();
+}
+
+void ConfigItem::paintCell(QPainter* p, const QColorGroup& cg, int column, int width, int align)
+{
+ ConfigList* list = listView();
+
+ if (visible) {
+ if (isSelected() && !list->hasFocus() && list->mode == menuMode)
+ Parent::paintCell(p, list->inactivedColorGroup, column, width, align);
+ else
+ Parent::paintCell(p, cg, column, width, align);
+ } else
+ Parent::paintCell(p, list->disabledColorGroup, column, width, align);
+}
+
+/*
+ * construct a menu entry
+ */
+void ConfigItem::init(void)
+{
+ if (menu) {
+ ConfigList* list = listView();
+ nextItem = (ConfigItem*)menu->data;
+ menu->data = this;
+
+ if (list->mode != fullMode)
+ setOpen(TRUE);
+ sym_calc_value(menu->sym);
+ }
+ updateMenu();
+}
+
+/*
+ * destruct a menu entry
+ */
+ConfigItem::~ConfigItem(void)
+{
+ if (menu) {
+ ConfigItem** ip = (ConfigItem**)&menu->data;
+ for (; *ip; ip = &(*ip)->nextItem) {
+ if (*ip == this) {
+ *ip = nextItem;
+ break;
+ }
+ }
+ }
+}
+
+void ConfigLineEdit::show(ConfigItem* i)
+{
+ item = i;
+ if (sym_get_string_value(item->menu->sym))
+ setText(QString::fromLocal8Bit(sym_get_string_value(item->menu->sym)));
+ else
+ setText(QString::null);
+ Parent::show();
+ setFocus();
+}
+
+void ConfigLineEdit::keyPressEvent(QKeyEvent* e)
+{
+ switch (e->key()) {
+ case Key_Escape:
+ break;
+ case Key_Return:
+ case Key_Enter:
+ sym_set_string_value(item->menu->sym, text().latin1());
+ parent()->updateList(item);
+ break;
+ default:
+ Parent::keyPressEvent(e);
+ return;
+ }
+ e->accept();
+ parent()->list->setFocus();
+ hide();
+}
+
+ConfigList::ConfigList(ConfigView* p, ConfigMainWindow* cv, ConfigSettings* configSettings)
+ : Parent(p), cview(cv),
+ updateAll(false),
+ symbolYesPix(xpm_symbol_yes), symbolModPix(xpm_symbol_mod), symbolNoPix(xpm_symbol_no),
+ choiceYesPix(xpm_choice_yes), choiceNoPix(xpm_choice_no),
+ menuPix(xpm_menu), menuInvPix(xpm_menu_inv), menuBackPix(xpm_menuback), voidPix(xpm_void),
+ showAll(false), showName(false), showRange(false), showData(false),
+ rootEntry(0)
+{
+ int i;
+
+ setSorting(-1);
+ setRootIsDecorated(TRUE);
+ disabledColorGroup = palette().active();
+ disabledColorGroup.setColor(QColorGroup::Text, palette().disabled().text());
+ inactivedColorGroup = palette().active();
+ inactivedColorGroup.setColor(QColorGroup::Highlight, palette().disabled().highlight());
+
+ connect(this, SIGNAL(selectionChanged(void)),
+ SLOT(updateSelection(void)));
+
+ if (configSettings) {
+ showAll = configSettings->showAll;
+ showName = configSettings->showName;
+ showRange = configSettings->showRange;
+ showData = configSettings->showData;
+ }
+
+ for (i = 0; i < colNr; i++)
+ colMap[i] = colRevMap[i] = -1;
+ addColumn(promptColIdx, "Option");
+
+ reinit();
+}
+
+void ConfigList::reinit(void)
+{
+ removeColumn(dataColIdx);
+ removeColumn(yesColIdx);
+ removeColumn(modColIdx);
+ removeColumn(noColIdx);
+ removeColumn(nameColIdx);
+
+ if (showName)
+ addColumn(nameColIdx, "Name");
+ if (showRange) {
+ addColumn(noColIdx, "N");
+ addColumn(modColIdx, "M");
+ addColumn(yesColIdx, "Y");
+ }
+ if (showData)
+ addColumn(dataColIdx, "Value");
+
+ updateListAll();
+}
+
+void ConfigList::updateSelection(void)
+{
+ struct menu *menu;
+ enum prop_type type;
+
+ ConfigItem* item = (ConfigItem*)selectedItem();
+ if (!item)
+ return;
+
+ cview->setHelp(item);
+
+ menu = item->menu;
+ if (!menu)
+ return;
+ type = menu->prompt ? menu->prompt->type : P_UNKNOWN;
+ if (mode == menuMode && type == P_MENU)
+ emit menuSelected(menu);
+}
+
+void ConfigList::updateList(ConfigItem* item)
+{
+ ConfigItem* last = 0;
+
+ if (!rootEntry)
+ goto update;
+
+ if (rootEntry != &rootmenu && (mode == singleMode ||
+ (mode == symbolMode && rootEntry->parent != &rootmenu))) {
+ item = firstChild();
+ if (!item)
+ item = new ConfigItem(this, 0, true);
+ last = item;
+ }
+ if ((mode == singleMode || (mode == symbolMode && !(rootEntry->flags & MENU_ROOT))) &&
+ rootEntry->sym && rootEntry->prompt) {
+ item = last ? last->nextSibling() : firstChild();
+ if (!item)
+ item = new ConfigItem(this, last, rootEntry, true);
+ else
+ item->testUpdateMenu(true);
+
+ updateMenuList(item, rootEntry);
+ triggerUpdate();
+ return;
+ }
+update:
+ updateMenuList(this, rootEntry);
+ triggerUpdate();
+}
+
+void ConfigList::setAllOpen(bool open)
+{
+ QListViewItemIterator it(this);
+
+ for (; it.current(); it++)
+ it.current()->setOpen(open);
+}
+
+void ConfigList::setValue(ConfigItem* item, tristate val)
+{
+ struct symbol* sym;
+ int type;
+ tristate oldval;
+
+ sym = item->menu ? item->menu->sym : 0;
+ if (!sym)
+ return;
+
+ type = sym_get_type(sym);
+ switch (type) {
+ case S_BOOLEAN:
+ case S_TRISTATE:
+ oldval = sym_get_tristate_value(sym);
+
+ if (!sym_set_tristate_value(sym, val))
+ return;
+ if (oldval == no && item->menu->list)
+ item->setOpen(TRUE);
+ parent()->updateList(item);
+ break;
+ }
+}
+
+void ConfigList::changeValue(ConfigItem* item)
+{
+ struct symbol* sym;
+ struct menu* menu;
+ int type, oldexpr, newexpr;
+
+ menu = item->menu;
+ if (!menu)
+ return;
+ sym = menu->sym;
+ if (!sym) {
+ if (item->menu->list)
+ item->setOpen(!item->isOpen());
+ return;
+ }
+
+ type = sym_get_type(sym);
+ switch (type) {
+ case S_BOOLEAN:
+ case S_TRISTATE:
+ oldexpr = sym_get_tristate_value(sym);
+ newexpr = sym_toggle_tristate_value(sym);
+ if (item->menu->list) {
+ if (oldexpr == newexpr)
+ item->setOpen(!item->isOpen());
+ else if (oldexpr == no)
+ item->setOpen(TRUE);
+ }
+ if (oldexpr != newexpr)
+ parent()->updateList(item);
+ break;
+ case S_INT:
+ case S_HEX:
+ case S_STRING:
+#if QT_VERSION >= 300
+ if (colMap[dataColIdx] >= 0)
+ item->startRename(colMap[dataColIdx]);
+ else
+#endif
+ parent()->lineEdit->show(item);
+ break;
+ }
+}
+
+void ConfigList::setRootMenu(struct menu *menu)
+{
+ enum prop_type type;
+
+ if (rootEntry == menu)
+ return;
+ type = menu && menu->prompt ? menu->prompt->type : P_UNKNOWN;
+ if (type != P_MENU)
+ return;
+ updateMenuList(this, 0);
+ rootEntry = menu;
+ updateListAll();
+ setSelected(currentItem(), hasFocus());
+}
+
+void ConfigList::setParentMenu(void)
+{
+ ConfigItem* item;
+ struct menu *oldroot;
+
+ oldroot = rootEntry;
+ if (rootEntry == &rootmenu)
+ return;
+ setRootMenu(menu_get_parent_menu(rootEntry->parent));
+
+ QListViewItemIterator it(this);
+ for (; (item = (ConfigItem*)it.current()); it++) {
+ if (item->menu == oldroot) {
+ setCurrentItem(item);
+ ensureItemVisible(item);
+ break;
+ }
+ }
+}
+
+void ConfigList::keyPressEvent(QKeyEvent* ev)
+{
+ QListViewItem* i = currentItem();
+ ConfigItem* item;
+ struct menu *menu;
+ enum prop_type type;
+
+ if (ev->key() == Key_Escape && mode != fullMode) {
+ emit parentSelected();
+ ev->accept();
+ return;
+ }
+
+ if (!i) {
+ Parent::keyPressEvent(ev);
+ return;
+ }
+ item = (ConfigItem*)i;
+
+ switch (ev->key()) {
+ case Key_Return:
+ case Key_Enter:
+ if (item->goParent) {
+ emit parentSelected();
+ break;
+ }
+ menu = item->menu;
+ if (!menu)
+ break;
+ type = menu->prompt ? menu->prompt->type : P_UNKNOWN;
+ if (type == P_MENU && rootEntry != menu &&
+ mode != fullMode && mode != menuMode) {
+ emit menuSelected(menu);
+ break;
+ }
+ case Key_Space:
+ changeValue(item);
+ break;
+ case Key_N:
+ setValue(item, no);
+ break;
+ case Key_M:
+ setValue(item, mod);
+ break;
+ case Key_Y:
+ setValue(item, yes);
+ break;
+ default:
+ Parent::keyPressEvent(ev);
+ return;
+ }
+ ev->accept();
+}
+
+void ConfigList::contentsMousePressEvent(QMouseEvent* e)
+{
+ //QPoint p(contentsToViewport(e->pos()));
+ //printf("contentsMousePressEvent: %d,%d\n", p.x(), p.y());
+ Parent::contentsMousePressEvent(e);
+}
+
+void ConfigList::contentsMouseReleaseEvent(QMouseEvent* e)
+{
+ QPoint p(contentsToViewport(e->pos()));
+ ConfigItem* item = (ConfigItem*)itemAt(p);
+ struct menu *menu;
+ enum prop_type ptype;
+ const QPixmap* pm;
+ int idx, x;
+
+ if (!item)
+ goto skip;
+
+ menu = item->menu;
+ x = header()->offset() + p.x();
+ idx = colRevMap[header()->sectionAt(x)];
+ switch (idx) {
+ case promptColIdx:
+ pm = item->pixmap(promptColIdx);
+ if (pm) {
+ int off = header()->sectionPos(0) + itemMargin() +
+ treeStepSize() * (item->depth() + (rootIsDecorated() ? 1 : 0));
+ if (x >= off && x < off + pm->width()) {
+ if (item->goParent) {
+ emit parentSelected();
+ break;
+ } else if (!menu)
+ break;
+ ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN;
+ if (ptype == P_MENU && rootEntry != menu &&
+ mode != fullMode && mode != menuMode)
+ emit menuSelected(menu);
+ else
+ changeValue(item);
+ }
+ }
+ break;
+ case noColIdx:
+ setValue(item, no);
+ break;
+ case modColIdx:
+ setValue(item, mod);
+ break;
+ case yesColIdx:
+ setValue(item, yes);
+ break;
+ case dataColIdx:
+ changeValue(item);
+ break;
+ }
+
+skip:
+ //printf("contentsMouseReleaseEvent: %d,%d\n", p.x(), p.y());
+ Parent::contentsMouseReleaseEvent(e);
+}
+
+void ConfigList::contentsMouseMoveEvent(QMouseEvent* e)
+{
+ //QPoint p(contentsToViewport(e->pos()));
+ //printf("contentsMouseMoveEvent: %d,%d\n", p.x(), p.y());
+ Parent::contentsMouseMoveEvent(e);
+}
+
+void ConfigList::contentsMouseDoubleClickEvent(QMouseEvent* e)
+{
+ QPoint p(contentsToViewport(e->pos()));
+ ConfigItem* item = (ConfigItem*)itemAt(p);
+ struct menu *menu;
+ enum prop_type ptype;
+
+ if (!item)
+ goto skip;
+ if (item->goParent) {
+ emit parentSelected();
+ goto skip;
+ }
+ menu = item->menu;
+ if (!menu)
+ goto skip;
+ ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN;
+ if (ptype == P_MENU && (mode == singleMode || mode == symbolMode))
+ emit menuSelected(menu);
+ else if (menu->sym)
+ changeValue(item);
+
+skip:
+ //printf("contentsMouseDoubleClickEvent: %d,%d\n", p.x(), p.y());
+ Parent::contentsMouseDoubleClickEvent(e);
+}
+
+void ConfigList::focusInEvent(QFocusEvent *e)
+{
+ Parent::focusInEvent(e);
+
+ QListViewItem* item = currentItem();
+ if (!item)
+ return;
+
+ setSelected(item, TRUE);
+ emit gotFocus();
+}
+
+ConfigView* ConfigView::viewList;
+
+ConfigView::ConfigView(QWidget* parent, ConfigMainWindow* cview,
+ ConfigSettings *configSettings)
+ : Parent(parent)
+{
+ list = new ConfigList(this, cview, configSettings);
+ lineEdit = new ConfigLineEdit(this);
+ lineEdit->hide();
+
+ this->nextView = viewList;
+ viewList = this;
+}
+
+ConfigView::~ConfigView(void)
+{
+ ConfigView** vp;
+
+ for (vp = &viewList; *vp; vp = &(*vp)->nextView) {
+ if (*vp == this) {
+ *vp = nextView;
+ break;
+ }
+ }
+}
+
+void ConfigView::updateList(ConfigItem* item)
+{
+ ConfigView* v;
+
+ for (v = viewList; v; v = v->nextView)
+ v->list->updateList(item);
+}
+
+void ConfigView::updateListAll(void)
+{
+ ConfigView* v;
+
+ for (v = viewList; v; v = v->nextView)
+ v->list->updateListAll();
+}
+
+/*
+ * Construct the complete config widget
+ */
+ConfigMainWindow::ConfigMainWindow(void)
+{
+ QMenuBar* menu;
+ bool ok;
+ int x, y, width, height;
+
+ QWidget *d = configApp->desktop();
+
+ ConfigSettings* configSettings = new ConfigSettings();
+#if QT_VERSION >= 300
+ width = configSettings->readNumEntry("/kconfig/qconf/window width", d->width() - 64);
+ height = configSettings->readNumEntry("/kconfig/qconf/window height", d->height() - 64);
+ resize(width, height);
+ x = configSettings->readNumEntry("/kconfig/qconf/window x", 0, &ok);
+ if (ok)
+ y = configSettings->readNumEntry("/kconfig/qconf/window y", 0, &ok);
+ if (ok)
+ move(x, y);
+ showDebug = configSettings->readBoolEntry("/kconfig/qconf/showDebug", false);
+
+ // read list settings into configSettings, will be used later for ConfigList setup
+ configSettings->readListSettings();
+#else
+ width = d->width() - 64;
+ height = d->height() - 64;
+ resize(width, height);
+ showDebug = false;
+#endif
+
+ split1 = new QSplitter(this);
+ split1->setOrientation(QSplitter::Horizontal);
+ setCentralWidget(split1);
+
+ menuView = new ConfigView(split1, this, configSettings);
+ menuList = menuView->list;
+
+ split2 = new QSplitter(split1);
+ split2->setOrientation(QSplitter::Vertical);
+
+ // create config tree
+ configView = new ConfigView(split2, this, configSettings);
+ configList = configView->list;
+
+ helpText = new QTextView(split2);
+ helpText->setTextFormat(Qt::RichText);
+
+ setTabOrder(configList, helpText);
+ configList->setFocus();
+
+ menu = menuBar();
+ toolBar = new QToolBar("Tools", this);
+
+ backAction = new QAction("Back", QPixmap(xpm_back), "Back", 0, this);
+ connect(backAction, SIGNAL(activated()), SLOT(goBack()));
+ backAction->setEnabled(FALSE);
+ QAction *quitAction = new QAction("Quit", "&Quit", CTRL+Key_Q, this);
+ connect(quitAction, SIGNAL(activated()), SLOT(close()));
+ QAction *loadAction = new QAction("Load", QPixmap(xpm_load), "&Load", CTRL+Key_L, this);
+ connect(loadAction, SIGNAL(activated()), SLOT(loadConfig()));
+ QAction *saveAction = new QAction("Save", QPixmap(xpm_save), "&Save", CTRL+Key_S, this);
+ connect(saveAction, SIGNAL(activated()), SLOT(saveConfig()));
+ QAction *saveAsAction = new QAction("Save As...", "Save &As...", 0, this);
+ connect(saveAsAction, SIGNAL(activated()), SLOT(saveConfigAs()));
+ QAction *singleViewAction = new QAction("Single View", QPixmap(xpm_single_view), "Split View", 0, this);
+ connect(singleViewAction, SIGNAL(activated()), SLOT(showSingleView()));
+ QAction *splitViewAction = new QAction("Split View", QPixmap(xpm_split_view), "Split View", 0, this);
+ connect(splitViewAction, SIGNAL(activated()), SLOT(showSplitView()));
+ QAction *fullViewAction = new QAction("Full View", QPixmap(xpm_tree_view), "Full View", 0, this);
+ connect(fullViewAction, SIGNAL(activated()), SLOT(showFullView()));
+
+ QAction *showNameAction = new QAction(NULL, "Show Name", 0, this);
+ showNameAction->setToggleAction(TRUE);
+ showNameAction->setOn(configList->showName);
+ connect(showNameAction, SIGNAL(toggled(bool)), SLOT(setShowName(bool)));
+ QAction *showRangeAction = new QAction(NULL, "Show Range", 0, this);
+ showRangeAction->setToggleAction(TRUE);
+ showRangeAction->setOn(configList->showRange);
+ connect(showRangeAction, SIGNAL(toggled(bool)), SLOT(setShowRange(bool)));
+ QAction *showDataAction = new QAction(NULL, "Show Data", 0, this);
+ showDataAction->setToggleAction(TRUE);
+ showDataAction->setOn(configList->showData);
+ connect(showDataAction, SIGNAL(toggled(bool)), SLOT(setShowData(bool)));
+ QAction *showAllAction = new QAction(NULL, "Show All Options", 0, this);
+ showAllAction->setToggleAction(TRUE);
+ showAllAction->setOn(configList->showAll);
+ connect(showAllAction, SIGNAL(toggled(bool)), SLOT(setShowAll(bool)));
+ QAction *showDebugAction = new QAction(NULL, "Show Debug Info", 0, this);
+ showDebugAction->setToggleAction(TRUE);
+ showDebugAction->setOn(showDebug);
+ connect(showDebugAction, SIGNAL(toggled(bool)), SLOT(setShowDebug(bool)));
+
+ QAction *showIntroAction = new QAction(NULL, "Introduction", 0, this);
+ connect(showIntroAction, SIGNAL(activated()), SLOT(showIntro()));
+ QAction *showAboutAction = new QAction(NULL, "About", 0, this);
+ connect(showAboutAction, SIGNAL(activated()), SLOT(showAbout()));
+
+ // init tool bar
+ backAction->addTo(toolBar);
+ toolBar->addSeparator();
+ loadAction->addTo(toolBar);
+ saveAction->addTo(toolBar);
+ toolBar->addSeparator();
+ singleViewAction->addTo(toolBar);
+ splitViewAction->addTo(toolBar);
+ fullViewAction->addTo(toolBar);
+
+ // create config menu
+ QPopupMenu* config = new QPopupMenu(this);
+ menu->insertItem("&File", config);
+ loadAction->addTo(config);
+ saveAction->addTo(config);
+ saveAsAction->addTo(config);
+ config->insertSeparator();
+ quitAction->addTo(config);
+
+ // create options menu
+ QPopupMenu* optionMenu = new QPopupMenu(this);
+ menu->insertItem("&Option", optionMenu);
+ showNameAction->addTo(optionMenu);
+ showRangeAction->addTo(optionMenu);
+ showDataAction->addTo(optionMenu);
+ optionMenu->insertSeparator();
+ showAllAction->addTo(optionMenu);
+ showDebugAction->addTo(optionMenu);
+
+ // create help menu
+ QPopupMenu* helpMenu = new QPopupMenu(this);
+ menu->insertSeparator();
+ menu->insertItem("&Help", helpMenu);
+ showIntroAction->addTo(helpMenu);
+ showAboutAction->addTo(helpMenu);
+
+ connect(configList, SIGNAL(menuSelected(struct menu *)),
+ SLOT(changeMenu(struct menu *)));
+ connect(configList, SIGNAL(parentSelected()),
+ SLOT(goBack()));
+ connect(menuList, SIGNAL(menuSelected(struct menu *)),
+ SLOT(changeMenu(struct menu *)));
+
+ connect(configList, SIGNAL(gotFocus(void)),
+ SLOT(listFocusChanged(void)));
+ connect(menuList, SIGNAL(gotFocus(void)),
+ SLOT(listFocusChanged(void)));
+
+#if QT_VERSION >= 300
+ QString listMode = configSettings->readEntry("/kconfig/qconf/listMode", "symbol");
+ if (listMode == "single")
+ showSingleView();
+ else if (listMode == "full")
+ showFullView();
+ else /*if (listMode == "split")*/
+ showSplitView();
+
+ // UI setup done, restore splitter positions
+ QValueList<int> sizes = configSettings->readSizes("/kconfig/qconf/split1", &ok);
+ if (ok)
+ split1->setSizes(sizes);
+
+ sizes = configSettings->readSizes("/kconfig/qconf/split2", &ok);
+ if (ok)
+ split2->setSizes(sizes);
+#else
+ showSplitView();
+#endif
+ delete configSettings;
+}
+
+static QString print_filter(const QString &str)
+{
+ QRegExp re("[<>&\"\\n]");
+ QString res = str;
+ for (int i = 0; (i = res.find(re, i)) >= 0;) {
+ switch (res[i].latin1()) {
+ case '<':
+ res.replace(i, 1, "&lt;");
+ i += 4;
+ break;
+ case '>':
+ res.replace(i, 1, "&gt;");
+ i += 4;
+ break;
+ case '&':
+ res.replace(i, 1, "&amp;");
+ i += 5;
+ break;
+ case '"':
+ res.replace(i, 1, "&quot;");
+ i += 6;
+ break;
+ case '\n':
+ res.replace(i, 1, "<br>");
+ i += 4;
+ break;
+ }
+ }
+ return res;
+}
+
+static void expr_print_help(void *data, const char *str)
+{
+ reinterpret_cast<QString*>(data)->append(print_filter(str));
+}
+
+/*
+ * display a new help entry as soon as a new menu entry is selected
+ */
+void ConfigMainWindow::setHelp(QListViewItem* item)
+{
+ struct symbol* sym;
+ struct menu* menu = 0;
+
+ configList->parent()->lineEdit->hide();
+ if (item)
+ menu = ((ConfigItem*)item)->menu;
+ if (!menu) {
+ helpText->setText(QString::null);
+ return;
+ }
+
+ QString head, debug, help;
+ menu = ((ConfigItem*)item)->menu;
+ sym = menu->sym;
+ if (sym) {
+ if (menu->prompt) {
+ head += "<big><b>";
+ head += print_filter(_(menu->prompt->text));
+ head += "</b></big>";
+ if (sym->name) {
+ head += " (";
+ head += print_filter(_(sym->name));
+ head += ")";
+ }
+ } else if (sym->name) {
+ head += "<big><b>";
+ head += print_filter(_(sym->name));
+ head += "</b></big>";
+ }
+ head += "<br><br>";
+
+ if (showDebug) {
+ debug += "type: ";
+ debug += print_filter(sym_type_name(sym->type));
+ if (sym_is_choice(sym))
+ debug += " (choice)";
+ debug += "<br>";
+ if (sym->rev_dep.expr) {
+ debug += "reverse dep: ";
+ expr_print(sym->rev_dep.expr, expr_print_help, &debug, E_NONE);
+ debug += "<br>";
+ }
+ for (struct property *prop = sym->prop; prop; prop = prop->next) {
+ switch (prop->type) {
+ case P_PROMPT:
+ case P_MENU:
+ debug += "prompt: ";
+ debug += print_filter(_(prop->text));
+ debug += "<br>";
+ break;
+ case P_DEFAULT:
+ debug += "default: ";
+ expr_print(prop->expr, expr_print_help, &debug, E_NONE);
+ debug += "<br>";
+ break;
+ case P_CHOICE:
+ if (sym_is_choice(sym)) {
+ debug += "choice: ";
+ expr_print(prop->expr, expr_print_help, &debug, E_NONE);
+ debug += "<br>";
+ }
+ break;
+ case P_SELECT:
+ debug += "select: ";
+ expr_print(prop->expr, expr_print_help, &debug, E_NONE);
+ debug += "<br>";
+ break;
+ case P_RANGE:
+ debug += "range: ";
+ expr_print(prop->expr, expr_print_help, &debug, E_NONE);
+ debug += "<br>";
+ break;
+ default:
+ debug += "unknown property: ";
+ debug += prop_get_type_name(prop->type);
+ debug += "<br>";
+ }
+ if (prop->visible.expr) {
+ debug += "&nbsp;&nbsp;&nbsp;&nbsp;dep: ";
+ expr_print(prop->visible.expr, expr_print_help, &debug, E_NONE);
+ debug += "<br>";
+ }
+ }
+ debug += "<br>";
+ }
+
+ help = print_filter(_(sym->help));
+ } else if (menu->prompt) {
+ head += "<big><b>";
+ head += print_filter(_(menu->prompt->text));
+ head += "</b></big><br><br>";
+ if (showDebug) {
+ if (menu->prompt->visible.expr) {
+ debug += "&nbsp;&nbsp;dep: ";
+ expr_print(menu->prompt->visible.expr, expr_print_help, &debug, E_NONE);
+ debug += "<br><br>";
+ }
+ }
+ }
+ if (showDebug)
+ debug += QString().sprintf("defined at %s:%d<br><br>", menu->file->name, menu->lineno);
+ helpText->setText(head + debug + help);
+}
+
+void ConfigMainWindow::loadConfig(void)
+{
+ QString s = QFileDialog::getOpenFileName(".config", NULL, this);
+ if (s.isNull())
+ return;
+ if (conf_read(QFile::encodeName(s)))
+ QMessageBox::information(this, "qconf", "Unable to load configuration!");
+ ConfigView::updateListAll();
+}
+
+void ConfigMainWindow::saveConfig(void)
+{
+ if (conf_write(NULL))
+ QMessageBox::information(this, "qconf", "Unable to save configuration!");
+}
+
+void ConfigMainWindow::saveConfigAs(void)
+{
+ QString s = QFileDialog::getSaveFileName(".config", NULL, this);
+ if (s.isNull())
+ return;
+ if (conf_write(QFile::encodeName(s)))
+ QMessageBox::information(this, "qconf", "Unable to save configuration!");
+}
+
+void ConfigMainWindow::changeMenu(struct menu *menu)
+{
+ configList->setRootMenu(menu);
+ backAction->setEnabled(TRUE);
+}
+
+void ConfigMainWindow::listFocusChanged(void)
+{
+ if (menuList->hasFocus()) {
+ if (menuList->mode == menuMode)
+ configList->clearSelection();
+ setHelp(menuList->selectedItem());
+ } else if (configList->hasFocus()) {
+ setHelp(configList->selectedItem());
+ }
+}
+
+void ConfigMainWindow::goBack(void)
+{
+ ConfigItem* item;
+
+ configList->setParentMenu();
+ if (configList->rootEntry == &rootmenu)
+ backAction->setEnabled(FALSE);
+ item = (ConfigItem*)menuList->selectedItem();
+ while (item) {
+ if (item->menu == configList->rootEntry) {
+ menuList->setSelected(item, TRUE);
+ break;
+ }
+ item = (ConfigItem*)item->parent();
+ }
+}
+
+void ConfigMainWindow::showSingleView(void)
+{
+ menuView->hide();
+ menuList->setRootMenu(0);
+ configList->mode = singleMode;
+ if (configList->rootEntry == &rootmenu)
+ configList->updateListAll();
+ else
+ configList->setRootMenu(&rootmenu);
+ configList->setAllOpen(TRUE);
+ configList->setFocus();
+}
+
+void ConfigMainWindow::showSplitView(void)
+{
+ configList->mode = symbolMode;
+ if (configList->rootEntry == &rootmenu)
+ configList->updateListAll();
+ else
+ configList->setRootMenu(&rootmenu);
+ configList->setAllOpen(TRUE);
+ configApp->processEvents();
+ menuList->mode = menuMode;
+ menuList->setRootMenu(&rootmenu);
+ menuList->setAllOpen(TRUE);
+ menuView->show();
+ menuList->setFocus();
+}
+
+void ConfigMainWindow::showFullView(void)
+{
+ menuView->hide();
+ menuList->setRootMenu(0);
+ configList->mode = fullMode;
+ if (configList->rootEntry == &rootmenu)
+ configList->updateListAll();
+ else
+ configList->setRootMenu(&rootmenu);
+ configList->setAllOpen(FALSE);
+ configList->setFocus();
+}
+
+void ConfigMainWindow::setShowAll(bool b)
+{
+ if (configList->showAll == b)
+ return;
+ configList->showAll = b;
+ configList->updateListAll();
+ menuList->showAll = b;
+ menuList->updateListAll();
+}
+
+void ConfigMainWindow::setShowDebug(bool b)
+{
+ if (showDebug == b)
+ return;
+ showDebug = b;
+}
+
+void ConfigMainWindow::setShowName(bool b)
+{
+ if (configList->showName == b)
+ return;
+ configList->showName = b;
+ configList->reinit();
+ menuList->showName = b;
+ menuList->reinit();
+}
+
+void ConfigMainWindow::setShowRange(bool b)
+{
+ if (configList->showRange == b)
+ return;
+ configList->showRange = b;
+ configList->reinit();
+ menuList->showRange = b;
+ menuList->reinit();
+}
+
+void ConfigMainWindow::setShowData(bool b)
+{
+ if (configList->showData == b)
+ return;
+ configList->showData = b;
+ configList->reinit();
+ menuList->showData = b;
+ menuList->reinit();
+}
+
+/*
+ * ask for saving configuration before quitting
+ * TODO ask only when something changed
+ */
+void ConfigMainWindow::closeEvent(QCloseEvent* e)
+{
+ if (!sym_change_count) {
+ e->accept();
+ return;
+ }
+ QMessageBox mb("qconf", "Save configuration?", QMessageBox::Warning,
+ QMessageBox::Yes | QMessageBox::Default, QMessageBox::No, QMessageBox::Cancel | QMessageBox::Escape);
+ mb.setButtonText(QMessageBox::Yes, "&Save Changes");
+ mb.setButtonText(QMessageBox::No, "&Discard Changes");
+ mb.setButtonText(QMessageBox::Cancel, "Cancel Exit");
+ switch (mb.exec()) {
+ case QMessageBox::Yes:
+ conf_write(NULL);
+ case QMessageBox::No:
+ e->accept();
+ break;
+ case QMessageBox::Cancel:
+ e->ignore();
+ break;
+ }
+}
+
+void ConfigMainWindow::showIntro(void)
+{
+ static char str[] = "Welcome to the qconf graphical busybox configuration tool for Linux.\n\n"
+ "For each option, a blank box indicates the feature is disabled, a check\n"
+ "indicates it is enabled, and a dot indicates that it is to be compiled\n"
+ "as a module. Clicking on the box will cycle through the three states.\n\n"
+ "If you do not see an option (e.g., a device driver) that you believe\n"
+ "should be present, try turning on Show All Options under the Options menu.\n"
+ "Although there is no cross reference yet to help you figure out what other\n"
+ "options must be enabled to support the option you are interested in, you can\n"
+ "still view the help of a grayed-out option.\n\n"
+ "Toggling Show Debug Info under the Options menu will show the dependencies,\n"
+ "which you can then match by examining other options.\n\n";
+
+ QMessageBox::information(this, "qconf", str);
+}
+
+void ConfigMainWindow::showAbout(void)
+{
+ static char str[] = "qconf is Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>.\n\n"
+ "Bug reports and feature request can also be entered at http://bugs.busybox.net/\n";
+
+ QMessageBox::information(this, "qconf", str);
+}
+
+void ConfigMainWindow::saveSettings(void)
+{
+#if QT_VERSION >= 300
+ ConfigSettings *configSettings = new ConfigSettings;
+ configSettings->writeEntry("/kconfig/qconf/window x", pos().x());
+ configSettings->writeEntry("/kconfig/qconf/window y", pos().y());
+ configSettings->writeEntry("/kconfig/qconf/window width", size().width());
+ configSettings->writeEntry("/kconfig/qconf/window height", size().height());
+ configSettings->writeEntry("/kconfig/qconf/showName", configList->showName);
+ configSettings->writeEntry("/kconfig/qconf/showRange", configList->showRange);
+ configSettings->writeEntry("/kconfig/qconf/showData", configList->showData);
+ configSettings->writeEntry("/kconfig/qconf/showAll", configList->showAll);
+ configSettings->writeEntry("/kconfig/qconf/showDebug", showDebug);
+
+ QString entry;
+ switch(configList->mode) {
+ case singleMode :
+ entry = "single";
+ break;
+
+ case symbolMode :
+ entry = "split";
+ break;
+
+ case fullMode :
+ entry = "full";
+ break;
+ }
+ configSettings->writeEntry("/kconfig/qconf/listMode", entry);
+
+ configSettings->writeSizes("/kconfig/qconf/split1", split1->sizes());
+ configSettings->writeSizes("/kconfig/qconf/split2", split2->sizes());
+
+ delete configSettings;
+#endif
+}
+
+void fixup_rootmenu(struct menu *menu)
+{
+ struct menu *child;
+ static int menu_cnt = 0;
+
+ menu->flags |= MENU_ROOT;
+ for (child = menu->list; child; child = child->next) {
+ if (child->prompt && child->prompt->type == P_MENU) {
+ menu_cnt++;
+ fixup_rootmenu(child);
+ menu_cnt--;
+ } else if (!menu_cnt)
+ fixup_rootmenu(child);
+ }
+}
+
+static const char *progname;
+
+static void usage(void)
+{
+ printf("%s <config>\n", progname);
+ exit(0);
+}
+
+int main(int ac, char** av)
+{
+ ConfigMainWindow* v;
+ const char *name;
+
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+
+#ifndef LKC_DIRECT_LINK
+ kconfig_load();
+#endif
+
+ progname = av[0];
+ configApp = new QApplication(ac, av);
+ if (ac > 1 && av[1][0] == '-') {
+ switch (av[1][1]) {
+ case 'h':
+ case '?':
+ usage();
+ }
+ name = av[2];
+ } else
+ name = av[1];
+ if (!name)
+ usage();
+
+ conf_parse(name);
+ fixup_rootmenu(&rootmenu);
+ conf_read(NULL);
+ //zconfdump(stdout);
+
+ v = new ConfigMainWindow();
+
+ //zconfdump(stdout);
+ v->show();
+ configApp->connect(configApp, SIGNAL(lastWindowClosed()), SLOT(quit()));
+ configApp->connect(configApp, SIGNAL(aboutToQuit()), v, SLOT(saveSettings()));
+ configApp->exec();
+
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/scripts/kconfig/qconf.h b/i/pc104/initrd/conf/busybox/scripts/kconfig/qconf.h
new file mode 100644
index 0000000..e52f3e9
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/scripts/kconfig/qconf.h
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#include <qlistview.h>
+#if QT_VERSION >= 300
+#include <qsettings.h>
+#else
+class QSettings { };
+#endif
+
+class ConfigList;
+class ConfigItem;
+class ConfigLineEdit;
+class ConfigMainWindow;
+
+
+class ConfigSettings : public QSettings {
+public:
+ ConfigSettings();
+
+#if QT_VERSION >= 300
+ void readListSettings();
+ QValueList<int> readSizes(const QString& key, bool *ok);
+ bool writeSizes(const QString& key, const QValueList<int>& value);
+#endif
+
+ bool showAll;
+ bool showName;
+ bool showRange;
+ bool showData;
+};
+
+class ConfigView : public QVBox {
+ Q_OBJECT
+ typedef class QVBox Parent;
+public:
+ ConfigView(QWidget* parent, ConfigMainWindow* cview, ConfigSettings* configSettings);
+ ~ConfigView(void);
+ static void updateList(ConfigItem* item);
+ static void updateListAll(void);
+
+public:
+ ConfigList* list;
+ ConfigLineEdit* lineEdit;
+
+ static ConfigView* viewList;
+ ConfigView* nextView;
+};
+
+enum colIdx {
+ promptColIdx, nameColIdx, noColIdx, modColIdx, yesColIdx, dataColIdx, colNr
+};
+enum listMode {
+ singleMode, menuMode, symbolMode, fullMode
+};
+
+class ConfigList : public QListView {
+ Q_OBJECT
+ typedef class QListView Parent;
+public:
+ ConfigList(ConfigView* p, ConfigMainWindow* cview, ConfigSettings *configSettings);
+ void reinit(void);
+ ConfigView* parent(void) const
+ {
+ return (ConfigView*)Parent::parent();
+ }
+
+protected:
+ ConfigMainWindow* cview;
+
+ void keyPressEvent(QKeyEvent *e);
+ void contentsMousePressEvent(QMouseEvent *e);
+ void contentsMouseReleaseEvent(QMouseEvent *e);
+ void contentsMouseMoveEvent(QMouseEvent *e);
+ void contentsMouseDoubleClickEvent(QMouseEvent *e);
+ void focusInEvent(QFocusEvent *e);
+public slots:
+ void setRootMenu(struct menu *menu);
+
+ void updateList(ConfigItem *item);
+ void setValue(ConfigItem* item, tristate val);
+ void changeValue(ConfigItem* item);
+ void updateSelection(void);
+signals:
+ void menuSelected(struct menu *menu);
+ void parentSelected(void);
+ void gotFocus(void);
+
+public:
+ void updateListAll(void)
+ {
+ updateAll = true;
+ updateList(NULL);
+ updateAll = false;
+ }
+ ConfigList* listView()
+ {
+ return this;
+ }
+ ConfigItem* firstChild() const
+ {
+ return (ConfigItem *)Parent::firstChild();
+ }
+ int mapIdx(colIdx idx)
+ {
+ return colMap[idx];
+ }
+ void addColumn(colIdx idx, const QString& label)
+ {
+ colMap[idx] = Parent::addColumn(label);
+ colRevMap[colMap[idx]] = idx;
+ }
+ void removeColumn(colIdx idx)
+ {
+ int col = colMap[idx];
+ if (col >= 0) {
+ Parent::removeColumn(col);
+ colRevMap[col] = colMap[idx] = -1;
+ }
+ }
+ void setAllOpen(bool open);
+ void setParentMenu(void);
+
+ template <class P>
+ void updateMenuList(P*, struct menu*);
+
+ bool updateAll;
+
+ QPixmap symbolYesPix, symbolModPix, symbolNoPix;
+ QPixmap choiceYesPix, choiceNoPix;
+ QPixmap menuPix, menuInvPix, menuBackPix, voidPix;
+
+ bool showAll, showName, showRange, showData;
+ enum listMode mode;
+ struct menu *rootEntry;
+ QColorGroup disabledColorGroup;
+ QColorGroup inactivedColorGroup;
+
+private:
+ int colMap[colNr];
+ int colRevMap[colNr];
+};
+
+class ConfigItem : public QListViewItem {
+ typedef class QListViewItem Parent;
+public:
+ ConfigItem(QListView *parent, ConfigItem *after, struct menu *m, bool v)
+ : Parent(parent, after), menu(m), visible(v), goParent(false)
+ {
+ init();
+ }
+ ConfigItem(ConfigItem *parent, ConfigItem *after, struct menu *m, bool v)
+ : Parent(parent, after), menu(m), visible(v), goParent(false)
+ {
+ init();
+ }
+ ConfigItem(QListView *parent, ConfigItem *after, bool v)
+ : Parent(parent, after), menu(0), visible(v), goParent(true)
+ {
+ init();
+ }
+ ~ConfigItem(void);
+ void init(void);
+#if QT_VERSION >= 300
+ void okRename(int col);
+#endif
+ void updateMenu(void);
+ void testUpdateMenu(bool v);
+ ConfigList* listView() const
+ {
+ return (ConfigList*)Parent::listView();
+ }
+ ConfigItem* firstChild() const
+ {
+ return (ConfigItem *)Parent::firstChild();
+ }
+ ConfigItem* nextSibling() const
+ {
+ return (ConfigItem *)Parent::nextSibling();
+ }
+ void setText(colIdx idx, const QString& text)
+ {
+ Parent::setText(listView()->mapIdx(idx), text);
+ }
+ QString text(colIdx idx) const
+ {
+ return Parent::text(listView()->mapIdx(idx));
+ }
+ void setPixmap(colIdx idx, const QPixmap& pm)
+ {
+ Parent::setPixmap(listView()->mapIdx(idx), pm);
+ }
+ const QPixmap* pixmap(colIdx idx) const
+ {
+ return Parent::pixmap(listView()->mapIdx(idx));
+ }
+ void paintCell(QPainter* p, const QColorGroup& cg, int column, int width, int align);
+
+ ConfigItem* nextItem;
+ struct menu *menu;
+ bool visible;
+ bool goParent;
+};
+
+class ConfigLineEdit : public QLineEdit {
+ Q_OBJECT
+ typedef class QLineEdit Parent;
+public:
+ ConfigLineEdit(ConfigView* parent)
+ : Parent(parent)
+ { }
+ ConfigView* parent(void) const
+ {
+ return (ConfigView*)Parent::parent();
+ }
+ void show(ConfigItem *i);
+ void keyPressEvent(QKeyEvent *e);
+
+public:
+ ConfigItem *item;
+};
+
+class ConfigMainWindow : public QMainWindow {
+ Q_OBJECT
+public:
+ ConfigMainWindow(void);
+public slots:
+ void setHelp(QListViewItem* item);
+ void changeMenu(struct menu *);
+ void listFocusChanged(void);
+ void goBack(void);
+ void loadConfig(void);
+ void saveConfig(void);
+ void saveConfigAs(void);
+ void showSingleView(void);
+ void showSplitView(void);
+ void showFullView(void);
+ void setShowAll(bool);
+ void setShowDebug(bool);
+ void setShowRange(bool);
+ void setShowName(bool);
+ void setShowData(bool);
+ void showIntro(void);
+ void showAbout(void);
+ void saveSettings(void);
+
+protected:
+ void closeEvent(QCloseEvent *e);
+
+ ConfigView *menuView;
+ ConfigList *menuList;
+ ConfigView *configView;
+ ConfigList *configList;
+ QTextView *helpText;
+ QToolBar *toolBar;
+ QAction *backAction;
+ QSplitter* split1;
+ QSplitter* split2;
+
+ bool showDebug;
+};
diff --git a/i/pc104/initrd/conf/busybox/scripts/kconfig/symbol.c b/i/pc104/initrd/conf/busybox/scripts/kconfig/symbol.c
new file mode 100644
index 0000000..3d7877a
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/scripts/kconfig/symbol.c
@@ -0,0 +1,882 @@
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <regex.h>
+#include <sys/utsname.h>
+
+#define LKC_DIRECT_LINK
+#include "lkc.h"
+
+struct symbol symbol_yes = {
+ .name = "y",
+ .curr = { "y", yes },
+ .flags = SYMBOL_YES|SYMBOL_VALID,
+}, symbol_mod = {
+ .name = "m",
+ .curr = { "m", mod },
+ .flags = SYMBOL_MOD|SYMBOL_VALID,
+}, symbol_no = {
+ .name = "n",
+ .curr = { "n", no },
+ .flags = SYMBOL_NO|SYMBOL_VALID,
+}, symbol_empty = {
+ .name = "",
+ .curr = { "", no },
+ .flags = SYMBOL_VALID,
+};
+
+int sym_change_count;
+struct symbol *modules_sym;
+tristate modules_val;
+
+void sym_add_default(struct symbol *sym, const char *def)
+{
+ struct property *prop = prop_alloc(P_DEFAULT, sym);
+
+ prop->expr = expr_alloc_symbol(sym_lookup(def, 1));
+}
+
+void sym_init(void)
+{
+ struct symbol *sym;
+ struct utsname uts;
+ char *p;
+ static bool inited = false;
+
+ if (inited)
+ return;
+ inited = true;
+
+ uname(&uts);
+
+ sym = sym_lookup("ARCH", 0);
+ sym->type = S_STRING;
+ sym->flags |= SYMBOL_AUTO;
+ p = getenv("ARCH");
+ if (p)
+ sym_add_default(sym, p);
+
+ sym = sym_lookup("KERNELVERSION", 0);
+ sym->type = S_STRING;
+ sym->flags |= SYMBOL_AUTO;
+ p = getenv("KERNELVERSION");
+ if (p)
+ sym_add_default(sym, p);
+
+ sym = sym_lookup("UNAME_RELEASE", 0);
+ sym->type = S_STRING;
+ sym->flags |= SYMBOL_AUTO;
+ sym_add_default(sym, uts.release);
+}
+
+enum symbol_type sym_get_type(struct symbol *sym)
+{
+ enum symbol_type type = sym->type;
+
+ if (type == S_TRISTATE) {
+ if (sym_is_choice_value(sym) && sym->visible == yes)
+ type = S_BOOLEAN;
+ else if (modules_val == no)
+ type = S_BOOLEAN;
+ }
+ return type;
+}
+
+const char *sym_type_name(enum symbol_type type)
+{
+ switch (type) {
+ case S_BOOLEAN:
+ return "boolean";
+ case S_TRISTATE:
+ return "tristate";
+ case S_INT:
+ return "integer";
+ case S_HEX:
+ return "hex";
+ case S_STRING:
+ return "string";
+ case S_UNKNOWN:
+ return "unknown";
+ case S_OTHER:
+ break;
+ }
+ return "???";
+}
+
+struct property *sym_get_choice_prop(struct symbol *sym)
+{
+ struct property *prop;
+
+ for_all_choices(sym, prop)
+ return prop;
+ return NULL;
+}
+
+struct property *sym_get_default_prop(struct symbol *sym)
+{
+ struct property *prop;
+
+ for_all_defaults(sym, prop) {
+ prop->visible.tri = expr_calc_value(prop->visible.expr);
+ if (prop->visible.tri != no)
+ return prop;
+ }
+ return NULL;
+}
+
+struct property *sym_get_range_prop(struct symbol *sym)
+{
+ struct property *prop;
+
+ for_all_properties(sym, prop, P_RANGE) {
+ prop->visible.tri = expr_calc_value(prop->visible.expr);
+ if (prop->visible.tri != no)
+ return prop;
+ }
+ return NULL;
+}
+
+static int sym_get_range_val(struct symbol *sym, int base)
+{
+ sym_calc_value(sym);
+ switch (sym->type) {
+ case S_INT:
+ base = 10;
+ break;
+ case S_HEX:
+ base = 16;
+ break;
+ default:
+ break;
+ }
+ return strtol(sym->curr.val, NULL, base);
+}
+
+static void sym_validate_range(struct symbol *sym)
+{
+ struct property *prop;
+ int base, val, val2;
+ char str[64];
+
+ switch (sym->type) {
+ case S_INT:
+ base = 10;
+ break;
+ case S_HEX:
+ base = 16;
+ break;
+ default:
+ return;
+ }
+ prop = sym_get_range_prop(sym);
+ if (!prop)
+ return;
+ val = strtol(sym->curr.val, NULL, base);
+ val2 = sym_get_range_val(prop->expr->left.sym, base);
+ if (val >= val2) {
+ val2 = sym_get_range_val(prop->expr->right.sym, base);
+ if (val <= val2)
+ return;
+ }
+ if (sym->type == S_INT)
+ sprintf(str, "%d", val2);
+ else
+ sprintf(str, "0x%x", val2);
+ sym->curr.val = strdup(str);
+}
+
+static void sym_calc_visibility(struct symbol *sym)
+{
+ struct property *prop;
+ tristate tri;
+
+ /* any prompt visible? */
+ tri = no;
+ for_all_prompts(sym, prop) {
+ prop->visible.tri = expr_calc_value(prop->visible.expr);
+ tri = E_OR(tri, prop->visible.tri);
+ }
+ if (tri == mod && (sym->type != S_TRISTATE || modules_val == no))
+ tri = yes;
+ if (sym->visible != tri) {
+ sym->visible = tri;
+ sym_set_changed(sym);
+ }
+ if (sym_is_choice_value(sym))
+ return;
+ tri = no;
+ if (sym->rev_dep.expr)
+ tri = expr_calc_value(sym->rev_dep.expr);
+ if (tri == mod && sym_get_type(sym) == S_BOOLEAN)
+ tri = yes;
+ if (sym->rev_dep.tri != tri) {
+ sym->rev_dep.tri = tri;
+ sym_set_changed(sym);
+ }
+}
+
+static struct symbol *sym_calc_choice(struct symbol *sym)
+{
+ struct symbol *def_sym;
+ struct property *prop;
+ struct expr *e;
+
+ /* is the user choice visible? */
+ def_sym = sym->user.val;
+ if (def_sym) {
+ sym_calc_visibility(def_sym);
+ if (def_sym->visible != no)
+ return def_sym;
+ }
+
+ /* any of the defaults visible? */
+ for_all_defaults(sym, prop) {
+ prop->visible.tri = expr_calc_value(prop->visible.expr);
+ if (prop->visible.tri == no)
+ continue;
+ def_sym = prop_get_symbol(prop);
+ sym_calc_visibility(def_sym);
+ if (def_sym->visible != no)
+ return def_sym;
+ }
+
+ /* just get the first visible value */
+ prop = sym_get_choice_prop(sym);
+ for (e = prop->expr; e; e = e->left.expr) {
+ def_sym = e->right.sym;
+ sym_calc_visibility(def_sym);
+ if (def_sym->visible != no)
+ return def_sym;
+ }
+
+ /* no choice? reset tristate value */
+ sym->curr.tri = no;
+ return NULL;
+}
+
+void sym_calc_value(struct symbol *sym)
+{
+ struct symbol_value newval, oldval;
+ struct property *prop;
+ struct expr *e;
+
+ if (!sym)
+ return;
+
+ if (sym->flags & SYMBOL_VALID)
+ return;
+ sym->flags |= SYMBOL_VALID;
+
+ oldval = sym->curr;
+
+ switch (sym->type) {
+ case S_INT:
+ case S_HEX:
+ case S_STRING:
+ newval = symbol_empty.curr;
+ break;
+ case S_BOOLEAN:
+ case S_TRISTATE:
+ newval = symbol_no.curr;
+ break;
+ default:
+ sym->curr.val = sym->name;
+ sym->curr.tri = no;
+ return;
+ }
+ if (!sym_is_choice_value(sym))
+ sym->flags &= ~SYMBOL_WRITE;
+
+ sym_calc_visibility(sym);
+
+ /* set default if recursively called */
+ sym->curr = newval;
+
+ switch (sym_get_type(sym)) {
+ case S_BOOLEAN:
+ case S_TRISTATE:
+ if (sym_is_choice_value(sym) && sym->visible == yes) {
+ prop = sym_get_choice_prop(sym);
+ newval.tri = (prop_get_symbol(prop)->curr.val == sym) ? yes : no;
+ } else if (E_OR(sym->visible, sym->rev_dep.tri) != no) {
+ sym->flags |= SYMBOL_WRITE;
+ if (sym_has_value(sym))
+ newval.tri = sym->user.tri;
+ else if (!sym_is_choice(sym)) {
+ prop = sym_get_default_prop(sym);
+ if (prop)
+ newval.tri = expr_calc_value(prop->expr);
+ }
+ newval.tri = E_OR(E_AND(newval.tri, sym->visible), sym->rev_dep.tri);
+ } else if (!sym_is_choice(sym)) {
+ prop = sym_get_default_prop(sym);
+ if (prop) {
+ sym->flags |= SYMBOL_WRITE;
+ newval.tri = expr_calc_value(prop->expr);
+ }
+ }
+ if (newval.tri == mod && sym_get_type(sym) == S_BOOLEAN)
+ newval.tri = yes;
+ break;
+ case S_STRING:
+ case S_HEX:
+ case S_INT:
+ if (sym->visible != no) {
+ sym->flags |= SYMBOL_WRITE;
+ if (sym_has_value(sym)) {
+ newval.val = sym->user.val;
+ break;
+ }
+ }
+ prop = sym_get_default_prop(sym);
+ if (prop) {
+ struct symbol *ds = prop_get_symbol(prop);
+ if (ds) {
+ sym->flags |= SYMBOL_WRITE;
+ sym_calc_value(ds);
+ newval.val = ds->curr.val;
+ }
+ }
+ break;
+ default:
+ ;
+ }
+
+ sym->curr = newval;
+ if (sym_is_choice(sym) && newval.tri == yes)
+ sym->curr.val = sym_calc_choice(sym);
+ sym_validate_range(sym);
+
+ if (memcmp(&oldval, &sym->curr, sizeof(oldval)))
+ sym_set_changed(sym);
+ if (modules_sym == sym)
+ modules_val = modules_sym->curr.tri;
+
+ if (sym_is_choice(sym)) {
+ int flags = sym->flags & (SYMBOL_CHANGED | SYMBOL_WRITE);
+ prop = sym_get_choice_prop(sym);
+ for (e = prop->expr; e; e = e->left.expr) {
+ e->right.sym->flags |= flags;
+ if (flags & SYMBOL_CHANGED)
+ sym_set_changed(e->right.sym);
+ }
+ }
+}
+
+void sym_clear_all_valid(void)
+{
+ struct symbol *sym;
+ int i;
+
+ for_all_symbols(i, sym)
+ sym->flags &= ~SYMBOL_VALID;
+ sym_change_count++;
+ if (modules_sym)
+ sym_calc_value(modules_sym);
+}
+
+void sym_set_changed(struct symbol *sym)
+{
+ struct property *prop;
+
+ sym->flags |= SYMBOL_CHANGED;
+ for (prop = sym->prop; prop; prop = prop->next) {
+ if (prop->menu)
+ prop->menu->flags |= MENU_CHANGED;
+ }
+}
+
+void sym_set_all_changed(void)
+{
+ struct symbol *sym;
+ int i;
+
+ for_all_symbols(i, sym)
+ sym_set_changed(sym);
+}
+
+bool sym_tristate_within_range(struct symbol *sym, tristate val)
+{
+ int type = sym_get_type(sym);
+
+ if (sym->visible == no)
+ return false;
+
+ if (type != S_BOOLEAN && type != S_TRISTATE)
+ return false;
+
+ if (type == S_BOOLEAN && val == mod)
+ return false;
+ if (sym->visible <= sym->rev_dep.tri)
+ return false;
+ if (sym_is_choice_value(sym) && sym->visible == yes)
+ return val == yes;
+ return val >= sym->rev_dep.tri && val <= sym->visible;
+}
+
+bool sym_set_tristate_value(struct symbol *sym, tristate val)
+{
+ tristate oldval = sym_get_tristate_value(sym);
+
+ if (oldval != val && !sym_tristate_within_range(sym, val))
+ return false;
+
+ if (sym->flags & SYMBOL_NEW) {
+ sym->flags &= ~SYMBOL_NEW;
+ sym_set_changed(sym);
+ }
+ /*
+ * setting a choice value also resets the new flag of the choice
+ * symbol and all other choice values.
+ */
+ if (sym_is_choice_value(sym) && val == yes) {
+ struct symbol *cs = prop_get_symbol(sym_get_choice_prop(sym));
+ struct property *prop;
+ struct expr *e;
+
+ cs->user.val = sym;
+ cs->flags &= ~SYMBOL_NEW;
+ prop = sym_get_choice_prop(cs);
+ for (e = prop->expr; e; e = e->left.expr) {
+ if (e->right.sym->visible != no)
+ e->right.sym->flags &= ~SYMBOL_NEW;
+ }
+ }
+
+ sym->user.tri = val;
+ if (oldval != val) {
+ sym_clear_all_valid();
+ if (sym == modules_sym)
+ sym_set_all_changed();
+ }
+
+ return true;
+}
+
+tristate sym_toggle_tristate_value(struct symbol *sym)
+{
+ tristate oldval, newval;
+
+ oldval = newval = sym_get_tristate_value(sym);
+ do {
+ switch (newval) {
+ case no:
+ newval = mod;
+ break;
+ case mod:
+ newval = yes;
+ break;
+ case yes:
+ newval = no;
+ break;
+ }
+ if (sym_set_tristate_value(sym, newval))
+ break;
+ } while (oldval != newval);
+ return newval;
+}
+
+bool sym_string_valid(struct symbol *sym, const char *str)
+{
+ signed char ch;
+
+ switch (sym->type) {
+ case S_STRING:
+ return true;
+ case S_INT:
+ ch = *str++;
+ if (ch == '-')
+ ch = *str++;
+ if (!isdigit(ch))
+ return false;
+ if (ch == '0' && *str != 0)
+ return false;
+ while ((ch = *str++)) {
+ if (!isdigit(ch))
+ return false;
+ }
+ return true;
+ case S_HEX:
+ if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X'))
+ str += 2;
+ ch = *str++;
+ do {
+ if (!isxdigit(ch))
+ return false;
+ } while ((ch = *str++));
+ return true;
+ case S_BOOLEAN:
+ case S_TRISTATE:
+ switch (str[0]) {
+ case 'y': case 'Y':
+ case 'm': case 'M':
+ case 'n': case 'N':
+ return true;
+ }
+ return false;
+ default:
+ return false;
+ }
+}
+
+bool sym_string_within_range(struct symbol *sym, const char *str)
+{
+ struct property *prop;
+ int val;
+
+ switch (sym->type) {
+ case S_STRING:
+ return sym_string_valid(sym, str);
+ case S_INT:
+ if (!sym_string_valid(sym, str))
+ return false;
+ prop = sym_get_range_prop(sym);
+ if (!prop)
+ return true;
+ val = strtol(str, NULL, 10);
+ return val >= sym_get_range_val(prop->expr->left.sym, 10) &&
+ val <= sym_get_range_val(prop->expr->right.sym, 10);
+ case S_HEX:
+ if (!sym_string_valid(sym, str))
+ return false;
+ prop = sym_get_range_prop(sym);
+ if (!prop)
+ return true;
+ val = strtol(str, NULL, 16);
+ return val >= sym_get_range_val(prop->expr->left.sym, 16) &&
+ val <= sym_get_range_val(prop->expr->right.sym, 16);
+ case S_BOOLEAN:
+ case S_TRISTATE:
+ switch (str[0]) {
+ case 'y': case 'Y':
+ return sym_tristate_within_range(sym, yes);
+ case 'm': case 'M':
+ return sym_tristate_within_range(sym, mod);
+ case 'n': case 'N':
+ return sym_tristate_within_range(sym, no);
+ }
+ return false;
+ default:
+ return false;
+ }
+}
+
+bool sym_set_string_value(struct symbol *sym, const char *newval)
+{
+ const char *oldval;
+ char *val;
+ int size;
+
+ switch (sym->type) {
+ case S_BOOLEAN:
+ case S_TRISTATE:
+ switch (newval[0]) {
+ case 'y': case 'Y':
+ return sym_set_tristate_value(sym, yes);
+ case 'm': case 'M':
+ return sym_set_tristate_value(sym, mod);
+ case 'n': case 'N':
+ return sym_set_tristate_value(sym, no);
+ }
+ return false;
+ default:
+ ;
+ }
+
+ if (!sym_string_within_range(sym, newval))
+ return false;
+
+ if (sym->flags & SYMBOL_NEW) {
+ sym->flags &= ~SYMBOL_NEW;
+ sym_set_changed(sym);
+ }
+
+ oldval = sym->user.val;
+ size = strlen(newval) + 1;
+ if (sym->type == S_HEX && (newval[0] != '0' || (newval[1] != 'x' && newval[1] != 'X'))) {
+ size += 2;
+ sym->user.val = val = malloc(size);
+ *val++ = '0';
+ *val++ = 'x';
+ } else if (!oldval || strcmp(oldval, newval))
+ sym->user.val = val = malloc(size);
+ else
+ return true;
+
+ strcpy(val, newval);
+ free((void *)oldval);
+ sym_clear_all_valid();
+
+ return true;
+}
+
+const char *sym_get_string_value(struct symbol *sym)
+{
+ tristate val;
+
+ switch (sym->type) {
+ case S_BOOLEAN:
+ case S_TRISTATE:
+ val = sym_get_tristate_value(sym);
+ switch (val) {
+ case no:
+ return "n";
+ case mod:
+ return "m";
+ case yes:
+ return "y";
+ }
+ break;
+ default:
+ ;
+ }
+ return (const char *)sym->curr.val;
+}
+
+bool sym_is_changable(struct symbol *sym)
+{
+ return sym->visible > sym->rev_dep.tri;
+}
+
+struct symbol *sym_lookup(const char *name, int isconst)
+{
+ struct symbol *symbol;
+ const char *ptr;
+ char *new_name;
+ int hash = 0;
+
+ if (name) {
+ if (name[0] && !name[1]) {
+ switch (name[0]) {
+ case 'y': return &symbol_yes;
+ case 'm': return &symbol_mod;
+ case 'n': return &symbol_no;
+ }
+ }
+ for (ptr = name; *ptr; ptr++)
+ hash += *ptr;
+ hash &= 0xff;
+
+ for (symbol = symbol_hash[hash]; symbol; symbol = symbol->next) {
+ if (!strcmp(symbol->name, name)) {
+ if ((isconst && symbol->flags & SYMBOL_CONST) ||
+ (!isconst && !(symbol->flags & SYMBOL_CONST)))
+ return symbol;
+ }
+ }
+ new_name = strdup(name);
+ } else {
+ new_name = NULL;
+ hash = 256;
+ }
+
+ symbol = malloc(sizeof(*symbol));
+ memset(symbol, 0, sizeof(*symbol));
+ symbol->name = new_name;
+ symbol->type = S_UNKNOWN;
+ symbol->flags = SYMBOL_NEW;
+ if (isconst)
+ symbol->flags |= SYMBOL_CONST;
+
+ symbol->next = symbol_hash[hash];
+ symbol_hash[hash] = symbol;
+
+ return symbol;
+}
+
+struct symbol *sym_find(const char *name)
+{
+ struct symbol *symbol = NULL;
+ const char *ptr;
+ int hash = 0;
+
+ if (!name)
+ return NULL;
+
+ if (name[0] && !name[1]) {
+ switch (name[0]) {
+ case 'y': return &symbol_yes;
+ case 'm': return &symbol_mod;
+ case 'n': return &symbol_no;
+ }
+ }
+ for (ptr = name; *ptr; ptr++)
+ hash += *ptr;
+ hash &= 0xff;
+
+ for (symbol = symbol_hash[hash]; symbol; symbol = symbol->next) {
+ if (!strcmp(symbol->name, name) &&
+ !(symbol->flags & SYMBOL_CONST))
+ break;
+ }
+
+ return symbol;
+}
+
+struct symbol **sym_re_search(const char *pattern)
+{
+ struct symbol *sym, **sym_arr = NULL;
+ int i, cnt, size;
+ regex_t re;
+
+ cnt = size = 0;
+ /* Skip if empty */
+ if (strlen(pattern) == 0)
+ return NULL;
+ if (regcomp(&re, pattern, REG_EXTENDED|REG_NOSUB|REG_ICASE))
+ return NULL;
+
+ for_all_symbols(i, sym) {
+ if (sym->flags & SYMBOL_CONST || !sym->name)
+ continue;
+ if (regexec(&re, sym->name, 0, NULL, 0))
+ continue;
+ if (cnt + 1 >= size) {
+ void *tmp = sym_arr;
+ size += 16;
+ sym_arr = realloc(sym_arr, size * sizeof(struct symbol *));
+ if (!sym_arr) {
+ free(tmp);
+ return NULL;
+ }
+ }
+ sym_arr[cnt++] = sym;
+ }
+ if (sym_arr)
+ sym_arr[cnt] = NULL;
+ regfree(&re);
+
+ return sym_arr;
+}
+
+
+struct symbol *sym_check_deps(struct symbol *sym);
+
+static struct symbol *sym_check_expr_deps(struct expr *e)
+{
+ struct symbol *sym;
+
+ if (!e)
+ return NULL;
+ switch (e->type) {
+ case E_OR:
+ case E_AND:
+ sym = sym_check_expr_deps(e->left.expr);
+ if (sym)
+ return sym;
+ return sym_check_expr_deps(e->right.expr);
+ case E_NOT:
+ return sym_check_expr_deps(e->left.expr);
+ case E_EQUAL:
+ case E_UNEQUAL:
+ sym = sym_check_deps(e->left.sym);
+ if (sym)
+ return sym;
+ return sym_check_deps(e->right.sym);
+ case E_SYMBOL:
+ return sym_check_deps(e->left.sym);
+ default:
+ break;
+ }
+ printf("Oops! How to check %d?\n", e->type);
+ return NULL;
+}
+
+struct symbol *sym_check_deps(struct symbol *sym)
+{
+ struct symbol *sym2;
+ struct property *prop;
+
+ if (sym->flags & SYMBOL_CHECK) {
+ printf("Warning! Found recursive dependency: %s", sym->name);
+ return sym;
+ }
+ if (sym->flags & SYMBOL_CHECKED)
+ return NULL;
+
+ sym->flags |= (SYMBOL_CHECK | SYMBOL_CHECKED);
+ sym2 = sym_check_expr_deps(sym->rev_dep.expr);
+ if (sym2)
+ goto out;
+
+ for (prop = sym->prop; prop; prop = prop->next) {
+ if (prop->type == P_CHOICE || prop->type == P_SELECT)
+ continue;
+ sym2 = sym_check_expr_deps(prop->visible.expr);
+ if (sym2)
+ goto out;
+ if (prop->type != P_DEFAULT || sym_is_choice(sym))
+ continue;
+ sym2 = sym_check_expr_deps(prop->expr);
+ if (sym2)
+ goto out;
+ }
+out:
+ if (sym2) {
+ printf(" %s", sym->name);
+ if (sym2 == sym) {
+ printf("\n");
+ sym2 = NULL;
+ }
+ }
+ sym->flags &= ~SYMBOL_CHECK;
+ return sym2;
+}
+
+struct property *prop_alloc(enum prop_type type, struct symbol *sym)
+{
+ struct property *prop;
+ struct property **propp;
+
+ prop = malloc(sizeof(*prop));
+ memset(prop, 0, sizeof(*prop));
+ prop->type = type;
+ prop->sym = sym;
+ prop->file = current_file;
+ prop->lineno = zconf_lineno();
+
+ /* append property to the prop list of symbol */
+ if (sym) {
+ for (propp = &sym->prop; *propp; propp = &(*propp)->next)
+ ;
+ *propp = prop;
+ }
+
+ return prop;
+}
+
+struct symbol *prop_get_symbol(struct property *prop)
+{
+ if (prop->expr && (prop->expr->type == E_SYMBOL ||
+ prop->expr->type == E_CHOICE))
+ return prop->expr->left.sym;
+ return NULL;
+}
+
+const char *prop_get_type_name(enum prop_type type)
+{
+ switch (type) {
+ case P_PROMPT:
+ return "prompt";
+ case P_COMMENT:
+ return "comment";
+ case P_MENU:
+ return "menu";
+ case P_DEFAULT:
+ return "default";
+ case P_CHOICE:
+ return "choice";
+ case P_SELECT:
+ return "select";
+ case P_RANGE:
+ return "range";
+ case P_UNKNOWN:
+ break;
+ }
+ return "unknown";
+}
diff --git a/i/pc104/initrd/conf/busybox/scripts/kconfig/util.c b/i/pc104/initrd/conf/busybox/scripts/kconfig/util.c
new file mode 100644
index 0000000..aea8d56
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/scripts/kconfig/util.c
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2002-2005 Roman Zippel <zippel@linux-m68k.org>
+ * Copyright (C) 2002-2005 Sam Ravnborg <sam@ravnborg.org>
+ *
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#include <string.h>
+#include "lkc.h"
+
+/* file already present in list? If not add it */
+struct file *file_lookup(const char *name)
+{
+ struct file *file;
+
+ for (file = file_list; file; file = file->next) {
+ if (!strcmp(name, file->name))
+ return file;
+ }
+
+ file = malloc(sizeof(*file));
+ memset(file, 0, sizeof(*file));
+ file->name = strdup(name);
+ file->next = file_list;
+ file_list = file;
+ return file;
+}
+
+/* write a dependency file as used by kbuild to track dependencies */
+int file_write_dep(const char *name)
+{
+ struct file *file;
+ FILE *out;
+
+ if (!name)
+ name = ".kconfig.d";
+ out = fopen("..config.tmp", "w");
+ if (!out)
+ return 1;
+ fprintf(out, "deps_config := \\\n");
+ for (file = file_list; file; file = file->next) {
+ if (file->next)
+ fprintf(out, "\t%s \\\n", file->name);
+ else
+ fprintf(out, "\t%s\n", file->name);
+ }
+ fprintf(out, "\n.config include/autoconf.h: $(deps_config)\n\n$(deps_config):\n");
+ fclose(out);
+ rename("..config.tmp", name);
+ return 0;
+}
+
+
+/* Allocate initial growable sting */
+struct gstr str_new(void)
+{
+ struct gstr gs;
+ gs.s = malloc(sizeof(char) * 64);
+ gs.len = 16;
+ strcpy(gs.s, "\0");
+ return gs;
+}
+
+/* Allocate and assign growable string */
+struct gstr str_assign(const char *s)
+{
+ struct gstr gs;
+ gs.s = strdup(s);
+ gs.len = strlen(s) + 1;
+ return gs;
+}
+
+/* Free storage for growable string */
+void str_free(struct gstr *gs)
+{
+ if (gs->s)
+ free(gs->s);
+ gs->s = NULL;
+ gs->len = 0;
+}
+
+/* Append to growable string */
+void str_append(struct gstr *gs, const char *s)
+{
+ size_t l = strlen(gs->s) + strlen(s) + 1;
+ if (l > gs->len) {
+ gs->s = realloc(gs->s, l);
+ gs->len = l;
+ }
+ strcat(gs->s, s);
+}
+
+/* Append printf formatted string to growable string */
+void str_printf(struct gstr *gs, const char *fmt, ...)
+{
+ va_list ap;
+ char s[10000]; /* big enough... */
+ va_start(ap, fmt);
+ vsnprintf(s, sizeof(s), fmt, ap);
+ str_append(gs, s);
+ va_end(ap);
+}
+
+/* Retrieve value of growable string */
+const char *str_get(struct gstr *gs)
+{
+ return gs->s;
+}
+
diff --git a/i/pc104/initrd/conf/busybox/scripts/kconfig/zconf.gperf b/i/pc104/initrd/conf/busybox/scripts/kconfig/zconf.gperf
new file mode 100644
index 0000000..b032206
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/scripts/kconfig/zconf.gperf
@@ -0,0 +1,43 @@
+%language=ANSI-C
+%define hash-function-name kconf_id_hash
+%define lookup-function-name kconf_id_lookup
+%define string-pool-name kconf_id_strings
+%compare-strncmp
+%enum
+%pic
+%struct-type
+
+struct kconf_id;
+
+%%
+mainmenu, T_MAINMENU, TF_COMMAND
+menu, T_MENU, TF_COMMAND
+endmenu, T_ENDMENU, TF_COMMAND
+source, T_SOURCE, TF_COMMAND
+choice, T_CHOICE, TF_COMMAND
+endchoice, T_ENDCHOICE, TF_COMMAND
+comment, T_COMMENT, TF_COMMAND
+config, T_CONFIG, TF_COMMAND
+menuconfig, T_MENUCONFIG, TF_COMMAND
+help, T_HELP, TF_COMMAND
+if, T_IF, TF_COMMAND|TF_PARAM
+endif, T_ENDIF, TF_COMMAND
+depends, T_DEPENDS, TF_COMMAND
+requires, T_REQUIRES, TF_COMMAND
+optional, T_OPTIONAL, TF_COMMAND
+default, T_DEFAULT, TF_COMMAND, S_UNKNOWN
+prompt, T_PROMPT, TF_COMMAND
+tristate, T_TYPE, TF_COMMAND, S_TRISTATE
+def_tristate, T_DEFAULT, TF_COMMAND, S_TRISTATE
+bool, T_TYPE, TF_COMMAND, S_BOOLEAN
+boolean, T_TYPE, TF_COMMAND, S_BOOLEAN
+def_bool, T_DEFAULT, TF_COMMAND, S_BOOLEAN
+def_boolean, T_DEFAULT, TF_COMMAND, S_BOOLEAN
+int, T_TYPE, TF_COMMAND, S_INT
+hex, T_TYPE, TF_COMMAND, S_HEX
+string, T_TYPE, TF_COMMAND, S_STRING
+select, T_SELECT, TF_COMMAND
+enable, T_SELECT, TF_COMMAND
+range, T_RANGE, TF_COMMAND
+on, T_ON, TF_PARAM
+%%
diff --git a/i/pc104/initrd/conf/busybox/scripts/kconfig/zconf.hash.c_shipped b/i/pc104/initrd/conf/busybox/scripts/kconfig/zconf.hash.c_shipped
new file mode 100644
index 0000000..345f0fc
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/scripts/kconfig/zconf.hash.c_shipped
@@ -0,0 +1,231 @@
+/* ANSI-C code produced by gperf version 3.0.1 */
+/* Command-line: gperf */
+/* Computed positions: -k'1,3' */
+
+#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \
+ && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \
+ && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \
+ && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \
+ && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \
+ && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \
+ && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \
+ && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \
+ && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \
+ && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \
+ && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \
+ && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \
+ && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \
+ && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \
+ && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \
+ && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \
+ && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \
+ && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \
+ && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \
+ && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \
+ && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \
+ && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \
+ && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126))
+/* The character set is not based on ISO-646. */
+#error "gperf generated tables don't work with this execution character set. Please report a bug to <bug-gnu-gperf@gnu.org>."
+#endif
+
+struct kconf_id;
+/* maximum key range = 45, duplicates = 0 */
+
+#ifdef __GNUC__
+__inline
+#else
+#ifdef __cplusplus
+inline
+#endif
+#endif
+static unsigned int
+kconf_id_hash (register const char *str, register unsigned int len)
+{
+ static unsigned char asso_values[] =
+ {
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 25, 10, 15,
+ 0, 0, 5, 47, 0, 0, 47, 47, 0, 10,
+ 0, 20, 20, 20, 5, 0, 0, 20, 47, 47,
+ 20, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47
+ };
+ register int hval = len;
+
+ switch (hval)
+ {
+ default:
+ hval += asso_values[(unsigned char)str[2]];
+ /*FALLTHROUGH*/
+ case 2:
+ case 1:
+ hval += asso_values[(unsigned char)str[0]];
+ break;
+ }
+ return hval;
+}
+
+struct kconf_id_strings_t
+ {
+ char kconf_id_strings_str2[sizeof("if")];
+ char kconf_id_strings_str3[sizeof("int")];
+ char kconf_id_strings_str4[sizeof("help")];
+ char kconf_id_strings_str5[sizeof("endif")];
+ char kconf_id_strings_str6[sizeof("select")];
+ char kconf_id_strings_str7[sizeof("endmenu")];
+ char kconf_id_strings_str8[sizeof("tristate")];
+ char kconf_id_strings_str9[sizeof("endchoice")];
+ char kconf_id_strings_str10[sizeof("range")];
+ char kconf_id_strings_str11[sizeof("string")];
+ char kconf_id_strings_str12[sizeof("default")];
+ char kconf_id_strings_str13[sizeof("def_bool")];
+ char kconf_id_strings_str14[sizeof("menu")];
+ char kconf_id_strings_str16[sizeof("def_boolean")];
+ char kconf_id_strings_str17[sizeof("def_tristate")];
+ char kconf_id_strings_str18[sizeof("mainmenu")];
+ char kconf_id_strings_str20[sizeof("menuconfig")];
+ char kconf_id_strings_str21[sizeof("config")];
+ char kconf_id_strings_str22[sizeof("on")];
+ char kconf_id_strings_str23[sizeof("hex")];
+ char kconf_id_strings_str26[sizeof("source")];
+ char kconf_id_strings_str27[sizeof("depends")];
+ char kconf_id_strings_str28[sizeof("optional")];
+ char kconf_id_strings_str31[sizeof("enable")];
+ char kconf_id_strings_str32[sizeof("comment")];
+ char kconf_id_strings_str33[sizeof("requires")];
+ char kconf_id_strings_str34[sizeof("bool")];
+ char kconf_id_strings_str37[sizeof("boolean")];
+ char kconf_id_strings_str41[sizeof("choice")];
+ char kconf_id_strings_str46[sizeof("prompt")];
+ };
+static struct kconf_id_strings_t kconf_id_strings_contents =
+ {
+ "if",
+ "int",
+ "help",
+ "endif",
+ "select",
+ "endmenu",
+ "tristate",
+ "endchoice",
+ "range",
+ "string",
+ "default",
+ "def_bool",
+ "menu",
+ "def_boolean",
+ "def_tristate",
+ "mainmenu",
+ "menuconfig",
+ "config",
+ "on",
+ "hex",
+ "source",
+ "depends",
+ "optional",
+ "enable",
+ "comment",
+ "requires",
+ "bool",
+ "boolean",
+ "choice",
+ "prompt"
+ };
+#define kconf_id_strings ((const char *) &kconf_id_strings_contents)
+#ifdef __GNUC__
+__inline
+#endif
+struct kconf_id *
+kconf_id_lookup (register const char *str, register unsigned int len)
+{
+ enum
+ {
+ TOTAL_KEYWORDS = 30,
+ MIN_WORD_LENGTH = 2,
+ MAX_WORD_LENGTH = 12,
+ MIN_HASH_VALUE = 2,
+ MAX_HASH_VALUE = 46
+ };
+
+ static struct kconf_id wordlist[] =
+ {
+ {-1}, {-1},
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str2, T_IF, TF_COMMAND|TF_PARAM},
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str3, T_TYPE, TF_COMMAND, S_INT},
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str4, T_HELP, TF_COMMAND},
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str5, T_ENDIF, TF_COMMAND},
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str6, T_SELECT, TF_COMMAND},
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str7, T_ENDMENU, TF_COMMAND},
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str8, T_TYPE, TF_COMMAND, S_TRISTATE},
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str9, T_ENDCHOICE, TF_COMMAND},
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str10, T_RANGE, TF_COMMAND},
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str11, T_TYPE, TF_COMMAND, S_STRING},
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str12, T_DEFAULT, TF_COMMAND, S_UNKNOWN},
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str13, T_DEFAULT, TF_COMMAND, S_BOOLEAN},
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str14, T_MENU, TF_COMMAND},
+ {-1},
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str16, T_DEFAULT, TF_COMMAND, S_BOOLEAN},
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str17, T_DEFAULT, TF_COMMAND, S_TRISTATE},
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str18, T_MAINMENU, TF_COMMAND},
+ {-1},
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str20, T_MENUCONFIG, TF_COMMAND},
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str21, T_CONFIG, TF_COMMAND},
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str22, T_ON, TF_PARAM},
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str23, T_TYPE, TF_COMMAND, S_HEX},
+ {-1}, {-1},
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str26, T_SOURCE, TF_COMMAND},
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str27, T_DEPENDS, TF_COMMAND},
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str28, T_OPTIONAL, TF_COMMAND},
+ {-1}, {-1},
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str31, T_SELECT, TF_COMMAND},
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str32, T_COMMENT, TF_COMMAND},
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str33, T_REQUIRES, TF_COMMAND},
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str34, T_TYPE, TF_COMMAND, S_BOOLEAN},
+ {-1}, {-1},
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str37, T_TYPE, TF_COMMAND, S_BOOLEAN},
+ {-1}, {-1}, {-1},
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str41, T_CHOICE, TF_COMMAND},
+ {-1}, {-1}, {-1}, {-1},
+ {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str46, T_PROMPT, TF_COMMAND}
+ };
+
+ if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
+ {
+ register int key = kconf_id_hash (str, len);
+
+ if (key <= MAX_HASH_VALUE && key >= 0)
+ {
+ register int o = wordlist[key].name;
+ if (o >= 0)
+ {
+ register const char *s = o + kconf_id_strings;
+
+ if (*str == *s && !strncmp (str + 1, s + 1, len - 1) && s[len] == '\0')
+ return &wordlist[key];
+ }
+ }
+ }
+ return 0;
+}
+
diff --git a/i/pc104/initrd/conf/busybox/scripts/kconfig/zconf.l b/i/pc104/initrd/conf/busybox/scripts/kconfig/zconf.l
new file mode 100644
index 0000000..cfa4607
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/scripts/kconfig/zconf.l
@@ -0,0 +1,350 @@
+%option backup nostdinit noyywrap never-interactive full ecs
+%option 8bit backup nodefault perf-report perf-report
+%x COMMAND HELP STRING PARAM
+%{
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define LKC_DIRECT_LINK
+#include "lkc.h"
+
+#define START_STRSIZE 16
+
+static struct {
+ struct file *file;
+ int lineno;
+} current_pos;
+
+static char *text;
+static int text_size, text_asize;
+
+struct buffer {
+ struct buffer *parent;
+ YY_BUFFER_STATE state;
+};
+
+struct buffer *current_buf;
+
+static int last_ts, first_ts;
+
+static void zconf_endhelp(void);
+static void zconf_endfile(void);
+
+void new_string(void)
+{
+ text = malloc(START_STRSIZE);
+ text_asize = START_STRSIZE;
+ text_size = 0;
+ *text = 0;
+}
+
+void append_string(const char *str, int size)
+{
+ int new_size = text_size + size + 1;
+ if (new_size > text_asize) {
+ new_size += START_STRSIZE - 1;
+ new_size &= -START_STRSIZE;
+ text = realloc(text, new_size);
+ text_asize = new_size;
+ }
+ memcpy(text + text_size, str, size);
+ text_size += size;
+ text[text_size] = 0;
+}
+
+void alloc_string(const char *str, int size)
+{
+ text = malloc(size + 1);
+ memcpy(text, str, size);
+ text[size] = 0;
+}
+%}
+
+ws [ \n\t]
+n [A-Za-z0-9_]
+
+%%
+ int str = 0;
+ int ts, i;
+
+[ \t]*#.*\n |
+[ \t]*\n {
+ current_file->lineno++;
+ return T_EOL;
+}
+[ \t]*#.*
+
+
+[ \t]+ {
+ BEGIN(COMMAND);
+}
+
+. {
+ unput(yytext[0]);
+ BEGIN(COMMAND);
+}
+
+
+<COMMAND>{
+ {n}+ {
+ struct kconf_id *id = kconf_id_lookup(yytext, yyleng);
+ BEGIN(PARAM);
+ current_pos.file = current_file;
+ current_pos.lineno = current_file->lineno;
+ if (id && id->flags & TF_COMMAND) {
+ zconflval.id = id;
+ return id->token;
+ }
+ alloc_string(yytext, yyleng);
+ zconflval.string = text;
+ return T_WORD;
+ }
+ .
+ \n {
+ BEGIN(INITIAL);
+ current_file->lineno++;
+ return T_EOL;
+ }
+}
+
+<PARAM>{
+ "&&" return T_AND;
+ "||" return T_OR;
+ "(" return T_OPEN_PAREN;
+ ")" return T_CLOSE_PAREN;
+ "!" return T_NOT;
+ "=" return T_EQUAL;
+ "!=" return T_UNEQUAL;
+ \"|\' {
+ str = yytext[0];
+ new_string();
+ BEGIN(STRING);
+ }
+ \n BEGIN(INITIAL); current_file->lineno++; return T_EOL;
+ --- /* ignore */
+ ({n}|[-/.])+ {
+ struct kconf_id *id = kconf_id_lookup(yytext, yyleng);
+ if (id && id->flags & TF_PARAM) {
+ zconflval.id = id;
+ return id->token;
+ }
+ alloc_string(yytext, yyleng);
+ zconflval.string = text;
+ return T_WORD;
+ }
+ #.* /* comment */
+ \\\n current_file->lineno++;
+ .
+ <<EOF>> {
+ BEGIN(INITIAL);
+ }
+}
+
+<STRING>{
+ [^'"\\\n]+/\n {
+ append_string(yytext, yyleng);
+ zconflval.string = text;
+ return T_WORD_QUOTE;
+ }
+ [^'"\\\n]+ {
+ append_string(yytext, yyleng);
+ }
+ \\.?/\n {
+ append_string(yytext + 1, yyleng - 1);
+ zconflval.string = text;
+ return T_WORD_QUOTE;
+ }
+ \\.? {
+ append_string(yytext + 1, yyleng - 1);
+ }
+ \'|\" {
+ if (str == yytext[0]) {
+ BEGIN(PARAM);
+ zconflval.string = text;
+ return T_WORD_QUOTE;
+ } else
+ append_string(yytext, 1);
+ }
+ \n {
+ printf("%s:%d:warning: multi-line strings not supported\n", zconf_curname(), zconf_lineno());
+ current_file->lineno++;
+ BEGIN(INITIAL);
+ return T_EOL;
+ }
+ <<EOF>> {
+ BEGIN(INITIAL);
+ }
+}
+
+<HELP>{
+ [ \t]+ {
+ ts = 0;
+ for (i = 0; i < yyleng; i++) {
+ if (yytext[i] == '\t')
+ ts = (ts & ~7) + 8;
+ else
+ ts++;
+ }
+ last_ts = ts;
+ if (first_ts) {
+ if (ts < first_ts) {
+ zconf_endhelp();
+ return T_HELPTEXT;
+ }
+ ts -= first_ts;
+ while (ts > 8) {
+ append_string(" ", 8);
+ ts -= 8;
+ }
+ append_string(" ", ts);
+ }
+ }
+ [ \t]*\n/[^ \t\n] {
+ current_file->lineno++;
+ zconf_endhelp();
+ return T_HELPTEXT;
+ }
+ [ \t]*\n {
+ current_file->lineno++;
+ append_string("\n", 1);
+ }
+ [^ \t\n].* {
+ append_string(yytext, yyleng);
+ if (!first_ts)
+ first_ts = last_ts;
+ }
+ <<EOF>> {
+ zconf_endhelp();
+ return T_HELPTEXT;
+ }
+}
+
+<<EOF>> {
+ if (current_file) {
+ zconf_endfile();
+ return T_EOL;
+ }
+ fclose(yyin);
+ yyterminate();
+}
+
+%%
+void zconf_starthelp(void)
+{
+ new_string();
+ last_ts = first_ts = 0;
+ BEGIN(HELP);
+}
+
+static void zconf_endhelp(void)
+{
+ zconflval.string = text;
+ BEGIN(INITIAL);
+}
+
+
+/*
+ * Try to open specified file with following names:
+ * ./name
+ * $(srctree)/name
+ * The latter is used when srctree is separate from objtree
+ * when compiling the kernel.
+ * Return NULL if file is not found.
+ */
+FILE *zconf_fopen(const char *name)
+{
+ char *env, fullname[PATH_MAX+1];
+ FILE *f;
+
+ f = fopen(name, "r");
+ if (!f && name[0] != '/') {
+ env = getenv(SRCTREE);
+ if (env) {
+ sprintf(fullname, "%s/%s", env, name);
+ f = fopen(fullname, "r");
+ }
+ }
+ return f;
+}
+
+void zconf_initscan(const char *name)
+{
+ yyin = zconf_fopen(name);
+ if (!yyin) {
+ printf("can't find file %s\n", name);
+ exit(1);
+ }
+
+ current_buf = malloc(sizeof(*current_buf));
+ memset(current_buf, 0, sizeof(*current_buf));
+
+ current_file = file_lookup(name);
+ current_file->lineno = 1;
+ current_file->flags = FILE_BUSY;
+}
+
+void zconf_nextfile(const char *name)
+{
+ struct file *file = file_lookup(name);
+ struct buffer *buf = malloc(sizeof(*buf));
+ memset(buf, 0, sizeof(*buf));
+
+ current_buf->state = YY_CURRENT_BUFFER;
+ yyin = zconf_fopen(name);
+ if (!yyin) {
+ printf("%s:%d: can't open file \"%s\"\n", zconf_curname(), zconf_lineno(), name);
+ exit(1);
+ }
+ yy_switch_to_buffer(yy_create_buffer(yyin, YY_BUF_SIZE));
+ buf->parent = current_buf;
+ current_buf = buf;
+
+ if (file->flags & FILE_BUSY) {
+ printf("recursive scan (%s)?\n", name);
+ exit(1);
+ }
+ if (file->flags & FILE_SCANNED) {
+ printf("file %s already scanned?\n", name);
+ exit(1);
+ }
+ file->flags |= FILE_BUSY;
+ file->lineno = 1;
+ file->parent = current_file;
+ current_file = file;
+}
+
+static void zconf_endfile(void)
+{
+ struct buffer *parent;
+
+ current_file->flags |= FILE_SCANNED;
+ current_file->flags &= ~FILE_BUSY;
+ current_file = current_file->parent;
+
+ parent = current_buf->parent;
+ if (parent) {
+ fclose(yyin);
+ yy_delete_buffer(YY_CURRENT_BUFFER);
+ yy_switch_to_buffer(parent->state);
+ }
+ free(current_buf);
+ current_buf = parent;
+}
+
+int zconf_lineno(void)
+{
+ return current_pos.lineno;
+}
+
+char *zconf_curname(void)
+{
+ return current_pos.file ? current_pos.file->name : "<none>";
+}
diff --git a/i/pc104/initrd/conf/busybox/scripts/kconfig/zconf.tab.c_shipped b/i/pc104/initrd/conf/busybox/scripts/kconfig/zconf.tab.c_shipped
new file mode 100644
index 0000000..ca8003d
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/scripts/kconfig/zconf.tab.c_shipped
@@ -0,0 +1,2173 @@
+/* A Bison parser, made by GNU Bison 2.0. */
+
+/* Skeleton parser for Yacc-like parsing with Bison,
+ Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+/* As a special exception, when this file is copied by Bison into a
+ Bison output file, you may use that output file without restriction.
+ This special exception was added by the Free Software Foundation
+ in version 1.24 of Bison. */
+
+/* Written by Richard Stallman by simplifying the original so called
+ ``semantic'' parser. */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+ infringing on user name space. This should be done even for local
+ variables, as they might otherwise be expanded by user macros.
+ There are some unavoidable exceptions within include files to
+ define necessary library symbols; they are noted "INFRINGES ON
+ USER NAME SPACE" below. */
+
+/* Identify Bison output. */
+#define YYBISON 1
+
+/* Skeleton name. */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers. */
+#define YYPURE 0
+
+/* Using locations. */
+#define YYLSP_NEEDED 0
+
+/* Substitute the variable and function names. */
+#define yyparse zconfparse
+#define yylex zconflex
+#define yyerror zconferror
+#define yylval zconflval
+#define yychar zconfchar
+#define yydebug zconfdebug
+#define yynerrs zconfnerrs
+
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ T_MAINMENU = 258,
+ T_MENU = 259,
+ T_ENDMENU = 260,
+ T_SOURCE = 261,
+ T_CHOICE = 262,
+ T_ENDCHOICE = 263,
+ T_COMMENT = 264,
+ T_CONFIG = 265,
+ T_MENUCONFIG = 266,
+ T_HELP = 267,
+ T_HELPTEXT = 268,
+ T_IF = 269,
+ T_ENDIF = 270,
+ T_DEPENDS = 271,
+ T_REQUIRES = 272,
+ T_OPTIONAL = 273,
+ T_PROMPT = 274,
+ T_TYPE = 275,
+ T_DEFAULT = 276,
+ T_SELECT = 277,
+ T_RANGE = 278,
+ T_ON = 279,
+ T_WORD = 280,
+ T_WORD_QUOTE = 281,
+ T_UNEQUAL = 282,
+ T_CLOSE_PAREN = 283,
+ T_OPEN_PAREN = 284,
+ T_EOL = 285,
+ T_OR = 286,
+ T_AND = 287,
+ T_EQUAL = 288,
+ T_NOT = 289
+ };
+#endif
+#define T_MAINMENU 258
+#define T_MENU 259
+#define T_ENDMENU 260
+#define T_SOURCE 261
+#define T_CHOICE 262
+#define T_ENDCHOICE 263
+#define T_COMMENT 264
+#define T_CONFIG 265
+#define T_MENUCONFIG 266
+#define T_HELP 267
+#define T_HELPTEXT 268
+#define T_IF 269
+#define T_ENDIF 270
+#define T_DEPENDS 271
+#define T_REQUIRES 272
+#define T_OPTIONAL 273
+#define T_PROMPT 274
+#define T_TYPE 275
+#define T_DEFAULT 276
+#define T_SELECT 277
+#define T_RANGE 278
+#define T_ON 279
+#define T_WORD 280
+#define T_WORD_QUOTE 281
+#define T_UNEQUAL 282
+#define T_CLOSE_PAREN 283
+#define T_OPEN_PAREN 284
+#define T_EOL 285
+#define T_OR 286
+#define T_AND 287
+#define T_EQUAL 288
+#define T_NOT 289
+
+
+
+
+/* Copy the first part of user declarations. */
+
+
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+
+#define LKC_DIRECT_LINK
+#include "lkc.h"
+
+#include "zconf.hash.c"
+
+#define printd(mask, fmt...) if (cdebug & (mask)) printf(fmt)
+
+#define PRINTD 0x0001
+#define DEBUG_PARSE 0x0002
+
+int cdebug = PRINTD;
+
+extern int zconflex(void);
+static void zconfprint(const char *err, ...);
+static void zconf_error(const char *err, ...);
+static void zconferror(const char *err);
+static bool zconf_endtoken(struct kconf_id *id, int starttoken, int endtoken);
+
+struct symbol *symbol_hash[257];
+
+static struct menu *current_menu, *current_entry;
+
+#define YYDEBUG 0
+#if YYDEBUG
+#define YYERROR_VERBOSE
+#endif
+
+
+/* Enabling traces. */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+
+/* Enabling verbose error messages. */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 0
+#endif
+
+#if ! defined (YYSTYPE) && ! defined (YYSTYPE_IS_DECLARED)
+
+typedef union YYSTYPE {
+ char *string;
+ struct file *file;
+ struct symbol *symbol;
+ struct expr *expr;
+ struct menu *menu;
+ struct kconf_id *id;
+} YYSTYPE;
+/* Line 190 of yacc.c. */
+
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+
+
+/* Copy the second part of user declarations. */
+
+
+/* Line 213 of yacc.c. */
+
+
+#if ! defined (yyoverflow) || YYERROR_VERBOSE
+
+# ifndef YYFREE
+# define YYFREE free
+# endif
+# ifndef YYMALLOC
+# define YYMALLOC malloc
+# endif
+
+/* The parser invokes alloca or malloc; define the necessary symbols. */
+
+# ifdef YYSTACK_USE_ALLOCA
+# if YYSTACK_USE_ALLOCA
+# ifdef __GNUC__
+# define YYSTACK_ALLOC __builtin_alloca
+# else
+# define YYSTACK_ALLOC alloca
+# endif
+# endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+ /* Pacify GCC's `empty if-body' warning. */
+# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0)
+# else
+# if defined (__STDC__) || defined (__cplusplus)
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+# define YYSIZE_T size_t
+# endif
+# define YYSTACK_ALLOC YYMALLOC
+# define YYSTACK_FREE YYFREE
+# endif
+#endif /* ! defined (yyoverflow) || YYERROR_VERBOSE */
+
+
+#if (! defined (yyoverflow) \
+ && (! defined (__cplusplus) \
+ || (defined (YYSTYPE_IS_TRIVIAL) && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member. */
+union yyalloc
+{
+ short int yyss;
+ YYSTYPE yyvs;
+ };
+
+/* The size of the maximum gap between one aligned stack and the next. */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+ N elements. */
+# define YYSTACK_BYTES(N) \
+ ((N) * (sizeof (short int) + sizeof (YYSTYPE)) \
+ + YYSTACK_GAP_MAXIMUM)
+
+/* Copy COUNT objects from FROM to TO. The source and destination do
+ not overlap. */
+# ifndef YYCOPY
+# if defined (__GNUC__) && 1 < __GNUC__
+# define YYCOPY(To, From, Count) \
+ __builtin_memcpy (To, From, (Count) * sizeof (*(From)))
+# else
+# define YYCOPY(To, From, Count) \
+ do \
+ { \
+ register YYSIZE_T yyi; \
+ for (yyi = 0; yyi < (Count); yyi++) \
+ (To)[yyi] = (From)[yyi]; \
+ } \
+ while (0)
+# endif
+# endif
+
+/* Relocate STACK from its old location to the new one. The
+ local variables YYSIZE and YYSTACKSIZE give the old and new number of
+ elements in the stack, and YYPTR gives the new location of the
+ stack. Advance YYPTR to a properly aligned location for the next
+ stack. */
+# define YYSTACK_RELOCATE(Stack) \
+ do \
+ { \
+ YYSIZE_T yynewbytes; \
+ YYCOPY (&yyptr->Stack, Stack, yysize); \
+ Stack = &yyptr->Stack; \
+ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+ yyptr += yynewbytes / sizeof (*yyptr); \
+ } \
+ while (0)
+
+#endif
+
+#if defined (__STDC__) || defined (__cplusplus)
+ typedef signed char yysigned_char;
+#else
+ typedef short int yysigned_char;
+#endif
+
+/* YYFINAL -- State number of the termination state. */
+#define YYFINAL 3
+/* YYLAST -- Last index in YYTABLE. */
+#define YYLAST 264
+
+/* YYNTOKENS -- Number of terminals. */
+#define YYNTOKENS 35
+/* YYNNTS -- Number of nonterminals. */
+#define YYNNTS 42
+/* YYNRULES -- Number of rules. */
+#define YYNRULES 104
+/* YYNRULES -- Number of states. */
+#define YYNSTATES 175
+
+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */
+#define YYUNDEFTOK 2
+#define YYMAXUTOK 289
+
+#define YYTRANSLATE(YYX) \
+ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */
+static const unsigned char yytranslate[] =
+{
+ 0, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 26, 27, 28, 29, 30, 31, 32, 33, 34
+};
+
+#if YYDEBUG
+/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
+ YYRHS. */
+static const unsigned short int yyprhs[] =
+{
+ 0, 0, 3, 5, 6, 9, 12, 15, 20, 23,
+ 28, 33, 37, 39, 41, 43, 45, 47, 49, 51,
+ 53, 55, 57, 59, 61, 63, 67, 70, 74, 77,
+ 81, 84, 85, 88, 91, 94, 97, 100, 104, 109,
+ 114, 119, 125, 128, 131, 133, 137, 138, 141, 144,
+ 147, 150, 153, 158, 162, 165, 170, 171, 174, 178,
+ 180, 184, 185, 188, 191, 194, 198, 201, 203, 207,
+ 208, 211, 214, 217, 221, 225, 228, 231, 234, 235,
+ 238, 241, 244, 249, 253, 257, 258, 261, 263, 265,
+ 268, 271, 274, 276, 279, 280, 283, 285, 289, 293,
+ 297, 300, 304, 308, 310
+};
+
+/* YYRHS -- A `-1'-separated list of the rules' RHS. */
+static const yysigned_char yyrhs[] =
+{
+ 36, 0, -1, 37, -1, -1, 37, 39, -1, 37,
+ 50, -1, 37, 61, -1, 37, 3, 71, 73, -1,
+ 37, 72, -1, 37, 25, 1, 30, -1, 37, 38,
+ 1, 30, -1, 37, 1, 30, -1, 16, -1, 19,
+ -1, 20, -1, 22, -1, 18, -1, 23, -1, 21,
+ -1, 30, -1, 56, -1, 65, -1, 42, -1, 44,
+ -1, 63, -1, 25, 1, 30, -1, 1, 30, -1,
+ 10, 25, 30, -1, 41, 45, -1, 11, 25, 30,
+ -1, 43, 45, -1, -1, 45, 46, -1, 45, 69,
+ -1, 45, 67, -1, 45, 40, -1, 45, 30, -1,
+ 20, 70, 30, -1, 19, 71, 74, 30, -1, 21,
+ 75, 74, 30, -1, 22, 25, 74, 30, -1, 23,
+ 76, 76, 74, 30, -1, 7, 30, -1, 47, 51,
+ -1, 72, -1, 48, 53, 49, -1, -1, 51, 52,
+ -1, 51, 69, -1, 51, 67, -1, 51, 30, -1,
+ 51, 40, -1, 19, 71, 74, 30, -1, 20, 70,
+ 30, -1, 18, 30, -1, 21, 25, 74, 30, -1,
+ -1, 53, 39, -1, 14, 75, 73, -1, 72, -1,
+ 54, 57, 55, -1, -1, 57, 39, -1, 57, 61,
+ -1, 57, 50, -1, 4, 71, 30, -1, 58, 68,
+ -1, 72, -1, 59, 62, 60, -1, -1, 62, 39,
+ -1, 62, 61, -1, 62, 50, -1, 6, 71, 30,
+ -1, 9, 71, 30, -1, 64, 68, -1, 12, 30,
+ -1, 66, 13, -1, -1, 68, 69, -1, 68, 30,
+ -1, 68, 40, -1, 16, 24, 75, 30, -1, 16,
+ 75, 30, -1, 17, 75, 30, -1, -1, 71, 74,
+ -1, 25, -1, 26, -1, 5, 30, -1, 8, 30,
+ -1, 15, 30, -1, 30, -1, 73, 30, -1, -1,
+ 14, 75, -1, 76, -1, 76, 33, 76, -1, 76,
+ 27, 76, -1, 29, 75, 28, -1, 34, 75, -1,
+ 75, 31, 75, -1, 75, 32, 75, -1, 25, -1,
+ 26, -1
+};
+
+/* YYRLINE[YYN] -- source line where rule number YYN was defined. */
+static const unsigned short int yyrline[] =
+{
+ 0, 103, 103, 105, 107, 108, 109, 110, 111, 112,
+ 113, 117, 121, 121, 121, 121, 121, 121, 121, 125,
+ 126, 127, 128, 129, 130, 134, 135, 141, 149, 155,
+ 163, 173, 175, 176, 177, 178, 179, 182, 190, 196,
+ 206, 212, 220, 229, 234, 242, 245, 247, 248, 249,
+ 250, 251, 254, 260, 271, 277, 287, 289, 294, 302,
+ 310, 313, 315, 316, 317, 322, 329, 334, 342, 345,
+ 347, 348, 349, 352, 360, 367, 374, 380, 387, 389,
+ 390, 391, 394, 399, 404, 412, 414, 419, 420, 423,
+ 424, 425, 429, 430, 433, 434, 437, 438, 439, 440,
+ 441, 442, 443, 446, 447
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE
+/* YYTNME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+ First, the terminals, then, starting at YYNTOKENS, nonterminals. */
+static const char *const yytname[] =
+{
+ "$end", "error", "$undefined", "T_MAINMENU", "T_MENU", "T_ENDMENU",
+ "T_SOURCE", "T_CHOICE", "T_ENDCHOICE", "T_COMMENT", "T_CONFIG",
+ "T_MENUCONFIG", "T_HELP", "T_HELPTEXT", "T_IF", "T_ENDIF", "T_DEPENDS",
+ "T_REQUIRES", "T_OPTIONAL", "T_PROMPT", "T_TYPE", "T_DEFAULT",
+ "T_SELECT", "T_RANGE", "T_ON", "T_WORD", "T_WORD_QUOTE", "T_UNEQUAL",
+ "T_CLOSE_PAREN", "T_OPEN_PAREN", "T_EOL", "T_OR", "T_AND", "T_EQUAL",
+ "T_NOT", "$accept", "input", "stmt_list", "option_name", "common_stmt",
+ "option_error", "config_entry_start", "config_stmt",
+ "menuconfig_entry_start", "menuconfig_stmt", "config_option_list",
+ "config_option", "choice", "choice_entry", "choice_end", "choice_stmt",
+ "choice_option_list", "choice_option", "choice_block", "if_entry",
+ "if_end", "if_stmt", "if_block", "menu", "menu_entry", "menu_end",
+ "menu_stmt", "menu_block", "source_stmt", "comment", "comment_stmt",
+ "help_start", "help", "depends_list", "depends", "prompt_stmt_opt",
+ "prompt", "end", "nl", "if_expr", "expr", "symbol", 0
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
+ token YYLEX-NUM. */
+static const unsigned short int yytoknum[] =
+{
+ 0, 256, 257, 258, 259, 260, 261, 262, 263, 264,
+ 265, 266, 267, 268, 269, 270, 271, 272, 273, 274,
+ 275, 276, 277, 278, 279, 280, 281, 282, 283, 284,
+ 285, 286, 287, 288, 289
+};
+# endif
+
+/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */
+static const unsigned char yyr1[] =
+{
+ 0, 35, 36, 37, 37, 37, 37, 37, 37, 37,
+ 37, 37, 38, 38, 38, 38, 38, 38, 38, 39,
+ 39, 39, 39, 39, 39, 40, 40, 41, 42, 43,
+ 44, 45, 45, 45, 45, 45, 45, 46, 46, 46,
+ 46, 46, 47, 48, 49, 50, 51, 51, 51, 51,
+ 51, 51, 52, 52, 52, 52, 53, 53, 54, 55,
+ 56, 57, 57, 57, 57, 58, 59, 60, 61, 62,
+ 62, 62, 62, 63, 64, 65, 66, 67, 68, 68,
+ 68, 68, 69, 69, 69, 70, 70, 71, 71, 72,
+ 72, 72, 73, 73, 74, 74, 75, 75, 75, 75,
+ 75, 75, 75, 76, 76
+};
+
+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */
+static const unsigned char yyr2[] =
+{
+ 0, 2, 1, 0, 2, 2, 2, 4, 2, 4,
+ 4, 3, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 3, 2, 3, 2, 3,
+ 2, 0, 2, 2, 2, 2, 2, 3, 4, 4,
+ 4, 5, 2, 2, 1, 3, 0, 2, 2, 2,
+ 2, 2, 4, 3, 2, 4, 0, 2, 3, 1,
+ 3, 0, 2, 2, 2, 3, 2, 1, 3, 0,
+ 2, 2, 2, 3, 3, 2, 2, 2, 0, 2,
+ 2, 2, 4, 3, 3, 0, 2, 1, 1, 2,
+ 2, 2, 1, 2, 0, 2, 1, 3, 3, 3,
+ 2, 3, 3, 1, 1
+};
+
+/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state
+ STATE-NUM when YYTABLE doesn't specify something else to do. Zero
+ means the default is an error. */
+static const unsigned char yydefact[] =
+{
+ 3, 0, 0, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 12, 16, 13, 14,
+ 18, 15, 17, 0, 19, 0, 4, 31, 22, 31,
+ 23, 46, 56, 5, 61, 20, 78, 69, 6, 24,
+ 78, 21, 8, 11, 87, 88, 0, 0, 89, 0,
+ 42, 90, 0, 0, 0, 103, 104, 0, 0, 0,
+ 96, 91, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 92, 7, 65, 73, 74, 27, 29, 0,
+ 100, 0, 0, 58, 0, 0, 9, 10, 0, 0,
+ 0, 0, 0, 85, 0, 0, 0, 0, 36, 35,
+ 32, 0, 34, 33, 0, 0, 85, 0, 50, 51,
+ 47, 49, 48, 57, 45, 44, 62, 64, 60, 63,
+ 59, 80, 81, 79, 70, 72, 68, 71, 67, 93,
+ 99, 101, 102, 98, 97, 26, 76, 0, 0, 0,
+ 94, 0, 94, 94, 94, 0, 0, 77, 54, 94,
+ 0, 94, 0, 83, 84, 0, 0, 37, 86, 0,
+ 0, 94, 25, 0, 53, 0, 82, 95, 38, 39,
+ 40, 0, 52, 55, 41
+};
+
+/* YYDEFGOTO[NTERM-NUM]. */
+static const short int yydefgoto[] =
+{
+ -1, 1, 2, 25, 26, 99, 27, 28, 29, 30,
+ 64, 100, 31, 32, 114, 33, 66, 110, 67, 34,
+ 118, 35, 68, 36, 37, 126, 38, 70, 39, 40,
+ 41, 101, 102, 69, 103, 141, 142, 42, 73, 156,
+ 59, 60
+};
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+ STATE-NUM. */
+#define YYPACT_NINF -78
+static const short int yypact[] =
+{
+ -78, 2, 159, -78, -21, 0, 0, -12, 0, 1,
+ 4, 0, 27, 38, 60, 58, -78, -78, -78, -78,
+ -78, -78, -78, 100, -78, 104, -78, -78, -78, -78,
+ -78, -78, -78, -78, -78, -78, -78, -78, -78, -78,
+ -78, -78, -78, -78, -78, -78, 86, 113, -78, 114,
+ -78, -78, 125, 127, 128, -78, -78, 60, 60, 210,
+ 65, -78, 141, 142, 39, 103, 182, 200, 6, 66,
+ 6, 131, -78, 146, -78, -78, -78, -78, -78, 196,
+ -78, 60, 60, 146, 40, 40, -78, -78, 155, 156,
+ -2, 60, 0, 0, 60, 105, 40, 194, -78, -78,
+ -78, 206, -78, -78, 183, 0, 0, 195, -78, -78,
+ -78, -78, -78, -78, -78, -78, -78, -78, -78, -78,
+ -78, -78, -78, -78, -78, -78, -78, -78, -78, -78,
+ -78, 197, -78, -78, -78, -78, -78, 60, 213, 216,
+ 212, 203, 212, 190, 212, 40, 208, -78, -78, 212,
+ 222, 212, 219, -78, -78, 60, 223, -78, -78, 224,
+ 225, 212, -78, 226, -78, 227, -78, 47, -78, -78,
+ -78, 228, -78, -78, -78
+};
+
+/* YYPGOTO[NTERM-NUM]. */
+static const short int yypgoto[] =
+{
+ -78, -78, -78, -78, 164, -36, -78, -78, -78, -78,
+ 230, -78, -78, -78, -78, 29, -78, -78, -78, -78,
+ -78, -78, -78, -78, -78, -78, 59, -78, -78, -78,
+ -78, -78, 198, 220, 24, 157, -5, 169, 202, 74,
+ -53, -77
+};
+
+/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If
+ positive, shift that token. If negative, reduce the rule which
+ number is the opposite. If zero, do what YYDEFACT says.
+ If YYTABLE_NINF, syntax error. */
+#define YYTABLE_NINF -76
+static const short int yytable[] =
+{
+ 46, 47, 3, 49, 79, 80, 52, 133, 134, 43,
+ 6, 7, 8, 9, 10, 11, 12, 13, 48, 145,
+ 14, 15, 137, 55, 56, 44, 45, 57, 131, 132,
+ 109, 50, 58, 122, 51, 122, 24, 138, 139, -28,
+ 88, 143, -28, -28, -28, -28, -28, -28, -28, -28,
+ -28, 89, 53, -28, -28, 90, 91, -28, 92, 93,
+ 94, 95, 96, 54, 97, 55, 56, 88, 161, 98,
+ -66, -66, -66, -66, -66, -66, -66, -66, 81, 82,
+ -66, -66, 90, 91, 152, 55, 56, 140, 61, 57,
+ 112, 97, 84, 123, 58, 123, 121, 117, 85, 125,
+ 149, 62, 167, -30, 88, 63, -30, -30, -30, -30,
+ -30, -30, -30, -30, -30, 89, 72, -30, -30, 90,
+ 91, -30, 92, 93, 94, 95, 96, 119, 97, 127,
+ 144, -75, 88, 98, -75, -75, -75, -75, -75, -75,
+ -75, -75, -75, 74, 75, -75, -75, 90, 91, -75,
+ -75, -75, -75, -75, -75, 76, 97, 77, 78, -2,
+ 4, 121, 5, 6, 7, 8, 9, 10, 11, 12,
+ 13, 86, 87, 14, 15, 16, 129, 17, 18, 19,
+ 20, 21, 22, 88, 23, 135, 136, -43, -43, 24,
+ -43, -43, -43, -43, 89, 146, -43, -43, 90, 91,
+ 104, 105, 106, 107, 155, 7, 8, 97, 10, 11,
+ 12, 13, 108, 148, 14, 15, 158, 159, 160, 147,
+ 151, 81, 82, 163, 130, 165, 155, 81, 82, 82,
+ 24, 113, 116, 157, 124, 171, 115, 120, 162, 128,
+ 72, 81, 82, 153, 81, 82, 154, 81, 82, 166,
+ 81, 82, 164, 168, 169, 170, 172, 173, 174, 65,
+ 71, 83, 0, 150, 111
+};
+
+static const short int yycheck[] =
+{
+ 5, 6, 0, 8, 57, 58, 11, 84, 85, 30,
+ 4, 5, 6, 7, 8, 9, 10, 11, 30, 96,
+ 14, 15, 24, 25, 26, 25, 26, 29, 81, 82,
+ 66, 30, 34, 69, 30, 71, 30, 90, 91, 0,
+ 1, 94, 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, 12, 25, 14, 15, 16, 17, 18, 19, 20,
+ 21, 22, 23, 25, 25, 25, 26, 1, 145, 30,
+ 4, 5, 6, 7, 8, 9, 10, 11, 31, 32,
+ 14, 15, 16, 17, 137, 25, 26, 92, 30, 29,
+ 66, 25, 27, 69, 34, 71, 30, 68, 33, 70,
+ 105, 1, 155, 0, 1, 1, 3, 4, 5, 6,
+ 7, 8, 9, 10, 11, 12, 30, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, 23, 68, 25, 70,
+ 25, 0, 1, 30, 3, 4, 5, 6, 7, 8,
+ 9, 10, 11, 30, 30, 14, 15, 16, 17, 18,
+ 19, 20, 21, 22, 23, 30, 25, 30, 30, 0,
+ 1, 30, 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, 30, 30, 14, 15, 16, 30, 18, 19, 20,
+ 21, 22, 23, 1, 25, 30, 30, 5, 6, 30,
+ 8, 9, 10, 11, 12, 1, 14, 15, 16, 17,
+ 18, 19, 20, 21, 14, 5, 6, 25, 8, 9,
+ 10, 11, 30, 30, 14, 15, 142, 143, 144, 13,
+ 25, 31, 32, 149, 28, 151, 14, 31, 32, 32,
+ 30, 67, 68, 30, 70, 161, 67, 68, 30, 70,
+ 30, 31, 32, 30, 31, 32, 30, 31, 32, 30,
+ 31, 32, 30, 30, 30, 30, 30, 30, 30, 29,
+ 40, 59, -1, 106, 66
+};
+
+/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+ symbol of state STATE-NUM. */
+static const unsigned char yystos[] =
+{
+ 0, 36, 37, 0, 1, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 14, 15, 16, 18, 19, 20,
+ 21, 22, 23, 25, 30, 38, 39, 41, 42, 43,
+ 44, 47, 48, 50, 54, 56, 58, 59, 61, 63,
+ 64, 65, 72, 30, 25, 26, 71, 71, 30, 71,
+ 30, 30, 71, 25, 25, 25, 26, 29, 34, 75,
+ 76, 30, 1, 1, 45, 45, 51, 53, 57, 68,
+ 62, 68, 30, 73, 30, 30, 30, 30, 30, 75,
+ 75, 31, 32, 73, 27, 33, 30, 30, 1, 12,
+ 16, 17, 19, 20, 21, 22, 23, 25, 30, 40,
+ 46, 66, 67, 69, 18, 19, 20, 21, 30, 40,
+ 52, 67, 69, 39, 49, 72, 39, 50, 55, 61,
+ 72, 30, 40, 69, 39, 50, 60, 61, 72, 30,
+ 28, 75, 75, 76, 76, 30, 30, 24, 75, 75,
+ 71, 70, 71, 75, 25, 76, 1, 13, 30, 71,
+ 70, 25, 75, 30, 30, 14, 74, 30, 74, 74,
+ 74, 76, 30, 74, 30, 74, 30, 75, 30, 30,
+ 30, 74, 30, 30, 30
+};
+
+#if ! defined (YYSIZE_T) && defined (__SIZE_TYPE__)
+# define YYSIZE_T __SIZE_TYPE__
+#endif
+#if ! defined (YYSIZE_T) && defined (size_t)
+# define YYSIZE_T size_t
+#endif
+#if ! defined (YYSIZE_T)
+# if defined (__STDC__) || defined (__cplusplus)
+# include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+# define YYSIZE_T size_t
+# endif
+#endif
+#if ! defined (YYSIZE_T)
+# define YYSIZE_T unsigned int
+#endif
+
+#define yyerrok (yyerrstatus = 0)
+#define yyclearin (yychar = YYEMPTY)
+#define YYEMPTY (-2)
+#define YYEOF 0
+
+#define YYACCEPT goto yyacceptlab
+#define YYABORT goto yyabortlab
+#define YYERROR goto yyerrorlab
+
+
+/* Like YYERROR except do call yyerror. This remains here temporarily
+ to ease the transition to the new meaning of YYERROR, for GCC.
+ Once GCC version 2 has supplanted version 1, this can go. */
+
+#define YYFAIL goto yyerrlab
+
+#define YYRECOVERING() (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value) \
+do \
+ if (yychar == YYEMPTY && yylen == 1) \
+ { \
+ yychar = (Token); \
+ yylval = (Value); \
+ yytoken = YYTRANSLATE (yychar); \
+ YYPOPSTACK; \
+ goto yybackup; \
+ } \
+ else \
+ { \
+ yyerror ("syntax error: cannot back up");\
+ YYERROR; \
+ } \
+while (0)
+
+
+#define YYTERROR 1
+#define YYERRCODE 256
+
+
+/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N].
+ If N is 0, then set CURRENT to the empty location which ends
+ the previous symbol: RHS[0] (always defined). */
+
+#define YYRHSLOC(Rhs, K) ((Rhs)[K])
+#ifndef YYLLOC_DEFAULT
+# define YYLLOC_DEFAULT(Current, Rhs, N) \
+ do \
+ if (N) \
+ { \
+ (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \
+ (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \
+ (Current).last_line = YYRHSLOC (Rhs, N).last_line; \
+ (Current).last_column = YYRHSLOC (Rhs, N).last_column; \
+ } \
+ else \
+ { \
+ (Current).first_line = (Current).last_line = \
+ YYRHSLOC (Rhs, 0).last_line; \
+ (Current).first_column = (Current).last_column = \
+ YYRHSLOC (Rhs, 0).last_column; \
+ } \
+ while (0)
+#endif
+
+
+/* YY_LOCATION_PRINT -- Print the location on the stream.
+ This macro was not mandated originally: define only if we know
+ we won't break user code: when these are the locations we know. */
+
+#ifndef YY_LOCATION_PRINT
+# if YYLTYPE_IS_TRIVIAL
+# define YY_LOCATION_PRINT(File, Loc) \
+ fprintf (File, "%d.%d-%d.%d", \
+ (Loc).first_line, (Loc).first_column, \
+ (Loc).last_line, (Loc).last_column)
+# else
+# define YY_LOCATION_PRINT(File, Loc) ((void) 0)
+# endif
+#endif
+
+
+/* YYLEX -- calling `yylex' with the right arguments. */
+
+#ifdef YYLEX_PARAM
+# define YYLEX yylex (YYLEX_PARAM)
+#else
+# define YYLEX yylex ()
+#endif
+
+/* Enable debugging if requested. */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+# include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+# define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args) \
+do { \
+ if (yydebug) \
+ YYFPRINTF Args; \
+} while (0)
+
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \
+do { \
+ if (yydebug) \
+ { \
+ YYFPRINTF (stderr, "%s ", Title); \
+ yysymprint (stderr, \
+ Type, Value); \
+ YYFPRINTF (stderr, "\n"); \
+ } \
+} while (0)
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included). |
+`------------------------------------------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yy_stack_print (short int *bottom, short int *top)
+#else
+static void
+yy_stack_print (bottom, top)
+ short int *bottom;
+ short int *top;
+#endif
+{
+ YYFPRINTF (stderr, "Stack now");
+ for (/* Nothing. */; bottom <= top; ++bottom)
+ YYFPRINTF (stderr, " %d", *bottom);
+ YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top) \
+do { \
+ if (yydebug) \
+ yy_stack_print ((Bottom), (Top)); \
+} while (0)
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced. |
+`------------------------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yy_reduce_print (int yyrule)
+#else
+static void
+yy_reduce_print (yyrule)
+ int yyrule;
+#endif
+{
+ int yyi;
+ unsigned int yylno = yyrline[yyrule];
+ YYFPRINTF (stderr, "Reducing stack by rule %d (line %u), ",
+ yyrule - 1, yylno);
+ /* Print the symbols being reduced, and their result. */
+ for (yyi = yyprhs[yyrule]; 0 <= yyrhs[yyi]; yyi++)
+ YYFPRINTF (stderr, "%s ", yytname [yyrhs[yyi]]);
+ YYFPRINTF (stderr, "-> %s\n", yytname [yyr1[yyrule]]);
+}
+
+# define YY_REDUCE_PRINT(Rule) \
+do { \
+ if (yydebug) \
+ yy_reduce_print (Rule); \
+} while (0)
+
+/* Nonzero means print parse trace. It is left uninitialized so that
+ multiple parsers can coexist. */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks. */
+#ifndef YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+ if the built-in stack extension method is used).
+
+ Do not make this value too large; the results are undefined if
+ SIZE_MAX < YYSTACK_BYTES (YYMAXDEPTH)
+ evaluated with infinite-precision integer arithmetic. */
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+# if defined (__GLIBC__) && defined (_STRING_H)
+# define yystrlen strlen
+# else
+/* Return the length of YYSTR. */
+static YYSIZE_T
+# if defined (__STDC__) || defined (__cplusplus)
+yystrlen (const char *yystr)
+# else
+yystrlen (yystr)
+ const char *yystr;
+# endif
+{
+ register const char *yys = yystr;
+
+ while (*yys++ != '\0')
+ continue;
+
+ return yys - yystr - 1;
+}
+# endif
+# endif
+
+# ifndef yystpcpy
+# if defined (__GLIBC__) && defined (_STRING_H) && defined (_GNU_SOURCE)
+# define yystpcpy stpcpy
+# else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+ YYDEST. */
+static char *
+# if defined (__STDC__) || defined (__cplusplus)
+yystpcpy (char *yydest, const char *yysrc)
+# else
+yystpcpy (yydest, yysrc)
+ char *yydest;
+ const char *yysrc;
+# endif
+{
+ register char *yyd = yydest;
+ register const char *yys = yysrc;
+
+ while ((*yyd++ = *yys++) != '\0')
+ continue;
+
+ return yyd - 1;
+}
+# endif
+# endif
+
+#endif /* !YYERROR_VERBOSE */
+
+
+
+#if YYDEBUG
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yysymprint (FILE *yyoutput, int yytype, YYSTYPE *yyvaluep)
+#else
+static void
+yysymprint (yyoutput, yytype, yyvaluep)
+ FILE *yyoutput;
+ int yytype;
+ YYSTYPE *yyvaluep;
+#endif
+{
+ /* Pacify ``unused variable'' warnings. */
+ (void) yyvaluep;
+
+ if (yytype < YYNTOKENS)
+ YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
+ else
+ YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
+
+
+# ifdef YYPRINT
+ if (yytype < YYNTOKENS)
+ YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# endif
+ switch (yytype)
+ {
+ default:
+ break;
+ }
+ YYFPRINTF (yyoutput, ")");
+}
+
+#endif /* ! YYDEBUG */
+/*-----------------------------------------------.
+| Release the memory associated to this symbol. |
+`-----------------------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep)
+#else
+static void
+yydestruct (yymsg, yytype, yyvaluep)
+ const char *yymsg;
+ int yytype;
+ YYSTYPE *yyvaluep;
+#endif
+{
+ /* Pacify ``unused variable'' warnings. */
+ (void) yyvaluep;
+
+ if (!yymsg)
+ yymsg = "Deleting";
+ YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
+
+ switch (yytype)
+ {
+ case 48: /* choice_entry */
+
+ {
+ fprintf(stderr, "%s:%d: missing end statement for this entry\n",
+ (yyvaluep->menu)->file->name, (yyvaluep->menu)->lineno);
+ if (current_menu == (yyvaluep->menu))
+ menu_end_menu();
+};
+
+ break;
+ case 54: /* if_entry */
+
+ {
+ fprintf(stderr, "%s:%d: missing end statement for this entry\n",
+ (yyvaluep->menu)->file->name, (yyvaluep->menu)->lineno);
+ if (current_menu == (yyvaluep->menu))
+ menu_end_menu();
+};
+
+ break;
+ case 59: /* menu_entry */
+
+ {
+ fprintf(stderr, "%s:%d: missing end statement for this entry\n",
+ (yyvaluep->menu)->file->name, (yyvaluep->menu)->lineno);
+ if (current_menu == (yyvaluep->menu))
+ menu_end_menu();
+};
+
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+/* Prevent warnings from -Wmissing-prototypes. */
+
+#ifdef YYPARSE_PARAM
+# if defined (__STDC__) || defined (__cplusplus)
+int yyparse (void *YYPARSE_PARAM);
+# else
+int yyparse ();
+# endif
+#else /* ! YYPARSE_PARAM */
+#if defined (__STDC__) || defined (__cplusplus)
+int yyparse (void);
+#else
+int yyparse ();
+#endif
+#endif /* ! YYPARSE_PARAM */
+
+
+
+/* The look-ahead symbol. */
+int yychar;
+
+/* The semantic value of the look-ahead symbol. */
+YYSTYPE yylval;
+
+/* Number of syntax errors so far. */
+int yynerrs;
+
+
+
+/*----------.
+| yyparse. |
+`----------*/
+
+#ifdef YYPARSE_PARAM
+# if defined (__STDC__) || defined (__cplusplus)
+int yyparse (void *YYPARSE_PARAM)
+# else
+int yyparse (YYPARSE_PARAM)
+ void *YYPARSE_PARAM;
+# endif
+#else /* ! YYPARSE_PARAM */
+#if defined (__STDC__) || defined (__cplusplus)
+int
+yyparse (void)
+#else
+int
+yyparse ()
+
+#endif
+#endif
+{
+
+ register int yystate;
+ register int yyn;
+ int yyresult;
+ /* Number of tokens to shift before error messages enabled. */
+ int yyerrstatus;
+ /* Look-ahead token as an internal (translated) token number. */
+ int yytoken = 0;
+
+ /* Three stacks and their tools:
+ `yyss': related to states,
+ `yyvs': related to semantic values,
+ `yyls': related to locations.
+
+ Refer to the stacks thru separate pointers, to allow yyoverflow
+ to reallocate them elsewhere. */
+
+ /* The state stack. */
+ short int yyssa[YYINITDEPTH];
+ short int *yyss = yyssa;
+ register short int *yyssp;
+
+ /* The semantic value stack. */
+ YYSTYPE yyvsa[YYINITDEPTH];
+ YYSTYPE *yyvs = yyvsa;
+ register YYSTYPE *yyvsp;
+
+
+
+#define YYPOPSTACK (yyvsp--, yyssp--)
+
+ YYSIZE_T yystacksize = YYINITDEPTH;
+
+ /* The variables used to return semantic value and location from the
+ action routines. */
+ YYSTYPE yyval;
+
+
+ /* When reducing, the number of symbols on the RHS of the reduced
+ rule. */
+ int yylen;
+
+ YYDPRINTF ((stderr, "Starting parse\n"));
+
+ yystate = 0;
+ yyerrstatus = 0;
+ yynerrs = 0;
+ yychar = YYEMPTY; /* Cause a token to be read. */
+
+ /* Initialize stack pointers.
+ Waste one element of value and location stack
+ so that they stay on the same level as the state stack.
+ The wasted elements are never initialized. */
+
+ yyssp = yyss;
+ yyvsp = yyvs;
+
+
+ yyvsp[0] = yylval;
+
+ goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate. |
+`------------------------------------------------------------*/
+ yynewstate:
+ /* In all cases, when you get here, the value and location stacks
+ have just been pushed. so pushing a state here evens the stacks.
+ */
+ yyssp++;
+
+ yysetstate:
+ *yyssp = yystate;
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ {
+ /* Get the current used size of the three stacks, in elements. */
+ YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+ {
+ /* Give user a chance to reallocate the stack. Use copies of
+ these so that the &'s don't force the real ones into
+ memory. */
+ YYSTYPE *yyvs1 = yyvs;
+ short int *yyss1 = yyss;
+
+
+ /* Each stack pointer address is followed by the size of the
+ data in use in that stack, in bytes. This used to be a
+ conditional around just the two extra args, but that might
+ be undefined if yyoverflow is a macro. */
+ yyoverflow ("parser stack overflow",
+ &yyss1, yysize * sizeof (*yyssp),
+ &yyvs1, yysize * sizeof (*yyvsp),
+
+ &yystacksize);
+
+ yyss = yyss1;
+ yyvs = yyvs1;
+ }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+ goto yyoverflowlab;
+# else
+ /* Extend the stack our own way. */
+ if (YYMAXDEPTH <= yystacksize)
+ goto yyoverflowlab;
+ yystacksize *= 2;
+ if (YYMAXDEPTH < yystacksize)
+ yystacksize = YYMAXDEPTH;
+
+ {
+ short int *yyss1 = yyss;
+ union yyalloc *yyptr =
+ (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+ if (! yyptr)
+ goto yyoverflowlab;
+ YYSTACK_RELOCATE (yyss);
+ YYSTACK_RELOCATE (yyvs);
+
+# undef YYSTACK_RELOCATE
+ if (yyss1 != yyssa)
+ YYSTACK_FREE (yyss1);
+ }
+# endif
+#endif /* no yyoverflow */
+
+ yyssp = yyss + yysize - 1;
+ yyvsp = yyvs + yysize - 1;
+
+
+ YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+ (unsigned long int) yystacksize));
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ YYABORT;
+ }
+
+ YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+ goto yybackup;
+
+/*-----------.
+| yybackup. |
+`-----------*/
+yybackup:
+
+/* Do appropriate processing given the current state. */
+/* Read a look-ahead token if we need one and don't already have one. */
+/* yyresume: */
+
+ /* First try to decide what to do without reference to look-ahead token. */
+
+ yyn = yypact[yystate];
+ if (yyn == YYPACT_NINF)
+ goto yydefault;
+
+ /* Not known => get a look-ahead token if don't already have one. */
+
+ /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */
+ if (yychar == YYEMPTY)
+ {
+ YYDPRINTF ((stderr, "Reading a token: "));
+ yychar = YYLEX;
+ }
+
+ if (yychar <= YYEOF)
+ {
+ yychar = yytoken = YYEOF;
+ YYDPRINTF ((stderr, "Now at end of input.\n"));
+ }
+ else
+ {
+ yytoken = YYTRANSLATE (yychar);
+ YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
+ }
+
+ /* If the proper action on seeing token YYTOKEN is to reduce or to
+ detect an error, take that action. */
+ yyn += yytoken;
+ if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+ goto yydefault;
+ yyn = yytable[yyn];
+ if (yyn <= 0)
+ {
+ if (yyn == 0 || yyn == YYTABLE_NINF)
+ goto yyerrlab;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ /* Shift the look-ahead token. */
+ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+
+ /* Discard the token being shifted unless it is eof. */
+ if (yychar != YYEOF)
+ yychar = YYEMPTY;
+
+ *++yyvsp = yylval;
+
+
+ /* Count tokens shifted since error; after three, turn off error
+ status. */
+ if (yyerrstatus)
+ yyerrstatus--;
+
+ yystate = yyn;
+ goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state. |
+`-----------------------------------------------------------*/
+yydefault:
+ yyn = yydefact[yystate];
+ if (yyn == 0)
+ goto yyerrlab;
+ goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction. |
+`-----------------------------*/
+yyreduce:
+ /* yyn is the number of a rule to reduce with. */
+ yylen = yyr2[yyn];
+
+ /* If YYLEN is nonzero, implement the default value of the action:
+ `$$ = $1'.
+
+ Otherwise, the following line sets YYVAL to garbage.
+ This behavior is undocumented and Bison
+ users should not rely upon it. Assigning to YYVAL
+ unconditionally makes the parser a bit smaller, and it avoids a
+ GCC warning that YYVAL may be used uninitialized. */
+ yyval = yyvsp[1-yylen];
+
+
+ YY_REDUCE_PRINT (yyn);
+ switch (yyn)
+ {
+ case 8:
+
+ { zconf_error("unexpected end statement"); ;}
+ break;
+
+ case 9:
+
+ { zconf_error("unknown statement \"%s\"", (yyvsp[-2].string)); ;}
+ break;
+
+ case 10:
+
+ {
+ zconf_error("unexpected option \"%s\"", kconf_id_strings + (yyvsp[-2].id)->name);
+;}
+ break;
+
+ case 11:
+
+ { zconf_error("invalid statement"); ;}
+ break;
+
+ case 25:
+
+ { zconf_error("unknown option \"%s\"", (yyvsp[-2].string)); ;}
+ break;
+
+ case 26:
+
+ { zconf_error("invalid option"); ;}
+ break;
+
+ case 27:
+
+ {
+ struct symbol *sym = sym_lookup((yyvsp[-1].string), 0);
+ sym->flags |= SYMBOL_OPTIONAL;
+ menu_add_entry(sym);
+ printd(DEBUG_PARSE, "%s:%d:config %s\n", zconf_curname(), zconf_lineno(), (yyvsp[-1].string));
+;}
+ break;
+
+ case 28:
+
+ {
+ menu_end_entry();
+ printd(DEBUG_PARSE, "%s:%d:endconfig\n", zconf_curname(), zconf_lineno());
+;}
+ break;
+
+ case 29:
+
+ {
+ struct symbol *sym = sym_lookup((yyvsp[-1].string), 0);
+ sym->flags |= SYMBOL_OPTIONAL;
+ menu_add_entry(sym);
+ printd(DEBUG_PARSE, "%s:%d:menuconfig %s\n", zconf_curname(), zconf_lineno(), (yyvsp[-1].string));
+;}
+ break;
+
+ case 30:
+
+ {
+ if (current_entry->prompt)
+ current_entry->prompt->type = P_MENU;
+ else
+ zconfprint("warning: menuconfig statement without prompt");
+ menu_end_entry();
+ printd(DEBUG_PARSE, "%s:%d:endconfig\n", zconf_curname(), zconf_lineno());
+;}
+ break;
+
+ case 37:
+
+ {
+ menu_set_type((yyvsp[-2].id)->stype);
+ printd(DEBUG_PARSE, "%s:%d:type(%u)\n",
+ zconf_curname(), zconf_lineno(),
+ (yyvsp[-2].id)->stype);
+;}
+ break;
+
+ case 38:
+
+ {
+ menu_add_prompt(P_PROMPT, (yyvsp[-2].string), (yyvsp[-1].expr));
+ printd(DEBUG_PARSE, "%s:%d:prompt\n", zconf_curname(), zconf_lineno());
+;}
+ break;
+
+ case 39:
+
+ {
+ menu_add_expr(P_DEFAULT, (yyvsp[-2].expr), (yyvsp[-1].expr));
+ if ((yyvsp[-3].id)->stype != S_UNKNOWN)
+ menu_set_type((yyvsp[-3].id)->stype);
+ printd(DEBUG_PARSE, "%s:%d:default(%u)\n",
+ zconf_curname(), zconf_lineno(),
+ (yyvsp[-3].id)->stype);
+;}
+ break;
+
+ case 40:
+
+ {
+ menu_add_symbol(P_SELECT, sym_lookup((yyvsp[-2].string), 0), (yyvsp[-1].expr));
+ printd(DEBUG_PARSE, "%s:%d:select\n", zconf_curname(), zconf_lineno());
+;}
+ break;
+
+ case 41:
+
+ {
+ menu_add_expr(P_RANGE, expr_alloc_comp(E_RANGE,(yyvsp[-3].symbol), (yyvsp[-2].symbol)), (yyvsp[-1].expr));
+ printd(DEBUG_PARSE, "%s:%d:range\n", zconf_curname(), zconf_lineno());
+;}
+ break;
+
+ case 42:
+
+ {
+ struct symbol *sym = sym_lookup(NULL, 0);
+ sym->flags |= SYMBOL_CHOICE;
+ menu_add_entry(sym);
+ menu_add_expr(P_CHOICE, NULL, NULL);
+ printd(DEBUG_PARSE, "%s:%d:choice\n", zconf_curname(), zconf_lineno());
+;}
+ break;
+
+ case 43:
+
+ {
+ (yyval.menu) = menu_add_menu();
+;}
+ break;
+
+ case 44:
+
+ {
+ if (zconf_endtoken((yyvsp[0].id), T_CHOICE, T_ENDCHOICE)) {
+ menu_end_menu();
+ printd(DEBUG_PARSE, "%s:%d:endchoice\n", zconf_curname(), zconf_lineno());
+ }
+;}
+ break;
+
+ case 52:
+
+ {
+ menu_add_prompt(P_PROMPT, (yyvsp[-2].string), (yyvsp[-1].expr));
+ printd(DEBUG_PARSE, "%s:%d:prompt\n", zconf_curname(), zconf_lineno());
+;}
+ break;
+
+ case 53:
+
+ {
+ if ((yyvsp[-2].id)->stype == S_BOOLEAN || (yyvsp[-2].id)->stype == S_TRISTATE) {
+ menu_set_type((yyvsp[-2].id)->stype);
+ printd(DEBUG_PARSE, "%s:%d:type(%u)\n",
+ zconf_curname(), zconf_lineno(),
+ (yyvsp[-2].id)->stype);
+ } else
+ YYERROR;
+;}
+ break;
+
+ case 54:
+
+ {
+ current_entry->sym->flags |= SYMBOL_OPTIONAL;
+ printd(DEBUG_PARSE, "%s:%d:optional\n", zconf_curname(), zconf_lineno());
+;}
+ break;
+
+ case 55:
+
+ {
+ if ((yyvsp[-3].id)->stype == S_UNKNOWN) {
+ menu_add_symbol(P_DEFAULT, sym_lookup((yyvsp[-2].string), 0), (yyvsp[-1].expr));
+ printd(DEBUG_PARSE, "%s:%d:default\n",
+ zconf_curname(), zconf_lineno());
+ } else
+ YYERROR;
+;}
+ break;
+
+ case 58:
+
+ {
+ printd(DEBUG_PARSE, "%s:%d:if\n", zconf_curname(), zconf_lineno());
+ menu_add_entry(NULL);
+ menu_add_dep((yyvsp[-1].expr));
+ (yyval.menu) = menu_add_menu();
+;}
+ break;
+
+ case 59:
+
+ {
+ if (zconf_endtoken((yyvsp[0].id), T_IF, T_ENDIF)) {
+ menu_end_menu();
+ printd(DEBUG_PARSE, "%s:%d:endif\n", zconf_curname(), zconf_lineno());
+ }
+;}
+ break;
+
+ case 65:
+
+ {
+ menu_add_entry(NULL);
+ menu_add_prompt(P_MENU, (yyvsp[-1].string), NULL);
+ printd(DEBUG_PARSE, "%s:%d:menu\n", zconf_curname(), zconf_lineno());
+;}
+ break;
+
+ case 66:
+
+ {
+ (yyval.menu) = menu_add_menu();
+;}
+ break;
+
+ case 67:
+
+ {
+ if (zconf_endtoken((yyvsp[0].id), T_MENU, T_ENDMENU)) {
+ menu_end_menu();
+ printd(DEBUG_PARSE, "%s:%d:endmenu\n", zconf_curname(), zconf_lineno());
+ }
+;}
+ break;
+
+ case 73:
+
+ {
+ printd(DEBUG_PARSE, "%s:%d:source %s\n", zconf_curname(), zconf_lineno(), (yyvsp[-1].string));
+ zconf_nextfile((yyvsp[-1].string));
+;}
+ break;
+
+ case 74:
+
+ {
+ menu_add_entry(NULL);
+ menu_add_prompt(P_COMMENT, (yyvsp[-1].string), NULL);
+ printd(DEBUG_PARSE, "%s:%d:comment\n", zconf_curname(), zconf_lineno());
+;}
+ break;
+
+ case 75:
+
+ {
+ menu_end_entry();
+;}
+ break;
+
+ case 76:
+
+ {
+ printd(DEBUG_PARSE, "%s:%d:help\n", zconf_curname(), zconf_lineno());
+ zconf_starthelp();
+;}
+ break;
+
+ case 77:
+
+ {
+ current_entry->sym->help = (yyvsp[0].string);
+;}
+ break;
+
+ case 82:
+
+ {
+ menu_add_dep((yyvsp[-1].expr));
+ printd(DEBUG_PARSE, "%s:%d:depends on\n", zconf_curname(), zconf_lineno());
+;}
+ break;
+
+ case 83:
+
+ {
+ menu_add_dep((yyvsp[-1].expr));
+ printd(DEBUG_PARSE, "%s:%d:depends\n", zconf_curname(), zconf_lineno());
+;}
+ break;
+
+ case 84:
+
+ {
+ menu_add_dep((yyvsp[-1].expr));
+ printd(DEBUG_PARSE, "%s:%d:requires\n", zconf_curname(), zconf_lineno());
+;}
+ break;
+
+ case 86:
+
+ {
+ menu_add_prompt(P_PROMPT, (yyvsp[-1].string), (yyvsp[0].expr));
+;}
+ break;
+
+ case 89:
+
+ { (yyval.id) = (yyvsp[-1].id); ;}
+ break;
+
+ case 90:
+
+ { (yyval.id) = (yyvsp[-1].id); ;}
+ break;
+
+ case 91:
+
+ { (yyval.id) = (yyvsp[-1].id); ;}
+ break;
+
+ case 94:
+
+ { (yyval.expr) = NULL; ;}
+ break;
+
+ case 95:
+
+ { (yyval.expr) = (yyvsp[0].expr); ;}
+ break;
+
+ case 96:
+
+ { (yyval.expr) = expr_alloc_symbol((yyvsp[0].symbol)); ;}
+ break;
+
+ case 97:
+
+ { (yyval.expr) = expr_alloc_comp(E_EQUAL, (yyvsp[-2].symbol), (yyvsp[0].symbol)); ;}
+ break;
+
+ case 98:
+
+ { (yyval.expr) = expr_alloc_comp(E_UNEQUAL, (yyvsp[-2].symbol), (yyvsp[0].symbol)); ;}
+ break;
+
+ case 99:
+
+ { (yyval.expr) = (yyvsp[-1].expr); ;}
+ break;
+
+ case 100:
+
+ { (yyval.expr) = expr_alloc_one(E_NOT, (yyvsp[0].expr)); ;}
+ break;
+
+ case 101:
+
+ { (yyval.expr) = expr_alloc_two(E_OR, (yyvsp[-2].expr), (yyvsp[0].expr)); ;}
+ break;
+
+ case 102:
+
+ { (yyval.expr) = expr_alloc_two(E_AND, (yyvsp[-2].expr), (yyvsp[0].expr)); ;}
+ break;
+
+ case 103:
+
+ { (yyval.symbol) = sym_lookup((yyvsp[0].string), 0); free((yyvsp[0].string)); ;}
+ break;
+
+ case 104:
+
+ { (yyval.symbol) = sym_lookup((yyvsp[0].string), 1); free((yyvsp[0].string)); ;}
+ break;
+
+
+ }
+
+/* Line 1037 of yacc.c. */
+
+
+ yyvsp -= yylen;
+ yyssp -= yylen;
+
+
+ YY_STACK_PRINT (yyss, yyssp);
+
+ *++yyvsp = yyval;
+
+
+ /* Now `shift' the result of the reduction. Determine what state
+ that goes to, based on the state we popped back to and the rule
+ number reduced by. */
+
+ yyn = yyr1[yyn];
+
+ yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+ if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+ yystate = yytable[yystate];
+ else
+ yystate = yydefgoto[yyn - YYNTOKENS];
+
+ goto yynewstate;
+
+
+/*------------------------------------.
+| yyerrlab -- here on detecting error |
+`------------------------------------*/
+yyerrlab:
+ /* If not already recovering from an error, report this error. */
+ if (!yyerrstatus)
+ {
+ ++yynerrs;
+#if YYERROR_VERBOSE
+ yyn = yypact[yystate];
+
+ if (YYPACT_NINF < yyn && yyn < YYLAST)
+ {
+ YYSIZE_T yysize = 0;
+ int yytype = YYTRANSLATE (yychar);
+ const char* yyprefix;
+ char *yymsg;
+ int yyx;
+
+ /* Start YYX at -YYN if negative to avoid negative indexes in
+ YYCHECK. */
+ int yyxbegin = yyn < 0 ? -yyn : 0;
+
+ /* Stay within bounds of both yycheck and yytname. */
+ int yychecklim = YYLAST - yyn;
+ int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+ int yycount = 0;
+
+ yyprefix = ", expecting ";
+ for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+ if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
+ {
+ yysize += yystrlen (yyprefix) + yystrlen (yytname [yyx]);
+ yycount += 1;
+ if (yycount == 5)
+ {
+ yysize = 0;
+ break;
+ }
+ }
+ yysize += (sizeof ("syntax error, unexpected ")
+ + yystrlen (yytname[yytype]));
+ yymsg = (char *) YYSTACK_ALLOC (yysize);
+ if (yymsg != 0)
+ {
+ char *yyp = yystpcpy (yymsg, "syntax error, unexpected ");
+ yyp = yystpcpy (yyp, yytname[yytype]);
+
+ if (yycount < 5)
+ {
+ yyprefix = ", expecting ";
+ for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+ if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
+ {
+ yyp = yystpcpy (yyp, yyprefix);
+ yyp = yystpcpy (yyp, yytname[yyx]);
+ yyprefix = " or ";
+ }
+ }
+ yyerror (yymsg);
+ YYSTACK_FREE (yymsg);
+ }
+ else
+ yyerror ("syntax error; also virtual memory exhausted");
+ }
+ else
+#endif /* YYERROR_VERBOSE */
+ yyerror ("syntax error");
+ }
+
+
+
+ if (yyerrstatus == 3)
+ {
+ /* If just tried and failed to reuse look-ahead token after an
+ error, discard it. */
+
+ if (yychar <= YYEOF)
+ {
+ /* If at end of input, pop the error token,
+ then the rest of the stack, then return failure. */
+ if (yychar == YYEOF)
+ for (;;)
+ {
+
+ YYPOPSTACK;
+ if (yyssp == yyss)
+ YYABORT;
+ yydestruct ("Error: popping",
+ yystos[*yyssp], yyvsp);
+ }
+ }
+ else
+ {
+ yydestruct ("Error: discarding", yytoken, &yylval);
+ yychar = YYEMPTY;
+ }
+ }
+
+ /* Else will try to reuse look-ahead token after shifting the error
+ token. */
+ goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR. |
+`---------------------------------------------------*/
+yyerrorlab:
+
+#ifdef __GNUC__
+ /* Pacify GCC when the user code never invokes YYERROR and the label
+ yyerrorlab therefore never appears in user code. */
+ if (0)
+ goto yyerrorlab;
+#endif
+
+yyvsp -= yylen;
+ yyssp -= yylen;
+ yystate = *yyssp;
+ goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR. |
+`-------------------------------------------------------------*/
+yyerrlab1:
+ yyerrstatus = 3; /* Each real token shifted decrements this. */
+
+ for (;;)
+ {
+ yyn = yypact[yystate];
+ if (yyn != YYPACT_NINF)
+ {
+ yyn += YYTERROR;
+ if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+ {
+ yyn = yytable[yyn];
+ if (0 < yyn)
+ break;
+ }
+ }
+
+ /* Pop the current state because it cannot handle the error token. */
+ if (yyssp == yyss)
+ YYABORT;
+
+
+ yydestruct ("Error: popping", yystos[yystate], yyvsp);
+ YYPOPSTACK;
+ yystate = *yyssp;
+ YY_STACK_PRINT (yyss, yyssp);
+ }
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ *++yyvsp = yylval;
+
+
+ /* Shift the error token. */
+ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
+
+ yystate = yyn;
+ goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here. |
+`-------------------------------------*/
+yyacceptlab:
+ yyresult = 0;
+ goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here. |
+`-----------------------------------*/
+yyabortlab:
+ yydestruct ("Error: discarding lookahead",
+ yytoken, &yylval);
+ yychar = YYEMPTY;
+ yyresult = 1;
+ goto yyreturn;
+
+#ifndef yyoverflow
+/*----------------------------------------------.
+| yyoverflowlab -- parser overflow comes here. |
+`----------------------------------------------*/
+yyoverflowlab:
+ yyerror ("parser stack overflow");
+ yyresult = 2;
+ /* Fall through. */
+#endif
+
+yyreturn:
+#ifndef yyoverflow
+ if (yyss != yyssa)
+ YYSTACK_FREE (yyss);
+#endif
+ return yyresult;
+}
+
+
+
+
+
+void conf_parse(const char *name)
+{
+ struct symbol *sym;
+ int i;
+
+ zconf_initscan(name);
+
+ sym_init();
+ menu_init();
+ modules_sym = sym_lookup("MODULES", 0);
+ rootmenu.prompt = menu_add_prompt(P_MENU, "Busybox Configuration", NULL);
+
+#if YYDEBUG
+ if (getenv("ZCONF_DEBUG"))
+ zconfdebug = 1;
+#endif
+ zconfparse();
+ if (zconfnerrs)
+ exit(1);
+ menu_finalize(&rootmenu);
+ for_all_symbols(i, sym) {
+ sym_check_deps(sym);
+ }
+
+ sym_change_count = 1;
+}
+
+const char *zconf_tokenname(int token)
+{
+ switch (token) {
+ case T_MENU: return "menu";
+ case T_ENDMENU: return "endmenu";
+ case T_CHOICE: return "choice";
+ case T_ENDCHOICE: return "endchoice";
+ case T_IF: return "if";
+ case T_ENDIF: return "endif";
+ case T_DEPENDS: return "depends";
+ }
+ return "<token>";
+}
+
+static bool zconf_endtoken(struct kconf_id *id, int starttoken, int endtoken)
+{
+ if (id->token != endtoken) {
+ zconf_error("unexpected '%s' within %s block",
+ kconf_id_strings + id->name, zconf_tokenname(starttoken));
+ zconfnerrs++;
+ return false;
+ }
+ if (current_menu->file != current_file) {
+ zconf_error("'%s' in different file than '%s'",
+ kconf_id_strings + id->name, zconf_tokenname(starttoken));
+ fprintf(stderr, "%s:%d: location of the '%s'\n",
+ current_menu->file->name, current_menu->lineno,
+ zconf_tokenname(starttoken));
+ zconfnerrs++;
+ return false;
+ }
+ return true;
+}
+
+static void zconfprint(const char *err, ...)
+{
+ va_list ap;
+
+ fprintf(stderr, "%s:%d: ", zconf_curname(), zconf_lineno());
+ va_start(ap, err);
+ vfprintf(stderr, err, ap);
+ va_end(ap);
+ fprintf(stderr, "\n");
+}
+
+static void zconf_error(const char *err, ...)
+{
+ va_list ap;
+
+ zconfnerrs++;
+ fprintf(stderr, "%s:%d: ", zconf_curname(), zconf_lineno());
+ va_start(ap, err);
+ vfprintf(stderr, err, ap);
+ va_end(ap);
+ fprintf(stderr, "\n");
+}
+
+static void zconferror(const char *err)
+{
+#if YYDEBUG
+ fprintf(stderr, "%s:%d: %s\n", zconf_curname(), zconf_lineno() + 1, err);
+#endif
+}
+
+void print_quoted_string(FILE *out, const char *str)
+{
+ const char *p;
+ int len;
+
+ putc('"', out);
+ while ((p = strchr(str, '"'))) {
+ len = p - str;
+ if (len)
+ fprintf(out, "%.*s", len, str);
+ fputs("\\\"", out);
+ str = p + 1;
+ }
+ fputs(str, out);
+ putc('"', out);
+}
+
+void print_symbol(FILE *out, struct menu *menu)
+{
+ struct symbol *sym = menu->sym;
+ struct property *prop;
+
+ if (sym_is_choice(sym))
+ fprintf(out, "choice\n");
+ else
+ fprintf(out, "config %s\n", sym->name);
+ switch (sym->type) {
+ case S_BOOLEAN:
+ fputs(" boolean\n", out);
+ break;
+ case S_TRISTATE:
+ fputs(" tristate\n", out);
+ break;
+ case S_STRING:
+ fputs(" string\n", out);
+ break;
+ case S_INT:
+ fputs(" integer\n", out);
+ break;
+ case S_HEX:
+ fputs(" hex\n", out);
+ break;
+ default:
+ fputs(" ???\n", out);
+ break;
+ }
+ for (prop = sym->prop; prop; prop = prop->next) {
+ if (prop->menu != menu)
+ continue;
+ switch (prop->type) {
+ case P_PROMPT:
+ fputs(" prompt ", out);
+ print_quoted_string(out, prop->text);
+ if (!expr_is_yes(prop->visible.expr)) {
+ fputs(" if ", out);
+ expr_fprint(prop->visible.expr, out);
+ }
+ fputc('\n', out);
+ break;
+ case P_DEFAULT:
+ fputs( " default ", out);
+ expr_fprint(prop->expr, out);
+ if (!expr_is_yes(prop->visible.expr)) {
+ fputs(" if ", out);
+ expr_fprint(prop->visible.expr, out);
+ }
+ fputc('\n', out);
+ break;
+ case P_CHOICE:
+ fputs(" #choice value\n", out);
+ break;
+ default:
+ fprintf(out, " unknown prop %d!\n", prop->type);
+ break;
+ }
+ }
+ if (sym->help) {
+ int len = strlen(sym->help);
+ while (sym->help[--len] == '\n')
+ sym->help[len] = 0;
+ fprintf(out, " help\n%s\n", sym->help);
+ }
+ fputc('\n', out);
+}
+
+void zconfdump(FILE *out)
+{
+ struct property *prop;
+ struct symbol *sym;
+ struct menu *menu;
+
+ menu = rootmenu.list;
+ while (menu) {
+ if ((sym = menu->sym))
+ print_symbol(out, menu);
+ else if ((prop = menu->prompt)) {
+ switch (prop->type) {
+ case P_COMMENT:
+ fputs("\ncomment ", out);
+ print_quoted_string(out, prop->text);
+ fputs("\n", out);
+ break;
+ case P_MENU:
+ fputs("\nmenu ", out);
+ print_quoted_string(out, prop->text);
+ fputs("\n", out);
+ break;
+ default:
+ ;
+ }
+ if (!expr_is_yes(prop->visible.expr)) {
+ fputs(" depends ", out);
+ expr_fprint(prop->visible.expr, out);
+ fputc('\n', out);
+ }
+ fputs("\n", out);
+ }
+
+ if (menu->list)
+ menu = menu->list;
+ else if (menu->next)
+ menu = menu->next;
+ else while ((menu = menu->parent)) {
+ if (menu->prompt && menu->prompt->type == P_MENU)
+ fputs("\nendmenu\n", out);
+ if (menu->next) {
+ menu = menu->next;
+ break;
+ }
+ }
+ }
+}
+
+#include "lex.zconf.c"
+#include "util.c"
+#include "confdata.c"
+#include "expr.c"
+#include "symbol.c"
+#include "menu.c"
+
+
diff --git a/i/pc104/initrd/conf/busybox/scripts/kconfig/zconf.y b/i/pc104/initrd/conf/busybox/scripts/kconfig/zconf.y
new file mode 100644
index 0000000..0a7a796
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/scripts/kconfig/zconf.y
@@ -0,0 +1,681 @@
+%{
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+
+#define LKC_DIRECT_LINK
+#include "lkc.h"
+
+#include "zconf.hash.c"
+
+#define printd(mask, fmt...) if (cdebug & (mask)) printf(fmt)
+
+#define PRINTD 0x0001
+#define DEBUG_PARSE 0x0002
+
+int cdebug = PRINTD;
+
+extern int zconflex(void);
+static void zconfprint(const char *err, ...);
+static void zconf_error(const char *err, ...);
+static void zconferror(const char *err);
+static bool zconf_endtoken(struct kconf_id *id, int starttoken, int endtoken);
+
+struct symbol *symbol_hash[257];
+
+static struct menu *current_menu, *current_entry;
+
+#define YYDEBUG 0
+#if YYDEBUG
+#define YYERROR_VERBOSE
+#endif
+%}
+%expect 26
+
+%union
+{
+ char *string;
+ struct file *file;
+ struct symbol *symbol;
+ struct expr *expr;
+ struct menu *menu;
+ struct kconf_id *id;
+}
+
+%token <id>T_MAINMENU
+%token <id>T_MENU
+%token <id>T_ENDMENU
+%token <id>T_SOURCE
+%token <id>T_CHOICE
+%token <id>T_ENDCHOICE
+%token <id>T_COMMENT
+%token <id>T_CONFIG
+%token <id>T_MENUCONFIG
+%token <id>T_HELP
+%token <string> T_HELPTEXT
+%token <id>T_IF
+%token <id>T_ENDIF
+%token <id>T_DEPENDS
+%token <id>T_REQUIRES
+%token <id>T_OPTIONAL
+%token <id>T_PROMPT
+%token <id>T_TYPE
+%token <id>T_DEFAULT
+%token <id>T_SELECT
+%token <id>T_RANGE
+%token <id>T_ON
+%token <string> T_WORD
+%token <string> T_WORD_QUOTE
+%token T_UNEQUAL
+%token T_CLOSE_PAREN
+%token T_OPEN_PAREN
+%token T_EOL
+
+%left T_OR
+%left T_AND
+%left T_EQUAL T_UNEQUAL
+%nonassoc T_NOT
+
+%type <string> prompt
+%type <symbol> symbol
+%type <expr> expr
+%type <expr> if_expr
+%type <id> end
+%type <id> option_name
+%type <menu> if_entry menu_entry choice_entry
+
+%destructor {
+ fprintf(stderr, "%s:%d: missing end statement for this entry\n",
+ $$->file->name, $$->lineno);
+ if (current_menu == $$)
+ menu_end_menu();
+} if_entry menu_entry choice_entry
+
+%%
+input: stmt_list;
+
+stmt_list:
+ /* empty */
+ | stmt_list common_stmt
+ | stmt_list choice_stmt
+ | stmt_list menu_stmt
+ | stmt_list T_MAINMENU prompt nl
+ | stmt_list end { zconf_error("unexpected end statement"); }
+ | stmt_list T_WORD error T_EOL { zconf_error("unknown statement \"%s\"", $2); }
+ | stmt_list option_name error T_EOL
+{
+ zconf_error("unexpected option \"%s\"", kconf_id_strings + $2->name);
+}
+ | stmt_list error T_EOL { zconf_error("invalid statement"); }
+;
+
+option_name:
+ T_DEPENDS | T_PROMPT | T_TYPE | T_SELECT | T_OPTIONAL | T_RANGE | T_DEFAULT
+;
+
+common_stmt:
+ T_EOL
+ | if_stmt
+ | comment_stmt
+ | config_stmt
+ | menuconfig_stmt
+ | source_stmt
+;
+
+option_error:
+ T_WORD error T_EOL { zconf_error("unknown option \"%s\"", $1); }
+ | error T_EOL { zconf_error("invalid option"); }
+;
+
+
+/* config/menuconfig entry */
+
+config_entry_start: T_CONFIG T_WORD T_EOL
+{
+ struct symbol *sym = sym_lookup($2, 0);
+ sym->flags |= SYMBOL_OPTIONAL;
+ menu_add_entry(sym);
+ printd(DEBUG_PARSE, "%s:%d:config %s\n", zconf_curname(), zconf_lineno(), $2);
+};
+
+config_stmt: config_entry_start config_option_list
+{
+ menu_end_entry();
+ printd(DEBUG_PARSE, "%s:%d:endconfig\n", zconf_curname(), zconf_lineno());
+};
+
+menuconfig_entry_start: T_MENUCONFIG T_WORD T_EOL
+{
+ struct symbol *sym = sym_lookup($2, 0);
+ sym->flags |= SYMBOL_OPTIONAL;
+ menu_add_entry(sym);
+ printd(DEBUG_PARSE, "%s:%d:menuconfig %s\n", zconf_curname(), zconf_lineno(), $2);
+};
+
+menuconfig_stmt: menuconfig_entry_start config_option_list
+{
+ if (current_entry->prompt)
+ current_entry->prompt->type = P_MENU;
+ else
+ zconfprint("warning: menuconfig statement without prompt");
+ menu_end_entry();
+ printd(DEBUG_PARSE, "%s:%d:endconfig\n", zconf_curname(), zconf_lineno());
+};
+
+config_option_list:
+ /* empty */
+ | config_option_list config_option
+ | config_option_list depends
+ | config_option_list help
+ | config_option_list option_error
+ | config_option_list T_EOL
+;
+
+config_option: T_TYPE prompt_stmt_opt T_EOL
+{
+ menu_set_type($1->stype);
+ printd(DEBUG_PARSE, "%s:%d:type(%u)\n",
+ zconf_curname(), zconf_lineno(),
+ $1->stype);
+};
+
+config_option: T_PROMPT prompt if_expr T_EOL
+{
+ menu_add_prompt(P_PROMPT, $2, $3);
+ printd(DEBUG_PARSE, "%s:%d:prompt\n", zconf_curname(), zconf_lineno());
+};
+
+config_option: T_DEFAULT expr if_expr T_EOL
+{
+ menu_add_expr(P_DEFAULT, $2, $3);
+ if ($1->stype != S_UNKNOWN)
+ menu_set_type($1->stype);
+ printd(DEBUG_PARSE, "%s:%d:default(%u)\n",
+ zconf_curname(), zconf_lineno(),
+ $1->stype);
+};
+
+config_option: T_SELECT T_WORD if_expr T_EOL
+{
+ menu_add_symbol(P_SELECT, sym_lookup($2, 0), $3);
+ printd(DEBUG_PARSE, "%s:%d:select\n", zconf_curname(), zconf_lineno());
+};
+
+config_option: T_RANGE symbol symbol if_expr T_EOL
+{
+ menu_add_expr(P_RANGE, expr_alloc_comp(E_RANGE,$2, $3), $4);
+ printd(DEBUG_PARSE, "%s:%d:range\n", zconf_curname(), zconf_lineno());
+};
+
+/* choice entry */
+
+choice: T_CHOICE T_EOL
+{
+ struct symbol *sym = sym_lookup(NULL, 0);
+ sym->flags |= SYMBOL_CHOICE;
+ menu_add_entry(sym);
+ menu_add_expr(P_CHOICE, NULL, NULL);
+ printd(DEBUG_PARSE, "%s:%d:choice\n", zconf_curname(), zconf_lineno());
+};
+
+choice_entry: choice choice_option_list
+{
+ $$ = menu_add_menu();
+};
+
+choice_end: end
+{
+ if (zconf_endtoken($1, T_CHOICE, T_ENDCHOICE)) {
+ menu_end_menu();
+ printd(DEBUG_PARSE, "%s:%d:endchoice\n", zconf_curname(), zconf_lineno());
+ }
+};
+
+choice_stmt: choice_entry choice_block choice_end
+;
+
+choice_option_list:
+ /* empty */
+ | choice_option_list choice_option
+ | choice_option_list depends
+ | choice_option_list help
+ | choice_option_list T_EOL
+ | choice_option_list option_error
+;
+
+choice_option: T_PROMPT prompt if_expr T_EOL
+{
+ menu_add_prompt(P_PROMPT, $2, $3);
+ printd(DEBUG_PARSE, "%s:%d:prompt\n", zconf_curname(), zconf_lineno());
+};
+
+choice_option: T_TYPE prompt_stmt_opt T_EOL
+{
+ if ($1->stype == S_BOOLEAN || $1->stype == S_TRISTATE) {
+ menu_set_type($1->stype);
+ printd(DEBUG_PARSE, "%s:%d:type(%u)\n",
+ zconf_curname(), zconf_lineno(),
+ $1->stype);
+ } else
+ YYERROR;
+};
+
+choice_option: T_OPTIONAL T_EOL
+{
+ current_entry->sym->flags |= SYMBOL_OPTIONAL;
+ printd(DEBUG_PARSE, "%s:%d:optional\n", zconf_curname(), zconf_lineno());
+};
+
+choice_option: T_DEFAULT T_WORD if_expr T_EOL
+{
+ if ($1->stype == S_UNKNOWN) {
+ menu_add_symbol(P_DEFAULT, sym_lookup($2, 0), $3);
+ printd(DEBUG_PARSE, "%s:%d:default\n",
+ zconf_curname(), zconf_lineno());
+ } else
+ YYERROR;
+};
+
+choice_block:
+ /* empty */
+ | choice_block common_stmt
+;
+
+/* if entry */
+
+if_entry: T_IF expr nl
+{
+ printd(DEBUG_PARSE, "%s:%d:if\n", zconf_curname(), zconf_lineno());
+ menu_add_entry(NULL);
+ menu_add_dep($2);
+ $$ = menu_add_menu();
+};
+
+if_end: end
+{
+ if (zconf_endtoken($1, T_IF, T_ENDIF)) {
+ menu_end_menu();
+ printd(DEBUG_PARSE, "%s:%d:endif\n", zconf_curname(), zconf_lineno());
+ }
+};
+
+if_stmt: if_entry if_block if_end
+;
+
+if_block:
+ /* empty */
+ | if_block common_stmt
+ | if_block menu_stmt
+ | if_block choice_stmt
+;
+
+/* menu entry */
+
+menu: T_MENU prompt T_EOL
+{
+ menu_add_entry(NULL);
+ menu_add_prompt(P_MENU, $2, NULL);
+ printd(DEBUG_PARSE, "%s:%d:menu\n", zconf_curname(), zconf_lineno());
+};
+
+menu_entry: menu depends_list
+{
+ $$ = menu_add_menu();
+};
+
+menu_end: end
+{
+ if (zconf_endtoken($1, T_MENU, T_ENDMENU)) {
+ menu_end_menu();
+ printd(DEBUG_PARSE, "%s:%d:endmenu\n", zconf_curname(), zconf_lineno());
+ }
+};
+
+menu_stmt: menu_entry menu_block menu_end
+;
+
+menu_block:
+ /* empty */
+ | menu_block common_stmt
+ | menu_block menu_stmt
+ | menu_block choice_stmt
+;
+
+source_stmt: T_SOURCE prompt T_EOL
+{
+ printd(DEBUG_PARSE, "%s:%d:source %s\n", zconf_curname(), zconf_lineno(), $2);
+ zconf_nextfile($2);
+};
+
+/* comment entry */
+
+comment: T_COMMENT prompt T_EOL
+{
+ menu_add_entry(NULL);
+ menu_add_prompt(P_COMMENT, $2, NULL);
+ printd(DEBUG_PARSE, "%s:%d:comment\n", zconf_curname(), zconf_lineno());
+};
+
+comment_stmt: comment depends_list
+{
+ menu_end_entry();
+};
+
+/* help option */
+
+help_start: T_HELP T_EOL
+{
+ printd(DEBUG_PARSE, "%s:%d:help\n", zconf_curname(), zconf_lineno());
+ zconf_starthelp();
+};
+
+help: help_start T_HELPTEXT
+{
+ current_entry->sym->help = $2;
+};
+
+/* depends option */
+
+depends_list:
+ /* empty */
+ | depends_list depends
+ | depends_list T_EOL
+ | depends_list option_error
+;
+
+depends: T_DEPENDS T_ON expr T_EOL
+{
+ menu_add_dep($3);
+ printd(DEBUG_PARSE, "%s:%d:depends on\n", zconf_curname(), zconf_lineno());
+}
+ | T_DEPENDS expr T_EOL
+{
+ menu_add_dep($2);
+ printd(DEBUG_PARSE, "%s:%d:depends\n", zconf_curname(), zconf_lineno());
+}
+ | T_REQUIRES expr T_EOL
+{
+ menu_add_dep($2);
+ printd(DEBUG_PARSE, "%s:%d:requires\n", zconf_curname(), zconf_lineno());
+};
+
+/* prompt statement */
+
+prompt_stmt_opt:
+ /* empty */
+ | prompt if_expr
+{
+ menu_add_prompt(P_PROMPT, $1, $2);
+};
+
+prompt: T_WORD
+ | T_WORD_QUOTE
+;
+
+end: T_ENDMENU T_EOL { $$ = $1; }
+ | T_ENDCHOICE T_EOL { $$ = $1; }
+ | T_ENDIF T_EOL { $$ = $1; }
+;
+
+nl:
+ T_EOL
+ | nl T_EOL
+;
+
+if_expr: /* empty */ { $$ = NULL; }
+ | T_IF expr { $$ = $2; }
+;
+
+expr: symbol { $$ = expr_alloc_symbol($1); }
+ | symbol T_EQUAL symbol { $$ = expr_alloc_comp(E_EQUAL, $1, $3); }
+ | symbol T_UNEQUAL symbol { $$ = expr_alloc_comp(E_UNEQUAL, $1, $3); }
+ | T_OPEN_PAREN expr T_CLOSE_PAREN { $$ = $2; }
+ | T_NOT expr { $$ = expr_alloc_one(E_NOT, $2); }
+ | expr T_OR expr { $$ = expr_alloc_two(E_OR, $1, $3); }
+ | expr T_AND expr { $$ = expr_alloc_two(E_AND, $1, $3); }
+;
+
+symbol: T_WORD { $$ = sym_lookup($1, 0); free($1); }
+ | T_WORD_QUOTE { $$ = sym_lookup($1, 1); free($1); }
+;
+
+%%
+
+void conf_parse(const char *name)
+{
+ struct symbol *sym;
+ int i;
+
+ zconf_initscan(name);
+
+ sym_init();
+ menu_init();
+ modules_sym = sym_lookup("MODULES", 0);
+ rootmenu.prompt = menu_add_prompt(P_MENU, "Busybox Configuration", NULL);
+
+#if YYDEBUG
+ if (getenv("ZCONF_DEBUG"))
+ zconfdebug = 1;
+#endif
+ zconfparse();
+ if (zconfnerrs)
+ exit(1);
+ menu_finalize(&rootmenu);
+ for_all_symbols(i, sym) {
+ sym_check_deps(sym);
+ }
+
+ sym_change_count = 1;
+}
+
+const char *zconf_tokenname(int token)
+{
+ switch (token) {
+ case T_MENU: return "menu";
+ case T_ENDMENU: return "endmenu";
+ case T_CHOICE: return "choice";
+ case T_ENDCHOICE: return "endchoice";
+ case T_IF: return "if";
+ case T_ENDIF: return "endif";
+ case T_DEPENDS: return "depends";
+ }
+ return "<token>";
+}
+
+static bool zconf_endtoken(struct kconf_id *id, int starttoken, int endtoken)
+{
+ if (id->token != endtoken) {
+ zconf_error("unexpected '%s' within %s block",
+ kconf_id_strings + id->name, zconf_tokenname(starttoken));
+ zconfnerrs++;
+ return false;
+ }
+ if (current_menu->file != current_file) {
+ zconf_error("'%s' in different file than '%s'",
+ kconf_id_strings + id->name, zconf_tokenname(starttoken));
+ fprintf(stderr, "%s:%d: location of the '%s'\n",
+ current_menu->file->name, current_menu->lineno,
+ zconf_tokenname(starttoken));
+ zconfnerrs++;
+ return false;
+ }
+ return true;
+}
+
+static void zconfprint(const char *err, ...)
+{
+ va_list ap;
+
+ fprintf(stderr, "%s:%d: ", zconf_curname(), zconf_lineno());
+ va_start(ap, err);
+ vfprintf(stderr, err, ap);
+ va_end(ap);
+ fprintf(stderr, "\n");
+}
+
+static void zconf_error(const char *err, ...)
+{
+ va_list ap;
+
+ zconfnerrs++;
+ fprintf(stderr, "%s:%d: ", zconf_curname(), zconf_lineno());
+ va_start(ap, err);
+ vfprintf(stderr, err, ap);
+ va_end(ap);
+ fprintf(stderr, "\n");
+}
+
+static void zconferror(const char *err)
+{
+#if YYDEBUG
+ fprintf(stderr, "%s:%d: %s\n", zconf_curname(), zconf_lineno() + 1, err);
+#endif
+}
+
+void print_quoted_string(FILE *out, const char *str)
+{
+ const char *p;
+ int len;
+
+ putc('"', out);
+ while ((p = strchr(str, '"'))) {
+ len = p - str;
+ if (len)
+ fprintf(out, "%.*s", len, str);
+ fputs("\\\"", out);
+ str = p + 1;
+ }
+ fputs(str, out);
+ putc('"', out);
+}
+
+void print_symbol(FILE *out, struct menu *menu)
+{
+ struct symbol *sym = menu->sym;
+ struct property *prop;
+
+ if (sym_is_choice(sym))
+ fprintf(out, "choice\n");
+ else
+ fprintf(out, "config %s\n", sym->name);
+ switch (sym->type) {
+ case S_BOOLEAN:
+ fputs(" boolean\n", out);
+ break;
+ case S_TRISTATE:
+ fputs(" tristate\n", out);
+ break;
+ case S_STRING:
+ fputs(" string\n", out);
+ break;
+ case S_INT:
+ fputs(" integer\n", out);
+ break;
+ case S_HEX:
+ fputs(" hex\n", out);
+ break;
+ default:
+ fputs(" ???\n", out);
+ break;
+ }
+ for (prop = sym->prop; prop; prop = prop->next) {
+ if (prop->menu != menu)
+ continue;
+ switch (prop->type) {
+ case P_PROMPT:
+ fputs(" prompt ", out);
+ print_quoted_string(out, prop->text);
+ if (!expr_is_yes(prop->visible.expr)) {
+ fputs(" if ", out);
+ expr_fprint(prop->visible.expr, out);
+ }
+ fputc('\n', out);
+ break;
+ case P_DEFAULT:
+ fputs( " default ", out);
+ expr_fprint(prop->expr, out);
+ if (!expr_is_yes(prop->visible.expr)) {
+ fputs(" if ", out);
+ expr_fprint(prop->visible.expr, out);
+ }
+ fputc('\n', out);
+ break;
+ case P_CHOICE:
+ fputs(" #choice value\n", out);
+ break;
+ default:
+ fprintf(out, " unknown prop %d!\n", prop->type);
+ break;
+ }
+ }
+ if (sym->help) {
+ int len = strlen(sym->help);
+ while (sym->help[--len] == '\n')
+ sym->help[len] = 0;
+ fprintf(out, " help\n%s\n", sym->help);
+ }
+ fputc('\n', out);
+}
+
+void zconfdump(FILE *out)
+{
+ struct property *prop;
+ struct symbol *sym;
+ struct menu *menu;
+
+ menu = rootmenu.list;
+ while (menu) {
+ if ((sym = menu->sym))
+ print_symbol(out, menu);
+ else if ((prop = menu->prompt)) {
+ switch (prop->type) {
+ case P_COMMENT:
+ fputs("\ncomment ", out);
+ print_quoted_string(out, prop->text);
+ fputs("\n", out);
+ break;
+ case P_MENU:
+ fputs("\nmenu ", out);
+ print_quoted_string(out, prop->text);
+ fputs("\n", out);
+ break;
+ default:
+ ;
+ }
+ if (!expr_is_yes(prop->visible.expr)) {
+ fputs(" depends ", out);
+ expr_fprint(prop->visible.expr, out);
+ fputc('\n', out);
+ }
+ fputs("\n", out);
+ }
+
+ if (menu->list)
+ menu = menu->list;
+ else if (menu->next)
+ menu = menu->next;
+ else while ((menu = menu->parent)) {
+ if (menu->prompt && menu->prompt->type == P_MENU)
+ fputs("\nendmenu\n", out);
+ if (menu->next) {
+ menu = menu->next;
+ break;
+ }
+ }
+ }
+}
+
+#include "lex.zconf.c"
+#include "util.c"
+#include "confdata.c"
+#include "expr.c"
+#include "symbol.c"
+#include "menu.c"
diff --git a/i/pc104/initrd/conf/busybox/scripts/mkconfigs b/i/pc104/initrd/conf/busybox/scripts/mkconfigs
new file mode 100755
index 0000000..d212ad4
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/scripts/mkconfigs
@@ -0,0 +1,52 @@
+#!/bin/sh
+#
+# Copyright (C) 2002 Khalid Aziz <khalid_aziz at hp.com>
+# Copyright (C) 2002 Randy Dunlap <rddunlap at osdl.org>
+# Copyright (C) 2002 Al Stone <ahs3 at fc.hp.com>
+# Copyright (C) 2002 Hewlett-Packard Company
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+# Busybox version by Matteo Croce <3297627799 at wind.it>
+#
+# Rules to generate bbconfigopts.h from .config:
+# - Retain lines that begin with "CONFIG_"
+# - Retain lines that begin with "# CONFIG_"
+# - lines that use double-quotes must \\-escape-quote them
+
+config="$1"
+if [ $# -lt 1 ]
+then
+ config=.config
+fi
+
+echo "\
+#ifndef _BBCONFIGOPTS_H
+#define _BBCONFIGOPTS_H
+/*
+ * busybox configuration settings.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ *
+ * This file is generated automatically by scripts/mkconfigs.
+ * Do not edit.
+ *
+ */
+static const char * const bbconfig_config ="
+
+sed 's/\"/\\\"/g' $config | grep "^#\? \?CONFIG_" | awk '{print "\"" $0 "\\n\"";}'
+
+echo ";"
+echo "#endif /* _BBCONFIGOPTS_H */"
diff --git a/i/pc104/initrd/conf/busybox/scripts/mkmakefile b/i/pc104/initrd/conf/busybox/scripts/mkmakefile
new file mode 100755
index 0000000..7f9d544
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/scripts/mkmakefile
@@ -0,0 +1,36 @@
+#!/bin/sh
+# Generates a small Makefile used in the root of the output
+# directory, to allow make to be started from there.
+# The Makefile also allow for more convinient build of external modules
+
+# Usage
+# $1 - Kernel src directory
+# $2 - Output directory
+# $3 - version
+# $4 - patchlevel
+
+
+test ! -r $2/Makefile -o -O $2/Makefile || exit 0
+echo " GEN $2/Makefile"
+
+cat << EOF > $2/Makefile
+# Automatically generated by $0: don't edit
+
+VERSION = $3
+PATCHLEVEL = $4
+
+KERNELSRC := $1
+KERNELOUTPUT := $2
+
+MAKEFLAGS += --no-print-directory
+
+.PHONY: all \$(MAKECMDGOALS)
+
+all:
+ \$(MAKE) -C \$(KERNELSRC) O=\$(KERNELOUTPUT)
+
+Makefile:;
+
+\$(filter-out all Makefile,\$(MAKECMDGOALS)) %/:
+ \$(MAKE) -C \$(KERNELSRC) O=\$(KERNELOUTPUT) \$@
+EOF
diff --git a/i/pc104/initrd/conf/busybox/scripts/objsizes b/i/pc104/initrd/conf/busybox/scripts/objsizes
new file mode 100755
index 0000000..09de114
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/scripts/objsizes
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+t_text=0
+t_data=0
+t_bss=0
+
+printf "%9s %11s %9s %9s %s\n" "text+data" "text+rodata" rwdata bss filename
+
+find -name '*.o' | grep -v '^\./scripts/' | grep -vF built-in.o \
+| sed 's:^\./::' | xargs "${CROSS_COMPILE}size" | grep '^ *[0-9]' \
+| {
+while read text data bss dec hex filename; do
+ t_text=$((t_text+text))
+ t_data=$((t_data+data))
+ t_bss=$((t_bss+bss))
+ printf "%9d %11d %9d %9d %s\n" $((text+data)) $text $data $bss "$filename"
+done
+printf "%9d %11d %9d %9d %s\n" $((t_text+t_data)) $t_text $t_data $t_bss "TOTAL"
+} | sort -r
diff --git a/i/pc104/initrd/conf/busybox/scripts/showasm b/i/pc104/initrd/conf/busybox/scripts/showasm
new file mode 100755
index 0000000..0464426
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/scripts/showasm
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+# Copyright 2006 Rob Landley <rob@landley.net>
+# Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+
+# Dumb little utility function to print out the assembly dump of a single
+# function, or list the functions so dumpable in an executable. You'd think
+# there would be a way to get objdump to do this, but I can't find it.
+
+[ $# -lt 1 ] || [ $# -gt 2 ] && { echo "usage: showasm file function"; exit 1; }
+
+[ ! -f $1 ] && { echo "File $1 not found"; exit 1; }
+
+if [ $# -eq 1 ]
+then
+ objdump -d $1 | sed -n -e 's/^[0-9a-fA-F]* <\(.*\)>:$/\1/p'
+ exit 0
+fi
+
+objdump -d $1 | sed -n -e '/./{H;$!d}' -e "x;/^.[0-9a-fA-F]* <$2>:/p"
+
diff --git a/i/pc104/initrd/conf/busybox/scripts/trylink b/i/pc104/initrd/conf/busybox/scripts/trylink
new file mode 100755
index 0000000..52931b0
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/scripts/trylink
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+debug=false
+
+try() {
+ added="$1"
+ shift
+ $debug && echo "Trying: $* $added"
+ "$@" $added >busybox.map 2>busybox_ld.err \
+ && { rm busybox_ld.err; exit 0; }
+}
+
+try "" "$@"
+try "-lm" "$@"
+try "-lcrypt" "$@"
+try "-Wl,--start-group -lcrypt -lm -Wl,--end-group" "$@"
+# It failed. Let people see the error messages
+cat busybox_ld.err
diff --git a/i/pc104/initrd/conf/busybox/selinux/Config.in b/i/pc104/initrd/conf/busybox/selinux/Config.in
new file mode 100644
index 0000000..6c08e51
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/selinux/Config.in
@@ -0,0 +1,75 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+menu "Selinux Utilities"
+ depends on SELINUX
+
+config CHCON
+ bool "chcon"
+ default n
+ depends on SELINUX
+ help
+ Enable support to change the security context of file.
+
+config FEATURE_CHCON_LONG_OPTIONS
+ bool "Enable long options"
+ default y
+ depends on CHCON && GETOPT_LONG
+ help
+ Support long options for the chcon applet.
+
+config GETENFORCE
+ bool "getenforce"
+ default n
+ depends on SELINUX
+ help
+ Enable support to get the current mode of SELinux.
+
+config GETSEBOOL
+ bool "getsebool"
+ default n
+ depends on SELINUX
+ help
+ Enable support to get SELinux boolean values.
+
+config MATCHPATHCON
+ bool "matchpathcon"
+ default n
+ depends on SELINUX
+ help
+ Enable support to get default security context of the
+ specified path from the file contexts configuration.
+
+config RUNCON
+ bool "runcon"
+ default n
+ depends on SELINUX
+ help
+ Enable support to run command in speficied security context.
+
+config FEATURE_RUNCON_LONG_OPTIONS
+ bool "Enable long options"
+ default y
+ depends on RUNCON && GETOPT_LONG
+ help
+ Support long options for the runcon applet.
+
+config SELINUXENABLED
+ bool "selinuxenabled"
+ default n
+ depends on SELINUX
+ help
+ Enable support for this command to be used within shell scripts
+ to determine if selinux is enabled.
+
+config SETENFORCE
+ bool "setenforce"
+ default n
+ depends on SELINUX
+ help
+ Enable support to modify the mode SELinux is running in.
+
+endmenu
+
diff --git a/i/pc104/initrd/conf/busybox/selinux/Kbuild b/i/pc104/initrd/conf/busybox/selinux/Kbuild
new file mode 100644
index 0000000..398d136
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/selinux/Kbuild
@@ -0,0 +1,15 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+# Copyright (C) 2007 by KaiGai Kohei <kaigai@kaigai.gr.jp>
+#
+# Licensed under the GPL v2, see the file LICENSE in this tarball.
+
+lib-y:=
+lib-$(CONFIG_CHCON) += chcon.o
+lib-$(CONFIG_GETENFORCE) += getenforce.o
+lib-$(CONFIG_GETSEBOOL) += getsebool.o
+lib-$(CONFIG_MATCHPATHCON) += matchpathcon.o
+lib-$(CONFIG_RUNCON) += runcon.o
+lib-$(CONFIG_SELINUXENABLED) += selinuxenabled.o
+lib-$(CONFIG_SETENFORCE) += setenforce.o
diff --git a/i/pc104/initrd/conf/busybox/selinux/chcon.c b/i/pc104/initrd/conf/busybox/selinux/chcon.c
new file mode 100644
index 0000000..72cfa93
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/selinux/chcon.c
@@ -0,0 +1,175 @@
+/*
+ * chcon -- change security context, based on coreutils-5.97-13
+ *
+ * Port to busybox: KaiGai Kohei <kaigai@kaigai.gr.jp>
+ *
+ * Copyright (C) 2006 - 2007 KaiGai Kohei <kaigai@kaigai.gr.jp>
+ */
+#include "busybox.h"
+#include <getopt.h>
+#include <selinux/context.h>
+
+#define OPT_RECURSIVE (1<<0) /* 'R' */
+#define OPT_CHANHES (1<<1) /* 'c' */
+#define OPT_NODEREFERENCE (1<<2) /* 'h' */
+#define OPT_QUIET (1<<3) /* 'f' */
+#define OPT_USER (1<<4) /* 'u' */
+#define OPT_ROLE (1<<5) /* 'r' */
+#define OPT_TYPE (1<<6) /* 't' */
+#define OPT_RANGE (1<<7) /* 'l' */
+#define OPT_VERBOSE (1<<8) /* 'v' */
+#define OPT_REFERENCE ((1<<9) * ENABLE_FEATURE_CHCON_LONG_OPTIONS)
+#define OPT_COMPONENT_SPECIFIED (OPT_USER | OPT_ROLE | OPT_TYPE | OPT_RANGE)
+
+static char *user = NULL;
+static char *role = NULL;
+static char *type = NULL;
+static char *range = NULL;
+static char *specified_context = NULL;
+
+static int change_filedir_context(const char *fname, struct stat *stbuf, void *userData, int depth)
+{
+ context_t context = NULL;
+ security_context_t file_context = NULL;
+ security_context_t context_string;
+ int rc = FALSE;
+ int status = 0;
+
+ if (option_mask32 & OPT_NODEREFERENCE) {
+ status = lgetfilecon(fname, &file_context);
+ } else {
+ status = getfilecon(fname, &file_context);
+ }
+ if (status < 0 && errno != ENODATA) {
+ if ((option_mask32 & OPT_QUIET) == 0)
+ bb_error_msg("cannot obtain security context: %s", fname);
+ goto skip;
+ }
+
+ if (file_context == NULL && specified_context == NULL) {
+ bb_error_msg("cannot apply partial context to unlabeled file %s", fname);
+ goto skip;
+ }
+
+ if (specified_context == NULL) {
+ context = set_security_context_component(file_context,
+ user, role, type, range);
+ if (!context) {
+ bb_error_msg("cannot compute security context from %s", file_context);
+ goto skip;
+ }
+ } else {
+ context = context_new(specified_context);
+ if (!context) {
+ bb_error_msg("invalid context: %s", specified_context);
+ goto skip;
+ }
+ }
+
+ context_string = context_str(context);
+ if (!context_string) {
+ bb_error_msg("cannot obtain security context in text expression");
+ goto skip;
+ }
+
+ if (file_context == NULL || strcmp(context_string, file_context) != 0) {
+ int fail;
+
+ if (option_mask32 & OPT_NODEREFERENCE) {
+ fail = lsetfilecon(fname, context_string);
+ } else {
+ fail = setfilecon(fname, context_string);
+ }
+ if ((option_mask32 & OPT_VERBOSE) || ((option_mask32 & OPT_CHANHES) && !fail)) {
+ printf(!fail
+ ? "context of %s changed to %s\n"
+ : "failed to change context of %s to %s\n",
+ fname, context_string);
+ }
+ if (!fail) {
+ rc = TRUE;
+ } else if ((option_mask32 & OPT_QUIET) == 0) {
+ bb_error_msg("failed to change context of %s to %s",
+ fname, context_string);
+ }
+ } else if (option_mask32 & OPT_VERBOSE) {
+ printf("context of %s retained as %s\n", fname, context_string);
+ rc = TRUE;
+ }
+skip:
+ context_free(context);
+ freecon(file_context);
+
+ return rc;
+}
+
+#if ENABLE_FEATURE_CHCON_LONG_OPTIONS
+static struct option chcon_options[] = {
+ { "recursive", 0, NULL, 'R' },
+ { "changes", 0, NULL, 'c' },
+ { "no-dereference", 0, NULL, 'h' },
+ { "silent", 0, NULL, 'f' },
+ { "quiet", 0, NULL, 'f' },
+ { "user", 1, NULL, 'u' },
+ { "role", 1, NULL, 'r' },
+ { "type", 1, NULL, 't' },
+ { "range", 1, NULL, 'l' },
+ { "verbose", 0, NULL, 'v' },
+ { "reference", 1, NULL, 0xff }, /* no short option */
+ { NULL, 0, NULL, 0 },
+};
+#endif
+
+int chcon_main(int argc, char *argv[]);
+int chcon_main(int argc, char *argv[])
+{
+ char *reference_file;
+ char *fname;
+ int i, errors = 0;
+
+#if ENABLE_FEATURE_CHCON_LONG_OPTIONS
+ applet_long_options = chcon_options;
+#endif
+ opt_complementary = "-1" /* at least 1 param */
+ ":?" /* error if exclusivity constraints are violated */
+#if ENABLE_FEATURE_CHCON_LONG_OPTIONS
+ ":\xff--urtl:u--\xff:r--\xff:t--\xff:l--\xff"
+#endif
+ ":f--v:v--f"; /* 'verbose' and 'quiet' are exclusive */
+ getopt32(argc, argv, "Rchf:u:r:t:l:v",
+ &user, &role, &type, &range, &reference_file);
+ argv += optind;
+
+#if ENABLE_FEATURE_CHCON_LONG_OPTIONS
+ if (option_mask32 & OPT_REFERENCE) {
+ /* FIXME: lgetfilecon() should be used when '-h' is specified.
+ But current implementation follows the original one. */
+ if (getfilecon(reference_file, &specified_context) < 0)
+ bb_perror_msg_and_die("getfilecon('%s') failed", reference_file);
+ } else
+#endif
+ if ((option_mask32 & OPT_COMPONENT_SPECIFIED) == 0) {
+ specified_context = *argv++;
+ /* specified_context is never NULL -
+ * "-1" in opt_complementary prevents this. */
+ if (!argv[0])
+ bb_error_msg_and_die("too few arguments");
+ }
+
+ for (i = 0; (fname = argv[i]) != NULL; i++) {
+ int fname_len = strlen(fname);
+ while (fname_len > 1 && fname[fname_len - 1] == '/')
+ fname_len--;
+ fname[fname_len] = '\0';
+
+ if (recursive_action(fname,
+ option_mask32 & OPT_RECURSIVE,
+ FALSE, /* followLinks */
+ FALSE, /* depthFirst */
+ change_filedir_context,
+ change_filedir_context,
+ NULL, 0) != TRUE)
+ errors = 1;
+ }
+ return errors;
+}
diff --git a/i/pc104/initrd/conf/busybox/selinux/getenforce.c b/i/pc104/initrd/conf/busybox/selinux/getenforce.c
new file mode 100644
index 0000000..865fed9
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/selinux/getenforce.c
@@ -0,0 +1,34 @@
+/*
+ * getenforce
+ *
+ * Based on libselinux 1.33.1
+ * Port to BusyBox Hiroshi Shinji <shiroshi@my.email.ne.jp>
+ *
+ */
+
+#include "busybox.h"
+
+int getenforce_main(int argc, char **argv);
+int getenforce_main(int argc, char **argv)
+{
+ int rc;
+
+ rc = is_selinux_enabled();
+ if (rc < 0)
+ bb_error_msg_and_die("is_selinux_enabled() failed");
+
+ if (rc == 1) {
+ rc = security_getenforce();
+ if (rc < 0)
+ bb_error_msg_and_die("getenforce() failed");
+
+ if (rc)
+ puts("Enforcing");
+ else
+ puts("Permissive");
+ } else {
+ puts("Disabled");
+ }
+
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/selinux/getsebool.c b/i/pc104/initrd/conf/busybox/selinux/getsebool.c
new file mode 100644
index 0000000..0479598
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/selinux/getsebool.c
@@ -0,0 +1,66 @@
+/*
+ * getsebool
+ *
+ * Based on libselinux 1.33.1
+ * Port to BusyBox Hiroshi Shinji <shiroshi@my.email.ne.jp>
+ *
+ */
+
+#include "busybox.h"
+
+int getsebool_main(int argc, char **argv);
+int getsebool_main(int argc, char **argv)
+{
+ int i, rc = 0, active, pending, len = 0;
+ char **names;
+ unsigned opt;
+
+ selinux_or_die();
+ opt = getopt32(argc, argv, "a");
+
+ if (opt) { /* -a */
+ if (argc > 2)
+ bb_show_usage();
+
+ rc = security_get_boolean_names(&names, &len);
+ if (rc)
+ bb_perror_msg_and_die("cannot get boolean names");
+
+ if (!len) {
+ puts("No booleans");
+ return 0;
+ }
+ }
+
+ if (!len) {
+ if (argc < 2)
+ bb_show_usage();
+ len = argc - 1;
+ names = xmalloc(sizeof(char *) * len);
+ for (i = 0; i < len; i++)
+ names[i] = xstrdup(argv[i + 1]);
+ }
+
+ for (i = 0; i < len; i++) {
+ active = security_get_boolean_active(names[i]);
+ if (active < 0) {
+ bb_error_msg_and_die("error getting active value for %s", names[i]);
+ }
+ pending = security_get_boolean_pending(names[i]);
+ if (pending < 0) {
+ bb_error_msg_and_die("error getting pending value for %s", names[i]);
+ }
+ printf("%s --> %s", names[i], (active ? "on" : "off"));
+ if (pending != active)
+ printf(" pending: %s", (pending ? "on" : "off"));
+ putchar('\n');
+ }
+
+ if (ENABLE_FEATURE_CLEAN_UP) {
+ for (i = 0; i < len; i++)
+ free(names[i]);
+ free(names);
+ }
+
+ return rc;
+}
diff --git a/i/pc104/initrd/conf/busybox/selinux/matchpathcon.c b/i/pc104/initrd/conf/busybox/selinux/matchpathcon.c
new file mode 100644
index 0000000..83ea75d
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/selinux/matchpathcon.c
@@ -0,0 +1,86 @@
+/* matchpathcon - get the default security context for the specified
+ * path from the file contexts configuration.
+ * based on libselinux-1.32
+ * Port to busybox: KaiGai Kohei <kaigai@kaigai.gr.jp>
+ *
+ */
+#include "busybox.h"
+
+static int print_matchpathcon(char *path, int noprint)
+{
+ char *buf;
+ int rc = matchpathcon(path, 0, &buf);
+ if (rc < 0) {
+ bb_perror_msg("matchpathcon(%s) failed", path);
+ return 1;
+ }
+ if (!noprint)
+ printf("%s\t%s\n", path, buf);
+ else
+ printf("%s\n", buf);
+
+ freecon(buf);
+ return 0;
+}
+
+#define OPT_NOT_PRINT (1<<0) /* -n */
+#define OPT_NOT_TRANS (1<<1) /* -N */
+#define OPT_FCONTEXT (1<<2) /* -f */
+#define OPT_PREFIX (1<<3) /* -p */
+#define OPT_VERIFY (1<<4) /* -V */
+
+int matchpathcon_main(int argc, char **argv);
+int matchpathcon_main(int argc, char **argv)
+{
+ int error = 0;
+ unsigned opts;
+ char *fcontext, *prefix, *path;
+
+ opt_complementary = "-1" /* at least one param reqd */
+ ":?:f--p:p--f"; /* mutually exclusive */
+ opts = getopt32(argc, argv, "nNf:p:V", &fcontext, &prefix);
+ argv += optind;
+
+ if (opts & OPT_NOT_TRANS) {
+ set_matchpathcon_flags(MATCHPATHCON_NOTRANS);
+ }
+ if (opts & OPT_FCONTEXT) {
+ if (matchpathcon_init(fcontext))
+ bb_perror_msg_and_die("error while processing %s", fcontext);
+ }
+ if (opts & OPT_PREFIX) {
+ if (matchpathcon_init_prefix(NULL, prefix))
+ bb_perror_msg_and_die("error while processing %s", prefix);
+ }
+
+ while((path = *argv++) != NULL) {
+ security_context_t con;
+ int rc;
+
+ if (!(opts & OPT_VERIFY)) {
+ error += print_matchpathcon(path, opts & OPT_NOT_PRINT);
+ continue;
+ }
+
+ if (selinux_file_context_verify(path, 0)) {
+ printf("%s verified\n", path);
+ continue;
+ }
+
+ if (opts & OPT_NOT_TRANS)
+ rc = lgetfilecon_raw(path, &con);
+ else
+ rc = lgetfilecon(path, &con);
+
+ if (rc >= 0) {
+ printf("%s has context %s, should be ", path, con);
+ error += print_matchpathcon(path, 1);
+ freecon(con);
+ continue;
+ }
+ printf("actual context unknown: %s, should be ", strerror(errno));
+ error += print_matchpathcon(path, 1);
+ }
+ matchpathcon_fini();
+ return error;
+}
diff --git a/i/pc104/initrd/conf/busybox/selinux/runcon.c b/i/pc104/initrd/conf/busybox/selinux/runcon.c
new file mode 100644
index 0000000..8888ccc
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/selinux/runcon.c
@@ -0,0 +1,137 @@
+/*
+ * runcon [ context |
+ * ( [ -c ] [ -r role ] [-t type] [ -u user ] [ -l levelrange ] )
+ * command [arg1 [arg2 ...] ]
+ *
+ * attempt to run the specified command with the specified context.
+ *
+ * -r role : use the current context with the specified role
+ * -t type : use the current context with the specified type
+ * -u user : use the current context with the specified user
+ * -l level : use the current context with the specified level range
+ * -c : compute process transition context before modifying
+ *
+ * Contexts are interpreted as follows:
+ *
+ * Number of MLS
+ * components system?
+ *
+ * 1 - type
+ * 2 - role:type
+ * 3 Y role:type:range
+ * 3 N user:role:type
+ * 4 Y user:role:type:range
+ * 4 N error
+ *
+ * Port to busybox: KaiGai Kohei <kaigai@kaigai.gr.jp>
+ * - based on coreutils-5.97 (in Fedora Core 6)
+ */
+#include "busybox.h"
+#include <getopt.h>
+#include <selinux/context.h>
+#include <selinux/flask.h>
+
+static context_t runcon_compute_new_context(char *user, char *role, char *type, char *range,
+ char *command, int compute_trans)
+{
+ context_t con;
+ security_context_t cur_context;
+
+ if (getcon(&cur_context))
+ bb_error_msg_and_die("cannot get current context");
+
+ if (compute_trans) {
+ security_context_t file_context, new_context;
+
+ if (getfilecon(command, &file_context) < 0)
+ bb_error_msg_and_die("cannot retrieve attributes of '%s'",
+ command);
+ if (security_compute_create(cur_context, file_context,
+ SECCLASS_PROCESS, &new_context))
+ bb_error_msg_and_die("unable to compute a new context");
+ cur_context = new_context;
+ }
+
+ con = context_new(cur_context);
+ if (!con)
+ bb_error_msg_and_die("'%s' is not a valid context", cur_context);
+ if (user && context_user_set(con, user))
+ bb_error_msg_and_die("failed to set new user '%s'", user);
+ if (type && context_type_set(con, type))
+ bb_error_msg_and_die("failed to set new type '%s'", type);
+ if (range && context_range_set(con, range))
+ bb_error_msg_and_die("failed to set new range '%s'", range);
+ if (role && context_role_set(con, role))
+ bb_error_msg_and_die("failed to set new role '%s'", role);
+
+ return con;
+}
+
+#if ENABLE_FEATURE_RUNCON_LONG_OPTIONS
+static const struct option runcon_options[] = {
+ { "user", 1, NULL, 'u' },
+ { "role", 1, NULL, 'r' },
+ { "type", 1, NULL, 't' },
+ { "range", 1, NULL, 'l' },
+ { "compute", 0, NULL, 'c' },
+ { "help", 0, NULL, 'h' },
+ { NULL, 0, NULL, 0 },
+};
+#endif
+
+#define OPTS_ROLE (1<<0) /* r */
+#define OPTS_TYPE (1<<1) /* t */
+#define OPTS_USER (1<<2) /* u */
+#define OPTS_RANGE (1<<3) /* l */
+#define OPTS_COMPUTE (1<<4) /* c */
+#define OPTS_HELP (1<<5) /* h */
+#define OPTS_CONTEXT_COMPONENT (OPTS_ROLE | OPTS_TYPE | OPTS_USER | OPTS_RANGE)
+
+int runcon_main(int argc, char *argv[]);
+int runcon_main(int argc, char *argv[])
+{
+ char *role = NULL;
+ char *range = NULL;
+ char *user = NULL;
+ char *type = NULL;
+ char *context = NULL;
+ unsigned opts;
+ context_t con;
+
+ selinux_or_die();
+
+#if ENABLE_FEATURE_RUNCON_LONG_OPTIONS
+ applet_long_options = runcon_options;
+#endif
+ opt_complementary = "-1";
+ opts = getopt32(argc, argv, "r:t:u:l:ch", &role, &type, &user, &range);
+ argv += optind;
+
+ if (!(opts & OPTS_CONTEXT_COMPONENT)) {
+ context = *argv++;
+ if (!argv[0])
+ bb_error_msg_and_die("no command found");
+ }
+
+ if (context) {
+ con = context_new(context);
+ if (!con)
+ bb_error_msg_and_die("'%s' is not a valid context", context);
+ } else {
+ con = runcon_compute_new_context(user, role, type, range,
+ argv[0], opts & OPTS_COMPUTE);
+ }
+
+ if (security_check_context(context_str(con)))
+ bb_error_msg_and_die("'%s' is not a valid context",
+ context_str(con));
+
+ if (setexeccon(context_str(con)))
+ bb_error_msg_and_die("cannot set up security context '%s'",
+ context_str(con));
+
+ execvp(argv[0], argv);
+
+ bb_perror_msg_and_die("cannot execute '%s'", argv[0]);
+ return 1;
+}
diff --git a/i/pc104/initrd/conf/busybox/selinux/selinuxenabled.c b/i/pc104/initrd/conf/busybox/selinux/selinuxenabled.c
new file mode 100644
index 0000000..c93ba70
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/selinux/selinuxenabled.c
@@ -0,0 +1,14 @@
+/*
+ * selinuxenabled
+ *
+ * Based on libselinux 1.33.1
+ * Port to BusyBox Hiroshi Shinji <shiroshi@my.email.ne.jp>
+ *
+ */
+#include "busybox.h"
+
+int selinuxenabled_main(int argc, char **argv);
+int selinuxenabled_main(int argc, char **argv)
+{
+ return !is_selinux_enabled();
+}
diff --git a/i/pc104/initrd/conf/busybox/selinux/setenforce.c b/i/pc104/initrd/conf/busybox/selinux/setenforce.c
new file mode 100644
index 0000000..9204fcc
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/selinux/setenforce.c
@@ -0,0 +1,42 @@
+/*
+ * setenforce
+ *
+ * Based on libselinux 1.33.1
+ * Port to BusyBox Hiroshi Shinji <shiroshi@my.email.ne.jp>
+ *
+ */
+
+#include "busybox.h"
+
+/* These strings are arranged so that odd ones
+ * result in security_setenforce(1) being done,
+ * the rest will do security_setenforce(0) */
+static const char *const setenforce_cmd[] = {
+ "0",
+ "1",
+ "permissive",
+ "enforcing",
+ NULL,
+};
+
+int setenforce_main(int argc, char **argv);
+int setenforce_main(int argc, char **argv)
+{
+ int i, rc;
+
+ if (argc != 2)
+ bb_show_usage();
+
+ selinux_or_die();
+
+ for (i = 0; setenforce_cmd[i]; i++) {
+ if (strcasecmp(argv[1], setenforce_cmd[i]) != 0)
+ continue;
+ rc = security_setenforce(i & 1);
+ if (rc < 0)
+ bb_perror_msg_and_die("setenforce() failed");
+ return 0;
+ }
+
+ bb_show_usage();
+}
diff --git a/i/pc104/initrd/conf/busybox/shell/Config.in b/i/pc104/initrd/conf/busybox/shell/Config.in
new file mode 100644
index 0000000..4b7270e
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/shell/Config.in
@@ -0,0 +1,243 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+menu "Shells"
+
+choice
+ prompt "Choose your default shell"
+ default FEATURE_SH_IS_NONE
+ help
+ Choose a shell. The ash shell is the most bash compatible
+ and full featured one.
+
+config FEATURE_SH_IS_ASH
+ select ASH
+ bool "ash"
+
+config FEATURE_SH_IS_HUSH
+ select HUSH
+ bool "hush"
+
+config FEATURE_SH_IS_LASH
+ select LASH
+ bool "lash"
+
+config FEATURE_SH_IS_MSH
+ select MSH
+ bool "msh"
+
+config FEATURE_SH_IS_NONE
+ bool "none"
+
+endchoice
+
+config ASH
+ bool "ash"
+ default n
+ select TEST
+ help
+ Tha 'ash' shell adds about 60k in the default configuration and is
+ the most complete and most pedantically correct shell included with
+ busybox. This shell is actually a derivative of the Debian 'dash'
+ shell (by Herbert Xu), which was created by porting the 'ash' shell
+ (written by Kenneth Almquist) from NetBSD.
+
+comment "Ash Shell Options"
+ depends on ASH
+
+config ASH_JOB_CONTROL
+ bool "Job control"
+ default y
+ depends on ASH
+ help
+ Enable job control in the ash shell.
+
+config ASH_READ_NCHARS
+ bool "'read -n N' and 'read -s' support"
+ default n
+ depends on ASH
+ help
+ 'read -n N' will return a value after N characters have been read.
+ 'read -s' will read without echoing the user's input.
+
+config ASH_READ_TIMEOUT
+ bool "'read -t S' support."
+ default n
+ depends on ASH
+ help
+ 'read -t S' will return a value after S seconds have passed.
+ This implementation will allow fractional seconds, expressed
+ as a decimal fraction, e.g. 'read -t 2.5 foo'.
+
+config ASH_ALIAS
+ bool "alias support"
+ default y
+ depends on ASH
+ help
+ Enable alias support in the ash shell.
+
+config ASH_MATH_SUPPORT
+ bool "Posix math support"
+ default y
+ depends on ASH
+ help
+ Enable math support in the ash shell.
+
+config ASH_MATH_SUPPORT_64
+ bool "Extend Posix math support to 64 bit"
+ default n
+ depends on ASH_MATH_SUPPORT
+ help
+ Enable 64-bit math support in the ash shell. This will make
+ the shell slightly larger, but will allow computation with very
+ large numbers.
+
+config ASH_GETOPTS
+ bool "Builtin getopt to parse positional parameters"
+ default n
+ depends on ASH
+ help
+ Enable getopts builtin in the ash shell.
+
+config ASH_BUILTIN_ECHO
+ bool "Builtin version of 'echo'"
+ default y
+ select ECHO
+ depends on ASH
+ help
+ Enable support for echo, builtin to ash.
+
+config ASH_BUILTIN_TEST
+ bool "Builtin version of 'test'"
+ default y
+ select TEST
+ depends on ASH
+ help
+ Enable support for test, builtin to ash.
+
+config ASH_CMDCMD
+ bool "'command' command to override shell builtins"
+ default n
+ depends on ASH
+ help
+ Enable support for the ash 'command' builtin, which allows
+ you to run the specified command with the specified arguments,
+ even when there is an ash builtin command with the same name.
+
+config ASH_MAIL
+ bool "Check for new mail on interactive shells"
+ default y
+ depends on ASH
+ help
+ Enable "check for new mail" in the ash shell.
+
+config ASH_OPTIMIZE_FOR_SIZE
+ bool "Optimize for size instead of speed"
+ default y
+ depends on ASH
+ help
+ Compile ash for reduced size at the price of speed.
+
+config ASH_RANDOM_SUPPORT
+ bool "Pseudorandom generator and variable $RANDOM"
+ default n
+ depends on ASH
+ help
+ Enable pseudorandom generator and dynamic variable "$RANDOM".
+ Each read of "$RANDOM" will generate a new pseudorandom value.
+ You can reset the generator by using a specified start value.
+ After "unset RANDOM" then generator will switch off and this
+ variable will no longer have special treatment.
+
+config ASH_EXPAND_PRMT
+ bool "Expand prompt string"
+ default n
+ depends on ASH
+ help
+ "PS#" may be contain volatile content, such as backquote commands.
+ This option recreates the prompt string from the environment
+ variable each time it is displayed.
+
+config HUSH
+ bool "hush"
+ default n
+ select TRUE
+ select FALSE
+ select TEST
+ help
+ hush is a very small shell (just 18k) and it has fairly complete
+ Bourne shell grammar. It even handles all the normal flow control
+ options such as if/then/elif/else/fi, for/in/do/done, while loops,
+ etc.
+
+ It does not handle case/esac, select, function, here documents ( <<
+ word ), arithmetic expansion, aliases, brace expansion, tilde
+ expansion, &> and >& redirection of stdout+stderr, etc.
+
+
+config LASH
+ bool "lash"
+ default n
+ select TRUE
+ select FALSE
+ select TEST
+ help
+ lash is the very smallest shell (adds just 10k) and it is quite
+ usable as a command prompt, but it is not suitable for any but the
+ most trivial scripting (such as an initrd that calls insmod a few
+ times) since it does not understand any Bourne shell grammar. It
+ does handle pipes, redirects, and job control though. Adding in
+ command editing makes it a very nice lightweight command prompt.
+
+
+config MSH
+ bool "msh"
+ default n
+ select TRUE
+ select FALSE
+ select TEST
+ help
+ The minix shell (adds just 30k) is quite complete and handles things
+ like for/do/done, case/esac and all the things you expect a Bourne
+ shell to do. It is not always pedantically correct about Bourne
+ shell grammar (try running the shell testscript "tests/sh.testcases"
+ on it and compare vs bash) but for most things it works quite well.
+ It also uses only vfork, so it can be used on uClinux systems.
+
+comment "Bourne Shell Options"
+ depends on MSH || LASH || HUSH || ASH
+
+config FEATURE_SH_EXTRA_QUIET
+ bool "Hide message on interactive shell startup"
+ default n
+ depends on MSH || LASH || HUSH || ASH
+ help
+ Remove the busybox introduction when starting a shell.
+
+config FEATURE_SH_STANDALONE_SHELL
+ bool "Standalone shell"
+ default n
+ depends on MSH || LASH || HUSH || ASH
+ help
+ This option causes the selected busybox shell to use busybox applets
+ in preference to executables in the PATH whenever possible. For
+ example, entering the command 'ifconfig' into the shell would cause
+ busybox to use the ifconfig busybox applet. Specifying the fully
+ qualified executable name, such as '/sbin/ifconfig' will still
+ execute the /sbin/ifconfig executable on the filesystem. This option
+ is generally used when creating a statically linked version of busybox
+ for use as a rescue shell, in the event that you screw up your system.
+
+ Note that this will *also* cause applets to take precedence
+ over shell builtins of the same name. So turning this on will
+ eliminate any performance gained by turning on the builtin "echo"
+ and "test" commands in ash.
+
+ Note that when using this option, the shell will attempt to directly
+ run '/bin/busybox'. If you do not have the busybox binary sitting in
+ that exact location with that exact name, this option will not work at
+ all.
+
+endmenu
diff --git a/i/pc104/initrd/conf/busybox/shell/Kbuild b/i/pc104/initrd/conf/busybox/shell/Kbuild
new file mode 100644
index 0000000..6b58040
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/shell/Kbuild
@@ -0,0 +1,11 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under the GPL v2, see the file LICENSE in this tarball.
+
+lib-y:=
+lib-$(CONFIG_ASH) += ash.o
+lib-$(CONFIG_HUSH) += hush.o
+lib-$(CONFIG_LASH) += lash.o
+lib-$(CONFIG_MSH) += msh.o
diff --git a/i/pc104/initrd/conf/busybox/shell/ash.c b/i/pc104/initrd/conf/busybox/shell/ash.c
new file mode 100644
index 0000000..a5ffaaf
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/shell/ash.c
@@ -0,0 +1,13003 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ash shell port for busybox
+ *
+ * Copyright (c) 1989, 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Copyright (c) 1997-2005 Herbert Xu <herbert@gondor.apana.org.au>
+ * was re-ported from NetBSD and debianized.
+ *
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ *
+ * Original BSD copyright notice is retained at the end of this file.
+ */
+
+/*
+ * rewrite arith.y to micro stack based cryptic algorithm by
+ * Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
+ *
+ * Modified by Paul Mundt <lethal@linux-sh.org> (c) 2004 to support
+ * dynamic variables.
+ *
+ * Modified by Vladimir Oleynik <dzo@simtreas.ru> (c) 2001-2005 to be
+ * used in busybox and size optimizations,
+ * rewrote arith (see notes to this), added locale support,
+ * rewrote dynamic variables.
+ *
+ */
+
+/*
+ * The follow should be set to reflect the type of system you have:
+ * JOBS -> 1 if you have Berkeley job control, 0 otherwise.
+ * define SYSV if you are running under System V.
+ * define DEBUG=1 to compile in debugging ('set -o debug' to turn on)
+ * define DEBUG=2 to compile in and turn on debugging.
+ *
+ * When debugging is on, debugging info will be written to ./trace and
+ * a quit signal will generate a core dump.
+ */
+#define DEBUG 0
+#define IFS_BROKEN
+#define PROFILE 0
+#if ENABLE_ASH_JOB_CONTROL
+#define JOBS 1
+#else
+#define JOBS 0
+#endif
+
+#if DEBUG
+#define _GNU_SOURCE
+#endif
+#include "busybox.h"
+#include <paths.h>
+#include <setjmp.h>
+#include <fnmatch.h>
+#if JOBS || ENABLE_ASH_READ_NCHARS
+#include <termios.h>
+#endif
+
+#if defined(__uClinux__)
+#error "Do not even bother, ash will not run on uClinux"
+#endif
+
+
+/* ============ Misc helpers */
+
+#define xbarrier() do { __asm__ __volatile__ ("": : :"memory"); } while (0)
+
+/* C99 say: "char" declaration may be signed or unsigned default */
+#define signed_char2int(sc) ((int)((signed char)sc))
+
+
+/* ============ Shell options */
+
+static const char *const optletters_optnames[] = {
+ "e" "errexit",
+ "f" "noglob",
+ "I" "ignoreeof",
+ "i" "interactive",
+ "m" "monitor",
+ "n" "noexec",
+ "s" "stdin",
+ "x" "xtrace",
+ "v" "verbose",
+ "C" "noclobber",
+ "a" "allexport",
+ "b" "notify",
+ "u" "nounset",
+ "\0" "vi",
+#if DEBUG
+ "\0" "nolog",
+ "\0" "debug",
+#endif
+};
+
+#define optletters(n) optletters_optnames[(n)][0]
+#define optnames(n) (&optletters_optnames[(n)][1])
+
+#define NOPTS (sizeof(optletters_optnames)/sizeof(optletters_optnames[0]))
+
+static char optlist[NOPTS];
+
+#define eflag optlist[0]
+#define fflag optlist[1]
+#define Iflag optlist[2]
+#define iflag optlist[3]
+#define mflag optlist[4]
+#define nflag optlist[5]
+#define sflag optlist[6]
+#define xflag optlist[7]
+#define vflag optlist[8]
+#define Cflag optlist[9]
+#define aflag optlist[10]
+#define bflag optlist[11]
+#define uflag optlist[12]
+#define viflag optlist[13]
+#if DEBUG
+#define nolog optlist[14]
+#define debug optlist[15]
+#endif
+
+
+/* ============ Misc data */
+
+#ifdef __GLIBC__
+/* glibc sucks */
+static int *dash_errno;
+#undef errno
+#define errno (*dash_errno)
+#endif
+
+static char nullstr[1]; /* zero length string */
+static const char homestr[] = "HOME";
+static const char snlfmt[] = "%s\n";
+static const char illnum[] = "Illegal number: %s";
+
+static char *minusc; /* argument to -c option */
+
+static int isloginsh;
+/* pid of main shell */
+static int rootpid;
+/* shell level: 0 for the main shell, 1 for its children, and so on */
+static int shlvl;
+#define rootshell (!shlvl)
+/* trap handler commands */
+static char *trap[NSIG];
+/* current value of signal */
+static char sigmode[NSIG - 1];
+/* indicates specified signal received */
+static char gotsig[NSIG - 1];
+static char *arg0; /* value of $0 */
+
+
+/* ============ Interrupts / exceptions */
+
+/*
+ * We enclose jmp_buf in a structure so that we can declare pointers to
+ * jump locations. The global variable handler contains the location to
+ * jump to when an exception occurs, and the global variable exception
+ * contains a code identifying the exception. To implement nested
+ * exception handlers, the user should save the value of handler on entry
+ * to an inner scope, set handler to point to a jmploc structure for the
+ * inner scope, and restore handler on exit from the scope.
+ */
+struct jmploc {
+ jmp_buf loc;
+};
+static struct jmploc *exception_handler;
+static int exception;
+/* exceptions */
+#define EXINT 0 /* SIGINT received */
+#define EXERROR 1 /* a generic error */
+#define EXSHELLPROC 2 /* execute a shell procedure */
+#define EXEXEC 3 /* command execution failed */
+#define EXEXIT 4 /* exit the shell */
+#define EXSIG 5 /* trapped signal in wait(1) */
+static volatile int suppressint;
+static volatile sig_atomic_t intpending;
+/* do we generate EXSIG events */
+static int exsig;
+/* last pending signal */
+static volatile sig_atomic_t pendingsig;
+
+/*
+ * Sigmode records the current value of the signal handlers for the various
+ * modes. A value of zero means that the current handler is not known.
+ * S_HARD_IGN indicates that the signal was ignored on entry to the shell,
+ */
+
+#define S_DFL 1 /* default signal handling (SIG_DFL) */
+#define S_CATCH 2 /* signal is caught */
+#define S_IGN 3 /* signal is ignored (SIG_IGN) */
+#define S_HARD_IGN 4 /* signal is ignored permenantly */
+#define S_RESET 5 /* temporary - to reset a hard ignored sig */
+
+/*
+ * These macros allow the user to suspend the handling of interrupt signals
+ * over a period of time. This is similar to SIGHOLD to or sigblock, but
+ * much more efficient and portable. (But hacking the kernel is so much
+ * more fun than worrying about efficiency and portability. :-))
+ */
+#define INT_OFF \
+ do { \
+ suppressint++; \
+ xbarrier(); \
+ } while (0)
+
+/*
+ * Called to raise an exception. Since C doesn't include exceptions, we
+ * just do a longjmp to the exception handler. The type of exception is
+ * stored in the global variable "exception".
+ */
+static void raise_exception(int) ATTRIBUTE_NORETURN;
+static void
+raise_exception(int e)
+{
+#if DEBUG
+ if (exception_handler == NULL)
+ abort();
+#endif
+ INT_OFF;
+ exception = e;
+ longjmp(exception_handler->loc, 1);
+}
+
+/*
+ * Called from trap.c when a SIGINT is received. (If the user specifies
+ * that SIGINT is to be trapped or ignored using the trap builtin, then
+ * this routine is not called.) Suppressint is nonzero when interrupts
+ * are held using the INT_OFF macro. (The test for iflag is just
+ * defensive programming.)
+ */
+static void raise_interrupt(void) ATTRIBUTE_NORETURN;
+static void
+raise_interrupt(void)
+{
+ int i;
+ sigset_t mask;
+
+ intpending = 0;
+ /* Signal is not automatically re-enabled after it is raised,
+ * do it ourself */
+ sigemptyset(&mask);
+ sigprocmask(SIG_SETMASK, &mask, 0);
+ /* pendingsig = 0; - now done in onsig() */
+
+ i = EXSIG;
+ if (gotsig[SIGINT - 1] && !trap[SIGINT]) {
+ if (!(rootshell && iflag)) {
+ signal(SIGINT, SIG_DFL);
+ raise(SIGINT);
+ }
+ i = EXINT;
+ }
+ raise_exception(i);
+ /* NOTREACHED */
+}
+
+#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
+static void
+int_on(void)
+{
+ if (--suppressint == 0 && intpending) {
+ raise_interrupt();
+ }
+}
+#define INT_ON int_on()
+static void
+force_int_on(void)
+{
+ suppressint = 0;
+ if (intpending)
+ raise_interrupt();
+}
+#define FORCE_INT_ON force_int_on()
+#else
+#define INT_ON \
+ do { \
+ xbarrier(); \
+ if (--suppressint == 0 && intpending) \
+ raise_interrupt(); \
+ } while (0)
+#define FORCE_INT_ON \
+ do { \
+ xbarrier(); \
+ suppressint = 0; \
+ if (intpending) \
+ raise_interrupt(); \
+ } while (0)
+#endif /* ASH_OPTIMIZE_FOR_SIZE */
+
+#define SAVE_INT(v) ((v) = suppressint)
+
+#define RESTORE_INT(v) \
+ do { \
+ xbarrier(); \
+ suppressint = (v); \
+ if (suppressint == 0 && intpending) \
+ raise_interrupt(); \
+ } while (0)
+
+#define EXSIGON \
+ do { \
+ exsig++; \
+ xbarrier(); \
+ if (pendingsig) \
+ raise_exception(EXSIG); \
+ } while (0)
+/* EXSIG is turned off by evalbltin(). */
+
+/*
+ * Ignore a signal. Only one usage site - in forkchild()
+ */
+static void
+ignoresig(int signo)
+{
+ if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
+ signal(signo, SIG_IGN);
+ }
+ sigmode[signo - 1] = S_HARD_IGN;
+}
+
+/*
+ * Signal handler. Only one usage site - in setsignal()
+ */
+static void
+onsig(int signo)
+{
+ gotsig[signo - 1] = 1;
+ pendingsig = signo;
+
+ if (exsig || (signo == SIGINT && !trap[SIGINT])) {
+ if (!suppressint) {
+ pendingsig = 0;
+ raise_interrupt();
+ }
+ intpending = 1;
+ }
+}
+
+
+/* ============ Stdout/stderr output */
+
+static void
+outstr(const char *p, FILE *file)
+{
+ INT_OFF;
+ fputs(p, file);
+ INT_ON;
+}
+
+static void
+flush_stdout_stderr(void)
+{
+ INT_OFF;
+ fflush(stdout);
+ fflush(stderr);
+ INT_ON;
+}
+
+static void
+flush_stderr(void)
+{
+ INT_OFF;
+ fflush(stderr);
+ INT_ON;
+}
+
+static void
+outcslow(int c, FILE *dest)
+{
+ INT_OFF;
+ putc(c, dest);
+ fflush(dest);
+ INT_ON;
+}
+
+static int out1fmt(const char *, ...) __attribute__((__format__(__printf__,1,2)));
+static int
+out1fmt(const char *fmt, ...)
+{
+ va_list ap;
+ int r;
+
+ INT_OFF;
+ va_start(ap, fmt);
+ r = vprintf(fmt, ap);
+ va_end(ap);
+ INT_ON;
+ return r;
+}
+
+static int fmtstr(char *, size_t, const char *, ...) __attribute__((__format__(__printf__,3,4)));
+static int
+fmtstr(char *outbuf, size_t length, const char *fmt, ...)
+{
+ va_list ap;
+ int ret;
+
+ va_start(ap, fmt);
+ INT_OFF;
+ ret = vsnprintf(outbuf, length, fmt, ap);
+ va_end(ap);
+ INT_ON;
+ return ret;
+}
+
+static void
+out1str(const char *p)
+{
+ outstr(p, stdout);
+}
+
+static void
+out2str(const char *p)
+{
+ outstr(p, stderr);
+ flush_stderr();
+}
+
+
+/* ============ Parser structures */
+
+/* control characters in argument strings */
+#define CTLESC '\201' /* escape next character */
+#define CTLVAR '\202' /* variable defn */
+#define CTLENDVAR '\203'
+#define CTLBACKQ '\204'
+#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */
+/* CTLBACKQ | CTLQUOTE == '\205' */
+#define CTLARI '\206' /* arithmetic expression */
+#define CTLENDARI '\207'
+#define CTLQUOTEMARK '\210'
+
+/* variable substitution byte (follows CTLVAR) */
+#define VSTYPE 0x0f /* type of variable substitution */
+#define VSNUL 0x10 /* colon--treat the empty string as unset */
+#define VSQUOTE 0x80 /* inside double quotes--suppress splitting */
+
+/* values of VSTYPE field */
+#define VSNORMAL 0x1 /* normal variable: $var or ${var} */
+#define VSMINUS 0x2 /* ${var-text} */
+#define VSPLUS 0x3 /* ${var+text} */
+#define VSQUESTION 0x4 /* ${var?message} */
+#define VSASSIGN 0x5 /* ${var=text} */
+#define VSTRIMRIGHT 0x6 /* ${var%pattern} */
+#define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */
+#define VSTRIMLEFT 0x8 /* ${var#pattern} */
+#define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */
+#define VSLENGTH 0xa /* ${#var} */
+
+static const char dolatstr[] = { CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0' };
+
+#define NCMD 0
+#define NPIPE 1
+#define NREDIR 2
+#define NBACKGND 3
+#define NSUBSHELL 4
+#define NAND 5
+#define NOR 6
+#define NSEMI 7
+#define NIF 8
+#define NWHILE 9
+#define NUNTIL 10
+#define NFOR 11
+#define NCASE 12
+#define NCLIST 13
+#define NDEFUN 14
+#define NARG 15
+#define NTO 16
+#define NCLOBBER 17
+#define NFROM 18
+#define NFROMTO 19
+#define NAPPEND 20
+#define NTOFD 21
+#define NFROMFD 22
+#define NHERE 23
+#define NXHERE 24
+#define NNOT 25
+
+union node;
+
+struct ncmd {
+ int type;
+ union node *assign;
+ union node *args;
+ union node *redirect;
+};
+
+struct npipe {
+ int type;
+ int backgnd;
+ struct nodelist *cmdlist;
+};
+
+struct nredir {
+ int type;
+ union node *n;
+ union node *redirect;
+};
+
+struct nbinary {
+ int type;
+ union node *ch1;
+ union node *ch2;
+};
+
+struct nif {
+ int type;
+ union node *test;
+ union node *ifpart;
+ union node *elsepart;
+};
+
+struct nfor {
+ int type;
+ union node *args;
+ union node *body;
+ char *var;
+};
+
+struct ncase {
+ int type;
+ union node *expr;
+ union node *cases;
+};
+
+struct nclist {
+ int type;
+ union node *next;
+ union node *pattern;
+ union node *body;
+};
+
+struct narg {
+ int type;
+ union node *next;
+ char *text;
+ struct nodelist *backquote;
+};
+
+struct nfile {
+ int type;
+ union node *next;
+ int fd;
+ union node *fname;
+ char *expfname;
+};
+
+struct ndup {
+ int type;
+ union node *next;
+ int fd;
+ int dupfd;
+ union node *vname;
+};
+
+struct nhere {
+ int type;
+ union node *next;
+ int fd;
+ union node *doc;
+};
+
+struct nnot {
+ int type;
+ union node *com;
+};
+
+union node {
+ int type;
+ struct ncmd ncmd;
+ struct npipe npipe;
+ struct nredir nredir;
+ struct nbinary nbinary;
+ struct nif nif;
+ struct nfor nfor;
+ struct ncase ncase;
+ struct nclist nclist;
+ struct narg narg;
+ struct nfile nfile;
+ struct ndup ndup;
+ struct nhere nhere;
+ struct nnot nnot;
+};
+
+struct nodelist {
+ struct nodelist *next;
+ union node *n;
+};
+
+struct funcnode {
+ int count;
+ union node n;
+};
+
+/*
+ * Free a parse tree.
+ */
+static void
+freefunc(struct funcnode *f)
+{
+ if (f && --f->count < 0)
+ free(f);
+}
+
+
+/* ============ Debugging output */
+
+#if DEBUG
+
+static FILE *tracefile;
+
+static void
+trace_printf(const char *fmt, ...)
+{
+ va_list va;
+
+ if (debug != 1)
+ return;
+ va_start(va, fmt);
+ vfprintf(tracefile, fmt, va);
+ va_end(va);
+}
+
+static void
+trace_vprintf(const char *fmt, va_list va)
+{
+ if (debug != 1)
+ return;
+ vfprintf(tracefile, fmt, va);
+}
+
+static void
+trace_puts(const char *s)
+{
+ if (debug != 1)
+ return;
+ fputs(s, tracefile);
+}
+
+static void
+trace_puts_quoted(char *s)
+{
+ char *p;
+ char c;
+
+ if (debug != 1)
+ return;
+ putc('"', tracefile);
+ for (p = s; *p; p++) {
+ switch (*p) {
+ case '\n': c = 'n'; goto backslash;
+ case '\t': c = 't'; goto backslash;
+ case '\r': c = 'r'; goto backslash;
+ case '"': c = '"'; goto backslash;
+ case '\\': c = '\\'; goto backslash;
+ case CTLESC: c = 'e'; goto backslash;
+ case CTLVAR: c = 'v'; goto backslash;
+ case CTLVAR+CTLQUOTE: c = 'V'; goto backslash;
+ case CTLBACKQ: c = 'q'; goto backslash;
+ case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash;
+ backslash:
+ putc('\\', tracefile);
+ putc(c, tracefile);
+ break;
+ default:
+ if (*p >= ' ' && *p <= '~')
+ putc(*p, tracefile);
+ else {
+ putc('\\', tracefile);
+ putc(*p >> 6 & 03, tracefile);
+ putc(*p >> 3 & 07, tracefile);
+ putc(*p & 07, tracefile);
+ }
+ break;
+ }
+ }
+ putc('"', tracefile);
+}
+
+static void
+trace_puts_args(char **ap)
+{
+ if (debug != 1)
+ return;
+ if (!*ap)
+ return;
+ while (1) {
+ trace_puts_quoted(*ap);
+ if (!*++ap) {
+ putc('\n', tracefile);
+ break;
+ }
+ putc(' ', tracefile);
+ }
+}
+
+static void
+opentrace(void)
+{
+ char s[100];
+#ifdef O_APPEND
+ int flags;
+#endif
+
+ if (debug != 1) {
+ if (tracefile)
+ fflush(tracefile);
+ /* leave open because libedit might be using it */
+ return;
+ }
+ strcpy(s, "./trace");
+ if (tracefile) {
+ if (!freopen(s, "a", tracefile)) {
+ fprintf(stderr, "Can't re-open %s\n", s);
+ debug = 0;
+ return;
+ }
+ } else {
+ tracefile = fopen(s, "a");
+ if (tracefile == NULL) {
+ fprintf(stderr, "Can't open %s\n", s);
+ debug = 0;
+ return;
+ }
+ }
+#ifdef O_APPEND
+ flags = fcntl(fileno(tracefile), F_GETFL, 0);
+ if (flags >= 0)
+ fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
+#endif
+ setlinebuf(tracefile);
+ fputs("\nTracing started.\n", tracefile);
+}
+
+static void
+indent(int amount, char *pfx, FILE *fp)
+{
+ int i;
+
+ for (i = 0; i < amount; i++) {
+ if (pfx && i == amount - 1)
+ fputs(pfx, fp);
+ putc('\t', fp);
+ }
+}
+
+/* little circular references here... */
+static void shtree(union node *n, int ind, char *pfx, FILE *fp);
+
+static void
+sharg(union node *arg, FILE *fp)
+{
+ char *p;
+ struct nodelist *bqlist;
+ int subtype;
+
+ if (arg->type != NARG) {
+ out1fmt("<node type %d>\n", arg->type);
+ abort();
+ }
+ bqlist = arg->narg.backquote;
+ for (p = arg->narg.text; *p; p++) {
+ switch (*p) {
+ case CTLESC:
+ putc(*++p, fp);
+ break;
+ case CTLVAR:
+ putc('$', fp);
+ putc('{', fp);
+ subtype = *++p;
+ if (subtype == VSLENGTH)
+ putc('#', fp);
+
+ while (*p != '=')
+ putc(*p++, fp);
+
+ if (subtype & VSNUL)
+ putc(':', fp);
+
+ switch (subtype & VSTYPE) {
+ case VSNORMAL:
+ putc('}', fp);
+ break;
+ case VSMINUS:
+ putc('-', fp);
+ break;
+ case VSPLUS:
+ putc('+', fp);
+ break;
+ case VSQUESTION:
+ putc('?', fp);
+ break;
+ case VSASSIGN:
+ putc('=', fp);
+ break;
+ case VSTRIMLEFT:
+ putc('#', fp);
+ break;
+ case VSTRIMLEFTMAX:
+ putc('#', fp);
+ putc('#', fp);
+ break;
+ case VSTRIMRIGHT:
+ putc('%', fp);
+ break;
+ case VSTRIMRIGHTMAX:
+ putc('%', fp);
+ putc('%', fp);
+ break;
+ case VSLENGTH:
+ break;
+ default:
+ out1fmt("<subtype %d>", subtype);
+ }
+ break;
+ case CTLENDVAR:
+ putc('}', fp);
+ break;
+ case CTLBACKQ:
+ case CTLBACKQ|CTLQUOTE:
+ putc('$', fp);
+ putc('(', fp);
+ shtree(bqlist->n, -1, NULL, fp);
+ putc(')', fp);
+ break;
+ default:
+ putc(*p, fp);
+ break;
+ }
+ }
+}
+
+static void
+shcmd(union node *cmd, FILE *fp)
+{
+ union node *np;
+ int first;
+ const char *s;
+ int dftfd;
+
+ first = 1;
+ for (np = cmd->ncmd.args; np; np = np->narg.next) {
+ if (! first)
+ putchar(' ');
+ sharg(np, fp);
+ first = 0;
+ }
+ for (np = cmd->ncmd.redirect; np; np = np->nfile.next) {
+ if (! first)
+ putchar(' ');
+ switch (np->nfile.type) {
+ case NTO: s = ">"; dftfd = 1; break;
+ case NCLOBBER: s = ">|"; dftfd = 1; break;
+ case NAPPEND: s = ">>"; dftfd = 1; break;
+ case NTOFD: s = ">&"; dftfd = 1; break;
+ case NFROM: s = "<"; dftfd = 0; break;
+ case NFROMFD: s = "<&"; dftfd = 0; break;
+ case NFROMTO: s = "<>"; dftfd = 0; break;
+ default: s = "*error*"; dftfd = 0; break;
+ }
+ if (np->nfile.fd != dftfd)
+ fprintf(fp, "%d", np->nfile.fd);
+ fputs(s, fp);
+ if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
+ fprintf(fp, "%d", np->ndup.dupfd);
+ } else {
+ sharg(np->nfile.fname, fp);
+ }
+ first = 0;
+ }
+}
+
+static void
+shtree(union node *n, int ind, char *pfx, FILE *fp)
+{
+ struct nodelist *lp;
+ const char *s;
+
+ if (n == NULL)
+ return;
+
+ indent(ind, pfx, fp);
+ switch (n->type) {
+ case NSEMI:
+ s = "; ";
+ goto binop;
+ case NAND:
+ s = " && ";
+ goto binop;
+ case NOR:
+ s = " || ";
+ binop:
+ shtree(n->nbinary.ch1, ind, NULL, fp);
+ /* if (ind < 0) */
+ fputs(s, fp);
+ shtree(n->nbinary.ch2, ind, NULL, fp);
+ break;
+ case NCMD:
+ shcmd(n, fp);
+ if (ind >= 0)
+ putc('\n', fp);
+ break;
+ case NPIPE:
+ for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
+ shcmd(lp->n, fp);
+ if (lp->next)
+ fputs(" | ", fp);
+ }
+ if (n->npipe.backgnd)
+ fputs(" &", fp);
+ if (ind >= 0)
+ putc('\n', fp);
+ break;
+ default:
+ fprintf(fp, "<node type %d>", n->type);
+ if (ind >= 0)
+ putc('\n', fp);
+ break;
+ }
+}
+
+static void
+showtree(union node *n)
+{
+ trace_puts("showtree called\n");
+ shtree(n, 1, NULL, stdout);
+}
+
+#define TRACE(param) trace_printf param
+#define TRACEV(param) trace_vprintf param
+
+#else
+
+#define TRACE(param)
+#define TRACEV(param)
+
+#endif /* DEBUG */
+
+
+/* ============ Parser data */
+
+/*
+ * ash_vmsg() needs parsefile->fd, hence parsefile definition is moved up.
+ */
+struct strlist {
+ struct strlist *next;
+ char *text;
+};
+
+#if ENABLE_ASH_ALIAS
+struct alias;
+#endif
+
+struct strpush {
+ struct strpush *prev; /* preceding string on stack */
+ char *prevstring;
+ int prevnleft;
+#if ENABLE_ASH_ALIAS
+ struct alias *ap; /* if push was associated with an alias */
+#endif
+ char *string; /* remember the string since it may change */
+};
+
+struct parsefile {
+ struct parsefile *prev; /* preceding file on stack */
+ int linno; /* current line */
+ int fd; /* file descriptor (or -1 if string) */
+ int nleft; /* number of chars left in this line */
+ int lleft; /* number of chars left in this buffer */
+ char *nextc; /* next char in buffer */
+ char *buf; /* input buffer */
+ struct strpush *strpush; /* for pushing strings at this level */
+ struct strpush basestrpush; /* so pushing one is fast */
+};
+
+static struct parsefile basepf; /* top level input file */
+static struct parsefile *parsefile = &basepf; /* current input file */
+static int startlinno; /* line # where last token started */
+static char *commandname; /* currently executing command */
+static struct strlist *cmdenviron; /* environment for builtin command */
+static int exitstatus; /* exit status of last command */
+
+
+/* ============ Message printing */
+
+static void
+ash_vmsg(const char *msg, va_list ap)
+{
+ fprintf(stderr, "%s: ", arg0);
+ if (commandname) {
+ if (strcmp(arg0, commandname))
+ fprintf(stderr, "%s: ", commandname);
+ if (!iflag || parsefile->fd)
+ fprintf(stderr, "line %d: ", startlinno);
+ }
+ vfprintf(stderr, msg, ap);
+ outcslow('\n', stderr);
+}
+
+/*
+ * Exverror is called to raise the error exception. If the second argument
+ * is not NULL then error prints an error message using printf style
+ * formatting. It then raises the error exception.
+ */
+static void ash_vmsg_and_raise(int, const char *, va_list) ATTRIBUTE_NORETURN;
+static void
+ash_vmsg_and_raise(int cond, const char *msg, va_list ap)
+{
+#if DEBUG
+ if (msg) {
+ TRACE(("ash_vmsg_and_raise(%d, \"", cond));
+ TRACEV((msg, ap));
+ TRACE(("\") pid=%d\n", getpid()));
+ } else
+ TRACE(("ash_vmsg_and_raise(%d, NULL) pid=%d\n", cond, getpid()));
+ if (msg)
+#endif
+ ash_vmsg(msg, ap);
+
+ flush_stdout_stderr();
+ raise_exception(cond);
+ /* NOTREACHED */
+}
+
+static void ash_msg_and_raise_error(const char *, ...) ATTRIBUTE_NORETURN;
+static void
+ash_msg_and_raise_error(const char *msg, ...)
+{
+ va_list ap;
+
+ va_start(ap, msg);
+ ash_vmsg_and_raise(EXERROR, msg, ap);
+ /* NOTREACHED */
+ va_end(ap);
+}
+
+static void ash_msg_and_raise(int, const char *, ...) ATTRIBUTE_NORETURN;
+static void
+ash_msg_and_raise(int cond, const char *msg, ...)
+{
+ va_list ap;
+
+ va_start(ap, msg);
+ ash_vmsg_and_raise(cond, msg, ap);
+ /* NOTREACHED */
+ va_end(ap);
+}
+
+/*
+ * error/warning routines for external builtins
+ */
+static void
+ash_msg(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ ash_vmsg(fmt, ap);
+ va_end(ap);
+}
+
+/*
+ * Return a string describing an error. The returned string may be a
+ * pointer to a static buffer that will be overwritten on the next call.
+ * Action describes the operation that got the error.
+ */
+static const char *
+errmsg(int e, const char *em)
+{
+ if (e == ENOENT || e == ENOTDIR) {
+ return em;
+ }
+ return strerror(e);
+}
+
+
+/* ============ Memory allocation */
+
+/*
+ * It appears that grabstackstr() will barf with such alignments
+ * because stalloc() will return a string allocated in a new stackblock.
+ */
+#define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE)
+enum {
+ /* Most machines require the value returned from malloc to be aligned
+ * in some way. The following macro will get this right
+ * on many machines. */
+ SHELL_SIZE = sizeof(union {int i; char *cp; double d; }) - 1,
+ /* Minimum size of a block */
+ MINSIZE = SHELL_ALIGN(504),
+};
+
+struct stack_block {
+ struct stack_block *prev;
+ char space[MINSIZE];
+};
+
+struct stackmark {
+ struct stack_block *stackp;
+ char *stacknxt;
+ size_t stacknleft;
+ struct stackmark *marknext;
+};
+
+static struct stack_block stackbase;
+static struct stack_block *stackp = &stackbase;
+static struct stackmark *markp;
+static char *stacknxt = stackbase.space;
+static size_t stacknleft = MINSIZE;
+static char *sstrend = stackbase.space + MINSIZE;
+static int herefd = -1;
+
+#define stackblock() ((void *)stacknxt)
+#define stackblocksize() stacknleft
+
+static void *
+ckrealloc(void * p, size_t nbytes)
+{
+ p = realloc(p, nbytes);
+ if (!p)
+ ash_msg_and_raise_error(bb_msg_memory_exhausted);
+ return p;
+}
+
+static void *
+ckmalloc(size_t nbytes)
+{
+ return ckrealloc(NULL, nbytes);
+}
+
+/*
+ * Make a copy of a string in safe storage.
+ */
+static char *
+ckstrdup(const char *s)
+{
+ char *p = strdup(s);
+ if (!p)
+ ash_msg_and_raise_error(bb_msg_memory_exhausted);
+ return p;
+}
+
+/*
+ * Parse trees for commands are allocated in lifo order, so we use a stack
+ * to make this more efficient, and also to avoid all sorts of exception
+ * handling code to handle interrupts in the middle of a parse.
+ *
+ * The size 504 was chosen because the Ultrix malloc handles that size
+ * well.
+ */
+static void *
+stalloc(size_t nbytes)
+{
+ char *p;
+ size_t aligned;
+
+ aligned = SHELL_ALIGN(nbytes);
+ if (aligned > stacknleft) {
+ size_t len;
+ size_t blocksize;
+ struct stack_block *sp;
+
+ blocksize = aligned;
+ if (blocksize < MINSIZE)
+ blocksize = MINSIZE;
+ len = sizeof(struct stack_block) - MINSIZE + blocksize;
+ if (len < blocksize)
+ ash_msg_and_raise_error(bb_msg_memory_exhausted);
+ INT_OFF;
+ sp = ckmalloc(len);
+ sp->prev = stackp;
+ stacknxt = sp->space;
+ stacknleft = blocksize;
+ sstrend = stacknxt + blocksize;
+ stackp = sp;
+ INT_ON;
+ }
+ p = stacknxt;
+ stacknxt += aligned;
+ stacknleft -= aligned;
+ return p;
+}
+
+static void
+stunalloc(void *p)
+{
+#if DEBUG
+ if (!p || (stacknxt < (char *)p) || ((char *)p < stackp->space)) {
+ write(2, "stunalloc\n", 10);
+ abort();
+ }
+#endif
+ stacknleft += stacknxt - (char *)p;
+ stacknxt = p;
+}
+
+/*
+ * Like strdup but works with the ash stack.
+ */
+static char *
+ststrdup(const char *p)
+{
+ size_t len = strlen(p) + 1;
+ return memcpy(stalloc(len), p, len);
+}
+
+static void
+setstackmark(struct stackmark *mark)
+{
+ mark->stackp = stackp;
+ mark->stacknxt = stacknxt;
+ mark->stacknleft = stacknleft;
+ mark->marknext = markp;
+ markp = mark;
+}
+
+static void
+popstackmark(struct stackmark *mark)
+{
+ struct stack_block *sp;
+
+ if (!mark->stackp)
+ return;
+
+ INT_OFF;
+ markp = mark->marknext;
+ while (stackp != mark->stackp) {
+ sp = stackp;
+ stackp = sp->prev;
+ free(sp);
+ }
+ stacknxt = mark->stacknxt;
+ stacknleft = mark->stacknleft;
+ sstrend = mark->stacknxt + mark->stacknleft;
+ INT_ON;
+}
+
+/*
+ * When the parser reads in a string, it wants to stick the string on the
+ * stack and only adjust the stack pointer when it knows how big the
+ * string is. Stackblock (defined in stack.h) returns a pointer to a block
+ * of space on top of the stack and stackblocklen returns the length of
+ * this block. Growstackblock will grow this space by at least one byte,
+ * possibly moving it (like realloc). Grabstackblock actually allocates the
+ * part of the block that has been used.
+ */
+static void
+growstackblock(void)
+{
+ size_t newlen;
+
+ newlen = stacknleft * 2;
+ if (newlen < stacknleft)
+ ash_msg_and_raise_error(bb_msg_memory_exhausted);
+ if (newlen < 128)
+ newlen += 128;
+
+ if (stacknxt == stackp->space && stackp != &stackbase) {
+ struct stack_block *oldstackp;
+ struct stackmark *xmark;
+ struct stack_block *sp;
+ struct stack_block *prevstackp;
+ size_t grosslen;
+
+ INT_OFF;
+ oldstackp = stackp;
+ sp = stackp;
+ prevstackp = sp->prev;
+ grosslen = newlen + sizeof(struct stack_block) - MINSIZE;
+ sp = ckrealloc(sp, grosslen);
+ sp->prev = prevstackp;
+ stackp = sp;
+ stacknxt = sp->space;
+ stacknleft = newlen;
+ sstrend = sp->space + newlen;
+
+ /*
+ * Stack marks pointing to the start of the old block
+ * must be relocated to point to the new block
+ */
+ xmark = markp;
+ while (xmark != NULL && xmark->stackp == oldstackp) {
+ xmark->stackp = stackp;
+ xmark->stacknxt = stacknxt;
+ xmark->stacknleft = stacknleft;
+ xmark = xmark->marknext;
+ }
+ INT_ON;
+ } else {
+ char *oldspace = stacknxt;
+ int oldlen = stacknleft;
+ char *p = stalloc(newlen);
+
+ /* free the space we just allocated */
+ stacknxt = memcpy(p, oldspace, oldlen);
+ stacknleft += newlen;
+ }
+}
+
+static void
+grabstackblock(size_t len)
+{
+ len = SHELL_ALIGN(len);
+ stacknxt += len;
+ stacknleft -= len;
+}
+
+/*
+ * The following routines are somewhat easier to use than the above.
+ * The user declares a variable of type STACKSTR, which may be declared
+ * to be a register. The macro STARTSTACKSTR initializes things. Then
+ * the user uses the macro STPUTC to add characters to the string. In
+ * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
+ * grown as necessary. When the user is done, she can just leave the
+ * string there and refer to it using stackblock(). Or she can allocate
+ * the space for it using grabstackstr(). If it is necessary to allow
+ * someone else to use the stack temporarily and then continue to grow
+ * the string, the user should use grabstack to allocate the space, and
+ * then call ungrabstr(p) to return to the previous mode of operation.
+ *
+ * USTPUTC is like STPUTC except that it doesn't check for overflow.
+ * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
+ * is space for at least one character.
+ */
+static void *
+growstackstr(void)
+{
+ size_t len = stackblocksize();
+ if (herefd >= 0 && len >= 1024) {
+ full_write(herefd, stackblock(), len);
+ return stackblock();
+ }
+ growstackblock();
+ return stackblock() + len;
+}
+
+/*
+ * Called from CHECKSTRSPACE.
+ */
+static char *
+makestrspace(size_t newlen, char *p)
+{
+ size_t len = p - stacknxt;
+ size_t size = stackblocksize();
+
+ for (;;) {
+ size_t nleft;
+
+ size = stackblocksize();
+ nleft = size - len;
+ if (nleft >= newlen)
+ break;
+ growstackblock();
+ }
+ return stackblock() + len;
+}
+
+static char *
+stack_nputstr(const char *s, size_t n, char *p)
+{
+ p = makestrspace(n, p);
+ p = memcpy(p, s, n) + n;
+ return p;
+}
+
+static char *
+stack_putstr(const char *s, char *p)
+{
+ return stack_nputstr(s, strlen(s), p);
+}
+
+static char *
+_STPUTC(int c, char *p)
+{
+ if (p == sstrend)
+ p = growstackstr();
+ *p++ = c;
+ return p;
+}
+
+#define STARTSTACKSTR(p) ((p) = stackblock())
+#define STPUTC(c, p) ((p) = _STPUTC((c), (p)))
+#define CHECKSTRSPACE(n, p) \
+ do { \
+ char *q = (p); \
+ size_t l = (n); \
+ size_t m = sstrend - q; \
+ if (l > m) \
+ (p) = makestrspace(l, q); \
+ } while (0)
+#define USTPUTC(c, p) (*p++ = (c))
+#define STACKSTRNUL(p) \
+ do { \
+ if ((p) == sstrend) \
+ p = growstackstr(); \
+ *p = '\0'; \
+ } while (0)
+#define STUNPUTC(p) (--p)
+#define STTOPC(p) (p[-1])
+#define STADJUST(amount, p) (p += (amount))
+
+#define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock())
+#define ungrabstackstr(s, p) stunalloc((s))
+#define stackstrend() ((void *)sstrend)
+
+
+/* ============ String helpers */
+
+/*
+ * prefix -- see if pfx is a prefix of string.
+ */
+static char *
+prefix(const char *string, const char *pfx)
+{
+ while (*pfx) {
+ if (*pfx++ != *string++)
+ return 0;
+ }
+ return (char *) string;
+}
+
+/*
+ * Check for a valid number. This should be elsewhere.
+ */
+static int
+is_number(const char *p)
+{
+ do {
+ if (!isdigit(*p))
+ return 0;
+ } while (*++p != '\0');
+ return 1;
+}
+
+/*
+ * Convert a string of digits to an integer, printing an error message on
+ * failure.
+ */
+static int
+number(const char *s)
+{
+ if (!is_number(s))
+ ash_msg_and_raise_error(illnum, s);
+ return atoi(s);
+}
+
+/*
+ * Produce a possibly single quoted string suitable as input to the shell.
+ * The return string is allocated on the stack.
+ */
+static char *
+single_quote(const char *s)
+{
+ char *p;
+
+ STARTSTACKSTR(p);
+
+ do {
+ char *q;
+ size_t len;
+
+ len = strchrnul(s, '\'') - s;
+
+ q = p = makestrspace(len + 3, p);
+
+ *q++ = '\'';
+ q = memcpy(q, s, len) + len;
+ *q++ = '\'';
+ s += len;
+
+ STADJUST(q - p, p);
+
+ len = strspn(s, "'");
+ if (!len)
+ break;
+
+ q = p = makestrspace(len + 3, p);
+
+ *q++ = '"';
+ q = memcpy(q, s, len) + len;
+ *q++ = '"';
+ s += len;
+
+ STADJUST(q - p, p);
+ } while (*s);
+
+ USTPUTC(0, p);
+
+ return stackblock();
+}
+
+
+/* ============ nextopt */
+
+static char **argptr; /* argument list for builtin commands */
+static char *optionarg; /* set by nextopt (like getopt) */
+static char *optptr; /* used by nextopt */
+
+/*
+ * XXX - should get rid of. have all builtins use getopt(3). the
+ * library getopt must have the BSD extension static variable "optreset"
+ * otherwise it can't be used within the shell safely.
+ *
+ * Standard option processing (a la getopt) for builtin routines. The
+ * only argument that is passed to nextopt is the option string; the
+ * other arguments are unnecessary. It return the character, or '\0' on
+ * end of input.
+ */
+static int
+nextopt(const char *optstring)
+{
+ char *p;
+ const char *q;
+ char c;
+
+ p = optptr;
+ if (p == NULL || *p == '\0') {
+ p = *argptr;
+ if (p == NULL || *p != '-' || *++p == '\0')
+ return '\0';
+ argptr++;
+ if (LONE_DASH(p)) /* check for "--" */
+ return '\0';
+ }
+ c = *p++;
+ for (q = optstring; *q != c; ) {
+ if (*q == '\0')
+ ash_msg_and_raise_error("illegal option -%c", c);
+ if (*++q == ':')
+ q++;
+ }
+ if (*++q == ':') {
+ if (*p == '\0' && (p = *argptr++) == NULL)
+ ash_msg_and_raise_error("no arg for -%c option", c);
+ optionarg = p;
+ p = NULL;
+ }
+ optptr = p;
+ return c;
+}
+
+
+/* ============ Math support definitions */
+
+#if ENABLE_ASH_MATH_SUPPORT_64
+typedef int64_t arith_t;
+#define arith_t_type long long
+#else
+typedef long arith_t;
+#define arith_t_type long
+#endif
+
+#if ENABLE_ASH_MATH_SUPPORT
+static arith_t dash_arith(const char *);
+static arith_t arith(const char *expr, int *perrcode);
+#endif
+
+#if ENABLE_ASH_RANDOM_SUPPORT
+static unsigned long rseed;
+#ifndef DYNAMIC_VAR
+#define DYNAMIC_VAR
+#endif
+#endif
+
+
+/* ============ Shell variables */
+
+/* flags */
+#define VEXPORT 0x01 /* variable is exported */
+#define VREADONLY 0x02 /* variable cannot be modified */
+#define VSTRFIXED 0x04 /* variable struct is statically allocated */
+#define VTEXTFIXED 0x08 /* text is statically allocated */
+#define VSTACK 0x10 /* text is allocated on the stack */
+#define VUNSET 0x20 /* the variable is not set */
+#define VNOFUNC 0x40 /* don't call the callback function */
+#define VNOSET 0x80 /* do not set variable - just readonly test */
+#define VNOSAVE 0x100 /* when text is on the heap before setvareq */
+#ifdef DYNAMIC_VAR
+# define VDYNAMIC 0x200 /* dynamic variable */
+#else
+# define VDYNAMIC 0
+#endif
+
+static const char defpathvar[] = "PATH=/usr/local/bin:/usr/bin:/sbin:/bin";
+#ifdef IFS_BROKEN
+static const char defifsvar[] = "IFS= \t\n";
+#define defifs (defifsvar + 4)
+#else
+static const char defifs[] = " \t\n";
+#endif
+
+struct shparam {
+ int nparam; /* # of positional parameters (without $0) */
+ unsigned char malloc; /* if parameter list dynamically allocated */
+ char **p; /* parameter list */
+#if ENABLE_ASH_GETOPTS
+ int optind; /* next parameter to be processed by getopts */
+ int optoff; /* used by getopts */
+#endif
+};
+
+static struct shparam shellparam; /* $@ current positional parameters */
+
+/*
+ * Free the list of positional parameters.
+ */
+static void
+freeparam(volatile struct shparam *param)
+{
+ char **ap;
+
+ if (param->malloc) {
+ for (ap = param->p; *ap; ap++)
+ free(*ap);
+ free(param->p);
+ }
+}
+
+#if ENABLE_ASH_GETOPTS
+static void
+getoptsreset(const char *value)
+{
+ shellparam.optind = number(value);
+ shellparam.optoff = -1;
+}
+#endif
+
+struct var {
+ struct var *next; /* next entry in hash list */
+ int flags; /* flags are defined above */
+ const char *text; /* name=value */
+ void (*func)(const char *); /* function to be called when */
+ /* the variable gets set/unset */
+};
+
+struct localvar {
+ struct localvar *next; /* next local variable in list */
+ struct var *vp; /* the variable that was made local */
+ int flags; /* saved flags */
+ const char *text; /* saved text */
+};
+
+/* Forward decls for varinit[] */
+#if ENABLE_LOCALE_SUPPORT
+static void
+change_lc_all(const char *value)
+{
+ if (value && *value != '\0')
+ setlocale(LC_ALL, value);
+}
+static void
+change_lc_ctype(const char *value)
+{
+ if (value && *value != '\0')
+ setlocale(LC_CTYPE, value);
+}
+#endif
+#if ENABLE_ASH_MAIL
+static void chkmail(void);
+static void changemail(const char *);
+#endif
+static void changepath(const char *);
+#if ENABLE_ASH_RANDOM_SUPPORT
+static void change_random(const char *);
+#endif
+
+static struct var varinit[] = {
+#ifdef IFS_BROKEN
+ { NULL, VSTRFIXED|VTEXTFIXED, defifsvar, NULL },
+#else
+ { NULL, VSTRFIXED|VTEXTFIXED|VUNSET, "IFS\0", NULL },
+#endif
+#if ENABLE_ASH_MAIL
+ { NULL, VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL\0", changemail },
+ { NULL, VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH\0", changemail },
+#endif
+ { NULL, VSTRFIXED|VTEXTFIXED, defpathvar, changepath },
+ { NULL, VSTRFIXED|VTEXTFIXED, "PS1=$ ", NULL },
+ { NULL, VSTRFIXED|VTEXTFIXED, "PS2=> ", NULL },
+ { NULL, VSTRFIXED|VTEXTFIXED, "PS4=+ ", NULL },
+#if ENABLE_ASH_GETOPTS
+ { NULL, VSTRFIXED|VTEXTFIXED, "OPTIND=1", getoptsreset },
+#endif
+#if ENABLE_ASH_RANDOM_SUPPORT
+ { NULL, VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM\0", change_random },
+#endif
+#if ENABLE_LOCALE_SUPPORT
+ { NULL, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_ALL\0", change_lc_all },
+ { NULL, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_CTYPE\0", change_lc_ctype },
+#endif
+#if ENABLE_FEATURE_EDITING_SAVEHISTORY
+ { NULL, VSTRFIXED | VTEXTFIXED | VUNSET, "HISTFILE\0", NULL },
+#endif
+};
+
+#define vifs varinit[0]
+#if ENABLE_ASH_MAIL
+#define vmail (&vifs)[1]
+#define vmpath (&vmail)[1]
+#else
+#define vmpath vifs
+#endif
+#define vpath (&vmpath)[1]
+#define vps1 (&vpath)[1]
+#define vps2 (&vps1)[1]
+#define vps4 (&vps2)[1]
+#define voptind (&vps4)[1]
+#if ENABLE_ASH_GETOPTS
+#define vrandom (&voptind)[1]
+#else
+#define vrandom (&vps4)[1]
+#endif
+#define defpath (defpathvar + 5)
+
+/*
+ * The following macros access the values of the above variables.
+ * They have to skip over the name. They return the null string
+ * for unset variables.
+ */
+#define ifsval() (vifs.text + 4)
+#define ifsset() ((vifs.flags & VUNSET) == 0)
+#define mailval() (vmail.text + 5)
+#define mpathval() (vmpath.text + 9)
+#define pathval() (vpath.text + 5)
+#define ps1val() (vps1.text + 4)
+#define ps2val() (vps2.text + 4)
+#define ps4val() (vps4.text + 4)
+#define optindval() (voptind.text + 7)
+
+#define mpathset() ((vmpath.flags & VUNSET) == 0)
+
+/*
+ * The parsefile structure pointed to by the global variable parsefile
+ * contains information about the current file being read.
+ */
+struct redirtab {
+ struct redirtab *next;
+ int renamed[10];
+ int nullredirs;
+};
+
+static struct redirtab *redirlist;
+static int nullredirs;
+extern char **environ;
+static int preverrout_fd; /* save fd2 before print debug if xflag is set. */
+
+#define VTABSIZE 39
+
+static struct var *vartab[VTABSIZE];
+
+#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
+#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
+
+/*
+ * Return of a legal variable name (a letter or underscore followed by zero or
+ * more letters, underscores, and digits).
+ */
+static char *
+endofname(const char *name)
+{
+ char *p;
+
+ p = (char *) name;
+ if (!is_name(*p))
+ return p;
+ while (*++p) {
+ if (!is_in_name(*p))
+ break;
+ }
+ return p;
+}
+
+/*
+ * Compares two strings up to the first = or '\0'. The first
+ * string must be terminated by '='; the second may be terminated by
+ * either '=' or '\0'.
+ */
+static int
+varcmp(const char *p, const char *q)
+{
+ int c, d;
+
+ while ((c = *p) == (d = *q)) {
+ if (!c || c == '=')
+ goto out;
+ p++;
+ q++;
+ }
+ if (c == '=')
+ c = '\0';
+ if (d == '=')
+ d = '\0';
+ out:
+ return c - d;
+}
+
+static int
+varequal(const char *a, const char *b)
+{
+ return !varcmp(a, b);
+}
+
+/*
+ * Find the appropriate entry in the hash table from the name.
+ */
+static struct var **
+hashvar(const char *p)
+{
+ unsigned hashval;
+
+ hashval = ((unsigned char) *p) << 4;
+ while (*p && *p != '=')
+ hashval += (unsigned char) *p++;
+ return &vartab[hashval % VTABSIZE];
+}
+
+static int
+vpcmp(const void *a, const void *b)
+{
+ return varcmp(*(const char **)a, *(const char **)b);
+}
+
+/*
+ * This routine initializes the builtin variables.
+ */
+static void
+initvar(void)
+{
+ struct var *vp;
+ struct var *end;
+ struct var **vpp;
+
+ /*
+ * PS1 depends on uid
+ */
+#if ENABLE_FEATURE_EDITING && ENABLE_FEATURE_EDITING_FANCY_PROMPT
+ vps1.text = "PS1=\\w \\$ ";
+#else
+ if (!geteuid())
+ vps1.text = "PS1=# ";
+#endif
+ vp = varinit;
+ end = vp + sizeof(varinit) / sizeof(varinit[0]);
+ do {
+ vpp = hashvar(vp->text);
+ vp->next = *vpp;
+ *vpp = vp;
+ } while (++vp < end);
+}
+
+static struct var **
+findvar(struct var **vpp, const char *name)
+{
+ for (; *vpp; vpp = &(*vpp)->next) {
+ if (varequal((*vpp)->text, name)) {
+ break;
+ }
+ }
+ return vpp;
+}
+
+/*
+ * Find the value of a variable. Returns NULL if not set.
+ */
+static char *
+lookupvar(const char *name)
+{
+ struct var *v;
+
+ v = *findvar(hashvar(name), name);
+ if (v) {
+#ifdef DYNAMIC_VAR
+ /*
+ * Dynamic variables are implemented roughly the same way they are
+ * in bash. Namely, they're "special" so long as they aren't unset.
+ * As soon as they're unset, they're no longer dynamic, and dynamic
+ * lookup will no longer happen at that point. -- PFM.
+ */
+ if ((v->flags & VDYNAMIC))
+ (*v->func)(NULL);
+#endif
+ if (!(v->flags & VUNSET))
+ return strchrnul(v->text, '=') + 1;
+ }
+ return NULL;
+}
+
+/*
+ * Search the environment of a builtin command.
+ */
+static char *
+bltinlookup(const char *name)
+{
+ struct strlist *sp;
+
+ for (sp = cmdenviron; sp; sp = sp->next) {
+ if (varequal(sp->text, name))
+ return strchrnul(sp->text, '=') + 1;
+ }
+ return lookupvar(name);
+}
+
+/*
+ * Same as setvar except that the variable and value are passed in
+ * the first argument as name=value. Since the first argument will
+ * be actually stored in the table, it should not be a string that
+ * will go away.
+ * Called with interrupts off.
+ */
+static void
+setvareq(char *s, int flags)
+{
+ struct var *vp, **vpp;
+
+ vpp = hashvar(s);
+ flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
+ vp = *findvar(vpp, s);
+ if (vp) {
+ if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) {
+ const char *n;
+
+ if (flags & VNOSAVE)
+ free(s);
+ n = vp->text;
+ ash_msg_and_raise_error("%.*s: is read only", strchrnul(n, '=') - n, n);
+ }
+
+ if (flags & VNOSET)
+ return;
+
+ if (vp->func && (flags & VNOFUNC) == 0)
+ (*vp->func)(strchrnul(s, '=') + 1);
+
+ if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
+ free((char*)vp->text);
+
+ flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
+ } else {
+ if (flags & VNOSET)
+ return;
+ /* not found */
+ vp = ckmalloc(sizeof(*vp));
+ vp->next = *vpp;
+ vp->func = NULL;
+ *vpp = vp;
+ }
+ if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE)))
+ s = ckstrdup(s);
+ vp->text = s;
+ vp->flags = flags;
+}
+
+/*
+ * Set the value of a variable. The flags argument is ored with the
+ * flags of the variable. If val is NULL, the variable is unset.
+ */
+static void
+setvar(const char *name, const char *val, int flags)
+{
+ char *p, *q;
+ size_t namelen;
+ char *nameeq;
+ size_t vallen;
+
+ q = endofname(name);
+ p = strchrnul(q, '=');
+ namelen = p - name;
+ if (!namelen || p != q)
+ ash_msg_and_raise_error("%.*s: bad variable name", namelen, name);
+ vallen = 0;
+ if (val == NULL) {
+ flags |= VUNSET;
+ } else {
+ vallen = strlen(val);
+ }
+ INT_OFF;
+ nameeq = ckmalloc(namelen + vallen + 2);
+ p = memcpy(nameeq, name, namelen) + namelen;
+ if (val) {
+ *p++ = '=';
+ p = memcpy(p, val, vallen) + vallen;
+ }
+ *p = '\0';
+ setvareq(nameeq, flags | VNOSAVE);
+ INT_ON;
+}
+
+#if ENABLE_ASH_GETOPTS
+/*
+ * Safe version of setvar, returns 1 on success 0 on failure.
+ */
+static int
+setvarsafe(const char *name, const char *val, int flags)
+{
+ int err;
+ volatile int saveint;
+ struct jmploc *volatile savehandler = exception_handler;
+ struct jmploc jmploc;
+
+ SAVE_INT(saveint);
+ if (setjmp(jmploc.loc))
+ err = 1;
+ else {
+ exception_handler = &jmploc;
+ setvar(name, val, flags);
+ err = 0;
+ }
+ exception_handler = savehandler;
+ RESTORE_INT(saveint);
+ return err;
+}
+#endif
+
+/*
+ * Unset the specified variable.
+ */
+static int
+unsetvar(const char *s)
+{
+ struct var **vpp;
+ struct var *vp;
+ int retval;
+
+ vpp = findvar(hashvar(s), s);
+ vp = *vpp;
+ retval = 2;
+ if (vp) {
+ int flags = vp->flags;
+
+ retval = 1;
+ if (flags & VREADONLY)
+ goto out;
+#ifdef DYNAMIC_VAR
+ vp->flags &= ~VDYNAMIC;
+#endif
+ if (flags & VUNSET)
+ goto ok;
+ if ((flags & VSTRFIXED) == 0) {
+ INT_OFF;
+ if ((flags & (VTEXTFIXED|VSTACK)) == 0)
+ free((char*)vp->text);
+ *vpp = vp->next;
+ free(vp);
+ INT_ON;
+ } else {
+ setvar(s, 0, 0);
+ vp->flags &= ~VEXPORT;
+ }
+ ok:
+ retval = 0;
+ }
+ out:
+ return retval;
+}
+
+/*
+ * Process a linked list of variable assignments.
+ */
+static void
+listsetvar(struct strlist *list_set_var, int flags)
+{
+ struct strlist *lp = list_set_var;
+
+ if (!lp)
+ return;
+ INT_OFF;
+ do {
+ setvareq(lp->text, flags);
+ lp = lp->next;
+ } while (lp);
+ INT_ON;
+}
+
+/*
+ * Generate a list of variables satisfying the given conditions.
+ */
+static char **
+listvars(int on, int off, char ***end)
+{
+ struct var **vpp;
+ struct var *vp;
+ char **ep;
+ int mask;
+
+ STARTSTACKSTR(ep);
+ vpp = vartab;
+ mask = on | off;
+ do {
+ for (vp = *vpp; vp; vp = vp->next) {
+ if ((vp->flags & mask) == on) {
+ if (ep == stackstrend())
+ ep = growstackstr();
+ *ep++ = (char *) vp->text;
+ }
+ }
+ } while (++vpp < vartab + VTABSIZE);
+ if (ep == stackstrend())
+ ep = growstackstr();
+ if (end)
+ *end = ep;
+ *ep++ = NULL;
+ return grabstackstr(ep);
+}
+
+
+/* ============ Path search helper
+ *
+ * The variable path (passed by reference) should be set to the start
+ * of the path before the first call; padvance will update
+ * this value as it proceeds. Successive calls to padvance will return
+ * the possible path expansions in sequence. If an option (indicated by
+ * a percent sign) appears in the path entry then the global variable
+ * pathopt will be set to point to it; otherwise pathopt will be set to
+ * NULL.
+ */
+static const char *pathopt; /* set by padvance */
+
+static char *
+padvance(const char **path, const char *name)
+{
+ const char *p;
+ char *q;
+ const char *start;
+ size_t len;
+
+ if (*path == NULL)
+ return NULL;
+ start = *path;
+ for (p = start; *p && *p != ':' && *p != '%'; p++);
+ len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
+ while (stackblocksize() < len)
+ growstackblock();
+ q = stackblock();
+ if (p != start) {
+ memcpy(q, start, p - start);
+ q += p - start;
+ *q++ = '/';
+ }
+ strcpy(q, name);
+ pathopt = NULL;
+ if (*p == '%') {
+ pathopt = ++p;
+ while (*p && *p != ':') p++;
+ }
+ if (*p == ':')
+ *path = p + 1;
+ else
+ *path = NULL;
+ return stalloc(len);
+}
+
+
+/* ============ Prompt */
+
+static int doprompt; /* if set, prompt the user */
+static int needprompt; /* true if interactive and at start of line */
+
+#if ENABLE_FEATURE_EDITING
+static line_input_t *line_input_state;
+static const char *cmdedit_prompt;
+static void
+putprompt(const char *s)
+{
+ if (ENABLE_ASH_EXPAND_PRMT) {
+ free((char*)cmdedit_prompt);
+ cmdedit_prompt = ckstrdup(s);
+ return;
+ }
+ cmdedit_prompt = s;
+}
+#else
+static void
+putprompt(const char *s)
+{
+ out2str(s);
+}
+#endif
+
+#if ENABLE_ASH_EXPAND_PRMT
+/* expandstr() needs parsing machinery, so it is far away ahead... */
+static const char *expandstr(const char *ps);
+#else
+#define expandstr(s) s
+#endif
+
+static void
+setprompt(int whichprompt)
+{
+ const char *prompt;
+#if ENABLE_ASH_EXPAND_PRMT
+ struct stackmark smark;
+#endif
+
+ needprompt = 0;
+
+ switch (whichprompt) {
+ case 1:
+ prompt = ps1val();
+ break;
+ case 2:
+ prompt = ps2val();
+ break;
+ default: /* 0 */
+ prompt = nullstr;
+ }
+#if ENABLE_ASH_EXPAND_PRMT
+ setstackmark(&smark);
+ stalloc(stackblocksize());
+#endif
+ putprompt(expandstr(prompt));
+#if ENABLE_ASH_EXPAND_PRMT
+ popstackmark(&smark);
+#endif
+}
+
+
+/* ============ The cd and pwd commands */
+
+#define CD_PHYSICAL 1
+#define CD_PRINT 2
+
+static int docd(const char *, int);
+
+static char *curdir = nullstr; /* current working directory */
+static char *physdir = nullstr; /* physical working directory */
+
+static int
+cdopt(void)
+{
+ int flags = 0;
+ int i, j;
+
+ j = 'L';
+ while ((i = nextopt("LP"))) {
+ if (i != j) {
+ flags ^= CD_PHYSICAL;
+ j = i;
+ }
+ }
+
+ return flags;
+}
+
+/*
+ * Update curdir (the name of the current directory) in response to a
+ * cd command.
+ */
+static const char *
+updatepwd(const char *dir)
+{
+ char *new;
+ char *p;
+ char *cdcomppath;
+ const char *lim;
+
+ cdcomppath = ststrdup(dir);
+ STARTSTACKSTR(new);
+ if (*dir != '/') {
+ if (curdir == nullstr)
+ return 0;
+ new = stack_putstr(curdir, new);
+ }
+ new = makestrspace(strlen(dir) + 2, new);
+ lim = stackblock() + 1;
+ if (*dir != '/') {
+ if (new[-1] != '/')
+ USTPUTC('/', new);
+ if (new > lim && *lim == '/')
+ lim++;
+ } else {
+ USTPUTC('/', new);
+ cdcomppath++;
+ if (dir[1] == '/' && dir[2] != '/') {
+ USTPUTC('/', new);
+ cdcomppath++;
+ lim++;
+ }
+ }
+ p = strtok(cdcomppath, "/");
+ while (p) {
+ switch (*p) {
+ case '.':
+ if (p[1] == '.' && p[2] == '\0') {
+ while (new > lim) {
+ STUNPUTC(new);
+ if (new[-1] == '/')
+ break;
+ }
+ break;
+ } else if (p[1] == '\0')
+ break;
+ /* fall through */
+ default:
+ new = stack_putstr(p, new);
+ USTPUTC('/', new);
+ }
+ p = strtok(0, "/");
+ }
+ if (new > lim)
+ STUNPUTC(new);
+ *new = 0;
+ return stackblock();
+}
+
+/*
+ * Find out what the current directory is. If we already know the current
+ * directory, this routine returns immediately.
+ */
+static char *
+getpwd(void)
+{
+ char *dir = getcwd(0, 0);
+ return dir ? dir : nullstr;
+}
+
+static void
+setpwd(const char *val, int setold)
+{
+ char *oldcur, *dir;
+
+ oldcur = dir = curdir;
+
+ if (setold) {
+ setvar("OLDPWD", oldcur, VEXPORT);
+ }
+ INT_OFF;
+ if (physdir != nullstr) {
+ if (physdir != oldcur)
+ free(physdir);
+ physdir = nullstr;
+ }
+ if (oldcur == val || !val) {
+ char *s = getpwd();
+ physdir = s;
+ if (!val)
+ dir = s;
+ } else
+ dir = ckstrdup(val);
+ if (oldcur != dir && oldcur != nullstr) {
+ free(oldcur);
+ }
+ curdir = dir;
+ INT_ON;
+ setvar("PWD", dir, VEXPORT);
+}
+
+static void hashcd(void);
+
+/*
+ * Actually do the chdir. We also call hashcd to let the routines in exec.c
+ * know that the current directory has changed.
+ */
+static int
+docd(const char *dest, int flags)
+{
+ const char *dir = 0;
+ int err;
+
+ TRACE(("docd(\"%s\", %d) called\n", dest, flags));
+
+ INT_OFF;
+ if (!(flags & CD_PHYSICAL)) {
+ dir = updatepwd(dest);
+ if (dir)
+ dest = dir;
+ }
+ err = chdir(dest);
+ if (err)
+ goto out;
+ setpwd(dir, 1);
+ hashcd();
+ out:
+ INT_ON;
+ return err;
+}
+
+static int
+cdcmd(int argc, char **argv)
+{
+ const char *dest;
+ const char *path;
+ const char *p;
+ char c;
+ struct stat statb;
+ int flags;
+
+ flags = cdopt();
+ dest = *argptr;
+ if (!dest)
+ dest = bltinlookup(homestr);
+ else if (LONE_DASH(dest)) {
+ dest = bltinlookup("OLDPWD");
+ flags |= CD_PRINT;
+ }
+ if (!dest)
+ dest = nullstr;
+ if (*dest == '/')
+ goto step7;
+ if (*dest == '.') {
+ c = dest[1];
+ dotdot:
+ switch (c) {
+ case '\0':
+ case '/':
+ goto step6;
+ case '.':
+ c = dest[2];
+ if (c != '.')
+ goto dotdot;
+ }
+ }
+ if (!*dest)
+ dest = ".";
+ path = bltinlookup("CDPATH");
+ if (!path) {
+ step6:
+ step7:
+ p = dest;
+ goto docd;
+ }
+ do {
+ c = *path;
+ p = padvance(&path, dest);
+ if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
+ if (c && c != ':')
+ flags |= CD_PRINT;
+ docd:
+ if (!docd(p, flags))
+ goto out;
+ break;
+ }
+ } while (path);
+ ash_msg_and_raise_error("can't cd to %s", dest);
+ /* NOTREACHED */
+ out:
+ if (flags & CD_PRINT)
+ out1fmt(snlfmt, curdir);
+ return 0;
+}
+
+static int
+pwdcmd(int argc, char **argv)
+{
+ int flags;
+ const char *dir = curdir;
+
+ flags = cdopt();
+ if (flags) {
+ if (physdir == nullstr)
+ setpwd(dir, 0);
+ dir = physdir;
+ }
+ out1fmt(snlfmt, dir);
+ return 0;
+}
+
+
+/* ============ ... */
+
+#define IBUFSIZ (BUFSIZ + 1)
+#define basebuf bb_common_bufsiz1 /* buffer for top level input file */
+
+/* Syntax classes */
+#define CWORD 0 /* character is nothing special */
+#define CNL 1 /* newline character */
+#define CBACK 2 /* a backslash character */
+#define CSQUOTE 3 /* single quote */
+#define CDQUOTE 4 /* double quote */
+#define CENDQUOTE 5 /* a terminating quote */
+#define CBQUOTE 6 /* backwards single quote */
+#define CVAR 7 /* a dollar sign */
+#define CENDVAR 8 /* a '}' character */
+#define CLP 9 /* a left paren in arithmetic */
+#define CRP 10 /* a right paren in arithmetic */
+#define CENDFILE 11 /* end of file */
+#define CCTL 12 /* like CWORD, except it must be escaped */
+#define CSPCL 13 /* these terminate a word */
+#define CIGN 14 /* character should be ignored */
+
+#if ENABLE_ASH_ALIAS
+#define SYNBASE 130
+#define PEOF -130
+#define PEOA -129
+#define PEOA_OR_PEOF PEOA
+#else
+#define SYNBASE 129
+#define PEOF -129
+#define PEOA_OR_PEOF PEOF
+#endif
+
+/* number syntax index */
+#define BASESYNTAX 0 /* not in quotes */
+#define DQSYNTAX 1 /* in double quotes */
+#define SQSYNTAX 2 /* in single quotes */
+#define ARISYNTAX 3 /* in arithmetic */
+
+#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
+#define USE_SIT_FUNCTION
+#endif
+
+#if ENABLE_ASH_MATH_SUPPORT
+static const char S_I_T[][4] = {
+#if ENABLE_ASH_ALIAS
+ { CSPCL, CIGN, CIGN, CIGN }, /* 0, PEOA */
+#endif
+ { CSPCL, CWORD, CWORD, CWORD }, /* 1, ' ' */
+ { CNL, CNL, CNL, CNL }, /* 2, \n */
+ { CWORD, CCTL, CCTL, CWORD }, /* 3, !*-/:=?[]~ */
+ { CDQUOTE, CENDQUOTE, CWORD, CWORD }, /* 4, '"' */
+ { CVAR, CVAR, CWORD, CVAR }, /* 5, $ */
+ { CSQUOTE, CWORD, CENDQUOTE, CWORD }, /* 6, "'" */
+ { CSPCL, CWORD, CWORD, CLP }, /* 7, ( */
+ { CSPCL, CWORD, CWORD, CRP }, /* 8, ) */
+ { CBACK, CBACK, CCTL, CBACK }, /* 9, \ */
+ { CBQUOTE, CBQUOTE, CWORD, CBQUOTE }, /* 10, ` */
+ { CENDVAR, CENDVAR, CWORD, CENDVAR }, /* 11, } */
+#ifndef USE_SIT_FUNCTION
+ { CENDFILE, CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
+ { CWORD, CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
+ { CCTL, CCTL, CCTL, CCTL } /* 14, CTLESC ... */
+#endif
+};
+#else
+static const char S_I_T[][3] = {
+#if ENABLE_ASH_ALIAS
+ { CSPCL, CIGN, CIGN }, /* 0, PEOA */
+#endif
+ { CSPCL, CWORD, CWORD }, /* 1, ' ' */
+ { CNL, CNL, CNL }, /* 2, \n */
+ { CWORD, CCTL, CCTL }, /* 3, !*-/:=?[]~ */
+ { CDQUOTE, CENDQUOTE, CWORD }, /* 4, '"' */
+ { CVAR, CVAR, CWORD }, /* 5, $ */
+ { CSQUOTE, CWORD, CENDQUOTE }, /* 6, "'" */
+ { CSPCL, CWORD, CWORD }, /* 7, ( */
+ { CSPCL, CWORD, CWORD }, /* 8, ) */
+ { CBACK, CBACK, CCTL }, /* 9, \ */
+ { CBQUOTE, CBQUOTE, CWORD }, /* 10, ` */
+ { CENDVAR, CENDVAR, CWORD }, /* 11, } */
+#ifndef USE_SIT_FUNCTION
+ { CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
+ { CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
+ { CCTL, CCTL, CCTL } /* 14, CTLESC ... */
+#endif
+};
+#endif /* ASH_MATH_SUPPORT */
+
+#ifdef USE_SIT_FUNCTION
+
+static int
+SIT(int c, int syntax)
+{
+ static const char spec_symbls[] = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
+#if ENABLE_ASH_ALIAS
+ static const char syntax_index_table[] = {
+ 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */
+ 7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */
+ 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */
+ 11, 3 /* "}~" */
+ };
+#else
+ static const char syntax_index_table[] = {
+ 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */
+ 6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */
+ 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */
+ 10, 2 /* "}~" */
+ };
+#endif
+ const char *s;
+ int indx;
+
+ if (c == PEOF) /* 2^8+2 */
+ return CENDFILE;
+#if ENABLE_ASH_ALIAS
+ if (c == PEOA) /* 2^8+1 */
+ indx = 0;
+ else
+#endif
+#define U_C(c) ((unsigned char)(c))
+
+ if ((unsigned char)c >= (unsigned char)(CTLESC)
+ && (unsigned char)c <= (unsigned char)(CTLQUOTEMARK)
+ ) {
+ return CCTL;
+ } else {
+ s = strchr(spec_symbls, c);
+ if (s == NULL || *s == '\0')
+ return CWORD;
+ indx = syntax_index_table[(s - spec_symbls)];
+ }
+ return S_I_T[indx][syntax];
+}
+
+#else /* !USE_SIT_FUNCTION */
+
+#if ENABLE_ASH_ALIAS
+#define CSPCL_CIGN_CIGN_CIGN 0
+#define CSPCL_CWORD_CWORD_CWORD 1
+#define CNL_CNL_CNL_CNL 2
+#define CWORD_CCTL_CCTL_CWORD 3
+#define CDQUOTE_CENDQUOTE_CWORD_CWORD 4
+#define CVAR_CVAR_CWORD_CVAR 5
+#define CSQUOTE_CWORD_CENDQUOTE_CWORD 6
+#define CSPCL_CWORD_CWORD_CLP 7
+#define CSPCL_CWORD_CWORD_CRP 8
+#define CBACK_CBACK_CCTL_CBACK 9
+#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 10
+#define CENDVAR_CENDVAR_CWORD_CENDVAR 11
+#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 12
+#define CWORD_CWORD_CWORD_CWORD 13
+#define CCTL_CCTL_CCTL_CCTL 14
+#else
+#define CSPCL_CWORD_CWORD_CWORD 0
+#define CNL_CNL_CNL_CNL 1
+#define CWORD_CCTL_CCTL_CWORD 2
+#define CDQUOTE_CENDQUOTE_CWORD_CWORD 3
+#define CVAR_CVAR_CWORD_CVAR 4
+#define CSQUOTE_CWORD_CENDQUOTE_CWORD 5
+#define CSPCL_CWORD_CWORD_CLP 6
+#define CSPCL_CWORD_CWORD_CRP 7
+#define CBACK_CBACK_CCTL_CBACK 8
+#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 9
+#define CENDVAR_CENDVAR_CWORD_CENDVAR 10
+#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 11
+#define CWORD_CWORD_CWORD_CWORD 12
+#define CCTL_CCTL_CCTL_CCTL 13
+#endif
+
+static const char syntax_index_table[258] = {
+ /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
+ /* 0 PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE,
+#if ENABLE_ASH_ALIAS
+ /* 1 PEOA */ CSPCL_CIGN_CIGN_CIGN,
+#endif
+ /* 2 -128 0x80 */ CWORD_CWORD_CWORD_CWORD,
+ /* 3 -127 CTLESC */ CCTL_CCTL_CCTL_CCTL,
+ /* 4 -126 CTLVAR */ CCTL_CCTL_CCTL_CCTL,
+ /* 5 -125 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL,
+ /* 6 -124 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL,
+ /* 7 -123 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL,
+ /* 8 -122 CTLARI */ CCTL_CCTL_CCTL_CCTL,
+ /* 9 -121 CTLENDARI */ CCTL_CCTL_CCTL_CCTL,
+ /* 10 -120 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
+ /* 11 -119 */ CWORD_CWORD_CWORD_CWORD,
+ /* 12 -118 */ CWORD_CWORD_CWORD_CWORD,
+ /* 13 -117 */ CWORD_CWORD_CWORD_CWORD,
+ /* 14 -116 */ CWORD_CWORD_CWORD_CWORD,
+ /* 15 -115 */ CWORD_CWORD_CWORD_CWORD,
+ /* 16 -114 */ CWORD_CWORD_CWORD_CWORD,
+ /* 17 -113 */ CWORD_CWORD_CWORD_CWORD,
+ /* 18 -112 */ CWORD_CWORD_CWORD_CWORD,
+ /* 19 -111 */ CWORD_CWORD_CWORD_CWORD,
+ /* 20 -110 */ CWORD_CWORD_CWORD_CWORD,
+ /* 21 -109 */ CWORD_CWORD_CWORD_CWORD,
+ /* 22 -108 */ CWORD_CWORD_CWORD_CWORD,
+ /* 23 -107 */ CWORD_CWORD_CWORD_CWORD,
+ /* 24 -106 */ CWORD_CWORD_CWORD_CWORD,
+ /* 25 -105 */ CWORD_CWORD_CWORD_CWORD,
+ /* 26 -104 */ CWORD_CWORD_CWORD_CWORD,
+ /* 27 -103 */ CWORD_CWORD_CWORD_CWORD,
+ /* 28 -102 */ CWORD_CWORD_CWORD_CWORD,
+ /* 29 -101 */ CWORD_CWORD_CWORD_CWORD,
+ /* 30 -100 */ CWORD_CWORD_CWORD_CWORD,
+ /* 31 -99 */ CWORD_CWORD_CWORD_CWORD,
+ /* 32 -98 */ CWORD_CWORD_CWORD_CWORD,
+ /* 33 -97 */ CWORD_CWORD_CWORD_CWORD,
+ /* 34 -96 */ CWORD_CWORD_CWORD_CWORD,
+ /* 35 -95 */ CWORD_CWORD_CWORD_CWORD,
+ /* 36 -94 */ CWORD_CWORD_CWORD_CWORD,
+ /* 37 -93 */ CWORD_CWORD_CWORD_CWORD,
+ /* 38 -92 */ CWORD_CWORD_CWORD_CWORD,
+ /* 39 -91 */ CWORD_CWORD_CWORD_CWORD,
+ /* 40 -90 */ CWORD_CWORD_CWORD_CWORD,
+ /* 41 -89 */ CWORD_CWORD_CWORD_CWORD,
+ /* 42 -88 */ CWORD_CWORD_CWORD_CWORD,
+ /* 43 -87 */ CWORD_CWORD_CWORD_CWORD,
+ /* 44 -86 */ CWORD_CWORD_CWORD_CWORD,
+ /* 45 -85 */ CWORD_CWORD_CWORD_CWORD,
+ /* 46 -84 */ CWORD_CWORD_CWORD_CWORD,
+ /* 47 -83 */ CWORD_CWORD_CWORD_CWORD,
+ /* 48 -82 */ CWORD_CWORD_CWORD_CWORD,
+ /* 49 -81 */ CWORD_CWORD_CWORD_CWORD,
+ /* 50 -80 */ CWORD_CWORD_CWORD_CWORD,
+ /* 51 -79 */ CWORD_CWORD_CWORD_CWORD,
+ /* 52 -78 */ CWORD_CWORD_CWORD_CWORD,
+ /* 53 -77 */ CWORD_CWORD_CWORD_CWORD,
+ /* 54 -76 */ CWORD_CWORD_CWORD_CWORD,
+ /* 55 -75 */ CWORD_CWORD_CWORD_CWORD,
+ /* 56 -74 */ CWORD_CWORD_CWORD_CWORD,
+ /* 57 -73 */ CWORD_CWORD_CWORD_CWORD,
+ /* 58 -72 */ CWORD_CWORD_CWORD_CWORD,
+ /* 59 -71 */ CWORD_CWORD_CWORD_CWORD,
+ /* 60 -70 */ CWORD_CWORD_CWORD_CWORD,
+ /* 61 -69 */ CWORD_CWORD_CWORD_CWORD,
+ /* 62 -68 */ CWORD_CWORD_CWORD_CWORD,
+ /* 63 -67 */ CWORD_CWORD_CWORD_CWORD,
+ /* 64 -66 */ CWORD_CWORD_CWORD_CWORD,
+ /* 65 -65 */ CWORD_CWORD_CWORD_CWORD,
+ /* 66 -64 */ CWORD_CWORD_CWORD_CWORD,
+ /* 67 -63 */ CWORD_CWORD_CWORD_CWORD,
+ /* 68 -62 */ CWORD_CWORD_CWORD_CWORD,
+ /* 69 -61 */ CWORD_CWORD_CWORD_CWORD,
+ /* 70 -60 */ CWORD_CWORD_CWORD_CWORD,
+ /* 71 -59 */ CWORD_CWORD_CWORD_CWORD,
+ /* 72 -58 */ CWORD_CWORD_CWORD_CWORD,
+ /* 73 -57 */ CWORD_CWORD_CWORD_CWORD,
+ /* 74 -56 */ CWORD_CWORD_CWORD_CWORD,
+ /* 75 -55 */ CWORD_CWORD_CWORD_CWORD,
+ /* 76 -54 */ CWORD_CWORD_CWORD_CWORD,
+ /* 77 -53 */ CWORD_CWORD_CWORD_CWORD,
+ /* 78 -52 */ CWORD_CWORD_CWORD_CWORD,
+ /* 79 -51 */ CWORD_CWORD_CWORD_CWORD,
+ /* 80 -50 */ CWORD_CWORD_CWORD_CWORD,
+ /* 81 -49 */ CWORD_CWORD_CWORD_CWORD,
+ /* 82 -48 */ CWORD_CWORD_CWORD_CWORD,
+ /* 83 -47 */ CWORD_CWORD_CWORD_CWORD,
+ /* 84 -46 */ CWORD_CWORD_CWORD_CWORD,
+ /* 85 -45 */ CWORD_CWORD_CWORD_CWORD,
+ /* 86 -44 */ CWORD_CWORD_CWORD_CWORD,
+ /* 87 -43 */ CWORD_CWORD_CWORD_CWORD,
+ /* 88 -42 */ CWORD_CWORD_CWORD_CWORD,
+ /* 89 -41 */ CWORD_CWORD_CWORD_CWORD,
+ /* 90 -40 */ CWORD_CWORD_CWORD_CWORD,
+ /* 91 -39 */ CWORD_CWORD_CWORD_CWORD,
+ /* 92 -38 */ CWORD_CWORD_CWORD_CWORD,
+ /* 93 -37 */ CWORD_CWORD_CWORD_CWORD,
+ /* 94 -36 */ CWORD_CWORD_CWORD_CWORD,
+ /* 95 -35 */ CWORD_CWORD_CWORD_CWORD,
+ /* 96 -34 */ CWORD_CWORD_CWORD_CWORD,
+ /* 97 -33 */ CWORD_CWORD_CWORD_CWORD,
+ /* 98 -32 */ CWORD_CWORD_CWORD_CWORD,
+ /* 99 -31 */ CWORD_CWORD_CWORD_CWORD,
+ /* 100 -30 */ CWORD_CWORD_CWORD_CWORD,
+ /* 101 -29 */ CWORD_CWORD_CWORD_CWORD,
+ /* 102 -28 */ CWORD_CWORD_CWORD_CWORD,
+ /* 103 -27 */ CWORD_CWORD_CWORD_CWORD,
+ /* 104 -26 */ CWORD_CWORD_CWORD_CWORD,
+ /* 105 -25 */ CWORD_CWORD_CWORD_CWORD,
+ /* 106 -24 */ CWORD_CWORD_CWORD_CWORD,
+ /* 107 -23 */ CWORD_CWORD_CWORD_CWORD,
+ /* 108 -22 */ CWORD_CWORD_CWORD_CWORD,
+ /* 109 -21 */ CWORD_CWORD_CWORD_CWORD,
+ /* 110 -20 */ CWORD_CWORD_CWORD_CWORD,
+ /* 111 -19 */ CWORD_CWORD_CWORD_CWORD,
+ /* 112 -18 */ CWORD_CWORD_CWORD_CWORD,
+ /* 113 -17 */ CWORD_CWORD_CWORD_CWORD,
+ /* 114 -16 */ CWORD_CWORD_CWORD_CWORD,
+ /* 115 -15 */ CWORD_CWORD_CWORD_CWORD,
+ /* 116 -14 */ CWORD_CWORD_CWORD_CWORD,
+ /* 117 -13 */ CWORD_CWORD_CWORD_CWORD,
+ /* 118 -12 */ CWORD_CWORD_CWORD_CWORD,
+ /* 119 -11 */ CWORD_CWORD_CWORD_CWORD,
+ /* 120 -10 */ CWORD_CWORD_CWORD_CWORD,
+ /* 121 -9 */ CWORD_CWORD_CWORD_CWORD,
+ /* 122 -8 */ CWORD_CWORD_CWORD_CWORD,
+ /* 123 -7 */ CWORD_CWORD_CWORD_CWORD,
+ /* 124 -6 */ CWORD_CWORD_CWORD_CWORD,
+ /* 125 -5 */ CWORD_CWORD_CWORD_CWORD,
+ /* 126 -4 */ CWORD_CWORD_CWORD_CWORD,
+ /* 127 -3 */ CWORD_CWORD_CWORD_CWORD,
+ /* 128 -2 */ CWORD_CWORD_CWORD_CWORD,
+ /* 129 -1 */ CWORD_CWORD_CWORD_CWORD,
+ /* 130 0 */ CWORD_CWORD_CWORD_CWORD,
+ /* 131 1 */ CWORD_CWORD_CWORD_CWORD,
+ /* 132 2 */ CWORD_CWORD_CWORD_CWORD,
+ /* 133 3 */ CWORD_CWORD_CWORD_CWORD,
+ /* 134 4 */ CWORD_CWORD_CWORD_CWORD,
+ /* 135 5 */ CWORD_CWORD_CWORD_CWORD,
+ /* 136 6 */ CWORD_CWORD_CWORD_CWORD,
+ /* 137 7 */ CWORD_CWORD_CWORD_CWORD,
+ /* 138 8 */ CWORD_CWORD_CWORD_CWORD,
+ /* 139 9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
+ /* 140 10 "\n" */ CNL_CNL_CNL_CNL,
+ /* 141 11 */ CWORD_CWORD_CWORD_CWORD,
+ /* 142 12 */ CWORD_CWORD_CWORD_CWORD,
+ /* 143 13 */ CWORD_CWORD_CWORD_CWORD,
+ /* 144 14 */ CWORD_CWORD_CWORD_CWORD,
+ /* 145 15 */ CWORD_CWORD_CWORD_CWORD,
+ /* 146 16 */ CWORD_CWORD_CWORD_CWORD,
+ /* 147 17 */ CWORD_CWORD_CWORD_CWORD,
+ /* 148 18 */ CWORD_CWORD_CWORD_CWORD,
+ /* 149 19 */ CWORD_CWORD_CWORD_CWORD,
+ /* 150 20 */ CWORD_CWORD_CWORD_CWORD,
+ /* 151 21 */ CWORD_CWORD_CWORD_CWORD,
+ /* 152 22 */ CWORD_CWORD_CWORD_CWORD,
+ /* 153 23 */ CWORD_CWORD_CWORD_CWORD,
+ /* 154 24 */ CWORD_CWORD_CWORD_CWORD,
+ /* 155 25 */ CWORD_CWORD_CWORD_CWORD,
+ /* 156 26 */ CWORD_CWORD_CWORD_CWORD,
+ /* 157 27 */ CWORD_CWORD_CWORD_CWORD,
+ /* 158 28 */ CWORD_CWORD_CWORD_CWORD,
+ /* 159 29 */ CWORD_CWORD_CWORD_CWORD,
+ /* 160 30 */ CWORD_CWORD_CWORD_CWORD,
+ /* 161 31 */ CWORD_CWORD_CWORD_CWORD,
+ /* 162 32 " " */ CSPCL_CWORD_CWORD_CWORD,
+ /* 163 33 "!" */ CWORD_CCTL_CCTL_CWORD,
+ /* 164 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD,
+ /* 165 35 "#" */ CWORD_CWORD_CWORD_CWORD,
+ /* 166 36 "$" */ CVAR_CVAR_CWORD_CVAR,
+ /* 167 37 "%" */ CWORD_CWORD_CWORD_CWORD,
+ /* 168 38 "&" */ CSPCL_CWORD_CWORD_CWORD,
+ /* 169 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD,
+ /* 170 40 "(" */ CSPCL_CWORD_CWORD_CLP,
+ /* 171 41 ")" */ CSPCL_CWORD_CWORD_CRP,
+ /* 172 42 "*" */ CWORD_CCTL_CCTL_CWORD,
+ /* 173 43 "+" */ CWORD_CWORD_CWORD_CWORD,
+ /* 174 44 "," */ CWORD_CWORD_CWORD_CWORD,
+ /* 175 45 "-" */ CWORD_CCTL_CCTL_CWORD,
+ /* 176 46 "." */ CWORD_CWORD_CWORD_CWORD,
+ /* 177 47 "/" */ CWORD_CCTL_CCTL_CWORD,
+ /* 178 48 "0" */ CWORD_CWORD_CWORD_CWORD,
+ /* 179 49 "1" */ CWORD_CWORD_CWORD_CWORD,
+ /* 180 50 "2" */ CWORD_CWORD_CWORD_CWORD,
+ /* 181 51 "3" */ CWORD_CWORD_CWORD_CWORD,
+ /* 182 52 "4" */ CWORD_CWORD_CWORD_CWORD,
+ /* 183 53 "5" */ CWORD_CWORD_CWORD_CWORD,
+ /* 184 54 "6" */ CWORD_CWORD_CWORD_CWORD,
+ /* 185 55 "7" */ CWORD_CWORD_CWORD_CWORD,
+ /* 186 56 "8" */ CWORD_CWORD_CWORD_CWORD,
+ /* 187 57 "9" */ CWORD_CWORD_CWORD_CWORD,
+ /* 188 58 ":" */ CWORD_CCTL_CCTL_CWORD,
+ /* 189 59 ";" */ CSPCL_CWORD_CWORD_CWORD,
+ /* 190 60 "<" */ CSPCL_CWORD_CWORD_CWORD,
+ /* 191 61 "=" */ CWORD_CCTL_CCTL_CWORD,
+ /* 192 62 ">" */ CSPCL_CWORD_CWORD_CWORD,
+ /* 193 63 "?" */ CWORD_CCTL_CCTL_CWORD,
+ /* 194 64 "@" */ CWORD_CWORD_CWORD_CWORD,
+ /* 195 65 "A" */ CWORD_CWORD_CWORD_CWORD,
+ /* 196 66 "B" */ CWORD_CWORD_CWORD_CWORD,
+ /* 197 67 "C" */ CWORD_CWORD_CWORD_CWORD,
+ /* 198 68 "D" */ CWORD_CWORD_CWORD_CWORD,
+ /* 199 69 "E" */ CWORD_CWORD_CWORD_CWORD,
+ /* 200 70 "F" */ CWORD_CWORD_CWORD_CWORD,
+ /* 201 71 "G" */ CWORD_CWORD_CWORD_CWORD,
+ /* 202 72 "H" */ CWORD_CWORD_CWORD_CWORD,
+ /* 203 73 "I" */ CWORD_CWORD_CWORD_CWORD,
+ /* 204 74 "J" */ CWORD_CWORD_CWORD_CWORD,
+ /* 205 75 "K" */ CWORD_CWORD_CWORD_CWORD,
+ /* 206 76 "L" */ CWORD_CWORD_CWORD_CWORD,
+ /* 207 77 "M" */ CWORD_CWORD_CWORD_CWORD,
+ /* 208 78 "N" */ CWORD_CWORD_CWORD_CWORD,
+ /* 209 79 "O" */ CWORD_CWORD_CWORD_CWORD,
+ /* 210 80 "P" */ CWORD_CWORD_CWORD_CWORD,
+ /* 211 81 "Q" */ CWORD_CWORD_CWORD_CWORD,
+ /* 212 82 "R" */ CWORD_CWORD_CWORD_CWORD,
+ /* 213 83 "S" */ CWORD_CWORD_CWORD_CWORD,
+ /* 214 84 "T" */ CWORD_CWORD_CWORD_CWORD,
+ /* 215 85 "U" */ CWORD_CWORD_CWORD_CWORD,
+ /* 216 86 "V" */ CWORD_CWORD_CWORD_CWORD,
+ /* 217 87 "W" */ CWORD_CWORD_CWORD_CWORD,
+ /* 218 88 "X" */ CWORD_CWORD_CWORD_CWORD,
+ /* 219 89 "Y" */ CWORD_CWORD_CWORD_CWORD,
+ /* 220 90 "Z" */ CWORD_CWORD_CWORD_CWORD,
+ /* 221 91 "[" */ CWORD_CCTL_CCTL_CWORD,
+ /* 222 92 "\" */ CBACK_CBACK_CCTL_CBACK,
+ /* 223 93 "]" */ CWORD_CCTL_CCTL_CWORD,
+ /* 224 94 "^" */ CWORD_CWORD_CWORD_CWORD,
+ /* 225 95 "_" */ CWORD_CWORD_CWORD_CWORD,
+ /* 226 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
+ /* 227 97 "a" */ CWORD_CWORD_CWORD_CWORD,
+ /* 228 98 "b" */ CWORD_CWORD_CWORD_CWORD,
+ /* 229 99 "c" */ CWORD_CWORD_CWORD_CWORD,
+ /* 230 100 "d" */ CWORD_CWORD_CWORD_CWORD,
+ /* 231 101 "e" */ CWORD_CWORD_CWORD_CWORD,
+ /* 232 102 "f" */ CWORD_CWORD_CWORD_CWORD,
+ /* 233 103 "g" */ CWORD_CWORD_CWORD_CWORD,
+ /* 234 104 "h" */ CWORD_CWORD_CWORD_CWORD,
+ /* 235 105 "i" */ CWORD_CWORD_CWORD_CWORD,
+ /* 236 106 "j" */ CWORD_CWORD_CWORD_CWORD,
+ /* 237 107 "k" */ CWORD_CWORD_CWORD_CWORD,
+ /* 238 108 "l" */ CWORD_CWORD_CWORD_CWORD,
+ /* 239 109 "m" */ CWORD_CWORD_CWORD_CWORD,
+ /* 240 110 "n" */ CWORD_CWORD_CWORD_CWORD,
+ /* 241 111 "o" */ CWORD_CWORD_CWORD_CWORD,
+ /* 242 112 "p" */ CWORD_CWORD_CWORD_CWORD,
+ /* 243 113 "q" */ CWORD_CWORD_CWORD_CWORD,
+ /* 244 114 "r" */ CWORD_CWORD_CWORD_CWORD,
+ /* 245 115 "s" */ CWORD_CWORD_CWORD_CWORD,
+ /* 246 116 "t" */ CWORD_CWORD_CWORD_CWORD,
+ /* 247 117 "u" */ CWORD_CWORD_CWORD_CWORD,
+ /* 248 118 "v" */ CWORD_CWORD_CWORD_CWORD,
+ /* 249 119 "w" */ CWORD_CWORD_CWORD_CWORD,
+ /* 250 120 "x" */ CWORD_CWORD_CWORD_CWORD,
+ /* 251 121 "y" */ CWORD_CWORD_CWORD_CWORD,
+ /* 252 122 "z" */ CWORD_CWORD_CWORD_CWORD,
+ /* 253 123 "{" */ CWORD_CWORD_CWORD_CWORD,
+ /* 254 124 "|" */ CSPCL_CWORD_CWORD_CWORD,
+ /* 255 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
+ /* 256 126 "~" */ CWORD_CCTL_CCTL_CWORD,
+ /* 257 127 */ CWORD_CWORD_CWORD_CWORD,
+};
+
+#define SIT(c, syntax) (S_I_T[(int)syntax_index_table[((int)c)+SYNBASE]][syntax])
+
+#endif /* USE_SIT_FUNCTION */
+
+
+/* ============ Alias handling */
+
+#if ENABLE_ASH_ALIAS
+
+#define ALIASINUSE 1
+#define ALIASDEAD 2
+
+#define ATABSIZE 39
+
+struct alias {
+ struct alias *next;
+ char *name;
+ char *val;
+ int flag;
+};
+
+static struct alias *atab[ATABSIZE];
+
+static struct alias **
+__lookupalias(const char *name) {
+ unsigned int hashval;
+ struct alias **app;
+ const char *p;
+ unsigned int ch;
+
+ p = name;
+
+ ch = (unsigned char)*p;
+ hashval = ch << 4;
+ while (ch) {
+ hashval += ch;
+ ch = (unsigned char)*++p;
+ }
+ app = &atab[hashval % ATABSIZE];
+
+ for (; *app; app = &(*app)->next) {
+ if (strcmp(name, (*app)->name) == 0) {
+ break;
+ }
+ }
+
+ return app;
+}
+
+static struct alias *
+lookupalias(const char *name, int check)
+{
+ struct alias *ap = *__lookupalias(name);
+
+ if (check && ap && (ap->flag & ALIASINUSE))
+ return NULL;
+ return ap;
+}
+
+static struct alias *
+freealias(struct alias *ap)
+{
+ struct alias *next;
+
+ if (ap->flag & ALIASINUSE) {
+ ap->flag |= ALIASDEAD;
+ return ap;
+ }
+
+ next = ap->next;
+ free(ap->name);
+ free(ap->val);
+ free(ap);
+ return next;
+}
+
+static void
+setalias(const char *name, const char *val)
+{
+ struct alias *ap, **app;
+
+ app = __lookupalias(name);
+ ap = *app;
+ INT_OFF;
+ if (ap) {
+ if (!(ap->flag & ALIASINUSE)) {
+ free(ap->val);
+ }
+ ap->val = ckstrdup(val);
+ ap->flag &= ~ALIASDEAD;
+ } else {
+ /* not found */
+ ap = ckmalloc(sizeof(struct alias));
+ ap->name = ckstrdup(name);
+ ap->val = ckstrdup(val);
+ ap->flag = 0;
+ ap->next = 0;
+ *app = ap;
+ }
+ INT_ON;
+}
+
+static int
+unalias(const char *name)
+{
+ struct alias **app;
+
+ app = __lookupalias(name);
+
+ if (*app) {
+ INT_OFF;
+ *app = freealias(*app);
+ INT_ON;
+ return 0;
+ }
+
+ return 1;
+}
+
+static void
+rmaliases(void)
+{
+ struct alias *ap, **app;
+ int i;
+
+ INT_OFF;
+ for (i = 0; i < ATABSIZE; i++) {
+ app = &atab[i];
+ for (ap = *app; ap; ap = *app) {
+ *app = freealias(*app);
+ if (ap == *app) {
+ app = &ap->next;
+ }
+ }
+ }
+ INT_ON;
+}
+
+static void
+printalias(const struct alias *ap)
+{
+ out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
+}
+
+/*
+ * TODO - sort output
+ */
+static int
+aliascmd(int argc, char **argv)
+{
+ char *n, *v;
+ int ret = 0;
+ struct alias *ap;
+
+ if (argc == 1) {
+ int i;
+
+ for (i = 0; i < ATABSIZE; i++)
+ for (ap = atab[i]; ap; ap = ap->next) {
+ printalias(ap);
+ }
+ return 0;
+ }
+ while ((n = *++argv) != NULL) {
+ v = strchr(n+1, '=');
+ if (v == NULL) { /* n+1: funny ksh stuff */
+ ap = *__lookupalias(n);
+ if (ap == NULL) {
+ fprintf(stderr, "%s: %s not found\n", "alias", n);
+ ret = 1;
+ } else
+ printalias(ap);
+ } else {
+ *v++ = '\0';
+ setalias(n, v);
+ }
+ }
+
+ return ret;
+}
+
+static int
+unaliascmd(int argc, char **argv)
+{
+ int i;
+
+ while ((i = nextopt("a")) != '\0') {
+ if (i == 'a') {
+ rmaliases();
+ return 0;
+ }
+ }
+ for (i = 0; *argptr; argptr++) {
+ if (unalias(*argptr)) {
+ fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
+ i = 1;
+ }
+ }
+
+ return i;
+}
+
+#endif /* ASH_ALIAS */
+
+
+/* ============ jobs.c */
+
+/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
+#define FORK_FG 0
+#define FORK_BG 1
+#define FORK_NOJOB 2
+
+/* mode flags for showjob(s) */
+#define SHOW_PGID 0x01 /* only show pgid - for jobs -p */
+#define SHOW_PID 0x04 /* include process pid */
+#define SHOW_CHANGED 0x08 /* only jobs whose state has changed */
+
+/*
+ * A job structure contains information about a job. A job is either a
+ * single process or a set of processes contained in a pipeline. In the
+ * latter case, pidlist will be non-NULL, and will point to a -1 terminated
+ * array of pids.
+ */
+
+struct procstat {
+ pid_t pid; /* process id */
+ int status; /* last process status from wait() */
+ char *cmd; /* text of command being run */
+};
+
+struct job {
+ struct procstat ps0; /* status of process */
+ struct procstat *ps; /* status or processes when more than one */
+#if JOBS
+ int stopstatus; /* status of a stopped job */
+#endif
+ uint32_t
+ nprocs: 16, /* number of processes */
+ state: 8,
+#define JOBRUNNING 0 /* at least one proc running */
+#define JOBSTOPPED 1 /* all procs are stopped */
+#define JOBDONE 2 /* all procs are completed */
+#if JOBS
+ sigint: 1, /* job was killed by SIGINT */
+ jobctl: 1, /* job running under job control */
+#endif
+ waited: 1, /* true if this entry has been waited for */
+ used: 1, /* true if this entry is in used */
+ changed: 1; /* true if status has changed */
+ struct job *prev_job; /* previous job */
+};
+
+static pid_t backgndpid; /* pid of last background process */
+static int job_warning; /* user was warned about stopped jobs */
+#if JOBS
+static int jobctl; /* true if doing job control */
+#endif
+
+static struct job *makejob(union node *, int);
+static int forkshell(struct job *, union node *, int);
+static int waitforjob(struct job *);
+
+#if ! JOBS
+#define setjobctl(on) /* do nothing */
+#else
+static void setjobctl(int);
+static void showjobs(FILE *, int);
+#endif
+
+/*
+ * Set the signal handler for the specified signal. The routine figures
+ * out what it should be set to.
+ */
+static void
+setsignal(int signo)
+{
+ int action;
+ char *t, tsig;
+ struct sigaction act;
+
+ t = trap[signo];
+ if (t == NULL)
+ action = S_DFL;
+ else if (*t != '\0')
+ action = S_CATCH;
+ else
+ action = S_IGN;
+ if (rootshell && action == S_DFL) {
+ switch (signo) {
+ case SIGINT:
+ if (iflag || minusc || sflag == 0)
+ action = S_CATCH;
+ break;
+ case SIGQUIT:
+#if DEBUG
+ if (debug)
+ break;
+#endif
+ /* FALLTHROUGH */
+ case SIGTERM:
+ if (iflag)
+ action = S_IGN;
+ break;
+#if JOBS
+ case SIGTSTP:
+ case SIGTTOU:
+ if (mflag)
+ action = S_IGN;
+ break;
+#endif
+ }
+ }
+
+ t = &sigmode[signo - 1];
+ tsig = *t;
+ if (tsig == 0) {
+ /*
+ * current setting unknown
+ */
+ if (sigaction(signo, 0, &act) == -1) {
+ /*
+ * Pretend it worked; maybe we should give a warning
+ * here, but other shells don't. We don't alter
+ * sigmode, so that we retry every time.
+ */
+ return;
+ }
+ if (act.sa_handler == SIG_IGN) {
+ if (mflag
+ && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU)
+ ) {
+ tsig = S_IGN; /* don't hard ignore these */
+ } else
+ tsig = S_HARD_IGN;
+ } else {
+ tsig = S_RESET; /* force to be set */
+ }
+ }
+ if (tsig == S_HARD_IGN || tsig == action)
+ return;
+ switch (action) {
+ case S_CATCH:
+ act.sa_handler = onsig;
+ break;
+ case S_IGN:
+ act.sa_handler = SIG_IGN;
+ break;
+ default:
+ act.sa_handler = SIG_DFL;
+ }
+ *t = action;
+ act.sa_flags = 0;
+ sigfillset(&act.sa_mask);
+ sigaction(signo, &act, 0);
+}
+
+/* mode flags for set_curjob */
+#define CUR_DELETE 2
+#define CUR_RUNNING 1
+#define CUR_STOPPED 0
+
+/* mode flags for dowait */
+#define DOWAIT_NORMAL 0
+#define DOWAIT_BLOCK 1
+
+#if JOBS
+/* pgrp of shell on invocation */
+static int initialpgrp;
+static int ttyfd = -1;
+#endif
+/* array of jobs */
+static struct job *jobtab;
+/* size of array */
+static unsigned njobs;
+/* current job */
+static struct job *curjob;
+/* number of presumed living untracked jobs */
+static int jobless;
+
+static void
+set_curjob(struct job *jp, unsigned mode)
+{
+ struct job *jp1;
+ struct job **jpp, **curp;
+
+ /* first remove from list */
+ jpp = curp = &curjob;
+ do {
+ jp1 = *jpp;
+ if (jp1 == jp)
+ break;
+ jpp = &jp1->prev_job;
+ } while (1);
+ *jpp = jp1->prev_job;
+
+ /* Then re-insert in correct position */
+ jpp = curp;
+ switch (mode) {
+ default:
+#if DEBUG
+ abort();
+#endif
+ case CUR_DELETE:
+ /* job being deleted */
+ break;
+ case CUR_RUNNING:
+ /* newly created job or backgrounded job,
+ put after all stopped jobs. */
+ do {
+ jp1 = *jpp;
+#if JOBS
+ if (!jp1 || jp1->state != JOBSTOPPED)
+#endif
+ break;
+ jpp = &jp1->prev_job;
+ } while (1);
+ /* FALLTHROUGH */
+#if JOBS
+ case CUR_STOPPED:
+#endif
+ /* newly stopped job - becomes curjob */
+ jp->prev_job = *jpp;
+ *jpp = jp;
+ break;
+ }
+}
+
+#if JOBS || DEBUG
+static int
+jobno(const struct job *jp)
+{
+ return jp - jobtab + 1;
+}
+#endif
+
+/*
+ * Convert a job name to a job structure.
+ */
+static struct job *
+getjob(const char *name, int getctl)
+{
+ struct job *jp;
+ struct job *found;
+ const char *err_msg = "No such job: %s";
+ unsigned num;
+ int c;
+ const char *p;
+ char *(*match)(const char *, const char *);
+
+ jp = curjob;
+ p = name;
+ if (!p)
+ goto currentjob;
+
+ if (*p != '%')
+ goto err;
+
+ c = *++p;
+ if (!c)
+ goto currentjob;
+
+ if (!p[1]) {
+ if (c == '+' || c == '%') {
+ currentjob:
+ err_msg = "No current job";
+ goto check;
+ }
+ if (c == '-') {
+ if (jp)
+ jp = jp->prev_job;
+ err_msg = "No previous job";
+ check:
+ if (!jp)
+ goto err;
+ goto gotit;
+ }
+ }
+
+ if (is_number(p)) {
+ num = atoi(p);
+ if (num < njobs) {
+ jp = jobtab + num - 1;
+ if (jp->used)
+ goto gotit;
+ goto err;
+ }
+ }
+
+ match = prefix;
+ if (*p == '?') {
+ match = strstr;
+ p++;
+ }
+
+ found = 0;
+ while (1) {
+ if (!jp)
+ goto err;
+ if (match(jp->ps[0].cmd, p)) {
+ if (found)
+ goto err;
+ found = jp;
+ err_msg = "%s: ambiguous";
+ }
+ jp = jp->prev_job;
+ }
+
+ gotit:
+#if JOBS
+ err_msg = "job %s not created under job control";
+ if (getctl && jp->jobctl == 0)
+ goto err;
+#endif
+ return jp;
+ err:
+ ash_msg_and_raise_error(err_msg, name);
+}
+
+/*
+ * Mark a job structure as unused.
+ */
+static void
+freejob(struct job *jp)
+{
+ struct procstat *ps;
+ int i;
+
+ INT_OFF;
+ for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
+ if (ps->cmd != nullstr)
+ free(ps->cmd);
+ }
+ if (jp->ps != &jp->ps0)
+ free(jp->ps);
+ jp->used = 0;
+ set_curjob(jp, CUR_DELETE);
+ INT_ON;
+}
+
+#if JOBS
+static void
+xtcsetpgrp(int fd, pid_t pgrp)
+{
+ if (tcsetpgrp(fd, pgrp))
+ ash_msg_and_raise_error("cannot set tty process group (%m)");
+}
+
+/*
+ * Turn job control on and off.
+ *
+ * Note: This code assumes that the third arg to ioctl is a character
+ * pointer, which is true on Berkeley systems but not System V. Since
+ * System V doesn't have job control yet, this isn't a problem now.
+ *
+ * Called with interrupts off.
+ */
+static void
+setjobctl(int on)
+{
+ int fd;
+ int pgrp;
+
+ if (on == jobctl || rootshell == 0)
+ return;
+ if (on) {
+ int ofd;
+ ofd = fd = open(_PATH_TTY, O_RDWR);
+ if (fd < 0) {
+ /* BTW, bash will try to open(ttyname(0)) if open("/dev/tty") fails.
+ * That sometimes helps to acquire controlling tty.
+ * Obviously, a workaround for bugs when someone
+ * failed to provide a controlling tty to bash! :) */
+ fd += 3;
+ while (!isatty(fd) && --fd >= 0)
+ ;
+ }
+ fd = fcntl(fd, F_DUPFD, 10);
+ close(ofd);
+ if (fd < 0)
+ goto out;
+ fcntl(fd, F_SETFD, FD_CLOEXEC);
+ do { /* while we are in the background */
+ pgrp = tcgetpgrp(fd);
+ if (pgrp < 0) {
+ out:
+ ash_msg("can't access tty; job control turned off");
+ mflag = on = 0;
+ goto close;
+ }
+ if (pgrp == getpgrp())
+ break;
+ killpg(0, SIGTTIN);
+ } while (1);
+ initialpgrp = pgrp;
+
+ setsignal(SIGTSTP);
+ setsignal(SIGTTOU);
+ setsignal(SIGTTIN);
+ pgrp = rootpid;
+ setpgid(0, pgrp);
+ xtcsetpgrp(fd, pgrp);
+ } else {
+ /* turning job control off */
+ fd = ttyfd;
+ pgrp = initialpgrp;
+ xtcsetpgrp(fd, pgrp);
+ setpgid(0, pgrp);
+ setsignal(SIGTSTP);
+ setsignal(SIGTTOU);
+ setsignal(SIGTTIN);
+ close:
+ close(fd);
+ fd = -1;
+ }
+ ttyfd = fd;
+ jobctl = on;
+}
+
+static int
+killcmd(int argc, char **argv)
+{
+ int signo = -1;
+ int list = 0;
+ int i;
+ pid_t pid;
+ struct job *jp;
+
+ if (argc <= 1) {
+ usage:
+ ash_msg_and_raise_error(
+"usage: kill [-s sigspec | -signum | -sigspec] [pid | job]... or\n"
+"kill -l [exitstatus]"
+ );
+ }
+
+ if (**++argv == '-') {
+ signo = get_signum(*argv + 1);
+ if (signo < 0) {
+ int c;
+
+ while ((c = nextopt("ls:")) != '\0') {
+ switch (c) {
+ default:
+#if DEBUG
+ abort();
+#endif
+ case 'l':
+ list = 1;
+ break;
+ case 's':
+ signo = get_signum(optionarg);
+ if (signo < 0) {
+ ash_msg_and_raise_error(
+ "invalid signal number or name: %s",
+ optionarg
+ );
+ }
+ break;
+ }
+ }
+ argv = argptr;
+ } else
+ argv++;
+ }
+
+ if (!list && signo < 0)
+ signo = SIGTERM;
+
+ if ((signo < 0 || !*argv) ^ list) {
+ goto usage;
+ }
+
+ if (list) {
+ const char *name;
+
+ if (!*argv) {
+ for (i = 1; i < NSIG; i++) {
+ name = get_signame(i);
+ if (isdigit(*name))
+ out1fmt(snlfmt, name);
+ }
+ return 0;
+ }
+ name = get_signame(signo);
+ if (!isdigit(*name))
+ ash_msg_and_raise_error("invalid signal number or exit status: %s", *argptr);
+ out1fmt(snlfmt, name);
+ return 0;
+ }
+
+ i = 0;
+ do {
+ if (**argv == '%') {
+ jp = getjob(*argv, 0);
+ pid = -jp->ps[0].pid;
+ } else {
+ pid = **argv == '-' ?
+ -number(*argv + 1) : number(*argv);
+ }
+ if (kill(pid, signo) != 0) {
+ ash_msg("(%d) - %m", pid);
+ i = 1;
+ }
+ } while (*++argv);
+
+ return i;
+}
+
+static void
+showpipe(struct job *jp, FILE *out)
+{
+ struct procstat *sp;
+ struct procstat *spend;
+
+ spend = jp->ps + jp->nprocs;
+ for (sp = jp->ps + 1; sp < spend; sp++)
+ fprintf(out, " | %s", sp->cmd);
+ outcslow('\n', out);
+ flush_stdout_stderr();
+}
+
+
+static int
+restartjob(struct job *jp, int mode)
+{
+ struct procstat *ps;
+ int i;
+ int status;
+ pid_t pgid;
+
+ INT_OFF;
+ if (jp->state == JOBDONE)
+ goto out;
+ jp->state = JOBRUNNING;
+ pgid = jp->ps->pid;
+ if (mode == FORK_FG)
+ xtcsetpgrp(ttyfd, pgid);
+ killpg(pgid, SIGCONT);
+ ps = jp->ps;
+ i = jp->nprocs;
+ do {
+ if (WIFSTOPPED(ps->status)) {
+ ps->status = -1;
+ }
+ } while (ps++, --i);
+ out:
+ status = (mode == FORK_FG) ? waitforjob(jp) : 0;
+ INT_ON;
+ return status;
+}
+
+static int
+fg_bgcmd(int argc, char **argv)
+{
+ struct job *jp;
+ FILE *out;
+ int mode;
+ int retval;
+
+ mode = (**argv == 'f') ? FORK_FG : FORK_BG;
+ nextopt(nullstr);
+ argv = argptr;
+ out = stdout;
+ do {
+ jp = getjob(*argv, 1);
+ if (mode == FORK_BG) {
+ set_curjob(jp, CUR_RUNNING);
+ fprintf(out, "[%d] ", jobno(jp));
+ }
+ outstr(jp->ps->cmd, out);
+ showpipe(jp, out);
+ retval = restartjob(jp, mode);
+ } while (*argv && *++argv);
+ return retval;
+}
+#endif
+
+static int
+sprint_status(char *s, int status, int sigonly)
+{
+ int col;
+ int st;
+
+ col = 0;
+ if (!WIFEXITED(status)) {
+#if JOBS
+ if (WIFSTOPPED(status))
+ st = WSTOPSIG(status);
+ else
+#endif
+ st = WTERMSIG(status);
+ if (sigonly) {
+ if (st == SIGINT || st == SIGPIPE)
+ goto out;
+#if JOBS
+ if (WIFSTOPPED(status))
+ goto out;
+#endif
+ }
+ st &= 0x7f;
+ col = fmtstr(s, 32, strsignal(st));
+ if (WCOREDUMP(status)) {
+ col += fmtstr(s + col, 16, " (core dumped)");
+ }
+ } else if (!sigonly) {
+ st = WEXITSTATUS(status);
+ if (st)
+ col = fmtstr(s, 16, "Done(%d)", st);
+ else
+ col = fmtstr(s, 16, "Done");
+ }
+ out:
+ return col;
+}
+
+/*
+ * Do a wait system call. If job control is compiled in, we accept
+ * stopped processes. If block is zero, we return a value of zero
+ * rather than blocking.
+ *
+ * System V doesn't have a non-blocking wait system call. It does
+ * have a SIGCLD signal that is sent to a process when one of it's
+ * children dies. The obvious way to use SIGCLD would be to install
+ * a handler for SIGCLD which simply bumped a counter when a SIGCLD
+ * was received, and have waitproc bump another counter when it got
+ * the status of a process. Waitproc would then know that a wait
+ * system call would not block if the two counters were different.
+ * This approach doesn't work because if a process has children that
+ * have not been waited for, System V will send it a SIGCLD when it
+ * installs a signal handler for SIGCLD. What this means is that when
+ * a child exits, the shell will be sent SIGCLD signals continuously
+ * until is runs out of stack space, unless it does a wait call before
+ * restoring the signal handler. The code below takes advantage of
+ * this (mis)feature by installing a signal handler for SIGCLD and
+ * then checking to see whether it was called. If there are any
+ * children to be waited for, it will be.
+ *
+ * If neither SYSV nor BSD is defined, we don't implement nonblocking
+ * waits at all. In this case, the user will not be informed when
+ * a background process until the next time she runs a real program
+ * (as opposed to running a builtin command or just typing return),
+ * and the jobs command may give out of date information.
+ */
+static int
+waitproc(int block, int *status)
+{
+ int flags = 0;
+
+#if JOBS
+ if (jobctl)
+ flags |= WUNTRACED;
+#endif
+ if (block == 0)
+ flags |= WNOHANG;
+ return wait3(status, flags, (struct rusage *)NULL);
+}
+
+/*
+ * Wait for a process to terminate.
+ */
+static int
+dowait(int block, struct job *job)
+{
+ int pid;
+ int status;
+ struct job *jp;
+ struct job *thisjob;
+ int state;
+
+ TRACE(("dowait(%d) called\n", block));
+ pid = waitproc(block, &status);
+ TRACE(("wait returns pid %d, status=%d\n", pid, status));
+ if (pid <= 0)
+ return pid;
+ INT_OFF;
+ thisjob = NULL;
+ for (jp = curjob; jp; jp = jp->prev_job) {
+ struct procstat *sp;
+ struct procstat *spend;
+ if (jp->state == JOBDONE)
+ continue;
+ state = JOBDONE;
+ spend = jp->ps + jp->nprocs;
+ sp = jp->ps;
+ do {
+ if (sp->pid == pid) {
+ TRACE(("Job %d: changing status of proc %d "
+ "from 0x%x to 0x%x\n",
+ jobno(jp), pid, sp->status, status));
+ sp->status = status;
+ thisjob = jp;
+ }
+ if (sp->status == -1)
+ state = JOBRUNNING;
+#if JOBS
+ if (state == JOBRUNNING)
+ continue;
+ if (WIFSTOPPED(sp->status)) {
+ jp->stopstatus = sp->status;
+ state = JOBSTOPPED;
+ }
+#endif
+ } while (++sp < spend);
+ if (thisjob)
+ goto gotjob;
+ }
+#if JOBS
+ if (!WIFSTOPPED(status))
+#endif
+
+ jobless--;
+ goto out;
+
+ gotjob:
+ if (state != JOBRUNNING) {
+ thisjob->changed = 1;
+
+ if (thisjob->state != state) {
+ TRACE(("Job %d: changing state from %d to %d\n",
+ jobno(thisjob), thisjob->state, state));
+ thisjob->state = state;
+#if JOBS
+ if (state == JOBSTOPPED) {
+ set_curjob(thisjob, CUR_STOPPED);
+ }
+#endif
+ }
+ }
+
+ out:
+ INT_ON;
+
+ if (thisjob && thisjob == job) {
+ char s[48 + 1];
+ int len;
+
+ len = sprint_status(s, status, 1);
+ if (len) {
+ s[len] = '\n';
+ s[len + 1] = 0;
+ out2str(s);
+ }
+ }
+ return pid;
+}
+
+#if JOBS
+static void
+showjob(FILE *out, struct job *jp, int mode)
+{
+ struct procstat *ps;
+ struct procstat *psend;
+ int col;
+ int indent;
+ char s[80];
+
+ ps = jp->ps;
+
+ if (mode & SHOW_PGID) {
+ /* just output process (group) id of pipeline */
+ fprintf(out, "%d\n", ps->pid);
+ return;
+ }
+
+ col = fmtstr(s, 16, "[%d] ", jobno(jp));
+ indent = col;
+
+ if (jp == curjob)
+ s[col - 2] = '+';
+ else if (curjob && jp == curjob->prev_job)
+ s[col - 2] = '-';
+
+ if (mode & SHOW_PID)
+ col += fmtstr(s + col, 16, "%d ", ps->pid);
+
+ psend = ps + jp->nprocs;
+
+ if (jp->state == JOBRUNNING) {
+ strcpy(s + col, "Running");
+ col += sizeof("Running") - 1;
+ } else {
+ int status = psend[-1].status;
+ if (jp->state == JOBSTOPPED)
+ status = jp->stopstatus;
+ col += sprint_status(s + col, status, 0);
+ }
+
+ goto start;
+
+ do {
+ /* for each process */
+ col = fmtstr(s, 48, " |\n%*c%d ", indent, ' ', ps->pid) - 3;
+ start:
+ fprintf(out, "%s%*c%s",
+ s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd
+ );
+ if (!(mode & SHOW_PID)) {
+ showpipe(jp, out);
+ break;
+ }
+ if (++ps == psend) {
+ outcslow('\n', out);
+ break;
+ }
+ } while (1);
+
+ jp->changed = 0;
+
+ if (jp->state == JOBDONE) {
+ TRACE(("showjob: freeing job %d\n", jobno(jp)));
+ freejob(jp);
+ }
+}
+
+static int
+jobscmd(int argc, char **argv)
+{
+ int mode, m;
+ FILE *out;
+
+ mode = 0;
+ while ((m = nextopt("lp"))) {
+ if (m == 'l')
+ mode = SHOW_PID;
+ else
+ mode = SHOW_PGID;
+ }
+
+ out = stdout;
+ argv = argptr;
+ if (*argv) {
+ do
+ showjob(out, getjob(*argv,0), mode);
+ while (*++argv);
+ } else
+ showjobs(out, mode);
+
+ return 0;
+}
+
+/*
+ * Print a list of jobs. If "change" is nonzero, only print jobs whose
+ * statuses have changed since the last call to showjobs.
+ */
+static void
+showjobs(FILE *out, int mode)
+{
+ struct job *jp;
+
+ TRACE(("showjobs(%x) called\n", mode));
+
+ /* If not even one one job changed, there is nothing to do */
+ while (dowait(DOWAIT_NORMAL, NULL) > 0)
+ continue;
+
+ for (jp = curjob; jp; jp = jp->prev_job) {
+ if (!(mode & SHOW_CHANGED) || jp->changed)
+ showjob(out, jp, mode);
+ }
+}
+#endif /* JOBS */
+
+static int
+getstatus(struct job *job)
+{
+ int status;
+ int retval;
+
+ status = job->ps[job->nprocs - 1].status;
+ retval = WEXITSTATUS(status);
+ if (!WIFEXITED(status)) {
+#if JOBS
+ retval = WSTOPSIG(status);
+ if (!WIFSTOPPED(status))
+#endif
+ {
+ /* XXX: limits number of signals */
+ retval = WTERMSIG(status);
+#if JOBS
+ if (retval == SIGINT)
+ job->sigint = 1;
+#endif
+ }
+ retval += 128;
+ }
+ TRACE(("getstatus: job %d, nproc %d, status %x, retval %x\n",
+ jobno(job), job->nprocs, status, retval));
+ return retval;
+}
+
+static int
+waitcmd(int argc, char **argv)
+{
+ struct job *job;
+ int retval;
+ struct job *jp;
+
+ EXSIGON;
+
+ nextopt(nullstr);
+ retval = 0;
+
+ argv = argptr;
+ if (!*argv) {
+ /* wait for all jobs */
+ for (;;) {
+ jp = curjob;
+ while (1) {
+ if (!jp) {
+ /* no running procs */
+ goto out;
+ }
+ if (jp->state == JOBRUNNING)
+ break;
+ jp->waited = 1;
+ jp = jp->prev_job;
+ }
+ dowait(DOWAIT_BLOCK, 0);
+ }
+ }
+
+ retval = 127;
+ do {
+ if (**argv != '%') {
+ pid_t pid = number(*argv);
+ job = curjob;
+ goto start;
+ do {
+ if (job->ps[job->nprocs - 1].pid == pid)
+ break;
+ job = job->prev_job;
+ start:
+ if (!job)
+ goto repeat;
+ } while (1);
+ } else
+ job = getjob(*argv, 0);
+ /* loop until process terminated or stopped */
+ while (job->state == JOBRUNNING)
+ dowait(DOWAIT_BLOCK, 0);
+ job->waited = 1;
+ retval = getstatus(job);
+ repeat:
+ ;
+ } while (*++argv);
+
+ out:
+ return retval;
+}
+
+static struct job *
+growjobtab(void)
+{
+ size_t len;
+ ptrdiff_t offset;
+ struct job *jp, *jq;
+
+ len = njobs * sizeof(*jp);
+ jq = jobtab;
+ jp = ckrealloc(jq, len + 4 * sizeof(*jp));
+
+ offset = (char *)jp - (char *)jq;
+ if (offset) {
+ /* Relocate pointers */
+ size_t l = len;
+
+ jq = (struct job *)((char *)jq + l);
+ while (l) {
+ l -= sizeof(*jp);
+ jq--;
+#define joff(p) ((struct job *)((char *)(p) + l))
+#define jmove(p) (p) = (void *)((char *)(p) + offset)
+ if (joff(jp)->ps == &jq->ps0)
+ jmove(joff(jp)->ps);
+ if (joff(jp)->prev_job)
+ jmove(joff(jp)->prev_job);
+ }
+ if (curjob)
+ jmove(curjob);
+#undef joff
+#undef jmove
+ }
+
+ njobs += 4;
+ jobtab = jp;
+ jp = (struct job *)((char *)jp + len);
+ jq = jp + 3;
+ do {
+ jq->used = 0;
+ } while (--jq >= jp);
+ return jp;
+}
+
+/*
+ * Return a new job structure.
+ * Called with interrupts off.
+ */
+static struct job *
+makejob(union node *node, int nprocs)
+{
+ int i;
+ struct job *jp;
+
+ for (i = njobs, jp = jobtab; ; jp++) {
+ if (--i < 0) {
+ jp = growjobtab();
+ break;
+ }
+ if (jp->used == 0)
+ break;
+ if (jp->state != JOBDONE || !jp->waited)
+ continue;
+#if JOBS
+ if (jobctl)
+ continue;
+#endif
+ freejob(jp);
+ break;
+ }
+ memset(jp, 0, sizeof(*jp));
+#if JOBS
+ if (jobctl)
+ jp->jobctl = 1;
+#endif
+ jp->prev_job = curjob;
+ curjob = jp;
+ jp->used = 1;
+ jp->ps = &jp->ps0;
+ if (nprocs > 1) {
+ jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
+ }
+ TRACE(("makejob(0x%lx, %d) returns %%%d\n", (long)node, nprocs,
+ jobno(jp)));
+ return jp;
+}
+
+#if JOBS
+/*
+ * Return a string identifying a command (to be printed by the
+ * jobs command).
+ */
+static char *cmdnextc;
+
+static void
+cmdputs(const char *s)
+{
+ const char *p, *str;
+ char c, cc[2] = " ";
+ char *nextc;
+ int subtype = 0;
+ int quoted = 0;
+ static const char vstype[VSTYPE + 1][4] = {
+ "", "}", "-", "+", "?", "=",
+ "%", "%%", "#", "##"
+ };
+
+ nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
+ p = s;
+ while ((c = *p++) != 0) {
+ str = 0;
+ switch (c) {
+ case CTLESC:
+ c = *p++;
+ break;
+ case CTLVAR:
+ subtype = *p++;
+ if ((subtype & VSTYPE) == VSLENGTH)
+ str = "${#";
+ else
+ str = "${";
+ if (!(subtype & VSQUOTE) == !(quoted & 1))
+ goto dostr;
+ quoted ^= 1;
+ c = '"';
+ break;
+ case CTLENDVAR:
+ str = "\"}" + !(quoted & 1);
+ quoted >>= 1;
+ subtype = 0;
+ goto dostr;
+ case CTLBACKQ:
+ str = "$(...)";
+ goto dostr;
+ case CTLBACKQ+CTLQUOTE:
+ str = "\"$(...)\"";
+ goto dostr;
+#if ENABLE_ASH_MATH_SUPPORT
+ case CTLARI:
+ str = "$((";
+ goto dostr;
+ case CTLENDARI:
+ str = "))";
+ goto dostr;
+#endif
+ case CTLQUOTEMARK:
+ quoted ^= 1;
+ c = '"';
+ break;
+ case '=':
+ if (subtype == 0)
+ break;
+ if ((subtype & VSTYPE) != VSNORMAL)
+ quoted <<= 1;
+ str = vstype[subtype & VSTYPE];
+ if (subtype & VSNUL)
+ c = ':';
+ else
+ goto checkstr;
+ break;
+ case '\'':
+ case '\\':
+ case '"':
+ case '$':
+ /* These can only happen inside quotes */
+ cc[0] = c;
+ str = cc;
+ c = '\\';
+ break;
+ default:
+ break;
+ }
+ USTPUTC(c, nextc);
+ checkstr:
+ if (!str)
+ continue;
+ dostr:
+ while ((c = *str++)) {
+ USTPUTC(c, nextc);
+ }
+ }
+ if (quoted & 1) {
+ USTPUTC('"', nextc);
+ }
+ *nextc = 0;
+ cmdnextc = nextc;
+}
+
+/* cmdtxt() and cmdlist() call each other */
+static void cmdtxt(union node *n);
+
+static void
+cmdlist(union node *np, int sep)
+{
+ for (; np; np = np->narg.next) {
+ if (!sep)
+ cmdputs(" ");
+ cmdtxt(np);
+ if (sep && np->narg.next)
+ cmdputs(" ");
+ }
+}
+
+static void
+cmdtxt(union node *n)
+{
+ union node *np;
+ struct nodelist *lp;
+ const char *p;
+ char s[2];
+
+ if (!n)
+ return;
+ switch (n->type) {
+ default:
+#if DEBUG
+ abort();
+#endif
+ case NPIPE:
+ lp = n->npipe.cmdlist;
+ for (;;) {
+ cmdtxt(lp->n);
+ lp = lp->next;
+ if (!lp)
+ break;
+ cmdputs(" | ");
+ }
+ break;
+ case NSEMI:
+ p = "; ";
+ goto binop;
+ case NAND:
+ p = " && ";
+ goto binop;
+ case NOR:
+ p = " || ";
+ binop:
+ cmdtxt(n->nbinary.ch1);
+ cmdputs(p);
+ n = n->nbinary.ch2;
+ goto donode;
+ case NREDIR:
+ case NBACKGND:
+ n = n->nredir.n;
+ goto donode;
+ case NNOT:
+ cmdputs("!");
+ n = n->nnot.com;
+ donode:
+ cmdtxt(n);
+ break;
+ case NIF:
+ cmdputs("if ");
+ cmdtxt(n->nif.test);
+ cmdputs("; then ");
+ n = n->nif.ifpart;
+ if (n->nif.elsepart) {
+ cmdtxt(n);
+ cmdputs("; else ");
+ n = n->nif.elsepart;
+ }
+ p = "; fi";
+ goto dotail;
+ case NSUBSHELL:
+ cmdputs("(");
+ n = n->nredir.n;
+ p = ")";
+ goto dotail;
+ case NWHILE:
+ p = "while ";
+ goto until;
+ case NUNTIL:
+ p = "until ";
+ until:
+ cmdputs(p);
+ cmdtxt(n->nbinary.ch1);
+ n = n->nbinary.ch2;
+ p = "; done";
+ dodo:
+ cmdputs("; do ");
+ dotail:
+ cmdtxt(n);
+ goto dotail2;
+ case NFOR:
+ cmdputs("for ");
+ cmdputs(n->nfor.var);
+ cmdputs(" in ");
+ cmdlist(n->nfor.args, 1);
+ n = n->nfor.body;
+ p = "; done";
+ goto dodo;
+ case NDEFUN:
+ cmdputs(n->narg.text);
+ p = "() { ... }";
+ goto dotail2;
+ case NCMD:
+ cmdlist(n->ncmd.args, 1);
+ cmdlist(n->ncmd.redirect, 0);
+ break;
+ case NARG:
+ p = n->narg.text;
+ dotail2:
+ cmdputs(p);
+ break;
+ case NHERE:
+ case NXHERE:
+ p = "<<...";
+ goto dotail2;
+ case NCASE:
+ cmdputs("case ");
+ cmdputs(n->ncase.expr->narg.text);
+ cmdputs(" in ");
+ for (np = n->ncase.cases; np; np = np->nclist.next) {
+ cmdtxt(np->nclist.pattern);
+ cmdputs(") ");
+ cmdtxt(np->nclist.body);
+ cmdputs(";; ");
+ }
+ p = "esac";
+ goto dotail2;
+ case NTO:
+ p = ">";
+ goto redir;
+ case NCLOBBER:
+ p = ">|";
+ goto redir;
+ case NAPPEND:
+ p = ">>";
+ goto redir;
+ case NTOFD:
+ p = ">&";
+ goto redir;
+ case NFROM:
+ p = "<";
+ goto redir;
+ case NFROMFD:
+ p = "<&";
+ goto redir;
+ case NFROMTO:
+ p = "<>";
+ redir:
+ s[0] = n->nfile.fd + '0';
+ s[1] = '\0';
+ cmdputs(s);
+ cmdputs(p);
+ if (n->type == NTOFD || n->type == NFROMFD) {
+ s[0] = n->ndup.dupfd + '0';
+ p = s;
+ goto dotail2;
+ }
+ n = n->nfile.fname;
+ goto donode;
+ }
+}
+
+static char *
+commandtext(union node *n)
+{
+ char *name;
+
+ STARTSTACKSTR(cmdnextc);
+ cmdtxt(n);
+ name = stackblock();
+ TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
+ name, cmdnextc, cmdnextc));
+ return ckstrdup(name);
+}
+#endif /* JOBS */
+
+/*
+ * Fork off a subshell. If we are doing job control, give the subshell its
+ * own process group. Jp is a job structure that the job is to be added to.
+ * N is the command that will be evaluated by the child. Both jp and n may
+ * be NULL. The mode parameter can be one of the following:
+ * FORK_FG - Fork off a foreground process.
+ * FORK_BG - Fork off a background process.
+ * FORK_NOJOB - Like FORK_FG, but don't give the process its own
+ * process group even if job control is on.
+ *
+ * When job control is turned off, background processes have their standard
+ * input redirected to /dev/null (except for the second and later processes
+ * in a pipeline).
+ *
+ * Called with interrupts off.
+ */
+/*
+ * Clear traps on a fork.
+ */
+static void
+clear_traps(void)
+{
+ char **tp;
+
+ for (tp = trap; tp < &trap[NSIG]; tp++) {
+ if (*tp && **tp) { /* trap not NULL or SIG_IGN */
+ INT_OFF;
+ free(*tp);
+ *tp = NULL;
+ if (tp != &trap[0])
+ setsignal(tp - trap);
+ INT_ON;
+ }
+ }
+}
+/* lives far away from here, needed for forkchild */
+static void closescript(void);
+static void
+forkchild(struct job *jp, union node *n, int mode)
+{
+ int oldlvl;
+
+ TRACE(("Child shell %d\n", getpid()));
+ oldlvl = shlvl;
+ shlvl++;
+
+ closescript();
+ clear_traps();
+#if JOBS
+ /* do job control only in root shell */
+ jobctl = 0;
+ if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) {
+ pid_t pgrp;
+
+ if (jp->nprocs == 0)
+ pgrp = getpid();
+ else
+ pgrp = jp->ps[0].pid;
+ /* This can fail because we are doing it in the parent also */
+ (void)setpgid(0, pgrp);
+ if (mode == FORK_FG)
+ xtcsetpgrp(ttyfd, pgrp);
+ setsignal(SIGTSTP);
+ setsignal(SIGTTOU);
+ } else
+#endif
+ if (mode == FORK_BG) {
+ ignoresig(SIGINT);
+ ignoresig(SIGQUIT);
+ if (jp->nprocs == 0) {
+ close(0);
+ if (open(bb_dev_null, O_RDONLY) != 0)
+ ash_msg_and_raise_error("can't open %s", bb_dev_null);
+ }
+ }
+ if (!oldlvl && iflag) {
+ setsignal(SIGINT);
+ setsignal(SIGQUIT);
+ setsignal(SIGTERM);
+ }
+ for (jp = curjob; jp; jp = jp->prev_job)
+ freejob(jp);
+ jobless = 0;
+}
+
+static void
+forkparent(struct job *jp, union node *n, int mode, pid_t pid)
+{
+ TRACE(("In parent shell: child = %d\n", pid));
+ if (!jp) {
+ while (jobless && dowait(DOWAIT_NORMAL, 0) > 0);
+ jobless++;
+ return;
+ }
+#if JOBS
+ if (mode != FORK_NOJOB && jp->jobctl) {
+ int pgrp;
+
+ if (jp->nprocs == 0)
+ pgrp = pid;
+ else
+ pgrp = jp->ps[0].pid;
+ /* This can fail because we are doing it in the child also */
+ setpgid(pid, pgrp);
+ }
+#endif
+ if (mode == FORK_BG) {
+ backgndpid = pid; /* set $! */
+ set_curjob(jp, CUR_RUNNING);
+ }
+ if (jp) {
+ struct procstat *ps = &jp->ps[jp->nprocs++];
+ ps->pid = pid;
+ ps->status = -1;
+ ps->cmd = nullstr;
+#if JOBS
+ if (jobctl && n)
+ ps->cmd = commandtext(n);
+#endif
+ }
+}
+
+static int
+forkshell(struct job *jp, union node *n, int mode)
+{
+ int pid;
+
+ TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
+ pid = fork();
+ if (pid < 0) {
+ TRACE(("Fork failed, errno=%d", errno));
+ if (jp)
+ freejob(jp);
+ ash_msg_and_raise_error("cannot fork");
+ }
+ if (pid == 0)
+ forkchild(jp, n, mode);
+ else
+ forkparent(jp, n, mode, pid);
+ return pid;
+}
+
+/*
+ * Wait for job to finish.
+ *
+ * Under job control we have the problem that while a child process is
+ * running interrupts generated by the user are sent to the child but not
+ * to the shell. This means that an infinite loop started by an inter-
+ * active user may be hard to kill. With job control turned off, an
+ * interactive user may place an interactive program inside a loop. If
+ * the interactive program catches interrupts, the user doesn't want
+ * these interrupts to also abort the loop. The approach we take here
+ * is to have the shell ignore interrupt signals while waiting for a
+ * foreground process to terminate, and then send itself an interrupt
+ * signal if the child process was terminated by an interrupt signal.
+ * Unfortunately, some programs want to do a bit of cleanup and then
+ * exit on interrupt; unless these processes terminate themselves by
+ * sending a signal to themselves (instead of calling exit) they will
+ * confuse this approach.
+ *
+ * Called with interrupts off.
+ */
+static int
+waitforjob(struct job *jp)
+{
+ int st;
+
+ TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
+ while (jp->state == JOBRUNNING) {
+ dowait(DOWAIT_BLOCK, jp);
+ }
+ st = getstatus(jp);
+#if JOBS
+ if (jp->jobctl) {
+ xtcsetpgrp(ttyfd, rootpid);
+ /*
+ * This is truly gross.
+ * If we're doing job control, then we did a TIOCSPGRP which
+ * caused us (the shell) to no longer be in the controlling
+ * session -- so we wouldn't have seen any ^C/SIGINT. So, we
+ * intuit from the subprocess exit status whether a SIGINT
+ * occurred, and if so interrupt ourselves. Yuck. - mycroft
+ */
+ if (jp->sigint)
+ raise(SIGINT);
+ }
+ if (jp->state == JOBDONE)
+#endif
+ freejob(jp);
+ return st;
+}
+
+/*
+ * return 1 if there are stopped jobs, otherwise 0
+ */
+static int
+stoppedjobs(void)
+{
+ struct job *jp;
+ int retval;
+
+ retval = 0;
+ if (job_warning)
+ goto out;
+ jp = curjob;
+ if (jp && jp->state == JOBSTOPPED) {
+ out2str("You have stopped jobs.\n");
+ job_warning = 2;
+ retval++;
+ }
+ out:
+ return retval;
+}
+
+
+/* ============ redir.c
+ *
+ * Code for dealing with input/output redirection.
+ */
+
+#define EMPTY -2 /* marks an unused slot in redirtab */
+#ifndef PIPE_BUF
+# define PIPESIZE 4096 /* amount of buffering in a pipe */
+#else
+# define PIPESIZE PIPE_BUF
+#endif
+
+/*
+ * Open a file in noclobber mode.
+ * The code was copied from bash.
+ */
+static int
+noclobberopen(const char *fname)
+{
+ int r, fd;
+ struct stat finfo, finfo2;
+
+ /*
+ * If the file exists and is a regular file, return an error
+ * immediately.
+ */
+ r = stat(fname, &finfo);
+ if (r == 0 && S_ISREG(finfo.st_mode)) {
+ errno = EEXIST;
+ return -1;
+ }
+
+ /*
+ * If the file was not present (r != 0), make sure we open it
+ * exclusively so that if it is created before we open it, our open
+ * will fail. Make sure that we do not truncate an existing file.
+ * Note that we don't turn on O_EXCL unless the stat failed -- if the
+ * file was not a regular file, we leave O_EXCL off.
+ */
+ if (r != 0)
+ return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
+ fd = open(fname, O_WRONLY|O_CREAT, 0666);
+
+ /* If the open failed, return the file descriptor right away. */
+ if (fd < 0)
+ return fd;
+
+ /*
+ * OK, the open succeeded, but the file may have been changed from a
+ * non-regular file to a regular file between the stat and the open.
+ * We are assuming that the O_EXCL open handles the case where FILENAME
+ * did not exist and is symlinked to an existing file between the stat
+ * and open.
+ */
+
+ /*
+ * If we can open it and fstat the file descriptor, and neither check
+ * revealed that it was a regular file, and the file has not been
+ * replaced, return the file descriptor.
+ */
+ if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode)
+ && finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
+ return fd;
+
+ /* The file has been replaced. badness. */
+ close(fd);
+ errno = EEXIST;
+ return -1;
+}
+
+/*
+ * Handle here documents. Normally we fork off a process to write the
+ * data to a pipe. If the document is short, we can stuff the data in
+ * the pipe without forking.
+ */
+/* openhere needs this forward reference */
+static void expandhere(union node *arg, int fd);
+static int
+openhere(union node *redir)
+{
+ int pip[2];
+ size_t len = 0;
+
+ if (pipe(pip) < 0)
+ ash_msg_and_raise_error("pipe call failed");
+ if (redir->type == NHERE) {
+ len = strlen(redir->nhere.doc->narg.text);
+ if (len <= PIPESIZE) {
+ full_write(pip[1], redir->nhere.doc->narg.text, len);
+ goto out;
+ }
+ }
+ if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
+ close(pip[0]);
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ signal(SIGHUP, SIG_IGN);
+#ifdef SIGTSTP
+ signal(SIGTSTP, SIG_IGN);
+#endif
+ signal(SIGPIPE, SIG_DFL);
+ if (redir->type == NHERE)
+ full_write(pip[1], redir->nhere.doc->narg.text, len);
+ else
+ expandhere(redir->nhere.doc, pip[1]);
+ _exit(0);
+ }
+ out:
+ close(pip[1]);
+ return pip[0];
+}
+
+static int
+openredirect(union node *redir)
+{
+ char *fname;
+ int f;
+
+ switch (redir->nfile.type) {
+ case NFROM:
+ fname = redir->nfile.expfname;
+ f = open(fname, O_RDONLY);
+ if (f < 0)
+ goto eopen;
+ break;
+ case NFROMTO:
+ fname = redir->nfile.expfname;
+ f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666);
+ if (f < 0)
+ goto ecreate;
+ break;
+ case NTO:
+ /* Take care of noclobber mode. */
+ if (Cflag) {
+ fname = redir->nfile.expfname;
+ f = noclobberopen(fname);
+ if (f < 0)
+ goto ecreate;
+ break;
+ }
+ /* FALLTHROUGH */
+ case NCLOBBER:
+ fname = redir->nfile.expfname;
+ f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
+ if (f < 0)
+ goto ecreate;
+ break;
+ case NAPPEND:
+ fname = redir->nfile.expfname;
+ f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
+ if (f < 0)
+ goto ecreate;
+ break;
+ default:
+#if DEBUG
+ abort();
+#endif
+ /* Fall through to eliminate warning. */
+ case NTOFD:
+ case NFROMFD:
+ f = -1;
+ break;
+ case NHERE:
+ case NXHERE:
+ f = openhere(redir);
+ break;
+ }
+
+ return f;
+ ecreate:
+ ash_msg_and_raise_error("cannot create %s: %s", fname, errmsg(errno, "nonexistent directory"));
+ eopen:
+ ash_msg_and_raise_error("cannot open %s: %s", fname, errmsg(errno, "no such file"));
+}
+
+/*
+ * Copy a file descriptor to be >= to. Returns -1
+ * if the source file descriptor is closed, EMPTY if there are no unused
+ * file descriptors left.
+ */
+static int
+copyfd(int from, int to)
+{
+ int newfd;
+
+ newfd = fcntl(from, F_DUPFD, to);
+ if (newfd < 0) {
+ if (errno == EMFILE)
+ return EMPTY;
+ ash_msg_and_raise_error("%d: %m", from);
+ }
+ return newfd;
+}
+
+static void
+dupredirect(union node *redir, int f)
+{
+ int fd = redir->nfile.fd;
+
+ if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
+ if (redir->ndup.dupfd >= 0) { /* if not ">&-" */
+ copyfd(redir->ndup.dupfd, fd);
+ }
+ return;
+ }
+
+ if (f != fd) {
+ copyfd(f, fd);
+ close(f);
+ }
+}
+
+/*
+ * Process a list of redirection commands. If the REDIR_PUSH flag is set,
+ * old file descriptors are stashed away so that the redirection can be
+ * undone by calling popredir. If the REDIR_BACKQ flag is set, then the
+ * standard output, and the standard error if it becomes a duplicate of
+ * stdout, is saved in memory.
+ */
+/* flags passed to redirect */
+#define REDIR_PUSH 01 /* save previous values of file descriptors */
+#define REDIR_SAVEFD2 03 /* set preverrout */
+static void
+redirect(union node *redir, int flags)
+{
+ union node *n;
+ struct redirtab *sv;
+ int i;
+ int fd;
+ int newfd;
+ int *p;
+ nullredirs++;
+ if (!redir) {
+ return;
+ }
+ sv = NULL;
+ INT_OFF;
+ if (flags & REDIR_PUSH) {
+ struct redirtab *q;
+ q = ckmalloc(sizeof(struct redirtab));
+ q->next = redirlist;
+ redirlist = q;
+ q->nullredirs = nullredirs - 1;
+ for (i = 0; i < 10; i++)
+ q->renamed[i] = EMPTY;
+ nullredirs = 0;
+ sv = q;
+ }
+ n = redir;
+ do {
+ fd = n->nfile.fd;
+ if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD)
+ && n->ndup.dupfd == fd)
+ continue; /* redirect from/to same file descriptor */
+
+ newfd = openredirect(n);
+ if (fd == newfd)
+ continue;
+ if (sv && *(p = &sv->renamed[fd]) == EMPTY) {
+ i = fcntl(fd, F_DUPFD, 10);
+
+ if (i == -1) {
+ i = errno;
+ if (i != EBADF) {
+ close(newfd);
+ errno = i;
+ ash_msg_and_raise_error("%d: %m", fd);
+ /* NOTREACHED */
+ }
+ } else {
+ *p = i;
+ close(fd);
+ }
+ } else {
+ close(fd);
+ }
+ dupredirect(n, newfd);
+ } while ((n = n->nfile.next));
+ INT_ON;
+ if (flags & REDIR_SAVEFD2 && sv && sv->renamed[2] >= 0)
+ preverrout_fd = sv->renamed[2];
+}
+
+/*
+ * Undo the effects of the last redirection.
+ */
+static void
+popredir(int drop)
+{
+ struct redirtab *rp;
+ int i;
+
+ if (--nullredirs >= 0)
+ return;
+ INT_OFF;
+ rp = redirlist;
+ for (i = 0; i < 10; i++) {
+ if (rp->renamed[i] != EMPTY) {
+ if (!drop) {
+ close(i);
+ copyfd(rp->renamed[i], i);
+ }
+ close(rp->renamed[i]);
+ }
+ }
+ redirlist = rp->next;
+ nullredirs = rp->nullredirs;
+ free(rp);
+ INT_ON;
+}
+
+/*
+ * Undo all redirections. Called on error or interrupt.
+ */
+
+/*
+ * Discard all saved file descriptors.
+ */
+static void
+clearredir(int drop)
+{
+ for (;;) {
+ nullredirs = 0;
+ if (!redirlist)
+ break;
+ popredir(drop);
+ }
+}
+
+static int
+redirectsafe(union node *redir, int flags)
+{
+ int err;
+ volatile int saveint;
+ struct jmploc *volatile savehandler = exception_handler;
+ struct jmploc jmploc;
+
+ SAVE_INT(saveint);
+ err = setjmp(jmploc.loc) * 2;
+ if (!err) {
+ exception_handler = &jmploc;
+ redirect(redir, flags);
+ }
+ exception_handler = savehandler;
+ if (err && exception != EXERROR)
+ longjmp(exception_handler->loc, 1);
+ RESTORE_INT(saveint);
+ return err;
+}
+
+
+/* ============ Routines to expand arguments to commands
+ *
+ * We have to deal with backquotes, shell variables, and file metacharacters.
+ */
+
+/*
+ * expandarg flags
+ */
+#define EXP_FULL 0x1 /* perform word splitting & file globbing */
+#define EXP_TILDE 0x2 /* do normal tilde expansion */
+#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
+#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
+#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
+#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
+#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
+#define EXP_WORD 0x80 /* expand word in parameter expansion */
+#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */
+/*
+ * _rmescape() flags
+ */
+#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
+#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
+#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
+#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
+#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
+
+/*
+ * Structure specifying which parts of the string should be searched
+ * for IFS characters.
+ */
+struct ifsregion {
+ struct ifsregion *next; /* next region in list */
+ int begoff; /* offset of start of region */
+ int endoff; /* offset of end of region */
+ int nulonly; /* search for nul bytes only */
+};
+
+struct arglist {
+ struct strlist *list;
+ struct strlist **lastp;
+};
+
+/* output of current string */
+static char *expdest;
+/* list of back quote expressions */
+static struct nodelist *argbackq;
+/* first struct in list of ifs regions */
+static struct ifsregion ifsfirst;
+/* last struct in list */
+static struct ifsregion *ifslastp;
+/* holds expanded arg list */
+static struct arglist exparg;
+
+/*
+ * Our own itoa().
+ */
+static int
+cvtnum(arith_t num)
+{
+ int len;
+
+ expdest = makestrspace(32, expdest);
+#if ENABLE_ASH_MATH_SUPPORT_64
+ len = fmtstr(expdest, 32, "%lld", (long long) num);
+#else
+ len = fmtstr(expdest, 32, "%ld", num);
+#endif
+ STADJUST(len, expdest);
+ return len;
+}
+
+static size_t
+esclen(const char *start, const char *p)
+{
+ size_t esc = 0;
+
+ while (p > start && *--p == CTLESC) {
+ esc++;
+ }
+ return esc;
+}
+
+/*
+ * Remove any CTLESC characters from a string.
+ */
+static char *
+_rmescapes(char *str, int flag)
+{
+ char *p, *q, *r;
+ static const char qchars[] = { CTLESC, CTLQUOTEMARK, 0 };
+ unsigned inquotes;
+ int notescaped;
+ int globbing;
+
+ p = strpbrk(str, qchars);
+ if (!p) {
+ return str;
+ }
+ q = p;
+ r = str;
+ if (flag & RMESCAPE_ALLOC) {
+ size_t len = p - str;
+ size_t fulllen = len + strlen(p) + 1;
+
+ if (flag & RMESCAPE_GROW) {
+ r = makestrspace(fulllen, expdest);
+ } else if (flag & RMESCAPE_HEAP) {
+ r = ckmalloc(fulllen);
+ } else {
+ r = stalloc(fulllen);
+ }
+ q = r;
+ if (len > 0) {
+ q = memcpy(q, str, len) + len;
+ }
+ }
+ inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
+ globbing = flag & RMESCAPE_GLOB;
+ notescaped = globbing;
+ while (*p) {
+ if (*p == CTLQUOTEMARK) {
+ inquotes = ~inquotes;
+ p++;
+ notescaped = globbing;
+ continue;
+ }
+ if (*p == '\\') {
+ /* naked back slash */
+ notescaped = 0;
+ goto copy;
+ }
+ if (*p == CTLESC) {
+ p++;
+ if (notescaped && inquotes && *p != '/') {
+ *q++ = '\\';
+ }
+ }
+ notescaped = globbing;
+ copy:
+ *q++ = *p++;
+ }
+ *q = '\0';
+ if (flag & RMESCAPE_GROW) {
+ expdest = r;
+ STADJUST(q - r + 1, expdest);
+ }
+ return r;
+}
+#define rmescapes(p) _rmescapes((p), 0)
+
+#define pmatch(a, b) !fnmatch((a), (b), 0)
+
+/*
+ * Prepare a pattern for a expmeta (internal glob(3)) call.
+ *
+ * Returns an stalloced string.
+ */
+static char *
+preglob(const char *pattern, int quoted, int flag)
+{
+ flag |= RMESCAPE_GLOB;
+ if (quoted) {
+ flag |= RMESCAPE_QUOTED;
+ }
+ return _rmescapes((char *)pattern, flag);
+}
+
+/*
+ * Put a string on the stack.
+ */
+static void
+memtodest(const char *p, size_t len, int syntax, int quotes)
+{
+ char *q = expdest;
+
+ q = makestrspace(len * 2, q);
+
+ while (len--) {
+ int c = signed_char2int(*p++);
+ if (!c)
+ continue;
+ if (quotes && (SIT(c, syntax) == CCTL || SIT(c, syntax) == CBACK))
+ USTPUTC(CTLESC, q);
+ USTPUTC(c, q);
+ }
+
+ expdest = q;
+}
+
+static void
+strtodest(const char *p, int syntax, int quotes)
+{
+ memtodest(p, strlen(p), syntax, quotes);
+}
+
+/*
+ * Record the fact that we have to scan this region of the
+ * string for IFS characters.
+ */
+static void
+recordregion(int start, int end, int nulonly)
+{
+ struct ifsregion *ifsp;
+
+ if (ifslastp == NULL) {
+ ifsp = &ifsfirst;
+ } else {
+ INT_OFF;
+ ifsp = ckmalloc(sizeof(*ifsp));
+ ifsp->next = NULL;
+ ifslastp->next = ifsp;
+ INT_ON;
+ }
+ ifslastp = ifsp;
+ ifslastp->begoff = start;
+ ifslastp->endoff = end;
+ ifslastp->nulonly = nulonly;
+}
+
+static void
+removerecordregions(int endoff)
+{
+ if (ifslastp == NULL)
+ return;
+
+ if (ifsfirst.endoff > endoff) {
+ while (ifsfirst.next != NULL) {
+ struct ifsregion *ifsp;
+ INT_OFF;
+ ifsp = ifsfirst.next->next;
+ free(ifsfirst.next);
+ ifsfirst.next = ifsp;
+ INT_ON;
+ }
+ if (ifsfirst.begoff > endoff)
+ ifslastp = NULL;
+ else {
+ ifslastp = &ifsfirst;
+ ifsfirst.endoff = endoff;
+ }
+ return;
+ }
+
+ ifslastp = &ifsfirst;
+ while (ifslastp->next && ifslastp->next->begoff < endoff)
+ ifslastp=ifslastp->next;
+ while (ifslastp->next != NULL) {
+ struct ifsregion *ifsp;
+ INT_OFF;
+ ifsp = ifslastp->next->next;
+ free(ifslastp->next);
+ ifslastp->next = ifsp;
+ INT_ON;
+ }
+ if (ifslastp->endoff > endoff)
+ ifslastp->endoff = endoff;
+}
+
+static char *
+exptilde(char *startp, char *p, int flag)
+{
+ char c;
+ char *name;
+ struct passwd *pw;
+ const char *home;
+ int quotes = flag & (EXP_FULL | EXP_CASE);
+ int startloc;
+
+ name = p + 1;
+
+ while ((c = *++p) != '\0') {
+ switch (c) {
+ case CTLESC:
+ return startp;
+ case CTLQUOTEMARK:
+ return startp;
+ case ':':
+ if (flag & EXP_VARTILDE)
+ goto done;
+ break;
+ case '/':
+ case CTLENDVAR:
+ goto done;
+ }
+ }
+ done:
+ *p = '\0';
+ if (*name == '\0') {
+ home = lookupvar(homestr);
+ } else {
+ pw = getpwnam(name);
+ if (pw == NULL)
+ goto lose;
+ home = pw->pw_dir;
+ }
+ if (!home || !*home)
+ goto lose;
+ *p = c;
+ startloc = expdest - (char *)stackblock();
+ strtodest(home, SQSYNTAX, quotes);
+ recordregion(startloc, expdest - (char *)stackblock(), 0);
+ return p;
+ lose:
+ *p = c;
+ return startp;
+}
+
+/*
+ * Execute a command inside back quotes. If it's a builtin command, we
+ * want to save its output in a block obtained from malloc. Otherwise
+ * we fork off a subprocess and get the output of the command via a pipe.
+ * Should be called with interrupts off.
+ */
+struct backcmd { /* result of evalbackcmd */
+ int fd; /* file descriptor to read from */
+ char *buf; /* buffer */
+ int nleft; /* number of chars in buffer */
+ struct job *jp; /* job structure for command */
+};
+
+/* These forward decls are needed to use "eval" code for backticks handling: */
+static int back_exitstatus; /* exit status of backquoted command */
+#define EV_EXIT 01 /* exit after evaluating tree */
+static void evaltree(union node *, int);
+
+static void
+evalbackcmd(union node *n, struct backcmd *result)
+{
+ int saveherefd;
+
+ result->fd = -1;
+ result->buf = NULL;
+ result->nleft = 0;
+ result->jp = NULL;
+ if (n == NULL) {
+ goto out;
+ }
+
+ saveherefd = herefd;
+ herefd = -1;
+
+ {
+ int pip[2];
+ struct job *jp;
+
+ if (pipe(pip) < 0)
+ ash_msg_and_raise_error("pipe call failed");
+ jp = makejob(n, 1);
+ if (forkshell(jp, n, FORK_NOJOB) == 0) {
+ FORCE_INT_ON;
+ close(pip[0]);
+ if (pip[1] != 1) {
+ close(1);
+ copyfd(pip[1], 1);
+ close(pip[1]);
+ }
+ eflag = 0;
+ evaltree(n, EV_EXIT); /* actually evaltreenr... */
+ /* NOTREACHED */
+ }
+ close(pip[1]);
+ result->fd = pip[0];
+ result->jp = jp;
+ }
+ herefd = saveherefd;
+ out:
+ TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
+ result->fd, result->buf, result->nleft, result->jp));
+}
+
+/*
+ * Expand stuff in backwards quotes.
+ */
+static void
+expbackq(union node *cmd, int quoted, int quotes)
+{
+ struct backcmd in;
+ int i;
+ char buf[128];
+ char *p;
+ char *dest;
+ int startloc;
+ int syntax = quoted? DQSYNTAX : BASESYNTAX;
+ struct stackmark smark;
+
+ INT_OFF;
+ setstackmark(&smark);
+ dest = expdest;
+ startloc = dest - (char *)stackblock();
+ grabstackstr(dest);
+ evalbackcmd(cmd, &in);
+ popstackmark(&smark);
+
+ p = in.buf;
+ i = in.nleft;
+ if (i == 0)
+ goto read;
+ for (;;) {
+ memtodest(p, i, syntax, quotes);
+ read:
+ if (in.fd < 0)
+ break;
+ i = safe_read(in.fd, buf, sizeof(buf));
+ TRACE(("expbackq: read returns %d\n", i));
+ if (i <= 0)
+ break;
+ p = buf;
+ }
+
+ if (in.buf)
+ free(in.buf);
+ if (in.fd >= 0) {
+ close(in.fd);
+ back_exitstatus = waitforjob(in.jp);
+ }
+ INT_ON;
+
+ /* Eat all trailing newlines */
+ dest = expdest;
+ for (; dest > (char *)stackblock() && dest[-1] == '\n';)
+ STUNPUTC(dest);
+ expdest = dest;
+
+ if (quoted == 0)
+ recordregion(startloc, dest - (char *)stackblock(), 0);
+ TRACE(("evalbackq: size=%d: \"%.*s\"\n",
+ (dest - (char *)stackblock()) - startloc,
+ (dest - (char *)stackblock()) - startloc,
+ stackblock() + startloc));
+}
+
+#if ENABLE_ASH_MATH_SUPPORT
+/*
+ * Expand arithmetic expression. Backup to start of expression,
+ * evaluate, place result in (backed up) result, adjust string position.
+ */
+static void
+expari(int quotes)
+{
+ char *p, *start;
+ int begoff;
+ int flag;
+ int len;
+
+ /* ifsfree(); */
+
+ /*
+ * This routine is slightly over-complicated for
+ * efficiency. Next we scan backwards looking for the
+ * start of arithmetic.
+ */
+ start = stackblock();
+ p = expdest - 1;
+ *p = '\0';
+ p--;
+ do {
+ int esc;
+
+ while (*p != CTLARI) {
+ p--;
+#if DEBUG
+ if (p < start) {
+ ash_msg_and_raise_error("missing CTLARI (shouldn't happen)");
+ }
+#endif
+ }
+
+ esc = esclen(start, p);
+ if (!(esc % 2)) {
+ break;
+ }
+
+ p -= esc + 1;
+ } while (1);
+
+ begoff = p - start;
+
+ removerecordregions(begoff);
+
+ flag = p[1];
+
+ expdest = p;
+
+ if (quotes)
+ rmescapes(p + 2);
+
+ len = cvtnum(dash_arith(p + 2));
+
+ if (flag != '"')
+ recordregion(begoff, begoff + len, 0);
+}
+#endif
+
+/* argstr needs it */
+static char *evalvar(char *p, int flag);
+
+/*
+ * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
+ * characters to allow for further processing. Otherwise treat
+ * $@ like $* since no splitting will be performed.
+ */
+static void
+argstr(char *p, int flag)
+{
+ static const char spclchars[] = {
+ '=',
+ ':',
+ CTLQUOTEMARK,
+ CTLENDVAR,
+ CTLESC,
+ CTLVAR,
+ CTLBACKQ,
+ CTLBACKQ | CTLQUOTE,
+#if ENABLE_ASH_MATH_SUPPORT
+ CTLENDARI,
+#endif
+ 0
+ };
+ const char *reject = spclchars;
+ int c;
+ int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */
+ int breakall = flag & EXP_WORD;
+ int inquotes;
+ size_t length;
+ int startloc;
+
+ if (!(flag & EXP_VARTILDE)) {
+ reject += 2;
+ } else if (flag & EXP_VARTILDE2) {
+ reject++;
+ }
+ inquotes = 0;
+ length = 0;
+ if (flag & EXP_TILDE) {
+ char *q;
+
+ flag &= ~EXP_TILDE;
+ tilde:
+ q = p;
+ if (*q == CTLESC && (flag & EXP_QWORD))
+ q++;
+ if (*q == '~')
+ p = exptilde(p, q, flag);
+ }
+ start:
+ startloc = expdest - (char *)stackblock();
+ for (;;) {
+ length += strcspn(p + length, reject);
+ c = p[length];
+ if (c && (!(c & 0x80)
+#if ENABLE_ASH_MATH_SUPPORT
+ || c == CTLENDARI
+#endif
+ )) {
+ /* c == '=' || c == ':' || c == CTLENDARI */
+ length++;
+ }
+ if (length > 0) {
+ int newloc;
+ expdest = stack_nputstr(p, length, expdest);
+ newloc = expdest - (char *)stackblock();
+ if (breakall && !inquotes && newloc > startloc) {
+ recordregion(startloc, newloc, 0);
+ }
+ startloc = newloc;
+ }
+ p += length + 1;
+ length = 0;
+
+ switch (c) {
+ case '\0':
+ goto breakloop;
+ case '=':
+ if (flag & EXP_VARTILDE2) {
+ p--;
+ continue;
+ }
+ flag |= EXP_VARTILDE2;
+ reject++;
+ /* fall through */
+ case ':':
+ /*
+ * sort of a hack - expand tildes in variable
+ * assignments (after the first '=' and after ':'s).
+ */
+ if (*--p == '~') {
+ goto tilde;
+ }
+ continue;
+ }
+
+ switch (c) {
+ case CTLENDVAR: /* ??? */
+ goto breakloop;
+ case CTLQUOTEMARK:
+ /* "$@" syntax adherence hack */
+ if (
+ !inquotes &&
+ !memcmp(p, dolatstr, 4) &&
+ (p[4] == CTLQUOTEMARK || (
+ p[4] == CTLENDVAR &&
+ p[5] == CTLQUOTEMARK
+ ))
+ ) {
+ p = evalvar(p + 1, flag) + 1;
+ goto start;
+ }
+ inquotes = !inquotes;
+ addquote:
+ if (quotes) {
+ p--;
+ length++;
+ startloc++;
+ }
+ break;
+ case CTLESC:
+ startloc++;
+ length++;
+ goto addquote;
+ case CTLVAR:
+ p = evalvar(p, flag);
+ goto start;
+ case CTLBACKQ:
+ c = 0;
+ case CTLBACKQ|CTLQUOTE:
+ expbackq(argbackq->n, c, quotes);
+ argbackq = argbackq->next;
+ goto start;
+#if ENABLE_ASH_MATH_SUPPORT
+ case CTLENDARI:
+ p--;
+ expari(quotes);
+ goto start;
+#endif
+ }
+ }
+ breakloop:
+ ;
+}
+
+static char *
+scanleft(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
+ int zero)
+{
+ char *loc;
+ char *loc2;
+ char c;
+
+ loc = startp;
+ loc2 = rmesc;
+ do {
+ int match;
+ const char *s = loc2;
+ c = *loc2;
+ if (zero) {
+ *loc2 = '\0';
+ s = rmesc;
+ }
+ match = pmatch(str, s);
+ *loc2 = c;
+ if (match)
+ return loc;
+ if (quotes && *loc == CTLESC)
+ loc++;
+ loc++;
+ loc2++;
+ } while (c);
+ return 0;
+}
+
+static char *
+scanright(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
+ int zero)
+{
+ int esc = 0;
+ char *loc;
+ char *loc2;
+
+ for (loc = str - 1, loc2 = rmescend; loc >= startp; loc2--) {
+ int match;
+ char c = *loc2;
+ const char *s = loc2;
+ if (zero) {
+ *loc2 = '\0';
+ s = rmesc;
+ }
+ match = pmatch(str, s);
+ *loc2 = c;
+ if (match)
+ return loc;
+ loc--;
+ if (quotes) {
+ if (--esc < 0) {
+ esc = esclen(startp, loc);
+ }
+ if (esc % 2) {
+ esc--;
+ loc--;
+ }
+ }
+ }
+ return 0;
+}
+
+static void varunset(const char *, const char *, const char *, int) ATTRIBUTE_NORETURN;
+static void
+varunset(const char *end, const char *var, const char *umsg, int varflags)
+{
+ const char *msg;
+ const char *tail;
+
+ tail = nullstr;
+ msg = "parameter not set";
+ if (umsg) {
+ if (*end == CTLENDVAR) {
+ if (varflags & VSNUL)
+ tail = " or null";
+ } else
+ msg = umsg;
+ }
+ ash_msg_and_raise_error("%.*s: %s%s", end - var - 1, var, msg, tail);
+}
+
+static const char *
+subevalvar(char *p, char *str, int strloc, int subtype, int startloc, int varflags, int quotes)
+{
+ char *startp;
+ char *loc;
+ int saveherefd = herefd;
+ struct nodelist *saveargbackq = argbackq;
+ int amount;
+ char *rmesc, *rmescend;
+ int zero;
+ char *(*scan)(char *, char *, char *, char *, int , int);
+
+ herefd = -1;
+ argstr(p, subtype != VSASSIGN && subtype != VSQUESTION ? EXP_CASE : 0);
+ STPUTC('\0', expdest);
+ herefd = saveherefd;
+ argbackq = saveargbackq;
+ startp = stackblock() + startloc;
+
+ switch (subtype) {
+ case VSASSIGN:
+ setvar(str, startp, 0);
+ amount = startp - expdest;
+ STADJUST(amount, expdest);
+ return startp;
+
+ case VSQUESTION:
+ varunset(p, str, startp, varflags);
+ /* NOTREACHED */
+ }
+
+ subtype -= VSTRIMRIGHT;
+#if DEBUG
+ if (subtype < 0 || subtype > 3)
+ abort();
+#endif
+
+ rmesc = startp;
+ rmescend = stackblock() + strloc;
+ if (quotes) {
+ rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
+ if (rmesc != startp) {
+ rmescend = expdest;
+ startp = stackblock() + startloc;
+ }
+ }
+ rmescend--;
+ str = stackblock() + strloc;
+ preglob(str, varflags & VSQUOTE, 0);
+
+ /* zero = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX */
+ zero = subtype >> 1;
+ /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
+ scan = (subtype & 1) ^ zero ? scanleft : scanright;
+
+ loc = scan(startp, rmesc, rmescend, str, quotes, zero);
+ if (loc) {
+ if (zero) {
+ memmove(startp, loc, str - loc);
+ loc = startp + (str - loc) - 1;
+ }
+ *loc = '\0';
+ amount = loc - expdest;
+ STADJUST(amount, expdest);
+ }
+ return loc;
+}
+
+/*
+ * Add the value of a specialized variable to the stack string.
+ */
+static ssize_t
+varvalue(char *name, int varflags, int flags)
+{
+ int num;
+ char *p;
+ int i;
+ int sep = 0;
+ int sepq = 0;
+ ssize_t len = 0;
+ char **ap;
+ int syntax;
+ int quoted = varflags & VSQUOTE;
+ int subtype = varflags & VSTYPE;
+ int quotes = flags & (EXP_FULL | EXP_CASE);
+
+ if (quoted && (flags & EXP_FULL))
+ sep = 1 << CHAR_BIT;
+
+ syntax = quoted ? DQSYNTAX : BASESYNTAX;
+ switch (*name) {
+ case '$':
+ num = rootpid;
+ goto numvar;
+ case '?':
+ num = exitstatus;
+ goto numvar;
+ case '#':
+ num = shellparam.nparam;
+ goto numvar;
+ case '!':
+ num = backgndpid;
+ if (num == 0)
+ return -1;
+ numvar:
+ len = cvtnum(num);
+ break;
+ case '-':
+ p = makestrspace(NOPTS, expdest);
+ for (i = NOPTS - 1; i >= 0; i--) {
+ if (optlist[i]) {
+ USTPUTC(optletters(i), p);
+ len++;
+ }
+ }
+ expdest = p;
+ break;
+ case '@':
+ if (sep)
+ goto param;
+ /* fall through */
+ case '*':
+ sep = ifsset() ? signed_char2int(ifsval()[0]) : ' ';
+ if (quotes && (SIT(sep, syntax) == CCTL || SIT(sep, syntax) == CBACK))
+ sepq = 1;
+ param:
+ ap = shellparam.p;
+ if (!ap)
+ return -1;
+ while ((p = *ap++)) {
+ size_t partlen;
+
+ partlen = strlen(p);
+ len += partlen;
+
+ if (!(subtype == VSPLUS || subtype == VSLENGTH))
+ memtodest(p, partlen, syntax, quotes);
+
+ if (*ap && sep) {
+ char *q;
+
+ len++;
+ if (subtype == VSPLUS || subtype == VSLENGTH) {
+ continue;
+ }
+ q = expdest;
+ if (sepq)
+ STPUTC(CTLESC, q);
+ STPUTC(sep, q);
+ expdest = q;
+ }
+ }
+ return len;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ num = atoi(name);
+ if (num < 0 || num > shellparam.nparam)
+ return -1;
+ p = num ? shellparam.p[num - 1] : arg0;
+ goto value;
+ default:
+ p = lookupvar(name);
+ value:
+ if (!p)
+ return -1;
+
+ len = strlen(p);
+ if (!(subtype == VSPLUS || subtype == VSLENGTH))
+ memtodest(p, len, syntax, quotes);
+ return len;
+ }
+
+ if (subtype == VSPLUS || subtype == VSLENGTH)
+ STADJUST(-len, expdest);
+ return len;
+}
+
+/*
+ * Expand a variable, and return a pointer to the next character in the
+ * input string.
+ */
+static char *
+evalvar(char *p, int flag)
+{
+ int subtype;
+ int varflags;
+ char *var;
+ int patloc;
+ int c;
+ int startloc;
+ ssize_t varlen;
+ int easy;
+ int quotes;
+ int quoted;
+
+ quotes = flag & (EXP_FULL | EXP_CASE);
+ varflags = *p++;
+ subtype = varflags & VSTYPE;
+ quoted = varflags & VSQUOTE;
+ var = p;
+ easy = (!quoted || (*var == '@' && shellparam.nparam));
+ startloc = expdest - (char *)stackblock();
+ p = strchr(p, '=') + 1;
+
+ again:
+ varlen = varvalue(var, varflags, flag);
+ if (varflags & VSNUL)
+ varlen--;
+
+ if (subtype == VSPLUS) {
+ varlen = -1 - varlen;
+ goto vsplus;
+ }
+
+ if (subtype == VSMINUS) {
+ vsplus:
+ if (varlen < 0) {
+ argstr(
+ p, flag | EXP_TILDE |
+ (quoted ? EXP_QWORD : EXP_WORD)
+ );
+ goto end;
+ }
+ if (easy)
+ goto record;
+ goto end;
+ }
+
+ if (subtype == VSASSIGN || subtype == VSQUESTION) {
+ if (varlen < 0) {
+ if (subevalvar(p, var, 0, subtype, startloc, varflags, 0)) {
+ varflags &= ~VSNUL;
+ /*
+ * Remove any recorded regions beyond
+ * start of variable
+ */
+ removerecordregions(startloc);
+ goto again;
+ }
+ goto end;
+ }
+ if (easy)
+ goto record;
+ goto end;
+ }
+
+ if (varlen < 0 && uflag)
+ varunset(p, var, 0, 0);
+
+ if (subtype == VSLENGTH) {
+ cvtnum(varlen > 0 ? varlen : 0);
+ goto record;
+ }
+
+ if (subtype == VSNORMAL) {
+ if (!easy)
+ goto end;
+ record:
+ recordregion(startloc, expdest - (char *)stackblock(), quoted);
+ goto end;
+ }
+
+#if DEBUG
+ switch (subtype) {
+ case VSTRIMLEFT:
+ case VSTRIMLEFTMAX:
+ case VSTRIMRIGHT:
+ case VSTRIMRIGHTMAX:
+ break;
+ default:
+ abort();
+ }
+#endif
+
+ if (varlen >= 0) {
+ /*
+ * Terminate the string and start recording the pattern
+ * right after it
+ */
+ STPUTC('\0', expdest);
+ patloc = expdest - (char *)stackblock();
+ if (subevalvar(p, NULL, patloc, subtype,
+ startloc, varflags, quotes) == 0) {
+ int amount = expdest - (
+ (char *)stackblock() + patloc - 1
+ );
+ STADJUST(-amount, expdest);
+ }
+ /* Remove any recorded regions beyond start of variable */
+ removerecordregions(startloc);
+ goto record;
+ }
+
+ end:
+ if (subtype != VSNORMAL) { /* skip to end of alternative */
+ int nesting = 1;
+ for (;;) {
+ c = *p++;
+ if (c == CTLESC)
+ p++;
+ else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
+ if (varlen >= 0)
+ argbackq = argbackq->next;
+ } else if (c == CTLVAR) {
+ if ((*p++ & VSTYPE) != VSNORMAL)
+ nesting++;
+ } else if (c == CTLENDVAR) {
+ if (--nesting == 0)
+ break;
+ }
+ }
+ }
+ return p;
+}
+
+/*
+ * Break the argument string into pieces based upon IFS and add the
+ * strings to the argument list. The regions of the string to be
+ * searched for IFS characters have been stored by recordregion.
+ */
+static void
+ifsbreakup(char *string, struct arglist *arglist)
+{
+ struct ifsregion *ifsp;
+ struct strlist *sp;
+ char *start;
+ char *p;
+ char *q;
+ const char *ifs, *realifs;
+ int ifsspc;
+ int nulonly;
+
+ start = string;
+ if (ifslastp != NULL) {
+ ifsspc = 0;
+ nulonly = 0;
+ realifs = ifsset() ? ifsval() : defifs;
+ ifsp = &ifsfirst;
+ do {
+ p = string + ifsp->begoff;
+ nulonly = ifsp->nulonly;
+ ifs = nulonly ? nullstr : realifs;
+ ifsspc = 0;
+ while (p < string + ifsp->endoff) {
+ q = p;
+ if (*p == CTLESC)
+ p++;
+ if (!strchr(ifs, *p)) {
+ p++;
+ continue;
+ }
+ if (!nulonly)
+ ifsspc = (strchr(defifs, *p) != NULL);
+ /* Ignore IFS whitespace at start */
+ if (q == start && ifsspc) {
+ p++;
+ start = p;
+ continue;
+ }
+ *q = '\0';
+ sp = stalloc(sizeof(*sp));
+ sp->text = start;
+ *arglist->lastp = sp;
+ arglist->lastp = &sp->next;
+ p++;
+ if (!nulonly) {
+ for (;;) {
+ if (p >= string + ifsp->endoff) {
+ break;
+ }
+ q = p;
+ if (*p == CTLESC)
+ p++;
+ if (strchr(ifs, *p) == NULL ) {
+ p = q;
+ break;
+ } else if (strchr(defifs, *p) == NULL) {
+ if (ifsspc) {
+ p++;
+ ifsspc = 0;
+ } else {
+ p = q;
+ break;
+ }
+ } else
+ p++;
+ }
+ }
+ start = p;
+ } /* while */
+ ifsp = ifsp->next;
+ } while (ifsp != NULL);
+ if (nulonly)
+ goto add;
+ }
+
+ if (!*start)
+ return;
+
+ add:
+ sp = stalloc(sizeof(*sp));
+ sp->text = start;
+ *arglist->lastp = sp;
+ arglist->lastp = &sp->next;
+}
+
+static void
+ifsfree(void)
+{
+ struct ifsregion *p;
+
+ INT_OFF;
+ p = ifsfirst.next;
+ do {
+ struct ifsregion *ifsp;
+ ifsp = p->next;
+ free(p);
+ p = ifsp;
+ } while (p);
+ ifslastp = NULL;
+ ifsfirst.next = NULL;
+ INT_ON;
+}
+
+/*
+ * Add a file name to the list.
+ */
+static void
+addfname(const char *name)
+{
+ struct strlist *sp;
+
+ sp = stalloc(sizeof(*sp));
+ sp->text = ststrdup(name);
+ *exparg.lastp = sp;
+ exparg.lastp = &sp->next;
+}
+
+static char *expdir;
+
+/*
+ * Do metacharacter (i.e. *, ?, [...]) expansion.
+ */
+static void
+expmeta(char *enddir, char *name)
+{
+ char *p;
+ const char *cp;
+ char *start;
+ char *endname;
+ int metaflag;
+ struct stat statb;
+ DIR *dirp;
+ struct dirent *dp;
+ int atend;
+ int matchdot;
+
+ metaflag = 0;
+ start = name;
+ for (p = name; *p; p++) {
+ if (*p == '*' || *p == '?')
+ metaflag = 1;
+ else if (*p == '[') {
+ char *q = p + 1;
+ if (*q == '!')
+ q++;
+ for (;;) {
+ if (*q == '\\')
+ q++;
+ if (*q == '/' || *q == '\0')
+ break;
+ if (*++q == ']') {
+ metaflag = 1;
+ break;
+ }
+ }
+ } else if (*p == '\\')
+ p++;
+ else if (*p == '/') {
+ if (metaflag)
+ goto out;
+ start = p + 1;
+ }
+ }
+ out:
+ if (metaflag == 0) { /* we've reached the end of the file name */
+ if (enddir != expdir)
+ metaflag++;
+ p = name;
+ do {
+ if (*p == '\\')
+ p++;
+ *enddir++ = *p;
+ } while (*p++);
+ if (metaflag == 0 || lstat(expdir, &statb) >= 0)
+ addfname(expdir);
+ return;
+ }
+ endname = p;
+ if (name < start) {
+ p = name;
+ do {
+ if (*p == '\\')
+ p++;
+ *enddir++ = *p++;
+ } while (p < start);
+ }
+ if (enddir == expdir) {
+ cp = ".";
+ } else if (enddir == expdir + 1 && *expdir == '/') {
+ cp = "/";
+ } else {
+ cp = expdir;
+ enddir[-1] = '\0';
+ }
+ dirp = opendir(cp);
+ if (dirp == NULL)
+ return;
+ if (enddir != expdir)
+ enddir[-1] = '/';
+ if (*endname == 0) {
+ atend = 1;
+ } else {
+ atend = 0;
+ *endname++ = '\0';
+ }
+ matchdot = 0;
+ p = start;
+ if (*p == '\\')
+ p++;
+ if (*p == '.')
+ matchdot++;
+ while (! intpending && (dp = readdir(dirp)) != NULL) {
+ if (dp->d_name[0] == '.' && ! matchdot)
+ continue;
+ if (pmatch(start, dp->d_name)) {
+ if (atend) {
+ strcpy(enddir, dp->d_name);
+ addfname(expdir);
+ } else {
+ for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';)
+ continue;
+ p[-1] = '/';
+ expmeta(p, endname);
+ }
+ }
+ }
+ closedir(dirp);
+ if (! atend)
+ endname[-1] = '/';
+}
+
+static struct strlist *
+msort(struct strlist *list, int len)
+{
+ struct strlist *p, *q = NULL;
+ struct strlist **lpp;
+ int half;
+ int n;
+
+ if (len <= 1)
+ return list;
+ half = len >> 1;
+ p = list;
+ for (n = half; --n >= 0; ) {
+ q = p;
+ p = p->next;
+ }
+ q->next = NULL; /* terminate first half of list */
+ q = msort(list, half); /* sort first half of list */
+ p = msort(p, len - half); /* sort second half */
+ lpp = &list;
+ for (;;) {
+#if ENABLE_LOCALE_SUPPORT
+ if (strcoll(p->text, q->text) < 0)
+#else
+ if (strcmp(p->text, q->text) < 0)
+#endif
+ {
+ *lpp = p;
+ lpp = &p->next;
+ p = *lpp;
+ if (p == NULL) {
+ *lpp = q;
+ break;
+ }
+ } else {
+ *lpp = q;
+ lpp = &q->next;
+ q = *lpp;
+ if (q == NULL) {
+ *lpp = p;
+ break;
+ }
+ }
+ }
+ return list;
+}
+
+/*
+ * Sort the results of file name expansion. It calculates the number of
+ * strings to sort and then calls msort (short for merge sort) to do the
+ * work.
+ */
+static struct strlist *
+expsort(struct strlist *str)
+{
+ int len;
+ struct strlist *sp;
+
+ len = 0;
+ for (sp = str; sp; sp = sp->next)
+ len++;
+ return msort(str, len);
+}
+
+static void
+expandmeta(struct strlist *str, int flag)
+{
+ static const char metachars[] = {
+ '*', '?', '[', 0
+ };
+ /* TODO - EXP_REDIR */
+
+ while (str) {
+ struct strlist **savelastp;
+ struct strlist *sp;
+ char *p;
+
+ if (fflag)
+ goto nometa;
+ if (!strpbrk(str->text, metachars))
+ goto nometa;
+ savelastp = exparg.lastp;
+
+ INT_OFF;
+ p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
+ {
+ int i = strlen(str->text);
+ expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
+ }
+
+ expmeta(expdir, p);
+ free(expdir);
+ if (p != str->text)
+ free(p);
+ INT_ON;
+ if (exparg.lastp == savelastp) {
+ /*
+ * no matches
+ */
+ nometa:
+ *exparg.lastp = str;
+ rmescapes(str->text);
+ exparg.lastp = &str->next;
+ } else {
+ *exparg.lastp = NULL;
+ *savelastp = sp = expsort(*savelastp);
+ while (sp->next != NULL)
+ sp = sp->next;
+ exparg.lastp = &sp->next;
+ }
+ str = str->next;
+ }
+}
+
+/*
+ * Perform variable substitution and command substitution on an argument,
+ * placing the resulting list of arguments in arglist. If EXP_FULL is true,
+ * perform splitting and file name expansion. When arglist is NULL, perform
+ * here document expansion.
+ */
+static void
+expandarg(union node *arg, struct arglist *arglist, int flag)
+{
+ struct strlist *sp;
+ char *p;
+
+ argbackq = arg->narg.backquote;
+ STARTSTACKSTR(expdest);
+ ifsfirst.next = NULL;
+ ifslastp = NULL;
+ argstr(arg->narg.text, flag);
+ p = _STPUTC('\0', expdest);
+ expdest = p - 1;
+ if (arglist == NULL) {
+ return; /* here document expanded */
+ }
+ p = grabstackstr(p);
+ exparg.lastp = &exparg.list;
+ /*
+ * TODO - EXP_REDIR
+ */
+ if (flag & EXP_FULL) {
+ ifsbreakup(p, &exparg);
+ *exparg.lastp = NULL;
+ exparg.lastp = &exparg.list;
+ expandmeta(exparg.list, flag);
+ } else {
+ if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
+ rmescapes(p);
+ sp = stalloc(sizeof(*sp));
+ sp->text = p;
+ *exparg.lastp = sp;
+ exparg.lastp = &sp->next;
+ }
+ if (ifsfirst.next)
+ ifsfree();
+ *exparg.lastp = NULL;
+ if (exparg.list) {
+ *arglist->lastp = exparg.list;
+ arglist->lastp = exparg.lastp;
+ }
+}
+
+/*
+ * Expand shell variables and backquotes inside a here document.
+ */
+static void
+expandhere(union node *arg, int fd)
+{
+ herefd = fd;
+ expandarg(arg, (struct arglist *)NULL, 0);
+ full_write(fd, stackblock(), expdest - (char *)stackblock());
+}
+
+/*
+ * Returns true if the pattern matches the string.
+ */
+static int
+patmatch(char *pattern, const char *string)
+{
+ return pmatch(preglob(pattern, 0, 0), string);
+}
+
+/*
+ * See if a pattern matches in a case statement.
+ */
+static int
+casematch(union node *pattern, char *val)
+{
+ struct stackmark smark;
+ int result;
+
+ setstackmark(&smark);
+ argbackq = pattern->narg.backquote;
+ STARTSTACKSTR(expdest);
+ ifslastp = NULL;
+ argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
+ STACKSTRNUL(expdest);
+ result = patmatch(stackblock(), val);
+ popstackmark(&smark);
+ return result;
+}
+
+
+/* ============ find_command */
+
+#if ENABLE_FEATURE_SH_STANDALONE_SHELL
+static int
+is_safe_applet(char *name)
+{
+ /* It isn't a bug to have non-existent applet here... */
+ /* ...just a waste of space... */
+ static const char safe_applets[][8] = {
+ "["
+ USE_AWK (, "awk" )
+ USE_CAT (, "cat" )
+ USE_CHMOD (, "chmod" )
+ USE_CHOWN (, "chown" )
+ USE_CP (, "cp" )
+ USE_CUT (, "cut" )
+ USE_DD (, "dd" )
+ USE_ECHO (, "echo" )
+ USE_FIND (, "find" )
+ USE_HEXDUMP(, "hexdump")
+ USE_LN (, "ln" )
+ USE_LS (, "ls" )
+ USE_MKDIR (, "mkdir" )
+ USE_RM (, "rm" )
+ USE_SORT (, "sort" )
+ USE_TEST (, "test" )
+ USE_TOUCH (, "touch" )
+ USE_XARGS (, "xargs" )
+ };
+ int n = sizeof(safe_applets) / sizeof(safe_applets[0]);
+ int i;
+ for (i = 0; i < n; i++)
+ if (strcmp(safe_applets[i], name) == 0)
+ return 1;
+
+ return 0;
+}
+#endif
+
+struct builtincmd {
+ const char *name;
+ int (*builtin)(int, char **);
+ /* unsigned flags; */
+};
+#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1)
+#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
+#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
+
+struct cmdentry {
+ int cmdtype;
+ union param {
+ int index;
+ const struct builtincmd *cmd;
+ struct funcnode *func;
+ } u;
+};
+/* values of cmdtype */
+#define CMDUNKNOWN -1 /* no entry in table for command */
+#define CMDNORMAL 0 /* command is an executable program */
+#define CMDFUNCTION 1 /* command is a shell function */
+#define CMDBUILTIN 2 /* command is a shell builtin */
+
+/* action to find_command() */
+#define DO_ERR 0x01 /* prints errors */
+#define DO_ABS 0x02 /* checks absolute paths */
+#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
+#define DO_ALTPATH 0x08 /* using alternate path */
+#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */
+
+static void find_command(char *, struct cmdentry *, int, const char *);
+
+
+/* ============ Hashing commands */
+
+/*
+ * When commands are first encountered, they are entered in a hash table.
+ * This ensures that a full path search will not have to be done for them
+ * on each invocation.
+ *
+ * We should investigate converting to a linear search, even though that
+ * would make the command name "hash" a misnomer.
+ */
+
+#define CMDTABLESIZE 31 /* should be prime */
+#define ARB 1 /* actual size determined at run time */
+
+struct tblentry {
+ struct tblentry *next; /* next entry in hash chain */
+ union param param; /* definition of builtin function */
+ short cmdtype; /* index identifying command */
+ char rehash; /* if set, cd done since entry created */
+ char cmdname[ARB]; /* name of command */
+};
+
+static struct tblentry *cmdtable[CMDTABLESIZE];
+static int builtinloc = -1; /* index in path of %builtin, or -1 */
+
+static void
+tryexec(char *cmd, char **argv, char **envp)
+{
+ int repeated = 0;
+
+#if ENABLE_FEATURE_SH_STANDALONE_SHELL
+ if (strchr(cmd, '/') == NULL) {
+ struct BB_applet *a;
+ char **c;
+
+ a = find_applet_by_name(cmd);
+ if (a) {
+ if (is_safe_applet(cmd)) {
+ c = argv;
+ while (*c)
+ c++;
+ applet_name = cmd;
+ exit(a->main(c - argv, argv));
+ }
+ /* re-exec ourselves with the new arguments */
+ execve(CONFIG_BUSYBOX_EXEC_PATH, argv, envp);
+ /* If they called chroot or otherwise made the binary no longer
+ * executable, fall through */
+ }
+ }
+#endif
+
+ repeat:
+#ifdef SYSV
+ do {
+ execve(cmd, argv, envp);
+ } while (errno == EINTR);
+#else
+ execve(cmd, argv, envp);
+#endif
+ if (repeated++) {
+ free(argv);
+ } else if (errno == ENOEXEC) {
+ char **ap;
+ char **new;
+
+ for (ap = argv; *ap; ap++)
+ ;
+ ap = new = ckmalloc((ap - argv + 2) * sizeof(char *));
+ ap[1] = cmd;
+ *ap = cmd = (char *)DEFAULT_SHELL;
+ ap += 2;
+ argv++;
+ while ((*ap++ = *argv++))
+ ;
+ argv = new;
+ goto repeat;
+ }
+}
+
+/*
+ * Exec a program. Never returns. If you change this routine, you may
+ * have to change the find_command routine as well.
+ */
+#define environment() listvars(VEXPORT, VUNSET, 0)
+static void shellexec(char **, const char *, int) ATTRIBUTE_NORETURN;
+static void
+shellexec(char **argv, const char *path, int idx)
+{
+ char *cmdname;
+ int e;
+ char **envp;
+ int exerrno;
+
+ clearredir(1);
+ envp = environment();
+ if (strchr(argv[0], '/')
+#if ENABLE_FEATURE_SH_STANDALONE_SHELL
+ || find_applet_by_name(argv[0])
+#endif
+ ) {
+ tryexec(argv[0], argv, envp);
+ e = errno;
+ } else {
+ e = ENOENT;
+ while ((cmdname = padvance(&path, argv[0])) != NULL) {
+ if (--idx < 0 && pathopt == NULL) {
+ tryexec(cmdname, argv, envp);
+ if (errno != ENOENT && errno != ENOTDIR)
+ e = errno;
+ }
+ stunalloc(cmdname);
+ }
+ }
+
+ /* Map to POSIX errors */
+ switch (e) {
+ case EACCES:
+ exerrno = 126;
+ break;
+ case ENOENT:
+ exerrno = 127;
+ break;
+ default:
+ exerrno = 2;
+ break;
+ }
+ exitstatus = exerrno;
+ TRACE(("shellexec failed for %s, errno %d, suppressint %d\n",
+ argv[0], e, suppressint ));
+ ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found"));
+ /* NOTREACHED */
+}
+
+static void
+printentry(struct tblentry *cmdp)
+{
+ int idx;
+ const char *path;
+ char *name;
+
+ idx = cmdp->param.index;
+ path = pathval();
+ do {
+ name = padvance(&path, cmdp->cmdname);
+ stunalloc(name);
+ } while (--idx >= 0);
+ out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
+}
+
+/*
+ * Clear out command entries. The argument specifies the first entry in
+ * PATH which has changed.
+ */
+static void
+clearcmdentry(int firstchange)
+{
+ struct tblentry **tblp;
+ struct tblentry **pp;
+ struct tblentry *cmdp;
+
+ INT_OFF;
+ for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
+ pp = tblp;
+ while ((cmdp = *pp) != NULL) {
+ if ((cmdp->cmdtype == CMDNORMAL &&
+ cmdp->param.index >= firstchange)
+ || (cmdp->cmdtype == CMDBUILTIN &&
+ builtinloc >= firstchange)
+ ) {
+ *pp = cmdp->next;
+ free(cmdp);
+ } else {
+ pp = &cmdp->next;
+ }
+ }
+ }
+ INT_ON;
+}
+
+/*
+ * Locate a command in the command hash table. If "add" is nonzero,
+ * add the command to the table if it is not already present. The
+ * variable "lastcmdentry" is set to point to the address of the link
+ * pointing to the entry, so that delete_cmd_entry can delete the
+ * entry.
+ *
+ * Interrupts must be off if called with add != 0.
+ */
+static struct tblentry **lastcmdentry;
+
+static struct tblentry *
+cmdlookup(const char *name, int add)
+{
+ unsigned int hashval;
+ const char *p;
+ struct tblentry *cmdp;
+ struct tblentry **pp;
+
+ p = name;
+ hashval = (unsigned char)*p << 4;
+ while (*p)
+ hashval += (unsigned char)*p++;
+ hashval &= 0x7FFF;
+ pp = &cmdtable[hashval % CMDTABLESIZE];
+ for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
+ if (strcmp(cmdp->cmdname, name) == 0)
+ break;
+ pp = &cmdp->next;
+ }
+ if (add && cmdp == NULL) {
+ cmdp = *pp = ckmalloc(sizeof(struct tblentry) - ARB
+ + strlen(name) + 1);
+ cmdp->next = NULL;
+ cmdp->cmdtype = CMDUNKNOWN;
+ strcpy(cmdp->cmdname, name);
+ }
+ lastcmdentry = pp;
+ return cmdp;
+}
+
+/*
+ * Delete the command entry returned on the last lookup.
+ */
+static void
+delete_cmd_entry(void)
+{
+ struct tblentry *cmdp;
+
+ INT_OFF;
+ cmdp = *lastcmdentry;
+ *lastcmdentry = cmdp->next;
+ if (cmdp->cmdtype == CMDFUNCTION)
+ freefunc(cmdp->param.func);
+ free(cmdp);
+ INT_ON;
+}
+
+/*
+ * Add a new command entry, replacing any existing command entry for
+ * the same name - except special builtins.
+ */
+static void
+addcmdentry(char *name, struct cmdentry *entry)
+{
+ struct tblentry *cmdp;
+
+ cmdp = cmdlookup(name, 1);
+ if (cmdp->cmdtype == CMDFUNCTION) {
+ freefunc(cmdp->param.func);
+ }
+ cmdp->cmdtype = entry->cmdtype;
+ cmdp->param = entry->u;
+ cmdp->rehash = 0;
+}
+
+static int
+hashcmd(int argc, char **argv)
+{
+ struct tblentry **pp;
+ struct tblentry *cmdp;
+ int c;
+ struct cmdentry entry;
+ char *name;
+
+ while ((c = nextopt("r")) != '\0') {
+ clearcmdentry(0);
+ return 0;
+ }
+ if (*argptr == NULL) {
+ for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
+ for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
+ if (cmdp->cmdtype == CMDNORMAL)
+ printentry(cmdp);
+ }
+ }
+ return 0;
+ }
+ c = 0;
+ while ((name = *argptr) != NULL) {
+ cmdp = cmdlookup(name, 0);
+ if (cmdp != NULL
+ && (cmdp->cmdtype == CMDNORMAL
+ || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)))
+ delete_cmd_entry();
+ find_command(name, &entry, DO_ERR, pathval());
+ if (entry.cmdtype == CMDUNKNOWN)
+ c = 1;
+ argptr++;
+ }
+ return c;
+}
+
+/*
+ * Called when a cd is done. Marks all commands so the next time they
+ * are executed they will be rehashed.
+ */
+static void
+hashcd(void)
+{
+ struct tblentry **pp;
+ struct tblentry *cmdp;
+
+ for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
+ for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
+ if (cmdp->cmdtype == CMDNORMAL || (
+ cmdp->cmdtype == CMDBUILTIN &&
+ !(IS_BUILTIN_REGULAR(cmdp->param.cmd)) &&
+ builtinloc > 0
+ ))
+ cmdp->rehash = 1;
+ }
+ }
+}
+
+/*
+ * Fix command hash table when PATH changed.
+ * Called before PATH is changed. The argument is the new value of PATH;
+ * pathval() still returns the old value at this point.
+ * Called with interrupts off.
+ */
+static void
+changepath(const char *newval)
+{
+ const char *old, *new;
+ int idx;
+ int firstchange;
+ int idx_bltin;
+
+ old = pathval();
+ new = newval;
+ firstchange = 9999; /* assume no change */
+ idx = 0;
+ idx_bltin = -1;
+ for (;;) {
+ if (*old != *new) {
+ firstchange = idx;
+ if ((*old == '\0' && *new == ':')
+ || (*old == ':' && *new == '\0'))
+ firstchange++;
+ old = new; /* ignore subsequent differences */
+ }
+ if (*new == '\0')
+ break;
+ if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
+ idx_bltin = idx;
+ if (*new == ':') {
+ idx++;
+ }
+ new++, old++;
+ }
+ if (builtinloc < 0 && idx_bltin >= 0)
+ builtinloc = idx_bltin; /* zap builtins */
+ if (builtinloc >= 0 && idx_bltin < 0)
+ firstchange = 0;
+ clearcmdentry(firstchange);
+ builtinloc = idx_bltin;
+}
+
+#define TEOF 0
+#define TNL 1
+#define TREDIR 2
+#define TWORD 3
+#define TSEMI 4
+#define TBACKGND 5
+#define TAND 6
+#define TOR 7
+#define TPIPE 8
+#define TLP 9
+#define TRP 10
+#define TENDCASE 11
+#define TENDBQUOTE 12
+#define TNOT 13
+#define TCASE 14
+#define TDO 15
+#define TDONE 16
+#define TELIF 17
+#define TELSE 18
+#define TESAC 19
+#define TFI 20
+#define TFOR 21
+#define TIF 22
+#define TIN 23
+#define TTHEN 24
+#define TUNTIL 25
+#define TWHILE 26
+#define TBEGIN 27
+#define TEND 28
+
+/* first char is indicating which tokens mark the end of a list */
+static const char *const tokname_array[] = {
+ "\1end of file",
+ "\0newline",
+ "\0redirection",
+ "\0word",
+ "\0;",
+ "\0&",
+ "\0&&",
+ "\0||",
+ "\0|",
+ "\0(",
+ "\1)",
+ "\1;;",
+ "\1`",
+#define KWDOFFSET 13
+ /* the following are keywords */
+ "\0!",
+ "\0case",
+ "\1do",
+ "\1done",
+ "\1elif",
+ "\1else",
+ "\1esac",
+ "\1fi",
+ "\0for",
+ "\0if",
+ "\0in",
+ "\1then",
+ "\0until",
+ "\0while",
+ "\0{",
+ "\1}",
+};
+
+static const char *
+tokname(int tok)
+{
+ static char buf[16];
+
+ if (tok >= TSEMI)
+ buf[0] = '"';
+ sprintf(buf + (tok >= TSEMI), "%s%c",
+ tokname_array[tok] + 1, (tok >= TSEMI ? '"' : 0));
+ return buf;
+}
+
+/* Wrapper around strcmp for qsort/bsearch/... */
+static int
+pstrcmp(const void *a, const void *b)
+{
+ return strcmp((const char *) a, (*(const char *const *) b) + 1);
+}
+
+static const char *const *
+findkwd(const char *s)
+{
+ return bsearch(s, tokname_array + KWDOFFSET,
+ (sizeof(tokname_array) / sizeof(const char *)) - KWDOFFSET,
+ sizeof(const char *), pstrcmp);
+}
+
+/*
+ * Locate and print what a word is...
+ */
+#if ENABLE_ASH_CMDCMD
+static int
+describe_command(char *command, int describe_command_verbose)
+#else
+#define describe_command_verbose 1
+static int
+describe_command(char *command)
+#endif
+{
+ struct cmdentry entry;
+ struct tblentry *cmdp;
+#if ENABLE_ASH_ALIAS
+ const struct alias *ap;
+#endif
+ const char *path = pathval();
+
+ if (describe_command_verbose) {
+ out1str(command);
+ }
+
+ /* First look at the keywords */
+ if (findkwd(command)) {
+ out1str(describe_command_verbose ? " is a shell keyword" : command);
+ goto out;
+ }
+
+#if ENABLE_ASH_ALIAS
+ /* Then look at the aliases */
+ ap = lookupalias(command, 0);
+ if (ap != NULL) {
+ if (describe_command_verbose) {
+ out1fmt(" is an alias for %s", ap->val);
+ } else {
+ out1str("alias ");
+ printalias(ap);
+ return 0;
+ }
+ goto out;
+ }
+#endif
+ /* Then check if it is a tracked alias */
+ cmdp = cmdlookup(command, 0);
+ if (cmdp != NULL) {
+ entry.cmdtype = cmdp->cmdtype;
+ entry.u = cmdp->param;
+ } else {
+ /* Finally use brute force */
+ find_command(command, &entry, DO_ABS, path);
+ }
+
+ switch (entry.cmdtype) {
+ case CMDNORMAL: {
+ int j = entry.u.index;
+ char *p;
+ if (j == -1) {
+ p = command;
+ } else {
+ do {
+ p = padvance(&path, command);
+ stunalloc(p);
+ } while (--j >= 0);
+ }
+ if (describe_command_verbose) {
+ out1fmt(" is%s %s",
+ (cmdp ? " a tracked alias for" : nullstr), p
+ );
+ } else {
+ out1str(p);
+ }
+ break;
+ }
+
+ case CMDFUNCTION:
+ if (describe_command_verbose) {
+ out1str(" is a shell function");
+ } else {
+ out1str(command);
+ }
+ break;
+
+ case CMDBUILTIN:
+ if (describe_command_verbose) {
+ out1fmt(" is a %sshell builtin",
+ IS_BUILTIN_SPECIAL(entry.u.cmd) ?
+ "special " : nullstr
+ );
+ } else {
+ out1str(command);
+ }
+ break;
+
+ default:
+ if (describe_command_verbose) {
+ out1str(": not found\n");
+ }
+ return 127;
+ }
+ out:
+ outstr("\n", stdout);
+ return 0;
+}
+
+static int
+typecmd(int argc, char **argv)
+{
+ int i;
+ int err = 0;
+
+ for (i = 1; i < argc; i++) {
+#if ENABLE_ASH_CMDCMD
+ err |= describe_command(argv[i], 1);
+#else
+ err |= describe_command(argv[i]);
+#endif
+ }
+ return err;
+}
+
+#if ENABLE_ASH_CMDCMD
+static int
+commandcmd(int argc, char **argv)
+{
+ int c;
+ enum {
+ VERIFY_BRIEF = 1,
+ VERIFY_VERBOSE = 2,
+ } verify = 0;
+
+ while ((c = nextopt("pvV")) != '\0')
+ if (c == 'V')
+ verify |= VERIFY_VERBOSE;
+ else if (c == 'v')
+ verify |= VERIFY_BRIEF;
+#if DEBUG
+ else if (c != 'p')
+ abort();
+#endif
+ if (verify)
+ return describe_command(*argptr, verify - VERIFY_BRIEF);
+
+ return 0;
+}
+#endif
+
+
+/* ============ eval.c */
+
+static int funcblocksize; /* size of structures in function */
+static int funcstringsize; /* size of strings in node */
+static void *funcblock; /* block to allocate function from */
+static char *funcstring; /* block to allocate strings from */
+
+/* flags in argument to evaltree */
+#define EV_EXIT 01 /* exit after evaluating tree */
+#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
+#define EV_BACKCMD 04 /* command executing within back quotes */
+
+static const short nodesize[26] = {
+ SHELL_ALIGN(sizeof(struct ncmd)),
+ SHELL_ALIGN(sizeof(struct npipe)),
+ SHELL_ALIGN(sizeof(struct nredir)),
+ SHELL_ALIGN(sizeof(struct nredir)),
+ SHELL_ALIGN(sizeof(struct nredir)),
+ SHELL_ALIGN(sizeof(struct nbinary)),
+ SHELL_ALIGN(sizeof(struct nbinary)),
+ SHELL_ALIGN(sizeof(struct nbinary)),
+ SHELL_ALIGN(sizeof(struct nif)),
+ SHELL_ALIGN(sizeof(struct nbinary)),
+ SHELL_ALIGN(sizeof(struct nbinary)),
+ SHELL_ALIGN(sizeof(struct nfor)),
+ SHELL_ALIGN(sizeof(struct ncase)),
+ SHELL_ALIGN(sizeof(struct nclist)),
+ SHELL_ALIGN(sizeof(struct narg)),
+ SHELL_ALIGN(sizeof(struct narg)),
+ SHELL_ALIGN(sizeof(struct nfile)),
+ SHELL_ALIGN(sizeof(struct nfile)),
+ SHELL_ALIGN(sizeof(struct nfile)),
+ SHELL_ALIGN(sizeof(struct nfile)),
+ SHELL_ALIGN(sizeof(struct nfile)),
+ SHELL_ALIGN(sizeof(struct ndup)),
+ SHELL_ALIGN(sizeof(struct ndup)),
+ SHELL_ALIGN(sizeof(struct nhere)),
+ SHELL_ALIGN(sizeof(struct nhere)),
+ SHELL_ALIGN(sizeof(struct nnot)),
+};
+
+static void calcsize(union node *n);
+
+static void
+sizenodelist(struct nodelist *lp)
+{
+ while (lp) {
+ funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
+ calcsize(lp->n);
+ lp = lp->next;
+ }
+}
+
+static void
+calcsize(union node *n)
+{
+ if (n == NULL)
+ return;
+ funcblocksize += nodesize[n->type];
+ switch (n->type) {
+ case NCMD:
+ calcsize(n->ncmd.redirect);
+ calcsize(n->ncmd.args);
+ calcsize(n->ncmd.assign);
+ break;
+ case NPIPE:
+ sizenodelist(n->npipe.cmdlist);
+ break;
+ case NREDIR:
+ case NBACKGND:
+ case NSUBSHELL:
+ calcsize(n->nredir.redirect);
+ calcsize(n->nredir.n);
+ break;
+ case NAND:
+ case NOR:
+ case NSEMI:
+ case NWHILE:
+ case NUNTIL:
+ calcsize(n->nbinary.ch2);
+ calcsize(n->nbinary.ch1);
+ break;
+ case NIF:
+ calcsize(n->nif.elsepart);
+ calcsize(n->nif.ifpart);
+ calcsize(n->nif.test);
+ break;
+ case NFOR:
+ funcstringsize += strlen(n->nfor.var) + 1;
+ calcsize(n->nfor.body);
+ calcsize(n->nfor.args);
+ break;
+ case NCASE:
+ calcsize(n->ncase.cases);
+ calcsize(n->ncase.expr);
+ break;
+ case NCLIST:
+ calcsize(n->nclist.body);
+ calcsize(n->nclist.pattern);
+ calcsize(n->nclist.next);
+ break;
+ case NDEFUN:
+ case NARG:
+ sizenodelist(n->narg.backquote);
+ funcstringsize += strlen(n->narg.text) + 1;
+ calcsize(n->narg.next);
+ break;
+ case NTO:
+ case NCLOBBER:
+ case NFROM:
+ case NFROMTO:
+ case NAPPEND:
+ calcsize(n->nfile.fname);
+ calcsize(n->nfile.next);
+ break;
+ case NTOFD:
+ case NFROMFD:
+ calcsize(n->ndup.vname);
+ calcsize(n->ndup.next);
+ break;
+ case NHERE:
+ case NXHERE:
+ calcsize(n->nhere.doc);
+ calcsize(n->nhere.next);
+ break;
+ case NNOT:
+ calcsize(n->nnot.com);
+ break;
+ };
+}
+
+static char *
+nodeckstrdup(char *s)
+{
+ char *rtn = funcstring;
+
+ strcpy(funcstring, s);
+ funcstring += strlen(s) + 1;
+ return rtn;
+}
+
+static union node *copynode(union node *);
+
+static struct nodelist *
+copynodelist(struct nodelist *lp)
+{
+ struct nodelist *start;
+ struct nodelist **lpp;
+
+ lpp = &start;
+ while (lp) {
+ *lpp = funcblock;
+ funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
+ (*lpp)->n = copynode(lp->n);
+ lp = lp->next;
+ lpp = &(*lpp)->next;
+ }
+ *lpp = NULL;
+ return start;
+}
+
+static union node *
+copynode(union node *n)
+{
+ union node *new;
+
+ if (n == NULL)
+ return NULL;
+ new = funcblock;
+ funcblock = (char *) funcblock + nodesize[n->type];
+
+ switch (n->type) {
+ case NCMD:
+ new->ncmd.redirect = copynode(n->ncmd.redirect);
+ new->ncmd.args = copynode(n->ncmd.args);
+ new->ncmd.assign = copynode(n->ncmd.assign);
+ break;
+ case NPIPE:
+ new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
+ new->npipe.backgnd = n->npipe.backgnd;
+ break;
+ case NREDIR:
+ case NBACKGND:
+ case NSUBSHELL:
+ new->nredir.redirect = copynode(n->nredir.redirect);
+ new->nredir.n = copynode(n->nredir.n);
+ break;
+ case NAND:
+ case NOR:
+ case NSEMI:
+ case NWHILE:
+ case NUNTIL:
+ new->nbinary.ch2 = copynode(n->nbinary.ch2);
+ new->nbinary.ch1 = copynode(n->nbinary.ch1);
+ break;
+ case NIF:
+ new->nif.elsepart = copynode(n->nif.elsepart);
+ new->nif.ifpart = copynode(n->nif.ifpart);
+ new->nif.test = copynode(n->nif.test);
+ break;
+ case NFOR:
+ new->nfor.var = nodeckstrdup(n->nfor.var);
+ new->nfor.body = copynode(n->nfor.body);
+ new->nfor.args = copynode(n->nfor.args);
+ break;
+ case NCASE:
+ new->ncase.cases = copynode(n->ncase.cases);
+ new->ncase.expr = copynode(n->ncase.expr);
+ break;
+ case NCLIST:
+ new->nclist.body = copynode(n->nclist.body);
+ new->nclist.pattern = copynode(n->nclist.pattern);
+ new->nclist.next = copynode(n->nclist.next);
+ break;
+ case NDEFUN:
+ case NARG:
+ new->narg.backquote = copynodelist(n->narg.backquote);
+ new->narg.text = nodeckstrdup(n->narg.text);
+ new->narg.next = copynode(n->narg.next);
+ break;
+ case NTO:
+ case NCLOBBER:
+ case NFROM:
+ case NFROMTO:
+ case NAPPEND:
+ new->nfile.fname = copynode(n->nfile.fname);
+ new->nfile.fd = n->nfile.fd;
+ new->nfile.next = copynode(n->nfile.next);
+ break;
+ case NTOFD:
+ case NFROMFD:
+ new->ndup.vname = copynode(n->ndup.vname);
+ new->ndup.dupfd = n->ndup.dupfd;
+ new->ndup.fd = n->ndup.fd;
+ new->ndup.next = copynode(n->ndup.next);
+ break;
+ case NHERE:
+ case NXHERE:
+ new->nhere.doc = copynode(n->nhere.doc);
+ new->nhere.fd = n->nhere.fd;
+ new->nhere.next = copynode(n->nhere.next);
+ break;
+ case NNOT:
+ new->nnot.com = copynode(n->nnot.com);
+ break;
+ };
+ new->type = n->type;
+ return new;
+}
+
+/*
+ * Make a copy of a parse tree.
+ */
+static struct funcnode *
+copyfunc(union node *n)
+{
+ struct funcnode *f;
+ size_t blocksize;
+
+ funcblocksize = offsetof(struct funcnode, n);
+ funcstringsize = 0;
+ calcsize(n);
+ blocksize = funcblocksize;
+ f = ckmalloc(blocksize + funcstringsize);
+ funcblock = (char *) f + offsetof(struct funcnode, n);
+ funcstring = (char *) f + blocksize;
+ copynode(n);
+ f->count = 0;
+ return f;
+}
+
+/*
+ * Define a shell function.
+ */
+static void
+defun(char *name, union node *func)
+{
+ struct cmdentry entry;
+
+ INT_OFF;
+ entry.cmdtype = CMDFUNCTION;
+ entry.u.func = copyfunc(func);
+ addcmdentry(name, &entry);
+ INT_ON;
+}
+
+static int evalskip; /* set if we are skipping commands */
+/* reasons for skipping commands (see comment on breakcmd routine) */
+#define SKIPBREAK (1 << 0)
+#define SKIPCONT (1 << 1)
+#define SKIPFUNC (1 << 2)
+#define SKIPFILE (1 << 3)
+#define SKIPEVAL (1 << 4)
+static int skipcount; /* number of levels to skip */
+static int funcnest; /* depth of function calls */
+
+/* forward decl way out to parsing code - dotrap needs it */
+static int evalstring(char *s, int mask);
+
+/*
+ * Called to execute a trap. Perhaps we should avoid entering new trap
+ * handlers while we are executing a trap handler.
+ */
+static int
+dotrap(void)
+{
+ char *p;
+ char *q;
+ int i;
+ int savestatus;
+ int skip = 0;
+
+ savestatus = exitstatus;
+ pendingsig = 0;
+ xbarrier();
+
+ for (i = 0, q = gotsig; i < NSIG - 1; i++, q++) {
+ if (!*q)
+ continue;
+ *q = '\0';
+
+ p = trap[i + 1];
+ if (!p)
+ continue;
+ skip = evalstring(p, SKIPEVAL);
+ exitstatus = savestatus;
+ if (skip)
+ break;
+ }
+
+ return skip;
+}
+
+/* forward declarations - evaluation is fairly recursive business... */
+static void evalloop(union node *, int);
+static void evalfor(union node *, int);
+static void evalcase(union node *, int);
+static void evalsubshell(union node *, int);
+static void expredir(union node *);
+static void evalpipe(union node *, int);
+static void evalcommand(union node *, int);
+static int evalbltin(const struct builtincmd *, int, char **);
+static void prehash(union node *);
+
+/*
+ * Evaluate a parse tree. The value is left in the global variable
+ * exitstatus.
+ */
+static void
+evaltree(union node *n, int flags)
+{
+ int checkexit = 0;
+ void (*evalfn)(union node *, int);
+ unsigned isor;
+ int status;
+ if (n == NULL) {
+ TRACE(("evaltree(NULL) called\n"));
+ goto out;
+ }
+ TRACE(("pid %d, evaltree(%p: %d, %d) called\n",
+ getpid(), n, n->type, flags));
+ switch (n->type) {
+ default:
+#if DEBUG
+ out1fmt("Node type = %d\n", n->type);
+ fflush(stdout);
+ break;
+#endif
+ case NNOT:
+ evaltree(n->nnot.com, EV_TESTED);
+ status = !exitstatus;
+ goto setstatus;
+ case NREDIR:
+ expredir(n->nredir.redirect);
+ status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
+ if (!status) {
+ evaltree(n->nredir.n, flags & EV_TESTED);
+ status = exitstatus;
+ }
+ popredir(0);
+ goto setstatus;
+ case NCMD:
+ evalfn = evalcommand;
+ checkexit:
+ if (eflag && !(flags & EV_TESTED))
+ checkexit = ~0;
+ goto calleval;
+ case NFOR:
+ evalfn = evalfor;
+ goto calleval;
+ case NWHILE:
+ case NUNTIL:
+ evalfn = evalloop;
+ goto calleval;
+ case NSUBSHELL:
+ case NBACKGND:
+ evalfn = evalsubshell;
+ goto calleval;
+ case NPIPE:
+ evalfn = evalpipe;
+ goto checkexit;
+ case NCASE:
+ evalfn = evalcase;
+ goto calleval;
+ case NAND:
+ case NOR:
+ case NSEMI:
+#if NAND + 1 != NOR
+#error NAND + 1 != NOR
+#endif
+#if NOR + 1 != NSEMI
+#error NOR + 1 != NSEMI
+#endif
+ isor = n->type - NAND;
+ evaltree(
+ n->nbinary.ch1,
+ (flags | ((isor >> 1) - 1)) & EV_TESTED
+ );
+ if (!exitstatus == isor)
+ break;
+ if (!evalskip) {
+ n = n->nbinary.ch2;
+ evaln:
+ evalfn = evaltree;
+ calleval:
+ evalfn(n, flags);
+ break;
+ }
+ break;
+ case NIF:
+ evaltree(n->nif.test, EV_TESTED);
+ if (evalskip)
+ break;
+ if (exitstatus == 0) {
+ n = n->nif.ifpart;
+ goto evaln;
+ } else if (n->nif.elsepart) {
+ n = n->nif.elsepart;
+ goto evaln;
+ }
+ goto success;
+ case NDEFUN:
+ defun(n->narg.text, n->narg.next);
+ success:
+ status = 0;
+ setstatus:
+ exitstatus = status;
+ break;
+ }
+ out:
+ if ((checkexit & exitstatus))
+ evalskip |= SKIPEVAL;
+ else if (pendingsig && dotrap())
+ goto exexit;
+
+ if (flags & EV_EXIT) {
+ exexit:
+ raise_exception(EXEXIT);
+ }
+}
+
+#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
+static
+#endif
+void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
+
+static int loopnest; /* current loop nesting level */
+
+static void
+evalloop(union node *n, int flags)
+{
+ int status;
+
+ loopnest++;
+ status = 0;
+ flags &= EV_TESTED;
+ for (;;) {
+ int i;
+
+ evaltree(n->nbinary.ch1, EV_TESTED);
+ if (evalskip) {
+ skipping:
+ if (evalskip == SKIPCONT && --skipcount <= 0) {
+ evalskip = 0;
+ continue;
+ }
+ if (evalskip == SKIPBREAK && --skipcount <= 0)
+ evalskip = 0;
+ break;
+ }
+ i = exitstatus;
+ if (n->type != NWHILE)
+ i = !i;
+ if (i != 0)
+ break;
+ evaltree(n->nbinary.ch2, flags);
+ status = exitstatus;
+ if (evalskip)
+ goto skipping;
+ }
+ loopnest--;
+ exitstatus = status;
+}
+
+static void
+evalfor(union node *n, int flags)
+{
+ struct arglist arglist;
+ union node *argp;
+ struct strlist *sp;
+ struct stackmark smark;
+
+ setstackmark(&smark);
+ arglist.lastp = &arglist.list;
+ for (argp = n->nfor.args; argp; argp = argp->narg.next) {
+ expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
+ /* XXX */
+ if (evalskip)
+ goto out;
+ }
+ *arglist.lastp = NULL;
+
+ exitstatus = 0;
+ loopnest++;
+ flags &= EV_TESTED;
+ for (sp = arglist.list; sp; sp = sp->next) {
+ setvar(n->nfor.var, sp->text, 0);
+ evaltree(n->nfor.body, flags);
+ if (evalskip) {
+ if (evalskip == SKIPCONT && --skipcount <= 0) {
+ evalskip = 0;
+ continue;
+ }
+ if (evalskip == SKIPBREAK && --skipcount <= 0)
+ evalskip = 0;
+ break;
+ }
+ }
+ loopnest--;
+ out:
+ popstackmark(&smark);
+}
+
+static void
+evalcase(union node *n, int flags)
+{
+ union node *cp;
+ union node *patp;
+ struct arglist arglist;
+ struct stackmark smark;
+
+ setstackmark(&smark);
+ arglist.lastp = &arglist.list;
+ expandarg(n->ncase.expr, &arglist, EXP_TILDE);
+ exitstatus = 0;
+ for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
+ for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
+ if (casematch(patp, arglist.list->text)) {
+ if (evalskip == 0) {
+ evaltree(cp->nclist.body, flags);
+ }
+ goto out;
+ }
+ }
+ }
+ out:
+ popstackmark(&smark);
+}
+
+/*
+ * Kick off a subshell to evaluate a tree.
+ */
+static void
+evalsubshell(union node *n, int flags)
+{
+ struct job *jp;
+ int backgnd = (n->type == NBACKGND);
+ int status;
+
+ expredir(n->nredir.redirect);
+ if (!backgnd && flags & EV_EXIT && !trap[0])
+ goto nofork;
+ INT_OFF;
+ jp = makejob(n, 1);
+ if (forkshell(jp, n, backgnd) == 0) {
+ INT_ON;
+ flags |= EV_EXIT;
+ if (backgnd)
+ flags &=~ EV_TESTED;
+ nofork:
+ redirect(n->nredir.redirect, 0);
+ evaltreenr(n->nredir.n, flags);
+ /* never returns */
+ }
+ status = 0;
+ if (! backgnd)
+ status = waitforjob(jp);
+ exitstatus = status;
+ INT_ON;
+}
+
+/*
+ * Compute the names of the files in a redirection list.
+ */
+static void fixredir(union node *, const char *, int);
+static void
+expredir(union node *n)
+{
+ union node *redir;
+
+ for (redir = n; redir; redir = redir->nfile.next) {
+ struct arglist fn;
+
+ memset(&fn, 0, sizeof(fn));
+ fn.lastp = &fn.list;
+ switch (redir->type) {
+ case NFROMTO:
+ case NFROM:
+ case NTO:
+ case NCLOBBER:
+ case NAPPEND:
+ expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
+ redir->nfile.expfname = fn.list->text;
+ break;
+ case NFROMFD:
+ case NTOFD:
+ if (redir->ndup.vname) {
+ expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
+ if (fn.list == NULL)
+ ash_msg_and_raise_error("redir error");
+ fixredir(redir, fn.list->text, 1);
+ }
+ break;
+ }
+ }
+}
+
+/*
+ * Evaluate a pipeline. All the processes in the pipeline are children
+ * of the process creating the pipeline. (This differs from some versions
+ * of the shell, which make the last process in a pipeline the parent
+ * of all the rest.)
+ */
+static void
+evalpipe(union node *n, int flags)
+{
+ struct job *jp;
+ struct nodelist *lp;
+ int pipelen;
+ int prevfd;
+ int pip[2];
+
+ TRACE(("evalpipe(0x%lx) called\n", (long)n));
+ pipelen = 0;
+ for (lp = n->npipe.cmdlist; lp; lp = lp->next)
+ pipelen++;
+ flags |= EV_EXIT;
+ INT_OFF;
+ jp = makejob(n, pipelen);
+ prevfd = -1;
+ for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
+ prehash(lp->n);
+ pip[1] = -1;
+ if (lp->next) {
+ if (pipe(pip) < 0) {
+ close(prevfd);
+ ash_msg_and_raise_error("pipe call failed");
+ }
+ }
+ if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
+ INT_ON;
+ if (pip[1] >= 0) {
+ close(pip[0]);
+ }
+ if (prevfd > 0) {
+ dup2(prevfd, 0);
+ close(prevfd);
+ }
+ if (pip[1] > 1) {
+ dup2(pip[1], 1);
+ close(pip[1]);
+ }
+ evaltreenr(lp->n, flags);
+ /* never returns */
+ }
+ if (prevfd >= 0)
+ close(prevfd);
+ prevfd = pip[0];
+ close(pip[1]);
+ }
+ if (n->npipe.backgnd == 0) {
+ exitstatus = waitforjob(jp);
+ TRACE(("evalpipe: job done exit status %d\n", exitstatus));
+ }
+ INT_ON;
+}
+
+/*
+ * Controls whether the shell is interactive or not.
+ */
+static void
+setinteractive(int on)
+{
+ static int is_interactive;
+
+ if (++on == is_interactive)
+ return;
+ is_interactive = on;
+ setsignal(SIGINT);
+ setsignal(SIGQUIT);
+ setsignal(SIGTERM);
+#if !ENABLE_FEATURE_SH_EXTRA_QUIET
+ if (is_interactive > 1) {
+ /* Looks like they want an interactive shell */
+ static smallint do_banner;
+
+ if (!do_banner) {
+ out1fmt(
+ "\n\n"
+ "%s Built-in shell (ash)\n"
+ "Enter 'help' for a list of built-in commands."
+ "\n\n",
+ BB_BANNER);
+ do_banner = 1;
+ }
+ }
+#endif
+}
+
+#if ENABLE_FEATURE_EDITING_VI
+#define setvimode(on) do { \
+ if (on) line_input_state->flags |= VI_MODE; \
+ else line_input_state->flags &= ~VI_MODE; \
+} while (0)
+#else
+#define setvimode(on) viflag = 0 /* forcibly keep the option off */
+#endif
+
+static void
+optschanged(void)
+{
+#if DEBUG
+ opentrace();
+#endif
+ setinteractive(iflag);
+ setjobctl(mflag);
+ setvimode(viflag);
+}
+
+static struct localvar *localvars;
+
+/*
+ * Called after a function returns.
+ * Interrupts must be off.
+ */
+static void
+poplocalvars(void)
+{
+ struct localvar *lvp;
+ struct var *vp;
+
+ while ((lvp = localvars) != NULL) {
+ localvars = lvp->next;
+ vp = lvp->vp;
+ TRACE(("poplocalvar %s", vp ? vp->text : "-"));
+ if (vp == NULL) { /* $- saved */
+ memcpy(optlist, lvp->text, sizeof(optlist));
+ free((char*)lvp->text);
+ optschanged();
+ } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
+ unsetvar(vp->text);
+ } else {
+ if (vp->func)
+ (*vp->func)(strchrnul(lvp->text, '=') + 1);
+ if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
+ free((char*)vp->text);
+ vp->flags = lvp->flags;
+ vp->text = lvp->text;
+ }
+ free(lvp);
+ }
+}
+
+static int
+evalfun(struct funcnode *func, int argc, char **argv, int flags)
+{
+ volatile struct shparam saveparam;
+ struct localvar *volatile savelocalvars;
+ struct jmploc *volatile savehandler;
+ struct jmploc jmploc;
+ int e;
+
+ saveparam = shellparam;
+ savelocalvars = localvars;
+ e = setjmp(jmploc.loc);
+ if (e) {
+ goto funcdone;
+ }
+ INT_OFF;
+ savehandler = exception_handler;
+ exception_handler = &jmploc;
+ localvars = NULL;
+ shellparam.malloc = 0;
+ func->count++;
+ funcnest++;
+ INT_ON;
+ shellparam.nparam = argc - 1;
+ shellparam.p = argv + 1;
+#if ENABLE_ASH_GETOPTS
+ shellparam.optind = 1;
+ shellparam.optoff = -1;
+#endif
+ evaltree(&func->n, flags & EV_TESTED);
+funcdone:
+ INT_OFF;
+ funcnest--;
+ freefunc(func);
+ poplocalvars();
+ localvars = savelocalvars;
+ freeparam(&shellparam);
+ shellparam = saveparam;
+ exception_handler = savehandler;
+ INT_ON;
+ evalskip &= ~SKIPFUNC;
+ return e;
+}
+
+#if ENABLE_ASH_CMDCMD
+static char **
+parse_command_args(char **argv, const char **path)
+{
+ char *cp, c;
+
+ for (;;) {
+ cp = *++argv;
+ if (!cp)
+ return 0;
+ if (*cp++ != '-')
+ break;
+ c = *cp++;
+ if (!c)
+ break;
+ if (c == '-' && !*cp) {
+ argv++;
+ break;
+ }
+ do {
+ switch (c) {
+ case 'p':
+ *path = defpath;
+ break;
+ default:
+ /* run 'typecmd' for other options */
+ return 0;
+ }
+ c = *cp++;
+ } while (c);
+ }
+ return argv;
+}
+#endif
+
+/*
+ * Make a variable a local variable. When a variable is made local, it's
+ * value and flags are saved in a localvar structure. The saved values
+ * will be restored when the shell function returns. We handle the name
+ * "-" as a special case.
+ */
+static void
+mklocal(char *name)
+{
+ struct localvar *lvp;
+ struct var **vpp;
+ struct var *vp;
+
+ INT_OFF;
+ lvp = ckmalloc(sizeof(struct localvar));
+ if (LONE_DASH(name)) {
+ char *p;
+ p = ckmalloc(sizeof(optlist));
+ lvp->text = memcpy(p, optlist, sizeof(optlist));
+ vp = NULL;
+ } else {
+ char *eq;
+
+ vpp = hashvar(name);
+ vp = *findvar(vpp, name);
+ eq = strchr(name, '=');
+ if (vp == NULL) {
+ if (eq)
+ setvareq(name, VSTRFIXED);
+ else
+ setvar(name, NULL, VSTRFIXED);
+ vp = *vpp; /* the new variable */
+ lvp->flags = VUNSET;
+ } else {
+ lvp->text = vp->text;
+ lvp->flags = vp->flags;
+ vp->flags |= VSTRFIXED|VTEXTFIXED;
+ if (eq)
+ setvareq(name, 0);
+ }
+ }
+ lvp->vp = vp;
+ lvp->next = localvars;
+ localvars = lvp;
+ INT_ON;
+}
+
+/*
+ * The "local" command.
+ */
+static int
+localcmd(int argc, char **argv)
+{
+ char *name;
+
+ argv = argptr;
+ while ((name = *argv++) != NULL) {
+ mklocal(name);
+ }
+ return 0;
+}
+
+static int
+falsecmd(int argc, char **argv)
+{
+ return 1;
+}
+
+static int
+truecmd(int argc, char **argv)
+{
+ return 0;
+}
+
+static int
+execcmd(int argc, char **argv)
+{
+ if (argc > 1) {
+ iflag = 0; /* exit on error */
+ mflag = 0;
+ optschanged();
+ shellexec(argv + 1, pathval(), 0);
+ }
+ return 0;
+}
+
+/*
+ * The return command.
+ */
+static int
+returncmd(int argc, char **argv)
+{
+ /*
+ * If called outside a function, do what ksh does;
+ * skip the rest of the file.
+ */
+ evalskip = funcnest ? SKIPFUNC : SKIPFILE;
+ return argv[1] ? number(argv[1]) : exitstatus;
+}
+
+/* Forward declarations for builtintab[] */
+static int breakcmd(int, char **);
+static int dotcmd(int, char **);
+static int evalcmd(int, char **);
+#if ENABLE_ASH_BUILTIN_ECHO
+static int echocmd(int, char **);
+#endif
+#if ENABLE_ASH_BUILTIN_TEST
+static int testcmd(int, char **);
+#endif
+static int exitcmd(int, char **);
+static int exportcmd(int, char **);
+#if ENABLE_ASH_GETOPTS
+static int getoptscmd(int, char **);
+#endif
+#if !ENABLE_FEATURE_SH_EXTRA_QUIET
+static int helpcmd(int argc, char **argv);
+#endif
+#if ENABLE_ASH_MATH_SUPPORT
+static int letcmd(int, char **);
+#endif
+static int readcmd(int, char **);
+static int setcmd(int, char **);
+static int shiftcmd(int, char **);
+static int timescmd(int, char **);
+static int trapcmd(int, char **);
+static int umaskcmd(int, char **);
+static int unsetcmd(int, char **);
+static int ulimitcmd(int, char **);
+
+#define BUILTIN_NOSPEC "0"
+#define BUILTIN_SPECIAL "1"
+#define BUILTIN_REGULAR "2"
+#define BUILTIN_SPEC_REG "3"
+#define BUILTIN_ASSIGN "4"
+#define BUILTIN_SPEC_ASSG "5"
+#define BUILTIN_REG_ASSG "6"
+#define BUILTIN_SPEC_REG_ASSG "7"
+
+/* make sure to keep these in proper order since it is searched via bsearch() */
+static const struct builtincmd builtintab[] = {
+ { BUILTIN_SPEC_REG ".", dotcmd },
+ { BUILTIN_SPEC_REG ":", truecmd },
+#if ENABLE_ASH_BUILTIN_TEST
+ { BUILTIN_REGULAR "[", testcmd },
+ { BUILTIN_REGULAR "[[", testcmd },
+#endif
+#if ENABLE_ASH_ALIAS
+ { BUILTIN_REG_ASSG "alias", aliascmd },
+#endif
+#if JOBS
+ { BUILTIN_REGULAR "bg", fg_bgcmd },
+#endif
+ { BUILTIN_SPEC_REG "break", breakcmd },
+ { BUILTIN_REGULAR "cd", cdcmd },
+ { BUILTIN_NOSPEC "chdir", cdcmd },
+#if ENABLE_ASH_CMDCMD
+ { BUILTIN_REGULAR "command", commandcmd },
+#endif
+ { BUILTIN_SPEC_REG "continue", breakcmd },
+#if ENABLE_ASH_BUILTIN_ECHO
+ { BUILTIN_REGULAR "echo", echocmd },
+#endif
+ { BUILTIN_SPEC_REG "eval", evalcmd },
+ { BUILTIN_SPEC_REG "exec", execcmd },
+ { BUILTIN_SPEC_REG "exit", exitcmd },
+ { BUILTIN_SPEC_REG_ASSG "export", exportcmd },
+ { BUILTIN_REGULAR "false", falsecmd },
+#if JOBS
+ { BUILTIN_REGULAR "fg", fg_bgcmd },
+#endif
+#if ENABLE_ASH_GETOPTS
+ { BUILTIN_REGULAR "getopts", getoptscmd },
+#endif
+ { BUILTIN_NOSPEC "hash", hashcmd },
+#if !ENABLE_FEATURE_SH_EXTRA_QUIET
+ { BUILTIN_NOSPEC "help", helpcmd },
+#endif
+#if JOBS
+ { BUILTIN_REGULAR "jobs", jobscmd },
+ { BUILTIN_REGULAR "kill", killcmd },
+#endif
+#if ENABLE_ASH_MATH_SUPPORT
+ { BUILTIN_NOSPEC "let", letcmd },
+#endif
+ { BUILTIN_ASSIGN "local", localcmd },
+ { BUILTIN_NOSPEC "pwd", pwdcmd },
+ { BUILTIN_REGULAR "read", readcmd },
+ { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
+ { BUILTIN_SPEC_REG "return", returncmd },
+ { BUILTIN_SPEC_REG "set", setcmd },
+ { BUILTIN_SPEC_REG "shift", shiftcmd },
+ { BUILTIN_SPEC_REG "source", dotcmd },
+#if ENABLE_ASH_BUILTIN_TEST
+ { BUILTIN_REGULAR "test", testcmd },
+#endif
+ { BUILTIN_SPEC_REG "times", timescmd },
+ { BUILTIN_SPEC_REG "trap", trapcmd },
+ { BUILTIN_REGULAR "true", truecmd },
+ { BUILTIN_NOSPEC "type", typecmd },
+ { BUILTIN_NOSPEC "ulimit", ulimitcmd },
+ { BUILTIN_REGULAR "umask", umaskcmd },
+#if ENABLE_ASH_ALIAS
+ { BUILTIN_REGULAR "unalias", unaliascmd },
+#endif
+ { BUILTIN_SPEC_REG "unset", unsetcmd },
+ { BUILTIN_REGULAR "wait", waitcmd },
+};
+
+#define NUMBUILTINS (sizeof(builtintab) / sizeof(builtintab[0]))
+
+#define COMMANDCMD (builtintab + 5 + \
+ 2 * ENABLE_ASH_BUILTIN_TEST + \
+ ENABLE_ASH_ALIAS + \
+ ENABLE_ASH_JOB_CONTROL)
+#define EXECCMD (builtintab + 7 + \
+ 2 * ENABLE_ASH_BUILTIN_TEST + \
+ ENABLE_ASH_ALIAS + \
+ ENABLE_ASH_JOB_CONTROL + \
+ ENABLE_ASH_CMDCMD + \
+ ENABLE_ASH_BUILTIN_ECHO)
+
+/*
+ * Search the table of builtin commands.
+ */
+static struct builtincmd *
+find_builtin(const char *name)
+{
+ struct builtincmd *bp;
+
+ bp = bsearch(
+ name, builtintab, NUMBUILTINS, sizeof(builtintab[0]),
+ pstrcmp
+ );
+ return bp;
+}
+
+/*
+ * Execute a simple command.
+ */
+static int back_exitstatus; /* exit status of backquoted command */
+static int
+isassignment(const char *p)
+{
+ const char *q = endofname(p);
+ if (p == q)
+ return 0;
+ return *q == '=';
+}
+static int
+bltincmd(int argc, char **argv)
+{
+ /* Preserve exitstatus of a previous possible redirection
+ * as POSIX mandates */
+ return back_exitstatus;
+}
+static void
+evalcommand(union node *cmd, int flags)
+{
+ static const struct builtincmd bltin = {
+ "\0\0", bltincmd
+ };
+ struct stackmark smark;
+ union node *argp;
+ struct arglist arglist;
+ struct arglist varlist;
+ char **argv;
+ int argc;
+ const struct strlist *sp;
+ struct cmdentry cmdentry;
+ struct job *jp;
+ char *lastarg;
+ const char *path;
+ int spclbltin;
+ int cmd_is_exec;
+ int status;
+ char **nargv;
+ struct builtincmd *bcmd;
+ int pseudovarflag = 0;
+
+ /* First expand the arguments. */
+ TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
+ setstackmark(&smark);
+ back_exitstatus = 0;
+
+ cmdentry.cmdtype = CMDBUILTIN;
+ cmdentry.u.cmd = &bltin;
+ varlist.lastp = &varlist.list;
+ *varlist.lastp = NULL;
+ arglist.lastp = &arglist.list;
+ *arglist.lastp = NULL;
+
+ argc = 0;
+ if (cmd->ncmd.args) {
+ bcmd = find_builtin(cmd->ncmd.args->narg.text);
+ pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
+ }
+
+ for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
+ struct strlist **spp;
+
+ spp = arglist.lastp;
+ if (pseudovarflag && isassignment(argp->narg.text))
+ expandarg(argp, &arglist, EXP_VARTILDE);
+ else
+ expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
+
+ for (sp = *spp; sp; sp = sp->next)
+ argc++;
+ }
+
+ argv = nargv = stalloc(sizeof(char *) * (argc + 1));
+ for (sp = arglist.list; sp; sp = sp->next) {
+ TRACE(("evalcommand arg: %s\n", sp->text));
+ *nargv++ = sp->text;
+ }
+ *nargv = NULL;
+
+ lastarg = NULL;
+ if (iflag && funcnest == 0 && argc > 0)
+ lastarg = nargv[-1];
+
+ preverrout_fd = 2;
+ expredir(cmd->ncmd.redirect);
+ status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
+
+ path = vpath.text;
+ for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
+ struct strlist **spp;
+ char *p;
+
+ spp = varlist.lastp;
+ expandarg(argp, &varlist, EXP_VARTILDE);
+
+ /*
+ * Modify the command lookup path, if a PATH= assignment
+ * is present
+ */
+ p = (*spp)->text;
+ if (varequal(p, path))
+ path = p;
+ }
+
+ /* Print the command if xflag is set. */
+ if (xflag) {
+ int n;
+ const char *p = " %s";
+
+ p++;
+ dprintf(preverrout_fd, p, expandstr(ps4val()));
+
+ sp = varlist.list;
+ for (n = 0; n < 2; n++) {
+ while (sp) {
+ dprintf(preverrout_fd, p, sp->text);
+ sp = sp->next;
+ if (*p == '%') {
+ p--;
+ }
+ }
+ sp = arglist.list;
+ }
+ full_write(preverrout_fd, "\n", 1);
+ }
+
+ cmd_is_exec = 0;
+ spclbltin = -1;
+
+ /* Now locate the command. */
+ if (argc) {
+ const char *oldpath;
+ int cmd_flag = DO_ERR;
+
+ path += 5;
+ oldpath = path;
+ for (;;) {
+ find_command(argv[0], &cmdentry, cmd_flag, path);
+ if (cmdentry.cmdtype == CMDUNKNOWN) {
+ status = 127;
+ flush_stderr();
+ goto bail;
+ }
+
+ /* implement bltin and command here */
+ if (cmdentry.cmdtype != CMDBUILTIN)
+ break;
+ if (spclbltin < 0)
+ spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
+ if (cmdentry.u.cmd == EXECCMD)
+ cmd_is_exec++;
+#if ENABLE_ASH_CMDCMD
+ if (cmdentry.u.cmd == COMMANDCMD) {
+ path = oldpath;
+ nargv = parse_command_args(argv, &path);
+ if (!nargv)
+ break;
+ argc -= nargv - argv;
+ argv = nargv;
+ cmd_flag |= DO_NOFUNC;
+ } else
+#endif
+ break;
+ }
+ }
+
+ if (status) {
+ /* We have a redirection error. */
+ if (spclbltin > 0)
+ raise_exception(EXERROR);
+ bail:
+ exitstatus = status;
+ goto out;
+ }
+
+ /* Execute the command. */
+ switch (cmdentry.cmdtype) {
+ default:
+ /* Fork off a child process if necessary. */
+ if (!(flags & EV_EXIT) || trap[0]) {
+ INT_OFF;
+ jp = makejob(cmd, 1);
+ if (forkshell(jp, cmd, FORK_FG) != 0) {
+ exitstatus = waitforjob(jp);
+ INT_ON;
+ break;
+ }
+ FORCE_INT_ON;
+ }
+ listsetvar(varlist.list, VEXPORT|VSTACK);
+ shellexec(argv, path, cmdentry.u.index);
+ /* NOTREACHED */
+
+ case CMDBUILTIN:
+ cmdenviron = varlist.list;
+ if (cmdenviron) {
+ struct strlist *list = cmdenviron;
+ int i = VNOSET;
+ if (spclbltin > 0 || argc == 0) {
+ i = 0;
+ if (cmd_is_exec && argc > 1)
+ i = VEXPORT;
+ }
+ listsetvar(list, i);
+ }
+ if (evalbltin(cmdentry.u.cmd, argc, argv)) {
+ int exit_status;
+ int i, j;
+
+ i = exception;
+ if (i == EXEXIT)
+ goto raise;
+
+ exit_status = 2;
+ j = 0;
+ if (i == EXINT)
+ j = SIGINT;
+ if (i == EXSIG)
+ j = pendingsig;
+ if (j)
+ exit_status = j + 128;
+ exitstatus = exit_status;
+
+ if (i == EXINT || spclbltin > 0) {
+ raise:
+ longjmp(exception_handler->loc, 1);
+ }
+ FORCE_INT_ON;
+ }
+ break;
+
+ case CMDFUNCTION:
+ listsetvar(varlist.list, 0);
+ if (evalfun(cmdentry.u.func, argc, argv, flags))
+ goto raise;
+ break;
+ }
+
+ out:
+ popredir(cmd_is_exec);
+ if (lastarg)
+ /* dsl: I think this is intended to be used to support
+ * '_' in 'vi' command mode during line editing...
+ * However I implemented that within libedit itself.
+ */
+ setvar("_", lastarg, 0);
+ popstackmark(&smark);
+}
+
+static int
+evalbltin(const struct builtincmd *cmd, int argc, char **argv)
+{
+ char *volatile savecmdname;
+ struct jmploc *volatile savehandler;
+ struct jmploc jmploc;
+ int i;
+
+ savecmdname = commandname;
+ i = setjmp(jmploc.loc);
+ if (i)
+ goto cmddone;
+ savehandler = exception_handler;
+ exception_handler = &jmploc;
+ commandname = argv[0];
+ argptr = argv + 1;
+ optptr = NULL; /* initialize nextopt */
+ exitstatus = (*cmd->builtin)(argc, argv);
+ flush_stdout_stderr();
+ cmddone:
+ exitstatus |= ferror(stdout);
+ clearerr(stdout);
+ commandname = savecmdname;
+ exsig = 0;
+ exception_handler = savehandler;
+
+ return i;
+}
+
+static int
+goodname(const char *p)
+{
+ return !*endofname(p);
+}
+
+
+/*
+ * Search for a command. This is called before we fork so that the
+ * location of the command will be available in the parent as well as
+ * the child. The check for "goodname" is an overly conservative
+ * check that the name will not be subject to expansion.
+ */
+static void
+prehash(union node *n)
+{
+ struct cmdentry entry;
+
+ if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
+ find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
+}
+
+
+/* ============ Builtin commands
+ *
+ * Builtin commands whose functions are closely tied to evaluation
+ * are implemented here.
+ */
+
+/*
+ * Handle break and continue commands. Break, continue, and return are
+ * all handled by setting the evalskip flag. The evaluation routines
+ * above all check this flag, and if it is set they start skipping
+ * commands rather than executing them. The variable skipcount is
+ * the number of loops to break/continue, or the number of function
+ * levels to return. (The latter is always 1.) It should probably
+ * be an error to break out of more loops than exist, but it isn't
+ * in the standard shell so we don't make it one here.
+ */
+static int
+breakcmd(int argc, char **argv)
+{
+ int n = argc > 1 ? number(argv[1]) : 1;
+
+ if (n <= 0)
+ ash_msg_and_raise_error(illnum, argv[1]);
+ if (n > loopnest)
+ n = loopnest;
+ if (n > 0) {
+ evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
+ skipcount = n;
+ }
+ return 0;
+}
+
+
+/* ============ input.c
+ *
+ * This implements the input routines used by the parser.
+ */
+
+#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */
+
+enum {
+ INPUT_PUSH_FILE = 1,
+ INPUT_NOFILE_OK = 2,
+};
+
+/*
+ * NEOF is returned by parsecmd when it encounters an end of file. It
+ * must be distinct from NULL, so we use the address of a variable that
+ * happens to be handy.
+ */
+static int plinno = 1; /* input line number */
+/* number of characters left in input buffer */
+static int parsenleft; /* copy of parsefile->nleft */
+static int parselleft; /* copy of parsefile->lleft */
+/* next character in input buffer */
+static char *parsenextc; /* copy of parsefile->nextc */
+
+static int checkkwd;
+/* values of checkkwd variable */
+#define CHKALIAS 0x1
+#define CHKKWD 0x2
+#define CHKNL 0x4
+
+static void
+popstring(void)
+{
+ struct strpush *sp = parsefile->strpush;
+
+ INT_OFF;
+#if ENABLE_ASH_ALIAS
+ if (sp->ap) {
+ if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') {
+ checkkwd |= CHKALIAS;
+ }
+ if (sp->string != sp->ap->val) {
+ free(sp->string);
+ }
+ sp->ap->flag &= ~ALIASINUSE;
+ if (sp->ap->flag & ALIASDEAD) {
+ unalias(sp->ap->name);
+ }
+ }
+#endif
+ parsenextc = sp->prevstring;
+ parsenleft = sp->prevnleft;
+/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
+ parsefile->strpush = sp->prev;
+ if (sp != &(parsefile->basestrpush))
+ free(sp);
+ INT_ON;
+}
+
+static int
+preadfd(void)
+{
+ int nr;
+ char *buf = parsefile->buf;
+ parsenextc = buf;
+
+ retry:
+#if ENABLE_FEATURE_EDITING
+ if (!iflag || parsefile->fd)
+ nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
+ else {
+#if ENABLE_FEATURE_TAB_COMPLETION
+ line_input_state->path_lookup = pathval();
+#endif
+ nr = read_line_input(cmdedit_prompt, buf, BUFSIZ, line_input_state);
+ if (nr == 0) {
+ /* Ctrl+C pressed */
+ if (trap[SIGINT]) {
+ buf[0] = '\n';
+ buf[1] = '\0';
+ raise(SIGINT);
+ return 1;
+ }
+ goto retry;
+ }
+ if (nr < 0 && errno == 0) {
+ /* Ctrl+D presend */
+ nr = 0;
+ }
+ }
+#else
+ nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
+#endif
+
+ if (nr < 0) {
+ if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
+ int flags = fcntl(0, F_GETFL, 0);
+ if (flags >= 0 && flags & O_NONBLOCK) {
+ flags &=~ O_NONBLOCK;
+ if (fcntl(0, F_SETFL, flags) >= 0) {
+ out2str("sh: turning off NDELAY mode\n");
+ goto retry;
+ }
+ }
+ }
+ }
+ return nr;
+}
+
+/*
+ * Refill the input buffer and return the next input character:
+ *
+ * 1) If a string was pushed back on the input, pop it;
+ * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
+ * from a string so we can't refill the buffer, return EOF.
+ * 3) If the is more stuff in this buffer, use it else call read to fill it.
+ * 4) Process input up to the next newline, deleting nul characters.
+ */
+static int
+preadbuffer(void)
+{
+ char *q;
+ int more;
+ char savec;
+
+ while (parsefile->strpush) {
+#if ENABLE_ASH_ALIAS
+ if (parsenleft == -1 && parsefile->strpush->ap &&
+ parsenextc[-1] != ' ' && parsenextc[-1] != '\t') {
+ return PEOA;
+ }
+#endif
+ popstring();
+ if (--parsenleft >= 0)
+ return signed_char2int(*parsenextc++);
+ }
+ if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
+ return PEOF;
+ flush_stdout_stderr();
+
+ more = parselleft;
+ if (more <= 0) {
+ again:
+ more = preadfd();
+ if (more <= 0) {
+ parselleft = parsenleft = EOF_NLEFT;
+ return PEOF;
+ }
+ }
+
+ q = parsenextc;
+
+ /* delete nul characters */
+ for (;;) {
+ int c;
+
+ more--;
+ c = *q;
+
+ if (!c)
+ memmove(q, q + 1, more);
+ else {
+ q++;
+ if (c == '\n') {
+ parsenleft = q - parsenextc - 1;
+ break;
+ }
+ }
+
+ if (more <= 0) {
+ parsenleft = q - parsenextc - 1;
+ if (parsenleft < 0)
+ goto again;
+ break;
+ }
+ }
+ parselleft = more;
+
+ savec = *q;
+ *q = '\0';
+
+ if (vflag) {
+ out2str(parsenextc);
+ }
+
+ *q = savec;
+
+ return signed_char2int(*parsenextc++);
+}
+
+#define pgetc_as_macro() (--parsenleft >= 0? signed_char2int(*parsenextc++) : preadbuffer())
+static int
+pgetc(void)
+{
+ return pgetc_as_macro();
+}
+
+#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
+#define pgetc_macro() pgetc()
+#else
+#define pgetc_macro() pgetc_as_macro()
+#endif
+
+/*
+ * Same as pgetc(), but ignores PEOA.
+ */
+#if ENABLE_ASH_ALIAS
+static int
+pgetc2(void)
+{
+ int c;
+
+ do {
+ c = pgetc_macro();
+ } while (c == PEOA);
+ return c;
+}
+#else
+static int
+pgetc2(void)
+{
+ return pgetc_macro();
+}
+#endif
+
+/*
+ * Read a line from the script.
+ */
+static char *
+pfgets(char *line, int len)
+{
+ char *p = line;
+ int nleft = len;
+ int c;
+
+ while (--nleft > 0) {
+ c = pgetc2();
+ if (c == PEOF) {
+ if (p == line)
+ return NULL;
+ break;
+ }
+ *p++ = c;
+ if (c == '\n')
+ break;
+ }
+ *p = '\0';
+ return line;
+}
+
+/*
+ * Undo the last call to pgetc. Only one character may be pushed back.
+ * PEOF may be pushed back.
+ */
+static void
+pungetc(void)
+{
+ parsenleft++;
+ parsenextc--;
+}
+
+/*
+ * Push a string back onto the input at this current parsefile level.
+ * We handle aliases this way.
+ */
+static void
+pushstring(char *s, void *ap)
+{
+ struct strpush *sp;
+ size_t len;
+
+ len = strlen(s);
+ INT_OFF;
+/*dprintf("*** calling pushstring: %s, %d\n", s, len);*/
+ if (parsefile->strpush) {
+ sp = ckmalloc(sizeof(struct strpush));
+ sp->prev = parsefile->strpush;
+ parsefile->strpush = sp;
+ } else
+ sp = parsefile->strpush = &(parsefile->basestrpush);
+ sp->prevstring = parsenextc;
+ sp->prevnleft = parsenleft;
+#if ENABLE_ASH_ALIAS
+ sp->ap = (struct alias *)ap;
+ if (ap) {
+ ((struct alias *)ap)->flag |= ALIASINUSE;
+ sp->string = s;
+ }
+#endif
+ parsenextc = s;
+ parsenleft = len;
+ INT_ON;
+}
+
+/*
+ * To handle the "." command, a stack of input files is used. Pushfile
+ * adds a new entry to the stack and popfile restores the previous level.
+ */
+static void
+pushfile(void)
+{
+ struct parsefile *pf;
+
+ parsefile->nleft = parsenleft;
+ parsefile->lleft = parselleft;
+ parsefile->nextc = parsenextc;
+ parsefile->linno = plinno;
+ pf = ckmalloc(sizeof(*pf));
+ pf->prev = parsefile;
+ pf->fd = -1;
+ pf->strpush = NULL;
+ pf->basestrpush.prev = NULL;
+ parsefile = pf;
+}
+
+static void
+popfile(void)
+{
+ struct parsefile *pf = parsefile;
+
+ INT_OFF;
+ if (pf->fd >= 0)
+ close(pf->fd);
+ if (pf->buf)
+ free(pf->buf);
+ while (pf->strpush)
+ popstring();
+ parsefile = pf->prev;
+ free(pf);
+ parsenleft = parsefile->nleft;
+ parselleft = parsefile->lleft;
+ parsenextc = parsefile->nextc;
+ plinno = parsefile->linno;
+ INT_ON;
+}
+
+/*
+ * Return to top level.
+ */
+static void
+popallfiles(void)
+{
+ while (parsefile != &basepf)
+ popfile();
+}
+
+/*
+ * Close the file(s) that the shell is reading commands from. Called
+ * after a fork is done.
+ */
+static void
+closescript(void)
+{
+ popallfiles();
+ if (parsefile->fd > 0) {
+ close(parsefile->fd);
+ parsefile->fd = 0;
+ }
+}
+
+/*
+ * Like setinputfile, but takes an open file descriptor. Call this with
+ * interrupts off.
+ */
+static void
+setinputfd(int fd, int push)
+{
+ fcntl(fd, F_SETFD, FD_CLOEXEC);
+ if (push) {
+ pushfile();
+ parsefile->buf = 0;
+ }
+ parsefile->fd = fd;
+ if (parsefile->buf == NULL)
+ parsefile->buf = ckmalloc(IBUFSIZ);
+ parselleft = parsenleft = 0;
+ plinno = 1;
+}
+
+/*
+ * Set the input to take input from a file. If push is set, push the
+ * old input onto the stack first.
+ */
+static int
+setinputfile(const char *fname, int flags)
+{
+ int fd;
+ int fd2;
+
+ INT_OFF;
+ fd = open(fname, O_RDONLY);
+ if (fd < 0) {
+ if (flags & INPUT_NOFILE_OK)
+ goto out;
+ ash_msg_and_raise_error("can't open %s", fname);
+ }
+ if (fd < 10) {
+ fd2 = copyfd(fd, 10);
+ close(fd);
+ if (fd2 < 0)
+ ash_msg_and_raise_error("out of file descriptors");
+ fd = fd2;
+ }
+ setinputfd(fd, flags & INPUT_PUSH_FILE);
+ out:
+ INT_ON;
+ return fd;
+}
+
+/*
+ * Like setinputfile, but takes input from a string.
+ */
+static void
+setinputstring(char *string)
+{
+ INT_OFF;
+ pushfile();
+ parsenextc = string;
+ parsenleft = strlen(string);
+ parsefile->buf = NULL;
+ plinno = 1;
+ INT_ON;
+}
+
+
+/* ============ mail.c
+ *
+ * Routines to check for mail.
+ */
+
+#if ENABLE_ASH_MAIL
+
+#define MAXMBOXES 10
+
+/* times of mailboxes */
+static time_t mailtime[MAXMBOXES];
+/* Set if MAIL or MAILPATH is changed. */
+static int mail_var_path_changed;
+
+/*
+ * Print appropriate message(s) if mail has arrived.
+ * If mail_var_path_changed is set,
+ * then the value of MAIL has mail_var_path_changed,
+ * so we just update the values.
+ */
+static void
+chkmail(void)
+{
+ const char *mpath;
+ char *p;
+ char *q;
+ time_t *mtp;
+ struct stackmark smark;
+ struct stat statb;
+
+ setstackmark(&smark);
+ mpath = mpathset() ? mpathval() : mailval();
+ for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
+ p = padvance(&mpath, nullstr);
+ if (p == NULL)
+ break;
+ if (*p == '\0')
+ continue;
+ for (q = p; *q; q++);
+#if DEBUG
+ if (q[-1] != '/')
+ abort();
+#endif
+ q[-1] = '\0'; /* delete trailing '/' */
+ if (stat(p, &statb) < 0) {
+ *mtp = 0;
+ continue;
+ }
+ if (!mail_var_path_changed && statb.st_mtime != *mtp) {
+ fprintf(
+ stderr, snlfmt,
+ pathopt ? pathopt : "you have mail"
+ );
+ }
+ *mtp = statb.st_mtime;
+ }
+ mail_var_path_changed = 0;
+ popstackmark(&smark);
+}
+
+static void
+changemail(const char *val)
+{
+ mail_var_path_changed++;
+}
+
+#endif /* ASH_MAIL */
+
+
+/* ============ ??? */
+
+/*
+ * Set the shell parameters.
+ */
+static void
+setparam(char **argv)
+{
+ char **newparam;
+ char **ap;
+ int nparam;
+
+ for (nparam = 0; argv[nparam]; nparam++);
+ ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
+ while (*argv) {
+ *ap++ = ckstrdup(*argv++);
+ }
+ *ap = NULL;
+ freeparam(&shellparam);
+ shellparam.malloc = 1;
+ shellparam.nparam = nparam;
+ shellparam.p = newparam;
+#if ENABLE_ASH_GETOPTS
+ shellparam.optind = 1;
+ shellparam.optoff = -1;
+#endif
+}
+
+/*
+ * Process shell options. The global variable argptr contains a pointer
+ * to the argument list; we advance it past the options.
+ */
+static void
+minus_o(char *name, int val)
+{
+ int i;
+
+ if (name) {
+ for (i = 0; i < NOPTS; i++) {
+ if (strcmp(name, optnames(i)) == 0) {
+ optlist[i] = val;
+ return;
+ }
+ }
+ ash_msg_and_raise_error("illegal option -o %s", name);
+ }
+ out1str("Current option settings\n");
+ for (i = 0; i < NOPTS; i++)
+ out1fmt("%-16s%s\n", optnames(i),
+ optlist[i] ? "on" : "off");
+}
+static void
+setoption(int flag, int val)
+{
+ int i;
+
+ for (i = 0; i < NOPTS; i++) {
+ if (optletters(i) == flag) {
+ optlist[i] = val;
+ return;
+ }
+ }
+ ash_msg_and_raise_error("illegal option -%c", flag);
+ /* NOTREACHED */
+}
+static void
+options(int cmdline)
+{
+ char *p;
+ int val;
+ int c;
+
+ if (cmdline)
+ minusc = NULL;
+ while ((p = *argptr) != NULL) {
+ argptr++;
+ c = *p++;
+ if (c == '-') {
+ val = 1;
+ if (p[0] == '\0' || LONE_DASH(p)) {
+ if (!cmdline) {
+ /* "-" means turn off -x and -v */
+ if (p[0] == '\0')
+ xflag = vflag = 0;
+ /* "--" means reset params */
+ else if (*argptr == NULL)
+ setparam(argptr);
+ }
+ break; /* "-" or "--" terminates options */
+ }
+ } else if (c == '+') {
+ val = 0;
+ } else {
+ argptr--;
+ break;
+ }
+ while ((c = *p++) != '\0') {
+ if (c == 'c' && cmdline) {
+ minusc = p; /* command is after shell args*/
+ } else if (c == 'o') {
+ minus_o(*argptr, val);
+ if (*argptr)
+ argptr++;
+ } else if (cmdline && (c == '-')) { // long options
+ if (strcmp(p, "login") == 0)
+ isloginsh = 1;
+ break;
+ } else {
+ setoption(c, val);
+ }
+ }
+ }
+}
+
+/*
+ * The shift builtin command.
+ */
+static int
+shiftcmd(int argc, char **argv)
+{
+ int n;
+ char **ap1, **ap2;
+
+ n = 1;
+ if (argc > 1)
+ n = number(argv[1]);
+ if (n > shellparam.nparam)
+ ash_msg_and_raise_error("can't shift that many");
+ INT_OFF;
+ shellparam.nparam -= n;
+ for (ap1 = shellparam.p; --n >= 0; ap1++) {
+ if (shellparam.malloc)
+ free(*ap1);
+ }
+ ap2 = shellparam.p;
+ while ((*ap2++ = *ap1++) != NULL);
+#if ENABLE_ASH_GETOPTS
+ shellparam.optind = 1;
+ shellparam.optoff = -1;
+#endif
+ INT_ON;
+ return 0;
+}
+
+/*
+ * POSIX requires that 'set' (but not export or readonly) output the
+ * variables in lexicographic order - by the locale's collating order (sigh).
+ * Maybe we could keep them in an ordered balanced binary tree
+ * instead of hashed lists.
+ * For now just roll 'em through qsort for printing...
+ */
+static int
+showvars(const char *sep_prefix, int on, int off)
+{
+ const char *sep;
+ char **ep, **epend;
+
+ ep = listvars(on, off, &epend);
+ qsort(ep, epend - ep, sizeof(char *), vpcmp);
+
+ sep = *sep_prefix ? " " : sep_prefix;
+
+ for (; ep < epend; ep++) {
+ const char *p;
+ const char *q;
+
+ p = strchrnul(*ep, '=');
+ q = nullstr;
+ if (*p)
+ q = single_quote(++p);
+ out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
+ }
+ return 0;
+}
+
+/*
+ * The set command builtin.
+ */
+static int
+setcmd(int argc, char **argv)
+{
+ if (argc == 1)
+ return showvars(nullstr, 0, VUNSET);
+ INT_OFF;
+ options(0);
+ optschanged();
+ if (*argptr != NULL) {
+ setparam(argptr);
+ }
+ INT_ON;
+ return 0;
+}
+
+#if ENABLE_ASH_RANDOM_SUPPORT
+/* Roughly copied from bash.. */
+static void
+change_random(const char *value)
+{
+ if (value == NULL) {
+ /* "get", generate */
+ char buf[16];
+
+ rseed = rseed * 1103515245 + 12345;
+ sprintf(buf, "%d", (unsigned int)((rseed & 32767)));
+ /* set without recursion */
+ setvar(vrandom.text, buf, VNOFUNC);
+ vrandom.flags &= ~VNOFUNC;
+ } else {
+ /* set/reset */
+ rseed = strtoul(value, (char **)NULL, 10);
+ }
+}
+#endif
+
+#if ENABLE_ASH_GETOPTS
+static int
+getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
+{
+ char *p, *q;
+ char c = '?';
+ int done = 0;
+ int err = 0;
+ char s[12];
+ char **optnext;
+
+ if (*param_optind < 1)
+ return 1;
+ optnext = optfirst + *param_optind - 1;
+
+ if (*param_optind <= 1 || *optoff < 0 || strlen(optnext[-1]) < *optoff)
+ p = NULL;
+ else
+ p = optnext[-1] + *optoff;
+ if (p == NULL || *p == '\0') {
+ /* Current word is done, advance */
+ p = *optnext;
+ if (p == NULL || *p != '-' || *++p == '\0') {
+ atend:
+ p = NULL;
+ done = 1;
+ goto out;
+ }
+ optnext++;
+ if (LONE_DASH(p)) /* check for "--" */
+ goto atend;
+ }
+
+ c = *p++;
+ for (q = optstr; *q != c; ) {
+ if (*q == '\0') {
+ if (optstr[0] == ':') {
+ s[0] = c;
+ s[1] = '\0';
+ err |= setvarsafe("OPTARG", s, 0);
+ } else {
+ fprintf(stderr, "Illegal option -%c\n", c);
+ unsetvar("OPTARG");
+ }
+ c = '?';
+ goto out;
+ }
+ if (*++q == ':')
+ q++;
+ }
+
+ if (*++q == ':') {
+ if (*p == '\0' && (p = *optnext) == NULL) {
+ if (optstr[0] == ':') {
+ s[0] = c;
+ s[1] = '\0';
+ err |= setvarsafe("OPTARG", s, 0);
+ c = ':';
+ } else {
+ fprintf(stderr, "No arg for -%c option\n", c);
+ unsetvar("OPTARG");
+ c = '?';
+ }
+ goto out;
+ }
+
+ if (p == *optnext)
+ optnext++;
+ err |= setvarsafe("OPTARG", p, 0);
+ p = NULL;
+ } else
+ err |= setvarsafe("OPTARG", nullstr, 0);
+ out:
+ *optoff = p ? p - *(optnext - 1) : -1;
+ *param_optind = optnext - optfirst + 1;
+ fmtstr(s, sizeof(s), "%d", *param_optind);
+ err |= setvarsafe("OPTIND", s, VNOFUNC);
+ s[0] = c;
+ s[1] = '\0';
+ err |= setvarsafe(optvar, s, 0);
+ if (err) {
+ *param_optind = 1;
+ *optoff = -1;
+ flush_stdout_stderr();
+ raise_exception(EXERROR);
+ }
+ return done;
+}
+
+/*
+ * The getopts builtin. Shellparam.optnext points to the next argument
+ * to be processed. Shellparam.optptr points to the next character to
+ * be processed in the current argument. If shellparam.optnext is NULL,
+ * then it's the first time getopts has been called.
+ */
+static int
+getoptscmd(int argc, char **argv)
+{
+ char **optbase;
+
+ if (argc < 3)
+ ash_msg_and_raise_error("usage: getopts optstring var [arg]");
+ if (argc == 3) {
+ optbase = shellparam.p;
+ if (shellparam.optind > shellparam.nparam + 1) {
+ shellparam.optind = 1;
+ shellparam.optoff = -1;
+ }
+ } else {
+ optbase = &argv[3];
+ if (shellparam.optind > argc - 2) {
+ shellparam.optind = 1;
+ shellparam.optoff = -1;
+ }
+ }
+
+ return getopts(argv[1], argv[2], optbase, &shellparam.optind,
+ &shellparam.optoff);
+}
+#endif /* ASH_GETOPTS */
+
+
+/* ============ Shell parser */
+
+static int tokpushback; /* last token pushed back */
+#define NEOF ((union node *)&tokpushback)
+static int parsebackquote; /* nonzero if we are inside backquotes */
+static int lasttoken; /* last token read */
+static char *wordtext; /* text of last word returned by readtoken */
+static struct nodelist *backquotelist;
+static union node *redirnode;
+static struct heredoc *heredoc;
+static int quoteflag; /* set if (part of) last token was quoted */
+
+static void raise_error_syntax(const char *) ATTRIBUTE_NORETURN;
+static void
+raise_error_syntax(const char *msg)
+{
+ ash_msg_and_raise_error("syntax error: %s", msg);
+ /* NOTREACHED */
+}
+
+/*
+ * Called when an unexpected token is read during the parse. The argument
+ * is the token that is expected, or -1 if more than one type of token can
+ * occur at this point.
+ */
+static void raise_error_unexpected_syntax(int) ATTRIBUTE_NORETURN;
+static void
+raise_error_unexpected_syntax(int token)
+{
+ char msg[64];
+ int l;
+
+ l = sprintf(msg, "%s unexpected", tokname(lasttoken));
+ if (token >= 0)
+ sprintf(msg + l, " (expecting %s)", tokname(token));
+ raise_error_syntax(msg);
+ /* NOTREACHED */
+}
+
+#define EOFMARKLEN 79
+
+struct heredoc {
+ struct heredoc *next; /* next here document in list */
+ union node *here; /* redirection node */
+ char *eofmark; /* string indicating end of input */
+ int striptabs; /* if set, strip leading tabs */
+};
+
+static struct heredoc *heredoclist; /* list of here documents to read */
+
+/* parsing is heavily cross-recursive, need these forward decls */
+static union node *andor(void);
+static union node *pipeline(void);
+static union node *parse_command(void);
+static void parseheredoc(void);
+static char peektoken(void);
+static int readtoken(void);
+
+static union node *
+list(int nlflag)
+{
+ union node *n1, *n2, *n3;
+ int tok;
+
+ checkkwd = CHKNL | CHKKWD | CHKALIAS;
+ if (nlflag == 2 && peektoken())
+ return NULL;
+ n1 = NULL;
+ for (;;) {
+ n2 = andor();
+ tok = readtoken();
+ if (tok == TBACKGND) {
+ if (n2->type == NPIPE) {
+ n2->npipe.backgnd = 1;
+ } else {
+ if (n2->type != NREDIR) {
+ n3 = stalloc(sizeof(struct nredir));
+ n3->nredir.n = n2;
+ n3->nredir.redirect = NULL;
+ n2 = n3;
+ }
+ n2->type = NBACKGND;
+ }
+ }
+ if (n1 == NULL) {
+ n1 = n2;
+ } else {
+ n3 = stalloc(sizeof(struct nbinary));
+ n3->type = NSEMI;
+ n3->nbinary.ch1 = n1;
+ n3->nbinary.ch2 = n2;
+ n1 = n3;
+ }
+ switch (tok) {
+ case TBACKGND:
+ case TSEMI:
+ tok = readtoken();
+ /* fall through */
+ case TNL:
+ if (tok == TNL) {
+ parseheredoc();
+ if (nlflag == 1)
+ return n1;
+ } else {
+ tokpushback++;
+ }
+ checkkwd = CHKNL | CHKKWD | CHKALIAS;
+ if (peektoken())
+ return n1;
+ break;
+ case TEOF:
+ if (heredoclist)
+ parseheredoc();
+ else
+ pungetc(); /* push back EOF on input */
+ return n1;
+ default:
+ if (nlflag == 1)
+ raise_error_unexpected_syntax(-1);
+ tokpushback++;
+ return n1;
+ }
+ }
+}
+
+static union node *
+andor(void)
+{
+ union node *n1, *n2, *n3;
+ int t;
+
+ n1 = pipeline();
+ for (;;) {
+ t = readtoken();
+ if (t == TAND) {
+ t = NAND;
+ } else if (t == TOR) {
+ t = NOR;
+ } else {
+ tokpushback++;
+ return n1;
+ }
+ checkkwd = CHKNL | CHKKWD | CHKALIAS;
+ n2 = pipeline();
+ n3 = stalloc(sizeof(struct nbinary));
+ n3->type = t;
+ n3->nbinary.ch1 = n1;
+ n3->nbinary.ch2 = n2;
+ n1 = n3;
+ }
+}
+
+static union node *
+pipeline(void)
+{
+ union node *n1, *n2, *pipenode;
+ struct nodelist *lp, *prev;
+ int negate;
+
+ negate = 0;
+ TRACE(("pipeline: entered\n"));
+ if (readtoken() == TNOT) {
+ negate = !negate;
+ checkkwd = CHKKWD | CHKALIAS;
+ } else
+ tokpushback++;
+ n1 = parse_command();
+ if (readtoken() == TPIPE) {
+ pipenode = stalloc(sizeof(struct npipe));
+ pipenode->type = NPIPE;
+ pipenode->npipe.backgnd = 0;
+ lp = stalloc(sizeof(struct nodelist));
+ pipenode->npipe.cmdlist = lp;
+ lp->n = n1;
+ do {
+ prev = lp;
+ lp = stalloc(sizeof(struct nodelist));
+ checkkwd = CHKNL | CHKKWD | CHKALIAS;
+ lp->n = parse_command();
+ prev->next = lp;
+ } while (readtoken() == TPIPE);
+ lp->next = NULL;
+ n1 = pipenode;
+ }
+ tokpushback++;
+ if (negate) {
+ n2 = stalloc(sizeof(struct nnot));
+ n2->type = NNOT;
+ n2->nnot.com = n1;
+ return n2;
+ }
+ return n1;
+}
+
+static union node *
+makename(void)
+{
+ union node *n;
+
+ n = stalloc(sizeof(struct narg));
+ n->type = NARG;
+ n->narg.next = NULL;
+ n->narg.text = wordtext;
+ n->narg.backquote = backquotelist;
+ return n;
+}
+
+static void
+fixredir(union node *n, const char *text, int err)
+{
+ TRACE(("Fix redir %s %d\n", text, err));
+ if (!err)
+ n->ndup.vname = NULL;
+
+ if (isdigit(text[0]) && text[1] == '\0')
+ n->ndup.dupfd = text[0] - '0';
+ else if (LONE_DASH(text))
+ n->ndup.dupfd = -1;
+ else {
+ if (err)
+ raise_error_syntax("Bad fd number");
+ n->ndup.vname = makename();
+ }
+}
+
+/*
+ * Returns true if the text contains nothing to expand (no dollar signs
+ * or backquotes).
+ */
+static int
+noexpand(char *text)
+{
+ char *p;
+ char c;
+
+ p = text;
+ while ((c = *p++) != '\0') {
+ if (c == CTLQUOTEMARK)
+ continue;
+ if (c == CTLESC)
+ p++;
+ else if (SIT(c, BASESYNTAX) == CCTL)
+ return 0;
+ }
+ return 1;
+}
+
+static void
+parsefname(void)
+{
+ union node *n = redirnode;
+
+ if (readtoken() != TWORD)
+ raise_error_unexpected_syntax(-1);
+ if (n->type == NHERE) {
+ struct heredoc *here = heredoc;
+ struct heredoc *p;
+ int i;
+
+ if (quoteflag == 0)
+ n->type = NXHERE;
+ TRACE(("Here document %d\n", n->type));
+ if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
+ raise_error_syntax("Illegal eof marker for << redirection");
+ rmescapes(wordtext);
+ here->eofmark = wordtext;
+ here->next = NULL;
+ if (heredoclist == NULL)
+ heredoclist = here;
+ else {
+ for (p = heredoclist; p->next; p = p->next);
+ p->next = here;
+ }
+ } else if (n->type == NTOFD || n->type == NFROMFD) {
+ fixredir(n, wordtext, 0);
+ } else {
+ n->nfile.fname = makename();
+ }
+}
+
+static union node *
+simplecmd(void)
+{
+ union node *args, **app;
+ union node *n = NULL;
+ union node *vars, **vpp;
+ union node **rpp, *redir;
+ int savecheckkwd;
+
+ args = NULL;
+ app = &args;
+ vars = NULL;
+ vpp = &vars;
+ redir = NULL;
+ rpp = &redir;
+
+ savecheckkwd = CHKALIAS;
+ for (;;) {
+ checkkwd = savecheckkwd;
+ switch (readtoken()) {
+ case TWORD:
+ n = stalloc(sizeof(struct narg));
+ n->type = NARG;
+ n->narg.text = wordtext;
+ n->narg.backquote = backquotelist;
+ if (savecheckkwd && isassignment(wordtext)) {
+ *vpp = n;
+ vpp = &n->narg.next;
+ } else {
+ *app = n;
+ app = &n->narg.next;
+ savecheckkwd = 0;
+ }
+ break;
+ case TREDIR:
+ *rpp = n = redirnode;
+ rpp = &n->nfile.next;
+ parsefname(); /* read name of redirection file */
+ break;
+ case TLP:
+ if (args && app == &args->narg.next
+ && !vars && !redir
+ ) {
+ struct builtincmd *bcmd;
+ const char *name;
+
+ /* We have a function */
+ if (readtoken() != TRP)
+ raise_error_unexpected_syntax(TRP);
+ name = n->narg.text;
+ if (!goodname(name)
+ || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
+ ) {
+ raise_error_syntax("Bad function name");
+ }
+ n->type = NDEFUN;
+ checkkwd = CHKNL | CHKKWD | CHKALIAS;
+ n->narg.next = parse_command();
+ return n;
+ }
+ /* fall through */
+ default:
+ tokpushback++;
+ goto out;
+ }
+ }
+ out:
+ *app = NULL;
+ *vpp = NULL;
+ *rpp = NULL;
+ n = stalloc(sizeof(struct ncmd));
+ n->type = NCMD;
+ n->ncmd.args = args;
+ n->ncmd.assign = vars;
+ n->ncmd.redirect = redir;
+ return n;
+}
+
+static union node *
+parse_command(void)
+{
+ union node *n1, *n2;
+ union node *ap, **app;
+ union node *cp, **cpp;
+ union node *redir, **rpp;
+ union node **rpp2;
+ int t;
+
+ redir = NULL;
+ rpp2 = &redir;
+
+ switch (readtoken()) {
+ default:
+ raise_error_unexpected_syntax(-1);
+ /* NOTREACHED */
+ case TIF:
+ n1 = stalloc(sizeof(struct nif));
+ n1->type = NIF;
+ n1->nif.test = list(0);
+ if (readtoken() != TTHEN)
+ raise_error_unexpected_syntax(TTHEN);
+ n1->nif.ifpart = list(0);
+ n2 = n1;
+ while (readtoken() == TELIF) {
+ n2->nif.elsepart = stalloc(sizeof(struct nif));
+ n2 = n2->nif.elsepart;
+ n2->type = NIF;
+ n2->nif.test = list(0);
+ if (readtoken() != TTHEN)
+ raise_error_unexpected_syntax(TTHEN);
+ n2->nif.ifpart = list(0);
+ }
+ if (lasttoken == TELSE)
+ n2->nif.elsepart = list(0);
+ else {
+ n2->nif.elsepart = NULL;
+ tokpushback++;
+ }
+ t = TFI;
+ break;
+ case TWHILE:
+ case TUNTIL: {
+ int got;
+ n1 = stalloc(sizeof(struct nbinary));
+ n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
+ n1->nbinary.ch1 = list(0);
+ got = readtoken();
+ if (got != TDO) {
+ TRACE(("expecting DO got %s %s\n", tokname(got),
+ got == TWORD ? wordtext : ""));
+ raise_error_unexpected_syntax(TDO);
+ }
+ n1->nbinary.ch2 = list(0);
+ t = TDONE;
+ break;
+ }
+ case TFOR:
+ if (readtoken() != TWORD || quoteflag || ! goodname(wordtext))
+ raise_error_syntax("Bad for loop variable");
+ n1 = stalloc(sizeof(struct nfor));
+ n1->type = NFOR;
+ n1->nfor.var = wordtext;
+ checkkwd = CHKKWD | CHKALIAS;
+ if (readtoken() == TIN) {
+ app = &ap;
+ while (readtoken() == TWORD) {
+ n2 = stalloc(sizeof(struct narg));
+ n2->type = NARG;
+ n2->narg.text = wordtext;
+ n2->narg.backquote = backquotelist;
+ *app = n2;
+ app = &n2->narg.next;
+ }
+ *app = NULL;
+ n1->nfor.args = ap;
+ if (lasttoken != TNL && lasttoken != TSEMI)
+ raise_error_unexpected_syntax(-1);
+ } else {
+ n2 = stalloc(sizeof(struct narg));
+ n2->type = NARG;
+ n2->narg.text = (char *)dolatstr;
+ n2->narg.backquote = NULL;
+ n2->narg.next = NULL;
+ n1->nfor.args = n2;
+ /*
+ * Newline or semicolon here is optional (but note
+ * that the original Bourne shell only allowed NL).
+ */
+ if (lasttoken != TNL && lasttoken != TSEMI)
+ tokpushback++;
+ }
+ checkkwd = CHKNL | CHKKWD | CHKALIAS;
+ if (readtoken() != TDO)
+ raise_error_unexpected_syntax(TDO);
+ n1->nfor.body = list(0);
+ t = TDONE;
+ break;
+ case TCASE:
+ n1 = stalloc(sizeof(struct ncase));
+ n1->type = NCASE;
+ if (readtoken() != TWORD)
+ raise_error_unexpected_syntax(TWORD);
+ n1->ncase.expr = n2 = stalloc(sizeof(struct narg));
+ n2->type = NARG;
+ n2->narg.text = wordtext;
+ n2->narg.backquote = backquotelist;
+ n2->narg.next = NULL;
+ do {
+ checkkwd = CHKKWD | CHKALIAS;
+ } while (readtoken() == TNL);
+ if (lasttoken != TIN)
+ raise_error_unexpected_syntax(TIN);
+ cpp = &n1->ncase.cases;
+ next_case:
+ checkkwd = CHKNL | CHKKWD;
+ t = readtoken();
+ while (t != TESAC) {
+ if (lasttoken == TLP)
+ readtoken();
+ *cpp = cp = stalloc(sizeof(struct nclist));
+ cp->type = NCLIST;
+ app = &cp->nclist.pattern;
+ for (;;) {
+ *app = ap = stalloc(sizeof(struct narg));
+ ap->type = NARG;
+ ap->narg.text = wordtext;
+ ap->narg.backquote = backquotelist;
+ if (readtoken() != TPIPE)
+ break;
+ app = &ap->narg.next;
+ readtoken();
+ }
+ ap->narg.next = NULL;
+ if (lasttoken != TRP)
+ raise_error_unexpected_syntax(TRP);
+ cp->nclist.body = list(2);
+
+ cpp = &cp->nclist.next;
+
+ checkkwd = CHKNL | CHKKWD;
+ t = readtoken();
+ if (t != TESAC) {
+ if (t != TENDCASE)
+ raise_error_unexpected_syntax(TENDCASE);
+ goto next_case;
+ }
+ }
+ *cpp = NULL;
+ goto redir;
+ case TLP:
+ n1 = stalloc(sizeof(struct nredir));
+ n1->type = NSUBSHELL;
+ n1->nredir.n = list(0);
+ n1->nredir.redirect = NULL;
+ t = TRP;
+ break;
+ case TBEGIN:
+ n1 = list(0);
+ t = TEND;
+ break;
+ case TWORD:
+ case TREDIR:
+ tokpushback++;
+ return simplecmd();
+ }
+
+ if (readtoken() != t)
+ raise_error_unexpected_syntax(t);
+
+ redir:
+ /* Now check for redirection which may follow command */
+ checkkwd = CHKKWD | CHKALIAS;
+ rpp = rpp2;
+ while (readtoken() == TREDIR) {
+ *rpp = n2 = redirnode;
+ rpp = &n2->nfile.next;
+ parsefname();
+ }
+ tokpushback++;
+ *rpp = NULL;
+ if (redir) {
+ if (n1->type != NSUBSHELL) {
+ n2 = stalloc(sizeof(struct nredir));
+ n2->type = NREDIR;
+ n2->nredir.n = n1;
+ n1 = n2;
+ }
+ n1->nredir.redirect = redir;
+ }
+ return n1;
+}
+
+/*
+ * If eofmark is NULL, read a word or a redirection symbol. If eofmark
+ * is not NULL, read a here document. In the latter case, eofmark is the
+ * word which marks the end of the document and striptabs is true if
+ * leading tabs should be stripped from the document. The argument firstc
+ * is the first character of the input token or document.
+ *
+ * Because C does not have internal subroutines, I have simulated them
+ * using goto's to implement the subroutine linkage. The following macros
+ * will run code that appears at the end of readtoken1.
+ */
+
+static int parsebackquote; /* nonzero if we are inside backquotes */
+
+#define CHECKEND() {goto checkend; checkend_return:;}
+#define PARSEREDIR() {goto parseredir; parseredir_return:;}
+#define PARSESUB() {goto parsesub; parsesub_return:;}
+#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
+#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
+#define PARSEARITH() {goto parsearith; parsearith_return:;}
+
+static int
+readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
+{
+ int c = firstc;
+ char *out;
+ int len;
+ char line[EOFMARKLEN + 1];
+ struct nodelist *bqlist = 0;
+ int quotef = 0;
+ int dblquote = 0;
+ int varnest = 0; /* levels of variables expansion */
+ int arinest = 0; /* levels of arithmetic expansion */
+ int parenlevel = 0; /* levels of parens in arithmetic */
+ int dqvarnest = 0; /* levels of variables expansion within double quotes */
+ int oldstyle = 0;
+ int prevsyntax = 0; /* syntax before arithmetic */
+#if __GNUC__
+ /* Avoid longjmp clobbering */
+ (void) &out;
+ (void) &quotef;
+ (void) &dblquote;
+ (void) &varnest;
+ (void) &arinest;
+ (void) &parenlevel;
+ (void) &dqvarnest;
+ (void) &oldstyle;
+ (void) &prevsyntax;
+ (void) &syntax;
+#endif
+
+ startlinno = plinno;
+ dblquote = 0;
+ if (syntax == DQSYNTAX)
+ dblquote = 1;
+ quotef = 0;
+ bqlist = NULL;
+ varnest = 0;
+ arinest = 0;
+ parenlevel = 0;
+ dqvarnest = 0;
+
+ STARTSTACKSTR(out);
+ loop: { /* for each line, until end of word */
+ CHECKEND(); /* set c to PEOF if at end of here document */
+ for (;;) { /* until end of line or end of word */
+ CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
+ switch (SIT(c, syntax)) {
+ case CNL: /* '\n' */
+ if (syntax == BASESYNTAX)
+ goto endword; /* exit outer loop */
+ USTPUTC(c, out);
+ plinno++;
+ if (doprompt)
+ setprompt(2);
+ c = pgetc();
+ goto loop; /* continue outer loop */
+ case CWORD:
+ USTPUTC(c, out);
+ break;
+ case CCTL:
+ if (eofmark == NULL || dblquote)
+ USTPUTC(CTLESC, out);
+ USTPUTC(c, out);
+ break;
+ case CBACK: /* backslash */
+ c = pgetc2();
+ if (c == PEOF) {
+ USTPUTC(CTLESC, out);
+ USTPUTC('\\', out);
+ pungetc();
+ } else if (c == '\n') {
+ if (doprompt)
+ setprompt(2);
+ } else {
+ if (dblquote &&
+ c != '\\' && c != '`' &&
+ c != '$' && (
+ c != '"' ||
+ eofmark != NULL)
+ ) {
+ USTPUTC(CTLESC, out);
+ USTPUTC('\\', out);
+ }
+ if (SIT(c, SQSYNTAX) == CCTL)
+ USTPUTC(CTLESC, out);
+ USTPUTC(c, out);
+ quotef++;
+ }
+ break;
+ case CSQUOTE:
+ syntax = SQSYNTAX;
+ quotemark:
+ if (eofmark == NULL) {
+ USTPUTC(CTLQUOTEMARK, out);
+ }
+ break;
+ case CDQUOTE:
+ syntax = DQSYNTAX;
+ dblquote = 1;
+ goto quotemark;
+ case CENDQUOTE:
+ if (eofmark != NULL && arinest == 0
+ && varnest == 0
+ ) {
+ USTPUTC(c, out);
+ } else {
+ if (dqvarnest == 0) {
+ syntax = BASESYNTAX;
+ dblquote = 0;
+ }
+ quotef++;
+ goto quotemark;
+ }
+ break;
+ case CVAR: /* '$' */
+ PARSESUB(); /* parse substitution */
+ break;
+ case CENDVAR: /* '}' */
+ if (varnest > 0) {
+ varnest--;
+ if (dqvarnest > 0) {
+ dqvarnest--;
+ }
+ USTPUTC(CTLENDVAR, out);
+ } else {
+ USTPUTC(c, out);
+ }
+ break;
+#if ENABLE_ASH_MATH_SUPPORT
+ case CLP: /* '(' in arithmetic */
+ parenlevel++;
+ USTPUTC(c, out);
+ break;
+ case CRP: /* ')' in arithmetic */
+ if (parenlevel > 0) {
+ USTPUTC(c, out);
+ --parenlevel;
+ } else {
+ if (pgetc() == ')') {
+ if (--arinest == 0) {
+ USTPUTC(CTLENDARI, out);
+ syntax = prevsyntax;
+ if (syntax == DQSYNTAX)
+ dblquote = 1;
+ else
+ dblquote = 0;
+ } else
+ USTPUTC(')', out);
+ } else {
+ /*
+ * unbalanced parens
+ * (don't 2nd guess - no error)
+ */
+ pungetc();
+ USTPUTC(')', out);
+ }
+ }
+ break;
+#endif
+ case CBQUOTE: /* '`' */
+ PARSEBACKQOLD();
+ break;
+ case CENDFILE:
+ goto endword; /* exit outer loop */
+ case CIGN:
+ break;
+ default:
+ if (varnest == 0)
+ goto endword; /* exit outer loop */
+#if ENABLE_ASH_ALIAS
+ if (c != PEOA)
+#endif
+ USTPUTC(c, out);
+
+ }
+ c = pgetc_macro();
+ }
+ }
+ endword:
+#if ENABLE_ASH_MATH_SUPPORT
+ if (syntax == ARISYNTAX)
+ raise_error_syntax("Missing '))'");
+#endif
+ if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
+ raise_error_syntax("Unterminated quoted string");
+ if (varnest != 0) {
+ startlinno = plinno;
+ /* { */
+ raise_error_syntax("Missing '}'");
+ }
+ USTPUTC('\0', out);
+ len = out - (char *)stackblock();
+ out = stackblock();
+ if (eofmark == NULL) {
+ if ((c == '>' || c == '<')
+ && quotef == 0
+ && len <= 2
+ && (*out == '\0' || isdigit(*out))) {
+ PARSEREDIR();
+ return lasttoken = TREDIR;
+ } else {
+ pungetc();
+ }
+ }
+ quoteflag = quotef;
+ backquotelist = bqlist;
+ grabstackblock(len);
+ wordtext = out;
+ lasttoken = TWORD;
+ return lasttoken;
+/* end of readtoken routine */
+
+/*
+ * Check to see whether we are at the end of the here document. When this
+ * is called, c is set to the first character of the next input line. If
+ * we are at the end of the here document, this routine sets the c to PEOF.
+ */
+checkend: {
+ if (eofmark) {
+#if ENABLE_ASH_ALIAS
+ if (c == PEOA) {
+ c = pgetc2();
+ }
+#endif
+ if (striptabs) {
+ while (c == '\t') {
+ c = pgetc2();
+ }
+ }
+ if (c == *eofmark) {
+ if (pfgets(line, sizeof(line)) != NULL) {
+ char *p, *q;
+
+ p = line;
+ for (q = eofmark + 1; *q && *p == *q; p++, q++);
+ if (*p == '\n' && *q == '\0') {
+ c = PEOF;
+ plinno++;
+ needprompt = doprompt;
+ } else {
+ pushstring(line, NULL);
+ }
+ }
+ }
+ }
+ goto checkend_return;
+}
+
+/*
+ * Parse a redirection operator. The variable "out" points to a string
+ * specifying the fd to be redirected. The variable "c" contains the
+ * first character of the redirection operator.
+ */
+parseredir: {
+ char fd = *out;
+ union node *np;
+
+ np = stalloc(sizeof(struct nfile));
+ if (c == '>') {
+ np->nfile.fd = 1;
+ c = pgetc();
+ if (c == '>')
+ np->type = NAPPEND;
+ else if (c == '|')
+ np->type = NCLOBBER;
+ else if (c == '&')
+ np->type = NTOFD;
+ else {
+ np->type = NTO;
+ pungetc();
+ }
+ } else { /* c == '<' */
+ np->nfile.fd = 0;
+ c = pgetc();
+ switch (c) {
+ case '<':
+ if (sizeof(struct nfile) != sizeof(struct nhere)) {
+ np = stalloc(sizeof(struct nhere));
+ np->nfile.fd = 0;
+ }
+ np->type = NHERE;
+ heredoc = stalloc(sizeof(struct heredoc));
+ heredoc->here = np;
+ c = pgetc();
+ if (c == '-') {
+ heredoc->striptabs = 1;
+ } else {
+ heredoc->striptabs = 0;
+ pungetc();
+ }
+ break;
+
+ case '&':
+ np->type = NFROMFD;
+ break;
+
+ case '>':
+ np->type = NFROMTO;
+ break;
+
+ default:
+ np->type = NFROM;
+ pungetc();
+ break;
+ }
+ }
+ if (fd != '\0')
+ np->nfile.fd = fd - '0';
+ redirnode = np;
+ goto parseredir_return;
+}
+
+/*
+ * Parse a substitution. At this point, we have read the dollar sign
+ * and nothing else.
+ */
+
+/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
+ * (assuming ascii char codes, as the original implementation did) */
+#define is_special(c) \
+ ((((unsigned int)c) - 33 < 32) \
+ && ((0xc1ff920dUL >> (((unsigned int)c) - 33)) & 1))
+parsesub: {
+ int subtype;
+ int typeloc;
+ int flags;
+ char *p;
+ static const char types[] = "}-+?=";
+
+ c = pgetc();
+ if (
+ c <= PEOA_OR_PEOF ||
+ (c != '(' && c != '{' && !is_name(c) && !is_special(c))
+ ) {
+ USTPUTC('$', out);
+ pungetc();
+ } else if (c == '(') { /* $(command) or $((arith)) */
+ if (pgetc() == '(') {
+#if ENABLE_ASH_MATH_SUPPORT
+ PARSEARITH();
+#else
+ raise_error_syntax("We unsupport $((arith))");
+#endif
+ } else {
+ pungetc();
+ PARSEBACKQNEW();
+ }
+ } else {
+ USTPUTC(CTLVAR, out);
+ typeloc = out - (char *)stackblock();
+ USTPUTC(VSNORMAL, out);
+ subtype = VSNORMAL;
+ if (c == '{') {
+ c = pgetc();
+ if (c == '#') {
+ c = pgetc();
+ if (c == '}')
+ c = '#';
+ else
+ subtype = VSLENGTH;
+ } else
+ subtype = 0;
+ }
+ if (c > PEOA_OR_PEOF && is_name(c)) {
+ do {
+ STPUTC(c, out);
+ c = pgetc();
+ } while (c > PEOA_OR_PEOF && is_in_name(c));
+ } else if (isdigit(c)) {
+ do {
+ STPUTC(c, out);
+ c = pgetc();
+ } while (isdigit(c));
+ } else if (is_special(c)) {
+ USTPUTC(c, out);
+ c = pgetc();
+ } else
+ badsub: raise_error_syntax("Bad substitution");
+
+ STPUTC('=', out);
+ flags = 0;
+ if (subtype == 0) {
+ switch (c) {
+ case ':':
+ flags = VSNUL;
+ c = pgetc();
+ /*FALLTHROUGH*/
+ default:
+ p = strchr(types, c);
+ if (p == NULL)
+ goto badsub;
+ subtype = p - types + VSNORMAL;
+ break;
+ case '%':
+ case '#':
+ {
+ int cc = c;
+ subtype = c == '#' ? VSTRIMLEFT :
+ VSTRIMRIGHT;
+ c = pgetc();
+ if (c == cc)
+ subtype++;
+ else
+ pungetc();
+ break;
+ }
+ }
+ } else {
+ pungetc();
+ }
+ if (dblquote || arinest)
+ flags |= VSQUOTE;
+ *((char *)stackblock() + typeloc) = subtype | flags;
+ if (subtype != VSNORMAL) {
+ varnest++;
+ if (dblquote || arinest) {
+ dqvarnest++;
+ }
+ }
+ }
+ goto parsesub_return;
+}
+
+/*
+ * Called to parse command substitutions. Newstyle is set if the command
+ * is enclosed inside $(...); nlpp is a pointer to the head of the linked
+ * list of commands (passed by reference), and savelen is the number of
+ * characters on the top of the stack which must be preserved.
+ */
+parsebackq: {
+ struct nodelist **nlpp;
+ int savepbq;
+ union node *n;
+ char *volatile str;
+ struct jmploc jmploc;
+ struct jmploc *volatile savehandler;
+ size_t savelen;
+ int saveprompt = 0;
+#ifdef __GNUC__
+ (void) &saveprompt;
+#endif
+
+ savepbq = parsebackquote;
+ if (setjmp(jmploc.loc)) {
+ if (str)
+ free(str);
+ parsebackquote = 0;
+ exception_handler = savehandler;
+ longjmp(exception_handler->loc, 1);
+ }
+ INT_OFF;
+ str = NULL;
+ savelen = out - (char *)stackblock();
+ if (savelen > 0) {
+ str = ckmalloc(savelen);
+ memcpy(str, stackblock(), savelen);
+ }
+ savehandler = exception_handler;
+ exception_handler = &jmploc;
+ INT_ON;
+ if (oldstyle) {
+ /* We must read until the closing backquote, giving special
+ treatment to some slashes, and then push the string and
+ reread it as input, interpreting it normally. */
+ char *pout;
+ int pc;
+ size_t psavelen;
+ char *pstr;
+
+
+ STARTSTACKSTR(pout);
+ for (;;) {
+ if (needprompt) {
+ setprompt(2);
+ }
+ pc = pgetc();
+ switch (pc) {
+ case '`':
+ goto done;
+
+ case '\\':
+ pc = pgetc();
+ if (pc == '\n') {
+ plinno++;
+ if (doprompt)
+ setprompt(2);
+ /*
+ * If eating a newline, avoid putting
+ * the newline into the new character
+ * stream (via the STPUTC after the
+ * switch).
+ */
+ continue;
+ }
+ if (pc != '\\' && pc != '`' && pc != '$'
+ && (!dblquote || pc != '"'))
+ STPUTC('\\', pout);
+ if (pc > PEOA_OR_PEOF) {
+ break;
+ }
+ /* fall through */
+
+ case PEOF:
+#if ENABLE_ASH_ALIAS
+ case PEOA:
+#endif
+ startlinno = plinno;
+ raise_error_syntax("EOF in backquote substitution");
+
+ case '\n':
+ plinno++;
+ needprompt = doprompt;
+ break;
+
+ default:
+ break;
+ }
+ STPUTC(pc, pout);
+ }
+ done:
+ STPUTC('\0', pout);
+ psavelen = pout - (char *)stackblock();
+ if (psavelen > 0) {
+ pstr = grabstackstr(pout);
+ setinputstring(pstr);
+ }
+ }
+ nlpp = &bqlist;
+ while (*nlpp)
+ nlpp = &(*nlpp)->next;
+ *nlpp = stalloc(sizeof(**nlpp));
+ (*nlpp)->next = NULL;
+ parsebackquote = oldstyle;
+
+ if (oldstyle) {
+ saveprompt = doprompt;
+ doprompt = 0;
+ }
+
+ n = list(2);
+
+ if (oldstyle)
+ doprompt = saveprompt;
+ else if (readtoken() != TRP)
+ raise_error_unexpected_syntax(TRP);
+
+ (*nlpp)->n = n;
+ if (oldstyle) {
+ /*
+ * Start reading from old file again, ignoring any pushed back
+ * tokens left from the backquote parsing
+ */
+ popfile();
+ tokpushback = 0;
+ }
+ while (stackblocksize() <= savelen)
+ growstackblock();
+ STARTSTACKSTR(out);
+ if (str) {
+ memcpy(out, str, savelen);
+ STADJUST(savelen, out);
+ INT_OFF;
+ free(str);
+ str = NULL;
+ INT_ON;
+ }
+ parsebackquote = savepbq;
+ exception_handler = savehandler;
+ if (arinest || dblquote)
+ USTPUTC(CTLBACKQ | CTLQUOTE, out);
+ else
+ USTPUTC(CTLBACKQ, out);
+ if (oldstyle)
+ goto parsebackq_oldreturn;
+ goto parsebackq_newreturn;
+}
+
+#if ENABLE_ASH_MATH_SUPPORT
+/*
+ * Parse an arithmetic expansion (indicate start of one and set state)
+ */
+parsearith: {
+ if (++arinest == 1) {
+ prevsyntax = syntax;
+ syntax = ARISYNTAX;
+ USTPUTC(CTLARI, out);
+ if (dblquote)
+ USTPUTC('"', out);
+ else
+ USTPUTC(' ', out);
+ } else {
+ /*
+ * we collapse embedded arithmetic expansion to
+ * parenthesis, which should be equivalent
+ */
+ USTPUTC('(', out);
+ }
+ goto parsearith_return;
+}
+#endif
+
+} /* end of readtoken */
+
+/*
+ * Read the next input token.
+ * If the token is a word, we set backquotelist to the list of cmds in
+ * backquotes. We set quoteflag to true if any part of the word was
+ * quoted.
+ * If the token is TREDIR, then we set redirnode to a structure containing
+ * the redirection.
+ * In all cases, the variable startlinno is set to the number of the line
+ * on which the token starts.
+ *
+ * [Change comment: here documents and internal procedures]
+ * [Readtoken shouldn't have any arguments. Perhaps we should make the
+ * word parsing code into a separate routine. In this case, readtoken
+ * doesn't need to have any internal procedures, but parseword does.
+ * We could also make parseoperator in essence the main routine, and
+ * have parseword (readtoken1?) handle both words and redirection.]
+ */
+#define NEW_xxreadtoken
+#ifdef NEW_xxreadtoken
+/* singles must be first! */
+static const char xxreadtoken_chars[7] = { '\n', '(', ')', '&', '|', ';', 0 };
+
+static const char xxreadtoken_tokens[] = {
+ TNL, TLP, TRP, /* only single occurrence allowed */
+ TBACKGND, TPIPE, TSEMI, /* if single occurrence */
+ TEOF, /* corresponds to trailing nul */
+ TAND, TOR, TENDCASE, /* if double occurrence */
+};
+
+#define xxreadtoken_doubles \
+ (sizeof(xxreadtoken_tokens) - sizeof(xxreadtoken_chars))
+#define xxreadtoken_singles \
+ (sizeof(xxreadtoken_chars) - xxreadtoken_doubles - 1)
+
+static int
+xxreadtoken(void)
+{
+ int c;
+
+ if (tokpushback) {
+ tokpushback = 0;
+ return lasttoken;
+ }
+ if (needprompt) {
+ setprompt(2);
+ }
+ startlinno = plinno;
+ for (;;) { /* until token or start of word found */
+ c = pgetc_macro();
+
+ if ((c != ' ') && (c != '\t')
+#if ENABLE_ASH_ALIAS
+ && (c != PEOA)
+#endif
+ ) {
+ if (c == '#') {
+ while ((c = pgetc()) != '\n' && c != PEOF);
+ pungetc();
+ } else if (c == '\\') {
+ if (pgetc() != '\n') {
+ pungetc();
+ goto READTOKEN1;
+ }
+ startlinno = ++plinno;
+ if (doprompt)
+ setprompt(2);
+ } else {
+ const char *p
+ = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
+
+ if (c != PEOF) {
+ if (c == '\n') {
+ plinno++;
+ needprompt = doprompt;
+ }
+
+ p = strchr(xxreadtoken_chars, c);
+ if (p == NULL) {
+ READTOKEN1:
+ return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
+ }
+
+ if (p - xxreadtoken_chars >= xxreadtoken_singles) {
+ if (pgetc() == *p) { /* double occurrence? */
+ p += xxreadtoken_doubles + 1;
+ } else {
+ pungetc();
+ }
+ }
+ }
+ return lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
+ }
+ }
+ } /* for */
+}
+#else
+#define RETURN(token) return lasttoken = token
+static int
+xxreadtoken(void)
+{
+ int c;
+
+ if (tokpushback) {
+ tokpushback = 0;
+ return lasttoken;
+ }
+ if (needprompt) {
+ setprompt(2);
+ }
+ startlinno = plinno;
+ for (;;) { /* until token or start of word found */
+ c = pgetc_macro();
+ switch (c) {
+ case ' ': case '\t':
+#if ENABLE_ASH_ALIAS
+ case PEOA:
+#endif
+ continue;
+ case '#':
+ while ((c = pgetc()) != '\n' && c != PEOF);
+ pungetc();
+ continue;
+ case '\\':
+ if (pgetc() == '\n') {
+ startlinno = ++plinno;
+ if (doprompt)
+ setprompt(2);
+ continue;
+ }
+ pungetc();
+ goto breakloop;
+ case '\n':
+ plinno++;
+ needprompt = doprompt;
+ RETURN(TNL);
+ case PEOF:
+ RETURN(TEOF);
+ case '&':
+ if (pgetc() == '&')
+ RETURN(TAND);
+ pungetc();
+ RETURN(TBACKGND);
+ case '|':
+ if (pgetc() == '|')
+ RETURN(TOR);
+ pungetc();
+ RETURN(TPIPE);
+ case ';':
+ if (pgetc() == ';')
+ RETURN(TENDCASE);
+ pungetc();
+ RETURN(TSEMI);
+ case '(':
+ RETURN(TLP);
+ case ')':
+ RETURN(TRP);
+ default:
+ goto breakloop;
+ }
+ }
+ breakloop:
+ return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
+#undef RETURN
+}
+#endif /* NEW_xxreadtoken */
+
+static int
+readtoken(void)
+{
+ int t;
+#if DEBUG
+ int alreadyseen = tokpushback;
+#endif
+
+#if ENABLE_ASH_ALIAS
+ top:
+#endif
+
+ t = xxreadtoken();
+
+ /*
+ * eat newlines
+ */
+ if (checkkwd & CHKNL) {
+ while (t == TNL) {
+ parseheredoc();
+ t = xxreadtoken();
+ }
+ }
+
+ if (t != TWORD || quoteflag) {
+ goto out;
+ }
+
+ /*
+ * check for keywords
+ */
+ if (checkkwd & CHKKWD) {
+ const char *const *pp;
+
+ pp = findkwd(wordtext);
+ if (pp) {
+ lasttoken = t = pp - tokname_array;
+ TRACE(("keyword %s recognized\n", tokname(t)));
+ goto out;
+ }
+ }
+
+ if (checkkwd & CHKALIAS) {
+#if ENABLE_ASH_ALIAS
+ struct alias *ap;
+ ap = lookupalias(wordtext, 1);
+ if (ap != NULL) {
+ if (*ap->val) {
+ pushstring(ap->val, ap);
+ }
+ goto top;
+ }
+#endif
+ }
+ out:
+ checkkwd = 0;
+#if DEBUG
+ if (!alreadyseen)
+ TRACE(("token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
+ else
+ TRACE(("reread token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
+#endif
+ return t;
+}
+
+static char
+peektoken(void)
+{
+ int t;
+
+ t = readtoken();
+ tokpushback++;
+ return tokname_array[t][0];
+}
+
+/*
+ * Read and parse a command. Returns NEOF on end of file. (NULL is a
+ * valid parse tree indicating a blank line.)
+ */
+static union node *
+parsecmd(int interact)
+{
+ int t;
+
+ tokpushback = 0;
+ doprompt = interact;
+ if (doprompt)
+ setprompt(doprompt);
+ needprompt = 0;
+ t = readtoken();
+ if (t == TEOF)
+ return NEOF;
+ if (t == TNL)
+ return NULL;
+ tokpushback++;
+ return list(1);
+}
+
+/*
+ * Input any here documents.
+ */
+static void
+parseheredoc(void)
+{
+ struct heredoc *here;
+ union node *n;
+
+ here = heredoclist;
+ heredoclist = 0;
+
+ while (here) {
+ if (needprompt) {
+ setprompt(2);
+ }
+ readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
+ here->eofmark, here->striptabs);
+ n = stalloc(sizeof(struct narg));
+ n->narg.type = NARG;
+ n->narg.next = NULL;
+ n->narg.text = wordtext;
+ n->narg.backquote = backquotelist;
+ here->here->nhere.doc = n;
+ here = here->next;
+ }
+}
+
+
+/*
+ * called by editline -- any expansions to the prompt should be added here.
+ */
+#if ENABLE_ASH_EXPAND_PRMT
+static const char *
+expandstr(const char *ps)
+{
+ union node n;
+
+ /* XXX Fix (char *) cast. */
+ setinputstring((char *)ps);
+ readtoken1(pgetc(), DQSYNTAX, nullstr, 0);
+ popfile();
+
+ n.narg.type = NARG;
+ n.narg.next = NULL;
+ n.narg.text = wordtext;
+ n.narg.backquote = backquotelist;
+
+ expandarg(&n, NULL, 0);
+ return stackblock();
+}
+#endif
+
+/*
+ * Execute a command or commands contained in a string.
+ */
+static int
+evalstring(char *s, int mask)
+{
+ union node *n;
+ struct stackmark smark;
+ int skip;
+
+ setinputstring(s);
+ setstackmark(&smark);
+
+ skip = 0;
+ while ((n = parsecmd(0)) != NEOF) {
+ evaltree(n, 0);
+ popstackmark(&smark);
+ skip = evalskip;
+ if (skip)
+ break;
+ }
+ popfile();
+
+ skip &= mask;
+ evalskip = skip;
+ return skip;
+}
+
+/*
+ * The eval command.
+ */
+static int
+evalcmd(int argc, char **argv)
+{
+ char *p;
+ char *concat;
+ char **ap;
+
+ if (argc > 1) {
+ p = argv[1];
+ if (argc > 2) {
+ STARTSTACKSTR(concat);
+ ap = argv + 2;
+ for (;;) {
+ concat = stack_putstr(p, concat);
+ p = *ap++;
+ if (p == NULL)
+ break;
+ STPUTC(' ', concat);
+ }
+ STPUTC('\0', concat);
+ p = grabstackstr(concat);
+ }
+ evalstring(p, ~SKIPEVAL);
+
+ }
+ return exitstatus;
+}
+
+/*
+ * Read and execute commands. "Top" is nonzero for the top level command
+ * loop; it turns on prompting if the shell is interactive.
+ */
+static int
+cmdloop(int top)
+{
+ union node *n;
+ struct stackmark smark;
+ int inter;
+ int numeof = 0;
+
+ TRACE(("cmdloop(%d) called\n", top));
+ for (;;) {
+ int skip;
+
+ setstackmark(&smark);
+#if JOBS
+ if (jobctl)
+ showjobs(stderr, SHOW_CHANGED);
+#endif
+ inter = 0;
+ if (iflag && top) {
+ inter++;
+#if ENABLE_ASH_MAIL
+ chkmail();
+#endif
+ }
+ n = parsecmd(inter);
+ /* showtree(n); DEBUG */
+ if (n == NEOF) {
+ if (!top || numeof >= 50)
+ break;
+ if (!stoppedjobs()) {
+ if (!Iflag)
+ break;
+ out2str("\nUse \"exit\" to leave shell.\n");
+ }
+ numeof++;
+ } else if (nflag == 0) {
+ job_warning = (job_warning == 2) ? 1 : 0;
+ numeof = 0;
+ evaltree(n, 0);
+ }
+ popstackmark(&smark);
+ skip = evalskip;
+
+ if (skip) {
+ evalskip = 0;
+ return skip & SKIPEVAL;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Take commands from a file. To be compatible we should do a path
+ * search for the file, which is necessary to find sub-commands.
+ */
+static char *
+find_dot_file(char *name)
+{
+ char *fullname;
+ const char *path = pathval();
+ struct stat statb;
+
+ /* don't try this for absolute or relative paths */
+ if (strchr(name, '/'))
+ return name;
+
+ while ((fullname = padvance(&path, name)) != NULL) {
+ if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
+ /*
+ * Don't bother freeing here, since it will
+ * be freed by the caller.
+ */
+ return fullname;
+ }
+ stunalloc(fullname);
+ }
+
+ /* not found in the PATH */
+ ash_msg_and_raise_error("%s: not found", name);
+ /* NOTREACHED */
+}
+
+static int
+dotcmd(int argc, char **argv)
+{
+ struct strlist *sp;
+ volatile struct shparam saveparam;
+ int status = 0;
+
+ for (sp = cmdenviron; sp; sp = sp->next)
+ setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
+
+ if (argc >= 2) { /* That's what SVR2 does */
+ char *fullname;
+
+ fullname = find_dot_file(argv[1]);
+
+ if (argc > 2) {
+ saveparam = shellparam;
+ shellparam.malloc = 0;
+ shellparam.nparam = argc - 2;
+ shellparam.p = argv + 2;
+ };
+
+ setinputfile(fullname, INPUT_PUSH_FILE);
+ commandname = fullname;
+ cmdloop(0);
+ popfile();
+
+ if (argc > 2) {
+ freeparam(&shellparam);
+ shellparam = saveparam;
+ };
+ status = exitstatus;
+ }
+ return status;
+}
+
+static int
+exitcmd(int argc, char **argv)
+{
+ if (stoppedjobs())
+ return 0;
+ if (argc > 1)
+ exitstatus = number(argv[1]);
+ raise_exception(EXEXIT);
+ /* NOTREACHED */
+}
+
+#if ENABLE_ASH_BUILTIN_ECHO
+static int
+echocmd(int argc, char **argv)
+{
+ return bb_echo(argv);
+}
+#endif
+
+#if ENABLE_ASH_BUILTIN_TEST
+static int
+testcmd(int argc, char **argv)
+{
+ return bb_test(argc, argv);
+}
+#endif
+
+/*
+ * Read a file containing shell functions.
+ */
+static void
+readcmdfile(char *name)
+{
+ setinputfile(name, INPUT_PUSH_FILE);
+ cmdloop(0);
+ popfile();
+}
+
+
+/* ============ find_command inplementation */
+
+/*
+ * Resolve a command name. If you change this routine, you may have to
+ * change the shellexec routine as well.
+ */
+static void
+find_command(char *name, struct cmdentry *entry, int act, const char *path)
+{
+ struct tblentry *cmdp;
+ int idx;
+ int prev;
+ char *fullname;
+ struct stat statb;
+ int e;
+ int updatetbl;
+ struct builtincmd *bcmd;
+
+ /* If name contains a slash, don't use PATH or hash table */
+ if (strchr(name, '/') != NULL) {
+ entry->u.index = -1;
+ if (act & DO_ABS) {
+ while (stat(name, &statb) < 0) {
+#ifdef SYSV
+ if (errno == EINTR)
+ continue;
+#endif
+ entry->cmdtype = CMDUNKNOWN;
+ return;
+ }
+ }
+ entry->cmdtype = CMDNORMAL;
+ return;
+ }
+
+#if ENABLE_FEATURE_SH_STANDALONE_SHELL
+ if (find_applet_by_name(name)) {
+ entry->cmdtype = CMDNORMAL;
+ entry->u.index = -1;
+ return;
+ }
+ /* Already caught above
+ if (is_safe_applet(name)) {
+ entry->cmdtype = CMDNORMAL;
+ entry->u.index = -1;
+ return;
+ }*/
+#endif
+
+ updatetbl = (path == pathval());
+ if (!updatetbl) {
+ act |= DO_ALTPATH;
+ if (strstr(path, "%builtin") != NULL)
+ act |= DO_ALTBLTIN;
+ }
+
+ /* If name is in the table, check answer will be ok */
+ cmdp = cmdlookup(name, 0);
+ if (cmdp != NULL) {
+ int bit;
+
+ switch (cmdp->cmdtype) {
+ default:
+#if DEBUG
+ abort();
+#endif
+ case CMDNORMAL:
+ bit = DO_ALTPATH;
+ break;
+ case CMDFUNCTION:
+ bit = DO_NOFUNC;
+ break;
+ case CMDBUILTIN:
+ bit = DO_ALTBLTIN;
+ break;
+ }
+ if (act & bit) {
+ updatetbl = 0;
+ cmdp = NULL;
+ } else if (cmdp->rehash == 0)
+ /* if not invalidated by cd, we're done */
+ goto success;
+ }
+
+ /* If %builtin not in path, check for builtin next */
+ bcmd = find_builtin(name);
+ if (bcmd) {
+ if (IS_BUILTIN_REGULAR(bcmd))
+ goto builtin_success;
+ if (act & DO_ALTPATH) {
+ if (!(act & DO_ALTBLTIN))
+ goto builtin_success;
+ } else if (builtinloc <= 0) {
+ goto builtin_success;
+ }
+ }
+
+ /* We have to search path. */
+ prev = -1; /* where to start */
+ if (cmdp && cmdp->rehash) { /* doing a rehash */
+ if (cmdp->cmdtype == CMDBUILTIN)
+ prev = builtinloc;
+ else
+ prev = cmdp->param.index;
+ }
+
+ e = ENOENT;
+ idx = -1;
+ loop:
+ while ((fullname = padvance(&path, name)) != NULL) {
+ stunalloc(fullname);
+ idx++;
+ if (pathopt) {
+ if (prefix(pathopt, "builtin")) {
+ if (bcmd)
+ goto builtin_success;
+ continue;
+ } else if (!(act & DO_NOFUNC) &&
+ prefix(pathopt, "func")) {
+ /* handled below */
+ } else {
+ /* ignore unimplemented options */
+ continue;
+ }
+ }
+ /* if rehash, don't redo absolute path names */
+ if (fullname[0] == '/' && idx <= prev) {
+ if (idx < prev)
+ continue;
+ TRACE(("searchexec \"%s\": no change\n", name));
+ goto success;
+ }
+ while (stat(fullname, &statb) < 0) {
+#ifdef SYSV
+ if (errno == EINTR)
+ continue;
+#endif
+ if (errno != ENOENT && errno != ENOTDIR)
+ e = errno;
+ goto loop;
+ }
+ e = EACCES; /* if we fail, this will be the error */
+ if (!S_ISREG(statb.st_mode))
+ continue;
+ if (pathopt) { /* this is a %func directory */
+ stalloc(strlen(fullname) + 1);
+ readcmdfile(fullname);
+ cmdp = cmdlookup(name, 0);
+ if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
+ ash_msg_and_raise_error("%s not defined in %s", name, fullname);
+ stunalloc(fullname);
+ goto success;
+ }
+ TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
+ if (!updatetbl) {
+ entry->cmdtype = CMDNORMAL;
+ entry->u.index = idx;
+ return;
+ }
+ INT_OFF;
+ cmdp = cmdlookup(name, 1);
+ cmdp->cmdtype = CMDNORMAL;
+ cmdp->param.index = idx;
+ INT_ON;
+ goto success;
+ }
+
+ /* We failed. If there was an entry for this command, delete it */
+ if (cmdp && updatetbl)
+ delete_cmd_entry();
+ if (act & DO_ERR)
+ ash_msg("%s: %s", name, errmsg(e, "not found"));
+ entry->cmdtype = CMDUNKNOWN;
+ return;
+
+ builtin_success:
+ if (!updatetbl) {
+ entry->cmdtype = CMDBUILTIN;
+ entry->u.cmd = bcmd;
+ return;
+ }
+ INT_OFF;
+ cmdp = cmdlookup(name, 1);
+ cmdp->cmdtype = CMDBUILTIN;
+ cmdp->param.cmd = bcmd;
+ INT_ON;
+ success:
+ cmdp->rehash = 0;
+ entry->cmdtype = cmdp->cmdtype;
+ entry->u = cmdp->param;
+}
+
+
+/* ============ trap.c */
+
+/*
+ * The trap builtin.
+ */
+static int
+trapcmd(int argc, char **argv)
+{
+ char *action;
+ char **ap;
+ int signo;
+
+ nextopt(nullstr);
+ ap = argptr;
+ if (!*ap) {
+ for (signo = 0; signo < NSIG; signo++) {
+ if (trap[signo] != NULL) {
+ const char *sn;
+
+ sn = get_signame(signo);
+ out1fmt("trap -- %s %s\n",
+ single_quote(trap[signo]), sn);
+ }
+ }
+ return 0;
+ }
+ if (!ap[1])
+ action = NULL;
+ else
+ action = *ap++;
+ while (*ap) {
+ signo = get_signum(*ap);
+ if (signo < 0)
+ ash_msg_and_raise_error("%s: bad trap", *ap);
+ INT_OFF;
+ if (action) {
+ if (LONE_DASH(action))
+ action = NULL;
+ else
+ action = ckstrdup(action);
+ }
+ if (trap[signo])
+ free(trap[signo]);
+ trap[signo] = action;
+ if (signo != 0)
+ setsignal(signo);
+ INT_ON;
+ ap++;
+ }
+ return 0;
+}
+
+
+/* ============ Builtins */
+
+#if !ENABLE_FEATURE_SH_EXTRA_QUIET
+/*
+ * Lists available builtins
+ */
+static int
+helpcmd(int argc, char **argv)
+{
+ int col, i;
+
+ out1fmt("\nBuilt-in commands:\n-------------------\n");
+ for (col = 0, i = 0; i < NUMBUILTINS; i++) {
+ col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
+ builtintab[i].name + 1);
+ if (col > 60) {
+ out1fmt("\n");
+ col = 0;
+ }
+ }
+#if ENABLE_FEATURE_SH_STANDALONE_SHELL
+ for (i = 0; i < NUM_APPLETS; i++) {
+ col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), applets[i].name);
+ if (col > 60) {
+ out1fmt("\n");
+ col = 0;
+ }
+ }
+#endif
+ out1fmt("\n\n");
+ return EXIT_SUCCESS;
+}
+#endif /* FEATURE_SH_EXTRA_QUIET */
+
+/*
+ * The export and readonly commands.
+ */
+static int
+exportcmd(int argc, char **argv)
+{
+ struct var *vp;
+ char *name;
+ const char *p;
+ char **aptr;
+ int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
+
+ if (nextopt("p") != 'p') {
+ aptr = argptr;
+ name = *aptr;
+ if (name) {
+ do {
+ p = strchr(name, '=');
+ if (p != NULL) {
+ p++;
+ } else {
+ vp = *findvar(hashvar(name), name);
+ if (vp) {
+ vp->flags |= flag;
+ continue;
+ }
+ }
+ setvar(name, p, flag);
+ } while ((name = *++aptr) != NULL);
+ return 0;
+ }
+ }
+ showvars(argv[0], flag, 0);
+ return 0;
+}
+
+/*
+ * Delete a function if it exists.
+ */
+static void
+unsetfunc(const char *name)
+{
+ struct tblentry *cmdp;
+
+ cmdp = cmdlookup(name, 0);
+ if (cmdp!= NULL && cmdp->cmdtype == CMDFUNCTION)
+ delete_cmd_entry();
+}
+
+/*
+ * The unset builtin command. We unset the function before we unset the
+ * variable to allow a function to be unset when there is a readonly variable
+ * with the same name.
+ */
+static int
+unsetcmd(int argc, char **argv)
+{
+ char **ap;
+ int i;
+ int flag = 0;
+ int ret = 0;
+
+ while ((i = nextopt("vf")) != '\0') {
+ flag = i;
+ }
+
+ for (ap = argptr; *ap; ap++) {
+ if (flag != 'f') {
+ i = unsetvar(*ap);
+ ret |= i;
+ if (!(i & 2))
+ continue;
+ }
+ if (flag != 'v')
+ unsetfunc(*ap);
+ }
+ return ret & 1;
+}
+
+
+/* setmode.c */
+
+#include <sys/times.h>
+
+static const unsigned char timescmd_str[] = {
+ ' ', offsetof(struct tms, tms_utime),
+ '\n', offsetof(struct tms, tms_stime),
+ ' ', offsetof(struct tms, tms_cutime),
+ '\n', offsetof(struct tms, tms_cstime),
+ 0
+};
+
+static int
+timescmd(int ac, char **av)
+{
+ long clk_tck, s, t;
+ const unsigned char *p;
+ struct tms buf;
+
+ clk_tck = sysconf(_SC_CLK_TCK);
+ times(&buf);
+
+ p = timescmd_str;
+ do {
+ t = *(clock_t *)(((char *) &buf) + p[1]);
+ s = t / clk_tck;
+ out1fmt("%ldm%ld.%.3lds%c",
+ s/60, s%60,
+ ((t - s * clk_tck) * 1000) / clk_tck,
+ p[0]);
+ } while (*(p += 2));
+
+ return 0;
+}
+
+#if ENABLE_ASH_MATH_SUPPORT
+static arith_t
+dash_arith(const char *s)
+{
+ arith_t result;
+ int errcode = 0;
+
+ INT_OFF;
+ result = arith(s, &errcode);
+ if (errcode < 0) {
+ if (errcode == -3)
+ ash_msg_and_raise_error("exponent less than 0");
+ if (errcode == -2)
+ ash_msg_and_raise_error("divide by zero");
+ if (errcode == -5)
+ ash_msg_and_raise_error("expression recursion loop detected");
+ raise_error_syntax(s);
+ }
+ INT_ON;
+
+ return result;
+}
+
+/*
+ * The let builtin. partial stolen from GNU Bash, the Bourne Again SHell.
+ * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
+ *
+ * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
+ */
+static int
+letcmd(int argc, char **argv)
+{
+ char **ap;
+ arith_t i = 0;
+
+ ap = argv + 1;
+ if (!*ap)
+ ash_msg_and_raise_error("expression expected");
+ for (ap = argv + 1; *ap; ap++) {
+ i = dash_arith(*ap);
+ }
+
+ return !i;
+}
+#endif /* ASH_MATH_SUPPORT */
+
+
+/* ============ miscbltin.c
+ *
+ * Miscellaneous builtins.
+ */
+
+#undef rflag
+
+#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
+typedef enum __rlimit_resource rlim_t;
+#endif
+
+/*
+ * The read builtin. The -e option causes backslashes to escape the
+ * following character.
+ *
+ * This uses unbuffered input, which may be avoidable in some cases.
+ */
+static int
+readcmd(int argc, char **argv)
+{
+ char **ap;
+ int backslash;
+ char c;
+ int rflag;
+ char *prompt;
+ const char *ifs;
+ char *p;
+ int startword;
+ int status;
+ int i;
+#if ENABLE_ASH_READ_NCHARS
+ int nch_flag = 0;
+ int nchars = 0;
+ int silent = 0;
+ struct termios tty, old_tty;
+#endif
+#if ENABLE_ASH_READ_TIMEOUT
+ fd_set set;
+ struct timeval ts;
+
+ ts.tv_sec = ts.tv_usec = 0;
+#endif
+
+ rflag = 0;
+ prompt = NULL;
+#if ENABLE_ASH_READ_NCHARS && ENABLE_ASH_READ_TIMEOUT
+ while ((i = nextopt("p:rt:n:s")) != '\0')
+#elif ENABLE_ASH_READ_NCHARS
+ while ((i = nextopt("p:rn:s")) != '\0')
+#elif ENABLE_ASH_READ_TIMEOUT
+ while ((i = nextopt("p:rt:")) != '\0')
+#else
+ while ((i = nextopt("p:r")) != '\0')
+#endif
+ {
+ switch (i) {
+ case 'p':
+ prompt = optionarg;
+ break;
+#if ENABLE_ASH_READ_NCHARS
+ case 'n':
+ nchars = strtol(optionarg, &p, 10);
+ if (*p)
+ ash_msg_and_raise_error("invalid count");
+ nch_flag = (nchars > 0);
+ break;
+ case 's':
+ silent = 1;
+ break;
+#endif
+#if ENABLE_ASH_READ_TIMEOUT
+ case 't':
+ ts.tv_sec = strtol(optionarg, &p, 10);
+ ts.tv_usec = 0;
+ if (*p == '.') {
+ char *p2;
+ if (*++p) {
+ int scale;
+ ts.tv_usec = strtol(p, &p2, 10);
+ if (*p2)
+ ash_msg_and_raise_error("invalid timeout");
+ scale = p2 - p;
+ /* normalize to usec */
+ if (scale > 6)
+ ash_msg_and_raise_error("invalid timeout");
+ while (scale++ < 6)
+ ts.tv_usec *= 10;
+ }
+ } else if (*p) {
+ ash_msg_and_raise_error("invalid timeout");
+ }
+ if ( ! ts.tv_sec && ! ts.tv_usec)
+ ash_msg_and_raise_error("invalid timeout");
+ break;
+#endif
+ case 'r':
+ rflag = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ if (prompt && isatty(0)) {
+ out2str(prompt);
+ }
+ ap = argptr;
+ if (*ap == NULL)
+ ash_msg_and_raise_error("arg count");
+ ifs = bltinlookup("IFS");
+ if (ifs == NULL)
+ ifs = defifs;
+#if ENABLE_ASH_READ_NCHARS
+ if (nch_flag || silent) {
+ tcgetattr(0, &tty);
+ old_tty = tty;
+ if (nch_flag) {
+ tty.c_lflag &= ~ICANON;
+ tty.c_cc[VMIN] = nchars;
+ }
+ if (silent) {
+ tty.c_lflag &= ~(ECHO|ECHOK|ECHONL);
+
+ }
+ tcsetattr(0, TCSANOW, &tty);
+ }
+#endif
+#if ENABLE_ASH_READ_TIMEOUT
+ if (ts.tv_sec || ts.tv_usec) {
+ FD_ZERO (&set);
+ FD_SET (0, &set);
+
+ i = select(FD_SETSIZE, &set, NULL, NULL, &ts);
+ if (!i) {
+#if ENABLE_ASH_READ_NCHARS
+ if (nch_flag)
+ tcsetattr(0, TCSANOW, &old_tty);
+#endif
+ return 1;
+ }
+ }
+#endif
+ status = 0;
+ startword = 1;
+ backslash = 0;
+ STARTSTACKSTR(p);
+#if ENABLE_ASH_READ_NCHARS
+ while (!nch_flag || nchars--)
+#else
+ for (;;)
+#endif
+ {
+ if (read(0, &c, 1) != 1) {
+ status = 1;
+ break;
+ }
+ if (c == '\0')
+ continue;
+ if (backslash) {
+ backslash = 0;
+ if (c != '\n')
+ goto put;
+ continue;
+ }
+ if (!rflag && c == '\\') {
+ backslash++;
+ continue;
+ }
+ if (c == '\n')
+ break;
+ if (startword && *ifs == ' ' && strchr(ifs, c)) {
+ continue;
+ }
+ startword = 0;
+ if (ap[1] != NULL && strchr(ifs, c) != NULL) {
+ STACKSTRNUL(p);
+ setvar(*ap, stackblock(), 0);
+ ap++;
+ startword = 1;
+ STARTSTACKSTR(p);
+ } else {
+ put:
+ STPUTC(c, p);
+ }
+ }
+#if ENABLE_ASH_READ_NCHARS
+ if (nch_flag || silent)
+ tcsetattr(0, TCSANOW, &old_tty);
+#endif
+
+ STACKSTRNUL(p);
+ /* Remove trailing blanks */
+ while ((char *)stackblock() <= --p && strchr(ifs, *p) != NULL)
+ *p = '\0';
+ setvar(*ap, stackblock(), 0);
+ while (*++ap != NULL)
+ setvar(*ap, nullstr, 0);
+ return status;
+}
+
+static int
+umaskcmd(int argc, char **argv)
+{
+ static const char permuser[3] = "ugo";
+ static const char permmode[3] = "rwx";
+ static const short int permmask[] = {
+ S_IRUSR, S_IWUSR, S_IXUSR,
+ S_IRGRP, S_IWGRP, S_IXGRP,
+ S_IROTH, S_IWOTH, S_IXOTH
+ };
+
+ char *ap;
+ mode_t mask;
+ int i;
+ int symbolic_mode = 0;
+
+ while (nextopt("S") != '\0') {
+ symbolic_mode = 1;
+ }
+
+ INT_OFF;
+ mask = umask(0);
+ umask(mask);
+ INT_ON;
+
+ ap = *argptr;
+ if (ap == NULL) {
+ if (symbolic_mode) {
+ char buf[18];
+ char *p = buf;
+
+ for (i = 0; i < 3; i++) {
+ int j;
+
+ *p++ = permuser[i];
+ *p++ = '=';
+ for (j = 0; j < 3; j++) {
+ if ((mask & permmask[3 * i + j]) == 0) {
+ *p++ = permmode[j];
+ }
+ }
+ *p++ = ',';
+ }
+ *--p = 0;
+ puts(buf);
+ } else {
+ out1fmt("%.4o\n", mask);
+ }
+ } else {
+ if (isdigit((unsigned char) *ap)) {
+ mask = 0;
+ do {
+ if (*ap >= '8' || *ap < '0')
+ ash_msg_and_raise_error(illnum, argv[1]);
+ mask = (mask << 3) + (*ap - '0');
+ } while (*++ap != '\0');
+ umask(mask);
+ } else {
+ mask = ~mask & 0777;
+ if (!bb_parse_mode(ap, &mask)) {
+ ash_msg_and_raise_error("illegal mode: %s", ap);
+ }
+ umask(~mask & 0777);
+ }
+ }
+ return 0;
+}
+
+/*
+ * ulimit builtin
+ *
+ * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
+ * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
+ * ash by J.T. Conklin.
+ *
+ * Public domain.
+ */
+
+struct limits {
+ const char *name;
+ int cmd;
+ int factor; /* multiply by to get rlim_{cur,max} values */
+ char option;
+};
+
+static const struct limits limits[] = {
+#ifdef RLIMIT_CPU
+ { "time(seconds)", RLIMIT_CPU, 1, 't' },
+#endif
+#ifdef RLIMIT_FSIZE
+ { "file(blocks)", RLIMIT_FSIZE, 512, 'f' },
+#endif
+#ifdef RLIMIT_DATA
+ { "data(kbytes)", RLIMIT_DATA, 1024, 'd' },
+#endif
+#ifdef RLIMIT_STACK
+ { "stack(kbytes)", RLIMIT_STACK, 1024, 's' },
+#endif
+#ifdef RLIMIT_CORE
+ { "coredump(blocks)", RLIMIT_CORE, 512, 'c' },
+#endif
+#ifdef RLIMIT_RSS
+ { "memory(kbytes)", RLIMIT_RSS, 1024, 'm' },
+#endif
+#ifdef RLIMIT_MEMLOCK
+ { "locked memory(kbytes)", RLIMIT_MEMLOCK, 1024, 'l' },
+#endif
+#ifdef RLIMIT_NPROC
+ { "process", RLIMIT_NPROC, 1, 'p' },
+#endif
+#ifdef RLIMIT_NOFILE
+ { "nofiles", RLIMIT_NOFILE, 1, 'n' },
+#endif
+#ifdef RLIMIT_AS
+ { "vmemory(kbytes)", RLIMIT_AS, 1024, 'v' },
+#endif
+#ifdef RLIMIT_LOCKS
+ { "locks", RLIMIT_LOCKS, 1, 'w' },
+#endif
+ { NULL, 0, 0, '\0' }
+};
+
+enum limtype { SOFT = 0x1, HARD = 0x2 };
+
+static void
+printlim(enum limtype how, const struct rlimit *limit,
+ const struct limits *l)
+{
+ rlim_t val;
+
+ val = limit->rlim_max;
+ if (how & SOFT)
+ val = limit->rlim_cur;
+
+ if (val == RLIM_INFINITY)
+ out1fmt("unlimited\n");
+ else {
+ val /= l->factor;
+ out1fmt("%lld\n", (long long) val);
+ }
+}
+
+static int
+ulimitcmd(int argc, char **argv)
+{
+ int c;
+ rlim_t val = 0;
+ enum limtype how = SOFT | HARD;
+ const struct limits *l;
+ int set, all = 0;
+ int optc, what;
+ struct rlimit limit;
+
+ what = 'f';
+ while ((optc = nextopt("HSa"
+#ifdef RLIMIT_CPU
+ "t"
+#endif
+#ifdef RLIMIT_FSIZE
+ "f"
+#endif
+#ifdef RLIMIT_DATA
+ "d"
+#endif
+#ifdef RLIMIT_STACK
+ "s"
+#endif
+#ifdef RLIMIT_CORE
+ "c"
+#endif
+#ifdef RLIMIT_RSS
+ "m"
+#endif
+#ifdef RLIMIT_MEMLOCK
+ "l"
+#endif
+#ifdef RLIMIT_NPROC
+ "p"
+#endif
+#ifdef RLIMIT_NOFILE
+ "n"
+#endif
+#ifdef RLIMIT_AS
+ "v"
+#endif
+#ifdef RLIMIT_LOCKS
+ "w"
+#endif
+ )) != '\0')
+ switch (optc) {
+ case 'H':
+ how = HARD;
+ break;
+ case 'S':
+ how = SOFT;
+ break;
+ case 'a':
+ all = 1;
+ break;
+ default:
+ what = optc;
+ }
+
+ for (l = limits; l->option != what; l++)
+ ;
+
+ set = *argptr ? 1 : 0;
+ if (set) {
+ char *p = *argptr;
+
+ if (all || argptr[1])
+ ash_msg_and_raise_error("too many arguments");
+ if (strncmp(p, "unlimited\n", 9) == 0)
+ val = RLIM_INFINITY;
+ else {
+ val = (rlim_t) 0;
+
+ while ((c = *p++) >= '0' && c <= '9') {
+ val = (val * 10) + (long)(c - '0');
+ if (val < (rlim_t) 0)
+ break;
+ }
+ if (c)
+ ash_msg_and_raise_error("bad number");
+ val *= l->factor;
+ }
+ }
+ if (all) {
+ for (l = limits; l->name; l++) {
+ getrlimit(l->cmd, &limit);
+ out1fmt("%-20s ", l->name);
+ printlim(how, &limit, l);
+ }
+ return 0;
+ }
+
+ getrlimit(l->cmd, &limit);
+ if (set) {
+ if (how & HARD)
+ limit.rlim_max = val;
+ if (how & SOFT)
+ limit.rlim_cur = val;
+ if (setrlimit(l->cmd, &limit) < 0)
+ ash_msg_and_raise_error("error setting limit (%m)");
+ } else {
+ printlim(how, &limit, l);
+ }
+ return 0;
+}
+
+
+/* ============ Math support */
+
+#if ENABLE_ASH_MATH_SUPPORT
+
+/* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+/* This is my infix parser/evaluator. It is optimized for size, intended
+ * as a replacement for yacc-based parsers. However, it may well be faster
+ * than a comparable parser written in yacc. The supported operators are
+ * listed in #defines below. Parens, order of operations, and error handling
+ * are supported. This code is thread safe. The exact expression format should
+ * be that which POSIX specifies for shells. */
+
+/* The code uses a simple two-stack algorithm. See
+ * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html
+ * for a detailed explanation of the infix-to-postfix algorithm on which
+ * this is based (this code differs in that it applies operators immediately
+ * to the stack instead of adding them to a queue to end up with an
+ * expression). */
+
+/* To use the routine, call it with an expression string and error return
+ * pointer */
+
+/*
+ * Aug 24, 2001 Manuel Novoa III
+ *
+ * Reduced the generated code size by about 30% (i386) and fixed several bugs.
+ *
+ * 1) In arith_apply():
+ * a) Cached values of *numptr and &(numptr[-1]).
+ * b) Removed redundant test for zero denominator.
+ *
+ * 2) In arith():
+ * a) Eliminated redundant code for processing operator tokens by moving
+ * to a table-based implementation. Also folded handling of parens
+ * into the table.
+ * b) Combined all 3 loops which called arith_apply to reduce generated
+ * code size at the cost of speed.
+ *
+ * 3) The following expressions were treated as valid by the original code:
+ * 1() , 0! , 1 ( *3 ) .
+ * These bugs have been fixed by internally enclosing the expression in
+ * parens and then checking that all binary ops and right parens are
+ * preceded by a valid expression (NUM_TOKEN).
+ *
+ * Note: It may be desirable to replace Aaron's test for whitespace with
+ * ctype's isspace() if it is used by another busybox applet or if additional
+ * whitespace chars should be considered. Look below the "#include"s for a
+ * precompiler test.
+ */
+
+/*
+ * Aug 26, 2001 Manuel Novoa III
+ *
+ * Return 0 for null expressions. Pointed out by Vladimir Oleynik.
+ *
+ * Merge in Aaron's comments previously posted to the busybox list,
+ * modified slightly to take account of my changes to the code.
+ *
+ */
+
+/*
+ * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
+ *
+ * - allow access to variable,
+ * used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6)
+ * - realize assign syntax (VAR=expr, +=, *= etc)
+ * - realize exponentiation (** operator)
+ * - realize comma separated - expr, expr
+ * - realise ++expr --expr expr++ expr--
+ * - realise expr ? expr : expr (but, second expr calculate always)
+ * - allow hexadecimal and octal numbers
+ * - was restored loses XOR operator
+ * - remove one goto label, added three ;-)
+ * - protect $((num num)) as true zero expr (Manuel`s error)
+ * - always use special isspace(), see comment from bash ;-)
+ */
+
+#define arith_isspace(arithval) \
+ (arithval == ' ' || arithval == '\n' || arithval == '\t')
+
+typedef unsigned char operator;
+
+/* An operator's token id is a bit of a bitfield. The lower 5 bits are the
+ * precedence, and 3 high bits are an ID unique across operators of that
+ * precedence. The ID portion is so that multiple operators can have the
+ * same precedence, ensuring that the leftmost one is evaluated first.
+ * Consider * and /. */
+
+#define tok_decl(prec,id) (((id)<<5)|(prec))
+#define PREC(op) ((op) & 0x1F)
+
+#define TOK_LPAREN tok_decl(0,0)
+
+#define TOK_COMMA tok_decl(1,0)
+
+#define TOK_ASSIGN tok_decl(2,0)
+#define TOK_AND_ASSIGN tok_decl(2,1)
+#define TOK_OR_ASSIGN tok_decl(2,2)
+#define TOK_XOR_ASSIGN tok_decl(2,3)
+#define TOK_PLUS_ASSIGN tok_decl(2,4)
+#define TOK_MINUS_ASSIGN tok_decl(2,5)
+#define TOK_LSHIFT_ASSIGN tok_decl(2,6)
+#define TOK_RSHIFT_ASSIGN tok_decl(2,7)
+
+#define TOK_MUL_ASSIGN tok_decl(3,0)
+#define TOK_DIV_ASSIGN tok_decl(3,1)
+#define TOK_REM_ASSIGN tok_decl(3,2)
+
+/* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */
+#define convert_prec_is_assing(prec) do { if (prec == 3) prec = 2; } while (0)
+
+/* conditional is right associativity too */
+#define TOK_CONDITIONAL tok_decl(4,0)
+#define TOK_CONDITIONAL_SEP tok_decl(4,1)
+
+#define TOK_OR tok_decl(5,0)
+
+#define TOK_AND tok_decl(6,0)
+
+#define TOK_BOR tok_decl(7,0)
+
+#define TOK_BXOR tok_decl(8,0)
+
+#define TOK_BAND tok_decl(9,0)
+
+#define TOK_EQ tok_decl(10,0)
+#define TOK_NE tok_decl(10,1)
+
+#define TOK_LT tok_decl(11,0)
+#define TOK_GT tok_decl(11,1)
+#define TOK_GE tok_decl(11,2)
+#define TOK_LE tok_decl(11,3)
+
+#define TOK_LSHIFT tok_decl(12,0)
+#define TOK_RSHIFT tok_decl(12,1)
+
+#define TOK_ADD tok_decl(13,0)
+#define TOK_SUB tok_decl(13,1)
+
+#define TOK_MUL tok_decl(14,0)
+#define TOK_DIV tok_decl(14,1)
+#define TOK_REM tok_decl(14,2)
+
+/* exponent is right associativity */
+#define TOK_EXPONENT tok_decl(15,1)
+
+/* For now unary operators. */
+#define UNARYPREC 16
+#define TOK_BNOT tok_decl(UNARYPREC,0)
+#define TOK_NOT tok_decl(UNARYPREC,1)
+
+#define TOK_UMINUS tok_decl(UNARYPREC+1,0)
+#define TOK_UPLUS tok_decl(UNARYPREC+1,1)
+
+#define PREC_PRE (UNARYPREC+2)
+
+#define TOK_PRE_INC tok_decl(PREC_PRE, 0)
+#define TOK_PRE_DEC tok_decl(PREC_PRE, 1)
+
+#define PREC_POST (UNARYPREC+3)
+
+#define TOK_POST_INC tok_decl(PREC_POST, 0)
+#define TOK_POST_DEC tok_decl(PREC_POST, 1)
+
+#define SPEC_PREC (UNARYPREC+4)
+
+#define TOK_NUM tok_decl(SPEC_PREC, 0)
+#define TOK_RPAREN tok_decl(SPEC_PREC, 1)
+
+#define NUMPTR (*numstackptr)
+
+static int
+tok_have_assign(operator op)
+{
+ operator prec = PREC(op);
+
+ convert_prec_is_assing(prec);
+ return (prec == PREC(TOK_ASSIGN) ||
+ prec == PREC_PRE || prec == PREC_POST);
+}
+
+static int
+is_right_associativity(operator prec)
+{
+ return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT)
+ || prec == PREC(TOK_CONDITIONAL));
+}
+
+typedef struct ARITCH_VAR_NUM {
+ arith_t val;
+ arith_t contidional_second_val;
+ char contidional_second_val_initialized;
+ char *var; /* if NULL then is regular number,
+ else is variable name */
+} v_n_t;
+
+typedef struct CHK_VAR_RECURSIVE_LOOPED {
+ const char *var;
+ struct CHK_VAR_RECURSIVE_LOOPED *next;
+} chk_var_recursive_looped_t;
+
+static chk_var_recursive_looped_t *prev_chk_var_recursive;
+
+static int
+arith_lookup_val(v_n_t *t)
+{
+ if (t->var) {
+ const char * p = lookupvar(t->var);
+
+ if (p) {
+ int errcode;
+
+ /* recursive try as expression */
+ chk_var_recursive_looped_t *cur;
+ chk_var_recursive_looped_t cur_save;
+
+ for (cur = prev_chk_var_recursive; cur; cur = cur->next) {
+ if (strcmp(cur->var, t->var) == 0) {
+ /* expression recursion loop detected */
+ return -5;
+ }
+ }
+ /* save current lookuped var name */
+ cur = prev_chk_var_recursive;
+ cur_save.var = t->var;
+ cur_save.next = cur;
+ prev_chk_var_recursive = &cur_save;
+
+ t->val = arith (p, &errcode);
+ /* restore previous ptr after recursiving */
+ prev_chk_var_recursive = cur;
+ return errcode;
+ }
+ /* allow undefined var as 0 */
+ t->val = 0;
+ }
+ return 0;
+}
+
+/* "applying" a token means performing it on the top elements on the integer
+ * stack. For a unary operator it will only change the top element, but a
+ * binary operator will pop two arguments and push a result */
+static int
+arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
+{
+ v_n_t *numptr_m1;
+ arith_t numptr_val, rez;
+ int ret_arith_lookup_val;
+
+ /* There is no operator that can work without arguments */
+ if (NUMPTR == numstack) goto err;
+ numptr_m1 = NUMPTR - 1;
+
+ /* check operand is var with noninteger value */
+ ret_arith_lookup_val = arith_lookup_val(numptr_m1);
+ if (ret_arith_lookup_val)
+ return ret_arith_lookup_val;
+
+ rez = numptr_m1->val;
+ if (op == TOK_UMINUS)
+ rez *= -1;
+ else if (op == TOK_NOT)
+ rez = !rez;
+ else if (op == TOK_BNOT)
+ rez = ~rez;
+ else if (op == TOK_POST_INC || op == TOK_PRE_INC)
+ rez++;
+ else if (op == TOK_POST_DEC || op == TOK_PRE_DEC)
+ rez--;
+ else if (op != TOK_UPLUS) {
+ /* Binary operators */
+
+ /* check and binary operators need two arguments */
+ if (numptr_m1 == numstack) goto err;
+
+ /* ... and they pop one */
+ --NUMPTR;
+ numptr_val = rez;
+ if (op == TOK_CONDITIONAL) {
+ if (! numptr_m1->contidional_second_val_initialized) {
+ /* protect $((expr1 ? expr2)) without ": expr" */
+ goto err;
+ }
+ rez = numptr_m1->contidional_second_val;
+ } else if (numptr_m1->contidional_second_val_initialized) {
+ /* protect $((expr1 : expr2)) without "expr ? " */
+ goto err;
+ }
+ numptr_m1 = NUMPTR - 1;
+ if (op != TOK_ASSIGN) {
+ /* check operand is var with noninteger value for not '=' */
+ ret_arith_lookup_val = arith_lookup_val(numptr_m1);
+ if (ret_arith_lookup_val)
+ return ret_arith_lookup_val;
+ }
+ if (op == TOK_CONDITIONAL) {
+ numptr_m1->contidional_second_val = rez;
+ }
+ rez = numptr_m1->val;
+ if (op == TOK_BOR || op == TOK_OR_ASSIGN)
+ rez |= numptr_val;
+ else if (op == TOK_OR)
+ rez = numptr_val || rez;
+ else if (op == TOK_BAND || op == TOK_AND_ASSIGN)
+ rez &= numptr_val;
+ else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN)
+ rez ^= numptr_val;
+ else if (op == TOK_AND)
+ rez = rez && numptr_val;
+ else if (op == TOK_EQ)
+ rez = (rez == numptr_val);
+ else if (op == TOK_NE)
+ rez = (rez != numptr_val);
+ else if (op == TOK_GE)
+ rez = (rez >= numptr_val);
+ else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN)
+ rez >>= numptr_val;
+ else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN)
+ rez <<= numptr_val;
+ else if (op == TOK_GT)
+ rez = (rez > numptr_val);
+ else if (op == TOK_LT)
+ rez = (rez < numptr_val);
+ else if (op == TOK_LE)
+ rez = (rez <= numptr_val);
+ else if (op == TOK_MUL || op == TOK_MUL_ASSIGN)
+ rez *= numptr_val;
+ else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN)
+ rez += numptr_val;
+ else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN)
+ rez -= numptr_val;
+ else if (op == TOK_ASSIGN || op == TOK_COMMA)
+ rez = numptr_val;
+ else if (op == TOK_CONDITIONAL_SEP) {
+ if (numptr_m1 == numstack) {
+ /* protect $((expr : expr)) without "expr ? " */
+ goto err;
+ }
+ numptr_m1->contidional_second_val_initialized = op;
+ numptr_m1->contidional_second_val = numptr_val;
+ } else if (op == TOK_CONDITIONAL) {
+ rez = rez ?
+ numptr_val : numptr_m1->contidional_second_val;
+ } else if (op == TOK_EXPONENT) {
+ if (numptr_val < 0)
+ return -3; /* exponent less than 0 */
+ else {
+ arith_t c = 1;
+
+ if (numptr_val)
+ while (numptr_val--)
+ c *= rez;
+ rez = c;
+ }
+ } else if (numptr_val==0) /* zero divisor check */
+ return -2;
+ else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
+ rez /= numptr_val;
+ else if (op == TOK_REM || op == TOK_REM_ASSIGN)
+ rez %= numptr_val;
+ }
+ if (tok_have_assign(op)) {
+ char buf[sizeof(arith_t_type)*3 + 2];
+
+ if (numptr_m1->var == NULL) {
+ /* Hmm, 1=2 ? */
+ goto err;
+ }
+ /* save to shell variable */
+#if ENABLE_ASH_MATH_SUPPORT_64
+ snprintf(buf, sizeof(buf), "%lld", (arith_t_type) rez);
+#else
+ snprintf(buf, sizeof(buf), "%ld", (arith_t_type) rez);
+#endif
+ setvar(numptr_m1->var, buf, 0);
+ /* after saving, make previous value for v++ or v-- */
+ if (op == TOK_POST_INC)
+ rez--;
+ else if (op == TOK_POST_DEC)
+ rez++;
+ }
+ numptr_m1->val = rez;
+ /* protect geting var value, is number now */
+ numptr_m1->var = NULL;
+ return 0;
+ err:
+ return -1;
+}
+
+/* longest must be first */
+static const char op_tokens[] = {
+ '<','<','=',0, TOK_LSHIFT_ASSIGN,
+ '>','>','=',0, TOK_RSHIFT_ASSIGN,
+ '<','<', 0, TOK_LSHIFT,
+ '>','>', 0, TOK_RSHIFT,
+ '|','|', 0, TOK_OR,
+ '&','&', 0, TOK_AND,
+ '!','=', 0, TOK_NE,
+ '<','=', 0, TOK_LE,
+ '>','=', 0, TOK_GE,
+ '=','=', 0, TOK_EQ,
+ '|','=', 0, TOK_OR_ASSIGN,
+ '&','=', 0, TOK_AND_ASSIGN,
+ '*','=', 0, TOK_MUL_ASSIGN,
+ '/','=', 0, TOK_DIV_ASSIGN,
+ '%','=', 0, TOK_REM_ASSIGN,
+ '+','=', 0, TOK_PLUS_ASSIGN,
+ '-','=', 0, TOK_MINUS_ASSIGN,
+ '-','-', 0, TOK_POST_DEC,
+ '^','=', 0, TOK_XOR_ASSIGN,
+ '+','+', 0, TOK_POST_INC,
+ '*','*', 0, TOK_EXPONENT,
+ '!', 0, TOK_NOT,
+ '<', 0, TOK_LT,
+ '>', 0, TOK_GT,
+ '=', 0, TOK_ASSIGN,
+ '|', 0, TOK_BOR,
+ '&', 0, TOK_BAND,
+ '*', 0, TOK_MUL,
+ '/', 0, TOK_DIV,
+ '%', 0, TOK_REM,
+ '+', 0, TOK_ADD,
+ '-', 0, TOK_SUB,
+ '^', 0, TOK_BXOR,
+ /* uniq */
+ '~', 0, TOK_BNOT,
+ ',', 0, TOK_COMMA,
+ '?', 0, TOK_CONDITIONAL,
+ ':', 0, TOK_CONDITIONAL_SEP,
+ ')', 0, TOK_RPAREN,
+ '(', 0, TOK_LPAREN,
+ 0
+};
+/* ptr to ")" */
+#define endexpression &op_tokens[sizeof(op_tokens)-7]
+
+static arith_t
+arith(const char *expr, int *perrcode)
+{
+ char arithval; /* Current character under analysis */
+ operator lasttok, op;
+ operator prec;
+
+ const char *p = endexpression;
+ int errcode;
+
+ size_t datasizes = strlen(expr) + 2;
+
+ /* Stack of integers */
+ /* The proof that there can be no more than strlen(startbuf)/2+1 integers
+ * in any given correct or incorrect expression is left as an exercise to
+ * the reader. */
+ v_n_t *numstack = alloca(((datasizes)/2)*sizeof(v_n_t)),
+ *numstackptr = numstack;
+ /* Stack of operator tokens */
+ operator *stack = alloca((datasizes) * sizeof(operator)),
+ *stackptr = stack;
+
+ *stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */
+ *perrcode = errcode = 0;
+
+ while (1) {
+ arithval = *expr;
+ if (arithval == 0) {
+ if (p == endexpression) {
+ /* Null expression. */
+ return 0;
+ }
+
+ /* This is only reached after all tokens have been extracted from the
+ * input stream. If there are still tokens on the operator stack, they
+ * are to be applied in order. At the end, there should be a final
+ * result on the integer stack */
+
+ if (expr != endexpression + 1) {
+ /* If we haven't done so already, */
+ /* append a closing right paren */
+ expr = endexpression;
+ /* and let the loop process it. */
+ continue;
+ }
+ /* At this point, we're done with the expression. */
+ if (numstackptr != numstack+1) {
+ /* ... but if there isn't, it's bad */
+ err:
+ return (*perrcode = -1);
+ }
+ if (numstack->var) {
+ /* expression is $((var)) only, lookup now */
+ errcode = arith_lookup_val(numstack);
+ }
+ ret:
+ *perrcode = errcode;
+ return numstack->val;
+ }
+
+ /* Continue processing the expression. */
+ if (arith_isspace(arithval)) {
+ /* Skip whitespace */
+ goto prologue;
+ }
+ p = endofname(expr);
+ if (p != expr) {
+ size_t var_name_size = (p-expr) + 1; /* trailing zero */
+
+ numstackptr->var = alloca(var_name_size);
+ safe_strncpy(numstackptr->var, expr, var_name_size);
+ expr = p;
+ num:
+ numstackptr->contidional_second_val_initialized = 0;
+ numstackptr++;
+ lasttok = TOK_NUM;
+ continue;
+ }
+ if (isdigit(arithval)) {
+ numstackptr->var = NULL;
+#if ENABLE_ASH_MATH_SUPPORT_64
+ numstackptr->val = strtoll(expr, (char **) &expr, 0);
+#else
+ numstackptr->val = strtol(expr, (char **) &expr, 0);
+#endif
+ goto num;
+ }
+ for (p = op_tokens; ; p++) {
+ const char *o;
+
+ if (*p == 0) {
+ /* strange operator not found */
+ goto err;
+ }
+ for (o = expr; *p && *o == *p; p++)
+ o++;
+ if (! *p) {
+ /* found */
+ expr = o - 1;
+ break;
+ }
+ /* skip tail uncompared token */
+ while (*p)
+ p++;
+ /* skip zero delim */
+ p++;
+ }
+ op = p[1];
+
+ /* post grammar: a++ reduce to num */
+ if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC)
+ lasttok = TOK_NUM;
+
+ /* Plus and minus are binary (not unary) _only_ if the last
+ * token was as number, or a right paren (which pretends to be
+ * a number, since it evaluates to one). Think about it.
+ * It makes sense. */
+ if (lasttok != TOK_NUM) {
+ switch (op) {
+ case TOK_ADD:
+ op = TOK_UPLUS;
+ break;
+ case TOK_SUB:
+ op = TOK_UMINUS;
+ break;
+ case TOK_POST_INC:
+ op = TOK_PRE_INC;
+ break;
+ case TOK_POST_DEC:
+ op = TOK_PRE_DEC;
+ break;
+ }
+ }
+ /* We don't want a unary operator to cause recursive descent on the
+ * stack, because there can be many in a row and it could cause an
+ * operator to be evaluated before its argument is pushed onto the
+ * integer stack. */
+ /* But for binary operators, "apply" everything on the operator
+ * stack until we find an operator with a lesser priority than the
+ * one we have just extracted. */
+ /* Left paren is given the lowest priority so it will never be
+ * "applied" in this way.
+ * if associativity is right and priority eq, applied also skip
+ */
+ prec = PREC(op);
+ if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) {
+ /* not left paren or unary */
+ if (lasttok != TOK_NUM) {
+ /* binary op must be preceded by a num */
+ goto err;
+ }
+ while (stackptr != stack) {
+ if (op == TOK_RPAREN) {
+ /* The algorithm employed here is simple: while we don't
+ * hit an open paren nor the bottom of the stack, pop
+ * tokens and apply them */
+ if (stackptr[-1] == TOK_LPAREN) {
+ --stackptr;
+ /* Any operator directly after a */
+ lasttok = TOK_NUM;
+ /* close paren should consider itself binary */
+ goto prologue;
+ }
+ } else {
+ operator prev_prec = PREC(stackptr[-1]);
+
+ convert_prec_is_assing(prec);
+ convert_prec_is_assing(prev_prec);
+ if (prev_prec < prec)
+ break;
+ /* check right assoc */
+ if (prev_prec == prec && is_right_associativity(prec))
+ break;
+ }
+ errcode = arith_apply(*--stackptr, numstack, &numstackptr);
+ if (errcode) goto ret;
+ }
+ if (op == TOK_RPAREN) {
+ goto err;
+ }
+ }
+
+ /* Push this operator to the stack and remember it. */
+ *stackptr++ = lasttok = op;
+ prologue:
+ ++expr;
+ } /* while */
+}
+#endif /* ASH_MATH_SUPPORT */
+
+
+/* ============ main() and helpers */
+
+/*
+ * Called to exit the shell.
+ */
+static void exitshell(void) ATTRIBUTE_NORETURN;
+static void
+exitshell(void)
+{
+ struct jmploc loc;
+ char *p;
+ int status;
+
+ status = exitstatus;
+ TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
+ if (setjmp(loc.loc)) {
+ if (exception == EXEXIT)
+/* dash bug: it just does _exit(exitstatus) here
+ * but we have to do setjobctl(0) first!
+ * (bug is still not fixed in dash-0.5.3 - if you run dash
+ * under Midnight Commander, on exit from dash MC is backgrounded) */
+ status = exitstatus;
+ goto out;
+ }
+ exception_handler = &loc;
+ p = trap[0];
+ if (p) {
+ trap[0] = NULL;
+ evalstring(p, 0);
+ }
+ flush_stdout_stderr();
+ out:
+ setjobctl(0);
+ _exit(status);
+ /* NOTREACHED */
+}
+
+static void
+init(void)
+{
+ /* from input.c: */
+ basepf.nextc = basepf.buf = basebuf;
+
+ /* from trap.c: */
+ signal(SIGCHLD, SIG_DFL);
+
+ /* from var.c: */
+ {
+ char **envp;
+ char ppid[sizeof(int)*3 + 1];
+ const char *p;
+ struct stat st1, st2;
+
+ initvar();
+ for (envp = environ; envp && *envp; envp++) {
+ if (strchr(*envp, '=')) {
+ setvareq(*envp, VEXPORT|VTEXTFIXED);
+ }
+ }
+
+ snprintf(ppid, sizeof(ppid), "%u", (unsigned) getppid());
+ setvar("PPID", ppid, 0);
+
+ p = lookupvar("PWD");
+ if (p)
+ if (*p != '/' || stat(p, &st1) || stat(".", &st2)
+ || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
+ p = '\0';
+ setpwd(p, 0);
+ }
+}
+
+/*
+ * Process the shell command line arguments.
+ */
+static void
+procargs(int argc, char **argv)
+{
+ int i;
+ const char *xminusc;
+ char **xargv;
+
+ xargv = argv;
+ arg0 = xargv[0];
+ if (argc > 0)
+ xargv++;
+ for (i = 0; i < NOPTS; i++)
+ optlist[i] = 2;
+ argptr = xargv;
+ options(1);
+ xargv = argptr;
+ xminusc = minusc;
+ if (*xargv == NULL) {
+ if (xminusc)
+ ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
+ sflag = 1;
+ }
+ if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
+ iflag = 1;
+ if (mflag == 2)
+ mflag = iflag;
+ for (i = 0; i < NOPTS; i++)
+ if (optlist[i] == 2)
+ optlist[i] = 0;
+#if DEBUG == 2
+ debug = 1;
+#endif
+ /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
+ if (xminusc) {
+ minusc = *xargv++;
+ if (*xargv)
+ goto setarg0;
+ } else if (!sflag) {
+ setinputfile(*xargv, 0);
+ setarg0:
+ arg0 = *xargv++;
+ commandname = arg0;
+ }
+
+ shellparam.p = xargv;
+#if ENABLE_ASH_GETOPTS
+ shellparam.optind = 1;
+ shellparam.optoff = -1;
+#endif
+ /* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */
+ while (*xargv) {
+ shellparam.nparam++;
+ xargv++;
+ }
+ optschanged();
+}
+
+/*
+ * Read /etc/profile or .profile.
+ */
+static void
+read_profile(const char *name)
+{
+ int skip;
+
+ if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
+ return;
+ skip = cmdloop(0);
+ popfile();
+ if (skip)
+ exitshell();
+}
+
+/*
+ * This routine is called when an error or an interrupt occurs in an
+ * interactive shell and control is returned to the main command loop.
+ */
+static void
+reset(void)
+{
+ /* from eval.c: */
+ evalskip = 0;
+ loopnest = 0;
+ /* from input.c: */
+ parselleft = parsenleft = 0; /* clear input buffer */
+ popallfiles();
+ /* from parser.c: */
+ tokpushback = 0;
+ checkkwd = 0;
+ /* from redir.c: */
+ clearredir(0);
+}
+
+#if PROFILE
+static short profile_buf[16384];
+extern int etext();
+#endif
+
+/*
+ * Main routine. We initialize things, parse the arguments, execute
+ * profiles if we're a login shell, and then call cmdloop to execute
+ * commands. The setjmp call sets up the location to jump to when an
+ * exception occurs. When an exception occurs the variable "state"
+ * is used to figure out how far we had gotten.
+ */
+int ash_main(int argc, char **argv);
+int ash_main(int argc, char **argv)
+{
+ char *shinit;
+ volatile int state;
+ struct jmploc jmploc;
+ struct stackmark smark;
+
+#ifdef __GLIBC__
+ dash_errno = __errno_location();
+#endif
+
+#if PROFILE
+ monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
+#endif
+
+#if ENABLE_FEATURE_EDITING
+ line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
+#endif
+ state = 0;
+ if (setjmp(jmploc.loc)) {
+ int e;
+ int s;
+
+ reset();
+
+ e = exception;
+ if (e == EXERROR)
+ exitstatus = 2;
+ s = state;
+ if (e == EXEXIT || s == 0 || iflag == 0 || shlvl)
+ exitshell();
+
+ if (e == EXINT) {
+ outcslow('\n', stderr);
+ }
+ popstackmark(&smark);
+ FORCE_INT_ON; /* enable interrupts */
+ if (s == 1)
+ goto state1;
+ if (s == 2)
+ goto state2;
+ if (s == 3)
+ goto state3;
+ goto state4;
+ }
+ exception_handler = &jmploc;
+#if DEBUG
+ opentrace();
+ trace_puts("Shell args: ");
+ trace_puts_args(argv);
+#endif
+ rootpid = getpid();
+
+#if ENABLE_ASH_RANDOM_SUPPORT
+ rseed = rootpid + time(NULL);
+#endif
+ init();
+ setstackmark(&smark);
+ procargs(argc, argv);
+#if ENABLE_FEATURE_EDITING_SAVEHISTORY
+ if (iflag) {
+ const char *hp = lookupvar("HISTFILE");
+
+ if (hp == NULL) {
+ hp = lookupvar("HOME");
+ if (hp != NULL) {
+ char *defhp = concat_path_file(hp, ".ash_history");
+ setvar("HISTFILE", defhp, 0);
+ free(defhp);
+ }
+ }
+ }
+#endif
+ if (argv[0] && argv[0][0] == '-')
+ isloginsh = 1;
+ if (isloginsh) {
+ state = 1;
+ read_profile("/etc/profile");
+ state1:
+ state = 2;
+ read_profile(".profile");
+ }
+ state2:
+ state = 3;
+ if (
+#ifndef linux
+ getuid() == geteuid() && getgid() == getegid() &&
+#endif
+ iflag
+ ) {
+ shinit = lookupvar("ENV");
+ if (shinit != NULL && *shinit != '\0') {
+ read_profile(shinit);
+ }
+ }
+ state3:
+ state = 4;
+ if (minusc)
+ evalstring(minusc, 0);
+
+ if (sflag || minusc == NULL) {
+#if ENABLE_FEATURE_EDITING_SAVEHISTORY
+ if ( iflag ) {
+ const char *hp = lookupvar("HISTFILE");
+
+ if (hp != NULL)
+ line_input_state->hist_file = hp;
+ }
+#endif
+ state4: /* XXX ??? - why isn't this before the "if" statement */
+ cmdloop(1);
+ }
+#if PROFILE
+ monitor(0);
+#endif
+#ifdef GPROF
+ {
+ extern void _mcleanup(void);
+ _mcleanup();
+ }
+#endif
+ exitshell();
+ /* NOTREACHED */
+}
+
+#if DEBUG
+const char *applet_name = "debug stuff usage";
+int main(int argc, char **argv)
+{
+ return ash_main(argc, argv);
+}
+#endif
+
+
+/*-
+ * Copyright (c) 1989, 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
diff --git a/i/pc104/initrd/conf/busybox/shell/ash_test/ash-alias/alias.right b/i/pc104/initrd/conf/busybox/shell/ash_test/ash-alias/alias.right
new file mode 100644
index 0000000..0667b21
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/shell/ash_test/ash-alias/alias.right
@@ -0,0 +1,4 @@
+alias: 0
+alias: 0
+./alias.tests: line 25: qfoo: not found
+quux
diff --git a/i/pc104/initrd/conf/busybox/shell/ash_test/ash-alias/alias.tests b/i/pc104/initrd/conf/busybox/shell/ash_test/ash-alias/alias.tests
new file mode 100755
index 0000000..8d07b0b
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/shell/ash_test/ash-alias/alias.tests
@@ -0,0 +1,37 @@
+# place holder for future alias testing
+#ash# shopt -s expand_aliases
+
+# alias/unalias tests originally in builtins.tests
+
+unalias -a
+# this should return success, according to POSIX.2
+alias
+echo alias: $?
+alias foo=bar
+unalias foo
+# this had better return success, according to POSIX.2
+alias
+echo alias: $?
+
+# bug in all versions through bash-2.05b
+
+unalias qfoo qbar qbaz quux 2>/dev/null
+
+alias qfoo=qbar
+alias qbar=qbaz
+alias qbaz=quux
+alias quux=qfoo
+
+qfoo
+
+unalias qfoo qbar qbaz quux
+
+unalias -a
+
+alias foo='echo '
+alias bar=baz
+alias baz=quux
+
+foo bar
+
+unalias foo bar baz
diff --git a/i/pc104/initrd/conf/busybox/shell/ash_test/ash-arith/README.ash b/i/pc104/initrd/conf/busybox/shell/ash_test/ash-arith/README.ash
new file mode 100644
index 0000000..7da22ef
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/shell/ash_test/ash-arith/README.ash
@@ -0,0 +1 @@
+there is no support for (( )) constructs in ash
diff --git a/i/pc104/initrd/conf/busybox/shell/ash_test/ash-arith/arith-for.right b/i/pc104/initrd/conf/busybox/shell/ash_test/ash-arith/arith-for.right
new file mode 100644
index 0000000..88dbc15
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/shell/ash_test/ash-arith/arith-for.right
@@ -0,0 +1,74 @@
+0
+1
+2
+0
+1
+2
+0
+1
+2
+0
+2
+4
+fx is a function
+fx ()
+{
+ i=0;
+ for ((1; i < 3; i++ ))
+ do
+ echo $i;
+ done;
+ for ((i=0; 1; i++ ))
+ do
+ if (( i >= 3 )); then
+ break;
+ fi;
+ echo $i;
+ done;
+ for ((i=0; i<3; 1))
+ do
+ echo $i;
+ (( i++ ));
+ done;
+ i=0;
+ for ((1; 1; 1))
+ do
+ if (( i > 2 )); then
+ break;
+ fi;
+ echo $i;
+ (( i++ ));
+ done;
+ i=0;
+ for ((1; 1; 1))
+ do
+ if (( i > 2 )); then
+ break;
+ fi;
+ echo $i;
+ (( i++ ));
+ done
+}
+0
+1
+2
+0
+1
+2
+0
+1
+2
+0
+1
+2
+0
+1
+2
+./arith-for.tests: line 77: syntax error: arithmetic expression required
+./arith-for.tests: line 77: syntax error: `(( i=0; "i < 3" ))'
+2
+./arith-for.tests: line 83: syntax error: `;' unexpected
+./arith-for.tests: line 83: syntax error: `(( i=0; i < 3; i++; 7 ))'
+2
+20
+20
diff --git a/i/pc104/initrd/conf/busybox/shell/ash_test/ash-arith/arith-for.testsx b/i/pc104/initrd/conf/busybox/shell/ash_test/ash-arith/arith-for.testsx
new file mode 100755
index 0000000..4fa30ff
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/shell/ash_test/ash-arith/arith-for.testsx
@@ -0,0 +1,94 @@
+fx()
+{
+i=0
+for (( ; i < 3; i++ ))
+do
+ echo $i
+done
+
+for (( i=0; ; i++ ))
+do
+ if (( i >= 3 )); then
+ break;
+ fi
+ echo $i
+done
+
+for (( i=0; i<3; ))
+do
+ echo $i
+ (( i++ ))
+done
+
+i=0
+for (( ; ; ))
+do
+ if (( i > 2 )); then
+ break;
+ fi
+ echo $i;
+ (( i++ ))
+done
+
+i=0
+for ((;;))
+do
+ if (( i > 2 )); then
+ break;
+ fi
+ echo $i;
+ (( i++ ))
+done
+}
+
+for (( i=0; "i < 3" ; i++ ))
+do
+ echo $i
+done
+
+i=0
+for (( ; "i < 3"; i++ ))
+do
+ echo $i
+done
+
+for (( i=0; ; i++ ))
+do
+ if (( i >= 3 )); then
+ break;
+ fi
+ echo $i
+done
+
+for ((i = 0; ;i++ ))
+do
+ echo $i
+ if (( i < 3 )); then
+ (( i++ ))
+ continue;
+ fi
+ break
+done
+
+type fx
+fx
+
+# errors
+for (( i=0; "i < 3" ))
+do
+ echo $i
+done
+echo $?
+
+for (( i=0; i < 3; i++; 7 ))
+do
+ echo $i
+done
+echo $?
+
+# one-liners added in post-bash-2.04
+for ((i=0; i < 20; i++)) do : ; done
+echo $i
+
+for ((i=0; i < 20; i++)) { : ; }
+echo $i
diff --git a/i/pc104/initrd/conf/busybox/shell/ash_test/ash-arith/arith.right b/i/pc104/initrd/conf/busybox/shell/ash_test/ash-arith/arith.right
new file mode 100644
index 0000000..3ea7ce6
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/shell/ash_test/ash-arith/arith.right
@@ -0,0 +1,138 @@
+Format: 'expected actual'
+163 163
+4 4
+16 16
+8 8
+2 2
+4 4
+2 2
+2 2
+1 1
+0 0
+0 0
+0 0
+1 1
+1 1
+2 2
+-3 -3
+-2 -2
+1 1
+0 0
+2 2
+131072 131072
+29 29
+33 33
+49 49
+1 1
+1 1
+0 0
+0 0
+1 1
+1 1
+1 1
+2 2
+3 3
+1 1
+58 58
+2 2
+60 60
+1 1
+256 256
+16 16
+62 62
+4 4
+29 29
+5 5
+-4 -4
+4 4
+1 1
+32 32
+32 32
+1 1
+1 1
+32 32
+20 20
+30 30
+20 20
+30 30
+./arith.tests: line 117: syntax error: 1 ? 20 : x+=2
+6 6
+6,5,3 6,5,3
+263 263
+255 255
+40 40
+./arith.tests: line 163: syntax error: 7 = 43
+./arith.tests: line 165: divide by zero
+./arith.tests: let: line 166: syntax error: jv += $iv
+./arith.tests: line 167: syntax error: jv += $iv
+./arith.tests: let: line 168: syntax error: rv = 7 + (43 * 6
+abc
+def
+ghi
+./arith.tests: line 191: syntax error: ( 4 + A ) + 4
+16 16
+./arith.tests: line 196: syntax error: 4 ? : 3 + 5
+./arith.tests: line 197: syntax error: 1 ? 20
+./arith.tests: line 198: syntax error: 4 ? 20 :
+9 9
+./arith.tests: line 205: syntax error: 0 && B=42
+./arith.tests: line 208: syntax error: 1 || B=88
+9 9
+9 9
+9 9
+7 7
+7
+4 4
+32767 32767
+32768 32768
+131072 131072
+2147483647 2147483647
+1 1
+4 4
+4 4
+5 5
+5 5
+4 4
+3 3
+3 3
+4 4
+4 4
+./arith.tests: line 257: syntax error: 7--
+./arith.tests: line 259: syntax error: --x=7
+./arith.tests: line 260: syntax error: ++x=7
+./arith.tests: line 262: syntax error: x++=7
+./arith.tests: line 263: syntax error: x--=7
+4 4
+7 7
+-7 -7
+./arith1.sub: line 2: syntax error: 4--
+./arith1.sub: line 3: syntax error: 4++
+./arith1.sub: line 4: syntax error: 4 --
+./arith1.sub: line 5: syntax error: 4 ++
+6 6
+3 3
+7 7
+4 4
+0 0
+3 3
+7 7
+2 2
+-2 -2
+1 1
+./arith1.sub: line 37: syntax error: +++7
+./arith2.sub: line 2: syntax error: --7
+./arith2.sub: line 3: syntax error: ++7
+./arith2.sub: line 4: syntax error: -- 7
+./arith2.sub: line 5: syntax error: ++ 7
+5 5
+1 1
+4 4
+0 0
+./arith2.sub: line 42: syntax error: -- - 7
+./arith2.sub: line 47: syntax error: ++ + 7
+8 12
+./arith.tests: line 290: syntax error: a b
+42
+42
+42
+./arith.tests: line 302: a[b[c]d]=e: not found
diff --git a/i/pc104/initrd/conf/busybox/shell/ash_test/ash-arith/arith.tests b/i/pc104/initrd/conf/busybox/shell/ash_test/ash-arith/arith.tests
new file mode 100755
index 0000000..d65758e
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/shell/ash_test/ash-arith/arith.tests
@@ -0,0 +1,302 @@
+#ash# set +o posix
+#ash# declare -i iv jv
+
+echo "Format: 'expected actual'"
+
+iv=$(( 3 + 5 * 32 ))
+echo 163 $iv
+#ash# iv=iv+3
+#ash# echo 166 $iv
+iv=2
+jv=iv
+
+let "jv *= 2"
+echo 4 $jv
+jv=$(( $jv << 2 ))
+echo 16 $jv
+
+let jv="$jv / 2"
+echo 8 $jv
+#ash# jv="jv >> 2"
+ let jv="jv >> 2"
+echo 2 $jv
+
+iv=$((iv+ $jv))
+echo 4 $iv
+echo 2 $((iv -= jv))
+echo 2 $iv
+echo 1 $(( iv == jv ))
+echo 0 $(( iv != $jv ))
+echo 0 $(( iv < jv ))
+echo 0 $(( $iv > $jv ))
+echo 1 $(( iv <= $jv ))
+echo 1 $(( $iv >= jv ))
+
+echo 2 $jv
+echo -3 $(( ~$jv ))
+echo -2 $(( ~1 ))
+echo 1 $(( ! 0 ))
+
+echo 0 $(( jv % 2 ))
+echo 2 $(( $iv % 4 ))
+
+echo 131072 $(( iv <<= 16 ))
+echo 29 $(( iv %= 33 ))
+
+echo 33 $(( 33 & 55 ))
+echo 49 $(( 33 | 17 ))
+
+echo 1 $(( iv && $jv ))
+echo 1 $(( $iv || jv ))
+
+echo 0 $(( iv && 0 ))
+echo 0 $(( iv & 0 ))
+echo 1 $(( iv && 1 ))
+echo 1 $(( iv & 1 ))
+
+echo 1 $(( $jv || 0 ))
+echo 2 $(( jv | 0 ))
+echo 3 $(( jv | 1 ))
+echo 1 $(( $jv || 1 ))
+
+let 'iv *= jv'
+echo 58 $iv
+echo 2 $jv
+let "jv += $iv"
+echo 60 $jv
+
+echo 1 $(( jv /= iv ))
+echo 256 $(( jv <<= 8 ))
+echo 16 $(( jv >>= 4 ))
+
+echo 62 $(( iv |= 4 ))
+echo 4 $(( iv &= 4 ))
+
+echo 29 $(( iv += (jv + 9)))
+echo 5 $(( (iv + 4) % 7 ))
+
+# unary plus, minus
+echo -4 $(( +4 - 8 ))
+echo 4 $(( -4 + 8 ))
+
+# conditional expressions
+echo 1 $(( 4<5 ? 1 : 32))
+echo 32 $(( 4>5 ? 1 : 32))
+echo 32 $(( 4>(2+3) ? 1 : 32))
+echo 1 $(( 4<(2+3) ? 1 : 32))
+echo 1 $(( (2+2)<(2+3) ? 1 : 32))
+echo 32 $(( (2+2)>(2+3) ? 1 : 32))
+
+# check that the unevaluated part of the ternary operator does not do
+# evaluation or assignment
+x=i+=2
+y=j+=2
+#ash# declare -i i=1 j=1
+ i=1
+ j=1
+echo 20 $((1 ? 20 : (x+=2)))
+#ash# echo $i,$x # ash mishandles this
+echo 30 $((0 ? (y+=2) : 30))
+#ash# echo $j,$y # ash mishandles this
+
+x=i+=2
+y=j+=2
+#ash# declare -i i=1 j=1
+ i=1
+ j=1
+echo 20 $((1 ? 20 : (x+=2)))
+#ash# echo $i,$x # ash mishandles this
+echo 30 $((0 ? (y+=2) : 30))
+#ash# echo $i,$y # ash mishandles this
+
+# check precedence of assignment vs. conditional operator
+# should be an error
+#ash# declare -i x=2
+ x=2
+#ashnote# bash reports error but continues, ash aborts - using subshell to 'emulate' bash:
+( y=$((1 ? 20 : x+=2)) )
+
+# check precedence of assignment vs. conditional operator
+#ash# declare -i x=2
+ x=2
+# ash says "line NNN: syntax error: 0 ? x+=2 : 20"
+#ash# echo 20 $((0 ? x+=2 : 20))
+
+# associativity of assignment-operator operator
+#ash# declare -i i=1 j=2 k=3
+i=1
+j=2
+k=3
+echo 6 $((i += j += k))
+echo 6,5,3 $i,$j,$k
+
+# octal, hex
+echo 263 $(( 0x100 | 007 ))
+echo 255 $(( 0xff ))
+#ash# echo 255 $(( 16#ff ))
+#ash# echo 127 $(( 16#FF/2 ))
+#ash# echo 36 $(( 8#44 ))
+
+echo 40 $(( 8 ^ 32 ))
+
+#ash# # other bases
+#ash# echo 10 $(( 16#a ))
+#ash# echo 10 $(( 32#a ))
+#ash# echo 10 $(( 56#a ))
+#ash# echo 10 $(( 64#a ))
+#ash#
+#ash# echo 10 $(( 16#A ))
+#ash# echo 10 $(( 32#A ))
+#ash# echo 36 $(( 56#A ))
+#ash# echo 36 $(( 64#A ))
+#ash#
+#ash# echo 62 $(( 64#@ ))
+#ash# echo 63 $(( 64#_ ))
+
+#ash# # weird bases (error)
+#ash# echo $(( 3425#56 ))
+
+#ash# # missing number after base
+#ash# echo 0 $(( 2# ))
+
+# these should generate errors
+( echo $(( 7 = 43 )) )
+#ash# echo $(( 2#44 ))
+( echo $(( 44 / 0 )) )
+( let 'jv += $iv' )
+( echo $(( jv += \$iv )) )
+( let 'rv = 7 + (43 * 6' )
+
+#ash# # more errors
+#ash# declare -i i
+#ash# i=0#4
+#ash# i=2#110#11
+
+((echo abc; echo def;); echo ghi)
+
+#ash# if (((4+4) + (4 + 7))); then
+#ash# echo ok
+#ash# fi
+
+#ash# (()) # make sure the null expression works OK
+
+#ash# a=(0 2 4 6)
+#ash# echo 6 $(( a[1] + a[2] ))
+#ash# echo 1 $(( (a[1] + a[2]) == a[3] ))
+#ash# (( (a[1] + a[2]) == a[3] )) ; echo 0 $?
+
+# test pushing and popping the expression stack
+unset A
+A="4 + "
+( echo A $(( ( 4 + A ) + 4 )) )
+A="3 + 5"
+echo 16 $(( ( 4 + A ) + 4 ))
+
+# badly-formed conditional expressions
+( echo $(( 4 ? : $A )) )
+( echo $(( 1 ? 20 )) )
+( echo $(( 4 ? 20 : )) )
+
+# precedence and short-circuit evaluation
+B=9
+echo 9 $B
+
+# error
+( echo $(( 0 && B=42 )); echo 9 $B )
+
+# error
+( echo $(( 1 || B=88 )); echo 9 $B )
+
+# ash mistakenly evaluates B=... below
+#ash# echo 0 $(( 0 && (B=42) ))
+echo 9 $B
+#ash# echo 0 $(( (${$} - $$) && (B=42) ))
+echo 9 $B
+#ash# echo 1 $(( 1 || (B=88) ))
+echo 9 $B
+
+
+# until command with (( )) command
+x=7
+
+echo 7 $x
+#ash# until (( x == 4 ))
+ until test "$x" = 4
+do
+ echo $x
+ x=4
+done
+
+echo 4 $x
+
+# exponentiation
+echo 32767 $(( 2**15 - 1))
+echo 32768 $(( 2**(16-1)))
+echo 131072 $(( 2**16*2 ))
+echo 2147483647 $(( 2**31-1))
+echo 1 $(( 2**0 ))
+
+# {pre,post}-{inc,dec}rement and associated errors
+
+x=4
+
+echo 4 $x
+echo 4 $(( x++ ))
+echo 5 $x
+echo 5 $(( x-- ))
+echo 4 $x
+
+echo 3 $(( --x ))
+echo 3 $x
+
+echo 4 $(( ++x ))
+echo 4 $x
+
+# bash 3.2 apparently thinks that ++7 is 7
+#ash# echo 7 $(( ++7 ))
+( echo $(( 7-- )) )
+
+( echo $(( --x=7 )) )
+( echo $(( ++x=7 )) )
+
+( echo $(( x++=7 )) )
+( echo $(( x--=7 )) )
+
+echo 4 $x
+
+echo 7 $(( +7 ))
+echo -7 $(( -7 ))
+
+# bash 3.2 apparently thinks that ++7 is 7
+#ash# echo $(( ++7 ))
+#ash# echo $(( --7 ))
+
+${THIS_SH} ./arith1.sub
+${THIS_SH} ./arith2.sub
+
+x=4
+y=7
+
+#ash# (( x=8 , y=12 ))
+ x=8
+ y=12
+echo $x $y
+
+#ash# # should be an error
+#ash# (( x=9 y=41 ))
+
+# These are errors
+unset b
+( echo $((a b)) )
+#ash# ((a b))
+
+n=42
+printf "%d\n" $n
+printf "%i\n" $n
+#ash# echo $(( 8#$(printf "%o\n" $n) ))
+printf "%u\n" $n
+#ash# echo $(( 16#$(printf "%x\n" $n) ))
+#ash# echo $(( 16#$(printf "%X\n" $n) ))
+
+# causes longjmp botches through bash-2.05b
+a[b[c]d]=e
diff --git a/i/pc104/initrd/conf/busybox/shell/ash_test/ash-arith/arith1.sub b/i/pc104/initrd/conf/busybox/shell/ash_test/ash-arith/arith1.sub
new file mode 100755
index 0000000..80aa999
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/shell/ash_test/ash-arith/arith1.sub
@@ -0,0 +1,40 @@
+# test of redone post-increment and post-decrement code
+( echo $(( 4-- )) )
+( echo $(( 4++ )) )
+( echo $(( 4 -- )) )
+( echo $(( 4 ++ )) )
+
+#ash# (( array[0]++ ))
+#ash# echo ${array}
+
+#ash# (( array[0] ++ ))
+#ash# echo ${array}
+
+#ash# (( a++ ))
+#ash# echo $a
+#ash# (( a ++ ))
+#ash# echo $a
+ a=2
+
+echo 6 $(( a ++ + 4 ))
+echo 3 $a
+
+echo 7 $(( a+++4 ))
+echo 4 $a
+
+echo 0 $(( a---4 ))
+echo 3 $a
+
+echo 7 $(( a -- + 4 ))
+echo 2 $a
+
+echo -2 $(( a -- - 4 ))
+echo 1 $a
+
+#ash# (( ++ + 7 ))
+
+#ash# (( ++ ))
+( echo $(( +++7 )) )
+# bash 3.2 apparently thinks that ++ +7 is 7
+#ash# echo $(( ++ + 7 ))
+#ash# (( -- ))
diff --git a/i/pc104/initrd/conf/busybox/shell/ash_test/ash-arith/arith2.sub b/i/pc104/initrd/conf/busybox/shell/ash_test/ash-arith/arith2.sub
new file mode 100755
index 0000000..f7e3c92
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/shell/ash_test/ash-arith/arith2.sub
@@ -0,0 +1,57 @@
+# bash 3.2 apparently thinks that ++7 is 7 etc
+( echo $(( --7 )) )
+( echo $(( ++7 )) )
+( echo $(( -- 7 )) )
+( echo $(( ++ 7 )) )
+
+#ash# ((++array[0] ))
+#ash# echo 1 $array
+#ash# (( ++ array[0] ))
+#ash# echo 2 $array
+
+#ash# (( ++a ))
+#ash# echo 1 $a
+#ash# (( ++ a ))
+#ash# echo 2 $a
+
+#ash# (( --a ))
+#ash# echo 1 $a
+#ash# (( -- a ))
+#ash# echo 0 $a
+ a=0
+
+echo 5 $(( 4 + ++a ))
+echo 1 $a
+
+# ash doesn't handle it right...
+#ash# echo 6 $(( 4+++a ))
+#ash# echo 2 $a
+ a=2
+
+# ash doesn't handle it right...
+#ash# echo 3 $(( 4---a ))
+#ash# echo 1 $a
+ a=1
+
+echo 4 $(( 4 - -- a ))
+echo 0 $a
+
+#ash# (( -- ))
+# bash 3.2 apparently thinks that ---7 is -7
+#ash# echo $(( ---7 ))
+( echo $(( -- - 7 )) )
+
+#ash# (( ++ ))
+# bash 3.2: 7
+#ash# echo 7 $(( ++7 ))
+( echo $(( ++ + 7 )) )
+
+# bash 3.2: -7
+#ash# echo -7 $(( ++-7 ))
+# bash 3.2: -7
+#ash# echo -7 $(( ++ - 7 ))
+
+# bash 3.2: 7
+#ash# echo 7 $(( +--7 ))
+# bash 3.2: 7
+#ash# echo 7 $(( -- + 7 ))
diff --git a/i/pc104/initrd/conf/busybox/shell/ash_test/ash-heredoc/heredoc.right b/i/pc104/initrd/conf/busybox/shell/ash_test/ash-heredoc/heredoc.right
new file mode 100644
index 0000000..baf1151
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/shell/ash_test/ash-heredoc/heredoc.right
@@ -0,0 +1,21 @@
+there
+one - alpha
+two - beta
+three - gamma
+hi\
+there$a
+stuff
+hi\
+there
+EO\
+F
+hi
+tab 1
+tab 2
+tab 3
+abc
+def ghi
+jkl mno
+fff is a shell function
+hi
+there
diff --git a/i/pc104/initrd/conf/busybox/shell/ash_test/ash-heredoc/heredoc.tests b/i/pc104/initrd/conf/busybox/shell/ash_test/ash-heredoc/heredoc.tests
new file mode 100755
index 0000000..b3cdc3f
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/shell/ash_test/ash-heredoc/heredoc.tests
@@ -0,0 +1,94 @@
+# check order and content of multiple here docs
+
+cat << EOF1 << EOF2
+hi
+EOF1
+there
+EOF2
+
+while read line1; do
+ read line2 <&3
+ echo $line1 - $line2
+done <<EOF1 3<<EOF2
+one
+two
+three
+EOF1
+alpha
+beta
+gamma
+EOF2
+
+
+# check quoted here-doc is protected
+
+a=foo
+cat << 'EOF'
+hi\
+there$a
+stuff
+EOF
+
+# check that quoted here-documents don't have \newline processing done
+
+cat << 'EOF'
+hi\
+there
+EO\
+F
+EOF
+true
+
+# check that \newline is removed at start of here-doc
+cat << EO\
+F
+hi
+EOF
+
+#ash# # check that \newline removal works for here-doc delimiter
+#ash# cat << EOF
+#ash# hi
+#ash# EO\
+#ash# F
+
+# check operation of tab removal in here documents
+cat <<- EOF
+ tab 1
+ tab 2
+ tab 3
+ EOF
+
+# check appending of text to file from here document
+rm -f /tmp/bash-zzz
+cat > /tmp/bash-zzz << EOF
+abc
+EOF
+cat >> /tmp/bash-zzz << EOF
+def ghi
+jkl mno
+EOF
+cat /tmp/bash-zzz
+rm -f /tmp/bash-zzz
+
+# make sure command printing puts the here-document as the last redirection
+# on the line, and the function export code preserves syntactic correctness
+fff()
+{
+ ed /tmp/foo <<ENDOFINPUT >/dev/null
+/^name/d
+w
+q
+ENDOFINPUT
+aa=1
+}
+
+type fff
+#ash# export -f fff
+#ash# ${THIS_SH} -c 'type fff'
+
+# check that end of file delimits a here-document
+# THIS MUST BE LAST!
+
+cat << EOF
+hi
+there
diff --git a/i/pc104/initrd/conf/busybox/shell/ash_test/ash-invert/invert.right b/i/pc104/initrd/conf/busybox/shell/ash_test/ash-invert/invert.right
new file mode 100644
index 0000000..5a9239a
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/shell/ash_test/ash-invert/invert.right
@@ -0,0 +1,10 @@
+1
+1
+1
+0
+0
+1
+0
+1
+0
+1
diff --git a/i/pc104/initrd/conf/busybox/shell/ash_test/ash-invert/invert.tests b/i/pc104/initrd/conf/busybox/shell/ash_test/ash-invert/invert.tests
new file mode 100755
index 0000000..8393d95
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/shell/ash_test/ash-invert/invert.tests
@@ -0,0 +1,19 @@
+# tests of return value inversion
+# placeholder for future expansion
+
+# user subshells (...) did this wrong in bash versions before 2.04
+
+! ( echo hello | grep h >/dev/null 2>&1 ); echo $?
+! echo hello | grep h >/dev/null 2>&1 ; echo $?
+
+! true ; echo $?
+! false; echo $?
+
+! (false) ; echo $?
+! (true); echo $?
+
+! true | false ; echo $?
+! false | true ; echo $?
+
+! (true | false) ; echo $?
+! (false | true) ; echo $?
diff --git a/i/pc104/initrd/conf/busybox/shell/ash_test/printenv.c b/i/pc104/initrd/conf/busybox/shell/ash_test/printenv.c
new file mode 100644
index 0000000..06df21f
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/shell/ash_test/printenv.c
@@ -0,0 +1,67 @@
+/* printenv -- minimal clone of BSD printenv(1).
+
+ usage: printenv [varname]
+
+ Chet Ramey
+ chet@po.cwru.edu
+*/
+
+/* Copyright (C) 1997-2002 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2, or (at your option) any later
+ version.
+
+ Bash is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with Bash; see the file COPYING. If not, write to the Free Software
+ Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
+
+#include <stdlib.h>
+#include <string.h>
+
+extern char **environ;
+
+int
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ register char **envp, *eval;
+ int len;
+
+ argv++;
+ argc--;
+
+ /* printenv */
+ if (argc == 0)
+ {
+ for (envp = environ; *envp; envp++)
+ puts (*envp);
+ exit (0);
+ }
+
+ /* printenv varname */
+ len = strlen (*argv);
+ for (envp = environ; *envp; envp++)
+ {
+ if (**argv == **envp && strncmp (*envp, *argv, len) == 0)
+ {
+ eval = *envp + len;
+ /* If the environment variable doesn't have an `=', ignore it. */
+ if (*eval == '=')
+ {
+ puts (eval + 1);
+ exit (0);
+ }
+ }
+ }
+ exit (1);
+}
diff --git a/i/pc104/initrd/conf/busybox/shell/ash_test/recho.c b/i/pc104/initrd/conf/busybox/shell/ash_test/recho.c
new file mode 100644
index 0000000..02be0d7
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/shell/ash_test/recho.c
@@ -0,0 +1,63 @@
+/*
+ recho -- really echo args, bracketed with <> and with invisible chars
+ made visible.
+
+ Chet Ramey
+ chet@po.cwru.edu
+*/
+
+/* Copyright (C) 2002-2005 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2, or (at your option) any later
+ version.
+
+ Bash is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with Bash; see the file COPYING. If not, write to the Free Software
+ Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+void strprint();
+
+int
+main(argc, argv)
+int argc;
+char **argv;
+{
+ register int i;
+
+ for (i = 1; i < argc; i++) {
+ printf("argv[%d] = <", i);
+ strprint(argv[i]);
+ printf(">\n");
+ }
+ exit(0);
+}
+
+void
+strprint(str)
+char *str;
+{
+ register unsigned char *s;
+
+ for (s = (unsigned char *)str; s && *s; s++) {
+ if (*s < ' ') {
+ putchar('^');
+ putchar(*s+64);
+ } else if (*s == 127) {
+ putchar('^');
+ putchar('?');
+ } else
+ putchar(*s);
+ }
+}
diff --git a/i/pc104/initrd/conf/busybox/shell/ash_test/run-all b/i/pc104/initrd/conf/busybox/shell/ash_test/run-all
new file mode 100755
index 0000000..02f6d2a
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/shell/ash_test/run-all
@@ -0,0 +1,62 @@
+#!/bin/sh
+
+test -x ash || { echo "No ./ash?!"; exit; }
+test -x printenv || gcc -O2 -o printenv printenv.c || exit $?
+test -x recho || gcc -O2 -o recho recho.c || exit $?
+test -x zecho || gcc -O2 -o zecho zecho.c || exit $?
+
+PATH="$PWD:$PATH" # for ash and recho/zecho/printenv
+export PATH
+
+THIS_SH="$PWD/ash"
+export THIS_SH
+
+do_test()
+{
+ test -d "$1" || return 0
+ (
+ cd "$1" || { echo "cannot cd $1!"; exit 1; }
+ for x in run-*; do
+ test -f "$x" || continue
+ case "$x" in
+ "$0"|run-minimal|run-gprof) ;;
+ *.orig|*~) ;;
+ #*) echo $x ; sh $x ;;
+ *)
+ sh "$x" >"../$1-$x.fail" 2>&1 && \
+ { echo "$1/$x: ok"; rm "../$1-$x.fail"; } || echo "$1/$x: fail";
+ ;;
+ esac
+ done
+ # Many bash run-XXX scripts just do this,
+ # no point in duplication it all over the place
+ for x in *.tests; do
+ test -x "$x" || continue
+ name="${x%%.tests}"
+ test -f "$name.right" || continue
+ {
+ "$THIS_SH" "./$x" >"$name.xx" 2>&1
+ diff -u "$name.xx" "$name.right" >"../$1-$x.fail" && rm -f "$name.xx" "../$1-$x.fail"
+ } && echo "$1/$x: ok" || echo "$1/$x: fail"
+ done
+ )
+}
+
+# main part of this script
+# Usage: run-all [directories]
+
+if [ $# -lt 1 ]; then
+ # All sub directories
+ modules=`ls -d ash-*`
+
+ for module in $modules; do
+ do_test $module
+ done
+else
+ while [ $# -ge 1 ]; do
+ if [ -d $1 ]; then
+ do_test $1
+ fi
+ shift
+ done
+fi
diff --git a/i/pc104/initrd/conf/busybox/shell/ash_test/zecho.c b/i/pc104/initrd/conf/busybox/shell/ash_test/zecho.c
new file mode 100644
index 0000000..621d06d
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/shell/ash_test/zecho.c
@@ -0,0 +1,39 @@
+/* zecho - bare-bones echo */
+
+/* Copyright (C) 1996-2002 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2, or (at your option) any later
+ version.
+
+ Bash is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with Bash; see the file COPYING. If not, write to the Free Software
+ Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+int
+main(argc, argv)
+int argc;
+char **argv;
+{
+ argv++;
+
+ while (*argv) {
+ (void)printf("%s", *argv);
+ if (*++argv)
+ putchar(' ');
+ }
+
+ putchar('\n');
+ exit(0);
+}
diff --git a/i/pc104/initrd/conf/busybox/shell/bbsh.c b/i/pc104/initrd/conf/busybox/shell/bbsh.c
new file mode 100644
index 0000000..fe27f44
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/shell/bbsh.c
@@ -0,0 +1,223 @@
+/* vi: set ts=4 :
+ *
+ * bbsh - busybox shell
+ *
+ * Copyright 2006 Rob Landley <rob@landley.net>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+// A section of code that gets repeatedly or conditionally executed is stored
+// as a string and parsed each time it's run.
+
+
+
+// Wheee, debugging.
+
+// Terminal control
+#define ENABLE_BBSH_TTY 0
+
+// &, fg, bg, jobs. (ctrl-z with tty.)
+#define ENABLE_BBSH_JOBCTL 0
+
+// Flow control (if, while, for, functions { })
+#define ENABLE_BBSH_FLOWCTL 0
+
+#define ENABLE_BBSH_ENVVARS 0 // Environment variable support
+
+// Local and synthetic variables, fancy prompts, set, $?, etc.
+#define ENABLE_BBSH_LOCALVARS 0
+
+// Pipes and redirects: | > < >> << && || & () ;
+#define ENABLE_BBSH_PIPES 0
+
+/* Fun:
+
+ echo `echo hello#comment " woot` and more
+*/
+
+#include "busybox.h"
+
+// A single executable, its arguments, and other information we know about it.
+#define BBSH_FLAG_EXIT 1
+#define BBSH_FLAG_SUSPEND 2
+#define BBSH_FLAG_PIPE 4
+#define BBSH_FLAG_AND 8
+#define BBSH_FLAG_OR 16
+#define BBSH_FLAG_AMP 32
+#define BBSH_FLAG_SEMI 64
+#define BBSH_FLAG_PAREN 128
+
+// What we know about a single process.
+struct command {
+ struct command *next;
+ int flags; // exit, suspend, && ||
+ int pid; // pid (or exit code)
+ int argc;
+ char *argv[0];
+};
+
+// A collection of processes piped into/waiting on each other.
+struct pipeline {
+ struct pipeline *next;
+ int job_id;
+ struct command *cmd;
+ char *cmdline;
+ int cmdlinelen;
+};
+
+static void free_list(void *list, void (*freeit)(void *data))
+{
+ while(list) {
+ void **next = (void **)list;
+ void *list_next = *next;
+ freeit(list);
+ free(list);
+ list = list_next;
+ }
+}
+
+// Parse one word from the command line, appending one or more argv[] entries
+// to struct command. Handles environment variable substitution and
+// substrings. Returns pointer to next used byte, or NULL if it
+// hit an ending token.
+static char *parse_word(char *start, struct command **cmd)
+{
+ char *end;
+
+ // Detect end of line (and truncate line at comment)
+ if (ENABLE_BBSH_PIPES && strchr("><&|(;", *start)) return 0;
+
+ // Grab next word. (Add dequote and envvar logic here)
+ end = start;
+ while (*end && !isspace(*end)) end++;
+ (*cmd)->argv[(*cmd)->argc++] = xstrndup(start, end-start);
+
+ // Allocate more space if there's no room for NULL terminator.
+
+ if (!((*cmd)->argc & 7))
+ *cmd = xrealloc(*cmd,
+ sizeof(struct command) + ((*cmd)->argc+8)*sizeof(char *));
+ (*cmd)->argv[(*cmd)->argc] = 0;
+ return end;
+}
+
+// Parse a line of text into a pipeline.
+// Returns a pointer to the next line.
+
+static char *parse_pipeline(char *cmdline, struct pipeline *line)
+{
+ struct command **cmd = &(line->cmd);
+ char *start = line->cmdline = cmdline;
+
+ if (!cmdline) return 0;
+
+ if (ENABLE_BBSH_JOBCTL) line->cmdline = cmdline;
+
+ // Parse command into argv[]
+ for (;;) {
+ char *end;
+
+ // Skip leading whitespace and detect end of line.
+ start = skip_whitespace(start);
+ if (!*start || *start=='#') {
+ if (ENABLE_BBSH_JOBCTL) line->cmdlinelen = start-cmdline;
+ return 0;
+ }
+
+ // Allocate next command structure if necessary
+ if (!*cmd) *cmd = xzalloc(sizeof(struct command)+8*sizeof(char *));
+
+ // Parse next argument and add the results to argv[]
+ end = parse_word(start, cmd);
+
+ // If we hit the end of this command, how did it end?
+ if (!end) {
+ if (ENABLE_BBSH_PIPES && *start) {
+ if (*start==';') {
+ start++;
+ break;
+ }
+ // handle | & < > >> << || &&
+ }
+ break;
+ }
+ start = end;
+ }
+
+ if (ENABLE_BBSH_JOBCTL) line->cmdlinelen = start-cmdline;
+
+ return start;
+}
+
+// Execute the commands in a pipeline
+static int run_pipeline(struct pipeline *line)
+{
+ struct command *cmd = line->cmd;
+ if (!cmd || !cmd->argc) return 0;
+
+ // Handle local commands. This is totally fake and plastic.
+ if (cmd->argc==2 && !strcmp(cmd->argv[0],"cd"))
+ chdir(cmd->argv[1]);
+ else if(!strcmp(cmd->argv[0],"exit"))
+ exit(cmd->argc>1 ? atoi(cmd->argv[1]) : 0);
+ else {
+ int status;
+ pid_t pid=fork();
+ if(!pid) {
+ run_applet_by_name(cmd->argv[0],cmd->argc,cmd->argv);
+ execvp(cmd->argv[0],cmd->argv);
+ printf("No %s",cmd->argv[0]);
+ exit(1);
+ } else waitpid(pid, &status, 0);
+ }
+
+ return 0;
+}
+
+static void free_cmd(void *data)
+{
+ struct command *cmd=(struct command *)data;
+
+ while(cmd->argc) free(cmd->argv[--cmd->argc]);
+}
+
+
+static void handle(char *command)
+{
+ struct pipeline line;
+ char *start = command;
+
+ for (;;) {
+ memset(&line,0,sizeof(struct pipeline));
+ start = parse_pipeline(start, &line);
+ if (!line.cmd) break;
+
+ run_pipeline(&line);
+ free_list(line.cmd, free_cmd);
+ }
+}
+
+int bbsh_main(int argc, char *argv[]);
+int bbsh_main(int argc, char *argv[])
+{
+ char *command=NULL;
+ FILE *f;
+
+ getopt32(argc, argv, "c:", &command);
+
+ f = argv[optind] ? xfopen(argv[optind],"r") : NULL;
+ if (command) handle(command);
+ else {
+ unsigned cmdlen=0;
+ for (;;) {
+ if(!f) putchar('$');
+ if(1 > getline(&command, &cmdlen,f ? : stdin)) break;
+
+ handle(command);
+ }
+ if (ENABLE_FEATURE_CLEAN_UP) free(command);
+ }
+
+ return 1;
+}
diff --git a/i/pc104/initrd/conf/busybox/shell/hush.c b/i/pc104/initrd/conf/busybox/shell/hush.c
new file mode 100644
index 0000000..7658aeb
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/shell/hush.c
@@ -0,0 +1,2928 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * sh.c -- a prototype Bourne shell grammar parser
+ * Intended to follow the original Thompson and Ritchie
+ * "small and simple is beautiful" philosophy, which
+ * incidentally is a good match to today's BusyBox.
+ *
+ * Copyright (C) 2000,2001 Larry Doolittle <larry@doolittle.boa.org>
+ *
+ * Credits:
+ * The parser routines proper are all original material, first
+ * written Dec 2000 and Jan 2001 by Larry Doolittle. The
+ * execution engine, the builtins, and much of the underlying
+ * support has been adapted from busybox-0.49pre's lash, which is
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ * written by Erik Andersen <andersen@codepoet.org>. That, in turn,
+ * is based in part on ladsh.c, by Michael K. Johnson and Erik W.
+ * Troan, which they placed in the public domain. I don't know
+ * how much of the Johnson/Troan code has survived the repeated
+ * rewrites.
+ *
+ * Other credits:
+ * simple_itoa() was lifted from boa-0.93.15
+ * b_addchr() derived from similar w_addchar function in glibc-2.2
+ * setup_redirect(), redirect_opt_num(), and big chunks of main()
+ * and many builtins derived from contributions by Erik Andersen
+ * miscellaneous bugfixes from Matt Kraai
+ *
+ * There are two big (and related) architecture differences between
+ * this parser and the lash parser. One is that this version is
+ * actually designed from the ground up to understand nearly all
+ * of the Bourne grammar. The second, consequential change is that
+ * the parser and input reader have been turned inside out. Now,
+ * the parser is in control, and asks for input as needed. The old
+ * way had the input reader in control, and it asked for parsing to
+ * take place as needed. The new way makes it much easier to properly
+ * handle the recursion implicit in the various substitutions, especially
+ * across continuation lines.
+ *
+ * Bash grammar not implemented: (how many of these were in original sh?)
+ * $@ (those sure look like weird quoting rules)
+ * $_
+ * ! negation operator for pipes
+ * &> and >& redirection of stdout+stderr
+ * Brace Expansion
+ * Tilde Expansion
+ * fancy forms of Parameter Expansion
+ * aliases
+ * Arithmetic Expansion
+ * <(list) and >(list) Process Substitution
+ * reserved words: case, esac, select, function
+ * Here Documents ( << word )
+ * Functions
+ * Major bugs:
+ * job handling woefully incomplete and buggy
+ * reserved word execution woefully incomplete and buggy
+ * to-do:
+ * port selected bugfixes from post-0.49 busybox lash - done?
+ * finish implementing reserved words: for, while, until, do, done
+ * change { and } from special chars to reserved words
+ * builtins: break, continue, eval, return, set, trap, ulimit
+ * test magic exec
+ * handle children going into background
+ * clean up recognition of null pipes
+ * check setting of global_argc and global_argv
+ * control-C handling, probably with longjmp
+ * follow IFS rules more precisely, including update semantics
+ * figure out what to do with backslash-newline
+ * explain why we use signal instead of sigaction
+ * propagate syntax errors, die on resource errors?
+ * continuation lines, both explicit and implicit - done?
+ * memory leak finding and plugging - done?
+ * more testing, especially quoting rules and redirection
+ * document how quoting rules not precisely followed for variable assignments
+ * maybe change map[] to use 2-bit entries
+ * (eventually) remove all the printf's
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ */
+
+#include "busybox.h"
+#include <glob.h> /* glob, of course */
+#include <getopt.h> /* should be pretty obvious */
+
+//#include <sys/wait.h>
+//#include <signal.h>
+
+/* #include <dmalloc.h> */
+/* #define DEBUG_SHELL */
+
+
+#define SPECIAL_VAR_SYMBOL 03
+#define FLAG_EXIT_FROM_LOOP 1
+#define FLAG_PARSE_SEMICOLON (1 << 1) /* symbol ';' is special for parser */
+#define FLAG_REPARSING (1 << 2) /* >=2nd pass */
+
+typedef enum {
+ REDIRECT_INPUT = 1,
+ REDIRECT_OVERWRITE = 2,
+ REDIRECT_APPEND = 3,
+ REDIRECT_HEREIS = 4,
+ REDIRECT_IO = 5
+} redir_type;
+
+/* The descrip member of this structure is only used to make debugging
+ * output pretty */
+static const struct {int mode; int default_fd; const char *descrip;} redir_table[] = {
+ { 0, 0, "()" },
+ { O_RDONLY, 0, "<" },
+ { O_CREAT|O_TRUNC|O_WRONLY, 1, ">" },
+ { O_CREAT|O_APPEND|O_WRONLY, 1, ">>" },
+ { O_RDONLY, -1, "<<" },
+ { O_RDWR, 1, "<>" }
+};
+
+typedef enum {
+ PIPE_SEQ = 1,
+ PIPE_AND = 2,
+ PIPE_OR = 3,
+ PIPE_BG = 4,
+} pipe_style;
+
+/* might eventually control execution */
+typedef enum {
+ RES_NONE = 0,
+ RES_IF = 1,
+ RES_THEN = 2,
+ RES_ELIF = 3,
+ RES_ELSE = 4,
+ RES_FI = 5,
+ RES_FOR = 6,
+ RES_WHILE = 7,
+ RES_UNTIL = 8,
+ RES_DO = 9,
+ RES_DONE = 10,
+ RES_XXXX = 11,
+ RES_IN = 12,
+ RES_SNTX = 13
+} reserved_style;
+#define FLAG_END (1<<RES_NONE)
+#define FLAG_IF (1<<RES_IF)
+#define FLAG_THEN (1<<RES_THEN)
+#define FLAG_ELIF (1<<RES_ELIF)
+#define FLAG_ELSE (1<<RES_ELSE)
+#define FLAG_FI (1<<RES_FI)
+#define FLAG_FOR (1<<RES_FOR)
+#define FLAG_WHILE (1<<RES_WHILE)
+#define FLAG_UNTIL (1<<RES_UNTIL)
+#define FLAG_DO (1<<RES_DO)
+#define FLAG_DONE (1<<RES_DONE)
+#define FLAG_IN (1<<RES_IN)
+#define FLAG_START (1<<RES_XXXX)
+
+/* This holds pointers to the various results of parsing */
+struct p_context {
+ struct child_prog *child;
+ struct pipe *list_head;
+ struct pipe *pipe;
+ struct redir_struct *pending_redirect;
+ reserved_style w;
+ int old_flag; /* for figuring out valid reserved words */
+ struct p_context *stack;
+ int type; /* define type of parser : ";$" common or special symbol */
+ /* How about quoting status? */
+};
+
+struct redir_struct {
+ redir_type type; /* type of redirection */
+ int fd; /* file descriptor being redirected */
+ int dup; /* -1, or file descriptor being duplicated */
+ struct redir_struct *next; /* pointer to the next redirect in the list */
+ glob_t word; /* *word.gl_pathv is the filename */
+};
+
+struct child_prog {
+ pid_t pid; /* 0 if exited */
+ char **argv; /* program name and arguments */
+ struct pipe *group; /* if non-NULL, first in group or subshell */
+ int subshell; /* flag, non-zero if group must be forked */
+ struct redir_struct *redirects; /* I/O redirections */
+ glob_t glob_result; /* result of parameter globbing */
+ int is_stopped; /* is the program currently running? */
+ struct pipe *family; /* pointer back to the child's parent pipe */
+ int sp; /* number of SPECIAL_VAR_SYMBOL */
+ int type;
+};
+
+struct pipe {
+ int jobid; /* job number */
+ int num_progs; /* total number of programs in job */
+ int running_progs; /* number of programs running */
+ char *text; /* name of job */
+ char *cmdbuf; /* buffer various argv's point into */
+ pid_t pgrp; /* process group ID for the job */
+ struct child_prog *progs; /* array of commands in pipe */
+ struct pipe *next; /* to track background commands */
+ int stopped_progs; /* number of programs alive, but stopped */
+ int job_context; /* bitmask defining current context */
+ pipe_style followup; /* PIPE_BG, PIPE_SEQ, PIPE_OR, PIPE_AND */
+ reserved_style r_mode; /* supports if, for, while, until */
+};
+
+struct close_me {
+ int fd;
+ struct close_me *next;
+};
+
+struct variables {
+ const char *name;
+ const char *value;
+ int flg_export;
+ int flg_read_only;
+ struct variables *next;
+};
+
+/* globals, connect us to the outside world
+ * the first three support $?, $#, and $1 */
+static char **global_argv;
+static int global_argc;
+static int last_return_code;
+extern char **environ; /* This is in <unistd.h>, but protected with __USE_GNU */
+
+/* "globals" within this file */
+static const char *ifs;
+static unsigned char map[256];
+static int fake_mode;
+static int interactive;
+static struct close_me *close_me_head;
+static const char *cwd;
+static struct pipe *job_list;
+static unsigned last_bg_pid;
+static int last_jobid;
+static unsigned shell_terminal;
+static const char *PS1;
+static const char *PS2;
+static struct variables shell_ver = { "HUSH_VERSION", "0.01", 1, 1, 0 };
+static struct variables *top_vars = &shell_ver;
+
+
+#define B_CHUNK (100)
+#define B_NOSPAC 1
+
+typedef struct {
+ char *data;
+ int length;
+ int maxlen;
+ int quote;
+ int nonnull;
+} o_string;
+#define NULL_O_STRING {NULL,0,0,0,0}
+/* used for initialization:
+ o_string foo = NULL_O_STRING; */
+
+/* I can almost use ordinary FILE *. Is open_memstream() universally
+ * available? Where is it documented? */
+struct in_str {
+ const char *p;
+ char peek_buf[2];
+ int __promptme;
+ int promptmode;
+ FILE *file;
+ int (*get) (struct in_str *);
+ int (*peek) (struct in_str *);
+};
+#define b_getch(input) ((input)->get(input))
+#define b_peek(input) ((input)->peek(input))
+
+#define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n"
+
+struct built_in_command {
+ const char *cmd; /* name */
+ const char *descr; /* description */
+ int (*function) (struct child_prog *); /* function ptr */
+};
+
+/* belongs in busybox.h */
+static int max(int a, int b)
+{
+ return (a>b)?a:b;
+}
+
+/* This should be in utility.c */
+#ifdef DEBUG_SHELL
+static void debug_printf(const char *format, ...)
+{
+ va_list args;
+ va_start(args, format);
+ vfprintf(stderr, format, args);
+ va_end(args);
+}
+/* broken, of course, but OK for testing */
+static char *indenter(int i)
+{
+ static char blanks[] = " ";
+ return &blanks[sizeof(blanks)-i-1];
+}
+#else
+#define debug_printf(...) do {;} while (0);
+#endif
+#define final_printf debug_printf
+
+static void __syntax(const char *file, int line)
+{
+ bb_error_msg("syntax error %s:%d", file, line);
+}
+// NB: was __FILE__, but that produces full path sometimess, so...
+#define syntax() __syntax("hush.c", __LINE__)
+
+/* Index of subroutines: */
+/* function prototypes for builtins */
+static int builtin_cd(struct child_prog *child);
+static int builtin_env(struct child_prog *child);
+static int builtin_eval(struct child_prog *child);
+static int builtin_exec(struct child_prog *child);
+static int builtin_exit(struct child_prog *child);
+static int builtin_export(struct child_prog *child);
+static int builtin_fg_bg(struct child_prog *child);
+static int builtin_help(struct child_prog *child);
+static int builtin_jobs(struct child_prog *child);
+static int builtin_pwd(struct child_prog *child);
+static int builtin_read(struct child_prog *child);
+static int builtin_set(struct child_prog *child);
+static int builtin_shift(struct child_prog *child);
+static int builtin_source(struct child_prog *child);
+static int builtin_umask(struct child_prog *child);
+static int builtin_unset(struct child_prog *child);
+static int builtin_not_written(struct child_prog *child);
+/* o_string manipulation: */
+static int b_check_space(o_string *o, int len);
+static int b_addchr(o_string *o, int ch);
+static void b_reset(o_string *o);
+static int b_addqchr(o_string *o, int ch, int quote);
+static int b_adduint(o_string *o, unsigned i);
+/* in_str manipulations: */
+static int static_get(struct in_str *i);
+static int static_peek(struct in_str *i);
+static int file_get(struct in_str *i);
+static int file_peek(struct in_str *i);
+static void setup_file_in_str(struct in_str *i, FILE *f);
+static void setup_string_in_str(struct in_str *i, const char *s);
+/* close_me manipulations: */
+static void mark_open(int fd);
+static void mark_closed(int fd);
+static void close_all(void);
+/* "run" the final data structures: */
+static int free_pipe_list(struct pipe *head, int indent);
+static int free_pipe(struct pipe *pi, int indent);
+/* really run the final data structures: */
+static int setup_redirects(struct child_prog *prog, int squirrel[]);
+static int run_list_real(struct pipe *pi);
+static void pseudo_exec(struct child_prog *child) ATTRIBUTE_NORETURN;
+static int run_pipe_real(struct pipe *pi);
+/* extended glob support: */
+static int globhack(const char *src, int flags, glob_t *pglob);
+static int glob_needed(const char *s);
+static int xglob(o_string *dest, int flags, glob_t *pglob);
+/* variable assignment: */
+static int is_assignment(const char *s);
+/* data structure manipulation: */
+static int setup_redirect(struct p_context *ctx, int fd, redir_type style, struct in_str *input);
+static void initialize_context(struct p_context *ctx);
+static int done_word(o_string *dest, struct p_context *ctx);
+static int done_command(struct p_context *ctx);
+static int done_pipe(struct p_context *ctx, pipe_style type);
+/* primary string parsing: */
+static int redirect_dup_num(struct in_str *input);
+static int redirect_opt_num(o_string *o);
+static int process_command_subs(o_string *dest, struct p_context *ctx, struct in_str *input, int subst_end);
+static int parse_group(o_string *dest, struct p_context *ctx, struct in_str *input, int ch);
+static const char *lookup_param(const char *src);
+static char *make_string(char **inp);
+static int handle_dollar(o_string *dest, struct p_context *ctx, struct in_str *input);
+static int parse_string(o_string *dest, struct p_context *ctx, const char *src);
+static int parse_stream(o_string *dest, struct p_context *ctx, struct in_str *input0, int end_trigger);
+/* setup: */
+static int parse_stream_outer(struct in_str *inp, int flag);
+static int parse_string_outer(const char *s, int flag);
+static int parse_file_outer(FILE *f);
+/* job management: */
+static int checkjobs(struct pipe* fg_pipe);
+static void insert_bg_job(struct pipe *pi);
+static void remove_bg_job(struct pipe *pi);
+/* local variable support */
+static char **make_list_in(char **inp, char *name);
+static char *insert_var_value(char *inp);
+static const char *get_local_var(const char *var);
+static void unset_local_var(const char *name);
+static int set_local_var(const char *s, int flg_export);
+
+/* Table of built-in functions. They can be forked or not, depending on
+ * context: within pipes, they fork. As simple commands, they do not.
+ * When used in non-forking context, they can change global variables
+ * in the parent shell process. If forked, of course they cannot.
+ * For example, 'unset foo | whatever' will parse and run, but foo will
+ * still be set at the end. */
+static const struct built_in_command bltins[] = {
+ { "bg", "Resume a job in the background", builtin_fg_bg },
+ { "break", "Exit for, while or until loop", builtin_not_written },
+ { "cd", "Change working directory", builtin_cd },
+ { "continue", "Continue for, while or until loop", builtin_not_written },
+ { "env", "Print all environment variables", builtin_env },
+ { "eval", "Construct and run shell command", builtin_eval },
+ { "exec", "Exec command, replacing this shell with the exec'd process",
+ builtin_exec },
+ { "exit", "Exit from shell()", builtin_exit },
+ { "export", "Set environment variable", builtin_export },
+ { "fg", "Bring job into the foreground", builtin_fg_bg },
+ { "jobs", "Lists the active jobs", builtin_jobs },
+ { "pwd", "Print current directory", builtin_pwd },
+ { "read", "Input environment variable", builtin_read },
+ { "return", "Return from a function", builtin_not_written },
+ { "set", "Set/unset shell local variables", builtin_set },
+ { "shift", "Shift positional parameters", builtin_shift },
+ { "trap", "Trap signals", builtin_not_written },
+ { "ulimit","Controls resource limits", builtin_not_written },
+ { "umask","Sets file creation mask", builtin_umask },
+ { "unset", "Unset environment variable", builtin_unset },
+ { ".", "Source-in and run commands in a file", builtin_source },
+ { "help", "List shell built-in commands", builtin_help },
+ { NULL, NULL, NULL }
+};
+
+static const char *set_cwd(void)
+{
+ if (cwd == bb_msg_unknown)
+ cwd = NULL; /* xrealloc_getcwd_or_warn(arg) called free(arg) */
+ cwd = xrealloc_getcwd_or_warn((char *)cwd);
+ if (!cwd)
+ cwd = bb_msg_unknown;
+ return cwd;
+}
+
+/* built-in 'eval' handler */
+static int builtin_eval(struct child_prog *child)
+{
+ char *str = NULL;
+ int rcode = EXIT_SUCCESS;
+
+ if (child->argv[1]) {
+ str = make_string(child->argv + 1);
+ parse_string_outer(str, FLAG_EXIT_FROM_LOOP |
+ FLAG_PARSE_SEMICOLON);
+ free(str);
+ rcode = last_return_code;
+ }
+ return rcode;
+}
+
+/* built-in 'cd <path>' handler */
+static int builtin_cd(struct child_prog *child)
+{
+ char *newdir;
+ if (child->argv[1] == NULL)
+ newdir = getenv("HOME");
+ else
+ newdir = child->argv[1];
+ if (chdir(newdir)) {
+ printf("cd: %s: %s\n", newdir, strerror(errno));
+ return EXIT_FAILURE;
+ }
+ set_cwd();
+ return EXIT_SUCCESS;
+}
+
+/* built-in 'env' handler */
+static int builtin_env(struct child_prog *dummy ATTRIBUTE_UNUSED)
+{
+ char **e = environ;
+ if (e == NULL)
+ return EXIT_FAILURE;
+ while (*e) {
+ puts(*e++);
+ }
+ return EXIT_SUCCESS;
+}
+
+/* built-in 'exec' handler */
+static int builtin_exec(struct child_prog *child)
+{
+ if (child->argv[1] == NULL)
+ return EXIT_SUCCESS; /* Really? */
+ child->argv++;
+ pseudo_exec(child);
+ /* never returns */
+}
+
+/* built-in 'exit' handler */
+static int builtin_exit(struct child_prog *child)
+{
+ if (child->argv[1] == NULL)
+ exit(last_return_code);
+ exit (atoi(child->argv[1]));
+}
+
+/* built-in 'export VAR=value' handler */
+static int builtin_export(struct child_prog *child)
+{
+ int res = 0;
+ char *name = child->argv[1];
+
+ if (name == NULL) {
+ return builtin_env(child);
+ }
+
+ name = strdup(name);
+
+ if (name) {
+ const char *value = strchr(name, '=');
+
+ if (!value) {
+ char *tmp;
+ /* They are exporting something without an =VALUE */
+
+ value = get_local_var(name);
+ if (value) {
+ size_t ln = strlen(name);
+
+ tmp = realloc(name, ln+strlen(value)+2);
+ if (tmp == NULL)
+ res = -1;
+ else {
+ sprintf(tmp+ln, "=%s", value);
+ name = tmp;
+ }
+ } else {
+ /* bash does not return an error when trying to export
+ * an undefined variable. Do likewise. */
+ res = 1;
+ }
+ }
+ }
+ if (res < 0)
+ bb_perror_msg("export");
+ else if (res == 0)
+ res = set_local_var(name, 1);
+ else
+ res = 0;
+ free(name);
+ return res;
+}
+
+/* built-in 'fg' and 'bg' handler */
+static int builtin_fg_bg(struct child_prog *child)
+{
+ int i, jobnum;
+ struct pipe *pi = NULL;
+
+ if (!interactive)
+ return EXIT_FAILURE;
+ /* If they gave us no args, assume they want the last backgrounded task */
+ if (!child->argv[1]) {
+ for (pi = job_list; pi; pi = pi->next) {
+ if (pi->jobid == last_jobid) {
+ break;
+ }
+ }
+ if (!pi) {
+ bb_error_msg("%s: no current job", child->argv[0]);
+ return EXIT_FAILURE;
+ }
+ } else {
+ if (sscanf(child->argv[1], "%%%d", &jobnum) != 1) {
+ bb_error_msg("%s: bad argument '%s'", child->argv[0], child->argv[1]);
+ return EXIT_FAILURE;
+ }
+ for (pi = job_list; pi; pi = pi->next) {
+ if (pi->jobid == jobnum) {
+ break;
+ }
+ }
+ if (!pi) {
+ bb_error_msg("%s: %d: no such job", child->argv[0], jobnum);
+ return EXIT_FAILURE;
+ }
+ }
+
+ if (*child->argv[0] == 'f') {
+ /* Put the job into the foreground. */
+ tcsetpgrp(shell_terminal, pi->pgrp);
+ }
+
+ /* Restart the processes in the job */
+ for (i = 0; i < pi->num_progs; i++)
+ pi->progs[i].is_stopped = 0;
+
+ i = kill(- pi->pgrp, SIGCONT);
+ if (i < 0) {
+ if (i == ESRCH) {
+ remove_bg_job(pi);
+ } else {
+ bb_perror_msg("kill (SIGCONT)");
+ }
+ }
+
+ pi->stopped_progs = 0;
+ return EXIT_SUCCESS;
+}
+
+/* built-in 'help' handler */
+static int builtin_help(struct child_prog *dummy ATTRIBUTE_UNUSED)
+{
+ const struct built_in_command *x;
+
+ printf("\nBuilt-in commands:\n");
+ printf("-------------------\n");
+ for (x = bltins; x->cmd; x++) {
+ if (x->descr == NULL)
+ continue;
+ printf("%s\t%s\n", x->cmd, x->descr);
+ }
+ printf("\n\n");
+ return EXIT_SUCCESS;
+}
+
+/* built-in 'jobs' handler */
+static int builtin_jobs(struct child_prog *child ATTRIBUTE_UNUSED)
+{
+ struct pipe *job;
+ const char *status_string;
+
+ for (job = job_list; job; job = job->next) {
+ if (job->running_progs == job->stopped_progs)
+ status_string = "Stopped";
+ else
+ status_string = "Running";
+
+ printf(JOB_STATUS_FORMAT, job->jobid, status_string, job->text);
+ }
+ return EXIT_SUCCESS;
+}
+
+
+/* built-in 'pwd' handler */
+static int builtin_pwd(struct child_prog *dummy ATTRIBUTE_UNUSED)
+{
+ puts(set_cwd());
+ return EXIT_SUCCESS;
+}
+
+/* built-in 'read VAR' handler */
+static int builtin_read(struct child_prog *child)
+{
+ int res;
+
+ if (child->argv[1]) {
+ char string[BUFSIZ];
+ char *var = NULL;
+
+ string[0] = '\0'; /* In case stdin has only EOF */
+ /* read string */
+ fgets(string, sizeof(string), stdin);
+ chomp(string);
+ var = malloc(strlen(child->argv[1]) + strlen(string) + 2);
+ if (var) {
+ sprintf(var, "%s=%s", child->argv[1], string);
+ res = set_local_var(var, 0);
+ } else
+ res = -1;
+ if (res)
+ bb_perror_msg("read");
+ free(var); /* So not move up to avoid breaking errno */
+ return res;
+ }
+ do res = getchar(); while (res != '\n' && res != EOF);
+ return 0;
+}
+
+/* built-in 'set VAR=value' handler */
+static int builtin_set(struct child_prog *child)
+{
+ char *temp = child->argv[1];
+ struct variables *e;
+
+ if (temp == NULL)
+ for (e = top_vars; e; e = e->next)
+ printf("%s=%s\n", e->name, e->value);
+ else
+ set_local_var(temp, 0);
+
+ return EXIT_SUCCESS;
+}
+
+
+/* Built-in 'shift' handler */
+static int builtin_shift(struct child_prog *child)
+{
+ int n = 1;
+ if (child->argv[1]) {
+ n = atoi(child->argv[1]);
+ }
+ if (n >= 0 && n < global_argc) {
+ /* XXX This probably breaks $0 */
+ global_argc -= n;
+ global_argv += n;
+ return EXIT_SUCCESS;
+ } else {
+ return EXIT_FAILURE;
+ }
+}
+
+/* Built-in '.' handler (read-in and execute commands from file) */
+static int builtin_source(struct child_prog *child)
+{
+ FILE *input;
+ int status;
+
+ if (child->argv[1] == NULL)
+ return EXIT_FAILURE;
+
+ /* XXX search through $PATH is missing */
+ input = fopen(child->argv[1], "r");
+ if (!input) {
+ bb_error_msg("cannot open '%s'", child->argv[1]);
+ return EXIT_FAILURE;
+ }
+
+ /* Now run the file */
+ /* XXX argv and argc are broken; need to save old global_argv
+ * (pointer only is OK!) on this stack frame,
+ * set global_argv=child->argv+1, recurse, and restore. */
+ mark_open(fileno(input));
+ status = parse_file_outer(input);
+ mark_closed(fileno(input));
+ fclose(input);
+ return status;
+}
+
+static int builtin_umask(struct child_prog *child)
+{
+ mode_t new_umask;
+ const char *arg = child->argv[1];
+ char *end;
+ if (arg) {
+ new_umask = strtoul(arg, &end, 8);
+ if (*end != '\0' || end == arg) {
+ return EXIT_FAILURE;
+ }
+ } else {
+ new_umask = umask(0);
+ printf("%.3o\n", (unsigned) new_umask);
+ }
+ umask(new_umask);
+ return EXIT_SUCCESS;
+}
+
+/* built-in 'unset VAR' handler */
+static int builtin_unset(struct child_prog *child)
+{
+ /* bash returned already true */
+ unset_local_var(child->argv[1]);
+ return EXIT_SUCCESS;
+}
+
+static int builtin_not_written(struct child_prog *child)
+{
+ printf("builtin_%s not written\n", child->argv[0]);
+ return EXIT_FAILURE;
+}
+
+static int b_check_space(o_string *o, int len)
+{
+ /* It would be easy to drop a more restrictive policy
+ * in here, such as setting a maximum string length */
+ if (o->length + len > o->maxlen) {
+ char *old_data = o->data;
+ /* assert (data == NULL || o->maxlen != 0); */
+ o->maxlen += max(2*len, B_CHUNK);
+ o->data = realloc(o->data, 1 + o->maxlen);
+ if (o->data == NULL) {
+ free(old_data);
+ }
+ }
+ return o->data == NULL;
+}
+
+static int b_addchr(o_string *o, int ch)
+{
+ debug_printf("b_addchr: %c %d %p\n", ch, o->length, o);
+ if (b_check_space(o, 1))
+ return B_NOSPAC;
+ o->data[o->length] = ch;
+ o->length++;
+ o->data[o->length] = '\0';
+ return 0;
+}
+
+static void b_reset(o_string *o)
+{
+ o->length = 0;
+ o->nonnull = 0;
+ if (o->data != NULL)
+ *o->data = '\0';
+}
+
+static void b_free(o_string *o)
+{
+ b_reset(o);
+ free(o->data);
+ o->data = NULL;
+ o->maxlen = 0;
+}
+
+/* My analysis of quoting semantics tells me that state information
+ * is associated with a destination, not a source.
+ */
+static int b_addqchr(o_string *o, int ch, int quote)
+{
+ if (quote && strchr("*?[\\",ch)) {
+ int rc;
+ rc = b_addchr(o, '\\');
+ if (rc)
+ return rc;
+ }
+ return b_addchr(o, ch);
+}
+
+/* belongs in utility.c */
+static char *simple_itoa(unsigned i)
+{
+ static char local[sizeof(int)*3 + 2];
+ char *p = &local[sizeof(int)*3 + 2 - 1];
+ *p-- = '\0';
+ do {
+ *p-- = '0' + i % 10;
+ i /= 10;
+ } while (i > 0);
+ return p + 1;
+}
+
+static int b_adduint(o_string *o, unsigned i)
+{
+ int r;
+ char *p = simple_itoa(i);
+ /* no escape checking necessary */
+ do r = b_addchr(o, *p++); while (r == 0 && *p);
+ return r;
+}
+
+static int static_get(struct in_str *i)
+{
+ int ch = *i->p++;
+ if (ch == '\0') return EOF;
+ return ch;
+}
+
+static int static_peek(struct in_str *i)
+{
+ return *i->p;
+}
+
+static void cmdedit_set_initial_prompt(void)
+{
+#if !ENABLE_FEATURE_EDITING_FANCY_PROMPT
+ PS1 = NULL;
+#else
+ PS1 = getenv("PS1");
+ if (PS1 == NULL)
+ PS1 = "\\w \\$ ";
+#endif
+}
+
+static const char* setup_prompt_string(int promptmode)
+{
+ const char *prompt_str;
+ debug_printf("setup_prompt_string %d ", promptmode);
+#if !ENABLE_FEATURE_EDITING_FANCY_PROMPT
+ /* Set up the prompt */
+ if (promptmode == 1) {
+ char *ns;
+ free((char*)PS1);
+ ns = xmalloc(strlen(cwd)+4);
+ sprintf(ns, "%s %s", cwd, (geteuid() != 0) ? "$ " : "# ");
+ prompt_str = ns;
+ PS1 = ns;
+ } else {
+ prompt_str = PS2;
+ }
+#else
+ prompt_str = (promptmode == 1) ? PS1 : PS2;
+#endif
+ debug_printf("result %s\n", prompt_str);
+ return prompt_str;
+}
+
+#if ENABLE_FEATURE_EDITING
+static line_input_t *line_input_state;
+#endif
+
+static void get_user_input(struct in_str *i)
+{
+ const char *prompt_str;
+ static char the_command[BUFSIZ];
+
+ prompt_str = setup_prompt_string(i->promptmode);
+#if ENABLE_FEATURE_EDITING
+ /*
+ ** enable command line editing only while a command line
+ ** is actually being read; otherwise, we'll end up bequeathing
+ ** atexit() handlers and other unwanted stuff to our
+ ** child processes (rob@sysgo.de)
+ */
+ read_line_input(prompt_str, the_command, BUFSIZ, line_input_state);
+#else
+ fputs(prompt_str, stdout);
+ fflush(stdout);
+ the_command[0] = fgetc(i->file);
+ the_command[1] = '\0';
+#endif
+ fflush(stdout);
+ i->p = the_command;
+}
+
+/* This is the magic location that prints prompts
+ * and gets data back from the user */
+static int file_get(struct in_str *i)
+{
+ int ch;
+
+ ch = 0;
+ /* If there is data waiting, eat it up */
+ if (i->p && *i->p) {
+ ch = *i->p++;
+ } else {
+ /* need to double check i->file because we might be doing something
+ * more complicated by now, like sourcing or substituting. */
+ if (i->__promptme && interactive && i->file == stdin) {
+ while (!i->p || !(interactive && strlen(i->p))) {
+ get_user_input(i);
+ }
+ i->promptmode = 2;
+ i->__promptme = 0;
+ if (i->p && *i->p) {
+ ch = *i->p++;
+ }
+ } else {
+ ch = fgetc(i->file);
+ }
+
+ debug_printf("b_getch: got a %d\n", ch);
+ }
+ if (ch == '\n')
+ i->__promptme = 1;
+ return ch;
+}
+
+/* All the callers guarantee this routine will never be
+ * used right after a newline, so prompting is not needed.
+ */
+static int file_peek(struct in_str *i)
+{
+ if (i->p && *i->p) {
+ return *i->p;
+ } else {
+ i->peek_buf[0] = fgetc(i->file);
+ i->peek_buf[1] = '\0';
+ i->p = i->peek_buf;
+ debug_printf("b_peek: got a %d\n", *i->p);
+ return *i->p;
+ }
+}
+
+static void setup_file_in_str(struct in_str *i, FILE *f)
+{
+ i->peek = file_peek;
+ i->get = file_get;
+ i->__promptme = 1;
+ i->promptmode = 1;
+ i->file = f;
+ i->p = NULL;
+}
+
+static void setup_string_in_str(struct in_str *i, const char *s)
+{
+ i->peek = static_peek;
+ i->get = static_get;
+ i->__promptme = 1;
+ i->promptmode = 1;
+ i->p = s;
+}
+
+static void mark_open(int fd)
+{
+ struct close_me *new = xmalloc(sizeof(struct close_me));
+ new->fd = fd;
+ new->next = close_me_head;
+ close_me_head = new;
+}
+
+static void mark_closed(int fd)
+{
+ struct close_me *tmp;
+ if (close_me_head == NULL || close_me_head->fd != fd)
+ bb_error_msg_and_die("corrupt close_me");
+ tmp = close_me_head;
+ close_me_head = close_me_head->next;
+ free(tmp);
+}
+
+static void close_all(void)
+{
+ struct close_me *c;
+ for (c = close_me_head; c; c = c->next) {
+ close(c->fd);
+ }
+ close_me_head = NULL;
+}
+
+/* squirrel != NULL means we squirrel away copies of stdin, stdout,
+ * and stderr if they are redirected. */
+static int setup_redirects(struct child_prog *prog, int squirrel[])
+{
+ int openfd, mode;
+ struct redir_struct *redir;
+
+ for (redir = prog->redirects; redir; redir = redir->next) {
+ if (redir->dup == -1 && redir->word.gl_pathv == NULL) {
+ /* something went wrong in the parse. Pretend it didn't happen */
+ continue;
+ }
+ if (redir->dup == -1) {
+ mode = redir_table[redir->type].mode;
+ openfd = open(redir->word.gl_pathv[0], mode, 0666);
+ if (openfd < 0) {
+ /* this could get lost if stderr has been redirected, but
+ bash and ash both lose it as well (though zsh doesn't!) */
+ bb_perror_msg("error opening %s", redir->word.gl_pathv[0]);
+ return 1;
+ }
+ } else {
+ openfd = redir->dup;
+ }
+
+ if (openfd != redir->fd) {
+ if (squirrel && redir->fd < 3) {
+ squirrel[redir->fd] = dup(redir->fd);
+ }
+ if (openfd == -3) {
+ close(openfd);
+ } else {
+ dup2(openfd, redir->fd);
+ if (redir->dup == -1)
+ close (openfd);
+ }
+ }
+ }
+ return 0;
+}
+
+static void restore_redirects(int squirrel[])
+{
+ int i, fd;
+ for (i = 0; i < 3; i++) {
+ fd = squirrel[i];
+ if (fd != -1) {
+ /* No error checking. I sure wouldn't know what
+ * to do with an error if I found one! */
+ dup2(fd, i);
+ close(fd);
+ }
+ }
+}
+
+/* never returns */
+/* XXX no exit() here. If you don't exec, use _exit instead.
+ * The at_exit handlers apparently confuse the calling process,
+ * in particular stdin handling. Not sure why? */
+static void pseudo_exec(struct child_prog *child)
+{
+ int i, rcode;
+ char *p;
+ const struct built_in_command *x;
+ if (child->argv) {
+ for (i = 0; is_assignment(child->argv[i]); i++) {
+ debug_printf("pid %d environment modification: %s\n",
+ getpid(), child->argv[i]);
+ p = insert_var_value(child->argv[i]);
+ putenv(strdup(p));
+ if (p != child->argv[i])
+ free(p);
+ }
+ child->argv += i; /* XXX this hack isn't so horrible, since we are about
+ to exit, and therefore don't need to keep data
+ structures consistent for free() use. */
+ /* If a variable is assigned in a forest, and nobody listens,
+ * was it ever really set?
+ */
+ if (child->argv[0] == NULL) {
+ _exit(EXIT_SUCCESS);
+ }
+
+ /*
+ * Check if the command matches any of the builtins.
+ * Depending on context, this might be redundant. But it's
+ * easier to waste a few CPU cycles than it is to figure out
+ * if this is one of those cases.
+ */
+ for (x = bltins; x->cmd; x++) {
+ if (strcmp(child->argv[0], x->cmd) == 0 ) {
+ debug_printf("builtin exec %s\n", child->argv[0]);
+ rcode = x->function(child);
+ fflush(stdout);
+ _exit(rcode);
+ }
+ }
+
+ /* Check if the command matches any busybox internal commands
+ * ("applets") here.
+ * FIXME: This feature is not 100% safe, since
+ * BusyBox is not fully reentrant, so we have no guarantee the things
+ * from the .bss are still zeroed, or that things from .data are still
+ * at their defaults. We could exec ourself from /proc/self/exe, but I
+ * really dislike relying on /proc for things. We could exec ourself
+ * from global_argv[0], but if we are in a chroot, we may not be able
+ * to find ourself... */
+#if ENABLE_FEATURE_SH_STANDALONE_SHELL
+ {
+ int argc_l;
+ char** argv_l = child->argv;
+ char *name = child->argv[0];
+
+ /* Count argc for use in a second... */
+ for (argc_l = 0; *argv_l; argv_l++, argc_l++)
+ /**/;
+ optind = 1;
+ debug_printf("running applet %s\n", name);
+ run_applet_by_name(name, argc_l, child->argv);
+ }
+#endif
+ debug_printf("exec of %s\n", child->argv[0]);
+ execvp(child->argv[0], child->argv);
+ bb_perror_msg("cannot exec: %s", child->argv[0]);
+ _exit(1);
+ } else if (child->group) {
+ debug_printf("runtime nesting to group\n");
+ interactive = 0; /* crucial!!!! */
+ rcode = run_list_real(child->group);
+ /* OK to leak memory by not calling free_pipe_list,
+ * since this process is about to exit */
+ _exit(rcode);
+ } else {
+ /* Can happen. See what bash does with ">foo" by itself. */
+ debug_printf("trying to pseudo_exec null command\n");
+ _exit(EXIT_SUCCESS);
+ }
+}
+
+static void insert_bg_job(struct pipe *pi)
+{
+ struct pipe *thejob;
+
+ /* Linear search for the ID of the job to use */
+ pi->jobid = 1;
+ for (thejob = job_list; thejob; thejob = thejob->next)
+ if (thejob->jobid >= pi->jobid)
+ pi->jobid = thejob->jobid + 1;
+
+ /* add thejob to the list of running jobs */
+ if (!job_list) {
+ thejob = job_list = xmalloc(sizeof(*thejob));
+ } else {
+ for (thejob = job_list; thejob->next; thejob = thejob->next)
+ /* nothing */;
+ thejob->next = xmalloc(sizeof(*thejob));
+ thejob = thejob->next;
+ }
+
+ /* physically copy the struct job */
+ memcpy(thejob, pi, sizeof(struct pipe));
+ thejob->next = NULL;
+ thejob->running_progs = thejob->num_progs;
+ thejob->stopped_progs = 0;
+ thejob->text = xmalloc(BUFSIZ); /* cmdedit buffer size */
+
+ //if (pi->progs[0] && pi->progs[0].argv && pi->progs[0].argv[0])
+ {
+ char *bar = thejob->text;
+ char **foo = pi->progs[0].argv;
+ if (foo)
+ while (*foo)
+ bar += sprintf(bar, "%s ", *foo++);
+ }
+
+ /* we don't wait for background thejobs to return -- append it
+ to the list of backgrounded thejobs and leave it alone */
+ printf("[%d] %d\n", thejob->jobid, thejob->progs[0].pid);
+ last_bg_pid = thejob->progs[0].pid;
+ last_jobid = thejob->jobid;
+}
+
+/* remove a backgrounded job */
+static void remove_bg_job(struct pipe *pi)
+{
+ struct pipe *prev_pipe;
+
+ if (pi == job_list) {
+ job_list = pi->next;
+ } else {
+ prev_pipe = job_list;
+ while (prev_pipe->next != pi)
+ prev_pipe = prev_pipe->next;
+ prev_pipe->next = pi->next;
+ }
+ if (job_list)
+ last_jobid = job_list->jobid;
+ else
+ last_jobid = 0;
+
+ pi->stopped_progs = 0;
+ free_pipe(pi, 0);
+ free(pi);
+}
+
+/* Checks to see if any processes have exited -- if they
+ have, figure out why and see if a job has completed */
+static int checkjobs(struct pipe* fg_pipe)
+{
+ int attributes;
+ int status;
+ int prognum = 0;
+ struct pipe *pi;
+ pid_t childpid;
+
+ attributes = WUNTRACED;
+ if (fg_pipe == NULL) {
+ attributes |= WNOHANG;
+ }
+
+ while ((childpid = waitpid(-1, &status, attributes)) > 0) {
+ if (fg_pipe) {
+ int i, rcode = 0;
+ for (i = 0; i < fg_pipe->num_progs; i++) {
+ if (fg_pipe->progs[i].pid == childpid) {
+ if (i == fg_pipe->num_progs-1)
+ rcode = WEXITSTATUS(status);
+ fg_pipe->num_progs--;
+ return rcode;
+ }
+ }
+ }
+
+ for (pi = job_list; pi; pi = pi->next) {
+ prognum = 0;
+ while (prognum < pi->num_progs && pi->progs[prognum].pid != childpid) {
+ prognum++;
+ }
+ if (prognum < pi->num_progs)
+ break;
+ }
+
+ if (pi == NULL) {
+ debug_printf("checkjobs: pid %d was not in our list!\n",
+ childpid);
+ continue;
+ }
+
+ if (WIFEXITED(status) || WIFSIGNALED(status)) {
+ /* child exited */
+ pi->running_progs--;
+ pi->progs[prognum].pid = 0;
+
+ if (!pi->running_progs) {
+ printf(JOB_STATUS_FORMAT, pi->jobid,
+ "Done", pi->text);
+ remove_bg_job(pi);
+ }
+ } else {
+ /* child stopped */
+ pi->stopped_progs++;
+ pi->progs[prognum].is_stopped = 1;
+ }
+ }
+
+ if (childpid == -1 && errno != ECHILD)
+ bb_perror_msg("waitpid");
+
+ /* move the shell to the foreground */
+ //if (interactive && tcsetpgrp(shell_terminal, getpgid(0)))
+ // bb_perror_msg("tcsetpgrp-2");
+ return -1;
+}
+
+/* run_pipe_real() starts all the jobs, but doesn't wait for anything
+ * to finish. See checkjobs().
+ *
+ * return code is normally -1, when the caller has to wait for children
+ * to finish to determine the exit status of the pipe. If the pipe
+ * is a simple builtin command, however, the action is done by the
+ * time run_pipe_real returns, and the exit code is provided as the
+ * return value.
+ *
+ * The input of the pipe is always stdin, the output is always
+ * stdout. The outpipe[] mechanism in BusyBox-0.48 lash is bogus,
+ * because it tries to avoid running the command substitution in
+ * subshell, when that is in fact necessary. The subshell process
+ * now has its stdout directed to the input of the appropriate pipe,
+ * so this routine is noticeably simpler.
+ */
+static int run_pipe_real(struct pipe *pi)
+{
+ int i;
+ int nextin, nextout;
+ int pipefds[2]; /* pipefds[0] is for reading */
+ struct child_prog *child;
+ const struct built_in_command *x;
+ char *p;
+
+ nextin = 0;
+ pi->pgrp = -1;
+
+ /* Check if this is a simple builtin (not part of a pipe).
+ * Builtins within pipes have to fork anyway, and are handled in
+ * pseudo_exec. "echo foo | read bar" doesn't work on bash, either.
+ */
+ child = &(pi->progs[0]);
+ if (pi->num_progs == 1 && child->group && child->subshell == 0) {
+ int squirrel[] = { -1, -1, -1 };
+ int rcode;
+ debug_printf("non-subshell grouping\n");
+ setup_redirects(child, squirrel);
+ /* XXX could we merge code with following builtin case,
+ * by creating a pseudo builtin that calls run_list_real? */
+ rcode = run_list_real(child->group);
+ restore_redirects(squirrel);
+ return rcode;
+ } else if (pi->num_progs == 1 && pi->progs[0].argv != NULL) {
+ for (i = 0; is_assignment(child->argv[i]); i++)
+ /* nothing */;
+ if (i != 0 && child->argv[i] == NULL) {
+ /* assignments, but no command: set the local environment */
+ for (i = 0; child->argv[i] != NULL; i++) {
+ /* Ok, this case is tricky. We have to decide if this is a
+ * local variable, or an already exported variable. If it is
+ * already exported, we have to export the new value. If it is
+ * not exported, we need only set this as a local variable.
+ * This junk is all to decide whether or not to export this
+ * variable. */
+ int export_me = 0;
+ char *name, *value;
+ name = xstrdup(child->argv[i]);
+ debug_printf("Local environment set: %s\n", name);
+ value = strchr(name, '=');
+ if (value)
+ *value = 0;
+ if (get_local_var(name)) {
+ export_me = 1;
+ }
+ free(name);
+ p = insert_var_value(child->argv[i]);
+ set_local_var(p, export_me);
+ if (p != child->argv[i])
+ free(p);
+ }
+ return EXIT_SUCCESS; /* don't worry about errors in set_local_var() yet */
+ }
+ for (i = 0; is_assignment(child->argv[i]); i++) {
+ p = insert_var_value(child->argv[i]);
+ putenv(strdup(p));
+ if (p != child->argv[i]) {
+ child->sp--;
+ free(p);
+ }
+ }
+ if (child->sp) {
+ char *str = NULL;
+
+ str = make_string((child->argv + i));
+ parse_string_outer(str, FLAG_EXIT_FROM_LOOP | FLAG_REPARSING);
+ free(str);
+ return last_return_code;
+ }
+ for (x = bltins; x->cmd; x++) {
+ if (strcmp(child->argv[i], x->cmd) == 0 ) {
+ int squirrel[] = { -1, -1, -1 };
+ int rcode;
+ if (x->function == builtin_exec && child->argv[i+1] == NULL) {
+ debug_printf("magic exec\n");
+ setup_redirects(child,NULL);
+ return EXIT_SUCCESS;
+ }
+ debug_printf("builtin inline %s\n", child->argv[0]);
+ /* XXX setup_redirects acts on file descriptors, not FILEs.
+ * This is perfect for work that comes after exec().
+ * Is it really safe for inline use? Experimentally,
+ * things seem to work with glibc. */
+ setup_redirects(child, squirrel);
+ child->argv += i; /* XXX horrible hack */
+ rcode = x->function(child);
+ child->argv -= i; /* XXX restore hack so free() can work right */
+ restore_redirects(squirrel);
+ return rcode;
+ }
+ }
+ }
+
+ for (i = 0; i < pi->num_progs; i++) {
+ child = &(pi->progs[i]);
+
+ /* pipes are inserted between pairs of commands */
+ if ((i + 1) < pi->num_progs) {
+ if (pipe(pipefds) < 0)
+ bb_perror_msg_and_die("pipe");
+ nextout = pipefds[1];
+ } else {
+ nextout = 1;
+ pipefds[0] = -1;
+ }
+
+ /* XXX test for failed fork()? */
+#if !defined(__UCLIBC__) || defined(__ARCH_HAS_MMU__)
+ child->pid = fork();
+#else
+ child->pid = vfork();
+#endif
+ if (!child->pid) {
+ /* Set the handling for job control signals back to the default. */
+ signal(SIGINT, SIG_DFL);
+ signal(SIGQUIT, SIG_DFL);
+ signal(SIGTERM, SIG_DFL);
+ signal(SIGTSTP, SIG_DFL);
+ signal(SIGTTIN, SIG_DFL);
+ signal(SIGTTOU, SIG_DFL);
+ signal(SIGCHLD, SIG_DFL);
+
+ close_all();
+
+ if (nextin != 0) {
+ dup2(nextin, 0);
+ close(nextin);
+ }
+ if (nextout != 1) {
+ dup2(nextout, 1);
+ close(nextout);
+ }
+ if (pipefds[0] != -1) {
+ close(pipefds[0]); /* opposite end of our output pipe */
+ }
+
+ /* Like bash, explicit redirects override pipes,
+ * and the pipe fd is available for dup'ing. */
+ setup_redirects(child,NULL);
+
+ if (interactive && pi->followup != PIPE_BG) {
+ /* If we (the child) win the race, put ourselves in the process
+ * group whose leader is the first process in this pipe. */
+ if (pi->pgrp < 0) {
+ pi->pgrp = getpid();
+ }
+ if (setpgid(0, pi->pgrp) == 0) {
+ tcsetpgrp(2, pi->pgrp);
+ }
+ }
+
+ pseudo_exec(child);
+ }
+
+ /* put our child in the process group whose leader is the
+ first process in this pipe */
+ if (pi->pgrp < 0) {
+ pi->pgrp = child->pid;
+ }
+ /* Don't check for errors. The child may be dead already,
+ * in which case setpgid returns error code EACCES. */
+ setpgid(child->pid, pi->pgrp);
+
+ if (nextin != 0)
+ close(nextin);
+ if (nextout != 1)
+ close(nextout);
+
+ /* If there isn't another process, nextin is garbage
+ but it doesn't matter */
+ nextin = pipefds[0];
+ }
+ return -1;
+}
+
+static int run_list_real(struct pipe *pi)
+{
+ char *save_name = NULL;
+ char **list = NULL;
+ char **save_list = NULL;
+ struct pipe *rpipe;
+ int flag_rep = 0;
+ int save_num_progs;
+ int rcode = 0, flag_skip = 1;
+ int flag_restore = 0;
+ int if_code = 0, next_if_code = 0; /* need double-buffer to handle elif */
+ reserved_style rmode, skip_more_in_this_rmode = RES_XXXX;
+ /* check syntax for "for" */
+ for (rpipe = pi; rpipe; rpipe = rpipe->next) {
+ if ((rpipe->r_mode == RES_IN || rpipe->r_mode == RES_FOR)
+ && (rpipe->next == NULL)
+ ) {
+ syntax();
+ return 1;
+ }
+ if ((rpipe->r_mode == RES_IN && rpipe->next->r_mode == RES_IN && rpipe->next->progs->argv != NULL)
+ || (rpipe->r_mode == RES_FOR && rpipe->next->r_mode != RES_IN)
+ ) {
+ syntax();
+ return 1;
+ }
+ }
+ for (; pi; pi = (flag_restore != 0) ? rpipe : pi->next) {
+ if (pi->r_mode == RES_WHILE || pi->r_mode == RES_UNTIL
+ || pi->r_mode == RES_FOR
+ ) {
+ flag_restore = 0;
+ if (!rpipe) {
+ flag_rep = 0;
+ rpipe = pi;
+ }
+ }
+ rmode = pi->r_mode;
+ debug_printf("rmode=%d if_code=%d next_if_code=%d skip_more=%d\n",
+ rmode, if_code, next_if_code, skip_more_in_this_rmode);
+ if (rmode == skip_more_in_this_rmode && flag_skip) {
+ if (pi->followup == PIPE_SEQ)
+ flag_skip = 0;
+ continue;
+ }
+ flag_skip = 1;
+ skip_more_in_this_rmode = RES_XXXX;
+ if (rmode == RES_THEN || rmode == RES_ELSE)
+ if_code = next_if_code;
+ if (rmode == RES_THEN && if_code)
+ continue;
+ if (rmode == RES_ELSE && !if_code)
+ continue;
+ if (rmode == RES_ELIF && !if_code)
+ break;
+ if (rmode == RES_FOR && pi->num_progs) {
+ if (!list) {
+ /* if no variable values after "in" we skip "for" */
+ if (!pi->next->progs->argv)
+ continue;
+ /* create list of variable values */
+ list = make_list_in(pi->next->progs->argv,
+ pi->progs->argv[0]);
+ save_list = list;
+ save_name = pi->progs->argv[0];
+ pi->progs->argv[0] = NULL;
+ flag_rep = 1;
+ }
+ if (!*list) {
+ free(pi->progs->argv[0]);
+ free(save_list);
+ list = NULL;
+ flag_rep = 0;
+ pi->progs->argv[0] = save_name;
+ pi->progs->glob_result.gl_pathv[0] = pi->progs->argv[0];
+ continue;
+ } else {
+ /* insert new value from list for variable */
+ if (pi->progs->argv[0])
+ free(pi->progs->argv[0]);
+ pi->progs->argv[0] = *list++;
+ pi->progs->glob_result.gl_pathv[0] = pi->progs->argv[0];
+ }
+ }
+ if (rmode == RES_IN)
+ continue;
+ if (rmode == RES_DO) {
+ if (!flag_rep)
+ continue;
+ }
+ if ((rmode == RES_DONE)) {
+ if (flag_rep) {
+ flag_restore = 1;
+ } else {
+ rpipe = NULL;
+ }
+ }
+ if (pi->num_progs == 0)
+ continue;
+ save_num_progs = pi->num_progs; /* save number of programs */
+ rcode = run_pipe_real(pi);
+ debug_printf("run_pipe_real returned %d\n",rcode);
+ if (rcode != -1) {
+ /* We only ran a builtin: rcode was set by the return value
+ * of run_pipe_real(), and we don't need to wait for anything. */
+ } else if (pi->followup == PIPE_BG) {
+ /* XXX check bash's behavior with nontrivial pipes */
+ /* XXX compute jobid */
+ /* XXX what does bash do with attempts to background builtins? */
+ insert_bg_job(pi);
+ rcode = EXIT_SUCCESS;
+ } else {
+ if (interactive) {
+ /* move the new process group into the foreground */
+ if (tcsetpgrp(shell_terminal, pi->pgrp) && errno != ENOTTY)
+ bb_perror_msg("tcsetpgrp-3");
+ rcode = checkjobs(pi);
+ /* move the shell to the foreground */
+ if (tcsetpgrp(shell_terminal, getpgid(0)) && errno != ENOTTY)
+ bb_perror_msg("tcsetpgrp-4");
+ } else {
+ rcode = checkjobs(pi);
+ }
+ debug_printf("checkjobs returned %d\n",rcode);
+ }
+ last_return_code = rcode;
+ pi->num_progs = save_num_progs; /* restore number of programs */
+ if ( rmode == RES_IF || rmode == RES_ELIF )
+ next_if_code = rcode; /* can be overwritten a number of times */
+ if (rmode == RES_WHILE)
+ flag_rep = !last_return_code;
+ if (rmode == RES_UNTIL)
+ flag_rep = last_return_code;
+ if ((rcode == EXIT_SUCCESS && pi->followup == PIPE_OR)
+ || (rcode != EXIT_SUCCESS && pi->followup == PIPE_AND)
+ ) {
+ skip_more_in_this_rmode = rmode;
+ }
+ checkjobs(NULL);
+ }
+ return rcode;
+}
+
+/* return code is the exit status of the pipe */
+static int free_pipe(struct pipe *pi, int indent)
+{
+ char **p;
+ struct child_prog *child;
+ struct redir_struct *r, *rnext;
+ int a, i, ret_code = 0;
+
+ if (pi->stopped_progs > 0)
+ return ret_code;
+ final_printf("%s run pipe: (pid %d)\n", indenter(indent), getpid());
+ for (i = 0; i < pi->num_progs; i++) {
+ child = &pi->progs[i];
+ final_printf("%s command %d:\n", indenter(indent), i);
+ if (child->argv) {
+ for (a = 0, p = child->argv; *p; a++, p++) {
+ final_printf("%s argv[%d] = %s\n", indenter(indent), a, *p);
+ }
+ globfree(&child->glob_result);
+ child->argv = NULL;
+ } else if (child->group) {
+ final_printf("%s begin group (subshell:%d)\n", indenter(indent), child->subshell);
+ ret_code = free_pipe_list(child->group, indent+3);
+ final_printf("%s end group\n", indenter(indent));
+ } else {
+ final_printf("%s (nil)\n", indenter(indent));
+ }
+ for (r = child->redirects; r; r = rnext) {
+ final_printf("%s redirect %d%s", indenter(indent), r->fd, redir_table[r->type].descrip);
+ if (r->dup == -1) {
+ /* guard against the case >$FOO, where foo is unset or blank */
+ if (r->word.gl_pathv) {
+ final_printf(" %s\n", *r->word.gl_pathv);
+ globfree(&r->word);
+ }
+ } else {
+ final_printf("&%d\n", r->dup);
+ }
+ rnext = r->next;
+ free(r);
+ }
+ child->redirects = NULL;
+ }
+ free(pi->progs); /* children are an array, they get freed all at once */
+ pi->progs = NULL;
+ return ret_code;
+}
+
+static int free_pipe_list(struct pipe *head, int indent)
+{
+ int rcode = 0; /* if list has no members */
+ struct pipe *pi, *next;
+ for (pi = head; pi; pi = next) {
+ final_printf("%s pipe reserved mode %d\n", indenter(indent), pi->r_mode);
+ rcode = free_pipe(pi, indent);
+ final_printf("%s pipe followup code %d\n", indenter(indent), pi->followup);
+ next = pi->next;
+ pi->next = NULL;
+ free(pi);
+ }
+ return rcode;
+}
+
+/* Select which version we will use */
+static int run_list(struct pipe *pi)
+{
+ int rcode = 0;
+ if (fake_mode == 0) {
+ rcode = run_list_real(pi);
+ }
+ /* free_pipe_list has the side effect of clearing memory
+ * In the long run that function can be merged with run_list_real,
+ * but doing that now would hobble the debugging effort. */
+ free_pipe_list(pi,0);
+ return rcode;
+}
+
+/* The API for glob is arguably broken. This routine pushes a non-matching
+ * string into the output structure, removing non-backslashed backslashes.
+ * If someone can prove me wrong, by performing this function within the
+ * original glob(3) api, feel free to rewrite this routine into oblivion.
+ * Return code (0 vs. GLOB_NOSPACE) matches glob(3).
+ * XXX broken if the last character is '\\', check that before calling.
+ */
+static int globhack(const char *src, int flags, glob_t *pglob)
+{
+ int cnt = 0, pathc;
+ const char *s;
+ char *dest;
+ for (cnt = 1, s = src; s && *s; s++) {
+ if (*s == '\\') s++;
+ cnt++;
+ }
+ dest = malloc(cnt);
+ if (!dest) return GLOB_NOSPACE;
+ if (!(flags & GLOB_APPEND)) {
+ pglob->gl_pathv = NULL;
+ pglob->gl_pathc = 0;
+ pglob->gl_offs = 0;
+ pglob->gl_offs = 0;
+ }
+ pathc = ++pglob->gl_pathc;
+ pglob->gl_pathv = realloc(pglob->gl_pathv, (pathc+1)*sizeof(*pglob->gl_pathv));
+ if (pglob->gl_pathv == NULL) return GLOB_NOSPACE;
+ pglob->gl_pathv[pathc-1] = dest;
+ pglob->gl_pathv[pathc] = NULL;
+ for (s = src; s && *s; s++, dest++) {
+ if (*s == '\\') s++;
+ *dest = *s;
+ }
+ *dest = '\0';
+ return 0;
+}
+
+/* XXX broken if the last character is '\\', check that before calling */
+static int glob_needed(const char *s)
+{
+ for (; *s; s++) {
+ if (*s == '\\') s++;
+ if (strchr("*[?", *s)) return 1;
+ }
+ return 0;
+}
+
+static int xglob(o_string *dest, int flags, glob_t *pglob)
+{
+ int gr;
+
+ /* short-circuit for null word */
+ /* we can code this better when the debug_printf's are gone */
+ if (dest->length == 0) {
+ if (dest->nonnull) {
+ /* bash man page calls this an "explicit" null */
+ gr = globhack(dest->data, flags, pglob);
+ debug_printf("globhack returned %d\n", gr);
+ } else {
+ return 0;
+ }
+ } else if (glob_needed(dest->data)) {
+ gr = glob(dest->data, flags, NULL, pglob);
+ debug_printf("glob returned %d\n", gr);
+ if (gr == GLOB_NOMATCH) {
+ /* quote removal, or more accurately, backslash removal */
+ gr = globhack(dest->data, flags, pglob);
+ debug_printf("globhack returned %d\n", gr);
+ }
+ } else {
+ gr = globhack(dest->data, flags, pglob);
+ debug_printf("globhack returned %d\n", gr);
+ }
+ if (gr == GLOB_NOSPACE)
+ bb_error_msg_and_die("out of memory during glob");
+ if (gr != 0) { /* GLOB_ABORTED ? */
+ bb_error_msg("glob(3) error %d", gr);
+ }
+ /* globprint(glob_target); */
+ return gr;
+}
+
+/* This is used to get/check local shell variables */
+static const char *get_local_var(const char *s)
+{
+ struct variables *cur;
+
+ if (!s)
+ return NULL;
+ for (cur = top_vars; cur; cur = cur->next)
+ if (strcmp(cur->name, s) == 0)
+ return cur->value;
+ return NULL;
+}
+
+/* This is used to set local shell variables
+ flg_export == 0 if only local (not exporting) variable
+ flg_export == 1 if "new" exporting environ
+ flg_export > 1 if current startup environ (not call putenv()) */
+static int set_local_var(const char *s, int flg_export)
+{
+ char *name, *value;
+ int result = 0;
+ struct variables *cur;
+
+ name = strdup(s);
+
+ /* Assume when we enter this function that we are already in
+ * NAME=VALUE format. So the first order of business is to
+ * split 's' on the '=' into 'name' and 'value' */
+ value = strchr(name, '=');
+ if (value == 0 && ++value == 0) {
+ free(name);
+ return -1;
+ }
+ *value++ = 0;
+
+ for (cur = top_vars; cur; cur = cur->next) {
+ if (strcmp(cur->name, name) == 0)
+ break;
+ }
+
+ if (cur) {
+ if (strcmp(cur->value, value) == 0) {
+ if (flg_export > 0 && cur->flg_export == 0)
+ cur->flg_export = flg_export;
+ else
+ result++;
+ } else {
+ if (cur->flg_read_only) {
+ bb_error_msg("%s: readonly variable", name);
+ result = -1;
+ } else {
+ if (flg_export > 0 || cur->flg_export > 1)
+ cur->flg_export = 1;
+ free((char*)cur->value);
+
+ cur->value = strdup(value);
+ }
+ }
+ } else {
+ cur = malloc(sizeof(struct variables));
+ if (!cur) {
+ result = -1;
+ } else {
+ cur->name = strdup(name);
+ if (cur->name) {
+ free(cur);
+ result = -1;
+ } else {
+ struct variables *bottom = top_vars;
+ cur->value = strdup(value);
+ cur->next = 0;
+ cur->flg_export = flg_export;
+ cur->flg_read_only = 0;
+ while (bottom->next)
+ bottom = bottom->next;
+ bottom->next = cur;
+ }
+ }
+ }
+
+ if (result == 0 && cur->flg_export == 1) {
+ *(value-1) = '=';
+ result = putenv(name);
+ } else {
+ free(name);
+ if (result > 0) /* equivalent to previous set */
+ result = 0;
+ }
+ return result;
+}
+
+static void unset_local_var(const char *name)
+{
+ struct variables *cur;
+
+ if (name) {
+ for (cur = top_vars; cur; cur = cur->next) {
+ if (strcmp(cur->name, name) == 0)
+ break;
+ }
+ if (cur != 0) {
+ struct variables *next = top_vars;
+ if (cur->flg_read_only) {
+ bb_error_msg("%s: readonly variable", name);
+ return;
+ }
+ if (cur->flg_export)
+ unsetenv(cur->name);
+ free((char*)cur->name);
+ free((char*)cur->value);
+ while (next->next != cur)
+ next = next->next;
+ next->next = cur->next;
+ free(cur);
+ }
+ }
+}
+
+static int is_assignment(const char *s)
+{
+ if (!s || !isalpha(*s))
+ return 0;
+ s++;
+ while (isalnum(*s) || *s == '_')
+ s++;
+ return *s == '=';
+}
+
+/* the src parameter allows us to peek forward to a possible &n syntax
+ * for file descriptor duplication, e.g., "2>&1".
+ * Return code is 0 normally, 1 if a syntax error is detected in src.
+ * Resource errors (in xmalloc) cause the process to exit */
+static int setup_redirect(struct p_context *ctx, int fd, redir_type style,
+ struct in_str *input)
+{
+ struct child_prog *child = ctx->child;
+ struct redir_struct *redir = child->redirects;
+ struct redir_struct *last_redir = NULL;
+
+ /* Create a new redir_struct and drop it onto the end of the linked list */
+ while (redir) {
+ last_redir = redir;
+ redir = redir->next;
+ }
+ redir = xmalloc(sizeof(struct redir_struct));
+ redir->next = NULL;
+ redir->word.gl_pathv = NULL;
+ if (last_redir) {
+ last_redir->next = redir;
+ } else {
+ child->redirects = redir;
+ }
+
+ redir->type = style;
+ redir->fd = (fd == -1) ? redir_table[style].default_fd : fd;
+
+ debug_printf("Redirect type %d%s\n", redir->fd, redir_table[style].descrip);
+
+ /* Check for a '2>&1' type redirect */
+ redir->dup = redirect_dup_num(input);
+ if (redir->dup == -2) return 1; /* syntax error */
+ if (redir->dup != -1) {
+ /* Erik had a check here that the file descriptor in question
+ * is legit; I postpone that to "run time"
+ * A "-" representation of "close me" shows up as a -3 here */
+ debug_printf("Duplicating redirect '%d>&%d'\n", redir->fd, redir->dup);
+ } else {
+ /* We do _not_ try to open the file that src points to,
+ * since we need to return and let src be expanded first.
+ * Set ctx->pending_redirect, so we know what to do at the
+ * end of the next parsed word.
+ */
+ ctx->pending_redirect = redir;
+ }
+ return 0;
+}
+
+static struct pipe *new_pipe(void) {
+ struct pipe *pi;
+ pi = xmalloc(sizeof(struct pipe));
+ pi->num_progs = 0;
+ pi->progs = NULL;
+ pi->next = NULL;
+ pi->followup = 0; /* invalid */
+ pi->r_mode = RES_NONE;
+ return pi;
+}
+
+static void initialize_context(struct p_context *ctx)
+{
+ ctx->pipe = NULL;
+ ctx->pending_redirect = NULL;
+ ctx->child = NULL;
+ ctx->list_head = new_pipe();
+ ctx->pipe = ctx->list_head;
+ ctx->w = RES_NONE;
+ ctx->stack = NULL;
+ ctx->old_flag = 0;
+ done_command(ctx); /* creates the memory for working child */
+}
+
+/* normal return is 0
+ * if a reserved word is found, and processed, return 1
+ * should handle if, then, elif, else, fi, for, while, until, do, done.
+ * case, function, and select are obnoxious, save those for later.
+ */
+static int reserved_word(o_string *dest, struct p_context *ctx)
+{
+ struct reserved_combo {
+ const char *literal;
+ int code;
+ long flag;
+ };
+ /* Mostly a list of accepted follow-up reserved words.
+ * FLAG_END means we are done with the sequence, and are ready
+ * to turn the compound list into a command.
+ * FLAG_START means the word must start a new compound list.
+ */
+ static const struct reserved_combo reserved_list[] = {
+ { "if", RES_IF, FLAG_THEN | FLAG_START },
+ { "then", RES_THEN, FLAG_ELIF | FLAG_ELSE | FLAG_FI },
+ { "elif", RES_ELIF, FLAG_THEN },
+ { "else", RES_ELSE, FLAG_FI },
+ { "fi", RES_FI, FLAG_END },
+ { "for", RES_FOR, FLAG_IN | FLAG_START },
+ { "while", RES_WHILE, FLAG_DO | FLAG_START },
+ { "until", RES_UNTIL, FLAG_DO | FLAG_START },
+ { "in", RES_IN, FLAG_DO },
+ { "do", RES_DO, FLAG_DONE },
+ { "done", RES_DONE, FLAG_END }
+ };
+ const struct reserved_combo *r;
+#define NRES sizeof(reserved_list)/sizeof(reserved_list[0])
+
+ for (r = reserved_list; r < reserved_list+NRES; r++) {
+ if (strcmp(dest->data, r->literal) == 0) {
+ debug_printf("found reserved word %s, code %d\n",r->literal,r->code);
+ if (r->flag & FLAG_START) {
+ struct p_context *new = xmalloc(sizeof(struct p_context));
+ debug_printf("push stack\n");
+ if (ctx->w == RES_IN || ctx->w == RES_FOR) {
+ syntax();
+ free(new);
+ ctx->w = RES_SNTX;
+ b_reset(dest);
+ return 1;
+ }
+ *new = *ctx; /* physical copy */
+ initialize_context(ctx);
+ ctx->stack = new;
+ } else if (ctx->w == RES_NONE || !(ctx->old_flag & (1 << r->code))) {
+ syntax();
+ ctx->w = RES_SNTX;
+ b_reset(dest);
+ return 1;
+ }
+ ctx->w = r->code;
+ ctx->old_flag = r->flag;
+ if (ctx->old_flag & FLAG_END) {
+ struct p_context *old;
+ debug_printf("pop stack\n");
+ done_pipe(ctx,PIPE_SEQ);
+ old = ctx->stack;
+ old->child->group = ctx->list_head;
+ old->child->subshell = 0;
+ *ctx = *old; /* physical copy */
+ free(old);
+ }
+ b_reset (dest);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* normal return is 0.
+ * Syntax or xglob errors return 1. */
+static int done_word(o_string *dest, struct p_context *ctx)
+{
+ struct child_prog *child = ctx->child;
+ glob_t *glob_target;
+ int gr, flags = 0;
+
+ debug_printf("done_word: %s %p\n", dest->data, child);
+ if (dest->length == 0 && !dest->nonnull) {
+ debug_printf(" true null, ignored\n");
+ return 0;
+ }
+ if (ctx->pending_redirect) {
+ glob_target = &ctx->pending_redirect->word;
+ } else {
+ if (child->group) {
+ syntax();
+ return 1; /* syntax error, groups and arglists don't mix */
+ }
+ if (!child->argv && (ctx->type & FLAG_PARSE_SEMICOLON)) {
+ debug_printf("checking %s for reserved-ness\n",dest->data);
+ if (reserved_word(dest,ctx))
+ return (ctx->w == RES_SNTX);
+ }
+ glob_target = &child->glob_result;
+ if (child->argv) flags |= GLOB_APPEND;
+ }
+ gr = xglob(dest, flags, glob_target);
+ if (gr != 0) return 1;
+
+ b_reset(dest);
+ if (ctx->pending_redirect) {
+ ctx->pending_redirect = NULL;
+ if (glob_target->gl_pathc != 1) {
+ bb_error_msg("ambiguous redirect");
+ return 1;
+ }
+ } else {
+ child->argv = glob_target->gl_pathv;
+ }
+ if (ctx->w == RES_FOR) {
+ done_word(dest,ctx);
+ done_pipe(ctx,PIPE_SEQ);
+ }
+ return 0;
+}
+
+/* The only possible error here is out of memory, in which case
+ * xmalloc exits. */
+static int done_command(struct p_context *ctx)
+{
+ /* The child is really already in the pipe structure, so
+ * advance the pipe counter and make a new, null child.
+ * Only real trickiness here is that the uncommitted
+ * child structure, to which ctx->child points, is not
+ * counted in pi->num_progs. */
+ struct pipe *pi = ctx->pipe;
+ struct child_prog *prog = ctx->child;
+
+ if (prog && prog->group == NULL
+ && prog->argv == NULL
+ && prog->redirects == NULL) {
+ debug_printf("done_command: skipping null command\n");
+ return 0;
+ } else if (prog) {
+ pi->num_progs++;
+ debug_printf("done_command: num_progs incremented to %d\n",pi->num_progs);
+ } else {
+ debug_printf("done_command: initializing\n");
+ }
+ pi->progs = xrealloc(pi->progs, sizeof(*pi->progs) * (pi->num_progs+1));
+
+ prog = pi->progs + pi->num_progs;
+ prog->redirects = NULL;
+ prog->argv = NULL;
+ prog->is_stopped = 0;
+ prog->group = NULL;
+ prog->glob_result.gl_pathv = NULL;
+ prog->family = pi;
+ prog->sp = 0;
+ ctx->child = prog;
+ prog->type = ctx->type;
+
+ /* but ctx->pipe and ctx->list_head remain unchanged */
+ return 0;
+}
+
+static int done_pipe(struct p_context *ctx, pipe_style type)
+{
+ struct pipe *new_p;
+ done_command(ctx); /* implicit closure of previous command */
+ debug_printf("done_pipe, type %d\n", type);
+ ctx->pipe->followup = type;
+ ctx->pipe->r_mode = ctx->w;
+ new_p = new_pipe();
+ ctx->pipe->next = new_p;
+ ctx->pipe = new_p;
+ ctx->child = NULL;
+ done_command(ctx); /* set up new pipe to accept commands */
+ return 0;
+}
+
+/* peek ahead in the in_str to find out if we have a "&n" construct,
+ * as in "2>&1", that represents duplicating a file descriptor.
+ * returns either -2 (syntax error), -1 (no &), or the number found.
+ */
+static int redirect_dup_num(struct in_str *input)
+{
+ int ch, d = 0, ok = 0;
+ ch = b_peek(input);
+ if (ch != '&') return -1;
+
+ b_getch(input); /* get the & */
+ ch = b_peek(input);
+ if (ch == '-') {
+ b_getch(input);
+ return -3; /* "-" represents "close me" */
+ }
+ while (isdigit(ch)) {
+ d = d*10+(ch-'0');
+ ok = 1;
+ b_getch(input);
+ ch = b_peek(input);
+ }
+ if (ok) return d;
+
+ bb_error_msg("ambiguous redirect");
+ return -2;
+}
+
+/* If a redirect is immediately preceded by a number, that number is
+ * supposed to tell which file descriptor to redirect. This routine
+ * looks for such preceding numbers. In an ideal world this routine
+ * needs to handle all the following classes of redirects...
+ * echo 2>foo # redirects fd 2 to file "foo", nothing passed to echo
+ * echo 49>foo # redirects fd 49 to file "foo", nothing passed to echo
+ * echo -2>foo # redirects fd 1 to file "foo", "-2" passed to echo
+ * echo 49x>foo # redirects fd 1 to file "foo", "49x" passed to echo
+ * A -1 output from this program means no valid number was found, so the
+ * caller should use the appropriate default for this redirection.
+ */
+static int redirect_opt_num(o_string *o)
+{
+ int num;
+
+ if (o->length == 0)
+ return -1;
+ for (num = 0; num < o->length; num++) {
+ if (!isdigit(*(o->data + num))) {
+ return -1;
+ }
+ }
+ /* reuse num (and save an int) */
+ num = atoi(o->data);
+ b_reset(o);
+ return num;
+}
+
+static FILE *generate_stream_from_list(struct pipe *head)
+{
+ FILE *pf;
+ int pid, channel[2];
+ if (pipe(channel) < 0) bb_perror_msg_and_die("pipe");
+#if !defined(__UCLIBC__) || defined(__ARCH_HAS_MMU__)
+ pid = fork();
+#else
+ pid = vfork();
+#endif
+ if (pid < 0) {
+ bb_perror_msg_and_die("fork");
+ } else if (pid == 0) {
+ close(channel[0]);
+ if (channel[1] != 1) {
+ dup2(channel[1],1);
+ close(channel[1]);
+ }
+ _exit(run_list_real(head)); /* leaks memory */
+ }
+ debug_printf("forked child %d\n",pid);
+ close(channel[1]);
+ pf = fdopen(channel[0],"r");
+ debug_printf("pipe on FILE *%p\n",pf);
+ return pf;
+}
+
+/* this version hacked for testing purposes */
+/* return code is exit status of the process that is run. */
+static int process_command_subs(o_string *dest, struct p_context *ctx, struct in_str *input, int subst_end)
+{
+ int retcode;
+ o_string result = NULL_O_STRING;
+ struct p_context inner;
+ FILE *p;
+ struct in_str pipe_str;
+ initialize_context(&inner);
+
+ /* recursion to generate command */
+ retcode = parse_stream(&result, &inner, input, subst_end);
+ if (retcode != 0) return retcode; /* syntax error or EOF */
+ done_word(&result, &inner);
+ done_pipe(&inner, PIPE_SEQ);
+ b_free(&result);
+
+ p = generate_stream_from_list(inner.list_head);
+ if (p == NULL) return 1;
+ mark_open(fileno(p));
+ setup_file_in_str(&pipe_str, p);
+
+ /* now send results of command back into original context */
+ retcode = parse_stream(dest, ctx, &pipe_str, '\0');
+ /* XXX In case of a syntax error, should we try to kill the child?
+ * That would be tough to do right, so just read until EOF. */
+ if (retcode == 1) {
+ while (b_getch(&pipe_str) != EOF)
+ /* discard */;
+ }
+
+ debug_printf("done reading from pipe, pclose()ing\n");
+ /* This is the step that wait()s for the child. Should be pretty
+ * safe, since we just read an EOF from its stdout. We could try
+ * to better, by using wait(), and keeping track of background jobs
+ * at the same time. That would be a lot of work, and contrary
+ * to the KISS philosophy of this program. */
+ mark_closed(fileno(p));
+ retcode = pclose(p);
+ free_pipe_list(inner.list_head,0);
+ debug_printf("pclosed, retcode=%d\n",retcode);
+ /* XXX this process fails to trim a single trailing newline */
+ return retcode;
+}
+
+static int parse_group(o_string *dest, struct p_context *ctx,
+ struct in_str *input, int ch)
+{
+ int rcode, endch = 0;
+ struct p_context sub;
+ struct child_prog *child = ctx->child;
+ if (child->argv) {
+ syntax();
+ return 1; /* syntax error, groups and arglists don't mix */
+ }
+ initialize_context(&sub);
+ switch (ch) {
+ case '(':
+ endch = ')';
+ child->subshell = 1;
+ break;
+ case '{':
+ endch = '}';
+ break;
+ default:
+ syntax(); /* really logic error */
+ }
+ rcode = parse_stream(dest,&sub,input,endch);
+ done_word(dest,&sub); /* finish off the final word in the subcontext */
+ done_pipe(&sub, PIPE_SEQ); /* and the final command there, too */
+ child->group = sub.list_head;
+ return rcode;
+ /* child remains "open", available for possible redirects */
+}
+
+/* basically useful version until someone wants to get fancier,
+ * see the bash man page under "Parameter Expansion" */
+static const char *lookup_param(const char *src)
+{
+ const char *p = NULL;
+ if (src) {
+ p = getenv(src);
+ if (!p)
+ p = get_local_var(src);
+ }
+ return p;
+}
+
+/* return code: 0 for OK, 1 for syntax error */
+static int handle_dollar(o_string *dest, struct p_context *ctx, struct in_str *input)
+{
+ int i, advance = 0;
+ char sep[] = " ";
+ int ch = input->peek(input); /* first character after the $ */
+ debug_printf("handle_dollar: ch=%c\n",ch);
+ if (isalpha(ch)) {
+ b_addchr(dest, SPECIAL_VAR_SYMBOL);
+ ctx->child->sp++;
+ while (ch = b_peek(input),isalnum(ch) || ch == '_') {
+ b_getch(input);
+ b_addchr(dest,ch);
+ }
+ b_addchr(dest, SPECIAL_VAR_SYMBOL);
+ } else if (isdigit(ch)) {
+ i = ch-'0'; /* XXX is $0 special? */
+ if (i < global_argc) {
+ parse_string(dest, ctx, global_argv[i]); /* recursion */
+ }
+ advance = 1;
+ } else switch (ch) {
+ case '$':
+ b_adduint(dest,getpid());
+ advance = 1;
+ break;
+ case '!':
+ if (last_bg_pid > 0) b_adduint(dest, last_bg_pid);
+ advance = 1;
+ break;
+ case '?':
+ b_adduint(dest,last_return_code);
+ advance = 1;
+ break;
+ case '#':
+ b_adduint(dest,global_argc ? global_argc-1 : 0);
+ advance = 1;
+ break;
+ case '{':
+ b_addchr(dest, SPECIAL_VAR_SYMBOL);
+ ctx->child->sp++;
+ b_getch(input);
+ /* XXX maybe someone will try to escape the '}' */
+ while (1) {
+ ch = b_getch(input);
+ if (ch == EOF || ch == '}')
+ break;
+ b_addchr(dest,ch);
+ }
+ if (ch != '}') {
+ syntax();
+ return 1;
+ }
+ b_addchr(dest, SPECIAL_VAR_SYMBOL);
+ break;
+ case '(':
+ b_getch(input);
+ process_command_subs(dest, ctx, input, ')');
+ break;
+ case '*':
+ sep[0] = ifs[0];
+ for (i = 1; i < global_argc; i++) {
+ parse_string(dest, ctx, global_argv[i]);
+ if (i+1 < global_argc)
+ parse_string(dest, ctx, sep);
+ }
+ break;
+ case '@':
+ case '-':
+ case '_':
+ /* still unhandled, but should be eventually */
+ bb_error_msg("unhandled syntax: $%c",ch);
+ return 1;
+ break;
+ default:
+ b_addqchr(dest,'$',dest->quote);
+ }
+ /* Eat the character if the flag was set. If the compiler
+ * is smart enough, we could substitute "b_getch(input);"
+ * for all the "advance = 1;" above, and also end up with
+ * a nice size-optimized program. Hah! That'll be the day.
+ */
+ if (advance) b_getch(input);
+ return 0;
+}
+
+int parse_string(o_string *dest, struct p_context *ctx, const char *src)
+{
+ struct in_str foo;
+ setup_string_in_str(&foo, src);
+ return parse_stream(dest, ctx, &foo, '\0');
+}
+
+/* return code is 0 for normal exit, 1 for syntax error */
+int parse_stream(o_string *dest, struct p_context *ctx,
+ struct in_str *input, int end_trigger)
+{
+ int ch, m;
+ int redir_fd;
+ redir_type redir_style;
+ int next;
+
+ /* Only double-quote state is handled in the state variable dest->quote.
+ * A single-quote triggers a bypass of the main loop until its mate is
+ * found. When recursing, quote state is passed in via dest->quote. */
+
+ debug_printf("parse_stream, end_trigger=%d\n",end_trigger);
+ while ((ch = b_getch(input)) != EOF) {
+ m = map[ch];
+ next = (ch == '\n') ? 0 : b_peek(input);
+ debug_printf("parse_stream: ch=%c (%d) m=%d quote=%d\n",
+ ch, ch, m, dest->quote);
+ if (m == 0 || ((m == 1 || m == 2) && dest->quote)) {
+ b_addqchr(dest, ch, dest->quote);
+ continue;
+ }
+ if (m == 2) { /* unquoted IFS */
+ if (done_word(dest, ctx)) {
+ return 1;
+ }
+ /* If we aren't performing a substitution, treat a newline as a
+ * command separator. */
+ if (end_trigger != '\0' && ch == '\n')
+ done_pipe(ctx,PIPE_SEQ);
+ }
+ if (ch == end_trigger && !dest->quote && ctx->w == RES_NONE) {
+ debug_printf("leaving parse_stream (triggered)\n");
+ return 0;
+ }
+ if (m == 2)
+ continue;
+ switch (ch) {
+ case '#':
+ if (dest->length == 0 && !dest->quote) {
+ while (1) {
+ ch = b_peek(input);
+ if (ch == EOF || ch == '\n')
+ break;
+ b_getch(input);
+ }
+ } else {
+ b_addqchr(dest, ch, dest->quote);
+ }
+ break;
+ case '\\':
+ if (next == EOF) {
+ syntax();
+ return 1;
+ }
+ b_addqchr(dest, '\\', dest->quote);
+ b_addqchr(dest, b_getch(input), dest->quote);
+ break;
+ case '$':
+ if (handle_dollar(dest, ctx, input) != 0) return 1;
+ break;
+ case '\'':
+ dest->nonnull = 1;
+ while (1) {
+ ch = b_getch(input);
+ if (ch == EOF || ch == '\'')
+ break;
+ b_addchr(dest,ch);
+ }
+ if (ch == EOF) {
+ syntax();
+ return 1;
+ }
+ break;
+ case '"':
+ dest->nonnull = 1;
+ dest->quote = !dest->quote;
+ break;
+ case '`':
+ process_command_subs(dest, ctx, input, '`');
+ break;
+ case '>':
+ redir_fd = redirect_opt_num(dest);
+ done_word(dest, ctx);
+ redir_style = REDIRECT_OVERWRITE;
+ if (next == '>') {
+ redir_style = REDIRECT_APPEND;
+ b_getch(input);
+ } else if (next == '(') {
+ syntax(); /* until we support >(list) Process Substitution */
+ return 1;
+ }
+ setup_redirect(ctx, redir_fd, redir_style, input);
+ break;
+ case '<':
+ redir_fd = redirect_opt_num(dest);
+ done_word(dest, ctx);
+ redir_style = REDIRECT_INPUT;
+ if (next == '<') {
+ redir_style = REDIRECT_HEREIS;
+ b_getch(input);
+ } else if (next == '>') {
+ redir_style = REDIRECT_IO;
+ b_getch(input);
+ } else if (next == '(') {
+ syntax(); /* until we support <(list) Process Substitution */
+ return 1;
+ }
+ setup_redirect(ctx, redir_fd, redir_style, input);
+ break;
+ case ';':
+ done_word(dest, ctx);
+ done_pipe(ctx,PIPE_SEQ);
+ break;
+ case '&':
+ done_word(dest, ctx);
+ if (next == '&') {
+ b_getch(input);
+ done_pipe(ctx,PIPE_AND);
+ } else {
+ done_pipe(ctx,PIPE_BG);
+ }
+ break;
+ case '|':
+ done_word(dest, ctx);
+ if (next == '|') {
+ b_getch(input);
+ done_pipe(ctx,PIPE_OR);
+ } else {
+ /* we could pick up a file descriptor choice here
+ * with redirect_opt_num(), but bash doesn't do it.
+ * "echo foo 2| cat" yields "foo 2". */
+ done_command(ctx);
+ }
+ break;
+ case '(':
+ case '{':
+ if (parse_group(dest, ctx, input, ch) != 0)
+ return 1;
+ break;
+ case ')':
+ case '}':
+ syntax(); /* Proper use of this character caught by end_trigger */
+ return 1;
+ default:
+ syntax(); /* this is really an internal logic error */
+ return 1;
+ }
+ }
+ /* complain if quote? No, maybe we just finished a command substitution
+ * that was quoted. Example:
+ * $ echo "`cat foo` plus more"
+ * and we just got the EOF generated by the subshell that ran "cat foo"
+ * The only real complaint is if we got an EOF when end_trigger != '\0',
+ * that is, we were really supposed to get end_trigger, and never got
+ * one before the EOF. Can't use the standard "syntax error" return code,
+ * so that parse_stream_outer can distinguish the EOF and exit smoothly. */
+ debug_printf("leaving parse_stream (EOF)\n");
+ if (end_trigger != '\0') return -1;
+ return 0;
+}
+
+static void mapset(const char *set, int code)
+{
+ const unsigned char *s;
+ for (s = (const unsigned char *)set; *s; s++) map[(int)*s] = code;
+}
+
+static void update_ifs_map(void)
+{
+ /* char *ifs and char map[256] are both globals. */
+ ifs = getenv("IFS");
+ if (ifs == NULL) ifs = " \t\n";
+ /* Precompute a list of 'flow through' behavior so it can be treated
+ * quickly up front. Computation is necessary because of IFS.
+ * Special case handling of IFS == " \t\n" is not implemented.
+ * The map[] array only really needs two bits each, and on most machines
+ * that would be faster because of the reduced L1 cache footprint.
+ */
+ memset(map, 0, sizeof(map)); /* most characters flow through always */
+ mapset("\\$'\"`", 3); /* never flow through */
+ mapset("<>;&|(){}#", 1); /* flow through if quoted */
+ mapset(ifs, 2); /* also flow through if quoted */
+}
+
+/* most recursion does not come through here, the exception is
+ * from builtin_source() */
+int parse_stream_outer(struct in_str *inp, int flag)
+{
+
+ struct p_context ctx;
+ o_string temp = NULL_O_STRING;
+ int rcode;
+ do {
+ ctx.type = flag;
+ initialize_context(&ctx);
+ update_ifs_map();
+ if (!(flag & FLAG_PARSE_SEMICOLON) || (flag & FLAG_REPARSING)) mapset(";$&|", 0);
+ inp->promptmode = 1;
+ rcode = parse_stream(&temp, &ctx, inp, '\n');
+ if (rcode != 1 && ctx.old_flag != 0) {
+ syntax();
+ }
+ if (rcode != 1 && ctx.old_flag == 0) {
+ done_word(&temp, &ctx);
+ done_pipe(&ctx,PIPE_SEQ);
+ run_list(ctx.list_head);
+ } else {
+ if (ctx.old_flag != 0) {
+ free(ctx.stack);
+ b_reset(&temp);
+ }
+ temp.nonnull = 0;
+ temp.quote = 0;
+ inp->p = NULL;
+ free_pipe_list(ctx.list_head,0);
+ }
+ b_free(&temp);
+ } while (rcode != -1 && !(flag & FLAG_EXIT_FROM_LOOP)); /* loop on syntax errors, return on EOF */
+ return 0;
+}
+
+static int parse_string_outer(const char *s, int flag)
+{
+ struct in_str input;
+ setup_string_in_str(&input, s);
+ return parse_stream_outer(&input, flag);
+}
+
+static int parse_file_outer(FILE *f)
+{
+ int rcode;
+ struct in_str input;
+ setup_file_in_str(&input, f);
+ rcode = parse_stream_outer(&input, FLAG_PARSE_SEMICOLON);
+ return rcode;
+}
+
+/* Make sure we have a controlling tty. If we get started under a job
+ * aware app (like bash for example), make sure we are now in charge so
+ * we don't fight over who gets the foreground */
+static void setup_job_control(void)
+{
+ static pid_t shell_pgrp;
+ /* Loop until we are in the foreground. */
+ while (tcgetpgrp (shell_terminal) != (shell_pgrp = getpgrp ()))
+ kill (- shell_pgrp, SIGTTIN);
+
+ /* Ignore interactive and job-control signals. */
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ signal(SIGTERM, SIG_IGN);
+ signal(SIGTSTP, SIG_IGN);
+ signal(SIGTTIN, SIG_IGN);
+ signal(SIGTTOU, SIG_IGN);
+ signal(SIGCHLD, SIG_IGN);
+
+ /* Put ourselves in our own process group. */
+ setsid();
+ shell_pgrp = getpid();
+ setpgid(shell_pgrp, shell_pgrp);
+
+ /* Grab control of the terminal. */
+ tcsetpgrp(shell_terminal, shell_pgrp);
+}
+
+int hush_main(int argc, char **argv);
+int hush_main(int argc, char **argv)
+{
+ int opt;
+ FILE *input;
+ char **e = environ;
+
+#if ENABLE_FEATURE_EDITING
+ line_input_state = new_line_input_t(FOR_SHELL);
+#endif
+
+ /* XXX what should these be while sourcing /etc/profile? */
+ global_argc = argc;
+ global_argv = argv;
+
+ /* (re?) initialize globals. Sometimes hush_main() ends up calling
+ * hush_main(), therefore we cannot rely on the BSS to zero out this
+ * stuff. Reset these to 0 every time. */
+ ifs = NULL;
+ /* map[] is taken care of with call to update_ifs_map() */
+ fake_mode = 0;
+ interactive = 0;
+ close_me_head = NULL;
+ last_bg_pid = 0;
+ job_list = NULL;
+ last_jobid = 0;
+
+ /* Initialize some more globals to non-zero values */
+ set_cwd();
+ if (ENABLE_FEATURE_EDITING)
+ cmdedit_set_initial_prompt();
+ else PS1 = NULL;
+ PS2 = "> ";
+
+ /* initialize our shell local variables with the values
+ * currently living in the environment */
+ if (e) {
+ for (; *e; e++)
+ set_local_var(*e, 2); /* without call putenv() */
+ }
+
+ last_return_code = EXIT_SUCCESS;
+
+ if (argv[0] && argv[0][0] == '-') {
+ debug_printf("\nsourcing /etc/profile\n");
+ if ((input = fopen("/etc/profile", "r")) != NULL) {
+ mark_open(fileno(input));
+ parse_file_outer(input);
+ mark_closed(fileno(input));
+ fclose(input);
+ }
+ }
+ input = stdin;
+
+ while ((opt = getopt(argc, argv, "c:xif")) > 0) {
+ switch (opt) {
+ case 'c':
+ {
+ global_argv = argv+optind;
+ global_argc = argc-optind;
+ opt = parse_string_outer(optarg, FLAG_PARSE_SEMICOLON);
+ goto final_return;
+ }
+ break;
+ case 'i':
+ interactive++;
+ break;
+ case 'f':
+ fake_mode++;
+ break;
+ default:
+#ifndef BB_VER
+ fprintf(stderr, "Usage: sh [FILE]...\n"
+ " or: sh -c command [args]...\n\n");
+ exit(EXIT_FAILURE);
+#else
+ bb_show_usage();
+#endif
+ }
+ }
+ /* A shell is interactive if the `-i' flag was given, or if all of
+ * the following conditions are met:
+ * no -c command
+ * no arguments remaining or the -s flag given
+ * standard input is a terminal
+ * standard output is a terminal
+ * Refer to Posix.2, the description of the `sh' utility. */
+ if (argv[optind] == NULL && input == stdin
+ && isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)
+ ) {
+ interactive++;
+ }
+
+ debug_printf("\ninteractive=%d\n", interactive);
+ if (interactive) {
+ /* Looks like they want an interactive shell */
+#if !ENABLE_FEATURE_SH_EXTRA_QUIET
+ printf( "\n\n%s hush - the humble shell v0.01 (testing)\n",
+ BB_BANNER);
+ printf( "Enter 'help' for a list of built-in commands.\n\n");
+#endif
+ setup_job_control();
+ }
+
+ if (argv[optind] == NULL) {
+ opt = parse_file_outer(stdin);
+ goto final_return;
+ }
+
+ debug_printf("\nrunning script '%s'\n", argv[optind]);
+ global_argv = argv + optind;
+ global_argc = argc - optind;
+ input = xfopen(argv[optind], "r");
+ opt = parse_file_outer(input);
+
+#if ENABLE_FEATURE_CLEAN_UP
+ fclose(input);
+ if (cwd && cwd != bb_msg_unknown)
+ free((char*)cwd);
+ {
+ struct variables *cur, *tmp;
+ for (cur = top_vars; cur; cur = tmp) {
+ tmp = cur->next;
+ if (!cur->flg_read_only) {
+ free((char*)cur->name);
+ free((char*)cur->value);
+ free(cur);
+ }
+ }
+ }
+#endif
+
+final_return:
+ return opt ? opt : last_return_code;
+}
+
+static char *insert_var_value(char *inp)
+{
+ int res_str_len = 0;
+ int len;
+ int done = 0;
+ char *p, *res_str = NULL;
+ const char *p1;
+
+ while ((p = strchr(inp, SPECIAL_VAR_SYMBOL))) {
+ if (p != inp) {
+ len = p - inp;
+ res_str = xrealloc(res_str, (res_str_len + len));
+ strncpy((res_str + res_str_len), inp, len);
+ res_str_len += len;
+ }
+ inp = ++p;
+ p = strchr(inp, SPECIAL_VAR_SYMBOL);
+ *p = '\0';
+ p1 = lookup_param(inp);
+ if (p1) {
+ len = res_str_len + strlen(p1);
+ res_str = xrealloc(res_str, (1 + len));
+ strcpy((res_str + res_str_len), p1);
+ res_str_len = len;
+ }
+ *p = SPECIAL_VAR_SYMBOL;
+ inp = ++p;
+ done = 1;
+ }
+ if (done) {
+ res_str = xrealloc(res_str, (1 + res_str_len + strlen(inp)));
+ strcpy((res_str + res_str_len), inp);
+ while ((p = strchr(res_str, '\n'))) {
+ *p = ' ';
+ }
+ }
+ return (res_str == NULL) ? inp : res_str;
+}
+
+static char **make_list_in(char **inp, char *name)
+{
+ int len, i;
+ int name_len = strlen(name);
+ int n = 0;
+ char **list;
+ char *p1, *p2, *p3;
+
+ /* create list of variable values */
+ list = xmalloc(sizeof(*list));
+ for (i = 0; inp[i]; i++) {
+ p3 = insert_var_value(inp[i]);
+ p1 = p3;
+ while (*p1) {
+ if ((*p1 == ' ')) {
+ p1++;
+ continue;
+ }
+ if ((p2 = strchr(p1, ' '))) {
+ len = p2 - p1;
+ } else {
+ len = strlen(p1);
+ p2 = p1 + len;
+ }
+ /* we use n + 2 in realloc for list,because we add
+ * new element and then we will add NULL element */
+ list = xrealloc(list, sizeof(*list) * (n + 2));
+ list[n] = xmalloc(2 + name_len + len);
+ strcpy(list[n], name);
+ strcat(list[n], "=");
+ strncat(list[n], p1, len);
+ list[n++][name_len + len + 1] = '\0';
+ p1 = p2;
+ }
+ if (p3 != inp[i]) free(p3);
+ }
+ list[n] = NULL;
+ return list;
+}
+
+/* Make new string for parser */
+static char* make_string(char ** inp)
+{
+ char *p;
+ char *str = NULL;
+ int n;
+ int len = 2;
+
+ for (n = 0; inp[n]; n++) {
+ p = insert_var_value(inp[n]);
+ str = xrealloc(str, (len + strlen(p)));
+ if (n) {
+ strcat(str, " ");
+ } else {
+ *str = '\0';
+ }
+ strcat(str, p);
+ len = strlen(str) + 3;
+ if (p != inp[n]) free(p);
+ }
+ len = strlen(str);
+ *(str + len) = '\n';
+ *(str + len + 1) = '\0';
+ return str;
+}
diff --git a/i/pc104/initrd/conf/busybox/shell/lash.c b/i/pc104/initrd/conf/busybox/shell/lash.c
new file mode 100644
index 0000000..09067fd
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/shell/lash.c
@@ -0,0 +1,1582 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * lash -- the BusyBox Lame-Ass SHell
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Based in part on ladsh.c by Michael K. Johnson and Erik W. Troan, which is
+ * under the following liberal license: "We have placed this source code in the
+ * public domain. Use it in any project, free or commercial."
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ */
+
+/* This shell's parsing engine is officially at a dead-end. Future
+ * work shell work should be done using hush, msh, or ash. This is
+ * still a very useful, small shell -- it just don't need any more
+ * features beyond what it already has...
+ */
+
+//For debugging/development on the shell only...
+//#define DEBUG_SHELL
+
+
+#include "busybox.h"
+#include <getopt.h>
+#include <glob.h>
+#define expand_t glob_t
+
+/* Always enable for the moment... */
+#define CONFIG_LASH_PIPE_N_REDIRECTS
+#define CONFIG_LASH_JOB_CONTROL
+
+enum { MAX_READ = 128 }; /* size of input buffer for 'read' builtin */
+#define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n"
+
+
+#ifdef CONFIG_LASH_PIPE_N_REDIRECTS
+enum redir_type { REDIRECT_INPUT, REDIRECT_OVERWRITE,
+ REDIRECT_APPEND
+};
+#endif
+
+enum {
+ DEFAULT_CONTEXT = 0x1,
+ IF_TRUE_CONTEXT = 0x2,
+ IF_FALSE_CONTEXT = 0x4,
+ THEN_EXP_CONTEXT = 0x8,
+ ELSE_EXP_CONTEXT = 0x10
+};
+
+#define LASH_OPT_DONE (1)
+#define LASH_OPT_SAW_QUOTE (2)
+
+#ifdef CONFIG_LASH_PIPE_N_REDIRECTS
+struct redir_struct {
+ enum redir_type type; /* type of redirection */
+ int fd; /* file descriptor being redirected */
+ char *filename; /* file to redirect fd to */
+};
+#endif
+
+struct child_prog {
+ pid_t pid; /* 0 if exited */
+ char **argv; /* program name and arguments */
+ int num_redirects; /* elements in redirection array */
+ int is_stopped; /* is the program currently running? */
+ struct job *family; /* pointer back to the child's parent job */
+#ifdef CONFIG_LASH_PIPE_N_REDIRECTS
+ struct redir_struct *redirects; /* I/O redirects */
+#endif
+};
+
+struct jobset {
+ struct job *head; /* head of list of running jobs */
+ struct job *fg; /* current foreground job */
+};
+
+struct job {
+ int jobid; /* job number */
+ int num_progs; /* total number of programs in job */
+ int running_progs; /* number of programs running */
+ char *text; /* name of job */
+ char *cmdbuf; /* buffer various argv's point into */
+ pid_t pgrp; /* process group ID for the job */
+ struct child_prog *progs; /* array of programs in job */
+ struct job *next; /* to track background commands */
+ int stopped_progs; /* number of programs alive, but stopped */
+ unsigned int job_context; /* bitmask defining current context */
+ struct jobset *job_list;
+};
+
+struct built_in_command {
+ const char *cmd; /* name */
+ const char *descr; /* description */
+ int (*function) (struct child_prog *); /* function ptr */
+};
+
+/* function prototypes for builtins */
+static int builtin_cd(struct child_prog *cmd);
+static int builtin_exec(struct child_prog *cmd);
+static int builtin_exit(struct child_prog *cmd);
+static int builtin_fg_bg(struct child_prog *cmd);
+static int builtin_help(struct child_prog *cmd);
+static int builtin_jobs(struct child_prog *dummy);
+static int builtin_pwd(struct child_prog *dummy);
+static int builtin_export(struct child_prog *cmd);
+static int builtin_source(struct child_prog *cmd);
+static int builtin_unset(struct child_prog *cmd);
+static int builtin_read(struct child_prog *cmd);
+
+
+/* function prototypes for shell stuff */
+static void checkjobs(struct jobset *job_list);
+static void remove_job(struct jobset *j_list, struct job *job);
+static int get_command(FILE * source, char *command);
+static int parse_command(char **command_ptr, struct job *job, int *inbg);
+static int run_command(struct job *newjob, int inbg, int outpipe[2]);
+static int pseudo_exec(struct child_prog *cmd) ATTRIBUTE_NORETURN;
+static int busy_loop(FILE * input);
+
+
+/* Table of built-in functions (these are non-forking builtins, meaning they
+ * can change global variables in the parent shell process but they will not
+ * work with pipes and redirects; 'unset foo | whatever' will not work) */
+static const struct built_in_command bltins[] = {
+ {"bg", "Resume a job in the background", builtin_fg_bg},
+ {"cd", "Change working directory", builtin_cd},
+ {"exec", "Exec command, replacing this shell with the exec'd process", builtin_exec},
+ {"exit", "Exit from shell()", builtin_exit},
+ {"fg", "Bring job into the foreground", builtin_fg_bg},
+ {"jobs", "Lists the active jobs", builtin_jobs},
+ {"export", "Set environment variable", builtin_export},
+ {"unset", "Unset environment variable", builtin_unset},
+ {"read", "Input environment variable", builtin_read},
+ {".", "Source-in and run commands in a file", builtin_source},
+ /* to do: add ulimit */
+ {NULL, NULL, NULL}
+};
+
+/* Table of forking built-in functions (things that fork cannot change global
+ * variables in the parent process, such as the current working directory) */
+static struct built_in_command bltins_forking[] = {
+ {"pwd", "Print current directory", builtin_pwd},
+ {"help", "List shell built-in commands", builtin_help},
+ {NULL, NULL, NULL}
+};
+
+
+static int shell_context; /* Type prompt trigger (PS1 or PS2) */
+
+
+/* Globals that are static to this file */
+static const char *cwd;
+static char *local_pending_command;
+static struct jobset job_list = { NULL, NULL };
+static int argc;
+static char **argv;
+static llist_t *close_me_list;
+static int last_return_code;
+static int last_bg_pid;
+static unsigned int last_jobid;
+static int shell_terminal;
+static const char *PS1;
+static const char *PS2 = "> ";
+
+
+#ifdef DEBUG_SHELL
+static inline void debug_printf(const char *format, ...)
+{
+ va_list args;
+ va_start(args, format);
+ vfprintf(stderr, format, args);
+ va_end(args);
+}
+#else
+static inline void debug_printf(const char ATTRIBUTE_UNUSED *format, ...) { }
+#endif
+
+/*
+ Most builtins need access to the struct child_prog that has
+ their arguments, previously coded as cmd->progs[0]. That coding
+ can exhibit a bug, if the builtin is not the first command in
+ a pipeline: "echo foo | exec sort" will attempt to exec foo.
+
+builtin previous use notes
+------ ----------------- ---------
+cd cmd->progs[0]
+exec cmd->progs[0] squashed bug: didn't look for applets or forking builtins
+exit cmd->progs[0]
+fg_bg cmd->progs[0], job_list->head, job_list->fg
+help 0
+jobs job_list->head
+pwd 0
+export cmd->progs[0]
+source cmd->progs[0]
+unset cmd->progs[0]
+read cmd->progs[0]
+
+I added "struct job *family;" to struct child_prog,
+and switched API to builtin_foo(struct child_prog *child);
+So cmd->text becomes child->family->text
+ cmd->job_context becomes child->family->job_context
+ cmd->progs[0] becomes *child
+ job_list becomes child->family->job_list
+ */
+
+/* built-in 'cd <path>' handler */
+static int builtin_cd(struct child_prog *child)
+{
+ char *newdir;
+
+ if (child->argv[1] == NULL)
+ newdir = getenv("HOME");
+ else
+ newdir = child->argv[1];
+ if (chdir(newdir)) {
+ bb_perror_msg("cd: %s", newdir);
+ return EXIT_FAILURE;
+ }
+ cwd = xrealloc_getcwd_or_warn((char *)cwd);
+ if (!cwd)
+ cwd = bb_msg_unknown;
+ return EXIT_SUCCESS;
+}
+
+/* built-in 'exec' handler */
+static int builtin_exec(struct child_prog *child)
+{
+ if (child->argv[1] == NULL)
+ return EXIT_SUCCESS; /* Really? */
+ child->argv++;
+ while(close_me_list) close((long)llist_pop(&close_me_list));
+ pseudo_exec(child);
+ /* never returns */
+}
+
+/* built-in 'exit' handler */
+static int builtin_exit(struct child_prog *child)
+{
+ if (child->argv[1] == NULL)
+ exit(EXIT_SUCCESS);
+
+ exit (atoi(child->argv[1]));
+}
+
+/* built-in 'fg' and 'bg' handler */
+static int builtin_fg_bg(struct child_prog *child)
+{
+ int i, jobnum;
+ struct job *job=NULL;
+
+ /* If they gave us no args, assume they want the last backgrounded task */
+ if (!child->argv[1]) {
+ for (job = child->family->job_list->head; job; job = job->next) {
+ if (job->jobid == last_jobid) {
+ break;
+ }
+ }
+ if (!job) {
+ bb_error_msg("%s: no current job", child->argv[0]);
+ return EXIT_FAILURE;
+ }
+ } else {
+ if (sscanf(child->argv[1], "%%%d", &jobnum) != 1) {
+ bb_error_msg(bb_msg_invalid_arg, child->argv[1], child->argv[0]);
+ return EXIT_FAILURE;
+ }
+ for (job = child->family->job_list->head; job; job = job->next) {
+ if (job->jobid == jobnum) {
+ break;
+ }
+ }
+ if (!job) {
+ bb_error_msg("%s: %d: no such job", child->argv[0], jobnum);
+ return EXIT_FAILURE;
+ }
+ }
+
+ if (*child->argv[0] == 'f') {
+ /* Put the job into the foreground. */
+ tcsetpgrp(shell_terminal, job->pgrp);
+
+ child->family->job_list->fg = job;
+ }
+
+ /* Restart the processes in the job */
+ for (i = 0; i < job->num_progs; i++)
+ job->progs[i].is_stopped = 0;
+
+ job->stopped_progs = 0;
+
+ if ( (i=kill(- job->pgrp, SIGCONT)) < 0) {
+ if (i == ESRCH) {
+ remove_job(&job_list, job);
+ } else {
+ bb_perror_msg("kill (SIGCONT)");
+ }
+ }
+
+ return EXIT_SUCCESS;
+}
+
+/* built-in 'help' handler */
+static int builtin_help(struct child_prog ATTRIBUTE_UNUSED *dummy)
+{
+ const struct built_in_command *x;
+
+ printf("\nBuilt-in commands:\n"
+ "-------------------\n");
+ for (x = bltins; x->cmd; x++) {
+ if (x->descr == NULL)
+ continue;
+ printf("%s\t%s\n", x->cmd, x->descr);
+ }
+ for (x = bltins_forking; x->cmd; x++) {
+ if (x->descr == NULL)
+ continue;
+ printf("%s\t%s\n", x->cmd, x->descr);
+ }
+ putchar('\n');
+ return EXIT_SUCCESS;
+}
+
+/* built-in 'jobs' handler */
+static int builtin_jobs(struct child_prog *child)
+{
+ struct job *job;
+ const char *status_string;
+
+ for (job = child->family->job_list->head; job; job = job->next) {
+ if (job->running_progs == job->stopped_progs)
+ status_string = "Stopped";
+ else
+ status_string = "Running";
+
+ printf(JOB_STATUS_FORMAT, job->jobid, status_string, job->text);
+ }
+ return EXIT_SUCCESS;
+}
+
+
+/* built-in 'pwd' handler */
+static int builtin_pwd(struct child_prog ATTRIBUTE_UNUSED *dummy)
+{
+ cwd = xrealloc_getcwd_or_warn((char *)cwd);
+ if (!cwd)
+ cwd = bb_msg_unknown;
+ puts(cwd);
+ return EXIT_SUCCESS;
+}
+
+/* built-in 'export VAR=value' handler */
+static int builtin_export(struct child_prog *child)
+{
+ int res;
+ char *v = child->argv[1];
+
+ if (v == NULL) {
+ char **e;
+ for (e = environ; *e; e++) {
+ puts(*e);
+ }
+ return 0;
+ }
+ res = putenv(v);
+ if (res)
+ bb_perror_msg("export");
+#ifdef CONFIG_FEATURE_EDITING_FANCY_PROMPT
+ if (strncmp(v, "PS1=", 4)==0)
+ PS1 = getenv("PS1");
+#endif
+
+#ifdef CONFIG_LOCALE_SUPPORT
+ // TODO: why getenv? "" would be just as good...
+ if(strncmp(v, "LC_ALL=", 7)==0)
+ setlocale(LC_ALL, getenv("LC_ALL"));
+ if(strncmp(v, "LC_CTYPE=", 9)==0)
+ setlocale(LC_CTYPE, getenv("LC_CTYPE"));
+#endif
+
+ return res;
+}
+
+/* built-in 'read VAR' handler */
+static int builtin_read(struct child_prog *child)
+{
+ int res = 0, len;
+ char *s;
+ char string[MAX_READ];
+
+ if (child->argv[1]) {
+ /* argument (VAR) given: put "VAR=" into buffer */
+ safe_strncpy(string, child->argv[1], MAX_READ-1);
+ len = strlen(string);
+ string[len++] = '=';
+ string[len] = '\0';
+ fgets(&string[len], sizeof(string) - len, stdin); /* read string */
+ res = strlen(string);
+ if (res > len)
+ string[--res] = '\0'; /* chomp trailing newline */
+ /*
+ ** string should now contain "VAR=<value>"
+ ** copy it (putenv() won't do that, so we must make sure
+ ** the string resides in a static buffer!)
+ */
+ res = -1;
+ if ((s = strdup(string)))
+ res = putenv(s);
+ if (res)
+ bb_perror_msg("read");
+ }
+ else
+ fgets(string, sizeof(string), stdin);
+
+ return res;
+}
+
+/* Built-in '.' handler (read-in and execute commands from file) */
+static int builtin_source(struct child_prog *child)
+{
+ FILE *input;
+ int status;
+
+ input = fopen_or_warn(child->argv[1], "r");
+ if (!input) {
+ return EXIT_FAILURE;
+ }
+
+ llist_add_to(&close_me_list, (void *)(long)fileno(input));
+ /* Now run the file */
+ status = busy_loop(input);
+ fclose(input);
+ llist_pop(&close_me_list);
+ return status;
+}
+
+/* built-in 'unset VAR' handler */
+static int builtin_unset(struct child_prog *child)
+{
+ if (child->argv[1] == NULL) {
+ printf(bb_msg_requires_arg, "unset");
+ return EXIT_FAILURE;
+ }
+ unsetenv(child->argv[1]);
+ return EXIT_SUCCESS;
+}
+
+#ifdef CONFIG_LASH_JOB_CONTROL
+/* free up all memory from a job */
+static void free_job(struct job *cmd)
+{
+ int i;
+ struct jobset *keep;
+
+ for (i = 0; i < cmd->num_progs; i++) {
+ free(cmd->progs[i].argv);
+#ifdef CONFIG_LASH_PIPE_N_REDIRECTS
+ if (cmd->progs[i].redirects)
+ free(cmd->progs[i].redirects);
+#endif
+ }
+ free(cmd->progs);
+ free(cmd->text);
+ free(cmd->cmdbuf);
+ keep = cmd->job_list;
+ memset(cmd, 0, sizeof(struct job));
+ cmd->job_list = keep;
+}
+
+/* remove a job from a jobset */
+static void remove_job(struct jobset *j_list, struct job *job)
+{
+ struct job *prevjob;
+
+ free_job(job);
+ if (job == j_list->head) {
+ j_list->head = job->next;
+ } else {
+ prevjob = j_list->head;
+ while (prevjob->next != job)
+ prevjob = prevjob->next;
+ prevjob->next = job->next;
+ }
+
+ if (j_list->head)
+ last_jobid = j_list->head->jobid;
+ else
+ last_jobid = 0;
+
+ free(job);
+}
+
+/* Checks to see if any background processes have exited -- if they
+ have, figure out why and see if a job has completed */
+static void checkjobs(struct jobset *j_list)
+{
+ struct job *job;
+ pid_t childpid;
+ int status;
+ int prognum = 0;
+
+ while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
+ for (job = j_list->head; job; job = job->next) {
+ prognum = 0;
+ while (prognum < job->num_progs &&
+ job->progs[prognum].pid != childpid) prognum++;
+ if (prognum < job->num_progs)
+ break;
+ }
+
+ /* This happens on backticked commands */
+ if(job==NULL)
+ return;
+
+ if (WIFEXITED(status) || WIFSIGNALED(status)) {
+ /* child exited */
+ job->running_progs--;
+ job->progs[prognum].pid = 0;
+
+ if (!job->running_progs) {
+ printf(JOB_STATUS_FORMAT, job->jobid, "Done", job->text);
+ last_jobid=0;
+ remove_job(j_list, job);
+ }
+ } else {
+ /* child stopped */
+ job->stopped_progs++;
+ job->progs[prognum].is_stopped = 1;
+ }
+ }
+
+ if (childpid == -1 && errno != ECHILD)
+ bb_perror_msg("waitpid");
+}
+#else
+static void checkjobs(struct jobset *j_list)
+{
+}
+static void free_job(struct job *cmd)
+{
+}
+static void remove_job(struct jobset *j_list, struct job *job)
+{
+}
+#endif
+
+#ifdef CONFIG_LASH_PIPE_N_REDIRECTS
+/* squirrel != NULL means we squirrel away copies of stdin, stdout,
+ * and stderr if they are redirected. */
+static int setup_redirects(struct child_prog *prog, int squirrel[])
+{
+ int i;
+ int openfd;
+ int mode = O_RDONLY;
+ struct redir_struct *redir = prog->redirects;
+
+ for (i = 0; i < prog->num_redirects; i++, redir++) {
+ switch (redir->type) {
+ case REDIRECT_INPUT:
+ mode = O_RDONLY;
+ break;
+ case REDIRECT_OVERWRITE:
+ mode = O_WRONLY | O_CREAT | O_TRUNC;
+ break;
+ case REDIRECT_APPEND:
+ mode = O_WRONLY | O_CREAT | O_APPEND;
+ break;
+ }
+
+ openfd = open(redir->filename, mode, 0666);
+ if (openfd < 0) {
+ /* this could get lost if stderr has been redirected, but
+ bash and ash both lose it as well (though zsh doesn't!) */
+ bb_perror_msg("error opening %s", redir->filename);
+ return 1;
+ }
+
+ if (openfd != redir->fd) {
+ if (squirrel && redir->fd < 3) {
+ squirrel[redir->fd] = dup(redir->fd);
+ fcntl (squirrel[redir->fd], F_SETFD, FD_CLOEXEC);
+ }
+ dup2(openfd, redir->fd);
+ close(openfd);
+ }
+ }
+
+ return 0;
+}
+
+static void restore_redirects(int squirrel[])
+{
+ int i, fd;
+ for (i=0; i<3; i++) {
+ fd = squirrel[i];
+ if (fd != -1) {
+ /* No error checking. I sure wouldn't know what
+ * to do with an error if I found one! */
+ dup2(fd, i);
+ close(fd);
+ }
+ }
+}
+#else
+static inline int setup_redirects(struct child_prog *prog, int squirrel[])
+{
+ return 0;
+}
+static inline void restore_redirects(int squirrel[])
+{
+}
+#endif
+
+static inline void cmdedit_set_initial_prompt(void)
+{
+#if !ENABLE_FEATURE_EDITING_FANCY_PROMPT
+ PS1 = NULL;
+#else
+ PS1 = getenv("PS1");
+ if(PS1==0)
+ PS1 = "\\w \\$ ";
+#endif
+}
+
+static inline const char* setup_prompt_string(void)
+{
+#if !ENABLE_FEATURE_EDITING_FANCY_PROMPT
+ /* Set up the prompt */
+ if (shell_context == 0) {
+ char *ns;
+ free((char*)PS1);
+ ns = xmalloc(strlen(cwd)+4);
+ sprintf(ns, "%s %c ", cwd, (geteuid() != 0) ? '$': '#');
+ PS1 = ns;
+ return ns;
+ } else {
+ return PS2;
+ }
+#else
+ return (shell_context==0)? PS1 : PS2;
+#endif
+}
+
+#if ENABLE_FEATURE_EDITING
+static line_input_t *line_input_state;
+#endif
+
+static int get_command(FILE * source, char *command)
+{
+ const char *prompt_str;
+
+ if (source == NULL) {
+ if (local_pending_command) {
+ /* a command specified (-c option): return it & mark it done */
+ strcpy(command, local_pending_command);
+ free(local_pending_command);
+ local_pending_command = NULL;
+ return 0;
+ }
+ return 1;
+ }
+
+ if (source == stdin) {
+ prompt_str = setup_prompt_string();
+
+#if ENABLE_FEATURE_EDITING
+ /*
+ ** enable command line editing only while a command line
+ ** is actually being read; otherwise, we'll end up bequeathing
+ ** atexit() handlers and other unwanted stuff to our
+ ** child processes (rob@sysgo.de)
+ */
+ read_line_input(prompt_str, command, BUFSIZ, line_input_state);
+ return 0;
+#else
+ fputs(prompt_str, stdout);
+#endif
+ }
+
+ if (!fgets(command, BUFSIZ - 2, source)) {
+ if (source == stdin)
+ puts("");
+ return 1;
+ }
+
+ return 0;
+}
+
+static char * strsep_space( char *string, int * ix)
+{
+ /* Short circuit the trivial case */
+ if ( !string || ! string[*ix])
+ return NULL;
+
+ /* Find the end of the token. */
+ while (string[*ix] && !isspace(string[*ix]) ) {
+ (*ix)++;
+ }
+
+ /* Find the end of any whitespace trailing behind
+ * the token and let that be part of the token */
+ while (string[*ix] && (isspace)(string[*ix]) ) {
+ (*ix)++;
+ }
+
+ if (!*ix) {
+ /* Nothing useful was found */
+ return NULL;
+ }
+
+ return xstrndup(string, *ix);
+}
+
+static int expand_arguments(char *command)
+{
+ int total_length=0, length, i, retval, ix = 0;
+ expand_t expand_result;
+ char *tmpcmd, *cmd, *cmd_copy;
+ char *src, *dst, *var;
+ const char * const out_of_space = "out of space during expansion";
+ int flags = GLOB_NOCHECK
+#ifdef GLOB_BRACE
+ | GLOB_BRACE
+#endif
+#ifdef GLOB_TILDE
+ | GLOB_TILDE
+#endif
+ ;
+
+ /* get rid of the terminating \n */
+ chomp(command);
+
+ /* Fix up escape sequences to be the Real Thing(tm) */
+ while( command && command[ix]) {
+ if (command[ix] == '\\') {
+ const char *tmp = command+ix+1;
+ command[ix] = bb_process_escape_sequence( &tmp );
+ memmove(command+ix + 1, tmp, strlen(tmp)+1);
+ }
+ ix++;
+ }
+ /* Use glob and then fixup environment variables and such */
+
+ /* It turns out that glob is very stupid. We have to feed it one word at a
+ * time since it can't cope with a full string. Here we convert command
+ * (char*) into cmd (char**, one word per string) */
+
+ /* We need a clean copy, so strsep can mess up the copy while
+ * we write stuff into the original (in a minute) */
+ cmd = cmd_copy = xstrdup(command);
+ *command = '\0';
+ for (ix = 0, tmpcmd = cmd;
+ (tmpcmd = strsep_space(cmd, &ix)) != NULL; cmd += ix, ix=0) {
+ if (*tmpcmd == '\0')
+ break;
+ /* we need to trim() the result for glob! */
+ trim(tmpcmd);
+ retval = glob(tmpcmd, flags, NULL, &expand_result);
+ free(tmpcmd); /* Free mem allocated by strsep_space */
+ if (retval == GLOB_NOSPACE) {
+ /* Mem may have been allocated... */
+ globfree (&expand_result);
+ bb_error_msg(out_of_space);
+ return FALSE;
+ } else if (retval != 0) {
+ /* Some other error. GLOB_NOMATCH shouldn't
+ * happen because of the GLOB_NOCHECK flag in
+ * the glob call. */
+ bb_error_msg("syntax error");
+ return FALSE;
+ } else {
+ /* Convert from char** (one word per string) to a simple char*,
+ * but don't overflow command which is BUFSIZ in length */
+ for (i=0; i < expand_result.gl_pathc; i++) {
+ length=strlen(expand_result.gl_pathv[i]);
+ if (total_length+length+1 >= BUFSIZ) {
+ bb_error_msg(out_of_space);
+ return FALSE;
+ }
+ strcat(command+total_length, " ");
+ total_length+=1;
+ strcat(command+total_length, expand_result.gl_pathv[i]);
+ total_length+=length;
+ }
+ globfree (&expand_result);
+ }
+ }
+ free(cmd_copy);
+ trim(command);
+
+ /* Now do the shell variable substitutions which
+ * wordexp can't do for us, namely $? and $! */
+ src = command;
+ while((dst = strchr(src,'$')) != NULL){
+ var = NULL;
+ switch (*(dst+1)) {
+ case '?':
+ var = itoa(last_return_code);
+ break;
+ case '!':
+ if (last_bg_pid==-1)
+ *var = '\0';
+ else
+ var = itoa(last_bg_pid);
+ break;
+ /* Everything else like $$, $#, $[0-9], etc. should all be
+ * expanded by wordexp(), so we can in theory skip that stuff
+ * here, but just to be on the safe side (i.e., since uClibc
+ * wordexp doesn't do this stuff yet), lets leave it in for
+ * now. */
+ case '$':
+ var = itoa(getpid());
+ break;
+ case '#':
+ var = itoa(argc-1);
+ break;
+ case '0':case '1':case '2':case '3':case '4':
+ case '5':case '6':case '7':case '8':case '9':
+ {
+ int ixx=*(dst+1)-48+1;
+ if (ixx >= argc) {
+ var='\0';
+ } else {
+ var = argv[ixx];
+ }
+ }
+ break;
+
+ }
+ if (var) {
+ /* a single character construction was found, and
+ * already handled in the case statement */
+ src=dst+2;
+ } else {
+ /* Looks like an environment variable */
+ char delim_hold;
+ int num_skip_chars=0;
+ int dstlen = strlen(dst);
+ /* Is this a ${foo} type variable? */
+ if (dstlen >=2 && *(dst+1) == '{') {
+ src=strchr(dst+1, '}');
+ num_skip_chars=1;
+ } else {
+ src=dst+1;
+ while((isalnum)(*src) || *src=='_') src++;
+ }
+ if (src == NULL) {
+ src = dst+dstlen;
+ }
+ delim_hold=*src;
+ *src='\0'; /* temporary */
+ var = getenv(dst + 1 + num_skip_chars);
+ *src=delim_hold;
+ src += num_skip_chars;
+ }
+ if (var == NULL) {
+ /* Seems we got an un-expandable variable. So delete it. */
+ var = (char*)"";
+ }
+ {
+ int subst_len = strlen(var);
+ int trail_len = strlen(src);
+ if (dst+subst_len+trail_len >= command+BUFSIZ) {
+ bb_error_msg(out_of_space);
+ return FALSE;
+ }
+ /* Move stuff to the end of the string to accommodate
+ * filling the created gap with the new stuff */
+ memmove(dst+subst_len, src, trail_len+1);
+ /* Now copy in the new stuff */
+ memcpy(dst, var, subst_len);
+ src = dst+subst_len;
+ }
+ }
+
+ return TRUE;
+}
+
+/* Return cmd->num_progs as 0 if no command is present (e.g. an empty
+ line). If a valid command is found, command_ptr is set to point to
+ the beginning of the next command (if the original command had more
+ then one job associated with it) or NULL if no more commands are
+ present. */
+static int parse_command(char **command_ptr, struct job *job, int *inbg)
+{
+ char *command;
+ char *return_command = NULL;
+ char *src, *buf;
+ int argc_l;
+ int flag;
+ int argv_alloced;
+ char quote = '\0';
+ struct child_prog *prog;
+#ifdef CONFIG_LASH_PIPE_N_REDIRECTS
+ int i;
+ char *chptr;
+#endif
+
+ /* skip leading white space */
+ *command_ptr = skip_whitespace(*command_ptr);
+
+ /* this handles empty lines or leading '#' characters */
+ if (!**command_ptr || (**command_ptr == '#')) {
+ job->num_progs=0;
+ return 0;
+ }
+
+ *inbg = 0;
+ job->num_progs = 1;
+ job->progs = xmalloc(sizeof(*job->progs));
+
+ /* We set the argv elements to point inside of this string. The
+ memory is freed by free_job(). Allocate twice the original
+ length in case we need to quote every single character.
+
+ Getting clean memory relieves us of the task of NULL
+ terminating things and makes the rest of this look a bit
+ cleaner (though it is, admittedly, a tad less efficient) */
+ job->cmdbuf = command = xzalloc(2*strlen(*command_ptr) + 1);
+ job->text = NULL;
+
+ prog = job->progs;
+ prog->num_redirects = 0;
+ prog->is_stopped = 0;
+ prog->family = job;
+#ifdef CONFIG_LASH_PIPE_N_REDIRECTS
+ prog->redirects = NULL;
+#endif
+
+ argv_alloced = 5;
+ prog->argv = xmalloc(sizeof(*prog->argv) * argv_alloced);
+ prog->argv[0] = job->cmdbuf;
+
+ flag = argc_l = 0;
+ buf = command;
+ src = *command_ptr;
+ while (*src && !(flag & LASH_OPT_DONE)) {
+ if (quote == *src) {
+ quote = '\0';
+ } else if (quote) {
+ if (*src == '\\') {
+ src++;
+ if (!*src) {
+ bb_error_msg("character expected after \\");
+ free_job(job);
+ return 1;
+ }
+
+ /* in shell, "\'" should yield \' */
+ if (*src != quote) {
+ *buf++ = '\\';
+ *buf++ = '\\';
+ }
+ } else if (*src == '*' || *src == '?' || *src == '[' ||
+ *src == ']') *buf++ = '\\';
+ *buf++ = *src;
+ } else if (isspace(*src)) {
+ if (*prog->argv[argc_l] || flag & LASH_OPT_SAW_QUOTE) {
+ buf++, argc_l++;
+ /* +1 here leaves room for the NULL which ends argv */
+ if ((argc_l + 1) == argv_alloced) {
+ argv_alloced += 5;
+ prog->argv = xrealloc(prog->argv,
+ sizeof(*prog->argv) *
+ argv_alloced);
+ }
+ prog->argv[argc_l] = buf;
+ flag ^= LASH_OPT_SAW_QUOTE;
+ }
+ } else
+ switch (*src) {
+ case '"':
+ case '\'':
+ quote = *src;
+ flag |= LASH_OPT_SAW_QUOTE;
+ break;
+
+ case '#': /* comment */
+ if (*(src-1)== '$')
+ *buf++ = *src;
+ else
+ flag |= LASH_OPT_DONE;
+ break;
+
+#ifdef CONFIG_LASH_PIPE_N_REDIRECTS
+ case '>': /* redirects */
+ case '<':
+ i = prog->num_redirects++;
+ prog->redirects = xrealloc(prog->redirects,
+ sizeof(*prog->redirects) *
+ (i + 1));
+
+ prog->redirects[i].fd = -1;
+ if (buf != prog->argv[argc_l]) {
+ /* the stuff before this character may be the file number
+ being redirected */
+ prog->redirects[i].fd =
+ strtol(prog->argv[argc_l], &chptr, 10);
+
+ if (*chptr && *prog->argv[argc_l]) {
+ buf++, argc_l++;
+ prog->argv[argc_l] = buf;
+ }
+ }
+
+ if (prog->redirects[i].fd == -1) {
+ if (*src == '>')
+ prog->redirects[i].fd = 1;
+ else
+ prog->redirects[i].fd = 0;
+ }
+
+ if (*src++ == '>') {
+ if (*src == '>')
+ prog->redirects[i].type =
+ REDIRECT_APPEND, src++;
+ else
+ prog->redirects[i].type = REDIRECT_OVERWRITE;
+ } else {
+ prog->redirects[i].type = REDIRECT_INPUT;
+ }
+
+ /* This isn't POSIX sh compliant. Oh well. */
+ chptr = src;
+ chptr = skip_whitespace(chptr);
+
+ if (!*chptr) {
+ bb_error_msg("file name expected after %c", *(src-1));
+ free_job(job);
+ job->num_progs=0;
+ return 1;
+ }
+
+ prog->redirects[i].filename = buf;
+ while (*chptr && !isspace(*chptr))
+ *buf++ = *chptr++;
+
+ src = chptr - 1; /* we src++ later */
+ prog->argv[argc_l] = ++buf;
+ break;
+
+ case '|': /* pipe */
+ /* finish this command */
+ if (*prog->argv[argc_l] || flag & LASH_OPT_SAW_QUOTE)
+ argc_l++;
+ if (!argc_l) {
+ goto empty_command_in_pipe;
+ }
+ prog->argv[argc_l] = NULL;
+
+ /* and start the next */
+ job->num_progs++;
+ job->progs = xrealloc(job->progs,
+ sizeof(*job->progs) * job->num_progs);
+ prog = job->progs + (job->num_progs - 1);
+ prog->num_redirects = 0;
+ prog->redirects = NULL;
+ prog->is_stopped = 0;
+ prog->family = job;
+ argc_l = 0;
+
+ argv_alloced = 5;
+ prog->argv = xmalloc(sizeof(*prog->argv) * argv_alloced);
+ prog->argv[0] = ++buf;
+
+ src++;
+ src = skip_whitespace(src);
+
+ if (!*src) {
+empty_command_in_pipe:
+ bb_error_msg("empty command in pipe");
+ free_job(job);
+ job->num_progs=0;
+ return 1;
+ }
+ src--; /* we'll ++ it at the end of the loop */
+
+ break;
+#endif
+
+#ifdef CONFIG_LASH_JOB_CONTROL
+ case '&': /* background */
+ *inbg = 1;
+ /* fallthrough */
+#endif
+ case ';': /* multiple commands */
+ flag |= LASH_OPT_DONE;
+ return_command = *command_ptr + (src - *command_ptr) + 1;
+ break;
+
+ case '\\':
+ src++;
+ if (!*src) {
+ bb_error_msg("character expected after \\");
+ free_job(job);
+ return 1;
+ }
+ if (*src == '*' || *src == '[' || *src == ']'
+ || *src == '?') *buf++ = '\\';
+ /* fallthrough */
+ default:
+ *buf++ = *src;
+ }
+
+ src++;
+ }
+
+ if (*prog->argv[argc_l] || flag & LASH_OPT_SAW_QUOTE) {
+ argc_l++;
+ }
+ if (!argc_l) {
+ free_job(job);
+ return 0;
+ }
+ prog->argv[argc_l] = NULL;
+
+ if (!return_command) {
+ job->text = xstrdup(*command_ptr);
+ } else {
+ /* This leaves any trailing spaces, which is a bit sloppy */
+ job->text = xstrndup(*command_ptr, return_command - *command_ptr);
+ }
+
+ *command_ptr = return_command;
+
+ return 0;
+}
+
+/* Run the child_prog, no matter what kind of command it uses.
+ */
+static int pseudo_exec(struct child_prog *child)
+{
+ const struct built_in_command *x;
+
+ /* Check if the command matches any of the non-forking builtins.
+ * Depending on context, this might be redundant. But it's
+ * easier to waste a few CPU cycles than it is to figure out
+ * if this is one of those cases.
+ */
+ for (x = bltins; x->cmd; x++) {
+ if (strcmp(child->argv[0], x->cmd) == 0 ) {
+ _exit(x->function(child));
+ }
+ }
+
+ /* Check if the command matches any of the forking builtins. */
+ for (x = bltins_forking; x->cmd; x++) {
+ if (strcmp(child->argv[0], x->cmd) == 0) {
+ applet_name=x->cmd;
+ _exit (x->function(child));
+ }
+ }
+
+ /* Check if the command matches any busybox internal
+ * commands ("applets") here. Following discussions from
+ * November 2000 on busybox@busybox.net, don't use
+ * bb_get_last_path_component(). This way explicit (with
+ * slashes) filenames will never be interpreted as an
+ * applet, just like with builtins. This way the user can
+ * override an applet with an explicit filename reference.
+ * The only downside to this change is that an explicit
+ * /bin/foo invocation will fork and exec /bin/foo, even if
+ * /bin/foo is a symlink to busybox.
+ */
+
+ if (ENABLE_FEATURE_SH_STANDALONE_SHELL) {
+ char **argv_l = child->argv;
+ int argc_l;
+
+ for (argc_l=0; *argv_l; argv_l++, argc_l++);
+ optind = 1;
+ run_applet_by_name(child->argv[0], argc_l, child->argv);
+ }
+
+ execvp(child->argv[0], child->argv);
+
+ /* Do not use bb_perror_msg_and_die() here, since we must not
+ * call exit() but should call _exit() instead */
+ bb_perror_msg("%s", child->argv[0]);
+ _exit(EXIT_FAILURE);
+}
+
+static void insert_job(struct job *newjob, int inbg)
+{
+ struct job *thejob;
+ struct jobset *j_list=newjob->job_list;
+
+ /* find the ID for thejob to use */
+ newjob->jobid = 1;
+ for (thejob = j_list->head; thejob; thejob = thejob->next)
+ if (thejob->jobid >= newjob->jobid)
+ newjob->jobid = thejob->jobid + 1;
+
+ /* add thejob to the list of running jobs */
+ if (!j_list->head) {
+ thejob = j_list->head = xmalloc(sizeof(*thejob));
+ } else {
+ for (thejob = j_list->head; thejob->next; thejob = thejob->next) /* nothing */;
+ thejob->next = xmalloc(sizeof(*thejob));
+ thejob = thejob->next;
+ }
+
+ *thejob = *newjob; /* physically copy the struct job */
+ thejob->next = NULL;
+ thejob->running_progs = thejob->num_progs;
+ thejob->stopped_progs = 0;
+
+#ifdef CONFIG_LASH_JOB_CONTROL
+ if (inbg) {
+ /* we don't wait for background thejobs to return -- append it
+ to the list of backgrounded thejobs and leave it alone */
+ printf("[%d] %d\n", thejob->jobid,
+ newjob->progs[newjob->num_progs - 1].pid);
+ last_jobid = newjob->jobid;
+ last_bg_pid=newjob->progs[newjob->num_progs - 1].pid;
+ } else {
+ newjob->job_list->fg = thejob;
+
+ /* move the new process group into the foreground */
+ /* Ignore errors since child could have already exited */
+ tcsetpgrp(shell_terminal, newjob->pgrp);
+ }
+#endif
+}
+
+static int run_command(struct job *newjob, int inbg, int outpipe[2])
+{
+ /* struct job *thejob; */
+ int i;
+ int nextin, nextout;
+ int pipefds[2]; /* pipefd[0] is for reading */
+ const struct built_in_command *x;
+ struct child_prog *child;
+
+ nextin = 0, nextout = 1;
+ for (i = 0; i < newjob->num_progs; i++) {
+ child = & (newjob->progs[i]);
+
+ if ((i + 1) < newjob->num_progs) {
+ if (pipe(pipefds)<0) bb_perror_msg_and_die("pipe");
+ nextout = pipefds[1];
+ } else {
+ if (outpipe[1]!=-1) {
+ nextout = outpipe[1];
+ } else {
+ nextout = 1;
+ }
+ }
+
+
+ /* Check if the command matches any non-forking builtins,
+ * but only if this is a simple command.
+ * Non-forking builtins within pipes have to fork anyway,
+ * and are handled in pseudo_exec. "echo foo | read bar"
+ * is doomed to failure, and doesn't work on bash, either.
+ */
+ if (newjob->num_progs == 1) {
+ /* Check if the command sets an environment variable. */
+ if (strchr(child->argv[0], '=') != NULL) {
+ child->argv[1] = child->argv[0];
+ return builtin_export(child);
+ }
+
+ for (x = bltins; x->cmd; x++) {
+ if (strcmp(child->argv[0], x->cmd) == 0 ) {
+ int rcode;
+ int squirrel[] = {-1, -1, -1};
+ setup_redirects(child, squirrel);
+ rcode = x->function(child);
+ restore_redirects(squirrel);
+ return rcode;
+ }
+ }
+ }
+
+#if !defined(__UCLIBC__) || defined(__ARCH_HAS_MMU__)
+ if (!(child->pid = fork()))
+#else
+ if (!(child->pid = vfork()))
+#endif
+ {
+ /* Set the handling for job control signals back to the default. */
+ signal(SIGINT, SIG_DFL);
+ signal(SIGQUIT, SIG_DFL);
+ signal(SIGTSTP, SIG_DFL);
+ signal(SIGTTIN, SIG_DFL);
+ signal(SIGTTOU, SIG_DFL);
+ signal(SIGCHLD, SIG_DFL);
+
+ /* Close all open filehandles. */
+ while(close_me_list) close((long)llist_pop(&close_me_list));
+
+ if (outpipe[1]!=-1) {
+ close(outpipe[0]);
+ }
+ if (nextin != 0) {
+ dup2(nextin, 0);
+ close(nextin);
+ }
+
+ if (nextout != 1) {
+ dup2(nextout, 1);
+ dup2(nextout, 2); /* Really? */
+ close(nextout);
+ close(pipefds[0]);
+ }
+
+ /* explicit redirects override pipes */
+ setup_redirects(child,NULL);
+
+ pseudo_exec(child);
+ }
+ if (outpipe[1]!=-1) {
+ close(outpipe[1]);
+ }
+
+ /* put our child in the process group whose leader is the
+ first process in this pipe */
+ setpgid(child->pid, newjob->progs[0].pid);
+ if (nextin != 0)
+ close(nextin);
+ if (nextout != 1)
+ close(nextout);
+
+ /* If there isn't another process, nextin is garbage
+ but it doesn't matter */
+ nextin = pipefds[0];
+ }
+
+ newjob->pgrp = newjob->progs[0].pid;
+
+ insert_job(newjob, inbg);
+
+ return 0;
+}
+
+static int busy_loop(FILE * input)
+{
+ char *command;
+ char *next_command = NULL;
+ struct job newjob;
+ int i;
+ int inbg = 0;
+ int status;
+#ifdef CONFIG_LASH_JOB_CONTROL
+ pid_t parent_pgrp;
+ /* save current owner of TTY so we can restore it on exit */
+ parent_pgrp = tcgetpgrp(shell_terminal);
+#endif
+ newjob.job_list = &job_list;
+ newjob.job_context = DEFAULT_CONTEXT;
+
+ command = xzalloc(BUFSIZ);
+
+ while (1) {
+ if (!job_list.fg) {
+ /* no job is in the foreground */
+
+ /* see if any background processes have exited */
+ checkjobs(&job_list);
+
+ if (!next_command) {
+ if (get_command(input, command))
+ break;
+ next_command = command;
+ }
+
+ if (! expand_arguments(next_command)) {
+ free(command);
+ command = xzalloc(BUFSIZ);
+ next_command = NULL;
+ continue;
+ }
+
+ if (!parse_command(&next_command, &newjob, &inbg) &&
+ newjob.num_progs) {
+ int pipefds[2] = {-1,-1};
+ debug_printf( "job=%p fed to run_command by busy_loop()'\n",
+ &newjob);
+ run_command(&newjob, inbg, pipefds);
+ }
+ else {
+ free(command);
+ command = xzalloc(BUFSIZ);
+ next_command = NULL;
+ }
+ } else {
+ /* a job is running in the foreground; wait for it */
+ i = 0;
+ while (!job_list.fg->progs[i].pid ||
+ job_list.fg->progs[i].is_stopped == 1) i++;
+
+ if (waitpid(job_list.fg->progs[i].pid, &status, WUNTRACED)<0) {
+ if (errno != ECHILD) {
+ bb_perror_msg_and_die("waitpid(%d)",job_list.fg->progs[i].pid);
+ }
+ }
+
+ if (WIFEXITED(status) || WIFSIGNALED(status)) {
+ /* the child exited */
+ job_list.fg->running_progs--;
+ job_list.fg->progs[i].pid = 0;
+
+ last_return_code=WEXITSTATUS(status);
+
+ if (!job_list.fg->running_progs) {
+ /* child exited */
+ remove_job(&job_list, job_list.fg);
+ job_list.fg = NULL;
+ }
+ }
+#ifdef CONFIG_LASH_JOB_CONTROL
+ else {
+ /* the child was stopped */
+ job_list.fg->stopped_progs++;
+ job_list.fg->progs[i].is_stopped = 1;
+
+ if (job_list.fg->stopped_progs == job_list.fg->running_progs) {
+ printf("\n" JOB_STATUS_FORMAT, job_list.fg->jobid,
+ "Stopped", job_list.fg->text);
+ job_list.fg = NULL;
+ }
+ }
+
+ if (!job_list.fg) {
+ /* move the shell to the foreground */
+ /* suppress messages when run from /linuxrc mag@sysgo.de */
+ if (tcsetpgrp(shell_terminal, getpgrp()) && errno != ENOTTY)
+ bb_perror_msg("tcsetpgrp");
+ }
+#endif
+ }
+ }
+ free(command);
+
+#ifdef CONFIG_LASH_JOB_CONTROL
+ /* return controlling TTY back to parent process group before exiting */
+ if (tcsetpgrp(shell_terminal, parent_pgrp) && errno != ENOTTY)
+ bb_perror_msg("tcsetpgrp");
+#endif
+
+ /* return exit status if called with "-c" */
+ if (input == NULL && WIFEXITED(status))
+ return WEXITSTATUS(status);
+
+ return 0;
+}
+
+#ifdef CONFIG_FEATURE_CLEAN_UP
+static void free_memory(void)
+{
+ if (cwd && cwd!=bb_msg_unknown) {
+ free((char*)cwd);
+ }
+ if (local_pending_command)
+ free(local_pending_command);
+
+ if (job_list.fg && !job_list.fg->running_progs) {
+ remove_job(&job_list, job_list.fg);
+ }
+}
+#else
+void free_memory(void);
+#endif
+
+#ifdef CONFIG_LASH_JOB_CONTROL
+/* Make sure we have a controlling tty. If we get started under a job
+ * aware app (like bash for example), make sure we are now in charge so
+ * we don't fight over who gets the foreground */
+static void setup_job_control(void)
+{
+ int status;
+ pid_t shell_pgrp;
+
+ /* Loop until we are in the foreground. */
+ while ((status = tcgetpgrp (shell_terminal)) >= 0) {
+ if (status == (shell_pgrp = getpgrp ())) {
+ break;
+ }
+ kill (- shell_pgrp, SIGTTIN);
+ }
+
+ /* Ignore interactive and job-control signals. */
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ signal(SIGTSTP, SIG_IGN);
+ signal(SIGTTIN, SIG_IGN);
+ signal(SIGTTOU, SIG_IGN);
+ signal(SIGCHLD, SIG_IGN);
+
+ /* Put ourselves in our own process group. */
+ setsid();
+ shell_pgrp = getpid();
+ setpgid(shell_pgrp, shell_pgrp);
+
+ /* Grab control of the terminal. */
+ tcsetpgrp(shell_terminal, shell_pgrp);
+}
+#else
+static inline void setup_job_control(void)
+{
+}
+#endif
+
+int lash_main(int argc_l, char **argv_l);
+int lash_main(int argc_l, char **argv_l)
+{
+ unsigned opt;
+ FILE *input = stdin;
+ argc = argc_l;
+ argv = argv_l;
+
+#if ENABLE_FEATURE_EDITING
+ line_input_state = new_line_input_t(FOR_SHELL);
+#endif
+
+ /* These variables need re-initializing when recursing */
+ last_jobid = 0;
+ close_me_list = NULL;
+ job_list.head = NULL;
+ job_list.fg = NULL;
+ last_return_code=1;
+
+ if (argv[0] && argv[0][0] == '-') {
+ FILE *prof_input;
+ prof_input = fopen("/etc/profile", "r");
+ if (prof_input) {
+ llist_add_to(&close_me_list, (void *)(long)fileno(prof_input));
+ /* Now run the file */
+ busy_loop(prof_input);
+ fclose_if_not_stdin(prof_input);
+ llist_pop(&close_me_list);
+ }
+ }
+
+ opt = getopt32(argc_l, argv_l, "+ic:", &local_pending_command);
+#define LASH_OPT_i (1<<0)
+#define LASH_OPT_c (1<<2)
+ if (opt & LASH_OPT_c) {
+ input = NULL;
+ optind++;
+ argv += optind;
+ }
+ /* A shell is interactive if the `-i' flag was given, or if all of
+ * the following conditions are met:
+ * no -c command
+ * no arguments remaining or the -s flag given
+ * standard input is a terminal
+ * standard output is a terminal
+ * Refer to Posix.2, the description of the `sh' utility. */
+ if (argv[optind]==NULL && input==stdin &&
+ isatty(STDIN_FILENO) && isatty(STDOUT_FILENO))
+ {
+ opt |= LASH_OPT_i;
+ }
+ setup_job_control();
+ if (opt & LASH_OPT_i) {
+ /* Looks like they want an interactive shell */
+ if (!ENABLE_FEATURE_SH_EXTRA_QUIET) {
+ printf("\n\n%s Built-in shell (lash)\n"
+ "Enter 'help' for a list of built-in commands.\n\n",
+ BB_BANNER);
+ }
+ } else if (!local_pending_command && argv[optind]) {
+ //printf( "optind=%d argv[optind]='%s'\n", optind, argv[optind]);
+ input = xfopen(argv[optind], "r");
+ /* be lazy, never mark this closed */
+ llist_add_to(&close_me_list, (void *)(long)fileno(input));
+ }
+
+ /* initialize the cwd -- this is never freed...*/
+ cwd = xrealloc_getcwd_or_warn(NULL);
+ if (!cwd)
+ cwd = bb_msg_unknown;
+
+ if (ENABLE_FEATURE_CLEAN_UP) atexit(free_memory);
+
+ if (ENABLE_FEATURE_EDITING) cmdedit_set_initial_prompt();
+ else PS1 = NULL;
+
+ return (busy_loop(input));
+}
diff --git a/i/pc104/initrd/conf/busybox/shell/msh.c b/i/pc104/initrd/conf/busybox/shell/msh.c
new file mode 100644
index 0000000..41f4cc6
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/shell/msh.c
@@ -0,0 +1,5406 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Minix shell port for busybox
+ *
+ * This version of the Minix shell was adapted for use in busybox
+ * by Erik Andersen <andersen@codepoet.org>
+ *
+ * - backtick expansion did not work properly
+ * Jonas Holmberg <jonas.holmberg@axis.com>
+ * Robert Schwebel <r.schwebel@pengutronix.de>
+ * Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#ifdef STANDALONE
+# ifndef _GNU_SOURCE
+# define _GNU_SOURCE
+# endif
+# include <setjmp.h>
+# include <sys/times.h>
+# include <sys/types.h>
+# include <sys/stat.h>
+# include <sys/wait.h>
+# include <signal.h>
+# include <stdio.h>
+# include <stdlib.h>
+# include <unistd.h>
+# include <string.h>
+# include <errno.h>
+# include <dirent.h>
+# include <fcntl.h>
+# include <ctype.h>
+# include <assert.h>
+# define bb_dev_null "/dev/null"
+# define DEFAULT_SHELL "/proc/self/exe"
+# define CONFIG_BUSYBOX_EXEC_PATH "/proc/self/exe"
+# define BB_BANNER "busybox standalone"
+# define ENABLE_FEATURE_SH_STANDALONE_SHELL 0
+# define bb_msg_memory_exhausted "memory exhausted"
+# define xmalloc(size) malloc(size)
+# define msh_main(argc,argv) main(argc,argv)
+# define safe_read(fd,buf,count) read(fd,buf,count)
+# define NOT_LONE_DASH(s) ((s)[0] != '-' || (s)[1])
+# define LONE_CHAR(s,c) ((s)[0] == (c) && !(s)[1])
+# define ATTRIBUTE_NORETURN __attribute__ ((__noreturn__))
+static char *find_applet_by_name(const char *applet)
+{
+ return NULL;
+}
+static void utoa_to_buf(unsigned n, char *buf, unsigned buflen)
+{
+ unsigned i, out, res;
+ assert(sizeof(unsigned) == 4);
+ if (buflen) {
+ out = 0;
+ for (i = 1000000000; i; i /= 10) {
+ res = n / i;
+ if (res || out || i == 1) {
+ if (!--buflen) break;
+ out++;
+ n -= res*i;
+ *buf++ = '0' + res;
+ }
+ }
+ *buf = '\0';
+ }
+}
+static void itoa_to_buf(int n, char *buf, unsigned buflen)
+{
+ if (buflen && n < 0) {
+ n = -n;
+ *buf++ = '-';
+ buflen--;
+ }
+ utoa_to_buf((unsigned)n, buf, buflen);
+}
+static char local_buf[12];
+static char *itoa(int n)
+{
+ itoa_to_buf(n, local_buf, sizeof(local_buf));
+ return local_buf;
+}
+#else
+# include <setjmp.h>
+# include <sys/times.h>
+# include "busybox.h"
+extern char **environ;
+#endif
+
+/*#define MSHDEBUG 1*/
+
+#ifdef MSHDEBUG
+int mshdbg = MSHDEBUG;
+
+#define DBGPRINTF(x) if(mshdbg>0)printf x
+#define DBGPRINTF0(x) if(mshdbg>0)printf x
+#define DBGPRINTF1(x) if(mshdbg>1)printf x
+#define DBGPRINTF2(x) if(mshdbg>2)printf x
+#define DBGPRINTF3(x) if(mshdbg>3)printf x
+#define DBGPRINTF4(x) if(mshdbg>4)printf x
+#define DBGPRINTF5(x) if(mshdbg>5)printf x
+#define DBGPRINTF6(x) if(mshdbg>6)printf x
+#define DBGPRINTF7(x) if(mshdbg>7)printf x
+#define DBGPRINTF8(x) if(mshdbg>8)printf x
+#define DBGPRINTF9(x) if(mshdbg>9)printf x
+
+int mshdbg_rc = 0;
+
+#define RCPRINTF(x) if(mshdbg_rc)printf x
+
+#else
+
+#define DBGPRINTF(x)
+#define DBGPRINTF0(x) ((void)0)
+#define DBGPRINTF1(x) ((void)0)
+#define DBGPRINTF2(x) ((void)0)
+#define DBGPRINTF3(x) ((void)0)
+#define DBGPRINTF4(x) ((void)0)
+#define DBGPRINTF5(x) ((void)0)
+#define DBGPRINTF6(x) ((void)0)
+#define DBGPRINTF7(x) ((void)0)
+#define DBGPRINTF8(x) ((void)0)
+#define DBGPRINTF9(x) ((void)0)
+
+#define RCPRINTF(x) ((void)0)
+
+#endif /* MSHDEBUG */
+
+
+#if ENABLE_FEATURE_EDITING_FANCY_PROMPT
+# define DEFAULT_ROOT_PROMPT "\\u:\\w> "
+# define DEFAULT_USER_PROMPT "\\u:\\w$ "
+#else
+# define DEFAULT_ROOT_PROMPT "# "
+# define DEFAULT_USER_PROMPT "$ "
+#endif
+
+
+/* -------- sh.h -------- */
+/*
+ * shell
+ */
+
+#define LINELIM 2100
+#define NPUSH 8 /* limit to input nesting */
+
+#undef NOFILE
+#define NOFILE 20 /* Number of open files */
+#define NUFILE 10 /* Number of user-accessible files */
+#define FDBASE 10 /* First file usable by Shell */
+
+/*
+ * values returned by wait
+ */
+#define WAITSIG(s) ((s)&0177)
+#define WAITVAL(s) (((s)>>8)&0377)
+#define WAITCORE(s) (((s)&0200)!=0)
+
+/*
+ * library and system definitions
+ */
+typedef void xint; /* base type of jmp_buf, for not broken compilers */
+
+/*
+ * shell components
+ */
+
+#define QUOTE 0200
+
+#define NOBLOCK ((struct op *)NULL)
+#define NOWORD ((char *)NULL)
+#define NOWORDS ((char **)NULL)
+#define NOPIPE ((int *)NULL)
+
+
+/*
+ * redirection
+ */
+struct ioword {
+ short io_unit; /* unit affected */
+ short io_flag; /* action (below) */
+ char *io_name; /* file name */
+};
+
+#define IOREAD 1 /* < */
+#define IOHERE 2 /* << (here file) */
+#define IOWRITE 4 /* > */
+#define IOCAT 8 /* >> */
+#define IOXHERE 16 /* ${}, ` in << */
+#define IODUP 32 /* >&digit */
+#define IOCLOSE 64 /* >&- */
+
+#define IODEFAULT (-1) /* token for default IO unit */
+
+
+/*
+ * Description of a command or an operation on commands.
+ * Might eventually use a union.
+ */
+struct op {
+ int type; /* operation type, see below */
+ char **words; /* arguments to a command */
+ struct ioword **ioact; /* IO actions (eg, < > >>) */
+ struct op *left;
+ struct op *right;
+ char *str; /* identifier for case and for */
+};
+
+#define TCOM 1 /* command */
+#define TPAREN 2 /* (c-list) */
+#define TPIPE 3 /* a | b */
+#define TLIST 4 /* a [&;] b */
+#define TOR 5 /* || */
+#define TAND 6 /* && */
+#define TFOR 7
+#define TDO 8
+#define TCASE 9
+#define TIF 10
+#define TWHILE 11
+#define TUNTIL 12
+#define TELIF 13
+#define TPAT 14 /* pattern in case */
+#define TBRACE 15 /* {c-list} */
+#define TASYNC 16 /* c & */
+/* Added to support "." file expansion */
+#define TDOT 17
+
+/* Strings for names to make debug easier */
+#ifdef MSHDEBUG
+static const char *const T_CMD_NAMES[] = {
+ "PLACEHOLDER",
+ "TCOM",
+ "TPAREN",
+ "TPIPE",
+ "TLIST",
+ "TOR",
+ "TAND",
+ "TFOR",
+ "TDO",
+ "TCASE",
+ "TIF",
+ "TWHILE",
+ "TUNTIL",
+ "TELIF",
+ "TPAT",
+ "TBRACE",
+ "TASYNC",
+ "TDOT",
+};
+#endif
+
+/*
+ * actions determining the environment of a process
+ */
+#define BIT(i) (1<<(i))
+#define FEXEC BIT(0) /* execute without forking */
+
+#define AREASIZE (90000)
+
+/*
+ * flags to control evaluation of words
+ */
+#define DOSUB 1 /* interpret $, `, and quotes */
+#define DOBLANK 2 /* perform blank interpretation */
+#define DOGLOB 4 /* interpret [?* */
+#define DOKEY 8 /* move words with `=' to 2nd arg. list */
+#define DOTRIM 16 /* trim resulting string */
+
+#define DOALL (DOSUB|DOBLANK|DOGLOB|DOKEY|DOTRIM)
+
+
+/* PROTOTYPES */
+static int newfile(char *s);
+
+
+struct brkcon {
+ jmp_buf brkpt;
+ struct brkcon *nextlev;
+};
+
+
+/*
+ * flags:
+ * -e: quit on error
+ * -k: look for name=value everywhere on command line
+ * -n: no execution
+ * -t: exit after reading and executing one command
+ * -v: echo as read
+ * -x: trace
+ * -u: unset variables net diagnostic
+ */
+static char flags['z' - 'a' + 1];
+/* this looks weird, but is OK ... we index flag with 'a'...'z' */
+static char *flag = flags - 'a';
+
+static char *null; /* null value for variable */
+static int intr; /* interrupt pending */
+
+static char *trap[_NSIG + 1];
+static char ourtrap[_NSIG + 1];
+static int trapset; /* trap pending */
+
+static int heedint; /* heed interrupt signals */
+
+static int yynerrs; /* yacc */
+
+static char line[LINELIM];
+static char *elinep;
+
+#if ENABLE_FEATURE_EDITING
+static char *current_prompt;
+static line_input_t *line_input_state;
+#endif
+
+static int areanum; /* current allocation area */
+
+
+/*
+ * other functions
+ */
+static const char *rexecve(char *c, char **v, char **envp);
+static char *evalstr(char *cp, int f);
+static char *putn(int n);
+static char *unquote(char *as);
+static int rlookup(char *n);
+static struct wdblock *glob(char *cp, struct wdblock *wb);
+static int my_getc(int ec);
+static int subgetc(char ec, int quoted);
+static char **makenv(int all, struct wdblock *wb);
+static char **eval(char **ap, int f);
+static int setstatus(int s);
+static int waitfor(int lastpid, int canintr);
+
+static void onintr(int s); /* SIGINT handler */
+
+static int newenv(int f);
+static void quitenv(void);
+static void next(int f);
+static void setdash(void);
+static void onecommand(void);
+static void runtrap(int i);
+
+
+/* -------- area stuff -------- */
+
+#define REGSIZE sizeof(struct region)
+#define GROWBY (256)
+/* #define SHRINKBY (64) */
+#undef SHRINKBY
+#define FREE (32767)
+#define BUSY (0)
+#define ALIGN (sizeof(int)-1)
+
+
+struct region {
+ struct region *next;
+ int area;
+};
+
+
+/* -------- grammar stuff -------- */
+typedef union {
+ char *cp;
+ char **wp;
+ int i;
+ struct op *o;
+} YYSTYPE;
+
+#define WORD 256
+#define LOGAND 257
+#define LOGOR 258
+#define BREAK 259
+#define IF 260
+#define THEN 261
+#define ELSE 262
+#define ELIF 263
+#define FI 264
+#define CASE 265
+#define ESAC 266
+#define FOR 267
+#define WHILE 268
+#define UNTIL 269
+#define DO 270
+#define DONE 271
+#define IN 272
+/* Added for "." file expansion */
+#define DOT 273
+
+#define YYERRCODE 300
+
+/* flags to yylex */
+#define CONTIN 01 /* skip new lines to complete command */
+
+static struct op *pipeline(int cf);
+static struct op *andor(void);
+static struct op *c_list(void);
+static int synio(int cf);
+static void musthave(int c, int cf);
+static struct op *simple(void);
+static struct op *nested(int type, int mark);
+static struct op *command(int cf);
+static struct op *dogroup(int onlydone);
+static struct op *thenpart(void);
+static struct op *elsepart(void);
+static struct op *caselist(void);
+static struct op *casepart(void);
+static char **pattern(void);
+static char **wordlist(void);
+static struct op *list(struct op *t1, struct op *t2);
+static struct op *block(int type, struct op *t1, struct op *t2, char **wp);
+static struct op *newtp(void);
+static struct op *namelist(struct op *t);
+static char **copyw(void);
+static void word(char *cp);
+static struct ioword **copyio(void);
+static struct ioword *io(int u, int f, char *cp);
+static int yylex(int cf);
+static int collect(int c, int c1);
+static int dual(int c);
+static void diag(int ec);
+static char *tree(unsigned size);
+
+/* -------- var.h -------- */
+
+struct var {
+ char *value;
+ char *name;
+ struct var *next;
+ char status;
+};
+
+#define COPYV 1 /* flag to setval, suggesting copy */
+#define RONLY 01 /* variable is read-only */
+#define EXPORT 02 /* variable is to be exported */
+#define GETCELL 04 /* name & value space was got with getcell */
+
+static int yyparse(void);
+
+static int execute(struct op *t, int *pin, int *pout, int act);
+
+
+#define AFID_NOBUF (~0)
+#define AFID_ID 0
+
+
+/* -------- io.h -------- */
+/* io buffer */
+struct iobuf {
+ unsigned id; /* buffer id */
+ char buf[512]; /* buffer */
+ char *bufp; /* pointer into buffer */
+ char *ebufp; /* pointer to end of buffer */
+};
+
+/* possible arguments to an IO function */
+struct ioarg {
+ const char *aword;
+ char **awordlist;
+ int afile; /* file descriptor */
+ unsigned afid; /* buffer id */
+ long afpos; /* file position */
+ struct iobuf *afbuf; /* buffer for this file */
+};
+
+/* an input generator's state */
+struct io {
+ int (*iofn) (struct ioarg *, struct io *);
+ struct ioarg *argp;
+ int peekc;
+ char prev; /* previous character read by readc() */
+ char nlcount; /* for `'s */
+ char xchar; /* for `'s */
+ char task; /* reason for pushed IO */
+};
+
+#define XOTHER 0 /* none of the below */
+#define XDOLL 1 /* expanding ${} */
+#define XGRAVE 2 /* expanding `'s */
+#define XIO 3 /* file IO */
+
+/* in substitution */
+#define INSUB() (e.iop->task == XGRAVE || e.iop->task == XDOLL)
+
+static struct ioarg temparg = { 0, 0, 0, AFID_NOBUF, 0 }; /* temporary for PUSHIO */
+static struct ioarg ioargstack[NPUSH];
+static struct io iostack[NPUSH];
+static struct iobuf sharedbuf = { AFID_NOBUF };
+static struct iobuf mainbuf = { AFID_NOBUF };
+static unsigned bufid = AFID_ID; /* buffer id counter */
+
+#define PUSHIO(what,arg,gen) ((temparg.what = (arg)), pushio(&temparg,(gen)))
+#define RUN(what,arg,gen) ((temparg.what = (arg)), run(&temparg,(gen)))
+
+
+/*
+ * parsing & execution environment
+ */
+static struct env {
+ char *linep;
+ struct io *iobase;
+ struct io *iop;
+ xint *errpt; /* void * */
+ int iofd;
+ struct env *oenv;
+} e;
+
+
+/*
+ * input generators for IO structure
+ */
+static int nlchar(struct ioarg *ap);
+static int strchar(struct ioarg *ap);
+static int qstrchar(struct ioarg *ap);
+static int filechar(struct ioarg *ap);
+static int herechar(struct ioarg *ap);
+static int linechar(struct ioarg *ap);
+static int gravechar(struct ioarg *ap, struct io *iop);
+static int qgravechar(struct ioarg *ap, struct io *iop);
+static int dolchar(struct ioarg *ap);
+static int wdchar(struct ioarg *ap);
+static void scraphere(void);
+static void freehere(int area);
+static void gethere(void);
+static void markhere(char *s, struct ioword *iop);
+static int herein(char *hname, int xdoll);
+static int run(struct ioarg *argp, int (*f) (struct ioarg *));
+
+
+static int eofc(void);
+static int readc(void);
+static void unget(int c);
+static void ioecho(char c);
+
+
+/*
+ * IO control
+ */
+static void pushio(struct ioarg *argp, int (*f) (struct ioarg *));
+static int remap(int fd);
+static int openpipe(int *pv);
+static void closepipe(int *pv);
+static struct io *setbase(struct io *ip);
+
+/* -------- word.h -------- */
+
+#define NSTART 16 /* default number of words to allow for initially */
+
+struct wdblock {
+ short w_bsize;
+ short w_nword;
+ /* bounds are arbitrary */
+ char *w_words[1];
+};
+
+static struct wdblock *addword(char *wd, struct wdblock *wb);
+static struct wdblock *newword(int nw);
+static char **getwords(struct wdblock *wb);
+
+/* -------- misc stuff -------- */
+
+static int forkexec(struct op *t, int *pin, int *pout, int act, char **wp);
+static int iosetup(struct ioword *iop, int pipein, int pipeout);
+static void brkset(struct brkcon *bc);
+static int dolabel(struct op *t);
+static int dohelp(struct op *t);
+static int dochdir(struct op *t);
+static int doshift(struct op *t);
+static int dologin(struct op *t);
+static int doumask(struct op *t);
+static int doexec(struct op *t);
+static int dodot(struct op *t);
+static int dowait(struct op *t);
+static int doread(struct op *t);
+static int doeval(struct op *t);
+static int dotrap(struct op *t);
+static int getsig(char *s);
+static void setsig(int n, sighandler_t f);
+static int getn(char *as);
+static int dobreak(struct op *t);
+static int docontinue(struct op *t);
+static int brkcontin(char *cp, int val);
+static int doexit(struct op *t);
+static int doexport(struct op *t);
+static int doreadonly(struct op *t);
+static void rdexp(char **wp, void (*f) (struct var *), int key);
+static void badid(char *s);
+static int doset(struct op *t);
+static void varput(char *s, int out);
+static int dotimes(struct op *t);
+static int expand(const char *cp, struct wdblock **wbp, int f);
+static char *blank(int f);
+static int dollar(int quoted);
+static int grave(int quoted);
+static void globname(char *we, char *pp);
+static char *generate(char *start1, char *end1, char *middle, char *end);
+static int anyspcl(struct wdblock *wb);
+static int xstrcmp(char *p1, char *p2);
+static void glob0(char *a0, unsigned a1, int a2,
+ int (*a3) (char *, char *));
+static void readhere(char **name, char *s, int ec);
+static void pushio(struct ioarg *argp, int (*f) (struct ioarg *));
+static int xxchar(struct ioarg *ap);
+
+struct here {
+ char *h_tag;
+ int h_dosub;
+ struct ioword *h_iop;
+ struct here *h_next;
+};
+
+static const char * const signame[] = {
+ "Signal 0",
+ "Hangup",
+ NULL, /* interrupt */
+ "Quit",
+ "Illegal instruction",
+ "Trace/BPT trap",
+ "Abort",
+ "Bus error",
+ "Floating Point Exception",
+ "Killed",
+ "SIGUSR1",
+ "SIGSEGV",
+ "SIGUSR2",
+ NULL, /* broken pipe */
+ "Alarm clock",
+ "Terminated",
+};
+
+#define NSIGNAL (sizeof(signame)/sizeof(signame[0]))
+
+struct res {
+ const char *r_name;
+ int r_val;
+};
+static const struct res restab[] = {
+ {"for", FOR},
+ {"case", CASE},
+ {"esac", ESAC},
+ {"while", WHILE},
+ {"do", DO},
+ {"done", DONE},
+ {"if", IF},
+ {"in", IN},
+ {"then", THEN},
+ {"else", ELSE},
+ {"elif", ELIF},
+ {"until", UNTIL},
+ {"fi", FI},
+ {";;", BREAK},
+ {"||", LOGOR},
+ {"&&", LOGAND},
+ {"{", '{'},
+ {"}", '}'},
+ {".", DOT},
+ {0, 0},
+};
+
+
+struct builtincmd {
+ const char *name;
+ int (*builtinfunc) (struct op * t);
+};
+static const struct builtincmd builtincmds[] = {
+ {".", dodot},
+ {":", dolabel},
+ {"break", dobreak},
+ {"cd", dochdir},
+ {"continue", docontinue},
+ {"eval", doeval},
+ {"exec", doexec},
+ {"exit", doexit},
+ {"export", doexport},
+ {"help", dohelp},
+ {"login", dologin},
+ {"newgrp", dologin},
+ {"read", doread},
+ {"readonly", doreadonly},
+ {"set", doset},
+ {"shift", doshift},
+ {"times", dotimes},
+ {"trap", dotrap},
+ {"umask", doumask},
+ {"wait", dowait},
+ {0, 0}
+};
+
+static struct op *scantree(struct op *);
+static struct op *dowholefile(int, int);
+
+
+/* Globals */
+static char **dolv;
+static int dolc;
+static int exstat;
+static char gflg;
+static int interactive; /* Is this an interactive shell */
+static int execflg;
+static int multiline; /* \n changed to ; */
+static struct op *outtree; /* result from parser */
+static xint *failpt;
+static xint *errpt;
+static struct brkcon *brklist;
+static int isbreak;
+static struct wdblock *wdlist;
+static struct wdblock *iolist;
+static char *trap[_NSIG + 1];
+static char ourtrap[_NSIG + 1];
+static int trapset; /* trap pending */
+
+#ifdef MSHDEBUG
+static struct var *mshdbg_var;
+#endif
+static struct var *vlist; /* dictionary */
+static struct var *homedir; /* home directory */
+static struct var *prompt; /* main prompt */
+static struct var *cprompt; /* continuation prompt */
+static struct var *path; /* search path for commands */
+static struct var *shell; /* shell to interpret command files */
+static struct var *ifs; /* field separators */
+
+static int areanum; /* current allocation area */
+static int intr;
+static int inparse;
+static char *null = (char*)"";
+static int heedint = 1;
+static void (*qflag) (int) = SIG_IGN;
+static int startl;
+static int peeksym;
+static int nlseen;
+static int iounit = IODEFAULT;
+static YYSTYPE yylval;
+static char *elinep = line + sizeof(line) - 5;
+
+
+static struct here *inhere; /* list of hear docs while parsing */
+static struct here *acthere; /* list of active here documents */
+static struct region *areabot; /* bottom of area */
+static struct region *areatop; /* top of area */
+static struct region *areanxt; /* starting point of scan */
+static void *brktop;
+static void *brkaddr;
+
+static struct env e = {
+ line, /* linep: char ptr */
+ iostack, /* iobase: struct io ptr */
+ iostack - 1, /* iop: struct io ptr */
+ (xint *) NULL, /* errpt: void ptr for errors? */
+ FDBASE, /* iofd: file desc */
+ (struct env *) NULL /* oenv: struct env ptr */
+};
+
+#ifdef MSHDEBUG
+void print_t(struct op *t)
+{
+ DBGPRINTF(("T: t=%p, type %s, words=%p, IOword=%p\n", t,
+ T_CMD_NAMES[t->type], t->words, t->ioact));
+
+ if (t->words) {
+ DBGPRINTF(("T: W1: %s", t->words[0]));
+ }
+}
+
+void print_tree(struct op *head)
+{
+ if (head == NULL) {
+ DBGPRINTF(("PRINT_TREE: no tree\n"));
+ return;
+ }
+
+ DBGPRINTF(("NODE: %p, left %p, right %p\n", head, head->left,
+ head->right));
+
+ if (head->left)
+ print_tree(head->left);
+
+ if (head->right)
+ print_tree(head->right);
+}
+#endif /* MSHDEBUG */
+
+
+/*
+ * IO functions
+ */
+static void prs(const char *s)
+{
+ if (*s)
+ write(2, s, strlen(s));
+}
+
+static void prn(unsigned u)
+{
+ prs(itoa(u));
+}
+
+static void echo(char **wp)
+{
+ int i;
+
+ prs("+");
+ for (i = 0; wp[i]; i++) {
+ if (i)
+ prs(" ");
+ prs(wp[i]);
+ }
+ prs("\n");
+}
+
+static void closef(int i)
+{
+ if (i > 2)
+ close(i);
+}
+
+static void closeall(void)
+{
+ int u;
+
+ for (u = NUFILE; u < NOFILE;)
+ close(u++);
+}
+
+
+/* fail but return to process next command */
+static void fail(void) ATTRIBUTE_NORETURN;
+static void fail(void)
+{
+ longjmp(failpt, 1);
+ /* NOTREACHED */
+}
+
+/* abort shell (or fail in subshell) */
+static void leave(void) ATTRIBUTE_NORETURN;
+static void leave(void)
+{
+ DBGPRINTF(("LEAVE: leave called!\n"));
+
+ if (execflg)
+ fail();
+ scraphere();
+ freehere(1);
+ runtrap(0);
+ _exit(exstat);
+ /* NOTREACHED */
+}
+
+static void warn(const char *s)
+{
+ if (*s) {
+ prs(s);
+ exstat = -1;
+ }
+ prs("\n");
+ if (flag['e'])
+ leave();
+}
+
+static void err(const char *s)
+{
+ warn(s);
+ if (flag['n'])
+ return;
+ if (!interactive)
+ leave();
+ if (e.errpt)
+ longjmp(e.errpt, 1);
+ closeall();
+ e.iop = e.iobase = iostack;
+}
+
+
+/* -------- area.c -------- */
+
+/*
+ * All memory between (char *)areabot and (char *)(areatop+1) is
+ * exclusively administered by the area management routines.
+ * It is assumed that sbrk() and brk() manipulate the high end.
+ */
+
+#define sbrk(X) ({ \
+ void * __q = (void *)-1; \
+ if (brkaddr + (int)(X) < brktop) { \
+ __q = brkaddr; \
+ brkaddr += (int)(X); \
+ } \
+ __q; \
+})
+
+static void initarea(void)
+{
+ brkaddr = xmalloc(AREASIZE);
+ brktop = brkaddr + AREASIZE;
+
+ while ((long) sbrk(0) & ALIGN)
+ sbrk(1);
+ areabot = (struct region *) sbrk(REGSIZE);
+
+ areabot->next = areabot;
+ areabot->area = BUSY;
+ areatop = areabot;
+ areanxt = areabot;
+}
+
+static char *getcell(unsigned nbytes)
+{
+ int nregio;
+ struct region *p, *q;
+ int i;
+
+ if (nbytes == 0) {
+ puts("getcell(0)");
+ abort();
+ }
+ /* silly and defeats the algorithm */
+ /*
+ * round upwards and add administration area
+ */
+ nregio = (nbytes + (REGSIZE - 1)) / REGSIZE + 1;
+ p = areanxt;
+ for (;;) {
+ if (p->area > areanum) {
+ /*
+ * merge free cells
+ */
+ while ((q = p->next)->area > areanum && q != areanxt)
+ p->next = q->next;
+ /*
+ * exit loop if cell big enough
+ */
+ if (q >= p + nregio)
+ goto found;
+ }
+ p = p->next;
+ if (p == areanxt)
+ break;
+ }
+ i = nregio >= GROWBY ? nregio : GROWBY;
+ p = (struct region *) sbrk(i * REGSIZE);
+ if (p == (struct region *) -1)
+ return NULL;
+ p--;
+ if (p != areatop) {
+ puts("not contig");
+ abort(); /* allocated areas are contiguous */
+ }
+ q = p + i;
+ p->next = q;
+ p->area = FREE;
+ q->next = areabot;
+ q->area = BUSY;
+ areatop = q;
+ found:
+ /*
+ * we found a FREE area big enough, pointed to by 'p', and up to 'q'
+ */
+ areanxt = p + nregio;
+ if (areanxt < q) {
+ /*
+ * split into requested area and rest
+ */
+ if (areanxt + 1 > q) {
+ puts("OOM");
+ abort(); /* insufficient space left for admin */
+ }
+ areanxt->next = q;
+ areanxt->area = FREE;
+ p->next = areanxt;
+ }
+ p->area = areanum;
+ return (char *) (p + 1);
+}
+
+static void freecell(char *cp)
+{
+ struct region *p;
+
+ p = (struct region *) cp;
+ if (p != NULL) {
+ p--;
+ if (p < areanxt)
+ areanxt = p;
+ p->area = FREE;
+ }
+}
+#define DELETE(obj) freecell((char *)obj)
+
+static void freearea(int a)
+{
+ struct region *p, *top;
+
+ top = areatop;
+ for (p = areabot; p != top; p = p->next)
+ if (p->area >= a)
+ p->area = FREE;
+}
+
+static void setarea(char *cp, int a)
+{
+ struct region *p;
+
+ p = (struct region *) cp;
+ if (p != NULL)
+ (p - 1)->area = a;
+}
+
+static int getarea(char *cp)
+{
+ return ((struct region *) cp - 1)->area;
+}
+
+static void garbage(void)
+{
+ struct region *p, *q, *top;
+
+ top = areatop;
+ for (p = areabot; p != top; p = p->next) {
+ if (p->area > areanum) {
+ while ((q = p->next)->area > areanum)
+ p->next = q->next;
+ areanxt = p;
+ }
+ }
+#ifdef SHRINKBY
+ if (areatop >= q + SHRINKBY && q->area > areanum) {
+ brk((char *) (q + 1));
+ q->next = areabot;
+ q->area = BUSY;
+ areatop = q;
+ }
+#endif
+}
+
+static char *space(int n)
+{
+ char *cp;
+
+ cp = getcell(n);
+ if (cp == NULL)
+ err("out of string space");
+ return cp;
+}
+
+static char *strsave(const char *s, int a)
+{
+ char *cp;
+
+ cp = space(strlen(s) + 1);
+ if (cp == NULL) {
+// FIXME: I highly doubt this is good.
+ return (char*)"";
+ }
+ setarea(cp, a);
+ strcpy(cp, s);
+ return cp;
+}
+
+
+/* -------- var.c -------- */
+
+static int eqname(const char *n1, const char *n2)
+{
+ for (; *n1 != '=' && *n1 != '\0'; n1++)
+ if (*n2++ != *n1)
+ return 0;
+ return *n2 == '\0' || *n2 == '=';
+}
+
+static const char *findeq(const char *cp)
+{
+ while (*cp != '\0' && *cp != '=')
+ cp++;
+ return cp;
+}
+
+/*
+ * Find the given name in the dictionary
+ * and return its value. If the name was
+ * not previously there, enter it now and
+ * return a null value.
+ */
+static struct var *lookup(const char *n)
+{
+// FIXME: dirty hack
+ static struct var dummy;
+
+ struct var *vp;
+ const char *cp;
+ char *xp;
+ int c;
+
+ if (isdigit(*n)) {
+ dummy.name = (char*)n;
+ for (c = 0; isdigit(*n) && c < 1000; n++)
+ c = c * 10 + *n - '0';
+ dummy.status = RONLY;
+ dummy.value = (c <= dolc ? dolv[c] : null);
+ return &dummy;
+ }
+
+ for (vp = vlist; vp; vp = vp->next)
+ if (eqname(vp->name, n))
+ return vp;
+
+ cp = findeq(n);
+ vp = (struct var *) space(sizeof(*vp));
+ if (vp == 0 || (vp->name = space((int) (cp - n) + 2)) == 0) {
+ dummy.name = dummy.value = (char*)"";
+ return &dummy;
+ }
+
+ xp = vp->name;
+ while ((*xp = *n++) != '\0' && *xp != '=')
+ xp++;
+ *xp++ = '=';
+ *xp = '\0';
+ setarea((char *) vp, 0);
+ setarea((char *) vp->name, 0);
+ vp->value = null;
+ vp->next = vlist;
+ vp->status = GETCELL;
+ vlist = vp;
+ return vp;
+}
+
+/*
+ * if name is not NULL, it must be
+ * a prefix of the space `val',
+ * and end with `='.
+ * this is all so that exporting
+ * values is reasonably painless.
+ */
+static void nameval(struct var *vp, const char *val, const char *name)
+{
+ const char *cp;
+ char *xp;
+ int fl;
+
+ if (vp->status & RONLY) {
+ xp = vp->name;
+ while (*xp && *xp != '=')
+ putc(*xp++, stderr);
+ err(" is read-only");
+ return;
+ }
+ fl = 0;
+ if (name == NULL) {
+ xp = space(strlen(vp->name) + strlen(val) + 2);
+ if (xp == NULL)
+ return;
+ /* make string: name=value */
+ setarea(xp, 0);
+ name = xp;
+ cp = vp->name;
+ while ((*xp = *cp++) != '\0' && *xp != '=')
+ xp++;
+ *xp++ = '=';
+ strcpy(xp, val);
+ val = xp;
+ fl = GETCELL;
+ }
+ if (vp->status & GETCELL)
+ freecell(vp->name); /* form new string `name=value' */
+ vp->name = (char*)name;
+ vp->value = (char*)val;
+ vp->status |= fl;
+}
+
+/*
+ * give variable at `vp' the value `val'.
+ */
+static void setval(struct var *vp, const char *val)
+{
+ nameval(vp, val, NULL);
+}
+
+static void export(struct var *vp)
+{
+ vp->status |= EXPORT;
+}
+
+static void ronly(struct var *vp)
+{
+ if (isalpha(vp->name[0]) || vp->name[0] == '_') /* not an internal symbol */
+ vp->status |= RONLY;
+}
+
+static int isassign(const char *s)
+{
+ unsigned char c;
+ DBGPRINTF7(("ISASSIGN: enter, s=%s\n", s));
+
+ /* no isalpha() - we shouldn't use locale */
+ c = *s;
+ if (c != '_'
+ && (unsigned)((c|0x20) - 'a') > 25 /* not letter */
+ ) {
+ return 0;
+ }
+ while (1) {
+ c = *++s;
+ if (c == '\0')
+ return 0;
+ if (c == '=')
+ return 1;
+ c |= 0x20; /* lowercase letters, doesn't affect numbers */
+ if (c != '_'
+ && (unsigned)(c - '0') > 9 /* not number */
+ && (unsigned)(c - 'a') > 25 /* not letter */
+ ) {
+ return 0;
+ }
+ }
+}
+
+static int assign(const char *s, int cf)
+{
+ const char *cp;
+ struct var *vp;
+
+ DBGPRINTF7(("ASSIGN: enter, s=%s, cf=%d\n", s, cf));
+
+ if (!isalpha(*s) && *s != '_')
+ return 0;
+ for (cp = s; *cp != '='; cp++)
+ if (*cp == '\0' || (!isalnum(*cp) && *cp != '_'))
+ return 0;
+ vp = lookup(s);
+ nameval(vp, ++cp, cf == COPYV ? NULL : s);
+ if (cf != COPYV)
+ vp->status &= ~GETCELL;
+ return 1;
+}
+
+static int checkname(char *cp)
+{
+ DBGPRINTF7(("CHECKNAME: enter, cp=%s\n", cp));
+
+ if (!isalpha(*cp++) && *(cp - 1) != '_')
+ return 0;
+ while (*cp)
+ if (!isalnum(*cp++) && *(cp - 1) != '_')
+ return 0;
+ return 1;
+}
+
+static void putvlist(int f, int out)
+{
+ struct var *vp;
+
+ for (vp = vlist; vp; vp = vp->next) {
+ if (vp->status & f && (isalpha(*vp->name) || *vp->name == '_')) {
+ if (vp->status & EXPORT)
+ write(out, "export ", 7);
+ if (vp->status & RONLY)
+ write(out, "readonly ", 9);
+ write(out, vp->name, (int) (findeq(vp->name) - vp->name));
+ write(out, "\n", 1);
+ }
+ }
+}
+
+
+/*
+ * trap handling
+ */
+static void sig(int i)
+{
+ trapset = i;
+ signal(i, sig);
+}
+
+static void runtrap(int i)
+{
+ char *trapstr;
+
+ trapstr = trap[i];
+ if (trapstr == NULL)
+ return;
+
+ if (i == 0)
+ trap[i] = NULL;
+
+ RUN(aword, trapstr, nlchar);
+}
+
+
+static void setdash(void)
+{
+ char *cp;
+ int c;
+ char m['z' - 'a' + 1];
+
+ cp = m;
+ for (c = 'a'; c <= 'z'; c++)
+ if (flag[c])
+ *cp++ = c;
+ *cp = '\0';
+ setval(lookup("-"), m);
+}
+
+static int newfile(char *s)
+{
+ int f;
+
+ DBGPRINTF7(("NEWFILE: opening %s\n", s));
+
+ f = 0;
+ if (NOT_LONE_DASH(s)) {
+ DBGPRINTF(("NEWFILE: s is %s\n", s));
+ f = open(s, 0);
+ if (f < 0) {
+ prs(s);
+ err(": cannot open");
+ return 1;
+ }
+ }
+
+ next(remap(f));
+ return 0;
+}
+
+
+struct op *scantree(struct op *head)
+{
+ struct op *dotnode;
+
+ if (head == NULL)
+ return NULL;
+
+ if (head->left != NULL) {
+ dotnode = scantree(head->left);
+ if (dotnode)
+ return dotnode;
+ }
+
+ if (head->right != NULL) {
+ dotnode = scantree(head->right);
+ if (dotnode)
+ return dotnode;
+ }
+
+ if (head->words == NULL)
+ return NULL;
+
+ DBGPRINTF5(("SCANTREE: checking node %p\n", head));
+
+ if ((head->type != TDOT) && LONE_CHAR(head->words[0], '.')) {
+ DBGPRINTF5(("SCANTREE: dot found in node %p\n", head));
+ return head;
+ }
+
+ return NULL;
+}
+
+
+static void onecommand(void)
+{
+ int i;
+ jmp_buf m1;
+
+ DBGPRINTF(("ONECOMMAND: enter, outtree=%p\n", outtree));
+
+ while (e.oenv)
+ quitenv();
+
+ areanum = 1;
+ freehere(areanum);
+ freearea(areanum);
+ garbage();
+ wdlist = 0;
+ iolist = 0;
+ e.errpt = 0;
+ e.linep = line;
+ yynerrs = 0;
+ multiline = 0;
+ inparse = 1;
+ intr = 0;
+ execflg = 0;
+
+ failpt = m1;
+ setjmp(failpt); /* Bruce Evans' fix */
+ failpt = m1;
+ if (setjmp(failpt) || yyparse() || intr) {
+ DBGPRINTF(("ONECOMMAND: this is not good.\n"));
+
+ while (e.oenv)
+ quitenv();
+ scraphere();
+ if (!interactive && intr)
+ leave();
+ inparse = 0;
+ intr = 0;
+ return;
+ }
+
+ inparse = 0;
+ brklist = 0;
+ intr = 0;
+ execflg = 0;
+
+ if (!flag['n']) {
+ DBGPRINTF(("ONECOMMAND: calling execute, t=outtree=%p\n",
+ outtree));
+ execute(outtree, NOPIPE, NOPIPE, 0);
+ }
+
+ if (!interactive && intr) {
+ execflg = 0;
+ leave();
+ }
+
+ i = trapset;
+ if (i != 0) {
+ trapset = 0;
+ runtrap(i);
+ }
+}
+
+static int newenv(int f)
+{
+ struct env *ep;
+
+ DBGPRINTF(("NEWENV: f=%d (indicates quitenv and return)\n", f));
+
+ if (f) {
+ quitenv();
+ return 1;
+ }
+
+ ep = (struct env *) space(sizeof(*ep));
+ if (ep == NULL) {
+ while (e.oenv)
+ quitenv();
+ fail();
+ }
+ *ep = e;
+ e.oenv = ep;
+ e.errpt = errpt;
+
+ return 0;
+}
+
+static void quitenv(void)
+{
+ struct env *ep;
+ int fd;
+
+ DBGPRINTF(("QUITENV: e.oenv=%p\n", e.oenv));
+
+ ep = e.oenv;
+ if (ep != NULL) {
+ fd = e.iofd;
+ e = *ep;
+ /* should close `'d files */
+ DELETE(ep);
+ while (--fd >= e.iofd)
+ close(fd);
+ }
+}
+
+/*
+ * Is character c in s?
+ */
+static int any(int c, const char *s)
+{
+ while (*s)
+ if (*s++ == c)
+ return 1;
+ return 0;
+}
+
+/*
+ * Is any character from s1 in s2?
+ */
+static int anys(const char *s1, const char *s2)
+{
+ while (*s1)
+ if (any(*s1++, s2))
+ return 1;
+ return 0;
+}
+
+static char *putn(int n)
+{
+ return itoa(n);
+}
+
+static void next(int f)
+{
+ PUSHIO(afile, f, filechar);
+}
+
+static void onintr(int s) /* ANSI C requires a parameter */
+{
+ signal(SIGINT, onintr);
+ intr = 1;
+ if (interactive) {
+ if (inparse) {
+ prs("\n");
+ fail();
+ }
+ } else if (heedint) {
+ execflg = 0;
+ leave();
+ }
+}
+
+
+/* -------- gmatch.c -------- */
+/*
+ * int gmatch(string, pattern)
+ * char *string, *pattern;
+ *
+ * Match a pattern as in sh(1).
+ */
+
+#define CMASK 0377
+#define QUOTE 0200
+#define QMASK (CMASK&~QUOTE)
+#define NOT '!' /* might use ^ */
+
+static const char *cclass(const char *p, int sub)
+{
+ int c, d, not, found;
+
+ not = (*p == NOT);
+ if (not != 0)
+ p++;
+ found = not;
+ do {
+ if (*p == '\0')
+ return NULL;
+ c = *p & CMASK;
+ if (p[1] == '-' && p[2] != ']') {
+ d = p[2] & CMASK;
+ p++;
+ } else
+ d = c;
+ if (c == sub || (c <= sub && sub <= d))
+ found = !not;
+ } while (*++p != ']');
+ return found ? p + 1 : NULL;
+}
+
+static int gmatch(const char *s, const char *p)
+{
+ int sc, pc;
+
+ if (s == NULL || p == NULL)
+ return 0;
+
+ while ((pc = *p++ & CMASK) != '\0') {
+ sc = *s++ & QMASK;
+ switch (pc) {
+ case '[':
+ p = cclass(p, sc);
+ if (p == NULL)
+ return 0;
+ break;
+
+ case '?':
+ if (sc == 0)
+ return 0;
+ break;
+
+ case '*':
+ s--;
+ do {
+ if (*p == '\0' || gmatch(s, p))
+ return 1;
+ } while (*s++ != '\0');
+ return 0;
+
+ default:
+ if (sc != (pc & ~QUOTE))
+ return 0;
+ }
+ }
+ return *s == '\0';
+}
+
+
+/* -------- csyn.c -------- */
+/*
+ * shell: syntax (C version)
+ */
+
+static void yyerror(const char *s) ATTRIBUTE_NORETURN;
+static void yyerror(const char *s)
+{
+ yynerrs++;
+ if (interactive && e.iop <= iostack) {
+ multiline = 0;
+ while (eofc() == 0 && yylex(0) != '\n');
+ }
+ err(s);
+ fail();
+}
+
+static void zzerr(void) ATTRIBUTE_NORETURN;
+static void zzerr(void)
+{
+ yyerror("syntax error");
+}
+
+int yyparse(void)
+{
+ DBGPRINTF7(("YYPARSE: enter...\n"));
+
+ startl = 1;
+ peeksym = 0;
+ yynerrs = 0;
+ outtree = c_list();
+ musthave('\n', 0);
+ return (yynerrs != 0);
+}
+
+static struct op *pipeline(int cf)
+{
+ struct op *t, *p;
+ int c;
+
+ DBGPRINTF7(("PIPELINE: enter, cf=%d\n", cf));
+
+ t = command(cf);
+
+ DBGPRINTF9(("PIPELINE: t=%p\n", t));
+
+ if (t != NULL) {
+ while ((c = yylex(0)) == '|') {
+ p = command(CONTIN);
+ if (p == NULL) {
+ DBGPRINTF8(("PIPELINE: error!\n"));
+ zzerr();
+ }
+
+ if (t->type != TPAREN && t->type != TCOM) {
+ /* shell statement */
+ t = block(TPAREN, t, NOBLOCK, NOWORDS);
+ }
+
+ t = block(TPIPE, t, p, NOWORDS);
+ }
+ peeksym = c;
+ }
+
+ DBGPRINTF7(("PIPELINE: returning t=%p\n", t));
+ return t;
+}
+
+static struct op *andor(void)
+{
+ struct op *t, *p;
+ int c;
+
+ DBGPRINTF7(("ANDOR: enter...\n"));
+
+ t = pipeline(0);
+
+ DBGPRINTF9(("ANDOR: t=%p\n", t));
+
+ if (t != NULL) {
+ while ((c = yylex(0)) == LOGAND || c == LOGOR) {
+ p = pipeline(CONTIN);
+ if (p == NULL) {
+ DBGPRINTF8(("ANDOR: error!\n"));
+ zzerr();
+ }
+
+ t = block(c == LOGAND ? TAND : TOR, t, p, NOWORDS);
+ } /* WHILE */
+
+ peeksym = c;
+ }
+
+ DBGPRINTF7(("ANDOR: returning t=%p\n", t));
+ return t;
+}
+
+static struct op *c_list(void)
+{
+ struct op *t, *p;
+ int c;
+
+ DBGPRINTF7(("C_LIST: enter...\n"));
+
+ t = andor();
+
+ if (t != NULL) {
+ peeksym = yylex(0);
+ if (peeksym == '&')
+ t = block(TASYNC, t, NOBLOCK, NOWORDS);
+
+ while ((c = yylex(0)) == ';' || c == '&'
+ || (multiline && c == '\n')) {
+
+ p = andor();
+ if (p== NULL)
+ return t;
+
+ peeksym = yylex(0);
+ if (peeksym == '&')
+ p = block(TASYNC, p, NOBLOCK, NOWORDS);
+
+ t = list(t, p);
+ } /* WHILE */
+
+ peeksym = c;
+ }
+ /* IF */
+ DBGPRINTF7(("C_LIST: returning t=%p\n", t));
+ return t;
+}
+
+static int synio(int cf)
+{
+ struct ioword *iop;
+ int i;
+ int c;
+
+ DBGPRINTF7(("SYNIO: enter, cf=%d\n", cf));
+
+ c = yylex(cf);
+ if (c != '<' && c != '>') {
+ peeksym = c;
+ return 0;
+ }
+
+ i = yylval.i;
+ musthave(WORD, 0);
+ iop = io(iounit, i, yylval.cp);
+ iounit = IODEFAULT;
+
+ if (i & IOHERE)
+ markhere(yylval.cp, iop);
+
+ DBGPRINTF7(("SYNIO: returning 1\n"));
+ return 1;
+}
+
+static void musthave(int c, int cf)
+{
+ peeksym = yylex(cf);
+ if (peeksym != c) {
+ DBGPRINTF7(("MUSTHAVE: error!\n"));
+ zzerr();
+ }
+
+ peeksym = 0;
+}
+
+static struct op *simple(void)
+{
+ struct op *t;
+
+ t = NULL;
+ for (;;) {
+ switch (peeksym = yylex(0)) {
+ case '<':
+ case '>':
+ (void) synio(0);
+ break;
+
+ case WORD:
+ if (t == NULL) {
+ t = newtp();
+ t->type = TCOM;
+ }
+ peeksym = 0;
+ word(yylval.cp);
+ break;
+
+ default:
+ return t;
+ }
+ }
+}
+
+static struct op *nested(int type, int mark)
+{
+ struct op *t;
+
+ DBGPRINTF3(("NESTED: enter, type=%d, mark=%d\n", type, mark));
+
+ multiline++;
+ t = c_list();
+ musthave(mark, 0);
+ multiline--;
+ return block(type, t, NOBLOCK, NOWORDS);
+}
+
+static struct op *command(int cf)
+{
+ struct op *t;
+ struct wdblock *iosave;
+ int c;
+
+ DBGPRINTF(("COMMAND: enter, cf=%d\n", cf));
+
+ iosave = iolist;
+ iolist = NULL;
+
+ if (multiline)
+ cf |= CONTIN;
+
+ while (synio(cf))
+ cf = 0;
+
+ c = yylex(cf);
+
+ switch (c) {
+ default:
+ peeksym = c;
+ t = simple();
+ if (t == NULL) {
+ if (iolist == NULL)
+ return NULL;
+ t = newtp();
+ t->type = TCOM;
+ }
+ break;
+
+ case '(':
+ t = nested(TPAREN, ')');
+ break;
+
+ case '{':
+ t = nested(TBRACE, '}');
+ break;
+
+ case FOR:
+ t = newtp();
+ t->type = TFOR;
+ musthave(WORD, 0);
+ startl = 1;
+ t->str = yylval.cp;
+ multiline++;
+ t->words = wordlist();
+ c = yylex(0);
+ if (c != '\n' && c != ';')
+ peeksym = c;
+ t->left = dogroup(0);
+ multiline--;
+ break;
+
+ case WHILE:
+ case UNTIL:
+ multiline++;
+ t = newtp();
+ t->type = c == WHILE ? TWHILE : TUNTIL;
+ t->left = c_list();
+ t->right = dogroup(1);
+ t->words = NULL;
+ multiline--;
+ break;
+
+ case CASE:
+ t = newtp();
+ t->type = TCASE;
+ musthave(WORD, 0);
+ t->str = yylval.cp;
+ startl++;
+ multiline++;
+ musthave(IN, CONTIN);
+ startl++;
+
+ t->left = caselist();
+
+ musthave(ESAC, 0);
+ multiline--;
+ break;
+
+ case IF:
+ multiline++;
+ t = newtp();
+ t->type = TIF;
+ t->left = c_list();
+ t->right = thenpart();
+ musthave(FI, 0);
+ multiline--;
+ break;
+
+ case DOT:
+ t = newtp();
+ t->type = TDOT;
+
+ musthave(WORD, 0); /* gets name of file */
+ DBGPRINTF7(("COMMAND: DOT clause, yylval.cp is %s\n", yylval.cp));
+
+ word(yylval.cp); /* add word to wdlist */
+ word(NOWORD); /* terminate wdlist */
+ t->words = copyw(); /* dup wdlist */
+ break;
+
+ }
+
+ while (synio(0));
+
+ t = namelist(t);
+ iolist = iosave;
+
+ DBGPRINTF(("COMMAND: returning %p\n", t));
+
+ return t;
+}
+
+static struct op *dowholefile(int type, int mark)
+{
+ struct op *t;
+
+ DBGPRINTF(("DOWHOLEFILE: enter, type=%d, mark=%d\n", type, mark));
+
+ multiline++;
+ t = c_list();
+ multiline--;
+ t = block(type, t, NOBLOCK, NOWORDS);
+ DBGPRINTF(("DOWHOLEFILE: return t=%p\n", t));
+ return t;
+}
+
+static struct op *dogroup(int onlydone)
+{
+ int c;
+ struct op *mylist;
+
+ c = yylex(CONTIN);
+ if (c == DONE && onlydone)
+ return NULL;
+ if (c != DO)
+ zzerr();
+ mylist = c_list();
+ musthave(DONE, 0);
+ return mylist;
+}
+
+static struct op *thenpart(void)
+{
+ int c;
+ struct op *t;
+
+ c = yylex(0);
+ if (c != THEN) {
+ peeksym = c;
+ return NULL;
+ }
+ t = newtp();
+ t->type = 0;
+ t->left = c_list();
+ if (t->left == NULL)
+ zzerr();
+ t->right = elsepart();
+ return t;
+}
+
+static struct op *elsepart(void)
+{
+ int c;
+ struct op *t;
+
+ switch (c = yylex(0)) {
+ case ELSE:
+ t = c_list();
+ if (t == NULL)
+ zzerr();
+ return t;
+
+ case ELIF:
+ t = newtp();
+ t->type = TELIF;
+ t->left = c_list();
+ t->right = thenpart();
+ return t;
+
+ default:
+ peeksym = c;
+ return NULL;
+ }
+}
+
+static struct op *caselist(void)
+{
+ struct op *t;
+
+ t = NULL;
+ while ((peeksym = yylex(CONTIN)) != ESAC) {
+ DBGPRINTF(("CASELIST, doing yylex, peeksym=%d\n", peeksym));
+ t = list(t, casepart());
+ }
+
+ DBGPRINTF(("CASELIST, returning t=%p\n", t));
+ return t;
+}
+
+static struct op *casepart(void)
+{
+ struct op *t;
+
+ DBGPRINTF7(("CASEPART: enter...\n"));
+
+ t = newtp();
+ t->type = TPAT;
+ t->words = pattern();
+ musthave(')', 0);
+ t->left = c_list();
+ peeksym = yylex(CONTIN);
+ if (peeksym != ESAC)
+ musthave(BREAK, CONTIN);
+
+ DBGPRINTF7(("CASEPART: made newtp(TPAT, t=%p)\n", t));
+
+ return t;
+}
+
+static char **pattern(void)
+{
+ int c, cf;
+
+ cf = CONTIN;
+ do {
+ musthave(WORD, cf);
+ word(yylval.cp);
+ cf = 0;
+ c = yylex(0);
+ } while (c == '|');
+ peeksym = c;
+ word(NOWORD);
+
+ return copyw();
+}
+
+static char **wordlist(void)
+{
+ int c;
+
+ c = yylex(0);
+ if (c != IN) {
+ peeksym = c;
+ return NULL;
+ }
+ startl = 0;
+ while ((c = yylex(0)) == WORD)
+ word(yylval.cp);
+ word(NOWORD);
+ peeksym = c;
+ return copyw();
+}
+
+/*
+ * supporting functions
+ */
+static struct op *list(struct op *t1, struct op *t2)
+{
+ DBGPRINTF7(("LIST: enter, t1=%p, t2=%p\n", t1, t2));
+
+ if (t1 == NULL)
+ return t2;
+ if (t2 == NULL)
+ return t1;
+
+ return block(TLIST, t1, t2, NOWORDS);
+}
+
+static struct op *block(int type, struct op *t1, struct op *t2, char **wp)
+{
+ struct op *t;
+
+ DBGPRINTF7(("BLOCK: enter, type=%d (%s)\n", type, T_CMD_NAMES[type]));
+
+ t = newtp();
+ t->type = type;
+ t->left = t1;
+ t->right = t2;
+ t->words = wp;
+
+ DBGPRINTF7(("BLOCK: inserted %p between %p and %p\n", t, t1,
+ t2));
+
+ return t;
+}
+
+/* See if given string is a shell multiline (FOR, IF, etc) */
+static int rlookup(char *n)
+{
+ const struct res *rp;
+
+ DBGPRINTF7(("RLOOKUP: enter, n is %s\n", n));
+
+ for (rp = restab; rp->r_name; rp++)
+ if (strcmp(rp->r_name, n) == 0) {
+ DBGPRINTF7(("RLOOKUP: match, returning %d\n", rp->r_val));
+ return rp->r_val; /* Return numeric code for shell multiline */
+ }
+
+ DBGPRINTF7(("RLOOKUP: NO match, returning 0\n"));
+ return 0; /* Not a shell multiline */
+}
+
+static struct op *newtp(void)
+{
+ struct op *t;
+
+ t = (struct op *) tree(sizeof(*t));
+ t->type = 0;
+ t->words = NULL;
+ t->ioact = NULL;
+ t->left = NULL;
+ t->right = NULL;
+ t->str = NULL;
+
+ DBGPRINTF3(("NEWTP: allocated %p\n", t));
+
+ return t;
+}
+
+static struct op *namelist(struct op *t)
+{
+
+ DBGPRINTF7(("NAMELIST: enter, t=%p, type %s, iolist=%p\n", t,
+ T_CMD_NAMES[t->type], iolist));
+
+ if (iolist) {
+ iolist = addword((char *) NULL, iolist);
+ t->ioact = copyio();
+ } else
+ t->ioact = NULL;
+
+ if (t->type != TCOM) {
+ if (t->type != TPAREN && t->ioact != NULL) {
+ t = block(TPAREN, t, NOBLOCK, NOWORDS);
+ t->ioact = t->left->ioact;
+ t->left->ioact = NULL;
+ }
+ return t;
+ }
+
+ word(NOWORD);
+ t->words = copyw();
+
+ return t;
+}
+
+static char **copyw(void)
+{
+ char **wd;
+
+ wd = getwords(wdlist);
+ wdlist = 0;
+ return wd;
+}
+
+static void word(char *cp)
+{
+ wdlist = addword(cp, wdlist);
+}
+
+static struct ioword **copyio(void)
+{
+ struct ioword **iop;
+
+ iop = (struct ioword **) getwords(iolist);
+ iolist = 0;
+ return iop;
+}
+
+static struct ioword *io(int u, int f, char *cp)
+{
+ struct ioword *iop;
+
+ iop = (struct ioword *) tree(sizeof(*iop));
+ iop->io_unit = u;
+ iop->io_flag = f;
+ iop->io_name = cp;
+ iolist = addword((char *) iop, iolist);
+ return iop;
+}
+
+static int yylex(int cf)
+{
+ int c, c1;
+ int atstart;
+
+ c = peeksym;
+ if (c > 0) {
+ peeksym = 0;
+ if (c == '\n')
+ startl = 1;
+ return c;
+ }
+
+ nlseen = 0;
+ atstart = startl;
+ startl = 0;
+ yylval.i = 0;
+ e.linep = line;
+
+/* MALAMO */
+ line[LINELIM - 1] = '\0';
+
+ loop:
+ while ((c = my_getc(0)) == ' ' || c == '\t') /* Skip whitespace */
+ ;
+
+ switch (c) {
+ default:
+ if (any(c, "0123456789")) {
+ c1 = my_getc(0);
+ unget(c1);
+ if (c1 == '<' || c1 == '>') {
+ iounit = c - '0';
+ goto loop;
+ }
+ *e.linep++ = c;
+ c = c1;
+ }
+ break;
+
+ case '#': /* Comment, skip to next newline or End-of-string */
+ while ((c = my_getc(0)) != '\0' && c != '\n');
+ unget(c);
+ goto loop;
+
+ case 0:
+ DBGPRINTF5(("YYLEX: return 0, c=%d\n", c));
+ return c;
+
+ case '$':
+ DBGPRINTF9(("YYLEX: found $\n"));
+ *e.linep++ = c;
+ c = my_getc(0);
+ if (c == '{') {
+ c = collect(c, '}');
+ if (c != '\0')
+ return c;
+ goto pack;
+ }
+ break;
+
+ case '`':
+ case '\'':
+ case '"':
+ c = collect(c, c);
+ if (c != '\0')
+ return c;
+ goto pack;
+
+ case '|':
+ case '&':
+ case ';':
+ startl = 1;
+ /* If more chars process them, else return NULL char */
+ c1 = dual(c);
+ if (c1 != '\0')
+ return c1;
+ return c;
+
+ case '^':
+ startl = 1;
+ return '|';
+ case '>':
+ case '<':
+ diag(c);
+ return c;
+
+ case '\n':
+ nlseen++;
+ gethere();
+ startl = 1;
+ if (multiline || cf & CONTIN) {
+ if (interactive && e.iop <= iostack) {
+#if ENABLE_FEATURE_EDITING
+ current_prompt = cprompt->value;
+#else
+ prs(cprompt->value);
+#endif
+ }
+ if (cf & CONTIN)
+ goto loop;
+ }
+ return c;
+
+ case '(':
+ case ')':
+ startl = 1;
+ return c;
+ }
+
+ unget(c);
+
+ pack:
+ while ((c = my_getc(0)) != '\0' && !any(c, "`$ '\"\t;&<>()|^\n")) {
+ if (e.linep >= elinep)
+ err("word too long");
+ else
+ *e.linep++ = c;
+ };
+
+ unget(c);
+
+ if (any(c, "\"'`$"))
+ goto loop;
+
+ *e.linep++ = '\0';
+
+ if (atstart) {
+ c = rlookup(line);
+ if (c != 0) {
+ startl = 1;
+ return c;
+ }
+ }
+
+ yylval.cp = strsave(line, areanum);
+ return WORD;
+}
+
+
+static int collect(int c, int c1)
+{
+ char s[2];
+
+ DBGPRINTF8(("COLLECT: enter, c=%d, c1=%d\n", c, c1));
+
+ *e.linep++ = c;
+ while ((c = my_getc(c1)) != c1) {
+ if (c == 0) {
+ unget(c);
+ s[0] = c1;
+ s[1] = 0;
+ prs("no closing ");
+ yyerror(s);
+ return YYERRCODE;
+ }
+ if (interactive && c == '\n' && e.iop <= iostack) {
+#if ENABLE_FEATURE_EDITING
+ current_prompt = cprompt->value;
+#else
+ prs(cprompt->value);
+#endif
+ }
+ *e.linep++ = c;
+ }
+
+ *e.linep++ = c;
+
+ DBGPRINTF8(("COLLECT: return 0, line is %s\n", line));
+
+ return 0;
+}
+
+/* "multiline commands" helper func */
+/* see if next 2 chars form a shell multiline */
+static int dual(int c)
+{
+ char s[3];
+ char *cp = s;
+
+ DBGPRINTF8(("DUAL: enter, c=%d\n", c));
+
+ *cp++ = c; /* c is the given "peek" char */
+ *cp++ = my_getc(0); /* get next char of input */
+ *cp = '\0'; /* add EOS marker */
+
+ c = rlookup(s); /* see if 2 chars form a shell multiline */
+ if (c == 0)
+ unget(*--cp); /* String is not a shell multiline, put peek char back */
+
+ return c; /* String is multiline, return numeric multiline (restab) code */
+}
+
+static void diag(int ec)
+{
+ int c;
+
+ DBGPRINTF8(("DIAG: enter, ec=%d\n", ec));
+
+ c = my_getc(0);
+ if (c == '>' || c == '<') {
+ if (c != ec)
+ zzerr();
+ yylval.i = (ec == '>' ? IOWRITE | IOCAT : IOHERE);
+ c = my_getc(0);
+ } else
+ yylval.i = (ec == '>' ? IOWRITE : IOREAD);
+ if (c != '&' || yylval.i == IOHERE)
+ unget(c);
+ else
+ yylval.i |= IODUP;
+}
+
+static char *tree(unsigned size)
+{
+ char *t;
+
+ t = getcell(size);
+ if (t == NULL) {
+ DBGPRINTF2(("TREE: getcell(%d) failed!\n", size));
+ prs("command line too complicated\n");
+ fail();
+ /* NOTREACHED */
+ }
+ return t;
+}
+
+
+/* VARARGS1 */
+/* ARGSUSED */
+
+/* -------- exec.c -------- */
+
+static struct op **find1case(struct op *t, const char *w)
+{
+ struct op *t1;
+ struct op **tp;
+ char **wp;
+ char *cp;
+
+ if (t == NULL) {
+ DBGPRINTF3(("FIND1CASE: enter, t==NULL, returning.\n"));
+ return NULL;
+ }
+
+ DBGPRINTF3(("FIND1CASE: enter, t->type=%d (%s)\n", t->type,
+ T_CMD_NAMES[t->type]));
+
+ if (t->type == TLIST) {
+ tp = find1case(t->left, w);
+ if (tp != NULL) {
+ DBGPRINTF3(("FIND1CASE: found one to the left, returning tp=%p\n", tp));
+ return tp;
+ }
+ t1 = t->right; /* TPAT */
+ } else
+ t1 = t;
+
+ for (wp = t1->words; *wp;) {
+ cp = evalstr(*wp++, DOSUB);
+ if (cp && gmatch(w, cp)) {
+ DBGPRINTF3(("FIND1CASE: returning &t1->left= %p.\n",
+ &t1->left));
+ return &t1->left;
+ }
+ }
+
+ DBGPRINTF(("FIND1CASE: returning NULL\n"));
+ return NULL;
+}
+
+static struct op *findcase(struct op *t, const char *w)
+{
+ struct op **tp;
+
+ tp = find1case(t, w);
+ return tp != NULL ? *tp : NULL;
+}
+
+/*
+ * execute tree
+ */
+
+static int execute(struct op *t, int *pin, int *pout, int act)
+{
+ struct op *t1;
+ volatile int i, rv, a;
+ const char *cp;
+ char **wp, **wp2;
+ struct var *vp;
+ struct op *outtree_save;
+ struct brkcon bc;
+
+#if __GNUC__
+ /* Avoid longjmp clobbering */
+ (void) &wp;
+#endif
+
+ if (t == NULL) {
+ DBGPRINTF4(("EXECUTE: enter, t==null, returning.\n"));
+ return 0;
+ }
+
+ DBGPRINTF(("EXECUTE: t=%p, t->type=%d (%s), t->words is %s\n", t,
+ t->type, T_CMD_NAMES[t->type],
+ ((t->words == NULL) ? "NULL" : t->words[0])));
+
+ rv = 0;
+ a = areanum++;
+ wp = (wp2 = t->words) != NULL
+ ? eval(wp2, t->type == TCOM ? DOALL : DOALL & ~DOKEY)
+ : NULL;
+
+ switch (t->type) {
+ case TDOT:
+ DBGPRINTF3(("EXECUTE: TDOT\n"));
+
+ outtree_save = outtree;
+
+ newfile(evalstr(t->words[0], DOALL));
+
+ t->left = dowholefile(TLIST, 0);
+ t->right = NULL;
+
+ outtree = outtree_save;
+
+ if (t->left)
+ rv = execute(t->left, pin, pout, 0);
+ if (t->right)
+ rv = execute(t->right, pin, pout, 0);
+ break;
+
+ case TPAREN:
+ rv = execute(t->left, pin, pout, 0);
+ break;
+
+ case TCOM:
+ rv = forkexec(t, pin, pout, act, wp);
+ break;
+
+ case TPIPE:
+ {
+ int pv[2];
+
+ rv = openpipe(pv);
+ if (rv < 0)
+ break;
+ pv[0] = remap(pv[0]);
+ pv[1] = remap(pv[1]);
+ (void) execute(t->left, pin, pv, 0);
+ rv = execute(t->right, pv, pout, 0);
+ }
+ break;
+
+ case TLIST:
+ (void) execute(t->left, pin, pout, 0);
+ rv = execute(t->right, pin, pout, 0);
+ break;
+
+ case TASYNC:
+ {
+ int hinteractive = interactive;
+
+ DBGPRINTF7(("EXECUTE: TASYNC clause, calling vfork()...\n"));
+
+ i = vfork();
+ if (i == 0) { /* child */
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ if (interactive)
+ signal(SIGTERM, SIG_DFL);
+ interactive = 0;
+ if (pin == NULL) {
+ close(0);
+ open(bb_dev_null, 0);
+ }
+ _exit(execute(t->left, pin, pout, FEXEC));
+ }
+ interactive = hinteractive;
+ if (i != -1) {
+ setval(lookup("!"), putn(i));
+ if (pin != NULL)
+ closepipe(pin);
+ if (interactive) {
+ prs(putn(i));
+ prs("\n");
+ }
+ } else
+ rv = -1;
+ setstatus(rv);
+ }
+ break;
+
+ case TOR:
+ case TAND:
+ rv = execute(t->left, pin, pout, 0);
+ t1 = t->right;
+ if (t1 != NULL && (rv == 0) == (t->type == TAND))
+ rv = execute(t1, pin, pout, 0);
+ break;
+
+ case TFOR:
+ if (wp == NULL) {
+ wp = dolv + 1;
+ i = dolc;
+ if (i < 0)
+ i = 0;
+ } else {
+ i = -1;
+ while (*wp++ != NULL);
+ }
+ vp = lookup(t->str);
+ while (setjmp(bc.brkpt))
+ if (isbreak)
+ goto broken;
+ brkset(&bc);
+ for (t1 = t->left; i-- && *wp != NULL;) {
+ setval(vp, *wp++);
+ rv = execute(t1, pin, pout, 0);
+ }
+ brklist = brklist->nextlev;
+ break;
+
+ case TWHILE:
+ case TUNTIL:
+ while (setjmp(bc.brkpt))
+ if (isbreak)
+ goto broken;
+ brkset(&bc);
+ t1 = t->left;
+ while ((execute(t1, pin, pout, 0) == 0) == (t->type == TWHILE))
+ rv = execute(t->right, pin, pout, 0);
+ brklist = brklist->nextlev;
+ break;
+
+ case TIF:
+ case TELIF:
+ if (t->right != NULL) {
+ rv = !execute(t->left, pin, pout, 0) ?
+ execute(t->right->left, pin, pout, 0) :
+ execute(t->right->right, pin, pout, 0);
+ }
+ break;
+
+ case TCASE:
+ cp = evalstr(t->str, DOSUB | DOTRIM);
+ if (cp == NULL)
+ cp = "";
+
+ DBGPRINTF7(("EXECUTE: TCASE, t->str is %s, cp is %s\n",
+ ((t->str == NULL) ? "NULL" : t->str),
+ ((cp == NULL) ? "NULL" : cp)));
+
+ t1 = findcase(t->left, cp);
+ if (t1 != NULL) {
+ DBGPRINTF7(("EXECUTE: TCASE, calling execute(t=%p, t1=%p)...\n", t, t1));
+ rv = execute(t1, pin, pout, 0);
+ DBGPRINTF7(("EXECUTE: TCASE, back from execute(t=%p, t1=%p)...\n", t, t1));
+ }
+ break;
+
+ case TBRACE:
+/*
+ iopp = t->ioact;
+ if (i)
+ while (*iopp)
+ if (iosetup(*iopp++, pin!=NULL, pout!=NULL)) {
+ rv = -1;
+ break;
+ }
+*/
+ if (rv >= 0) {
+ t1 = t->left;
+ if (t1) {
+ rv = execute(t1, pin, pout, 0);
+ }
+ }
+ break;
+
+ };
+
+ broken:
+ t->words = wp2;
+ isbreak = 0;
+ freehere(areanum);
+ freearea(areanum);
+ areanum = a;
+ if (interactive && intr) {
+ closeall();
+ fail();
+ }
+
+ i = trapset;
+ if (i != 0) {
+ trapset = 0;
+ runtrap(i);
+ }
+
+ DBGPRINTF(("EXECUTE: returning from t=%p, rv=%d\n", t, rv));
+ return rv;
+}
+
+typedef int (*builtin_func_ptr)(struct op *);
+
+static builtin_func_ptr inbuilt(const char *s) {
+ const struct builtincmd *bp;
+
+ for (bp = builtincmds; bp->name != NULL; bp++)
+ if (strcmp(bp->name, s) == 0)
+ return bp->builtinfunc;
+
+ return NULL;
+}
+
+static int forkexec(struct op *t, int *pin, int *pout, int act, char **wp)
+{
+ pid_t newpid;
+ int i, rv;
+ builtin_func_ptr shcom = NULL;
+ int f;
+ const char *cp = NULL;
+ struct ioword **iopp;
+ int resetsig;
+ char **owp;
+ int forked = 0;
+
+ int *hpin = pin;
+ int *hpout = pout;
+ char *hwp;
+ int hinteractive;
+ int hintr;
+ struct brkcon *hbrklist;
+ int hexecflg;
+
+#if __GNUC__
+ /* Avoid longjmp clobbering */
+ (void) &pin;
+ (void) &pout;
+ (void) &wp;
+ (void) &shcom;
+ (void) &cp;
+ (void) &resetsig;
+ (void) &owp;
+#endif
+
+ DBGPRINTF(("FORKEXEC: t=%p, pin %p, pout %p, act %d\n", t, pin,
+ pout, act));
+ DBGPRINTF7(("FORKEXEC: t->words is %s\n",
+ ((t->words == NULL) ? "NULL" : t->words[0])));
+
+ owp = wp;
+ resetsig = 0;
+ rv = -1; /* system-detected error */
+ if (t->type == TCOM) {
+ while (*wp++ != NULL);
+ cp = *wp;
+
+ /* strip all initial assignments */
+ /* not correct wrt PATH=yyy command etc */
+ if (flag['x']) {
+ DBGPRINTF9(("FORKEXEC: echo'ing, cp=%p, wp=%p, owp=%p\n",
+ cp, wp, owp));
+ echo(cp ? wp : owp);
+ }
+
+ if (cp == NULL && t->ioact == NULL) {
+ while ((cp = *owp++) != NULL && assign(cp, COPYV))
+ /**/;
+ DBGPRINTF(("FORKEXEC: returning setstatus()\n"));
+ return setstatus(0);
+ }
+ if (cp != NULL) {
+ shcom = inbuilt(cp);
+ }
+ }
+
+ t->words = wp;
+ f = act;
+
+ DBGPRINTF(("FORKEXEC: shcom %p, f&FEXEC 0x%x, owp %p\n", shcom,
+ f & FEXEC, owp));
+
+ if (shcom == NULL && (f & FEXEC) == 0) {
+ /* Save values in case the child process alters them */
+ hpin = pin;
+ hpout = pout;
+ hwp = *wp;
+ hinteractive = interactive;
+ hintr = intr;
+ hbrklist = brklist;
+ hexecflg = execflg;
+
+ DBGPRINTF3(("FORKEXEC: calling vfork()...\n"));
+
+ newpid = vfork();
+
+ if (newpid == -1) {
+ DBGPRINTF(("FORKEXEC: ERROR, cannot vfork()!\n"));
+ return -1;
+ }
+
+ if (newpid > 0) { /* Parent */
+ /* Restore values */
+ pin = hpin;
+ pout = hpout;
+ *wp = hwp;
+ interactive = hinteractive;
+ intr = hintr;
+ brklist = hbrklist;
+ execflg = hexecflg;
+/* moved up
+ if (i == -1)
+ return rv;
+*/
+ if (pin != NULL)
+ closepipe(pin);
+
+ return (pout == NULL ? setstatus(waitfor(newpid, 0)) : 0);
+ }
+
+ /* Must be the child process, pid should be 0 */
+ DBGPRINTF(("FORKEXEC: child process, shcom=%p\n", shcom));
+
+ if (interactive) {
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ resetsig = 1;
+ }
+ interactive = 0;
+ intr = 0;
+ forked = 1;
+ brklist = 0;
+ execflg = 0;
+ }
+
+ if (owp != NULL)
+ while ((cp = *owp++) != NULL && assign(cp, COPYV))
+ if (shcom == NULL)
+ export(lookup(cp));
+
+#ifdef COMPIPE
+ if ((pin != NULL || pout != NULL) && shcom != NULL && shcom != doexec) {
+ err("piping to/from shell builtins not yet done");
+ if (forked)
+ _exit(-1);
+ return -1;
+ }
+#endif
+
+ if (pin != NULL) {
+ dup2(pin[0], 0);
+ closepipe(pin);
+ }
+ if (pout != NULL) {
+ dup2(pout[1], 1);
+ closepipe(pout);
+ }
+
+ iopp = t->ioact;
+ if (iopp != NULL) {
+ if (shcom != NULL && shcom != doexec) {
+ prs(cp);
+ err(": cannot redirect shell command");
+ if (forked)
+ _exit(-1);
+ return -1;
+ }
+ while (*iopp)
+ if (iosetup(*iopp++, pin != NULL, pout != NULL)) {
+ if (forked)
+ _exit(rv);
+ return rv;
+ }
+ }
+
+ if (shcom) {
+ i = setstatus((*shcom) (t));
+ if (forked)
+ _exit(i);
+ DBGPRINTF(("FORKEXEC: returning i=%d\n", i));
+ return i;
+ }
+
+ /* should use FIOCEXCL */
+ for (i = FDBASE; i < NOFILE; i++)
+ close(i);
+ if (resetsig) {
+ signal(SIGINT, SIG_DFL);
+ signal(SIGQUIT, SIG_DFL);
+ }
+
+ if (t->type == TPAREN)
+ _exit(execute(t->left, NOPIPE, NOPIPE, FEXEC));
+ if (wp[0] == NULL)
+ _exit(0);
+
+ cp = rexecve(wp[0], wp, makenv(0, NULL));
+ prs(wp[0]);
+ prs(": ");
+ err(cp);
+ if (!execflg)
+ trap[0] = NULL;
+
+ DBGPRINTF(("FORKEXEC: calling leave(), pid=%d\n", newpid));
+
+ leave();
+ /* NOTREACHED */
+ _exit(1);
+}
+
+/*
+ * 0< 1> are ignored as required
+ * within pipelines.
+ */
+static int iosetup(struct ioword *iop, int pipein, int pipeout)
+{
+ int u = -1;
+ char *cp = NULL;
+ const char *msg;
+
+ DBGPRINTF(("IOSETUP: iop %p, pipein %i, pipeout %i\n", iop,
+ pipein, pipeout));
+
+ if (iop->io_unit == IODEFAULT) /* take default */
+ iop->io_unit = iop->io_flag & (IOREAD | IOHERE) ? 0 : 1;
+
+ if (pipein && iop->io_unit == 0)
+ return 0;
+
+ if (pipeout && iop->io_unit == 1)
+ return 0;
+
+ msg = iop->io_flag & (IOREAD | IOHERE) ? "open" : "create";
+ if ((iop->io_flag & IOHERE) == 0) {
+ cp = iop->io_name; /* huh?? */
+ cp = evalstr(cp, DOSUB | DOTRIM);
+ if (cp == NULL)
+ return 1;
+ }
+
+ if (iop->io_flag & IODUP) {
+ if (cp[1] || (!isdigit(*cp) && *cp != '-')) {
+ prs(cp);
+ err(": illegal >& argument");
+ return 1;
+ }
+ if (*cp == '-')
+ iop->io_flag = IOCLOSE;
+ iop->io_flag &= ~(IOREAD | IOWRITE);
+ }
+ switch (iop->io_flag) {
+ case IOREAD:
+ u = open(cp, 0);
+ break;
+
+ case IOHERE:
+ case IOHERE | IOXHERE:
+ u = herein(iop->io_name, iop->io_flag & IOXHERE);
+ cp = (char*)"here file";
+ break;
+
+ case IOWRITE | IOCAT:
+ u = open(cp, 1);
+ if (u >= 0) {
+ lseek(u, (long) 0, SEEK_END);
+ break;
+ }
+ case IOWRITE:
+ u = creat(cp, 0666);
+ break;
+
+ case IODUP:
+ u = dup2(*cp - '0', iop->io_unit);
+ break;
+
+ case IOCLOSE:
+ close(iop->io_unit);
+ return 0;
+ }
+ if (u < 0) {
+ prs(cp);
+ prs(": cannot ");
+ warn(msg);
+ return 1;
+ }
+ if (u != iop->io_unit) {
+ dup2(u, iop->io_unit);
+ close(u);
+ }
+ return 0;
+}
+
+/*
+ * Enter a new loop level (marked for break/continue).
+ */
+static void brkset(struct brkcon *bc)
+{
+ bc->nextlev = brklist;
+ brklist = bc;
+}
+
+/*
+ * Wait for the last process created.
+ * Print a message for each process found
+ * that was killed by a signal.
+ * Ignore interrupt signals while waiting
+ * unless `canintr' is true.
+ */
+static int waitfor(int lastpid, int canintr)
+{
+ int pid, rv;
+ int s;
+ int oheedint = heedint;
+
+ heedint = 0;
+ rv = 0;
+ do {
+ pid = wait(&s);
+ if (pid == -1) {
+ if (errno != EINTR || canintr)
+ break;
+ } else {
+ rv = WAITSIG(s);
+ if (rv != 0) {
+ if (rv < NSIGNAL) {
+ if (signame[rv] != NULL) {
+ if (pid != lastpid) {
+ prn(pid);
+ prs(": ");
+ }
+ prs(signame[rv]);
+ }
+ } else {
+ if (pid != lastpid) {
+ prn(pid);
+ prs(": ");
+ }
+ prs("Signal ");
+ prn(rv);
+ prs(" ");
+ }
+ if (WAITCORE(s))
+ prs(" - core dumped");
+ if (rv >= NSIGNAL || signame[rv])
+ prs("\n");
+ rv = -1;
+ } else
+ rv = WAITVAL(s);
+ }
+ } while (pid != lastpid);
+ heedint = oheedint;
+ if (intr) {
+ if (interactive) {
+ if (canintr)
+ intr = 0;
+ } else {
+ if (exstat == 0)
+ exstat = rv;
+ onintr(0);
+ }
+ }
+ return rv;
+}
+
+static int setstatus(int s)
+{
+ exstat = s;
+ setval(lookup("?"), putn(s));
+ return s;
+}
+
+/*
+ * PATH-searching interface to execve.
+ * If getenv("PATH") were kept up-to-date,
+ * execvp might be used.
+ */
+static const char *rexecve(char *c, char **v, char **envp)
+{
+ int i;
+ const char *sp;
+ char *tp;
+ int eacces = 0, asis = 0;
+ char *name = c;
+
+ if (ENABLE_FEATURE_SH_STANDALONE_SHELL) {
+ optind = 1;
+ if (find_applet_by_name(name)) {
+ /* We have to exec here since we vforked. Running
+ * run_applet_by_name() won't work and bad things
+ * will happen. */
+ execve(CONFIG_BUSYBOX_EXEC_PATH, v, envp);
+ }
+ }
+
+ DBGPRINTF(("REXECVE: c=%p, v=%p, envp=%p\n", c, v, envp));
+
+ sp = any('/', c) ? "" : path->value;
+ asis = (*sp == '\0');
+ while (asis || *sp != '\0') {
+ asis = 0;
+ tp = e.linep;
+ for (; *sp != '\0'; tp++) {
+ *tp = *sp++;
+ if (*tp == ':') {
+ asis = (*sp == '\0');
+ break;
+ }
+ }
+ if (tp != e.linep)
+ *tp++ = '/';
+ for (i = 0; (*tp++ = c[i++]) != '\0';);
+
+ DBGPRINTF3(("REXECVE: e.linep is %s\n", e.linep));
+
+ execve(e.linep, v, envp);
+
+ switch (errno) {
+ case ENOEXEC:
+ *v = e.linep;
+ tp = *--v;
+ *v = e.linep;
+ execve(DEFAULT_SHELL, v, envp);
+ *v = tp;
+ return "no Shell";
+
+ case ENOMEM:
+ return (char *) bb_msg_memory_exhausted;
+
+ case E2BIG:
+ return "argument list too long";
+
+ case EACCES:
+ eacces++;
+ break;
+ }
+ }
+ return errno == ENOENT ? "not found" : "cannot execute";
+}
+
+/*
+ * Run the command produced by generator `f'
+ * applied to stream `arg'.
+ */
+static int run(struct ioarg *argp, int (*f) (struct ioarg *))
+{
+ struct op *otree;
+ struct wdblock *swdlist;
+ struct wdblock *siolist;
+ jmp_buf ev, rt;
+ xint *ofail;
+ int rv;
+
+#if __GNUC__
+ /* Avoid longjmp clobbering */
+ (void) &rv;
+#endif
+
+ DBGPRINTF(("RUN: enter, areanum %d, outtree %p, failpt %p\n",
+ areanum, outtree, failpt));
+
+ areanum++;
+ swdlist = wdlist;
+ siolist = iolist;
+ otree = outtree;
+ ofail = failpt;
+ rv = -1;
+
+ errpt = ev;
+ if (newenv(setjmp(errpt)) == 0) {
+ wdlist = 0;
+ iolist = 0;
+ pushio(argp, f);
+ e.iobase = e.iop;
+ yynerrs = 0;
+ failpt = rt;
+ if (setjmp(failpt) == 0 && yyparse() == 0)
+ rv = execute(outtree, NOPIPE, NOPIPE, 0);
+ quitenv();
+ } else {
+ DBGPRINTF(("RUN: error from newenv()!\n"));
+ }
+
+ wdlist = swdlist;
+ iolist = siolist;
+ failpt = ofail;
+ outtree = otree;
+ freearea(areanum--);
+
+ return rv;
+}
+
+/* -------- do.c -------- */
+
+/*
+ * built-in commands: doX
+ */
+
+static int dohelp(struct op *t)
+{
+ int col;
+ const struct builtincmd *x;
+
+ puts("\nBuilt-in commands:\n"
+ "-------------------");
+
+ for (col = 0, x = builtincmds; x->builtinfunc != NULL; x++) {
+ if (!x->name)
+ continue;
+ col += printf("%s%s", ((col == 0) ? "\t" : " "), x->name);
+ if (col > 60) {
+ puts("");
+ col = 0;
+ }
+ }
+#if ENABLE_FEATURE_SH_STANDALONE_SHELL
+ {
+ int i;
+ const struct BB_applet *applet;
+
+ for (i = 0, applet = applets; i < NUM_APPLETS; applet++, i++) {
+ if (!applet->name)
+ continue;
+
+ col += printf("%s%s", ((col == 0) ? "\t" : " "), applet->name);
+ if (col > 60) {
+ puts("");
+ col = 0;
+ }
+ }
+ }
+#endif
+ puts("\n");
+ return EXIT_SUCCESS;
+}
+
+
+
+static int dolabel(struct op *t)
+{
+ return 0;
+}
+
+static int dochdir(struct op *t)
+{
+ const char *cp, *er;
+
+ cp = t->words[1];
+ if (cp == NULL) {
+ cp = homedir->value;
+ if (cp != NULL)
+ goto do_cd;
+ er = ": no home directory";
+ } else {
+ do_cd:
+ if (chdir(cp) >= 0)
+ return 0;
+ er = ": bad directory";
+ }
+ prs(cp != NULL ? cp : "cd");
+ err(er);
+ return 1;
+}
+
+static int doshift(struct op *t)
+{
+ int n;
+
+ n = t->words[1] ? getn(t->words[1]) : 1;
+ if (dolc < n) {
+ err("nothing to shift");
+ return 1;
+ }
+ dolv[n] = dolv[0];
+ dolv += n;
+ dolc -= n;
+ setval(lookup("#"), putn(dolc));
+ return 0;
+}
+
+/*
+ * execute login and newgrp directly
+ */
+static int dologin(struct op *t)
+{
+ const char *cp;
+
+ if (interactive) {
+ signal(SIGINT, SIG_DFL);
+ signal(SIGQUIT, SIG_DFL);
+ }
+ cp = rexecve(t->words[0], t->words, makenv(0, NULL));
+ prs(t->words[0]);
+ prs(": ");
+ err(cp);
+ return 1;
+}
+
+static int doumask(struct op *t)
+{
+ int i, n;
+ char *cp;
+
+ cp = t->words[1];
+ if (cp == NULL) {
+ i = umask(0);
+ umask(i);
+ for (n = 3 * 4; (n -= 3) >= 0;)
+ putc('0' + ((i >> n) & 07), stderr);
+ putc('\n', stderr);
+ } else {
+/* huh??? '8','9' are not allowed! */
+ for (n = 0; *cp >= '0' && *cp <= '9'; cp++)
+ n = n * 8 + (*cp - '0');
+ umask(n);
+ }
+ return 0;
+}
+
+static int doexec(struct op *t)
+{
+ int i;
+ jmp_buf ex;
+ xint *ofail;
+
+ t->ioact = NULL;
+ for (i = 0; (t->words[i] = t->words[i + 1]) != NULL; i++);
+ if (i == 0)
+ return 1;
+ execflg = 1;
+ ofail = failpt;
+ failpt = ex;
+ if (setjmp(failpt) == 0)
+ execute(t, NOPIPE, NOPIPE, FEXEC);
+ failpt = ofail;
+ execflg = 0;
+ return 1;
+}
+
+static int dodot(struct op *t)
+{
+ int i;
+ const char *sp;
+ char *tp;
+ char *cp;
+ int maltmp;
+
+ DBGPRINTF(("DODOT: enter, t=%p, tleft %p, tright %p, e.linep is %s\n", t, t->left, t->right, ((e.linep == NULL) ? "NULL" : e.linep)));
+
+ cp = t->words[1];
+ if (cp == NULL) {
+ DBGPRINTF(("DODOT: bad args, ret 0\n"));
+ return 0;
+ }
+ DBGPRINTF(("DODOT: cp is %s\n", cp));
+
+ sp = any('/', cp) ? ":" : path->value;
+
+ DBGPRINTF(("DODOT: sp is %s, e.linep is %s\n",
+ ((sp == NULL) ? "NULL" : sp),
+ ((e.linep == NULL) ? "NULL" : e.linep)));
+
+ while (*sp) {
+ tp = e.linep;
+ while (*sp && (*tp = *sp++) != ':')
+ tp++;
+ if (tp != e.linep)
+ *tp++ = '/';
+
+ for (i = 0; (*tp++ = cp[i++]) != '\0';);
+
+ /* Original code */
+ i = open(e.linep, 0);
+ if (i >= 0) {
+ exstat = 0;
+ maltmp = remap(i);
+ DBGPRINTF(("DODOT: remap=%d, exstat=%d, e.iofd %d, i %d, e.linep is %s\n", maltmp, exstat, e.iofd, i, e.linep));
+
+ next(maltmp); /* Basically a PUSHIO */
+
+ DBGPRINTF(("DODOT: returning exstat=%d\n", exstat));
+
+ return exstat;
+ }
+ } /* while */
+
+ prs(cp);
+ err(": not found");
+
+ return -1;
+}
+
+static int dowait(struct op *t)
+{
+ int i;
+ char *cp;
+
+ cp = t->words[1];
+ if (cp != NULL) {
+ i = getn(cp);
+ if (i == 0)
+ return 0;
+ } else
+ i = -1;
+ setstatus(waitfor(i, 1));
+ return 0;
+}
+
+static int doread(struct op *t)
+{
+ char *cp, **wp;
+ int nb = 0;
+ int nl = 0;
+
+ if (t->words[1] == NULL) {
+ err("Usage: read name ...");
+ return 1;
+ }
+ for (wp = t->words + 1; *wp; wp++) {
+ for (cp = e.linep; !nl && cp < elinep - 1; cp++) {
+ nb = read(0, cp, sizeof(*cp));
+ if (nb != sizeof(*cp))
+ break;
+ nl = (*cp == '\n');
+ if (nl || (wp[1] && any(*cp, ifs->value)))
+ break;
+ }
+ *cp = '\0';
+ if (nb <= 0)
+ break;
+ setval(lookup(*wp), e.linep);
+ }
+ return nb <= 0;
+}
+
+static int doeval(struct op *t)
+{
+ return RUN(awordlist, t->words + 1, wdchar);
+}
+
+static int dotrap(struct op *t)
+{
+ int n, i;
+ int resetsig;
+
+ if (t->words[1] == NULL) {
+ for (i = 0; i <= _NSIG; i++)
+ if (trap[i]) {
+ prn(i);
+ prs(": ");
+ prs(trap[i]);
+ prs("\n");
+ }
+ return 0;
+ }
+ resetsig = isdigit(*t->words[1]);
+ for (i = resetsig ? 1 : 2; t->words[i] != NULL; ++i) {
+ n = getsig(t->words[i]);
+ freecell(trap[n]);
+ trap[n] = 0;
+ if (!resetsig) {
+ if (*t->words[1] != '\0') {
+ trap[n] = strsave(t->words[1], 0);
+ setsig(n, sig);
+ } else
+ setsig(n, SIG_IGN);
+ } else {
+ if (interactive) {
+ if (n == SIGINT)
+ setsig(n, onintr);
+ else
+ setsig(n, n == SIGQUIT ? SIG_IGN : SIG_DFL);
+ } else
+ setsig(n, SIG_DFL);
+ }
+ }
+ return 0;
+}
+
+static int getsig(char *s)
+{
+ int n;
+
+ n = getn(s);
+ if (n < 0 || n > _NSIG) {
+ err("trap: bad signal number");
+ n = 0;
+ }
+ return n;
+}
+
+static void setsig(int n, sighandler_t f)
+{
+ if (n == 0)
+ return;
+ if (signal(n, SIG_IGN) != SIG_IGN || ourtrap[n]) {
+ ourtrap[n] = 1;
+ signal(n, f);
+ }
+}
+
+static int getn(char *as)
+{
+ char *s;
+ int n, m;
+
+ s = as;
+ m = 1;
+ if (*s == '-') {
+ m = -1;
+ s++;
+ }
+ for (n = 0; isdigit(*s); s++)
+ n = (n * 10) + (*s - '0');
+ if (*s) {
+ prs(as);
+ err(": bad number");
+ }
+ return n * m;
+}
+
+static int dobreak(struct op *t)
+{
+ return brkcontin(t->words[1], 1);
+}
+
+static int docontinue(struct op *t)
+{
+ return brkcontin(t->words[1], 0);
+}
+
+static int brkcontin(char *cp, int val)
+{
+ struct brkcon *bc;
+ int nl;
+
+ nl = cp == NULL ? 1 : getn(cp);
+ if (nl <= 0)
+ nl = 999;
+ do {
+ bc = brklist;
+ if (bc == NULL)
+ break;
+ brklist = bc->nextlev;
+ } while (--nl);
+ if (nl) {
+ err("bad break/continue level");
+ return 1;
+ }
+ isbreak = val;
+ longjmp(bc->brkpt, 1);
+ /* NOTREACHED */
+}
+
+static int doexit(struct op *t)
+{
+ char *cp;
+
+ execflg = 0;
+ cp = t->words[1];
+ if (cp != NULL)
+ setstatus(getn(cp));
+
+ DBGPRINTF(("DOEXIT: calling leave(), t=%p\n", t));
+
+ leave();
+ /* NOTREACHED */
+ return 0;
+}
+
+static int doexport(struct op *t)
+{
+ rdexp(t->words + 1, export, EXPORT);
+ return 0;
+}
+
+static int doreadonly(struct op *t)
+{
+ rdexp(t->words + 1, ronly, RONLY);
+ return 0;
+}
+
+static void rdexp(char **wp, void (*f) (struct var *), int key)
+{
+ DBGPRINTF6(("RDEXP: enter, wp=%p, func=%p, key=%d\n", wp, f, key));
+ DBGPRINTF6(("RDEXP: *wp=%s\n", *wp));
+
+ if (*wp != NULL) {
+ for (; *wp != NULL; wp++) {
+ if (isassign(*wp)) {
+ char *cp;
+
+ assign(*wp, COPYV);
+ for (cp = *wp; *cp != '='; cp++);
+ *cp = '\0';
+ }
+ if (checkname(*wp))
+ (*f) (lookup(*wp));
+ else
+ badid(*wp);
+ }
+ } else
+ putvlist(key, 1);
+}
+
+static void badid(char *s)
+{
+ prs(s);
+ err(": bad identifier");
+}
+
+static int doset(struct op *t)
+{
+ struct var *vp;
+ char *cp;
+ int n;
+
+ cp = t->words[1];
+ if (cp == NULL) {
+ for (vp = vlist; vp; vp = vp->next)
+ varput(vp->name, 1);
+ return 0;
+ }
+ if (*cp == '-') {
+ /* bad: t->words++; */
+ for (n = 0; (t->words[n] = t->words[n + 1]) != NULL; n++);
+ if (*++cp == 0)
+ flag['x'] = flag['v'] = 0;
+ else {
+ for (; *cp; cp++) {
+ switch (*cp) {
+ case 'e':
+ if (!interactive)
+ flag['e']++;
+ break;
+
+ default:
+ if (*cp >= 'a' && *cp <= 'z')
+ flag[(int) *cp]++;
+ break;
+ }
+ }
+ }
+ setdash();
+ }
+ if (t->words[1]) {
+ t->words[0] = dolv[0];
+ for (n = 1; t->words[n]; n++)
+ setarea((char *) t->words[n], 0);
+ dolc = n - 1;
+ dolv = t->words;
+ setval(lookup("#"), putn(dolc));
+ setarea((char *) (dolv - 1), 0);
+ }
+ return 0;
+}
+
+static void varput(char *s, int out)
+{
+ if (isalnum(*s) || *s == '_') {
+ write(out, s, strlen(s));
+ write(out, "\n", 1);
+ }
+}
+
+
+/*
+ * Copyright (c) 1999 Herbert Xu <herbert@debian.org>
+ * This file contains code for the times builtin.
+ */
+static int dotimes(struct op *t)
+{
+ struct tms buf;
+ long clk_tck = sysconf(_SC_CLK_TCK);
+
+ times(&buf);
+ printf("%dm%fs %dm%fs\n%dm%fs %dm%fs\n",
+ (int) (buf.tms_utime / clk_tck / 60),
+ ((double) buf.tms_utime) / clk_tck,
+ (int) (buf.tms_stime / clk_tck / 60),
+ ((double) buf.tms_stime) / clk_tck,
+ (int) (buf.tms_cutime / clk_tck / 60),
+ ((double) buf.tms_cutime) / clk_tck,
+ (int) (buf.tms_cstime / clk_tck / 60),
+ ((double) buf.tms_cstime) / clk_tck);
+ return 0;
+}
+
+
+/* -------- eval.c -------- */
+
+/*
+ * ${}
+ * `command`
+ * blank interpretation
+ * quoting
+ * glob
+ */
+
+static char **eval(char **ap, int f)
+{
+ struct wdblock *wb;
+ char **wp;
+ char **wf;
+ jmp_buf ev;
+
+#if __GNUC__
+ /* Avoid longjmp clobbering */
+ (void) &wp;
+ (void) &ap;
+#endif
+
+ DBGPRINTF4(("EVAL: enter, f=%d\n", f));
+
+ wp = NULL;
+ wb = NULL;
+ wf = NULL;
+ errpt = ev;
+ if (newenv(setjmp(errpt)) == 0) {
+ while (*ap && isassign(*ap))
+ expand(*ap++, &wb, f & ~DOGLOB);
+ if (flag['k']) {
+ for (wf = ap; *wf; wf++) {
+ if (isassign(*wf))
+ expand(*wf, &wb, f & ~DOGLOB);
+ }
+ }
+ for (wb = addword((char *) 0, wb); *ap; ap++) {
+ if (!flag['k'] || !isassign(*ap))
+ expand(*ap, &wb, f & ~DOKEY);
+ }
+ wb = addword((char *) 0, wb);
+ wp = getwords(wb);
+ quitenv();
+ } else
+ gflg = 1;
+
+ return gflg ? (char **) NULL : wp;
+}
+
+
+/*
+ * Make the exported environment from the exported
+ * names in the dictionary. Keyword assignments
+ * will already have been done.
+ */
+static char **makenv(int all, struct wdblock *wb)
+{
+ struct var *vp;
+
+ DBGPRINTF5(("MAKENV: enter, all=%d\n", all));
+
+ for (vp = vlist; vp; vp = vp->next)
+ if (all || vp->status & EXPORT)
+ wb = addword(vp->name, wb);
+ wb = addword((char *) 0, wb);
+ return getwords(wb);
+}
+
+static int expand(const char *cp, struct wdblock **wbp, int f)
+{
+ jmp_buf ev;
+ char *xp;
+
+#if __GNUC__
+ /* Avoid longjmp clobbering */
+ (void) &cp;
+#endif
+
+ DBGPRINTF3(("EXPAND: enter, f=%d\n", f));
+
+ gflg = 0;
+
+ if (cp == NULL)
+ return 0;
+
+ if (!anys("$`'\"", cp) && !anys(ifs->value, cp)
+ && ((f & DOGLOB) == 0 || !anys("[*?", cp))
+ ) {
+ xp = strsave(cp, areanum);
+ if (f & DOTRIM)
+ unquote(xp);
+ *wbp = addword(xp, *wbp);
+ return 1;
+ }
+ errpt = ev;
+ if (newenv(setjmp(errpt)) == 0) {
+ PUSHIO(aword, cp, strchar);
+ e.iobase = e.iop;
+ while ((xp = blank(f)) && gflg == 0) {
+ e.linep = xp;
+ xp = strsave(xp, areanum);
+ if ((f & DOGLOB) == 0) {
+ if (f & DOTRIM)
+ unquote(xp);
+ *wbp = addword(xp, *wbp);
+ } else
+ *wbp = glob(xp, *wbp);
+ }
+ quitenv();
+ } else
+ gflg = 1;
+ return gflg == 0;
+}
+
+static char *evalstr(char *cp, int f)
+{
+ struct wdblock *wb;
+
+ DBGPRINTF6(("EVALSTR: enter, cp=%p, f=%d\n", cp, f));
+
+ wb = NULL;
+ if (expand(cp, &wb, f)) {
+ if (wb == NULL || wb->w_nword == 0
+ || (cp = wb->w_words[0]) == NULL
+ ) {
+// TODO: I suspect that
+// char *evalstr(char *cp, int f) is actually
+// const char *evalstr(const char *cp, int f)!
+ cp = (char*)"";
+ }
+ DELETE(wb);
+ } else
+ cp = NULL;
+ return cp;
+}
+
+
+/*
+ * Blank interpretation and quoting
+ */
+static char *blank(int f)
+{
+ int c, c1;
+ char *sp;
+ int scanequals, foundequals;
+
+ DBGPRINTF3(("BLANK: enter, f=%d\n", f));
+
+ sp = e.linep;
+ scanequals = f & DOKEY;
+ foundequals = 0;
+
+ loop:
+ c = subgetc('"', foundequals);
+ switch (c) {
+ case 0:
+ if (sp == e.linep)
+ return 0;
+ *e.linep++ = 0;
+ return sp;
+
+ default:
+ if (f & DOBLANK && any(c, ifs->value))
+ goto loop;
+ break;
+
+ case '"':
+ case '\'':
+ scanequals = 0;
+ if (INSUB())
+ break;
+ for (c1 = c; (c = subgetc(c1, 1)) != c1;) {
+ if (c == 0)
+ break;
+ if (c == '\'' || !any(c, "$`\""))
+ c |= QUOTE;
+ *e.linep++ = c;
+ }
+ c = 0;
+ }
+ unget(c);
+ if (!isalpha(c) && c != '_')
+ scanequals = 0;
+ for (;;) {
+ c = subgetc('"', foundequals);
+ if (c == 0 ||
+ f & (DOBLANK && any(c, ifs->value)) ||
+ (!INSUB() && any(c, "\"'"))) {
+ scanequals = 0;
+ unget(c);
+ if (any(c, "\"'"))
+ goto loop;
+ break;
+ }
+ if (scanequals) {
+ if (c == '=') {
+ foundequals = 1;
+ scanequals = 0;
+ } else if (!isalnum(c) && c != '_')
+ scanequals = 0;
+ }
+ *e.linep++ = c;
+ }
+ *e.linep++ = 0;
+ return sp;
+}
+
+/*
+ * Get characters, substituting for ` and $
+ */
+static int subgetc(char ec, int quoted)
+{
+ char c;
+
+ DBGPRINTF3(("SUBGETC: enter, quoted=%d\n", quoted));
+
+ again:
+ c = my_getc(ec);
+ if (!INSUB() && ec != '\'') {
+ if (c == '`') {
+ if (grave(quoted) == 0)
+ return 0;
+ e.iop->task = XGRAVE;
+ goto again;
+ }
+ if (c == '$') {
+ c = dollar(quoted);
+ if (c == 0) {
+ e.iop->task = XDOLL;
+ goto again;
+ }
+ }
+ }
+ return c;
+}
+
+/*
+ * Prepare to generate the string returned by ${} substitution.
+ */
+static int dollar(int quoted)
+{
+ int otask;
+ struct io *oiop;
+ char *dolp;
+ char *s, c, *cp = NULL;
+ struct var *vp;
+
+ DBGPRINTF3(("DOLLAR: enter, quoted=%d\n", quoted));
+
+ c = readc();
+ s = e.linep;
+ if (c != '{') {
+ *e.linep++ = c;
+ if (isalpha(c) || c == '_') {
+ while ((c = readc()) != 0 && (isalnum(c) || c == '_'))
+ if (e.linep < elinep)
+ *e.linep++ = c;
+ unget(c);
+ }
+ c = 0;
+ } else {
+ oiop = e.iop;
+ otask = e.iop->task;
+
+ e.iop->task = XOTHER;
+ while ((c = subgetc('"', 0)) != 0 && c != '}' && c != '\n')
+ if (e.linep < elinep)
+ *e.linep++ = c;
+ if (oiop == e.iop)
+ e.iop->task = otask;
+ if (c != '}') {
+ err("unclosed ${");
+ gflg++;
+ return c;
+ }
+ }
+ if (e.linep >= elinep) {
+ err("string in ${} too long");
+ gflg++;
+ e.linep -= 10;
+ }
+ *e.linep = 0;
+ if (*s)
+ for (cp = s + 1; *cp; cp++)
+ if (any(*cp, "=-+?")) {
+ c = *cp;
+ *cp++ = 0;
+ break;
+ }
+ if (s[1] == 0 && (*s == '*' || *s == '@')) {
+ if (dolc > 1) {
+ /* currently this does not distinguish $* and $@ */
+ /* should check dollar */
+ e.linep = s;
+ PUSHIO(awordlist, dolv + 1, dolchar);
+ return 0;
+ } else { /* trap the nasty ${=} */
+ s[0] = '1';
+ s[1] = '\0';
+ }
+ }
+ vp = lookup(s);
+ dolp = vp->value;
+ if (dolp == null) {
+ switch (c) {
+ case '=':
+ if (isdigit(*s)) {
+ err("cannot use ${...=...} with $n");
+ gflg++;
+ break;
+ }
+ setval(vp, cp);
+ dolp = vp->value;
+ break;
+
+ case '-':
+ dolp = strsave(cp, areanum);
+ break;
+
+ case '?':
+ if (*cp == 0) {
+ prs("missing value for ");
+ err(s);
+ } else
+ err(cp);
+ gflg++;
+ break;
+ }
+ } else if (c == '+')
+ dolp = strsave(cp, areanum);
+ if (flag['u'] && dolp == null) {
+ prs("unset variable: ");
+ err(s);
+ gflg++;
+ }
+ e.linep = s;
+ PUSHIO(aword, dolp, quoted ? qstrchar : strchar);
+ return 0;
+}
+
+/*
+ * Run the command in `...` and read its output.
+ */
+
+static int grave(int quoted)
+{
+ const char *cp;
+ int i;
+ int j;
+ int pf[2];
+ static char child_cmd[LINELIM];
+ const char *src;
+ char *dest;
+ int count;
+ int ignore;
+ int ignore_once;
+ char *argument_list[4];
+ struct wdblock *wb = NULL;
+
+#if __GNUC__
+ /* Avoid longjmp clobbering */
+ (void) &cp;
+#endif
+
+ for (cp = e.iop->argp->aword; *cp != '`'; cp++) {
+ if (*cp == 0) {
+ err("no closing `");
+ return 0;
+ }
+ }
+
+ /* string copy with dollar expansion */
+ src = e.iop->argp->aword;
+ dest = child_cmd;
+ count = 0;
+ ignore = 0;
+ ignore_once = 0;
+ while ((*src != '`') && (count < LINELIM)) {
+ if (*src == '\'')
+ ignore = !ignore;
+ if (*src == '\\')
+ ignore_once = 1;
+ if (*src == '$' && !ignore && !ignore_once) {
+ struct var *vp;
+ char var_name[LINELIM];
+ char alt_value[LINELIM];
+ int var_index = 0;
+ int alt_index = 0;
+ char operator = 0;
+ int braces = 0;
+ char *value;
+
+ src++;
+ if (*src == '{') {
+ braces = 1;
+ src++;
+ }
+
+ var_name[var_index++] = *src++;
+ while (isalnum(*src) || *src=='_')
+ var_name[var_index++] = *src++;
+ var_name[var_index] = 0;
+
+ if (braces) {
+ switch (*src) {
+ case '}':
+ break;
+ case '-':
+ case '=':
+ case '+':
+ case '?':
+ operator = * src;
+ break;
+ default:
+ err("unclosed ${\n");
+ return 0;
+ }
+ if (operator) {
+ src++;
+ while (*src && (*src != '}')) {
+ alt_value[alt_index++] = *src++;
+ }
+ alt_value[alt_index] = 0;
+ if (*src != '}') {
+ err("unclosed ${\n");
+ return 0;
+ }
+ }
+ src++;
+ }
+
+ if (isalpha(*var_name)) {
+ /* let subshell handle it instead */
+
+ char *namep = var_name;
+
+ *dest++ = '$';
+ if (braces)
+ *dest++ = '{';
+ while (*namep)
+ *dest++ = *namep++;
+ if (operator) {
+ char *altp = alt_value;
+ *dest++ = operator;
+ while (*altp)
+ *dest++ = *altp++;
+ }
+ if (braces)
+ *dest++ = '}';
+
+ wb = addword(lookup(var_name)->name, wb);
+ } else {
+ /* expand */
+
+ vp = lookup(var_name);
+ if (vp->value != null)
+ value = (operator == '+') ?
+ alt_value : vp->value;
+ else if (operator == '?') {
+ err(alt_value);
+ return 0;
+ } else if (alt_index && (operator != '+')) {
+ value = alt_value;
+ if (operator == '=')
+ setval(vp, value);
+ } else
+ continue;
+
+ while (*value && (count < LINELIM)) {
+ *dest++ = *value++;
+ count++;
+ }
+ }
+ } else {
+ *dest++ = *src++;
+ count++;
+ ignore_once = 0;
+ }
+ }
+ *dest = '\0';
+
+ if (openpipe(pf) < 0)
+ return 0;
+
+ while ((i = vfork()) == -1 && errno == EAGAIN);
+
+ DBGPRINTF3(("GRAVE: i is %p\n", io));
+
+ if (i < 0) {
+ closepipe(pf);
+ err((char *) bb_msg_memory_exhausted);
+ return 0;
+ }
+ if (i != 0) {
+ waitpid(i, NULL, 0);
+ e.iop->argp->aword = ++cp;
+ close(pf[1]);
+ PUSHIO(afile, remap(pf[0]),
+ (int (*)(struct ioarg *)) ((quoted) ? qgravechar : gravechar));
+ return 1;
+ }
+ /* allow trapped signals */
+ /* XXX - Maybe this signal stuff should go as well? */
+ for (j = 0; j <= _NSIG; j++)
+ if (ourtrap[j] && signal(j, SIG_IGN) != SIG_IGN)
+ signal(j, SIG_DFL);
+
+ dup2(pf[1], 1);
+ closepipe(pf);
+
+ argument_list[0] = (char *) DEFAULT_SHELL;
+ argument_list[1] = (char *) "-c";
+ argument_list[2] = child_cmd;
+ argument_list[3] = NULL;
+
+ cp = rexecve(argument_list[0], argument_list, makenv(1, wb));
+ prs(argument_list[0]);
+ prs(": ");
+ err(cp);
+ _exit(1);
+}
+
+
+static char *unquote(char *as)
+{
+ char *s;
+
+ s = as;
+ if (s != NULL)
+ while (*s)
+ *s++ &= ~QUOTE;
+ return as;
+}
+
+/* -------- glob.c -------- */
+
+/*
+ * glob
+ */
+
+#define scopy(x) strsave((x), areanum)
+#define BLKSIZ 512
+#define NDENT ((BLKSIZ+sizeof(struct dirent)-1)/sizeof(struct dirent))
+
+static struct wdblock *cl, *nl;
+static char spcl[] = "[?*";
+
+static struct wdblock *glob(char *cp, struct wdblock *wb)
+{
+ int i;
+ char *pp;
+
+ if (cp == 0)
+ return wb;
+ i = 0;
+ for (pp = cp; *pp; pp++)
+ if (any(*pp, spcl))
+ i++;
+ else if (!any(*pp & ~QUOTE, spcl))
+ *pp &= ~QUOTE;
+ if (i != 0) {
+ for (cl = addword(scopy(cp), NULL); anyspcl(cl); cl = nl) {
+ nl = newword(cl->w_nword * 2);
+ for (i = 0; i < cl->w_nword; i++) { /* for each argument */
+ for (pp = cl->w_words[i]; *pp; pp++)
+ if (any(*pp, spcl)) {
+ globname(cl->w_words[i], pp);
+ break;
+ }
+ if (*pp == '\0')
+ nl = addword(scopy(cl->w_words[i]), nl);
+ }
+ for (i = 0; i < cl->w_nword; i++)
+ DELETE(cl->w_words[i]);
+ DELETE(cl);
+ }
+ for (i = 0; i < cl->w_nword; i++)
+ unquote(cl->w_words[i]);
+ glob0((char *) cl->w_words, cl->w_nword, sizeof(char *), xstrcmp);
+ if (cl->w_nword) {
+ for (i = 0; i < cl->w_nword; i++)
+ wb = addword(cl->w_words[i], wb);
+ DELETE(cl);
+ return wb;
+ }
+ }
+ wb = addword(unquote(cp), wb);
+ return wb;
+}
+
+static void globname(char *we, char *pp)
+{
+ char *np, *cp;
+ char *name, *gp, *dp;
+ int k;
+ DIR *dirp;
+ struct dirent *de;
+ char dname[NAME_MAX + 1];
+ struct stat dbuf;
+
+ for (np = we; np != pp; pp--)
+ if (pp[-1] == '/')
+ break;
+ for (dp = cp = space((int) (pp - np) + 3); np < pp;)
+ *cp++ = *np++;
+ *cp++ = '.';
+ *cp = '\0';
+ for (gp = cp = space(strlen(pp) + 1); *np && *np != '/';)
+ *cp++ = *np++;
+ *cp = '\0';
+ dirp = opendir(dp);
+ if (dirp == 0) {
+ DELETE(dp);
+ DELETE(gp);
+ return;
+ }
+ dname[NAME_MAX] = '\0';
+ while ((de = readdir(dirp)) != NULL) {
+ /* XXX Hmmm... What this could be? (abial) */
+ /*
+ if (ent[j].d_ino == 0)
+ continue;
+ */
+ strncpy(dname, de->d_name, NAME_MAX);
+ if (dname[0] == '.')
+ if (*gp != '.')
+ continue;
+ for (k = 0; k < NAME_MAX; k++)
+ if (any(dname[k], spcl))
+ dname[k] |= QUOTE;
+ if (gmatch(dname, gp)) {
+ name = generate(we, pp, dname, np);
+ if (*np && !anys(np, spcl)) {
+ if (stat(name, &dbuf)) {
+ DELETE(name);
+ continue;
+ }
+ }
+ nl = addword(name, nl);
+ }
+ }
+ closedir(dirp);
+ DELETE(dp);
+ DELETE(gp);
+}
+
+/*
+ * generate a pathname as below.
+ * start..end1 / middle end
+ * the slashes come for free
+ */
+static char *generate(char *start1, char *end1, char *middle, char *end)
+{
+ char *p;
+ char *op, *xp;
+
+ p = op = space((int)(end1 - start1) + strlen(middle) + strlen(end) + 2);
+ for (xp = start1; xp != end1;)
+ *op++ = *xp++;
+ for (xp = middle; (*op++ = *xp++) != '\0';);
+ op--;
+ for (xp = end; (*op++ = *xp++) != '\0';);
+ return p;
+}
+
+static int anyspcl(struct wdblock *wb)
+{
+ int i;
+ char **wd;
+
+ wd = wb->w_words;
+ for (i = 0; i < wb->w_nword; i++)
+ if (anys(spcl, *wd++))
+ return 1;
+ return 0;
+}
+
+static int xstrcmp(char *p1, char *p2)
+{
+ return strcmp(*(char **) p1, *(char **) p2);
+}
+
+
+/* -------- word.c -------- */
+
+static struct wdblock *newword(int nw)
+{
+ struct wdblock *wb;
+
+ wb = (struct wdblock *) space(sizeof(*wb) + nw * sizeof(char *));
+ wb->w_bsize = nw;
+ wb->w_nword = 0;
+ return wb;
+}
+
+static struct wdblock *addword(char *wd, struct wdblock *wb)
+{
+ struct wdblock *wb2;
+ int nw;
+
+ if (wb == NULL)
+ wb = newword(NSTART);
+ nw = wb->w_nword;
+ if (nw >= wb->w_bsize) {
+ wb2 = newword(nw * 2);
+ memcpy((char *) wb2->w_words, (char *) wb->w_words,
+ nw * sizeof(char *));
+ wb2->w_nword = nw;
+ DELETE(wb);
+ wb = wb2;
+ }
+ wb->w_words[wb->w_nword++] = wd;
+ return wb;
+}
+
+static
+char **getwords(struct wdblock *wb)
+{
+ char **wd;
+ int nb;
+
+ if (wb == NULL)
+ return NULL;
+ if (wb->w_nword == 0) {
+ DELETE(wb);
+ return NULL;
+ }
+ wd = (char **) space(nb = sizeof(*wd) * wb->w_nword);
+ memcpy((char *) wd, (char *) wb->w_words, nb);
+ DELETE(wb); /* perhaps should done by caller */
+ return wd;
+}
+
+static int (*func) (char *, char *);
+static int globv;
+
+static void glob3(char *i, char *j, char *k)
+{
+ char *index1, *index2, *index3;
+ int c;
+ int m;
+
+ m = globv;
+ index1 = i;
+ index2 = j;
+ index3 = k;
+ do {
+ c = *index1;
+ *index1++ = *index3;
+ *index3++ = *index2;
+ *index2++ = c;
+ } while (--m);
+}
+
+static void glob2(char *i, char *j)
+{
+ char *index1, *index2, c;
+ int m;
+
+ m = globv;
+ index1 = i;
+ index2 = j;
+ do {
+ c = *index1;
+ *index1++ = *index2;
+ *index2++ = c;
+ } while (--m);
+}
+
+static void glob1(char *base, char *lim)
+{
+ char *i, *j;
+ int v2;
+ char *lptr, *hptr;
+ int c;
+ unsigned n;
+
+ v2 = globv;
+
+ top:
+ n = (int) (lim - base);
+ if (n <= v2)
+ return;
+ n = v2 * (n / (2 * v2));
+ hptr = lptr = base + n;
+ i = base;
+ j = lim - v2;
+ for (;;) {
+ if (i < lptr) {
+ c = (*func) (i, lptr);
+ if (c == 0) {
+ lptr -= v2;
+ glob2(i, lptr);
+ continue;
+ }
+ if (c < 0) {
+ i += v2;
+ continue;
+ }
+ }
+
+ begin:
+ if (j > hptr) {
+ c = (*func) (hptr, j);
+ if (c == 0) {
+ hptr += v2;
+ glob2(hptr, j);
+ goto begin;
+ }
+ if (c > 0) {
+ if (i == lptr) {
+ hptr += v2;
+ glob3(i, hptr, j);
+ i = (lptr += v2);
+ goto begin;
+ }
+ glob2(i, j);
+ j -= v2;
+ i += v2;
+ continue;
+ }
+ j -= v2;
+ goto begin;
+ }
+
+
+ if (i == lptr) {
+ if (lptr - base >= lim - hptr) {
+ glob1(hptr + v2, lim);
+ lim = lptr;
+ } else {
+ glob1(base, lptr);
+ base = hptr + v2;
+ }
+ goto top;
+ }
+
+ lptr -= v2;
+ glob3(j, lptr, i);
+ j = (hptr -= v2);
+ }
+}
+
+static void glob0(char *a0, unsigned a1, int a2, int (*a3) (char *, char *))
+{
+ func = a3;
+ globv = a2;
+ glob1(a0, a0 + a1 * a2);
+}
+
+
+/* -------- io.c -------- */
+
+/*
+ * shell IO
+ */
+
+static int my_getc(int ec)
+{
+ int c;
+
+ if (e.linep > elinep) {
+ while ((c = readc()) != '\n' && c);
+ err("input line too long");
+ gflg++;
+ return c;
+ }
+ c = readc();
+ if ((ec != '\'') && (ec != '`') && (e.iop->task != XGRAVE)) {
+ if (c == '\\') {
+ c = readc();
+ if (c == '\n' && ec != '\"')
+ return my_getc(ec);
+ c |= QUOTE;
+ }
+ }
+ return c;
+}
+
+static void unget(int c)
+{
+ if (e.iop >= e.iobase)
+ e.iop->peekc = c;
+}
+
+static int eofc(void)
+{
+ return e.iop < e.iobase || (e.iop->peekc == 0 && e.iop->prev == 0);
+}
+
+static int readc(void)
+{
+ int c;
+
+ RCPRINTF(("READC: e.iop %p, e.iobase %p\n", e.iop, e.iobase));
+
+ for (; e.iop >= e.iobase; e.iop--) {
+ RCPRINTF(("READC: e.iop %p, peekc 0x%x\n", e.iop, e.iop->peekc));
+ c = e.iop->peekc;
+ if (c != '\0') {
+ e.iop->peekc = 0;
+ return c;
+ }
+ if (e.iop->prev != 0) {
+ c = (*e.iop->iofn)(e.iop->argp, e.iop);
+ if (c != '\0') {
+ if (c == -1) {
+ e.iop++;
+ continue;
+ }
+ if (e.iop == iostack)
+ ioecho(c);
+ e.iop->prev = c;
+ return e.iop->prev;
+ }
+ if (e.iop->task == XIO && e.iop->prev != '\n') {
+ e.iop->prev = 0;
+ if (e.iop == iostack)
+ ioecho('\n');
+ return '\n';
+ }
+ }
+ if (e.iop->task == XIO) {
+ if (multiline) {
+ e.iop->prev = 0;
+ return e.iop->prev;
+ }
+ if (interactive && e.iop == iostack + 1) {
+#if ENABLE_FEATURE_EDITING
+ current_prompt = prompt->value;
+#else
+ prs(prompt->value);
+#endif
+ }
+ }
+ } /* FOR */
+
+ if (e.iop >= iostack) {
+ RCPRINTF(("READC: return 0, e.iop %p\n", e.iop));
+ return 0;
+ }
+
+ DBGPRINTF(("READC: leave()...\n"));
+ leave();
+
+ /* NOTREACHED */
+ return 0;
+}
+
+static void ioecho(char c)
+{
+ if (flag['v'])
+ write(2, &c, sizeof c);
+}
+
+
+static void pushio(struct ioarg *argp, int (*fn) (struct ioarg *))
+{
+ DBGPRINTF(("PUSHIO: argp %p, argp->afid 0x%x, e.iop %p\n", argp,
+ argp->afid, e.iop));
+
+ /* Set env ptr for io source to next array spot and check for array overflow */
+ if (++e.iop >= &iostack[NPUSH]) {
+ e.iop--;
+ err("Shell input nested too deeply");
+ gflg++;
+ return;
+ }
+
+ /* We did not overflow the NPUSH array spots so setup data structs */
+
+ e.iop->iofn = (int (*)(struct ioarg *, struct io *)) fn; /* Store data source func ptr */
+
+ if (argp->afid != AFID_NOBUF)
+ e.iop->argp = argp;
+ else {
+
+ e.iop->argp = ioargstack + (e.iop - iostack); /* MAL - index into stack */
+ *e.iop->argp = *argp; /* copy data from temp area into stack spot */
+
+ /* MAL - mainbuf is for 1st data source (command line?) and all nested use a single shared buffer? */
+
+ if (e.iop == &iostack[0])
+ e.iop->argp->afbuf = &mainbuf;
+ else
+ e.iop->argp->afbuf = &sharedbuf;
+
+ /* MAL - if not a termimal AND (commandline OR readable file) then give it a buffer id? */
+ /* This line appears to be active when running scripts from command line */
+ if ((isatty(e.iop->argp->afile) == 0)
+ && (e.iop == &iostack[0]
+ || lseek(e.iop->argp->afile, 0L, SEEK_CUR) != -1)) {
+ if (++bufid == AFID_NOBUF) /* counter rollover check, AFID_NOBUF = 11111111 */
+ bufid = AFID_ID; /* AFID_ID = 0 */
+
+ e.iop->argp->afid = bufid; /* assign buffer id */
+ }
+
+ DBGPRINTF(("PUSHIO: iostack %p, e.iop %p, afbuf %p\n",
+ iostack, e.iop, e.iop->argp->afbuf));
+ DBGPRINTF(("PUSHIO: mbuf %p, sbuf %p, bid %d, e.iop %p\n",
+ &mainbuf, &sharedbuf, bufid, e.iop));
+
+ }
+
+ e.iop->prev = ~'\n';
+ e.iop->peekc = 0;
+ e.iop->xchar = 0;
+ e.iop->nlcount = 0;
+
+ if (fn == filechar || fn == linechar)
+ e.iop->task = XIO;
+ else if (fn == (int (*)(struct ioarg *)) gravechar
+ || fn == (int (*)(struct ioarg *)) qgravechar)
+ e.iop->task = XGRAVE;
+ else
+ e.iop->task = XOTHER;
+}
+
+static struct io *setbase(struct io *ip)
+{
+ struct io *xp;
+
+ xp = e.iobase;
+ e.iobase = ip;
+ return xp;
+}
+
+/*
+ * Input generating functions
+ */
+
+/*
+ * Produce the characters of a string, then a newline, then EOF.
+ */
+static int nlchar(struct ioarg *ap)
+{
+ int c;
+
+ if (ap->aword == NULL)
+ return 0;
+ c = *ap->aword++;
+ if (c == 0) {
+ ap->aword = NULL;
+ return '\n';
+ }
+ return c;
+}
+
+/*
+ * Given a list of words, produce the characters
+ * in them, with a space after each word.
+ */
+static int wdchar(struct ioarg *ap)
+{
+ char c;
+ char **wl;
+
+ wl = ap->awordlist;
+ if (wl == NULL)
+ return 0;
+ if (*wl != NULL) {
+ c = *(*wl)++;
+ if (c != 0)
+ return c & 0177;
+ ap->awordlist++;
+ return ' ';
+ }
+ ap->awordlist = NULL;
+ return '\n';
+}
+
+/*
+ * Return the characters of a list of words,
+ * producing a space between them.
+ */
+static int dolchar(struct ioarg *ap)
+{
+ char *wp;
+
+ wp = *ap->awordlist++;
+ if (wp != NULL) {
+ PUSHIO(aword, wp, *ap->awordlist == NULL ? strchar : xxchar);
+ return -1;
+ }
+ return 0;
+}
+
+static int xxchar(struct ioarg *ap)
+{
+ int c;
+
+ if (ap->aword == NULL)
+ return 0;
+ c = *ap->aword++;
+ if (c == '\0') {
+ ap->aword = NULL;
+ return ' ';
+ }
+ return c;
+}
+
+/*
+ * Produce the characters from a single word (string).
+ */
+static int strchar(struct ioarg *ap)
+{
+ if (ap->aword == NULL)
+ return 0;
+ return *ap->aword++;
+}
+
+/*
+ * Produce quoted characters from a single word (string).
+ */
+static int qstrchar(struct ioarg *ap)
+{
+ int c;
+
+ if (ap->aword == NULL)
+ return 0;
+ c = *ap->aword++;
+ if (c)
+ c |= QUOTE;
+ return c;
+}
+
+/*
+ * Return the characters from a file.
+ */
+static int filechar(struct ioarg *ap)
+{
+ int i;
+ char c;
+ struct iobuf *bp = ap->afbuf;
+
+ if (ap->afid != AFID_NOBUF) {
+ i = (ap->afid != bp->id);
+ if (i || bp->bufp == bp->ebufp) {
+ if (i)
+ lseek(ap->afile, ap->afpos, SEEK_SET);
+
+ i = safe_read(ap->afile, bp->buf, sizeof(bp->buf));
+ if (i <= 0) {
+ closef(ap->afile);
+ return 0;
+ }
+
+ bp->id = ap->afid;
+ bp->bufp = bp->buf;
+ bp->ebufp = bp->bufp + i;
+ }
+
+ ap->afpos++;
+ return *bp->bufp++ & 0177;
+ }
+#if ENABLE_FEATURE_EDITING
+ if (interactive && isatty(ap->afile)) {
+ static char mycommand[BUFSIZ];
+ static int position = 0, size = 0;
+
+ while (size == 0 || position >= size) {
+ read_line_input(current_prompt, mycommand, BUFSIZ, line_input_state);
+ size = strlen(mycommand);
+ position = 0;
+ }
+ c = mycommand[position];
+ position++;
+ return c;
+ }
+#endif
+ i = safe_read(ap->afile, &c, sizeof(c));
+ return i == sizeof(c) ? (c & 0x7f) : (closef(ap->afile), 0);
+}
+
+/*
+ * Return the characters from a here temp file.
+ */
+static int herechar(struct ioarg *ap)
+{
+ char c;
+
+ if (read(ap->afile, &c, sizeof(c)) != sizeof(c)) {
+ close(ap->afile);
+ c = '\0';
+ }
+ return c;
+}
+
+/*
+ * Return the characters produced by a process (`...`).
+ * Quote them if required, and remove any trailing newline characters.
+ */
+static int gravechar(struct ioarg *ap, struct io *iop)
+{
+ int c;
+
+ c = qgravechar(ap, iop) & ~QUOTE;
+ if (c == '\n')
+ c = ' ';
+ return c;
+}
+
+static int qgravechar(struct ioarg *ap, struct io *iop)
+{
+ int c;
+
+ DBGPRINTF3(("QGRAVECHAR: enter, ap=%p, iop=%p\n", ap, iop));
+
+ if (iop->xchar) {
+ if (iop->nlcount) {
+ iop->nlcount--;
+ return '\n' | QUOTE;
+ }
+ c = iop->xchar;
+ iop->xchar = 0;
+ } else if ((c = filechar(ap)) == '\n') {
+ iop->nlcount = 1;
+ while ((c = filechar(ap)) == '\n')
+ iop->nlcount++;
+ iop->xchar = c;
+ if (c == 0)
+ return c;
+ iop->nlcount--;
+ c = '\n';
+ }
+ return c != 0 ? c | QUOTE : 0;
+}
+
+/*
+ * Return a single command (usually the first line) from a file.
+ */
+static int linechar(struct ioarg *ap)
+{
+ int c;
+
+ c = filechar(ap);
+ if (c == '\n') {
+ if (!multiline) {
+ closef(ap->afile);
+ ap->afile = -1; /* illegal value */
+ }
+ }
+ return c;
+}
+
+/*
+ * remap fd into Shell's fd space
+ */
+static int remap(int fd)
+{
+ int i;
+ int map[NOFILE];
+ int newfd;
+
+ DBGPRINTF(("REMAP: fd=%d, e.iofd=%d\n", fd, e.iofd));
+
+ if (fd < e.iofd) {
+ for (i = 0; i < NOFILE; i++)
+ map[i] = 0;
+
+ do {
+ map[fd] = 1;
+ newfd = dup(fd);
+ fd = newfd;
+ } while (fd >= 0 && fd < e.iofd);
+
+ for (i = 0; i < NOFILE; i++)
+ if (map[i])
+ close(i);
+
+ if (fd < 0)
+ err("too many files open in shell");
+ }
+
+ return fd;
+}
+
+static int openpipe(int *pv)
+{
+ int i;
+
+ i = pipe(pv);
+ if (i < 0)
+ err("can't create pipe - try again");
+ return i;
+}
+
+static void closepipe(int *pv)
+{
+ if (pv != NULL) {
+ close(*pv++);
+ close(*pv);
+ }
+}
+
+
+/* -------- here.c -------- */
+
+/*
+ * here documents
+ */
+
+static void markhere(char *s, struct ioword *iop)
+{
+ struct here *h, *lh;
+
+ DBGPRINTF7(("MARKHERE: enter, s=%p\n", s));
+
+ h = (struct here *) space(sizeof(struct here));
+ if (h == NULL)
+ return;
+
+ h->h_tag = evalstr(s, DOSUB);
+ if (h->h_tag == 0)
+ return;
+
+ h->h_iop = iop;
+ iop->io_name = 0;
+ h->h_next = NULL;
+ if (inhere == 0)
+ inhere = h;
+ else {
+ for (lh = inhere; lh != NULL; lh = lh->h_next) {
+ if (lh->h_next == 0) {
+ lh->h_next = h;
+ break;
+ }
+ }
+ }
+ iop->io_flag |= IOHERE | IOXHERE;
+ for (s = h->h_tag; *s; s++) {
+ if (*s & QUOTE) {
+ iop->io_flag &= ~IOXHERE;
+ *s &= ~QUOTE;
+ }
+ }
+ h->h_dosub = iop->io_flag & IOXHERE;
+}
+
+static void gethere(void)
+{
+ struct here *h, *hp;
+
+ DBGPRINTF7(("GETHERE: enter...\n"));
+
+ /* Scan here files first leaving inhere list in place */
+ for (hp = h = inhere; h != NULL; hp = h, h = h->h_next)
+ readhere(&h->h_iop->io_name, h->h_tag, h->h_dosub ? 0 : '\'');
+
+ /* Make inhere list active - keep list intact for scraphere */
+ if (hp != NULL) {
+ hp->h_next = acthere;
+ acthere = inhere;
+ inhere = NULL;
+ }
+}
+
+static void readhere(char **name, char *s, int ec)
+{
+ int tf;
+ char tname[30] = ".msh_XXXXXX";
+ int c;
+ jmp_buf ev;
+ char myline[LINELIM + 1];
+ char *thenext;
+
+ DBGPRINTF7(("READHERE: enter, name=%p, s=%p\n", name, s));
+
+ tf = mkstemp(tname);
+ if (tf < 0)
+ return;
+
+ *name = strsave(tname, areanum);
+ errpt = ev;
+ if (newenv(setjmp(errpt)) != 0)
+ unlink(tname);
+ else {
+ pushio(e.iop->argp, (int (*)(struct ioarg *)) e.iop->iofn);
+ e.iobase = e.iop;
+ for (;;) {
+ if (interactive && e.iop <= iostack) {
+#if ENABLE_FEATURE_EDITING
+ current_prompt = cprompt->value;
+#else
+ prs(cprompt->value);
+#endif
+ }
+ thenext = myline;
+ while ((c = my_getc(ec)) != '\n' && c) {
+ if (ec == '\'')
+ c &= ~QUOTE;
+ if (thenext >= &myline[LINELIM]) {
+ c = 0;
+ break;
+ }
+ *thenext++ = c;
+ }
+ *thenext = 0;
+ if (strcmp(s, myline) == 0 || c == 0)
+ break;
+ *thenext++ = '\n';
+ write(tf, myline, (int) (thenext - myline));
+ }
+ if (c == 0) {
+ prs("here document `");
+ prs(s);
+ err("' unclosed");
+ }
+ quitenv();
+ }
+ close(tf);
+}
+
+/*
+ * open here temp file.
+ * if unquoted here, expand here temp file into second temp file.
+ */
+static int herein(char *hname, int xdoll)
+{
+ int hf;
+ int tf;
+
+#if __GNUC__
+ /* Avoid longjmp clobbering */
+ (void) &tf;
+#endif
+ if (hname == NULL)
+ return -1;
+
+ DBGPRINTF7(("HEREIN: hname is %s, xdoll=%d\n", hname, xdoll));
+
+ hf = open(hname, 0);
+ if (hf < 0)
+ return -1;
+
+ if (xdoll) {
+ char c;
+ char tname[30] = ".msh_XXXXXX";
+ jmp_buf ev;
+
+ tf = mkstemp(tname);
+ if (tf < 0)
+ return -1;
+ errpt = ev;
+ if (newenv(setjmp(errpt)) == 0) {
+ PUSHIO(afile, hf, herechar);
+ setbase(e.iop);
+ while ((c = subgetc(0, 0)) != 0) {
+ c &= ~QUOTE;
+ write(tf, &c, sizeof c);
+ }
+ quitenv();
+ } else
+ unlink(tname);
+ close(tf);
+ tf = open(tname, 0);
+ unlink(tname);
+ return tf;
+ }
+ return hf;
+}
+
+static void scraphere(void)
+{
+ struct here *h;
+
+ DBGPRINTF7(("SCRAPHERE: enter...\n"));
+
+ for (h = inhere; h != NULL; h = h->h_next) {
+ if (h->h_iop && h->h_iop->io_name)
+ unlink(h->h_iop->io_name);
+ }
+ inhere = NULL;
+}
+
+/* unlink here temp files before a freearea(area) */
+static void freehere(int area)
+{
+ struct here *h, *hl;
+
+ DBGPRINTF6(("FREEHERE: enter, area=%d\n", area));
+
+ hl = NULL;
+ for (h = acthere; h != NULL; h = h->h_next)
+ if (getarea((char *) h) >= area) {
+ if (h->h_iop->io_name != NULL)
+ unlink(h->h_iop->io_name);
+ if (hl == NULL)
+ acthere = h->h_next;
+ else
+ hl->h_next = h->h_next;
+ } else
+ hl = h;
+}
+
+
+/* -------- sh.c -------- */
+/*
+ * shell
+ */
+
+int msh_main(int argc, char **argv);
+int msh_main(int argc, char **argv)
+{
+ int f;
+ char *s;
+ int cflag;
+ char *name, **ap;
+ int (*iof) (struct ioarg *);
+
+#if ENABLE_FEATURE_EDITING
+ line_input_state = new_line_input_t(FOR_SHELL);
+#endif
+
+ DBGPRINTF(("MSH_MAIN: argc %d, environ %p\n", argc, environ));
+
+ initarea();
+ ap = environ;
+ if (ap != NULL) {
+ while (*ap)
+ assign(*ap++, !COPYV);
+ for (ap = environ; *ap;)
+ export(lookup(*ap++));
+ }
+ closeall();
+ areanum = 1;
+
+ shell = lookup("SHELL");
+ if (shell->value == null)
+ setval(shell, (char *)DEFAULT_SHELL);
+ export(shell);
+
+ homedir = lookup("HOME");
+ if (homedir->value == null)
+ setval(homedir, "/");
+ export(homedir);
+
+ setval(lookup("$"), putn(getpid()));
+
+ path = lookup("PATH");
+ if (path->value == null) {
+ if (geteuid() == 0)
+ setval(path, "/sbin:/bin:/usr/sbin:/usr/bin");
+ else
+ setval(path, "/bin:/usr/bin");
+ }
+ export(path);
+
+ ifs = lookup("IFS");
+ if (ifs->value == null)
+ setval(ifs, " \t\n");
+
+#ifdef MSHDEBUG
+ mshdbg_var = lookup("MSHDEBUG");
+ if (mshdbg_var->value == null)
+ setval(mshdbg_var, "0");
+#endif
+
+ prompt = lookup("PS1");
+#if ENABLE_FEATURE_EDITING_FANCY_PROMPT
+ if (prompt->value == null)
+#endif
+ setval(prompt, DEFAULT_USER_PROMPT);
+ if (geteuid() == 0) {
+ setval(prompt, DEFAULT_ROOT_PROMPT);
+ prompt->status &= ~EXPORT;
+ }
+ cprompt = lookup("PS2");
+#if ENABLE_FEATURE_EDITING_FANCY_PROMPT
+ if (cprompt->value == null)
+#endif
+ setval(cprompt, "> ");
+
+ iof = filechar;
+ cflag = 0;
+ name = *argv++;
+ if (--argc >= 1) {
+ if (argv[0][0] == '-' && argv[0][1] != '\0') {
+ for (s = argv[0] + 1; *s; s++)
+ switch (*s) {
+ case 'c':
+ prompt->status &= ~EXPORT;
+ cprompt->status &= ~EXPORT;
+ setval(prompt, "");
+ setval(cprompt, "");
+ cflag = 1;
+ if (--argc > 0)
+ PUSHIO(aword, *++argv, iof = nlchar);
+ break;
+
+ case 'q':
+ qflag = SIG_DFL;
+ break;
+
+ case 's':
+ /* standard input */
+ break;
+
+ case 't':
+ prompt->status &= ~EXPORT;
+ setval(prompt, "");
+ iof = linechar;
+ break;
+
+ case 'i':
+ interactive++;
+ default:
+ if (*s >= 'a' && *s <= 'z')
+ flag[(int) *s]++;
+ }
+ } else {
+ argv--;
+ argc++;
+ }
+
+ if (iof == filechar && --argc > 0) {
+ setval(prompt, "");
+ setval(cprompt, "");
+ prompt->status &= ~EXPORT;
+ cprompt->status &= ~EXPORT;
+
+/* Shell is non-interactive, activate printf-based debug */
+#ifdef MSHDEBUG
+ mshdbg = (int) (((char) (mshdbg_var->value[0])) - '0');
+ if (mshdbg < 0)
+ mshdbg = 0;
+#endif
+ DBGPRINTF(("MSH_MAIN: calling newfile()\n"));
+
+ name = *++argv;
+ if (newfile(name))
+ exit(1); /* Exit on error */
+ }
+ }
+
+ setdash();
+
+ /* This won't be true if PUSHIO has been called, say from newfile() above */
+ if (e.iop < iostack) {
+ PUSHIO(afile, 0, iof);
+ if (isatty(0) && isatty(1) && !cflag) {
+ interactive++;
+#if !ENABLE_FEATURE_SH_EXTRA_QUIET
+#ifdef MSHDEBUG
+ printf("\n\n%s Built-in shell (msh with debug)\n", BB_BANNER);
+#else
+ printf("\n\n%s Built-in shell (msh)\n", BB_BANNER);
+#endif
+ printf("Enter 'help' for a list of built-in commands.\n\n");
+#endif
+ }
+ }
+
+ signal(SIGQUIT, qflag);
+ if (name && name[0] == '-') {
+ interactive++;
+ f = open(".profile", 0);
+ if (f >= 0)
+ next(remap(f));
+ f = open("/etc/profile", 0);
+ if (f >= 0)
+ next(remap(f));
+ }
+ if (interactive)
+ signal(SIGTERM, sig);
+
+ if (signal(SIGINT, SIG_IGN) != SIG_IGN)
+ signal(SIGINT, onintr);
+ dolv = argv;
+ dolc = argc;
+ dolv[0] = name;
+ if (dolc > 1) {
+ for (ap = ++argv; --argc > 0;) {
+ *ap = *argv++;
+ if (assign(*ap, !COPYV)) {
+ dolc--; /* keyword */
+ } else {
+ ap++;
+ }
+ }
+ }
+ setval(lookup("#"), putn((--dolc < 0) ? (dolc = 0) : dolc));
+
+ DBGPRINTF(("MSH_MAIN: begin FOR loop, interactive %d, e.iop %p, iostack %p\n", interactive, e.iop, iostack));
+
+ for (;;) {
+ if (interactive && e.iop <= iostack) {
+#if ENABLE_FEATURE_EDITING
+ current_prompt = prompt->value;
+#else
+ prs(prompt->value);
+#endif
+ }
+ onecommand();
+ /* Ensure that getenv("PATH") stays current */
+ setenv("PATH", path->value, 1);
+ }
+
+ DBGPRINTF(("MSH_MAIN: returning.\n"));
+}
+
+
+/*
+ * Copyright (c) 1987,1997, Prentice Hall
+ * All rights reserved.
+ *
+ * Redistribution and use of the MINIX operating system in source and
+ * binary forms, with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * Neither the name of Prentice Hall nor the names of the software
+ * authors or contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS, AUTHORS, AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL PRENTICE HALL OR ANY AUTHORS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
diff --git a/i/pc104/initrd/conf/busybox/sysklogd/Config.in b/i/pc104/initrd/conf/busybox/sysklogd/Config.in
new file mode 100644
index 0000000..9875b3c
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/sysklogd/Config.in
@@ -0,0 +1,111 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+menu "System Logging Utilities"
+
+config SYSLOGD
+ bool "syslogd"
+ default n
+ help
+ The syslogd utility is used to record logs of all the
+ significant events that occur on a system. Every
+ message that is logged records the date and time of the
+ event, and will generally also record the name of the
+ application that generated the message. When used in
+ conjunction with klogd, messages from the Linux kernel
+ can also be recorded. This is terribly useful,
+ especially for finding what happened when something goes
+ wrong. And something almost always will go wrong if
+ you wait long enough....
+
+config FEATURE_ROTATE_LOGFILE
+ bool "Rotate message files"
+ default n
+ depends on SYSLOGD
+ help
+ This enables syslogd to rotate the message files
+ on his own. No need to use an external rotatescript.
+
+config FEATURE_REMOTE_LOG
+ bool "Remote Log support"
+ default n
+ depends on SYSLOGD
+ help
+ When you enable this feature, the syslogd utility can
+ be used to send system log messages to another system
+ connected via a network. This allows the remote
+ machine to log all the system messages, which can be
+ terribly useful for reducing the number of serial
+ cables you use. It can also be a very good security
+ measure to prevent system logs from being tampered with
+ by an intruder.
+
+config FEATURE_IPC_SYSLOG
+ bool "Circular Buffer support"
+ default n
+ depends on SYSLOGD
+ help
+ When you enable this feature, the syslogd utility will
+ use a circular buffer to record system log messages.
+ When the buffer is filled it will continue to overwrite
+ the oldest messages. This can be very useful for
+ systems with little or no permanent storage, since
+ otherwise system logs can eventually fill up your
+ entire filesystem, which may cause your system to
+ break badly.
+
+config FEATURE_IPC_SYSLOG_BUFFER_SIZE
+ int " Circular buffer size in Kbytes (minimum 4KB)"
+ default 16
+ depends on FEATURE_IPC_SYSLOG
+ help
+ This option sets the size of the circular buffer
+ used to record system log messages.
+
+config LOGREAD
+ bool "logread"
+ default y
+ depends on FEATURE_IPC_SYSLOG
+ help
+ If you enabled Circular Buffer support, you almost
+ certainly want to enable this feature as well. This
+ utility will allow you to read the messages that are
+ stored in the syslogd circular buffer.
+
+config FEATURE_LOGREAD_REDUCED_LOCKING
+ bool "logread double buffering"
+ default n
+ depends on LOGREAD
+ help
+ 'logread' ouput to slow serial terminals can have
+ side effects on syslog because of the semaphore.
+ This option make logread to double buffer copy
+ from circular buffer, minimizing semaphore
+ contention at some minor memory expense.
+
+config KLOGD
+ bool "klogd"
+ default n
+ depends on SYSLOGD
+ select FEATURE_SYSLOG
+ help
+ klogd is a utility which intercepts and logs all
+ messages from the Linux kernel and sends the messages
+ out to the 'syslogd' utility so they can be logged. If
+ you wish to record the messages produced by the kernel,
+ you should enable this option.
+
+config LOGGER
+ bool "logger"
+ default n
+ select FEATURE_SYSLOG
+ help
+ The logger utility allows you to send arbitrary text
+ messages to the system log (i.e. the 'syslogd' utility) so
+ they can be logged. This is generally used to help locate
+ problems that occur within programs and scripts.
+
+endmenu
+
diff --git a/i/pc104/initrd/conf/busybox/sysklogd/Kbuild b/i/pc104/initrd/conf/busybox/sysklogd/Kbuild
new file mode 100644
index 0000000..0d5b2b9
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/sysklogd/Kbuild
@@ -0,0 +1,11 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under the GPL v2, see the file LICENSE in this tarball.
+
+lib-y:=
+lib-$(CONFIG_KLOGD) += klogd.o
+lib-$(CONFIG_LOGGER) += logger.o
+lib-$(CONFIG_LOGREAD) += logread.o
+lib-$(CONFIG_SYSLOGD) += syslogd.o
diff --git a/i/pc104/initrd/conf/busybox/sysklogd/klogd.c b/i/pc104/initrd/conf/busybox/sysklogd/klogd.c
new file mode 100644
index 0000000..cca6f56
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/sysklogd/klogd.c
@@ -0,0 +1,123 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini klogd implementation for busybox
+ *
+ * Copyright (C) 2001 by Gennady Feldman <gfeldman@gena01.com>.
+ * Changes: Made this a standalone busybox module which uses standalone
+ * syslog() client interface.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Copyright (C) 2000 by Karl M. Hegbloom <karlheg@debian.org>
+ *
+ * "circular buffer" Copyright (C) 2000 by Gennady Feldman <gfeldman@gena01.com>
+ *
+ * Maintainer: Gennady Feldman <gfeldman@gena01.com> as of Mar 12, 2001
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ */
+
+#include "busybox.h"
+#include <sys/syslog.h>
+#include <sys/klog.h>
+
+static void klogd_signal(int sig ATTRIBUTE_UNUSED)
+{
+ klogctl(7, NULL, 0);
+ klogctl(0, 0, 0);
+ syslog(LOG_NOTICE, "Kernel log daemon exiting");
+ exit(EXIT_SUCCESS);
+}
+
+#define OPT_LEVEL 1
+#define OPT_FOREGROUND 2
+
+#define KLOGD_LOGBUF_SIZE BUFSIZ
+#define log_buffer bb_common_bufsiz1
+
+int klogd_main(int argc, char **argv);
+int klogd_main(int argc, char **argv)
+{
+ int i = i; /* silence gcc */
+ char *start;
+
+ /* do normal option parsing */
+ getopt32(argc, argv, "c:n", &start);
+
+ if (option_mask32 & OPT_LEVEL) {
+ /* Valid levels are between 1 and 8 */
+ i = xatoul_range(start, 1, 8);
+ }
+
+ if (!(option_mask32 & OPT_FOREGROUND)) {
+#ifdef BB_NOMMU
+ vfork_daemon_rexec(0, 1, argc, argv, "-n");
+#else
+ bb_daemonize();
+#endif
+ }
+
+ openlog("kernel", 0, LOG_KERN);
+
+ /* Set up sig handlers */
+ signal(SIGINT, klogd_signal);
+ signal(SIGKILL, klogd_signal);
+ signal(SIGTERM, klogd_signal);
+ signal(SIGHUP, SIG_IGN);
+
+ /* "Open the log. Currently a NOP." */
+ klogctl(1, NULL, 0);
+
+ /* Set level of kernel console messaging. */
+ if (option_mask32 & OPT_LEVEL)
+ klogctl(8, NULL, i);
+
+ syslog(LOG_NOTICE, "klogd started: %s", BB_BANNER);
+
+ /* Note: this code does not detect incomplete messages
+ * (messages not ending with '\n' or just when kernel
+ * generates too many messages for us to keep up)
+ * and will split them in two separate lines */
+ while (1) {
+ int n;
+ int priority;
+
+ n = klogctl(2, log_buffer, KLOGD_LOGBUF_SIZE - 1);
+ if (n < 0) {
+ if (errno == EINTR)
+ continue;
+ syslog(LOG_ERR, "klogd: error from klogctl(2): %d - %m",
+ errno);
+ break;
+ }
+ log_buffer[n] = '\n';
+ i = 0;
+ while (i < n) {
+ priority = LOG_INFO;
+ start = &log_buffer[i];
+ if (log_buffer[i] == '<') {
+ i++;
+ // kernel never ganerates multi-digit prios
+ //priority = 0;
+ //while (log_buffer[i] >= '0' && log_buffer[i] <= '9') {
+ // priority = priority * 10 + (log_buffer[i] - '0');
+ // i++;
+ //}
+ if (isdigit(log_buffer[i])) {
+ priority = (log_buffer[i] - '0');
+ i++;
+ }
+ if (log_buffer[i] == '>')
+ i++;
+ start = &log_buffer[i];
+ }
+ while (log_buffer[i] != '\n')
+ i++;
+ log_buffer[i] = '\0';
+ syslog(priority, "%s", start);
+ i++;
+ }
+ }
+
+ return EXIT_FAILURE;
+}
diff --git a/i/pc104/initrd/conf/busybox/sysklogd/logger.c b/i/pc104/initrd/conf/busybox/sysklogd/logger.c
new file mode 100644
index 0000000..71dd531
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/sysklogd/logger.c
@@ -0,0 +1,168 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini logger implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "busybox.h"
+
+#if !defined CONFIG_SYSLOGD
+
+#define SYSLOG_NAMES
+#include <sys/syslog.h>
+
+#else
+#include <sys/syslog.h>
+# ifndef __dietlibc__
+ /* We have to do this since the header file defines static
+ * structures. Argh.... bad libc, bad, bad...
+ */
+ typedef struct _code {
+ char *c_name;
+ int c_val;
+ } CODE;
+ extern CODE prioritynames[];
+ extern CODE facilitynames[];
+# endif
+#endif
+
+/* Decode a symbolic name to a numeric value
+ * this function is based on code
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Original copyright notice is retained at the end of this file.
+ */
+static int decode(char *name, CODE * codetab)
+{
+ CODE *c;
+
+ if (isdigit(*name))
+ return atoi(name);
+ for (c = codetab; c->c_name; c++) {
+ if (!strcasecmp(name, c->c_name)) {
+ return c->c_val;
+ }
+ }
+
+ return -1;
+}
+
+/* Decode a symbolic name to a numeric value
+ * this function is based on code
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Original copyright notice is retained at the end of this file.
+ */
+static int pencode(char *s)
+{
+ char *save;
+ int lev, fac = LOG_USER;
+
+ for (save = s; *s && *s != '.'; ++s)
+ ;
+ if (*s) {
+ *s = '\0';
+ fac = decode(save, facilitynames);
+ if (fac < 0)
+ bb_error_msg_and_die("unknown %s name: %s", "facility", save);
+ *s++ = '.';
+ } else {
+ s = save;
+ }
+ lev = decode(s, prioritynames);
+ if (lev < 0)
+ bb_error_msg_and_die("unknown %s name: %s", "priority", save);
+ return ((lev & LOG_PRIMASK) | (fac & LOG_FACMASK));
+}
+
+
+int logger_main(int argc, char **argv);
+int logger_main(int argc, char **argv)
+{
+ char *str_p, *str_t;
+ int i = 0;
+ char name[80];
+
+ /* Fill out the name string early (may be overwritten later) */
+ bb_getpwuid(name, geteuid(), sizeof(name));
+ str_t = name;
+
+ /* Parse any options */
+ getopt32(argc, argv, "p:st:", &str_p, &str_t);
+
+ if (option_mask32 & 0x2) /* -s */
+ i |= LOG_PERROR;
+ //if (option_mask32 & 0x4) /* -t */
+ openlog(str_t, i, 0);
+ i = LOG_USER | LOG_NOTICE;
+ if (option_mask32 & 0x1) /* -p */
+ i = pencode(str_p);
+
+ argc -= optind;
+ argv += optind;
+ if (!argc) {
+ while (fgets(bb_common_bufsiz1, BUFSIZ, stdin)) {
+ if (bb_common_bufsiz1[0]
+ && NOT_LONE_CHAR(bb_common_bufsiz1, '\n')
+ ) {
+ /* Neither "" nor "\n" */
+ syslog(i, "%s", bb_common_bufsiz1);
+ }
+ }
+ } else {
+ char *message = NULL;
+ int len = 1; /* for NUL */
+ int pos = 0;
+ do {
+ len += strlen(*argv) + 1;
+ message = xrealloc(message, len);
+ sprintf(message + pos, " %s", *argv),
+ pos = len;
+ } while (*++argv);
+ syslog(i, "%s", message + 1); /* skip leading " " */
+ }
+
+ closelog();
+ return EXIT_SUCCESS;
+}
+
+
+/*-
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This is the original license statement for the decode and pencode functions.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change
+ * ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>
+ *
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
diff --git a/i/pc104/initrd/conf/busybox/sysklogd/logread.c b/i/pc104/initrd/conf/busybox/sysklogd/logread.c
new file mode 100644
index 0000000..b1758a6
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/sysklogd/logread.c
@@ -0,0 +1,144 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * circular buffer syslog implementation for busybox
+ *
+ * Copyright (C) 2000 by Gennady Feldman <gfeldman@gena01.com>
+ *
+ * Maintainer: Gennady Feldman <gfeldman@gena01.com> as of Mar 12, 2001
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "busybox.h"
+#include <sys/ipc.h>
+#include <sys/sem.h>
+#include <sys/shm.h>
+
+#define DEBUG 0
+
+enum { KEY_ID = 0x414e4547 }; /* "GENA" */
+
+static struct shbuf_ds {
+ int32_t size; // size of data written
+ int32_t head; // start of message list
+ int32_t tail; // end of message list
+ char data[1]; // data/messages
+} *buf; // shared memory pointer
+
+// Semaphore operation structures
+static struct sembuf SMrup[1] = {{0, -1, IPC_NOWAIT | SEM_UNDO}}; // set SMrup
+static struct sembuf SMrdn[2] = {{1, 0}, {0, +1, SEM_UNDO}}; // set SMrdn
+
+
+static void error_exit(const char *str) ATTRIBUTE_NORETURN;
+static void error_exit(const char *str)
+{
+ //release all acquired resources
+ shmdt(buf);
+ bb_perror_msg_and_die(str);
+}
+
+/*
+ * sem_up - up()'s a semaphore.
+ */
+static void sem_up(int semid)
+{
+ if (semop(semid, SMrup, 1) == -1)
+ error_exit("semop[SMrup]");
+}
+
+static void interrupted(int sig ATTRIBUTE_UNUSED)
+{
+ signal(SIGINT, SIG_IGN);
+ shmdt(buf);
+ exit(0);
+}
+
+int logread_main(int argc, char **argv);
+int logread_main(int argc, char **argv)
+{
+ int cur;
+ int log_semid; /* ipc semaphore id */
+ int log_shmid; /* ipc shared memory id */
+ smallint follow = getopt32(argc, argv, "f");
+
+ log_shmid = shmget(KEY_ID, 0, 0);
+ if (log_shmid == -1)
+ bb_perror_msg_and_die("can't find syslogd buffer");
+
+ // Attach shared memory to our char*
+ buf = shmat(log_shmid, NULL, SHM_RDONLY);
+ if (buf == NULL)
+ bb_perror_msg_and_die("can't access syslogd buffer");
+
+ log_semid = semget(KEY_ID, 0, 0);
+ if (log_semid == -1)
+ error_exit("can't get access to semaphores for syslogd buffer");
+
+ // attempt to redefine ^C signal
+ signal(SIGINT, interrupted);
+
+ // Suppose atomic memory move
+ cur = follow ? buf->tail : buf->head;
+
+ do {
+#if ENABLE_FEATURE_LOGREAD_REDUCED_LOCKING
+ char *buf_data;
+ int log_len, j;
+#endif
+ if (semop(log_semid, SMrdn, 2) == -1)
+ error_exit("semop[SMrdn]");
+
+ if (DEBUG)
+ printf("head:%i cur:%d tail:%i size:%i\n",
+ buf->head, cur, buf->tail, buf->size);
+
+ if (buf->head == buf->tail || cur == buf->tail) {
+ if (follow) {
+ sem_up(log_semid);
+ fflush(stdout);
+ sleep(1); /* TODO: replace me with a sleep_on */
+ continue;
+ }
+ puts("<empty syslog>");
+ }
+
+ // Read Memory
+#if ENABLE_FEATURE_LOGREAD_REDUCED_LOCKING
+ log_len = buf->tail - cur;
+ if (log_len < 0)
+ log_len += buf->size;
+ buf_data = xmalloc(log_len);
+
+ if (buf->tail >= cur)
+ j = log_len;
+ else
+ j = buf->size - cur;
+ memcpy(buf_data, buf->data + cur, j);
+
+ if (buf->tail < cur)
+ memcpy(buf_data + buf->size - cur, buf->data, buf->tail);
+ cur = buf->tail;
+#else
+ while (cur != buf->tail) {
+ fputs(buf->data + cur, stdout);
+ cur += strlen(buf->data + cur) + 1;
+ if (cur >= buf->size)
+ cur = 0;
+ }
+#endif
+ // release the lock on the log chain
+ sem_up(log_semid);
+
+#if ENABLE_FEATURE_LOGREAD_REDUCED_LOCKING
+ for (j = 0; j < log_len; j += strlen(buf_data+j) + 1) {
+ fputs(buf_data + j, stdout);
+ }
+ free(buf_data);
+#endif
+ } while (follow);
+
+ shmdt(buf);
+
+ fflush_stdout_and_exit(EXIT_SUCCESS);
+}
diff --git a/i/pc104/initrd/conf/busybox/sysklogd/syslogd.c b/i/pc104/initrd/conf/busybox/sysklogd/syslogd.c
new file mode 100644
index 0000000..2a2b20c
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/sysklogd/syslogd.c
@@ -0,0 +1,654 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini syslogd implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Copyright (C) 2000 by Karl M. Hegbloom <karlheg@debian.org>
+ *
+ * "circular buffer" Copyright (C) 2001 by Gennady Feldman <gfeldman@gena01.com>
+ *
+ * Maintainer: Gennady Feldman <gfeldman@gena01.com> as of Mar 12, 2001
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ */
+
+#include "busybox.h"
+#include <paths.h>
+#include <sys/un.h>
+
+/* SYSLOG_NAMES defined to pull some extra junk from syslog.h: */
+/* prioritynames[] and facilitynames[]. uclibc pulls those in _rwdata_! :( */
+
+#define SYSLOG_NAMES
+#include <sys/syslog.h>
+#include <sys/uio.h>
+
+#if ENABLE_FEATURE_REMOTE_LOG
+#include <netinet/in.h>
+#endif
+
+#if ENABLE_FEATURE_IPC_SYSLOG
+#include <sys/ipc.h>
+#include <sys/sem.h>
+#include <sys/shm.h>
+#endif
+
+
+#define DEBUG 0
+
+/* MARK code is not very useful, is bloat, and broken:
+ * can deadlock if alarmed to make MARK while writing to IPC buffer
+ * (semaphores are down but do_mark routine tries to down them again) */
+#undef SYSLOGD_MARK
+
+enum { MAX_READ = 256 };
+
+/* Semaphore operation structures */
+struct shbuf_ds {
+ int32_t size; /* size of data written */
+ int32_t head; /* start of message list */
+ int32_t tail; /* end of message list */
+ char data[1]; /* data/messages */
+};
+
+/* Allows us to have smaller initializer. Ugly. */
+#define GLOBALS \
+ const char *logFilePath; \
+ int logFD; \
+ /* interval between marks in seconds */ \
+ /*int markInterval;*/ \
+ /* level of messages to be logged */ \
+ int logLevel; \
+USE_FEATURE_ROTATE_LOGFILE( \
+ /* max size of file before rotation */ \
+ unsigned logFileSize; \
+ /* number of rotated message files */ \
+ unsigned logFileRotate; \
+ unsigned curFileSize; \
+ smallint isRegular; \
+) \
+USE_FEATURE_REMOTE_LOG( \
+ /* udp socket for remote logging */ \
+ int remoteFD; \
+ len_and_sockaddr* remoteAddr; \
+) \
+USE_FEATURE_IPC_SYSLOG( \
+ int shmid; /* ipc shared memory id */ \
+ int s_semid; /* ipc semaphore id */ \
+ int shm_size; \
+ struct sembuf SMwup[1]; \
+ struct sembuf SMwdn[3]; \
+)
+
+struct init_globals {
+ GLOBALS
+};
+
+struct globals {
+ GLOBALS
+#if ENABLE_FEATURE_IPC_SYSLOG
+ struct shbuf_ds *shbuf;
+#endif
+ time_t last_log_time;
+ /* localhost's name */
+ char localHostName[64];
+
+ /* We recv into recvbuf... */
+ char recvbuf[MAX_READ];
+ /* ...then copy to parsebuf, escaping control chars */
+ /* (can grow x2 max) */
+ char parsebuf[MAX_READ*2];
+ /* ...then sprintf into printbuf, adding timestamp (15 chars),
+ * host (64), fac.prio (20) to the message */
+ /* (growth by: 15 + 64 + 20 + delims = ~110) */
+ char printbuf[MAX_READ*2 + 128];
+};
+
+static const struct init_globals init_data = {
+ .logFilePath = "/var/log/messages",
+ .logFD = -1,
+#ifdef SYSLOGD_MARK
+ .markInterval = 20 * 60,
+#endif
+ .logLevel = 8,
+#if ENABLE_FEATURE_ROTATE_LOGFILE
+ .logFileSize = 200 * 1024,
+ .logFileRotate = 1,
+#endif
+#if ENABLE_FEATURE_REMOTE_LOG
+ .remoteFD = -1,
+#endif
+#if ENABLE_FEATURE_IPC_SYSLOG
+ .shmid = -1,
+ .s_semid = -1,
+ .shm_size = ((CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE)*1024), // default shm size
+ .SMwup = { {1, -1, IPC_NOWAIT} },
+ .SMwdn = { {0, 0}, {1, 0}, {1, +1} },
+#endif
+};
+
+#define G (*ptr_to_globals)
+
+
+/* Options */
+enum {
+ OPTBIT_mark = 0, // -m
+ OPTBIT_nofork, // -n
+ OPTBIT_outfile, // -O
+ OPTBIT_loglevel, // -l
+ OPTBIT_small, // -S
+ USE_FEATURE_ROTATE_LOGFILE(OPTBIT_filesize ,) // -s
+ USE_FEATURE_ROTATE_LOGFILE(OPTBIT_rotatecnt ,) // -b
+ USE_FEATURE_REMOTE_LOG( OPTBIT_remote ,) // -R
+ USE_FEATURE_REMOTE_LOG( OPTBIT_localtoo ,) // -L
+ USE_FEATURE_IPC_SYSLOG( OPTBIT_circularlog,) // -C
+
+ OPT_mark = 1 << OPTBIT_mark ,
+ OPT_nofork = 1 << OPTBIT_nofork ,
+ OPT_outfile = 1 << OPTBIT_outfile ,
+ OPT_loglevel = 1 << OPTBIT_loglevel,
+ OPT_small = 1 << OPTBIT_small ,
+ OPT_filesize = USE_FEATURE_ROTATE_LOGFILE((1 << OPTBIT_filesize )) + 0,
+ OPT_rotatecnt = USE_FEATURE_ROTATE_LOGFILE((1 << OPTBIT_rotatecnt )) + 0,
+ OPT_remotelog = USE_FEATURE_REMOTE_LOG( (1 << OPTBIT_remote )) + 0,
+ OPT_locallog = USE_FEATURE_REMOTE_LOG( (1 << OPTBIT_localtoo )) + 0,
+ OPT_circularlog = USE_FEATURE_IPC_SYSLOG( (1 << OPTBIT_circularlog)) + 0,
+};
+#define OPTION_STR "m:nO:l:S" \
+ USE_FEATURE_ROTATE_LOGFILE("s:" ) \
+ USE_FEATURE_ROTATE_LOGFILE("b:" ) \
+ USE_FEATURE_REMOTE_LOG( "R:" ) \
+ USE_FEATURE_REMOTE_LOG( "L" ) \
+ USE_FEATURE_IPC_SYSLOG( "C::")
+#define OPTION_DECL *opt_m, *opt_l \
+ USE_FEATURE_ROTATE_LOGFILE(,*opt_s) \
+ USE_FEATURE_ROTATE_LOGFILE(,*opt_b) \
+ USE_FEATURE_REMOTE_LOG( ,*opt_R) \
+ USE_FEATURE_IPC_SYSLOG( ,*opt_C = NULL)
+#define OPTION_PARAM &opt_m, &G.logFilePath, &opt_l \
+ USE_FEATURE_ROTATE_LOGFILE(,&opt_s) \
+ USE_FEATURE_ROTATE_LOGFILE(,&opt_b) \
+ USE_FEATURE_REMOTE_LOG( ,&opt_R) \
+ USE_FEATURE_IPC_SYSLOG( ,&opt_C)
+
+
+/* circular buffer variables/structures */
+#if ENABLE_FEATURE_IPC_SYSLOG
+
+#if CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE < 4
+#error Sorry, you must set the syslogd buffer size to at least 4KB.
+#error Please check CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE
+#endif
+
+/* our shared key */
+#define KEY_ID ((long)0x414e4547) /* "GENA" */
+
+static void ipcsyslog_cleanup(void)
+{
+ if (G.shmid != -1) {
+ shmdt(G.shbuf);
+ }
+ if (G.shmid != -1) {
+ shmctl(G.shmid, IPC_RMID, NULL);
+ }
+ if (G.s_semid != -1) {
+ semctl(G.s_semid, 0, IPC_RMID, 0);
+ }
+}
+
+static void ipcsyslog_init(void)
+{
+ if (DEBUG)
+ printf("shmget(%lx, %d,...)\n", KEY_ID, G.shm_size);
+
+ G.shmid = shmget(KEY_ID, G.shm_size, IPC_CREAT | 1023);
+ if (G.shmid == -1) {
+ bb_perror_msg_and_die("shmget");
+ }
+
+ G.shbuf = shmat(G.shmid, NULL, 0);
+ if (!G.shbuf) {
+ bb_perror_msg_and_die("shmat");
+ }
+
+ G.shbuf->size = G.shm_size - offsetof(struct shbuf_ds, data);
+ G.shbuf->head = G.shbuf->tail = 0;
+
+ // we'll trust the OS to set initial semval to 0 (let's hope)
+ G.s_semid = semget(KEY_ID, 2, IPC_CREAT | IPC_EXCL | 1023);
+ if (G.s_semid == -1) {
+ if (errno == EEXIST) {
+ G.s_semid = semget(KEY_ID, 2, 0);
+ if (G.s_semid != -1)
+ return;
+ }
+ bb_perror_msg_and_die("semget");
+ }
+}
+
+/* Write message to shared mem buffer */
+static void log_to_shmem(const char *msg, int len)
+{
+ int old_tail, new_tail;
+ char *c;
+
+ if (semop(G.s_semid, G.SMwdn, 3) == -1) {
+ bb_perror_msg_and_die("SMwdn");
+ }
+
+ /* Circular Buffer Algorithm:
+ * --------------------------
+ * tail == position where to store next syslog message.
+ * head == position of next message to retrieve ("print").
+ * if head == tail, there is no "unprinted" messages left.
+ * head is typically advanced by separate "reader" program,
+ * but if there isn't one, we have to do it ourself.
+ * messages are NUL-separated.
+ */
+ len++; /* length with NUL included */
+ again:
+ old_tail = G.shbuf->tail;
+ new_tail = old_tail + len;
+ if (new_tail < G.shbuf->size) {
+ /* No need to move head if shbuf->head <= old_tail,
+ * else... */
+ if (old_tail < G.shbuf->head && G.shbuf->head <= new_tail) {
+ /* ...need to move head forward */
+ c = memchr(G.shbuf->data + new_tail, '\0',
+ G.shbuf->size - new_tail);
+ if (!c) /* no NUL ahead of us, wrap around */
+ c = memchr(G.shbuf->data, '\0', old_tail);
+ if (!c) { /* still nothing? point to this msg... */
+ G.shbuf->head = old_tail;
+ } else {
+ /* convert pointer to offset + skip NUL */
+ G.shbuf->head = c - G.shbuf->data + 1;
+ }
+ }
+ /* store message, set new tail */
+ memcpy(G.shbuf->data + old_tail, msg, len);
+ G.shbuf->tail = new_tail;
+ } else {
+ /* we need to break up the message and wrap it around */
+ /* k == available buffer space ahead of old tail */
+ int k = G.shbuf->size - old_tail - 1;
+ if (G.shbuf->head > old_tail) {
+ /* we are going to overwrite head, need to
+ * move it out of the way */
+ c = memchr(G.shbuf->data, '\0', old_tail);
+ if (!c) { /* nothing? point to this msg... */
+ G.shbuf->head = old_tail;
+ } else { /* convert pointer to offset + skip NUL */
+ G.shbuf->head = c - G.shbuf->data + 1;
+ }
+ }
+ /* copy what fits to the end of buffer, and repeat */
+ memcpy(G.shbuf->data + old_tail, msg, k);
+ msg += k;
+ len -= k;
+ G.shbuf->tail = 0;
+ goto again;
+ }
+ if (semop(G.s_semid, G.SMwup, 1) == -1) {
+ bb_perror_msg_and_die("SMwup");
+ }
+ if (DEBUG)
+ printf("head:%d tail:%d\n", G.shbuf->head, G.shbuf->tail);
+}
+#else
+void ipcsyslog_cleanup(void);
+void ipcsyslog_init(void);
+void log_to_shmem(const char *msg);
+#endif /* FEATURE_IPC_SYSLOG */
+
+
+/* Print a message to the log file. */
+static void log_locally(char *msg)
+{
+ struct flock fl;
+ int len = strlen(msg);
+
+#if ENABLE_FEATURE_IPC_SYSLOG
+ if ((option_mask32 & OPT_circularlog) && G.shbuf) {
+ log_to_shmem(msg, len);
+ return;
+ }
+#endif
+ if (G.logFD >= 0) {
+ time_t cur;
+ time(&cur);
+ if (G.last_log_time != cur) {
+ G.last_log_time = cur; /* reopen log file every second */
+ close(G.logFD);
+ goto reopen;
+ }
+ } else {
+ reopen:
+ G.logFD = device_open(G.logFilePath, O_WRONLY | O_CREAT
+ | O_NOCTTY | O_APPEND | O_NONBLOCK);
+ if (G.logFD < 0) {
+ /* cannot open logfile? - print to /dev/console then */
+ int fd = device_open(_PATH_CONSOLE, O_WRONLY | O_NOCTTY | O_NONBLOCK);
+ if (fd < 0)
+ fd = 2; /* then stderr, dammit */
+ full_write(fd, msg, len);
+ if (fd != 2)
+ close(fd);
+ return;
+ }
+#if ENABLE_FEATURE_ROTATE_LOGFILE
+ {
+ struct stat statf;
+
+ G.isRegular = (fstat(G.logFD, &statf) == 0 && (statf.st_mode & S_IFREG));
+ /* bug (mostly harmless): can wrap around if file > 4gb */
+ G.curFileSize = statf.st_size;
+ }
+#endif
+ }
+
+ fl.l_whence = SEEK_SET;
+ fl.l_start = 0;
+ fl.l_len = 1;
+ fl.l_type = F_WRLCK;
+ fcntl(G.logFD, F_SETLKW, &fl);
+
+#if ENABLE_FEATURE_ROTATE_LOGFILE
+ if (G.logFileSize && G.isRegular && G.curFileSize > G.logFileSize) {
+ if (G.logFileRotate) { /* always 0..99 */
+ int i = strlen(G.logFilePath) + 3 + 1;
+ char oldFile[i];
+ char newFile[i];
+ i = G.logFileRotate - 1;
+ /* rename: f.8 -> f.9; f.7 -> f.8; ... */
+ while (1) {
+ sprintf(newFile, "%s.%d", G.logFilePath, i);
+ if (i == 0) break;
+ sprintf(oldFile, "%s.%d", G.logFilePath, --i);
+ rename(oldFile, newFile);
+ }
+ /* newFile == "f.0" now */
+ rename(G.logFilePath, newFile);
+ fl.l_type = F_UNLCK;
+ fcntl(G.logFD, F_SETLKW, &fl);
+ close(G.logFD);
+ goto reopen;
+ }
+ ftruncate(G.logFD, 0);
+ }
+ G.curFileSize +=
+#endif
+ full_write(G.logFD, msg, len);
+ fl.l_type = F_UNLCK;
+ fcntl(G.logFD, F_SETLKW, &fl);
+}
+
+static void parse_fac_prio_20(int pri, char *res20)
+{
+ CODE *c_pri, *c_fac;
+
+ if (pri != 0) {
+ c_fac = facilitynames;
+ while (c_fac->c_name) {
+ if (c_fac->c_val != (LOG_FAC(pri) << 3)) {
+ c_fac++; continue;
+ }
+ /* facility is found, look for prio */
+ c_pri = prioritynames;
+ while (c_pri->c_name) {
+ if (c_pri->c_val != LOG_PRI(pri)) {
+ c_pri++; continue;
+ }
+ snprintf(res20, 20, "%s.%s",
+ c_fac->c_name, c_pri->c_name);
+ return;
+ }
+ /* prio not found, bail out */
+ break;
+ }
+ snprintf(res20, 20, "<%d>", pri);
+ }
+}
+
+/* len parameter is used only for "is there a timestamp?" check.
+ * NB: some callers cheat and supply 0 when they know
+ * that there is no timestamp, short-cutting the test. */
+static void timestamp_and_log(int pri, char *msg, int len)
+{
+ char *timestamp;
+
+ if (len < 16 || msg[3] != ' ' || msg[6] != ' '
+ || msg[9] != ':' || msg[12] != ':' || msg[15] != ' '
+ ) {
+ time_t now;
+ time(&now);
+ timestamp = ctime(&now) + 4;
+ } else {
+ timestamp = msg;
+ msg += 16;
+ }
+ timestamp[15] = '\0';
+
+ /* Log message locally (to file or shared mem) */
+ if (!ENABLE_FEATURE_REMOTE_LOG || (option_mask32 & OPT_locallog)) {
+ if (LOG_PRI(pri) < G.logLevel) {
+ if (option_mask32 & OPT_small)
+ sprintf(G.printbuf, "%s %s\n", timestamp, msg);
+ else {
+ char res[20];
+ parse_fac_prio_20(pri, res);
+ sprintf(G.printbuf, "%s %s %s %s\n", timestamp, G.localHostName, res, msg);
+ }
+ log_locally(G.printbuf);
+ }
+ }
+}
+
+static void split_escape_and_log(char *tmpbuf, int len)
+{
+ char *p = tmpbuf;
+
+ tmpbuf += len;
+ while (p < tmpbuf) {
+ char c;
+ char *q = G.parsebuf;
+ int pri = (LOG_USER | LOG_NOTICE);
+
+ if (*p == '<') {
+ /* Parse the magic priority number */
+ pri = bb_strtou(p + 1, &p, 10);
+ if (*p == '>') p++;
+ if (pri & ~(LOG_FACMASK | LOG_PRIMASK)) {
+ pri = (LOG_USER | LOG_NOTICE);
+ }
+ }
+
+ while ((c = *p++)) {
+ if (c == '\n')
+ c = ' ';
+ if (!(c & ~0x1f)) {
+ *q++ = '^';
+ c += '@'; /* ^@, ^A, ^B... */
+ }
+ *q++ = c;
+ }
+ *q = '\0';
+ /* Now log it */
+ timestamp_and_log(pri, G.parsebuf, q - G.parsebuf);
+ }
+}
+
+static void quit_signal(int sig)
+{
+ timestamp_and_log(LOG_SYSLOG | LOG_INFO, (char*)"syslogd exiting", 0);
+ puts("syslogd exiting");
+ if (ENABLE_FEATURE_IPC_SYSLOG)
+ ipcsyslog_cleanup();
+ exit(1);
+}
+
+#ifdef SYSLOGD_MARK
+static void do_mark(int sig)
+{
+ if (G.markInterval) {
+ timestamp_and_log(LOG_SYSLOG | LOG_INFO, (char*)"-- MARK --", 0);
+ alarm(G.markInterval);
+ }
+}
+#endif
+
+static void do_syslogd(void) ATTRIBUTE_NORETURN;
+static void do_syslogd(void)
+{
+ struct sockaddr_un sunx;
+ int sock_fd;
+ fd_set fds;
+ char *dev_log_name;
+
+ /* Set up signal handlers */
+ signal(SIGINT, quit_signal);
+ signal(SIGTERM, quit_signal);
+ signal(SIGQUIT, quit_signal);
+ signal(SIGHUP, SIG_IGN);
+ signal(SIGCHLD, SIG_IGN);
+#ifdef SIGCLD
+ signal(SIGCLD, SIG_IGN);
+#endif
+#ifdef SYSLOGD_MARK
+ signal(SIGALRM, do_mark);
+ alarm(G.markInterval);
+#endif
+
+ memset(&sunx, 0, sizeof(sunx));
+ sunx.sun_family = AF_UNIX;
+ strcpy(sunx.sun_path, "/dev/log");
+
+ /* Unlink old /dev/log or object it points to. */
+ /* (if it exists, bind will fail) */
+ logmode = LOGMODE_NONE;
+ dev_log_name = xmalloc_readlink_or_warn("/dev/log");
+ logmode = LOGMODE_STDIO;
+ if (dev_log_name) {
+ int fd = xopen(".", O_NONBLOCK);
+ xchdir("/dev");
+ /* we do not check whether this is a link also */
+ unlink(dev_log_name);
+ fchdir(fd);
+ close(fd);
+ safe_strncpy(sunx.sun_path, dev_log_name, sizeof(sunx.sun_path));
+ free(dev_log_name);
+ } else {
+ unlink("/dev/log");
+ }
+
+ sock_fd = xsocket(AF_UNIX, SOCK_DGRAM, 0);
+ xbind(sock_fd, (struct sockaddr *) &sunx, sizeof(sunx));
+
+ if (chmod("/dev/log", 0666) < 0) {
+ bb_perror_msg_and_die("cannot set permission on /dev/log");
+ }
+ if (ENABLE_FEATURE_IPC_SYSLOG && (option_mask32 & OPT_circularlog)) {
+ ipcsyslog_init();
+ }
+
+ timestamp_and_log(LOG_SYSLOG | LOG_INFO,
+ (char*)"syslogd started: BusyBox v" BB_VER, 0);
+
+ for (;;) {
+ FD_ZERO(&fds);
+ FD_SET(sock_fd, &fds);
+
+ if (select(sock_fd + 1, &fds, NULL, NULL, NULL) < 0) {
+ if (errno == EINTR) {
+ /* alarm may have happened */
+ continue;
+ }
+ bb_perror_msg_and_die("select");
+ }
+
+ if (FD_ISSET(sock_fd, &fds)) {
+ int i;
+ i = recv(sock_fd, G.recvbuf, MAX_READ - 1, 0);
+ if (i <= 0)
+ bb_perror_msg_and_die("UNIX socket error");
+ /* TODO: maybe suppress duplicates? */
+#if ENABLE_FEATURE_REMOTE_LOG
+ /* We are not modifying log messages in any way before send */
+ /* Remote site cannot trust _us_ anyway and need to do validation again */
+ if (G.remoteAddr) {
+ if (-1 == G.remoteFD) {
+ G.remoteFD = socket(G.remoteAddr->sa.sa_family, SOCK_DGRAM, 0);
+ }
+ if (-1 != G.remoteFD) {
+ /* send message to remote logger, ignore possible error */
+ sendto(G.remoteFD, G.recvbuf, i, MSG_DONTWAIT,
+ &G.remoteAddr->sa, G.remoteAddr->len);
+ }
+ }
+#endif
+ G.recvbuf[i] = '\0';
+ split_escape_and_log(G.recvbuf, i);
+ } /* FD_ISSET() */
+ } /* for */
+}
+
+int syslogd_main(int argc, char **argv);
+int syslogd_main(int argc, char **argv)
+{
+ char OPTION_DECL;
+ char *p;
+
+ PTR_TO_GLOBALS = memcpy(xzalloc(sizeof(G)), &init_data, sizeof(init_data));
+
+ /* do normal option parsing */
+ opt_complementary = "=0"; /* no non-option params */
+ getopt32(argc, argv, OPTION_STR, OPTION_PARAM);
+#ifdef SYSLOGD_MARK
+ if (option_mask32 & OPT_mark) // -m
+ G.markInterval = xatou_range(opt_m, 0, INT_MAX/60) * 60;
+#endif
+ //if (option_mask32 & OPT_nofork) // -n
+ //if (option_mask32 & OPT_outfile) // -O
+ if (option_mask32 & OPT_loglevel) // -l
+ G.logLevel = xatou_range(opt_l, 1, 8);
+ //if (option_mask32 & OPT_small) // -S
+#if ENABLE_FEATURE_ROTATE_LOGFILE
+ if (option_mask32 & OPT_filesize) // -s
+ G.logFileSize = xatou_range(opt_s, 0, INT_MAX/1024) * 1024;
+ if (option_mask32 & OPT_rotatecnt) // -b
+ G.logFileRotate = xatou_range(opt_b, 0, 99);
+#endif
+#if ENABLE_FEATURE_REMOTE_LOG
+ if (option_mask32 & OPT_remotelog) { // -R
+ G.remoteAddr = xhost2sockaddr(opt_R, 514);
+ }
+ //if (option_mask32 & OPT_locallog) // -L
+#endif
+#if ENABLE_FEATURE_IPC_SYSLOG
+ if (opt_C) // -Cn
+ G.shm_size = xatoul_range(opt_C, 4, INT_MAX/1024) * 1024;
+#endif
+
+ /* If they have not specified remote logging, then log locally */
+ if (ENABLE_FEATURE_REMOTE_LOG && !(option_mask32 & OPT_remotelog))
+ option_mask32 |= OPT_locallog;
+
+ /* Store away localhost's name before the fork */
+ gethostname(G.localHostName, sizeof(G.localHostName));
+ p = strchr(G.localHostName, '.');
+ if (p) {
+ *p = '\0';
+ }
+
+ if (!(option_mask32 & OPT_nofork)) {
+#ifdef BB_NOMMU
+ vfork_daemon_rexec(0, 1, argc, argv, "-n");
+#else
+ bb_daemonize();
+#endif
+ }
+ umask(0);
+ do_syslogd();
+ /* return EXIT_SUCCESS; */
+}
diff --git a/i/pc104/initrd/conf/busybox/testsuite/README b/i/pc104/initrd/conf/busybox/testsuite/README
new file mode 100644
index 0000000..377f20e
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/README
@@ -0,0 +1,34 @@
+Update: doesn't work as described. Try "make check" from parent dir...
+* * *
+
+To run the test suite, change to this directory and run "./runtest". It will
+run all of the test cases, and list those with unexpected outcomes. Adding the
+-v option will cause it to show expected outcomes as well. To only run the test
+cases for particular applets, specify them as parameters to runtest.
+
+The test cases for an applet reside in the subdirectory of the applet name. The
+name of the test case should be the assertion that is tested. The test case
+should be a shell fragment that returns successfully if the test case passes,
+and unsuccessfully otherwise.
+
+If the test case relies on a certain feature, it should include the string
+"FEATURE: " followed by the name of the feature in a comment. If it is always
+expected to fail, it should include the string "XFAIL" in a comment.
+
+For the entire testsuite, the copyright is as follows:
+
+Copyright (C) 2001, 2002 Matt Kraai
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
diff --git a/i/pc104/initrd/conf/busybox/testsuite/TODO b/i/pc104/initrd/conf/busybox/testsuite/TODO
new file mode 100644
index 0000000..b8957f4
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/TODO
@@ -0,0 +1,26 @@
+This testsuite is quite obviously a work in progress. As such,
+there are a number of good extensions. If you are looking for
+something to do, feel free to tackle one or more of the following:
+
+Moving to the new format.
+ The old way was "lots of little tests files in a directory", which
+ doesn't interact well with source control systems. The new test
+ format is command.tests files that use testing.sh.
+
+Every busybox applet needs a corresponding applet.tests.
+
+Full SUSv3 test suite.
+ Let's make the Linux Test Project jealous, shall we? Don't just
+ audit programs for standards compliance, _prove_ it with a regression
+ test harness.
+
+ http://www.opengroup.org/onlinepubs/009695399/utilities/
+
+Some tests need root access.
+ It's hard to test things like mount or init as a normal user.
+ Possibly User Mode Linux could be used for this, or perhaps
+ Erik's buildroot.
+
+libbb unit testing
+ Being able to test the functions of libbb individually may
+ help to prevent regressions.
diff --git a/i/pc104/initrd/conf/busybox/testsuite/all_sourcecode.tests b/i/pc104/initrd/conf/busybox/testsuite/all_sourcecode.tests
new file mode 100755
index 0000000..c115878
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/all_sourcecode.tests
@@ -0,0 +1,78 @@
+#!/bin/sh
+
+# Tests for the sourcecode base itself.
+# Copyright 2006 by Mike Frysinger <vapier@gentoo.org>
+# Licensed under GPL v2, see file LICENSE for details.
+
+[ -n "$srcdir" ] || srcdir=$(pwd)
+. testing.sh
+
+
+#
+# if we don't have the sourcecode available, let's just bail
+#
+[ -s "$srcdir/../Makefile" ] || exit 0
+[ -s "$srcdir/../include/applets.h" ] || exit 0
+
+
+#
+# verify the applet order is correct in applets.h, otherwise
+# applets won't be called properly.
+#
+sed -n -e '/^USE_[A-Z]*(APPLET(/{s:.*(::;s:,.*::;s:"::g;p}' \
+ $srcdir/../include/applets.h > applet.order.current
+LC_ALL=C sort applet.order.current > applet.order.correct
+testing "Applet order" "diff -u applet.order.current applet.order.correct" "" "" ""
+rm -f applet.order.current applet.order.correct
+
+
+#
+# check for misc common typos
+#
+find $srcdir/../ \
+ '(' -type d -a '(' -name .svn -o -name testsuite ')' -prune ')' \
+ -o '(' -type f -a -print0 ')' | xargs -0 \
+ grep -I \
+ -e '\<compatability\>' \
+ -e '\<compatable\>' \
+ -e '\<fordeground\>' \
+ -e '\<depency\>' -e '\<dependancy\>' -e '\<dependancies\>' \
+ -e '\<defalt\>' \
+ -e '\<remaing\>' \
+ -e '\<queueing\>' \
+ -e '\<detatch\>' \
+ -e '\<sempahore\>' \
+ -e '\<reprenstative\>' \
+ -e '\<overriden\>' \
+ -e '\<readed\>' \
+ -e '\<formated\>' \
+ -e '\<algorithic\>' \
+ -e '\<deamon\>' \
+ -e '\<derefernce\>' \
+ -e '\<acomadate\>' \
+ | sed -e "s:^$srcdir/\.\./::g" > src.typos
+testing "Common typos" "cat src.typos" "" "" ""
+rm -f src.typos
+
+
+#
+# don't allow obsolete functions
+#
+find $srcdir/.. '(' -name '*.c' -o -name '*.h' ')' -print0 | xargs -0 \
+ grep -E -e '\<(bcmp|bcopy|bzero|getwd|index|mktemp|rindex|utimes)\>[[:space:]]*\(' \
+ | sed -e "s:^$srcdir/\.\./::g" > src.obsolete.funcs
+testing "Obsolete function usage" "cat src.obsolete.funcs" "" "" ""
+rm -f src.obsolete.funcs
+
+
+#
+# don't allow obsolete headers
+#
+find $srcdir/.. '(' -name '*.c' -o -name '*.h' ')' -print0 | xargs -0 \
+ grep -E -e '\<(malloc|memory|sys/(errno|fcntl|signal|stropts|termios|unistd))\.h\>' \
+ | sed -e "s:^$srcdir/\.\./::g" > src.obsolete.headers
+testing "Obsolete headers" "cat src.obsolete.headers" "" "" ""
+rm -f src.obsolete.headers
+
+
+exit $FAILCOUNT
diff --git a/i/pc104/initrd/conf/busybox/testsuite/basename/basename-does-not-remove-identical-extension b/i/pc104/initrd/conf/busybox/testsuite/basename/basename-does-not-remove-identical-extension
new file mode 100644
index 0000000..4448fde
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/basename/basename-does-not-remove-identical-extension
@@ -0,0 +1 @@
+test xfoo = x`busybox basename foo foo`
diff --git a/i/pc104/initrd/conf/busybox/testsuite/basename/basename-works b/i/pc104/initrd/conf/busybox/testsuite/basename/basename-works
new file mode 100644
index 0000000..38907d4
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/basename/basename-works
@@ -0,0 +1,2 @@
+test x$(basename $(pwd)) = x$(busybox basename $(pwd))
+
diff --git a/i/pc104/initrd/conf/busybox/testsuite/bunzip2.tests b/i/pc104/initrd/conf/busybox/testsuite/bunzip2.tests
new file mode 100755
index 0000000..2b27dd5
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/bunzip2.tests
@@ -0,0 +1,84 @@
+#!/bin/sh
+
+if test "${0##*/}" = "gunzip.tests"; then
+ unpack=gunzip
+ ext=gz
+elif test "${0##*/}" = "bunzip2.tests"; then
+ unpack=bunzip2
+ ext=bz2
+else
+ echo "WTF? argv0='$0'"
+ exit 1
+fi
+
+bb="busybox "
+
+unset LC_ALL
+unset LC_MESSAGES
+unset LANG
+unset LANGUAGE
+
+hello_gz() {
+ # Gzipped "HELLO\n"
+ #_________________________ vvv vvv vvv vvv - mtime
+ echo -ne "\x1f\x8b\x08\x00\x85\x1d\xef\x45\x02\x03\xf3\x70\xf5\xf1\xf1\xe7"
+ echo -ne "\x02\x00\x6e\xd7\xac\xfd\x06\x00\x00\x00"
+}
+
+hello_bz2() {
+ # Bzipped "HELLO\n"
+ echo -ne "\x42\x5a\x68\x39\x31\x41\x59\x26\x53\x59\x5b\xb8\xe8\xa3\x00\x00"
+ echo -ne "\x01\x44\x00\x00\x10\x02\x44\xa0\x00\x30\xcd\x00\xc3\x46\x29\x97"
+ echo -ne "\x17\x72\x45\x38\x50\x90\x5b\xb8\xe8\xa3"
+}
+
+prep() {
+ rm -f t*
+ hello_$ext >t1.$ext
+ hello_$ext >t2.$ext
+}
+
+check() {
+ eval $2 >t_actual 2>&1
+ if echo -ne "$expected" | cmp - t_actual; then
+ echo "$1: PASS"
+ else
+ echo "$1: FAIL"
+ fi
+}
+
+mkdir testdir 2>/dev/null
+(
+cd testdir || { echo "cannot cd testdir!"; exit 1; }
+
+expected="$unpack: z: No such file or directory
+1
+HELLO
+"
+prep; check "$unpack: doesnt exist" "${bb}$unpack z t1.$ext; echo \$?; cat t1"
+
+
+expected="$unpack: t.zz: unknown suffix - ignored
+1
+HELLO
+"
+prep; >t.zz; check "$unpack: unknown suffix" "${bb}$unpack t.zz t1.$ext; echo \$?; cat t1"
+
+
+# In this case file "t1" exists, and we skip t1.gz and unpack t2.gz
+expected="$unpack: t1: File exists
+1
+HELLO
+"
+prep; >t1; check "$unpack: already exists" "${bb}$unpack t1.$ext t2.$ext; echo \$?; cat t1 t2"
+
+
+# From old testsuite
+expected="HELLO\n0\n"
+prep; check "$unpack: stream unpack" "cat t1.$ext | ${bb}$unpack; echo $?"
+
+expected="ok\n"
+prep; check "$unpack: delete src" "${bb}$unpack t2.$ext; test ! -f t2.$ext && echo ok"
+
+)
+rm -rf testdir
diff --git a/i/pc104/initrd/conf/busybox/testsuite/bunzip2/bunzip2-reads-from-standard-input b/i/pc104/initrd/conf/busybox/testsuite/bunzip2/bunzip2-reads-from-standard-input
new file mode 100644
index 0000000..e212a12
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/bunzip2/bunzip2-reads-from-standard-input
@@ -0,0 +1,2 @@
+echo foo | bzip2 | busybox bunzip2 > output
+echo foo | cmp - output
diff --git a/i/pc104/initrd/conf/busybox/testsuite/bunzip2/bunzip2-removes-compressed-file b/i/pc104/initrd/conf/busybox/testsuite/bunzip2/bunzip2-removes-compressed-file
new file mode 100644
index 0000000..f1d1550
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/bunzip2/bunzip2-removes-compressed-file
@@ -0,0 +1,3 @@
+echo foo | bzip2 >foo.bz2
+busybox bunzip2 foo.bz2
+test ! -f foo.bz2
diff --git a/i/pc104/initrd/conf/busybox/testsuite/bunzip2/bzcat-does-not-remove-compressed-file b/i/pc104/initrd/conf/busybox/testsuite/bunzip2/bzcat-does-not-remove-compressed-file
new file mode 100644
index 0000000..7d4016e
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/bunzip2/bzcat-does-not-remove-compressed-file
@@ -0,0 +1,3 @@
+echo foo | bzip2 >foo.bz2
+busybox bzcat foo.bz2
+test -f foo.bz2
diff --git a/i/pc104/initrd/conf/busybox/testsuite/busybox.tests b/i/pc104/initrd/conf/busybox/testsuite/busybox.tests
new file mode 100755
index 0000000..26536c6
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/busybox.tests
@@ -0,0 +1,46 @@
+#!/bin/sh
+
+# Tests for busybox applet itself.
+# Copyright 2005 by Rob Landley <rob@landley.net>
+# Licensed under GPL v2, see file LICENSE for details.
+
+. testing.sh
+
+HELPDUMP=`busybox`
+
+# We need to test under calling the binary under other names.
+
+
+testing "busybox --help busybox" "busybox --help busybox" "$HELPDUMP\n\n" "" ""
+
+ln -s `which busybox` busybox-suffix
+for i in busybox ./busybox-suffix
+do
+ # The gratuitous "\n"s are due to a shell idiosyncrasy:
+ # environment variables seem to strip trailing whitespace.
+
+ testing "" "$i" "$HELPDUMP\n\n" "" ""
+
+ testing "$i unknown" "$i unknown 2>&1" \
+ "unknown: applet not found\n" "" ""
+
+ testing "$i --help" "$i --help 2>&1" "$HELPDUMP\n\n" "" ""
+
+ optional CAT
+ testing "" "$i cat" "moo" "" "moo"
+ testing "$i --help cat" "$i --help cat 2>&1 | grep prints" \
+ "Concatenates FILE(s) and prints them to stdout.\n" "" ""
+ optional ""
+
+ testing "$i --help unknown" "$i --help unknown 2>&1" \
+ "unknown: applet not found\n" "" ""
+done
+rm busybox-suffix
+
+ln -s `which busybox` unknown
+
+testing "busybox as unknown name" "./unknown 2>&1" \
+ "unknown: applet not found\n" "" ""
+rm unknown
+
+exit $FAILCOUNT
diff --git a/i/pc104/initrd/conf/busybox/testsuite/bzcat.tests b/i/pc104/initrd/conf/busybox/testsuite/bzcat.tests
new file mode 100755
index 0000000..16fa3c2
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/bzcat.tests
@@ -0,0 +1,49 @@
+#!/bin/sh
+
+ext=bz2
+
+bb="busybox "
+
+unset LC_ALL
+unset LC_MESSAGES
+unset LANG
+unset LANGUAGE
+
+hello_gz() {
+ # Gzipped "HELLO\n"
+ #_________________________ vvv vvv vvv vvv - mtime
+ echo -ne "\x1f\x8b\x08\x00\x85\x1d\xef\x45\x02\x03\xf3\x70\xf5\xf1\xf1\xe7"
+ echo -ne "\x02\x00\x6e\xd7\xac\xfd\x06\x00\x00\x00"
+}
+
+hello_bz2() {
+ # Bzipped "HELLO\n"
+ echo -ne "\x42\x5a\x68\x39\x31\x41\x59\x26\x53\x59\x5b\xb8\xe8\xa3\x00\x00"
+ echo -ne "\x01\x44\x00\x00\x10\x02\x44\xa0\x00\x30\xcd\x00\xc3\x46\x29\x97"
+ echo -ne "\x17\x72\x45\x38\x50\x90\x5b\xb8\xe8\xa3"
+}
+
+prep() {
+ rm -f t*
+ hello_$ext >t1.$ext
+ hello_$ext >t2.$ext
+}
+
+check() {
+ eval $2 >t_actual 2>&1
+ if echo -ne "$expected" | cmp - t_actual; then
+ echo "$1: PASS"
+ else
+ echo "$1: FAIL"
+ fi
+}
+
+mkdir testdir 2>/dev/null
+(
+cd testdir || { echo "cannot cd testdir!"; exit 1; }
+
+expected="HELLO\nok\n"
+prep; check "bzcat: dont delete src" "${bb}bzcat t2.bz2; test -f t2.bz2 && echo ok"
+
+)
+rm -rf testdir
diff --git a/i/pc104/initrd/conf/busybox/testsuite/cat/cat-prints-a-file b/i/pc104/initrd/conf/busybox/testsuite/cat/cat-prints-a-file
new file mode 100644
index 0000000..e3f35a8
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/cat/cat-prints-a-file
@@ -0,0 +1,3 @@
+echo I WANT > foo
+busybox cat foo >bar
+cmp foo bar
diff --git a/i/pc104/initrd/conf/busybox/testsuite/cat/cat-prints-a-file-and-standard-input b/i/pc104/initrd/conf/busybox/testsuite/cat/cat-prints-a-file-and-standard-input
new file mode 100644
index 0000000..bc92318
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/cat/cat-prints-a-file-and-standard-input
@@ -0,0 +1,7 @@
+echo I WANT > foo
+echo SOMETHING | busybox cat foo - >bar
+cat >baz <<EOF
+I WANT
+SOMETHING
+EOF
+cmp bar baz
diff --git a/i/pc104/initrd/conf/busybox/testsuite/cmp/cmp-detects-difference b/i/pc104/initrd/conf/busybox/testsuite/cmp/cmp-detects-difference
new file mode 100644
index 0000000..b9bb628
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/cmp/cmp-detects-difference
@@ -0,0 +1,9 @@
+echo foo >foo
+echo bar >bar
+set +e
+busybox cmp -s foo bar
+if [ $? != 0 ] ; then
+ exit 0;
+fi
+
+exit 1;
diff --git a/i/pc104/initrd/conf/busybox/testsuite/cp/cp-a-files-to-dir b/i/pc104/initrd/conf/busybox/testsuite/cp/cp-a-files-to-dir
new file mode 100644
index 0000000..39f8f81
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/cp/cp-a-files-to-dir
@@ -0,0 +1,14 @@
+echo file number one > file1
+echo file number two > file2
+ln -s file2 link1
+mkdir dir1
+touch --date='Sat Jan 29 21:24:08 PST 2000' dir1/file3
+mkdir there
+busybox cp -a file1 file2 link1 dir1 there
+test -f there/file1
+test -f there/file2
+test ! -s there/dir1/file3
+test -L there/link1
+test xfile2 = x`readlink there/link1`
+test ! dir1/file3 -ot there/dir1/file3
+test ! dir1/file3 -nt there/dir1/file3
diff --git a/i/pc104/initrd/conf/busybox/testsuite/cp/cp-a-preserves-links b/i/pc104/initrd/conf/busybox/testsuite/cp/cp-a-preserves-links
new file mode 100644
index 0000000..0c0cd96
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/cp/cp-a-preserves-links
@@ -0,0 +1,5 @@
+touch foo
+ln -s foo bar
+busybox cp -a bar baz
+test -L baz
+test xfoo = x`readlink baz`
diff --git a/i/pc104/initrd/conf/busybox/testsuite/cp/cp-copies-empty-file b/i/pc104/initrd/conf/busybox/testsuite/cp/cp-copies-empty-file
new file mode 100644
index 0000000..ad25aa1
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/cp/cp-copies-empty-file
@@ -0,0 +1,3 @@
+touch foo
+busybox cp foo bar
+cmp foo bar
diff --git a/i/pc104/initrd/conf/busybox/testsuite/cp/cp-copies-large-file b/i/pc104/initrd/conf/busybox/testsuite/cp/cp-copies-large-file
new file mode 100644
index 0000000..c2225c6
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/cp/cp-copies-large-file
@@ -0,0 +1,3 @@
+dd if=/dev/zero of=foo seek=10k count=1 2>/dev/null
+busybox cp foo bar
+cmp foo bar
diff --git a/i/pc104/initrd/conf/busybox/testsuite/cp/cp-copies-small-file b/i/pc104/initrd/conf/busybox/testsuite/cp/cp-copies-small-file
new file mode 100644
index 0000000..d52a887
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/cp/cp-copies-small-file
@@ -0,0 +1,3 @@
+echo I WANT > foo
+busybox cp foo bar
+cmp foo bar
diff --git a/i/pc104/initrd/conf/busybox/testsuite/cp/cp-d-files-to-dir b/i/pc104/initrd/conf/busybox/testsuite/cp/cp-d-files-to-dir
new file mode 100644
index 0000000..9571a56
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/cp/cp-d-files-to-dir
@@ -0,0 +1,11 @@
+echo file number one > file1
+echo file number two > file2
+touch file3
+ln -s file2 link1
+mkdir there
+busybox cp -d file1 file2 file3 link1 there
+test -f there/file1
+test -f there/file2
+test ! -s there/file3
+test -L there/link1
+test xfile2 = x`readlink there/link1`
diff --git a/i/pc104/initrd/conf/busybox/testsuite/cp/cp-dir-create-dir b/i/pc104/initrd/conf/busybox/testsuite/cp/cp-dir-create-dir
new file mode 100644
index 0000000..a8d7b50
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/cp/cp-dir-create-dir
@@ -0,0 +1,4 @@
+mkdir bar
+touch bar/baz
+busybox cp -R bar foo
+test -f foo/baz
diff --git a/i/pc104/initrd/conf/busybox/testsuite/cp/cp-dir-existing-dir b/i/pc104/initrd/conf/busybox/testsuite/cp/cp-dir-existing-dir
new file mode 100644
index 0000000..4c788ba
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/cp/cp-dir-existing-dir
@@ -0,0 +1,5 @@
+mkdir bar
+touch bar/baz
+mkdir foo
+busybox cp -R bar foo
+test -f foo/bar/baz
diff --git a/i/pc104/initrd/conf/busybox/testsuite/cp/cp-does-not-copy-unreadable-file b/i/pc104/initrd/conf/busybox/testsuite/cp/cp-does-not-copy-unreadable-file
new file mode 100644
index 0000000..ce11bfa
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/cp/cp-does-not-copy-unreadable-file
@@ -0,0 +1,6 @@
+touch foo
+chmod a-r foo
+set +e
+busybox cp foo bar
+set -e
+test ! -f bar
diff --git a/i/pc104/initrd/conf/busybox/testsuite/cp/cp-files-to-dir b/i/pc104/initrd/conf/busybox/testsuite/cp/cp-files-to-dir
new file mode 100644
index 0000000..fdb8191
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/cp/cp-files-to-dir
@@ -0,0 +1,11 @@
+echo file number one > file1
+echo file number two > file2
+touch file3
+ln -s file2 link1
+mkdir there
+busybox cp file1 file2 file3 link1 there
+test -f there/file1
+test -f there/file2
+test ! -s there/file3
+test -f there/link1
+cmp there/file2 there/link1
diff --git a/i/pc104/initrd/conf/busybox/testsuite/cp/cp-follows-links b/i/pc104/initrd/conf/busybox/testsuite/cp/cp-follows-links
new file mode 100644
index 0000000..2d9f05e
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/cp/cp-follows-links
@@ -0,0 +1,4 @@
+touch foo
+ln -s foo bar
+busybox cp bar baz
+test -f baz
diff --git a/i/pc104/initrd/conf/busybox/testsuite/cp/cp-preserves-hard-links b/i/pc104/initrd/conf/busybox/testsuite/cp/cp-preserves-hard-links
new file mode 100644
index 0000000..4de7b85
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/cp/cp-preserves-hard-links
@@ -0,0 +1,6 @@
+# FEATURE: CONFIG_FEATURE_PRESERVE_HARDLINKS
+touch foo
+ln foo bar
+mkdir baz
+busybox cp -d foo bar baz
+test baz/foo -ef baz/bar
diff --git a/i/pc104/initrd/conf/busybox/testsuite/cp/cp-preserves-links b/i/pc104/initrd/conf/busybox/testsuite/cp/cp-preserves-links
new file mode 100644
index 0000000..301dc5f
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/cp/cp-preserves-links
@@ -0,0 +1,5 @@
+touch foo
+ln -s foo bar
+busybox cp -d bar baz
+test -L baz
+test xfoo = x`readlink baz`
diff --git a/i/pc104/initrd/conf/busybox/testsuite/cp/cp-preserves-source-file b/i/pc104/initrd/conf/busybox/testsuite/cp/cp-preserves-source-file
new file mode 100644
index 0000000..f0f5065
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/cp/cp-preserves-source-file
@@ -0,0 +1,3 @@
+touch foo
+busybox cp foo bar
+test -f foo
diff --git a/i/pc104/initrd/conf/busybox/testsuite/cut/cut-cuts-a-character b/i/pc104/initrd/conf/busybox/testsuite/cut/cut-cuts-a-character
new file mode 100644
index 0000000..d6c5efa
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/cut/cut-cuts-a-character
@@ -0,0 +1 @@
+test $(echo abcd | busybox cut -c 3) = c
diff --git a/i/pc104/initrd/conf/busybox/testsuite/cut/cut-cuts-a-closed-range b/i/pc104/initrd/conf/busybox/testsuite/cut/cut-cuts-a-closed-range
new file mode 100644
index 0000000..9680b76
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/cut/cut-cuts-a-closed-range
@@ -0,0 +1 @@
+test $(echo abcd | busybox cut -c 1-2) = ab
diff --git a/i/pc104/initrd/conf/busybox/testsuite/cut/cut-cuts-a-field b/i/pc104/initrd/conf/busybox/testsuite/cut/cut-cuts-a-field
new file mode 100644
index 0000000..4c7f440
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/cut/cut-cuts-a-field
@@ -0,0 +1 @@
+test $(echo -e "f1\tf2\tf3" | busybox cut -f 2) = f2
diff --git a/i/pc104/initrd/conf/busybox/testsuite/cut/cut-cuts-an-open-range b/i/pc104/initrd/conf/busybox/testsuite/cut/cut-cuts-an-open-range
new file mode 100644
index 0000000..1fbf277
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/cut/cut-cuts-an-open-range
@@ -0,0 +1 @@
+test $(echo abcd | busybox cut -c -3) = abc
diff --git a/i/pc104/initrd/conf/busybox/testsuite/cut/cut-cuts-an-unclosed-range b/i/pc104/initrd/conf/busybox/testsuite/cut/cut-cuts-an-unclosed-range
new file mode 100644
index 0000000..a2b0cdb
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/cut/cut-cuts-an-unclosed-range
@@ -0,0 +1 @@
+test $(echo abcd | busybox cut -c 3-) = cd
diff --git a/i/pc104/initrd/conf/busybox/testsuite/date/date-R-works b/i/pc104/initrd/conf/busybox/testsuite/date/date-R-works
new file mode 100644
index 0000000..ec3a067
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/date/date-R-works
@@ -0,0 +1,2 @@
+test x"`date -R`" = x"`busybox date -R`"
+
diff --git a/i/pc104/initrd/conf/busybox/testsuite/date/date-format-works b/i/pc104/initrd/conf/busybox/testsuite/date/date-format-works
new file mode 100644
index 0000000..f28d06c
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/date/date-format-works
@@ -0,0 +1 @@
+test x"`date +%d/%m/%y`" = x"`busybox date +%d/%m/%y`"
diff --git a/i/pc104/initrd/conf/busybox/testsuite/date/date-u-works b/i/pc104/initrd/conf/busybox/testsuite/date/date-u-works
new file mode 100644
index 0000000..7d9902a
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/date/date-u-works
@@ -0,0 +1,2 @@
+test x"`date -u`" = x"`busybox date -u`"
+
diff --git a/i/pc104/initrd/conf/busybox/testsuite/date/date-works b/i/pc104/initrd/conf/busybox/testsuite/date/date-works
new file mode 100644
index 0000000..2f6dd1e
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/date/date-works
@@ -0,0 +1,2 @@
+test x"`date`" = x"`busybox date`"
+
diff --git a/i/pc104/initrd/conf/busybox/testsuite/dd/dd-accepts-if b/i/pc104/initrd/conf/busybox/testsuite/dd/dd-accepts-if
new file mode 100644
index 0000000..03d1af8
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/dd/dd-accepts-if
@@ -0,0 +1,2 @@
+echo I WANT >foo
+test "$(busybox dd if=foo 2>/dev/null)" = "I WANT"
diff --git a/i/pc104/initrd/conf/busybox/testsuite/dd/dd-accepts-of b/i/pc104/initrd/conf/busybox/testsuite/dd/dd-accepts-of
new file mode 100644
index 0000000..84405e6
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/dd/dd-accepts-of
@@ -0,0 +1,2 @@
+echo I WANT | busybox dd of=foo 2>/dev/null
+echo I WANT | cmp foo -
diff --git a/i/pc104/initrd/conf/busybox/testsuite/dd/dd-copies-from-standard-input-to-standard-output b/i/pc104/initrd/conf/busybox/testsuite/dd/dd-copies-from-standard-input-to-standard-output
new file mode 100644
index 0000000..d890eb0
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/dd/dd-copies-from-standard-input-to-standard-output
@@ -0,0 +1 @@
+test "$(echo I WANT | busybox dd 2>/dev/null)" = "I WANT"
diff --git a/i/pc104/initrd/conf/busybox/testsuite/dd/dd-prints-count-to-standard-error b/i/pc104/initrd/conf/busybox/testsuite/dd/dd-prints-count-to-standard-error
new file mode 100644
index 0000000..2187dc0
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/dd/dd-prints-count-to-standard-error
@@ -0,0 +1,2 @@
+echo I WANT | busybox dd of=foo >/dev/null 2>bar
+grep -q records bar
diff --git a/i/pc104/initrd/conf/busybox/testsuite/dirname/dirname-handles-absolute-path b/i/pc104/initrd/conf/busybox/testsuite/dirname/dirname-handles-absolute-path
new file mode 100644
index 0000000..ca1a51b
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/dirname/dirname-handles-absolute-path
@@ -0,0 +1 @@
+test $(busybox dirname /foo/bar/baz) = /foo/bar
diff --git a/i/pc104/initrd/conf/busybox/testsuite/dirname/dirname-handles-empty-path b/i/pc104/initrd/conf/busybox/testsuite/dirname/dirname-handles-empty-path
new file mode 100644
index 0000000..04134a5
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/dirname/dirname-handles-empty-path
@@ -0,0 +1 @@
+test $(busybox dirname '') = .
diff --git a/i/pc104/initrd/conf/busybox/testsuite/dirname/dirname-handles-multiple-slashes b/i/pc104/initrd/conf/busybox/testsuite/dirname/dirname-handles-multiple-slashes
new file mode 100644
index 0000000..286f253
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/dirname/dirname-handles-multiple-slashes
@@ -0,0 +1 @@
+test $(busybox dirname foo/bar///baz) = foo/bar
diff --git a/i/pc104/initrd/conf/busybox/testsuite/dirname/dirname-handles-relative-path b/i/pc104/initrd/conf/busybox/testsuite/dirname/dirname-handles-relative-path
new file mode 100644
index 0000000..ffe4ab4
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/dirname/dirname-handles-relative-path
@@ -0,0 +1 @@
+test $(busybox dirname foo/bar/baz) = foo/bar
diff --git a/i/pc104/initrd/conf/busybox/testsuite/dirname/dirname-handles-root b/i/pc104/initrd/conf/busybox/testsuite/dirname/dirname-handles-root
new file mode 100644
index 0000000..6bd62b8
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/dirname/dirname-handles-root
@@ -0,0 +1 @@
+test $(busybox dirname /) = /
diff --git a/i/pc104/initrd/conf/busybox/testsuite/dirname/dirname-handles-single-component b/i/pc104/initrd/conf/busybox/testsuite/dirname/dirname-handles-single-component
new file mode 100644
index 0000000..24f9ae1
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/dirname/dirname-handles-single-component
@@ -0,0 +1 @@
+test $(busybox dirname foo) = .
diff --git a/i/pc104/initrd/conf/busybox/testsuite/dirname/dirname-works b/i/pc104/initrd/conf/busybox/testsuite/dirname/dirname-works
new file mode 100644
index 0000000..f339c8f
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/dirname/dirname-works
@@ -0,0 +1,2 @@
+test x$(dirname $(pwd)) = x$(busybox dirname $(pwd))
+
diff --git a/i/pc104/initrd/conf/busybox/testsuite/du/du-h-works b/i/pc104/initrd/conf/busybox/testsuite/du/du-h-works
new file mode 100644
index 0000000..82041ab
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/du/du-h-works
@@ -0,0 +1,4 @@
+[ -n "$d" ] || d=..
+du -h "$d" > logfile.gnu
+busybox du -h "$d" > logfile.bb
+cmp logfile.gnu logfile.bb
diff --git a/i/pc104/initrd/conf/busybox/testsuite/du/du-k-works b/i/pc104/initrd/conf/busybox/testsuite/du/du-k-works
new file mode 100644
index 0000000..177a1a2
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/du/du-k-works
@@ -0,0 +1,4 @@
+[ -n "$d" ] || d=..
+du -k "$d" > logfile.gnu
+busybox du -k "$d" > logfile.bb
+cmp logfile.gnu logfile.bb
diff --git a/i/pc104/initrd/conf/busybox/testsuite/du/du-l-works b/i/pc104/initrd/conf/busybox/testsuite/du/du-l-works
new file mode 100644
index 0000000..61e9140
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/du/du-l-works
@@ -0,0 +1,4 @@
+[ -n "$d" ] || d=..
+du -l "$d" > logfile.gnu
+busybox du -l "$d" > logfile.bb
+cmp logfile.gnu logfile.bb
diff --git a/i/pc104/initrd/conf/busybox/testsuite/du/du-m-works b/i/pc104/initrd/conf/busybox/testsuite/du/du-m-works
new file mode 100644
index 0000000..bc97073
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/du/du-m-works
@@ -0,0 +1,4 @@
+[ -n "$d" ] || d=..
+du -m "$d" > logfile.gnu
+busybox du -m "$d" > logfile.bb
+cmp logfile.gnu logfile.bb
diff --git a/i/pc104/initrd/conf/busybox/testsuite/du/du-s-works b/i/pc104/initrd/conf/busybox/testsuite/du/du-s-works
new file mode 100644
index 0000000..f0b3bf0
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/du/du-s-works
@@ -0,0 +1,4 @@
+[ -n "$d" ] || d=..
+du -s "$d" > logfile.gnu
+busybox du -s "$d" > logfile.bb
+cmp logfile.gnu logfile.bb
diff --git a/i/pc104/initrd/conf/busybox/testsuite/du/du-works b/i/pc104/initrd/conf/busybox/testsuite/du/du-works
new file mode 100644
index 0000000..47949c6
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/du/du-works
@@ -0,0 +1,4 @@
+[ -n "$d" ] || d=..
+du "$d" > logfile.gnu
+busybox du "$d" > logfile.bb
+cmp logfile.gnu logfile.bb
diff --git a/i/pc104/initrd/conf/busybox/testsuite/echo/echo-does-not-print-newline b/i/pc104/initrd/conf/busybox/testsuite/echo/echo-does-not-print-newline
new file mode 100644
index 0000000..2ed03ca
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/echo/echo-does-not-print-newline
@@ -0,0 +1 @@
+test `busybox echo -n word | wc -c` -eq 4
diff --git a/i/pc104/initrd/conf/busybox/testsuite/echo/echo-prints-argument b/i/pc104/initrd/conf/busybox/testsuite/echo/echo-prints-argument
new file mode 100644
index 0000000..479dac8
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/echo/echo-prints-argument
@@ -0,0 +1 @@
+test xfubar = x`busybox echo fubar`
diff --git a/i/pc104/initrd/conf/busybox/testsuite/echo/echo-prints-arguments b/i/pc104/initrd/conf/busybox/testsuite/echo/echo-prints-arguments
new file mode 100644
index 0000000..4e4e3b4
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/echo/echo-prints-arguments
@@ -0,0 +1 @@
+test "`busybox echo foo bar`" = "foo bar"
diff --git a/i/pc104/initrd/conf/busybox/testsuite/echo/echo-prints-newline b/i/pc104/initrd/conf/busybox/testsuite/echo/echo-prints-newline
new file mode 100644
index 0000000..838671e
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/echo/echo-prints-newline
@@ -0,0 +1 @@
+test `busybox echo word | wc -c` -eq 5
diff --git a/i/pc104/initrd/conf/busybox/testsuite/expr/expr-works b/i/pc104/initrd/conf/busybox/testsuite/expr/expr-works
new file mode 100644
index 0000000..af49ac4
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/expr/expr-works
@@ -0,0 +1,59 @@
+# busybox expr
+busybox expr 1 \| 1
+busybox expr 1 \| 0
+busybox expr 0 \| 1
+busybox expr 1 \& 1
+busybox expr 0 \< 1
+busybox expr 1 \> 0
+busybox expr 0 \<= 1
+busybox expr 1 \<= 1
+busybox expr 1 \>= 0
+busybox expr 1 \>= 1
+busybox expr 1 + 2
+busybox expr 2 - 1
+busybox expr 2 \* 3
+busybox expr 12 / 2
+busybox expr 12 % 5
+
+
+set +e
+busybox expr 0 \| 0
+if [ $? != 1 ] ; then
+ exit 1;
+fi;
+
+busybox expr 1 \& 0
+if [ $? != 1 ] ; then
+ exit 1;
+fi;
+
+busybox expr 0 \& 1
+if [ $? != 1 ] ; then
+ exit 1;
+fi;
+
+busybox expr 0 \& 0
+if [ $? != 1 ] ; then
+ exit 1;
+fi;
+
+busybox expr 1 \< 0
+if [ $? != 1 ] ; then
+ exit 1;
+fi;
+
+busybox expr 0 \> 1
+if [ $? != 1 ] ; then
+ exit 1;
+fi;
+
+busybox expr 1 \<= 0
+if [ $? != 1 ] ; then
+ exit 1;
+fi;
+
+busybox expr 0 \>= 1
+if [ $? != 1 ] ; then
+ exit 1;
+fi;
+
diff --git a/i/pc104/initrd/conf/busybox/testsuite/false/false-is-silent b/i/pc104/initrd/conf/busybox/testsuite/false/false-is-silent
new file mode 100644
index 0000000..8a9aa0c
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/false/false-is-silent
@@ -0,0 +1 @@
+busybox false 2>&1 | cmp - /dev/null
diff --git a/i/pc104/initrd/conf/busybox/testsuite/false/false-returns-failure b/i/pc104/initrd/conf/busybox/testsuite/false/false-returns-failure
new file mode 100644
index 0000000..1a061f2
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/false/false-returns-failure
@@ -0,0 +1 @@
+! busybox false
diff --git a/i/pc104/initrd/conf/busybox/testsuite/find/find-supports-minus-xdev b/i/pc104/initrd/conf/busybox/testsuite/find/find-supports-minus-xdev
new file mode 100644
index 0000000..4c559a1
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/find/find-supports-minus-xdev
@@ -0,0 +1 @@
+busybox find . -xdev >/dev/null 2>&1
diff --git a/i/pc104/initrd/conf/busybox/testsuite/grep.tests b/i/pc104/initrd/conf/busybox/testsuite/grep.tests
new file mode 100755
index 0000000..ef0de48
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/grep.tests
@@ -0,0 +1,84 @@
+#!/bin/sh
+
+# grep tests.
+# Copyright 2005 by Rob Landley <rob@landley.net>
+# Licensed under GPL v2, see file LICENSE for details.
+
+# AUDIT:
+
+. testing.sh
+
+# testing "test name" "options" "expected result" "file input" "stdin"
+# file input will be file called "input"
+# test can create a file "actual" instead of writing to stdout
+
+# Test exit status
+
+testing "grep (exit with error)" "grep nonexistent 2> /dev/null ; echo \$?" \
+ "1\n" "" ""
+testing "grep (exit success)" "grep grep $0 > /dev/null 2>&1 ; echo \$?" "0\n" \
+ "" ""
+# Test various data sources and destinations
+
+testing "grep (default to stdin)" "grep two" "two\n" "" \
+ "one\ntwo\nthree\nthree\nthree\n"
+testing "grep - (specify stdin)" "grep two -" "two\n" "" \
+ "one\ntwo\nthree\nthree\nthree\n"
+testing "grep input (specify file)" "grep two input" "two\n" \
+ "one\ntwo\nthree\nthree\nthree\n" ""
+
+testing "grep (no newline at EOL)" "grep bug" "bug" "bug" ""
+
+# Note that this assumes actual is empty.
+testing "grep input actual (two files)" "grep two input actual 2> /dev/null" \
+ "input:two\n" "one\ntwo\nthree\nthree\nthree\n" ""
+
+testing "grep - infile (specify stdin and file)" "grep two - input" \
+ "(standard input):two\ninput:two\n" "one\ntwo\nthree\n" \
+ "one\ntwo\ntoo\nthree\nthree\n"
+
+# Check if we see the correct return value if both stdin and non-existing file
+# are given.
+testing "grep - nofile (specify stdin and nonexisting file)" \
+ "grep two - nonexistent 2> /dev/null ; echo \$?" \
+ "(standard input):two\n(standard input):two\n2\n" \
+ "" "one\ntwo\ntwo\nthree\nthree\nthree\n"
+testing "grep -q - nofile (specify stdin and nonexisting file, no match)" \
+ "grep -q nomatch - nonexistent 2> /dev/null ; echo \$?" \
+ "2\n" "" "one\ntwo\ntwo\nthree\nthree\nthree\n"
+# SUSv3: If the -q option is specified, the exit status shall be zero
+# if an input line is selected, even if an error was detected.
+testing "grep -q - nofile (specify stdin and nonexisting file, match)" \
+ "grep -q two - nonexistent ; echo \$?" \
+ "0\n" "" "one\ntwo\ntwo\nthree\nthree\nthree\n"
+
+# Test various command line options
+# -s no error messages
+testing "grep -s nofile (nonexisting file, no match)" \
+ "grep -s nomatch nonexistent ; echo \$?" "2\n" "" ""
+testing "grep -s nofile - (stdin and nonexisting file, match)" \
+ "grep -s domatch nonexistent - ; echo \$?" \
+ "(standard input):domatch\n2\n" "" "nomatch\ndomatch\nend\n"
+
+# This doesn't match GNU behaviour (Binary file input matches)
+# acts like GNU grep -a
+testing "grep handles binary files" "grep foo input" "foo\n" "\0foo\n\n" ""
+# This doesn't match GNU behaviour (Binary file (standard input) matches)
+# acts like GNU grep -a
+testing "grep handles binary stdin" "grep foo" "foo\n" "" "\0foo\n\n"
+
+testing "grep matches NUL" "grep . input > /dev/null 2>&1 ; echo \$?" \
+ "0\n" "\0\n" ""
+
+# -e regex
+testing "grep handles multiple regexps" "grep -e one -e two input ; echo \$?" \
+ "one\ntwo\n0\n" "one\ntwo\n" ""
+
+optional FEATURE_GREP_EGREP_ALIAS
+testing "grep -E supports extended regexps" "grep -E fo+" "foo\n" "" \
+ "b\ar\nfoo\nbaz"
+testing "grep is also egrep" "egrep foo" "foo\n" "" "foo\nbar\n"
+testing "egrep is not case insensitive" \
+ "egrep foo ; [ \$? -ne 0 ] && echo yes" "yes\n" "" "FOO\n"
+
+exit $FAILCOUNT
diff --git a/i/pc104/initrd/conf/busybox/testsuite/gunzip.tests b/i/pc104/initrd/conf/busybox/testsuite/gunzip.tests
new file mode 100755
index 0000000..d781004
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/gunzip.tests
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+. bunzip2.tests
diff --git a/i/pc104/initrd/conf/busybox/testsuite/gunzip/gunzip-reads-from-standard-input b/i/pc104/initrd/conf/busybox/testsuite/gunzip/gunzip-reads-from-standard-input
new file mode 100644
index 0000000..7c498c0
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/gunzip/gunzip-reads-from-standard-input
@@ -0,0 +1,2 @@
+echo foo | gzip | busybox gunzip > output
+echo foo | cmp - output
diff --git a/i/pc104/initrd/conf/busybox/testsuite/gzip/gzip-accepts-multiple-files b/i/pc104/initrd/conf/busybox/testsuite/gzip/gzip-accepts-multiple-files
new file mode 100644
index 0000000..8f0d9c8
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/gzip/gzip-accepts-multiple-files
@@ -0,0 +1,3 @@
+touch foo bar
+busybox gzip foo bar
+test -f foo.gz -a -f bar.gz
diff --git a/i/pc104/initrd/conf/busybox/testsuite/gzip/gzip-accepts-single-minus b/i/pc104/initrd/conf/busybox/testsuite/gzip/gzip-accepts-single-minus
new file mode 100644
index 0000000..8b51fdf
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/gzip/gzip-accepts-single-minus
@@ -0,0 +1 @@
+echo foo | busybox gzip - >/dev/null
diff --git a/i/pc104/initrd/conf/busybox/testsuite/gzip/gzip-removes-original-file b/i/pc104/initrd/conf/busybox/testsuite/gzip/gzip-removes-original-file
new file mode 100644
index 0000000..b9cb995
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/gzip/gzip-removes-original-file
@@ -0,0 +1,3 @@
+touch foo
+busybox gzip foo
+test ! -f foo
diff --git a/i/pc104/initrd/conf/busybox/testsuite/head/head-n-works b/i/pc104/initrd/conf/busybox/testsuite/head/head-n-works
new file mode 100644
index 0000000..db43255
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/head/head-n-works
@@ -0,0 +1,4 @@
+[ -n "$d" ] || d=..
+head -n 2 "$d/README" > logfile.gnu
+busybox head -n 2 "$d/README" > logfile.bb
+cmp logfile.gnu logfile.bb
diff --git a/i/pc104/initrd/conf/busybox/testsuite/head/head-works b/i/pc104/initrd/conf/busybox/testsuite/head/head-works
new file mode 100644
index 0000000..56ad3e3
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/head/head-works
@@ -0,0 +1,4 @@
+[ -n "$d" ] || d=..
+head "$d/README" > logfile.gnu
+busybox head "$d/README" > logfile.bb
+cmp logfile.gnu logfile.bb
diff --git a/i/pc104/initrd/conf/busybox/testsuite/hostid/hostid-works b/i/pc104/initrd/conf/busybox/testsuite/hostid/hostid-works
new file mode 100644
index 0000000..e85698e
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/hostid/hostid-works
@@ -0,0 +1,2 @@
+test x$(hostid) = x$(busybox hostid)
+
diff --git a/i/pc104/initrd/conf/busybox/testsuite/hostname/hostname-d-works b/i/pc104/initrd/conf/busybox/testsuite/hostname/hostname-d-works
new file mode 100644
index 0000000..a9aeb92
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/hostname/hostname-d-works
@@ -0,0 +1,2 @@
+test x$(hostname -d) = x$(busybox hostname -d)
+
diff --git a/i/pc104/initrd/conf/busybox/testsuite/hostname/hostname-i-works b/i/pc104/initrd/conf/busybox/testsuite/hostname/hostname-i-works
new file mode 100644
index 0000000..68a3e67
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/hostname/hostname-i-works
@@ -0,0 +1,2 @@
+test x$(hostname -i) = x$(busybox hostname -i)
+
diff --git a/i/pc104/initrd/conf/busybox/testsuite/hostname/hostname-s-works b/i/pc104/initrd/conf/busybox/testsuite/hostname/hostname-s-works
new file mode 100644
index 0000000..172b944
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/hostname/hostname-s-works
@@ -0,0 +1 @@
+test x$(hostname -s) = x$(busybox hostname -s)
diff --git a/i/pc104/initrd/conf/busybox/testsuite/hostname/hostname-works b/i/pc104/initrd/conf/busybox/testsuite/hostname/hostname-works
new file mode 100644
index 0000000..f51a406
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/hostname/hostname-works
@@ -0,0 +1 @@
+test x$(hostname) = x$(busybox hostname)
diff --git a/i/pc104/initrd/conf/busybox/testsuite/id/id-g-works b/i/pc104/initrd/conf/busybox/testsuite/id/id-g-works
new file mode 100644
index 0000000..671fc53
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/id/id-g-works
@@ -0,0 +1 @@
+test x$(id -g) = x$(busybox id -g)
diff --git a/i/pc104/initrd/conf/busybox/testsuite/id/id-u-works b/i/pc104/initrd/conf/busybox/testsuite/id/id-u-works
new file mode 100644
index 0000000..2358cb0
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/id/id-u-works
@@ -0,0 +1 @@
+test x$(id -u) = x$(busybox id -u)
diff --git a/i/pc104/initrd/conf/busybox/testsuite/id/id-un-works b/i/pc104/initrd/conf/busybox/testsuite/id/id-un-works
new file mode 100644
index 0000000..db390e7
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/id/id-un-works
@@ -0,0 +1 @@
+test x$(id -un) = x$(busybox id -un)
diff --git a/i/pc104/initrd/conf/busybox/testsuite/id/id-ur-works b/i/pc104/initrd/conf/busybox/testsuite/id/id-ur-works
new file mode 100644
index 0000000..6b0fcb0
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/id/id-ur-works
@@ -0,0 +1 @@
+test x$(id -ur) = x$(busybox id -ur)
diff --git a/i/pc104/initrd/conf/busybox/testsuite/ln/ln-creates-hard-links b/i/pc104/initrd/conf/busybox/testsuite/ln/ln-creates-hard-links
new file mode 100644
index 0000000..2f6e23f
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/ln/ln-creates-hard-links
@@ -0,0 +1,4 @@
+echo file number one > file1
+busybox ln file1 link1
+test -f file1
+test -f link1
diff --git a/i/pc104/initrd/conf/busybox/testsuite/ln/ln-creates-soft-links b/i/pc104/initrd/conf/busybox/testsuite/ln/ln-creates-soft-links
new file mode 100644
index 0000000..e875e4c
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/ln/ln-creates-soft-links
@@ -0,0 +1,4 @@
+echo file number one > file1
+busybox ln -s file1 link1
+test -L link1
+test xfile1 = x`readlink link1`
diff --git a/i/pc104/initrd/conf/busybox/testsuite/ln/ln-force-creates-hard-links b/i/pc104/initrd/conf/busybox/testsuite/ln/ln-force-creates-hard-links
new file mode 100644
index 0000000..c96b7d6
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/ln/ln-force-creates-hard-links
@@ -0,0 +1,5 @@
+echo file number one > file1
+echo file number two > link1
+busybox ln -f file1 link1
+test -f file1
+test -f link1
diff --git a/i/pc104/initrd/conf/busybox/testsuite/ln/ln-force-creates-soft-links b/i/pc104/initrd/conf/busybox/testsuite/ln/ln-force-creates-soft-links
new file mode 100644
index 0000000..cab8d1d
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/ln/ln-force-creates-soft-links
@@ -0,0 +1,5 @@
+echo file number one > file1
+echo file number two > link1
+busybox ln -f -s file1 link1
+test -L link1
+test xfile1 = x`readlink link1`
diff --git a/i/pc104/initrd/conf/busybox/testsuite/ln/ln-preserves-hard-links b/i/pc104/initrd/conf/busybox/testsuite/ln/ln-preserves-hard-links
new file mode 100644
index 0000000..47fb989
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/ln/ln-preserves-hard-links
@@ -0,0 +1,8 @@
+echo file number one > file1
+echo file number two > link1
+set +e
+busybox ln file1 link1
+if [ $? != 0 ] ; then
+ exit 0;
+fi
+exit 1;
diff --git a/i/pc104/initrd/conf/busybox/testsuite/ln/ln-preserves-soft-links b/i/pc104/initrd/conf/busybox/testsuite/ln/ln-preserves-soft-links
new file mode 100644
index 0000000..a8123ec
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/ln/ln-preserves-soft-links
@@ -0,0 +1,9 @@
+echo file number one > file1
+echo file number two > link1
+set +e
+busybox ln -s file1 link1
+if [ $? != 0 ] ; then
+ exit 0;
+fi
+exit 1;
+
diff --git a/i/pc104/initrd/conf/busybox/testsuite/ls/ls-1-works b/i/pc104/initrd/conf/busybox/testsuite/ls/ls-1-works
new file mode 100644
index 0000000..8ad484f
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/ls/ls-1-works
@@ -0,0 +1,4 @@
+[ -n "$d" ] || d=..
+ls -1 "$d" > logfile.gnu
+busybox ls -1 "$d" > logfile.bb
+cmp logfile.gnu logfile.bb
diff --git a/i/pc104/initrd/conf/busybox/testsuite/ls/ls-h-works b/i/pc104/initrd/conf/busybox/testsuite/ls/ls-h-works
new file mode 100644
index 0000000..7331262
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/ls/ls-h-works
@@ -0,0 +1,4 @@
+[ -n "$d" ] || d=..
+ls -h "$d" > logfile.gnu
+busybox ls -h "$d" > logfile.bb
+cmp logfile.gnu logfile.bb
diff --git a/i/pc104/initrd/conf/busybox/testsuite/ls/ls-l-works b/i/pc104/initrd/conf/busybox/testsuite/ls/ls-l-works
new file mode 100644
index 0000000..efc2b19
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/ls/ls-l-works
@@ -0,0 +1,4 @@
+[ -n "$d" ] || d=..
+LC_ALL=C ls -l "$d" > logfile.gnu
+busybox ls -l "$d" > logfile.bb
+diff -w logfile.gnu logfile.bb
diff --git a/i/pc104/initrd/conf/busybox/testsuite/ls/ls-s-works b/i/pc104/initrd/conf/busybox/testsuite/ls/ls-s-works
new file mode 100644
index 0000000..6c8bf36
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/ls/ls-s-works
@@ -0,0 +1,4 @@
+[ -n "$d" ] || d=..
+LC_ALL=C ls -1s "$d" > logfile.gnu
+busybox ls -1s "$d" > logfile.bb
+cmp logfile.gnu logfile.bb
diff --git a/i/pc104/initrd/conf/busybox/testsuite/md5sum/md5sum-verifies-non-binary-file b/i/pc104/initrd/conf/busybox/testsuite/md5sum/md5sum-verifies-non-binary-file
new file mode 100644
index 0000000..8566a23
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/md5sum/md5sum-verifies-non-binary-file
@@ -0,0 +1,3 @@
+touch foo
+md5sum foo > bar
+busybox md5sum -c bar
diff --git a/i/pc104/initrd/conf/busybox/testsuite/mkdir/mkdir-makes-a-directory b/i/pc104/initrd/conf/busybox/testsuite/mkdir/mkdir-makes-a-directory
new file mode 100644
index 0000000..6ca5c4d
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/mkdir/mkdir-makes-a-directory
@@ -0,0 +1,2 @@
+busybox mkdir foo
+test -d foo
diff --git a/i/pc104/initrd/conf/busybox/testsuite/mkdir/mkdir-makes-parent-directories b/i/pc104/initrd/conf/busybox/testsuite/mkdir/mkdir-makes-parent-directories
new file mode 100644
index 0000000..992facb
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/mkdir/mkdir-makes-parent-directories
@@ -0,0 +1,2 @@
+busybox mkdir -p foo/bar
+test -d foo -a -d foo/bar
diff --git a/i/pc104/initrd/conf/busybox/testsuite/mount.testroot b/i/pc104/initrd/conf/busybox/testsuite/mount.testroot
new file mode 100755
index 0000000..e18d046
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/mount.testroot
@@ -0,0 +1,183 @@
+#!/bin/sh
+
+# SUSv3 compliant mount and umount tests.
+# Copyright 2005 by Rob Landley <rob@landley.net>
+# Licensed under GPL v2, see file LICENSE for details.
+
+if [ -z "$TESTDIR" ]
+then
+ echo 'Need $TESTDIR'
+ exit 1
+fi
+
+cd "$TESTDIR"
+
+. testing.sh
+
+# If we aren't PID 1, barf.
+
+#if [ $$ -ne 1 ]
+#then
+# echo "SKIPPED: mount test requires emulation environment"
+# exit 0
+#fi
+
+# Run tests within the chroot environment.
+dochroot bash rm ls ln cat ps mknod mkdir dd grep cmp diff tail \
+ mkfs.ext2 mkfs.vfat mount umount losetup wc << EOF
+#!/bin/bash
+
+. /testing.sh
+
+mknod /dev/loop0 b 7 0
+mknod /dev/loop1 b 7 1
+
+# We need /proc to do much. Make sure we can mount it explicitly.
+
+testing "mount no proc [GNUFAIL]" "mount 2> /dev/null || echo yes" "yes\n" "" ""
+testing "mount /proc" "mount -t proc /proc /proc && ls -d /proc/1" \
+ "/proc/1\n" "" ""
+
+# Make sure the last thing in the list is /proc
+
+testing "mount list1" "mount | tail -n 1" "/proc on /proc type proc (rw)\n" \
+ "" ""
+
+
+# Create an ext2 image
+
+mkdir -p images/{ext2.dir,vfat.dir,test1,test2,test3}
+dd if=/dev/zero of=images/ext2.img bs=1M count=1 2> /dev/null
+mkfs.ext2 -F -b 1024 images/ext2.img > /dev/null 2> /dev/null
+dd if=/dev/zero of=images/vfat.img bs=1M count=1 2> /dev/null
+mkfs.vfat images/vfat.img > /dev/null
+
+# Test mount it
+
+testing "mount vfat image (explicitly autodetect type)" \
+ "mount -t auto images/vfat.img images/vfat.dir && mount | tail -n 1 | grep -o 'vfat.dir type vfat'" \
+ "vfat.dir type vfat\n" "" ""
+testing "mount ext2 image (autodetect type)" \
+ "mount images/ext2.img images/ext2.dir 2> /dev/null && mount | tail -n 1" \
+ "/dev/loop1 on /images/ext2.dir type ext2 (rw)\n" "" ""
+testing "mount remount ext2 image noatime" \
+ "mount -o remount,noatime images/ext2.dir && mount | tail -n 1" \
+ "/dev/loop1 on /images/ext2.dir type ext2 (rw,noatime)\n" "" ""
+testing "mount remount ext2 image ro remembers noatime" \
+ "mount -o remount,ro images/ext2.dir && mount | tail -n 1" \
+ "/dev/loop1 on /images/ext2.dir type ext2 (ro,noatime)\n" "" ""
+
+umount -d images/vfat.dir
+umount -d images/ext2.dir
+
+testing "mount umount freed loop device" \
+ "mount images/ext2.img images/ext2.dir && mount | tail -n 1" \
+ "/dev/loop0 on /images/ext2.dir type ext2 (rw)\n" "" ""
+
+testing "mount block device" \
+ "mount -t ext2 /dev/loop0 images/test1 && mount | tail -n 1" \
+ "/dev/loop0 on /images/test1 type ext2 (rw)\n" "" ""
+
+umount -d images/ext2.dir images/test1
+
+testing "mount remount nonexistent directory" \
+ "mount -o remount,noatime images/ext2.dir 2> /dev/null || echo yes" \
+ "yes\n" "" ""
+
+# Fun with mount -a
+
+testing "mount -a no fstab" "mount -a 2>/dev/null || echo yes" "yes\n" "" ""
+
+umount /proc
+
+# The first field is space delimited, the rest tabs.
+
+cat > /etc/fstab << FSTAB
+/proc /proc proc defaults 0 0
+# Autodetect loop, and provide flags with commas in them.
+/images/ext2.img /images/ext2.dir ext2 noatime,nodev 0 0
+# autodetect filesystem, flags without commas.
+/images/vfat.img /images/vfat.dir auto ro 0 0
+# A block device
+/dev/loop2 /images/test1 auto defaults 0 0
+# tmpfs, filesystem specific flag.
+walrus /images/test2 tmpfs size=42 0 0
+# Autodetect a bind mount.
+/images/test2 /images/test3 auto defaults 0 0
+FSTAB
+
+# Put something on loop2.
+mknod /dev/loop2 b 7 2
+cat images/ext2.img > images/ext2-2.img
+losetup /dev/loop2 images/ext2-2.img
+
+testing "mount -a" "mount -a && echo hello > /images/test2/abc && cat /images/test3/abc && (mount | wc -l)" "hello\n8\n" "" ""
+
+testing "umount -a" "umount -a && ls /proc" "" "" ""
+
+#/bin/bash < /dev/tty > /dev/tty 2> /dev/tty
+mknod /dev/console c 5 1
+/bin/bash < /dev/console > /dev/console 2> /dev/console
+EOF
+
+exit 0
+
+# Run some tests
+
+losetup nonexistent device (should return error 2)
+losetup unbound loop device (should return error 1)
+losetup bind file to loop device
+losetup bound loop device (display) (should return error 0)
+losetup filename (error)
+losetup nofile (file not found)
+losetup -d
+losetup bind with offset
+losetup -f (print first loop device)
+losetup -f filename (associate file with first loop device)
+losetup -o (past end of file) -f filename
+
+mount -a
+ with multiple entries in fstab
+ with duplicate entries in fstab
+ with relative paths in fstab
+ with user entries in fstab
+mount -o async,sync,atime,noatime,dev,nodev,exec,noexec,loop,suid,nosuid,remount,ro,rw,bind,move
+mount -r
+mount -o rw -r
+mount -w -o ro
+mount -t auto
+
+mount with relative path in fstab
+mount block device
+mount char device
+mount file (autoloop)
+mount directory (autobind)
+
+
+testing "umount with no /proc"
+testing "umount curdir"
+
+# The basic tests. These should work even with the small busybox.
+
+testing "sort" "input" "a\nb\nc\n" "c\na\nb\n" ""
+testing "sort #2" "input" "010\n1\n3\n" "3\n1\n010\n" ""
+testing "sort stdin" "" "a\nb\nc\n" "" "b\na\nc\n"
+testing "sort numeric" "-n input" "1\n3\n010\n" "3\n1\n010\n" ""
+testing "sort reverse" "-r input" "wook\nwalrus\npoint\npabst\naargh\n" \
+ "point\nwook\npabst\naargh\nwalrus\n" ""
+
+optional FEATURE_MOUNT_LOOP
+testing "umount -D"
+
+optional FEATURE_MTAB_SUPPORT
+optional FEATURE_MOUNT_NFS
+# No idea what to test here.
+
+optional UMOUNT
+optional FEATURE_UMOUNT_ALL
+testing "umount -a"
+testing "umount -r"
+testing "umount -l"
+testing "umount -f"
+
+exit $FAILCOUNT
diff --git a/i/pc104/initrd/conf/busybox/testsuite/msh/msh-supports-underscores-in-variable-names b/i/pc104/initrd/conf/busybox/testsuite/msh/msh-supports-underscores-in-variable-names
new file mode 100644
index 0000000..9c7834b
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/msh/msh-supports-underscores-in-variable-names
@@ -0,0 +1 @@
+test "`busybox msh -c 'FOO_BAR=foo; echo $FOO_BAR'`" = foo
diff --git a/i/pc104/initrd/conf/busybox/testsuite/mv/mv-files-to-dir b/i/pc104/initrd/conf/busybox/testsuite/mv/mv-files-to-dir
new file mode 100644
index 0000000..c8eaba8
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/mv/mv-files-to-dir
@@ -0,0 +1,16 @@
+echo file number one > file1
+echo file number two > file2
+ln -s file2 link1
+mkdir dir1
+touch --date='Sat Jan 29 21:24:08 PST 2000' dir1/file3
+mkdir there
+busybox mv file1 file2 link1 dir1 there
+test -f there/file1
+test -f there/file2
+test -f there/dir1/file3
+test -L there/link1
+test xfile2 = x`readlink there/link1`
+test ! -e file1
+test ! -e file2
+test ! -e link1
+test ! -e dir1/file3
diff --git a/i/pc104/initrd/conf/busybox/testsuite/mv/mv-follows-links b/i/pc104/initrd/conf/busybox/testsuite/mv/mv-follows-links
new file mode 100644
index 0000000..1fb355b
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/mv/mv-follows-links
@@ -0,0 +1,4 @@
+touch foo
+ln -s foo bar
+busybox mv bar baz
+test -f baz
diff --git a/i/pc104/initrd/conf/busybox/testsuite/mv/mv-moves-empty-file b/i/pc104/initrd/conf/busybox/testsuite/mv/mv-moves-empty-file
new file mode 100644
index 0000000..48afca4
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/mv/mv-moves-empty-file
@@ -0,0 +1,4 @@
+touch foo
+busybox mv foo bar
+test ! -e foo
+test -f bar
diff --git a/i/pc104/initrd/conf/busybox/testsuite/mv/mv-moves-file b/i/pc104/initrd/conf/busybox/testsuite/mv/mv-moves-file
new file mode 100644
index 0000000..edb4c37
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/mv/mv-moves-file
@@ -0,0 +1,3 @@
+touch foo
+busybox mv foo bar
+test ! -f foo -a -f bar
diff --git a/i/pc104/initrd/conf/busybox/testsuite/mv/mv-moves-hardlinks b/i/pc104/initrd/conf/busybox/testsuite/mv/mv-moves-hardlinks
new file mode 100644
index 0000000..eaa8215
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/mv/mv-moves-hardlinks
@@ -0,0 +1,4 @@
+touch foo
+ln foo bar
+busybox mv bar baz
+test ! -f bar -a -f baz
diff --git a/i/pc104/initrd/conf/busybox/testsuite/mv/mv-moves-large-file b/i/pc104/initrd/conf/busybox/testsuite/mv/mv-moves-large-file
new file mode 100644
index 0000000..77d088f
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/mv/mv-moves-large-file
@@ -0,0 +1,4 @@
+dd if=/dev/zero of=foo seek=10k count=1 2>/dev/null
+busybox mv foo bar
+test ! -e foo
+test -f bar
diff --git a/i/pc104/initrd/conf/busybox/testsuite/mv/mv-moves-small-file b/i/pc104/initrd/conf/busybox/testsuite/mv/mv-moves-small-file
new file mode 100644
index 0000000..065c7f1
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/mv/mv-moves-small-file
@@ -0,0 +1,4 @@
+echo I WANT > foo
+busybox mv foo bar
+test ! -e foo
+test -f bar
diff --git a/i/pc104/initrd/conf/busybox/testsuite/mv/mv-moves-symlinks b/i/pc104/initrd/conf/busybox/testsuite/mv/mv-moves-symlinks
new file mode 100644
index 0000000..c413af0
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/mv/mv-moves-symlinks
@@ -0,0 +1,6 @@
+touch foo
+ln -s foo bar
+busybox mv bar baz
+test -f foo
+test ! -e bar
+test -L baz
diff --git a/i/pc104/initrd/conf/busybox/testsuite/mv/mv-moves-unreadable-files b/i/pc104/initrd/conf/busybox/testsuite/mv/mv-moves-unreadable-files
new file mode 100644
index 0000000..bc9c313
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/mv/mv-moves-unreadable-files
@@ -0,0 +1,5 @@
+touch foo
+chmod a-r foo
+busybox mv foo bar
+test ! -e foo
+test -f bar
diff --git a/i/pc104/initrd/conf/busybox/testsuite/mv/mv-preserves-hard-links b/i/pc104/initrd/conf/busybox/testsuite/mv/mv-preserves-hard-links
new file mode 100644
index 0000000..b3ba3aa
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/mv/mv-preserves-hard-links
@@ -0,0 +1,6 @@
+# FEATURE: CONFIG_FEATURE_PRESERVE_HARDLINKS
+touch foo
+ln foo bar
+mkdir baz
+busybox mv foo bar baz
+test baz/foo -ef baz/bar
diff --git a/i/pc104/initrd/conf/busybox/testsuite/mv/mv-preserves-links b/i/pc104/initrd/conf/busybox/testsuite/mv/mv-preserves-links
new file mode 100644
index 0000000..ea565d2
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/mv/mv-preserves-links
@@ -0,0 +1,5 @@
+touch foo
+ln -s foo bar
+busybox mv bar baz
+test -L baz
+test xfoo = x`readlink baz`
diff --git a/i/pc104/initrd/conf/busybox/testsuite/mv/mv-refuses-mv-dir-to-subdir b/i/pc104/initrd/conf/busybox/testsuite/mv/mv-refuses-mv-dir-to-subdir
new file mode 100644
index 0000000..7c572c4
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/mv/mv-refuses-mv-dir-to-subdir
@@ -0,0 +1,23 @@
+echo file number one > file1
+echo file number two > file2
+ln -s file2 link1
+mkdir dir1
+touch --date='Sat Jan 29 21:24:08 PST 2000' dir1/file3
+mkdir there
+busybox mv file1 file2 link1 dir1 there
+test -f there/file1
+test -f there/file2
+test -f there/dir1/file3
+test -L there/link1
+test xfile2 = x`readlink there/link1`
+test ! -e file1
+test ! -e file2
+test ! -e link1
+test ! -e dir1/file3
+set +e
+busybox mv there there/dir1
+if [ $? != 0 ] ; then
+ exit 0;
+fi
+
+exit 1;
diff --git a/i/pc104/initrd/conf/busybox/testsuite/mv/mv-removes-source-file b/i/pc104/initrd/conf/busybox/testsuite/mv/mv-removes-source-file
new file mode 100644
index 0000000..48afca4
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/mv/mv-removes-source-file
@@ -0,0 +1,4 @@
+touch foo
+busybox mv foo bar
+test ! -e foo
+test -f bar
diff --git a/i/pc104/initrd/conf/busybox/testsuite/pidof.tests b/i/pc104/initrd/conf/busybox/testsuite/pidof.tests
new file mode 100755
index 0000000..bfde26e
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/pidof.tests
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+# pidof tests.
+# Copyright 2005 by Bernhard Fischer
+# Licensed under GPL v2, see file LICENSE for details.
+
+# AUDIT:
+
+. testing.sh
+
+# testing "test name" "options" "expected result" "file input" "stdin"
+
+testing "pidof (exit with error)" \
+ "pidof veryunlikelyoccuringbinaryname ; echo \$?" "1\n" "" ""
+testing "pidof (exit with success)" "pidof pidof > /dev/null; echo \$?" \
+ "0\n" "" ""
+# We can get away with this because it says #!/bin/sh up top.
+
+testing "pidof this" "pidof pidof.tests | grep -o -w $$" "$$\n" "" ""
+
+optional FEATURE_PIDOF_SINGLE
+testing "pidof -s" "pidof -s init" "1\n" "" ""
+
+optional FEATURE_PIDOF_OMIT
+testing "pidof -o %PPID" "pidof -o %PPID pidof.tests | grep -o -w $$" "" "" ""
+testing "pidof -o %PPID NOP" "pidof -o %PPID -s init" "1\n" "" ""
+testing "pidof -o init" "pidof -o 1 init | grep -o -w 1" "" "" ""
+
+exit $FAILCOUNT
diff --git a/i/pc104/initrd/conf/busybox/testsuite/pwd/pwd-prints-working-directory b/i/pc104/initrd/conf/busybox/testsuite/pwd/pwd-prints-working-directory
new file mode 100644
index 0000000..8575347
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/pwd/pwd-prints-working-directory
@@ -0,0 +1 @@
+test $(pwd) = $(busybox pwd)
diff --git a/i/pc104/initrd/conf/busybox/testsuite/readlink.tests b/i/pc104/initrd/conf/busybox/testsuite/readlink.tests
new file mode 100755
index 0000000..0faa6ed
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/readlink.tests
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+# Readlink tests.
+# Copyright 2006 by Natanael Copa <n@tanael.org>
+# Licensed under GPL v2, see file LICENSE for details.
+
+. testing.sh
+
+TESTDIR=readlink_testdir
+TESTFILE="$TESTDIR/testfile"
+TESTLINK="testlink"
+FAILLINK="$TESTDIR/$TESTDIR/testlink"
+
+# create the dir and test files
+mkdir -p "./$TESTDIR"
+touch "./$TESTFILE"
+ln -s "./$TESTFILE" "./$TESTLINK"
+
+testing "readlink on a file" "readlink ./$TESTFILE" "" "" ""
+testing "readlink on a link" "readlink ./$TESTLINK" "./$TESTFILE\n" "" ""
+
+optional FEATURE_READLINK_FOLLOW
+
+testing "readlink -f on a file" "readlink -f ./$TESTFILE" "$PWD/$TESTFILE\n" "" ""
+testing "readlink -f on a link" "readlink -f ./$TESTLINK" "$PWD/$TESTFILE\n" "" ""
+testing "readlink -f on an invalid link" "readlink -f ./$FAILLINK" "" "" ""
+testing "readlink -f on a wierd dir" "readlink -f $TESTDIR/../$TESTFILE" "$PWD/$TESTFILE\n" "" ""
+
+
+# clean up
+rm -r "$TESTLINK" "$TESTDIR"
+
diff --git a/i/pc104/initrd/conf/busybox/testsuite/rm/rm-removes-file b/i/pc104/initrd/conf/busybox/testsuite/rm/rm-removes-file
new file mode 100644
index 0000000..46571a9
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/rm/rm-removes-file
@@ -0,0 +1,3 @@
+touch foo
+busybox rm foo
+test ! -f foo
diff --git a/i/pc104/initrd/conf/busybox/testsuite/rmdir/rmdir-removes-parent-directories b/i/pc104/initrd/conf/busybox/testsuite/rmdir/rmdir-removes-parent-directories
new file mode 100644
index 0000000..222f5de
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/rmdir/rmdir-removes-parent-directories
@@ -0,0 +1,3 @@
+mkdir -p foo/bar
+busybox rmdir -p foo/bar
+test ! -d foo
diff --git a/i/pc104/initrd/conf/busybox/testsuite/runtest b/i/pc104/initrd/conf/busybox/testsuite/runtest
new file mode 100755
index 0000000..279f51f
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/runtest
@@ -0,0 +1,150 @@
+#!/bin/sh
+
+[ -n "$srcdir" ] || srcdir=$(pwd)
+[ -n "$bindir" ] || bindir=$(dirname $(pwd))
+PATH=$bindir:$PATH
+
+# Run old-style test.
+
+run_applet_testcase()
+{
+ local applet=$1
+ local testcase=$2
+
+ local status=0
+ local RES=
+
+ local uc_applet=$(echo $applet | tr a-z A-Z)
+ local testname=$(basename $testcase)
+
+ if grep -q "^# CONFIG_${uc_applet} is not set$" $bindir/.config; then
+ echo UNTESTED: $testname
+ return 0
+ fi
+
+ if grep -q "^# FEATURE: " $testcase; then
+ local feature=`sed -ne 's/^# FEATURE: //p' $testcase`
+
+ if grep -q "^# ${feature} is not set$" $bindir/.config; then
+ echo UNTESTED: $testname
+ return 0
+ fi
+ fi
+
+ rm -rf tmp
+ mkdir -p tmp
+ pushd tmp > /dev/null
+
+ d=$srcdir sh -x -e $testcase >.logfile.txt 2>&1 || status=$?
+
+ if [ $status -ne 0 ] ; then
+ echo FAIL: $testname
+ if [ $verbose -gt 0 ]; then
+ cat .logfile.txt
+ fi
+ status=$?
+ else
+ echo PASS: $testname
+ rm -f .logfile.txt
+ status=$?
+ fi
+
+ popd > /dev/null
+ rm -rf tmp
+
+ return $status
+}
+
+run_applet_tests()
+{
+ local applet=$1
+
+ local status=0
+
+ for testcase in $srcdir/$applet/*; do
+ if [ "$testcase" = "$srcdir/$applet/CVS" ]; then
+ continue
+ fi
+
+ if run_applet_testcase $applet $testcase; then
+ :
+ else
+ status=1
+ fi
+ done
+
+ return $status
+}
+
+
+status=0
+verbose=0
+
+if [ x"$1" = x"-v" ]; then
+ verbose=1
+ export VERBOSE=$verbose
+ shift
+fi
+
+if [ $# -ne 0 ]; then
+ applets=$(cd $srcdir ; for i in $@; do ls ${i}* ; done)
+else
+ applets=$(ls $srcdir)
+fi
+
+# Populate a directory with links to all busybox applets
+
+LINKSDIR="$bindir/runtest-tempdir-links"
+implemented=$($bindir/busybox 2>&1 |
+ while read line
+ do
+ if test x"$line" = x"Currently defined functions:"
+ then
+ xargs | sed 's/,//g'
+ break
+ fi
+ done
+ )
+rm -rf "$LINKSDIR" 2>/dev/null
+mkdir "$LINKSDIR"
+for i in $implemented
+do
+ ln -s $bindir/busybox "$LINKSDIR"/$i
+done
+
+# Set up option flags so tests can be selective.
+
+configfile=${bindir:-../../}/.config
+export OPTIONFLAGS=:$(echo $(sed -nr 's/^CONFIG_(.*)=.*/\1/p' $configfile) | sed 's/ /:/g')
+
+for applet in $applets; do
+ if [ "$applet" = "links" ]; then continue; fi
+ if [ "$applet" != "CVS" -a -d "$srcdir/$applet" ]; then
+ if run_applet_tests $applet; then
+ :
+ else
+ status=1
+ fi
+ fi
+
+ # Is this a new-style test?
+ applet=$(echo "$applet" | sed -n 's/\.tests$//p')
+ if [ ${#applet} -ne 0 ]
+ then
+ if [ ! -h "$LINKSDIR/$applet" ] && [ "${applet:0:4}" != "all_" ]
+ then
+ echo "SKIPPED: $applet (not built)"
+ continue
+ fi
+ if PATH="$LINKSDIR":$srcdir:$bindir:$PATH \
+ "${srcdir:-.}/$applet".tests
+ then
+ :
+ else
+ status=1
+ fi
+ fi
+
+done
+rm -rf "$LINKSDIR"
+exit $status
diff --git a/i/pc104/initrd/conf/busybox/testsuite/sed.tests b/i/pc104/initrd/conf/busybox/testsuite/sed.tests
new file mode 100755
index 0000000..a054de6
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/sed.tests
@@ -0,0 +1,203 @@
+#!/bin/sh
+
+# SUSv3 compliant sed tests.
+# Copyright 2005 by Rob Landley <rob@landley.net>
+# Licensed under GPL v2, see file LICENSE for details.
+
+. testing.sh
+
+# testing "description" "arguments" "result" "infile" "stdin"
+
+# Corner cases
+testing "sed no files (stdin)" 'sed ""' "hello\n" "" "hello\n"
+testing "sed explicit stdin" 'sed "" -' "hello\n" "" "hello\n"
+testing "sed handles empty lines" "sed -e 's/\$/@/'" "@\n" "" "\n"
+testing "sed stdin twice" 'sed "" - -' "hello" "" "hello"
+
+# Trailing EOF.
+# Match $, at end of each file or all files?
+
+# -e corner cases
+# without -e
+# multiple -e
+# interact with a
+# -eee arg1 arg2 arg3
+# -f corner cases
+# -e -f -e
+# -n corner cases
+# no newline at EOF?
+# -r corner cases
+# Just make sure it works.
+# -i corner cases:
+# sed -i -
+# permissions
+# -i on a symlink
+# on a directory
+# With $ last-line test
+# Continue with \
+# End of script with trailing \
+
+# command list
+testing "sed accepts blanks before command" "sed -e '1 d'" "" "" ""
+testing "sed accepts newlines in -e" "sed -e 'i\
+1
+a\
+3'" "1\n2\n3\n" "" "2\n"
+testing "sed accepts multiple -e" "sed -e 'i\' -e '1' -e 'a\' -e '3'" \
+ "1\n2\n3\n" "" "2\n"
+
+# substitutions
+testing "sed -n" "sed -n -e s/foo/bar/ -e s/bar/baz/" "" "" "foo\n"
+testing "sed s//p" "sed -e s/foo/bar/p -e s/bar/baz/p" "bar\nbaz\nbaz\n" \
+ "" "foo\n"
+testing "sed -n s//p" "sed -ne s/abc/def/p" "def\n" "" "abc\n"
+testing "sed s//g (exhaustive)" "sed -e 's/[[:space:]]*/,/g'" ",1,2,3,4,5,\n" \
+ "" "12345\n"
+testing "sed s arbitrary delimiter" "sed -e 's woo boing '" "boing\n" "" "woo\n"
+testing "sed s chains" "sed -e s/foo/bar/ -e s/bar/baz/" "baz\n" "" "foo\n"
+testing "sed s chains2" "sed -e s/foo/bar/ -e s/baz/nee/" "bar\n" "" "foo\n"
+testing "sed s [delimiter]" "sed -e 's@[@]@@'" "onetwo" "" "one@two"
+
+# branch
+testing "sed b (branch)" "sed -e 'b one;p;: one'" "foo\n" "" "foo\n"
+testing "sed b (branch with no label jumps to end)" "sed -e 'b;p'" \
+ "foo\n" "" "foo\n"
+
+# test and branch
+testing "sed t (test/branch)" "sed -e 's/a/1/;t one;p;: one;p'" \
+ "1\n1\nb\nb\nb\nc\nc\nc\n" "" "a\nb\nc\n"
+testing "sed t (test/branch clears test bit)" "sed -e 's/a/b/;:loop;t loop'" \
+ "b\nb\nc\n" "" "a\nb\nc\n"
+testing "sed T (!test/branch)" "sed -e 's/a/1/;T notone;p;: notone;p'" \
+ "1\n1\n1\nb\nb\nc\nc\n" "" "a\nb\nc\n"
+
+# Normal sed end-of-script doesn't print "c" because n flushed the pattern
+# space. If n hits EOF, pattern space is empty when script ends.
+# Query: how does this interact with no newline at EOF?
+testing "sed n (flushes pattern space, terminates early)" "sed -e 'n;p'" \
+ "a\nb\nb\nc\n" "" "a\nb\nc\n"
+# N does _not_ flush pattern space, therefore c is still in there @ script end.
+testing "sed N (doesn't flush pattern space when terminating)" "sed -e 'N;p'" \
+ "a\nb\na\nb\nc\n" "" "a\nb\nc\n"
+testing "sed address match newline" 'sed "/b/N;/b\\nc/i woo"' \
+ "a\nwoo\nb\nc\nd\n" "" "a\nb\nc\nd\n"
+
+# Multiple lines in pattern space
+testing "sed N (stops at end of input) and P (prints to first newline only)" \
+ "sed -n 'N;P;p'" "a\na\nb\n" "" "a\nb\nc\n"
+
+# Hold space
+testing "sed G (append hold space to pattern space)" 'sed G' "a\n\nb\n\nc\n\n" \
+ "" "a\nb\nc\n"
+#testing "sed g/G (swap/append hold and patter space)"
+#testing "sed g (swap hold/pattern space)"
+
+testing "sed d ends script iteration" \
+ "sed -e '/ook/d;s/ook/ping/p;i woot'" "" "" "ook\n"
+testing "sed d ends script iteration (2)" \
+ "sed -e '/ook/d;a\' -e 'bang'" "woot\nbang\n" "" "ook\nwoot\n"
+
+# Multiple files, with varying newlines and NUL bytes
+testing "sed embedded NUL" "sed -e 's/woo/bang/'" "\0bang\0woo\0" "" \
+ "\0woo\0woo\0"
+testing "sed embedded NUL g" "sed -e 's/woo/bang/g'" "bang\0bang\0" "" \
+ "woo\0woo\0"
+echo -e "/woo/a he\0llo" > sed.commands
+testing "sed NUL in command" "sed -f sed.commands" "woo\nhe\0llo\n" "" "woo"
+rm sed.commands
+
+# sed has funky behavior with newlines at the end of file. Test lots of
+# corner cases with the optional newline appending behavior.
+
+testing "sed normal newlines" "sed -e 's/woo/bang/' input -" "bang\nbang\n" \
+ "woo\n" "woo\n"
+testing "sed leave off trailing newline" "sed -e 's/woo/bang/' input -" \
+ "bang\nbang" "woo\n" "woo"
+testing "sed autoinsert newline" "sed -e 's/woo/bang/' input -" "bang\nbang" \
+ "woo" "woo"
+testing "sed empty file plus cat" "sed -e 's/nohit//' input -" "one\ntwo" \
+ "" "one\ntwo"
+testing "sed cat plus empty file" "sed -e 's/nohit//' input -" "one\ntwo" \
+ "one\ntwo" ""
+testing "sed append autoinserts newline" "sed -e '/woot/a woo' -" \
+ "woot\nwoo\n" "" "woot"
+testing "sed insert doesn't autoinsert newline" "sed -e '/woot/i woo' -" \
+ "woo\nwoot" "" "woot"
+testing "sed print autoinsert newlines" "sed -e 'p' -" "one\none" "" "one"
+testing "sed print autoinsert newlines two files" "sed -e 'p' input -" \
+ "one\none\ntwo\ntwo" "one" "two"
+testing "sed noprint, no match, no newline" "sed -ne 's/woo/bang/' input" \
+ "" "no\n" ""
+testing "sed selective matches with one nl" "sed -ne 's/woo/bang/p' input -" \
+ "a bang\nc bang\n" "a woo\nb no" "c woo\nd no"
+testing "sed selective matches insert newline" \
+ "sed -ne 's/woo/bang/p' input -" "a bang\nb bang\nd bang" \
+ "a woo\nb woo" "c no\nd woo"
+testing "sed selective matches noinsert newline" \
+ "sed -ne 's/woo/bang/p' input -" "a bang\nb bang" "a woo\nb woo" \
+ "c no\nd no"
+testing "sed clusternewline" \
+ "sed -e '/one/a 111' -e '/two/i 222' -e p input -" \
+ "one\none\n111\n222\ntwo\ntwo" "one" "two"
+testing "sed subst+write" \
+ "sed -e 's/i/z/' -e 'woutputw' input -; echo -n X; cat outputw" \
+ "thzngy\nagaznXthzngy\nagazn" "thingy" "again"
+rm outputw
+testing "sed trailing NUL" \
+ "sed 's/i/z/' input -" \
+ "a\0b\0\nc" "a\0b\0" "c"
+testing "sed escaped newline in command" \
+ "sed 's/a/z\\
+z/' input" \
+ "z\nz" "a" ""
+
+# Test end-of-file matching behavior
+
+testing "sed match EOF" "sed -e '"'$p'"'" "hello\nthere\nthere" "" \
+ "hello\nthere"
+testing "sed match EOF two files" "sed -e '"'$p'"' input -" \
+ "one\ntwo\nthree\nfour\nfour" "one\ntwo" "three\nfour"
+# sed match EOF inline: gnu sed 4.1.5 outputs this:
+#00000000 6f 6e 65 0a 6f 6f 6b 0a 6f 6f 6b 0a 74 77 6f 0a |one.ook.ook.two.|
+#00000010 0a 74 68 72 65 65 0a 6f 6f 6b 0a 6f 6f 6b 0a 66 |.three.ook.ook.f|
+#00000020 6f 75 72 |our|
+# which looks buggy to me.
+echo -ne "three\nfour" > input2
+testing "sed match EOF inline" \
+ "sed -e '"'$i ook'"' -i input input2 && cat input input2" \
+ "one\nook\ntwothree\nook\nfour" "one\ntwo" ""
+rm input2
+
+# Test lie-to-autoconf
+
+testing "sed lie-to-autoconf" "sed --version | grep -o 'GNU sed version '" \
+ "GNU sed version \n" "" ""
+
+# Jump to nonexistent label
+testing "sed nonexistent label" "sed -e 'b walrus' 2> /dev/null || echo yes" \
+ "yes\n" "" ""
+
+testing "sed backref from empty s uses range regex" \
+ "sed -e '/woot/s//eep \0 eep/'" "eep woot eep" "" "woot"
+
+testing "sed backref from empty s uses range regex with newline" \
+ "sed -e '/woot/s//eep \0 eep/'" "eep woot eep\n" "" "woot\n"
+
+# -i with no filename
+
+touch ./- # Detect gnu failure mode here.
+testing "sed -i with no arg [GNUFAIL]" "sed -e '' -i 2> /dev/null || echo yes" \
+ "yes\n" "" ""
+rm ./- # Clean up
+
+testing "sed s/xxx/[/" "sed -e 's/xxx/[/'" "[\n" "" "xxx\n"
+
+# Ponder this a bit more, why "woo not found" from gnu version?
+#testing "sed doesn't substitute in deleted line" \
+# "sed -e '/ook/d;s/ook//;t woo;a bang;'" "bang" "" "ook\n"
+
+# This makes both seds very unhappy. Why?
+#testing "sed -g (exhaustive)" "sed -e 's/[[:space:]]*/,/g'" ",1,2,3,4,5," \
+# "" "12345"
+
+exit $FAILCOUNT
diff --git a/i/pc104/initrd/conf/busybox/testsuite/seq.tests b/i/pc104/initrd/conf/busybox/testsuite/seq.tests
new file mode 100755
index 0000000..ebb44e7
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/seq.tests
@@ -0,0 +1,36 @@
+#!/bin/sh
+
+# SUSv3 compliant seq tests.
+# Copyright 2006 by Rob Landley <rob@landley.net>
+# Licensed under GPL v2, see file LICENSE for details.
+
+# AUDIT: Full SUSv3 coverage (except internationalization).
+
+. testing.sh
+
+# testing "test name" "options" "expected result" "file input" "stdin"
+# file input will be file called "input"
+# test can create a file "actual" instead of writing to stdout
+
+# Test exit status
+
+testing "seq (exit with error)" "seq 2> /dev/null || echo yes" "yes\n" "" ""
+testing "seq (exit with error)" "seq 1 2 3 4 2> /dev/null || echo yes" \
+ "yes\n" "" ""
+testing "seq one argument" "seq 3" "1\n2\n3\n" "" ""
+testing "seq two arguments" "seq 5 7" "5\n6\n7\n" "" ""
+testing "seq two arguments reversed" "seq 7 5" "" "" ""
+testing "seq two arguments equal" "seq 3 3" "3\n" "" ""
+testing "seq two arguments equal, arbitrary negative step" "seq 1 -15 1" \
+ "1\n" "" ""
+testing "seq two arguments equal, arbitrary positive step" "seq 1 +15 1" \
+ "1\n" "" ""
+testing "seq count up by 2" "seq 4 2 8" "4\n6\n8\n" "" ""
+testing "seq count down by 2" "seq 8 -2 4" "8\n6\n4\n" "" ""
+testing "seq count wrong way #1" "seq 4 -2 8" "" "" ""
+testing "seq count wrong way #2" "seq 8 2 4" "" "" ""
+testing "seq count by .3" "seq 3 .3 4" "3\n3.3\n3.6\n3.9\n" "" ""
+testing "seq count by -.9" "seq .7 -.9 -2.2" "0.7\n-0.2\n-1.1\n-2\n" "" ""
+testing "seq count by zero" "seq 4 0 8 | head -n 10" "" "" ""
+
+exit $FAILCOUNT
diff --git a/i/pc104/initrd/conf/busybox/testsuite/sort.tests b/i/pc104/initrd/conf/busybox/testsuite/sort.tests
new file mode 100755
index 0000000..1db7870
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/sort.tests
@@ -0,0 +1,113 @@
+#!/bin/bash
+
+# SUSv3 compliant sort tests.
+# Copyright 2005 by Rob Landley <rob@landley.net>
+# Licensed under GPL v2, see file LICENSE for details.
+
+. testing.sh
+
+# The basic tests. These should work even with the small busybox.
+
+testing "sort" "sort input" "a\nb\nc\n" "c\na\nb\n" ""
+testing "sort #2" "sort input" "010\n1\n3\n" "3\n1\n010\n" ""
+testing "sort stdin" "sort" "a\nb\nc\n" "" "b\na\nc\n"
+testing "sort numeric" "sort -n input" "1\n3\n010\n" "3\n1\n010\n" ""
+testing "sort reverse" "sort -r input" "wook\nwalrus\npoint\npabst\naargh\n" \
+ "point\nwook\npabst\naargh\nwalrus\n" ""
+
+# These tests require the full option set.
+
+optional FEATURE_SORT_BIG
+# Longish chunk of data re-used by the next few tests
+
+data="42 1 3 woot
+42 1 010 zoology
+egg 1 2 papyrus
+7 3 42 soup
+999 3 0 algebra
+"
+
+# Sorting with keys
+
+testing "sort one key" "sort -k4,4 input" \
+"999 3 0 algebra
+egg 1 2 papyrus
+7 3 42 soup
+42 1 3 woot
+42 1 010 zoology
+" "$data" ""
+
+testing "sort key range with numeric option" "sort -k2,3n input" \
+"42 1 010 zoology
+42 1 3 woot
+egg 1 2 papyrus
+7 3 42 soup
+999 3 0 algebra
+" "$data" ""
+
+# Busybox is definitely doing this one wrong just now. FIXME
+
+testing "sort key range with numeric option and global reverse" \
+"sort -k2,3n -r input" \
+"egg 1 2 papyrus
+42 1 3 woot
+42 1 010 zoology
+999 3 0 algebra
+7 3 42 soup
+" "$data" ""
+
+#
+
+testing "sort key range with multiple options" "sort -k2,3rn input" \
+"7 3 42 soup
+999 3 0 algebra
+42 1 010 zoology
+42 1 3 woot
+egg 1 2 papyrus
+" "$data" ""
+
+testing "sort key range with two -k options" "sort -k 2,2n -k 1,1r input" "\
+d 2
+b 2
+c 3
+" "\
+c 3
+b 2
+d 2
+" ""
+
+testing "sort with non-default leading delim 1" "sort -n -k2 -t/ input" "\
+/a/2
+/b/1
+" "\
+/a/2
+/b/1
+" ""
+
+testing "sort with non-default leading delim 2" "sort -n -k3 -t/ input" "\
+/b/1
+/a/2
+" "\
+/b/1
+/a/2
+" ""
+
+testing "sort with non-default leading delim 3" "sort -n -k3 -t/ input" "\
+//a/2
+//b/1
+" "\
+//a/2
+//b/1
+" ""
+
+testing "sort -u should consider field only when discarding" "sort -u -k2 input" "\
+a c
+" "\
+a c
+b c
+" ""
+
+testing "sort key doesn't strip leading blanks, disables fallback global sort" \
+"sort -n -k2 -t ' '" " a \n 1 \n 2 \n" "" " 2 \n 1 \n a \n"
+
+exit $FAILCOUNT
diff --git a/i/pc104/initrd/conf/busybox/testsuite/strings/strings-works-like-GNU b/i/pc104/initrd/conf/busybox/testsuite/strings/strings-works-like-GNU
new file mode 100644
index 0000000..2d64710
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/strings/strings-works-like-GNU
@@ -0,0 +1,9 @@
+rm -f foo bar
+strings -af ../../busybox > foo
+busybox strings -af ../../busybox > bar
+set +e
+test ! -f foo -a -f bar
+if [ $? = 0 ] ; then
+ set -e
+ diff -q foo bar
+fi
diff --git a/i/pc104/initrd/conf/busybox/testsuite/sum.tests b/i/pc104/initrd/conf/busybox/testsuite/sum.tests
new file mode 100755
index 0000000..0993f03
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/sum.tests
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+# unit test for sum.
+# Copyright 2007 by Bernhard Fischer
+# Licensed under GPL v2 or later, see file LICENSE for details.
+
+# AUDIT: Unit tests for sum
+
+. testing.sh
+
+# testing "test name" "options" "expected result" "file input" "stdin"
+# file input will be file called "input"
+# test can create a file "actual" instead of writing to stdout
+
+testing "sum -r file doesn't print file's name" \
+ "sum -r $0 | grep -c $0 && echo wrongly_printed_filename || echo yes" \
+ "0\nyes\n" "" ""
+testing "sum -r file file does print both names" \
+ "sum -r $0 $0 | grep -c $0 && echo yes || echo wrongly_omitted_filename" \
+ "2\nyes\n" "" ""
+testing "sum -s file does print file's name" \
+ "sum -s $0 | grep -c $0 && echo yes || echo wrongly_omitted_filename" \
+ "1\nyes\n" "" ""
+exit $FAILCOUNT
diff --git a/i/pc104/initrd/conf/busybox/testsuite/tail/tail-n-works b/i/pc104/initrd/conf/busybox/testsuite/tail/tail-n-works
new file mode 100644
index 0000000..27a905f
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/tail/tail-n-works
@@ -0,0 +1,4 @@
+[ -n "$d" ] || d=..
+tail -n 2 "$d/README" > logfile.gnu
+busybox tail -n 2 "$d/README" > logfile.bb
+cmp logfile.gnu logfile.bb
diff --git a/i/pc104/initrd/conf/busybox/testsuite/tail/tail-works b/i/pc104/initrd/conf/busybox/testsuite/tail/tail-works
new file mode 100644
index 0000000..27a905f
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/tail/tail-works
@@ -0,0 +1,4 @@
+[ -n "$d" ] || d=..
+tail -n 2 "$d/README" > logfile.gnu
+busybox tail -n 2 "$d/README" > logfile.bb
+cmp logfile.gnu logfile.bb
diff --git a/i/pc104/initrd/conf/busybox/testsuite/tar/tar-archives-multiple-files b/i/pc104/initrd/conf/busybox/testsuite/tar/tar-archives-multiple-files
new file mode 100644
index 0000000..245d9e9
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/tar/tar-archives-multiple-files
@@ -0,0 +1,6 @@
+# FEATURE: CONFIG_FEATURE_TAR_CREATE
+touch foo bar
+busybox tar cf foo.tar foo bar
+rm foo bar
+tar xf foo.tar
+test -f foo -a -f bar
diff --git a/i/pc104/initrd/conf/busybox/testsuite/tar/tar-complains-about-missing-file b/i/pc104/initrd/conf/busybox/testsuite/tar/tar-complains-about-missing-file
new file mode 100644
index 0000000..26e8cbb
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/tar/tar-complains-about-missing-file
@@ -0,0 +1,3 @@
+touch foo
+tar cf foo.tar foo
+! busybox tar xf foo.tar bar
diff --git a/i/pc104/initrd/conf/busybox/testsuite/tar/tar-demands-at-least-one-ctx b/i/pc104/initrd/conf/busybox/testsuite/tar/tar-demands-at-least-one-ctx
new file mode 100644
index 0000000..85e7f60
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/tar/tar-demands-at-least-one-ctx
@@ -0,0 +1 @@
+! busybox tar v
diff --git a/i/pc104/initrd/conf/busybox/testsuite/tar/tar-demands-at-most-one-ctx b/i/pc104/initrd/conf/busybox/testsuite/tar/tar-demands-at-most-one-ctx
new file mode 100644
index 0000000..130d0e7
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/tar/tar-demands-at-most-one-ctx
@@ -0,0 +1 @@
+! busybox tar tx
diff --git a/i/pc104/initrd/conf/busybox/testsuite/tar/tar-extracts-all-subdirs b/i/pc104/initrd/conf/busybox/testsuite/tar/tar-extracts-all-subdirs
new file mode 100644
index 0000000..886c37c
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/tar/tar-extracts-all-subdirs
@@ -0,0 +1,12 @@
+# FEATURE: CONFIG_FEATURE_TAR_CREATE
+mkdir -p foo/{1,2,3}
+mkdir -p foo/1/{10,11}
+mkdir -p foo/1/10/{100,101,102}
+tar cf foo.tar -C foo .
+rm -rf foo/*
+busybox tar xf foo.tar -C foo ./1/10
+find foo | sort >logfile.bb
+rm -rf foo/*
+tar xf foo.tar -C foo ./1/10
+find foo | sort >logfile.gnu
+cmp logfile.gnu logfile.bb
diff --git a/i/pc104/initrd/conf/busybox/testsuite/tar/tar-extracts-file b/i/pc104/initrd/conf/busybox/testsuite/tar/tar-extracts-file
new file mode 100644
index 0000000..ca72f24
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/tar/tar-extracts-file
@@ -0,0 +1,5 @@
+touch foo
+tar cf foo.tar foo
+rm foo
+busybox tar xf foo.tar
+test -f foo
diff --git a/i/pc104/initrd/conf/busybox/testsuite/tar/tar-extracts-from-standard-input b/i/pc104/initrd/conf/busybox/testsuite/tar/tar-extracts-from-standard-input
new file mode 100644
index 0000000..a30e9f0
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/tar/tar-extracts-from-standard-input
@@ -0,0 +1,5 @@
+touch foo
+tar cf foo.tar foo
+rm foo
+cat foo.tar | busybox tar x
+test -f foo
diff --git a/i/pc104/initrd/conf/busybox/testsuite/tar/tar-extracts-multiple-files b/i/pc104/initrd/conf/busybox/testsuite/tar/tar-extracts-multiple-files
new file mode 100644
index 0000000..8ae8cdd
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/tar/tar-extracts-multiple-files
@@ -0,0 +1,6 @@
+touch foo bar
+busybox tar cf foo.tar foo bar
+rm foo bar
+busybox tar -xf foo.tar
+test -f foo
+test -f bar
diff --git a/i/pc104/initrd/conf/busybox/testsuite/tar/tar-extracts-to-standard-output b/i/pc104/initrd/conf/busybox/testsuite/tar/tar-extracts-to-standard-output
new file mode 100644
index 0000000..ca48e36
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/tar/tar-extracts-to-standard-output
@@ -0,0 +1,3 @@
+echo foo > foo
+tar cf foo.tar foo
+cat foo.tar | busybox tar Ox | cmp foo -
diff --git a/i/pc104/initrd/conf/busybox/testsuite/tar/tar-handles-cz-options b/i/pc104/initrd/conf/busybox/testsuite/tar/tar-handles-cz-options
new file mode 100644
index 0000000..5b55e46
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/tar/tar-handles-cz-options
@@ -0,0 +1,5 @@
+# FEATURE: CONFIG_FEATURE_TAR_CREATE
+# FEATURE: CONFIG_FEATURE_TAR_GZIP
+touch foo
+busybox tar czf foo.tar.gz foo
+gzip -d foo.tar.gz
diff --git a/i/pc104/initrd/conf/busybox/testsuite/tar/tar-handles-empty-include-and-non-empty-exclude-list b/i/pc104/initrd/conf/busybox/testsuite/tar/tar-handles-empty-include-and-non-empty-exclude-list
new file mode 100644
index 0000000..5033642
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/tar/tar-handles-empty-include-and-non-empty-exclude-list
@@ -0,0 +1,6 @@
+# FEATURE: CONFIG_FEATURE_TAR_FROM
+# FEATURE: CONFIG_FEATURE_TAR_CREATE
+touch foo
+tar cf foo.tar foo
+echo foo >foo.exclude
+busybox tar xf foo.tar -X foo.exclude
diff --git a/i/pc104/initrd/conf/busybox/testsuite/tar/tar-handles-exclude-and-extract-lists b/i/pc104/initrd/conf/busybox/testsuite/tar/tar-handles-exclude-and-extract-lists
new file mode 100644
index 0000000..2de0f0e
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/tar/tar-handles-exclude-and-extract-lists
@@ -0,0 +1,8 @@
+# FEATURE: CONFIG_FEATURE_TAR_FROM
+# FEATURE: CONFIG_FEATURE_TAR_CREATE
+touch foo bar baz
+tar cf foo.tar foo bar baz
+echo foo >foo.exclude
+rm foo bar baz
+busybox tar xf foo.tar foo bar -X foo.exclude
+test ! -f foo -a -f bar -a ! -f baz
diff --git a/i/pc104/initrd/conf/busybox/testsuite/tar/tar-handles-multiple-X-options b/i/pc104/initrd/conf/busybox/testsuite/tar/tar-handles-multiple-X-options
new file mode 100644
index 0000000..155b27e
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/tar/tar-handles-multiple-X-options
@@ -0,0 +1,10 @@
+# FEATURE: CONFIG_FEATURE_TAR_FROM
+# FEATURE: CONFIG_FEATURE_TAR_CREATE
+touch foo
+touch bar
+tar cf foo.tar foo bar
+echo foo > foo.exclude
+echo bar > bar.exclude
+rm foo bar
+busybox tar xf foo.tar -X foo.exclude -X bar.exclude
+test ! -f foo -a ! -f bar
diff --git a/i/pc104/initrd/conf/busybox/testsuite/tar/tar-handles-nested-exclude b/i/pc104/initrd/conf/busybox/testsuite/tar/tar-handles-nested-exclude
new file mode 100644
index 0000000..39013a1
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/tar/tar-handles-nested-exclude
@@ -0,0 +1,9 @@
+# FEATURE: CONFIG_FEATURE_TAR_FROM
+# FEATURE: CONFIG_FEATURE_TAR_CREATE
+mkdir foo
+touch foo/bar
+tar cf foo.tar foo
+rm -rf foo
+echo foo/bar >foobar.exclude
+busybox tar xf foo.tar foo -X foobar.exclude
+test -d foo -a ! -f foo/bar
diff --git a/i/pc104/initrd/conf/busybox/testsuite/taskset.tests b/i/pc104/initrd/conf/busybox/testsuite/taskset.tests
new file mode 100755
index 0000000..6001c3e
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/taskset.tests
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+# Copyright 2006 Bernhard Fischer
+# Licensed under GPL v2 or later, see file LICENSE for details.
+
+. testing.sh
+a="taskset"
+
+# testing "test name" "opts" "expected result" "file inp" "stdin"
+testing "taskset (get from pid 1)" "$a -p1 >/dev/null;echo \$?" "0\n" "" ""
+testing "taskset (invalid pid)" "$a -p0 >/dev/null 2>&1;echo \$?" "1\n" "" ""
+testing "taskset (set_aff, needs CAP_SYS_NICE)" \
+ "$a 0x1 $SHELL -c $a\ -p\ \$$\|grep\ \"current\ affinity\ mask:\ 1\" >/dev/null;echo \$?" \
+ "0\n" "" ""
+
+unset a
+exit $FAILCOUNT
diff --git a/i/pc104/initrd/conf/busybox/testsuite/tee/tee-appends-input b/i/pc104/initrd/conf/busybox/testsuite/tee/tee-appends-input
new file mode 100644
index 0000000..cff20bf
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/tee/tee-appends-input
@@ -0,0 +1,5 @@
+echo i\'m a little teapot >foo
+cp foo bar
+echo i\'m a little teapot >>foo
+echo i\'m a little teapot | busybox tee -a bar >/dev/null
+cmp foo bar
diff --git a/i/pc104/initrd/conf/busybox/testsuite/tee/tee-tees-input b/i/pc104/initrd/conf/busybox/testsuite/tee/tee-tees-input
new file mode 100644
index 0000000..26e2173
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/tee/tee-tees-input
@@ -0,0 +1,3 @@
+echo i\'m a little teapot >foo
+echo i\'m a little teapot | busybox tee bar >baz
+cmp foo bar && cmp foo baz
diff --git a/i/pc104/initrd/conf/busybox/testsuite/testing.sh b/i/pc104/initrd/conf/busybox/testsuite/testing.sh
new file mode 100755
index 0000000..e253e1a
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/testing.sh
@@ -0,0 +1,154 @@
+# Simple test harness infrastructurei for BusyBox
+#
+# Copyright 2005 by Rob Landley
+#
+# License is GPLv2, see LICENSE in the busybox tarball for full license text.
+
+# This file defines two functions, "testing" and "optionflag"
+
+# The following environment variables may be set to enable optional behavior
+# in "testing":
+# VERBOSE - Print the diff -u of each failed test case.
+# DEBUG - Enable command tracing.
+# SKIP - do not perform this test (this is set by "optionflag")
+#
+# The "testing" function takes five arguments:
+# $1) Description to display when running command
+# $2) Command line arguments to command
+# $3) Expected result (on stdout)
+# $4) Data written to file "input"
+# $5) Data written to stdin
+#
+# The exit value of testing is the exit value of the command it ran.
+#
+# The environment variable "FAILCOUNT" contains a cumulative total of the
+# number of failed tests.
+
+# The "optional" function is used to skip certain tests, ala:
+# optionflag CONFIG_FEATURE_THINGY
+#
+# The "optional" function checks the environment variable "OPTIONFLAGS",
+# which is either empty (in which case it always clears SKIP) or
+# else contains a colon-separated list of features (in which case the function
+# clears SKIP if the flag was found, or sets it to 1 if the flag was not found).
+
+export FAILCOUNT=0
+export SKIP=
+
+# Helper functions
+
+optional()
+{
+ option=`echo "$OPTIONFLAGS" | egrep "(^|:)$1(:|\$)"`
+ # Not set?
+ if [ -z "$1" ] || [ -z "$OPTIONFLAGS" ] || [ ${#option} -ne 0 ]
+ then
+ SKIP=""
+ return
+ fi
+ SKIP=1
+}
+
+# The testing function
+
+testing ()
+{
+ NAME="$1"
+ [ -z "$1" ] && NAME=$2
+
+ if [ $# -ne 5 ]
+ then
+ echo "Test $NAME has the wrong number of arguments ($# $*)" >&2
+ exit
+ fi
+
+ [ -n "$DEBUG" ] && set -x
+
+ if [ -n "$SKIP" ]
+ then
+ echo "SKIPPED: $NAME"
+ return 0
+ fi
+
+ echo -ne "$3" > expected
+ echo -ne "$4" > input
+ [ -z "$VERBOSE" ] || echo "echo '$5' | $2"
+ echo -ne "$5" | eval "$2" > actual
+ RETVAL=$?
+
+ cmp expected actual > /dev/null
+ if [ $? -ne 0 ]
+ then
+ FAILCOUNT=$[$FAILCOUNT+1]
+ echo "FAIL: $NAME"
+ [ -n "$VERBOSE" ] && diff -u expected actual
+ else
+ echo "PASS: $NAME"
+ fi
+ rm -f input expected actual
+
+ [ -n "$DEBUG" ] && set +x
+
+ return $RETVAL
+}
+
+# Recursively grab an executable and all the libraries needed to run it.
+# Source paths beginning with / will be copied into destpath, otherwise
+# the file is assumed to already be there and only its library dependencies
+# are copied.
+
+function mkchroot
+{
+ [ $# -lt 2 ] && return
+
+ echo -n .
+
+ dest=$1
+ shift
+ for i in "$@"
+ do
+ [ "${i:0:1}" == "/" ] || i=$(which $i)
+ [ -f "$dest/$i" ] && continue
+ if [ -e "$i" ]
+ then
+ d=`echo "$i" | grep -o '.*/'` &&
+ mkdir -p "$dest/$d" &&
+ cat "$i" > "$dest/$i" &&
+ chmod +x "$dest/$i"
+ else
+ echo "Not found: $i"
+ fi
+ mkchroot "$dest" $(ldd "$i" | egrep -o '/.* ')
+ done
+}
+
+# Set up a chroot environment and run commands within it.
+# Needed commands listed on command line
+# Script fed to stdin.
+
+function dochroot
+{
+ mkdir tmpdir4chroot
+ mount -t ramfs tmpdir4chroot tmpdir4chroot
+ mkdir -p tmpdir4chroot/{etc,sys,proc,tmp,dev}
+ cp -L testing.sh tmpdir4chroot
+
+ # Copy utilities from command line arguments
+
+ echo -n "Setup chroot"
+ mkchroot tmpdir4chroot $*
+ echo
+
+ mknod tmpdir4chroot/dev/tty c 5 0
+ mknod tmpdir4chroot/dev/null c 1 3
+ mknod tmpdir4chroot/dev/zero c 1 5
+
+ # Copy script from stdin
+
+ cat > tmpdir4chroot/test.sh
+ chmod +x tmpdir4chroot/test.sh
+ chroot tmpdir4chroot /test.sh
+ umount -l tmpdir4chroot
+ rmdir tmpdir4chroot
+}
+
diff --git a/i/pc104/initrd/conf/busybox/testsuite/touch/touch-creates-file b/i/pc104/initrd/conf/busybox/testsuite/touch/touch-creates-file
new file mode 100644
index 0000000..4b49354
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/touch/touch-creates-file
@@ -0,0 +1,2 @@
+busybox touch foo
+test -f foo
diff --git a/i/pc104/initrd/conf/busybox/testsuite/touch/touch-does-not-create-file b/i/pc104/initrd/conf/busybox/testsuite/touch/touch-does-not-create-file
new file mode 100644
index 0000000..8852592
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/touch/touch-does-not-create-file
@@ -0,0 +1,2 @@
+busybox touch -c foo
+test ! -f foo
diff --git a/i/pc104/initrd/conf/busybox/testsuite/touch/touch-touches-files-after-non-existent-file b/i/pc104/initrd/conf/busybox/testsuite/touch/touch-touches-files-after-non-existent-file
new file mode 100644
index 0000000..a869ec2
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/touch/touch-touches-files-after-non-existent-file
@@ -0,0 +1,3 @@
+touch -t 198001010000 bar
+busybox touch -c foo bar
+test x"`find bar -mtime -1`" = xbar
diff --git a/i/pc104/initrd/conf/busybox/testsuite/tr/tr-d-works b/i/pc104/initrd/conf/busybox/testsuite/tr/tr-d-works
new file mode 100644
index 0000000..d939e8b
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/tr/tr-d-works
@@ -0,0 +1,4 @@
+echo testing | tr -d aeiou > logfile.gnu
+echo testing | busybox tr -d aeiou > logfile.bb
+
+cmp logfile.gnu logfile.bb
diff --git a/i/pc104/initrd/conf/busybox/testsuite/tr/tr-non-gnu b/i/pc104/initrd/conf/busybox/testsuite/tr/tr-non-gnu
new file mode 100644
index 0000000..ffa6951
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/tr/tr-non-gnu
@@ -0,0 +1 @@
+echo fdhrnzvfu bffvsentr | busybox tr '[a-z]' '[n-z][a-m]'
diff --git a/i/pc104/initrd/conf/busybox/testsuite/tr/tr-works b/i/pc104/initrd/conf/busybox/testsuite/tr/tr-works
new file mode 100644
index 0000000..9b2e90e
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/tr/tr-works
@@ -0,0 +1,24 @@
+run_tr ()
+{
+ echo -n "echo '$1' | tr '$2' '$3': "
+ echo "$1" | $bb tr "$2" "$3"
+ echo
+}
+tr_test ()
+{
+ run_tr "cbaab" abc zyx
+ run_tr "TESTING A B C" '[A-Z]' '[a-z]'
+ run_tr "abc[]" "a[b" AXB
+ run_tr abc '[:alpha:]' A-ZA-Z
+ run_tr abc56 '[:alnum:]' A-ZA-Zxxxxxxxxxx
+ run_tr 012 '[:digit:]' abcdefghi
+ run_tr abc56 '[:lower:]' '[:upper:]'
+ run_tr " " '[:space:]' 12345
+ run_tr " " '[:blank:]' 12
+ run_tr 'a b' '[= =]' X
+ run_tr "[:" '[:' ab
+}
+
+bb= tr_test > logfile.gnu
+bb=busybox tr_test > logfile.bb
+cmp logfile.gnu logfile.bb
diff --git a/i/pc104/initrd/conf/busybox/testsuite/true/true-is-silent b/i/pc104/initrd/conf/busybox/testsuite/true/true-is-silent
new file mode 100644
index 0000000..1d1bdb2
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/true/true-is-silent
@@ -0,0 +1 @@
+busybox true 2>&1 | cmp - /dev/null
diff --git a/i/pc104/initrd/conf/busybox/testsuite/true/true-returns-success b/i/pc104/initrd/conf/busybox/testsuite/true/true-returns-success
new file mode 100644
index 0000000..cdf2d55
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/true/true-returns-success
@@ -0,0 +1 @@
+busybox true
diff --git a/i/pc104/initrd/conf/busybox/testsuite/umlwrapper.sh b/i/pc104/initrd/conf/busybox/testsuite/umlwrapper.sh
new file mode 100755
index 0000000..e55e4db
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/umlwrapper.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+# Wrapper for User Mode Linux emulation environment
+
+RUNFILE="$(pwd)/${1}.testroot"
+if [ -z "$RUNFILE" ] || [ ! -x "$RUNFILE" ]
+then
+ echo "Can't run '$RUNFILE'"
+ exit 1
+fi
+
+shift
+
+if [ -z $(which linux) ]
+then
+ echo "No User Mode Linux."
+ exit 1;
+fi
+
+linux rootfstype=hostfs rw init="$RUNFILE" TESTDIR=`pwd` PATH="$PATH" $* quiet
diff --git a/i/pc104/initrd/conf/busybox/testsuite/uniq.tests b/i/pc104/initrd/conf/busybox/testsuite/uniq.tests
new file mode 100755
index 0000000..49d4bed
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/uniq.tests
@@ -0,0 +1,70 @@
+#!/bin/sh
+
+# SUSv3 compliant uniq tests.
+# Copyright 2005 by Rob Landley <rob@landley.net>
+# Licensed under GPL v2, see file LICENSE for details.
+
+# AUDIT: Full SUSv3 coverage (except internationalization).
+
+. testing.sh
+
+# testing "test name" "options" "expected result" "file input" "stdin"
+# file input will be file called "input"
+# test can create a file "actual" instead of writing to stdout
+
+# Test exit status
+
+testing "uniq (exit with error)" "uniq nonexistent 2> /dev/null || echo yes" \
+ "yes\n" "" ""
+testing "uniq (exit success)" "uniq /dev/null && echo yes" "yes\n" "" ""
+
+# Test various data sources and destinations
+
+testing "uniq (default to stdin)" "uniq" "one\ntwo\nthree\n" "" \
+ "one\ntwo\ntwo\nthree\nthree\nthree\n"
+testing "uniq - (specify stdin)" "uniq -" "one\ntwo\nthree\n" "" \
+ "one\ntwo\ntwo\nthree\nthree\nthree\n"
+testing "uniq input (specify file)" "uniq input" "one\ntwo\nthree\n" \
+ "one\ntwo\ntwo\nthree\nthree\nthree\n" ""
+
+testing "uniq input outfile (two files)" "uniq input actual > /dev/null" \
+ "one\ntwo\nthree\n" "one\ntwo\ntwo\nthree\nthree\nthree\n" ""
+testing "uniq (stdin) outfile" "uniq - actual" \
+ "one\ntwo\nthree\n" "" "one\ntwo\ntwo\nthree\nthree\nthree\n"
+# Note: SUSv3 doesn't seem to require support for "-" output, but we do anyway.
+testing "uniq input - (specify stdout)" "uniq input -" \
+ "one\ntwo\nthree\n" "one\ntwo\ntwo\nthree\nthree\nthree\n" ""
+
+
+#-f skip fields
+#-s skip chars
+#-c occurrences
+#-d dups only
+#-u
+
+# Test various command line options
+
+# Leading whitespace is a minor technical violation of the spec,
+# but since gnu does it...
+testing "uniq -c (occurrence count)" "uniq -c | sed 's/^[ \t]*//'" \
+ "1 one\n2 two\n3 three\n" "" \
+ "one\ntwo\ntwo\nthree\nthree\nthree\n"
+testing "uniq -d (dups only) " "uniq -d" "two\nthree\n" "" \
+ "one\ntwo\ntwo\nthree\nthree\nthree\n"
+
+testing "uniq -f -s (skip fields and chars)" "uniq -f2 -s 3" \
+"cc dd ee8
+aa bb cc9
+" "" \
+"cc dd ee8
+bb cc dd8
+aa bb cc9
+"
+
+# -d is "Suppress the writing fo lines that are not repeated in the input."
+# -u is "Suppress the writing of lines that are repeated in the input."
+# Therefore, together this means they should produce no output.
+testing "uniq -u and -d produce no output" "uniq -d -u" "" "" \
+ "one\ntwo\ntwo\nthree\nthree\nthree\n"
+
+exit $FAILCOUNT
diff --git a/i/pc104/initrd/conf/busybox/testsuite/unzip.tests b/i/pc104/initrd/conf/busybox/testsuite/unzip.tests
new file mode 100755
index 0000000..7781488
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/unzip.tests
@@ -0,0 +1,38 @@
+#!/bin/sh
+
+# Tests for unzip.
+# Copyright 2006 Rob Landley <rob@landley.net>
+# Copyright 2006 Glenn McGrath <bug1@ihug.co.nz>
+# Licensed under GPL v2, see file LICENSE for details.
+
+. testing.sh
+
+# testing "test name" "options" "expected result" "file input" "stdin"
+# file input will be file called "input"
+# test can create a file "actual" instead of writing to stdout
+
+# Create a scratch directory
+
+mkdir temp
+cd temp
+
+# Create test file to work with.
+
+mkdir foo
+touch foo/bar
+zip foo.zip foo foo/bar > /dev/null
+rm -f foo/bar
+rmdir foo
+
+# Test that unzipping just foo doesn't create bar.
+testing "unzip (subdir only)" "unzip -q foo.zip foo/ && test -d foo && test ! -f foo/bar && echo yes" "yes\n" "" ""
+
+rmdir foo
+rm foo.zip
+
+# Clean up scratch directory.
+
+cd ..
+rm -rf temp
+
+exit $FAILCOUNT
diff --git a/i/pc104/initrd/conf/busybox/testsuite/uptime/uptime-works b/i/pc104/initrd/conf/busybox/testsuite/uptime/uptime-works
new file mode 100644
index 0000000..80e5787
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/uptime/uptime-works
@@ -0,0 +1,2 @@
+busybox uptime
+
diff --git a/i/pc104/initrd/conf/busybox/testsuite/uuencode.tests b/i/pc104/initrd/conf/busybox/testsuite/uuencode.tests
new file mode 100755
index 0000000..cb658db
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/uuencode.tests
@@ -0,0 +1,28 @@
+#!/bin/sh
+
+# unit test for uuencode to test functionality.
+# Copyright 2006 by Erik Hovland <erik@hovland.org>
+# Licensed under GPL v2, see file LICENSE for details.
+
+# AUDIT: Unit tests for uuencode
+
+. testing.sh
+
+# testing "test name" "options" "expected result" "file input" "stdin"
+# file input will be file called "input"
+# test can create a file "actual" instead of writing to stdout
+
+# Test setup of standard input
+saved_umask=$(umask)
+umask 0
+testing "uuencode sets standard input mode correctly" \
+ "uuencode foo </dev/null | head -n 1 | grep -q 666 && echo yes" "yes\n" "" ""
+umask $saved_umask
+
+testing "uuencode correct encoding" "uuencode bb_uuenc_test.out" \
+"begin 644 bb_uuenc_test.out\nM5&AE(&9A<W0@9W)E>2!F;W@@:G5M<&5D(&]V97(@=&AE(&QA>GD@8G)O=VX@\n%9&]G+@H\`\n\`\nend\n" \
+ "" "The fast grey fox jumped over the lazy brown dog.\n"
+testing "uuencode correct base64 encoding" "uuencode -m bb_uuenc_test.out" \
+"begin-base64 644 bb_uuenc_test.out\nVGhlIGZhc3QgZ3JleSBmb3gganVtcGVkIG92ZXIgdGhlIGxhenkgYnJvd24g\nZG9nLgo=\n====\n" \
+ "" "The fast grey fox jumped over the lazy brown dog.\n"
+exit $FAILCOUNT
diff --git a/i/pc104/initrd/conf/busybox/testsuite/wc/wc-counts-all b/i/pc104/initrd/conf/busybox/testsuite/wc/wc-counts-all
new file mode 100644
index 0000000..7083645
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/wc/wc-counts-all
@@ -0,0 +1,2 @@
+# 1 line, 4 words, 20 chars.
+test "`echo i\'m a little teapot | busybox wc | sed 's/ */ /g' | sed 's/^ //'`" = '1 4 20'
diff --git a/i/pc104/initrd/conf/busybox/testsuite/wc/wc-counts-characters b/i/pc104/initrd/conf/busybox/testsuite/wc/wc-counts-characters
new file mode 100644
index 0000000..7558646
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/wc/wc-counts-characters
@@ -0,0 +1 @@
+test `echo i\'m a little teapot | busybox wc -c` -eq 20
diff --git a/i/pc104/initrd/conf/busybox/testsuite/wc/wc-counts-lines b/i/pc104/initrd/conf/busybox/testsuite/wc/wc-counts-lines
new file mode 100644
index 0000000..5be6ed0
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/wc/wc-counts-lines
@@ -0,0 +1 @@
+test `echo i\'m a little teapot | busybox wc -l` -eq 1
diff --git a/i/pc104/initrd/conf/busybox/testsuite/wc/wc-counts-words b/i/pc104/initrd/conf/busybox/testsuite/wc/wc-counts-words
new file mode 100644
index 0000000..331650e
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/wc/wc-counts-words
@@ -0,0 +1 @@
+test `echo i\'m a little teapot | busybox wc -w` -eq 4
diff --git a/i/pc104/initrd/conf/busybox/testsuite/wc/wc-prints-longest-line-length b/i/pc104/initrd/conf/busybox/testsuite/wc/wc-prints-longest-line-length
new file mode 100644
index 0000000..78831fc
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/wc/wc-prints-longest-line-length
@@ -0,0 +1 @@
+test `echo i\'m a little teapot | busybox wc -L` -eq 19
diff --git a/i/pc104/initrd/conf/busybox/testsuite/wget/wget--O-overrides--P b/i/pc104/initrd/conf/busybox/testsuite/wget/wget--O-overrides--P
new file mode 100644
index 0000000..fdb5d47
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/wget/wget--O-overrides--P
@@ -0,0 +1,3 @@
+mkdir foo
+busybox wget -q -O index.html -P foo http://www.google.com/
+test -s index.html
diff --git a/i/pc104/initrd/conf/busybox/testsuite/wget/wget-handles-empty-path b/i/pc104/initrd/conf/busybox/testsuite/wget/wget-handles-empty-path
new file mode 100644
index 0000000..5b59183
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/wget/wget-handles-empty-path
@@ -0,0 +1 @@
+busybox wget http://www.google.com
diff --git a/i/pc104/initrd/conf/busybox/testsuite/wget/wget-retrieves-google-index b/i/pc104/initrd/conf/busybox/testsuite/wget/wget-retrieves-google-index
new file mode 100644
index 0000000..7be9a80
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/wget/wget-retrieves-google-index
@@ -0,0 +1,2 @@
+busybox wget -q -O foo http://www.google.com/
+test -s foo
diff --git a/i/pc104/initrd/conf/busybox/testsuite/wget/wget-supports--P b/i/pc104/initrd/conf/busybox/testsuite/wget/wget-supports--P
new file mode 100644
index 0000000..9b4d095
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/wget/wget-supports--P
@@ -0,0 +1,3 @@
+mkdir foo
+busybox wget -q -P foo http://www.google.com/
+test -s foo/index.html
diff --git a/i/pc104/initrd/conf/busybox/testsuite/which/which-uses-default-path b/i/pc104/initrd/conf/busybox/testsuite/which/which-uses-default-path
new file mode 100644
index 0000000..63ceb9f
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/which/which-uses-default-path
@@ -0,0 +1,4 @@
+BUSYBOX=$(type -p busybox)
+SAVED_PATH=$PATH
+unset PATH
+$BUSYBOX which ls
diff --git a/i/pc104/initrd/conf/busybox/testsuite/xargs/xargs-works b/i/pc104/initrd/conf/busybox/testsuite/xargs/xargs-works
new file mode 100644
index 0000000..c95869e
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/testsuite/xargs/xargs-works
@@ -0,0 +1,4 @@
+[ -n "$d" ] || d=..
+find "$d" -name \*works -type f | xargs md5sum > logfile.gnu
+find "$d" -name \*works -type f | busybox xargs md5sum > logfile.bb
+diff -u logfile.gnu logfile.bb
diff --git a/i/pc104/initrd/conf/busybox/util-linux/Config.in b/i/pc104/initrd/conf/busybox/util-linux/Config.in
new file mode 100644
index 0000000..2184df1
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/util-linux/Config.in
@@ -0,0 +1,509 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+menu "Linux System Utilities"
+
+config DMESG
+ bool "dmesg"
+ default n
+ help
+ dmesg is used to examine or control the kernel ring buffer. When the
+ Linux kernel prints messages to the system log, they are stored in
+ the kernel ring buffer. You can use dmesg to print the kernel's ring
+ buffer, clear the kernel ring buffer, change the size of the kernel
+ ring buffer, and change the priority level at which kernel messages
+ are also logged to the system console. Enable this option if you
+ wish to enable the 'dmesg' utility.
+
+config FEATURE_DMESG_PRETTY
+ bool "pretty dmesg output"
+ default y
+ depends on DMESG
+ help
+ If you wish to scrub the syslog level from the output, say 'Y' here.
+ The syslog level is a string prefixed to every line with the form "<#>".
+
+ With this option you will see:
+ # dmesg
+ Linux version 2.6.17.4 .....
+ BIOS-provided physical RAM map:
+ BIOS-e820: 0000000000000000 - 000000000009f000 (usable)
+
+ Without this option you will see:
+ # dmesg
+ <5>Linux version 2.6.17.4 .....
+ <6>BIOS-provided physical RAM map:
+ <6> BIOS-e820: 0000000000000000 - 000000000009f000 (usable)
+
+config FBSET
+ bool "fbset"
+ default n
+ help
+ fbset is used to show or change the settings of a Linux frame buffer
+ device. The frame buffer device provides a simple and unique
+ interface to access a graphics display. Enable this option
+ if you wish to enable the 'fbset' utility.
+
+config FEATURE_FBSET_FANCY
+ bool "Turn on extra fbset options"
+ default n
+ depends on FBSET
+ help
+ This option enables extended fbset options, allowing one to set the
+ framebuffer size, color depth, etc. interface to access a graphics
+ display. Enable this option if you wish to enable extended fbset
+ options.
+
+config FEATURE_FBSET_READMODE
+ bool "Turn on fbset readmode support"
+ default n
+ depends on FBSET
+ help
+ This option allows fbset to read the video mode database stored by
+ default as /etc/fb.modes, which can be used to set frame buffer
+ device to pre-defined video modes.
+
+config FDFLUSH
+ bool "fdflush"
+ default n
+ help
+ fdflush is only needed when changing media on slightly-broken
+ removable media drives. It is used to make Linux believe that a
+ hardware disk-change switch has been actuated, which causes Linux to
+ forget anything it has cached from the previous media. If you have
+ such a slightly-broken drive, you will need to run fdflush every time
+ you change a disk. Most people have working hardware and can safely
+ leave this disabled.
+
+config FDFORMAT
+ bool "fdformat"
+ default n
+ help
+ fdformat is used to low-level format a floppy disk.
+
+config FDISK
+ bool "fdisk"
+ default n
+ help
+ The fdisk utility is used to divide hard disks into one or more
+ logical disks, which are generally called partitions. This utility
+ can be used to list and edit the set of partitions or BSD style
+ 'disk slices' that are defined on a hard drive.
+
+config FDISK_SUPPORT_LARGE_DISKS
+ bool "support over 4GB disks"
+ default y
+ depends on FDISK
+ help
+ Enable this option to support large disks > 4GB.
+
+config FEATURE_FDISK_WRITABLE
+ bool "Write support"
+ default y
+ depends on FDISK
+ help
+ Enabling this option allows you to create or change a partition table
+ and write those changes out to disk. If you leave this option
+ disabled, you will only be able to view the partition table.
+
+config FEATURE_AIX_LABEL
+ bool "Support AIX disklabels"
+ default n
+ depends on FDISK && FEATURE_FDISK_WRITABLE
+ help
+ Enabling this option allows you to create or change AIX disklabels.
+ Most people can safely leave this option disabled.
+
+config FEATURE_SGI_LABEL
+ bool "Support SGI disklabels"
+ default n
+ depends on FDISK && FEATURE_FDISK_WRITABLE
+ help
+ Enabling this option allows you to create or change SGI disklabels.
+ Most people can safely leave this option disabled.
+
+config FEATURE_SUN_LABEL
+ bool "Support SUN disklabels"
+ default n
+ depends on FDISK && FEATURE_FDISK_WRITABLE
+ help
+ Enabling this option allows you to create or change SUN disklabels.
+ Most people can safely leave this option disabled.
+
+config FEATURE_OSF_LABEL
+ bool "Support BSD disklabels"
+ default n
+ depends on FDISK && FEATURE_FDISK_WRITABLE
+ help
+ Enabling this option allows you to create or change BSD disklabels
+ and define and edit BSD disk slices.
+
+config FEATURE_FDISK_ADVANCED
+ bool "Support expert mode"
+ default n
+ depends on FDISK && FEATURE_FDISK_WRITABLE
+ help
+ Enabling this option allows you to do terribly unsafe things like
+ define arbitrary drive geometry, move the beginning of data in a
+ partition, and similarly evil things. Unless you have a very good
+ reason you would be wise to leave this disabled.
+
+config FREERAMDISK
+ bool "freeramdisk"
+ default n
+ help
+ Linux allows you to create ramdisks. This utility allows you to
+ delete them and completely free all memory that was used for the
+ ramdisk. For example, if you boot Linux into a ramdisk and later
+ pivot_root, you may want to free the memory that is allocated to the
+ ramdisk. If you have no use for freeing memory from a ramdisk, leave
+ this disabled.
+
+config FSCK_MINIX
+ bool "fsck_minix"
+ default n
+ help
+ The minix filesystem is a nice, small, compact, read-write filesystem
+ with little overhead. It is not a journaling filesystem however and
+ can experience corruption if it is not properly unmounted or if the
+ power goes off in the middle of a write. This utility allows you to
+ check for and attempt to repair any corruption that occurs to a minix
+ filesystem.
+
+config MKFS_MINIX
+ bool "mkfs_minix"
+ default n
+ help
+ The minix filesystem is a nice, small, compact, read-write filesystem
+ with little overhead. If you wish to be able to create minix filesystems
+ this utility will do the job for you.
+
+comment "Minix filesystem support"
+ depends on FSCK_MINIX || MKFS_MINIX
+
+config FEATURE_MINIX2
+ bool "Support Minix fs v2 (fsck_minix/mkfs_minix)"
+ default y
+ depends on FSCK_MINIX || MKFS_MINIX
+ help
+ If you wish to be able to create version 2 minix filesystems, enable this.
+ If you enabled 'mkfs_minix' then you almost certainly want to be using the
+ version 2 filesystem support.
+
+config GETOPT
+ bool "getopt"
+ default n
+ help
+ The getopt utility is used to break up (parse) options in command
+ lines to make it easy to write complex shell scripts that also check
+ for legal (and illegal) options. If you want to write horribly
+ complex shell scripts, or use some horribly complex shell script
+ written by others, this utility may be for you. Most people will
+ wisely leave this disabled.
+
+config HEXDUMP
+ bool "hexdump"
+ default n
+ help
+ The hexdump utility is used to display binary data in a readable
+ way that is comparable to the output from most hex editors.
+
+config HWCLOCK
+ bool "hwclock"
+ default n
+ help
+ The hwclock utility is used to read and set the hardware clock
+ on a system. This is primarily used to set the current time on
+ shutdown in the hardware clock, so the hardware will keep the
+ correct time when Linux is _not_ running.
+
+config FEATURE_HWCLOCK_LONG_OPTIONS
+ bool "Support long options (--hctosys,...)"
+ default n
+ depends on HWCLOCK && GETOPT_LONG
+ help
+ By default, the hwclock utility only uses short options. If you
+ are overly fond of its long options, such as --hctosys, --utc, etc)
+ then enable this option.
+
+config FEATURE_HWCLOCK_ADJTIME_FHS
+ bool "Use FHS /var/lib/hwclock/adjtime"
+ default y
+ depends on HWCLOCK
+ help
+ Starting with FHS 2.3, the adjtime state file is supposed to exist
+ at /var/lib/hwclock/adjtime instead of /etc/adjtime. If you wish
+ to use the FHS behavior, answer Y here, otherwise answer N for the
+ classic /etc/adjtime path.
+
+ http://www.pathname.com/fhs/pub/fhs-2.3.html#VARLIBHWCLOCKSTATEDIRECTORYFORHWCLO
+
+config IPCRM
+ bool "ipcrm"
+ default n
+ select FEATURE_SUID
+ help
+ The ipcrm utility allows the removal of System V interprocess
+ communication (IPC) objects and the associated data structures
+ from the system.
+
+config IPCS
+ bool "ipcs"
+ default n
+ select FEATURE_SUID
+ help
+ The ipcs utility is used to provide information on the currently
+ allocated System V interprocess (IPC) objects in the system.
+
+config LOSETUP
+ bool "losetup"
+ default n
+ help
+ losetup is used to associate or detach a loop device with a regular
+ file or block device, and to query the status of a loop device. This
+ version does not currently support enabling data encryption.
+
+config MDEV
+ bool "mdev"
+ default n
+ help
+ mdev is a mini-udev implementation for dynamically creating device
+ nodes in the /dev directory.
+
+ For more information, please see docs/mdev.txt
+
+config FEATURE_MDEV_CONF
+ bool "Support /etc/mdev.conf"
+ default n
+ depends on MDEV
+ help
+ Add support for the mdev config file to control ownership and
+ permissions of the device nodes.
+
+ For more information, please see docs/mdev.txt
+
+config FEATURE_MDEV_EXEC
+ bool "Support command execution at device addition/removal"
+ default n
+ depends on FEATURE_MDEV_CONF
+ help
+ This adds support for an optional field to /etc/mdev.conf for
+ executing commands when devices are created/removed.
+
+ For more information, please see docs/mdev.txt
+
+config MKSWAP
+ bool "mkswap"
+ default n
+ help
+ The mkswap utility is used to configure a file or disk partition as
+ Linux swap space. This allows Linux to use the entire file or
+ partition as if it were additional RAM, which can greatly increase
+ the capability of low-memory machines. This additional memory is
+ much slower than real RAM, but can be very helpful at preventing your
+ applications being killed by the Linux out of memory (OOM) killer.
+ Once you have created swap space using 'mkswap' you need to enable
+ the swap space using the 'swapon' utility.
+
+config FEATURE_MKSWAP_V0
+ bool "version 0 support"
+ default n
+ depends on MKSWAP
+# depends on MKSWAP && DEPRECATED
+ help
+ Enable support for the old v0 style.
+ If your kernel is older than 2.1.117, then v0 support is the
+ only option.
+
+config MORE
+ bool "more"
+ default n
+ help
+ more is a simple utility which allows you to read text one screen
+ sized page at a time. If you want to read text that is larger than
+ the screen, and you are using anything faster than a 300 baud modem,
+ you will probably find this utility very helpful. If you don't have
+ any need to reading text files, you can leave this disabled.
+
+config FEATURE_USE_TERMIOS
+ bool "Use termios to manipulate the screen"
+ default y
+ depends on MORE
+ help
+ This option allows utilities such as 'more' and 'top' to determine
+ the size of the screen. If you leave this disabled, your utilities
+ that display things on the screen will be especially primitive and
+ will be unable to determine the current screen size, and will be
+ unable to move the cursor.
+
+config MOUNT
+ bool "mount"
+ default n
+ help
+ All files and filesystems in Unix are arranged into one big directory
+ tree. The 'mount' utility is used to graft a filesystem onto a
+ particular part of the tree. A filesystem can either live on a block
+ device, or it can be accessible over the network, as is the case with
+ NFS filesystems. Most people using BusyBox will also want to enable
+ the 'mount' utility.
+
+config FEATURE_MOUNT_NFS
+ bool "Support mounting NFS file systems"
+ default n
+ depends on MOUNT && FEATURE_HAVE_RPC
+ select FEATURE_SYSLOG
+ help
+ Enable mounting of NFS file systems.
+
+config FEATURE_MOUNT_CIFS
+ bool "Support mounting CIFS/SMB file systems"
+ default n
+ depends on MOUNT
+ help
+ Enable support for samba mounts.
+
+config FEATURE_MOUNT_FLAGS
+ depends on MOUNT
+ bool "Support lots of -o flags in mount"
+ default y
+ help
+ Without this, mount only supports ro/rw/remount. With this, it
+ supports nosuid, suid, dev, nodev, exec, noexec, sync, async, atime,
+ noatime, diratime, nodiratime, loud, bind, move, shared, slave,
+ private, unbindable, rshared, rslave, rprivate, and runbindable.
+
+config FEATURE_MOUNT_FSTAB
+ depends on MOUNT
+ bool "Support /etc/fstab and -a"
+ default y
+ help
+ Support mount all and looking for files in /etc/fstab.
+
+config PIVOT_ROOT
+ bool "pivot_root"
+ default n
+ help
+ The pivot_root utility swaps the mount points for the root filesystem
+ with some other mounted filesystem. This allows you to do all sorts
+ of wild and crazy things with your Linux system and is far more
+ powerful than 'chroot'.
+
+ Note: This is for initrd in linux 2.4. Under initramfs (introduced
+ in linux 2.6) use switch_root instead.
+
+config RDATE
+ bool "rdate"
+ default n
+ help
+ The rdate utility allows you to synchronize the date and time of your
+ system clock with the date and time of a remote networked system using
+ the RFC868 protocol, which is built into the inetd daemon on most
+ systems.
+
+config READPROFILE
+ bool "readprofile"
+ default n
+ help
+ This allows you to parse /proc/profile for basic profiling.
+
+config SETARCH
+ bool "setarch"
+ default n
+ help
+ The linux32 utility is used to create a 32bit environment for the
+ specified program (usually a shell). It only makes sense to have
+ this util on a system that supports both 64bit and 32bit userland
+ (like amd64/x86, ppc64/ppc, sparc64/sparc, etc...).
+
+config SWAPONOFF
+ bool "swaponoff"
+ default n
+ help
+ This option enables both the 'swapon' and the 'swapoff' utilities.
+ Once you have created some swap space using 'mkswap', you also need
+ to enable your swap space with the 'swapon' utility. The 'swapoff'
+ utility is used, typically at system shutdown, to disable any swap
+ space. If you are not using any swap space, you can leave this
+ option disabled.
+
+config SWITCH_ROOT
+ bool "switch_root"
+ default n
+ help
+ The switch_root utility is used from initramfs to select a new
+ root device. Under initramfs, you have to use this instead of
+ pivot_root. (Stop reading here if you don't care why.)
+
+ Booting with initramfs extracts a gzipped cpio archive into rootfs
+ (which is a variant of ramfs/tmpfs). Because rootfs can't be moved
+ or unmounted*, pivot_root will not work from initramfs. Instead,
+ switch_root deletes everything out of rootfs (including itself),
+ does a mount --move that overmounts rootfs with the new root, and
+ then execs the specified init program.
+
+ * Because the Linux kernel uses rootfs internally as the starting
+ and ending point for searching through the kernel's doubly linked
+ list of active mount points. That's why.
+
+config UMOUNT
+ bool "umount"
+ default n
+ help
+ When you want to remove a mounted filesystem from its current mount point,
+ for example when you are shutting down the system, the 'umount' utility is
+ the tool to use. If you enabled the 'mount' utility, you almost certainly
+ also want to enable 'umount'.
+
+config FEATURE_UMOUNT_ALL
+ bool "umount -a option"
+ default n
+ depends on UMOUNT
+ help
+ Support -a option to unmount all currently mounted filesystems.
+
+comment "Common options for mount/umount"
+ depends on MOUNT || UMOUNT
+
+config FEATURE_MOUNT_LOOP
+ bool "Support loopback mounts"
+ default n
+ depends on MOUNT || UMOUNT
+ help
+ Enabling this feature allows automatic mounting of files (containing
+ filesystem images) via the linux kernel's loopback devices. The mount
+ command will detect you are trying to mount a file instead of a block
+ device, and transparently associate the file with a loopback device.
+ The umount command will also free that loopback device.
+
+ You can still use the 'losetup' utility (to manually associate files
+ with loop devices) if you need to do something advanced, such as
+ specify an offset or cryptographic options to the loopback device.
+ (If you don't want umount to free the loop device, use "umount -D".)
+
+config FEATURE_MTAB_SUPPORT
+ bool "Support for the old /etc/mtab file"
+ default n
+ depends on MOUNT || UMOUNT
+ help
+ Historically, Unix systems kept track of the currently mounted
+ partitions in the file "/etc/mtab". These days, the kernel exports
+ the list of currently mounted partitions in "/proc/mounts", rendering
+ the old mtab file obsolete. (In modern systems, /etc/mtab should be
+ a symlink to /proc/mounts.)
+
+ The only reason to have mount maintain an /etc/mtab file itself is if
+ your stripped-down embedded system does not have a /proc directory.
+ If you must use this, keep in mind it's inherently brittle (for
+ example a mount under chroot won't update it), can't handle modern
+ features like separate per-process filesystem namespaces, requires
+ that your /etc directory be writeable, tends to get easily confused
+ by --bind or --move mounts, won't update if you rename a directory
+ that contains a mount point, and so on. (In brief: avoid.)
+
+ About the only reason to use this is if you've removed /proc from
+ your kernel.
+
+endmenu
+
diff --git a/i/pc104/initrd/conf/busybox/util-linux/Kbuild b/i/pc104/initrd/conf/busybox/util-linux/Kbuild
new file mode 100644
index 0000000..cc1d0e0
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/util-linux/Kbuild
@@ -0,0 +1,32 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under the GPL v2, see the file LICENSE in this tarball.
+
+lib-y:=
+lib-$(CONFIG_DMESG) +=dmesg.o
+lib-$(CONFIG_FBSET) +=fbset.o
+lib-$(CONFIG_FDFLUSH) +=freeramdisk.o
+lib-$(CONFIG_FDFORMAT) +=fdformat.o
+lib-$(CONFIG_FDISK) +=fdisk.o
+lib-$(CONFIG_FREERAMDISK) +=freeramdisk.o
+lib-$(CONFIG_FSCK_MINIX) +=fsck_minix.o
+lib-$(CONFIG_GETOPT) +=getopt.o
+lib-$(CONFIG_HEXDUMP) +=hexdump.o
+lib-$(CONFIG_HWCLOCK) +=hwclock.o
+lib-$(CONFIG_IPCRM) +=ipcrm.o
+lib-$(CONFIG_IPCS) +=ipcs.o
+lib-$(CONFIG_LOSETUP) +=losetup.o
+lib-$(CONFIG_MDEV) +=mdev.o
+lib-$(CONFIG_MKFS_MINIX) +=mkfs_minix.o
+lib-$(CONFIG_MKSWAP) +=mkswap.o
+lib-$(CONFIG_MORE) +=more.o
+lib-$(CONFIG_MOUNT) +=mount.o
+lib-$(CONFIG_PIVOT_ROOT) +=pivot_root.o
+lib-$(CONFIG_RDATE) +=rdate.o
+lib-$(CONFIG_READPROFILE) +=readprofile.o
+lib-$(CONFIG_SETARCH) +=setarch.o
+lib-$(CONFIG_SWAPONOFF) +=swaponoff.o
+lib-$(CONFIG_SWITCH_ROOT) +=switch_root.o
+lib-$(CONFIG_UMOUNT) +=umount.o
diff --git a/i/pc104/initrd/conf/busybox/util-linux/dmesg.c b/i/pc104/initrd/conf/busybox/util-linux/dmesg.c
new file mode 100644
index 0000000..0bbc306
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/util-linux/dmesg.c
@@ -0,0 +1,54 @@
+/* vi: set sw=4 ts=4: */
+/*
+ *
+ * dmesg - display/control kernel ring buffer.
+ *
+ * Copyright 2006 Rob Landley <rob@landley.net>
+ * Copyright 2006 Bernhard Fischer <rep.nop@aon.at>
+ *
+ * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ */
+
+#include "busybox.h"
+#include <unistd.h>
+#include <sys/klog.h>
+
+int dmesg_main(int argc, char *argv[]);
+int dmesg_main(int argc, char *argv[])
+{
+ char *size, *level;
+ int flags = getopt32(argc, argv, "cs:n:", &size, &level);
+
+ if (flags & 4) {
+ if (klogctl(8, NULL, xatoul_range(level, 0, 10)))
+ bb_perror_msg_and_die("klogctl");
+ } else {
+ int len;
+ char *buf;
+
+ len = (flags & 2) ? xatoul_range(size, 2, INT_MAX) : 16384;
+ buf = xmalloc(len);
+ if (0 > (len = klogctl(3 + (flags & 1), buf, len)))
+ bb_perror_msg_and_die("klogctl");
+
+ // Skip <#> at the start of lines, and make sure we end with a newline.
+
+ if (ENABLE_FEATURE_DMESG_PRETTY) {
+ int last = '\n';
+ int in;
+
+ for (in = 0; in<len;) {
+ if (last == '\n' && buf[in] == '<') in += 3;
+ else putchar(last = buf[in++]);
+ }
+ if (last != '\n') putchar('\n');
+ } else {
+ write(1,buf,len);
+ if (len && buf[len-1]!='\n') putchar('\n');
+ }
+
+ if (ENABLE_FEATURE_CLEAN_UP) free(buf);
+ }
+
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/util-linux/fbset.c b/i/pc104/initrd/conf/busybox/util-linux/fbset.c
new file mode 100644
index 0000000..bd584e4
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/util-linux/fbset.c
@@ -0,0 +1,408 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini fbset implementation for busybox
+ *
+ * Copyright (C) 1999 by Randolph Chung <tausq@debian.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ *
+ * This is a from-scratch implementation of fbset; but the de facto fbset
+ * implementation was a good reference. fbset (original) is released under
+ * the GPL, and is (c) 1995-1999 by:
+ * Geert Uytterhoeven (Geert.Uytterhoeven@cs.kuleuven.ac.be)
+ */
+
+#include "busybox.h"
+
+#define DEFAULTFBDEV FB_0
+#define DEFAULTFBMODE "/etc/fb.modes"
+
+enum {
+ OPT_CHANGE = (1 << 0),
+ OPT_INFO = (1 << 1),
+ OPT_READMODE = (1 << 2),
+ OPT_ALL = (1 << 9),
+
+ CMD_FB = 1,
+ CMD_DB = 2,
+ CMD_GEOMETRY = 3,
+ CMD_TIMING = 4,
+ CMD_ACCEL = 5,
+ CMD_HSYNC = 6,
+ CMD_VSYNC = 7,
+ CMD_LACED = 8,
+ CMD_DOUBLE = 9,
+/* CMD_XCOMPAT = 10, */
+ CMD_ALL = 11,
+ CMD_INFO = 12,
+ CMD_CHANGE = 13,
+
+#ifdef CONFIG_FEATURE_FBSET_FANCY
+ CMD_XRES = 100,
+ CMD_YRES = 101,
+ CMD_VXRES = 102,
+ CMD_VYRES = 103,
+ CMD_DEPTH = 104,
+ CMD_MATCH = 105,
+ CMD_PIXCLOCK = 106,
+ CMD_LEFT = 107,
+ CMD_RIGHT = 108,
+ CMD_UPPER = 109,
+ CMD_LOWER = 110,
+ CMD_HSLEN = 111,
+ CMD_VSLEN = 112,
+ CMD_CSYNC = 113,
+ CMD_GSYNC = 114,
+ CMD_EXTSYNC = 115,
+ CMD_BCAST = 116,
+ CMD_RGBA = 117,
+ CMD_STEP = 118,
+ CMD_MOVE = 119,
+#endif
+};
+
+static unsigned g_options;
+
+/* Stuff stolen from the kernel's fb.h */
+#define FB_ACTIVATE_ALL 64
+enum {
+ FBIOGET_VSCREENINFO = 0x4600,
+ FBIOPUT_VSCREENINFO = 0x4601
+};
+struct fb_bitfield {
+ uint32_t offset; /* beginning of bitfield */
+ uint32_t length; /* length of bitfield */
+ uint32_t msb_right; /* !=0: Most significant bit is right */
+};
+struct fb_var_screeninfo {
+ uint32_t xres; /* visible resolution */
+ uint32_t yres;
+ uint32_t xres_virtual; /* virtual resolution */
+ uint32_t yres_virtual;
+ uint32_t xoffset; /* offset from virtual to visible */
+ uint32_t yoffset; /* resolution */
+
+ uint32_t bits_per_pixel;
+ uint32_t grayscale; /* !=0 Graylevels instead of colors */
+
+ struct fb_bitfield red; /* bitfield in fb mem if true color, */
+ struct fb_bitfield green; /* else only length is significant */
+ struct fb_bitfield blue;
+ struct fb_bitfield transp; /* transparency */
+
+ uint32_t nonstd; /* !=0 Non standard pixel format */
+
+ uint32_t activate; /* see FB_ACTIVATE_x */
+
+ uint32_t height; /* height of picture in mm */
+ uint32_t width; /* width of picture in mm */
+
+ uint32_t accel_flags; /* acceleration flags (hints) */
+
+ /* Timing: All values in pixclocks, except pixclock (of course) */
+ uint32_t pixclock; /* pixel clock in ps (pico seconds) */
+ uint32_t left_margin; /* time from sync to picture */
+ uint32_t right_margin; /* time from picture to sync */
+ uint32_t upper_margin; /* time from sync to picture */
+ uint32_t lower_margin;
+ uint32_t hsync_len; /* length of horizontal sync */
+ uint32_t vsync_len; /* length of vertical sync */
+ uint32_t sync; /* see FB_SYNC_x */
+ uint32_t vmode; /* see FB_VMODE_x */
+ uint32_t reserved[6]; /* Reserved for future compatibility */
+};
+
+
+static const struct cmdoptions_t {
+ const char name[10];
+ const unsigned char param_count;
+ const unsigned char code;
+} g_cmdoptions[] = {
+ { "-fb", 1, CMD_FB },
+ { "-db", 1, CMD_DB },
+ { "-a", 0, CMD_ALL },
+ { "-i", 0, CMD_INFO },
+ { "-g", 5, CMD_GEOMETRY },
+ { "-t", 7, CMD_TIMING },
+ { "-accel", 1, CMD_ACCEL },
+ { "-hsync", 1, CMD_HSYNC },
+ { "-vsync", 1, CMD_VSYNC },
+ { "-laced", 1, CMD_LACED },
+ { "-double", 1, CMD_DOUBLE },
+ { "-n", 0, CMD_CHANGE },
+#ifdef CONFIG_FEATURE_FBSET_FANCY
+ { "-all", 0, CMD_ALL },
+ { "-xres", 1, CMD_XRES },
+ { "-yres", 1, CMD_YRES },
+ { "-vxres", 1, CMD_VXRES },
+ { "-vyres", 1, CMD_VYRES },
+ { "-depth", 1, CMD_DEPTH },
+ { "-match", 0, CMD_MATCH },
+ { "-geometry", 5, CMD_GEOMETRY },
+ { "-pixclock", 1, CMD_PIXCLOCK },
+ { "-left", 1, CMD_LEFT },
+ { "-right", 1, CMD_RIGHT },
+ { "-upper", 1, CMD_UPPER },
+ { "-lower", 1, CMD_LOWER },
+ { "-hslen", 1, CMD_HSLEN },
+ { "-vslen", 1, CMD_VSLEN },
+ { "-timings", 7, CMD_TIMING },
+ { "-csync", 1, CMD_CSYNC },
+ { "-gsync", 1, CMD_GSYNC },
+ { "-extsync", 1, CMD_EXTSYNC },
+ { "-bcast", 1, CMD_BCAST },
+ { "-rgba", 1, CMD_RGBA },
+ { "-step", 1, CMD_STEP },
+ { "-move", 1, CMD_MOVE },
+#endif
+ { "", 0, 0 }
+};
+
+#ifdef CONFIG_FEATURE_FBSET_READMODE
+/* taken from linux/fb.h */
+enum {
+ FB_VMODE_INTERLACED = 1, /* interlaced */
+ FB_VMODE_DOUBLE = 2, /* double scan */
+ FB_SYNC_HOR_HIGH_ACT = 1, /* horizontal sync high active */
+ FB_SYNC_VERT_HIGH_ACT = 2, /* vertical sync high active */
+ FB_SYNC_EXT = 4, /* external sync */
+ FB_SYNC_COMP_HIGH_ACT = 8 /* composite sync high active */
+};
+#endif
+
+static int readmode(struct fb_var_screeninfo *base, const char *fn,
+ const char *mode)
+{
+#ifdef CONFIG_FEATURE_FBSET_READMODE
+ FILE *f;
+ char buf[256];
+ char *p = buf;
+
+ f = xfopen(fn, "r");
+ while (!feof(f)) {
+ fgets(buf, sizeof(buf), f);
+ if (!(p = strstr(buf, "mode ")) && !(p = strstr(buf, "mode\t")))
+ continue;
+ p += 5;
+ if (!(p = strstr(buf, mode)))
+ continue;
+ p += strlen(mode);
+ if (!isspace(*p) && (*p != 0) && (*p != '"')
+ && (*p != '\r') && (*p != '\n'))
+ continue; /* almost, but not quite */
+
+ while (!feof(f)) {
+ fgets(buf, sizeof(buf), f);
+ if ((p = strstr(buf, "geometry "))) {
+ p += 9;
+ /* FIXME: catastrophic on arches with 64bit ints */
+ sscanf(p, "%d %d %d %d %d",
+ &(base->xres), &(base->yres),
+ &(base->xres_virtual), &(base->yres_virtual),
+ &(base->bits_per_pixel));
+ } else if ((p = strstr(buf, "timings "))) {
+ p += 8;
+ sscanf(p, "%d %d %d %d %d %d %d",
+ &(base->pixclock),
+ &(base->left_margin), &(base->right_margin),
+ &(base->upper_margin), &(base->lower_margin),
+ &(base->hsync_len), &(base->vsync_len));
+ } else if ((p = strstr(buf, "laced "))) {
+ //p += 6;
+ if (strstr(buf, "false")) {
+ base->vmode &= ~FB_VMODE_INTERLACED;
+ } else {
+ base->vmode |= FB_VMODE_INTERLACED;
+ }
+ } else if ((p = strstr(buf, "double "))) {
+ //p += 7;
+ if (strstr(buf, "false")) {
+ base->vmode &= ~FB_VMODE_DOUBLE;
+ } else {
+ base->vmode |= FB_VMODE_DOUBLE;
+ }
+ } else if ((p = strstr(buf, "vsync "))) {
+ //p += 6;
+ if (strstr(buf, "low")) {
+ base->sync &= ~FB_SYNC_VERT_HIGH_ACT;
+ } else {
+ base->sync |= FB_SYNC_VERT_HIGH_ACT;
+ }
+ } else if ((p = strstr(buf, "hsync "))) {
+ //p += 6;
+ if (strstr(buf, "low")) {
+ base->sync &= ~FB_SYNC_HOR_HIGH_ACT;
+ } else {
+ base->sync |= FB_SYNC_HOR_HIGH_ACT;
+ }
+ } else if ((p = strstr(buf, "csync "))) {
+ //p += 6;
+ if (strstr(buf, "low")) {
+ base->sync &= ~FB_SYNC_COMP_HIGH_ACT;
+ } else {
+ base->sync |= FB_SYNC_COMP_HIGH_ACT;
+ }
+ } else if ((p = strstr(buf, "extsync "))) {
+ //p += 8;
+ if (strstr(buf, "false")) {
+ base->sync &= ~FB_SYNC_EXT;
+ } else {
+ base->sync |= FB_SYNC_EXT;
+ }
+ }
+
+ if (strstr(buf, "endmode"))
+ return 1;
+ }
+ }
+#else
+ bb_error_msg("mode reading not compiled in");
+#endif
+ return 0;
+}
+
+static inline void setmode(struct fb_var_screeninfo *base,
+ struct fb_var_screeninfo *set)
+{
+ if ((int) set->xres > 0)
+ base->xres = set->xres;
+ if ((int) set->yres > 0)
+ base->yres = set->yres;
+ if ((int) set->xres_virtual > 0)
+ base->xres_virtual = set->xres_virtual;
+ if ((int) set->yres_virtual > 0)
+ base->yres_virtual = set->yres_virtual;
+ if ((int) set->bits_per_pixel > 0)
+ base->bits_per_pixel = set->bits_per_pixel;
+}
+
+static inline void showmode(struct fb_var_screeninfo *v)
+{
+ double drate = 0, hrate = 0, vrate = 0;
+
+ if (v->pixclock) {
+ drate = 1e12 / v->pixclock;
+ hrate = drate / (v->left_margin + v->xres + v->right_margin + v->hsync_len);
+ vrate = hrate / (v->upper_margin + v->yres + v->lower_margin + v->vsync_len);
+ }
+ printf("\nmode \"%ux%u-%u\"\n"
+#ifdef CONFIG_FEATURE_FBSET_FANCY
+ "\t# D: %.3f MHz, H: %.3f kHz, V: %.3f Hz\n"
+#endif
+ "\tgeometry %u %u %u %u %u\n"
+ "\ttimings %u %u %u %u %u %u %u\n"
+ "\taccel %s\n"
+ "\trgba %u/%u,%u/%u,%u/%u,%u/%u\n"
+ "endmode\n\n",
+ v->xres, v->yres, (int) (vrate + 0.5),
+#ifdef CONFIG_FEATURE_FBSET_FANCY
+ drate / 1e6, hrate / 1e3, vrate,
+#endif
+ v->xres, v->yres, v->xres_virtual, v->yres_virtual, v->bits_per_pixel,
+ v->pixclock, v->left_margin, v->right_margin, v->upper_margin, v->lower_margin,
+ v->hsync_len, v->vsync_len,
+ (v->accel_flags > 0 ? "true" : "false"),
+ v->red.length, v->red.offset, v->green.length, v->green.offset,
+ v->blue.length, v->blue.offset, v->transp.length, v->transp.offset);
+}
+
+#ifdef STANDALONE
+int main(int argc, char **argv)
+#else
+int fbset_main(int argc, char **argv);
+int fbset_main(int argc, char **argv)
+#endif
+{
+ struct fb_var_screeninfo var, varset;
+ int fh, i;
+ const char *fbdev = DEFAULTFBDEV;
+ const char *modefile = DEFAULTFBMODE;
+ char *thisarg, *mode = NULL;
+
+ memset(&varset, 0xFF, sizeof(varset));
+
+ /* parse cmd args.... why do they have to make things so difficult? */
+ argv++;
+ argc--;
+ for (; argc > 0 && (thisarg = *argv); argc--, argv++) {
+ for (i = 0; g_cmdoptions[i].name[0]; i++) {
+ if (strcmp(thisarg, g_cmdoptions[i].name))
+ continue;
+ if (argc-1 < g_cmdoptions[i].param_count)
+ bb_show_usage();
+
+ switch (g_cmdoptions[i].code) {
+ case CMD_FB:
+ fbdev = argv[1];
+ break;
+ case CMD_DB:
+ modefile = argv[1];
+ break;
+ case CMD_GEOMETRY:
+ varset.xres = xatou32(argv[1]);
+ varset.yres = xatou32(argv[2]);
+ varset.xres_virtual = xatou32(argv[3]);
+ varset.yres_virtual = xatou32(argv[4]);
+ varset.bits_per_pixel = xatou32(argv[5]);
+ break;
+ case CMD_TIMING:
+ varset.pixclock = xatou32(argv[1]);
+ varset.left_margin = xatou32(argv[2]);
+ varset.right_margin = xatou32(argv[3]);
+ varset.upper_margin = xatou32(argv[4]);
+ varset.lower_margin = xatou32(argv[5]);
+ varset.hsync_len = xatou32(argv[6]);
+ varset.vsync_len = xatou32(argv[7]);
+ break;
+ case CMD_ALL:
+ g_options |= OPT_ALL;
+ break;
+ case CMD_CHANGE:
+ g_options |= OPT_CHANGE;
+ break;
+#ifdef CONFIG_FEATURE_FBSET_FANCY
+ case CMD_XRES:
+ varset.xres = xatou32(argv[1]);
+ break;
+ case CMD_YRES:
+ varset.yres = xatou32(argv[1]);
+ break;
+ case CMD_DEPTH:
+ varset.bits_per_pixel = xatou32(argv[1]);
+ break;
+#endif
+ }
+ argc -= g_cmdoptions[i].param_count;
+ argv += g_cmdoptions[i].param_count;
+ break;
+ }
+ if (!g_cmdoptions[i].name[0]) {
+ if (argc != 1)
+ bb_show_usage();
+ mode = *argv;
+ g_options |= OPT_READMODE;
+ }
+ }
+
+ fh = xopen(fbdev, O_RDONLY);
+ if (ioctl(fh, FBIOGET_VSCREENINFO, &var))
+ bb_perror_msg_and_die("ioctl(%sT_VSCREENINFO)", "GE");
+ if (g_options & OPT_READMODE) {
+ if (!readmode(&var, modefile, mode)) {
+ bb_error_msg_and_die("unknown video mode '%s'", mode);
+ }
+ }
+
+ setmode(&var, &varset);
+ if (g_options & OPT_CHANGE) {
+ if (g_options & OPT_ALL)
+ var.activate = FB_ACTIVATE_ALL;
+ if (ioctl(fh, FBIOPUT_VSCREENINFO, &var))
+ bb_perror_msg_and_die("ioctl(%sT_VSCREENINFO)", "PU");
+ }
+ showmode(&var);
+ /* Don't close the file, as exiting will take care of that */
+ /* close(fh); */
+
+ return EXIT_SUCCESS;
+}
diff --git a/i/pc104/initrd/conf/busybox/util-linux/fdformat.c b/i/pc104/initrd/conf/busybox/util-linux/fdformat.c
new file mode 100644
index 0000000..d694bd6
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/util-linux/fdformat.c
@@ -0,0 +1,144 @@
+/* vi: set sw=4 ts=4: */
+/* fdformat.c - Low-level formats a floppy disk - Werner Almesberger */
+
+/* 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
+ * - added Native Language Support
+ * 1999-03-20 Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ * - more i18n/nls translatable strings marked
+ *
+ * 5 July 2003 -- modified for Busybox by Erik Andersen
+ */
+
+#include "busybox.h"
+
+
+/* Stuff extracted from linux/fd.h */
+struct floppy_struct {
+ unsigned int size, /* nr of sectors total */
+ sect, /* sectors per track */
+ head, /* nr of heads */
+ track, /* nr of tracks */
+ stretch; /* !=0 means double track steps */
+#define FD_STRETCH 1
+#define FD_SWAPSIDES 2
+
+ unsigned char gap, /* gap1 size */
+
+ rate, /* data rate. |= 0x40 for perpendicular */
+#define FD_2M 0x4
+#define FD_SIZECODEMASK 0x38
+#define FD_SIZECODE(floppy) (((((floppy)->rate&FD_SIZECODEMASK)>> 3)+ 2) %8)
+#define FD_SECTSIZE(floppy) ( (floppy)->rate & FD_2M ? \
+ 512 : 128 << FD_SIZECODE(floppy) )
+#define FD_PERP 0x40
+
+ spec1, /* stepping rate, head unload time */
+ fmt_gap; /* gap2 size */
+ const char * name; /* used only for predefined formats */
+};
+struct format_descr {
+ unsigned int device,head,track;
+};
+#define FDFMTBEG _IO(2,0x47)
+#define FDFMTTRK _IOW(2,0x48, struct format_descr)
+#define FDFMTEND _IO(2,0x49)
+#define FDGETPRM _IOR(2, 0x04, struct floppy_struct)
+#define FD_FILL_BYTE 0xF6 /* format fill byte. */
+
+static void xioctl(int fd, int request, void *argp, const char *string)
+{
+ if (ioctl(fd, request, argp) < 0) {
+ bb_perror_msg_and_die(string);
+ }
+}
+
+int fdformat_main(int argc,char **argv);
+int fdformat_main(int argc,char **argv)
+{
+ int fd, n, cyl, read_bytes, verify;
+ unsigned char *data;
+ struct stat st;
+ struct floppy_struct param;
+ struct format_descr descr;
+
+ if (argc < 2) {
+ bb_show_usage();
+ }
+ verify = !getopt32(argc, argv, "n");
+ argv += optind;
+
+ xstat(*argv, &st);
+ if (!S_ISBLK(st.st_mode)) {
+ bb_error_msg_and_die("%s: not a block device", *argv);
+ /* do not test major - perhaps this was an USB floppy */
+ }
+
+ /* O_RDWR for formatting and verifying */
+ fd = xopen(*argv, O_RDWR);
+
+ /* original message was: "Could not determine current format type" */
+ xioctl(fd, FDGETPRM, &param, "FDGETPRM");
+
+ printf("%s-sided, %d tracks, %d sec/track. Total capacity %d kB\n",
+ (param.head == 2) ? "Double" : "Single",
+ param.track, param.sect, param.size >> 1);
+
+ /* FORMAT */
+ printf("Formatting... ");
+ xioctl(fd, FDFMTBEG, NULL, "FDFMTBEG");
+
+ /* n == track */
+ for (n = 0; n < param.track; n++) {
+ descr.head = 0;
+ descr.track = n;
+ xioctl(fd, FDFMTTRK, &descr, "FDFMTTRK");
+ printf("%3d\b\b\b", n);
+ if (param.head == 2) {
+ descr.head = 1;
+ xioctl(fd, FDFMTTRK, &descr, "FDFMTTRK");
+ }
+ }
+
+ xioctl(fd, FDFMTEND, NULL, "FDFMTEND");
+ printf("done\n");
+
+ /* VERIFY */
+ if (verify) {
+ /* n == cyl_size */
+ n = param.sect*param.head*512;
+
+ data = xmalloc(n);
+ printf("Verifying... ");
+ for (cyl = 0; cyl < param.track; cyl++) {
+ printf("%3d\b\b\b", cyl);
+ read_bytes = safe_read(fd, data, n);
+ if (read_bytes != n) {
+ if (read_bytes < 0) {
+ bb_perror_msg(bb_msg_read_error);
+ }
+ bb_error_msg_and_die("problem reading cylinder %d, "
+ "expected %d, read %d", cyl, n, read_bytes);
+ // FIXME: maybe better seek & continue??
+ }
+ /* Check backwards so we don't need a counter */
+ while (--read_bytes >= 0) {
+ if (data[read_bytes] != FD_FILL_BYTE) {
+ printf("bad data in cyl %d\nContinuing... ",cyl);
+ }
+ }
+ }
+ /* There is no point in freeing blocks at the end of a program, because
+ all of the program's space is given back to the system when the process
+ terminates.*/
+
+ if (ENABLE_FEATURE_CLEAN_UP) free(data);
+
+ printf("done\n");
+ }
+
+ if (ENABLE_FEATURE_CLEAN_UP) close(fd);
+
+ /* Don't bother closing. Exit does
+ * that, so we can save a few bytes */
+ return EXIT_SUCCESS;
+}
diff --git a/i/pc104/initrd/conf/busybox/util-linux/fdisk.c b/i/pc104/initrd/conf/busybox/util-linux/fdisk.c
new file mode 100644
index 0000000..f3c7ca4
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/util-linux/fdisk.c
@@ -0,0 +1,3018 @@
+/* vi: set sw=4 ts=4: */
+/* fdisk.c -- Partition table manipulator for Linux.
+ *
+ * Copyright (C) 1992 A. V. Le Blanc (LeBlanc@mcc.ac.uk)
+ * Copyright (C) 2001,2002 Vladimir Oleynik <dzo@simtreas.ru> (initial bb port)
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ */
+
+#include <assert.h> /* assert */
+#include "busybox.h"
+
+/* Looks like someone forgot to add this to config system */
+#ifndef ENABLE_FEATURE_FDISK_BLKSIZE
+# define ENABLE_FEATURE_FDISK_BLKSIZE 0
+# define USE_FEATURE_FDISK_BLKSIZE(a)
+#endif
+
+#define SIZE(a) (sizeof(a)/sizeof((a)[0]))
+
+#define DEFAULT_SECTOR_SIZE 512
+#define MAX_SECTOR_SIZE 2048
+#define SECTOR_SIZE 512 /* still used in osf/sgi/sun code */
+#define MAXIMUM_PARTS 60
+
+#define ACTIVE_FLAG 0x80
+
+#define EXTENDED 0x05
+#define WIN98_EXTENDED 0x0f
+#define LINUX_PARTITION 0x81
+#define LINUX_SWAP 0x82
+#define LINUX_NATIVE 0x83
+#define LINUX_EXTENDED 0x85
+#define LINUX_LVM 0x8e
+#define LINUX_RAID 0xfd
+
+struct hd_geometry {
+ unsigned char heads;
+ unsigned char sectors;
+ unsigned short cylinders;
+ unsigned long start;
+};
+
+#define HDIO_GETGEO 0x0301 /* get device geometry */
+
+static const char msg_building_new_label[] =
+"Building a new %s. Changes will remain in memory only,\n"
+"until you decide to write them. After that the previous content\n"
+"won't be recoverable.\n\n";
+
+static const char msg_part_already_defined[] =
+"Partition %d is already defined, delete it before re-adding\n";
+
+
+static unsigned sector_size = DEFAULT_SECTOR_SIZE;
+static unsigned user_set_sector_size;
+static unsigned sector_offset = 1;
+
+#if ENABLE_FEATURE_OSF_LABEL
+static int possibly_osf_label;
+#endif
+
+static unsigned heads, sectors, cylinders;
+static void update_units(void);
+
+
+struct partition {
+ unsigned char boot_ind; /* 0x80 - active */
+ unsigned char head; /* starting head */
+ unsigned char sector; /* starting sector */
+ unsigned char cyl; /* starting cylinder */
+ unsigned char sys_ind; /* What partition type */
+ unsigned char end_head; /* end head */
+ unsigned char end_sector; /* end sector */
+ unsigned char end_cyl; /* end cylinder */
+ unsigned char start4[4]; /* starting sector counting from 0 */
+ unsigned char size4[4]; /* nr of sectors in partition */
+} ATTRIBUTE_PACKED;
+
+enum failure {
+ ioctl_error, unable_to_open, unable_to_read, unable_to_seek,
+ unable_to_write
+};
+
+enum label_type {
+ label_dos, label_sun, label_sgi, label_aix, label_osf
+};
+
+#define LABEL_IS_DOS (label_dos == current_label_type)
+
+#if ENABLE_FEATURE_SUN_LABEL
+#define LABEL_IS_SUN (label_sun == current_label_type)
+#define STATIC_SUN static
+#else
+#define LABEL_IS_SUN 0
+#define STATIC_SUN extern
+#endif
+
+#if ENABLE_FEATURE_SGI_LABEL
+#define LABEL_IS_SGI (label_sgi == current_label_type)
+#define STATIC_SGI static
+#else
+#define LABEL_IS_SGI 0
+#define STATIC_SGI extern
+#endif
+
+#if ENABLE_FEATURE_AIX_LABEL
+#define LABEL_IS_AIX (label_aix == current_label_type)
+#define STATIC_AIX static
+#else
+#define LABEL_IS_AIX 0
+#define STATIC_AIX extern
+#endif
+
+#if ENABLE_FEATURE_OSF_LABEL
+#define LABEL_IS_OSF (label_osf == current_label_type)
+#define STATIC_OSF static
+#else
+#define LABEL_IS_OSF 0
+#define STATIC_OSF extern
+#endif
+
+enum action { fdisk, require, try_only, create_empty_dos, create_empty_sun };
+
+static enum label_type current_label_type;
+
+static const char *disk_device;
+static int fd; /* the disk */
+static int partitions = 4; /* maximum partition + 1 */
+static int display_in_cyl_units = 1;
+static unsigned units_per_sector = 1;
+#if ENABLE_FEATURE_FDISK_WRITABLE
+static void change_units(void);
+static void reread_partition_table(int leave);
+static void delete_partition(int i);
+static int get_partition(int warn, int max);
+static void list_types(const char *const *sys);
+static unsigned read_int(unsigned low, unsigned dflt, unsigned high, unsigned base, const char *mesg);
+#endif
+static const char *partition_type(unsigned char type);
+static void fdisk_fatal(enum failure why) ATTRIBUTE_NORETURN;
+static void get_geometry(void);
+static int get_boot(enum action what);
+
+#define PLURAL 0
+#define SINGULAR 1
+
+static unsigned get_start_sect(const struct partition *p);
+static unsigned get_nr_sects(const struct partition *p);
+
+/*
+ * per partition table entry data
+ *
+ * The four primary partitions have the same sectorbuffer (MBRbuffer)
+ * and have NULL ext_pointer.
+ * Each logical partition table entry has two pointers, one for the
+ * partition and one link to the next one.
+ */
+struct pte {
+ struct partition *part_table; /* points into sectorbuffer */
+ struct partition *ext_pointer; /* points into sectorbuffer */
+ off_t offset; /* disk sector number */
+ char *sectorbuffer; /* disk sector contents */
+#if ENABLE_FEATURE_FDISK_WRITABLE
+ char changed; /* boolean */
+#endif
+};
+
+/* DOS partition types */
+
+static const char *const i386_sys_types[] = {
+ "\x00" "Empty",
+ "\x01" "FAT12",
+ "\x04" "FAT16 <32M",
+ "\x05" "Extended", /* DOS 3.3+ extended partition */
+ "\x06" "FAT16", /* DOS 16-bit >=32M */
+ "\x07" "HPFS/NTFS", /* OS/2 IFS, eg, HPFS or NTFS or QNX */
+ "\x0a" "OS/2 Boot Manager",/* OS/2 Boot Manager */
+ "\x0b" "Win95 FAT32",
+ "\x0c" "Win95 FAT32 (LBA)",/* LBA really is 'Extended Int 13h' */
+ "\x0e" "Win95 FAT16 (LBA)",
+ "\x0f" "Win95 Ext'd (LBA)",
+ "\x11" "Hidden FAT12",
+ "\x12" "Compaq diagnostics",
+ "\x14" "Hidden FAT16 <32M",
+ "\x16" "Hidden FAT16",
+ "\x17" "Hidden HPFS/NTFS",
+ "\x1b" "Hidden Win95 FAT32",
+ "\x1c" "Hidden W95 FAT32 (LBA)",
+ "\x1e" "Hidden W95 FAT16 (LBA)",
+ "\x3c" "Part.Magic recovery",
+ "\x41" "PPC PReP Boot",
+ "\x42" "SFS",
+ "\x63" "GNU HURD or SysV", /* GNU HURD or Mach or Sys V/386 (such as ISC UNIX) */
+ "\x80" "Old Minix", /* Minix 1.4a and earlier */
+ "\x81" "Minix / old Linux",/* Minix 1.4b and later */
+ "\x82" "Linux swap", /* also Solaris */
+ "\x83" "Linux",
+ "\x84" "OS/2 hidden C: drive",
+ "\x85" "Linux extended",
+ "\x86" "NTFS volume set",
+ "\x87" "NTFS volume set",
+ "\x8e" "Linux LVM",
+ "\x9f" "BSD/OS", /* BSDI */
+ "\xa0" "Thinkpad hibernation",
+ "\xa5" "FreeBSD", /* various BSD flavours */
+ "\xa6" "OpenBSD",
+ "\xa8" "Darwin UFS",
+ "\xa9" "NetBSD",
+ "\xab" "Darwin boot",
+ "\xb7" "BSDI fs",
+ "\xb8" "BSDI swap",
+ "\xbe" "Solaris boot",
+ "\xeb" "BeOS fs",
+ "\xee" "EFI GPT", /* Intel EFI GUID Partition Table */
+ "\xef" "EFI (FAT-12/16/32)", /* Intel EFI System Partition */
+ "\xf0" "Linux/PA-RISC boot", /* Linux/PA-RISC boot loader */
+ "\xf2" "DOS secondary", /* DOS 3.3+ secondary */
+ "\xfd" "Linux raid autodetect", /* New (2.2.x) raid partition with
+ autodetect using persistent
+ superblock */
+#if 0 /* ENABLE_WEIRD_PARTITION_TYPES */
+ "\x02" "XENIX root",
+ "\x03" "XENIX usr",
+ "\x08" "AIX", /* AIX boot (AIX -- PS/2 port) or SplitDrive */
+ "\x09" "AIX bootable", /* AIX data or Coherent */
+ "\x10" "OPUS",
+ "\x18" "AST SmartSleep",
+ "\x24" "NEC DOS",
+ "\x39" "Plan 9",
+ "\x40" "Venix 80286",
+ "\x4d" "QNX4.x",
+ "\x4e" "QNX4.x 2nd part",
+ "\x4f" "QNX4.x 3rd part",
+ "\x50" "OnTrack DM",
+ "\x51" "OnTrack DM6 Aux1", /* (or Novell) */
+ "\x52" "CP/M", /* CP/M or Microport SysV/AT */
+ "\x53" "OnTrack DM6 Aux3",
+ "\x54" "OnTrackDM6",
+ "\x55" "EZ-Drive",
+ "\x56" "Golden Bow",
+ "\x5c" "Priam Edisk",
+ "\x61" "SpeedStor",
+ "\x64" "Novell Netware 286",
+ "\x65" "Novell Netware 386",
+ "\x70" "DiskSecure Multi-Boot",
+ "\x75" "PC/IX",
+ "\x93" "Amoeba",
+ "\x94" "Amoeba BBT", /* (bad block table) */
+ "\xa7" "NeXTSTEP",
+ "\xbb" "Boot Wizard hidden",
+ "\xc1" "DRDOS/sec (FAT-12)",
+ "\xc4" "DRDOS/sec (FAT-16 < 32M)",
+ "\xc6" "DRDOS/sec (FAT-16)",
+ "\xc7" "Syrinx",
+ "\xda" "Non-FS data",
+ "\xdb" "CP/M / CTOS / ...",/* CP/M or Concurrent CP/M or
+ Concurrent DOS or CTOS */
+ "\xde" "Dell Utility", /* Dell PowerEdge Server utilities */
+ "\xdf" "BootIt", /* BootIt EMBRM */
+ "\xe1" "DOS access", /* DOS access or SpeedStor 12-bit FAT
+ extended partition */
+ "\xe3" "DOS R/O", /* DOS R/O or SpeedStor */
+ "\xe4" "SpeedStor", /* SpeedStor 16-bit FAT extended
+ partition < 1024 cyl. */
+ "\xf1" "SpeedStor",
+ "\xf4" "SpeedStor", /* SpeedStor large partition */
+ "\xfe" "LANstep", /* SpeedStor >1024 cyl. or LANstep */
+ "\xff" "BBT", /* Xenix Bad Block Table */
+#endif
+ NULL
+};
+
+
+/* Globals */
+
+struct globals {
+ char *line_ptr;
+ char line_buffer[80];
+ char partname_buffer[80];
+ jmp_buf listingbuf;
+ /* Raw disk label. For DOS-type partition tables the MBR,
+ * with descriptions of the primary partitions. */
+ char MBRbuffer[MAX_SECTOR_SIZE];
+ /* Partition tables */
+ struct pte ptes[MAXIMUM_PARTS];
+};
+/* bb_common_bufsiz1 is too small for this on 64 bit CPUs */
+#define G (*ptr_to_globals)
+
+#define line_ptr (G.line_ptr)
+#define listingbuf (G.listingbuf)
+#define line_buffer (G.line_buffer)
+#define partname_buffer (G.partname_buffer)
+#define MBRbuffer (G.MBRbuffer)
+#define ptes (G.ptes)
+
+
+/* Code */
+
+#define IS_EXTENDED(i) \
+ ((i) == EXTENDED || (i) == WIN98_EXTENDED || (i) == LINUX_EXTENDED)
+
+#define cround(n) (display_in_cyl_units ? ((n)/units_per_sector)+1 : (n))
+
+#define scround(x) (((x)+units_per_sector-1)/units_per_sector)
+
+#define pt_offset(b, n) \
+ ((struct partition *)((b) + 0x1be + (n) * sizeof(struct partition)))
+
+#define sector(s) ((s) & 0x3f)
+
+#define cylinder(s, c) ((c) | (((s) & 0xc0) << 2))
+
+#define hsc2sector(h,s,c) \
+ (sector(s) - 1 + sectors * ((h) + heads * cylinder(s,c)))
+
+#define set_hsc(h,s,c,sector) \
+ do { \
+ s = sector % sectors + 1; \
+ sector /= sectors; \
+ h = sector % heads; \
+ sector /= heads; \
+ c = sector & 0xff; \
+ s |= (sector >> 2) & 0xc0; \
+ } while (0)
+
+#if ENABLE_FEATURE_FDISK_WRITABLE
+/* read line; return 0 or first printable char */
+static int
+read_line(const char *prompt)
+{
+ int sz;
+
+ sz = read_line_input(prompt, line_buffer, sizeof(line_buffer), NULL);
+ if (sz <= 0)
+ exit(0); /* Ctrl-D or Ctrl-C */
+
+ if (line_buffer[sz-1] == '\n')
+ line_buffer[--sz] = '\0';
+
+ line_ptr = line_buffer;
+ while (*line_ptr && !isgraph(*line_ptr))
+ line_ptr++;
+ return *line_ptr;
+}
+#endif
+
+/*
+ * return partition name - uses static storage
+ */
+static const char *
+partname(const char *dev, int pno, int lth)
+{
+ const char *p;
+ int w, wp;
+ int bufsiz;
+ char *bufp;
+
+ bufp = partname_buffer;
+ bufsiz = sizeof(partname_buffer);
+
+ w = strlen(dev);
+ p = "";
+
+ if (isdigit(dev[w-1]))
+ p = "p";
+
+ /* devfs kludge - note: fdisk partition names are not supposed
+ to equal kernel names, so there is no reason to do this */
+ if (strcmp(dev + w - 4, "disc") == 0) {
+ w -= 4;
+ p = "part";
+ }
+
+ wp = strlen(p);
+
+ if (lth) {
+ snprintf(bufp, bufsiz, "%*.*s%s%-2u",
+ lth-wp-2, w, dev, p, pno);
+ } else {
+ snprintf(bufp, bufsiz, "%.*s%s%-2u", w, dev, p, pno);
+ }
+ return bufp;
+}
+
+#if ENABLE_FEATURE_FDISK_WRITABLE
+static void
+set_all_unchanged(void)
+{
+ int i;
+
+ for (i = 0; i < MAXIMUM_PARTS; i++)
+ ptes[i].changed = 0;
+}
+
+static ATTRIBUTE_ALWAYS_INLINE void
+set_changed(int i)
+{
+ ptes[i].changed = 1;
+}
+#endif /* FEATURE_FDISK_WRITABLE */
+
+static ATTRIBUTE_ALWAYS_INLINE struct partition *
+get_part_table(int i)
+{
+ return ptes[i].part_table;
+}
+
+static const char *
+str_units(int n)
+{ /* n==1: use singular */
+ if (n == 1)
+ return display_in_cyl_units ? "cylinder" : "sector";
+ return display_in_cyl_units ? "cylinders" : "sectors";
+}
+
+static int
+valid_part_table_flag(const char *mbuffer)
+{
+ return (mbuffer[510] == 0x55 && (uint8_t)mbuffer[511] == 0xaa);
+}
+
+#if ENABLE_FEATURE_FDISK_WRITABLE
+static ATTRIBUTE_ALWAYS_INLINE void
+write_part_table_flag(char *b)
+{
+ b[510] = 0x55;
+ b[511] = 0xaa;
+}
+
+static char
+read_nonempty(const char *mesg)
+{
+ while (!read_line(mesg)) /* repeat */;
+ return *line_ptr;
+}
+
+static char
+read_maybe_empty(const char *mesg)
+{
+ if (!read_line(mesg)) {
+ line_ptr = line_buffer;
+ line_ptr[0] = '\n';
+ line_ptr[1] = '\0';
+ }
+ return line_ptr[0];
+}
+
+static int
+read_hex(const char *const *sys)
+{
+ unsigned long v;
+ while (1) {
+ read_nonempty("Hex code (type L to list codes): ");
+ if (*line_ptr == 'l' || *line_ptr == 'L') {
+ list_types(sys);
+ continue;
+ }
+ v = bb_strtoul(line_ptr, NULL, 16);
+ if (v > 0xff)
+ /* Bad input also triggers this */
+ continue;
+ return v;
+ }
+}
+#endif /* FEATURE_FDISK_WRITABLE */
+
+#include "fdisk_aix.c"
+
+typedef struct {
+ unsigned char info[128]; /* Informative text string */
+ unsigned char spare0[14];
+ struct sun_info {
+ unsigned char spare1;
+ unsigned char id;
+ unsigned char spare2;
+ unsigned char flags;
+ } infos[8];
+ unsigned char spare1[246]; /* Boot information etc. */
+ unsigned short rspeed; /* Disk rotational speed */
+ unsigned short pcylcount; /* Physical cylinder count */
+ unsigned short sparecyl; /* extra sects per cylinder */
+ unsigned char spare2[4]; /* More magic... */
+ unsigned short ilfact; /* Interleave factor */
+ unsigned short ncyl; /* Data cylinder count */
+ unsigned short nacyl; /* Alt. cylinder count */
+ unsigned short ntrks; /* Tracks per cylinder */
+ unsigned short nsect; /* Sectors per track */
+ unsigned char spare3[4]; /* Even more magic... */
+ struct sun_partinfo {
+ uint32_t start_cylinder;
+ uint32_t num_sectors;
+ } partitions[8];
+ unsigned short magic; /* Magic number */
+ unsigned short csum; /* Label xor'd checksum */
+} sun_partition;
+#define sunlabel ((sun_partition *)MBRbuffer)
+STATIC_OSF void bsd_select(void);
+STATIC_OSF void xbsd_print_disklabel(int);
+#include "fdisk_osf.c"
+
+#if ENABLE_FEATURE_SGI_LABEL || ENABLE_FEATURE_SUN_LABEL
+static uint16_t
+fdisk_swap16(uint16_t x)
+{
+ return (x << 8) | (x >> 8);
+}
+
+static uint32_t
+fdisk_swap32(uint32_t x)
+{
+ return (x << 24) |
+ ((x & 0xFF00) << 8) |
+ ((x & 0xFF0000) >> 8) |
+ (x >> 24);
+}
+#endif
+
+STATIC_SGI const char *const sgi_sys_types[];
+STATIC_SGI unsigned sgi_get_num_sectors(int i);
+STATIC_SGI int sgi_get_sysid(int i);
+STATIC_SGI void sgi_delete_partition(int i);
+STATIC_SGI void sgi_change_sysid(int i, int sys);
+STATIC_SGI void sgi_list_table(int xtra);
+#if ENABLE_FEATURE_FDISK_ADVANCED
+STATIC_SGI void sgi_set_xcyl(void);
+#endif
+STATIC_SGI int verify_sgi(int verbose);
+STATIC_SGI void sgi_add_partition(int n, int sys);
+STATIC_SGI void sgi_set_swappartition(int i);
+STATIC_SGI const char *sgi_get_bootfile(void);
+STATIC_SGI void sgi_set_bootfile(const char* aFile);
+STATIC_SGI void create_sgiinfo(void);
+STATIC_SGI void sgi_write_table(void);
+STATIC_SGI void sgi_set_bootpartition(int i);
+#include "fdisk_sgi.c"
+
+STATIC_SUN const char *const sun_sys_types[];
+STATIC_SUN void sun_delete_partition(int i);
+STATIC_SUN void sun_change_sysid(int i, int sys);
+STATIC_SUN void sun_list_table(int xtra);
+STATIC_SUN void add_sun_partition(int n, int sys);
+#if ENABLE_FEATURE_FDISK_ADVANCED
+STATIC_SUN void sun_set_alt_cyl(void);
+STATIC_SUN void sun_set_ncyl(int cyl);
+STATIC_SUN void sun_set_xcyl(void);
+STATIC_SUN void sun_set_ilfact(void);
+STATIC_SUN void sun_set_rspeed(void);
+STATIC_SUN void sun_set_pcylcount(void);
+#endif
+STATIC_SUN void toggle_sunflags(int i, unsigned char mask);
+STATIC_SUN void verify_sun(void);
+STATIC_SUN void sun_write_table(void);
+#include "fdisk_sun.c"
+
+#if ENABLE_FEATURE_FDISK_WRITABLE
+/* start_sect and nr_sects are stored little endian on all machines */
+/* moreover, they are not aligned correctly */
+static void
+store4_little_endian(unsigned char *cp, unsigned val)
+{
+ cp[0] = val;
+ cp[1] = val >> 8;
+ cp[2] = val >> 16;
+ cp[3] = val >> 24;
+}
+#endif /* FEATURE_FDISK_WRITABLE */
+
+static unsigned
+read4_little_endian(const unsigned char *cp)
+{
+ return cp[0] + (cp[1] << 8) + (cp[2] << 16) + (cp[3] << 24);
+}
+
+#if ENABLE_FEATURE_FDISK_WRITABLE
+static void
+set_start_sect(struct partition *p, unsigned start_sect)
+{
+ store4_little_endian(p->start4, start_sect);
+}
+#endif
+
+static unsigned
+get_start_sect(const struct partition *p)
+{
+ return read4_little_endian(p->start4);
+}
+
+#if ENABLE_FEATURE_FDISK_WRITABLE
+static void
+set_nr_sects(struct partition *p, unsigned nr_sects)
+{
+ store4_little_endian(p->size4, nr_sects);
+}
+#endif
+
+static unsigned
+get_nr_sects(const struct partition *p)
+{
+ return read4_little_endian(p->size4);
+}
+
+/* normally O_RDWR, -l option gives O_RDONLY */
+static int type_open = O_RDWR;
+
+
+static int ext_index; /* the prime extended partition */
+static int listing; /* no aborts for fdisk -l */
+static int dos_compatible_flag = ~0;
+#if ENABLE_FEATURE_FDISK_WRITABLE
+static int dos_changed;
+static int nowarn; /* no warnings for fdisk -l/-s */
+#endif
+
+
+
+static unsigned user_cylinders, user_heads, user_sectors;
+static unsigned pt_heads, pt_sectors;
+static unsigned kern_heads, kern_sectors;
+
+static off_t extended_offset; /* offset of link pointers */
+
+static unsigned long long total_number_of_sectors;
+
+
+static void fdisk_fatal(enum failure why)
+{
+ const char *message;
+
+ if (listing) {
+ close(fd);
+ longjmp(listingbuf, 1);
+ }
+
+ switch (why) {
+ case unable_to_open:
+ message = "cannot open %s";
+ break;
+ case unable_to_read:
+ message = "cannot read from %s";
+ break;
+ case unable_to_seek:
+ message = "cannot seek on %s";
+ break;
+ case unable_to_write:
+ message = "cannot write to %s";
+ break;
+ case ioctl_error:
+ message = "BLKGETSIZE ioctl failed on %s";
+ break;
+ default:
+ message = "fatal error";
+ }
+
+ bb_error_msg_and_die(message, disk_device);
+}
+
+static void
+seek_sector(off_t secno)
+{
+ off_t offset = secno * sector_size;
+ if (lseek(fd, offset, SEEK_SET) == (off_t) -1)
+ fdisk_fatal(unable_to_seek);
+}
+
+#if ENABLE_FEATURE_FDISK_WRITABLE
+static void
+write_sector(off_t secno, char *buf)
+{
+ seek_sector(secno);
+ if (write(fd, buf, sector_size) != sector_size)
+ fdisk_fatal(unable_to_write);
+}
+#endif
+
+/* Allocate a buffer and read a partition table sector */
+static void
+read_pte(struct pte *pe, off_t offset)
+{
+ pe->offset = offset;
+ pe->sectorbuffer = xmalloc(sector_size);
+ seek_sector(offset);
+ if (read(fd, pe->sectorbuffer, sector_size) != sector_size)
+ fdisk_fatal(unable_to_read);
+#if ENABLE_FEATURE_FDISK_WRITABLE
+ pe->changed = 0;
+#endif
+ pe->part_table = pe->ext_pointer = NULL;
+}
+
+static unsigned
+get_partition_start(const struct pte *pe)
+{
+ return pe->offset + get_start_sect(pe->part_table);
+}
+
+#if ENABLE_FEATURE_FDISK_WRITABLE
+/*
+ * Avoid warning about DOS partitions when no DOS partition was changed.
+ * Here a heuristic "is probably dos partition".
+ * We might also do the opposite and warn in all cases except
+ * for "is probably nondos partition".
+ */
+static int
+is_dos_partition(int t)
+{
+ return (t == 1 || t == 4 || t == 6 ||
+ t == 0x0b || t == 0x0c || t == 0x0e ||
+ t == 0x11 || t == 0x12 || t == 0x14 || t == 0x16 ||
+ t == 0x1b || t == 0x1c || t == 0x1e || t == 0x24 ||
+ t == 0xc1 || t == 0xc4 || t == 0xc6);
+}
+
+static void
+menu(void)
+{
+ puts("Command Action");
+ if (LABEL_IS_SUN) {
+ puts("a\ttoggle a read only flag"); /* sun */
+ puts("b\tedit bsd disklabel");
+ puts("c\ttoggle the mountable flag"); /* sun */
+ puts("d\tdelete a partition");
+ puts("l\tlist known partition types");
+ puts("n\tadd a new partition");
+ puts("o\tcreate a new empty DOS partition table");
+ puts("p\tprint the partition table");
+ puts("q\tquit without saving changes");
+ puts("s\tcreate a new empty Sun disklabel"); /* sun */
+ puts("t\tchange a partition's system id");
+ puts("u\tchange display/entry units");
+ puts("v\tverify the partition table");
+ puts("w\twrite table to disk and exit");
+#if ENABLE_FEATURE_FDISK_ADVANCED
+ puts("x\textra functionality (experts only)");
+#endif
+ } else if (LABEL_IS_SGI) {
+ puts("a\tselect bootable partition"); /* sgi flavour */
+ puts("b\tedit bootfile entry"); /* sgi */
+ puts("c\tselect sgi swap partition"); /* sgi flavour */
+ puts("d\tdelete a partition");
+ puts("l\tlist known partition types");
+ puts("n\tadd a new partition");
+ puts("o\tcreate a new empty DOS partition table");
+ puts("p\tprint the partition table");
+ puts("q\tquit without saving changes");
+ puts("s\tcreate a new empty Sun disklabel"); /* sun */
+ puts("t\tchange a partition's system id");
+ puts("u\tchange display/entry units");
+ puts("v\tverify the partition table");
+ puts("w\twrite table to disk and exit");
+ } else if (LABEL_IS_AIX) {
+ puts("o\tcreate a new empty DOS partition table");
+ puts("q\tquit without saving changes");
+ puts("s\tcreate a new empty Sun disklabel"); /* sun */
+ } else {
+ puts("a\ttoggle a bootable flag");
+ puts("b\tedit bsd disklabel");
+ puts("c\ttoggle the dos compatibility flag");
+ puts("d\tdelete a partition");
+ puts("l\tlist known partition types");
+ puts("n\tadd a new partition");
+ puts("o\tcreate a new empty DOS partition table");
+ puts("p\tprint the partition table");
+ puts("q\tquit without saving changes");
+ puts("s\tcreate a new empty Sun disklabel"); /* sun */
+ puts("t\tchange a partition's system id");
+ puts("u\tchange display/entry units");
+ puts("v\tverify the partition table");
+ puts("w\twrite table to disk and exit");
+#if ENABLE_FEATURE_FDISK_ADVANCED
+ puts("x\textra functionality (experts only)");
+#endif
+ }
+}
+#endif /* FEATURE_FDISK_WRITABLE */
+
+
+#if ENABLE_FEATURE_FDISK_ADVANCED
+static void
+xmenu(void)
+{
+ puts("Command Action");
+ if (LABEL_IS_SUN) {
+ puts("a\tchange number of alternate cylinders"); /*sun*/
+ puts("c\tchange number of cylinders");
+ puts("d\tprint the raw data in the partition table");
+ puts("e\tchange number of extra sectors per cylinder");/*sun*/
+ puts("h\tchange number of heads");
+ puts("i\tchange interleave factor"); /*sun*/
+ puts("o\tchange rotation speed (rpm)"); /*sun*/
+ puts("p\tprint the partition table");
+ puts("q\tquit without saving changes");
+ puts("r\treturn to main menu");
+ puts("s\tchange number of sectors/track");
+ puts("v\tverify the partition table");
+ puts("w\twrite table to disk and exit");
+ puts("y\tchange number of physical cylinders"); /*sun*/
+ } else if (LABEL_IS_SGI) {
+ puts("b\tmove beginning of data in a partition"); /* !sun */
+ puts("c\tchange number of cylinders");
+ puts("d\tprint the raw data in the partition table");
+ puts("e\tlist extended partitions"); /* !sun */
+ puts("g\tcreate an IRIX (SGI) partition table");/* sgi */
+ puts("h\tchange number of heads");
+ puts("p\tprint the partition table");
+ puts("q\tquit without saving changes");
+ puts("r\treturn to main menu");
+ puts("s\tchange number of sectors/track");
+ puts("v\tverify the partition table");
+ puts("w\twrite table to disk and exit");
+ } else if (LABEL_IS_AIX) {
+ puts("b\tmove beginning of data in a partition"); /* !sun */
+ puts("c\tchange number of cylinders");
+ puts("d\tprint the raw data in the partition table");
+ puts("e\tlist extended partitions"); /* !sun */
+ puts("g\tcreate an IRIX (SGI) partition table");/* sgi */
+ puts("h\tchange number of heads");
+ puts("p\tprint the partition table");
+ puts("q\tquit without saving changes");
+ puts("r\treturn to main menu");
+ puts("s\tchange number of sectors/track");
+ puts("v\tverify the partition table");
+ puts("w\twrite table to disk and exit");
+ } else {
+ puts("b\tmove beginning of data in a partition"); /* !sun */
+ puts("c\tchange number of cylinders");
+ puts("d\tprint the raw data in the partition table");
+ puts("e\tlist extended partitions"); /* !sun */
+ puts("f\tfix partition order"); /* !sun, !aix, !sgi */
+#if ENABLE_FEATURE_SGI_LABEL
+ puts("g\tcreate an IRIX (SGI) partition table");/* sgi */
+#endif
+ puts("h\tchange number of heads");
+ puts("p\tprint the partition table");
+ puts("q\tquit without saving changes");
+ puts("r\treturn to main menu");
+ puts("s\tchange number of sectors/track");
+ puts("v\tverify the partition table");
+ puts("w\twrite table to disk and exit");
+ }
+}
+#endif /* ADVANCED mode */
+
+#if ENABLE_FEATURE_FDISK_WRITABLE
+static const char *const *
+get_sys_types(void)
+{
+ return (
+ LABEL_IS_SUN ? sun_sys_types :
+ LABEL_IS_SGI ? sgi_sys_types :
+ i386_sys_types);
+}
+#else
+#define get_sys_types() i386_sys_types
+#endif /* FEATURE_FDISK_WRITABLE */
+
+static const char *
+partition_type(unsigned char type)
+{
+ int i;
+ const char *const *types = get_sys_types();
+
+ for (i = 0; types[i]; i++)
+ if ((unsigned char)types[i][0] == type)
+ return types[i] + 1;
+
+ return "Unknown";
+}
+
+
+#if ENABLE_FEATURE_FDISK_WRITABLE
+static int
+get_sysid(int i)
+{
+ return LABEL_IS_SUN ? sunlabel->infos[i].id :
+ (LABEL_IS_SGI ? sgi_get_sysid(i) :
+ ptes[i].part_table->sys_ind);
+}
+
+static void
+list_types(const char *const *sys)
+{
+ enum { COLS = 3 };
+
+ unsigned last[COLS];
+ unsigned done, next, size;
+ int i;
+
+ for (size = 0; sys[size]; size++) /* */;
+
+ done = 0;
+ for (i = COLS-1; i >= 0; i--) {
+ done += (size + i - done) / (i + 1);
+ last[COLS-1 - i] = done;
+ }
+
+ i = done = next = 0;
+ do {
+ printf("%c%2x %-22.22s", i ? ' ' : '\n',
+ (unsigned char)sys[next][0],
+ sys[next] + 1);
+ next = last[i++] + done;
+ if (i >= COLS || next >= last[i]) {
+ i = 0;
+ next = ++done;
+ }
+ } while (done < last[0]);
+ putchar('\n');
+}
+#endif /* FEATURE_FDISK_WRITABLE */
+
+static int
+is_cleared_partition(const struct partition *p)
+{
+ return !(!p || p->boot_ind || p->head || p->sector || p->cyl ||
+ p->sys_ind || p->end_head || p->end_sector || p->end_cyl ||
+ get_start_sect(p) || get_nr_sects(p));
+}
+
+static void
+clear_partition(struct partition *p)
+{
+ if (!p)
+ return;
+ memset(p, 0, sizeof(struct partition));
+}
+
+#if ENABLE_FEATURE_FDISK_WRITABLE
+static void
+set_partition(int i, int doext, off_t start, off_t stop, int sysid)
+{
+ struct partition *p;
+ off_t offset;
+
+ if (doext) {
+ p = ptes[i].ext_pointer;
+ offset = extended_offset;
+ } else {
+ p = ptes[i].part_table;
+ offset = ptes[i].offset;
+ }
+ p->boot_ind = 0;
+ p->sys_ind = sysid;
+ set_start_sect(p, start - offset);
+ set_nr_sects(p, stop - start + 1);
+ if (dos_compatible_flag && (start/(sectors*heads) > 1023))
+ start = heads*sectors*1024 - 1;
+ set_hsc(p->head, p->sector, p->cyl, start);
+ if (dos_compatible_flag && (stop/(sectors*heads) > 1023))
+ stop = heads*sectors*1024 - 1;
+ set_hsc(p->end_head, p->end_sector, p->end_cyl, stop);
+ ptes[i].changed = 1;
+}
+#endif
+
+static int
+warn_geometry(void)
+{
+ if (heads && sectors && cylinders)
+ return 0;
+
+ printf("Unknown value(s) for:");
+ if (!heads)
+ printf(" heads");
+ if (!sectors)
+ printf(" sectors");
+ if (!cylinders)
+ printf(" cylinders");
+ printf(
+#if ENABLE_FEATURE_FDISK_WRITABLE
+ " (settable in the extra functions menu)"
+#endif
+ "\n");
+ return 1;
+}
+
+static void
+update_units(void)
+{
+ int cyl_units = heads * sectors;
+
+ if (display_in_cyl_units && cyl_units)
+ units_per_sector = cyl_units;
+ else
+ units_per_sector = 1; /* in sectors */
+}
+
+#if ENABLE_FEATURE_FDISK_WRITABLE
+static void
+warn_cylinders(void)
+{
+ if (LABEL_IS_DOS && cylinders > 1024 && !nowarn)
+ printf("\n"
+"The number of cylinders for this disk is set to %d.\n"
+"There is nothing wrong with that, but this is larger than 1024,\n"
+"and could in certain setups cause problems with:\n"
+"1) software that runs at boot time (e.g., old versions of LILO)\n"
+"2) booting and partitioning software from other OSs\n"
+" (e.g., DOS FDISK, OS/2 FDISK)\n",
+ cylinders);
+}
+#endif
+
+static void
+read_extended(int ext)
+{
+ int i;
+ struct pte *pex;
+ struct partition *p, *q;
+
+ ext_index = ext;
+ pex = &ptes[ext];
+ pex->ext_pointer = pex->part_table;
+
+ p = pex->part_table;
+ if (!get_start_sect(p)) {
+ printf("Bad offset in primary extended partition\n");
+ return;
+ }
+
+ while (IS_EXTENDED(p->sys_ind)) {
+ struct pte *pe = &ptes[partitions];
+
+ if (partitions >= MAXIMUM_PARTS) {
+ /* This is not a Linux restriction, but
+ this program uses arrays of size MAXIMUM_PARTS.
+ Do not try to 'improve' this test. */
+ struct pte *pre = &ptes[partitions-1];
+#if ENABLE_FEATURE_FDISK_WRITABLE
+ printf("Warning: deleting partitions after %d\n",
+ partitions);
+ pre->changed = 1;
+#endif
+ clear_partition(pre->ext_pointer);
+ return;
+ }
+
+ read_pte(pe, extended_offset + get_start_sect(p));
+
+ if (!extended_offset)
+ extended_offset = get_start_sect(p);
+
+ q = p = pt_offset(pe->sectorbuffer, 0);
+ for (i = 0; i < 4; i++, p++) if (get_nr_sects(p)) {
+ if (IS_EXTENDED(p->sys_ind)) {
+ if (pe->ext_pointer)
+ printf("Warning: extra link "
+ "pointer in partition table"
+ " %d\n", partitions + 1);
+ else
+ pe->ext_pointer = p;
+ } else if (p->sys_ind) {
+ if (pe->part_table)
+ printf("Warning: ignoring extra "
+ "data in partition table"
+ " %d\n", partitions + 1);
+ else
+ pe->part_table = p;
+ }
+ }
+
+ /* very strange code here... */
+ if (!pe->part_table) {
+ if (q != pe->ext_pointer)
+ pe->part_table = q;
+ else
+ pe->part_table = q + 1;
+ }
+ if (!pe->ext_pointer) {
+ if (q != pe->part_table)
+ pe->ext_pointer = q;
+ else
+ pe->ext_pointer = q + 1;
+ }
+
+ p = pe->ext_pointer;
+ partitions++;
+ }
+
+#if ENABLE_FEATURE_FDISK_WRITABLE
+ /* remove empty links */
+ remove:
+ for (i = 4; i < partitions; i++) {
+ struct pte *pe = &ptes[i];
+
+ if (!get_nr_sects(pe->part_table)
+ && (partitions > 5 || ptes[4].part_table->sys_ind)
+ ) {
+ printf("Omitting empty partition (%d)\n", i+1);
+ delete_partition(i);
+ goto remove; /* numbering changed */
+ }
+ }
+#endif
+}
+
+#if ENABLE_FEATURE_FDISK_WRITABLE
+static void
+create_doslabel(void)
+{
+ int i;
+
+ printf(msg_building_new_label, "DOS disklabel");
+
+ current_label_type = label_dos;
+
+#if ENABLE_FEATURE_OSF_LABEL
+ possibly_osf_label = 0;
+#endif
+ partitions = 4;
+
+ for (i = 510-64; i < 510; i++)
+ MBRbuffer[i] = 0;
+ write_part_table_flag(MBRbuffer);
+ extended_offset = 0;
+ set_all_unchanged();
+ set_changed(0);
+ get_boot(create_empty_dos);
+}
+#endif /* FEATURE_FDISK_WRITABLE */
+
+static void
+get_sectorsize(void)
+{
+ if (!user_set_sector_size) {
+ int arg;
+ if (ioctl(fd, BLKSSZGET, &arg) == 0)
+ sector_size = arg;
+ if (sector_size != DEFAULT_SECTOR_SIZE)
+ printf("Note: sector size is %d (not %d)\n",
+ sector_size, DEFAULT_SECTOR_SIZE);
+ }
+}
+
+static void
+get_kernel_geometry(void)
+{
+ struct hd_geometry geometry;
+
+ if (!ioctl(fd, HDIO_GETGEO, &geometry)) {
+ kern_heads = geometry.heads;
+ kern_sectors = geometry.sectors;
+ /* never use geometry.cylinders - it is truncated */
+ }
+}
+
+static void
+get_partition_table_geometry(void)
+{
+ const unsigned char *bufp = (const unsigned char *)MBRbuffer;
+ struct partition *p;
+ int i, h, s, hh, ss;
+ int first = 1;
+ int bad = 0;
+
+ if (!(valid_part_table_flag((char*)bufp)))
+ return;
+
+ hh = ss = 0;
+ for (i = 0; i < 4; i++) {
+ p = pt_offset(bufp, i);
+ if (p->sys_ind != 0) {
+ h = p->end_head + 1;
+ s = (p->end_sector & 077);
+ if (first) {
+ hh = h;
+ ss = s;
+ first = 0;
+ } else if (hh != h || ss != s)
+ bad = 1;
+ }
+ }
+
+ if (!first && !bad) {
+ pt_heads = hh;
+ pt_sectors = ss;
+ }
+}
+
+static void
+get_geometry(void)
+{
+ int sec_fac;
+ unsigned long long bytes; /* really u64 */
+
+ get_sectorsize();
+ sec_fac = sector_size / 512;
+#if ENABLE_FEATURE_SUN_LABEL
+ guess_device_type();
+#endif
+ heads = cylinders = sectors = 0;
+ kern_heads = kern_sectors = 0;
+ pt_heads = pt_sectors = 0;
+
+ get_kernel_geometry();
+ get_partition_table_geometry();
+
+ heads = user_heads ? user_heads :
+ pt_heads ? pt_heads :
+ kern_heads ? kern_heads : 255;
+ sectors = user_sectors ? user_sectors :
+ pt_sectors ? pt_sectors :
+ kern_sectors ? kern_sectors : 63;
+ if (ioctl(fd, BLKGETSIZE64, &bytes) == 0) {
+ /* got bytes */
+ } else {
+ unsigned long longsectors;
+
+ if (ioctl(fd, BLKGETSIZE, &longsectors))
+ longsectors = 0;
+ bytes = ((unsigned long long) longsectors) << 9;
+ }
+
+ total_number_of_sectors = (bytes >> 9);
+
+ sector_offset = 1;
+ if (dos_compatible_flag)
+ sector_offset = sectors;
+
+ cylinders = total_number_of_sectors / (heads * sectors * sec_fac);
+ if (!cylinders)
+ cylinders = user_cylinders;
+}
+
+/*
+ * Read MBR. Returns:
+ * -1: no 0xaa55 flag present (possibly entire disk BSD)
+ * 0: found or created label
+ * 1: I/O error
+ */
+static int
+get_boot(enum action what)
+{
+ int i;
+
+ partitions = 4;
+
+ for (i = 0; i < 4; i++) {
+ struct pte *pe = &ptes[i];
+
+ pe->part_table = pt_offset(MBRbuffer, i);
+ pe->ext_pointer = NULL;
+ pe->offset = 0;
+ pe->sectorbuffer = MBRbuffer;
+#if ENABLE_FEATURE_FDISK_WRITABLE
+ pe->changed = (what == create_empty_dos);
+#endif
+ }
+
+#if ENABLE_FEATURE_SUN_LABEL
+ if (what == create_empty_sun && check_sun_label())
+ return 0;
+#endif
+
+ memset(MBRbuffer, 0, 512);
+
+#if ENABLE_FEATURE_FDISK_WRITABLE
+ if (what == create_empty_dos)
+ goto got_dos_table; /* skip reading disk */
+
+ fd = open(disk_device, type_open);
+ if (fd < 0) {
+ fd = open(disk_device, O_RDONLY);
+ if (fd < 0) {
+ if (what == try_only)
+ return 1;
+ fdisk_fatal(unable_to_open);
+ } else
+ printf("You will not be able to write "
+ "the partition table\n");
+ }
+
+ if (512 != read(fd, MBRbuffer, 512)) {
+ if (what == try_only)
+ return 1;
+ fdisk_fatal(unable_to_read);
+ }
+#else
+ fd = open(disk_device, O_RDONLY);
+ if (fd < 0)
+ return 1;
+ if (512 != read(fd, MBRbuffer, 512))
+ return 1;
+#endif
+
+ get_geometry();
+
+ update_units();
+
+#if ENABLE_FEATURE_SUN_LABEL
+ if (check_sun_label())
+ return 0;
+#endif
+
+#if ENABLE_FEATURE_SGI_LABEL
+ if (check_sgi_label())
+ return 0;
+#endif
+
+#if ENABLE_FEATURE_AIX_LABEL
+ if (check_aix_label())
+ return 0;
+#endif
+
+#if ENABLE_FEATURE_OSF_LABEL
+ if (check_osf_label()) {
+ possibly_osf_label = 1;
+ if (!valid_part_table_flag(MBRbuffer)) {
+ current_label_type = label_osf;
+ return 0;
+ }
+ printf("This disk has both DOS and BSD magic.\n"
+ "Give the 'b' command to go to BSD mode.\n");
+ }
+#endif
+
+#if ENABLE_FEATURE_FDISK_WRITABLE
+ got_dos_table:
+#endif
+
+ if (!valid_part_table_flag(MBRbuffer)) {
+#if !ENABLE_FEATURE_FDISK_WRITABLE
+ return -1;
+#else
+ switch (what) {
+ case fdisk:
+ printf("Device contains neither a valid DOS "
+ "partition table, nor Sun, SGI or OSF "
+ "disklabel\n");
+#ifdef __sparc__
+#if ENABLE_FEATURE_SUN_LABEL
+ create_sunlabel();
+#endif
+#else
+ create_doslabel();
+#endif
+ return 0;
+ case try_only:
+ return -1;
+ case create_empty_dos:
+#if ENABLE_FEATURE_SUN_LABEL
+ case create_empty_sun:
+#endif
+ break;
+ default:
+ bb_error_msg_and_die("internal error");
+ }
+#endif /* FEATURE_FDISK_WRITABLE */
+ }
+
+#if ENABLE_FEATURE_FDISK_WRITABLE
+ warn_cylinders();
+#endif
+ warn_geometry();
+
+ for (i = 0; i < 4; i++) {
+ struct pte *pe = &ptes[i];
+
+ if (IS_EXTENDED(pe->part_table->sys_ind)) {
+ if (partitions != 4)
+ printf("Ignoring extra extended "
+ "partition %d\n", i + 1);
+ else
+ read_extended(i);
+ }
+ }
+
+ for (i = 3; i < partitions; i++) {
+ struct pte *pe = &ptes[i];
+
+ if (!valid_part_table_flag(pe->sectorbuffer)) {
+ printf("Warning: invalid flag 0x%02x,0x%02x of partition "
+ "table %d will be corrected by w(rite)\n",
+ pe->sectorbuffer[510],
+ pe->sectorbuffer[511],
+ i + 1);
+#if ENABLE_FEATURE_FDISK_WRITABLE
+ pe->changed = 1;
+#endif
+ }
+ }
+
+ return 0;
+}
+
+#if ENABLE_FEATURE_FDISK_WRITABLE
+/*
+ * Print the message MESG, then read an integer between LOW and HIGH (inclusive).
+ * If the user hits Enter, DFLT is returned.
+ * Answers like +10 are interpreted as offsets from BASE.
+ *
+ * There is no default if DFLT is not between LOW and HIGH.
+ */
+static unsigned
+read_int(unsigned low, unsigned dflt, unsigned high, unsigned base, const char *mesg)
+{
+ unsigned i;
+ int default_ok = 1;
+ const char *fmt = "%s (%u-%u, default %u): ";
+
+ if (dflt < low || dflt > high) {
+ fmt = "%s (%u-%u): ";
+ default_ok = 0;
+ }
+
+ while (1) {
+ int use_default = default_ok;
+
+ /* ask question and read answer */
+ do {
+ printf(fmt, mesg, low, high, dflt);
+ read_maybe_empty("");
+ } while (*line_ptr != '\n' && !isdigit(*line_ptr)
+ && *line_ptr != '-' && *line_ptr != '+');
+
+ if (*line_ptr == '+' || *line_ptr == '-') {
+ int minus = (*line_ptr == '-');
+ int absolute = 0;
+
+ i = atoi(line_ptr + 1);
+
+ while (isdigit(*++line_ptr))
+ use_default = 0;
+
+ switch (*line_ptr) {
+ case 'c':
+ case 'C':
+ if (!display_in_cyl_units)
+ i *= heads * sectors;
+ break;
+ case 'K':
+ absolute = 1024;
+ break;
+ case 'k':
+ absolute = 1000;
+ break;
+ case 'm':
+ case 'M':
+ absolute = 1000000;
+ break;
+ case 'g':
+ case 'G':
+ absolute = 1000000000;
+ break;
+ default:
+ break;
+ }
+ if (absolute) {
+ unsigned long long bytes;
+ unsigned long unit;
+
+ bytes = (unsigned long long) i * absolute;
+ unit = sector_size * units_per_sector;
+ bytes += unit/2; /* round */
+ bytes /= unit;
+ i = bytes;
+ }
+ if (minus)
+ i = -i;
+ i += base;
+ } else {
+ i = atoi(line_ptr);
+ while (isdigit(*line_ptr)) {
+ line_ptr++;
+ use_default = 0;
+ }
+ }
+ if (use_default) {
+ i = dflt;
+ printf("Using default value %u\n", i);
+ }
+ if (i >= low && i <= high)
+ break;
+ printf("Value is out of range\n");
+ }
+ return i;
+}
+
+static int
+get_partition(int warn, int max)
+{
+ struct pte *pe;
+ int i;
+
+ i = read_int(1, 0, max, 0, "Partition number") - 1;
+ pe = &ptes[i];
+
+ if (warn) {
+ if ((!LABEL_IS_SUN && !LABEL_IS_SGI && !pe->part_table->sys_ind)
+ || (LABEL_IS_SUN && (!sunlabel->partitions[i].num_sectors || !sunlabel->infos[i].id))
+ || (LABEL_IS_SGI && !sgi_get_num_sectors(i))
+ ) {
+ printf("Warning: partition %d has empty type\n", i+1);
+ }
+ }
+ return i;
+}
+
+static int
+get_existing_partition(int warn, int max)
+{
+ int pno = -1;
+ int i;
+
+ for (i = 0; i < max; i++) {
+ struct pte *pe = &ptes[i];
+ struct partition *p = pe->part_table;
+
+ if (p && !is_cleared_partition(p)) {
+ if (pno >= 0)
+ goto not_unique;
+ pno = i;
+ }
+ }
+ if (pno >= 0) {
+ printf("Selected partition %d\n", pno+1);
+ return pno;
+ }
+ printf("No partition is defined yet!\n");
+ return -1;
+
+ not_unique:
+ return get_partition(warn, max);
+}
+
+static int
+get_nonexisting_partition(int warn, int max)
+{
+ int pno = -1;
+ int i;
+
+ for (i = 0; i < max; i++) {
+ struct pte *pe = &ptes[i];
+ struct partition *p = pe->part_table;
+
+ if (p && is_cleared_partition(p)) {
+ if (pno >= 0)
+ goto not_unique;
+ pno = i;
+ }
+ }
+ if (pno >= 0) {
+ printf("Selected partition %d\n", pno+1);
+ return pno;
+ }
+ printf("All primary partitions have been defined already!\n");
+ return -1;
+
+ not_unique:
+ return get_partition(warn, max);
+}
+
+
+static void
+change_units(void)
+{
+ display_in_cyl_units = !display_in_cyl_units;
+ update_units();
+ printf("Changing display/entry units to %s\n",
+ str_units(PLURAL));
+}
+
+static void
+toggle_active(int i)
+{
+ struct pte *pe = &ptes[i];
+ struct partition *p = pe->part_table;
+
+ if (IS_EXTENDED(p->sys_ind) && !p->boot_ind)
+ printf("WARNING: Partition %d is an extended partition\n", i + 1);
+ p->boot_ind = (p->boot_ind ? 0 : ACTIVE_FLAG);
+ pe->changed = 1;
+}
+
+static void
+toggle_dos_compatibility_flag(void)
+{
+ dos_compatible_flag = ~dos_compatible_flag;
+ if (dos_compatible_flag) {
+ sector_offset = sectors;
+ printf("DOS Compatibility flag is set\n");
+ } else {
+ sector_offset = 1;
+ printf("DOS Compatibility flag is not set\n");
+ }
+}
+
+static void
+delete_partition(int i)
+{
+ struct pte *pe = &ptes[i];
+ struct partition *p = pe->part_table;
+ struct partition *q = pe->ext_pointer;
+
+/* Note that for the fifth partition (i == 4) we don't actually
+ * decrement partitions.
+ */
+
+ if (warn_geometry())
+ return; /* C/H/S not set */
+ pe->changed = 1;
+
+ if (LABEL_IS_SUN) {
+ sun_delete_partition(i);
+ return;
+ }
+ if (LABEL_IS_SGI) {
+ sgi_delete_partition(i);
+ return;
+ }
+
+ if (i < 4) {
+ if (IS_EXTENDED(p->sys_ind) && i == ext_index) {
+ partitions = 4;
+ ptes[ext_index].ext_pointer = NULL;
+ extended_offset = 0;
+ }
+ clear_partition(p);
+ return;
+ }
+
+ if (!q->sys_ind && i > 4) {
+ /* the last one in the chain - just delete */
+ --partitions;
+ --i;
+ clear_partition(ptes[i].ext_pointer);
+ ptes[i].changed = 1;
+ } else {
+ /* not the last one - further ones will be moved down */
+ if (i > 4) {
+ /* delete this link in the chain */
+ p = ptes[i-1].ext_pointer;
+ *p = *q;
+ set_start_sect(p, get_start_sect(q));
+ set_nr_sects(p, get_nr_sects(q));
+ ptes[i-1].changed = 1;
+ } else if (partitions > 5) { /* 5 will be moved to 4 */
+ /* the first logical in a longer chain */
+ pe = &ptes[5];
+
+ if (pe->part_table) /* prevent SEGFAULT */
+ set_start_sect(pe->part_table,
+ get_partition_start(pe) -
+ extended_offset);
+ pe->offset = extended_offset;
+ pe->changed = 1;
+ }
+
+ if (partitions > 5) {
+ partitions--;
+ while (i < partitions) {
+ ptes[i] = ptes[i+1];
+ i++;
+ }
+ } else
+ /* the only logical: clear only */
+ clear_partition(ptes[i].part_table);
+ }
+}
+
+static void
+change_sysid(void)
+{
+ int i, sys, origsys;
+ struct partition *p;
+
+ /* If sgi_label then don't use get_existing_partition,
+ let the user select a partition, since get_existing_partition()
+ only works for Linux like partition tables. */
+ if (!LABEL_IS_SGI) {
+ i = get_existing_partition(0, partitions);
+ } else {
+ i = get_partition(0, partitions);
+ }
+ if (i == -1)
+ return;
+ p = ptes[i].part_table;
+ origsys = sys = get_sysid(i);
+
+ /* if changing types T to 0 is allowed, then
+ the reverse change must be allowed, too */
+ if (!sys && !LABEL_IS_SGI && !LABEL_IS_SUN && !get_nr_sects(p)) {
+ printf("Partition %d does not exist yet!\n", i + 1);
+ return;
+ }
+ while (1) {
+ sys = read_hex(get_sys_types());
+
+ if (!sys && !LABEL_IS_SGI && !LABEL_IS_SUN) {
+ printf("Type 0 means free space to many systems\n"
+ "(but not to Linux). Having partitions of\n"
+ "type 0 is probably unwise.\n");
+ /* break; */
+ }
+
+ if (!LABEL_IS_SUN && !LABEL_IS_SGI) {
+ if (IS_EXTENDED(sys) != IS_EXTENDED(p->sys_ind)) {
+ printf("You cannot change a partition into"
+ " an extended one or vice versa\n");
+ break;
+ }
+ }
+
+ if (sys < 256) {
+#if ENABLE_FEATURE_SUN_LABEL
+ if (LABEL_IS_SUN && i == 2 && sys != SUN_WHOLE_DISK)
+ printf("Consider leaving partition 3 "
+ "as Whole disk (5),\n"
+ "as SunOS/Solaris expects it and "
+ "even Linux likes it\n\n");
+#endif
+#if ENABLE_FEATURE_SGI_LABEL
+ if (LABEL_IS_SGI &&
+ (
+ (i == 10 && sys != SGI_ENTIRE_DISK) ||
+ (i == 8 && sys != 0)
+ )
+ ) {
+ printf("Consider leaving partition 9 "
+ "as volume header (0),\nand "
+ "partition 11 as entire volume (6)"
+ "as IRIX expects it\n\n");
+ }
+#endif
+ if (sys == origsys)
+ break;
+ if (LABEL_IS_SUN) {
+ sun_change_sysid(i, sys);
+ } else if (LABEL_IS_SGI) {
+ sgi_change_sysid(i, sys);
+ } else
+ p->sys_ind = sys;
+
+ printf("Changed system type of partition %d "
+ "to %x (%s)\n", i + 1, sys,
+ partition_type(sys));
+ ptes[i].changed = 1;
+ if (is_dos_partition(origsys) ||
+ is_dos_partition(sys))
+ dos_changed = 1;
+ break;
+ }
+ }
+}
+#endif /* FEATURE_FDISK_WRITABLE */
+
+
+/* check_consistency() and linear2chs() added Sat Mar 6 12:28:16 1993,
+ * faith@cs.unc.edu, based on code fragments from pfdisk by Gordon W. Ross,
+ * Jan. 1990 (version 1.2.1 by Gordon W. Ross Aug. 1990; Modified by S.
+ * Lubkin Oct. 1991). */
+
+static void
+linear2chs(unsigned ls, unsigned *c, unsigned *h, unsigned *s)
+{
+ int spc = heads * sectors;
+
+ *c = ls / spc;
+ ls = ls % spc;
+ *h = ls / sectors;
+ *s = ls % sectors + 1; /* sectors count from 1 */
+}
+
+static void
+check_consistency(const struct partition *p, int partition)
+{
+ unsigned pbc, pbh, pbs; /* physical beginning c, h, s */
+ unsigned pec, peh, pes; /* physical ending c, h, s */
+ unsigned lbc, lbh, lbs; /* logical beginning c, h, s */
+ unsigned lec, leh, les; /* logical ending c, h, s */
+
+ if (!heads || !sectors || (partition >= 4))
+ return; /* do not check extended partitions */
+
+/* physical beginning c, h, s */
+ pbc = (p->cyl & 0xff) | ((p->sector << 2) & 0x300);
+ pbh = p->head;
+ pbs = p->sector & 0x3f;
+
+/* physical ending c, h, s */
+ pec = (p->end_cyl & 0xff) | ((p->end_sector << 2) & 0x300);
+ peh = p->end_head;
+ pes = p->end_sector & 0x3f;
+
+/* compute logical beginning (c, h, s) */
+ linear2chs(get_start_sect(p), &lbc, &lbh, &lbs);
+
+/* compute logical ending (c, h, s) */
+ linear2chs(get_start_sect(p) + get_nr_sects(p) - 1, &lec, &leh, &les);
+
+/* Same physical / logical beginning? */
+ if (cylinders <= 1024 && (pbc != lbc || pbh != lbh || pbs != lbs)) {
+ printf("Partition %d has different physical/logical "
+ "beginnings (non-Linux?):\n", partition + 1);
+ printf(" phys=(%d, %d, %d) ", pbc, pbh, pbs);
+ printf("logical=(%d, %d, %d)\n",lbc, lbh, lbs);
+ }
+
+/* Same physical / logical ending? */
+ if (cylinders <= 1024 && (pec != lec || peh != leh || pes != les)) {
+ printf("Partition %d has different physical/logical "
+ "endings:\n", partition + 1);
+ printf(" phys=(%d, %d, %d) ", pec, peh, pes);
+ printf("logical=(%d, %d, %d)\n", lec, leh, les);
+ }
+
+/* Ending on cylinder boundary? */
+ if (peh != (heads - 1) || pes != sectors) {
+ printf("Partition %i does not end on cylinder boundary\n",
+ partition + 1);
+ }
+}
+
+static void
+list_disk_geometry(void)
+{
+ long long bytes = (total_number_of_sectors << 9);
+ long megabytes = bytes/1000000;
+
+ if (megabytes < 10000)
+ printf("\nDisk %s: %ld MB, %lld bytes\n",
+ disk_device, megabytes, bytes);
+ else
+ printf("\nDisk %s: %ld.%ld GB, %lld bytes\n",
+ disk_device, megabytes/1000, (megabytes/100)%10, bytes);
+ printf("%d heads, %d sectors/track, %d cylinders",
+ heads, sectors, cylinders);
+ if (units_per_sector == 1)
+ printf(", total %llu sectors",
+ total_number_of_sectors / (sector_size/512));
+ printf("\nUnits = %s of %d * %d = %d bytes\n\n",
+ str_units(PLURAL),
+ units_per_sector, sector_size, units_per_sector * sector_size);
+}
+
+/*
+ * Check whether partition entries are ordered by their starting positions.
+ * Return 0 if OK. Return i if partition i should have been earlier.
+ * Two separate checks: primary and logical partitions.
+ */
+static int
+wrong_p_order(int *prev)
+{
+ const struct pte *pe;
+ const struct partition *p;
+ off_t last_p_start_pos = 0, p_start_pos;
+ int i, last_i = 0;
+
+ for (i = 0 ; i < partitions; i++) {
+ if (i == 4) {
+ last_i = 4;
+ last_p_start_pos = 0;
+ }
+ pe = &ptes[i];
+ if ((p = pe->part_table)->sys_ind) {
+ p_start_pos = get_partition_start(pe);
+
+ if (last_p_start_pos > p_start_pos) {
+ if (prev)
+ *prev = last_i;
+ return i;
+ }
+
+ last_p_start_pos = p_start_pos;
+ last_i = i;
+ }
+ }
+ return 0;
+}
+
+#if ENABLE_FEATURE_FDISK_ADVANCED
+/*
+ * Fix the chain of logicals.
+ * extended_offset is unchanged, the set of sectors used is unchanged
+ * The chain is sorted so that sectors increase, and so that
+ * starting sectors increase.
+ *
+ * After this it may still be that cfdisk doesnt like the table.
+ * (This is because cfdisk considers expanded parts, from link to
+ * end of partition, and these may still overlap.)
+ * Now
+ * sfdisk /dev/hda > ohda; sfdisk /dev/hda < ohda
+ * may help.
+ */
+static void
+fix_chain_of_logicals(void)
+{
+ int j, oj, ojj, sj, sjj;
+ struct partition *pj,*pjj,tmp;
+
+ /* Stage 1: sort sectors but leave sector of part 4 */
+ /* (Its sector is the global extended_offset.) */
+ stage1:
+ for (j = 5; j < partitions-1; j++) {
+ oj = ptes[j].offset;
+ ojj = ptes[j+1].offset;
+ if (oj > ojj) {
+ ptes[j].offset = ojj;
+ ptes[j+1].offset = oj;
+ pj = ptes[j].part_table;
+ set_start_sect(pj, get_start_sect(pj)+oj-ojj);
+ pjj = ptes[j+1].part_table;
+ set_start_sect(pjj, get_start_sect(pjj)+ojj-oj);
+ set_start_sect(ptes[j-1].ext_pointer,
+ ojj-extended_offset);
+ set_start_sect(ptes[j].ext_pointer,
+ oj-extended_offset);
+ goto stage1;
+ }
+ }
+
+ /* Stage 2: sort starting sectors */
+ stage2:
+ for (j = 4; j < partitions-1; j++) {
+ pj = ptes[j].part_table;
+ pjj = ptes[j+1].part_table;
+ sj = get_start_sect(pj);
+ sjj = get_start_sect(pjj);
+ oj = ptes[j].offset;
+ ojj = ptes[j+1].offset;
+ if (oj+sj > ojj+sjj) {
+ tmp = *pj;
+ *pj = *pjj;
+ *pjj = tmp;
+ set_start_sect(pj, ojj+sjj-oj);
+ set_start_sect(pjj, oj+sj-ojj);
+ goto stage2;
+ }
+ }
+
+ /* Probably something was changed */
+ for (j = 4; j < partitions; j++)
+ ptes[j].changed = 1;
+}
+
+
+static void
+fix_partition_table_order(void)
+{
+ struct pte *pei, *pek;
+ int i,k;
+
+ if (!wrong_p_order(NULL)) {
+ printf("Ordering is already correct\n\n");
+ return;
+ }
+
+ while ((i = wrong_p_order(&k)) != 0 && i < 4) {
+ /* partition i should have come earlier, move it */
+ /* We have to move data in the MBR */
+ struct partition *pi, *pk, *pe, pbuf;
+ pei = &ptes[i];
+ pek = &ptes[k];
+
+ pe = pei->ext_pointer;
+ pei->ext_pointer = pek->ext_pointer;
+ pek->ext_pointer = pe;
+
+ pi = pei->part_table;
+ pk = pek->part_table;
+
+ memmove(&pbuf, pi, sizeof(struct partition));
+ memmove(pi, pk, sizeof(struct partition));
+ memmove(pk, &pbuf, sizeof(struct partition));
+
+ pei->changed = pek->changed = 1;
+ }
+
+ if (i)
+ fix_chain_of_logicals();
+
+ printf("Done.\n");
+
+}
+#endif
+
+static void
+list_table(int xtra)
+{
+ const struct partition *p;
+ int i, w;
+
+ if (LABEL_IS_SUN) {
+ sun_list_table(xtra);
+ return;
+ }
+ if (LABEL_IS_SUN) {
+ sgi_list_table(xtra);
+ return;
+ }
+
+ list_disk_geometry();
+
+ if (LABEL_IS_OSF) {
+ xbsd_print_disklabel(xtra);
+ return;
+ }
+
+ /* Heuristic: we list partition 3 of /dev/foo as /dev/foo3,
+ but if the device name ends in a digit, say /dev/foo1,
+ then the partition is called /dev/foo1p3. */
+ w = strlen(disk_device);
+ if (w && isdigit(disk_device[w-1]))
+ w++;
+ if (w < 5)
+ w = 5;
+
+ // 1 12345678901 12345678901 12345678901 12
+ printf("%*s Boot Start End Blocks Id System\n",
+ w+1, "Device");
+
+ for (i = 0; i < partitions; i++) {
+ const struct pte *pe = &ptes[i];
+ off_t psects;
+ off_t pblocks;
+ unsigned podd;
+
+ p = pe->part_table;
+ if (!p || is_cleared_partition(p))
+ continue;
+
+ psects = get_nr_sects(p);
+ pblocks = psects;
+ podd = 0;
+
+ if (sector_size < 1024) {
+ pblocks /= (1024 / sector_size);
+ podd = psects % (1024 / sector_size);
+ }
+ if (sector_size > 1024)
+ pblocks *= (sector_size / 1024);
+
+ printf("%s %c %11llu %11llu %11llu%c %2x %s\n",
+ partname(disk_device, i+1, w+2),
+ !p->boot_ind ? ' ' : p->boot_ind == ACTIVE_FLAG /* boot flag */
+ ? '*' : '?',
+ (unsigned long long) cround(get_partition_start(pe)), /* start */
+ (unsigned long long) cround(get_partition_start(pe) + psects /* end */
+ - (psects ? 1 : 0)),
+ (unsigned long long) pblocks, podd ? '+' : ' ', /* odd flag on end */
+ p->sys_ind, /* type id */
+ partition_type(p->sys_ind)); /* type name */
+
+ check_consistency(p, i);
+ }
+
+ /* Is partition table in disk order? It need not be, but... */
+ /* partition table entries are not checked for correct order if this
+ is a sgi, sun or aix labeled disk... */
+ if (LABEL_IS_DOS && wrong_p_order(NULL)) {
+ /* FIXME */
+ printf("\nPartition table entries are not in disk order\n");
+ }
+}
+
+#if ENABLE_FEATURE_FDISK_ADVANCED
+static void
+x_list_table(int extend)
+{
+ const struct pte *pe;
+ const struct partition *p;
+ int i;
+
+ printf("\nDisk %s: %d heads, %d sectors, %d cylinders\n\n",
+ disk_device, heads, sectors, cylinders);
+ printf("Nr AF Hd Sec Cyl Hd Sec Cyl Start Size ID\n");
+ for (i = 0 ; i < partitions; i++) {
+ pe = &ptes[i];
+ p = (extend ? pe->ext_pointer : pe->part_table);
+ if (p != NULL) {
+ printf("%2d %02x%4d%4d%5d%4d%4d%5d%11u%11u %02x\n",
+ i + 1, p->boot_ind, p->head,
+ sector(p->sector),
+ cylinder(p->sector, p->cyl), p->end_head,
+ sector(p->end_sector),
+ cylinder(p->end_sector, p->end_cyl),
+ get_start_sect(p), get_nr_sects(p), p->sys_ind);
+ if (p->sys_ind)
+ check_consistency(p, i);
+ }
+ }
+}
+#endif
+
+#if ENABLE_FEATURE_FDISK_WRITABLE
+static void
+fill_bounds(off_t *first, off_t *last)
+{
+ int i;
+ const struct pte *pe = &ptes[0];
+ const struct partition *p;
+
+ for (i = 0; i < partitions; pe++,i++) {
+ p = pe->part_table;
+ if (!p->sys_ind || IS_EXTENDED(p->sys_ind)) {
+ first[i] = 0xffffffff;
+ last[i] = 0;
+ } else {
+ first[i] = get_partition_start(pe);
+ last[i] = first[i] + get_nr_sects(p) - 1;
+ }
+ }
+}
+
+static void
+check(int n, unsigned h, unsigned s, unsigned c, off_t start)
+{
+ off_t total, real_s, real_c;
+
+ real_s = sector(s) - 1;
+ real_c = cylinder(s, c);
+ total = (real_c * sectors + real_s) * heads + h;
+ if (!total)
+ printf("Partition %d contains sector 0\n", n);
+ if (h >= heads)
+ printf("Partition %d: head %d greater than maximum %d\n",
+ n, h + 1, heads);
+ if (real_s >= sectors)
+ printf("Partition %d: sector %d greater than "
+ "maximum %d\n", n, s, sectors);
+ if (real_c >= cylinders)
+ printf("Partition %d: cylinder %"OFF_FMT"u greater than "
+ "maximum %d\n", n, real_c + 1, cylinders);
+ if (cylinders <= 1024 && start != total)
+ printf("Partition %d: previous sectors %"OFF_FMT"u disagrees with "
+ "total %"OFF_FMT"u\n", n, start, total);
+}
+
+static void
+verify(void)
+{
+ int i, j;
+ unsigned total = 1;
+ off_t first[partitions], last[partitions];
+ struct partition *p;
+
+ if (warn_geometry())
+ return;
+
+ if (LABEL_IS_SUN) {
+ verify_sun();
+ return;
+ }
+ if (LABEL_IS_SGI) {
+ verify_sgi(1);
+ return;
+ }
+
+ fill_bounds(first, last);
+ for (i = 0; i < partitions; i++) {
+ struct pte *pe = &ptes[i];
+
+ p = pe->part_table;
+ if (p->sys_ind && !IS_EXTENDED(p->sys_ind)) {
+ check_consistency(p, i);
+ if (get_partition_start(pe) < first[i])
+ printf("Warning: bad start-of-data in "
+ "partition %d\n", i + 1);
+ check(i + 1, p->end_head, p->end_sector, p->end_cyl,
+ last[i]);
+ total += last[i] + 1 - first[i];
+ for (j = 0; j < i; j++) {
+ if ((first[i] >= first[j] && first[i] <= last[j])
+ || ((last[i] <= last[j] && last[i] >= first[j]))) {
+ printf("Warning: partition %d overlaps "
+ "partition %d\n", j + 1, i + 1);
+ total += first[i] >= first[j] ?
+ first[i] : first[j];
+ total -= last[i] <= last[j] ?
+ last[i] : last[j];
+ }
+ }
+ }
+ }
+
+ if (extended_offset) {
+ struct pte *pex = &ptes[ext_index];
+ off_t e_last = get_start_sect(pex->part_table) +
+ get_nr_sects(pex->part_table) - 1;
+
+ for (i = 4; i < partitions; i++) {
+ total++;
+ p = ptes[i].part_table;
+ if (!p->sys_ind) {
+ if (i != 4 || i + 1 < partitions)
+ printf("Warning: partition %d "
+ "is empty\n", i + 1);
+ } else if (first[i] < extended_offset || last[i] > e_last) {
+ printf("Logical partition %d not entirely in "
+ "partition %d\n", i + 1, ext_index + 1);
+ }
+ }
+ }
+
+ if (total > heads * sectors * cylinders)
+ printf("Total allocated sectors %d greater than the maximum "
+ "%d\n", total, heads * sectors * cylinders);
+ else {
+ total = heads * sectors * cylinders - total;
+ if (total != 0)
+ printf("%d unallocated sectors\n", total);
+ }
+}
+
+static void
+add_partition(int n, int sys)
+{
+ char mesg[256]; /* 48 does not suffice in Japanese */
+ int i, num_read = 0;
+ struct partition *p = ptes[n].part_table;
+ struct partition *q = ptes[ext_index].part_table;
+ long long llimit;
+ off_t start, stop = 0, limit, temp,
+ first[partitions], last[partitions];
+
+ if (p && p->sys_ind) {
+ printf(msg_part_already_defined, n + 1);
+ return;
+ }
+ fill_bounds(first, last);
+ if (n < 4) {
+ start = sector_offset;
+ if (display_in_cyl_units || !total_number_of_sectors)
+ llimit = heads * sectors * cylinders - 1;
+ else
+ llimit = total_number_of_sectors - 1;
+ limit = llimit;
+ if (limit != llimit)
+ limit = 0x7fffffff;
+ if (extended_offset) {
+ first[ext_index] = extended_offset;
+ last[ext_index] = get_start_sect(q) +
+ get_nr_sects(q) - 1;
+ }
+ } else {
+ start = extended_offset + sector_offset;
+ limit = get_start_sect(q) + get_nr_sects(q) - 1;
+ }
+ if (display_in_cyl_units)
+ for (i = 0; i < partitions; i++)
+ first[i] = (cround(first[i]) - 1) * units_per_sector;
+
+ snprintf(mesg, sizeof(mesg), "First %s", str_units(SINGULAR));
+ do {
+ temp = start;
+ for (i = 0; i < partitions; i++) {
+ int lastplusoff;
+
+ if (start == ptes[i].offset)
+ start += sector_offset;
+ lastplusoff = last[i] + ((n < 4) ? 0 : sector_offset);
+ if (start >= first[i] && start <= lastplusoff)
+ start = lastplusoff + 1;
+ }
+ if (start > limit)
+ break;
+ if (start >= temp+units_per_sector && num_read) {
+ printf("Sector %"OFF_FMT"d is already allocated\n", temp);
+ temp = start;
+ num_read = 0;
+ }
+ if (!num_read && start == temp) {
+ off_t saved_start;
+
+ saved_start = start;
+ start = read_int(cround(saved_start), cround(saved_start), cround(limit),
+ 0, mesg);
+ if (display_in_cyl_units) {
+ start = (start - 1) * units_per_sector;
+ if (start < saved_start) start = saved_start;
+ }
+ num_read = 1;
+ }
+ } while (start != temp || !num_read);
+ if (n > 4) { /* NOT for fifth partition */
+ struct pte *pe = &ptes[n];
+
+ pe->offset = start - sector_offset;
+ if (pe->offset == extended_offset) { /* must be corrected */
+ pe->offset++;
+ if (sector_offset == 1)
+ start++;
+ }
+ }
+
+ for (i = 0; i < partitions; i++) {
+ struct pte *pe = &ptes[i];
+
+ if (start < pe->offset && limit >= pe->offset)
+ limit = pe->offset - 1;
+ if (start < first[i] && limit >= first[i])
+ limit = first[i] - 1;
+ }
+ if (start > limit) {
+ printf("No free sectors available\n");
+ if (n > 4)
+ partitions--;
+ return;
+ }
+ if (cround(start) == cround(limit)) {
+ stop = limit;
+ } else {
+ snprintf(mesg, sizeof(mesg),
+ "Last %s or +size or +sizeM or +sizeK",
+ str_units(SINGULAR));
+ stop = read_int(cround(start), cround(limit), cround(limit),
+ cround(start), mesg);
+ if (display_in_cyl_units) {
+ stop = stop * units_per_sector - 1;
+ if (stop >limit)
+ stop = limit;
+ }
+ }
+
+ set_partition(n, 0, start, stop, sys);
+ if (n > 4)
+ set_partition(n - 1, 1, ptes[n].offset, stop, EXTENDED);
+
+ if (IS_EXTENDED(sys)) {
+ struct pte *pe4 = &ptes[4];
+ struct pte *pen = &ptes[n];
+
+ ext_index = n;
+ pen->ext_pointer = p;
+ pe4->offset = extended_offset = start;
+ pe4->sectorbuffer = xzalloc(sector_size);
+ pe4->part_table = pt_offset(pe4->sectorbuffer, 0);
+ pe4->ext_pointer = pe4->part_table + 1;
+ pe4->changed = 1;
+ partitions = 5;
+ }
+}
+
+static void
+add_logical(void)
+{
+ if (partitions > 5 || ptes[4].part_table->sys_ind) {
+ struct pte *pe = &ptes[partitions];
+
+ pe->sectorbuffer = xzalloc(sector_size);
+ pe->part_table = pt_offset(pe->sectorbuffer, 0);
+ pe->ext_pointer = pe->part_table + 1;
+ pe->offset = 0;
+ pe->changed = 1;
+ partitions++;
+ }
+ add_partition(partitions - 1, LINUX_NATIVE);
+}
+
+static void
+new_partition(void)
+{
+ int i, free_primary = 0;
+
+ if (warn_geometry())
+ return;
+
+ if (LABEL_IS_SUN) {
+ add_sun_partition(get_partition(0, partitions), LINUX_NATIVE);
+ return;
+ }
+ if (LABEL_IS_SGI) {
+ sgi_add_partition(get_partition(0, partitions), LINUX_NATIVE);
+ return;
+ }
+ if (LABEL_IS_AIX) {
+ printf("Sorry - this fdisk cannot handle AIX disk labels.\n"
+"If you want to add DOS-type partitions, create a new empty DOS partition\n"
+"table first (use 'o'). This will destroy the present disk contents.\n");
+ return;
+ }
+
+ for (i = 0; i < 4; i++)
+ free_primary += !ptes[i].part_table->sys_ind;
+
+ if (!free_primary && partitions >= MAXIMUM_PARTS) {
+ printf("The maximum number of partitions has been created\n");
+ return;
+ }
+
+ if (!free_primary) {
+ if (extended_offset)
+ add_logical();
+ else
+ printf("You must delete some partition and add "
+ "an extended partition first\n");
+ } else {
+ char c, line[80];
+ snprintf(line, sizeof(line),
+ "Command action\n"
+ " %s\n"
+ " p primary partition (1-4)\n",
+ (extended_offset ?
+ "l logical (5 or over)" : "e extended"));
+ while (1) {
+ c = read_nonempty(line);
+ if (c == 'p' || c == 'P') {
+ i = get_nonexisting_partition(0, 4);
+ if (i >= 0)
+ add_partition(i, LINUX_NATIVE);
+ return;
+ }
+ if (c == 'l' && extended_offset) {
+ add_logical();
+ return;
+ }
+ if (c == 'e' && !extended_offset) {
+ i = get_nonexisting_partition(0, 4);
+ if (i >= 0)
+ add_partition(i, EXTENDED);
+ return;
+ }
+ printf("Invalid partition number "
+ "for type '%c'\n", c);
+ }
+ }
+}
+
+static void
+write_table(void)
+{
+ int i;
+
+ if (LABEL_IS_DOS) {
+ for (i = 0; i < 3; i++)
+ if (ptes[i].changed)
+ ptes[3].changed = 1;
+ for (i = 3; i < partitions; i++) {
+ struct pte *pe = &ptes[i];
+
+ if (pe->changed) {
+ write_part_table_flag(pe->sectorbuffer);
+ write_sector(pe->offset, pe->sectorbuffer);
+ }
+ }
+ }
+ else if (LABEL_IS_SGI) {
+ /* no test on change? the printf below might be mistaken */
+ sgi_write_table();
+ }
+ else if (LABEL_IS_SUN) {
+ int needw = 0;
+
+ for (i = 0; i < 8; i++)
+ if (ptes[i].changed)
+ needw = 1;
+ if (needw)
+ sun_write_table();
+ }
+
+ printf("The partition table has been altered!\n\n");
+ reread_partition_table(1);
+}
+
+static void
+reread_partition_table(int leave)
+{
+ int i;
+
+ printf("Calling ioctl() to re-read partition table\n");
+ sync();
+ /* sleep(2); Huh? */
+ i = ioctl(fd, BLKRRPART);
+#if 0
+ else {
+ /* some kernel versions (1.2.x) seem to have trouble
+ rereading the partition table, but if asked to do it
+ twice, the second time works. - biro@yggdrasil.com */
+ sync();
+ sleep(2);
+ i = ioctl(fd, BLKRRPART);
+ }
+#endif
+
+ if (i) {
+ bb_perror_msg("WARNING: rereading partition table "
+ "failed, kernel still uses old table");
+ }
+
+#if 0
+ if (dos_changed)
+ printf(
+ "\nWARNING: If you have created or modified any DOS 6.x\n"
+ "partitions, please see the fdisk manual page for additional\n"
+ "information\n");
+#endif
+
+ if (leave) {
+ if (ENABLE_FEATURE_CLEAN_UP)
+ close(fd);
+ exit(i != 0);
+ }
+}
+#endif /* FEATURE_FDISK_WRITABLE */
+
+#if ENABLE_FEATURE_FDISK_ADVANCED
+#define MAX_PER_LINE 16
+static void
+print_buffer(char *pbuffer)
+{
+ int i,l;
+
+ for (i = 0, l = 0; i < sector_size; i++, l++) {
+ if (l == 0)
+ printf("0x%03X:", i);
+ printf(" %02X", (unsigned char) pbuffer[i]);
+ if (l == MAX_PER_LINE - 1) {
+ puts("");
+ l = -1;
+ }
+ }
+ if (l > 0)
+ puts("");
+ puts("");
+}
+
+static void
+print_raw(void)
+{
+ int i;
+
+ printf("Device: %s\n", disk_device);
+ if (LABEL_IS_SGI || LABEL_IS_SUN)
+ print_buffer(MBRbuffer);
+ else {
+ for (i = 3; i < partitions; i++)
+ print_buffer(ptes[i].sectorbuffer);
+ }
+}
+
+static void
+move_begin(int i)
+{
+ struct pte *pe = &ptes[i];
+ struct partition *p = pe->part_table;
+ off_t new, first;
+
+ if (warn_geometry())
+ return;
+ if (!p->sys_ind || !get_nr_sects(p) || IS_EXTENDED(p->sys_ind)) {
+ printf("Partition %d has no data area\n", i + 1);
+ return;
+ }
+ first = get_partition_start(pe);
+ new = read_int(first, first, first + get_nr_sects(p) - 1, first,
+ "New beginning of data") - pe->offset;
+
+ if (new != get_nr_sects(p)) {
+ first = get_nr_sects(p) + get_start_sect(p) - new;
+ set_nr_sects(p, first);
+ set_start_sect(p, new);
+ pe->changed = 1;
+ }
+}
+
+static void
+xselect(void)
+{
+ char c;
+
+ while (1) {
+ putchar('\n');
+ c = tolower(read_nonempty("Expert command (m for help): "));
+ switch (c) {
+ case 'a':
+ if (LABEL_IS_SUN)
+ sun_set_alt_cyl();
+ break;
+ case 'b':
+ if (LABEL_IS_DOS)
+ move_begin(get_partition(0, partitions));
+ break;
+ case 'c':
+ user_cylinders = cylinders =
+ read_int(1, cylinders, 1048576, 0,
+ "Number of cylinders");
+ if (LABEL_IS_SUN)
+ sun_set_ncyl(cylinders);
+ if (LABEL_IS_DOS)
+ warn_cylinders();
+ break;
+ case 'd':
+ print_raw();
+ break;
+ case 'e':
+ if (LABEL_IS_SGI)
+ sgi_set_xcyl();
+ else if (LABEL_IS_SUN)
+ sun_set_xcyl();
+ else if (LABEL_IS_DOS)
+ x_list_table(1);
+ break;
+ case 'f':
+ if (LABEL_IS_DOS)
+ fix_partition_table_order();
+ break;
+ case 'g':
+#if ENABLE_FEATURE_SGI_LABEL
+ create_sgilabel();
+#endif
+ break;
+ case 'h':
+ user_heads = heads = read_int(1, heads, 256, 0,
+ "Number of heads");
+ update_units();
+ break;
+ case 'i':
+ if (LABEL_IS_SUN)
+ sun_set_ilfact();
+ break;
+ case 'o':
+ if (LABEL_IS_SUN)
+ sun_set_rspeed();
+ break;
+ case 'p':
+ if (LABEL_IS_SUN)
+ list_table(1);
+ else
+ x_list_table(0);
+ break;
+ case 'q':
+ close(fd);
+ puts("");
+ exit(0);
+ case 'r':
+ return;
+ case 's':
+ user_sectors = sectors = read_int(1, sectors, 63, 0,
+ "Number of sectors");
+ if (dos_compatible_flag) {
+ sector_offset = sectors;
+ printf("Warning: setting sector offset for DOS "
+ "compatiblity\n");
+ }
+ update_units();
+ break;
+ case 'v':
+ verify();
+ break;
+ case 'w':
+ write_table(); /* does not return */
+ break;
+ case 'y':
+ if (LABEL_IS_SUN)
+ sun_set_pcylcount();
+ break;
+ default:
+ xmenu();
+ }
+ }
+}
+#endif /* ADVANCED mode */
+
+static int
+is_ide_cdrom_or_tape(const char *device)
+{
+ FILE *procf;
+ char buf[100];
+ struct stat statbuf;
+ int is_ide = 0;
+
+ /* No device was given explicitly, and we are trying some
+ likely things. But opening /dev/hdc may produce errors like
+ "hdc: tray open or drive not ready"
+ if it happens to be a CD-ROM drive. It even happens that
+ the process hangs on the attempt to read a music CD.
+ So try to be careful. This only works since 2.1.73. */
+
+ if (strncmp("/dev/hd", device, 7))
+ return 0;
+
+ snprintf(buf, sizeof(buf), "/proc/ide/%s/media", device+5);
+ procf = fopen(buf, "r");
+ if (procf != NULL && fgets(buf, sizeof(buf), procf))
+ is_ide = (!strncmp(buf, "cdrom", 5) ||
+ !strncmp(buf, "tape", 4));
+ else
+ /* Now when this proc file does not exist, skip the
+ device when it is read-only. */
+ if (stat(device, &statbuf) == 0)
+ is_ide = ((statbuf.st_mode & 0222) == 0);
+
+ if (procf)
+ fclose(procf);
+ return is_ide;
+}
+
+
+static void
+trydev(const char *device, int user_specified)
+{
+ int gb;
+
+ disk_device = device;
+ if (setjmp(listingbuf))
+ return;
+ if (!user_specified)
+ if (is_ide_cdrom_or_tape(device))
+ return;
+ fd = open(disk_device, type_open);
+ if (fd >= 0) {
+ gb = get_boot(try_only);
+ if (gb > 0) { /* I/O error */
+ close(fd);
+ } else if (gb < 0) { /* no DOS signature */
+ list_disk_geometry();
+ if (LABEL_IS_AIX) {
+ return;
+ }
+#if ENABLE_FEATURE_OSF_LABEL
+ if (bsd_trydev(device) < 0)
+#endif
+ printf("Disk %s doesn't contain a valid "
+ "partition table\n", device);
+ close(fd);
+ } else {
+ close(fd);
+ list_table(0);
+#if ENABLE_FEATURE_FDISK_WRITABLE
+ if (!LABEL_IS_SUN && partitions > 4){
+ delete_partition(ext_index);
+ }
+#endif
+ }
+ } else {
+ /* Ignore other errors, since we try IDE
+ and SCSI hard disks which may not be
+ installed on the system. */
+ if (errno == EACCES) {
+ printf("Cannot open %s\n", device);
+ return;
+ }
+ }
+}
+
+/* for fdisk -l: try all things in /proc/partitions
+ that look like a partition name (do not end in a digit) */
+static void
+tryprocpt(void)
+{
+ FILE *procpt;
+ char line[100], ptname[100], devname[120], *s;
+ int ma, mi, sz;
+
+ procpt = fopen_or_warn("/proc/partitions", "r");
+
+ while (fgets(line, sizeof(line), procpt)) {
+ if (sscanf(line, " %d %d %d %[^\n ]",
+ &ma, &mi, &sz, ptname) != 4)
+ continue;
+ for (s = ptname; *s; s++);
+ if (isdigit(s[-1]))
+ continue;
+ sprintf(devname, "/dev/%s", ptname);
+ trydev(devname, 0);
+ }
+#if ENABLE_FEATURE_CLEAN_UP
+ fclose(procpt);
+#endif
+}
+
+#if ENABLE_FEATURE_FDISK_WRITABLE
+static void
+unknown_command(int c)
+{
+ printf("%c: unknown command\n", c);
+}
+#endif
+
+int fdisk_main(int argc, char **argv);
+int fdisk_main(int argc, char **argv)
+{
+ char *str_b, *str_C, *str_H, *str_S;
+ unsigned opt;
+ /*
+ * fdisk -v
+ * fdisk -l [-b sectorsize] [-u] device ...
+ * fdisk -s [partition] ...
+ * fdisk [-b sectorsize] [-u] device
+ *
+ * Options -C, -H, -S set the geometry.
+ */
+ enum {
+ OPT_b = 1 << 0,
+ OPT_C = 1 << 1,
+ OPT_H = 1 << 2,
+ OPT_l = 1 << 3,
+ OPT_S = 1 << 4,
+ OPT_u = 1 << 5,
+ OPT_s = (1 << 6) * ENABLE_FEATURE_FDISK_BLKSIZE,
+ };
+
+ PTR_TO_GLOBALS = xzalloc(sizeof(G));
+
+ opt = getopt32(argc, argv, "b:C:H:lS:u" USE_FEATURE_FDISK_BLKSIZE("s"),
+ &str_b, &str_C, &str_H, &str_S);
+ argc -= optind;
+ argv += optind;
+ if (opt & OPT_b) { // -b
+ /* Ugly: this sector size is really per device,
+ so cannot be combined with multiple disks,
+ and the same goes for the C/H/S options.
+ */
+ sector_size = xatoi_u(str_b);
+ if (sector_size != 512 && sector_size != 1024 &&
+ sector_size != 2048)
+ bb_show_usage();
+ sector_offset = 2;
+ user_set_sector_size = 1;
+ }
+ if (opt & OPT_C) user_cylinders = xatoi_u(str_C); // -C
+ if (opt & OPT_H) { // -H
+ user_heads = xatoi_u(str_H);
+ if (user_heads <= 0 || user_heads >= 256)
+ user_heads = 0;
+ }
+ //if (opt & OPT_l) // -l
+ if (opt & OPT_S) { // -S
+ user_sectors = xatoi_u(str_S);
+ if (user_sectors <= 0 || user_sectors >= 64)
+ user_sectors = 0;
+ }
+ if (opt & OPT_u) display_in_cyl_units = 0; // -u
+ //if (opt & OPT_s) // -s
+
+ if (user_set_sector_size && argc != 1)
+ printf("Warning: the -b (set sector size) option should"
+ " be used with one specified device\n");
+
+#if ENABLE_FEATURE_FDISK_WRITABLE
+ if (opt & OPT_l) {
+ nowarn = 1;
+#endif
+ type_open = O_RDONLY;
+ if (argc > 0) {
+ int k;
+#if defined(__GNUC__)
+ /* avoid gcc warning:
+ variable `k' might be clobbered by `longjmp' */
+ (void)&k;
+#endif
+ listing = 1;
+ for (k = 0; k < argc; k++)
+ trydev(argv[k], 1);
+ } else {
+ /* we no longer have default device names */
+ /* but, we can use /proc/partitions instead */
+ tryprocpt();
+ }
+ return 0;
+#if ENABLE_FEATURE_FDISK_WRITABLE
+ }
+#endif
+
+#if ENABLE_FEATURE_FDISK_BLKSIZE
+ if (opt & OPT_s) {
+ long size;
+ int j;
+
+ nowarn = 1;
+ type_open = O_RDONLY;
+
+ if (argc <= 0)
+ bb_show_usage();
+
+ for (j = 0; j < argc; j++) {
+ disk_device = argv[j];
+ fd = open(disk_device, type_open);
+ if (fd < 0)
+ fdisk_fatal(unable_to_open);
+ if (ioctl(fd, BLKGETSIZE, &size))
+ fdisk_fatal(ioctl_error);
+ close(fd);
+ if (argc == 1)
+ printf("%ld\n", size/2);
+ else
+ printf("%s: %ld\n", argv[j], size/2);
+ }
+ return 0;
+ }
+#endif
+
+#if ENABLE_FEATURE_FDISK_WRITABLE
+ if (argc != 1)
+ bb_show_usage();
+
+ disk_device = argv[0];
+ get_boot(fdisk);
+
+ if (LABEL_IS_OSF) {
+ /* OSF label, and no DOS label */
+ printf("Detected an OSF/1 disklabel on %s, entering "
+ "disklabel mode\n", disk_device);
+ bsd_select();
+ /*Why do we do this? It seems to be counter-intuitive*/
+ current_label_type = label_dos;
+ /* If we return we may want to make an empty DOS label? */
+ }
+
+ while (1) {
+ int c;
+ putchar('\n');
+ c = tolower(read_nonempty("Command (m for help): "));
+ switch (c) {
+ case 'a':
+ if (LABEL_IS_DOS)
+ toggle_active(get_partition(1, partitions));
+ else if (LABEL_IS_SUN)
+ toggle_sunflags(get_partition(1, partitions),
+ 0x01);
+ else if (LABEL_IS_SGI)
+ sgi_set_bootpartition(
+ get_partition(1, partitions));
+ else
+ unknown_command(c);
+ break;
+ case 'b':
+ if (LABEL_IS_SGI) {
+ printf("\nThe current boot file is: %s\n",
+ sgi_get_bootfile());
+ if (read_maybe_empty("Please enter the name of the "
+ "new boot file: ") == '\n')
+ printf("Boot file unchanged\n");
+ else
+ sgi_set_bootfile(line_ptr);
+ }
+#if ENABLE_FEATURE_OSF_LABEL
+ else
+ bsd_select();
+#endif
+ break;
+ case 'c':
+ if (LABEL_IS_DOS)
+ toggle_dos_compatibility_flag();
+ else if (LABEL_IS_SUN)
+ toggle_sunflags(get_partition(1, partitions),
+ 0x10);
+ else if (LABEL_IS_SGI)
+ sgi_set_swappartition(
+ get_partition(1, partitions));
+ else
+ unknown_command(c);
+ break;
+ case 'd':
+ {
+ int j;
+ /* If sgi_label then don't use get_existing_partition,
+ let the user select a partition, since
+ get_existing_partition() only works for Linux-like
+ partition tables */
+ if (!LABEL_IS_SGI) {
+ j = get_existing_partition(1, partitions);
+ } else {
+ j = get_partition(1, partitions);
+ }
+ if (j >= 0)
+ delete_partition(j);
+ }
+ break;
+ case 'i':
+ if (LABEL_IS_SGI)
+ create_sgiinfo();
+ else
+ unknown_command(c);
+ case 'l':
+ list_types(get_sys_types());
+ break;
+ case 'm':
+ menu();
+ break;
+ case 'n':
+ new_partition();
+ break;
+ case 'o':
+ create_doslabel();
+ break;
+ case 'p':
+ list_table(0);
+ break;
+ case 'q':
+ close(fd);
+ puts("");
+ return 0;
+ case 's':
+#if ENABLE_FEATURE_SUN_LABEL
+ create_sunlabel();
+#endif
+ break;
+ case 't':
+ change_sysid();
+ break;
+ case 'u':
+ change_units();
+ break;
+ case 'v':
+ verify();
+ break;
+ case 'w':
+ write_table(); /* does not return */
+ break;
+#if ENABLE_FEATURE_FDISK_ADVANCED
+ case 'x':
+ if (LABEL_IS_SGI) {
+ printf("\n\tSorry, no experts menu for SGI "
+ "partition tables available\n\n");
+ } else
+ xselect();
+ break;
+#endif
+ default:
+ unknown_command(c);
+ menu();
+ }
+ }
+ return 0;
+#endif /* FEATURE_FDISK_WRITABLE */
+}
diff --git a/i/pc104/initrd/conf/busybox/util-linux/fdisk_aix.c b/i/pc104/initrd/conf/busybox/util-linux/fdisk_aix.c
new file mode 100644
index 0000000..8095fc4
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/util-linux/fdisk_aix.c
@@ -0,0 +1,73 @@
+#if ENABLE_FEATURE_AIX_LABEL
+/*
+ * Copyright (C) Andreas Neuper, Sep 1998.
+ * This file may be redistributed under
+ * the terms of the GNU Public License.
+ */
+
+typedef struct {
+ unsigned int magic; /* expect AIX_LABEL_MAGIC */
+ unsigned int fillbytes1[124];
+ unsigned int physical_volume_id;
+ unsigned int fillbytes2[124];
+} aix_partition;
+
+#define AIX_LABEL_MAGIC 0xc9c2d4c1
+#define AIX_LABEL_MAGIC_SWAPPED 0xc1d4c2c9
+#define AIX_INFO_MAGIC 0x00072959
+#define AIX_INFO_MAGIC_SWAPPED 0x59290700
+
+#define aixlabel ((aix_partition *)MBRbuffer)
+
+
+/*
+ Changes:
+ * 1999-03-20 Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ * Internationalization
+ *
+ * 2003-03-20 Phillip Kesling <pkesling@sgi.com>
+ * Some fixes
+*/
+
+static int aix_other_endian;
+static short aix_volumes = 1;
+
+/*
+ * only dealing with free blocks here
+ */
+
+static void
+aix_info(void)
+{
+ puts("\n"
+"There is a valid AIX label on this disk.\n"
+"Unfortunately Linux cannot handle these disks at the moment.\n"
+"Nevertheless some advice:\n"
+"1. fdisk will destroy its contents on write.\n"
+"2. Be sure that this disk is NOT a still vital part of a volume group.\n"
+" (Otherwise you may erase the other disks as well, if unmirrored.)\n"
+"3. Before deleting this physical volume be sure to remove the disk\n"
+" logically from your AIX machine. (Otherwise you become an AIXpert).\n"
+ );
+}
+
+static int
+check_aix_label(void)
+{
+ if (aixlabel->magic != AIX_LABEL_MAGIC &&
+ aixlabel->magic != AIX_LABEL_MAGIC_SWAPPED) {
+ current_label_type = 0;
+ aix_other_endian = 0;
+ return 0;
+ }
+ aix_other_endian = (aixlabel->magic == AIX_LABEL_MAGIC_SWAPPED);
+ update_units();
+ current_label_type = label_aix;
+ partitions = 1016;
+ aix_volumes = 15;
+ aix_info();
+ /*aix_nolabel();*/ /* %% */
+ /*aix_label = 1;*/ /* %% */
+ return 1;
+}
+#endif /* AIX_LABEL */
diff --git a/i/pc104/initrd/conf/busybox/util-linux/fdisk_osf.c b/i/pc104/initrd/conf/busybox/util-linux/fdisk_osf.c
new file mode 100644
index 0000000..afb8459
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/util-linux/fdisk_osf.c
@@ -0,0 +1,1063 @@
+#if ENABLE_FEATURE_OSF_LABEL
+/*
+ * Copyright (c) 1987, 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgment:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#ifndef BSD_DISKMAGIC
+#define BSD_DISKMAGIC ((uint32_t) 0x82564557)
+#endif
+
+#ifndef BSD_MAXPARTITIONS
+#define BSD_MAXPARTITIONS 16
+#endif
+
+#define BSD_LINUX_BOOTDIR "/usr/ucb/mdec"
+
+#if defined(i386) || defined(__sparc__) || defined(__arm__) \
+ || defined(__m68k__) || defined(__mips__) || defined(__s390__) \
+ || defined(__sh__) || defined(__x86_64__)
+#define BSD_LABELSECTOR 1
+#define BSD_LABELOFFSET 0
+#elif defined(__alpha__) || defined(__powerpc__) || defined(__ia64__) \
+ || defined(__hppa__)
+#define BSD_LABELSECTOR 0
+#define BSD_LABELOFFSET 64
+#elif defined(__s390__) || defined(__s390x__)
+#define BSD_LABELSECTOR 1
+#define BSD_LABELOFFSET 0
+#else
+#error unknown architecture
+#endif
+
+#define BSD_BBSIZE 8192 /* size of boot area, with label */
+#define BSD_SBSIZE 8192 /* max size of fs superblock */
+
+struct xbsd_disklabel {
+ uint32_t d_magic; /* the magic number */
+ int16_t d_type; /* drive type */
+ int16_t d_subtype; /* controller/d_type specific */
+ char d_typename[16]; /* type name, e.g. "eagle" */
+ char d_packname[16]; /* pack identifier */
+ /* disk geometry: */
+ uint32_t d_secsize; /* # of bytes per sector */
+ uint32_t d_nsectors; /* # of data sectors per track */
+ uint32_t d_ntracks; /* # of tracks per cylinder */
+ uint32_t d_ncylinders; /* # of data cylinders per unit */
+ uint32_t d_secpercyl; /* # of data sectors per cylinder */
+ uint32_t d_secperunit; /* # of data sectors per unit */
+ /*
+ * Spares (bad sector replacements) below
+ * are not counted in d_nsectors or d_secpercyl.
+ * Spare sectors are assumed to be physical sectors
+ * which occupy space at the end of each track and/or cylinder.
+ */
+ uint16_t d_sparespertrack; /* # of spare sectors per track */
+ uint16_t d_sparespercyl; /* # of spare sectors per cylinder */
+ /*
+ * Alternate cylinders include maintenance, replacement,
+ * configuration description areas, etc.
+ */
+ uint32_t d_acylinders; /* # of alt. cylinders per unit */
+
+ /* hardware characteristics: */
+ /*
+ * d_interleave, d_trackskew and d_cylskew describe perturbations
+ * in the media format used to compensate for a slow controller.
+ * Interleave is physical sector interleave, set up by the formatter
+ * or controller when formatting. When interleaving is in use,
+ * logically adjacent sectors are not physically contiguous,
+ * but instead are separated by some number of sectors.
+ * It is specified as the ratio of physical sectors traversed
+ * per logical sector. Thus an interleave of 1:1 implies contiguous
+ * layout, while 2:1 implies that logical sector 0 is separated
+ * by one sector from logical sector 1.
+ * d_trackskew is the offset of sector 0 on track N
+ * relative to sector 0 on track N-1 on the same cylinder.
+ * Finally, d_cylskew is the offset of sector 0 on cylinder N
+ * relative to sector 0 on cylinder N-1.
+ */
+ uint16_t d_rpm; /* rotational speed */
+ uint16_t d_interleave; /* hardware sector interleave */
+ uint16_t d_trackskew; /* sector 0 skew, per track */
+ uint16_t d_cylskew; /* sector 0 skew, per cylinder */
+ uint32_t d_headswitch; /* head switch time, usec */
+ uint32_t d_trkseek; /* track-to-track seek, usec */
+ uint32_t d_flags; /* generic flags */
+#define NDDATA 5
+ uint32_t d_drivedata[NDDATA]; /* drive-type specific information */
+#define NSPARE 5
+ uint32_t d_spare[NSPARE]; /* reserved for future use */
+ uint32_t d_magic2; /* the magic number (again) */
+ uint16_t d_checksum; /* xor of data incl. partitions */
+ /* filesystem and partition information: */
+ uint16_t d_npartitions; /* number of partitions in following */
+ uint32_t d_bbsize; /* size of boot area at sn0, bytes */
+ uint32_t d_sbsize; /* max size of fs superblock, bytes */
+ struct xbsd_partition { /* the partition table */
+ uint32_t p_size; /* number of sectors in partition */
+ uint32_t p_offset; /* starting sector */
+ uint32_t p_fsize; /* filesystem basic fragment size */
+ uint8_t p_fstype; /* filesystem type, see below */
+ uint8_t p_frag; /* filesystem fragments per block */
+ uint16_t p_cpg; /* filesystem cylinders per group */
+ } d_partitions[BSD_MAXPARTITIONS]; /* actually may be more */
+};
+
+/* d_type values: */
+#define BSD_DTYPE_SMD 1 /* SMD, XSMD; VAX hp/up */
+#define BSD_DTYPE_MSCP 2 /* MSCP */
+#define BSD_DTYPE_DEC 3 /* other DEC (rk, rl) */
+#define BSD_DTYPE_SCSI 4 /* SCSI */
+#define BSD_DTYPE_ESDI 5 /* ESDI interface */
+#define BSD_DTYPE_ST506 6 /* ST506 etc. */
+#define BSD_DTYPE_HPIB 7 /* CS/80 on HP-IB */
+#define BSD_DTYPE_HPFL 8 /* HP Fiber-link */
+#define BSD_DTYPE_FLOPPY 10 /* floppy */
+
+/* d_subtype values: */
+#define BSD_DSTYPE_INDOSPART 0x8 /* is inside dos partition */
+#define BSD_DSTYPE_DOSPART(s) ((s) & 3) /* dos partition number */
+#define BSD_DSTYPE_GEOMETRY 0x10 /* drive params in label */
+
+static const char * const xbsd_dktypenames[] = {
+ "unknown",
+ "SMD",
+ "MSCP",
+ "old DEC",
+ "SCSI",
+ "ESDI",
+ "ST506",
+ "HP-IB",
+ "HP-FL",
+ "type 9",
+ "floppy",
+ 0
+};
+#define BSD_DKMAXTYPES (sizeof(xbsd_dktypenames) / sizeof(xbsd_dktypenames[0]) - 1)
+
+/*
+ * Filesystem type and version.
+ * Used to interpret other filesystem-specific
+ * per-partition information.
+ */
+#define BSD_FS_UNUSED 0 /* unused */
+#define BSD_FS_SWAP 1 /* swap */
+#define BSD_FS_V6 2 /* Sixth Edition */
+#define BSD_FS_V7 3 /* Seventh Edition */
+#define BSD_FS_SYSV 4 /* System V */
+#define BSD_FS_V71K 5 /* V7 with 1K blocks (4.1, 2.9) */
+#define BSD_FS_V8 6 /* Eighth Edition, 4K blocks */
+#define BSD_FS_BSDFFS 7 /* 4.2BSD fast file system */
+#define BSD_FS_BSDLFS 9 /* 4.4BSD log-structured file system */
+#define BSD_FS_OTHER 10 /* in use, but unknown/unsupported */
+#define BSD_FS_HPFS 11 /* OS/2 high-performance file system */
+#define BSD_FS_ISO9660 12 /* ISO-9660 filesystem (cdrom) */
+#define BSD_FS_ISOFS BSD_FS_ISO9660
+#define BSD_FS_BOOT 13 /* partition contains bootstrap */
+#define BSD_FS_ADOS 14 /* AmigaDOS fast file system */
+#define BSD_FS_HFS 15 /* Macintosh HFS */
+#define BSD_FS_ADVFS 16 /* Digital Unix AdvFS */
+
+/* this is annoying, but it's also the way it is :-( */
+#ifdef __alpha__
+#define BSD_FS_EXT2 8 /* ext2 file system */
+#else
+#define BSD_FS_MSDOS 8 /* MS-DOS file system */
+#endif
+
+static const char *const xbsd_fstypes[] = {
+ "\x00" "unused", /* BSD_FS_UNUSED */
+ "\x01" "swap", /* BSD_FS_SWAP */
+ "\x02" "Version 6", /* BSD_FS_V6 */
+ "\x03" "Version 7", /* BSD_FS_V7 */
+ "\x04" "System V", /* BSD_FS_SYSV */
+ "\x05" "4.1BSD", /* BSD_FS_V71K */
+ "\x06" "Eighth Edition", /* BSD_FS_V8 */
+ "\x07" "4.2BSD", /* BSD_FS_BSDFFS */
+#ifdef __alpha__
+ "\x08" "ext2", /* BSD_FS_EXT2 */
+#else
+ "\x08" "MS-DOS", /* BSD_FS_MSDOS */
+#endif
+ "\x09" "4.4LFS", /* BSD_FS_BSDLFS */
+ "\x0a" "unknown", /* BSD_FS_OTHER */
+ "\x0b" "HPFS", /* BSD_FS_HPFS */
+ "\x0c" "ISO-9660", /* BSD_FS_ISO9660 */
+ "\x0d" "boot", /* BSD_FS_BOOT */
+ "\x0e" "ADOS", /* BSD_FS_ADOS */
+ "\x0f" "HFS", /* BSD_FS_HFS */
+ "\x10" "AdvFS", /* BSD_FS_ADVFS */
+ NULL
+};
+#define BSD_FSMAXTYPES (SIZE(xbsd_fstypes)-1)
+
+
+/*
+ * flags shared by various drives:
+ */
+#define BSD_D_REMOVABLE 0x01 /* removable media */
+#define BSD_D_ECC 0x02 /* supports ECC */
+#define BSD_D_BADSECT 0x04 /* supports bad sector forw. */
+#define BSD_D_RAMDISK 0x08 /* disk emulator */
+#define BSD_D_CHAIN 0x10 /* can do back-back transfers */
+#define BSD_D_DOSPART 0x20 /* within MSDOS partition */
+
+/*
+ Changes:
+ 19990319 - Arnaldo Carvalho de Melo <acme@conectiva.com.br> - i18n/nls
+
+ 20000101 - David Huggins-Daines <dhuggins@linuxcare.com> - Better
+ support for OSF/1 disklabels on Alpha.
+ Also fixed unaligned accesses in alpha_bootblock_checksum()
+*/
+
+static int possibly_osf_label;
+
+#define FREEBSD_PARTITION 0xa5
+#define NETBSD_PARTITION 0xa9
+
+static void xbsd_delete_part(void);
+static void xbsd_new_part(void);
+static void xbsd_write_disklabel(void);
+static int xbsd_create_disklabel(void);
+static void xbsd_edit_disklabel(void);
+static void xbsd_write_bootstrap(void);
+static void xbsd_change_fstype(void);
+static int xbsd_get_part_index(int max);
+static int xbsd_check_new_partition(int *i);
+static void xbsd_list_types(void);
+static uint16_t xbsd_dkcksum(struct xbsd_disklabel *lp);
+static int xbsd_initlabel(struct partition *p);
+static int xbsd_readlabel(struct partition *p);
+static int xbsd_writelabel(struct partition *p);
+
+#if defined(__alpha__)
+static void alpha_bootblock_checksum(char *boot);
+#endif
+
+#if !defined(__alpha__)
+static int xbsd_translate_fstype(int linux_type);
+static void xbsd_link_part(void);
+static struct partition *xbsd_part;
+static int xbsd_part_index;
+#endif
+
+
+/* Group big globals data and allocate it in one go */
+struct bsd_globals {
+/* We access this through a uint64_t * when checksumming */
+/* hopefully xmalloc gives us required alignment */
+ char disklabelbuffer[BSD_BBSIZE];
+ struct xbsd_disklabel xbsd_dlabel;
+};
+
+static struct bsd_globals *bsd_globals_ptr;
+
+#define disklabelbuffer (bsd_globals_ptr->disklabelbuffer)
+#define xbsd_dlabel (bsd_globals_ptr->xbsd_dlabel)
+
+
+/* Code */
+
+#define bsd_cround(n) \
+ (display_in_cyl_units ? ((n)/xbsd_dlabel.d_secpercyl) + 1 : (n))
+
+/*
+ * Test whether the whole disk has BSD disk label magic.
+ *
+ * Note: often reformatting with DOS-type label leaves the BSD magic,
+ * so this does not mean that there is a BSD disk label.
+ */
+static int
+check_osf_label(void)
+{
+ if (xbsd_readlabel(NULL) == 0)
+ return 0;
+ return 1;
+}
+
+static int
+bsd_trydev(const char * dev)
+{
+ if (xbsd_readlabel(NULL) == 0)
+ return -1;
+ printf("\nBSD label for device: %s\n", dev);
+ xbsd_print_disklabel(0);
+ return 0;
+}
+
+static void
+bsd_menu(void)
+{
+ puts("Command Action");
+ puts("d\tdelete a BSD partition");
+ puts("e\tedit drive data");
+ puts("i\tinstall bootstrap");
+ puts("l\tlist known filesystem types");
+ puts("n\tadd a new BSD partition");
+ puts("p\tprint BSD partition table");
+ puts("q\tquit without saving changes");
+ puts("r\treturn to main menu");
+ puts("s\tshow complete disklabel");
+ puts("t\tchange a partition's filesystem id");
+ puts("u\tchange units (cylinders/sectors)");
+ puts("w\twrite disklabel to disk");
+#if !defined(__alpha__)
+ puts("x\tlink BSD partition to non-BSD partition");
+#endif
+}
+
+#if !defined(__alpha__)
+static int
+hidden(int type)
+{
+ return type ^ 0x10;
+}
+
+static int
+is_bsd_partition_type(int type)
+{
+ return (type == FREEBSD_PARTITION ||
+ type == hidden(FREEBSD_PARTITION) ||
+ type == NETBSD_PARTITION ||
+ type == hidden(NETBSD_PARTITION));
+}
+#endif
+
+static void
+bsd_select(void)
+{
+#if !defined(__alpha__)
+ int t, ss;
+ struct partition *p;
+
+ for (t = 0; t < 4; t++) {
+ p = get_part_table(t);
+ if (p && is_bsd_partition_type(p->sys_ind)) {
+ xbsd_part = p;
+ xbsd_part_index = t;
+ ss = get_start_sect(xbsd_part);
+ if (ss == 0) {
+ printf("Partition %s has invalid starting sector 0\n",
+ partname(disk_device, t+1, 0));
+ return;
+ }
+ printf("Reading disklabel of %s at sector %d\n",
+ partname(disk_device, t+1, 0), ss + BSD_LABELSECTOR);
+ if (xbsd_readlabel(xbsd_part) == 0)
+ if (xbsd_create_disklabel() == 0)
+ return;
+ break;
+ }
+ }
+
+ if (t == 4) {
+ printf("There is no *BSD partition on %s\n", disk_device);
+ return;
+ }
+
+#elif defined(__alpha__)
+
+ if (xbsd_readlabel(NULL) == 0)
+ if (xbsd_create_disklabel() == 0)
+ exit(EXIT_SUCCESS);
+
+#endif
+
+ while (1) {
+ putchar('\n');
+ switch (tolower(read_nonempty("BSD disklabel command (m for help): "))) {
+ case 'd':
+ xbsd_delete_part();
+ break;
+ case 'e':
+ xbsd_edit_disklabel();
+ break;
+ case 'i':
+ xbsd_write_bootstrap();
+ break;
+ case 'l':
+ xbsd_list_types();
+ break;
+ case 'n':
+ xbsd_new_part();
+ break;
+ case 'p':
+ xbsd_print_disklabel(0);
+ break;
+ case 'q':
+ close(fd);
+ exit(EXIT_SUCCESS);
+ case 'r':
+ return;
+ case 's':
+ xbsd_print_disklabel(1);
+ break;
+ case 't':
+ xbsd_change_fstype();
+ break;
+ case 'u':
+ change_units();
+ break;
+ case 'w':
+ xbsd_write_disklabel();
+ break;
+#if !defined(__alpha__)
+ case 'x':
+ xbsd_link_part();
+ break;
+#endif
+ default:
+ bsd_menu();
+ break;
+ }
+ }
+}
+
+static void
+xbsd_delete_part(void)
+{
+ int i;
+
+ i = xbsd_get_part_index(xbsd_dlabel.d_npartitions);
+ xbsd_dlabel.d_partitions[i].p_size = 0;
+ xbsd_dlabel.d_partitions[i].p_offset = 0;
+ xbsd_dlabel.d_partitions[i].p_fstype = BSD_FS_UNUSED;
+ if (xbsd_dlabel.d_npartitions == i + 1)
+ while (xbsd_dlabel.d_partitions[xbsd_dlabel.d_npartitions-1].p_size == 0)
+ xbsd_dlabel.d_npartitions--;
+}
+
+static void
+xbsd_new_part(void)
+{
+ off_t begin, end;
+ char mesg[256];
+ int i;
+
+ if (!xbsd_check_new_partition(&i))
+ return;
+
+#if !defined(__alpha__) && !defined(__powerpc__) && !defined(__hppa__)
+ begin = get_start_sect(xbsd_part);
+ end = begin + get_nr_sects(xbsd_part) - 1;
+#else
+ begin = 0;
+ end = xbsd_dlabel.d_secperunit - 1;
+#endif
+
+ snprintf(mesg, sizeof(mesg), "First %s", str_units(SINGULAR));
+ begin = read_int(bsd_cround(begin), bsd_cround(begin), bsd_cround(end),
+ 0, mesg);
+
+ if (display_in_cyl_units)
+ begin = (begin - 1) * xbsd_dlabel.d_secpercyl;
+
+ snprintf(mesg, sizeof(mesg), "Last %s or +size or +sizeM or +sizeK",
+ str_units(SINGULAR));
+ end = read_int(bsd_cround(begin), bsd_cround(end), bsd_cround(end),
+ bsd_cround(begin), mesg);
+
+ if (display_in_cyl_units)
+ end = end * xbsd_dlabel.d_secpercyl - 1;
+
+ xbsd_dlabel.d_partitions[i].p_size = end - begin + 1;
+ xbsd_dlabel.d_partitions[i].p_offset = begin;
+ xbsd_dlabel.d_partitions[i].p_fstype = BSD_FS_UNUSED;
+}
+
+static void
+xbsd_print_disklabel(int show_all)
+{
+ struct xbsd_disklabel *lp = &xbsd_dlabel;
+ struct xbsd_partition *pp;
+ int i, j;
+
+ if (show_all) {
+#if defined(__alpha__)
+ printf("# %s:\n", disk_device);
+#else
+ printf("# %s:\n", partname(disk_device, xbsd_part_index+1, 0));
+#endif
+ if ((unsigned) lp->d_type < BSD_DKMAXTYPES)
+ printf("type: %s\n", xbsd_dktypenames[lp->d_type]);
+ else
+ printf("type: %d\n", lp->d_type);
+ printf("disk: %.*s\n", (int) sizeof(lp->d_typename), lp->d_typename);
+ printf("label: %.*s\n", (int) sizeof(lp->d_packname), lp->d_packname);
+ printf("flags:");
+ if (lp->d_flags & BSD_D_REMOVABLE)
+ printf(" removable");
+ if (lp->d_flags & BSD_D_ECC)
+ printf(" ecc");
+ if (lp->d_flags & BSD_D_BADSECT)
+ printf(" badsect");
+ puts("");
+ /* On various machines the fields of *lp are short/int/long */
+ /* In order to avoid problems, we cast them all to long. */
+ printf("bytes/sector: %ld\n", (long) lp->d_secsize);
+ printf("sectors/track: %ld\n", (long) lp->d_nsectors);
+ printf("tracks/cylinder: %ld\n", (long) lp->d_ntracks);
+ printf("sectors/cylinder: %ld\n", (long) lp->d_secpercyl);
+ printf("cylinders: %ld\n", (long) lp->d_ncylinders);
+ printf("rpm: %d\n", lp->d_rpm);
+ printf("interleave: %d\n", lp->d_interleave);
+ printf("trackskew: %d\n", lp->d_trackskew);
+ printf("cylinderskew: %d\n", lp->d_cylskew);
+ printf("headswitch: %ld\t\t# milliseconds\n",
+ (long) lp->d_headswitch);
+ printf("track-to-track seek: %ld\t# milliseconds\n",
+ (long) lp->d_trkseek);
+ printf("drivedata: ");
+ for (i = NDDATA - 1; i >= 0; i--)
+ if (lp->d_drivedata[i])
+ break;
+ if (i < 0)
+ i = 0;
+ for (j = 0; j <= i; j++)
+ printf("%ld ", (long) lp->d_drivedata[j]);
+ }
+ printf("\n%d partitions:\n", lp->d_npartitions);
+ printf("# start end size fstype [fsize bsize cpg]\n");
+ pp = lp->d_partitions;
+ for (i = 0; i < lp->d_npartitions; i++, pp++) {
+ if (pp->p_size) {
+ if (display_in_cyl_units && lp->d_secpercyl) {
+ printf(" %c: %8ld%c %8ld%c %8ld%c ",
+ 'a' + i,
+ (long) pp->p_offset / lp->d_secpercyl + 1,
+ (pp->p_offset % lp->d_secpercyl) ? '*' : ' ',
+ (long) (pp->p_offset + pp->p_size + lp->d_secpercyl - 1) / lp->d_secpercyl,
+ ((pp->p_offset + pp->p_size) % lp->d_secpercyl) ? '*' : ' ',
+ (long) pp->p_size / lp->d_secpercyl,
+ (pp->p_size % lp->d_secpercyl) ? '*' : ' '
+ );
+ } else {
+ printf(" %c: %8ld %8ld %8ld ",
+ 'a' + i,
+ (long) pp->p_offset,
+ (long) pp->p_offset + pp->p_size - 1,
+ (long) pp->p_size
+ );
+ }
+
+ if ((unsigned) pp->p_fstype < BSD_FSMAXTYPES)
+ printf("%8.8s", xbsd_fstypes[pp->p_fstype]);
+ else
+ printf("%8x", pp->p_fstype);
+
+ switch (pp->p_fstype) {
+ case BSD_FS_UNUSED:
+ printf(" %5ld %5ld %5.5s ",
+ (long) pp->p_fsize, (long) pp->p_fsize * pp->p_frag, "");
+ break;
+ case BSD_FS_BSDFFS:
+ printf(" %5ld %5ld %5d ",
+ (long) pp->p_fsize, (long) pp->p_fsize * pp->p_frag, pp->p_cpg);
+ break;
+ default:
+ printf("%22.22s", "");
+ break;
+ }
+ puts("");
+ }
+ }
+}
+
+static void
+xbsd_write_disklabel(void)
+{
+#if defined(__alpha__)
+ printf("Writing disklabel to %s\n", disk_device);
+ xbsd_writelabel(NULL);
+#else
+ printf("Writing disklabel to %s\n",
+ partname(disk_device, xbsd_part_index + 1, 0));
+ xbsd_writelabel(xbsd_part);
+#endif
+ reread_partition_table(0); /* no exit yet */
+}
+
+static int
+xbsd_create_disklabel(void)
+{
+ char c;
+
+#if defined(__alpha__)
+ printf("%s contains no disklabel\n", disk_device);
+#else
+ printf("%s contains no disklabel\n",
+ partname(disk_device, xbsd_part_index + 1, 0));
+#endif
+
+ while (1) {
+ c = read_nonempty("Do you want to create a disklabel? (y/n) ");
+ if (c == 'y' || c == 'Y') {
+ if (xbsd_initlabel(
+#if defined(__alpha__) || defined(__powerpc__) || defined(__hppa__) || \
+ defined(__s390__) || defined(__s390x__)
+ NULL
+#else
+ xbsd_part
+#endif
+ ) == 1) {
+ xbsd_print_disklabel(1);
+ return 1;
+ } else
+ return 0;
+ } else if (c == 'n')
+ return 0;
+ }
+}
+
+static int
+edit_int(int def, const char *mesg)
+{
+ mesg = xasprintf("%s (%d): ", mesg, def);
+ do {
+ if (!read_line(mesg))
+ goto ret;
+ } while (!isdigit(*line_ptr));
+ def = atoi(line_ptr);
+ ret:
+ free((char*)mesg);
+ return def;
+}
+
+static void
+xbsd_edit_disklabel(void)
+{
+ struct xbsd_disklabel *d;
+
+ d = &xbsd_dlabel;
+
+#if defined(__alpha__) || defined(__ia64__)
+ d->d_secsize = edit_int(d->d_secsize , "bytes/sector");
+ d->d_nsectors = edit_int(d->d_nsectors , "sectors/track");
+ d->d_ntracks = edit_int(d->d_ntracks , "tracks/cylinder");
+ d->d_ncylinders = edit_int(d->d_ncylinders , "cylinders");
+#endif
+
+ /* d->d_secpercyl can be != d->d_nsectors * d->d_ntracks */
+ while (1) {
+ d->d_secpercyl = edit_int(d->d_nsectors * d->d_ntracks,
+ "sectors/cylinder");
+ if (d->d_secpercyl <= d->d_nsectors * d->d_ntracks)
+ break;
+
+ printf("Must be <= sectors/track * tracks/cylinder (default)\n");
+ }
+ d->d_rpm = edit_int(d->d_rpm , "rpm");
+ d->d_interleave = edit_int(d->d_interleave, "interleave");
+ d->d_trackskew = edit_int(d->d_trackskew , "trackskew");
+ d->d_cylskew = edit_int(d->d_cylskew , "cylinderskew");
+ d->d_headswitch = edit_int(d->d_headswitch, "headswitch");
+ d->d_trkseek = edit_int(d->d_trkseek , "track-to-track seek");
+
+ d->d_secperunit = d->d_secpercyl * d->d_ncylinders;
+}
+
+static int
+xbsd_get_bootstrap(char *path, void *ptr, int size)
+{
+ int fdb;
+
+ fdb = open(path, O_RDONLY);
+ if (fdb < 0) {
+ perror(path);
+ return 0;
+ }
+ if (read(fdb, ptr, size) < 0) {
+ perror(path);
+ close(fdb);
+ return 0;
+ }
+ printf(" ... %s\n", path);
+ close(fdb);
+ return 1;
+}
+
+static void
+sync_disks(void)
+{
+ printf("Syncing disks\n");
+ sync();
+ /* sleep(4); What? */
+}
+
+static void
+xbsd_write_bootstrap(void)
+{
+ char path[MAXPATHLEN];
+ const char *bootdir = BSD_LINUX_BOOTDIR;
+ const char *dkbasename;
+ struct xbsd_disklabel dl;
+ char *d, *p, *e;
+ int sector;
+
+ if (xbsd_dlabel.d_type == BSD_DTYPE_SCSI)
+ dkbasename = "sd";
+ else
+ dkbasename = "wd";
+
+ snprintf(path, sizeof(path), "Bootstrap: %sboot -> boot%s (%s): ",
+ dkbasename, dkbasename, dkbasename);
+ if (read_line(path)) {
+ dkbasename = line_ptr;
+ }
+ snprintf(path, sizeof(path), "%s/%sboot", bootdir, dkbasename);
+ if (!xbsd_get_bootstrap(path, disklabelbuffer, (int) xbsd_dlabel.d_secsize))
+ return;
+
+/* We need a backup of the disklabel (xbsd_dlabel might have changed). */
+ d = &disklabelbuffer[BSD_LABELSECTOR * SECTOR_SIZE];
+ memmove(&dl, d, sizeof(struct xbsd_disklabel));
+
+/* The disklabel will be overwritten by 0's from bootxx anyway */
+ memset(d, 0, sizeof(struct xbsd_disklabel));
+
+ snprintf(path, sizeof(path), "%s/boot%s", bootdir, dkbasename);
+ if (!xbsd_get_bootstrap(path, &disklabelbuffer[xbsd_dlabel.d_secsize],
+ (int) xbsd_dlabel.d_bbsize - xbsd_dlabel.d_secsize))
+ return;
+
+ e = d + sizeof(struct xbsd_disklabel);
+ for (p = d; p < e; p++)
+ if (*p) {
+ printf("Bootstrap overlaps with disk label!\n");
+ exit(EXIT_FAILURE);
+ }
+
+ memmove(d, &dl, sizeof(struct xbsd_disklabel));
+
+#if defined(__powerpc__) || defined(__hppa__)
+ sector = 0;
+#elif defined(__alpha__)
+ sector = 0;
+ alpha_bootblock_checksum(disklabelbuffer);
+#else
+ sector = get_start_sect(xbsd_part);
+#endif
+
+ if (lseek(fd, sector * SECTOR_SIZE, SEEK_SET) == -1)
+ fdisk_fatal(unable_to_seek);
+ if (BSD_BBSIZE != write(fd, disklabelbuffer, BSD_BBSIZE))
+ fdisk_fatal(unable_to_write);
+
+#if defined(__alpha__)
+ printf("Bootstrap installed on %s\n", disk_device);
+#else
+ printf("Bootstrap installed on %s\n",
+ partname(disk_device, xbsd_part_index+1, 0));
+#endif
+
+ sync_disks();
+}
+
+static void
+xbsd_change_fstype(void)
+{
+ int i;
+
+ i = xbsd_get_part_index(xbsd_dlabel.d_npartitions);
+ xbsd_dlabel.d_partitions[i].p_fstype = read_hex(xbsd_fstypes);
+}
+
+static int
+xbsd_get_part_index(int max)
+{
+ char prompt[sizeof("Partition (a-%c): ") + 16];
+ char l;
+
+ snprintf(prompt, sizeof(prompt), "Partition (a-%c): ", 'a' + max - 1);
+ do
+ l = tolower(read_nonempty(prompt));
+ while (l < 'a' || l > 'a' + max - 1);
+ return l - 'a';
+}
+
+static int
+xbsd_check_new_partition(int *i)
+{
+ /* room for more? various BSD flavours have different maxima */
+ if (xbsd_dlabel.d_npartitions == BSD_MAXPARTITIONS) {
+ int t;
+
+ for (t = 0; t < BSD_MAXPARTITIONS; t++)
+ if (xbsd_dlabel.d_partitions[t].p_size == 0)
+ break;
+
+ if (t == BSD_MAXPARTITIONS) {
+ printf("The maximum number of partitions has been created\n");
+ return 0;
+ }
+ }
+
+ *i = xbsd_get_part_index(BSD_MAXPARTITIONS);
+
+ if (*i >= xbsd_dlabel.d_npartitions)
+ xbsd_dlabel.d_npartitions = (*i) + 1;
+
+ if (xbsd_dlabel.d_partitions[*i].p_size != 0) {
+ printf("This partition already exists\n");
+ return 0;
+ }
+
+ return 1;
+}
+
+static void
+xbsd_list_types(void)
+{
+ list_types(xbsd_fstypes);
+}
+
+static uint16_t
+xbsd_dkcksum(struct xbsd_disklabel *lp)
+{
+ uint16_t *start, *end;
+ uint16_t sum = 0;
+
+ start = (uint16_t *) lp;
+ end = (uint16_t *) &lp->d_partitions[lp->d_npartitions];
+ while (start < end)
+ sum ^= *start++;
+ return sum;
+}
+
+static int
+xbsd_initlabel(struct partition *p)
+{
+ struct xbsd_disklabel *d = &xbsd_dlabel;
+ struct xbsd_partition *pp;
+
+ get_geometry();
+ memset(d, 0, sizeof(struct xbsd_disklabel));
+
+ d->d_magic = BSD_DISKMAGIC;
+
+ if (strncmp(disk_device, "/dev/sd", 7) == 0)
+ d->d_type = BSD_DTYPE_SCSI;
+ else
+ d->d_type = BSD_DTYPE_ST506;
+
+#if !defined(__alpha__)
+ d->d_flags = BSD_D_DOSPART;
+#else
+ d->d_flags = 0;
+#endif
+ d->d_secsize = SECTOR_SIZE; /* bytes/sector */
+ d->d_nsectors = sectors; /* sectors/track */
+ d->d_ntracks = heads; /* tracks/cylinder (heads) */
+ d->d_ncylinders = cylinders;
+ d->d_secpercyl = sectors * heads; /* sectors/cylinder */
+ if (d->d_secpercyl == 0)
+ d->d_secpercyl = 1; /* avoid segfaults */
+ d->d_secperunit = d->d_secpercyl * d->d_ncylinders;
+
+ d->d_rpm = 3600;
+ d->d_interleave = 1;
+ d->d_trackskew = 0;
+ d->d_cylskew = 0;
+ d->d_headswitch = 0;
+ d->d_trkseek = 0;
+
+ d->d_magic2 = BSD_DISKMAGIC;
+ d->d_bbsize = BSD_BBSIZE;
+ d->d_sbsize = BSD_SBSIZE;
+
+#if !defined(__alpha__)
+ d->d_npartitions = 4;
+ pp = &d->d_partitions[2]; /* Partition C should be NetBSD partition */
+
+ pp->p_offset = get_start_sect(p);
+ pp->p_size = get_nr_sects(p);
+ pp->p_fstype = BSD_FS_UNUSED;
+ pp = &d->d_partitions[3]; /* Partition D should be whole disk */
+
+ pp->p_offset = 0;
+ pp->p_size = d->d_secperunit;
+ pp->p_fstype = BSD_FS_UNUSED;
+#else
+ d->d_npartitions = 3;
+ pp = &d->d_partitions[2]; /* Partition C should be
+ the whole disk */
+ pp->p_offset = 0;
+ pp->p_size = d->d_secperunit;
+ pp->p_fstype = BSD_FS_UNUSED;
+#endif
+
+ return 1;
+}
+
+/*
+ * Read a xbsd_disklabel from sector 0 or from the starting sector of p.
+ * If it has the right magic, return 1.
+ */
+static int
+xbsd_readlabel(struct partition *p)
+{
+ struct xbsd_disklabel *d;
+ int t, sector;
+
+ if (!bsd_globals_ptr)
+ bsd_globals_ptr = xzalloc(sizeof(*bsd_globals_ptr));
+
+ d = &xbsd_dlabel;
+
+ /* p is used only to get the starting sector */
+#if !defined(__alpha__)
+ sector = (p ? get_start_sect(p) : 0);
+#else
+ sector = 0;
+#endif
+
+ if (lseek(fd, sector * SECTOR_SIZE, SEEK_SET) == -1)
+ fdisk_fatal(unable_to_seek);
+ if (BSD_BBSIZE != read(fd, disklabelbuffer, BSD_BBSIZE))
+ fdisk_fatal(unable_to_read);
+
+ memmove(d, &disklabelbuffer[BSD_LABELSECTOR * SECTOR_SIZE + BSD_LABELOFFSET],
+ sizeof(struct xbsd_disklabel));
+
+ if (d->d_magic != BSD_DISKMAGIC || d->d_magic2 != BSD_DISKMAGIC)
+ return 0;
+
+ for (t = d->d_npartitions; t < BSD_MAXPARTITIONS; t++) {
+ d->d_partitions[t].p_size = 0;
+ d->d_partitions[t].p_offset = 0;
+ d->d_partitions[t].p_fstype = BSD_FS_UNUSED;
+ }
+
+ if (d->d_npartitions > BSD_MAXPARTITIONS)
+ printf("Warning: too many partitions (%d, maximum is %d)\n",
+ d->d_npartitions, BSD_MAXPARTITIONS);
+ return 1;
+}
+
+static int
+xbsd_writelabel(struct partition *p)
+{
+ struct xbsd_disklabel *d = &xbsd_dlabel;
+ unsigned int sector;
+
+#if !defined(__alpha__) && !defined(__powerpc__) && !defined(__hppa__)
+ sector = get_start_sect(p) + BSD_LABELSECTOR;
+#else
+ sector = BSD_LABELSECTOR;
+#endif
+
+ d->d_checksum = 0;
+ d->d_checksum = xbsd_dkcksum(d);
+
+ /* This is necessary if we want to write the bootstrap later,
+ otherwise we'd write the old disklabel with the bootstrap.
+ */
+ memmove(&disklabelbuffer[BSD_LABELSECTOR * SECTOR_SIZE + BSD_LABELOFFSET],
+ d, sizeof(struct xbsd_disklabel));
+
+#if defined(__alpha__) && BSD_LABELSECTOR == 0
+ alpha_bootblock_checksum(disklabelbuffer);
+ if (lseek(fd, 0, SEEK_SET) == -1)
+ fdisk_fatal(unable_to_seek);
+ if (BSD_BBSIZE != write(fd, disklabelbuffer, BSD_BBSIZE))
+ fdisk_fatal(unable_to_write);
+#else
+ if (lseek(fd, sector * SECTOR_SIZE + BSD_LABELOFFSET, SEEK_SET) == -1)
+ fdisk_fatal(unable_to_seek);
+ if (sizeof(struct xbsd_disklabel) != write(fd, d, sizeof(struct xbsd_disklabel)))
+ fdisk_fatal(unable_to_write);
+#endif
+ sync_disks();
+ return 1;
+}
+
+
+#if !defined(__alpha__)
+static int
+xbsd_translate_fstype(int linux_type)
+{
+ switch (linux_type) {
+ case 0x01: /* DOS 12-bit FAT */
+ case 0x04: /* DOS 16-bit <32M */
+ case 0x06: /* DOS 16-bit >=32M */
+ case 0xe1: /* DOS access */
+ case 0xe3: /* DOS R/O */
+ case 0xf2: /* DOS secondary */
+ return BSD_FS_MSDOS;
+ case 0x07: /* OS/2 HPFS */
+ return BSD_FS_HPFS;
+ default:
+ return BSD_FS_OTHER;
+ }
+}
+
+static void
+xbsd_link_part(void)
+{
+ int k, i;
+ struct partition *p;
+
+ k = get_partition(1, partitions);
+
+ if (!xbsd_check_new_partition(&i))
+ return;
+
+ p = get_part_table(k);
+
+ xbsd_dlabel.d_partitions[i].p_size = get_nr_sects(p);
+ xbsd_dlabel.d_partitions[i].p_offset = get_start_sect(p);
+ xbsd_dlabel.d_partitions[i].p_fstype = xbsd_translate_fstype(p->sys_ind);
+}
+#endif
+
+#if defined(__alpha__)
+static void
+alpha_bootblock_checksum(char *boot)
+{
+ uint64_t *dp, sum;
+ int i;
+
+ dp = (uint64_t *)boot;
+ sum = 0;
+ for (i = 0; i < 63; i++)
+ sum += dp[i];
+ dp[63] = sum;
+}
+#endif /* __alpha__ */
+
+/* Undefine 'global' tricks */
+#undef disklabelbuffer
+#undef xbsd_dlabel
+
+#endif /* OSF_LABEL */
diff --git a/i/pc104/initrd/conf/busybox/util-linux/fdisk_sgi.c b/i/pc104/initrd/conf/busybox/util-linux/fdisk_sgi.c
new file mode 100644
index 0000000..b2e0d7a
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/util-linux/fdisk_sgi.c
@@ -0,0 +1,891 @@
+#if ENABLE_FEATURE_SGI_LABEL
+
+/*
+ * Copyright (C) Andreas Neuper, Sep 1998.
+ * This file may be modified and redistributed under
+ * the terms of the GNU Public License.
+ */
+
+#define SGI_VOLHDR 0x00
+/* 1 and 2 were used for drive types no longer supported by SGI */
+#define SGI_SWAP 0x03
+/* 4 and 5 were for filesystem types SGI haven't ever supported on MIPS CPUs */
+#define SGI_VOLUME 0x06
+#define SGI_EFS 0x07
+#define SGI_LVOL 0x08
+#define SGI_RLVOL 0x09
+#define SGI_XFS 0x0a
+#define SGI_XFSLOG 0x0b
+#define SGI_XLV 0x0c
+#define SGI_XVM 0x0d
+#define SGI_ENTIRE_DISK SGI_VOLUME
+
+struct device_parameter { /* 48 bytes */
+ unsigned char skew;
+ unsigned char gap1;
+ unsigned char gap2;
+ unsigned char sparecyl;
+ unsigned short pcylcount;
+ unsigned short head_vol0;
+ unsigned short ntrks; /* tracks in cyl 0 or vol 0 */
+ unsigned char cmd_tag_queue_depth;
+ unsigned char unused0;
+ unsigned short unused1;
+ unsigned short nsect; /* sectors/tracks in cyl 0 or vol 0 */
+ unsigned short bytes;
+ unsigned short ilfact;
+ unsigned int flags; /* controller flags */
+ unsigned int datarate;
+ unsigned int retries_on_error;
+ unsigned int ms_per_word;
+ unsigned short xylogics_gap1;
+ unsigned short xylogics_syncdelay;
+ unsigned short xylogics_readdelay;
+ unsigned short xylogics_gap2;
+ unsigned short xylogics_readgate;
+ unsigned short xylogics_writecont;
+};
+
+/*
+ * controller flags
+ */
+#define SECTOR_SLIP 0x01
+#define SECTOR_FWD 0x02
+#define TRACK_FWD 0x04
+#define TRACK_MULTIVOL 0x08
+#define IGNORE_ERRORS 0x10
+#define RESEEK 0x20
+#define ENABLE_CMDTAGQ 0x40
+
+typedef struct {
+ unsigned int magic; /* expect SGI_LABEL_MAGIC */
+ unsigned short boot_part; /* active boot partition */
+ unsigned short swap_part; /* active swap partition */
+ unsigned char boot_file[16]; /* name of the bootfile */
+ struct device_parameter devparam; /* 1 * 48 bytes */
+ struct volume_directory { /* 15 * 16 bytes */
+ unsigned char vol_file_name[8]; /* a character array */
+ unsigned int vol_file_start; /* number of logical block */
+ unsigned int vol_file_size; /* number of bytes */
+ } directory[15];
+ struct sgi_partinfo { /* 16 * 12 bytes */
+ unsigned int num_sectors; /* number of blocks */
+ unsigned int start_sector; /* must be cylinder aligned */
+ unsigned int id;
+ } partitions[16];
+ unsigned int csum;
+ unsigned int fillbytes;
+} sgi_partition;
+
+typedef struct {
+ unsigned int magic; /* looks like a magic number */
+ unsigned int a2;
+ unsigned int a3;
+ unsigned int a4;
+ unsigned int b1;
+ unsigned short b2;
+ unsigned short b3;
+ unsigned int c[16];
+ unsigned short d[3];
+ unsigned char scsi_string[50];
+ unsigned char serial[137];
+ unsigned short check1816;
+ unsigned char installer[225];
+} sgiinfo;
+
+#define SGI_LABEL_MAGIC 0x0be5a941
+#define SGI_LABEL_MAGIC_SWAPPED 0x41a9e50b
+#define SGI_INFO_MAGIC 0x00072959
+#define SGI_INFO_MAGIC_SWAPPED 0x59290700
+
+#define SGI_SSWAP16(x) (sgi_other_endian ? fdisk_swap16(x) : (uint16_t)(x))
+#define SGI_SSWAP32(x) (sgi_other_endian ? fdisk_swap32(x) : (uint32_t)(x))
+
+#define sgilabel ((sgi_partition *)MBRbuffer)
+#define sgiparam (sgilabel->devparam)
+
+/*
+ *
+ * fdisksgilabel.c
+ *
+ * Copyright (C) Andreas Neuper, Sep 1998.
+ * This file may be modified and redistributed under
+ * the terms of the GNU Public License.
+ *
+ * Sat Mar 20 EST 1999 Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ * Internationalization
+ */
+
+
+static int sgi_other_endian;
+static int debug;
+static short sgi_volumes = 1;
+
+/*
+ * only dealing with free blocks here
+ */
+
+typedef struct {
+ unsigned int first;
+ unsigned int last;
+} freeblocks;
+static freeblocks freelist[17]; /* 16 partitions can produce 17 vacant slots */
+
+static void
+setfreelist(int i, unsigned int f, unsigned int l)
+{
+ freelist[i].first = f;
+ freelist[i].last = l;
+}
+
+static void
+add2freelist(unsigned int f, unsigned int l)
+{
+ int i;
+ for (i = 0; i < 17 ; i++)
+ if (freelist[i].last == 0)
+ break;
+ setfreelist(i, f, l);
+}
+
+static void
+clearfreelist(void)
+{
+ int i;
+
+ for (i = 0; i < 17 ; i++)
+ setfreelist(i, 0, 0);
+}
+
+static unsigned int
+isinfreelist(unsigned int b)
+{
+ int i;
+
+ for (i = 0; i < 17 ; i++)
+ if (freelist[i].first <= b && freelist[i].last >= b)
+ return freelist[i].last;
+ return 0;
+}
+ /* return last vacant block of this stride (never 0). */
+ /* the '>=' is not quite correct, but simplifies the code */
+/*
+ * end of free blocks section
+ */
+
+static const char *const sgi_sys_types[] = {
+/* SGI_VOLHDR */ "\x00" "SGI volhdr" ,
+/* 0x01 */ "\x01" "SGI trkrepl" ,
+/* 0x02 */ "\x02" "SGI secrepl" ,
+/* SGI_SWAP */ "\x03" "SGI raw" ,
+/* 0x04 */ "\x04" "SGI bsd" ,
+/* 0x05 */ "\x05" "SGI sysv" ,
+/* SGI_ENTIRE_DISK */ "\x06" "SGI volume" ,
+/* SGI_EFS */ "\x07" "SGI efs" ,
+/* 0x08 */ "\x08" "SGI lvol" ,
+/* 0x09 */ "\x09" "SGI rlvol" ,
+/* SGI_XFS */ "\x0a" "SGI xfs" ,
+/* SGI_XFSLOG */ "\x0b" "SGI xfslog" ,
+/* SGI_XLV */ "\x0c" "SGI xlv" ,
+/* SGI_XVM */ "\x0d" "SGI xvm" ,
+/* LINUX_SWAP */ "\x82" "Linux swap" ,
+/* LINUX_NATIVE */ "\x83" "Linux native",
+/* LINUX_LVM */ "\x8d" "Linux LVM" ,
+/* LINUX_RAID */ "\xfd" "Linux RAID" ,
+ NULL
+};
+
+
+static int
+sgi_get_nsect(void)
+{
+ return SGI_SSWAP16(sgilabel->devparam.nsect);
+}
+
+static int
+sgi_get_ntrks(void)
+{
+ return SGI_SSWAP16(sgilabel->devparam.ntrks);
+}
+
+static unsigned int
+two_s_complement_32bit_sum(unsigned int* base, int size /* in bytes */)
+{
+ int i = 0;
+ unsigned int sum = 0;
+
+ size /= sizeof(unsigned int);
+ for (i = 0; i < size; i++)
+ sum -= SGI_SSWAP32(base[i]);
+ return sum;
+}
+
+void BUG_bad_sgi_partition_size(void);
+
+static int
+check_sgi_label(void)
+{
+ if (sizeof(sgi_partition) > 512) {
+ /* According to MIPS Computer Systems, Inc the label
+ * must not contain more than 512 bytes */
+ BUG_bad_sgi_partition_size();
+ }
+
+ if (sgilabel->magic != SGI_LABEL_MAGIC
+ && sgilabel->magic != SGI_LABEL_MAGIC_SWAPPED
+ ) {
+ current_label_type = label_dos;
+ return 0;
+ }
+
+ sgi_other_endian = (sgilabel->magic == SGI_LABEL_MAGIC_SWAPPED);
+ /*
+ * test for correct checksum
+ */
+ if (two_s_complement_32bit_sum((unsigned int*)sgilabel,
+ sizeof(*sgilabel))) {
+ printf("Detected sgi disklabel with wrong checksum\n");
+ }
+ update_units();
+ current_label_type = label_sgi;
+ partitions = 16;
+ sgi_volumes = 15;
+ return 1;
+}
+
+static unsigned int
+sgi_get_start_sector(int i)
+{
+ return SGI_SSWAP32(sgilabel->partitions[i].start_sector);
+}
+
+static unsigned int
+sgi_get_num_sectors(int i)
+{
+ return SGI_SSWAP32(sgilabel->partitions[i].num_sectors);
+}
+
+static int
+sgi_get_sysid(int i)
+{
+ return SGI_SSWAP32(sgilabel->partitions[i].id);
+}
+
+static int
+sgi_get_bootpartition(void)
+{
+ return SGI_SSWAP16(sgilabel->boot_part);
+}
+
+static int
+sgi_get_swappartition(void)
+{
+ return SGI_SSWAP16(sgilabel->swap_part);
+}
+
+static void
+sgi_list_table(int xtra)
+{
+ int i, w, wd;
+ int kpi = 0; /* kernel partition ID */
+
+ if (xtra) {
+ printf("\nDisk %s (SGI disk label): %d heads, %d sectors\n"
+ "%d cylinders, %d physical cylinders\n"
+ "%d extra sects/cyl, interleave %d:1\n"
+ "%s\n"
+ "Units = %s of %d * 512 bytes\n\n",
+ disk_device, heads, sectors, cylinders,
+ SGI_SSWAP16(sgiparam.pcylcount),
+ SGI_SSWAP16(sgiparam.sparecyl),
+ SGI_SSWAP16(sgiparam.ilfact),
+ (char *)sgilabel,
+ str_units(PLURAL), units_per_sector);
+ } else {
+ printf("\nDisk %s (SGI disk label): "
+ "%d heads, %d sectors, %d cylinders\n"
+ "Units = %s of %d * 512 bytes\n\n",
+ disk_device, heads, sectors, cylinders,
+ str_units(PLURAL), units_per_sector );
+ }
+
+ w = strlen(disk_device);
+ wd = sizeof("Device") - 1;
+ if (w < wd)
+ w = wd;
+
+ printf("----- partitions -----\n"
+ "Pt# %*s Info Start End Sectors Id System\n",
+ w + 2, "Device");
+ for (i = 0 ; i < partitions; i++) {
+ if (sgi_get_num_sectors(i) || debug ) {
+ uint32_t start = sgi_get_start_sector(i);
+ uint32_t len = sgi_get_num_sectors(i);
+ kpi++; /* only count nonempty partitions */
+ printf(
+ "%2d: %s %4s %9ld %9ld %9ld %2x %s\n",
+/* fdisk part number */ i+1,
+/* device */ partname(disk_device, kpi, w+3),
+/* flags */ (sgi_get_swappartition() == i) ? "swap" :
+/* flags */ (sgi_get_bootpartition() == i) ? "boot" : " ",
+/* start */ (long) scround(start),
+/* end */ (long) scround(start+len)-1,
+/* no odd flag on end */(long) len,
+/* type id */ sgi_get_sysid(i),
+/* type name */ partition_type(sgi_get_sysid(i)));
+ }
+ }
+ printf("----- Bootinfo -----\nBootfile: %s\n"
+ "----- Directory Entries -----\n",
+ sgilabel->boot_file);
+ for (i = 0 ; i < sgi_volumes; i++) {
+ if (sgilabel->directory[i].vol_file_size) {
+ uint32_t start = SGI_SSWAP32(sgilabel->directory[i].vol_file_start);
+ uint32_t len = SGI_SSWAP32(sgilabel->directory[i].vol_file_size);
+ unsigned char *name = sgilabel->directory[i].vol_file_name;
+
+ printf("%2d: %-10s sector%5u size%8u\n",
+ i, (char*)name, (unsigned int) start, (unsigned int) len);
+ }
+ }
+}
+
+static void
+sgi_set_bootpartition(int i)
+{
+ sgilabel->boot_part = SGI_SSWAP16(((short)i));
+}
+
+static unsigned int
+sgi_get_lastblock(void)
+{
+ return heads * sectors * cylinders;
+}
+
+static void
+sgi_set_swappartition(int i)
+{
+ sgilabel->swap_part = SGI_SSWAP16(((short)i));
+}
+
+static int
+sgi_check_bootfile(const char* aFile)
+{
+ if (strlen(aFile) < 3) /* "/a\n" is minimum */ {
+ printf("\nInvalid Bootfile!\n"
+ "\tThe bootfile must be an absolute non-zero pathname,\n"
+ "\te.g. \"/unix\" or \"/unix.save\".\n");
+ return 0;
+ }
+ if (strlen(aFile) > 16) {
+ printf("\nName of Bootfile too long (>16 bytes)\n");
+ return 0;
+ }
+ if (aFile[0] != '/') {
+ printf("\nBootfile must have a fully qualified pathname\n");
+ return 0;
+ }
+ if (strncmp(aFile, (char*)sgilabel->boot_file, 16)) {
+ printf("\nBe aware, that the bootfile is not checked for existence.\n"
+ "\tSGI's default is \"/unix\" and for backup \"/unix.save\".\n");
+ /* filename is correct and did change */
+ return 1;
+ }
+ return 0; /* filename did not change */
+}
+
+static const char *
+sgi_get_bootfile(void)
+{
+ return (char*)sgilabel->boot_file;
+}
+
+static void
+sgi_set_bootfile(const char* aFile)
+{
+ int i = 0;
+
+ if (sgi_check_bootfile(aFile)) {
+ while (i < 16) {
+ if ((aFile[i] != '\n') /* in principle caught again by next line */
+ && (strlen(aFile) > i))
+ sgilabel->boot_file[i] = aFile[i];
+ else
+ sgilabel->boot_file[i] = 0;
+ i++;
+ }
+ printf("\n\tBootfile is changed to \"%s\"\n", sgilabel->boot_file);
+ }
+}
+
+static void
+create_sgiinfo(void)
+{
+ /* I keep SGI's habit to write the sgilabel to the second block */
+ sgilabel->directory[0].vol_file_start = SGI_SSWAP32(2);
+ sgilabel->directory[0].vol_file_size = SGI_SSWAP32(sizeof(sgiinfo));
+ strncpy((char*)sgilabel->directory[0].vol_file_name, "sgilabel", 8);
+}
+
+static sgiinfo *fill_sgiinfo(void);
+
+static void
+sgi_write_table(void)
+{
+ sgilabel->csum = 0;
+ sgilabel->csum = SGI_SSWAP32(two_s_complement_32bit_sum(
+ (unsigned int*)sgilabel, sizeof(*sgilabel)));
+ assert(two_s_complement_32bit_sum(
+ (unsigned int*)sgilabel, sizeof(*sgilabel)) == 0);
+
+ if (lseek(fd, 0, SEEK_SET) < 0)
+ fdisk_fatal(unable_to_seek);
+ if (write(fd, sgilabel, SECTOR_SIZE) != SECTOR_SIZE)
+ fdisk_fatal(unable_to_write);
+ if (!strncmp((char*)sgilabel->directory[0].vol_file_name, "sgilabel", 8)) {
+ /*
+ * keep this habit of first writing the "sgilabel".
+ * I never tested whether it works without (AN 981002).
+ */
+ sgiinfo *info = fill_sgiinfo();
+ int infostartblock = SGI_SSWAP32(sgilabel->directory[0].vol_file_start);
+ if (lseek(fd, infostartblock*SECTOR_SIZE, SEEK_SET) < 0)
+ fdisk_fatal(unable_to_seek);
+ if (write(fd, info, SECTOR_SIZE) != SECTOR_SIZE)
+ fdisk_fatal(unable_to_write);
+ free(info);
+ }
+}
+
+static int
+compare_start(int *x, int *y)
+{
+ /*
+ * sort according to start sectors
+ * and prefers largest partition:
+ * entry zero is entire disk entry
+ */
+ unsigned int i = *x;
+ unsigned int j = *y;
+ unsigned int a = sgi_get_start_sector(i);
+ unsigned int b = sgi_get_start_sector(j);
+ unsigned int c = sgi_get_num_sectors(i);
+ unsigned int d = sgi_get_num_sectors(j);
+
+ if (a == b)
+ return (d > c) ? 1 : (d == c) ? 0 : -1;
+ return (a > b) ? 1 : -1;
+}
+
+
+static int
+verify_sgi(int verbose)
+{
+ int Index[16]; /* list of valid partitions */
+ int sortcount = 0; /* number of used partitions, i.e. non-zero lengths */
+ int entire = 0, i = 0;
+ unsigned int start = 0;
+ long long gap = 0; /* count unused blocks */
+ unsigned int lastblock = sgi_get_lastblock();
+
+ clearfreelist();
+ for (i = 0; i < 16; i++) {
+ if (sgi_get_num_sectors(i) != 0) {
+ Index[sortcount++] = i;
+ if (sgi_get_sysid(i) == SGI_ENTIRE_DISK) {
+ if (entire++ == 1) {
+ if (verbose)
+ printf("More than one entire disk entry present\n");
+ }
+ }
+ }
+ }
+ if (sortcount == 0) {
+ if (verbose)
+ printf("No partitions defined\n");
+ return (lastblock > 0) ? 1 : (lastblock == 0) ? 0 : -1;
+ }
+ qsort(Index, sortcount, sizeof(Index[0]), (void*)compare_start);
+ if (sgi_get_sysid(Index[0]) == SGI_ENTIRE_DISK) {
+ if ((Index[0] != 10) && verbose)
+ printf("IRIX likes when Partition 11 covers the entire disk\n");
+ if ((sgi_get_start_sector(Index[0]) != 0) && verbose)
+ printf("The entire disk partition should start "
+ "at block 0,\n"
+ "not at diskblock %d\n",
+ sgi_get_start_sector(Index[0]));
+ if (debug) /* I do not understand how some disks fulfil it */
+ if ((sgi_get_num_sectors(Index[0]) != lastblock) && verbose)
+ printf("The entire disk partition is only %d diskblock large,\n"
+ "but the disk is %d diskblocks long\n",
+ sgi_get_num_sectors(Index[0]), lastblock);
+ lastblock = sgi_get_num_sectors(Index[0]);
+ } else {
+ if (verbose)
+ printf("One Partition (#11) should cover the entire disk\n");
+ if (debug > 2)
+ printf("sysid=%d\tpartition=%d\n",
+ sgi_get_sysid(Index[0]), Index[0]+1);
+ }
+ for (i = 1, start = 0; i < sortcount; i++) {
+ int cylsize = sgi_get_nsect() * sgi_get_ntrks();
+
+ if ((sgi_get_start_sector(Index[i]) % cylsize) != 0) {
+ if (debug) /* I do not understand how some disks fulfil it */
+ if (verbose)
+ printf("Partition %d does not start on cylinder boundary\n",
+ Index[i]+1);
+ }
+ if (sgi_get_num_sectors(Index[i]) % cylsize != 0) {
+ if (debug) /* I do not understand how some disks fulfil it */
+ if (verbose)
+ printf("Partition %d does not end on cylinder boundary\n",
+ Index[i]+1);
+ }
+ /* We cannot handle several "entire disk" entries. */
+ if (sgi_get_sysid(Index[i]) == SGI_ENTIRE_DISK) continue;
+ if (start > sgi_get_start_sector(Index[i])) {
+ if (verbose)
+ printf("Partitions %d and %d overlap by %d sectors\n",
+ Index[i-1]+1, Index[i]+1,
+ start - sgi_get_start_sector(Index[i]));
+ if (gap > 0) gap = -gap;
+ if (gap == 0) gap = -1;
+ }
+ if (start < sgi_get_start_sector(Index[i])) {
+ if (verbose)
+ printf("Unused gap of %8u sectors - sectors %8u-%8u\n",
+ sgi_get_start_sector(Index[i]) - start,
+ start, sgi_get_start_sector(Index[i])-1);
+ gap += sgi_get_start_sector(Index[i]) - start;
+ add2freelist(start, sgi_get_start_sector(Index[i]));
+ }
+ start = sgi_get_start_sector(Index[i])
+ + sgi_get_num_sectors(Index[i]);
+ if (debug > 1) {
+ if (verbose)
+ printf("%2d:%12d\t%12d\t%12d\n", Index[i],
+ sgi_get_start_sector(Index[i]),
+ sgi_get_num_sectors(Index[i]),
+ sgi_get_sysid(Index[i]));
+ }
+ }
+ if (start < lastblock) {
+ if (verbose)
+ printf("Unused gap of %8u sectors - sectors %8u-%8u\n",
+ lastblock - start, start, lastblock-1);
+ gap += lastblock - start;
+ add2freelist(start, lastblock);
+ }
+ /*
+ * Done with arithmetics
+ * Go for details now
+ */
+ if (verbose) {
+ if (!sgi_get_num_sectors(sgi_get_bootpartition())) {
+ printf("\nThe boot partition does not exist\n");
+ }
+ if (!sgi_get_num_sectors(sgi_get_swappartition())) {
+ printf("\nThe swap partition does not exist\n");
+ } else {
+ if ((sgi_get_sysid(sgi_get_swappartition()) != SGI_SWAP)
+ && (sgi_get_sysid(sgi_get_swappartition()) != LINUX_SWAP))
+ printf("\nThe swap partition has no swap type\n");
+ }
+ if (sgi_check_bootfile("/unix"))
+ printf("\tYou have chosen an unusual boot file name\n");
+ }
+ return (gap > 0) ? 1 : (gap == 0) ? 0 : -1;
+}
+
+static int
+sgi_gaps(void)
+{
+ /*
+ * returned value is:
+ * = 0 : disk is properly filled to the rim
+ * < 0 : there is an overlap
+ * > 0 : there is still some vacant space
+ */
+ return verify_sgi(0);
+}
+
+static void
+sgi_change_sysid(int i, int sys)
+{
+ if (sgi_get_num_sectors(i) == 0 ) { /* caught already before, ... */
+ printf("Sorry you may change the Tag of non-empty partitions\n");
+ return;
+ }
+ if ((sys != SGI_ENTIRE_DISK) && (sys != SGI_VOLHDR)
+ && (sgi_get_start_sector(i) < 1)
+ ) {
+ read_maybe_empty(
+ "It is highly recommended that the partition at offset 0\n"
+ "is of type \"SGI volhdr\", the IRIX system will rely on it to\n"
+ "retrieve from its directory standalone tools like sash and fx.\n"
+ "Only the \"SGI volume\" entire disk section may violate this.\n"
+ "Type YES if you are sure about tagging this partition differently.\n");
+ if (strcmp(line_ptr, "YES\n") != 0)
+ return;
+ }
+ sgilabel->partitions[i].id = SGI_SSWAP32(sys);
+}
+
+/* returns partition index of first entry marked as entire disk */
+static int
+sgi_entire(void)
+{
+ int i;
+
+ for (i = 0; i < 16; i++)
+ if (sgi_get_sysid(i) == SGI_VOLUME)
+ return i;
+ return -1;
+}
+
+static void
+sgi_set_partition(int i, unsigned int start, unsigned int length, int sys)
+{
+ sgilabel->partitions[i].id = SGI_SSWAP32(sys);
+ sgilabel->partitions[i].num_sectors = SGI_SSWAP32(length);
+ sgilabel->partitions[i].start_sector = SGI_SSWAP32(start);
+ set_changed(i);
+ if (sgi_gaps() < 0) /* rebuild freelist */
+ printf("Partition overlap detected\n");
+}
+
+static void
+sgi_set_entire(void)
+{
+ int n;
+
+ for (n = 10; n < partitions; n++) {
+ if (!sgi_get_num_sectors(n) ) {
+ sgi_set_partition(n, 0, sgi_get_lastblock(), SGI_VOLUME);
+ break;
+ }
+ }
+}
+
+static void
+sgi_set_volhdr(void)
+{
+ int n;
+
+ for (n = 8; n < partitions; n++) {
+ if (!sgi_get_num_sectors(n)) {
+ /*
+ * 5 cylinders is an arbitrary value I like
+ * IRIX 5.3 stored files in the volume header
+ * (like sash, symmon, fx, ide) with ca. 3200
+ * sectors.
+ */
+ if (heads * sectors * 5 < sgi_get_lastblock())
+ sgi_set_partition(n, 0, heads * sectors * 5, SGI_VOLHDR);
+ break;
+ }
+ }
+}
+
+static void
+sgi_delete_partition(int i)
+{
+ sgi_set_partition(i, 0, 0, 0);
+}
+
+static void
+sgi_add_partition(int n, int sys)
+{
+ char mesg[256];
+ unsigned int first = 0, last = 0;
+
+ if (n == 10) {
+ sys = SGI_VOLUME;
+ } else if (n == 8) {
+ sys = 0;
+ }
+ if (sgi_get_num_sectors(n)) {
+ printf(msg_part_already_defined, n + 1);
+ return;
+ }
+ if ((sgi_entire() == -1) && (sys != SGI_VOLUME)) {
+ printf("Attempting to generate entire disk entry automatically\n");
+ sgi_set_entire();
+ sgi_set_volhdr();
+ }
+ if ((sgi_gaps() == 0) && (sys != SGI_VOLUME)) {
+ printf("The entire disk is already covered with partitions\n");
+ return;
+ }
+ if (sgi_gaps() < 0) {
+ printf("You got a partition overlap on the disk. Fix it first!\n");
+ return;
+ }
+ snprintf(mesg, sizeof(mesg), "First %s", str_units(SINGULAR));
+ while (1) {
+ if (sys == SGI_VOLUME) {
+ last = sgi_get_lastblock();
+ first = read_int(0, 0, last-1, 0, mesg);
+ if (first != 0) {
+ printf("It is highly recommended that eleventh partition\n"
+ "covers the entire disk and is of type 'SGI volume'\n");
+ }
+ } else {
+ first = freelist[0].first;
+ last = freelist[0].last;
+ first = read_int(scround(first), scround(first), scround(last)-1,
+ 0, mesg);
+ }
+ if (display_in_cyl_units)
+ first *= units_per_sector;
+ else
+ first = first; /* align to cylinder if you know how ... */
+ if (!last )
+ last = isinfreelist(first);
+ if (last != 0)
+ break;
+ printf("You will get a partition overlap on the disk. "
+ "Fix it first!\n");
+ }
+ snprintf(mesg, sizeof(mesg), " Last %s", str_units(SINGULAR));
+ last = read_int(scround(first), scround(last)-1, scround(last)-1,
+ scround(first), mesg)+1;
+ if (display_in_cyl_units)
+ last *= units_per_sector;
+ else
+ last = last; /* align to cylinder if You know how ... */
+ if ( (sys == SGI_VOLUME) && (first != 0 || last != sgi_get_lastblock() ) )
+ printf("It is highly recommended that eleventh partition\n"
+ "covers the entire disk and is of type 'SGI volume'\n");
+ sgi_set_partition(n, first, last-first, sys);
+}
+
+#if ENABLE_FEATURE_FDISK_ADVANCED
+static void
+create_sgilabel(void)
+{
+ struct hd_geometry geometry;
+ struct {
+ unsigned int start;
+ unsigned int nsect;
+ int sysid;
+ } old[4];
+ int i = 0;
+ long longsectors; /* the number of sectors on the device */
+ int res; /* the result from the ioctl */
+ int sec_fac; /* the sector factor */
+
+ sec_fac = sector_size / 512; /* determine the sector factor */
+
+ printf(msg_building_new_label, "SGI disklabel");
+
+ sgi_other_endian = (BYTE_ORDER == LITTLE_ENDIAN);
+ res = ioctl(fd, BLKGETSIZE, &longsectors);
+ if (!ioctl(fd, HDIO_GETGEO, &geometry)) {
+ heads = geometry.heads;
+ sectors = geometry.sectors;
+ if (res == 0) {
+ /* the get device size ioctl was successful */
+ cylinders = longsectors / (heads * sectors);
+ cylinders /= sec_fac;
+ } else {
+ /* otherwise print error and use truncated version */
+ cylinders = geometry.cylinders;
+ printf(
+"Warning: BLKGETSIZE ioctl failed on %s. Using geometry cylinder value of %d.\n"
+"This value may be truncated for devices > 33.8 GB.\n", disk_device, cylinders);
+ }
+ }
+ for (i = 0; i < 4; i++) {
+ old[i].sysid = 0;
+ if (valid_part_table_flag(MBRbuffer)) {
+ if (get_part_table(i)->sys_ind) {
+ old[i].sysid = get_part_table(i)->sys_ind;
+ old[i].start = get_start_sect(get_part_table(i));
+ old[i].nsect = get_nr_sects(get_part_table(i));
+ printf("Trying to keep parameters of partition %d\n", i);
+ if (debug)
+ printf("ID=%02x\tSTART=%d\tLENGTH=%d\n",
+ old[i].sysid, old[i].start, old[i].nsect);
+ }
+ }
+ }
+
+ memset(MBRbuffer, 0, sizeof(MBRbuffer));
+ /* fields with '//' are already zeroed out by memset above */
+
+ sgilabel->magic = SGI_SSWAP32(SGI_LABEL_MAGIC);
+ //sgilabel->boot_part = SGI_SSWAP16(0);
+ sgilabel->swap_part = SGI_SSWAP16(1);
+
+ //memset(sgilabel->boot_file, 0, 16);
+ strcpy((char*)sgilabel->boot_file, "/unix"); /* sizeof(sgilabel->boot_file) == 16 > 6 */
+
+ //sgilabel->devparam.skew = (0);
+ //sgilabel->devparam.gap1 = (0);
+ //sgilabel->devparam.gap2 = (0);
+ //sgilabel->devparam.sparecyl = (0);
+ sgilabel->devparam.pcylcount = SGI_SSWAP16(geometry.cylinders);
+ //sgilabel->devparam.head_vol0 = SGI_SSWAP16(0);
+ /* tracks/cylinder (heads) */
+ sgilabel->devparam.ntrks = SGI_SSWAP16(geometry.heads);
+ //sgilabel->devparam.cmd_tag_queue_depth = (0);
+ //sgilabel->devparam.unused0 = (0);
+ //sgilabel->devparam.unused1 = SGI_SSWAP16(0);
+ /* sectors/track */
+ sgilabel->devparam.nsect = SGI_SSWAP16(geometry.sectors);
+ sgilabel->devparam.bytes = SGI_SSWAP16(512);
+ sgilabel->devparam.ilfact = SGI_SSWAP16(1);
+ sgilabel->devparam.flags = SGI_SSWAP32(TRACK_FWD|
+ IGNORE_ERRORS|RESEEK);
+ //sgilabel->devparam.datarate = SGI_SSWAP32(0);
+ sgilabel->devparam.retries_on_error = SGI_SSWAP32(1);
+ //sgilabel->devparam.ms_per_word = SGI_SSWAP32(0);
+ //sgilabel->devparam.xylogics_gap1 = SGI_SSWAP16(0);
+ //sgilabel->devparam.xylogics_syncdelay = SGI_SSWAP16(0);
+ //sgilabel->devparam.xylogics_readdelay = SGI_SSWAP16(0);
+ //sgilabel->devparam.xylogics_gap2 = SGI_SSWAP16(0);
+ //sgilabel->devparam.xylogics_readgate = SGI_SSWAP16(0);
+ //sgilabel->devparam.xylogics_writecont = SGI_SSWAP16(0);
+ //memset( &(sgilabel->directory), 0, sizeof(struct volume_directory)*15 );
+ //memset( &(sgilabel->partitions), 0, sizeof(struct sgi_partinfo)*16 );
+ current_label_type = label_sgi;
+ partitions = 16;
+ sgi_volumes = 15;
+ sgi_set_entire();
+ sgi_set_volhdr();
+ for (i = 0; i < 4; i++) {
+ if (old[i].sysid) {
+ sgi_set_partition(i, old[i].start, old[i].nsect, old[i].sysid);
+ }
+ }
+}
+
+static void
+sgi_set_xcyl(void)
+{
+ /* do nothing in the beginning */
+}
+#endif /* FEATURE_FDISK_ADVANCED */
+
+/* _____________________________________________________________
+ */
+
+static sgiinfo *
+fill_sgiinfo(void)
+{
+ sgiinfo *info = xzalloc(sizeof(sgiinfo));
+
+ info->magic = SGI_SSWAP32(SGI_INFO_MAGIC);
+ info->b1 = SGI_SSWAP32(-1);
+ info->b2 = SGI_SSWAP16(-1);
+ info->b3 = SGI_SSWAP16(1);
+ /* You may want to replace this string !!!!!!! */
+ strcpy( (char*)info->scsi_string, "IBM OEM 0662S12 3 30" );
+ strcpy( (char*)info->serial, "0000" );
+ info->check1816 = SGI_SSWAP16(18*256 +16 );
+ strcpy( (char*)info->installer, "Sfx version 5.3, Oct 18, 1994" );
+ return info;
+}
+#endif /* SGI_LABEL */
diff --git a/i/pc104/initrd/conf/busybox/util-linux/fdisk_sun.c b/i/pc104/initrd/conf/busybox/util-linux/fdisk_sun.c
new file mode 100644
index 0000000..2fa7b2b
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/util-linux/fdisk_sun.c
@@ -0,0 +1,730 @@
+#if ENABLE_FEATURE_SUN_LABEL
+
+#define SUNOS_SWAP 3
+#define SUN_WHOLE_DISK 5
+
+#define SUN_LABEL_MAGIC 0xDABE
+#define SUN_LABEL_MAGIC_SWAPPED 0xBEDA
+#define SUN_SSWAP16(x) (sun_other_endian ? fdisk_swap16(x) : (uint16_t)(x))
+#define SUN_SSWAP32(x) (sun_other_endian ? fdisk_swap32(x) : (uint32_t)(x))
+
+/* Copied from linux/major.h */
+#define FLOPPY_MAJOR 2
+
+#define SCSI_IOCTL_GET_IDLUN 0x5382
+
+/*
+ * fdisksunlabel.c
+ *
+ * I think this is mostly, or entirely, due to
+ * Jakub Jelinek (jj@sunsite.mff.cuni.cz), July 1996
+ *
+ * Merged with fdisk for other architectures, aeb, June 1998.
+ *
+ * Sat Mar 20 EST 1999 Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ * Internationalization
+ */
+
+
+static int sun_other_endian;
+static int scsi_disk;
+static int floppy;
+
+#ifndef IDE0_MAJOR
+#define IDE0_MAJOR 3
+#endif
+#ifndef IDE1_MAJOR
+#define IDE1_MAJOR 22
+#endif
+
+static void
+guess_device_type(void)
+{
+ struct stat bootstat;
+
+ if (fstat(fd, &bootstat) < 0) {
+ scsi_disk = 0;
+ floppy = 0;
+ } else if (S_ISBLK(bootstat.st_mode)
+ && (major(bootstat.st_rdev) == IDE0_MAJOR ||
+ major(bootstat.st_rdev) == IDE1_MAJOR)) {
+ scsi_disk = 0;
+ floppy = 0;
+ } else if (S_ISBLK(bootstat.st_mode)
+ && major(bootstat.st_rdev) == FLOPPY_MAJOR) {
+ scsi_disk = 0;
+ floppy = 1;
+ } else {
+ scsi_disk = 1;
+ floppy = 0;
+ }
+}
+
+static const char *const sun_sys_types[] = {
+ "\x00" "Empty" , /* 0 */
+ "\x01" "Boot" , /* 1 */
+ "\x02" "SunOS root" , /* 2 */
+ "\x03" "SunOS swap" , /* SUNOS_SWAP */
+ "\x04" "SunOS usr" , /* 4 */
+ "\x05" "Whole disk" , /* SUN_WHOLE_DISK */
+ "\x06" "SunOS stand" , /* 6 */
+ "\x07" "SunOS var" , /* 7 */
+ "\x08" "SunOS home" , /* 8 */
+ "\x82" "Linux swap" , /* LINUX_SWAP */
+ "\x83" "Linux native", /* LINUX_NATIVE */
+ "\x8e" "Linux LVM" , /* 0x8e */
+/* New (2.2.x) raid partition with autodetect using persistent superblock */
+ "\xfd" "Linux raid autodetect", /* 0xfd */
+ NULL
+};
+
+
+static void
+set_sun_partition(int i, uint start, uint stop, int sysid)
+{
+ sunlabel->infos[i].id = sysid;
+ sunlabel->partitions[i].start_cylinder =
+ SUN_SSWAP32(start / (heads * sectors));
+ sunlabel->partitions[i].num_sectors =
+ SUN_SSWAP32(stop - start);
+ set_changed(i);
+}
+
+static int
+check_sun_label(void)
+{
+ unsigned short *ush;
+ int csum;
+
+ if (sunlabel->magic != SUN_LABEL_MAGIC
+ && sunlabel->magic != SUN_LABEL_MAGIC_SWAPPED) {
+ current_label_type = label_dos;
+ sun_other_endian = 0;
+ return 0;
+ }
+ sun_other_endian = (sunlabel->magic == SUN_LABEL_MAGIC_SWAPPED);
+ ush = ((unsigned short *) (sunlabel + 1)) - 1;
+ for (csum = 0; ush >= (unsigned short *)sunlabel;) csum ^= *ush--;
+ if (csum) {
+ printf("Detected sun disklabel with wrong checksum.\n"
+"Probably you'll have to set all the values,\n"
+"e.g. heads, sectors, cylinders and partitions\n"
+"or force a fresh label (s command in main menu)\n");
+ } else {
+ heads = SUN_SSWAP16(sunlabel->ntrks);
+ cylinders = SUN_SSWAP16(sunlabel->ncyl);
+ sectors = SUN_SSWAP16(sunlabel->nsect);
+ }
+ update_units();
+ current_label_type = label_sun;
+ partitions = 8;
+ return 1;
+}
+
+static const struct sun_predefined_drives {
+ const char *vendor;
+ const char *model;
+ unsigned short sparecyl;
+ unsigned short ncyl;
+ unsigned short nacyl;
+ unsigned short pcylcount;
+ unsigned short ntrks;
+ unsigned short nsect;
+ unsigned short rspeed;
+} sun_drives[] = {
+ { "Quantum","ProDrive 80S",1,832,2,834,6,34,3662},
+ { "Quantum","ProDrive 105S",1,974,2,1019,6,35,3662},
+ { "CDC","Wren IV 94171-344",3,1545,2,1549,9,46,3600},
+ { "IBM","DPES-31080",0,4901,2,4903,4,108,5400},
+ { "IBM","DORS-32160",0,1015,2,1017,67,62,5400},
+ { "IBM","DNES-318350",0,11199,2,11474,10,320,7200},
+ { "SEAGATE","ST34371",0,3880,2,3882,16,135,7228},
+ { "","SUN0104",1,974,2,1019,6,35,3662},
+ { "","SUN0207",4,1254,2,1272,9,36,3600},
+ { "","SUN0327",3,1545,2,1549,9,46,3600},
+ { "","SUN0340",0,1538,2,1544,6,72,4200},
+ { "","SUN0424",2,1151,2,2500,9,80,4400},
+ { "","SUN0535",0,1866,2,2500,7,80,5400},
+ { "","SUN0669",5,1614,2,1632,15,54,3600},
+ { "","SUN1.0G",5,1703,2,1931,15,80,3597},
+ { "","SUN1.05",0,2036,2,2038,14,72,5400},
+ { "","SUN1.3G",6,1965,2,3500,17,80,5400},
+ { "","SUN2.1G",0,2733,2,3500,19,80,5400},
+ { "IOMEGA","Jaz",0,1019,2,1021,64,32,5394},
+};
+
+static const struct sun_predefined_drives *
+sun_autoconfigure_scsi(void)
+{
+ const struct sun_predefined_drives *p = NULL;
+
+#ifdef SCSI_IOCTL_GET_IDLUN
+ unsigned int id[2];
+ char buffer[2048];
+ char buffer2[2048];
+ FILE *pfd;
+ char *vendor;
+ char *model;
+ char *q;
+ int i;
+
+ if (ioctl(fd, SCSI_IOCTL_GET_IDLUN, &id))
+ return NULL;
+
+ sprintf(buffer,
+ "Host: scsi%d Channel: %02d Id: %02d Lun: %02d\n",
+ /* This is very wrong (works only if you have one HBA),
+ but I haven't found a way how to get hostno
+ from the current kernel */
+ 0,
+ (id[0]>>16) & 0xff,
+ id[0] & 0xff,
+ (id[0]>>8) & 0xff
+ );
+ pfd = fopen("/proc/scsi/scsi", "r");
+ if (!pfd) {
+ return NULL;
+ }
+ while (fgets(buffer2, 2048, pfd)) {
+ if (strcmp(buffer, buffer2))
+ continue;
+ if (!fgets(buffer2, 2048, pfd))
+ break;
+ q = strstr(buffer2, "Vendor: ");
+ if (!q)
+ break;
+ q += 8;
+ vendor = q;
+ q = strstr(q, " ");
+ *q++ = '\0'; /* truncate vendor name */
+ q = strstr(q, "Model: ");
+ if (!q)
+ break;
+ *q = '\0';
+ q += 7;
+ model = q;
+ q = strstr(q, " Rev: ");
+ if (!q)
+ break;
+ *q = '\0';
+ for (i = 0; i < SIZE(sun_drives); i++) {
+ if (*sun_drives[i].vendor && strcasecmp(sun_drives[i].vendor, vendor))
+ continue;
+ if (!strstr(model, sun_drives[i].model))
+ continue;
+ printf("Autoconfigure found a %s%s%s\n",
+ sun_drives[i].vendor,
+ (*sun_drives[i].vendor) ? " " : "",
+ sun_drives[i].model);
+ p = sun_drives + i;
+ break;
+ }
+ break;
+ }
+ fclose(pfd);
+#endif
+ return p;
+}
+
+static void
+create_sunlabel(void)
+{
+ struct hd_geometry geometry;
+ unsigned int ndiv;
+ int i;
+ unsigned char c;
+ const struct sun_predefined_drives *p = NULL;
+
+ printf(msg_building_new_label, "sun disklabel");
+
+ sun_other_endian = BB_LITTLE_ENDIAN;
+ memset(MBRbuffer, 0, sizeof(MBRbuffer));
+ sunlabel->magic = SUN_SSWAP16(SUN_LABEL_MAGIC);
+ if (!floppy) {
+ puts("Drive type\n"
+ " ? auto configure\n"
+ " 0 custom (with hardware detected defaults)");
+ for (i = 0; i < SIZE(sun_drives); i++) {
+ printf(" %c %s%s%s\n",
+ i + 'a', sun_drives[i].vendor,
+ (*sun_drives[i].vendor) ? " " : "",
+ sun_drives[i].model);
+ }
+ while (1) {
+ c = read_nonempty("Select type (? for auto, 0 for custom): ");
+ if (c == '0') {
+ break;
+ }
+ if (c >= 'a' && c < 'a' + SIZE(sun_drives)) {
+ p = sun_drives + c - 'a';
+ break;
+ }
+ if (c >= 'A' && c < 'A' + SIZE(sun_drives)) {
+ p = sun_drives + c - 'A';
+ break;
+ }
+ if (c == '?' && scsi_disk) {
+ p = sun_autoconfigure_scsi();
+ if (p)
+ break;
+ printf("Autoconfigure failed\n");
+ }
+ }
+ }
+ if (!p || floppy) {
+ if (!ioctl(fd, HDIO_GETGEO, &geometry)) {
+ heads = geometry.heads;
+ sectors = geometry.sectors;
+ cylinders = geometry.cylinders;
+ } else {
+ heads = 0;
+ sectors = 0;
+ cylinders = 0;
+ }
+ if (floppy) {
+ sunlabel->nacyl = 0;
+ sunlabel->pcylcount = SUN_SSWAP16(cylinders);
+ sunlabel->rspeed = SUN_SSWAP16(300);
+ sunlabel->ilfact = SUN_SSWAP16(1);
+ sunlabel->sparecyl = 0;
+ } else {
+ heads = read_int(1, heads, 1024, 0, "Heads");
+ sectors = read_int(1, sectors, 1024, 0, "Sectors/track");
+ if (cylinders)
+ cylinders = read_int(1, cylinders-2, 65535, 0, "Cylinders");
+ else
+ cylinders = read_int(1, 0, 65535, 0, "Cylinders");
+ sunlabel->nacyl = SUN_SSWAP16(read_int(0, 2, 65535, 0, "Alternate cylinders"));
+ sunlabel->pcylcount = SUN_SSWAP16(read_int(0, cylinders+SUN_SSWAP16(sunlabel->nacyl), 65535, 0, "Physical cylinders"));
+ sunlabel->rspeed = SUN_SSWAP16(read_int(1, 5400, 100000, 0, "Rotation speed (rpm)"));
+ sunlabel->ilfact = SUN_SSWAP16(read_int(1, 1, 32, 0, "Interleave factor"));
+ sunlabel->sparecyl = SUN_SSWAP16(read_int(0, 0, sectors, 0, "Extra sectors per cylinder"));
+ }
+ } else {
+ sunlabel->sparecyl = SUN_SSWAP16(p->sparecyl);
+ sunlabel->ncyl = SUN_SSWAP16(p->ncyl);
+ sunlabel->nacyl = SUN_SSWAP16(p->nacyl);
+ sunlabel->pcylcount = SUN_SSWAP16(p->pcylcount);
+ sunlabel->ntrks = SUN_SSWAP16(p->ntrks);
+ sunlabel->nsect = SUN_SSWAP16(p->nsect);
+ sunlabel->rspeed = SUN_SSWAP16(p->rspeed);
+ sunlabel->ilfact = SUN_SSWAP16(1);
+ cylinders = p->ncyl;
+ heads = p->ntrks;
+ sectors = p->nsect;
+ puts("You may change all the disk params from the x menu");
+ }
+
+ snprintf((char *)(sunlabel->info), sizeof(sunlabel->info),
+ "%s%s%s cyl %d alt %d hd %d sec %d",
+ p ? p->vendor : "", (p && *p->vendor) ? " " : "",
+ p ? p->model : (floppy ? "3,5\" floppy" : "Linux custom"),
+ cylinders, SUN_SSWAP16(sunlabel->nacyl), heads, sectors);
+
+ sunlabel->ntrks = SUN_SSWAP16(heads);
+ sunlabel->nsect = SUN_SSWAP16(sectors);
+ sunlabel->ncyl = SUN_SSWAP16(cylinders);
+ if (floppy)
+ set_sun_partition(0, 0, cylinders * heads * sectors, LINUX_NATIVE);
+ else {
+ if (cylinders * heads * sectors >= 150 * 2048) {
+ ndiv = cylinders - (50 * 2048 / (heads * sectors)); /* 50M swap */
+ } else
+ ndiv = cylinders * 2 / 3;
+ set_sun_partition(0, 0, ndiv * heads * sectors, LINUX_NATIVE);
+ set_sun_partition(1, ndiv * heads * sectors, cylinders * heads * sectors, LINUX_SWAP);
+ sunlabel->infos[1].flags |= 0x01; /* Not mountable */
+ }
+ set_sun_partition(2, 0, cylinders * heads * sectors, SUN_WHOLE_DISK);
+ {
+ unsigned short *ush = (unsigned short *)sunlabel;
+ unsigned short csum = 0;
+ while (ush < (unsigned short *)(&sunlabel->csum))
+ csum ^= *ush++;
+ sunlabel->csum = csum;
+ }
+
+ set_all_unchanged();
+ set_changed(0);
+ get_boot(create_empty_sun);
+}
+
+static void
+toggle_sunflags(int i, unsigned char mask)
+{
+ if (sunlabel->infos[i].flags & mask)
+ sunlabel->infos[i].flags &= ~mask;
+ else
+ sunlabel->infos[i].flags |= mask;
+ set_changed(i);
+}
+
+static void
+fetch_sun(uint *starts, uint *lens, uint *start, uint *stop)
+{
+ int i, continuous = 1;
+
+ *start = 0;
+ *stop = cylinders * heads * sectors;
+ for (i = 0; i < partitions; i++) {
+ if (sunlabel->partitions[i].num_sectors
+ && sunlabel->infos[i].id
+ && sunlabel->infos[i].id != SUN_WHOLE_DISK) {
+ starts[i] = SUN_SSWAP32(sunlabel->partitions[i].start_cylinder) * heads * sectors;
+ lens[i] = SUN_SSWAP32(sunlabel->partitions[i].num_sectors);
+ if (continuous) {
+ if (starts[i] == *start)
+ *start += lens[i];
+ else if (starts[i] + lens[i] >= *stop)
+ *stop = starts[i];
+ else
+ continuous = 0;
+ /* There will be probably more gaps
+ than one, so lets check afterwards */
+ }
+ } else {
+ starts[i] = 0;
+ lens[i] = 0;
+ }
+ }
+}
+
+static uint *verify_sun_starts;
+
+static int
+verify_sun_cmp(int *a, int *b)
+{
+ if (*a == -1) return 1;
+ if (*b == -1) return -1;
+ if (verify_sun_starts[*a] > verify_sun_starts[*b]) return 1;
+ return -1;
+}
+
+static void
+verify_sun(void)
+{
+ uint starts[8], lens[8], start, stop;
+ int i,j,k,starto,endo;
+ int array[8];
+
+ verify_sun_starts = starts;
+ fetch_sun(starts,lens,&start,&stop);
+ for (k = 0; k < 7; k++) {
+ for (i = 0; i < 8; i++) {
+ if (k && (lens[i] % (heads * sectors))) {
+ printf("Partition %d doesn't end on cylinder boundary\n", i+1);
+ }
+ if (lens[i]) {
+ for (j = 0; j < i; j++)
+ if (lens[j]) {
+ if (starts[j] == starts[i]+lens[i]) {
+ starts[j] = starts[i]; lens[j] += lens[i];
+ lens[i] = 0;
+ } else if (starts[i] == starts[j]+lens[j]){
+ lens[j] += lens[i];
+ lens[i] = 0;
+ } else if (!k) {
+ if (starts[i] < starts[j]+lens[j]
+ && starts[j] < starts[i]+lens[i]) {
+ starto = starts[i];
+ if (starts[j] > starto)
+ starto = starts[j];
+ endo = starts[i]+lens[i];
+ if (starts[j]+lens[j] < endo)
+ endo = starts[j]+lens[j];
+ printf("Partition %d overlaps with others in "
+ "sectors %d-%d\n", i+1, starto, endo);
+ }
+ }
+ }
+ }
+ }
+ }
+ for (i = 0; i < 8; i++) {
+ if (lens[i])
+ array[i] = i;
+ else
+ array[i] = -1;
+ }
+ qsort(array,SIZE(array),sizeof(array[0]),
+ (int (*)(const void *,const void *)) verify_sun_cmp);
+ if (array[0] == -1) {
+ printf("No partitions defined\n");
+ return;
+ }
+ stop = cylinders * heads * sectors;
+ if (starts[array[0]])
+ printf("Unused gap - sectors 0-%d\n", starts[array[0]]);
+ for (i = 0; i < 7 && array[i+1] != -1; i++) {
+ printf("Unused gap - sectors %d-%d\n", starts[array[i]]+lens[array[i]], starts[array[i+1]]);
+ }
+ start = starts[array[i]] + lens[array[i]];
+ if (start < stop)
+ printf("Unused gap - sectors %d-%d\n", start, stop);
+}
+
+static void
+add_sun_partition(int n, int sys)
+{
+ uint start, stop, stop2;
+ uint starts[8], lens[8];
+ int whole_disk = 0;
+
+ char mesg[256];
+ int i, first, last;
+
+ if (sunlabel->partitions[n].num_sectors && sunlabel->infos[n].id) {
+ printf(msg_part_already_defined, n + 1);
+ return;
+ }
+
+ fetch_sun(starts,lens,&start,&stop);
+ if (stop <= start) {
+ if (n == 2)
+ whole_disk = 1;
+ else {
+ printf("Other partitions already cover the whole disk.\n"
+ "Delete/shrink them before retry.\n");
+ return;
+ }
+ }
+ snprintf(mesg, sizeof(mesg), "First %s", str_units(SINGULAR));
+ while (1) {
+ if (whole_disk)
+ first = read_int(0, 0, 0, 0, mesg);
+ else
+ first = read_int(scround(start), scround(stop)+1,
+ scround(stop), 0, mesg);
+ if (display_in_cyl_units)
+ first *= units_per_sector;
+ else
+ /* Starting sector has to be properly aligned */
+ first = (first + heads * sectors - 1) / (heads * sectors);
+ if (n == 2 && first != 0)
+ printf("\
+It is highly recommended that the third partition covers the whole disk\n\
+and is of type 'Whole disk'\n");
+ /* ewt asks to add: "don't start a partition at cyl 0"
+ However, edmundo@rano.demon.co.uk writes:
+ "In addition to having a Sun partition table, to be able to
+ boot from the disc, the first partition, /dev/sdX1, must
+ start at cylinder 0. This means that /dev/sdX1 contains
+ the partition table and the boot block, as these are the
+ first two sectors of the disc. Therefore you must be
+ careful what you use /dev/sdX1 for. In particular, you must
+ not use a partition starting at cylinder 0 for Linux swap,
+ as that would overwrite the partition table and the boot
+ block. You may, however, use such a partition for a UFS
+ or EXT2 file system, as these file systems leave the first
+ 1024 bytes undisturbed. */
+ /* On the other hand, one should not use partitions
+ starting at block 0 in an md, or the label will
+ be trashed. */
+ for (i = 0; i < partitions; i++)
+ if (lens[i] && starts[i] <= first && starts[i] + lens[i] > first)
+ break;
+ if (i < partitions && !whole_disk) {
+ if (n == 2 && !first) {
+ whole_disk = 1;
+ break;
+ }
+ printf("Sector %d is already allocated\n", first);
+ } else
+ break;
+ }
+ stop = cylinders * heads * sectors;
+ stop2 = stop;
+ for (i = 0; i < partitions; i++) {
+ if (starts[i] > first && starts[i] < stop)
+ stop = starts[i];
+ }
+ snprintf(mesg, sizeof(mesg),
+ "Last %s or +size or +sizeM or +sizeK",
+ str_units(SINGULAR));
+ if (whole_disk)
+ last = read_int(scround(stop2), scround(stop2), scround(stop2),
+ 0, mesg);
+ else if (n == 2 && !first)
+ last = read_int(scround(first), scround(stop2), scround(stop2),
+ scround(first), mesg);
+ else
+ last = read_int(scround(first), scround(stop), scround(stop),
+ scround(first), mesg);
+ if (display_in_cyl_units)
+ last *= units_per_sector;
+ if (n == 2 && !first) {
+ if (last >= stop2) {
+ whole_disk = 1;
+ last = stop2;
+ } else if (last > stop) {
+ printf(
+"You haven't covered the whole disk with the 3rd partition,\n"
+"but your value %d %s covers some other partition.\n"
+"Your entry has been changed to %d %s\n",
+ scround(last), str_units(SINGULAR),
+ scround(stop), str_units(SINGULAR));
+ last = stop;
+ }
+ } else if (!whole_disk && last > stop)
+ last = stop;
+
+ if (whole_disk)
+ sys = SUN_WHOLE_DISK;
+ set_sun_partition(n, first, last, sys);
+}
+
+static void
+sun_delete_partition(int i)
+{
+ unsigned int nsec;
+
+ if (i == 2
+ && sunlabel->infos[i].id == SUN_WHOLE_DISK
+ && !sunlabel->partitions[i].start_cylinder
+ && (nsec = SUN_SSWAP32(sunlabel->partitions[i].num_sectors)) == heads * sectors * cylinders)
+ printf("If you want to maintain SunOS/Solaris compatibility, "
+ "consider leaving this\n"
+ "partition as Whole disk (5), starting at 0, with %u "
+ "sectors\n", nsec);
+ sunlabel->infos[i].id = 0;
+ sunlabel->partitions[i].num_sectors = 0;
+}
+
+static void
+sun_change_sysid(int i, int sys)
+{
+ if (sys == LINUX_SWAP && !sunlabel->partitions[i].start_cylinder) {
+ read_maybe_empty(
+ "It is highly recommended that the partition at offset 0\n"
+ "is UFS, EXT2FS filesystem or SunOS swap. Putting Linux swap\n"
+ "there may destroy your partition table and bootblock.\n"
+ "Type YES if you're very sure you would like that partition\n"
+ "tagged with 82 (Linux swap): ");
+ if (strcmp (line_ptr, "YES\n"))
+ return;
+ }
+ switch (sys) {
+ case SUNOS_SWAP:
+ case LINUX_SWAP:
+ /* swaps are not mountable by default */
+ sunlabel->infos[i].flags |= 0x01;
+ break;
+ default:
+ /* assume other types are mountable;
+ user can change it anyway */
+ sunlabel->infos[i].flags &= ~0x01;
+ break;
+ }
+ sunlabel->infos[i].id = sys;
+}
+
+static void
+sun_list_table(int xtra)
+{
+ int i, w;
+
+ w = strlen(disk_device);
+ if (xtra)
+ printf(
+ "\nDisk %s (Sun disk label): %d heads, %d sectors, %d rpm\n"
+ "%d cylinders, %d alternate cylinders, %d physical cylinders\n"
+ "%d extra sects/cyl, interleave %d:1\n"
+ "%s\n"
+ "Units = %s of %d * 512 bytes\n\n",
+ disk_device, heads, sectors, SUN_SSWAP16(sunlabel->rspeed),
+ cylinders, SUN_SSWAP16(sunlabel->nacyl),
+ SUN_SSWAP16(sunlabel->pcylcount),
+ SUN_SSWAP16(sunlabel->sparecyl),
+ SUN_SSWAP16(sunlabel->ilfact),
+ (char *)sunlabel,
+ str_units(PLURAL), units_per_sector);
+ else
+ printf(
+ "\nDisk %s (Sun disk label): %d heads, %d sectors, %d cylinders\n"
+ "Units = %s of %d * 512 bytes\n\n",
+ disk_device, heads, sectors, cylinders,
+ str_units(PLURAL), units_per_sector);
+
+ printf("%*s Flag Start End Blocks Id System\n",
+ w + 1, "Device");
+ for (i = 0 ; i < partitions; i++) {
+ if (sunlabel->partitions[i].num_sectors) {
+ uint32_t start = SUN_SSWAP32(sunlabel->partitions[i].start_cylinder) * heads * sectors;
+ uint32_t len = SUN_SSWAP32(sunlabel->partitions[i].num_sectors);
+ printf("%s %c%c %9ld %9ld %9ld%c %2x %s\n",
+ partname(disk_device, i+1, w), /* device */
+ (sunlabel->infos[i].flags & 0x01) ? 'u' : ' ', /* flags */
+ (sunlabel->infos[i].flags & 0x10) ? 'r' : ' ',
+ (long) scround(start), /* start */
+ (long) scround(start+len), /* end */
+ (long) len / 2, len & 1 ? '+' : ' ', /* odd flag on end */
+ sunlabel->infos[i].id, /* type id */
+ partition_type(sunlabel->infos[i].id)); /* type name */
+ }
+ }
+}
+
+#if ENABLE_FEATURE_FDISK_ADVANCED
+
+static void
+sun_set_alt_cyl(void)
+{
+ sunlabel->nacyl =
+ SUN_SSWAP16(read_int(0, SUN_SSWAP16(sunlabel->nacyl), 65535, 0,
+ "Number of alternate cylinders"));
+}
+
+static void
+sun_set_ncyl(int cyl)
+{
+ sunlabel->ncyl = SUN_SSWAP16(cyl);
+}
+
+static void
+sun_set_xcyl(void)
+{
+ sunlabel->sparecyl =
+ SUN_SSWAP16(read_int(0, SUN_SSWAP16(sunlabel->sparecyl), sectors, 0,
+ "Extra sectors per cylinder"));
+}
+
+static void
+sun_set_ilfact(void)
+{
+ sunlabel->ilfact =
+ SUN_SSWAP16(read_int(1, SUN_SSWAP16(sunlabel->ilfact), 32, 0,
+ "Interleave factor"));
+}
+
+static void
+sun_set_rspeed(void)
+{
+ sunlabel->rspeed =
+ SUN_SSWAP16(read_int(1, SUN_SSWAP16(sunlabel->rspeed), 100000, 0,
+ "Rotation speed (rpm)"));
+}
+
+static void
+sun_set_pcylcount(void)
+{
+ sunlabel->pcylcount =
+ SUN_SSWAP16(read_int(0, SUN_SSWAP16(sunlabel->pcylcount), 65535, 0,
+ "Number of physical cylinders"));
+}
+#endif /* FEATURE_FDISK_ADVANCED */
+
+static void
+sun_write_table(void)
+{
+ unsigned short *ush = (unsigned short *)sunlabel;
+ unsigned short csum = 0;
+
+ while (ush < (unsigned short *)(&sunlabel->csum))
+ csum ^= *ush++;
+ sunlabel->csum = csum;
+ if (lseek(fd, 0, SEEK_SET) < 0)
+ fdisk_fatal(unable_to_seek);
+ if (write(fd, sunlabel, SECTOR_SIZE) != SECTOR_SIZE)
+ fdisk_fatal(unable_to_write);
+}
+#endif /* SUN_LABEL */
diff --git a/i/pc104/initrd/conf/busybox/util-linux/freeramdisk.c b/i/pc104/initrd/conf/busybox/util-linux/freeramdisk.c
new file mode 100644
index 0000000..ae341b0
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/util-linux/freeramdisk.c
@@ -0,0 +1,35 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * freeramdisk and fdflush implementations for busybox
+ *
+ * Copyright (C) 2000 and written by Emanuele Caratti <wiz@iol.it>
+ * Adjusted a bit by Erik Andersen <andersen@codepoet.org>
+ * Unified with fdflush by Tito Ragusa <farmatito@tiscali.it>
+ *
+ * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ */
+
+#include "busybox.h"
+
+/* From <linux/fd.h> */
+#define FDFLUSH _IO(2,0x4b)
+
+int freeramdisk_main(int argc, char **argv);
+int freeramdisk_main(int argc, char **argv)
+{
+ int result;
+ int fd;
+
+ if (argc != 2) bb_show_usage();
+
+ fd = xopen(argv[1], O_RDWR);
+
+ // Act like freeramdisk, fdflush, or both depending on configuration.
+ result = ioctl(fd, (ENABLE_FREERAMDISK && applet_name[1]=='r')
+ || !ENABLE_FDFLUSH ? BLKFLSBUF : FDFLUSH);
+
+ if (ENABLE_FEATURE_CLEAN_UP) close(fd);
+
+ if (result) bb_perror_msg_and_die("%s", argv[1]);
+ return EXIT_SUCCESS;
+}
diff --git a/i/pc104/initrd/conf/busybox/util-linux/fsck_minix.c b/i/pc104/initrd/conf/busybox/util-linux/fsck_minix.c
new file mode 100644
index 0000000..cc73cdd
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/util-linux/fsck_minix.c
@@ -0,0 +1,1390 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * fsck.c - a file system consistency checker for Linux.
+ *
+ * (C) 1991, 1992 Linus Torvalds.
+ *
+ * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ */
+
+/*
+ * 09.11.91 - made the first rudimentary functions
+ *
+ * 10.11.91 - updated, does checking, no repairs yet.
+ * Sent out to the mailing-list for testing.
+ *
+ * 14.11.91 - Testing seems to have gone well. Added some
+ * correction-code, and changed some functions.
+ *
+ * 15.11.91 - More correction code. Hopefully it notices most
+ * cases now, and tries to do something about them.
+ *
+ * 16.11.91 - More corrections (thanks to Mika Jalava). Most
+ * things seem to work now. Yeah, sure.
+ *
+ *
+ * 19.04.92 - Had to start over again from this old version, as a
+ * kernel bug ate my enhanced fsck in february.
+ *
+ * 28.02.93 - added support for different directory entry sizes..
+ *
+ * Sat Mar 6 18:59:42 1993, faith@cs.unc.edu: Output namelen with
+ * super-block information
+ *
+ * Sat Oct 9 11:17:11 1993, faith@cs.unc.edu: make exit status conform
+ * to that required by fsutil
+ *
+ * Mon Jan 3 11:06:52 1994 - Dr. Wettstein (greg%wind.uucp@plains.nodak.edu)
+ * Added support for file system valid flag. Also
+ * added program_version variable and output of
+ * program name and version number when program
+ * is executed.
+ *
+ * 30.10.94 - added support for v2 filesystem
+ * (Andreas Schwab, schwab@issan.informatik.uni-dortmund.de)
+ *
+ * 10.12.94 - added test to prevent checking of mounted fs adapted
+ * from Theodore Ts'o's (tytso@athena.mit.edu) e2fsck
+ * program. (Daniel Quinlan, quinlan@yggdrasil.com)
+ *
+ * 01.07.96 - Fixed the v2 fs stuff to use the right #defines and such
+ * for modern libcs (janl@math.uio.no, Nicolai Langfeldt)
+ *
+ * 02.07.96 - Added C bit fiddling routines from rmk@ecs.soton.ac.uk
+ * (Russell King). He made them for ARM. It would seem
+ * that the ARM is powerful enough to do this in C whereas
+ * i386 and m64k must use assembly to get it fast >:-)
+ * This should make minix fsck system-independent.
+ * (janl@math.uio.no, Nicolai Langfeldt)
+ *
+ * 04.11.96 - Added minor fixes from Andreas Schwab to avoid compiler
+ * warnings. Added mc68k bitops from
+ * Joerg Dorchain <dorchain@mpi-sb.mpg.de>.
+ *
+ * 06.11.96 - Added v2 code submitted by Joerg Dorchain, but written by
+ * Andreas Schwab.
+ *
+ * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@misiek.eu.org>
+ * - added Native Language Support
+ *
+ *
+ * I've had no time to add comments - hopefully the function names
+ * are comments enough. As with all file system checkers, this assumes
+ * the file system is quiescent - don't use it on a mounted device
+ * unless you can be sure nobody is writing to it (and remember that the
+ * kernel can write to it when it searches for files).
+ *
+ * Usage: fsck [-larvsm] device
+ * -l for a listing of all the filenames
+ * -a for automatic repairs (not implemented)
+ * -r for repairs (interactive) (not implemented)
+ * -v for verbose (tells how many files)
+ * -s for super-block info
+ * -m for minix-like "mode not cleared" warnings
+ * -f force filesystem check even if filesystem marked as valid
+ *
+ * The device may be a block device or a image of one, but this isn't
+ * enforced (but it's not much fun on a character device :-).
+ */
+
+#include "busybox.h"
+#include <mntent.h>
+
+#include "minix.h"
+
+#ifndef BLKGETSIZE
+#define BLKGETSIZE _IO(0x12,96) /* return device size */
+#endif
+
+#ifdef UNUSED
+enum {
+ MINIX1_LINK_MAX = 250,
+ MINIX2_LINK_MAX = 65530,
+ MINIX_I_MAP_SLOTS = 8,
+ MINIX_Z_MAP_SLOTS = 64,
+ MINIX_V1 = 0x0001, /* original minix fs */
+ MINIX_V2 = 0x0002, /* minix V2 fs */
+ NAME_MAX = 255, /* # chars in a file name */
+};
+#endif
+
+#if ENABLE_FEATURE_MINIX2
+static smallint version2;
+#else
+enum { version2 = 0 };
+#endif
+
+#define PROGRAM_VERSION "1.2 - 11/11/96"
+static smallint repair, automatic, verbose, list, show, warn_mode, force;
+static smallint changed; /* is filesystem modified? */
+static smallint errors_uncorrected; /* flag if some error was not corrected */
+
+static smallint termios_set;
+static struct termios termios;
+
+static char *device_name;
+static int IN;
+static int directory, regular, blockdev, chardev, links, symlinks, total;
+
+//also smallint?
+static int dirsize = 16;
+static int namelen = 14;
+
+static char *inode_buffer;
+
+static struct {
+ char super_block_buffer[BLOCK_SIZE];
+ char add_zone_ind_blk[BLOCK_SIZE];
+ char add_zone_dind_blk[BLOCK_SIZE];
+ USE_FEATURE_MINIX2(char add_zone_tind_blk[BLOCK_SIZE];)
+ char check_file_blk[BLOCK_SIZE];
+} *blockbuf;
+
+#define Inode1 (((struct minix1_inode *) inode_buffer)-1)
+#define Inode2 (((struct minix2_inode *) inode_buffer)-1)
+
+#define Super (*(struct minix_super_block *)(blockbuf->super_block_buffer))
+
+#if ENABLE_FEATURE_MINIX2
+# define ZONES ((unsigned)(version2 ? Super.s_zones : Super.s_nzones))
+#else
+# define ZONES ((unsigned)(Super.s_nzones))
+#endif
+#define INODES ((unsigned)Super.s_ninodes)
+#define IMAPS ((unsigned)Super.s_imap_blocks)
+#define ZMAPS ((unsigned)Super.s_zmap_blocks)
+#define FIRSTZONE ((unsigned)Super.s_firstdatazone)
+#define ZONESIZE ((unsigned)Super.s_log_zone_size)
+#define MAXSIZE ((unsigned)Super.s_max_size)
+#define MAGIC (Super.s_magic)
+
+/* gcc likes this more (code is smaller) than macro variant */
+static ATTRIBUTE_ALWAYS_INLINE unsigned div_roundup(unsigned size, unsigned n)
+{
+ return (size + n-1) / n;
+}
+
+#if ENABLE_FEATURE_MINIX2
+#define INODE_BLOCKS div_roundup(INODES, (version2 ? MINIX2_INODES_PER_BLOCK \
+ : MINIX1_INODES_PER_BLOCK))
+#else
+#define INODE_BLOCKS div_roundup(INODES, MINIX1_INODES_PER_BLOCK)
+#endif
+
+#define INODE_BUFFER_SIZE (INODE_BLOCKS * BLOCK_SIZE)
+#define NORM_FIRSTZONE (2 + IMAPS + ZMAPS + INODE_BLOCKS)
+
+static char *inode_map;
+static char *zone_map;
+
+static unsigned char *inode_count;
+static unsigned char *zone_count;
+
+/* Before you ask "where they come from?": */
+/* setbit/clrbit are supplied by sys/param.h */
+
+static int minix_bit(const char *a, unsigned i)
+{
+ return (a[i >> 3] & (1<<(i & 7)));
+}
+
+static void minix_setbit(char *a, unsigned i)
+{
+ setbit(a, i);
+ changed = 1;
+}
+static void minix_clrbit(char *a, unsigned i)
+{
+ clrbit(a, i);
+ changed = 1;
+}
+
+/* Note: do not assume 0/1, it is 0/nonzero */
+#define zone_in_use(x) (minix_bit(zone_map,(x)-FIRSTZONE+1))
+#define inode_in_use(x) (minix_bit(inode_map,(x)))
+
+#define mark_inode(x) (minix_setbit(inode_map,(x)))
+#define unmark_inode(x) (minix_clrbit(inode_map,(x)))
+
+#define mark_zone(x) (minix_setbit(zone_map,(x)-FIRSTZONE+1))
+#define unmark_zone(x) (minix_clrbit(zone_map,(x)-FIRSTZONE+1))
+
+
+static void recursive_check(unsigned ino);
+#if ENABLE_FEATURE_MINIX2
+static void recursive_check2(unsigned ino);
+#endif
+
+static void die(const char *str) ATTRIBUTE_NORETURN;
+static void die(const char *str)
+{
+ if (termios_set)
+ tcsetattr(0, TCSANOW, &termios);
+ bb_error_msg_and_die("%s", str);
+}
+
+/* File-name data */
+enum { MAX_DEPTH = 32 };
+static int name_depth;
+static char *current_name;
+static char *name_component[MAX_DEPTH+1];
+
+/* Wed Feb 9 15:17:06 MST 2000 */
+/* dynamically allocate name_list (instead of making it static) */
+static void alloc_current_name(void)
+{
+ current_name = xmalloc(MAX_DEPTH * (BUFSIZ + 1));
+ current_name[0] = '/';
+ current_name[1] = '\0';
+ name_component[0] = &current_name[0];
+}
+
+#if ENABLE_FEATURE_CLEAN_UP
+/* execute this atexit() to deallocate name_list[] */
+/* piptigger was here */
+static void free_current_name(void)
+{
+ free(current_name);
+}
+#endif
+
+static void push_filename(const char *name)
+{
+ // /dir/dir/dir/file
+ // ^ ^ ^
+ // [0] [1] [2] <-name_component[i]
+ if (name_depth < MAX_DEPTH) {
+ int len;
+ char *p = name_component[name_depth];
+ *p++ = '/';
+ len = sprintf(p, "%.*s", namelen, name);
+ name_component[name_depth + 1] = p + len;
+ }
+ name_depth++;
+}
+
+static void pop_filename(void)
+{
+ name_depth--;
+ if (name_depth < MAX_DEPTH) {
+ *name_component[name_depth] = '\0';
+ if (!name_depth) {
+ current_name[0] = '/';
+ current_name[1] = '\0';
+ }
+ }
+}
+
+static int ask(const char *string, int def)
+{
+ int c;
+
+ if (!repair) {
+ puts("");
+ errors_uncorrected = 1;
+ return 0;
+ }
+ if (automatic) {
+ puts("");
+ if (!def)
+ errors_uncorrected = 1;
+ return def;
+ }
+ printf(def ? "%s (y/n)? " : "%s (n/y)? ", string);
+ for (;;) {
+ fflush(stdout);
+ c = getchar();
+ if (c == EOF) {
+ if (!def)
+ errors_uncorrected = 1;
+ return def;
+ }
+ c = toupper(c);
+ if (c == 'Y') {
+ def = 1;
+ break;
+ } else if (c == 'N') {
+ def = 0;
+ break;
+ } else if (c == ' ' || c == '\n')
+ break;
+ }
+ if (def)
+ printf("y\n");
+ else {
+ printf("n\n");
+ errors_uncorrected = 1;
+ }
+ return def;
+}
+
+/*
+ * Make certain that we aren't checking a filesystem that is on a
+ * mounted partition. Code adapted from e2fsck, Copyright (C) 1993,
+ * 1994 Theodore Ts'o. Also licensed under GPL.
+ */
+static void check_mount(void)
+{
+ FILE *f;
+ struct mntent *mnt;
+ int cont;
+ int fd;
+
+ f = setmntent(MOUNTED, "r");
+ if (f == NULL)
+ return;
+ while ((mnt = getmntent(f)) != NULL)
+ if (strcmp(device_name, mnt->mnt_fsname) == 0)
+ break;
+ endmntent(f);
+ if (!mnt)
+ return;
+
+ /*
+ * If the root is mounted read-only, then /etc/mtab is
+ * probably not correct; so we won't issue a warning based on
+ * it.
+ */
+ fd = open(MOUNTED, O_RDWR);
+ if (fd < 0 && errno == EROFS)
+ return;
+ close(fd);
+
+ printf("%s is mounted. ", device_name);
+ cont = 0;
+ if (isatty(0) && isatty(1))
+ cont = ask("Do you really want to continue", 0);
+ if (!cont) {
+ printf("Check aborted\n");
+ exit(0);
+ }
+}
+
+/*
+ * check_zone_nr checks to see that *nr is a valid zone nr. If it
+ * isn't, it will possibly be repaired. Check_zone_nr sets *corrected
+ * if an error was corrected, and returns the zone (0 for no zone
+ * or a bad zone-number).
+ */
+static int check_zone_nr2(uint32_t *nr, smallint *corrected)
+{
+ const char *msg;
+ if (!*nr)
+ return 0;
+ if (*nr < FIRSTZONE)
+ msg = "< FIRSTZONE";
+ else if (*nr >= ZONES)
+ msg = ">= ZONES";
+ else
+ return *nr;
+ printf("Zone nr %s in file '%s'. ", msg, current_name);
+ if (ask("Remove block", 1)) {
+ *nr = 0;
+ *corrected = 1;
+ }
+ return 0;
+}
+
+static int check_zone_nr(uint16_t *nr, smallint *corrected)
+{
+ uint32_t nr32 = *nr;
+ int r = check_zone_nr2(&nr32, corrected);
+ *nr = (uint16_t)nr32;
+ return r;
+}
+
+/*
+ * read-block reads block nr into the buffer at addr.
+ */
+static void read_block(unsigned nr, char *addr)
+{
+ if (!nr) {
+ memset(addr, 0, BLOCK_SIZE);
+ return;
+ }
+ if (BLOCK_SIZE * nr != lseek(IN, BLOCK_SIZE * nr, SEEK_SET)) {
+ printf("%s: cannot seek to block in file '%s'\n",
+ bb_msg_read_error, current_name);
+ errors_uncorrected = 1;
+ memset(addr, 0, BLOCK_SIZE);
+ } else if (BLOCK_SIZE != read(IN, addr, BLOCK_SIZE)) {
+ printf("%s: bad block in file '%s'\n",
+ bb_msg_read_error, current_name);
+ errors_uncorrected = 1;
+ memset(addr, 0, BLOCK_SIZE);
+ }
+}
+
+/*
+ * write_block writes block nr to disk.
+ */
+static void write_block(unsigned nr, char *addr)
+{
+ if (!nr)
+ return;
+ if (nr < FIRSTZONE || nr >= ZONES) {
+ printf("Internal error: trying to write bad block\n"
+ "Write request ignored\n");
+ errors_uncorrected = 1;
+ return;
+ }
+ if (BLOCK_SIZE * nr != lseek(IN, BLOCK_SIZE * nr, SEEK_SET))
+ die("seek failed in write_block");
+ if (BLOCK_SIZE != write(IN, addr, BLOCK_SIZE)) {
+ printf("%s: bad block in file '%s'\n",
+ bb_msg_write_error, current_name);
+ errors_uncorrected = 1;
+ }
+}
+
+/*
+ * map_block calculates the absolute block nr of a block in a file.
+ * It sets 'changed' if the inode has needed changing, and re-writes
+ * any indirect blocks with errors.
+ */
+static int map_block(struct minix1_inode *inode, unsigned blknr)
+{
+ uint16_t ind[BLOCK_SIZE >> 1];
+ uint16_t dind[BLOCK_SIZE >> 1];
+ int block, result;
+ smallint blk_chg;
+
+ if (blknr < 7)
+ return check_zone_nr(inode->i_zone + blknr, &changed);
+ blknr -= 7;
+ if (blknr < 512) {
+ block = check_zone_nr(inode->i_zone + 7, &changed);
+ read_block(block, (char *) ind);
+ blk_chg = 0;
+ result = check_zone_nr(blknr + ind, &blk_chg);
+ if (blk_chg)
+ write_block(block, (char *) ind);
+ return result;
+ }
+ blknr -= 512;
+ block = check_zone_nr(inode->i_zone + 8, &changed);
+ read_block(block, (char *) dind);
+ blk_chg = 0;
+ result = check_zone_nr(dind + (blknr / 512), &blk_chg);
+ if (blk_chg)
+ write_block(block, (char *) dind);
+ block = result;
+ read_block(block, (char *) ind);
+ blk_chg = 0;
+ result = check_zone_nr(ind + (blknr % 512), &blk_chg);
+ if (blk_chg)
+ write_block(block, (char *) ind);
+ return result;
+}
+
+#if ENABLE_FEATURE_MINIX2
+static int map_block2(struct minix2_inode *inode, unsigned blknr)
+{
+ uint32_t ind[BLOCK_SIZE >> 2];
+ uint32_t dind[BLOCK_SIZE >> 2];
+ uint32_t tind[BLOCK_SIZE >> 2];
+ int block, result;
+ smallint blk_chg;
+
+ if (blknr < 7)
+ return check_zone_nr2(inode->i_zone + blknr, &changed);
+ blknr -= 7;
+ if (blknr < 256) {
+ block = check_zone_nr2(inode->i_zone + 7, &changed);
+ read_block(block, (char *) ind);
+ blk_chg = 0;
+ result = check_zone_nr2(blknr + ind, &blk_chg);
+ if (blk_chg)
+ write_block(block, (char *) ind);
+ return result;
+ }
+ blknr -= 256;
+ if (blknr >= 256 * 256) {
+ block = check_zone_nr2(inode->i_zone + 8, &changed);
+ read_block(block, (char *) dind);
+ blk_chg = 0;
+ result = check_zone_nr2(dind + blknr / 256, &blk_chg);
+ if (blk_chg)
+ write_block(block, (char *) dind);
+ block = result;
+ read_block(block, (char *) ind);
+ blk_chg = 0;
+ result = check_zone_nr2(ind + blknr % 256, &blk_chg);
+ if (blk_chg)
+ write_block(block, (char *) ind);
+ return result;
+ }
+ blknr -= 256 * 256;
+ block = check_zone_nr2(inode->i_zone + 9, &changed);
+ read_block(block, (char *) tind);
+ blk_chg = 0;
+ result = check_zone_nr2(tind + blknr / (256 * 256), &blk_chg);
+ if (blk_chg)
+ write_block(block, (char *) tind);
+ block = result;
+ read_block(block, (char *) dind);
+ blk_chg = 0;
+ result = check_zone_nr2(dind + (blknr / 256) % 256, &blk_chg);
+ if (blk_chg)
+ write_block(block, (char *) dind);
+ block = result;
+ read_block(block, (char *) ind);
+ blk_chg = 0;
+ result = check_zone_nr2(ind + blknr % 256, &blk_chg);
+ if (blk_chg)
+ write_block(block, (char *) ind);
+ return result;
+}
+#endif
+
+static void write_super_block(void)
+{
+ /*
+ * Set the state of the filesystem based on whether or not there
+ * are uncorrected errors. The filesystem valid flag is
+ * unconditionally set if we get this far.
+ */
+ Super.s_state |= MINIX_VALID_FS | MINIX_ERROR_FS;
+ if (!errors_uncorrected)
+ Super.s_state &= ~MINIX_ERROR_FS;
+
+ if (BLOCK_SIZE != lseek(IN, BLOCK_SIZE, SEEK_SET))
+ die("seek failed in write_super_block");
+ if (BLOCK_SIZE != write(IN, blockbuf->super_block_buffer, BLOCK_SIZE))
+ die("cannot write super-block");
+}
+
+static void write_tables(void)
+{
+ write_super_block();
+
+ if (IMAPS * BLOCK_SIZE != write(IN, inode_map, IMAPS * BLOCK_SIZE))
+ die("cannot write inode map");
+ if (ZMAPS * BLOCK_SIZE != write(IN, zone_map, ZMAPS * BLOCK_SIZE))
+ die("cannot write zone map");
+ if (INODE_BUFFER_SIZE != write(IN, inode_buffer, INODE_BUFFER_SIZE))
+ die("cannot write inodes");
+}
+
+static void get_dirsize(void)
+{
+ int block;
+ char blk[BLOCK_SIZE];
+ int size;
+
+#if ENABLE_FEATURE_MINIX2
+ if (version2)
+ block = Inode2[MINIX_ROOT_INO].i_zone[0];
+ else
+#endif
+ block = Inode1[MINIX_ROOT_INO].i_zone[0];
+ read_block(block, blk);
+ for (size = 16; size < BLOCK_SIZE; size <<= 1) {
+ if (strcmp(blk + size + 2, "..") == 0) {
+ dirsize = size;
+ namelen = size - 2;
+ return;
+ }
+ }
+ /* use defaults */
+}
+
+static void read_superblock(void)
+{
+ if (BLOCK_SIZE != lseek(IN, BLOCK_SIZE, SEEK_SET))
+ die("seek failed");
+ if (BLOCK_SIZE != read(IN, blockbuf->super_block_buffer, BLOCK_SIZE))
+ die("cannot read super block");
+ /* already initialized to:
+ namelen = 14;
+ dirsize = 16;
+ version2 = 0;
+ */
+ if (MAGIC == MINIX1_SUPER_MAGIC) {
+ } else if (MAGIC == MINIX1_SUPER_MAGIC2) {
+ namelen = 30;
+ dirsize = 32;
+#if ENABLE_FEATURE_MINIX2
+ } else if (MAGIC == MINIX2_SUPER_MAGIC) {
+ version2 = 1;
+ } else if (MAGIC == MINIX2_SUPER_MAGIC2) {
+ namelen = 30;
+ dirsize = 32;
+ version2 = 1;
+#endif
+ } else
+ die("bad magic number in super-block");
+ if (ZONESIZE != 0 || BLOCK_SIZE != 1024)
+ die("only 1k blocks/zones supported");
+ if (IMAPS * BLOCK_SIZE * 8 < INODES + 1)
+ die("bad s_imap_blocks field in super-block");
+ if (ZMAPS * BLOCK_SIZE * 8 < ZONES - FIRSTZONE + 1)
+ die("bad s_zmap_blocks field in super-block");
+}
+
+static void read_tables(void)
+{
+ inode_map = xzalloc(IMAPS * BLOCK_SIZE);
+ zone_map = xzalloc(ZMAPS * BLOCK_SIZE);
+ inode_buffer = xmalloc(INODE_BUFFER_SIZE);
+ inode_count = xmalloc(INODES + 1);
+ zone_count = xmalloc(ZONES);
+ if (IMAPS * BLOCK_SIZE != read(IN, inode_map, IMAPS * BLOCK_SIZE))
+ die("cannot read inode map");
+ if (ZMAPS * BLOCK_SIZE != read(IN, zone_map, ZMAPS * BLOCK_SIZE))
+ die("cannot read zone map");
+ if (INODE_BUFFER_SIZE != read(IN, inode_buffer, INODE_BUFFER_SIZE))
+ die("cannot read inodes");
+ if (NORM_FIRSTZONE != FIRSTZONE) {
+ printf("warning: firstzone!=norm_firstzone\n");
+ errors_uncorrected = 1;
+ }
+ get_dirsize();
+ if (show) {
+ printf("%u inodes\n"
+ "%u blocks\n"
+ "Firstdatazone=%u (%u)\n"
+ "Zonesize=%u\n"
+ "Maxsize=%u\n"
+ "Filesystem state=%u\n"
+ "namelen=%u\n\n",
+ INODES,
+ ZONES,
+ FIRSTZONE, NORM_FIRSTZONE,
+ BLOCK_SIZE << ZONESIZE,
+ MAXSIZE,
+ Super.s_state,
+ namelen);
+ }
+}
+
+static struct minix1_inode *get_inode(unsigned nr)
+{
+ struct minix1_inode *inode;
+
+ if (!nr || nr > INODES)
+ return NULL;
+ total++;
+ inode = Inode1 + nr;
+ if (!inode_count[nr]) {
+ if (!inode_in_use(nr)) {
+ printf("Inode %d is marked as 'unused', but it is used "
+ "for file '%s'\n", nr, current_name);
+ if (repair) {
+ if (ask("Mark as 'in use'", 1))
+ mark_inode(nr);
+ else
+ errors_uncorrected = 1;
+ }
+ }
+ if (S_ISDIR(inode->i_mode))
+ directory++;
+ else if (S_ISREG(inode->i_mode))
+ regular++;
+ else if (S_ISCHR(inode->i_mode))
+ chardev++;
+ else if (S_ISBLK(inode->i_mode))
+ blockdev++;
+ else if (S_ISLNK(inode->i_mode))
+ symlinks++;
+ else if (S_ISSOCK(inode->i_mode));
+ else if (S_ISFIFO(inode->i_mode));
+ else {
+ printf("%s has mode %05o\n", current_name, inode->i_mode);
+ }
+
+ } else
+ links++;
+ if (!++inode_count[nr]) {
+ printf("Warning: inode count too big\n");
+ inode_count[nr]--;
+ errors_uncorrected = 1;
+ }
+ return inode;
+}
+
+#if ENABLE_FEATURE_MINIX2
+static struct minix2_inode *get_inode2(unsigned nr)
+{
+ struct minix2_inode *inode;
+
+ if (!nr || nr > INODES)
+ return NULL;
+ total++;
+ inode = Inode2 + nr;
+ if (!inode_count[nr]) {
+ if (!inode_in_use(nr)) {
+ printf("Inode %d is marked as 'unused', but it is used "
+ "for file '%s'\n", nr, current_name);
+ if (repair) {
+ if (ask("Mark as 'in use'", 1))
+ mark_inode(nr);
+ else
+ errors_uncorrected = 1;
+ }
+ }
+ if (S_ISDIR(inode->i_mode))
+ directory++;
+ else if (S_ISREG(inode->i_mode))
+ regular++;
+ else if (S_ISCHR(inode->i_mode))
+ chardev++;
+ else if (S_ISBLK(inode->i_mode))
+ blockdev++;
+ else if (S_ISLNK(inode->i_mode))
+ symlinks++;
+ else if (S_ISSOCK(inode->i_mode));
+ else if (S_ISFIFO(inode->i_mode));
+ else {
+ printf("%s has mode %05o\n", current_name, inode->i_mode);
+ }
+ } else
+ links++;
+ if (!++inode_count[nr]) {
+ printf("Warning: inode count too big\n");
+ inode_count[nr]--;
+ errors_uncorrected = 1;
+ }
+ return inode;
+}
+#endif
+
+static void check_root(void)
+{
+ struct minix1_inode *inode = Inode1 + MINIX_ROOT_INO;
+
+ if (!inode || !S_ISDIR(inode->i_mode))
+ die("root inode isn't a directory");
+}
+
+#if ENABLE_FEATURE_MINIX2
+static void check_root2(void)
+{
+ struct minix2_inode *inode = Inode2 + MINIX_ROOT_INO;
+
+ if (!inode || !S_ISDIR(inode->i_mode))
+ die("root inode isn't a directory");
+}
+#else
+void check_root2(void);
+#endif
+
+static int add_zone(uint16_t *znr, smallint *corrected)
+{
+ int result;
+ int block;
+
+ result = 0;
+ block = check_zone_nr(znr, corrected);
+ if (!block)
+ return 0;
+ if (zone_count[block]) {
+ printf("Already used block is reused in file '%s'. ",
+ current_name);
+ if (ask("Clear", 1)) {
+ *znr = 0;
+ block = 0;
+ *corrected = 1;
+ return 0;
+ }
+ }
+ if (!zone_in_use(block)) {
+ printf("Block %d in file '%s' is marked as 'unused'. ",
+ block, current_name);
+ if (ask("Correct", 1))
+ mark_zone(block);
+ }
+ if (!++zone_count[block])
+ zone_count[block]--;
+ return block;
+}
+
+#if ENABLE_FEATURE_MINIX2
+static int add_zone2(uint32_t *znr, smallint *corrected)
+{
+ int result;
+ int block;
+
+ result = 0;
+ block = check_zone_nr2(znr, corrected);
+ if (!block)
+ return 0;
+ if (zone_count[block]) {
+ printf("Already used block is reused in file '%s'. ",
+ current_name);
+ if (ask("Clear", 1)) {
+ *znr = 0;
+ block = 0;
+ *corrected = 1;
+ return 0;
+ }
+ }
+ if (!zone_in_use(block)) {
+ printf("Block %d in file '%s' is marked as 'unused'. ",
+ block, current_name);
+ if (ask("Correct", 1))
+ mark_zone(block);
+ }
+ if (!++zone_count[block])
+ zone_count[block]--;
+ return block;
+}
+#endif
+
+static void add_zone_ind(uint16_t *znr, smallint *corrected)
+{
+#define blk (blockbuf->add_zone_ind_blk)
+ int i;
+ int block;
+ smallint chg_blk = 0;
+
+ block = add_zone(znr, corrected);
+ if (!block)
+ return;
+ read_block(block, blk);
+ for (i = 0; i < (BLOCK_SIZE >> 1); i++)
+ add_zone(i + (uint16_t *) blk, &chg_blk);
+ if (chg_blk)
+ write_block(block, blk);
+#undef blk
+}
+
+#if ENABLE_FEATURE_MINIX2
+static void add_zone_ind2(uint32_t *znr, smallint *corrected)
+{
+#define blk (blockbuf->add_zone_ind_blk)
+ int i;
+ int block;
+ smallint chg_blk = 0;
+
+ block = add_zone2(znr, corrected);
+ if (!block)
+ return;
+ read_block(block, blk);
+ for (i = 0; i < BLOCK_SIZE >> 2; i++)
+ add_zone2(i + (uint32_t *) blk, &chg_blk);
+ if (chg_blk)
+ write_block(block, blk);
+#undef blk
+}
+#endif
+
+static void add_zone_dind(uint16_t *znr, smallint *corrected)
+{
+#define blk (blockbuf->add_zone_dind_blk)
+ int i;
+ int block;
+ smallint chg_blk = 0;
+
+ block = add_zone(znr, corrected);
+ if (!block)
+ return;
+ read_block(block, blk);
+ for (i = 0; i < (BLOCK_SIZE >> 1); i++)
+ add_zone_ind(i + (uint16_t *) blk, &chg_blk);
+ if (chg_blk)
+ write_block(block, blk);
+#undef blk
+}
+
+#if ENABLE_FEATURE_MINIX2
+static void add_zone_dind2(uint32_t *znr, smallint *corrected)
+{
+#define blk (blockbuf->add_zone_dind_blk)
+ int i;
+ int block;
+ smallint chg_blk = 0;
+
+ block = add_zone2(znr, corrected);
+ if (!block)
+ return;
+ read_block(block, blk);
+ for (i = 0; i < BLOCK_SIZE >> 2; i++)
+ add_zone_ind2(i + (uint32_t *) blk, &chg_blk);
+ if (chg_blk)
+ write_block(block, blk);
+#undef blk
+}
+
+static void add_zone_tind2(uint32_t *znr, smallint *corrected)
+{
+#define blk (blockbuf->add_zone_tind_blk)
+ int i;
+ int block;
+ smallint chg_blk = 0;
+
+ block = add_zone2(znr, corrected);
+ if (!block)
+ return;
+ read_block(block, blk);
+ for (i = 0; i < BLOCK_SIZE >> 2; i++)
+ add_zone_dind2(i + (uint32_t *) blk, &chg_blk);
+ if (chg_blk)
+ write_block(block, blk);
+#undef blk
+}
+#endif
+
+static void check_zones(unsigned i)
+{
+ struct minix1_inode *inode;
+
+ if (!i || i > INODES)
+ return;
+ if (inode_count[i] > 1) /* have we counted this file already? */
+ return;
+ inode = Inode1 + i;
+ if (!S_ISDIR(inode->i_mode) && !S_ISREG(inode->i_mode) &&
+ !S_ISLNK(inode->i_mode)) return;
+ for (i = 0; i < 7; i++)
+ add_zone(i + inode->i_zone, &changed);
+ add_zone_ind(7 + inode->i_zone, &changed);
+ add_zone_dind(8 + inode->i_zone, &changed);
+}
+
+#if ENABLE_FEATURE_MINIX2
+static void check_zones2(unsigned i)
+{
+ struct minix2_inode *inode;
+
+ if (!i || i > INODES)
+ return;
+ if (inode_count[i] > 1) /* have we counted this file already? */
+ return;
+ inode = Inode2 + i;
+ if (!S_ISDIR(inode->i_mode) && !S_ISREG(inode->i_mode)
+ && !S_ISLNK(inode->i_mode))
+ return;
+ for (i = 0; i < 7; i++)
+ add_zone2(i + inode->i_zone, &changed);
+ add_zone_ind2(7 + inode->i_zone, &changed);
+ add_zone_dind2(8 + inode->i_zone, &changed);
+ add_zone_tind2(9 + inode->i_zone, &changed);
+}
+#endif
+
+static void check_file(struct minix1_inode *dir, unsigned offset)
+{
+#define blk (blockbuf->check_file_blk)
+ struct minix1_inode *inode;
+ int ino;
+ char *name;
+ int block;
+
+ block = map_block(dir, offset / BLOCK_SIZE);
+ read_block(block, blk);
+ name = blk + (offset % BLOCK_SIZE) + 2;
+ ino = *(uint16_t *) (name - 2);
+ if (ino > INODES) {
+ printf("%s contains a bad inode number for file '%.*s'. ",
+ current_name, namelen, name);
+ if (ask("Remove", 1)) {
+ *(uint16_t *) (name - 2) = 0;
+ write_block(block, blk);
+ }
+ ino = 0;
+ }
+ push_filename(name);
+ inode = get_inode(ino);
+ pop_filename();
+ if (!offset) {
+ if (inode && LONE_CHAR(name, '.'))
+ return;
+ printf("%s: bad directory: '.' isn't first\n", current_name);
+ errors_uncorrected = 1;
+ }
+ if (offset == dirsize) {
+ if (inode && strcmp("..", name) == 0)
+ return;
+ printf("%s: bad directory: '..' isn't second\n", current_name);
+ errors_uncorrected = 1;
+ }
+ if (!inode)
+ return;
+ push_filename(name);
+ if (list) {
+ if (verbose)
+ printf("%6d %07o %3d ", ino, inode->i_mode, inode->i_nlinks);
+ printf("%s%s\n", current_name, S_ISDIR(inode->i_mode) ? ":" : "");
+ }
+ check_zones(ino);
+ if (inode && S_ISDIR(inode->i_mode))
+ recursive_check(ino);
+ pop_filename();
+#undef blk
+}
+
+#if ENABLE_FEATURE_MINIX2
+static void check_file2(struct minix2_inode *dir, unsigned offset)
+{
+#define blk (blockbuf->check_file_blk)
+ struct minix2_inode *inode;
+ int ino;
+ char *name;
+ int block;
+
+ block = map_block2(dir, offset / BLOCK_SIZE);
+ read_block(block, blk);
+ name = blk + (offset % BLOCK_SIZE) + 2;
+ ino = *(uint16_t *) (name - 2);
+ if (ino > INODES) {
+ printf("%s contains a bad inode number for file '%.*s'. ",
+ current_name, namelen, name);
+ if (ask("Remove", 1)) {
+ *(uint16_t *) (name - 2) = 0;
+ write_block(block, blk);
+ }
+ ino = 0;
+ }
+ push_filename(name);
+ inode = get_inode2(ino);
+ pop_filename();
+ if (!offset) {
+ if (inode && LONE_CHAR(name, '.'))
+ return;
+ printf("%s: bad directory: '.' isn't first\n", current_name);
+ errors_uncorrected = 1;
+ }
+ if (offset == dirsize) {
+ if (inode && strcmp("..", name) == 0)
+ return;
+ printf("%s: bad directory: '..' isn't second\n", current_name);
+ errors_uncorrected = 1;
+ }
+ if (!inode)
+ return;
+ push_filename(name);
+ if (list) {
+ if (verbose)
+ printf("%6d %07o %3d ", ino, inode->i_mode, inode->i_nlinks);
+ printf("%s%s\n", current_name, S_ISDIR(inode->i_mode) ? ":" : "");
+ }
+ check_zones2(ino);
+ if (inode && S_ISDIR(inode->i_mode))
+ recursive_check2(ino);
+ pop_filename();
+#undef blk
+}
+#endif
+
+static void recursive_check(unsigned ino)
+{
+ struct minix1_inode *dir;
+ unsigned offset;
+
+ dir = Inode1 + ino;
+ if (!S_ISDIR(dir->i_mode))
+ die("internal error");
+ if (dir->i_size < 2 * dirsize) {
+ printf("%s: bad directory: size<32", current_name);
+ errors_uncorrected = 1;
+ }
+ for (offset = 0; offset < dir->i_size; offset += dirsize)
+ check_file(dir, offset);
+}
+
+#if ENABLE_FEATURE_MINIX2
+static void recursive_check2(unsigned ino)
+{
+ struct minix2_inode *dir;
+ unsigned offset;
+
+ dir = Inode2 + ino;
+ if (!S_ISDIR(dir->i_mode))
+ die("internal error");
+ if (dir->i_size < 2 * dirsize) {
+ printf("%s: bad directory: size<32", current_name);
+ errors_uncorrected = 1;
+ }
+ for (offset = 0; offset < dir->i_size; offset += dirsize)
+ check_file2(dir, offset);
+}
+#endif
+
+static int bad_zone(int i)
+{
+ char buffer[BLOCK_SIZE];
+
+ if (BLOCK_SIZE * i != lseek(IN, BLOCK_SIZE * i, SEEK_SET))
+ die("seek failed in bad_zone");
+ return (BLOCK_SIZE != read(IN, buffer, BLOCK_SIZE));
+}
+
+static void check_counts(void)
+{
+ int i;
+
+ for (i = 1; i <= INODES; i++) {
+ if (warn_mode && Inode1[i].i_mode && !inode_in_use(i)) {
+ printf("Inode %d has non-zero mode. ", i);
+ if (ask("Clear", 1)) {
+ Inode1[i].i_mode = 0;
+ changed = 1;
+ }
+ }
+ if (!inode_count[i]) {
+ if (!inode_in_use(i))
+ continue;
+ printf("Unused inode %d is marked as 'used' in the bitmap. ", i);
+ if (ask("Clear", 1))
+ unmark_inode(i);
+ continue;
+ }
+ if (!inode_in_use(i)) {
+ printf("Inode %d is used, but marked as 'unused' in the bitmap. ", i);
+ if (ask("Set", 1))
+ mark_inode(i);
+ }
+ if (Inode1[i].i_nlinks != inode_count[i]) {
+ printf("Inode %d (mode=%07o), i_nlinks=%d, counted=%d. ",
+ i, Inode1[i].i_mode, Inode1[i].i_nlinks,
+ inode_count[i]);
+ if (ask("Set i_nlinks to count", 1)) {
+ Inode1[i].i_nlinks = inode_count[i];
+ changed = 1;
+ }
+ }
+ }
+ for (i = FIRSTZONE; i < ZONES; i++) {
+ if ((zone_in_use(i) != 0) == zone_count[i])
+ continue;
+ if (!zone_count[i]) {
+ if (bad_zone(i))
+ continue;
+ printf("Zone %d is marked 'in use', but no file uses it. ", i);
+ if (ask("Unmark", 1))
+ unmark_zone(i);
+ continue;
+ }
+ printf("Zone %d: %sin use, counted=%d\n",
+ i, zone_in_use(i) ? "" : "not ", zone_count[i]);
+ }
+}
+
+#if ENABLE_FEATURE_MINIX2
+static void check_counts2(void)
+{
+ int i;
+
+ for (i = 1; i <= INODES; i++) {
+ if (warn_mode && Inode2[i].i_mode && !inode_in_use(i)) {
+ printf("Inode %d has non-zero mode. ", i);
+ if (ask("Clear", 1)) {
+ Inode2[i].i_mode = 0;
+ changed = 1;
+ }
+ }
+ if (!inode_count[i]) {
+ if (!inode_in_use(i))
+ continue;
+ printf("Unused inode %d is marked as 'used' in the bitmap. ", i);
+ if (ask("Clear", 1))
+ unmark_inode(i);
+ continue;
+ }
+ if (!inode_in_use(i)) {
+ printf("Inode %d is used, but marked as 'unused' in the bitmap. ", i);
+ if (ask("Set", 1))
+ mark_inode(i);
+ }
+ if (Inode2[i].i_nlinks != inode_count[i]) {
+ printf("Inode %d (mode=%07o), i_nlinks=%d, counted=%d. ",
+ i, Inode2[i].i_mode, Inode2[i].i_nlinks,
+ inode_count[i]);
+ if (ask("Set i_nlinks to count", 1)) {
+ Inode2[i].i_nlinks = inode_count[i];
+ changed = 1;
+ }
+ }
+ }
+ for (i = FIRSTZONE; i < ZONES; i++) {
+ if ((zone_in_use(i) != 0) == zone_count[i])
+ continue;
+ if (!zone_count[i]) {
+ if (bad_zone(i))
+ continue;
+ printf("Zone %d is marked 'in use', but no file uses it. ", i);
+ if (ask("Unmark", 1))
+ unmark_zone(i);
+ continue;
+ }
+ printf("Zone %d: %sin use, counted=%d\n",
+ i, zone_in_use(i) ? "" : "not ", zone_count[i]);
+ }
+}
+#endif
+
+static void check(void)
+{
+ memset(inode_count, 0, (INODES + 1) * sizeof(*inode_count));
+ memset(zone_count, 0, ZONES * sizeof(*zone_count));
+ check_zones(MINIX_ROOT_INO);
+ recursive_check(MINIX_ROOT_INO);
+ check_counts();
+}
+
+#if ENABLE_FEATURE_MINIX2
+static void check2(void)
+{
+ memset(inode_count, 0, (INODES + 1) * sizeof(*inode_count));
+ memset(zone_count, 0, ZONES * sizeof(*zone_count));
+ check_zones2(MINIX_ROOT_INO);
+ recursive_check2(MINIX_ROOT_INO);
+ check_counts2();
+}
+#else
+void check2(void);
+#endif
+
+int fsck_minix_main(int argc, char **argv);
+int fsck_minix_main(int argc, char **argv)
+{
+ struct termios tmp;
+ int retcode = 0;
+
+ xfunc_error_retval = 8;
+ blockbuf = xzalloc(sizeof(*blockbuf));
+
+ alloc_current_name();
+#if ENABLE_FEATURE_CLEAN_UP
+ /* Don't bother to free memory. Exit does
+ * that automagically, so we can save a few bytes */
+ atexit(free_current_name);
+#endif
+
+ if (INODE_SIZE1 * MINIX1_INODES_PER_BLOCK != BLOCK_SIZE)
+ die("bad inode size");
+#if ENABLE_FEATURE_MINIX2
+ if (INODE_SIZE2 * MINIX2_INODES_PER_BLOCK != BLOCK_SIZE)
+ die("bad v2 inode size");
+#endif
+ while (--argc != 0) {
+ argv++;
+ if (argv[0][0] != '-') {
+ if (device_name)
+ bb_show_usage();
+ device_name = argv[0];
+ } else {
+ while (*++argv[0]) {
+ switch (argv[0][0]) {
+ case 'l':
+ list = 1;
+ break;
+ case 'a':
+ automatic = 1;
+ repair = 1;
+ break;
+ case 'r':
+ automatic = 0;
+ repair = 1;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case 's':
+ show = 1;
+ break;
+ case 'm':
+ warn_mode = 1;
+ break;
+ case 'f':
+ force = 1;
+ break;
+ default:
+ bb_show_usage();
+ }
+ }
+ }
+ }
+ if (!device_name)
+ bb_show_usage();
+
+ check_mount(); /* trying to check a mounted filesystem? */
+ if (repair && !automatic) {
+ if (!isatty(0) || !isatty(1))
+ die("need terminal for interactive repairs");
+ }
+ IN = xopen(device_name, repair ? O_RDWR : O_RDONLY);
+
+ /*sync(); paranoia? */
+ read_superblock();
+
+ /*
+ * Determine whether or not we should continue with the checking.
+ * This is based on the status of the filesystem valid and error
+ * flags and whether or not the -f switch was specified on the
+ * command line.
+ */
+ printf("%s, "PROGRAM_VERSION"\n", applet_name);
+
+ if (!(Super.s_state & MINIX_ERROR_FS)
+ && (Super.s_state & MINIX_VALID_FS) && !force
+ ) {
+ if (repair)
+ printf("%s is clean, check is skipped\n", device_name);
+ return 0;
+ } else if (force)
+ printf("Forcing filesystem check on %s\n", device_name);
+ else if (repair)
+ printf("Filesystem on %s is dirty, needs checking\n",
+ device_name);
+
+ read_tables();
+
+ if (repair && !automatic) {
+ tcgetattr(0, &termios);
+ tmp = termios;
+ tmp.c_lflag &= ~(ICANON | ECHO);
+ tcsetattr(0, TCSANOW, &tmp);
+ termios_set = 1;
+ }
+
+ if (version2) {
+ check_root2();
+ check2();
+ } else {
+ check_root();
+ check();
+ }
+
+ if (verbose) {
+ int i, free_cnt;
+
+ for (i = 1, free_cnt = 0; i <= INODES; i++)
+ if (!inode_in_use(i))
+ free_cnt++;
+ printf("\n%6u inodes used (%u%%)\n", (INODES - free_cnt),
+ 100 * (INODES - free_cnt) / INODES);
+ for (i = FIRSTZONE, free_cnt = 0; i < ZONES; i++)
+ if (!zone_in_use(i))
+ free_cnt++;
+ printf("%6u zones used (%u%%)\n\n"
+ "%6u regular files\n"
+ "%6u directories\n"
+ "%6u character device files\n"
+ "%6u block device files\n"
+ "%6u links\n"
+ "%6u symbolic links\n"
+ "------\n"
+ "%6u files\n",
+ (ZONES - free_cnt), 100 * (ZONES - free_cnt) / ZONES,
+ regular, directory, chardev, blockdev,
+ links - 2 * directory + 1, symlinks,
+ total - 2 * directory + 1);
+ }
+ if (changed) {
+ write_tables();
+ printf("FILE SYSTEM HAS BEEN CHANGED\n");
+ sync();
+ } else if (repair)
+ write_super_block();
+
+ if (repair && !automatic)
+ tcsetattr(0, TCSANOW, &termios);
+
+ if (changed)
+ retcode += 3;
+ if (errors_uncorrected)
+ retcode += 4;
+ return retcode;
+}
diff --git a/i/pc104/initrd/conf/busybox/util-linux/getopt.c b/i/pc104/initrd/conf/busybox/util-linux/getopt.c
new file mode 100644
index 0000000..4861990
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/util-linux/getopt.c
@@ -0,0 +1,366 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * getopt.c - Enhanced implementation of BSD getopt(1)
+ * Copyright (c) 1997, 1998, 1999, 2000 Frodo Looijaard <frodol@dds.nl>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/*
+ * Version 1.0-b4: Tue Sep 23 1997. First public release.
+ * Version 1.0: Wed Nov 19 1997.
+ * Bumped up the version number to 1.0
+ * Fixed minor typo (CSH instead of TCSH)
+ * Version 1.0.1: Tue Jun 3 1998
+ * Fixed sizeof instead of strlen bug
+ * Bumped up the version number to 1.0.1
+ * Version 1.0.2: Thu Jun 11 1998 (not present)
+ * Fixed gcc-2.8.1 warnings
+ * Fixed --version/-V option (not present)
+ * Version 1.0.5: Tue Jun 22 1999
+ * Make -u option work (not present)
+ * Version 1.0.6: Tue Jun 27 2000
+ * No important changes
+ * Version 1.1.0: Tue Jun 30 2000
+ * Added NLS support (partly written by Arkadiusz Mi<B6>kiewicz
+ * <misiek@misiek.eu.org>)
+ * Ported to Busybox - Alfred M. Szmidt <ams@trillian.itslinux.org>
+ * Removed --version/-V and --help/-h in
+ * Removed parse_error(), using bb_error_msg() from Busybox instead
+ * Replaced our_malloc with xmalloc and our_realloc with xrealloc
+ *
+ */
+
+#include "busybox.h"
+#include <getopt.h>
+
+/* NON_OPT is the code that is returned when a non-option is found in '+'
+ mode */
+enum {
+ NON_OPT = 1,
+/* LONG_OPT is the code that is returned when a long option is found. */
+ LONG_OPT = 2
+};
+
+/* The shells recognized. */
+typedef enum {BASH,TCSH} shell_t;
+
+
+/* Some global variables that tells us how to parse. */
+static shell_t shell=BASH; /* The shell we generate output for. */
+static int quiet_errors; /* 0 is not quiet. */
+static int quiet_output; /* 0 is not quiet. */
+static int quote=1; /* 1 is do quote. */
+static int alternative; /* 0 is getopt_long, 1 is getopt_long_only */
+
+/* Function prototypes */
+static const char *normalize(const char *arg);
+static int generate_output(char * argv[],int argc,const char *optstr,
+ const struct option *longopts);
+static void add_long_options(char *options);
+static void add_longopt(const char *name,int has_arg);
+static void set_shell(const char *new_shell);
+
+
+/*
+ * This function 'normalizes' a single argument: it puts single quotes around
+ * it and escapes other special characters. If quote is false, it just
+ * returns its argument.
+ * Bash only needs special treatment for single quotes; tcsh also recognizes
+ * exclamation marks within single quotes, and nukes whitespace.
+ * This function returns a pointer to a buffer that is overwritten by
+ * each call.
+ */
+const char *normalize(const char *arg)
+{
+ static char *BUFFER=NULL;
+ const char *argptr=arg;
+ char *bufptr;
+
+ free(BUFFER);
+
+ if (!quote) { /* Just copy arg */
+ BUFFER=xstrdup(arg);
+ return BUFFER;
+ }
+
+ /* Each character in arg may take up to four characters in the result:
+ For a quote we need a closing quote, a backslash, a quote and an
+ opening quote! We need also the global opening and closing quote,
+ and one extra character for '\0'. */
+ BUFFER=xmalloc(strlen(arg)*4+3);
+
+ bufptr=BUFFER;
+ *bufptr++='\'';
+
+ while (*argptr) {
+ if (*argptr == '\'') {
+ /* Quote: replace it with: '\'' */
+ *bufptr++='\'';
+ *bufptr++='\\';
+ *bufptr++='\'';
+ *bufptr++='\'';
+ } else if (shell==TCSH && *argptr=='!') {
+ /* Exclamation mark: replace it with: \! */
+ *bufptr++='\'';
+ *bufptr++='\\';
+ *bufptr++='!';
+ *bufptr++='\'';
+ } else if (shell==TCSH && *argptr=='\n') {
+ /* Newline: replace it with: \n */
+ *bufptr++='\\';
+ *bufptr++='n';
+ } else if (shell==TCSH && isspace(*argptr)) {
+ /* Non-newline whitespace: replace it with \<ws> */
+ *bufptr++='\'';
+ *bufptr++='\\';
+ *bufptr++=*argptr;
+ *bufptr++='\'';
+ } else
+ /* Just copy */
+ *bufptr++=*argptr;
+ argptr++;
+ }
+ *bufptr++='\'';
+ *bufptr++='\0';
+ return BUFFER;
+}
+
+/*
+ * Generate the output. argv[0] is the program name (used for reporting errors).
+ * argv[1..] contains the options to be parsed. argc must be the number of
+ * elements in argv (ie. 1 if there are no options, only the program name),
+ * optstr must contain the short options, and longopts the long options.
+ * Other settings are found in global variables.
+ */
+int generate_output(char * argv[],int argc,const char *optstr,
+ const struct option *longopts)
+{
+ int exit_code = 0; /* We assume everything will be OK */
+ int opt;
+ int longindex;
+ const char *charptr;
+
+ if (quiet_errors) /* No error reporting from getopt(3) */
+ opterr=0;
+ optind=0; /* Reset getopt(3) */
+
+ while ((opt = (alternative?
+ getopt_long_only(argc,argv,optstr,longopts,&longindex):
+ getopt_long(argc,argv,optstr,longopts,&longindex)))
+ != EOF)
+ if (opt == '?' || opt == ':' )
+ exit_code = 1;
+ else if (!quiet_output) {
+ if (opt == LONG_OPT) {
+ printf(" --%s",longopts[longindex].name);
+ if (longopts[longindex].has_arg)
+ printf(" %s",
+ normalize(optarg?optarg:""));
+ } else if (opt == NON_OPT)
+ printf(" %s",normalize(optarg));
+ else {
+ printf(" -%c",opt);
+ charptr = strchr(optstr,opt);
+ if (charptr != NULL && *++charptr == ':')
+ printf(" %s",
+ normalize(optarg?optarg:""));
+ }
+ }
+
+ if (! quiet_output) {
+ printf(" --");
+ while (optind < argc)
+ printf(" %s",normalize(argv[optind++]));
+ puts("");
+ }
+ return exit_code;
+}
+
+static struct option *long_options;
+static int long_options_length; /* Length of array */
+static int long_options_nr; /* Nr of used elements in array */
+enum { LONG_OPTIONS_INCR = 10 };
+#define init_longopt() add_longopt(NULL,0)
+
+/* Register a long option. The contents of name is copied. */
+void add_longopt(const char *name, int has_arg)
+{
+ if (!name) { /* init */
+ free(long_options);
+ long_options=NULL;
+ long_options_length=0;
+ long_options_nr=0;
+ }
+
+ if (long_options_nr == long_options_length) {
+ long_options_length += LONG_OPTIONS_INCR;
+ long_options=xrealloc(long_options,
+ sizeof(struct option) *
+ long_options_length);
+ }
+
+ long_options[long_options_nr].name=NULL;
+ long_options[long_options_nr].has_arg=0;
+ long_options[long_options_nr].flag=NULL;
+ long_options[long_options_nr].val=0;
+
+ if (long_options_nr) { /* Not for init! */
+ long_options[long_options_nr-1].has_arg=has_arg;
+ long_options[long_options_nr-1].flag=NULL;
+ long_options[long_options_nr-1].val=LONG_OPT;
+ long_options[long_options_nr-1].name=xstrdup(name);
+ }
+ long_options_nr++;
+}
+
+
+/*
+ * Register several long options. options is a string of long options,
+ * separated by commas or whitespace.
+ * This nukes options!
+ */
+void add_long_options(char *options)
+{
+ int arg_opt, tlen;
+ char *tokptr=strtok(options,", \t\n");
+ while (tokptr) {
+ arg_opt=no_argument;
+ tlen=strlen(tokptr);
+ if (tlen > 0) {
+ if (tokptr[tlen-1] == ':') {
+ if (tlen > 1 && tokptr[tlen-2] == ':') {
+ tokptr[tlen-2]='\0';
+ tlen -= 2;
+ arg_opt=optional_argument;
+ } else {
+ tokptr[tlen-1]='\0';
+ tlen -= 1;
+ arg_opt=required_argument;
+ }
+ if (tlen == 0)
+ bb_error_msg("empty long option after -l or --long argument");
+ }
+ add_longopt(tokptr,arg_opt);
+ }
+ tokptr=strtok(NULL,", \t\n");
+ }
+}
+
+void set_shell(const char *new_shell)
+{
+ if (!strcmp(new_shell,"bash"))
+ shell=BASH;
+ else if (!strcmp(new_shell,"tcsh"))
+ shell=TCSH;
+ else if (!strcmp(new_shell,"sh"))
+ shell=BASH;
+ else if (!strcmp(new_shell,"csh"))
+ shell=TCSH;
+ else
+ bb_error_msg("unknown shell after -s or --shell argument");
+}
+
+
+/* Exit codes:
+ * 0) No errors, successful operation.
+ * 1) getopt(3) returned an error.
+ * 2) A problem with parameter parsing for getopt(1).
+ * 3) Internal error, out of memory
+ * 4) Returned for -T
+ */
+
+static const struct option longopts[]=
+{
+ {"options",required_argument,NULL,'o'},
+ {"longoptions",required_argument,NULL,'l'},
+ {"quiet",no_argument,NULL,'q'},
+ {"quiet-output",no_argument,NULL,'Q'},
+ {"shell",required_argument,NULL,'s'},
+ {"test",no_argument,NULL,'T'},
+ {"unquoted",no_argument,NULL,'u'},
+ {"alternative",no_argument,NULL,'a'},
+ {"name",required_argument,NULL,'n'},
+ {NULL,0,NULL,0}
+};
+
+/* Stop scanning as soon as a non-option argument is found! */
+static const char shortopts[]="+ao:l:n:qQs:Tu";
+
+
+int getopt_main(int argc, char *argv[]);
+int getopt_main(int argc, char *argv[])
+{
+ const char *optstr = NULL;
+ char *name = NULL;
+ int opt;
+ int compatible=0;
+
+ init_longopt();
+
+ if (getenv("GETOPT_COMPATIBLE"))
+ compatible=1;
+
+ if (argc == 1) {
+ if (compatible) {
+ /* For some reason, the original getopt gave no error
+ when there were no arguments. */
+ printf(" --\n");
+ return 0;
+ } else
+ bb_error_msg_and_die("missing optstring argument");
+ }
+
+ if (argv[1][0] != '-' || compatible) {
+ char *s;
+
+ quote=0;
+ s=xmalloc(strlen(argv[1])+1);
+ strcpy(s,argv[1]+strspn(argv[1],"-+"));
+ argv[1]=argv[0];
+ return generate_output(argv+1,argc-1,s,long_options);
+ }
+
+ while ((opt = getopt_long(argc,argv,shortopts,longopts,NULL)) != EOF)
+ switch (opt) {
+ case 'a':
+ alternative=1;
+ break;
+ case 'o':
+ optstr = optarg;
+ break;
+ case 'l':
+ add_long_options(optarg);
+ break;
+ case 'n':
+ name = optarg;
+ break;
+ case 'q':
+ quiet_errors=1;
+ break;
+ case 'Q':
+ quiet_output=1;
+ break;
+ case 's':
+ set_shell(optarg);
+ break;
+ case 'T':
+ return 4;
+ case 'u':
+ quote=0;
+ break;
+ default:
+ bb_show_usage();
+ }
+
+ if (!optstr) {
+ if (optind >= argc)
+ bb_error_msg_and_die("missing optstring argument");
+ else optstr=argv[optind++];
+ }
+ if (name)
+ argv[optind-1]=name;
+ else
+ argv[optind-1]=argv[0];
+ return generate_output(argv+optind-1,argc-optind+1,optstr,long_options);
+}
diff --git a/i/pc104/initrd/conf/busybox/util-linux/hexdump.c b/i/pc104/initrd/conf/busybox/util-linux/hexdump.c
new file mode 100644
index 0000000..cddd185
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/util-linux/hexdump.c
@@ -0,0 +1,102 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * hexdump implementation for busybox
+ * Based on code from util-linux v 2.11l
+ *
+ * Copyright (c) 1989
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Licensed under GPLv2 or later, see file License in this tarball for details.
+ */
+
+#include "busybox.h"
+#include <getopt.h>
+#include "dump.h"
+
+static void bb_dump_addfile(char *name)
+{
+ char *p;
+ FILE *fp;
+ char *buf;
+
+ fp = xfopen(name, "r");
+
+ while ((buf = xmalloc_getline(fp)) != NULL) {
+ p = skip_whitespace(buf);
+
+ if (*p && (*p != '#')) {
+ bb_dump_add(p);
+ }
+ free(buf);
+ }
+ fclose(fp);
+}
+
+static const char * const add_strings[] = {
+ "\"%07.7_ax \" 16/1 \"%03o \" \"\\n\"", /* b */
+ "\"%07.7_ax \" 16/1 \"%3_c \" \"\\n\"", /* c */
+ "\"%07.7_ax \" 8/2 \" %05u \" \"\\n\"", /* d */
+ "\"%07.7_ax \" 8/2 \" %06o \" \"\\n\"", /* o */
+ "\"%07.7_ax \" 8/2 \" %04x \" \"\\n\"", /* x */
+};
+
+static const char add_first[] = "\"%07.7_Ax\n\"";
+
+static const char hexdump_opts[] = "bcdoxCe:f:n:s:v";
+
+static const struct suffix_mult suffixes[] = {
+ {"b", 512 },
+ {"k", 1024 },
+ {"m", 1024*1024 },
+ {NULL, 0 }
+};
+
+int hexdump_main(int argc, char **argv);
+int hexdump_main(int argc, char **argv)
+{
+ const char *p;
+ int ch;
+
+ bb_dump_vflag = FIRST;
+ bb_dump_length = -1;
+
+ while ((ch = getopt(argc, argv, hexdump_opts)) > 0) {
+ p = strchr(hexdump_opts, ch);
+ if (!p)
+ bb_show_usage();
+ if ((p - hexdump_opts) < 5) {
+ bb_dump_add(add_first);
+ bb_dump_add(add_strings[(int)(p - hexdump_opts)]);
+ } else if (ch == 'C') {
+ bb_dump_add("\"%08.8_Ax\n\"");
+ bb_dump_add("\"%08.8_ax \" 8/1 \"%02x \" \" \" 8/1 \"%02x \" ");
+ bb_dump_add("\" |\" 16/1 \"%_p\" \"|\\n\"");
+ } else {
+ /* Save a little bit of space below by omitting the 'else's. */
+ if (ch == 'e') {
+ bb_dump_add(optarg);
+ } /* else */
+ if (ch == 'f') {
+ bb_dump_addfile(optarg);
+ } /* else */
+ if (ch == 'n') {
+ bb_dump_length = xatoi_u(optarg);
+ } /* else */
+ if (ch == 's') {
+ bb_dump_skip = xatoul_range_sfx(optarg, 0, LONG_MAX, suffixes);
+ } /* else */
+ if (ch == 'v') {
+ bb_dump_vflag = ALL;
+ }
+ }
+ }
+
+ if (!bb_dump_fshead) {
+ bb_dump_add(add_first);
+ bb_dump_add("\"%07.7_ax \" 8/2 \"%04x \" \"\\n\"");
+ }
+
+ argv += optind;
+
+ return bb_dump_dump(argv);
+}
diff --git a/i/pc104/initrd/conf/busybox/util-linux/hwclock.c b/i/pc104/initrd/conf/busybox/util-linux/hwclock.c
new file mode 100644
index 0000000..86235e9
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/util-linux/hwclock.c
@@ -0,0 +1,222 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini hwclock implementation for busybox
+ *
+ * Copyright (C) 2002 Robert Griebl <griebl@gmx.de>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+*/
+
+
+#include <sys/ioctl.h>
+#include <sys/utsname.h>
+#include <getopt.h>
+#include "busybox.h"
+
+/* Copied from linux/rtc.h to eliminate the kernel dependency */
+struct linux_rtc_time {
+ int tm_sec;
+ int tm_min;
+ int tm_hour;
+ int tm_mday;
+ int tm_mon;
+ int tm_year;
+ int tm_wday;
+ int tm_yday;
+ int tm_isdst;
+};
+
+#define RTC_SET_TIME _IOW('p', 0x0a, struct linux_rtc_time) /* Set RTC time */
+#define RTC_RD_TIME _IOR('p', 0x09, struct linux_rtc_time) /* Read RTC time */
+
+#if ENABLE_FEATURE_HWCLOCK_LONG_OPTIONS
+# ifndef _GNU_SOURCE
+# define _GNU_SOURCE
+# endif
+#endif
+
+static const char *rtcname;
+
+static int xopen_rtc(int flags)
+{
+ int rtc;
+
+ if (!rtcname) {
+ rtc = open("/dev/rtc", flags);
+ if (rtc >= 0)
+ return rtc;
+ rtc = open("/dev/rtc0", flags);
+ if (rtc >= 0)
+ return rtc;
+ rtcname = "/dev/misc/rtc";
+ }
+ return xopen(rtcname, flags);
+}
+
+static time_t read_rtc(int utc)
+{
+ struct tm tm;
+ char *oldtz = 0;
+ time_t t = 0;
+ int rtc = xopen_rtc(O_RDONLY);
+
+ memset(&tm, 0, sizeof(struct tm));
+ if (ioctl(rtc, RTC_RD_TIME, &tm) < 0 )
+ bb_perror_msg_and_die("cannot read time from RTC");
+ tm.tm_isdst = -1; /* not known */
+
+ close(rtc);
+
+ if (utc) {
+ oldtz = getenv("TZ");
+ setenv("TZ", "UTC 0", 1);
+ tzset();
+ }
+
+ t = mktime(&tm);
+
+ if (utc) {
+ if (oldtz)
+ setenv("TZ", oldtz, 1);
+ else
+ unsetenv("TZ");
+ tzset();
+ }
+ return t;
+}
+
+static void write_rtc(time_t t, int utc)
+{
+ struct tm tm;
+ int rtc = xopen_rtc(O_WRONLY);
+
+ tm = *(utc ? gmtime(&t) : localtime(&t));
+ tm.tm_isdst = 0;
+
+ if (ioctl(rtc, RTC_SET_TIME, &tm) < 0)
+ bb_perror_msg_and_die("cannot set the RTC time");
+
+ close(rtc);
+}
+
+static int show_clock(int utc)
+{
+ struct tm *ptm;
+ time_t t;
+ RESERVE_CONFIG_BUFFER(buffer, 64);
+
+ t = read_rtc(utc);
+ ptm = localtime(&t); /* Sets 'tzname[]' */
+
+ safe_strncpy(buffer, ctime(&t), 64);
+ if (buffer[0])
+ buffer[strlen(buffer) - 1] = 0;
+
+ //printf("%s %.6f seconds %s\n", buffer, 0.0, utc ? "" : (ptm->tm_isdst ? tzname[1] : tzname[0]));
+ printf( "%s %.6f seconds\n", buffer, 0.0);
+ RELEASE_CONFIG_BUFFER(buffer);
+
+ return 0;
+}
+
+static int to_sys_clock(int utc)
+{
+ struct timeval tv = { 0, 0 };
+ const struct timezone tz = { timezone/60 - 60*daylight, 0 };
+
+ tv.tv_sec = read_rtc(utc);
+
+ if (settimeofday(&tv, &tz))
+ bb_perror_msg_and_die("settimeofday() failed");
+
+ return 0;
+}
+
+static int from_sys_clock(int utc)
+{
+ struct timeval tv = { 0, 0 };
+ struct timezone tz = { 0, 0 };
+
+ if (gettimeofday(&tv, &tz))
+ bb_perror_msg_and_die("gettimeofday() failed");
+
+ write_rtc(tv.tv_sec, utc);
+ return 0;
+}
+
+#ifdef CONFIG_FEATURE_HWCLOCK_ADJTIME_FHS
+# define ADJTIME_PATH "/var/lib/hwclock/adjtime"
+#else
+# define ADJTIME_PATH "/etc/adjtime"
+#endif
+static int check_utc(void)
+{
+ int utc = 0;
+ FILE *f = fopen(ADJTIME_PATH, "r");
+
+ if (f) {
+ RESERVE_CONFIG_BUFFER(buffer, 128);
+
+ while (fgets(buffer, sizeof(buffer), f)) {
+ int len = strlen(buffer);
+
+ while (len && isspace(buffer[len - 1]))
+ len--;
+
+ buffer[len] = 0;
+
+ if (strncmp(buffer, "UTC", 3) == 0 ) {
+ utc = 1;
+ break;
+ }
+ }
+ fclose(f);
+ RELEASE_CONFIG_BUFFER(buffer);
+ }
+ return utc;
+}
+
+#define HWCLOCK_OPT_LOCALTIME 0x01
+#define HWCLOCK_OPT_UTC 0x02
+#define HWCLOCK_OPT_SHOW 0x04
+#define HWCLOCK_OPT_HCTOSYS 0x08
+#define HWCLOCK_OPT_SYSTOHC 0x10
+#define HWCLOCK_OPT_RTCFILE 0x20
+
+int hwclock_main(int argc, char **argv );
+int hwclock_main(int argc, char **argv )
+{
+ unsigned opt;
+ int utc;
+
+#if ENABLE_FEATURE_HWCLOCK_LONG_OPTIONS
+ static const struct option hwclock_long_options[] = {
+ { "localtime", 0, 0, 'l' },
+ { "utc", 0, 0, 'u' },
+ { "show", 0, 0, 'r' },
+ { "hctosys", 0, 0, 's' },
+ { "systohc", 0, 0, 'w' },
+ { "file", 1, 0, 'f' },
+ { 0, 0, 0, 0 }
+ };
+ applet_long_options = hwclock_long_options;
+#endif
+ opt_complementary = "?:r--ws:w--rs:s--wr:l--u:u--l";
+ opt = getopt32(argc, argv, "lurswf:", &rtcname);
+
+ /* If -u or -l wasn't given check if we are using utc */
+ if (opt & (HWCLOCK_OPT_UTC | HWCLOCK_OPT_LOCALTIME))
+ utc = opt & HWCLOCK_OPT_UTC;
+ else
+ utc = check_utc();
+
+ if (opt & HWCLOCK_OPT_HCTOSYS) {
+ return to_sys_clock(utc);
+ }
+ else if (opt & HWCLOCK_OPT_SYSTOHC) {
+ return from_sys_clock(utc);
+ } else {
+ /* default HWCLOCK_OPT_SHOW */
+ return show_clock(utc);
+ }
+}
diff --git a/i/pc104/initrd/conf/busybox/util-linux/ipcrm.c b/i/pc104/initrd/conf/busybox/util-linux/ipcrm.c
new file mode 100644
index 0000000..9240e2c
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/util-linux/ipcrm.c
@@ -0,0 +1,218 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ipcrm.c - utility to allow removal of IPC objects and data structures.
+ *
+ * 01 Sept 2004 - Rodney Radford <rradford@mindspring.com>
+ * Adapted for busybox from util-linux-2.12a.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "busybox.h"
+
+/* X/OPEN tells us to use <sys/{types,ipc,sem}.h> for semctl() */
+/* X/OPEN tells us to use <sys/{types,ipc,msg}.h> for msgctl() */
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <sys/msg.h>
+#include <sys/sem.h>
+
+#if defined (__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED)
+/* union semun is defined by including <sys/sem.h> */
+#else
+/* according to X/OPEN we have to define it ourselves */
+union semun {
+ int val;
+ struct semid_ds *buf;
+ unsigned short int *array;
+ struct seminfo *__buf;
+};
+#endif
+
+#ifndef CONFIG_IPCRM_DROP_LEGACY
+
+typedef enum type_id {
+ SHM,
+ SEM,
+ MSG
+} type_id;
+
+static int remove_ids(type_id type, int argc, char **argv)
+{
+ unsigned long id;
+ int ret = 0; /* silence gcc */
+ int nb_errors = 0;
+ union semun arg;
+
+ arg.val = 0;
+
+ while (argc) {
+ id = bb_strtoul(argv[0], NULL, 10);
+ if (errno || id > INT_MAX) {
+ bb_error_msg("invalid id: %s", argv[0]);
+ nb_errors++;
+ } else {
+ if (type == SEM)
+ ret = semctl(id, 0, IPC_RMID, arg);
+ else if (type == MSG)
+ ret = msgctl(id, IPC_RMID, NULL);
+ else if (type == SHM)
+ ret = shmctl(id, IPC_RMID, NULL);
+
+ if (ret) {
+ bb_perror_msg("cannot remove id %s", argv[0]);
+ nb_errors++;
+ }
+ }
+ argc--;
+ argv++;
+ }
+
+ return nb_errors;
+}
+#endif /* #ifndef CONFIG_IPCRM_DROP_LEGACY */
+
+
+int ipcrm_main(int argc, char **argv);
+int ipcrm_main(int argc, char **argv)
+{
+ int c;
+ int error = 0;
+
+ /* if the command is executed without parameters, do nothing */
+ if (argc == 1)
+ return 0;
+#ifndef CONFIG_IPCRM_DROP_LEGACY
+ /* check to see if the command is being invoked in the old way if so
+ then run the old code. Valid commands are msg, shm, sem. */
+ {
+ type_id what = 0; /* silence gcc */
+ char w;
+
+ w=argv[1][0];
+ if ( ((w == 'm' && argv[1][1] == 's' && argv[1][2] == 'g')
+ || (argv[1][0] == 's'
+ && ((w=argv[1][1]) == 'h' || w == 'e')
+ && argv[1][2] == 'm')
+ ) && argv[1][3] == '\0'
+ ) {
+
+ if (argc < 3)
+ bb_show_usage();
+
+ if (w == 'h')
+ what = SHM;
+ else if (w == 'm')
+ what = MSG;
+ else if (w == 'e')
+ what = SEM;
+
+ if (remove_ids(what, argc-2, &argv[2]))
+ fflush_stdout_and_exit(1);
+ printf("resource(s) deleted\n");
+ return 0;
+ }
+ }
+#endif /* #ifndef CONFIG_IPCRM_DROP_LEGACY */
+
+ /* process new syntax to conform with SYSV ipcrm */
+ while ((c = getopt(argc, argv, "q:m:s:Q:M:S:h?")) != -1) {
+ int result;
+ int id = 0;
+ int iskey = (isupper)(c);
+
+ /* needed to delete semaphores */
+ union semun arg;
+
+ arg.val = 0;
+
+ if ((c == '?') || (c == 'h')) {
+ bb_show_usage();
+ }
+
+ /* we don't need case information any more */
+ c = tolower(c);
+
+ /* make sure the option is in range: allowed are q, m, s */
+ if (c != 'q' && c != 'm' && c != 's') {
+ bb_show_usage();
+ }
+
+ if (iskey) {
+ /* keys are in hex or decimal */
+ key_t key = xstrtoul(optarg, 0);
+
+ if (key == IPC_PRIVATE) {
+ error++;
+ bb_error_msg("illegal key (%s)", optarg);
+ continue;
+ }
+
+ /* convert key to id */
+ id = ((c == 'q') ? msgget(key, 0) :
+ (c == 'm') ? shmget(key, 0, 0) : semget(key, 0, 0));
+
+ if (id < 0) {
+ const char *errmsg;
+ const char *const what = "key";
+
+ error++;
+ switch (errno) {
+ case EACCES:
+ errmsg = "permission denied for";
+ break;
+ case EIDRM:
+ errmsg = "already removed";
+ break;
+ case ENOENT:
+ errmsg = "invalid";
+ break;
+ default:
+ errmsg = "unknown error in";
+ break;
+ }
+ bb_error_msg("%s %s (%s)", errmsg, what, optarg);
+ continue;
+ }
+ } else {
+ /* ids are in decimal */
+ id = xatoul(optarg);
+ }
+
+ result = ((c == 'q') ? msgctl(id, IPC_RMID, NULL) :
+ (c == 'm') ? shmctl(id, IPC_RMID, NULL) :
+ semctl(id, 0, IPC_RMID, arg));
+
+ if (result) {
+ const char *errmsg;
+ const char *const what = iskey ? "key" : "id";
+
+ error++;
+ switch (errno) {
+ case EACCES:
+ case EPERM:
+ errmsg = "permission denied for";
+ break;
+ case EINVAL:
+ errmsg = "invalid";
+ break;
+ case EIDRM:
+ errmsg = "already removed";
+ break;
+ default:
+ errmsg = "unknown error in";
+ break;
+ }
+ bb_error_msg("%s %s (%s)", errmsg, what, optarg);
+ continue;
+ }
+ }
+
+ /* print usage if we still have some arguments left over */
+ if (optind != argc) {
+ bb_show_usage();
+ }
+
+ /* exit value reflects the number of errors encountered */
+ return error;
+}
diff --git a/i/pc104/initrd/conf/busybox/util-linux/ipcs.c b/i/pc104/initrd/conf/busybox/util-linux/ipcs.c
new file mode 100644
index 0000000..5a7b224
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/util-linux/ipcs.c
@@ -0,0 +1,631 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ipcs.c -- provides information on allocated ipc resources.
+ *
+ * 01 Sept 2004 - Rodney Radford <rradford@mindspring.com>
+ * Adapted for busybox from util-linux-2.12a.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "busybox.h"
+#include <errno.h>
+#include <time.h>
+#include <pwd.h>
+#include <grp.h>
+
+/* X/OPEN tells us to use <sys/{types,ipc,sem}.h> for semctl() */
+/* X/OPEN tells us to use <sys/{types,ipc,msg}.h> for msgctl() */
+/* X/OPEN tells us to use <sys/{types,ipc,shm}.h> for shmctl() */
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/sem.h>
+#include <sys/msg.h>
+#include <sys/shm.h>
+
+
+
+/*-------------------------------------------------------------------*/
+/* SHM_DEST and SHM_LOCKED are defined in kernel headers,
+ but inside #ifdef __KERNEL__ ... #endif */
+#ifndef SHM_DEST
+/* shm_mode upper byte flags */
+#define SHM_DEST 01000 /* segment will be destroyed on last detach */
+#define SHM_LOCKED 02000 /* segment will not be swapped */
+#endif
+
+/* For older kernels the same holds for the defines below */
+#ifndef MSG_STAT
+#define MSG_STAT 11
+#define MSG_INFO 12
+#endif
+
+#ifndef SHM_STAT
+#define SHM_STAT 13
+#define SHM_INFO 14
+struct shm_info {
+ int used_ids;
+ ulong shm_tot; /* total allocated shm */
+ ulong shm_rss; /* total resident shm */
+ ulong shm_swp; /* total swapped shm */
+ ulong swap_attempts;
+ ulong swap_successes;
+};
+#endif
+
+#ifndef SEM_STAT
+#define SEM_STAT 18
+#define SEM_INFO 19
+#endif
+
+/* Some versions of libc only define IPC_INFO when __USE_GNU is defined. */
+#ifndef IPC_INFO
+#define IPC_INFO 3
+#endif
+/*-------------------------------------------------------------------*/
+
+/* The last arg of semctl is a union semun, but where is it defined?
+ X/OPEN tells us to define it ourselves, but until recently
+ Linux include files would also define it. */
+#if defined (__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED)
+/* union semun is defined by including <sys/sem.h> */
+#else
+/* according to X/OPEN we have to define it ourselves */
+union semun {
+ int val;
+ struct semid_ds *buf;
+ unsigned short int *array;
+ struct seminfo *__buf;
+};
+#endif
+
+/* X/OPEN (Jan 1987) does not define fields key, seq in struct ipc_perm;
+ libc 4/5 does not mention struct ipc_term at all, but includes
+ <linux/ipc.h>, which defines a struct ipc_perm with such fields.
+ glibc-1.09 has no support for sysv ipc.
+ glibc 2 uses __key, __seq */
+#if defined (__GNU_LIBRARY__) && __GNU_LIBRARY__ > 1
+#define KEY __key
+#else
+#define KEY key
+#endif
+
+#define LIMITS 1
+#define STATUS 2
+#define CREATOR 3
+#define TIME 4
+#define PID 5
+
+static char format;
+
+static void print_perms(int id, struct ipc_perm *ipcp)
+{
+ struct passwd *pw;
+ struct group *gr;
+
+ printf("%-10d %-10o", id, ipcp->mode & 0777);
+
+ if ((pw = getpwuid(ipcp->cuid)))
+ printf(" %-10s", pw->pw_name);
+ else
+ printf(" %-10d", ipcp->cuid);
+ if ((gr = getgrgid(ipcp->cgid)))
+ printf(" %-10s", gr->gr_name);
+ else
+ printf(" %-10d", ipcp->cgid);
+
+ if ((pw = getpwuid(ipcp->uid)))
+ printf(" %-10s", pw->pw_name);
+ else
+ printf(" %-10d", ipcp->uid);
+ if ((gr = getgrgid(ipcp->gid)))
+ printf(" %-10s\n", gr->gr_name);
+ else
+ printf(" %-10d\n", ipcp->gid);
+}
+
+
+static void do_shm(void)
+{
+ int maxid, shmid, id;
+ struct shmid_ds shmseg;
+ struct shm_info shm_info;
+ struct shminfo shminfo;
+ struct ipc_perm *ipcp = &shmseg.shm_perm;
+ struct passwd *pw;
+
+ maxid = shmctl(0, SHM_INFO, (struct shmid_ds *) (void *) &shm_info);
+ if (maxid < 0) {
+ printf("kernel not configured for %s\n", "shared memory");
+ return;
+ }
+
+ switch (format) {
+ case LIMITS:
+ printf("------ Shared Memory %s --------\n", "Limits");
+ if ((shmctl(0, IPC_INFO, (struct shmid_ds *) (void *) &shminfo)) < 0)
+ return;
+ /* glibc 2.1.3 and all earlier libc's have ints as fields
+ of struct shminfo; glibc 2.1.91 has unsigned long; ach */
+ printf("max number of segments = %lu\n"
+ "max seg size (kbytes) = %lu\n"
+ "max total shared memory (pages) = %lu\n"
+ "min seg size (bytes) = %lu\n",
+ (unsigned long) shminfo.shmmni,
+ (unsigned long) (shminfo.shmmax >> 10),
+ (unsigned long) shminfo.shmall,
+ (unsigned long) shminfo.shmmin);
+ return;
+
+ case STATUS:
+ printf("------ Shared Memory %s --------\n", "Status");
+ printf( "segments allocated %d\n"
+ "pages allocated %ld\n"
+ "pages resident %ld\n"
+ "pages swapped %ld\n"
+ "Swap performance: %ld attempts\t%ld successes\n",
+ shm_info.used_ids,
+ shm_info.shm_tot,
+ shm_info.shm_rss,
+ shm_info.shm_swp,
+ shm_info.swap_attempts, shm_info.swap_successes);
+ return;
+
+ case CREATOR:
+ printf("------ Shared Memory %s --------\n", "Segment Creators/Owners");
+ printf( "%-10s %-10s %-10s %-10s %-10s %-10s\n",
+ "shmid", "perms", "cuid", "cgid", "uid", "gid");
+ break;
+
+ case TIME:
+ printf("------ Shared Memory %s --------\n", "Attach/Detach/Change Times");
+ printf( "%-10s %-10s %-20s %-20s %-20s\n",
+ "shmid", "owner", "attached", "detached", "changed");
+ break;
+
+ case PID:
+ printf("------ Shared Memory %s --------\n", "Creator/Last-op");
+ printf( "%-10s %-10s %-10s %-10s\n",
+ "shmid", "owner", "cpid", "lpid");
+ break;
+
+ default:
+ printf("------ Shared Memory %s --------\n", "Segments");
+ printf( "%-10s %-10s %-10s %-10s %-10s %-10s %-12s\n",
+ "key", "shmid", "owner", "perms", "bytes", "nattch",
+ "status");
+ break;
+ }
+
+ for (id = 0; id <= maxid; id++) {
+ shmid = shmctl(id, SHM_STAT, &shmseg);
+ if (shmid < 0)
+ continue;
+ if (format == CREATOR) {
+ print_perms(shmid, ipcp);
+ continue;
+ }
+ pw = getpwuid(ipcp->uid);
+ switch (format) {
+ case TIME:
+ if (pw)
+ printf("%-10d %-10.10s", shmid, pw->pw_name);
+ else
+ printf("%-10d %-10d", shmid, ipcp->uid);
+ /* ctime uses static buffer: use separate calls */
+ printf(" %-20.16s", shmseg.shm_atime
+ ? ctime(&shmseg.shm_atime) + 4 : "Not set");
+ printf(" %-20.16s", shmseg.shm_dtime
+ ? ctime(&shmseg.shm_dtime) + 4 : "Not set");
+ printf(" %-20.16s\n", shmseg.shm_ctime
+ ? ctime(&shmseg.shm_ctime) + 4 : "Not set");
+ break;
+ case PID:
+ if (pw)
+ printf("%-10d %-10.10s", shmid, pw->pw_name);
+ else
+ printf("%-10d %-10d", shmid, ipcp->uid);
+ printf(" %-10d %-10d\n", shmseg.shm_cpid, shmseg.shm_lpid);
+ break;
+
+ default:
+ printf("0x%08x ", ipcp->KEY);
+ if (pw)
+ printf("%-10d %-10.10s", shmid, pw->pw_name);
+ else
+ printf("%-10d %-10d", shmid, ipcp->uid);
+ printf(" %-10o %-10lu %-10ld %-6s %-6s\n", ipcp->mode & 0777,
+ /*
+ * earlier: int, Austin has size_t
+ */
+ (unsigned long) shmseg.shm_segsz,
+ /*
+ * glibc-2.1.3 and earlier has unsigned short;
+ * Austin has shmatt_t
+ */
+ (long) shmseg.shm_nattch,
+ ipcp->mode & SHM_DEST ? "dest" : " ",
+ ipcp->mode & SHM_LOCKED ? "locked" : " ");
+ break;
+ }
+ }
+}
+
+
+static void do_sem(void)
+{
+ int maxid, semid, id;
+ struct semid_ds semary;
+ struct seminfo seminfo;
+ struct ipc_perm *ipcp = &semary.sem_perm;
+ struct passwd *pw;
+ union semun arg;
+
+ arg.array = (ushort *) (void *) &seminfo;
+ maxid = semctl(0, 0, SEM_INFO, arg);
+ if (maxid < 0) {
+ printf("kernel not configured for %s\n", "semaphores");
+ return;
+ }
+
+ switch (format) {
+ case LIMITS:
+ printf("------ Semaphore %s --------\n", "Limits");
+ arg.array = (ushort *) (void *) &seminfo; /* damn union */
+ if ((semctl(0, 0, IPC_INFO, arg)) < 0)
+ return;
+ printf("max number of arrays = %d\n"
+ "max semaphores per array = %d\n"
+ "max semaphores system wide = %d\n"
+ "max ops per semop call = %d\n"
+ "semaphore max value = %d\n",
+ seminfo.semmni,
+ seminfo.semmsl,
+ seminfo.semmns, seminfo.semopm, seminfo.semvmx);
+ return;
+
+ case STATUS:
+ printf("------ Semaphore %s --------\n", "Status");
+ printf( "used arrays = %d\n"
+ "allocated semaphores = %d\n",
+ seminfo.semusz, seminfo.semaem);
+ return;
+
+ case CREATOR:
+ printf("------ Semaphore %s --------\n", "Arrays Creators/Owners");
+ printf( "%-10s %-10s %-10s %-10s %-10s %-10s\n",
+ "semid", "perms", "cuid", "cgid", "uid", "gid");
+ break;
+
+ case TIME:
+ printf("------ Shared Memory %s --------\n", "Operation/Change Times");
+ printf( "%-8s %-10s %-26.24s %-26.24s\n",
+ "shmid", "owner", "last-op", "last-changed");
+ break;
+
+ case PID:
+ break;
+
+ default:
+ printf("------ Semaphore %s --------\n", "Arrays");
+ printf( "%-10s %-10s %-10s %-10s %-10s\n",
+ "key", "semid", "owner", "perms", "nsems");
+ break;
+ }
+
+ for (id = 0; id <= maxid; id++) {
+ arg.buf = (struct semid_ds *) &semary;
+ semid = semctl(id, 0, SEM_STAT, arg);
+ if (semid < 0)
+ continue;
+ if (format == CREATOR) {
+ print_perms(semid, ipcp);
+ continue;
+ }
+ pw = getpwuid(ipcp->uid);
+ switch (format) {
+ case TIME:
+ if (pw)
+ printf("%-8d %-10.10s", semid, pw->pw_name);
+ else
+ printf("%-8d %-10d", semid, ipcp->uid);
+ /* ctime uses static buffer: use separate calls */
+ printf(" %-26.24s", semary.sem_otime
+ ? ctime(&semary.sem_otime) : "Not set");
+ printf(" %-26.24s\n", semary.sem_ctime
+ ? ctime(&semary.sem_ctime) : "Not set");
+ break;
+ case PID:
+ break;
+
+ default:
+ printf("0x%08x ", ipcp->KEY);
+ if (pw)
+ printf("%-10d %-10.9s", semid, pw->pw_name);
+ else
+ printf("%-10d %-9d", semid, ipcp->uid);
+ printf(" %-10o %-10ld\n", ipcp->mode & 0777,
+ /*
+ * glibc-2.1.3 and earlier has unsigned short;
+ * glibc-2.1.91 has variation between
+ * unsigned short and unsigned long
+ * Austin prescribes unsigned short.
+ */
+ (long) semary.sem_nsems);
+ break;
+ }
+ }
+}
+
+
+static void do_msg(void)
+{
+ int maxid, msqid, id;
+ struct msqid_ds msgque;
+ struct msginfo msginfo;
+ struct ipc_perm *ipcp = &msgque.msg_perm;
+ struct passwd *pw;
+
+ maxid = msgctl(0, MSG_INFO, (struct msqid_ds *) (void *) &msginfo);
+ if (maxid < 0) {
+ printf("kernel not configured for %s\n", "message queues");
+ return;
+ }
+
+ switch (format) {
+ case LIMITS:
+ if ((msgctl(0, IPC_INFO, (struct msqid_ds *) (void *) &msginfo)) < 0)
+ return;
+ printf("------ Message%s --------\n", "s: Limits");
+ printf( "max queues system wide = %d\n"
+ "max size of message (bytes) = %d\n"
+ "default max size of queue (bytes) = %d\n",
+ msginfo.msgmni, msginfo.msgmax, msginfo.msgmnb);
+ return;
+
+ case STATUS:
+ printf("------ Message%s --------\n", "s: Status");
+ printf( "allocated queues = %d\n"
+ "used headers = %d\n"
+ "used space = %d bytes\n",
+ msginfo.msgpool, msginfo.msgmap, msginfo.msgtql);
+ return;
+
+ case CREATOR:
+ printf("------ Message%s --------\n", " Queues: Creators/Owners");
+ printf( "%-10s %-10s %-10s %-10s %-10s %-10s\n",
+ "msqid", "perms", "cuid", "cgid", "uid", "gid");
+ break;
+
+ case TIME:
+ printf("------ Message%s --------\n", " Queues Send/Recv/Change Times");
+ printf( "%-8s %-10s %-20s %-20s %-20s\n",
+ "msqid", "owner", "send", "recv", "change");
+ break;
+
+ case PID:
+ printf("------ Message%s --------\n", " Queues PIDs");
+ printf( "%-10s %-10s %-10s %-10s\n",
+ "msqid", "owner", "lspid", "lrpid");
+ break;
+
+ default:
+ printf("------ Message%s --------\n", " Queues");
+ printf( "%-10s %-10s %-10s %-10s %-12s %-12s\n",
+ "key", "msqid", "owner", "perms", "used-bytes", "messages");
+ break;
+ }
+
+ for (id = 0; id <= maxid; id++) {
+ msqid = msgctl(id, MSG_STAT, &msgque);
+ if (msqid < 0)
+ continue;
+ if (format == CREATOR) {
+ print_perms(msqid, ipcp);
+ continue;
+ }
+ pw = getpwuid(ipcp->uid);
+ switch (format) {
+ case TIME:
+ if (pw)
+ printf("%-8d %-10.10s", msqid, pw->pw_name);
+ else
+ printf("%-8d %-10d", msqid, ipcp->uid);
+ printf(" %-20.16s", msgque.msg_stime
+ ? ctime(&msgque.msg_stime) + 4 : "Not set");
+ printf(" %-20.16s", msgque.msg_rtime
+ ? ctime(&msgque.msg_rtime) + 4 : "Not set");
+ printf(" %-20.16s\n", msgque.msg_ctime
+ ? ctime(&msgque.msg_ctime) + 4 : "Not set");
+ break;
+ case PID:
+ if (pw)
+ printf("%-8d %-10.10s", msqid, pw->pw_name);
+ else
+ printf("%-8d %-10d", msqid, ipcp->uid);
+ printf(" %5d %5d\n", msgque.msg_lspid, msgque.msg_lrpid);
+ break;
+
+ default:
+ printf("0x%08x ", ipcp->KEY);
+ if (pw)
+ printf("%-10d %-10.10s", msqid, pw->pw_name);
+ else
+ printf("%-10d %-10d", msqid, ipcp->uid);
+ printf(" %-10o %-12ld %-12ld\n", ipcp->mode & 0777,
+ /*
+ * glibc-2.1.3 and earlier has unsigned short;
+ * glibc-2.1.91 has variation between
+ * unsigned short, unsigned long
+ * Austin has msgqnum_t
+ */
+ (long) msgque.msg_cbytes, (long) msgque.msg_qnum);
+ break;
+ }
+ }
+}
+
+
+static void print_shm(int shmid)
+{
+ struct shmid_ds shmds;
+ struct ipc_perm *ipcp = &shmds.shm_perm;
+
+ if (shmctl(shmid, IPC_STAT, &shmds) == -1) {
+ bb_perror_msg("shmctl");
+ return;
+ }
+
+ printf("\nShared memory Segment shmid=%d\n"
+ "uid=%d\tgid=%d\tcuid=%d\tcgid=%d\n"
+ "mode=%#o\taccess_perms=%#o\n"
+ "bytes=%ld\tlpid=%d\tcpid=%d\tnattch=%ld\n",
+ shmid,
+ ipcp->uid, ipcp->gid, ipcp->cuid, ipcp->cgid,
+ ipcp->mode, ipcp->mode & 0777,
+ (long) shmds.shm_segsz, shmds.shm_lpid, shmds.shm_cpid,
+ (long) shmds.shm_nattch);
+ printf("att_time=%-26.24s\n",
+ shmds.shm_atime ? ctime(&shmds.shm_atime) : "Not set");
+ printf("det_time=%-26.24s\n",
+ shmds.shm_dtime ? ctime(&shmds.shm_dtime) : "Not set");
+ printf("change_time=%-26.24s\n\n", ctime(&shmds.shm_ctime));
+}
+
+
+static void print_msg(int msqid)
+{
+ struct msqid_ds buf;
+ struct ipc_perm *ipcp = &buf.msg_perm;
+
+ if (msgctl(msqid, IPC_STAT, &buf) == -1) {
+ bb_perror_msg("msgctl");
+ return;
+ }
+
+ printf("\nMessage Queue msqid=%d\n"
+ "uid=%d\tgid=%d\tcuid=%d\tcgid=%d\tmode=%#o\n"
+ "cbytes=%ld\tqbytes=%ld\tqnum=%ld\tlspid=%d\tlrpid=%d\n",
+ msqid, ipcp->uid, ipcp->gid, ipcp->cuid, ipcp->cgid, ipcp->mode,
+ /*
+ * glibc-2.1.3 and earlier has unsigned short;
+ * glibc-2.1.91 has variation between
+ * unsigned short, unsigned long
+ * Austin has msgqnum_t (for msg_qbytes)
+ */
+ (long) buf.msg_cbytes, (long) buf.msg_qbytes,
+ (long) buf.msg_qnum, buf.msg_lspid, buf.msg_lrpid);
+
+ printf("send_time=%-26.24s\n",
+ buf.msg_stime ? ctime(&buf.msg_stime) : "Not set");
+ printf("rcv_time=%-26.24s\n",
+ buf.msg_rtime ? ctime(&buf.msg_rtime) : "Not set");
+ printf("change_time=%-26.24s\n\n",
+ buf.msg_ctime ? ctime(&buf.msg_ctime) : "Not set");
+}
+
+static void print_sem(int semid)
+{
+ struct semid_ds semds;
+ struct ipc_perm *ipcp = &semds.sem_perm;
+ union semun arg;
+ unsigned int i;
+
+ arg.buf = &semds;
+ if (semctl(semid, 0, IPC_STAT, arg)) {
+ bb_perror_msg("semctl");
+ return;
+ }
+
+ printf("\nSemaphore Array semid=%d\n"
+ "uid=%d\t gid=%d\t cuid=%d\t cgid=%d\n"
+ "mode=%#o, access_perms=%#o\n"
+ "nsems = %ld\n"
+ "otime = %-26.24s\n",
+ semid,
+ ipcp->uid, ipcp->gid, ipcp->cuid, ipcp->cgid,
+ ipcp->mode, ipcp->mode & 0777,
+ (long) semds.sem_nsems,
+ semds.sem_otime ? ctime(&semds.sem_otime) : "Not set");
+ printf("ctime = %-26.24s\n"
+ "%-10s %-10s %-10s %-10s %-10s\n",
+ ctime(&semds.sem_ctime),
+ "semnum", "value", "ncount", "zcount", "pid");
+
+ arg.val = 0;
+ for (i = 0; i < semds.sem_nsems; i++) {
+ int val, ncnt, zcnt, pid;
+
+ val = semctl(semid, i, GETVAL, arg);
+ ncnt = semctl(semid, i, GETNCNT, arg);
+ zcnt = semctl(semid, i, GETZCNT, arg);
+ pid = semctl(semid, i, GETPID, arg);
+ if (val < 0 || ncnt < 0 || zcnt < 0 || pid < 0) {
+ bb_perror_msg_and_die("semctl");
+ }
+ printf("%-10d %-10d %-10d %-10d %-10d\n", i, val, ncnt, zcnt, pid);
+ }
+ puts("");
+}
+
+int ipcs_main(int argc, char **argv);
+int ipcs_main(int argc, char **argv)
+{
+ int id = 0;
+ unsigned flags = 0;
+ unsigned opt;
+ char *opt_i;
+#define flag_print (1<<0)
+#define flag_msg (1<<1)
+#define flag_sem (1<<2)
+#define flag_shm (1<<3)
+
+ opt = getopt32(argc, argv, "i:aqsmtcplu", &opt_i);
+ if (opt & 0x1) { // -i
+ id = xatoi(opt_i);
+ flags |= flag_print;
+ }
+ if (opt & 0x2) flags |= flag_msg | flag_sem | flag_shm; // -a
+ if (opt & 0x4) flags |= flag_msg; // -q
+ if (opt & 0x8) flags |= flag_sem; // -s
+ if (opt & 0x10) flags |= flag_shm; // -m
+ if (opt & 0x20) format = TIME; // -t
+ if (opt & 0x40) format = CREATOR; // -c
+ if (opt & 0x80) format = PID; // -p
+ if (opt & 0x100) format = LIMITS; // -l
+ if (opt & 0x200) format = STATUS; // -u
+
+ if (flags & flag_print) {
+ if (flags & flag_shm) {
+ print_shm(id);
+ fflush_stdout_and_exit(0);
+ }
+ if (flags & flag_sem) {
+ print_sem(id);
+ fflush_stdout_and_exit(0);
+ }
+ if (flags & flag_msg) {
+ print_msg(id);
+ fflush_stdout_and_exit(0);
+ }
+ bb_show_usage();
+ }
+
+ if (!(flags & (flag_shm | flag_msg | flag_sem)))
+ flags |= flag_msg | flag_shm | flag_sem;
+ puts("");
+
+ if (flags & flag_shm) {
+ do_shm();
+ puts("");
+ }
+ if (flags & flag_sem) {
+ do_sem();
+ puts("");
+ }
+ if (flags & flag_msg) {
+ do_msg();
+ puts("");
+ }
+ fflush_stdout_and_exit(0);
+}
diff --git a/i/pc104/initrd/conf/busybox/util-linux/losetup.c b/i/pc104/initrd/conf/busybox/util-linux/losetup.c
new file mode 100644
index 0000000..b220c98
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/util-linux/losetup.c
@@ -0,0 +1,65 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini losetup implementation for busybox
+ *
+ * Copyright (C) 2002 Matt Kraai.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include <getopt.h>
+#include <stdlib.h>
+
+#include "busybox.h"
+
+int losetup_main(int argc, char **argv);
+int losetup_main(int argc, char **argv)
+{
+ unsigned opt;
+ char *opt_o;
+ unsigned long long offset = 0;
+
+ opt = getopt32(argc, argv, "do:", &opt_o);
+ argc -= optind;
+ argv += optind;
+
+ if (opt == 0x3) // -d + -o (illegal)
+ bb_show_usage();
+
+ if (opt == 0x1) { // -d
+ /* detach takes exactly one argument */
+ if (argc != 1)
+ bb_show_usage();
+ if (!del_loop(argv[0]))
+ return EXIT_SUCCESS;
+ bb_perror_nomsg_and_die();
+ }
+
+ if (opt == 0x2) // -o
+ offset = xatoull(opt_o);
+
+ /* -o or no option */
+
+ if (argc == 2) {
+ if (set_loop(&argv[0], argv[1], offset) < 0)
+ bb_perror_nomsg_and_die();
+ } else if (argc == 1) {
+ char *s = query_loop(argv[0]);
+ if (!s) bb_perror_nomsg_and_die();
+ printf("%s: %s\n", argv[0], s);
+ if (ENABLE_FEATURE_CLEAN_UP) free(s);
+ } else {
+ char dev[sizeof(LOOP_NAME"0")] = LOOP_NAME"0";
+ char c;
+ for (c = '0'; c <= '9'; ++c) {
+ char *s;
+ dev[sizeof(LOOP_NAME"0")-2] = c;
+ s = query_loop(dev);
+ if (s) {
+ printf("%s: %s\n", dev, s);
+ if (ENABLE_FEATURE_CLEAN_UP) free(s);
+ }
+ }
+ }
+ return EXIT_SUCCESS;
+}
diff --git a/i/pc104/initrd/conf/busybox/util-linux/mdev.c b/i/pc104/initrd/conf/busybox/util-linux/mdev.c
new file mode 100644
index 0000000..f9f067d
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/util-linux/mdev.c
@@ -0,0 +1,270 @@
+/* vi: set sw=4 ts=4: */
+/*
+ *
+ * mdev - Mini udev for busybox
+ *
+ * Copyright 2005 Rob Landley <rob@landley.net>
+ * Copyright 2005 Frank Sorenson <frank@tuxrocks.com>
+ *
+ * Licensed under GPL version 2, see file LICENSE in this tarball for details.
+ */
+
+#include "busybox.h"
+#include "xregex.h"
+
+#define DEV_PATH "/dev"
+
+struct mdev_globals
+{
+ int root_major, root_minor;
+} mdev_globals;
+
+#define bbg mdev_globals
+
+/* mknod in /dev based on a path like "/sys/block/hda/hda1" */
+static void make_device(char *path, int delete)
+{
+ char *device_name;
+ int major, minor, type, len;
+ int mode = 0660;
+ uid_t uid = 0;
+ gid_t gid = 0;
+ char *temp = path + strlen(path);
+ char *command = NULL;
+
+ /* Try to read major/minor string. Note that the kernel puts \n after
+ * the data, so we don't need to worry about null terminating the string
+ * because sscanf() will stop at the first nondigit, which \n is. We
+ * also depend on path having writeable space after it. */
+
+ if (!delete) {
+ strcat(path, "/dev");
+ len = open_read_close(path, temp + 1, 64);
+ *temp++ = 0;
+ if (len < 1) return;
+ }
+
+ /* Determine device name, type, major and minor */
+
+ device_name = strrchr(path, '/') + 1;
+ type = path[5]=='c' ? S_IFCHR : S_IFBLK;
+
+ /* If we have a config file, look up permissions for this device */
+
+ if (ENABLE_FEATURE_MDEV_CONF) {
+ char *conf, *pos, *end;
+ int line, fd;
+
+ /* mmap the config file */
+ fd = open("/etc/mdev.conf", O_RDONLY);
+ if (fd < 0)
+ goto end_parse;
+ len = xlseek(fd, 0, SEEK_END);
+ conf = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0);
+ close(fd);
+ if (!conf)
+ goto end_parse;
+
+ line = 0;
+ /* Loop through lines in mmaped file*/
+ for (pos=conf; pos-conf<len;) {
+ int field;
+ char *end2;
+
+ line++;
+ /* find end of this line */
+ for (end=pos; end-conf<len && *end!='\n'; end++)
+ ;
+
+ /* Three fields: regex, uid:gid, mode */
+ for (field=0; field < (3 + ENABLE_FEATURE_MDEV_EXEC);
+ field++)
+ {
+ /* Skip whitespace */
+ while (pos<end && isspace(*pos)) pos++;
+ if (pos==end || *pos=='#') break;
+ for (end2=pos;
+ end2<end && !isspace(*end2) && *end2!='#'; end2++)
+ ;
+
+ if (field == 0) {
+ /* Regex to match this device */
+
+ char *regex = strndupa(pos, end2-pos);
+ regex_t match;
+ regmatch_t off;
+ int result;
+
+ /* Is this it? */
+ xregcomp(&match,regex, REG_EXTENDED);
+ result = regexec(&match, device_name, 1, &off, 0);
+ regfree(&match);
+
+ /* If not this device, skip rest of line */
+ if (result || off.rm_so
+ || off.rm_eo != strlen(device_name))
+ break;
+ }
+ if (field == 1) {
+ /* uid:gid */
+
+ char *s, *s2;
+
+ /* Find : */
+ for (s=pos; s<end2 && *s!=':'; s++)
+ ;
+ if (s == end2) break;
+
+ /* Parse UID */
+ uid = strtoul(pos, &s2, 10);
+ if (s != s2) {
+ struct passwd *pass;
+ pass = getpwnam(strndupa(pos, s-pos));
+ if (!pass) break;
+ uid = pass->pw_uid;
+ }
+ s++;
+ /* parse GID */
+ gid = strtoul(s, &s2, 10);
+ if (end2 != s2) {
+ struct group *grp;
+ grp = getgrnam(strndupa(s, end2-s));
+ if (!grp) break;
+ gid = grp->gr_gid;
+ }
+ }
+ if (field == 2) {
+ /* mode */
+
+ mode = strtoul(pos, &pos, 8);
+ if (pos != end2) break;
+ }
+ if (ENABLE_FEATURE_MDEV_EXEC && field == 3) {
+ // Command to run
+ const char *s = "@$*";
+ const char *s2;
+ s2 = strchr(s, *pos++);
+ if (!s2) {
+ // Force error
+ field = 1;
+ break;
+ }
+ if ((s2-s+1) & (1<<delete))
+ command = xstrndup(pos, end-pos);
+ }
+
+ pos = end2;
+ }
+
+ /* Did everything parse happily? */
+
+ if (field > 2) break;
+ if (field) bb_error_msg_and_die("bad line %d",line);
+
+ /* Next line */
+ pos = ++end;
+ }
+ munmap(conf, len);
+ end_parse: /* nothing */ ;
+ }
+
+ umask(0);
+ if (!delete) {
+ if (sscanf(temp, "%d:%d", &major, &minor) != 2) return;
+ if (mknod(device_name, mode | type, makedev(major, minor)) && errno != EEXIST)
+ bb_perror_msg_and_die("mknod %s", device_name);
+
+ if (major == bbg.root_major && minor == bbg.root_minor)
+ symlink(device_name, "root");
+
+ if (ENABLE_FEATURE_MDEV_CONF) chown(device_name, uid, gid);
+ }
+ if (command) {
+ int rc;
+ char *s;
+
+ s = xasprintf("MDEV=%s", device_name);
+ putenv(s);
+ rc = system(command);
+ s[4] = 0;
+ putenv(s);
+ free(s);
+ free(command);
+ if (rc == -1) bb_perror_msg_and_die("cannot run %s", command);
+ }
+ if (delete) unlink(device_name);
+}
+
+/* Recursive search of /sys/block or /sys/class. path must be a writeable
+ * buffer of size PATH_MAX containing the directory string to start at. */
+
+static void find_dev(char *path)
+{
+ DIR *dir;
+ size_t len = strlen(path);
+ struct dirent *entry;
+
+ dir = opendir(path);
+ if (dir == NULL)
+ return;
+
+ while ((entry = readdir(dir)) != NULL) {
+ struct stat st;
+
+ /* Skip "." and ".." (also skips hidden files, which is ok) */
+
+ if (entry->d_name[0] == '.')
+ continue;
+
+ // uClibc doesn't fill out entry->d_type reliably. so we use lstat().
+
+ snprintf(path+len, PATH_MAX-len, "/%s", entry->d_name);
+ if (!lstat(path, &st) && S_ISDIR(st.st_mode)) find_dev(path);
+ path[len] = 0;
+
+ /* If there's a dev entry, mknod it */
+
+ if (!strcmp(entry->d_name, "dev")) make_device(path, 0);
+ }
+
+ closedir(dir);
+}
+
+int mdev_main(int argc, char *argv[]);
+int mdev_main(int argc, char *argv[])
+{
+ char *action;
+ char *env_path;
+ RESERVE_CONFIG_BUFFER(temp,PATH_MAX);
+
+ xchdir(DEV_PATH);
+
+ /* Scan */
+
+ if (argc == 2 && !strcmp(argv[1],"-s")) {
+ struct stat st;
+
+ xstat("/", &st);
+ bbg.root_major = major(st.st_dev);
+ bbg.root_minor = minor(st.st_dev);
+ strcpy(temp,"/sys/block");
+ find_dev(temp);
+ strcpy(temp,"/sys/class");
+ find_dev(temp);
+
+ /* Hotplug */
+
+ } else {
+ action = getenv("ACTION");
+ env_path = getenv("DEVPATH");
+ if (!action || !env_path)
+ bb_show_usage();
+
+ sprintf(temp, "/sys%s", env_path);
+ if (!strcmp(action, "add")) make_device(temp,0);
+ else if (!strcmp(action, "remove")) make_device(temp,1);
+ }
+
+ if (ENABLE_FEATURE_CLEAN_UP) RELEASE_CONFIG_BUFFER(temp);
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/util-linux/minix.h b/i/pc104/initrd/conf/busybox/util-linux/minix.h
new file mode 100644
index 0000000..476f327
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/util-linux/minix.h
@@ -0,0 +1,76 @@
+/*
+ * This is the original minix inode layout on disk.
+ * Note the 8-bit gid and atime and ctime.
+ */
+struct minix1_inode {
+ uint16_t i_mode;
+ uint16_t i_uid;
+ uint32_t i_size;
+ uint32_t i_time;
+ uint8_t i_gid;
+ uint8_t i_nlinks;
+ uint16_t i_zone[9];
+};
+
+/*
+ * The new minix inode has all the time entries, as well as
+ * long block numbers and a third indirect block (7+1+1+1
+ * instead of 7+1+1). Also, some previously 8-bit values are
+ * now 16-bit. The inode is now 64 bytes instead of 32.
+ */
+struct minix2_inode {
+ uint16_t i_mode;
+ uint16_t i_nlinks;
+ uint16_t i_uid;
+ uint16_t i_gid;
+ uint32_t i_size;
+ uint32_t i_atime;
+ uint32_t i_mtime;
+ uint32_t i_ctime;
+ uint32_t i_zone[10];
+};
+
+/*
+ * minix super-block data on disk
+ */
+struct minix_super_block {
+ uint16_t s_ninodes;
+ uint16_t s_nzones;
+ uint16_t s_imap_blocks;
+ uint16_t s_zmap_blocks;
+ uint16_t s_firstdatazone;
+ uint16_t s_log_zone_size;
+ uint32_t s_max_size;
+ uint16_t s_magic;
+ uint16_t s_state;
+ uint32_t s_zones;
+};
+
+struct minix_dir_entry {
+ uint16_t inode;
+ char name[0];
+};
+
+/* Believe it or not, but mount.h has this one #defined */
+#undef BLOCK_SIZE
+
+enum {
+ BLOCK_SIZE = 1024,
+ BITS_PER_BLOCK = BLOCK_SIZE << 3,
+
+ MINIX_ROOT_INO = 1,
+ MINIX_BAD_INO = 2,
+
+ MINIX1_SUPER_MAGIC = 0x137F, /* original minix fs */
+ MINIX1_SUPER_MAGIC2 = 0x138F, /* minix fs, 30 char names */
+ MINIX2_SUPER_MAGIC = 0x2468, /* minix V2 fs */
+ MINIX2_SUPER_MAGIC2 = 0x2478, /* minix V2 fs, 30 char names */
+ MINIX_VALID_FS = 0x0001, /* clean fs */
+ MINIX_ERROR_FS = 0x0002, /* fs has errors */
+
+ INODE_SIZE1 = sizeof(struct minix1_inode),
+ INODE_SIZE2 = sizeof(struct minix2_inode),
+ MINIX1_INODES_PER_BLOCK = BLOCK_SIZE / sizeof(struct minix1_inode),
+ MINIX2_INODES_PER_BLOCK = BLOCK_SIZE / sizeof(struct minix2_inode),
+};
+
diff --git a/i/pc104/initrd/conf/busybox/util-linux/mkfs_minix.c b/i/pc104/initrd/conf/busybox/util-linux/mkfs_minix.c
new file mode 100644
index 0000000..d70d20f
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/util-linux/mkfs_minix.c
@@ -0,0 +1,718 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * mkfs.c - make a linux (minix) file-system.
+ *
+ * (C) 1991 Linus Torvalds. This file may be redistributed as per
+ * the Linux copyright.
+ */
+
+/*
+ * DD.MM.YY
+ *
+ * 24.11.91 - Time began. Used the fsck sources to get started.
+ *
+ * 25.11.91 - Corrected some bugs. Added support for ".badblocks"
+ * The algorithm for ".badblocks" is a bit weird, but
+ * it should work. Oh, well.
+ *
+ * 25.01.92 - Added the -l option for getting the list of bad blocks
+ * out of a named file. (Dave Rivers, rivers@ponds.uucp)
+ *
+ * 28.02.92 - Added %-information when using -c.
+ *
+ * 28.02.93 - Added support for other namelengths than the original
+ * 14 characters so that I can test the new kernel routines..
+ *
+ * 09.10.93 - Make exit status conform to that required by fsutil
+ * (Rik Faith, faith@cs.unc.edu)
+ *
+ * 31.10.93 - Added inode request feature, for backup floppies: use
+ * 32 inodes, for a news partition use more.
+ * (Scott Heavner, sdh@po.cwru.edu)
+ *
+ * 03.01.94 - Added support for file system valid flag.
+ * (Dr. Wettstein, greg%wind.uucp@plains.nodak.edu)
+ *
+ * 30.10.94 - added support for v2 filesystem
+ * (Andreas Schwab, schwab@issan.informatik.uni-dortmund.de)
+ *
+ * 09.11.94 - Added test to prevent overwrite of mounted fs adapted
+ * from Theodore Ts'o's (tytso@athena.mit.edu) mke2fs
+ * program. (Daniel Quinlan, quinlan@yggdrasil.com)
+ *
+ * 03.20.95 - Clear first 512 bytes of filesystem to make certain that
+ * the filesystem is not misidentified as a MS-DOS FAT filesystem.
+ * (Daniel Quinlan, quinlan@yggdrasil.com)
+ *
+ * 02.07.96 - Added small patch from Russell King to make the program a
+ * good deal more portable (janl@math.uio.no)
+ *
+ * Usage: mkfs [-c | -l filename ] [-v] [-nXX] [-iXX] device [size-in-blocks]
+ *
+ * -c for readability checking (SLOW!)
+ * -l for getting a list of bad blocks from a file.
+ * -n for namelength (currently the kernel only uses 14 or 30)
+ * -i for number of inodes
+ * -v for v2 filesystem
+ *
+ * The device may be a block device or a image of one, but this isn't
+ * enforced (but it's not much fun on a character device :-).
+ *
+ * Modified for BusyBox by Erik Andersen <andersen@debian.org> --
+ * removed getopt based parser and added a hand rolled one.
+ */
+
+#include "busybox.h"
+#include <mntent.h>
+
+#include "minix.h"
+
+#define DEBUG 0
+
+/* If debugging, store the very same times/uids/gids for image consistency */
+#if DEBUG
+# define CUR_TIME 0
+# define GETUID 0
+# define GETGID 0
+#else
+# define CUR_TIME time(NULL)
+# define GETUID getuid()
+# define GETGID getgid()
+#endif
+
+enum {
+ MAX_GOOD_BLOCKS = 512,
+ TEST_BUFFER_BLOCKS = 16,
+};
+
+#if !ENABLE_FEATURE_MINIX2
+enum { version2 = 0 };
+#endif
+
+struct globals {
+
+ int dev_fd;
+
+#if ENABLE_FEATURE_MINIX2
+ int version2;
+#define version2 G.version2
+#endif
+ char *device_name;
+ uint32_t total_blocks;
+ int badblocks;
+ int namelen;
+ int dirsize;
+ int magic;
+ char *inode_buffer;
+ char *inode_map;
+ char *zone_map;
+ int used_good_blocks;
+ unsigned long req_nr_inodes;
+ unsigned currently_testing;
+
+
+ char root_block[BLOCK_SIZE];
+ char super_block_buffer[BLOCK_SIZE];
+ char boot_block_buffer[512];
+ unsigned short good_blocks_table[MAX_GOOD_BLOCKS];
+ /* check_blocks(): buffer[] was the biggest static in entire bbox */
+ char check_blocks_buffer[BLOCK_SIZE * TEST_BUFFER_BLOCKS];
+};
+
+#define G (*ptr_to_globals)
+
+static ATTRIBUTE_ALWAYS_INLINE unsigned div_roundup(unsigned size, unsigned n)
+{
+ return (size + n-1) / n;
+}
+
+#define INODE_BUF1 (((struct minix1_inode*)G.inode_buffer) - 1)
+#define INODE_BUF2 (((struct minix2_inode*)G.inode_buffer) - 1)
+
+#define SB (*(struct minix_super_block*)G.super_block_buffer)
+
+#define SB_INODES (SB.s_ninodes)
+#define SB_IMAPS (SB.s_imap_blocks)
+#define SB_ZMAPS (SB.s_zmap_blocks)
+#define SB_FIRSTZONE (SB.s_firstdatazone)
+#define SB_ZONE_SIZE (SB.s_log_zone_size)
+#define SB_MAXSIZE (SB.s_max_size)
+#define SB_MAGIC (SB.s_magic)
+
+#if !ENABLE_FEATURE_MINIX2
+# define SB_ZONES (SB.s_nzones)
+# define INODE_BLOCKS div_roundup(SB_INODES, MINIX1_INODES_PER_BLOCK)
+#else
+# define SB_ZONES (version2 ? SB.s_zones : SB.s_nzones)
+# define INODE_BLOCKS div_roundup(SB_INODES, \
+ version2 ? MINIX2_INODES_PER_BLOCK : MINIX1_INODES_PER_BLOCK)
+#endif
+
+#define INODE_BUFFER_SIZE (INODE_BLOCKS * BLOCK_SIZE)
+#define NORM_FIRSTZONE (2 + SB_IMAPS + SB_ZMAPS + INODE_BLOCKS)
+
+/* Before you ask "where they come from?": */
+/* setbit/clrbit are supplied by sys/param.h */
+
+static int minix_bit(const char* a, unsigned i)
+{
+ return a[i >> 3] & (1<<(i & 7));
+}
+
+static void minix_setbit(char *a, unsigned i)
+{
+ setbit(a, i);
+}
+static void minix_clrbit(char *a, unsigned i)
+{
+ clrbit(a, i);
+}
+
+/* Note: do not assume 0/1, it is 0/nonzero */
+#define zone_in_use(x) minix_bit(G.zone_map,(x)-SB_FIRSTZONE+1)
+/*#define inode_in_use(x) minix_bit(G.inode_map,(x))*/
+
+#define mark_inode(x) minix_setbit(G.inode_map,(x))
+#define unmark_inode(x) minix_clrbit(G.inode_map,(x))
+#define mark_zone(x) minix_setbit(G.zone_map,(x)-SB_FIRSTZONE+1)
+#define unmark_zone(x) minix_clrbit(G.zone_map,(x)-SB_FIRSTZONE+1)
+
+#ifndef BLKGETSIZE
+# define BLKGETSIZE _IO(0x12,96) /* return device size */
+#endif
+
+
+static long valid_offset(int fd, int offset)
+{
+ char ch;
+
+ if (lseek(fd, offset, SEEK_SET) < 0)
+ return 0;
+ if (read(fd, &ch, 1) < 1)
+ return 0;
+ return 1;
+}
+
+static int count_blocks(int fd)
+{
+ int high, low;
+
+ low = 0;
+ for (high = 1; valid_offset(fd, high); high *= 2)
+ low = high;
+
+ while (low < high - 1) {
+ const int mid = (low + high) / 2;
+
+ if (valid_offset(fd, mid))
+ low = mid;
+ else
+ high = mid;
+ }
+ valid_offset(fd, 0);
+ return (low + 1);
+}
+
+static int get_size(const char *file)
+{
+ int fd;
+ long size;
+
+ fd = xopen(file, O_RDWR);
+ if (ioctl(fd, BLKGETSIZE, &size) >= 0) {
+ close(fd);
+ return (size * 512);
+ }
+
+ size = count_blocks(fd);
+ close(fd);
+ return size;
+}
+
+static void write_tables(void)
+{
+ /* Mark the super block valid. */
+ SB.s_state |= MINIX_VALID_FS;
+ SB.s_state &= ~MINIX_ERROR_FS;
+
+ msg_eol = "seek to 0 failed";
+ xlseek(G.dev_fd, 0, SEEK_SET);
+
+ msg_eol = "cannot clear boot sector";
+ xwrite(G.dev_fd, G.boot_block_buffer, 512);
+
+ msg_eol = "seek to BLOCK_SIZE failed";
+ xlseek(G.dev_fd, BLOCK_SIZE, SEEK_SET);
+
+ msg_eol = "cannot write superblock";
+ xwrite(G.dev_fd, G.super_block_buffer, BLOCK_SIZE);
+
+ msg_eol = "cannot write inode map";
+ xwrite(G.dev_fd, G.inode_map, SB_IMAPS * BLOCK_SIZE);
+
+ msg_eol = "cannot write zone map";
+ xwrite(G.dev_fd, G.zone_map, SB_ZMAPS * BLOCK_SIZE);
+
+ msg_eol = "cannot write inodes";
+ xwrite(G.dev_fd, G.inode_buffer, INODE_BUFFER_SIZE);
+
+ msg_eol = "\n";
+}
+
+static void write_block(int blk, char *buffer)
+{
+ xlseek(G.dev_fd, blk * BLOCK_SIZE, SEEK_SET);
+ xwrite(G.dev_fd, buffer, BLOCK_SIZE);
+}
+
+static int get_free_block(void)
+{
+ int blk;
+
+ if (G.used_good_blocks + 1 >= MAX_GOOD_BLOCKS)
+ bb_error_msg_and_die("too many bad blocks");
+ if (G.used_good_blocks)
+ blk = G.good_blocks_table[G.used_good_blocks - 1] + 1;
+ else
+ blk = SB_FIRSTZONE;
+ while (blk < SB_ZONES && zone_in_use(blk))
+ blk++;
+ if (blk >= SB_ZONES)
+ bb_error_msg_and_die("not enough good blocks");
+ G.good_blocks_table[G.used_good_blocks] = blk;
+ G.used_good_blocks++;
+ return blk;
+}
+
+static void mark_good_blocks(void)
+{
+ int blk;
+
+ for (blk = 0; blk < G.used_good_blocks; blk++)
+ mark_zone(G.good_blocks_table[blk]);
+}
+
+static int next(int zone)
+{
+ if (!zone)
+ zone = SB_FIRSTZONE - 1;
+ while (++zone < SB_ZONES)
+ if (zone_in_use(zone))
+ return zone;
+ return 0;
+}
+
+static void make_bad_inode(void)
+{
+ struct minix1_inode *inode = &INODE_BUF1[MINIX_BAD_INO];
+ int i, j, zone;
+ int ind = 0, dind = 0;
+ unsigned short ind_block[BLOCK_SIZE >> 1];
+ unsigned short dind_block[BLOCK_SIZE >> 1];
+
+#define NEXT_BAD (zone = next(zone))
+
+ if (!G.badblocks)
+ return;
+ mark_inode(MINIX_BAD_INO);
+ inode->i_nlinks = 1;
+ /* BTW, setting this makes all images different */
+ /* it's harder to check for bugs then - diff isn't helpful :(... */
+ inode->i_time = CUR_TIME;
+ inode->i_mode = S_IFREG + 0000;
+ inode->i_size = G.badblocks * BLOCK_SIZE;
+ zone = next(0);
+ for (i = 0; i < 7; i++) {
+ inode->i_zone[i] = zone;
+ if (!NEXT_BAD)
+ goto end_bad;
+ }
+ inode->i_zone[7] = ind = get_free_block();
+ memset(ind_block, 0, BLOCK_SIZE);
+ for (i = 0; i < 512; i++) {
+ ind_block[i] = zone;
+ if (!NEXT_BAD)
+ goto end_bad;
+ }
+ inode->i_zone[8] = dind = get_free_block();
+ memset(dind_block, 0, BLOCK_SIZE);
+ for (i = 0; i < 512; i++) {
+ write_block(ind, (char *) ind_block);
+ dind_block[i] = ind = get_free_block();
+ memset(ind_block, 0, BLOCK_SIZE);
+ for (j = 0; j < 512; j++) {
+ ind_block[j] = zone;
+ if (!NEXT_BAD)
+ goto end_bad;
+ }
+ }
+ bb_error_msg_and_die("too many bad blocks");
+ end_bad:
+ if (ind)
+ write_block(ind, (char *) ind_block);
+ if (dind)
+ write_block(dind, (char *) dind_block);
+}
+
+#if ENABLE_FEATURE_MINIX2
+static void make_bad_inode2(void)
+{
+ struct minix2_inode *inode = &INODE_BUF2[MINIX_BAD_INO];
+ int i, j, zone;
+ int ind = 0, dind = 0;
+ unsigned long ind_block[BLOCK_SIZE >> 2];
+ unsigned long dind_block[BLOCK_SIZE >> 2];
+
+ if (!G.badblocks)
+ return;
+ mark_inode(MINIX_BAD_INO);
+ inode->i_nlinks = 1;
+ inode->i_atime = inode->i_mtime = inode->i_ctime = CUR_TIME;
+ inode->i_mode = S_IFREG + 0000;
+ inode->i_size = G.badblocks * BLOCK_SIZE;
+ zone = next(0);
+ for (i = 0; i < 7; i++) {
+ inode->i_zone[i] = zone;
+ if (!NEXT_BAD)
+ goto end_bad;
+ }
+ inode->i_zone[7] = ind = get_free_block();
+ memset(ind_block, 0, BLOCK_SIZE);
+ for (i = 0; i < 256; i++) {
+ ind_block[i] = zone;
+ if (!NEXT_BAD)
+ goto end_bad;
+ }
+ inode->i_zone[8] = dind = get_free_block();
+ memset(dind_block, 0, BLOCK_SIZE);
+ for (i = 0; i < 256; i++) {
+ write_block(ind, (char *) ind_block);
+ dind_block[i] = ind = get_free_block();
+ memset(ind_block, 0, BLOCK_SIZE);
+ for (j = 0; j < 256; j++) {
+ ind_block[j] = zone;
+ if (!NEXT_BAD)
+ goto end_bad;
+ }
+ }
+ /* Could make triple indirect block here */
+ bb_error_msg_and_die("too many bad blocks");
+ end_bad:
+ if (ind)
+ write_block(ind, (char *) ind_block);
+ if (dind)
+ write_block(dind, (char *) dind_block);
+}
+#else
+void make_bad_inode2(void);
+#endif
+
+static void make_root_inode(void)
+{
+ struct minix1_inode *inode = &INODE_BUF1[MINIX_ROOT_INO];
+
+ mark_inode(MINIX_ROOT_INO);
+ inode->i_zone[0] = get_free_block();
+ inode->i_nlinks = 2;
+ inode->i_time = CUR_TIME;
+ if (G.badblocks)
+ inode->i_size = 3 * G.dirsize;
+ else {
+ G.root_block[2 * G.dirsize] = '\0';
+ G.root_block[2 * G.dirsize + 1] = '\0';
+ inode->i_size = 2 * G.dirsize;
+ }
+ inode->i_mode = S_IFDIR + 0755;
+ inode->i_uid = GETUID;
+ if (inode->i_uid)
+ inode->i_gid = GETGID;
+ write_block(inode->i_zone[0], G.root_block);
+}
+
+#if ENABLE_FEATURE_MINIX2
+static void make_root_inode2(void)
+{
+ struct minix2_inode *inode = &INODE_BUF2[MINIX_ROOT_INO];
+
+ mark_inode(MINIX_ROOT_INO);
+ inode->i_zone[0] = get_free_block();
+ inode->i_nlinks = 2;
+ inode->i_atime = inode->i_mtime = inode->i_ctime = CUR_TIME;
+ if (G.badblocks)
+ inode->i_size = 3 * G.dirsize;
+ else {
+ G.root_block[2 * G.dirsize] = '\0';
+ G.root_block[2 * G.dirsize + 1] = '\0';
+ inode->i_size = 2 * G.dirsize;
+ }
+ inode->i_mode = S_IFDIR + 0755;
+ inode->i_uid = GETUID;
+ if (inode->i_uid)
+ inode->i_gid = GETGID;
+ write_block(inode->i_zone[0], G.root_block);
+}
+#else
+void make_root_inode2(void);
+#endif
+
+/*
+ * Perform a test of a block; return the number of
+ * blocks readable.
+ */
+static size_t do_check(char *buffer, size_t try, unsigned current_block)
+{
+ ssize_t got;
+
+ /* Seek to the correct loc. */
+ msg_eol = "seek failed during testing of blocks";
+ xlseek(G.dev_fd, current_block * BLOCK_SIZE, SEEK_SET);
+ msg_eol = "\n";
+
+ /* Try the read */
+ got = read(G.dev_fd, buffer, try * BLOCK_SIZE);
+ if (got < 0)
+ got = 0;
+ try = ((size_t)got) / BLOCK_SIZE;
+
+ if (got & (BLOCK_SIZE - 1))
+ fprintf(stderr, "Short read at block %u\n", (unsigned)(current_block + try));
+ return try;
+}
+
+static void alarm_intr(int alnum)
+{
+ if (G.currently_testing >= SB_ZONES)
+ return;
+ signal(SIGALRM, alarm_intr);
+ alarm(5);
+ if (!G.currently_testing)
+ return;
+ printf("%d ...", G.currently_testing);
+ fflush(stdout);
+}
+
+static void check_blocks(void)
+{
+ size_t try, got;
+
+ G.currently_testing = 0;
+ signal(SIGALRM, alarm_intr);
+ alarm(5);
+ while (G.currently_testing < SB_ZONES) {
+ msg_eol = "seek failed in check_blocks";
+ xlseek(G.dev_fd, G.currently_testing * BLOCK_SIZE, SEEK_SET);
+ msg_eol = "\n";
+ try = TEST_BUFFER_BLOCKS;
+ if (G.currently_testing + try > SB_ZONES)
+ try = SB_ZONES - G.currently_testing;
+ got = do_check(G.check_blocks_buffer, try, G.currently_testing);
+ G.currently_testing += got;
+ if (got == try)
+ continue;
+ if (G.currently_testing < SB_FIRSTZONE)
+ bb_error_msg_and_die("bad blocks before data-area: cannot make fs");
+ mark_zone(G.currently_testing);
+ G.badblocks++;
+ G.currently_testing++;
+ }
+ alarm(0);
+ printf("%d bad block(s)\n", G.badblocks);
+}
+
+static void get_list_blocks(char *filename)
+{
+ FILE *listfile;
+ unsigned long blockno;
+
+ listfile = xfopen(filename, "r");
+ while (!feof(listfile)) {
+ fscanf(listfile, "%ld\n", &blockno);
+ mark_zone(blockno);
+ G.badblocks++;
+ }
+ printf("%d bad block(s)\n", G.badblocks);
+}
+
+static void setup_tables(void)
+{
+ unsigned long inodes;
+ unsigned norm_firstzone;
+ unsigned sb_zmaps;
+ unsigned i;
+
+ /* memset(G.super_block_buffer, 0, BLOCK_SIZE); */
+ /* memset(G.boot_block_buffer, 0, 512); */
+ SB_MAGIC = G.magic;
+ SB_ZONE_SIZE = 0;
+ SB_MAXSIZE = version2 ? 0x7fffffff : (7 + 512 + 512 * 512) * 1024;
+ if (version2)
+ SB.s_zones = G.total_blocks;
+ else
+ SB.s_nzones = G.total_blocks;
+
+ /* some magic nrs: 1 inode / 3 blocks */
+ if (G.req_nr_inodes == 0)
+ inodes = G.total_blocks / 3;
+ else
+ inodes = G.req_nr_inodes;
+ /* Round up inode count to fill block size */
+ if (version2)
+ inodes = (inodes + MINIX2_INODES_PER_BLOCK - 1) &
+ ~(MINIX2_INODES_PER_BLOCK - 1);
+ else
+ inodes = (inodes + MINIX1_INODES_PER_BLOCK - 1) &
+ ~(MINIX1_INODES_PER_BLOCK - 1);
+ if (inodes > 65535)
+ inodes = 65535;
+ SB_INODES = inodes;
+ SB_IMAPS = div_roundup(SB_INODES + 1, BITS_PER_BLOCK);
+
+ /* Real bad hack but overwise mkfs.minix can be thrown
+ * in infinite loop...
+ * try:
+ * dd if=/dev/zero of=test.fs count=10 bs=1024
+ * mkfs.minix -i 200 test.fs
+ */
+ /* This code is not insane: NORM_FIRSTZONE is not a constant,
+ * it is calculated from SB_INODES, SB_IMAPS and SB_ZMAPS */
+ i = 999;
+ SB_ZMAPS = 0;
+ do {
+ norm_firstzone = NORM_FIRSTZONE;
+ sb_zmaps = div_roundup(G.total_blocks - norm_firstzone + 1, BITS_PER_BLOCK);
+ if (SB_ZMAPS == sb_zmaps) goto got_it;
+ SB_ZMAPS = sb_zmaps;
+ /* new SB_ZMAPS, need to recalc NORM_FIRSTZONE */
+ } while (--i);
+ bb_error_msg_and_die("incompatible size/inode count, try different -i N");
+ got_it:
+
+ SB_FIRSTZONE = norm_firstzone;
+ G.inode_map = xmalloc(SB_IMAPS * BLOCK_SIZE);
+ G.zone_map = xmalloc(SB_ZMAPS * BLOCK_SIZE);
+ memset(G.inode_map, 0xff, SB_IMAPS * BLOCK_SIZE);
+ memset(G.zone_map, 0xff, SB_ZMAPS * BLOCK_SIZE);
+ for (i = SB_FIRSTZONE; i < SB_ZONES; i++)
+ unmark_zone(i);
+ for (i = MINIX_ROOT_INO; i <= SB_INODES; i++)
+ unmark_inode(i);
+ G.inode_buffer = xzalloc(INODE_BUFFER_SIZE);
+ printf("%ld inodes\n", (long)SB_INODES);
+ printf("%ld blocks\n", (long)SB_ZONES);
+ printf("Firstdatazone=%ld (%ld)\n", (long)SB_FIRSTZONE, (long)norm_firstzone);
+ printf("Zonesize=%d\n", BLOCK_SIZE << SB_ZONE_SIZE);
+ printf("Maxsize=%ld\n", (long)SB_MAXSIZE);
+}
+
+int mkfs_minix_main(int argc, char **argv);
+int mkfs_minix_main(int argc, char **argv)
+{
+ struct mntent *mp;
+ unsigned opt;
+ char *tmp;
+ struct stat statbuf;
+ char *str_i, *str_n;
+ char *listfile = NULL;
+
+ PTR_TO_GLOBALS = xzalloc(sizeof(G));
+/* default (changed to 30, per Linus's suggestion, Sun Nov 21 08:05:07 1993) */
+ G.namelen = 30;
+ G.dirsize = 32;
+ G.magic = MINIX1_SUPER_MAGIC2;
+
+ if (INODE_SIZE1 * MINIX1_INODES_PER_BLOCK != BLOCK_SIZE)
+ bb_error_msg_and_die("bad inode size");
+#if ENABLE_FEATURE_MINIX2
+ if (INODE_SIZE2 * MINIX2_INODES_PER_BLOCK != BLOCK_SIZE)
+ bb_error_msg_and_die("bad inode size");
+#endif
+
+ opt = getopt32(argc, argv, "ci:l:n:v", &str_i, &listfile, &str_n);
+ argv += optind;
+ //if (opt & 1) -c
+ if (opt & 2) G.req_nr_inodes = xatoul(str_i); // -i
+ //if (opt & 4) -l
+ if (opt & 8) { // -n
+ G.namelen = xatoi_u(str_n);
+ if (G.namelen == 14) G.magic = MINIX1_SUPER_MAGIC;
+ else if (G.namelen == 30) G.magic = MINIX1_SUPER_MAGIC2;
+ else bb_show_usage();
+ G.dirsize = G.namelen + 2;
+ }
+ if (opt & 0x10) { // -v
+#if ENABLE_FEATURE_MINIX2
+ version2 = 1;
+#else
+ bb_error_msg_and_die("not compiled with minix v2 support");
+#endif
+ }
+
+ G.device_name = *argv++;
+ if (!G.device_name)
+ bb_show_usage();
+ if (*argv)
+ G.total_blocks = xatou32(*argv);
+ else
+ G.total_blocks = get_size(G.device_name) / 1024;
+
+ if (G.total_blocks < 10)
+ bb_error_msg_and_die("must have at least 10 blocks");
+
+ if (version2) {
+ G.magic = MINIX2_SUPER_MAGIC2;
+ if (G.namelen == 14)
+ G.magic = MINIX2_SUPER_MAGIC;
+ } else if (G.total_blocks > 65535)
+ G.total_blocks = 65535;
+
+ /* Check if it is mounted */
+ mp = find_mount_point(G.device_name, NULL);
+ if (mp && strcmp(G.device_name, mp->mnt_fsname) == 0)
+ bb_error_msg_and_die("%s is mounted on %s; "
+ "refusing to make a filesystem",
+ G.device_name, mp->mnt_dir);
+
+ G.dev_fd = xopen(G.device_name, O_RDWR);
+ if (fstat(G.dev_fd, &statbuf) < 0)
+ bb_error_msg_and_die("cannot stat %s", G.device_name);
+ if (!S_ISBLK(statbuf.st_mode))
+ opt &= ~1; // clear -c (check)
+
+/* I don't know why someone has special code to prevent mkfs.minix
+ * on IDE devices. Why IDE but not SCSI, etc?... */
+#if 0
+ else if (statbuf.st_rdev == 0x0300 || statbuf.st_rdev == 0x0340)
+ /* what is this? */
+ bb_error_msg_and_die("will not try "
+ "to make filesystem on '%s'", G.device_name);
+#endif
+
+ tmp = G.root_block;
+ *(short *) tmp = 1;
+ strcpy(tmp + 2, ".");
+ tmp += G.dirsize;
+ *(short *) tmp = 1;
+ strcpy(tmp + 2, "..");
+ tmp += G.dirsize;
+ *(short *) tmp = 2;
+ strcpy(tmp + 2, ".badblocks");
+
+ setup_tables();
+
+ if (opt & 1) // -c ?
+ check_blocks();
+ else if (listfile)
+ get_list_blocks(listfile);
+
+ if (version2) {
+ make_root_inode2();
+ make_bad_inode2();
+ } else {
+ make_root_inode();
+ make_bad_inode();
+ }
+
+ mark_good_blocks();
+ write_tables();
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/util-linux/mkswap.c b/i/pc104/initrd/conf/busybox/util-linux/mkswap.c
new file mode 100644
index 0000000..ac894d5
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/util-linux/mkswap.c
@@ -0,0 +1,48 @@
+/* vi: set sw=4 ts=4: */
+/* mkswap.c - format swap device (Linux v1 only)
+ *
+ * Copyright 2006 Rob Landley <rob@landley.net>
+ *
+ * Licensed under GPL version 2, see file LICENSE in this tarball for details.
+ */
+
+#include "busybox.h"
+
+int mkswap_main(int argc, char *argv[]);
+int mkswap_main(int argc, char *argv[])
+{
+ int fd, pagesize;
+ off_t len;
+ unsigned int hdr[129];
+
+ // No options supported.
+
+ if (argc != 2) bb_show_usage();
+
+ // Figure out how big the device is and announce our intentions.
+
+ fd = xopen(argv[1], O_RDWR);
+ len = fdlength(fd);
+ pagesize = getpagesize();
+ printf("Setting up swapspace version 1, size = %"OFF_FMT"d bytes\n",
+ len - pagesize);
+
+ // Make a header.
+
+ memset(hdr, 0, sizeof(hdr));
+ hdr[0] = 1;
+ hdr[1] = (len / pagesize) - 1;
+
+ // Write the header. Sync to disk because some kernel versions check
+ // signature on disk (not in cache) during swapon.
+
+ xlseek(fd, 1024, SEEK_SET);
+ xwrite(fd, hdr, sizeof(hdr));
+ xlseek(fd, pagesize-10, SEEK_SET);
+ xwrite(fd, "SWAPSPACE2", 10);
+ fsync(fd);
+
+ if (ENABLE_FEATURE_CLEAN_UP) close(fd);
+
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/util-linux/more.c b/i/pc104/initrd/conf/busybox/util-linux/more.c
new file mode 100644
index 0000000..2a38ef3
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/util-linux/more.c
@@ -0,0 +1,173 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini more implementation for busybox
+ *
+ * Copyright (C) 1995, 1996 by Bruce Perens <bruce@pixar.com>.
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Latest version blended together by Erik Andersen <andersen@codepoet.org>,
+ * based on the original more implementation by Bruce, and code from the
+ * Debian boot-floppies team.
+ *
+ * Termios corrects by Vladimir Oleynik <dzo@simtreas.ru>
+ *
+ * Licensed under GPLv2 or later, see file License in this tarball for details.
+ */
+
+#include "busybox.h"
+
+
+#if ENABLE_FEATURE_USE_TERMIOS
+static int cin_fileno;
+#include <termios.h>
+#define setTermSettings(fd, argp) tcsetattr(fd, TCSANOW, argp)
+#define getTermSettings(fd, argp) tcgetattr(fd, argp);
+
+static struct termios initial_settings, new_settings;
+
+static void set_tty_to_initial_mode(void)
+{
+ setTermSettings(cin_fileno, &initial_settings);
+}
+
+static void gotsig(int sig)
+{
+ putchar('\n');
+ exit(EXIT_FAILURE);
+}
+#endif /* FEATURE_USE_TERMIOS */
+
+
+int more_main(int argc, char **argv);
+int more_main(int argc, char **argv)
+{
+ int c, lines, input = 0;
+ int please_display_more_prompt = 0;
+ struct stat st;
+ FILE *file;
+ FILE *cin;
+ int len, page_height;
+ int terminal_width;
+ int terminal_height;
+
+ argv++;
+ /* Another popular pager, most, detects when stdout
+ * is not a tty and turns into cat. This makes sense. */
+ if (!isatty(STDOUT_FILENO))
+ return bb_cat(argv);
+ cin = fopen(CURRENT_TTY, "r");
+ if (!cin)
+ return bb_cat(argv);
+
+#if ENABLE_FEATURE_USE_TERMIOS
+ cin_fileno = fileno(cin);
+ getTermSettings(cin_fileno, &initial_settings);
+ new_settings = initial_settings;
+ new_settings.c_lflag &= ~ICANON;
+ new_settings.c_lflag &= ~ECHO;
+ new_settings.c_cc[VMIN] = 1;
+ new_settings.c_cc[VTIME] = 0;
+ setTermSettings(cin_fileno, &new_settings);
+ atexit(set_tty_to_initial_mode);
+ signal(SIGINT, gotsig);
+ signal(SIGQUIT, gotsig);
+ signal(SIGTERM, gotsig);
+#endif
+ please_display_more_prompt = 2;
+
+ do {
+ file = stdin;
+ if (*argv) {
+ file = fopen_or_warn(*argv, "r");
+ if (!file)
+ continue;
+ }
+ st.st_size = 0;
+ fstat(fileno(file), &st);
+
+ please_display_more_prompt &= ~1;
+ /* never returns w, h <= 1 */
+ get_terminal_width_height(fileno(cin), &terminal_width, &terminal_height);
+ terminal_width -= 1;
+ terminal_height -= 1;
+
+ len = 0;
+ lines = 0;
+ page_height = terminal_height;
+ while ((c = getc(file)) != EOF) {
+
+ if ((please_display_more_prompt & 3) == 3) {
+ len = printf("--More-- ");
+ if (/*file != stdin &&*/ st.st_size > 0) {
+ len += printf("(%d%% of %"OFF_FMT"d bytes)",
+ (int) (ftello(file)*100 / st.st_size),
+ st.st_size);
+ }
+ fflush(stdout);
+
+ /*
+ * We've just displayed the "--More--" prompt, so now we need
+ * to get input from the user.
+ */
+ input = getc(cin);
+#if !ENABLE_FEATURE_USE_TERMIOS
+ printf("\033[A"); /* up cursor */
+#endif
+ /* Erase the "More" message */
+ printf("\r%*s\r", len, "");
+ len = 0;
+ lines = 0;
+ /* Bottom line on page will become top line
+ * after one page forward. Thus -1: */
+ page_height = terminal_height - 1;
+ please_display_more_prompt &= ~1;
+
+ if (input == 'q')
+ goto end;
+ }
+
+ /*
+ * There are two input streams to worry about here:
+ *
+ * c : the character we are reading from the file being "mored"
+ * input : a character received from the keyboard
+ *
+ * If we hit a newline in the _file_ stream, we want to test and
+ * see if any characters have been hit in the _input_ stream. This
+ * allows the user to quit while in the middle of a file.
+ */
+ if (c == '\n') {
+ /* increment by just one line if we are at
+ * the end of this line */
+ if (input == '\n')
+ please_display_more_prompt |= 1;
+ /* Adjust the terminal height for any overlap, so that
+ * no lines get lost off the top. */
+ if (len >= terminal_width) {
+ int quot, rem;
+ quot = len / terminal_width;
+ rem = len - (quot * terminal_width);
+ page_height -= (quot - 1);
+ if (rem)
+ page_height--;
+ }
+ if (++lines >= page_height) {
+ please_display_more_prompt |= 1;
+ }
+ len = 0;
+ }
+ /*
+ * If we just read a newline from the file being 'mored' and any
+ * key other than a return is hit, scroll by one page
+ */
+ putc(c, stdout);
+ /* My small mind cannot fathom tabs, backspaces,
+ * and UTF-8 */
+ len++;
+ }
+ fclose(file);
+ fflush(stdout);
+ } while (*argv && *++argv);
+ end:
+ return 0;
+}
diff --git a/i/pc104/initrd/conf/busybox/util-linux/mount.c b/i/pc104/initrd/conf/busybox/util-linux/mount.c
new file mode 100644
index 0000000..567514c
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/util-linux/mount.c
@@ -0,0 +1,1742 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini mount implementation for busybox
+ *
+ * Copyright (C) 1995, 1996 by Bruce Perens <bruce@pixar.com>.
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ * Copyright (C) 2005-2006 by Rob Landley <rob@landley.net>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/* Design notes: There is no spec for mount. Remind me to write one.
+
+ mount_main() calls singlemount() which calls mount_it_now().
+
+ mount_main() can loop through /etc/fstab for mount -a
+ singlemount() can loop through /etc/filesystems for fstype detection.
+ mount_it_now() does the actual mount.
+*/
+
+#include "busybox.h"
+#include <mntent.h>
+
+/* Needed for nfs support only... */
+#include <syslog.h>
+#include <sys/utsname.h>
+#undef TRUE
+#undef FALSE
+#include <rpc/rpc.h>
+#include <rpc/pmap_prot.h>
+#include <rpc/pmap_clnt.h>
+
+
+#if defined(__dietlibc__)
+/* 16.12.2006, Sampo Kellomaki (sampo@iki.fi)
+ * dietlibc-0.30 does not have implementation of getmntent_r() */
+/* OTOH: why we use getmntent_r instead of getmntent? TODO... */
+struct mntent *getmntent_r(FILE* stream, struct mntent* result, char* buffer, int bufsize)
+{
+ /* *** XXX FIXME WARNING: This hack is NOT thread safe. --Sampo */
+ struct mntent* ment = getmntent(stream);
+ memcpy(result, ment, sizeof(struct mntent));
+ return result;
+}
+#endif
+
+
+// Not real flags, but we want to be able to check for this.
+enum {
+ MOUNT_USERS = (1<<28)*ENABLE_DESKTOP,
+ MOUNT_NOAUTO = (1<<29),
+ MOUNT_SWAP = (1<<30),
+};
+// TODO: more "user" flag compatibility.
+// "user" option (from mount manpage):
+// Only the user that mounted a filesystem can unmount it again.
+// If any user should be able to unmount, then use users instead of user
+// in the fstab line. The owner option is similar to the user option,
+// with the restriction that the user must be the owner of the special file.
+// This may be useful e.g. for /dev/fd if a login script makes
+// the console user owner of this device.
+
+/* Standard mount options (from -o options or --options), with corresponding
+ * flags */
+
+struct {
+ const char *name;
+ long flags;
+} static mount_options[] = {
+ // MS_FLAGS set a bit. ~MS_FLAGS disable that bit. 0 flags are NOPs.
+
+ USE_FEATURE_MOUNT_LOOP(
+ {"loop", 0},
+ )
+
+ USE_FEATURE_MOUNT_FSTAB(
+ {"defaults", 0},
+ /* {"quiet", 0}, - do not filter out, vfat wants to see it */
+ {"noauto", MOUNT_NOAUTO},
+ {"swap", MOUNT_SWAP},
+ USE_DESKTOP({"user", MOUNT_USERS},)
+ USE_DESKTOP({"users", MOUNT_USERS},)
+ )
+
+ USE_FEATURE_MOUNT_FLAGS(
+ // vfs flags
+ {"nosuid", MS_NOSUID},
+ {"suid", ~MS_NOSUID},
+ {"dev", ~MS_NODEV},
+ {"nodev", MS_NODEV},
+ {"exec", ~MS_NOEXEC},
+ {"noexec", MS_NOEXEC},
+ {"sync", MS_SYNCHRONOUS},
+ {"async", ~MS_SYNCHRONOUS},
+ {"atime", ~MS_NOATIME},
+ {"noatime", MS_NOATIME},
+ {"diratime", ~MS_NODIRATIME},
+ {"nodiratime", MS_NODIRATIME},
+ {"loud", ~MS_SILENT},
+
+ // action flags
+
+ {"bind", MS_BIND},
+ {"move", MS_MOVE},
+ {"shared", MS_SHARED},
+ {"slave", MS_SLAVE},
+ {"private", MS_PRIVATE},
+ {"unbindable", MS_UNBINDABLE},
+ {"rshared", MS_SHARED|MS_RECURSIVE},
+ {"rslave", MS_SLAVE|MS_RECURSIVE},
+ {"rprivate", MS_SLAVE|MS_RECURSIVE},
+ {"runbindable", MS_UNBINDABLE|MS_RECURSIVE},
+ )
+
+ // Always understood.
+
+ {"ro", MS_RDONLY}, // vfs flag
+ {"rw", ~MS_RDONLY}, // vfs flag
+ {"remount", MS_REMOUNT}, // action flag
+};
+
+#define VECTOR_SIZE(v) (sizeof(v) / sizeof((v)[0]))
+
+/* Append mount options to string */
+static void append_mount_options(char **oldopts, const char *newopts)
+{
+ if (*oldopts && **oldopts) {
+ /* do not insert options which are already there */
+ while (newopts[0]) {
+ char *p;
+ int len = strlen(newopts);
+ p = strchr(newopts, ',');
+ if (p) len = p - newopts;
+ p = *oldopts;
+ while (1) {
+ if (!strncmp(p, newopts, len)
+ && (p[len]==',' || p[len]==0))
+ goto skip;
+ p = strchr(p,',');
+ if(!p) break;
+ p++;
+ }
+ p = xasprintf("%s,%.*s", *oldopts, len, newopts);
+ free(*oldopts);
+ *oldopts = p;
+skip:
+ newopts += len;
+ while (newopts[0] == ',') newopts++;
+ }
+ } else {
+ if (ENABLE_FEATURE_CLEAN_UP) free(*oldopts);
+ *oldopts = xstrdup(newopts);
+ }
+}
+
+/* Use the mount_options list to parse options into flags.
+ * Also return list of unrecognized options if unrecognized!=NULL */
+static int parse_mount_options(char *options, char **unrecognized)
+{
+ int flags = MS_SILENT;
+
+ // Loop through options
+ for (;;) {
+ int i;
+ char *comma = strchr(options, ',');
+
+ if (comma) *comma = 0;
+
+ // Find this option in mount_options
+ for (i = 0; i < VECTOR_SIZE(mount_options); i++) {
+ if (!strcasecmp(mount_options[i].name, options)) {
+ long fl = mount_options[i].flags;
+ if (fl < 0) flags &= fl;
+ else flags |= fl;
+ break;
+ }
+ }
+ // If unrecognized not NULL, append unrecognized mount options */
+ if (unrecognized && i == VECTOR_SIZE(mount_options)) {
+ // Add it to strflags, to pass on to kernel
+ i = *unrecognized ? strlen(*unrecognized) : 0;
+ *unrecognized = xrealloc(*unrecognized, i+strlen(options)+2);
+
+ // Comma separated if it's not the first one
+ if (i) (*unrecognized)[i++] = ',';
+ strcpy((*unrecognized)+i, options);
+ }
+
+ // Advance to next option, or finish
+ if (comma) {
+ *comma = ',';
+ options = ++comma;
+ } else break;
+ }
+
+ return flags;
+}
+
+// Return a list of all block device backed filesystems
+
+static llist_t *get_block_backed_filesystems(void)
+{
+ static const char *const filesystems[] = {
+ "/etc/filesystems",
+ "/proc/filesystems",
+ 0
+ };
+ char *fs, *buf;
+ llist_t *list = 0;
+ int i;
+ FILE *f;
+
+ for (i = 0; filesystems[i]; i++) {
+ f = fopen(filesystems[i], "r");
+ if (!f) continue;
+
+ while ((buf = xmalloc_getline(f)) != 0) {
+ if (!strncmp(buf, "nodev", 5) && isspace(buf[5]))
+ continue;
+ fs = skip_whitespace(buf);
+ if (*fs=='#' || *fs=='*' || !*fs) continue;
+
+ llist_add_to_end(&list, xstrdup(fs));
+ free(buf);
+ }
+ if (ENABLE_FEATURE_CLEAN_UP) fclose(f);
+ }
+
+ return list;
+}
+
+llist_t *fslist = 0;
+
+#if ENABLE_FEATURE_CLEAN_UP
+static void delete_block_backed_filesystems(void)
+{
+ llist_free(fslist, free);
+}
+#else
+void delete_block_backed_filesystems(void);
+#endif
+
+#if ENABLE_FEATURE_MTAB_SUPPORT
+static int useMtab = 1;
+static int fakeIt;
+#else
+#define useMtab 0
+#define fakeIt 0
+#endif
+
+// Perform actual mount of specific filesystem at specific location.
+// NB: mp->xxx fields may be trashed on exit
+static int mount_it_now(struct mntent *mp, int vfsflags, char *filteropts)
+{
+ int rc = 0;
+
+ if (fakeIt) goto mtab;
+
+ // Mount, with fallback to read-only if necessary.
+
+ for (;;) {
+ rc = mount(mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
+ vfsflags, filteropts);
+ if (!rc || (vfsflags&MS_RDONLY) || (errno!=EACCES && errno!=EROFS))
+ break;
+ bb_error_msg("%s is write-protected, mounting read-only",
+ mp->mnt_fsname);
+ vfsflags |= MS_RDONLY;
+ }
+
+ // Abort entirely if permission denied.
+
+ if (rc && errno == EPERM)
+ bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
+
+ /* If the mount was successful, and we're maintaining an old-style
+ * mtab file by hand, add the new entry to it now. */
+ mtab:
+ if (ENABLE_FEATURE_MTAB_SUPPORT && useMtab && !rc && !(vfsflags & MS_REMOUNT)) {
+ char *fsname;
+ FILE *mountTable = setmntent(bb_path_mtab_file, "a+");
+ int i;
+
+ if (!mountTable) {
+ bb_error_msg("no %s",bb_path_mtab_file);
+ goto ret;
+ }
+
+ // Add vfs string flags
+
+ for (i=0; mount_options[i].flags != MS_REMOUNT; i++)
+ if (mount_options[i].flags > 0 && (mount_options[i].flags & vfsflags))
+ append_mount_options(&(mp->mnt_opts), mount_options[i].name);
+
+ // Remove trailing / (if any) from directory we mounted on
+
+ i = strlen(mp->mnt_dir) - 1;
+ if (i > 0 && mp->mnt_dir[i] == '/') mp->mnt_dir[i] = 0;
+
+ // Convert to canonical pathnames as needed
+
+ mp->mnt_dir = bb_simplify_path(mp->mnt_dir);
+ fsname = 0;
+ if (!mp->mnt_type || !*mp->mnt_type) { /* bind mount */
+ mp->mnt_fsname = fsname = bb_simplify_path(mp->mnt_fsname);
+ mp->mnt_type = (char*)"bind";
+ }
+ mp->mnt_freq = mp->mnt_passno = 0;
+
+ // Write and close.
+
+ addmntent(mountTable, mp);
+ endmntent(mountTable);
+ if (ENABLE_FEATURE_CLEAN_UP) {
+ free(mp->mnt_dir);
+ free(fsname);
+ }
+ }
+ ret:
+ return rc;
+}
+
+#if ENABLE_FEATURE_MOUNT_NFS
+
+/*
+ * Linux NFS mount
+ * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
+ *
+ * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ *
+ * Wed Feb 8 12:51:48 1995, biro@yggdrasil.com (Ross Biro): allow all port
+ * numbers to be specified on the command line.
+ *
+ * Fri, 8 Mar 1996 18:01:39, Swen Thuemmler <swen@uni-paderborn.de>:
+ * Omit the call to connect() for Linux version 1.3.11 or later.
+ *
+ * Wed Oct 1 23:55:28 1997: Dick Streefland <dick_streefland@tasking.com>
+ * Implemented the "bg", "fg" and "retry" mount options for NFS.
+ *
+ * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@misiek.eu.org>
+ * - added Native Language Support
+ *
+ * Modified by Olaf Kirch and Trond Myklebust for new NFS code,
+ * plus NFSv3 stuff.
+ */
+
+/* This is just a warning of a common mistake. Possibly this should be a
+ * uclibc faq entry rather than in busybox... */
+#if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__)
+#error "You need to build uClibc with UCLIBC_HAS_RPC for NFS support."
+#endif
+
+#define MOUNTPORT 635
+#define MNTPATHLEN 1024
+#define MNTNAMLEN 255
+#define FHSIZE 32
+#define FHSIZE3 64
+
+typedef char fhandle[FHSIZE];
+
+typedef struct {
+ unsigned int fhandle3_len;
+ char *fhandle3_val;
+} fhandle3;
+
+enum mountstat3 {
+ MNT_OK = 0,
+ MNT3ERR_PERM = 1,
+ MNT3ERR_NOENT = 2,
+ MNT3ERR_IO = 5,
+ MNT3ERR_ACCES = 13,
+ MNT3ERR_NOTDIR = 20,
+ MNT3ERR_INVAL = 22,
+ MNT3ERR_NAMETOOLONG = 63,
+ MNT3ERR_NOTSUPP = 10004,
+ MNT3ERR_SERVERFAULT = 10006,
+};
+typedef enum mountstat3 mountstat3;
+
+struct fhstatus {
+ unsigned int fhs_status;
+ union {
+ fhandle fhs_fhandle;
+ } fhstatus_u;
+};
+typedef struct fhstatus fhstatus;
+
+struct mountres3_ok {
+ fhandle3 fhandle;
+ struct {
+ unsigned int auth_flavours_len;
+ char *auth_flavours_val;
+ } auth_flavours;
+};
+typedef struct mountres3_ok mountres3_ok;
+
+struct mountres3 {
+ mountstat3 fhs_status;
+ union {
+ mountres3_ok mountinfo;
+ } mountres3_u;
+};
+typedef struct mountres3 mountres3;
+
+typedef char *dirpath;
+
+typedef char *name;
+
+typedef struct mountbody *mountlist;
+
+struct mountbody {
+ name ml_hostname;
+ dirpath ml_directory;
+ mountlist ml_next;
+};
+typedef struct mountbody mountbody;
+
+typedef struct groupnode *groups;
+
+struct groupnode {
+ name gr_name;
+ groups gr_next;
+};
+typedef struct groupnode groupnode;
+
+typedef struct exportnode *exports;
+
+struct exportnode {
+ dirpath ex_dir;
+ groups ex_groups;
+ exports ex_next;
+};
+typedef struct exportnode exportnode;
+
+struct ppathcnf {
+ int pc_link_max;
+ short pc_max_canon;
+ short pc_max_input;
+ short pc_name_max;
+ short pc_path_max;
+ short pc_pipe_buf;
+ uint8_t pc_vdisable;
+ char pc_xxx;
+ short pc_mask[2];
+};
+typedef struct ppathcnf ppathcnf;
+
+#define MOUNTPROG 100005
+#define MOUNTVERS 1
+
+#define MOUNTPROC_NULL 0
+#define MOUNTPROC_MNT 1
+#define MOUNTPROC_DUMP 2
+#define MOUNTPROC_UMNT 3
+#define MOUNTPROC_UMNTALL 4
+#define MOUNTPROC_EXPORT 5
+#define MOUNTPROC_EXPORTALL 6
+
+#define MOUNTVERS_POSIX 2
+
+#define MOUNTPROC_PATHCONF 7
+
+#define MOUNT_V3 3
+
+#define MOUNTPROC3_NULL 0
+#define MOUNTPROC3_MNT 1
+#define MOUNTPROC3_DUMP 2
+#define MOUNTPROC3_UMNT 3
+#define MOUNTPROC3_UMNTALL 4
+#define MOUNTPROC3_EXPORT 5
+
+enum {
+#ifndef NFS_FHSIZE
+ NFS_FHSIZE = 32,
+#endif
+#ifndef NFS_PORT
+ NFS_PORT = 2049
+#endif
+};
+
+/*
+ * We want to be able to compile mount on old kernels in such a way
+ * that the binary will work well on more recent kernels.
+ * Thus, if necessary we teach nfsmount.c the structure of new fields
+ * that will come later.
+ *
+ * Moreover, the new kernel includes conflict with glibc includes
+ * so it is easiest to ignore the kernel altogether (at compile time).
+ */
+
+struct nfs2_fh {
+ char data[32];
+};
+struct nfs3_fh {
+ unsigned short size;
+ unsigned char data[64];
+};
+
+struct nfs_mount_data {
+ int version; /* 1 */
+ int fd; /* 1 */
+ struct nfs2_fh old_root; /* 1 */
+ int flags; /* 1 */
+ int rsize; /* 1 */
+ int wsize; /* 1 */
+ int timeo; /* 1 */
+ int retrans; /* 1 */
+ int acregmin; /* 1 */
+ int acregmax; /* 1 */
+ int acdirmin; /* 1 */
+ int acdirmax; /* 1 */
+ struct sockaddr_in addr; /* 1 */
+ char hostname[256]; /* 1 */
+ int namlen; /* 2 */
+ unsigned int bsize; /* 3 */
+ struct nfs3_fh root; /* 4 */
+};
+
+/* bits in the flags field */
+enum {
+ NFS_MOUNT_SOFT = 0x0001, /* 1 */
+ NFS_MOUNT_INTR = 0x0002, /* 1 */
+ NFS_MOUNT_SECURE = 0x0004, /* 1 */
+ NFS_MOUNT_POSIX = 0x0008, /* 1 */
+ NFS_MOUNT_NOCTO = 0x0010, /* 1 */
+ NFS_MOUNT_NOAC = 0x0020, /* 1 */
+ NFS_MOUNT_TCP = 0x0040, /* 2 */
+ NFS_MOUNT_VER3 = 0x0080, /* 3 */
+ NFS_MOUNT_KERBEROS = 0x0100, /* 3 */
+ NFS_MOUNT_NONLM = 0x0200 /* 3 */
+};
+
+
+/*
+ * We need to translate between nfs status return values and
+ * the local errno values which may not be the same.
+ *
+ * Andreas Schwab <schwab@LS5.informatik.uni-dortmund.de>: change errno:
+ * "after #include <errno.h> the symbol errno is reserved for any use,
+ * it cannot even be used as a struct tag or field name".
+ */
+
+#ifndef EDQUOT
+#define EDQUOT ENOSPC
+#endif
+
+// Convert each NFSERR_BLAH into EBLAH
+
+static const struct {
+ int stat;
+ int errnum;
+} nfs_errtbl[] = {
+ {0,0}, {1,EPERM}, {2,ENOENT}, {5,EIO}, {6,ENXIO}, {13,EACCES}, {17,EEXIST},
+ {19,ENODEV}, {20,ENOTDIR}, {21,EISDIR}, {22,EINVAL}, {27,EFBIG},
+ {28,ENOSPC}, {30,EROFS}, {63,ENAMETOOLONG}, {66,ENOTEMPTY}, {69,EDQUOT},
+ {70,ESTALE}, {71,EREMOTE}, {-1,EIO}
+};
+
+static char *nfs_strerror(int status)
+{
+ int i;
+ static char buf[sizeof("unknown nfs status return value: ") + sizeof(int)*3];
+
+ for (i = 0; nfs_errtbl[i].stat != -1; i++) {
+ if (nfs_errtbl[i].stat == status)
+ return strerror(nfs_errtbl[i].errnum);
+ }
+ sprintf(buf, "unknown nfs status return value: %d", status);
+ return buf;
+}
+
+static bool_t xdr_fhandle(XDR *xdrs, fhandle objp)
+{
+ if (!xdr_opaque(xdrs, objp, FHSIZE))
+ return FALSE;
+ return TRUE;
+}
+
+static bool_t xdr_fhstatus(XDR *xdrs, fhstatus *objp)
+{
+ if (!xdr_u_int(xdrs, &objp->fhs_status))
+ return FALSE;
+ switch (objp->fhs_status) {
+ case 0:
+ if (!xdr_fhandle(xdrs, objp->fhstatus_u.fhs_fhandle))
+ return FALSE;
+ break;
+ default:
+ break;
+ }
+ return TRUE;
+}
+
+static bool_t xdr_dirpath(XDR *xdrs, dirpath *objp)
+{
+ if (!xdr_string(xdrs, objp, MNTPATHLEN))
+ return FALSE;
+ return TRUE;
+}
+
+static bool_t xdr_fhandle3(XDR *xdrs, fhandle3 *objp)
+{
+ if (!xdr_bytes(xdrs, (char **)&objp->fhandle3_val, (unsigned int *) &objp->fhandle3_len, FHSIZE3))
+ return FALSE;
+ return TRUE;
+}
+
+static bool_t xdr_mountres3_ok(XDR *xdrs, mountres3_ok *objp)
+{
+ if (!xdr_fhandle3(xdrs, &objp->fhandle))
+ return FALSE;
+ if (!xdr_array(xdrs, &(objp->auth_flavours.auth_flavours_val), &(objp->auth_flavours.auth_flavours_len), ~0,
+ sizeof (int), (xdrproc_t) xdr_int))
+ return FALSE;
+ return TRUE;
+}
+
+static bool_t xdr_mountstat3(XDR *xdrs, mountstat3 *objp)
+{
+ if (!xdr_enum(xdrs, (enum_t *) objp))
+ return FALSE;
+ return TRUE;
+}
+
+static bool_t xdr_mountres3(XDR *xdrs, mountres3 *objp)
+{
+ if (!xdr_mountstat3(xdrs, &objp->fhs_status))
+ return FALSE;
+ switch (objp->fhs_status) {
+ case MNT_OK:
+ if (!xdr_mountres3_ok(xdrs, &objp->mountres3_u.mountinfo))
+ return FALSE;
+ break;
+ default:
+ break;
+ }
+ return TRUE;
+}
+
+#define MAX_NFSPROT ((nfs_mount_version >= 4) ? 3 : 2)
+
+/*
+ * nfs_mount_version according to the sources seen at compile time.
+ */
+static int nfs_mount_version;
+static int kernel_version;
+
+/*
+ * Unfortunately, the kernel prints annoying console messages
+ * in case of an unexpected nfs mount version (instead of
+ * just returning some error). Therefore we'll have to try
+ * and figure out what version the kernel expects.
+ *
+ * Variables:
+ * KERNEL_NFS_MOUNT_VERSION: kernel sources at compile time
+ * NFS_MOUNT_VERSION: these nfsmount sources at compile time
+ * nfs_mount_version: version this source and running kernel can handle
+ */
+static void
+find_kernel_nfs_mount_version(void)
+{
+ if (kernel_version)
+ return;
+
+ nfs_mount_version = 4; /* default */
+
+ kernel_version = get_linux_version_code();
+ if (kernel_version) {
+ if (kernel_version < KERNEL_VERSION(2,1,32))
+ nfs_mount_version = 1;
+ else if (kernel_version < KERNEL_VERSION(2,2,18) ||
+ (kernel_version >= KERNEL_VERSION(2,3,0) &&
+ kernel_version < KERNEL_VERSION(2,3,99)))
+ nfs_mount_version = 3;
+ /* else v4 since 2.3.99pre4 */
+ }
+}
+
+static struct pmap *
+get_mountport(struct sockaddr_in *server_addr,
+ long unsigned prog,
+ long unsigned version,
+ long unsigned proto,
+ long unsigned port)
+{
+ struct pmaplist *pmap;
+ static struct pmap p = {0, 0, 0, 0};
+
+ server_addr->sin_port = PMAPPORT;
+/* glibc 2.4 (still) has pmap_getmaps(struct sockaddr_in *).
+ * I understand it like "IPv6 for this is not 100% ready" */
+ pmap = pmap_getmaps(server_addr);
+
+ if (version > MAX_NFSPROT)
+ version = MAX_NFSPROT;
+ if (!prog)
+ prog = MOUNTPROG;
+ p.pm_prog = prog;
+ p.pm_vers = version;
+ p.pm_prot = proto;
+ p.pm_port = port;
+
+ while (pmap) {
+ if (pmap->pml_map.pm_prog != prog)
+ goto next;
+ if (!version && p.pm_vers > pmap->pml_map.pm_vers)
+ goto next;
+ if (version > 2 && pmap->pml_map.pm_vers != version)
+ goto next;
+ if (version && version <= 2 && pmap->pml_map.pm_vers > 2)
+ goto next;
+ if (pmap->pml_map.pm_vers > MAX_NFSPROT ||
+ (proto && p.pm_prot && pmap->pml_map.pm_prot != proto) ||
+ (port && pmap->pml_map.pm_port != port))
+ goto next;
+ memcpy(&p, &pmap->pml_map, sizeof(p));
+next:
+ pmap = pmap->pml_next;
+ }
+ if (!p.pm_vers)
+ p.pm_vers = MOUNTVERS;
+ if (!p.pm_port)
+ p.pm_port = MOUNTPORT;
+ if (!p.pm_prot)
+ p.pm_prot = IPPROTO_TCP;
+ return &p;
+}
+
+static int daemonize(void)
+{
+ int fd;
+ int pid = fork();
+ if (pid < 0) /* error */
+ return -errno;
+ if (pid > 0) /* parent */
+ return 0;
+ /* child */
+ fd = xopen(bb_dev_null, O_RDWR);
+ dup2(fd, 0);
+ dup2(fd, 1);
+ dup2(fd, 2);
+ while (fd > 2) close(fd--);
+ setsid();
+ openlog(applet_name, LOG_PID, LOG_DAEMON);
+ logmode = LOGMODE_SYSLOG;
+ return 1;
+}
+
+// TODO
+static inline int we_saw_this_host_before(const char *hostname)
+{
+ return 0;
+}
+
+/* RPC strerror analogs are terminally idiotic:
+ * *mandatory* prefix and \n at end.
+ * This hopefully helps. Usage:
+ * error_msg_rpc(clnt_*error*(" ")) */
+static void error_msg_rpc(const char *msg)
+{
+ int len;
+ while (msg[0] == ' ' || msg[0] == ':') msg++;
+ len = strlen(msg);
+ while (len && msg[len-1] == '\n') len--;
+ bb_error_msg("%.*s", len, msg);
+}
+
+// NB: mp->xxx fields may be trashed on exit
+static int nfsmount(struct mntent *mp, int vfsflags, char *filteropts)
+{
+ CLIENT *mclient;
+ char *hostname;
+ char *pathname;
+ char *mounthost;
+ struct nfs_mount_data data;
+ char *opt;
+ struct hostent *hp;
+ struct sockaddr_in server_addr;
+ struct sockaddr_in mount_server_addr;
+ int msock, fsock;
+ union {
+ struct fhstatus nfsv2;
+ struct mountres3 nfsv3;
+ } status;
+ int daemonized;
+ char *s;
+ int port;
+ int mountport;
+ int proto;
+ int bg;
+ int soft;
+ int intr;
+ int posix;
+ int nocto;
+ int noac;
+ int nolock;
+ int retry;
+ int tcp;
+ int mountprog;
+ int mountvers;
+ int nfsprog;
+ int nfsvers;
+ int retval;
+
+ find_kernel_nfs_mount_version();
+
+ daemonized = 0;
+ mounthost = NULL;
+ retval = ETIMEDOUT;
+ msock = fsock = -1;
+ mclient = NULL;
+
+ /* NB: hostname, mounthost, filteropts must be free()d prior to return */
+
+ filteropts = xstrdup(filteropts); /* going to trash it later... */
+
+ hostname = xstrdup(mp->mnt_fsname);
+ /* mount_main() guarantees that ':' is there */
+ s = strchr(hostname, ':');
+ pathname = s + 1;
+ *s = '\0';
+ /* Ignore all but first hostname in replicated mounts
+ until they can be fully supported. (mack@sgi.com) */
+ s = strchr(hostname, ',');
+ if (s) {
+ *s = '\0';
+ bb_error_msg("warning: multiple hostnames not supported");
+ }
+
+ server_addr.sin_family = AF_INET;
+ if (!inet_aton(hostname, &server_addr.sin_addr)) {
+ hp = gethostbyname(hostname);
+ if (hp == NULL) {
+ bb_herror_msg("%s", hostname);
+ goto fail;
+ }
+ if (hp->h_length > sizeof(struct in_addr)) {
+ bb_error_msg("got bad hp->h_length");
+ hp->h_length = sizeof(struct in_addr);
+ }
+ memcpy(&server_addr.sin_addr,
+ hp->h_addr, hp->h_length);
+ }
+
+ memcpy(&mount_server_addr, &server_addr, sizeof(mount_server_addr));
+
+ /* add IP address to mtab options for use when unmounting */
+
+ if (!mp->mnt_opts) { /* TODO: actually mp->mnt_opts is never NULL */
+ mp->mnt_opts = xasprintf("addr=%s", inet_ntoa(server_addr.sin_addr));
+ } else {
+ char *tmp = xasprintf("%s%saddr=%s", mp->mnt_opts,
+ mp->mnt_opts[0] ? "," : "",
+ inet_ntoa(server_addr.sin_addr));
+ free(mp->mnt_opts);
+ mp->mnt_opts = tmp;
+ }
+
+ /* Set default options.
+ * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to
+ * let the kernel decide.
+ * timeo is filled in after we know whether it'll be TCP or UDP. */
+ memset(&data, 0, sizeof(data));
+ data.retrans = 3;
+ data.acregmin = 3;
+ data.acregmax = 60;
+ data.acdirmin = 30;
+ data.acdirmax = 60;
+ data.namlen = NAME_MAX;
+
+ bg = 0;
+ soft = 0;
+ intr = 0;
+ posix = 0;
+ nocto = 0;
+ nolock = 0;
+ noac = 0;
+ retry = 10000; /* 10000 minutes ~ 1 week */
+ tcp = 0;
+
+ mountprog = MOUNTPROG;
+ mountvers = 0;
+ port = 0;
+ mountport = 0;
+ nfsprog = 100003;
+ nfsvers = 0;
+
+ /* parse options */
+
+ for (opt = strtok(filteropts, ","); opt; opt = strtok(NULL, ",")) {
+ char *opteq = strchr(opt, '=');
+ if (opteq) {
+ const char *const options[] = {
+ /* 0 */ "rsize",
+ /* 1 */ "wsize",
+ /* 2 */ "timeo",
+ /* 3 */ "retrans",
+ /* 4 */ "acregmin",
+ /* 5 */ "acregmax",
+ /* 6 */ "acdirmin",
+ /* 7 */ "acdirmax",
+ /* 8 */ "actimeo",
+ /* 9 */ "retry",
+ /* 10 */ "port",
+ /* 11 */ "mountport",
+ /* 12 */ "mounthost",
+ /* 13 */ "mountprog",
+ /* 14 */ "mountvers",
+ /* 15 */ "nfsprog",
+ /* 16 */ "nfsvers",
+ /* 17 */ "vers",
+ /* 18 */ "proto",
+ /* 19 */ "namlen",
+ /* 20 */ "addr",
+ NULL
+ };
+ int val = xatoi_u(opteq + 1);
+ *opteq = '\0';
+ switch (index_in_str_array(options, opt)) {
+ case 0: // "rsize"
+ data.rsize = val;
+ break;
+ case 1: // "wsize"
+ data.wsize = val;
+ break;
+ case 2: // "timeo"
+ data.timeo = val;
+ break;
+ case 3: // "retrans"
+ data.retrans = val;
+ break;
+ case 4: // "acregmin"
+ data.acregmin = val;
+ break;
+ case 5: // "acregmax"
+ data.acregmax = val;
+ break;
+ case 6: // "acdirmin"
+ data.acdirmin = val;
+ break;
+ case 7: // "acdirmax"
+ data.acdirmax = val;
+ break;
+ case 8: // "actimeo"
+ data.acregmin = val;
+ data.acregmax = val;
+ data.acdirmin = val;
+ data.acdirmax = val;
+ break;
+ case 9: // "retry"
+ retry = val;
+ break;
+ case 10: // "port"
+ port = val;
+ break;
+ case 11: // "mountport"
+ mountport = val;
+ break;
+ case 12: // "mounthost"
+ mounthost = xstrndup(opteq+1,
+ strcspn(opteq+1," \t\n\r,"));
+ break;
+ case 13: // "mountprog"
+ mountprog = val;
+ break;
+ case 14: // "mountvers"
+ mountvers = val;
+ break;
+ case 15: // "nfsprog"
+ nfsprog = val;
+ break;
+ case 16: // "nfsvers"
+ case 17: // "vers"
+ nfsvers = val;
+ break;
+ case 18: // "proto"
+ if (!strncmp(opteq+1, "tcp", 3))
+ tcp = 1;
+ else if (!strncmp(opteq+1, "udp", 3))
+ tcp = 0;
+ else
+ bb_error_msg("warning: unrecognized proto= option");
+ break;
+ case 19: // "namlen"
+ if (nfs_mount_version >= 2)
+ data.namlen = val;
+ else
+ bb_error_msg("warning: option namlen is not supported\n");
+ break;
+ case 20: // "addr" - ignore
+ break;
+ default:
+ bb_error_msg("unknown nfs mount parameter: %s=%d", opt, val);
+ goto fail;
+ }
+ }
+ else {
+ const char *const options[] = {
+ "bg",
+ "fg",
+ "soft",
+ "hard",
+ "intr",
+ "posix",
+ "cto",
+ "ac",
+ "tcp",
+ "udp",
+ "lock",
+ NULL
+ };
+ int val = 1;
+ if (!strncmp(opt, "no", 2)) {
+ val = 0;
+ opt += 2;
+ }
+ switch (index_in_str_array(options, opt)) {
+ case 0: // "bg"
+ bg = val;
+ break;
+ case 1: // "fg"
+ bg = !val;
+ break;
+ case 2: // "soft"
+ soft = val;
+ break;
+ case 3: // "hard"
+ soft = !val;
+ break;
+ case 4: // "intr"
+ intr = val;
+ break;
+ case 5: // "posix"
+ posix = val;
+ break;
+ case 6: // "cto"
+ nocto = !val;
+ break;
+ case 7: // "ac"
+ noac = !val;
+ break;
+ case 8: // "tcp"
+ tcp = val;
+ break;
+ case 9: // "udp"
+ tcp = !val;
+ break;
+ case 10: // "lock"
+ if (nfs_mount_version >= 3)
+ nolock = !val;
+ else
+ bb_error_msg("warning: option nolock is not supported");
+ break;
+ default:
+ bb_error_msg("unknown nfs mount option: %s%s", val ? "" : "no", opt);
+ goto fail;
+ }
+ }
+ }
+ proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP;
+
+ data.flags = (soft ? NFS_MOUNT_SOFT : 0)
+ | (intr ? NFS_MOUNT_INTR : 0)
+ | (posix ? NFS_MOUNT_POSIX : 0)
+ | (nocto ? NFS_MOUNT_NOCTO : 0)
+ | (noac ? NFS_MOUNT_NOAC : 0);
+ if (nfs_mount_version >= 2)
+ data.flags |= (tcp ? NFS_MOUNT_TCP : 0);
+ if (nfs_mount_version >= 3)
+ data.flags |= (nolock ? NFS_MOUNT_NONLM : 0);
+ if (nfsvers > MAX_NFSPROT || mountvers > MAX_NFSPROT) {
+ bb_error_msg("NFSv%d not supported", nfsvers);
+ goto fail;
+ }
+ if (nfsvers && !mountvers)
+ mountvers = (nfsvers < 3) ? 1 : nfsvers;
+ if (nfsvers && nfsvers < mountvers) {
+ mountvers = nfsvers;
+ }
+
+ /* Adjust options if none specified */
+ if (!data.timeo)
+ data.timeo = tcp ? 70 : 7;
+
+ data.version = nfs_mount_version;
+
+ if (vfsflags & MS_REMOUNT)
+ goto do_mount;
+
+ /*
+ * If the previous mount operation on the same host was
+ * backgrounded, and the "bg" for this mount is also set,
+ * give up immediately, to avoid the initial timeout.
+ */
+ if (bg && we_saw_this_host_before(hostname)) {
+ daemonized = daemonize(); /* parent or error */
+ if (daemonized <= 0) { /* parent or error */
+ retval = -daemonized;
+ goto ret;
+ }
+ }
+
+ /* create mount daemon client */
+ /* See if the nfs host = mount host. */
+ if (mounthost) {
+ if (mounthost[0] >= '0' && mounthost[0] <= '9') {
+ mount_server_addr.sin_family = AF_INET;
+ mount_server_addr.sin_addr.s_addr = inet_addr(hostname);
+ } else {
+ hp = gethostbyname(mounthost);
+ if (hp == NULL) {
+ bb_herror_msg("%s", mounthost);
+ goto fail;
+ } else {
+ if (hp->h_length > sizeof(struct in_addr)) {
+ bb_error_msg("got bad hp->h_length?");
+ hp->h_length = sizeof(struct in_addr);
+ }
+ mount_server_addr.sin_family = AF_INET;
+ memcpy(&mount_server_addr.sin_addr,
+ hp->h_addr, hp->h_length);
+ }
+ }
+ }
+
+ /*
+ * The following loop implements the mount retries. When the mount
+ * times out, and the "bg" option is set, we background ourself
+ * and continue trying.
+ *
+ * The case where the mount point is not present and the "bg"
+ * option is set, is treated as a timeout. This is done to
+ * support nested mounts.
+ *
+ * The "retry" count specified by the user is the number of
+ * minutes to retry before giving up.
+ */
+ {
+ struct timeval total_timeout;
+ struct timeval retry_timeout;
+ struct pmap* pm_mnt;
+ time_t t;
+ time_t prevt;
+ time_t timeout;
+
+ retry_timeout.tv_sec = 3;
+ retry_timeout.tv_usec = 0;
+ total_timeout.tv_sec = 20;
+ total_timeout.tv_usec = 0;
+ timeout = time(NULL) + 60 * retry;
+ prevt = 0;
+ t = 30;
+retry:
+ /* be careful not to use too many CPU cycles */
+ if (t - prevt < 30)
+ sleep(30);
+
+ pm_mnt = get_mountport(&mount_server_addr,
+ mountprog,
+ mountvers,
+ proto,
+ mountport);
+ nfsvers = (pm_mnt->pm_vers < 2) ? 2 : pm_mnt->pm_vers;
+
+ /* contact the mount daemon via TCP */
+ mount_server_addr.sin_port = htons(pm_mnt->pm_port);
+ msock = RPC_ANYSOCK;
+
+ switch (pm_mnt->pm_prot) {
+ case IPPROTO_UDP:
+ mclient = clntudp_create(&mount_server_addr,
+ pm_mnt->pm_prog,
+ pm_mnt->pm_vers,
+ retry_timeout,
+ &msock);
+ if (mclient)
+ break;
+ mount_server_addr.sin_port = htons(pm_mnt->pm_port);
+ msock = RPC_ANYSOCK;
+ case IPPROTO_TCP:
+ mclient = clnttcp_create(&mount_server_addr,
+ pm_mnt->pm_prog,
+ pm_mnt->pm_vers,
+ &msock, 0, 0);
+ break;
+ default:
+ mclient = 0;
+ }
+ if (!mclient) {
+ if (!daemonized && prevt == 0)
+ error_msg_rpc(clnt_spcreateerror(" "));
+ } else {
+ enum clnt_stat clnt_stat;
+ /* try to mount hostname:pathname */
+ mclient->cl_auth = authunix_create_default();
+
+ /* make pointers in xdr_mountres3 NULL so
+ * that xdr_array allocates memory for us
+ */
+ memset(&status, 0, sizeof(status));
+
+ if (pm_mnt->pm_vers == 3)
+ clnt_stat = clnt_call(mclient, MOUNTPROC3_MNT,
+ (xdrproc_t) xdr_dirpath,
+ (caddr_t) &pathname,
+ (xdrproc_t) xdr_mountres3,
+ (caddr_t) &status,
+ total_timeout);
+ else
+ clnt_stat = clnt_call(mclient, MOUNTPROC_MNT,
+ (xdrproc_t) xdr_dirpath,
+ (caddr_t) &pathname,
+ (xdrproc_t) xdr_fhstatus,
+ (caddr_t) &status,
+ total_timeout);
+
+ if (clnt_stat == RPC_SUCCESS)
+ goto prepare_kernel_data; /* we're done */
+ if (errno != ECONNREFUSED) {
+ error_msg_rpc(clnt_sperror(mclient, " "));
+ goto fail; /* don't retry */
+ }
+ /* Connection refused */
+ if (!daemonized && prevt == 0) /* print just once */
+ error_msg_rpc(clnt_sperror(mclient, " "));
+ auth_destroy(mclient->cl_auth);
+ clnt_destroy(mclient);
+ mclient = 0;
+ close(msock);
+ }
+
+ /* Timeout. We are going to retry... maybe */
+
+ if (!bg)
+ goto fail;
+ if (!daemonized) {
+ daemonized = daemonize();
+ if (daemonized <= 0) { /* parent or error */
+ retval = -daemonized;
+ goto ret;
+ }
+ }
+ prevt = t;
+ t = time(NULL);
+ if (t >= timeout)
+ /* TODO error message */
+ goto fail;
+
+ goto retry;
+ }
+
+prepare_kernel_data:
+
+ if (nfsvers == 2) {
+ if (status.nfsv2.fhs_status != 0) {
+ bb_error_msg("%s:%s failed, reason given by server: %s",
+ hostname, pathname,
+ nfs_strerror(status.nfsv2.fhs_status));
+ goto fail;
+ }
+ memcpy(data.root.data,
+ (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
+ NFS_FHSIZE);
+ data.root.size = NFS_FHSIZE;
+ memcpy(data.old_root.data,
+ (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
+ NFS_FHSIZE);
+ } else {
+ fhandle3 *my_fhandle;
+ if (status.nfsv3.fhs_status != 0) {
+ bb_error_msg("%s:%s failed, reason given by server: %s",
+ hostname, pathname,
+ nfs_strerror(status.nfsv3.fhs_status));
+ goto fail;
+ }
+ my_fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle;
+ memset(data.old_root.data, 0, NFS_FHSIZE);
+ memset(&data.root, 0, sizeof(data.root));
+ data.root.size = my_fhandle->fhandle3_len;
+ memcpy(data.root.data,
+ (char *) my_fhandle->fhandle3_val,
+ my_fhandle->fhandle3_len);
+
+ data.flags |= NFS_MOUNT_VER3;
+ }
+
+ /* create nfs socket for kernel */
+
+ if (tcp) {
+ if (nfs_mount_version < 3) {
+ bb_error_msg("NFS over TCP is not supported");
+ goto fail;
+ }
+ fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ } else
+ fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (fsock < 0) {
+ bb_perror_msg("nfs socket");
+ goto fail;
+ }
+ if (bindresvport(fsock, 0) < 0) {
+ bb_perror_msg("nfs bindresvport");
+ goto fail;
+ }
+ if (port == 0) {
+ server_addr.sin_port = PMAPPORT;
+ port = pmap_getport(&server_addr, nfsprog, nfsvers,
+ tcp ? IPPROTO_TCP : IPPROTO_UDP);
+ if (port == 0)
+ port = NFS_PORT;
+ }
+ server_addr.sin_port = htons(port);
+
+ /* prepare data structure for kernel */
+
+ data.fd = fsock;
+ memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr));
+ strncpy(data.hostname, hostname, sizeof(data.hostname));
+
+ /* clean up */
+
+ auth_destroy(mclient->cl_auth);
+ clnt_destroy(mclient);
+ close(msock);
+
+ if (bg) {
+ /* We must wait until mount directory is available */
+ struct stat statbuf;
+ int delay = 1;
+ while (stat(mp->mnt_dir, &statbuf) == -1) {
+ if (!daemonized) {
+ daemonized = daemonize();
+ if (daemonized <= 0) { /* parent or error */
+ retval = -daemonized;
+ goto ret;
+ }
+ }
+ sleep(delay); /* 1, 2, 4, 8, 16, 30, ... */
+ delay *= 2;
+ if (delay > 30)
+ delay = 30;
+ }
+ }
+
+do_mount: /* perform actual mount */
+
+ mp->mnt_type = (char*)"nfs";
+ retval = mount_it_now(mp, vfsflags, (char*)&data);
+ goto ret;
+
+fail: /* abort */
+
+ if (msock != -1) {
+ if (mclient) {
+ auth_destroy(mclient->cl_auth);
+ clnt_destroy(mclient);
+ }
+ close(msock);
+ }
+ if (fsock != -1)
+ close(fsock);
+
+ret:
+ free(hostname);
+ free(mounthost);
+ free(filteropts);
+ return retval;
+}
+
+#else /* !ENABLE_FEATURE_MOUNT_NFS */
+
+/* Never called. Call should be optimized out. */
+int nfsmount(struct mntent *mp, int vfsflags, char *filteropts);
+
+#endif /* !ENABLE_FEATURE_MOUNT_NFS */
+
+// Mount one directory. Handles CIFS, NFS, loopback, autobind, and filesystem
+// type detection. Returns 0 for success, nonzero for failure.
+// NB: mp->xxx fields may be trashed on exit
+static int singlemount(struct mntent *mp, int ignore_busy)
+{
+ int rc = -1, vfsflags;
+ char *loopFile = 0, *filteropts = 0;
+ llist_t *fl = 0;
+ struct stat st;
+
+ vfsflags = parse_mount_options(mp->mnt_opts, &filteropts);
+
+ // Treat fstype "auto" as unspecified.
+
+ if (mp->mnt_type && strcmp(mp->mnt_type,"auto") == 0)
+ mp->mnt_type = 0;
+
+ // Might this be an CIFS filesystem?
+
+ if (ENABLE_FEATURE_MOUNT_CIFS
+ && (!mp->mnt_type || strcmp(mp->mnt_type,"cifs") == 0)
+ && (mp->mnt_fsname[0]=='/' || mp->mnt_fsname[0]=='\\')
+ && mp->mnt_fsname[0]==mp->mnt_fsname[1]
+ ) {
+ len_and_sockaddr *lsa;
+ char *ip, *dotted;
+ char *s;
+
+ rc = 1;
+ // Replace '/' with '\' and verify that unc points to "//server/share".
+
+ for (s = mp->mnt_fsname; *s; ++s)
+ if (*s == '/') *s = '\\';
+
+ // get server IP
+
+ s = strrchr(mp->mnt_fsname, '\\');
+ if (s <= mp->mnt_fsname+1) goto report_error;
+ *s = '\0';
+ lsa = host2sockaddr(mp->mnt_fsname+2, 0);
+ *s = '\\';
+ if (!lsa) goto report_error;
+
+ // insert ip=... option into string flags.
+
+ dotted = xmalloc_sockaddr2dotted_noport(&lsa->sa, lsa->len);
+ ip = xasprintf("ip=%s", dotted);
+ parse_mount_options(ip, &filteropts);
+
+ // compose new unc '\\server-ip\share'
+ // (s => slash after hostname)
+
+ mp->mnt_fsname = xasprintf("\\\\%s%s", dotted, s);
+
+ // lock is required
+ vfsflags |= MS_MANDLOCK;
+
+ mp->mnt_type = (char*)"cifs";
+ rc = mount_it_now(mp, vfsflags, filteropts);
+ if (ENABLE_FEATURE_CLEAN_UP) {
+ free(mp->mnt_fsname);
+ free(ip);
+ free(dotted);
+ free(lsa);
+ }
+ goto report_error;
+ }
+
+ // Might this be an NFS filesystem?
+
+ if (ENABLE_FEATURE_MOUNT_NFS
+ && (!mp->mnt_type || !strcmp(mp->mnt_type,"nfs"))
+ && strchr(mp->mnt_fsname, ':') != NULL
+ ) {
+ rc = nfsmount(mp, vfsflags, filteropts);
+ goto report_error;
+ }
+
+ // Look at the file. (Not found isn't a failure for remount, or for
+ // a synthetic filesystem like proc or sysfs.)
+
+ if (!lstat(mp->mnt_fsname, &st)
+ && !(vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))
+ ) {
+ // Do we need to allocate a loopback device for it?
+
+ if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) {
+ loopFile = bb_simplify_path(mp->mnt_fsname);
+ mp->mnt_fsname = 0;
+ switch (set_loop(&(mp->mnt_fsname), loopFile, 0)) {
+ case 0:
+ case 1:
+ break;
+ default:
+ bb_error_msg( errno == EPERM || errno == EACCES
+ ? bb_msg_perm_denied_are_you_root
+ : "cannot setup loop device");
+ return errno;
+ }
+
+ // Autodetect bind mounts
+
+ } else if (S_ISDIR(st.st_mode) && !mp->mnt_type)
+ vfsflags |= MS_BIND;
+ }
+
+ /* If we know the fstype (or don't need to), jump straight
+ * to the actual mount. */
+
+ if (mp->mnt_type || (vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE)))
+ rc = mount_it_now(mp, vfsflags, filteropts);
+ else {
+ // Loop through filesystem types until mount succeeds
+ // or we run out
+
+ /* Initialize list of block backed filesystems. This has to be
+ * done here so that during "mount -a", mounts after /proc shows up
+ * can autodetect. */
+
+ if (!fslist) {
+ fslist = get_block_backed_filesystems();
+ if (ENABLE_FEATURE_CLEAN_UP && fslist)
+ atexit(delete_block_backed_filesystems);
+ }
+
+ for (fl = fslist; fl; fl = fl->link) {
+ mp->mnt_type = fl->data;
+ rc = mount_it_now(mp, vfsflags, filteropts);
+ if (!rc) break;
+ }
+ }
+
+ // If mount failed, clean up loop file (if any).
+
+ if (ENABLE_FEATURE_MOUNT_LOOP && rc && loopFile) {
+ del_loop(mp->mnt_fsname);
+ if (ENABLE_FEATURE_CLEAN_UP) {
+ free(loopFile);
+ free(mp->mnt_fsname);
+ }
+ }
+
+ report_error:
+ if (ENABLE_FEATURE_CLEAN_UP)
+ free(filteropts);
+
+ if (rc && errno == EBUSY && ignore_busy) rc = 0;
+ if (rc < 0)
+ /* perror here sometimes says "mounting ... on ... failed: Success" */
+ bb_error_msg("mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir);
+
+ return rc;
+}
+
+// Parse options, if necessary parse fstab/mtab, and call singlemount for
+// each directory to be mounted.
+
+const char must_be_root[] = "you must be root";
+
+int mount_main(int argc, char **argv);
+int mount_main(int argc, char **argv)
+{
+ enum { OPT_ALL = 0x10 };
+
+ char *cmdopts = xstrdup(""), *fstype=0, *storage_path=0;
+ char *opt_o;
+ const char *fstabname;
+ FILE *fstab;
+ int i, j, rc = 0;
+ unsigned opt;
+ struct mntent mtpair[2], *mtcur = mtpair;
+ SKIP_DESKTOP(const int nonroot = 0;)
+ USE_DESKTOP( int nonroot = (getuid() != 0);)
+
+ /* parse long options, like --bind and --move. Note that -o option
+ * and --option are synonymous. Yes, this means --remount,rw works. */
+
+ for (i = j = 0; i < argc; i++) {
+ if (argv[i][0] == '-' && argv[i][1] == '-') {
+ append_mount_options(&cmdopts, argv[i]+2);
+ } else argv[j++] = argv[i];
+ }
+ argv[j] = 0;
+ argc = j;
+
+ // Parse remaining options
+
+ opt = getopt32(argc, argv, "o:t:rwanfvs", &opt_o, &fstype);
+ if (opt & 0x1) append_mount_options(&cmdopts, opt_o); // -o
+ //if (opt & 0x2) // -t
+ if (opt & 0x4) append_mount_options(&cmdopts, "ro"); // -r
+ if (opt & 0x8) append_mount_options(&cmdopts, "rw"); // -w
+ //if (opt & 0x10) // -a
+ if (opt & 0x20) USE_FEATURE_MTAB_SUPPORT(useMtab = 0); // -n
+ if (opt & 0x40) USE_FEATURE_MTAB_SUPPORT(fakeIt = 1); // -f
+ //if (opt & 0x80) // -v: verbose (ignore)
+ //if (opt & 0x100) // -s: sloppy (ignore)
+ argv += optind;
+ argc -= optind;
+
+ // Three or more non-option arguments? Die with a usage message.
+
+ if (argc > 2) bb_show_usage();
+
+ // If we have no arguments, show currently mounted filesystems
+
+ if (!argc) {
+ if (!(opt & OPT_ALL)) {
+ FILE *mountTable = setmntent(bb_path_mtab_file, "r");
+
+ if (!mountTable) bb_error_msg_and_die("no %s", bb_path_mtab_file);
+
+ while (getmntent_r(mountTable, mtpair, bb_common_bufsiz1,
+ sizeof(bb_common_bufsiz1)))
+ {
+ // Don't show rootfs. FIXME: why??
+ // util-linux 2.12a happily shows rootfs...
+ //if (!strcmp(mtpair->mnt_fsname, "rootfs")) continue;
+
+ if (!fstype || !strcmp(mtpair->mnt_type, fstype))
+ printf("%s on %s type %s (%s)\n", mtpair->mnt_fsname,
+ mtpair->mnt_dir, mtpair->mnt_type,
+ mtpair->mnt_opts);
+ }
+ if (ENABLE_FEATURE_CLEAN_UP) endmntent(mountTable);
+ return EXIT_SUCCESS;
+ }
+ } else storage_path = bb_simplify_path(argv[0]);
+
+ // When we have two arguments, the second is the directory and we can
+ // skip looking at fstab entirely. We can always abspath() the directory
+ // argument when we get it.
+
+ if (argc == 2) {
+ if (nonroot)
+ bb_error_msg_and_die(must_be_root);
+ mtpair->mnt_fsname = argv[0];
+ mtpair->mnt_dir = argv[1];
+ mtpair->mnt_type = fstype;
+ mtpair->mnt_opts = cmdopts;
+ rc = singlemount(mtpair, 0);
+ goto clean_up;
+ }
+
+ i = parse_mount_options(cmdopts, 0);
+ if (nonroot && (i & ~MS_SILENT)) // Non-root users cannot specify flags
+ bb_error_msg_and_die(must_be_root);
+
+ // If we have a shared subtree flag, don't worry about fstab or mtab.
+
+ if (ENABLE_FEATURE_MOUNT_FLAGS
+ && (i & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
+ ) {
+ rc = mount("", argv[0], "", i, "");
+ if (rc) bb_perror_msg_and_die("%s", argv[0]);
+ goto clean_up;
+ }
+
+ // Open either fstab or mtab
+
+ fstabname = "/etc/fstab";
+ if (i & MS_REMOUNT) {
+ fstabname = bb_path_mtab_file;
+ }
+ fstab = setmntent(fstabname, "r");
+ if (!fstab)
+ bb_perror_msg_and_die("cannot read %s", fstabname);
+
+ // Loop through entries until we find what we're looking for.
+
+ memset(mtpair, 0, sizeof(mtpair));
+ for (;;) {
+ struct mntent *mtnext = (mtcur==mtpair ? mtpair+1 : mtpair);
+
+ // Get next fstab entry
+
+ if (!getmntent_r(fstab, mtcur, bb_common_bufsiz1
+ + (mtcur==mtpair ? sizeof(bb_common_bufsiz1)/2 : 0),
+ sizeof(bb_common_bufsiz1)/2))
+ {
+ // Were we looking for something specific?
+
+ if (argc) {
+
+ // If we didn't find anything, complain.
+
+ if (!mtnext->mnt_fsname)
+ bb_error_msg_and_die("can't find %s in %s",
+ argv[0], fstabname);
+
+ mtcur = mtnext;
+ if (nonroot) {
+ // fstab must have "users" or "user"
+ if (!(parse_mount_options(mtcur->mnt_opts, 0) & MOUNT_USERS))
+ bb_error_msg_and_die(must_be_root);
+ }
+
+ // Mount the last thing we found.
+
+ mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
+ append_mount_options(&(mtcur->mnt_opts), cmdopts);
+ rc = singlemount(mtcur, 0);
+ free(mtcur->mnt_opts);
+ }
+ goto clean_up;
+ }
+
+ /* If we're trying to mount something specific and this isn't it,
+ * skip it. Note we must match both the exact text in fstab (ala
+ * "proc") or a full path from root */
+
+ if (argc) {
+
+ // Is this what we're looking for?
+
+ if (strcmp(argv[0], mtcur->mnt_fsname) &&
+ strcmp(storage_path, mtcur->mnt_fsname) &&
+ strcmp(argv[0], mtcur->mnt_dir) &&
+ strcmp(storage_path, mtcur->mnt_dir)) continue;
+
+ // Remember this entry. Something later may have overmounted
+ // it, and we want the _last_ match.
+
+ mtcur = mtnext;
+
+ // If we're mounting all.
+
+ } else {
+ // Do we need to match a filesystem type?
+ // TODO: support "-t type1,type2"; "-t notype1,type2"
+
+ if (fstype && strcmp(mtcur->mnt_type, fstype)) continue;
+
+ // Skip noauto and swap anyway.
+
+ if (parse_mount_options(mtcur->mnt_opts, 0)
+ & (MOUNT_NOAUTO | MOUNT_SWAP)) continue;
+
+ // No, mount -a won't mount anything,
+ // even user mounts, for mere humans.
+
+ if (nonroot)
+ bb_error_msg_and_die(must_be_root);
+
+ // Mount this thing.
+
+ // NFS mounts want this to be xrealloc-able
+ mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
+ if (singlemount(mtcur, 1)) {
+ /* Count number of failed mounts */
+ rc++;
+ }
+ free(mtcur->mnt_opts);
+ }
+ }
+ if (ENABLE_FEATURE_CLEAN_UP) endmntent(fstab);
+
+clean_up:
+
+ if (ENABLE_FEATURE_CLEAN_UP) {
+ free(storage_path);
+ free(cmdopts);
+ }
+
+ return rc;
+}
diff --git a/i/pc104/initrd/conf/busybox/util-linux/pivot_root.c b/i/pc104/initrd/conf/busybox/util-linux/pivot_root.c
new file mode 100644
index 0000000..2706bd2
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/util-linux/pivot_root.c
@@ -0,0 +1,27 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * pivot_root.c - Change root file system. Based on util-linux 2.10s
+ *
+ * busyboxed by Evin Robertson
+ * pivot_root syscall stubbed by Erik Andersen, so it will compile
+ * regardless of the kernel being used.
+ *
+ * Licensed under GPL version 2, see file LICENSE in this tarball for details.
+ */
+#include "busybox.h"
+
+extern int pivot_root(const char * new_root,const char * put_old);
+
+int pivot_root_main(int argc, char **argv);
+int pivot_root_main(int argc, char **argv)
+{
+ if (argc != 3)
+ bb_show_usage();
+
+ if (pivot_root(argv[1], argv[2]) < 0) {
+ /* prints "pivot_root: <strerror text>" */
+ bb_perror_nomsg_and_die();
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/i/pc104/initrd/conf/busybox/util-linux/rdate.c b/i/pc104/initrd/conf/busybox/util-linux/rdate.c
new file mode 100644
index 0000000..33cf64e
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/util-linux/rdate.c
@@ -0,0 +1,71 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * The Rdate command will ask a time server for the RFC 868 time
+ * and optionally set the system time.
+ *
+ * by Sterling Huxley <sterling@europa.com>
+ *
+ * Licensed under GPL v2 or later, see file License for details.
+*/
+
+#include "busybox.h"
+
+enum { RFC_868_BIAS = 2208988800UL };
+
+static void socket_timeout(int sig)
+{
+ bb_error_msg_and_die("timeout connecting to time server");
+}
+
+static time_t askremotedate(const char *host)
+{
+ uint32_t nett;
+ int fd;
+
+ /* Add a timeout for dead or inaccessible servers */
+ alarm(10);
+ signal(SIGALRM, socket_timeout);
+
+ fd = create_and_connect_stream_or_die(host, bb_lookup_port("time", "tcp", 37));
+
+ if (safe_read(fd, (void *)&nett, 4) != 4) /* read time from server */
+ bb_error_msg_and_die("%s did not send the complete time", host);
+ close(fd);
+
+ /* convert from network byte order to local byte order.
+ * RFC 868 time is the number of seconds
+ * since 00:00 (midnight) 1 January 1900 GMT
+ * the RFC 868 time 2,208,988,800 corresponds to 00:00 1 Jan 1970 GMT
+ * Subtract the RFC 868 time to get Linux epoch
+ */
+
+ return ntohl(nett) - RFC_868_BIAS;
+}
+
+int rdate_main(int argc, char **argv);
+int rdate_main(int argc, char **argv)
+{
+ time_t remote_time;
+ unsigned long flags;
+
+ opt_complementary = "-1";
+ flags = getopt32(argc, argv, "sp");
+
+ remote_time = askremotedate(argv[optind]);
+
+ if ((flags & 2) == 0) {
+ time_t current_time;
+
+ time(&current_time);
+ if (current_time == remote_time)
+ bb_error_msg("current time matches remote time");
+ else
+ if (stime(&remote_time) < 0)
+ bb_perror_msg_and_die("cannot set time of day");
+ }
+
+ if ((flags & 1) == 0)
+ printf("%s", ctime(&remote_time));
+
+ return EXIT_SUCCESS;
+}
diff --git a/i/pc104/initrd/conf/busybox/util-linux/readprofile.c b/i/pc104/initrd/conf/busybox/util-linux/readprofile.c
new file mode 100644
index 0000000..a17b9c8
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/util-linux/readprofile.c
@@ -0,0 +1,237 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * readprofile.c - used to read /proc/profile
+ *
+ * Copyright (C) 1994,1996 Alessandro Rubini (rubini@ipvvis.unipv.it)
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/*
+ * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
+ * - added Native Language Support
+ * 1999-09-01 Stephane Eranian <eranian@cello.hpl.hp.com>
+ * - 64bit clean patch
+ * 3Feb2001 Andrew Morton <andrewm@uow.edu.au>
+ * - -M option to write profile multiplier.
+ * 2001-11-07 Werner Almesberger <wa@almesberger.net>
+ * - byte order auto-detection and -n option
+ * 2001-11-09 Werner Almesberger <wa@almesberger.net>
+ * - skip step size (index 0)
+ * 2002-03-09 John Levon <moz@compsoc.man.ac.uk>
+ * - make maplineno do something
+ * 2002-11-28 Mads Martin Joergensen +
+ * - also try /boot/System.map-`uname -r`
+ * 2003-04-09 Werner Almesberger <wa@almesberger.net>
+ * - fixed off-by eight error and improved heuristics in byte order detection
+ * 2003-08-12 Nikita Danilov <Nikita@Namesys.COM>
+ * - added -s option; example of use:
+ * "readprofile -s -m /boot/System.map-test | grep __d_lookup | sort -n -k3"
+ *
+ * Taken from util-linux and adapted for busybox by
+ * Paul Mundt <lethal@linux-sh.org>.
+ */
+
+#include "busybox.h"
+#include <sys/utsname.h>
+
+#define S_LEN 128
+
+/* These are the defaults */
+static const char defaultmap[] = "/boot/System.map";
+static const char defaultpro[] = "/proc/profile";
+
+int readprofile_main(int argc, char **argv);
+int readprofile_main(int argc, char **argv)
+{
+ FILE *map;
+ const char *mapFile, *proFile, *mult = 0;
+ unsigned long indx = 1;
+ size_t len;
+ uint64_t add0 = 0;
+ unsigned int step;
+ unsigned int *buf, total, fn_len;
+ unsigned long long fn_add, next_add; /* current and next address */
+ char fn_name[S_LEN], next_name[S_LEN]; /* current and next name */
+ char mapline[S_LEN];
+ char mode[8];
+ int optAll = 0, optInfo = 0, optReset = 0;
+ int optVerbose = 0, optNative = 0;
+ int optBins = 0, optSub = 0;
+ int maplineno = 1;
+ int header_printed;
+
+#define next (current^1)
+
+ proFile = defaultpro;
+ mapFile = defaultmap;
+
+ opt_complementary = "nn:aa:bb:ss:ii:rr:vv";
+ getopt32(argc, argv, "M:m:p:nabsirv",
+ &mult, &mapFile, &proFile,
+ &optNative, &optAll, &optBins, &optSub,
+ &optInfo, &optReset, &optVerbose);
+
+ if (optReset || mult) {
+ int multiplier, fd, to_write;
+
+ /*
+ * When writing the multiplier, if the length of the write is
+ * not sizeof(int), the multiplier is not changed
+ */
+ if (mult) {
+ multiplier = xatoi_u(mult);
+ to_write = sizeof(int);
+ } else {
+ multiplier = 0;
+ to_write = 1; /* sth different from sizeof(int) */
+ }
+
+ fd = xopen(defaultpro, O_WRONLY);
+
+ if (full_write(fd, &multiplier, to_write) != to_write)
+ bb_perror_msg_and_die("error writing %s", defaultpro);
+
+ close(fd);
+ return EXIT_SUCCESS;
+ }
+
+ /*
+ * Use an fd for the profiling buffer, to skip stdio overhead
+ */
+ len = INT_MAX;
+ buf = xmalloc_open_read_close(proFile, &len);
+ if (!optNative) {
+ int entries = len/sizeof(*buf);
+ int big = 0, small = 0, i;
+ unsigned *p;
+
+ for (p = buf+1; p < buf+entries; p++) {
+ if (*p & ~0U << (sizeof(*buf)*4))
+ big++;
+ if (*p & ((1 << (sizeof(*buf)*4))-1))
+ small++;
+ }
+ if (big > small) {
+ bb_error_msg("assuming reversed byte order, "
+ "use -n to force native byte order");
+ for (p = buf; p < buf+entries; p++)
+ for (i = 0; i < sizeof(*buf)/2; i++) {
+ unsigned char *b = (unsigned char *) p;
+ unsigned char tmp;
+
+ tmp = b[i];
+ b[i] = b[sizeof(*buf)-i-1];
+ b[sizeof(*buf)-i-1] = tmp;
+ }
+ }
+ }
+
+ step = buf[0];
+ if (optInfo) {
+ printf("Sampling_step: %i\n", step);
+ return EXIT_SUCCESS;
+ }
+
+ total = 0;
+
+ map = xfopen(mapFile, "r");
+
+ while (fgets(mapline, S_LEN, map)) {
+ if (sscanf(mapline, "%llx %s %s", &fn_add, mode, fn_name) != 3)
+ bb_error_msg_and_die("%s(%i): wrong map line",
+ mapFile, maplineno);
+
+ if (!strcmp(fn_name, "_stext")) /* only elf works like this */ {
+ add0 = fn_add;
+ break;
+ }
+ maplineno++;
+ }
+
+ if (!add0)
+ bb_error_msg_and_die("can't find \"_stext\" in %s", mapFile);
+
+ /*
+ * Main loop.
+ */
+ while (fgets(mapline, S_LEN, map)) {
+ unsigned int this = 0;
+
+ if (sscanf(mapline, "%llx %s %s", &next_add, mode, next_name) != 3)
+ bb_error_msg_and_die("%s(%i): wrong map line",
+ mapFile, maplineno);
+
+ header_printed = 0;
+
+ /* ignore any LEADING (before a '[tT]' symbol is found)
+ Absolute symbols */
+ if ((*mode == 'A' || *mode == '?') && total == 0) continue;
+ if (*mode != 'T' && *mode != 't' &&
+ *mode != 'W' && *mode != 'w')
+ break; /* only text is profiled */
+
+ if (indx >= len / sizeof(*buf))
+ bb_error_msg_and_die("profile address out of range. "
+ "Wrong map file?");
+
+ while (indx < (next_add-add0)/step) {
+ if (optBins && (buf[indx] || optAll)) {
+ if (!header_printed) {
+ printf("%s:\n", fn_name);
+ header_printed = 1;
+ }
+ printf("\t%"PRIx64"\t%u\n", (indx - 1)*step + add0, buf[indx]);
+ }
+ this += buf[indx++];
+ }
+ total += this;
+
+ if (optBins) {
+ if (optVerbose || this > 0)
+ printf(" total\t\t\t\t%u\n", this);
+ } else if ((this || optAll) &&
+ (fn_len = next_add-fn_add) != 0) {
+ if (optVerbose)
+ printf("%016llx %-40s %6i %8.4f\n", fn_add,
+ fn_name, this, this/(double)fn_len);
+ else
+ printf("%6i %-40s %8.4f\n",
+ this, fn_name, this/(double)fn_len);
+ if (optSub) {
+ unsigned long long scan;
+
+ for (scan = (fn_add-add0)/step + 1;
+ scan < (next_add-add0)/step; scan++) {
+ unsigned long long addr;
+
+ addr = (scan - 1)*step + add0;
+ printf("\t%#llx\t%s+%#llx\t%u\n",
+ addr, fn_name, addr - fn_add,
+ buf[scan]);
+ }
+ }
+ }
+
+ fn_add = next_add;
+ strcpy(fn_name, next_name);
+
+ maplineno++;
+ }
+
+ /* clock ticks, out of kernel text - probably modules */
+ printf("%6i %s\n", buf[len/sizeof(*buf)-1], "*unknown*");
+
+ /* trailer */
+ if (optVerbose)
+ printf("%016x %-40s %6i %8.4f\n",
+ 0, "total", total, total/(double)(fn_add-add0));
+ else
+ printf("%6i %-40s %8.4f\n",
+ total, "total", total/(double)(fn_add-add0));
+
+ fclose(map);
+ free(buf);
+
+ return EXIT_SUCCESS;
+}
diff --git a/i/pc104/initrd/conf/busybox/util-linux/setarch.c b/i/pc104/initrd/conf/busybox/util-linux/setarch.c
new file mode 100644
index 0000000..a7a45ec
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/util-linux/setarch.c
@@ -0,0 +1,53 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Linux32/linux64 allows for changing uname emulation.
+ *
+ * Copyright 2002 Andi Kleen, SuSE Labs.
+ *
+ * Licensed under GPL v2 or later, see file License for details.
+*/
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+#include <sys/personality.h>
+
+#include "busybox.h"
+
+int setarch_main(int ATTRIBUTE_UNUSED argc, char **argv);
+int setarch_main(int ATTRIBUTE_UNUSED argc, char **argv)
+{
+ int pers = -1;
+
+ /* Figure out what personality we are supposed to switch to ...
+ * we can be invoked as either:
+ * argv[0],argv[1] -> "setarch","personality"
+ * argv[0] -> "personality"
+ */
+retry:
+ if (argv[0][5] == '6') /* linux64 */
+ pers = PER_LINUX;
+ else if (argv[0][5] == '3') /* linux32 */
+ pers = PER_LINUX32;
+ else if (pers == -1 && argv[1] != NULL) {
+ pers = PER_LINUX32;
+ ++argv;
+ goto retry;
+ }
+
+ /* make user actually gave us something to do */
+ ++argv;
+ if (argv[0] == NULL)
+ bb_show_usage();
+
+ /* Try to set personality */
+ if (personality(pers) >= 0) {
+
+ /* Try to execute the program */
+ BB_EXECVP(argv[0], argv);
+ }
+
+ bb_perror_msg_and_die("%s", argv[0]);
+}
diff --git a/i/pc104/initrd/conf/busybox/util-linux/swaponoff.c b/i/pc104/initrd/conf/busybox/util-linux/swaponoff.c
new file mode 100644
index 0000000..8930035
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/util-linux/swaponoff.c
@@ -0,0 +1,77 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini swapon/swapoff implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under the GPL version 2, see the file LICENSE in this tarball.
+ */
+
+#include "busybox.h"
+#include <mntent.h>
+#include <sys/swap.h>
+
+static int swap_enable_disable(char *device)
+{
+ int status;
+ struct stat st;
+
+ xstat(device, &st);
+
+#if ENABLE_DESKTOP
+ /* test for holes */
+ if (S_ISREG(st.st_mode))
+ if (st.st_blocks * 512 < st.st_size)
+ bb_error_msg("warning: swap file has holes");
+#endif
+
+ if (applet_name[5] == 'n')
+ status = swapon(device, 0);
+ else
+ status = swapoff(device);
+
+ if (status != 0) {
+ bb_perror_msg("%s", device);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int do_em_all(void)
+{
+ struct mntent *m;
+ FILE *f;
+ int err;
+
+ f = setmntent("/etc/fstab", "r");
+ if (f == NULL)
+ bb_perror_msg_and_die("/etc/fstab");
+
+ err = 0;
+ while ((m = getmntent(f)) != NULL)
+ if (strcmp(m->mnt_type, MNTTYPE_SWAP) == 0)
+ err += swap_enable_disable(m->mnt_fsname);
+
+ endmntent(f);
+
+ return err;
+}
+
+int swap_on_off_main(int argc, char **argv);
+int swap_on_off_main(int argc, char **argv)
+{
+ int ret;
+
+ if (argc == 1)
+ bb_show_usage();
+
+ ret = getopt32(argc, argv, "a");
+ if (ret)
+ return do_em_all();
+
+ /* ret = 0; redundant */
+ while (*++argv)
+ ret += swap_enable_disable(*argv);
+ return ret;
+}
diff --git a/i/pc104/initrd/conf/busybox/util-linux/switch_root.c b/i/pc104/initrd/conf/busybox/util-linux/switch_root.c
new file mode 100644
index 0000000..5a01fd6
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/util-linux/switch_root.c
@@ -0,0 +1,123 @@
+/* vi: set sw=4 ts=4: */
+/* Copyright 2005 Rob Landley <rob@landley.net>
+ *
+ * Switch from rootfs to another filesystem as the root of the mount tree.
+ *
+ * Licensed under GPL version 2, see file LICENSE in this tarball for details.
+ */
+
+#include "busybox.h"
+#include <sys/vfs.h>
+
+
+// Make up for header deficiencies.
+
+#ifndef RAMFS_MAGIC
+#define RAMFS_MAGIC 0x858458f6
+#endif
+
+#ifndef TMPFS_MAGIC
+#define TMPFS_MAGIC 0x01021994
+#endif
+
+#ifndef MS_MOVE
+#define MS_MOVE 8192
+#endif
+
+dev_t rootdev;
+
+// Recursively delete contents of rootfs.
+
+static void delete_contents(const char *directory)
+{
+ DIR *dir;
+ struct dirent *d;
+ struct stat st;
+
+ // Don't descend into other filesystems
+ if (lstat(directory, &st) || st.st_dev != rootdev) return;
+
+ // Recursively delete the contents of directories.
+ if (S_ISDIR(st.st_mode)) {
+ if((dir = opendir(directory))) {
+ while ((d = readdir(dir))) {
+ char *newdir=d->d_name;
+
+ // Skip . and ..
+ if(*newdir=='.' && (!newdir[1] || (newdir[1]=='.' && !newdir[2])))
+ continue;
+
+ // Recurse to delete contents
+ newdir = alloca(strlen(directory) + strlen(d->d_name) + 2);
+ sprintf(newdir, "%s/%s", directory, d->d_name);
+ delete_contents(newdir);
+ }
+ closedir(dir);
+
+ // Directory should now be empty. Zap it.
+ rmdir(directory);
+ }
+
+ // It wasn't a directory. Zap it.
+
+ } else unlink(directory);
+}
+
+int switch_root_main(int argc, char *argv[]);
+int switch_root_main(int argc, char *argv[])
+{
+ char *newroot, *console=NULL;
+ struct stat st1, st2;
+ struct statfs stfs;
+
+ // Parse args (-c console)
+
+ opt_complementary = "-2";
+ getopt32(argc, argv, "c:", &console);
+
+ // Change to new root directory and verify it's a different fs.
+
+ newroot=argv[optind++];
+
+ if (chdir(newroot) || lstat(".", &st1) || lstat("/", &st2) ||
+ st1.st_dev == st2.st_dev)
+ {
+ bb_error_msg_and_die("bad newroot %s", newroot);
+ }
+ rootdev=st2.st_dev;
+
+ // Additional sanity checks: we're about to rm -rf /, so be REALLY SURE
+ // we mean it. (I could make this a CONFIG option, but I would get email
+ // from all the people who WILL eat their filesystemss.)
+
+ if (lstat("/init", &st1) || !S_ISREG(st1.st_mode) || statfs("/", &stfs) ||
+ (stfs.f_type != RAMFS_MAGIC && stfs.f_type != TMPFS_MAGIC) ||
+ getpid() != 1)
+ {
+ bb_error_msg_and_die("not rootfs");
+ }
+
+ // Zap everything out of rootdev
+
+ delete_contents("/");
+
+ // Overmount / with newdir and chroot into it. The chdir is needed to
+ // recalculate "." and ".." links.
+
+ if (mount(".", "/", NULL, MS_MOVE, NULL) || chroot(".") || chdir("/"))
+ bb_error_msg_and_die("moving root");
+
+ // If a new console specified, redirect stdin/stdout/stderr to that.
+
+ if (console) {
+ close(0);
+ if(open(console, O_RDWR) < 0)
+ bb_error_msg_and_die("bad console '%s'", console);
+ dup2(0, 1);
+ dup2(0, 2);
+ }
+
+ // Exec real init. (This is why we must be pid 1.)
+ execv(argv[optind], argv+optind);
+ bb_error_msg_and_die("bad init '%s'", argv[optind]);
+}
diff --git a/i/pc104/initrd/conf/busybox/util-linux/umount.c b/i/pc104/initrd/conf/busybox/util-linux/umount.c
new file mode 100644
index 0000000..4ea15d9
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/util-linux/umount.c
@@ -0,0 +1,152 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini umount implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ * Copyright (C) 2005 by Rob Landley <rob@landley.net>
+ *
+ * Licensed under GPL version 2, see file LICENSE in this tarball for details.
+ */
+
+#include "busybox.h"
+#include <mntent.h>
+#include <getopt.h>
+
+#define OPTION_STRING "flDnravd"
+#define OPT_FORCE 1
+#define OPT_LAZY 2
+#define OPT_DONTFREELOOP 4
+#define OPT_NO_MTAB 8
+#define OPT_REMOUNT 16
+#define OPT_ALL (ENABLE_FEATURE_UMOUNT_ALL ? 32 : 0)
+
+int umount_main(int argc, char **argv);
+int umount_main(int argc, char **argv)
+{
+ int doForce;
+ char path[2*PATH_MAX];
+ struct mntent me;
+ FILE *fp;
+ int status = EXIT_SUCCESS;
+ unsigned opt;
+ struct mtab_list {
+ char *dir;
+ char *device;
+ struct mtab_list *next;
+ } *mtl, *m;
+
+ /* Parse any options */
+
+ opt = getopt32(argc, argv, OPTION_STRING);
+
+ argc -= optind;
+ argv += optind;
+
+ doForce = MAX((opt & OPT_FORCE), (opt & OPT_LAZY));
+
+ /* Get a list of mount points from mtab. We read them all in now mostly
+ * for umount -a (so we don't have to worry about the list changing while
+ * we iterate over it, or about getting stuck in a loop on the same failing
+ * entry. Notice that this also naturally reverses the list so that -a
+ * umounts the most recent entries first. */
+
+ m = mtl = 0;
+
+ /* If we're umounting all, then m points to the start of the list and
+ * the argument list should be empty (which will match all). */
+
+ fp = setmntent(bb_path_mtab_file, "r");
+ if (!fp) {
+ if (opt & OPT_ALL)
+ bb_error_msg_and_die("cannot open %s", bb_path_mtab_file);
+ } else {
+ while (getmntent_r(fp, &me, path, sizeof(path))) {
+ m = xmalloc(sizeof(struct mtab_list));
+ m->next = mtl;
+ m->device = xstrdup(me.mnt_fsname);
+ m->dir = xstrdup(me.mnt_dir);
+ mtl = m;
+ }
+ endmntent(fp);
+ }
+
+ /* If we're not umounting all, we need at least one argument. */
+ if (!(opt & OPT_ALL)) {
+ m = 0;
+ if (!argc) bb_show_usage();
+ }
+
+ // Loop through everything we're supposed to umount, and do so.
+ for (;;) {
+ int curstat;
+ char *zapit = *argv;
+
+ // Do we already know what to umount this time through the loop?
+ if (m) safe_strncpy(path, m->dir, PATH_MAX);
+ // For umount -a, end of mtab means time to exit.
+ else if (opt & OPT_ALL) break;
+ // Get next command line argument (and look it up in mtab list)
+ else if (!argc--) break;
+ else {
+ argv++;
+ realpath(zapit, path);
+ for (m = mtl; m; m = m->next)
+ if (!strcmp(path, m->dir) || !strcmp(path, m->device))
+ break;
+ }
+ // If we couldn't find this sucker in /etc/mtab, punt by passing our
+ // command line argument straight to the umount syscall. Otherwise,
+ // umount the directory even if we were given the block device.
+ if (m) zapit = m->dir;
+
+ // Let's ask the thing nicely to unmount.
+ curstat = umount(zapit);
+
+ // Force the unmount, if necessary.
+ if (curstat && doForce) {
+ curstat = umount2(zapit, doForce);
+ if (curstat)
+ bb_error_msg("forced umount of %s failed!", zapit);
+ }
+
+ // If still can't umount, maybe remount read-only?
+ if (curstat && (opt & OPT_REMOUNT) && errno == EBUSY && m) {
+ curstat = mount(m->device, zapit, NULL, MS_REMOUNT|MS_RDONLY, NULL);
+ bb_error_msg(curstat ? "cannot remount %s read-only" :
+ "%s busy - remounted read-only", m->device);
+ }
+
+ if (curstat) {
+ status = EXIT_FAILURE;
+ bb_perror_msg("cannot umount %s", zapit);
+ } else {
+ /* De-allocate the loop device. This ioctl should be ignored on
+ * any non-loop block devices. */
+ if (ENABLE_FEATURE_MOUNT_LOOP && !(opt & OPT_DONTFREELOOP) && m)
+ del_loop(m->device);
+ if (ENABLE_FEATURE_MTAB_SUPPORT && !(opt & OPT_NO_MTAB) && m)
+ erase_mtab(m->dir);
+ }
+
+ // Find next matching mtab entry for -a or umount /dev
+ // Note this means that "umount /dev/blah" will unmount all instances
+ // of /dev/blah, not just the most recent.
+ while (m && (m = m->next))
+ if ((opt & OPT_ALL) || !strcmp(path, m->device))
+ break;
+ }
+
+ // Free mtab list if necessary
+
+ if (ENABLE_FEATURE_CLEAN_UP) {
+ while (mtl) {
+ m = mtl->next;
+ free(mtl->device);
+ free(mtl->dir);
+ free(mtl);
+ mtl = m;
+ }
+ }
+
+ return status;
+}